Repository navigation
feat(plugins): bundle Math equations so LaTeX typesets by default - #3384
LordMelkor wants to merge 4 commits into
Conversation
|
Instructions to get the build to run: |
|
Still working through the community contribution model for UX changes so can't provide a review timeline as of now. Additional context can be found here: |
joahg
left a comment
There was a problem hiding this comment.
🤖 This account isn't approving UI changes, and this PR changes rendering and bundled plugins (new default-on bundled plugin under src/bundled/), so it needs a design or core-team reviewer. Findings below, none blocking on security.
Checked:
- KaTeX is an npm dependency (katex@0.16.47, MIT, lockfile integrity matches the registry), not vendored. The only local change is the small, documented patch in patches/katex@0.16.47.patch.
- XSS: no P0 found. KaTeX runs with
trust: false, renders into a detached node and the plugin clones that node. There is no innerHTML or dangerouslySetInnerHTML in the plugin source.\href,\url,\includegraphicsand\html*are refused before parsing, sender-defined macros are refused, and size and expansion are capped. Error text is rendered as React text. - Currency like "$5 and $10" stays text (a closing
$must not follow whitespace or precede a digit). - No edited file carries a FOUNDATION marker.
Findings:
- P2 startup cost:
src/bundled/index.tsimports the plugin statically, so every launch now loads KaTeX (~270 KB min), mhchem (~50 KB), its CSS (32 KB) and all 20 woff2 fonts inlined as base64 (~260 KB raw, ~350 KB encoded), and installs 20 FontFaces at activation, whether or not any message has math. Mermaid and Charts load their libraries with dynamicimport()on first use. Lazy-loading render and fonts on the first matching message would keep default-on cheap. A maintainer should decide whether to accept this cost or require it first. - P3
$false positives: a shell-variable path such as$HOME/$USERin prose pairs the two$and typesetsHOME/. Prices are fine. - P3 design guard:
check-app-foundations.mjsgets a new exception list for Math's em sizes. AGENTS.md says not to broaden guard exceptions without sign-off, so a design-system owner should approve it. - P3 provenance and defaults: NOTICE.md records adapted sources (for example the Charts plugin), but the Math plugin's source origin and authorship have no entry. The ID prefix
com.squareupis kept for upgrade replacement, and the plugin turns on for existing users on upgrade. Maintainers should confirm both. - Only Semgrep, zizmor and DCO have reported at this head. The JS, browser and Rust jobs haven't, so the test claims are the author's local runs.
Not reviewed in depth: the test files and the host-segments corpus, ui/composer.tsx, ui/expand.tsx, ui/completion.tsx, engine/tool.ts, engine/commands.ts, references and spoilers, and the rest of detect.ts beyond the $ pairing rules. I did not build or run the app.
| if ( | ||
| source[j + 1] !== "$" && | ||
| source[j - 1] !== "$" && | ||
| (!/\s/.test(source.charAt(j - 1)) || |
There was a problem hiding this comment.
🤖 $HOME/$USER passes this closer rule (the second $ follows / and precedes a letter), so prose with shell variables typesets HOME/ as math. Worth a test, and a stricter closer rule if you want to avoid it.
| // Math sizes KaTeX against the inherited text, not the type scale: TeX's x-height | ||
| // needs 1.15em to match Inter's, and equation tags keep TeX's 2em quad. Each | ||
| // exact declaration is listed; any other size or spacing there is still checked. | ||
| const texGeometry = new Map([ |
There was a problem hiding this comment.
🤖 This adds a per-file exception to the size and spacing guard. A design-system owner should sign off on widening the guard, per AGENTS.md.
Bundle the Math equations plugin (com.squareup.math, KaTeX) in src/bundled/math the way Mermaid is bundled, on by default, so LaTeX in messages typesets for everyone. Behavior is unchanged from the Marketplace build. Copy goes through the app's writeClipboardText instead of a manifest pbcopy command. The ID is kept and listed in FORMERLY_INSTALLED, so the bundled copy replaces an installed Marketplace copy and a disabled install keeps it off. KaTeX 0.16.47 comes in with the size-cap patch. Host-fidelity tests now mount the live MessageMarkdown instead of recorded segments. Co-authored-by: Clod <c7b21872e9ddb79ca62967d0e21e6a4518a669eb04648e963cdfb2426e62fafb@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: LordMelkor <kray@squareup.com>
Drop Math's Marketplace-only discovery tags, which no other bundled plugin shares (Mermaid and Charts carry none). Use the space-half token without a literal fallback, and keep the font-face regex from reading as CSS. List Math's TeX-relative KaTeX scales and its 2em tag quad as exact exceptions in check-app-foundations. Co-authored-by: Clod <c7b21872e9ddb79ca62967d0e21e6a4518a669eb04648e963cdfb2426e62fafb@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: LordMelkor <kray@squareup.com>
Bundled plugins are imported statically by src/bundled/index.ts, so the bundled Math plugin put KaTeX, mhchem, its stylesheet and its 20 inlined woff2 fonts into the main bundle, loaded on every launch, even with Math disabled. Review flagged this startup cost. Math's entry now holds only its id and dependencies; apply() imports the plugin module and registers its contributions once that chunk loads. Cordis waits for an apply() that returns a promise, so the plugin becomes active then, and a disable during the load registers nothing. Mermaid and Charts already keep their libraries out of startup with dynamic import(). Co-authored-by: Clod <c7b21872e9ddb79ca62967d0e21e6a4518a669eb04648e963cdfb2426e62fafb@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: LordMelkor <kray@squareup.com>
The bundled Math plugin is adapted from Block's Math equations Marketplace plugin and embeds KaTeX, its fonts, stylesheet and mhchem extension. List them with their licenses, as NOTICE does for the other adapted plugins. Co-authored-by: Clod <c7b21872e9ddb79ca62967d0e21e6a4518a669eb04648e963cdfb2426e62fafb@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: LordMelkor <kray@squareup.com>
8570aef to
1e3dda8
Compare
|
🤖 Thanks @joahg. Updated at
|
joahg
left a comment
There was a problem hiding this comment.
🤖 Thanks for the update. I re-checked at 1e3dda81:
- Startup cost: confirmed.
math/index.tsnow only carries the ID and inject list, andapply()dynamic-imports./plugin, so KaTeX, mhchem, the CSS and the fonts are out of the startup bundle. One nuance for the maintainers: because the plugin is default-on, the chunk still loads on every launch, just after startup instead of on the first message with math. That's the same trade-off Mermaid and Charts make, so I'm fine with it. Deferring the import until a message actually contains math would be a possible follow-up. - Provenance: confirmed. The
NOTICE.mdentry covers KaTeX (MIT, including the patch, fonts and stylesheet) and mhchem (Apache 2.0). $HOME/$USER: agreed, that's the engine's existing rule and fine to keep as a known limitation here.- Design-guard exception and defaults: agreed, those are for a design-system owner and a maintainer.
No new findings. I'm still leaving this as a comment rather than an approval: it adds a default-on bundled plugin that changes how messages render, so it needs a design or core-team reviewer. CI at this head has again only reported Semgrep, zizmor and DCO, so the JS and browser jobs still need to run before merge.
Problem
LaTeX in a message typesets only for people who installed the Math equations Marketplace plugin. Everyone else sees raw
$…$, so a sender can't count on readers seeing the equation. Mermaid diagrams are bundled for the same reason, but Math isn't.Change
src/bundled/math/: the Math equations plugin, bundled the way Mermaid is and on by default. Its behavior is unchanged:$…$and\(…\)throughregisterInline. Display$$…$$,\[…\]and bare environments.math,latex,texandkatexfences, with an Expand dialog (Copy LaTeX, Copy MathML).math_checktool.writeClipboardText(native on macOS). The Marketplace build fell back to a manifestpbcopycommand; the bundled manifest declares no host commands.com.squareup.mathis kept and added toFORMERLY_INSTALLED, the path Claude and Codex took. The bundled copy replaces an installed Marketplace copy, so nobody gets equations drawn twice, and a disabled install keeps the bundled copy off.enabledByDefaultandenabled_by_default. This is a newly introduced bundled plugin, so perdocs/plugin-defaults.mdit uses its declared default when first encountered.katex@0.16.47, plus three markdown packages already in the lockfile throughremark-gfm:mdast-util-gfm,micromark-extension-gfmandmicromark-util-decode-string.patches/katex@0.16.47.patch, which clamps negative sizes to the same 20em cap as positive ones. It's documented inpatches/README.md.check-icons.mjs.check-app-foundations.mjsgets an exact-declaration exception for Math's KaTeX sizes: 1.15em inline, matching TeX's x-height to Inter's, plus larger previews and the 2em tag quad. These scale against the inherited text, not the type scale. Any other size or spacing in that file is still checked (mutation-tested).src/bundled/math/index.tsholds only the plugin's ID and dependencies. Itsapply()importsplugin.tsxwhen the plugin activates. That chunk is about 733 KB and holds KaTeX, mhchem, the KaTeX stylesheet and the 20 embedded fonts. Mermaid and Charts already keep their libraries out of startup with a dynamicimport(). Cordis waits for anapply()that returns a promise, so the plugin becomes active once the chunk has loaded. If someone turns it off during the load, nothing is registered.docs/plugin-architecture.mddescribes the plugin and lists it among the on-by-default bundles.Safety
TeX from messages is untrusted.
trust: false, so it creates no links and no images.\def,\newcommand,\letand the rest) are refused before parsing.FontFacebytes, so they need nofont-srcchange.Tests
// @vitest-environment jsdomheaders, andvitest.config.tsnow lets KaTeX's CSS load as text instead of being stubbed.MessageMarkdowncuts a message into inline segments. The Marketplace build checked that replica against segments recorded from an older release.engine/host-segments.test.tsxmounts the realMessageMarkdownfor each message in the corpus and compares against what it actually offers. A host change that breaks math now fails here.ui/host-copy.test.tsxmounts real messages and checks the host's copy serializer keeps each equation's LaTeX.formerly_installed_plugins_upgrade_to_the_bundled_copynow coverscom.squareup.math, enabled and disabled. The existing parity test checks that the app's default and Rust's default agree.Intent
Original request: "bundle the Math equations plugin (
com.squareup.math, KaTeX) with Buzz, the way Mermaid is bundled, so LaTeX in messages typesets by default."Validation
Update (2026-11-15), head
1e3dda81, rebased on main2ccfbb34:vite build, KaTeX, its fonts and mhchem appear only inplugin-*.js(733,466 bytes). That chunk loads only through a dynamicimport()inmain, andindex.htmlneither links nor preloads it. None of the 7 startup files contains KaTeX, itsKaTeX_*font names, a KaTeX woff2 data URI or mhchem.tsc --noEmit -p ., the design tsconfig anddesign:check: clean.apply()and checks that nothing registers until the plugin chunk loads.vitest relatedon the 51 changed source files: 1,507 passed in 28 files.cargo test -p buzzodz-plugins --locked: 153 passed.fiber.tswaits on the in-flightapply()before unloading. After that, anyctx.effector registration fails its active check, so nothing stays registered. A runtime test on a held load showed no effect and no registration, and turning it off then on again activated it once.At head
8570aef2, against main1b02fe93(the full suite and both reviews ran on the first commit,79bc9af0):tsc --noEmit -p .: clean.biome check --error-on-warningson every changed file: clean (18 infos,noUselessStringRaw, left as ported). The pre-commit checks (staged names, icon allowlist, rustfmt) pass.vitest run src/bundled/math: 1,377 tests in 14 files, all passing in the adversarial reviewer's run. One wall-clock check,maps many LF-only segments without quadratic line-start searches, has a 2 s budget and ran 2.3–2.9 s when the machine's load average was 35–47. The app's other bundles use the same kind of budget.vitest run --maxWorkers=3at79bc9af0: 22,632 passed and 21 failed, in 1,104 files (17.5 minutes).8570aef2.ChannelLifecycleDialog("keeps a paused delete's confirmation…") fails the same way on main1b02fe93.MessageReactions(19 tests) cascaded from one 5 s timeout at load 30+, and passes 25/25 rerun alone.vitest relatedon the changed files, 269 passed;design:checkand the design tsconfig, clean.--clippylane (cargo clippy --workspace --all-targets) was skipped: it builds the whole Tauri app on an overloaded machine. The crate-level clippy below covers the only Rust file changed, and CI's native lane runs the workspace.cargo test -p buzzodz-plugins --locked: 153 passed.cargo clippy -p buzzodz-plugins --all-targets -- -D warnings: clean.sq agents review: four P2s at79bc9af0, and four at8570aef2(two of them repeats). All are in engine or UI code carried over unchanged from the Marketplace build, and none is in the fix commit. They are listed under Known limitations, since this PR doesn't change engine behavior.Known limitations (carried over from the Marketplace build)
\frac{}{} $) can stop a closing$from pairing, so that line stays as text.$HOME/$USERin prose pairs its two$signs and typesetsHOME/. Prices such as "$5 and $10" stay text.math_checkdoesn't say when its span limit left later equations unchecked.Try it (AGENTS.md step 3, for a human)
Euler: $e^{i\pi}+1=0$, and lunch cost $5 and $10.Expected: the equation is typeset and the prices stay text.mathholding\int_0^1 x^2\,dx = \tfrac13, then choose Expand equation → Copy LaTeX and paste. Expected: the LaTeX source.🤖 Generated with Claude Code