feat: render LaTeX formulas as MathJax SVG - #47
Conversation
|
Someone is attempting to deploy a commit to the Faberon Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
🟡 Changes recommended
The new math rendering and outline injection introduce confirmed error-handling and PDF-structure preservation issues that can cause crashes or silently degrade output.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a strict TeX-to-SVG math rendering path (MathJax SVG) to Kami’s HTML→PDF pipeline, plus a post-render outline/bookmark helper, and propagates the new workflow through docs, tests, packaging, and generated mirrors.
Changes:
- Introduces
ensure_mathjax.sh+ a Node bootstrap (mathjax_svg.js) and a Python transformer/checker (math_render.py) to render LaTeX placeholders into self-contained MathJax SVG. - Updates
render_pdfto perform strict math rendering in-memory before code highlighting and PDF generation. - Adds
add_outline.pyto preserve renderer outlines or inject a chapter-anchor fallback when outlines are absent; syncs tests, packaging allowlists, docs, plugin mirror, and.well-knowndigests.
File summaries
| File | Description |
|---|---|
| SKILL.md | Documents strict math + outline workflow and related “done” checklist items. |
| CHEATSHEET.md | Adds strict math + outline guidance to the quick reference. |
| references/production.md | Adds production guidance for MathJax SVG math rendering. |
| scripts/render.py | Adds strict math rendering step into the canonical PDF pipeline. |
| scripts/math_render.py | New: placeholder-based TeX→MathJax SVG renderer + strict checker + CLI. |
| scripts/mathjax_svg.js | New: Node JSON-in/JSON-out MathJax SVG renderer (no node_modules in package). |
| scripts/ensure_mathjax.sh | New: installs MathJax into a user-writable cache for packaged runs. |
| scripts/add_outline.py | New: post-render outline preservation/fallback injection tool. |
| scripts/tests/test_build.py | Adds coverage for strict math placeholder checking; updates site-facts file list. |
| scripts/package-skill.sh | Packages new scripts and expands allowlist to include .js. |
| .well-known/agent-skills/index.json | Updates SKILL digest to reflect SKILL.md changes. |
| plugins/kami/skills/kami/SKILL.md | Generated mirror sync of SKILL.md updates. |
| plugins/kami/skills/kami/CHEATSHEET.md | Generated mirror sync of cheatsheet updates. |
| plugins/kami/skills/kami/references/production.md | Generated mirror sync of production docs updates. |
| plugins/kami/skills/kami/scripts/render.py | Generated mirror sync of render pipeline updates. |
| plugins/kami/skills/kami/scripts/math_render.py | Generated mirror adds strict math renderer/checker. |
| plugins/kami/skills/kami/scripts/mathjax_svg.js | Generated mirror adds Node MathJax renderer. |
| plugins/kami/skills/kami/scripts/ensure_mathjax.sh | Generated mirror adds MathJax installer. |
| plugins/kami/skills/kami/scripts/add_outline.py | Generated mirror adds outline helper. |
| plugins/kami/skills/kami/scripts/tests/test_build.py | Generated mirror sync of tests updates. |
| plugins/kami/skills/kami/scripts/package-skill.sh | Generated mirror sync of packaging allowlist updates. |
Review details
- Files reviewed: 21/22 changed files
- Comments generated: 9
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| html_text = src.read_text(encoding="utf-8") | ||
| html_text = render_latex_in_html(html_text) | ||
| html_text = highlight_code_blocks(html_text) |
| # Raw display delimiters are forbidden in a completed HTML delivery. | ||
| if re.search(r"\\\[|\\\]", raw): | ||
| issues.append("raw display TeX delimiter \\[...\\] remains") | ||
| if re.search(r'class="latex-(?:inline|display)"', raw): | ||
| issues.append("LaTeX placeholder class remains") | ||
| return issues |
| writer = PdfWriter() | ||
| writer.append(reader) | ||
| count = 0 | ||
| for anchor, title in parser.chapters: | ||
| if anchor in anchor_page: | ||
| writer.add_outline_item(title, anchor_page[anchor]) | ||
| count += 1 |
|
|
||
| Code blocks with `class="language-*"` are highlighted only when optional `Pygments` is installed in the build environment. Without it, PDFs still render and code blocks stay monochrome. | ||
|
|
||
| **Mathematics is strict LaTeX, never pseudo-math.** When a document contains mathematics, author formulas only as standard LaTeX delimiters: inline `\( ... \)` and display `\[ ... \]`. Do not replace formulas with Unicode approximations, ASCII fractions, screenshots, or raw TeX printed on the page. Before shipping HTML/PDF, run `bash scripts/ensure_mathjax.sh`, then `python3 scripts/math_render.py --in-place filled.html`; this converts every formula into MathJax SVG. `render_pdf` also performs this conversion in memory as a hard fallback and fails if MathJax or the TeX is invalid. Finish with `python3 scripts/math_render.py --check filled.html`; a completed HTML must contain no raw TeX delimiters or unrendered LaTeX placeholders. |
|
|
||
| ### Strict LaTeX mathematics | ||
|
|
||
| WeasyPrint has no JavaScript runtime, so MathJax/KaTeX script tags do **not** render mathematics. Kami therefore uses a local MathJax SVG path. Author source formulas strictly as inline `\( ... \)` or display `\[ ... \]`; do not ship Unicode approximations, raw TeX, or screenshot formulas. |
|
|
||
| For multi-page documents (long-doc / portfolio / equity-report / changelog), run `python3 scripts/add_outline.py filled.html out.pdf` after rendering. It preserves a renderer-generated H1/H2/H3 PDF outline when present and injects a chapter-anchor fallback when absent. The printed TOC page remains required; the PDF outline is its sidebar navigation twin. | ||
|
|
||
| **Strict mathematics**: author formulas only as standard LaTeX `\( inline \)` or `\[ display \]`. Before delivery run `bash scripts/ensure_mathjax.sh`, `python3 scripts/math_render.py --in-place filled.html`, then `python3 scripts/math_render.py --check filled.html`. The accepted HTML/PDF result is MathJax SVG, never Unicode pseudo-formulas, raw TeX, or formula screenshots. |
| writer = PdfWriter() | ||
| writer.append(reader) | ||
| count = 0 | ||
| for anchor, title in parser.chapters: | ||
| if anchor in anchor_page: | ||
| writer.add_outline_item(title, anchor_page[anchor]) | ||
| count += 1 |
| # Raw display delimiters are forbidden in a completed HTML delivery. | ||
| if re.search(r"\\\[|\\\]", raw): | ||
| issues.append("raw display TeX delimiter \\[...\\] remains") | ||
| if re.search(r'class="latex-(?:inline|display)"', raw): | ||
| issues.append("LaTeX placeholder class remains") |
| out.parent.mkdir(parents=True, exist_ok=True) | ||
| html_text = highlight_code_blocks(src.read_text(encoding="utf-8")) | ||
| html_text = src.read_text(encoding="utf-8") | ||
| html_text = render_latex_in_html(html_text) | ||
| html_text = highlight_code_blocks(html_text) |
Fail closed on malformed TeX, HTML, and SVG while bounding renderer resources. Lock MathJax installation behavior and keep generated package mirrors in sync.
Summary
Validation
python3 scripts/tests/test_build.py(335 passed)python3 scripts/build_metadata.py --checkpython3 scripts/build.py --checkpython3 scripts/build.py --verifyrender_pdf