Compare commits

...
Author SHA1 Message Date
陈大猫 2c0c1c77fd ci: retrigger lint-and-test after 20m job timeout cancel (3) 2026-11-04 23:42:23 +08:00
陈大猫 37c0f491aa ci: retrigger lint-and-test after container-runtime-vite ETIMEDOUT flake (2) 2026-11-04 23:14:44 +08:00
陈大猫 6161e1ece3 ci: retrigger lint-and-test after vite ETIMEDOUT flake 2026-11-04 22:36:06 +08:00
陈大猫 0f061b76dd fix(ui): keep follow-system themeSource from chrome override
Preserve the published system preference when refreshing active chrome surfaces while continuing to apply the chrome background and explicit light/dark title bar behavior.

Chrome setTheme(light|dark) locked nativeTheme.themeSource, so matchMedia change events never fired when macOS switched appearance.

Fixes #4130
2026-11-04 22:10:52 +08:00
2 changed files with 62 additions and 2 deletions

No files matched your search

@@ -11,6 +11,10 @@ import {
} from "./useActiveChromeTheme.ts";
import { TERMINAL_THEMES } from "../../infrastructure/config/terminalThemes.ts";
import { readFileSync } from "node:fs";
import {
getSettingsChromeSnapshot,
publishSettingsChromeSnapshot,
} from "./settingsChromeStore.ts";
function createInlineStyle() {
const values = new Map<string, string>();
@@ -98,6 +102,60 @@ test("syncActiveChromeTheme skips surface refresh when the active theme fingerpr
}
});
for (const preference of ["system", "light", "dark"] as const) {
for (const chromeType of ["light", "dark"] as const) {
test(`syncActiveChromeTheme uses ${preference === "system" ? "system" : chromeType} native theme for ${preference} preference and ${chromeType} chrome`, () => {
const globals = globalThis as typeof globalThis & { document?: Document; window?: Window };
const originalDocument = globals.document;
const originalWindow = globals.window;
const originalSnapshot = getSettingsChromeSnapshot();
const theme = TERMINAL_THEMES.find((candidate) => candidate.type === chromeType);
assert.ok(theme);
const themeCalls: string[] = [];
const backgroundCalls: string[] = [];
globals.document = {
documentElement: {
dataset: {},
classList: { remove: () => {}, add: () => {} },
removeAttribute: () => {},
},
getElementById: () => null,
createElement: () => ({}),
head: { appendChild: () => {} },
querySelector: () => null,
} as unknown as Document;
globals.window = {
netcatty: {
setTheme: (value: string) => themeCalls.push(value),
setBackgroundColor: (value: string) => backgroundCalls.push(value),
},
} as unknown as Window & typeof globalThis;
try {
publishSettingsChromeSnapshot({ ...originalSnapshot, theme: preference });
syncActiveChromeTheme(theme, () => {
throw new Error("app theme should not be restored for an active chrome theme");
});
assert.deepEqual(themeCalls, [preference === "system" ? "system" : chromeType]);
assert.deepEqual(backgroundCalls, [theme.colors.background]);
} finally {
publishSettingsChromeSnapshot(originalSnapshot);
if (originalDocument) {
globals.document = originalDocument;
} else {
delete globals.document;
}
if (originalWindow) {
globals.window = originalWindow;
} else {
delete globals.window;
}
}
});
}
}
test("active chrome theme foreground uses contrast calculation", () => {
const source = readFileSync(new URL("./useActiveChromeTheme.ts", import.meta.url), "utf8");
+4 -2
View File
@@ -1,6 +1,6 @@
import { pluginThemeRegistry } from '../../infrastructure/config/pluginThemeRegistry';
import { usePluginThemeRevision } from './usePluginThemeRuntime';
import { useSettingsChromeStore } from './settingsChromeStore';
import { getSettingsChromeSnapshot, useSettingsChromeStore } from './settingsChromeStore';
import { useLayoutEffect, useRef } from "react";
import type { TerminalTheme } from "../../domain/models";
import {
@@ -239,7 +239,9 @@ function applyActiveChromeTheme(theme: TerminalTheme) {
function refreshActiveChromeThemeSurfaces(theme: TerminalTheme) {
const targetClass = theme.type === "dark" ? "dark" : "light";
if (typeof window !== "undefined") {
netcattyBridge.get()?.setTheme?.(targetClass);
// Preserve OS preference events instead of locking nativeTheme to the chrome type.
const themeSource = getSettingsChromeSnapshot().theme === "system" ? "system" : targetClass;
netcattyBridge.get()?.setTheme?.(themeSource);
netcattyBridge.get()?.setBackgroundColor?.(theme.colors.background);
}
applyTopTabsChromeThemeVars(theme);