Repository navigation
fix(mobile): typeset \(…\) and \[…\] math when Render math is on - #29739
juliusmarminge wants to merge 6 commits into
Conversation
These tests reproduce #29599: \(…\) and \[…\] formulas reach md4c, which only typesets $ and $$ delimiters, so mobile shows them as raw text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The native md4c parser only knows $ and $$ delimiters, so replies using backslash delimiters showed raw TeX on iOS and Android. Rewrite just the delimiters of formulas the shared math parser (the one web renders with) finds, leaving code, escapes and container prefixes untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
md4c opens `$` only after a line edge, whitespace or punctuation and closes it only before one, and merges touching `$` runs. Rewrite a `\(`/`\[` formula only when its dollar delimiters flank and touch no other `$` or formula; otherwise leave it as authored. Skip the rewrite when unclosed delimiters could make the math parser scan more than a fixed budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous guard charged every `\(`, `\[` and `$` to the end of the message, so long replies full of closed formulas stayed raw while short unclosed-delimiter inputs still took up to a second to parse. The estimate now charges per character, per non-blank line and per delimiter. A failed opener also pays for how far it sits along its line. An unclosed `\(`, `\[` or `$$` rereads its paragraph. A `\[` alone on its line reads to the next line ending in `\]`, and `$$` reads to the next `$$`. Closed formulas cost nothing extra. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…conversion Charge a bare \[ fence line and stop inline reads at a backtick in the parse guard, leave replies whose literal $ md4c would pair with an inserted delimiter as authored, and fall back to the source when the range walker overflows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Mobile Markdown now renders recognized backslash-delimited math when Render math is enabled. The change preserves the existing preference and uses conservative fallbacks without altering stored content. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
|
Thanks @juliusmarminge for the fix and device evidence! #32180 (986a4f56a8) now covers this on main using native support for |
Problem
With Settings → Appearance → Render math on, the mobile app (iOS, iPadOS, Android) shows formulas written as
\(…\)and\[…\]as raw TeX, while$…$and$$…$$typeset. Web reads all four forms, anddocs/user/appearance.mdsays mobile does too. Reproduced on main at 60aec28 on a Pixel 9 dev client paired to an isolated dev server: a real Claude reply containing the issue's\[A=\begin{pmatrix}1&1\\0&1\end{pmatrix}\],\[\det(A-\lambda I)=0\]and inline\(…\)formulas rendered as[A=\begin{pmatrix}1&1\0&1\end{pmatrix}], in light and dark themes, while the$$…$$line in the same reply typeset.Mobile renders Markdown with react-native-enriched-markdown, whose md4c parser only recognises
$as a math delimiter.Change
markdownWithDollarMath(apps/mobile/src/lib/markdownDocument.ts) rewrites the delimiters of\(…\)and\[…\]formulas to$…$and$$…$$before the text reaches md4c.MobileEnrichedMarkdownTextapplies it only when Render math is on. Formulas are found with the sharedmarkdownMathRanges, the same parser web renders with, so code spans, fences, escapes and citations such as\[1\]are untouched. Only the two delimiter characters at each end are replaced, so the TeX and container prefixes like>stay as written.A message is left exactly as authored, which is what main shows, in these cases:
$: a formula touching a letter, a digit or CJK text, or touching another formula.$outside a dollar formula, such as a price or$HOME, which md4c would pair with an inserted delimiter.Messages without
\(or\[return before any parsing.Scope and approval
Fixes #29599
Verification
vp test run apps/mobile/src/lib/markdownDollarMath.test.ts: 8 passed.The issue's reply test fails on main. The unflanked, long-reply, literal-dollar and reread tests each failed before the commit that fixed them.
vp run check:changed: format, lint, knip, typecheck (2 packages), tests and React compiler all pass.Device captures below. Environment:
T3CODE_DEV_SEED=0.F1: the issue's reply
F3: guards. Eligible, punctuated, Unicode-flanked and consecutive-line formulas typeset. Suffixed, CJK-adjacent and touching formulas stay as on main. A reply with a price stays as authored.
F4: Markdown file preview (the file-embed preprocessing path) at app text size 22. It covers frontmatter, HTML, a blockquote, a list, a table, consecutive display lines, a wide fraction and invalid TeX.
F9: over the parse budget. A formula followed by 300 unclosed
\[lines stays as authored. The pair has zero changed pixels at threshold zero.F2: Render math off → on on the same thread, without resending. Steps: open Settings → Appearance, turn Render math off, open the thread (source shows), then turn it back on and reopen the thread (math typesets).
What else changed in the captures (before and after were seeded independently, so these are not frozen-clock comparisons):
Not verified:
<details>disclosure. The Android recording passes. The iOS recordings show the reopen only in their last frames, about 0.24 s before the export ends.cannot-verify ios-device. The simulator covers the same JS and native parser.The seed prompts and the F6/F8 harness are not linked: this VM had no file-upload tool. All fixtures are synthetic.
Model and harness: Claude Opus 5.5 (issue lead, implementation, red test, reviewer B) and GPT-6.1 Sol (reviewer A, proof audit, evidence capture), in T3 Code via Claude Code and Codex.
🤖 Generated with Claude Code