Add accessible interface font choices (#1306)

This commit is contained in:
ZacharyZcR
2026-08-24 01:08:03 +08:00
committed by GitHub
parent f1226b1f1b
commit 5e76aec2bf
8 changed files with 119 additions and 4 deletions
+6 -2
View File
@@ -9,9 +9,9 @@ import { isElectron } from "@/lib/electron";
import { Toaster } from "@/components/sonner";
import { Auth, getStoredAuth, clearStoredAuth } from "@/auth/Auth";
import { getUserInfo, getCurrentToken, appReadyPromise } from "@/main-axios";
import { applyAccentColor, applyFontSize } from "@/lib/theme";
import { applyAccentColor, applyFontSize, applyUiFont } from "@/lib/theme";
import { installElectronWheelZoomGuard } from "@/lib/electron-wheel-zoom";
import type { FontSizeId } from "@/types/ui-types";
import type { FontSizeId, UiFontId } from "@/types/ui-types";
import { useServiceWorker } from "@/hooks/use-service-worker";
import { useTranslation } from "react-i18next";
import { UiPreferencesProvider } from "@/contexts/UiPreferencesContext";
@@ -202,6 +202,10 @@ function App() {
"termix-font-size",
) as FontSizeId | null;
applyFontSize(savedSize ?? "md");
applyUiFont(
(localStorage.getItem("termix-ui-font") as UiFontId | null) ??
"jetbrains-mono",
);
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
+6
View File
@@ -357,6 +357,12 @@ export type ThemeId =
| "one-dark"
| "gruvbox";
export type FontSizeId = "xs" | "sm" | "md" | "lg" | "xl";
export type UiFontId =
| "jetbrains-mono"
| "system-sans"
| "fira-code"
| "source-code-pro"
| "caskaydia-cove";
export type ToolsTab =
"ssh-tools" | "snippets" | "macros" | "history" | "split-screen";
+1
View File
@@ -861,6 +861,7 @@ export function AppShell({
const SNAPSHOT_KEYS = [
"termix-accent",
"termix-font-size",
"termix-ui-font",
"i18nextLng",
"commandAutocomplete",
"commandPaletteShortcutEnabled",
+1 -1
View File
@@ -55,7 +55,7 @@
@theme inline {
--font-heading: var(--font-mono);
--font-mono: "JetBrains Mono Variable", monospace;
--font-mono: var(--font-ui, "JetBrains Mono Variable", monospace);
--color-accent-brand: var(--accent-brand);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
+35
View File
@@ -1,6 +1,7 @@
import type {
DashboardCardConfig,
FontSizeId,
UiFontId,
SplitMode,
} from "@/types/ui-types";
@@ -89,6 +90,40 @@ export function applyFontSize(id: FontSizeId) {
localStorage.setItem("termix-font-size", id);
}
export const UI_FONTS: { id: UiFontId; label: string; family: string }[] = [
{
id: "jetbrains-mono",
label: "JetBrains Mono",
family: '"JetBrains Mono Variable", monospace',
},
{
id: "system-sans",
label: "System Sans",
family: "ui-sans-serif, system-ui, sans-serif",
},
{
id: "fira-code",
label: "Fira Code",
family: '"Fira Code", monospace',
},
{
id: "source-code-pro",
label: "Source Code Pro",
family: '"Source Code Pro", monospace',
},
{
id: "caskaydia-cove",
label: "Caskaydia Cove",
family: '"Caskaydia Cove Nerd Font Mono", monospace',
},
];
export function applyUiFont(id: UiFontId) {
const font = UI_FONTS.find((candidate) => candidate.id === id) ?? UI_FONTS[0];
document.documentElement.style.setProperty("--font-ui", font.family);
localStorage.setItem("termix-ui-font", font.id);
}
export const FOLDER_COLORS = [
"#ef4444",
"#f97316",
+2
View File
@@ -4150,6 +4150,8 @@
"languageLabel": "Language",
"themeLabel": "Theme",
"fontSizeLabel": "Font Size",
"interfaceFontLabel": "Interface Font",
"interfaceFontDescription": "Changes the application interface font. Terminal profiles keep their own font settings.",
"accentColorLabel": "Accent Color",
"settingsTerminal": "Terminal",
"commandAutocomplete": "Command Autocomplete",
+44 -1
View File
@@ -77,11 +77,13 @@ import {
ACCENT_PRESET_COLORS,
applyAccentColor,
applyFontSize,
applyUiFont,
FONT_SIZES,
UI_FONTS,
} from "@/lib/theme";
import type { ApiKey } from "@/main-axios";
import { useTheme } from "@/components/theme-provider";
import type { FontSizeId, ThemeId } from "@/types/ui-types";
import type { FontSizeId, ThemeId, UiFontId } from "@/types/ui-types";
import { toast } from "sonner";
import { changeAppLanguage, normalizeLanguageCode } from "@/i18n/i18n";
import { clearLocalAdaptivePreferences } from "@/lib/local-adaptive-preferences";
@@ -597,6 +599,10 @@ export function UserProfilePanel({
const [fontSize, setFontSize] = useState<FontSizeId>(
() => (localStorage.getItem("termix-font-size") as FontSizeId) ?? "md",
);
const [uiFont, setUiFont] = useState<UiFontId>(
() =>
(localStorage.getItem("termix-ui-font") as UiFontId) ?? "jetbrains-mono",
);
const [language, setLanguage] = useState(() =>
normalizeLanguageCode(localStorage.getItem("i18nextLng")),
);
@@ -831,6 +837,7 @@ export function UserProfilePanel({
const SNAPSHOT_KEYS = [
"termix-accent",
"termix-font-size",
"termix-ui-font",
"i18nextLng",
"commandAutocomplete",
"commandPaletteShortcutEnabled",
@@ -961,6 +968,8 @@ export function UserProfilePanel({
setTheme("system");
setFontSize("md");
applyFontSize("md");
setUiFont("jetbrains-mono");
applyUiFont("jetbrains-mono");
setAccentColor(DEFAULT_ACCENT);
setCustomColorInput(DEFAULT_ACCENT);
localStorage.setItem("termix-accent", DEFAULT_ACCENT);
@@ -1047,6 +1056,12 @@ export function UserProfilePanel({
setFontSize(restoredFontSize);
applyFontSize(restoredFontSize);
const restoredUiFont =
(restore("termix-ui-font", "jetbrains-mono") as UiFontId) ??
"jetbrains-mono";
setUiFont(restoredUiFont);
applyUiFont(restoredUiFont);
const restoredAccent = restore("termix-accent", "#f59145") ?? "#f59145";
setAccentColor(restoredAccent);
setCustomColorInput(restoredAccent);
@@ -1156,6 +1171,11 @@ export function UserProfilePanel({
if (storageMode === "cloud") saveToCloud({ fontSize: id });
}
function handleUiFontChange(id: UiFontId) {
setUiFont(id);
applyUiFont(id);
}
function handleLanguageChange(code: string) {
void changeAppLanguage(code)
.then((language) => {
@@ -1723,6 +1743,29 @@ export function UserProfilePanel({
</div>
</div>
<div className="flex flex-col gap-1.5">
<span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5">
<Type className="size-3" />
{t("newUi.sidebar.userProfile.interfaceFontLabel")}
</span>
<select
value={uiFont}
onChange={(event) =>
handleUiFontChange(event.target.value as UiFontId)
}
className="px-2.5 py-1.5 text-xs bg-background border border-border text-foreground outline-none focus:ring-1 focus:ring-ring w-full"
>
{UI_FONTS.map((font) => (
<option key={font.id} value={font.id}>
{font.label}
</option>
))}
</select>
<span className="text-[10px] text-muted-foreground">
{t("newUi.sidebar.userProfile.interfaceFontDescription")}
</span>
</div>
<div className="flex flex-col gap-1.5">
<span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5">
<Type className="size-3" />
+24
View File
@@ -0,0 +1,24 @@
import { beforeEach, describe, expect, it } from "vitest";
import { applyUiFont } from "../../lib/theme";
describe("applyUiFont", () => {
beforeEach(() => {
localStorage.clear();
document.documentElement.style.removeProperty("--font-ui");
});
it("applies and persists a readable system font", () => {
applyUiFont("system-sans");
expect(document.documentElement.style.getPropertyValue("--font-ui")).toBe(
"ui-sans-serif, system-ui, sans-serif",
);
expect(localStorage.getItem("termix-ui-font")).toBe("system-sans");
});
it("falls back to JetBrains Mono for an unknown stored value", () => {
applyUiFont("unknown" as never);
expect(localStorage.getItem("termix-ui-font")).toBe("jetbrains-mono");
});
});