Fix False Browser Fallback In Electron

Date: 2026-05-20 ยท Issue: islandflow-y7b

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

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

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

Validation

Issues, Limitations, and Mitigations

Follow-up Work