Skip to content

Commit 20b32fa

Browse files
committed
fix(themes): persist manual override per system appearance
When `autoThemeFollowsSystem` is on and the user manually picks a theme via `set-theme`, route the persistence to `darkTheme` or `lightTheme` based on the current macOS appearance instead of `theme`. Previously the choice was written to `theme`, which the poll loop ignored, so the manual override was silently overwritten on the next 3s tick. Also re-load `darkTheme` / `lightTheme` from disk inside the poll cycle so an override made via `set-theme` is picked up immediately on the next poll. Drops the `saveConfig({ theme: desired })` write inside the poll loop — that was clobbering the user's static-mode `theme` field whenever auto-follow ran. Co-authored-by: Isaac
1 parent 19ec1d9 commit 20b32fa

1 file changed

Lines changed: 24 additions & 3 deletions

File tree

packages/runtime/src/server/index.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,11 @@ export function startServer(mux: MuxProvider, extraProviders?: MuxProvider[], wa
306306
let currentTheme: string | undefined = typeof config.theme === "string" ? config.theme : undefined;
307307
let currentFilter: SessionFilterMode | undefined = config.sessionFilter;
308308
let systemThemePollTimer: ReturnType<typeof setInterval> | null = null;
309+
// Tracks the most recently observed macOS appearance while auto-follow is active.
310+
// Used by the `set-theme` handler so a manual override is persisted to the
311+
// appearance-specific slot, not to `theme` (which would be clobbered next poll).
312+
let autoThemeFollowing = false;
313+
let currentSystemMode: "dark" | "light" | undefined;
309314
const initialSidebarWidth = clampSidebarWidth(config.sidebarWidth ?? 26);
310315
let sidebarPosition: "left" | "right" = config.sidebarPosition ?? "left";
311316
const sidebarCoordinator = createSidebarCoordinator({ width: initialSidebarWidth });
@@ -2061,7 +2066,16 @@ export function startServer(mux: MuxProvider, extraProviders?: MuxProvider[], wa
20612066
break;
20622067
case "set-theme":
20632068
currentTheme = cmd.theme;
2064-
saveConfig({ theme: cmd.theme });
2069+
if (autoThemeFollowing) {
2070+
// When auto-follow is active, persist the manual choice to the
2071+
// appearance-specific slot so the next poll cycle does not silently
2072+
// overwrite it. Falls back to `theme` if mode hasn't been read yet.
2073+
if (currentSystemMode === "dark") saveConfig({ darkTheme: cmd.theme });
2074+
else if (currentSystemMode === "light") saveConfig({ lightTheme: cmd.theme });
2075+
else saveConfig({ theme: cmd.theme });
2076+
} else {
2077+
saveConfig({ theme: cmd.theme });
2078+
}
20652079
broadcastState();
20662080
break;
20672081
case "set-filter":
@@ -2614,16 +2628,23 @@ export function startServer(mux: MuxProvider, extraProviders?: MuxProvider[], wa
26142628
// every few seconds and flip between the configured dark/light themes.
26152629
// macOS does not expose a CLI change-notification; polling is cheap.
26162630
if (config.autoThemeFollowsSystem && process.platform === "darwin") {
2631+
autoThemeFollowing = true;
26172632
const darkTheme = config.darkTheme ?? "catppuccin-mocha";
26182633
const lightTheme = config.lightTheme ?? "catppuccin-latte";
26192634

26202635
async function syncSystemTheme() {
26212636
const mode = await readMacSystemAppearance();
2622-
const desired = themeForSystemMode(mode, darkTheme, lightTheme);
2637+
currentSystemMode = mode;
2638+
// Re-read the per-mode theme each cycle so a manual override via the
2639+
// `set-theme` handler (which writes to `darkTheme` / `lightTheme`) is
2640+
// picked up on the next poll instead of being silently overwritten.
2641+
const fresh = loadConfig();
2642+
const dark = fresh.darkTheme ?? darkTheme;
2643+
const light = fresh.lightTheme ?? lightTheme;
2644+
const desired = themeForSystemMode(mode, dark, light);
26232645
if (desired === currentTheme) return;
26242646
log("system-theme", "switching", { mode, from: currentTheme, to: desired });
26252647
currentTheme = desired;
2626-
saveConfig({ theme: desired });
26272648
broadcastState();
26282649
}
26292650

0 commit comments

Comments
 (0)