Skip to content

Feature/font customization#730

Open
rm1dev wants to merge 2 commits intositeboon:mainfrom
rm1dev:feature/font-customization
Open

Feature/font customization#730
rm1dev wants to merge 2 commits intositeboon:mainfrom
rm1dev:feature/font-customization

Conversation

@rm1dev
Copy link
Copy Markdown

@rm1dev rm1dev commented Apr 30, 2026

Summary by CodeRabbit

  • New Features
    • Added customizable interface font with default and custom font family options
    • Added customizable code editor font for code blocks and inline code
    • Added font size controls for both the interface and code editor
    • Implemented automatic text direction detection for improved multi-language support
    • Font settings are now persisted and applied across sessions

rm1dev added 2 commits April 23, 2026 17:38
Add dir="auto" to chat message content and composer textarea so
Persian and Arabic text automatically renders right-to-left
while English and other LTR text remains unaffected.
- Add font selection and custom font input to code editor settings
- Add appearance font settings with font family, custom font, and font size options
- Create fontSettings utility module for font management
- Update Markdown component to respect code editor font preferences
- Add event listener for real-time font changes in code blocks
- Extend AppearanceSettingsTab with new font customization controls
- Update all locale files (en, de, it, ja, ko, ru, tr, zh-CN) with font setting labels
- Persist font preferences to localStorage and sync across application
- Enable users to select from default fonts or specify custom font families
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

Introduces comprehensive font customization across the application, enabling users to configure UI and code editor fonts (default or custom variants) with persistent storage, event-driven runtime updates, bidirectional text direction support, and localization across 8 languages.

Changes

Cohort / File(s) Summary
Settings Infrastructure
src/components/settings/types/types.ts, src/components/settings/constants/constants.ts, src/components/settings/hooks/useSettingsController.ts
Extended CodeEditorSettingsState with font properties (fontSize, font, customFont); added new AppearanceFontSettings type; updated settings controller to manage and persist appearance font and code editor font preferences via localStorage and event dispatch.
Settings UI
src/components/settings/view/Settings.tsx, src/components/settings/view/tabs/AppearanceSettingsTab.tsx
Added font selection handlers and state for both appearance and code editor fonts; expanded UI with dropdown menus to toggle between default/custom font modes and text inputs for custom font names, plus font size selector.
Chat & Markdown Rendering
src/components/chat/view/subcomponents/ChatComposer.tsx, src/components/chat/view/subcomponents/Markdown.tsx, src/components/chat/view/subcomponents/MessageComponent.tsx
Added bidirectional text direction (dir="auto") to message and input components; implemented dynamic code font application in Markdown with localStorage-driven font selection and event listener for runtime updates.
Font Settings Utility
src/utils/fontSettings.ts, src/main.jsx
Created new utility module exporting functions to apply font configuration from localStorage to document body and code elements; added initialization call in app entrypoint to apply fonts and register event listeners at startup.
Localization (i18n)
src/i18n/locales/de/settings.json, src/i18n/locales/en/settings.json, src/i18n/locales/it/settings.json, src/i18n/locales/ja/settings.json, src/i18n/locales/ko/settings.json, src/i18n/locales/ru/settings.json, src/i18n/locales/tr/settings.json, src/i18n/locales/zh-CN/settings.json
Added translation strings across 8 languages for UI font and code editor font selection controls, including default/custom mode labels, custom font input placeholders, and font size descriptors.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant UI as AppearanceSettingsTab
    participant Settings as useSettingsController
    participant Storage as localStorage
    participant Events as Custom Events
    participant Renderer as Markdown/ChatComposer
    
    User->>UI: Change font setting (default/custom)
    UI->>Settings: updateAppearanceFontSetting()
    Settings->>Settings: Update appearanceFontSettings state
    Settings->>Storage: Persist appearanceFont, appearanceCustomFont, appearanceFontSize
    Settings->>Events: Dispatch appearanceFontSettingsChanged event
    
    Events->>Renderer: Event listener triggered
    Renderer->>Storage: Read updated font settings
    Renderer->>Renderer: Apply fontFamily to document.body and code elements
    Renderer->>User: Render with updated fonts
Loading

Possibly related PRs

  • PR #303: Both PRs add/modify the same i18n localization resources (src/i18n/locales/*/settings.json), indicating coordinated localization updates.
  • PR #374: Both PRs modify the same chat view components (Markdown.tsx, MessageComponent.tsx, ChatComposer.tsx), with overlapping rendering logic updates.
  • PR #402: Both PRs implement the same font settings infrastructure (types, constants, controller hooks, settings UI, and app initialization), sharing the complete feature plumbing.

Suggested reviewers

  • blackmammoth
  • viper151

Poem

🐰 Hop into custom fonts with ease,

Whether serif or sans you please!

Code blocks dance in typefaces new,

While bidirectional text runs true,

From German prose to Japanese too,

We've styled the web anew! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/font customization' is directly related to the main changes in the changeset, which comprehensively add font customization functionality including custom font selection, font size control, and font settings persistence across the application.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 6/8 reviews remaining, refill in 13 minutes and 42 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/settings/hooks/useSettingsController.ts (1)

88-102: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use non-empty fallback semantics when hydrating font settings from storage.

Lines 94-95 and 99-101 use ??, so '' is accepted and can put invalid empty values into settings state. Prefer || (or a small normalizer) for these fields.

Proposed fix
-  font: localStorage.getItem('codeEditorFont') ?? DEFAULT_CODE_EDITOR_SETTINGS.font,
-  customFont: localStorage.getItem('codeEditorCustomFont') ?? DEFAULT_CODE_EDITOR_SETTINGS.customFont,
+  font: localStorage.getItem('codeEditorFont') || DEFAULT_CODE_EDITOR_SETTINGS.font,
+  customFont: localStorage.getItem('codeEditorCustomFont') || DEFAULT_CODE_EDITOR_SETTINGS.customFont,
@@
-  font: localStorage.getItem('appearanceFont') ?? DEFAULT_APPEARANCE_FONT_SETTINGS.font,
-  customFont: localStorage.getItem('appearanceCustomFont') ?? DEFAULT_APPEARANCE_FONT_SETTINGS.customFont,
-  fontSize: localStorage.getItem('appearanceFontSize') ?? DEFAULT_APPEARANCE_FONT_SETTINGS.fontSize,
+  font: localStorage.getItem('appearanceFont') || DEFAULT_APPEARANCE_FONT_SETTINGS.font,
+  customFont: localStorage.getItem('appearanceCustomFont') || DEFAULT_APPEARANCE_FONT_SETTINGS.customFont,
+  fontSize: localStorage.getItem('appearanceFontSize') || DEFAULT_APPEARANCE_FONT_SETTINGS.fontSize,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/settings/hooks/useSettingsController.ts` around lines 88 -
102, readCodeEditorSettings and readAppearanceFontSettings currently use the
nullish coalescing operator (??) for font-related fields, which allows empty
strings from localStorage to be accepted into state; change those font and
customFont and fontSize fallbacks to use logical OR (||) or a small normalizer
(e.g., value => value?.trim() || DEFAULT_...) so empty strings become the
default values; update occurrences in readCodeEditorSettings (font, customFont,
fontSize) and readAppearanceFontSettings (font, customFont, fontSize) to use
this non-empty fallback logic.
🧹 Nitpick comments (1)
src/utils/fontSettings.ts (1)

35-42: ⚡ Quick win

Make initializeFontSettings teardown-safe to avoid duplicate global listeners.

If this initializer is called more than once (tests/HMR/re-init paths), listeners accumulate and handlers run repeatedly.

Proposed refactor
 export const initializeFontSettings = () => {
   applyAppearanceFontSettings();
   applyCodeEditorFontSettings();

   // Listen for settings changes
   window.addEventListener('appearanceFontSettingsChanged', applyAppearanceFontSettings);
   window.addEventListener('codeEditorSettingsChanged', applyCodeEditorFontSettings);
+
+  return () => {
+    window.removeEventListener('appearanceFontSettingsChanged', applyAppearanceFontSettings);
+    window.removeEventListener('codeEditorSettingsChanged', applyCodeEditorFontSettings);
+  };
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/fontSettings.ts` around lines 35 - 42, The initializer currently
adds global listeners each call causing duplicates; make initializeFontSettings
idempotent by first removing existing listeners for
'appearanceFontSettingsChanged' and 'codeEditorSettingsChanged' (using
window.removeEventListener with the same handler functions
applyAppearanceFontSettings and applyCodeEditorFontSettings) before calling
addEventListener, and optionally return or export a teardown function that calls
removeEventListener for those same handlers so tests/HMR can clean up; reference
applyAppearanceFontSettings, applyCodeEditorFontSettings, and the event names
when implementing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/fontSettings.ts`:
- Around line 4-15: The code reads localStorage.getItem('appearanceFontSize')
into fontSize and injects it directly into body.style.fontSize causing malformed
values to create invalid CSS; update the logic where fontSize is derived (the
const fontSize assignment) to parse the stored value as an integer (use parseInt
or Number), validate it, and clamp it to a safe range (e.g., min 12, max 24)
falling back to the default 16 when parsing fails or value is out of bounds;
then apply the clamped numeric value when setting body.style.fontSize (the usage
around body.style.fontSize = `${fontSize}px`) so only normalized, safe pixel
sizes are written.

---

Outside diff comments:
In `@src/components/settings/hooks/useSettingsController.ts`:
- Around line 88-102: readCodeEditorSettings and readAppearanceFontSettings
currently use the nullish coalescing operator (??) for font-related fields,
which allows empty strings from localStorage to be accepted into state; change
those font and customFont and fontSize fallbacks to use logical OR (||) or a
small normalizer (e.g., value => value?.trim() || DEFAULT_...) so empty strings
become the default values; update occurrences in readCodeEditorSettings (font,
customFont, fontSize) and readAppearanceFontSettings (font, customFont,
fontSize) to use this non-empty fallback logic.

---

Nitpick comments:
In `@src/utils/fontSettings.ts`:
- Around line 35-42: The initializer currently adds global listeners each call
causing duplicates; make initializeFontSettings idempotent by first removing
existing listeners for 'appearanceFontSettingsChanged' and
'codeEditorSettingsChanged' (using window.removeEventListener with the same
handler functions applyAppearanceFontSettings and applyCodeEditorFontSettings)
before calling addEventListener, and optionally return or export a teardown
function that calls removeEventListener for those same handlers so tests/HMR can
clean up; reference applyAppearanceFontSettings, applyCodeEditorFontSettings,
and the event names when implementing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e0dedc8d-5958-4b01-a538-9981d5862dd3

📥 Commits

Reviewing files that changed from the base of the PR and between 392c73b and 73c1b63.

📒 Files selected for processing (18)
  • src/components/chat/view/subcomponents/ChatComposer.tsx
  • src/components/chat/view/subcomponents/Markdown.tsx
  • src/components/chat/view/subcomponents/MessageComponent.tsx
  • src/components/settings/constants/constants.ts
  • src/components/settings/hooks/useSettingsController.ts
  • src/components/settings/types/types.ts
  • src/components/settings/view/Settings.tsx
  • src/components/settings/view/tabs/AppearanceSettingsTab.tsx
  • src/i18n/locales/de/settings.json
  • src/i18n/locales/en/settings.json
  • src/i18n/locales/it/settings.json
  • src/i18n/locales/ja/settings.json
  • src/i18n/locales/ko/settings.json
  • src/i18n/locales/ru/settings.json
  • src/i18n/locales/tr/settings.json
  • src/i18n/locales/zh-CN/settings.json
  • src/main.jsx
  • src/utils/fontSettings.ts

Comment thread src/utils/fontSettings.ts
Comment on lines +4 to +15
const fontSize = localStorage.getItem('appearanceFontSize') || '16';

const body = document.body;

if (font === 'custom' && customFont.trim()) {
body.style.fontFamily = customFont;
} else {
body.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
}

body.style.fontSize = `${fontSize}px`;
};
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize and clamp appearanceFontSize before applying styles.

Line 14 currently trusts raw localStorage text; malformed values become invalid CSS and silently fail.

Proposed fix
-  const fontSize = localStorage.getItem('appearanceFontSize') || '16';
+  const rawFontSize = localStorage.getItem('appearanceFontSize');
+  const parsedFontSize = Number.parseInt(rawFontSize ?? '', 10);
+  const fontSize = Number.isFinite(parsedFontSize) && parsedFontSize >= 10 && parsedFontSize <= 32
+    ? parsedFontSize
+    : 16;
@@
-  body.style.fontSize = `${fontSize}px`;
+  body.style.fontSize = `${fontSize}px`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/fontSettings.ts` around lines 4 - 15, The code reads
localStorage.getItem('appearanceFontSize') into fontSize and injects it directly
into body.style.fontSize causing malformed values to create invalid CSS; update
the logic where fontSize is derived (the const fontSize assignment) to parse the
stored value as an integer (use parseInt or Number), validate it, and clamp it
to a safe range (e.g., min 12, max 24) falling back to the default 16 when
parsing fails or value is out of bounds; then apply the clamped numeric value
when setting body.style.fontSize (the usage around body.style.fontSize =
`${fontSize}px`) so only normalized, safe pixel sizes are written.

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