explico 0.4.0 → 0.5.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 +273 -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 +58 -1
- package/dist/compiler/course-config.js +28 -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 +90 -6
- package/dist/compiler/manifest.js +150 -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-math.d.ts +28 -5
- package/dist/reference/gate-math.js +5 -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 +30 -0
- package/dist/reference/math-glyphs.mjs +367 -0
- 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-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 +33 -4
- package/docs/authoring/README.md +8 -0
- package/docs/authoring/agents.md +17 -2
- package/docs/authoring/components.md +135 -0
- package/docs/authoring/migrating.md +92 -7
- package/docs/authoring/notation.md +560 -1
- 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 +92 -0
- package/src/compiler/exports/standalone-html.ts +963 -0
- package/src/compiler/manifest.ts +268 -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 +444 -83
- 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 +105 -8
- 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 +27 -2
- 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 +20 -0
- 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-math.ts +27 -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 +367 -0
- 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-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,34 @@ 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 {
|
|
92
|
+
splitMixedContent,
|
|
93
|
+
type ContentSegment,
|
|
94
|
+
} from '../reference/mixed-content';
|
|
71
95
|
import { loadLintIgnore } from '../reference/lint-ignore';
|
|
72
96
|
import { buildNotationRegistry } from '../reference/registry';
|
|
73
97
|
import { formulaGlyphScope } from '../reference/gloss';
|
|
@@ -82,12 +106,18 @@ import type {
|
|
|
82
106
|
NotationLessonInput,
|
|
83
107
|
NotationPageBundle,
|
|
84
108
|
} from '../reference/types';
|
|
85
|
-
import {
|
|
109
|
+
import {
|
|
110
|
+
coursePartsOf,
|
|
111
|
+
normalizeLessonExports,
|
|
112
|
+
type CourseConfig,
|
|
113
|
+
type NormalizedLessonExports,
|
|
114
|
+
} from './course-config';
|
|
86
115
|
import {
|
|
87
116
|
resolveKnowledgeVersion,
|
|
88
117
|
type KnowledgeVersionDiagnostic,
|
|
89
118
|
} from './knowledge-version';
|
|
90
119
|
import {
|
|
120
|
+
loadAbbreviationRegistryInput,
|
|
91
121
|
loadCurriculumCatalog,
|
|
92
122
|
loadNotationDefinitions,
|
|
93
123
|
loadNotationRegistryInput,
|
|
@@ -112,7 +142,7 @@ import {
|
|
|
112
142
|
* `lessons[].equations`, and `diagnostics.equations`.
|
|
113
143
|
* v4 (F-tables-figures): `tables`/`figures` (same shape as `equations`, for
|
|
114
144
|
* `<Table>`/`<Figure>`), `lessons[].tables`/`lessons[].figures`, and
|
|
115
|
-
* `diagnostics.tables`/`diagnostics.figures`.
|
|
145
|
+
* `diagnostics.tables`/`diagnostics.figures`/`diagnostics.diagrams`.
|
|
116
146
|
* v5 (about): `about` (engine identity from `explico`'s `package.json`, the
|
|
117
147
|
* course's reader-facing `about` block, and the knowledge version resolved
|
|
118
148
|
* against `content/CHANGELOG.md`) and `diagnostics.knowledge`.
|
|
@@ -122,8 +152,33 @@ import {
|
|
|
122
152
|
* now the number before the dot), `lessons[].chapter` redefined as the
|
|
123
153
|
* lesson's ordinal *within its part*, and `lessons[].numbering` — the
|
|
124
154
|
* per-kind offsets that make `n` run continuously across a part.
|
|
155
|
+
* v8 (assessment math): `assessmentContent` — every assessment item's
|
|
156
|
+
* `prompt` / `explanation`, pre-split into text/math segments and resolved
|
|
157
|
+
* against the scope of the lesson that embeds it, so `AssessmentSet.astro`
|
|
158
|
+
* can render them through the same KaTeX pipeline as lesson body math instead
|
|
159
|
+
* of showing raw text.
|
|
160
|
+
* v9 (diagram identity): `diagrams` (the independent `<Diagram>` number and
|
|
161
|
+
* cross-page lookup), `lessons[].diagrams`, `lessons[].numbering.diagrams`,
|
|
162
|
+
* and `diagnostics.diagrams`.
|
|
163
|
+
* v10 (knowledge-check identity): `checks` (part-wide numbering and cross-page
|
|
164
|
+
* lookup), `lessons[].knowledgeChecks`, `lessons[].numbering.checks`, and
|
|
165
|
+
* `diagnostics.checks`.
|
|
166
|
+
* v11 (lesson exports): normalized standalone HTML and print build/UI settings.
|
|
167
|
+
* v12 (abbreviation hover cards): `manifest.abbreviations` — the
|
|
168
|
+
* `abbreviations` content collection (N14), flattened to plain
|
|
169
|
+
* `{key, abbr, expansion, aliases, domain?}` records sorted by key, the way
|
|
170
|
+
* `manifest.notation.raw` already carries notation frontmatter for
|
|
171
|
+
* `remarkNotation`. `remarkAbbreviations` reads it to wrap the first
|
|
172
|
+
* occurrence of a known abbreviation in a lesson's prose with a hover/click
|
|
173
|
+
* card.
|
|
174
|
+
* v13 (raw-float diagnostics, additive): `diagnostics.rawFloats` — a raw
|
|
175
|
+
* Markdown table, a raw HTML `<table>`, or an Explico `<Diagram>` that is
|
|
176
|
+
* unkeyed or nested outside `<Table>`/`<Figure>` never reaches keyed-float
|
|
177
|
+
* validation and can never get a stable number or `[[…]]` reference; this
|
|
178
|
+
* flags it (warning-only — the manifest shape a client already reads is
|
|
179
|
+
* unchanged).
|
|
125
180
|
*/
|
|
126
|
-
export const MANIFEST_SCHEMA_VERSION =
|
|
181
|
+
export const MANIFEST_SCHEMA_VERSION = 13;
|
|
127
182
|
|
|
128
183
|
const REPOSITORY_ROOT = process.cwd();
|
|
129
184
|
// Content is the top-level `content/` folder; the build output is `build/`
|
|
@@ -214,8 +269,9 @@ export interface ManifestLesson {
|
|
|
214
269
|
/**
|
|
215
270
|
* The 1-based ordinal of the **part** this lesson sits in — its sidebar
|
|
216
271
|
* 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
|
|
272
|
+
* in every equation, table, figure, diagram, and knowledge-check number on
|
|
273
|
+
* the page: `(2.1)` is the first equation of part 2. See
|
|
274
|
+
* `docs/authoring/structure.md`.
|
|
219
275
|
*/
|
|
220
276
|
readonly part: number;
|
|
221
277
|
/**
|
|
@@ -233,6 +289,8 @@ export interface ManifestLesson {
|
|
|
233
289
|
readonly equations: number;
|
|
234
290
|
readonly tables: number;
|
|
235
291
|
readonly figures: number;
|
|
292
|
+
readonly diagrams: number;
|
|
293
|
+
readonly checks: number;
|
|
236
294
|
};
|
|
237
295
|
readonly file: string;
|
|
238
296
|
readonly title: string;
|
|
@@ -243,6 +301,11 @@ export interface ManifestLesson {
|
|
|
243
301
|
readonly requires: readonly string[];
|
|
244
302
|
readonly assumptions: readonly string[];
|
|
245
303
|
readonly assessments: readonly string[];
|
|
304
|
+
/** Numbered knowledge checks from `assessments`, in `.checks.yml` order. */
|
|
305
|
+
readonly knowledgeChecks: readonly {
|
|
306
|
+
readonly key: string;
|
|
307
|
+
readonly number: string;
|
|
308
|
+
}[];
|
|
246
309
|
readonly sources: readonly string[];
|
|
247
310
|
readonly citations: readonly ManifestCitation[];
|
|
248
311
|
/** Competency prerequisite edges this lesson introduces. */
|
|
@@ -262,6 +325,11 @@ export interface ManifestLesson {
|
|
|
262
325
|
readonly key: string;
|
|
263
326
|
readonly number: string;
|
|
264
327
|
}[];
|
|
328
|
+
/** Keyed `<Diagram>` elements in appearance order. */
|
|
329
|
+
readonly diagrams: readonly {
|
|
330
|
+
readonly key: string;
|
|
331
|
+
readonly number: string;
|
|
332
|
+
}[];
|
|
265
333
|
readonly notation: {
|
|
266
334
|
readonly bindings: readonly {
|
|
267
335
|
readonly key: string;
|
|
@@ -301,8 +369,19 @@ export interface ManifestAbout {
|
|
|
301
369
|
readonly changelogHref?: string;
|
|
302
370
|
}
|
|
303
371
|
|
|
372
|
+
/** One assessment item's pre-rendered `prompt` / `explanation` (v8). */
|
|
373
|
+
export interface ManifestAssessmentItemContent {
|
|
374
|
+
readonly lessonId: string;
|
|
375
|
+
readonly assessmentId: string;
|
|
376
|
+
readonly itemId: string;
|
|
377
|
+
readonly promptSegments: readonly ContentSegment[];
|
|
378
|
+
readonly explanationSegments: readonly ContentSegment[];
|
|
379
|
+
}
|
|
380
|
+
|
|
304
381
|
export interface Manifest {
|
|
305
382
|
readonly schemaVersion: number;
|
|
383
|
+
/** Normalized build/UI settings for opt-in per-lesson exports. */
|
|
384
|
+
readonly lessonExports: NormalizedLessonExports;
|
|
306
385
|
/** Reader-facing identity + versions (the `[?]` About panel). */
|
|
307
386
|
readonly about: ManifestAbout;
|
|
308
387
|
readonly notation: {
|
|
@@ -316,6 +395,8 @@ export interface Manifest {
|
|
|
316
395
|
readonly competencies: readonly Record<string, unknown>[];
|
|
317
396
|
readonly sources: readonly Record<string, unknown>[];
|
|
318
397
|
readonly tracks: readonly Record<string, unknown>[];
|
|
398
|
+
/** Abbreviations table (N14), for `remarkAbbreviations`. Ordered by key. */
|
|
399
|
+
readonly abbreviations: readonly Record<string, unknown>[];
|
|
319
400
|
readonly sidebar: readonly SidebarGroup[];
|
|
320
401
|
readonly lessons: readonly ManifestLesson[];
|
|
321
402
|
/** Full competency prerequisite graph. */
|
|
@@ -336,6 +417,26 @@ export interface Manifest {
|
|
|
336
417
|
readonly labels: readonly FigureLabelRecord[];
|
|
337
418
|
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
338
419
|
};
|
|
420
|
+
/** Keyed-float identity for `<Diagram>` — independent of figure numbering. */
|
|
421
|
+
readonly diagrams: {
|
|
422
|
+
readonly labels: readonly DiagramLabelRecord[];
|
|
423
|
+
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
424
|
+
};
|
|
425
|
+
/** Knowledge-check identity, derived from each lesson's `.checks.yml`. */
|
|
426
|
+
readonly checks: {
|
|
427
|
+
readonly labels: readonly KnowledgeCheckLabelRecord[];
|
|
428
|
+
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
429
|
+
};
|
|
430
|
+
/**
|
|
431
|
+
* Every assessment item's `prompt` / `explanation`, pre-split into
|
|
432
|
+
* text/math segments (v8). `AssessmentSet.astro` filters this by
|
|
433
|
+
* `lessonId` + `assessmentId` and renders each `math` segment's `latex`
|
|
434
|
+
* through `renderNotationMath` before handing the result to
|
|
435
|
+
* `AssessmentRunner`. An assessment embedded in more than one lesson (via
|
|
436
|
+
* more than one `checks.yml`) appears once per lesson here, since each
|
|
437
|
+
* lesson's scope can resolve its glyphs differently.
|
|
438
|
+
*/
|
|
439
|
+
readonly assessmentContent: readonly ManifestAssessmentItemContent[];
|
|
339
440
|
readonly diagnostics: {
|
|
340
441
|
readonly curriculum: readonly CurriculumIssue[];
|
|
341
442
|
readonly notation: readonly NotationDiagnostic[];
|
|
@@ -351,6 +452,16 @@ export interface Manifest {
|
|
|
351
452
|
readonly tables: readonly TableDiagnostic[];
|
|
352
453
|
/** `<Figure>` identity checks (F-tables-figures) — same codes, `fig-` prefixed. */
|
|
353
454
|
readonly figures: readonly FigureDiagnostic[];
|
|
455
|
+
/** `<Diagram>` identity checks — same codes, `dia-` prefixed. */
|
|
456
|
+
readonly diagrams: readonly DiagramDiagnostic[];
|
|
457
|
+
/** Knowledge-check identity and `[[check-…]]` reference checks. */
|
|
458
|
+
readonly checks: readonly KnowledgeCheckDiagnostic[];
|
|
459
|
+
/**
|
|
460
|
+
* A raw Markdown table, a raw HTML `<table>`, or an Explico `<Diagram>`
|
|
461
|
+
* that is unkeyed or nested outside `<Table>`/`<Figure>` — invisible to
|
|
462
|
+
* keyed-float validation above. Warnings only.
|
|
463
|
+
*/
|
|
464
|
+
readonly rawFloats: readonly RawFloatDiagnostic[];
|
|
354
465
|
};
|
|
355
466
|
readonly hashes: {
|
|
356
467
|
/** `sha256` of every content source file, keyed by repo-relative path. */
|
|
@@ -546,9 +657,10 @@ export async function compileManifest(
|
|
|
546
657
|
);
|
|
547
658
|
}
|
|
548
659
|
|
|
549
|
-
const [catalog, notationInput] = await Promise.all([
|
|
660
|
+
const [catalog, notationInput, abbreviationsInput] = await Promise.all([
|
|
550
661
|
loadCurriculumCatalog(),
|
|
551
662
|
loadNotationRegistryInput(),
|
|
663
|
+
loadAbbreviationRegistryInput(),
|
|
552
664
|
]);
|
|
553
665
|
|
|
554
666
|
const registry = buildNotationRegistry(notationInput);
|
|
@@ -568,19 +680,65 @@ export async function compileManifest(
|
|
|
568
680
|
registry.bundles.map((bundle) => [bundle.lessonId, bundle]),
|
|
569
681
|
);
|
|
570
682
|
|
|
683
|
+
const assessmentsInput = catalog.assessments as unknown as {
|
|
684
|
+
id: string;
|
|
685
|
+
items?: { id?: string; prompt?: string; explanation?: string }[];
|
|
686
|
+
}[];
|
|
687
|
+
|
|
571
688
|
const curriculumIssues = validateCurriculum(catalog);
|
|
572
689
|
const alignmentIssues = validateNotationAlignment(registry, catalog);
|
|
573
|
-
const mathIssues =
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
690
|
+
const mathIssues = [
|
|
691
|
+
...gateContentMath({
|
|
692
|
+
notationInput,
|
|
693
|
+
assessments: assessmentsInput,
|
|
694
|
+
lessonAssessments: catalog.lessons.map((lesson) => ({
|
|
695
|
+
lessonId: lesson.id,
|
|
696
|
+
assessmentIds: lesson.assessments,
|
|
697
|
+
})),
|
|
698
|
+
}),
|
|
699
|
+
...gateProseNotation(notationInput.lessons),
|
|
700
|
+
...gateProseNotationInAssessments(assessmentsInput),
|
|
701
|
+
...gateAbbreviations({
|
|
702
|
+
notationInput,
|
|
703
|
+
abbreviations: abbreviationsInput,
|
|
704
|
+
courseAbbreviations: courseConfig.abbreviations,
|
|
705
|
+
}),
|
|
706
|
+
];
|
|
707
|
+
|
|
708
|
+
// Pre-render every assessment `prompt` / `explanation` into text/math
|
|
709
|
+
// segments, resolved against the scope of whichever lesson embeds it —
|
|
710
|
+
// same relationship, same `pageScope`, as the `gateContentMath` assessment
|
|
711
|
+
// check above, so a symbol that passes the gate is exactly the symbol
|
|
712
|
+
// `AssessmentSet.astro` renders. An assessment shared by two lessons is
|
|
713
|
+
// rendered once per (lesson, assessment) pair, since the two lessons can
|
|
714
|
+
// have different scopes and therefore different resolved LaTeX.
|
|
715
|
+
const notationLessonById = new Map(
|
|
716
|
+
notationInput.lessons.map((lesson) => [lesson.lessonId, lesson]),
|
|
717
|
+
);
|
|
718
|
+
const assessmentInputById = new Map(
|
|
719
|
+
assessmentsInput.map((assessment) => [assessment.id, assessment]),
|
|
720
|
+
);
|
|
721
|
+
const assessmentContent: ManifestAssessmentItemContent[] = [];
|
|
722
|
+
for (const lesson of catalog.lessons) {
|
|
723
|
+
if (lesson.assessments.length === 0) continue;
|
|
724
|
+
const lessonInput = notationLessonById.get(lesson.id);
|
|
725
|
+
if (!lessonInput) continue;
|
|
726
|
+
const scope = pageScope(lessonInput, cardsByKey);
|
|
727
|
+
for (const assessmentId of lesson.assessments) {
|
|
728
|
+
const assessment = assessmentInputById.get(assessmentId);
|
|
729
|
+
if (!assessment?.items) continue;
|
|
730
|
+
for (const item of assessment.items) {
|
|
731
|
+
if (!item.id) continue;
|
|
732
|
+
assessmentContent.push({
|
|
733
|
+
lessonId: lesson.id,
|
|
734
|
+
assessmentId,
|
|
735
|
+
itemId: item.id,
|
|
736
|
+
promptSegments: splitMixedContent(item.prompt ?? '', scope),
|
|
737
|
+
explanationSegments: splitMixedContent(item.explanation ?? '', scope),
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
584
742
|
|
|
585
743
|
const prerequisitesById = new Map(
|
|
586
744
|
catalog.competencies.map((competency) => [
|
|
@@ -615,8 +773,8 @@ export async function compileManifest(
|
|
|
615
773
|
// Part and chapter, read straight off the sidebar so there is one
|
|
616
774
|
// lesson-ordering source and not two: a part is a sidebar group (parts in
|
|
617
775
|
// 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
|
-
//
|
|
776
|
+
// track order, then slug). The part is what prefixes every numbered item —
|
|
777
|
+
// `(2.1)` is the first item of one independent kind in part 2 — while `n`
|
|
620
778
|
// continues across the part's chapters rather than restarting on each page,
|
|
621
779
|
// so every lesson also needs the running count that precedes it.
|
|
622
780
|
// See `docs/authoring/structure.md`.
|
|
@@ -628,7 +786,13 @@ export async function compileManifest(
|
|
|
628
786
|
{
|
|
629
787
|
part: number;
|
|
630
788
|
chapter: number;
|
|
631
|
-
numbering: {
|
|
789
|
+
numbering: {
|
|
790
|
+
equations: number;
|
|
791
|
+
tables: number;
|
|
792
|
+
figures: number;
|
|
793
|
+
diagrams: number;
|
|
794
|
+
checks: number;
|
|
795
|
+
};
|
|
632
796
|
}
|
|
633
797
|
>();
|
|
634
798
|
let partCursor = 0;
|
|
@@ -639,7 +803,13 @@ export async function compileManifest(
|
|
|
639
803
|
if (lessonIds.length === 0) continue; // the static Reference group
|
|
640
804
|
partCursor += 1;
|
|
641
805
|
// Running totals for this part, advanced one chapter at a time.
|
|
642
|
-
const used = {
|
|
806
|
+
const used = {
|
|
807
|
+
equations: 0,
|
|
808
|
+
tables: 0,
|
|
809
|
+
figures: 0,
|
|
810
|
+
diagrams: 0,
|
|
811
|
+
checks: 0,
|
|
812
|
+
};
|
|
643
813
|
let chapterCursor = 0;
|
|
644
814
|
for (const lessonId of lessonIds) {
|
|
645
815
|
const body = bodyByLessonId.get(lessonId) ?? '';
|
|
@@ -651,10 +821,14 @@ export async function compileManifest(
|
|
|
651
821
|
used.equations += countDisplayEquations(body);
|
|
652
822
|
used.tables += countKeyedJsxFloats(body, { tagName: 'Table' });
|
|
653
823
|
used.figures += countKeyedJsxFloats(body, { tagName: 'Figure' });
|
|
824
|
+
used.diagrams += countKeyedJsxFloats(body, { tagName: 'Diagram' });
|
|
825
|
+
used.checks += lessonMetaById.get(lessonId)?.assessments.length ?? 0;
|
|
654
826
|
}
|
|
655
827
|
}
|
|
656
828
|
|
|
657
|
-
const floatInputs = (
|
|
829
|
+
const floatInputs = (
|
|
830
|
+
kind: 'equations' | 'tables' | 'figures' | 'diagrams',
|
|
831
|
+
) =>
|
|
658
832
|
notationInput.lessons.map((lesson) => {
|
|
659
833
|
const placement = placementByLessonId.get(lesson.lessonId);
|
|
660
834
|
return {
|
|
@@ -693,6 +867,48 @@ export async function compileManifest(
|
|
|
693
867
|
Object.entries(table).map(([key, number]) => ({ key, number })),
|
|
694
868
|
]),
|
|
695
869
|
);
|
|
870
|
+
const diagramValidation = validateDiagrams(floatInputs('diagrams'));
|
|
871
|
+
const diagramsBySlug = new Map(
|
|
872
|
+
Object.entries(diagramValidation.numbersBySlug).map(([slug, table]) => [
|
|
873
|
+
slug,
|
|
874
|
+
Object.entries(table).map(([key, number]) => ({ key, number })),
|
|
875
|
+
]),
|
|
876
|
+
);
|
|
877
|
+
const checkValidation = validateKnowledgeChecks(
|
|
878
|
+
notationInput.lessons.map((lesson) => {
|
|
879
|
+
const placement = placementByLessonId.get(lesson.lessonId);
|
|
880
|
+
return {
|
|
881
|
+
lessonId: lesson.lessonId,
|
|
882
|
+
slug: lesson.lessonId.replace(/\./g, '/'),
|
|
883
|
+
file: lesson.source.file,
|
|
884
|
+
body: lesson.body,
|
|
885
|
+
assessmentIds:
|
|
886
|
+
lessonMetaById.get(lesson.lessonId)?.assessments ?? [],
|
|
887
|
+
...(placement
|
|
888
|
+
? {
|
|
889
|
+
numbering: {
|
|
890
|
+
prefix: placement.part,
|
|
891
|
+
offset: placement.numbering.checks,
|
|
892
|
+
},
|
|
893
|
+
}
|
|
894
|
+
: {}),
|
|
895
|
+
};
|
|
896
|
+
}),
|
|
897
|
+
);
|
|
898
|
+
const checksBySlug = new Map(
|
|
899
|
+
Object.entries(checkValidation.numbersBySlug).map(([slug, table]) => [
|
|
900
|
+
slug,
|
|
901
|
+
Object.entries(table).map(([key, number]) => ({ key, number })),
|
|
902
|
+
]),
|
|
903
|
+
);
|
|
904
|
+
const rawFloatDiagnostics = validateRawFloats(
|
|
905
|
+
notationInput.lessons.map((lesson) => ({
|
|
906
|
+
lessonId: lesson.lessonId,
|
|
907
|
+
slug: lesson.lessonId.replace(/\./g, '/'),
|
|
908
|
+
file: lesson.source.file,
|
|
909
|
+
body: lesson.body,
|
|
910
|
+
})),
|
|
911
|
+
);
|
|
696
912
|
|
|
697
913
|
const lessons: ManifestLesson[] = [];
|
|
698
914
|
for (const notationLesson of notationInput.lessons) {
|
|
@@ -716,6 +932,8 @@ export async function compileManifest(
|
|
|
716
932
|
equations: 0,
|
|
717
933
|
tables: 0,
|
|
718
934
|
figures: 0,
|
|
935
|
+
diagrams: 0,
|
|
936
|
+
checks: 0,
|
|
719
937
|
},
|
|
720
938
|
file: notationLesson.source.file,
|
|
721
939
|
title: frontmatter.title,
|
|
@@ -725,11 +943,13 @@ export async function compileManifest(
|
|
|
725
943
|
requires: [...meta.requires],
|
|
726
944
|
assumptions: [...meta.assumptions],
|
|
727
945
|
assessments: [...meta.assessments],
|
|
946
|
+
knowledgeChecks: checksBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
728
947
|
sources: deriveSources(notationLesson.body),
|
|
729
948
|
citations: lessonCitations(notationLesson.body),
|
|
730
949
|
equations: equationsBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
731
950
|
tables: tablesBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
732
951
|
figures: figuresBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
952
|
+
diagrams: diagramsBySlug.get(id.replace(/\./g, '/')) ?? [],
|
|
733
953
|
prereqEdges: sortEdges(
|
|
734
954
|
teaches.flatMap((to) =>
|
|
735
955
|
(prerequisitesById.get(to) ?? [])
|
|
@@ -761,6 +981,9 @@ export async function compileManifest(
|
|
|
761
981
|
...(courseConfig.operators === undefined
|
|
762
982
|
? {}
|
|
763
983
|
: { operators: courseConfig.operators }),
|
|
984
|
+
...(courseConfig.romanLabels === undefined
|
|
985
|
+
? {}
|
|
986
|
+
: { romanLabels: courseConfig.romanLabels }),
|
|
764
987
|
});
|
|
765
988
|
|
|
766
989
|
const rawNotation = [...loadNotationDefinitions()].sort((a, b) =>
|
|
@@ -771,6 +994,15 @@ export async function compileManifest(
|
|
|
771
994
|
.sort(byId);
|
|
772
995
|
const sources = [...loadSourceRecords()].sort(byId);
|
|
773
996
|
const tracks = catalog.tracks.map((entry) => ({ ...entry })).sort(byId);
|
|
997
|
+
const abbreviations = abbreviationsInput
|
|
998
|
+
.map((entry) => ({
|
|
999
|
+
key: entry.key,
|
|
1000
|
+
abbr: entry.abbr,
|
|
1001
|
+
expansion: entry.expansion,
|
|
1002
|
+
aliases: entry.aliases,
|
|
1003
|
+
...(entry.domain === undefined ? {} : { domain: entry.domain }),
|
|
1004
|
+
}))
|
|
1005
|
+
.sort((a, b) => a.key.localeCompare(b.key));
|
|
774
1006
|
|
|
775
1007
|
const hashes = contentHashes();
|
|
776
1008
|
|
|
@@ -819,6 +1051,7 @@ export async function compileManifest(
|
|
|
819
1051
|
|
|
820
1052
|
return {
|
|
821
1053
|
schemaVersion: MANIFEST_SCHEMA_VERSION,
|
|
1054
|
+
lessonExports: normalizeLessonExports(courseConfig),
|
|
822
1055
|
about,
|
|
823
1056
|
notation: {
|
|
824
1057
|
definitions: registry.definitions,
|
|
@@ -830,6 +1063,7 @@ export async function compileManifest(
|
|
|
830
1063
|
competencies,
|
|
831
1064
|
sources,
|
|
832
1065
|
tracks,
|
|
1066
|
+
abbreviations,
|
|
833
1067
|
sidebar,
|
|
834
1068
|
lessons,
|
|
835
1069
|
prereqEdges,
|
|
@@ -845,6 +1079,15 @@ export async function compileManifest(
|
|
|
845
1079
|
labels: figureValidation.labels,
|
|
846
1080
|
numbersBySlug: figureValidation.numbersBySlug,
|
|
847
1081
|
},
|
|
1082
|
+
diagrams: {
|
|
1083
|
+
labels: diagramValidation.labels,
|
|
1084
|
+
numbersBySlug: diagramValidation.numbersBySlug,
|
|
1085
|
+
},
|
|
1086
|
+
checks: {
|
|
1087
|
+
labels: checkValidation.labels,
|
|
1088
|
+
numbersBySlug: checkValidation.numbersBySlug,
|
|
1089
|
+
},
|
|
1090
|
+
assessmentContent,
|
|
848
1091
|
diagnostics: {
|
|
849
1092
|
curriculum: curriculumIssues,
|
|
850
1093
|
notation: registry.diagnostics,
|
|
@@ -854,7 +1097,10 @@ export async function compileManifest(
|
|
|
854
1097
|
equations: equationValidation.diagnostics,
|
|
855
1098
|
tables: tableValidation.diagnostics,
|
|
856
1099
|
figures: figureValidation.diagnostics,
|
|
1100
|
+
diagrams: diagramValidation.diagnostics,
|
|
1101
|
+
checks: checkValidation.diagnostics,
|
|
857
1102
|
knowledge: knowledge.diagnostics,
|
|
1103
|
+
rawFloats: rawFloatDiagnostics,
|
|
858
1104
|
},
|
|
859
1105
|
hashes,
|
|
860
1106
|
};
|
|
@@ -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>
|