fix: apply custom UI font to full app interface
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m32s
CI / Lint & Test (pull_request) Successful in 17m23s
CI / Build Linux (pull_request) Successful in 20m21s
CI / Build Windows (cross-compile) (pull_request) Successful in 30m18s

- 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
This commit is contained in:
2026-03-03 19:36:35 -08:00
committed by Naomi Carrigan
parent d2c39fd5c2
commit c3a6a2950a
4 changed files with 38 additions and 15 deletions
+2 -2
View File
@@ -953,9 +953,9 @@
</p> </p>
</div> </div>
<!-- Custom Font --> <!-- Custom Terminal Font -->
<div class="mb-4"> <div class="mb-4">
<span class="block text-sm text-[var(--text-secondary)] mb-2">Custom Font</span> <span class="block text-sm text-[var(--text-secondary)] mb-2">Custom Terminal Font</span>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<input <input
type="text" type="text"
+25 -7
View File
@@ -1331,23 +1331,28 @@ describe("config store", () => {
beforeEach(() => { beforeEach(() => {
document.getElementById("hikari-custom-ui-font")?.remove(); document.getElementById("hikari-custom-ui-font")?.remove();
document.documentElement.style.removeProperty("--ui-font-family"); document.documentElement.style.removeProperty("--ui-font-family");
document.body.style.removeProperty("font-family");
readFileMock.mockReset(); 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.documentElement.style.setProperty("--ui-font-family", "'SomeFont', sans-serif");
document.body.style.setProperty("font-family", "'SomeFont', sans-serif");
await applyCustomUiFont(null, null); await applyCustomUiFont(null, null);
expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(""); 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.documentElement.style.setProperty("--ui-font-family", "'SomeFont', sans-serif");
document.body.style.setProperty("font-family", "'SomeFont', sans-serif");
await applyCustomUiFont("", ""); await applyCustomUiFont("", "");
expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(""); 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(); 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"); await applyCustomUiFont("https://fonts.googleapis.com/css2?family=Inter", "Inter");
const style = document.getElementById("hikari-custom-ui-font"); const style = document.getElementById("hikari-custom-ui-font");
expect(style).not.toBeNull(); expect(style).not.toBeNull();
@@ -1357,9 +1362,10 @@ describe("config store", () => {
expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(
"'Inter', sans-serif" "'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"); await applyCustomUiFont("https://example.com/fonts/Inter.woff2", "Inter");
const style = document.getElementById("hikari-custom-ui-font"); const style = document.getElementById("hikari-custom-ui-font");
expect(style).not.toBeNull(); expect(style).not.toBeNull();
@@ -1368,15 +1374,19 @@ describe("config store", () => {
expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(
"'Inter', sans-serif" "'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", ""); await applyCustomUiFont("https://example.com/fonts/Inter.woff2", "");
const style = document.getElementById("hikari-custom-ui-font"); const style = document.getElementById("hikari-custom-ui-font");
expect(style?.textContent).toContain("'HikariCustomUiFont'"); 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]); const fakeData = new Uint8Array([104, 101, 108, 108, 111]);
readFileMock.mockResolvedValueOnce(fakeData); readFileMock.mockResolvedValueOnce(fakeData);
@@ -1388,13 +1398,17 @@ describe("config store", () => {
expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(
"'Inter', sans-serif" "'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"); await applyCustomUiFont("", "SystemUiFont");
expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(
"'SystemUiFont', sans-serif" "'SystemUiFont', sans-serif"
); );
expect(document.body.style.getPropertyValue("font-family")).toBe(
'"SystemUiFont", sans-serif'
);
}); });
it("replaces a previously injected style element", async () => { it("replaces a previously injected style element", async () => {
@@ -1415,6 +1429,7 @@ describe("config store", () => {
beforeEach(() => { beforeEach(() => {
document.getElementById("hikari-custom-ui-font")?.remove(); document.getElementById("hikari-custom-ui-font")?.remove();
document.documentElement.style.removeProperty("--ui-font-family"); document.documentElement.style.removeProperty("--ui-font-family");
document.body.style.removeProperty("font-family");
readFileMock.mockReset(); readFileMock.mockReset();
invokeMock.mockResolvedValue(undefined); invokeMock.mockResolvedValue(undefined);
}); });
@@ -1435,13 +1450,16 @@ describe("config store", () => {
expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe( expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(
"'Inter', sans-serif" "'Inter', sans-serif"
); );
expect(document.body.style.getPropertyValue("font-family")).toBe('"Inter", sans-serif');
}); });
it("clears UI font when called with nulls", async () => { it("clears UI font when called with nulls", async () => {
document.documentElement.style.setProperty("--ui-font-family", "'SomeFont', sans-serif"); document.documentElement.style.setProperty("--ui-font-family", "'SomeFont', sans-serif");
document.body.style.setProperty("font-family", "'SomeFont', sans-serif");
await configStore.setCustomUiFont(null, null); await configStore.setCustomUiFont(null, null);
expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe(""); expect(document.documentElement.style.getPropertyValue("--ui-font-family")).toBe("");
expect(document.body.style.getPropertyValue("font-family")).toBe("");
expect(invokeMock).toHaveBeenCalledWith( expect(invokeMock).toHaveBeenCalledWith(
"save_config", "save_config",
expect.objectContaining({ expect.objectContaining({
+7 -4
View File
@@ -404,16 +404,19 @@ export async function applyCustomUiFont(path: string | null, family: string | nu
if (!trimmedPath && !trimmedFamily) { if (!trimmedPath && !trimmedFamily) {
document.documentElement.style.removeProperty(cssVar); document.documentElement.style.removeProperty(cssVar);
document.body?.style.removeProperty("font-family");
return; return;
} }
const effectiveFamily = trimmedFamily || fallbackFamily;
if (trimmedPath) { if (trimmedPath) {
await applyFontFromSource(trimmedPath, trimmedFamily || fallbackFamily, styleId); await applyFontFromSource(trimmedPath, effectiveFamily, styleId);
} }
if (trimmedFamily) { const fontValue = `'${effectiveFamily}', sans-serif`;
document.documentElement.style.setProperty(cssVar, `'${trimmedFamily}', 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 }; export { MIN_FONT_SIZE, MAX_FONT_SIZE, DEFAULT_FONT_SIZE };
+4 -2
View File
@@ -602,13 +602,15 @@
<style> <style>
.app-container { .app-container {
font-family: font-family: var(
--ui-font-family,
"Inter", "Inter",
-apple-system, -apple-system,
BlinkMacSystemFont, BlinkMacSystemFont,
"Segoe UI", "Segoe UI",
Roboto, Roboto,
sans-serif; sans-serif
);
} }
.character-panel { .character-panel {