fix electron desktop ai runtime fallback detection
This commit is contained in:
parent
a54e847c8e
commit
d15f96d7be
4 changed files with 390 additions and 47 deletions
194
docs/turns/2026-05-20-fix-false-browser-fallback-electron.html
Normal file
194
docs/turns/2026-05-20-fix-false-browser-fallback-electron.html
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Turn Doc - Fix False Browser Fallback In Electron</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--bg: #f5f7fb;
|
||||
--panel: #ffffff;
|
||||
--text: #142032;
|
||||
--muted: #4d607a;
|
||||
--accent: #2359d1;
|
||||
--border: #d9e2f0;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
}
|
||||
main {
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1.25rem 3rem;
|
||||
}
|
||||
h1, h2 { line-height: 1.25; }
|
||||
h1 {
|
||||
margin: 0 0 0.4rem;
|
||||
font-size: 1.9rem;
|
||||
}
|
||||
.lede {
|
||||
margin: 0 0 1.5rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
section {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 1rem 1.1rem;
|
||||
margin: 0 0 0.9rem;
|
||||
}
|
||||
h2 { margin: 0 0 0.55rem; font-size: 1.1rem; }
|
||||
p, li { font-size: 0.98rem; }
|
||||
ul { margin: 0.4rem 0 0 1.25rem; padding: 0; }
|
||||
code, pre {
|
||||
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
||||
}
|
||||
pre {
|
||||
background: #0f1725;
|
||||
color: #d5e3ff;
|
||||
border-radius: 10px;
|
||||
padding: 0.85rem;
|
||||
overflow: auto;
|
||||
margin: 0.55rem 0 0;
|
||||
}
|
||||
.meta {
|
||||
display: inline-block;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
a { color: var(--accent); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Fix False Browser Fallback In Electron</h1>
|
||||
<p class="meta">Date: 2026-05-20 · Issue: islandflow-y7b</p>
|
||||
<p class="lede">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<section>
|
||||
<h2>Summary</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Changes Made</h2>
|
||||
<ul>
|
||||
<li>Refactored <code>DesktopAiProvider</code> to initialize from <code>resolveCurrentDesktopAiRuntime()</code> instead of browser-first defaults.</li>
|
||||
<li>Separated runtime synchronization from <code>bridge.ai.getState()</code> and subscription state flow.</li>
|
||||
<li>Added guarded bridge attachment logic to avoid duplicate subscriptions across runtime resync events.</li>
|
||||
<li>Added bounded runtime retry window plus lifecycle resync hooks on <code>focus</code> and <code>pageshow</code>.</li>
|
||||
<li>Extended runtime regression coverage in <code>apps/web/app/desktop-ai.test.ts</code> for pending/rejecting bridge state and late marker availability.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Context</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Important Implementation Details</h2>
|
||||
<ul>
|
||||
<li><code>BROWSER_RUNTIME</code> provides explicit non-desktop defaults only when no client window exists.</li>
|
||||
<li><code>isSameDesktopAiRuntime()</code> avoids unnecessary runtime state churn on repeated sync checks.</li>
|
||||
<li><code>attachBridge()</code> tracks active bridge identity and ignores stale async callbacks from superseded bridges.</li>
|
||||
<li>When runtime is desktop but bridge is missing, unavailable state remains desktop-scoped and never browser-scoped.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Relevant Diff Snippets</h2>
|
||||
<p>Rendered in standard unified diff format compatible with <a href="https://diffs.com/docs">diffs.com</a>.</p>
|
||||
<pre><code class="language-diff">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);
|
||||
</code></pre>
|
||||
<pre><code class="language-diff">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);
|
||||
+ });
|
||||
</code></pre>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Expected Impact for End-Users</h2>
|
||||
<ul>
|
||||
<li>Electron users should no longer see false browser-only "Desktop app required" fallback while already inside desktop shell.</li>
|
||||
<li>When bridge state is delayed or fails, users see bridge/login recovery messaging that matches desktop context.</li>
|
||||
<li>Pure browser sessions still correctly show desktop-required fallback copy.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Validation</h2>
|
||||
<ul>
|
||||
<li><code>bun test apps/web/app/desktop-ai.test.ts</code> (pass)</li>
|
||||
<li><code>bun test apps/desktop</code> (pass)</li>
|
||||
<li><code>bun --cwd=apps/web run build</code> (fails on pre-existing typecheck issue in <code>packages/types/src/desktop-ai.ts</code> using <code>.ts</code> import-path extensions)</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Issues, Limitations, and Mitigations</h2>
|
||||
<ul>
|
||||
<li>Manual Electron UI validation at <code>127.0.0.1:3000/settings</code> was not executed in this CLI pass.</li>
|
||||
<li>Web production build remains blocked by an unrelated repository typecheck issue outside the touched files.</li>
|
||||
<li>Mitigation: runtime behavior is covered with focused regression tests and desktop-side test suite remains green.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Follow-up Work</h2>
|
||||
<ul>
|
||||
<li>Perform manual Electron verification of settings copy and fallback pane behavior once local desktop app is running.</li>
|
||||
<li>Resolve existing <code>packages/types</code> TypeScript extension import issue to restore green web production builds.</li>
|
||||
<li>No additional follow-up issue created in this pass; current bug tracked as <code>islandflow-y7b</code>.</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue