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
@@ -0,0 +1,92 @@
1
+ import { Manifest, ManifestDeck } from '../../manifest.js';
2
+ import { StaticDeck, NotationEntry } from '../static-tree.js';
3
+ import { ChapterContext } from './serialize.js';
4
+ import { SourceFile } from './document.js';
5
+ import '../../../curriculum/validation.js';
6
+ import '../../../reference/consistency.js';
7
+ import '../../../course-config-BLtIdkei.js';
8
+ import '../../../rules/catalog.js';
9
+ import '../../../rules/course-rules.js';
10
+ import '../../collections.js';
11
+ import '../../../reference/types.js';
12
+ import '../../../reference/gate-retired-keys.js';
13
+ import '../../../reference/lint-ignore.js';
14
+ import '../../../reference/equations-validate.js';
15
+ import '../../../reference/tables-validate.js';
16
+ import '../../../reference/keyed-float-validate.js';
17
+ import '../../../reference/figures-validate.js';
18
+ import '../../../reference/diagrams-validate.js';
19
+ import '../../../reference/examples-validate.js';
20
+ import '../../../reference/folds-validate.js';
21
+ import '../../../reference/knowledge-checks-validate.js';
22
+ import '../../../reference/raw-floats-validate.js';
23
+ import '../../../reference/lab-katex-validate.js';
24
+ import '../../../reference/gate-math.js';
25
+ import '../../../reference/mixed-content.js';
26
+ import '../../../reference/gloss.js';
27
+ import '../../../rules/waivers.js';
28
+ import '../../knowledge-version.js';
29
+ import '../../page-versions.js';
30
+ import '../../decks.js';
31
+ import '../../../reference/decks-validate.js';
32
+ import '../../../reference/deck-source.js';
33
+ import '../../lesson-derivation.js';
34
+ import '../html-tree.js';
35
+ import 'parse5';
36
+
37
+ /** `beamerthemeexplico.sty`, read next to this module. */
38
+ declare function deckTheme(): string;
39
+ /**
40
+ * The notation keys a deck's LaTeX names, in order of first use: in a
41
+ * formula (`\explain{key}{…}`) or in prose (`\ExplicoTerm{key}{…}`).
42
+ */
43
+ declare function deckNotationKeys(tex: string): string[];
44
+ /**
45
+ * The symbols a deck's frames use, in order of first use, from its page's
46
+ * notation list: its appendix in every download.
47
+ */
48
+ declare function deckSymbols(context: Omit<ChapterContext, 'page'> & {
49
+ readonly deck: StaticDeck;
50
+ }): NotationEntry[];
51
+ interface DeckSerialization {
52
+ /** `deck.tex`: every frame, in order. */
53
+ readonly tex: string;
54
+ /** The line of `deck.tex` each frame ends on, by frame number: a paper overflow names its frame by it. */
55
+ readonly frameEnds: ReadonlyMap<number, number>;
56
+ }
57
+ /**
58
+ * The frames of a deck in beamer, then the notation appendix of the symbols
59
+ * they use (`appendix`, each linked from its uses through `notationTargets`).
60
+ */
61
+ declare function serializeDeck(context: Omit<ChapterContext, 'page'> & {
62
+ readonly deck: StaticDeck;
63
+ readonly appendix?: readonly NotationEntry[];
64
+ }): DeckSerialization;
65
+ interface DeckSourceInput {
66
+ readonly manifest: Manifest;
67
+ readonly deck: ManifestDeck;
68
+ readonly page: StaticDeck;
69
+ readonly checkItems: ChapterContext['checkItems'];
70
+ readonly pictures: ReadonlyMap<string, {
71
+ readonly path: string;
72
+ readonly content: Uint8Array;
73
+ }>;
74
+ readonly scripts?: boolean;
75
+ }
76
+ /** A deck's LaTeX folder, and where each of its frames ends in `deck.tex`. */
77
+ declare function deckSource(input: DeckSourceInput): {
78
+ files: SourceFile[];
79
+ frameEnds: ReadonlyMap<number, number>;
80
+ };
81
+ /**
82
+ * The frames LaTeX numbered differently from the deck page: each frame's
83
+ * `\label{frame:N}` must carry frame number N.
84
+ */
85
+ declare function deckNumberingProblems(aux: string, frames: number): string[];
86
+ /** The frames a beamer log says run over their slide, by `deck.tex` line: `{ frame, points }`. */
87
+ declare function deckOverflows(log: string, frameEnds: ReadonlyMap<number, number>): {
88
+ frame: number;
89
+ points: number;
90
+ }[];
91
+
92
+ export { type DeckSerialization, type DeckSourceInput, deckNotationKeys, deckNumberingProblems, deckOverflows, deckSource, deckSymbols, deckTheme, serializeDeck };
@@ -0,0 +1,349 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { escapeText, ChapterWriter, labelOf, linkExplains } from "./serialize.js";
3
+ import { versionPhrase } from "../../page-versions.js";
4
+ import { explicoCommon, needsScripts, trailerId } from "./document.js";
5
+ function deckTheme() {
6
+ return readFileSync(new URL("./beamerthemeexplico.sty", import.meta.url), "utf8");
7
+ }
8
+ const ASPECT = { "16:9": "169", "16:10": "1610", "4:3": "43" };
9
+ function deckPage(deck) {
10
+ return {
11
+ slug: deck.slug,
12
+ route: deck.route,
13
+ title: deck.title,
14
+ blocks: deck.frames.flatMap((frame) => [...frame.segments.flatMap((segment) => segment.blocks), ...frame.notes]),
15
+ notation: [],
16
+ notationRemarks: []
17
+ };
18
+ }
19
+ class DeckWriter extends ChapterWriter {
20
+ inline(inline) {
21
+ if (inline.kind === "cite") return `[${escapeText(inline.number)}]`;
22
+ return super.inline(inline);
23
+ }
24
+ /** A float's caption on a frame: its number (the chapter's, when recalled), its short and long caption. */
25
+ deckCaption(kind, block, extra = []) {
26
+ const number = block.number ? `${kind}~${escapeText(block.number)}` : "";
27
+ const short = block.short?.trim() ? escapeText(block.short.trim().replace(/\.$/u, "")) : "";
28
+ const long = [this.plain(block.caption), this.plain(extra)].filter(Boolean).join(" ");
29
+ return `\\ExplicoDeckCaption{${number}}{${short}}{${long}}`;
30
+ }
31
+ block(block, inBox = false) {
32
+ switch (block.kind) {
33
+ case "heading":
34
+ return `\\par\\textbf{${this.plain(block.children)}}\\par`;
35
+ case "equation":
36
+ return `\\begin{equation*}
37
+ ${this.math(block.tex)}
38
+ \\tag{${escapeText(block.number)}}
39
+ \\end{equation*}`;
40
+ case "list": {
41
+ const environment = block.ordered ? "enumerate" : "itemize";
42
+ const items = block.items.map((item, index) => {
43
+ const step = block.itemSteps?.[index] ?? 1;
44
+ return `\\item${step > 1 ? `<${step}->` : ""} ${this.blocks(item, inBox)}`;
45
+ });
46
+ return `\\begin{${environment}}
47
+ ${items.join("\n")}
48
+ \\end{${environment}}`;
49
+ }
50
+ case "figure": {
51
+ const alt = block.alt ?? [block.short, ...block.caption.map((inline) => "value" in inline ? inline.value : "")].filter(Boolean).join(" ");
52
+ const picture = this.picture(block.anchor, block.picture, alt).replace("max height=0.6\\textheight", "max height=0.62\\textheight");
53
+ return `\\begin{center}
54
+ ${picture}
55
+ ${this.deckCaption("Figure", block, block.live)}
56
+ \\end{center}`;
57
+ }
58
+ case "diagram":
59
+ return [
60
+ "\\begin{center}",
61
+ "\\begin{adjustbox}{max width=\\linewidth,max height=0.62\\textheight}",
62
+ this.unlinkedWhile(() => this.picture(block.anchor, { kind: "diagram", geometry: block.geometry }, "")),
63
+ "\\end{adjustbox}",
64
+ this.deckCaption("Diagram", block),
65
+ "\\end{center}"
66
+ ].join("\n");
67
+ case "table": {
68
+ const tabular = this.table({ ...block, number: "" }, true);
69
+ return `\\begin{center}
70
+ ${this.deckCaption("Table", block)}
71
+ \\smallskip
72
+ ${tabular}
73
+ \\end{center}`;
74
+ }
75
+ case "memo":
76
+ return [`\\begin{block}{${this.plain(block.title)}}`, this.blocks(block.children, true), "\\end{block}"].join("\n");
77
+ case "note":
78
+ return [`\\begin{block}{${escapeText(block.title)}}`, this.blocks(block.children, true), "\\end{block}"].join("\n");
79
+ case "aside": {
80
+ const environment = block.variant === "caution" || block.variant === "danger" ? "alertblock" : "block";
81
+ return [`\\begin{${environment}}{${this.plain(block.title)}}`, this.blocks(block.children, true), `\\end{${environment}}`].join("\n");
82
+ }
83
+ case "examples":
84
+ return block.items.map((item) => [`\\begin{block}{${this.plain(item.title)}}`, this.blocks(item.children, true), "\\end{block}"].join("\n")).join("\n");
85
+ case "check": {
86
+ const items = this.context.checkItems.get(block.assessmentId) ?? [];
87
+ const questions = items.map((item) => {
88
+ const options = item.type === "single-choice" && item.options && item.options.length > 0 ? `
89
+ \\begin{enumerate}
90
+ ${item.options.map((option) => `\\item[(${String.fromCharCode(97 + item.options.indexOf(option))})] ${this.optionLabel(option)}`).join("\n")}
91
+ \\end{enumerate}` : "";
92
+ return `\\item ${this.segments(item.promptSegments)}${options}`;
93
+ });
94
+ return [
95
+ `\\par{\\usebeamercolor[fg]{structure}\\textbf{Knowledge check ${escapeText(block.number)}}}\\enspace ${this.plain(block.title)}\\par`,
96
+ ...questions.length > 0 ? ["\\begin{enumerate}", ...questions, "\\end{enumerate}"] : []
97
+ ].join("\n");
98
+ }
99
+ default:
100
+ return super.block(block, inBox);
101
+ }
102
+ }
103
+ /** A check's answers, for the frame's notes. */
104
+ checkAnswers(block) {
105
+ const items = this.context.checkItems.get(block.assessmentId) ?? [];
106
+ return items.map((item) => {
107
+ if (item.type === "single-choice" && item.options) {
108
+ const index = item.options.findIndex((option) => option.id === item.correctOptionId);
109
+ const answer = index === -1 ? "" : `(${String.fromCharCode(97 + index)}) ${this.optionLabel(item.options[index])}. `;
110
+ return `Answer: ${answer}${this.segments(item.explanationSegments)}`;
111
+ }
112
+ if (item.type === "numeric" && item.answer) {
113
+ return `Answer: $${item.answer.value}$ (within $\\pm${item.answer.tolerance}$). ${this.segments(item.explanationSegments)}`;
114
+ }
115
+ return this.segments(item.explanationSegments);
116
+ }).join("\\par ");
117
+ }
118
+ frameTitle(frame) {
119
+ return this.plain(frame.title);
120
+ }
121
+ /** A legend line's symbol, linked to its appendix entry. */
122
+ legendSymbol(tex) {
123
+ return this.math(tex);
124
+ }
125
+ /**
126
+ * The notation appendix: each symbol the deck uses, by first use, with its
127
+ * name, what it is, its units and its defining formula, as the chapter's
128
+ * notation lists it. It breaks over as many frames as it needs.
129
+ */
130
+ notationFrames(entries) {
131
+ if (entries.length === 0) return "";
132
+ const targets = new Set(entries.map((entry) => entry.key));
133
+ const items = entries.map((entry) => {
134
+ const formula = entry.formulaTex ? linkExplains(entry.formulaTex, (named) => named !== entry.key && targets.has(named) ? labelOf("notation", named) : void 0) : void 0;
135
+ const parts = [
136
+ `\\textbf{${this.inlines(entry.label)}}.`,
137
+ this.inlines(entry.summary),
138
+ ...entry.units ? [`\\emph{Units:} ${this.inlines(entry.units)}.`] : [],
139
+ ...formula ? [`$${formula}$`] : []
140
+ ];
141
+ return `\\item[{${entry.symbolTex ? `$${entry.symbolTex}$` : ""}}]\\hypertarget{${labelOf("notation", entry.key)}}{}${parts.join(" ")}`;
142
+ });
143
+ return [
144
+ "\\appendix",
145
+ "\\section{Notation}",
146
+ "\\begin{frame}[allowframebreaks,t]{Notation}",
147
+ "\\small",
148
+ "\\begin{description}",
149
+ ...items,
150
+ "\\end{description}",
151
+ "\\end{frame}"
152
+ ].join("\n");
153
+ }
154
+ }
155
+ function deckNotationKeys(tex) {
156
+ const keys = /* @__PURE__ */ new Set();
157
+ for (const match of tex.matchAll(/\\(?:explain|ExplicoTerm)\{([^{}]+)\}\{/gu)) keys.add(match[1]);
158
+ return [...keys];
159
+ }
160
+ function deckSymbols(context) {
161
+ const byKey = new Map(context.deck.notation.map((entry) => [entry.key, entry]));
162
+ return deckNotationKeys(serializeDeck(context).tex).flatMap((key) => {
163
+ const entry = byKey.get(key);
164
+ return entry ? [entry] : [];
165
+ });
166
+ }
167
+ function checksOf(blocks) {
168
+ const out = [];
169
+ for (const block of blocks) {
170
+ if (block.kind === "check") out.push(block);
171
+ if ("children" in block && Array.isArray(block.children) && block.kind !== "paragraph" && block.kind !== "heading") {
172
+ out.push(...checksOf(block.children));
173
+ }
174
+ }
175
+ return out;
176
+ }
177
+ function hasCode(blocks) {
178
+ return blocks.some(
179
+ (block) => block.kind === "code" || "children" in block && Array.isArray(block.children) && block.kind !== "paragraph" && block.kind !== "heading" && hasCode(block.children)
180
+ );
181
+ }
182
+ function serializeDeck(context) {
183
+ const writer = new DeckWriter({ ...context, page: deckPage(context.deck) });
184
+ const lines = [];
185
+ const frameEnds = /* @__PURE__ */ new Map();
186
+ const push = (text) => lines.push(...text.split("\n"));
187
+ push(`\\ExplicoDeckFrames{${context.deck.frames.length}}`);
188
+ lines.push("");
189
+ for (const frame of context.deck.frames) {
190
+ const label = `\\label{frame:${frame.number}}`;
191
+ if (frame.kind === "title") {
192
+ push(`\\begin{frame}[plain]${label}
193
+ \\titlepage
194
+ \\end{frame}`);
195
+ } else if (frame.kind === "section") {
196
+ push(`\\section{${writer.frameTitle(frame)}}
197
+ \\begin{frame}${label}
198
+ \\ExplicoSectionPage
199
+ \\end{frame}`);
200
+ } else {
201
+ const blocks = frame.segments.flatMap((segment) => segment.blocks);
202
+ const options = hasCode(blocks) ? "[fragile]" : "";
203
+ const body = frame.segments.map((segment) => {
204
+ const content = writer.blocks(segment.blocks);
205
+ return segment.step > 1 ? `\\begin{uncoverenv}<${segment.step}->
206
+ ${content}
207
+ \\end{uncoverenv}` : content;
208
+ });
209
+ const legend = frame.legend.filter((item) => item.tex);
210
+ const answers = checksOf(blocks).map((check) => writer.checkAnswers(check)).filter(Boolean);
211
+ const notes = [writer.blocks(frame.notes), ...answers].filter((text) => text.trim().length > 0);
212
+ push(
213
+ [
214
+ `\\begin{frame}${options}{${writer.frameTitle(frame)}}${label}`,
215
+ ...body,
216
+ ...legend.length > 0 ? [`\\ExplicoLegend{${legend.map((item) => `$${writer.legendSymbol(item.tex)}$~${escapeText(item.label.map((inline) => "value" in inline ? inline.value : "").join(""))}`).join("\\quad ")}}`] : [],
217
+ ...notes.length > 0 ? [`\\note{${notes.join("\n\n")}}`] : [],
218
+ "\\end{frame}"
219
+ ].join("\n")
220
+ );
221
+ }
222
+ frameEnds.set(frame.number, lines.length);
223
+ lines.push("");
224
+ }
225
+ const appendix = writer.notationFrames(context.appendix ?? []);
226
+ if (appendix) push(`${appendix}
227
+ `);
228
+ return { tex: `${lines.join("\n")}
229
+ `, frameEnds };
230
+ }
231
+ const LATEXMKRC = [
232
+ "# LuaLaTeX by default; `latexmk -pdf` uses pdfLaTeX instead.",
233
+ "$pdf_mode = 4;",
234
+ "$postscript_mode = $dvi_mode = 0;",
235
+ "# luaotfload rewrites its font caches as fonts are looked up; they are not",
236
+ "# this document's sources, so a rewrite is no reason to run again.",
237
+ "$hash_calc_ignore_pattern{'luc'} = '^';",
238
+ ""
239
+ ].join("\n");
240
+ function deckMain(manifest, deck, handout, scripts) {
241
+ const ratio = ASPECT[manifest.deck?.aspectRatio ?? "16:9"] ?? "169";
242
+ const course = manifest.about.material.name;
243
+ const authors = (manifest.about.authors ?? []).map((author) => author.name).filter(Boolean).join(", ");
244
+ const version = versionPhrase(deck.since, manifest.about.knowledge.version);
245
+ const document = `${deck.slug}${handout ? "#handout" : ""}`;
246
+ const trailer = trailerId(manifest, document, deck.since);
247
+ return [
248
+ `% The deck's ${handout ? "handout: each slide followed by its notes" : "slides"}. Compile with \`latexmk${handout ? " handout.tex" : ""}\` (LuaLaTeX, see latexmkrc).`,
249
+ "% A fixed seed for TeX's random numbers: the same source gives the same PDF.",
250
+ "\\ExplSyntaxOn \\sys_gset_rand_seed:n { 1 } \\ExplSyntaxOff",
251
+ // usepdftitle=false: the title below is the PDF's, not beamer's "title - subtitle".
252
+ `\\documentclass[aspectratio=${ratio},11pt,usepdftitle=false${handout ? ",handout" : ""}]{beamer}`,
253
+ `\\usetheme${scripts ? "[scripts]" : ""}{explico}`,
254
+ ...handout ? ["\\setbeameroption{show notes}"] : [],
255
+ `\\ifdefined\\directlua\\pdfvariable trailerid {[<${trailer}><${trailer}>]}\\else\\pdftrailerid{${trailer}}\\fi`,
256
+ `\\title{${escapeText(deck.title)}}`,
257
+ `\\subtitle{${escapeText(course)}}`,
258
+ `\\author{${escapeText(authors || course)}}`,
259
+ `\\date{Content ${escapeText(version)}}`,
260
+ `\\hypersetup{pdftitle={${escapeText(deck.title)}},pdfauthor={${escapeText(authors || course)}},pdfsubject={${escapeText(course)}}}`,
261
+ "\\begin{document}",
262
+ "\\input{deck}",
263
+ "\\end{document}",
264
+ ""
265
+ ].join("\n");
266
+ }
267
+ function deckReadme(manifest, deck, url) {
268
+ const course = manifest.about.material.name;
269
+ return [
270
+ `# ${deck.title} \u2014 slides`,
271
+ "",
272
+ `The LaTeX source of a deck of *${course}*, ${versionPhrase(deck.since, manifest.about.knowledge.version)}.` + (url ? ` Its interactive version: ${url}` : ""),
273
+ "",
274
+ "- `main.tex` \u2014 the slides: `latexmk` builds `main.pdf`.",
275
+ "- `handout.tex` \u2014 each slide followed by its speaker notes: `latexmk handout.tex`.",
276
+ "- `deck.tex` \u2014 the frames, which both read.",
277
+ "- `beamerthemeexplico.sty` \u2014 explico's beamer theme: the legend line, the frame number, the progress bar.",
278
+ "- `explico-common.sty` \u2014 what the theme shares with explico's book class: fonts, colours, figures,",
279
+ " diagrams and the notation markers.",
280
+ "- `figures/` \u2014 the deck's figures, as vector PDF where the website draws them.",
281
+ "- `latexmkrc` \u2014 LuaLaTeX by default; `latexmk -pdf` uses pdfLaTeX.",
282
+ "",
283
+ "Beamer cannot write a tagged PDF: these PDFs are not accessible to a screen reader. The deck's",
284
+ "web page and its chapter's PDF are.",
285
+ ""
286
+ ].join("\n");
287
+ }
288
+ function deckSource(input) {
289
+ const { manifest, deck, page } = input;
290
+ const base = manifest.lessonExports.canonicalBaseUrl;
291
+ const url = base ? `${base}${deck.slug}/` : void 0;
292
+ const context = {
293
+ deck: page,
294
+ documentRoutes: /* @__PURE__ */ new Set([page.route]),
295
+ ...base ? { canonicalBaseUrl: base } : {},
296
+ pictures: new Map([...input.pictures].map(([anchor, picture]) => [anchor, picture.path])),
297
+ checkItems: input.checkItems
298
+ };
299
+ const appendix = deckSymbols(context);
300
+ const serialized = serializeDeck({
301
+ ...context,
302
+ appendix,
303
+ notationTargets: new Set(appendix.map((entry) => entry.key))
304
+ });
305
+ const scripts = input.scripts ?? needsScripts(serialized.tex);
306
+ const files = [
307
+ { path: "main.tex", content: deckMain(manifest, deck, false, scripts) },
308
+ { path: "handout.tex", content: deckMain(manifest, deck, true, scripts) },
309
+ { path: "deck.tex", content: serialized.tex },
310
+ { path: "beamerthemeexplico.sty", content: deckTheme() },
311
+ { path: "explico-common.sty", content: explicoCommon() },
312
+ { path: "latexmkrc", content: LATEXMKRC },
313
+ { path: "README.md", content: deckReadme(manifest, deck, url) },
314
+ ...[...input.pictures.values()].map((picture) => ({ path: picture.path, content: picture.content }))
315
+ ];
316
+ return { files, frameEnds: serialized.frameEnds };
317
+ }
318
+ function deckNumberingProblems(aux, frames) {
319
+ const numbers = /* @__PURE__ */ new Map();
320
+ for (const match of aux.matchAll(/\\newlabel\{frame:(\d+)\}\{\{(\d+)\}/gu)) {
321
+ numbers.set(Number(match[1]), Number(match[2]));
322
+ }
323
+ const problems = [];
324
+ for (let frame = 1; frame <= frames; frame += 1) {
325
+ const printed = numbers.get(frame);
326
+ if (printed === void 0) problems.push(`slide ${frame} has no frame in the PDF`);
327
+ else if (printed !== frame) problems.push(`slide ${frame} is frame ${printed} in the PDF`);
328
+ }
329
+ return problems;
330
+ }
331
+ function deckOverflows(log, frameEnds) {
332
+ const ends = [...frameEnds].sort((a, b) => a[1] - b[1]);
333
+ const found = /* @__PURE__ */ new Map();
334
+ for (const match of log.matchAll(/Overfull \\vbox \(([\d.]+)pt too high\) detected at line (\d+)/gu)) {
335
+ const line = Number(match[2]);
336
+ const frame = ends.find(([, end]) => line <= end + 1)?.[0];
337
+ if (frame !== void 0) found.set(frame, Math.max(found.get(frame) ?? 0, Number(match[1])));
338
+ }
339
+ return [...found].map(([frame, points]) => ({ frame, points: Math.round(points) })).sort((a, b) => a.frame - b.frame);
340
+ }
341
+ export {
342
+ deckNotationKeys,
343
+ deckNumberingProblems,
344
+ deckOverflows,
345
+ deckSource,
346
+ deckSymbols,
347
+ deckTheme,
348
+ serializeDeck
349
+ };
@@ -1,9 +1,9 @@
1
- import { ManifestLesson, ManifestPart, Manifest } from '../../manifest.js';
1
+ import { ManifestLesson, ManifestPart, Manifest, ManifestDeck } from '../../manifest.js';
2
2
  import { StaticPage } from '../static-tree.js';
3
3
  import { CheckItem } from './serialize.js';
4
4
  import '../../../curriculum/validation.js';
5
5
  import '../../../reference/consistency.js';
6
- import '../../../course-config-rRtME5UV.js';
6
+ import '../../../course-config-BLtIdkei.js';
7
7
  import '../../../rules/catalog.js';
8
8
  import '../../../rules/course-rules.js';
9
9
  import '../../collections.js';
@@ -25,6 +25,10 @@ import '../../../reference/mixed-content.js';
25
25
  import '../../../reference/gloss.js';
26
26
  import '../../../rules/waivers.js';
27
27
  import '../../knowledge-version.js';
28
+ import '../../page-versions.js';
29
+ import '../../decks.js';
30
+ import '../../../reference/decks-validate.js';
31
+ import '../../../reference/deck-source.js';
28
32
  import '../../lesson-derivation.js';
29
33
  import '../html-tree.js';
30
34
  import 'parse5';
@@ -41,18 +45,33 @@ interface SourceFile {
41
45
  }
42
46
  /** explico's class, shipped next to this module (copied into `dist/` by `copy-assets`). */
43
47
  declare function explicoClass(): string;
48
+ /** `explico-common.sty`: what the book class and the deck theme share. */
49
+ declare function explicoCommon(): string;
44
50
  /** `parts/02-projectiles/02-optimal-launch-angle.tex` for chapter 2.2. */
45
51
  declare function chapterPath(manifest: Pick<Manifest, 'parts'>, lesson: ManifestLesson): string;
46
52
  /** The folder name a chapter's download unzips to. */
47
53
  declare function chapterFolderName(lesson: ManifestLesson): string;
48
54
  /** A chapter's check answers, by assessment id, in authored order. */
49
55
  declare function checkItems(manifest: Pick<Manifest, 'assessmentContent'>, lesson: Pick<ManifestLesson, 'id'>): Map<string, CheckItem[]>;
56
+ /**
57
+ * The items a deck's question frames ask: its lesson's checks, or on a
58
+ * part's deck those of each chapter it covers, as its frames show them
59
+ * (an assessment two chapters share, as the first one does).
60
+ */
61
+ declare function deckCheckItems(manifest: Pick<Manifest, 'assessmentContent'>, deck: Pick<ManifestDeck, 'lessonId' | 'covers'>): Map<string, CheckItem[]>;
50
62
  /**
51
63
  * The PDF's file identifier (its trailer `/ID`), which the engines derive
52
- * from the output file's path unless told: fixed from the course, its
53
- * content version and the document, so every compile of a source agrees.
64
+ * from the output file's path unless told: fixed from the course, the
65
+ * version the document's content is at (rule C5) and the document, so every
66
+ * compile of a source agrees, and a release that left the document alone
67
+ * leaves its identifier.
68
+ */
69
+ declare function trailerId(manifest: Pick<Manifest, 'about'>, document: string, since: string | null): string;
70
+ /**
71
+ * The version a book's content is at: the newest among its chapters and
72
+ * part pages, `null` when any has a change not yet released.
54
73
  */
55
- declare function trailerId(manifest: Pick<Manifest, 'about'>, document: string): string;
74
+ declare function bookSince(parts: readonly BookPart[]): string | null;
56
75
  interface ChapterSourceInput {
57
76
  readonly manifest: Manifest;
58
77
  readonly lesson: ManifestLesson;
@@ -67,6 +86,19 @@ interface ChapterSourceInput {
67
86
  }
68
87
  /** Every file of one chapter's LaTeX folder. */
69
88
  declare function chapterSource(input: ChapterSourceInput): SourceFile[];
89
+ /**
90
+ * The document's files, with the class option `scripts` on `main.tex` when a
91
+ * `.tex` file holds a character of another script: the class then loads its
92
+ * fallback fonts under LuaLaTeX, which costs every compile that does not
93
+ * need them about two seconds.
94
+ */
95
+ /**
96
+ * Whether a document's LaTeX holds a character of another script, so its
97
+ * class (or a deck's theme) should load the fallback fonts. A Greek letter on
98
+ * its own is a symbol set as math; a Greek word (`\ExplicoGreekWord`), an
99
+ * accented letter or another script needs a text font.
100
+ */
101
+ declare function needsScripts(tex: string): boolean;
70
102
  /** A chapter of a book, with its figure files. */
71
103
  interface BookChapter {
72
104
  readonly lesson: ManifestLesson;
@@ -76,11 +108,24 @@ interface BookChapter {
76
108
  readonly content: Uint8Array;
77
109
  }>;
78
110
  }
111
+ /**
112
+ * A lesson of a book's part that has no chapter, only slides (a slides-first
113
+ * lesson, docs/authoring/deck.md): the book keeps its place and its number
114
+ * with a short chapter that points at the slides.
115
+ */
116
+ interface BookSlidesLesson {
117
+ readonly lesson: ManifestLesson;
118
+ /** The deck's PDF and interactive version, when the course publishes them. */
119
+ readonly slidesUrl?: string;
120
+ readonly outcomes: readonly string[];
121
+ }
79
122
  /** A part of a book: its own page, when it has one, and its chapters in order. */
80
123
  interface BookPart {
81
124
  readonly part: ManifestPart;
82
125
  readonly overview?: StaticPage;
83
126
  readonly chapters: readonly BookChapter[];
127
+ /** Its slides-first lessons, placed among the chapters by their chapter number. */
128
+ readonly slides?: readonly BookSlidesLesson[];
84
129
  }
85
130
  interface BookSourceInput {
86
131
  readonly manifest: Manifest;
@@ -99,4 +144,4 @@ interface BookSourceInput {
99
144
  */
100
145
  declare function bookSource(input: BookSourceInput): SourceFile[];
101
146
 
102
- export { type BookChapter, type BookPart, type BookSourceInput, type ChapterSourceInput, type SourceFile, bookSource, chapterFolderName, chapterPath, chapterSource, checkItems, explicoClass, trailerId };
147
+ export { type BookChapter, type BookPart, type BookSlidesLesson, type BookSourceInput, type ChapterSourceInput, type SourceFile, bookSince, bookSource, chapterFolderName, chapterPath, chapterSource, checkItems, deckCheckItems, explicoClass, explicoCommon, needsScripts, trailerId };