explico 0.13.1 → 0.15.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 (256) hide show
  1. package/CHANGELOG.md +990 -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 +100 -0
  6. package/dist/compiler/cli-main.js +433 -0
  7. package/dist/compiler/cli-reference.d.ts +50 -0
  8. package/dist/compiler/cli-reference.js +213 -0
  9. package/dist/compiler/cli-verbs.d.ts +45 -0
  10. package/dist/compiler/cli-verbs.js +224 -0
  11. package/dist/compiler/cli-verify.d.ts +51 -0
  12. package/dist/compiler/cli-verify.js +151 -0
  13. package/dist/compiler/cli.d.ts +408 -24
  14. package/dist/compiler/cli.js +1328 -8
  15. package/dist/compiler/collections.d.ts +88 -1
  16. package/dist/compiler/collections.js +170 -25
  17. package/dist/compiler/content-release.d.ts +49 -0
  18. package/dist/compiler/content-release.js +63 -0
  19. package/dist/compiler/content-schemas.d.ts +2 -2
  20. package/dist/compiler/course-config.d.ts +1 -1
  21. package/dist/compiler/course-config.js +105 -1
  22. package/dist/compiler/decks.d.ts +161 -0
  23. package/dist/compiler/decks.js +235 -0
  24. package/dist/compiler/exports/combined-html.d.ts +5 -1
  25. package/dist/compiler/exports/deck-fit.d.ts +55 -0
  26. package/dist/compiler/exports/deck-fit.js +90 -0
  27. package/dist/compiler/exports/export-chapters.d.ts +37 -6
  28. package/dist/compiler/exports/export-chapters.js +64 -5
  29. package/dist/compiler/exports/export-pages.d.ts +30 -3
  30. package/dist/compiler/exports/export-pages.js +24 -1
  31. package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  32. package/dist/compiler/exports/latex/cache.d.ts +78 -0
  33. package/dist/compiler/exports/latex/cache.js +129 -0
  34. package/dist/compiler/exports/latex/capture.d.ts +10 -1
  35. package/dist/compiler/exports/latex/capture.js +3 -1
  36. package/dist/compiler/exports/latex/compile.d.ts +13 -2
  37. package/dist/compiler/exports/latex/compile.js +12 -1
  38. package/dist/compiler/exports/latex/deck.d.ts +92 -0
  39. package/dist/compiler/exports/latex/deck.js +349 -0
  40. package/dist/compiler/exports/latex/document.d.ts +51 -6
  41. package/dist/compiler/exports/latex/document.js +71 -12
  42. package/dist/compiler/exports/latex/explico-common.sty +198 -0
  43. package/dist/compiler/exports/latex/explico.cls +13 -183
  44. package/dist/compiler/exports/latex/serialize.d.ts +60 -4
  45. package/dist/compiler/exports/latex/serialize.js +4 -2
  46. package/dist/compiler/exports/latex-export.d.ts +24 -2
  47. package/dist/compiler/exports/latex-export.js +107 -26
  48. package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
  49. package/dist/compiler/exports/markdown/serialize.js +85 -8
  50. package/dist/compiler/exports/markdown-export.d.ts +6 -1
  51. package/dist/compiler/exports/markdown-export.js +50 -11
  52. package/dist/compiler/exports/standalone-html.d.ts +10 -5
  53. package/dist/compiler/exports/standalone-html.js +3 -3
  54. package/dist/compiler/exports/static-tree.d.ts +38 -1
  55. package/dist/compiler/exports/static-tree.js +120 -4
  56. package/dist/compiler/knowledge-version.d.ts +26 -2
  57. package/dist/compiler/knowledge-version.js +27 -17
  58. package/dist/compiler/lesson-derivation.d.ts +143 -6
  59. package/dist/compiler/lesson-derivation.js +145 -13
  60. package/dist/compiler/manifest.d.ts +133 -4
  61. package/dist/compiler/manifest.js +364 -58
  62. package/dist/compiler/page-versions.d.ts +184 -0
  63. package/dist/compiler/page-versions.js +217 -0
  64. package/dist/compiler/render-diagnostics.d.ts +5 -1
  65. package/dist/compiler/resolution-report.d.ts +24 -6
  66. package/dist/compiler/resolution-report.js +22 -4
  67. package/dist/compiler/validate-content.d.ts +7 -0
  68. package/dist/compiler/validate-content.js +160 -0
  69. package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
  70. package/dist/curriculum/competency-graph.d.ts +54 -0
  71. package/dist/curriculum/competency-graph.js +41 -0
  72. package/dist/reference/base-library.d.mts +8 -0
  73. package/dist/reference/base-library.mjs +19 -0
  74. package/dist/reference/consistency.d.ts +8 -4
  75. package/dist/reference/consistency.js +82 -26
  76. package/dist/reference/deck-remark.d.mts +123 -0
  77. package/dist/reference/deck-remark.mjs +600 -0
  78. package/dist/reference/deck-source.d.ts +107 -0
  79. package/dist/reference/deck-source.js +140 -0
  80. package/dist/reference/decks-validate.d.ts +133 -0
  81. package/dist/reference/decks-validate.js +359 -0
  82. package/dist/reference/derived-entries.d.ts +5 -1
  83. package/dist/reference/diagram-fit.d.ts +6 -2
  84. package/dist/reference/diagram-fit.js +5 -1
  85. package/dist/reference/figure-symbols.d.ts +23 -6
  86. package/dist/reference/figure-symbols.js +51 -15
  87. package/dist/reference/float-name.d.ts +43 -0
  88. package/dist/reference/float-name.js +58 -0
  89. package/dist/reference/folds-validate.d.ts +1 -1
  90. package/dist/reference/gate-abbreviations.d.ts +1 -1
  91. package/dist/reference/gate-indices.js +5 -4
  92. package/dist/reference/gate-math.d.ts +29 -2
  93. package/dist/reference/gate-math.js +99 -18
  94. package/dist/reference/gate-part-pages.d.ts +1 -1
  95. package/dist/reference/lab-katex-validate.d.ts +10 -6
  96. package/dist/reference/lab-katex-validate.js +17 -9
  97. package/dist/reference/lab-math-scope.js +29 -4
  98. package/dist/reference/math-glyphs.d.mts +25 -0
  99. package/dist/reference/math-glyphs.mjs +189 -25
  100. package/dist/reference/page-notation.d.ts +22 -3
  101. package/dist/reference/page-notation.js +32 -10
  102. package/dist/reference/references.d.ts +14 -1
  103. package/dist/reference/references.js +13 -0
  104. package/dist/reference/registry-scope.d.mts +33 -0
  105. package/dist/reference/registry-scope.mjs +34 -0
  106. package/dist/reference/registry.js +29 -10
  107. package/dist/reference/rehype-deck.d.mts +6 -0
  108. package/dist/reference/rehype-deck.mjs +286 -0
  109. package/dist/reference/rehype-heading-anchors.d.mts +20 -0
  110. package/dist/reference/rehype-heading-anchors.mjs +48 -6
  111. package/dist/reference/remark-notation.mjs +249 -40
  112. package/dist/reference/source-lines.d.mts +12 -0
  113. package/dist/reference/source-lines.mjs +65 -0
  114. package/dist/reference/types.d.ts +28 -0
  115. package/dist/rules/catalog.d.ts +21 -4
  116. package/dist/rules/catalog.js +237 -2
  117. package/dist/rules/course-rules.d.ts +5 -1
  118. package/dist/rules/course-rules.js +17 -3
  119. package/dist/rules/course-rules.schema.json +12 -0
  120. package/dist/rules/in-effect.d.ts +13 -3
  121. package/dist/rules/in-effect.js +34 -10
  122. package/dist/rules/rules.schema.json +280 -1
  123. package/dist/rules/settings.d.ts +1 -1
  124. package/dist/session/PreferenceStore.d.ts +6 -0
  125. package/dist/session/PreferenceStore.js +18 -1
  126. package/dist/session/deck-memory.d.ts +116 -0
  127. package/dist/session/deck-memory.js +143 -0
  128. package/dist/session/reading-memory.d.ts +96 -0
  129. package/dist/session/reading-memory.js +133 -0
  130. package/docs/VERSIONING.md +21 -18
  131. package/docs/authoring/README.md +31 -9
  132. package/docs/authoring/agents.md +72 -18
  133. package/docs/authoring/cli-reference.json +2172 -0
  134. package/docs/authoring/cli.md +887 -0
  135. package/docs/authoring/components.md +15 -0
  136. package/docs/authoring/deck.md +612 -0
  137. package/docs/authoring/migrating.md +1195 -16
  138. package/docs/authoring/notation.md +49 -6
  139. package/docs/authoring/structure.md +80 -1
  140. package/docs/authoring/versioning.md +56 -3
  141. package/docs/authoring/visualizations.md +7 -2
  142. package/docs/course-agent/AGENTS.md +40 -0
  143. package/docs/course-agent/agents/rules-reviewer.md +33 -0
  144. package/docs/course-agent/skills/draft-lesson/SKILL.md +38 -0
  145. package/docs/course-agent/skills/migrate-course/SKILL.md +32 -0
  146. package/docs/course-agent/skills/release-content/SKILL.md +26 -0
  147. package/docs/diagram-scaling.md +148 -0
  148. package/docs/privacy.md +59 -0
  149. package/package.json +8 -2
  150. package/src/compiler/astro.ts +60 -21
  151. package/src/compiler/cli-main.ts +557 -0
  152. package/src/compiler/cli-reference.ts +285 -0
  153. package/src/compiler/cli-verbs.ts +270 -0
  154. package/src/compiler/cli-verify.ts +241 -0
  155. package/src/compiler/cli.ts +2119 -170
  156. package/src/compiler/collections.ts +284 -25
  157. package/src/compiler/content-release.ts +91 -0
  158. package/src/compiler/course-config.ts +267 -0
  159. package/src/compiler/decks.ts +455 -0
  160. package/src/compiler/exports/deck-fit.ts +127 -0
  161. package/src/compiler/exports/export-chapters.ts +105 -6
  162. package/src/compiler/exports/export-pages.ts +46 -2
  163. package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  164. package/src/compiler/exports/latex/cache.ts +183 -0
  165. package/src/compiler/exports/latex/capture.ts +5 -2
  166. package/src/compiler/exports/latex/compile.ts +18 -0
  167. package/src/compiler/exports/latex/deck.ts +419 -0
  168. package/src/compiler/exports/latex/document.ts +117 -21
  169. package/src/compiler/exports/latex/explico-common.sty +198 -0
  170. package/src/compiler/exports/latex/explico.cls +13 -183
  171. package/src/compiler/exports/latex/serialize.ts +25 -21
  172. package/src/compiler/exports/latex/texlive-packages.txt +6 -2
  173. package/src/compiler/exports/latex-export.ts +187 -45
  174. package/src/compiler/exports/markdown/serialize.ts +118 -9
  175. package/src/compiler/exports/markdown-export.ts +56 -11
  176. package/src/compiler/exports/standalone-html.ts +8 -7
  177. package/src/compiler/exports/static-tree.ts +175 -3
  178. package/src/compiler/knowledge-version.ts +56 -21
  179. package/src/compiler/lesson-derivation.ts +282 -16
  180. package/src/compiler/manifest.ts +568 -63
  181. package/src/compiler/page-versions.ts +401 -0
  182. package/src/compiler/resolution-report.ts +38 -7
  183. package/src/compiler/validate-content.ts +215 -0
  184. package/src/components/CourseContents.astro +124 -6
  185. package/src/components/CurriculumMap.astro +49 -1
  186. package/src/components/FloatIndex.astro +1 -1
  187. package/src/components/PartOverview.astro +51 -5
  188. package/src/components/assessments/AssessmentSet.astro +41 -15
  189. package/src/components/block-heading.ts +15 -6
  190. package/src/components/card-carry.ts +7 -4
  191. package/src/components/card-manager.ts +14 -2
  192. package/src/components/card-memory.ts +13 -9
  193. package/src/components/deck/DeckEnhancer.astro +13 -0
  194. package/src/components/deck/deck-context.ts +104 -0
  195. package/src/components/deck/deck-controller.ts +1287 -0
  196. package/src/components/deck/deck-map.ts +91 -0
  197. package/src/components/deck-lane.ts +163 -0
  198. package/src/components/diagram/Diagram.astro +131 -86
  199. package/src/components/disclosure/persisted-disclosure.ts +9 -1
  200. package/src/components/figure/Figure.astro +7 -2
  201. package/src/components/figure/FloatCaption.astro +5 -3
  202. package/src/components/fold/Fold.astro +2 -2
  203. package/src/components/notation/NotationFormula.astro +81 -11
  204. package/src/components/notation/NotationGlossary.astro +132 -1
  205. package/src/components/notation/NotationLayer.astro +15 -7
  206. package/src/components/starlight/AboutPanel.astro +186 -3
  207. package/src/components/starlight/CollapsibleSections.astro +24 -3
  208. package/src/components/starlight/DiagramEnhancer.astro +5 -1
  209. package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
  210. package/src/components/starlight/LayoutHeader.astro +17 -0
  211. package/src/components/starlight/LayoutPageTitle.astro +110 -0
  212. package/src/components/starlight/LayoutSidebar.astro +807 -21
  213. package/src/components/starlight/LayoutSocialIcons.astro +62 -6
  214. package/src/components/starlight/LessonExportActions.astro +27 -9
  215. package/src/components/starlight/LessonFooter.astro +59 -0
  216. package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
  217. package/src/components/starlight/TextSizeControl.astro +24 -13
  218. package/src/components/starlight/page-marks.ts +139 -0
  219. package/src/components/starlight/section-summary.ts +1 -1
  220. package/src/components/starlight/sidebar-editions.ts +245 -0
  221. package/src/components/table/Table.astro +15 -7
  222. package/src/content-config.ts +87 -1
  223. package/src/curriculum/competency-graph.ts +95 -0
  224. package/src/reference/base-library.mjs +19 -0
  225. package/src/reference/consistency.ts +137 -39
  226. package/src/reference/deck-remark.mjs +600 -0
  227. package/src/reference/deck-source.ts +273 -0
  228. package/src/reference/decks-validate.ts +604 -0
  229. package/src/reference/diagram-fit.ts +18 -4
  230. package/src/reference/diagram-layout.ts +3 -1
  231. package/src/reference/figure-symbols.ts +108 -31
  232. package/src/reference/folds-validate.ts +1 -1
  233. package/src/reference/gate-indices.ts +5 -3
  234. package/src/reference/gate-math.ts +169 -25
  235. package/src/reference/lab-katex-validate.ts +30 -15
  236. package/src/reference/lab-math-scope.ts +34 -3
  237. package/src/reference/math-glyphs.mjs +189 -25
  238. package/src/reference/page-notation.ts +69 -15
  239. package/src/reference/references.ts +28 -0
  240. package/src/reference/registry-scope.mjs +34 -0
  241. package/src/reference/registry.ts +41 -11
  242. package/src/reference/rehype-deck.mjs +286 -0
  243. package/src/reference/rehype-heading-anchors.mjs +48 -6
  244. package/src/reference/remark-notation.mjs +249 -40
  245. package/src/reference/source-lines.mjs +65 -0
  246. package/src/reference/types.ts +28 -0
  247. package/src/rules/catalog.ts +274 -4
  248. package/src/rules/course-rules.ts +21 -1
  249. package/src/rules/in-effect.ts +58 -11
  250. package/src/session/PreferenceStore.ts +23 -0
  251. package/src/session/deck-memory.ts +263 -0
  252. package/src/session/reading-memory.ts +243 -0
  253. package/src/styles/deck.css +1150 -0
  254. package/src/styles/global.css +958 -27
  255. package/src/styles/zones.css +277 -0
  256. /package/src/{components → reference}/float-name.ts +0 -0
@@ -0,0 +1,160 @@
1
+ import { notationSourceLocatorGaps } from "./collections.js";
2
+ import { createRunLog, diagnosticLine } from "./logging.js";
3
+ import { compileManifest, MANIFEST_PATH, writeManifest } from "./manifest.js";
4
+ import { syncPageVersions } from "./page-versions.js";
5
+ import { syncResolutionReports } from "./resolution-report.js";
6
+ import { consistencyCounts } from "../reference/consistency.js";
7
+ import { curriculumErrors } from "../curriculum/validation.js";
8
+ import { writeRulesInEffect } from "../rules/in-effect.js";
9
+ async function runValidateContent(courseConfig) {
10
+ const log = createRunLog();
11
+ let manifest;
12
+ try {
13
+ manifest = await compileManifest(courseConfig);
14
+ } catch (error) {
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ log.error("manifest", `Manifest compilation failed: ${message}`);
17
+ const summary2 = log.close();
18
+ console.error(`Manifest compilation failed: ${message}
19
+ Run log: ${summary2.relativeDirectory}.`);
20
+ return 1;
21
+ }
22
+ writeManifest(manifest);
23
+ const {
24
+ curriculum,
25
+ notation,
26
+ alignment,
27
+ math,
28
+ consistency,
29
+ equations,
30
+ tables,
31
+ figures,
32
+ diagrams,
33
+ checks,
34
+ examples,
35
+ folds,
36
+ knowledge,
37
+ rawFloats,
38
+ config,
39
+ waivers
40
+ } = manifest.diagnostics;
41
+ for (const warning of curriculum.filter((issue) => issue.severity === "warning")) {
42
+ console.warn(log.warning("curriculum", `[${warning.kind}] ${warning.message}`));
43
+ }
44
+ for (const warning of notation.filter((d) => d.severity === "warning")) {
45
+ console.warn(log.warning("notation", `[${warning.code}] ${warning.message}`));
46
+ }
47
+ for (const warning of alignment.filter((d) => d.severity === "warning")) {
48
+ console.warn(log.warning("alignment", `[${warning.code}] ${warning.message}`));
49
+ }
50
+ const floatGroups = [
51
+ ["equations", equations],
52
+ ["tables", tables],
53
+ ["figures", figures],
54
+ ["diagrams", diagrams],
55
+ ["checks", checks],
56
+ ["examples", examples],
57
+ ["folds", folds],
58
+ ["rawFloats", rawFloats]
59
+ ];
60
+ for (const [source, group] of floatGroups) {
61
+ for (const warning of group.filter((d) => d.severity === "warning")) {
62
+ console.warn(log.warning(source, `[${warning.code}] ${warning.message}`));
63
+ }
64
+ }
65
+ for (const issue of math.filter((item) => item.severity === "warning")) {
66
+ console.warn(log.warning("math", diagnosticLine(issue)));
67
+ }
68
+ for (const warning of knowledge) {
69
+ console.warn(log.warning("knowledge", `[${warning.code}] ${warning.message}`));
70
+ }
71
+ for (const warning of config) {
72
+ console.warn(log.warning("config", `[${warning.code}] ${warning.message}`));
73
+ }
74
+ for (const warning of waivers.filter((d) => d.severity === "warning")) {
75
+ console.warn(log.warning("waivers", diagnosticLine(warning)));
76
+ }
77
+ const locatorGaps = notationSourceLocatorGaps();
78
+ if (locatorGaps.bare > 0) {
79
+ console.warn(
80
+ log.warning(
81
+ "consistency",
82
+ `[notation-source-locator] ${locatorGaps.bare} of ${locatorGaps.total} notation source citations lack a non-empty locator.`
83
+ )
84
+ );
85
+ }
86
+ const consistencyWarnings = consistency.filter((diagnostic) => diagnostic.severity === "warning");
87
+ if (consistencyWarnings.length > 0) {
88
+ console.warn(
89
+ log.info(
90
+ "consistency",
91
+ `[consistency] ${consistencyWarnings.length} Tier-3 finding(s): ` + Object.entries(consistencyCounts(consistencyWarnings)).filter(([, count]) => count > 0).map(([code, count]) => `${code}=${count}`).join(", ")
92
+ )
93
+ );
94
+ }
95
+ log.infoDiagnostics(manifest.diagnostics);
96
+ for (const diagnostic of consistencyWarnings) {
97
+ log.warning("consistency", `[${diagnostic.code}] ${diagnostic.message}`);
98
+ }
99
+ const reports = syncResolutionReports(manifest);
100
+ console.log(
101
+ log.info(
102
+ "manifest",
103
+ `Resolution report: ${reports.written} file(s) under resolution/` + (reports.refreshed.length > 0 ? ` (${reports.refreshed.length} refreshed; git does not track them, nothing to commit).` : ".")
104
+ )
105
+ );
106
+ const pageVersions = syncPageVersions(manifest);
107
+ if (pageVersions.written) {
108
+ console.log(
109
+ log.info(
110
+ "page-versions",
111
+ `content/versions.json ${pageVersions.changed[0] === "*" ? "written" : `updated for ${pageVersions.changed.length} page(s)`} \u2014 commit it with the change (rule C5).`
112
+ )
113
+ );
114
+ }
115
+ writeRulesInEffect(manifest);
116
+ console.log(
117
+ log.info("manifest", `Rules in effect: build/rules-in-effect.md (${manifest.rules.disabled.length} rule(s) off).`)
118
+ );
119
+ const failures = [];
120
+ const block = (source, message) => {
121
+ failures.push(log.error(source, message));
122
+ };
123
+ for (const stalePath of reports.stale) {
124
+ block("resolution-report", `[resolution-report] ${stalePath} is out of date \u2014 commit the regenerated resolution/ files`);
125
+ }
126
+ if (pageVersions.diagnostic) block("page-versions", diagnosticLine(pageVersions.diagnostic));
127
+ for (const issue of curriculumErrors(curriculum)) block("curriculum", `[curriculum:${issue.kind}] ${issue.message}`);
128
+ for (const issue of notation.filter((d) => d.severity === "error")) block("notation", `[notation:${issue.code}] ${issue.message}`);
129
+ for (const issue of alignment.filter((d) => d.severity === "error")) block("alignment", `[alignment:${issue.code}] ${issue.message}`);
130
+ for (const issue of math.filter((item) => item.severity === "error")) block("math", diagnosticLine(issue));
131
+ for (const issue of waivers.filter((d) => d.severity === "error")) block("waivers", diagnosticLine(issue));
132
+ for (const [source, group] of floatGroups) {
133
+ for (const issue of group.filter((d) => d.severity === "error")) block(source, `[${issue.code}] ${issue.message}`);
134
+ }
135
+ if (failures.length > 0) {
136
+ const summary2 = log.close();
137
+ console.error(
138
+ `Content manifest has ${failures.length} blocking diagnostic(s):
139
+ ${failures.map((line) => `- ${line}`).join("\n")}
140
+ Run log: ${summary2.relativeDirectory} (${summary2.counts.ERROR} error(s), ${summary2.counts.WARNING} warning(s)).`
141
+ );
142
+ return 1;
143
+ }
144
+ const itemCount = manifest.lessons.reduce((count, lesson) => count + lesson.assessments.length, 0);
145
+ const relPath = MANIFEST_PATH.replace(`${process.cwd()}/`, "");
146
+ console.log(
147
+ log.info(
148
+ "manifest",
149
+ `Manifest written to ${relPath} (schema v${manifest.schemaVersion}, knowledge v${manifest.about.knowledge.version}): ${manifest.competencies.length} competencies, ${manifest.lessons.length} lessons, ${itemCount} lesson assessment links, ${manifest.notation.definitions.length} notation definitions, ${manifest.sources.length} sources, ${manifest.tracks.length} track(s). Content fingerprint ${manifest.hashes.contentTree}.`
150
+ )
151
+ );
152
+ const summary = log.close();
153
+ console.log(
154
+ `Run log: ${summary.relativeDirectory} (${summary.counts.ERROR} error(s), ${summary.counts.WARNING} warning(s), ${summary.counts.INFO} info).`
155
+ );
156
+ return 0;
157
+ }
158
+ export {
159
+ runValidateContent
160
+ };
@@ -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 };