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
@@ -1,5 +1,5 @@
1
1
  import { ScopeEntry } from './gate-math.js';
2
- import './types.js';
2
+ import { DerivedUse } from './types.js';
3
3
 
4
4
  type ContentSegment = {
5
5
  readonly type: 'text';
@@ -10,8 +10,13 @@ type ContentSegment = {
10
10
  };
11
11
  /**
12
12
  * Split `text` into ordered segments, resolving each math span's glyphs
13
- * against `scope`. Never throws.
13
+ * against `scope`. Never throws. With `derive` (an assessment, which is page
14
+ * math), an indexed symbol takes its derived card (rule N23), and each one
15
+ * used is added to `derived`.
14
16
  */
15
- declare function splitMixedContent(text: string, scope: readonly ScopeEntry[]): ContentSegment[];
17
+ declare function splitMixedContent(text: string, scope: readonly ScopeEntry[], options?: {
18
+ readonly derive?: boolean;
19
+ readonly derived?: Map<string, DerivedUse>;
20
+ }): ContentSegment[];
16
21
 
17
22
  export { type ContentSegment, splitMixedContent };
@@ -1,6 +1,6 @@
1
1
  import { resolveMathGlyphs } from "./math-glyphs.mjs";
2
2
  const MATH_SPAN = /\$\$([\s\S]+?)\$\$|(?<![\\$])\$(?!\s)([^$\n]+?)(?<![\\\s])\$(?!\d)/g;
3
- function splitMixedContent(text, scope) {
3
+ function splitMixedContent(text, scope, options = {}) {
4
4
  const segments = [];
5
5
  let cursor = 0;
6
6
  const pattern = new RegExp(MATH_SPAN);
@@ -12,7 +12,9 @@ function splitMixedContent(text, scope) {
12
12
  const raw = match[1] ?? match[2] ?? "";
13
13
  let latex = raw;
14
14
  try {
15
- latex = resolveMathGlyphs(raw, [...scope]).latex;
15
+ const result = resolveMathGlyphs(raw, [...scope], { derive: options.derive === true });
16
+ latex = result.latex;
17
+ for (const use of result.derived) options.derived?.set(use.key, use);
16
18
  } catch {
17
19
  latex = raw;
18
20
  }
@@ -1,11 +1,12 @@
1
- import { Manifest, ManifestLessonNotation } from '../compiler/manifest.js';
1
+ import { Manifest, ManifestDerivedNotation, ManifestLessonNotation } from '../compiler/manifest.js';
2
+ import { DerivedUse } from './types.js';
2
3
  import '../curriculum/validation.js';
3
4
  import './consistency.js';
4
- import '../course-config-Dsnri9Tk.js';
5
+ import '../course-config-rRtME5UV.js';
5
6
  import '../rules/catalog.js';
6
7
  import '../rules/course-rules.js';
7
8
  import '../compiler/collections.js';
8
- import './types.js';
9
+ import './gate-retired-keys.js';
9
10
  import './lint-ignore.js';
10
11
  import './equations-validate.js';
11
12
  import './tables-validate.js';
@@ -16,6 +17,7 @@ import './examples-validate.js';
16
17
  import './folds-validate.js';
17
18
  import './knowledge-checks-validate.js';
18
19
  import './raw-floats-validate.js';
20
+ import './lab-katex-validate.js';
19
21
  import './gate-math.js';
20
22
  import './mixed-content.js';
21
23
  import './gloss.js';
@@ -34,6 +36,27 @@ interface PageNotationDependency {
34
36
  * between lessons and defeat page-local notation scope.
35
37
  */
36
38
  declare function registerPageNotationDependencies(locals: object, keys: Iterable<string>, context: string): void;
39
+ /**
40
+ * Record the derived cards (rule N23) a nested server component's math uses —
41
+ * a lab's or a diagram's — so the page ships them with its own, and their
42
+ * base and index entries, which a derived card's formula opens. Keyed by
43
+ * `Astro.locals`, like {@link registerPageNotationDependencies}.
44
+ */
45
+ declare function registerPageDerivedNotation(locals: object, uses: Iterable<DerivedUse>, context: string): void;
46
+ /** The page a glyph table was built for, and whether rule N23 is on there. */
47
+ interface ScopePage {
48
+ readonly locals: object;
49
+ readonly context: string;
50
+ readonly checkIndices: boolean;
51
+ }
52
+ /**
53
+ * Tie a lab's glyph table to the page it renders on (`labMathScope` does), so
54
+ * `renderLabMath` can give its indexed symbols derived cards and register
55
+ * them, and knows whether to hold them to rule N23. A table a course copies
56
+ * loses the tie: its math then composes no derived card.
57
+ */
58
+ declare function bindScopeToPage(scope: object, page: ScopePage): void;
59
+ declare function pageOfScope(scope: object): ScopePage | undefined;
37
60
  declare function pageNotationDependencies(locals: object): PageNotationDependency[];
38
61
  /** Starlight's route id as exposed to nested Astro components. */
39
62
  declare function routeIdFromLocals(locals: object): string | undefined;
@@ -44,14 +67,23 @@ declare function notationKeysInMathNodes(nodes: readonly {
44
67
  readonly children?: readonly unknown[];
45
68
  }[]): string[];
46
69
  /**
47
- * Extend one compiled lesson bundle with only the definitions requested by
48
- * components rendered on that page (plus those definitions' normal closure).
70
+ * The derived cards (rule N23) a page uses: the ones its body and assessment
71
+ * math use, as the manifest composed them, and the ones a lab or a diagram on
72
+ * it registered while rendering (`locals`), composed here the same way. They
73
+ * travel with the page as hidden sources; their base and index entries are
74
+ * in its payload already.
75
+ */
76
+ declare function derivedNotationForPage(manifest: Manifest, pageId: string, locals?: object): readonly ManifestDerivedNotation[];
77
+ /**
78
+ * Extend one compiled page bundle — a lesson's, or a non-chapter page's
79
+ * (rule S10) — with only the definitions requested by components rendered on
80
+ * that page (plus those definitions' normal closure).
49
81
  */
50
- declare function notationDefinitionsForRenderedPage(manifest: Manifest, lessonId: string, dependencies: readonly PageNotationDependency[]): ManifestLessonNotation[];
82
+ declare function notationDefinitionsForRenderedPage(manifest: Manifest, pageId: string, dependencies: readonly PageNotationDependency[]): ManifestLessonNotation[];
51
83
  /**
52
84
  * Structural backstop over final HTML: every trigger must have a source entry
53
85
  * from which the popup enhancer can populate a card.
54
86
  */
55
87
  declare function missingNotationPayloadKeys(html: string): string[];
56
88
 
57
- export { type PageNotationDependency, missingNotationPayloadKeys, notationDefinitionsForRenderedPage, notationKeysInMathNodes, pageNotationDependencies, registerPageNotationDependencies, routeIdFromLocals };
89
+ export { type PageNotationDependency, type ScopePage, bindScopeToPage, derivedNotationForPage, missingNotationPayloadKeys, notationDefinitionsForRenderedPage, notationKeysInMathNodes, pageNotationDependencies, pageOfScope, registerPageDerivedNotation, registerPageNotationDependencies, routeIdFromLocals };
@@ -3,6 +3,7 @@ import {
3
3
  formulaGlyphScope
4
4
  } from "./gloss.js";
5
5
  import { resolveReferenceId } from "./references.js";
6
+ import { derivedNotationEntries } from "./derived-entries.js";
6
7
  const dependenciesByLocals = /* @__PURE__ */ new WeakMap();
7
8
  function registerPageNotationDependencies(locals, keys, context) {
8
9
  let byKey = dependenciesByLocals.get(locals);
@@ -19,6 +20,27 @@ function registerPageNotationDependencies(locals, keys, context) {
19
20
  contexts.add(context);
20
21
  }
21
22
  }
23
+ const derivedByLocals = /* @__PURE__ */ new WeakMap();
24
+ function registerPageDerivedNotation(locals, uses, context) {
25
+ let byKey = derivedByLocals.get(locals);
26
+ if (!byKey) {
27
+ byKey = /* @__PURE__ */ new Map();
28
+ derivedByLocals.set(locals, byKey);
29
+ }
30
+ const entries = [];
31
+ for (const use of uses) {
32
+ byKey.set(use.key, use);
33
+ entries.push(use.base, use.index);
34
+ }
35
+ registerPageNotationDependencies(locals, entries, context);
36
+ }
37
+ const pageByScope = /* @__PURE__ */ new WeakMap();
38
+ function bindScopeToPage(scope, page) {
39
+ pageByScope.set(scope, page);
40
+ }
41
+ function pageOfScope(scope) {
42
+ return pageByScope.get(scope);
43
+ }
22
44
  function pageNotationDependencies(locals) {
23
45
  const byKey = dependenciesByLocals.get(locals);
24
46
  if (!byKey) return [];
@@ -52,7 +74,6 @@ function manifestDefinition(definition, cardsByKey) {
52
74
  notation: definition.notation,
53
75
  label: definition.label,
54
76
  summary: definition.summary,
55
- editorialStatus: definition.status,
56
77
  seeAlso: [...definition.seeAlso],
57
78
  glosses: [...definition.glosses],
58
79
  formulaScope: formulaGlyphScope({
@@ -63,17 +84,44 @@ function manifestDefinition(definition, cardsByKey) {
63
84
  ...definition.formula === void 0 ? {} : { formula: definition.formula }
64
85
  }),
65
86
  ...definition.units === void 0 ? {} : { units: definition.units },
66
- ...definition.formula === void 0 ? {} : { formula: definition.formula }
87
+ ...definition.formula === void 0 ? {} : { formula: definition.formula },
88
+ ...definition.range === void 0 ? {} : { range: definition.range }
67
89
  };
68
90
  }
69
- function notationDefinitionsForRenderedPage(manifest, lessonId, dependencies) {
70
- const lesson = manifest.lessons.find((entry) => entry.id === lessonId);
71
- if (!lesson) return [];
91
+ function derivedNotationForPage(manifest, pageId, locals) {
92
+ const page = manifest.lessons.find((entry) => entry.id === pageId) ?? manifest.pages.find((entry) => entry.id === pageId);
93
+ const compiled = page?.notation.derived ?? [];
94
+ const registered = [...(locals && derivedByLocals.get(locals)?.values()) ?? []].filter(
95
+ (use) => !compiled.some((entry) => entry.key === use.key)
96
+ );
97
+ if (registered.length === 0) return compiled;
98
+ const definitionsById = new Map(
99
+ manifest.notation.definitions.map((definition) => [definition.id, definition])
100
+ );
101
+ const availableIds = new Set(definitionsById.keys());
102
+ const records = /* @__PURE__ */ new Map();
103
+ for (const use of registered) {
104
+ for (const key of [use.base, use.index]) {
105
+ const id = resolveReferenceId(key, { kind: "page", lessonId: pageId }, availableIds);
106
+ const record = id === void 0 ? void 0 : definitionsById.get(id);
107
+ if (record) records.set(key, record);
108
+ }
109
+ }
110
+ const cardsByKey = new Map(
111
+ manifest.notation.definitions.filter((definition) => definition.kind === "shared").map((definition) => [definition.key, { key: definition.key, notation: definition.notation }])
112
+ );
113
+ return [...compiled, ...derivedNotationEntries(registered, records, cardsByKey)].sort(
114
+ (left, right) => left.key.localeCompare(right.key)
115
+ );
116
+ }
117
+ function notationDefinitionsForRenderedPage(manifest, pageId, dependencies) {
118
+ const page = manifest.lessons.find((entry) => entry.id === pageId) ?? manifest.pages.find((entry) => entry.id === pageId);
119
+ if (!page) return [];
72
120
  const definitionsById = new Map(
73
121
  manifest.notation.definitions.map((definition) => [definition.id, definition])
74
122
  );
75
123
  const availableIds = new Set(definitionsById.keys());
76
- const ids = new Set(lesson.notation.definitionIds);
124
+ const ids = new Set(page.notation.definitionIds);
77
125
  const contextsByKey = /* @__PURE__ */ new Map();
78
126
  for (const dependency of dependencies) {
79
127
  let contexts = contextsByKey.get(dependency.key);
@@ -84,12 +132,12 @@ function notationDefinitionsForRenderedPage(manifest, lessonId, dependencies) {
84
132
  contexts.add(dependency.context);
85
133
  const definitionId = resolveReferenceId(
86
134
  dependency.key,
87
- { kind: "page", lessonId },
135
+ { kind: "page", lessonId: pageId },
88
136
  availableIds
89
137
  );
90
138
  if (!definitionId) {
91
139
  throw new Error(
92
- `Notation payload error in lesson "${lessonId}": ${dependency.context} emitted data-notation-key="${dependency.key}", but that key is not defined in this lesson or the shared notation registry.`
140
+ `Notation payload error on page "${pageId}": ${dependency.context} emitted data-notation-key="${dependency.key}", but that key is not defined on this page or in the shared notation registry.`
93
141
  );
94
142
  }
95
143
  ids.add(definitionId);
@@ -124,7 +172,7 @@ function notationDefinitionsForRenderedPage(manifest, lessonId, dependencies) {
124
172
  if (previous && previous !== id) {
125
173
  const context = [...contextsByKey.get(definition.key) ?? []].join(", ");
126
174
  throw new Error(
127
- `Notation payload error in lesson "${lessonId}": key "${definition.key}" would have two meanings (${previous} and ${id})` + (context ? ` after ${context} emitted it.` : ".")
175
+ `Notation payload error on page "${pageId}": key "${definition.key}" would have two meanings (${previous} and ${id})` + (context ? ` after ${context} emitted it.` : ".")
128
176
  );
129
177
  }
130
178
  definitionIdByKey.set(definition.key, id);
@@ -138,7 +186,7 @@ function notationDefinitionsForRenderedPage(manifest, lessonId, dependencies) {
138
186
  });
139
187
  }
140
188
  const existingById = new Map(
141
- lesson.notation.definitions.map((definition) => [definition.id, definition])
189
+ page.notation.definitions.map((definition) => [definition.id, definition])
142
190
  );
143
191
  return [...ids].sort().flatMap((id) => {
144
192
  const existing = existingById.get(id);
@@ -162,10 +210,14 @@ function missingNotationPayloadKeys(html) {
162
210
  return [...triggers].filter((key) => !entries.has(key)).sort();
163
211
  }
164
212
  export {
213
+ bindScopeToPage,
214
+ derivedNotationForPage,
165
215
  missingNotationPayloadKeys,
166
216
  notationDefinitionsForRenderedPage,
167
217
  notationKeysInMathNodes,
168
218
  pageNotationDependencies,
219
+ pageOfScope,
220
+ registerPageDerivedNotation,
169
221
  registerPageNotationDependencies,
170
222
  routeIdFromLocals
171
223
  };
@@ -12,11 +12,13 @@
12
12
  * `keyed-jsx-floats.mjs`'s top-level-only scan entirely, so it never even
13
13
  * reaches diagram numbering.
14
14
  *
15
- * A raw table nested in `<Table>`/`<Figure>` is the intended, wrapped case
16
- * and is not flagged; nesting inside anything else (`<CompactExample>`,
17
- * `<Aside>`, `<Memo>`, …) still is, because a raw table has no numbering
18
- * scheme of its own to opt into the way a `<Diagram>`'s top-level/`<Memo>`-
19
- * transparent scan does.
15
+ * A raw table counts only where wrapping it would number it: at the top level
16
+ * of the lesson body or in a `TRANSPARENT_COMPONENTS` body (`<Memo>`,
17
+ * `<ShortNote>`, `<Fold>`), exactly where `keyed-jsx-floats.mjs` numbers a
18
+ * `<Table>`. A raw table in `<Table>`/`<Figure>` is the intended, wrapped
19
+ * case; one in any other component (a worked example's `<CompactExample>`,
20
+ * an `<Aside>`) is illustrative, like an equation there, and a `<Table>`
21
+ * there would get no number — so neither is flagged.
20
22
  *
21
23
  * `scanRawFloats` (raw Markdown, for the manifest builder) and
22
24
  * `collectRawFloats` (mdast, for `remark-notation.mjs`) MUST agree on counts
@@ -24,7 +26,11 @@
24
26
  * `keyed-jsx-floats.mjs` holds `scanKeyedJsxFloats`/`collectKeyedJsxFloats` to.
25
27
  */
26
28
 
27
- import { stripFencedCode } from './equations.mjs';
29
+ import {
30
+ stripFencedCode,
31
+ stripJsxComponents,
32
+ TRANSPARENT_COMPONENTS,
33
+ } from './equations.mjs';
28
34
  import { collectKeyedJsxFloats, scanKeyedJsxFloats } from './keyed-jsx-floats.mjs';
29
35
 
30
36
  /** Blank `<Table>…</Table>` and `<Figure>…</Figure>` bodies — legitimately wrapped, not raw. */
@@ -67,9 +73,16 @@ function findDiagramTags(text) {
67
73
  export function scanRawFloats(markdown) {
68
74
  const codeStripped = stripFencedCode(String(markdown));
69
75
  const withoutWrappedFloats = blankFloatWrappers(codeStripped);
76
+ // Every component body but a transparent one is blanked — `<Table>` and
77
+ // `<Figure>` among them — so only the tables a `<Table>` would number remain.
78
+ const numberableText = stripJsxComponents(
79
+ codeStripped,
80
+ undefined,
81
+ TRANSPARENT_COMPONENTS,
82
+ );
70
83
 
71
- const markdownTableCount = findMarkdownTableStarts(withoutWrappedFloats).length;
72
- const htmlTableCount = findHtmlTableTags(withoutWrappedFloats).length;
84
+ const markdownTableCount = findMarkdownTableStarts(numberableText).length;
85
+ const htmlTableCount = findHtmlTableTags(numberableText).length;
73
86
 
74
87
  const diagramsOutsideWrappers = findDiagramTags(withoutWrappedFloats).length;
75
88
  const visibleDiagrams = scanKeyedJsxFloats(markdown, { tagName: 'Diagram' });
@@ -95,7 +108,10 @@ export function collectRawFloats(tree) {
95
108
  const htmlTableNodes = [];
96
109
  const allDiagramNodes = [];
97
110
 
98
- const walk = (node) => {
111
+ // `numberable`: a `<Table>` here would be numbered — the top level, or a
112
+ // transparent component's body. Any other MDX component (a capitalized
113
+ // name, the ones `stripJsxComponents` blanks) ends it for its subtree.
114
+ const walk = (node, numberable) => {
99
115
  if (!node || typeof node !== 'object') return;
100
116
  if (
101
117
  node.type === 'mdxJsxFlowElement' &&
@@ -103,18 +119,26 @@ export function collectRawFloats(tree) {
103
119
  ) {
104
120
  return; // legitimately wrapped — don't descend
105
121
  }
106
- if (node.type === 'table') markdownTableNodes.push(node);
107
- if (node.type === 'html' && /<table\b/i.test(String(node.value ?? ''))) {
122
+ if (numberable && node.type === 'table') markdownTableNodes.push(node);
123
+ if (
124
+ numberable &&
125
+ node.type === 'html' &&
126
+ /<table\b/i.test(String(node.value ?? ''))
127
+ ) {
108
128
  htmlTableNodes.push(node);
109
129
  }
110
130
  if (node.type === 'mdxJsxFlowElement' && node.name === 'Diagram') {
111
131
  allDiagramNodes.push(node);
112
132
  }
133
+ const component =
134
+ (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') &&
135
+ /^[A-Z]/.test(String(node.name ?? '')) &&
136
+ !TRANSPARENT_COMPONENTS.includes(node.name);
113
137
  if (Array.isArray(node.children)) {
114
- for (const child of node.children) walk(child);
138
+ for (const child of node.children) walk(child, numberable && !component);
115
139
  }
116
140
  };
117
- walk(tree);
141
+ walk(tree, true);
118
142
 
119
143
  const visibleDiagrams = collectKeyedJsxFloats(tree, { tagName: 'Diagram' });
120
144
  const visibleNodes = new Set(visibleDiagrams.map((diagram) => diagram.node));
@@ -2,6 +2,16 @@ import { NotationDiagnostic, SourceSpan, NotationDefinitionScope } from './types
2
2
 
3
3
  declare const NOTATION_KEY_PATTERN: RegExp;
4
4
  declare function isNotationKey(value: string): boolean;
5
+ /**
6
+ * The `[[…]]` prefixes that name something other than a notation entry: an
7
+ * equation, a table, a figure, a diagram, a worked-example set, a knowledge
8
+ * check, a section, a memo. `remark-notation.mjs` and `remark-memo.mjs` route
9
+ * a reference by its prefix before notation keys are looked at, so a key
10
+ * that starts with one could never be reached.
11
+ */
12
+ declare const RESERVED_KEY_PREFIXES: readonly ["eq-", "tab-", "fig-", "dia-", "ex-", "check-", "sec-", "memo-"];
13
+ /** The reserved prefix `key` starts with, if any. */
14
+ declare function reservedKeyPrefix(key: string): string | undefined;
5
15
  declare function sharedDefinitionId(key: string): string;
6
16
  declare function localDefinitionId(lessonId: string, key: string): string;
7
17
  declare function definitionId(scope: NotationDefinitionScope, key: string): string;
@@ -14,4 +24,4 @@ declare function resolveReferenceId(key: string, scope: NotationDefinitionScope,
14
24
  declare function compareSourceSpans(left: SourceSpan | undefined, right: SourceSpan | undefined): number;
15
25
  declare function compareDiagnostics(left: NotationDiagnostic, right: NotationDiagnostic): number;
16
26
 
17
- export { NOTATION_KEY_PATTERN, compareDiagnostics, compareSourceSpans, definitionId, isNotationKey, localDefinitionId, resolveReferenceId, sharedDefinitionId };
27
+ export { NOTATION_KEY_PATTERN, RESERVED_KEY_PREFIXES, compareDiagnostics, compareSourceSpans, definitionId, isNotationKey, localDefinitionId, reservedKeyPrefix, resolveReferenceId, sharedDefinitionId };
@@ -2,6 +2,10 @@ const NOTATION_KEY_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
2
2
  function isNotationKey(value) {
3
3
  return NOTATION_KEY_PATTERN.test(value);
4
4
  }
5
+ const RESERVED_KEY_PREFIXES = ["eq-", "tab-", "fig-", "dia-", "ex-", "check-", "sec-", "memo-"];
6
+ function reservedKeyPrefix(key) {
7
+ return RESERVED_KEY_PREFIXES.find((prefix) => key.startsWith(prefix));
8
+ }
5
9
  function sharedDefinitionId(key) {
6
10
  return `shared:${key}`;
7
11
  }
@@ -33,11 +37,13 @@ function severityRank(severity) {
33
37
  }
34
38
  export {
35
39
  NOTATION_KEY_PATTERN,
40
+ RESERVED_KEY_PREFIXES,
36
41
  compareDiagnostics,
37
42
  compareSourceSpans,
38
43
  definitionId,
39
44
  isNotationKey,
40
45
  localDefinitionId,
46
+ reservedKeyPrefix,
41
47
  resolveReferenceId,
42
48
  sharedDefinitionId
43
49
  };
@@ -3,9 +3,12 @@ import {
3
3
  compareSourceSpans,
4
4
  isNotationKey,
5
5
  localDefinitionId,
6
+ RESERVED_KEY_PREFIXES,
7
+ reservedKeyPrefix,
6
8
  resolveReferenceId,
7
9
  sharedDefinitionId
8
10
  } from "./references.js";
11
+ import { lineOf, proseOnly } from "./gate-prose-notation.js";
9
12
  function buildNotationRegistry(rawInput) {
10
13
  const diagnostics = [];
11
14
  const lessons = buildLessonIndex(rawInput.lessons, diagnostics);
@@ -105,7 +108,23 @@ function buildNotationRegistry(rawInput) {
105
108
  )
106
109
  );
107
110
  }
108
- detectCycles(definitions, edgeIndex, diagnostics);
111
+ const dependencyEdges = /* @__PURE__ */ new Map();
112
+ for (const candidate of definitions.values()) {
113
+ dependencyEdges.set(
114
+ candidate.id,
115
+ uniqueSorted(
116
+ candidate.resolvedReferences.filter((reference) => reference.kind !== "formula").map((reference) => reference.definitionId).filter((definitionId) => definitionId !== candidate.id)
117
+ )
118
+ );
119
+ }
120
+ detectCycles(definitions, dependencyEdges, diagnostics);
121
+ const localIdsByLesson = /* @__PURE__ */ new Map();
122
+ for (const candidate of definitions.values()) {
123
+ if (candidate.kind !== "local") continue;
124
+ const ids = localIdsByLesson.get(candidate.lesson.lessonId) ?? [];
125
+ ids.push(candidate.id);
126
+ localIdsByLesson.set(candidate.lesson.lessonId, ids);
127
+ }
109
128
  const bundles = [];
110
129
  const backlinks = [];
111
130
  const reachableDefinitions = /* @__PURE__ */ new Set();
@@ -114,7 +133,10 @@ function buildNotationRegistry(rawInput) {
114
133
  const directIds = new Set(
115
134
  references.map((reference) => reference.definitionId)
116
135
  );
117
- const definitionIds = transitiveClosure(directIds, edgeIndex);
136
+ const definitionIds = transitiveClosure(
137
+ /* @__PURE__ */ new Set([...directIds, ...localIdsByLesson.get(lesson.lessonId) ?? []]),
138
+ edgeIndex
139
+ );
118
140
  for (const id of definitionIds) reachableDefinitions.add(id);
119
141
  const bindingByKey = /* @__PURE__ */ new Map();
120
142
  for (const reference of references) {
@@ -122,7 +144,6 @@ function buildNotationRegistry(rawInput) {
122
144
  }
123
145
  bundles.push({
124
146
  lessonId: lesson.lessonId,
125
- status: lesson.status,
126
147
  source: lesson.source,
127
148
  bindings: [...bindingByKey].map(([key, definitionId]) => ({ key, definitionId })).sort(
128
149
  (left, right) => left.key.localeCompare(right.key) || left.definitionId.localeCompare(right.definitionId)
@@ -140,6 +161,21 @@ function buildNotationRegistry(rawInput) {
140
161
  });
141
162
  }
142
163
  }
164
+ for (const candidate of definitions.values()) {
165
+ if (candidate.kind !== "local") continue;
166
+ const shared = definitions.get(sharedDefinitionId(candidate.input.key));
167
+ if (shared === void 0) continue;
168
+ diagnostics.push({
169
+ code: "local-shadows-shared",
170
+ severity: "warning",
171
+ message: `${candidate.lesson.lessonId} defines notation.local ${JSON.stringify(candidate.input.key)}, which is also a shared entry (${shared.input.source.file}): on this page [[${candidate.input.key}]] means "${candidate.input.label}", everywhere else "${shared.input.label}". Give the local entry a key of its own, or use the shared one.`,
172
+ key: candidate.input.key,
173
+ lessonId: candidate.lesson.lessonId,
174
+ definitionId: candidate.id,
175
+ source: candidate.input.source,
176
+ relatedSources: [shared.input.source]
177
+ });
178
+ }
143
179
  for (const candidate of definitions.values()) {
144
180
  if (!reachableDefinitions.has(candidate.id)) {
145
181
  diagnostics.push({
@@ -193,6 +229,7 @@ function buildLessonIndex(rawLessons, diagnostics) {
193
229
  );
194
230
  continue;
195
231
  }
232
+ diagnostics.push(...malformedReferences(lesson));
196
233
  const previous = lessons.get(lesson.lessonId);
197
234
  if (previous !== void 0) {
198
235
  diagnostics.push(
@@ -241,7 +278,21 @@ function buildDefinitionIndex(rawShared, lessons, diagnostics) {
241
278
  diagnostics.push(
242
279
  diagnostic({
243
280
  code: "invalid-key",
244
- message: `${candidate.id} has invalid notation key ${JSON.stringify(candidate.input.key)}`,
281
+ message: `${candidate.id} has invalid notation key ${JSON.stringify(candidate.input.key)}${keyHint(candidate.input.key)}`,
282
+ key: candidate.input.key,
283
+ lessonId: candidate.kind === "local" ? candidate.lesson.lessonId : void 0,
284
+ definitionId: candidate.id,
285
+ source: candidate.input.source
286
+ })
287
+ );
288
+ continue;
289
+ }
290
+ const reserved = reservedKeyPrefix(candidate.input.key);
291
+ if (reserved !== void 0) {
292
+ diagnostics.push(
293
+ diagnostic({
294
+ code: "invalid-key",
295
+ message: `${candidate.id} has notation key ${JSON.stringify(candidate.input.key)}, which starts with the reserved \`${reserved}\`: [[${candidate.input.key}]] is read as a reference to that kind of float and never reaches this entry. Rename the key; none may start with ${RESERVED_KEY_PREFIXES.map((prefix) => `\`${prefix}\``).join(", ")}.`,
245
296
  key: candidate.input.key,
246
297
  lessonId: candidate.kind === "local" ? candidate.lesson.lessonId : void 0,
247
298
  definitionId: candidate.id,
@@ -324,7 +375,7 @@ function validateReferenceKey(reference, owner, diagnostics) {
324
375
  diagnostics.push(
325
376
  diagnostic({
326
377
  code: "invalid-key",
327
- message: `${owner.id} references invalid notation key ${JSON.stringify(reference.key)}`,
378
+ message: `${owner.id} references invalid notation key ${JSON.stringify(reference.key)}${keyHint(reference.key)}`,
328
379
  key: reference.key,
329
380
  lessonId: owner.kind === "local" ? owner.lesson.lessonId : void 0,
330
381
  definitionId: owner.id,
@@ -338,7 +389,7 @@ function validatePageReferenceKey(reference, lesson, diagnostics) {
338
389
  diagnostics.push(
339
390
  diagnostic({
340
391
  code: "invalid-key",
341
- message: `${lesson.lessonId} references invalid notation key ${JSON.stringify(reference.key)}`,
392
+ message: `${lesson.lessonId} references invalid notation key ${JSON.stringify(reference.key)}${keyHint(reference.key)}`,
342
393
  key: reference.key,
343
394
  lessonId: lesson.lessonId,
344
395
  source: reference.source
@@ -389,14 +440,14 @@ function toDefinitionRecord(candidate) {
389
440
  sources: input2.sources,
390
441
  seeAlso: input2.seeAlso,
391
442
  alignment: input2.alignment,
392
- status: input2.status,
393
443
  aiAssisted: input2.aiAssisted,
394
444
  body: input2.body,
395
445
  glosses: input2.glosses,
396
446
  source: input2.source,
397
447
  resolvedReferences: candidate.resolvedReferences,
398
448
  ...input2.formula === void 0 ? {} : { formula: input2.formula },
399
- ...input2.units === void 0 ? {} : { units: input2.units }
449
+ ...input2.units === void 0 ? {} : { units: input2.units },
450
+ ...indexRecordFields(input2)
400
451
  };
401
452
  return record2;
402
453
  }
@@ -413,15 +464,23 @@ function toDefinitionRecord(candidate) {
413
464
  sources: input.sources,
414
465
  seeAlso: input.seeAlso,
415
466
  alignment: input.alignment,
416
- status: candidate.lesson.status,
417
467
  glosses: input.glosses,
418
468
  source: input.source,
419
469
  resolvedReferences: candidate.resolvedReferences,
420
470
  ...input.formula === void 0 ? {} : { formula: input.formula },
421
- ...input.units === void 0 ? {} : { units: input.units }
471
+ ...input.units === void 0 ? {} : { units: input.units },
472
+ ...indexRecordFields(input)
422
473
  };
423
474
  return record;
424
475
  }
476
+ function indexRecordFields(input) {
477
+ return {
478
+ ...input.reads === void 0 ? {} : { reads: input.reads },
479
+ ...input.range === void 0 ? {} : { range: input.range },
480
+ ...input.fixedIndex === void 0 ? {} : { fixedIndex: input.fixedIndex },
481
+ ...input.formulaTemplate === void 0 ? {} : { formulaTemplate: input.formulaTemplate }
482
+ };
483
+ }
425
484
  function definitionFingerprint(candidate) {
426
485
  const references = sortedReferences(candidate.input.references).map(
427
486
  (reference) => ({
@@ -452,7 +511,6 @@ function definitionFingerprint(candidate) {
452
511
  sources: [...input2.sources].sort(),
453
512
  seeAlso: [...input2.seeAlso].sort(),
454
513
  alignment,
455
- status: input2.status,
456
514
  aiAssisted: input2.aiAssisted,
457
515
  body: normalizeText(input2.body),
458
516
  glosses: input2.glosses,
@@ -564,6 +622,52 @@ function uniqueSorted(values) {
564
622
  function sortedSources(values) {
565
623
  return [...values].sort(compareSourceSpans);
566
624
  }
625
+ function malformedReferences(lesson) {
626
+ if (!lesson.body?.includes("[[")) return [];
627
+ const prose = proseOnly(lesson.body, { keepReferences: true });
628
+ const found = [];
629
+ for (let at = prose.indexOf("[["); at !== -1; at = prose.indexOf("[[", at + 2)) {
630
+ if (lesson.body[at - 1] === "\\") continue;
631
+ const closed = /^\[\[[^[\]\n]*\]\]/.exec(prose.slice(at));
632
+ if (closed) {
633
+ at += closed[0].length - 2;
634
+ continue;
635
+ }
636
+ const near = lesson.body.slice(at, at + 40).split("\n")[0];
637
+ const line = lineOf(lesson.body, at) + (lesson.bodyLine ?? 1) - 1;
638
+ found.push(
639
+ diagnostic({
640
+ code: "malformed-reference",
641
+ message: `${lesson.lessonId} opens a reference that never closes, near ${JSON.stringify(near)}: close it with ]], as [[semantic.key]], on the same line.`,
642
+ lessonId: lesson.lessonId,
643
+ source: { ...lesson.source, line }
644
+ })
645
+ );
646
+ }
647
+ return found;
648
+ }
649
+ const FLOAT_KINDS = {
650
+ "eq-": "an equation",
651
+ "tab-": "a table",
652
+ "fig-": "a figure",
653
+ "dia-": "a diagram",
654
+ "ex-": "a worked-example set",
655
+ "check-": "a knowledge check",
656
+ "sec-": "a section",
657
+ "memo-": "a memo"
658
+ };
659
+ function keyHint(key) {
660
+ const reserved = reservedKeyPrefix(key);
661
+ if (reserved !== void 0) {
662
+ return `: [[${reserved}\u2026]] names ${FLOAT_KINDS[reserved]}, whose id is lower-case words and digits joined by "-"; ${JSON.stringify(key.slice(reserved.length))} is not one.`;
663
+ }
664
+ const bar = key.indexOf("|");
665
+ if (bar > 0) {
666
+ const target = key.slice(0, bar).trim();
667
+ return `: a [[\u2026]] reference takes no link text of its own \u2014 it reads as the entry's label. Write [[${target}]], with the words around it.`;
668
+ }
669
+ return ': a key is lower-case words and digits joined by "-" or "." (motion.launch-speed).';
670
+ }
567
671
  function diagnostic(input) {
568
672
  return { ...input, severity: "error" };
569
673
  }
@@ -1,6 +1,15 @@
1
+ /**
2
+ * KaTeX writes each formula twice: as MathML, whose `<annotation>` holds the
3
+ * LaTeX source, for assistive technology, and as the visible glyphs. Mark the
4
+ * MathML copy `data-pagefind-ignore`, so the site search indexes a formula
5
+ * once, as it reads, and never its LaTeX (`\explain{…}{…}`, `\dfrac`).
6
+ */
7
+ export function markMathmlForSearch(node: any): void;
1
8
  /**
2
9
  * rehype-katex reports parse failures as non-fatal VFile messages and renders
3
10
  * a `.katex-error` fallback. Turn those recovered failures into compiler
4
11
  * errors so Astro cannot successfully build a lesson containing broken math.
12
+ * The pass every course runs right after KaTeX, it also keeps the LaTeX
13
+ * annotations out of the site search (`markMathmlForSearch`).
5
14
  */
6
15
  export default function rehypeFailKatexErrors(): (tree: any, file: any) => void;