explico 0.13.1 → 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 +879 -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 +1045 -16
  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 +15 -7
  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
@@ -20,6 +20,73 @@ import { ContentRecord } from '../reference/gate-retired-keys.js';
20
20
  * ignoring code fences and inline code, sorted by position.
21
21
  */
22
22
  declare function extractNotationReferences(text: string, file: string, kind: NotationReferenceInput['kind'] | undefined): NotationReferenceInput[];
23
+ /** What every deck file is, whichever it belongs to ({@link DeckFile}). */
24
+ interface DeckFileBase {
25
+ /** Absolute path of the deck file. */
26
+ readonly path: string;
27
+ /** Repository-relative path, the `file` of its diagnostics. */
28
+ readonly file: string;
29
+ /** Docs slug of the deck file, extension included: `motion/range.deck.mdx`. */
30
+ readonly slug: string;
31
+ /** Where the deck is served: `motion/range/slides`, `motion/range` alone, or a part's `motion/slides`. */
32
+ readonly route: string;
33
+ /** The id of the deck's page: `motion.range.slides`, the lesson's id alone, or a part's `motion.slides`. */
34
+ readonly pageId: string;
35
+ /** The part directory the deck sits in: `motion`; empty for a deck outside one. */
36
+ readonly partDir: string;
37
+ /** True when the deck sits in a part's directory, as a lesson does. */
38
+ readonly inPart: boolean;
39
+ /** The deck file's frontmatter. */
40
+ readonly data: Record<string, unknown>;
41
+ readonly body: string;
42
+ /** 1-based file line {@link body} starts on. */
43
+ readonly bodyLine: number;
44
+ }
45
+ /**
46
+ * A lesson's deck (`<lesson>.deck.mdx`): its slide edition, beside the
47
+ * lesson's chapter or, for a lesson that is only slides, alone. See
48
+ * `docs/authoring/deck.md`.
49
+ */
50
+ interface LessonDeckFile extends DeckFileBase {
51
+ readonly scope: 'lesson';
52
+ /** The lesson's slug: `motion/range`. */
53
+ readonly lessonSlug: string;
54
+ readonly lessonId: string;
55
+ /** The lesson's chapter file, when it has one. */
56
+ readonly chapterPath?: string;
57
+ }
58
+ /**
59
+ * A part's deck (`<part>/index.deck.mdx`, beside the part's page): the
60
+ * slides of the part, whose sections may name the chapters they cover. It
61
+ * belongs to no lesson.
62
+ */
63
+ interface PartDeckFile extends DeckFileBase {
64
+ readonly scope: 'part';
65
+ }
66
+ /** A deck file: a lesson's slide edition, or a part's deck. */
67
+ type DeckFile = LessonDeckFile | PartDeckFile;
68
+ /**
69
+ * Every deck file, by slug: each lesson's, and each part's. A part's deck
70
+ * (`<part>/index.deck.mdx`) is never read as the deck of a lesson
71
+ * `<part>/index` whose chapter is the part's page.
72
+ */
73
+ declare function loadDeckFiles(): DeckFile[];
74
+ /**
75
+ * A file that defines a lesson: every chapter, and every deck whose lesson has
76
+ * no chapter (a slides-first lesson). Its frontmatter is the lesson's —
77
+ * `teaches`, `assumptions`, `studyMinutes`, `notation` — and its body is what
78
+ * the lesson's numbering and curriculum passes read.
79
+ */
80
+ interface LessonFile {
81
+ readonly path: string;
82
+ /** Docs slug of the file, extension included. */
83
+ readonly slug: string;
84
+ readonly lessonSlug: string;
85
+ readonly lessonId: string;
86
+ readonly edition: 'chapter' | 'deck';
87
+ }
88
+ /** Every lesson's defining file, sorted by lesson slug. */
89
+ declare function loadLessonFiles(): LessonFile[];
23
90
  /**
24
91
  * One part's **overview page** — `content/docs/<dir>/index.mdx`, the page the
25
92
  * part's own directory URL resolves to. `isLessonSlug` already excludes any
@@ -55,6 +122,26 @@ declare function loadAbbreviationRegistryInput(): Promise<readonly AbbreviationD
55
122
  * decoration (the page would contradict itself) each fail the load.
56
123
  */
57
124
  declare function notationSuspensions(raw: unknown, localDefinitions: readonly LocalNotationDefinitionInput[], file: string): NotationSuspension[];
125
+ /**
126
+ * A deck beside its chapter, as a page of the notation registry: its own id
127
+ * (`<lessonId>.slides`), no entries of its own, and its chapter's scope — the
128
+ * chapter's `notation.local` entries are the lesson's, so a deck names them
129
+ * as the chapter does, and it suspends what the chapter suspends. A part's
130
+ * deck (`<part>.slides`) has its part's scope: every chapter of the part, in
131
+ * the order the tracks read them, a key one chapter defines locally binding
132
+ * to the first that does; it suspends what any of them suspends. A deck that
133
+ * is a lesson's only edition is not here: it is that lesson's page
134
+ * (`loadNotationRegistryInput`).
135
+ */
136
+ interface DeckNotationPage {
137
+ readonly deck: DeckFile;
138
+ readonly data: Record<string, unknown>;
139
+ readonly notation: NotationLessonInput;
140
+ }
141
+ declare function loadDeckNotationPages(decks?: readonly DeckFile[], { order, }?: {
142
+ /** The lessons of a part in reading order (the tracks'); by slug without it. */
143
+ readonly order?: (lessonIds: readonly string[]) => readonly string[];
144
+ }): DeckNotationPage[];
58
145
  /**
59
146
  * A docs page that is **not a chapter** (rule S10): the landing page, a part's
60
147
  * overview page, and any other page outside a part directory — every slug
@@ -132,4 +219,4 @@ declare function notationSourceLocatorGaps(): {
132
219
  total: number;
133
220
  };
134
221
 
135
- export { type NonChapterPage, type NotationSpec, type PartOverviewPage, extractNotationReferences, loadAbbreviationRegistryInput, loadContentRecords, loadCurriculumCatalog, loadLessonChecks, loadNonChapterPages, loadNotationDefinitions, loadNotationRegistryInput, loadNotationSpecs, loadPartOverviews, loadSourceRecords, notationSourceLocatorGaps, notationSuspensions };
222
+ export { type DeckFile, type DeckNotationPage, type LessonDeckFile, type LessonFile, type NonChapterPage, type NotationSpec, type PartDeckFile, type PartOverviewPage, extractNotationReferences, loadAbbreviationRegistryInput, loadContentRecords, loadCurriculumCatalog, loadDeckFiles, loadDeckNotationPages, loadLessonChecks, loadLessonFiles, loadNonChapterPages, loadNotationDefinitions, loadNotationRegistryInput, loadNotationSpecs, loadPartOverviews, loadSourceRecords, notationSourceLocatorGaps, notationSuspensions };
@@ -16,11 +16,19 @@ import { resolveGlosses } from "../reference/gloss.js";
16
16
  import { indexEntryFormula } from "../reference/derived-notation.mjs";
17
17
  import { isIndexEntryLatex } from "./content-schemas.js";
18
18
  import {
19
+ deckLessonSlug,
20
+ deckPageId,
21
+ deckRouteSlug,
19
22
  deriveRequires,
20
23
  deriveSources,
24
+ isDeckSlug,
21
25
  isLessonSlug,
26
+ isPartDeckSlug,
22
27
  lessonIdFromSlug,
23
- parseCheckList
28
+ parseCheckList,
29
+ partDeckDir,
30
+ partDeckPageId,
31
+ partDeckRouteSlug
24
32
  } from "./lesson-derivation.js";
25
33
  const REPOSITORY_ROOT = process.cwd();
26
34
  const CONTENT_ROOT = join(REPOSITORY_ROOT, "content");
@@ -186,9 +194,81 @@ function docSlug(path) {
186
194
  return relative(join(CONTENT_ROOT, "docs"), path).replace(/\\/g, "/");
187
195
  }
188
196
  function lessonCheckIds(lessonPath) {
189
- const checksPath = lessonPath.replace(/\.mdx?$/, ".checks.yml");
197
+ const checksPath = lessonPath.replace(/(?:\.deck)?\.mdx?$/, ".checks.yml");
190
198
  return existsSync(checksPath) ? parseCheckList(readFileSync(checksPath, "utf8")) : [];
191
199
  }
200
+ function docsFiles() {
201
+ const root = join(CONTENT_ROOT, "docs");
202
+ return existsSync(root) ? filesBelow(root, [".md", ".mdx"]) : [];
203
+ }
204
+ function chapterPathOf(lessonSlug) {
205
+ for (const extension of [".mdx", ".md"]) {
206
+ const path = join(CONTENT_ROOT, "docs", `${lessonSlug}${extension}`);
207
+ if (existsSync(path)) return path;
208
+ }
209
+ return void 0;
210
+ }
211
+ function loadDeckFiles() {
212
+ const decks = [];
213
+ for (const path of docsFiles()) {
214
+ const slug = docSlug(path);
215
+ if (!isDeckSlug(slug)) continue;
216
+ const { file, data, body, bodyLine } = parseDocument(path);
217
+ if (isPartDeckSlug(slug)) {
218
+ const partDir = partDeckDir(slug);
219
+ decks.push({
220
+ scope: "part",
221
+ path,
222
+ file,
223
+ slug,
224
+ route: partDeckRouteSlug(partDir),
225
+ pageId: partDeckPageId(partDir),
226
+ partDir,
227
+ inPart: true,
228
+ data,
229
+ body,
230
+ bodyLine
231
+ });
232
+ continue;
233
+ }
234
+ const lessonSlug = deckLessonSlug(slug);
235
+ const lessonId = lessonIdFromSlug(lessonSlug);
236
+ const chapterPath = chapterPathOf(lessonSlug);
237
+ const hasChapter = chapterPath !== void 0;
238
+ decks.push({
239
+ scope: "lesson",
240
+ path,
241
+ file,
242
+ slug,
243
+ lessonSlug,
244
+ lessonId,
245
+ ...chapterPath === void 0 ? {} : { chapterPath },
246
+ route: deckRouteSlug(lessonSlug, hasChapter),
247
+ pageId: deckPageId(lessonId, hasChapter),
248
+ partDir: lessonSlug.includes("/") ? lessonSlug.slice(0, lessonSlug.indexOf("/")) : "",
249
+ inPart: lessonSlug.includes("/") && !lessonSlug.endsWith("/index"),
250
+ data,
251
+ body,
252
+ bodyLine
253
+ });
254
+ }
255
+ return decks;
256
+ }
257
+ function loadLessonFiles() {
258
+ const lessons = [];
259
+ for (const path of docsFiles()) {
260
+ const slug = docSlug(path);
261
+ if (isLessonSlug(slug)) {
262
+ const lessonSlug = slug.replace(/\.(md|mdx)$/, "");
263
+ lessons.push({ path, slug, lessonSlug, lessonId: lessonIdFromSlug(slug), edition: "chapter" });
264
+ } else if (isDeckSlug(slug) && slug.includes("/") && !isPartDeckSlug(slug)) {
265
+ const lessonSlug = deckLessonSlug(slug);
266
+ if (chapterPathOf(lessonSlug) !== void 0) continue;
267
+ lessons.push({ path, slug, lessonSlug, lessonId: lessonIdFromSlug(lessonSlug), edition: "deck" });
268
+ }
269
+ }
270
+ return lessons.sort((left, right) => left.lessonSlug.localeCompare(right.lessonSlug));
271
+ }
192
272
  function loadPartOverviews(parts) {
193
273
  const pages = [];
194
274
  for (const { dir } of parts) {
@@ -214,17 +294,19 @@ function loadPartOverviews(parts) {
214
294
  }
215
295
  function lessonEntries(prerequisitesById) {
216
296
  const lessons = [];
217
- for (const path of filesBelow(join(CONTENT_ROOT, "docs"), [".md", ".mdx"])) {
218
- const slug = docSlug(path);
219
- if (!isLessonSlug(slug)) continue;
297
+ const deckBodies = new Map(
298
+ loadDeckFiles().filter((deck) => deck.scope === "lesson" && deck.chapterPath !== void 0).map((deck) => [deck.lessonId, parseDocument(deck.path).body])
299
+ );
300
+ for (const { path, lessonId } of loadLessonFiles()) {
220
301
  const { data, body } = parseDocument(path);
221
302
  const teaches = data.teaches ?? [];
303
+ const deckBody = deckBodies.get(lessonId);
222
304
  lessons.push({
223
- id: lessonIdFromSlug(slug),
305
+ id: lessonId,
224
306
  requires: deriveRequires(teaches, prerequisitesById),
225
307
  teaches,
226
308
  assessments: lessonCheckIds(path),
227
- sources: deriveSources(body),
309
+ sources: deckBody === void 0 ? deriveSources(body) : [.../* @__PURE__ */ new Set([...deriveSources(body), ...deriveSources(deckBody)])].sort(),
228
310
  assumptions: data.assumptions ?? [],
229
311
  body
230
312
  });
@@ -317,7 +399,7 @@ function localDefinition(raw, file, index) {
317
399
  function sharedNotationEntries() {
318
400
  const directory = join(CONTENT_ROOT, "notation");
319
401
  return filesBelow(directory, [".md"]).map((path) => {
320
- const { file, data, body } = parseDocument(path);
402
+ const { file, data, body, bodyLine } = parseDocument(path);
321
403
  const key = string(data.key, `${file} key`);
322
404
  const filenameKey = basename(path, ".md");
323
405
  if (key !== filenameKey) {
@@ -339,6 +421,7 @@ function sharedNotationEntries() {
339
421
  alignment: data.alignment === void 0 ? { kind: "general", rationale: "General notation." } : alignment(data.alignment, `${file} alignment`),
340
422
  aiAssisted: data.aiAssisted === void 0 ? false : boolean(data.aiAssisted, `${file} aiAssisted`),
341
423
  body,
424
+ bodyLine,
342
425
  references: extractNotationReferences(body, file, void 0),
343
426
  glosses: glossEntries(data.glosses, key, `${file} glosses`),
344
427
  source: { file },
@@ -403,21 +486,26 @@ function notationSuspensions(raw, localDefinitions, file) {
403
486
  return reason === void 0 ? { operator } : { operator, reason: reason.trim() };
404
487
  });
405
488
  }
489
+ function lessonNotationOf(file, data) {
490
+ const notation = record(data.notation ?? {}, `${file} notation`);
491
+ const local = Array.isArray(notation.local) ? notation.local : [];
492
+ const localDefinitions = local.map(
493
+ (entry, index) => localDefinition(entry, file, index)
494
+ );
495
+ return {
496
+ localDefinitions,
497
+ suspended: notationSuspensions(notation.suspend, localDefinitions, file)
498
+ };
499
+ }
406
500
  function notationLessonEntries() {
407
501
  const lessons = [];
408
- for (const path of filesBelow(join(CONTENT_ROOT, "docs"), [".md", ".mdx"])) {
409
- const slug = docSlug(path);
410
- if (!isLessonSlug(slug)) continue;
502
+ for (const { path, lessonId } of loadLessonFiles()) {
411
503
  const { file, data, body, bodyLine } = parseDocument(path);
412
- const notation = record(data.notation ?? {}, `${file} notation`);
413
- const local = Array.isArray(notation.local) ? notation.local : [];
414
- const localDefinitions = local.map(
415
- (entry, index) => localDefinition(entry, file, index)
416
- );
504
+ const { localDefinitions, suspended } = lessonNotationOf(file, data);
417
505
  lessons.push({
418
- lessonId: lessonIdFromSlug(slug),
506
+ lessonId,
419
507
  localDefinitions,
420
- suspended: notationSuspensions(notation.suspend, localDefinitions, file),
508
+ suspended,
421
509
  references: extractNotationReferences(body, file, void 0),
422
510
  body,
423
511
  bodyLine,
@@ -426,11 +514,68 @@ function notationLessonEntries() {
426
514
  }
427
515
  return lessons;
428
516
  }
517
+ function loadDeckNotationPages(decks = loadDeckFiles(), {
518
+ order = (lessonIds) => lessonIds
519
+ } = {}) {
520
+ const pages = [];
521
+ const chapterFiles = loadLessonFiles().filter((lesson) => lesson.edition === "chapter");
522
+ for (const deck of decks) {
523
+ let chapters;
524
+ if (deck.scope === "part") {
525
+ const inPart = chapterFiles.filter((lesson) => lesson.lessonSlug.startsWith(`${deck.partDir}/`));
526
+ const pathById = new Map(inPart.map((lesson) => [lesson.lessonId, lesson.path]));
527
+ chapters = order(inPart.map((lesson) => lesson.lessonId)).flatMap((lessonId) => {
528
+ const path = pathById.get(lessonId);
529
+ return path === void 0 ? [] : [{ lessonId, path }];
530
+ });
531
+ } else if (deck.chapterPath !== void 0) {
532
+ chapters = [{ lessonId: deck.lessonId, path: deck.chapterPath }];
533
+ } else {
534
+ continue;
535
+ }
536
+ const inheritedLocalDefinitions = [];
537
+ const suspended = [];
538
+ const inheritedKeys = /* @__PURE__ */ new Set();
539
+ for (const { path } of chapters) {
540
+ const chapter = parseDocument(path);
541
+ const notation = lessonNotationOf(chapter.file, chapter.data);
542
+ for (const definition of notation.localDefinitions) {
543
+ if (!inheritedLocalDefinitions.some((known) => known.key === definition.key)) {
544
+ inheritedLocalDefinitions.push(definition);
545
+ }
546
+ }
547
+ for (const suspension of notation.suspended) {
548
+ if (!suspended.some((known) => known.operator === suspension.operator)) suspended.push(suspension);
549
+ }
550
+ for (const reference of extractNotationReferences(chapter.body, chapter.file, void 0)) {
551
+ inheritedKeys.add(reference.key);
552
+ }
553
+ }
554
+ const { file, data, body, bodyLine } = deck;
555
+ pages.push({
556
+ deck,
557
+ data,
558
+ notation: {
559
+ lessonId: deck.pageId,
560
+ localDefinitions: [],
561
+ suspended,
562
+ scopeLessonIds: chapters.map((chapter) => chapter.lessonId),
563
+ inheritedLocalDefinitions,
564
+ inheritedKeys: [...inheritedKeys].sort(),
565
+ references: extractNotationReferences(body, file, void 0),
566
+ body,
567
+ bodyLine,
568
+ source: { file }
569
+ }
570
+ });
571
+ }
572
+ return pages;
573
+ }
429
574
  function loadNonChapterPages() {
430
575
  const pages = [];
431
- for (const path of filesBelow(join(CONTENT_ROOT, "docs"), [".md", ".mdx"])) {
576
+ for (const path of docsFiles()) {
432
577
  const slug = docSlug(path);
433
- if (isLessonSlug(slug)) continue;
578
+ if (isLessonSlug(slug) || isDeckSlug(slug)) continue;
434
579
  const { file, data, body, bodyLine } = parseDocument(path);
435
580
  const id = lessonIdFromSlug(slug);
436
581
  pages.push({
@@ -500,7 +645,7 @@ function loadSourceRecords() {
500
645
  }
501
646
  function loadLessonChecks(lessonId) {
502
647
  const slug = lessonId.replace(/\./g, "/");
503
- for (const extension of [".mdx", ".md"]) {
648
+ for (const extension of [".mdx", ".md", ".deck.mdx", ".deck.md"]) {
504
649
  const lessonPath = join(CONTENT_ROOT, "docs", `${slug}${extension}`);
505
650
  if (existsSync(lessonPath)) return lessonCheckIds(lessonPath);
506
651
  }
@@ -534,11 +679,8 @@ function loadNotationSpecs() {
534
679
  sources: specSources(data.sources)
535
680
  });
536
681
  }
537
- for (const path of filesBelow(join(CONTENT_ROOT, "docs"), [".md", ".mdx"])) {
538
- const slug = docSlug(path);
539
- if (!isLessonSlug(slug)) continue;
682
+ for (const { path, lessonId } of loadLessonFiles()) {
540
683
  const { file, data } = parseDocument(path);
541
- const lessonId = lessonIdFromSlug(slug);
542
684
  const local = data.notation?.local;
543
685
  if (!Array.isArray(local)) continue;
544
686
  for (const raw of local) {
@@ -576,7 +718,10 @@ export {
576
718
  loadAbbreviationRegistryInput,
577
719
  loadContentRecords,
578
720
  loadCurriculumCatalog,
721
+ loadDeckFiles,
722
+ loadDeckNotationPages,
579
723
  loadLessonChecks,
724
+ loadLessonFiles,
580
725
  loadNonChapterPages,
581
726
  loadNotationDefinitions,
582
727
  loadNotationRegistryInput,
@@ -1,3 +1,3 @@
1
- export { s as BLOCK_LABELS_DEFAULT, B as BlockLabels, t as CARDS_DEFAULTS, h as CourseAbbreviations, u as CourseAbout, g as CourseAccents, v as CourseAuthor, w as CourseCards, C as CourseConfig, x as CourseDisclosure, y as CourseHeadingDisclosure, z as CourseNumbering, A as CourseOpenDefault, e as CourseOperators, D as CoursePart, f as CourseRomanLabels, E as CourseSection, F as DISCLOSURE_DEFAULTS, L as LessonExportsConfig, c as NormalizedCards, a as NormalizedDisclosure, N as NormalizedLessonExports, b as NormalizedNumbering, i as NormalizedPartPages, G as coursePartsOf, H as normalizeBlockLabels, I as normalizeCards, J as normalizeDisclosure, K as normalizeLessonExports, O as normalizeNumbering } from '../course-config-rRtME5UV.js';
1
+ export { A as APPEARANCE_DEFAULTS, x as AppearanceSlides, y as AppearanceZones, z as BLOCK_LABELS_DEFAULT, B as BlockLabels, D as CARDS_DEFAULTS, l as CourseAbbreviations, E as CourseAbout, k as CourseAccents, F as CourseAppearance, G as CourseAuthor, H as CourseCards, C as CourseConfig, I as CourseDeck, J as CourseDisclosure, K as CourseHeadingDisclosure, L as CourseNumbering, O as CourseOpenDefault, i as CourseOperators, P as CoursePart, j as CourseRomanLabels, Q as CourseSection, S as CourseSidebarEditions, T as CourseSidebarMarks, U as DECK_CANVAS, V as DECK_DEFAULTS, W as DISCLOSURE_DEFAULTS, X as DeckAspectRatio, Y as DeckLegend, Z as DeckNotes, _ as LessonExportsConfig, f as NormalizedAppearance, c as NormalizedCards, g as NormalizedDeck, a as NormalizedDisclosure, N as NormalizedLessonExports, b as NormalizedNumbering, m as NormalizedPartPages, e as NormalizedSidebarEditions, d as NormalizedSidebarMarks, R as ReadingMemorySetting, $ as SIDEBAR_EDITIONS_DEFAULTS, a0 as SIDEBAR_MARKS_DEFAULTS, a1 as SidebarEditionLead, a2 as SidebarEditionOther, a3 as coursePartsOf, a4 as normalizeAppearance, a5 as normalizeBlockLabels, a6 as normalizeCards, a7 as normalizeDeck, a8 as normalizeDisclosure, a9 as normalizeLessonExports, aa as normalizeNumbering, ab as normalizeReadingMemory, ac as normalizeSidebarEditions, ad as normalizeSidebarMarks } from '../course-config-BLtIdkei.js';
2
2
  import '../rules/course-rules.js';
3
3
  import '../rules/catalog.js';
@@ -83,6 +83,100 @@ function normalizeCards(config) {
83
83
  }
84
84
  return { carry };
85
85
  }
86
+ const SIDEBAR_MARKS_DEFAULTS = { unopened: true, changed: true };
87
+ function normalizeSidebarMarks(config) {
88
+ const marks = config.sidebarMarks ?? {};
89
+ const read = (field) => {
90
+ const value = marks[field] ?? SIDEBAR_MARKS_DEFAULTS[field];
91
+ if (typeof value !== "boolean") {
92
+ throw new Error(
93
+ `CourseConfig sidebarMarks.${field} must be true or false; received ${JSON.stringify(value)}.`
94
+ );
95
+ }
96
+ return value;
97
+ };
98
+ return { unopened: read("unopened"), changed: read("changed") };
99
+ }
100
+ const SIDEBAR_EDITIONS_DEFAULTS = { lead: "chapter", other: "listed" };
101
+ function normalizeSidebarEditions(config) {
102
+ const editions = config.sidebarEditions ?? {};
103
+ const choice = (field, allowed) => {
104
+ const value = editions[field] ?? SIDEBAR_EDITIONS_DEFAULTS[field];
105
+ if (typeof value !== "string" || !allowed.includes(value)) {
106
+ throw new Error(
107
+ `CourseConfig sidebarEditions.${field} must be one of ${allowed.map((item) => JSON.stringify(item)).join(", ")}; received ${JSON.stringify(value)}.`
108
+ );
109
+ }
110
+ return value;
111
+ };
112
+ return {
113
+ lead: choice("lead", ["chapter", "slides"]),
114
+ other: choice("other", ["listed", "hidden"])
115
+ };
116
+ }
117
+ const DECK_CANVAS = {
118
+ "16:9": { width: 800, height: 450 },
119
+ "16:10": { width: 720, height: 450 },
120
+ "4:3": { width: 600, height: 450 }
121
+ };
122
+ const DECK_DEFAULTS = {
123
+ aspectRatio: "16:9",
124
+ legend: "first-use",
125
+ notes: "reader",
126
+ outcomesFrame: true,
127
+ handoutPdf: true
128
+ };
129
+ function normalizeDeck(config) {
130
+ const deck = config.deck ?? {};
131
+ const choice = (field, allowed) => {
132
+ const value = deck[field] ?? DECK_DEFAULTS[field];
133
+ if (typeof value !== "string" || !allowed.includes(value)) {
134
+ throw new Error(
135
+ `CourseConfig deck.${field} must be one of ${allowed.map((item) => JSON.stringify(item)).join(", ")}; received ${JSON.stringify(value)}.`
136
+ );
137
+ }
138
+ return value;
139
+ };
140
+ const flag = (field) => {
141
+ const value = deck[field] ?? DECK_DEFAULTS[field];
142
+ if (typeof value !== "boolean") {
143
+ throw new Error(`CourseConfig deck.${field} must be true or false; received ${JSON.stringify(value)}.`);
144
+ }
145
+ return value;
146
+ };
147
+ const aspectRatio = choice("aspectRatio", ["16:9", "16:10", "4:3"]);
148
+ return {
149
+ aspectRatio,
150
+ legend: choice("legend", ["first-use", "every-frame", "off"]),
151
+ notes: choice("notes", ["reader", "presenter", "off"]),
152
+ outcomesFrame: flag("outcomesFrame"),
153
+ handoutPdf: flag("handoutPdf"),
154
+ canvas: DECK_CANVAS[aspectRatio]
155
+ };
156
+ }
157
+ function normalizeReadingMemory(config) {
158
+ const value = config.readingMemory ?? "ask";
159
+ if (value !== "ask" && value !== "off") {
160
+ throw new Error(`CourseConfig readingMemory must be "ask" or "off"; received ${JSON.stringify(value)}.`);
161
+ }
162
+ return value;
163
+ }
164
+ const APPEARANCE_DEFAULTS = { zones: "tonal", slides: "lifted" };
165
+ const APPEARANCE_ZONES = ["tonal", "accent-header", "paper", "none"];
166
+ const APPEARANCE_SLIDES = ["lifted", "edge", "sorter"];
167
+ function normalizeAppearance(config) {
168
+ const appearance = config.appearance ?? {};
169
+ const choice = (field, allowed) => {
170
+ const value = appearance[field] ?? APPEARANCE_DEFAULTS[field];
171
+ if (!allowed.includes(value)) {
172
+ throw new Error(
173
+ `CourseConfig appearance.${field} must be one of ${allowed.map((v) => `"${v}"`).join(", ")}; received ${JSON.stringify(value)}.`
174
+ );
175
+ }
176
+ return value;
177
+ };
178
+ return { zones: choice("zones", APPEARANCE_ZONES), slides: choice("slides", APPEARANCE_SLIDES) };
179
+ }
86
180
  function normalizeLessonExports(config) {
87
181
  const standaloneHtml = config.lessonExports?.standaloneHtml === true;
88
182
  const print = config.lessonExports?.print === true;
@@ -135,13 +229,23 @@ function coursePartsOf(config) {
135
229
  return parts;
136
230
  }
137
231
  export {
232
+ APPEARANCE_DEFAULTS,
138
233
  BLOCK_LABELS_DEFAULT,
139
234
  CARDS_DEFAULTS,
235
+ DECK_CANVAS,
236
+ DECK_DEFAULTS,
140
237
  DISCLOSURE_DEFAULTS,
238
+ SIDEBAR_EDITIONS_DEFAULTS,
239
+ SIDEBAR_MARKS_DEFAULTS,
141
240
  coursePartsOf,
241
+ normalizeAppearance,
142
242
  normalizeBlockLabels,
143
243
  normalizeCards,
244
+ normalizeDeck,
144
245
  normalizeDisclosure,
145
246
  normalizeLessonExports,
146
- normalizeNumbering
247
+ normalizeNumbering,
248
+ normalizeReadingMemory,
249
+ normalizeSidebarEditions,
250
+ normalizeSidebarMarks
147
251
  };