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
|
@@ -26,19 +26,92 @@
|
|
|
26
26
|
|
|
27
27
|
import { citedIds, withoutLinkCitations } from '../reference/citation-format.mjs';
|
|
28
28
|
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* `foundations/discount-factors` → `foundations.discount-factors`. A deck
|
|
31
|
+
* file's slug (`foundations/discount-factors.deck.mdx`) gives its lesson's id;
|
|
32
|
+
* a deck's route (`foundations/discount-factors/slides`) gives the deck's page
|
|
33
|
+
* id, `foundations.discount-factors.slides` ({@link deckPageId}).
|
|
34
|
+
*/
|
|
30
35
|
export function lessonIdFromSlug(slug: string): string {
|
|
31
|
-
return slug.replace(/\.(md|mdx)$/, '').replace(/\//g, '.');
|
|
36
|
+
return slug.replace(/\.(md|mdx)$/, '').replace(/\.deck$/, '').replace(/\//g, '.');
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
|
-
* A docs entry is a
|
|
40
|
+
* A docs entry is a chapter when it lives in a section directory. The three
|
|
36
41
|
* top-level pages (`index`, `curriculum-map`, `glossary`) are not lessons; a
|
|
37
|
-
* future `<section>/index` page would not be one either
|
|
42
|
+
* future `<section>/index` page would not be one either, and neither is a
|
|
43
|
+
* lesson's deck file (`<lesson>.deck.mdx`, {@link isDeckSlug}), which is an
|
|
44
|
+
* edition of a lesson rather than a lesson.
|
|
38
45
|
*/
|
|
39
46
|
export function isLessonSlug(slug: string): boolean {
|
|
40
47
|
const clean = slug.replace(/\.(md|mdx)$/, '');
|
|
41
|
-
return clean.includes('/') && !clean.endsWith('/index');
|
|
48
|
+
return clean.includes('/') && !clean.endsWith('/index') && !isDeckSlug(clean);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The last segment of a deck's route when the deck stands beside its chapter:
|
|
53
|
+
* `projectiles/projectile-range` is the chapter, `projectiles/projectile-range/slides`
|
|
54
|
+
* the deck.
|
|
55
|
+
*/
|
|
56
|
+
export const DECK_ROUTE_SEGMENT = 'slides';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A deck file — a lesson's slide edition, `content/docs/<part>/<lesson>.deck.mdx`
|
|
60
|
+
* beside the chapter `<lesson>.mdx` or alone for a lesson that is only slides,
|
|
61
|
+
* or a part's deck ({@link isPartDeckSlug}). True for the docs slug with or
|
|
62
|
+
* without its extension; a caller that needs the lesson asks
|
|
63
|
+
* {@link isPartDeckSlug} first.
|
|
64
|
+
*/
|
|
65
|
+
export function isDeckSlug(slug: string): boolean {
|
|
66
|
+
return /\.deck(?:\.mdx?)?$/.test(slug);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The lesson a lesson's deck file belongs to: `motion/range.deck.mdx` → `motion/range`. */
|
|
70
|
+
export function deckLessonSlug(slug: string): string {
|
|
71
|
+
return slug.replace(/\.(md|mdx)$/, '').replace(/\.deck$/, '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A part's deck, `content/docs/<part>/index.deck.mdx` beside the part's page
|
|
76
|
+
* `index.mdx`: the slides of the part, served at `/<part>/slides/`
|
|
77
|
+
* ({@link partDeckRouteSlug}), whose `##` sections may each name the chapter
|
|
78
|
+
* they cover (`docs/authoring/deck.md`). It is no lesson's edition.
|
|
79
|
+
*/
|
|
80
|
+
export function isPartDeckSlug(slug: string): boolean {
|
|
81
|
+
return /^[^/]+\/index\.deck(?:\.mdx?)?$/.test(slug);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The part a part's deck belongs to: `motion/index.deck.mdx` → `motion`. */
|
|
85
|
+
export function partDeckDir(slug: string): string {
|
|
86
|
+
return slug.replace(/\/index\.deck(?:\.mdx?)?$/, '');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Where a part's deck is served: `<part>/slides`, as a chapter's deck is `<lesson>/slides`. */
|
|
90
|
+
export function partDeckRouteSlug(partDir: string): string {
|
|
91
|
+
return `${partDir}/${DECK_ROUTE_SEGMENT}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The page id of a part's deck, the id of its route: `<part>.slides`. */
|
|
95
|
+
export function partDeckPageId(partDir: string): string {
|
|
96
|
+
return `${partDir}.${DECK_ROUTE_SEGMENT}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Where a lesson's deck is served: `<lesson>/slides` beside its chapter, or
|
|
101
|
+
* the lesson's own route when the lesson has no chapter (a slides-first
|
|
102
|
+
* lesson, whose deck is its only edition).
|
|
103
|
+
*/
|
|
104
|
+
export function deckRouteSlug(lessonSlug: string, hasChapter: boolean): string {
|
|
105
|
+
return hasChapter ? `${lessonSlug}/${DECK_ROUTE_SEGMENT}` : lessonSlug;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The page id a deck's notation bundle, gates and diagnostics use — the id of
|
|
110
|
+
* its route ({@link lessonIdFromSlug}): `<lessonId>.slides` beside a chapter,
|
|
111
|
+
* the lesson's own id for a slides-first lesson.
|
|
112
|
+
*/
|
|
113
|
+
export function deckPageId(lessonId: string, hasChapter: boolean): string {
|
|
114
|
+
return hasChapter ? `${lessonId}.${DECK_ROUTE_SEGMENT}` : lessonId;
|
|
42
115
|
}
|
|
43
116
|
|
|
44
117
|
/**
|
|
@@ -153,7 +226,8 @@ export interface SidebarSection {
|
|
|
153
226
|
/**
|
|
154
227
|
* One sidebar entry: a lesson slug, or an explicit link. `attrs` is Starlight's
|
|
155
228
|
* own passthrough onto the rendered `<a>` — the engine uses it to mark a part's
|
|
156
|
-
* overview row so CSS can give it its own icon
|
|
229
|
+
* overview row so CSS can give it its own icon, and to name each edition of a
|
|
230
|
+
* lesson for the sidebar's script ({@link buildSidebar}).
|
|
157
231
|
*/
|
|
158
232
|
export type SidebarItem =
|
|
159
233
|
| string
|
|
@@ -161,6 +235,15 @@ export type SidebarItem =
|
|
|
161
235
|
readonly label: string;
|
|
162
236
|
readonly link: string;
|
|
163
237
|
readonly attrs?: Readonly<Record<string, string>>;
|
|
238
|
+
}
|
|
239
|
+
| {
|
|
240
|
+
/**
|
|
241
|
+
* A docs entry by its id (Starlight's `slug` item), with its own label,
|
|
242
|
+
* or the entry's title without one.
|
|
243
|
+
*/
|
|
244
|
+
readonly slug: string;
|
|
245
|
+
readonly label?: string;
|
|
246
|
+
readonly attrs?: Readonly<Record<string, string>>;
|
|
164
247
|
};
|
|
165
248
|
|
|
166
249
|
export interface SidebarGroup {
|
|
@@ -171,49 +254,190 @@ export interface SidebarGroup {
|
|
|
171
254
|
/** Marks the overview row so `global.css` can give it its own icon. */
|
|
172
255
|
export const PART_OVERVIEW_CLASS = 'explico-part-overview';
|
|
173
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Marks a deck's row, listed under its chapter, so `deck.css` can set it
|
|
259
|
+
* apart. That is the sidebar without its script; with it, the deck is its
|
|
260
|
+
* chapter row's trailing icon, or leads the row (`sidebar-editions.ts`).
|
|
261
|
+
*/
|
|
262
|
+
export const DECK_ROW_CLASS = 'explico-deck-row';
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Marks a slides-first lesson's row, whose deck is the lesson, so `deck.css`
|
|
266
|
+
* gives it the deck's glyph where a chapter has none (a row) or a sheet (a
|
|
267
|
+
* compact tile). It is a lesson of the reading order, unlike {@link DECK_ROW_CLASS}.
|
|
268
|
+
*/
|
|
269
|
+
export const DECK_LESSON_CLASS = 'explico-deck-lesson';
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Marks a part's deck, listed right after the part's Overview (with
|
|
273
|
+
* {@link DECK_ROW_CLASS}, so Next and Previous step past it as past any
|
|
274
|
+
* deck): the part's own, as Overview is, with the part's deck glyph. The
|
|
275
|
+
* sidebar's script makes the two one row, the part's, as a lesson's chapter
|
|
276
|
+
* and deck are one (`sidebar-editions.ts`).
|
|
277
|
+
*/
|
|
278
|
+
export const PART_DECK_CLASS = 'explico-part-deck';
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Numbers as a reader reads a run of them: `1–2`, `1, 3`, `2–4, 6`. A run of
|
|
282
|
+
* two or more is its ends with an en dash.
|
|
283
|
+
*/
|
|
284
|
+
export function numberList(numbers: readonly number[]): string {
|
|
285
|
+
const sorted = [...new Set(numbers)].sort((left, right) => left - right);
|
|
286
|
+
const runs: string[] = [];
|
|
287
|
+
for (let start = 0; start < sorted.length; ) {
|
|
288
|
+
let end = start;
|
|
289
|
+
while (end + 1 < sorted.length && sorted[end + 1] === sorted[end]! + 1) end += 1;
|
|
290
|
+
runs.push(end === start ? String(sorted[start]) : `${sorted[start]}–${sorted[end]}`);
|
|
291
|
+
start = end + 1;
|
|
292
|
+
}
|
|
293
|
+
return runs.join(', ');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The lesson a sidebar item lists, by its slug: an item served at its
|
|
298
|
+
* lesson's own URL (its `data-lesson` is its `slug`), a chapter or a
|
|
299
|
+
* slides-first lesson's deck. A part's overview, a chapter's slides row
|
|
300
|
+
* (served under the chapter) and a link list no lesson. A bare slug is a
|
|
301
|
+
* lesson too.
|
|
302
|
+
*/
|
|
303
|
+
export function sidebarLessonSlug(item: SidebarItem): string | undefined {
|
|
304
|
+
if (typeof item === 'string') return item;
|
|
305
|
+
if (!('slug' in item)) return undefined;
|
|
306
|
+
return item.attrs?.['data-lesson'] === item.slug ? item.slug : undefined;
|
|
307
|
+
}
|
|
308
|
+
|
|
174
309
|
/**
|
|
175
310
|
* The Starlight `sidebar` config: one group per section, lessons ordered by the
|
|
176
311
|
* merged track order (ties broken by slug), then the static Reference group.
|
|
177
312
|
* Replaces seven `autogenerate` blocks that read an authored `sidebar.order`.
|
|
178
313
|
* `floatIndex`: the course has a top-level "Figures and tables" page
|
|
179
314
|
* (`figures-and-tables.mdx`, `FloatIndex.astro`), listed in Reference.
|
|
315
|
+
*
|
|
316
|
+
* Each edition of a lesson is an item of its own, its chapter first, and
|
|
317
|
+
* names its edition, lesson and title (`data-edition`, `data-lesson`,
|
|
318
|
+
* `data-title` on the link): the HTML lists both, and the sidebar's script
|
|
319
|
+
* (`LayoutSidebar.astro`, `sidebar-editions.ts`) makes the two one row.
|
|
320
|
+
*
|
|
321
|
+
* A part's deck is an item of the part right after its Overview. Both name
|
|
322
|
+
* their part and which of its two editions they are (`data-part`,
|
|
323
|
+
* `data-part-edition`), and the deck its part's label and the ordinals of
|
|
324
|
+
* the chapters it covers (`data-title`, `data-chapters`: `1–2`), so the
|
|
325
|
+
* script makes them the part's one row. A chapter it covers that has no deck
|
|
326
|
+
* of its own carries the address of its section in the part's deck
|
|
327
|
+
* (`data-deck-href`, `/<part>/slides/#slide-N`, without the site's base),
|
|
328
|
+
* which the script makes the chapter's other edition: a lesson's own deck
|
|
329
|
+
* wins over its part's. The frames the deck and each section hold are
|
|
330
|
+
* `buildDecks`'s, added once it has run ({@link withPartDeckFrames}).
|
|
180
331
|
*/
|
|
181
332
|
export function buildSidebar(
|
|
182
333
|
tracks: readonly TrackLike[],
|
|
183
334
|
lessonSlugs: readonly string[],
|
|
184
335
|
parts: readonly SidebarSection[],
|
|
185
|
-
{
|
|
336
|
+
{
|
|
337
|
+
floatIndex = false,
|
|
338
|
+
decks = new Map(),
|
|
339
|
+
slidesFirst = new Set(),
|
|
340
|
+
partDecks = new Map(),
|
|
341
|
+
titleOf = () => undefined,
|
|
342
|
+
}: {
|
|
343
|
+
readonly floatIndex?: boolean;
|
|
344
|
+
/** A deck beside its chapter: chapter slug → the deck's route, listed under the chapter. */
|
|
345
|
+
readonly decks?: ReadonlyMap<string, string>;
|
|
346
|
+
/** The lessons whose only edition is a deck, by slug: listed with the deck's glyph. */
|
|
347
|
+
readonly slidesFirst?: ReadonlySet<string>;
|
|
348
|
+
/**
|
|
349
|
+
* A part's deck, by the part's directory: its route, and the frame each
|
|
350
|
+
* chapter it covers opens on (its section's divider), by chapter slug.
|
|
351
|
+
*/
|
|
352
|
+
readonly partDecks?: ReadonlyMap<
|
|
353
|
+
string,
|
|
354
|
+
{ readonly route: string; readonly covers: ReadonlyMap<string, number> }
|
|
355
|
+
>;
|
|
356
|
+
/** A lesson's title, by its slug: `data-title` on each of its items, left out when unknown. */
|
|
357
|
+
readonly titleOf?: (slug: string) => string | undefined;
|
|
358
|
+
} = {},
|
|
186
359
|
): SidebarGroup[] {
|
|
187
360
|
const rank = new Map(
|
|
188
361
|
mergeTrackOrder(tracks).map((lessonId, index) => [lessonId, index]),
|
|
189
362
|
);
|
|
190
363
|
const positionOf = (slug: string) =>
|
|
191
364
|
rank.get(lessonIdFromSlug(slug)) ?? Number.POSITIVE_INFINITY;
|
|
365
|
+
const edition = (lesson: string, name: 'chapter' | 'slides'): Record<string, string> => {
|
|
366
|
+
const title = titleOf(lesson);
|
|
367
|
+
return {
|
|
368
|
+
'data-edition': name,
|
|
369
|
+
'data-lesson': lesson,
|
|
370
|
+
...(title === undefined ? {} : { 'data-title': title }),
|
|
371
|
+
};
|
|
372
|
+
};
|
|
192
373
|
|
|
193
374
|
const groups: SidebarGroup[] = parts.map(
|
|
194
375
|
({ dir, label, overviewLabel, overviewLink }) => {
|
|
195
|
-
const
|
|
376
|
+
const partDeck = partDecks.get(dir);
|
|
377
|
+
const ordered = lessonSlugs
|
|
196
378
|
.filter((slug) => slug.startsWith(`${dir}/`))
|
|
197
379
|
.sort(
|
|
198
380
|
(left, right) =>
|
|
199
381
|
positionOf(left) - positionOf(right) || left.localeCompare(right),
|
|
200
382
|
);
|
|
383
|
+
const chapters: SidebarItem[] = ordered.flatMap((slug): SidebarItem[] => {
|
|
384
|
+
if (slidesFirst.has(slug)) {
|
|
385
|
+
return [{ slug, attrs: { class: DECK_LESSON_CLASS, ...edition(slug, 'slides') } }];
|
|
386
|
+
}
|
|
387
|
+
const deck = decks.get(slug);
|
|
388
|
+
// A chapter with no deck of its own: its section of the part's deck.
|
|
389
|
+
const frame = deck === undefined ? partDeck?.covers.get(slug) : undefined;
|
|
390
|
+
const chapter = {
|
|
391
|
+
slug,
|
|
392
|
+
attrs: {
|
|
393
|
+
...edition(slug, 'chapter'),
|
|
394
|
+
...(frame === undefined || partDeck === undefined
|
|
395
|
+
? {}
|
|
396
|
+
: { 'data-deck-href': `/${partDeck.route}/#slide-${frame}` }),
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
return deck === undefined
|
|
400
|
+
? [chapter]
|
|
401
|
+
: [chapter, { slug: deck, label: 'Slides', attrs: { class: DECK_ROW_CLASS, ...edition(slug, 'slides') } }];
|
|
402
|
+
});
|
|
201
403
|
// The overview leads the group: it is the part's own page, so it reads
|
|
202
404
|
// as the chapter list's heading rather than an appendix to it, and in
|
|
203
|
-
// compact mode it is the first icon instead of the last.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
405
|
+
// compact mode it is the first icon instead of the last. The part's
|
|
406
|
+
// deck follows it, as the part's own too.
|
|
407
|
+
const own: SidebarItem[] = [
|
|
408
|
+
...(overviewLink
|
|
207
409
|
? [
|
|
208
410
|
{
|
|
209
411
|
label: overviewLabel ?? 'Overview',
|
|
210
412
|
link: overviewLink,
|
|
211
|
-
attrs: { class: PART_OVERVIEW_CLASS },
|
|
413
|
+
attrs: { class: PART_OVERVIEW_CLASS, 'data-part': dir, 'data-part-edition': 'overview' },
|
|
212
414
|
},
|
|
213
|
-
...chapters,
|
|
214
415
|
]
|
|
215
|
-
:
|
|
216
|
-
|
|
416
|
+
: []),
|
|
417
|
+
...(partDeck === undefined
|
|
418
|
+
? []
|
|
419
|
+
: [
|
|
420
|
+
{
|
|
421
|
+
slug: partDeck.route,
|
|
422
|
+
label: 'Slides',
|
|
423
|
+
attrs: {
|
|
424
|
+
class: `${DECK_ROW_CLASS} ${PART_DECK_CLASS}`,
|
|
425
|
+
'data-edition': 'slides',
|
|
426
|
+
'data-part': dir,
|
|
427
|
+
'data-part-edition': 'slides',
|
|
428
|
+
'data-title': label,
|
|
429
|
+
// A chapter's ordinal is its place in the part, as `lessons[].chapter` numbers it.
|
|
430
|
+
'data-chapters': numberList(
|
|
431
|
+
[...partDeck.covers.keys()].flatMap((slug) => {
|
|
432
|
+
const at = ordered.indexOf(slug);
|
|
433
|
+
return at === -1 ? [] : [at + 1];
|
|
434
|
+
}),
|
|
435
|
+
),
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
]),
|
|
439
|
+
];
|
|
440
|
+
return { label, items: [...own, ...chapters] };
|
|
217
441
|
},
|
|
218
442
|
);
|
|
219
443
|
|
|
@@ -228,3 +452,45 @@ export function buildSidebar(
|
|
|
228
452
|
|
|
229
453
|
return groups;
|
|
230
454
|
}
|
|
455
|
+
|
|
456
|
+
/** A part's deck as {@link withPartDeckFrames} reads it: its part and its frames, each with the chapter it covers. */
|
|
457
|
+
export interface PartDeckFrames {
|
|
458
|
+
readonly part?: string;
|
|
459
|
+
readonly frames: readonly { readonly number: number; readonly chapter?: string }[];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* The sidebar with each part's deck's frames, as `buildDecks` numbers them:
|
|
464
|
+
* the deck's count on its item (`data-frames`), and on each chapter that
|
|
465
|
+
* links into its section the frames that section holds (`data-deck-range`,
|
|
466
|
+
* `2–16`, or `5` for one), its first to its last when it runs over several
|
|
467
|
+
* sections. `buildDecks` runs after the sidebar is built, since the chapters
|
|
468
|
+
* it numbers are ordered by the sidebar.
|
|
469
|
+
*/
|
|
470
|
+
export function withPartDeckFrames(
|
|
471
|
+
groups: readonly SidebarGroup[],
|
|
472
|
+
decks: readonly PartDeckFrames[],
|
|
473
|
+
): SidebarGroup[] {
|
|
474
|
+
const byPart = new Map(decks.flatMap((deck) => (deck.part === undefined ? [] : [[deck.part, deck] as const])));
|
|
475
|
+
const rangeOf = (chapter: string): string | undefined => {
|
|
476
|
+
const numbers = decks.flatMap((deck) =>
|
|
477
|
+
deck.part === undefined ? [] : deck.frames.filter((frame) => frame.chapter === chapter).map((frame) => frame.number),
|
|
478
|
+
);
|
|
479
|
+
if (numbers.length === 0) return undefined;
|
|
480
|
+
const first = Math.min(...numbers);
|
|
481
|
+
const last = Math.max(...numbers);
|
|
482
|
+
return first === last ? String(first) : `${first}–${last}`;
|
|
483
|
+
};
|
|
484
|
+
return groups.map((group) => ({
|
|
485
|
+
...group,
|
|
486
|
+
items: group.items.map((item): SidebarItem => {
|
|
487
|
+
if (typeof item === 'string' || item.attrs === undefined) return item;
|
|
488
|
+
const attrs = item.attrs;
|
|
489
|
+
const deck = attrs['data-part-edition'] === 'slides' ? byPart.get(attrs['data-part'] ?? '') : undefined;
|
|
490
|
+
if (deck) return { ...item, attrs: { ...attrs, 'data-frames': String(deck.frames.length) } };
|
|
491
|
+
const lesson = attrs['data-lesson'];
|
|
492
|
+
const range = attrs['data-deck-href'] === undefined || lesson === undefined ? undefined : rangeOf(lesson);
|
|
493
|
+
return range === undefined ? item : { ...item, attrs: { ...attrs, 'data-deck-range': range } };
|
|
494
|
+
}),
|
|
495
|
+
}));
|
|
496
|
+
}
|