explico 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +879 -1
- package/README.md +200 -30
- package/dist/compiler/astro.d.ts +15 -2
- package/dist/compiler/astro.js +40 -11
- package/dist/compiler/cli-main.d.ts +43 -0
- package/dist/compiler/cli-main.js +392 -0
- package/dist/compiler/cli.d.ts +424 -9
- package/dist/compiler/cli.js +1289 -8
- package/dist/compiler/collections.d.ts +88 -1
- package/dist/compiler/collections.js +170 -25
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +105 -1
- package/dist/compiler/decks.d.ts +161 -0
- package/dist/compiler/decks.js +235 -0
- package/dist/compiler/exports/combined-html.d.ts +5 -1
- package/dist/compiler/exports/deck-fit.d.ts +55 -0
- package/dist/compiler/exports/deck-fit.js +90 -0
- package/dist/compiler/exports/export-chapters.d.ts +37 -6
- package/dist/compiler/exports/export-chapters.js +64 -5
- package/dist/compiler/exports/export-pages.d.ts +30 -3
- package/dist/compiler/exports/export-pages.js +24 -1
- package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/dist/compiler/exports/latex/cache.d.ts +78 -0
- package/dist/compiler/exports/latex/cache.js +129 -0
- package/dist/compiler/exports/latex/capture.d.ts +10 -1
- package/dist/compiler/exports/latex/capture.js +3 -1
- package/dist/compiler/exports/latex/compile.d.ts +13 -2
- package/dist/compiler/exports/latex/compile.js +12 -1
- package/dist/compiler/exports/latex/deck.d.ts +92 -0
- package/dist/compiler/exports/latex/deck.js +349 -0
- package/dist/compiler/exports/latex/document.d.ts +51 -6
- package/dist/compiler/exports/latex/document.js +71 -12
- package/dist/compiler/exports/latex/explico-common.sty +198 -0
- package/dist/compiler/exports/latex/explico.cls +13 -183
- package/dist/compiler/exports/latex/serialize.d.ts +60 -4
- package/dist/compiler/exports/latex/serialize.js +4 -2
- package/dist/compiler/exports/latex-export.d.ts +24 -2
- package/dist/compiler/exports/latex-export.js +107 -26
- package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
- package/dist/compiler/exports/markdown/serialize.js +85 -8
- package/dist/compiler/exports/markdown-export.d.ts +6 -1
- package/dist/compiler/exports/markdown-export.js +50 -11
- package/dist/compiler/exports/standalone-html.d.ts +10 -5
- package/dist/compiler/exports/standalone-html.js +3 -3
- package/dist/compiler/exports/static-tree.d.ts +38 -1
- package/dist/compiler/exports/static-tree.js +120 -4
- package/dist/compiler/knowledge-version.d.ts +26 -2
- package/dist/compiler/knowledge-version.js +27 -17
- package/dist/compiler/lesson-derivation.d.ts +143 -6
- package/dist/compiler/lesson-derivation.js +145 -13
- package/dist/compiler/manifest.d.ts +133 -4
- package/dist/compiler/manifest.js +359 -58
- package/dist/compiler/page-versions.d.ts +160 -0
- package/dist/compiler/page-versions.js +198 -0
- package/dist/compiler/render-diagnostics.d.ts +5 -1
- package/dist/compiler/resolution-report.d.ts +24 -6
- package/dist/compiler/resolution-report.js +22 -4
- package/dist/compiler/validate-content.d.ts +7 -0
- package/dist/compiler/validate-content.js +160 -0
- package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
- package/dist/curriculum/competency-graph.d.ts +54 -0
- package/dist/curriculum/competency-graph.js +41 -0
- package/dist/reference/base-library.d.mts +8 -0
- package/dist/reference/base-library.mjs +19 -0
- package/dist/reference/consistency.d.ts +8 -4
- package/dist/reference/consistency.js +76 -26
- package/dist/reference/deck-remark.d.mts +123 -0
- package/dist/reference/deck-remark.mjs +600 -0
- package/dist/reference/deck-source.d.ts +107 -0
- package/dist/reference/deck-source.js +140 -0
- package/dist/reference/decks-validate.d.ts +133 -0
- package/dist/reference/decks-validate.js +359 -0
- package/dist/reference/derived-entries.d.ts +5 -1
- package/dist/reference/diagram-fit.d.ts +1 -1
- package/dist/reference/figure-symbols.d.ts +23 -6
- package/dist/reference/figure-symbols.js +51 -15
- package/dist/reference/float-name.d.ts +43 -0
- package/dist/reference/float-name.js +58 -0
- package/dist/reference/folds-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-indices.js +5 -4
- package/dist/reference/gate-math.d.ts +29 -2
- package/dist/reference/gate-math.js +99 -18
- package/dist/reference/gate-part-pages.d.ts +1 -1
- package/dist/reference/lab-katex-validate.d.ts +10 -6
- package/dist/reference/lab-katex-validate.js +17 -9
- package/dist/reference/lab-math-scope.js +29 -4
- package/dist/reference/math-glyphs.d.mts +25 -0
- package/dist/reference/math-glyphs.mjs +189 -25
- package/dist/reference/page-notation.d.ts +22 -3
- package/dist/reference/page-notation.js +32 -10
- package/dist/reference/references.d.ts +14 -1
- package/dist/reference/references.js +13 -0
- package/dist/reference/registry-scope.d.mts +33 -0
- package/dist/reference/registry-scope.mjs +34 -0
- package/dist/reference/registry.js +29 -10
- package/dist/reference/rehype-deck.d.mts +6 -0
- package/dist/reference/rehype-deck.mjs +286 -0
- package/dist/reference/rehype-heading-anchors.d.mts +20 -0
- package/dist/reference/rehype-heading-anchors.mjs +48 -6
- package/dist/reference/remark-notation.mjs +249 -40
- package/dist/reference/source-lines.d.mts +12 -0
- package/dist/reference/source-lines.mjs +65 -0
- package/dist/reference/types.d.ts +28 -0
- package/dist/rules/catalog.d.ts +21 -4
- package/dist/rules/catalog.js +221 -2
- package/dist/rules/course-rules.d.ts +5 -1
- package/dist/rules/course-rules.js +17 -3
- package/dist/rules/course-rules.schema.json +12 -0
- package/dist/rules/in-effect.d.ts +13 -3
- package/dist/rules/in-effect.js +30 -7
- package/dist/rules/rules.schema.json +259 -1
- package/dist/rules/settings.d.ts +1 -1
- package/dist/session/PreferenceStore.d.ts +6 -0
- package/dist/session/PreferenceStore.js +18 -1
- package/dist/session/deck-memory.d.ts +116 -0
- package/dist/session/deck-memory.js +143 -0
- package/dist/session/reading-memory.d.ts +96 -0
- package/dist/session/reading-memory.js +133 -0
- package/docs/VERSIONING.md +21 -18
- package/docs/authoring/README.md +29 -9
- package/docs/authoring/agents.md +63 -16
- package/docs/authoring/components.md +15 -0
- package/docs/authoring/deck.md +588 -0
- package/docs/authoring/migrating.md +1045 -16
- package/docs/authoring/notation.md +49 -6
- package/docs/authoring/structure.md +80 -1
- package/docs/authoring/versioning.md +54 -2
- package/docs/authoring/visualizations.md +7 -2
- package/docs/course-agent/AGENTS.md +37 -0
- package/docs/course-agent/agents/rules-reviewer.md +33 -0
- package/docs/course-agent/skills/draft-lesson/SKILL.md +37 -0
- package/docs/course-agent/skills/migrate-course/SKILL.md +31 -0
- package/docs/course-agent/skills/release-content/SKILL.md +24 -0
- package/docs/diagram-scaling.md +148 -0
- package/docs/privacy.md +59 -0
- package/package.json +8 -2
- package/src/compiler/astro.ts +60 -21
- package/src/compiler/cli-main.ts +469 -0
- package/src/compiler/cli.ts +1905 -16
- package/src/compiler/collections.ts +284 -25
- package/src/compiler/course-config.ts +267 -0
- package/src/compiler/decks.ts +455 -0
- package/src/compiler/exports/deck-fit.ts +127 -0
- package/src/compiler/exports/export-chapters.ts +105 -6
- package/src/compiler/exports/export-pages.ts +46 -2
- package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/src/compiler/exports/latex/cache.ts +183 -0
- package/src/compiler/exports/latex/capture.ts +5 -2
- package/src/compiler/exports/latex/compile.ts +18 -0
- package/src/compiler/exports/latex/deck.ts +419 -0
- package/src/compiler/exports/latex/document.ts +117 -21
- package/src/compiler/exports/latex/explico-common.sty +198 -0
- package/src/compiler/exports/latex/explico.cls +13 -183
- package/src/compiler/exports/latex/serialize.ts +25 -21
- package/src/compiler/exports/latex/texlive-packages.txt +6 -2
- package/src/compiler/exports/latex-export.ts +187 -45
- package/src/compiler/exports/markdown/serialize.ts +118 -9
- package/src/compiler/exports/markdown-export.ts +56 -11
- package/src/compiler/exports/standalone-html.ts +8 -7
- package/src/compiler/exports/static-tree.ts +175 -3
- package/src/compiler/knowledge-version.ts +56 -21
- package/src/compiler/lesson-derivation.ts +282 -16
- package/src/compiler/manifest.ts +558 -63
- package/src/compiler/page-versions.ts +359 -0
- package/src/compiler/resolution-report.ts +38 -7
- package/src/compiler/validate-content.ts +212 -0
- package/src/components/CourseContents.astro +124 -6
- package/src/components/CurriculumMap.astro +49 -1
- package/src/components/FloatIndex.astro +1 -1
- package/src/components/PartOverview.astro +51 -5
- package/src/components/assessments/AssessmentSet.astro +41 -15
- package/src/components/block-heading.ts +15 -6
- package/src/components/card-carry.ts +7 -4
- package/src/components/card-manager.ts +14 -2
- package/src/components/card-memory.ts +13 -9
- package/src/components/deck/DeckEnhancer.astro +13 -0
- package/src/components/deck/deck-context.ts +104 -0
- package/src/components/deck/deck-controller.ts +1287 -0
- package/src/components/deck/deck-map.ts +91 -0
- package/src/components/deck-lane.ts +163 -0
- package/src/components/diagram/Diagram.astro +131 -86
- package/src/components/disclosure/persisted-disclosure.ts +9 -1
- package/src/components/figure/Figure.astro +7 -2
- package/src/components/figure/FloatCaption.astro +5 -3
- package/src/components/fold/Fold.astro +2 -2
- package/src/components/notation/NotationFormula.astro +81 -11
- package/src/components/notation/NotationGlossary.astro +132 -1
- package/src/components/notation/NotationLayer.astro +15 -7
- package/src/components/starlight/AboutPanel.astro +186 -3
- package/src/components/starlight/CollapsibleSections.astro +24 -3
- package/src/components/starlight/DiagramEnhancer.astro +5 -1
- package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
- package/src/components/starlight/LayoutHeader.astro +17 -0
- package/src/components/starlight/LayoutPageTitle.astro +110 -0
- package/src/components/starlight/LayoutSidebar.astro +807 -21
- package/src/components/starlight/LayoutSocialIcons.astro +62 -6
- package/src/components/starlight/LessonExportActions.astro +27 -9
- package/src/components/starlight/LessonFooter.astro +59 -0
- package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
- package/src/components/starlight/TextSizeControl.astro +24 -13
- package/src/components/starlight/page-marks.ts +139 -0
- package/src/components/starlight/section-summary.ts +1 -1
- package/src/components/starlight/sidebar-editions.ts +245 -0
- package/src/components/table/Table.astro +15 -7
- package/src/content-config.ts +87 -1
- package/src/curriculum/competency-graph.ts +95 -0
- package/src/reference/base-library.mjs +19 -0
- package/src/reference/consistency.ts +126 -39
- package/src/reference/deck-remark.mjs +600 -0
- package/src/reference/deck-source.ts +273 -0
- package/src/reference/decks-validate.ts +604 -0
- package/src/reference/diagram-layout.ts +3 -1
- package/src/reference/figure-symbols.ts +108 -31
- package/src/reference/folds-validate.ts +1 -1
- package/src/reference/gate-indices.ts +5 -3
- package/src/reference/gate-math.ts +169 -25
- package/src/reference/lab-katex-validate.ts +30 -15
- package/src/reference/lab-math-scope.ts +34 -3
- package/src/reference/math-glyphs.mjs +189 -25
- package/src/reference/page-notation.ts +69 -15
- package/src/reference/references.ts +28 -0
- package/src/reference/registry-scope.mjs +34 -0
- package/src/reference/registry.ts +41 -11
- package/src/reference/rehype-deck.mjs +286 -0
- package/src/reference/rehype-heading-anchors.mjs +48 -6
- package/src/reference/remark-notation.mjs +249 -40
- package/src/reference/source-lines.mjs +65 -0
- package/src/reference/types.ts +28 -0
- package/src/rules/catalog.ts +257 -4
- package/src/rules/course-rules.ts +21 -1
- package/src/rules/in-effect.ts +54 -8
- package/src/session/PreferenceStore.ts +23 -0
- package/src/session/deck-memory.ts +263 -0
- package/src/session/reading-memory.ts +243 -0
- package/src/styles/deck.css +1150 -0
- package/src/styles/global.css +958 -27
- package/src/styles/zones.css +277 -0
- /package/src/{components → reference}/float-name.ts +0 -0
package/README.md
CHANGED
|
@@ -51,15 +51,39 @@ What it gives a course:
|
|
|
51
51
|
tick-labeled axes, log scales, or several series sharing a scale; Observable
|
|
52
52
|
Plot pays off once a course is authoring many charts per lecture. Pick per
|
|
53
53
|
figure — nothing in the engine assumes one plotting approach.
|
|
54
|
+
- **Slides from the same lesson** — a lesson's deck, `<lesson>.deck.mdx`, is
|
|
55
|
+
its slide edition: frames on a fixed canvas, steps that reveal, drive a lab
|
|
56
|
+
or light up a symbol, speaker notes, and the chapter's equations and figures
|
|
57
|
+
recalled with their numbers. The deck's page presents full screen, with a
|
|
58
|
+
presenter window, and as one offline HTML file; it exports to beamer slides
|
|
59
|
+
and a handout with every symbol linked to its entry, and to Marp's Markdown.
|
|
60
|
+
Rules D1–D10 keep frames short, and the build fails a frame that does not
|
|
61
|
+
fit. A lesson can also be slides only (`docs/authoring/deck.md`).
|
|
54
62
|
- **LMS / analytics / identity seams** — framework-free interfaces shipped as
|
|
55
63
|
no-ops; a host supplies real implementations without touching a component.
|
|
56
64
|
- **A `content` CLI** — `status`, `diagnostics`, `warnings`, `context <lesson>`,
|
|
57
65
|
`check <lesson>` (compiles MDX → remark → KaTeX and fails loudly),
|
|
58
|
-
`new lesson|term`.
|
|
66
|
+
`new lesson|term|deck`.
|
|
59
67
|
|
|
60
68
|
The prerequisite graph itself is authored by the lecturer or an AI; the engine
|
|
61
69
|
validates it (DAG, ordering, coverage, orphans) rather than inferring it.
|
|
62
70
|
|
|
71
|
+
## Start from a working course
|
|
72
|
+
|
|
73
|
+
Two complete courses built on explico are downloadable, always at their newest
|
|
74
|
+
version, from **[https://danieltuzes.github.io/explico/start/](https://danieltuzes.github.io/explico/start/)**:
|
|
75
|
+
|
|
76
|
+
- [the playground](https://danieltuzes.github.io/explico/downloads/explico-playground-latest.zip) — every engine feature on a short physics course, at
|
|
77
|
+
the engine's latest release, standalone (`pnpm install && pnpm
|
|
78
|
+
validate:content && pnpm dev`). A path in these docs that starts with
|
|
79
|
+
`playground/` is a file in it.
|
|
80
|
+
- [credit-and-derivatives](https://danieltuzes.github.io/explico/downloads/credit-and-derivatives-latest.zip) — a full course as it is taught
|
|
81
|
+
([repository](https://github.com/danieltuzes/credit-and-derivatives)).
|
|
82
|
+
|
|
83
|
+
[`sources.json`](https://danieltuzes.github.io/explico/downloads/sources.json) lists both with the engine version, source commit and
|
|
84
|
+
checksum. An agent reads [`docs/authoring/agents.md`](https://cdn.jsdelivr.net/npm/explico@latest/docs/authoring/agents.md) first
|
|
85
|
+
(`node_modules/explico/docs/authoring/agents.md` once installed).
|
|
86
|
+
|
|
63
87
|
## Install
|
|
64
88
|
|
|
65
89
|
```bash
|
|
@@ -72,7 +96,7 @@ Peer dependencies (the course provides them, pinned): `astro`,
|
|
|
72
96
|
|
|
73
97
|
## Use
|
|
74
98
|
|
|
75
|
-
A course provides `content/` (the
|
|
99
|
+
A course provides `content/` (the engine collections + `content/domain/` +
|
|
76
100
|
`content/course.config.ts`) and wires the engine in three places:
|
|
77
101
|
|
|
78
102
|
```ts
|
|
@@ -97,36 +121,49 @@ import rehypeHeadingAnchors from 'explico/reference/rehype-heading-anchors.mjs';
|
|
|
97
121
|
import { courseConfig } from './content/course.config.ts';
|
|
98
122
|
|
|
99
123
|
const manifest = await loadManifest(courseConfig);
|
|
100
|
-
// markdown
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
// //
|
|
106
|
-
// //
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
124
|
+
// import { unified } from '@astrojs/markdown-remark';
|
|
125
|
+
// import remarkMath from 'remark-math';
|
|
126
|
+
// import rehypeKatex from 'rehype-katex';
|
|
127
|
+
//
|
|
128
|
+
// markdown: {
|
|
129
|
+
// // Astro 7.3 deprecates the flat `markdown.remarkPlugins` / `rehypePlugins`
|
|
130
|
+
// // lists; the plugins go to `unified()` instead.
|
|
131
|
+
// processor: unified({
|
|
132
|
+
// remarkPlugins: [
|
|
133
|
+
// remarkMath,
|
|
134
|
+
// remarkShortNote,
|
|
135
|
+
// remarkMemo,
|
|
136
|
+
// // `createRemarkNotationOptions` reads every numbering input off one
|
|
137
|
+
// // manifest — the equation / table / figure number tables *and* the part
|
|
138
|
+
// // each lesson sits in — so they can't drift apart. `strict: true` fails
|
|
139
|
+
// // the build when a catalogued lesson has equations/tables/figures but no
|
|
140
|
+
// // numbering or cross-page entry, instead of silently numbering it by
|
|
141
|
+
// // `##` section while references to it read the part scheme.
|
|
142
|
+
// // `base` is the Astro `base` of a subpath deployment (e.g. a GitHub
|
|
143
|
+
// // Pages project site); the links these plugins write 404 without it.
|
|
144
|
+
// [remarkNotation, createRemarkNotationOptions(manifest, { strict: true, base })],
|
|
145
|
+
// // The generated References section's first state, from the course's
|
|
146
|
+
// // `disclosure` settings block (`CourseConfig.disclosure`).
|
|
147
|
+
// [remarkCitation, {
|
|
148
|
+
// sources: manifest.sources,
|
|
149
|
+
// defaultOpen: courseConfig.disclosure?.references?.defaultOpen === true,
|
|
150
|
+
// }],
|
|
151
|
+
// // Last: renders `:::note` / `:::tip` / `:::caution` / `:::danger` as
|
|
152
|
+
// // Starlight's `<Aside>` (Starlight's own pass skips `content/docs/`).
|
|
153
|
+
// remarkAsides,
|
|
154
|
+
// ],
|
|
155
|
+
// rehypePlugins: [
|
|
156
|
+
// [rehypeKatex, createNotationKatexOptions()],
|
|
157
|
+
// rehypeFailKatexErrors,
|
|
158
|
+
// // Heading collapse defaults: the same `disclosure` block.
|
|
159
|
+
// [rehypeHeadingAnchors, { defaultCollapsible: courseConfig.disclosure }],
|
|
160
|
+
// ],
|
|
161
|
+
// }),
|
|
162
|
+
// },
|
|
123
163
|
// … integrations: [explicoContentRefresh(courseConfig, manifest),
|
|
124
164
|
// starlight({ customCss: ['explico/styles/global.css'],
|
|
125
165
|
// components: { Footer: 'explico/components/starlight/LessonFooter.astro',
|
|
126
166
|
// SocialIcons: 'explico/components/starlight/LayoutSocialIcons.astro', … } })
|
|
127
|
-
// … rehypePlugins: [[rehypeKatex, createNotationKatexOptions()], rehypeFailKatexErrors,
|
|
128
|
-
// // Heading collapse defaults: the same `disclosure` block.
|
|
129
|
-
// [rehypeHeadingAnchors, { defaultCollapsible: courseConfig.disclosure }]]
|
|
130
167
|
```
|
|
131
168
|
|
|
132
169
|
`loadManifest(courseConfig)` always recompiles current content before Astro
|
|
@@ -185,6 +222,68 @@ or, without one, the numbers and short captions of the floats inside
|
|
|
185
222
|
</Fold>
|
|
186
223
|
```
|
|
187
224
|
|
|
225
|
+
### A page for paths that miss
|
|
226
|
+
|
|
227
|
+
Starlight looks for a docs entry named `404` on every build. Give the course a
|
|
228
|
+
`content/docs/404.mdx`; without it, Astro falls back correctly but prints
|
|
229
|
+
`Entry docs → 404 was not found`. This small page uses Astro's `BASE_URL`, so
|
|
230
|
+
its links work both at `/` and below a deployment `base`:
|
|
231
|
+
|
|
232
|
+
```mdx
|
|
233
|
+
---
|
|
234
|
+
title: "404: Page outside the syllabus"
|
|
235
|
+
description: The requested course page could not be found.
|
|
236
|
+
template: splash
|
|
237
|
+
hero:
|
|
238
|
+
title: "404"
|
|
239
|
+
tagline: We followed this URL as far as the course map goes. It does not land on a page.
|
|
240
|
+
pagefind: false
|
|
241
|
+
sidebar:
|
|
242
|
+
hidden: true
|
|
243
|
+
editUrl: false
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
import { LinkButton } from '@astrojs/starlight/components';
|
|
247
|
+
|
|
248
|
+
<div style="display: flex; justify-content: center;">
|
|
249
|
+
<LinkButton href={import.meta.env.BASE_URL} icon="left-arrow" iconPlacement="start">
|
|
250
|
+
Return to the course
|
|
251
|
+
</LinkButton>
|
|
252
|
+
</div>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Let the docs entry own `/404.html` by turning off Starlight's otherwise
|
|
256
|
+
duplicate special route:
|
|
257
|
+
|
|
258
|
+
```js
|
|
259
|
+
starlight({
|
|
260
|
+
disable404Route: process.env.NODE_ENV === 'production',
|
|
261
|
+
// …the course's other Starlight settings
|
|
262
|
+
})
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
That keeps Starlight's catch-all page during development. In a static build,
|
|
266
|
+
the authored docs entry supplies `/404.html`; disabling the duplicate injected
|
|
267
|
+
route there prevents a route-collision warning.
|
|
268
|
+
|
|
269
|
+
Set Astro's `site` to the published origin too; Starlight's sitemap integration
|
|
270
|
+
skips itself, with a warning, when it is missing. Keep a subpath in `base`, not
|
|
271
|
+
in `site`:
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
export default defineConfig({
|
|
275
|
+
site: 'https://example.edu',
|
|
276
|
+
base: '/course/',
|
|
277
|
+
// …
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`explico/content-config` also exports Starlight's `i18n` collection. It supplies
|
|
282
|
+
an empty English fallback for a course with no translations, avoiding Astro's
|
|
283
|
+
empty-collection warning. To translate the interface, add
|
|
284
|
+
`content/i18n/<locale>.json` (or `.yml` / `.yaml`); those authored entries are
|
|
285
|
+
loaded instead.
|
|
286
|
+
|
|
188
287
|
### Lesson exports
|
|
189
288
|
|
|
190
289
|
A course can opt into self-contained HTML downloads of each chapter, each part
|
|
@@ -233,6 +332,12 @@ the same file becomes a link into it, whose hover preview works offline. Each
|
|
|
233
332
|
page is a section with `content-visibility: auto`, which keeps a long course
|
|
234
333
|
responsive, and starts a new sheet when printed.
|
|
235
334
|
|
|
335
|
+
A lesson's deck (`docs/authoring/deck.md`) has a row of its own in the menu, on
|
|
336
|
+
its page and on its chapter's: `downloads/<lesson>-slides.html` presents
|
|
337
|
+
offline, with its notes, labs and checks; with `latex`, `-slides.pdf` and the
|
|
338
|
+
handout `-slides-notes.pdf` compile in beamer from `-slides-latex.zip`; with
|
|
339
|
+
`markdown`, `-slides-markdown.zip` holds the deck in Marp's dialect.
|
|
340
|
+
|
|
236
341
|
The **Print / save as PDF** action calls the browser's native print dialog; it
|
|
237
342
|
does not download or build a `.pdf` file itself. The print layout removes site
|
|
238
343
|
chrome and prints every disclosure as the reader left it on screen: an open
|
|
@@ -311,9 +416,74 @@ and still writes the source zips. On GitHub Actions:
|
|
|
311
416
|
- run: pnpm build
|
|
312
417
|
```
|
|
313
418
|
|
|
419
|
+
A course sets no cookies and loads nothing from another site. It asks each
|
|
420
|
+
reader once, at the bottom of the window, whether it may remember their
|
|
421
|
+
reading in their browser (the chapters they opened, the cards they keep or
|
|
422
|
+
carry), and keeps nothing of it beyond the tab without a yes; settings the
|
|
423
|
+
reader chooses are kept either way, and the `[?]` panel changes the answer or
|
|
424
|
+
forgets everything. `readingMemory: "off"` in the course settings never asks.
|
|
425
|
+
[`docs/privacy.md`](docs/privacy.md) lists every key and when it is written,
|
|
426
|
+
for a course's own privacy notice.
|
|
427
|
+
|
|
428
|
+
Each part of the page has a colour of its own, so a reader can tell the
|
|
429
|
+
course's tools, its contents, the page's outline and the text apart:
|
|
430
|
+
`appearance.zones` in the course settings is `tonal` (the default, neutral
|
|
431
|
+
grey steps), `accent-header` (the header in the course's accent), `paper`
|
|
432
|
+
(the text as a sheet on a desk) or `none`, and `appearance.slides` gives each
|
|
433
|
+
slide on a deck's page an edge and a shadow (`lifted`, `edge` or `sorter`).
|
|
434
|
+
|
|
435
|
+
Every document says which version its content is at (rule C5): "From
|
|
436
|
+
*Course*, unchanged since version 0.2.0." under a chapter's title, the release
|
|
437
|
+
its content last changed in, or "with changes after version 0.4.0, not yet
|
|
438
|
+
released" while `content/CHANGELOG.md`'s `## [Unreleased]` holds a change to
|
|
439
|
+
it; a part's and the course's PDF carry the newest among their pages, and the
|
|
440
|
+
`[?]` panel shows the same line on the page. The record behind it is
|
|
441
|
+
`content/versions.json`, which `pnpm validate:content` writes and the course
|
|
442
|
+
commits with each content change, like a lockfile; in CI a stale record fails
|
|
443
|
+
the build.
|
|
444
|
+
|
|
445
|
+
Every build compiles every document, one LuaLaTeX run per chapter, part and
|
|
446
|
+
the course. `EXPLICO_LATEX_CACHE=<directory>` keeps each compile there, keyed
|
|
447
|
+
by every file of the document's folder, the TeX installation and the
|
|
448
|
+
document's date, and reuses it while they are unchanged: a push that changes
|
|
449
|
+
one chapter compiles that chapter, its part and the course, and so does a
|
|
450
|
+
release, since an unchanged document keeps its version line and its date. The build's summary line says
|
|
451
|
+
how many it compiled and reused. `EXPLICO_LATEX_CACHE_SALT` is the escape
|
|
452
|
+
hatch for what the key cannot see, such as a package updated with `tlmgr`.
|
|
453
|
+
Entries no build has used for a week are deleted. In CI, restore the
|
|
454
|
+
directory before the build and save it under a new key after:
|
|
455
|
+
|
|
456
|
+
```yaml
|
|
457
|
+
- uses: actions/cache@v4
|
|
458
|
+
with:
|
|
459
|
+
path: .cache/explico-latex
|
|
460
|
+
key: explico-latex-${{ github.sha }}
|
|
461
|
+
restore-keys: explico-latex-
|
|
462
|
+
- run: pnpm build
|
|
463
|
+
env:
|
|
464
|
+
EXPLICO_LATEX_CACHE: .cache/explico-latex
|
|
465
|
+
```
|
|
466
|
+
|
|
314
467
|
```jsonc
|
|
315
|
-
// package.json
|
|
316
|
-
"scripts": {
|
|
468
|
+
// package.json
|
|
469
|
+
"scripts": {
|
|
470
|
+
"validate:content": "tsx scripts/compile-manifest.ts",
|
|
471
|
+
"content": "tsx scripts/content.ts"
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Both scripts are three lines; the engine owns what they run:
|
|
476
|
+
|
|
477
|
+
```ts
|
|
478
|
+
// scripts/compile-manifest.ts
|
|
479
|
+
import { runValidateContent } from 'explico/compiler/validate-content';
|
|
480
|
+
import { courseConfig } from '../content/course.config';
|
|
481
|
+
process.exitCode = await runValidateContent(courseConfig);
|
|
482
|
+
|
|
483
|
+
// scripts/content.ts — `pnpm content help` lists every verb
|
|
484
|
+
import { runContentCli } from 'explico/compiler/cli-main';
|
|
485
|
+
import { courseConfig } from '../content/course.config';
|
|
486
|
+
process.exitCode = await runContentCli(courseConfig);
|
|
317
487
|
```
|
|
318
488
|
|
|
319
489
|
Diagnostics have three distinct severities: an `error` blocks validation because
|
package/dist/compiler/astro.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AstroConfig, AstroIntegration } from 'astro';
|
|
2
|
-
import { C as CourseConfig } from '../course-config-
|
|
2
|
+
import { C as CourseConfig } from '../course-config-BLtIdkei.js';
|
|
3
3
|
import { Manifest } from './manifest.js';
|
|
4
|
+
import { RenderDiagnostic } from './render-diagnostics.js';
|
|
4
5
|
import '../rules/catalog.js';
|
|
5
6
|
import '../rules/course-rules.js';
|
|
6
7
|
import '../curriculum/validation.js';
|
|
@@ -24,6 +25,10 @@ import '../reference/mixed-content.js';
|
|
|
24
25
|
import '../reference/gloss.js';
|
|
25
26
|
import '../rules/waivers.js';
|
|
26
27
|
import './knowledge-version.js';
|
|
28
|
+
import './page-versions.js';
|
|
29
|
+
import './decks.js';
|
|
30
|
+
import '../reference/decks-validate.js';
|
|
31
|
+
import '../reference/deck-source.js';
|
|
27
32
|
import './lesson-derivation.js';
|
|
28
33
|
|
|
29
34
|
/** Match only a configured page artifact below Astro's deployment base. */
|
|
@@ -48,6 +53,14 @@ type MarkdownConfig = AstroConfig['markdown'];
|
|
|
48
53
|
* text with no signal anywhere.
|
|
49
54
|
*/
|
|
50
55
|
declare function assertRemarkPluginsWired(manifest: Pick<Manifest, 'abbreviations' | 'sources' | 'notation'>, markdown: MarkdownConfig): void;
|
|
56
|
+
/**
|
|
57
|
+
* Check every built page, and return the findings only a rendered page gives
|
|
58
|
+
* (`render-diagnostics.ts`) for `content warnings`. A page that fails a check
|
|
59
|
+
* does not stop the walk: every page is read first, then the build throws once
|
|
60
|
+
* with the whole list, so it reports each page at once and not one per run.
|
|
61
|
+
* Exported for the test.
|
|
62
|
+
*/
|
|
63
|
+
declare function validateGeneratedPages(directory: URL, manifest: Pick<Manifest, 'lessons'>, warn: (message: string) => void): Promise<RenderDiagnostic[]>;
|
|
51
64
|
/**
|
|
52
65
|
* Restart Astro when an authored content file changes. Astro reloads MDX on
|
|
53
66
|
* its own, but Explico's manifest is created by `astro.config.mjs`; a restart
|
|
@@ -58,4 +71,4 @@ declare function assertRemarkPluginsWired(manifest: Pick<Manifest, 'abbreviation
|
|
|
58
71
|
*/
|
|
59
72
|
declare function explicoContentRefresh(courseConfig: CourseConfig, manifest: Manifest): AstroIntegration;
|
|
60
73
|
|
|
61
|
-
export { assertLocalNotationParity, assertNotationPayloadHtml, assertRemarkPluginsWired, explicoContentRefresh, matchStandaloneDownload };
|
|
74
|
+
export { assertLocalNotationParity, assertNotationPayloadHtml, assertRemarkPluginsWired, explicoContentRefresh, matchStandaloneDownload, validateGeneratedPages };
|
package/dist/compiler/astro.js
CHANGED
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
writeManifest
|
|
9
9
|
} from "./manifest.js";
|
|
10
10
|
import { missingNotationPayloadKeys } from "../reference/page-notation.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
assertNoUnboundFigureSymbols,
|
|
13
|
+
unboundFigureSymbols
|
|
14
|
+
} from "../reference/figure-symbols.js";
|
|
12
15
|
import { undrawnFigurePictures, undrawnFigureWarning } from "../reference/figure-pictures.js";
|
|
13
16
|
import {
|
|
14
17
|
pageRenderDiagnostics,
|
|
@@ -20,7 +23,8 @@ import { writeLatexExports } from "./exports/latex-export.js";
|
|
|
20
23
|
import { writeMarkdownExports } from "./exports/markdown-export.js";
|
|
21
24
|
import { prepareChapters } from "./exports/export-chapters.js";
|
|
22
25
|
import { writeDownloadIndex } from "./exports/download-index.js";
|
|
23
|
-
import {
|
|
26
|
+
import { checkFrameFit } from "./exports/deck-fit.js";
|
|
27
|
+
import { standaloneDownloads, standaloneFilename } from "./exports/export-pages.js";
|
|
24
28
|
import remarkAbbreviations from "../reference/remark-abbreviations.mjs";
|
|
25
29
|
import remarkCitation from "../reference/remark-citation.mjs";
|
|
26
30
|
import remarkNotation from "../reference/remark-notation.mjs";
|
|
@@ -124,17 +128,17 @@ function assertRemarkPluginsWired(manifest, markdown) {
|
|
|
124
128
|
const missing = [];
|
|
125
129
|
if (manifest.abbreviations.length > 0 && !remarkPluginIsWired(remarkPlugins, remarkAbbreviations)) {
|
|
126
130
|
missing.push(
|
|
127
|
-
`remarkAbbreviations is missing from
|
|
131
|
+
`remarkAbbreviations is missing from the remark plugins of astro.config.mjs, but manifest.abbreviations has ${manifest.abbreviations.length} entr${manifest.abbreviations.length === 1 ? "y" : "ies"}. Import it from 'explico/reference/remark-abbreviations.mjs' and add [remarkAbbreviations, { abbreviations: manifest.abbreviations }] to the \`remarkPlugins\` of \`markdown.processor: unified({ \u2026 })\` \u2014 see docs/authoring/migrating.md#4-rule-n14.`
|
|
128
132
|
);
|
|
129
133
|
}
|
|
130
134
|
if (manifest.sources.length > 0 && !remarkPluginIsWired(remarkPlugins, remarkCitation)) {
|
|
131
135
|
missing.push(
|
|
132
|
-
`remarkCitation is missing from
|
|
136
|
+
`remarkCitation is missing from the remark plugins of astro.config.mjs, but manifest.sources has ${manifest.sources.length} entr${manifest.sources.length === 1 ? "y" : "ies"}. Import it from 'explico/reference/remark-citation.mjs' and add [remarkCitation, { sources: manifest.sources }] to the \`remarkPlugins\` of \`markdown.processor: unified({ \u2026 })\`.`
|
|
133
137
|
);
|
|
134
138
|
}
|
|
135
139
|
if (manifest.notation.raw.length > 0 && !remarkPluginIsWired(remarkPlugins, remarkNotation)) {
|
|
136
140
|
missing.push(
|
|
137
|
-
`remarkNotation is missing from
|
|
141
|
+
`remarkNotation is missing from the remark plugins of astro.config.mjs, but manifest.notation.raw has ${manifest.notation.raw.length} entr${manifest.notation.raw.length === 1 ? "y" : "ies"}. Import it from 'explico/reference/remark-notation.mjs' and add [remarkNotation, createRemarkNotationOptions(manifest)] to the \`remarkPlugins\` of \`markdown.processor: unified({ \u2026 })\`.`
|
|
138
142
|
);
|
|
139
143
|
}
|
|
140
144
|
if (missing.length === 0) return;
|
|
@@ -146,16 +150,27 @@ ${missing.map((line) => ` - ${line}`).join("\n")}`
|
|
|
146
150
|
async function validateGeneratedPages(directory, manifest, warn) {
|
|
147
151
|
const root = fileURLToPath(directory);
|
|
148
152
|
const rendered = [];
|
|
153
|
+
const failures = [];
|
|
154
|
+
const unbound = [];
|
|
155
|
+
const messageOf = (error) => error instanceof Error ? error.message : String(error);
|
|
149
156
|
const walk = async (path) => {
|
|
150
|
-
|
|
157
|
+
const entries = (await readdir(path, { withFileTypes: true })).sort(
|
|
158
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
159
|
+
);
|
|
160
|
+
for (const entry of entries) {
|
|
151
161
|
const child = join(path, entry.name);
|
|
152
162
|
if (entry.isDirectory()) {
|
|
153
163
|
await walk(child);
|
|
154
164
|
} else if (entry.isFile() && entry.name.endsWith(".html")) {
|
|
155
165
|
const html = await readFile(child, "utf8");
|
|
156
166
|
const page = relative(root, child);
|
|
157
|
-
|
|
158
|
-
|
|
167
|
+
try {
|
|
168
|
+
assertNotationPayloadHtml(page, html);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
failures.push(messageOf(error));
|
|
171
|
+
}
|
|
172
|
+
const figures = unboundFigureSymbols(html);
|
|
173
|
+
if (figures.length > 0) unbound.push({ page, figures });
|
|
159
174
|
const undrawn = undrawnFigurePictures(html);
|
|
160
175
|
if (undrawn.length > 0) warn(undrawnFigureWarning(page, undrawn));
|
|
161
176
|
rendered.push(...pageRenderDiagnostics(manifest, page, html));
|
|
@@ -163,6 +178,12 @@ async function validateGeneratedPages(directory, manifest, warn) {
|
|
|
163
178
|
}
|
|
164
179
|
};
|
|
165
180
|
await walk(root);
|
|
181
|
+
try {
|
|
182
|
+
assertNoUnboundFigureSymbols(unbound);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
failures.push(messageOf(error));
|
|
185
|
+
}
|
|
186
|
+
if (failures.length > 0) throw new Error(failures.join("\n\n"));
|
|
166
187
|
return rendered;
|
|
167
188
|
}
|
|
168
189
|
function replaceInPlace(target, source) {
|
|
@@ -260,7 +281,7 @@ function explicoContentRefresh(courseConfig, manifest) {
|
|
|
260
281
|
const slug = matchStandaloneDownload(
|
|
261
282
|
pathname,
|
|
262
283
|
configuredDevelopment.base,
|
|
263
|
-
|
|
284
|
+
standaloneDownloads(manifest).map(({ download }) => download)
|
|
264
285
|
);
|
|
265
286
|
if (!slug) return next();
|
|
266
287
|
try {
|
|
@@ -269,7 +290,7 @@ function explicoContentRefresh(courseConfig, manifest) {
|
|
|
269
290
|
);
|
|
270
291
|
developmentDownloads ??= buildDevelopmentDownloads(
|
|
271
292
|
configuredDevelopment,
|
|
272
|
-
|
|
293
|
+
standaloneDownloads(manifest).map(({ download }) => download)
|
|
273
294
|
);
|
|
274
295
|
const currentDownloads = developmentDownloads;
|
|
275
296
|
currentDownloads.catch(() => {
|
|
@@ -310,6 +331,13 @@ ${message}
|
|
|
310
331
|
for (const diagnostic of writeRenderDiagnostics(rendered)) {
|
|
311
332
|
logger.warn(`[${diagnostic.code}] ${diagnostic.file}: ${diagnostic.message}`);
|
|
312
333
|
}
|
|
334
|
+
const overflowing = await checkFrameFit(fileURLToPath(dir), manifest, { logger });
|
|
335
|
+
if (overflowing.length > 0) {
|
|
336
|
+
throw new Error(
|
|
337
|
+
`${overflowing.length} deck frame(s) do not fit their canvas (rule D2, frame-overflow):
|
|
338
|
+
` + overflowing.map((finding) => ` - ${finding.message}`).join("\n")
|
|
339
|
+
);
|
|
340
|
+
}
|
|
313
341
|
await writeStandaloneLessonExports(dir, manifest);
|
|
314
342
|
await writeCombinedExports(dir, manifest);
|
|
315
343
|
const { latex, markdown } = manifest.lessonExports;
|
|
@@ -326,5 +354,6 @@ export {
|
|
|
326
354
|
assertNotationPayloadHtml,
|
|
327
355
|
assertRemarkPluginsWired,
|
|
328
356
|
explicoContentRefresh,
|
|
329
|
-
matchStandaloneDownload
|
|
357
|
+
matchStandaloneDownload,
|
|
358
|
+
validateGeneratedPages
|
|
330
359
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { C as CourseConfig } from '../course-config-BLtIdkei.js';
|
|
2
|
+
import '../rules/catalog.js';
|
|
3
|
+
import '../rules/course-rules.js';
|
|
4
|
+
|
|
5
|
+
/** The usage text `content help` prints; `scripts/agent/gen-map.mjs` lists the verbs from it. */
|
|
6
|
+
declare const CONTENT_USAGE = "Usage:\n pnpm content status\n pnpm content diagnostics [info|warning|error] [--all | --code <c> | --lesson <id> | --changed] [--json]\n pnpm content warnings [--json]\n pnpm content context <lesson> [--json]\n pnpm content equation \"<latex>\" [--lesson <id>] [--json]\n pnpm content bindings <lesson> [--json]\n pnpm content symbol <latex | key> [--json]\n pnpm content check <lesson | deck>\u2026 [--changed] [--json]\n pnpm content rule <ID> [--json]\n pnpm content migrate [--from <version>] [--to <version>] [--json]\n pnpm content rules [--element <e>[,<e>]] [--enforcement script|partial|guide|locked] [--edition chapter|deck] [--json]\n pnpm content move <lesson>:<key> [--to <key>] [--write]\n pnpm content rename <old> <new> [--write]\n pnpm content fix <lesson> [--write]\n pnpm content new lesson <id>\n pnpm content new deck <lesson | part>\n pnpm content new term <key>\n pnpm content init-agent [--write]";
|
|
7
|
+
interface ContentArgs {
|
|
8
|
+
readonly command: string;
|
|
9
|
+
readonly positionals: readonly string[];
|
|
10
|
+
readonly json: boolean;
|
|
11
|
+
readonly changed: boolean;
|
|
12
|
+
/** `--element a,b` — a value-taking flag; also `--element=a,b`. */
|
|
13
|
+
readonly element?: string;
|
|
14
|
+
/** `--enforcement script` — a value-taking flag; also `--enforcement=script`. */
|
|
15
|
+
readonly enforcement?: string;
|
|
16
|
+
/** `--edition deck` — a value-taking flag; also `--edition=deck`. */
|
|
17
|
+
readonly edition?: string;
|
|
18
|
+
/** `--all`: list every diagnostic instead of the counts. */
|
|
19
|
+
readonly all: boolean;
|
|
20
|
+
/** `--code <code>`: only that diagnostic code. */
|
|
21
|
+
readonly code?: string;
|
|
22
|
+
/** `--lesson <id>`: only that lesson's diagnostics; the scope for `equation`. */
|
|
23
|
+
readonly lesson?: string;
|
|
24
|
+
/** `--from <version>` / `--to <version>` for `migrate`; `--to <key>` for `move`. */
|
|
25
|
+
readonly from?: string;
|
|
26
|
+
readonly to?: string;
|
|
27
|
+
/** `--write`: apply a planned edit instead of only showing its diff. */
|
|
28
|
+
readonly write: boolean;
|
|
29
|
+
}
|
|
30
|
+
declare function parseContentArgs(argv: readonly string[]): ContentArgs;
|
|
31
|
+
/** Where a run prints; the process streams by default, a buffer in a test. */
|
|
32
|
+
interface ContentIo {
|
|
33
|
+
readonly out: (text: string) => void;
|
|
34
|
+
readonly err: (text: string) => void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Run one `content` command for this course and return its exit code: 0 on
|
|
38
|
+
* success, 1 on a usage error, a `ContentCliError`, a check that failed, or
|
|
39
|
+
* an unexpected throw (reported by message, never a stack).
|
|
40
|
+
*/
|
|
41
|
+
declare function runContentCli(courseConfig: CourseConfig, argv?: readonly string[], io?: ContentIo): Promise<number>;
|
|
42
|
+
|
|
43
|
+
export { CONTENT_USAGE, type ContentArgs, type ContentIo, parseContentArgs, runContentCli };
|