explico 0.7.0 → 0.11.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 (236) hide show
  1. package/CHANGELOG.md +1409 -1
  2. package/README.md +60 -15
  3. package/dist/compiler/astro.d.ts +6 -1
  4. package/dist/compiler/cli.d.ts +54 -3
  5. package/dist/compiler/cli.js +80 -6
  6. package/dist/compiler/collections.d.ts +35 -2
  7. package/dist/compiler/collections.js +84 -8
  8. package/dist/compiler/course-config.d.ts +3 -194
  9. package/dist/compiler/course-config.js +78 -5
  10. package/dist/compiler/exports/standalone-html.d.ts +6 -1
  11. package/dist/compiler/knowledge-version.js +1 -1
  12. package/dist/compiler/lesson-derivation.d.ts +27 -10
  13. package/dist/compiler/lesson-derivation.js +23 -8
  14. package/dist/compiler/logging.d.ts +14 -1
  15. package/dist/compiler/logging.js +6 -2
  16. package/dist/compiler/manifest.d.ts +204 -31
  17. package/dist/compiler/manifest.js +270 -55
  18. package/dist/compiler/resolution-report.d.ts +6 -1
  19. package/dist/course-config-Dsnri9Tk.d.ts +464 -0
  20. package/dist/reference/base-accents.d.mts +26 -0
  21. package/dist/reference/base-accents.mjs +31 -0
  22. package/dist/reference/base-roman-labels.d.mts +2 -2
  23. package/dist/reference/base-roman-labels.mjs +2 -2
  24. package/dist/reference/consistency.d.ts +14 -2
  25. package/dist/reference/consistency.js +108 -4
  26. package/dist/reference/delimiter-size.d.ts +21 -11
  27. package/dist/reference/delimiter-size.js +15 -39
  28. package/dist/reference/diagrams-validate.d.ts +7 -2
  29. package/dist/reference/diagrams-validate.js +9 -1
  30. package/dist/reference/equations-validate.d.ts +8 -5
  31. package/dist/reference/equations-validate.js +1 -0
  32. package/dist/reference/equations.d.mts +69 -34
  33. package/dist/reference/equations.mjs +90 -44
  34. package/dist/reference/examples-validate.d.ts +31 -0
  35. package/dist/reference/examples-validate.js +48 -0
  36. package/dist/reference/figures-validate.d.ts +1 -1
  37. package/dist/reference/figures-validate.js +4 -1
  38. package/dist/reference/folds-validate.d.ts +33 -0
  39. package/dist/reference/folds-validate.js +32 -0
  40. package/dist/reference/gate-abbreviations.d.ts +3 -1
  41. package/dist/reference/gate-abbreviations.js +3 -2
  42. package/dist/reference/gate-course-rules.d.ts +32 -0
  43. package/dist/reference/gate-course-rules.js +85 -0
  44. package/dist/reference/gate-heading-length.d.ts +5 -2
  45. package/dist/reference/gate-heading-length.js +47 -23
  46. package/dist/reference/gate-heading-structure.d.ts +41 -0
  47. package/dist/reference/gate-heading-structure.js +113 -0
  48. package/dist/reference/gate-math-punctuation.d.ts +71 -0
  49. package/dist/reference/gate-math-punctuation.js +251 -0
  50. package/dist/reference/gate-math.d.ts +44 -6
  51. package/dist/reference/gate-math.js +63 -10
  52. package/dist/reference/gate-part-pages.d.ts +50 -0
  53. package/dist/reference/gate-part-pages.js +118 -0
  54. package/dist/reference/gate-prose-notation.d.ts +32 -11
  55. package/dist/reference/gate-prose-notation.js +30 -8
  56. package/dist/reference/katex-delimiters.d.mts +15 -0
  57. package/dist/reference/katex-delimiters.mjs +138 -0
  58. package/dist/reference/katex-options.mjs +4 -0
  59. package/dist/reference/keyed-float-validate.d.ts +27 -10
  60. package/dist/reference/keyed-float-validate.js +15 -1
  61. package/dist/reference/keyed-jsx-floats.d.mts +11 -5
  62. package/dist/reference/keyed-jsx-floats.mjs +73 -20
  63. package/dist/reference/knowledge-checks-validate.d.ts +1 -1
  64. package/dist/reference/knowledge-checks.d.mts +3 -1
  65. package/dist/reference/knowledge-checks.mjs +9 -4
  66. package/dist/reference/lab-math-scope.d.ts +1 -0
  67. package/dist/reference/lab-math-slot.d.ts +34 -0
  68. package/dist/reference/lab-math-slot.js +6 -0
  69. package/dist/reference/math-glyphs.d.mts +80 -4
  70. package/dist/reference/math-glyphs.mjs +588 -39
  71. package/dist/reference/page-notation.d.ts +6 -1
  72. package/dist/reference/raw-floats-validate.js +1 -1
  73. package/dist/reference/raw-floats.mjs +1 -1
  74. package/dist/reference/rehype-heading-anchors.d.mts +14 -0
  75. package/dist/reference/rehype-heading-anchors.mjs +116 -17
  76. package/dist/reference/rehype-table-header-labels.d.mts +6 -1
  77. package/dist/reference/rehype-table-header-labels.mjs +21 -3
  78. package/dist/reference/remark-asides.d.mts +3 -0
  79. package/dist/reference/remark-asides.mjs +180 -0
  80. package/dist/reference/remark-memo.d.mts +3 -1
  81. package/dist/reference/remark-memo.mjs +42 -11
  82. package/dist/reference/remark-notation.d.mts +60 -10
  83. package/dist/reference/remark-notation.mjs +387 -36
  84. package/dist/reference/render-lab-math.d.ts +4 -26
  85. package/dist/reference/render-lab-math.js +5 -5
  86. package/dist/reference/sections-validate.d.ts +27 -0
  87. package/dist/reference/sections-validate.js +41 -0
  88. package/dist/reference/sections.d.mts +97 -0
  89. package/dist/reference/sections.mjs +306 -0
  90. package/dist/reference/tables-validate.d.ts +1 -1
  91. package/dist/reference/types.d.ts +24 -1
  92. package/dist/reference/unit-words.d.ts +37 -0
  93. package/dist/reference/unit-words.js +170 -0
  94. package/dist/rules/catalog.d.ts +176 -0
  95. package/dist/rules/catalog.js +983 -0
  96. package/dist/rules/course-rules.d.ts +115 -0
  97. package/dist/rules/course-rules.js +292 -0
  98. package/dist/rules/course-rules.schema.json +138 -0
  99. package/dist/rules/in-effect.d.ts +53 -0
  100. package/dist/rules/in-effect.js +142 -0
  101. package/dist/rules/rules.schema.json +1072 -0
  102. package/dist/rules/settings.d.ts +3 -0
  103. package/dist/rules/settings.js +290 -0
  104. package/dist/rules/waivers.d.ts +53 -0
  105. package/dist/rules/waivers.js +132 -0
  106. package/dist/session/PreferenceStore.d.ts +5 -0
  107. package/dist/session/PreferenceStore.js +9 -0
  108. package/docs/VERSIONING.md +2 -2
  109. package/docs/authoring/README.md +232 -40
  110. package/docs/authoring/agents.md +55 -9
  111. package/docs/authoring/components.md +178 -22
  112. package/docs/authoring/migrating.md +1324 -2
  113. package/docs/authoring/notation.md +616 -344
  114. package/docs/authoring/prose.md +551 -0
  115. package/docs/authoring/retired.md +35 -0
  116. package/docs/authoring/structure-rules.md +422 -40
  117. package/docs/authoring/structure.md +445 -62
  118. package/docs/authoring/versioning.md +20 -28
  119. package/docs/authoring/visualizations.md +142 -20
  120. package/docs/katex-bracket-scaling.md +191 -0
  121. package/package.json +3 -2
  122. package/src/compiler/cli.ts +139 -6
  123. package/src/compiler/collections.ts +144 -10
  124. package/src/compiler/course-config.ts +273 -34
  125. package/src/compiler/knowledge-version.ts +1 -1
  126. package/src/compiler/lesson-derivation.ts +54 -15
  127. package/src/compiler/logging.ts +21 -4
  128. package/src/compiler/manifest.ts +573 -104
  129. package/src/components/BlockHeading.astro +113 -0
  130. package/src/components/CourseContents.astro +221 -0
  131. package/src/components/CurriculumMap.astro +5 -6
  132. package/src/components/FloatIndex.astro +200 -0
  133. package/src/components/HoverPanel.astro +431 -79
  134. package/src/components/PartBridge.astro +27 -0
  135. package/src/components/PartOverview.astro +401 -0
  136. package/src/components/PartScope.astro +27 -0
  137. package/src/components/abbreviation/AbbreviationLayer.astro +49 -51
  138. package/src/components/assessments/AssessmentRunner.tsx +8 -5
  139. package/src/components/assessments/AssessmentSet.astro +23 -26
  140. package/src/components/block-heading.ts +146 -0
  141. package/src/components/card-arrow.ts +243 -0
  142. package/src/components/card-drag.ts +631 -0
  143. package/src/components/card-jump.ts +45 -0
  144. package/src/components/card-manager.ts +1589 -0
  145. package/src/components/card-memory.ts +177 -0
  146. package/src/components/card-placement.ts +504 -0
  147. package/src/components/card-side.ts +336 -0
  148. package/src/components/card-travel.ts +197 -0
  149. package/src/components/citation/CitationLayer.astro +77 -76
  150. package/src/components/diagram/Diagram.astro +161 -316
  151. package/src/components/disclosure/course-disclosure.ts +24 -0
  152. package/src/components/disclosure/hover-preview.ts +122 -42
  153. package/src/components/disclosure/persisted-disclosure.ts +3 -3
  154. package/src/components/disclosure/print-as-shown.ts +252 -0
  155. package/src/components/examples/CompactExample.astro +3 -1
  156. package/src/components/examples/CompactExamples.astro +101 -94
  157. package/src/components/figure/Figure.astro +174 -274
  158. package/src/components/figure/FloatCaption.astro +87 -0
  159. package/src/components/figure/figure-state.ts +58 -0
  160. package/src/components/figure/figure-tools.ts +87 -0
  161. package/src/components/float-name.ts +101 -0
  162. package/src/components/fold/Fold.astro +125 -0
  163. package/src/components/fold/fold-id.ts +40 -0
  164. package/src/components/memo/Memo.astro +162 -231
  165. package/src/components/memo/ShortNote.astro +6 -6
  166. package/src/components/notation/NotationFormula.astro +5 -1
  167. package/src/components/notation/NotationGlossary.astro +24 -14
  168. package/src/components/notation/NotationLayer.astro +169 -162
  169. package/src/components/notation/NotationMath.astro +10 -1
  170. package/src/components/notation/render-notation-math.ts +111 -10
  171. package/src/components/reference/FloatRefLayer.astro +485 -423
  172. package/src/components/starlight/AboutPanel.astro +22 -6
  173. package/src/components/starlight/CollapsibleSections.astro +207 -93
  174. package/src/components/starlight/DiagramEnhancer.astro +15 -138
  175. package/src/components/starlight/HeadingAnchorEnhancer.astro +193 -1
  176. package/src/components/starlight/KeyedFloatEnhancer.astro +100 -9
  177. package/src/components/starlight/LayoutHeader.astro +298 -19
  178. package/src/components/starlight/LayoutPageSidebar.astro +99 -12
  179. package/src/components/starlight/LayoutPageTitle.astro +120 -0
  180. package/src/components/starlight/LayoutSidebar.astro +229 -11
  181. package/src/components/starlight/LessonExportActions.astro +53 -72
  182. package/src/components/starlight/LessonFooter.astro +26 -4
  183. package/src/components/starlight/section-parts.ts +15 -2
  184. package/src/components/starlight/section-summary.ts +181 -0
  185. package/src/components/table/Table.astro +95 -76
  186. package/src/components/table/TableOverflowExpander.astro +65 -28
  187. package/src/content-config.ts +40 -4
  188. package/src/reference/base-accents.mjs +31 -0
  189. package/src/reference/base-roman-labels.mjs +2 -2
  190. package/src/reference/consistency.ts +176 -5
  191. package/src/reference/delimiter-size.ts +40 -61
  192. package/src/reference/diagrams-validate.ts +17 -1
  193. package/src/reference/equations-validate.ts +10 -5
  194. package/src/reference/equations.mjs +90 -44
  195. package/src/reference/examples-validate.ts +97 -0
  196. package/src/reference/figures-validate.ts +6 -1
  197. package/src/reference/folds-validate.ts +65 -0
  198. package/src/reference/gate-abbreviations.ts +3 -2
  199. package/src/reference/gate-course-rules.ts +134 -0
  200. package/src/reference/gate-heading-length.ts +68 -37
  201. package/src/reference/gate-heading-structure.ts +193 -0
  202. package/src/reference/gate-math-punctuation.ts +381 -0
  203. package/src/reference/gate-math.ts +119 -12
  204. package/src/reference/gate-part-pages.ts +217 -0
  205. package/src/reference/gate-prose-notation.ts +74 -13
  206. package/src/reference/katex-delimiters.mjs +138 -0
  207. package/src/reference/katex-options.mjs +4 -0
  208. package/src/reference/keyed-float-validate.ts +45 -11
  209. package/src/reference/keyed-jsx-floats.mjs +73 -20
  210. package/src/reference/knowledge-checks-validate.ts +1 -1
  211. package/src/reference/knowledge-checks.mjs +9 -4
  212. package/src/reference/lab-math-slot.ts +36 -0
  213. package/src/reference/math-glyphs.mjs +588 -39
  214. package/src/reference/raw-floats-validate.ts +1 -1
  215. package/src/reference/raw-floats.mjs +1 -1
  216. package/src/reference/rehype-heading-anchors.mjs +116 -17
  217. package/src/reference/rehype-table-header-labels.mjs +21 -3
  218. package/src/reference/remark-asides.mjs +180 -0
  219. package/src/reference/remark-memo.mjs +42 -11
  220. package/src/reference/remark-notation.mjs +387 -36
  221. package/src/reference/render-lab-math.ts +17 -32
  222. package/src/reference/sections-validate.ts +81 -0
  223. package/src/reference/sections.mjs +306 -0
  224. package/src/reference/tables-validate.ts +2 -1
  225. package/src/reference/types.ts +24 -0
  226. package/src/reference/unit-words.ts +233 -0
  227. package/src/rules/catalog.ts +1298 -0
  228. package/src/rules/course-rules.ts +456 -0
  229. package/src/rules/in-effect.ts +235 -0
  230. package/src/rules/settings.ts +448 -0
  231. package/src/rules/waivers.ts +249 -0
  232. package/src/session/PreferenceStore.ts +15 -0
  233. package/src/styles/global.css +1471 -163
  234. package/src/styles/surfaces.css +182 -0
  235. package/src/components/hover-panel.ts +0 -339
  236. package/src/components/popup-stack.ts +0 -345
@@ -0,0 +1,464 @@
1
+ import { RuleId } from './rules/catalog.js';
2
+ import { CourseRule, CourseRuleDefinitions } from './rules/course-rules.js';
3
+
4
+ /**
5
+ * A course's per-rule settings — `rules` in `CourseConfig` — resolved against
6
+ * the catalog.
7
+ *
8
+ * The block is flat by rule ID and holds only deviations from the defaults:
9
+ *
10
+ * ```json
11
+ * "rules": {
12
+ * "P5": { "enabled": false },
13
+ * "S1": { "maxLength": 72 },
14
+ * "N14": { "allow": ["GDP"] }
15
+ * }
16
+ * ```
17
+ *
18
+ * It also resolves the course's *own* rules — `courseRules`, a separate block
19
+ * with a separate module (`src/rules/course-rules.ts`) — so that one call hands
20
+ * the compiler every rule decision the course made, engine or not.
21
+ *
22
+ * `resolveRules` validates it (an unknown ID, an unknown key, a value outside
23
+ * its spec, or `enabled: false` on a locked rule is an error — the settings
24
+ * file is wrong, and a wrong bump would ship), applies every default from the
25
+ * catalog, folds in the deprecated top-level `operators` / `romanLabels` /
26
+ * `abbreviations` blocks, and hands the gates one object to ask. It never
27
+ * decides *what* a rule means — that is the catalog's job.
28
+ *
29
+ * `rulesJsonSchema` renders the same catalog as the JSON Schema fragment the
30
+ * course's settings form uses (`dist/rules/rules.schema.json`, built by
31
+ * `scripts/gen-rules-schema.mjs`), with properties ordered by element and an
32
+ * `x-element` hint per rule so the form can group them.
33
+ */
34
+
35
+ /** One rule's block in the settings file. Every key is optional. */
36
+ interface RuleSettingsInput {
37
+ readonly enabled?: boolean;
38
+ readonly [key: string]: unknown;
39
+ }
40
+ /** `CourseConfig.rules` — flat by rule ID. */
41
+ type CourseRulesConfig = Readonly<Record<string, RuleSettingsInput>>;
42
+ /** A non-fatal finding about the settings file itself. */
43
+ interface ConfigDiagnostic {
44
+ readonly code: 'config-deprecated-key' | 'config-retired-rule';
45
+ readonly severity: 'warning';
46
+ readonly message: string;
47
+ }
48
+ interface ResolvedRules {
49
+ /** False only for a rule the course turned off. An invariant is always on. */
50
+ isEnabled(id: string): boolean;
51
+ /** The course's own rules (`courseRules`), validated, in file order. */
52
+ readonly course: readonly CourseRule[];
53
+ /** The rule's settings with every default applied. `{}` for a rule without any. */
54
+ settings(id: string): Readonly<Record<string, unknown>>;
55
+ number(id: string, key: string): number;
56
+ strings(id: string, key: string): readonly string[];
57
+ /** Rules the course turned off — catalog order, then its own `X` rules. */
58
+ readonly disabled: readonly RuleId[];
59
+ readonly diagnostics: readonly ConfigDiagnostic[];
60
+ }
61
+ /**
62
+ * Validate `config.rules` (plus the deprecated top-level blocks) against the
63
+ * catalog and apply defaults. Throws on anything that would make a gate run
64
+ * with a value the author did not mean.
65
+ */
66
+ declare function resolveRules(config: CourseConfig): ResolvedRules;
67
+ /** Rule S2's word ranges in the shape `gatePartPages` takes. */
68
+ declare function partPageLimits(rules: ResolvedRules): NormalizedPartPages;
69
+ type JsonSchema = Record<string, unknown>;
70
+ /**
71
+ * The `rules` property for a course's `course.settings.schema.json`:
72
+ * self-contained (no `$ref`), properties ordered by primary element, then
73
+ * catalog order. A retired rule is not in the catalog at all, so it cannot be a
74
+ * form field; a block for one is a warning at resolve time.
75
+ */
76
+ declare function rulesJsonSchema(): JsonSchema;
77
+ /** Every rule and invariant ID, for tooling that wants the closed list. */
78
+ declare function catalogIds(): readonly string[];
79
+ /** What the manifest publishes about the course's rule settings. */
80
+ interface ManifestRules {
81
+ /** Rules the course turned off — catalog order, then its own `X` rules. */
82
+ readonly disabled: readonly RuleId[];
83
+ /** Every rule's settings with defaults applied, by ID. `{}` for a rule without any. */
84
+ readonly settings: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
85
+ /**
86
+ * The course's own rules, as written in `courseRules` and validated. Carried
87
+ * in the manifest so the rules-in-effect page and `content rules` list them
88
+ * beside the engine's without re-reading the settings file.
89
+ */
90
+ readonly course: readonly CourseRule[];
91
+ }
92
+ declare function rulesSnapshot(rules: ResolvedRules): ManifestRules;
93
+ /** The diagnostic codes owned by every rule the course turned off. */
94
+ declare function disabledCodes(rules: ResolvedRules): ReadonlySet<string>;
95
+ /**
96
+ * Drop every diagnostic a disabled rule owns. This is how a switch takes
97
+ * effect for a validator that hosts several owners (`consistency.ts`,
98
+ * `raw-floats-validate.ts`, the resolver behind `gate-math.ts`): the
99
+ * validator stays ignorant of the catalog and the manifest filters by code.
100
+ * A whole-rule gate (N9, N14, S1, S2) is additionally skipped at its call
101
+ * site so its work is not done at all. Curriculum issues carry `kind`, not
102
+ * `code`.
103
+ */
104
+ declare function dropDisabled<T extends {
105
+ readonly code?: string;
106
+ readonly kind?: string;
107
+ }>(diagnostics: readonly T[], disabled: ReadonlySet<string>): T[];
108
+
109
+ /**
110
+ * The shape `content/course.config.ts` must satisfy (Phase F).
111
+ *
112
+ * The engine never imports the course's config file; the wiring layer
113
+ * (`astro.config.mjs`, `scripts/*`, `src/content.config.ts`) imports the plain
114
+ * data value and passes it here, typed against `CourseConfig`. A second course
115
+ * supplies its own value of this shape.
116
+ */
117
+ /**
118
+ * One **part** — a sidebar group, holding the chapters (lessons) whose slugs
119
+ * start with `dir`. A part's 1-based position in `CourseConfig.parts` is the
120
+ * first number in every section, equation, table, and figure number inside
121
+ * it. See `docs/authoring/structure.md` for the full level taxonomy.
122
+ */
123
+ interface CoursePart {
124
+ /** Lesson-slug prefix, e.g. `bonds` for `bonds/yield-to-maturity`. */
125
+ readonly dir: string;
126
+ /** Sidebar group heading. */
127
+ readonly label: string;
128
+ /**
129
+ * The sidebar row that opens this part's **overview page**, when
130
+ * `content/docs/<dir>/index.mdx` exists. Defaults to `Overview`; the page's
131
+ * own title stays the part's name, which would only repeat the group
132
+ * heading one row below it.
133
+ */
134
+ readonly overviewLabel?: string;
135
+ }
136
+ /**
137
+ * @deprecated Renamed to {@link CoursePart}. "Section" now only ever means an
138
+ * `##` heading inside a lesson. Removed in the next MINOR release.
139
+ */
140
+ type CourseSection = CoursePart;
141
+ interface CourseConventions {
142
+ /** Notation key a lesson's stated sign convention must resolve to. */
143
+ readonly signConventionKey: string;
144
+ /** Competency id that also counts as declaring the cash-flow perspective. */
145
+ readonly cashFlowPerspectiveCompetency: string;
146
+ }
147
+ /**
148
+ * A course's edits to the shipped named-operator baseline (`BASE_OPERATORS`),
149
+ * consumed by the `operator-explained` consistency check (rule N8). An operator
150
+ * a lesson uses is fine when it is on the baseline plus `allow` minus `deny`;
151
+ * otherwise it needs a notation entry the reader can hover.
152
+ *
153
+ * Names are the KaTeX command without its backslash (`erf`, not `\erf`) for a
154
+ * built-in, and the verbatim body for an `\operatorname{…}` operator (`Var`).
155
+ */
156
+ interface CourseOperators {
157
+ /** Operators this course also treats as universal (`Var`, `Cov`, `sgn`, …). */
158
+ readonly allow?: readonly string[];
159
+ /** Baseline operators this course wants explained anyway. */
160
+ readonly deny?: readonly string[];
161
+ }
162
+ /**
163
+ * A course's edits to the shipped roman-label baseline (`BASE_ROMAN_LABELS`),
164
+ * consumed by the `roman-label-explained` consistency check (rule N10). A
165
+ * `\mathrm{…}` multi-letter label a lesson uses is fine when it is on the
166
+ * baseline plus `allow` minus `deny`; otherwise it needs a notation entry the
167
+ * reader can hover. Same shape as {@link CourseOperators}, one level down:
168
+ * this governs `\mathrm{KE}`-style labels, not `\operatorname{Var}`-style
169
+ * operators.
170
+ */
171
+ interface CourseRomanLabels {
172
+ /** Labels this course also treats as universal qualifiers (`eff`, `net`, …). */
173
+ readonly allow?: readonly string[];
174
+ /** Baseline labels this course wants explained anyway. */
175
+ readonly deny?: readonly string[];
176
+ }
177
+ /**
178
+ * A course's edits to the shipped accent baseline (`BASE_ACCENTS`, empty),
179
+ * consumed by the `accent-explained` consistency check (rule N22). A
180
+ * decoration a lesson uses (`\hat`, `\bar`, `'`, `*`, …) is fine when it is on
181
+ * the baseline plus `allow` minus `deny`, explained by an operator entry
182
+ * (`latex: \hat{}`), or part of a whole-form entry (`latex: \hat{\beta}`).
183
+ * Same shape as {@link CourseOperators}; set under `rules.N22` only.
184
+ */
185
+ interface CourseAccents {
186
+ /** Decorations this course treats as universal (`\dot`, `\vec`, …). */
187
+ readonly allow?: readonly string[];
188
+ /** Baseline decorations this course wants explained anyway. */
189
+ readonly deny?: readonly string[];
190
+ }
191
+ /**
192
+ * A course's edits to the shipped abbreviation baseline (`BASE_ABBREVIATIONS`),
193
+ * consumed by the `abbreviation-explained` gate (rule N14). An abbreviation-
194
+ * shaped token — an all-caps acronym or a dotted form like `w.r.t.` — a
195
+ * lesson uses in prose is fine when it is on the baseline plus `allow` minus
196
+ * `deny`; otherwise it needs an entry in the `abbreviations` content
197
+ * collection, or — if it also names a math quantity — a notation entry the
198
+ * reader can hover. Same shape as {@link CourseOperators} / {@link
199
+ * CourseRomanLabels}.
200
+ */
201
+ interface CourseAbbreviations {
202
+ /** Abbreviations this course also treats as universal (`GDP`, `CPU`, …). */
203
+ readonly allow?: readonly string[];
204
+ /** Baseline abbreviations this course wants explained anyway. */
205
+ readonly deny?: readonly string[];
206
+ }
207
+ /** One person or team credited for the study material. */
208
+ interface CourseAuthor {
209
+ readonly name: string;
210
+ /**
211
+ * Email, URL, or handle. Rendered in the About panel as a link when it looks
212
+ * like a `mailto:` or `https:` target, plain text otherwise.
213
+ */
214
+ readonly contact?: string;
215
+ }
216
+ /**
217
+ * The reader-facing "about this material" block, surfaced by the `[?]` panel in
218
+ * the header (`components/starlight/AboutPanel.astro`) on every page.
219
+ *
220
+ * `version` is the course's **knowledge version** — semver over the
221
+ * learning-facing content, independent of the `explico` engine's own version.
222
+ * It must equal the newest entry in `content/CHANGELOG.md`; a mismatch is a
223
+ * Tier-3 `knowledge-version-mismatch` warning (rule C3, `docs/authoring/versioning.md`).
224
+ */
225
+ interface CourseAbout {
226
+ /** Display name of the material. Defaults to {@link CourseConfig.title}. */
227
+ readonly materialName?: string;
228
+ /** One or two sentences on what the material covers. Defaults to {@link CourseConfig.description}. */
229
+ readonly summary?: string;
230
+ /** Knowledge version — semver; equals the latest `content/CHANGELOG.md` heading. */
231
+ readonly version: string;
232
+ /** Who authored / maintains the material. At least one entry. */
233
+ readonly authors: readonly CourseAuthor[];
234
+ /** Shown verbatim in the About panel — scope, warranty, AI-assistance note. */
235
+ readonly disclaimer: string;
236
+ /** Link to the published content changelog (repo blob, docs page, …). */
237
+ readonly changelogHref?: string;
238
+ }
239
+ /** A heading-like block's collapse default: may it fold, and does it start open? */
240
+ interface CourseHeadingDisclosure {
241
+ /** Wrap it in a collapsible section. */
242
+ readonly collapsible?: boolean;
243
+ /** Its state for a reader with nothing saved yet; ignored when not collapsible. */
244
+ readonly defaultOpen?: boolean;
245
+ }
246
+ /** A block that always folds; only its first-visit state is a course choice. */
247
+ interface CourseOpenDefault {
248
+ readonly defaultOpen?: boolean;
249
+ }
250
+ /**
251
+ * Every collapse default a course sets — the `disclosure` block of the course
252
+ * settings, in its own shape (docs/plans/titles-and-containers.md Part 4).
253
+ * Each kind names its default here:
254
+ *
255
+ * - **Full collapse**, the section wrapper (`CollapsibleSections.astro`):
256
+ * `sections` (`##`), `subsections` (`###`–`######`), `knowledgeChecks`, the
257
+ * generated `references` section, `memos` (a memo is a `######`-rank
258
+ * section of its own), `folds` (a `<Fold>`, the same without a memo's
259
+ * marker, letter or frame) and `workedExamples` (a set is a `##`-rank
260
+ * section of its own, Part 6). The notation panel (`notation`) folds the
261
+ * same way.
262
+ * - **A float's own fold**, which keeps its picture and caption line:
263
+ * `figures` sets whether a lab's tools start open
264
+ * (docs/plans/figures-and-short-captions.md, decision 5); a table is
265
+ * minimized by hand, and starts truncated when it has more than
266
+ * `tables.truncateAfter` rows. A figure or a diagram never folds by
267
+ * itself (decision 7).
268
+ *
269
+ * A heading's own marker (`[collapsible]`, `[collapsed]`, `[no-collapsible]`)
270
+ * and a component's `defaultOpen` prop override the kind's default at one
271
+ * place. Every collapsible remembers the reader's choice per page.
272
+ * {@link normalizeDisclosure} fills in the engine defaults — the values each
273
+ * kind had before they became settings.
274
+ */
275
+ interface CourseDisclosure {
276
+ readonly sections?: CourseHeadingDisclosure;
277
+ readonly subsections?: CourseHeadingDisclosure;
278
+ readonly knowledgeChecks?: CourseHeadingDisclosure;
279
+ readonly notation?: CourseOpenDefault;
280
+ readonly references?: CourseOpenDefault;
281
+ readonly memos?: CourseOpenDefault;
282
+ /** Whether a `<Fold>` starts open. */
283
+ readonly folds?: CourseOpenDefault;
284
+ /** Whether a lab's tools start open. */
285
+ readonly figures?: CourseOpenDefault;
286
+ readonly workedExamples?: CourseOpenDefault;
287
+ readonly tables?: {
288
+ /** A table with more rows than this starts truncated. Default `6`. */
289
+ readonly truncateAfter?: number;
290
+ };
291
+ }
292
+ /** {@link CourseDisclosure} with every default applied. */
293
+ interface NormalizedDisclosure {
294
+ readonly sections: Required<CourseHeadingDisclosure>;
295
+ readonly subsections: Required<CourseHeadingDisclosure>;
296
+ readonly knowledgeChecks: Required<CourseHeadingDisclosure>;
297
+ readonly notation: Required<CourseOpenDefault>;
298
+ readonly references: Required<CourseOpenDefault>;
299
+ readonly memos: Required<CourseOpenDefault>;
300
+ readonly folds: Required<CourseOpenDefault>;
301
+ readonly figures: Required<CourseOpenDefault>;
302
+ readonly workedExamples: Required<CourseOpenDefault>;
303
+ readonly tables: {
304
+ readonly truncateAfter: number;
305
+ };
306
+ }
307
+ /**
308
+ * The engine's defaults. Headings do not fold unless the course says so;
309
+ * the notation panel, References, memos, folds and worked examples start
310
+ * closed; a knowledge check folds and starts open; a lab's tools start open;
311
+ * a table over 6 rows starts truncated.
312
+ */
313
+ declare const DISCLOSURE_DEFAULTS: NormalizedDisclosure;
314
+ /** Validate `CourseConfig.disclosure` and apply its defaults. Throws on a bad value. */
315
+ declare function normalizeDisclosure(config: CourseConfig): NormalizedDisclosure;
316
+ /**
317
+ * How the course numbers its outline and its floats (`numbering` in the
318
+ * course settings; docs/authoring/structure.md § Numbering). Every field is
319
+ * optional; {@link normalizeNumbering} fills in the defaults.
320
+ */
321
+ interface CourseNumbering {
322
+ readonly sections?: {
323
+ /**
324
+ * Show section numbers: on wide screens, in print, and on a collapsed
325
+ * section's bar. Default `true`. Off, `[[sec-…]]` prints the title.
326
+ */
327
+ readonly show?: boolean;
328
+ /** The deepest heading level numbered, `2` (`##`) to `6`. Default `4`. */
329
+ readonly depth?: number;
330
+ };
331
+ /**
332
+ * The scope a float's counter runs over. `chapter` (default) restarts every
333
+ * kind in each chapter and prints `‹part›.‹chapter›.‹n›`; `part` runs on
334
+ * across the part's chapters and prints `‹part›.‹n›`. A plain `string`
335
+ * because the settings arrive as JSON; {@link normalizeNumbering} rejects
336
+ * any other value.
337
+ */
338
+ readonly floats?: string;
339
+ /**
340
+ * Resolve a number-form fragment a reader types (`#sec-1.1.2`,
341
+ * `#fig-2.2.1`) to the named anchor. Default `true`. The site itself only
342
+ * ever links and copies the named anchor.
343
+ */
344
+ readonly aliases?: boolean;
345
+ }
346
+ /** {@link CourseNumbering} with every default applied. */
347
+ interface NormalizedNumbering {
348
+ readonly sections: {
349
+ readonly show: boolean;
350
+ readonly depth: number;
351
+ };
352
+ readonly floats: 'chapter' | 'part';
353
+ readonly aliases: boolean;
354
+ }
355
+ /** Validate `CourseConfig.numbering` and apply its defaults. Throws on a bad value. */
356
+ declare function normalizeNumbering(config: CourseConfig): NormalizedNumbering;
357
+ /**
358
+ * How a knowledge check's and a worked-example set's heading names its kind
359
+ * (`blockLabels` in the course settings; docs/plans/titles-and-containers.md
360
+ * Q11). The engine draws the kind before the author's title: `icon-word`
361
+ * (the default) shows the kind's icon and its word, "Knowledge check" or
362
+ * "Example"; `icon` shows the icon alone and keeps the word for a screen
363
+ * reader.
364
+ */
365
+ type BlockLabels = 'icon' | 'icon-word';
366
+ /** The engine default for {@link CourseConfig.blockLabels}. */
367
+ declare const BLOCK_LABELS_DEFAULT: BlockLabels;
368
+ /** Validate `CourseConfig.blockLabels` and apply its default. Throws on a bad value. */
369
+ declare function normalizeBlockLabels(config: CourseConfig): BlockLabels;
370
+ /**
371
+ * Rule S2's word ranges (`rules.S2.bridgeMinWords` … in the course settings),
372
+ * in the shape `gate-part-pages.ts` takes. Built by `partPageLimits` in
373
+ * `src/rules/settings.ts`.
374
+ */
375
+ interface NormalizedPartPages {
376
+ readonly bridge: {
377
+ readonly minWords: number;
378
+ readonly maxWords: number;
379
+ };
380
+ readonly scope: {
381
+ readonly minWords: number;
382
+ readonly maxWords: number;
383
+ };
384
+ }
385
+ /** Optional, build-time lesson artifacts exposed to readers. */
386
+ interface LessonExportsConfig {
387
+ /** Emit one self-contained, offline-capable HTML file per lesson. */
388
+ readonly standaloneHtml?: boolean;
389
+ /** Offer the browser's native print dialog with explico's lesson print layout. */
390
+ readonly print?: boolean;
391
+ /**
392
+ * Published course root used for links that intentionally leave the exported
393
+ * lesson. Required at export time when such a link is present.
394
+ */
395
+ readonly canonicalBaseUrl?: string;
396
+ }
397
+ /** The stable, non-optional export settings carried by the manifest. */
398
+ interface NormalizedLessonExports {
399
+ readonly standaloneHtml: boolean;
400
+ readonly print: boolean;
401
+ readonly canonicalBaseUrl?: string;
402
+ }
403
+ interface CourseConfig {
404
+ readonly title: string;
405
+ readonly description: string;
406
+ /**
407
+ * The course's parts (sidebar groups), in order. Exactly one of `parts` or
408
+ * the deprecated `sections` must be given.
409
+ */
410
+ readonly parts?: readonly CoursePart[];
411
+ /**
412
+ * @deprecated Renamed to {@link CourseConfig.parts}. Still honoured, so a
413
+ * course on the old name keeps building; removed in the next MINOR release.
414
+ */
415
+ readonly sections?: readonly CoursePart[];
416
+ /** Namespaces used by competency / notation `domain` fields. */
417
+ readonly domains: readonly string[];
418
+ readonly conventions: CourseConventions;
419
+ /** Reader-facing identity + knowledge version (the `[?]` About panel). */
420
+ readonly about: CourseAbout;
421
+ /**
422
+ * Per-rule switches and settings, flat by rule ID — only what differs from
423
+ * the defaults (`{ "P5": { "enabled": false }, "S1": { "maxLength": 72 } }`).
424
+ * Resolved and validated by `resolveRules` (`src/rules/settings.ts`); the
425
+ * closed list of IDs and settings is `src/rules/catalog.ts`.
426
+ */
427
+ readonly rules?: CourseRulesConfig;
428
+ /**
429
+ * The course's *own* rules, on top of the engine's — `X1`…`X99`, each with
430
+ * its own text, elements, severity and optional `forbid` patterns. A list,
431
+ * not a map, so the settings form can add and remove one. Resolved and
432
+ * validated by `resolveCourseRules` (`src/rules/course-rules.ts`).
433
+ */
434
+ readonly courseRules?: CourseRuleDefinitions;
435
+ /** @deprecated Moved to `rules.N8` (same shape). Honoured for now; removed in a later MINOR. */
436
+ readonly operators?: CourseOperators;
437
+ /** @deprecated Moved to `rules.N10` (same shape). Honoured for now; removed in a later MINOR. */
438
+ readonly romanLabels?: CourseRomanLabels;
439
+ /** @deprecated Moved to `rules.N14` (same shape). Honoured for now; removed in a later MINOR. */
440
+ readonly abbreviations?: CourseAbbreviations;
441
+ /** Per-lesson export actions and downloadable artifacts. Disabled when omitted. */
442
+ readonly lessonExports?: LessonExportsConfig;
443
+ /** Every collapse default: headings, checks, memos, floats, tables. */
444
+ readonly disclosure?: CourseDisclosure;
445
+ /** Outline and float numbering. The defaults apply when omitted. */
446
+ readonly numbering?: CourseNumbering;
447
+ /**
448
+ * How a check's and an example set's heading names its kind: `icon-word`
449
+ * (default) or `icon` ({@link BlockLabels}). A plain `string` because the
450
+ * settings arrive as JSON; {@link normalizeBlockLabels} rejects any other
451
+ * value.
452
+ */
453
+ readonly blockLabels?: string;
454
+ }
455
+ /** Validate and canonicalize the build-facing lesson-export configuration. */
456
+ declare function normalizeLessonExports(config: CourseConfig): NormalizedLessonExports;
457
+ /**
458
+ * The course's parts, from whichever field the config uses. Throws rather than
459
+ * silently producing a course with no sidebar groups — a config with neither
460
+ * field would otherwise number every float by the `##`-section fallback.
461
+ */
462
+ declare function coursePartsOf(config: CourseConfig): readonly CoursePart[];
463
+
464
+ export { type CourseSection as A, type BlockLabels as B, type CourseConfig as C, DISCLOSURE_DEFAULTS as D, coursePartsOf as E, normalizeBlockLabels as F, normalizeDisclosure as G, normalizeLessonExports as H, normalizeNumbering as I, type LessonExportsConfig as L, type ManifestRules as M, type NormalizedPartPages as N, type ResolvedRules as R, type CourseAbbreviations as a, type NormalizedLessonExports as b, type NormalizedDisclosure as c, type NormalizedNumbering as d, type ConfigDiagnostic as e, type CourseConventions as f, type CourseOperators as g, type CourseRomanLabels as h, type CourseAccents 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, type CourseAbout as t, type CourseAuthor as u, type CourseDisclosure as v, type CourseHeadingDisclosure as w, type CourseNumbering as x, type CourseOpenDefault as y, type CoursePart as z };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The decorations a course treats as universal, given its `rules.N22` block:
3
+ * the shipped baseline, plus `allow`, minus `deny`.
4
+ *
5
+ * @param {{ allow?: readonly string[], deny?: readonly string[] } | undefined} accents
6
+ * @returns {Set<string>}
7
+ */
8
+ export function effectiveAccentAllowlist(accents: {
9
+ allow?: readonly string[];
10
+ deny?: readonly string[];
11
+ } | undefined): Set<string>;
12
+ /**
13
+ * The base accent baseline: decorations every reader is assumed to know, so a
14
+ * lesson never has to explain them (rule N22). The named-operator counterpart
15
+ * is `base-operators.mjs` (rule N8).
16
+ *
17
+ * It ships **empty**, because no decoration has one meaning across fields:
18
+ * `\hat` can mean an estimator, a unit vector or a Fourier transform; `\bar`
19
+ * a mean, a conjugate or a closure; `'` a derivative or "another one". A
20
+ * course that uses one consistently says so with `rules.N22.allow` (a physics
21
+ * course might allow `\dot` and `\vec`); anything else is explained by an
22
+ * operator entry (`latex: \hat{}`) or a whole-form entry (`latex:
23
+ * \hat{\beta}`). Names are the canonical ones in `DECORATION_NAMES`
24
+ * (`math-glyphs.mjs`): `\hat`, `\overline`, `'`, `*`, `\dagger`, ….
25
+ */
26
+ export const BASE_ACCENTS: Readonly<Set<any>>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The base accent baseline: decorations every reader is assumed to know, so a
3
+ * lesson never has to explain them (rule N22). The named-operator counterpart
4
+ * is `base-operators.mjs` (rule N8).
5
+ *
6
+ * It ships **empty**, because no decoration has one meaning across fields:
7
+ * `\hat` can mean an estimator, a unit vector or a Fourier transform; `\bar`
8
+ * a mean, a conjugate or a closure; `'` a derivative or "another one". A
9
+ * course that uses one consistently says so with `rules.N22.allow` (a physics
10
+ * course might allow `\dot` and `\vec`); anything else is explained by an
11
+ * operator entry (`latex: \hat{}`) or a whole-form entry (`latex:
12
+ * \hat{\beta}`). Names are the canonical ones in `DECORATION_NAMES`
13
+ * (`math-glyphs.mjs`): `\hat`, `\overline`, `'`, `*`, `\dagger`, ….
14
+ */
15
+ export const BASE_ACCENTS = Object.freeze(new Set());
16
+
17
+ /**
18
+ * The decorations a course treats as universal, given its `rules.N22` block:
19
+ * the shipped baseline, plus `allow`, minus `deny`.
20
+ *
21
+ * @param {{ allow?: readonly string[], deny?: readonly string[] } | undefined} accents
22
+ * @returns {Set<string>}
23
+ */
24
+ export function effectiveAccentAllowlist(accents) {
25
+ const allow = accents?.allow ?? [];
26
+ const deny = new Set(accents?.deny ?? []);
27
+ const effective = new Set(BASE_ACCENTS);
28
+ for (const name of allow) effective.add(name);
29
+ for (const name of deny) effective.delete(name);
30
+ return effective;
31
+ }
@@ -18,8 +18,8 @@ export function isBaseRomanLabel(name: any, romanLabels: any): boolean;
18
18
  * (rule N8); this is its `\mathrm{…}` twin (rule N10).
19
19
  *
20
20
  * Rationale: the completeness gate has no opinion about `\mathrm{…}` bodies —
21
- * they are a typographic label to KaTeX, not a bindable symbol, and N3
22
- * requires the format without asking whether the label is ever explained.
21
+ * they are a typographic label to KaTeX, not a bindable symbol, so the
22
+ * format half of rule N10 says nothing about whether the label is explained.
23
23
  * The `roman-label-explained` consistency check fills that gap: a label a
24
24
  * lesson uses is either on this list (or the course's additions to it) or it
25
25
  * carries a notation entry the reader can hover.
@@ -5,8 +5,8 @@
5
5
  * (rule N8); this is its `\mathrm{…}` twin (rule N10).
6
6
  *
7
7
  * Rationale: the completeness gate has no opinion about `\mathrm{…}` bodies —
8
- * they are a typographic label to KaTeX, not a bindable symbol, and N3
9
- * requires the format without asking whether the label is ever explained.
8
+ * they are a typographic label to KaTeX, not a bindable symbol, so the
9
+ * format half of rule N10 says nothing about whether the label is explained.
10
10
  * The `roman-label-explained` consistency check fills that gap: a label a
11
11
  * lesson uses is either on this list (or the course's additions to it) or it
12
12
  * carries a notation entry the reader can hover.
@@ -1,8 +1,10 @@
1
- import { CourseConventions, CourseOperators, CourseRomanLabels } from '../compiler/course-config.js';
1
+ import { f as CourseConventions, g as CourseOperators, h as CourseRomanLabels, i as CourseAccents } from '../course-config-Dsnri9Tk.js';
2
2
  import { CurriculumCatalog } from '../curriculum/validation.js';
3
3
  import { NotationSpec } from '../compiler/collections.js';
4
4
  import { LintIgnore } from './lint-ignore.js';
5
5
  import { NotationRegistry, NotationRegistryInput } from './types.js';
6
+ import '../rules/catalog.js';
7
+ import '../rules/course-rules.js';
6
8
 
7
9
  /**
8
10
  * Corpus consistency checks (Step D6) — the Tier 1–2 checks from the cleanup
@@ -30,12 +32,20 @@ import { NotationRegistry, NotationRegistryInput } from './types.js';
30
32
  * uses is on the shipped/course roman-label
31
33
  * allowlist or carries a notation entry
32
34
  * (rule N10)
35
+ * (j) accent-explained a decoration a lesson composes (`\hat`,
36
+ * `'`, `^*`, …) is on the course accent
37
+ * allowlist, explained by an operator entry,
38
+ * or part of a whole-form entry (rule N22)
39
+ * (k) operator-suspended / a `notation.suspend` item, with or without
40
+ * operator-suspension-reason its reason, and a whole-form entry that
41
+ * / operator-shadowed shadows an operator the page does not
42
+ * suspend (rule N22)
33
43
  *
34
44
  * Check (d) — the committed per-lesson resolution report — lives in
35
45
  * `src/content/resolution-report.ts`.
36
46
  */
37
47
 
38
- type ConsistencyCode = 'glyph-unique-in-corpus' | 'notation-units' | 'units-vocab' | 'convention-single-definition' | 'weak-local' | 'notation-source-locator' | 'gloss-wants-promoting' | 'card-wants-demoting' | 'gloss-name-shape' | 'operator-explained' | 'roman-label-explained';
48
+ type ConsistencyCode = 'glyph-unique-in-corpus' | 'notation-units' | 'units-vocab' | 'convention-single-definition' | 'weak-local' | 'notation-source-locator' | 'gloss-wants-promoting' | 'card-wants-demoting' | 'gloss-name-shape' | 'operator-explained' | 'roman-label-explained' | 'accent-explained' | 'operator-suspended' | 'operator-suspension-reason' | 'operator-shadowed';
39
49
  interface ConsistencyDiagnostic {
40
50
  readonly code: ConsistencyCode;
41
51
  /** Tier 3 — repairable violations are warnings; optional advice is info. */
@@ -60,6 +70,8 @@ interface ConsistencyInput {
60
70
  readonly operators?: CourseOperators;
61
71
  /** This course's edits to the roman-label baseline (rule N10). */
62
72
  readonly romanLabels?: CourseRomanLabels;
73
+ /** This course's edits to the (empty) accent baseline (rule N22). */
74
+ readonly accents?: CourseAccents;
63
75
  }
64
76
  /**
65
77
  * Run every consistency check. The result is sorted deterministically (code,