From 09a6a2ddd71d93b973736853e534085f5f511a0a Mon Sep 17 00:00:00 2001 From: brooklyn! Date: Mon, 8 Jun 2026 17:01:08 -0500 Subject: [PATCH] fix(desktop): stream the transcript while the window is backgrounded (#42399) The chat transcript reaches the screen through a requestAnimationFrame-gated flush (useSessionStateCache). The main BrowserWindow never set backgroundThrottling, so Chromium paused rAF and clamped timers whenever the window was blurred or occluded -- the live answer would stall until the window regained focus or the user refreshed. In practice this bit any time Hermes wasn't the focused window mid-turn (typing in your editor while the agent replies, detached devtools, another window on top), presenting as "thinking, no text, have to refresh." Opt the renderer out of background throttling so a streaming chat app actually streams in the background: - backgroundThrottling: false on the main window (matches the secondary windows that already set it) - disable-renderer-backgrounding / disable-backgrounding-occluded-windows / disable-background-timer-throttling at the process level for the occlusion case Latent since the desktop app landed (#20059), not a recent regression. --- apps/desktop/electron/main.cjs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index c28aea0bb1b..333e2b136a6 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -119,6 +119,20 @@ if (REMOTE_DISPLAY_REASON) { `[hermes] remote display detected (${REMOTE_DISPLAY_REASON}); disabling GPU hardware acceleration to prevent flicker` ) } + +// Keep the renderer running at full speed while the window is in the background +// or occluded. The chat transcript streams to screen through a +// requestAnimationFrame-gated flush; Chromium pauses rAF (and clamps timers) +// for backgrounded/occluded renderers, so without these the live answer stalls +// whenever the window loses focus (switching to your editor mid-turn, detached +// devtools, another window covering it) and only paints on refocus or refresh. +// `backgroundThrottling: false` on the BrowserWindow covers the blurred case; +// these process-level switches additionally stop Chromium from backgrounding or +// occlusion-throttling the renderer. Must run before app `ready`. +app.commandLine.appendSwitch('disable-renderer-backgrounding') +app.commandLine.appendSwitch('disable-backgrounding-occluded-windows') +app.commandLine.appendSwitch('disable-background-timer-throttling') + const SOURCE_REPO_ROOT = path.resolve(APP_ROOT, '../..') // Build-time install stamp -- the git ref this .exe was built against. @@ -4689,7 +4703,16 @@ function createWindow() { webviewTag: true, sandbox: true, nodeIntegration: false, - devTools: true + devTools: true, + // Keep timers + requestAnimationFrame running at full speed when the + // window is blurred/occluded. The chat transcript streams to the screen + // through a requestAnimationFrame-gated flush (useSessionStateCache), + // so with Chromium's default background throttling the live answer + // stalls whenever this window isn't focused (e.g. you switch to your + // editor mid-turn, or open detached devtools) and only appears once you + // refocus or refresh. A streaming chat app must render in the + // background, so opt out — matching the secondary windows above. + backgroundThrottling: false } })