Fix False Browser Fallback In Electron
Updated the desktop AI runtime handling in the web renderer so Electron runtime detection is treated as an immediate signal, independent from async bridge state loading, which prevents false browser-only fallback UI in desktop windows.
Summary
The renderer now initializes desktop shell and bridge availability from a synchronous runtime snapshot, then handles AI state loading separately. If state loading hangs or fails, the UI remains in desktop mode and shows bridge/login guidance instead of browser-only "open desktop app" fallback messaging.
Changes Made
- Refactored
DesktopAiProviderto initialize fromresolveCurrentDesktopAiRuntime()instead of browser-first defaults. - Separated runtime synchronization from
bridge.ai.getState()and subscription state flow. - Added guarded bridge attachment logic to avoid duplicate subscriptions across runtime resync events.
- Added bounded runtime retry window plus lifecycle resync hooks on
focusandpageshow. - Extended runtime regression coverage in
apps/web/app/desktop-ai.test.tsfor pending/rejecting bridge state and late marker availability.
Context
In some Electron sessions, the settings UI could render browser-only fallback copy before or during bridge startup. The root issue was coupling runtime identity and async bridge state fetch outcomes. This patch keeps runtime identity authoritative even when bridge state is delayed or unavailable.
Important Implementation Details
BROWSER_RUNTIMEprovides explicit non-desktop defaults only when no client window exists.isSameDesktopAiRuntime()avoids unnecessary runtime state churn on repeated sync checks.attachBridge()tracks active bridge identity and ignores stale async callbacks from superseded bridges.- When runtime is desktop but bridge is missing, unavailable state remains desktop-scoped and never browser-scoped.
Relevant Diff Snippets
Rendered in standard unified diff format compatible with diffs.com.
diff --git a/apps/web/app/desktop-ai.tsx b/apps/web/app/desktop-ai.tsx
@@
+export const resolveCurrentDesktopAiRuntime = (): DesktopAiRuntime =>
+ typeof window === "undefined" ? BROWSER_RUNTIME : resolveDesktopAiRuntime(window);
@@
- const [state, setState] = useState<IslandflowAiState>(() => createUnavailableState());
- const [bridge, setBridge] = useState<DesktopAiBridge | null>(null);
- const [shellAvailable, setShellAvailable] = useState(false);
+ const initialRuntime = resolveCurrentDesktopAiRuntime();
+ const [runtime, setRuntime] = useState<DesktopAiRuntime>(initialRuntime);
+ const [state, setState] = useState<IslandflowAiState>(() =>
+ createUnavailableState(initialRuntime)
+ );
@@
+ window.addEventListener("focus", onWindowLifecycle);
+ window.addEventListener("pageshow", onWindowLifecycle);
diff --git a/apps/web/app/desktop-ai.test.ts b/apps/web/app/desktop-ai.test.ts
@@
+ it("keeps desktop runtime when the bridge exists before ai state resolves", () => {
+ ...
+ expect(runtime.shellAvailable).toBe(true);
+ expect(runtime.bridgeAvailable).toBe(true);
+ });
+
+ it("keeps bridge-specific recovery copy when bridge state loading fails", async () => {
+ ...
+ expect(requireDesktopActionCopy(...)).not.toContain("Open Islandflow Desktop");
+ });
+
+ it("flips from browser runtime to desktop runtime when the marker appears later", () => {
+ ...
+ expect(nextRuntime.shellAvailable).toBe(true);
+ });
Expected Impact for End-Users
- Electron users should no longer see false browser-only "Desktop app required" fallback while already inside desktop shell.
- When bridge state is delayed or fails, users see bridge/login recovery messaging that matches desktop context.
- Pure browser sessions still correctly show desktop-required fallback copy.
Validation
bun test apps/web/app/desktop-ai.test.ts(pass)bun test apps/desktop(pass)bun --cwd=apps/web run build(fails on pre-existing typecheck issue inpackages/types/src/desktop-ai.tsusing.tsimport-path extensions)
Issues, Limitations, and Mitigations
- Manual Electron UI validation at
127.0.0.1:3000/settingswas not executed in this CLI pass. - Web production build remains blocked by an unrelated repository typecheck issue outside the touched files.
- Mitigation: runtime behavior is covered with focused regression tests and desktop-side test suite remains green.
Follow-up Work
- Perform manual Electron verification of settings copy and fallback pane behavior once local desktop app is running.
- Resolve existing
packages/typesTypeScript extension import issue to restore green web production builds. - No additional follow-up issue created in this pass; current bug tracked as
islandflow-y7b.