explico 0.13.0 → 0.14.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 +894 -1
- package/README.md +200 -30
- package/dist/compiler/astro.d.ts +15 -2
- package/dist/compiler/astro.js +40 -11
- package/dist/compiler/cli-main.d.ts +43 -0
- package/dist/compiler/cli-main.js +392 -0
- package/dist/compiler/cli.d.ts +424 -9
- package/dist/compiler/cli.js +1289 -8
- package/dist/compiler/collections.d.ts +88 -1
- package/dist/compiler/collections.js +170 -25
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +105 -1
- package/dist/compiler/decks.d.ts +161 -0
- package/dist/compiler/decks.js +235 -0
- package/dist/compiler/exports/combined-html.d.ts +5 -1
- package/dist/compiler/exports/deck-fit.d.ts +55 -0
- package/dist/compiler/exports/deck-fit.js +90 -0
- package/dist/compiler/exports/export-chapters.d.ts +37 -6
- package/dist/compiler/exports/export-chapters.js +64 -5
- package/dist/compiler/exports/export-pages.d.ts +30 -3
- package/dist/compiler/exports/export-pages.js +24 -1
- package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/dist/compiler/exports/latex/cache.d.ts +78 -0
- package/dist/compiler/exports/latex/cache.js +129 -0
- package/dist/compiler/exports/latex/capture.d.ts +10 -1
- package/dist/compiler/exports/latex/capture.js +3 -1
- package/dist/compiler/exports/latex/compile.d.ts +13 -2
- package/dist/compiler/exports/latex/compile.js +12 -1
- package/dist/compiler/exports/latex/deck.d.ts +92 -0
- package/dist/compiler/exports/latex/deck.js +349 -0
- package/dist/compiler/exports/latex/document.d.ts +51 -6
- package/dist/compiler/exports/latex/document.js +71 -12
- package/dist/compiler/exports/latex/explico-common.sty +198 -0
- package/dist/compiler/exports/latex/explico.cls +13 -183
- package/dist/compiler/exports/latex/serialize.d.ts +60 -4
- package/dist/compiler/exports/latex/serialize.js +4 -2
- package/dist/compiler/exports/latex-export.d.ts +24 -2
- package/dist/compiler/exports/latex-export.js +107 -26
- package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
- package/dist/compiler/exports/markdown/serialize.js +85 -8
- package/dist/compiler/exports/markdown-export.d.ts +6 -1
- package/dist/compiler/exports/markdown-export.js +50 -11
- package/dist/compiler/exports/standalone-html.d.ts +10 -5
- package/dist/compiler/exports/standalone-html.js +3 -3
- package/dist/compiler/exports/static-tree.d.ts +38 -1
- package/dist/compiler/exports/static-tree.js +120 -4
- package/dist/compiler/knowledge-version.d.ts +26 -2
- package/dist/compiler/knowledge-version.js +27 -17
- package/dist/compiler/lesson-derivation.d.ts +143 -6
- package/dist/compiler/lesson-derivation.js +145 -13
- package/dist/compiler/manifest.d.ts +133 -4
- package/dist/compiler/manifest.js +359 -58
- package/dist/compiler/page-versions.d.ts +160 -0
- package/dist/compiler/page-versions.js +198 -0
- package/dist/compiler/render-diagnostics.d.ts +5 -1
- package/dist/compiler/resolution-report.d.ts +24 -6
- package/dist/compiler/resolution-report.js +22 -4
- package/dist/compiler/validate-content.d.ts +7 -0
- package/dist/compiler/validate-content.js +160 -0
- package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
- package/dist/curriculum/competency-graph.d.ts +54 -0
- package/dist/curriculum/competency-graph.js +41 -0
- package/dist/reference/base-library.d.mts +8 -0
- package/dist/reference/base-library.mjs +19 -0
- package/dist/reference/consistency.d.ts +8 -4
- package/dist/reference/consistency.js +76 -26
- package/dist/reference/deck-remark.d.mts +123 -0
- package/dist/reference/deck-remark.mjs +600 -0
- package/dist/reference/deck-source.d.ts +107 -0
- package/dist/reference/deck-source.js +140 -0
- package/dist/reference/decks-validate.d.ts +133 -0
- package/dist/reference/decks-validate.js +359 -0
- package/dist/reference/derived-entries.d.ts +5 -1
- package/dist/reference/diagram-fit.d.ts +1 -1
- package/dist/reference/figure-symbols.d.ts +23 -6
- package/dist/reference/figure-symbols.js +51 -15
- package/dist/reference/float-name.d.ts +43 -0
- package/dist/reference/float-name.js +58 -0
- package/dist/reference/folds-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-indices.js +5 -4
- package/dist/reference/gate-math.d.ts +29 -2
- package/dist/reference/gate-math.js +99 -18
- package/dist/reference/gate-part-pages.d.ts +1 -1
- package/dist/reference/lab-katex-validate.d.ts +10 -6
- package/dist/reference/lab-katex-validate.js +17 -9
- package/dist/reference/lab-math-scope.js +29 -4
- package/dist/reference/math-glyphs.d.mts +25 -0
- package/dist/reference/math-glyphs.mjs +189 -25
- package/dist/reference/page-notation.d.ts +22 -3
- package/dist/reference/page-notation.js +32 -10
- package/dist/reference/references.d.ts +14 -1
- package/dist/reference/references.js +13 -0
- package/dist/reference/registry-scope.d.mts +33 -0
- package/dist/reference/registry-scope.mjs +34 -0
- package/dist/reference/registry.js +29 -10
- package/dist/reference/rehype-deck.d.mts +6 -0
- package/dist/reference/rehype-deck.mjs +286 -0
- package/dist/reference/rehype-heading-anchors.d.mts +20 -0
- package/dist/reference/rehype-heading-anchors.mjs +48 -6
- package/dist/reference/remark-notation.mjs +249 -40
- package/dist/reference/source-lines.d.mts +12 -0
- package/dist/reference/source-lines.mjs +65 -0
- package/dist/reference/types.d.ts +28 -0
- package/dist/rules/catalog.d.ts +21 -4
- package/dist/rules/catalog.js +221 -2
- package/dist/rules/course-rules.d.ts +5 -1
- package/dist/rules/course-rules.js +17 -3
- package/dist/rules/course-rules.schema.json +12 -0
- package/dist/rules/in-effect.d.ts +13 -3
- package/dist/rules/in-effect.js +30 -7
- package/dist/rules/rules.schema.json +259 -1
- package/dist/rules/settings.d.ts +1 -1
- package/dist/session/PreferenceStore.d.ts +6 -0
- package/dist/session/PreferenceStore.js +18 -1
- package/dist/session/deck-memory.d.ts +116 -0
- package/dist/session/deck-memory.js +143 -0
- package/dist/session/reading-memory.d.ts +96 -0
- package/dist/session/reading-memory.js +133 -0
- package/docs/VERSIONING.md +21 -18
- package/docs/authoring/README.md +29 -9
- package/docs/authoring/agents.md +63 -16
- package/docs/authoring/components.md +15 -0
- package/docs/authoring/deck.md +588 -0
- package/docs/authoring/migrating.md +1087 -14
- package/docs/authoring/notation.md +49 -6
- package/docs/authoring/structure.md +80 -1
- package/docs/authoring/versioning.md +54 -2
- package/docs/authoring/visualizations.md +7 -2
- package/docs/course-agent/AGENTS.md +37 -0
- package/docs/course-agent/agents/rules-reviewer.md +33 -0
- package/docs/course-agent/skills/draft-lesson/SKILL.md +37 -0
- package/docs/course-agent/skills/migrate-course/SKILL.md +31 -0
- package/docs/course-agent/skills/release-content/SKILL.md +24 -0
- package/docs/diagram-scaling.md +148 -0
- package/docs/privacy.md +59 -0
- package/package.json +8 -2
- package/src/compiler/astro.ts +60 -21
- package/src/compiler/cli-main.ts +469 -0
- package/src/compiler/cli.ts +1905 -16
- package/src/compiler/collections.ts +284 -25
- package/src/compiler/course-config.ts +267 -0
- package/src/compiler/decks.ts +455 -0
- package/src/compiler/exports/deck-fit.ts +127 -0
- package/src/compiler/exports/export-chapters.ts +105 -6
- package/src/compiler/exports/export-pages.ts +46 -2
- package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/src/compiler/exports/latex/cache.ts +183 -0
- package/src/compiler/exports/latex/capture.ts +5 -2
- package/src/compiler/exports/latex/compile.ts +18 -0
- package/src/compiler/exports/latex/deck.ts +419 -0
- package/src/compiler/exports/latex/document.ts +117 -21
- package/src/compiler/exports/latex/explico-common.sty +198 -0
- package/src/compiler/exports/latex/explico.cls +13 -183
- package/src/compiler/exports/latex/serialize.ts +25 -21
- package/src/compiler/exports/latex/texlive-packages.txt +6 -2
- package/src/compiler/exports/latex-export.ts +187 -45
- package/src/compiler/exports/markdown/serialize.ts +118 -9
- package/src/compiler/exports/markdown-export.ts +56 -11
- package/src/compiler/exports/standalone-html.ts +8 -7
- package/src/compiler/exports/static-tree.ts +175 -3
- package/src/compiler/knowledge-version.ts +56 -21
- package/src/compiler/lesson-derivation.ts +282 -16
- package/src/compiler/manifest.ts +558 -63
- package/src/compiler/page-versions.ts +359 -0
- package/src/compiler/resolution-report.ts +38 -7
- package/src/compiler/validate-content.ts +212 -0
- package/src/components/CourseContents.astro +124 -6
- package/src/components/CurriculumMap.astro +49 -1
- package/src/components/FloatIndex.astro +1 -1
- package/src/components/PartOverview.astro +51 -5
- package/src/components/assessments/AssessmentSet.astro +41 -15
- package/src/components/block-heading.ts +15 -6
- package/src/components/card-carry.ts +7 -4
- package/src/components/card-manager.ts +14 -2
- package/src/components/card-memory.ts +13 -9
- package/src/components/deck/DeckEnhancer.astro +13 -0
- package/src/components/deck/deck-context.ts +104 -0
- package/src/components/deck/deck-controller.ts +1287 -0
- package/src/components/deck/deck-map.ts +91 -0
- package/src/components/deck-lane.ts +163 -0
- package/src/components/diagram/Diagram.astro +131 -86
- package/src/components/disclosure/persisted-disclosure.ts +9 -1
- package/src/components/figure/Figure.astro +7 -2
- package/src/components/figure/FloatCaption.astro +5 -3
- package/src/components/fold/Fold.astro +2 -2
- package/src/components/notation/NotationFormula.astro +81 -11
- package/src/components/notation/NotationGlossary.astro +132 -1
- package/src/components/notation/NotationLayer.astro +29 -0
- package/src/components/starlight/AboutPanel.astro +186 -3
- package/src/components/starlight/CollapsibleSections.astro +24 -3
- package/src/components/starlight/DiagramEnhancer.astro +5 -1
- package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
- package/src/components/starlight/LayoutHeader.astro +17 -0
- package/src/components/starlight/LayoutPageTitle.astro +110 -0
- package/src/components/starlight/LayoutSidebar.astro +807 -21
- package/src/components/starlight/LayoutSocialIcons.astro +62 -6
- package/src/components/starlight/LessonExportActions.astro +27 -9
- package/src/components/starlight/LessonFooter.astro +59 -0
- package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
- package/src/components/starlight/TextSizeControl.astro +24 -13
- package/src/components/starlight/page-marks.ts +139 -0
- package/src/components/starlight/section-summary.ts +1 -1
- package/src/components/starlight/sidebar-editions.ts +245 -0
- package/src/components/table/Table.astro +15 -7
- package/src/content-config.ts +87 -1
- package/src/curriculum/competency-graph.ts +95 -0
- package/src/reference/base-library.mjs +19 -0
- package/src/reference/consistency.ts +126 -39
- package/src/reference/deck-remark.mjs +600 -0
- package/src/reference/deck-source.ts +273 -0
- package/src/reference/decks-validate.ts +604 -0
- package/src/reference/diagram-layout.ts +3 -1
- package/src/reference/figure-symbols.ts +108 -31
- package/src/reference/folds-validate.ts +1 -1
- package/src/reference/gate-indices.ts +5 -3
- package/src/reference/gate-math.ts +169 -25
- package/src/reference/lab-katex-validate.ts +30 -15
- package/src/reference/lab-math-scope.ts +34 -3
- package/src/reference/math-glyphs.mjs +189 -25
- package/src/reference/page-notation.ts +69 -15
- package/src/reference/references.ts +28 -0
- package/src/reference/registry-scope.mjs +34 -0
- package/src/reference/registry.ts +41 -11
- package/src/reference/rehype-deck.mjs +286 -0
- package/src/reference/rehype-heading-anchors.mjs +48 -6
- package/src/reference/remark-notation.mjs +249 -40
- package/src/reference/source-lines.mjs +65 -0
- package/src/reference/types.ts +28 -0
- package/src/rules/catalog.ts +257 -4
- package/src/rules/course-rules.ts +21 -1
- package/src/rules/in-effect.ts +54 -8
- package/src/session/PreferenceStore.ts +23 -0
- package/src/session/deck-memory.ts +263 -0
- package/src/session/reading-memory.ts +243 -0
- package/src/styles/deck.css +1150 -0
- package/src/styles/global.css +958 -27
- package/src/styles/zones.css +277 -0
- /package/src/{components → reference}/float-name.ts +0 -0
|
@@ -382,6 +382,154 @@ interface NormalizedCards {
|
|
|
382
382
|
declare const CARDS_DEFAULTS: NormalizedCards;
|
|
383
383
|
/** Validate `CourseConfig.cards` and apply its defaults. Throws on a bad value. */
|
|
384
384
|
declare function normalizeCards(config: CourseConfig): NormalizedCards;
|
|
385
|
+
/**
|
|
386
|
+
* The marks the sidebar puts beside a chapter or part page for one reader
|
|
387
|
+
* (`sidebarMarks` in the course settings, `page-marks.ts`): a quiet ring
|
|
388
|
+
* while they have not opened it, an accent dot when its content changed
|
|
389
|
+
* since they last did. Every field is optional; {@link normalizeSidebarMarks}
|
|
390
|
+
* fills in the defaults.
|
|
391
|
+
*/
|
|
392
|
+
interface CourseSidebarMarks {
|
|
393
|
+
/** Mark a page the reader has not opened yet. Default `true`. */
|
|
394
|
+
readonly unopened?: boolean;
|
|
395
|
+
/** Mark a page whose content changed since the reader last opened it. Default `true`. */
|
|
396
|
+
readonly changed?: boolean;
|
|
397
|
+
}
|
|
398
|
+
/** {@link CourseSidebarMarks} with every default applied. */
|
|
399
|
+
interface NormalizedSidebarMarks {
|
|
400
|
+
readonly unopened: boolean;
|
|
401
|
+
readonly changed: boolean;
|
|
402
|
+
}
|
|
403
|
+
declare const SIDEBAR_MARKS_DEFAULTS: NormalizedSidebarMarks;
|
|
404
|
+
/** Validate `CourseConfig.sidebarMarks` and apply its defaults. Throws on a bad value. */
|
|
405
|
+
declare function normalizeSidebarMarks(config: CourseConfig): NormalizedSidebarMarks;
|
|
406
|
+
/**
|
|
407
|
+
* Which edition of a lesson its sidebar row opens, for a reader who has not
|
|
408
|
+
* chosen (`sidebarEditions` in the course settings, `sidebar-editions.ts`). A
|
|
409
|
+
* lesson with a chapter and a deck is one row: the row opens the **lead**
|
|
410
|
+
* edition, and the **other** is an icon at the row's end, or not listed. A
|
|
411
|
+
* reader switches both in the sidebar's foot. The plain `string` fields
|
|
412
|
+
* arrive as JSON; {@link normalizeSidebarEditions} rejects any value it does
|
|
413
|
+
* not list.
|
|
414
|
+
*/
|
|
415
|
+
interface CourseSidebarEditions {
|
|
416
|
+
/** The edition a lesson's row opens: `chapter` (default) or `slides`. */
|
|
417
|
+
readonly lead?: string;
|
|
418
|
+
/** Whether the row also lists the other edition: `listed` (default) or `hidden`. */
|
|
419
|
+
readonly other?: string;
|
|
420
|
+
}
|
|
421
|
+
type SidebarEditionLead = 'chapter' | 'slides';
|
|
422
|
+
type SidebarEditionOther = 'listed' | 'hidden';
|
|
423
|
+
/** {@link CourseSidebarEditions} with every default applied. */
|
|
424
|
+
interface NormalizedSidebarEditions {
|
|
425
|
+
readonly lead: SidebarEditionLead;
|
|
426
|
+
readonly other: SidebarEditionOther;
|
|
427
|
+
}
|
|
428
|
+
/** The engine's defaults (decision D10): the chapter leads, the slides are listed. */
|
|
429
|
+
declare const SIDEBAR_EDITIONS_DEFAULTS: NormalizedSidebarEditions;
|
|
430
|
+
/** Validate `CourseConfig.sidebarEditions` and apply its defaults. Throws on a bad value. */
|
|
431
|
+
declare function normalizeSidebarEditions(config: CourseConfig): NormalizedSidebarEditions;
|
|
432
|
+
/**
|
|
433
|
+
* A course's decks: the slide edition of a lesson (`<lesson>.deck.mdx`,
|
|
434
|
+
* `docs/authoring/deck.md`). Every field is optional; {@link normalizeDeck}
|
|
435
|
+
* fills in the defaults. The plain `string` fields arrive as JSON, and
|
|
436
|
+
* {@link normalizeDeck} rejects any value it does not list.
|
|
437
|
+
*/
|
|
438
|
+
interface CourseDeck {
|
|
439
|
+
/**
|
|
440
|
+
* The canvas every deck of the course is laid out on: `16:9` (default),
|
|
441
|
+
* `16:10` or `4:3`. One setting for the course, because the frame-fit check,
|
|
442
|
+
* the captured figures and the PDF's page all read it.
|
|
443
|
+
*/
|
|
444
|
+
readonly aspectRatio?: string;
|
|
445
|
+
/**
|
|
446
|
+
* The line on a frame that names the symbols it shows: `first-use`
|
|
447
|
+
* (default, the symbols the deck has not shown before), `every-frame`
|
|
448
|
+
* (every symbol on the frame), or `off`.
|
|
449
|
+
*/
|
|
450
|
+
readonly legend?: string;
|
|
451
|
+
/**
|
|
452
|
+
* Who sees a frame's speaker notes: `reader` (default — under each frame in
|
|
453
|
+
* the scroll view, in the presenter window and in the handout), `presenter`
|
|
454
|
+
* (the presenter window and the handout only), or `off` (nowhere; the
|
|
455
|
+
* handout PDF is not built).
|
|
456
|
+
*/
|
|
457
|
+
readonly notes?: string;
|
|
458
|
+
/** End each deck on a frame of what its lesson's competencies say a student can now do. Default `true`. */
|
|
459
|
+
readonly outcomesFrame?: boolean;
|
|
460
|
+
/** Offer the slides-with-notes PDF beside the slides PDF. Default `true`. */
|
|
461
|
+
readonly handoutPdf?: boolean;
|
|
462
|
+
}
|
|
463
|
+
type DeckAspectRatio = '16:9' | '16:10' | '4:3';
|
|
464
|
+
type DeckLegend = 'first-use' | 'every-frame' | 'off';
|
|
465
|
+
type DeckNotes = 'reader' | 'presenter' | 'off';
|
|
466
|
+
/**
|
|
467
|
+
* The logical canvas a frame is laid out on, in CSS pixels: the width of the
|
|
468
|
+
* page's reading column, so a figure, a table or a lab lays out on a frame at
|
|
469
|
+
* the size it was made for, and the frame is scaled to the screen. Every
|
|
470
|
+
* ratio is 450 px tall, so the type scale and a frame's capacity in lines are
|
|
471
|
+
* the same in all three. Printed, the canvas is scaled by 1.6 onto a page
|
|
472
|
+
* 7.5 in tall (13.333 × 7.5 in for 16:9).
|
|
473
|
+
*/
|
|
474
|
+
declare const DECK_CANVAS: Readonly<Record<DeckAspectRatio, {
|
|
475
|
+
readonly width: number;
|
|
476
|
+
readonly height: number;
|
|
477
|
+
}>>;
|
|
478
|
+
/** {@link CourseDeck} with every default applied, as the manifest publishes it. */
|
|
479
|
+
interface NormalizedDeck {
|
|
480
|
+
readonly aspectRatio: DeckAspectRatio;
|
|
481
|
+
readonly legend: DeckLegend;
|
|
482
|
+
readonly notes: DeckNotes;
|
|
483
|
+
readonly outcomesFrame: boolean;
|
|
484
|
+
readonly handoutPdf: boolean;
|
|
485
|
+
readonly canvas: {
|
|
486
|
+
readonly width: number;
|
|
487
|
+
readonly height: number;
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
declare const DECK_DEFAULTS: Omit<NormalizedDeck, 'canvas'>;
|
|
491
|
+
/** Validate `CourseConfig.deck` and apply its defaults. Throws on a bad value. */
|
|
492
|
+
declare function normalizeDeck(config: CourseConfig): NormalizedDeck;
|
|
493
|
+
/**
|
|
494
|
+
* Whether the course asks each reader if it may remember their reading in
|
|
495
|
+
* their browser (`readingMemory` in the course settings,
|
|
496
|
+
* `session/reading-memory.ts`): which chapters they opened (the sidebar's
|
|
497
|
+
* marks) and the cards they keep or carry, across visits. `ask` (default)
|
|
498
|
+
* shows the question until the reader answers; `off` never asks and never
|
|
499
|
+
* keeps reading memory, so the marks are off and cards last as long as the tab.
|
|
500
|
+
*/
|
|
501
|
+
type ReadingMemorySetting = 'ask' | 'off';
|
|
502
|
+
/** Validate `CourseConfig.readingMemory`. Throws on a bad value. */
|
|
503
|
+
declare function normalizeReadingMemory(config: CourseConfig): ReadingMemorySetting;
|
|
504
|
+
/**
|
|
505
|
+
* How the page tells its zones apart, and how a deck's slides sit on it
|
|
506
|
+
* (`appearance` in the course settings; `zones.css` and `deck.css` hold the
|
|
507
|
+
* colours). `zones`: `tonal` (the default) gives both sidebars one step of the
|
|
508
|
+
* neutral grey and the header a step further; `accent-header` makes the
|
|
509
|
+
* header a deep band of the course accent; `paper` lays the reading column
|
|
510
|
+
* on the sidebars' colour as a sheet; `none` keeps Starlight's colours.
|
|
511
|
+
* `slides`: `lifted` (the default) gives each slide an edge and a drop
|
|
512
|
+
* shadow, and in the dark theme a sheet a step lighter than the page; `edge`
|
|
513
|
+
* the edge and shadow on the page's own colour; `sorter` lays the deck on a
|
|
514
|
+
* tinted well. Every field is optional; {@link normalizeAppearance} fills in
|
|
515
|
+
* the defaults.
|
|
516
|
+
*/
|
|
517
|
+
interface CourseAppearance {
|
|
518
|
+
/** `tonal` (default), `accent-header`, `paper` or `none`. */
|
|
519
|
+
readonly zones?: string;
|
|
520
|
+
/** `lifted` (default), `edge` or `sorter`. */
|
|
521
|
+
readonly slides?: string;
|
|
522
|
+
}
|
|
523
|
+
type AppearanceZones = 'tonal' | 'accent-header' | 'paper' | 'none';
|
|
524
|
+
type AppearanceSlides = 'lifted' | 'edge' | 'sorter';
|
|
525
|
+
/** {@link CourseAppearance} with every default applied. */
|
|
526
|
+
interface NormalizedAppearance {
|
|
527
|
+
readonly zones: AppearanceZones;
|
|
528
|
+
readonly slides: AppearanceSlides;
|
|
529
|
+
}
|
|
530
|
+
declare const APPEARANCE_DEFAULTS: NormalizedAppearance;
|
|
531
|
+
/** Validate `CourseConfig.appearance` and apply its defaults. Throws on a bad value. */
|
|
532
|
+
declare function normalizeAppearance(config: CourseConfig): NormalizedAppearance;
|
|
385
533
|
/**
|
|
386
534
|
* Rule S2's word ranges (`rules.S2.bridgeMinWords` … in the course settings),
|
|
387
535
|
* in the shape `gate-part-pages.ts` takes. Built by `partPageLimits` in
|
|
@@ -487,6 +635,21 @@ interface CourseConfig {
|
|
|
487
635
|
readonly numbering?: CourseNumbering;
|
|
488
636
|
/** How explanation cards start for a reader: "Carry cards" on or off. */
|
|
489
637
|
readonly cards?: CourseCards;
|
|
638
|
+
/** The sidebar's marks for a page the reader has not opened, or that changed since. */
|
|
639
|
+
readonly sidebarMarks?: CourseSidebarMarks;
|
|
640
|
+
/** Which edition a lesson's sidebar row opens, and whether it lists the other. */
|
|
641
|
+
readonly sidebarEditions?: CourseSidebarEditions;
|
|
642
|
+
/** How the course's decks look and what they show (`docs/authoring/deck.md`). */
|
|
643
|
+
readonly deck?: CourseDeck;
|
|
644
|
+
/**
|
|
645
|
+
* Ask each reader whether the course may remember their reading in their
|
|
646
|
+
* browser (`ask`, the default), or never keep it (`off`). A plain `string`
|
|
647
|
+
* because the settings arrive as JSON; {@link normalizeReadingMemory}
|
|
648
|
+
* rejects any other value.
|
|
649
|
+
*/
|
|
650
|
+
readonly readingMemory?: string;
|
|
651
|
+
/** How the page tells header, sidebars and article apart, and how slides sit on it. */
|
|
652
|
+
readonly appearance?: CourseAppearance;
|
|
490
653
|
/**
|
|
491
654
|
* How a check's and an example set's heading names its kind: `icon-word`
|
|
492
655
|
* (default) or `icon` ({@link BlockLabels}). A plain `string` because the
|
|
@@ -504,4 +667,4 @@ declare function normalizeLessonExports(config: CourseConfig): NormalizedLessonE
|
|
|
504
667
|
*/
|
|
505
668
|
declare function coursePartsOf(config: CourseConfig): readonly CoursePart[];
|
|
506
669
|
|
|
507
|
-
export {
|
|
670
|
+
export { SIDEBAR_EDITIONS_DEFAULTS as $, APPEARANCE_DEFAULTS as A, type BlockLabels as B, type CourseConfig as C, CARDS_DEFAULTS as D, type CourseAbout as E, type CourseAppearance as F, type CourseAuthor as G, type CourseCards as H, type CourseDeck as I, type CourseDisclosure as J, type CourseHeadingDisclosure as K, type CourseNumbering as L, type ManifestRules as M, type NormalizedLessonExports as N, type CourseOpenDefault as O, type CoursePart as P, type CourseSection as Q, type ReadingMemorySetting as R, type CourseSidebarEditions as S, type CourseSidebarMarks as T, DECK_CANVAS as U, DECK_DEFAULTS as V, DISCLOSURE_DEFAULTS as W, type DeckAspectRatio as X, type DeckLegend as Y, type DeckNotes as Z, type LessonExportsConfig as _, type NormalizedDisclosure as a, SIDEBAR_MARKS_DEFAULTS as a0, type SidebarEditionLead as a1, type SidebarEditionOther as a2, coursePartsOf as a3, normalizeAppearance as a4, normalizeBlockLabels as a5, normalizeCards as a6, normalizeDeck as a7, normalizeDisclosure as a8, normalizeLessonExports as a9, normalizeNumbering as aa, normalizeReadingMemory as ab, normalizeSidebarEditions as ac, normalizeSidebarMarks as ad, type NormalizedNumbering as b, type NormalizedCards as c, type NormalizedSidebarMarks as d, type NormalizedSidebarEditions as e, type NormalizedAppearance as f, type NormalizedDeck as g, type ConfigDiagnostic as h, type CourseOperators as i, type CourseRomanLabels as j, type CourseAccents as k, type CourseAbbreviations as l, type NormalizedPartPages as m, type CourseRulesConfig as n, type ResolvedRules as o, type RuleSettingsInput as p, catalogIds as q, disabledCodes as r, dropDisabled as s, partPageLimits as t, resolveRules as u, rulesJsonSchema as v, rulesSnapshot as w, type AppearanceSlides as x, type AppearanceZones as y, BLOCK_LABELS_DEFAULT as z };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The competency dependency graph, cut into one piece per part
|
|
3
|
+
* (`<CurriculumMap graph="per-part">`).
|
|
4
|
+
*
|
|
5
|
+
* The whole-course graph is one diagram, and a course with many competencies
|
|
6
|
+
* lays it out far wider than the lesson column (rule V8) — narrower boxes do
|
|
7
|
+
* not help, since the widest rank holds a dozen competencies. One diagram per
|
|
8
|
+
* part keeps each to the competencies a reader meets together.
|
|
9
|
+
*/
|
|
10
|
+
interface GraphNode {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly text: string;
|
|
13
|
+
}
|
|
14
|
+
interface GraphEdge {
|
|
15
|
+
readonly from: string;
|
|
16
|
+
readonly to: string;
|
|
17
|
+
}
|
|
18
|
+
interface PartGraph {
|
|
19
|
+
/** The diagram's id, unique on the page: `competency-graph-<part dir>`. */
|
|
20
|
+
readonly id: string;
|
|
21
|
+
/** The part's overview title, its label when it has none; the untaught group's name. */
|
|
22
|
+
readonly title: string;
|
|
23
|
+
/** The competencies this group teaches, then one box per earlier competency they build on. */
|
|
24
|
+
readonly nodes: readonly GraphNode[];
|
|
25
|
+
/** An arrow from each prerequisite to a competency the group teaches. */
|
|
26
|
+
readonly edges: readonly GraphEdge[];
|
|
27
|
+
}
|
|
28
|
+
interface PartInput {
|
|
29
|
+
readonly dir: string;
|
|
30
|
+
readonly label: string;
|
|
31
|
+
/** Lesson ids in chapter order. */
|
|
32
|
+
readonly lessons: readonly string[];
|
|
33
|
+
readonly overview?: {
|
|
34
|
+
readonly title: string;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
interface CompetencyInput {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
readonly title: string;
|
|
40
|
+
readonly prerequisites: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
declare const UNTAUGHT_GRAPH_ID = "competency-graph-untaught";
|
|
43
|
+
declare const UNTAUGHT_TITLE = "Competencies no lesson teaches";
|
|
44
|
+
/**
|
|
45
|
+
* One graph per part, in `parts` order, then a last one for competencies no
|
|
46
|
+
* lesson teaches, so every competency is drawn once as a taught box. A
|
|
47
|
+
* competency belongs to the first part whose lessons teach it. A prerequisite
|
|
48
|
+
* taught in another part is drawn in this one as a box labelled
|
|
49
|
+
* `<part label>: <title>`, so the arrow into the competency still shows.
|
|
50
|
+
* A part that teaches no competency has no graph.
|
|
51
|
+
*/
|
|
52
|
+
declare function competencyGraphsByPart(parts: readonly PartInput[], teachesByLesson: ReadonlyMap<string, readonly string[]>, competencies: readonly CompetencyInput[]): PartGraph[];
|
|
53
|
+
|
|
54
|
+
export { type CompetencyInput, type GraphEdge, type GraphNode, type PartGraph, type PartInput, UNTAUGHT_GRAPH_ID, UNTAUGHT_TITLE, competencyGraphsByPart };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const UNTAUGHT_GRAPH_ID = "competency-graph-untaught";
|
|
2
|
+
const UNTAUGHT_TITLE = "Competencies no lesson teaches";
|
|
3
|
+
function competencyGraphsByPart(parts, teachesByLesson, competencies) {
|
|
4
|
+
const partOf = /* @__PURE__ */ new Map();
|
|
5
|
+
for (const part of parts) {
|
|
6
|
+
for (const lessonId of part.lessons) {
|
|
7
|
+
for (const id of teachesByLesson.get(lessonId) ?? []) {
|
|
8
|
+
if (!partOf.has(id)) partOf.set(id, part);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const titleOf = new Map(competencies.map((competency) => [competency.id, competency.title]));
|
|
13
|
+
const graphs = [];
|
|
14
|
+
for (const part of [...parts, void 0]) {
|
|
15
|
+
const taught = competencies.filter((competency) => partOf.get(competency.id) === part);
|
|
16
|
+
if (taught.length === 0) continue;
|
|
17
|
+
const earlier = [...new Set(taught.flatMap((competency) => competency.prerequisites))].filter(
|
|
18
|
+
(id) => partOf.get(id) !== part
|
|
19
|
+
);
|
|
20
|
+
graphs.push({
|
|
21
|
+
id: part ? `competency-graph-${part.dir.replace(/[^A-Za-z0-9_-]/g, "-")}` : UNTAUGHT_GRAPH_ID,
|
|
22
|
+
title: part ? part.overview?.title ?? part.label : UNTAUGHT_TITLE,
|
|
23
|
+
nodes: [
|
|
24
|
+
...taught.map((competency) => ({ id: competency.id, text: competency.title })),
|
|
25
|
+
...earlier.map((id) => ({
|
|
26
|
+
id,
|
|
27
|
+
text: `${partOf.get(id)?.label ?? "Not taught"}: ${titleOf.get(id) ?? id}`
|
|
28
|
+
}))
|
|
29
|
+
],
|
|
30
|
+
edges: taught.flatMap(
|
|
31
|
+
(competency) => competency.prerequisites.map((from) => ({ from, to: competency.id }))
|
|
32
|
+
)
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return graphs;
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
UNTAUGHT_GRAPH_ID,
|
|
39
|
+
UNTAUGHT_TITLE,
|
|
40
|
+
competencyGraphsByPart
|
|
41
|
+
};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/** True when `token` is a base-library atom and needs no semantic key. */
|
|
2
2
|
export function isBaseLibraryAtom(token: any): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* True when `glyph`, as a rendered page shows it, is a base-library atom. The
|
|
5
|
+
* rendered-page check (`figure-symbols.ts`) reads glyphs, not LaTeX, and
|
|
6
|
+
* exempts the same library the math gates do.
|
|
7
|
+
*/
|
|
8
|
+
export function isBaseLibraryGlyph(glyph: any): boolean;
|
|
3
9
|
/**
|
|
4
10
|
* The base notation library: a small, fixed set of universal identifier atoms
|
|
5
11
|
* that the page-level glyph map never asks an author to declare.
|
|
@@ -15,3 +21,5 @@ export function isBaseLibraryAtom(token: any): boolean;
|
|
|
15
21
|
* atoms, so the tokenizer never treats them as bindable symbols.
|
|
16
22
|
*/
|
|
17
23
|
export const BASE_LIBRARY: Readonly<Set<string>>;
|
|
24
|
+
/** What KaTeX draws for each base-library atom: `d`, `e`, `i`, `π`. */
|
|
25
|
+
export const BASE_LIBRARY_GLYPHS: Readonly<Set<any>>;
|
|
@@ -25,3 +25,22 @@ export const BASE_LIBRARY = Object.freeze(
|
|
|
25
25
|
export function isBaseLibraryAtom(token) {
|
|
26
26
|
return BASE_LIBRARY.has(token);
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
// An atom's LaTeX is its own glyph unless it is a command: `\pi` draws `π`.
|
|
30
|
+
// `tests/notation/figure-symbols.test.ts` renders every atom and holds this
|
|
31
|
+
// table to what KaTeX draws, so a new command atom cannot be forgotten here.
|
|
32
|
+
const DRAWN_AS = Object.freeze({ '\\pi': 'π' });
|
|
33
|
+
|
|
34
|
+
/** What KaTeX draws for each base-library atom: `d`, `e`, `i`, `π`. */
|
|
35
|
+
export const BASE_LIBRARY_GLYPHS = Object.freeze(
|
|
36
|
+
new Set([...BASE_LIBRARY].map((atom) => DRAWN_AS[atom] ?? atom)),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* True when `glyph`, as a rendered page shows it, is a base-library atom. The
|
|
41
|
+
* rendered-page check (`figure-symbols.ts`) reads glyphs, not LaTeX, and
|
|
42
|
+
* exempts the same library the math gates do.
|
|
43
|
+
*/
|
|
44
|
+
export function isBaseLibraryGlyph(glyph) {
|
|
45
|
+
return BASE_LIBRARY_GLYPHS.has(glyph);
|
|
46
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as CourseOperators, j as CourseRomanLabels, k as CourseAccents } from '../course-config-BLtIdkei.js';
|
|
2
2
|
import { NotationSpec } from '../compiler/collections.js';
|
|
3
3
|
import { LintIgnore } from './lint-ignore.js';
|
|
4
4
|
import { NotationRegistry, NotationRegistryInput } from './types.js';
|
|
@@ -18,7 +18,10 @@ import './gate-retired-keys.js';
|
|
|
18
18
|
* snapshot. Nothing here changes content. A finding is silenced only by a
|
|
19
19
|
* reviewed line in `lint-ignore.yml` (see `./lint-ignore`).
|
|
20
20
|
*
|
|
21
|
-
* (a) glyph-unique-in-corpus one rendered glyph → one meaning
|
|
21
|
+
* (a) glyph-unique-in-corpus one rendered glyph → one shared meaning; a
|
|
22
|
+
* local card or a gloss may reuse a letter
|
|
23
|
+
* (a') definition-repeated one local key declared in two lessons
|
|
24
|
+
* (a'') meaning-repeated one symbol and name defined at two sites, any tier
|
|
22
25
|
* (b) notation-units / units-vocab every entry has units or `dimensionless`,
|
|
23
26
|
* unit strings drawn from a controlled vocab
|
|
24
27
|
* (f) weak-local a `notation.local` symbol used only in its
|
|
@@ -44,7 +47,7 @@ import './gate-retired-keys.js';
|
|
|
44
47
|
* `src/content/resolution-report.ts`.
|
|
45
48
|
*/
|
|
46
49
|
|
|
47
|
-
type ConsistencyCode = 'glyph-unique-in-corpus' | 'notation-units' | 'units-vocab' | 'weak-local' | 'notation-source-locator' | '
|
|
50
|
+
type ConsistencyCode = 'glyph-unique-in-corpus' | 'notation-units' | 'units-vocab' | 'weak-local' | 'notation-source-locator' | 'definition-repeated' | 'meaning-repeated' | 'card-wants-demoting' | 'gloss-name-shape' | 'operator-explained' | 'roman-label-explained' | 'accent-explained' | 'accent-swallowed' | 'operator-suspended' | 'operator-suspension-reason' | 'operator-shadowed';
|
|
48
51
|
interface ConsistencyDiagnostic {
|
|
49
52
|
readonly code: ConsistencyCode;
|
|
50
53
|
/** Tier 3 — repairable violations are warnings; optional advice is info. */
|
|
@@ -69,6 +72,7 @@ interface ConsistencyInput {
|
|
|
69
72
|
/** This course's edits to the (empty) accent baseline (rule N22). */
|
|
70
73
|
readonly accents?: CourseAccents;
|
|
71
74
|
}
|
|
75
|
+
declare function glyphMatcher(latex: string): RegExp | undefined;
|
|
72
76
|
/**
|
|
73
77
|
* Run every consistency check. The result is sorted deterministically (code,
|
|
74
78
|
* then lesson / key / glyph, then message) so it is byte-stable in the manifest
|
|
@@ -78,4 +82,4 @@ declare function runConsistencyChecks(input: ConsistencyInput): ConsistencyDiagn
|
|
|
78
82
|
/** `{ code: count }` for the CLI summary and the report header. */
|
|
79
83
|
declare function consistencyCounts(diagnostics: readonly ConsistencyDiagnostic[]): Record<ConsistencyCode, number>;
|
|
80
84
|
|
|
81
|
-
export { type ConsistencyCode, type ConsistencyDiagnostic, type ConsistencyInput, consistencyCounts, runConsistencyChecks };
|
|
85
|
+
export { type ConsistencyCode, type ConsistencyDiagnostic, type ConsistencyInput, consistencyCounts, glyphMatcher, runConsistencyChecks };
|
|
@@ -54,14 +54,14 @@ function checkGlyphUniqueness(input, out) {
|
|
|
54
54
|
glyphs.set(trimmed, keys);
|
|
55
55
|
};
|
|
56
56
|
for (const definition of input.registry.definitions) {
|
|
57
|
-
if (definition.kind === "
|
|
58
|
-
record(definition.notation, definition.key,
|
|
57
|
+
if (definition.kind === "shared") {
|
|
58
|
+
record(definition.notation, definition.key, "");
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
for (const bundle of input.registry.bundles) {
|
|
62
62
|
for (const binding of bundle.bindings) {
|
|
63
63
|
const definition = byId.get(binding.definitionId);
|
|
64
|
-
if (definition) record(definition.notation, binding.key, bundle.lessonId);
|
|
64
|
+
if (definition?.kind === "shared") record(definition.notation, binding.key, bundle.lessonId);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
for (const [glyph, keys] of [...glyphs].sort(
|
|
@@ -69,12 +69,15 @@ function checkGlyphUniqueness(input, out) {
|
|
|
69
69
|
)) {
|
|
70
70
|
if (keys.size < 2) continue;
|
|
71
71
|
if (input.lintIgnore.matches("glyph-unique-in-corpus", glyph)) continue;
|
|
72
|
-
const detail = [...keys].sort(([a], [b]) => a.localeCompare(b)).map(([key, lessons]) =>
|
|
72
|
+
const detail = [...keys].sort(([a], [b]) => a.localeCompare(b)).map(([key, lessons]) => {
|
|
73
|
+
const bound = [...lessons].filter((lesson) => lesson !== "").sort();
|
|
74
|
+
return `${key} (${bound.length > 0 ? bound.join(", ") : "no lesson binds it"})`;
|
|
75
|
+
}).join("; ");
|
|
73
76
|
out.push({
|
|
74
77
|
code: "glyph-unique-in-corpus",
|
|
75
78
|
severity: "warning",
|
|
76
79
|
glyph,
|
|
77
|
-
message: `glyph ${JSON.stringify(glyph)}
|
|
80
|
+
message: `glyph ${JSON.stringify(glyph)} is ${keys.size} shared cards: ${detail}; course vocabulary gives a glyph one meaning \u2014 keep one card, or make the page-bound one a notation.local entry`
|
|
78
81
|
});
|
|
79
82
|
}
|
|
80
83
|
}
|
|
@@ -144,34 +147,78 @@ function checkNotationSourceLocator(input, out) {
|
|
|
144
147
|
}
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
function checkDefinitionRepeated(input, out) {
|
|
151
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
152
|
+
const chapters = input.notationInput.lessons.filter((lesson) => lesson.scopeLessonIds === void 0);
|
|
153
|
+
for (const lesson of [...chapters].sort((a, b) => a.lessonId.localeCompare(b.lessonId))) {
|
|
154
|
+
for (const definition of lesson.localDefinitions) {
|
|
155
|
+
const list = byKey.get(definition.key) ?? [];
|
|
156
|
+
if (!list.some((entry) => entry.lessonId === lesson.lessonId)) {
|
|
157
|
+
list.push({ lessonId: lesson.lessonId, file: lesson.source.file, definition });
|
|
158
|
+
}
|
|
159
|
+
byKey.set(definition.key, list);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
for (const [key, copies] of [...byKey].sort(([a], [b]) => a.localeCompare(b))) {
|
|
163
|
+
if (copies.length < 2) continue;
|
|
164
|
+
if (input.lintIgnore.matches("definition-repeated", key)) continue;
|
|
165
|
+
const first = copies[0];
|
|
166
|
+
const differing = ["notation", "label", "summary", "units", "sources"].filter(
|
|
167
|
+
(field) => copies.some((copy) => JSON.stringify(copy.definition[field] ?? null) !== JSON.stringify(first.definition[field] ?? null))
|
|
168
|
+
);
|
|
169
|
+
const lessons = copies.map((copy) => copy.lessonId).sort();
|
|
170
|
+
out.push({
|
|
171
|
+
code: "definition-repeated",
|
|
172
|
+
severity: "warning",
|
|
173
|
+
key,
|
|
174
|
+
lessonId: first.lessonId,
|
|
175
|
+
file: first.file,
|
|
176
|
+
count: copies.length,
|
|
177
|
+
message: `local entry ${key} (${first.definition.notation}) is declared in ${copies.length} lessons (${lessons.join(", ")}); ` + (differing.length === 0 ? "the copies agree" : `the copies differ in ${differing.join(", ")}`) + ` \u2014 move one copy to content/notation/${key}.md and delete the others: every [[${key}]] and \\explain{${key}} keeps working; if they are two meanings, give one another key`
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function checkMeaningRepeated(input, out) {
|
|
182
|
+
const normalize = (label) => label.toLowerCase().replace(/\s+/g, " ").trim();
|
|
183
|
+
const groups = /* @__PURE__ */ new Map();
|
|
184
|
+
const record = (notation, label, site) => {
|
|
185
|
+
const id = `${notation.trim()}\0${normalize(label)}`;
|
|
186
|
+
const group = groups.get(id) ?? { notation: notation.trim(), label, sites: [] };
|
|
187
|
+
if (!group.sites.some((known) => known.tier === site.tier && known.key === site.key && known.where === site.where)) {
|
|
188
|
+
group.sites.push(site);
|
|
189
|
+
}
|
|
190
|
+
groups.set(id, group);
|
|
155
191
|
};
|
|
156
192
|
for (const definition of input.notationInput.sharedDefinitions) {
|
|
157
|
-
|
|
193
|
+
record(definition.notation, definition.label, { tier: "shared", key: definition.key, where: `card ${definition.key}` });
|
|
194
|
+
for (const gloss of definition.glosses) {
|
|
195
|
+
record(gloss.notation, gloss.label, { tier: "gloss", key: gloss.key, where: `gloss on ${definition.key}` });
|
|
196
|
+
}
|
|
158
197
|
}
|
|
159
198
|
for (const lesson of input.notationInput.lessons) {
|
|
199
|
+
if (lesson.scopeLessonIds !== void 0) continue;
|
|
160
200
|
for (const definition of lesson.localDefinitions) {
|
|
161
|
-
|
|
201
|
+
record(definition.notation, definition.label, {
|
|
202
|
+
tier: "local",
|
|
203
|
+
key: definition.key,
|
|
204
|
+
where: `local ${definition.key} on ${lesson.lessonId}`
|
|
205
|
+
});
|
|
206
|
+
for (const gloss of definition.glosses) {
|
|
207
|
+
record(gloss.notation, gloss.label, { tier: "gloss", key: gloss.key, where: `gloss on ${definition.key} (${lesson.lessonId})` });
|
|
208
|
+
}
|
|
162
209
|
}
|
|
163
210
|
}
|
|
164
|
-
for (const [
|
|
165
|
-
(
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
const
|
|
169
|
-
if (input.lintIgnore.matches("gloss-wants-promoting", glyph)) continue;
|
|
211
|
+
for (const [, group] of [...groups].sort(([a], [b]) => a.localeCompare(b))) {
|
|
212
|
+
if (group.sites.length < 2) continue;
|
|
213
|
+
if (group.sites.every((site) => site.tier === "local" && site.key === group.sites[0].key)) continue;
|
|
214
|
+
if (input.lintIgnore.matches("meaning-repeated", group.notation)) continue;
|
|
215
|
+
const shared = group.sites.find((site) => site.tier === "shared");
|
|
170
216
|
out.push({
|
|
171
|
-
code: "
|
|
217
|
+
code: "meaning-repeated",
|
|
172
218
|
severity: "info",
|
|
173
|
-
glyph,
|
|
174
|
-
|
|
219
|
+
glyph: group.notation,
|
|
220
|
+
count: group.sites.length,
|
|
221
|
+
message: `${group.notation} as ${JSON.stringify(group.label)} is defined ${group.sites.length} times: ${group.sites.map((site) => site.where).join("; ")}` + (shared ? ` \u2014 ${shared.key} is the shared card; bind it with [[${shared.key}]] (or \\explain{${shared.key}}{${group.notation}} in a formula) instead of declaring it again` : " \u2014 one shared card would serve every site; promote it and reference it")
|
|
175
222
|
});
|
|
176
223
|
}
|
|
177
224
|
}
|
|
@@ -435,7 +482,8 @@ const CHECKS = [
|
|
|
435
482
|
checkNotationUnits,
|
|
436
483
|
checkWeakLocal,
|
|
437
484
|
checkNotationSourceLocator,
|
|
438
|
-
|
|
485
|
+
checkDefinitionRepeated,
|
|
486
|
+
checkMeaningRepeated,
|
|
439
487
|
checkCardWantsDemoting,
|
|
440
488
|
checkGlossNameShape,
|
|
441
489
|
checkOperatorExplained,
|
|
@@ -457,7 +505,8 @@ function consistencyCounts(diagnostics) {
|
|
|
457
505
|
"units-vocab": 0,
|
|
458
506
|
"weak-local": 0,
|
|
459
507
|
"notation-source-locator": 0,
|
|
460
|
-
"
|
|
508
|
+
"definition-repeated": 0,
|
|
509
|
+
"meaning-repeated": 0,
|
|
461
510
|
"card-wants-demoting": 0,
|
|
462
511
|
"gloss-name-shape": 0,
|
|
463
512
|
"operator-explained": 0,
|
|
@@ -473,5 +522,6 @@ function consistencyCounts(diagnostics) {
|
|
|
473
522
|
}
|
|
474
523
|
export {
|
|
475
524
|
consistencyCounts,
|
|
525
|
+
glyphMatcher,
|
|
476
526
|
runConsistencyChecks
|
|
477
527
|
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deck a docs file is, or `undefined`. A lesson's: `{ scope: 'lesson',
|
|
3
|
+
* lessonSlug, lessonId, path, chapterPath }`, the chapter being the lesson's
|
|
4
|
+
* `.mdx` (or `.md`) beside it. A part's (`<part>/index.deck.mdx`): `{ scope:
|
|
5
|
+
* 'part', partDir, pageId, path }`, its page id `<part>.slides`.
|
|
6
|
+
*/
|
|
7
|
+
export function deckOfFile(file: any): {
|
|
8
|
+
scope: string;
|
|
9
|
+
path: string;
|
|
10
|
+
partDir: string;
|
|
11
|
+
pageId: string;
|
|
12
|
+
} | {
|
|
13
|
+
chapterPath?: string | undefined;
|
|
14
|
+
scope: string;
|
|
15
|
+
path: string;
|
|
16
|
+
lessonSlug: string;
|
|
17
|
+
lessonId: string;
|
|
18
|
+
partDir?: undefined;
|
|
19
|
+
pageId?: undefined;
|
|
20
|
+
} | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* The chapter file of a lesson of a part's deck's part (`motion/range` →
|
|
23
|
+
* `…/motion/range.mdx`, or `.md`), beside the deck, if there is one.
|
|
24
|
+
*/
|
|
25
|
+
export function partChapterPath(deck: any, lessonSlug: any): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The chapter files whose scope a deck's symbols bind in, in order: a
|
|
28
|
+
* lesson's deck its chapter (none for a slides-first lesson); a part's deck
|
|
29
|
+
* every chapter of its part, in reading order (`manifest.parts[].lessons`).
|
|
30
|
+
*/
|
|
31
|
+
export function deckScopeChapters(deck: any, manifest: any): any;
|
|
32
|
+
/** A chapter file's frontmatter and body, read once per change of the file. */
|
|
33
|
+
export function readChapter(chapterPath: any): any;
|
|
34
|
+
/**
|
|
35
|
+
* The manifest's deck for a deck file (`deckOfFile`) — a lesson's or a
|
|
36
|
+
* part's — or, given a lesson id, for that lesson, if there is one.
|
|
37
|
+
*/
|
|
38
|
+
export function manifestDeckOf(manifest: any, owner: any): any;
|
|
39
|
+
/**
|
|
40
|
+
* Replace each `<Recall ref="eq-…" />` (or `fig-`, `tab-`, `dia-`) with the
|
|
41
|
+
* chapter's own float, parsed from the chapter with this page's parser.
|
|
42
|
+
* A recalled equation's math node and a recalled float's element carry
|
|
43
|
+
* `data.explicoRecall = { prefix, key, number, href }`; the imports a
|
|
44
|
+
* recalled float's components need are added to the deck's own.
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* What MDX's compile does to a tree before any course plugin sees it: a
|
|
48
|
+
* paragraph holding only JSX (and white space) becomes that JSX, as flow.
|
|
49
|
+
* An inline SVG's `<text x="…">0</text>` line parses as such a paragraph;
|
|
50
|
+
* kept, it would put a `<p>` inside the `<svg>`. `processor.parse` stops
|
|
51
|
+
* short of that step, so a chapter read for a recall takes it here
|
|
52
|
+
* (`remark-mark-and-unravel` in `@mdx-js/mdx`).
|
|
53
|
+
*/
|
|
54
|
+
export function unravelJsxParagraphs(tree: any): any;
|
|
55
|
+
export function expandRecalls(tree: any, { processor, deck, deckEntry, manifest, base, fail }: {
|
|
56
|
+
processor: any;
|
|
57
|
+
deck: any;
|
|
58
|
+
deckEntry: any;
|
|
59
|
+
manifest: any;
|
|
60
|
+
base: any;
|
|
61
|
+
fail: any;
|
|
62
|
+
}): void;
|
|
63
|
+
/**
|
|
64
|
+
* The deck page's equations and floats, in its reading order, and what each
|
|
65
|
+
* shows: a recalled one the chapter's number, a labelled equation of the
|
|
66
|
+
* deck its own (1), (2) in label order (`manifest.decks[].equations`), and
|
|
67
|
+
* the deck's own floats no number.
|
|
68
|
+
*/
|
|
69
|
+
export function collectDeckFloats(tree: any, deckEntry: any): {
|
|
70
|
+
equations: Map<any, any>;
|
|
71
|
+
floats: Map<any, any>;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Give the deck's `##` and `###` headings the anchors and frame numbers the
|
|
75
|
+
* manifest decided, in order; a different count fails the build (a stale
|
|
76
|
+
* manifest), as a chapter's headings do.
|
|
77
|
+
*/
|
|
78
|
+
export function stampDeckHeadings(tree: any, deckEntry: any, fail: any): void;
|
|
79
|
+
/**
|
|
80
|
+
* On a part's deck, give each `<AssessmentSet>` the lesson whose checks it
|
|
81
|
+
* asks (`lesson`, the component's prop): the chapter its frame's section
|
|
82
|
+
* covers (`frames[].chapter`). A frame in no such section asks none, which
|
|
83
|
+
* the manifest reports as `deck-check-no-chapter`.
|
|
84
|
+
*/
|
|
85
|
+
export function markDeckChecks(tree: any, deckEntry: any, fail: any): void;
|
|
86
|
+
/**
|
|
87
|
+
* The generated title frame: the deck's title, its course, its authors and
|
|
88
|
+
* the version its content is at (rules D10 and C5), in the words of a
|
|
89
|
+
* chapter's PDF (`versionPhrase` in compiler/page-versions.ts).
|
|
90
|
+
*/
|
|
91
|
+
export function titleFrameNode(manifest: any, deckEntry: any): {
|
|
92
|
+
type: string;
|
|
93
|
+
data: {
|
|
94
|
+
hName: any;
|
|
95
|
+
hProperties: any;
|
|
96
|
+
};
|
|
97
|
+
children: any[];
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* The competencies a deck's outcomes frame lists: its lesson's, or on a
|
|
101
|
+
* part's deck every covered chapter's, in reading order, each once.
|
|
102
|
+
*/
|
|
103
|
+
export function deckTeaches(manifest: any, deckEntry: any): any;
|
|
104
|
+
/** The generated outcomes frame: what the lesson's competencies say a student can now do. */
|
|
105
|
+
export function outcomesFrameNode(manifest: any, deckEntry: any, lesson: any): {
|
|
106
|
+
type: string;
|
|
107
|
+
data: {
|
|
108
|
+
hName: any;
|
|
109
|
+
hProperties: any;
|
|
110
|
+
};
|
|
111
|
+
children: any[];
|
|
112
|
+
} | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* After notation ran: give each `###` frame its legend line — the symbols it
|
|
115
|
+
* shows for the first time in the deck (`first-use`), or all it shows
|
|
116
|
+
* (`every-frame`) — as math the page's KaTeX pass renders, each symbol bound
|
|
117
|
+
* to its card. `resolved` is the page's resolved notation (`key → entry`).
|
|
118
|
+
*/
|
|
119
|
+
export function insertLegends(tree: any, { resolved, mode }: {
|
|
120
|
+
resolved: any;
|
|
121
|
+
mode: any;
|
|
122
|
+
}): void;
|
|
123
|
+
export const DECK_CAPTION_PATTERN: RegExp;
|