Skip to content

Add configurable Arabic chat font settings and improve RTL chat rendering#5

Closed
Mo999salah wants to merge 1 commit into
boggedbrush:mainfrom
Mo999salah:mo999salah/feat/add-rtl-chat-font-settings
Closed

Add configurable Arabic chat font settings and improve RTL chat rendering#5
Mo999salah wants to merge 1 commit into
boggedbrush:mainfrom
Mo999salah:mo999salah/feat/add-rtl-chat-font-settings

Conversation

@Mo999salah
Copy link
Copy Markdown
Contributor

Summary

  • add a client-side chat font setting with Auto, DM Sans, and Noto Sans Arabic options
  • apply automatic text direction and the selected font across assistant messages, user bubbles, and the composer
  • switch chat markdown layout styles to logical CSS properties so Arabic content renders correctly in RTL
  • import Noto Sans Arabic locally and share chat typography mapping through a small helper module
  • fix useSettings typing so components can subscribe to individual settings slices without casts

Why

The current chat UI had no way to choose an Arabic-friendly font and still assumed mostly LTR typography. That made Arabic conversations look inconsistent across the composer, user messages, and assistant output.

User impact

  • Arabic chat content now aligns and reads correctly in RTL
  • maintainers can keep the default UI font, force DM Sans, or force Noto Sans Arabic from Settings > Appearance
  • code blocks and monospace surfaces stay LTR and readable

Validation

  • bun fmt
  • bun lint
  • bun typecheck
  • bun run test src/hooks/useSettings.test.ts
  • bun run test src/lib/textDirection.test.ts

Add a client setting for chat font selection and apply it across chat markdown, user messages, and the composer.

Keep Arabic content readable in RTL while preserving code blocks and default app typography. Also fix the settings hook typing so components can subscribe to setting slices cleanly.
@Mo999salah Mo999salah marked this pull request as ready for review April 17, 2026 19:49
Copilot AI review requested due to automatic review settings April 17, 2026 19:49
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a user-configurable chat font setting (including an Arabic-optimized option) and improves RTL chat rendering by applying automatic text direction + logical CSS properties across the chat surface.

Changes:

  • Introduces chatFontFamily as a client setting (Auto / DM Sans / Noto Sans Arabic) and wires it into the Appearance settings UI.
  • Adds shared helpers for resolving text direction and mapping direction/font selection to CSS classes; applies these to composer, user messages, and assistant markdown.
  • Updates chat markdown CSS to use logical properties and enforces LTR rendering for code/monospace areas.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/contracts/src/settings.ts Adds ChatFontFamily setting + default to client settings schema.
apps/web/package.json Adds @fontsource/noto-sans-arabic dependency.
bun.lock Locks the new font dependency.
apps/web/src/main.tsx Imports Noto Sans Arabic font weights globally.
apps/web/src/lib/textDirection.ts Adds direction resolution helpers (resolveTextDirection, isRtlText).
apps/web/src/lib/textDirection.test.ts Adds unit tests for direction detection.
apps/web/src/lib/chatTypography.ts Centralizes mapping of direction/font selection to CSS class names.
apps/web/src/index.css Introduces chat font CSS variables + RTL/LTR direction classes; converts markdown styles to logical properties.
apps/web/src/hooks/useSettings.ts Updates useSettings typing to better support slice selectors.
apps/web/src/components/settings/SettingsGeneralPanel.tsx Includes the new setting in “dirty settings” detection.
apps/web/src/components/settings/SettingsAppearancePanel.tsx Adds “Chat font” select control + reset behavior.
apps/web/src/components/chat/MessagesTimeline.tsx Applies resolved direction + chat typography classes to user message bubbles.
apps/web/src/components/ComposerPromptEditor.tsx Applies resolved direction + chat typography classes to the composer and placeholder.
apps/web/src/components/ChatMarkdown.tsx Applies resolved direction + chat typography classes to assistant markdown output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

export function useSettings<T extends UnifiedSettings = UnifiedSettings>(
selector?: (s: UnifiedSettings) => T,
): T {
export function useSettings<T = UnifiedSettings>(selector?: (s: UnifiedSettings) => T): T {
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

The new generic signature allows useSettings<SomeType>() to be called without a selector, which becomes an unsound cast at runtime. To keep the improved selector typing while preventing misuse, consider using overloads (no selector → UnifiedSettings, selector → inferred return type) instead of an unconstrained generic default.

Copilot uses AI. Check for mistakes.
Comment on lines +769 to +772
<div
dir={textDirection}
lang={textDirection === "rtl" ? "ar" : undefined}
className={bodyClassName}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

lang is set to "ar" whenever the resolved direction is RTL, but resolveTextDirection treats multiple scripts as RTL (e.g., Hebrew). This can mislabel content language for screen readers and text shaping. Consider either omitting lang entirely here, or setting it based on an Arabic-script detection (separate regex) rather than direction alone.

Copilot uses AI. Check for mistakes.
Comment on lines +801 to +804
<div
dir={textDirection}
lang={textDirection === "rtl" ? "ar" : undefined}
className={bodyClassName}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

Same issue as above: lang is derived from RTL direction and hardcoded to "ar", which can mislabel RTL-but-non-Arabic content. Prefer omitting lang or deriving it from Arabic-script detection instead of direction.

Copilot uses AI. Check for mistakes.
<div className="chat-markdown w-full min-w-0 text-sm leading-relaxed text-foreground/80">
<div
dir={textDirection}
lang={textDirection === "rtl" ? "ar" : undefined}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

lang is hardcoded to "ar" for any RTL text, but direction != language (e.g., Hebrew is RTL). This can degrade screen reader pronunciation and font shaping. Consider removing lang here or computing it from Arabic-script detection separately from resolveTextDirection.

Suggested change
lang={textDirection === "rtl" ? "ar" : undefined}

Copilot uses AI. Check for mistakes.
Comment on lines +1109 to 1111
dir={composerDirection}
lang={composerDirection === "rtl" ? "ar" : undefined}
className={cn(
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

lang is set to "ar" whenever the composer direction is RTL. Since RTL direction can come from non-Arabic scripts, this can mislabel the user’s input for assistive tech. Prefer omitting lang or setting it only when Arabic script is detected.

Copilot uses AI. Check for mistakes.
<div className="pointer-events-none absolute inset-0 text-[14px] leading-relaxed text-muted-foreground/35">
<div
dir={composerDirection}
lang={composerDirection === "rtl" ? "ar" : undefined}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

Placeholder uses the same RTL→lang="ar" mapping, which can mislabel the placeholder text language for assistive technologies. Consider removing lang here or deriving it from Arabic-script detection instead of direction.

Suggested change
lang={composerDirection === "rtl" ? "ar" : undefined}

Copilot uses AI. Check for mistakes.
@boggedbrush
Copy link
Copy Markdown
Owner

Hey @Mo999salah, thank you for the contribution.

I appreciate you taking the time to improve Kodo Code and put this PR together.

I reviewed the changes here and decided to continue the work in a follow-up branch so the feature can support a broader set of users, not just Arabic/RTL-specific cases. You can follow that work in PR #6.

The main additions on top of this PR are:

  1. Expanded multilingual chat support for:
    • Latin-script languages such as English, French, Spanish, German, Portuguese, and Italian
    • Greek
    • Cyrillic-script languages such as Russian, Ukrainian, and Bulgarian
    • Hebrew
    • Arabic-script languages
    • Devanagari-script languages such as Hindi, Marathi, and Nepali
    • Thai
  2. Broader typography and readability changes so Arabic is supported well without being treated as the only highlighted language.

For the best experience with this update, I recommend using Noto Sans Multilingual in Chat typography.

image

I’ll be closing PR #5 in favor of PR #6 so the discussion and follow-up work stay in one place. If you have any additional changes, questions, or comments, please add them on PR #6.

If all goes as planned, this will ship in v0.0.2. In the meantime, you’re welcome to use the PR #6 branch directly.

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.

3 participants