Skip to content

feat(app): render LaTeX math in markdown (KaTeX) - #1494

Open
chphch wants to merge 1 commit into
slopus:mainfrom
chphch:feat/latex-equation-rendering
Open

chphch wants to merge 1 commit into
slopus:mainfrom
chphch:feat/latex-equation-rendering

Conversation

@chphch

@chphch chphch commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Implements #1629.

Renders LaTeX math in the markdown view using KaTeX — display equations ($$…$$, \[…\]) on their own line, and inline math ($…$, \(…\)) within prose, lists, and headers. It follows the existing Mermaid special-block pattern: KaTeX runs inside a react-native-webview on native and via a lazy import('katex') on web, so a message full of equations renders as math instead of raw dollar-sign source. Currency ($5), escaped \$, and $ x $ (space after the opener) stay plain text so ordinary prose isn't mangled.

How it works

  • Parser (parseMarkdownBlock.ts, parseMarkdownSpans.ts): a new math block type for display equations, and inline math split out by a single unified tokenizer so a $ inside a `code` span, a [markdown](link), or a bare URL stays literal, and TeX bodies (which contain *, _, \) aren't re-parsed as markdown. Unclosed/partial $$ (mid-stream) and trailing-text-after-close fall through to normal text handling.
  • Renderer (MathView.tsx / MathView.web.tsx): MathBlock for display equations. On web, inline math is a per-span element that flows inside the normal <Text>, so native text selection is preserved. RN <Text> can't hold a WebView, so on native a text run containing inline math is rendered as one KaTeX WebView; non-math runs keep the native <Text> path.

Proof

BEFORE → AFTER on the dev/messages-demo screen (a demo agent message containing inline + display LaTeX), captured on Expo web via a throwaway standalone happy-server (PGlite) + Playwright.

latex before/after

BEFORE, the LaTeX shows as raw source ($\mathcal{L} = -\sum_i y_i \log \hat{y}_i$, $$ \int … $$); AFTER, inline math flows inside the prose and list items and display equations render centered on their own line. The capture also asserts Prices like $5 and $10 stays literal text (the currency guard), and that the pre-existing markdown table still renders unchanged.

Verification

pnpm typecheck clean. Parser behaviour is pinned by unit tests in parseMarkdownMath.test.ts covering block detection, inline splitting, and the currency / escaped-\$ / inline-code / link / bare-URL guards.

@chphch
chphch force-pushed the feat/latex-equation-rendering branch 4 times, most recently from e5d5632 to 784fa52 Compare August 28, 2026 00:21
@chphch
chphch force-pushed the feat/latex-equation-rendering branch from 784fa52 to cc5d9b7 Compare October 17, 2026 03:57
Renders display equations (`$$…$$`, `\[…\]`) on their own line and inline
math (`$…$`, `\(…\)`) within prose, lists, and headers. Mirrors the existing
Mermaid special-block pattern: KaTeX runs inside a react-native-webview on
native and via a lazy `import('katex')` on web.

- Parser: a `math` block type for display equations, and inline math split out
  by a single unified tokenizer so `$…$` inside code spans, markdown links, and
  bare URLs stays literal, and TeX bodies aren't re-parsed as markdown.
- Currency (`$5`), escaped `\$`, and `$ x $` (space after opener) stay as plain
  text. Unclosed/partial `$$` (mid-stream) and trailing-text-after-close fall
  through to normal text handling.
- Renderer: on web inline math is a per-span element that flows inside the
  native <Text> (selection preserved). RN <Text> can't hold a WebView, so on
  native a run containing inline math is rendered as one KaTeX WebView; non-math
  runs keep the native <Text> path.
- Hardening: WebView script-context injection escaped, per-element render (no
  delimiter text-scan), http(s)-only link gating, and length-capped scanners to
  avoid a quadratic blow-up on pathological single-line input.

Unit tests cover block detection, inline splitting, and the
currency/escape/code/link/URL guards.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@chphch
chphch force-pushed the feat/latex-equation-rendering branch from cc5d9b7 to 4ecd630 Compare October 19, 2026 01:27

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant