Skip to content

feat: add interface, monospace, and code font preferences - #4666

Merged
kmendell merged 2 commits into
mainfrom
feat/custom-fonts-cjk-fallback
Nov 6, 2026
Merged

kmendell merged 2 commits into
mainfrom
feat/custom-fonts-cjk-fallback

Conversation

@kmendell

@kmendell kmendell commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Checklist

  • This PR is not opened from my fork's main branch
  • All new user-facing strings are translated via Paraglide (m.*())

What This PR Implements

Adds per-user font preferences for interface text, monospace/headings, and the code editor and terminal. Also adds locale-ordered system font fallbacks so Chinese, Japanese, and Korean text uses a matching system font.

Addresses: #4604 (discussion)

Changes Made

  • Preferences: interfaceFont, monospaceFont, and codeFont on UserPreferences (types/user.go, frontend/src/lib/types/user.ts). They are stored in the existing JSON column, so no migration is needed. Values are limited to 128 characters from letters, digits, spaces, and _ ' " , -, which keeps them from injecting CSS.
  • Apply: applyFonts in theme.svelte.ts sets --font-sans-user, --font-mono-user, and --font-code-user on :root and writes them to the appearance cache. The app.html pre-paint script restores them before first paint. user-store.svelte.ts applies them on load and clears them on logout.
  • Font stacks: layout.css puts the user font first, then the bundled font, then a CJK fallback. The CJK order follows <html lang>, which now tracks the active locale.
  • Code editor and terminal: the editor uses var(--font-code). The terminal reads --font-code when it mounts, so a change takes effect the next time the terminal opens.
  • Picker: font-family-picker.svelte appears on the account preferences panel. Options are Default, System UI, Montserrat, Geist Mono, and Custom. Custom accepts any typed font name. Where the browser supports queryLocalFonts, it also offers a searchable list of installed fonts.
  • Not included: the CJK fallback uses system fonts only. Bundled Noto Sans SC/TC/JP/KR slices are not part of this PR.

Testing Done

Ran:

  • just format all
  • just lint frontend: svelte-check reports 0 errors and 0 warnings

Added:

  • TestUserProfileValidationAndPersistence in backend/internal/user/service_test.go now round-trips the three font fields through UpdateUser and ToUserResponseDto.

Not yet run:

  • just test backend
  • just lint all

Not yet verified manually:

  • zh-CN login page renders a CJK font
  • a custom interface font applies with no flash on reload
  • the terminal and code editor use the chosen code font

AI Tool Used (if applicable)

N/A

Additional Context

  • --font-code still names 'Mona Sans Mono', which is not bundled. This predates the PR, and the fallback stack covers it. Left unchanged.
  • queryLocalFonts is Chromium-only. The browse button is hidden elsewhere, but typed font names still work.
  • A font name that is not installed falls through to the next font in the stack. No error is shown.

Disclaimer Greptiles Reviews use AI, make sure to check over its work.

To better help train Greptile on our codebase, if the comment is useful and valid Like the comment, if its not helpful or invalid Dislike

To have Greptile Re-Review the changes, mention greptileai.

RetriggerView in GreptileConfidence Score: 5/5 Tier: plus

The PR appears safe to merge, with a non-blocking fix needed for font names containing apostrophes.

Fix All in CodexFindings

  1. P2 Fonts with apostrophes are rejected ▶
Fix with agent prompt
### Issue 1
frontend/src/lib/utils/theme.svelte.ts:368-373
`quoteFontFamily` rejects names containing an apostrophe even when enclosed in double quotes. For example, `"O'Brien"` is a valid quoted family, but the picker shows an error. The same check also filters these fonts out of the installed-font list.

Allow the other quote character inside a quoted family, and safely quote raw installed names.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds per-user interface, monospace, and code fonts.

  • Your saved font choices now shape interface and code text.
  • CJK text follows the page’s language for its system font fallback.

kmendell intentionally limits terminal font updates to reopening and leaves the existing unbundled Mona Sans Mono fallback unchanged.

Verification was limited to source review. Tests and browser captures were not run; frontend dependencies were absent, and no development stack was started.

Reviews (2) · Last reviewed commit: "feat: allow custom interface font with v..." · Reviewed by Greptile

@kmendell
kmendell requested a review from a team November 6, 2026 01:06
@@ -0,0 +1,169 @@
<script lang="ts">
import { browser } from '$app/env';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$app/env is not a SvelteKit module. The current module is $app/environment, and $app/env was removed long ago. This import will fail to resolve, which breaks the Vite build and the account preferences page that imports the picker.

import { browser } from '$app/environment';
Suggested change
import { browser } from '$app/env';
import { browser } from '$app/environment';

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@kmendell
kmendell force-pushed the feat/custom-fonts-cjk-fallback branch from 5b92a91 to f9f71ff Compare November 6, 2026 01:11
Comment thread frontend/src/lib/utils/theme.svelte.ts Resolved
Comment thread frontend/src/routes/layout.css Resolved
Comment thread frontend/src/lib/utils/theme.svelte.ts Resolved
Comment on lines +368 to +373
if (/^(['"])[^'"]+\1$/.test(name) || GENERIC_FONT_FAMILIES.has(name.toLowerCase())) {
return name;
}

if (/['"]/.test(name)) {
return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Fonts with apostrophes are rejected

quoteFontFamily rejects names containing an apostrophe even when enclosed in double quotes. For example, "O'Brien" is a valid quoted family, but the picker shows an error. The same check also filters these fonts out of the installed-font list.

Allow the other quote character inside a quoted family, and safely quote raw installed names.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/lib/utils/theme.svelte.ts
Line: 368-373

Comment:
**Fonts with apostrophes are rejected**

`quoteFontFamily` rejects names containing an apostrophe even when enclosed in double quotes. For example, `"O'Brien"` is a valid quoted family, but the picker shows an error. The same check also filters these fonts out of the installed-font list.

Allow the other quote character inside a quoted family, and safely quote raw installed names.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

greptile-apps[bot]
greptile-apps Bot previously approved these changes Nov 6, 2026
- Add font pickers for interface, monospace, and code text in account preferences
- Accept installed font names, validated on the client and in the user API
- Fall back to CJK system fonts, ordered by the document language
- Apply fonts through CSS variables; the terminal picks up the code font on reopen
- Validate custom and installed font names, and accept comma-separated font stacks
- Quote multi-word family names and pass generic families (serif, monospace, etc.) through unquoted
- Localize the Montserrat and Geist Mono preset labels
- Keep the bundled Montserrat and Geist Mono as fallbacks ahead of CJK fonts when a custom font is set
- Cover setting, persisting, and resetting the interface font in the account e2e spec
@kmendell
kmendell force-pushed the feat/custom-fonts-cjk-fallback branch from 28c48db to d399dd9 Compare November 6, 2026 02:29
@greptile-apps
greptile-apps Bot dismissed their stale review November 6, 2026 02:29

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@kmendell
kmendell merged commit b8a5b76 into main Nov 6, 2026
20 checks passed
@kmendell
kmendell deleted the feat/custom-fonts-cjk-fallback branch November 6, 2026 02:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant