feat(desktop): auto-detect RTL paragraph direction in chat#44169
feat(desktop): auto-detect RTL paragraph direction in chat#44169AIalliAI wants to merge 1 commit into
Conversation
Arabic/Hebrew/Persian/Urdu text in the desktop chat rendered LTR, scattering mixed RTL/LTR conversations. Resolve base direction per paragraph from the first strong character (unicode-bidi: plaintext) across assistant prose, user bubbles, and both composers, so RTL paragraphs read and align right-to-left while English ones stay LTR. Inline code and KaTeX output are pinned as isolated LTR runs so paths, flags, and commands inside an RTL sentence keep their internal order; fenced code blocks keep the document's LTR direction untouched. Fixes NousResearch#44150 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tonydwb
left a comment
There was a problem hiding this comment.
Code Review: PR #44169
Verdict: Approved — pure CSS RTL fix, well-implemented.
Summary
- Files changed: apps/desktop/src/components/assistant-ui/user-message-text.tsx, apps/desktop/src/styles.css (+31, -0)
- Adds UAX#9 bidi text support for RTL languages (Arabic, Hebrew, Persian, Urdu)
- Uses unicode-bidi: plaintext for per-paragraph direction detection
- Pins code and KaTeX to LTR with unicode-bidi: isolate
Assessment
Correctness: Implementation correctly handles RTL without forcing the entire app UI to RTL. Code blocks stay LTR. Whitespace-pre-line ensures each line is its own bidi paragraph.
Code quality: Clean, well-commented CSS. No JS changes needed.
No issues found.
Reviewed by Hermes Agent
|
Superseded by #44596, which builds on this CSS-only approach and fixes two gaps: it adds |
|
Superseded by #44596 (unified CSS-only RTL/bidi approach). Closing per the superseding PR. |
Arabic/Hebrew/Persian/Urdu chat text rendered left-to-right and left-aligned, and mixed RTL/English technical messages (the common case) read backwards. Resolve each chat block's base direction from its own first strong character (UAX#9) with pure CSS, scoped to the chat surfaces only: - `unicode-bidi: plaintext` + `text-align: start` on assistant prose blocks (p, h1-h6, li, blockquote), the user bubble's text lines, and both composers (main + edit share the composer-rich-input slot). RTL blocks read and right-align RTL; English stays LTR; mixed conversations resolve per block. `text-align: start` is required because the user bubble hardcodes `text-left`. - Inline `code` and KaTeX are pinned `direction: ltr; unicode-bidi: isolate`, so the bidi first-strong heuristic skips them: a sentence that *starts* with a command (`./run.sh ...`) followed by Arabic still resolves RTL, and the command's own neutrals keep their order. - Fenced code surfaces (code-card, user fences) are pinned LTR so they never mirror or right-align inside an RTL list item or blockquote. `direction` is never forced, so app chrome, layout, and list indent stay LTR per the issue's request not to flip the whole UI. English-only content is byte-for-byte unchanged. Salvaged and unified from NousResearch#44065 and NousResearch#44169; verified in Chromium that isolate removes inline code from the paragraph direction vote (the code-first case), making the JS dir-resolution in NousResearch#44065 unnecessary. Fixes NousResearch#44150 Co-authored-by: Adolanium <Adolanium@users.noreply.github.com> Co-authored-by: Adalsteinn Helgason <AIalliAI@users.noreply.github.com>
Fixes #44150
Problem
Arabic (and Hebrew/Persian/Urdu) text in the desktop chat renders left-to-right: RTL paragraphs read backwards-feeling and left-aligned, and mixed Arabic/English technical conversations — the normal case, per the issue — become genuinely hard to follow.
Approach
Pure CSS, exactly the behavior proposed in the issue (first-strong-character paragraph detection), with one
data-slothook added to the user-message renderer:unicode-bidi: plaintexton assistant prose blocks (p, headings,li,blockquote), user message text, and both composers (main + edit share thecomposer-rich-inputslot). Each paragraph-level block resolves its base direction from its own first strong character (UAX#9), andtext-align: startfollows the resolved direction — so Arabic paragraphs read and align right-to-left while English ones stay LTR, per paragraph. In the user bubble and composer, each line is its own bidi paragraph (whitespace-pre-line/pre-wrap).direction: ltr; unicode-bidi: isolate, so commands, flags, and file paths embedded in an RTL sentence keep their internal character order instead of having their neutrals (dots, slashes, dashes) reordered.directionis never changed anywhere, so layout, list indent, and the rest of the UI stay LTR as the issue requests ("do not force the whole app UI to RTL").No config knob in this iteration: plaintext auto-detection covers
autosemantics with zero configuration. Achat_text_direction: auto | ltr | rtloverride can be layered on later if maintainers want it.Verification
Rendered the compiled rules against mixed Arabic/English fixtures (assistant prose with inline code, lists, fenced block; multi-line user message; composer):
npm run dev --host 127.0.0.1intact as an LTR runmain.pyisolated, line 2 (English) LTRdeploy.shintactnpm run typecheck, eslint, and the desktop vitest suite pass (the 6 failing tests on this branch fail identically on cleanmain— pre-existing, unrelated areas).Notes / scope
This covers the desktop app chat (
apps/desktop), which is where HTML rendering makes bidi fixable. The dashboard's embedded/chatpage is an xterm.js terminal (web/src/pages/ChatPage.tsx) — xterm.js has no bidi support upstream (xtermjs/xterm.js#701), so the terminal canvas can't be fixed at this layer; that part would need upstream terminal support and is left out of scope here.🤖 Generated with Claude Code