diff --git a/src/editor/core/colorSchemeRuntime.ts b/src/editor/core/colorSchemeRuntime.ts index 41e3a68f..443dc91c 100644 --- a/src/editor/core/colorSchemeRuntime.ts +++ b/src/editor/core/colorSchemeRuntime.ts @@ -2,7 +2,8 @@ * Applies Settings › Editor › Color Scheme (scheme + attribute overrides) as one * stylesheet, and Editor › Font › Typography Settings as the root's * `--fw-code` / `--fw-code-bold`. When the IDE theme turns light or dark, a chosen - * scheme of the other kind is switched back to the theme's own. + * scheme of the other kind is switched back to the theme's own, unless it was chosen + * in the same change as the theme (one Apply of both). */ import { useProject } from "@/platform/project"; import { editorPrefs, fontWeightVars } from "@/platform/editorSettings"; @@ -24,6 +25,10 @@ export function installColorScheme(): () => void { if (root.style.getPropertyValue(name) !== value) root.style.setProperty(name, value); }; let kind = root.dataset.theme; + // The scheme set by the latest settings change, until the next task: the theme's + // `data-theme` change from the same change is observed before then. + let picked: string | undefined; + let clearPicked: ReturnType | undefined; const followTheme = () => { const next = root.dataset.theme; if (next === kind) return; @@ -31,6 +36,7 @@ export function installColorScheme(): () => void { kind = next; const settings = useProject.getState().settings; if (!previous || !settings || (next !== "light" && next !== "dark")) return; + if (picked !== undefined && picked === settings.editor?.colorScheme) return; const scheme = schemeAfterThemeChange(settings.editor?.colorScheme, next, editorPrefs(settings).customSchemes); if (scheme === null) return; if (!isTauri()) { @@ -44,7 +50,13 @@ export function installColorScheme(): () => void { }; apply(); const unsubscribe = useProject.subscribe((s, prev) => { - if (s.settings !== prev.settings) apply(); + if (s.settings === prev.settings) return; + apply(); + const scheme = s.settings?.editor?.colorScheme; + if (scheme === prev.settings?.editor?.colorScheme) return; + picked = scheme; + clearTimeout(clearPicked); + clearPicked = setTimeout(() => (picked = undefined)); }); const observer = typeof MutationObserver !== "undefined" @@ -59,6 +71,7 @@ export function installColorScheme(): () => void { }); return () => { unsubscribe(); + clearTimeout(clearPicked); observer?.disconnect(); el.remove(); root.style.removeProperty("--fw-code"); diff --git a/src/platform/colorScheme.test.ts b/src/platform/colorScheme.test.ts index 67cecc22..08e95786 100644 --- a/src/platform/colorScheme.test.ts +++ b/src/platform/colorScheme.test.ts @@ -92,6 +92,19 @@ describe("color schemes", () => { root.dataset.theme = "dark"; await Promise.resolve(); expect(scheme()).toBe("dark"); + // A scheme chosen in the same change as the theme (one Apply) goes with it. + await new Promise((r) => setTimeout(r)); + useProject.setState({ settings: { editor: { colorScheme: "solarized-dark" } } as unknown as Settings }); + root.dataset.theme = "light"; + await Promise.resolve(); + expect(scheme()).toBe("solarized-dark"); + // A later theme change on its own still switches it back. + await new Promise((r) => setTimeout(r)); + root.dataset.theme = "dark"; + await Promise.resolve(); + root.dataset.theme = "light"; + await Promise.resolve(); + expect(scheme()).toBe("theme-default"); } finally { dispose(); delete root.dataset.colorTheme;