feat(web): render LaTeX math in chat via KaTeX#1035
Conversation
🦋 Changeset detectedLatest commit: 06201d3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7fce349091
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // runs once on first import of this module and is safe at module scope. Without | ||
| // the CSS the math renders unstyled, so both must travel together. | ||
| import 'katex/dist/katex.min.css'; | ||
| enableKatex(); |
There was a problem hiding this comment.
Guard literal dollar text before enabling inline math
When KaTeX is enabled globally here, ordinary assistant text that contains two literal dollar-prefixed tokens, such as Check $PATH before $HOME or costs $5 and $10, is now parsed as a single $...$ inline formula between the first two dollars and rendered as math instead of readable prose; before this patch those dollars stayed literal. Please add a guard/escape for common literal-dollar patterns or otherwise avoid enabling single-dollar math globally without that protection.
Useful? React with 👍 / 👎.
Resolve the modify/rename conflict: the KaTeX math-rendering change moved from src/components/Markdown.vue to its new home at src/components/chat/Markdown.vue after main's component-subdirectory refactor, keeping the feature intact on the new path.
Enabling KaTeX turned plain prose with two dollar-prefixed tokens
(`Check $PATH before $HOME`, `costs $5 and $10`) into a single
inline formula, since markstream's $…$ tokenizer has no
"no whitespace inside the delimiters" rule.
Add a postTransformTokens guard that turns a single-$ inline span back
into literal text when its content starts or ends with whitespace. Real
inline math is written tight (`$E=mc^2$`, `$\frac{1}{2}$`), while
the prose false-positives always have whitespace inside the delimiters,
so this keeps inline/block math working while leaving prices, env vars,
and ranges as readable text. Code spans are already excluded by the
tokenizer, and running on the flat token stream also covers dollars
nested inside lists and blockquotes.
Addresses the Codex review comment on PR #1035.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bdb5c351c9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if ( | ||
| child.type === 'math_inline' && | ||
| child.markup === '$' && | ||
| /^\s|\s$/.test(child.content ?? '') |
There was a problem hiding this comment.
Handle compact currency ranges before rendering math
When an assistant message contains compact prose amounts such as costs $5-$10 or $5/$10, the two dollar signs are still parsed as one math_inline span whose content has no leading/trailing whitespace, so this predicate lets it through. With KaTeX now enabled, the first amount is rendered as math and the second dollar delimiter disappears, corrupting common price/range text; the dollar guard needs to reject these non-LaTeX currency/variable patterns as well as the spaced examples.
Useful? React with 👍 / 👎.
Resolve an import conflict in chat/Markdown.vue: keep main's new copyTextToClipboard import (clipboard-over-HTTP fix) alongside the KaTeX literal-dollar guard import.
The literal-dollar guard only caught prose whose content had whitespace inside the delimiters, so a compact range like `costs $5/$10` still rendered `5/` as a formula and dropped the second dollar. (markstream's own currency check rejects `-`/`~` ranges but not `/`.) Extend the guard to also reject a single-$ span whose content is a numeric amount with a trailing range connector (`/`, `-`, `~`, en/em dash) -- a complete formula never ends in a dangling operator. Scoped to digit-led content so symbolic math is left alone, and numeric math that is not a range (`$5/2$`, `$5-2$`, `$0.5$`) still renders. Added tests for the range cases and the non-range math. Addresses the follow-up Codex review comment on PR #1035.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 08deac2884
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (/^\s|\s$/.test(content)) return true; | ||
| if (TRAILING_RANGE_CONNECTOR.test(content.trim())) return true; |
There was a problem hiding this comment.
Treat shell-variable pairs as literal dollars
When an assistant message mentions adjacent shell variables or a PATH-like value outside backticks, e.g. Use $HOME/bin:$PATH or $PATH:$HOME, markstream pairs the two dollar signs and produces a math_inline child with content like HOME/bin:/PATH:. Because this guard only rejects leading/trailing whitespace or numeric range connectors, those spans still go through KaTeX, so the web chat drops the literal dollars and typesets a shell path as math. Please extend the prose-dollar guard for shell-variable/path separators before enabling KaTeX globally.
Useful? React with 👍 / 👎.
Adjacent shell variables and PATH-like values (`Use \$HOME/bin:\$PATH`,
`\$PATH:\$HOME`) were still rendered as math, because the prose-dollar
guard only looked at the span's own content (whitespace inside the
delimiters, or a trailing numeric range connector) and never at what
touches the delimiters from the outside.
Replace the two bespoke heuristics with the two industry-standard rules,
now driven by the surrounding text tokens:
- Pandoc (tex_math_dollars): no whitespace immediately inside the
delimiters.
- GitHub: each \$ must be bounded on its outer side by whitespace, a
line boundary, or structural punctuation. A letter or digit there
means a second prose token, so the span is literal text.
The GitHub outer-boundary rule subsumes the old numeric-range check (a
closing \$ in \$5/\$10 is followed by a digit) and also catches
shell/path cases Pandoc's inner rule misses. Normal math -- including
bare \$x\$, \$x^2\$., and (\$x^2\$) -- still renders. Added
tests for shell/path values and punctuation-wrapped math.
Addresses the third Codex review comment on PR #1035.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 50c92564a3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const LEFT_BOUNDARY = /[\s([{]/; | ||
| const RIGHT_BOUNDARY = /[\s.,;:!?)\]}]/; |
There was a problem hiding this comment.
Allow localized punctuation around math spans
When a valid formula is followed by full-width/CJK punctuation or wrapped in quotes, this boundary guard classifies it as prose and rewrites the math_inline token back to text because only ASCII punctuation is accepted here. For example, Chinese chat output like 公式为 $E=mc^2$,其中... or a quoted formula like “$x$” will show raw dollars instead of KaTeX, which makes the new math rendering fail in common localized prose contexts.
Useful? React with 👍 / 👎.
The outer-boundary guard only accepted ASCII punctuation, so a formula followed by full-width punctuation or wrapped in typographic quotes was misclassified as prose: `公式为 \$E=mc^2\$,其中` and `“\$x\$”` showed raw dollars instead of rendering. Invert the boundary check from an allow-list of ASCII punctuation to a deny-list of ASCII letters/digits. A \$ glued to an ASCII letter/digit still means a second prose token (\$PATH:\$HOME, \$5/\$10), but whitespace, line boundaries, and every other character -- full-width punctuation, CJK ideographs, curly quotes -- is now a valid math boundary, which is the correct behavior for localized prose. Addresses the fourth Codex review comment on PR #1035.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 06201d3da5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (isProseDollarSpan(child.content ?? '', prevChar, nextChar)) { | ||
| child.type = 'text'; | ||
| child.markup = ''; | ||
| child.content = `$${child.content ?? ''}$`; |
There was a problem hiding this comment.
Preserve later math after literal-dollar spans
When a message contains a prose dollar before a real formula in the same inline run—for example costs $5 and formula $x$ or Use $HOME before $E=mc^2$—the tokenizer has already paired the first literal $ with the formula's opening $. This branch only converts that paired token back to text ($5 and formula $) and never re-tokenizes the remaining x$, so the actual formula is left as raw text even though KaTeX is now enabled. The guard needs to split/reparse or avoid consuming a later math opener as the closing delimiter for prose dollars.
Useful? React with 👍 / 👎.
Related Issue
No prior issue. Web chat showed raw
$…$/$$…$$instead of rendered math; this PR adds that rendering. Happy to convert to issue-first if preferred.Problem
LaTeX math in assistant messages rendered as raw dollar-delimited source in the web UI —
$E = mc^2$showed literally instead of as a formula. markstream-vue can render math via KaTeX, but the optional peer was never enabled or styled, so nothing rendered.What changed
Enable KaTeX in the web chat:
katexas a dependency and import its stylesheet (which also pulls in the bundled math fonts).$…$(inline) and$$…$$(block) parse and render.No application logic changes beyond the renderer wiring.
Checklist
data-markstream-pending, no raw$), KaTeX fonts load with no 404s, long block formulas scroll instead of overflowing, and colors are correct across terminal/modern/kimi themes in light and dark; the existing 70 web tests still pass and a code block still highlights (no regression).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update. — N/A: no user-facing docs affected.