explico 0.11.1 → 0.13.0

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 (298) hide show
  1. package/CHANGELOG.md +1292 -1
  2. package/README.md +91 -13
  3. package/dist/compiler/astro.d.ts +11 -4
  4. package/dist/compiler/astro.js +48 -15
  5. package/dist/compiler/cli.d.ts +27 -12
  6. package/dist/compiler/cli.js +96 -25
  7. package/dist/compiler/collections.d.ts +33 -2
  8. package/dist/compiler/collections.js +91 -19
  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 +15 -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 +115 -8
  51. package/dist/compiler/manifest.js +221 -39
  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 +3 -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 +8 -2
  61. package/dist/reference/base-operators.mjs +8 -10
  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/figure-symbols.d.ts +32 -0
  88. package/dist/reference/figure-symbols.js +55 -0
  89. package/dist/reference/figures-validate.d.ts +1 -1
  90. package/dist/reference/gate-abbreviations.d.ts +1 -1
  91. package/dist/reference/gate-abbreviations.js +1 -1
  92. package/dist/reference/gate-course-rules.d.ts +4 -3
  93. package/dist/reference/gate-course-rules.js +9 -4
  94. package/dist/reference/gate-heading-length.d.ts +2 -0
  95. package/dist/reference/gate-heading-length.js +7 -5
  96. package/dist/reference/gate-heading-structure.js +26 -0
  97. package/dist/reference/gate-indices.d.ts +35 -0
  98. package/dist/reference/gate-indices.js +299 -0
  99. package/dist/reference/gate-math.d.ts +32 -2
  100. package/dist/reference/gate-math.js +68 -12
  101. package/dist/reference/gate-mdx-expression.d.ts +17 -0
  102. package/dist/reference/gate-mdx-expression.js +29 -0
  103. package/dist/reference/gate-non-chapter-pages.d.ts +39 -0
  104. package/dist/reference/gate-non-chapter-pages.js +156 -0
  105. package/dist/reference/gate-part-pages.d.ts +4 -2
  106. package/dist/reference/gate-part-pages.js +2 -1
  107. package/dist/reference/gate-prop-text.d.ts +28 -0
  108. package/dist/reference/gate-prop-text.js +106 -0
  109. package/dist/reference/gate-prose-notation.d.ts +3 -1
  110. package/dist/reference/gate-prose-notation.js +4 -2
  111. package/dist/reference/gate-retired-keys.d.ts +35 -0
  112. package/dist/reference/gate-retired-keys.js +58 -0
  113. package/dist/reference/gloss.d.ts +2 -0
  114. package/dist/reference/katex-measure.d.ts +17 -0
  115. package/dist/reference/katex-measure.js +223 -0
  116. package/dist/reference/keyed-float-validate.js +12 -0
  117. package/dist/reference/keyed-jsx-floats.d.mts +2 -2
  118. package/dist/reference/keyed-jsx-floats.mjs +11 -14
  119. package/dist/reference/lab-katex-validate.d.ts +43 -0
  120. package/dist/reference/lab-katex-validate.js +96 -0
  121. package/dist/reference/lab-math-scope.d.ts +1 -0
  122. package/dist/reference/lab-math-scope.js +13 -2
  123. package/dist/reference/lint-ignore.js +1 -1
  124. package/dist/reference/math-glyphs.d.mts +94 -2
  125. package/dist/reference/math-glyphs.mjs +851 -46
  126. package/dist/reference/mixed-content.d.ts +8 -3
  127. package/dist/reference/mixed-content.js +4 -2
  128. package/dist/reference/page-notation.d.ts +39 -7
  129. package/dist/reference/page-notation.js +62 -10
  130. package/dist/reference/raw-floats.mjs +37 -13
  131. package/dist/reference/references.d.ts +11 -1
  132. package/dist/reference/references.js +6 -0
  133. package/dist/reference/registry.js +115 -11
  134. package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
  135. package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
  136. package/dist/reference/rehype-heading-anchors.mjs +11 -1
  137. package/dist/reference/remark-citation.mjs +0 -0
  138. package/dist/reference/remark-notation.mjs +275 -23
  139. package/dist/reference/render-lab-math.d.ts +13 -2
  140. package/dist/reference/render-lab-math.js +18 -6
  141. package/dist/reference/sections-validate.d.ts +28 -3
  142. package/dist/reference/sections-validate.js +31 -4
  143. package/dist/reference/sections.d.mts +24 -6
  144. package/dist/reference/sections.mjs +92 -17
  145. package/dist/reference/tables-validate.d.ts +1 -1
  146. package/dist/reference/tables-validate.js +24 -1
  147. package/dist/reference/text-measure.d.ts +10 -0
  148. package/dist/reference/text-measure.js +41 -0
  149. package/dist/reference/text-widths.d.ts +10 -0
  150. package/dist/reference/text-widths.js +1238 -0
  151. package/dist/reference/types.d.ts +64 -8
  152. package/dist/rules/catalog.js +223 -32
  153. package/dist/rules/in-effect.d.ts +1 -1
  154. package/dist/rules/rules.schema.json +100 -27
  155. package/dist/rules/settings.d.ts +1 -1
  156. package/dist/rules/settings.js +7 -0
  157. package/dist/session/PreferenceStore.d.ts +18 -1
  158. package/dist/session/PreferenceStore.js +29 -1
  159. package/docs/authoring/README.md +33 -21
  160. package/docs/authoring/agents.md +38 -7
  161. package/docs/authoring/components.md +115 -10
  162. package/docs/authoring/migrating.md +878 -0
  163. package/docs/authoring/notation.md +285 -30
  164. package/docs/authoring/retired.md +1 -0
  165. package/docs/authoring/structure-rules.md +84 -45
  166. package/docs/authoring/structure.md +67 -2
  167. package/docs/authoring/visualizations.md +167 -25
  168. package/package.json +12 -7
  169. package/src/compiler/astro.ts +72 -21
  170. package/src/compiler/cli.ts +143 -35
  171. package/src/compiler/collections.ts +158 -27
  172. package/src/compiler/content-schema.ts +108 -0
  173. package/src/compiler/content-schemas.ts +391 -0
  174. package/src/compiler/course-config.ts +86 -15
  175. package/src/compiler/exports/combined-html.ts +669 -0
  176. package/src/compiler/exports/download-index.ts +36 -0
  177. package/src/compiler/exports/export-chapters.ts +233 -0
  178. package/src/compiler/exports/export-pages.ts +44 -0
  179. package/src/compiler/exports/html-tree.ts +62 -0
  180. package/src/compiler/exports/latex/capture.ts +651 -0
  181. package/src/compiler/exports/latex/compile.ts +168 -0
  182. package/src/compiler/exports/latex/document.ts +479 -0
  183. package/src/compiler/exports/latex/explico.cls +415 -0
  184. package/src/compiler/exports/latex/numbering.ts +74 -0
  185. package/src/compiler/exports/latex/serialize.ts +787 -0
  186. package/src/compiler/exports/latex/texlive-packages.txt +76 -0
  187. package/src/compiler/exports/latex-export.ts +255 -0
  188. package/src/compiler/exports/markdown/serialize.ts +632 -0
  189. package/src/compiler/exports/markdown-export.ts +329 -0
  190. package/src/compiler/exports/standalone-html.ts +39 -70
  191. package/src/compiler/exports/static-tree.ts +944 -0
  192. package/src/compiler/exports/zip.ts +88 -0
  193. package/src/compiler/lesson-derivation.ts +13 -8
  194. package/src/compiler/manifest.ts +387 -40
  195. package/src/compiler/render-diagnostics.ts +111 -0
  196. package/src/components/CourseContents.astro +6 -0
  197. package/src/components/HoverPanel.astro +204 -6
  198. package/src/components/PartOverview.astro +18 -9
  199. package/src/components/abbreviation/AbbreviationGlossary.astro +6 -2
  200. package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
  201. package/src/components/assessments/AssessmentRunner.tsx +7 -1
  202. package/src/components/assessments/AssessmentSet.astro +14 -1
  203. package/src/components/block-heading.ts +38 -0
  204. package/src/components/card-arrow.ts +18 -4
  205. package/src/components/card-carry.ts +406 -0
  206. package/src/components/card-drag.ts +1 -30
  207. package/src/components/card-fetch.ts +87 -0
  208. package/src/components/card-manager.ts +1222 -38
  209. package/src/components/card-memory.ts +5 -1
  210. package/src/components/card-placement.ts +211 -13
  211. package/src/components/card-side.ts +281 -31
  212. package/src/components/citation/CitationLayer.astro +45 -40
  213. package/src/components/diagram/Diagram.astro +123 -44
  214. package/src/components/disclosure/print-as-shown.ts +42 -6
  215. package/src/components/figure/Figure.astro +17 -1
  216. package/src/components/figure/FloatCaption.astro +4 -17
  217. package/src/components/figure/figure-state.ts +26 -17
  218. package/src/components/float-toolbar.ts +274 -0
  219. package/src/components/memo/Memo.astro +21 -10
  220. package/src/components/memo/ShortNote.astro +4 -0
  221. package/src/components/notation/NotationGlossary.astro +22 -17
  222. package/src/components/notation/NotationLayer.astro +300 -79
  223. package/src/components/notation/NotationMath.astro +8 -1
  224. package/src/components/notation/render-notation-math.ts +51 -5
  225. package/src/components/readable-text.ts +25 -0
  226. package/src/components/reference/FloatRefLayer.astro +162 -89
  227. package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
  228. package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
  229. package/src/components/starlight/LayoutPageSidebar.astro +17 -3
  230. package/src/components/starlight/LayoutSocialIcons.astro +79 -29
  231. package/src/components/starlight/LessonExportActions.astro +384 -310
  232. package/src/components/starlight/LessonFooter.astro +66 -8
  233. package/src/components/starlight/PrintController.astro +28 -0
  234. package/src/components/starlight/TextSizeControl.astro +137 -0
  235. package/src/components/starlight/print-controller.ts +380 -0
  236. package/src/components/starlight/section-summary.ts +13 -5
  237. package/src/components/table/Table.astro +14 -22
  238. package/src/components/table/table-truncation.ts +15 -0
  239. package/src/content-config.ts +20 -312
  240. package/src/curriculum/validation.ts +72 -24
  241. package/src/reference/base-abbreviations.mjs +2 -2
  242. package/src/reference/base-operators.mjs +8 -10
  243. package/src/reference/base-roman-labels.mjs +4 -3
  244. package/src/reference/citation-format.mjs +74 -0
  245. package/src/reference/consistency.ts +47 -61
  246. package/src/reference/curriculum-alignment.ts +4 -41
  247. package/src/reference/derived-entries.ts +68 -0
  248. package/src/reference/derived-notation.mjs +121 -0
  249. package/src/reference/diagram-fit.ts +104 -0
  250. package/src/reference/diagram-layout.ts +182 -51
  251. package/src/reference/diagrams-validate.ts +2 -1
  252. package/src/reference/equations-validate.ts +41 -2
  253. package/src/reference/equations.mjs +120 -22
  254. package/src/reference/examples-validate.ts +1 -0
  255. package/src/reference/figure-fit.ts +139 -0
  256. package/src/reference/figure-pictures.ts +96 -0
  257. package/src/reference/figure-symbols.ts +117 -0
  258. package/src/reference/figures-validate.ts +2 -1
  259. package/src/reference/gate-abbreviations.ts +1 -1
  260. package/src/reference/gate-course-rules.ts +22 -7
  261. package/src/reference/gate-heading-length.ts +25 -7
  262. package/src/reference/gate-heading-structure.ts +40 -1
  263. package/src/reference/gate-indices.ts +470 -0
  264. package/src/reference/gate-math.ts +102 -13
  265. package/src/reference/gate-mdx-expression.ts +45 -0
  266. package/src/reference/gate-non-chapter-pages.ts +241 -0
  267. package/src/reference/gate-part-pages.ts +1 -1
  268. package/src/reference/gate-prop-text.ts +145 -0
  269. package/src/reference/gate-prose-notation.ts +19 -5
  270. package/src/reference/gate-retired-keys.ts +101 -0
  271. package/src/reference/gloss.ts +2 -0
  272. package/src/reference/katex-measure.ts +384 -0
  273. package/src/reference/keyed-float-validate.ts +22 -0
  274. package/src/reference/keyed-jsx-floats.mjs +11 -14
  275. package/src/reference/lab-katex-validate.ts +156 -0
  276. package/src/reference/lab-math-scope.ts +15 -1
  277. package/src/reference/lint-ignore.ts +4 -1
  278. package/src/reference/math-glyphs.mjs +851 -46
  279. package/src/reference/mixed-content.ts +11 -2
  280. package/src/reference/page-notation.ts +114 -14
  281. package/src/reference/raw-floats.mjs +37 -13
  282. package/src/reference/references.ts +14 -0
  283. package/src/reference/registry.ts +170 -9
  284. package/src/reference/rehype-fail-katex-errors.mjs +19 -0
  285. package/src/reference/rehype-heading-anchors.mjs +11 -1
  286. package/src/reference/remark-citation.mjs +0 -0
  287. package/src/reference/remark-notation.mjs +275 -23
  288. package/src/reference/render-lab-math.ts +40 -5
  289. package/src/reference/sections-validate.ts +67 -3
  290. package/src/reference/sections.mjs +92 -17
  291. package/src/reference/tables-validate.ts +40 -2
  292. package/src/reference/text-measure.ts +63 -0
  293. package/src/reference/text-widths.ts +133 -0
  294. package/src/reference/types.ts +75 -8
  295. package/src/rules/catalog.ts +241 -31
  296. package/src/rules/settings.ts +10 -0
  297. package/src/session/PreferenceStore.ts +53 -0
  298. package/src/styles/global.css +322 -83
@@ -0,0 +1,223 @@
1
+ import katex, {} from "katex";
2
+ import fontMetricsData from "katex/src/fontMetricsData.js";
3
+ import { createNotationKatexOptions } from "./katex-options.mjs";
4
+ const SIZE_MULTIPLIERS = [0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.44, 1.728, 2.074, 2.488];
5
+ const CLASS_PADDING = {
6
+ "x-arrow-pad": [0.5, 0.5],
7
+ "cd-arrow-pad": [0.27778, 0.55556],
8
+ boxpad: [0.3, 0.3],
9
+ "cancel-pad": [0.2, 0.2],
10
+ anglpad: [0.03889, 0.03889]
11
+ };
12
+ const CLASS_MARGIN = {
13
+ "cancel-lap": [-0.2, -0.2],
14
+ angl: [0, 0.03889]
15
+ };
16
+ const CLASS_BORDER = {
17
+ fbox: [0.04, 0.04],
18
+ fcolorbox: [0.04, 0.04],
19
+ angl: [0, 0.049]
20
+ };
21
+ const ZERO_WIDTH = /* @__PURE__ */ new Set(["llap", "rlap", "clap", "pstrut", "vlist-s"]);
22
+ function has(node, name) {
23
+ return node.classes?.includes(name) ?? false;
24
+ }
25
+ function em(value) {
26
+ if (!value) return 0;
27
+ const match = /^(-?\d*\.?\d+)em$/.exec(value.trim());
28
+ return match ? Number(match[1]) : 0;
29
+ }
30
+ function sizing(node) {
31
+ if (!has(node, "sizing") && !has(node, "fontsize-ensurer")) return 1;
32
+ let from = 0;
33
+ let to = 0;
34
+ for (const name of node.classes ?? []) {
35
+ const reset = /^reset-size(\d+)$/.exec(name);
36
+ if (reset) from = Number(reset[1]);
37
+ const size = /^size(\d+)$/.exec(name);
38
+ if (size) to = Number(size[1]);
39
+ }
40
+ const fromMultiplier = SIZE_MULTIPLIERS[from - 1];
41
+ const toMultiplier = SIZE_MULTIPLIERS[to - 1];
42
+ return fromMultiplier && toMultiplier ? toMultiplier / fromMultiplier : 1;
43
+ }
44
+ function pairOf(table, node) {
45
+ let total = 0;
46
+ for (const name of node.classes ?? []) {
47
+ const pair = table[name];
48
+ if (pair) total += pair[0] + pair[1];
49
+ }
50
+ return total;
51
+ }
52
+ const ROOT_FONT = { family: "Main", bold: false, italic: false };
53
+ const FONT_CLASSES = {
54
+ textbf: { bold: true },
55
+ textit: { italic: true },
56
+ textrm: { family: "Main" },
57
+ texttt: { family: "Typewriter" },
58
+ mathnormal: { family: "Math", italic: true },
59
+ mathit: { family: "Main", italic: true },
60
+ mathrm: { italic: false },
61
+ mathbf: { family: "Main", bold: true },
62
+ boldsymbol: { family: "Math", bold: true, italic: true },
63
+ amsrm: { family: "AMS" },
64
+ mathbb: { family: "AMS" },
65
+ textbb: { family: "AMS" },
66
+ mathcal: { family: "Caligraphic" },
67
+ mathfrak: { family: "Fraktur" },
68
+ textfrak: { family: "Fraktur" },
69
+ mathboldfrak: { family: "Fraktur", bold: true },
70
+ textboldfrak: { family: "Fraktur", bold: true },
71
+ mathtt: { family: "Typewriter" },
72
+ mathscr: { family: "Script" },
73
+ textscr: { family: "Script" },
74
+ mathsf: { family: "SansSerif" },
75
+ textsf: { family: "SansSerif" },
76
+ mathboldsf: { family: "SansSerif", bold: true },
77
+ textboldsf: { family: "SansSerif", bold: true },
78
+ mathsfit: { family: "SansSerif", italic: true },
79
+ mathitsf: { family: "SansSerif", italic: true },
80
+ textitsf: { family: "SansSerif", italic: true },
81
+ mainrm: { family: "Main", italic: false }
82
+ };
83
+ function fontOf(node, inherited) {
84
+ let font = inherited;
85
+ for (const name of node.classes ?? []) {
86
+ const rule = FONT_CLASSES[name];
87
+ if (rule) font = { ...font, ...rule };
88
+ }
89
+ if (has(node, "delimsizing")) {
90
+ for (const size of [1, 2, 3, 4]) {
91
+ if (has(node, `size${size}`)) font = { ...font, family: `Size${size}` };
92
+ }
93
+ }
94
+ if (has(node, "op-symbol")) {
95
+ if (has(node, "small-op")) font = { ...font, family: "Size1" };
96
+ if (has(node, "large-op")) font = { ...font, family: "Size2" };
97
+ }
98
+ return font;
99
+ }
100
+ const METRICS = fontMetricsData;
101
+ function metricsFor(font) {
102
+ const variants = [
103
+ font.bold && font.italic ? "BoldItalic" : font.bold ? "Bold" : font.italic ? "Italic" : "Regular",
104
+ font.bold ? "Bold" : void 0,
105
+ font.italic ? "Italic" : void 0,
106
+ "Regular"
107
+ ];
108
+ for (const variant of variants) {
109
+ const table = variant && METRICS[`${font.family}-${variant}`];
110
+ if (table) return table;
111
+ }
112
+ return METRICS["Main-Regular"] ?? {};
113
+ }
114
+ const WIDE_GLYPHS = /* @__PURE__ */ new Map([
115
+ [8748, 0.85],
116
+ // ∬ \iint
117
+ [8749, 1.22],
118
+ // ∭ \iiint
119
+ [8751, 0.85],
120
+ // ∯ \oiint
121
+ [8752, 1.22],
122
+ // ∰ \oiiint
123
+ [10764, 1.6]
124
+ // ⨌ \iiiint
125
+ ]);
126
+ function glyphWidth(table, char) {
127
+ if (char === "\u200B") return 0;
128
+ const metrics = table[String(char.codePointAt(0))] ?? table["77"];
129
+ return metrics?.[4] ?? 1;
130
+ }
131
+ function glyphAdvances(node, font) {
132
+ const [first, ...rest] = [...node.text ?? ""];
133
+ if (first === void 0) return [];
134
+ const table = metricsFor(font);
135
+ const wide = (char) => WIDE_GLYPHS.get(char.codePointAt(0) ?? 0);
136
+ const firstWidth = wide(first) ?? (node.width && node.width > 0 ? node.width : glyphWidth(table, first));
137
+ return [firstWidth, ...rest.map((char) => wide(char) ?? glyphWidth(table, char))];
138
+ }
139
+ function glyphPx(advanceEm, fontPx) {
140
+ return Math.max(Math.round(advanceEm * Math.round(fontPx)), advanceEm * fontPx);
141
+ }
142
+ function advance(node, inherited, context, parent) {
143
+ const frame = {
144
+ scale: inherited.scale * sizing(node),
145
+ font: fontOf(node, inherited.font)
146
+ };
147
+ const scale = frame.scale * context.fontPx;
148
+ const style = node.style ?? {};
149
+ if (typeof node.text === "string") {
150
+ const marginRight = style.marginRight !== void 0 ? em(style.marginRight) : Math.max(0, node.italic ?? 0);
151
+ const glyphs = glyphAdvances(node, frame.font).reduce(
152
+ (sum, width) => sum + glyphPx(width, scale),
153
+ 0
154
+ );
155
+ return glyphs + (marginRight + em(style.marginLeft)) * scale;
156
+ }
157
+ if (!node.classes) return 0;
158
+ const margins = (em(style.marginLeft) + em(style.marginRight) + pairOf(CLASS_MARGIN, node) + rootMargin(node, parent)) * scale;
159
+ if (node.classes.some((name) => ZERO_WIDTH.has(name))) return margins;
160
+ if (has(node, "accent-body") && !has(node, "accent-full")) return margins;
161
+ let content;
162
+ if (has(node, "nulldelimiter")) {
163
+ content = 0.12 * scale;
164
+ } else if (em(style.width) > 0) {
165
+ content = em(style.width) * scale;
166
+ } else if (has(node, "vlist-t")) {
167
+ content = tableWidth(node, frame, context);
168
+ } else if (has(node, "vlist")) {
169
+ content = Math.max(
170
+ 0,
171
+ ...(node.children ?? []).map((row) => advance(row, frame, context, node))
172
+ );
173
+ } else {
174
+ content = (node.children ?? []).reduce(
175
+ (sum, child) => sum + advance(child, frame, context, node),
176
+ 0
177
+ );
178
+ }
179
+ content = Math.max(content, em(style.minWidth) * scale);
180
+ if (has(node, "vertical-separator")) content = Math.max(content, 1);
181
+ const padding = (em(style.paddingLeft) + pairOf(CLASS_PADDING, node)) * scale;
182
+ const border = (em(style.borderRightWidth) + pairOf(CLASS_BORDER, node)) * scale;
183
+ return content + padding + border + margins;
184
+ }
185
+ function rootMargin(node, parent) {
186
+ return has(node, "root") && parent !== void 0 && has(parent, "sqrt") ? 0.2777777778 - 0.5555555556 : 0;
187
+ }
188
+ function tableWidth(node, frame, context) {
189
+ const columns = [];
190
+ for (const row of node.children ?? []) {
191
+ let column = 0;
192
+ for (const cell of row.children ?? []) {
193
+ if (has(cell, "vlist-s")) continue;
194
+ columns[column] = Math.max(columns[column] ?? 0, advance(cell, frame, context, row));
195
+ column += 1;
196
+ }
197
+ }
198
+ return columns.reduce((sum, width) => sum + width, 0);
199
+ }
200
+ function collectBases(node, out) {
201
+ if (has(node, "base")) {
202
+ out.push(node);
203
+ return out;
204
+ }
205
+ for (const child of node.children ?? []) collectBases(child, out);
206
+ return out;
207
+ }
208
+ function measureFormula(latex, fontPx) {
209
+ const { __renderToHTMLTree } = katex;
210
+ const tree = __renderToHTMLTree(latex, {
211
+ ...createNotationKatexOptions(),
212
+ displayMode: false
213
+ });
214
+ const bases = collectBases(tree, []);
215
+ return {
216
+ width: advance(tree, { scale: 1, font: ROOT_FONT }, { fontPx }),
217
+ height: Math.max(0, ...bases.map((base) => base.height ?? 0)) * fontPx,
218
+ depth: Math.max(0, ...bases.map((base) => base.depth ?? 0)) * fontPx
219
+ };
220
+ }
221
+ export {
222
+ measureFormula
223
+ };
@@ -46,6 +46,18 @@ function validateKeyedFloats(lessons, kind) {
46
46
  ...raw.key ? { key: raw.key } : {}
47
47
  });
48
48
  }
49
+ for (const prop of ["short", "caption"]) {
50
+ if (!raw.props.includes(prop) || raw[prop] != null) continue;
51
+ const places = prefix === "ex" ? `its [[${prefix}-\u2026]] card and a copied link` : `its [[${prefix}-\u2026]] card, a copied link, a folded section's line and the "Figures and tables" page`;
52
+ diagnostics.push({
53
+ code: `${prefix}-unread-prop`,
54
+ severity: "warning",
55
+ message: `${lesson.lessonId} has a <${tagName} ${prop}={\u2026}>${raw.key ? ` (id="${raw.key}")` : ""}, but the manifest reads only a quoted \`${prop}\`, so ${places} name this ${noun} without it. Write ${prop}="\u2026", with typographic quotes (\u2018\u2019 \u201C\u201D) inside.`,
56
+ file: lesson.file,
57
+ lessonId: lesson.lessonId,
58
+ ...raw.key ? { key: raw.key } : {}
59
+ });
60
+ }
49
61
  if (raw.key == null) continue;
50
62
  if (!KEYED_FLOAT_KEY_PATTERN.test(raw.key)) {
51
63
  diagnostics.push({
@@ -50,8 +50,8 @@ export function countKeyedJsxFloats(markdown: any, { tagName }: {
50
50
  tagName: any;
51
51
  }): number;
52
52
  /**
53
- * The mdast equivalent of `scanKeyedJsxFloats`: walk the tree's top-level
54
- * children (and through the `TRANSPARENT_COMPONENTS`' bodies) in document order, advancing the
53
+ * The mdast equivalent of `scanKeyedJsxFloats`: walk the lesson's flow (and
54
+ * through the `TRANSPARENT_COMPONENTS`' bodies) in document order, advancing the
55
55
  * section counter on every top-level depth-2 heading and emitting a record for
56
56
  * every `mdxJsxFlowElement` named `tagName` — keyed or not, exactly like
57
57
  * `collectDisplayEquations`. `numbering` is threaded the same way as in
@@ -25,6 +25,7 @@
25
25
 
26
26
  import {
27
27
  resolveNumbering,
28
+ stripContainerDirectives,
28
29
  stripFencedCode,
29
30
  stripJsxComponents,
30
31
  TRANSPARENT_COMPONENTS,
@@ -131,10 +132,9 @@ function mdxStringAttribute(node, name) {
131
132
  */
132
133
  export function scanKeyedJsxFloats(markdown, { tagName, numbering }) {
133
134
  const { prefix, offset } = resolveNumbering(numbering);
134
- const text = stripJsxComponents(
135
- stripFencedCode(markdown),
136
- tagName,
137
- TRANSPARENT_COMPONENTS,
135
+ // A `:::note` aside is a callout, like `<Aside>`: nothing in it numbers.
136
+ const text = stripContainerDirectives(
137
+ stripJsxComponents(stripFencedCode(markdown), tagName, TRANSPARENT_COMPONENTS),
138
138
  );
139
139
  const tokens = [];
140
140
  const heading = /^[ \t]{0,3}##[ \t]+\S/gm;
@@ -196,8 +196,8 @@ export function countKeyedJsxFloats(markdown, { tagName }) {
196
196
  }
197
197
 
198
198
  /**
199
- * The mdast equivalent of `scanKeyedJsxFloats`: walk the tree's top-level
200
- * children (and through the `TRANSPARENT_COMPONENTS`' bodies) in document order, advancing the
199
+ * The mdast equivalent of `scanKeyedJsxFloats`: walk the lesson's flow (and
200
+ * through the `TRANSPARENT_COMPONENTS`' bodies) in document order, advancing the
201
201
  * section counter on every top-level depth-2 heading and emitting a record for
202
202
  * every `mdxJsxFlowElement` named `tagName` — keyed or not, exactly like
203
203
  * `collectDisplayEquations`. `numbering` is threaded the same way as in
@@ -210,8 +210,12 @@ export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
210
210
  let indexInSection = 0;
211
211
  let indexInLesson = 0;
212
212
 
213
+ // The lesson's flow — top-level blocks, list items, blockquotes — and the
214
+ // `TRANSPARENT_COMPONENTS`' bodies; never another component's body or a
215
+ // `:::note` aside. `scanKeyedJsxFloats` reads the same set from the text.
213
216
  const collectFromDetail = (node) => {
214
217
  if (!node || typeof node !== 'object') return;
218
+ if (node.type === 'containerDirective') return;
215
219
  if (node.type === 'mdxJsxFlowElement' && node.name === tagName) {
216
220
  indexInSection += 1;
217
221
  indexInLesson += 1;
@@ -256,14 +260,7 @@ export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
256
260
  indexInSection = 0;
257
261
  continue;
258
262
  }
259
- if (node.type === 'mdxJsxFlowElement' && node.name === tagName) {
260
- collectFromDetail(node);
261
- } else if (
262
- node.type === 'mdxJsxFlowElement' &&
263
- TRANSPARENT_COMPONENTS.includes(node.name)
264
- ) {
265
- collectFromDetail(node);
266
- }
263
+ collectFromDetail(node);
267
264
  }
268
265
  return floats;
269
266
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Rule V3's seam, checked in the source: a component a lesson renders draws
3
+ * its math through `renderLabMath`, which binds every symbol to the page's
4
+ * notation, never by calling KaTeX itself — a legend helper around
5
+ * `katex.renderToString` binds none, so its symbols open no card.
6
+ *
7
+ * Starting at each lesson's own imports, this follows every relative import
8
+ * — the course's visuals, wherever they live — and reports each file that
9
+ * imports `katex` as `lab-katex-direct`, with the chain of imports that
10
+ * reaches it. A package import (`explico/…`, `react`) is not followed:
11
+ * explico's own KaTeX use is the seam. A type-only import and the KaTeX
12
+ * stylesheet draw nothing and are not reported.
13
+ *
14
+ * `figure-symbols.ts` checks the same promise on the rendered page, which
15
+ * also catches math a lab draws some other way.
16
+ */
17
+ interface LabKatexDiagnostic {
18
+ readonly code: 'lab-katex-direct';
19
+ readonly severity: 'error';
20
+ readonly message: string;
21
+ /** The file that imports `katex`, repository-relative. */
22
+ readonly file: string;
23
+ /** The first lesson (by id) whose imports reach it. */
24
+ readonly lessonId: string;
25
+ }
26
+ interface LabKatexLessonInput {
27
+ readonly lessonId: string;
28
+ /** Repository-relative path of the lesson file. */
29
+ readonly file: string;
30
+ readonly body: string;
31
+ }
32
+ /**
33
+ * Every module a source file imports at run time, in order: static, re-exported,
34
+ * dynamic and `require`d. A type-only import is left out.
35
+ */
36
+ declare function importSpecifiers(source: string): string[];
37
+ /**
38
+ * Each course file that a lesson's imports reach and that imports `katex`.
39
+ * `root` is the repository root the lesson paths are relative to.
40
+ */
41
+ declare function validateLabKatexImports(lessons: readonly LabKatexLessonInput[], root?: string): LabKatexDiagnostic[];
42
+
43
+ export { type LabKatexDiagnostic, type LabKatexLessonInput, importSpecifiers, validateLabKatexImports };
@@ -0,0 +1,96 @@
1
+ import { readFileSync, statSync } from "node:fs";
2
+ import { dirname, join, relative, sep } from "node:path";
3
+ import { stripFencedCode } from "./equations.mjs";
4
+ const EXTENSIONS = [".astro", ".tsx", ".ts", ".jsx", ".js", ".mjs", ".mts", ".cjs"];
5
+ const SOURCE_FILE = /\.(?:astro|tsx|ts|jsx|js|mjs|mts|cjs|mdx)$/u;
6
+ const IMPORT_PATTERNS = [
7
+ // import x from 'y' · import { x } from 'y' · import 'y' · import type … from 'y'
8
+ /\bimport\s+(type\s+)?(?:[\w*{}\s,$]+?\s+from\s+)?(['"])([^'"\n]+)\2/gu,
9
+ // export { x } from 'y' · export * from 'y' · export type { x } from 'y'
10
+ /\bexport\s+(type\s+)?(?:\*(?:\s+as\s+[\w$]+)?|\{[^}]*\})\s+from\s+(['"])([^'"\n]+)\2/gu,
11
+ // import('y') · require('y')
12
+ /\b(?:import|require)\s*\(\s*()(['"])([^'"\n]+)\2\s*\)/gu
13
+ ];
14
+ function importSpecifiers(source) {
15
+ const found = [];
16
+ for (const pattern of IMPORT_PATTERNS) {
17
+ for (const match of source.matchAll(pattern)) {
18
+ if (match[1]) continue;
19
+ found.push({ index: match.index ?? 0, specifier: match[3] });
20
+ }
21
+ }
22
+ return found.sort((a, b) => a.index - b.index).map(({ specifier }) => specifier);
23
+ }
24
+ function isKatexModule(specifier) {
25
+ if (specifier !== "katex" && !specifier.startsWith("katex/")) return false;
26
+ return !/\.(?:css|woff2?|ttf)$/u.test(specifier);
27
+ }
28
+ function isFile(path) {
29
+ try {
30
+ return statSync(path).isFile();
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+ function resolveRelative(fromFile, specifier) {
36
+ const base = join(dirname(fromFile), specifier);
37
+ const candidates = [
38
+ base,
39
+ // `./chart.js` written for `./chart.ts`, the TypeScript ESM convention.
40
+ .../\.[cm]?js$/u.test(base) ? [".ts", ".tsx", ".mts", ".cts"].map((ext) => base.replace(/\.[cm]?js$/u, ext)) : [],
41
+ ...EXTENSIONS.map((ext) => `${base}${ext}`),
42
+ ...EXTENSIONS.map((ext) => join(base, `index${ext}`))
43
+ ];
44
+ return candidates.find(isFile);
45
+ }
46
+ function validateLabKatexImports(lessons, root = process.cwd()) {
47
+ const read = (path) => {
48
+ try {
49
+ return readFileSync(path, "utf8");
50
+ } catch {
51
+ return void 0;
52
+ }
53
+ };
54
+ const repoPath = (path) => relative(root, path).split(sep).join("/");
55
+ const diagnostics = /* @__PURE__ */ new Map();
56
+ const seen = /* @__PURE__ */ new Set();
57
+ for (const lesson of [...lessons].sort((a, b) => a.lessonId.localeCompare(b.lessonId))) {
58
+ const lessonPath = join(root, lesson.file);
59
+ const queue = [
60
+ { path: lessonPath, source: stripFencedCode(lesson.body), chain: [lesson.file] }
61
+ ];
62
+ while (queue.length > 0) {
63
+ const { path, source, chain } = queue.shift();
64
+ for (const specifier of importSpecifiers(source)) {
65
+ if (isKatexModule(specifier)) {
66
+ const file = repoPath(path);
67
+ if (!diagnostics.has(file)) {
68
+ diagnostics.set(file, {
69
+ code: "lab-katex-direct",
70
+ severity: "error",
71
+ file,
72
+ lessonId: lesson.lessonId,
73
+ message: `${file} imports '${specifier}' and draws math whose symbols open no notation card (reached by ${chain.join(" \u2192 ")}). Render a lab's math with renderLabMath(template, scope) \u2014 { display: false } for a legend entry, an axis title or a label \u2014 so every symbol is bound to the page's notation (rule V3).`
74
+ });
75
+ }
76
+ continue;
77
+ }
78
+ if (!specifier.startsWith(".")) continue;
79
+ const target = resolveRelative(path, specifier);
80
+ if (!target || !SOURCE_FILE.test(target) || target.includes(`${sep}node_modules${sep}`)) {
81
+ continue;
82
+ }
83
+ if (seen.has(target)) continue;
84
+ seen.add(target);
85
+ const text = read(target);
86
+ if (text === void 0) continue;
87
+ queue.push({ path: target, source: text, chain: [...chain, repoPath(target)] });
88
+ }
89
+ }
90
+ }
91
+ return [...diagnostics.values()].sort((a, b) => a.file.localeCompare(b.file));
92
+ }
93
+ export {
94
+ importSpecifiers,
95
+ validateLabKatexImports
96
+ };
@@ -5,6 +5,7 @@ interface LessonNotationFrontmatter {
5
5
  readonly local?: ReadonlyArray<{
6
6
  readonly key: string;
7
7
  readonly latex: string;
8
+ readonly fixedIndex?: boolean;
8
9
  }>;
9
10
  }
10
11
  /**
@@ -1,5 +1,6 @@
1
1
  import { loadManifest } from "../compiler/manifest.js";
2
2
  import {
3
+ bindScopeToPage,
3
4
  registerPageNotationDependencies,
4
5
  routeIdFromLocals
5
6
  } from "./page-notation.js";
@@ -9,7 +10,8 @@ async function labMathScope(notation, body = "", component) {
9
10
  const local = notation?.local ?? [];
10
11
  const scope = local.map((definition) => ({
11
12
  key: definition.key,
12
- notation: definition.latex
13
+ notation: definition.latex,
14
+ ...definition.fixedIndex === true ? { fixedIndex: true } : {}
13
15
  }));
14
16
  const referenced = /* @__PURE__ */ new Set();
15
17
  const explicitlyExplained = /* @__PURE__ */ new Set();
@@ -32,7 +34,11 @@ async function labMathScope(notation, body = "", component) {
32
34
  }
33
35
  const shared = sharedByKey.get(key);
34
36
  if (shared) {
35
- scope.push({ key: shared.key, notation: shared.notation });
37
+ scope.push({
38
+ key: shared.key,
39
+ notation: shared.notation,
40
+ ...shared.fixedIndex ? { fixedIndex: true } : {}
41
+ });
36
42
  resolvedKeys.add(key);
37
43
  continue;
38
44
  }
@@ -48,6 +54,11 @@ async function labMathScope(notation, body = "", component) {
48
54
  resolvedKeys,
49
55
  component.context
50
56
  );
57
+ bindScopeToPage(scope, {
58
+ locals: component.locals,
59
+ context: component.context,
60
+ checkIndices: !manifest.rules.disabled.includes("N23")
61
+ });
51
62
  }
52
63
  return scope;
53
64
  }
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  const IGNORE_PATH = join(process.cwd(), "lint-ignore.yml");
4
- const LINE = /^-\s*([a-z0-9-]+)(?::([^\s—-][^—-]*?))?\s*(?:—|--|\s-\s)\s*(.+?)\s*$/;
4
+ const LINE = /^-\s*([a-z0-9-]+)(?::([^\s—][^—]*?))?\s*(?:—|--|\s-\s)\s*(.+?)\s*$/;
5
5
  function parseLintIgnore(text) {
6
6
  const entries = [];
7
7
  for (const rawLine of text.split("\n")) {
@@ -39,6 +39,64 @@ export function operatorDisplayLatex(name: string): string;
39
39
  * @returns {string | undefined}
40
40
  */
41
41
  export function decorationOfEntry(latex: string): string | undefined;
42
+ /** True for a letter that is a running index wherever it is a whole slot. */
43
+ export function isRunningIndexLetter(text: any): boolean;
44
+ /**
45
+ * The key of a derived entry: a base entry composed with an index entry
46
+ * (`\mathbf{v}_0` from `velocity-vector` and `{}_0`). The build materializes
47
+ * it from the two (`derived-notation.mjs`); it is never authored, and an
48
+ * authored key that equals one fails the build.
49
+ *
50
+ * @param {string} baseKey
51
+ * @param {string} indexKey
52
+ * @returns {string}
53
+ */
54
+ export function derivedNotationKey(baseKey: string, indexKey: string): string;
55
+ /**
56
+ * The subscript an **index entry** explains — an empty group with a subscript:
57
+ * `{}_0`, `{}_x`, `{}_{\mathrm{max}}` — as the source after `{}`, else
58
+ * `undefined`. Such an entry binds no symbol of its own; composed with the
59
+ * entry of the symbol it indexes, it makes a derived card (rule N23).
60
+ *
61
+ * @param {string} latex
62
+ * @returns {string | undefined}
63
+ */
64
+ export function indexOfEntry(latex: string): string | undefined;
65
+ /**
66
+ * The symbol a base entry gives a derived card: its whole `latex`, or the
67
+ * glyph it lends to — the head of a function form (`v` of `v(t)`), the base
68
+ * of a superscript form (`j` of `j^{(m)}`).
69
+ *
70
+ * @param {string} notation
71
+ * @returns {string}
72
+ */
73
+ export function baseSymbolLatex(notation: string): string;
74
+ /**
75
+ * The symbol of a derived card: the base's symbol with the index entry's
76
+ * subscript — `\mathbf{v}` and `{}_0` make `\mathbf{v}_0`.
77
+ *
78
+ * @param {string} baseNotation
79
+ * @param {string} indexNotation
80
+ * @returns {string}
81
+ */
82
+ export function derivedNotationLatex(baseNotation: string, indexNotation: string): string;
83
+ /**
84
+ * How an index entry is shown on its own — over a placeholder square, as an
85
+ * operator entry is (`\square_0`). Display only.
86
+ *
87
+ * @param {string} latex
88
+ * @returns {string}
89
+ */
90
+ export function indexDisplayLatex(latex: string): string;
91
+ /**
92
+ * Whether an entry's `latex` has a subscript on a symbol anywhere — what an
93
+ * `\explain{key}{X_i}` wrap needs from `key` before its card can explain the
94
+ * index (rule N23, `index-swallowed`).
95
+ *
96
+ * @param {string} latex
97
+ * @returns {boolean}
98
+ */
99
+ export function entryHasIndex(latex: string): boolean;
42
100
  /**
43
101
  * Resolve and annotate one KaTeX expression against a page's flat glyph table.
44
102
  * Source offsets in the result always refer to the original, unmodified LaTeX.
@@ -51,15 +109,28 @@ export function decorationOfEntry(latex: string): string | undefined;
51
109
  * decorated form no whole form covers is reported in `unresolved`, marked
52
110
  * `suspended`.
53
111
  *
112
+ * A symbol with a subscript (rule N23) takes its exact whole form, else the
113
+ * family whose index has its shape, else — with `options.derive`, which page
114
+ * math sets (a lesson body, its assessments, labs and diagrams) — a derived
115
+ * card from an index entry (`{}_0`) and the base's entry; each such card
116
+ * wraps the whole indexed symbol. `derived`
117
+ * lists the derived cards used, as `{ key, base, index }`, for the build to
118
+ * materialize; `indices` says how each indexed symbol is explained (see
119
+ * `classifyIndexed`). A definition may carry `fixedIndex: true`: its index
120
+ * (or, on a letter, the letter as an index) is a fixed label, not a running
121
+ * one, so it is no family.
122
+ *
54
123
  * @param {string} latex
55
- * @param {Array<{key: string, notation: string}>} definitions
56
- * @param {{ suspended?: readonly string[] }} [options]
124
+ * @param {Array<{key: string, notation: string, fixedIndex?: boolean}>} definitions
125
+ * @param {{ suspended?: readonly string[], derive?: boolean }} [options]
57
126
  */
58
127
  export function resolveMathGlyphs(latex: string, definitions: Array<{
59
128
  key: string;
60
129
  notation: string;
130
+ fixedIndex?: boolean;
61
131
  }>, options?: {
62
132
  suspended?: readonly string[];
133
+ derive?: boolean;
63
134
  }): {
64
135
  latex: string;
65
136
  bindings: {
@@ -72,11 +143,32 @@ export function resolveMathGlyphs(latex: string, definitions: Array<{
72
143
  decorations: {
73
144
  token: string;
74
145
  wholeForm: any;
146
+ wholeFormExplicit: boolean;
75
147
  operator: any;
76
148
  start: any;
77
149
  end: any;
78
150
  name: any;
79
151
  }[];
152
+ indices: {
153
+ token: string;
154
+ start: any;
155
+ end: any;
156
+ base: any;
157
+ index: any;
158
+ how: any;
159
+ key: any;
160
+ explained: boolean;
161
+ baseExplained: boolean;
162
+ unexplained: any[];
163
+ running: {
164
+ letter: any;
165
+ start: any;
166
+ end: any;
167
+ key: any;
168
+ cover: any;
169
+ }[];
170
+ }[];
171
+ derived: any[];
80
172
  };
81
173
  /**
82
174
  * Every named operator in one KaTeX expression: a built-in `op` node