Skip to content

Typeset the TeX the dependency explorer draws - #455

Merged
Deicyde merged 9 commits into
mainfrom
fix/explorer-math
Oct 24, 2026
Merged

Deicyde merged 9 commits into
mainfrom
fix/explorer-math

Conversation

@Deicyde

@Deicyde Deicyde commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Draft, based on main, which now has #284 and #217 and so the MathJax test setup its tests use (the Node suite and the checked MathJax 3.2.2 tarball). Jack decided the behavior on 2026-10-22: the explorer's titles and summaries are typeset, with plain $ delimiters. The lead agreed to it opening; the open agent PRs are over the cap of 8 only through Jack's per-PR overrides, and this one counts as one on his decision.

The dependency explorer's TeX is never typeset on a real site. Its rail and details show $\mathbb{Z}/n$ as typed, on main.

Why

#90 (4f14924, merged 2026-10-21) made the explorer call MathJax.typesetClear and MathJax.typesetPromise on its rail and its details after each render. That reads MathJax's startup document. #215 (35a81f0, merged 2026-10-20) made the site's generated javascripts/mathjax.js give that document a TeX input with no delimiters, and typeset only the formulas the site's Markdown marks with its formula class, through passes of its own. So the explorer's calls resolve and find nothing. 4f14924 was written against the earlier vault template, whose startup typeset the page; the browser test replaced MathJax with a stub that recorded the calls, so it could not see that nothing was typeset.

Reproduced twice before the fix: in Node with MathJax 3.2.2 on the generated script (rail 0, details 0 typeset formulas, where 4 and 7 were expected), and on sites built from main (before and after #284) loaded in jsdom with the page's own scripts in order (Material, javascripts/mathjax.js, MathJax's browser bundle served from the npm package, the explorer). Both typeset the explorer when given the old template, so the harnesses detect typesetting.

What changes

  • dag_viewer.py. The explorer marks the authored titles and summaries it draws in its rail and details with the site's formula class (markdown.FORMULA_CLASS, substituted into the script): the rail's trail, title, summary, group headings and rows, and the details' kicker, title, summary, relation rows and distribution labels, and the phone sheet's handle while it repeats a node's title (the landing page's title it shows otherwise is printed as typed). Map cards, search results and the explorer's head stay as they were. After each render, typeset() dispatches an autoform:typeset event instead of calling MathJax, and the explorer no longer reads window.MathJax. No div is marked, since the site styles div.arithmatex. The runtime goes from 64,262 to 64,222 bytes of its 66,000. A typeset formula keeps its letters' case under the rail's group headings, which are set in capitals.
  • mathjax.py. The generated script answers autoform:typeset with its own pass: a new page input with its packages and the project's macros, the safe options, one finished document per step, in the queue of passes. The event carries nothing, so the explorer cannot choose other settings. Before MathJax has started, the event is ignored, and the first pass reads the marks. Two things a pass did once per page now depend on what it is for:
    • Only a pass for a page just shown resets MathJax's output. CHTML's style sheet holds only the glyphs drawn since its last reset, so a reset on the explorer's passes dropped the rules that draw the formulas already on the page, and a pass that found nothing blanked them all.
    • A formula inside a link or a button, or marked on one, such as a rail row, the trail, a relation row or the sheet handle, is typeset in a document without MathJax's menu and without its accessibility explorer, with the same page input. The menu would give it a tab stop, a context menu and a Space key of its own inside the control; the explorer, which a reader can turn on from any formula's menu for the whole site, would give it a tab stop ahead of the page and take its Enter key. This holds for an article formula inside a link too.
  • Docs. The formula class's comment, the module docstring and the README say the explorer's titles and summaries are the exception to titles showing their TeX as typed, and that formulas in links and buttons get no menu; the changelog gains a line.

It adds no command, flag, JSON key or schema; the one new name is the internal event autoform:typeset.

Tests

  • tests/test_site_mathjax_node.py: MathJax itself, on the script render writes. After the page's pass, explorer-like content is drawn into the page, marked and unmarked, and the explorer's event asks for a pass. Only the marked formulas are typeset, with the page's packages and the project's macros; a formula in a link gets a document of its own; every glyph on the page keeps its rule in the renderer's style sheet; MathJax's startup document stays empty; an event before MathJax starts does nothing. Removing the listener, its started guard, started = true, the link's own document, or resetting the output on the explorer's passes fails it.
  • tests/test_dag_viewer_browser.py: the explorer's typesetting test no longer stubs MathJax. It serves MathJax 3.2.2 from the checked tarball in place of the CDN, gives the payload TeX titles and summaries, and asserts typeset output in the rail's title, summary and rows, in the details' title and summary, in the phone sheet's handle, and in a relation row that Show more appends; that the rail's glyphs keep their drawing rules after the details' pass; that a formula in a rail link is no tab stop while the rail's title keeps MathJax's menu; none in map cards; and an empty startup document.
  • tests/test_graph_view_rendering.py: pins the event, the class, the three call sites, and that the script no longer reads window.MathJax.
  • CI: the WebKit job unpacks the same pinned and checksummed MathJax tarball as the MathJax job (AUTOFORM_MATHJAX_DIR, with AUTOFORM_REQUIRE_MATHJAX_TESTS=1). CONTRIBUTING.md says so.

Before pushing, the fixed site was also checked in the jsdom harness above: rail 5 and details 3 typeset formulas, 5 after a node is selected, with the explorer drawn before MathJax starts, after it, and with nothing delayed; MathJax's startup document stays empty.

Overlap with other open pull requests

  • Integrate mathematical book reading and dependency exploration #442 (Integrate mathematical book reading and dependency exploration) rewrites much of the explorer, and a merge of the two conflicts in eight files: autoform_cli/dag_viewer.py, autoform_cli/mathjax.py, autoform_cli/README.md, .github/workflows/tests.yml, CONTRIBUTING.md, tests/test_dag_viewer_browser.py, tests/test_graph_view_rendering.py and tests/test_site_mathjax_node.py. At its head the explorer typesets through a window.AutoformMath.refresh(container) hook that Integrate mathematical book reading and dependency exploration #442's mathjax.js defines, not through this PR's autoform:typeset event. The interface of whichever lands first stays, and the second moves its explorer onto it and carries the rest: the explorer's titles and summaries get the articles' input and the project's macros; a pass that adds to a page does not reset MathJax's output, which would blank the formulas already drawn; formulas in links and buttons, or marked on one, get neither MathJax's menu nor its accessibility explorer; and the Node test, the real-MathJax browser tests and the WebKit job's MathJax step come along.
  • Friendlier explorer: status-only colour, plain words, quiet links, a peeking phone reader #444 (Calmer explorer) also changes tests/test_dag_viewer_browser.py and tests/test_graph_view_rendering.py.
  • Run the WebKit job in Playwright's pinned image #456, which runs the WebKit job in Playwright's 1.62.0 container, conflicts in tests.yml: keep the container and its defaults: run: shell: bash (the MathJax step uses set -euo pipefail), drop the install step, and keep this PR's MathJax step and AUTOFORM_REQUIRE_MATHJAX_TESTS. The container has no Node, so the browser test asks for the MathJax package without it (mathjax_package(node=False)).

Review

An adversarial review of dfdaf404 (three reviewers, each finding checked by a skeptic) confirmed the reset that blanked earlier formulas and the tab stops inside controls, both fixed in 81b67e6d with the tests above, plus documentation gaps and the sheet handle typesetting the landing page's title, fixed too. Checked again in jsdom with real MathJax after the fix: no glyph on the page loses its rule, with the explorer drawn before or after MathJax starts and after a node is chosen by its link.

A second review, by jackmccarthy-77 at 20f55c61 (comment), found the menu still on formulas marked on a control itself (the relation buttons and the sheet handle) and on the trail's entries, whose class sat on the list item around the link. fba2802a starts the check at the marked element and puts the trail's class on its link; the Node test marks a trail link, the handle and a relation button, and the browser test checks those, with the rail rows, hold no tab stop.

A final pass at fba2802a (four reviewers, a skeptic per finding, and a judge) recommended fixing one thing before merge: with MathJax's accessibility explorer turned on, formulas in controls still became tab stops at tabindex 1 and took Enter. 83005c92 turns the explorer's render action off in their document, and a browser test turns the setting on and checks it. The same push decouples the browser test from Node, keeps formulas' letter case under capitalised headings, and names the Contents list in the docs. Its other findings are follow-ups, listed in the verdict comment.

jackmccarthy-77's second round, at dad0dce8, found nothing that blocks merge (comment).

Validation

a8e753f4 merges main 7677dbf. #458 cut and condensed the docs, so autoform_cli/README.md and CONTRIBUTING.md conflicted; both take main's text, with this PR's facts in its wording and within its line budgets (tests/test_doc_budgets.py). All 9 checks pass on a8e753f4, and the branch merges cleanly with main.

The explorer asked MathJax to typeset its rail and details through
MathJax.typesetPromise, which reads MathJax's startup document. The site's
javascripts/mathjax.js gives that document a TeX input that finds nothing and
typesets only the formulas the site marks, so the explorer's titles and
summaries stayed raw TeX on every site. The browser test stubbed MathJax, so
it could not see this.

The explorer now marks the authored titles and summaries in its rail and
details with the site's formula class and, after each render, dispatches an
autoform:typeset event. mathjax.js answers it with its own pass, so the
explorer's TeX gets the articles' input, packages, macros and safe options.
Before MathJax starts the event is ignored, and the first pass reads the
marks. The Node suite checks the pass against MathJax itself, and the browser
test now serves MathJax 3.2.2 from the checked tarball and looks for typeset
output.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 22, 2026
MathJax keeps a document's math in a linked list, which has no length, so the browser test read None; it reads the list through Array.from, as the Node test does.
…order

Every pass reset MathJax's output, and CHTML's style sheet holds only the
glyphs drawn since its last reset, so each pass the explorer asked for
dropped the rules that draw the formulas already on the page: a pass that
found nothing blanked them all. Only a pass for a page just shown resets the
output now; the explorer's passes add to the page.

MathJax's menu gave each formula a tab stop, a context menu and a Space key
of its own, inside the rail's links, the relation buttons and the sheet
handle. A formula inside a link or a button is now typeset in a document
without the menu, with the same page input.

The sheet handle carries the formula class only while it shows a node's
title; the landing page's title it shows otherwise is printed as typed.
The README said the explorer shows titles as typed and that the articles' input reads only the Markdown's formulas; it now names the explorer's typeset titles and summaries and the formulas in links and buttons, which get no menu. The changelog gains a line.
@Deicyde

Deicyde commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Review of #455 at 20f55c6: fix before merge. The explorer's titles and summaries are typeset with the same safe settings as articles, and its passes queue correctly. But three of the controls it marks still give their formulas MathJax's menu, which the code comment and the README say cannot happen.

Blocks merge

  1. The relation buttons, the phone sheet's handle and the trail links make their formulas tab stops inside a control. inControl (autoform_cli/mathjax.py:270-276) starts at the marked element's parent, so it only catches a marked element that sits inside a link or button. Three places put the class on the control itself, or on an element that holds the control:

    • the details' relation buttons, button.bp-dag-relation-button (autoform_cli/dag_viewer.py:1791);
    • the phone sheet's handle, which is a button (autoform_cli/dag_viewer.py:1710);
    • the rail's trail entries, where the marked li holds the link (autoform_cli/dag_viewer.py:989).

    Repro. I used the PR's own Node harness, with MathJax 3.2.2 from the checked tarball, and had the explorer draw those three shapes plus the two the rail rows use (a marked span inside a link, and inside a button).

    • The explorer's pass put t_0 (trail link), h_1 (handle) and r^2 (relation button) in the page document, the one with the menu. Only g^k and b_2, the rail rows' formulas, went to the document without it.
    • In a browser, a formula in a menu document gets tabindex="0", as the WebKit test's assertion on the rail title shows. So each of these becomes a focusable element inside a button or a link.
    • The comment above typeset says this can't happen, and so does the README's "A formula inside a link or a button is typeset without MathJax's menu, so it is not a tab stop of its own" (autoform_cli/README.md:1606-1607).

    Fix. Start the walk at the node itself (for (var up = node; ...)), and in trailList put the class on the link, not its li, when there is one. With both changes, all five formulas went to the menu-free document, and the PR's 7 Node tests still passed. Extend the WebKit test's [tabindex] assertion to a relation button, the handle and a trail link.

Note

Checked and fine

  • Settings. The explorer's pass goes through the same render as an article's. It makes a new TeX input copied from SETTINGS.page, with its packages, the project's macros and the neutralised commands, plus safeOptions copied from SETTINGS.safe. The event carries nothing, so no script can choose other settings, and the explorer still writes every title with textContent.
  • Passes. typeset() dispatches synchronously, and the pass runs from the queue after the render that asked for it, so it reads the new rail and details. "Show more" asks after appending.
    • Locally, with the checked tarball, all 7 Node tests pass. The explorer's pass finds only the explorer's marked text, formulas typeset earlier aren't found again, MathJax's startup document stays empty, and an explorer pass keeps the glyph styles the first pass drew.
    • An event before MathJax starts returns nothing.
  • What stays as typed. Map cards, search results and the explorer's head aren't marked, and the handle is marked only while a node is selected.
  • CI and merge. The WebKit job's MathJax step downloads the same URL and checks the same sha256 as the MathJax job. All 9 checks passed at 20f55c6. The branch merges cleanly with main 7a52246, and tests/test_graph_view_rendering.py passes locally (37 tests).
  • Docs. The CHANGELOG, CONTRIBUTING and markdown.py lines match the code, as does the README's explorer paragraph, except the tab-stop sentence in item 1.

Posted by jackmccarthy-77, the reviewer the lead assigned.

The check for a formula in a link or a button began at the marked element's
parent, so formulas marked on the control itself, the relation buttons and
the sheet handle, still got MathJax's menu and a tab stop, as did the trail's
entries, whose class was on the list item around the link. The check now
begins at the marked element, and the trail puts the class on its link. The
Node test marks a trail link, the handle and a relation button, and the
browser test checks those, with the rail rows, hold no tab stop.

Reviewed by jackmccarthy-77.
A reader can turn on MathJax's accessibility explorer from the menu of any
formula, and the setting holds for every page of the site. It then gave each
formula in a rail link, the trail, a relation row or the sheet handle a tab
stop at tabindex 1, ahead of the page, and took the Enter key from its
control, because the explorer attaches before the menu would reset the tab
stop, and the document for those formulas has no menu. That document now
turns the explorer's render action off too. A browser test turns the setting
on and checks that those controls hold no tab stop or application role while
the rail's title, outside any control, becomes one.

Also from the final pass: the browser test asks for the MathJax package
without needing Node, which a Playwright container may lack; a typeset
formula keeps its letters' case under the rail's group headings, which are
set in capitals; and the docs name the Contents list among the places that
show titles as typed.
One conflict, in CONTRIBUTING.md: #424 condensed the CI paragraph, and this branch's sentence on the WebKit job's MathJax tarball moves into its wording.
@Deicyde

Deicyde commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Final pass on #455: merge. The pass ran at fba2802a and recommended one fix before merge. That fix and three small items from the same pass are in 83005c92, and dad0dce8 merges main 63ed6bd. All 9 checks pass at dad0dce8, including the WebKit job (73 tests, the two real-MathJax tests among them), and the branch merges cleanly with main. Mark it ready and merge.

How it was checked

Four reviewers, a skeptic per finding, and a judge, all at fba2802a:

  • Runtime. A built site's explorer page ran in jsdom with MathJax 3.2.2 and the page's own scripts. It was driven in all three load orders, by click and by link, through Show more, pivots, the Contents list, search, history and the phone sheet. Article pages with read-back cards and the landing page were checked too. Every marked title and summary was typeset. No glyph lost its style rule, nothing unmarked was typeset, MathJax's startup document stayed empty, and the console stayed clean.
  • Safety and accessibility. Explorer formulas get exactly the page input and its safe options. The event carries nothing, so no script can choose other settings. Read-back cards keep their own inputs.
  • Tests and CI. Mutants of the reset, the walk from the parent, the started guard and the listener each fail a test. The WebKit job's MathJax step downloads the same URL and checks the same sha256 as the MathJax job.
  • Merge readiness. The PR conflicts with main nowhere. It adds no command, flag, JSON key or schema, and the description matches the code.

The one fix before merge

With MathJax's accessibility explorer on, formulas in controls became tab stops that took Enter. A reader can turn the explorer on from any formula's menu (Accessibility > Activate), and the setting holds for every page of the site. With it on, each formula in a rail link, a trail link, a relation row or the sheet handle got tabindex="1" and role="application". That put them ahead of the page in tab order, and Enter on one started the explorer instead of following the link.

  • Why. In MathJax 3.2.2 the explorer attaches at render state 160 and sets the tab stop. Only the menu's step at state 170 resets it, and the document for formulas in controls has no menu.
  • Fix, 83005c92. That document also turns off the explorer's render action (renderActions: {explorable: []}). Setting enableExplorer: false would not work, because the menu overwrites it.
  • Test. A new WebKit test turns the setting on and waits until the rail's title, which is outside any control, becomes an application, so the explorer has loaded. It then checks that the rail rows, the trail, the relation rows and the sheet handle hold no tab stop and no application role. In jsdom, with the setting on and off: no formula in a control has a tab stop or role, and the titles and summaries outside controls behave as article formulas do.

Also in 83005c92

  • Node. The browser test asks for the MathJax package without needing Node (mathjax_package(node=False)). Run the WebKit job in Playwright's pinned image #456's Playwright container has no Node, and there the test failed with "Node.js is not on PATH".
  • Letter case. A typeset formula keeps its letters' case under the rail's group headings, which are set in capitals. Before, p drew as P and \sigma as Σ.
  • Docs. The README and the changelog name the Contents list among the places that show titles as typed.

Follow-ups, not blocking

  • References and environments. The page input keeps MathJax's processRefs and processEnvironments on. In explorer prose, a bare \ref{x} therefore shows ???, and a bare \begin{proof} becomes an error box. Turning processRefs off is safe for articles; processEnvironments needs a decision.
  • Assistive copies. Each added pass rereads every formula element, and MathJax's TeX scan also reads the hidden assistive MathML. So a title whose \text holds two literal \$ gets a second, hidden formula. Skipping mjx-container in the page steps fixes it.
  • Focus rings. The site's :has(mjx-container) { overflow-x: clip } rule now matches the rail and relation lists, which clips the sides of their rows' focus rings. This was shown in jsdom but not seen painted.
  • Distribution labels. These are flex containers, so a typeset $K$-theory shows as K -theory. Mark an inner span instead.
  • Trail tooltips. They keep the raw TeX of entries that are now typeset.
  • Tests. Nothing yet asserts, under real MathJax, the places that should stay as typed: the explorer's head trail, search rows, and the handle with nothing selected. The Node harness could also record the options each document was asked for, so dropping enableMenu: false fails there as well as in WebKit.

Posted by the PR's author, from that final pass and the fix it asked for.

@Deicyde
Deicyde marked this pull request as ready for review October 24, 2026 02:22
Conflicts in autoform_cli/README.md and CONTRIBUTING.md, where #458 cut and condensed the docs. Both take main's text, with this branch's facts in its wording and within its line budgets: the explorer typesets its rail's and details' titles and summaries, formulas in links and buttons get no MathJax tab stop or menu, and CI's WebKit job unpacks the MathJax package too.
@Deicyde

Deicyde commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Second round on #455 at dad0dce: the blocker is fixed, and nothing blocks merge.

  • Formulas in controls. inControl now starts at the marked node, and the trail puts the class on its link when there is one.
    • I reran the Node harness with MathJax 3.2.2 from the checked tarball, on the DOM the explorer now draws. Every control formula went to the document without the menu: a trail link, the sheet handle, a relation button, and the rail rows' link and button. At 20f55c6, the first three went to the document with the menu.
    • With the walk starting at the parent again, test_the_explorer_s_titles_and_summaries_are_typeset_as_articles_are fails.
    • The WebKit test now asserts there is no [tabindex] inside a rail row, a trail link, a relation button or the handle.
  • The accessibility explorer. The menu-free document also drops MathJax's explorable render action (renderActions: {explorable: []}). A reader who turned the explorer on from another formula's menu therefore can't give these formulas a tab stop either, and the WebKit test sets that option to check it. This goes beyond my finding but follows the same rule.
  • Merge. Merging main 63ed6bd conflicted only in CONTRIBUTING.md, which keeps Test what the docs do, not their wording #424's wording plus this PR's sentence on the WebKit job's MathJax tarball. Every other file matches Git's automatic merge.
  • CI and local runs. All 9 checks pass at dad0dce, and the WebKit job ran 73 tests. Locally, the 7 Node tests and the 37 in tests/test_graph_view_rendering.py pass.

Posted by jackmccarthy-77.

@Deicyde

Deicyde commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Head check at a8e753f (merge of main 7677dbf, #458): nothing blocks merge. Eight of the PR's ten files are unchanged line for line from the round at dad0dce, and the two conflicts were resolved keeping both sides.

  • In autoform_cli/README.md, main's sentence on percent-encoded links and ./-prefixed colon segments, so that no name can make a javascript: URL, stays beside the PR's rule that the explorer typesets only its rail's and details' titles and summaries.
  • In CONTRIBUTING.md, Cut the docs agents read, split the CLI reference, and give them line budgets #458's bullet on MathJax tests now also says the Node ones need node, and that CI's MathJax and WebKit jobs unpack the package.

CI is green at a8e753f, 9 of 9: https://github.com/facebookresearch/autoform-bot/actions/runs/40060027990

Posted by PR swarm: PR Swarm Lead

@Deicyde
Deicyde merged commit cf6ce25 into main Oct 24, 2026
9 checks passed
@Deicyde
Deicyde deleted the fix/explorer-math branch October 24, 2026 07:52
Deicyde added a commit that referenced this pull request Oct 24, 2026
#455 added a pinned MathJax download step to the WebKit job, after the
install step this branch removes. The resolution, as agreed on #456:
keep the Playwright 1.62.0 image and its bash default, drop the install
step, and keep #455's checksummed MathJax step, its AUTOFORM_MATHJAX_DIR
export and its AUTOFORM_REQUIRE_MATHJAX_TESTS variable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant