authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-08-18 21:29:02-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-18 21:29:02-04:00
logefcf7d1ac03e784bdb26843d61ae0ff81c03cec6
tree3374261eb7129c5c739121cf5235e53b4172626a
parente7f6a30caba5c390d5bbc9c300de89b68601c057
signature Signed by PGP key B5690EEEBB952194

fix(desktop): boot the main window unthrottled so cold start paints at full speed (#7460)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

2 files changed, 43 insertions(+), 1 deletions(-)

apps/desktop/src/window/DesktopWindow.test.ts+32-1
......@@ -80,6 +80,7 @@ function makeFakeBrowserWindow() {
8080 reload: vi.fn(),
8181 replaceMisspelling: vi.fn(),
8282 send: vi.fn(),
83 setBackgroundThrottling: vi.fn(),
8384 setWindowOpenHandler: vi.fn(),
8485 };
8586
......@@ -124,6 +125,7 @@ function makeFakeBrowserWindow() {
124125 reload: webContents.reload,
125126 send: webContents.send,
126127 setZoomLevel: webContents.setZoomLevel,
128 setBackgroundThrottling: webContents.setBackgroundThrottling,
127129 setAutoHideCursor: window.setAutoHideCursor,
128130 webContentsListeners,
129131 windowListeners,
......@@ -455,7 +457,7 @@ describe("DesktopWindow", () => {
455457 assert.isUndefined(createdWindowOptions[0]?.x);
456458 assert.isUndefined(createdWindowOptions[0]?.y);
457459 assert.isTrue(createdWindowOptions[0]?.disableAutoHideCursor);
458 assert.isUndefined(createdWindowOptions[0]?.webPreferences?.backgroundThrottling);
460 assert.isFalse(createdWindowOptions[0]?.webPreferences?.backgroundThrottling);
459461 assert.deepEqual(fakeWindow.setAutoHideCursor.mock.calls, [[false]]);
460462 assert.deepEqual(fakeWindow.loadURL.mock.calls[0], ["t3code-dev://app/"]);
461463 assert.equal(fakeWindow.openDevTools.mock.calls.length, 1);
......@@ -604,6 +606,35 @@ describe("DesktopWindow", () => {
604606 }),
605607 );
606608
609 // The window boots hidden with throttling disabled so first paint runs at
610 // full speed; the first reveal must hand it back to normal hidden-window
611 // throttling or a minimized window stays expensive forever.
612 it.effect("re-enables background throttling on first reveal", () =>
613 Effect.gen(function* () {
614 const fakeWindow = makeFakeBrowserWindow();
615 const createCount = yield* Ref.make(0);
616 const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none());
617 const layer = makeTestLayer({
618 window: fakeWindow.window,
619 createCount,
620 mainWindow,
621 });
622
623 yield* Effect.gen(function* () {
624 const desktopWindow = yield* DesktopWindow.DesktopWindow;
625 yield* desktopWindow.handleBackendReady(new URL("http://127.0.0.1:3773"));
626
627 assert.equal(fakeWindow.setBackgroundThrottling.mock.calls.length, 0);
628 const readyToShow = fakeWindow.windowListeners.get("ready-to-show");
629 if (!readyToShow) {
630 return yield* Effect.die("window ready-to-show listener was not registered");
631 }
632 readyToShow();
633 assert.deepEqual(fakeWindow.setBackgroundThrottling.mock.calls, [[true]]);
634 }).pipe(Effect.provide(layer));
635 }),
636 );
637
607638 it.effect("debounces move and resize bounds updates", () =>
608639 Effect.gen(function* () {
609640 const fakeWindow = makeFakeBrowserWindow();
apps/desktop/src/window/DesktopWindow.ts+11
......@@ -359,6 +359,12 @@ export const make = Effect.gen(function* () {
359359 ...getWindowTitleBarOptions(shouldUseDarkColors, environment.platform),
360360 webPreferences: {
361361 preload: environment.preloadPath,
362 // The window boots hidden (show: false until ready-to-show), and
363 // Chromium throttles hidden renderers: timers coalesce and rAF stops,
364 // which stalls first paint. Boot unthrottled; the first-reveal trigger
365 // re-enables throttling so a hidden or minimized window goes back to
366 // being cheap after it has been shown once.
367 backgroundThrottling: false,
362368 contextIsolation: true,
363369 nodeIntegration: false,
364370 sandbox: true,
......@@ -725,6 +731,11 @@ export const make = Effect.gen(function* () {
725731 revealSubscribers.push((fire) => window.webContents.once("did-finish-load", fire));
726732 }
727733 bindFirstRevealTrigger(revealSubscribers, () => {
734 // Boot is done; hand the window back to normal hidden-window throttling
735 // (see the backgroundThrottling comment on the create options above).
736 if (!window.isDestroyed()) {
737 window.webContents.setBackgroundThrottling(true);
738 }
728739 // Reveal the real window, then close the connecting splash (if any) so the
729740 // two don't overlap and there's no blank gap between them.
730741 if (persistedSettings.mainWindowMaximized) {