explico 0.12.0 → 0.13.1
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 +1137 -1
- package/README.md +91 -13
- package/dist/compiler/astro.d.ts +4 -3
- package/dist/compiler/astro.js +45 -15
- package/dist/compiler/cli.d.ts +26 -12
- package/dist/compiler/cli.js +96 -25
- package/dist/compiler/collections.d.ts +9 -1
- package/dist/compiler/collections.js +55 -16
- package/dist/compiler/content-schema.d.ts +31 -0
- package/dist/compiler/content-schema.js +82 -0
- package/dist/compiler/content-schemas.d.ts +422 -0
- package/dist/compiler/content-schemas.js +229 -0
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +30 -2
- package/dist/compiler/exports/combined-html.d.ts +97 -0
- package/dist/compiler/exports/combined-html.js +492 -0
- package/dist/compiler/exports/download-index.d.ts +9 -0
- package/dist/compiler/exports/download-index.js +33 -0
- package/dist/compiler/exports/export-chapters.d.ts +98 -0
- package/dist/compiler/exports/export-chapters.js +164 -0
- package/dist/compiler/exports/export-pages.d.ts +48 -0
- package/dist/compiler/exports/export-pages.js +22 -0
- package/dist/compiler/exports/html-tree.d.ts +22 -0
- package/dist/compiler/exports/html-tree.js +47 -0
- package/dist/compiler/exports/latex/capture.d.ts +106 -0
- package/dist/compiler/exports/latex/capture.js +435 -0
- package/dist/compiler/exports/latex/compile.d.ts +73 -0
- package/dist/compiler/exports/latex/compile.js +115 -0
- package/dist/compiler/exports/latex/document.d.ts +102 -0
- package/dist/compiler/exports/latex/document.js +349 -0
- package/dist/compiler/exports/latex/explico.cls +415 -0
- package/dist/compiler/exports/latex/numbering.d.ts +18 -0
- package/dist/compiler/exports/latex/numbering.js +57 -0
- package/dist/compiler/exports/latex/serialize.d.ts +112 -0
- package/dist/compiler/exports/latex/serialize.js +606 -0
- package/dist/compiler/exports/latex-export.d.ts +44 -0
- package/dist/compiler/exports/latex-export.js +169 -0
- package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
- package/dist/compiler/exports/markdown/serialize.js +527 -0
- package/dist/compiler/exports/markdown-export.d.ts +39 -0
- package/dist/compiler/exports/markdown-export.js +240 -0
- package/dist/compiler/exports/standalone-html.d.ts +14 -5
- package/dist/compiler/exports/standalone-html.js +23 -51
- package/dist/compiler/exports/static-tree.d.ts +253 -0
- package/dist/compiler/exports/static-tree.js +625 -0
- package/dist/compiler/exports/zip.d.ts +16 -0
- package/dist/compiler/exports/zip.js +72 -0
- package/dist/compiler/lesson-derivation.d.ts +8 -2
- package/dist/compiler/lesson-derivation.js +6 -7
- package/dist/compiler/manifest.d.ts +87 -6
- package/dist/compiler/manifest.js +174 -31
- package/dist/compiler/render-diagnostics.d.ts +65 -0
- package/dist/compiler/render-diagnostics.js +56 -0
- package/dist/compiler/resolution-report.d.ts +2 -1
- package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
- package/dist/curriculum/validation.d.ts +9 -3
- package/dist/curriculum/validation.js +35 -17
- package/dist/reference/base-abbreviations.d.mts +2 -2
- package/dist/reference/base-abbreviations.mjs +2 -2
- package/dist/reference/base-operators.d.mts +2 -2
- package/dist/reference/base-operators.mjs +2 -2
- package/dist/reference/base-roman-labels.d.mts +4 -3
- package/dist/reference/base-roman-labels.mjs +4 -3
- package/dist/reference/citation-format.d.mts +35 -0
- package/dist/reference/citation-format.mjs +74 -0
- package/dist/reference/consistency.d.ts +5 -9
- package/dist/reference/consistency.js +34 -38
- package/dist/reference/curriculum-alignment.js +3 -24
- package/dist/reference/derived-entries.d.ts +45 -0
- package/dist/reference/derived-entries.js +50 -0
- package/dist/reference/derived-notation.d.mts +125 -0
- package/dist/reference/derived-notation.mjs +121 -0
- package/dist/reference/diagram-fit.d.ts +45 -0
- package/dist/reference/diagram-fit.js +54 -0
- package/dist/reference/diagram-layout.d.ts +59 -10
- package/dist/reference/diagram-layout.js +85 -24
- package/dist/reference/diagrams-validate.d.ts +1 -1
- package/dist/reference/equations-validate.d.ts +1 -1
- package/dist/reference/equations-validate.js +26 -1
- package/dist/reference/equations.d.mts +27 -0
- package/dist/reference/equations.mjs +120 -22
- package/dist/reference/examples-validate.d.ts +1 -1
- package/dist/reference/figure-fit.d.ts +70 -0
- package/dist/reference/figure-fit.js +60 -0
- package/dist/reference/figure-pictures.d.ts +27 -0
- package/dist/reference/figure-pictures.js +49 -0
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-abbreviations.js +1 -1
- package/dist/reference/gate-course-rules.d.ts +4 -3
- package/dist/reference/gate-course-rules.js +9 -4
- package/dist/reference/gate-heading-length.d.ts +2 -0
- package/dist/reference/gate-heading-length.js +7 -5
- package/dist/reference/gate-heading-structure.js +26 -0
- package/dist/reference/gate-indices.d.ts +35 -0
- package/dist/reference/gate-indices.js +299 -0
- package/dist/reference/gate-math.d.ts +32 -2
- package/dist/reference/gate-math.js +68 -12
- package/dist/reference/gate-mdx-expression.d.ts +17 -0
- package/dist/reference/gate-mdx-expression.js +29 -0
- package/dist/reference/gate-part-pages.d.ts +1 -1
- package/dist/reference/gate-prop-text.d.ts +28 -0
- package/dist/reference/gate-prop-text.js +106 -0
- package/dist/reference/gate-prose-notation.d.ts +3 -1
- package/dist/reference/gate-prose-notation.js +4 -2
- package/dist/reference/gate-retired-keys.d.ts +35 -0
- package/dist/reference/gate-retired-keys.js +58 -0
- package/dist/reference/gloss.d.ts +2 -0
- package/dist/reference/katex-measure.d.ts +17 -0
- package/dist/reference/katex-measure.js +223 -0
- package/dist/reference/keyed-float-validate.js +12 -0
- package/dist/reference/keyed-jsx-floats.d.mts +2 -2
- package/dist/reference/keyed-jsx-floats.mjs +11 -14
- package/dist/reference/lab-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-scope.js +13 -2
- package/dist/reference/math-glyphs.d.mts +94 -2
- package/dist/reference/math-glyphs.mjs +851 -46
- package/dist/reference/mixed-content.d.ts +8 -3
- package/dist/reference/mixed-content.js +4 -2
- package/dist/reference/page-notation.d.ts +34 -4
- package/dist/reference/page-notation.js +54 -2
- package/dist/reference/references.d.ts +11 -1
- package/dist/reference/references.js +6 -0
- package/dist/reference/registry.js +104 -10
- package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
- package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
- package/dist/reference/rehype-heading-anchors.mjs +11 -1
- package/dist/reference/remark-citation.mjs +0 -0
- package/dist/reference/remark-notation.mjs +84 -20
- package/dist/reference/render-lab-math.d.ts +2 -0
- package/dist/reference/render-lab-math.js +17 -5
- package/dist/reference/sections-validate.d.ts +28 -3
- package/dist/reference/sections-validate.js +31 -4
- package/dist/reference/sections.d.mts +24 -6
- package/dist/reference/sections.mjs +92 -17
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/tables-validate.js +24 -1
- package/dist/reference/text-measure.d.ts +10 -0
- package/dist/reference/text-measure.js +41 -0
- package/dist/reference/text-widths.d.ts +10 -0
- package/dist/reference/text-widths.js +1238 -0
- package/dist/reference/types.d.ts +64 -8
- package/dist/rules/catalog.js +191 -30
- package/dist/rules/in-effect.d.ts +1 -1
- package/dist/rules/rules.schema.json +79 -24
- package/dist/rules/settings.d.ts +1 -1
- package/dist/rules/settings.js +7 -0
- package/dist/session/PreferenceStore.d.ts +18 -1
- package/dist/session/PreferenceStore.js +29 -1
- package/docs/authoring/README.md +32 -21
- package/docs/authoring/agents.md +38 -7
- package/docs/authoring/components.md +111 -6
- package/docs/authoring/migrating.md +707 -0
- package/docs/authoring/notation.md +281 -29
- package/docs/authoring/retired.md +1 -0
- package/docs/authoring/structure-rules.md +10 -41
- package/docs/authoring/structure.md +53 -2
- package/docs/authoring/visualizations.md +144 -18
- package/package.json +12 -7
- package/src/compiler/astro.ts +57 -17
- package/src/compiler/cli.ts +143 -35
- package/src/compiler/collections.ts +92 -19
- package/src/compiler/content-schema.ts +108 -0
- package/src/compiler/content-schemas.ts +391 -0
- package/src/compiler/course-config.ts +86 -15
- package/src/compiler/exports/combined-html.ts +669 -0
- package/src/compiler/exports/download-index.ts +36 -0
- package/src/compiler/exports/export-chapters.ts +233 -0
- package/src/compiler/exports/export-pages.ts +44 -0
- package/src/compiler/exports/html-tree.ts +62 -0
- package/src/compiler/exports/latex/capture.ts +651 -0
- package/src/compiler/exports/latex/compile.ts +168 -0
- package/src/compiler/exports/latex/document.ts +479 -0
- package/src/compiler/exports/latex/explico.cls +415 -0
- package/src/compiler/exports/latex/numbering.ts +74 -0
- package/src/compiler/exports/latex/serialize.ts +787 -0
- package/src/compiler/exports/latex/texlive-packages.txt +76 -0
- package/src/compiler/exports/latex-export.ts +255 -0
- package/src/compiler/exports/markdown/serialize.ts +632 -0
- package/src/compiler/exports/markdown-export.ts +329 -0
- package/src/compiler/exports/standalone-html.ts +39 -70
- package/src/compiler/exports/static-tree.ts +944 -0
- package/src/compiler/exports/zip.ts +88 -0
- package/src/compiler/lesson-derivation.ts +13 -8
- package/src/compiler/manifest.ts +299 -28
- package/src/compiler/render-diagnostics.ts +111 -0
- package/src/components/HoverPanel.astro +204 -6
- package/src/components/PartOverview.astro +12 -9
- package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
- package/src/components/assessments/AssessmentRunner.tsx +7 -1
- package/src/components/assessments/AssessmentSet.astro +14 -1
- package/src/components/block-heading.ts +38 -0
- package/src/components/card-arrow.ts +18 -4
- package/src/components/card-carry.ts +406 -0
- package/src/components/card-drag.ts +1 -30
- package/src/components/card-fetch.ts +87 -0
- package/src/components/card-manager.ts +1222 -38
- package/src/components/card-memory.ts +5 -1
- package/src/components/card-placement.ts +211 -13
- package/src/components/card-side.ts +281 -31
- package/src/components/citation/CitationLayer.astro +45 -40
- package/src/components/diagram/Diagram.astro +123 -44
- package/src/components/figure/Figure.astro +11 -0
- package/src/components/figure/FloatCaption.astro +4 -17
- package/src/components/float-toolbar.ts +274 -0
- package/src/components/memo/Memo.astro +21 -10
- package/src/components/memo/ShortNote.astro +4 -0
- package/src/components/notation/NotationGlossary.astro +22 -17
- package/src/components/notation/NotationLayer.astro +321 -79
- package/src/components/notation/NotationMath.astro +8 -1
- package/src/components/notation/render-notation-math.ts +51 -5
- package/src/components/readable-text.ts +25 -0
- package/src/components/reference/FloatRefLayer.astro +162 -89
- package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
- package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
- package/src/components/starlight/LayoutPageSidebar.astro +17 -3
- package/src/components/starlight/LayoutSocialIcons.astro +79 -29
- package/src/components/starlight/LessonExportActions.astro +384 -310
- package/src/components/starlight/LessonFooter.astro +47 -9
- package/src/components/starlight/PrintController.astro +28 -0
- package/src/components/starlight/TextSizeControl.astro +137 -0
- package/src/components/starlight/print-controller.ts +380 -0
- package/src/components/starlight/section-summary.ts +13 -5
- package/src/components/table/Table.astro +13 -21
- package/src/components/table/table-truncation.ts +15 -0
- package/src/content-config.ts +20 -312
- package/src/curriculum/validation.ts +72 -24
- package/src/reference/base-abbreviations.mjs +2 -2
- package/src/reference/base-operators.mjs +2 -2
- package/src/reference/base-roman-labels.mjs +4 -3
- package/src/reference/citation-format.mjs +74 -0
- package/src/reference/consistency.ts +47 -61
- package/src/reference/curriculum-alignment.ts +4 -41
- package/src/reference/derived-entries.ts +68 -0
- package/src/reference/derived-notation.mjs +121 -0
- package/src/reference/diagram-fit.ts +104 -0
- package/src/reference/diagram-layout.ts +182 -51
- package/src/reference/diagrams-validate.ts +2 -1
- package/src/reference/equations-validate.ts +41 -2
- package/src/reference/equations.mjs +120 -22
- package/src/reference/examples-validate.ts +1 -0
- package/src/reference/figure-fit.ts +139 -0
- package/src/reference/figure-pictures.ts +96 -0
- package/src/reference/figures-validate.ts +2 -1
- package/src/reference/gate-abbreviations.ts +1 -1
- package/src/reference/gate-course-rules.ts +22 -7
- package/src/reference/gate-heading-length.ts +25 -7
- package/src/reference/gate-heading-structure.ts +40 -1
- package/src/reference/gate-indices.ts +470 -0
- package/src/reference/gate-math.ts +102 -13
- package/src/reference/gate-mdx-expression.ts +45 -0
- package/src/reference/gate-prop-text.ts +145 -0
- package/src/reference/gate-prose-notation.ts +19 -5
- package/src/reference/gate-retired-keys.ts +101 -0
- package/src/reference/gloss.ts +2 -0
- package/src/reference/katex-measure.ts +384 -0
- package/src/reference/keyed-float-validate.ts +22 -0
- package/src/reference/keyed-jsx-floats.mjs +11 -14
- package/src/reference/lab-math-scope.ts +15 -1
- package/src/reference/math-glyphs.mjs +851 -46
- package/src/reference/mixed-content.ts +11 -2
- package/src/reference/page-notation.ts +100 -3
- package/src/reference/references.ts +14 -0
- package/src/reference/registry.ts +154 -8
- package/src/reference/rehype-fail-katex-errors.mjs +19 -0
- package/src/reference/rehype-heading-anchors.mjs +11 -1
- package/src/reference/remark-citation.mjs +0 -0
- package/src/reference/remark-notation.mjs +84 -20
- package/src/reference/render-lab-math.ts +29 -5
- package/src/reference/sections-validate.ts +67 -3
- package/src/reference/sections.mjs +92 -17
- package/src/reference/tables-validate.ts +40 -2
- package/src/reference/text-measure.ts +63 -0
- package/src/reference/text-widths.ts +133 -0
- package/src/reference/types.ts +75 -8
- package/src/rules/catalog.ts +205 -29
- package/src/rules/settings.ts +10 -0
- package/src/session/PreferenceStore.ts +53 -0
- package/src/styles/global.css +291 -75
package/src/compiler/manifest.ts
CHANGED
|
@@ -91,8 +91,11 @@ import {
|
|
|
91
91
|
import {
|
|
92
92
|
gateContentMath,
|
|
93
93
|
pageScope,
|
|
94
|
+
scopeEntry,
|
|
94
95
|
type MathGateDiagnostic,
|
|
95
96
|
} from '../reference/gate-math';
|
|
97
|
+
import { gateIndices, type DerivedUse } from '../reference/gate-indices';
|
|
98
|
+
import { derivedNotationEntries } from '../reference/derived-entries';
|
|
96
99
|
import {
|
|
97
100
|
gateProseNotation,
|
|
98
101
|
gateProseNotationInAssessments,
|
|
@@ -107,12 +110,17 @@ import { gateHeadingLength } from '../reference/gate-heading-length';
|
|
|
107
110
|
import { gateHeadingStructure } from '../reference/gate-heading-structure';
|
|
108
111
|
import { gatePartPages } from '../reference/gate-part-pages';
|
|
109
112
|
import { gateNonChapterPages } from '../reference/gate-non-chapter-pages';
|
|
113
|
+
import { findRetiredKeys, findUnsupportedKeys } from '../reference/gate-retired-keys';
|
|
114
|
+
import { findMarkupInProps } from '../reference/gate-prop-text';
|
|
115
|
+
import { gateMdxExpressions } from '../reference/gate-mdx-expression';
|
|
116
|
+
import { CITATION_BRACKET, bracketCitations, withoutLinkCitations } from '../reference/citation-format.mjs';
|
|
117
|
+
import { validateContentSchemas } from './content-schema';
|
|
110
118
|
import {
|
|
111
119
|
scanSectionHeadings,
|
|
112
120
|
scanTitledBlocks,
|
|
113
121
|
} from '../reference/sections.mjs';
|
|
114
122
|
import { knowledgeCheckAnchorId } from '../reference/knowledge-checks.mjs';
|
|
115
|
-
import { validateSectionRefs } from '../reference/sections-validate';
|
|
123
|
+
import { validateHeadingAnchors, validateSectionRefs } from '../reference/sections-validate';
|
|
116
124
|
import {
|
|
117
125
|
splitMixedContent,
|
|
118
126
|
type ContentSegment,
|
|
@@ -124,7 +132,6 @@ import type { GlyphScopeEntry } from '../reference/gloss';
|
|
|
124
132
|
import { resolveReferenceId } from '../reference/references';
|
|
125
133
|
import type { NotationGlossEntry } from '../reference/types';
|
|
126
134
|
import type {
|
|
127
|
-
EditorialStatus,
|
|
128
135
|
NotationBacklink,
|
|
129
136
|
NotationDefinitionRecord,
|
|
130
137
|
NotationDiagnostic,
|
|
@@ -134,11 +141,13 @@ import type {
|
|
|
134
141
|
import {
|
|
135
142
|
coursePartsOf,
|
|
136
143
|
normalizeBlockLabels,
|
|
144
|
+
normalizeCards,
|
|
137
145
|
normalizeLessonExports,
|
|
138
146
|
normalizeDisclosure,
|
|
139
147
|
normalizeNumbering,
|
|
140
148
|
type BlockLabels,
|
|
141
149
|
type CourseConfig,
|
|
150
|
+
type NormalizedCards,
|
|
142
151
|
type NormalizedLessonExports,
|
|
143
152
|
type NormalizedDisclosure,
|
|
144
153
|
type NormalizedNumbering,
|
|
@@ -163,6 +172,7 @@ import {
|
|
|
163
172
|
} from './knowledge-version';
|
|
164
173
|
import {
|
|
165
174
|
loadAbbreviationRegistryInput,
|
|
175
|
+
loadContentRecords,
|
|
166
176
|
loadCurriculumCatalog,
|
|
167
177
|
loadNotationDefinitions,
|
|
168
178
|
loadNotationRegistryInput,
|
|
@@ -302,8 +312,42 @@ import {
|
|
|
302
312
|
* `id`, `slug`, `file` and a `notation` bundle of the lesson shape, so the
|
|
303
313
|
* footer renders the cards its `[[key]]` terms and math open.
|
|
304
314
|
* `notation.bundles` and `notation.backlinks` now carry those pages' ids too.
|
|
315
|
+
*
|
|
316
|
+
* v26 (carried cards, additive): `cards` — the course's `cards` settings with
|
|
317
|
+
* the engine defaults applied: whether "Carry cards" starts on (rule K1).
|
|
318
|
+
*
|
|
319
|
+
* v27 (LaTeX export): `lessonExports.latex` and `lessonExports.latexPaper`;
|
|
320
|
+
* every `assessmentContent` item carries its `type`, a choice item's
|
|
321
|
+
* `options` (plain-text labels) and `correctOptionId`, and a
|
|
322
|
+
* numeric item's `answer`, so an answer key can be printed without re-reading
|
|
323
|
+
* `content/assessments/`.
|
|
324
|
+
*
|
|
325
|
+
* v28 (Markdown export): `lessonExports.markdown`.
|
|
326
|
+
*
|
|
327
|
+
* v29 (no editorial status): the draft / in-review / reviewed flag is gone —
|
|
328
|
+
* `lessons[].status`, `editorialStatus` on the notation definitions of
|
|
329
|
+
* `lessons[]` and `pages[]`, and `status` on `notation.definitions[]` and
|
|
330
|
+
* `notation.bundles[]`. `diagnostics.curriculum` carries `retired-key`, with
|
|
331
|
+
* `file` and `line`, for a content file that still sets the key, and the
|
|
332
|
+
* lesson-assumptions check reports `missing-assumptions`, not `review-state`.
|
|
333
|
+
*
|
|
334
|
+
* v30 (headings with math, additive): `lessons[].headings[].text` is what a
|
|
335
|
+
* reader sees — inline math as it reads (`$\explain{k}{R}$` is `R`, not its
|
|
336
|
+
* LaTeX) and a `[[key]]` term as its label — and `math: true` marks a heading
|
|
337
|
+
* with inline math. A heading's `slug` is unchanged. Each choice option of
|
|
338
|
+
* `assessmentContent` carries `labelSegments`, its label split into text and
|
|
339
|
+
* math like `promptSegments`, so a label's math renders.
|
|
340
|
+
*
|
|
341
|
+
* v31 (indexed symbols, rule N23): the `notation` bundle of `lessons[]` and
|
|
342
|
+
* `pages[]` gains `derived` — the derived cards its body math uses, each
|
|
343
|
+
* composed from a base entry and an index entry (`\mathbf{v}_0` from
|
|
344
|
+
* `\mathbf{v}` and `{}_0`), keyed `<base>.sub.<index>`. A notation
|
|
345
|
+
* definition carries `reads`, `range`, `fixedIndex` and an index entry's
|
|
346
|
+
* `formulaTemplate` when its entry sets them, and a bundle definition its
|
|
347
|
+
* `range`. `diagnostics.math` carries `index-explained`, `index-swallowed`,
|
|
348
|
+
* `index-range` and `derived-key-taken`.
|
|
305
349
|
*/
|
|
306
|
-
export const MANIFEST_SCHEMA_VERSION =
|
|
350
|
+
export const MANIFEST_SCHEMA_VERSION = 31;
|
|
307
351
|
|
|
308
352
|
const REPOSITORY_ROOT = process.cwd();
|
|
309
353
|
// Content is the top-level `content/` folder; the build output is `build/`
|
|
@@ -373,10 +417,11 @@ export interface ManifestLessonNotation {
|
|
|
373
417
|
readonly notation: string;
|
|
374
418
|
readonly label: string;
|
|
375
419
|
readonly summary: string;
|
|
376
|
-
readonly editorialStatus: EditorialStatus;
|
|
377
420
|
readonly seeAlso: readonly string[];
|
|
378
421
|
readonly units?: string;
|
|
379
422
|
readonly formula?: string;
|
|
423
|
+
/** Rule N23: the set a running index letter runs over, shown on its card (v31). */
|
|
424
|
+
readonly range?: string;
|
|
380
425
|
/** Symbols the formula names that have no card of their own (D15). */
|
|
381
426
|
readonly glosses: readonly NotationGlossEntry[];
|
|
382
427
|
/**
|
|
@@ -387,6 +432,27 @@ export interface ManifestLessonNotation {
|
|
|
387
432
|
readonly formulaScope: readonly GlyphScopeEntry[];
|
|
388
433
|
}
|
|
389
434
|
|
|
435
|
+
/**
|
|
436
|
+
* A derived card (rule N23, v31): the card of a symbol with a subscript that
|
|
437
|
+
* an index entry explains, composed from the `base` entry and the `index`
|
|
438
|
+
* entry by `derived-notation.mjs`. Its title and summary are about the
|
|
439
|
+
* indexed symbol; its formula names the base, whose card is one level deeper.
|
|
440
|
+
* It is listed nowhere — not in the Page notation panel, the glossary or an
|
|
441
|
+
* export's notation section — and opens only from the symbol.
|
|
442
|
+
*/
|
|
443
|
+
export interface ManifestDerivedNotation {
|
|
444
|
+
readonly key: string;
|
|
445
|
+
readonly base: string;
|
|
446
|
+
readonly index: string;
|
|
447
|
+
readonly notation: string;
|
|
448
|
+
readonly label: string;
|
|
449
|
+
readonly summary: string;
|
|
450
|
+
readonly units?: string;
|
|
451
|
+
readonly formula?: string;
|
|
452
|
+
readonly glosses: readonly NotationGlossEntry[];
|
|
453
|
+
readonly formulaScope: readonly GlyphScopeEntry[];
|
|
454
|
+
}
|
|
455
|
+
|
|
390
456
|
/**
|
|
391
457
|
* One keyed float of a lesson (v21): its key and number, and its caption pair
|
|
392
458
|
* — `short` (an equation's `\caption{…}`, or the `short` prop) and, for a
|
|
@@ -456,16 +522,18 @@ export interface ManifestLesson {
|
|
|
456
522
|
*/
|
|
457
523
|
readonly headings: readonly {
|
|
458
524
|
readonly slug: string;
|
|
525
|
+
/** What a reader sees: math as it reads, a `[[key]]` term as its label (v30). */
|
|
459
526
|
readonly text: string;
|
|
460
527
|
readonly level: number;
|
|
461
528
|
readonly number?: string;
|
|
529
|
+
/** The heading has inline math (v30). */
|
|
530
|
+
readonly math?: true;
|
|
462
531
|
}[];
|
|
463
532
|
readonly file: string;
|
|
464
533
|
readonly title: string;
|
|
465
534
|
readonly description: string;
|
|
466
535
|
/** Authored reader-facing estimate; `40` and `[30, 40]` both land here. */
|
|
467
536
|
readonly studyMinutes?: { readonly min: number; readonly max: number };
|
|
468
|
-
readonly status: EditorialStatus;
|
|
469
537
|
/** Ordered — the curriculum contract. */
|
|
470
538
|
readonly teaches: readonly string[];
|
|
471
539
|
readonly requires: readonly string[];
|
|
@@ -509,6 +577,8 @@ export interface ManifestLesson {
|
|
|
509
577
|
}[];
|
|
510
578
|
readonly definitionIds: readonly string[];
|
|
511
579
|
readonly definitions: readonly ManifestLessonNotation[];
|
|
580
|
+
/** The derived cards (rule N23) the page's body math uses (v31). */
|
|
581
|
+
readonly derived: readonly ManifestDerivedNotation[];
|
|
512
582
|
/** Every `[[key]]` / `\explain{key}` occurrence, resolved and located. */
|
|
513
583
|
readonly resolution: readonly ManifestNotationResolution[];
|
|
514
584
|
};
|
|
@@ -562,6 +632,22 @@ export interface ManifestAssessmentItemContent {
|
|
|
562
632
|
readonly itemId: string;
|
|
563
633
|
readonly promptSegments: readonly ContentSegment[];
|
|
564
634
|
readonly explanationSegments: readonly ContentSegment[];
|
|
635
|
+
/** What kind of answer the item takes (v27). */
|
|
636
|
+
readonly type: 'single-choice' | 'numeric';
|
|
637
|
+
/**
|
|
638
|
+
* A choice item's options in authored order: the label as authored, and
|
|
639
|
+
* split into text and math like the prompt (`labelSegments`, v30), so a
|
|
640
|
+
* label's math renders.
|
|
641
|
+
*/
|
|
642
|
+
readonly options?: readonly {
|
|
643
|
+
readonly id: string;
|
|
644
|
+
readonly label: string;
|
|
645
|
+
readonly labelSegments: readonly ContentSegment[];
|
|
646
|
+
}[];
|
|
647
|
+
/** A choice item's correct option. */
|
|
648
|
+
readonly correctOptionId?: string;
|
|
649
|
+
/** A numeric item's accepted value and tolerance. */
|
|
650
|
+
readonly answer?: { readonly value: number; readonly tolerance: number };
|
|
565
651
|
}
|
|
566
652
|
|
|
567
653
|
/**
|
|
@@ -604,6 +690,8 @@ export interface Manifest {
|
|
|
604
690
|
readonly numbering: NormalizedNumbering;
|
|
605
691
|
/** How a check's and an example set's heading names its kind (`CourseConfig.blockLabels`). */
|
|
606
692
|
readonly blockLabels: BlockLabels;
|
|
693
|
+
/** How the explanation cards start for a reader, defaults applied (`CourseConfig.cards`). */
|
|
694
|
+
readonly cards: NormalizedCards;
|
|
607
695
|
/** Reader-facing identity + versions (the `[?]` About panel). */
|
|
608
696
|
readonly about: ManifestAbout;
|
|
609
697
|
readonly notation: {
|
|
@@ -723,7 +811,6 @@ export interface Manifest {
|
|
|
723
811
|
|
|
724
812
|
// --- compile ---------------------------------------------------------
|
|
725
813
|
|
|
726
|
-
const CITE_PATTERN = /\[@([a-z0-9][a-z0-9.-]*)(?:;[ \t\r\n]*([^\]]*))?\]/g;
|
|
727
814
|
|
|
728
815
|
/** Distinct `[@id; locator]` occurrences in a body, in first-appearance order. */
|
|
729
816
|
function lessonCitations(body: string): ManifestCitation[] {
|
|
@@ -732,13 +819,16 @@ function lessonCitations(body: string): ManifestCitation[] {
|
|
|
732
819
|
.replace(/`[^`\n]*`/g, '');
|
|
733
820
|
const seen = new Set<string>();
|
|
734
821
|
const citations: ManifestCitation[] = [];
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
822
|
+
// A citation in a link's text renders as typed (`citation-in-link`).
|
|
823
|
+
for (const match of withoutLinkCitations(withoutCode).matchAll(CITATION_BRACKET)) {
|
|
824
|
+
for (const citation of bracketCitations(match[1] ?? '', match[2])) {
|
|
825
|
+
const id = citation.id;
|
|
826
|
+
const locator = citation.locator || undefined;
|
|
827
|
+
const dedupeKey = `${id} ${locator ?? ''}`;
|
|
828
|
+
if (seen.has(dedupeKey)) continue;
|
|
829
|
+
seen.add(dedupeKey);
|
|
830
|
+
citations.push(locator === undefined ? { id } : { id, locator });
|
|
831
|
+
}
|
|
742
832
|
}
|
|
743
833
|
return citations;
|
|
744
834
|
}
|
|
@@ -802,7 +892,9 @@ function lessonNotationResolution(
|
|
|
802
892
|
: 'local'
|
|
803
893
|
: 'unresolved',
|
|
804
894
|
definitionId: definitionId ?? null,
|
|
805
|
-
|
|
895
|
+
// A page's own references are prose or math; a definition's formula
|
|
896
|
+
// reference is reported as the definition reference it always was.
|
|
897
|
+
kind: reference.kind === 'formula' ? 'definition' : reference.kind,
|
|
806
898
|
line: reference.source.line ?? null,
|
|
807
899
|
column: reference.source.column ?? null,
|
|
808
900
|
};
|
|
@@ -814,11 +906,14 @@ function lessonNotationBundle(
|
|
|
814
906
|
definitionsById: ReadonlyMap<string, NotationDefinitionRecord>,
|
|
815
907
|
resolution: readonly ManifestNotationResolution[],
|
|
816
908
|
cardsByKey: ReadonlyMap<string, GlyphScopeEntry>,
|
|
909
|
+
derivedUses: readonly DerivedUse[] = [],
|
|
817
910
|
): ManifestLesson['notation'] {
|
|
818
911
|
const definitions: ManifestLessonNotation[] = [];
|
|
912
|
+
const records: NotationDefinitionRecord[] = [];
|
|
819
913
|
for (const definitionId of bundle?.definitionIds ?? []) {
|
|
820
914
|
const definition = definitionsById.get(definitionId);
|
|
821
915
|
if (!definition) continue;
|
|
916
|
+
records.push(definition);
|
|
822
917
|
definitions.push({
|
|
823
918
|
id: definition.id,
|
|
824
919
|
key: definition.key,
|
|
@@ -826,7 +921,6 @@ function lessonNotationBundle(
|
|
|
826
921
|
notation: definition.notation,
|
|
827
922
|
label: definition.label,
|
|
828
923
|
summary: definition.summary,
|
|
829
|
-
editorialStatus: definition.status,
|
|
830
924
|
seeAlso: [...definition.seeAlso],
|
|
831
925
|
glosses: [...definition.glosses],
|
|
832
926
|
formulaScope: formulaGlyphScope({
|
|
@@ -842,12 +936,18 @@ function lessonNotationBundle(
|
|
|
842
936
|
...(definition.formula === undefined
|
|
843
937
|
? {}
|
|
844
938
|
: { formula: definition.formula }),
|
|
939
|
+
...(definition.range === undefined ? {} : { range: definition.range }),
|
|
845
940
|
});
|
|
846
941
|
}
|
|
847
942
|
return {
|
|
848
943
|
bindings: [...(bundle?.bindings ?? [])].map((binding) => ({ ...binding })),
|
|
849
944
|
definitionIds: [...(bundle?.definitionIds ?? [])],
|
|
850
945
|
definitions,
|
|
946
|
+
derived: derivedNotationEntries(
|
|
947
|
+
derivedUses,
|
|
948
|
+
new Map(records.map((record) => [record.key, record])),
|
|
949
|
+
cardsByKey,
|
|
950
|
+
),
|
|
851
951
|
resolution: [...resolution],
|
|
852
952
|
};
|
|
853
953
|
}
|
|
@@ -975,7 +1075,7 @@ export async function compileManifest(
|
|
|
975
1075
|
const cardsByKey = new Map<string, GlyphScopeEntry>(
|
|
976
1076
|
notationInput.sharedDefinitions.map((definition) => [
|
|
977
1077
|
definition.key,
|
|
978
|
-
|
|
1078
|
+
scopeEntry(definition),
|
|
979
1079
|
]),
|
|
980
1080
|
);
|
|
981
1081
|
const definitionsById = new Map(
|
|
@@ -989,16 +1089,62 @@ export async function compileManifest(
|
|
|
989
1089
|
const assessmentsInput = catalog.assessments as unknown as {
|
|
990
1090
|
id: string;
|
|
991
1091
|
title?: string;
|
|
992
|
-
items?: {
|
|
1092
|
+
items?: {
|
|
1093
|
+
id?: string;
|
|
1094
|
+
type?: string;
|
|
1095
|
+
prompt?: string;
|
|
1096
|
+
explanation?: string;
|
|
1097
|
+
options?: { id?: string; label?: string }[];
|
|
1098
|
+
correctOptionId?: string;
|
|
1099
|
+
answer?: { value?: number; tolerance?: number };
|
|
1100
|
+
}[];
|
|
993
1101
|
}[];
|
|
994
1102
|
|
|
995
|
-
const
|
|
1103
|
+
const contentRecords = loadContentRecords();
|
|
1104
|
+
const curriculumIssues = [
|
|
1105
|
+
...validateCurriculum(catalog),
|
|
1106
|
+
...validateContentSchemas(contentRecords),
|
|
1107
|
+
...findRetiredKeys(contentRecords),
|
|
1108
|
+
...findUnsupportedKeys(contentRecords),
|
|
1109
|
+
...findMarkupInProps(contentRecords),
|
|
1110
|
+
];
|
|
996
1111
|
const alignmentIssues = validateNotationAlignment(registry, catalog);
|
|
997
1112
|
const mathEndAbbreviations = {
|
|
998
1113
|
allow: rules.strings('N12', 'allow'),
|
|
999
1114
|
deny: rules.strings('N12', 'deny'),
|
|
1000
1115
|
};
|
|
1116
|
+
// A heading's text is what the reader sees: a `[[key]]` term in it reads as
|
|
1117
|
+
// its label, the page's own symbol first (rule S1 measures it, and the
|
|
1118
|
+
// outline records it).
|
|
1119
|
+
const sharedLabels = new Map(
|
|
1120
|
+
notationInput.sharedDefinitions.map((definition) => [definition.key, definition.label]),
|
|
1121
|
+
);
|
|
1122
|
+
const localLabelsByLesson = new Map(
|
|
1123
|
+
notationInput.lessons.map((lesson) => [
|
|
1124
|
+
lesson.lessonId,
|
|
1125
|
+
new Map(lesson.localDefinitions.map((definition) => [definition.key, definition.label])),
|
|
1126
|
+
]),
|
|
1127
|
+
);
|
|
1128
|
+
const termLabel = (lessonId: string, key: string): string | undefined =>
|
|
1129
|
+
localLabelsByLesson.get(lessonId)?.get(key) ?? sharedLabels.get(key);
|
|
1130
|
+
// Rule N23 and the derived cards a page uses: one resolver pass over every
|
|
1131
|
+
// body. The cards are built whatever the rule's switch says; its findings
|
|
1132
|
+
// drop with the rule (`dropDisabled`) and yield to a waiver.
|
|
1133
|
+
const indexGate = gateIndices(
|
|
1134
|
+
withPages.lessons,
|
|
1135
|
+
new Map(
|
|
1136
|
+
notationInput.sharedDefinitions.map((definition) => [definition.key, definition]),
|
|
1137
|
+
),
|
|
1138
|
+
{
|
|
1139
|
+
assessments: assessmentsInput,
|
|
1140
|
+
lessonAssessments: catalog.lessons.map((lesson) => ({
|
|
1141
|
+
lessonId: lesson.id,
|
|
1142
|
+
assessmentIds: lesson.assessments,
|
|
1143
|
+
})),
|
|
1144
|
+
},
|
|
1145
|
+
);
|
|
1001
1146
|
const mathIssues = [
|
|
1147
|
+
...indexGate.diagnostics,
|
|
1002
1148
|
...gateContentMath({
|
|
1003
1149
|
notationInput: withPages,
|
|
1004
1150
|
assessments: assessmentsInput,
|
|
@@ -1007,6 +1153,7 @@ export async function compileManifest(
|
|
|
1007
1153
|
assessmentIds: lesson.assessments,
|
|
1008
1154
|
})),
|
|
1009
1155
|
}),
|
|
1156
|
+
...gateMdxExpressions(withPages.lessons),
|
|
1010
1157
|
...(rules.isEnabled('N9')
|
|
1011
1158
|
? [
|
|
1012
1159
|
...gateProseNotation(withPages.lessons, {
|
|
@@ -1033,6 +1180,7 @@ export async function compileManifest(
|
|
|
1033
1180
|
...(rules.isEnabled('S1')
|
|
1034
1181
|
? gateHeadingLength(notationInput.lessons, {
|
|
1035
1182
|
maxLength: rules.number('S1', 'maxLength'),
|
|
1183
|
+
labelFor: termLabel,
|
|
1036
1184
|
})
|
|
1037
1185
|
: []),
|
|
1038
1186
|
...gateHeadingStructure(notationInput.lessons, {
|
|
@@ -1061,22 +1209,57 @@ export async function compileManifest(
|
|
|
1061
1209
|
assessmentsInput.map((assessment) => [assessment.id, assessment]),
|
|
1062
1210
|
);
|
|
1063
1211
|
const assessmentContent: ManifestAssessmentItemContent[] = [];
|
|
1212
|
+
// An assessment is page math: its indexed symbols take derived cards
|
|
1213
|
+
// (rule N23), which the lesson then ships with its body's.
|
|
1214
|
+
const assessmentDerived = new Map<string, Map<string, DerivedUse>>();
|
|
1064
1215
|
for (const lesson of catalog.lessons) {
|
|
1065
1216
|
if (lesson.assessments.length === 0) continue;
|
|
1066
1217
|
const lessonInput = notationLessonById.get(lesson.id);
|
|
1067
1218
|
if (!lessonInput) continue;
|
|
1068
1219
|
const scope = pageScope(lessonInput, cardsByKey);
|
|
1220
|
+
const derived = new Map<string, DerivedUse>();
|
|
1221
|
+
assessmentDerived.set(lesson.id, derived);
|
|
1222
|
+
const page = { derive: true, derived };
|
|
1069
1223
|
for (const assessmentId of lesson.assessments) {
|
|
1070
1224
|
const assessment = assessmentInputById.get(assessmentId);
|
|
1071
1225
|
if (!assessment?.items) continue;
|
|
1072
1226
|
for (const item of assessment.items) {
|
|
1073
1227
|
if (!item.id) continue;
|
|
1228
|
+
const numeric =
|
|
1229
|
+
item.type === 'numeric' &&
|
|
1230
|
+
typeof item.answer?.value === 'number' &&
|
|
1231
|
+
typeof item.answer.tolerance === 'number';
|
|
1074
1232
|
assessmentContent.push({
|
|
1075
1233
|
lessonId: lesson.id,
|
|
1076
1234
|
assessmentId,
|
|
1077
1235
|
itemId: item.id,
|
|
1078
|
-
promptSegments: splitMixedContent(item.prompt ?? '', scope),
|
|
1079
|
-
explanationSegments: splitMixedContent(item.explanation ?? '', scope),
|
|
1236
|
+
promptSegments: splitMixedContent(item.prompt ?? '', scope, page),
|
|
1237
|
+
explanationSegments: splitMixedContent(item.explanation ?? '', scope, page),
|
|
1238
|
+
type: item.type === 'numeric' ? 'numeric' : 'single-choice',
|
|
1239
|
+
...(item.options
|
|
1240
|
+
? {
|
|
1241
|
+
options: item.options.flatMap((option) =>
|
|
1242
|
+
option.id
|
|
1243
|
+
? [
|
|
1244
|
+
{
|
|
1245
|
+
id: option.id,
|
|
1246
|
+
label: option.label ?? '',
|
|
1247
|
+
labelSegments: splitMixedContent(option.label ?? '', scope, page),
|
|
1248
|
+
},
|
|
1249
|
+
]
|
|
1250
|
+
: [],
|
|
1251
|
+
),
|
|
1252
|
+
}
|
|
1253
|
+
: {}),
|
|
1254
|
+
...(item.correctOptionId ? { correctOptionId: item.correctOptionId } : {}),
|
|
1255
|
+
...(numeric
|
|
1256
|
+
? {
|
|
1257
|
+
answer: {
|
|
1258
|
+
value: item.answer!.value!,
|
|
1259
|
+
tolerance: item.answer!.tolerance!,
|
|
1260
|
+
},
|
|
1261
|
+
}
|
|
1262
|
+
: {}),
|
|
1080
1263
|
});
|
|
1081
1264
|
}
|
|
1082
1265
|
}
|
|
@@ -1106,9 +1289,44 @@ export async function compileManifest(
|
|
|
1106
1289
|
const lessonSlugs = [...notationInput.lessons]
|
|
1107
1290
|
.map((lesson) => lesson.lessonId.replace(/\./g, '/'))
|
|
1108
1291
|
.sort();
|
|
1109
|
-
|
|
1110
|
-
|
|
1292
|
+
// A part takes a sidebar group and an ordinal once it has a chapter or an
|
|
1293
|
+
// overview page; one with neither yet (planned, its directory not written)
|
|
1294
|
+
// is left out of both, with a warning, so every part number — a lesson's,
|
|
1295
|
+
// `parts[]`, the LaTeX export's — counts the same parts.
|
|
1296
|
+
const listedParts = coursePartsOf(courseConfig);
|
|
1297
|
+
const overviewPages = loadPartOverviews(listedParts);
|
|
1111
1298
|
const overviewByDir = new Map(overviewPages.map((page) => [page.dir, page]));
|
|
1299
|
+
const hasChapter = (dir: string) => lessonSlugs.some((slug) => slug.startsWith(`${dir}/`));
|
|
1300
|
+
const configuredParts = listedParts.filter(
|
|
1301
|
+
(part) => hasChapter(part.dir) || overviewByDir.has(part.dir),
|
|
1302
|
+
);
|
|
1303
|
+
for (const part of listedParts) {
|
|
1304
|
+
if (configuredParts.includes(part)) continue;
|
|
1305
|
+
curriculumIssues.push({
|
|
1306
|
+
kind: 'part-empty',
|
|
1307
|
+
severity: 'warning',
|
|
1308
|
+
message:
|
|
1309
|
+
`course.settings.json \`parts\` lists "${part.dir}" (${JSON.stringify(part.label)}), but ` +
|
|
1310
|
+
`content/docs/${part.dir}/ has no chapter and no overview page yet, so the part ` +
|
|
1311
|
+
'is left out of the sidebar and the numbering until it has one.',
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
// A lesson whose directory no part lists has no part, no chapter number and
|
|
1315
|
+
// no place in the sidebar.
|
|
1316
|
+
const partDirs = new Set(listedParts.map((part) => part.dir));
|
|
1317
|
+
for (const lesson of notationInput.lessons) {
|
|
1318
|
+
const dir = lesson.lessonId.split('.')[0] ?? '';
|
|
1319
|
+
if (partDirs.has(dir)) continue;
|
|
1320
|
+
curriculumIssues.push({
|
|
1321
|
+
kind: 'lesson-outside-parts',
|
|
1322
|
+
message:
|
|
1323
|
+
`${lesson.source.file} is in content/docs/${dir}/, which no part in ` +
|
|
1324
|
+
`course.settings.json \`parts\` lists, so it has no part, no chapter number and no ` +
|
|
1325
|
+
`place in the sidebar. Add { "dir": "${dir}", "label": "…" } to \`parts\`, or move the ` +
|
|
1326
|
+
"lesson into a part's directory.",
|
|
1327
|
+
file: lesson.source.file,
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1112
1330
|
// S2: every authored part page carries its bridge and scope paragraphs.
|
|
1113
1331
|
if (rules.isEnabled('S2')) {
|
|
1114
1332
|
mathIssues.push(
|
|
@@ -1175,12 +1393,14 @@ export async function compileManifest(
|
|
|
1175
1393
|
>();
|
|
1176
1394
|
const chapterScoped = numberingSettings.floats === 'chapter';
|
|
1177
1395
|
let partCursor = 0;
|
|
1178
|
-
|
|
1396
|
+
// The part groups, in `configuredParts` order; the static Reference group
|
|
1397
|
+
// after them is no part. An overview-only part counts, as `parts[]` does.
|
|
1398
|
+
for (const group of sidebar.slice(0, configuredParts.length)) {
|
|
1399
|
+
partCursor += 1;
|
|
1179
1400
|
const lessonIds = group.items
|
|
1180
1401
|
.filter((item): item is string => typeof item === 'string')
|
|
1181
1402
|
.map(lessonIdFromSlug);
|
|
1182
|
-
if (lessonIds.length === 0) continue;
|
|
1183
|
-
partCursor += 1;
|
|
1403
|
+
if (lessonIds.length === 0) continue;
|
|
1184
1404
|
// Running totals for this part, advanced one chapter at a time; they
|
|
1185
1405
|
// only reach a number under the part scope.
|
|
1186
1406
|
const used = {
|
|
@@ -1232,6 +1452,7 @@ export async function compileManifest(
|
|
|
1232
1452
|
? { prefix: `${placement.part}.${placement.chapter}` }
|
|
1233
1453
|
: {}),
|
|
1234
1454
|
depth: numberingSettings.sections.depth,
|
|
1455
|
+
labelFor: (key: string) => termLabel(lesson.lessonId, key),
|
|
1235
1456
|
}) as ManifestLesson['headings'],
|
|
1236
1457
|
];
|
|
1237
1458
|
}),
|
|
@@ -1247,6 +1468,7 @@ export async function compileManifest(
|
|
|
1247
1468
|
(heading) => heading.slug,
|
|
1248
1469
|
),
|
|
1249
1470
|
})),
|
|
1471
|
+
nonChapterPages.map((page) => page.slug),
|
|
1250
1472
|
),
|
|
1251
1473
|
);
|
|
1252
1474
|
|
|
@@ -1385,6 +1607,52 @@ export async function compileManifest(
|
|
|
1385
1607
|
}),
|
|
1386
1608
|
]),
|
|
1387
1609
|
);
|
|
1610
|
+
// A heading's anchor must not be one the engine gives something else on
|
|
1611
|
+
// the page (`heading-anchor-collision`, keyed-floats invariant).
|
|
1612
|
+
const engineAnchorsFor = (lessonId: string, body: string): Map<string, string> => {
|
|
1613
|
+
const slug = lessonId.replace(/\./g, '/');
|
|
1614
|
+
const anchors = new Map<string, string>();
|
|
1615
|
+
const keyed = (
|
|
1616
|
+
floats: readonly { readonly key: string }[],
|
|
1617
|
+
prefix: string,
|
|
1618
|
+
owner: (key: string) => string,
|
|
1619
|
+
) => {
|
|
1620
|
+
for (const float of floats) anchors.set(`${prefix}-${float.key}`, owner(float.key));
|
|
1621
|
+
};
|
|
1622
|
+
keyed(equationsBySlug.get(slug) ?? [], 'eq', (key) => `the equation labelled eq:${key}`);
|
|
1623
|
+
keyed(tablesBySlug.get(slug) ?? [], 'tab', (key) => `<Table id="${key}">`);
|
|
1624
|
+
keyed(figuresBySlug.get(slug) ?? [], 'fig', (key) => `<Figure id="${key}">`);
|
|
1625
|
+
keyed(diagramsBySlug.get(slug) ?? [], 'dia', (key) => `<Diagram id="${key}">`);
|
|
1626
|
+
keyed(examplesBySlug.get(slug) ?? [], 'ex', (key) => `<CompactExamples id="${key}">`);
|
|
1627
|
+
for (const check of checksBySlug.get(slug) ?? []) {
|
|
1628
|
+
anchors.set(knowledgeCheckAnchorId(check.key), `the knowledge check ${check.key}`);
|
|
1629
|
+
}
|
|
1630
|
+
for (const [name, prefix] of [
|
|
1631
|
+
['Memo', 'memo'],
|
|
1632
|
+
['ShortNote', 'short-note'],
|
|
1633
|
+
] as const) {
|
|
1634
|
+
const tag = new RegExp(`<${name}\\b[^>]*?\\bid=["']([^"']+)["']`, 'g');
|
|
1635
|
+
for (const match of body.matchAll(tag)) {
|
|
1636
|
+
anchors.set(`${prefix}-${match[1]}`, `<${name} id="${match[1]}">`);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
if (deriveSources(body).length > 0) {
|
|
1640
|
+
anchors.set('references', 'the References section generated from the page’s citations');
|
|
1641
|
+
}
|
|
1642
|
+
return anchors;
|
|
1643
|
+
};
|
|
1644
|
+
mathIssues.push(
|
|
1645
|
+
...validateHeadingAnchors(
|
|
1646
|
+
notationInput.lessons.map((lesson) => ({
|
|
1647
|
+
file: lesson.source.file,
|
|
1648
|
+
body: lesson.body,
|
|
1649
|
+
...(lesson.bodyLine === undefined ? {} : { bodyLine: lesson.bodyLine }),
|
|
1650
|
+
headings: headingsByLessonId.get(lesson.lessonId) ?? [],
|
|
1651
|
+
anchors: engineAnchorsFor(lesson.lessonId, lesson.body),
|
|
1652
|
+
})),
|
|
1653
|
+
),
|
|
1654
|
+
);
|
|
1655
|
+
|
|
1388
1656
|
// The blocks that draw an `<h2>` where their tag stands, placed after the
|
|
1389
1657
|
// outline heading they follow — "On this page" lists them there (v23).
|
|
1390
1658
|
// `<AssessmentSet />` draws every check of the lesson, or the one its `id`
|
|
@@ -1470,7 +1738,6 @@ export async function compileManifest(
|
|
|
1470
1738
|
...(frontmatter.studyMinutes === undefined
|
|
1471
1739
|
? {}
|
|
1472
1740
|
: { studyMinutes: frontmatter.studyMinutes }),
|
|
1473
|
-
status: meta.status,
|
|
1474
1741
|
teaches: [...teaches],
|
|
1475
1742
|
requires: [...meta.requires],
|
|
1476
1743
|
assumptions: [...meta.assumptions],
|
|
@@ -1500,6 +1767,10 @@ export async function compileManifest(
|
|
|
1500
1767
|
availableDefinitionIds,
|
|
1501
1768
|
),
|
|
1502
1769
|
cardsByKey,
|
|
1770
|
+
[
|
|
1771
|
+
...(indexGate.derivedByLesson.get(id) ?? []),
|
|
1772
|
+
...(assessmentDerived.get(id)?.values() ?? []),
|
|
1773
|
+
],
|
|
1503
1774
|
),
|
|
1504
1775
|
});
|
|
1505
1776
|
}
|
|
@@ -1518,6 +1789,7 @@ export async function compileManifest(
|
|
|
1518
1789
|
availableDefinitionIds,
|
|
1519
1790
|
),
|
|
1520
1791
|
cardsByKey,
|
|
1792
|
+
indexGate.derivedByLesson.get(page.id),
|
|
1521
1793
|
),
|
|
1522
1794
|
}))
|
|
1523
1795
|
.sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -1526,9 +1798,7 @@ export async function compileManifest(
|
|
|
1526
1798
|
registry,
|
|
1527
1799
|
notationInput: withPages,
|
|
1528
1800
|
specs: loadNotationSpecs(),
|
|
1529
|
-
catalog,
|
|
1530
1801
|
lintIgnore: loadLintIgnore(),
|
|
1531
|
-
conventions: courseConfig.conventions,
|
|
1532
1802
|
operators: rules.settings('N8'),
|
|
1533
1803
|
romanLabels: rules.settings('N10'),
|
|
1534
1804
|
accents: rules.settings('N22'),
|
|
@@ -1603,6 +1873,7 @@ export async function compileManifest(
|
|
|
1603
1873
|
disclosure: normalizeDisclosure(courseConfig),
|
|
1604
1874
|
numbering: numberingSettings,
|
|
1605
1875
|
blockLabels,
|
|
1876
|
+
cards: normalizeCards(courseConfig),
|
|
1606
1877
|
about,
|
|
1607
1878
|
notation: {
|
|
1608
1879
|
definitions: registry.definitions,
|