Skip to content

Add LaTeX math support to Markdown live preview - #496

Merged
jsgrrchg merged 16 commits into
mainfrom
zeron/latex-markdown-preview-feasibility
Oct 9, 2026
Merged

jsgrrchg merged 16 commits into
mainfrom
zeron/latex-markdown-preview-feasibility

Conversation

@jsgrrchg

@jsgrrchg jsgrrchg commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Markdown live preview now renders inline $…$ and display $$…$$ formulas using the existing KaTeX dependency. Click a formula or move the caret into it to edit its LaTeX source; leaving the formula restores its preview.

The shared delimiter parser keeps common dollar prices, escaped dollars, code, frontmatter, and link destinations literal. Single-line and multiline display formulas use display layout, wide formulas scroll horizontally, and invalid formulas remain readable and editable. The Markdown source is preserved through edits, undo/redo, tab switches, and source/preview mode changes.

Screenshots

image

Validation

  • 200 unit tests passed across the editor extensions after rebasing onto main.
  • All 7 math browser tests passed in Chromium after the rebase, including click-to-edit, arrow navigation, undo/redo, mode and tab switching, light/dark layouts, and a note with 600 formulas.
  • The 22-test math, list, and tab-rendering browser regression run passed before the rebase.
  • Production build, application/E2E TypeScript checks, and ESLint on changed files passed.
  • Added the math browser suite to CI and documented syntax and limitations in docs/markdown-math.md.

Pre-merge audit fixes

An audit before merging found regressions against main, now fixed in separate commits:

  • Markdown styling around formulas (3568c530): a paragraph, list item, quote or whole note starting with a formula lost all node-based styling (bold, headings, links). Only nodes that cut through a formula are skipped now.
  • Inline tokens containing a formula (291d1081): highlights, <kbd>, footnote references and inline HTML vanished when they contained a whole formula.
  • Wikilinks (78612a30): [[Note $x$]] rendered part of the note name as math; wikilinks and embeds are now excluded from math parsing.
  • Baseline alignment (65b5cbe1): inline formulas and errors sat 9–24 px above the text baseline (inline-block scroll container). Switched to an inline flex container and absorbed KaTeX's -2px script margin, which also removes stray 15 px scrollbars under short formulas such as y_1 on platforms with classic scrollbars.
  • Formula scrollbars (06ea7041): pressing a wide formula's scrollbar revealed its source instead of scrolling it.
  • Reparse performance (af38b4f0): the math range field reparses per keystroke; it now exits early without dollars, walks the syntax tree only across the dollar span and jumps between dollars. In a 700 KB note: ~12 → ~1 ms without formulas, ~15 → ~10 ms with prices, ~18 → ~14 ms with a formula per line. A 60,000-document differential fuzz matched the previous parser exactly.
  • Docs (969abb17): documented where multiline display math stays literal (quotes/callouts, after list markers, 4+ space indentation), the wikilink exclusion and scrollbar behavior.

Each fix ships with regression tests that fail without it: unit tests for styling, tokens, wikilinks and scrollbar handling, plus browser tests for baseline alignment, stray scrollbars (with real scrollbars enabled) and scrollbar presses.

After the fixes: 594 editor unit tests, the 25-test math/list/tab-rendering browser suite, tsc, ESLint and the production build pass.

Known leftovers: a formula filling a token exactly (==$x$==) renders without the token's style (CodeMirror mark/widget behavior), and math-dense notes still reparse in O(n).

Scope

Supports KaTeX-compatible mathematics, not full LaTeX documents or arbitrary packages. Math inside tables and the alternative \(…\) / \[…\] delimiters remain outside this initial implementation.

@jsgrrchg
jsgrrchg marked this pull request as ready for review September 28, 2026 19:07
Node rules skipped every syntax node whose start fell inside a formula, so a
paragraph, list item or whole note beginning with $x$ lost all its Markdown
styling. Only skip nodes that cut through a formula, and look formulas up in
the viewport with a binary search.
Every formula was registered as a block range, so highlights, footnote
references and inline HTML that merely contained one stopped rendering.
Only skip tokens that start or end inside a formula.
Note names may contain dollars, e.g. [[Cost $5 and $x$]]. Treat wikilinks
and embeds like link destinations so they keep resolving as links instead of
rendering part of the target as a formula.
An inline-block scroll container aligns its bottom edge with the baseline,
which raised every inline formula and error above the surrounding text. Use
an inline flex container instead, and pad the KaTeX box so the -2px margin of
scripts and roots no longer overflows and shows a scrollbar under short
formulas on platforms with classic scrollbars.
Math widgets handled every mousedown, so pressing a wide formula's horizontal
scrollbar revealed its source instead of scrolling. Let presses below the
client area of an overflowing formula reach the native scrollbar.
The math range field reparses on every keystroke. Return early for notes
without dollars, walk the syntax tree only across the span that holds them,
and jump between dollars instead of visiting every character.

In a 700 KB note this cuts reparsing from ~12 ms to ~1 ms without formulas
and from ~18 ms to ~14 ms when every line has one. A differential fuzz run
over 60,000 generated documents matched the previous parser exactly.
Describe where multiline display math stays literal, the wikilink exclusion,
scrollbar behavior and how formulas interact with surrounding Markdown.
Type the elements measured by the new math browser tests so the e2e harness
type check passes in CI. Wait for background parsing to finish before jumping
to the end of the long-note test: display blocks that appeared after the jump
grew the note under the scroll target and failed about one run in four.
@jsgrrchg
jsgrrchg merged commit 35d0818 into main Oct 9, 2026
9 checks passed
@jsgrrchg
jsgrrchg deleted the zeron/latex-markdown-preview-feasibility branch October 9, 2026 01:03
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