From c3a6a2950aa93127b04066ea764c9781b2b96137 Mon Sep 17 00:00:00 2001 From: Hikari Date: Tue, 3 Mar 2026 19:36:35 -0800 Subject: [PATCH] fix: apply custom UI font to full app interface - Set document.body.style.fontFamily directly (belt-and-suspenders alongside the CSS variable) to ensure the font propagates - Use var(--ui-font-family) in .app-container so the main app area respects the custom font (was hardcoded, blocking inheritance) - Rename 'Custom Font' setting to 'Custom Terminal Font' for clarity --- src/lib/components/ConfigSidebar.svelte | 4 ++-- src/lib/stores/config.test.ts | 32 +++++++++++++++++++------ src/lib/stores/config.ts | 11 +++++---- src/routes/+page.svelte | 6 +++-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/lib/components/ConfigSidebar.svelte b/src/lib/components/ConfigSidebar.svelte index 0047489..1328fbb 100644 --- a/src/lib/components/ConfigSidebar.svelte +++ b/src/lib/components/ConfigSidebar.svelte @@ -953,9 +953,9 @@

- +
- Custom Font + Custom Terminal Font
{ beforeEach(() => { document.getElementById("hikari-custom-ui-font")?.remove(); document.documentElement.style.removeProperty("--ui-font-family"); + document.body.style.removeProperty("font-family"); readFileMock.mockReset(); }); - it("removes CSS variable when both path and family are null", async () => { + it("removes CSS variable and body font-family when both path and family are null", async () => { document.documentElement.style.setProperty("--ui-font-family", "'SomeFont', sans-serif"); + document.body.style.setProperty("font-family", "'SomeFont', sans-serif"); await applyCustomUiFont(null, null); expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(""); + expect(document.body.style.getPropertyValue("font-family")).toBe(""); }); - it("removes CSS variable when both path and family are empty strings", async () => { + it("removes CSS variable and body font-family when both path and family are empty strings", async () => { document.documentElement.style.setProperty("--ui-font-family", "'SomeFont', sans-serif"); + document.body.style.setProperty("font-family", "'SomeFont', sans-serif"); await applyCustomUiFont("", ""); expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(""); + expect(document.body.style.getPropertyValue("font-family")).toBe(""); expect(document.getElementById("hikari-custom-ui-font")).toBeNull(); }); - it("injects @import for a CSS stylesheet URL", async () => { + it("injects @import for a CSS stylesheet URL and applies font to body", async () => { await applyCustomUiFont("https://fonts.googleapis.com/css2?family=Inter", "Inter"); const style = document.getElementById("hikari-custom-ui-font"); expect(style).not.toBeNull(); @@ -1357,9 +1362,10 @@ describe("config store", () => { expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( "'Inter', sans-serif" ); + expect(document.body.style.getPropertyValue("font-family")).toBe('"Inter", sans-serif'); }); - it("injects @font-face for a direct font file URL (.woff2)", async () => { + it("injects @font-face for a direct font file URL (.woff2) and applies font to body", async () => { await applyCustomUiFont("https://example.com/fonts/Inter.woff2", "Inter"); const style = document.getElementById("hikari-custom-ui-font"); expect(style).not.toBeNull(); @@ -1368,15 +1374,19 @@ describe("config store", () => { expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( "'Inter', sans-serif" ); + expect(document.body.style.getPropertyValue("font-family")).toBe('"Inter", sans-serif'); }); - it("uses HikariCustomUiFont as fallback family for direct font URLs when family is empty", async () => { + it("uses HikariCustomUiFont as fallback family and applies it to body", async () => { await applyCustomUiFont("https://example.com/fonts/Inter.woff2", ""); const style = document.getElementById("hikari-custom-ui-font"); expect(style?.textContent).toContain("'HikariCustomUiFont'"); + expect(document.body.style.getPropertyValue("font-family")).toBe( + '"HikariCustomUiFont", sans-serif' + ); }); - it("reads local file and embeds as data URL", async () => { + it("reads local file and embeds as data URL, applies font to body", async () => { const fakeData = new Uint8Array([104, 101, 108, 108, 111]); readFileMock.mockResolvedValueOnce(fakeData); @@ -1388,13 +1398,17 @@ describe("config store", () => { expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( "'Inter', sans-serif" ); + expect(document.body.style.getPropertyValue("font-family")).toBe('"Inter", sans-serif'); }); - it("sets CSS variable when only family is provided (no path)", async () => { + it("sets CSS variable and body font-family when only family is provided (no path)", async () => { await applyCustomUiFont("", "SystemUiFont"); expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( "'SystemUiFont', sans-serif" ); + expect(document.body.style.getPropertyValue("font-family")).toBe( + '"SystemUiFont", sans-serif' + ); }); it("replaces a previously injected style element", async () => { @@ -1415,6 +1429,7 @@ describe("config store", () => { beforeEach(() => { document.getElementById("hikari-custom-ui-font")?.remove(); document.documentElement.style.removeProperty("--ui-font-family"); + document.body.style.removeProperty("font-family"); readFileMock.mockReset(); invokeMock.mockResolvedValue(undefined); }); @@ -1435,13 +1450,16 @@ describe("config store", () => { expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( "'Inter', sans-serif" ); + expect(document.body.style.getPropertyValue("font-family")).toBe('"Inter", sans-serif'); }); it("clears UI font when called with nulls", async () => { document.documentElement.style.setProperty("--ui-font-family", "'SomeFont', sans-serif"); + document.body.style.setProperty("font-family", "'SomeFont', sans-serif"); await configStore.setCustomUiFont(null, null); expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(""); + expect(document.body.style.getPropertyValue("font-family")).toBe(""); expect(invokeMock).toHaveBeenCalledWith( "save_config", expect.objectContaining({ diff --git a/src/lib/stores/config.ts b/src/lib/stores/config.ts index 001d2f5..bd71637 100644 --- a/src/lib/stores/config.ts +++ b/src/lib/stores/config.ts @@ -404,16 +404,19 @@ export async function applyCustomUiFont(path: string | null, family: string | nu if (!trimmedPath && !trimmedFamily) { document.documentElement.style.removeProperty(cssVar); + document.body?.style.removeProperty("font-family"); return; } + const effectiveFamily = trimmedFamily || fallbackFamily; + if (trimmedPath) { - await applyFontFromSource(trimmedPath, trimmedFamily || fallbackFamily, styleId); + await applyFontFromSource(trimmedPath, effectiveFamily, styleId); } - if (trimmedFamily) { - document.documentElement.style.setProperty(cssVar, `'${trimmedFamily}', sans-serif`); - } + const fontValue = `'${effectiveFamily}', sans-serif`; + document.documentElement.style.setProperty(cssVar, fontValue); + document.body?.style.setProperty("font-family", fontValue); } export { MIN_FONT_SIZE, MAX_FONT_SIZE, DEFAULT_FONT_SIZE }; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index d2c777d..4fb5754 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -602,13 +602,15 @@