explico 0.11.1 → 0.13.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 +1292 -1
- package/README.md +91 -13
- package/dist/compiler/astro.d.ts +11 -4
- package/dist/compiler/astro.js +48 -15
- package/dist/compiler/cli.d.ts +27 -12
- package/dist/compiler/cli.js +96 -25
- package/dist/compiler/collections.d.ts +33 -2
- package/dist/compiler/collections.js +91 -19
- package/dist/compiler/content-schema.d.ts +31 -0
- package/dist/compiler/content-schema.js +82 -0
- package/dist/compiler/content-schemas.d.ts +422 -0
- package/dist/compiler/content-schemas.js +229 -0
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +30 -2
- package/dist/compiler/exports/combined-html.d.ts +97 -0
- package/dist/compiler/exports/combined-html.js +492 -0
- package/dist/compiler/exports/download-index.d.ts +9 -0
- package/dist/compiler/exports/download-index.js +33 -0
- package/dist/compiler/exports/export-chapters.d.ts +98 -0
- package/dist/compiler/exports/export-chapters.js +164 -0
- package/dist/compiler/exports/export-pages.d.ts +48 -0
- package/dist/compiler/exports/export-pages.js +22 -0
- package/dist/compiler/exports/html-tree.d.ts +22 -0
- package/dist/compiler/exports/html-tree.js +47 -0
- package/dist/compiler/exports/latex/capture.d.ts +106 -0
- package/dist/compiler/exports/latex/capture.js +435 -0
- package/dist/compiler/exports/latex/compile.d.ts +73 -0
- package/dist/compiler/exports/latex/compile.js +115 -0
- package/dist/compiler/exports/latex/document.d.ts +102 -0
- package/dist/compiler/exports/latex/document.js +349 -0
- package/dist/compiler/exports/latex/explico.cls +415 -0
- package/dist/compiler/exports/latex/numbering.d.ts +18 -0
- package/dist/compiler/exports/latex/numbering.js +57 -0
- package/dist/compiler/exports/latex/serialize.d.ts +112 -0
- package/dist/compiler/exports/latex/serialize.js +606 -0
- package/dist/compiler/exports/latex-export.d.ts +44 -0
- package/dist/compiler/exports/latex-export.js +169 -0
- package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
- package/dist/compiler/exports/markdown/serialize.js +527 -0
- package/dist/compiler/exports/markdown-export.d.ts +39 -0
- package/dist/compiler/exports/markdown-export.js +240 -0
- package/dist/compiler/exports/standalone-html.d.ts +15 -5
- package/dist/compiler/exports/standalone-html.js +23 -51
- package/dist/compiler/exports/static-tree.d.ts +253 -0
- package/dist/compiler/exports/static-tree.js +625 -0
- package/dist/compiler/exports/zip.d.ts +16 -0
- package/dist/compiler/exports/zip.js +72 -0
- package/dist/compiler/lesson-derivation.d.ts +8 -2
- package/dist/compiler/lesson-derivation.js +6 -7
- package/dist/compiler/manifest.d.ts +115 -8
- package/dist/compiler/manifest.js +221 -39
- package/dist/compiler/render-diagnostics.d.ts +65 -0
- package/dist/compiler/render-diagnostics.js +56 -0
- package/dist/compiler/resolution-report.d.ts +3 -1
- package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
- package/dist/curriculum/validation.d.ts +9 -3
- package/dist/curriculum/validation.js +35 -17
- package/dist/reference/base-abbreviations.d.mts +2 -2
- package/dist/reference/base-abbreviations.mjs +2 -2
- package/dist/reference/base-operators.d.mts +8 -2
- package/dist/reference/base-operators.mjs +8 -10
- package/dist/reference/base-roman-labels.d.mts +4 -3
- package/dist/reference/base-roman-labels.mjs +4 -3
- package/dist/reference/citation-format.d.mts +35 -0
- package/dist/reference/citation-format.mjs +74 -0
- package/dist/reference/consistency.d.ts +5 -9
- package/dist/reference/consistency.js +34 -38
- package/dist/reference/curriculum-alignment.js +3 -24
- package/dist/reference/derived-entries.d.ts +45 -0
- package/dist/reference/derived-entries.js +50 -0
- package/dist/reference/derived-notation.d.mts +125 -0
- package/dist/reference/derived-notation.mjs +121 -0
- package/dist/reference/diagram-fit.d.ts +45 -0
- package/dist/reference/diagram-fit.js +54 -0
- package/dist/reference/diagram-layout.d.ts +59 -10
- package/dist/reference/diagram-layout.js +85 -24
- package/dist/reference/diagrams-validate.d.ts +1 -1
- package/dist/reference/equations-validate.d.ts +1 -1
- package/dist/reference/equations-validate.js +26 -1
- package/dist/reference/equations.d.mts +27 -0
- package/dist/reference/equations.mjs +120 -22
- package/dist/reference/examples-validate.d.ts +1 -1
- package/dist/reference/figure-fit.d.ts +70 -0
- package/dist/reference/figure-fit.js +60 -0
- package/dist/reference/figure-pictures.d.ts +27 -0
- package/dist/reference/figure-pictures.js +49 -0
- package/dist/reference/figure-symbols.d.ts +32 -0
- package/dist/reference/figure-symbols.js +55 -0
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-abbreviations.js +1 -1
- package/dist/reference/gate-course-rules.d.ts +4 -3
- package/dist/reference/gate-course-rules.js +9 -4
- package/dist/reference/gate-heading-length.d.ts +2 -0
- package/dist/reference/gate-heading-length.js +7 -5
- package/dist/reference/gate-heading-structure.js +26 -0
- package/dist/reference/gate-indices.d.ts +35 -0
- package/dist/reference/gate-indices.js +299 -0
- package/dist/reference/gate-math.d.ts +32 -2
- package/dist/reference/gate-math.js +68 -12
- package/dist/reference/gate-mdx-expression.d.ts +17 -0
- package/dist/reference/gate-mdx-expression.js +29 -0
- package/dist/reference/gate-non-chapter-pages.d.ts +39 -0
- package/dist/reference/gate-non-chapter-pages.js +156 -0
- package/dist/reference/gate-part-pages.d.ts +4 -2
- package/dist/reference/gate-part-pages.js +2 -1
- package/dist/reference/gate-prop-text.d.ts +28 -0
- package/dist/reference/gate-prop-text.js +106 -0
- package/dist/reference/gate-prose-notation.d.ts +3 -1
- package/dist/reference/gate-prose-notation.js +4 -2
- package/dist/reference/gate-retired-keys.d.ts +35 -0
- package/dist/reference/gate-retired-keys.js +58 -0
- package/dist/reference/gloss.d.ts +2 -0
- package/dist/reference/katex-measure.d.ts +17 -0
- package/dist/reference/katex-measure.js +223 -0
- package/dist/reference/keyed-float-validate.js +12 -0
- package/dist/reference/keyed-jsx-floats.d.mts +2 -2
- package/dist/reference/keyed-jsx-floats.mjs +11 -14
- package/dist/reference/lab-katex-validate.d.ts +43 -0
- package/dist/reference/lab-katex-validate.js +96 -0
- package/dist/reference/lab-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-scope.js +13 -2
- package/dist/reference/lint-ignore.js +1 -1
- package/dist/reference/math-glyphs.d.mts +94 -2
- package/dist/reference/math-glyphs.mjs +851 -46
- package/dist/reference/mixed-content.d.ts +8 -3
- package/dist/reference/mixed-content.js +4 -2
- package/dist/reference/page-notation.d.ts +39 -7
- package/dist/reference/page-notation.js +62 -10
- package/dist/reference/raw-floats.mjs +37 -13
- package/dist/reference/references.d.ts +11 -1
- package/dist/reference/references.js +6 -0
- package/dist/reference/registry.js +115 -11
- package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
- package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
- package/dist/reference/rehype-heading-anchors.mjs +11 -1
- package/dist/reference/remark-citation.mjs +0 -0
- package/dist/reference/remark-notation.mjs +275 -23
- package/dist/reference/render-lab-math.d.ts +13 -2
- package/dist/reference/render-lab-math.js +18 -6
- package/dist/reference/sections-validate.d.ts +28 -3
- package/dist/reference/sections-validate.js +31 -4
- package/dist/reference/sections.d.mts +24 -6
- package/dist/reference/sections.mjs +92 -17
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/tables-validate.js +24 -1
- package/dist/reference/text-measure.d.ts +10 -0
- package/dist/reference/text-measure.js +41 -0
- package/dist/reference/text-widths.d.ts +10 -0
- package/dist/reference/text-widths.js +1238 -0
- package/dist/reference/types.d.ts +64 -8
- package/dist/rules/catalog.js +223 -32
- package/dist/rules/in-effect.d.ts +1 -1
- package/dist/rules/rules.schema.json +100 -27
- package/dist/rules/settings.d.ts +1 -1
- package/dist/rules/settings.js +7 -0
- package/dist/session/PreferenceStore.d.ts +18 -1
- package/dist/session/PreferenceStore.js +29 -1
- package/docs/authoring/README.md +33 -21
- package/docs/authoring/agents.md +38 -7
- package/docs/authoring/components.md +115 -10
- package/docs/authoring/migrating.md +878 -0
- package/docs/authoring/notation.md +285 -30
- package/docs/authoring/retired.md +1 -0
- package/docs/authoring/structure-rules.md +84 -45
- package/docs/authoring/structure.md +67 -2
- package/docs/authoring/visualizations.md +167 -25
- package/package.json +12 -7
- package/src/compiler/astro.ts +72 -21
- package/src/compiler/cli.ts +143 -35
- package/src/compiler/collections.ts +158 -27
- package/src/compiler/content-schema.ts +108 -0
- package/src/compiler/content-schemas.ts +391 -0
- package/src/compiler/course-config.ts +86 -15
- package/src/compiler/exports/combined-html.ts +669 -0
- package/src/compiler/exports/download-index.ts +36 -0
- package/src/compiler/exports/export-chapters.ts +233 -0
- package/src/compiler/exports/export-pages.ts +44 -0
- package/src/compiler/exports/html-tree.ts +62 -0
- package/src/compiler/exports/latex/capture.ts +651 -0
- package/src/compiler/exports/latex/compile.ts +168 -0
- package/src/compiler/exports/latex/document.ts +479 -0
- package/src/compiler/exports/latex/explico.cls +415 -0
- package/src/compiler/exports/latex/numbering.ts +74 -0
- package/src/compiler/exports/latex/serialize.ts +787 -0
- package/src/compiler/exports/latex/texlive-packages.txt +76 -0
- package/src/compiler/exports/latex-export.ts +255 -0
- package/src/compiler/exports/markdown/serialize.ts +632 -0
- package/src/compiler/exports/markdown-export.ts +329 -0
- package/src/compiler/exports/standalone-html.ts +39 -70
- package/src/compiler/exports/static-tree.ts +944 -0
- package/src/compiler/exports/zip.ts +88 -0
- package/src/compiler/lesson-derivation.ts +13 -8
- package/src/compiler/manifest.ts +387 -40
- package/src/compiler/render-diagnostics.ts +111 -0
- package/src/components/CourseContents.astro +6 -0
- package/src/components/HoverPanel.astro +204 -6
- package/src/components/PartOverview.astro +18 -9
- package/src/components/abbreviation/AbbreviationGlossary.astro +6 -2
- package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
- package/src/components/assessments/AssessmentRunner.tsx +7 -1
- package/src/components/assessments/AssessmentSet.astro +14 -1
- package/src/components/block-heading.ts +38 -0
- package/src/components/card-arrow.ts +18 -4
- package/src/components/card-carry.ts +406 -0
- package/src/components/card-drag.ts +1 -30
- package/src/components/card-fetch.ts +87 -0
- package/src/components/card-manager.ts +1222 -38
- package/src/components/card-memory.ts +5 -1
- package/src/components/card-placement.ts +211 -13
- package/src/components/card-side.ts +281 -31
- package/src/components/citation/CitationLayer.astro +45 -40
- package/src/components/diagram/Diagram.astro +123 -44
- package/src/components/disclosure/print-as-shown.ts +42 -6
- package/src/components/figure/Figure.astro +17 -1
- package/src/components/figure/FloatCaption.astro +4 -17
- package/src/components/figure/figure-state.ts +26 -17
- package/src/components/float-toolbar.ts +274 -0
- package/src/components/memo/Memo.astro +21 -10
- package/src/components/memo/ShortNote.astro +4 -0
- package/src/components/notation/NotationGlossary.astro +22 -17
- package/src/components/notation/NotationLayer.astro +300 -79
- package/src/components/notation/NotationMath.astro +8 -1
- package/src/components/notation/render-notation-math.ts +51 -5
- package/src/components/readable-text.ts +25 -0
- package/src/components/reference/FloatRefLayer.astro +162 -89
- package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
- package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
- package/src/components/starlight/LayoutPageSidebar.astro +17 -3
- package/src/components/starlight/LayoutSocialIcons.astro +79 -29
- package/src/components/starlight/LessonExportActions.astro +384 -310
- package/src/components/starlight/LessonFooter.astro +66 -8
- package/src/components/starlight/PrintController.astro +28 -0
- package/src/components/starlight/TextSizeControl.astro +137 -0
- package/src/components/starlight/print-controller.ts +380 -0
- package/src/components/starlight/section-summary.ts +13 -5
- package/src/components/table/Table.astro +14 -22
- package/src/components/table/table-truncation.ts +15 -0
- package/src/content-config.ts +20 -312
- package/src/curriculum/validation.ts +72 -24
- package/src/reference/base-abbreviations.mjs +2 -2
- package/src/reference/base-operators.mjs +8 -10
- package/src/reference/base-roman-labels.mjs +4 -3
- package/src/reference/citation-format.mjs +74 -0
- package/src/reference/consistency.ts +47 -61
- package/src/reference/curriculum-alignment.ts +4 -41
- package/src/reference/derived-entries.ts +68 -0
- package/src/reference/derived-notation.mjs +121 -0
- package/src/reference/diagram-fit.ts +104 -0
- package/src/reference/diagram-layout.ts +182 -51
- package/src/reference/diagrams-validate.ts +2 -1
- package/src/reference/equations-validate.ts +41 -2
- package/src/reference/equations.mjs +120 -22
- package/src/reference/examples-validate.ts +1 -0
- package/src/reference/figure-fit.ts +139 -0
- package/src/reference/figure-pictures.ts +96 -0
- package/src/reference/figure-symbols.ts +117 -0
- package/src/reference/figures-validate.ts +2 -1
- package/src/reference/gate-abbreviations.ts +1 -1
- package/src/reference/gate-course-rules.ts +22 -7
- package/src/reference/gate-heading-length.ts +25 -7
- package/src/reference/gate-heading-structure.ts +40 -1
- package/src/reference/gate-indices.ts +470 -0
- package/src/reference/gate-math.ts +102 -13
- package/src/reference/gate-mdx-expression.ts +45 -0
- package/src/reference/gate-non-chapter-pages.ts +241 -0
- package/src/reference/gate-part-pages.ts +1 -1
- package/src/reference/gate-prop-text.ts +145 -0
- package/src/reference/gate-prose-notation.ts +19 -5
- package/src/reference/gate-retired-keys.ts +101 -0
- package/src/reference/gloss.ts +2 -0
- package/src/reference/katex-measure.ts +384 -0
- package/src/reference/keyed-float-validate.ts +22 -0
- package/src/reference/keyed-jsx-floats.mjs +11 -14
- package/src/reference/lab-katex-validate.ts +156 -0
- package/src/reference/lab-math-scope.ts +15 -1
- package/src/reference/lint-ignore.ts +4 -1
- package/src/reference/math-glyphs.mjs +851 -46
- package/src/reference/mixed-content.ts +11 -2
- package/src/reference/page-notation.ts +114 -14
- package/src/reference/raw-floats.mjs +37 -13
- package/src/reference/references.ts +14 -0
- package/src/reference/registry.ts +170 -9
- package/src/reference/rehype-fail-katex-errors.mjs +19 -0
- package/src/reference/rehype-heading-anchors.mjs +11 -1
- package/src/reference/remark-citation.mjs +0 -0
- package/src/reference/remark-notation.mjs +275 -23
- package/src/reference/render-lab-math.ts +40 -5
- package/src/reference/sections-validate.ts +67 -3
- package/src/reference/sections.mjs +92 -17
- package/src/reference/tables-validate.ts +40 -2
- package/src/reference/text-measure.ts +63 -0
- package/src/reference/text-widths.ts +133 -0
- package/src/reference/types.ts +75 -8
- package/src/rules/catalog.ts +241 -31
- package/src/rules/settings.ts +10 -0
- package/src/session/PreferenceStore.ts +53 -0
- package/src/styles/global.css +322 -83
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import katex, {} from "katex";
|
|
2
|
+
import fontMetricsData from "katex/src/fontMetricsData.js";
|
|
3
|
+
import { createNotationKatexOptions } from "./katex-options.mjs";
|
|
4
|
+
const SIZE_MULTIPLIERS = [0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.44, 1.728, 2.074, 2.488];
|
|
5
|
+
const CLASS_PADDING = {
|
|
6
|
+
"x-arrow-pad": [0.5, 0.5],
|
|
7
|
+
"cd-arrow-pad": [0.27778, 0.55556],
|
|
8
|
+
boxpad: [0.3, 0.3],
|
|
9
|
+
"cancel-pad": [0.2, 0.2],
|
|
10
|
+
anglpad: [0.03889, 0.03889]
|
|
11
|
+
};
|
|
12
|
+
const CLASS_MARGIN = {
|
|
13
|
+
"cancel-lap": [-0.2, -0.2],
|
|
14
|
+
angl: [0, 0.03889]
|
|
15
|
+
};
|
|
16
|
+
const CLASS_BORDER = {
|
|
17
|
+
fbox: [0.04, 0.04],
|
|
18
|
+
fcolorbox: [0.04, 0.04],
|
|
19
|
+
angl: [0, 0.049]
|
|
20
|
+
};
|
|
21
|
+
const ZERO_WIDTH = /* @__PURE__ */ new Set(["llap", "rlap", "clap", "pstrut", "vlist-s"]);
|
|
22
|
+
function has(node, name) {
|
|
23
|
+
return node.classes?.includes(name) ?? false;
|
|
24
|
+
}
|
|
25
|
+
function em(value) {
|
|
26
|
+
if (!value) return 0;
|
|
27
|
+
const match = /^(-?\d*\.?\d+)em$/.exec(value.trim());
|
|
28
|
+
return match ? Number(match[1]) : 0;
|
|
29
|
+
}
|
|
30
|
+
function sizing(node) {
|
|
31
|
+
if (!has(node, "sizing") && !has(node, "fontsize-ensurer")) return 1;
|
|
32
|
+
let from = 0;
|
|
33
|
+
let to = 0;
|
|
34
|
+
for (const name of node.classes ?? []) {
|
|
35
|
+
const reset = /^reset-size(\d+)$/.exec(name);
|
|
36
|
+
if (reset) from = Number(reset[1]);
|
|
37
|
+
const size = /^size(\d+)$/.exec(name);
|
|
38
|
+
if (size) to = Number(size[1]);
|
|
39
|
+
}
|
|
40
|
+
const fromMultiplier = SIZE_MULTIPLIERS[from - 1];
|
|
41
|
+
const toMultiplier = SIZE_MULTIPLIERS[to - 1];
|
|
42
|
+
return fromMultiplier && toMultiplier ? toMultiplier / fromMultiplier : 1;
|
|
43
|
+
}
|
|
44
|
+
function pairOf(table, node) {
|
|
45
|
+
let total = 0;
|
|
46
|
+
for (const name of node.classes ?? []) {
|
|
47
|
+
const pair = table[name];
|
|
48
|
+
if (pair) total += pair[0] + pair[1];
|
|
49
|
+
}
|
|
50
|
+
return total;
|
|
51
|
+
}
|
|
52
|
+
const ROOT_FONT = { family: "Main", bold: false, italic: false };
|
|
53
|
+
const FONT_CLASSES = {
|
|
54
|
+
textbf: { bold: true },
|
|
55
|
+
textit: { italic: true },
|
|
56
|
+
textrm: { family: "Main" },
|
|
57
|
+
texttt: { family: "Typewriter" },
|
|
58
|
+
mathnormal: { family: "Math", italic: true },
|
|
59
|
+
mathit: { family: "Main", italic: true },
|
|
60
|
+
mathrm: { italic: false },
|
|
61
|
+
mathbf: { family: "Main", bold: true },
|
|
62
|
+
boldsymbol: { family: "Math", bold: true, italic: true },
|
|
63
|
+
amsrm: { family: "AMS" },
|
|
64
|
+
mathbb: { family: "AMS" },
|
|
65
|
+
textbb: { family: "AMS" },
|
|
66
|
+
mathcal: { family: "Caligraphic" },
|
|
67
|
+
mathfrak: { family: "Fraktur" },
|
|
68
|
+
textfrak: { family: "Fraktur" },
|
|
69
|
+
mathboldfrak: { family: "Fraktur", bold: true },
|
|
70
|
+
textboldfrak: { family: "Fraktur", bold: true },
|
|
71
|
+
mathtt: { family: "Typewriter" },
|
|
72
|
+
mathscr: { family: "Script" },
|
|
73
|
+
textscr: { family: "Script" },
|
|
74
|
+
mathsf: { family: "SansSerif" },
|
|
75
|
+
textsf: { family: "SansSerif" },
|
|
76
|
+
mathboldsf: { family: "SansSerif", bold: true },
|
|
77
|
+
textboldsf: { family: "SansSerif", bold: true },
|
|
78
|
+
mathsfit: { family: "SansSerif", italic: true },
|
|
79
|
+
mathitsf: { family: "SansSerif", italic: true },
|
|
80
|
+
textitsf: { family: "SansSerif", italic: true },
|
|
81
|
+
mainrm: { family: "Main", italic: false }
|
|
82
|
+
};
|
|
83
|
+
function fontOf(node, inherited) {
|
|
84
|
+
let font = inherited;
|
|
85
|
+
for (const name of node.classes ?? []) {
|
|
86
|
+
const rule = FONT_CLASSES[name];
|
|
87
|
+
if (rule) font = { ...font, ...rule };
|
|
88
|
+
}
|
|
89
|
+
if (has(node, "delimsizing")) {
|
|
90
|
+
for (const size of [1, 2, 3, 4]) {
|
|
91
|
+
if (has(node, `size${size}`)) font = { ...font, family: `Size${size}` };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (has(node, "op-symbol")) {
|
|
95
|
+
if (has(node, "small-op")) font = { ...font, family: "Size1" };
|
|
96
|
+
if (has(node, "large-op")) font = { ...font, family: "Size2" };
|
|
97
|
+
}
|
|
98
|
+
return font;
|
|
99
|
+
}
|
|
100
|
+
const METRICS = fontMetricsData;
|
|
101
|
+
function metricsFor(font) {
|
|
102
|
+
const variants = [
|
|
103
|
+
font.bold && font.italic ? "BoldItalic" : font.bold ? "Bold" : font.italic ? "Italic" : "Regular",
|
|
104
|
+
font.bold ? "Bold" : void 0,
|
|
105
|
+
font.italic ? "Italic" : void 0,
|
|
106
|
+
"Regular"
|
|
107
|
+
];
|
|
108
|
+
for (const variant of variants) {
|
|
109
|
+
const table = variant && METRICS[`${font.family}-${variant}`];
|
|
110
|
+
if (table) return table;
|
|
111
|
+
}
|
|
112
|
+
return METRICS["Main-Regular"] ?? {};
|
|
113
|
+
}
|
|
114
|
+
const WIDE_GLYPHS = /* @__PURE__ */ new Map([
|
|
115
|
+
[8748, 0.85],
|
|
116
|
+
// ∬ \iint
|
|
117
|
+
[8749, 1.22],
|
|
118
|
+
// ∭ \iiint
|
|
119
|
+
[8751, 0.85],
|
|
120
|
+
// ∯ \oiint
|
|
121
|
+
[8752, 1.22],
|
|
122
|
+
// ∰ \oiiint
|
|
123
|
+
[10764, 1.6]
|
|
124
|
+
// ⨌ \iiiint
|
|
125
|
+
]);
|
|
126
|
+
function glyphWidth(table, char) {
|
|
127
|
+
if (char === "\u200B") return 0;
|
|
128
|
+
const metrics = table[String(char.codePointAt(0))] ?? table["77"];
|
|
129
|
+
return metrics?.[4] ?? 1;
|
|
130
|
+
}
|
|
131
|
+
function glyphAdvances(node, font) {
|
|
132
|
+
const [first, ...rest] = [...node.text ?? ""];
|
|
133
|
+
if (first === void 0) return [];
|
|
134
|
+
const table = metricsFor(font);
|
|
135
|
+
const wide = (char) => WIDE_GLYPHS.get(char.codePointAt(0) ?? 0);
|
|
136
|
+
const firstWidth = wide(first) ?? (node.width && node.width > 0 ? node.width : glyphWidth(table, first));
|
|
137
|
+
return [firstWidth, ...rest.map((char) => wide(char) ?? glyphWidth(table, char))];
|
|
138
|
+
}
|
|
139
|
+
function glyphPx(advanceEm, fontPx) {
|
|
140
|
+
return Math.max(Math.round(advanceEm * Math.round(fontPx)), advanceEm * fontPx);
|
|
141
|
+
}
|
|
142
|
+
function advance(node, inherited, context, parent) {
|
|
143
|
+
const frame = {
|
|
144
|
+
scale: inherited.scale * sizing(node),
|
|
145
|
+
font: fontOf(node, inherited.font)
|
|
146
|
+
};
|
|
147
|
+
const scale = frame.scale * context.fontPx;
|
|
148
|
+
const style = node.style ?? {};
|
|
149
|
+
if (typeof node.text === "string") {
|
|
150
|
+
const marginRight = style.marginRight !== void 0 ? em(style.marginRight) : Math.max(0, node.italic ?? 0);
|
|
151
|
+
const glyphs = glyphAdvances(node, frame.font).reduce(
|
|
152
|
+
(sum, width) => sum + glyphPx(width, scale),
|
|
153
|
+
0
|
|
154
|
+
);
|
|
155
|
+
return glyphs + (marginRight + em(style.marginLeft)) * scale;
|
|
156
|
+
}
|
|
157
|
+
if (!node.classes) return 0;
|
|
158
|
+
const margins = (em(style.marginLeft) + em(style.marginRight) + pairOf(CLASS_MARGIN, node) + rootMargin(node, parent)) * scale;
|
|
159
|
+
if (node.classes.some((name) => ZERO_WIDTH.has(name))) return margins;
|
|
160
|
+
if (has(node, "accent-body") && !has(node, "accent-full")) return margins;
|
|
161
|
+
let content;
|
|
162
|
+
if (has(node, "nulldelimiter")) {
|
|
163
|
+
content = 0.12 * scale;
|
|
164
|
+
} else if (em(style.width) > 0) {
|
|
165
|
+
content = em(style.width) * scale;
|
|
166
|
+
} else if (has(node, "vlist-t")) {
|
|
167
|
+
content = tableWidth(node, frame, context);
|
|
168
|
+
} else if (has(node, "vlist")) {
|
|
169
|
+
content = Math.max(
|
|
170
|
+
0,
|
|
171
|
+
...(node.children ?? []).map((row) => advance(row, frame, context, node))
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
content = (node.children ?? []).reduce(
|
|
175
|
+
(sum, child) => sum + advance(child, frame, context, node),
|
|
176
|
+
0
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
content = Math.max(content, em(style.minWidth) * scale);
|
|
180
|
+
if (has(node, "vertical-separator")) content = Math.max(content, 1);
|
|
181
|
+
const padding = (em(style.paddingLeft) + pairOf(CLASS_PADDING, node)) * scale;
|
|
182
|
+
const border = (em(style.borderRightWidth) + pairOf(CLASS_BORDER, node)) * scale;
|
|
183
|
+
return content + padding + border + margins;
|
|
184
|
+
}
|
|
185
|
+
function rootMargin(node, parent) {
|
|
186
|
+
return has(node, "root") && parent !== void 0 && has(parent, "sqrt") ? 0.2777777778 - 0.5555555556 : 0;
|
|
187
|
+
}
|
|
188
|
+
function tableWidth(node, frame, context) {
|
|
189
|
+
const columns = [];
|
|
190
|
+
for (const row of node.children ?? []) {
|
|
191
|
+
let column = 0;
|
|
192
|
+
for (const cell of row.children ?? []) {
|
|
193
|
+
if (has(cell, "vlist-s")) continue;
|
|
194
|
+
columns[column] = Math.max(columns[column] ?? 0, advance(cell, frame, context, row));
|
|
195
|
+
column += 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return columns.reduce((sum, width) => sum + width, 0);
|
|
199
|
+
}
|
|
200
|
+
function collectBases(node, out) {
|
|
201
|
+
if (has(node, "base")) {
|
|
202
|
+
out.push(node);
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
for (const child of node.children ?? []) collectBases(child, out);
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
function measureFormula(latex, fontPx) {
|
|
209
|
+
const { __renderToHTMLTree } = katex;
|
|
210
|
+
const tree = __renderToHTMLTree(latex, {
|
|
211
|
+
...createNotationKatexOptions(),
|
|
212
|
+
displayMode: false
|
|
213
|
+
});
|
|
214
|
+
const bases = collectBases(tree, []);
|
|
215
|
+
return {
|
|
216
|
+
width: advance(tree, { scale: 1, font: ROOT_FONT }, { fontPx }),
|
|
217
|
+
height: Math.max(0, ...bases.map((base) => base.height ?? 0)) * fontPx,
|
|
218
|
+
depth: Math.max(0, ...bases.map((base) => base.depth ?? 0)) * fontPx
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
export {
|
|
222
|
+
measureFormula
|
|
223
|
+
};
|
|
@@ -46,6 +46,18 @@ function validateKeyedFloats(lessons, kind) {
|
|
|
46
46
|
...raw.key ? { key: raw.key } : {}
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
+
for (const prop of ["short", "caption"]) {
|
|
50
|
+
if (!raw.props.includes(prop) || raw[prop] != null) continue;
|
|
51
|
+
const places = prefix === "ex" ? `its [[${prefix}-\u2026]] card and a copied link` : `its [[${prefix}-\u2026]] card, a copied link, a folded section's line and the "Figures and tables" page`;
|
|
52
|
+
diagnostics.push({
|
|
53
|
+
code: `${prefix}-unread-prop`,
|
|
54
|
+
severity: "warning",
|
|
55
|
+
message: `${lesson.lessonId} has a <${tagName} ${prop}={\u2026}>${raw.key ? ` (id="${raw.key}")` : ""}, but the manifest reads only a quoted \`${prop}\`, so ${places} name this ${noun} without it. Write ${prop}="\u2026", with typographic quotes (\u2018\u2019 \u201C\u201D) inside.`,
|
|
56
|
+
file: lesson.file,
|
|
57
|
+
lessonId: lesson.lessonId,
|
|
58
|
+
...raw.key ? { key: raw.key } : {}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
49
61
|
if (raw.key == null) continue;
|
|
50
62
|
if (!KEYED_FLOAT_KEY_PATTERN.test(raw.key)) {
|
|
51
63
|
diagnostics.push({
|
|
@@ -50,8 +50,8 @@ export function countKeyedJsxFloats(markdown: any, { tagName }: {
|
|
|
50
50
|
tagName: any;
|
|
51
51
|
}): number;
|
|
52
52
|
/**
|
|
53
|
-
* The mdast equivalent of `scanKeyedJsxFloats`: walk the
|
|
54
|
-
*
|
|
53
|
+
* The mdast equivalent of `scanKeyedJsxFloats`: walk the lesson's flow (and
|
|
54
|
+
* through the `TRANSPARENT_COMPONENTS`' bodies) in document order, advancing the
|
|
55
55
|
* section counter on every top-level depth-2 heading and emitting a record for
|
|
56
56
|
* every `mdxJsxFlowElement` named `tagName` — keyed or not, exactly like
|
|
57
57
|
* `collectDisplayEquations`. `numbering` is threaded the same way as in
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import {
|
|
27
27
|
resolveNumbering,
|
|
28
|
+
stripContainerDirectives,
|
|
28
29
|
stripFencedCode,
|
|
29
30
|
stripJsxComponents,
|
|
30
31
|
TRANSPARENT_COMPONENTS,
|
|
@@ -131,10 +132,9 @@ function mdxStringAttribute(node, name) {
|
|
|
131
132
|
*/
|
|
132
133
|
export function scanKeyedJsxFloats(markdown, { tagName, numbering }) {
|
|
133
134
|
const { prefix, offset } = resolveNumbering(numbering);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
tagName,
|
|
137
|
-
TRANSPARENT_COMPONENTS,
|
|
135
|
+
// A `:::note` aside is a callout, like `<Aside>`: nothing in it numbers.
|
|
136
|
+
const text = stripContainerDirectives(
|
|
137
|
+
stripJsxComponents(stripFencedCode(markdown), tagName, TRANSPARENT_COMPONENTS),
|
|
138
138
|
);
|
|
139
139
|
const tokens = [];
|
|
140
140
|
const heading = /^[ \t]{0,3}##[ \t]+\S/gm;
|
|
@@ -196,8 +196,8 @@ export function countKeyedJsxFloats(markdown, { tagName }) {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* The mdast equivalent of `scanKeyedJsxFloats`: walk the
|
|
200
|
-
*
|
|
199
|
+
* The mdast equivalent of `scanKeyedJsxFloats`: walk the lesson's flow (and
|
|
200
|
+
* through the `TRANSPARENT_COMPONENTS`' bodies) in document order, advancing the
|
|
201
201
|
* section counter on every top-level depth-2 heading and emitting a record for
|
|
202
202
|
* every `mdxJsxFlowElement` named `tagName` — keyed or not, exactly like
|
|
203
203
|
* `collectDisplayEquations`. `numbering` is threaded the same way as in
|
|
@@ -210,8 +210,12 @@ export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
|
|
|
210
210
|
let indexInSection = 0;
|
|
211
211
|
let indexInLesson = 0;
|
|
212
212
|
|
|
213
|
+
// The lesson's flow — top-level blocks, list items, blockquotes — and the
|
|
214
|
+
// `TRANSPARENT_COMPONENTS`' bodies; never another component's body or a
|
|
215
|
+
// `:::note` aside. `scanKeyedJsxFloats` reads the same set from the text.
|
|
213
216
|
const collectFromDetail = (node) => {
|
|
214
217
|
if (!node || typeof node !== 'object') return;
|
|
218
|
+
if (node.type === 'containerDirective') return;
|
|
215
219
|
if (node.type === 'mdxJsxFlowElement' && node.name === tagName) {
|
|
216
220
|
indexInSection += 1;
|
|
217
221
|
indexInLesson += 1;
|
|
@@ -256,14 +260,7 @@ export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
|
|
|
256
260
|
indexInSection = 0;
|
|
257
261
|
continue;
|
|
258
262
|
}
|
|
259
|
-
|
|
260
|
-
collectFromDetail(node);
|
|
261
|
-
} else if (
|
|
262
|
-
node.type === 'mdxJsxFlowElement' &&
|
|
263
|
-
TRANSPARENT_COMPONENTS.includes(node.name)
|
|
264
|
-
) {
|
|
265
|
-
collectFromDetail(node);
|
|
266
|
-
}
|
|
263
|
+
collectFromDetail(node);
|
|
267
264
|
}
|
|
268
265
|
return floats;
|
|
269
266
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule V3's seam, checked in the source: a component a lesson renders draws
|
|
3
|
+
* its math through `renderLabMath`, which binds every symbol to the page's
|
|
4
|
+
* notation, never by calling KaTeX itself — a legend helper around
|
|
5
|
+
* `katex.renderToString` binds none, so its symbols open no card.
|
|
6
|
+
*
|
|
7
|
+
* Starting at each lesson's own imports, this follows every relative import
|
|
8
|
+
* — the course's visuals, wherever they live — and reports each file that
|
|
9
|
+
* imports `katex` as `lab-katex-direct`, with the chain of imports that
|
|
10
|
+
* reaches it. A package import (`explico/…`, `react`) is not followed:
|
|
11
|
+
* explico's own KaTeX use is the seam. A type-only import and the KaTeX
|
|
12
|
+
* stylesheet draw nothing and are not reported.
|
|
13
|
+
*
|
|
14
|
+
* `figure-symbols.ts` checks the same promise on the rendered page, which
|
|
15
|
+
* also catches math a lab draws some other way.
|
|
16
|
+
*/
|
|
17
|
+
interface LabKatexDiagnostic {
|
|
18
|
+
readonly code: 'lab-katex-direct';
|
|
19
|
+
readonly severity: 'error';
|
|
20
|
+
readonly message: string;
|
|
21
|
+
/** The file that imports `katex`, repository-relative. */
|
|
22
|
+
readonly file: string;
|
|
23
|
+
/** The first lesson (by id) whose imports reach it. */
|
|
24
|
+
readonly lessonId: string;
|
|
25
|
+
}
|
|
26
|
+
interface LabKatexLessonInput {
|
|
27
|
+
readonly lessonId: string;
|
|
28
|
+
/** Repository-relative path of the lesson file. */
|
|
29
|
+
readonly file: string;
|
|
30
|
+
readonly body: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Every module a source file imports at run time, in order: static, re-exported,
|
|
34
|
+
* dynamic and `require`d. A type-only import is left out.
|
|
35
|
+
*/
|
|
36
|
+
declare function importSpecifiers(source: string): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Each course file that a lesson's imports reach and that imports `katex`.
|
|
39
|
+
* `root` is the repository root the lesson paths are relative to.
|
|
40
|
+
*/
|
|
41
|
+
declare function validateLabKatexImports(lessons: readonly LabKatexLessonInput[], root?: string): LabKatexDiagnostic[];
|
|
42
|
+
|
|
43
|
+
export { type LabKatexDiagnostic, type LabKatexLessonInput, importSpecifiers, validateLabKatexImports };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
3
|
+
import { stripFencedCode } from "./equations.mjs";
|
|
4
|
+
const EXTENSIONS = [".astro", ".tsx", ".ts", ".jsx", ".js", ".mjs", ".mts", ".cjs"];
|
|
5
|
+
const SOURCE_FILE = /\.(?:astro|tsx|ts|jsx|js|mjs|mts|cjs|mdx)$/u;
|
|
6
|
+
const IMPORT_PATTERNS = [
|
|
7
|
+
// import x from 'y' · import { x } from 'y' · import 'y' · import type … from 'y'
|
|
8
|
+
/\bimport\s+(type\s+)?(?:[\w*{}\s,$]+?\s+from\s+)?(['"])([^'"\n]+)\2/gu,
|
|
9
|
+
// export { x } from 'y' · export * from 'y' · export type { x } from 'y'
|
|
10
|
+
/\bexport\s+(type\s+)?(?:\*(?:\s+as\s+[\w$]+)?|\{[^}]*\})\s+from\s+(['"])([^'"\n]+)\2/gu,
|
|
11
|
+
// import('y') · require('y')
|
|
12
|
+
/\b(?:import|require)\s*\(\s*()(['"])([^'"\n]+)\2\s*\)/gu
|
|
13
|
+
];
|
|
14
|
+
function importSpecifiers(source) {
|
|
15
|
+
const found = [];
|
|
16
|
+
for (const pattern of IMPORT_PATTERNS) {
|
|
17
|
+
for (const match of source.matchAll(pattern)) {
|
|
18
|
+
if (match[1]) continue;
|
|
19
|
+
found.push({ index: match.index ?? 0, specifier: match[3] });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return found.sort((a, b) => a.index - b.index).map(({ specifier }) => specifier);
|
|
23
|
+
}
|
|
24
|
+
function isKatexModule(specifier) {
|
|
25
|
+
if (specifier !== "katex" && !specifier.startsWith("katex/")) return false;
|
|
26
|
+
return !/\.(?:css|woff2?|ttf)$/u.test(specifier);
|
|
27
|
+
}
|
|
28
|
+
function isFile(path) {
|
|
29
|
+
try {
|
|
30
|
+
return statSync(path).isFile();
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function resolveRelative(fromFile, specifier) {
|
|
36
|
+
const base = join(dirname(fromFile), specifier);
|
|
37
|
+
const candidates = [
|
|
38
|
+
base,
|
|
39
|
+
// `./chart.js` written for `./chart.ts`, the TypeScript ESM convention.
|
|
40
|
+
.../\.[cm]?js$/u.test(base) ? [".ts", ".tsx", ".mts", ".cts"].map((ext) => base.replace(/\.[cm]?js$/u, ext)) : [],
|
|
41
|
+
...EXTENSIONS.map((ext) => `${base}${ext}`),
|
|
42
|
+
...EXTENSIONS.map((ext) => join(base, `index${ext}`))
|
|
43
|
+
];
|
|
44
|
+
return candidates.find(isFile);
|
|
45
|
+
}
|
|
46
|
+
function validateLabKatexImports(lessons, root = process.cwd()) {
|
|
47
|
+
const read = (path) => {
|
|
48
|
+
try {
|
|
49
|
+
return readFileSync(path, "utf8");
|
|
50
|
+
} catch {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const repoPath = (path) => relative(root, path).split(sep).join("/");
|
|
55
|
+
const diagnostics = /* @__PURE__ */ new Map();
|
|
56
|
+
const seen = /* @__PURE__ */ new Set();
|
|
57
|
+
for (const lesson of [...lessons].sort((a, b) => a.lessonId.localeCompare(b.lessonId))) {
|
|
58
|
+
const lessonPath = join(root, lesson.file);
|
|
59
|
+
const queue = [
|
|
60
|
+
{ path: lessonPath, source: stripFencedCode(lesson.body), chain: [lesson.file] }
|
|
61
|
+
];
|
|
62
|
+
while (queue.length > 0) {
|
|
63
|
+
const { path, source, chain } = queue.shift();
|
|
64
|
+
for (const specifier of importSpecifiers(source)) {
|
|
65
|
+
if (isKatexModule(specifier)) {
|
|
66
|
+
const file = repoPath(path);
|
|
67
|
+
if (!diagnostics.has(file)) {
|
|
68
|
+
diagnostics.set(file, {
|
|
69
|
+
code: "lab-katex-direct",
|
|
70
|
+
severity: "error",
|
|
71
|
+
file,
|
|
72
|
+
lessonId: lesson.lessonId,
|
|
73
|
+
message: `${file} imports '${specifier}' and draws math whose symbols open no notation card (reached by ${chain.join(" \u2192 ")}). Render a lab's math with renderLabMath(template, scope) \u2014 { display: false } for a legend entry, an axis title or a label \u2014 so every symbol is bound to the page's notation (rule V3).`
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (!specifier.startsWith(".")) continue;
|
|
79
|
+
const target = resolveRelative(path, specifier);
|
|
80
|
+
if (!target || !SOURCE_FILE.test(target) || target.includes(`${sep}node_modules${sep}`)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (seen.has(target)) continue;
|
|
84
|
+
seen.add(target);
|
|
85
|
+
const text = read(target);
|
|
86
|
+
if (text === void 0) continue;
|
|
87
|
+
queue.push({ path: target, source: text, chain: [...chain, repoPath(target)] });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return [...diagnostics.values()].sort((a, b) => a.file.localeCompare(b.file));
|
|
92
|
+
}
|
|
93
|
+
export {
|
|
94
|
+
importSpecifiers,
|
|
95
|
+
validateLabKatexImports
|
|
96
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadManifest } from "../compiler/manifest.js";
|
|
2
2
|
import {
|
|
3
|
+
bindScopeToPage,
|
|
3
4
|
registerPageNotationDependencies,
|
|
4
5
|
routeIdFromLocals
|
|
5
6
|
} from "./page-notation.js";
|
|
@@ -9,7 +10,8 @@ async function labMathScope(notation, body = "", component) {
|
|
|
9
10
|
const local = notation?.local ?? [];
|
|
10
11
|
const scope = local.map((definition) => ({
|
|
11
12
|
key: definition.key,
|
|
12
|
-
notation: definition.latex
|
|
13
|
+
notation: definition.latex,
|
|
14
|
+
...definition.fixedIndex === true ? { fixedIndex: true } : {}
|
|
13
15
|
}));
|
|
14
16
|
const referenced = /* @__PURE__ */ new Set();
|
|
15
17
|
const explicitlyExplained = /* @__PURE__ */ new Set();
|
|
@@ -32,7 +34,11 @@ async function labMathScope(notation, body = "", component) {
|
|
|
32
34
|
}
|
|
33
35
|
const shared = sharedByKey.get(key);
|
|
34
36
|
if (shared) {
|
|
35
|
-
scope.push({
|
|
37
|
+
scope.push({
|
|
38
|
+
key: shared.key,
|
|
39
|
+
notation: shared.notation,
|
|
40
|
+
...shared.fixedIndex ? { fixedIndex: true } : {}
|
|
41
|
+
});
|
|
36
42
|
resolvedKeys.add(key);
|
|
37
43
|
continue;
|
|
38
44
|
}
|
|
@@ -48,6 +54,11 @@ async function labMathScope(notation, body = "", component) {
|
|
|
48
54
|
resolvedKeys,
|
|
49
55
|
component.context
|
|
50
56
|
);
|
|
57
|
+
bindScopeToPage(scope, {
|
|
58
|
+
locals: component.locals,
|
|
59
|
+
context: component.context,
|
|
60
|
+
checkIndices: !manifest.rules.disabled.includes("N23")
|
|
61
|
+
});
|
|
51
62
|
}
|
|
52
63
|
return scope;
|
|
53
64
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
const IGNORE_PATH = join(process.cwd(), "lint-ignore.yml");
|
|
4
|
-
const LINE = /^-\s*([a-z0-9-]+)(?::([^\s
|
|
4
|
+
const LINE = /^-\s*([a-z0-9-]+)(?::([^\s—][^—]*?))?\s*(?:—|--|\s-\s)\s*(.+?)\s*$/;
|
|
5
5
|
function parseLintIgnore(text) {
|
|
6
6
|
const entries = [];
|
|
7
7
|
for (const rawLine of text.split("\n")) {
|
|
@@ -39,6 +39,64 @@ export function operatorDisplayLatex(name: string): string;
|
|
|
39
39
|
* @returns {string | undefined}
|
|
40
40
|
*/
|
|
41
41
|
export function decorationOfEntry(latex: string): string | undefined;
|
|
42
|
+
/** True for a letter that is a running index wherever it is a whole slot. */
|
|
43
|
+
export function isRunningIndexLetter(text: any): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The key of a derived entry: a base entry composed with an index entry
|
|
46
|
+
* (`\mathbf{v}_0` from `velocity-vector` and `{}_0`). The build materializes
|
|
47
|
+
* it from the two (`derived-notation.mjs`); it is never authored, and an
|
|
48
|
+
* authored key that equals one fails the build.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} baseKey
|
|
51
|
+
* @param {string} indexKey
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
export function derivedNotationKey(baseKey: string, indexKey: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* The subscript an **index entry** explains — an empty group with a subscript:
|
|
57
|
+
* `{}_0`, `{}_x`, `{}_{\mathrm{max}}` — as the source after `{}`, else
|
|
58
|
+
* `undefined`. Such an entry binds no symbol of its own; composed with the
|
|
59
|
+
* entry of the symbol it indexes, it makes a derived card (rule N23).
|
|
60
|
+
*
|
|
61
|
+
* @param {string} latex
|
|
62
|
+
* @returns {string | undefined}
|
|
63
|
+
*/
|
|
64
|
+
export function indexOfEntry(latex: string): string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* The symbol a base entry gives a derived card: its whole `latex`, or the
|
|
67
|
+
* glyph it lends to — the head of a function form (`v` of `v(t)`), the base
|
|
68
|
+
* of a superscript form (`j` of `j^{(m)}`).
|
|
69
|
+
*
|
|
70
|
+
* @param {string} notation
|
|
71
|
+
* @returns {string}
|
|
72
|
+
*/
|
|
73
|
+
export function baseSymbolLatex(notation: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* The symbol of a derived card: the base's symbol with the index entry's
|
|
76
|
+
* subscript — `\mathbf{v}` and `{}_0` make `\mathbf{v}_0`.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} baseNotation
|
|
79
|
+
* @param {string} indexNotation
|
|
80
|
+
* @returns {string}
|
|
81
|
+
*/
|
|
82
|
+
export function derivedNotationLatex(baseNotation: string, indexNotation: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* How an index entry is shown on its own — over a placeholder square, as an
|
|
85
|
+
* operator entry is (`\square_0`). Display only.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} latex
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
90
|
+
export function indexDisplayLatex(latex: string): string;
|
|
91
|
+
/**
|
|
92
|
+
* Whether an entry's `latex` has a subscript on a symbol anywhere — what an
|
|
93
|
+
* `\explain{key}{X_i}` wrap needs from `key` before its card can explain the
|
|
94
|
+
* index (rule N23, `index-swallowed`).
|
|
95
|
+
*
|
|
96
|
+
* @param {string} latex
|
|
97
|
+
* @returns {boolean}
|
|
98
|
+
*/
|
|
99
|
+
export function entryHasIndex(latex: string): boolean;
|
|
42
100
|
/**
|
|
43
101
|
* Resolve and annotate one KaTeX expression against a page's flat glyph table.
|
|
44
102
|
* Source offsets in the result always refer to the original, unmodified LaTeX.
|
|
@@ -51,15 +109,28 @@ export function decorationOfEntry(latex: string): string | undefined;
|
|
|
51
109
|
* decorated form no whole form covers is reported in `unresolved`, marked
|
|
52
110
|
* `suspended`.
|
|
53
111
|
*
|
|
112
|
+
* A symbol with a subscript (rule N23) takes its exact whole form, else the
|
|
113
|
+
* family whose index has its shape, else — with `options.derive`, which page
|
|
114
|
+
* math sets (a lesson body, its assessments, labs and diagrams) — a derived
|
|
115
|
+
* card from an index entry (`{}_0`) and the base's entry; each such card
|
|
116
|
+
* wraps the whole indexed symbol. `derived`
|
|
117
|
+
* lists the derived cards used, as `{ key, base, index }`, for the build to
|
|
118
|
+
* materialize; `indices` says how each indexed symbol is explained (see
|
|
119
|
+
* `classifyIndexed`). A definition may carry `fixedIndex: true`: its index
|
|
120
|
+
* (or, on a letter, the letter as an index) is a fixed label, not a running
|
|
121
|
+
* one, so it is no family.
|
|
122
|
+
*
|
|
54
123
|
* @param {string} latex
|
|
55
|
-
* @param {Array<{key: string, notation: string}>} definitions
|
|
56
|
-
* @param {{ suspended?: readonly string[] }} [options]
|
|
124
|
+
* @param {Array<{key: string, notation: string, fixedIndex?: boolean}>} definitions
|
|
125
|
+
* @param {{ suspended?: readonly string[], derive?: boolean }} [options]
|
|
57
126
|
*/
|
|
58
127
|
export function resolveMathGlyphs(latex: string, definitions: Array<{
|
|
59
128
|
key: string;
|
|
60
129
|
notation: string;
|
|
130
|
+
fixedIndex?: boolean;
|
|
61
131
|
}>, options?: {
|
|
62
132
|
suspended?: readonly string[];
|
|
133
|
+
derive?: boolean;
|
|
63
134
|
}): {
|
|
64
135
|
latex: string;
|
|
65
136
|
bindings: {
|
|
@@ -72,11 +143,32 @@ export function resolveMathGlyphs(latex: string, definitions: Array<{
|
|
|
72
143
|
decorations: {
|
|
73
144
|
token: string;
|
|
74
145
|
wholeForm: any;
|
|
146
|
+
wholeFormExplicit: boolean;
|
|
75
147
|
operator: any;
|
|
76
148
|
start: any;
|
|
77
149
|
end: any;
|
|
78
150
|
name: any;
|
|
79
151
|
}[];
|
|
152
|
+
indices: {
|
|
153
|
+
token: string;
|
|
154
|
+
start: any;
|
|
155
|
+
end: any;
|
|
156
|
+
base: any;
|
|
157
|
+
index: any;
|
|
158
|
+
how: any;
|
|
159
|
+
key: any;
|
|
160
|
+
explained: boolean;
|
|
161
|
+
baseExplained: boolean;
|
|
162
|
+
unexplained: any[];
|
|
163
|
+
running: {
|
|
164
|
+
letter: any;
|
|
165
|
+
start: any;
|
|
166
|
+
end: any;
|
|
167
|
+
key: any;
|
|
168
|
+
cover: any;
|
|
169
|
+
}[];
|
|
170
|
+
}[];
|
|
171
|
+
derived: any[];
|
|
80
172
|
};
|
|
81
173
|
/**
|
|
82
174
|
* Every named operator in one KaTeX expression: a built-in `op` node
|