Skip to content

fix(ui): support bidirectional chat markdown#11944

Open
mjnaderi wants to merge 3 commits into
Kilo-Org:mainfrom
mjnaderi:fix-chat-bidi-markdown
Open

fix(ui): support bidirectional chat markdown#11944
mjnaderi wants to merge 3 commits into
Kilo-Org:mainfrom
mjnaderi:fix-chat-bidi-markdown

Conversation

@mjnaderi

@mjnaderi mjnaderi commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Issue

Text in right-to-left languages is currently rendered with the wrong direction in chat messages, which makes RTL and mixed RTL/LTR conversations hard to read. This affects plain user messages and rendered assistant markdown, especially lists, blockquotes, tables, and code nested inside RTL content.

Context

This PR adds bidirectional text support in chat messages. The goal is for Persian, Arabic, English, and mixed-language content to choose the correct base direction at the right visual boundary while preserving readable code rendering.

Implementation

The final implementation applies bidi direction as a post-render DOM decoration step in the shared Markdown component.

After Marked renders and DOMPurify sanitizes markdown, the existing decorate(...) path now adds dir="auto" to:

  • top-level markdown blocks that own their own direction, such as paragraphs, headings, blockquotes, lists, and tables
  • code elements, including fenced code blocks and inline code

The decoration intentionally only marks direct markdown children for block/container direction. It does not recursively add dir="auto" to nested paragraphs, list items, table cells, or quote contents. This matters because the HTML directionality algorithm for dir=auto skips text inside descendants that already have their own dir attribute. If every nested element gets dir="auto", then a parent list, table, or blockquote may fail to detect its RTL text and keep the wrong direction.

The bidi decoration logic is exported from the Markdown component module so it stays next to the existing DOM decoration helpers while still having focused regression coverage that does not depend on the full Solid rendering harness. The Markdown CSS also uses logical inline-start spacing and borders for lists and blockquotes, and table cells now use logical text-align: start so cell content follows the table’s resolved direction. Plain user message text bubbles also use dir="auto".

References:

I first implemented this with Marked renderer overrides for paragraphs, headings, lists, blockquotes, tables, and code. That worked functionally, but it required reimplementing some of Marked’s built-in HTML rendering, especially table/list markup. I changed the approach to the current decorate(...) implementation so Marked remains responsible for generating HTML and our code only annotates the rendered DOM with direction metadata. This reduces maintenance risk, avoids diverging from Marked’s native output, and keeps the bidi behavior in the same client-side decoration path already used for code wrappers and markdown links.

Screenshots / Video

before after
before after

How to Test

Manual/local verification

  • Manually built and tested the chat UI with different right-to-left, left-to-right, and mixed-language texts.
  • Verified mixed markdown content including paragraphs, headings, lists, blockquotes, tables, inline code, fenced code, and display math.
  • Ran bun test tests/unit/kilo-ui-contract.test.ts in packages/kilo-vscode/; passed.
  • Ran bun run typecheck in packages/ui/; passed.
  • Ran bun run typecheck in packages/kilo-vscode/; passed.

Reviewer test steps

  1. Open the VS Code extension chat panel.
  2. Send an RTL user prompt and confirm the user message bubble uses RTL direction.
  3. Ask the assistant for mixed RTL/LTR markdown with headings, paragraphs, lists, blockquotes, tables, inline code, fenced code, and display math.
  4. Confirm top-level Persian/Arabic/Hebrew paragraphs render RTL and English paragraphs render LTR.
  5. Confirm RTL list markers appear on the RTL side.
  6. Confirm RTL blockquotes place their border and padding on the RTL side.
  7. Confirm tables resolve direction at the table level and cell text aligns to the resolved inline start.
  8. Confirm code blocks remain readable after syntax highlighting, including when nested inside RTL lists.

Blocked checks and substitute verification

  • bun run typecheck could not be run in packages/kilo-ui because that package has no typecheck script. Substitute verification was bun run typecheck in packages/kilo-vscode, which consumes @kilocode/kilo-ui/message-part.

Checklist

  • Issue linked above, or exception explained
  • Tests/verification described
  • Screenshots/video included for visual changes, or marked N/A
  • Changeset considered for user-facing changes
  • I personally reviewed the diff and can explain the changes, including any AI-assisted work.

Comment thread packages/ui/src/components/markdown.tsx Outdated
Comment thread packages/ui/src/components/markdown.css Outdated
@kilo-code-bot

kilo-code-bot Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

This update adds a subtle background tint and a high-contrast-only border to [data-slot="user-message-text"] in the Kilo VS Code theme layer, addressing the earlier reviewer question about visually distinguishing user messages from assistant messages. The high-contrast override follows the existing body.vscode-high-contrast, body.vscode-high-contrast-light convention already used elsewhere in this package (agent-manager.css, high-contrast.css, migration.css, marketplace.css), and color-mix() is already used extensively across the UI packages, so no new browser-support concerns.

Files Reviewed (1 file)
  • packages/kilo-ui/src/components/message-part.css
Previous Review Summaries (2 snapshots, latest commit a8bb361)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit a8bb361)

Status: No Issues Found | Recommendation: Merge

Both previously flagged items are resolved in this update:

  • markBidi moved out of the shared markdown.tsx into packages/ui/src/kilocode/markdown-bidi.ts, following the established pattern for Kilo-specific markdown helpers.
  • margin-left: 0 converted to margin-inline-start: 0 for consistency with the rest of the bidi-aware rule.

The kilo-ui-contract.test.ts rewrite is also a nice improvement: it now imports and exercises the real markBidi implementation directly via happy-dom instead of the previous Bun.spawnSync eval-string harness.

Files Reviewed (5 files)
  • packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts
  • packages/ui/src/components/markdown.css
  • packages/ui/src/components/markdown.tsx
  • packages/ui/src/components/message-part.tsx
  • packages/ui/src/kilocode/markdown-bidi.ts

Previous review (commit e5730d1)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/ui/src/components/markdown.tsx 188 markBidi is additive Kilo logic living directly in a shared upstream file; consider moving it to ../kilocode/markdown-bidi.ts following the existing pattern used by mermaid/fast-path/stream-highlight/incremental-dom helpers

SUGGESTION

File Line Issue
packages/ui/src/components/markdown.css 63 margin-left: 0 stays a physical property in a rule otherwise converted to logical properties (padding-inline-start) for bidi support
Files Reviewed (7 files)
  • .changeset/chat-bidi-text.md - 0 issues
  • packages/kilo-ui/src/components/message-part.tsx - 0 issues
  • packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts - 0 issues
  • packages/ui/src/components/markdown.css - 1 issue
  • packages/ui/src/components/markdown.tsx - 1 issue
  • packages/ui/src/components/message-part.tsx - 0 issues
  • packages/ui/src/context/marked.tsx - 0 issues

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5-20260630 · Input: 50 · Output: 17.2K · Cached: 1.5M

Review guidance: REVIEW.md from base branch main

@marius-kilocode

marius-kilocode commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

@mjnaderi So whats a bit problematic with this: Left right actually separates user and assistant messages. So I wonder if the user messages should then be on the other side as well? How do other agent changes do that in this language?

@mjnaderi

mjnaderi commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@marius-kilocode I will check this and find a solution.

@mjnaderi

mjnaderi commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@marius-kilocode I decided not to move RTL user messages to the other side.

I checked a few other chat/agent-style interfaces, and they generally keep sender placement stable rather than changing the user message side based on text direction. If the bubble moved based on message language, mixed-language chats could make the same speaker jump between sides, which seems more confusing than helpful.

Example UI (ChatGPT):

Screenshot From 2026-07-08 16-45-48

Instead, I kept the user message position stable and made the bubble itself more visually distinct from the page background. Previously it used the same background as the surface behind it. The text still uses dir="auto", so RTL/LTR rendering is handled inside the message while the overall layout continues to identify who said what.

I also removed the user-message border, similar to other AI chat UIs, for a simpler look, and added a high-contrast fallback border so the bubble remains visible in high-contrast themes.

What do you think about this approach?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants