feat(desktop): auto-detect RTL/bidi text direction in chat - #44596
Merged
Conversation
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 #44065 and #44169; verified in Chromium that isolate removes inline code from the paragraph direction vote (the code-first case), making the JS dir-resolution in #44065 unnecessary. Fixes #44150 Co-authored-by: Adolanium <Adolanium@users.noreply.github.com> Co-authored-by: Adalsteinn Helgason <AIalliAI@users.noreply.github.com>
This was referenced Jun 12, 2026
OutThisLife
enabled auto-merge
June 12, 2026 02:07
Contributor
🔎 Lint report:
|
OutThisLife
force-pushed
the
bb/desktop-rtl-bidi
branch
from
June 12, 2026 02:39
167632c to
6c00077
Compare
19 tasks
alt-glitch
pushed a commit
that referenced
this pull request
Jun 14, 2026
feat(desktop): auto-detect RTL/bidi text direction in chat
AIalliAI
pushed a commit
to AIalliAI/Hermes
that referenced
this pull request
Jun 14, 2026
…l-bidi feat(desktop): auto-detect RTL/bidi text direction in chat
teknium1
pushed a commit
that referenced
this pull request
Jun 17, 2026
unicode-bidi:plaintext (#44596) resolves text direction per line, but list markers and the blockquote border are box chrome driven by the CSS direction property, which plaintext never sets, so an RTL list renders its numbers stranded at the far left edge. CSS cannot close this gap (:dir() only reads the dir attribute, never plaintext resolution), so ul/ol/blockquote carry dir="auto" and the browser resolves their box direction natively while the plaintext rules keep owning the text. Inline code carries dir="ltr", which HTML's auto algorithm skips, matching the no-vote contract the CSS isolate already gives it.
18 tasks
Adolanium
added a commit
to Adolanium/hermes-agent
that referenced
this pull request
Jun 17, 2026
Markdown tables were left out of the bidi work from NousResearch#44596: an RTL table (Hebrew, Arabic, ...) kept LTR column order with headers and cells pinned to the left, because the plaintext rules only cover prose blocks and the th was hardcoded text-left. Hang dir="auto" on the table so the browser resolves column order and a single base direction from the cells' content, in practice the column headers (a box-direction property unicode-bidi:plaintext cannot set and no CSS selector can read off the script). Cells then get unicode-bidi:plaintext so a value like "-3%" or "123 units" keeps its authored order instead of being reordered by the surrounding RTL run, and text-align is pinned to the table's resolved edge via :dir() so a column's header and values stay on the same side. English tables are unchanged.
T02200059
pushed a commit
to T02200059/hermes-agent
that referenced
this pull request
Jun 18, 2026
…l-bidi feat(desktop): auto-detect RTL/bidi text direction in chat
1 task
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…l-bidi feat(desktop): auto-detect RTL/bidi text direction in chat
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
unicode-bidi:plaintext (NousResearch#44596) resolves text direction per line, but list markers and the blockquote border are box chrome driven by the CSS direction property, which plaintext never sets, so an RTL list renders its numbers stranded at the far left edge. CSS cannot close this gap (:dir() only reads the dir attribute, never plaintext resolution), so ul/ol/blockquote carry dir="auto" and the browser resolves their box direction natively while the plaintext rules keep owning the text. Inline code carries dir="ltr", which HTML's auto algorithm skips, matching the no-vote contract the CSS isolate already gives it.
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
unicode-bidi:plaintext (NousResearch#44596) resolves text direction per line, but list markers and the blockquote border are box chrome driven by the CSS direction property, which plaintext never sets, so an RTL list renders its numbers stranded at the far left edge. CSS cannot close this gap (:dir() only reads the dir attribute, never plaintext resolution), so ul/ol/blockquote carry dir="auto" and the browser resolves their box direction natively while the plaintext rules keep owning the text. Inline code carries dir="ltr", which HTML's auto algorithm skips, matching the no-vote contract the CSS isolate already gives it.
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
…l-bidi feat(desktop): auto-detect RTL/bidi text direction in chat
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
unicode-bidi:plaintext (NousResearch#44596) resolves text direction per line, but list markers and the blockquote border are box chrome driven by the CSS direction property, which plaintext never sets, so an RTL list renders its numbers stranded at the far left edge. CSS cannot close this gap (:dir() only reads the dir attribute, never plaintext resolution), so ul/ol/blockquote carry dir="auto" and the browser resolves their box direction natively while the plaintext rules keep owning the text. Inline code carries dir="ltr", which HTML's auto algorithm skips, matching the no-vote contract the CSS isolate already gives it.
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…l-bidi feat(desktop): auto-detect RTL/bidi text direction in chat
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
unicode-bidi:plaintext (NousResearch#44596) resolves text direction per line, but list markers and the blockquote border are box chrome driven by the CSS direction property, which plaintext never sets, so an RTL list renders its numbers stranded at the far left edge. CSS cannot close this gap (:dir() only reads the dir attribute, never plaintext resolution), so ul/ol/blockquote carry dir="auto" and the browser resolves their box direction natively while the plaintext rules keep owning the text. Inline code carries dir="ltr", which HTML's auto algorithm skips, matching the no-vote contract the CSS isolate already gives it.
This was referenced Jul 29, 2026
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
…l-bidi feat(desktop): auto-detect RTL/bidi text direction in chat
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
unicode-bidi:plaintext (NousResearch#44596) resolves text direction per line, but list markers and the blockquote border are box chrome driven by the CSS direction property, which plaintext never sets, so an RTL list renders its numbers stranded at the far left edge. CSS cannot close this gap (:dir() only reads the dir attribute, never plaintext resolution), so ul/ol/blockquote carry dir="auto" and the browser resolves their box direction natively while the plaintext rules keep owning the text. Inline code carries dir="ltr", which HTML's auto algorithm skips, matching the no-vote contract the CSS isolate already gives it.
13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds automatic bidirectional text direction to the desktop chat, so Arabic/Hebrew/Persian/Urdu — and the common mixed RTL/English technical case — read and align correctly instead of running left-to-right with mangled punctuation order.
Each chat block resolves its own base direction from its first strong character (UAX#9), purely in CSS, scoped to the chat surfaces only:
unicode-bidi: plaintext+text-align: starton assistant prose blocks (p,h1–h6,li,blockquote), the user bubble's text lines, and both composers (main + edit share thecomposer-rich-inputslot). RTL blocks read and right-align RTL; English stays LTR; mixed conversations resolve per block. Thetext-align: startis load-bearing — the user bubble hardcodestext-left(USER_BUBBLE_BASE_CLASS), so without it RTL prose would read right-to-left but cling to the left edge.codeand KaTeX are pinneddirection: ltr; unicode-bidi: isolate.isolatemakes the bidi first-strong heuristic treat each as a single neutral, so a block that starts with a command (`./run.sh -v`followed by Arabic) still resolves RTL, and the command's own neutrals (dots/slashes/dashes) keep their order.code-card, user fences) are pinned LTR so they never mirror or right-align inside an RTL list item or blockquote.directionis never set, so app chrome, layout, and list indent stay LTR (the issue explicitly asks not to flip the whole UI). List markers stay on the left by design. English-only content is byte-for-byte unchanged.Supersedes
This supersedes #44065 and #44169 and unifies them into one minimal change (credited via
Co-authored-by):feat/desktop-rtl-bidi) took the JS route — atext-direction.tsfirst-strong resolver plusdir-attribute plumbing acrossmarkdown-text.tsx,user-message-text.tsx,thread.tsx, and the composer (~280 lines) — specifically to handle the "code-first" paragraph (a block starting with a command shouldn't flip LTR). I verified in Chromium (Electron's engine) thatunicode-bidi: isolatealready removes inline code from the paragraph's first-strong vote, so that case is handled in CSS and the JS is unnecessary.fix/44150-chat-rtl-bidi) was the right pure-CSS approach but didn'ttext-align: start(so RTL user bubbles read RTL but stayed left-pinned under the bubble'stext-left) and didn't pin fenced code blocks against mirroring inside RTL list items.This PR keeps #44169's CSS-only footprint and fixes both gaps. Please close #44065 and #44169 in favor of this.
Related (out of scope, no overlap)
/chatis an xterm.js terminal; xterm has no upstream bidi support, so it can't be fixed at this layer.Type of Change
How to Test
مرحبا كيف حالك— the bubble right-aligns.`./scripts/run.sh -v` ماذا يفعل هذا الأمر؟— it still right-aligns (code doesn't vote on direction) and the chip keeps its internal order.main.Verification
npm run typecheck— cleaneslinton touched files — cleanvitestfailures on this branch are identical to cleanmain(pre-existing, unrelated).Fixes #44150