Skip to content

Latest commit

 

History

History
110 lines (92 loc) · 5.96 KB

File metadata and controls

110 lines (92 loc) · 5.96 KB

The notation system

Single source of truth: the instance's notation/notation-map.json (authored, committed, evolves with the paper). Everything else — wraps in the PreTeXt source, far marks, the hover registry, order checking — is derived and regenerated by paperforge build web.

Map entry kinds

"GA":   {"kind": "macro",   "match": "\\GA", ...}          // wraps \GA tokens
"D0":   {"kind": "pattern", "match": "D_0(?![0-9])", ...}  // authored regex
"Qtwo": {..., "standard": true}      // hover only: no order gate, no far marks
"Y":    {"kind": "ambiguous", "match": "...Y...",          // single letters
         "senses": {"Ytarget": {...}, "Ygen": {...}}}

Common fields per entry (or per sense): definition (MathJax-ready HTML; the document's macros are available), defsite (xml:id used for order checking and far-distance), href (best link target when it differs from the defsite — e.g. defsite = the section where the symbol first appears in the flow, href = the theorem/definition that formally states it).

Ambiguous single letters (LLM disambiguation)

A bare Y, H, E, t means different things in different places; no regex can resolve that. The framework separates the mechanism (deterministic) from the judgment (LLM):

  1. tex2ptx --disambig notation/disambiguation.json wraps an ambiguous match only when a block-grain decision exists: {"Y": {"lem-covertransform": "Ytarget", "sec-quotients": "Ygen", "lem-subdirect": "none"}}. Blocks are theorem-like tags, else the enclosing (sub)section tag — the same stable tags as everything else, so decisions survive restructuring.
  2. Unclassified (key, block) pairs are left unwrapped (safe default) and written with context snippets to notation/unclassified.json; the build warns with a count.
  3. An LLM pass (Claude, in session or headless) reads the worklist and writes decisions into disambiguation.json — a committed, human-reviewable artifact. "none" is an explicit decision (generic use, never wrap).
  4. Draft updates create new blocks -> new unclassified entries -> incremental classification. Decisions for unchanged blocks carry over untouched.

Block grain (not per-occurrence) is deliberate: a statement essentially never mixes two senses of the same letter, decisions stay reviewable (~65 for a 70-page paper), and the cache is robust to intra-block edits.

Pattern lessons (hard-won; check the PDF after every map change)

  • Accents bind the next token: \bar \notnfar{x1}{x_1} is broken LaTeX and wrong semantics (x̄₁ is a different object). Every single-letter pattern needs (?<!\\bar )(?<!\\widetilde )(?<!\\widehat )(?<!\\ol )....
  • Font commands: bare-H matches inside \mathcal H, \mathsf H without (?<!\\mathcal )(?<!\\mathsf )...; bare-t inside \mathrm t subscripts.
  • Left-guard letter+subscript patterns: e_\Gamma matched inside \rangle_\Gamma (the trailing "e" of \rangle!). Use (?<![A-Za-z\\]).
  • Superscript exclusions where the letter has a standard super/sub meaning: the H pattern excludes H^... (cohomology) via lookahead.

Order checking and far marks

notation_order (validator): every non-standard key must have its defsite before all uses; uses inside <abstract> are exempt (abstracts forward- reference by design). notation_far.py rewrites \notn -> \notnfar beyond [notation] far_words from the defsite; standard keys are never far-marked. The hover UI: near symbols pop after near_hover_delay_ms (400), far symbols show cursor: help and pop after far_hover_delay_ms (1000) with a "see definition in context" link resolved from defsite/href.

Prose term links (ingest/prose_terms.py)

The prose companion of the math wrap, built for background/terminology hovers (first used to tie the gq2 paper's usage sites to its Appendix E background subsections). An authored map (paper.toml [notation] prose_map, conventionally notation/prose-map.json) gives per-key {match, scope?, first_per?, label, definition, href}; the script runs after tex2ptx + notation_far (so it sees insertion content and does not perturb far word counts) and wraps matches in the GENERATED tree as <termref key="K">...</termref>. The custom XSLs render that as <span class="ptxnotn-K ptxbg"> in HTML and as bare text in LaTeX.

Semantics: matching runs over a masked logical text per file — every tag is an opaque barrier (so wraps always nest), <ndash/>/<mdash/>/<nbsp/> read as their characters, $re$ in a pattern matches a whole inline <m> atomically, a space matches any whitespace run, and the compiled pattern gets word-boundary guards. First occurrence per block (nearest enclosing xml:id) is wrapped by default (first_per: block | division | all); titles, math displays, xrefs, code, biblio, and any division whose root id starts with bg- are skipped. Idempotent: existing termrefs are opaque and count toward the first-per-block bookkeeping.

Registry: notation_registry.py merges prose entries into window.PAPERFORGE_NOTATION with {html, href, label, more: true}; detail-ui shows label as the popup heading, uses the FAR delay for .ptxbg spans, and renders the footer link as "more details ↗" (entry.more). Href targets missing from the numbering database (e.g. insertion divisions like bg-*) fall back to a tag anchor on the single-page build.

Context-link landing highlight

Following a popup context link ("see definition in context" / "more details") deliberately bypasses the browser's :target styling (the theme paints the whole target block for 10s): detail-ui intercepts the click, jumps there, updates the URL with history.pushState (which does not engage :target), and paints ONLY the specific referenced text — the defining occurrence of the term inside the target block when one is wrapped there, else the target's heading — fading out over five seconds (.pf-landing-hl / .pf-landing-fade). Modified clicks (new tab) and cross-page hrefs keep default navigation.