feat(desktop): deliver font settings to plugin sandboxes
This commit is contained in:
1 parent
ba6b74beea
commit
b1b99eccfb
15 files changed
+309
-25
No files matched your search
@@ -175,4 +175,55 @@ describe("useTheme on Linux", () => {
|
||||
expect(theme.customUiColors.value.background).toBe("#ffffff");
|
||||
expect(theme.customUiColorsDark.value.background).toBe("#1a1b1e");
|
||||
});
|
||||
|
||||
it("bumps the theme revision so out-of-band token writers (font settings) reach plugin bridges", async () => {
|
||||
const theme = await loadTheme("light");
|
||||
const before = theme.themeRevision.value;
|
||||
|
||||
theme.bumpThemeRevision();
|
||||
|
||||
expect(theme.themeRevision.value).toBe(before + 1);
|
||||
// No theme classes are touched: this is a pure revision bump.
|
||||
expect(document.documentElement.classList.contains("disable-transitions")).toBe(false);
|
||||
});
|
||||
|
||||
it("writeRootToken writes the inline override and bumps the revision", async () => {
|
||||
const theme = await loadTheme("light");
|
||||
const before = theme.themeRevision.value;
|
||||
|
||||
theme.writeRootToken("--dbx-test-token", "1px");
|
||||
|
||||
expect(document.documentElement.style.getPropertyValue("--dbx-test-token")).toBe("1px");
|
||||
expect(theme.themeRevision.value).toBe(before + 1);
|
||||
document.documentElement.style.removeProperty("--dbx-test-token");
|
||||
});
|
||||
|
||||
it("writeRootToken coalesces rapid writes into one debounced bump", async () => {
|
||||
const theme = await loadTheme("light");
|
||||
const before = theme.themeRevision.value;
|
||||
|
||||
theme.writeRootToken("--dbx-test-token", "1px", { debounceMs: 30 });
|
||||
theme.writeRootToken("--dbx-test-token", "2px", { debounceMs: 30 });
|
||||
// Neither bump has fired yet, but the write itself is immediate.
|
||||
expect(theme.themeRevision.value).toBe(before);
|
||||
expect(document.documentElement.style.getPropertyValue("--dbx-test-token")).toBe("2px");
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 60));
|
||||
expect(theme.themeRevision.value).toBe(before + 1);
|
||||
document.documentElement.style.removeProperty("--dbx-test-token");
|
||||
});
|
||||
|
||||
it("writeRootToken with an immediate write supersedes a pending debounced bump", async () => {
|
||||
const theme = await loadTheme("light");
|
||||
const before = theme.themeRevision.value;
|
||||
|
||||
theme.writeRootToken("--dbx-test-token", "1px", { debounceMs: 30 });
|
||||
theme.writeRootToken("--dbx-test-token", "2px");
|
||||
expect(theme.themeRevision.value).toBe(before + 1);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 60));
|
||||
// The superseded timer must not fire a second bump.
|
||||
expect(theme.themeRevision.value).toBe(before + 1);
|
||||
document.documentElement.style.removeProperty("--dbx-test-token");
|
||||
});
|
||||
});
|
||||
@@ -63,6 +63,39 @@ const isDark = computed(() => resolveAppThemeAppearance(themeMode.value, systemP
|
||||
// resolved token set (plugin iframe bridge) watch this revision instead.
|
||||
const themeRevision = ref(0);
|
||||
|
||||
// Token mutations on the root that happen outside applyTheme() — e.g. the UI
|
||||
// and editor font settings write --font-sans / --font-mono inline — bump this
|
||||
// so open plugin workbench bridges re-read and re-push the resolved tokens.
|
||||
function bumpThemeRevision() {
|
||||
themeRevision.value += 1;
|
||||
}
|
||||
|
||||
let pendingBumpTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
// The one sanctioned way to write a design token onto the root outside
|
||||
// applyTheme(): writes the inline override AND bumps the theme revision.
|
||||
// Writing root tokens directly (documentElement.style.setProperty) skips the
|
||||
// bump and open plugin bridges keep stale tokens — always go through here.
|
||||
// `debounceMs` coalesces rapid writes (font-family preview keystrokes) into
|
||||
// one bump; an immediate write afterwards supersedes the pending one.
|
||||
function writeRootToken(cssVar: string, value: string, options?: { debounceMs?: number }) {
|
||||
if (typeof document === "undefined") return;
|
||||
document.documentElement.style.setProperty(cssVar, value);
|
||||
if (options?.debounceMs) {
|
||||
if (pendingBumpTimer) clearTimeout(pendingBumpTimer);
|
||||
pendingBumpTimer = setTimeout(() => {
|
||||
pendingBumpTimer = undefined;
|
||||
bumpThemeRevision();
|
||||
}, options.debounceMs);
|
||||
return;
|
||||
}
|
||||
if (pendingBumpTimer) {
|
||||
clearTimeout(pendingBumpTimer);
|
||||
pendingBumpTimer = undefined;
|
||||
}
|
||||
bumpThemeRevision();
|
||||
}
|
||||
|
||||
let mediaQuery: MediaQueryList | null = null;
|
||||
let isListeningForSystemTheme = false;
|
||||
let cachedTauriWindow: typeof import("@tauri-apps/api/window") | null = null;
|
||||
@@ -212,5 +245,25 @@ export function useTheme() {
|
||||
setThemeMode(isDark.value ? "light" : "dark");
|
||||
}
|
||||
|
||||
return { isDark, themeMode, themePalette, customUiColors, customUiColorsDark, activeCustomUiColors, themeRevision, cornerStyle, applyTheme, setThemeMode, setThemePalette, previewThemePalette, clearThemePalettePreview, setCustomUiColors, resetCustomUiColors, setCornerStyle, toggleTheme };
|
||||
return {
|
||||
isDark,
|
||||
themeMode,
|
||||
themePalette,
|
||||
customUiColors,
|
||||
customUiColorsDark,
|
||||
activeCustomUiColors,
|
||||
themeRevision,
|
||||
bumpThemeRevision,
|
||||
writeRootToken,
|
||||
cornerStyle,
|
||||
applyTheme,
|
||||
setThemeMode,
|
||||
setThemePalette,
|
||||
previewThemePalette,
|
||||
clearThemePalettePreview,
|
||||
setCustomUiColors,
|
||||
resetCustomUiColors,
|
||||
setCornerStyle,
|
||||
toggleTheme,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user