Skip to content

Equation numbering as a format-specific post-filter stage (bd-vlhi2zkj) - #708

Open
cscheid wants to merge 1 commit into
mainfrom
feature/bd-vlhi2zkj-equation-numbering
Open

cscheid wants to merge 1 commit into
mainfrom
feature/bd-vlhi2zkj-equation-numbering

Conversation

@cscheid

@cscheid cscheid commented Sep 22, 2026

Copy link
Copy Markdown
Member

Phase 1 of claude-notes/plans/2026-09-21-equation-numbering-and-mathml.md (strand bd-vlhi2zkj). Independent of quarto-math; lands on main first so the stacked MathML PRs on #706 can build on it.

Why

CrossrefRenderTransform appended \tag{N} to every numbered equation's TeX. \tag is an amsmath command only MathJax and KaTeX read: Quarto 1 emits it solely for those two engines and writes \qquad(N) otherwise, and any converter that reads math alone (Pandoc's, quarto-math's for the coming html-math-method: mathml) rejects the whole expression. The encoding of the number is a presentation decision, so it leaves the format-agnostic transform.

What changes

  • Crossref-render leaves the Math text byte-identical and records the number on the reserved span attribute quarto-eq-number (EQ_NUMBER_ATTR in crossref/mod.rs).
  • New EquationNumberStage, placed after UserFiltersStage::post and before CodeHighlightStage, encodes the number from the format and html-math-method: \tag{N} for MathJax/KaTeX, \qquad(N) for plain/unknown, a sibling span.quarto-eq-number outside the math (plus a quarto-eq-sibling-number modifier class) for mathml, nothing for non-HTML formats. It removes the attribute in every case. Running after the post filters is the point: a Lua filter can read, rewrite or delete el.attributes["quarto-eq-number"] — documented in the Lua filters guide as the supported way to customize equation numbers.
  • math_method.rs parses html-math-method once (string and object forms); MathEngine::from_meta in math-js maps over it so the two stages cannot drift.
  • equation-number.scss, a shared SCSS layer loaded at every HTML compile site and in assemble_reveal_scss (like copy-code.scss), lays the sibling label out at the right edge of the equation row.
  • The pipeline design doc gains a "post-filter presentation slot" section; Equation.tsx's comment points at the stage (the preview keeps its own KaTeX \tag append).

Tests

Written first. Crossref-render unit tests assert the attribute instead of the tag; 13 stage unit tests (encodings, selection, walker); 13 end-to-end tests in equation_numbering_pipeline.rs covering every encoding, revealjs, KaTeX, the object form, unlabelled math, sequential numbering, and Lua post filters rewriting/deleting the number plus a pin that pre filters never see it; quarto-sass asserts the layer in the HTML and revealjs CSS.

Collateral: the HTML stage-list assertions (25 → 26 stages) and the styles.css byte-identity baseline (tests/fixtures/phase5-single-doc-baseline/expected_hashes.txt, re-captured with a dated note; the layer is additive, doc.html unchanged).

Verification

Full cargo xtask verify green. End-to-end through cargo run --bin q2 -- render (recorded in the plan): default → \tag{1} inside the math with MathJax loaded; plain \qquad(1); mathml<span class="quarto-eq-number">(1)</span> after the math and no engine loaded.

🤖 Generated with Claude Code

`CrossrefRenderTransform` appended `\tag{N}` to every numbered
equation's TeX. `\tag` is an amsmath command only MathJax and KaTeX
read: Quarto 1 emits it solely for those two engines and uses
`\qquad(N)` otherwise, and a math-only converter (Pandoc's, or
quarto-math's for the coming `html-math-method: mathml`) rejects the
whole expression. The encoding is a presentation decision, so it moves
out of the format-agnostic transform.

- crossref-render now leaves the `Math` text byte-identical and records
  the number on the reserved span attribute `quarto-eq-number`
  (`EQ_NUMBER_ATTR` in `crossref/mod.rs`).
- New `EquationNumberStage` (after `UserFiltersStage::post`, before
  `CodeHighlightStage`) picks the encoding from the format and
  `html-math-method`: `\tag{N}` for MathJax/KaTeX, ` \qquad(N)` for
  `plain`/unknown, a sibling `span.quarto-eq-number` outside the math
  (plus a `quarto-eq-sibling-number` modifier class) for `mathml`,
  nothing for non-HTML formats. It removes the attribute in every case.
  Running after post filters is the point: a Lua filter can read,
  rewrite or delete `el.attributes["quarto-eq-number"]`.
- New `math_method.rs` parses `html-math-method` once (string and
  object forms); `MathEngine::from_meta` in math-js maps over it so the
  two stages cannot drift.
- New shared SCSS layer `equation-number.scss` (loaded at every HTML
  compile site and in `assemble_reveal_scss`, like `copy-code.scss`)
  lays the sibling label out at the right edge of the equation row.
- Design doc gains a "post-filter presentation slot" section; the Lua
  filters guide documents the attribute; `Equation.tsx`'s comment now
  points at the stage.

Tests (written first): crossref-render unit tests assert the attribute
instead of the tag; 13 stage unit tests (encodings, selection, walker);
13 end-to-end tests in `equation_numbering_pipeline.rs` covering every
encoding, revealjs, KaTeX, the object form, unlabelled math, sequential
numbering, and Lua post filters rewriting/deleting the number plus the
pin that pre filters never see it; quarto-sass asserts the layer in the
HTML and revealjs CSS.

Collateral test updates: HTML stage-list assertions (25 → 26 stages);
the `styles.css` byte-identity baseline in
`tests/fixtures/phase5-single-doc-baseline/expected_hashes.txt` is
re-captured with a dated note (additive layer; doc.html unchanged).

End-to-end (real binary, output inspected, recorded in the plan):
default → `\tag{1}` inside the math with MathJax loaded; `plain` →
` \qquad(1)`; `mathml` → `<span class="quarto-eq-number">(1)</span>`
after the math, no engine loaded; the compiled stylesheet carries the
three `.quarto-eq-sibling-number` rules.

Plan: claude-notes/plans/2026-09-21-equation-numbering-and-mathml.md

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
cscheid added a commit that referenced this pull request Sep 22, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@posit-snyk-bot

posit-snyk-bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

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.

2 participants