| name | fantasia-electron-main |
| description | Works on Fantasia Archive Electron main process: app lifecycle, window management, platform tweaks, native integrations, and ipcMain registration (register*Ipc + electron-ipc-bridge channel names). Use when editing electron-main.ts, src-electron/mainScripts/, or main-side tests. |
Fantasia Archive — Electron main process
Entry and flow
- Entry:
electron-main.ts — Chromium stderr filter, app name/userData, Windows DevTools workaround, startApp (all IPC registrars), native shell menu, openAppWindowManager. startApp() before any BrowserWindow so preload channels exist before renderer load.
- IPC style: prefer
ipcMain.handle + ipcRenderer.invoke. sendSync last resort only — document exceptions.
userData: fixAppName() in appIdentity_manager.ts. TEST_ENV components/e2e → playwright-user-data via playwrightIsolatedUserDataDirName.ts
- Modules:
src-electron/mainScripts/ feature folders over growing electron-main.ts
- IPC registration: channel strings
electron-ipc-bridge.ts; handlers ipcManagement/register*Ipc.ts; registerAllFaIpc() from ipcManagement_manager.ts
IPC payload validation (Zod)
ipcMain.handle args untrusted at runtime — validate in main before stores/privileged APIs.
- Objects/patches: Zod schema in
src-electron/shared/ — reference faUserSettingsPatchSchema.ts
- Throw on invalid →
invoke rejects; renderer handles
- Single primitives:
typeof + predicate OK (e.g. checkIfExternalUrl)
registerFaExtraEnvIpc: env/harness trust, not preload IPC
zod in package.json dependencies
- Vitest:
shared/_tests/ + registrar tests for invalid payloads
When Zod not replacing existing code
- External links: one string + URL check
- Window control, app details, devtools: no structured renderer payload
- Future bulk DB APIs: Zod from start
Testing
- Vitest:
mainScripts/_tests/, per-area _tests/, contentBridgeAPIs/_tests/
- After main changes: dev scoped gate + connected
src-electron/**/_tests during edits; full yarn testbatch:verify before commit (fantasia-dev-scoped-verify)
Renderer sandbox
webPreferences: sandbox: true, contextIsolation: true, nodeIntegration: false. Preload → main IPC for privileged work. Electron Process Sandboxing
Window chrome IPC
registerFaWindowControlIpc, registerFaAppDetailsIpc — BrowserWindow.fromWebContents(event.sender)
- Preload path:
path.resolve(currentDir, …) from bundled main chunk — no extra .. assuming subfolder of mainScripts/
Packaged DevTools (intentional — do not regress)
Product law: installed / app.isPackaged builds must open Chrome DevTools via Help → Toggle developer tools, default keybind (F12 / primary chord), and faDevToolsControl IPC — same as unpackaged.
- Registrar:
registerFaDevToolsIpc (FA_DEVTOOLS_IPC) → openDevTools / closeDevTools / status. Bridge: faDevToolsControlAPI → renderer toggleDevTools.
- Forbidden: gating DevTools on
app.isPackaged, webPreferences.devTools: false, or “security hardening” that no-ops packaged toggle/open/close/status. That regression has shipped more than once; treat as bug, not hardening.
- Allowed: keep sandbox / contextIsolation / sender checks elsewhere; DevTools itself stays available after install.
- Vitest:
registerFaDevToolsIpc.vitest.test.ts must assert packaged path can open — never re-add “packaged no-op” specs as desired behavior.
- Smoke: after Electron packaging, verify menu/keybind still opens DevTools (fantasia-release-build).
Security hardening (main)
app:// — registerFaAppProtocolWiring: host allowlist + path.relative guard against traversal outside app root.
- IPC sender — privileged mutate / project DB handlers validate
event.sender via assertMainWindowSender (main window webContents.id). Failsafe path reply + OS-open keep dedicated checks. Do not pair sender checks with packaged DevTools disable — see Packaged DevTools above.
- Navigation —
will-navigate allowlist: app: + DEV APP_URL origin only; foreign http(s) preventDefault then shell.openExternal when checkIfExternalUrl. setWindowOpenHandler deny. (mainWindowCreationWiring.ts).
openExternal — faExternalUrlPredicate: block RFC1918 + link-local targets.
- Project paths —
createResolveHardenedFaProjectFilePath (functions/) + faProjectFilePathHardeningWiring.ts before open/reconnect; failsafe reconnect prefers last-known mirrored path and only accepts a renderer reply that hardens to the same path; packaged builds omit dev ELECTRON_MAIN_FILEPATH leak.
Keybind persistence
mainScripts/keybinds/ — overrides, Zod patch, registerFaKeybindsIpc. See fantasia-keybinds.
SQLite and files
Types
Shared types → types/. See types-folder.mdc.