explico 0.13.0 → 0.14.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 (239) hide show
  1. package/CHANGELOG.md +894 -1
  2. package/README.md +200 -30
  3. package/dist/compiler/astro.d.ts +15 -2
  4. package/dist/compiler/astro.js +40 -11
  5. package/dist/compiler/cli-main.d.ts +43 -0
  6. package/dist/compiler/cli-main.js +392 -0
  7. package/dist/compiler/cli.d.ts +424 -9
  8. package/dist/compiler/cli.js +1289 -8
  9. package/dist/compiler/collections.d.ts +88 -1
  10. package/dist/compiler/collections.js +170 -25
  11. package/dist/compiler/course-config.d.ts +1 -1
  12. package/dist/compiler/course-config.js +105 -1
  13. package/dist/compiler/decks.d.ts +161 -0
  14. package/dist/compiler/decks.js +235 -0
  15. package/dist/compiler/exports/combined-html.d.ts +5 -1
  16. package/dist/compiler/exports/deck-fit.d.ts +55 -0
  17. package/dist/compiler/exports/deck-fit.js +90 -0
  18. package/dist/compiler/exports/export-chapters.d.ts +37 -6
  19. package/dist/compiler/exports/export-chapters.js +64 -5
  20. package/dist/compiler/exports/export-pages.d.ts +30 -3
  21. package/dist/compiler/exports/export-pages.js +24 -1
  22. package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  23. package/dist/compiler/exports/latex/cache.d.ts +78 -0
  24. package/dist/compiler/exports/latex/cache.js +129 -0
  25. package/dist/compiler/exports/latex/capture.d.ts +10 -1
  26. package/dist/compiler/exports/latex/capture.js +3 -1
  27. package/dist/compiler/exports/latex/compile.d.ts +13 -2
  28. package/dist/compiler/exports/latex/compile.js +12 -1
  29. package/dist/compiler/exports/latex/deck.d.ts +92 -0
  30. package/dist/compiler/exports/latex/deck.js +349 -0
  31. package/dist/compiler/exports/latex/document.d.ts +51 -6
  32. package/dist/compiler/exports/latex/document.js +71 -12
  33. package/dist/compiler/exports/latex/explico-common.sty +198 -0
  34. package/dist/compiler/exports/latex/explico.cls +13 -183
  35. package/dist/compiler/exports/latex/serialize.d.ts +60 -4
  36. package/dist/compiler/exports/latex/serialize.js +4 -2
  37. package/dist/compiler/exports/latex-export.d.ts +24 -2
  38. package/dist/compiler/exports/latex-export.js +107 -26
  39. package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
  40. package/dist/compiler/exports/markdown/serialize.js +85 -8
  41. package/dist/compiler/exports/markdown-export.d.ts +6 -1
  42. package/dist/compiler/exports/markdown-export.js +50 -11
  43. package/dist/compiler/exports/standalone-html.d.ts +10 -5
  44. package/dist/compiler/exports/standalone-html.js +3 -3
  45. package/dist/compiler/exports/static-tree.d.ts +38 -1
  46. package/dist/compiler/exports/static-tree.js +120 -4
  47. package/dist/compiler/knowledge-version.d.ts +26 -2
  48. package/dist/compiler/knowledge-version.js +27 -17
  49. package/dist/compiler/lesson-derivation.d.ts +143 -6
  50. package/dist/compiler/lesson-derivation.js +145 -13
  51. package/dist/compiler/manifest.d.ts +133 -4
  52. package/dist/compiler/manifest.js +359 -58
  53. package/dist/compiler/page-versions.d.ts +160 -0
  54. package/dist/compiler/page-versions.js +198 -0
  55. package/dist/compiler/render-diagnostics.d.ts +5 -1
  56. package/dist/compiler/resolution-report.d.ts +24 -6
  57. package/dist/compiler/resolution-report.js +22 -4
  58. package/dist/compiler/validate-content.d.ts +7 -0
  59. package/dist/compiler/validate-content.js +160 -0
  60. package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
  61. package/dist/curriculum/competency-graph.d.ts +54 -0
  62. package/dist/curriculum/competency-graph.js +41 -0
  63. package/dist/reference/base-library.d.mts +8 -0
  64. package/dist/reference/base-library.mjs +19 -0
  65. package/dist/reference/consistency.d.ts +8 -4
  66. package/dist/reference/consistency.js +76 -26
  67. package/dist/reference/deck-remark.d.mts +123 -0
  68. package/dist/reference/deck-remark.mjs +600 -0
  69. package/dist/reference/deck-source.d.ts +107 -0
  70. package/dist/reference/deck-source.js +140 -0
  71. package/dist/reference/decks-validate.d.ts +133 -0
  72. package/dist/reference/decks-validate.js +359 -0
  73. package/dist/reference/derived-entries.d.ts +5 -1
  74. package/dist/reference/diagram-fit.d.ts +1 -1
  75. package/dist/reference/figure-symbols.d.ts +23 -6
  76. package/dist/reference/figure-symbols.js +51 -15
  77. package/dist/reference/float-name.d.ts +43 -0
  78. package/dist/reference/float-name.js +58 -0
  79. package/dist/reference/folds-validate.d.ts +1 -1
  80. package/dist/reference/gate-abbreviations.d.ts +1 -1
  81. package/dist/reference/gate-indices.js +5 -4
  82. package/dist/reference/gate-math.d.ts +29 -2
  83. package/dist/reference/gate-math.js +99 -18
  84. package/dist/reference/gate-part-pages.d.ts +1 -1
  85. package/dist/reference/lab-katex-validate.d.ts +10 -6
  86. package/dist/reference/lab-katex-validate.js +17 -9
  87. package/dist/reference/lab-math-scope.js +29 -4
  88. package/dist/reference/math-glyphs.d.mts +25 -0
  89. package/dist/reference/math-glyphs.mjs +189 -25
  90. package/dist/reference/page-notation.d.ts +22 -3
  91. package/dist/reference/page-notation.js +32 -10
  92. package/dist/reference/references.d.ts +14 -1
  93. package/dist/reference/references.js +13 -0
  94. package/dist/reference/registry-scope.d.mts +33 -0
  95. package/dist/reference/registry-scope.mjs +34 -0
  96. package/dist/reference/registry.js +29 -10
  97. package/dist/reference/rehype-deck.d.mts +6 -0
  98. package/dist/reference/rehype-deck.mjs +286 -0
  99. package/dist/reference/rehype-heading-anchors.d.mts +20 -0
  100. package/dist/reference/rehype-heading-anchors.mjs +48 -6
  101. package/dist/reference/remark-notation.mjs +249 -40
  102. package/dist/reference/source-lines.d.mts +12 -0
  103. package/dist/reference/source-lines.mjs +65 -0
  104. package/dist/reference/types.d.ts +28 -0
  105. package/dist/rules/catalog.d.ts +21 -4
  106. package/dist/rules/catalog.js +221 -2
  107. package/dist/rules/course-rules.d.ts +5 -1
  108. package/dist/rules/course-rules.js +17 -3
  109. package/dist/rules/course-rules.schema.json +12 -0
  110. package/dist/rules/in-effect.d.ts +13 -3
  111. package/dist/rules/in-effect.js +30 -7
  112. package/dist/rules/rules.schema.json +259 -1
  113. package/dist/rules/settings.d.ts +1 -1
  114. package/dist/session/PreferenceStore.d.ts +6 -0
  115. package/dist/session/PreferenceStore.js +18 -1
  116. package/dist/session/deck-memory.d.ts +116 -0
  117. package/dist/session/deck-memory.js +143 -0
  118. package/dist/session/reading-memory.d.ts +96 -0
  119. package/dist/session/reading-memory.js +133 -0
  120. package/docs/VERSIONING.md +21 -18
  121. package/docs/authoring/README.md +29 -9
  122. package/docs/authoring/agents.md +63 -16
  123. package/docs/authoring/components.md +15 -0
  124. package/docs/authoring/deck.md +588 -0
  125. package/docs/authoring/migrating.md +1087 -14
  126. package/docs/authoring/notation.md +49 -6
  127. package/docs/authoring/structure.md +80 -1
  128. package/docs/authoring/versioning.md +54 -2
  129. package/docs/authoring/visualizations.md +7 -2
  130. package/docs/course-agent/AGENTS.md +37 -0
  131. package/docs/course-agent/agents/rules-reviewer.md +33 -0
  132. package/docs/course-agent/skills/draft-lesson/SKILL.md +37 -0
  133. package/docs/course-agent/skills/migrate-course/SKILL.md +31 -0
  134. package/docs/course-agent/skills/release-content/SKILL.md +24 -0
  135. package/docs/diagram-scaling.md +148 -0
  136. package/docs/privacy.md +59 -0
  137. package/package.json +8 -2
  138. package/src/compiler/astro.ts +60 -21
  139. package/src/compiler/cli-main.ts +469 -0
  140. package/src/compiler/cli.ts +1905 -16
  141. package/src/compiler/collections.ts +284 -25
  142. package/src/compiler/course-config.ts +267 -0
  143. package/src/compiler/decks.ts +455 -0
  144. package/src/compiler/exports/deck-fit.ts +127 -0
  145. package/src/compiler/exports/export-chapters.ts +105 -6
  146. package/src/compiler/exports/export-pages.ts +46 -2
  147. package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  148. package/src/compiler/exports/latex/cache.ts +183 -0
  149. package/src/compiler/exports/latex/capture.ts +5 -2
  150. package/src/compiler/exports/latex/compile.ts +18 -0
  151. package/src/compiler/exports/latex/deck.ts +419 -0
  152. package/src/compiler/exports/latex/document.ts +117 -21
  153. package/src/compiler/exports/latex/explico-common.sty +198 -0
  154. package/src/compiler/exports/latex/explico.cls +13 -183
  155. package/src/compiler/exports/latex/serialize.ts +25 -21
  156. package/src/compiler/exports/latex/texlive-packages.txt +6 -2
  157. package/src/compiler/exports/latex-export.ts +187 -45
  158. package/src/compiler/exports/markdown/serialize.ts +118 -9
  159. package/src/compiler/exports/markdown-export.ts +56 -11
  160. package/src/compiler/exports/standalone-html.ts +8 -7
  161. package/src/compiler/exports/static-tree.ts +175 -3
  162. package/src/compiler/knowledge-version.ts +56 -21
  163. package/src/compiler/lesson-derivation.ts +282 -16
  164. package/src/compiler/manifest.ts +558 -63
  165. package/src/compiler/page-versions.ts +359 -0
  166. package/src/compiler/resolution-report.ts +38 -7
  167. package/src/compiler/validate-content.ts +212 -0
  168. package/src/components/CourseContents.astro +124 -6
  169. package/src/components/CurriculumMap.astro +49 -1
  170. package/src/components/FloatIndex.astro +1 -1
  171. package/src/components/PartOverview.astro +51 -5
  172. package/src/components/assessments/AssessmentSet.astro +41 -15
  173. package/src/components/block-heading.ts +15 -6
  174. package/src/components/card-carry.ts +7 -4
  175. package/src/components/card-manager.ts +14 -2
  176. package/src/components/card-memory.ts +13 -9
  177. package/src/components/deck/DeckEnhancer.astro +13 -0
  178. package/src/components/deck/deck-context.ts +104 -0
  179. package/src/components/deck/deck-controller.ts +1287 -0
  180. package/src/components/deck/deck-map.ts +91 -0
  181. package/src/components/deck-lane.ts +163 -0
  182. package/src/components/diagram/Diagram.astro +131 -86
  183. package/src/components/disclosure/persisted-disclosure.ts +9 -1
  184. package/src/components/figure/Figure.astro +7 -2
  185. package/src/components/figure/FloatCaption.astro +5 -3
  186. package/src/components/fold/Fold.astro +2 -2
  187. package/src/components/notation/NotationFormula.astro +81 -11
  188. package/src/components/notation/NotationGlossary.astro +132 -1
  189. package/src/components/notation/NotationLayer.astro +29 -0
  190. package/src/components/starlight/AboutPanel.astro +186 -3
  191. package/src/components/starlight/CollapsibleSections.astro +24 -3
  192. package/src/components/starlight/DiagramEnhancer.astro +5 -1
  193. package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
  194. package/src/components/starlight/LayoutHeader.astro +17 -0
  195. package/src/components/starlight/LayoutPageTitle.astro +110 -0
  196. package/src/components/starlight/LayoutSidebar.astro +807 -21
  197. package/src/components/starlight/LayoutSocialIcons.astro +62 -6
  198. package/src/components/starlight/LessonExportActions.astro +27 -9
  199. package/src/components/starlight/LessonFooter.astro +59 -0
  200. package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
  201. package/src/components/starlight/TextSizeControl.astro +24 -13
  202. package/src/components/starlight/page-marks.ts +139 -0
  203. package/src/components/starlight/section-summary.ts +1 -1
  204. package/src/components/starlight/sidebar-editions.ts +245 -0
  205. package/src/components/table/Table.astro +15 -7
  206. package/src/content-config.ts +87 -1
  207. package/src/curriculum/competency-graph.ts +95 -0
  208. package/src/reference/base-library.mjs +19 -0
  209. package/src/reference/consistency.ts +126 -39
  210. package/src/reference/deck-remark.mjs +600 -0
  211. package/src/reference/deck-source.ts +273 -0
  212. package/src/reference/decks-validate.ts +604 -0
  213. package/src/reference/diagram-layout.ts +3 -1
  214. package/src/reference/figure-symbols.ts +108 -31
  215. package/src/reference/folds-validate.ts +1 -1
  216. package/src/reference/gate-indices.ts +5 -3
  217. package/src/reference/gate-math.ts +169 -25
  218. package/src/reference/lab-katex-validate.ts +30 -15
  219. package/src/reference/lab-math-scope.ts +34 -3
  220. package/src/reference/math-glyphs.mjs +189 -25
  221. package/src/reference/page-notation.ts +69 -15
  222. package/src/reference/references.ts +28 -0
  223. package/src/reference/registry-scope.mjs +34 -0
  224. package/src/reference/registry.ts +41 -11
  225. package/src/reference/rehype-deck.mjs +286 -0
  226. package/src/reference/rehype-heading-anchors.mjs +48 -6
  227. package/src/reference/remark-notation.mjs +249 -40
  228. package/src/reference/source-lines.mjs +65 -0
  229. package/src/reference/types.ts +28 -0
  230. package/src/rules/catalog.ts +257 -4
  231. package/src/rules/course-rules.ts +21 -1
  232. package/src/rules/in-effect.ts +54 -8
  233. package/src/session/PreferenceStore.ts +23 -0
  234. package/src/session/deck-memory.ts +263 -0
  235. package/src/session/reading-memory.ts +243 -0
  236. package/src/styles/deck.css +1150 -0
  237. package/src/styles/global.css +958 -27
  238. package/src/styles/zones.css +277 -0
  239. /package/src/{components → reference}/float-name.ts +0 -0
@@ -382,6 +382,154 @@ interface NormalizedCards {
382
382
  declare const CARDS_DEFAULTS: NormalizedCards;
383
383
  /** Validate `CourseConfig.cards` and apply its defaults. Throws on a bad value. */
384
384
  declare function normalizeCards(config: CourseConfig): NormalizedCards;
385
+ /**
386
+ * The marks the sidebar puts beside a chapter or part page for one reader
387
+ * (`sidebarMarks` in the course settings, `page-marks.ts`): a quiet ring
388
+ * while they have not opened it, an accent dot when its content changed
389
+ * since they last did. Every field is optional; {@link normalizeSidebarMarks}
390
+ * fills in the defaults.
391
+ */
392
+ interface CourseSidebarMarks {
393
+ /** Mark a page the reader has not opened yet. Default `true`. */
394
+ readonly unopened?: boolean;
395
+ /** Mark a page whose content changed since the reader last opened it. Default `true`. */
396
+ readonly changed?: boolean;
397
+ }
398
+ /** {@link CourseSidebarMarks} with every default applied. */
399
+ interface NormalizedSidebarMarks {
400
+ readonly unopened: boolean;
401
+ readonly changed: boolean;
402
+ }
403
+ declare const SIDEBAR_MARKS_DEFAULTS: NormalizedSidebarMarks;
404
+ /** Validate `CourseConfig.sidebarMarks` and apply its defaults. Throws on a bad value. */
405
+ declare function normalizeSidebarMarks(config: CourseConfig): NormalizedSidebarMarks;
406
+ /**
407
+ * Which edition of a lesson its sidebar row opens, for a reader who has not
408
+ * chosen (`sidebarEditions` in the course settings, `sidebar-editions.ts`). A
409
+ * lesson with a chapter and a deck is one row: the row opens the **lead**
410
+ * edition, and the **other** is an icon at the row's end, or not listed. A
411
+ * reader switches both in the sidebar's foot. The plain `string` fields
412
+ * arrive as JSON; {@link normalizeSidebarEditions} rejects any value it does
413
+ * not list.
414
+ */
415
+ interface CourseSidebarEditions {
416
+ /** The edition a lesson's row opens: `chapter` (default) or `slides`. */
417
+ readonly lead?: string;
418
+ /** Whether the row also lists the other edition: `listed` (default) or `hidden`. */
419
+ readonly other?: string;
420
+ }
421
+ type SidebarEditionLead = 'chapter' | 'slides';
422
+ type SidebarEditionOther = 'listed' | 'hidden';
423
+ /** {@link CourseSidebarEditions} with every default applied. */
424
+ interface NormalizedSidebarEditions {
425
+ readonly lead: SidebarEditionLead;
426
+ readonly other: SidebarEditionOther;
427
+ }
428
+ /** The engine's defaults (decision D10): the chapter leads, the slides are listed. */
429
+ declare const SIDEBAR_EDITIONS_DEFAULTS: NormalizedSidebarEditions;
430
+ /** Validate `CourseConfig.sidebarEditions` and apply its defaults. Throws on a bad value. */
431
+ declare function normalizeSidebarEditions(config: CourseConfig): NormalizedSidebarEditions;
432
+ /**
433
+ * A course's decks: the slide edition of a lesson (`<lesson>.deck.mdx`,
434
+ * `docs/authoring/deck.md`). Every field is optional; {@link normalizeDeck}
435
+ * fills in the defaults. The plain `string` fields arrive as JSON, and
436
+ * {@link normalizeDeck} rejects any value it does not list.
437
+ */
438
+ interface CourseDeck {
439
+ /**
440
+ * The canvas every deck of the course is laid out on: `16:9` (default),
441
+ * `16:10` or `4:3`. One setting for the course, because the frame-fit check,
442
+ * the captured figures and the PDF's page all read it.
443
+ */
444
+ readonly aspectRatio?: string;
445
+ /**
446
+ * The line on a frame that names the symbols it shows: `first-use`
447
+ * (default, the symbols the deck has not shown before), `every-frame`
448
+ * (every symbol on the frame), or `off`.
449
+ */
450
+ readonly legend?: string;
451
+ /**
452
+ * Who sees a frame's speaker notes: `reader` (default — under each frame in
453
+ * the scroll view, in the presenter window and in the handout), `presenter`
454
+ * (the presenter window and the handout only), or `off` (nowhere; the
455
+ * handout PDF is not built).
456
+ */
457
+ readonly notes?: string;
458
+ /** End each deck on a frame of what its lesson's competencies say a student can now do. Default `true`. */
459
+ readonly outcomesFrame?: boolean;
460
+ /** Offer the slides-with-notes PDF beside the slides PDF. Default `true`. */
461
+ readonly handoutPdf?: boolean;
462
+ }
463
+ type DeckAspectRatio = '16:9' | '16:10' | '4:3';
464
+ type DeckLegend = 'first-use' | 'every-frame' | 'off';
465
+ type DeckNotes = 'reader' | 'presenter' | 'off';
466
+ /**
467
+ * The logical canvas a frame is laid out on, in CSS pixels: the width of the
468
+ * page's reading column, so a figure, a table or a lab lays out on a frame at
469
+ * the size it was made for, and the frame is scaled to the screen. Every
470
+ * ratio is 450 px tall, so the type scale and a frame's capacity in lines are
471
+ * the same in all three. Printed, the canvas is scaled by 1.6 onto a page
472
+ * 7.5 in tall (13.333 × 7.5 in for 16:9).
473
+ */
474
+ declare const DECK_CANVAS: Readonly<Record<DeckAspectRatio, {
475
+ readonly width: number;
476
+ readonly height: number;
477
+ }>>;
478
+ /** {@link CourseDeck} with every default applied, as the manifest publishes it. */
479
+ interface NormalizedDeck {
480
+ readonly aspectRatio: DeckAspectRatio;
481
+ readonly legend: DeckLegend;
482
+ readonly notes: DeckNotes;
483
+ readonly outcomesFrame: boolean;
484
+ readonly handoutPdf: boolean;
485
+ readonly canvas: {
486
+ readonly width: number;
487
+ readonly height: number;
488
+ };
489
+ }
490
+ declare const DECK_DEFAULTS: Omit<NormalizedDeck, 'canvas'>;
491
+ /** Validate `CourseConfig.deck` and apply its defaults. Throws on a bad value. */
492
+ declare function normalizeDeck(config: CourseConfig): NormalizedDeck;
493
+ /**
494
+ * Whether the course asks each reader if it may remember their reading in
495
+ * their browser (`readingMemory` in the course settings,
496
+ * `session/reading-memory.ts`): which chapters they opened (the sidebar's
497
+ * marks) and the cards they keep or carry, across visits. `ask` (default)
498
+ * shows the question until the reader answers; `off` never asks and never
499
+ * keeps reading memory, so the marks are off and cards last as long as the tab.
500
+ */
501
+ type ReadingMemorySetting = 'ask' | 'off';
502
+ /** Validate `CourseConfig.readingMemory`. Throws on a bad value. */
503
+ declare function normalizeReadingMemory(config: CourseConfig): ReadingMemorySetting;
504
+ /**
505
+ * How the page tells its zones apart, and how a deck's slides sit on it
506
+ * (`appearance` in the course settings; `zones.css` and `deck.css` hold the
507
+ * colours). `zones`: `tonal` (the default) gives both sidebars one step of the
508
+ * neutral grey and the header a step further; `accent-header` makes the
509
+ * header a deep band of the course accent; `paper` lays the reading column
510
+ * on the sidebars' colour as a sheet; `none` keeps Starlight's colours.
511
+ * `slides`: `lifted` (the default) gives each slide an edge and a drop
512
+ * shadow, and in the dark theme a sheet a step lighter than the page; `edge`
513
+ * the edge and shadow on the page's own colour; `sorter` lays the deck on a
514
+ * tinted well. Every field is optional; {@link normalizeAppearance} fills in
515
+ * the defaults.
516
+ */
517
+ interface CourseAppearance {
518
+ /** `tonal` (default), `accent-header`, `paper` or `none`. */
519
+ readonly zones?: string;
520
+ /** `lifted` (default), `edge` or `sorter`. */
521
+ readonly slides?: string;
522
+ }
523
+ type AppearanceZones = 'tonal' | 'accent-header' | 'paper' | 'none';
524
+ type AppearanceSlides = 'lifted' | 'edge' | 'sorter';
525
+ /** {@link CourseAppearance} with every default applied. */
526
+ interface NormalizedAppearance {
527
+ readonly zones: AppearanceZones;
528
+ readonly slides: AppearanceSlides;
529
+ }
530
+ declare const APPEARANCE_DEFAULTS: NormalizedAppearance;
531
+ /** Validate `CourseConfig.appearance` and apply its defaults. Throws on a bad value. */
532
+ declare function normalizeAppearance(config: CourseConfig): NormalizedAppearance;
385
533
  /**
386
534
  * Rule S2's word ranges (`rules.S2.bridgeMinWords` … in the course settings),
387
535
  * in the shape `gate-part-pages.ts` takes. Built by `partPageLimits` in
@@ -487,6 +635,21 @@ interface CourseConfig {
487
635
  readonly numbering?: CourseNumbering;
488
636
  /** How explanation cards start for a reader: "Carry cards" on or off. */
489
637
  readonly cards?: CourseCards;
638
+ /** The sidebar's marks for a page the reader has not opened, or that changed since. */
639
+ readonly sidebarMarks?: CourseSidebarMarks;
640
+ /** Which edition a lesson's sidebar row opens, and whether it lists the other. */
641
+ readonly sidebarEditions?: CourseSidebarEditions;
642
+ /** How the course's decks look and what they show (`docs/authoring/deck.md`). */
643
+ readonly deck?: CourseDeck;
644
+ /**
645
+ * Ask each reader whether the course may remember their reading in their
646
+ * browser (`ask`, the default), or never keep it (`off`). A plain `string`
647
+ * because the settings arrive as JSON; {@link normalizeReadingMemory}
648
+ * rejects any other value.
649
+ */
650
+ readonly readingMemory?: string;
651
+ /** How the page tells header, sidebars and article apart, and how slides sit on it. */
652
+ readonly appearance?: CourseAppearance;
490
653
  /**
491
654
  * How a check's and an example set's heading names its kind: `icon-word`
492
655
  * (default) or `icon` ({@link BlockLabels}). A plain `string` because the
@@ -504,4 +667,4 @@ declare function normalizeLessonExports(config: CourseConfig): NormalizedLessonE
504
667
  */
505
668
  declare function coursePartsOf(config: CourseConfig): readonly CoursePart[];
506
669
 
507
- export { type CourseOpenDefault as A, type BlockLabels as B, type CourseConfig as C, type CoursePart as D, type CourseSection as E, DISCLOSURE_DEFAULTS as F, coursePartsOf as G, normalizeBlockLabels as H, normalizeCards as I, normalizeDisclosure as J, normalizeLessonExports as K, type LessonExportsConfig as L, type ManifestRules as M, type NormalizedLessonExports as N, normalizeNumbering as O, type ResolvedRules as R, type NormalizedDisclosure as a, type NormalizedNumbering as b, type NormalizedCards as c, type ConfigDiagnostic as d, type CourseOperators as e, type CourseRomanLabels as f, type CourseAccents as g, type CourseAbbreviations as h, type NormalizedPartPages as i, type CourseRulesConfig as j, type RuleSettingsInput as k, catalogIds as l, disabledCodes as m, dropDisabled as n, rulesJsonSchema as o, partPageLimits as p, rulesSnapshot as q, resolveRules as r, BLOCK_LABELS_DEFAULT as s, CARDS_DEFAULTS as t, type CourseAbout as u, type CourseAuthor as v, type CourseCards as w, type CourseDisclosure as x, type CourseHeadingDisclosure as y, type CourseNumbering as z };
670
+ export { SIDEBAR_EDITIONS_DEFAULTS as $, APPEARANCE_DEFAULTS as A, type BlockLabels as B, type CourseConfig as C, CARDS_DEFAULTS as D, type CourseAbout as E, type CourseAppearance as F, type CourseAuthor as G, type CourseCards as H, type CourseDeck as I, type CourseDisclosure as J, type CourseHeadingDisclosure as K, type CourseNumbering as L, type ManifestRules as M, type NormalizedLessonExports as N, type CourseOpenDefault as O, type CoursePart as P, type CourseSection as Q, type ReadingMemorySetting as R, type CourseSidebarEditions as S, type CourseSidebarMarks as T, DECK_CANVAS as U, DECK_DEFAULTS as V, DISCLOSURE_DEFAULTS as W, type DeckAspectRatio as X, type DeckLegend as Y, type DeckNotes as Z, type LessonExportsConfig as _, type NormalizedDisclosure as a, SIDEBAR_MARKS_DEFAULTS as a0, type SidebarEditionLead as a1, type SidebarEditionOther as a2, coursePartsOf as a3, normalizeAppearance as a4, normalizeBlockLabels as a5, normalizeCards as a6, normalizeDeck as a7, normalizeDisclosure as a8, normalizeLessonExports as a9, normalizeNumbering as aa, normalizeReadingMemory as ab, normalizeSidebarEditions as ac, normalizeSidebarMarks as ad, type NormalizedNumbering as b, type NormalizedCards as c, type NormalizedSidebarMarks as d, type NormalizedSidebarEditions as e, type NormalizedAppearance as f, type NormalizedDeck as g, type ConfigDiagnostic as h, type CourseOperators as i, type CourseRomanLabels as j, type CourseAccents as k, type CourseAbbreviations as l, type NormalizedPartPages as m, type CourseRulesConfig as n, type ResolvedRules as o, type RuleSettingsInput as p, catalogIds as q, disabledCodes as r, dropDisabled as s, partPageLimits as t, resolveRules as u, rulesJsonSchema as v, rulesSnapshot as w, type AppearanceSlides as x, type AppearanceZones as y, BLOCK_LABELS_DEFAULT as z };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The competency dependency graph, cut into one piece per part
3
+ * (`<CurriculumMap graph="per-part">`).
4
+ *
5
+ * The whole-course graph is one diagram, and a course with many competencies
6
+ * lays it out far wider than the lesson column (rule V8) — narrower boxes do
7
+ * not help, since the widest rank holds a dozen competencies. One diagram per
8
+ * part keeps each to the competencies a reader meets together.
9
+ */
10
+ interface GraphNode {
11
+ readonly id: string;
12
+ readonly text: string;
13
+ }
14
+ interface GraphEdge {
15
+ readonly from: string;
16
+ readonly to: string;
17
+ }
18
+ interface PartGraph {
19
+ /** The diagram's id, unique on the page: `competency-graph-<part dir>`. */
20
+ readonly id: string;
21
+ /** The part's overview title, its label when it has none; the untaught group's name. */
22
+ readonly title: string;
23
+ /** The competencies this group teaches, then one box per earlier competency they build on. */
24
+ readonly nodes: readonly GraphNode[];
25
+ /** An arrow from each prerequisite to a competency the group teaches. */
26
+ readonly edges: readonly GraphEdge[];
27
+ }
28
+ interface PartInput {
29
+ readonly dir: string;
30
+ readonly label: string;
31
+ /** Lesson ids in chapter order. */
32
+ readonly lessons: readonly string[];
33
+ readonly overview?: {
34
+ readonly title: string;
35
+ };
36
+ }
37
+ interface CompetencyInput {
38
+ readonly id: string;
39
+ readonly title: string;
40
+ readonly prerequisites: readonly string[];
41
+ }
42
+ declare const UNTAUGHT_GRAPH_ID = "competency-graph-untaught";
43
+ declare const UNTAUGHT_TITLE = "Competencies no lesson teaches";
44
+ /**
45
+ * One graph per part, in `parts` order, then a last one for competencies no
46
+ * lesson teaches, so every competency is drawn once as a taught box. A
47
+ * competency belongs to the first part whose lessons teach it. A prerequisite
48
+ * taught in another part is drawn in this one as a box labelled
49
+ * `<part label>: <title>`, so the arrow into the competency still shows.
50
+ * A part that teaches no competency has no graph.
51
+ */
52
+ declare function competencyGraphsByPart(parts: readonly PartInput[], teachesByLesson: ReadonlyMap<string, readonly string[]>, competencies: readonly CompetencyInput[]): PartGraph[];
53
+
54
+ export { type CompetencyInput, type GraphEdge, type GraphNode, type PartGraph, type PartInput, UNTAUGHT_GRAPH_ID, UNTAUGHT_TITLE, competencyGraphsByPart };
@@ -0,0 +1,41 @@
1
+ const UNTAUGHT_GRAPH_ID = "competency-graph-untaught";
2
+ const UNTAUGHT_TITLE = "Competencies no lesson teaches";
3
+ function competencyGraphsByPart(parts, teachesByLesson, competencies) {
4
+ const partOf = /* @__PURE__ */ new Map();
5
+ for (const part of parts) {
6
+ for (const lessonId of part.lessons) {
7
+ for (const id of teachesByLesson.get(lessonId) ?? []) {
8
+ if (!partOf.has(id)) partOf.set(id, part);
9
+ }
10
+ }
11
+ }
12
+ const titleOf = new Map(competencies.map((competency) => [competency.id, competency.title]));
13
+ const graphs = [];
14
+ for (const part of [...parts, void 0]) {
15
+ const taught = competencies.filter((competency) => partOf.get(competency.id) === part);
16
+ if (taught.length === 0) continue;
17
+ const earlier = [...new Set(taught.flatMap((competency) => competency.prerequisites))].filter(
18
+ (id) => partOf.get(id) !== part
19
+ );
20
+ graphs.push({
21
+ id: part ? `competency-graph-${part.dir.replace(/[^A-Za-z0-9_-]/g, "-")}` : UNTAUGHT_GRAPH_ID,
22
+ title: part ? part.overview?.title ?? part.label : UNTAUGHT_TITLE,
23
+ nodes: [
24
+ ...taught.map((competency) => ({ id: competency.id, text: competency.title })),
25
+ ...earlier.map((id) => ({
26
+ id,
27
+ text: `${partOf.get(id)?.label ?? "Not taught"}: ${titleOf.get(id) ?? id}`
28
+ }))
29
+ ],
30
+ edges: taught.flatMap(
31
+ (competency) => competency.prerequisites.map((from) => ({ from, to: competency.id }))
32
+ )
33
+ });
34
+ }
35
+ return graphs;
36
+ }
37
+ export {
38
+ UNTAUGHT_GRAPH_ID,
39
+ UNTAUGHT_TITLE,
40
+ competencyGraphsByPart
41
+ };
@@ -1,5 +1,11 @@
1
1
  /** True when `token` is a base-library atom and needs no semantic key. */
2
2
  export function isBaseLibraryAtom(token: any): boolean;
3
+ /**
4
+ * True when `glyph`, as a rendered page shows it, is a base-library atom. The
5
+ * rendered-page check (`figure-symbols.ts`) reads glyphs, not LaTeX, and
6
+ * exempts the same library the math gates do.
7
+ */
8
+ export function isBaseLibraryGlyph(glyph: any): boolean;
3
9
  /**
4
10
  * The base notation library: a small, fixed set of universal identifier atoms
5
11
  * that the page-level glyph map never asks an author to declare.
@@ -15,3 +21,5 @@ export function isBaseLibraryAtom(token: any): boolean;
15
21
  * atoms, so the tokenizer never treats them as bindable symbols.
16
22
  */
17
23
  export const BASE_LIBRARY: Readonly<Set<string>>;
24
+ /** What KaTeX draws for each base-library atom: `d`, `e`, `i`, `π`. */
25
+ export const BASE_LIBRARY_GLYPHS: Readonly<Set<any>>;
@@ -25,3 +25,22 @@ export const BASE_LIBRARY = Object.freeze(
25
25
  export function isBaseLibraryAtom(token) {
26
26
  return BASE_LIBRARY.has(token);
27
27
  }
28
+
29
+ // An atom's LaTeX is its own glyph unless it is a command: `\pi` draws `π`.
30
+ // `tests/notation/figure-symbols.test.ts` renders every atom and holds this
31
+ // table to what KaTeX draws, so a new command atom cannot be forgotten here.
32
+ const DRAWN_AS = Object.freeze({ '\\pi': 'π' });
33
+
34
+ /** What KaTeX draws for each base-library atom: `d`, `e`, `i`, `π`. */
35
+ export const BASE_LIBRARY_GLYPHS = Object.freeze(
36
+ new Set([...BASE_LIBRARY].map((atom) => DRAWN_AS[atom] ?? atom)),
37
+ );
38
+
39
+ /**
40
+ * True when `glyph`, as a rendered page shows it, is a base-library atom. The
41
+ * rendered-page check (`figure-symbols.ts`) reads glyphs, not LaTeX, and
42
+ * exempts the same library the math gates do.
43
+ */
44
+ export function isBaseLibraryGlyph(glyph) {
45
+ return BASE_LIBRARY_GLYPHS.has(glyph);
46
+ }
@@ -1,4 +1,4 @@
1
- import { e as CourseOperators, f as CourseRomanLabels, g as CourseAccents } from '../course-config-rRtME5UV.js';
1
+ import { i as CourseOperators, j as CourseRomanLabels, k as CourseAccents } from '../course-config-BLtIdkei.js';
2
2
  import { NotationSpec } from '../compiler/collections.js';
3
3
  import { LintIgnore } from './lint-ignore.js';
4
4
  import { NotationRegistry, NotationRegistryInput } from './types.js';
@@ -18,7 +18,10 @@ import './gate-retired-keys.js';
18
18
  * snapshot. Nothing here changes content. A finding is silenced only by a
19
19
  * reviewed line in `lint-ignore.yml` (see `./lint-ignore`).
20
20
  *
21
- * (a) glyph-unique-in-corpus one rendered glyph → one meaning corpus-wide
21
+ * (a) glyph-unique-in-corpus one rendered glyph → one shared meaning; a
22
+ * local card or a gloss may reuse a letter
23
+ * (a') definition-repeated one local key declared in two lessons
24
+ * (a'') meaning-repeated one symbol and name defined at two sites, any tier
22
25
  * (b) notation-units / units-vocab every entry has units or `dimensionless`,
23
26
  * unit strings drawn from a controlled vocab
24
27
  * (f) weak-local a `notation.local` symbol used only in its
@@ -44,7 +47,7 @@ import './gate-retired-keys.js';
44
47
  * `src/content/resolution-report.ts`.
45
48
  */
46
49
 
47
- type ConsistencyCode = 'glyph-unique-in-corpus' | 'notation-units' | 'units-vocab' | 'weak-local' | 'notation-source-locator' | 'gloss-wants-promoting' | 'card-wants-demoting' | 'gloss-name-shape' | 'operator-explained' | 'roman-label-explained' | 'accent-explained' | 'accent-swallowed' | 'operator-suspended' | 'operator-suspension-reason' | 'operator-shadowed';
50
+ type ConsistencyCode = 'glyph-unique-in-corpus' | 'notation-units' | 'units-vocab' | 'weak-local' | 'notation-source-locator' | 'definition-repeated' | 'meaning-repeated' | 'card-wants-demoting' | 'gloss-name-shape' | 'operator-explained' | 'roman-label-explained' | 'accent-explained' | 'accent-swallowed' | 'operator-suspended' | 'operator-suspension-reason' | 'operator-shadowed';
48
51
  interface ConsistencyDiagnostic {
49
52
  readonly code: ConsistencyCode;
50
53
  /** Tier 3 — repairable violations are warnings; optional advice is info. */
@@ -69,6 +72,7 @@ interface ConsistencyInput {
69
72
  /** This course's edits to the (empty) accent baseline (rule N22). */
70
73
  readonly accents?: CourseAccents;
71
74
  }
75
+ declare function glyphMatcher(latex: string): RegExp | undefined;
72
76
  /**
73
77
  * Run every consistency check. The result is sorted deterministically (code,
74
78
  * then lesson / key / glyph, then message) so it is byte-stable in the manifest
@@ -78,4 +82,4 @@ declare function runConsistencyChecks(input: ConsistencyInput): ConsistencyDiagn
78
82
  /** `{ code: count }` for the CLI summary and the report header. */
79
83
  declare function consistencyCounts(diagnostics: readonly ConsistencyDiagnostic[]): Record<ConsistencyCode, number>;
80
84
 
81
- export { type ConsistencyCode, type ConsistencyDiagnostic, type ConsistencyInput, consistencyCounts, runConsistencyChecks };
85
+ export { type ConsistencyCode, type ConsistencyDiagnostic, type ConsistencyInput, consistencyCounts, glyphMatcher, runConsistencyChecks };
@@ -54,14 +54,14 @@ function checkGlyphUniqueness(input, out) {
54
54
  glyphs.set(trimmed, keys);
55
55
  };
56
56
  for (const definition of input.registry.definitions) {
57
- if (definition.kind === "local") {
58
- record(definition.notation, definition.key, definition.lessonId);
57
+ if (definition.kind === "shared") {
58
+ record(definition.notation, definition.key, "");
59
59
  }
60
60
  }
61
61
  for (const bundle of input.registry.bundles) {
62
62
  for (const binding of bundle.bindings) {
63
63
  const definition = byId.get(binding.definitionId);
64
- if (definition) record(definition.notation, binding.key, bundle.lessonId);
64
+ if (definition?.kind === "shared") record(definition.notation, binding.key, bundle.lessonId);
65
65
  }
66
66
  }
67
67
  for (const [glyph, keys] of [...glyphs].sort(
@@ -69,12 +69,15 @@ function checkGlyphUniqueness(input, out) {
69
69
  )) {
70
70
  if (keys.size < 2) continue;
71
71
  if (input.lintIgnore.matches("glyph-unique-in-corpus", glyph)) continue;
72
- const detail = [...keys].sort(([a], [b]) => a.localeCompare(b)).map(([key, lessons]) => `${key} (${[...lessons].sort().join(", ")})`).join("; ");
72
+ const detail = [...keys].sort(([a], [b]) => a.localeCompare(b)).map(([key, lessons]) => {
73
+ const bound = [...lessons].filter((lesson) => lesson !== "").sort();
74
+ return `${key} (${bound.length > 0 ? bound.join(", ") : "no lesson binds it"})`;
75
+ }).join("; ");
73
76
  out.push({
74
77
  code: "glyph-unique-in-corpus",
75
78
  severity: "warning",
76
79
  glyph,
77
- message: `glyph ${JSON.stringify(glyph)} resolves to ${keys.size} meanings across the corpus: ${detail}`
80
+ message: `glyph ${JSON.stringify(glyph)} is ${keys.size} shared cards: ${detail}; course vocabulary gives a glyph one meaning \u2014 keep one card, or make the page-bound one a notation.local entry`
78
81
  });
79
82
  }
80
83
  }
@@ -144,34 +147,78 @@ function checkNotationSourceLocator(input, out) {
144
147
  }
145
148
  }
146
149
  }
147
- const GLOSS_PROMOTION_LIMIT = 3;
148
- function checkGlossWantsPromoting(input, out) {
149
- const owners = /* @__PURE__ */ new Map();
150
- const record = (ownerKey, gloss) => {
151
- const id = `${gloss.notation}\0${gloss.label.toLowerCase()}`;
152
- const current = owners.get(id) ?? { label: gloss.label, keys: /* @__PURE__ */ new Set() };
153
- current.keys.add(ownerKey);
154
- owners.set(id, current);
150
+ function checkDefinitionRepeated(input, out) {
151
+ const byKey = /* @__PURE__ */ new Map();
152
+ const chapters = input.notationInput.lessons.filter((lesson) => lesson.scopeLessonIds === void 0);
153
+ for (const lesson of [...chapters].sort((a, b) => a.lessonId.localeCompare(b.lessonId))) {
154
+ for (const definition of lesson.localDefinitions) {
155
+ const list = byKey.get(definition.key) ?? [];
156
+ if (!list.some((entry) => entry.lessonId === lesson.lessonId)) {
157
+ list.push({ lessonId: lesson.lessonId, file: lesson.source.file, definition });
158
+ }
159
+ byKey.set(definition.key, list);
160
+ }
161
+ }
162
+ for (const [key, copies] of [...byKey].sort(([a], [b]) => a.localeCompare(b))) {
163
+ if (copies.length < 2) continue;
164
+ if (input.lintIgnore.matches("definition-repeated", key)) continue;
165
+ const first = copies[0];
166
+ const differing = ["notation", "label", "summary", "units", "sources"].filter(
167
+ (field) => copies.some((copy) => JSON.stringify(copy.definition[field] ?? null) !== JSON.stringify(first.definition[field] ?? null))
168
+ );
169
+ const lessons = copies.map((copy) => copy.lessonId).sort();
170
+ out.push({
171
+ code: "definition-repeated",
172
+ severity: "warning",
173
+ key,
174
+ lessonId: first.lessonId,
175
+ file: first.file,
176
+ count: copies.length,
177
+ message: `local entry ${key} (${first.definition.notation}) is declared in ${copies.length} lessons (${lessons.join(", ")}); ` + (differing.length === 0 ? "the copies agree" : `the copies differ in ${differing.join(", ")}`) + ` \u2014 move one copy to content/notation/${key}.md and delete the others: every [[${key}]] and \\explain{${key}} keeps working; if they are two meanings, give one another key`
178
+ });
179
+ }
180
+ }
181
+ function checkMeaningRepeated(input, out) {
182
+ const normalize = (label) => label.toLowerCase().replace(/\s+/g, " ").trim();
183
+ const groups = /* @__PURE__ */ new Map();
184
+ const record = (notation, label, site) => {
185
+ const id = `${notation.trim()}\0${normalize(label)}`;
186
+ const group = groups.get(id) ?? { notation: notation.trim(), label, sites: [] };
187
+ if (!group.sites.some((known) => known.tier === site.tier && known.key === site.key && known.where === site.where)) {
188
+ group.sites.push(site);
189
+ }
190
+ groups.set(id, group);
155
191
  };
156
192
  for (const definition of input.notationInput.sharedDefinitions) {
157
- for (const gloss of definition.glosses) record(definition.key, gloss);
193
+ record(definition.notation, definition.label, { tier: "shared", key: definition.key, where: `card ${definition.key}` });
194
+ for (const gloss of definition.glosses) {
195
+ record(gloss.notation, gloss.label, { tier: "gloss", key: gloss.key, where: `gloss on ${definition.key}` });
196
+ }
158
197
  }
159
198
  for (const lesson of input.notationInput.lessons) {
199
+ if (lesson.scopeLessonIds !== void 0) continue;
160
200
  for (const definition of lesson.localDefinitions) {
161
- for (const gloss of definition.glosses) record(definition.key, gloss);
201
+ record(definition.notation, definition.label, {
202
+ tier: "local",
203
+ key: definition.key,
204
+ where: `local ${definition.key} on ${lesson.lessonId}`
205
+ });
206
+ for (const gloss of definition.glosses) {
207
+ record(gloss.notation, gloss.label, { tier: "gloss", key: gloss.key, where: `gloss on ${definition.key} (${lesson.lessonId})` });
208
+ }
162
209
  }
163
210
  }
164
- for (const [id, { label, keys }] of [...owners].sort(
165
- ([a], [b]) => a.localeCompare(b)
166
- )) {
167
- if (keys.size < GLOSS_PROMOTION_LIMIT) continue;
168
- const glyph = id.split("\0")[0] ?? "";
169
- if (input.lintIgnore.matches("gloss-wants-promoting", glyph)) continue;
211
+ for (const [, group] of [...groups].sort(([a], [b]) => a.localeCompare(b))) {
212
+ if (group.sites.length < 2) continue;
213
+ if (group.sites.every((site) => site.tier === "local" && site.key === group.sites[0].key)) continue;
214
+ if (input.lintIgnore.matches("meaning-repeated", group.notation)) continue;
215
+ const shared = group.sites.find((site) => site.tier === "shared");
170
216
  out.push({
171
- code: "gloss-wants-promoting",
217
+ code: "meaning-repeated",
172
218
  severity: "info",
173
- glyph,
174
- message: `gloss ${JSON.stringify(glyph)} (${label}) is declared on ${keys.size} entries (${[...keys].sort().join(", ")}); a meaning reused this widely wants promoting to a card`
219
+ glyph: group.notation,
220
+ count: group.sites.length,
221
+ message: `${group.notation} as ${JSON.stringify(group.label)} is defined ${group.sites.length} times: ${group.sites.map((site) => site.where).join("; ")}` + (shared ? ` \u2014 ${shared.key} is the shared card; bind it with [[${shared.key}]] (or \\explain{${shared.key}}{${group.notation}} in a formula) instead of declaring it again` : " \u2014 one shared card would serve every site; promote it and reference it")
175
222
  });
176
223
  }
177
224
  }
@@ -435,7 +482,8 @@ const CHECKS = [
435
482
  checkNotationUnits,
436
483
  checkWeakLocal,
437
484
  checkNotationSourceLocator,
438
- checkGlossWantsPromoting,
485
+ checkDefinitionRepeated,
486
+ checkMeaningRepeated,
439
487
  checkCardWantsDemoting,
440
488
  checkGlossNameShape,
441
489
  checkOperatorExplained,
@@ -457,7 +505,8 @@ function consistencyCounts(diagnostics) {
457
505
  "units-vocab": 0,
458
506
  "weak-local": 0,
459
507
  "notation-source-locator": 0,
460
- "gloss-wants-promoting": 0,
508
+ "definition-repeated": 0,
509
+ "meaning-repeated": 0,
461
510
  "card-wants-demoting": 0,
462
511
  "gloss-name-shape": 0,
463
512
  "operator-explained": 0,
@@ -473,5 +522,6 @@ function consistencyCounts(diagnostics) {
473
522
  }
474
523
  export {
475
524
  consistencyCounts,
525
+ glyphMatcher,
476
526
  runConsistencyChecks
477
527
  };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * The deck a docs file is, or `undefined`. A lesson's: `{ scope: 'lesson',
3
+ * lessonSlug, lessonId, path, chapterPath }`, the chapter being the lesson's
4
+ * `.mdx` (or `.md`) beside it. A part's (`<part>/index.deck.mdx`): `{ scope:
5
+ * 'part', partDir, pageId, path }`, its page id `<part>.slides`.
6
+ */
7
+ export function deckOfFile(file: any): {
8
+ scope: string;
9
+ path: string;
10
+ partDir: string;
11
+ pageId: string;
12
+ } | {
13
+ chapterPath?: string | undefined;
14
+ scope: string;
15
+ path: string;
16
+ lessonSlug: string;
17
+ lessonId: string;
18
+ partDir?: undefined;
19
+ pageId?: undefined;
20
+ } | undefined;
21
+ /**
22
+ * The chapter file of a lesson of a part's deck's part (`motion/range` →
23
+ * `…/motion/range.mdx`, or `.md`), beside the deck, if there is one.
24
+ */
25
+ export function partChapterPath(deck: any, lessonSlug: any): string | undefined;
26
+ /**
27
+ * The chapter files whose scope a deck's symbols bind in, in order: a
28
+ * lesson's deck its chapter (none for a slides-first lesson); a part's deck
29
+ * every chapter of its part, in reading order (`manifest.parts[].lessons`).
30
+ */
31
+ export function deckScopeChapters(deck: any, manifest: any): any;
32
+ /** A chapter file's frontmatter and body, read once per change of the file. */
33
+ export function readChapter(chapterPath: any): any;
34
+ /**
35
+ * The manifest's deck for a deck file (`deckOfFile`) — a lesson's or a
36
+ * part's — or, given a lesson id, for that lesson, if there is one.
37
+ */
38
+ export function manifestDeckOf(manifest: any, owner: any): any;
39
+ /**
40
+ * Replace each `<Recall ref="eq-…" />` (or `fig-`, `tab-`, `dia-`) with the
41
+ * chapter's own float, parsed from the chapter with this page's parser.
42
+ * A recalled equation's math node and a recalled float's element carry
43
+ * `data.explicoRecall = { prefix, key, number, href }`; the imports a
44
+ * recalled float's components need are added to the deck's own.
45
+ */
46
+ /**
47
+ * What MDX's compile does to a tree before any course plugin sees it: a
48
+ * paragraph holding only JSX (and white space) becomes that JSX, as flow.
49
+ * An inline SVG's `<text x="…">0</text>` line parses as such a paragraph;
50
+ * kept, it would put a `<p>` inside the `<svg>`. `processor.parse` stops
51
+ * short of that step, so a chapter read for a recall takes it here
52
+ * (`remark-mark-and-unravel` in `@mdx-js/mdx`).
53
+ */
54
+ export function unravelJsxParagraphs(tree: any): any;
55
+ export function expandRecalls(tree: any, { processor, deck, deckEntry, manifest, base, fail }: {
56
+ processor: any;
57
+ deck: any;
58
+ deckEntry: any;
59
+ manifest: any;
60
+ base: any;
61
+ fail: any;
62
+ }): void;
63
+ /**
64
+ * The deck page's equations and floats, in its reading order, and what each
65
+ * shows: a recalled one the chapter's number, a labelled equation of the
66
+ * deck its own (1), (2) in label order (`manifest.decks[].equations`), and
67
+ * the deck's own floats no number.
68
+ */
69
+ export function collectDeckFloats(tree: any, deckEntry: any): {
70
+ equations: Map<any, any>;
71
+ floats: Map<any, any>;
72
+ };
73
+ /**
74
+ * Give the deck's `##` and `###` headings the anchors and frame numbers the
75
+ * manifest decided, in order; a different count fails the build (a stale
76
+ * manifest), as a chapter's headings do.
77
+ */
78
+ export function stampDeckHeadings(tree: any, deckEntry: any, fail: any): void;
79
+ /**
80
+ * On a part's deck, give each `<AssessmentSet>` the lesson whose checks it
81
+ * asks (`lesson`, the component's prop): the chapter its frame's section
82
+ * covers (`frames[].chapter`). A frame in no such section asks none, which
83
+ * the manifest reports as `deck-check-no-chapter`.
84
+ */
85
+ export function markDeckChecks(tree: any, deckEntry: any, fail: any): void;
86
+ /**
87
+ * The generated title frame: the deck's title, its course, its authors and
88
+ * the version its content is at (rules D10 and C5), in the words of a
89
+ * chapter's PDF (`versionPhrase` in compiler/page-versions.ts).
90
+ */
91
+ export function titleFrameNode(manifest: any, deckEntry: any): {
92
+ type: string;
93
+ data: {
94
+ hName: any;
95
+ hProperties: any;
96
+ };
97
+ children: any[];
98
+ };
99
+ /**
100
+ * The competencies a deck's outcomes frame lists: its lesson's, or on a
101
+ * part's deck every covered chapter's, in reading order, each once.
102
+ */
103
+ export function deckTeaches(manifest: any, deckEntry: any): any;
104
+ /** The generated outcomes frame: what the lesson's competencies say a student can now do. */
105
+ export function outcomesFrameNode(manifest: any, deckEntry: any, lesson: any): {
106
+ type: string;
107
+ data: {
108
+ hName: any;
109
+ hProperties: any;
110
+ };
111
+ children: any[];
112
+ } | undefined;
113
+ /**
114
+ * After notation ran: give each `###` frame its legend line — the symbols it
115
+ * shows for the first time in the deck (`first-use`), or all it shows
116
+ * (`every-frame`) — as math the page's KaTeX pass renders, each symbol bound
117
+ * to its card. `resolved` is the page's resolved notation (`key → entry`).
118
+ */
119
+ export function insertLegends(tree: any, { resolved, mode }: {
120
+ resolved: any;
121
+ mode: any;
122
+ }): void;
123
+ export const DECK_CAPTION_PATTERN: RegExp;