Skip to content

feat(web): add opt-in rendering for TeX equations - #18202

Open
TheAnimatrix wants to merge 11 commits into
pingdotgg:mainfrom
TheAnimatrix:feat/render-chat-math
Open

TheAnimatrix wants to merge 11 commits into
pingdotgg:mainfrom
TheAnimatrix:feat/render-chat-math

Conversation

@TheAnimatrix

@TheAnimatrix TheAnimatrix commented Oct 11, 2026 •

Copy link
Copy Markdown

Problem

Assistant replies show raw TeX. Fractions, powers, Greek letters and table values are hard to read, which makes T3 Code a poor fit for anyone working with math: researchers, students, engineers, teachers (#9641). Rendering equations would greatly improve it for all of them, and for anyone who meets math in an agent response.

Change

Settings → Appearance → Render math, off by default, in the shared web/desktop Markdown renderer.

  • Syntax: \(…\) inline; \[ / \] or $$ on their own lines for display. Single $, one-line \[…\] and fenced math blocks stay as written, so currency, shell variables and \[1\] citations are unaffected.
  • One micromark construct (markdown-math.ts), registered only when the setting is on and the message contains a candidate delimiter. Off has no parsing or loading cost.
  • KaTeX is lazy-loaded with its stylesheet from bundled assets. It is already in the lockfile through Mermaid; this PR adds no newly resolved packages, no remark/rehype math plugins, and no change to packages/shared or the sanitizer.
  • Copying, table CSV export and composer quotes yield the TeX source, identically before and after KaTeX loads. Invalid TeX or a failed chunk load falls back to the source in place. An unclosed display block (including one still streaming) runs to the end of its container and shows as source, like an unclosed code fence.

Scope

This follows Julius's condition in #1784: opt-in, "if it doesn't add too much complexity", without "tons of remark plugins parsing latex." The delimiter set is fixed and documented; it is the forms models emit as standalone math, excluding single $, which is where currency and code-span bugs arise. Native mobile has a separate renderer and is out of scope.

Complexity, for that ruling: 582 runtime lines, 348 of them in one new file (markdown-math.ts) that imports nothing from the app. The shared renderer changes by 27 lines. Copy and citation code change by 77 lines, all keyed on one data attribute, and can be dropped if that is the preferred scope. The remaining 904 lines are tests. The scope question is open in #9641; the decision will be linked here before review handoff.

Related PRs

PR Default Footprint
This PR Off 0 new lockfile packages; no shared-pipeline or sanitizer edits
#17294 On (Off / Readable / On) rehype-katex, mdast-util-math, micromark-extension-math, @types/mdast (20 lockfile packages); edits the shared pipeline and sanitizer
#14574 Off Adds single-$ inline math; mdast-util-math; edits the shared pipeline
#16469 Off (draft) $$ only; remark-math (8 lockfile packages); edits the sanitizer
#17217 No setting Web + mobile; MathJax and KaTeX (17 lockfile packages); edits the shared pipeline

Earlier attempts (#9204, #9838, #10698) were closed for automatic activation or unapproved scope; #16724 (always on, remark-math + rehype-katex) is closed. This PR is the only one that is opt-in, adds no packages and leaves shared code untouched.

Verification

  • Replayed the reported response in the web client: 0 → 17 rendered formulas, including all display blocks; light/dark themes and a 390px viewport (wide equations scroll inside the message). Disabling restores the previous rendering with no KaTeX requests.
  • Added a $$ block to the same message: it typesets and copies as \[…\], while $5, $10, `$HOME` and a one-line $$x$$ in the same paragraph stay literal.
  • Web component and Markdown suites pass (277 files / 3,674 tests). Math coverage includes code spans, fences, tables, lists, blockquotes, unclosed blocks ending at their container (linear time), the $$ cases above, copy/quote/CSV parity across loading states, and failed chunk loads. Web typecheck, lint (no new warnings) and format pass.
  • Not tested: the native desktop shell and mobile. Known limit: find-in-thread counts matches in the message source, so counts inside typeset formulas can differ.
Before After enabling Render math
Before: raw TeX After: rendered equations

Dark theme

Replaces #17024, which was closed for not disclosing the model and harness.

Model: Claude Opus 5.5 (claude-opus-5-5), with Claude Sonnet 5.5 subagents for implementation. Harness: Claude Code in T3 Code.

# Conflicts:
#	apps/web/src/components/ChatMarkdown.test.tsx
#	apps/web/src/components/ChatMarkdown.tsx
- Accept `$$` alone on a line as a display fence, alongside `\[` / `\]`.
- Read the math setting once through the renderer context.
- Identify formula wrappers by `data-markdown-math` alone.
- Drop KaTeX options that restate its defaults; use the catalog for micromark-util-types.
- Trim comments and docs.
An unclosed `\[` or `$$` block failed after consuming later lines, which
flattened the lists and quotes that followed. Like a fenced code block,
it now ends at its closing line or the end of its container, and an
unclosed block is shown as source instead of typeset.
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Oct 11, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds a substantial TeX rendering capability with a custom parser, lazy KaTeX integration, new settings, and cross-cutting clipboard and citation behavior. Although disabled by default and well covered by tests, its size, scope, and persisted product setting warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Copy link
Copy Markdown
Member

Note

This comment is posted by Julius' dot

The opt-in setting and lazy loading follow Julius's condition in #1784, but the custom TeX parser and changes to copying and citations still need a scope decision. Does this implementation meet that condition, or should it be narrowed? Please link the maintainer's decision here before review handoff, as required by the scope policy.

@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a59aa123-92b7-4e24-823b-9af060579d30

📥 Commits

Reviewing files that changed from the base of the PR and between 3d2d9b8 and 1587811.


⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

📒 Files selected for processing (2)
  • apps/web/src/components/ChatMarkdown.tsx
  • packages/contracts/src/settings.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.



📝 Walkthrough

Walkthrough

Adds an opt-in setting to parse and render inline and display TeX in chat. Clipboard, table export, and citation handling use TeX source for rendered formulas.

Changes

Chat math rendering

Layer / File(s) Summary
Math rendering preference
packages/contracts/src/settings.ts, apps/web/src/components/settings/SettingsPanels.tsx, apps/web/src/components/settings/settingsSearch.ts, apps/desktop/src/settings/DesktopClientSettings.test.ts, docs/user/appearance.md
Adds the default-off mathRenderingEnabled setting, its searchable Typography toggle and reset behavior, and documentation of supported delimiters and copy behavior.
TeX parsing and chat rendering
apps/web/src/markdown-math.ts, apps/web/src/markdown-math.test.ts, apps/web/src/components/ChatMarkdown.tsx, apps/web/src/components/chat/KatexMath.tsx, apps/web/src/components/chat/MarkdownMath.tsx, apps/web/src/components/ChatMarkdown.test.tsx, apps/web/src/components/chat/MarkdownMath.test.tsx, apps/web/package.json
Adds inline and display TeX parsing, conditional chat rendering through KaTeX, and tests for syntax, rendering, and fallback behavior.
TeX clipboard and citation handling
apps/web/src/markdown-clipboard.ts, apps/web/src/lib/assistantTextSelection.ts, apps/web/src/components/ChatMarkdown.test.tsx, apps/web/src/components/chat/MarkdownMath.test.tsx
Uses TeX source for clipboard output, table serialization, and citation ranges when selections include rendered formulas. Tests cover selections during and after KaTeX loading, and after load failure.

Priority: ⬇️ Low

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Settings
  participant ChatMarkdown
  participant remarkTexMath
  participant MarkdownMath
  participant KatexMath
  Settings->>ChatMarkdown: provide mathRenderingEnabled
  ChatMarkdown->>remarkTexMath: parse text when enabled and a math opener is present
  ChatMarkdown->>MarkdownMath: render recognized TeX node
  MarkdownMath->>KatexMath: load KaTeX for TeX rendering
Loading

Possibly related PRs

  • pingdotgg/t3code#17024: Implements opt-in TeX rendering with overlapping setting, parsing, KaTeX, clipboard, and citation changes.

Suggested reviewers: juliusmarminge


Merge Risk | 🟡 Moderate · up to 15878

Merge Risk: 🟡 Moderate · up to 15878

Link the required scope decision or narrow the feature before merging, and address the potential responsiveness regression for long math-containing replies.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 15878

Math rendering is disabled by default and retains restrictive rendering controls and source fallbacks. The main remaining risk is increased client-side parsing work during long streamed replies containing math delimiters. No new privileged execution or cross-service exposure was observed.

Retained concerns

  • Low · security · inferred: With math enabled, a model-provided candidate delimiter disables completed-fence incremental parsing for the entire message, even inside ordinary code or otherwise non-math text. Subsequent streamed updates reparse accumulated content. For many small growing updates, this increases cumulative parsing work and can impair client responsiveness; source and exception fallbacks do not interrupt expensive synchronous parsing. This is an inferred client-availability risk, not a demonstrated denial of service.
Security review details

Security Blast Radius

  • inferred — The newly reachable parsing and typesetting work directly affects opted-in web or desktop rendering contexts. The inspected paths do not confer additional service, filesystem, credential, or agent authority. Expensive synchronous work can affect the client beyond the individual formula's fallback boundary.

Security Findings and Attack Paths

  • inferred — The supported availability path requires math to be enabled and attacker-influenced content to reach an assistant reply. A candidate delimiter in a growing reply suppresses prefix-cache reuse, amplifying repeated parsing. Exploitability and a client-disruption threshold remain unmeasured; no executable-content bypass was identified in the inspected path.

Trust Boundaries and Controls

  • observed — The existing raw-HTML sanitization and URL transformation pipeline remains active. Math recognition additionally requires a matching language class, numeric source offset, and corresponding opener at that offset, rather than trusting an authored HTML class alone. Generated KaTeX HTML relies on the dependency's trust-disabled rendering contract.

Resilience and Maintainability Implications

  • observed — Unclosed display blocks terminate as source at their container boundary. Loading and rendering failures preserve formula source and neighboring content. Parser safeguards abandon repeated inline openers, and tests specify single-parse timing bounds; these controls address rescanning and failure containment, not cumulative work across streamed updates.

Hardening Proposals

  • proposed — Consider a bounded streaming rendering strategy, such as source-only display above a work budget or interruptible parsing, while preserving source offsets and copy/citation identity. Do not simply combine the custom syntax extension with the existing prefix cache without establishing its document-wide invariants.

Pre-merge checks | Passed 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: opt-in TeX equation rendering for the web client.
Description check Passed The description covers the problem, implementation, scope, related work, verification, limitations, screenshots, and agent disclosure. The maintainer scope decision is still pending and should be link…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@TheAnimatrix

Copy link
Copy Markdown
Author

Note

Written by claude-opus-5-5 on behalf of TheAnimatrix

Asked for the scope ruling in #9641: #9641 (comment). I'll link the decision here, and narrow the PR if needed, before review handoff.

NikitaMGrimm pushed a commit to NikitaMGrimm/t3code that referenced this pull request Oct 12, 2026
Quoting a response, exporting a table to CSV, and pasting rich text used
KaTeX's glyph markup for formulas rendered by the selected LaTeX PR
(pingdotgg#14574). Each formula now contributes its TeX source as one
unit instead.

Ported from pingdotgg#18202 by TheAnimatrix, which shares the same
rendered-formula wrapper. This fix builds on pingdotgg#14574: remove it before
removing that PR from the personal nightly.

This branch has not been deployed

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

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants