Skip to content

feat(render): support right-to-left (RTL) languages in the preview - #797

Open
mostafasarhan wants to merge 1 commit into
sftwrdotdev:masterfrom
mostafasarhan:rtl-arabic-support
Open

mostafasarhan wants to merge 1 commit into
sftwrdotdev:masterfrom
mostafasarhan:rtl-arabic-support

Conversation

@mostafasarhan

@mostafasarhan mostafasarhan commented Sep 18, 2026

Copy link
Copy Markdown

What this is

Adds right-to-left (RTL) language support to rendered markdown, so content in RTL scripts displays correctly in the preview, the HTML export and print/PDF. No issue tracks this — it's a gap I hit opening an Arabic document.

Mechanism

comrak emits plain <p> / <li> / <h1> blocks with no dir attribute, and .markdown-body pins text-align: left (in src/styles.css and MarkdownViewer.svelte). RTL content therefore renders left-to-right: paragraphs line up on the left, and mixed text (digits, URLs, a Latin word inside an RTL sentence) scrambles in the visual order.

The fix is a single post-processing pass in processMarkdownHtml — the one choke point the preview, export and print all go through — that sets dir="auto" on text-bearing block elements (p, h1–h6, li, blockquote, td, th, figcaption, dt, dd), plus text-align: start instead of left on .markdown-body. dir="auto" lets the browser resolve each block's direction from its own first strong directional character — the same rule GitHub renders markdown with. pre and inline code are deliberately excluded so source stays LTR.

Scope

  • Only the preview/export/print path is touched. The editor (Monaco) has its own, separate RTL limitations and is left alone.
  • A dir the author already set is never overridden.
  • The footnote tooltip and frontmatter-summary text-align: left are left as-is (UI chrome, not document content).

Tests

No new behaviour test — the change is observable in the rendered output, and the existing suite already runs the real processMarkdownHtml end-to-end (foldKeys.test.ts, foldStatePerDocument.spec.ts, mathDelimiterContract.test.ts, …).

One regex in exportRichContent.test.ts was relaxed. It asserted <p data-math="display" data-math-source="E = mc\^2"><span class="katex"> with the attributes in one exact order; the assertion's intent — the element contains the typeset formula, not its source — is unchanged, but the new dir attribute lands between data-math-source and the content. It now reads <p data-math="display" data-math-source="E = mc\^2"[^>]*><span class="katex">, matching the order-tolerant [^>]*> pattern already used on the line below it.

Verification

npm run check    → 0 errors, 0 warnings
npm test         → 1023/1024 pass; the single failure is "official macOS signing
                   refuses partial credentials" (releaseWorkflow.test.ts), a
                   macOS-keychain test that cannot pass on this Windows box and
                   is unrelated to the change

Manual: rendered an Arabic sample (heading, mixed Arabic/English/digit paragraph, list, blockquote, table, code block) before and after — after the change paragraphs, lists and table cells align right and mixed text reads in the correct visual order, while code stays LTR.

Not verified locally: cargo test (frontend-only change, Rust untouched — left to CI) and macOS/Linux rendering.

comrak (the Rust renderer) emits plain block elements with no dir
attribute, so Arabic and Hebrew content renders left-to-right. Add
dir="auto" to text-bearing block elements and switch .markdown-body to
text-align:start so each block resolves its own direction. Code blocks
(pre/code) deliberately stay LTR.

Covers preview, HTML export and print/PDF through processMarkdownHtml.
Relaxes one export test regex that pinned attribute order rather than
the behaviour it asserts.
@mostafasarhan mostafasarhan changed the title feat(render): support right-to-left text (Arabic, Hebrew) in the preview feat(render): support right-to-left (RTL) languages in the preview Sep 18, 2026
@PathGao

PathGao commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Thanks for this. Markpad has no RTL handling anywhere today, so this change is the base for RTL support as a whole. It is also what #192 asks for, so I'd like this PR to be the answer to that request.

A few things before merge:

  1. Please drop the .demo/ line from .gitignore. It is for your local demo files, not the project.
  2. Lists and blockquotes are still anchored to the left in src/styles.css: ul, ol use padding-left: 2em and blockquote uses border-left. In an RTL list the bullet moves to the right edge, where there is no padding, and .markdown-body has overflow-x: hidden, so it can get clipped. The quote bar also stays on the left. Switching these to logical properties (padding-inline-start, border-inline-start) and adding ul, ol to the dir="auto" selector should fix both, since the padding side follows the list's own direction, not the li's.
  3. Please add a small test that runs processMarkdownHtml on a document with a paragraph, a list and a fenced code block, and asserts that p and li get dir="auto", pre and code don't, and an author-set dir is kept.

Two questions:

  • After this PR, do you expect the preview and the HTML/PDF export to be fully correct for RTL documents? Are there cases you know are still off, such as tables, task lists, footnotes or heading anchors?
  • The editor stays left-aligned. Is that right, and is it the best we can do? ✨Feature Request: Support for Right-to-Left (RTL) Text Direction for Languages like Persian and Arabic #192 also asks for RTL in the editor. As far as I know Monaco has no RTL layout mode, so I'm fine leaving it, but I'd like to hear what you saw with the Arabic document.

Once it's updated I'll test it on macOS as well.

@PathGao

PathGao commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Following up with the full picture, since I'd like this PR to cover RTL in the preview and export completely. My earlier point 2 was only the tip of it.

1. Put dir="auto" on containers too: ul, ol, table, blockquote.
The list indent and the marker side come from the ul/ol, not the li. With dir only on li, a mostly Arabic list with one English item (a product name, say) flips that one item and its bullet to the other side. On the container, the first item decides for the whole list. The same goes for column order in a table.

2. Left-anchored rules in src/styles.css that lay out document content. These should become logical properties (*-inline-start / *-inline-end, inset-inline-*):

  • blockquote border-left (906), and the print version (1608)
  • ul, ol padding-left (913), and the print version (1599)
  • .markdown-alert border-left (1144), the five border-left-color rules (1208–1248), and the title icon margin-right (1176)
  • .callout-fold-icon margin-left: auto (855, 1183). In RTL it ends up next to the title instead of at the far end.
  • .header-fold-icon right: 100% + margin-right (790–791). It hangs off the left of the heading.
  • .task-list-item padding-left: 0; margin-left: -1.5em (1743–1744)
  • footnotes: .footnotes ol padding-left (1101), the :target::before marker left/right (1111–1113), and the backref margin-left (1878)
  • a.block-id-anchor margin-left (1693)

These should stay physical: the checkmark drawn inside the task checkbox (1795, 1800, flipping it mirrors the tick), the code block .lang-label (code is always LTR), the CSV number alignment, the frontmatter tag chips, and the app layout (split divider, TOC side).

Two more places show document text: the footnote tooltip (text-align: left in MarkdownViewer.svelte) and the TOC entries. I'd treat both as content, since an Arabic footnote or heading shows up there.

3. Mixed-language cases to check.

  • A paragraph that starts with a Latin word, a number or a link (iPhone 15 هو …) resolves to LTR. As far as I can tell that's inherent to dir="auto".
  • A block with no strong character at all (a table cell with only 2024 or 45%, an image-only paragraph). Older spec text resolves that to LTR, newer text inherits from the parent. Could you check what WebView2 does with a numbers-only cell inside an Arabic table? I'll check WebKit on macOS. If it falls back to LTR, the numeric column aligns against the text columns.

For the first case, do you think a manual override is worth it, e.g. dir: rtl in the front matter? I'd rather leave it out unless you've run into it in real documents.

4. Editor. Agreed that it stays as it is. Monaco has no RTL layout mode (microsoft/vscode#11770 has been open since 2016), so there's nothing reasonable to do there in this PR.

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