# DOX — packages/electron/src

Files in this directory. One row per source file.

| File | Purpose |
|------|---------|
| `main.ts` | Electron main: 6-state startup machine (`checking-server-health` → `attach` \| `wizard-welcome` → `launch-server` → `health-wait` → `done` \| `loading-page-error`). 3 triggers (boot, health-check-result, server-spawn-result). 3 end states. Single-instance lock, tray, loading-page IPC. First-run marker `~/.pi/dashboard/first-run-done`. See change: eliminate-electron-runtime-install. **External-link hardening** (change: harden-external-link-handling, #13): `createMainWindow` registers `webContents.setWindowOpenHandler` + `will-navigate` BEFORE `loadURL` so `target="_blank"` / `window.open` + bare-`<a>` external nav route through `shell.openExternal` (system browser) instead of replacing dashboard. `will-navigate` **OAuth-aware** via `decideWillNavigate(serverUrl, webContents.getURL(), url)` (change: fix-oauth-blocked-by-external-link-guard) — trap-guard fires only when leaving dashboard, so mid-login on OAuth provider (Google/GitHub/generic OIDC) provider-internal multi-step nav allowed + callback redirect lands cleanly. `setWindowOpenHandler` unchanged. See change: electron-server-launch-controls — loadFile(loading.html) replaces inline data:text/html. mainWindow attaches preload exposing piDashboard. Registers ipcMain handlers `dashboard:request-launch`, `dashboard:open-doctor`, `dashboard:read-server-log`. Forwards `dashboard:launch-status` push events. Tray hooks pass `requestServerLaunch` + `isManagedServerRunning`. `wizard-welcome` arm closes splash before `await showWelcomeStep()`, re-opens splash after wizard returns. Splash and wizard mutually exclusive. See change: fix-wizard-occluded-by-splash. Startup reads mode.json before selectLaunchSource; remote mode attaches remoteUrl directly. See change: docker-packaging. |
| `preload.ts` | Exposes `wizardApi` (existing) + new `piDashboard` namespace: `requestLaunch`, `openDoctor`, `readServerLog`, `onStatus`. Same preload attached to wizard window + main window; each renderer uses what it needs. See change: electron-server-launch-controls. WizardApi.persistMode(mode,remoteUrl) → invoke wizard:persist-mode. See change: docker-packaging. |
