From ee9c37bea8fffc52c12ec3514e78109c032dfebd Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 22 Apr 2026 15:53:57 +0300 Subject: [PATCH] fix(electron): follow OS theme changes when Color Mode = system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit desktop_set_window_theme and readThemeSource both checked themeVariant before themeMode. When UI sends mode='system' with variant='dark' (the resolved appearance at call time), main pinned nativeTheme.themeSource to 'dark' — freezing Chromium's prefers-color-scheme and blocking the renderer's matchMedia listener from reacting to OS theme changes. Priority now: mode='system' → themeSource='system' (ignore variant). Variant is only a fallback for callers that omit mode. --- packages/electron/main.mjs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/electron/main.mjs b/packages/electron/main.mjs index 1b3024c7..a0ae6279 100644 --- a/packages/electron/main.mjs +++ b/packages/electron/main.mjs @@ -1053,9 +1053,14 @@ const nextWindowLabel = () => { const readThemeSource = () => { const settings = readSettingsRoot(); - if (settings.useSystemTheme === true) return 'system'; - if (settings.themeMode === 'light' || settings.themeVariant === 'light') return 'light'; - if (settings.themeMode === 'dark' || settings.themeVariant === 'dark') return 'dark'; + // themeMode is the user's intent; themeVariant is only the resolved + // concrete appearance at persist time. When mode === 'system', we must + // follow the OS even if variant was saved as a specific value. + if (settings.themeMode === 'system' || settings.useSystemTheme === true) return 'system'; + if (settings.themeMode === 'light') return 'light'; + if (settings.themeMode === 'dark') return 'dark'; + if (settings.themeVariant === 'light') return 'light'; + if (settings.themeVariant === 'dark') return 'dark'; return 'system'; }; @@ -1828,9 +1833,24 @@ const handleInvoke = async (browserWindow, command, args = {}) => { case 'desktop_set_window_theme': { const mode = typeof args.themeMode === 'string' ? args.themeMode : ''; const variant = typeof args.themeVariant === 'string' ? args.themeVariant : ''; - nativeTheme.themeSource = mode === 'dark' || variant === 'dark' - ? 'dark' - : (mode === 'light' || variant === 'light' ? 'light' : 'system'); + // Priority order: themeMode expresses the user's intent (including + // "follow OS"). Variant is just the resolved variant at send time; + // when mode === 'system' with variant === 'dark' (because OS is + // currently dark), we must still pin themeSource to 'system' so + // Chromium keeps reacting to OS theme changes. + if (mode === 'system') { + nativeTheme.themeSource = 'system'; + } else if (mode === 'light') { + nativeTheme.themeSource = 'light'; + } else if (mode === 'dark') { + nativeTheme.themeSource = 'dark'; + } else if (variant === 'light') { + nativeTheme.themeSource = 'light'; + } else if (variant === 'dark') { + nativeTheme.themeSource = 'dark'; + } else { + nativeTheme.themeSource = 'system'; + } return null; }