Repository Turn Document

Fix Electron Preload Bridge

Repaired the desktop ChatGPT login bridge by making the Electron preload script emit a classic runnable script instead of ESM. Local desktop settings now reconnect to the native bridge and show the managed account, transport readiness, and model controls again.

Area Electron desktop shell
Issue `islandflow-hj3`
Validated Build, tests, live desktop restart

Summary

The desktop Copilot settings page was not failing because login was broken. Electron was failing before the bridge could even load: the preload bundle contained runtime `import` syntax, so the renderer never got `window.islandflowDesktop`. After switching the preload implementation to CommonJS-compatible runtime code, the local Electron shell resumed exposing the bridge and the settings page recovered immediately after restart.

Changes Made

Context

The UI state on `localhost:3000/settings` was already designed to distinguish between “not in desktop” and “desktop shell detected but native bridge missing.” In this case the latter message was accurate but incomplete: Electron was present, yet the preload never executed because the emitted file was not valid for the preload runtime. That left the renderer with an Electron user agent but no bridge object.

Important Implementation Details

  • Electron logged `Unable to load preload script` followed by `Cannot use import statement outside a module` during local startup.
  • The desktop main process configuration was already pointing at the correct preload path, so the failure was in emitted file format, not window wiring.
  • Keeping the preload free of runtime imports ensures Electron can execute it even though the desktop workspace is otherwise ESM-oriented.
  • The bridge API shape exposed to the web app did not change, so no renderer updates were required.

Relevant Runtime Signal

Observed in Electron startup logs during reproduction

`Unable to load preload script: .../dist/preload.js` and `SyntaxError: Cannot use import statement outside a module` were the decisive clues that isolated the problem to preload emission.

Relevant Diff Snippets

apps/desktop/src/preload.ts
--- a/apps/desktop/src/preload.ts
+++ b/apps/desktop/src/preload.ts
@@
-import { contextBridge, ipcRenderer } from "electron";
-import { ... } from "./desktop-ai-ipc.js";
+const { contextBridge, ipcRenderer } = require("electron");
+const DESKTOP_AI_STATE_CHANNEL = "islandflow:desktop-ai:state";
+const DESKTOP_AI_GET_STATE = "islandflow:desktop-ai:get-state";
+const DESKTOP_AI_LOGIN_BROWSER = "islandflow:desktop-ai:login-browser";
+const DESKTOP_AI_LOGIN_DEVICE = "islandflow:desktop-ai:login-device";
+const DESKTOP_AI_CANCEL_LOGIN = "islandflow:desktop-ai:cancel-login";
+const DESKTOP_AI_LOGOUT = "islandflow:desktop-ai:logout";
+const DESKTOP_AI_UPDATE_PREFERENCES = "islandflow:desktop-ai:update-preferences";
+const DESKTOP_AI_RUN_TASK = "islandflow:desktop-ai:run-task";
@@
-    updatePreferences: (
-      next: Partial<{ model: string | null; reasoningEffort: IslandflowAiReasoningEffort | null }>
-    ): Promise<void> => ipcRenderer.invoke(DESKTOP_AI_UPDATE_PREFERENCES, next),
+    updatePreferences: (next: DesktopAiPreferenceUpdate): Promise<void> =>
+      ipcRenderer.invoke(DESKTOP_AI_UPDATE_PREFERENCES, next),
        

Expected Impact for End-Users

Local Islandflow Desktop sessions no longer get stuck in the misleading “Bridge unavailable in this window” state when the actual problem is preload execution. Users should see their managed ChatGPT session reconnect, transport status move to Ready, and model controls become interactive after launching or restarting the desktop shell.

Validation

Desktop Build `bun --cwd=apps/desktop run build` succeeded and emitted a plain preload script.
Desktop Tests `bun --cwd=apps/desktop test` passed.
Web Tests `bun test apps/web/app/desktop-ai.test.ts` passed.
Manual Verification Restarted `bun run dev:desktop` and confirmed settings showed the connected account, ready transport, and populated model controls.

Issues, Limitations, and Mitigations

Follow-up Work

No immediate follow-up required Possible hardening: build-time preload output guard