explico 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/CHANGELOG.md +1137 -1
  2. package/README.md +91 -13
  3. package/dist/compiler/astro.d.ts +4 -3
  4. package/dist/compiler/astro.js +45 -15
  5. package/dist/compiler/cli.d.ts +26 -12
  6. package/dist/compiler/cli.js +96 -25
  7. package/dist/compiler/collections.d.ts +9 -1
  8. package/dist/compiler/collections.js +55 -16
  9. package/dist/compiler/content-schema.d.ts +31 -0
  10. package/dist/compiler/content-schema.js +82 -0
  11. package/dist/compiler/content-schemas.d.ts +422 -0
  12. package/dist/compiler/content-schemas.js +229 -0
  13. package/dist/compiler/course-config.d.ts +1 -1
  14. package/dist/compiler/course-config.js +30 -2
  15. package/dist/compiler/exports/combined-html.d.ts +97 -0
  16. package/dist/compiler/exports/combined-html.js +492 -0
  17. package/dist/compiler/exports/download-index.d.ts +9 -0
  18. package/dist/compiler/exports/download-index.js +33 -0
  19. package/dist/compiler/exports/export-chapters.d.ts +98 -0
  20. package/dist/compiler/exports/export-chapters.js +164 -0
  21. package/dist/compiler/exports/export-pages.d.ts +48 -0
  22. package/dist/compiler/exports/export-pages.js +22 -0
  23. package/dist/compiler/exports/html-tree.d.ts +22 -0
  24. package/dist/compiler/exports/html-tree.js +47 -0
  25. package/dist/compiler/exports/latex/capture.d.ts +106 -0
  26. package/dist/compiler/exports/latex/capture.js +435 -0
  27. package/dist/compiler/exports/latex/compile.d.ts +73 -0
  28. package/dist/compiler/exports/latex/compile.js +115 -0
  29. package/dist/compiler/exports/latex/document.d.ts +102 -0
  30. package/dist/compiler/exports/latex/document.js +349 -0
  31. package/dist/compiler/exports/latex/explico.cls +415 -0
  32. package/dist/compiler/exports/latex/numbering.d.ts +18 -0
  33. package/dist/compiler/exports/latex/numbering.js +57 -0
  34. package/dist/compiler/exports/latex/serialize.d.ts +112 -0
  35. package/dist/compiler/exports/latex/serialize.js +606 -0
  36. package/dist/compiler/exports/latex-export.d.ts +44 -0
  37. package/dist/compiler/exports/latex-export.js +169 -0
  38. package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
  39. package/dist/compiler/exports/markdown/serialize.js +527 -0
  40. package/dist/compiler/exports/markdown-export.d.ts +39 -0
  41. package/dist/compiler/exports/markdown-export.js +240 -0
  42. package/dist/compiler/exports/standalone-html.d.ts +14 -5
  43. package/dist/compiler/exports/standalone-html.js +23 -51
  44. package/dist/compiler/exports/static-tree.d.ts +253 -0
  45. package/dist/compiler/exports/static-tree.js +625 -0
  46. package/dist/compiler/exports/zip.d.ts +16 -0
  47. package/dist/compiler/exports/zip.js +72 -0
  48. package/dist/compiler/lesson-derivation.d.ts +8 -2
  49. package/dist/compiler/lesson-derivation.js +6 -7
  50. package/dist/compiler/manifest.d.ts +87 -6
  51. package/dist/compiler/manifest.js +174 -31
  52. package/dist/compiler/render-diagnostics.d.ts +65 -0
  53. package/dist/compiler/render-diagnostics.js +56 -0
  54. package/dist/compiler/resolution-report.d.ts +2 -1
  55. package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
  56. package/dist/curriculum/validation.d.ts +9 -3
  57. package/dist/curriculum/validation.js +35 -17
  58. package/dist/reference/base-abbreviations.d.mts +2 -2
  59. package/dist/reference/base-abbreviations.mjs +2 -2
  60. package/dist/reference/base-operators.d.mts +2 -2
  61. package/dist/reference/base-operators.mjs +2 -2
  62. package/dist/reference/base-roman-labels.d.mts +4 -3
  63. package/dist/reference/base-roman-labels.mjs +4 -3
  64. package/dist/reference/citation-format.d.mts +35 -0
  65. package/dist/reference/citation-format.mjs +74 -0
  66. package/dist/reference/consistency.d.ts +5 -9
  67. package/dist/reference/consistency.js +34 -38
  68. package/dist/reference/curriculum-alignment.js +3 -24
  69. package/dist/reference/derived-entries.d.ts +45 -0
  70. package/dist/reference/derived-entries.js +50 -0
  71. package/dist/reference/derived-notation.d.mts +125 -0
  72. package/dist/reference/derived-notation.mjs +121 -0
  73. package/dist/reference/diagram-fit.d.ts +45 -0
  74. package/dist/reference/diagram-fit.js +54 -0
  75. package/dist/reference/diagram-layout.d.ts +59 -10
  76. package/dist/reference/diagram-layout.js +85 -24
  77. package/dist/reference/diagrams-validate.d.ts +1 -1
  78. package/dist/reference/equations-validate.d.ts +1 -1
  79. package/dist/reference/equations-validate.js +26 -1
  80. package/dist/reference/equations.d.mts +27 -0
  81. package/dist/reference/equations.mjs +120 -22
  82. package/dist/reference/examples-validate.d.ts +1 -1
  83. package/dist/reference/figure-fit.d.ts +70 -0
  84. package/dist/reference/figure-fit.js +60 -0
  85. package/dist/reference/figure-pictures.d.ts +27 -0
  86. package/dist/reference/figure-pictures.js +49 -0
  87. package/dist/reference/figures-validate.d.ts +1 -1
  88. package/dist/reference/gate-abbreviations.d.ts +1 -1
  89. package/dist/reference/gate-abbreviations.js +1 -1
  90. package/dist/reference/gate-course-rules.d.ts +4 -3
  91. package/dist/reference/gate-course-rules.js +9 -4
  92. package/dist/reference/gate-heading-length.d.ts +2 -0
  93. package/dist/reference/gate-heading-length.js +7 -5
  94. package/dist/reference/gate-heading-structure.js +26 -0
  95. package/dist/reference/gate-indices.d.ts +35 -0
  96. package/dist/reference/gate-indices.js +299 -0
  97. package/dist/reference/gate-math.d.ts +32 -2
  98. package/dist/reference/gate-math.js +68 -12
  99. package/dist/reference/gate-mdx-expression.d.ts +17 -0
  100. package/dist/reference/gate-mdx-expression.js +29 -0
  101. package/dist/reference/gate-part-pages.d.ts +1 -1
  102. package/dist/reference/gate-prop-text.d.ts +28 -0
  103. package/dist/reference/gate-prop-text.js +106 -0
  104. package/dist/reference/gate-prose-notation.d.ts +3 -1
  105. package/dist/reference/gate-prose-notation.js +4 -2
  106. package/dist/reference/gate-retired-keys.d.ts +35 -0
  107. package/dist/reference/gate-retired-keys.js +58 -0
  108. package/dist/reference/gloss.d.ts +2 -0
  109. package/dist/reference/katex-measure.d.ts +17 -0
  110. package/dist/reference/katex-measure.js +223 -0
  111. package/dist/reference/keyed-float-validate.js +12 -0
  112. package/dist/reference/keyed-jsx-floats.d.mts +2 -2
  113. package/dist/reference/keyed-jsx-floats.mjs +11 -14
  114. package/dist/reference/lab-math-scope.d.ts +1 -0
  115. package/dist/reference/lab-math-scope.js +13 -2
  116. package/dist/reference/math-glyphs.d.mts +94 -2
  117. package/dist/reference/math-glyphs.mjs +851 -46
  118. package/dist/reference/mixed-content.d.ts +8 -3
  119. package/dist/reference/mixed-content.js +4 -2
  120. package/dist/reference/page-notation.d.ts +34 -4
  121. package/dist/reference/page-notation.js +54 -2
  122. package/dist/reference/references.d.ts +11 -1
  123. package/dist/reference/references.js +6 -0
  124. package/dist/reference/registry.js +104 -10
  125. package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
  126. package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
  127. package/dist/reference/rehype-heading-anchors.mjs +11 -1
  128. package/dist/reference/remark-citation.mjs +0 -0
  129. package/dist/reference/remark-notation.mjs +84 -20
  130. package/dist/reference/render-lab-math.d.ts +2 -0
  131. package/dist/reference/render-lab-math.js +17 -5
  132. package/dist/reference/sections-validate.d.ts +28 -3
  133. package/dist/reference/sections-validate.js +31 -4
  134. package/dist/reference/sections.d.mts +24 -6
  135. package/dist/reference/sections.mjs +92 -17
  136. package/dist/reference/tables-validate.d.ts +1 -1
  137. package/dist/reference/tables-validate.js +24 -1
  138. package/dist/reference/text-measure.d.ts +10 -0
  139. package/dist/reference/text-measure.js +41 -0
  140. package/dist/reference/text-widths.d.ts +10 -0
  141. package/dist/reference/text-widths.js +1238 -0
  142. package/dist/reference/types.d.ts +64 -8
  143. package/dist/rules/catalog.js +191 -30
  144. package/dist/rules/in-effect.d.ts +1 -1
  145. package/dist/rules/rules.schema.json +79 -24
  146. package/dist/rules/settings.d.ts +1 -1
  147. package/dist/rules/settings.js +7 -0
  148. package/dist/session/PreferenceStore.d.ts +18 -1
  149. package/dist/session/PreferenceStore.js +29 -1
  150. package/docs/authoring/README.md +32 -21
  151. package/docs/authoring/agents.md +38 -7
  152. package/docs/authoring/components.md +111 -6
  153. package/docs/authoring/migrating.md +707 -0
  154. package/docs/authoring/notation.md +281 -29
  155. package/docs/authoring/retired.md +1 -0
  156. package/docs/authoring/structure-rules.md +10 -41
  157. package/docs/authoring/structure.md +53 -2
  158. package/docs/authoring/visualizations.md +144 -18
  159. package/package.json +12 -7
  160. package/src/compiler/astro.ts +57 -17
  161. package/src/compiler/cli.ts +143 -35
  162. package/src/compiler/collections.ts +92 -19
  163. package/src/compiler/content-schema.ts +108 -0
  164. package/src/compiler/content-schemas.ts +391 -0
  165. package/src/compiler/course-config.ts +86 -15
  166. package/src/compiler/exports/combined-html.ts +669 -0
  167. package/src/compiler/exports/download-index.ts +36 -0
  168. package/src/compiler/exports/export-chapters.ts +233 -0
  169. package/src/compiler/exports/export-pages.ts +44 -0
  170. package/src/compiler/exports/html-tree.ts +62 -0
  171. package/src/compiler/exports/latex/capture.ts +651 -0
  172. package/src/compiler/exports/latex/compile.ts +168 -0
  173. package/src/compiler/exports/latex/document.ts +479 -0
  174. package/src/compiler/exports/latex/explico.cls +415 -0
  175. package/src/compiler/exports/latex/numbering.ts +74 -0
  176. package/src/compiler/exports/latex/serialize.ts +787 -0
  177. package/src/compiler/exports/latex/texlive-packages.txt +76 -0
  178. package/src/compiler/exports/latex-export.ts +255 -0
  179. package/src/compiler/exports/markdown/serialize.ts +632 -0
  180. package/src/compiler/exports/markdown-export.ts +329 -0
  181. package/src/compiler/exports/standalone-html.ts +39 -70
  182. package/src/compiler/exports/static-tree.ts +944 -0
  183. package/src/compiler/exports/zip.ts +88 -0
  184. package/src/compiler/lesson-derivation.ts +13 -8
  185. package/src/compiler/manifest.ts +299 -28
  186. package/src/compiler/render-diagnostics.ts +111 -0
  187. package/src/components/HoverPanel.astro +204 -6
  188. package/src/components/PartOverview.astro +12 -9
  189. package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
  190. package/src/components/assessments/AssessmentRunner.tsx +7 -1
  191. package/src/components/assessments/AssessmentSet.astro +14 -1
  192. package/src/components/block-heading.ts +38 -0
  193. package/src/components/card-arrow.ts +18 -4
  194. package/src/components/card-carry.ts +406 -0
  195. package/src/components/card-drag.ts +1 -30
  196. package/src/components/card-fetch.ts +87 -0
  197. package/src/components/card-manager.ts +1222 -38
  198. package/src/components/card-memory.ts +5 -1
  199. package/src/components/card-placement.ts +211 -13
  200. package/src/components/card-side.ts +281 -31
  201. package/src/components/citation/CitationLayer.astro +45 -40
  202. package/src/components/diagram/Diagram.astro +123 -44
  203. package/src/components/figure/Figure.astro +11 -0
  204. package/src/components/figure/FloatCaption.astro +4 -17
  205. package/src/components/float-toolbar.ts +274 -0
  206. package/src/components/memo/Memo.astro +21 -10
  207. package/src/components/memo/ShortNote.astro +4 -0
  208. package/src/components/notation/NotationGlossary.astro +22 -17
  209. package/src/components/notation/NotationLayer.astro +321 -79
  210. package/src/components/notation/NotationMath.astro +8 -1
  211. package/src/components/notation/render-notation-math.ts +51 -5
  212. package/src/components/readable-text.ts +25 -0
  213. package/src/components/reference/FloatRefLayer.astro +162 -89
  214. package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
  215. package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
  216. package/src/components/starlight/LayoutPageSidebar.astro +17 -3
  217. package/src/components/starlight/LayoutSocialIcons.astro +79 -29
  218. package/src/components/starlight/LessonExportActions.astro +384 -310
  219. package/src/components/starlight/LessonFooter.astro +47 -9
  220. package/src/components/starlight/PrintController.astro +28 -0
  221. package/src/components/starlight/TextSizeControl.astro +137 -0
  222. package/src/components/starlight/print-controller.ts +380 -0
  223. package/src/components/starlight/section-summary.ts +13 -5
  224. package/src/components/table/Table.astro +13 -21
  225. package/src/components/table/table-truncation.ts +15 -0
  226. package/src/content-config.ts +20 -312
  227. package/src/curriculum/validation.ts +72 -24
  228. package/src/reference/base-abbreviations.mjs +2 -2
  229. package/src/reference/base-operators.mjs +2 -2
  230. package/src/reference/base-roman-labels.mjs +4 -3
  231. package/src/reference/citation-format.mjs +74 -0
  232. package/src/reference/consistency.ts +47 -61
  233. package/src/reference/curriculum-alignment.ts +4 -41
  234. package/src/reference/derived-entries.ts +68 -0
  235. package/src/reference/derived-notation.mjs +121 -0
  236. package/src/reference/diagram-fit.ts +104 -0
  237. package/src/reference/diagram-layout.ts +182 -51
  238. package/src/reference/diagrams-validate.ts +2 -1
  239. package/src/reference/equations-validate.ts +41 -2
  240. package/src/reference/equations.mjs +120 -22
  241. package/src/reference/examples-validate.ts +1 -0
  242. package/src/reference/figure-fit.ts +139 -0
  243. package/src/reference/figure-pictures.ts +96 -0
  244. package/src/reference/figures-validate.ts +2 -1
  245. package/src/reference/gate-abbreviations.ts +1 -1
  246. package/src/reference/gate-course-rules.ts +22 -7
  247. package/src/reference/gate-heading-length.ts +25 -7
  248. package/src/reference/gate-heading-structure.ts +40 -1
  249. package/src/reference/gate-indices.ts +470 -0
  250. package/src/reference/gate-math.ts +102 -13
  251. package/src/reference/gate-mdx-expression.ts +45 -0
  252. package/src/reference/gate-prop-text.ts +145 -0
  253. package/src/reference/gate-prose-notation.ts +19 -5
  254. package/src/reference/gate-retired-keys.ts +101 -0
  255. package/src/reference/gloss.ts +2 -0
  256. package/src/reference/katex-measure.ts +384 -0
  257. package/src/reference/keyed-float-validate.ts +22 -0
  258. package/src/reference/keyed-jsx-floats.mjs +11 -14
  259. package/src/reference/lab-math-scope.ts +15 -1
  260. package/src/reference/math-glyphs.mjs +851 -46
  261. package/src/reference/mixed-content.ts +11 -2
  262. package/src/reference/page-notation.ts +100 -3
  263. package/src/reference/references.ts +14 -0
  264. package/src/reference/registry.ts +154 -8
  265. package/src/reference/rehype-fail-katex-errors.mjs +19 -0
  266. package/src/reference/rehype-heading-anchors.mjs +11 -1
  267. package/src/reference/remark-citation.mjs +0 -0
  268. package/src/reference/remark-notation.mjs +84 -20
  269. package/src/reference/render-lab-math.ts +29 -5
  270. package/src/reference/sections-validate.ts +67 -3
  271. package/src/reference/sections.mjs +92 -17
  272. package/src/reference/tables-validate.ts +40 -2
  273. package/src/reference/text-measure.ts +63 -0
  274. package/src/reference/text-widths.ts +133 -0
  275. package/src/reference/types.ts +75 -8
  276. package/src/rules/catalog.ts +205 -29
  277. package/src/rules/settings.ts +10 -0
  278. package/src/session/PreferenceStore.ts +53 -0
  279. package/src/styles/global.css +291 -75
@@ -0,0 +1,415 @@
1
+ %% explico.cls — the book class of a course's LaTeX source.
2
+ %%
3
+ %% explico writes this file next to every LaTeX download, and builds the PDF
4
+ %% it offers from that same folder. Edit it freely in a download: it only sets
5
+ %% the layout and defines the commands the chapter files use.
6
+ %%
7
+ %% Engines: LuaLaTeX (preferred) or pdfLaTeX, TeX Live 2025 or later.
8
+ %%
9
+ %% Numbering follows the course website: parts and chapters are numbered
10
+ %% ‹part›.‹chapter›, sections ‹part›.‹chapter›.‹section›, and equations,
11
+ %% figures, tables, diagrams, worked examples and knowledge checks run per
12
+ %% chapter (‹part›.‹chapter›.‹n›) or, with \ExplicoFloatsByPart, per part
13
+ %% (‹part›.‹n›). LaTeX does the counting; explico checks every number against
14
+ %% the website when it builds the PDF.
15
+ \NeedsTeXFormat{LaTeX2e}
16
+ \ProvidesClass{explico}[2026/10/04 explico course book class]
17
+
18
+ %% `scripts`: the document holds Greek, Cyrillic, CJK or emoji (the exporter
19
+ %% sets it when it does), so LuaLaTeX loads the fallback fonts below.
20
+ \newif\ifexplico@scripts
21
+ \DeclareOption{scripts}{\explico@scriptstrue}
22
+ \DeclareOption*{\PassOptionsToClass{\CurrentOption}{book}}
23
+ \ProcessOptions\relax
24
+ \LoadClass[11pt,oneside,openany]{book}
25
+
26
+ %% --- Engine and fonts ------------------------------------------------------
27
+ \RequirePackage{iftex}
28
+ \RequirePackage{amsmath,amssymb,mathtools}
29
+ %% Rule N17's engine half, as on the website (src/reference/katex-delimiters.mjs):
30
+ %% a negative \delimitershortfall makes every \left...\right pair strictly taller
31
+ %% than its content, so a nest grows outward at each level. TeX's default (5pt)
32
+ %% lets nested pairs round to the same glyph.
33
+ \delimitershortfall=-1pt
34
+ %% Whether a text font sets Greek; if none does, a Greek letter in prose is
35
+ %% set as its math symbol (below, with the other Unicode characters).
36
+ \newif\ifexplico@greek
37
+ \ifPDFTeX
38
+ \RequirePackage[T1]{fontenc}
39
+ \RequirePackage{lmodern}
40
+ \RequirePackage{textcomp}
41
+ \RequirePackage{bm}
42
+ %% A character no pdfTeX font sets (accented Greek, Chinese, an emoji) stays
43
+ %% blank instead of stopping the compile, and the log says so in LuaLaTeX's
44
+ %% words, which the export turns into its warning about blank characters.
45
+ \def\UTFviii@undefined@err#1{%
46
+ \typeout{Missing character: There is no
47
+ \expandafter\UTFviii@splitcsname\string#1\relax\space in font pdftex!}}
48
+ \else
49
+ %% Latin Modern for text and math. unicode-math is also what lets a tagged
50
+ %% PDF carry MathML for every formula (luamml needs it); bm does not work
51
+ %% with it, so \bm becomes \boldsymbol.
52
+ \RequirePackage{unicode-math}
53
+ \providecommand{\bm}{\boldsymbol}
54
+ %% Latin Modern has no Greek, Cyrillic, Chinese, Japanese, Korean or emoji.
55
+ %% With `scripts`, prose in those scripts comes from fallback fonts: for each
56
+ %% script, the first of the fonts below this machine has (CMU Serif, from
57
+ %% TeX Live's cm-unicode, matches Latin Modern). A script none of them
58
+ %% covers stays blank, and the export warns about it.
59
+ \ifexplico@scripts
60
+ \def\explico@fallbacks{}
61
+ \def\explico@fallback#1#2{%
62
+ \let\explico@found\@empty
63
+ \@for\explico@candidate:=#1\do{%
64
+ \ifx\explico@found\@empty
65
+ \IfFontExistsTF{\explico@candidate}{%
66
+ \edef\explico@fallbacks{\explico@fallbacks"\explico@candidate:mode=harf;",}%
67
+ \let\explico@found\explico@candidate}{}%
68
+ \fi}%
69
+ \ifx\explico@found\@empty\else#2\fi}
70
+ \explico@fallback{CMU Serif,Noto Serif,DejaVu Serif,FreeSerif}{\explico@greektrue}
71
+ \explico@fallback{Noto Serif CJK SC,Noto Sans CJK SC,FandolSong,Source Han Serif SC}{}
72
+ \explico@fallback{Noto Color Emoji,Twemoji Mozilla,Symbola}{}
73
+ \ifx\explico@fallbacks\@empty\else
74
+ \directlua{luaotfload.add_fallback("explico", {\explico@fallbacks})}%
75
+ \setmainfont{Latin Modern Roman}[RawFeature={fallback=explico}]%
76
+ \fi
77
+ \fi
78
+ \fi
79
+ \RequirePackage{microtype}
80
+
81
+ %% --- Page and colour --------------------------------------------------------
82
+ \RequirePackage[margin=1in]{geometry}
83
+ \RequirePackage{xcolor}
84
+ \definecolor{explicoaccent}{HTML}{12775C}
85
+ \definecolor{explicoframe}{HTML}{9DB8AD}
86
+ \definecolor{explicoback}{HTML}{F3F8F6}
87
+ \definecolor{explicoasidecaution}{HTML}{B5651D}
88
+ \definecolor{explicoasidecautionback}{HTML}{FBF3EA}
89
+ \definecolor{explicorule}{HTML}{8A9A93}
90
+
91
+ %% --- Floats, tables and captions -------------------------------------------
92
+ \RequirePackage{graphicx}
93
+ \RequirePackage{array,booktabs,tabularx,longtable}
94
+ \RequirePackage[export]{adjustbox}
95
+ \RequirePackage{caption}
96
+ \RequirePackage{newfloat}
97
+ \DeclareFloatingEnvironment[
98
+ name=Diagram,
99
+ listname={List of Diagrams},
100
+ fileext=lod,
101
+ placement=htbp,
102
+ ]{diagram}
103
+ %% Tagging names each float type's structures from TeX Live 2026 on, and a
104
+ %% caption in a type it was not told about stops LuaLaTeX. TeX Live 2025 has
105
+ %% no such key and tags every float alike.
106
+ \ExplSyntaxOn
107
+ \keys_if_exist:nnT { __tag/setup } { float/new }
108
+ { \tagpdfsetup { float/new = diagram } }
109
+ \ExplSyntaxOff
110
+ %% Contents and lists: room for explico's numbers (`2.1`, `2.1.10`), wider
111
+ %% than the book class's one-level chapter numbers.
112
+ \RequirePackage{etoolbox}
113
+ \patchcmd{\l@chapter}{1.5em}{2.6em}{}{}
114
+ \renewcommand*{\l@section}{\@dottedtocline{1}{2.6em}{3.4em}}
115
+ \renewcommand*{\l@subsection}{\@dottedtocline{2}{6.0em}{4.2em}}
116
+ \renewcommand*{\l@figure}{\@dottedtocline{1}{0em}{3.4em}}
117
+ \let\l@table\l@figure
118
+ \let\l@diagram\l@figure
119
+ \captionsetup{font=small,labelsep=quad,format=plain,justification=justified,singlelinecheck=true}
120
+ \captionsetup[table]{position=top}
121
+ %% With \DocumentMetadata, which every export's main.tex has, the kernel's
122
+ %% tagging code sets a caption itself (latex-lab's \@makecaption, installed at
123
+ %% begin document) and none of the \captionsetup above applies. Its caption is
124
+ %% the kernel's: \abovecaptionskip above, \belowcaptionskip below, "Table 2.1:"
125
+ %% before the text. A table's caption stands above the table, so it gets the
126
+ %% same space on both sides, whichever code sets it (the caption package swaps
127
+ %% the two for `position=top`); and the label and the size are the ones the
128
+ %% caption package would give, and the web page shows: "Table 2.1", a quad,
129
+ %% the caption in small type.
130
+ \AtBeginEnvironment{table}{%
131
+ \setlength{\abovecaptionskip}{8pt}\setlength{\belowcaptionskip}{8pt}}
132
+ \ExplSyntaxOn
133
+ \socket_if_exist:nT { caption/label }
134
+ {
135
+ \NewSocketPlug { caption/label } { explico } { #1 \quad }
136
+ \AssignSocketPlug { caption/label } { explico }
137
+ \AddToHookWithArguments { cmd/@makecaption/before } { \small }
138
+ }
139
+ \ExplSyntaxOff
140
+ %% A float set where it stands, inside a memo or a worked example, which a
141
+ %% float cannot leave: the float package's [H]. Tagging then gives the float
142
+ %% its caption, as PDF/UA-2 asks. (TeX Live 2026 tags a minipage as a Div,
143
+ %% which a validator looks through, so a \captionof caption in one sits in
144
+ %% the middle of the box's text.) Without the float package: a minipage whose
145
+ %% \caption is the float's, as \captionof does it.
146
+ \newif\ifexplico@float
147
+ \IfFileExists{float.sty}{\RequirePackage{float}\explico@floattrue}{}
148
+ \ifexplico@float
149
+ \newenvironment{ExplicoHere}[1]{%
150
+ \def\explico@herekind{#1}\begin{#1}[H]\centering
151
+ }{\expandafter\end\expandafter{\explico@herekind}}
152
+ \else
153
+ \newenvironment{ExplicoHere}[1]{%
154
+ \par\medskip\noindent\begin{minipage}{\linewidth}\centering
155
+ \captionsetup{type=#1}%
156
+ }{\end{minipage}\par\medskip}
157
+ \fi
158
+ %% explico's short caption, run in bold before the long one.
159
+ \newcommand*{\ExplicoCaptionTitle}[1]{\textbf{#1}}
160
+
161
+ %% --- Diagrams ---------------------------------------------------------------
162
+ \RequirePackage{tikz}
163
+ \usetikzlibrary{arrows.meta}
164
+ \tikzset{
165
+ explico diagram node/.style={
166
+ draw=explicorule, fill=explicoback, rounded corners=3pt, line width=0.5pt,
167
+ align=center, inner sep=3pt, font=\small,
168
+ },
169
+ explico diagram edge/.style={
170
+ draw=explicorule, line width=0.6pt, -{Stealth[length=5pt,width=4pt]},
171
+ },
172
+ }
173
+
174
+ %% --- Boxes: memos, notes, asides, worked examples, checks ------------------
175
+ \RequirePackage[most]{tcolorbox}
176
+ \tcbset{
177
+ explico/.style={
178
+ enhanced, breakable, colback=explicoback, colframe=explicoframe,
179
+ boxrule=0.5pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt,
180
+ fonttitle=\bfseries\small, coltitle=black, colbacktitle=explicoback!60!white,
181
+ titlerule=0.4pt, before skip=10pt plus 2pt, after skip=10pt plus 2pt,
182
+ },
183
+ }
184
+ \RequirePackage{alphalph}
185
+ \RequirePackage{enumitem}
186
+
187
+ %% --- Unicode in text that the default fonts lack ---------------------------
188
+ \RequirePackage{newunicodechar}
189
+ \newunicodechar{°}{\ifmmode^\circ\else\textdegree\fi}
190
+ \newunicodechar{−}{\ensuremath{-}}
191
+ \newunicodechar{×}{\ensuremath{\times}}
192
+ \newunicodechar{±}{\ensuremath{\pm}}
193
+ \newunicodechar{≤}{\ensuremath{\le}}
194
+ \newunicodechar{≥}{\ensuremath{\ge}}
195
+ \newunicodechar{≈}{\ensuremath{\approx}}
196
+ \newunicodechar{≠}{\ensuremath{\neq}}
197
+ \newunicodechar{→}{\ensuremath{\rightarrow}}
198
+ \newunicodechar{←}{\ensuremath{\leftarrow}}
199
+ \newunicodechar{↑}{\ensuremath{\uparrow}}
200
+ \newunicodechar{↓}{\ensuremath{\downarrow}}
201
+ \newunicodechar{⟨}{\ensuremath{\langle}}
202
+ \newunicodechar{⟩}{\ensuremath{\rangle}}
203
+ \newunicodechar{⋮}{\ensuremath{\vdots}}
204
+ \newunicodechar{√}{\ensuremath{\surd}}
205
+ \newunicodechar{∞}{\ensuremath{\infty}}
206
+ \newunicodechar{′}{\ensuremath{'}}
207
+ \newunicodechar{𝒩}{\ensuremath{\mathcal{N}}}
208
+ \newunicodechar{①}{\textcircled{\scriptsize 1}}
209
+ \newunicodechar{②}{\textcircled{\scriptsize 2}}
210
+ \newunicodechar{③}{\textcircled{\scriptsize 3}}
211
+ \newunicodechar{④}{\textcircled{\scriptsize 4}}
212
+ \newunicodechar{⑤}{\textcircled{\scriptsize 5}}
213
+ \newunicodechar{₀}{\textsubscript{0}}
214
+ \newunicodechar{₁}{\textsubscript{1}}
215
+ \newunicodechar{₂}{\textsubscript{2}}
216
+ \newunicodechar{₃}{\textsubscript{3}}
217
+ \newunicodechar{¹}{\textsuperscript{1}}
218
+ \newunicodechar{²}{\textsuperscript{2}}
219
+ \newunicodechar{³}{\textsuperscript{3}}
220
+ %% A Greek letter on its own is a symbol (the polar coordinates ρ and φ in a
221
+ %% caption, which holds no math): it is set as its math symbol. Inside a Greek
222
+ %% word, which the exporter marks \ExplicoGreekWord{…}, it is a letter, set
223
+ %% from the text font's Greek where one is loaded (`scripts`).
224
+ \newif\ifexplico@word
225
+ \DeclareRobustCommand{\ExplicoGreekWord}[1]{{\explico@wordtrue#1}}
226
+ %% Protected, as \ensuremath is: the text of an equation is expanded for its
227
+ %% tagging (alt text, MathML), and an expandable body breaks it there.
228
+ \protected\def\explico@greekletter#1#2{%
229
+ \ifmmode#2\else
230
+ \ifexplico@word
231
+ \ifexplico@greek\char#1\relax\else\ensuremath{#2}\fi
232
+ \else\ensuremath{#2}\fi
233
+ \fi}
234
+ \newunicodechar{α}{\explico@greekletter{"03B1}{\alpha}}
235
+ \newunicodechar{β}{\explico@greekletter{"03B2}{\beta}}
236
+ \newunicodechar{γ}{\explico@greekletter{"03B3}{\gamma}}
237
+ \newunicodechar{δ}{\explico@greekletter{"03B4}{\delta}}
238
+ \newunicodechar{ε}{\explico@greekletter{"03B5}{\varepsilon}}
239
+ \newunicodechar{θ}{\explico@greekletter{"03B8}{\theta}}
240
+ \newunicodechar{λ}{\explico@greekletter{"03BB}{\lambda}}
241
+ \newunicodechar{μ}{\explico@greekletter{"03BC}{\mu}}
242
+ \newunicodechar{π}{\explico@greekletter{"03C0}{\pi}}
243
+ \newunicodechar{ρ}{\explico@greekletter{"03C1}{\rho}}
244
+ \newunicodechar{σ}{\explico@greekletter{"03C3}{\sigma}}
245
+ \newunicodechar{τ}{\explico@greekletter{"03C4}{\tau}}
246
+ \newunicodechar{φ}{\explico@greekletter{"03C6}{\varphi}}
247
+ \newunicodechar{ω}{\explico@greekletter{"03C9}{\omega}}
248
+ \newunicodechar{Γ}{\explico@greekletter{"0393}{\Gamma}}
249
+ \newunicodechar{Δ}{\explico@greekletter{"0394}{\Delta}}
250
+ \newunicodechar{Θ}{\explico@greekletter{"0398}{\Theta}}
251
+ \newunicodechar{Λ}{\explico@greekletter{"039B}{\Lambda}}
252
+ \newunicodechar{Π}{\explico@greekletter{"03A0}{\Pi}}
253
+ \newunicodechar{Σ}{\explico@greekletter{"03A3}{\Sigma}}
254
+ \newunicodechar{Φ}{\explico@greekletter{"03A6}{\Phi}}
255
+ \newunicodechar{Ω}{\explico@greekletter{"03A9}{\Omega}}
256
+
257
+ %% --- Links ------------------------------------------------------------------
258
+ \RequirePackage[colorlinks,linkcolor=explicoaccent,urlcolor=explicoaccent,citecolor=explicoaccent,bookmarksnumbered]{hyperref}
259
+ \pdfstringdefDisableCommands{\let\ExplicoGreekWord\@firstofone}
260
+
261
+ %% --- Numbering --------------------------------------------------------------
262
+ \renewcommand{\thepart}{\arabic{part}}
263
+ \counterwithin*{chapter}{part}
264
+ \renewcommand{\thechapter}{\thepart.\arabic{chapter}}
265
+ \newcounter{explicoexample}
266
+ \newcounter{explicocheck}
267
+ \newcounter{explicomemo}[chapter]
268
+ \renewcommand{\theexplicomemo}{\alphalph{\value{explicomemo}}}
269
+ \counterwithin{equation}{chapter}
270
+ \counterwithin{figure}{chapter}
271
+ \counterwithin{table}{chapter}
272
+ \counterwithin{diagram}{chapter}
273
+ \counterwithin{explicoexample}{chapter}
274
+ \counterwithin{explicocheck}{chapter}
275
+ %% Floats, examples and checks run on across each part's chapters.
276
+ \newcommand{\ExplicoFloatsByPart}{%
277
+ \@for\explico@kind:=equation,figure,table,diagram,explicoexample,explicocheck\do{%
278
+ \expandafter\counterwithout\expandafter{\explico@kind}{chapter}%
279
+ \expandafter\counterwithin\expandafter{\explico@kind}{part}%
280
+ }%
281
+ }
282
+ %% Outline numbering down to explico's `numbering.sections.depth`
283
+ %% (2 = ## … 6 = ######); 1 numbers no sections.
284
+ \newcommand{\ExplicoSectionDepth}[1]{%
285
+ \setcounter{secnumdepth}{\numexpr#1-1\relax}%
286
+ \setcounter{tocdepth}{\numexpr#1-1\relax}%
287
+ }
288
+ \ExplicoSectionDepth{4}
289
+ %% A chapter on its own starts where the course leaves it: part and chapter
290
+ %% number, and how many floats of each kind come earlier in the part.
291
+ %% #1 part, #2 chapter, #3 equations, #4 tables, #5 figures, #6 diagrams,
292
+ %% #7 checks, #8 examples.
293
+ \newcommand{\ExplicoStartChapterAt}[8]{%
294
+ \setcounter{part}{#1}%
295
+ \setcounter{chapter}{\numexpr#2-1\relax}%
296
+ \gdef\explico@offsets{{#3}{#4}{#5}{#6}{#7}{#8}}%
297
+ }
298
+ \gdef\explico@offsets{}
299
+ %% After \chapter, so a chapter's own reset cannot undo them.
300
+ \newcommand{\ExplicoApplyOffsets}{%
301
+ \ifx\explico@offsets\@empty\else
302
+ \expandafter\explico@applyoffsets\explico@offsets
303
+ \gdef\explico@offsets{}%
304
+ \fi
305
+ }
306
+ \newcommand{\explico@applyoffsets}[6]{%
307
+ \setcounter{equation}{#1}\setcounter{table}{#2}\setcounter{figure}{#3}%
308
+ \setcounter{diagram}{#4}\setcounter{explicocheck}{#5}\setcounter{explicoexample}{#6}%
309
+ }
310
+
311
+ %% --- Chapter front line -----------------------------------------------------
312
+ %% Under a chapter's title: where it comes from and where its live version is.
313
+ \newcommand{\ExplicoSource}[3]{%
314
+ \begingroup\small\color{black!65}%
315
+ \noindent From \emph{#1}%
316
+ \if\relax\detokenize{#2}\relax\else, version~#2\fi.%
317
+ \if\relax\detokenize{#3}\relax\else\ Interactive version: \url{#3}\fi
318
+ \par\endgroup\medskip
319
+ }
320
+
321
+ %% --- Books: a part's or the course's PDF ------------------------------------
322
+ %% The title page. #1 title, #2 what it belongs to (empty for the course),
323
+ %% #3 content version, #4 authors, #5 website.
324
+ \newcommand{\ExplicoTitlePage}[5]{%
325
+ %% The title page shares page number i with the contents: no anchor of its own.
326
+ \hypersetup{pageanchor=false}%
327
+ \begin{titlepage}
328
+ \centering
329
+ \vspace*{0.18\textheight}
330
+ {\Huge\bfseries #1\par}
331
+ \if\relax\detokenize{#2}\relax\else\vspace{0.8em}{\Large #2\par}\fi
332
+ \if\relax\detokenize{#4}\relax\else\vspace{2.5em}{\large #4\par}\fi
333
+ \vfill
334
+ {Content version~#3\par}
335
+ \if\relax\detokenize{#5}\relax\else\vspace{0.4em}{\small\url{#5}\par}\fi
336
+ \end{titlepage}%
337
+ \hypersetup{pageanchor=true}%
338
+ }
339
+ %% Under a chapter's title in a book: its live version. The title page names
340
+ %% the course and its version once.
341
+ \newcommand{\ExplicoInteractive}[1]{%
342
+ \begingroup\small\color{black!65}%
343
+ \noindent Interactive version: \url{#1}\par\endgroup\medskip
344
+ }
345
+ %% A part opens on its own page, numbered as on the website.
346
+ \newcommand{\ExplicoPart}[2]{\setcounter{part}{\numexpr#1-1\relax}\part{#2}}
347
+
348
+ %% --- Notation ---------------------------------------------------------------
349
+ %% \explain{key}{tex} and \slot{key}{tex} are explico's markers in math: the
350
+ %% website opens a card for the key; on paper they print their content.
351
+ \providecommand{\explain}[2]{#2}
352
+ \providecommand{\slot}[2]{#2}
353
+ %% \ExplicoNotation{target}{tex}: a formula's symbol that links to its notation entry
354
+ %% (the serializer writes it only where the book has that entry).
355
+ \providecommand{\ExplicoNotation}[2]{\hyperlink{#1}{#2}}
356
+ %% A term in prose with a notation card on the website.
357
+ \newcommand{\ExplicoTerm}[2]{#2}
358
+
359
+ %% --- Cross-references ---------------------------------------------------------
360
+ %% #1 label, #2 text before the number, #3 text after it.
361
+ \newcommand{\ExplicoRef}[3]{\hyperref[#1]{#2\ref*{#1}#3}}
362
+ \newcommand{\ExplicoEqRef}[1]{\hyperref[#1]{(\ref*{#1})}}
363
+
364
+ %% --- Memos --------------------------------------------------------------------
365
+ \newenvironment{ExplicoMemo}[1]{%
366
+ \refstepcounter{explicomemo}%
367
+ \begin{tcolorbox}[explico,title={Memo~\theexplicomemo:\enspace #1}]%
368
+ }{\end{tcolorbox}}
369
+ \newcommand{\ExplicoMemoMark}[1]{\textsuperscript{\hyperref[#1]{\ref*{#1}}}}
370
+
371
+ %% --- Short notes ----------------------------------------------------------------
372
+ \newenvironment{ExplicoNote}[2]{%
373
+ \begin{tcolorbox}[explico,colback=white,title={#2}]%
374
+ \hypertarget{#1}{}%
375
+ }{\end{tcolorbox}}
376
+ \newcommand{\ExplicoNoteMark}[2]{\hyperlink{#1}{#2}}
377
+
378
+ %% --- Starlight asides -------------------------------------------------------------
379
+ \newenvironment{ExplicoAside}[2]{%
380
+ \def\explico@variant{#1}\def\explico@caution{caution}\def\explico@danger{danger}%
381
+ \ifx\explico@variant\explico@caution
382
+ \tcbset{explico/.append style={colframe=explicoasidecaution,colback=explicoasidecautionback}}%
383
+ \else\ifx\explico@variant\explico@danger
384
+ \tcbset{explico/.append style={colframe=explicoasidecaution,colback=explicoasidecautionback}}%
385
+ \fi\fi
386
+ \begin{tcolorbox}[explico,title={#2}]%
387
+ }{\end{tcolorbox}}
388
+
389
+ %% --- Worked examples ---------------------------------------------------------------
390
+ \newenvironment{ExplicoExamples}[1]{%
391
+ \refstepcounter{explicoexample}%
392
+ \begin{tcolorbox}[explico,title={Example~\theexplicoexample:\enspace #1}]%
393
+ }{\end{tcolorbox}}
394
+ \newcommand{\ExplicoExample}[1]{\par\medskip\noindent\textbf{#1}\par\nopagebreak\smallskip}
395
+
396
+ %% --- Knowledge checks -----------------------------------------------------------
397
+ \newenvironment{ExplicoCheck}[1]{%
398
+ \refstepcounter{explicocheck}%
399
+ \begin{tcolorbox}[explico,colback=white,title={Knowledge check~\theexplicocheck:\enspace #1}]%
400
+ }{\end{tcolorbox}}
401
+ \newcommand{\ExplicoCheckAnswerNote}[1]{\par\smallskip{\small\itshape Answers: \hyperlink{#1}{end of the chapter}.}\par}
402
+
403
+ %% --- References ---------------------------------------------------------------------
404
+ %% The chapter's reference list, numbered as on the website.
405
+ \newenvironment{ExplicoReferences}[1]{%
406
+ \section*{References}%
407
+ \list{\@biblabel{\@arabic\c@enumiv}}{%
408
+ \settowidth\labelwidth{\@biblabel{#1}}%
409
+ \leftmargin\labelwidth\advance\leftmargin\labelsep
410
+ \@openbib@code\usecounter{enumiv}\let\p@enumiv\@empty
411
+ \renewcommand\theenumiv{\@arabic\c@enumiv}}%
412
+ \sloppy\clubpenalty4000\@clubpenalty\clubpenalty\widowpenalty4000\sfcode`\.\@m
413
+ }{\def\@noitemerr{\@latex@warning{Empty `ExplicoReferences' environment}}\endlist}
414
+
415
+ \endinput
@@ -0,0 +1,18 @@
1
+ import { StaticPage } from '../static-tree.js';
2
+ import '../html-tree.js';
3
+ import 'parse5';
4
+
5
+ /**
6
+ * The LaTeX source counts its own numbers (`\label`, `\ref`), so an edited
7
+ * download renumbers itself. When explico compiles the source unchanged,
8
+ * every number LaTeX prints must be the number the website prints: this
9
+ * module reads both and lists any disagreement, which fails the build.
10
+ */
11
+ /** The number the website gives each labelled anchor of a page, by LaTeX label. */
12
+ declare function websiteNumbers(page: StaticPage, chapterNumber: string): Map<string, string>;
13
+ /** `\newlabel{label}{{number}{page}…}` lines of a LaTeX `.aux` file. */
14
+ declare function auxLabels(aux: string): Map<string, string>;
15
+ /** Every label whose LaTeX number differs from the website's, or that LaTeX never set. */
16
+ declare function numberingMismatches(website: ReadonlyMap<string, string>, latex: ReadonlyMap<string, string>): string[];
17
+
18
+ export { auxLabels, numberingMismatches, websiteNumbers };
@@ -0,0 +1,57 @@
1
+ import { labelOf } from "./serialize.js";
2
+ function websiteNumbers(page, chapterNumber) {
3
+ const numbers = /* @__PURE__ */ new Map([[labelOf(page.route, "_top"), chapterNumber]]);
4
+ const add = (anchor, number) => {
5
+ if (anchor && number) numbers.set(labelOf(page.route, anchor), number);
6
+ };
7
+ const visit = (blocks) => {
8
+ for (const block of blocks) {
9
+ switch (block.kind) {
10
+ case "heading":
11
+ add(block.anchor, block.number);
12
+ break;
13
+ case "equation":
14
+ case "figure":
15
+ case "table":
16
+ case "diagram":
17
+ case "examples":
18
+ case "check":
19
+ add(block.anchor, block.number);
20
+ break;
21
+ case "memo":
22
+ add(block.anchor, block.label);
23
+ break;
24
+ default:
25
+ break;
26
+ }
27
+ if (block.kind === "list") block.items.forEach(visit);
28
+ if (block.kind === "examples") block.items.forEach((item) => visit(item.children));
29
+ if ("children" in block && block.kind !== "paragraph" && block.kind !== "heading" && Array.isArray(block.children)) {
30
+ visit(block.children);
31
+ }
32
+ }
33
+ };
34
+ visit(page.blocks);
35
+ return numbers;
36
+ }
37
+ function auxLabels(aux) {
38
+ const labels = /* @__PURE__ */ new Map();
39
+ for (const match of aux.matchAll(/^\\newlabel\{([^}]*)\}\{\{([^}]*)\}/gmu)) {
40
+ labels.set(match[1], match[2]);
41
+ }
42
+ return labels;
43
+ }
44
+ function numberingMismatches(website, latex) {
45
+ const problems = [];
46
+ for (const [label, expected] of website) {
47
+ const actual = latex.get(label);
48
+ if (actual === void 0) problems.push(`${label}: no number in LaTeX (website: ${expected})`);
49
+ else if (actual !== expected) problems.push(`${label}: LaTeX ${actual}, website ${expected}`);
50
+ }
51
+ return problems;
52
+ }
53
+ export {
54
+ auxLabels,
55
+ numberingMismatches,
56
+ websiteNumbers
57
+ };
@@ -0,0 +1,112 @@
1
+ import { ContentSegment } from '../../../reference/mixed-content.js';
2
+ import { NotationEntry, StaticPage, DiagramGeometry, Inline } from '../static-tree.js';
3
+ import '../../../reference/gate-math.js';
4
+ import '../../../reference/types.js';
5
+ import '../html-tree.js';
6
+ import 'parse5';
7
+
8
+ /**
9
+ * Serialize explico's static reading tree to the LaTeX of one chapter, written
10
+ * against `explico.cls`. The output is source a person can read and edit:
11
+ * explico's blocks are named environments, cross-references are `\ref`s to
12
+ * labels, and numbers are LaTeX's own (checked against the website after
13
+ * compiling, see `numbering.ts`).
14
+ */
15
+ /** What the serializer needs to know beyond the page itself. */
16
+ interface ChapterContext {
17
+ readonly page: StaticPage;
18
+ /** Routes of every page in this document; a reference to one becomes a `\ref`. */
19
+ readonly documentRoutes: ReadonlySet<string>;
20
+ /** Published course root, for links that leave the document. */
21
+ readonly canonicalBaseUrl?: string;
22
+ /** Picture files written for this chapter, by float anchor (`fig-…`), relative to the document. */
23
+ readonly pictures: ReadonlyMap<string, string>;
24
+ /** Answers of this chapter's checks, by assessment id, in authored order. */
25
+ readonly checkItems: ReadonlyMap<string, readonly CheckItem[]>;
26
+ /** Course title, version and the chapter's live URL for the front line. */
27
+ readonly source?: {
28
+ readonly course: string;
29
+ readonly version?: string;
30
+ readonly url?: string;
31
+ };
32
+ /**
33
+ * A chapter of a part's or the course's book: its citation keys gain the
34
+ * chapter (`projectiles/projectile-range/openstax`), since each chapter
35
+ * keeps its own reference list numbered as on its page; its notation is in
36
+ * the book's appendix, not after the chapter; and under its title is only
37
+ * the link to its live version, the title page naming the course.
38
+ */
39
+ readonly inBook?: boolean;
40
+ /**
41
+ * Keys with an entry in the document's notation appendix, so a formula's
42
+ * `\explain{key}{…}` can link to it. Absent for a chapter's own document,
43
+ * which has no appendix: its markers print their TeX, as the class does.
44
+ */
45
+ readonly notationTargets?: ReadonlySet<string>;
46
+ }
47
+ interface CheckItem {
48
+ readonly itemId: string;
49
+ readonly type: 'single-choice' | 'numeric';
50
+ readonly promptSegments: readonly ContentSegment[];
51
+ readonly explanationSegments: readonly ContentSegment[];
52
+ readonly options?: readonly {
53
+ readonly id: string;
54
+ readonly label: string;
55
+ /** The label as text and math (manifest v30); without it, the label is text. */
56
+ readonly labelSegments?: readonly ContentSegment[];
57
+ }[];
58
+ readonly correctOptionId?: string;
59
+ readonly answer?: {
60
+ readonly value: number;
61
+ readonly tolerance: number;
62
+ };
63
+ }
64
+ /** A LaTeX label for an anchor on a page: unique across a whole book. */
65
+ declare function labelOf(route: string, anchor: string): string;
66
+ declare function escapeText(text: string): string;
67
+ /**
68
+ * Rewrites each `\explain{key}{tex}` whose key `linkTo` names as
69
+ * `\ExplicoNotation{target}{tex}`, a link to the notation entry; any other
70
+ * marker stays, and the class prints its TeX. Brace-aware, so a body with
71
+ * nested groups is kept whole. A link cannot hold another, so the outermost
72
+ * linked marker wins: `\dot{x}` links to the time derivative, and the markers
73
+ * inside it print their TeX.
74
+ */
75
+ declare function linkExplains(tex: string, linkTo: (key: string) => string | undefined): string;
76
+ /**
77
+ * explico's `<Diagram>` in TikZ, from the layout Dagre gave it at build time:
78
+ * the same boxes and edges as the website, with LaTeX setting the text and the
79
+ * math. Coordinates are scaled to the text width; type keeps its size.
80
+ */
81
+ declare function diagramTikz(geometry: DiagramGeometry, writer: {
82
+ inlines(inlines: readonly Inline[]): string;
83
+ }): string;
84
+ /** The LaTeX of one chapter: `\chapter`, its body, references, notation and answers. */
85
+ declare function serializeChapter(context: ChapterContext): string;
86
+ /**
87
+ * A part's opener in a book: `\ExplicoPart`, numbered as on the website, then
88
+ * the text of the part's own page (its introduction, scope and objectives).
89
+ */
90
+ declare function serializePartOpener(context: ChapterContext, part: {
91
+ readonly ordinal: number;
92
+ readonly label: string;
93
+ }): string;
94
+ /** A symbol in a book's notation appendix: its entry, the page that defines it, and the chapter it is local to. */
95
+ interface AppendixNotation {
96
+ readonly entry: NotationEntry;
97
+ readonly page: StaticPage;
98
+ /** `2.2` for a symbol only one chapter defines; none for a shared one. */
99
+ readonly chapter?: string;
100
+ }
101
+ /**
102
+ * The book's notation appendix: every symbol its chapters use, in the order
103
+ * they first appear, each with its meaning, units and defining formula.
104
+ */
105
+ declare function serializeNotationAppendix(entries: readonly AppendixNotation[], context: Omit<ChapterContext, 'page'>): string;
106
+ /** The book's abbreviation appendix: each abbreviation and what it stands for. */
107
+ declare function serializeAbbreviationAppendix(abbreviations: readonly {
108
+ readonly abbr: string;
109
+ readonly expansion: string;
110
+ }[]): string;
111
+
112
+ export { type AppendixNotation, type ChapterContext, type CheckItem, diagramTikz, escapeText, labelOf, linkExplains, serializeAbbreviationAppendix, serializeChapter, serializeNotationAppendix, serializePartOpener };