diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index 772484bff8c..5d1f35ba450 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -527,34 +527,30 @@ function getWindowBackgroundColor() { return nativeTheme.shouldUseDarkColors ? '#111111' : '#f7f7f7' } +// Transparent WCO — renderer chrome shows through. rgba(0,0,0,0) can fall back +// to GetFrameColor() on some Electron builds; rgba(1,0,0,0) is the escape hatch. +const TITLEBAR_OVERLAY_COLOR = 'rgba(1, 0, 0, 0)' + function getTitleBarOverlayOptions() { if (IS_MAC) { return { height: TITLEBAR_HEIGHT } } - // Window Controls Overlay: Windows paints it natively, and WSLg honors it too - // (it renders through a real compositor), so keep it enabled there — disabling - // it removes the min/max/close buttons entirely. Only plain (non-WSL) Linux, - // where some WMs/builds don't support WCO, falls through to no overlay; the - // frameless titleBarStyle 'hidden' still applies. + // Windows + WSLg paint WCO natively; plain Linux disables it (frameless hidden + // titlebar still applies). if (!IS_WINDOWS && !IS_WSL) { return false } - if (rendererTitleBarTheme) { - return { - color: rendererTitleBarTheme.background, - height: TITLEBAR_HEIGHT, - symbolColor: rendererTitleBarTheme.foreground - } - } - - const useDarkColors = nativeTheme.shouldUseDarkColors - return { - color: useDarkColors ? '#111111' : '#f7f7f7', + color: TITLEBAR_OVERLAY_COLOR, height: TITLEBAR_HEIGHT, - symbolColor: useDarkColors ? '#f7f7f7' : '#242424' + symbolColor: + rendererTitleBarTheme && isHexColor(rendererTitleBarTheme.foreground) + ? rendererTitleBarTheme.foreground + : nativeTheme.shouldUseDarkColors + ? '#f7f7f7' + : '#242424' } } diff --git a/apps/desktop/src/app/shell/app-shell.tsx b/apps/desktop/src/app/shell/app-shell.tsx index c8405aadc84..56deb913952 100644 --- a/apps/desktop/src/app/shell/app-shell.tsx +++ b/apps/desktop/src/app/shell/app-shell.tsx @@ -189,6 +189,13 @@ export function AppShell({ )} + {nativeOverlayWidth > 0 && ( +
+ )} +
+ mix(background, NEUTRAL_CHROME[isDark ? 'dark' : 'light'], isDark ? 0.26 : 0.08) + const mixesFor = (isDark: boolean): Record => ({ '--theme-mix-chrome': isDark ? '74%' : '92%', '--theme-mix-sidebar': '100%', @@ -222,8 +228,10 @@ function applyTheme(theme: DesktopTheme, mode: 'light' | 'dark') { root.style.setProperty(k, v) } + const chromeBg = chromeBackground(c.background, isDark) + window.hermesDesktop?.setTitleBarTheme?.({ - background: c.background, + background: chromeBg, foreground: c.foreground }) @@ -231,7 +239,7 @@ function applyTheme(theme: DesktopTheme, mode: 'light' | 'dark') { // they let a brand-new window paint the themed background on its very first // frame, before this module has even loaded. try { - window.localStorage.setItem('hermes-boot-background', c.background) + window.localStorage.setItem('hermes-boot-background', chromeBg) window.localStorage.setItem('hermes-boot-color-scheme', rendered) } catch { // Storage may be unavailable (private mode / quota); the inline script