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
|
@@ -37,6 +37,8 @@ import {
|
|
|
37
37
|
GlyphResolutionError,
|
|
38
38
|
baseSymbolLatex,
|
|
39
39
|
derivedNotationLatex,
|
|
40
|
+
fixedLenderMessage,
|
|
41
|
+
mathExcerpt,
|
|
40
42
|
resolveMathGlyphs,
|
|
41
43
|
stripNotationMarkers,
|
|
42
44
|
} from './math-glyphs.mjs';
|
|
@@ -62,7 +64,23 @@ import {
|
|
|
62
64
|
parseKnowledgeCheckRef,
|
|
63
65
|
} from './knowledge-checks.mjs';
|
|
64
66
|
import { injectMdxStringAttributes } from './mdx-attributes.mjs';
|
|
67
|
+
import {
|
|
68
|
+
collectDeckFloats,
|
|
69
|
+
deckOfFile,
|
|
70
|
+
deckScopeChapters,
|
|
71
|
+
deckTeaches,
|
|
72
|
+
expandRecalls,
|
|
73
|
+
insertLegends,
|
|
74
|
+
manifestDeckOf,
|
|
75
|
+
markDeckChecks,
|
|
76
|
+
outcomesFrameNode,
|
|
77
|
+
readChapter,
|
|
78
|
+
stampDeckHeadings,
|
|
79
|
+
titleFrameNode,
|
|
80
|
+
} from './deck-remark.mjs';
|
|
65
81
|
import { collectSectionHeadings, parseSectionRef } from './sections.mjs';
|
|
82
|
+
import { REGISTRY_BODY_FIX, registryGlyphScope } from './registry-scope.mjs';
|
|
83
|
+
import { sourceLineOffset, shiftedPlace } from './source-lines.mjs';
|
|
66
84
|
|
|
67
85
|
const PROSE_SKIP_TYPES = new Set([
|
|
68
86
|
'code',
|
|
@@ -340,7 +358,7 @@ function readBracedGroup(value, start) {
|
|
|
340
358
|
}
|
|
341
359
|
|
|
342
360
|
function fail(file, message, node) {
|
|
343
|
-
if (typeof file?.fail === 'function') file.fail(message, node);
|
|
361
|
+
if (typeof file?.fail === 'function') file.fail(message, shiftedPlace(file, node));
|
|
344
362
|
|
|
345
363
|
const location = file?.path ? `${file.path}: ` : '';
|
|
346
364
|
throw new Error(`${location}${message}`);
|
|
@@ -348,7 +366,7 @@ function fail(file, message, node) {
|
|
|
348
366
|
|
|
349
367
|
function warn(file, message, node) {
|
|
350
368
|
if (typeof file?.message === 'function') {
|
|
351
|
-
file.message(message, node);
|
|
369
|
+
file.message(message, shiftedPlace(file, node));
|
|
352
370
|
return;
|
|
353
371
|
}
|
|
354
372
|
console.warn(message);
|
|
@@ -365,7 +383,7 @@ function tokenLocation(file, node, value, offset) {
|
|
|
365
383
|
if (!start || typeof start.line !== 'number') return path;
|
|
366
384
|
const before = String(value).slice(0, offset);
|
|
367
385
|
const newlines = before.split('\n').length - 1;
|
|
368
|
-
const line = start.line + newlines;
|
|
386
|
+
const line = start.line + sourceLineOffset(file) + newlines;
|
|
369
387
|
const column =
|
|
370
388
|
newlines === 0
|
|
371
389
|
? (start.column ?? 1) + offset
|
|
@@ -442,13 +460,16 @@ function inlineEquationNode(mathNode) {
|
|
|
442
460
|
* author-written one; either way the reader copies plain LaTeX, never this
|
|
443
461
|
* system's own notation-key plumbing (`math-glyphs.mjs:stripNotationMarkers`).
|
|
444
462
|
*/
|
|
445
|
-
function equationNode(mathNode, { key, number, short }) {
|
|
463
|
+
function equationNode(mathNode, { key, number, short, href }) {
|
|
446
464
|
const id = equationAnchorId({ key, number });
|
|
447
465
|
const hProperties = {
|
|
448
466
|
id,
|
|
449
467
|
className: ['keyed-equation'],
|
|
450
468
|
'data-eq-number': number,
|
|
451
469
|
};
|
|
470
|
+
// A deck's recall of its chapter's equation: the chapter's number, which
|
|
471
|
+
// links to the chapter (`deck-remark.mjs`).
|
|
472
|
+
if (href) hProperties['data-eq-recall'] = href;
|
|
452
473
|
if (key) hProperties['data-eq-key'] = key;
|
|
453
474
|
else hProperties['data-eq-auto'] = '';
|
|
454
475
|
// The `\caption{}` short caption names the equation wherever it is shown
|
|
@@ -465,7 +486,7 @@ function equationNode(mathNode, { key, number, short }) {
|
|
|
465
486
|
el('div', { className: ['keyed-equation__controls'] }, [
|
|
466
487
|
{
|
|
467
488
|
type: 'link',
|
|
468
|
-
url: `#${id}`,
|
|
489
|
+
url: href ?? `#${id}`,
|
|
469
490
|
children: [{ type: 'text', value: `(${number})` }],
|
|
470
491
|
data: {
|
|
471
492
|
hProperties: {
|
|
@@ -515,6 +536,20 @@ function injectFloatNumberAttributes(node, float, prefix) {
|
|
|
515
536
|
});
|
|
516
537
|
}
|
|
517
538
|
|
|
539
|
+
/**
|
|
540
|
+
* A float on a deck page: a recalled one shows the chapter's number, which
|
|
541
|
+
* links to the chapter; the deck's own is unnumbered (rule D8) and anchors at
|
|
542
|
+
* its key, or at its place among the deck's floats of its kind.
|
|
543
|
+
*/
|
|
544
|
+
function injectDeckFloatAttributes(node, float) {
|
|
545
|
+
// The component sees it is on a deck (`components/deck/deck-context.ts`):
|
|
546
|
+
// it takes no number of its own, and a recalled one links to its chapter.
|
|
547
|
+
injectMdxStringAttributes(node, {
|
|
548
|
+
...(float.number === undefined ? {} : { number: float.number }),
|
|
549
|
+
anchorId: float.key ? `${float.prefix}-${float.key}` : `${float.prefix}-deck-${float.position}`,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
518
553
|
/**
|
|
519
554
|
* The URL path Starlight serves a docs page at: `motion/index.mdx` is
|
|
520
555
|
* `/motion/`, `motion/speed.mdx` is `/motion/speed/`, `index.mdx` is `/`.
|
|
@@ -646,6 +681,29 @@ function unnumberedFloatMessage(tagName, component) {
|
|
|
646
681
|
);
|
|
647
682
|
}
|
|
648
683
|
|
|
684
|
+
/**
|
|
685
|
+
* The chapter a deck's unqualified `[[eq-…]]`, `[[fig-…]]` or `[[sec-…]]`
|
|
686
|
+
* names when the deck shows no such float or frame itself: its chapter's,
|
|
687
|
+
* or on a part's deck the one covered chapter whose table (`crossPage`, by
|
|
688
|
+
* lesson slug) has `key`. Two that do leave it open: the author writes
|
|
689
|
+
* `[[<chapter>#…]]`.
|
|
690
|
+
*/
|
|
691
|
+
function fallbackLessonFor(ctx, key, written, file, node) {
|
|
692
|
+
const lessons = ctx.fallbackLessons;
|
|
693
|
+
if (lessons.length === 1) return lessons[0];
|
|
694
|
+
const having = lessons.filter((lesson) => ctx.crossPage?.[lesson]?.[key] !== undefined);
|
|
695
|
+
if (having.length !== 1) {
|
|
696
|
+
fail(
|
|
697
|
+
file,
|
|
698
|
+
having.length === 0
|
|
699
|
+
? `Unknown reference ${written}: neither this deck nor a chapter it covers (${lessons.join(', ')}) has it.`
|
|
700
|
+
: `Reference ${written} on a part's deck: ${having.join(' and ')} each have one. Name the chapter: [[<chapter>#…]].`,
|
|
701
|
+
node,
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
return having[0];
|
|
705
|
+
}
|
|
706
|
+
|
|
649
707
|
/** Resolve a `[[eq-key]]` / `[[slug#eq-key]]` reference to `{ number, href }`. */
|
|
650
708
|
function resolveEquationRef(eqRef, eqCtx, file, node) {
|
|
651
709
|
if (eqRef.lesson) {
|
|
@@ -663,6 +721,11 @@ function resolveEquationRef(eqRef, eqCtx, file, node) {
|
|
|
663
721
|
};
|
|
664
722
|
}
|
|
665
723
|
const number = eqCtx.pageLabels.get(eqRef.key);
|
|
724
|
+
// On a deck, an equation the deck neither labels nor recalls is its chapter's.
|
|
725
|
+
if (number === undefined && eqCtx.fallbackLessons) {
|
|
726
|
+
const lesson = fallbackLessonFor(eqCtx, eqRef.key, `[[eq-${eqRef.key}]]`, file, node);
|
|
727
|
+
return resolveEquationRef({ lesson, key: eqRef.key }, eqCtx, file, node);
|
|
728
|
+
}
|
|
666
729
|
if (number === undefined) {
|
|
667
730
|
fail(
|
|
668
731
|
file,
|
|
@@ -694,6 +757,16 @@ function resolveKeyedFloatRef(floatRef, ctx, { prefix, tagName }, file, node) {
|
|
|
694
757
|
};
|
|
695
758
|
}
|
|
696
759
|
const number = ctx.pageLabels.get(floatRef.key);
|
|
760
|
+
// On a deck, a float the deck does not recall is its chapter's.
|
|
761
|
+
if (number === undefined && ctx.fallbackLessons) {
|
|
762
|
+
return resolveKeyedFloatRef(
|
|
763
|
+
{ lesson: fallbackLessonFor(ctx, floatRef.key, `[[${prefix}-${floatRef.key}]]`, file, node), key: floatRef.key },
|
|
764
|
+
ctx,
|
|
765
|
+
{ prefix, tagName },
|
|
766
|
+
file,
|
|
767
|
+
node,
|
|
768
|
+
);
|
|
769
|
+
}
|
|
697
770
|
if (number === undefined) {
|
|
698
771
|
fail(
|
|
699
772
|
file,
|
|
@@ -711,6 +784,10 @@ function resolveKeyedFloatRef(floatRef, ctx, { prefix, tagName }, file, node) {
|
|
|
711
784
|
* reference never prints a number the reader cannot find on the page.
|
|
712
785
|
*/
|
|
713
786
|
function resolveSectionRef(secRef, ctx, file, node) {
|
|
787
|
+
if (!secRef.lesson && ctx.fallbackLessons && !ctx.pageHeadings.has(secRef.key)) {
|
|
788
|
+
const lesson = fallbackLessonFor(ctx, secRef.key, `[[sec-${secRef.key}]]`, file, node);
|
|
789
|
+
return resolveSectionRef({ ...secRef, lesson }, ctx, file, node);
|
|
790
|
+
}
|
|
714
791
|
const heading = secRef.lesson
|
|
715
792
|
? ctx.crossPage?.[secRef.lesson]?.[secRef.key]
|
|
716
793
|
: ctx.pageHeadings.get(secRef.key);
|
|
@@ -1471,6 +1548,9 @@ function assertStrictNumbering({ file, slug, noun, count, placement, crossPage }
|
|
|
1471
1548
|
* covers every catalogued lesson.
|
|
1472
1549
|
*/
|
|
1473
1550
|
export default function remarkNotation(options = {}) {
|
|
1551
|
+
// The processor this plugin runs in: a deck's `<Recall>` parses its chapter
|
|
1552
|
+
// with the page's own parser (`deck-remark.mjs`).
|
|
1553
|
+
const processor = this;
|
|
1474
1554
|
const unknown = options.unknown ?? 'error';
|
|
1475
1555
|
const strict = options.strict ?? false;
|
|
1476
1556
|
// The manifest supplies every numbering input at once; an explicit option
|
|
@@ -1495,13 +1575,44 @@ export default function remarkNotation(options = {}) {
|
|
|
1495
1575
|
? options.definitions()
|
|
1496
1576
|
: options.definitions;
|
|
1497
1577
|
const sharedDefinitions = definitionsMap(suppliedDefinitions);
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
)
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1578
|
+
// The same table `validate:content` checks an entry's body against.
|
|
1579
|
+
const registryMathDefinitions = registryGlyphScope(sharedDefinitions.values());
|
|
1580
|
+
// A deck (`<lesson>.deck.mdx`) is a page of its lesson: its symbols bind
|
|
1581
|
+
// in the chapter's scope, and its numbers are the chapter's for what it
|
|
1582
|
+
// recalls and its own for what it labels (`deck-remark.mjs`). A part's
|
|
1583
|
+
// deck (`<part>/index.deck.mdx`) binds in its chapters' scopes, the
|
|
1584
|
+
// first to define a key binding it, and recalls from the chapters its
|
|
1585
|
+
// sections cover.
|
|
1586
|
+
const deck = deckOfFile(file);
|
|
1587
|
+
const deckEntry = deck ? manifestDeckOf(options.manifest, deck) : undefined;
|
|
1588
|
+
const scopeChapters = deck ? deckScopeChapters(deck, options.manifest).map((path) => readChapter(path)) : [];
|
|
1589
|
+
const localDefinitions = deck?.scope === 'part' || scopeChapters.length > 0
|
|
1590
|
+
? new Map(
|
|
1591
|
+
scopeChapters
|
|
1592
|
+
.flatMap((chapter) => [...definitionsMap(chapter.data.notation?.local)])
|
|
1593
|
+
.filter(([key], index, all) => all.findIndex(([other]) => other === key) === index),
|
|
1594
|
+
)
|
|
1595
|
+
: localDefinitionsFromFile(file);
|
|
1596
|
+
const suspendedOperators = deck?.scope === 'part' || scopeChapters.length > 0
|
|
1597
|
+
? [...new Set(scopeChapters.flatMap((chapter) => suspendedOperatorsFromFile({ data: chapter.data })))]
|
|
1598
|
+
: suspendedOperatorsFromFile(file);
|
|
1599
|
+
const lessonId = deck ? (deck.scope === 'part' ? deck.pageId : deck.lessonId) : lessonIdFrom(file);
|
|
1504
1600
|
const lessonSlug = lessonId ? lessonId.replace(/\./g, '/') : undefined;
|
|
1601
|
+
const failHere = (message, node) => fail(file, message, node);
|
|
1602
|
+
if (deck) {
|
|
1603
|
+
expandRecalls(tree, {
|
|
1604
|
+
processor,
|
|
1605
|
+
deck,
|
|
1606
|
+
deckEntry,
|
|
1607
|
+
manifest: options.manifest,
|
|
1608
|
+
base: options.base,
|
|
1609
|
+
fail: failHere,
|
|
1610
|
+
});
|
|
1611
|
+
if (deckEntry) stampDeckHeadings(tree, deckEntry, failHere);
|
|
1612
|
+
// A question frame on a part's deck asks its section's chapter's checks.
|
|
1613
|
+
if (deckEntry?.part !== undefined) markDeckChecks(tree, deckEntry, failHere);
|
|
1614
|
+
}
|
|
1615
|
+
const deckFloats = deck ? collectDeckFloats(tree, deckEntry) : undefined;
|
|
1505
1616
|
// The landing page, a part's overview page (rule S10): its math binds
|
|
1506
1617
|
// like a chapter's, but it numbers nothing.
|
|
1507
1618
|
const nonChapterPage = !lessonId && isDocsPageFile(file);
|
|
@@ -1509,11 +1620,15 @@ export default function remarkNotation(options = {}) {
|
|
|
1509
1620
|
// Missing ⇒ the legacy `##`-section numbering.
|
|
1510
1621
|
const equationNumbering = pageNumbering(numbering, lessonSlug, 'equations');
|
|
1511
1622
|
const registryFile = isNotationRegistryFile(file);
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1623
|
+
// A deck shows its lessons' symbols: those its chapters name count as
|
|
1624
|
+
// named here, as the manifest's gates count them.
|
|
1625
|
+
const pageSharedKeys = collectPageSharedKeys(tree);
|
|
1626
|
+
for (const chapter of scopeChapters) {
|
|
1627
|
+
for (const key of collectPageSharedKeys({ type: 'root', children: [{ type: 'text', value: chapter.body }] })) {
|
|
1628
|
+
pageSharedKeys.add(key);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
const mathScope = pageGlyphScope(localDefinitions, sharedDefinitions, pageSharedKeys);
|
|
1517
1632
|
const references = [];
|
|
1518
1633
|
const resolved = new Map();
|
|
1519
1634
|
|
|
@@ -1521,7 +1636,9 @@ export default function remarkNotation(options = {}) {
|
|
|
1521
1636
|
// math node is rewritten, so a `[[eq-key]]` earlier in the page still
|
|
1522
1637
|
// resolves. `equationNumbering` picks the `‹part›.‹n›` vs `##`-section
|
|
1523
1638
|
// scheme.
|
|
1524
|
-
const equationLabels =
|
|
1639
|
+
const equationLabels = deckFloats
|
|
1640
|
+
? [...deckFloats.equations.values()].filter((equation) => !equation.href)
|
|
1641
|
+
: collectEquationLabels(tree, equationNumbering);
|
|
1525
1642
|
const pageLabels = new Map();
|
|
1526
1643
|
for (const label of equationLabels) {
|
|
1527
1644
|
if (nonChapterPage) {
|
|
@@ -1540,33 +1657,65 @@ export default function remarkNotation(options = {}) {
|
|
|
1540
1657
|
}
|
|
1541
1658
|
pageLabels.set(label.key, label.number);
|
|
1542
1659
|
}
|
|
1660
|
+
// A deck's recalled equations answer to their chapter's key on the page;
|
|
1661
|
+
// a reference to anything else of the chapter's goes to the chapter.
|
|
1662
|
+
if (deckFloats) {
|
|
1663
|
+
for (const equation of deckFloats.equations.values()) {
|
|
1664
|
+
if (equation.href && !pageLabels.has(equation.key)) pageLabels.set(equation.key, equation.number);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
// What a deck's `[[eq-…]]`, `[[fig-…]]`, `[[sec-…]]` name when the deck
|
|
1668
|
+
// shows no such float itself: its chapter's, or the one chapter of those a
|
|
1669
|
+
// part's deck covers that has it.
|
|
1670
|
+
const fallbackLessons = deck?.scope === 'part'
|
|
1671
|
+
? (deckEntry?.covers ?? []).map((cover) => cover.chapter)
|
|
1672
|
+
: deck?.chapterPath
|
|
1673
|
+
? [lessonSlug]
|
|
1674
|
+
: [];
|
|
1675
|
+
const fallback = fallbackLessons.length > 0 ? { fallbackLessons } : {};
|
|
1543
1676
|
const eqCtx = {
|
|
1544
1677
|
base: options.base ?? '',
|
|
1545
1678
|
pageLabels,
|
|
1546
1679
|
crossPage: numbering.equations ?? {},
|
|
1680
|
+
...fallback,
|
|
1547
1681
|
};
|
|
1548
1682
|
// WERN guard: what this page prints next to the equation must be what a
|
|
1549
|
-
// reference from another page prints for it.
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1683
|
+
// reference from another page prints for it. A deck's numbers are its
|
|
1684
|
+
// own, checked against the manifest's deck instead.
|
|
1685
|
+
if (!deck) {
|
|
1686
|
+
assertManifestNumberParity({
|
|
1687
|
+
file,
|
|
1688
|
+
slug: lessonSlug,
|
|
1689
|
+
pageLabels,
|
|
1690
|
+
crossPage: eqCtx.crossPage,
|
|
1691
|
+
placement: equationNumbering,
|
|
1692
|
+
noun: 'equation',
|
|
1693
|
+
syntax: (key) => `eq:${key}`,
|
|
1694
|
+
});
|
|
1695
|
+
} else {
|
|
1696
|
+
for (const equation of deckFloats.equations.values()) {
|
|
1697
|
+
if (equation.number === '?') {
|
|
1698
|
+
fail(
|
|
1699
|
+
file,
|
|
1700
|
+
`Stale Explico manifest for the deck ${deckEntry?.slug ?? lessonSlug}: it lists no equation labelled eq:${equation.key}. Recompile the manifest.`,
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1559
1705
|
|
|
1560
1706
|
// Number and anchor every display equation on a lesson page, keyed or
|
|
1561
1707
|
// not (F3). Non-lesson pages (notation registry, stray docs) keep the
|
|
1562
|
-
// keyed-only behaviour so their body math is left untouched.
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1708
|
+
// keyed-only behaviour so their body math is left untouched. A deck
|
|
1709
|
+
// numbers only what it labels or recalls.
|
|
1710
|
+
const displayEquations = deckFloats
|
|
1711
|
+
? [...deckFloats.equations].map(([node, equation]) => ({ ...equation, node }))
|
|
1712
|
+
: lessonId
|
|
1713
|
+
? collectDisplayEquations(tree, equationNumbering)
|
|
1714
|
+
: [];
|
|
1566
1715
|
const equationByNode = new Map(
|
|
1567
1716
|
displayEquations.map((equation) => [equation.node, equation]),
|
|
1568
1717
|
);
|
|
1569
|
-
if (strict) {
|
|
1718
|
+
if (strict && !deck) {
|
|
1570
1719
|
assertStrictNumbering({
|
|
1571
1720
|
file,
|
|
1572
1721
|
slug: lessonSlug,
|
|
@@ -1581,6 +1730,25 @@ export default function remarkNotation(options = {}) {
|
|
|
1581
1730
|
// page is numbered and anchored, keyed or not — same rule as equations,
|
|
1582
1731
|
// via the shared engine in `keyed-jsx-floats.mjs`.
|
|
1583
1732
|
const makeFloatCtx = (tagName, crossPage, kind) => {
|
|
1733
|
+
// A deck's floats: recalled ones keep the chapter's number, the deck's
|
|
1734
|
+
// own are unnumbered (rule D8).
|
|
1735
|
+
if (deckFloats) {
|
|
1736
|
+
const prefix = { Table: 'tab', Figure: 'fig', Diagram: 'dia' }[tagName];
|
|
1737
|
+
const entries = [...deckFloats.floats].filter(([, float]) => float.prefix === prefix);
|
|
1738
|
+
return {
|
|
1739
|
+
ctx: {
|
|
1740
|
+
base: options.base ?? '',
|
|
1741
|
+
pageLabels: new Map(
|
|
1742
|
+
entries
|
|
1743
|
+
.filter(([, float]) => float.number !== undefined)
|
|
1744
|
+
.map(([, float]) => [float.key, float.number]),
|
|
1745
|
+
),
|
|
1746
|
+
crossPage: crossPage ?? {},
|
|
1747
|
+
...fallback,
|
|
1748
|
+
},
|
|
1749
|
+
byNode: new Map(entries),
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1584
1752
|
const floatNumbering = pageNumbering(numbering, lessonSlug, kind);
|
|
1585
1753
|
const floats = lessonId
|
|
1586
1754
|
? collectKeyedJsxFloats(tree, { tagName, numbering: floatNumbering })
|
|
@@ -1670,7 +1838,7 @@ export default function remarkNotation(options = {}) {
|
|
|
1670
1838
|
// Outline numbering: the lesson's top-level headings take the manifest's
|
|
1671
1839
|
// anchors and numbers before anything else reads them, and `[[sec-…]]`
|
|
1672
1840
|
// resolves against that same table.
|
|
1673
|
-
const publishedHeadings = lessonSlug
|
|
1841
|
+
const publishedHeadings = lessonSlug && !deck
|
|
1674
1842
|
? numbering.sections?.[lessonSlug]
|
|
1675
1843
|
: undefined;
|
|
1676
1844
|
if (lessonId && publishedHeadings) {
|
|
@@ -1686,8 +1854,12 @@ export default function remarkNotation(options = {}) {
|
|
|
1686
1854
|
const sectionCtx = {
|
|
1687
1855
|
base: options.base ?? '',
|
|
1688
1856
|
show: sectionSettings.show,
|
|
1689
|
-
|
|
1857
|
+
// Beside a chapter, a deck's `[[sec-…]]` names a section of the
|
|
1858
|
+
// chapter (`fallbackLessons`), on a part's deck one of a chapter it
|
|
1859
|
+
// covers; a slides-first deck's names its own frame.
|
|
1860
|
+
pageHeadings: new Map(fallbackLessons.length > 0 ? [] : Object.entries(headingsBySlug[lessonSlug] ?? {})),
|
|
1690
1861
|
crossPage: headingsBySlug,
|
|
1862
|
+
...fallback,
|
|
1691
1863
|
};
|
|
1692
1864
|
|
|
1693
1865
|
const resolve = (key, kind, node) => {
|
|
@@ -1806,23 +1978,29 @@ export default function remarkNotation(options = {}) {
|
|
|
1806
1978
|
if (result.unresolved.length > 0) {
|
|
1807
1979
|
const unresolved = result.unresolved
|
|
1808
1980
|
.map(
|
|
1809
|
-
({ token, start, suspended, font, lenders }) =>
|
|
1981
|
+
({ token, start, suspended, font, lenders, fixedLenders, instance }) =>
|
|
1810
1982
|
`${tokenLocation(file, node, value, start)}: "${token}"${
|
|
1811
1983
|
suspended
|
|
1812
1984
|
? ` (${suspended} is suspended on this page, so this form needs its own entry)`
|
|
1813
1985
|
: font
|
|
1814
1986
|
? ' (a different symbol from the plain letter; it needs its own entry)'
|
|
1815
|
-
:
|
|
1816
|
-
? ` (${
|
|
1817
|
-
|
|
1987
|
+
: fixedLenders?.length
|
|
1988
|
+
? ` (${fixedLenders
|
|
1989
|
+
.map((lender) => fixedLenderMessage(lender, instance ?? token))
|
|
1990
|
+
.join(' ')})`
|
|
1991
|
+
: lenders
|
|
1992
|
+
? ` (${lenders.join(', ')} explains only its own instances with a subscript, never a bare "${token}", rule N4: give it its own entry, or \\explain{${lenders[0]}}{${token}} if it is that entry's shorthand)`
|
|
1993
|
+
: ''
|
|
1818
1994
|
}`,
|
|
1819
1995
|
)
|
|
1820
1996
|
.join(', ');
|
|
1821
1997
|
fail(
|
|
1822
1998
|
file,
|
|
1823
1999
|
nonChapterPage
|
|
1824
|
-
? `Unresolved notation in ${context}: ${unresolved}. Introduce the symbol with [[key]], or wrap it in \\explain{key}{latex}: a page that is not a chapter recalls shared notation and defines none (rule S10).`
|
|
1825
|
-
:
|
|
2000
|
+
? `Unresolved notation in ${context}: ${unresolved}, in ${mathExcerpt(value)}. Introduce the symbol with [[key]], or wrap it in \\explain{key}{latex}: a page that is not a chapter recalls shared notation and defines none (rule S10).`
|
|
2001
|
+
: lessonId
|
|
2002
|
+
? `Unresolved notation in ${context}: ${unresolved}, in ${mathExcerpt(value)}. Introduce the symbol with [[key]], define it in notation.local, or wrap it in \\explain{key}{latex}.`
|
|
2003
|
+
: `Unresolved notation in ${context}: ${unresolved}, in ${mathExcerpt(value)}. ${REGISTRY_BODY_FIX}`,
|
|
1826
2004
|
node,
|
|
1827
2005
|
);
|
|
1828
2006
|
}
|
|
@@ -1888,6 +2066,11 @@ export default function remarkNotation(options = {}) {
|
|
|
1888
2066
|
// Scoped to lesson pages, same as `displayEquations` above — a
|
|
1889
2067
|
// registry/glossary page's inline math is left exactly as before.
|
|
1890
2068
|
node.children[index] = inlineEquationNode(child);
|
|
2069
|
+
} else if (deck && child?.type === 'mdxJsxFlowElement' && (tableByNode.has(child) || figureByNode.has(child) || diagramByNode.has(child))) {
|
|
2070
|
+
injectDeckFloatAttributes(
|
|
2071
|
+
child,
|
|
2072
|
+
tableByNode.get(child) ?? figureByNode.get(child) ?? diagramByNode.get(child),
|
|
2073
|
+
);
|
|
1891
2074
|
} else if (child?.type === 'mdxJsxFlowElement' && tableByNode.has(child)) {
|
|
1892
2075
|
injectFloatNumberAttributes(child, tableByNode.get(child), 'tab');
|
|
1893
2076
|
} else if (child?.type === 'mdxJsxFlowElement' && figureByNode.has(child)) {
|
|
@@ -1981,6 +2164,32 @@ export default function remarkNotation(options = {}) {
|
|
|
1981
2164
|
titleChapterLinks(tree, file, chapterTitleBySlug, options.base);
|
|
1982
2165
|
visit(tree);
|
|
1983
2166
|
|
|
2167
|
+
if (deck && deckEntry) {
|
|
2168
|
+
// The legend lines first: they read the frames between the headings.
|
|
2169
|
+
insertLegends(tree, {
|
|
2170
|
+
resolved: Object.fromEntries(resolved),
|
|
2171
|
+
mode: options.manifest?.deck?.legend ?? 'first-use',
|
|
2172
|
+
});
|
|
2173
|
+
const firstContent = tree.children.findIndex((node) => node?.type !== 'mdxjsEsm');
|
|
2174
|
+
tree.children.splice(firstContent === -1 ? tree.children.length : firstContent, 0, titleFrameNode(options.manifest, deckEntry));
|
|
2175
|
+
const outcomes = outcomesFrameNode(options.manifest, deckEntry, { teaches: deckTeaches(options.manifest, deckEntry) });
|
|
2176
|
+
if (outcomes) tree.children.push(outcomes);
|
|
2177
|
+
file.data.explicoDeck = {
|
|
2178
|
+
slug: deckEntry.slug,
|
|
2179
|
+
frames: deckEntry.frames.length,
|
|
2180
|
+
// Each frame's id, by its number less one: `rehype-deck.mjs` stamps
|
|
2181
|
+
// every frame with it, the references frame too, whose heading the
|
|
2182
|
+
// citations pass writes after this one.
|
|
2183
|
+
frameIds: deckEntry.frames.map((frame) => frame.id),
|
|
2184
|
+
// On a part's deck, the chapter each frame's section covers, by its
|
|
2185
|
+
// lesson slug (`data-chapter`), which the sidebar lights.
|
|
2186
|
+
frameChapters: deckEntry.frames.map((frame) => frame.chapter ?? null),
|
|
2187
|
+
aspectRatio: options.manifest?.deck?.aspectRatio ?? '16:9',
|
|
2188
|
+
canvas: options.manifest?.deck?.canvas ?? { width: 1280, height: 720 },
|
|
2189
|
+
notes: options.manifest?.deck?.notes ?? 'reader',
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2192
|
+
|
|
1984
2193
|
file.data.notationReferences = references;
|
|
1985
2194
|
file.data.notationResolved = Object.fromEntries(resolved);
|
|
1986
2195
|
file.data.equationLabels = equationLabels.map(
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lines to add to a position in `file.value` to get the line in `file.path`:
|
|
3
|
+
* 0 when the text is the whole file (or the file cannot be read), the height
|
|
4
|
+
* of the frontmatter when it is the body alone.
|
|
5
|
+
*/
|
|
6
|
+
export function sourceLineOffset(file: any): any;
|
|
7
|
+
/**
|
|
8
|
+
* `node` with its `position` moved to the file's own lines, for
|
|
9
|
+
* `file.fail` / `file.message`, which print it. The node itself is left alone:
|
|
10
|
+
* the tree keeps the positions the parser gave it.
|
|
11
|
+
*/
|
|
12
|
+
export function shiftedPlace(file: any, node: any): any;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a vfile's text sits in the file on disk.
|
|
3
|
+
*
|
|
4
|
+
* Astro's glob loader renders a content-collection entry (a notation entry)
|
|
5
|
+
* from its body alone: the frontmatter is gone, so every `position` the remark
|
|
6
|
+
* pass reads counts lines from the first line after it, and an error naming
|
|
7
|
+
* `entry.md:1:43` points at the wrong line. A page Astro compiles whole (a
|
|
8
|
+
* lesson, through MDX) carries its full text, and its positions are already the
|
|
9
|
+
* file's own. This finds which of the two a file is, so a location can be
|
|
10
|
+
* reported the way an editor opens it.
|
|
11
|
+
*
|
|
12
|
+
* It reads the file, and only when a location is being reported (a failure or
|
|
13
|
+
* a warning), never on the happy path.
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
|
|
17
|
+
const offsets = new WeakMap();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Lines to add to a position in `file.value` to get the line in `file.path`:
|
|
21
|
+
* 0 when the text is the whole file (or the file cannot be read), the height
|
|
22
|
+
* of the frontmatter when it is the body alone.
|
|
23
|
+
*/
|
|
24
|
+
export function sourceLineOffset(file) {
|
|
25
|
+
if (!file || typeof file !== 'object') return 0;
|
|
26
|
+
if (offsets.has(file)) return offsets.get(file);
|
|
27
|
+
|
|
28
|
+
let offset = 0;
|
|
29
|
+
try {
|
|
30
|
+
const text = typeof file.value === 'string' ? file.value : undefined;
|
|
31
|
+
if (text && typeof file.path === 'string') {
|
|
32
|
+
const raw = readFileSync(file.path, 'utf8');
|
|
33
|
+
const at = raw.endsWith(text) ? raw.length - text.length : raw.indexOf(text);
|
|
34
|
+
if (at > 0) {
|
|
35
|
+
for (let index = 0; index < at; index += 1) {
|
|
36
|
+
if (raw.charCodeAt(index) === 10) offset += 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
// A hand-built tree has no file on disk: its positions are all there is.
|
|
42
|
+
offset = 0;
|
|
43
|
+
}
|
|
44
|
+
offsets.set(file, offset);
|
|
45
|
+
return offset;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* `node` with its `position` moved to the file's own lines, for
|
|
50
|
+
* `file.fail` / `file.message`, which print it. The node itself is left alone:
|
|
51
|
+
* the tree keeps the positions the parser gave it.
|
|
52
|
+
*/
|
|
53
|
+
export function shiftedPlace(file, node) {
|
|
54
|
+
const position = node?.position;
|
|
55
|
+
if (!position?.start || typeof position.start.line !== 'number') return node;
|
|
56
|
+
const offset = sourceLineOffset(file);
|
|
57
|
+
if (offset === 0) return node;
|
|
58
|
+
const move = (point) =>
|
|
59
|
+
point && typeof point.line === 'number' ? { ...point, line: point.line + offset } : point;
|
|
60
|
+
// vfile-message reads a place from a node only when it has a `type`.
|
|
61
|
+
return {
|
|
62
|
+
type: node.type ?? 'unknown',
|
|
63
|
+
position: { start: move(position.start), end: move(position.end) },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -60,6 +60,12 @@ interface SharedNotationDefinitionInput {
|
|
|
60
60
|
readonly alignment: NotationAlignment;
|
|
61
61
|
readonly aiAssisted: boolean;
|
|
62
62
|
readonly body: string;
|
|
63
|
+
/**
|
|
64
|
+
* 1-based line in `source.file` where `body` starts, as on a lesson: the
|
|
65
|
+
* gate's body-relative findings are shifted by it. Optional for a
|
|
66
|
+
* hand-built input, which then behaves as 1.
|
|
67
|
+
*/
|
|
68
|
+
readonly bodyLine?: number;
|
|
63
69
|
/** References harvested from the Markdown body. */
|
|
64
70
|
readonly references: readonly NotationReferenceInput[];
|
|
65
71
|
/** Symbols this entry's `formula` names but does not make cards of. */
|
|
@@ -109,6 +115,28 @@ interface NotationLessonInput {
|
|
|
109
115
|
* so a hand-built input (a test, an adapter) stays valid.
|
|
110
116
|
*/
|
|
111
117
|
readonly suspended?: readonly NotationSuspension[];
|
|
118
|
+
/**
|
|
119
|
+
* A deck is a page of its own whose symbols resolve in other lessons'
|
|
120
|
+
* scopes: the ids of the lessons whose `notation.local` entries are in
|
|
121
|
+
* scope on this page, the first that defines a key binding it. A deck
|
|
122
|
+
* beside its chapter (`<lesson>.deck.mdx`, page `<lessonId>.slides`) has
|
|
123
|
+
* its chapter's; a part's deck (`<part>/index.deck.mdx`, page
|
|
124
|
+
* `<part>.slides`) every chapter of its part, in reading order. Absent:
|
|
125
|
+
* the page's own `lessonId`, and the page is no deck.
|
|
126
|
+
*/
|
|
127
|
+
readonly scopeLessonIds?: readonly string[];
|
|
128
|
+
/**
|
|
129
|
+
* Those lessons' `notation.local` entries, for the gates that build a
|
|
130
|
+
* page's glyph table, the first of a key kept. They stay the chapters'
|
|
131
|
+
* definitions: the registry defines each once, under its chapter.
|
|
132
|
+
*/
|
|
133
|
+
readonly inheritedLocalDefinitions?: readonly LocalNotationDefinitionInput[];
|
|
134
|
+
/**
|
|
135
|
+
* The shared keys those lessons' chapters name (`[[key]]`, `\explain{key}`):
|
|
136
|
+
* a deck may show any symbol of its lessons — what it recalls, and what its
|
|
137
|
+
* legend line introduces at its first use (`docs/authoring/deck.md`).
|
|
138
|
+
*/
|
|
139
|
+
readonly inheritedKeys?: readonly string[];
|
|
112
140
|
/** References harvested from lesson prose and equations, excluding definitions. */
|
|
113
141
|
readonly references: readonly NotationReferenceInput[];
|
|
114
142
|
/** Original lesson body, retained for lossless loader adapters. */
|
package/dist/rules/catalog.d.ts
CHANGED
|
@@ -26,14 +26,23 @@
|
|
|
26
26
|
* `unchecked` by {@link enforcementOf}.
|
|
27
27
|
*/
|
|
28
28
|
/** The part of a page a rule is about. The first tag on an entry is primary. */
|
|
29
|
-
type Element = 'prose' | 'math' | 'equation' | 'notation' | 'heading' | 'part-page' | 'plot' | 'diagram' | 'table' | 'assessment' | 'source' | 'changelog';
|
|
29
|
+
type Element = 'slide' | 'prose' | 'math' | 'equation' | 'notation' | 'heading' | 'part-page' | 'plot' | 'diagram' | 'table' | 'assessment' | 'source' | 'changelog';
|
|
30
30
|
/** Every element, in the order the rules-in-effect page lists them. */
|
|
31
31
|
declare const ELEMENTS: readonly Element[];
|
|
32
32
|
/**
|
|
33
|
-
* The
|
|
33
|
+
* The six areas the engine ships, plus `X` — reserved for a course's own rules
|
|
34
34
|
* and never used by an engine rule (see {@link CourseRuleId}).
|
|
35
35
|
*/
|
|
36
|
-
type RuleArea = 'N' | 'V' | 'C' | 'S' | 'P' | 'X';
|
|
36
|
+
type RuleArea = 'N' | 'V' | 'C' | 'S' | 'P' | 'D' | 'X';
|
|
37
|
+
/**
|
|
38
|
+
* Which edition of a lesson a rule is about (`docs/authoring/deck.md`): its
|
|
39
|
+
* chapter, its deck (`<lesson>.deck.mdx`), or `both`. A rule without one is
|
|
40
|
+
* `both`. The rules-in-effect page is written per edition, so an author of
|
|
41
|
+
* either reads one list: a chapter-only rule is off on a deck, where a `D`
|
|
42
|
+
* rule says the same thing about a frame.
|
|
43
|
+
*/
|
|
44
|
+
type Edition = 'chapter' | 'deck';
|
|
45
|
+
type Editions = Edition | 'both';
|
|
37
46
|
type RuleId = `${RuleArea}${number}`;
|
|
38
47
|
/**
|
|
39
48
|
* A course's own rule, defined in `courseRules` in the course settings rather
|
|
@@ -113,6 +122,8 @@ interface RuleEntry {
|
|
|
113
122
|
* absent means "shipped with the engine".
|
|
114
123
|
*/
|
|
115
124
|
readonly source?: 'course';
|
|
125
|
+
/** The edition the rule is about; absent is `both` ({@link Editions}). */
|
|
126
|
+
readonly editions?: Editions;
|
|
116
127
|
}
|
|
117
128
|
interface InvariantEntry {
|
|
118
129
|
readonly kind: 'invariant';
|
|
@@ -123,8 +134,14 @@ interface InvariantEntry {
|
|
|
123
134
|
readonly doc: string;
|
|
124
135
|
readonly summary: string;
|
|
125
136
|
readonly checks: readonly [RuleCheck, ...RuleCheck[]];
|
|
137
|
+
/** The edition the invariant is about; absent is `both` ({@link Editions}). */
|
|
138
|
+
readonly editions?: Editions;
|
|
126
139
|
}
|
|
127
140
|
type CatalogEntry = RuleEntry | InvariantEntry;
|
|
141
|
+
/** True when `entry` is in force for `edition` ({@link Editions}). */
|
|
142
|
+
declare function appliesTo(entry: {
|
|
143
|
+
readonly editions?: Editions;
|
|
144
|
+
}, edition: Edition): boolean;
|
|
128
145
|
type Enforcement = 'script' | 'partial' | 'guide';
|
|
129
146
|
/**
|
|
130
147
|
* A rule that is no longer in force. It keeps its ID forever — an old commit
|
|
@@ -173,4 +190,4 @@ declare function enforcementOf(entry: CatalogEntry): Enforcement;
|
|
|
173
190
|
/** Every code the catalog owns, sorted. */
|
|
174
191
|
declare function ownedCodes(): readonly string[];
|
|
175
192
|
|
|
176
|
-
export { CATALOG, type CatalogEntry, type CourseRuleId, ELEMENTS, type Element, type Emitter, type Enforcement, type InvariantEntry, type RetiredRule, type RuleArea, type RuleCheck, type RuleEntry, type RuleId, type SettingSchema, type SettingSpec, type Severity, enforcementOf, entryById, invariants, isCourseRuleId, ownedCodes, ownerOf, retiredRule, retiredRules, ruleById, ruleIdOf, rules };
|
|
193
|
+
export { CATALOG, type CatalogEntry, type CourseRuleId, ELEMENTS, type Edition, type Editions, type Element, type Emitter, type Enforcement, type InvariantEntry, type RetiredRule, type RuleArea, type RuleCheck, type RuleEntry, type RuleId, type SettingSchema, type SettingSpec, type Severity, appliesTo, enforcementOf, entryById, invariants, isCourseRuleId, ownedCodes, ownerOf, retiredRule, retiredRules, ruleById, ruleIdOf, rules };
|