Skip to content

feat: add customizable UI fonts settings#275

Open
bluelovers wants to merge 2 commits intoNeuralNomadsAI:devfrom
bluelovers:pr/font-settings-custom-ui-fonts
Open

feat: add customizable UI fonts settings#275
bluelovers wants to merge 2 commits intoNeuralNomadsAI:devfrom
bluelovers:pr/font-settings-custom-ui-fonts

Conversation

@bluelovers
Copy link
Copy Markdown
Contributor

PR: Customizable UI Fonts

Summary (English)

This PR implements customizable UI font settings, allowing users to specify custom sans-serif and monospace fonts for the interface. Users can enter custom font names in the Appearance settings section, and the system dynamically updates CSS variables to apply the new font settings.

Summary (中文精简版)

本 PR 实现了可自定义的 UI 字体设置功能,允许用户为界面指定无衬线字体和等宽字体。用户可以在设置界面的外观部分输入自定义字体名称,系统会动态更新 CSS 变量以应用新的字体设置。


Changes (English)

Core Features

  1. Font Settings UI: Added font input fields in Appearance settings
  2. CSS Variables: Dynamic update of --font-family-sans-user and --font-family-mono-user
  3. Input Validation: Trim and empty string handling with default fallback
  4. Internationalization: Full support for 6 languages (en, zh-Hans, ja, fr, es, he)

Technical Implementation

  1. Configuration Schema: Added fontFamilySans and fontFamilyMono fields
  2. State Management: Added setFontFamilySans and setFontFamilyMono functions
  3. CSS Normalization: Created normalizeFontFamily utility function
  4. Theme Support: Fixed dark theme CSS variables

Files Modified

  • packages/server/src/config/schema.ts
  • packages/ui/src/stores/preferences.tsx
  • packages/ui/src/lib/settings/behavior-registry.ts
  • packages/ui/src/components/settings/appearance-settings-section.tsx
  • packages/ui/src/styles/components/settings-screen.css
  • packages/ui/src/styles/tokens.css
  • packages/ui/src/lib/i18n/messages/*/settings.ts (6 files)

Changes (中文简要说明)

核心功能

  1. 字体设置界面:在外观设置中添加字体输入框
  2. CSS 变量:动态更新 --font-family-sans-user--font-family-mono-user
  3. 输入验证:trim 和空字符串处理,无效值回退到默认字体
  4. 国际化:完整支持 6 种语言

技术实现

  1. 配置模式:添加 fontFamilySansfontFamilyMono 字段
  2. 状态管理:添加 setFontFamilySanssetFontFamilyMono 函数
  3. CSS 标准化:创建 normalizeFontFamily 工具函数
  4. 主题支持:修复暗色主题 CSS 变量

Key Features (English)

Input Validation

  • Input " Inter " → Normalized to "Inter" → CSS variable: --font-family-sans-user: "Inter"
  • Input "" (empty) → Normalized to undefined → Default: --font-family-sans-user: -apple-system
  • Input " " (spaces only) → Normalized to undefined → Default: -apple-system
  • Input undefined → Default: -apple-system
  • Input 123 (non-string) → Normalized to undefined → Default: -apple-system

CSS Variables Structure

/* Light theme */
--font-family-sans-user: "-apple-system";
--font-family-mono-user: "ui-monospace";
--font-family-sans: var(--font-family-sans-user), -apple-system, BlinkMacSystemFont, ...;
--font-family-mono: var(--font-family-mono-user), ui-monospace, SFMono-Regular, ...;

/* Dark theme (both @media and [data-theme]) */
--font-family-sans-user: "-apple-system";
--font-family-mono-user: "ui-monospace";
--font-family-sans: var(--font-family-sans-user), -apple-system, BlinkMacSystemFont, ...;
--font-family-mono: var(--font-family-mono-user), ui-monospace, SFMono-Regular, ...;

核心功能

输入验证

  • 输入 " Inter " → 标准化为 "Inter" → CSS 变量:--font-family-sans-user: "Inter"
  • 输入 ""(空字符串)→ 标准化为 undefined → 默认值:--font-family-sans-user: -apple-system
  • 输入 " "(只包含空格)→ 标准化为 undefined → 默认值:-apple-system
  • 输入 undefined → 默认值:-apple-system
  • 输入 123(非字符串)→ 标准化为 undefined → 默认值:-apple-system

CSS 变量结构

/* 亮色主题 */
--font-family-sans-user: "-apple-system";
--font-family-mono-user: "ui-monospace";
--font-family-sans: var(--font-family-sans-user), -apple-system, BlinkMacSystemFont, ...;
--font-family-mono: var(--font-family-mono-user), ui-monospace, SFMono-Regular, ...;

/* 暗色主题(包括 @media 和 [data-theme]) */
--font-family-sans-user: "-apple-system";
--font-family-mono-user: "ui-monospace";
--font-family-sans: var(--font-family-sans-user), -apple-system, BlinkMacSystemFont, ...;
--font-family-mono: var(--font-family-mono-user), ui-monospace, SFMono-Regular, ...;

Testing Instructions (English)

  1. Basic Test:

    • Go to Settings → Appearance → Fonts
    • Enter font names (e.g., "Inter" for sans, "Fira Code" for mono)
    • Click outside to save
    • Verify fonts update immediately
  2. Default Fallback Test:

    • Clear font inputs
    • Verify fonts revert to defaults
    • Refresh app to confirm persistence
  3. Edge Cases:

    • Enter empty string or spaces only
    • Verify default fonts are used
    • Test non-string inputs (should be ignored)

测试说明

  1. 基本测试

    • 进入设置 → 外观 → 字体
    • 输入字体名称(例如:sans 输入 "Inter",mono 输入 "Fira Code")
    • 点击外部保存
    • 验证字体立即更新
  2. 默认值回退测试

    • 清空字体输入框
    • 验证字体恢复为默认值
    • 刷新应用程序确认持久化
  3. 边界情况

    • 输入空字符串或只包含空格的字符串
    • 验证使用默认字体
    • 测试非字符串输入(应被忽略)

Checklist

  • Font settings UI implemented
  • Input validation with normalizeFontFamily function
  • CSS variables dynamic update
  • Dark theme CSS variables fixed
  • Internationalization (6 languages)
  • Default font fallback logic
  • Styles for font input fields
  • Tested in multiple browsers
  • Tested with various font inputs
  • Performance tested

检查清单

  • 字体设置界面已实现
  • 使用 normalizeFontFamily 函数进行输入验证
  • CSS 变量动态更新
  • 暗色主题 CSS 变量已修复
  • 国际化支持(6 种语言)
  • 默认字体回退逻辑
  • 字体输入框样式
  • 多浏览器测试
  • 各种字体输入测试
  • 性能测试

- Add font settings UI in Appearance section
- Implement input validation with normalizeFontFamily utility
- Add CSS variables for sans-serif and monospace fonts
- Fix dark theme CSS variables
- Add internationalization support (6 languages)
- Add CSS styles for font input fields

This PR allows users to customize UI fonts with proper validation and default fallback.
@shantur
Copy link
Copy Markdown
Collaborator

shantur commented Apr 3, 2026

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot bot commented Apr 3, 2026

Reviewed the latest PR state as a gatekeeper against the current branch tip, focusing on regressions and implementation quality. Assumption: this review is for the full current diff on top of dev, including the follow-up Monaco font commit.

Findings

  1. High - packages/ui/src/stores/preferences.tsx:791-805
    User-entered font values are always wrapped in quotes before being written into CSS variables. That breaks valid font stacks like Inter, Arial by turning them into one literal family name, and it also breaks if the user follows the placeholder examples and types quoted names, because the code produces nested quoting.

  2. High - packages/ui/src/components/settings/appearance-settings-section.tsx:68-76,301-323
    The font input signals are initialized from preferences() only once and never resynced. Preferences load asynchronously in this app, so persisted font settings can already be active while the settings screen still shows empty or stale values. That is a real regression risk because the UI can misrepresent the saved state.

  3. Medium - packages/ui/src/styles/panels/session-layout.css:514-519, packages/ui/src/components/file-viewer/monaco-file-viewer.tsx:51-62, packages/ui/src/components/file-viewer/monaco-diff-viewer.tsx:48-69
    The Monaco font customization is implemented only through CSS, but the editor instances are created without fontFamily options or reactive updateOptions() calls. Monaco frequently applies its own inline/editor-managed font settings, so this approach is brittle and likely to be inconsistent or ineffective for actual editor text.

  4. Medium - packages/ui/src/lib/i18n/messages/ru/settings.ts:170-197
    The new settings.appearance.fonts.* keys were added for the other locales touched by this PR, but not for Russian. Russian users will fall back to English for the new font settings section.

Notes

I did not see evidence of targeted tests covering font normalization, persistence/display after async config load, or Monaco font application. I also could not rely on a local typecheck/build signal in this workspace because dependencies are not installed here.

--
Yours,
CodeNomadBot

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.

2 participants