Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions apps/desktop/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,9 @@
ipcMain.handle('hermes:get-remote-display-reason', () => REMOTE_DISPLAY_REASON)

// 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
// or occluded. The chat transcript streams to screen through a bounded timer
// flush; Chromium 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;
Expand Down Expand Up @@ -4939,6 +4939,8 @@
function getWindowState(win = mainWindow) {
return {
isFullscreen: Boolean(win?.isFullScreen?.()),
isMinimized: Boolean(win?.isMinimized?.()),
isVisible: Boolean(win?.isVisible?.()),
nativeOverlayWidth: getNativeOverlayWidth(),
windowButtonPosition: getWindowButtonPosition()
}
Expand Down Expand Up @@ -8427,8 +8429,12 @@
}
})

win.on('enter-full-screen', () => sendWindowStateChanged(true))
win.on('leave-full-screen', () => sendWindowStateChanged(false))
win.on('enter-full-screen', () => sendWindowStateChanged(true, win))
win.on('leave-full-screen', () => sendWindowStateChanged(false, win))
win.on('minimize', () => sendWindowStateChanged(undefined, win))
win.on('restore', () => sendWindowStateChanged(undefined, win))
win.on('hide', () => sendWindowStateChanged(undefined, win))
win.on('show', () => sendWindowStateChanged(undefined, win))

wireCommonWindowHandlers(win, zoomWiringForWindowKind('chat'))

Expand Down Expand Up @@ -8510,6 +8516,10 @@
// traffic lights hide/show independently of the primary.
win.on('enter-full-screen', () => sendWindowStateChanged(true, win))
win.on('leave-full-screen', () => sendWindowStateChanged(false, win))
win.on('minimize', () => sendWindowStateChanged(undefined, win))
win.on('restore', () => sendWindowStateChanged(undefined, win))
win.on('hide', () => sendWindowStateChanged(undefined, win))
win.on('show', () => sendWindowStateChanged(undefined, win))

wireCommonWindowHandlers(win, zoomWiringForWindowKind('chat'))

Expand Down Expand Up @@ -8898,9 +8908,9 @@
show: false,
backgroundColor: getWindowBackgroundColor(),
// Shared with the secondary session windows (chatWindowWebPreferences) so
// both keep `backgroundThrottling: false` — the chat transcript streams via
// a requestAnimationFrame-gated flush that Chromium pauses for blurred
// windows, stalling the live answer until refocus. See session-windows.ts.
// both keep `backgroundThrottling: false` — the chat transcript uses a
// bounded timer flush that Chromium clamps for blurred windows, stalling
// the live answer until refocus. See session-windows.ts.
webPreferences: chatWindowWebPreferences(PRELOAD_PATH)
})

Expand Down Expand Up @@ -8968,6 +8978,10 @@
mainWindow.on('enter-full-screen', () => sendWindowStateChanged(true))
mainWindow.on('will-leave-full-screen', () => sendWindowStateChanged(false))
mainWindow.on('leave-full-screen', () => sendWindowStateChanged(false))
mainWindow.on('minimize', () => sendWindowStateChanged())
mainWindow.on('restore', () => sendWindowStateChanged())
mainWindow.on('hide', () => sendWindowStateChanged())
mainWindow.on('show', () => sendWindowStateChanged())

// Reopen where the user left off. resized/moved settle once per drag; close is
// the cross-platform backstop, flushed synchronously before the window is gone.
Expand Down Expand Up @@ -10182,7 +10196,7 @@

ipcMain.handle('hermes:quick-entry:settings:set', async (_event, patch) => {
const current = readQuickEntrySettings()
const next = sanitizeQuickEntrySettings({

Check warning on line 10199 in apps/desktop/electron/main.ts

View workflow job for this annotation

GitHub Actions / JS & TS checks / apps/desktop / check:lint

Expected blank line before this statement
enabled: patch?.enabled === undefined ? current.enabled : patch.enabled === true,
shortcut: typeof patch?.shortcut === 'string' && patch.shortcut.trim() ? patch.shortcut : current.shortcut
})
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/electron/session-windows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ test('registry trims the session id before keying', () => {

test('chatWindowWebPreferences disables background throttling so streaming paints while blurred', () => {
// Regression: secondary session windows used to omit this flag, so a streamed
// answer stalled until the window regained focus (Chromium pauses the
// requestAnimationFrame-gated transcript flush for backgrounded windows).
// answer stalled until the window regained focus (Chromium clamps the
// transcript flush timer for backgrounded windows).
const prefs = chatWindowWebPreferences('/tmp/preload.cjs')

assert.equal(prefs.backgroundThrottling, false)
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/electron/session-windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const SESSION_WINDOW_MIN_HEIGHT = 620
// false`, so a streamed answer stalled until the window regained focus.
//
// `backgroundThrottling: false` is load-bearing: the transcript streams to the
// screen through a requestAnimationFrame-gated flush, which Chromium pauses for
// blurred/occluded windows. A streaming chat app must keep painting in the
// screen through a bounded timer flush, which Chromium clamps for blurred/
// occluded windows. A streaming chat app must keep painting in the
// background, so every chat window opts out. The preload path is injected
// because it depends on the Electron entry's __dirname.
function chatWindowWebPreferences(preloadPath: string) {
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/app/pet-overlay/pet-overlay-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ export function PetOverlayApp() {
<PetBubble />
</div>
<div style={{ lineHeight: 0, position: 'relative' }}>
<PetSprite info={info} />
<PetSprite info={info} pauseWhenUnfocused={false} />

{/* Hearts on the popped-out pet — identical to in-window. */}
<PetHeartField
Expand Down
Loading
Loading