cmux-next: font settings (text font, code font, chat size) with a live preview

Closed
TE
15d ago
+3280-68

Font settings for cmux NEXT (Leo: "also fonts!!! where they at"). Settings > Appearance gets a Fonts group with three rows and a live preview, and every page and the window's own text follow them. With no key set the app looks exactly as it does today.

What changes

  • appearance.fonts.ui (Text Font): System, Rounded, Serif or Monospaced. These are the system's own designs (SF Pro, SF Pro Rounded, New York, SF Mono), so every Mac has them. Chats, the composer, pages and native chrome (Typography.system, through CheckedFont) all draw in it.
  • appearance.fonts.code (Code Font): any installed fixed-pitch family, the same list the terminal font row offers. Unset, code uses the terminal's font: cmux.json's terminal.fontFamily, else the Ghostty config's font-family (#20880, #20910). That covers chat code blocks, diffs, markdown and the code editor.
  • appearance.fonts.chatSize (Chat Text Size): 11 to 20 pt, default 14. Line height follows it (1.625x, which is today's 22.75 px at 14). Chat height estimates measure in the chosen face and size.
  • Plumbing. AppFonts lives in CmuxNextDesign and AppFontsSetting in CmuxNextSettings, so CmuxNextApp gains no lines. WebTheme sends --cmux-font-ui, --cmux-font-code and --cmux-font-chat-size with every theme, plus codeFontFamily for scripts that need a literal family (the editor, the agent pane's diff and code variables). An unset token is initial, so each page's own var(--cmux-font-*, <its stack>) keeps today's look.
  • Live preview. A preview card under the rows shows a short chat exchange and a code block in the chosen faces and size, and redraws as a row changes.
  • Exports. The settings schema, MDM docs and page strings are regenerated. All 21 locales are covered.

Settings UI: variants considered

  • A. Menus + size + live preview card (shipped). Pick a face from a menu, set the size with a stepper, and see a chat sample right there. It works like Codex desktop's UI font and code font settings, and the preview answers "what will my chat look like" without leaving Settings.
  • B. Claude-style presets (Default / Match System / Dyslexic Friendly). Simpler, but it can't pick a code face. Not shipped.
  • C. Each menu item previewed in its own face, no card. Compact, but you can't see face and size together. Not shipped.

B and C are not in this PR, following the no-Labs rule: an experimental extra would be a Debug toggle, and none was needed.

Testing

  • Red, then green:
    • bun test src/pages/settings/fontPreview.test.tsx test/app-fonts-tokens.test.ts was red at 1965b123aee4 (0 pass, 4 fail) and is green on this head.
    • test/agent-pane-terminal-font.test.ts gains "the code font setting wins over the terminal's face" and still passes #20910's cases.
  • AppFontsSettingTests (Swift) covers parsing, the WebTheme variables and the code family fallback. It is compiled on the Linux builder and runs in PR CI.
  • On the Linux builder: bun run check, all settings page tests (141 pass), check-concurrency, lint-xcstrings and check-app-size are clean. The conventions lint's 2 errors are in base files (BuiltInAdBlockerSetting, HoverCardTiming).
  • Correctness review subagent: no code blockers. The defaults snapshot was stale, and is fixed here. Also fixed: the preview's code block (the UA sheet gave <code> its own monospace; unset, it now shows the terminal's font), chat estimates ignoring the text face, one extra re-estimate on the first theme, and AppFonts.Design is now nonisolated.

In app

A real cmux NEXT DEV build of this branch (7dc74fa89e9e) ran on a capture mini, with a slot-local cmux.json set to appearance.fonts = {ui: serif, code: "Courier New", chatSize: 17}. Then, with Settings open, the harness rewrote that file live three times: rounded/Courier New/17, then system/Menlo/12, then serif/Courier New/17.

  • The rows, the preview, open chats, the composer and the sidebar all followed within a frame, with no relaunch.
  • Diff, markdown and editor code drew in Courier New.
  • The chat text drew in New York at 17 pt.
  • Base (7295a6fe) with no keys set is shown for comparison. The host name is masked.

Known gap, left for a follow-up: the New Tab page's composer and its chips stay in the system face.

fonts live preview

Full quality:

settings fonts light

settings fonts dark

chat light

chat dark

diff light

diff dark

markdown light

markdown dark

Changelog

Added: Settings > Appearance > Fonts: choose the text font, the code font and the chat text size, with a live preview

🤖 Generated with Claude Code