feat(dashboard): change UI font from the theme picker, independent of theme (#41145)

The dashboard font is now selectable from the UI, not just YAML. A new Font
section in the header theme picker overrides the UI font of whatever theme is
active; the choice is orthogonal to the theme and survives theme switches.
Each theme keeps its own font as the default — picking "Theme default" clears
the override.

- web/src/themes/fonts.ts: curated font catalog (system + Google Fonts across
  sans/serif/mono), each with a family stack and optional webfont URL. The
  catalog is the only injected-font surface — no free-text URL box, so the
  injected <link> origins stay fixed.
- web/src/themes/context.tsx: font-override state (localStorage + server),
  applied after theme typography so it wins; theme apply re-asserts it, and
  clearing re-runs theme apply to restore the theme's own font. Mono is left
  to the theme so code/terminal are untouched.
- web/src/components/ThemeSwitcher.tsx: Font section with grouped, self-
  previewing font rows and a "Theme default" clear option.
- hermes_cli/web_server.py: GET/PUT /api/dashboard/font persisting to
  config.yaml dashboard.font, with a server-side id allow-list (unknown ids
  coerce to the theme sentinel).
- i18n + types, api client methods, tests, and docs.

Validation: 6 new backend endpoint tests pass; tsc + vite build clean; live
browser test confirmed pick/persist/survive-theme-switch/clear all work.
This commit is contained in:
Teknium
2026-06-07 03:39:01 -07:00
committed by GitHub
parent 136dae779e
commit 9e63109522
11 changed files with 551 additions and 9 deletions
+6
View File
@@ -518,6 +518,12 @@ export const en: Translations = {
theme: {
title: "Theme",
switchTheme: "Switch theme",
fontTitle: "Font",
fontDefault: "Theme default",
fontDefaultHint: "Use the active theme's font",
fontSans: "Sans",
fontSerif: "Serif",
fontMono: "Mono",
},
achievements: {
+7
View File
@@ -539,6 +539,13 @@ export interface Translations {
theme: {
title: string;
switchTheme: string;
/** Font-override section (optional — locales fall back to English). */
fontTitle?: string;
fontDefault?: string;
fontDefaultHint?: string;
fontSans?: string;
fontSerif?: string;
fontMono?: string;
};
// ── Achievements plugin (plugins/hermes-achievements) ──