From 963c79265aaf7f4774aafe6aa66a1232bebebbcb Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 1 Apr 2026 18:33:17 +0700 Subject: [PATCH] fix(desktop): eliminate white flash on window resize in dark mode Set native window backgroundColor to match the app's surface0 color so the backing layer is dark during resize repaints. Extend the existing updateWindowControls IPC to also call setBackgroundColor on all platforms (including macOS), keeping the renderer as the single source of truth for theme resolution. Add a prefers-color-scheme CSS rule in index.html to cover the HTML-to-React mount gap. --- packages/app/public/index.html | 6 ++++++ packages/desktop/src/main.ts | 2 ++ packages/desktop/src/window/window-manager.ts | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/app/public/index.html b/packages/app/public/index.html index 12161df30..688fb0609 100644 --- a/packages/app/public/index.html +++ b/packages/app/public/index.html @@ -19,6 +19,12 @@ body { overflow: hidden; } + /* Prevent white flash before React mounts */ + @media (prefers-color-scheme: dark) { + html, body { + background-color: #181B1A; + } + } /* These styles make the root element full-height */ #root { display: flex; diff --git a/packages/desktop/src/main.ts b/packages/desktop/src/main.ts index f368c696c..e2dce43f4 100644 --- a/packages/desktop/src/main.ts +++ b/packages/desktop/src/main.ts @@ -14,6 +14,7 @@ import { closeAllTransportSessions } from "./daemon/local-transport.js"; import { registerWindowManager, getMainWindowChromeOptions, + getWindowBackgroundColor, resolveSystemWindowTheme, setupWindowResizeEvents, setupDragDropPrevention, @@ -93,6 +94,7 @@ async function createMainWindow(): Promise { width: 1200, height: 800, show: false, + backgroundColor: getWindowBackgroundColor(systemTheme), ...(iconPath ? { icon: iconPath } : {}), ...getMainWindowChromeOptions({ platform: process.platform, diff --git a/packages/desktop/src/window/window-manager.ts b/packages/desktop/src/window/window-manager.ts index f7dee555c..ec02e8968 100644 --- a/packages/desktop/src/window/window-manager.ts +++ b/packages/desktop/src/window/window-manager.ts @@ -33,6 +33,10 @@ export function resolveSystemWindowTheme(): WindowTheme { return nativeTheme.shouldUseDarkColors ? "dark" : "light"; } +export function getWindowBackgroundColor(theme: WindowTheme): string { + return theme === "dark" ? "#181B1A" : "#ffffff"; +} + export function createWindowControlsOverlayState(theme: WindowTheme): WindowControlsOverlayState { const overlay = getTitleBarOverlayOptions(theme); return { @@ -44,7 +48,7 @@ export function createWindowControlsOverlayState(theme: WindowTheme): WindowCont export function getTitleBarOverlayOptions(theme: WindowTheme): Electron.TitleBarOverlayOptions { if (theme === "dark") { - return { color: "#18181c", symbolColor: "#e4e4e7", height: 29 }; + return { color: "#181B1A", symbolColor: "#e4e4e7", height: 29 }; } return { color: "#ffffff", symbolColor: "#09090b", height: 29 }; @@ -171,7 +175,7 @@ export function registerWindowManager(): void { ipcMain.handle("paseo:window:updateWindowControls", (event, update?: unknown) => { const win = BrowserWindow.fromWebContents(event.sender); - if (!win || process.platform === "darwin") { + if (!win) { return; } @@ -180,6 +184,14 @@ export function registerWindowManager(): void { return; } + if (nextUpdate.backgroundColor) { + win.setBackgroundColor(nextUpdate.backgroundColor); + } + + if (process.platform === "darwin") { + return; + } + const current = overlayStateByWindow.get(win) ?? createWindowControlsOverlayState(resolveSystemWindowTheme()); const nextState = applyWindowControlsOverlayUpdate({