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.
- package/CHANGELOG.md +1409 -1
- package/README.md +60 -15
- package/dist/compiler/astro.d.ts +6 -1
- package/dist/compiler/cli.d.ts +54 -3
- package/dist/compiler/cli.js +80 -6
- package/dist/compiler/collections.d.ts +35 -2
- package/dist/compiler/collections.js +84 -8
- 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 +14 -1
- package/dist/compiler/logging.js +6 -2
- 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 +21 -11
- package/dist/reference/delimiter-size.js +15 -39
- 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 +69 -34
- package/dist/reference/equations.mjs +90 -44
- 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 +3 -2
- 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 +47 -23
- 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 +44 -6
- package/dist/reference/gate-math.js +63 -10
- 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 +32 -11
- package/dist/reference/gate-prose-notation.js +30 -8
- package/dist/reference/katex-delimiters.d.mts +15 -0
- package/dist/reference/katex-delimiters.mjs +138 -0
- package/dist/reference/katex-options.mjs +4 -0
- 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 +588 -39
- 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 +5 -5
- 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 +24 -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 +2 -2
- 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 +1324 -2
- package/docs/authoring/notation.md +616 -344
- 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/docs/katex-bracket-scaling.md +191 -0
- package/package.json +3 -2
- package/src/compiler/cli.ts +139 -6
- package/src/compiler/collections.ts +144 -10
- 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 +21 -4
- 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 +24 -14
- package/src/components/notation/NotationLayer.astro +169 -162
- package/src/components/notation/NotationMath.astro +10 -1
- package/src/components/notation/render-notation-math.ts +111 -10
- package/src/components/reference/FloatRefLayer.astro +485 -423
- package/src/components/starlight/AboutPanel.astro +22 -6
- 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 +40 -61
- package/src/reference/diagrams-validate.ts +17 -1
- package/src/reference/equations-validate.ts +10 -5
- package/src/reference/equations.mjs +90 -44
- 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 +3 -2
- package/src/reference/gate-course-rules.ts +134 -0
- package/src/reference/gate-heading-length.ts +68 -37
- package/src/reference/gate-heading-structure.ts +193 -0
- package/src/reference/gate-math-punctuation.ts +381 -0
- package/src/reference/gate-math.ts +119 -12
- package/src/reference/gate-part-pages.ts +217 -0
- package/src/reference/gate-prose-notation.ts +74 -13
- package/src/reference/katex-delimiters.mjs +138 -0
- package/src/reference/katex-options.mjs +4 -0
- 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 +588 -39
- 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 +17 -32
- 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 +24 -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 +1471 -163
- package/src/styles/surfaces.css +182 -0
- package/src/components/hover-panel.ts +0 -339
- package/src/components/popup-stack.ts +0 -345
package/README.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="playground/src/assets/explico-mark-dark.svg">
|
|
3
|
+
<img src="playground/src/assets/explico-mark.svg" alt="" width="88" align="right">
|
|
4
|
+
</picture>
|
|
5
|
+
|
|
1
6
|
# explico
|
|
2
7
|
|
|
3
8
|
A build-time engine for **prerequisite-aware, notation-rigorous course
|
|
@@ -23,6 +28,13 @@ What it gives a course:
|
|
|
23
28
|
- **Equation identity** — `\label{eq:key}` gives a display equation a stable
|
|
24
29
|
key, an appearance-order number, an anchor, and cross-lesson `[[eq-key]]`
|
|
25
30
|
references.
|
|
31
|
+
- **Outline numbering** — sections number `part.chapter.section` like a book
|
|
32
|
+
(`§ 1.1.2`), floats `part.chapter.n` (`Figure 2.2.1`); `[[sec-…]]` cites a
|
|
33
|
+
section, a heading's chain icon copies its link, and a typed number form
|
|
34
|
+
(`#sec-1.1.2`) lands on the named anchor. A knowledge check and a set of
|
|
35
|
+
worked examples are numbered sections of their own: the author writes the
|
|
36
|
+
topic, the engine the kind's badge and the number (`[[ex-…]]`,
|
|
37
|
+
`[[check-…]]`).
|
|
26
38
|
- **A KaTeX trust boundary** — build-time HTML + MathML; the trust callback
|
|
27
39
|
accepts exactly one validated `data-notation-key`. No runtime math, no CDN.
|
|
28
40
|
- **A deterministic JSON manifest** — the MCP/LMS contract: lessons, notation,
|
|
@@ -78,8 +90,10 @@ import remarkNotation, {
|
|
|
78
90
|
} from 'explico/reference/remark-notation.mjs';
|
|
79
91
|
import remarkShortNote from 'explico/reference/remark-short-note.mjs';
|
|
80
92
|
import remarkCitation from 'explico/reference/remark-citation.mjs';
|
|
93
|
+
import remarkAsides from 'explico/reference/remark-asides.mjs';
|
|
81
94
|
import { createNotationKatexOptions } from 'explico/reference/katex-options.mjs';
|
|
82
95
|
import rehypeFailKatexErrors from 'explico/reference/rehype-fail-katex-errors.mjs';
|
|
96
|
+
import rehypeHeadingAnchors from 'explico/reference/rehype-heading-anchors.mjs';
|
|
83
97
|
import { courseConfig } from './content/course.config.ts';
|
|
84
98
|
|
|
85
99
|
const manifest = await loadManifest(courseConfig);
|
|
@@ -93,14 +107,26 @@ const manifest = await loadManifest(courseConfig);
|
|
|
93
107
|
// // the build when a catalogued lesson has equations/tables/figures but no
|
|
94
108
|
// // numbering or cross-page entry, instead of silently numbering it by
|
|
95
109
|
// // `##` section while references to it read the part scheme.
|
|
96
|
-
//
|
|
97
|
-
//
|
|
110
|
+
// // `base` is the Astro `base` of a subpath deployment (e.g. a GitHub
|
|
111
|
+
// // Pages project site); the links these plugins write 404 without it.
|
|
112
|
+
// [remarkNotation, createRemarkNotationOptions(manifest, { strict: true, base })],
|
|
113
|
+
// // The generated References section's first state, from the course's
|
|
114
|
+
// // `disclosure` settings block (`CourseConfig.disclosure`).
|
|
115
|
+
// [remarkCitation, {
|
|
116
|
+
// sources: manifest.sources,
|
|
117
|
+
// defaultOpen: courseConfig.disclosure?.references?.defaultOpen === true,
|
|
118
|
+
// }],
|
|
119
|
+
// // Last: renders `:::note` / `:::tip` / `:::caution` / `:::danger` as
|
|
120
|
+
// // Starlight's `<Aside>` (Starlight's own pass skips `content/docs/`).
|
|
121
|
+
// remarkAsides,
|
|
98
122
|
// ]
|
|
99
123
|
// … integrations: [explicoContentRefresh(courseConfig, manifest),
|
|
100
124
|
// starlight({ customCss: ['explico/styles/global.css'],
|
|
101
125
|
// components: { Footer: 'explico/components/starlight/LessonFooter.astro',
|
|
102
126
|
// SocialIcons: 'explico/components/starlight/LayoutSocialIcons.astro', … } })
|
|
103
|
-
// … rehypePlugins: [[rehypeKatex, createNotationKatexOptions()], rehypeFailKatexErrors
|
|
127
|
+
// … rehypePlugins: [[rehypeKatex, createNotationKatexOptions()], rehypeFailKatexErrors,
|
|
128
|
+
// // Heading collapse defaults: the same `disclosure` block.
|
|
129
|
+
// [rehypeHeadingAnchors, { defaultCollapsible: courseConfig.disclosure }]]
|
|
104
130
|
```
|
|
105
131
|
|
|
106
132
|
`loadManifest(courseConfig)` always recompiles current content before Astro
|
|
@@ -109,18 +135,18 @@ and restarts the development server when any file under `content/` is added,
|
|
|
109
135
|
changed, or removed, keeping Markdown transforms and UI components on the same
|
|
110
136
|
model without an editor-specific task.
|
|
111
137
|
|
|
112
|
-
A memo is a substantial optional derivation: it
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
fragment:
|
|
138
|
+
A memo is a substantial optional derivation: it folds like a `######`
|
|
139
|
+
section without adding a heading to the page outline. Cite it from earlier
|
|
140
|
+
prose with `[[memo-id]]` — a superscript letter — then place the block
|
|
141
|
+
wherever it best fits. The citation and the chain icon on its bar link to the
|
|
142
|
+
same URL fragment:
|
|
117
143
|
|
|
118
144
|
```mdx
|
|
119
145
|
The omitted calculation is recorded in [[memo-full-check]].
|
|
120
146
|
|
|
121
147
|
Several paragraphs may intervene.
|
|
122
148
|
|
|
123
|
-
<Memo id="full-check"
|
|
149
|
+
<Memo id="full-check" short="Full check" caption="The omitted algebra and evidence">
|
|
124
150
|
|
|
125
151
|
The longer derivation can contain paragraphs, equations, tables, and figures.
|
|
126
152
|
|
|
@@ -140,9 +166,24 @@ Differentiate the polynomial, set the result to zero, and solve.
|
|
|
140
166
|
</ShortNote>. But symmetry is quicker.
|
|
141
167
|
```
|
|
142
168
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
169
|
+
A memo requires literal `id` and `caption` strings (`short` is optional); a
|
|
170
|
+
short note requires `id`, `title` and `description`. Memos start as the
|
|
171
|
+
course's `disclosure.memos` says (closed by default) and persist reader state
|
|
172
|
+
unless `rememberState={false}`; short notes always start collapsed.
|
|
173
|
+
|
|
174
|
+
A figure or a diagram never folds by itself. To let a reader fold one away
|
|
175
|
+
where it stands — or any stretch of content — import
|
|
176
|
+
`explico/components/fold/Fold.astro` and wrap it in `<Fold>`: no heading,
|
|
177
|
+
number or letter, only a bar with the section chevron. The bar is its `label`
|
|
178
|
+
or, without one, the numbers and short captions of the floats inside
|
|
179
|
+
("Diagram 2.1.1 Steps of the derivation"). Folds start as the course's
|
|
180
|
+
`disclosure.folds` says (closed by default), or as `defaultOpen` says:
|
|
181
|
+
|
|
182
|
+
```mdx
|
|
183
|
+
<Fold>
|
|
184
|
+
<Diagram id="steps" caption="…" short="Steps of the derivation" nodes={…} edges={…} />
|
|
185
|
+
</Fold>
|
|
186
|
+
```
|
|
146
187
|
|
|
147
188
|
### Lesson exports
|
|
148
189
|
|
|
@@ -176,9 +217,13 @@ viewer’s system color scheme.
|
|
|
176
217
|
|
|
177
218
|
The **Print / save as PDF** action calls the browser's native print dialog; it
|
|
178
219
|
does not download or build a `.pdf` file itself. The print layout removes site
|
|
179
|
-
chrome
|
|
180
|
-
|
|
181
|
-
|
|
220
|
+
chrome and prints every disclosure as the reader left it on screen: an open
|
|
221
|
+
section, figure, memo, or question prints in full, and a closed one prints its
|
|
222
|
+
title and a "(content collapsed — full text: …)" note with its permalink.
|
|
223
|
+
Complete data surfaces (every table row, every worked example of an open set)
|
|
224
|
+
still print in full. It does all this without changing saved reader state,
|
|
225
|
+
hides controls that have no paper equivalent while retaining their current
|
|
226
|
+
visual result, and keeps linked text clickable with canonical destinations.
|
|
182
227
|
Paper size, margins, browser-added headers/footers, final filename, and the
|
|
183
228
|
choice to save as PDF remain browser and reader settings.
|
|
184
229
|
|
package/dist/compiler/astro.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AstroConfig, AstroIntegration } from 'astro';
|
|
2
|
-
import { CourseConfig } from '
|
|
2
|
+
import { C as CourseConfig } from '../course-config-Dsnri9Tk.js';
|
|
3
3
|
import { Manifest } from './manifest.js';
|
|
4
|
+
import '../rules/catalog.js';
|
|
5
|
+
import '../rules/course-rules.js';
|
|
4
6
|
import '../curriculum/validation.js';
|
|
5
7
|
import '../reference/consistency.js';
|
|
6
8
|
import './collections.js';
|
|
@@ -11,11 +13,14 @@ import '../reference/tables-validate.js';
|
|
|
11
13
|
import '../reference/keyed-float-validate.js';
|
|
12
14
|
import '../reference/figures-validate.js';
|
|
13
15
|
import '../reference/diagrams-validate.js';
|
|
16
|
+
import '../reference/examples-validate.js';
|
|
17
|
+
import '../reference/folds-validate.js';
|
|
14
18
|
import '../reference/knowledge-checks-validate.js';
|
|
15
19
|
import '../reference/raw-floats-validate.js';
|
|
16
20
|
import '../reference/gate-math.js';
|
|
17
21
|
import '../reference/mixed-content.js';
|
|
18
22
|
import '../reference/gloss.js';
|
|
23
|
+
import '../rules/waivers.js';
|
|
19
24
|
import './knowledge-version.js';
|
|
20
25
|
import './lesson-derivation.js';
|
|
21
26
|
|
package/dist/compiler/cli.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { DiagnosticSeverity } from '../reference/types.js';
|
|
2
|
+
import { EquationLink } from '../reference/gate-math.js';
|
|
2
3
|
import { Manifest, ManifestLesson } from './manifest.js';
|
|
4
|
+
import { RulesFilter, RuleInEffect } from '../rules/in-effect.js';
|
|
3
5
|
import '../curriculum/validation.js';
|
|
4
6
|
import '../reference/consistency.js';
|
|
5
|
-
import '
|
|
7
|
+
import '../course-config-Dsnri9Tk.js';
|
|
8
|
+
import '../rules/catalog.js';
|
|
9
|
+
import '../rules/course-rules.js';
|
|
6
10
|
import './collections.js';
|
|
7
11
|
import '../reference/lint-ignore.js';
|
|
8
12
|
import '../reference/equations-validate.js';
|
|
@@ -10,11 +14,13 @@ import '../reference/tables-validate.js';
|
|
|
10
14
|
import '../reference/keyed-float-validate.js';
|
|
11
15
|
import '../reference/figures-validate.js';
|
|
12
16
|
import '../reference/diagrams-validate.js';
|
|
17
|
+
import '../reference/examples-validate.js';
|
|
18
|
+
import '../reference/folds-validate.js';
|
|
13
19
|
import '../reference/knowledge-checks-validate.js';
|
|
14
20
|
import '../reference/raw-floats-validate.js';
|
|
15
|
-
import '../reference/gate-math.js';
|
|
16
21
|
import '../reference/mixed-content.js';
|
|
17
22
|
import '../reference/gloss.js';
|
|
23
|
+
import '../rules/waivers.js';
|
|
18
24
|
import './knowledge-version.js';
|
|
19
25
|
import './lesson-derivation.js';
|
|
20
26
|
|
|
@@ -56,11 +62,24 @@ declare class ContentCliError extends Error {
|
|
|
56
62
|
/** One normalized diagnostic. `code` is `<source>:<kind>` and always stable. */
|
|
57
63
|
interface CliDiagnostic {
|
|
58
64
|
readonly code: string;
|
|
65
|
+
/**
|
|
66
|
+
* The catalog entry that owns the code — a rule ID (`N9`) or an invariant
|
|
67
|
+
* slug (`keyed-floats`). Absent only for a code the catalog does not know,
|
|
68
|
+
* which `tests/rules/catalog.test.ts` treats as a bug.
|
|
69
|
+
*/
|
|
70
|
+
readonly rule?: string;
|
|
59
71
|
readonly severity: DiagnosticSeverity;
|
|
60
72
|
readonly message: string;
|
|
61
73
|
readonly file?: string;
|
|
62
74
|
readonly line?: number;
|
|
63
75
|
readonly column?: number;
|
|
76
|
+
/**
|
|
77
|
+
* The keyed equation the finding sits in, when the math carries a
|
|
78
|
+
* `\label{eq:…}` — `{ key, href }`, the href being the anchor on the
|
|
79
|
+
* rendered page. A size-only finding inside a long `aligned` block is
|
|
80
|
+
* otherwise anonymous.
|
|
81
|
+
*/
|
|
82
|
+
readonly equation?: EquationLink;
|
|
64
83
|
}
|
|
65
84
|
/**
|
|
66
85
|
* Every manifest diagnostic that names this lesson (by id or file), normalized
|
|
@@ -116,6 +135,7 @@ interface StatusReport {
|
|
|
116
135
|
readonly keyedTables: number;
|
|
117
136
|
readonly keyedFigures: number;
|
|
118
137
|
readonly keyedDiagrams: number;
|
|
138
|
+
readonly keyedExamples: number;
|
|
119
139
|
};
|
|
120
140
|
readonly draftDebt: {
|
|
121
141
|
readonly lessons: readonly string[];
|
|
@@ -143,6 +163,23 @@ interface WarningsReport {
|
|
|
143
163
|
declare function contentDiagnostics(manifest: Manifest, minimumSeverity?: DiagnosticThreshold): DiagnosticsReport;
|
|
144
164
|
/** Every warning exactly; errors and informational advice are excluded. */
|
|
145
165
|
declare function contentWarnings(manifest: Manifest): WarningsReport;
|
|
166
|
+
interface RulesReport {
|
|
167
|
+
readonly filter: RulesFilter;
|
|
168
|
+
readonly count: number;
|
|
169
|
+
readonly rules: readonly RuleInEffect[];
|
|
170
|
+
/** The same selection as the page `validate:content` writes. */
|
|
171
|
+
readonly markdown: string;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Parse `--element a,b` / `--enforcement script` into a filter, throwing a
|
|
175
|
+
* usage error on a value outside the vocabulary.
|
|
176
|
+
*/
|
|
177
|
+
declare function parseRulesFilter(input: {
|
|
178
|
+
readonly element?: string;
|
|
179
|
+
readonly enforcement?: string;
|
|
180
|
+
}): RulesFilter;
|
|
181
|
+
/** The rules this course has in effect, optionally narrowed. */
|
|
182
|
+
declare function contentRules(manifest: Manifest, filter?: RulesFilter): RulesReport;
|
|
146
183
|
interface ContextReport {
|
|
147
184
|
readonly lesson: {
|
|
148
185
|
readonly id: string;
|
|
@@ -189,9 +226,23 @@ interface ContextReport {
|
|
|
189
226
|
key: string;
|
|
190
227
|
number: string;
|
|
191
228
|
}[];
|
|
229
|
+
/** Worked-example sets: `short` is the title `[[ex-key]]` names. */
|
|
230
|
+
readonly examples: readonly {
|
|
231
|
+
key: string;
|
|
232
|
+
number: string;
|
|
233
|
+
short?: string;
|
|
234
|
+
}[];
|
|
192
235
|
readonly knowledgeChecks: readonly {
|
|
193
236
|
key: string;
|
|
194
237
|
number: string;
|
|
238
|
+
title?: string;
|
|
239
|
+
}[];
|
|
240
|
+
/** The top-level headings: `slug` is what `[[sec-slug]]` names. */
|
|
241
|
+
readonly sections: readonly {
|
|
242
|
+
slug: string;
|
|
243
|
+
text: string;
|
|
244
|
+
level: number;
|
|
245
|
+
number?: string;
|
|
195
246
|
}[];
|
|
196
247
|
readonly sources: readonly {
|
|
197
248
|
id: string;
|
|
@@ -235,4 +286,4 @@ interface Scaffold {
|
|
|
235
286
|
declare function scaffoldLesson(rawId: string): Scaffold;
|
|
236
287
|
declare function scaffoldTerm(rawKey: string): Scaffold;
|
|
237
288
|
|
|
238
|
-
export { type CheckLessonResult, type CheckReport, type CliDiagnostic, type ContentCliCode, ContentCliError, type ContextReport, type DiagnosticThreshold, type DiagnosticsReport, type DomainFileSignatures, type RenderResult, type Scaffold, type StatusReport, type TokenCounts, type WarningsReport, changedLessonIds, contentCheck, contentContext, contentDiagnostics, contentStatus, contentWarnings, estimateTokens, findLesson, lessonDiagnostics, normalizeLessonId, renderLesson, scaffoldLesson, scaffoldTerm };
|
|
289
|
+
export { type CheckLessonResult, type CheckReport, type CliDiagnostic, type ContentCliCode, ContentCliError, type ContextReport, type DiagnosticThreshold, type DiagnosticsReport, type DomainFileSignatures, type RenderResult, type RulesReport, type Scaffold, type StatusReport, type TokenCounts, type WarningsReport, changedLessonIds, contentCheck, contentContext, contentDiagnostics, contentRules, contentStatus, contentWarnings, estimateTokens, findLesson, lessonDiagnostics, normalizeLessonId, parseRulesFilter, renderLesson, scaffoldLesson, scaffoldTerm };
|
package/dist/compiler/cli.js
CHANGED
|
@@ -18,6 +18,11 @@ import remarkNotation, {
|
|
|
18
18
|
} from "../reference/remark-notation.mjs";
|
|
19
19
|
import remarkShortNote from "../reference/remark-short-note.mjs";
|
|
20
20
|
import { isLessonSlug, lessonIdFromSlug } from "./lesson-derivation.js";
|
|
21
|
+
import { isCourseRuleId, ownerOf, ELEMENTS } from "../rules/catalog.js";
|
|
22
|
+
import {
|
|
23
|
+
renderRulesInEffect,
|
|
24
|
+
rulesInEffect
|
|
25
|
+
} from "../rules/in-effect.js";
|
|
21
26
|
const REPOSITORY_ROOT = process.cwd();
|
|
22
27
|
const CONTENT_ROOT = join(REPOSITORY_ROOT, "content");
|
|
23
28
|
const DOCS_ROOT = join(CONTENT_ROOT, "docs");
|
|
@@ -33,6 +38,15 @@ class ContentCliError extends Error {
|
|
|
33
38
|
function diagnosticSeverity(value) {
|
|
34
39
|
return value === "info" || value === "warning" ? value : "error";
|
|
35
40
|
}
|
|
41
|
+
function ownerFields(code) {
|
|
42
|
+
const bare = code.slice(code.indexOf(":") + 1);
|
|
43
|
+
const owner = ownerOf(bare);
|
|
44
|
+
if (owner !== void 0) return { rule: owner.id };
|
|
45
|
+
return isCourseRuleId(bare) ? { rule: bare } : {};
|
|
46
|
+
}
|
|
47
|
+
function withOwner(diagnostic) {
|
|
48
|
+
return { ...diagnostic, ...ownerFields(diagnostic.code) };
|
|
49
|
+
}
|
|
36
50
|
function mentions(message, lessonId, file) {
|
|
37
51
|
return message.includes(lessonId) || message.includes(file) || message.includes(file.replace(/^content\/docs\//, ""));
|
|
38
52
|
}
|
|
@@ -71,12 +85,13 @@ function lessonDiagnostics(manifest, lesson) {
|
|
|
71
85
|
);
|
|
72
86
|
if (!forLesson) continue;
|
|
73
87
|
out.push({
|
|
74
|
-
code: `math:${diagnostic.code
|
|
88
|
+
code: `math:${diagnostic.code}`,
|
|
75
89
|
severity: diagnostic.severity,
|
|
76
90
|
message: diagnostic.message,
|
|
77
91
|
file: diagnostic.file,
|
|
78
92
|
...diagnostic.line === void 0 ? {} : { line: diagnostic.line },
|
|
79
|
-
...diagnostic.column === void 0 ? {} : { column: diagnostic.column }
|
|
93
|
+
...diagnostic.column === void 0 ? {} : { column: diagnostic.column },
|
|
94
|
+
...diagnostic.equation === void 0 ? {} : { equation: diagnostic.equation }
|
|
80
95
|
});
|
|
81
96
|
}
|
|
82
97
|
for (const diagnostic of manifest.diagnostics.equations) {
|
|
@@ -95,6 +110,8 @@ function lessonDiagnostics(manifest, lesson) {
|
|
|
95
110
|
...manifest.diagnostics.figures,
|
|
96
111
|
...manifest.diagnostics.diagrams,
|
|
97
112
|
...manifest.diagnostics.checks,
|
|
113
|
+
...manifest.diagnostics.examples,
|
|
114
|
+
...manifest.diagnostics.folds,
|
|
98
115
|
...manifest.diagnostics.rawFloats
|
|
99
116
|
]) {
|
|
100
117
|
if (diagnostic.lessonId !== id && diagnostic.file !== file && !mentions(diagnostic.message, id, file)) {
|
|
@@ -116,7 +133,7 @@ function lessonDiagnostics(manifest, lesson) {
|
|
|
116
133
|
...withFile(diagnostic.file)
|
|
117
134
|
});
|
|
118
135
|
}
|
|
119
|
-
return out.sort(
|
|
136
|
+
return out.map(withOwner).sort(
|
|
120
137
|
(a, b) => a.code.localeCompare(b.code) || a.message.localeCompare(b.message)
|
|
121
138
|
);
|
|
122
139
|
}
|
|
@@ -305,6 +322,7 @@ function contentStatus(manifest) {
|
|
|
305
322
|
const draftNotation = manifest.notation.definitions.filter((definition) => definition.status === "draft").map((definition) => definition.id).sort();
|
|
306
323
|
const orphans = manifest.notation.diagnostics.filter((diagnostic) => diagnostic.code === "unused-definition").map((diagnostic) => ({
|
|
307
324
|
code: `notation:${diagnostic.code}`,
|
|
325
|
+
...ownerFields(diagnostic.code),
|
|
308
326
|
severity: diagnosticSeverity(diagnostic.severity),
|
|
309
327
|
message: diagnostic.message,
|
|
310
328
|
...diagnostic.source?.file === void 0 ? {} : { file: diagnostic.source.file }
|
|
@@ -319,9 +337,13 @@ function contentStatus(manifest) {
|
|
|
319
337
|
...manifest.diagnostics.figures.map((d) => d.severity),
|
|
320
338
|
...manifest.diagnostics.diagrams.map((d) => d.severity),
|
|
321
339
|
...manifest.diagnostics.checks.map((d) => d.severity),
|
|
340
|
+
...manifest.diagnostics.examples.map((d) => d.severity),
|
|
341
|
+
...manifest.diagnostics.folds.map((d) => d.severity),
|
|
322
342
|
...manifest.diagnostics.rawFloats.map((d) => d.severity),
|
|
323
343
|
...manifest.diagnostics.consistency.map((d) => d.severity),
|
|
324
|
-
...manifest.diagnostics.knowledge.map((d) => d.severity)
|
|
344
|
+
...manifest.diagnostics.knowledge.map((d) => d.severity),
|
|
345
|
+
...manifest.diagnostics.config.map((d) => d.severity),
|
|
346
|
+
...manifest.diagnostics.waivers.map((d) => d.severity)
|
|
325
347
|
];
|
|
326
348
|
const keyedEquations = manifest.lessons.reduce(
|
|
327
349
|
(count, lesson) => count + lesson.equations.length,
|
|
@@ -339,6 +361,10 @@ function contentStatus(manifest) {
|
|
|
339
361
|
(count, lesson) => count + lesson.diagrams.length,
|
|
340
362
|
0
|
|
341
363
|
);
|
|
364
|
+
const keyedExamples = manifest.lessons.reduce(
|
|
365
|
+
(count, lesson) => count + lesson.examples.length,
|
|
366
|
+
0
|
|
367
|
+
);
|
|
342
368
|
const assessments = new Set(
|
|
343
369
|
manifest.lessons.flatMap((lesson) => lesson.assessments)
|
|
344
370
|
).size;
|
|
@@ -354,7 +380,8 @@ function contentStatus(manifest) {
|
|
|
354
380
|
keyedEquations,
|
|
355
381
|
keyedTables,
|
|
356
382
|
keyedFigures,
|
|
357
|
-
keyedDiagrams
|
|
383
|
+
keyedDiagrams,
|
|
384
|
+
keyedExamples
|
|
358
385
|
},
|
|
359
386
|
draftDebt: { lessons: draftLessons, notation: draftNotation },
|
|
360
387
|
orphans,
|
|
@@ -365,6 +392,11 @@ function contentStatus(manifest) {
|
|
|
365
392
|
}
|
|
366
393
|
};
|
|
367
394
|
}
|
|
395
|
+
function equationLink(value) {
|
|
396
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
397
|
+
const { key, href } = value;
|
|
398
|
+
return typeof key === "string" && typeof href === "string" ? { key, href } : void 0;
|
|
399
|
+
}
|
|
368
400
|
function numericLocation(value) {
|
|
369
401
|
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
370
402
|
}
|
|
@@ -394,13 +426,16 @@ function contentDiagnostics(manifest, minimumSeverity = "info") {
|
|
|
394
426
|
const line = numericLocation(diagnostic.line) ?? numericLocation(source?.line);
|
|
395
427
|
const column = numericLocation(diagnostic.column) ?? numericLocation(source?.column);
|
|
396
428
|
const detail = typeof diagnostic.code === "string" ? diagnostic.code : typeof diagnostic.kind === "string" ? diagnostic.kind : "diagnostic";
|
|
429
|
+
const equation = equationLink(diagnostic.equation);
|
|
397
430
|
diagnostics.push({
|
|
398
431
|
code: `${group}:${detail}`,
|
|
432
|
+
...ownerFields(detail),
|
|
399
433
|
severity,
|
|
400
434
|
message: String(diagnostic.message ?? detail),
|
|
401
435
|
...file === void 0 ? {} : { file },
|
|
402
436
|
...line === void 0 ? {} : { line },
|
|
403
|
-
...column === void 0 ? {} : { column }
|
|
437
|
+
...column === void 0 ? {} : { column },
|
|
438
|
+
...equation === void 0 ? {} : { equation }
|
|
404
439
|
});
|
|
405
440
|
}
|
|
406
441
|
}
|
|
@@ -419,6 +454,41 @@ function contentWarnings(manifest) {
|
|
|
419
454
|
);
|
|
420
455
|
return { count: warnings.length, warnings };
|
|
421
456
|
}
|
|
457
|
+
function parseRulesFilter(input) {
|
|
458
|
+
const filter = {};
|
|
459
|
+
if (input.element !== void 0) {
|
|
460
|
+
const elements = input.element.split(",").map((e) => e.trim()).filter(Boolean);
|
|
461
|
+
for (const element of elements) {
|
|
462
|
+
if (!ELEMENTS.includes(element)) {
|
|
463
|
+
throw new ContentCliError(
|
|
464
|
+
"usage",
|
|
465
|
+
`--element ${element}: not an element. One of: ${ELEMENTS.join(", ")}.`
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
filter.elements = elements;
|
|
470
|
+
}
|
|
471
|
+
if (input.enforcement !== void 0) {
|
|
472
|
+
const kinds = ["script", "partial", "guide", "locked"];
|
|
473
|
+
if (!kinds.includes(input.enforcement)) {
|
|
474
|
+
throw new ContentCliError(
|
|
475
|
+
"usage",
|
|
476
|
+
`--enforcement ${input.enforcement}: one of ${kinds.join(", ")}.`
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
filter.enforcement = input.enforcement;
|
|
480
|
+
}
|
|
481
|
+
return filter;
|
|
482
|
+
}
|
|
483
|
+
function contentRules(manifest, filter = {}) {
|
|
484
|
+
const rules = rulesInEffect(manifest, filter);
|
|
485
|
+
return {
|
|
486
|
+
filter,
|
|
487
|
+
count: rules.length,
|
|
488
|
+
rules,
|
|
489
|
+
markdown: renderRulesInEffect(manifest, filter)
|
|
490
|
+
};
|
|
491
|
+
}
|
|
422
492
|
function contentContext(manifest, rawId) {
|
|
423
493
|
const lesson = findLesson(manifest, rawId);
|
|
424
494
|
const { frontmatter, body } = lessonRaw(lesson);
|
|
@@ -466,7 +536,9 @@ function contentContext(manifest, rawId) {
|
|
|
466
536
|
tables: lesson.tables.map((table) => ({ ...table })),
|
|
467
537
|
figures: lesson.figures.map((figure) => ({ ...figure })),
|
|
468
538
|
diagrams: lesson.diagrams.map((diagram) => ({ ...diagram })),
|
|
539
|
+
examples: lesson.examples.map((example) => ({ ...example })),
|
|
469
540
|
knowledgeChecks: lesson.knowledgeChecks.map((check) => ({ ...check })),
|
|
541
|
+
sections: lesson.headings.map((heading) => ({ ...heading })),
|
|
470
542
|
sources: lesson.sources.map((id) => {
|
|
471
543
|
const record = sourceById.get(id);
|
|
472
544
|
return {
|
|
@@ -659,12 +731,14 @@ export {
|
|
|
659
731
|
contentCheck,
|
|
660
732
|
contentContext,
|
|
661
733
|
contentDiagnostics,
|
|
734
|
+
contentRules,
|
|
662
735
|
contentStatus,
|
|
663
736
|
contentWarnings,
|
|
664
737
|
estimateTokens,
|
|
665
738
|
findLesson,
|
|
666
739
|
lessonDiagnostics,
|
|
667
740
|
normalizeLessonId,
|
|
741
|
+
parseRulesFilter,
|
|
668
742
|
renderLesson,
|
|
669
743
|
scaffoldLesson,
|
|
670
744
|
scaffoldTerm
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CurriculumCatalog } from '../curriculum/validation.js';
|
|
2
|
-
import { NotationReferenceInput, AbbreviationDefinitionInput, NotationRegistryInput } from '../reference/types.js';
|
|
2
|
+
import { NotationReferenceInput, AbbreviationDefinitionInput, NotationRegistryInput, LocalNotationDefinitionInput, NotationSuspension } from '../reference/types.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The single path from content files to typed data.
|
|
@@ -19,8 +19,41 @@ import { NotationReferenceInput, AbbreviationDefinitionInput, NotationRegistryIn
|
|
|
19
19
|
* ignoring code fences and inline code, sorted by position.
|
|
20
20
|
*/
|
|
21
21
|
declare function extractNotationReferences(text: string, file: string, kind: NotationReferenceInput['kind'] | undefined): NotationReferenceInput[];
|
|
22
|
+
/**
|
|
23
|
+
* One part's **overview page** — `content/docs/<dir>/index.mdx`, the page the
|
|
24
|
+
* part's own directory URL resolves to. `isLessonSlug` already excludes any
|
|
25
|
+
* `<dir>/index`, so this page is not a chapter: it carries no competencies, no
|
|
26
|
+
* knowledge check, and no float numbers, and nothing in the curriculum graph
|
|
27
|
+
* changes when a course adds one.
|
|
28
|
+
*/
|
|
29
|
+
interface PartOverviewPage {
|
|
30
|
+
readonly dir: string;
|
|
31
|
+
readonly slug: string;
|
|
32
|
+
readonly file: string;
|
|
33
|
+
readonly title: string;
|
|
34
|
+
readonly description?: string;
|
|
35
|
+
/** MDX after the frontmatter — what the S2 gate (`gate-part-pages.ts`) reads. */
|
|
36
|
+
readonly body: string;
|
|
37
|
+
/** 1-based file line `body` starts on, for diagnostic locations. */
|
|
38
|
+
readonly bodyLine: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The overview page of every part that has one, keyed by the part's directory.
|
|
42
|
+
* A part without one is simply absent — overview pages are opt-in, one part at
|
|
43
|
+
* a time.
|
|
44
|
+
*/
|
|
45
|
+
declare function loadPartOverviews(parts: readonly {
|
|
46
|
+
readonly dir: string;
|
|
47
|
+
}[]): PartOverviewPage[];
|
|
22
48
|
declare function loadCurriculumCatalog(): Promise<CurriculumCatalog>;
|
|
23
49
|
declare function loadAbbreviationRegistryInput(): Promise<readonly AbbreviationDefinitionInput[]>;
|
|
50
|
+
/**
|
|
51
|
+
* A lesson's `notation.suspend` (rule N22): decorations that are not
|
|
52
|
+
* operators on this page. A name outside `DECORATION_NAMES`, a decoration
|
|
53
|
+
* suspended twice, and a `notation.local` operator entry for a suspended
|
|
54
|
+
* decoration (the page would contradict itself) each fail the load.
|
|
55
|
+
*/
|
|
56
|
+
declare function notationSuspensions(raw: unknown, localDefinitions: readonly LocalNotationDefinitionInput[], file: string): NotationSuspension[];
|
|
24
57
|
declare function loadNotationRegistryInput(): Promise<NotationRegistryInput>;
|
|
25
58
|
/**
|
|
26
59
|
* Notation frontmatter objects for `remark-notation`'s `definitions` option.
|
|
@@ -68,4 +101,4 @@ declare function notationSourceLocatorGaps(): {
|
|
|
68
101
|
total: number;
|
|
69
102
|
};
|
|
70
103
|
|
|
71
|
-
export { type NotationSpec, extractNotationReferences, loadAbbreviationRegistryInput, loadCurriculumCatalog, loadLessonChecks, loadNotationDefinitions, loadNotationRegistryInput, loadNotationSpecs, loadSourceRecords, notationSourceLocatorGaps };
|
|
104
|
+
export { type NotationSpec, type PartOverviewPage, extractNotationReferences, loadAbbreviationRegistryInput, loadCurriculumCatalog, loadLessonChecks, loadNotationDefinitions, loadNotationRegistryInput, loadNotationSpecs, loadPartOverviews, loadSourceRecords, notationSourceLocatorGaps, notationSuspensions };
|