explico 0.8.0 → 0.11.1
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.
- package/CHANGELOG.md +1257 -1
- package/README.md +60 -15
- package/dist/compiler/astro.d.ts +6 -1
- package/dist/compiler/cli.d.ts +46 -2
- package/dist/compiler/cli.js +70 -4
- package/dist/compiler/collections.d.ts +35 -2
- package/dist/compiler/collections.js +68 -5
- package/dist/compiler/course-config.d.ts +3 -194
- package/dist/compiler/course-config.js +78 -5
- package/dist/compiler/exports/standalone-html.d.ts +6 -1
- package/dist/compiler/knowledge-version.js +1 -1
- package/dist/compiler/lesson-derivation.d.ts +27 -10
- package/dist/compiler/lesson-derivation.js +23 -8
- package/dist/compiler/logging.d.ts +4 -1
- package/dist/compiler/logging.js +4 -1
- package/dist/compiler/manifest.d.ts +204 -31
- package/dist/compiler/manifest.js +270 -55
- package/dist/compiler/resolution-report.d.ts +6 -1
- package/dist/course-config-Dsnri9Tk.d.ts +464 -0
- package/dist/reference/base-accents.d.mts +26 -0
- package/dist/reference/base-accents.mjs +31 -0
- package/dist/reference/base-roman-labels.d.mts +2 -2
- package/dist/reference/base-roman-labels.mjs +2 -2
- package/dist/reference/consistency.d.ts +14 -2
- package/dist/reference/consistency.js +108 -4
- package/dist/reference/delimiter-size.d.ts +3 -2
- package/dist/reference/diagrams-validate.d.ts +7 -2
- package/dist/reference/diagrams-validate.js +9 -1
- package/dist/reference/equations-validate.d.ts +8 -5
- package/dist/reference/equations-validate.js +1 -0
- package/dist/reference/equations.d.mts +60 -31
- package/dist/reference/equations.mjs +83 -43
- package/dist/reference/examples-validate.d.ts +31 -0
- package/dist/reference/examples-validate.js +48 -0
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/figures-validate.js +4 -1
- package/dist/reference/folds-validate.d.ts +33 -0
- package/dist/reference/folds-validate.js +32 -0
- package/dist/reference/gate-abbreviations.d.ts +3 -1
- package/dist/reference/gate-abbreviations.js +1 -0
- package/dist/reference/gate-course-rules.d.ts +32 -0
- package/dist/reference/gate-course-rules.js +85 -0
- package/dist/reference/gate-heading-length.d.ts +5 -2
- package/dist/reference/gate-heading-length.js +45 -21
- package/dist/reference/gate-heading-structure.d.ts +41 -0
- package/dist/reference/gate-heading-structure.js +113 -0
- package/dist/reference/gate-math-punctuation.d.ts +71 -0
- package/dist/reference/gate-math-punctuation.js +251 -0
- package/dist/reference/gate-math.d.ts +22 -6
- package/dist/reference/gate-math.js +42 -8
- package/dist/reference/gate-part-pages.d.ts +50 -0
- package/dist/reference/gate-part-pages.js +118 -0
- package/dist/reference/gate-prose-notation.d.ts +21 -11
- package/dist/reference/gate-prose-notation.js +25 -7
- package/dist/reference/katex-delimiters.mjs +20 -7
- package/dist/reference/keyed-float-validate.d.ts +27 -10
- package/dist/reference/keyed-float-validate.js +15 -1
- package/dist/reference/keyed-jsx-floats.d.mts +11 -5
- package/dist/reference/keyed-jsx-floats.mjs +73 -20
- package/dist/reference/knowledge-checks-validate.d.ts +1 -1
- package/dist/reference/knowledge-checks.d.mts +3 -1
- package/dist/reference/knowledge-checks.mjs +9 -4
- package/dist/reference/lab-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-slot.d.ts +34 -0
- package/dist/reference/lab-math-slot.js +6 -0
- package/dist/reference/math-glyphs.d.mts +80 -4
- package/dist/reference/math-glyphs.mjs +572 -37
- package/dist/reference/page-notation.d.ts +6 -1
- package/dist/reference/raw-floats-validate.js +1 -1
- package/dist/reference/raw-floats.mjs +1 -1
- package/dist/reference/rehype-heading-anchors.d.mts +14 -0
- package/dist/reference/rehype-heading-anchors.mjs +116 -17
- package/dist/reference/rehype-table-header-labels.d.mts +6 -1
- package/dist/reference/rehype-table-header-labels.mjs +21 -3
- package/dist/reference/remark-asides.d.mts +3 -0
- package/dist/reference/remark-asides.mjs +180 -0
- package/dist/reference/remark-memo.d.mts +3 -1
- package/dist/reference/remark-memo.mjs +42 -11
- package/dist/reference/remark-notation.d.mts +60 -10
- package/dist/reference/remark-notation.mjs +387 -36
- package/dist/reference/render-lab-math.d.ts +4 -26
- package/dist/reference/render-lab-math.js +4 -4
- package/dist/reference/sections-validate.d.ts +27 -0
- package/dist/reference/sections-validate.js +41 -0
- package/dist/reference/sections.d.mts +97 -0
- package/dist/reference/sections.mjs +306 -0
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/types.d.ts +17 -1
- package/dist/reference/unit-words.d.ts +37 -0
- package/dist/reference/unit-words.js +170 -0
- package/dist/rules/catalog.d.ts +176 -0
- package/dist/rules/catalog.js +983 -0
- package/dist/rules/course-rules.d.ts +115 -0
- package/dist/rules/course-rules.js +292 -0
- package/dist/rules/course-rules.schema.json +138 -0
- package/dist/rules/in-effect.d.ts +53 -0
- package/dist/rules/in-effect.js +142 -0
- package/dist/rules/rules.schema.json +1072 -0
- package/dist/rules/settings.d.ts +3 -0
- package/dist/rules/settings.js +290 -0
- package/dist/rules/waivers.d.ts +53 -0
- package/dist/rules/waivers.js +132 -0
- package/dist/session/PreferenceStore.d.ts +5 -0
- package/dist/session/PreferenceStore.js +9 -0
- package/docs/VERSIONING.md +5 -3
- package/docs/authoring/README.md +232 -40
- package/docs/authoring/agents.md +55 -9
- package/docs/authoring/components.md +178 -22
- package/docs/authoring/migrating.md +1152 -2
- package/docs/authoring/notation.md +576 -322
- package/docs/authoring/prose.md +551 -0
- package/docs/authoring/retired.md +35 -0
- package/docs/authoring/structure-rules.md +422 -40
- package/docs/authoring/structure.md +445 -62
- package/docs/authoring/versioning.md +20 -28
- package/docs/authoring/visualizations.md +142 -20
- package/package.json +2 -2
- package/src/compiler/cli.ts +116 -6
- package/src/compiler/collections.ts +111 -7
- package/src/compiler/course-config.ts +273 -34
- package/src/compiler/knowledge-version.ts +1 -1
- package/src/compiler/lesson-derivation.ts +54 -15
- package/src/compiler/logging.ts +8 -2
- package/src/compiler/manifest.ts +573 -104
- package/src/components/BlockHeading.astro +113 -0
- package/src/components/CourseContents.astro +221 -0
- package/src/components/CurriculumMap.astro +5 -6
- package/src/components/FloatIndex.astro +200 -0
- package/src/components/HoverPanel.astro +431 -79
- package/src/components/PartBridge.astro +27 -0
- package/src/components/PartOverview.astro +401 -0
- package/src/components/PartScope.astro +27 -0
- package/src/components/abbreviation/AbbreviationLayer.astro +49 -51
- package/src/components/assessments/AssessmentRunner.tsx +8 -5
- package/src/components/assessments/AssessmentSet.astro +23 -26
- package/src/components/block-heading.ts +146 -0
- package/src/components/card-arrow.ts +243 -0
- package/src/components/card-drag.ts +631 -0
- package/src/components/card-jump.ts +45 -0
- package/src/components/card-manager.ts +1589 -0
- package/src/components/card-memory.ts +177 -0
- package/src/components/card-placement.ts +504 -0
- package/src/components/card-side.ts +336 -0
- package/src/components/card-travel.ts +197 -0
- package/src/components/citation/CitationLayer.astro +77 -76
- package/src/components/diagram/Diagram.astro +161 -316
- package/src/components/disclosure/course-disclosure.ts +24 -0
- package/src/components/disclosure/hover-preview.ts +122 -42
- package/src/components/disclosure/persisted-disclosure.ts +3 -3
- package/src/components/disclosure/print-as-shown.ts +252 -0
- package/src/components/examples/CompactExample.astro +3 -1
- package/src/components/examples/CompactExamples.astro +101 -94
- package/src/components/figure/Figure.astro +174 -274
- package/src/components/figure/FloatCaption.astro +87 -0
- package/src/components/figure/figure-state.ts +58 -0
- package/src/components/figure/figure-tools.ts +87 -0
- package/src/components/float-name.ts +101 -0
- package/src/components/fold/Fold.astro +125 -0
- package/src/components/fold/fold-id.ts +40 -0
- package/src/components/memo/Memo.astro +162 -231
- package/src/components/memo/ShortNote.astro +6 -6
- package/src/components/notation/NotationFormula.astro +5 -1
- package/src/components/notation/NotationGlossary.astro +3 -4
- package/src/components/notation/NotationLayer.astro +169 -162
- package/src/components/notation/NotationMath.astro +10 -1
- package/src/components/reference/FloatRefLayer.astro +485 -423
- package/src/components/starlight/AboutPanel.astro +5 -4
- package/src/components/starlight/CollapsibleSections.astro +207 -93
- package/src/components/starlight/DiagramEnhancer.astro +15 -138
- package/src/components/starlight/HeadingAnchorEnhancer.astro +193 -1
- package/src/components/starlight/KeyedFloatEnhancer.astro +100 -9
- package/src/components/starlight/LayoutHeader.astro +298 -19
- package/src/components/starlight/LayoutPageSidebar.astro +99 -12
- package/src/components/starlight/LayoutPageTitle.astro +120 -0
- package/src/components/starlight/LayoutSidebar.astro +229 -11
- package/src/components/starlight/LessonExportActions.astro +53 -72
- package/src/components/starlight/LessonFooter.astro +26 -4
- package/src/components/starlight/section-parts.ts +15 -2
- package/src/components/starlight/section-summary.ts +181 -0
- package/src/components/table/Table.astro +95 -76
- package/src/components/table/TableOverflowExpander.astro +65 -28
- package/src/content-config.ts +40 -4
- package/src/reference/base-accents.mjs +31 -0
- package/src/reference/base-roman-labels.mjs +2 -2
- package/src/reference/consistency.ts +176 -5
- package/src/reference/delimiter-size.ts +3 -2
- package/src/reference/diagrams-validate.ts +17 -1
- package/src/reference/equations-validate.ts +10 -5
- package/src/reference/equations.mjs +83 -43
- package/src/reference/examples-validate.ts +97 -0
- package/src/reference/figures-validate.ts +6 -1
- package/src/reference/folds-validate.ts +65 -0
- package/src/reference/gate-abbreviations.ts +1 -0
- package/src/reference/gate-course-rules.ts +134 -0
- package/src/reference/gate-heading-length.ts +66 -35
- package/src/reference/gate-heading-structure.ts +193 -0
- package/src/reference/gate-math-punctuation.ts +381 -0
- package/src/reference/gate-math.ts +69 -10
- package/src/reference/gate-part-pages.ts +217 -0
- package/src/reference/gate-prose-notation.ts +59 -12
- package/src/reference/katex-delimiters.mjs +20 -7
- package/src/reference/keyed-float-validate.ts +45 -11
- package/src/reference/keyed-jsx-floats.mjs +73 -20
- package/src/reference/knowledge-checks-validate.ts +1 -1
- package/src/reference/knowledge-checks.mjs +9 -4
- package/src/reference/lab-math-slot.ts +36 -0
- package/src/reference/math-glyphs.mjs +572 -37
- package/src/reference/raw-floats-validate.ts +1 -1
- package/src/reference/raw-floats.mjs +1 -1
- package/src/reference/rehype-heading-anchors.mjs +116 -17
- package/src/reference/rehype-table-header-labels.mjs +21 -3
- package/src/reference/remark-asides.mjs +180 -0
- package/src/reference/remark-memo.mjs +42 -11
- package/src/reference/remark-notation.mjs +387 -36
- package/src/reference/render-lab-math.ts +16 -31
- package/src/reference/sections-validate.ts +81 -0
- package/src/reference/sections.mjs +306 -0
- package/src/reference/tables-validate.ts +2 -1
- package/src/reference/types.ts +17 -0
- package/src/reference/unit-words.ts +233 -0
- package/src/rules/catalog.ts +1298 -0
- package/src/rules/course-rules.ts +456 -0
- package/src/rules/in-effect.ts +235 -0
- package/src/rules/settings.ts +448 -0
- package/src/rules/waivers.ts +249 -0
- package/src/session/PreferenceStore.ts +15 -0
- package/src/styles/global.css +1467 -163
- package/src/styles/surfaces.css +182 -0
- package/src/components/hover-panel.ts +0 -339
- package/src/components/popup-stack.ts +0 -345
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { CourseRulesConfig } from '../rules/settings';
|
|
2
|
+
import type { CourseRuleDefinitions } from '../rules/course-rules';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* The shape `content/course.config.ts` must satisfy (Phase F).
|
|
3
6
|
*
|
|
@@ -10,14 +13,21 @@
|
|
|
10
13
|
/**
|
|
11
14
|
* One **part** — a sidebar group, holding the chapters (lessons) whose slugs
|
|
12
15
|
* start with `dir`. A part's 1-based position in `CourseConfig.parts` is the
|
|
13
|
-
* number
|
|
14
|
-
* See `docs/authoring/structure.md` for the full level taxonomy.
|
|
16
|
+
* first number in every section, equation, table, and figure number inside
|
|
17
|
+
* it. See `docs/authoring/structure.md` for the full level taxonomy.
|
|
15
18
|
*/
|
|
16
19
|
export interface CoursePart {
|
|
17
20
|
/** Lesson-slug prefix, e.g. `bonds` for `bonds/yield-to-maturity`. */
|
|
18
21
|
readonly dir: string;
|
|
19
22
|
/** Sidebar group heading. */
|
|
20
23
|
readonly label: string;
|
|
24
|
+
/**
|
|
25
|
+
* The sidebar row that opens this part's **overview page**, when
|
|
26
|
+
* `content/docs/<dir>/index.mdx` exists. Defaults to `Overview`; the page's
|
|
27
|
+
* own title stays the part's name, which would only repeat the group
|
|
28
|
+
* heading one row below it.
|
|
29
|
+
*/
|
|
30
|
+
readonly overviewLabel?: string;
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
/**
|
|
@@ -65,6 +75,21 @@ export interface CourseRomanLabels {
|
|
|
65
75
|
readonly deny?: readonly string[];
|
|
66
76
|
}
|
|
67
77
|
|
|
78
|
+
/**
|
|
79
|
+
* A course's edits to the shipped accent baseline (`BASE_ACCENTS`, empty),
|
|
80
|
+
* consumed by the `accent-explained` consistency check (rule N22). A
|
|
81
|
+
* decoration a lesson uses (`\hat`, `\bar`, `'`, `*`, …) is fine when it is on
|
|
82
|
+
* the baseline plus `allow` minus `deny`, explained by an operator entry
|
|
83
|
+
* (`latex: \hat{}`), or part of a whole-form entry (`latex: \hat{\beta}`).
|
|
84
|
+
* Same shape as {@link CourseOperators}; set under `rules.N22` only.
|
|
85
|
+
*/
|
|
86
|
+
export interface CourseAccents {
|
|
87
|
+
/** Decorations this course treats as universal (`\dot`, `\vec`, …). */
|
|
88
|
+
readonly allow?: readonly string[];
|
|
89
|
+
/** Baseline decorations this course wants explained anyway. */
|
|
90
|
+
readonly deny?: readonly string[];
|
|
91
|
+
}
|
|
92
|
+
|
|
68
93
|
/**
|
|
69
94
|
* A course's edits to the shipped abbreviation baseline (`BASE_ABBREVIATIONS`),
|
|
70
95
|
* consumed by the `abbreviation-explained` gate (rule N14). An abbreviation-
|
|
@@ -116,43 +141,235 @@ export interface CourseAbout {
|
|
|
116
141
|
readonly changelogHref?: string;
|
|
117
142
|
}
|
|
118
143
|
|
|
144
|
+
/** A heading-like block's collapse default: may it fold, and does it start open? */
|
|
145
|
+
export interface CourseHeadingDisclosure {
|
|
146
|
+
/** Wrap it in a collapsible section. */
|
|
147
|
+
readonly collapsible?: boolean;
|
|
148
|
+
/** Its state for a reader with nothing saved yet; ignored when not collapsible. */
|
|
149
|
+
readonly defaultOpen?: boolean;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** A block that always folds; only its first-visit state is a course choice. */
|
|
153
|
+
export interface CourseOpenDefault {
|
|
154
|
+
readonly defaultOpen?: boolean;
|
|
155
|
+
}
|
|
156
|
+
|
|
119
157
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* independent of `defaultCollapsible.sections`/`.subsections` (an author's
|
|
124
|
-
* own `##` headings): both of these are engine-authored, not written by a
|
|
125
|
-
* lesson, so they need their own default rather than inheriting the site's
|
|
126
|
-
* general heading default.
|
|
158
|
+
* Every collapse default a course sets — the `disclosure` block of the course
|
|
159
|
+
* settings, in its own shape (docs/plans/titles-and-containers.md Part 4).
|
|
160
|
+
* Each kind names its default here:
|
|
127
161
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
162
|
+
* - **Full collapse**, the section wrapper (`CollapsibleSections.astro`):
|
|
163
|
+
* `sections` (`##`), `subsections` (`###`–`######`), `knowledgeChecks`, the
|
|
164
|
+
* generated `references` section, `memos` (a memo is a `######`-rank
|
|
165
|
+
* section of its own), `folds` (a `<Fold>`, the same without a memo's
|
|
166
|
+
* marker, letter or frame) and `workedExamples` (a set is a `##`-rank
|
|
167
|
+
* section of its own, Part 6). The notation panel (`notation`) folds the
|
|
168
|
+
* same way.
|
|
169
|
+
* - **A float's own fold**, which keeps its picture and caption line:
|
|
170
|
+
* `figures` sets whether a lab's tools start open
|
|
171
|
+
* (docs/plans/figures-and-short-captions.md, decision 5); a table is
|
|
172
|
+
* minimized by hand, and starts truncated when it has more than
|
|
173
|
+
* `tables.truncateAfter` rows. A figure or a diagram never folds by
|
|
174
|
+
* itself (decision 7).
|
|
175
|
+
*
|
|
176
|
+
* A heading's own marker (`[collapsible]`, `[collapsed]`, `[no-collapsible]`)
|
|
177
|
+
* and a component's `defaultOpen` prop override the kind's default at one
|
|
178
|
+
* place. Every collapsible remembers the reader's choice per page.
|
|
179
|
+
* {@link normalizeDisclosure} fills in the engine defaults — the values each
|
|
180
|
+
* kind had before they became settings.
|
|
131
181
|
*/
|
|
132
|
-
export interface
|
|
133
|
-
|
|
134
|
-
readonly
|
|
135
|
-
|
|
136
|
-
readonly
|
|
182
|
+
export interface CourseDisclosure {
|
|
183
|
+
readonly sections?: CourseHeadingDisclosure;
|
|
184
|
+
readonly subsections?: CourseHeadingDisclosure;
|
|
185
|
+
readonly knowledgeChecks?: CourseHeadingDisclosure;
|
|
186
|
+
readonly notation?: CourseOpenDefault;
|
|
187
|
+
readonly references?: CourseOpenDefault;
|
|
188
|
+
readonly memos?: CourseOpenDefault;
|
|
189
|
+
/** Whether a `<Fold>` starts open. */
|
|
190
|
+
readonly folds?: CourseOpenDefault;
|
|
191
|
+
/** Whether a lab's tools start open. */
|
|
192
|
+
readonly figures?: CourseOpenDefault;
|
|
193
|
+
readonly workedExamples?: CourseOpenDefault;
|
|
194
|
+
readonly tables?: {
|
|
195
|
+
/** A table with more rows than this starts truncated. Default `6`. */
|
|
196
|
+
readonly truncateAfter?: number;
|
|
197
|
+
};
|
|
137
198
|
}
|
|
138
199
|
|
|
139
|
-
/**
|
|
140
|
-
export interface
|
|
141
|
-
readonly
|
|
142
|
-
readonly
|
|
200
|
+
/** {@link CourseDisclosure} with every default applied. */
|
|
201
|
+
export interface NormalizedDisclosure {
|
|
202
|
+
readonly sections: Required<CourseHeadingDisclosure>;
|
|
203
|
+
readonly subsections: Required<CourseHeadingDisclosure>;
|
|
204
|
+
readonly knowledgeChecks: Required<CourseHeadingDisclosure>;
|
|
205
|
+
readonly notation: Required<CourseOpenDefault>;
|
|
206
|
+
readonly references: Required<CourseOpenDefault>;
|
|
207
|
+
readonly memos: Required<CourseOpenDefault>;
|
|
208
|
+
readonly folds: Required<CourseOpenDefault>;
|
|
209
|
+
readonly figures: Required<CourseOpenDefault>;
|
|
210
|
+
readonly workedExamples: Required<CourseOpenDefault>;
|
|
211
|
+
readonly tables: { readonly truncateAfter: number };
|
|
143
212
|
}
|
|
144
213
|
|
|
145
|
-
/**
|
|
146
|
-
*
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
214
|
+
/**
|
|
215
|
+
* The engine's defaults. Headings do not fold unless the course says so;
|
|
216
|
+
* the notation panel, References, memos, folds and worked examples start
|
|
217
|
+
* closed; a knowledge check folds and starts open; a lab's tools start open;
|
|
218
|
+
* a table over 6 rows starts truncated.
|
|
219
|
+
*/
|
|
220
|
+
export const DISCLOSURE_DEFAULTS: NormalizedDisclosure = {
|
|
221
|
+
sections: { collapsible: false, defaultOpen: true },
|
|
222
|
+
subsections: { collapsible: false, defaultOpen: true },
|
|
223
|
+
knowledgeChecks: { collapsible: true, defaultOpen: true },
|
|
224
|
+
notation: { defaultOpen: false },
|
|
225
|
+
references: { defaultOpen: false },
|
|
226
|
+
memos: { defaultOpen: false },
|
|
227
|
+
folds: { defaultOpen: false },
|
|
228
|
+
figures: { defaultOpen: true },
|
|
229
|
+
workedExamples: { defaultOpen: false },
|
|
230
|
+
tables: { truncateAfter: 6 },
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/** Validate `CourseConfig.disclosure` and apply its defaults. Throws on a bad value. */
|
|
234
|
+
export function normalizeDisclosure(config: CourseConfig): NormalizedDisclosure {
|
|
235
|
+
const disclosure = config.disclosure ?? {};
|
|
236
|
+
if ('diagrams' in disclosure) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
'CourseConfig disclosure.diagrams is gone: a diagram no longer folds by ' +
|
|
239
|
+
'itself. Delete the key; to let a reader fold a diagram away, wrap it ' +
|
|
240
|
+
'in a <Fold> or put it in a collapsible section.',
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
const heading = (
|
|
244
|
+
key: 'sections' | 'subsections' | 'knowledgeChecks',
|
|
245
|
+
): Required<CourseHeadingDisclosure> => ({
|
|
246
|
+
collapsible: disclosure[key]?.collapsible ?? DISCLOSURE_DEFAULTS[key].collapsible,
|
|
247
|
+
defaultOpen: disclosure[key]?.defaultOpen ?? DISCLOSURE_DEFAULTS[key].defaultOpen,
|
|
248
|
+
});
|
|
249
|
+
const open = (
|
|
250
|
+
key: 'notation' | 'references' | 'memos' | 'folds' | 'figures' | 'workedExamples',
|
|
251
|
+
): Required<CourseOpenDefault> => ({
|
|
252
|
+
defaultOpen: disclosure[key]?.defaultOpen ?? DISCLOSURE_DEFAULTS[key].defaultOpen,
|
|
253
|
+
});
|
|
254
|
+
const truncateAfter =
|
|
255
|
+
disclosure.tables?.truncateAfter ?? DISCLOSURE_DEFAULTS.tables.truncateAfter;
|
|
256
|
+
if (!Number.isInteger(truncateAfter) || truncateAfter < 2) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
`CourseConfig disclosure.tables.truncateAfter must be a whole number of ` +
|
|
259
|
+
`rows, at least 2; received ${JSON.stringify(truncateAfter)}.`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
150
262
|
return {
|
|
151
|
-
|
|
152
|
-
|
|
263
|
+
sections: heading('sections'),
|
|
264
|
+
subsections: heading('subsections'),
|
|
265
|
+
knowledgeChecks: heading('knowledgeChecks'),
|
|
266
|
+
notation: open('notation'),
|
|
267
|
+
references: open('references'),
|
|
268
|
+
memos: open('memos'),
|
|
269
|
+
folds: open('folds'),
|
|
270
|
+
figures: open('figures'),
|
|
271
|
+
workedExamples: open('workedExamples'),
|
|
272
|
+
tables: { truncateAfter },
|
|
153
273
|
};
|
|
154
274
|
}
|
|
155
275
|
|
|
276
|
+
/**
|
|
277
|
+
* How the course numbers its outline and its floats (`numbering` in the
|
|
278
|
+
* course settings; docs/authoring/structure.md § Numbering). Every field is
|
|
279
|
+
* optional; {@link normalizeNumbering} fills in the defaults.
|
|
280
|
+
*/
|
|
281
|
+
export interface CourseNumbering {
|
|
282
|
+
readonly sections?: {
|
|
283
|
+
/**
|
|
284
|
+
* Show section numbers: on wide screens, in print, and on a collapsed
|
|
285
|
+
* section's bar. Default `true`. Off, `[[sec-…]]` prints the title.
|
|
286
|
+
*/
|
|
287
|
+
readonly show?: boolean;
|
|
288
|
+
/** The deepest heading level numbered, `2` (`##`) to `6`. Default `4`. */
|
|
289
|
+
readonly depth?: number;
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* The scope a float's counter runs over. `chapter` (default) restarts every
|
|
293
|
+
* kind in each chapter and prints `‹part›.‹chapter›.‹n›`; `part` runs on
|
|
294
|
+
* across the part's chapters and prints `‹part›.‹n›`. A plain `string`
|
|
295
|
+
* because the settings arrive as JSON; {@link normalizeNumbering} rejects
|
|
296
|
+
* any other value.
|
|
297
|
+
*/
|
|
298
|
+
readonly floats?: string;
|
|
299
|
+
/**
|
|
300
|
+
* Resolve a number-form fragment a reader types (`#sec-1.1.2`,
|
|
301
|
+
* `#fig-2.2.1`) to the named anchor. Default `true`. The site itself only
|
|
302
|
+
* ever links and copies the named anchor.
|
|
303
|
+
*/
|
|
304
|
+
readonly aliases?: boolean;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** {@link CourseNumbering} with every default applied. */
|
|
308
|
+
export interface NormalizedNumbering {
|
|
309
|
+
readonly sections: { readonly show: boolean; readonly depth: number };
|
|
310
|
+
readonly floats: 'chapter' | 'part';
|
|
311
|
+
readonly aliases: boolean;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Validate `CourseConfig.numbering` and apply its defaults. Throws on a bad value. */
|
|
315
|
+
export function normalizeNumbering(config: CourseConfig): NormalizedNumbering {
|
|
316
|
+
const numbering = config.numbering ?? {};
|
|
317
|
+
const depth = numbering.sections?.depth ?? 4;
|
|
318
|
+
if (!Number.isInteger(depth) || depth < 2 || depth > 6) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
`CourseConfig numbering.sections.depth must be a heading level from 2 ` +
|
|
321
|
+
`(##) to 6 (######); received ${JSON.stringify(depth)}.`,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
const floats = numbering.floats ?? 'chapter';
|
|
325
|
+
if (floats !== 'chapter' && floats !== 'part') {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`CourseConfig numbering.floats must be "chapter" or "part"; received ` +
|
|
328
|
+
`${JSON.stringify(floats)}.`,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
sections: { show: numbering.sections?.show !== false, depth },
|
|
333
|
+
floats,
|
|
334
|
+
aliases: numbering.aliases !== false,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* How a knowledge check's and a worked-example set's heading names its kind
|
|
340
|
+
* (`blockLabels` in the course settings; docs/plans/titles-and-containers.md
|
|
341
|
+
* Q11). The engine draws the kind before the author's title: `icon-word`
|
|
342
|
+
* (the default) shows the kind's icon and its word, "Knowledge check" or
|
|
343
|
+
* "Example"; `icon` shows the icon alone and keeps the word for a screen
|
|
344
|
+
* reader.
|
|
345
|
+
*/
|
|
346
|
+
export type BlockLabels = 'icon' | 'icon-word';
|
|
347
|
+
|
|
348
|
+
/** The engine default for {@link CourseConfig.blockLabels}. */
|
|
349
|
+
export const BLOCK_LABELS_DEFAULT: BlockLabels = 'icon-word';
|
|
350
|
+
|
|
351
|
+
/** Validate `CourseConfig.blockLabels` and apply its default. Throws on a bad value. */
|
|
352
|
+
export function normalizeBlockLabels(config: CourseConfig): BlockLabels {
|
|
353
|
+
const labels = config.blockLabels ?? BLOCK_LABELS_DEFAULT;
|
|
354
|
+
if (labels !== 'icon' && labels !== 'icon-word') {
|
|
355
|
+
throw new Error(
|
|
356
|
+
`CourseConfig blockLabels must be "icon" or "icon-word"; received ` +
|
|
357
|
+
`${JSON.stringify(labels)}.`,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
return labels;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Rule S2's word ranges (`rules.S2.bridgeMinWords` … in the course settings),
|
|
365
|
+
* in the shape `gate-part-pages.ts` takes. Built by `partPageLimits` in
|
|
366
|
+
* `src/rules/settings.ts`.
|
|
367
|
+
*/
|
|
368
|
+
export interface NormalizedPartPages {
|
|
369
|
+
readonly bridge: { readonly minWords: number; readonly maxWords: number };
|
|
370
|
+
readonly scope: { readonly minWords: number; readonly maxWords: number };
|
|
371
|
+
}
|
|
372
|
+
|
|
156
373
|
/** Optional, build-time lesson artifacts exposed to readers. */
|
|
157
374
|
export interface LessonExportsConfig {
|
|
158
375
|
/** Emit one self-contained, offline-capable HTML file per lesson. */
|
|
@@ -191,17 +408,39 @@ export interface CourseConfig {
|
|
|
191
408
|
readonly conventions: CourseConventions;
|
|
192
409
|
/** Reader-facing identity + knowledge version (the `[?]` About panel). */
|
|
193
410
|
readonly about: CourseAbout;
|
|
194
|
-
/**
|
|
411
|
+
/**
|
|
412
|
+
* Per-rule switches and settings, flat by rule ID — only what differs from
|
|
413
|
+
* the defaults (`{ "P5": { "enabled": false }, "S1": { "maxLength": 72 } }`).
|
|
414
|
+
* Resolved and validated by `resolveRules` (`src/rules/settings.ts`); the
|
|
415
|
+
* closed list of IDs and settings is `src/rules/catalog.ts`.
|
|
416
|
+
*/
|
|
417
|
+
readonly rules?: CourseRulesConfig;
|
|
418
|
+
/**
|
|
419
|
+
* The course's *own* rules, on top of the engine's — `X1`…`X99`, each with
|
|
420
|
+
* its own text, elements, severity and optional `forbid` patterns. A list,
|
|
421
|
+
* not a map, so the settings form can add and remove one. Resolved and
|
|
422
|
+
* validated by `resolveCourseRules` (`src/rules/course-rules.ts`).
|
|
423
|
+
*/
|
|
424
|
+
readonly courseRules?: CourseRuleDefinitions;
|
|
425
|
+
/** @deprecated Moved to `rules.N8` (same shape). Honoured for now; removed in a later MINOR. */
|
|
195
426
|
readonly operators?: CourseOperators;
|
|
196
|
-
/**
|
|
427
|
+
/** @deprecated Moved to `rules.N10` (same shape). Honoured for now; removed in a later MINOR. */
|
|
197
428
|
readonly romanLabels?: CourseRomanLabels;
|
|
198
|
-
/**
|
|
429
|
+
/** @deprecated Moved to `rules.N14` (same shape). Honoured for now; removed in a later MINOR. */
|
|
199
430
|
readonly abbreviations?: CourseAbbreviations;
|
|
200
431
|
/** Per-lesson export actions and downloadable artifacts. Disabled when omitted. */
|
|
201
432
|
readonly lessonExports?: LessonExportsConfig;
|
|
202
|
-
/**
|
|
203
|
-
|
|
204
|
-
|
|
433
|
+
/** Every collapse default: headings, checks, memos, floats, tables. */
|
|
434
|
+
readonly disclosure?: CourseDisclosure;
|
|
435
|
+
/** Outline and float numbering. The defaults apply when omitted. */
|
|
436
|
+
readonly numbering?: CourseNumbering;
|
|
437
|
+
/**
|
|
438
|
+
* How a check's and an example set's heading names its kind: `icon-word`
|
|
439
|
+
* (default) or `icon` ({@link BlockLabels}). A plain `string` because the
|
|
440
|
+
* settings arrive as JSON; {@link normalizeBlockLabels} rejects any other
|
|
441
|
+
* value.
|
|
442
|
+
*/
|
|
443
|
+
readonly blockLabels?: string;
|
|
205
444
|
}
|
|
206
445
|
|
|
207
446
|
/** Validate and canonicalize the build-facing lesson-export configuration. */
|
|
@@ -141,7 +141,7 @@ export function resolveKnowledgeVersion(options: {
|
|
|
141
141
|
if (!latest) {
|
|
142
142
|
warn(
|
|
143
143
|
'knowledge-changelog-missing',
|
|
144
|
-
`${file} has no released \`## [x.y.z]\` entry — only \`## [Unreleased]\`. Cut a release: move it to \`## [${declaredVersion}] - <date>\` (rule
|
|
144
|
+
`${file} has no released \`## [x.y.z]\` entry — only \`## [Unreleased]\`. Cut a release: move it to \`## [${declaredVersion}] - <date>\` (rule C3).`,
|
|
145
145
|
);
|
|
146
146
|
return { version: declaredVersion, diagnostics };
|
|
147
147
|
}
|
|
@@ -136,27 +136,48 @@ export function mergeTrackOrder(tracks: readonly TrackLike[]): string[] {
|
|
|
136
136
|
export interface SidebarSection {
|
|
137
137
|
readonly dir: string;
|
|
138
138
|
readonly label: string;
|
|
139
|
+
readonly overviewLabel?: string;
|
|
140
|
+
/**
|
|
141
|
+
* Href of this part's overview page (`/<dir>/`), set only when the part has
|
|
142
|
+
* one. It becomes the group's first item, so the page a part directory
|
|
143
|
+
* resolves to is reachable from the navigation that names the part.
|
|
144
|
+
*/
|
|
145
|
+
readonly overviewLink?: string;
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
/**
|
|
149
|
+
* One sidebar entry: a lesson slug, or an explicit link. `attrs` is Starlight's
|
|
150
|
+
* own passthrough onto the rendered `<a>` — the engine uses it to mark a part's
|
|
151
|
+
* overview row so CSS can give it its own icon.
|
|
152
|
+
*/
|
|
153
|
+
export type SidebarItem =
|
|
154
|
+
| string
|
|
143
155
|
| {
|
|
144
156
|
readonly label: string;
|
|
145
|
-
readonly
|
|
146
|
-
|
|
147
|
-
readonly link: string;
|
|
148
|
-
}[];
|
|
157
|
+
readonly link: string;
|
|
158
|
+
readonly attrs?: Readonly<Record<string, string>>;
|
|
149
159
|
};
|
|
150
160
|
|
|
161
|
+
export interface SidebarGroup {
|
|
162
|
+
readonly label: string;
|
|
163
|
+
readonly items: readonly SidebarItem[];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Marks the overview row so `global.css` can give it its own icon. */
|
|
167
|
+
export const PART_OVERVIEW_CLASS = 'explico-part-overview';
|
|
168
|
+
|
|
151
169
|
/**
|
|
152
170
|
* The Starlight `sidebar` config: one group per section, lessons ordered by the
|
|
153
171
|
* merged track order (ties broken by slug), then the static Reference group.
|
|
154
172
|
* Replaces seven `autogenerate` blocks that read an authored `sidebar.order`.
|
|
173
|
+
* `floatIndex`: the course has a top-level "Figures and tables" page
|
|
174
|
+
* (`figures-and-tables.mdx`, `FloatIndex.astro`), listed in Reference.
|
|
155
175
|
*/
|
|
156
176
|
export function buildSidebar(
|
|
157
177
|
tracks: readonly TrackLike[],
|
|
158
178
|
lessonSlugs: readonly string[],
|
|
159
179
|
parts: readonly SidebarSection[],
|
|
180
|
+
{ floatIndex = false }: { readonly floatIndex?: boolean } = {},
|
|
160
181
|
): SidebarGroup[] {
|
|
161
182
|
const rank = new Map(
|
|
162
183
|
mergeTrackOrder(tracks).map((lessonId, index) => [lessonId, index]),
|
|
@@ -164,21 +185,39 @@ export function buildSidebar(
|
|
|
164
185
|
const positionOf = (slug: string) =>
|
|
165
186
|
rank.get(lessonIdFromSlug(slug)) ?? Number.POSITIVE_INFINITY;
|
|
166
187
|
|
|
167
|
-
const groups: SidebarGroup[] = parts.map(
|
|
168
|
-
label,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
188
|
+
const groups: SidebarGroup[] = parts.map(
|
|
189
|
+
({ dir, label, overviewLabel, overviewLink }) => {
|
|
190
|
+
const chapters = lessonSlugs
|
|
191
|
+
.filter((slug) => slug.startsWith(`${dir}/`))
|
|
192
|
+
.sort(
|
|
193
|
+
(left, right) =>
|
|
194
|
+
positionOf(left) - positionOf(right) || left.localeCompare(right),
|
|
195
|
+
);
|
|
196
|
+
// The overview leads the group: it is the part's own page, so it reads
|
|
197
|
+
// as the chapter list's heading rather than an appendix to it, and in
|
|
198
|
+
// compact mode it is the first icon instead of the last.
|
|
199
|
+
return {
|
|
200
|
+
label,
|
|
201
|
+
items: overviewLink
|
|
202
|
+
? [
|
|
203
|
+
{
|
|
204
|
+
label: overviewLabel ?? 'Overview',
|
|
205
|
+
link: overviewLink,
|
|
206
|
+
attrs: { class: PART_OVERVIEW_CLASS },
|
|
207
|
+
},
|
|
208
|
+
...chapters,
|
|
209
|
+
]
|
|
210
|
+
: chapters,
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
);
|
|
176
214
|
|
|
177
215
|
groups.push({
|
|
178
216
|
label: 'Reference',
|
|
179
217
|
items: [
|
|
180
218
|
{ label: 'Curriculum map', link: '/curriculum-map/' },
|
|
181
219
|
{ label: 'Notation glossary', link: '/glossary/' },
|
|
220
|
+
...(floatIndex ? [{ label: 'Figures and tables', link: '/figures-and-tables/' }] : []),
|
|
182
221
|
],
|
|
183
222
|
});
|
|
184
223
|
|
package/src/compiler/logging.ts
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
writeFileSync,
|
|
43
43
|
} from 'node:fs';
|
|
44
44
|
import { join, relative } from 'node:path';
|
|
45
|
+
import { ruleIdOf } from '../rules/catalog';
|
|
45
46
|
|
|
46
47
|
const REPOSITORY_ROOT = process.cwd();
|
|
47
48
|
/** Sits beside `build/manifest.json` and `build/resolution/` (Phase F). */
|
|
@@ -163,14 +164,19 @@ export interface RunLog {
|
|
|
163
164
|
* `[code] file:line:column: message`, matching what the console has always
|
|
164
165
|
* printed, with ` (eq:<key> -> <href>)` appended when the finding belongs to a
|
|
165
166
|
* keyed equation — so a size-only finding inside a long `aligned` block names
|
|
166
|
-
* the result it is about, not just its coordinates.
|
|
167
|
+
* the result it is about, not just its coordinates. A code owned by a lettered
|
|
168
|
+
* rule is prefixed with that rule's ID (`[N9 prose-notation]`), so the line
|
|
169
|
+
* names the rule page to read; an invariant's code stands alone, and a course
|
|
170
|
+
* rule's code *is* its ID (`[X1]`), so it is not repeated.
|
|
167
171
|
*/
|
|
168
172
|
export function diagnosticLine(diagnostic: AnyDiagnostic): string {
|
|
169
173
|
const code = diagnostic.code ?? diagnostic.kind;
|
|
170
174
|
const at = [diagnostic.file, diagnostic.line, diagnostic.column]
|
|
171
175
|
.filter((part) => part !== undefined)
|
|
172
176
|
.join(':');
|
|
173
|
-
const
|
|
177
|
+
const owner = code === undefined ? undefined : ruleIdOf(code);
|
|
178
|
+
const rule = owner === undefined || owner === code ? '' : `${owner} `;
|
|
179
|
+
const prefix = code === undefined ? '' : `[${rule}${code}] `;
|
|
174
180
|
const equation =
|
|
175
181
|
diagnostic.equation === undefined
|
|
176
182
|
? ''
|