Skip to content

fix(desktop): tables follow RTL message direction - #47911

Open
Adolanium wants to merge 1 commit into
NousResearch:mainfrom
Adolanium:fix/desktop-rtl-table-direction
Open

fix(desktop): tables follow RTL message direction#47911
Adolanium wants to merge 1 commit into
NousResearch:mainfrom
Adolanium:fix/desktop-rtl-table-direction

Conversation

@Adolanium

@Adolanium Adolanium commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Follow-up to #44596. The plaintext rules resolve text direction per prose block (paragraphs, headings, list items, blockquotes), but markdown tables were never covered and the th override was pinned to text-left. The visible result for a right-to-left table (Hebrew, Arabic, or any RTL script): it renders left-to-right with the first column on the left and every header stranded on the left edge, even though all of its content is RTL.

CSS alone cannot close this gap. A table's column order is its box direction, and unicode-bidi: plaintext (the property that resolves per-block text direction) never touches it; there is also no selector that can read direction off the cells' RTL content. So this PR uses the smallest possible HTML hook, plus CSS that follows it, with no JS logic:

  • the <table> carries dir="auto", so the browser's native first-strong algorithm resolves one base direction (and the column order) from the cells' content, in practice the column headers. The resolution is script-agnostic, so it covers any RTL language (Hebrew, Arabic, Persian, ...), not just one. An RTL table flips its columns so the first column sits on the right; a left-to-right table resolves ltr and renders byte-identically to current main.
  • table cells get unicode-bidi: plaintext, so each cell keeps its inline content in the order it was written. A value like 18 ₪, -3% or 123 units is never reordered by the surrounding RTL run.
  • alignment is pinned to the table's resolved direction with :dir() (table:dir(rtl) cells align right, :dir(ltr) left), so a column's header and its values stay on the same edge instead of splitting (an RTL header on the right with its numbers stranded on the left). The th override moves from text-left to the logical text-start.

Why :dir() rather than text-align: start: under unicode-bidi: plaintext, start resolves per cell, so a numeric or LTR cell falls to the opposite edge from its RTL header. Gating a physical text-align on the table's :dir() keeps every cell on the table's edge while plaintext still protects each value's internal order. :dir() is supported by the desktop's Chromium (Electron 40).

Direction is resolved per table, not per cell, which matches how RTL word processors lay out a table. Inline code inside a cell keeps the LTR-isolate from #44596, so commands and identifiers are unaffected.

Before (current main): an RTL table (Hebrew shown) renders left-to-right with its headers stranded on the left.

before-table

After (this PR): the same table resolves right-to-left. Columns flip, headers and cells align to the right, and values keep their authored order.

after-table

Both are captured in the desktop app, on a table generated by the model.

Related Issue

Follow-up to #44596 / #44150. The merged change covers prose blocks and notes that layout chrome stays LTR; this extends the same per-message direction to markdown tables, which it did not touch.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • apps/desktop/src/components/assistant-ui/markdown-text.tsx: dir="auto" on the table override; th from text-left to the logical text-start
  • apps/desktop/src/styles.css: unicode-bidi: plaintext on th/td, and text-align gated on table:dir(rtl)/:dir(ltr) so cells follow the table's resolved direction
  • apps/desktop/src/components/assistant-ui/table-direction.test.tsx: pins the contract: the table carries dir="auto", header cells use logical alignment (text-start, never text-left), and the hook is content-driven (an English table carries it too)

How to Test

  1. cd apps/desktop && npx vitest run --environment jsdom src/components/assistant-ui/table-direction.test.tsx - 3 passed
  2. npm run typecheck - clean; npx eslint on the touched files - clean
  3. Manual: ask for an RTL table (Hebrew or Arabic) - the columns flip (first column on the right) and headers and cells align right
  4. Ask for an RTL table with a price or percent column (18 ₪, -3%, 1,200 ₪) - the values keep their authored order and align with their header
  5. English tables - identical to current main (dir="auto" resolves ltr)

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass (renderer-only change; desktop vitest suite run instead)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Windows 11, dev build (npm run dev)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A (rendering only)

@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have labels Jun 17, 2026
@Adolanium
Adolanium force-pushed the fix/desktop-rtl-table-direction branch from 50610a7 to 63e8e18 Compare June 17, 2026 15:12
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.
@Adolanium
Adolanium force-pushed the fix/desktop-rtl-table-direction branch from 6a71dd6 to 6b1d2c1 Compare June 17, 2026 15:28
@alt-glitch alt-glitch added comp/desktop Electron desktop app (apps/desktop/*) and removed comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Jun 26, 2026
@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants