Skip to content

fix(web): render Hebrew/Arabic chat markdown right-to-left - #7574

Closed
nioasoft wants to merge 5 commits into
pingdotgg:mainfrom
nioasoft:fix/chat-markdown-bidi
Closed

nioasoft wants to merge 5 commits into
pingdotgg:mainfrom
nioasoft:fix/chat-markdown-bidi

Conversation

@nioasoft

@nioasoft nioasoft commented Aug 19, 2026 •

Copy link
Copy Markdown

What changed

Hebrew / Arabic (and any other RTL script) in chat messages rendered left-to-right: punctuation jumped to the wrong end of the line, list markers sat on the left, the blockquote bar was on the wrong side, table columns flowed LTR. This makes every markdown block in .chat-markdown resolve its own direction from its content — no global flip, no setting:

  • index.css: leaf blocks (p, li, h1–h6, td, th, dt, dd) get unicode-bidi: plaintext; text-align: start — the browser picks the base direction per block from its first strong character. pre/code stay LTR. Physical padding-left / border-left / text-align: left on ul/ol/blockquote/td/th become logical (padding-inline-start, …).
  • ChatMarkdown.tsx: ul, ol, blockquote, table (and the GitHub-alert div) get dir="auto", so list markers, the quote bar and column order land on the content's side.

The composer already works: Lexical stamps dir="auto" on root paragraphs.

Why

Mixed-language threads are common for non-Latin users; this is the standard per-block approach (same idea the Claude.ai / Claude Code markdown renderer uses — per-block first-strong direction) done with native dir="auto" + CSS instead of JS. English content is unaffected: an LTR block resolves exactly as before.

Diff: +45 / −11, two files, no new deps.

Before / after

Before After
before after

Test plan

  • vp test (web unit, markdown suites), tsgo --noEmit, vp lint, vp fmt --check — green
  • Manually in the web app: Hebrew user message + assistant reply with heading, bullets, numbered list, inline code, blockquote, table, followed by an English paragraph (screenshots above)
  • English-only thread unchanged

Note

Low Risk
Presentation-only bidi/CSS changes in chat markdown and scroll-fade masks; no auth, data, or API behavior.

Overview
Hebrew/Arabic (and other RTL) chat markdown now follows per-block first-strong direction instead of always rendering LTR. Mixed-language threads stay mixed: English blocks are unchanged, code stays LTR.

Lists, blockquotes, GitHub alerts, and tables get dir="auto" (tables via firstStrongDirection + DirectionProvider on the scroll viewport) so markers, quote bars, and column order sit on the content’s start edge. Overflowing RTL tables open on the first/rightmost column, and scroll-fade masks swap physical left/right under dir="rtl".

CSS switches list/quote/cell padding, borders, and alignment to logical properties, with unicode-bidi: plaintext on leaf text blocks. Unit tests cover first-strong detection including astral RTL scripts.

Reviewed by Cursor Bugbot for commit 94394cd. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Render Hebrew/Arabic chat markdown right-to-left in ChatMarkdown

  • Added firstStrongDirection and hastTextContent utilities in ChatMarkdown.tsx to detect text direction from the first strong Unicode letter in a node.
  • Updated blockquote, alert, ol, and ul markdown renderers to use dir="auto". Tables compute their direction and pass it to the MarkdownTable component.
  • Replaced physical CSS properties with logical equivalents (e.g., padding-inline-start) in index.css and set unicode-bidi: plaintext on leaf blocks. Code blocks remain LTR.
  • Fixed ScrollArea fade masks to align with RTL scroll edges.
  • Risk: Lists, blockquotes, and tables now resolve base text direction from content rather than defaulting to LTR, which may affect the visual layout of mixed-language content.

Macroscope summarized 94394cd.

@coderabbitai

coderabbitai Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f2e5bd2-e5a4-4db0-b189-2b2a6100174c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 19, 2026
Comment thread apps/web/src/index.css Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two bidi issues in ChatMarkdown.tsx where the new dir="auto" does not actually produce the direction the CSS rules assume.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ChatMarkdown.tsx Outdated
Comment thread apps/web/src/components/ChatMarkdown.tsx

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8e7f514. Configure here.

Comment thread apps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeapp Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR changes the layout and scrolling behavior of rendered chat markdown across paragraphs, lists, alerts, blockquotes, tables, and code, with additional RTL handling in a shared scroll-area component. The bidi and browser-layout interactions are non-trivial, while automated coverage is limited to the direction-detection helper.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One new finding on the RTL scroll-viewport change: the dir now on ScrollArea makes the DOM viewport RTL, but Base UI's ScrollArea reads direction from DirectionProvider, not the dir attribute, so the scrollFade overflow vars are computed with LTR assumptions. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ChatMarkdown.tsx Outdated
@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 19, 2026
Comment thread apps/web/src/components/ChatMarkdown.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the RTL alert layout; the rest of the bidi work (DirectionProvider on the table viewport, logical properties in index.css, the rtl: mask swap against Base UI's logical --scroll-area-overflow-x-* vars) looks consistent.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ChatMarkdown.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding: the new text-align: start in the .chat-markdown bidi rule clobbers author-provided alignment in sanitized raw HTML (PR descriptions, README previews). Details inline. The rest of the bidi work checks out — Base UI's --scroll-area-overflow-x-start/end are indeed logical (scrollLeftFromStart/scrollLeftFromEnd, negated for RTL) and are computed from useDirection(), so the DirectionProvider + dir pairing on the table ScrollArea and the rtl: mask swap in scroll-area.tsx line up with the primitive's contract; the alert label's dir="ltr" on the <span> (not the row) is correctly skipped by the container's dir="auto" resolution while the row still follows the container's direction.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/index.css
@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 19, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
Every leaf block in .chat-markdown resolves its own base direction from its
first strong character (unicode-bidi: plaintext + text-align: start), and
lists / blockquotes / tables get dir="auto" so markers, the quote bar and
column order land on the content's side. Physical paddings/borders on those
containers become logical. Code stays LTR. No global flip: a mixed
English/Hebrew message renders block by block.
- GitHub alerts: the injected English label was the first strong character, so
  dir="auto" on the container never resolved RTL. Give the label dir="ltr" so
  the auto algorithm skips it and the body decides the side of the bar/padding.
- Tables: put dir on the ScrollArea root rather than only the <table>, so an
  overflowing RTL table opens scrolled to its first (rightmost) column.
Resolve the table's direction from its text (first strong letter) instead of
dir="auto", pass it to the ScrollArea and to Base UI's DirectionProvider so
the viewport's scroll-edge math matches the rendered direction, and swap the
scroll-fade mask sides under rtl since Base UI's overflow vars are logical
while the mask utilities are physical.
…RTL scripts

- dir="ltr" now sits on the alert label text only, not the flex title row, so in a
  Hebrew alert the icon + label follow the bar and body to the right.
- firstStrongDirection recognises the astral RTL blocks (U+10800–U+10FFF,
  U+1E800–U+1EFFF: Phoenician … Adlam).
start is the initial value, so the declaration only ever overrode the HTML
align presentational hint that raw-HTML surfaces (PR bodies, README previews)
rely on. unicode-bidi: plaintext alone aligns each block to its own start edge.
@nioasoft
nioasoft force-pushed the fix/chat-markdown-bidi branch from 983c40b to 94394cd Compare August 24, 2026 12:20
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 24, 2026 12:20

Dismissing prior approval to re-evaluate 94394cd

@t3dotgg

t3dotgg commented Aug 28, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We are keeping OPEN #7466 as the review path for right-to-left chat text. The focused markdown tests here remain useful reference.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants