explico 0.12.0 → 0.13.1
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 +1137 -1
- package/README.md +91 -13
- package/dist/compiler/astro.d.ts +4 -3
- package/dist/compiler/astro.js +45 -15
- package/dist/compiler/cli.d.ts +26 -12
- package/dist/compiler/cli.js +96 -25
- package/dist/compiler/collections.d.ts +9 -1
- package/dist/compiler/collections.js +55 -16
- 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 +14 -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 +87 -6
- package/dist/compiler/manifest.js +174 -31
- package/dist/compiler/render-diagnostics.d.ts +65 -0
- package/dist/compiler/render-diagnostics.js +56 -0
- package/dist/compiler/resolution-report.d.ts +2 -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 +2 -2
- package/dist/reference/base-operators.mjs +2 -2
- 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/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-part-pages.d.ts +1 -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-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-scope.js +13 -2
- 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 +34 -4
- package/dist/reference/page-notation.js +54 -2
- package/dist/reference/references.d.ts +11 -1
- package/dist/reference/references.js +6 -0
- package/dist/reference/registry.js +104 -10
- 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 +84 -20
- package/dist/reference/render-lab-math.d.ts +2 -0
- package/dist/reference/render-lab-math.js +17 -5
- 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 +191 -30
- package/dist/rules/in-effect.d.ts +1 -1
- package/dist/rules/rules.schema.json +79 -24
- 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 +32 -21
- package/docs/authoring/agents.md +38 -7
- package/docs/authoring/components.md +111 -6
- package/docs/authoring/migrating.md +707 -0
- package/docs/authoring/notation.md +281 -29
- package/docs/authoring/retired.md +1 -0
- package/docs/authoring/structure-rules.md +10 -41
- package/docs/authoring/structure.md +53 -2
- package/docs/authoring/visualizations.md +144 -18
- package/package.json +12 -7
- package/src/compiler/astro.ts +57 -17
- package/src/compiler/cli.ts +143 -35
- package/src/compiler/collections.ts +92 -19
- 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 +299 -28
- package/src/compiler/render-diagnostics.ts +111 -0
- package/src/components/HoverPanel.astro +204 -6
- package/src/components/PartOverview.astro +12 -9
- 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/figure/Figure.astro +11 -0
- package/src/components/figure/FloatCaption.astro +4 -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 +321 -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 +47 -9
- 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 +13 -21
- 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 +2 -2
- 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/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-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-math-scope.ts +15 -1
- package/src/reference/math-glyphs.mjs +851 -46
- package/src/reference/mixed-content.ts +11 -2
- package/src/reference/page-notation.ts +100 -3
- package/src/reference/references.ts +14 -0
- package/src/reference/registry.ts +154 -8
- 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 +84 -20
- package/src/reference/render-lab-math.ts +29 -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 +205 -29
- package/src/rules/settings.ts +10 -0
- package/src/session/PreferenceStore.ts +53 -0
- package/src/styles/global.css +291 -75
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ScopeEntry } from './gate-math.js';
|
|
2
|
-
import './types.js';
|
|
2
|
+
import { DerivedUse } from './types.js';
|
|
3
3
|
|
|
4
4
|
type ContentSegment = {
|
|
5
5
|
readonly type: 'text';
|
|
@@ -10,8 +10,13 @@ type ContentSegment = {
|
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
12
|
* Split `text` into ordered segments, resolving each math span's glyphs
|
|
13
|
-
* against `scope`. Never throws.
|
|
13
|
+
* against `scope`. Never throws. With `derive` (an assessment, which is page
|
|
14
|
+
* math), an indexed symbol takes its derived card (rule N23), and each one
|
|
15
|
+
* used is added to `derived`.
|
|
14
16
|
*/
|
|
15
|
-
declare function splitMixedContent(text: string, scope: readonly ScopeEntry[]
|
|
17
|
+
declare function splitMixedContent(text: string, scope: readonly ScopeEntry[], options?: {
|
|
18
|
+
readonly derive?: boolean;
|
|
19
|
+
readonly derived?: Map<string, DerivedUse>;
|
|
20
|
+
}): ContentSegment[];
|
|
16
21
|
|
|
17
22
|
export { type ContentSegment, splitMixedContent };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveMathGlyphs } from "./math-glyphs.mjs";
|
|
2
2
|
const MATH_SPAN = /\$\$([\s\S]+?)\$\$|(?<![\\$])\$(?!\s)([^$\n]+?)(?<![\\\s])\$(?!\d)/g;
|
|
3
|
-
function splitMixedContent(text, scope) {
|
|
3
|
+
function splitMixedContent(text, scope, options = {}) {
|
|
4
4
|
const segments = [];
|
|
5
5
|
let cursor = 0;
|
|
6
6
|
const pattern = new RegExp(MATH_SPAN);
|
|
@@ -12,7 +12,9 @@ function splitMixedContent(text, scope) {
|
|
|
12
12
|
const raw = match[1] ?? match[2] ?? "";
|
|
13
13
|
let latex = raw;
|
|
14
14
|
try {
|
|
15
|
-
|
|
15
|
+
const result = resolveMathGlyphs(raw, [...scope], { derive: options.derive === true });
|
|
16
|
+
latex = result.latex;
|
|
17
|
+
for (const use of result.derived) options.derived?.set(use.key, use);
|
|
16
18
|
} catch {
|
|
17
19
|
latex = raw;
|
|
18
20
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { Manifest, ManifestLessonNotation } from '../compiler/manifest.js';
|
|
1
|
+
import { Manifest, ManifestDerivedNotation, ManifestLessonNotation } from '../compiler/manifest.js';
|
|
2
|
+
import { DerivedUse } from './types.js';
|
|
2
3
|
import '../curriculum/validation.js';
|
|
3
4
|
import './consistency.js';
|
|
4
|
-
import '../course-config-
|
|
5
|
+
import '../course-config-rRtME5UV.js';
|
|
5
6
|
import '../rules/catalog.js';
|
|
6
7
|
import '../rules/course-rules.js';
|
|
7
8
|
import '../compiler/collections.js';
|
|
8
|
-
import './
|
|
9
|
+
import './gate-retired-keys.js';
|
|
9
10
|
import './lint-ignore.js';
|
|
10
11
|
import './equations-validate.js';
|
|
11
12
|
import './tables-validate.js';
|
|
@@ -35,6 +36,27 @@ interface PageNotationDependency {
|
|
|
35
36
|
* between lessons and defeat page-local notation scope.
|
|
36
37
|
*/
|
|
37
38
|
declare function registerPageNotationDependencies(locals: object, keys: Iterable<string>, context: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Record the derived cards (rule N23) a nested server component's math uses —
|
|
41
|
+
* a lab's or a diagram's — so the page ships them with its own, and their
|
|
42
|
+
* base and index entries, which a derived card's formula opens. Keyed by
|
|
43
|
+
* `Astro.locals`, like {@link registerPageNotationDependencies}.
|
|
44
|
+
*/
|
|
45
|
+
declare function registerPageDerivedNotation(locals: object, uses: Iterable<DerivedUse>, context: string): void;
|
|
46
|
+
/** The page a glyph table was built for, and whether rule N23 is on there. */
|
|
47
|
+
interface ScopePage {
|
|
48
|
+
readonly locals: object;
|
|
49
|
+
readonly context: string;
|
|
50
|
+
readonly checkIndices: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Tie a lab's glyph table to the page it renders on (`labMathScope` does), so
|
|
54
|
+
* `renderLabMath` can give its indexed symbols derived cards and register
|
|
55
|
+
* them, and knows whether to hold them to rule N23. A table a course copies
|
|
56
|
+
* loses the tie: its math then composes no derived card.
|
|
57
|
+
*/
|
|
58
|
+
declare function bindScopeToPage(scope: object, page: ScopePage): void;
|
|
59
|
+
declare function pageOfScope(scope: object): ScopePage | undefined;
|
|
38
60
|
declare function pageNotationDependencies(locals: object): PageNotationDependency[];
|
|
39
61
|
/** Starlight's route id as exposed to nested Astro components. */
|
|
40
62
|
declare function routeIdFromLocals(locals: object): string | undefined;
|
|
@@ -44,6 +66,14 @@ declare function notationKeysInMathNodes(nodes: readonly {
|
|
|
44
66
|
readonly properties?: Readonly<Record<string, string>>;
|
|
45
67
|
readonly children?: readonly unknown[];
|
|
46
68
|
}[]): string[];
|
|
69
|
+
/**
|
|
70
|
+
* The derived cards (rule N23) a page uses: the ones its body and assessment
|
|
71
|
+
* math use, as the manifest composed them, and the ones a lab or a diagram on
|
|
72
|
+
* it registered while rendering (`locals`), composed here the same way. They
|
|
73
|
+
* travel with the page as hidden sources; their base and index entries are
|
|
74
|
+
* in its payload already.
|
|
75
|
+
*/
|
|
76
|
+
declare function derivedNotationForPage(manifest: Manifest, pageId: string, locals?: object): readonly ManifestDerivedNotation[];
|
|
47
77
|
/**
|
|
48
78
|
* Extend one compiled page bundle — a lesson's, or a non-chapter page's
|
|
49
79
|
* (rule S10) — with only the definitions requested by components rendered on
|
|
@@ -56,4 +86,4 @@ declare function notationDefinitionsForRenderedPage(manifest: Manifest, pageId:
|
|
|
56
86
|
*/
|
|
57
87
|
declare function missingNotationPayloadKeys(html: string): string[];
|
|
58
88
|
|
|
59
|
-
export { type PageNotationDependency, missingNotationPayloadKeys, notationDefinitionsForRenderedPage, notationKeysInMathNodes, pageNotationDependencies, registerPageNotationDependencies, routeIdFromLocals };
|
|
89
|
+
export { type PageNotationDependency, type ScopePage, bindScopeToPage, derivedNotationForPage, missingNotationPayloadKeys, notationDefinitionsForRenderedPage, notationKeysInMathNodes, pageNotationDependencies, pageOfScope, registerPageDerivedNotation, registerPageNotationDependencies, routeIdFromLocals };
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
formulaGlyphScope
|
|
4
4
|
} from "./gloss.js";
|
|
5
5
|
import { resolveReferenceId } from "./references.js";
|
|
6
|
+
import { derivedNotationEntries } from "./derived-entries.js";
|
|
6
7
|
const dependenciesByLocals = /* @__PURE__ */ new WeakMap();
|
|
7
8
|
function registerPageNotationDependencies(locals, keys, context) {
|
|
8
9
|
let byKey = dependenciesByLocals.get(locals);
|
|
@@ -19,6 +20,27 @@ function registerPageNotationDependencies(locals, keys, context) {
|
|
|
19
20
|
contexts.add(context);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
const derivedByLocals = /* @__PURE__ */ new WeakMap();
|
|
24
|
+
function registerPageDerivedNotation(locals, uses, context) {
|
|
25
|
+
let byKey = derivedByLocals.get(locals);
|
|
26
|
+
if (!byKey) {
|
|
27
|
+
byKey = /* @__PURE__ */ new Map();
|
|
28
|
+
derivedByLocals.set(locals, byKey);
|
|
29
|
+
}
|
|
30
|
+
const entries = [];
|
|
31
|
+
for (const use of uses) {
|
|
32
|
+
byKey.set(use.key, use);
|
|
33
|
+
entries.push(use.base, use.index);
|
|
34
|
+
}
|
|
35
|
+
registerPageNotationDependencies(locals, entries, context);
|
|
36
|
+
}
|
|
37
|
+
const pageByScope = /* @__PURE__ */ new WeakMap();
|
|
38
|
+
function bindScopeToPage(scope, page) {
|
|
39
|
+
pageByScope.set(scope, page);
|
|
40
|
+
}
|
|
41
|
+
function pageOfScope(scope) {
|
|
42
|
+
return pageByScope.get(scope);
|
|
43
|
+
}
|
|
22
44
|
function pageNotationDependencies(locals) {
|
|
23
45
|
const byKey = dependenciesByLocals.get(locals);
|
|
24
46
|
if (!byKey) return [];
|
|
@@ -52,7 +74,6 @@ function manifestDefinition(definition, cardsByKey) {
|
|
|
52
74
|
notation: definition.notation,
|
|
53
75
|
label: definition.label,
|
|
54
76
|
summary: definition.summary,
|
|
55
|
-
editorialStatus: definition.status,
|
|
56
77
|
seeAlso: [...definition.seeAlso],
|
|
57
78
|
glosses: [...definition.glosses],
|
|
58
79
|
formulaScope: formulaGlyphScope({
|
|
@@ -63,9 +84,36 @@ function manifestDefinition(definition, cardsByKey) {
|
|
|
63
84
|
...definition.formula === void 0 ? {} : { formula: definition.formula }
|
|
64
85
|
}),
|
|
65
86
|
...definition.units === void 0 ? {} : { units: definition.units },
|
|
66
|
-
...definition.formula === void 0 ? {} : { formula: definition.formula }
|
|
87
|
+
...definition.formula === void 0 ? {} : { formula: definition.formula },
|
|
88
|
+
...definition.range === void 0 ? {} : { range: definition.range }
|
|
67
89
|
};
|
|
68
90
|
}
|
|
91
|
+
function derivedNotationForPage(manifest, pageId, locals) {
|
|
92
|
+
const page = manifest.lessons.find((entry) => entry.id === pageId) ?? manifest.pages.find((entry) => entry.id === pageId);
|
|
93
|
+
const compiled = page?.notation.derived ?? [];
|
|
94
|
+
const registered = [...(locals && derivedByLocals.get(locals)?.values()) ?? []].filter(
|
|
95
|
+
(use) => !compiled.some((entry) => entry.key === use.key)
|
|
96
|
+
);
|
|
97
|
+
if (registered.length === 0) return compiled;
|
|
98
|
+
const definitionsById = new Map(
|
|
99
|
+
manifest.notation.definitions.map((definition) => [definition.id, definition])
|
|
100
|
+
);
|
|
101
|
+
const availableIds = new Set(definitionsById.keys());
|
|
102
|
+
const records = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const use of registered) {
|
|
104
|
+
for (const key of [use.base, use.index]) {
|
|
105
|
+
const id = resolveReferenceId(key, { kind: "page", lessonId: pageId }, availableIds);
|
|
106
|
+
const record = id === void 0 ? void 0 : definitionsById.get(id);
|
|
107
|
+
if (record) records.set(key, record);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const cardsByKey = new Map(
|
|
111
|
+
manifest.notation.definitions.filter((definition) => definition.kind === "shared").map((definition) => [definition.key, { key: definition.key, notation: definition.notation }])
|
|
112
|
+
);
|
|
113
|
+
return [...compiled, ...derivedNotationEntries(registered, records, cardsByKey)].sort(
|
|
114
|
+
(left, right) => left.key.localeCompare(right.key)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
69
117
|
function notationDefinitionsForRenderedPage(manifest, pageId, dependencies) {
|
|
70
118
|
const page = manifest.lessons.find((entry) => entry.id === pageId) ?? manifest.pages.find((entry) => entry.id === pageId);
|
|
71
119
|
if (!page) return [];
|
|
@@ -162,10 +210,14 @@ function missingNotationPayloadKeys(html) {
|
|
|
162
210
|
return [...triggers].filter((key) => !entries.has(key)).sort();
|
|
163
211
|
}
|
|
164
212
|
export {
|
|
213
|
+
bindScopeToPage,
|
|
214
|
+
derivedNotationForPage,
|
|
165
215
|
missingNotationPayloadKeys,
|
|
166
216
|
notationDefinitionsForRenderedPage,
|
|
167
217
|
notationKeysInMathNodes,
|
|
168
218
|
pageNotationDependencies,
|
|
219
|
+
pageOfScope,
|
|
220
|
+
registerPageDerivedNotation,
|
|
169
221
|
registerPageNotationDependencies,
|
|
170
222
|
routeIdFromLocals
|
|
171
223
|
};
|
|
@@ -2,6 +2,16 @@ import { NotationDiagnostic, SourceSpan, NotationDefinitionScope } from './types
|
|
|
2
2
|
|
|
3
3
|
declare const NOTATION_KEY_PATTERN: RegExp;
|
|
4
4
|
declare function isNotationKey(value: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* The `[[…]]` prefixes that name something other than a notation entry: an
|
|
7
|
+
* equation, a table, a figure, a diagram, a worked-example set, a knowledge
|
|
8
|
+
* check, a section, a memo. `remark-notation.mjs` and `remark-memo.mjs` route
|
|
9
|
+
* a reference by its prefix before notation keys are looked at, so a key
|
|
10
|
+
* that starts with one could never be reached.
|
|
11
|
+
*/
|
|
12
|
+
declare const RESERVED_KEY_PREFIXES: readonly ["eq-", "tab-", "fig-", "dia-", "ex-", "check-", "sec-", "memo-"];
|
|
13
|
+
/** The reserved prefix `key` starts with, if any. */
|
|
14
|
+
declare function reservedKeyPrefix(key: string): string | undefined;
|
|
5
15
|
declare function sharedDefinitionId(key: string): string;
|
|
6
16
|
declare function localDefinitionId(lessonId: string, key: string): string;
|
|
7
17
|
declare function definitionId(scope: NotationDefinitionScope, key: string): string;
|
|
@@ -14,4 +24,4 @@ declare function resolveReferenceId(key: string, scope: NotationDefinitionScope,
|
|
|
14
24
|
declare function compareSourceSpans(left: SourceSpan | undefined, right: SourceSpan | undefined): number;
|
|
15
25
|
declare function compareDiagnostics(left: NotationDiagnostic, right: NotationDiagnostic): number;
|
|
16
26
|
|
|
17
|
-
export { NOTATION_KEY_PATTERN, compareDiagnostics, compareSourceSpans, definitionId, isNotationKey, localDefinitionId, resolveReferenceId, sharedDefinitionId };
|
|
27
|
+
export { NOTATION_KEY_PATTERN, RESERVED_KEY_PREFIXES, compareDiagnostics, compareSourceSpans, definitionId, isNotationKey, localDefinitionId, reservedKeyPrefix, resolveReferenceId, sharedDefinitionId };
|
|
@@ -2,6 +2,10 @@ const NOTATION_KEY_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
|
|
|
2
2
|
function isNotationKey(value) {
|
|
3
3
|
return NOTATION_KEY_PATTERN.test(value);
|
|
4
4
|
}
|
|
5
|
+
const RESERVED_KEY_PREFIXES = ["eq-", "tab-", "fig-", "dia-", "ex-", "check-", "sec-", "memo-"];
|
|
6
|
+
function reservedKeyPrefix(key) {
|
|
7
|
+
return RESERVED_KEY_PREFIXES.find((prefix) => key.startsWith(prefix));
|
|
8
|
+
}
|
|
5
9
|
function sharedDefinitionId(key) {
|
|
6
10
|
return `shared:${key}`;
|
|
7
11
|
}
|
|
@@ -33,11 +37,13 @@ function severityRank(severity) {
|
|
|
33
37
|
}
|
|
34
38
|
export {
|
|
35
39
|
NOTATION_KEY_PATTERN,
|
|
40
|
+
RESERVED_KEY_PREFIXES,
|
|
36
41
|
compareDiagnostics,
|
|
37
42
|
compareSourceSpans,
|
|
38
43
|
definitionId,
|
|
39
44
|
isNotationKey,
|
|
40
45
|
localDefinitionId,
|
|
46
|
+
reservedKeyPrefix,
|
|
41
47
|
resolveReferenceId,
|
|
42
48
|
sharedDefinitionId
|
|
43
49
|
};
|
|
@@ -3,9 +3,12 @@ import {
|
|
|
3
3
|
compareSourceSpans,
|
|
4
4
|
isNotationKey,
|
|
5
5
|
localDefinitionId,
|
|
6
|
+
RESERVED_KEY_PREFIXES,
|
|
7
|
+
reservedKeyPrefix,
|
|
6
8
|
resolveReferenceId,
|
|
7
9
|
sharedDefinitionId
|
|
8
10
|
} from "./references.js";
|
|
11
|
+
import { lineOf, proseOnly } from "./gate-prose-notation.js";
|
|
9
12
|
function buildNotationRegistry(rawInput) {
|
|
10
13
|
const diagnostics = [];
|
|
11
14
|
const lessons = buildLessonIndex(rawInput.lessons, diagnostics);
|
|
@@ -105,7 +108,16 @@ function buildNotationRegistry(rawInput) {
|
|
|
105
108
|
)
|
|
106
109
|
);
|
|
107
110
|
}
|
|
108
|
-
|
|
111
|
+
const dependencyEdges = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const candidate of definitions.values()) {
|
|
113
|
+
dependencyEdges.set(
|
|
114
|
+
candidate.id,
|
|
115
|
+
uniqueSorted(
|
|
116
|
+
candidate.resolvedReferences.filter((reference) => reference.kind !== "formula").map((reference) => reference.definitionId).filter((definitionId) => definitionId !== candidate.id)
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
detectCycles(definitions, dependencyEdges, diagnostics);
|
|
109
121
|
const localIdsByLesson = /* @__PURE__ */ new Map();
|
|
110
122
|
for (const candidate of definitions.values()) {
|
|
111
123
|
if (candidate.kind !== "local") continue;
|
|
@@ -132,7 +144,6 @@ function buildNotationRegistry(rawInput) {
|
|
|
132
144
|
}
|
|
133
145
|
bundles.push({
|
|
134
146
|
lessonId: lesson.lessonId,
|
|
135
|
-
status: lesson.status,
|
|
136
147
|
source: lesson.source,
|
|
137
148
|
bindings: [...bindingByKey].map(([key, definitionId]) => ({ key, definitionId })).sort(
|
|
138
149
|
(left, right) => left.key.localeCompare(right.key) || left.definitionId.localeCompare(right.definitionId)
|
|
@@ -150,6 +161,21 @@ function buildNotationRegistry(rawInput) {
|
|
|
150
161
|
});
|
|
151
162
|
}
|
|
152
163
|
}
|
|
164
|
+
for (const candidate of definitions.values()) {
|
|
165
|
+
if (candidate.kind !== "local") continue;
|
|
166
|
+
const shared = definitions.get(sharedDefinitionId(candidate.input.key));
|
|
167
|
+
if (shared === void 0) continue;
|
|
168
|
+
diagnostics.push({
|
|
169
|
+
code: "local-shadows-shared",
|
|
170
|
+
severity: "warning",
|
|
171
|
+
message: `${candidate.lesson.lessonId} defines notation.local ${JSON.stringify(candidate.input.key)}, which is also a shared entry (${shared.input.source.file}): on this page [[${candidate.input.key}]] means "${candidate.input.label}", everywhere else "${shared.input.label}". Give the local entry a key of its own, or use the shared one.`,
|
|
172
|
+
key: candidate.input.key,
|
|
173
|
+
lessonId: candidate.lesson.lessonId,
|
|
174
|
+
definitionId: candidate.id,
|
|
175
|
+
source: candidate.input.source,
|
|
176
|
+
relatedSources: [shared.input.source]
|
|
177
|
+
});
|
|
178
|
+
}
|
|
153
179
|
for (const candidate of definitions.values()) {
|
|
154
180
|
if (!reachableDefinitions.has(candidate.id)) {
|
|
155
181
|
diagnostics.push({
|
|
@@ -203,6 +229,7 @@ function buildLessonIndex(rawLessons, diagnostics) {
|
|
|
203
229
|
);
|
|
204
230
|
continue;
|
|
205
231
|
}
|
|
232
|
+
diagnostics.push(...malformedReferences(lesson));
|
|
206
233
|
const previous = lessons.get(lesson.lessonId);
|
|
207
234
|
if (previous !== void 0) {
|
|
208
235
|
diagnostics.push(
|
|
@@ -251,7 +278,21 @@ function buildDefinitionIndex(rawShared, lessons, diagnostics) {
|
|
|
251
278
|
diagnostics.push(
|
|
252
279
|
diagnostic({
|
|
253
280
|
code: "invalid-key",
|
|
254
|
-
message: `${candidate.id} has invalid notation key ${JSON.stringify(candidate.input.key)}`,
|
|
281
|
+
message: `${candidate.id} has invalid notation key ${JSON.stringify(candidate.input.key)}${keyHint(candidate.input.key)}`,
|
|
282
|
+
key: candidate.input.key,
|
|
283
|
+
lessonId: candidate.kind === "local" ? candidate.lesson.lessonId : void 0,
|
|
284
|
+
definitionId: candidate.id,
|
|
285
|
+
source: candidate.input.source
|
|
286
|
+
})
|
|
287
|
+
);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const reserved = reservedKeyPrefix(candidate.input.key);
|
|
291
|
+
if (reserved !== void 0) {
|
|
292
|
+
diagnostics.push(
|
|
293
|
+
diagnostic({
|
|
294
|
+
code: "invalid-key",
|
|
295
|
+
message: `${candidate.id} has notation key ${JSON.stringify(candidate.input.key)}, which starts with the reserved \`${reserved}\`: [[${candidate.input.key}]] is read as a reference to that kind of float and never reaches this entry. Rename the key; none may start with ${RESERVED_KEY_PREFIXES.map((prefix) => `\`${prefix}\``).join(", ")}.`,
|
|
255
296
|
key: candidate.input.key,
|
|
256
297
|
lessonId: candidate.kind === "local" ? candidate.lesson.lessonId : void 0,
|
|
257
298
|
definitionId: candidate.id,
|
|
@@ -334,7 +375,7 @@ function validateReferenceKey(reference, owner, diagnostics) {
|
|
|
334
375
|
diagnostics.push(
|
|
335
376
|
diagnostic({
|
|
336
377
|
code: "invalid-key",
|
|
337
|
-
message: `${owner.id} references invalid notation key ${JSON.stringify(reference.key)}`,
|
|
378
|
+
message: `${owner.id} references invalid notation key ${JSON.stringify(reference.key)}${keyHint(reference.key)}`,
|
|
338
379
|
key: reference.key,
|
|
339
380
|
lessonId: owner.kind === "local" ? owner.lesson.lessonId : void 0,
|
|
340
381
|
definitionId: owner.id,
|
|
@@ -348,7 +389,7 @@ function validatePageReferenceKey(reference, lesson, diagnostics) {
|
|
|
348
389
|
diagnostics.push(
|
|
349
390
|
diagnostic({
|
|
350
391
|
code: "invalid-key",
|
|
351
|
-
message: `${lesson.lessonId} references invalid notation key ${JSON.stringify(reference.key)}`,
|
|
392
|
+
message: `${lesson.lessonId} references invalid notation key ${JSON.stringify(reference.key)}${keyHint(reference.key)}`,
|
|
352
393
|
key: reference.key,
|
|
353
394
|
lessonId: lesson.lessonId,
|
|
354
395
|
source: reference.source
|
|
@@ -399,14 +440,14 @@ function toDefinitionRecord(candidate) {
|
|
|
399
440
|
sources: input2.sources,
|
|
400
441
|
seeAlso: input2.seeAlso,
|
|
401
442
|
alignment: input2.alignment,
|
|
402
|
-
status: input2.status,
|
|
403
443
|
aiAssisted: input2.aiAssisted,
|
|
404
444
|
body: input2.body,
|
|
405
445
|
glosses: input2.glosses,
|
|
406
446
|
source: input2.source,
|
|
407
447
|
resolvedReferences: candidate.resolvedReferences,
|
|
408
448
|
...input2.formula === void 0 ? {} : { formula: input2.formula },
|
|
409
|
-
...input2.units === void 0 ? {} : { units: input2.units }
|
|
449
|
+
...input2.units === void 0 ? {} : { units: input2.units },
|
|
450
|
+
...indexRecordFields(input2)
|
|
410
451
|
};
|
|
411
452
|
return record2;
|
|
412
453
|
}
|
|
@@ -423,15 +464,23 @@ function toDefinitionRecord(candidate) {
|
|
|
423
464
|
sources: input.sources,
|
|
424
465
|
seeAlso: input.seeAlso,
|
|
425
466
|
alignment: input.alignment,
|
|
426
|
-
status: candidate.lesson.status,
|
|
427
467
|
glosses: input.glosses,
|
|
428
468
|
source: input.source,
|
|
429
469
|
resolvedReferences: candidate.resolvedReferences,
|
|
430
470
|
...input.formula === void 0 ? {} : { formula: input.formula },
|
|
431
|
-
...input.units === void 0 ? {} : { units: input.units }
|
|
471
|
+
...input.units === void 0 ? {} : { units: input.units },
|
|
472
|
+
...indexRecordFields(input)
|
|
432
473
|
};
|
|
433
474
|
return record;
|
|
434
475
|
}
|
|
476
|
+
function indexRecordFields(input) {
|
|
477
|
+
return {
|
|
478
|
+
...input.reads === void 0 ? {} : { reads: input.reads },
|
|
479
|
+
...input.range === void 0 ? {} : { range: input.range },
|
|
480
|
+
...input.fixedIndex === void 0 ? {} : { fixedIndex: input.fixedIndex },
|
|
481
|
+
...input.formulaTemplate === void 0 ? {} : { formulaTemplate: input.formulaTemplate }
|
|
482
|
+
};
|
|
483
|
+
}
|
|
435
484
|
function definitionFingerprint(candidate) {
|
|
436
485
|
const references = sortedReferences(candidate.input.references).map(
|
|
437
486
|
(reference) => ({
|
|
@@ -462,7 +511,6 @@ function definitionFingerprint(candidate) {
|
|
|
462
511
|
sources: [...input2.sources].sort(),
|
|
463
512
|
seeAlso: [...input2.seeAlso].sort(),
|
|
464
513
|
alignment,
|
|
465
|
-
status: input2.status,
|
|
466
514
|
aiAssisted: input2.aiAssisted,
|
|
467
515
|
body: normalizeText(input2.body),
|
|
468
516
|
glosses: input2.glosses,
|
|
@@ -574,6 +622,52 @@ function uniqueSorted(values) {
|
|
|
574
622
|
function sortedSources(values) {
|
|
575
623
|
return [...values].sort(compareSourceSpans);
|
|
576
624
|
}
|
|
625
|
+
function malformedReferences(lesson) {
|
|
626
|
+
if (!lesson.body?.includes("[[")) return [];
|
|
627
|
+
const prose = proseOnly(lesson.body, { keepReferences: true });
|
|
628
|
+
const found = [];
|
|
629
|
+
for (let at = prose.indexOf("[["); at !== -1; at = prose.indexOf("[[", at + 2)) {
|
|
630
|
+
if (lesson.body[at - 1] === "\\") continue;
|
|
631
|
+
const closed = /^\[\[[^[\]\n]*\]\]/.exec(prose.slice(at));
|
|
632
|
+
if (closed) {
|
|
633
|
+
at += closed[0].length - 2;
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
const near = lesson.body.slice(at, at + 40).split("\n")[0];
|
|
637
|
+
const line = lineOf(lesson.body, at) + (lesson.bodyLine ?? 1) - 1;
|
|
638
|
+
found.push(
|
|
639
|
+
diagnostic({
|
|
640
|
+
code: "malformed-reference",
|
|
641
|
+
message: `${lesson.lessonId} opens a reference that never closes, near ${JSON.stringify(near)}: close it with ]], as [[semantic.key]], on the same line.`,
|
|
642
|
+
lessonId: lesson.lessonId,
|
|
643
|
+
source: { ...lesson.source, line }
|
|
644
|
+
})
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
return found;
|
|
648
|
+
}
|
|
649
|
+
const FLOAT_KINDS = {
|
|
650
|
+
"eq-": "an equation",
|
|
651
|
+
"tab-": "a table",
|
|
652
|
+
"fig-": "a figure",
|
|
653
|
+
"dia-": "a diagram",
|
|
654
|
+
"ex-": "a worked-example set",
|
|
655
|
+
"check-": "a knowledge check",
|
|
656
|
+
"sec-": "a section",
|
|
657
|
+
"memo-": "a memo"
|
|
658
|
+
};
|
|
659
|
+
function keyHint(key) {
|
|
660
|
+
const reserved = reservedKeyPrefix(key);
|
|
661
|
+
if (reserved !== void 0) {
|
|
662
|
+
return `: [[${reserved}\u2026]] names ${FLOAT_KINDS[reserved]}, whose id is lower-case words and digits joined by "-"; ${JSON.stringify(key.slice(reserved.length))} is not one.`;
|
|
663
|
+
}
|
|
664
|
+
const bar = key.indexOf("|");
|
|
665
|
+
if (bar > 0) {
|
|
666
|
+
const target = key.slice(0, bar).trim();
|
|
667
|
+
return `: a [[\u2026]] reference takes no link text of its own \u2014 it reads as the entry's label. Write [[${target}]], with the words around it.`;
|
|
668
|
+
}
|
|
669
|
+
return ': a key is lower-case words and digits joined by "-" or "." (motion.launch-speed).';
|
|
670
|
+
}
|
|
577
671
|
function diagnostic(input) {
|
|
578
672
|
return { ...input, severity: "error" };
|
|
579
673
|
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KaTeX writes each formula twice: as MathML, whose `<annotation>` holds the
|
|
3
|
+
* LaTeX source, for assistive technology, and as the visible glyphs. Mark the
|
|
4
|
+
* MathML copy `data-pagefind-ignore`, so the site search indexes a formula
|
|
5
|
+
* once, as it reads, and never its LaTeX (`\explain{…}{…}`, `\dfrac`).
|
|
6
|
+
*/
|
|
7
|
+
export function markMathmlForSearch(node: any): void;
|
|
1
8
|
/**
|
|
2
9
|
* rehype-katex reports parse failures as non-fatal VFile messages and renders
|
|
3
10
|
* a `.katex-error` fallback. Turn those recovered failures into compiler
|
|
4
11
|
* errors so Astro cannot successfully build a lesson containing broken math.
|
|
12
|
+
* The pass every course runs right after KaTeX, it also keeps the LaTeX
|
|
13
|
+
* annotations out of the site search (`markMathmlForSearch`).
|
|
5
14
|
*/
|
|
6
15
|
export default function rehypeFailKatexErrors(): (tree: any, file: any) => void;
|
|
@@ -31,13 +31,32 @@ function katexErrors(node, output = []) {
|
|
|
31
31
|
return output;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* KaTeX writes each formula twice: as MathML, whose `<annotation>` holds the
|
|
36
|
+
* LaTeX source, for assistive technology, and as the visible glyphs. Mark the
|
|
37
|
+
* MathML copy `data-pagefind-ignore`, so the site search indexes a formula
|
|
38
|
+
* once, as it reads, and never its LaTeX (`\explain{…}{…}`, `\dfrac`).
|
|
39
|
+
*/
|
|
40
|
+
export function markMathmlForSearch(node) {
|
|
41
|
+
if (!node || typeof node !== 'object') return;
|
|
42
|
+
const classes = node.properties?.className;
|
|
43
|
+
if (node.type === 'element' && Array.isArray(classes) && classes.includes('katex-mathml')) {
|
|
44
|
+
node.properties.dataPagefindIgnore = '';
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(node.children)) node.children.forEach(markMathmlForSearch);
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
/**
|
|
35
51
|
* rehype-katex reports parse failures as non-fatal VFile messages and renders
|
|
36
52
|
* a `.katex-error` fallback. Turn those recovered failures into compiler
|
|
37
53
|
* errors so Astro cannot successfully build a lesson containing broken math.
|
|
54
|
+
* The pass every course runs right after KaTeX, it also keeps the LaTeX
|
|
55
|
+
* annotations out of the site search (`markMathmlForSearch`).
|
|
38
56
|
*/
|
|
39
57
|
export default function rehypeFailKatexErrors() {
|
|
40
58
|
return function failOnKatexErrors(tree, file) {
|
|
59
|
+
markMathmlForSearch(tree);
|
|
41
60
|
const errors = katexErrors(tree);
|
|
42
61
|
if (errors.length === 0) return;
|
|
43
62
|
|
|
@@ -69,6 +69,11 @@ const HEADING_TAG = /^h([1-6])$/;
|
|
|
69
69
|
const LINK_ICON_PATH =
|
|
70
70
|
'm12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42';
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* A heading's text as a reader hears it. KaTeX writes each formula twice —
|
|
74
|
+
* MathML, whose `<annotation>` holds the LaTeX, and the `aria-hidden` glyphs —
|
|
75
|
+
* so a formula is read once, from its MathML, without the LaTeX.
|
|
76
|
+
*/
|
|
72
77
|
function headingText(node) {
|
|
73
78
|
let text = '';
|
|
74
79
|
const walk = (current) => {
|
|
@@ -77,10 +82,15 @@ function headingText(node) {
|
|
|
77
82
|
text += String(current.value ?? '');
|
|
78
83
|
return;
|
|
79
84
|
}
|
|
85
|
+
if (current.type === 'element') {
|
|
86
|
+
const classes = current.properties?.className;
|
|
87
|
+
if (current.tagName === 'annotation') return;
|
|
88
|
+
if (Array.isArray(classes) && classes.includes('katex-html')) return;
|
|
89
|
+
}
|
|
80
90
|
if (Array.isArray(current.children)) current.children.forEach(walk);
|
|
81
91
|
};
|
|
82
92
|
walk(node);
|
|
83
|
-
return text.replace(/\s+/g, ' ').trim();
|
|
93
|
+
return text.replace(/[\u200b\u2061-\u2064]/g, '').replace(/\s+/g, ' ').trim();
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
// A heading's marker, e.g. `## Some title [collapsed]`, stripped from the
|
|
Binary file
|