explico 0.13.1 → 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 +879 -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 +1045 -16
- 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 +15 -7
- 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
|
@@ -1082,24 +1082,24 @@ export function isRunningIndexLetter(text) {
|
|
|
1082
1082
|
return RUNNING_INDEX_LETTER.test(text);
|
|
1083
1083
|
}
|
|
1084
1084
|
|
|
1085
|
+
const isRunningLetterNode = (node) =>
|
|
1086
|
+
node.type === 'mathord' && RUNNING_INDEX_LETTER.test(node.text ?? '');
|
|
1087
|
+
|
|
1085
1088
|
/**
|
|
1086
|
-
* What one index slot is: `running` (a lowercase letter), `
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
*
|
|
1089
|
+
* What one index slot is: `running` (a lowercase letter), `run` (two or more
|
|
1090
|
+
* juxtaposed lowercase letters, the double index of `x_{ij}`, each a running
|
|
1091
|
+
* letter), `digits`, `named` (a `\mathrm{…}` / `\text{…}` label, or a subscript
|
|
1092
|
+
* the page declares fixed: an index entry `{}_x`, a letter card with
|
|
1093
|
+
* `fixedIndex`), `symbol` (anything else: `N`, `\theta`, `T_{\mathrm{fwd}}`,
|
|
1094
|
+
* `t-1`), or `empty`.
|
|
1090
1095
|
*/
|
|
1091
1096
|
function slotKind(slot, fixedSlots) {
|
|
1092
1097
|
if (slot.length === 0) return 'empty';
|
|
1093
1098
|
if (fixedSlots.has(sequenceSignature(slot))) return 'named';
|
|
1094
1099
|
if (slot.every(isDigitNode)) return 'digits';
|
|
1095
1100
|
if (slot.length === 1 && isLabelNode(slot[0])) return 'named';
|
|
1096
|
-
if (
|
|
1097
|
-
|
|
1098
|
-
slot[0].type === 'mathord' &&
|
|
1099
|
-
RUNNING_INDEX_LETTER.test(slot[0].text ?? '')
|
|
1100
|
-
) {
|
|
1101
|
-
return 'running';
|
|
1102
|
-
}
|
|
1101
|
+
if (slot.length === 1 && isRunningLetterNode(slot[0])) return 'running';
|
|
1102
|
+
if (slot.length > 1 && slot.every(isRunningLetterNode)) return 'run';
|
|
1103
1103
|
return 'symbol';
|
|
1104
1104
|
}
|
|
1105
1105
|
|
|
@@ -1168,7 +1168,8 @@ function coreSourceSpan(node, latex) {
|
|
|
1168
1168
|
* slots, the same superscript or none on the family, and each slot fits. A
|
|
1169
1169
|
* running slot takes anything but a fixed label (`x_1`, `x_{k+1}`, `x_N`; not
|
|
1170
1170
|
* `x_{\mathrm{e}}`), a `symbol` slot any symbol (`T_{\mathrm{opt}}` in place
|
|
1171
|
-
* of `T_{\mathrm{fwd}}`),
|
|
1171
|
+
* of `T_{\mathrm{fwd}}`), a `run` slot (`x_{ij}`) a run of as many letters
|
|
1172
|
+
* and digits (`x_{i1}`, `x_{12}`, `x_{ji}`), and a fixed slot only itself.
|
|
1172
1173
|
*/
|
|
1173
1174
|
function isFamilyInstance(family, form, fixedSlots) {
|
|
1174
1175
|
if (family.form.baseSignature !== form.baseSignature) return false;
|
|
@@ -1180,6 +1181,12 @@ function isFamilyInstance(family, form, fixedSlots) {
|
|
|
1180
1181
|
const kind = family.kinds[index];
|
|
1181
1182
|
const filler = form.slots[index];
|
|
1182
1183
|
const fillerKind = slotKind(filler, fixedSlots);
|
|
1184
|
+
if (kind === 'run') {
|
|
1185
|
+
return (
|
|
1186
|
+
filler.length === slot.length &&
|
|
1187
|
+
filler.every((node) => isRunningLetterNode(node) || isDigitNode(node))
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1183
1190
|
if (kind === 'running' || kind === 'symbol') {
|
|
1184
1191
|
return fillerKind !== 'named' && fillerKind !== 'empty';
|
|
1185
1192
|
}
|
|
@@ -1462,7 +1469,13 @@ function buildGlyphIndex(definitions) {
|
|
|
1462
1469
|
const indexEntries = new Map();
|
|
1463
1470
|
const headForms = new Map();
|
|
1464
1471
|
const fixedSlots = new Set();
|
|
1472
|
+
// Who fixed a subscript (rule N23), and the entries marked `fixedIndex`
|
|
1473
|
+
// themselves, so a symbol that would have been a family's instance can say
|
|
1474
|
+
// why it is not (`unresolvedIdentifiers`).
|
|
1475
|
+
const fixedBy = new Map();
|
|
1476
|
+
const fixedEntries = new Set();
|
|
1465
1477
|
const indexedEntries = [];
|
|
1478
|
+
const indexedForms = new Map();
|
|
1466
1479
|
// What each bare glyph's indexed entries are, for the message of a symbol
|
|
1467
1480
|
// that no longer borrows one's card (rule N4).
|
|
1468
1481
|
const indexedBases = new Map();
|
|
@@ -1537,6 +1550,7 @@ function buildGlyphIndex(definitions) {
|
|
|
1537
1550
|
}
|
|
1538
1551
|
indexEntries.set(subscript, key);
|
|
1539
1552
|
fixedSlots.add(subscript);
|
|
1553
|
+
if (!fixedBy.has(subscript)) fixedBy.set(subscript, { key, via: 'index-entry', notation });
|
|
1540
1554
|
continue;
|
|
1541
1555
|
}
|
|
1542
1556
|
|
|
@@ -1545,10 +1559,14 @@ function buildGlyphIndex(definitions) {
|
|
|
1545
1559
|
if (significant.length === 1 && indexedForm(significant[0])) {
|
|
1546
1560
|
const form = indexedForm(significant[0]);
|
|
1547
1561
|
indexedEntries.push({ key, form, fixedIndex });
|
|
1562
|
+
if (fixedIndex) fixedEntries.add(key);
|
|
1563
|
+
indexedForms.set(key, form);
|
|
1548
1564
|
noteIndexedBase(form.baseSignature, key);
|
|
1549
1565
|
}
|
|
1550
1566
|
if (fixedIndex && significant.length === 1 && isIdentifierNode(significant[0])) {
|
|
1551
|
-
|
|
1567
|
+
const letter = sequenceSignature(significant);
|
|
1568
|
+
fixedSlots.add(letter);
|
|
1569
|
+
if (!fixedBy.has(letter)) fixedBy.set(letter, { key, via: 'fixedIndex', notation });
|
|
1552
1570
|
}
|
|
1553
1571
|
const indexedHead = indexedFunctionHead(tree);
|
|
1554
1572
|
if (indexedHead) {
|
|
@@ -1586,14 +1604,14 @@ function buildGlyphIndex(definitions) {
|
|
|
1586
1604
|
}
|
|
1587
1605
|
}
|
|
1588
1606
|
|
|
1589
|
-
// A family is an indexed entry with a running slot (`x_k`, `F_{t,T}`); one
|
|
1607
|
+
// A family is an indexed entry with a running slot (`x_k`, `F_{t,T}`, `x_{ij}`); one
|
|
1590
1608
|
// whose every slot is fixed (`v_0`, `T_{\mathrm{opt}}`, or `fixedIndex`)
|
|
1591
1609
|
// is a single symbol and lends its card to nothing.
|
|
1592
1610
|
const families = new Map();
|
|
1593
1611
|
for (const { key, form, fixedIndex } of indexedEntries) {
|
|
1594
1612
|
if (fixedIndex) continue;
|
|
1595
1613
|
const kinds = form.slots.map((slot) => slotKind(slot, fixedSlots));
|
|
1596
|
-
if (!kinds.includes('running')) continue;
|
|
1614
|
+
if (!kinds.includes('running') && !kinds.includes('run')) continue;
|
|
1597
1615
|
const list = families.get(form.baseSignature) ?? [];
|
|
1598
1616
|
list.push({ key, form, kinds });
|
|
1599
1617
|
families.set(form.baseSignature, list);
|
|
@@ -1608,7 +1626,10 @@ function buildGlyphIndex(definitions) {
|
|
|
1608
1626
|
indexEntries,
|
|
1609
1627
|
headForms,
|
|
1610
1628
|
fixedSlots,
|
|
1629
|
+
fixedBy,
|
|
1630
|
+
fixedEntries,
|
|
1611
1631
|
families,
|
|
1632
|
+
indexedForms,
|
|
1612
1633
|
indexedBases,
|
|
1613
1634
|
maxNodeLength,
|
|
1614
1635
|
};
|
|
@@ -1651,7 +1672,12 @@ function resolveIndexedNode(node, glyphs, source, derive) {
|
|
|
1651
1672
|
|
|
1652
1673
|
const head = sequenceSignature([node]);
|
|
1653
1674
|
if (glyphs.headForms.has(head) && !glyphs.ambiguousBase.has(head)) {
|
|
1654
|
-
return {
|
|
1675
|
+
return {
|
|
1676
|
+
key: glyphs.headForms.get(head),
|
|
1677
|
+
span: whole,
|
|
1678
|
+
match: 'canonical',
|
|
1679
|
+
sub: sourceSpan(node.sub, source),
|
|
1680
|
+
};
|
|
1655
1681
|
}
|
|
1656
1682
|
if (form.sup !== 'none') {
|
|
1657
1683
|
const signature = coreSignature(form);
|
|
@@ -1660,7 +1686,9 @@ function resolveIndexedNode(node, glyphs, source, derive) {
|
|
|
1660
1686
|
(glyphs.ambiguousBase.has(signature)
|
|
1661
1687
|
? undefined
|
|
1662
1688
|
: glyphs.headForms.get(signature));
|
|
1663
|
-
if (key !== undefined)
|
|
1689
|
+
if (key !== undefined) {
|
|
1690
|
+
return { key, span: core, match: 'canonical', sub: sourceSpan(node.sub, source) };
|
|
1691
|
+
}
|
|
1664
1692
|
}
|
|
1665
1693
|
|
|
1666
1694
|
const families = (glyphs.families.get(form.baseSignature) ?? []).filter(
|
|
@@ -1682,6 +1710,7 @@ function resolveIndexedNode(node, glyphs, source, derive) {
|
|
|
1682
1710
|
key: family.key,
|
|
1683
1711
|
span: family.form.sup !== 'none' ? whole : core,
|
|
1684
1712
|
match: 'family',
|
|
1713
|
+
sub: sourceSpan(node.sub, source),
|
|
1685
1714
|
};
|
|
1686
1715
|
}
|
|
1687
1716
|
|
|
@@ -1698,6 +1727,33 @@ function resolveIndexedNode(node, glyphs, source, derive) {
|
|
|
1698
1727
|
};
|
|
1699
1728
|
}
|
|
1700
1729
|
|
|
1730
|
+
/**
|
|
1731
|
+
* The base of each indexed symbol an author wrapped whole in the `\explain` of
|
|
1732
|
+
* an indexed entry with that same base — `\sigma` of
|
|
1733
|
+
* `\explain{component-sd}{\sigma_1}` under the family `\sigma_k`. The wrap
|
|
1734
|
+
* names the indexed symbol, not its base, so the base takes no card of its own
|
|
1735
|
+
* from another entry (a plain `\sigma` on the page): a binding for it would
|
|
1736
|
+
* open the wrong card on the letter. The index inside keeps resolving.
|
|
1737
|
+
*/
|
|
1738
|
+
function wrappedBaseSpans(indexed, explicitCalls, glyphs, latex, source) {
|
|
1739
|
+
const tight = (span) => (span ? unwrapEnclosingGroupSpan(latex, span) : span);
|
|
1740
|
+
const spans = [];
|
|
1741
|
+
for (const call of explicitCalls) {
|
|
1742
|
+
const entry = glyphs.indexedForms.get(call.key);
|
|
1743
|
+
if (!entry) continue;
|
|
1744
|
+
for (const node of indexed) {
|
|
1745
|
+
const form = indexedForm(node);
|
|
1746
|
+
if (form?.baseSignature !== entry.baseSignature) continue;
|
|
1747
|
+
const exactly = [coreSourceSpan(node, source), sourceSpan(node, source)].some(
|
|
1748
|
+
(span) => span && sameSpan(tight(span), call),
|
|
1749
|
+
);
|
|
1750
|
+
const base = tight(sourceSpan(node.base, source));
|
|
1751
|
+
if (exactly && base) spans.push(base);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
return spans;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1701
1757
|
/**
|
|
1702
1758
|
* Bind whole canonical forms: greedy longest match, left to right, per run.
|
|
1703
1759
|
* A symbol with a subscript that no whole form matches exactly is then
|
|
@@ -1716,6 +1772,7 @@ function matchCanonicalForms(
|
|
|
1716
1772
|
scriptSpans,
|
|
1717
1773
|
derive,
|
|
1718
1774
|
explicitCalls,
|
|
1775
|
+
wrappedBases = [],
|
|
1719
1776
|
) {
|
|
1720
1777
|
const bindings = [];
|
|
1721
1778
|
const claimed = [];
|
|
@@ -1729,6 +1786,7 @@ function matchCanonicalForms(
|
|
|
1729
1786
|
};
|
|
1730
1787
|
|
|
1731
1788
|
const blocked = (span, nodes) =>
|
|
1789
|
+
wrappedBases.some((base) => contains(base, span)) ||
|
|
1732
1790
|
claimed.some(
|
|
1733
1791
|
(existing) =>
|
|
1734
1792
|
sameSpan(existing, span) ||
|
|
@@ -1755,7 +1813,11 @@ function matchCanonicalForms(
|
|
|
1755
1813
|
// or `b'` wraps the decoration too, not just the letter under it.
|
|
1756
1814
|
const rawSpan = sourceSpan(nodes, strippedSource);
|
|
1757
1815
|
if (!rawSpan) continue;
|
|
1758
|
-
|
|
1816
|
+
const sub =
|
|
1817
|
+
nodes.length === 1 && nodes[0].type === 'supsub' && nodes[0].sub
|
|
1818
|
+
? sourceSpan(nodes[0].sub, strippedSource)
|
|
1819
|
+
: undefined;
|
|
1820
|
+
found = { key, nodes, rawSpan, match: 'canonical', ...(sub ? { sub } : {}) };
|
|
1759
1821
|
break;
|
|
1760
1822
|
}
|
|
1761
1823
|
// A group around one node (an `aligned` cell's) shares that node's
|
|
@@ -1788,6 +1850,7 @@ function matchCanonicalForms(
|
|
|
1788
1850
|
level: 'scope',
|
|
1789
1851
|
match: found.match,
|
|
1790
1852
|
...(found.derived ? { derived: found.derived } : {}),
|
|
1853
|
+
...(found.sub ? { sub: found.sub } : {}),
|
|
1791
1854
|
});
|
|
1792
1855
|
claimed.push({ ...span, match: found.match });
|
|
1793
1856
|
}
|
|
@@ -1890,26 +1953,109 @@ function injectScopeBindings(latex, bindings) {
|
|
|
1890
1953
|
return output;
|
|
1891
1954
|
}
|
|
1892
1955
|
|
|
1956
|
+
/** The kinds a slot has when nothing on the page fixes it. */
|
|
1957
|
+
const NO_FIXED_SLOTS = new Set();
|
|
1958
|
+
|
|
1959
|
+
/**
|
|
1960
|
+
* The families a bare base would have lent its card to, had a card not fixed
|
|
1961
|
+
* the letter their index runs over (rule N23), for a base that is written with
|
|
1962
|
+
* a subscript in the source: `r_2` under the family `r_m` once the card of `m`
|
|
1963
|
+
* says `fixedIndex: true`. The bare `r` is what stays unresolved, and "never a
|
|
1964
|
+
* bare r" would blame the wrong thing, so name the instance and what fixed the
|
|
1965
|
+
* letter. `undefined` when the identifier carries no subscript here, or no
|
|
1966
|
+
* lender fails for that reason.
|
|
1967
|
+
*
|
|
1968
|
+
* @returns {{ instance: string, fixedLenders: Array<{ key: string, via: string, card?: string, letter?: string }> } | undefined}
|
|
1969
|
+
*/
|
|
1970
|
+
function fixedLendersOf(identifier, lenders, latex, glyphs, indexed) {
|
|
1971
|
+
if (lenders.length === 0) return undefined;
|
|
1972
|
+
const symbol = indexed.find((node) => flattenGroup(node.base)[0] === identifier);
|
|
1973
|
+
const form = symbol === undefined ? undefined : indexedForm(symbol);
|
|
1974
|
+
if (symbol === undefined || form === undefined) return undefined;
|
|
1975
|
+
|
|
1976
|
+
const fixedLenders = [];
|
|
1977
|
+
for (const key of lenders) {
|
|
1978
|
+
const lender = glyphs.indexedForms.get(key);
|
|
1979
|
+
if (lender === undefined) continue;
|
|
1980
|
+
const family = { form: lender, kinds: lender.slots.map((slot) => slotKind(slot, NO_FIXED_SLOTS)) };
|
|
1981
|
+
if (!family.kinds.includes('running') && !family.kinds.includes('run')) continue;
|
|
1982
|
+
if (!isFamilyInstance(family, form, NO_FIXED_SLOTS)) continue;
|
|
1983
|
+
if (glyphs.fixedEntries.has(key)) {
|
|
1984
|
+
fixedLenders.push({ key, via: 'entry' });
|
|
1985
|
+
continue;
|
|
1986
|
+
}
|
|
1987
|
+
for (const slot of lender.slots) {
|
|
1988
|
+
const by = glyphs.fixedBy.get(sequenceSignature(slot));
|
|
1989
|
+
if (by === undefined) continue;
|
|
1990
|
+
fixedLenders.push({ key, via: by.via, card: by.key, letter: by.notation });
|
|
1991
|
+
break;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
if (fixedLenders.length === 0) return undefined;
|
|
1995
|
+
const span = coreSourceSpan(symbol, latex);
|
|
1996
|
+
return {
|
|
1997
|
+
instance: span ? latex.slice(span.start, span.end) : '',
|
|
1998
|
+
fixedLenders,
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
/**
|
|
2003
|
+
* The math span a finding sits in, as an author wrote it: `$…$`, whitespace
|
|
2004
|
+
* collapsed, cut at 100 characters. A line number can drift (a loader that
|
|
2005
|
+
* hands over a body, a generated file), but the span can be searched for, so a
|
|
2006
|
+
* message that quotes it still leads to the spot.
|
|
2007
|
+
*
|
|
2008
|
+
* @param {string} latex
|
|
2009
|
+
* @returns {string}
|
|
2010
|
+
*/
|
|
2011
|
+
export function mathExcerpt(latex) {
|
|
2012
|
+
const flat = String(latex).replace(/\s+/g, ' ').trim();
|
|
2013
|
+
return `$${flat.length > 100 ? `${flat.slice(0, 99)}…` : flat}$`;
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/**
|
|
2017
|
+
* What to tell an author whose subscripted symbol a family would have
|
|
2018
|
+
* explained, were its index not fixed (rule N23): the cause, and the repair
|
|
2019
|
+
* that fits it. One sentence, for an unresolved item's `fixedLenders`.
|
|
2020
|
+
*
|
|
2021
|
+
* @param {{ key: string, via: string, card?: string, letter?: string }} lender
|
|
2022
|
+
* @param {string} instance The subscripted symbol as written, `r_2`.
|
|
2023
|
+
* @returns {string}
|
|
2024
|
+
*/
|
|
2025
|
+
export function fixedLenderMessage(lender, instance) {
|
|
2026
|
+
const own = `give ${instance} an entry of its own (latex: ${instance})`;
|
|
2027
|
+
const lends = `${lender.key} is one symbol and lends its card to nothing, ${instance} included`;
|
|
2028
|
+
switch (lender.via) {
|
|
2029
|
+
case 'entry':
|
|
2030
|
+
return `${instance} would be an instance of ${lender.key}, but ${lender.key} is marked fixedIndex, so it is one symbol and lends its card to nothing (rule N23). Drop fixedIndex there if its index runs over a set, or ${own}.`;
|
|
2031
|
+
case 'index-entry':
|
|
2032
|
+
return `${instance} would be an instance of ${lender.key}, but the index entry ${lender.card} fixes that subscript, so ${lends} (rule N23). Drop that index entry, or ${own}.`;
|
|
2033
|
+
default:
|
|
2034
|
+
return `${instance} would be an instance of ${lender.key}, but ${lender.letter} is marked fixedIndex on ${lender.card}, so ${lends} (rule N23). If ${lender.letter} runs over a set, drop fixedIndex from ${lender.card} and give it a range; if it is a label, ${own}.`;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
|
|
1893
2038
|
/**
|
|
1894
2039
|
* Every identifier no binding covers, plus every decorated form whose
|
|
1895
2040
|
* decoration is suspended on this page and that no whole form covers (rule
|
|
1896
2041
|
* N22). A letter under such a decoration is reported once, as the decorated
|
|
1897
2042
|
* form (`b''''`, not `b`): that whole form is what needs the entry.
|
|
1898
2043
|
*/
|
|
1899
|
-
function unresolvedIdentifiers(latex, identifiers, bindings, suspended,
|
|
2044
|
+
function unresolvedIdentifiers(latex, identifiers, bindings, suspended, glyphs, indexed) {
|
|
1900
2045
|
const letters = identifiers
|
|
1901
2046
|
.filter(
|
|
1902
2047
|
(identifier) =>
|
|
1903
2048
|
!bindings.some((binding) => contains(binding, identifier)) &&
|
|
1904
2049
|
!suspended.some((decoration) => contains(decoration.part, identifier)),
|
|
1905
2050
|
)
|
|
1906
|
-
.map(({ start, end, font, signature }) => {
|
|
2051
|
+
.map(({ node, start, end, font, signature }) => {
|
|
1907
2052
|
// Report `\mathbb{E}`, not a bare `E`, so the fix hint points at the
|
|
1908
2053
|
// whole token the author has to wrap in `\explain`.
|
|
1909
2054
|
const wrapped = expandFontWrapperSpan(latex, { start, end });
|
|
1910
2055
|
// Rule N23: an indexed entry lends its card to no bare glyph, so name
|
|
1911
2056
|
// the ones this letter used to borrow from.
|
|
1912
|
-
const lenders = font === undefined ? (indexedBases?.get(signature) ?? []) : [];
|
|
2057
|
+
const lenders = font === undefined ? (glyphs.indexedBases?.get(signature) ?? []) : [];
|
|
2058
|
+
const fixed = fixedLendersOf(node, lenders, latex, glyphs, indexed);
|
|
1913
2059
|
return {
|
|
1914
2060
|
token: latex.slice(wrapped.start, wrapped.end),
|
|
1915
2061
|
// A styled base-library letter (`\mathbf{e}`) is not Euler's number.
|
|
@@ -1921,6 +2067,7 @@ function unresolvedIdentifiers(latex, identifiers, bindings, suspended, indexedB
|
|
|
1921
2067
|
end: wrapped.end,
|
|
1922
2068
|
...(font === undefined ? {} : { font }),
|
|
1923
2069
|
...(lenders.length === 0 ? {} : { lenders: [...lenders] }),
|
|
2070
|
+
...(fixed === undefined ? {} : fixed),
|
|
1924
2071
|
};
|
|
1925
2072
|
})
|
|
1926
2073
|
.filter(({ libraryToken }) => !BASE_LIBRARY.has(libraryToken))
|
|
@@ -1954,7 +2101,10 @@ function unresolvedIdentifiers(latex, identifiers, bindings, suspended, indexedB
|
|
|
1954
2101
|
*
|
|
1955
2102
|
* `running` lists the running index letters (`i`, `k`, `t`) in its slots that
|
|
1956
2103
|
* the page has not declared fixed, with the key of the letter's own card and
|
|
1957
|
-
* of the entry that covers the symbol, for the index-set check.
|
|
2104
|
+
* of the entry that covers the symbol, for the index-set check. A letter that
|
|
2105
|
+
* belongs to a larger symbol with its own card (`b` of `b_n` in
|
|
2106
|
+
* `\nabla_{b_n}`) and every letter of a slot that holds a relation
|
|
2107
|
+
* (`\mathbb{E}_{Z\sim g}`, a condition) is not an index and is not listed.
|
|
1958
2108
|
*/
|
|
1959
2109
|
function classifyIndexed(nodes, context) {
|
|
1960
2110
|
const { latex, source, bindings, explicitCalls, glyphs, identifiers, unresolved } =
|
|
@@ -1978,6 +2128,8 @@ function classifyIndexed(nodes, context) {
|
|
|
1978
2128
|
const slots = indexSlots(node.sub).map((slot) => ({
|
|
1979
2129
|
span: tight(sourceSpan(slot, source)),
|
|
1980
2130
|
kind: slotKind(slot, glyphs.fixedSlots),
|
|
2131
|
+
// `Z\sim g` in `\mathbb{E}_{Z\sim g}` states a condition, not a position.
|
|
2132
|
+
condition: slot.some((item) => item.type === 'atom' && item.family === 'rel'),
|
|
1981
2133
|
}));
|
|
1982
2134
|
const exactly = (span) => sameSpan(span, core) || sameSpan(span, whole);
|
|
1983
2135
|
|
|
@@ -2037,7 +2189,7 @@ function classifyIndexed(nodes, context) {
|
|
|
2037
2189
|
// A swallowed index is already an error; its letters wait for the fix.
|
|
2038
2190
|
if (!['inside-wrap', 'derived', 'unresolved', 'swallowed'].includes(how)) {
|
|
2039
2191
|
for (const slot of slots) {
|
|
2040
|
-
if (slot.kind === 'named' || !slot.span) continue;
|
|
2192
|
+
if (slot.kind === 'named' || slot.condition || !slot.span) continue;
|
|
2041
2193
|
for (const identifier of identifiers) {
|
|
2042
2194
|
if (identifier.font !== undefined) continue;
|
|
2043
2195
|
if (identifier.node.type !== 'mathord') continue;
|
|
@@ -2049,6 +2201,10 @@ function classifyIndexed(nodes, context) {
|
|
|
2049
2201
|
contains(binding, identifier) && contains(slot.span, binding),
|
|
2050
2202
|
)
|
|
2051
2203
|
.sort((left, right) => size(left) - size(right))[0];
|
|
2204
|
+
// A letter inside a larger symbol with a card of its own is part of
|
|
2205
|
+
// that symbol, not an index: `b` of `b_n` in `\nabla_{b_n}`. The
|
|
2206
|
+
// inner symbol is classified in its own right.
|
|
2207
|
+
if (own && own.level === 'scope' && size(own) > size(identifier)) continue;
|
|
2052
2208
|
running.push({
|
|
2053
2209
|
letter: identifier.node.text,
|
|
2054
2210
|
start: identifier.start,
|
|
@@ -2420,10 +2576,17 @@ export function resolveMathGlyphs(latex, definitions, options = {}) {
|
|
|
2420
2576
|
scriptSpans,
|
|
2421
2577
|
derive,
|
|
2422
2578
|
explicitCalls,
|
|
2579
|
+
wrappedBaseSpans(indexed, explicitCalls, glyphs, latex, strippedSource),
|
|
2423
2580
|
).filter(
|
|
2424
2581
|
(binding) =>
|
|
2425
2582
|
!explicitCalls.some(
|
|
2426
|
-
(call) =>
|
|
2583
|
+
(call) =>
|
|
2584
|
+
overlaps(binding, call) &&
|
|
2585
|
+
!contains(call, binding) &&
|
|
2586
|
+
// A symbol that holds an `\explain` in its own subscript
|
|
2587
|
+
// (`\beta_{\explain{count}{K}}`) encloses the call whole: the wraps
|
|
2588
|
+
// nest, and the index keeps its card.
|
|
2589
|
+
!(binding.sub !== undefined && contains(binding.sub, call)),
|
|
2427
2590
|
),
|
|
2428
2591
|
);
|
|
2429
2592
|
const covered = [...canonical, ...explicitCalls];
|
|
@@ -2518,7 +2681,8 @@ export function resolveMathGlyphs(latex, definitions, options = {}) {
|
|
|
2518
2681
|
identifiers,
|
|
2519
2682
|
bindings,
|
|
2520
2683
|
suspendedDecorations,
|
|
2521
|
-
glyphs
|
|
2684
|
+
glyphs,
|
|
2685
|
+
indexed,
|
|
2522
2686
|
);
|
|
2523
2687
|
const derived = new Map();
|
|
2524
2688
|
for (const binding of bindings) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Manifest, ManifestDerivedNotation, ManifestLessonNotation } from '../compiler/manifest.js';
|
|
1
|
+
import { Manifest, ManifestDerivedNotation, ManifestLessonNotation, ManifestLesson } from '../compiler/manifest.js';
|
|
2
2
|
import { DerivedUse } from './types.js';
|
|
3
3
|
import '../curriculum/validation.js';
|
|
4
4
|
import './consistency.js';
|
|
5
|
-
import '../course-config-
|
|
5
|
+
import '../course-config-BLtIdkei.js';
|
|
6
6
|
import '../rules/catalog.js';
|
|
7
7
|
import '../rules/course-rules.js';
|
|
8
8
|
import '../compiler/collections.js';
|
|
@@ -23,6 +23,10 @@ import './mixed-content.js';
|
|
|
23
23
|
import './gloss.js';
|
|
24
24
|
import '../rules/waivers.js';
|
|
25
25
|
import '../compiler/knowledge-version.js';
|
|
26
|
+
import '../compiler/page-versions.js';
|
|
27
|
+
import '../compiler/decks.js';
|
|
28
|
+
import './decks-validate.js';
|
|
29
|
+
import './deck-source.js';
|
|
26
30
|
import '../compiler/lesson-derivation.js';
|
|
27
31
|
|
|
28
32
|
interface PageNotationDependency {
|
|
@@ -66,6 +70,21 @@ declare function notationKeysInMathNodes(nodes: readonly {
|
|
|
66
70
|
readonly properties?: Readonly<Record<string, string>>;
|
|
67
71
|
readonly children?: readonly unknown[];
|
|
68
72
|
}[]): string[];
|
|
73
|
+
/**
|
|
74
|
+
* The compiled notation of a page: a lesson's (a chapter, or a slides-first
|
|
75
|
+
* lesson's deck), a page that is not a chapter (rule S10), or a deck beside
|
|
76
|
+
* its chapter (`manifest.decks`, by its page id).
|
|
77
|
+
*/
|
|
78
|
+
declare function pageNotationEntry(manifest: Manifest, pageId: string): {
|
|
79
|
+
readonly notation: ManifestLesson['notation'];
|
|
80
|
+
} | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* The lessons whose `notation.local` a page's symbols bind in, the first to
|
|
83
|
+
* define a key binding it (`NotationLessonInput.scopeLessonIds`): a lesson's
|
|
84
|
+
* deck's is its lesson; a part's deck's, every chapter of the part in
|
|
85
|
+
* reading order; any other page's, its own.
|
|
86
|
+
*/
|
|
87
|
+
declare function scopeLessonsOf(manifest: Manifest, pageId: string): readonly string[];
|
|
69
88
|
/**
|
|
70
89
|
* The derived cards (rule N23) a page uses: the ones its body and assessment
|
|
71
90
|
* math use, as the manifest composed them, and the ones a lab or a diagram on
|
|
@@ -86,4 +105,4 @@ declare function notationDefinitionsForRenderedPage(manifest: Manifest, pageId:
|
|
|
86
105
|
*/
|
|
87
106
|
declare function missingNotationPayloadKeys(html: string): string[];
|
|
88
107
|
|
|
89
|
-
export { type PageNotationDependency, type ScopePage, bindScopeToPage, derivedNotationForPage, missingNotationPayloadKeys, notationDefinitionsForRenderedPage, notationKeysInMathNodes, pageNotationDependencies, pageOfScope, registerPageDerivedNotation, registerPageNotationDependencies, routeIdFromLocals };
|
|
108
|
+
export { type PageNotationDependency, type ScopePage, bindScopeToPage, derivedNotationForPage, missingNotationPayloadKeys, notationDefinitionsForRenderedPage, notationKeysInMathNodes, pageNotationDependencies, pageNotationEntry, pageOfScope, registerPageDerivedNotation, registerPageNotationDependencies, routeIdFromLocals, scopeLessonsOf };
|
|
@@ -2,9 +2,18 @@ import {
|
|
|
2
2
|
formulaExplainKeys,
|
|
3
3
|
formulaGlyphScope
|
|
4
4
|
} from "./gloss.js";
|
|
5
|
-
import { resolveReferenceId } from "./references.js";
|
|
5
|
+
import { resolvePageReferenceId, resolveReferenceId } from "./references.js";
|
|
6
6
|
import { derivedNotationEntries } from "./derived-entries.js";
|
|
7
|
-
const
|
|
7
|
+
const REGISTRY_KEY = /* @__PURE__ */ Symbol.for("explico.reference.page-notation");
|
|
8
|
+
function sharedRegistry() {
|
|
9
|
+
const host = globalThis;
|
|
10
|
+
return host[REGISTRY_KEY] ??= {
|
|
11
|
+
dependenciesByLocals: /* @__PURE__ */ new WeakMap(),
|
|
12
|
+
derivedByLocals: /* @__PURE__ */ new WeakMap(),
|
|
13
|
+
pageByScope: /* @__PURE__ */ new WeakMap()
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const { dependenciesByLocals, derivedByLocals, pageByScope } = sharedRegistry();
|
|
8
17
|
function registerPageNotationDependencies(locals, keys, context) {
|
|
9
18
|
let byKey = dependenciesByLocals.get(locals);
|
|
10
19
|
if (!byKey) {
|
|
@@ -20,7 +29,6 @@ function registerPageNotationDependencies(locals, keys, context) {
|
|
|
20
29
|
contexts.add(context);
|
|
21
30
|
}
|
|
22
31
|
}
|
|
23
|
-
const derivedByLocals = /* @__PURE__ */ new WeakMap();
|
|
24
32
|
function registerPageDerivedNotation(locals, uses, context) {
|
|
25
33
|
let byKey = derivedByLocals.get(locals);
|
|
26
34
|
if (!byKey) {
|
|
@@ -34,7 +42,6 @@ function registerPageDerivedNotation(locals, uses, context) {
|
|
|
34
42
|
}
|
|
35
43
|
registerPageNotationDependencies(locals, entries, context);
|
|
36
44
|
}
|
|
37
|
-
const pageByScope = /* @__PURE__ */ new WeakMap();
|
|
38
45
|
function bindScopeToPage(scope, page) {
|
|
39
46
|
pageByScope.set(scope, page);
|
|
40
47
|
}
|
|
@@ -88,8 +95,21 @@ function manifestDefinition(definition, cardsByKey) {
|
|
|
88
95
|
...definition.range === void 0 ? {} : { range: definition.range }
|
|
89
96
|
};
|
|
90
97
|
}
|
|
98
|
+
function pageNotationEntry(manifest, pageId) {
|
|
99
|
+
return manifest.lessons.find((entry) => entry.id === pageId) ?? manifest.pages.find((entry) => entry.id === pageId) ?? (manifest.decks ?? []).find((entry) => entry.pageId === pageId);
|
|
100
|
+
}
|
|
101
|
+
function scopeLessonsOf(manifest, pageId) {
|
|
102
|
+
const deck = (manifest.decks ?? []).find((entry) => entry.pageId === pageId);
|
|
103
|
+
if (deck?.part !== void 0) {
|
|
104
|
+
const chapters = new Set(
|
|
105
|
+
manifest.lessons.filter((lesson) => (lesson.editions ?? ["chapter"]).includes("chapter")).map((lesson) => lesson.id)
|
|
106
|
+
);
|
|
107
|
+
return (manifest.parts.find((part) => part.dir === deck.part)?.lessons ?? []).filter((id) => chapters.has(id));
|
|
108
|
+
}
|
|
109
|
+
return [deck?.lessonId ?? pageId];
|
|
110
|
+
}
|
|
91
111
|
function derivedNotationForPage(manifest, pageId, locals) {
|
|
92
|
-
const page =
|
|
112
|
+
const page = pageNotationEntry(manifest, pageId);
|
|
93
113
|
const compiled = page?.notation.derived ?? [];
|
|
94
114
|
const registered = [...(locals && derivedByLocals.get(locals)?.values()) ?? []].filter(
|
|
95
115
|
(use) => !compiled.some((entry) => entry.key === use.key)
|
|
@@ -102,7 +122,7 @@ function derivedNotationForPage(manifest, pageId, locals) {
|
|
|
102
122
|
const records = /* @__PURE__ */ new Map();
|
|
103
123
|
for (const use of registered) {
|
|
104
124
|
for (const key of [use.base, use.index]) {
|
|
105
|
-
const id =
|
|
125
|
+
const id = resolvePageReferenceId(key, scopeLessonsOf(manifest, pageId), availableIds);
|
|
106
126
|
const record = id === void 0 ? void 0 : definitionsById.get(id);
|
|
107
127
|
if (record) records.set(key, record);
|
|
108
128
|
}
|
|
@@ -115,7 +135,7 @@ function derivedNotationForPage(manifest, pageId, locals) {
|
|
|
115
135
|
);
|
|
116
136
|
}
|
|
117
137
|
function notationDefinitionsForRenderedPage(manifest, pageId, dependencies) {
|
|
118
|
-
const page =
|
|
138
|
+
const page = pageNotationEntry(manifest, pageId);
|
|
119
139
|
if (!page) return [];
|
|
120
140
|
const definitionsById = new Map(
|
|
121
141
|
manifest.notation.definitions.map((definition) => [definition.id, definition])
|
|
@@ -130,9 +150,9 @@ function notationDefinitionsForRenderedPage(manifest, pageId, dependencies) {
|
|
|
130
150
|
contextsByKey.set(dependency.key, contexts);
|
|
131
151
|
}
|
|
132
152
|
contexts.add(dependency.context);
|
|
133
|
-
const definitionId =
|
|
153
|
+
const definitionId = resolvePageReferenceId(
|
|
134
154
|
dependency.key,
|
|
135
|
-
|
|
155
|
+
scopeLessonsOf(manifest, pageId),
|
|
136
156
|
availableIds
|
|
137
157
|
);
|
|
138
158
|
if (!definitionId) {
|
|
@@ -216,8 +236,10 @@ export {
|
|
|
216
236
|
notationDefinitionsForRenderedPage,
|
|
217
237
|
notationKeysInMathNodes,
|
|
218
238
|
pageNotationDependencies,
|
|
239
|
+
pageNotationEntry,
|
|
219
240
|
pageOfScope,
|
|
220
241
|
registerPageDerivedNotation,
|
|
221
242
|
registerPageNotationDependencies,
|
|
222
|
-
routeIdFromLocals
|
|
243
|
+
routeIdFromLocals,
|
|
244
|
+
scopeLessonsOf
|
|
223
245
|
};
|
|
@@ -21,7 +21,20 @@ declare function definitionId(scope: NotationDefinitionScope, key: string): stri
|
|
|
21
21
|
* can only see shared definitions, so their meaning cannot change by caller.
|
|
22
22
|
*/
|
|
23
23
|
declare function resolveReferenceId(key: string, scope: NotationDefinitionScope, availableDefinitionIds: ReadonlySet<string>): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve on a page whose symbols bind in several lessons' scopes, as a
|
|
26
|
+
* part's deck binds in its chapters' (`NotationLessonInput.scopeLessonIds`):
|
|
27
|
+
* the first lesson's local entry for the key, then the next lesson's, then
|
|
28
|
+
* the shared entry. With one lesson it is {@link resolveReferenceId}'s page
|
|
29
|
+
* scope.
|
|
30
|
+
*/
|
|
31
|
+
declare function resolvePageReferenceId(key: string, lessonIds: readonly string[], availableDefinitionIds: ReadonlySet<string>): string | undefined;
|
|
32
|
+
/** The lessons whose `notation.local` a page's symbols bind in: a deck's, or the page's own. */
|
|
33
|
+
declare function pageScopeLessonIds(lesson: {
|
|
34
|
+
readonly lessonId: string;
|
|
35
|
+
readonly scopeLessonIds?: readonly string[];
|
|
36
|
+
}): readonly string[];
|
|
24
37
|
declare function compareSourceSpans(left: SourceSpan | undefined, right: SourceSpan | undefined): number;
|
|
25
38
|
declare function compareDiagnostics(left: NotationDiagnostic, right: NotationDiagnostic): number;
|
|
26
39
|
|
|
27
|
-
export { NOTATION_KEY_PATTERN, RESERVED_KEY_PREFIXES, compareDiagnostics, compareSourceSpans, definitionId, isNotationKey, localDefinitionId, reservedKeyPrefix, resolveReferenceId, sharedDefinitionId };
|
|
40
|
+
export { NOTATION_KEY_PATTERN, RESERVED_KEY_PREFIXES, compareDiagnostics, compareSourceSpans, definitionId, isNotationKey, localDefinitionId, pageScopeLessonIds, reservedKeyPrefix, resolvePageReferenceId, resolveReferenceId, sharedDefinitionId };
|
|
@@ -23,6 +23,17 @@ function resolveReferenceId(key, scope, availableDefinitionIds) {
|
|
|
23
23
|
const sharedId = sharedDefinitionId(key);
|
|
24
24
|
return availableDefinitionIds.has(sharedId) ? sharedId : void 0;
|
|
25
25
|
}
|
|
26
|
+
function resolvePageReferenceId(key, lessonIds, availableDefinitionIds) {
|
|
27
|
+
for (const lessonId of lessonIds) {
|
|
28
|
+
const localId = localDefinitionId(lessonId, key);
|
|
29
|
+
if (availableDefinitionIds.has(localId)) return localId;
|
|
30
|
+
}
|
|
31
|
+
const sharedId = sharedDefinitionId(key);
|
|
32
|
+
return availableDefinitionIds.has(sharedId) ? sharedId : void 0;
|
|
33
|
+
}
|
|
34
|
+
function pageScopeLessonIds(lesson) {
|
|
35
|
+
return lesson.scopeLessonIds ?? [lesson.lessonId];
|
|
36
|
+
}
|
|
26
37
|
function compareSourceSpans(left, right) {
|
|
27
38
|
if (left === void 0) return right === void 0 ? 0 : 1;
|
|
28
39
|
if (right === void 0) return -1;
|
|
@@ -43,7 +54,9 @@ export {
|
|
|
43
54
|
definitionId,
|
|
44
55
|
isNotationKey,
|
|
45
56
|
localDefinitionId,
|
|
57
|
+
pageScopeLessonIds,
|
|
46
58
|
reservedKeyPrefix,
|
|
59
|
+
resolvePageReferenceId,
|
|
47
60
|
resolveReferenceId,
|
|
48
61
|
sharedDefinitionId
|
|
49
62
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The glyph table a notation entry's Markdown body resolves against, and the
|
|
3
|
+
* advice for a symbol it does not bind. The remark pass renders an entry's
|
|
4
|
+
* body with this table (`remark-notation.mjs`) and `validate:content` checks
|
|
5
|
+
* the same body with it (`gate-math.ts`), so the fast gate and the build read
|
|
6
|
+
* one table and cannot disagree on what binds.
|
|
7
|
+
*
|
|
8
|
+
* The table is every shared entry. An entry's body has no `notation.local`,
|
|
9
|
+
* names its neighbours without `[[key]]`, suspends no decoration (rule N22)
|
|
10
|
+
* and derives no cards (rule N23, a page's own): resolve it with
|
|
11
|
+
* `{ suspended: [], derive: false }`.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* One row per shared definition. `fixedIndex` is kept (rule N23): an entry
|
|
15
|
+
* whose index is a fixed label is no family, in an entry's body as on a page.
|
|
16
|
+
*
|
|
17
|
+
* @param {Iterable<{ readonly key: string; readonly notation: string; readonly fixedIndex?: boolean }>} definitions
|
|
18
|
+
* @returns {{ key: string; notation: string; fixedIndex?: true }[]}
|
|
19
|
+
*/
|
|
20
|
+
export function registryGlyphScope(definitions: Iterable<{
|
|
21
|
+
readonly key: string;
|
|
22
|
+
readonly notation: string;
|
|
23
|
+
readonly fixedIndex?: boolean;
|
|
24
|
+
}>): {
|
|
25
|
+
key: string;
|
|
26
|
+
notation: string;
|
|
27
|
+
fixedIndex?: true;
|
|
28
|
+
}[];
|
|
29
|
+
/**
|
|
30
|
+
* Every shared entry is already in scope, so neither `[[key]]` nor
|
|
31
|
+
* `notation.local` can bind a symbol the table missed.
|
|
32
|
+
*/
|
|
33
|
+
export const REGISTRY_BODY_FIX: "A notation entry\u2019s body binds every shared entry and nothing else: give the symbol an entry of its own in content/notation/, or wrap it in \\explain{key}{latex} with the entry it stands for.";
|