explico 0.12.0 → 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 +1122 -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 +663 -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 +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 +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
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, extname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { courseFileStem } from "./export-pages.js";
|
|
5
|
+
import {
|
|
6
|
+
consoleLogger,
|
|
7
|
+
diagramsOf,
|
|
8
|
+
figuresOf,
|
|
9
|
+
imageExtension,
|
|
10
|
+
imageFile,
|
|
11
|
+
imagesOf,
|
|
12
|
+
needsCapture,
|
|
13
|
+
prepareChapters,
|
|
14
|
+
readBuiltPage
|
|
15
|
+
} from "./export-chapters.js";
|
|
16
|
+
import { checkItems } from "./latex/document.js";
|
|
17
|
+
import { serializeMarkdownNodes, serializeMarkdownPage } from "./markdown/serialize.js";
|
|
18
|
+
import { zip } from "./zip.js";
|
|
19
|
+
async function pagePictures(outputRoot, base, page, captured, assetDir) {
|
|
20
|
+
const pictures = /* @__PURE__ */ new Map();
|
|
21
|
+
const copy = async (key, src, name) => {
|
|
22
|
+
const file = imageFile(outputRoot, base, page.route, src);
|
|
23
|
+
pictures.set(key, { path: `${assetDir}/${name}${extname(file).toLowerCase()}`, content: await readFile(file) });
|
|
24
|
+
};
|
|
25
|
+
for (const figure of figuresOf(page.blocks)) {
|
|
26
|
+
if (figure.picture.kind === "image" && (!needsCapture(figure) || imageExtension(figure.picture.src) === ".svg")) {
|
|
27
|
+
await copy(figure.anchor, figure.picture.src, figure.anchor);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const png = captured?.get(figure.anchor)?.png;
|
|
31
|
+
if (png) pictures.set(figure.anchor, { path: `${assetDir}/${figure.anchor}.png`, content: png });
|
|
32
|
+
}
|
|
33
|
+
for (const diagram of diagramsOf(page.blocks)) {
|
|
34
|
+
const png = captured?.get(diagram.anchor)?.png;
|
|
35
|
+
if (png) pictures.set(diagram.anchor, { path: `${assetDir}/${diagram.anchor}.png`, content: png });
|
|
36
|
+
}
|
|
37
|
+
for (const image of imagesOf(page.blocks)) {
|
|
38
|
+
if (pictures.has(image.src)) continue;
|
|
39
|
+
const name = basename(new URL(image.src, "https://x.invalid/").pathname, extname(image.src)) || "image";
|
|
40
|
+
await copy(image.src, image.src, name);
|
|
41
|
+
}
|
|
42
|
+
return pictures;
|
|
43
|
+
}
|
|
44
|
+
function twoDigits(value) {
|
|
45
|
+
return String(value).padStart(2, "0");
|
|
46
|
+
}
|
|
47
|
+
function chapterFile(lesson) {
|
|
48
|
+
return `${twoDigits(lesson.part)}-${twoDigits(lesson.chapter)}-${lesson.slug.split("/").at(-1) ?? lesson.slug}.md`;
|
|
49
|
+
}
|
|
50
|
+
function partFile(part) {
|
|
51
|
+
return `${twoDigits(part.ordinal)}-00-${part.dir}.md`;
|
|
52
|
+
}
|
|
53
|
+
function relativeTo(files, route) {
|
|
54
|
+
return new Map([...files].map(([key, file]) => [key, key === route ? "" : file]));
|
|
55
|
+
}
|
|
56
|
+
async function writeMarkdownExports(directory, manifest, options = {}) {
|
|
57
|
+
if (!manifest.lessonExports.markdown) return;
|
|
58
|
+
const logger = options.logger ?? consoleLogger;
|
|
59
|
+
const outputRoot = fileURLToPath(directory);
|
|
60
|
+
const { base, chapters, captures } = options.prepared ?? await prepareChapters(outputRoot, manifest, { logger, projectRoot: options.projectRoot });
|
|
61
|
+
const canonicalBaseUrl = manifest.lessonExports.canonicalBaseUrl;
|
|
62
|
+
const course = manifest.about.material.name;
|
|
63
|
+
const version = manifest.about.knowledge.version;
|
|
64
|
+
const source = (route) => ({
|
|
65
|
+
course,
|
|
66
|
+
version,
|
|
67
|
+
...canonicalBaseUrl ? { url: `${canonicalBaseUrl}${route ? `${route}/` : ""}` } : {}
|
|
68
|
+
});
|
|
69
|
+
const chapterFiles = async (chapter, file, assetDir, documentFiles) => {
|
|
70
|
+
const { lesson, page } = chapter;
|
|
71
|
+
const pictures = await pagePictures(outputRoot, base, page, captures.get(page.route), assetDir);
|
|
72
|
+
const markdown = serializeMarkdownPage(
|
|
73
|
+
{
|
|
74
|
+
page,
|
|
75
|
+
documentFiles: relativeTo(documentFiles, page.route),
|
|
76
|
+
...canonicalBaseUrl ? { canonicalBaseUrl } : {},
|
|
77
|
+
pictures: new Map([...pictures].map(([key, picture]) => [key, picture.path])),
|
|
78
|
+
checkItems: checkItems(manifest, lesson),
|
|
79
|
+
source: source(page.route)
|
|
80
|
+
},
|
|
81
|
+
{ number: `${lesson.part}.${lesson.chapter}` }
|
|
82
|
+
);
|
|
83
|
+
return [
|
|
84
|
+
{ path: file, content: markdown },
|
|
85
|
+
...[...pictures.values()].map((picture) => ({ path: picture.path, content: picture.content }))
|
|
86
|
+
];
|
|
87
|
+
};
|
|
88
|
+
const pending = [];
|
|
89
|
+
const pack = (target, folder, entries) => {
|
|
90
|
+
const unique = new Map(entries.map((entry) => [entry.path, entry]));
|
|
91
|
+
pending.push({
|
|
92
|
+
target: join(outputRoot, "downloads", target),
|
|
93
|
+
content: zip([...unique.values()].map((entry) => ({ path: `${folder}/${entry.path}`, content: entry.content })))
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
for (const chapter of chapters) {
|
|
97
|
+
const name = chapter.lesson.slug.split("/").at(-1) ?? chapter.lesson.slug;
|
|
98
|
+
pack(
|
|
99
|
+
`${chapter.lesson.slug}-markdown.zip`,
|
|
100
|
+
`${name}-markdown`,
|
|
101
|
+
await chapterFiles(chapter, "lesson.md", "assets", /* @__PURE__ */ new Map([[chapter.page.route, "lesson.md"]]))
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const byId = new Map(chapters.map((chapter) => [chapter.lesson.id, chapter]));
|
|
105
|
+
const pageSlugs = new Set(manifest.pages.map((page) => page.slug));
|
|
106
|
+
const parts = manifest.parts.map((part) => ({
|
|
107
|
+
part,
|
|
108
|
+
chapters: part.lessons.flatMap((id) => byId.has(id) ? [byId.get(id)] : []),
|
|
109
|
+
overview: pageSlugs.has(`${part.dir}/index`) ? `${part.dir}/index` : void 0
|
|
110
|
+
})).filter((entry) => entry.chapters.length > 0);
|
|
111
|
+
const overviewMarkdown = async (entry, documentFiles, assetDir) => {
|
|
112
|
+
if (!entry.overview) {
|
|
113
|
+
return { markdown: serializeMarkdownNodes([{ type: "heading", depth: 1, children: [{ type: "text", value: entry.part.label }] }]), pictures: [] };
|
|
114
|
+
}
|
|
115
|
+
const { page } = await readBuiltPage(outputRoot, entry.overview);
|
|
116
|
+
const pictures = await pagePictures(outputRoot, base, page, void 0, assetDir);
|
|
117
|
+
const markdown = serializeMarkdownPage(
|
|
118
|
+
{
|
|
119
|
+
page,
|
|
120
|
+
documentFiles: relativeTo(documentFiles, page.route),
|
|
121
|
+
...canonicalBaseUrl ? { canonicalBaseUrl } : {},
|
|
122
|
+
pictures: new Map([...pictures].map(([key, picture]) => [key, picture.path])),
|
|
123
|
+
checkItems: /* @__PURE__ */ new Map(),
|
|
124
|
+
source: source(page.route)
|
|
125
|
+
},
|
|
126
|
+
{ number: `Part ${entry.part.ordinal}:` }
|
|
127
|
+
);
|
|
128
|
+
return { markdown, pictures: [...pictures.values()].map((picture) => ({ path: picture.path, content: picture.content })) };
|
|
129
|
+
};
|
|
130
|
+
const contents = (entry, files) => entry.chapters.map((chapter) => ({
|
|
131
|
+
type: "listItem",
|
|
132
|
+
spread: false,
|
|
133
|
+
children: [
|
|
134
|
+
{
|
|
135
|
+
type: "paragraph",
|
|
136
|
+
children: [
|
|
137
|
+
{
|
|
138
|
+
type: "link",
|
|
139
|
+
url: files.get(chapter.page.route) ?? "",
|
|
140
|
+
children: [{ type: "text", value: `${chapter.lesson.part}.${chapter.lesson.chapter} ${chapter.page.title}` }]
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}));
|
|
146
|
+
for (const entry of parts) {
|
|
147
|
+
const files = new Map([
|
|
148
|
+
...entry.overview ? [[entry.part.dir, "index.md"]] : [],
|
|
149
|
+
...entry.chapters.map((chapter) => [chapter.page.route, chapterFile(chapter.lesson)])
|
|
150
|
+
]);
|
|
151
|
+
const overview = await overviewMarkdown(entry, files, "assets/index");
|
|
152
|
+
const index = `${overview.markdown}
|
|
153
|
+
` + serializeMarkdownNodes([
|
|
154
|
+
{ type: "heading", depth: 2, children: [{ type: "text", value: "Contents" }] },
|
|
155
|
+
{ type: "list", ordered: false, spread: false, children: contents(entry, files) }
|
|
156
|
+
]);
|
|
157
|
+
const entries = [{ path: "index.md", content: index }, ...overview.pictures];
|
|
158
|
+
for (const chapter of entry.chapters) {
|
|
159
|
+
const file = chapterFile(chapter.lesson);
|
|
160
|
+
entries.push(...await chapterFiles(chapter, file, `assets/${file.replace(/\.md$/u, "")}`, files));
|
|
161
|
+
}
|
|
162
|
+
pack(`part/${entry.part.dir}-markdown.zip`, `${entry.part.dir}-markdown`, entries);
|
|
163
|
+
}
|
|
164
|
+
if (parts.length > 0) {
|
|
165
|
+
const files = new Map([
|
|
166
|
+
["", "index.md"],
|
|
167
|
+
...parts.flatMap((entry) => [
|
|
168
|
+
...entry.overview ? [[entry.part.dir, partFile(entry.part)]] : [],
|
|
169
|
+
...entry.chapters.map((chapter) => [chapter.page.route, chapterFile(chapter.lesson)])
|
|
170
|
+
])
|
|
171
|
+
]);
|
|
172
|
+
const entries = [];
|
|
173
|
+
for (const entry of parts) {
|
|
174
|
+
if (entry.overview) {
|
|
175
|
+
const file = partFile(entry.part);
|
|
176
|
+
const overview = await overviewMarkdown(entry, files, `assets/${file.replace(/\.md$/u, "")}`);
|
|
177
|
+
entries.push({ path: file, content: overview.markdown }, ...overview.pictures);
|
|
178
|
+
}
|
|
179
|
+
for (const chapter of entry.chapters) {
|
|
180
|
+
const file = chapterFile(chapter.lesson);
|
|
181
|
+
entries.push(...await chapterFiles(chapter, file, `assets/${file.replace(/\.md$/u, "")}`, files));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const index = serializeMarkdownNodes([
|
|
185
|
+
{ type: "heading", depth: 1, children: [{ type: "text", value: course }] },
|
|
186
|
+
{
|
|
187
|
+
type: "paragraph",
|
|
188
|
+
children: [
|
|
189
|
+
{ type: "text", value: `Content version ${version}.` },
|
|
190
|
+
...canonicalBaseUrl ? [
|
|
191
|
+
{ type: "text", value: " Interactive version: " },
|
|
192
|
+
{ type: "link", url: canonicalBaseUrl, children: [{ type: "text", value: canonicalBaseUrl }] },
|
|
193
|
+
{ type: "text", value: "." }
|
|
194
|
+
] : []
|
|
195
|
+
]
|
|
196
|
+
},
|
|
197
|
+
...manifest.about.material.summary ? [{ type: "paragraph", children: [{ type: "text", value: manifest.about.material.summary }] }] : [],
|
|
198
|
+
{ type: "heading", depth: 2, children: [{ type: "text", value: "Contents" }] },
|
|
199
|
+
{
|
|
200
|
+
type: "list",
|
|
201
|
+
ordered: false,
|
|
202
|
+
spread: false,
|
|
203
|
+
children: parts.map((entry) => ({
|
|
204
|
+
type: "listItem",
|
|
205
|
+
spread: false,
|
|
206
|
+
children: [
|
|
207
|
+
{
|
|
208
|
+
type: "paragraph",
|
|
209
|
+
children: entry.overview ? [
|
|
210
|
+
{
|
|
211
|
+
type: "link",
|
|
212
|
+
url: partFile(entry.part),
|
|
213
|
+
children: [{ type: "text", value: `Part ${entry.part.ordinal}: ${entry.part.label}` }]
|
|
214
|
+
}
|
|
215
|
+
] : [{ type: "text", value: `Part ${entry.part.ordinal}: ${entry.part.label}` }]
|
|
216
|
+
},
|
|
217
|
+
{ type: "list", ordered: false, spread: false, children: contents(entry, files) }
|
|
218
|
+
]
|
|
219
|
+
}))
|
|
220
|
+
}
|
|
221
|
+
]);
|
|
222
|
+
entries.push({ path: "index.md", content: index });
|
|
223
|
+
pack("course-markdown.zip", `${courseFileStem(manifest)}-markdown`, entries);
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
for (const file of pending) {
|
|
227
|
+
await mkdir(dirname(file.target), { recursive: true });
|
|
228
|
+
await writeFile(`${file.target}.tmp-${process.pid}`, file.content);
|
|
229
|
+
}
|
|
230
|
+
for (const file of pending) await rename(`${file.target}.tmp-${process.pid}`, file.target);
|
|
231
|
+
} finally {
|
|
232
|
+
await Promise.all(pending.map((file) => rm(`${file.target}.tmp-${process.pid}`, { force: true })));
|
|
233
|
+
}
|
|
234
|
+
logger.info(
|
|
235
|
+
`Markdown export: ${chapters.length} chapter(s), ${parts.length} part(s)` + (parts.length > 0 ? " and the course" : "") + "."
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
export {
|
|
239
|
+
writeMarkdownExports
|
|
240
|
+
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Manifest } from '../manifest.js';
|
|
2
2
|
import '../../curriculum/validation.js';
|
|
3
3
|
import '../../reference/consistency.js';
|
|
4
|
-
import '../../course-config-
|
|
4
|
+
import '../../course-config-rRtME5UV.js';
|
|
5
5
|
import '../../rules/catalog.js';
|
|
6
6
|
import '../../rules/course-rules.js';
|
|
7
7
|
import '../collections.js';
|
|
8
8
|
import '../../reference/types.js';
|
|
9
|
+
import '../../reference/gate-retired-keys.js';
|
|
9
10
|
import '../../reference/lint-ignore.js';
|
|
10
11
|
import '../../reference/equations-validate.js';
|
|
11
12
|
import '../../reference/tables-validate.js';
|
|
@@ -24,17 +25,25 @@ import '../../rules/waivers.js';
|
|
|
24
25
|
import '../knowledge-version.js';
|
|
25
26
|
import '../lesson-derivation.js';
|
|
26
27
|
|
|
27
|
-
/** Inputs for producing one standalone
|
|
28
|
+
/** Inputs for producing one standalone page from Astro's final HTML. */
|
|
28
29
|
interface StandaloneLessonInput {
|
|
29
30
|
readonly html: string;
|
|
30
31
|
readonly sourceFile: string;
|
|
31
32
|
readonly outputRoot: string;
|
|
33
|
+
/** The page's docs slug: a chapter's, or `index`, `motion/index`, `glossary`. */
|
|
32
34
|
readonly lessonSlug: string;
|
|
33
35
|
readonly canonicalBaseUrl?: string;
|
|
34
36
|
}
|
|
35
|
-
/** Build one fully inlined
|
|
37
|
+
/** Build one fully inlined page document without writing it. */
|
|
36
38
|
declare function buildStandaloneLessonHtml(input: StandaloneLessonInput): Promise<string>;
|
|
37
|
-
/**
|
|
39
|
+
/** Astro's built HTML file of a docs slug. */
|
|
40
|
+
declare function pageSourceFile(outputRoot: string, slug: string): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Emit the opt-in standalone HTML artifact of every chapter
|
|
43
|
+
* (`standalonePageSlugs`) from Astro's completed static output, at
|
|
44
|
+
* `downloads/<docs slug>.html`. A page that is not a chapter is in its
|
|
45
|
+
* part's and the course's files (`combined-html.ts`).
|
|
46
|
+
*/
|
|
38
47
|
declare function writeStandaloneLessonExports(directory: URL, manifest: Manifest): Promise<void>;
|
|
39
48
|
|
|
40
|
-
export { type StandaloneLessonInput, buildStandaloneLessonHtml, writeStandaloneLessonExports };
|
|
49
|
+
export { type StandaloneLessonInput, buildStandaloneLessonHtml, pageSourceFile, writeStandaloneLessonExports };
|
|
@@ -9,10 +9,14 @@ import {
|
|
|
9
9
|
import { Buffer } from "node:buffer";
|
|
10
10
|
import { dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { parse, serialize } from "parse5";
|
|
13
|
+
import { exportPageRoute, standalonePageSlugs } from "./export-pages.js";
|
|
12
14
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
attribute,
|
|
16
|
+
elements,
|
|
17
|
+
hasClass,
|
|
18
|
+
inferBasePrefix
|
|
19
|
+
} from "./html-tree.js";
|
|
16
20
|
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
|
17
21
|
const CSS_URL = /url\(\s*(?:(["'])(.*?)\1|([^)]*?))\s*\)/giu;
|
|
18
22
|
const CSS_IMPORT = /@import\s+(?:url\(\s*(?:(["'])(.*?)\1|([^)]*?))\s*\)|(["'])(.*?)\4)\s*([^;]*);/giu;
|
|
@@ -98,38 +102,11 @@ const STANDALONE_THEME_SCRIPT = `(() => {
|
|
|
98
102
|
});
|
|
99
103
|
apply('auto');
|
|
100
104
|
})();`;
|
|
101
|
-
function isElement(node) {
|
|
102
|
-
return "tagName" in node;
|
|
103
|
-
}
|
|
104
|
-
function childrenOf(node) {
|
|
105
|
-
return "childNodes" in node ? node.childNodes : [];
|
|
106
|
-
}
|
|
107
|
-
function walk(node, visit) {
|
|
108
|
-
visit(node);
|
|
109
|
-
for (const child of childrenOf(node)) walk(child, visit);
|
|
110
|
-
if (isElement(node) && node.tagName === "template") {
|
|
111
|
-
const template = node;
|
|
112
|
-
for (const child of template.content.childNodes) walk(child, visit);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function elements(root, predicate) {
|
|
116
|
-
const found = [];
|
|
117
|
-
walk(root, (node) => {
|
|
118
|
-
if (isElement(node) && (predicate?.(node) ?? true)) found.push(node);
|
|
119
|
-
});
|
|
120
|
-
return found;
|
|
121
|
-
}
|
|
122
105
|
function firstElement(root, tagName) {
|
|
123
106
|
const found = elements(root, (node) => node.tagName === tagName)[0];
|
|
124
107
|
if (!found) throw new Error(`Standalone HTML source has no <${tagName}> element.`);
|
|
125
108
|
return found;
|
|
126
109
|
}
|
|
127
|
-
function attribute(node, name) {
|
|
128
|
-
return node.attrs.find((entry) => entry.name === name)?.value;
|
|
129
|
-
}
|
|
130
|
-
function hasClass(node, name) {
|
|
131
|
-
return (attribute(node, "class") ?? "").split(/\s+/u).includes(name);
|
|
132
|
-
}
|
|
133
110
|
function setAttribute(node, name, value = "") {
|
|
134
111
|
const existing = node.attrs.find((entry) => entry.name === name);
|
|
135
112
|
if (existing) existing.value = value;
|
|
@@ -392,7 +369,7 @@ async function bundleBrowserGraph(entryPaths, requests, context) {
|
|
|
392
369
|
});
|
|
393
370
|
const javascript = result.outputFiles.find((file) => file.path.endsWith(".js"));
|
|
394
371
|
if (!javascript) {
|
|
395
|
-
throw new Error("Browser bundler produced no JavaScript for the standalone
|
|
372
|
+
throw new Error("Browser bundler produced no JavaScript for the standalone page.");
|
|
396
373
|
}
|
|
397
374
|
const unsupported = result.outputFiles.filter(
|
|
398
375
|
(file) => !file.path.endsWith(".js") && !file.path.endsWith(".css")
|
|
@@ -408,17 +385,6 @@ async function bundleBrowserGraph(entryPaths, requests, context) {
|
|
|
408
385
|
css: result.outputFiles.filter((file) => file.path.endsWith(".css")).map((file) => file.text)
|
|
409
386
|
};
|
|
410
387
|
}
|
|
411
|
-
function inferBasePrefix(document) {
|
|
412
|
-
for (const node of elements(document)) {
|
|
413
|
-
for (const name of ["href", "src", "component-url", "renderer-url"]) {
|
|
414
|
-
const value = attribute(node, name);
|
|
415
|
-
if (!value?.startsWith("/")) continue;
|
|
416
|
-
const marker = value.indexOf("/astro-assets/");
|
|
417
|
-
if (marker !== -1) return `${value.slice(0, marker)}/`;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
return "/";
|
|
421
|
-
}
|
|
422
388
|
function normalizedPagePath(pathname) {
|
|
423
389
|
return pathname.replace(/\/index\.html$/iu, "/").replace(/\.html$/iu, "").replace(/\/+$/u, "");
|
|
424
390
|
}
|
|
@@ -429,14 +395,17 @@ function rewriteCourseLink(href, lessonSlug, basePrefix, canonicalBaseUrl) {
|
|
|
429
395
|
if (hasScheme(trimmed)) {
|
|
430
396
|
throw new Error(`Standalone HTML cannot publish local navigation URL ${JSON.stringify(href)}.`);
|
|
431
397
|
}
|
|
432
|
-
const pagePath = `${basePrefix}${lessonSlug}/`.replace(
|
|
398
|
+
const pagePath = `${basePrefix}${exportPageRoute(lessonSlug)}/`.replace(
|
|
399
|
+
/\/{2,}/gu,
|
|
400
|
+
"/"
|
|
401
|
+
);
|
|
433
402
|
const resolved = new URL(trimmed, `https://explico.invalid${pagePath}`);
|
|
434
403
|
if (normalizedPagePath(resolved.pathname) === normalizedPagePath(pagePath)) {
|
|
435
404
|
return `${resolved.search}${resolved.hash}` || "#_top";
|
|
436
405
|
}
|
|
437
406
|
if (!canonicalBaseUrl) {
|
|
438
407
|
throw new Error(
|
|
439
|
-
`
|
|
408
|
+
`Page ${lessonSlug} links outside itself (${JSON.stringify(href)}), so \`lessonExports.canonicalBaseUrl\` is required for standalone HTML.`
|
|
440
409
|
);
|
|
441
410
|
}
|
|
442
411
|
let coursePath = resolved.pathname;
|
|
@@ -751,7 +720,7 @@ async function buildStandaloneLessonHtml(input) {
|
|
|
751
720
|
const outputRoot = resolve(input.outputRoot);
|
|
752
721
|
const sourceFile = resolve(input.sourceFile);
|
|
753
722
|
if (!insideRoot(sourceFile, outputRoot)) {
|
|
754
|
-
throw new Error("Standalone
|
|
723
|
+
throw new Error("Standalone page source must be inside Astro build output.");
|
|
755
724
|
}
|
|
756
725
|
const context = {
|
|
757
726
|
outputRoot,
|
|
@@ -778,8 +747,10 @@ async function buildStandaloneLessonHtml(input) {
|
|
|
778
747
|
auditStandaloneHtml(html, input.lessonSlug);
|
|
779
748
|
return html;
|
|
780
749
|
}
|
|
781
|
-
async function
|
|
782
|
-
const
|
|
750
|
+
async function pageSourceFile(outputRoot, slug) {
|
|
751
|
+
const route = exportPageRoute(slug);
|
|
752
|
+
const candidates = [join(outputRoot, route, "index.html")];
|
|
753
|
+
if (route) candidates.push(join(outputRoot, `${route}.html`));
|
|
783
754
|
for (const candidate of candidates) if (await isFile(candidate)) return candidate;
|
|
784
755
|
throw new Error(
|
|
785
756
|
`Standalone HTML could not find Astro's built page for ${slug}; tried ` + candidates.map((path) => relative(outputRoot, path)).join(", ")
|
|
@@ -789,9 +760,9 @@ async function writeStandaloneLessonExports(directory, manifest) {
|
|
|
789
760
|
if (!manifest.lessonExports.standaloneHtml) return;
|
|
790
761
|
const outputRoot = fileURLToPath(directory);
|
|
791
762
|
const pending = [];
|
|
792
|
-
for (const
|
|
793
|
-
const sourceFile = await
|
|
794
|
-
const target = join(outputRoot, "downloads", `${
|
|
763
|
+
for (const slug of standalonePageSlugs(manifest)) {
|
|
764
|
+
const sourceFile = await pageSourceFile(outputRoot, slug);
|
|
765
|
+
const target = join(outputRoot, "downloads", `${slug}.html`);
|
|
795
766
|
pending.push({
|
|
796
767
|
target,
|
|
797
768
|
temporary: `${target}.tmp-${process.pid}`,
|
|
@@ -799,7 +770,7 @@ async function writeStandaloneLessonExports(directory, manifest) {
|
|
|
799
770
|
html: await readFile(sourceFile, "utf8"),
|
|
800
771
|
sourceFile,
|
|
801
772
|
outputRoot,
|
|
802
|
-
lessonSlug:
|
|
773
|
+
lessonSlug: slug,
|
|
803
774
|
canonicalBaseUrl: manifest.lessonExports.canonicalBaseUrl
|
|
804
775
|
})
|
|
805
776
|
});
|
|
@@ -812,5 +783,6 @@ async function writeStandaloneLessonExports(directory, manifest) {
|
|
|
812
783
|
}
|
|
813
784
|
export {
|
|
814
785
|
buildStandaloneLessonHtml,
|
|
786
|
+
pageSourceFile,
|
|
815
787
|
writeStandaloneLessonExports
|
|
816
788
|
};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { HtmlElement, HtmlNode } from './html-tree.js';
|
|
2
|
+
import 'parse5';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* explico's static reading tree: a built page reduced to what a reader reads,
|
|
6
|
+
* with explico's meaning kept — a float with its number and captions, a
|
|
7
|
+
* cross-reference by its target, a check by its anchor. Print exporters (the
|
|
8
|
+
* LaTeX source now, Markdown later) serialize this tree; none of them
|
|
9
|
+
* re-walks `content/` or recomputes a number.
|
|
10
|
+
*
|
|
11
|
+
* It is read off Astro's static output, where every disclosure's body, every
|
|
12
|
+
* table row and every formula's TeX already sit in the markup, whatever the
|
|
13
|
+
* reader would see collapsed. What the static page does not hold — a lab's
|
|
14
|
+
* drawing, a check's answers — comes from the capture pass and the manifest.
|
|
15
|
+
*
|
|
16
|
+
* Math keeps the author's `\explain{key}{…}` and `\slot{key}{…}` markers (the
|
|
17
|
+
* KaTeX annotation, not the stripped `data-latex`): a printer that understands
|
|
18
|
+
* them (explico's LaTeX class) needs no stripping, and one that doesn't strips
|
|
19
|
+
* them itself.
|
|
20
|
+
*/
|
|
21
|
+
type RefKind = 'equation' | 'figure' | 'table' | 'diagram' | 'section' | 'example' | 'check' | 'memo' | 'anchor' | 'page';
|
|
22
|
+
/** A link target inside the course: a page route (base-relative, no slashes at the ends) and an anchor. */
|
|
23
|
+
interface CourseTarget {
|
|
24
|
+
readonly route: string;
|
|
25
|
+
readonly anchor: string;
|
|
26
|
+
}
|
|
27
|
+
type Inline = {
|
|
28
|
+
readonly kind: 'text';
|
|
29
|
+
readonly value: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly kind: 'emphasis';
|
|
32
|
+
readonly children: readonly Inline[];
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: 'strong';
|
|
35
|
+
readonly children: readonly Inline[];
|
|
36
|
+
} | {
|
|
37
|
+
readonly kind: 'superscript';
|
|
38
|
+
readonly children: readonly Inline[];
|
|
39
|
+
} | {
|
|
40
|
+
readonly kind: 'subscript';
|
|
41
|
+
readonly children: readonly Inline[];
|
|
42
|
+
} | {
|
|
43
|
+
readonly kind: 'code';
|
|
44
|
+
readonly value: string;
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: 'math';
|
|
47
|
+
readonly tex: string;
|
|
48
|
+
} | {
|
|
49
|
+
readonly kind: 'break';
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'link';
|
|
52
|
+
readonly href: string;
|
|
53
|
+
readonly children: readonly Inline[];
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: 'ref';
|
|
56
|
+
readonly refKind: RefKind;
|
|
57
|
+
readonly target: CourseTarget;
|
|
58
|
+
readonly children: readonly Inline[];
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: 'term';
|
|
61
|
+
readonly key: string;
|
|
62
|
+
readonly children: readonly Inline[];
|
|
63
|
+
} | {
|
|
64
|
+
readonly kind: 'cite';
|
|
65
|
+
readonly number: string;
|
|
66
|
+
readonly anchor: string;
|
|
67
|
+
readonly sourceId?: string;
|
|
68
|
+
readonly locator?: string;
|
|
69
|
+
} | {
|
|
70
|
+
readonly kind: 'memoMark';
|
|
71
|
+
readonly anchor: string;
|
|
72
|
+
readonly label: string;
|
|
73
|
+
} | {
|
|
74
|
+
readonly kind: 'noteMark';
|
|
75
|
+
readonly anchor: string;
|
|
76
|
+
readonly children: readonly Inline[];
|
|
77
|
+
};
|
|
78
|
+
interface TableCell {
|
|
79
|
+
readonly header: boolean;
|
|
80
|
+
readonly colspan: number;
|
|
81
|
+
readonly rowspan: number;
|
|
82
|
+
readonly align?: 'left' | 'center' | 'right';
|
|
83
|
+
readonly children: readonly Inline[];
|
|
84
|
+
}
|
|
85
|
+
interface DiagramNodeShape {
|
|
86
|
+
readonly x: number;
|
|
87
|
+
readonly y: number;
|
|
88
|
+
readonly width: number;
|
|
89
|
+
readonly height: number;
|
|
90
|
+
readonly radius: number;
|
|
91
|
+
readonly text?: string;
|
|
92
|
+
readonly tex?: string;
|
|
93
|
+
}
|
|
94
|
+
interface DiagramGeometry {
|
|
95
|
+
readonly width: number;
|
|
96
|
+
readonly height: number;
|
|
97
|
+
readonly nodes: readonly DiagramNodeShape[];
|
|
98
|
+
/** Each edge as its polyline, start to end; the arrowhead sits at the end. */
|
|
99
|
+
readonly edges: readonly (readonly (readonly [number, number])[])[];
|
|
100
|
+
}
|
|
101
|
+
/** What a figure shows: an image file, or something only a browser can draw. */
|
|
102
|
+
type FigurePicture = {
|
|
103
|
+
readonly kind: 'image';
|
|
104
|
+
readonly src: string;
|
|
105
|
+
readonly alt: string;
|
|
106
|
+
} | {
|
|
107
|
+
readonly kind: 'diagram';
|
|
108
|
+
readonly geometry: DiagramGeometry;
|
|
109
|
+
} | {
|
|
110
|
+
readonly kind: 'capture';
|
|
111
|
+
};
|
|
112
|
+
interface FloatBase {
|
|
113
|
+
/** The float's anchor id on its page (`fig-…`, `tab-…`, `dia-…`). */
|
|
114
|
+
readonly anchor: string;
|
|
115
|
+
readonly number: string;
|
|
116
|
+
readonly key?: string;
|
|
117
|
+
readonly short?: string;
|
|
118
|
+
/** The long caption. */
|
|
119
|
+
readonly caption: readonly Inline[];
|
|
120
|
+
}
|
|
121
|
+
type Block = {
|
|
122
|
+
readonly kind: 'heading';
|
|
123
|
+
readonly level: number;
|
|
124
|
+
readonly anchor?: string;
|
|
125
|
+
readonly number?: string;
|
|
126
|
+
readonly children: readonly Inline[];
|
|
127
|
+
} | {
|
|
128
|
+
readonly kind: 'paragraph';
|
|
129
|
+
readonly children: readonly Inline[];
|
|
130
|
+
} | {
|
|
131
|
+
readonly kind: 'list';
|
|
132
|
+
readonly ordered: boolean;
|
|
133
|
+
readonly start?: number;
|
|
134
|
+
readonly items: readonly (readonly Block[])[];
|
|
135
|
+
} | {
|
|
136
|
+
readonly kind: 'equation';
|
|
137
|
+
readonly anchor: string;
|
|
138
|
+
readonly number: string;
|
|
139
|
+
readonly key?: string;
|
|
140
|
+
readonly tex: string;
|
|
141
|
+
} | {
|
|
142
|
+
readonly kind: 'displayMath';
|
|
143
|
+
readonly tex: string;
|
|
144
|
+
} | (FloatBase & {
|
|
145
|
+
readonly kind: 'figure';
|
|
146
|
+
/** The live clause a lab writes after its caption; empty in the static page. */
|
|
147
|
+
readonly live: readonly Inline[];
|
|
148
|
+
readonly picture: FigurePicture;
|
|
149
|
+
/** The lab's own accessible label, when it gives one. */
|
|
150
|
+
readonly alt?: string;
|
|
151
|
+
}) | (FloatBase & {
|
|
152
|
+
readonly kind: 'table';
|
|
153
|
+
readonly head: readonly (readonly TableCell[])[];
|
|
154
|
+
readonly body: readonly (readonly TableCell[])[];
|
|
155
|
+
}) | (FloatBase & {
|
|
156
|
+
readonly kind: 'diagram';
|
|
157
|
+
readonly geometry: DiagramGeometry;
|
|
158
|
+
}) | {
|
|
159
|
+
readonly kind: 'memo';
|
|
160
|
+
readonly anchor: string;
|
|
161
|
+
readonly label: string;
|
|
162
|
+
readonly short?: string;
|
|
163
|
+
readonly title: readonly Inline[];
|
|
164
|
+
readonly children: readonly Block[];
|
|
165
|
+
} | {
|
|
166
|
+
readonly kind: 'note';
|
|
167
|
+
readonly anchor: string;
|
|
168
|
+
readonly title: string;
|
|
169
|
+
readonly children: readonly Block[];
|
|
170
|
+
} | {
|
|
171
|
+
readonly kind: 'aside';
|
|
172
|
+
readonly variant: string;
|
|
173
|
+
readonly title: readonly Inline[];
|
|
174
|
+
readonly children: readonly Block[];
|
|
175
|
+
} | {
|
|
176
|
+
readonly kind: 'fold';
|
|
177
|
+
readonly title?: readonly Inline[];
|
|
178
|
+
readonly children: readonly Block[];
|
|
179
|
+
} | {
|
|
180
|
+
readonly kind: 'examples';
|
|
181
|
+
readonly anchor: string;
|
|
182
|
+
readonly number: string;
|
|
183
|
+
readonly title: readonly Inline[];
|
|
184
|
+
readonly items: readonly {
|
|
185
|
+
readonly title: readonly Inline[];
|
|
186
|
+
readonly children: readonly Block[];
|
|
187
|
+
}[];
|
|
188
|
+
} | {
|
|
189
|
+
readonly kind: 'check';
|
|
190
|
+
readonly anchor: string;
|
|
191
|
+
readonly number: string;
|
|
192
|
+
readonly assessmentId: string;
|
|
193
|
+
readonly title: readonly Inline[];
|
|
194
|
+
} | {
|
|
195
|
+
readonly kind: 'quote';
|
|
196
|
+
readonly children: readonly Block[];
|
|
197
|
+
} | {
|
|
198
|
+
readonly kind: 'image';
|
|
199
|
+
readonly src: string;
|
|
200
|
+
readonly alt: string;
|
|
201
|
+
} | {
|
|
202
|
+
readonly kind: 'code';
|
|
203
|
+
readonly value: string;
|
|
204
|
+
} | {
|
|
205
|
+
readonly kind: 'rule';
|
|
206
|
+
} | {
|
|
207
|
+
readonly kind: 'references';
|
|
208
|
+
readonly items: readonly {
|
|
209
|
+
readonly number: string;
|
|
210
|
+
readonly anchor: string;
|
|
211
|
+
readonly children: readonly Inline[];
|
|
212
|
+
}[];
|
|
213
|
+
};
|
|
214
|
+
interface NotationEntry {
|
|
215
|
+
readonly key: string;
|
|
216
|
+
readonly anchor: string;
|
|
217
|
+
readonly symbolTex?: string;
|
|
218
|
+
readonly label: readonly Inline[];
|
|
219
|
+
readonly summary: readonly Inline[];
|
|
220
|
+
readonly units?: readonly Inline[];
|
|
221
|
+
readonly formulaTex?: string;
|
|
222
|
+
}
|
|
223
|
+
interface StaticPage {
|
|
224
|
+
/** The page's docs slug, e.g. `projectiles/optimal-launch-angle`. */
|
|
225
|
+
readonly slug: string;
|
|
226
|
+
/** Base-relative route of the page, e.g. `projectiles/optimal-launch-angle`. */
|
|
227
|
+
readonly route: string;
|
|
228
|
+
readonly title: string;
|
|
229
|
+
readonly blocks: readonly Block[];
|
|
230
|
+
/** "Notation used on this page", in the page's order. */
|
|
231
|
+
readonly notation: readonly NotationEntry[];
|
|
232
|
+
/** The notation list's page-specific remarks ("On this page, ′ is not an operator …"). */
|
|
233
|
+
readonly notationRemarks: readonly (readonly Inline[])[];
|
|
234
|
+
}
|
|
235
|
+
interface ReadContext {
|
|
236
|
+
readonly route: string;
|
|
237
|
+
readonly base: string;
|
|
238
|
+
}
|
|
239
|
+
/** All text below `node`, as a reader sees it: no screen-reader-only or hidden helpers. */
|
|
240
|
+
declare function readableText(node: HtmlNode): string;
|
|
241
|
+
/** Resolve a link to a course target, or `undefined` when it leaves the course. */
|
|
242
|
+
declare function courseTarget(href: string, context: ReadContext): CourseTarget | undefined;
|
|
243
|
+
/** Node boxes and edge polylines of a built `<Diagram>`'s SVG. */
|
|
244
|
+
declare function readDiagramGeometry(figure: HtmlElement): DiagramGeometry | undefined;
|
|
245
|
+
/**
|
|
246
|
+
* Inline content from an HTML fragment of a page — a lab's live caption
|
|
247
|
+
* clause, which only exists once the lab has run.
|
|
248
|
+
*/
|
|
249
|
+
declare function readInlineFragment(html: string, route: string, base?: string): Inline[];
|
|
250
|
+
/** Read the static reading tree of one built page. */
|
|
251
|
+
declare function readStaticPage(html: string, slug: string, route: string): StaticPage;
|
|
252
|
+
|
|
253
|
+
export { type Block, type CourseTarget, type DiagramGeometry, type DiagramNodeShape, type FigurePicture, type Inline, type NotationEntry, type RefKind, type StaticPage, type TableCell, courseTarget, readDiagramGeometry, readInlineFragment, readStaticPage, readableText };
|