explico 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +990 -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 +100 -0
- package/dist/compiler/cli-main.js +433 -0
- package/dist/compiler/cli-reference.d.ts +50 -0
- package/dist/compiler/cli-reference.js +213 -0
- package/dist/compiler/cli-verbs.d.ts +45 -0
- package/dist/compiler/cli-verbs.js +224 -0
- package/dist/compiler/cli-verify.d.ts +51 -0
- package/dist/compiler/cli-verify.js +151 -0
- package/dist/compiler/cli.d.ts +408 -24
- package/dist/compiler/cli.js +1328 -8
- package/dist/compiler/collections.d.ts +88 -1
- package/dist/compiler/collections.js +170 -25
- package/dist/compiler/content-release.d.ts +49 -0
- package/dist/compiler/content-release.js +63 -0
- package/dist/compiler/content-schemas.d.ts +2 -2
- 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 +364 -58
- package/dist/compiler/page-versions.d.ts +184 -0
- package/dist/compiler/page-versions.js +217 -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 +82 -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 +6 -2
- package/dist/reference/diagram-fit.js +5 -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 +237 -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 +34 -10
- package/dist/rules/rules.schema.json +280 -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 +31 -9
- package/docs/authoring/agents.md +72 -18
- package/docs/authoring/cli-reference.json +2172 -0
- package/docs/authoring/cli.md +887 -0
- package/docs/authoring/components.md +15 -0
- package/docs/authoring/deck.md +612 -0
- package/docs/authoring/migrating.md +1195 -16
- package/docs/authoring/notation.md +49 -6
- package/docs/authoring/structure.md +80 -1
- package/docs/authoring/versioning.md +56 -3
- package/docs/authoring/visualizations.md +7 -2
- package/docs/course-agent/AGENTS.md +40 -0
- package/docs/course-agent/agents/rules-reviewer.md +33 -0
- package/docs/course-agent/skills/draft-lesson/SKILL.md +38 -0
- package/docs/course-agent/skills/migrate-course/SKILL.md +32 -0
- package/docs/course-agent/skills/release-content/SKILL.md +26 -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 +557 -0
- package/src/compiler/cli-reference.ts +285 -0
- package/src/compiler/cli-verbs.ts +270 -0
- package/src/compiler/cli-verify.ts +241 -0
- package/src/compiler/cli.ts +2119 -170
- package/src/compiler/collections.ts +284 -25
- package/src/compiler/content-release.ts +91 -0
- 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 +568 -63
- package/src/compiler/page-versions.ts +401 -0
- package/src/compiler/resolution-report.ts +38 -7
- package/src/compiler/validate-content.ts +215 -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 +137 -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-fit.ts +18 -4
- 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 +274 -4
- package/src/rules/course-rules.ts +21 -1
- package/src/rules/in-effect.ts +58 -11
- 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
|
@@ -16,16 +16,26 @@ The pattern every time:
|
|
|
16
16
|
|
|
17
17
|
1. Record the currently installed version before changing it:
|
|
18
18
|
`node -p "require('explico/package.json').version"`.
|
|
19
|
-
2. `pnpm add
|
|
20
|
-
|
|
19
|
+
2. `pnpm add explico@<version>`, without `-D`: pnpm then updates the block that
|
|
20
|
+
already lists `explico`, `dependencies` or `devDependencies`. `-D` would move
|
|
21
|
+
the entry into `devDependencies`.
|
|
22
|
+
3. `pnpm content migrate` prints exactly those sections: from the version
|
|
23
|
+
`package.json` pinned at HEAD to the one now installed, oldest first
|
|
24
|
+
(`--from` and `--to` override either end), and refuses when a section is
|
|
25
|
+
missing. Apply **every** section it prints. Reading this file whole instead
|
|
26
|
+
costs forty thousand tokens for the one or two sections an upgrade needs.
|
|
27
|
+
Without the verb, apply every section
|
|
21
28
|
after the old version through the target version, oldest first. Do not skip a
|
|
22
29
|
section merely because its changelog entry is not labelled **Breaking**.
|
|
23
30
|
4. Make every required course-side change named by those sections, including
|
|
24
31
|
changes under `content/`, Astro/source configuration, and course scripts; do
|
|
25
32
|
not stop after changing the dependency declaration.
|
|
26
|
-
5. `pnpm
|
|
27
|
-
`build/resolution/*` and
|
|
28
|
-
|
|
33
|
+
5. `pnpm content verify` (0.15 and later), or by hand: `pnpm validate:content`
|
|
34
|
+
(it rewrites `build/resolution/*` and fails only on a report git tracks that
|
|
35
|
+
it had to rewrite: a course whose `build/` is git-ignored passes on the
|
|
36
|
+
first run, one that commits it commits the rewritten reports and runs it
|
|
37
|
+
again), then `pnpm content check --changed`, then `astro check`, then
|
|
38
|
+
`astro build`.
|
|
29
39
|
Each stage names what it doesn't like; fix and re-run.
|
|
30
40
|
|
|
31
41
|
The engine never fails silently on a migration it can detect: a missing
|
|
@@ -33,18 +43,1159 @@ required config block throws with the section of this file to read.
|
|
|
33
43
|
|
|
34
44
|
---
|
|
35
45
|
|
|
46
|
+
## 0.14.0 → 0.15.0
|
|
47
|
+
|
|
48
|
+
**No course changes required.** This release adds verbs (`content help
|
|
49
|
+
<verb>`, `content verify`, `content release`, `content fix --all`), rule D11's
|
|
50
|
+
`deck-stale` info, and better messages; the manifest schema stays v37. Two
|
|
51
|
+
steps are conditional: a course that ran `content move --write` on 0.14.0
|
|
52
|
+
(step 4), and a course whose tests read the CLI's text output (step 6).
|
|
53
|
+
|
|
54
|
+
### 1. Update the dependency
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pnpm add explico@<this version>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
pnpm 11 holds back a release younger than its `minimumReleaseAge`: either
|
|
61
|
+
`pnpm add` stops with `ERR_PNPM_NO_MATURE_MATCHING_VERSION`, or pnpm adds an
|
|
62
|
+
`explico@<version>` line to `minimumReleaseAgeExclude` in
|
|
63
|
+
`pnpm-workspace.yaml` itself. Add that line when it does not, and commit it.
|
|
64
|
+
|
|
65
|
+
### 2. Rule D11: a deck whose chapter moved on — nothing to do
|
|
66
|
+
|
|
67
|
+
Rule D11 adds an info diagnostic, `deck-stale`, naming a lesson's own deck
|
|
68
|
+
whose chapter changed, by rule C5's version, after the deck itself last did. A
|
|
69
|
+
part's deck covers several chapters and is never flagged. Nothing to
|
|
70
|
+
configure; a course without decks sees nothing.
|
|
71
|
+
|
|
72
|
+
### 3. Optional: let `pnpm content verify` be the course's verification
|
|
73
|
+
|
|
74
|
+
`pnpm content verify` runs `validate:content`, `content check --changed`,
|
|
75
|
+
`astro check`, the course's `test` script and `astro build` (with `--e2e`, its
|
|
76
|
+
`test:e2e` script too), and answers at once on a tree its last pass covered. It
|
|
77
|
+
reaches a course through the `scripts/content.ts` shim of 0.14.0 step 14 and
|
|
78
|
+
needs nothing else. Where the course's `AGENTS.md`, README or `package.json`
|
|
79
|
+
spells that sequence out by hand, it can say `pnpm content verify` instead; a
|
|
80
|
+
`verify` script becomes `"verify": "pnpm content verify"`. The pass record and
|
|
81
|
+
the logs go to `node_modules/.cache/explico/verify/`, never into the tree.
|
|
82
|
+
`astro check` is skipped, by name, when `@astrojs/check` is not installed.
|
|
83
|
+
|
|
84
|
+
### 4. Only if you ran `content move --write` on 0.14.0: the card's placeholder body
|
|
85
|
+
|
|
86
|
+
0.14.0's `content move` wrote every card it created a placeholder body,
|
|
87
|
+
"Promoted from `<lesson>`'s `notation.local`; the meaning above is what that
|
|
88
|
+
page said. Extend this body with what a glossary reader needs.", which the
|
|
89
|
+
glossary publishes. Find them:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
grep -rl "Extend this body with what a glossary reader needs" content/notation
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Replace each with what a reader needs beyond the meaning, or delete the body
|
|
96
|
+
(a card needs none). Check those cards' `domain:` too: 0.14.0 wrote `general`
|
|
97
|
+
for a key without a prefix. From this release `move` writes no body, takes the
|
|
98
|
+
domain the course's own cards use, and says what the author still owes.
|
|
99
|
+
|
|
100
|
+
### 5. Rule N4's lints: better advice, and the run log counts them — nothing to do
|
|
101
|
+
|
|
102
|
+
`definition-repeated` on a local entry whose glyph a shared card already owns
|
|
103
|
+
no longer says "move one copy to `content/notation/`": the entry is a reused
|
|
104
|
+
letter (rule N4), and promoting it would put two shared cards on one glyph. It
|
|
105
|
+
says how to acknowledge the copies instead (`definition-repeated:<key> — <why>`
|
|
106
|
+
in `lint-ignore.yml`), and `content move` cautions before writing such a card.
|
|
107
|
+
`validate:content`'s run log records each consistency warning at WARNING now,
|
|
108
|
+
so the `Run log: (… warning(s))` line it prints matches `pnpm content status`
|
|
109
|
+
where it used to say 1; nothing fails on a warning.
|
|
110
|
+
|
|
111
|
+
The `explain-redundant` infos an upgrade to 0.14.0 left (a thousand, in one
|
|
112
|
+
course) clear in one plan: `pnpm content fix --all` shows the diff and
|
|
113
|
+
`--write` applies it. It edits lessons, so it is a content change: a bullet in
|
|
114
|
+
`content/CHANGELOG.md` (rule C1) and the version bump rule C2 asks for.
|
|
115
|
+
|
|
116
|
+
### 6. Only if a course test reads the CLI's text output
|
|
117
|
+
|
|
118
|
+
The `--json` reports did not change; a test that reads them keeps passing, and
|
|
119
|
+
`pnpm content help <verb> --json` now prints each one's JSON Schema. The text
|
|
120
|
+
did change:
|
|
121
|
+
|
|
122
|
+
- `pnpm content help`: every usage line ends in `[--json]`; `release`,
|
|
123
|
+
`verify` and `help` are listed, and `fix` reads `<lesson>… | --all`.
|
|
124
|
+
- `content check`: a lesson's info is one line counted by code
|
|
125
|
+
(`+ 28 info (…)`); errors and warnings are listed as before.
|
|
126
|
+
- `content status`: at most six of the pages with unreleased changes, then
|
|
127
|
+
`… N more in --json`.
|
|
128
|
+
- `content context`: `diagnostics: N (M in <file>, K in the shared cards it
|
|
129
|
+
shows)` when some come from the cards.
|
|
130
|
+
- `content init-agent`: `new` or `differs from the engine's copy` where it
|
|
131
|
+
said `installed` or `refreshed from the engine`; after `--write` every edit
|
|
132
|
+
verb prints a `next:` line.
|
|
133
|
+
- `dia-too-wide` on `<CurriculumMap>`'s graph (rule V8) names the map's props.
|
|
134
|
+
|
|
135
|
+
Match the `--json` instead of the text.
|
|
136
|
+
|
|
137
|
+
### 7. Optional: refresh the agent procedures; content releases with `content release`
|
|
138
|
+
|
|
139
|
+
`pnpm content release [major|minor|patch]` cuts a content release (rule C3):
|
|
140
|
+
`## [Unreleased]` in `content/CHANGELOG.md` becomes a dated `## [x.y.z]` and
|
|
141
|
+
`about.version` becomes `x.y.z`, as a diff that `--write` applies. With no
|
|
142
|
+
argument the bump is what the section's headings imply under rule C2
|
|
143
|
+
(`### Breaking` or `### Removed` a MAJOR, `### Added` a MINOR, else a PATCH);
|
|
144
|
+
check it against the rule. Then `pnpm validate:content` and commit the three
|
|
145
|
+
files together. 0.14.0's `release-content` skill called
|
|
146
|
+
`pnpm release:content`, a script only the playground had; a course that wrote
|
|
147
|
+
its own can make it `"release:content": "pnpm content release --write"`.
|
|
148
|
+
|
|
149
|
+
`pnpm content init-agent --write` refreshes the installed skills:
|
|
150
|
+
`draft-lesson` and `migrate-course` now end on `pnpm content verify`, and
|
|
151
|
+
`release-content` runs `pnpm content release`. It never edits an existing
|
|
152
|
+
`AGENTS.md`; the shipped baseline
|
|
153
|
+
(`node_modules/explico/docs/course-agent/AGENTS.md`) shows the two lines to
|
|
154
|
+
copy: `pnpm content verify` before handing back (step 3), and
|
|
155
|
+
`pnpm content help <verb>`, which prints a verb's flags and the shape of its
|
|
156
|
+
`--json` ([`cli.md`](cli.md) has every verb).
|
|
157
|
+
|
|
158
|
+
### 8. Verify
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
pnpm content verify # add --e2e when the course has browser tests
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The manifest schema did not move, so a course that commits
|
|
165
|
+
`build/resolution/` sees no rewritten report, and `content/versions.json`
|
|
166
|
+
changes only where step 4 or step 5 edited content.
|
|
167
|
+
|
|
168
|
+
## 0.13.1 → 0.14.0
|
|
169
|
+
|
|
170
|
+
**One course change is required, two are recommended, and a rare page needs
|
|
171
|
+
one.** Required: run `pnpm validate:content` once and commit the
|
|
172
|
+
`content/versions.json` it writes, the record of each page's own version
|
|
173
|
+
(rule C5, step 10); in CI the build fails until it is committed. Recommended:
|
|
174
|
+
`astro.config.mjs` keeps working as it is, but Astro 7.3 deprecates the flat
|
|
175
|
+
`markdown.remarkPlugins`, `markdown.rehypePlugins` and `markdown.remarkRehype`
|
|
176
|
+
(every `astro` command prints "Pass them to `unified({...})` from
|
|
177
|
+
`@astrojs/markdown-remark` directly instead.", and `astro check` reports a
|
|
178
|
+
`ts(6385)` hint wherever course code reads them), and its default processor is
|
|
179
|
+
now Sätteri, which runs neither list. Until the lists are removed Astro moves
|
|
180
|
+
them onto a `unified()` processor for the course. The manifest schema is now
|
|
181
|
+
v32 (step 10). **One new error can appear:** rule N4 now reads the math in a notation
|
|
182
|
+
entry's body, and the build fails on an entry whose body did not render
|
|
183
|
+
(step 9); no known course has one. The rest is optional or rare: a package
|
|
184
|
+
that now loads, a prop on `<CurriculumMap>`, four glyph-resolver fixes (step 4), one of which can turn
|
|
185
|
+
an ambiguous `x_{12}` into an `ambiguous-base-fallback` error, lab fixes
|
|
186
|
+
(step 5), and, only for a course whose CSS or browser tests reach into a
|
|
187
|
+
diagram's markup, the phone's text size or the `[?]` panel, a diagram that now
|
|
188
|
+
shrinks with the phone's text size inside a new frame element (step 6), a
|
|
189
|
+
contrast fix for an active term link (step 7), and a new tooltip on the
|
|
190
|
+
Download button (step 8). A course with the LaTeX export can turn on a
|
|
191
|
+
compile cache (step 13); `scripts/content.ts` and `scripts/compile-manifest.ts`
|
|
192
|
+
become three-line shims over the engine, which brings every new `content` verb
|
|
193
|
+
and rule C5 to a course that skipped it (step 14); `validate:content` now passes on its
|
|
194
|
+
first run and every diagnostic names its rule (step 15); rule N4's lints now
|
|
195
|
+
follow the tier model, which retires the acknowledgements for reused letters
|
|
196
|
+
and adds a warning and two infos to clear (step 20). The glossary page now
|
|
197
|
+
lists page-bound meanings under its index and a formula's symbol list names
|
|
198
|
+
every symbol (step 21): nothing to do unless course CSS or tests read them. A lesson can now have
|
|
199
|
+
a deck, its slide edition; nothing changes until a course writes one (step 16),
|
|
200
|
+
and the sidebar then lists its lesson once, with a setting and a reader's
|
|
201
|
+
switch for which edition leads; the manifest schema is now v34 (step 17). A
|
|
202
|
+
deck's page shows which slides Present mode has shown each reader, above each
|
|
203
|
+
frame and on a slide map, and presents from any frame; the manifest schema is
|
|
204
|
+
now v35 (step 18). A part can have a deck of its
|
|
205
|
+
own, the lecture of the whole part; nothing changes until a course writes one,
|
|
206
|
+
and the manifest schema is now v36 (step 19); in the sidebar it then shares
|
|
207
|
+
its Overview's row (step 22). The sidebar now marks the pages a
|
|
208
|
+
reader has not opened yet and those changed since (step 11), and every
|
|
209
|
+
course now asks each reader before it keeps their reading in the browser
|
|
210
|
+
(step 12); nothing to do unless a course wants either off, but a course's
|
|
211
|
+
browser tests that expect kept cards to survive a restart need a setup line.
|
|
212
|
+
Add a course-owned 404 page and an Astro `site` value to replace the fallback
|
|
213
|
+
and clear its two build warnings (step 24). The header, the sidebars and a
|
|
214
|
+
deck's slides now have colours and edges of their own, from a new
|
|
215
|
+
`appearance` setting; nothing to do unless screenshot tests or course CSS
|
|
216
|
+
read them, and the manifest schema is now v37 (step 25). The engine now supplies an empty
|
|
217
|
+
English `i18n` entry when a course has no translations, so the old empty-`i18n`
|
|
218
|
+
warning needs no course workaround.
|
|
219
|
+
|
|
220
|
+
### 1. Update the dependency
|
|
221
|
+
|
|
222
|
+
**A course on 0.13.0 or 0.13.1, first:** delete the downstream patch that
|
|
223
|
+
added `dist/components/float-name.js` to the installed package
|
|
224
|
+
(`patches/explico@0.13.1.patch`) and its `patchedDependencies` entry, in
|
|
225
|
+
`pnpm-workspace.yaml` or under `pnpm` in `package.json`, and any test that
|
|
226
|
+
pins the old path. This release compiles the module as
|
|
227
|
+
`dist/reference/float-name.js`, which the export serializers import, and
|
|
228
|
+
pnpm refuses the upgrade with `ERR_PNPM_UNUSED_PATCH` while an entry names a
|
|
229
|
+
patch for a version no longer installed. Then:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
pnpm add explico@<this version>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
pnpm 11 holds back a release younger than its `minimumReleaseAge`: either
|
|
236
|
+
`pnpm add` stops with `ERR_PNPM_NO_MATURE_MATCHING_VERSION`, or pnpm adds an
|
|
237
|
+
`explico@<version>` line to `minimumReleaseAgeExclude` in
|
|
238
|
+
`pnpm-workspace.yaml` itself. Add that line when it does not, and commit it
|
|
239
|
+
with the upgrade.
|
|
240
|
+
|
|
241
|
+
**Coming from 0.12.0 or earlier:** apply "0.13.0 → 0.13.1" and "0.12.0 →
|
|
242
|
+
0.13.0" below first.
|
|
243
|
+
|
|
244
|
+
### 2. Move the plugin lists into `markdown.processor`
|
|
245
|
+
|
|
246
|
+
Only when `astro.config.mjs` still has `markdown.remarkPlugins`,
|
|
247
|
+
`markdown.rehypePlugins` or `markdown.remarkRehype`:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
rg -n 'remarkPlugins|rehypePlugins|remarkRehype' astro.config.mjs src scripts
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Import `unified` and hand it the same lists:
|
|
254
|
+
|
|
255
|
+
```js
|
|
256
|
+
import { unified } from '@astrojs/markdown-remark';
|
|
257
|
+
|
|
258
|
+
export default defineConfig({
|
|
259
|
+
// …
|
|
260
|
+
markdown: {
|
|
261
|
+
processor: unified({
|
|
262
|
+
remarkPlugins: [/* the entries markdown.remarkPlugins had, in order */],
|
|
263
|
+
rehypePlugins: [/* the entries markdown.rehypePlugins had, in order */],
|
|
264
|
+
// remarkRehype: { … } // only when you set one
|
|
265
|
+
}),
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Keep the order: `remarkAsides` stays last in `remarkPlugins`. Nothing in
|
|
271
|
+
explico needs the flat form: `explicoContentRefresh()` checks the
|
|
272
|
+
manifest-driven plugins (`remarkAbbreviations`, `remarkCitation`,
|
|
273
|
+
`remarkNotation`) on both surfaces, MDX extends `markdown.processor`, and
|
|
274
|
+
Starlight 0.42 appends its own plugins to it. Course code that reads
|
|
275
|
+
`markdown.remarkPlugins` (a test that asserts the wiring, a script) reads
|
|
276
|
+
`markdown.processor.options.remarkPlugins` instead. The playground's
|
|
277
|
+
`astro.config.mjs` is a worked example; the time-series course made this move
|
|
278
|
+
and all 141 of its built pages came out byte-identical.
|
|
279
|
+
|
|
280
|
+
### 3. Only if a competency graph warns `dia-too-wide`
|
|
281
|
+
|
|
282
|
+
A course with many competencies gets `[dia-too-wide] curriculum-map/index.html:
|
|
283
|
+
Diagram "competency-graph" …` (rule V8) on the curriculum map, because the
|
|
284
|
+
graph is one diagram. `<CurriculumMap>` now takes three optional props, all
|
|
285
|
+
forwarded to the competency diagram(s); with none of them the page is
|
|
286
|
+
unchanged:
|
|
287
|
+
|
|
288
|
+
```mdx
|
|
289
|
+
<CurriculumMap graph="per-part" />
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
- `graph="per-part"` draws one diagram per part, in the order of
|
|
293
|
+
`course.settings.json` `parts`, under an `<h3>` with the part's overview
|
|
294
|
+
title (its label when it has none). A diagram holds the competencies the
|
|
295
|
+
part's lessons teach first and one box per earlier competency they build on,
|
|
296
|
+
labelled `<part>: <competency>`; competencies no lesson teaches form a last
|
|
297
|
+
group. The ids are `competency-graph-<part dir>` and
|
|
298
|
+
`competency-graph-untaught`. The default is `graph="whole"`.
|
|
299
|
+
- `direction="LR"` lays the diagrams out left to right (`TB` is the default).
|
|
300
|
+
- `warnAboveWidth={false}` (or a width in px) replaces rule V8's column width
|
|
301
|
+
for these diagrams, for a whole-course graph that is meant to scroll.
|
|
302
|
+
|
|
303
|
+
### 4. Rule N23 and the glyph resolver: findings that go away, one that can appear
|
|
304
|
+
|
|
305
|
+
Four resolver fixes change what `validate:content` reports, and one message is
|
|
306
|
+
clearer; none needs an edit to pass. Look for the workarounds they make
|
|
307
|
+
unnecessary, and for one new error:
|
|
308
|
+
|
|
309
|
+
- **`index-range` for a letter that is not an index.** `b` in `\nabla_{b_n}`
|
|
310
|
+
(the base of a carded symbol in a subscript) and the letters of a subscript
|
|
311
|
+
with a relation (`\mathbb{E}_{Z \sim g}`) are no longer running indices. A
|
|
312
|
+
`range` you gave the card of `b_n` only to silence `b`, and a
|
|
313
|
+
`fixedIndex: true` on a density `g` that only appeared in a condition, can
|
|
314
|
+
be deleted. `n` of `b_n` still needs its set, once per page.
|
|
315
|
+
- **A family of juxtaposed letters now reaches its instances.** `x_{ij}`,
|
|
316
|
+
`K_{ij}`, `r_{ik}` give their card to `x_{i1}`, `x_{12}`, `K_{ji}` (which
|
|
317
|
+
failed rule N4 as unresolved): a run of as many letters and digits. `\explain{responsibility}{r_{i1}}` wraps that you
|
|
318
|
+
added so an instance found its card are harmless and can stay or go. **A page
|
|
319
|
+
that has both `x_{ij}` and `x_k` as entries now fails
|
|
320
|
+
`ambiguous-base-fallback` on `x_{12}`**, which both fit: wrap that instance
|
|
321
|
+
in `\explain{key}{x_{12}}` for the one you mean.
|
|
322
|
+
- **An `\explain` in an instance's subscript** (`\beta_{\explain{count}{K}}`)
|
|
323
|
+
keeps the family's card on the instance. The workaround `\explain{beta-j}{\beta_{\explain{count}{K}}}`
|
|
324
|
+
resolves to the same markup, so nothing needs to change.
|
|
325
|
+
- **A wrapped instance is not re-bound inside.** `\explain{component-sd}{\sigma_1}`
|
|
326
|
+
no longer wraps the `\sigma` in a plain `\sigma` entry's card, so a page that
|
|
327
|
+
kept the wrap off to avoid that can add it back. Rendered pages that had the
|
|
328
|
+
inner card change: re-record screenshots of a hovered σ.
|
|
329
|
+
- **A clearer `unresolved-glyph` for an instance of a family whose index a card
|
|
330
|
+
fixed.** Marking the card of `m` `fixedIndex: true` makes the family `r_m` a
|
|
331
|
+
single symbol, so `r_2` is no longer its instance. The error used to say
|
|
332
|
+
"never a bare r" and suggest `\explain{periodic-rate}{r}`; it now names the
|
|
333
|
+
card that fixed `m` and suggests a `range` on it. The same error, only its
|
|
334
|
+
text and two new fields on the unresolved item (`instance`, `fixedLenders`
|
|
335
|
+
of `resolveMathGlyphs`) change, so a test that matched the old sentence for
|
|
336
|
+
such a symbol moves. A course that cleared an `index-range` error with
|
|
337
|
+
`fixedIndex: true` on a letter that also indexes a family (`m` under `r_m`)
|
|
338
|
+
and then wrapped the instances can give that card a `range` instead and
|
|
339
|
+
delete the wraps.
|
|
340
|
+
|
|
341
|
+
### 5. Lab math: workarounds you can drop
|
|
342
|
+
|
|
343
|
+
Three fixes touch what a lab template and the build do with a figure. None
|
|
344
|
+
needs an edit, and each makes an earlier workaround unnecessary:
|
|
345
|
+
|
|
346
|
+
- **`e`, `\pi`, `i` and `d` in a lab template** no longer fail the figure check
|
|
347
|
+
("figure symbols without a notation card"), so the notation cards a course
|
|
348
|
+
added only to bind Euler's number or the circle constant in a template can go
|
|
349
|
+
if nothing else uses them. An index (`e_i`, rule N23), a decorated letter
|
|
350
|
+
(rule N22) or a styled one still needs one.
|
|
351
|
+
- **A key only a lab template binds** now reaches the page's card payload. A
|
|
352
|
+
course that also named every such key in the lesson's prose so the card
|
|
353
|
+
would ship can stop doing so.
|
|
354
|
+
- **`lab-katex-direct` is one error per lesson that reaches a KaTeX-importing
|
|
355
|
+
helper**, not one per helper. A script that tallies the diagnostics by file
|
|
356
|
+
sees more of them, and `pnpm content check <lesson>` now answers for that
|
|
357
|
+
lesson alone. `astro build` reports every page with an unbound figure symbol
|
|
358
|
+
in one failure, not the first.
|
|
359
|
+
|
|
360
|
+
### 6. Only if your course CSS or browser tests reach into a diagram, the text size or the `[?]` panel
|
|
361
|
+
|
|
362
|
+
Nothing in `content/` or `course.config.ts` changes, and the manifest schema
|
|
363
|
+
stays v31. These are changes to markup and behaviour that course CSS and
|
|
364
|
+
browser tests can depend on:
|
|
365
|
+
|
|
366
|
+
- **A diagram has a new frame element.** The markup is now
|
|
367
|
+
`.diagram-scroll > .diagram-frame > .diagram-canvas > svg`. A selector that
|
|
368
|
+
takes `.diagram-canvas` as a *child* of `.diagram-scroll`
|
|
369
|
+
(`.diagram-scroll > .diagram-canvas`) must become a descendant selector
|
|
370
|
+
(`.diagram-scroll .diagram-canvas`). `--diagram-width` and `--diagram-height`
|
|
371
|
+
moved from the canvas to the frame; the canvas inherits them, so
|
|
372
|
+
`getComputedStyle(canvas).getPropertyValue(…)` still reads them, but
|
|
373
|
+
`canvas.style.getPropertyValue(…)` no longer does.
|
|
374
|
+
- **The phone's text size is 50, 59, 71, 84 and 100%** (it was 100, 90, 80 and
|
|
375
|
+
70%), smallest first. A reader's saved 90, 80 or 70 is ignored and the page
|
|
376
|
+
opens at 100% until they choose again; no action. A browser test that picks
|
|
377
|
+
a size or asserts the root font size must use the new values:
|
|
378
|
+
`data-text-size="84"` is 13.44 px, `71` is 11.36 px, `59` is 9.44 px, `50` is
|
|
379
|
+
8 px.
|
|
380
|
+
- **A diagram shrinks with the text size as a whole**, boxes and text,
|
|
381
|
+
through `transform: scale(var(--explico-text-scale))` on `.diagram-canvas`
|
|
382
|
+
and a scaled `width`/`height` on `.diagram-frame`, on a touch screen with a
|
|
383
|
+
size chosen. A test that reads a node's `getBoundingClientRect()` then sees
|
|
384
|
+
scaled numbers, and `.diagram-canvas`'s `offsetWidth` stays the build-time
|
|
385
|
+
size (it ignores the transform): measure the frame or the scroller's
|
|
386
|
+
`scrollWidth`. Course CSS must not set `transform`, `width` or `height` on
|
|
387
|
+
`.diagram-canvas` or `.diagram-frame`. A diagram inside a preview card is not
|
|
388
|
+
scaled. Print is unchanged. `node_modules/explico/docs/diagram-scaling.md`
|
|
389
|
+
has the reasons and the rules for changing it.
|
|
390
|
+
- **The `[?]` panel on a phone** is a sheet fixed under the header below 50rem
|
|
391
|
+
(`.about-body`), no longer an absolute box hanging from the button. Delete a
|
|
392
|
+
course override of `.about-body`'s position or width that worked around the
|
|
393
|
+
old placement, and any rule that restyled the `[?]` button's `summary`
|
|
394
|
+
inside `.layout-navigation-content`: the button is round there now.
|
|
395
|
+
- **A hover peek no longer overwrites a collapsed section's saved state.** A
|
|
396
|
+
reader whose choice was already overwritten stays open until they collapse it
|
|
397
|
+
again. A browser test that collapses a section and reloads should move the
|
|
398
|
+
pointer away first (`page.mouse.move(0, 0)`): with the pointer left on the
|
|
399
|
+
heading, the section peeks (`data-state="peeking"`) after the reload, as it
|
|
400
|
+
should, and asserting `closed` at once races that.
|
|
401
|
+
|
|
402
|
+
### 7. Only if your course CSS lifts the contrast of an active term link
|
|
403
|
+
|
|
404
|
+
0.13.1 blended the highlight of every active notation target, a prose term
|
|
405
|
+
link (`a.notation-term`) included, and in the light theme that took the
|
|
406
|
+
engine's own link colour below 4.5:1 over a tinted panel (axe `color-contrast`
|
|
407
|
+
in a glossary entry). This release blends a symbol inside `.katex-html` only;
|
|
408
|
+
a prose link keeps the opaque `--sl-color-accent-low` tint it had on 0.13.0.
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
rg -n 'notation-term.*is-notation-active|is-notation-active.*notation-term' src content tests
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
A hit is the workaround for that fault, such as
|
|
415
|
+
`a.notation-term.is-notation-active { color: var(--sl-color-accent-high); }`.
|
|
416
|
+
Delete it; the link text is 4.75:1 on the tint without it. Run the course's
|
|
417
|
+
axe checks over a glossary page with a hovered symbol to confirm.
|
|
418
|
+
|
|
419
|
+
### 8. Only if a browser test reads the Download button's tooltip
|
|
420
|
+
|
|
421
|
+
The header's Download button (`button.lesson-export-actions__download`) had
|
|
422
|
+
`title="Download this page"` on every page. Its `title` now names the scopes
|
|
423
|
+
the menu offers there: "Download this chapter, this part or the whole course"
|
|
424
|
+
on a chapter, "Download this part or the whole course" on a part's page and
|
|
425
|
+
"Download the whole course" on any other page. **Its accessible name is
|
|
426
|
+
unchanged**, so `getByRole('button', { name: 'Download this page' })` still
|
|
427
|
+
finds it.
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
rg -n "Download this page" tests src
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
A hit that selects the button by role and name needs nothing. One that matches
|
|
434
|
+
`title`, `getByTitle` or `[title="Download this page"]` takes the new text, or
|
|
435
|
+
better selects by role and name.
|
|
436
|
+
|
|
437
|
+
### 9. Rule N4 now reads a notation entry's body
|
|
438
|
+
|
|
439
|
+
`validate:content` resolves every `$…$` / `$$…$$` in the Markdown body of a
|
|
440
|
+
`content/notation/*.md` entry, against every shared entry, as the glossary's
|
|
441
|
+
render already did. Before, a symbol no entry binds there passed
|
|
442
|
+
`validate:content`, `astro build` logged one `[ERROR] [glob-loader]` line and
|
|
443
|
+
ended green, and the glossary showed the entry without its body. Now:
|
|
444
|
+
|
|
445
|
+
- `validate:content` reports `[N4 unresolved-glyph] content/notation/<key>.md:<line>`
|
|
446
|
+
(or `invalid-latex`, `currency-dollar`, … for the other faults) at the
|
|
447
|
+
file's own line. Fix it in the entry: give the symbol an entry of its own in
|
|
448
|
+
`content/notation/`, or wrap it in `\explain{key}{latex}` with the entry it
|
|
449
|
+
stands for. `[[key]]` adds nothing here, since every shared entry is already
|
|
450
|
+
in scope, and an entry has no `notation.local`.
|
|
451
|
+
- `astro build` fails at the glossary with "Notation entries whose body did not
|
|
452
|
+
render: …" for an entry whose body Astro could not render, whatever the
|
|
453
|
+
cause. The `[glob-loader]` error above it in the log says why.
|
|
454
|
+
- `astro build` reports a notation entry's failure at the file's own line, and
|
|
455
|
+
an unresolved-glyph error ends its token list with `, in $…$`. A script or
|
|
456
|
+
test that parses `entry.md:<line>` from the build log no longer needs to add
|
|
457
|
+
the frontmatter's height.
|
|
458
|
+
- An entry's body now honours `fixedIndex` (rule N23): `r_2` no longer takes
|
|
459
|
+
the card of the family `r_m` when `m` is marked `fixedIndex: true`. Give the
|
|
460
|
+
instance an entry or an `\explain` wrap, or the letter a `range` if it runs.
|
|
461
|
+
|
|
462
|
+
A course test that resolves the entry bodies itself (credit-and-derivatives'
|
|
463
|
+
`tests/notation/notation-entry-body-math.test.ts`) can be deleted once
|
|
464
|
+
`validate:content` passes: the gate covers it, against the same table.
|
|
465
|
+
|
|
466
|
+
### 10. Commit `content/versions.json`, each page's own version (rule C5)
|
|
467
|
+
|
|
468
|
+
Each chapter and part page now carries the knowledge version its content last
|
|
469
|
+
changed in, under the title of its PDF, in its source zip's README, under the
|
|
470
|
+
title of its Markdown, and in the `[?]` panel's new "This page" row:
|
|
471
|
+
"unchanged since version 0.2.0", or "with changes after version 0.4.0, not yet
|
|
472
|
+
released" while `## [Unreleased]` holds a change. A part's and the course's
|
|
473
|
+
PDF and Markdown carry the newest among their pages. The record is
|
|
474
|
+
`content/versions.json`, which `validate:content` writes and the course
|
|
475
|
+
commits like a lockfile; never edit it by hand. The manifest schema is now
|
|
476
|
+
v32 (`lessons[].since`, `parts[].overview.since`, `about.knowledge.releases`
|
|
477
|
+
and `unreleased`, `pageVersions`).
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
pnpm validate:content # prints "content/versions.json written — commit it with the change (rule C5)"
|
|
481
|
+
git add content/versions.json
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Commit it with the upgrade. From then on `validate:content` rewrites the file
|
|
485
|
+
after a content change and after a release (`pnpm release:content`, then
|
|
486
|
+
`pnpm validate:content`), and the file goes in the same commit. In CI (`CI`
|
|
487
|
+
set) `validate:content` does not write; a record that would change fails with
|
|
488
|
+
`[C5 page-versions-stale] content/versions.json is out of date (N page(s): …)`
|
|
489
|
+
until the commit carries the file. The first record has no history to draw
|
|
490
|
+
on: while `## [Unreleased]` holds bullets, every page reads "with changes after
|
|
491
|
+
version `x.y.z`, not yet released", `x.y.z` being `about.version`, until the
|
|
492
|
+
next release stamps them all; with an empty `## [Unreleased]`, every page reads
|
|
493
|
+
"unchanged since version `x.y.z`". Earlier releases are not reconstructed. So
|
|
494
|
+
the order matters: write the first record before adding the upgrade's own
|
|
495
|
+
bullet under `## [Unreleased]` (rule C1), or every page reads "not yet
|
|
496
|
+
released" although none changed.
|
|
497
|
+
|
|
498
|
+
What counts as a page's content: its file, its `.checks.yml` and the
|
|
499
|
+
assessments it lists, the shared notation entries its text uses, the sources
|
|
500
|
+
it cites, and its numbers. A lab, `content/domain/`, a part's label or the
|
|
501
|
+
engine change the PDF but not the stamp.
|
|
502
|
+
|
|
503
|
+
What else moves:
|
|
504
|
+
|
|
505
|
+
- The downloads' version line. `From Course, version 0.4.0.` under a
|
|
506
|
+
chapter's title is now `From Course, unchanged since version 0.2.0.`; a
|
|
507
|
+
book's title page says `Content unchanged since version 0.2.0` where it
|
|
508
|
+
said `Content version 0.4.0`; the zips' READMEs `(unchanged since version
|
|
509
|
+
0.2.0)` where they said `(content version 0.4.0)`; Markdown the same. A
|
|
510
|
+
test that reads these lines reads the new ones.
|
|
511
|
+
- A document unchanged since an earlier release keeps that release's date in
|
|
512
|
+
its PDF and figure PDFs (`SOURCE_DATE_EPOCH`) and its trailer `/ID`. With
|
|
513
|
+
`EXPLICO_LATEX_CACHE` (step 13) a release therefore recompiles only the
|
|
514
|
+
documents it changed.
|
|
515
|
+
- A browser test that reads the `[?]` panel finds a `This page` row with
|
|
516
|
+
`[data-page-since="0.2.0"]` (`"unreleased"` for a change not yet released)
|
|
517
|
+
on chapters and part pages, none elsewhere.
|
|
518
|
+
- `pnpm content status --json` reports `pageVersions: { tracked, unreleased }`
|
|
519
|
+
and `content context` a lesson's `since`. Optional: print them in the
|
|
520
|
+
course's `scripts/content.ts`, and end `scripts/release-content.ts` with a
|
|
521
|
+
reminder to run `validate:content`; the playground's copies show both.
|
|
522
|
+
|
|
523
|
+
To opt out, turn the rule off in `course.settings.json`:
|
|
524
|
+
`"rules": { "C5": { "enabled": false } }`. Every page then carries the
|
|
525
|
+
course's version as before, and no file is read or written.
|
|
526
|
+
|
|
527
|
+
### 11. The sidebar's marks: nothing to do, unless you want them off
|
|
528
|
+
|
|
529
|
+
The sidebar now marks, for each reader, the chapters and part pages they
|
|
530
|
+
have not opened yet (a quiet ring) and those whose content changed since they
|
|
531
|
+
last opened them (an accent dot), with a line under the chapter list that
|
|
532
|
+
says what the marks mean and offers "Mark all as seen" and "Mark all as
|
|
533
|
+
unseen". "Changed" uses the
|
|
534
|
+
page hashes of `content/versions.json` (step 10), so it needs nothing new
|
|
535
|
+
from the course. Both marks are on by default.
|
|
536
|
+
|
|
537
|
+
To turn either off, add the block to `content/course.settings.json`:
|
|
538
|
+
|
|
539
|
+
```json
|
|
540
|
+
"sidebarMarks": { "unopened": false, "changed": true }
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
and, when the course keeps a settings schema with
|
|
544
|
+
`"additionalProperties": false` (the playground's
|
|
545
|
+
`content/course.settings.schema.json` does), copy the playground's
|
|
546
|
+
`sidebarMarks` property into it, or the settings form flags the block.
|
|
547
|
+
|
|
548
|
+
Only if a course's CSS or browser tests reach into the sidebar:
|
|
549
|
+
|
|
550
|
+
- A marked row's `<li>` carries `data-page-mark="unopened"` or `"changed"`,
|
|
551
|
+
and draws the mark with its `::after`; a `<details>` part carries
|
|
552
|
+
`data-page-marks`, and so does `.layout-navigation-panel`. Course CSS that
|
|
553
|
+
sets `li::after` in the sidebar competes with the mark.
|
|
554
|
+
- A marked link gains `aria-describedby`, naming the mark ("Not opened yet",
|
|
555
|
+
"Changed since you last opened it"); its accessible name is unchanged.
|
|
556
|
+
- The legend (`.explico-page-marks-legend`) follows the sidebar's list
|
|
557
|
+
whenever the reader's record is kept, with a mark on screen or none. It
|
|
558
|
+
holds the two buttons (`.explico-page-marks-legend__action`, `aria-disabled`
|
|
559
|
+
while one has nothing to change) and a `role="status"` paragraph,
|
|
560
|
+
empty until a button is used. A test that finds `getByRole('status')` on
|
|
561
|
+
the whole page sees one more wherever the legend is.
|
|
562
|
+
- A compact part's chapter tile is one sheet, where it was two (its
|
|
563
|
+
`::before` has no `box-shadow`); the Overview tile keeps its two sheets.
|
|
564
|
+
Pointing at a part's `»`, or focusing it, lights the part's Overview row or
|
|
565
|
+
tile as a pointer on it would. No course change is required.
|
|
566
|
+
- The marks are reading memory (step 12): a fresh browser profile shows none
|
|
567
|
+
until the reader says yes to the course remembering their reading. After
|
|
568
|
+
the yes, every chapter but the open one is "not opened yet".
|
|
569
|
+
|
|
570
|
+
### 12. The reading-memory question: nothing to do, unless your tests or CSS need it
|
|
571
|
+
|
|
572
|
+
Every course now asks each reader, once, whether it may remember their
|
|
573
|
+
reading in this browser: a small card at the bottom of the window ("Remember
|
|
574
|
+
your reading?", "Remember" / "Don't remember"), on every page until they
|
|
575
|
+
answer. Reading memory is the chapters they opened (the sidebar marks, and
|
|
576
|
+
the new line under a changed page's title) and the explanation cards they keep
|
|
577
|
+
open or carry, across visits. Until a yes, and after a no, none of it
|
|
578
|
+
outlives the tab; kept and carried cards still work within the tab, through
|
|
579
|
+
its session storage. Settings the reader chooses (theme, text size, panel
|
|
580
|
+
layout, folded sections, the "Carry cards" switch) are kept as before. The
|
|
581
|
+
answer stands 13 months; the `[?]` panel's new "In this browser" section
|
|
582
|
+
changes it or forgets everything. The site sets no cookies, and nothing leaves
|
|
583
|
+
the browser.
|
|
584
|
+
|
|
585
|
+
To never ask, and never keep reading memory (the sidebar marks are then off,
|
|
586
|
+
and cards last as long as the tab), set it in `content/course.settings.json`:
|
|
587
|
+
|
|
588
|
+
```json
|
|
589
|
+
"readingMemory": "off"
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
and, with a settings schema that sets `"additionalProperties": false`, copy
|
|
593
|
+
the playground's `readingMemory` property into it.
|
|
594
|
+
|
|
595
|
+
Only if the course has browser tests, or CSS that reaches into the page:
|
|
596
|
+
|
|
597
|
+
- **A test that expects kept or carried cards to come back after a browser
|
|
598
|
+
restart, or in a new tab**, starts from a browser without the answer, so the
|
|
599
|
+
cards stay in the old tab. Start such specs from a yes: in
|
|
600
|
+
`playwright.config.ts`, `use.storageState` with the localStorage entry
|
|
601
|
+
`explico-reading-memory:<base path>` set to
|
|
602
|
+
`{"v":1,"remember":true,"at":"<today, YYYY-MM-DD>"}`. The playground's
|
|
603
|
+
`tests/e2e/reading-memory-state.ts` builds it (`rememberReadingState(origin,
|
|
604
|
+
base)`), and its `playwright.config.ts` uses it. A context a spec opens itself
|
|
605
|
+
with `browser.newContext()` does not inherit `use`; pass the same
|
|
606
|
+
`storageState`, or copy the first context's (`await
|
|
607
|
+
context.storageState()`).
|
|
608
|
+
- **A screenshot of a page from an empty browser** now shows the question
|
|
609
|
+
along the bottom of the window. The same `storageState` keeps it out.
|
|
610
|
+
- **A test that counts `getByRole('status')`** finds one more per copy of the
|
|
611
|
+
`[?]` panel (its "Forgotten" line, empty until used).
|
|
612
|
+
- **Course CSS**: the question is `.explico-reading-notice` (moved to the end
|
|
613
|
+
of `<body>` by its script), the panel's section `.about-browser`, the line
|
|
614
|
+
under a changed page's title `.page-changed`.
|
|
615
|
+
- **Opening a page no longer writes its sections' open/closed state** to the
|
|
616
|
+
browser; a test that read `disclosure:<path>:<section>` keys right after a
|
|
617
|
+
page loaded finds none until the reader folds or opens a section.
|
|
618
|
+
|
|
619
|
+
### 13. Optional: reuse the PDFs a build already compiled
|
|
620
|
+
|
|
621
|
+
Only for a course with `lessonExports.latex: true`. Nothing changes until
|
|
622
|
+
`EXPLICO_LATEX_CACHE` is set; then the LaTeX export compiles only the
|
|
623
|
+
documents whose folder changed (a chapter, its part and the course, for a
|
|
624
|
+
push that touches one chapter) and reuses the rest from that directory. Set
|
|
625
|
+
it in the CI job that builds, and keep the directory between runs:
|
|
626
|
+
|
|
627
|
+
```yaml
|
|
628
|
+
- uses: actions/cache@v4
|
|
629
|
+
with:
|
|
630
|
+
path: .cache/explico-latex
|
|
631
|
+
key: explico-latex-${{ github.sha }}
|
|
632
|
+
restore-keys: explico-latex-
|
|
633
|
+
- run: pnpm build
|
|
634
|
+
env:
|
|
635
|
+
EXPLICO_LATEX_CACHE: .cache/explico-latex
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Add the directory to `.gitignore`. The build's `LaTeX export:` line now says
|
|
639
|
+
how many PDFs it compiled and reused. After a `tlmgr update` that the cache
|
|
640
|
+
cannot see (a font or a package, not a TeX Live release), set
|
|
641
|
+
`EXPLICO_LATEX_CACHE_SALT` to any new value once. With step 10 a document's
|
|
642
|
+
version line and date are its own, so a content release (rule C3), too,
|
|
643
|
+
recompiles only the documents whose pages it changed, their parts and the
|
|
644
|
+
course.
|
|
645
|
+
|
|
646
|
+
### 14. Replace `scripts/content.ts` and `scripts/compile-manifest.ts` with the engine's shims
|
|
647
|
+
|
|
648
|
+
The `content` CLI and `validate:content` now live in the engine
|
|
649
|
+
(`explico/compiler/cli-main`, `explico/compiler/validate-content`), so every
|
|
650
|
+
verb reaches a course with the package instead of being ported by hand. Replace
|
|
651
|
+
both files whole:
|
|
652
|
+
|
|
653
|
+
```ts
|
|
654
|
+
// scripts/content.ts
|
|
655
|
+
import { runContentCli } from 'explico/compiler/cli-main';
|
|
656
|
+
import { courseConfig } from '../content/course.config';
|
|
657
|
+
|
|
658
|
+
process.exitCode = await runContentCli(courseConfig);
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
```ts
|
|
662
|
+
// scripts/compile-manifest.ts
|
|
663
|
+
import { runValidateContent } from 'explico/compiler/validate-content';
|
|
664
|
+
import { courseConfig } from '../content/course.config';
|
|
665
|
+
|
|
666
|
+
process.exitCode = await runValidateContent(courseConfig);
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
`package.json` keeps `"content": "tsx scripts/content.ts"` and
|
|
670
|
+
`"validate:content": "tsx scripts/compile-manifest.ts"`. `pnpm content help`
|
|
671
|
+
then lists every verb: `status`, `diagnostics`, `warnings`, `context`,
|
|
672
|
+
`equation`, `bindings`, `symbol`, `check`, `rule`, `migrate`, `rules`, `move`,
|
|
673
|
+
`rename`, `fix`, `new lesson | deck | term`.
|
|
674
|
+
|
|
675
|
+
What a course gains or changes by the swap:
|
|
676
|
+
|
|
677
|
+
- **New verbs:** `symbol` (where a symbol is already defined), `rule <ID>`
|
|
678
|
+
(one rule whole), `bindings <lesson>` and `equation "<latex>"` (what math
|
|
679
|
+
binds, before a build), `migrate` (this guide's sections between two
|
|
680
|
+
versions), and the write verbs `move`, `rename`, `fix` (a diff, applied only
|
|
681
|
+
with `--write`; `move` is how a `definition-repeated` warning, step 20, is
|
|
682
|
+
cleared in one command).
|
|
683
|
+
- **`validate:content` refreshes `content/versions.json` (rule C5, step 10)
|
|
684
|
+
and the resolution reports.** A course whose script did not call
|
|
685
|
+
`syncPageVersions` or `syncResolutionReports` (data-science-I and
|
|
686
|
+
time-series did not) starts writing both: commit `content/versions.json` with
|
|
687
|
+
the swap. The run fails only on a resolution report git tracks that it had to
|
|
688
|
+
rewrite, so a course whose `build/` is git-ignored passes on its first run.
|
|
689
|
+
- **Every diagnostic group gates.** Errors in curriculum, notation,
|
|
690
|
+
alignment, math, waivers, equations, tables, figures, diagrams, knowledge
|
|
691
|
+
checks, examples, folds and raw floats fail the run; warnings print; infos go
|
|
692
|
+
to `build/logs/`. A course script that added a check of its own keeps it by
|
|
693
|
+
running it after `runValidateContent` returns 0.
|
|
694
|
+
- **The output is the engine's.** A course test that matched the old
|
|
695
|
+
wrapper's text should match `pnpm content help` and the `--json` shapes
|
|
696
|
+
instead; the JSON reports did not change.
|
|
697
|
+
|
|
698
|
+
A course that keeps its own scripts loses only the new verbs; nothing else
|
|
699
|
+
depends on them.
|
|
700
|
+
|
|
701
|
+
Then, optionally, install the agent procedures that ship with the engine:
|
|
702
|
+
`pnpm content init-agent` shows what it would write (the `draft-lesson`,
|
|
703
|
+
`migrate-course` and `release-content` skills under `.claude/skills/`, the
|
|
704
|
+
`rules-reviewer` agent under `.claude/agents/`, and a baseline `AGENTS.md`
|
|
705
|
+
only when the course has none); `--write` writes it. An existing `AGENTS.md` is
|
|
706
|
+
never edited: `pnpm content status` says when it does not point at
|
|
707
|
+
`node_modules/explico/docs/authoring/agents.md`, and the course's file can then
|
|
708
|
+
shrink to what is particular to the course — the engine's guide covers the CLI,
|
|
709
|
+
the validate sequence and the notation rules.
|
|
710
|
+
|
|
711
|
+
### 15. `validate:content` passes on its first run; diagnostics name their rule
|
|
712
|
+
|
|
713
|
+
Nothing to do. `validate:content` still rewrites `build/resolution/*`, but it
|
|
714
|
+
now fails only on a report git tracks that it had to rewrite, so a course whose
|
|
715
|
+
`build/` is git-ignored passes on the first run; delete any "run it twice"
|
|
716
|
+
instruction from the course's own AGENTS.md. A course that commits
|
|
717
|
+
`build/resolution/` (credit-and-derivatives) keeps committing the rewritten
|
|
718
|
+
files with the change, as before — and this upgrade rewrites every one (the
|
|
719
|
+
schema version moved), so its first run fails with a `[resolution-report] …
|
|
720
|
+
out of date` line per report. Commit them; the next run passes.
|
|
721
|
+
|
|
722
|
+
Optional, in `scripts/content.ts`: print diagnostics through
|
|
723
|
+
`formatCliDiagnostic` (and `ownerTag` in the `check` case), both exported from
|
|
724
|
+
`explico/compiler/cli`, so every line names its rule and page
|
|
725
|
+
(`[N4 · notation.md#n4]`), and add `report.agentSurfaces` to the `status`
|
|
726
|
+
output. The JSON already carries `doc` and `agentSurfaces` with no wrapper
|
|
727
|
+
change; the playground's wrapper is the reference.
|
|
728
|
+
|
|
729
|
+
Also optional: make `content diagnostics` count by default. Pass the report
|
|
730
|
+
through `summarizeDiagnostics` and print `formatDiagnosticsSummary` unless
|
|
731
|
+
`--all`, `--code <code>`, `--lesson <id>` or `--changed` is given, in which
|
|
732
|
+
case `filterDiagnostics` narrows it and the lines print as before (the
|
|
733
|
+
playground's `diagnostics` case has the ten lines). On a course with hundreds
|
|
734
|
+
of diagnostics of two codes this turns a 20 000-token dump into a 300-token
|
|
735
|
+
table.
|
|
736
|
+
|
|
737
|
+
### 16. Decks: nothing to do, unless the course writes one
|
|
738
|
+
|
|
739
|
+
A lesson can now have a deck, its slide edition, in
|
|
740
|
+
`content/docs/<part>/<lesson>.deck.mdx` (`docs/authoring/deck.md`). A course
|
|
741
|
+
without one sees no change: the manifest schema is now v33, but only adds
|
|
742
|
+
`decks`, `deck` and `lessons[].editions`; `build/rules-in-effect.md` lists the
|
|
743
|
+
same rules as before, since the new area `D` is about decks only. Rules V7, V8,
|
|
744
|
+
S1, S6 and S7 now say they are about a chapter's page.
|
|
745
|
+
|
|
746
|
+
To write a deck:
|
|
747
|
+
|
|
748
|
+
1. Add the verb to the course's `scripts/content.ts`, as the playground's does:
|
|
749
|
+
`new deck` beside `new lesson` (`scaffoldDeck` from `explico/compiler/cli`),
|
|
750
|
+
and the `--edition` flag on `rules` (passed to `parseRulesFilter`).
|
|
751
|
+
2. If the course's `course.settings.schema.json` lists its properties with
|
|
752
|
+
`additionalProperties: false`, add the `deck` block (copy it from the
|
|
753
|
+
playground's schema) before setting `deck` in `course.settings.json`. The
|
|
754
|
+
defaults need no setting: a 16:9 canvas, a legend of first uses, notes shown
|
|
755
|
+
to readers, an outcomes frame and a handout PDF.
|
|
756
|
+
3. `pnpm content new deck <part>/<lesson>` drafts the deck from the chapter.
|
|
757
|
+
Rewrite it to the deck rules before committing: rule D1 (sections of frames),
|
|
758
|
+
rule D2 (a frame fits its canvas), rule D3 (a frame makes one claim), rule D4
|
|
759
|
+
(notes on a frame with text), rule D5 (a frame title fits one line), rule D6
|
|
760
|
+
(at most `maxSteps` steps), rule D7 (one picture per frame), rule D8 (recall
|
|
761
|
+
the chapter's result, never retype it), rule D9 (few new symbols per frame)
|
|
762
|
+
and rule D10 (the engine writes the title and outcomes frames). Read
|
|
763
|
+
`pnpm content rules --edition deck` before drafting.
|
|
764
|
+
4. `pnpm validate:content`, and commit `content/versions.json`: a deck beside
|
|
765
|
+
its chapter has a page version of its own (rule C5).
|
|
766
|
+
|
|
767
|
+
A course wrapper around `<Figure>`, `<Table>` or `<Diagram>` and a lab that
|
|
768
|
+
calls `labMathScope(…, { locals: Astro.locals, … })` need no change to be
|
|
769
|
+
recalled onto a deck.
|
|
770
|
+
|
|
771
|
+
In the sidebar, a deck is a play mark in a 16:9 frame, on its icon or tile and
|
|
772
|
+
on a slides-first lesson's own row and tile (`a.explico-deck-lesson`); a
|
|
773
|
+
lesson with a chapter and a deck is one row (step 17). The glyph is the link's
|
|
774
|
+
`::before` and `::after`. On a deck's page, a reader who left Present mode
|
|
775
|
+
past the first frame finds a "Continue at slide N" button
|
|
776
|
+
(`[data-deck-resume]`) beside Present. No course change is required; only
|
|
777
|
+
course CSS that styles the sidebar's links, or a browser test that counts the
|
|
778
|
+
deck page's buttons, can notice.
|
|
779
|
+
|
|
780
|
+
Once the course has a deck, its build needs a few more things:
|
|
781
|
+
|
|
782
|
+
- **A browser, in CI.** After `astro build`, every deck page is laid out in
|
|
783
|
+
the course's Playwright Chromium, and a frame that runs past its canvas fails
|
|
784
|
+
the build (`frame-overflow`, rule D2). Without Chromium the build warns and
|
|
785
|
+
measures nothing, except under `CI` or `EXPLICO_LATEX_STRICT=1`, where it
|
|
786
|
+
fails. A course whose CI already captures figures for the LaTeX export has
|
|
787
|
+
the browser. Otherwise, install it in CI before the build:
|
|
788
|
+
`pnpm exec playwright install --with-deps chromium`.
|
|
789
|
+
- **Beamer, with `lessonExports.latex`.** A deck compiles to slides and a
|
|
790
|
+
handout in beamer. A CI that installs TeX Live from
|
|
791
|
+
`node_modules/explico/src/compiler/exports/latex/texlive-packages.txt` gets
|
|
792
|
+
`beamer` and `translator` from the updated list. A CI with its own package
|
|
793
|
+
list adds those two.
|
|
794
|
+
- **Tests of the Download menu.** A chapter with a deck has one more row,
|
|
795
|
+
"Its slides", and the deck's page has "These slides" and "Its chapter". The
|
|
796
|
+
Download button's tooltip names "the slides" too. Update a test that counts
|
|
797
|
+
the rows or reads the tooltip on such a page.
|
|
798
|
+
|
|
799
|
+
### 17. A lesson is one row in the sidebar: nothing to do, unless your tests or CSS read it
|
|
800
|
+
|
|
801
|
+
A lesson with a chapter and a deck is one sidebar row: it opens the **lead**
|
|
802
|
+
edition under the lesson's title, and the other edition is an icon at the
|
|
803
|
+
row's end (`a.layout-edition-other`, named "Slides: *title*" or "Chapter:
|
|
804
|
+
*title*"), the second of two joined tiles in a compact part. A lesson with one
|
|
805
|
+
edition is a row as before. Without scripts the sidebar lists each chapter,
|
|
806
|
+
then its "Slides" row. The manifest schema is now v34: it adds
|
|
807
|
+
`sidebarEditions`, and every item of a lesson in `sidebar` carries
|
|
808
|
+
`data-edition`, `data-lesson` and `data-title` in `attrs`, so a chapter is
|
|
809
|
+
`{ slug, attrs }` where it was a bare slug.
|
|
810
|
+
|
|
811
|
+
No course change is required. The default leads with the chapter and lists
|
|
812
|
+
the slides, and each reader can switch both in the sidebar's foot; their
|
|
813
|
+
choice is a setting kept in their browser, `explico-sidebar-editions`, so a
|
|
814
|
+
course privacy notice that copies `docs/privacy.md` gains that row. A course
|
|
815
|
+
without a deck sees no change but the manifest's shape.
|
|
816
|
+
|
|
817
|
+
- **To open lessons at their slides, or to leave the other edition out of the
|
|
818
|
+
sidebar**, set `sidebarEditions` in `course.settings.json`. If the course's
|
|
819
|
+
`course.settings.schema.json` lists its properties with
|
|
820
|
+
`additionalProperties: false`, copy the `sidebarEditions` block from the
|
|
821
|
+
playground's schema first.
|
|
822
|
+
|
|
823
|
+
```json
|
|
824
|
+
"sidebarEditions": { "lead": "slides", "other": "listed" }
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
`lead` is `chapter` (default) or `slides`; `other` is `listed` (default) or
|
|
828
|
+
`hidden`. Any other value fails the build, naming the field.
|
|
829
|
+
- **A test that reads `manifest.sidebar`** and expects a chapter as a bare
|
|
830
|
+
slug reads the item's `slug`, or `sidebarLessonSlug(item)` from
|
|
831
|
+
`explico/compiler/lesson-derivation`, which gives a lesson's slug for its
|
|
832
|
+
own item and `undefined` for a slides row, an overview or a link.
|
|
833
|
+
- **A browser test** that finds a sidebar link by a lesson's title adds
|
|
834
|
+
`exact: true`: the icon beside it is named "Slides: *title*". One that
|
|
835
|
+
counts the sidebar's `li` items counts one per lesson; one that clicks the
|
|
836
|
+
"Slides" row clicks the deck's `href` or `a.layout-edition-other`. A row's
|
|
837
|
+
mark is on its `li`, as before; the icon's is on the link,
|
|
838
|
+
`a.layout-edition-other[data-page-mark]`.
|
|
839
|
+
- **Course CSS** that styles `a.explico-deck-row` in the sidebar: once the
|
|
840
|
+
script pairs the editions, the deck's link is `a.layout-edition-other` at
|
|
841
|
+
its chapter's row end, or carries no class while it leads; a compact tile
|
|
842
|
+
draws a deck's glyph from `a[data-edition="slides"]`. The marks' line no
|
|
843
|
+
longer draws the separator above it: the panel's foot,
|
|
844
|
+
`.explico-sidebar-foot`, holds the line and the two controls.
|
|
845
|
+
- **Course CSS that redraws the sidebar's chapter or deck glyph** (the
|
|
846
|
+
`::before` or `::after` of a sidebar link or a compact tile): each glyph is
|
|
847
|
+
one SVG mask over the link's own colour, the chapter a sheet with lines of
|
|
848
|
+
text (`--explico-chapter-glyph`), the deck a filled slide frame with the
|
|
849
|
+
play mark cut out (`--explico-deck-glyph`). A border on `::before` no longer
|
|
850
|
+
shapes it, and `::after` draws nothing. To draw another glyph, set the
|
|
851
|
+
custom property on `:root` to another `url("data:image/svg+xml,…")`, black
|
|
852
|
+
where the glyph is.
|
|
853
|
+
|
|
854
|
+
### 18. The slides a reader has seen: nothing to do, unless your tests or CSS read a deck's page
|
|
855
|
+
|
|
856
|
+
A deck's page keeps each frame Present mode showed the reader and shows it
|
|
857
|
+
frame by frame. A line above each frame says "Slide N" and "Shown", "Not shown
|
|
858
|
+
yet" or "Changed since shown", and ends on a button: "Continue here" on the
|
|
859
|
+
frame to continue at, "Present from here" on every other. Under the title, the
|
|
860
|
+
slide map shows every frame as a numbered square, pinned under the header
|
|
861
|
+
while the deck scrolls; a long deck's map leaves runs out, with a gap for
|
|
862
|
+
each. A line under Present counts the marks and offers "Mark all as seen" and
|
|
863
|
+
"Mark all as unseen"; the sidebar's two buttons cover every deck's frames too.
|
|
864
|
+
"Continue at slide N" presents from the frame the reader stopped at, or from
|
|
865
|
+
the one after it once they were shown its every step. Before the reader
|
|
866
|
+
answers the reading-memory question the frames shown are kept, and marked,
|
|
867
|
+
for the tab; "Remember" keeps them for later visits and "Don't remember"
|
|
868
|
+
clears them. The manifest schema is now v35: every frame of `decks[].frames`
|
|
869
|
+
carries `id`, 12 hex digits over what it shows, stamped on the frame as
|
|
870
|
+
`data-frame-id`.
|
|
871
|
+
|
|
872
|
+
No course change is required. The marks follow `sidebarMarks`, so a course
|
|
873
|
+
that turned a sidebar mark off has it off on its decks too, and one with
|
|
874
|
+
`readingMemory: "off"` shows none. A reader's record under
|
|
875
|
+
`explico:decks:<base>` moves to its new shape on its own. The reading-memory
|
|
876
|
+
question now names the slides it keeps. A course privacy notice that copies
|
|
877
|
+
`docs/privacy.md` gains that row: where each deck stopped, and the frames
|
|
878
|
+
Present mode showed, kept for the tab before an answer.
|
|
879
|
+
|
|
880
|
+
- **A browser test** that finds the deck's Present button by name adds
|
|
881
|
+
`exact: true`: every frame's line has a button named "Present from here,
|
|
882
|
+
slide N", or "Continue here, slide N".
|
|
883
|
+
|
|
884
|
+
```ts
|
|
885
|
+
await page.getByRole('button', { name: 'Present', exact: true }).click();
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
- **A browser test that reads the sidebar's status** after "Mark all as seen"
|
|
889
|
+
or "Mark all as unseen", in a course with a deck, expects "Every page and
|
|
890
|
+
slide is marked as seen." or "Every page is marked as not opened yet, and
|
|
891
|
+
every slide as not shown yet."
|
|
892
|
+
- **A browser test that runs before an answer** and expected a deck's page
|
|
893
|
+
to mark nothing: it marks the frames Present mode showed in that tab, and
|
|
894
|
+
the tab's session storage holds them under `explico:decks:<base>`.
|
|
895
|
+
- **A browser test that counts a deck page's links or buttons**: the slide
|
|
896
|
+
map (`nav.deck-map`) adds a link for each square and each gap, and each
|
|
897
|
+
frame's line (`.deck-frame__line`) a button.
|
|
898
|
+
- **A browser test that seeds `explico:decks:<base>`**: the old shape,
|
|
899
|
+
`{ "<deck slug>": 3 }`, still reads, as a deck stopped at frame 3 with no
|
|
900
|
+
frame seen. To seed frames seen, write
|
|
901
|
+
`{ "v": 2, "decks": { "<deck slug>": { "at": 3, "step": 1, "seen": { "1": "<id>" } } } }`
|
|
902
|
+
with the ids of the frames' `data-frame-id`. `step` is the step of frame
|
|
903
|
+
`at` Present mode stood on: at that frame's last step, Continue goes on to
|
|
904
|
+
the next frame; without `step`, it stays at `at`.
|
|
905
|
+
- **Course CSS** that styles a frame or the deck's actions row: a frame's
|
|
906
|
+
first child is now its line, `.deck-frame__line`, before `.deck-stage`; the
|
|
907
|
+
deck's first child is the slide map, `nav.deck-map`; "Present from here"
|
|
908
|
+
is in the line, not on the stage; the frame's number holds no mark; and the
|
|
909
|
+
actions row ends with the marks' line, `.deck-actions__marks`.
|
|
910
|
+
|
|
911
|
+
### 19. A deck for a part: nothing to do, unless the course writes one
|
|
912
|
+
|
|
913
|
+
A part can now have a deck, the lecture of the whole part:
|
|
914
|
+
`content/docs/<part>/index.deck.mdx`, beside the part's page, served at
|
|
915
|
+
`/<part>/slides/` (`docs/authoring/deck.md`, "A deck for a part"). A course
|
|
916
|
+
without one sees no change but the manifest's shape: the schema is now v36,
|
|
917
|
+
and every entry of `decks` gains `covers` (empty on a lesson's deck), while a
|
|
918
|
+
part's deck carries `part` and `recalledFrom` and no `lessonId`. The `decks`
|
|
919
|
+
invariant gains six codes, which only a part's deck or a deck's `[chapter: …]`
|
|
920
|
+
or `[morph]` marker can raise: `deck-chapter-unknown`,
|
|
921
|
+
`deck-chapter-repeated`, `deck-chapter-outside-part`, `deck-marker-misplaced`,
|
|
922
|
+
`deck-check-no-chapter` and `deck-route-taken`.
|
|
923
|
+
|
|
924
|
+
- **A course test or script that reads `manifest.decks`** and takes
|
|
925
|
+
`deck.lessonId` for granted skips or handles a part's deck: test
|
|
926
|
+
`deck.part !== undefined`.
|
|
927
|
+
|
|
928
|
+
To write a part's deck:
|
|
929
|
+
|
|
930
|
+
1. Update the course's `scripts/content.ts` usage line, as the playground's
|
|
931
|
+
does: `new deck <lesson | part>` (`scaffoldDeck` takes either), and
|
|
932
|
+
`check <lesson | deck>…` (a deck by its page, `<part>/slides`).
|
|
933
|
+
2. `pnpm content new deck <part>` drafts it from the part's chapters: a
|
|
934
|
+
`## <chapter title> [chapter: <name>]` section per chapter in the order the
|
|
935
|
+
tracks read them, a frame per section of the chapter, a `<Recall>` for each
|
|
936
|
+
float it numbers. Keep the sections that cover a chapter; a section without
|
|
937
|
+
the marker is the deck's own. Rewrite it to the deck rules, as a lesson's
|
|
938
|
+
deck (step 16).
|
|
939
|
+
- **Or merge the chapters' own decks into it**, when one lecture covers
|
|
940
|
+
several chapters: move each chapter's deck's sections into the part's
|
|
941
|
+
deck in reading order, give every one of its `##` headings the chapter's
|
|
942
|
+
marker (`## Two coordinate systems [chapter: position-and-coordinates]`:
|
|
943
|
+
a chapter may run over several sections in a row), keep one `import`
|
|
944
|
+
line, and delete the chapter's `<lesson>.deck.mdx`. The chapter's address
|
|
945
|
+
in the part's deck is its first section's divider.
|
|
946
|
+
3. A lesson's own deck wins over its part's: a chapter with a deck keeps its
|
|
947
|
+
sidebar icon and its "Slides for this chapter" link. Only a chapter the
|
|
948
|
+
part's deck covers and that has no deck of its own links into it, at its
|
|
949
|
+
first section's divider, and offers the part's deck as "Its slides" in its
|
|
950
|
+
Download menu. A chapter's deck merged into its part's (above) is gone from
|
|
951
|
+
the sidebar, from its chapter's title line and from `downloads/`: a link
|
|
952
|
+
to `/<part>/<lesson>/slides/` breaks, so point it at the part's deck.
|
|
953
|
+
4. `pnpm validate:content` and `pnpm content check <part>/slides`, then commit
|
|
954
|
+
`content/versions.json`: the part's deck has a page version of its own
|
|
955
|
+
(rule C5).
|
|
956
|
+
|
|
957
|
+
- **A browser test that counts a part's sidebar items or compact tiles** sees
|
|
958
|
+
the part's deck in its Overview's row (`a.explico-part-deck`, step 22), and
|
|
959
|
+
without scripts one more item, right after the Overview; one that lists
|
|
960
|
+
`downloads/` sees the part's deck's files, `<part>-slides.html`, `.pdf`,
|
|
961
|
+
`-notes.pdf`, `-latex.zip` and `-markdown.zip`.
|
|
962
|
+
|
|
963
|
+
### 20. Rule N4's lints: delete acknowledgements, move the repeated local entries
|
|
964
|
+
|
|
965
|
+
`validate:content` now reports these, all owned by rule N4:
|
|
966
|
+
|
|
967
|
+
- `glyph-unique-in-corpus` fires only when two **shared** cards claim one
|
|
968
|
+
glyph. A `notation.local` entry or a gloss that reuses a letter is the
|
|
969
|
+
declared exception and is no longer reported, so every
|
|
970
|
+
`glyph-unique-in-corpus:<glyph> — …` line in `lint-ignore.yml` that
|
|
971
|
+
acknowledged a page-local reuse can be deleted (they are harmless if kept).
|
|
972
|
+
It can also report a pair 0.13.1 did not: two shared cards on one glyph
|
|
973
|
+
where one card is named only in the other's `formula:` (data-science-I's
|
|
974
|
+
`\pi`, a prior density and the circle constant). Give one a different glyph,
|
|
975
|
+
or acknowledge the pair.
|
|
976
|
+
- `definition-repeated` (warning): one `notation.local` key declared in two
|
|
977
|
+
or more chapters (a deck beside its chapter never counts). Run
|
|
978
|
+
`pnpm content diagnostics warning --code definition-repeated`; each line
|
|
979
|
+
says whether the copies agree. Move one copy to `content/notation/<key>.md`
|
|
980
|
+
(`pnpm content new term <key>` scaffolds it; paste the block's fields) and
|
|
981
|
+
delete the other blocks: every `[[key]]` and `\explain{key}` keeps working.
|
|
982
|
+
Where the copies differ, keep the wording you want, or give the second
|
|
983
|
+
meaning its own key. A course with many such keys can clear them one lesson
|
|
984
|
+
at a time.
|
|
985
|
+
- `meaning-repeated` (info): one symbol and name defined at two sites in any
|
|
986
|
+
tier. Promote it to a shared card, or bind the shared card the message names
|
|
987
|
+
instead of redeclaring it.
|
|
988
|
+
- `explain-redundant` (info): an `\explain{key}{…}` in page math whose bare
|
|
989
|
+
symbol already resolves to the same card. Write the symbol bare; the wrap
|
|
990
|
+
is for a compound or a colliding glyph. Optional to clear.
|
|
991
|
+
- `gloss-wants-promoting` is gone; delete any `lint-ignore.yml` line for it.
|
|
992
|
+
|
|
993
|
+
Nothing reports a `lint-ignore.yml` line that no longer matches: search the
|
|
994
|
+
file for `glyph-unique-in-corpus` and `gloss-wants-promoting` yourself. A
|
|
995
|
+
course test that pins the list of consistency codes, or exercises
|
|
996
|
+
`gloss-wants-promoting`, needs the new list. And reread the course's own
|
|
997
|
+
notation guidance (its AGENTS.md, a style guide): advice such as "gloss
|
|
998
|
+
first", or a local entry as the default for a new symbol, now contradicts N4 —
|
|
999
|
+
one shared card per glyph, a local entry or a gloss only for a reused letter.
|
|
1000
|
+
|
|
1001
|
+
### 21. The glossary: nothing to do, unless your CSS or tests read it
|
|
1002
|
+
|
|
1003
|
+
The glossary page gains a section "On one page only" (`.glossary-page-meanings`)
|
|
1004
|
+
under its index, listing every `notation.local` entry with a link to its lesson
|
|
1005
|
+
and every gloss with a link to its card, and a formula's "Symbols" list
|
|
1006
|
+
(`.notation-gloss-list`) now holds the cards the formula names as well as its
|
|
1007
|
+
glosses, in formula order, a card's name linked (`a.notation-gloss-name`) and
|
|
1008
|
+
its row marked `data-notation-card`. A course that styles or asserts on either
|
|
1009
|
+
list adapts the selector; nothing else changes.
|
|
1010
|
+
|
|
1011
|
+
### 22. A part's deck shares the Overview's row: nothing to do, unless your tests or CSS read it
|
|
1012
|
+
|
|
1013
|
+
A part's deck is its Overview's other edition, as a lesson's deck is its
|
|
1014
|
+
chapter's (step 17): one row, the part's, that follows "Lessons open as" and
|
|
1015
|
+
"List the other edition too". With chapters leading the row is "Overview"
|
|
1016
|
+
and the deck is the icon at its end, `a.layout-edition-other`, named "Slides
|
|
1017
|
+
for this part: *part*"; with slides leading the row is "Slides for this
|
|
1018
|
+
part" and Overview is the icon, named "Overview: *part*". Unlisted, the other
|
|
1019
|
+
link is absent, unless it is the open page. In a compact part the two are
|
|
1020
|
+
one `li[data-editions="pair"]`. A part with no Overview keeps its deck as a
|
|
1021
|
+
row of its own. Without scripts the sidebar is as before: the Overview, then
|
|
1022
|
+
the deck's "Slides" row. A course with no part's deck sees no change.
|
|
1023
|
+
|
|
1024
|
+
No course change is required. What moved, for a course whose tests or CSS
|
|
1025
|
+
read it:
|
|
1026
|
+
|
|
1027
|
+
- **The deck's link** can be `a.layout-edition-other` inside the Overview's
|
|
1028
|
+
`li[data-editions="pair"]`, or absent, and the script takes
|
|
1029
|
+
`explico-deck-row` off it (it keeps `explico-part-deck`). A browser test
|
|
1030
|
+
that found its row by `li:has(> a[href="/<part>/slides/"])` finds the part's
|
|
1031
|
+
row by the Overview's `href`, and its mark on the link,
|
|
1032
|
+
`a.explico-part-deck[data-page-mark]`. One that finds the part page's
|
|
1033
|
+
"Slides for this part" link by name adds `exact: true`: the sidebar's icon
|
|
1034
|
+
is named "Slides for this part: *part*".
|
|
1035
|
+
- **New attributes** in `manifest.sidebar` and on the links:
|
|
1036
|
+
`data-part-edition` (`overview` or `slides`) and `data-part` on both,
|
|
1037
|
+
`data-title`, `data-chapters` and `data-frames` on the deck's,
|
|
1038
|
+
`data-deck-range` on each chapter that links into its section. A test that
|
|
1039
|
+
compares those items whole adds them.
|
|
1040
|
+
- **A tooltip's second line**: the part's deck and a chapter's section carry
|
|
1041
|
+
an `aria-description` ("Chapters 1–2 · 26 slides", "Slides 17–25 of the
|
|
1042
|
+
part's slides"), drawn as `small.layout-edition-detail` inside the link's
|
|
1043
|
+
`<span>`. A test that reads a tooltip's whole text (`toHaveText`) reads its
|
|
1044
|
+
accessible name instead, or expects the second line after it.
|
|
1045
|
+
- **The lit section**: with the part's deck open, the section link of the
|
|
1046
|
+
chapter on screen carries `aria-current="location"`, and the rail
|
|
1047
|
+
(`li[data-covered]`) runs to its row. The part deck's frames carry
|
|
1048
|
+
`data-chapter`, and `<html>` carries `data-deck-chapter`.
|
|
1049
|
+
- **Course CSS that redraws the part deck's glyph** sets
|
|
1050
|
+
`--explico-part-deck-glyph` on `:root`, as the other glyphs (step 17).
|
|
1051
|
+
|
|
1052
|
+
### 23. The contents list shows each lesson's slides: nothing to do, unless your tests or CSS read it
|
|
1053
|
+
|
|
1054
|
+
`<CourseContents>` and `<PartOverview>` draw a slides column at each row's
|
|
1055
|
+
end, a glyph before each title and, on the contents list, a deck count and a
|
|
1056
|
+
key, when the course has a deck. **A course with no deck sees no change.**
|
|
1057
|
+
|
|
1058
|
+
No course change is required. What moved, for a course with a deck:
|
|
1059
|
+
|
|
1060
|
+
- **Each row gains links.** A deck's cell is an `a.deck-lane` inside the
|
|
1061
|
+
row's `li`, after the title's link, so a browser test that counts or reads
|
|
1062
|
+
`li > a` (`.course-contents li > a`, `.part-chapters > li > a`) narrows it
|
|
1063
|
+
to the titles with `li > a:not(.deck-lane)`. A test that finds a title's
|
|
1064
|
+
link by its name is unaffected: the glyph is an `aria-hidden` span with no
|
|
1065
|
+
text.
|
|
1066
|
+
- **To keep the old lists**, pass `slides={false}`:
|
|
1067
|
+
`<CourseContents slides={false} />` and `<PartOverview slides={false} />`.
|
|
1068
|
+
- **Course CSS that redraws the glyphs** sets `--explico-chapter-glyph`,
|
|
1069
|
+
`--explico-deck-glyph`, `--explico-part-deck-glyph` and the new
|
|
1070
|
+
`--explico-part-glyph` on `:root`, as in steps 17 and 22.
|
|
1071
|
+
|
|
1072
|
+
### 24. Recommended: give missing paths a course page
|
|
1073
|
+
|
|
1074
|
+
Starlight asks Astro for the docs entry `404` during every build. Its fallback
|
|
1075
|
+
works, but Astro warns `Entry docs → 404 was not found`. Add
|
|
1076
|
+
`content/docs/404.mdx`; adapt the title and tagline to the course:
|
|
1077
|
+
|
|
1078
|
+
```mdx
|
|
1079
|
+
---
|
|
1080
|
+
title: "404: Page outside the syllabus"
|
|
1081
|
+
description: The requested course page could not be found.
|
|
1082
|
+
template: splash
|
|
1083
|
+
hero:
|
|
1084
|
+
title: "404"
|
|
1085
|
+
tagline: We followed this URL as far as the course map goes. It does not land on a page.
|
|
1086
|
+
pagefind: false
|
|
1087
|
+
sidebar:
|
|
1088
|
+
hidden: true
|
|
1089
|
+
editUrl: false
|
|
1090
|
+
---
|
|
1091
|
+
|
|
1092
|
+
import { LinkButton } from '@astrojs/starlight/components';
|
|
1093
|
+
|
|
1094
|
+
<LinkButton href={import.meta.env.BASE_URL} icon="left-arrow" iconPlacement="start">
|
|
1095
|
+
Return to the course
|
|
1096
|
+
</LinkButton>
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
`import.meta.env.BASE_URL` points home from the originally requested URL and
|
|
1100
|
+
survives a subpath deployment. Let the docs entry own
|
|
1101
|
+
`/404.html` rather than colliding with Starlight's injected route:
|
|
1102
|
+
|
|
1103
|
+
```js
|
|
1104
|
+
starlight({
|
|
1105
|
+
disable404Route: process.env.NODE_ENV === 'production',
|
|
1106
|
+
// …
|
|
1107
|
+
})
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
The special route catches arbitrary missing URLs in development. The authored
|
|
1111
|
+
docs entry owns `/404.html` in a static build, without a duplicate route.
|
|
1112
|
+
|
|
1113
|
+
Also give Astro the published
|
|
1114
|
+
origin so Starlight can build its sitemap without its `site` warning; leave the
|
|
1115
|
+
subpath in `base`:
|
|
1116
|
+
|
|
1117
|
+
```js
|
|
1118
|
+
export default defineConfig({
|
|
1119
|
+
site: 'https://example.edu',
|
|
1120
|
+
base: '/course/',
|
|
1121
|
+
// …
|
|
1122
|
+
});
|
|
1123
|
+
```
|
|
1124
|
+
|
|
1125
|
+
No placeholder `content/i18n/en.json` is needed. The `i18n` collection exported
|
|
1126
|
+
by `explico/content-config` supplies an empty English entry only when the course
|
|
1127
|
+
has no authored translations; real files under `content/i18n/` replace it.
|
|
1128
|
+
|
|
1129
|
+
### 25. The page's zones and slide edges: nothing to do, unless your CSS or screenshots read them
|
|
1130
|
+
|
|
1131
|
+
Every page now tells its parts apart by colour: both sidebars take one step
|
|
1132
|
+
of a neutral grey and the header a step further (`appearance.zones:
|
|
1133
|
+
"tonal"`), and each slide on a deck's page has a visible edge and a drop
|
|
1134
|
+
shadow, a step lighter than the page in the dark theme (`appearance.slides:
|
|
1135
|
+
"lifted"`). A hovered row in either sidebar now fills in the dark theme too
|
|
1136
|
+
(it had no fill there), and the part holding the open page is tinted a step
|
|
1137
|
+
darker than before, so it stays visible on the grey. The manifest schema is
|
|
1138
|
+
now v37, with `appearance`.
|
|
1139
|
+
|
|
1140
|
+
No course change is required, except in a browser test that compares a
|
|
1141
|
+
sidebar's background with the page's: they now differ by a step of grey (in
|
|
1142
|
+
the dark theme `rgb(32, 35, 42)` against `rgb(23, 24, 28)`), so the test reads
|
|
1143
|
+
the zone's own colour or the course sets `"zones": "none"`. To choose another
|
|
1144
|
+
look, add the block to `content/course.settings.json`:
|
|
1145
|
+
|
|
1146
|
+
```json
|
|
1147
|
+
"appearance": { "zones": "paper", "slides": "edge" }
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
`zones`: `tonal` (default), `accent-header` (the header a deep band of the
|
|
1151
|
+
course accent; on it, a course with a mark for each theme shows its
|
|
1152
|
+
dark-theme mark), `paper` (header and sidebars a desk, the reading column a
|
|
1153
|
+
sheet on it) or `none` (Starlight's colours, as before). `slides`: `lifted`
|
|
1154
|
+
(default), `edge` (edge and shadow on the page's colour) or `sorter` (the
|
|
1155
|
+
deck on a tinted well). When the course keeps a settings schema with
|
|
1156
|
+
`"additionalProperties": false`, copy the playground's `appearance` property
|
|
1157
|
+
into it, or the settings form flags the block.
|
|
1158
|
+
|
|
1159
|
+
Only if a course's CSS or browser tests read these:
|
|
1160
|
+
|
|
1161
|
+
- **Screenshot tests** of any page see the new header and sidebar colours,
|
|
1162
|
+
and of a deck's page the new slide edges: refresh their baselines, or set
|
|
1163
|
+
`"zones": "none"` to keep the old frame.
|
|
1164
|
+
- **Course CSS that set `--sl-color-bg-sidebar` or the header's background**
|
|
1165
|
+
now competes with `src/styles/zones.css`, which sets them from the
|
|
1166
|
+
`--ex-zone-*` tokens. Set the tokens instead (`--ex-zone-header`,
|
|
1167
|
+
`--ex-zone-rail`, `--ex-zone-toc`, their `-edge`s), on `:root` and
|
|
1168
|
+
`:root[data-theme='light']`.
|
|
1169
|
+
- **Course CSS for a slide's border or shadow** sets `--deck-stage-edge`
|
|
1170
|
+
and `--deck-stage-shadow`.
|
|
1171
|
+
|
|
1172
|
+
### 26. Verify
|
|
1173
|
+
|
|
1174
|
+
```bash
|
|
1175
|
+
pnpm validate:content # passes on its first run; a tracked report it rewrote is the one failure
|
|
1176
|
+
pnpm content check --changed
|
|
1177
|
+
astro check
|
|
1178
|
+
astro build
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
`astro build` prints no deprecation line for the markdown plugin lists after
|
|
1182
|
+
step 2 and none of the missing-404, empty-`i18n`, or sitemap notices after step
|
|
1183
|
+
24. `validate:content` writes `content/versions.json` (step 10) on its
|
|
1184
|
+
first run and passes; commit the file. With step 13, a second `astro build` reports every
|
|
1185
|
+
PDF as reused.
|
|
1186
|
+
|
|
36
1187
|
## 0.13.0 → 0.13.1
|
|
37
1188
|
|
|
38
1189
|
**No course changes required.** This release has one fix, in the engine's
|
|
39
1190
|
stylesheet: hovering a symbol in an equation, or in a card's formula, no longer
|
|
40
1191
|
paints its highlight over the glyph to its left. No authoring rule,
|
|
41
1192
|
`CourseConfig` field, Astro wiring, course script or CLI command changes. The
|
|
42
|
-
manifest schema stays
|
|
1193
|
+
manifest schema stays v31.
|
|
43
1194
|
|
|
44
1195
|
### 1. Update the dependency
|
|
45
1196
|
|
|
46
1197
|
```bash
|
|
47
|
-
pnpm add
|
|
1198
|
+
pnpm add explico@0.13.1
|
|
48
1199
|
```
|
|
49
1200
|
|
|
50
1201
|
**Coming from 0.12.0 or earlier:** apply "0.12.0 → 0.13.0" below first, and
|
|
@@ -561,7 +1712,23 @@ it (a MAJOR bump under rule C2, step 20).
|
|
|
561
1712
|
- **`resolveMathGlyphs`** returns `indices` and `derived` and takes a
|
|
562
1713
|
`derive` option. A course test that compares its whole result with
|
|
563
1714
|
`toEqual` sees an unresolved item carry `lenders` and a decoration carry
|
|
564
|
-
`wholeFormExplicit`.
|
|
1715
|
+
`wholeFormExplicit`. A bare symbol that two entries share only through
|
|
1716
|
+
forms with a fixed label no longer throws `GlyphResolutionError`
|
|
1717
|
+
("is the base glyph of more than one page symbol"): with the entries `P_0`
|
|
1718
|
+
and `P(y)`, `P` resolves to the card of `P(y)`. A test that expected the
|
|
1719
|
+
throw moves.
|
|
1720
|
+
- **A display equation is a `$$` on lines of its own.** The helpers that read
|
|
1721
|
+
a lesson's text (`scanEquationLabels`, `scanDisplayEquations`,
|
|
1722
|
+
`countDisplayEquations`, `validateEquations`; `explico/reference/equations.mjs`
|
|
1723
|
+
and `…/equations-validate`) follow remark-math. `$$ a=b \label{eq:x} $$` on
|
|
1724
|
+
one line is inline math there: 0.12.0 counted it, this release returns
|
|
1725
|
+
nothing for it. A course test whose fixture writes a display equation on one
|
|
1726
|
+
line gets empty results; put `$$` on a line, the equation on the next, and
|
|
1727
|
+
`$$` on a third, as `docs/authoring/structure.md` writes it.
|
|
1728
|
+
- **A test that compiles the whole course** with `compileManifest` renders
|
|
1729
|
+
every lesson's math, so on a large course it can pass vitest's 5 s default
|
|
1730
|
+
timeout. Give it an explicit one: `it('…', { timeout: 30_000 }, async () => {
|
|
1731
|
+
… })`.
|
|
565
1732
|
- **The `content` CLI's JSON** loses `draftDebt` and `lesson.status` (step
|
|
566
1733
|
3). Its lists now include the last build's render findings (from
|
|
567
1734
|
`build/render-diagnostics.json`) under a `render:` source, such as
|
|
@@ -618,8 +1785,11 @@ of its width in the article, and diagram boxes are measured (step 13).
|
|
|
618
1785
|
Rule V7 holds a `<Figure>`'s picture (plot, key and tables) to 80% of the
|
|
619
1786
|
viewport height, or `rules.V7.maxPercent`, so the reader sees it with its
|
|
620
1787
|
caption line. Only a laid-out page knows the height, so the check runs in
|
|
621
|
-
the course's Playwright suite.
|
|
622
|
-
|
|
1788
|
+
the course's Playwright suite. **Skip this step if no lesson has a `<Figure>`**
|
|
1789
|
+
(a lab that is not inside one is not checked): `lessonsWithFigures(manifest)` is
|
|
1790
|
+
then empty and the file defines no test, so running it alone, as
|
|
1791
|
+
`playwright test tests/e2e/figure-fit.spec.ts` does, fails with "No tests
|
|
1792
|
+
found". Otherwise add it there, at each viewport the course supports:
|
|
623
1793
|
|
|
624
1794
|
```ts
|
|
625
1795
|
import { readFileSync } from 'node:fs';
|
|
@@ -759,7 +1929,7 @@ or removed. The manifest schema is now v25 (step 8).
|
|
|
759
1929
|
### 1. Update the dependency
|
|
760
1930
|
|
|
761
1931
|
```bash
|
|
762
|
-
pnpm add
|
|
1932
|
+
pnpm add explico@0.12.0
|
|
763
1933
|
```
|
|
764
1934
|
|
|
765
1935
|
### 2. A lab never calls KaTeX itself (rule V3, rule N4)
|
|
@@ -931,6 +2101,15 @@ helpers directly:
|
|
|
931
2101
|
a lesson id or a `manifest.pages[].id`. Its errors, and the local-notation
|
|
932
2102
|
parity error, are reworded ("on page …", "notation mismatch"). Update a
|
|
933
2103
|
test that matched the old text.
|
|
2104
|
+
- `remarkNotation` over a page that is not a chapter binds its math (step 5)
|
|
2105
|
+
instead of leaving it alone. A test that fed it `src/content/docs/about.md`
|
|
2106
|
+
and expected the text back unchanged now gets `Unresolved notation in page
|
|
2107
|
+
math: … (rule S10)` for each symbol with no shared entry. Give the fixture a
|
|
2108
|
+
shared entry for the symbol, or remove the math from it.
|
|
2109
|
+
- A page's own `notation.local` entries seed its bundle (step 7), so
|
|
2110
|
+
`registry.bundles[n].definitionIds` and `manifest.notation.bundles` now list
|
|
2111
|
+
them as `page:<lessonId>:<key>` next to the shared ids. A test that compares
|
|
2112
|
+
a bundle's ids with `toEqual` adds those.
|
|
934
2113
|
- `.lab-math` no longer scrolls or pads; its `.katex-display` does. A
|
|
935
2114
|
float's caption line is at least `1.5rem` tall while it shows a tool
|
|
936
2115
|
button.
|
|
@@ -967,7 +2146,7 @@ v24.
|
|
|
967
2146
|
### 1. Update the dependency
|
|
968
2147
|
|
|
969
2148
|
```bash
|
|
970
|
-
pnpm add
|
|
2149
|
+
pnpm add explico@0.11.1
|
|
971
2150
|
```
|
|
972
2151
|
|
|
973
2152
|
**Coming from 0.8.0:** follow the "Coming from 0.8.0" note of "0.10.0 →
|
|
@@ -1050,7 +2229,7 @@ the check.
|
|
|
1050
2229
|
### 1. Update the dependency
|
|
1051
2230
|
|
|
1052
2231
|
```bash
|
|
1053
|
-
pnpm add
|
|
2232
|
+
pnpm add explico@0.11.0
|
|
1054
2233
|
```
|
|
1055
2234
|
|
|
1056
2235
|
### 2. Delete `disclosure.diagrams` (the build fails until you do)
|
|
@@ -1425,7 +2604,7 @@ seven bumps (step 6).
|
|
|
1425
2604
|
### 1. Update the dependency
|
|
1426
2605
|
|
|
1427
2606
|
```bash
|
|
1428
|
-
pnpm add
|
|
2607
|
+
pnpm add explico@0.10.0
|
|
1429
2608
|
```
|
|
1430
2609
|
|
|
1431
2610
|
0.10.0 was never published to npm. Coming from npm's 0.8.0, install 0.11.0
|
|
@@ -2019,7 +3198,7 @@ schema version is unchanged.
|
|
|
2019
3198
|
### 1. Update the dependency
|
|
2020
3199
|
|
|
2021
3200
|
```bash
|
|
2022
|
-
pnpm add
|
|
3201
|
+
pnpm add explico@0.9.0
|
|
2023
3202
|
```
|
|
2024
3203
|
|
|
2025
3204
|
0.9.0 was never published to npm. Coming from npm's 0.8.0, install 0.11.0
|
|
@@ -2323,7 +3502,7 @@ previously worked around may now be written directly.
|
|
|
2323
3502
|
### 10. Verify
|
|
2324
3503
|
|
|
2325
3504
|
```
|
|
2326
|
-
pnpm add
|
|
3505
|
+
pnpm add explico@0.8.0
|
|
2327
3506
|
pnpm validate:content # twice — the first run regenerates build/resolution/*
|
|
2328
3507
|
pnpm content check --changed
|
|
2329
3508
|
pnpm content warnings # expect new delimiter-manual-size findings (item 3)
|