Skip to content

feat(desktop): auto-detect RTL paragraph direction in chat#44169

Closed
AIalliAI wants to merge 1 commit into
NousResearch:mainfrom
AIalliAI:fix/44150-chat-rtl-bidi
Closed

feat(desktop): auto-detect RTL paragraph direction in chat#44169
AIalliAI wants to merge 1 commit into
NousResearch:mainfrom
AIalliAI:fix/44150-chat-rtl-bidi

Conversation

@AIalliAI

Copy link
Copy Markdown
Contributor

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-slot hook added to the user-message renderer:

  • unicode-bidi: plaintext on assistant prose blocks (p, headings, li, blockquote), user message text, and both composers (main + edit share the composer-rich-input slot). Each paragraph-level block resolves its base direction from its own first strong character (UAX#9), and text-align: start follows 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).
  • Inline code and KaTeX output are pinned 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.
  • Fenced code blocks (CodeCard/Shiki, user fences) are untouched and keep the document's LTR direction — direction is 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 auto semantics with zero configuration. A chat_text_direction: auto | ltr | rtl override 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):

  • Arabic paragraph → RTL, right-aligned, npm run dev --host 127.0.0.1 intact as an LTR run
  • English paragraph → unchanged
  • User message → line 1 (Arabic) RTL with main.py isolated, line 2 (English) LTR
  • Composer → follows typed text direction, deploy.sh intact
  • Fenced block → LTR

npm run typecheck, eslint, and the desktop vitest suite pass (the 6 failing tests on this branch fail identically on clean main — 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 /chat page 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

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>
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have labels Jun 11, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related: #44065 (desktop per-message dir=auto RTL, same goal, competing implementation) and the requesting issue #44150; TUI counterpart #41454/#41462. Maintainer should pick one of the two desktop RTL approaches.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

@OutThisLife

Copy link
Copy Markdown
Collaborator

Superseded by #44596, which builds on this CSS-only approach and fixes two gaps: it adds text-align: start (RTL user bubbles were reading RTL but staying left-pinned under the bubble's text-left) and pins fenced code blocks LTR so they don't mirror inside RTL list items. Credited you via Co-authored-by. Thanks — this was the right direction.

@OutThisLife

Copy link
Copy Markdown
Collaborator

Superseded by #44596 (unified CSS-only RTL/bidi approach). Closing per the superseding PR.

ilyanaxo pushed a commit to ilyanaxo/hermes-agent that referenced this pull request Jun 12, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Native RTL support for Arabic and mixed RTL/LTR text in Hermes Chat

4 participants