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
|
@@ -2,7 +2,8 @@ import { effectiveAccentAllowlist } from "./base-accents.mjs";
|
|
|
2
2
|
import { effectiveOperatorAllowlist } from "./base-operators.mjs";
|
|
3
3
|
import { effectiveRomanLabelAllowlist } from "./base-roman-labels.mjs";
|
|
4
4
|
import { stripEquationLabels } from "./equations.mjs";
|
|
5
|
-
import { pageScope } from "./gate-math.js";
|
|
5
|
+
import { pageScope, scopeEntry } from "./gate-math.js";
|
|
6
|
+
import { isIndexEntryLatex } from "../compiler/content-schemas.js";
|
|
6
7
|
import {
|
|
7
8
|
collectDecorations,
|
|
8
9
|
collectOperators,
|
|
@@ -12,12 +13,12 @@ import {
|
|
|
12
13
|
resolveMathGlyphs
|
|
13
14
|
} from "./math-glyphs.mjs";
|
|
14
15
|
import { offVocabularyTokens } from "./units-vocab.js";
|
|
15
|
-
|
|
16
|
+
import { blankPropStrings } from "./gate-prop-text.js";
|
|
16
17
|
function stripCode(text) {
|
|
17
18
|
return text.replace(/```[\s\S]*?```/g, (block) => block.replace(/[^\n]/g, " ")).replace(/(^|[^`])`[^`\n]*`/g, (match) => match.replace(/[^\n]/g, " "));
|
|
18
19
|
}
|
|
19
20
|
function mathSpans(body) {
|
|
20
|
-
const searchable = stripCode(body);
|
|
21
|
+
const searchable = stripCode(blankPropStrings(body));
|
|
21
22
|
const spans = [];
|
|
22
23
|
for (const match of searchable.matchAll(/\$\$([\s\S]+?)\$\$/g)) {
|
|
23
24
|
spans.push({ latex: stripEquationLabels(match[1] ?? "") });
|
|
@@ -79,6 +80,7 @@ function checkGlyphUniqueness(input, out) {
|
|
|
79
80
|
}
|
|
80
81
|
function checkNotationUnits(input, out) {
|
|
81
82
|
for (const spec of input.specs) {
|
|
83
|
+
if (isIndexEntryLatex(spec.latex)) continue;
|
|
82
84
|
if (spec.units === void 0 && !spec.dimensionless) {
|
|
83
85
|
if (input.lintIgnore.matches("notation-units", spec.key)) continue;
|
|
84
86
|
out.push({
|
|
@@ -105,35 +107,6 @@ function checkNotationUnits(input, out) {
|
|
|
105
107
|
});
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
|
-
function checkConventionSingleDefinition(input, out) {
|
|
109
|
-
const lessonById = new Map(
|
|
110
|
-
input.catalog.lessons.map((lesson) => [lesson.id, lesson])
|
|
111
|
-
);
|
|
112
|
-
for (const lesson of input.notationInput.lessons) {
|
|
113
|
-
const body = lesson.body;
|
|
114
|
-
if (!CONVENTION_RE.test(body)) continue;
|
|
115
|
-
const meta = lessonById.get(lesson.lessonId);
|
|
116
|
-
const { signConventionKey, cashFlowPerspectiveCompetency } = input.conventions;
|
|
117
|
-
const anchoredByCompetency = [
|
|
118
|
-
...meta?.teaches ?? [],
|
|
119
|
-
...meta?.requires ?? []
|
|
120
|
-
].includes(cashFlowPerspectiveCompetency);
|
|
121
|
-
const anchoredByTerm = new RegExp(
|
|
122
|
-
`\\[\\[\\s*${signConventionKey}\\s*\\]\\]`
|
|
123
|
-
).test(body);
|
|
124
|
-
if (anchoredByCompetency || anchoredByTerm) continue;
|
|
125
|
-
if (input.lintIgnore.matches("convention-single-definition", lesson.lessonId)) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
out.push({
|
|
129
|
-
code: "convention-single-definition",
|
|
130
|
-
severity: "warning",
|
|
131
|
-
lessonId: lesson.lessonId,
|
|
132
|
-
file: lesson.source.file,
|
|
133
|
-
message: `${lesson.lessonId} states a sign / cash-flow-perspective convention in prose but does not resolve it to a single declaration ([[${signConventionKey}]] or ${cashFlowPerspectiveCompetency})`
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
110
|
function checkWeakLocal(input, out) {
|
|
138
111
|
for (const lesson of input.notationInput.lessons) {
|
|
139
112
|
const spans = mathSpans(lesson.body).map((span) => span.latex);
|
|
@@ -295,7 +268,7 @@ function checkOperatorExplained(input, out) {
|
|
|
295
268
|
lessonId: lesson.lessonId,
|
|
296
269
|
file: lesson.source.file,
|
|
297
270
|
glyph: operator.name,
|
|
298
|
-
message: `${lesson.lessonId}: operator ${JSON.stringify(display)} is neither on the course operator allowlist nor explained by a notation entry; give it a card or notation.local entry (latex ${JSON.stringify(display)}) introduced with [[key]], or add ${JSON.stringify(operator.name)} to
|
|
271
|
+
message: `${lesson.lessonId}: operator ${JSON.stringify(display)} is neither on the course operator allowlist nor explained by a notation entry; give it a card or notation.local entry (latex ${JSON.stringify(display)}) introduced with [[key]], or add ${JSON.stringify(operator.name)} to rules.N8.allow in the course config (course.config.ts, or the course.settings.json it reads)`
|
|
299
272
|
});
|
|
300
273
|
}
|
|
301
274
|
}
|
|
@@ -339,7 +312,7 @@ function checkRomanLabelExplained(input, out) {
|
|
|
339
312
|
lessonId: lesson.lessonId,
|
|
340
313
|
file: lesson.source.file,
|
|
341
314
|
glyph: label.name,
|
|
342
|
-
message: `${lesson.lessonId}: roman label ${JSON.stringify(`\\mathrm{${label.name}}`)} is neither on the course roman-label allowlist nor explained by a notation entry; give it a card or notation.local entry (latex ${JSON.stringify(`\\mathrm{${label.name}}`)}) introduced with [[key]], or add ${JSON.stringify(label.name)} to
|
|
315
|
+
message: `${lesson.lessonId}: roman label ${JSON.stringify(`\\mathrm{${label.name}}`)} is neither on the course roman-label allowlist nor explained by a notation entry; give it a card or notation.local entry (latex ${JSON.stringify(`\\mathrm{${label.name}}`)}) introduced with [[key]], or add ${JSON.stringify(label.name)} to rules.N10.allow in the course config (course.config.ts, or the course.settings.json it reads)`
|
|
343
316
|
});
|
|
344
317
|
}
|
|
345
318
|
}
|
|
@@ -349,7 +322,7 @@ function sharedScopeByKey(input) {
|
|
|
349
322
|
return new Map(
|
|
350
323
|
input.notationInput.sharedDefinitions.map((definition) => [
|
|
351
324
|
definition.key,
|
|
352
|
-
|
|
325
|
+
scopeEntry(definition)
|
|
353
326
|
])
|
|
354
327
|
);
|
|
355
328
|
}
|
|
@@ -363,11 +336,35 @@ function checkAccentExplained(input, out) {
|
|
|
363
336
|
for (const span of mathSpans(lesson.body)) {
|
|
364
337
|
let decorations;
|
|
365
338
|
try {
|
|
366
|
-
({ decorations } = resolveMathGlyphs(span.latex, scope, {
|
|
339
|
+
({ decorations } = resolveMathGlyphs(span.latex, scope, {
|
|
340
|
+
suspended,
|
|
341
|
+
derive: true
|
|
342
|
+
}));
|
|
367
343
|
} catch {
|
|
368
344
|
continue;
|
|
369
345
|
}
|
|
370
346
|
for (const decoration of decorations) {
|
|
347
|
+
if (decoration.wholeForm !== null && decoration.wholeFormExplicit) {
|
|
348
|
+
const notation = scope.find((entry) => entry.key === decoration.wholeForm)?.notation ?? "";
|
|
349
|
+
const carries = collectDecorations(notation).some(
|
|
350
|
+
(item) => item.name === decoration.name
|
|
351
|
+
);
|
|
352
|
+
const id = `swallowed:${decoration.token}`;
|
|
353
|
+
if (!carries && !reported.has(id)) {
|
|
354
|
+
reported.add(id);
|
|
355
|
+
if (!input.lintIgnore.matches("accent-swallowed", decoration.name)) {
|
|
356
|
+
out.push({
|
|
357
|
+
code: "accent-swallowed",
|
|
358
|
+
severity: "warning",
|
|
359
|
+
lessonId: lesson.lessonId,
|
|
360
|
+
file: lesson.source.file,
|
|
361
|
+
glyph: decoration.name,
|
|
362
|
+
message: `${lesson.lessonId}: \\explain{${decoration.wholeForm}}{${decoration.token}} wraps the decoration ${JSON.stringify(decoration.name)} in ${decoration.wholeForm}, whose latex (${notation}) has none, so its card cannot explain it; wrap only the symbol under it and give the decoration an operator entry (latex ${JSON.stringify(operatorEntryLatex(decoration.name))}), or name an entry for the decorated form (latex ${JSON.stringify(decoration.token)})`
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
371
368
|
if (decoration.wholeForm !== null || decoration.operator !== null) continue;
|
|
372
369
|
if (suspended.includes(decoration.name)) continue;
|
|
373
370
|
if (allowlist.has(decoration.name)) continue;
|
|
@@ -436,7 +433,6 @@ function checkOperatorSuspension(input, out) {
|
|
|
436
433
|
const CHECKS = [
|
|
437
434
|
checkGlyphUniqueness,
|
|
438
435
|
checkNotationUnits,
|
|
439
|
-
checkConventionSingleDefinition,
|
|
440
436
|
checkWeakLocal,
|
|
441
437
|
checkNotationSourceLocator,
|
|
442
438
|
checkGlossWantsPromoting,
|
|
@@ -459,7 +455,6 @@ function consistencyCounts(diagnostics) {
|
|
|
459
455
|
"glyph-unique-in-corpus": 0,
|
|
460
456
|
"notation-units": 0,
|
|
461
457
|
"units-vocab": 0,
|
|
462
|
-
"convention-single-definition": 0,
|
|
463
458
|
"weak-local": 0,
|
|
464
459
|
"notation-source-locator": 0,
|
|
465
460
|
"gloss-wants-promoting": 0,
|
|
@@ -468,6 +463,7 @@ function consistencyCounts(diagnostics) {
|
|
|
468
463
|
"operator-explained": 0,
|
|
469
464
|
"roman-label-explained": 0,
|
|
470
465
|
"accent-explained": 0,
|
|
466
|
+
"accent-swallowed": 0,
|
|
471
467
|
"operator-suspended": 0,
|
|
472
468
|
"operator-suspension-reason": 0,
|
|
473
469
|
"operator-shadowed": 0
|
|
@@ -2,13 +2,10 @@ import { compareDiagnostics } from "./references.js";
|
|
|
2
2
|
function validateNotationAlignment(registry, catalog) {
|
|
3
3
|
const diagnostics = [];
|
|
4
4
|
const competencies = new Map(
|
|
5
|
-
catalog.competencies.map((entry) => [
|
|
6
|
-
entry.id,
|
|
7
|
-
entry
|
|
8
|
-
])
|
|
5
|
+
catalog.competencies.map((entry) => [entry.id, entry])
|
|
9
6
|
);
|
|
10
7
|
const lessons = new Map(catalog.lessons.map((entry) => [entry.id, entry]));
|
|
11
|
-
const sources = new
|
|
8
|
+
const sources = new Set(catalog.sources.map((entry) => entry.id));
|
|
12
9
|
const trackPositions = buildTrackPositions(catalog, diagnostics);
|
|
13
10
|
for (const definition of registry.definitions) {
|
|
14
11
|
validateSources(definition, sources, diagnostics);
|
|
@@ -60,15 +57,6 @@ function validateNotationAlignment(registry, catalog) {
|
|
|
60
57
|
trackPositions,
|
|
61
58
|
diagnostics
|
|
62
59
|
);
|
|
63
|
-
if (definition.status === "reviewed" && (lesson.status !== "reviewed" || competency?.editorialStatus !== "reviewed")) {
|
|
64
|
-
diagnostics.push(
|
|
65
|
-
error(
|
|
66
|
-
"alignment-review-state",
|
|
67
|
-
`${definition.id} is reviewed while its owning lesson or competency is not reviewed`,
|
|
68
|
-
definition
|
|
69
|
-
)
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
60
|
}
|
|
73
61
|
return diagnostics.sort(compareDiagnostics);
|
|
74
62
|
}
|
|
@@ -129,8 +117,7 @@ function validateIntroductionOrder(definition, introductionLessonId, registry, t
|
|
|
129
117
|
}
|
|
130
118
|
function validateSources(definition, sources, diagnostics) {
|
|
131
119
|
for (const sourceId of definition.sources) {
|
|
132
|
-
|
|
133
|
-
if (!source) {
|
|
120
|
+
if (!sources.has(sourceId)) {
|
|
134
121
|
diagnostics.push(
|
|
135
122
|
error(
|
|
136
123
|
"alignment-unavailable",
|
|
@@ -138,14 +125,6 @@ function validateSources(definition, sources, diagnostics) {
|
|
|
138
125
|
definition
|
|
139
126
|
)
|
|
140
127
|
);
|
|
141
|
-
} else if (definition.status === "reviewed" && source.editorialStatus !== "reviewed") {
|
|
142
|
-
diagnostics.push(
|
|
143
|
-
error(
|
|
144
|
-
"alignment-review-state",
|
|
145
|
-
`${definition.id} is reviewed while source ${sourceId} is ${source.editorialStatus}`,
|
|
146
|
-
definition
|
|
147
|
-
)
|
|
148
|
-
);
|
|
149
128
|
}
|
|
150
129
|
}
|
|
151
130
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ManifestDerivedNotation } from '../compiler/manifest.js';
|
|
2
|
+
import { GlyphScopeEntry } from './gloss.js';
|
|
3
|
+
import { DerivedUse, NotationDefinitionRecord } from './types.js';
|
|
4
|
+
import '../curriculum/validation.js';
|
|
5
|
+
import './consistency.js';
|
|
6
|
+
import '../course-config-rRtME5UV.js';
|
|
7
|
+
import '../rules/catalog.js';
|
|
8
|
+
import '../rules/course-rules.js';
|
|
9
|
+
import '../compiler/collections.js';
|
|
10
|
+
import './gate-retired-keys.js';
|
|
11
|
+
import './lint-ignore.js';
|
|
12
|
+
import './equations-validate.js';
|
|
13
|
+
import './tables-validate.js';
|
|
14
|
+
import './keyed-float-validate.js';
|
|
15
|
+
import './figures-validate.js';
|
|
16
|
+
import './diagrams-validate.js';
|
|
17
|
+
import './examples-validate.js';
|
|
18
|
+
import './folds-validate.js';
|
|
19
|
+
import './knowledge-checks-validate.js';
|
|
20
|
+
import './raw-floats-validate.js';
|
|
21
|
+
import './lab-katex-validate.js';
|
|
22
|
+
import './gate-math.js';
|
|
23
|
+
import './mixed-content.js';
|
|
24
|
+
import '../rules/waivers.js';
|
|
25
|
+
import '../compiler/knowledge-version.js';
|
|
26
|
+
import '../compiler/lesson-derivation.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Derived cards (rule N23) as the page payload carries them: each derived
|
|
30
|
+
* card a page's math uses, composed from its base and index entries by
|
|
31
|
+
* `derived-notation.mjs`, with the glyph table its formula resolves against.
|
|
32
|
+
* The manifest builds the ones lesson-body and assessment math use; the page
|
|
33
|
+
* payload (`page-notation.ts`) builds the ones a lab or a diagram registers
|
|
34
|
+
* while it renders, the same way.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The derived cards for `uses`, sorted by key. `records` holds the page's
|
|
39
|
+
* entries by key, local ones winning; a use whose base or index entry is not
|
|
40
|
+
* there is skipped. A card's formula resolves against the derived symbol,
|
|
41
|
+
* the index entry's glosses, the base, and the cards the formula names.
|
|
42
|
+
*/
|
|
43
|
+
declare function derivedNotationEntries(uses: readonly DerivedUse[], records: ReadonlyMap<string, NotationDefinitionRecord>, cardsByKey: ReadonlyMap<string, GlyphScopeEntry>): ManifestDerivedNotation[];
|
|
44
|
+
|
|
45
|
+
export { derivedNotationEntries };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { composeDerivedEntry } from "./derived-notation.mjs";
|
|
2
|
+
import { scopeEntry } from "./gate-math.js";
|
|
3
|
+
import { formulaExplainKeys } from "./gloss.js";
|
|
4
|
+
import { baseSymbolLatex, derivedNotationLatex } from "./math-glyphs.mjs";
|
|
5
|
+
function derivedNotationEntries(uses, records, cardsByKey) {
|
|
6
|
+
const derived = /* @__PURE__ */ new Map();
|
|
7
|
+
for (const use of uses) {
|
|
8
|
+
if (derived.has(use.key)) continue;
|
|
9
|
+
const base = records.get(use.base);
|
|
10
|
+
const index = records.get(use.index);
|
|
11
|
+
if (!base || !index) continue;
|
|
12
|
+
const notation = derivedNotationLatex(base.notation, index.notation);
|
|
13
|
+
const entry = composeDerivedEntry({
|
|
14
|
+
key: use.key,
|
|
15
|
+
notation,
|
|
16
|
+
baseLatex: baseSymbolLatex(base.notation),
|
|
17
|
+
base,
|
|
18
|
+
index: {
|
|
19
|
+
...index,
|
|
20
|
+
...index.formulaTemplate === void 0 ? {} : { formula: index.formulaTemplate }
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
const formulaScope = [
|
|
24
|
+
{ key: use.key, notation },
|
|
25
|
+
...index.glosses.map((gloss) => ({ key: gloss.key, notation: gloss.notation })),
|
|
26
|
+
scopeEntry(base)
|
|
27
|
+
];
|
|
28
|
+
for (const key of formulaExplainKeys(entry.formula ?? "")) {
|
|
29
|
+
if (formulaScope.some((row) => row.key === key)) continue;
|
|
30
|
+
const card = records.get(key) ?? cardsByKey.get(key);
|
|
31
|
+
if (card) formulaScope.push(scopeEntry(card));
|
|
32
|
+
}
|
|
33
|
+
derived.set(use.key, {
|
|
34
|
+
key: entry.key,
|
|
35
|
+
base: entry.base,
|
|
36
|
+
index: entry.index,
|
|
37
|
+
notation: entry.notation,
|
|
38
|
+
label: entry.label,
|
|
39
|
+
summary: entry.summary,
|
|
40
|
+
glosses: [...index.glosses],
|
|
41
|
+
formulaScope,
|
|
42
|
+
...entry.units === void 0 ? {} : { units: entry.units },
|
|
43
|
+
...entry.formula === void 0 ? {} : { formula: entry.formula }
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return [...derived.values()].sort((left, right) => left.key.localeCompare(right.key));
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
derivedNotationEntries
|
|
50
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A label as it reads inside a sentence: its first letter lowercased unless
|
|
3
|
+
* the first word is an acronym (`OLS estimate`) — the same rule
|
|
4
|
+
* `composeHeaderLabel` applies to an operator's operand.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} label
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
export function inSentence(label: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* A derived card's title: the index entry's `reads` with `{base}` replaced by
|
|
12
|
+
* the base's label (lowercased in mid-sentence), or `"<index label> of
|
|
13
|
+
* <base label>"` when the index entry gives no `reads`.
|
|
14
|
+
*
|
|
15
|
+
* @param {{ label: string, reads?: string }} index
|
|
16
|
+
* @param {string} baseLabel
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
export function derivedLabel(index: {
|
|
20
|
+
label: string;
|
|
21
|
+
reads?: string;
|
|
22
|
+
}, baseLabel: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* An index entry's formula with `{base}` bound: to the base symbol under its
|
|
25
|
+
* own `\explain`, for a derived card, so the reader opens the base's card
|
|
26
|
+
* from it.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} formula
|
|
29
|
+
* @param {string} baseKey
|
|
30
|
+
* @param {string} baseLatex
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
export function derivedFormula(formula: string, baseKey: string, baseLatex: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* An index entry's own formula, shown on the index entry's card and checked
|
|
36
|
+
* by the formula gate: `{base}` becomes a placeholder square bound to the
|
|
37
|
+
* entry itself, as an operator entry is shown over a square.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} formula
|
|
40
|
+
* @param {string} ownKey
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
export function indexEntryFormula(formula: string, ownKey: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {object} DerivedSource
|
|
46
|
+
* @property {string} key
|
|
47
|
+
* @property {string} notation
|
|
48
|
+
* @property {string} label
|
|
49
|
+
* @property {string} [summary]
|
|
50
|
+
* @property {string} [units]
|
|
51
|
+
* @property {string} [reads]
|
|
52
|
+
* @property {string} [formula]
|
|
53
|
+
* @property {readonly { key: string, notation: string, label: string, units?: string, ownerKey: string }[]} [glosses]
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* The derived entry for one (base, index) pair. `notation` is the indexed
|
|
57
|
+
* symbol, from `derivedNotationLatex`.
|
|
58
|
+
*
|
|
59
|
+
* @param {{ key: string, notation: string, baseLatex: string, base: DerivedSource, index: DerivedSource }} input
|
|
60
|
+
*/
|
|
61
|
+
export function composeDerivedEntry({ key, notation, baseLatex, base, index }: {
|
|
62
|
+
key: string;
|
|
63
|
+
notation: string;
|
|
64
|
+
baseLatex: string;
|
|
65
|
+
base: DerivedSource;
|
|
66
|
+
index: DerivedSource;
|
|
67
|
+
}): {
|
|
68
|
+
formula?: string | undefined;
|
|
69
|
+
units?: string | undefined;
|
|
70
|
+
key: string;
|
|
71
|
+
notation: string;
|
|
72
|
+
base: string;
|
|
73
|
+
index: string;
|
|
74
|
+
label: string;
|
|
75
|
+
summary: string;
|
|
76
|
+
glosses: {
|
|
77
|
+
key: string;
|
|
78
|
+
notation: string;
|
|
79
|
+
label: string;
|
|
80
|
+
units?: string;
|
|
81
|
+
ownerKey: string;
|
|
82
|
+
}[];
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Derived notation entries (rule N23): the card for a symbol with a subscript
|
|
86
|
+
* that an **index entry** explains — `\mathbf{v}_0` from the base entry
|
|
87
|
+
* `velocity-vector` (`\mathbf{v}`) and the index entry `motion.initial`
|
|
88
|
+
* (`{}_0`, "Initial value").
|
|
89
|
+
*
|
|
90
|
+
* The resolver writes one `\explain` around the whole indexed symbol, with
|
|
91
|
+
* the key `derivedNotationKey(base, index)` (`math-glyphs.mjs`); the build
|
|
92
|
+
* materializes the entry from the two, here, so that everything downstream
|
|
93
|
+
* treats it as one more card. What the reader sees first is about the
|
|
94
|
+
* indexed symbol; the base's own card is one level deeper, through the base
|
|
95
|
+
* symbol on the card's formula line:
|
|
96
|
+
*
|
|
97
|
+
* | Field | From |
|
|
98
|
+
* | ------- | ------------------------------------------- |
|
|
99
|
+
* | symbol | the base's symbol plus the index |
|
|
100
|
+
* | title | the index entry's `reads`, the base's label |
|
|
101
|
+
* | summary | the index entry's `meaning`, never the base's |
|
|
102
|
+
* | formula | the index entry's `formula`, `{base}` bound |
|
|
103
|
+
* | units | the base entry |
|
|
104
|
+
*
|
|
105
|
+
* Plain JavaScript with no parser, so the remark pass and the manifest share
|
|
106
|
+
* it; `derivedNotationLatex` in `math-glyphs.mjs` builds the symbol.
|
|
107
|
+
*/
|
|
108
|
+
/** The indexed symbol, in an index entry's `reads` and `formula`. */
|
|
109
|
+
export const BASE_PLACEHOLDER: "{base}";
|
|
110
|
+
export type DerivedSource = {
|
|
111
|
+
key: string;
|
|
112
|
+
notation: string;
|
|
113
|
+
label: string;
|
|
114
|
+
summary?: string | undefined;
|
|
115
|
+
units?: string | undefined;
|
|
116
|
+
reads?: string | undefined;
|
|
117
|
+
formula?: string | undefined;
|
|
118
|
+
glosses?: readonly {
|
|
119
|
+
key: string;
|
|
120
|
+
notation: string;
|
|
121
|
+
label: string;
|
|
122
|
+
units?: string;
|
|
123
|
+
ownerKey: string;
|
|
124
|
+
}[] | undefined;
|
|
125
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derived notation entries (rule N23): the card for a symbol with a subscript
|
|
3
|
+
* that an **index entry** explains — `\mathbf{v}_0` from the base entry
|
|
4
|
+
* `velocity-vector` (`\mathbf{v}`) and the index entry `motion.initial`
|
|
5
|
+
* (`{}_0`, "Initial value").
|
|
6
|
+
*
|
|
7
|
+
* The resolver writes one `\explain` around the whole indexed symbol, with
|
|
8
|
+
* the key `derivedNotationKey(base, index)` (`math-glyphs.mjs`); the build
|
|
9
|
+
* materializes the entry from the two, here, so that everything downstream
|
|
10
|
+
* treats it as one more card. What the reader sees first is about the
|
|
11
|
+
* indexed symbol; the base's own card is one level deeper, through the base
|
|
12
|
+
* symbol on the card's formula line:
|
|
13
|
+
*
|
|
14
|
+
* | Field | From |
|
|
15
|
+
* | ------- | ------------------------------------------- |
|
|
16
|
+
* | symbol | the base's symbol plus the index |
|
|
17
|
+
* | title | the index entry's `reads`, the base's label |
|
|
18
|
+
* | summary | the index entry's `meaning`, never the base's |
|
|
19
|
+
* | formula | the index entry's `formula`, `{base}` bound |
|
|
20
|
+
* | units | the base entry |
|
|
21
|
+
*
|
|
22
|
+
* Plain JavaScript with no parser, so the remark pass and the manifest share
|
|
23
|
+
* it; `derivedNotationLatex` in `math-glyphs.mjs` builds the symbol.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** The indexed symbol, in an index entry's `reads` and `formula`. */
|
|
27
|
+
export const BASE_PLACEHOLDER = '{base}';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A label as it reads inside a sentence: its first letter lowercased unless
|
|
31
|
+
* the first word is an acronym (`OLS estimate`) — the same rule
|
|
32
|
+
* `composeHeaderLabel` applies to an operator's operand.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} label
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
export function inSentence(label) {
|
|
38
|
+
const [first = ''] = label.split(/\s+/u);
|
|
39
|
+
if (first.length > 1 && first === first.toUpperCase()) return label;
|
|
40
|
+
return label.charAt(0).toLowerCase() + label.slice(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A derived card's title: the index entry's `reads` with `{base}` replaced by
|
|
45
|
+
* the base's label (lowercased in mid-sentence), or `"<index label> of
|
|
46
|
+
* <base label>"` when the index entry gives no `reads`.
|
|
47
|
+
*
|
|
48
|
+
* @param {{ label: string, reads?: string }} index
|
|
49
|
+
* @param {string} baseLabel
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
export function derivedLabel(index, baseLabel) {
|
|
53
|
+
const reads = index.reads ?? `${index.label} of ${BASE_PLACEHOLDER}`;
|
|
54
|
+
const at = reads.indexOf(BASE_PLACEHOLDER);
|
|
55
|
+
const filled = reads
|
|
56
|
+
.split(BASE_PLACEHOLDER)
|
|
57
|
+
.join(at === 0 ? baseLabel : inSentence(baseLabel));
|
|
58
|
+
// `{base}` first in a later position, or a `reads` that starts lowercase.
|
|
59
|
+
return filled.charAt(0).toUpperCase() + filled.slice(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* An index entry's formula with `{base}` bound: to the base symbol under its
|
|
64
|
+
* own `\explain`, for a derived card, so the reader opens the base's card
|
|
65
|
+
* from it.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} formula
|
|
68
|
+
* @param {string} baseKey
|
|
69
|
+
* @param {string} baseLatex
|
|
70
|
+
* @returns {string}
|
|
71
|
+
*/
|
|
72
|
+
export function derivedFormula(formula, baseKey, baseLatex) {
|
|
73
|
+
return formula.split(BASE_PLACEHOLDER).join(`\\explain{${baseKey}}{${baseLatex}}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* An index entry's own formula, shown on the index entry's card and checked
|
|
78
|
+
* by the formula gate: `{base}` becomes a placeholder square bound to the
|
|
79
|
+
* entry itself, as an operator entry is shown over a square.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} formula
|
|
82
|
+
* @param {string} ownKey
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
export function indexEntryFormula(formula, ownKey) {
|
|
86
|
+
return formula.split(BASE_PLACEHOLDER).join(`\\explain{${ownKey}}{\\square}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @typedef {object} DerivedSource
|
|
91
|
+
* @property {string} key
|
|
92
|
+
* @property {string} notation
|
|
93
|
+
* @property {string} label
|
|
94
|
+
* @property {string} [summary]
|
|
95
|
+
* @property {string} [units]
|
|
96
|
+
* @property {string} [reads]
|
|
97
|
+
* @property {string} [formula]
|
|
98
|
+
* @property {readonly { key: string, notation: string, label: string, units?: string, ownerKey: string }[]} [glosses]
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The derived entry for one (base, index) pair. `notation` is the indexed
|
|
103
|
+
* symbol, from `derivedNotationLatex`.
|
|
104
|
+
*
|
|
105
|
+
* @param {{ key: string, notation: string, baseLatex: string, base: DerivedSource, index: DerivedSource }} input
|
|
106
|
+
*/
|
|
107
|
+
export function composeDerivedEntry({ key, notation, baseLatex, base, index }) {
|
|
108
|
+
return {
|
|
109
|
+
key,
|
|
110
|
+
notation,
|
|
111
|
+
base: base.key,
|
|
112
|
+
index: index.key,
|
|
113
|
+
label: derivedLabel(index, base.label),
|
|
114
|
+
summary: index.summary ?? '',
|
|
115
|
+
glosses: [...(index.glosses ?? [])],
|
|
116
|
+
...(base.units === undefined ? {} : { units: base.units }),
|
|
117
|
+
...(index.formula === undefined
|
|
118
|
+
? {}
|
|
119
|
+
: { formula: derivedFormula(index.formula, base.key, baseLatex) }),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { M as ManifestRules } from '../course-config-rRtME5UV.js';
|
|
2
|
+
import '../rules/catalog.js';
|
|
3
|
+
import '../rules/course-rules.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Rule V8: a diagram is no wider than the lesson column.
|
|
7
|
+
*
|
|
8
|
+
* `Diagram.astro` lays a diagram out at build time and so knows its width, but
|
|
9
|
+
* not the page it is on or what the course allows, and a diagram drawn by a
|
|
10
|
+
* wrapper component (`<CourseDiagram kind="…">`) never shows its nodes to the
|
|
11
|
+
* manifest walk. So the check runs on what was rendered: the component marks
|
|
12
|
+
* a diagram wider than {@link diagramWidthLimit} with `data-diagram-too-wide`,
|
|
13
|
+
* `compiler/astro.ts` finds each mark with {@link oversizedDiagrams} on every
|
|
14
|
+
* built page and reports a `dia-too-wide` warning with the lesson file and the
|
|
15
|
+
* diagram id, and `content warnings` lists what the last build reported
|
|
16
|
+
* (`compiler/render-diagnostics.ts`). Under `astro dev` the component prints
|
|
17
|
+
* the same message as it renders.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The lesson column at a 1366px-wide window, the most common laptop screen
|
|
22
|
+
* (632px at 1280, 832px at 1920), unless the course sets
|
|
23
|
+
* `rules.V8.columnWidth`.
|
|
24
|
+
*/
|
|
25
|
+
declare const DEFAULT_COLUMN_WIDTH = 707;
|
|
26
|
+
/**
|
|
27
|
+
* The widest a diagram may lay out, or `undefined` when nothing checks it: rule
|
|
28
|
+
* V8 is off, or the diagram says `warnAboveWidth={false}`. A number in
|
|
29
|
+
* `warnAboveWidth` replaces the course's column width for that diagram.
|
|
30
|
+
*/
|
|
31
|
+
declare function diagramWidthLimit(warnAboveWidth: number | false | undefined, rules: Pick<ManifestRules, 'disabled' | 'settings'> | undefined): number | undefined;
|
|
32
|
+
/** What to tell the author about one diagram wider than its limit. */
|
|
33
|
+
declare function diagramTooWideMessage(id: string, width: number, limit: number): string;
|
|
34
|
+
/** A diagram on a built page that is wider than its limit. */
|
|
35
|
+
interface OversizedDiagram {
|
|
36
|
+
readonly code: 'dia-too-wide';
|
|
37
|
+
/** The diagram's `id`. */
|
|
38
|
+
readonly key: string;
|
|
39
|
+
readonly width: number;
|
|
40
|
+
readonly limit: number;
|
|
41
|
+
}
|
|
42
|
+
/** The diagrams `Diagram.astro` marked too wide in `html`. */
|
|
43
|
+
declare function oversizedDiagrams(html: string): OversizedDiagram[];
|
|
44
|
+
|
|
45
|
+
export { DEFAULT_COLUMN_WIDTH, type OversizedDiagram, diagramTooWideMessage, diagramWidthLimit, oversizedDiagrams };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { parse } from "parse5";
|
|
2
|
+
const DEFAULT_COLUMN_WIDTH = 707;
|
|
3
|
+
function diagramWidthLimit(warnAboveWidth, rules) {
|
|
4
|
+
if (warnAboveWidth === false || rules?.disabled.includes("V8")) return void 0;
|
|
5
|
+
if (warnAboveWidth !== void 0) return warnAboveWidth;
|
|
6
|
+
const columnWidth = rules?.settings.V8?.columnWidth;
|
|
7
|
+
return typeof columnWidth === "number" ? columnWidth : DEFAULT_COLUMN_WIDTH;
|
|
8
|
+
}
|
|
9
|
+
function diagramTooWideMessage(id, width, limit) {
|
|
10
|
+
return `Diagram "${id}" lays out ${Math.round(width)}px wide, wider than the ${limit}px it may take (rule V8), so a reader has to scroll it or widen it to see it whole. Try direction="TB", a shorter label, or a narrower node \`width\` (its label wraps inside it); set warnAboveWidth={false} only when the diagram is meant to scroll.`;
|
|
11
|
+
}
|
|
12
|
+
function isElement(node) {
|
|
13
|
+
return "tagName" in node;
|
|
14
|
+
}
|
|
15
|
+
function attribute(node, name) {
|
|
16
|
+
return node.attrs.find((attr) => attr.name === name)?.value;
|
|
17
|
+
}
|
|
18
|
+
function viewportKey(node) {
|
|
19
|
+
for (const child of "childNodes" in node ? node.childNodes : []) {
|
|
20
|
+
if (!isElement(child)) continue;
|
|
21
|
+
const match = /^diagram-(.+)-viewport$/.exec(attribute(child, "id") ?? "");
|
|
22
|
+
if (match) return match[1];
|
|
23
|
+
const inner = viewportKey(child);
|
|
24
|
+
if (inner) return inner;
|
|
25
|
+
}
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
function oversizedDiagrams(html) {
|
|
29
|
+
if (!html.includes("data-diagram-too-wide")) return [];
|
|
30
|
+
const found = [];
|
|
31
|
+
const visit = (node) => {
|
|
32
|
+
for (const child of "childNodes" in node ? node.childNodes : []) {
|
|
33
|
+
if (!isElement(child)) continue;
|
|
34
|
+
const limit = attribute(child, "data-diagram-too-wide");
|
|
35
|
+
if (limit !== void 0) {
|
|
36
|
+
found.push({
|
|
37
|
+
code: "dia-too-wide",
|
|
38
|
+
key: viewportKey(child) ?? attribute(child, "id") ?? "",
|
|
39
|
+
width: Number(attribute(child, "data-diagram-width")),
|
|
40
|
+
limit: Number(limit)
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
visit(child);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
visit(parse(html));
|
|
47
|
+
return found;
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
DEFAULT_COLUMN_WIDTH,
|
|
51
|
+
diagramTooWideMessage,
|
|
52
|
+
diagramWidthLimit,
|
|
53
|
+
oversizedDiagrams
|
|
54
|
+
};
|