Disable single-$ inline math so plain-text dollar signs are not parsed as KaTeX
#5684
Replies: 1 comment
|
Thanks for the very precise report, @marceloguedes. The bug is real and deterministic — I reproduced it against Confirmed: the pairing is not flanking-aware
function previous(code) {
return code !== 36 || this.events[this.events.length - 1][1].type === "characterEscape"
}There is no alphanumeric flanking check — no "reject That The regression: single-
|
| Input | Default | + alnum flanking guard | singleDollarTextMath:false |
|---|---|---|---|
cost $1.5B, target $7B |
inlineMath (garbage) | literal text ✅ | literal text ✅ |
$E = mc^2$ |
inlineMath ✅ | inlineMath ✅ | literal text ❌ |
$\theta$ |
inlineMath ✅ | inlineMath ✅ | literal text ❌ |
This is implementable in the existing seam. packages/client/ui-primitives/src/markdown/parse.ts:41 registers mathCompatibility() — a custom micromark extension (mathCompatibility.ts) that already exposes a previous construct and its own tokenizer. That's the right place to add the guard: extend the previous (and a symmetric close-side check) so a single $ adjacent to [A-Za-z0-9] is not treated as a delimiter, while $$…$$, \(…\), \[…\] and genuinely-padded $…$ all keep working.
The trade-off is narrower than the option flip: only the single-$ delimiter gets flanking rules, not the whole math feature. It keeps the three existing tests green and adds prose safety, instead of removing a tested capability.
If you'd like, I can put together the concrete mathCompatibility() guard patch and the two new regression fixtures ($1.5B / $7B literal, and re-affirm $E = mc^2$). Happy to iterate here before any PR.
Uh oh!
There was an error while loading. Please reload this page.
Target area:
packages/client/ui-primitives/src/markdown/(settled markdown pipeline)Summary
DSH's settled markdown arm enables
micromark-extension-mathwith default options. The upstream defaultsingleDollarTextMath: truemakes any pair of single dollar signs an inline math delimiter. Prose that contains ordinary currency amounts (e.g.The price moved from $1.5B to $7B this year) is therefore parsed as inline TeX from the first$to the last$in the paragraph, and everything in between is handed to KaTeX. When the "formula" is not valid TeX, KaTeX throws, and the renderer's error arm replaces the span with an error placeholder — the original prose is lost to the reader.The fix is a one-line option change:
math({ singleDollarTextMath: false }). All explicit math delimiters ($$…$$,\(…\),\[…\]) keep working; bare$…$becomes literal text again.Problem and reproduction
Given a settled message body such as:
the markdown parser produces an
inlineMathnode whose content is38.5B, up 5.8%, and guidance is for $3.0B of FCF— i.e. it pairs the first and last$of the sentence. The KaTeX error arm then renders an error box in place of the sentence. Symptoms reported in the GUI:The issue is deterministic and content-driven: it needs no user input beyond a normal message that mentions money twice.
Root cause
parseGfmWithMathinpackages/client/ui-primitives/src/markdown/parse.tsbuilds the finalized-arm micromark pipeline as GFM + math:micromark-extension-math(3.x) defaults to:With that default, the
mathTexttokenizer treats$…$as an inline math delimiter pair. The streaming arm (GFM only, no math extension) is unaffected — this is purely a settled-arm behavior.Proposed change
packages/client/ui-primitives/src/markdown/parse.ts:(Adapt to the actual pipeline call in the file — the change is the
math()invocation, wherever the finalized pipeline is assembled.)Behavior change matrix
cost $1.5B, target $7BinlineMath(garbage TeX, likely error arm)E = $$mc^2$$ inline$$not a single-$ pair)inlineMathnode$$E = mc^2$$on its own linemath(display) nodemath(display) node\( \frac{1}{2} \)inlineMathnode (mathCompatibility)\[ E = mc^2 \]math(display) node (mathCompatibility)$$left alone in prose (typo)Caveat (accepted consequence): with
singleDollarTextMath: false,$$is the only dollar-based delimiter, and a stray$$in prose will open/close display math. This mirrors KaTeX's own default configuration (KTeX/MathJax both disable single-$by default for exactly this reason) and is strictly less surprising for prose than the current behavior.Tests and fixtures to update
Any spec or golden fixture that asserts single-
$inline math must be re-expressed with the surviving delimiters ($$…$$,\(…\)):packages/client/ui-primitives/tests/markdown.client.spec.tsxpackages/client/ui-primitives/tests/markdown-incremental.client.spec.tsxpackages/client/ui-primitives/tests/markdown-dom-parity.client.spec.tsxpackages/client/ui-primitives/tests/fixtures/markdown-dom/math-*.streaming.txt(golden DOM; note the DOM-text convention in those files: backslashes are doubled in the file bytes, and\(…\)delimiters are consumed in the streaming arm so their content appears with plain parentheses)apps/web/tests/math-rendering.e2e.ts(+ itsui.expected.mdsnapshot if the DOM changes)Audit command to find remaining single-
$math assertions across the repo:rg -n '\$[\\a-zA-Z]' packages/client/ui-primitives/tests apps/web/testsEach hit must be audited: keep it (now legitimately literal) or rewrite it to
$$…$$/\(...\)if it is meant to assert math.Build and verification
Manual check: in a live GUI session, send/reply with a message containing two
$amounts in one paragraph; confirm both render as plain text and that a genuine$$E = mc^2$$block still renders as display math.All reactions