explico 0.7.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1409 -1
- package/README.md +60 -15
- package/dist/compiler/astro.d.ts +6 -1
- package/dist/compiler/cli.d.ts +54 -3
- package/dist/compiler/cli.js +80 -6
- package/dist/compiler/collections.d.ts +35 -2
- package/dist/compiler/collections.js +84 -8
- package/dist/compiler/course-config.d.ts +3 -194
- package/dist/compiler/course-config.js +78 -5
- package/dist/compiler/exports/standalone-html.d.ts +6 -1
- package/dist/compiler/knowledge-version.js +1 -1
- package/dist/compiler/lesson-derivation.d.ts +27 -10
- package/dist/compiler/lesson-derivation.js +23 -8
- package/dist/compiler/logging.d.ts +14 -1
- package/dist/compiler/logging.js +6 -2
- package/dist/compiler/manifest.d.ts +204 -31
- package/dist/compiler/manifest.js +270 -55
- package/dist/compiler/resolution-report.d.ts +6 -1
- package/dist/course-config-Dsnri9Tk.d.ts +464 -0
- package/dist/reference/base-accents.d.mts +26 -0
- package/dist/reference/base-accents.mjs +31 -0
- package/dist/reference/base-roman-labels.d.mts +2 -2
- package/dist/reference/base-roman-labels.mjs +2 -2
- package/dist/reference/consistency.d.ts +14 -2
- package/dist/reference/consistency.js +108 -4
- package/dist/reference/delimiter-size.d.ts +21 -11
- package/dist/reference/delimiter-size.js +15 -39
- package/dist/reference/diagrams-validate.d.ts +7 -2
- package/dist/reference/diagrams-validate.js +9 -1
- package/dist/reference/equations-validate.d.ts +8 -5
- package/dist/reference/equations-validate.js +1 -0
- package/dist/reference/equations.d.mts +69 -34
- package/dist/reference/equations.mjs +90 -44
- package/dist/reference/examples-validate.d.ts +31 -0
- package/dist/reference/examples-validate.js +48 -0
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/figures-validate.js +4 -1
- package/dist/reference/folds-validate.d.ts +33 -0
- package/dist/reference/folds-validate.js +32 -0
- package/dist/reference/gate-abbreviations.d.ts +3 -1
- package/dist/reference/gate-abbreviations.js +3 -2
- package/dist/reference/gate-course-rules.d.ts +32 -0
- package/dist/reference/gate-course-rules.js +85 -0
- package/dist/reference/gate-heading-length.d.ts +5 -2
- package/dist/reference/gate-heading-length.js +47 -23
- package/dist/reference/gate-heading-structure.d.ts +41 -0
- package/dist/reference/gate-heading-structure.js +113 -0
- package/dist/reference/gate-math-punctuation.d.ts +71 -0
- package/dist/reference/gate-math-punctuation.js +251 -0
- package/dist/reference/gate-math.d.ts +44 -6
- package/dist/reference/gate-math.js +63 -10
- package/dist/reference/gate-part-pages.d.ts +50 -0
- package/dist/reference/gate-part-pages.js +118 -0
- package/dist/reference/gate-prose-notation.d.ts +32 -11
- package/dist/reference/gate-prose-notation.js +30 -8
- package/dist/reference/katex-delimiters.d.mts +15 -0
- package/dist/reference/katex-delimiters.mjs +138 -0
- package/dist/reference/katex-options.mjs +4 -0
- package/dist/reference/keyed-float-validate.d.ts +27 -10
- package/dist/reference/keyed-float-validate.js +15 -1
- package/dist/reference/keyed-jsx-floats.d.mts +11 -5
- package/dist/reference/keyed-jsx-floats.mjs +73 -20
- package/dist/reference/knowledge-checks-validate.d.ts +1 -1
- package/dist/reference/knowledge-checks.d.mts +3 -1
- package/dist/reference/knowledge-checks.mjs +9 -4
- package/dist/reference/lab-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-slot.d.ts +34 -0
- package/dist/reference/lab-math-slot.js +6 -0
- package/dist/reference/math-glyphs.d.mts +80 -4
- package/dist/reference/math-glyphs.mjs +588 -39
- package/dist/reference/page-notation.d.ts +6 -1
- package/dist/reference/raw-floats-validate.js +1 -1
- package/dist/reference/raw-floats.mjs +1 -1
- package/dist/reference/rehype-heading-anchors.d.mts +14 -0
- package/dist/reference/rehype-heading-anchors.mjs +116 -17
- package/dist/reference/rehype-table-header-labels.d.mts +6 -1
- package/dist/reference/rehype-table-header-labels.mjs +21 -3
- package/dist/reference/remark-asides.d.mts +3 -0
- package/dist/reference/remark-asides.mjs +180 -0
- package/dist/reference/remark-memo.d.mts +3 -1
- package/dist/reference/remark-memo.mjs +42 -11
- package/dist/reference/remark-notation.d.mts +60 -10
- package/dist/reference/remark-notation.mjs +387 -36
- package/dist/reference/render-lab-math.d.ts +4 -26
- package/dist/reference/render-lab-math.js +5 -5
- package/dist/reference/sections-validate.d.ts +27 -0
- package/dist/reference/sections-validate.js +41 -0
- package/dist/reference/sections.d.mts +97 -0
- package/dist/reference/sections.mjs +306 -0
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/types.d.ts +24 -1
- package/dist/reference/unit-words.d.ts +37 -0
- package/dist/reference/unit-words.js +170 -0
- package/dist/rules/catalog.d.ts +176 -0
- package/dist/rules/catalog.js +983 -0
- package/dist/rules/course-rules.d.ts +115 -0
- package/dist/rules/course-rules.js +292 -0
- package/dist/rules/course-rules.schema.json +138 -0
- package/dist/rules/in-effect.d.ts +53 -0
- package/dist/rules/in-effect.js +142 -0
- package/dist/rules/rules.schema.json +1072 -0
- package/dist/rules/settings.d.ts +3 -0
- package/dist/rules/settings.js +290 -0
- package/dist/rules/waivers.d.ts +53 -0
- package/dist/rules/waivers.js +132 -0
- package/dist/session/PreferenceStore.d.ts +5 -0
- package/dist/session/PreferenceStore.js +9 -0
- package/docs/VERSIONING.md +2 -2
- package/docs/authoring/README.md +232 -40
- package/docs/authoring/agents.md +55 -9
- package/docs/authoring/components.md +178 -22
- package/docs/authoring/migrating.md +1324 -2
- package/docs/authoring/notation.md +616 -344
- package/docs/authoring/prose.md +551 -0
- package/docs/authoring/retired.md +35 -0
- package/docs/authoring/structure-rules.md +422 -40
- package/docs/authoring/structure.md +445 -62
- package/docs/authoring/versioning.md +20 -28
- package/docs/authoring/visualizations.md +142 -20
- package/docs/katex-bracket-scaling.md +191 -0
- package/package.json +3 -2
- package/src/compiler/cli.ts +139 -6
- package/src/compiler/collections.ts +144 -10
- package/src/compiler/course-config.ts +273 -34
- package/src/compiler/knowledge-version.ts +1 -1
- package/src/compiler/lesson-derivation.ts +54 -15
- package/src/compiler/logging.ts +21 -4
- package/src/compiler/manifest.ts +573 -104
- package/src/components/BlockHeading.astro +113 -0
- package/src/components/CourseContents.astro +221 -0
- package/src/components/CurriculumMap.astro +5 -6
- package/src/components/FloatIndex.astro +200 -0
- package/src/components/HoverPanel.astro +431 -79
- package/src/components/PartBridge.astro +27 -0
- package/src/components/PartOverview.astro +401 -0
- package/src/components/PartScope.astro +27 -0
- package/src/components/abbreviation/AbbreviationLayer.astro +49 -51
- package/src/components/assessments/AssessmentRunner.tsx +8 -5
- package/src/components/assessments/AssessmentSet.astro +23 -26
- package/src/components/block-heading.ts +146 -0
- package/src/components/card-arrow.ts +243 -0
- package/src/components/card-drag.ts +631 -0
- package/src/components/card-jump.ts +45 -0
- package/src/components/card-manager.ts +1589 -0
- package/src/components/card-memory.ts +177 -0
- package/src/components/card-placement.ts +504 -0
- package/src/components/card-side.ts +336 -0
- package/src/components/card-travel.ts +197 -0
- package/src/components/citation/CitationLayer.astro +77 -76
- package/src/components/diagram/Diagram.astro +161 -316
- package/src/components/disclosure/course-disclosure.ts +24 -0
- package/src/components/disclosure/hover-preview.ts +122 -42
- package/src/components/disclosure/persisted-disclosure.ts +3 -3
- package/src/components/disclosure/print-as-shown.ts +252 -0
- package/src/components/examples/CompactExample.astro +3 -1
- package/src/components/examples/CompactExamples.astro +101 -94
- package/src/components/figure/Figure.astro +174 -274
- package/src/components/figure/FloatCaption.astro +87 -0
- package/src/components/figure/figure-state.ts +58 -0
- package/src/components/figure/figure-tools.ts +87 -0
- package/src/components/float-name.ts +101 -0
- package/src/components/fold/Fold.astro +125 -0
- package/src/components/fold/fold-id.ts +40 -0
- package/src/components/memo/Memo.astro +162 -231
- package/src/components/memo/ShortNote.astro +6 -6
- package/src/components/notation/NotationFormula.astro +5 -1
- package/src/components/notation/NotationGlossary.astro +24 -14
- package/src/components/notation/NotationLayer.astro +169 -162
- package/src/components/notation/NotationMath.astro +10 -1
- package/src/components/notation/render-notation-math.ts +111 -10
- package/src/components/reference/FloatRefLayer.astro +485 -423
- package/src/components/starlight/AboutPanel.astro +22 -6
- package/src/components/starlight/CollapsibleSections.astro +207 -93
- package/src/components/starlight/DiagramEnhancer.astro +15 -138
- package/src/components/starlight/HeadingAnchorEnhancer.astro +193 -1
- package/src/components/starlight/KeyedFloatEnhancer.astro +100 -9
- package/src/components/starlight/LayoutHeader.astro +298 -19
- package/src/components/starlight/LayoutPageSidebar.astro +99 -12
- package/src/components/starlight/LayoutPageTitle.astro +120 -0
- package/src/components/starlight/LayoutSidebar.astro +229 -11
- package/src/components/starlight/LessonExportActions.astro +53 -72
- package/src/components/starlight/LessonFooter.astro +26 -4
- package/src/components/starlight/section-parts.ts +15 -2
- package/src/components/starlight/section-summary.ts +181 -0
- package/src/components/table/Table.astro +95 -76
- package/src/components/table/TableOverflowExpander.astro +65 -28
- package/src/content-config.ts +40 -4
- package/src/reference/base-accents.mjs +31 -0
- package/src/reference/base-roman-labels.mjs +2 -2
- package/src/reference/consistency.ts +176 -5
- package/src/reference/delimiter-size.ts +40 -61
- package/src/reference/diagrams-validate.ts +17 -1
- package/src/reference/equations-validate.ts +10 -5
- package/src/reference/equations.mjs +90 -44
- package/src/reference/examples-validate.ts +97 -0
- package/src/reference/figures-validate.ts +6 -1
- package/src/reference/folds-validate.ts +65 -0
- package/src/reference/gate-abbreviations.ts +3 -2
- package/src/reference/gate-course-rules.ts +134 -0
- package/src/reference/gate-heading-length.ts +68 -37
- package/src/reference/gate-heading-structure.ts +193 -0
- package/src/reference/gate-math-punctuation.ts +381 -0
- package/src/reference/gate-math.ts +119 -12
- package/src/reference/gate-part-pages.ts +217 -0
- package/src/reference/gate-prose-notation.ts +74 -13
- package/src/reference/katex-delimiters.mjs +138 -0
- package/src/reference/katex-options.mjs +4 -0
- package/src/reference/keyed-float-validate.ts +45 -11
- package/src/reference/keyed-jsx-floats.mjs +73 -20
- package/src/reference/knowledge-checks-validate.ts +1 -1
- package/src/reference/knowledge-checks.mjs +9 -4
- package/src/reference/lab-math-slot.ts +36 -0
- package/src/reference/math-glyphs.mjs +588 -39
- package/src/reference/raw-floats-validate.ts +1 -1
- package/src/reference/raw-floats.mjs +1 -1
- package/src/reference/rehype-heading-anchors.mjs +116 -17
- package/src/reference/rehype-table-header-labels.mjs +21 -3
- package/src/reference/remark-asides.mjs +180 -0
- package/src/reference/remark-memo.mjs +42 -11
- package/src/reference/remark-notation.mjs +387 -36
- package/src/reference/render-lab-math.ts +17 -32
- package/src/reference/sections-validate.ts +81 -0
- package/src/reference/sections.mjs +306 -0
- package/src/reference/tables-validate.ts +2 -1
- package/src/reference/types.ts +24 -0
- package/src/reference/unit-words.ts +233 -0
- package/src/rules/catalog.ts +1298 -0
- package/src/rules/course-rules.ts +456 -0
- package/src/rules/in-effect.ts +235 -0
- package/src/rules/settings.ts +448 -0
- package/src/rules/waivers.ts +249 -0
- package/src/session/PreferenceStore.ts +15 -0
- package/src/styles/global.css +1471 -163
- package/src/styles/surfaces.css +182 -0
- package/src/components/hover-panel.ts +0 -339
- package/src/components/popup-stack.ts +0 -345
|
@@ -3,9 +3,14 @@ import { basename, join, relative } from "node:path";
|
|
|
3
3
|
import { parseFrontmatter } from "@astrojs/markdown-remark";
|
|
4
4
|
import { isSubstantiveMeaning } from "../reference/prose.js";
|
|
5
5
|
import { parseEquationRef } from "../reference/equations.mjs";
|
|
6
|
+
import {
|
|
7
|
+
DECORATION_NAMES,
|
|
8
|
+
decorationOfEntry
|
|
9
|
+
} from "../reference/math-glyphs.mjs";
|
|
6
10
|
import { parseKeyedFloatRef } from "../reference/keyed-jsx-floats.mjs";
|
|
7
11
|
import { parseKnowledgeCheckRef } from "../reference/knowledge-checks.mjs";
|
|
8
12
|
import { parseMemoRef } from "../reference/remark-memo.mjs";
|
|
13
|
+
import { parseSectionRef } from "../reference/sections.mjs";
|
|
9
14
|
import { resolveLabel } from "../reference/label.js";
|
|
10
15
|
import { resolveGlosses } from "../reference/gloss.js";
|
|
11
16
|
import {
|
|
@@ -25,12 +30,24 @@ function filesBelow(directory, extensions) {
|
|
|
25
30
|
return entry.isFile() && extensions.some((extension) => entry.name.endsWith(extension)) ? [path] : [];
|
|
26
31
|
}).sort();
|
|
27
32
|
}
|
|
33
|
+
function bodyStartLine(raw, body) {
|
|
34
|
+
const offset = raw.endsWith(body) ? raw.length - body.length : raw.indexOf(body);
|
|
35
|
+
if (offset <= 0) return 1;
|
|
36
|
+
let line = 1;
|
|
37
|
+
for (let index = 0; index < offset; index += 1) {
|
|
38
|
+
if (raw.charCodeAt(index) === 10) line += 1;
|
|
39
|
+
}
|
|
40
|
+
return line;
|
|
41
|
+
}
|
|
28
42
|
function parseDocument(path) {
|
|
29
|
-
const
|
|
43
|
+
const raw = readFileSync(path, "utf8");
|
|
44
|
+
const parsed = parseFrontmatter(raw);
|
|
45
|
+
const body = parsed.content.replace(/^\n/, "");
|
|
30
46
|
return {
|
|
31
47
|
file: relative(REPOSITORY_ROOT, path),
|
|
32
48
|
data: parsed.frontmatter ?? {},
|
|
33
|
-
body
|
|
49
|
+
body,
|
|
50
|
+
bodyLine: bodyStartLine(raw, body)
|
|
34
51
|
};
|
|
35
52
|
}
|
|
36
53
|
function record(value, label) {
|
|
@@ -139,7 +156,7 @@ function extractNotationReferences(text, file, kind) {
|
|
|
139
156
|
for (const [pattern, referenceKind] of patterns) {
|
|
140
157
|
for (const match of searchable.matchAll(pattern)) {
|
|
141
158
|
const key = (match[1] ?? "").trim();
|
|
142
|
-
if (parseEquationRef(key) || parseKeyedFloatRef(key, { prefix: "tab" }) || parseKeyedFloatRef(key, { prefix: "fig" }) || parseKeyedFloatRef(key, { prefix: "dia" }) || parseKnowledgeCheckRef(key) || parseMemoRef(key)) {
|
|
159
|
+
if (parseEquationRef(key) || parseKeyedFloatRef(key, { prefix: "tab" }) || parseKeyedFloatRef(key, { prefix: "fig" }) || parseKeyedFloatRef(key, { prefix: "dia" }) || parseKeyedFloatRef(key, { prefix: "ex" }) || parseKnowledgeCheckRef(key) || parseSectionRef(key) || parseMemoRef(key)) {
|
|
143
160
|
continue;
|
|
144
161
|
}
|
|
145
162
|
references.push({
|
|
@@ -172,6 +189,29 @@ function lessonCheckIds(lessonPath) {
|
|
|
172
189
|
const checksPath = lessonPath.replace(/\.mdx?$/, ".checks.yml");
|
|
173
190
|
return existsSync(checksPath) ? parseCheckList(readFileSync(checksPath, "utf8")) : [];
|
|
174
191
|
}
|
|
192
|
+
function loadPartOverviews(parts) {
|
|
193
|
+
const pages = [];
|
|
194
|
+
for (const { dir } of parts) {
|
|
195
|
+
for (const extension of [".mdx", ".md"]) {
|
|
196
|
+
const path = join(CONTENT_ROOT, "docs", dir, `index${extension}`);
|
|
197
|
+
if (!existsSync(path)) continue;
|
|
198
|
+
const { data, file, body, bodyLine } = parseDocument(path);
|
|
199
|
+
const title = typeof data.title === "string" ? data.title : dir;
|
|
200
|
+
const description = typeof data.description === "string" ? data.description : void 0;
|
|
201
|
+
pages.push({
|
|
202
|
+
dir,
|
|
203
|
+
slug: `${dir}/index`,
|
|
204
|
+
file,
|
|
205
|
+
title,
|
|
206
|
+
...description === void 0 ? {} : { description },
|
|
207
|
+
body,
|
|
208
|
+
bodyLine
|
|
209
|
+
});
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return pages;
|
|
214
|
+
}
|
|
175
215
|
function lessonEntries(prerequisitesById) {
|
|
176
216
|
const lessons = [];
|
|
177
217
|
for (const path of filesBelow(join(CONTENT_ROOT, "docs"), [".md", ".mdx"])) {
|
|
@@ -317,22 +357,56 @@ function abbreviationEntries() {
|
|
|
317
357
|
async function loadAbbreviationRegistryInput() {
|
|
318
358
|
return abbreviationEntries();
|
|
319
359
|
}
|
|
360
|
+
function notationSuspensions(raw, localDefinitions, file) {
|
|
361
|
+
if (raw === void 0) return [];
|
|
362
|
+
if (!Array.isArray(raw)) {
|
|
363
|
+
throw new TypeError(`${file} notation.suspend must be an array`);
|
|
364
|
+
}
|
|
365
|
+
const seen = /* @__PURE__ */ new Set();
|
|
366
|
+
return raw.map((item, index) => {
|
|
367
|
+
const at = `${file} notation.suspend[${index}]`;
|
|
368
|
+
const data = record(item, at);
|
|
369
|
+
const operator = string(data.operator, `${at}.operator`).trim();
|
|
370
|
+
if (!DECORATION_NAMES.has(operator)) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`${at}.operator ${JSON.stringify(operator)} is not a decoration; use one of ${[...DECORATION_NAMES].join(", ")}`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
if (seen.has(operator)) {
|
|
376
|
+
throw new Error(`${at} suspends ${JSON.stringify(operator)} a second time`);
|
|
377
|
+
}
|
|
378
|
+
seen.add(operator);
|
|
379
|
+
const local = localDefinitions.find(
|
|
380
|
+
(definition) => decorationOfEntry(definition.notation) === operator
|
|
381
|
+
);
|
|
382
|
+
if (local) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
`${at} suspends ${JSON.stringify(operator)}, but notation.local entry ${local.key} explains it as an operator (latex ${JSON.stringify(local.notation)}); drop one of them`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
const reason = optionalString(data.reason, `${at}.reason`);
|
|
388
|
+
return reason === void 0 ? { operator } : { operator, reason: reason.trim() };
|
|
389
|
+
});
|
|
390
|
+
}
|
|
320
391
|
function notationLessonEntries() {
|
|
321
392
|
const lessons = [];
|
|
322
393
|
for (const path of filesBelow(join(CONTENT_ROOT, "docs"), [".md", ".mdx"])) {
|
|
323
394
|
const slug = docSlug(path);
|
|
324
395
|
if (!isLessonSlug(slug)) continue;
|
|
325
|
-
const { file, data, body } = parseDocument(path);
|
|
396
|
+
const { file, data, body, bodyLine } = parseDocument(path);
|
|
326
397
|
const notation = record(data.notation ?? {}, `${file} notation`);
|
|
327
398
|
const local = Array.isArray(notation.local) ? notation.local : [];
|
|
399
|
+
const localDefinitions = local.map(
|
|
400
|
+
(entry, index) => localDefinition(entry, file, index)
|
|
401
|
+
);
|
|
328
402
|
lessons.push({
|
|
329
403
|
lessonId: lessonIdFromSlug(slug),
|
|
330
404
|
status: status(data.editorialStatus, `${file} editorialStatus`),
|
|
331
|
-
localDefinitions
|
|
332
|
-
|
|
333
|
-
),
|
|
405
|
+
localDefinitions,
|
|
406
|
+
suspended: notationSuspensions(notation.suspend, localDefinitions, file),
|
|
334
407
|
references: extractNotationReferences(body, file, void 0),
|
|
335
408
|
body,
|
|
409
|
+
bodyLine,
|
|
336
410
|
source: { file }
|
|
337
411
|
});
|
|
338
412
|
}
|
|
@@ -435,6 +509,8 @@ export {
|
|
|
435
509
|
loadNotationDefinitions,
|
|
436
510
|
loadNotationRegistryInput,
|
|
437
511
|
loadNotationSpecs,
|
|
512
|
+
loadPartOverviews,
|
|
438
513
|
loadSourceRecords,
|
|
439
|
-
notationSourceLocatorGaps
|
|
514
|
+
notationSourceLocatorGaps,
|
|
515
|
+
notationSuspensions
|
|
440
516
|
};
|
|
@@ -1,194 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* The engine never imports the course's config file; the wiring layer
|
|
5
|
-
* (`astro.config.mjs`, `scripts/*`, `src/content.config.ts`) imports the plain
|
|
6
|
-
* data value and passes it here, typed against `CourseConfig`. A second course
|
|
7
|
-
* supplies its own value of this shape.
|
|
8
|
-
*/
|
|
9
|
-
/**
|
|
10
|
-
* One **part** — a sidebar group, holding the chapters (lessons) whose slugs
|
|
11
|
-
* start with `dir`. A part's 1-based position in `CourseConfig.parts` is the
|
|
12
|
-
* number before the dot in every equation, table, and figure number inside it.
|
|
13
|
-
* See `docs/authoring/structure.md` for the full level taxonomy.
|
|
14
|
-
*/
|
|
15
|
-
interface CoursePart {
|
|
16
|
-
/** Lesson-slug prefix, e.g. `bonds` for `bonds/yield-to-maturity`. */
|
|
17
|
-
readonly dir: string;
|
|
18
|
-
/** Sidebar group heading. */
|
|
19
|
-
readonly label: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* @deprecated Renamed to {@link CoursePart}. "Section" now only ever means an
|
|
23
|
-
* `##` heading inside a lesson. Removed in the next MINOR release.
|
|
24
|
-
*/
|
|
25
|
-
type CourseSection = CoursePart;
|
|
26
|
-
interface CourseConventions {
|
|
27
|
-
/** Notation key a lesson's stated sign convention must resolve to. */
|
|
28
|
-
readonly signConventionKey: string;
|
|
29
|
-
/** Competency id that also counts as declaring the cash-flow perspective. */
|
|
30
|
-
readonly cashFlowPerspectiveCompetency: string;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* A course's edits to the shipped named-operator baseline (`BASE_OPERATORS`),
|
|
34
|
-
* consumed by the `operator-explained` consistency check (rule N8). An operator
|
|
35
|
-
* a lesson uses is fine when it is on the baseline plus `allow` minus `deny`;
|
|
36
|
-
* otherwise it needs a notation entry the reader can hover.
|
|
37
|
-
*
|
|
38
|
-
* Names are the KaTeX command without its backslash (`erf`, not `\erf`) for a
|
|
39
|
-
* built-in, and the verbatim body for an `\operatorname{…}` operator (`Var`).
|
|
40
|
-
*/
|
|
41
|
-
interface CourseOperators {
|
|
42
|
-
/** Operators this course also treats as universal (`Var`, `Cov`, `sgn`, …). */
|
|
43
|
-
readonly allow?: readonly string[];
|
|
44
|
-
/** Baseline operators this course wants explained anyway. */
|
|
45
|
-
readonly deny?: readonly string[];
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* A course's edits to the shipped roman-label baseline (`BASE_ROMAN_LABELS`),
|
|
49
|
-
* consumed by the `roman-label-explained` consistency check (rule N10). A
|
|
50
|
-
* `\mathrm{…}` multi-letter label a lesson uses is fine when it is on the
|
|
51
|
-
* baseline plus `allow` minus `deny`; otherwise it needs a notation entry the
|
|
52
|
-
* reader can hover. Same shape as {@link CourseOperators}, one level down:
|
|
53
|
-
* this governs `\mathrm{KE}`-style labels, not `\operatorname{Var}`-style
|
|
54
|
-
* operators.
|
|
55
|
-
*/
|
|
56
|
-
interface CourseRomanLabels {
|
|
57
|
-
/** Labels this course also treats as universal qualifiers (`eff`, `net`, …). */
|
|
58
|
-
readonly allow?: readonly string[];
|
|
59
|
-
/** Baseline labels this course wants explained anyway. */
|
|
60
|
-
readonly deny?: readonly string[];
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* A course's edits to the shipped abbreviation baseline (`BASE_ABBREVIATIONS`),
|
|
64
|
-
* consumed by the `abbreviation-explained` gate (rule N14). An abbreviation-
|
|
65
|
-
* shaped token — an all-caps acronym or a dotted form like `w.r.t.` — a
|
|
66
|
-
* lesson uses in prose is fine when it is on the baseline plus `allow` minus
|
|
67
|
-
* `deny`; otherwise it needs an entry in the `abbreviations` content
|
|
68
|
-
* collection, or — if it also names a math quantity — a notation entry the
|
|
69
|
-
* reader can hover. Same shape as {@link CourseOperators} / {@link
|
|
70
|
-
* CourseRomanLabels}.
|
|
71
|
-
*/
|
|
72
|
-
interface CourseAbbreviations {
|
|
73
|
-
/** Abbreviations this course also treats as universal (`GDP`, `CPU`, …). */
|
|
74
|
-
readonly allow?: readonly string[];
|
|
75
|
-
/** Baseline abbreviations this course wants explained anyway. */
|
|
76
|
-
readonly deny?: readonly string[];
|
|
77
|
-
}
|
|
78
|
-
/** One person or team credited for the study material. */
|
|
79
|
-
interface CourseAuthor {
|
|
80
|
-
readonly name: string;
|
|
81
|
-
/**
|
|
82
|
-
* Email, URL, or handle. Rendered in the About panel as a link when it looks
|
|
83
|
-
* like a `mailto:` or `https:` target, plain text otherwise.
|
|
84
|
-
*/
|
|
85
|
-
readonly contact?: string;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* The reader-facing "about this material" block, surfaced by the `[?]` panel in
|
|
89
|
-
* the header (`components/starlight/AboutPanel.astro`) on every page.
|
|
90
|
-
*
|
|
91
|
-
* `version` is the course's **knowledge version** — semver over the
|
|
92
|
-
* learning-facing content, independent of the `explico` engine's own version.
|
|
93
|
-
* It must equal the newest entry in `content/CHANGELOG.md`; a mismatch is a
|
|
94
|
-
* Tier-3 `knowledge-version-mismatch` warning (rule C3, `docs/authoring/versioning.md`).
|
|
95
|
-
*/
|
|
96
|
-
interface CourseAbout {
|
|
97
|
-
/** Display name of the material. Defaults to {@link CourseConfig.title}. */
|
|
98
|
-
readonly materialName?: string;
|
|
99
|
-
/** One or two sentences on what the material covers. Defaults to {@link CourseConfig.description}. */
|
|
100
|
-
readonly summary?: string;
|
|
101
|
-
/** Knowledge version — semver; equals the latest `content/CHANGELOG.md` heading. */
|
|
102
|
-
readonly version: string;
|
|
103
|
-
/** Who authored / maintains the material. At least one entry. */
|
|
104
|
-
readonly authors: readonly CourseAuthor[];
|
|
105
|
-
/** Shown verbatim in the About panel — scope, warranty, AI-assistance note. */
|
|
106
|
-
readonly disclaimer: string;
|
|
107
|
-
/** Link to the published content changelog (repo blob, docs page, …). */
|
|
108
|
-
readonly changelogHref?: string;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* A course's chosen initial open/closed state for the engine's own built-in
|
|
112
|
-
* collapsible sections — the "Page notation" panel (`NotationLayer.astro`)
|
|
113
|
-
* and the generated "References" section (`remark-citation.mjs`). Each is
|
|
114
|
-
* independent of `defaultCollapsible.sections`/`.subsections` (an author's
|
|
115
|
-
* own `##` headings): both of these are engine-authored, not written by a
|
|
116
|
-
* lesson, so they need their own default rather than inheriting the site's
|
|
117
|
-
* general heading default.
|
|
118
|
-
*
|
|
119
|
-
* `true` starts open, `false` (or omitted) starts closed — the engine's own
|
|
120
|
-
* default is closed either way, matching a long panel a reader usually
|
|
121
|
-
* doesn't need open on arrival.
|
|
122
|
-
*/
|
|
123
|
-
interface CourseDisclosureDefaults {
|
|
124
|
-
/** Initial state of the "Page notation" panel. Default: closed. */
|
|
125
|
-
readonly notation?: boolean;
|
|
126
|
-
/** Initial state of the generated "References" section. Default: closed. */
|
|
127
|
-
readonly references?: boolean;
|
|
128
|
-
}
|
|
129
|
-
/** The stable, non-optional form `NormalizedLessonExports` mirrors. */
|
|
130
|
-
interface NormalizedDisclosureDefaults {
|
|
131
|
-
readonly notation: boolean;
|
|
132
|
-
readonly references: boolean;
|
|
133
|
-
}
|
|
134
|
-
/** Validate and canonicalize `CourseConfig.disclosureDefaults`. Absent (or
|
|
135
|
-
* an absent sub-field) resolves to closed, the engine's own default. */
|
|
136
|
-
declare function normalizeDisclosureDefaults(config: CourseConfig): NormalizedDisclosureDefaults;
|
|
137
|
-
/** Optional, build-time lesson artifacts exposed to readers. */
|
|
138
|
-
interface LessonExportsConfig {
|
|
139
|
-
/** Emit one self-contained, offline-capable HTML file per lesson. */
|
|
140
|
-
readonly standaloneHtml?: boolean;
|
|
141
|
-
/** Offer the browser's native print dialog with explico's lesson print layout. */
|
|
142
|
-
readonly print?: boolean;
|
|
143
|
-
/**
|
|
144
|
-
* Published course root used for links that intentionally leave the exported
|
|
145
|
-
* lesson. Required at export time when such a link is present.
|
|
146
|
-
*/
|
|
147
|
-
readonly canonicalBaseUrl?: string;
|
|
148
|
-
}
|
|
149
|
-
/** The stable, non-optional export settings carried by the manifest. */
|
|
150
|
-
interface NormalizedLessonExports {
|
|
151
|
-
readonly standaloneHtml: boolean;
|
|
152
|
-
readonly print: boolean;
|
|
153
|
-
readonly canonicalBaseUrl?: string;
|
|
154
|
-
}
|
|
155
|
-
interface CourseConfig {
|
|
156
|
-
readonly title: string;
|
|
157
|
-
readonly description: string;
|
|
158
|
-
/**
|
|
159
|
-
* The course's parts (sidebar groups), in order. Exactly one of `parts` or
|
|
160
|
-
* the deprecated `sections` must be given.
|
|
161
|
-
*/
|
|
162
|
-
readonly parts?: readonly CoursePart[];
|
|
163
|
-
/**
|
|
164
|
-
* @deprecated Renamed to {@link CourseConfig.parts}. Still honoured, so a
|
|
165
|
-
* course on the old name keeps building; removed in the next MINOR release.
|
|
166
|
-
*/
|
|
167
|
-
readonly sections?: readonly CoursePart[];
|
|
168
|
-
/** Namespaces used by competency / notation `domain` fields. */
|
|
169
|
-
readonly domains: readonly string[];
|
|
170
|
-
readonly conventions: CourseConventions;
|
|
171
|
-
/** Reader-facing identity + knowledge version (the `[?]` About panel). */
|
|
172
|
-
readonly about: CourseAbout;
|
|
173
|
-
/** Edits to the shipped named-operator baseline (rule N8). Optional. */
|
|
174
|
-
readonly operators?: CourseOperators;
|
|
175
|
-
/** Edits to the shipped roman-label baseline (rule N10). Optional. */
|
|
176
|
-
readonly romanLabels?: CourseRomanLabels;
|
|
177
|
-
/** Edits to the shipped abbreviation baseline (rule N14). Optional. */
|
|
178
|
-
readonly abbreviations?: CourseAbbreviations;
|
|
179
|
-
/** Per-lesson export actions and downloadable artifacts. Disabled when omitted. */
|
|
180
|
-
readonly lessonExports?: LessonExportsConfig;
|
|
181
|
-
/** Initial open/closed state for the engine's own built-in collapsible
|
|
182
|
-
* sections ("Page notation", "References"). Closed when omitted. */
|
|
183
|
-
readonly disclosureDefaults?: CourseDisclosureDefaults;
|
|
184
|
-
}
|
|
185
|
-
/** Validate and canonicalize the build-facing lesson-export configuration. */
|
|
186
|
-
declare function normalizeLessonExports(config: CourseConfig): NormalizedLessonExports;
|
|
187
|
-
/**
|
|
188
|
-
* The course's parts, from whichever field the config uses. Throws rather than
|
|
189
|
-
* silently producing a course with no sidebar groups — a config with neither
|
|
190
|
-
* field would otherwise number every float by the `##`-section fallback.
|
|
191
|
-
*/
|
|
192
|
-
declare function coursePartsOf(config: CourseConfig): readonly CoursePart[];
|
|
193
|
-
|
|
194
|
-
export { type CourseAbbreviations, type CourseAbout, type CourseAuthor, type CourseConfig, type CourseConventions, type CourseDisclosureDefaults, type CourseOperators, type CoursePart, type CourseRomanLabels, type CourseSection, type LessonExportsConfig, type NormalizedDisclosureDefaults, type NormalizedLessonExports, coursePartsOf, normalizeDisclosureDefaults, normalizeLessonExports };
|
|
1
|
+
export { s as BLOCK_LABELS_DEFAULT, B as BlockLabels, a as CourseAbbreviations, t as CourseAbout, i as CourseAccents, u as CourseAuthor, C as CourseConfig, f as CourseConventions, v as CourseDisclosure, w as CourseHeadingDisclosure, x as CourseNumbering, y as CourseOpenDefault, g as CourseOperators, z as CoursePart, h as CourseRomanLabels, A as CourseSection, D as DISCLOSURE_DEFAULTS, L as LessonExportsConfig, c as NormalizedDisclosure, b as NormalizedLessonExports, d as NormalizedNumbering, N as NormalizedPartPages, E as coursePartsOf, F as normalizeBlockLabels, G as normalizeDisclosure, H as normalizeLessonExports, I as normalizeNumbering } from '../course-config-Dsnri9Tk.js';
|
|
2
|
+
import '../rules/course-rules.js';
|
|
3
|
+
import '../rules/catalog.js';
|
|
@@ -1,9 +1,78 @@
|
|
|
1
|
-
|
|
1
|
+
const DISCLOSURE_DEFAULTS = {
|
|
2
|
+
sections: { collapsible: false, defaultOpen: true },
|
|
3
|
+
subsections: { collapsible: false, defaultOpen: true },
|
|
4
|
+
knowledgeChecks: { collapsible: true, defaultOpen: true },
|
|
5
|
+
notation: { defaultOpen: false },
|
|
6
|
+
references: { defaultOpen: false },
|
|
7
|
+
memos: { defaultOpen: false },
|
|
8
|
+
folds: { defaultOpen: false },
|
|
9
|
+
figures: { defaultOpen: true },
|
|
10
|
+
workedExamples: { defaultOpen: false },
|
|
11
|
+
tables: { truncateAfter: 6 }
|
|
12
|
+
};
|
|
13
|
+
function normalizeDisclosure(config) {
|
|
14
|
+
const disclosure = config.disclosure ?? {};
|
|
15
|
+
if ("diagrams" in disclosure) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"CourseConfig disclosure.diagrams is gone: a diagram no longer folds by itself. Delete the key; to let a reader fold a diagram away, wrap it in a <Fold> or put it in a collapsible section."
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const heading = (key) => ({
|
|
21
|
+
collapsible: disclosure[key]?.collapsible ?? DISCLOSURE_DEFAULTS[key].collapsible,
|
|
22
|
+
defaultOpen: disclosure[key]?.defaultOpen ?? DISCLOSURE_DEFAULTS[key].defaultOpen
|
|
23
|
+
});
|
|
24
|
+
const open = (key) => ({
|
|
25
|
+
defaultOpen: disclosure[key]?.defaultOpen ?? DISCLOSURE_DEFAULTS[key].defaultOpen
|
|
26
|
+
});
|
|
27
|
+
const truncateAfter = disclosure.tables?.truncateAfter ?? DISCLOSURE_DEFAULTS.tables.truncateAfter;
|
|
28
|
+
if (!Number.isInteger(truncateAfter) || truncateAfter < 2) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`CourseConfig disclosure.tables.truncateAfter must be a whole number of rows, at least 2; received ${JSON.stringify(truncateAfter)}.`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
sections: heading("sections"),
|
|
35
|
+
subsections: heading("subsections"),
|
|
36
|
+
knowledgeChecks: heading("knowledgeChecks"),
|
|
37
|
+
notation: open("notation"),
|
|
38
|
+
references: open("references"),
|
|
39
|
+
memos: open("memos"),
|
|
40
|
+
folds: open("folds"),
|
|
41
|
+
figures: open("figures"),
|
|
42
|
+
workedExamples: open("workedExamples"),
|
|
43
|
+
tables: { truncateAfter }
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function normalizeNumbering(config) {
|
|
47
|
+
const numbering = config.numbering ?? {};
|
|
48
|
+
const depth = numbering.sections?.depth ?? 4;
|
|
49
|
+
if (!Number.isInteger(depth) || depth < 2 || depth > 6) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`CourseConfig numbering.sections.depth must be a heading level from 2 (##) to 6 (######); received ${JSON.stringify(depth)}.`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const floats = numbering.floats ?? "chapter";
|
|
55
|
+
if (floats !== "chapter" && floats !== "part") {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`CourseConfig numbering.floats must be "chapter" or "part"; received ${JSON.stringify(floats)}.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
2
60
|
return {
|
|
3
|
-
|
|
4
|
-
|
|
61
|
+
sections: { show: numbering.sections?.show !== false, depth },
|
|
62
|
+
floats,
|
|
63
|
+
aliases: numbering.aliases !== false
|
|
5
64
|
};
|
|
6
65
|
}
|
|
66
|
+
const BLOCK_LABELS_DEFAULT = "icon-word";
|
|
67
|
+
function normalizeBlockLabels(config) {
|
|
68
|
+
const labels = config.blockLabels ?? BLOCK_LABELS_DEFAULT;
|
|
69
|
+
if (labels !== "icon" && labels !== "icon-word") {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`CourseConfig blockLabels must be "icon" or "icon-word"; received ${JSON.stringify(labels)}.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return labels;
|
|
75
|
+
}
|
|
7
76
|
function normalizeLessonExports(config) {
|
|
8
77
|
const standaloneHtml = config.lessonExports?.standaloneHtml === true;
|
|
9
78
|
const print = config.lessonExports?.print === true;
|
|
@@ -40,7 +109,11 @@ function coursePartsOf(config) {
|
|
|
40
109
|
return parts;
|
|
41
110
|
}
|
|
42
111
|
export {
|
|
112
|
+
BLOCK_LABELS_DEFAULT,
|
|
113
|
+
DISCLOSURE_DEFAULTS,
|
|
43
114
|
coursePartsOf,
|
|
44
|
-
|
|
45
|
-
|
|
115
|
+
normalizeBlockLabels,
|
|
116
|
+
normalizeDisclosure,
|
|
117
|
+
normalizeLessonExports,
|
|
118
|
+
normalizeNumbering
|
|
46
119
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Manifest } from '../manifest.js';
|
|
2
2
|
import '../../curriculum/validation.js';
|
|
3
3
|
import '../../reference/consistency.js';
|
|
4
|
-
import '
|
|
4
|
+
import '../../course-config-Dsnri9Tk.js';
|
|
5
|
+
import '../../rules/catalog.js';
|
|
6
|
+
import '../../rules/course-rules.js';
|
|
5
7
|
import '../collections.js';
|
|
6
8
|
import '../../reference/types.js';
|
|
7
9
|
import '../../reference/lint-ignore.js';
|
|
@@ -10,11 +12,14 @@ import '../../reference/tables-validate.js';
|
|
|
10
12
|
import '../../reference/keyed-float-validate.js';
|
|
11
13
|
import '../../reference/figures-validate.js';
|
|
12
14
|
import '../../reference/diagrams-validate.js';
|
|
15
|
+
import '../../reference/examples-validate.js';
|
|
16
|
+
import '../../reference/folds-validate.js';
|
|
13
17
|
import '../../reference/knowledge-checks-validate.js';
|
|
14
18
|
import '../../reference/raw-floats-validate.js';
|
|
15
19
|
import '../../reference/gate-math.js';
|
|
16
20
|
import '../../reference/mixed-content.js';
|
|
17
21
|
import '../../reference/gloss.js';
|
|
22
|
+
import '../../rules/waivers.js';
|
|
18
23
|
import '../knowledge-version.js';
|
|
19
24
|
import '../lesson-derivation.js';
|
|
20
25
|
|
|
@@ -61,7 +61,7 @@ function resolveKnowledgeVersion(options) {
|
|
|
61
61
|
if (!latest) {
|
|
62
62
|
warn(
|
|
63
63
|
"knowledge-changelog-missing",
|
|
64
|
-
`${file} has no released \`## [x.y.z]\` entry \u2014 only \`## [Unreleased]\`. Cut a release: move it to \`## [${declaredVersion}] - <date>\` (rule
|
|
64
|
+
`${file} has no released \`## [x.y.z]\` entry \u2014 only \`## [Unreleased]\`. Cut a release: move it to \`## [${declaredVersion}] - <date>\` (rule C3).`
|
|
65
65
|
);
|
|
66
66
|
return { version: declaredVersion, diagnostics };
|
|
67
67
|
}
|
|
@@ -68,22 +68,39 @@ declare function mergeTrackOrder(tracks: readonly TrackLike[]): string[];
|
|
|
68
68
|
interface SidebarSection {
|
|
69
69
|
readonly dir: string;
|
|
70
70
|
readonly label: string;
|
|
71
|
+
readonly overviewLabel?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Href of this part's overview page (`/<dir>/`), set only when the part has
|
|
74
|
+
* one. It becomes the group's first item, so the page a part directory
|
|
75
|
+
* resolves to is reachable from the navigation that names the part.
|
|
76
|
+
*/
|
|
77
|
+
readonly overviewLink?: string;
|
|
71
78
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
/**
|
|
80
|
+
* One sidebar entry: a lesson slug, or an explicit link. `attrs` is Starlight's
|
|
81
|
+
* own passthrough onto the rendered `<a>` — the engine uses it to mark a part's
|
|
82
|
+
* overview row so CSS can give it its own icon.
|
|
83
|
+
*/
|
|
84
|
+
type SidebarItem = string | {
|
|
76
85
|
readonly label: string;
|
|
77
|
-
readonly
|
|
78
|
-
|
|
79
|
-
readonly link: string;
|
|
80
|
-
}[];
|
|
86
|
+
readonly link: string;
|
|
87
|
+
readonly attrs?: Readonly<Record<string, string>>;
|
|
81
88
|
};
|
|
89
|
+
interface SidebarGroup {
|
|
90
|
+
readonly label: string;
|
|
91
|
+
readonly items: readonly SidebarItem[];
|
|
92
|
+
}
|
|
93
|
+
/** Marks the overview row so `global.css` can give it its own icon. */
|
|
94
|
+
declare const PART_OVERVIEW_CLASS = "explico-part-overview";
|
|
82
95
|
/**
|
|
83
96
|
* The Starlight `sidebar` config: one group per section, lessons ordered by the
|
|
84
97
|
* merged track order (ties broken by slug), then the static Reference group.
|
|
85
98
|
* Replaces seven `autogenerate` blocks that read an authored `sidebar.order`.
|
|
99
|
+
* `floatIndex`: the course has a top-level "Figures and tables" page
|
|
100
|
+
* (`figures-and-tables.mdx`, `FloatIndex.astro`), listed in Reference.
|
|
86
101
|
*/
|
|
87
|
-
declare function buildSidebar(tracks: readonly TrackLike[], lessonSlugs: readonly string[], parts: readonly SidebarSection[]
|
|
102
|
+
declare function buildSidebar(tracks: readonly TrackLike[], lessonSlugs: readonly string[], parts: readonly SidebarSection[], { floatIndex }?: {
|
|
103
|
+
readonly floatIndex?: boolean;
|
|
104
|
+
}): SidebarGroup[];
|
|
88
105
|
|
|
89
|
-
export { type SidebarGroup, type SidebarSection, buildSidebar, citedSourceIds, deriveRequires, deriveSources, isLessonSlug, lessonIdFromSlug, mergeTrackOrder, parseCheckList };
|
|
106
|
+
export { PART_OVERVIEW_CLASS, type SidebarGroup, type SidebarItem, type SidebarSection, buildSidebar, citedSourceIds, deriveRequires, deriveSources, isLessonSlug, lessonIdFromSlug, mergeTrackOrder, parseCheckList };
|
|
@@ -59,27 +59,42 @@ function mergeTrackOrder(tracks) {
|
|
|
59
59
|
}
|
|
60
60
|
return ordered;
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
const PART_OVERVIEW_CLASS = "explico-part-overview";
|
|
63
|
+
function buildSidebar(tracks, lessonSlugs, parts, { floatIndex = false } = {}) {
|
|
63
64
|
const rank = new Map(
|
|
64
65
|
mergeTrackOrder(tracks).map((lessonId, index) => [lessonId, index])
|
|
65
66
|
);
|
|
66
67
|
const positionOf = (slug) => rank.get(lessonIdFromSlug(slug)) ?? Number.POSITIVE_INFINITY;
|
|
67
|
-
const groups = parts.map(
|
|
68
|
-
label,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
const groups = parts.map(
|
|
69
|
+
({ dir, label, overviewLabel, overviewLink }) => {
|
|
70
|
+
const chapters = lessonSlugs.filter((slug) => slug.startsWith(`${dir}/`)).sort(
|
|
71
|
+
(left, right) => positionOf(left) - positionOf(right) || left.localeCompare(right)
|
|
72
|
+
);
|
|
73
|
+
return {
|
|
74
|
+
label,
|
|
75
|
+
items: overviewLink ? [
|
|
76
|
+
{
|
|
77
|
+
label: overviewLabel ?? "Overview",
|
|
78
|
+
link: overviewLink,
|
|
79
|
+
attrs: { class: PART_OVERVIEW_CLASS }
|
|
80
|
+
},
|
|
81
|
+
...chapters
|
|
82
|
+
] : chapters
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
);
|
|
73
86
|
groups.push({
|
|
74
87
|
label: "Reference",
|
|
75
88
|
items: [
|
|
76
89
|
{ label: "Curriculum map", link: "/curriculum-map/" },
|
|
77
|
-
{ label: "Notation glossary", link: "/glossary/" }
|
|
90
|
+
{ label: "Notation glossary", link: "/glossary/" },
|
|
91
|
+
...floatIndex ? [{ label: "Figures and tables", link: "/figures-and-tables/" }] : []
|
|
78
92
|
]
|
|
79
93
|
});
|
|
80
94
|
return groups;
|
|
81
95
|
}
|
|
82
96
|
export {
|
|
97
|
+
PART_OVERVIEW_CLASS,
|
|
83
98
|
buildSidebar,
|
|
84
99
|
citedSourceIds,
|
|
85
100
|
deriveRequires,
|
|
@@ -67,6 +67,11 @@ interface AnyDiagnostic {
|
|
|
67
67
|
readonly file?: string;
|
|
68
68
|
readonly line?: number;
|
|
69
69
|
readonly column?: number;
|
|
70
|
+
/** `{ key, href }` when the finding belongs to a `\label{eq:…}` equation. */
|
|
71
|
+
readonly equation?: {
|
|
72
|
+
readonly key: string;
|
|
73
|
+
readonly href: string;
|
|
74
|
+
};
|
|
70
75
|
}
|
|
71
76
|
/** `manifest.diagnostics`, structurally — one array per subsystem. */
|
|
72
77
|
type DiagnosticGroups = Readonly<Record<string, readonly AnyDiagnostic[]>>;
|
|
@@ -89,7 +94,15 @@ interface RunLog {
|
|
|
89
94
|
/** Refreshes `latest` and reports what the run recorded. */
|
|
90
95
|
close(): RunLogSummary;
|
|
91
96
|
}
|
|
92
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* `[code] file:line:column: message`, matching what the console has always
|
|
99
|
+
* printed, with ` (eq:<key> -> <href>)` appended when the finding belongs to a
|
|
100
|
+
* keyed equation — so a size-only finding inside a long `aligned` block names
|
|
101
|
+
* the result it is about, not just its coordinates. A code owned by a lettered
|
|
102
|
+
* rule is prefixed with that rule's ID (`[N9 prose-notation]`), so the line
|
|
103
|
+
* names the rule page to read; an invariant's code stands alone, and a course
|
|
104
|
+
* rule's code *is* its ID (`[X1]`), so it is not repeated.
|
|
105
|
+
*/
|
|
93
106
|
declare function diagnosticLine(diagnostic: AnyDiagnostic): string;
|
|
94
107
|
interface RunLogOptions {
|
|
95
108
|
/** Overridden in tests; defaults to `build/logs` under the repository root. */
|
package/dist/compiler/logging.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
writeFileSync
|
|
8
8
|
} from "node:fs";
|
|
9
9
|
import { join, relative } from "node:path";
|
|
10
|
+
import { ruleIdOf } from "../rules/catalog.js";
|
|
10
11
|
const REPOSITORY_ROOT = process.cwd();
|
|
11
12
|
const LOGS_DIR = join(REPOSITORY_ROOT, "build", "logs");
|
|
12
13
|
const LEVELS = ["ERROR", "WARNING", "INFO"];
|
|
@@ -37,8 +38,11 @@ function formatRecord(level, source, message, date) {
|
|
|
37
38
|
function diagnosticLine(diagnostic) {
|
|
38
39
|
const code = diagnostic.code ?? diagnostic.kind;
|
|
39
40
|
const at = [diagnostic.file, diagnostic.line, diagnostic.column].filter((part) => part !== void 0).join(":");
|
|
40
|
-
const
|
|
41
|
-
|
|
41
|
+
const owner = code === void 0 ? void 0 : ruleIdOf(code);
|
|
42
|
+
const rule = owner === void 0 || owner === code ? "" : `${owner} `;
|
|
43
|
+
const prefix = code === void 0 ? "" : `[${rule}${code}] `;
|
|
44
|
+
const equation = diagnostic.equation === void 0 ? "" : ` (eq:${diagnostic.equation.key} -> ${diagnostic.equation.href})`;
|
|
45
|
+
return at === "" ? `${prefix}${diagnostic.message}${equation}` : `${prefix}${at}: ${diagnostic.message}${equation}`;
|
|
42
46
|
}
|
|
43
47
|
function createRunLog(options = {}) {
|
|
44
48
|
const root = options.root ?? LOGS_DIR;
|