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
Contributor
🔎 Lint report:
|
167632c to
6c00077
Compare
19 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