explico 0.4.0 → 0.6.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 +382 -1
- package/README.md +103 -8
- package/dist/compiler/astro.d.ts +9 -1
- package/dist/compiler/astro.js +182 -3
- package/dist/compiler/cli.d.ts +13 -0
- package/dist/compiler/cli.js +28 -4
- package/dist/compiler/collections.d.ts +3 -2
- package/dist/compiler/collections.js +28 -1
- package/dist/compiler/course-config.d.ts +88 -1
- package/dist/compiler/course-config.js +35 -1
- package/dist/compiler/exports/standalone-html.d.ts +34 -0
- package/dist/compiler/exports/standalone-html.js +803 -0
- package/dist/compiler/manifest.d.ts +98 -6
- package/dist/compiler/manifest.js +154 -17
- package/dist/compiler/resolution-report.d.ts +4 -0
- package/dist/reference/base-abbreviations.d.mts +30 -0
- package/dist/reference/base-abbreviations.mjs +60 -0
- package/dist/reference/base-operators.d.mts +15 -0
- package/dist/reference/base-operators.mjs +40 -0
- package/dist/reference/base-roman-labels.d.mts +35 -0
- package/dist/reference/base-roman-labels.mjs +43 -0
- package/dist/reference/consistency.d.ts +8 -2
- package/dist/reference/consistency.js +50 -3
- package/dist/reference/diagrams-validate.d.ts +21 -0
- package/dist/reference/diagrams-validate.js +13 -0
- package/dist/reference/equations.d.mts +10 -15
- package/dist/reference/equations.mjs +67 -14
- package/dist/reference/gate-abbreviations.d.ts +17 -0
- package/dist/reference/gate-abbreviations.js +64 -0
- package/dist/reference/gate-heading-length.d.ts +12 -0
- package/dist/reference/gate-heading-length.js +79 -0
- package/dist/reference/gate-math.d.ts +32 -5
- package/dist/reference/gate-math.js +6 -2
- package/dist/reference/gate-prose-notation.d.ts +60 -0
- package/dist/reference/gate-prose-notation.js +125 -0
- package/dist/reference/keyed-float-validate.d.ts +5 -5
- package/dist/reference/keyed-jsx-floats.d.mts +7 -12
- package/dist/reference/keyed-jsx-floats.mjs +50 -25
- package/dist/reference/knowledge-checks-validate.d.ts +35 -0
- package/dist/reference/knowledge-checks-validate.js +78 -0
- package/dist/reference/knowledge-checks.d.mts +25 -0
- package/dist/reference/knowledge-checks.mjs +45 -0
- package/dist/reference/lab-math-scope.d.ts +9 -1
- package/dist/reference/lab-math-scope.js +32 -4
- package/dist/reference/math-glyphs.d.mts +42 -0
- package/dist/reference/math-glyphs.mjs +548 -40
- package/dist/reference/mdx-attributes.d.mts +13 -0
- package/dist/reference/mdx-attributes.mjs +38 -0
- package/dist/reference/mixed-content.d.ts +17 -0
- package/dist/reference/mixed-content.js +29 -0
- package/dist/reference/page-notation.d.ts +52 -0
- package/dist/reference/page-notation.js +171 -0
- package/dist/reference/raw-floats-validate.d.ts +18 -0
- package/dist/reference/raw-floats-validate.js +53 -0
- package/dist/reference/raw-floats.d.mts +18 -0
- package/dist/reference/raw-floats.mjs +129 -0
- package/dist/reference/rehype-heading-anchors.d.mts +1 -1
- package/dist/reference/rehype-heading-anchors.mjs +107 -1
- package/dist/reference/rehype-table-header-labels.d.mts +26 -0
- package/dist/reference/rehype-table-header-labels.mjs +152 -0
- package/dist/reference/remark-abbreviations.d.mts +7 -0
- package/dist/reference/remark-abbreviations.mjs +174 -0
- package/dist/reference/remark-citation.d.mts +5 -1
- package/dist/reference/remark-citation.mjs +0 -0
- package/dist/reference/remark-memo.d.mts +16 -0
- package/dist/reference/remark-memo.mjs +339 -0
- package/dist/reference/remark-notation.d.mts +57 -9
- package/dist/reference/remark-notation.mjs +354 -42
- package/dist/reference/remark-short-note.d.mts +4 -0
- package/dist/reference/remark-short-note.mjs +237 -0
- package/dist/reference/types.d.ts +16 -1
- package/dist/reference/units-vocab.js +4 -0
- package/docs/VERSIONING.md +53 -4
- package/docs/authoring/README.md +10 -0
- package/docs/authoring/agents.md +17 -2
- package/docs/authoring/components.md +138 -0
- package/docs/authoring/migrating.md +275 -7
- package/docs/authoring/notation.md +615 -1
- package/docs/authoring/structure-rules.md +69 -0
- package/docs/authoring/structure.md +120 -8
- package/docs/authoring/visualizations.md +36 -3
- package/package.json +5 -2
- package/src/compiler/astro.ts +208 -3
- package/src/compiler/cli.ts +29 -2
- package/src/compiler/collections.ts +45 -4
- package/src/compiler/course-config.ts +132 -0
- package/src/compiler/exports/standalone-html.ts +963 -0
- package/src/compiler/manifest.ts +281 -22
- package/src/components/HoverPanel.astro +45 -4
- package/src/components/abbreviation/AbbreviationGlossary.astro +70 -0
- package/src/components/abbreviation/AbbreviationLayer.astro +127 -0
- package/src/components/assessments/AssessmentRunner.tsx +52 -17
- package/src/components/assessments/AssessmentSet.astro +98 -14
- package/src/components/assessments/RenderedContent.tsx +55 -0
- package/src/components/assessments/assessment-randomization.ts +16 -0
- package/src/components/diagram/Diagram.astro +409 -103
- package/src/components/disclosure/hover-preview.ts +202 -0
- package/src/components/disclosure/persisted-disclosure.ts +66 -0
- package/src/components/examples/CompactExamples.astro +2 -0
- package/src/components/figure/Figure.astro +279 -10
- package/src/components/memo/Memo.astro +453 -0
- package/src/components/memo/ShortNote.astro +223 -0
- package/src/components/notation/NotationFormula.astro +18 -1
- package/src/components/notation/NotationLayer.astro +260 -83
- package/src/components/notation/render-notation-math.ts +39 -2
- package/src/components/notation/safe-math-to-react.ts +50 -0
- package/src/components/reference/FloatRefLayer.astro +233 -15
- package/src/components/starlight/CollapsibleSections.astro +461 -0
- package/src/components/starlight/DiagramEnhancer.astro +385 -0
- package/src/components/starlight/HeadingAnchorEnhancer.astro +5 -17
- package/src/components/starlight/KeyedFloatEnhancer.astro +81 -3
- package/src/components/starlight/LayoutHeader.astro +50 -0
- package/src/components/starlight/LayoutSocialIcons.astro +41 -2
- package/src/components/starlight/LessonExportActions.astro +436 -0
- package/src/components/starlight/LessonFooter.astro +30 -3
- package/src/components/starlight/section-parts.ts +31 -0
- package/src/components/table/Table.astro +293 -7
- package/src/components/table/TableOverflowExpander.astro +489 -0
- package/src/content-config.ts +36 -1
- package/src/reference/base-abbreviations.mjs +60 -0
- package/src/reference/base-operators.mjs +40 -0
- package/src/reference/base-roman-labels.mjs +43 -0
- package/src/reference/consistency.ts +73 -2
- package/src/reference/diagrams-validate.ts +40 -0
- package/src/reference/equations.mjs +67 -14
- package/src/reference/gate-abbreviations.ts +128 -0
- package/src/reference/gate-heading-length.ts +131 -0
- package/src/reference/gate-math.ts +32 -7
- package/src/reference/gate-prose-notation.ts +224 -0
- package/src/reference/keyed-float-validate.ts +5 -5
- package/src/reference/keyed-jsx-floats.mjs +50 -25
- package/src/reference/knowledge-checks-validate.ts +130 -0
- package/src/reference/knowledge-checks.mjs +45 -0
- package/src/reference/lab-math-scope.ts +45 -3
- package/src/reference/math-glyphs.mjs +548 -40
- package/src/reference/mdx-attributes.mjs +38 -0
- package/src/reference/mixed-content.ts +69 -0
- package/src/reference/page-notation.ts +252 -0
- package/src/reference/raw-floats-validate.ts +103 -0
- package/src/reference/raw-floats.mjs +129 -0
- package/src/reference/rehype-heading-anchors.mjs +107 -1
- package/src/reference/rehype-table-header-labels.mjs +152 -0
- package/src/reference/remark-abbreviations.mjs +174 -0
- package/src/reference/remark-citation.mjs +0 -0
- package/src/reference/remark-memo.mjs +339 -0
- package/src/reference/remark-notation.mjs +354 -42
- package/src/reference/remark-short-note.mjs +237 -0
- package/src/reference/types.ts +16 -0
- package/src/reference/units-vocab.ts +4 -0
- package/src/styles/global.css +996 -50
package/src/compiler/manifest.ts
CHANGED
|
@@ -64,10 +64,35 @@ import {
|
|
|
64
64
|
type FigureDiagnostic,
|
|
65
65
|
type FigureLabelRecord,
|
|
66
66
|
} from '../reference/figures-validate';
|
|
67
|
+
import {
|
|
68
|
+
validateDiagrams,
|
|
69
|
+
type DiagramDiagnostic,
|
|
70
|
+
type DiagramLabelRecord,
|
|
71
|
+
} from '../reference/diagrams-validate';
|
|
72
|
+
import {
|
|
73
|
+
validateKnowledgeChecks,
|
|
74
|
+
type KnowledgeCheckDiagnostic,
|
|
75
|
+
type KnowledgeCheckLabelRecord,
|
|
76
|
+
} from '../reference/knowledge-checks-validate';
|
|
77
|
+
import {
|
|
78
|
+
validateRawFloats,
|
|
79
|
+
type RawFloatDiagnostic,
|
|
80
|
+
} from '../reference/raw-floats-validate';
|
|
67
81
|
import {
|
|
68
82
|
gateContentMath,
|
|
83
|
+
pageScope,
|
|
69
84
|
type MathGateDiagnostic,
|
|
70
85
|
} from '../reference/gate-math';
|
|
86
|
+
import {
|
|
87
|
+
gateProseNotation,
|
|
88
|
+
gateProseNotationInAssessments,
|
|
89
|
+
} from '../reference/gate-prose-notation';
|
|
90
|
+
import { gateAbbreviations } from '../reference/gate-abbreviations';
|
|
91
|
+
import { gateHeadingLength } from '../reference/gate-heading-length';
|
|
92
|
+
import {
|
|
93
|
+
splitMixedContent,
|
|
94
|
+
type ContentSegment,
|
|
95
|
+
} from '../reference/mixed-content';
|
|
71
96
|
import { loadLintIgnore } from '../reference/lint-ignore';
|
|
72
97
|
import { buildNotationRegistry } from '../reference/registry';
|
|
73
98
|
import { formulaGlyphScope } from '../reference/gloss';
|
|
@@ -82,12 +107,20 @@ import type {
|
|
|
82
107
|
NotationLessonInput,
|
|
83
108
|
NotationPageBundle,
|
|
84
109
|
} from '../reference/types';
|
|
85
|
-
import {
|
|
110
|
+
import {
|
|
111
|
+
coursePartsOf,
|
|
112
|
+
normalizeLessonExports,
|
|
113
|
+
normalizeDisclosureDefaults,
|
|
114
|
+
type CourseConfig,
|
|
115
|
+
type NormalizedLessonExports,
|
|
116
|
+
type NormalizedDisclosureDefaults,
|
|
117
|
+
} from './course-config';
|
|
86
118
|
import {
|
|
87
119
|
resolveKnowledgeVersion,
|
|
88
120
|
type KnowledgeVersionDiagnostic,
|
|
89
121
|
} from './knowledge-version';
|
|
90
122
|
import {
|
|
123
|
+
loadAbbreviationRegistryInput,
|
|
91
124
|
loadCurriculumCatalog,
|
|
92
125
|
loadNotationDefinitions,
|
|
93
126
|
loadNotationRegistryInput,
|
|
@@ -112,7 +145,7 @@ import {
|
|
|
112
145
|
* `lessons[].equations`, and `diagnostics.equations`.
|
|
113
146
|
* v4 (F-tables-figures): `tables`/`figures` (same shape as `equations`, for
|
|
114
147
|
* `<Table>`/`<Figure>`), `lessons[].tables`/`lessons[].figures`, and
|
|
115
|
-
* `diagnostics.tables`/`diagnostics.figures`.
|
|
148
|
+
* `diagnostics.tables`/`diagnostics.figures`/`diagnostics.diagrams`.
|
|
116
149
|
* v5 (about): `about` (engine identity from `explico`'s `package.json`, the
|
|
117
150
|
* course's reader-facing `about` block, and the knowledge version resolved
|
|
118
151
|
* against `content/CHANGELOG.md`) and `diagnostics.knowledge`.
|
|
@@ -122,8 +155,39 @@ import {
|
|
|
122
155
|
* now the number before the dot), `lessons[].chapter` redefined as the
|
|
123
156
|
* lesson's ordinal *within its part*, and `lessons[].numbering` — the
|
|
124
157
|
* per-kind offsets that make `n` run continuously across a part.
|
|
158
|
+
* v8 (assessment math): `assessmentContent` — every assessment item's
|
|
159
|
+
* `prompt` / `explanation`, pre-split into text/math segments and resolved
|
|
160
|
+
* against the scope of the lesson that embeds it, so `AssessmentSet.astro`
|
|
161
|
+
* can render them through the same KaTeX pipeline as lesson body math instead
|
|
162
|
+
* of showing raw text.
|
|
163
|
+
* v9 (diagram identity): `diagrams` (the independent `<Diagram>` number and
|
|
164
|
+
* cross-page lookup), `lessons[].diagrams`, `lessons[].numbering.diagrams`,
|
|
165
|
+
* and `diagnostics.diagrams`.
|
|
166
|
+
* v10 (knowledge-check identity): `checks` (part-wide numbering and cross-page
|
|
167
|
+
* lookup), `lessons[].knowledgeChecks`, `lessons[].numbering.checks`, and
|
|
168
|
+
* `diagnostics.checks`.
|
|
169
|
+
* v11 (lesson exports): normalized standalone HTML and print build/UI settings.
|
|
170
|
+
* v12 (abbreviation hover cards): `manifest.abbreviations` — the
|
|
171
|
+
* `abbreviations` content collection (N14), flattened to plain
|
|
172
|
+
* `{key, abbr, expansion, aliases, domain?}` records sorted by key, the way
|
|
173
|
+
* `manifest.notation.raw` already carries notation frontmatter for
|
|
174
|
+
* `remarkNotation`. `remarkAbbreviations` reads it to wrap the first
|
|
175
|
+
* occurrence of a known abbreviation in a lesson's prose with a hover/click
|
|
176
|
+
* card.
|
|
177
|
+
* v13 (raw-float diagnostics, additive): `diagnostics.rawFloats` — a raw
|
|
178
|
+
* Markdown table, a raw HTML `<table>`, or an Explico `<Diagram>` that is
|
|
179
|
+
* unkeyed or nested outside `<Table>`/`<Figure>` never reaches keyed-float
|
|
180
|
+
* validation and can never get a stable number or `[[…]]` reference; this
|
|
181
|
+
* flags it (warning-only — the manifest shape a client already reads is
|
|
182
|
+
* unchanged).
|
|
183
|
+
* v14 (disclosure defaults, additive): `disclosureDefaults` — the resolved
|
|
184
|
+
* initial open/closed state for the "Page notation" panel and the generated
|
|
185
|
+
* "References" section, from `CourseConfig.disclosureDefaults`. Both are
|
|
186
|
+
* engine-authored sections, not written by a lesson, so they get their own
|
|
187
|
+
* default independent of a course's `##` heading default; closed unless the
|
|
188
|
+
* course sets one.
|
|
125
189
|
*/
|
|
126
|
-
export const MANIFEST_SCHEMA_VERSION =
|
|
190
|
+
export const MANIFEST_SCHEMA_VERSION = 14;
|
|
127
191
|
|
|
128
192
|
const REPOSITORY_ROOT = process.cwd();
|
|
129
193
|
// Content is the top-level `content/` folder; the build output is `build/`
|
|
@@ -214,8 +278,9 @@ export interface ManifestLesson {
|
|
|
214
278
|
/**
|
|
215
279
|
* The 1-based ordinal of the **part** this lesson sits in — its sidebar
|
|
216
280
|
* group's position in `courseConfig.parts`. It is the number before the dot
|
|
217
|
-
* in every equation, table, and
|
|
218
|
-
* first equation of part 2. See
|
|
281
|
+
* in every equation, table, figure, diagram, and knowledge-check number on
|
|
282
|
+
* the page: `(2.1)` is the first equation of part 2. See
|
|
283
|
+
* `docs/authoring/structure.md`.
|
|
219
284
|
*/
|
|
220
285
|
readonly part: number;
|
|
221
286
|
/**
|
|
@@ -233,6 +298,8 @@ export interface ManifestLesson {
|
|
|
233
298
|
readonly equations: number;
|
|
234
299
|
readonly tables: number;
|
|
235
300
|
readonly figures: number;
|
|
301
|
+
readonly diagrams: number;
|
|
302
|
+
readonly checks: number;
|
|
236
303
|
};
|
|
237
304
|
readonly file: string;
|
|
238
305
|
readonly title: string;
|
|
@@ -243,6 +310,11 @@ export interface ManifestLesson {
|
|
|
243
310
|
readonly requires: readonly string[];
|
|
244
311
|
readonly assumptions: readonly string[];
|
|
245
312
|
readonly assessments: readonly string[];
|
|
313
|
+
/** Numbered knowledge checks from `assessments`, in `.checks.yml` order. */
|
|
314
|
+
readonly knowledgeChecks: readonly {
|
|
315
|
+
readonly key: string;
|
|
316
|
+
readonly number: string;
|
|
317
|
+
}[];
|
|
246
318
|
readonly sources: readonly string[];
|
|
247
319
|
readonly citations: readonly ManifestCitation[];
|
|
248
320
|
/** Competency prerequisite edges this lesson introduces. */
|
|
@@ -262,6 +334,11 @@ export interface ManifestLesson {
|
|
|
262
334
|
readonly key: string;
|
|
263
335
|
readonly number: string;
|
|
264
336
|
}[];
|
|
337
|
+
/** Keyed `<Diagram>` elements in appearance order. */
|
|
338
|
+
readonly diagrams: readonly {
|
|
339
|
+
readonly key: string;
|
|
340
|
+
readonly number: string;
|
|
341
|
+
}[];
|
|
265
342
|
readonly notation: {
|
|
266
343
|
readonly bindings: readonly {
|
|
267
344
|
readonly key: string;
|
|
@@ -301,8 +378,21 @@ export interface ManifestAbout {
|
|
|
301
378
|
readonly changelogHref?: string;
|
|
302
379
|
}
|
|
303
380
|
|
|
381
|
+
/** One assessment item's pre-rendered `prompt` / `explanation` (v8). */
|
|
382
|
+
export interface ManifestAssessmentItemContent {
|
|
383
|
+
readonly lessonId: string;
|
|
384
|
+
readonly assessmentId: string;
|
|
385
|
+
readonly itemId: string;
|
|
386
|
+
readonly promptSegments: readonly ContentSegment[];
|
|
387
|
+
readonly explanationSegments: readonly ContentSegment[];
|
|
388
|
+
}
|
|
389
|
+
|
|
304
390
|
export interface Manifest {
|
|
305
391
|
readonly schemaVersion: number;
|
|
392
|
+
/** Normalized build/UI settings for opt-in per-lesson exports. */
|
|
393
|
+
readonly lessonExports: NormalizedLessonExports;
|
|
394
|
+
/** Initial open/closed state for "Page notation" and "References". */
|
|
395
|
+
readonly disclosureDefaults: NormalizedDisclosureDefaults;
|
|
306
396
|
/** Reader-facing identity + versions (the `[?]` About panel). */
|
|
307
397
|
readonly about: ManifestAbout;
|
|
308
398
|
readonly notation: {
|
|
@@ -316,6 +406,8 @@ export interface Manifest {
|
|
|
316
406
|
readonly competencies: readonly Record<string, unknown>[];
|
|
317
407
|
readonly sources: readonly Record<string, unknown>[];
|
|
318
408
|
readonly tracks: readonly Record<string, unknown>[];
|
|
409
|
+
/** Abbreviations table (N14), for `remarkAbbreviations`. Ordered by key. */
|
|
410
|
+
readonly abbreviations: readonly Record<string, unknown>[];
|
|
319
411
|
readonly sidebar: readonly SidebarGroup[];
|
|
320
412
|
readonly lessons: readonly ManifestLesson[];
|
|
321
413
|
/** Full competency prerequisite graph. */
|
|
@@ -336,6 +428,26 @@ export interface Manifest {
|
|
|
336
428
|
readonly labels: readonly FigureLabelRecord[];
|
|
337
429
|
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
338
430
|
};
|
|
431
|
+
/** Keyed-float identity for `<Diagram>` — independent of figure numbering. */
|
|
432
|
+
readonly diagrams: {
|
|
433
|
+
readonly labels: readonly DiagramLabelRecord[];
|
|
434
|
+
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
435
|
+
};
|
|
436
|
+
/** Knowledge-check identity, derived from each lesson's `.checks.yml`. */
|
|
437
|
+
readonly checks: {
|
|
438
|
+
readonly labels: readonly KnowledgeCheckLabelRecord[];
|
|
439
|
+
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* Every assessment item's `prompt` / `explanation`, pre-split into
|
|
443
|
+
* text/math segments (v8). `AssessmentSet.astro` filters this by
|
|
444
|
+
* `lessonId` + `assessmentId` and renders each `math` segment's `latex`
|
|
445
|
+
* through `renderNotationMath` before handing the result to
|
|
446
|
+
* `AssessmentRunner`. An assessment embedded in more than one lesson (via
|
|
447
|
+
* more than one `checks.yml`) appears once per lesson here, since each
|
|
448
|
+
* lesson's scope can resolve its glyphs differently.
|
|
449
|
+
*/
|
|
450
|
+
readonly assessmentContent: readonly ManifestAssessmentItemContent[];
|
|
339
451
|
readonly diagnostics: {
|
|
340
452
|
readonly curriculum: readonly CurriculumIssue[];
|
|
341
453
|
readonly notation: readonly NotationDiagnostic[];
|
|
@@ -351,6 +463,16 @@ export interface Manifest {
|
|
|
351
463
|
readonly tables: readonly TableDiagnostic[];
|
|
352
464
|
/** `<Figure>` identity checks (F-tables-figures) — same codes, `fig-` prefixed. */
|
|
353
465
|
readonly figures: readonly FigureDiagnostic[];
|
|
466
|
+
/** `<Diagram>` identity checks — same codes, `dia-` prefixed. */
|
|
467
|
+
readonly diagrams: readonly DiagramDiagnostic[];
|
|
468
|
+
/** Knowledge-check identity and `[[check-…]]` reference checks. */
|
|
469
|
+
readonly checks: readonly KnowledgeCheckDiagnostic[];
|
|
470
|
+
/**
|
|
471
|
+
* A raw Markdown table, a raw HTML `<table>`, or an Explico `<Diagram>`
|
|
472
|
+
* that is unkeyed or nested outside `<Table>`/`<Figure>` — invisible to
|
|
473
|
+
* keyed-float validation above. Warnings only.
|
|
474
|
+
*/
|
|
475
|
+
readonly rawFloats: readonly RawFloatDiagnostic[];
|
|
354
476
|
};
|
|
355
477
|
readonly hashes: {
|
|
356
478
|
/** `sha256` of every content source file, keyed by repo-relative path. */
|
|
@@ -546,9 +668,10 @@ export async function compileManifest(
|
|
|
546
668
|
);
|
|
547
669
|
}
|
|
548
670
|
|
|
549
|
-
const [catalog, notationInput] = await Promise.all([
|
|
671
|
+
const [catalog, notationInput, abbreviationsInput] = await Promise.all([
|
|
550
672
|
loadCurriculumCatalog(),
|
|
551
673
|
loadNotationRegistryInput(),
|
|
674
|
+
loadAbbreviationRegistryInput(),
|
|
552
675
|
]);
|
|
553
676
|
|
|
554
677
|
const registry = buildNotationRegistry(notationInput);
|
|
@@ -568,19 +691,66 @@ export async function compileManifest(
|
|
|
568
691
|
registry.bundles.map((bundle) => [bundle.lessonId, bundle]),
|
|
569
692
|
);
|
|
570
693
|
|
|
694
|
+
const assessmentsInput = catalog.assessments as unknown as {
|
|
695
|
+
id: string;
|
|
696
|
+
items?: { id?: string; prompt?: string; explanation?: string }[];
|
|
697
|
+
}[];
|
|
698
|
+
|
|
571
699
|
const curriculumIssues = validateCurriculum(catalog);
|
|
572
700
|
const alignmentIssues = validateNotationAlignment(registry, catalog);
|
|
573
|
-
const mathIssues =
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
701
|
+
const mathIssues = [
|
|
702
|
+
...gateContentMath({
|
|
703
|
+
notationInput,
|
|
704
|
+
assessments: assessmentsInput,
|
|
705
|
+
lessonAssessments: catalog.lessons.map((lesson) => ({
|
|
706
|
+
lessonId: lesson.id,
|
|
707
|
+
assessmentIds: lesson.assessments,
|
|
708
|
+
})),
|
|
709
|
+
}),
|
|
710
|
+
...gateProseNotation(notationInput.lessons),
|
|
711
|
+
...gateProseNotationInAssessments(assessmentsInput),
|
|
712
|
+
...gateAbbreviations({
|
|
713
|
+
notationInput,
|
|
714
|
+
abbreviations: abbreviationsInput,
|
|
715
|
+
courseAbbreviations: courseConfig.abbreviations,
|
|
716
|
+
}),
|
|
717
|
+
...gateHeadingLength(notationInput.lessons),
|
|
718
|
+
];
|
|
719
|
+
|
|
720
|
+
// Pre-render every assessment `prompt` / `explanation` into text/math
|
|
721
|
+
// segments, resolved against the scope of whichever lesson embeds it —
|
|
722
|
+
// same relationship, same `pageScope`, as the `gateContentMath` assessment
|
|
723
|
+
// check above, so a symbol that passes the gate is exactly the symbol
|
|
724
|
+
// `AssessmentSet.astro` renders. An assessment shared by two lessons is
|
|
725
|
+
// rendered once per (lesson, assessment) pair, since the two lessons can
|
|
726
|
+
// have different scopes and therefore different resolved LaTeX.
|
|
727
|
+
const notationLessonById = new Map(
|
|
728
|
+
notationInput.lessons.map((lesson) => [lesson.lessonId, lesson]),
|
|
729
|
+
);
|
|
730
|
+
const assessmentInputById = new Map(
|
|
731
|
+
assessmentsInput.map((assessment) => [assessment.id, assessment]),
|
|
732
|
+
);
|
|
733
|
+
const assessmentContent: ManifestAssessmentItemContent[] = [];
|
|
734
|
+
for (const lesson of catalog.lessons) {
|
|
735
|
+
if (lesson.assessments.length === 0) continue;
|
|
736
|
+
const lessonInput = notationLessonById.get(lesson.id);
|
|
737
|
+
if (!lessonInput) continue;
|
|
738
|
+
const scope = pageScope(lessonInput, cardsByKey);
|
|
739
|
+
for (const assessmentId of lesson.assessments) {
|
|
740
|
+
const assessment = assessmentInputById.get(assessmentId);
|
|
741
|
+
if (!assessment?.items) continue;
|
|
742
|
+
for (const item of assessment.items) {
|
|
743
|
+
if (!item.id) continue;
|
|
744
|
+
assessmentContent.push({
|
|
745
|
+
lessonId: lesson.id,
|
|
746
|
+
assessmentId,
|
|
747
|
+
itemId: item.id,
|
|
748
|
+
promptSegments: splitMixedContent(item.prompt ?? '', scope),
|
|
749
|
+
explanationSegments: splitMixedContent(item.explanation ?? '', scope),
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
584
754
|
|
|
585
755
|
const prerequisitesById = new Map(
|
|
586
756
|
catalog.competencies.map((competency) => [
|
|
@@ -615,8 +785,8 @@ export async function compileManifest(
|
|
|
615
785
|
// Part and chapter, read straight off the sidebar so there is one
|
|
616
786
|
// lesson-ordering source and not two: a part is a sidebar group (parts in
|
|
617
787
|
// config order), a chapter is a lesson's position inside its part (merged
|
|
618
|
-
// track order, then slug). The part is what prefixes every
|
|
619
|
-
//
|
|
788
|
+
// track order, then slug). The part is what prefixes every numbered item —
|
|
789
|
+
// `(2.1)` is the first item of one independent kind in part 2 — while `n`
|
|
620
790
|
// continues across the part's chapters rather than restarting on each page,
|
|
621
791
|
// so every lesson also needs the running count that precedes it.
|
|
622
792
|
// See `docs/authoring/structure.md`.
|
|
@@ -628,7 +798,13 @@ export async function compileManifest(
|
|
|
628
798
|
{
|
|
629
799
|
part: number;
|
|
630
800
|
chapter: number;
|
|
631
|
-
numbering: {
|
|
801
|
+
numbering: {
|
|
802
|
+
equations: number;
|
|
803
|
+
tables: number;
|
|
804
|
+
figures: number;
|
|
805
|
+
diagrams: number;
|
|
806
|
+
checks: number;
|
|
807
|
+
};
|
|
632
808
|
}
|
|
633
809
|
>();
|
|
634
810
|
let partCursor = 0;
|
|
@@ -639,7 +815,13 @@ export async function compileManifest(
|
|
|
639
815
|
if (lessonIds.length === 0) continue; // the static Reference group
|
|
640
816
|
partCursor += 1;
|
|
641
817
|
// Running totals for this part, advanced one chapter at a time.
|
|
642
|
-
const used = {
|
|
818
|
+
const used = {
|
|
819
|
+
equations: 0,
|
|
820
|
+
tables: 0,
|
|
821
|
+
figures: 0,
|
|
822
|
+
diagrams: 0,
|
|
823
|
+
checks: 0,
|
|
824
|
+
};
|
|
643
825
|
let chapterCursor = 0;
|
|
644
826
|
for (const lessonId of lessonIds) {
|
|
645
827
|
const body = bodyByLessonId.get(lessonId) ?? '';
|
|
@@ -651,10 +833,14 @@ export async function compileManifest(
|
|
|
651
833
|
used.equations += countDisplayEquations(body);
|
|
652
834
|
used.tables += countKeyedJsxFloats(body, { tagName: 'Table' });
|
|
653
835
|
used.figures += countKeyedJsxFloats(body, { tagName: 'Figure' });
|
|
836
|
+
used.diagrams += countKeyedJsxFloats(body, { tagName: 'Diagram' });
|
|
837
|
+
used.checks += lessonMetaById.get(lessonId)?.assessments.length ?? 0;
|
|
654
838
|
}
|
|
655
839
|
}
|
|
656
840
|
|
|
657
|
-
const floatInputs = (
|
|
841
|
+
const floatInputs = (
|
|
842
|
+
kind: 'equations' | 'tables' | 'figures' | 'diagrams',
|
|
843
|
+
) =>
|
|
658
844
|
notationInput.lessons.map((lesson) => {
|
|
659
845
|
const placement = placementByLessonId.get(lesson.lessonId);
|
|
660
846
|
return {
|
|
@@ -693,6 +879,48 @@ export async function compileManifest(
|
|
|
693
879
|
Object.entries(table).map(([key, number]) => ({ key, number })),
|
|
694
880
|
]),
|
|
695
881
|
);
|
|
882
|
+
const diagramValidation = validateDiagrams(floatInputs('diagrams'));
|
|
883
|
+
const diagramsBySlug = new Map(
|
|
884
|
+
Object.entries(diagramValidation.numbersBySlug).map(([slug, table]) => [
|
|
885
|
+
slug,
|
|
886
|
+
Object.entries(table).map(([key, number]) => ({ key, number })),
|
|
887
|
+
]),
|
|
888
|
+
);
|
|
889
|
+
const checkValidation = validateKnowledgeChecks(
|
|
890
|
+
notationInput.lessons.map((lesson) => {
|
|
891
|
+
const placement = placementByLessonId.get(lesson.lessonId);
|
|
892
|
+
return {
|
|
893
|
+
lessonId: lesson.lessonId,
|
|
894
|
+
slug: lesson.lessonId.replace(/\./g, '/'),
|
|
895
|
+
file: lesson.source.file,
|
|
896
|
+
body: lesson.body,
|
|
897
|
+
assessmentIds:
|
|
898
|
+
lessonMetaById.get(lesson.lessonId)?.assessments ?? [],
|
|
899
|
+
...(placement
|
|
900
|
+
? {
|
|
901
|
+
numbering: {
|
|
902
|
+
prefix: placement.part,
|
|
903
|
+
offset: placement.numbering.checks,
|
|
904
|
+
},
|
|
905
|
+
}
|
|
906
|
+
: {}),
|
|
907
|
+
};
|
|
908
|
+
}),
|
|
909
|
+
);
|
|
910
|
+
const checksBySlug = new Map(
|
|
911
|
+
Object.entries(checkValidation.numbersBySlug).map(([slug, table]) => [
|
|
912
|
+
slug,
|
|
913
|
+
Object.entries(table).map(([key, number]) => ({ key, number })),
|
|
914
|
+
]),
|
|
915
|
+
);
|
|
916
|
+
const rawFloatDiagnostics = validateRawFloats(
|
|
917
|
+
notationInput.lessons.map((lesson) => ({
|
|
918
|
+
lessonId: lesson.lessonId,
|
|
919
|
+
slug: lesson.lessonId.replace(/\./g, '/'),
|
|
920
|
+
file: lesson.source.file,
|
|
921
|
+
body: lesson.body,
|
|
922
|
+
})),
|
|
923
|
+
);
|
|
696
924
|
|
|
697
925
|
const lessons: ManifestLesson[] = [];
|
|
698
926
|
for (const notationLesson of notationInput.lessons) {
|
|
@@ -716,6 +944,8 @@ export async function compileManifest(
|
|
|
716
944
|
equations: 0,
|
|
717
945
|
tables: 0,
|
|
718
946
|
figures: 0,
|
|
947
|
+
diagrams: 0,
|
|
948
|
+
checks: 0,
|
|
719
949
|
},
|
|
720
950
|
file: notationLesson.source.file,
|
|
721
951
|
title: frontmatter.title,
|
|
@@ -725,11 +955,13 @@ export async function compileManifest(
|
|
|
725
955
|
requires: [...meta.requires],
|
|
726
956
|
assumptions: [...meta.assumptions],
|
|
727
957
|
assessments: [...meta.assessments],
|
|
958
|
+
knowledgeChecks: checksBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
728
959
|
sources: deriveSources(notationLesson.body),
|
|
729
960
|
citations: lessonCitations(notationLesson.body),
|
|
730
961
|
equations: equationsBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
731
962
|
tables: tablesBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
732
963
|
figures: figuresBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
964
|
+
diagrams: diagramsBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
733
965
|
prereqEdges: sortEdges(
|
|
734
966
|
teaches.flatMap((to) =>
|
|
735
967
|
(prerequisitesById.get(to) ?? [])
|
|
@@ -761,6 +993,9 @@ export async function compileManifest(
|
|
|
761
993
|
...(courseConfig.operators === undefined
|
|
762
994
|
? {}
|
|
763
995
|
: { operators: courseConfig.operators }),
|
|
996
|
+
...(courseConfig.romanLabels === undefined
|
|
997
|
+
? {}
|
|
998
|
+
: { romanLabels: courseConfig.romanLabels }),
|
|
764
999
|
});
|
|
765
1000
|
|
|
766
1001
|
const rawNotation = [...loadNotationDefinitions()].sort((a, b) =>
|
|
@@ -771,6 +1006,15 @@ export async function compileManifest(
|
|
|
771
1006
|
.sort(byId);
|
|
772
1007
|
const sources = [...loadSourceRecords()].sort(byId);
|
|
773
1008
|
const tracks = catalog.tracks.map((entry) => ({ ...entry })).sort(byId);
|
|
1009
|
+
const abbreviations = abbreviationsInput
|
|
1010
|
+
.map((entry) => ({
|
|
1011
|
+
key: entry.key,
|
|
1012
|
+
abbr: entry.abbr,
|
|
1013
|
+
expansion: entry.expansion,
|
|
1014
|
+
aliases: entry.aliases,
|
|
1015
|
+
...(entry.domain === undefined ? {} : { domain: entry.domain }),
|
|
1016
|
+
}))
|
|
1017
|
+
.sort((a, b) => a.key.localeCompare(b.key));
|
|
774
1018
|
|
|
775
1019
|
const hashes = contentHashes();
|
|
776
1020
|
|
|
@@ -819,6 +1063,8 @@ export async function compileManifest(
|
|
|
819
1063
|
|
|
820
1064
|
return {
|
|
821
1065
|
schemaVersion: MANIFEST_SCHEMA_VERSION,
|
|
1066
|
+
lessonExports: normalizeLessonExports(courseConfig),
|
|
1067
|
+
disclosureDefaults: normalizeDisclosureDefaults(courseConfig),
|
|
822
1068
|
about,
|
|
823
1069
|
notation: {
|
|
824
1070
|
definitions: registry.definitions,
|
|
@@ -830,6 +1076,7 @@ export async function compileManifest(
|
|
|
830
1076
|
competencies,
|
|
831
1077
|
sources,
|
|
832
1078
|
tracks,
|
|
1079
|
+
abbreviations,
|
|
833
1080
|
sidebar,
|
|
834
1081
|
lessons,
|
|
835
1082
|
prereqEdges,
|
|
@@ -845,6 +1092,15 @@ export async function compileManifest(
|
|
|
845
1092
|
labels: figureValidation.labels,
|
|
846
1093
|
numbersBySlug: figureValidation.numbersBySlug,
|
|
847
1094
|
},
|
|
1095
|
+
diagrams: {
|
|
1096
|
+
labels: diagramValidation.labels,
|
|
1097
|
+
numbersBySlug: diagramValidation.numbersBySlug,
|
|
1098
|
+
},
|
|
1099
|
+
checks: {
|
|
1100
|
+
labels: checkValidation.labels,
|
|
1101
|
+
numbersBySlug: checkValidation.numbersBySlug,
|
|
1102
|
+
},
|
|
1103
|
+
assessmentContent,
|
|
848
1104
|
diagnostics: {
|
|
849
1105
|
curriculum: curriculumIssues,
|
|
850
1106
|
notation: registry.diagnostics,
|
|
@@ -854,7 +1110,10 @@ export async function compileManifest(
|
|
|
854
1110
|
equations: equationValidation.diagnostics,
|
|
855
1111
|
tables: tableValidation.diagnostics,
|
|
856
1112
|
figures: figureValidation.diagnostics,
|
|
1113
|
+
diagrams: diagramValidation.diagnostics,
|
|
1114
|
+
checks: checkValidation.diagnostics,
|
|
857
1115
|
knowledge: knowledge.diagnostics,
|
|
1116
|
+
rawFloats: rawFloatDiagnostics,
|
|
858
1117
|
},
|
|
859
1118
|
hashes,
|
|
860
1119
|
};
|
|
@@ -50,7 +50,9 @@ const { id, label, class: className, ...rest } = Astro.props;
|
|
|
50
50
|
>
|
|
51
51
|
<span aria-hidden="true">×</span>
|
|
52
52
|
</button>
|
|
53
|
-
<
|
|
53
|
+
<div class="hover-panel-scroll">
|
|
54
|
+
<slot />
|
|
55
|
+
</div>
|
|
54
56
|
</aside>
|
|
55
57
|
|
|
56
58
|
<style>
|
|
@@ -63,9 +65,15 @@ const { id, label, class: className, ...rest } = Astro.props;
|
|
|
63
65
|
JS placement (which clears display equations) must start from zero. */
|
|
64
66
|
margin: 0;
|
|
65
67
|
width: min(23rem, calc(100vw - 1.5rem));
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
/* The scrolling lives on `.hover-panel-scroll` below, not here — the
|
|
71
|
+
close button is an absolutely-positioned sibling of that scroller,
|
|
72
|
+
so it stays put when the content scrolls instead of scrolling away
|
|
73
|
+
with it (which is what happens to an absolutely-positioned child of
|
|
74
|
+
the scroll container itself). `hidden` also keeps the rounded
|
|
75
|
+
corners clean around the inner scroller. */
|
|
76
|
+
overflow: hidden;
|
|
69
77
|
border: 1px solid var(--sl-color-gray-5);
|
|
70
78
|
border-top: 0.25rem solid var(--sl-color-accent);
|
|
71
79
|
border-radius: 0.65rem;
|
|
@@ -75,6 +83,39 @@ const { id, label, class: className, ...rest } = Astro.props;
|
|
|
75
83
|
font-size: var(--sl-text-sm);
|
|
76
84
|
}
|
|
77
85
|
|
|
86
|
+
/* `.hover-panel { display: flex }` above has the same specificity as the
|
|
87
|
+
UA stylesheet's `[hidden] { display: none }` and comes later in the
|
|
88
|
+
cascade, so it would otherwise win and defeat the `hidden` attribute
|
|
89
|
+
that every layer's JS relies on to actually hide the panel. */
|
|
90
|
+
.hover-panel[hidden] {
|
|
91
|
+
display: none;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* The equation/table/figure preview card (`FloatRefLayer.astro`) is the
|
|
95
|
+
one panel whose content size varies wildly — a short reference vs. a
|
|
96
|
+
wide equation — so it grows to fit instead of staying pinned at the
|
|
97
|
+
default width. `--float-ref-max-width` is computed in JS from the live
|
|
98
|
+
layout (the room between the left sidebar and the viewport edge); the
|
|
99
|
+
notation and citation panels never set it and keep the fixed default. */
|
|
100
|
+
.hover-panel[data-float-ref-panel] {
|
|
101
|
+
width: fit-content;
|
|
102
|
+
min-width: min(23rem, var(--float-ref-max-width, 100vw));
|
|
103
|
+
max-width: var(--float-ref-max-width, min(23rem, calc(100vw - 1.5rem)));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.hover-panel-scroll {
|
|
107
|
+
overflow: auto;
|
|
108
|
+
/* Without these, this flex item's default `auto` minimum size is its
|
|
109
|
+
*content's* size, not the panel's — content wider or taller than the
|
|
110
|
+
panel would inflate the panel itself outward instead of scrolling
|
|
111
|
+
inside it (the same flexbox trap `.float-ref-panel-content` guards
|
|
112
|
+
against with its own `min-width: 0`). */
|
|
113
|
+
min-width: 0;
|
|
114
|
+
min-height: 0;
|
|
115
|
+
padding: 1rem;
|
|
116
|
+
padding-inline-end: 2.4rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
78
119
|
.hover-panel-close {
|
|
79
120
|
position: absolute;
|
|
80
121
|
top: 0.5rem;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* A plain, alphabetical listing of the `abbreviations` collection (rule N14)
|
|
4
|
+
* — deliberately the simplest possible reader-facing view of this content.
|
|
5
|
+
* Unlike `NotationGlossary.astro`, there is no hover popup, no formula
|
|
6
|
+
* rendering, and no `NotationLayer`: an abbreviation card is a name
|
|
7
|
+
* resolution only, so a definition list is the whole job.
|
|
8
|
+
*/
|
|
9
|
+
import { getCollection } from 'astro:content';
|
|
10
|
+
|
|
11
|
+
const entries = (await getCollection('abbreviations')).sort((left, right) =>
|
|
12
|
+
left.data.abbr.localeCompare(right.data.abbr),
|
|
13
|
+
);
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
entries.length === 0 ? (
|
|
18
|
+
<p class="abbreviation-glossary-empty">
|
|
19
|
+
No abbreviations are catalogued yet.
|
|
20
|
+
</p>
|
|
21
|
+
) : (
|
|
22
|
+
<dl class="abbreviation-glossary">
|
|
23
|
+
{entries.map((entry) => (
|
|
24
|
+
<div class="abbreviation-entry" id={`abbr-${entry.data.key}`}>
|
|
25
|
+
<dt>
|
|
26
|
+
{entry.data.abbr}
|
|
27
|
+
{entry.data.aliases.length > 0 && (
|
|
28
|
+
<span class="abbreviation-aliases">
|
|
29
|
+
{' '}
|
|
30
|
+
(also {entry.data.aliases.join(', ')})
|
|
31
|
+
</span>
|
|
32
|
+
)}
|
|
33
|
+
</dt>
|
|
34
|
+
<dd>{entry.data.expansion}</dd>
|
|
35
|
+
</div>
|
|
36
|
+
))}
|
|
37
|
+
</dl>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
.abbreviation-glossary {
|
|
43
|
+
display: grid;
|
|
44
|
+
grid-template-columns: max-content 1fr;
|
|
45
|
+
column-gap: 1rem;
|
|
46
|
+
row-gap: 0.75rem;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.abbreviation-entry {
|
|
50
|
+
display: contents;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.abbreviation-entry dt {
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.abbreviation-entry dd {
|
|
59
|
+
margin: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.abbreviation-aliases {
|
|
63
|
+
font-weight: 400;
|
|
64
|
+
color: var(--sl-color-gray-3);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.abbreviation-glossary-empty {
|
|
68
|
+
color: var(--sl-color-gray-3);
|
|
69
|
+
}
|
|
70
|
+
</style>
|