Skip to content

Feature/font customization#730

Draft
rm1dev wants to merge 3 commits into
siteboon:mainfrom
rm1dev:feature/font-customization
Draft

Feature/font customization#730
rm1dev wants to merge 3 commits into
siteboon:mainfrom
rm1dev:feature/font-customization

Conversation

@rm1dev
Copy link
Copy Markdown

@rm1dev rm1dev commented Apr 30, 2026

Summary by CodeRabbit

  • New Features
    • Customizable interface font (Default/Custom) and font size
    • Customizable code editor font for code blocks and inline code
    • Automatic text-direction detection for messages and composer
    • Font settings persist across sessions, are applied at startup, and update live when changed
    • Added localization for new appearance/font controls in multiple languages

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

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3be35f61-fb25-4407-a9b6-254ae72e721e

📥 Commits

Reviewing files that changed from the base of the PR and between 73c1b63 and 086784c.

📒 Files selected for processing (5)
  • src/components/chat/view/subcomponents/ChatComposer.tsx
  • src/components/chat/view/subcomponents/MessageComponent.tsx
  • src/components/settings/constants/constants.ts
  • src/components/settings/hooks/useSettingsController.ts
  • src/i18n/locales/en/settings.json
💤 Files with no reviewable changes (5)
  • src/components/chat/view/subcomponents/ChatComposer.tsx
  • src/components/settings/constants/constants.ts
  • src/components/chat/view/subcomponents/MessageComponent.tsx
  • src/components/settings/hooks/useSettingsController.ts
  • src/i18n/locales/en/settings.json

📝 Walkthrough

Walkthrough

Adds appearance and code-editor font settings (types, defaults, controller persistence and events), applies bidirectional dir="auto" to chat input/messages, and inserts localization keys for the new font controls.

Changes

Appearance & rendering changes

Layer / File(s) Summary
Settings types & defaults, controller wiring
src/components/settings/types/types.ts, src/components/settings/constants/constants.ts, src/components/settings/hooks/useSettingsController.ts
Adds AppearanceFontSettings, extends CodeEditorSettingsState with font, customFont, fontSize; adds DEFAULT_APPEARANCE_FONT_SETTINGS; reads, persists, and dispatches appearanceFontSettings changes and extends code-editor persistence.
Chat bidi attributes
src/components/chat/view/subcomponents/ChatComposer.tsx, src/components/chat/view/subcomponents/MessageComponent.tsx
Adds dir="auto" to PromptInputTextarea, user message wrapper, and assistant/error/tool content wrapper to let the browser infer text direction per-content.
Localization (i18n)
src/i18n/locales/en/settings.json
Adds English localization keys for appearance font selection, custom font input placeholder, and font size, and mirrors keys for code-editor font selection.

Suggested reviewers

  • blackmammoth

Poem

🐰
I hopped through settings, quick and spry,
Chose fonts that make the text feel right.
Code blocks wear a new disguise,
Messages read left or right—surprise!
Fonts and bidi now take flight.

🚥 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 accurately summarizes the main change—adding font customization features throughout the application including appearance and code editor fonts.
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

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.

@blackmammoth blackmammoth marked this pull request as draft June 2, 2026 11:31
Copy link
Copy Markdown
Collaborator

@blackmammoth blackmammoth left a comment

Choose a reason for hiding this comment

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

Hey @rm1dev, thanks for the PR. However, here are some concerns:

  1. It's not clear how to change fonts. I think it would be better if the custom fonts are from a dropdown list instead.
  2. The font size changer doesn't work. I changed it from 12 to 20px and it's still the same.
    After this changes have been addressed, I would love to review it again. So, I'm making this a draft for now.

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