explico 0.8.0 → 0.11.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 +1239 -1
- package/README.md +60 -15
- package/dist/compiler/astro.d.ts +6 -1
- package/dist/compiler/cli.d.ts +46 -2
- package/dist/compiler/cli.js +70 -4
- package/dist/compiler/collections.d.ts +35 -2
- package/dist/compiler/collections.js +68 -5
- package/dist/compiler/course-config.d.ts +3 -194
- package/dist/compiler/course-config.js +78 -5
- package/dist/compiler/exports/standalone-html.d.ts +6 -1
- package/dist/compiler/knowledge-version.js +1 -1
- package/dist/compiler/lesson-derivation.d.ts +27 -10
- package/dist/compiler/lesson-derivation.js +23 -8
- package/dist/compiler/logging.d.ts +4 -1
- package/dist/compiler/logging.js +4 -1
- package/dist/compiler/manifest.d.ts +204 -31
- package/dist/compiler/manifest.js +270 -55
- package/dist/compiler/resolution-report.d.ts +6 -1
- package/dist/course-config-Dsnri9Tk.d.ts +464 -0
- package/dist/reference/base-accents.d.mts +26 -0
- package/dist/reference/base-accents.mjs +31 -0
- package/dist/reference/base-roman-labels.d.mts +2 -2
- package/dist/reference/base-roman-labels.mjs +2 -2
- package/dist/reference/consistency.d.ts +14 -2
- package/dist/reference/consistency.js +108 -4
- package/dist/reference/delimiter-size.d.ts +3 -2
- package/dist/reference/diagrams-validate.d.ts +7 -2
- package/dist/reference/diagrams-validate.js +9 -1
- package/dist/reference/equations-validate.d.ts +8 -5
- package/dist/reference/equations-validate.js +1 -0
- package/dist/reference/equations.d.mts +60 -31
- package/dist/reference/equations.mjs +83 -43
- package/dist/reference/examples-validate.d.ts +31 -0
- package/dist/reference/examples-validate.js +48 -0
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/figures-validate.js +4 -1
- package/dist/reference/folds-validate.d.ts +33 -0
- package/dist/reference/folds-validate.js +32 -0
- package/dist/reference/gate-abbreviations.d.ts +3 -1
- package/dist/reference/gate-abbreviations.js +1 -0
- package/dist/reference/gate-course-rules.d.ts +32 -0
- package/dist/reference/gate-course-rules.js +85 -0
- package/dist/reference/gate-heading-length.d.ts +5 -2
- package/dist/reference/gate-heading-length.js +45 -21
- package/dist/reference/gate-heading-structure.d.ts +41 -0
- package/dist/reference/gate-heading-structure.js +113 -0
- package/dist/reference/gate-math-punctuation.d.ts +71 -0
- package/dist/reference/gate-math-punctuation.js +251 -0
- package/dist/reference/gate-math.d.ts +22 -6
- package/dist/reference/gate-math.js +42 -8
- package/dist/reference/gate-part-pages.d.ts +50 -0
- package/dist/reference/gate-part-pages.js +118 -0
- package/dist/reference/gate-prose-notation.d.ts +21 -11
- package/dist/reference/gate-prose-notation.js +25 -7
- package/dist/reference/katex-delimiters.mjs +20 -7
- package/dist/reference/keyed-float-validate.d.ts +27 -10
- package/dist/reference/keyed-float-validate.js +15 -1
- package/dist/reference/keyed-jsx-floats.d.mts +11 -5
- package/dist/reference/keyed-jsx-floats.mjs +73 -20
- package/dist/reference/knowledge-checks-validate.d.ts +1 -1
- package/dist/reference/knowledge-checks.d.mts +3 -1
- package/dist/reference/knowledge-checks.mjs +9 -4
- package/dist/reference/lab-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-slot.d.ts +34 -0
- package/dist/reference/lab-math-slot.js +6 -0
- package/dist/reference/math-glyphs.d.mts +80 -4
- package/dist/reference/math-glyphs.mjs +572 -37
- package/dist/reference/page-notation.d.ts +6 -1
- package/dist/reference/raw-floats-validate.js +1 -1
- package/dist/reference/raw-floats.mjs +1 -1
- package/dist/reference/rehype-heading-anchors.d.mts +14 -0
- package/dist/reference/rehype-heading-anchors.mjs +116 -17
- package/dist/reference/rehype-table-header-labels.d.mts +6 -1
- package/dist/reference/rehype-table-header-labels.mjs +21 -3
- package/dist/reference/remark-asides.d.mts +3 -0
- package/dist/reference/remark-asides.mjs +180 -0
- package/dist/reference/remark-memo.d.mts +3 -1
- package/dist/reference/remark-memo.mjs +42 -11
- package/dist/reference/remark-notation.d.mts +60 -10
- package/dist/reference/remark-notation.mjs +387 -36
- package/dist/reference/render-lab-math.d.ts +4 -26
- package/dist/reference/render-lab-math.js +4 -4
- package/dist/reference/sections-validate.d.ts +27 -0
- package/dist/reference/sections-validate.js +41 -0
- package/dist/reference/sections.d.mts +97 -0
- package/dist/reference/sections.mjs +306 -0
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/types.d.ts +17 -1
- package/dist/reference/unit-words.d.ts +37 -0
- package/dist/reference/unit-words.js +170 -0
- package/dist/rules/catalog.d.ts +176 -0
- package/dist/rules/catalog.js +983 -0
- package/dist/rules/course-rules.d.ts +115 -0
- package/dist/rules/course-rules.js +292 -0
- package/dist/rules/course-rules.schema.json +138 -0
- package/dist/rules/in-effect.d.ts +53 -0
- package/dist/rules/in-effect.js +142 -0
- package/dist/rules/rules.schema.json +1072 -0
- package/dist/rules/settings.d.ts +3 -0
- package/dist/rules/settings.js +290 -0
- package/dist/rules/waivers.d.ts +53 -0
- package/dist/rules/waivers.js +132 -0
- package/dist/session/PreferenceStore.d.ts +5 -0
- package/dist/session/PreferenceStore.js +9 -0
- package/docs/VERSIONING.md +2 -2
- package/docs/authoring/README.md +232 -40
- package/docs/authoring/agents.md +55 -9
- package/docs/authoring/components.md +178 -22
- package/docs/authoring/migrating.md +1096 -2
- package/docs/authoring/notation.md +576 -322
- package/docs/authoring/prose.md +551 -0
- package/docs/authoring/retired.md +35 -0
- package/docs/authoring/structure-rules.md +422 -40
- package/docs/authoring/structure.md +445 -62
- package/docs/authoring/versioning.md +20 -28
- package/docs/authoring/visualizations.md +142 -20
- package/package.json +2 -2
- package/src/compiler/cli.ts +116 -6
- package/src/compiler/collections.ts +111 -7
- package/src/compiler/course-config.ts +273 -34
- package/src/compiler/knowledge-version.ts +1 -1
- package/src/compiler/lesson-derivation.ts +54 -15
- package/src/compiler/logging.ts +8 -2
- package/src/compiler/manifest.ts +573 -104
- package/src/components/BlockHeading.astro +113 -0
- package/src/components/CourseContents.astro +221 -0
- package/src/components/CurriculumMap.astro +5 -6
- package/src/components/FloatIndex.astro +200 -0
- package/src/components/HoverPanel.astro +431 -79
- package/src/components/PartBridge.astro +27 -0
- package/src/components/PartOverview.astro +401 -0
- package/src/components/PartScope.astro +27 -0
- package/src/components/abbreviation/AbbreviationLayer.astro +49 -51
- package/src/components/assessments/AssessmentRunner.tsx +8 -5
- package/src/components/assessments/AssessmentSet.astro +23 -26
- package/src/components/block-heading.ts +146 -0
- package/src/components/card-arrow.ts +243 -0
- package/src/components/card-drag.ts +631 -0
- package/src/components/card-jump.ts +45 -0
- package/src/components/card-manager.ts +1589 -0
- package/src/components/card-memory.ts +177 -0
- package/src/components/card-placement.ts +504 -0
- package/src/components/card-side.ts +336 -0
- package/src/components/card-travel.ts +197 -0
- package/src/components/citation/CitationLayer.astro +77 -76
- package/src/components/diagram/Diagram.astro +161 -316
- package/src/components/disclosure/course-disclosure.ts +24 -0
- package/src/components/disclosure/hover-preview.ts +122 -42
- package/src/components/disclosure/persisted-disclosure.ts +3 -3
- package/src/components/disclosure/print-as-shown.ts +252 -0
- package/src/components/examples/CompactExample.astro +3 -1
- package/src/components/examples/CompactExamples.astro +101 -94
- package/src/components/figure/Figure.astro +174 -274
- package/src/components/figure/FloatCaption.astro +87 -0
- package/src/components/figure/figure-state.ts +58 -0
- package/src/components/figure/figure-tools.ts +87 -0
- package/src/components/float-name.ts +101 -0
- package/src/components/fold/Fold.astro +125 -0
- package/src/components/fold/fold-id.ts +40 -0
- package/src/components/memo/Memo.astro +162 -231
- package/src/components/memo/ShortNote.astro +6 -6
- package/src/components/notation/NotationFormula.astro +5 -1
- package/src/components/notation/NotationGlossary.astro +3 -4
- package/src/components/notation/NotationLayer.astro +169 -162
- package/src/components/notation/NotationMath.astro +10 -1
- package/src/components/reference/FloatRefLayer.astro +485 -423
- package/src/components/starlight/AboutPanel.astro +5 -4
- package/src/components/starlight/CollapsibleSections.astro +207 -93
- package/src/components/starlight/DiagramEnhancer.astro +15 -138
- package/src/components/starlight/HeadingAnchorEnhancer.astro +193 -1
- package/src/components/starlight/KeyedFloatEnhancer.astro +100 -9
- package/src/components/starlight/LayoutHeader.astro +298 -19
- package/src/components/starlight/LayoutPageSidebar.astro +99 -12
- package/src/components/starlight/LayoutPageTitle.astro +120 -0
- package/src/components/starlight/LayoutSidebar.astro +229 -11
- package/src/components/starlight/LessonExportActions.astro +53 -72
- package/src/components/starlight/LessonFooter.astro +26 -4
- package/src/components/starlight/section-parts.ts +15 -2
- package/src/components/starlight/section-summary.ts +181 -0
- package/src/components/table/Table.astro +95 -76
- package/src/components/table/TableOverflowExpander.astro +65 -28
- package/src/content-config.ts +40 -4
- package/src/reference/base-accents.mjs +31 -0
- package/src/reference/base-roman-labels.mjs +2 -2
- package/src/reference/consistency.ts +176 -5
- package/src/reference/delimiter-size.ts +3 -2
- package/src/reference/diagrams-validate.ts +17 -1
- package/src/reference/equations-validate.ts +10 -5
- package/src/reference/equations.mjs +83 -43
- package/src/reference/examples-validate.ts +97 -0
- package/src/reference/figures-validate.ts +6 -1
- package/src/reference/folds-validate.ts +65 -0
- package/src/reference/gate-abbreviations.ts +1 -0
- package/src/reference/gate-course-rules.ts +134 -0
- package/src/reference/gate-heading-length.ts +66 -35
- package/src/reference/gate-heading-structure.ts +193 -0
- package/src/reference/gate-math-punctuation.ts +381 -0
- package/src/reference/gate-math.ts +69 -10
- package/src/reference/gate-part-pages.ts +217 -0
- package/src/reference/gate-prose-notation.ts +59 -12
- package/src/reference/katex-delimiters.mjs +20 -7
- package/src/reference/keyed-float-validate.ts +45 -11
- package/src/reference/keyed-jsx-floats.mjs +73 -20
- package/src/reference/knowledge-checks-validate.ts +1 -1
- package/src/reference/knowledge-checks.mjs +9 -4
- package/src/reference/lab-math-slot.ts +36 -0
- package/src/reference/math-glyphs.mjs +572 -37
- package/src/reference/raw-floats-validate.ts +1 -1
- package/src/reference/raw-floats.mjs +1 -1
- package/src/reference/rehype-heading-anchors.mjs +116 -17
- package/src/reference/rehype-table-header-labels.mjs +21 -3
- package/src/reference/remark-asides.mjs +180 -0
- package/src/reference/remark-memo.mjs +42 -11
- package/src/reference/remark-notation.mjs +387 -36
- package/src/reference/render-lab-math.ts +16 -31
- package/src/reference/sections-validate.ts +81 -0
- package/src/reference/sections.mjs +306 -0
- package/src/reference/tables-validate.ts +2 -1
- package/src/reference/types.ts +17 -0
- package/src/reference/unit-words.ts +233 -0
- package/src/rules/catalog.ts +1298 -0
- package/src/rules/course-rules.ts +456 -0
- package/src/rules/in-effect.ts +235 -0
- package/src/rules/settings.ts +448 -0
- package/src/rules/waivers.ts +249 -0
- package/src/session/PreferenceStore.ts +15 -0
- package/src/styles/global.css +1471 -163
- package/src/styles/surfaces.css +182 -0
- package/src/components/hover-panel.ts +0 -339
- package/src/components/popup-stack.ts +0 -345
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
import { effectiveAccentAllowlist } from "./base-accents.mjs";
|
|
1
2
|
import { effectiveOperatorAllowlist } from "./base-operators.mjs";
|
|
2
3
|
import { effectiveRomanLabelAllowlist } from "./base-roman-labels.mjs";
|
|
3
|
-
import {
|
|
4
|
+
import { stripEquationLabels } from "./equations.mjs";
|
|
5
|
+
import { pageScope } from "./gate-math.js";
|
|
6
|
+
import {
|
|
7
|
+
collectDecorations,
|
|
8
|
+
collectOperators,
|
|
9
|
+
collectRomanLabels,
|
|
10
|
+
decorationOfEntry,
|
|
11
|
+
operatorEntryLatex,
|
|
12
|
+
resolveMathGlyphs
|
|
13
|
+
} from "./math-glyphs.mjs";
|
|
4
14
|
import { offVocabularyTokens } from "./units-vocab.js";
|
|
5
15
|
const CONVENTION_RE = /\bsign convention\b|\bcash-flow perspective\b|\bholder(?:'s)?(?:\s+\w+){0,2}\s+perspective\b|\bfrom (?:a|one|the|its)(?:\s+\w+){0,3}\s+perspective\b|\breverses? every sign\b|\bpositive (?:means|amounts?)\b/i;
|
|
6
16
|
function stripCode(text) {
|
|
@@ -10,7 +20,7 @@ function mathSpans(body) {
|
|
|
10
20
|
const searchable = stripCode(body);
|
|
11
21
|
const spans = [];
|
|
12
22
|
for (const match of searchable.matchAll(/\$\$([\s\S]+?)\$\$/g)) {
|
|
13
|
-
spans.push({ latex: match[1] ?? "" });
|
|
23
|
+
spans.push({ latex: stripEquationLabels(match[1] ?? "") });
|
|
14
24
|
}
|
|
15
25
|
for (const match of searchable.replace(/\$\$([\s\S]+?)\$\$/g, (block) => block.replace(/[^\n]/g, " ")).matchAll(/(?<![\\$])\$(?!\s)([^$\n]+?)(?<![\\\s])\$(?!\d)/g)) {
|
|
16
26
|
spans.push({ latex: match[1] ?? "" });
|
|
@@ -335,6 +345,94 @@ function checkRomanLabelExplained(input, out) {
|
|
|
335
345
|
}
|
|
336
346
|
}
|
|
337
347
|
}
|
|
348
|
+
function sharedScopeByKey(input) {
|
|
349
|
+
return new Map(
|
|
350
|
+
input.notationInput.sharedDefinitions.map((definition) => [
|
|
351
|
+
definition.key,
|
|
352
|
+
{ key: definition.key, notation: definition.notation }
|
|
353
|
+
])
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
function checkAccentExplained(input, out) {
|
|
357
|
+
const allowlist = effectiveAccentAllowlist(input.accents);
|
|
358
|
+
const sharedByKey = sharedScopeByKey(input);
|
|
359
|
+
for (const lesson of input.notationInput.lessons) {
|
|
360
|
+
const scope = pageScope(lesson, sharedByKey);
|
|
361
|
+
const suspended = (lesson.suspended ?? []).map((item) => item.operator);
|
|
362
|
+
const reported = /* @__PURE__ */ new Set();
|
|
363
|
+
for (const span of mathSpans(lesson.body)) {
|
|
364
|
+
let decorations;
|
|
365
|
+
try {
|
|
366
|
+
({ decorations } = resolveMathGlyphs(span.latex, scope, { suspended }));
|
|
367
|
+
} catch {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
for (const decoration of decorations) {
|
|
371
|
+
if (decoration.wholeForm !== null || decoration.operator !== null) continue;
|
|
372
|
+
if (suspended.includes(decoration.name)) continue;
|
|
373
|
+
if (allowlist.has(decoration.name)) continue;
|
|
374
|
+
if (reported.has(decoration.name)) continue;
|
|
375
|
+
reported.add(decoration.name);
|
|
376
|
+
if (input.lintIgnore.matches("accent-explained", decoration.name)) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
out.push({
|
|
380
|
+
code: "accent-explained",
|
|
381
|
+
severity: "warning",
|
|
382
|
+
lessonId: lesson.lessonId,
|
|
383
|
+
file: lesson.source.file,
|
|
384
|
+
glyph: decoration.name,
|
|
385
|
+
message: `${lesson.lessonId}: decoration ${JSON.stringify(decoration.name)} (in ${JSON.stringify(decoration.token)}) is an operator that is neither universal for this course nor explained; give it an operator entry (latex ${JSON.stringify(operatorEntryLatex(decoration.name))}) introduced with [[key]] or in notation.local, give the whole form its own entry (latex ${JSON.stringify(decoration.token)}), or add ${JSON.stringify(decoration.name)} to rules.N22.allow`
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function checkOperatorSuspension(input, out) {
|
|
392
|
+
const allowlist = effectiveAccentAllowlist(input.accents);
|
|
393
|
+
const sharedByKey = sharedScopeByKey(input);
|
|
394
|
+
for (const lesson of input.notationInput.lessons) {
|
|
395
|
+
const suspensions = lesson.suspended ?? [];
|
|
396
|
+
for (const { operator, reason } of suspensions) {
|
|
397
|
+
const code = reason === void 0 ? "operator-suspension-reason" : "operator-suspended";
|
|
398
|
+
if (input.lintIgnore.matches(code, operator)) continue;
|
|
399
|
+
out.push({
|
|
400
|
+
code,
|
|
401
|
+
severity: reason === void 0 ? "warning" : "info",
|
|
402
|
+
lessonId: lesson.lessonId,
|
|
403
|
+
file: lesson.source.file,
|
|
404
|
+
glyph: operator,
|
|
405
|
+
message: reason === void 0 ? `${lesson.lessonId}: notation.suspend suspends ${JSON.stringify(operator)} with no reason; say why it is not an operator on this page (reason: \u2026)` : `${lesson.lessonId}: ${JSON.stringify(operator)} is not an operator on this page (notation.suspend): ${reason}`
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
const suspended = new Set(suspensions.map((item) => item.operator));
|
|
409
|
+
const scope = pageScope(lesson, sharedByKey);
|
|
410
|
+
const explained = new Set(
|
|
411
|
+
scope.map((entry) => decorationOfEntry(entry.notation)).filter((name) => name !== void 0)
|
|
412
|
+
);
|
|
413
|
+
for (const entry of scope) {
|
|
414
|
+
if (decorationOfEntry(entry.notation) !== void 0) continue;
|
|
415
|
+
const names = new Set(
|
|
416
|
+
collectDecorations(entry.notation).map((decoration) => decoration.name)
|
|
417
|
+
);
|
|
418
|
+
for (const name of names) {
|
|
419
|
+
if (suspended.has(name)) continue;
|
|
420
|
+
const how = explained.has(name) ? "explained by an operator entry" : allowlist.has(name) ? "universal for this course" : void 0;
|
|
421
|
+
if (how === void 0) continue;
|
|
422
|
+
if (input.lintIgnore.matches("operator-shadowed", entry.key)) continue;
|
|
423
|
+
out.push({
|
|
424
|
+
code: "operator-shadowed",
|
|
425
|
+
severity: "info",
|
|
426
|
+
lessonId: lesson.lessonId,
|
|
427
|
+
file: lesson.source.file,
|
|
428
|
+
key: entry.key,
|
|
429
|
+
glyph: name,
|
|
430
|
+
message: `${lesson.lessonId}: ${entry.key} (${entry.notation}) has its own entry, so its card wins over the ${JSON.stringify(name)} operator (${how}) for that form; if ${JSON.stringify(name)} never means the operator on this page, suspend it (notation.suspend)`
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
338
436
|
const CHECKS = [
|
|
339
437
|
checkGlyphUniqueness,
|
|
340
438
|
checkNotationUnits,
|
|
@@ -345,7 +443,9 @@ const CHECKS = [
|
|
|
345
443
|
checkCardWantsDemoting,
|
|
346
444
|
checkGlossNameShape,
|
|
347
445
|
checkOperatorExplained,
|
|
348
|
-
checkRomanLabelExplained
|
|
446
|
+
checkRomanLabelExplained,
|
|
447
|
+
checkAccentExplained,
|
|
448
|
+
checkOperatorSuspension
|
|
349
449
|
];
|
|
350
450
|
function runConsistencyChecks(input) {
|
|
351
451
|
const diagnostics = [];
|
|
@@ -366,7 +466,11 @@ function consistencyCounts(diagnostics) {
|
|
|
366
466
|
"card-wants-demoting": 0,
|
|
367
467
|
"gloss-name-shape": 0,
|
|
368
468
|
"operator-explained": 0,
|
|
369
|
-
"roman-label-explained": 0
|
|
469
|
+
"roman-label-explained": 0,
|
|
470
|
+
"accent-explained": 0,
|
|
471
|
+
"operator-suspended": 0,
|
|
472
|
+
"operator-suspension-reason": 0,
|
|
473
|
+
"operator-shadowed": 0
|
|
370
474
|
};
|
|
371
475
|
for (const diagnostic of diagnostics) counts[diagnostic.code] += 1;
|
|
372
476
|
return counts;
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* This deliberately parses only sizing syntax. It does not decide whether a
|
|
11
11
|
* parenthesis, bracket, or brace is semantic, and it never recommends changing
|
|
12
|
-
* one delimiter shape into another
|
|
13
|
-
*
|
|
12
|
+
* one delimiter shape into another — N17's other half, that a delimiter's shape
|
|
13
|
+
* is fixed by its meaning and never alternates to show depth, stays a
|
|
14
|
+
* review-only authoring concern (which is why N17 is `partial`).
|
|
14
15
|
*/
|
|
15
16
|
declare const DELIMITER_SIZE_DIAGNOSTIC_CODE: "delimiter-manual-size";
|
|
16
17
|
type DelimiterSizeName = 'automatic' | 'big' | 'Big' | 'bigg' | 'Bigg';
|
|
@@ -2,7 +2,7 @@ import { KeyedFloatLabelRecord, KeyedFloatLessonInput, KeyedFloatValidation } fr
|
|
|
2
2
|
|
|
3
3
|
/** `validateKeyedFloats` for `<Diagram>` — see `keyed-float-validate.ts`. */
|
|
4
4
|
|
|
5
|
-
type DiagramDiagnosticCode = 'dia-invalid-key' | 'dia-duplicate-key' | 'dia-ref-resolves' | 'dia-key-unused';
|
|
5
|
+
type DiagramDiagnosticCode = 'dia-invalid-key' | 'dia-duplicate-key' | 'dia-ref-resolves' | 'dia-key-unused' | 'dia-removed-prop';
|
|
6
6
|
interface DiagramDiagnostic {
|
|
7
7
|
readonly code: DiagramDiagnosticCode;
|
|
8
8
|
readonly severity: 'error' | 'warning' | 'info';
|
|
@@ -16,6 +16,11 @@ type DiagramLessonInput = KeyedFloatLessonInput;
|
|
|
16
16
|
interface DiagramValidation extends KeyedFloatValidation {
|
|
17
17
|
readonly diagnostics: readonly DiagramDiagnostic[];
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* The fix for a compaction prop: a diagram is always its graph and then its
|
|
21
|
+
* caption line (docs/plans/figures-and-short-captions.md, decision 7).
|
|
22
|
+
*/
|
|
23
|
+
declare const DIAGRAM_NO_LONGER_FOLDS = "a diagram no longer folds by itself \u2014 delete the prop. To let a reader fold it away, wrap it in a `<Fold>` or put it in a collapsible section.";
|
|
19
24
|
declare function validateDiagrams(lessons: readonly DiagramLessonInput[]): DiagramValidation;
|
|
20
25
|
|
|
21
|
-
export { type DiagramDiagnostic, type DiagramDiagnosticCode, type DiagramLabelRecord, type DiagramLessonInput, type DiagramValidation, validateDiagrams };
|
|
26
|
+
export { DIAGRAM_NO_LONGER_FOLDS, type DiagramDiagnostic, type DiagramDiagnosticCode, type DiagramLabelRecord, type DiagramLessonInput, type DiagramValidation, validateDiagrams };
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateKeyedFloats
|
|
3
3
|
} from "./keyed-float-validate.js";
|
|
4
|
+
const DIAGRAM_NO_LONGER_FOLDS = "a diagram no longer folds by itself \u2014 delete the prop. To let a reader fold it away, wrap it in a `<Fold>` or put it in a collapsible section.";
|
|
4
5
|
function validateDiagrams(lessons) {
|
|
5
6
|
return validateKeyedFloats(lessons, {
|
|
6
7
|
tagName: "Diagram",
|
|
7
8
|
prefix: "dia",
|
|
8
|
-
noun: "diagram"
|
|
9
|
+
noun: "diagram",
|
|
10
|
+
removedProps: {
|
|
11
|
+
label: "rename it `caption` (shown below the diagram with its number), and add an optional `short` caption if the caption is long.",
|
|
12
|
+
title: "a diagram is named by its `caption` (shown below it with its number) and an optional `short` caption.",
|
|
13
|
+
defaultOpen: DIAGRAM_NO_LONGER_FOLDS,
|
|
14
|
+
rememberState: DIAGRAM_NO_LONGER_FOLDS
|
|
15
|
+
}
|
|
9
16
|
});
|
|
10
17
|
}
|
|
11
18
|
export {
|
|
19
|
+
DIAGRAM_NO_LONGER_FOLDS,
|
|
12
20
|
validateDiagrams
|
|
13
21
|
};
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
interface EquationLabelRecord {
|
|
13
13
|
readonly key: string;
|
|
14
14
|
readonly number: string;
|
|
15
|
+
/** The equation's `\caption{…}` short caption, when it has one. */
|
|
16
|
+
readonly short?: string;
|
|
15
17
|
readonly lessonId: string;
|
|
16
18
|
readonly slug: string;
|
|
17
19
|
readonly section: number;
|
|
@@ -45,13 +47,14 @@ interface EquationLessonInput {
|
|
|
45
47
|
readonly file: string;
|
|
46
48
|
readonly body: string;
|
|
47
49
|
/**
|
|
48
|
-
* `{ prefix, offset }` — the
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* legacy `##`-section
|
|
50
|
+
* `{ prefix, offset }` — the lesson's float-number prefix (`‹part›.‹chapter›`,
|
|
51
|
+
* or `‹part›` under `numbering.floats: "part"`) and how many display
|
|
52
|
+
* equations precede the lesson in that scope. When set, equations are
|
|
53
|
+
* numbered `‹prefix›.‹offset + n›`; when omitted, the legacy `##`-section
|
|
54
|
+
* scheme. See `docs/authoring/structure.md`.
|
|
52
55
|
*/
|
|
53
56
|
readonly numbering?: {
|
|
54
|
-
readonly prefix: number;
|
|
57
|
+
readonly prefix: number | string;
|
|
55
58
|
readonly offset: number;
|
|
56
59
|
};
|
|
57
60
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The short caption a display equation carries, trimmed, or `null` when it
|
|
3
|
+
* has none. Only the first `\caption{}` counts; `remark-notation.mjs` fails a
|
|
4
|
+
* page that gives one equation two, or an empty one.
|
|
5
|
+
*/
|
|
6
|
+
export function equationCaption(latex: any): string | null;
|
|
1
7
|
/** `{ lesson?, key }` when `raw` is an equation reference, else `undefined`. */
|
|
2
8
|
export function parseEquationRef(raw: any): {
|
|
3
9
|
lesson: string;
|
|
@@ -6,27 +12,31 @@ export function parseEquationRef(raw: any): {
|
|
|
6
12
|
key: string;
|
|
7
13
|
lesson?: undefined;
|
|
8
14
|
} | undefined;
|
|
9
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Remove every `\label{eq:…}` and `\caption{…}` (and the whitespace they
|
|
17
|
+
* leave) from a math source: both are identity markers, not math KaTeX parses.
|
|
18
|
+
*/
|
|
10
19
|
export function stripEquationLabels(latex: any): string;
|
|
11
20
|
/**
|
|
12
21
|
* Normalise the numbering input to `{ prefix, offset }`.
|
|
13
22
|
*
|
|
14
|
-
* `prefix` is
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* legacy per-lesson form (`prefix = n`,
|
|
19
|
-
* hand-built trees and unit tests keep
|
|
20
|
-
* `##`-section fallback for
|
|
23
|
+
* `prefix` is everything before the last dot — `‹part›.‹chapter›` under the
|
|
24
|
+
* default `numbering.floats: "chapter"`, the part's ordinal under `"part"`.
|
|
25
|
+
* `offset` is how many items of the kind precede the lesson in that scope: 0
|
|
26
|
+
* for a chapter, the earlier chapters' count for a part, so `n` runs on
|
|
27
|
+
* across it. A bare number is the legacy per-lesson form (`prefix = n`,
|
|
28
|
+
* `offset = 0`) and is still accepted so hand-built trees and unit tests keep
|
|
29
|
+
* working. `undefined`/`null`/`0`/`''` ⇒ the `##`-section fallback for
|
|
30
|
+
* non-lesson pages.
|
|
21
31
|
*/
|
|
22
32
|
export function resolveNumbering(input: any): {
|
|
23
33
|
prefix: any;
|
|
24
34
|
offset: number;
|
|
25
35
|
};
|
|
26
36
|
/**
|
|
27
|
-
* The visible number for one display equation. With a `prefix`
|
|
28
|
-
*
|
|
29
|
-
*
|
|
37
|
+
* The visible number for one display equation. With a `prefix` the scheme is
|
|
38
|
+
* `‹prefix›.‹offset + indexInLesson›` and `##` headings are ignored; without
|
|
39
|
+
* it, the legacy `##`-section scheme.
|
|
30
40
|
*/
|
|
31
41
|
export function displayEquationNumber({ prefix, offset, section, indexInSection, indexInLesson, }: {
|
|
32
42
|
prefix: any;
|
|
@@ -59,20 +69,20 @@ export function stripFencedCode(markdown: any): string;
|
|
|
59
69
|
* component while still finding `<Table>`/`<Figure>` tags at the top level.
|
|
60
70
|
*
|
|
61
71
|
* `transparent`, when given, removes only those components' opening/closing
|
|
62
|
-
* tags and exposes their body to the later blanking pass.
|
|
63
|
-
*
|
|
64
|
-
* wrapper, their bodies are details in the lesson's reading order, so numbered
|
|
65
|
-
* equations/floats in them stay in the official sequence. Section headings in
|
|
72
|
+
* tags and exposes their body to the later blanking pass. The
|
|
73
|
+
* {@link TRANSPARENT_COMPONENTS} are the intentional uses. Section headings in
|
|
66
74
|
* a transparent body do not change the surrounding page's section counter.
|
|
67
75
|
*/
|
|
68
76
|
export function stripJsxComponents(markdown: any, keep: any, transparent?: any[]): string;
|
|
69
77
|
/**
|
|
70
78
|
* Every display equation in a Markdown body, keyed or not, in appearance order
|
|
71
|
-
* with its assigned number
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
79
|
+
* with its assigned number and its `\caption{}` short caption (`short`, or
|
|
80
|
+
* `null`). `$$…$$` blocks only. With `numbering`
|
|
81
|
+
* (`{ prefix, offset }`, the lesson's float-number prefix and the count of
|
|
82
|
+
* display equations before it in that scope) the number is
|
|
83
|
+
* `‹prefix›.‹offset + n›`, `n` sequential over every display block in the
|
|
84
|
+
* lesson; without it, `##` headings advance a section counter and the number
|
|
85
|
+
* is `‹section›.‹indexInSection›`.
|
|
76
86
|
* Either way every display block advances the index, so a keyed equation's
|
|
77
87
|
* number reflects its position among *all* display equations, not only the
|
|
78
88
|
* keyed ones.
|
|
@@ -80,6 +90,7 @@ export function stripJsxComponents(markdown: any, keep: any, transparent?: any[]
|
|
|
80
90
|
export function scanDisplayEquations(markdown: any, numbering: any): {
|
|
81
91
|
key: string | null | undefined;
|
|
82
92
|
number: string;
|
|
93
|
+
short: string | null | undefined;
|
|
83
94
|
section: number;
|
|
84
95
|
indexInSection: number;
|
|
85
96
|
}[];
|
|
@@ -87,6 +98,7 @@ export function scanDisplayEquations(markdown: any, numbering: any): {
|
|
|
87
98
|
export function scanEquationLabels(markdown: any, numbering: any): {
|
|
88
99
|
key: string | null | undefined;
|
|
89
100
|
number: string;
|
|
101
|
+
short: string | null | undefined;
|
|
90
102
|
section: number;
|
|
91
103
|
indexInSection: number;
|
|
92
104
|
}[];
|
|
@@ -115,23 +127,24 @@ export function collectEquationLabels(tree: any, numbering: any): any[];
|
|
|
115
127
|
* Every display equation (`$$…$$`) in a lesson is numbered and given an `id`
|
|
116
128
|
* from appearance order. When the caller supplies `numbering`
|
|
117
129
|
* (`{ prefix, offset }` — see `resolveNumbering`), the number is
|
|
118
|
-
* `‹
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* part
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* trees and non-lesson
|
|
130
|
+
* `‹prefix›.‹offset + n›`: `n` counts every display equation in the lesson,
|
|
131
|
+
* and the course's `numbering.floats` scope decides the rest — `chapter` (the
|
|
132
|
+
* default) makes the prefix `‹part›.‹chapter›` with no offset, so `(2.2.4)` is
|
|
133
|
+
* the fourth display equation of part 2's second chapter; `part` makes it the
|
|
134
|
+
* part's ordinal alone, with `offset` carrying the count from earlier lessons
|
|
135
|
+
* of the part so the sequence runs on across it. `##` and `###` headings never
|
|
136
|
+
* affect it. See `docs/authoring/structure.md` for the level names. With no
|
|
137
|
+
* `numbering` the number falls back to the `##`-section scheme
|
|
138
|
+
* `‹section›.‹indexInSection›` (used by hand-built test trees and non-lesson
|
|
139
|
+
* pages). An author additionally
|
|
127
140
|
* marks an equation with a trailing `\label{eq:<key>}` inside its `$$…$$`
|
|
128
141
|
* block (inside math, so the `:` is safe and the form matches amsmath) when
|
|
129
142
|
* they want a *stable* anchor that survives edits and cross-page references:
|
|
130
143
|
*
|
|
131
|
-
* [[eq-discount-factor-def]] same page → "(2.4)" linked to #eq-…
|
|
144
|
+
* [[eq-discount-factor-def]] same page → "(2.2.4)" linked to #eq-…
|
|
132
145
|
* [[foundations/present-value#eq-pv]] cross page → number from the manifest
|
|
133
146
|
*
|
|
134
|
-
* An unkeyed equation still gets a positional anchor (`#eq-2-4`) so it can be
|
|
147
|
+
* An unkeyed equation still gets a positional anchor (`#eq-2-2-4`) so it can be
|
|
135
148
|
* deep-linked, but only a keyed equation is referenceable by name.
|
|
136
149
|
*
|
|
137
150
|
* `scanEquationLabels` (raw Markdown) and `collectEquationLabels` (mdast tree)
|
|
@@ -144,9 +157,25 @@ export function collectEquationLabels(tree: any, numbering: any): any[];
|
|
|
144
157
|
export const EQ_KEY_PATTERN: RegExp;
|
|
145
158
|
/** A `\label{eq:<key>}` anywhere in a math source. Global; `key` in group 1. */
|
|
146
159
|
export const EQ_LABEL_PATTERN: RegExp;
|
|
160
|
+
/**
|
|
161
|
+
* A `\caption{<short caption>}` anywhere in a math source — the equation's
|
|
162
|
+
* short caption, plain text with no nested braces. Global; the text in group
|
|
163
|
+
* 1. An equation takes only a short caption: it names the equation in the
|
|
164
|
+
* `[[eq-…]]` preview card and in the "Copy LaTeX — ‹caption›" tooltip, and is
|
|
165
|
+
* stripped before KaTeX exactly like `\label{}`.
|
|
166
|
+
*/
|
|
167
|
+
export const EQ_CAPTION_PATTERN: RegExp;
|
|
147
168
|
/**
|
|
148
169
|
* A `[[ … ]]` payload that targets an equation: `eq-<key>` on the current page,
|
|
149
170
|
* or `<lesson-slug>#eq-<key>` across pages. `lesson` (group 1) is a `/`-slug.
|
|
150
171
|
* The `eq-` prefix is reserved — a notation key may not start with it.
|
|
151
172
|
*/
|
|
152
173
|
export const EQ_REF_PATTERN: RegExp;
|
|
174
|
+
/**
|
|
175
|
+
* The components whose bodies stay in the lesson's reading order: `<Memo>`,
|
|
176
|
+
* `<ShortNote>` and `<Fold>`. Unlike a worked-example/presentation wrapper,
|
|
177
|
+
* their bodies are details or folded stretches of the lesson itself, so a
|
|
178
|
+
* numbered equation or float in one stays in the official sequence. Every
|
|
179
|
+
* scan and tree walk that numbers equations or floats reads this list.
|
|
180
|
+
*/
|
|
181
|
+
export const TRANSPARENT_COMPONENTS: readonly string[];
|
|
@@ -4,23 +4,24 @@
|
|
|
4
4
|
* Every display equation (`$$…$$`) in a lesson is numbered and given an `id`
|
|
5
5
|
* from appearance order. When the caller supplies `numbering`
|
|
6
6
|
* (`{ prefix, offset }` — see `resolveNumbering`), the number is
|
|
7
|
-
* `‹
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* part
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* trees and non-lesson
|
|
7
|
+
* `‹prefix›.‹offset + n›`: `n` counts every display equation in the lesson,
|
|
8
|
+
* and the course's `numbering.floats` scope decides the rest — `chapter` (the
|
|
9
|
+
* default) makes the prefix `‹part›.‹chapter›` with no offset, so `(2.2.4)` is
|
|
10
|
+
* the fourth display equation of part 2's second chapter; `part` makes it the
|
|
11
|
+
* part's ordinal alone, with `offset` carrying the count from earlier lessons
|
|
12
|
+
* of the part so the sequence runs on across it. `##` and `###` headings never
|
|
13
|
+
* affect it. See `docs/authoring/structure.md` for the level names. With no
|
|
14
|
+
* `numbering` the number falls back to the `##`-section scheme
|
|
15
|
+
* `‹section›.‹indexInSection›` (used by hand-built test trees and non-lesson
|
|
16
|
+
* pages). An author additionally
|
|
16
17
|
* marks an equation with a trailing `\label{eq:<key>}` inside its `$$…$$`
|
|
17
18
|
* block (inside math, so the `:` is safe and the form matches amsmath) when
|
|
18
19
|
* they want a *stable* anchor that survives edits and cross-page references:
|
|
19
20
|
*
|
|
20
|
-
* [[eq-discount-factor-def]] same page → "(2.4)" linked to #eq-…
|
|
21
|
+
* [[eq-discount-factor-def]] same page → "(2.2.4)" linked to #eq-…
|
|
21
22
|
* [[foundations/present-value#eq-pv]] cross page → number from the manifest
|
|
22
23
|
*
|
|
23
|
-
* An unkeyed equation still gets a positional anchor (`#eq-2-4`) so it can be
|
|
24
|
+
* An unkeyed equation still gets a positional anchor (`#eq-2-2-4`) so it can be
|
|
24
25
|
* deep-linked, but only a keyed equation is referenceable by name.
|
|
25
26
|
*
|
|
26
27
|
* `scanEquationLabels` (raw Markdown) and `collectEquationLabels` (mdast tree)
|
|
@@ -37,6 +38,28 @@ export const EQ_KEY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
37
38
|
export const EQ_LABEL_PATTERN =
|
|
38
39
|
/\\label\{\s*eq:([a-z0-9]+(?:-[a-z0-9]+)*)\s*\}/g;
|
|
39
40
|
|
|
41
|
+
/**
|
|
42
|
+
* A `\caption{<short caption>}` anywhere in a math source — the equation's
|
|
43
|
+
* short caption, plain text with no nested braces. Global; the text in group
|
|
44
|
+
* 1. An equation takes only a short caption: it names the equation in the
|
|
45
|
+
* `[[eq-…]]` preview card and in the "Copy LaTeX — ‹caption›" tooltip, and is
|
|
46
|
+
* stripped before KaTeX exactly like `\label{}`.
|
|
47
|
+
*/
|
|
48
|
+
export const EQ_CAPTION_PATTERN = /\\caption\{([^{}]*)\}/g;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The short caption a display equation carries, trimmed, or `null` when it
|
|
52
|
+
* has none. Only the first `\caption{}` counts; `remark-notation.mjs` fails a
|
|
53
|
+
* page that gives one equation two, or an empty one.
|
|
54
|
+
*/
|
|
55
|
+
export function equationCaption(latex) {
|
|
56
|
+
EQ_CAPTION_PATTERN.lastIndex = 0;
|
|
57
|
+
const match = EQ_CAPTION_PATTERN.exec(String(latex ?? ''));
|
|
58
|
+
EQ_CAPTION_PATTERN.lastIndex = 0;
|
|
59
|
+
const text = match?.[1]?.replace(/\s+/g, ' ').trim();
|
|
60
|
+
return text ? text : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
40
63
|
/**
|
|
41
64
|
* A `[[ … ]]` payload that targets an equation: `eq-<key>` on the current page,
|
|
42
65
|
* or `<lesson-slug>#eq-<key>` across pages. `lesson` (group 1) is a `/`-slug.
|
|
@@ -52,10 +75,14 @@ export function parseEquationRef(raw) {
|
|
|
52
75
|
return match[1] ? { lesson: match[1], key: match[2] } : { key: match[2] };
|
|
53
76
|
}
|
|
54
77
|
|
|
55
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Remove every `\label{eq:…}` and `\caption{…}` (and the whitespace they
|
|
80
|
+
* leave) from a math source: both are identity markers, not math KaTeX parses.
|
|
81
|
+
*/
|
|
56
82
|
export function stripEquationLabels(latex) {
|
|
57
83
|
return String(latex)
|
|
58
84
|
.replace(EQ_LABEL_PATTERN, '')
|
|
85
|
+
.replace(EQ_CAPTION_PATTERN, '')
|
|
59
86
|
.replace(/[ \t]+\n/g, '\n')
|
|
60
87
|
.replace(/\n{3,}/g, '\n\n')
|
|
61
88
|
.replace(/[ \t]{2,}/g, ' ')
|
|
@@ -70,13 +97,14 @@ function equationNumber(section, index) {
|
|
|
70
97
|
/**
|
|
71
98
|
* Normalise the numbering input to `{ prefix, offset }`.
|
|
72
99
|
*
|
|
73
|
-
* `prefix` is
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* legacy per-lesson form (`prefix = n`,
|
|
78
|
-
* hand-built trees and unit tests keep
|
|
79
|
-
* `##`-section fallback for
|
|
100
|
+
* `prefix` is everything before the last dot — `‹part›.‹chapter›` under the
|
|
101
|
+
* default `numbering.floats: "chapter"`, the part's ordinal under `"part"`.
|
|
102
|
+
* `offset` is how many items of the kind precede the lesson in that scope: 0
|
|
103
|
+
* for a chapter, the earlier chapters' count for a part, so `n` runs on
|
|
104
|
+
* across it. A bare number is the legacy per-lesson form (`prefix = n`,
|
|
105
|
+
* `offset = 0`) and is still accepted so hand-built trees and unit tests keep
|
|
106
|
+
* working. `undefined`/`null`/`0`/`''` ⇒ the `##`-section fallback for
|
|
107
|
+
* non-lesson pages.
|
|
80
108
|
*/
|
|
81
109
|
export function resolveNumbering(input) {
|
|
82
110
|
if (input == null) return { prefix: undefined, offset: 0 };
|
|
@@ -90,9 +118,9 @@ export function resolveNumbering(input) {
|
|
|
90
118
|
}
|
|
91
119
|
|
|
92
120
|
/**
|
|
93
|
-
* The visible number for one display equation. With a `prefix`
|
|
94
|
-
*
|
|
95
|
-
*
|
|
121
|
+
* The visible number for one display equation. With a `prefix` the scheme is
|
|
122
|
+
* `‹prefix›.‹offset + indexInLesson›` and `##` headings are ignored; without
|
|
123
|
+
* it, the legacy `##`-section scheme.
|
|
96
124
|
*/
|
|
97
125
|
export function displayEquationNumber({
|
|
98
126
|
prefix,
|
|
@@ -136,10 +164,8 @@ export function stripFencedCode(markdown) {
|
|
|
136
164
|
* component while still finding `<Table>`/`<Figure>` tags at the top level.
|
|
137
165
|
*
|
|
138
166
|
* `transparent`, when given, removes only those components' opening/closing
|
|
139
|
-
* tags and exposes their body to the later blanking pass.
|
|
140
|
-
*
|
|
141
|
-
* wrapper, their bodies are details in the lesson's reading order, so numbered
|
|
142
|
-
* equations/floats in them stay in the official sequence. Section headings in
|
|
167
|
+
* tags and exposes their body to the later blanking pass. The
|
|
168
|
+
* {@link TRANSPARENT_COMPONENTS} are the intentional uses. Section headings in
|
|
143
169
|
* a transparent body do not change the surrounding page's section counter.
|
|
144
170
|
*/
|
|
145
171
|
export function stripJsxComponents(markdown, keep, transparent = []) {
|
|
@@ -169,23 +195,35 @@ export function stripJsxComponents(markdown, keep, transparent = []) {
|
|
|
169
195
|
);
|
|
170
196
|
}
|
|
171
197
|
|
|
198
|
+
/**
|
|
199
|
+
* The components whose bodies stay in the lesson's reading order: `<Memo>`,
|
|
200
|
+
* `<ShortNote>` and `<Fold>`. Unlike a worked-example/presentation wrapper,
|
|
201
|
+
* their bodies are details or folded stretches of the lesson itself, so a
|
|
202
|
+
* numbered equation or float in one stays in the official sequence. Every
|
|
203
|
+
* scan and tree walk that numbers equations or floats reads this list.
|
|
204
|
+
*/
|
|
205
|
+
export const TRANSPARENT_COMPONENTS = Object.freeze(['Memo', 'ShortNote', 'Fold']);
|
|
206
|
+
|
|
172
207
|
/**
|
|
173
208
|
* Every display equation in a Markdown body, keyed or not, in appearance order
|
|
174
|
-
* with its assigned number
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
209
|
+
* with its assigned number and its `\caption{}` short caption (`short`, or
|
|
210
|
+
* `null`). `$$…$$` blocks only. With `numbering`
|
|
211
|
+
* (`{ prefix, offset }`, the lesson's float-number prefix and the count of
|
|
212
|
+
* display equations before it in that scope) the number is
|
|
213
|
+
* `‹prefix›.‹offset + n›`, `n` sequential over every display block in the
|
|
214
|
+
* lesson; without it, `##` headings advance a section counter and the number
|
|
215
|
+
* is `‹section›.‹indexInSection›`.
|
|
179
216
|
* Either way every display block advances the index, so a keyed equation's
|
|
180
217
|
* number reflects its position among *all* display equations, not only the
|
|
181
218
|
* keyed ones.
|
|
182
219
|
*/
|
|
183
220
|
export function scanDisplayEquations(markdown, numbering) {
|
|
184
221
|
const { prefix, offset } = resolveNumbering(numbering);
|
|
185
|
-
const text = stripJsxComponents(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
222
|
+
const text = stripJsxComponents(
|
|
223
|
+
stripFencedCode(markdown),
|
|
224
|
+
undefined,
|
|
225
|
+
TRANSPARENT_COMPONENTS,
|
|
226
|
+
);
|
|
189
227
|
const tokens = [];
|
|
190
228
|
const heading = /^[ \t]{0,3}##[ \t]+\S/gm;
|
|
191
229
|
const display = /\$\$([\s\S]+?)\$\$/g;
|
|
@@ -199,6 +237,7 @@ export function scanDisplayEquations(markdown, numbering) {
|
|
|
199
237
|
kind: 'equation',
|
|
200
238
|
index: match.index,
|
|
201
239
|
key: label ? label[1] : null,
|
|
240
|
+
short: equationCaption(match[1]),
|
|
202
241
|
});
|
|
203
242
|
}
|
|
204
243
|
tokens.sort((a, b) => a.index - b.index);
|
|
@@ -224,6 +263,7 @@ export function scanDisplayEquations(markdown, numbering) {
|
|
|
224
263
|
indexInSection,
|
|
225
264
|
indexInLesson,
|
|
226
265
|
}),
|
|
266
|
+
short: token.short,
|
|
227
267
|
section,
|
|
228
268
|
indexInSection,
|
|
229
269
|
});
|
|
@@ -261,11 +301,11 @@ export function collectDisplayEquations(tree, numbering) {
|
|
|
261
301
|
let indexInSection = 0;
|
|
262
302
|
let indexInLesson = 0;
|
|
263
303
|
|
|
264
|
-
// Top-level blocks plus the bodies of
|
|
265
|
-
// components (a `<CompactExample>` worked example, an `<Aside>`) remain
|
|
266
|
-
// illustrative and outside the lesson's official equation sequence.
|
|
267
|
-
//
|
|
268
|
-
//
|
|
304
|
+
// Top-level blocks plus the bodies of the `TRANSPARENT_COMPONENTS`: other
|
|
305
|
+
// MDX components (a `<CompactExample>` worked example, an `<Aside>`) remain
|
|
306
|
+
// illustrative and outside the lesson's official equation sequence. The
|
|
307
|
+
// transparent ones occupy an exact point in the reading order, even while
|
|
308
|
+
// visually collapsed.
|
|
269
309
|
const collectFromDetail = (node) => {
|
|
270
310
|
if (!node || typeof node !== 'object') return;
|
|
271
311
|
if (node.type === 'math') {
|
|
@@ -275,6 +315,7 @@ export function collectDisplayEquations(tree, numbering) {
|
|
|
275
315
|
const label = EQ_LABEL_PATTERN.exec(String(node.value ?? ''));
|
|
276
316
|
equations.push({
|
|
277
317
|
key: label ? label[1] : null,
|
|
318
|
+
short: equationCaption(node.value),
|
|
278
319
|
number: displayEquationNumber({
|
|
279
320
|
prefix,
|
|
280
321
|
offset,
|
|
@@ -290,8 +331,7 @@ export function collectDisplayEquations(tree, numbering) {
|
|
|
290
331
|
}
|
|
291
332
|
if (
|
|
292
333
|
node.type === 'mdxJsxFlowElement' &&
|
|
293
|
-
node.name
|
|
294
|
-
node.name !== 'ShortNote'
|
|
334
|
+
!TRANSPARENT_COMPONENTS.includes(node.name)
|
|
295
335
|
) {
|
|
296
336
|
return;
|
|
297
337
|
}
|
|
@@ -310,7 +350,7 @@ export function collectDisplayEquations(tree, numbering) {
|
|
|
310
350
|
if (node.type === 'math') collectFromDetail(node);
|
|
311
351
|
else if (
|
|
312
352
|
node.type === 'mdxJsxFlowElement' &&
|
|
313
|
-
(node.name
|
|
353
|
+
TRANSPARENT_COMPONENTS.includes(node.name)
|
|
314
354
|
) {
|
|
315
355
|
collectFromDetail(node);
|
|
316
356
|
}
|