explico 0.13.1 → 0.15.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 +990 -1
- package/README.md +200 -30
- package/dist/compiler/astro.d.ts +15 -2
- package/dist/compiler/astro.js +40 -11
- package/dist/compiler/cli-main.d.ts +100 -0
- package/dist/compiler/cli-main.js +433 -0
- package/dist/compiler/cli-reference.d.ts +50 -0
- package/dist/compiler/cli-reference.js +213 -0
- package/dist/compiler/cli-verbs.d.ts +45 -0
- package/dist/compiler/cli-verbs.js +224 -0
- package/dist/compiler/cli-verify.d.ts +51 -0
- package/dist/compiler/cli-verify.js +151 -0
- package/dist/compiler/cli.d.ts +408 -24
- package/dist/compiler/cli.js +1328 -8
- package/dist/compiler/collections.d.ts +88 -1
- package/dist/compiler/collections.js +170 -25
- package/dist/compiler/content-release.d.ts +49 -0
- package/dist/compiler/content-release.js +63 -0
- package/dist/compiler/content-schemas.d.ts +2 -2
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +105 -1
- package/dist/compiler/decks.d.ts +161 -0
- package/dist/compiler/decks.js +235 -0
- package/dist/compiler/exports/combined-html.d.ts +5 -1
- package/dist/compiler/exports/deck-fit.d.ts +55 -0
- package/dist/compiler/exports/deck-fit.js +90 -0
- package/dist/compiler/exports/export-chapters.d.ts +37 -6
- package/dist/compiler/exports/export-chapters.js +64 -5
- package/dist/compiler/exports/export-pages.d.ts +30 -3
- package/dist/compiler/exports/export-pages.js +24 -1
- package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/dist/compiler/exports/latex/cache.d.ts +78 -0
- package/dist/compiler/exports/latex/cache.js +129 -0
- package/dist/compiler/exports/latex/capture.d.ts +10 -1
- package/dist/compiler/exports/latex/capture.js +3 -1
- package/dist/compiler/exports/latex/compile.d.ts +13 -2
- package/dist/compiler/exports/latex/compile.js +12 -1
- package/dist/compiler/exports/latex/deck.d.ts +92 -0
- package/dist/compiler/exports/latex/deck.js +349 -0
- package/dist/compiler/exports/latex/document.d.ts +51 -6
- package/dist/compiler/exports/latex/document.js +71 -12
- package/dist/compiler/exports/latex/explico-common.sty +198 -0
- package/dist/compiler/exports/latex/explico.cls +13 -183
- package/dist/compiler/exports/latex/serialize.d.ts +60 -4
- package/dist/compiler/exports/latex/serialize.js +4 -2
- package/dist/compiler/exports/latex-export.d.ts +24 -2
- package/dist/compiler/exports/latex-export.js +107 -26
- package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
- package/dist/compiler/exports/markdown/serialize.js +85 -8
- package/dist/compiler/exports/markdown-export.d.ts +6 -1
- package/dist/compiler/exports/markdown-export.js +50 -11
- package/dist/compiler/exports/standalone-html.d.ts +10 -5
- package/dist/compiler/exports/standalone-html.js +3 -3
- package/dist/compiler/exports/static-tree.d.ts +38 -1
- package/dist/compiler/exports/static-tree.js +120 -4
- package/dist/compiler/knowledge-version.d.ts +26 -2
- package/dist/compiler/knowledge-version.js +27 -17
- package/dist/compiler/lesson-derivation.d.ts +143 -6
- package/dist/compiler/lesson-derivation.js +145 -13
- package/dist/compiler/manifest.d.ts +133 -4
- package/dist/compiler/manifest.js +364 -58
- package/dist/compiler/page-versions.d.ts +184 -0
- package/dist/compiler/page-versions.js +217 -0
- package/dist/compiler/render-diagnostics.d.ts +5 -1
- package/dist/compiler/resolution-report.d.ts +24 -6
- package/dist/compiler/resolution-report.js +22 -4
- package/dist/compiler/validate-content.d.ts +7 -0
- package/dist/compiler/validate-content.js +160 -0
- package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
- package/dist/curriculum/competency-graph.d.ts +54 -0
- package/dist/curriculum/competency-graph.js +41 -0
- package/dist/reference/base-library.d.mts +8 -0
- package/dist/reference/base-library.mjs +19 -0
- package/dist/reference/consistency.d.ts +8 -4
- package/dist/reference/consistency.js +82 -26
- package/dist/reference/deck-remark.d.mts +123 -0
- package/dist/reference/deck-remark.mjs +600 -0
- package/dist/reference/deck-source.d.ts +107 -0
- package/dist/reference/deck-source.js +140 -0
- package/dist/reference/decks-validate.d.ts +133 -0
- package/dist/reference/decks-validate.js +359 -0
- package/dist/reference/derived-entries.d.ts +5 -1
- package/dist/reference/diagram-fit.d.ts +6 -2
- package/dist/reference/diagram-fit.js +5 -1
- package/dist/reference/figure-symbols.d.ts +23 -6
- package/dist/reference/figure-symbols.js +51 -15
- package/dist/reference/float-name.d.ts +43 -0
- package/dist/reference/float-name.js +58 -0
- package/dist/reference/folds-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-indices.js +5 -4
- package/dist/reference/gate-math.d.ts +29 -2
- package/dist/reference/gate-math.js +99 -18
- package/dist/reference/gate-part-pages.d.ts +1 -1
- package/dist/reference/lab-katex-validate.d.ts +10 -6
- package/dist/reference/lab-katex-validate.js +17 -9
- package/dist/reference/lab-math-scope.js +29 -4
- package/dist/reference/math-glyphs.d.mts +25 -0
- package/dist/reference/math-glyphs.mjs +189 -25
- package/dist/reference/page-notation.d.ts +22 -3
- package/dist/reference/page-notation.js +32 -10
- package/dist/reference/references.d.ts +14 -1
- package/dist/reference/references.js +13 -0
- package/dist/reference/registry-scope.d.mts +33 -0
- package/dist/reference/registry-scope.mjs +34 -0
- package/dist/reference/registry.js +29 -10
- package/dist/reference/rehype-deck.d.mts +6 -0
- package/dist/reference/rehype-deck.mjs +286 -0
- package/dist/reference/rehype-heading-anchors.d.mts +20 -0
- package/dist/reference/rehype-heading-anchors.mjs +48 -6
- package/dist/reference/remark-notation.mjs +249 -40
- package/dist/reference/source-lines.d.mts +12 -0
- package/dist/reference/source-lines.mjs +65 -0
- package/dist/reference/types.d.ts +28 -0
- package/dist/rules/catalog.d.ts +21 -4
- package/dist/rules/catalog.js +237 -2
- package/dist/rules/course-rules.d.ts +5 -1
- package/dist/rules/course-rules.js +17 -3
- package/dist/rules/course-rules.schema.json +12 -0
- package/dist/rules/in-effect.d.ts +13 -3
- package/dist/rules/in-effect.js +34 -10
- package/dist/rules/rules.schema.json +280 -1
- package/dist/rules/settings.d.ts +1 -1
- package/dist/session/PreferenceStore.d.ts +6 -0
- package/dist/session/PreferenceStore.js +18 -1
- package/dist/session/deck-memory.d.ts +116 -0
- package/dist/session/deck-memory.js +143 -0
- package/dist/session/reading-memory.d.ts +96 -0
- package/dist/session/reading-memory.js +133 -0
- package/docs/VERSIONING.md +21 -18
- package/docs/authoring/README.md +31 -9
- package/docs/authoring/agents.md +72 -18
- package/docs/authoring/cli-reference.json +2172 -0
- package/docs/authoring/cli.md +887 -0
- package/docs/authoring/components.md +15 -0
- package/docs/authoring/deck.md +612 -0
- package/docs/authoring/migrating.md +1195 -16
- package/docs/authoring/notation.md +49 -6
- package/docs/authoring/structure.md +80 -1
- package/docs/authoring/versioning.md +56 -3
- package/docs/authoring/visualizations.md +7 -2
- package/docs/course-agent/AGENTS.md +40 -0
- package/docs/course-agent/agents/rules-reviewer.md +33 -0
- package/docs/course-agent/skills/draft-lesson/SKILL.md +38 -0
- package/docs/course-agent/skills/migrate-course/SKILL.md +32 -0
- package/docs/course-agent/skills/release-content/SKILL.md +26 -0
- package/docs/diagram-scaling.md +148 -0
- package/docs/privacy.md +59 -0
- package/package.json +8 -2
- package/src/compiler/astro.ts +60 -21
- package/src/compiler/cli-main.ts +557 -0
- package/src/compiler/cli-reference.ts +285 -0
- package/src/compiler/cli-verbs.ts +270 -0
- package/src/compiler/cli-verify.ts +241 -0
- package/src/compiler/cli.ts +2119 -170
- package/src/compiler/collections.ts +284 -25
- package/src/compiler/content-release.ts +91 -0
- package/src/compiler/course-config.ts +267 -0
- package/src/compiler/decks.ts +455 -0
- package/src/compiler/exports/deck-fit.ts +127 -0
- package/src/compiler/exports/export-chapters.ts +105 -6
- package/src/compiler/exports/export-pages.ts +46 -2
- package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/src/compiler/exports/latex/cache.ts +183 -0
- package/src/compiler/exports/latex/capture.ts +5 -2
- package/src/compiler/exports/latex/compile.ts +18 -0
- package/src/compiler/exports/latex/deck.ts +419 -0
- package/src/compiler/exports/latex/document.ts +117 -21
- package/src/compiler/exports/latex/explico-common.sty +198 -0
- package/src/compiler/exports/latex/explico.cls +13 -183
- package/src/compiler/exports/latex/serialize.ts +25 -21
- package/src/compiler/exports/latex/texlive-packages.txt +6 -2
- package/src/compiler/exports/latex-export.ts +187 -45
- package/src/compiler/exports/markdown/serialize.ts +118 -9
- package/src/compiler/exports/markdown-export.ts +56 -11
- package/src/compiler/exports/standalone-html.ts +8 -7
- package/src/compiler/exports/static-tree.ts +175 -3
- package/src/compiler/knowledge-version.ts +56 -21
- package/src/compiler/lesson-derivation.ts +282 -16
- package/src/compiler/manifest.ts +568 -63
- package/src/compiler/page-versions.ts +401 -0
- package/src/compiler/resolution-report.ts +38 -7
- package/src/compiler/validate-content.ts +215 -0
- package/src/components/CourseContents.astro +124 -6
- package/src/components/CurriculumMap.astro +49 -1
- package/src/components/FloatIndex.astro +1 -1
- package/src/components/PartOverview.astro +51 -5
- package/src/components/assessments/AssessmentSet.astro +41 -15
- package/src/components/block-heading.ts +15 -6
- package/src/components/card-carry.ts +7 -4
- package/src/components/card-manager.ts +14 -2
- package/src/components/card-memory.ts +13 -9
- package/src/components/deck/DeckEnhancer.astro +13 -0
- package/src/components/deck/deck-context.ts +104 -0
- package/src/components/deck/deck-controller.ts +1287 -0
- package/src/components/deck/deck-map.ts +91 -0
- package/src/components/deck-lane.ts +163 -0
- package/src/components/diagram/Diagram.astro +131 -86
- package/src/components/disclosure/persisted-disclosure.ts +9 -1
- package/src/components/figure/Figure.astro +7 -2
- package/src/components/figure/FloatCaption.astro +5 -3
- package/src/components/fold/Fold.astro +2 -2
- package/src/components/notation/NotationFormula.astro +81 -11
- package/src/components/notation/NotationGlossary.astro +132 -1
- package/src/components/notation/NotationLayer.astro +15 -7
- package/src/components/starlight/AboutPanel.astro +186 -3
- package/src/components/starlight/CollapsibleSections.astro +24 -3
- package/src/components/starlight/DiagramEnhancer.astro +5 -1
- package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
- package/src/components/starlight/LayoutHeader.astro +17 -0
- package/src/components/starlight/LayoutPageTitle.astro +110 -0
- package/src/components/starlight/LayoutSidebar.astro +807 -21
- package/src/components/starlight/LayoutSocialIcons.astro +62 -6
- package/src/components/starlight/LessonExportActions.astro +27 -9
- package/src/components/starlight/LessonFooter.astro +59 -0
- package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
- package/src/components/starlight/TextSizeControl.astro +24 -13
- package/src/components/starlight/page-marks.ts +139 -0
- package/src/components/starlight/section-summary.ts +1 -1
- package/src/components/starlight/sidebar-editions.ts +245 -0
- package/src/components/table/Table.astro +15 -7
- package/src/content-config.ts +87 -1
- package/src/curriculum/competency-graph.ts +95 -0
- package/src/reference/base-library.mjs +19 -0
- package/src/reference/consistency.ts +137 -39
- package/src/reference/deck-remark.mjs +600 -0
- package/src/reference/deck-source.ts +273 -0
- package/src/reference/decks-validate.ts +604 -0
- package/src/reference/diagram-fit.ts +18 -4
- package/src/reference/diagram-layout.ts +3 -1
- package/src/reference/figure-symbols.ts +108 -31
- package/src/reference/folds-validate.ts +1 -1
- package/src/reference/gate-indices.ts +5 -3
- package/src/reference/gate-math.ts +169 -25
- package/src/reference/lab-katex-validate.ts +30 -15
- package/src/reference/lab-math-scope.ts +34 -3
- package/src/reference/math-glyphs.mjs +189 -25
- package/src/reference/page-notation.ts +69 -15
- package/src/reference/references.ts +28 -0
- package/src/reference/registry-scope.mjs +34 -0
- package/src/reference/registry.ts +41 -11
- package/src/reference/rehype-deck.mjs +286 -0
- package/src/reference/rehype-heading-anchors.mjs +48 -6
- package/src/reference/remark-notation.mjs +249 -40
- package/src/reference/source-lines.mjs +65 -0
- package/src/reference/types.ts +28 -0
- package/src/rules/catalog.ts +274 -4
- package/src/rules/course-rules.ts +21 -1
- package/src/rules/in-effect.ts +58 -11
- package/src/session/PreferenceStore.ts +23 -0
- package/src/session/deck-memory.ts +263 -0
- package/src/session/reading-memory.ts +243 -0
- package/src/styles/deck.css +1150 -0
- package/src/styles/global.css +958 -27
- package/src/styles/zones.css +277 -0
- /package/src/{components → reference}/float-name.ts +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
import { newestSince, versionPhrase } from "../../page-versions.js";
|
|
3
4
|
import {
|
|
4
5
|
escapeText,
|
|
6
|
+
escapeUrl,
|
|
7
|
+
labelOf,
|
|
5
8
|
serializeAbbreviationAppendix,
|
|
6
9
|
serializeChapter,
|
|
7
10
|
serializeNotationAppendix,
|
|
@@ -10,6 +13,9 @@ import {
|
|
|
10
13
|
function explicoClass() {
|
|
11
14
|
return readFileSync(new URL("./explico.cls", import.meta.url), "utf8");
|
|
12
15
|
}
|
|
16
|
+
function explicoCommon() {
|
|
17
|
+
return readFileSync(new URL("./explico-common.sty", import.meta.url), "utf8");
|
|
18
|
+
}
|
|
13
19
|
function twoDigits(value) {
|
|
14
20
|
return String(value).padStart(2, "0");
|
|
15
21
|
}
|
|
@@ -32,6 +38,16 @@ function checkItems(manifest, lesson) {
|
|
|
32
38
|
}
|
|
33
39
|
return items;
|
|
34
40
|
}
|
|
41
|
+
function deckCheckItems(manifest, deck) {
|
|
42
|
+
const lessonIds = deck.lessonId !== void 0 ? [deck.lessonId] : deck.covers.map((cover) => cover.chapter.replace(/\//g, "."));
|
|
43
|
+
const items = /* @__PURE__ */ new Map();
|
|
44
|
+
for (const id of lessonIds) {
|
|
45
|
+
for (const [assessment, list] of checkItems(manifest, { id })) {
|
|
46
|
+
if (!items.has(assessment)) items.set(assessment, list);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return items;
|
|
50
|
+
}
|
|
35
51
|
const DOCUMENT_METADATA = [
|
|
36
52
|
"% A fixed seed for TeX's random numbers, before tagging loads: tagpdf names",
|
|
37
53
|
"% one of its structure namespaces with random digits, and the same source",
|
|
@@ -45,11 +61,19 @@ const DOCUMENT_METADATA = [
|
|
|
45
61
|
" \\DocumentMetadata{lang=en,pdfversion=2.0,tagging=on}",
|
|
46
62
|
"\\fi"
|
|
47
63
|
];
|
|
48
|
-
function trailerId(manifest, document) {
|
|
64
|
+
function trailerId(manifest, document, since) {
|
|
49
65
|
return createHash("md5").update(`${manifest.about.material.name}
|
|
50
|
-
${manifest.about.knowledge.version}
|
|
66
|
+
${versionPhrase(since, manifest.about.knowledge.version)}
|
|
51
67
|
${document}`).digest("hex").toUpperCase();
|
|
52
68
|
}
|
|
69
|
+
function bookSince(parts) {
|
|
70
|
+
return newestSince(
|
|
71
|
+
parts.flatMap(({ part, chapters }) => [
|
|
72
|
+
...part.overview ? [part.overview.since] : [],
|
|
73
|
+
...chapters.map(({ lesson }) => lesson.since)
|
|
74
|
+
])
|
|
75
|
+
);
|
|
76
|
+
}
|
|
53
77
|
function trailerIdLine(id) {
|
|
54
78
|
return `\\ifdefined\\directlua\\pdfvariable trailerid {[<${id}><${id}>]}\\else\\pdftrailerid{${id}}\\fi`;
|
|
55
79
|
}
|
|
@@ -64,7 +88,7 @@ function mainTex(manifest, lesson, chapterFile) {
|
|
|
64
88
|
...DOCUMENT_METADATA,
|
|
65
89
|
`\\documentclass[${paper}]{explico}`,
|
|
66
90
|
"% The same identifier for every compile of this source.",
|
|
67
|
-
trailerIdLine(trailerId(manifest, lesson.slug)),
|
|
91
|
+
trailerIdLine(trailerId(manifest, lesson.slug, lesson.since)),
|
|
68
92
|
...numbering.floats === "part" ? ["\\ExplicoFloatsByPart"] : [],
|
|
69
93
|
`\\ExplicoSectionDepth{${numbering.sections.show ? numbering.sections.depth : 1}}`,
|
|
70
94
|
`\\hypersetup{pdftitle={${escapeText(lesson.title)}},pdfauthor={${escapeText(
|
|
@@ -91,7 +115,7 @@ function readme(manifest, lesson, url) {
|
|
|
91
115
|
return [
|
|
92
116
|
`# ${lesson.title}`,
|
|
93
117
|
"",
|
|
94
|
-
`The LaTeX source of a chapter of *${course}* (
|
|
118
|
+
`The LaTeX source of a chapter of *${course}* (${versionPhrase(lesson.since, manifest.about.knowledge.version)}).` + (url ? ` Interactive version: ${url}` : ""),
|
|
95
119
|
"",
|
|
96
120
|
"## Build",
|
|
97
121
|
"",
|
|
@@ -110,6 +134,8 @@ function readme(manifest, lesson, url) {
|
|
|
110
134
|
"- `main.tex` \u2014 the document: class options, then the chapter.",
|
|
111
135
|
"- `explico.cls` \u2014 explico's book class: layout, numbering, and the",
|
|
112
136
|
" environments the chapter uses (memos, notes, worked examples, checks).",
|
|
137
|
+
"- `explico-common.sty` \u2014 what the class shares with explico's slides: fonts,",
|
|
138
|
+
" colours, figures, diagrams and the notation markers.",
|
|
113
139
|
`- \`${chapterPath(manifest, lesson)}\` \u2014 the chapter.`,
|
|
114
140
|
"- `figures/` \u2014 the chapter's figures, as vector PDF where the website draws them.",
|
|
115
141
|
"",
|
|
@@ -131,7 +157,7 @@ function chapterSource(input) {
|
|
|
131
157
|
checkItems: checkItems(manifest, lesson),
|
|
132
158
|
source: {
|
|
133
159
|
course: manifest.about.material.name,
|
|
134
|
-
|
|
160
|
+
since: versionPhrase(lesson.since, manifest.about.knowledge.version),
|
|
135
161
|
...url ? { url } : {}
|
|
136
162
|
}
|
|
137
163
|
});
|
|
@@ -140,21 +166,36 @@ function chapterSource(input) {
|
|
|
140
166
|
{ path: "latexmkrc", content: LATEXMKRC },
|
|
141
167
|
{ path: "main.tex", content: mainTex(manifest, lesson, file) },
|
|
142
168
|
{ path: "explico.cls", content: explicoClass() },
|
|
169
|
+
{ path: "explico-common.sty", content: explicoCommon() },
|
|
143
170
|
{ path: file, content: chapter },
|
|
144
171
|
...[...input.pictures.values()].map((picture) => ({ path: picture.path, content: picture.content }))
|
|
145
172
|
]);
|
|
146
173
|
}
|
|
147
174
|
const OTHER_SCRIPTS = /[\u0370-\u03FF\u0400-\u04FF\u1F00-\u1FFF\u2E80-\u9FFF\uAC00-\uD7AF\uF900-\uFAFF\uFF00-\uFFEF\u2600-\u27BF\u{1F000}-\u{1FAFF}]/u;
|
|
148
175
|
const MATH_GREEK = /[αβγδεθλμπρστφωΓΔΘΛΠΣΦΩ]/gu;
|
|
176
|
+
function needsScripts(tex) {
|
|
177
|
+
return tex.includes("\\ExplicoGreekWord{") || OTHER_SCRIPTS.test(tex.replace(MATH_GREEK, ""));
|
|
178
|
+
}
|
|
149
179
|
function withScripts(files) {
|
|
150
180
|
const needed = files.some(
|
|
151
|
-
(file) => file.path.endsWith(".tex") && typeof file.content === "string" && (file.content
|
|
181
|
+
(file) => file.path.endsWith(".tex") && typeof file.content === "string" && needsScripts(file.content)
|
|
152
182
|
);
|
|
153
183
|
if (!needed) return files;
|
|
154
184
|
return files.map(
|
|
155
185
|
(file) => file.path === "main.tex" && typeof file.content === "string" ? { ...file, content: file.content.replace(/\\documentclass\[([^\]]*)\]\{explico\}/u, "\\documentclass[$1,scripts]{explico}") } : file
|
|
156
186
|
);
|
|
157
187
|
}
|
|
188
|
+
function slidesChapter(entry) {
|
|
189
|
+
const checks = entry.lesson.assessments.length;
|
|
190
|
+
return [
|
|
191
|
+
`\\chapter{${escapeText(entry.lesson.title)}}\\label{${labelOf(entry.lesson.slug, "_top")}}`,
|
|
192
|
+
`This lesson is taught as slides${entry.slidesUrl ? `: \\url{${escapeUrl(entry.slidesUrl)}}` : ""}.` + (checks > 0 ? ` Its knowledge check${checks === 1 ? " is" : "s are"} on its slides.` : ""),
|
|
193
|
+
...entry.outcomes.length > 0 ? ["", "After it, you can:", "\\begin{itemize}", ...entry.outcomes.map((outcome) => `\\item ${escapeText(outcome)}`), "\\end{itemize}"] : [],
|
|
194
|
+
// Its checks keep their numbers, so the chapters after it keep theirs.
|
|
195
|
+
...checks > 0 ? [`\\addtocounter{explicocheck}{${checks}}`] : [],
|
|
196
|
+
""
|
|
197
|
+
].join("\n");
|
|
198
|
+
}
|
|
158
199
|
function countNumbered(parts, kind) {
|
|
159
200
|
let count = 0;
|
|
160
201
|
const visit = (blocks) => {
|
|
@@ -210,7 +251,7 @@ function bookMainTex(input, inputs, appendices) {
|
|
|
210
251
|
...DOCUMENT_METADATA,
|
|
211
252
|
`\\documentclass[${paper}]{explico}`,
|
|
212
253
|
"% The same identifier for every compile of this source.",
|
|
213
|
-
trailerIdLine(trailerId(manifest, input.document)),
|
|
254
|
+
trailerIdLine(trailerId(manifest, input.document, bookSince(input.parts))),
|
|
214
255
|
...numbering.floats === "part" ? ["\\ExplicoFloatsByPart"] : [],
|
|
215
256
|
`\\ExplicoSectionDepth{${numbering.sections.show ? numbering.sections.depth : 1}}`,
|
|
216
257
|
"% The contents list parts, chapters and sections.",
|
|
@@ -221,7 +262,7 @@ function bookMainTex(input, inputs, appendices) {
|
|
|
221
262
|
"\\begin{document}",
|
|
222
263
|
"\\frontmatter",
|
|
223
264
|
`\\ExplicoTitlePage{${escapeText(input.title)}}{${escapeText(input.subtitle ?? "")}}{${escapeText(
|
|
224
|
-
manifest.about.knowledge.version
|
|
265
|
+
versionPhrase(bookSince(input.parts), manifest.about.knowledge.version)
|
|
225
266
|
)}}{${escapeText(authors.join(", "))}}{${url ? url.replace(/[\\{}]/gu, "").replace(/[%#]/gu, (char) => `\\${char}`) : ""}}`,
|
|
226
267
|
"% The contents and lists in black, as a book sets them; links in the text keep their colour.",
|
|
227
268
|
"\\begingroup\\hypersetup{linkcolor=black}",
|
|
@@ -241,7 +282,7 @@ function bookReadme(input) {
|
|
|
241
282
|
return [
|
|
242
283
|
`# ${input.title}`,
|
|
243
284
|
"",
|
|
244
|
-
`The LaTeX source of ${input.subtitle ? `${input.subtitle.replace(/^Part/u, "part")}` : `*${manifest.about.material.name}*`} (
|
|
285
|
+
`The LaTeX source of ${input.subtitle ? `${input.subtitle.replace(/^Part/u, "part")}` : `*${manifest.about.material.name}*`} (${versionPhrase(bookSince(input.parts), manifest.about.knowledge.version)}).` + (url ? ` Interactive version: ${url}` : ""),
|
|
245
286
|
"",
|
|
246
287
|
"## Build",
|
|
247
288
|
"",
|
|
@@ -261,6 +302,8 @@ function bookReadme(input) {
|
|
|
261
302
|
" diagrams, then each part and its chapters, then the appendices.",
|
|
262
303
|
"- `explico.cls` \u2014 explico's book class: layout, numbering, and the",
|
|
263
304
|
" environments the chapters use (memos, notes, worked examples, checks).",
|
|
305
|
+
"- `explico-common.sty` \u2014 what the class shares with explico's slides: fonts,",
|
|
306
|
+
" colours, figures, diagrams and the notation markers.",
|
|
264
307
|
"- `parts/NN-<part>.tex` \u2014 a part's opening page and text;",
|
|
265
308
|
" `parts/NN-<part>/MM-<chapter>.tex` \u2014 its chapters.",
|
|
266
309
|
"- `backmatter/` \u2014 the notation and abbreviation appendices.",
|
|
@@ -275,7 +318,6 @@ function bookSource(input) {
|
|
|
275
318
|
const { manifest, parts } = input;
|
|
276
319
|
const canonicalBaseUrl = manifest.lessonExports.canonicalBaseUrl;
|
|
277
320
|
const course = manifest.about.material.name;
|
|
278
|
-
const version = manifest.about.knowledge.version;
|
|
279
321
|
const documentRoutes = new Set(
|
|
280
322
|
parts.flatMap(({ overview, chapters }) => [
|
|
281
323
|
...overview ? [overview.route] : [],
|
|
@@ -286,7 +328,7 @@ function bookSource(input) {
|
|
|
286
328
|
const common = { documentRoutes, canonicalBaseUrl, inBook: true, notationTargets };
|
|
287
329
|
const files = [];
|
|
288
330
|
const inputs = [];
|
|
289
|
-
for (const { part, overview, chapters } of parts) {
|
|
331
|
+
for (const { part, overview, chapters, slides = [] } of parts) {
|
|
290
332
|
const partFile = `parts/${twoDigits(part.ordinal)}-${part.dir}.tex`;
|
|
291
333
|
files.push({
|
|
292
334
|
path: partFile,
|
|
@@ -294,7 +336,17 @@ function bookSource(input) {
|
|
|
294
336
|
`
|
|
295
337
|
});
|
|
296
338
|
inputs.push(partFile);
|
|
339
|
+
const pending = [...slides].sort((a, b) => a.lesson.chapter - b.lesson.chapter);
|
|
340
|
+
const writeSlides = (before) => {
|
|
341
|
+
while (pending.length > 0 && pending[0].lesson.chapter < before) {
|
|
342
|
+
const entry = pending.shift();
|
|
343
|
+
const file = chapterPath(manifest, entry.lesson);
|
|
344
|
+
files.push({ path: file, content: slidesChapter(entry) });
|
|
345
|
+
inputs.push(file);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
297
348
|
for (const { lesson, page, pictures } of chapters) {
|
|
349
|
+
writeSlides(lesson.chapter);
|
|
298
350
|
const file = chapterPath(manifest, lesson);
|
|
299
351
|
const url = canonicalBaseUrl ? `${canonicalBaseUrl}${lesson.slug}/` : void 0;
|
|
300
352
|
files.push({
|
|
@@ -304,12 +356,14 @@ function bookSource(input) {
|
|
|
304
356
|
page,
|
|
305
357
|
pictures: new Map([...pictures].map(([anchor, picture]) => [anchor, picture.path])),
|
|
306
358
|
checkItems: checkItems(manifest, lesson),
|
|
307
|
-
|
|
359
|
+
// In a book the line is the URL alone; the title page carries the version.
|
|
360
|
+
source: { course, ...url ? { url } : {} }
|
|
308
361
|
})
|
|
309
362
|
});
|
|
310
363
|
inputs.push(file);
|
|
311
364
|
files.push(...[...pictures.values()].map((picture) => ({ path: picture.path, content: picture.content })));
|
|
312
365
|
}
|
|
366
|
+
writeSlides(Number.POSITIVE_INFINITY);
|
|
313
367
|
}
|
|
314
368
|
const appendices = [];
|
|
315
369
|
const notation = serializeNotationAppendix(appendixNotation(manifest, parts), {
|
|
@@ -335,15 +389,20 @@ function bookSource(input) {
|
|
|
335
389
|
{ path: "latexmkrc", content: LATEXMKRC },
|
|
336
390
|
{ path: "main.tex", content: bookMainTex(input, inputs, appendices) },
|
|
337
391
|
{ path: "explico.cls", content: explicoClass() },
|
|
392
|
+
{ path: "explico-common.sty", content: explicoCommon() },
|
|
338
393
|
...files
|
|
339
394
|
]);
|
|
340
395
|
}
|
|
341
396
|
export {
|
|
397
|
+
bookSince,
|
|
342
398
|
bookSource,
|
|
343
399
|
chapterFolderName,
|
|
344
400
|
chapterPath,
|
|
345
401
|
chapterSource,
|
|
346
402
|
checkItems,
|
|
403
|
+
deckCheckItems,
|
|
347
404
|
explicoClass,
|
|
405
|
+
explicoCommon,
|
|
406
|
+
needsScripts,
|
|
348
407
|
trailerId
|
|
349
408
|
};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
%% explico-common.sty — what every LaTeX document explico writes shares: its
|
|
2
|
+
%% book class (`explico.cls`) and its deck theme (`beamerthemeexplico.sty`).
|
|
3
|
+
%%
|
|
4
|
+
%% The engine, the fonts and the Unicode characters, the colours, the
|
|
5
|
+
%% figures and tables, the TikZ styles of a diagram, the box style, the
|
|
6
|
+
%% notation markers (\explain, \slot) and the cross-references. Edit it
|
|
7
|
+
%% freely in a download.
|
|
8
|
+
\NeedsTeXFormat{LaTeX2e}
|
|
9
|
+
\ProvidesPackage{explico-common}[2026/10/10 explico shared definitions]
|
|
10
|
+
|
|
11
|
+
%% `scripts`: the document holds Greek, Cyrillic, CJK or emoji (the exporter
|
|
12
|
+
%% sets it when it does), so LuaLaTeX loads the fallback fonts below.
|
|
13
|
+
\newif\ifexplico@scripts
|
|
14
|
+
\DeclareOption{scripts}{\explico@scriptstrue}
|
|
15
|
+
\ProcessOptions\relax
|
|
16
|
+
|
|
17
|
+
%% --- Engine and fonts ------------------------------------------------------
|
|
18
|
+
\RequirePackage{iftex}
|
|
19
|
+
\RequirePackage{amsmath,amssymb,mathtools}
|
|
20
|
+
%% Rule N17's engine half, as on the website (src/reference/katex-delimiters.mjs):
|
|
21
|
+
%% a negative \delimitershortfall makes every \left...\right pair strictly taller
|
|
22
|
+
%% than its content, so a nest grows outward at each level. TeX's default (5pt)
|
|
23
|
+
%% lets nested pairs round to the same glyph.
|
|
24
|
+
\delimitershortfall=-1pt
|
|
25
|
+
%% Whether a text font sets Greek; if none does, a Greek letter in prose is
|
|
26
|
+
%% set as its math symbol (below, with the other Unicode characters).
|
|
27
|
+
\newif\ifexplico@greek
|
|
28
|
+
\ifPDFTeX
|
|
29
|
+
\RequirePackage[T1]{fontenc}
|
|
30
|
+
\RequirePackage{lmodern}
|
|
31
|
+
\RequirePackage{textcomp}
|
|
32
|
+
\RequirePackage{bm}
|
|
33
|
+
%% A character no pdfTeX font sets (accented Greek, Chinese, an emoji) stays
|
|
34
|
+
%% blank instead of stopping the compile, and the log says so in LuaLaTeX's
|
|
35
|
+
%% words, which the export turns into its warning about blank characters.
|
|
36
|
+
\def\UTFviii@undefined@err#1{%
|
|
37
|
+
\typeout{Missing character: There is no
|
|
38
|
+
\expandafter\UTFviii@splitcsname\string#1\relax\space in font pdftex!}}
|
|
39
|
+
\else
|
|
40
|
+
%% Latin Modern for text and math. unicode-math is also what lets a tagged
|
|
41
|
+
%% PDF carry MathML for every formula (luamml needs it); bm does not work
|
|
42
|
+
%% with it, so \bm becomes \boldsymbol.
|
|
43
|
+
\RequirePackage{unicode-math}
|
|
44
|
+
\providecommand{\bm}{\boldsymbol}
|
|
45
|
+
%% Latin Modern has no Greek, Cyrillic, Chinese, Japanese, Korean or emoji.
|
|
46
|
+
%% With `scripts`, prose in those scripts comes from fallback fonts: for each
|
|
47
|
+
%% script, the first of the fonts below this machine has (CMU Serif, from
|
|
48
|
+
%% TeX Live's cm-unicode, matches Latin Modern). A script none of them
|
|
49
|
+
%% covers stays blank, and the export warns about it.
|
|
50
|
+
\ifexplico@scripts
|
|
51
|
+
\def\explico@fallbacks{}
|
|
52
|
+
\def\explico@fallback#1#2{%
|
|
53
|
+
\let\explico@found\@empty
|
|
54
|
+
\@for\explico@candidate:=#1\do{%
|
|
55
|
+
\ifx\explico@found\@empty
|
|
56
|
+
\IfFontExistsTF{\explico@candidate}{%
|
|
57
|
+
\edef\explico@fallbacks{\explico@fallbacks"\explico@candidate:mode=harf;",}%
|
|
58
|
+
\let\explico@found\explico@candidate}{}%
|
|
59
|
+
\fi}%
|
|
60
|
+
\ifx\explico@found\@empty\else#2\fi}
|
|
61
|
+
\explico@fallback{CMU Serif,Noto Serif,DejaVu Serif,FreeSerif}{\explico@greektrue}
|
|
62
|
+
\explico@fallback{Noto Serif CJK SC,Noto Sans CJK SC,FandolSong,Source Han Serif SC}{}
|
|
63
|
+
\explico@fallback{Noto Color Emoji,Twemoji Mozilla,Symbola}{}
|
|
64
|
+
\ifx\explico@fallbacks\@empty\else
|
|
65
|
+
\directlua{luaotfload.add_fallback("explico", {\explico@fallbacks})}%
|
|
66
|
+
\setmainfont{Latin Modern Roman}[RawFeature={fallback=explico}]%
|
|
67
|
+
\fi
|
|
68
|
+
\fi
|
|
69
|
+
\fi
|
|
70
|
+
\RequirePackage{microtype}
|
|
71
|
+
|
|
72
|
+
%% --- Page and colour --------------------------------------------------------
|
|
73
|
+
\RequirePackage{xcolor}
|
|
74
|
+
\definecolor{explicoaccent}{HTML}{12775C}
|
|
75
|
+
\definecolor{explicoframe}{HTML}{9DB8AD}
|
|
76
|
+
\definecolor{explicoback}{HTML}{F3F8F6}
|
|
77
|
+
\definecolor{explicoasidecaution}{HTML}{B5651D}
|
|
78
|
+
\definecolor{explicoasidecautionback}{HTML}{FBF3EA}
|
|
79
|
+
\definecolor{explicorule}{HTML}{8A9A93}
|
|
80
|
+
|
|
81
|
+
%% --- Floats, tables and captions -------------------------------------------
|
|
82
|
+
\RequirePackage{graphicx}
|
|
83
|
+
\RequirePackage{array,booktabs,tabularx}
|
|
84
|
+
\RequirePackage[export]{adjustbox}
|
|
85
|
+
%% explico's short caption, run in bold before the long one.
|
|
86
|
+
\newcommand*{\ExplicoCaptionTitle}[1]{\textbf{#1}}
|
|
87
|
+
|
|
88
|
+
%% --- Diagrams ---------------------------------------------------------------
|
|
89
|
+
\RequirePackage{tikz}
|
|
90
|
+
\usetikzlibrary{arrows.meta}
|
|
91
|
+
\tikzset{
|
|
92
|
+
explico diagram node/.style={
|
|
93
|
+
draw=explicorule, fill=explicoback, rounded corners=3pt, line width=0.5pt,
|
|
94
|
+
align=center, inner sep=3pt, font=\small,
|
|
95
|
+
},
|
|
96
|
+
explico diagram edge/.style={
|
|
97
|
+
draw=explicorule, line width=0.6pt, -{Stealth[length=5pt,width=4pt]},
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
%% --- Boxes: memos, notes, asides, worked examples, checks ------------------
|
|
102
|
+
\RequirePackage[most]{tcolorbox}
|
|
103
|
+
\tcbset{
|
|
104
|
+
explico/.style={
|
|
105
|
+
enhanced, breakable, colback=explicoback, colframe=explicoframe,
|
|
106
|
+
boxrule=0.5pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt,
|
|
107
|
+
fonttitle=\bfseries\small, coltitle=black, colbacktitle=explicoback!60!white,
|
|
108
|
+
titlerule=0.4pt, before skip=10pt plus 2pt, after skip=10pt plus 2pt,
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
%% --- Unicode in text that the default fonts lack ---------------------------
|
|
113
|
+
\RequirePackage{newunicodechar}
|
|
114
|
+
\newunicodechar{°}{\ifmmode^\circ\else\textdegree\fi}
|
|
115
|
+
\newunicodechar{−}{\ensuremath{-}}
|
|
116
|
+
\newunicodechar{×}{\ensuremath{\times}}
|
|
117
|
+
\newunicodechar{±}{\ensuremath{\pm}}
|
|
118
|
+
\newunicodechar{≤}{\ensuremath{\le}}
|
|
119
|
+
\newunicodechar{≥}{\ensuremath{\ge}}
|
|
120
|
+
\newunicodechar{≈}{\ensuremath{\approx}}
|
|
121
|
+
\newunicodechar{≠}{\ensuremath{\neq}}
|
|
122
|
+
\newunicodechar{→}{\ensuremath{\rightarrow}}
|
|
123
|
+
\newunicodechar{←}{\ensuremath{\leftarrow}}
|
|
124
|
+
\newunicodechar{↑}{\ensuremath{\uparrow}}
|
|
125
|
+
\newunicodechar{↓}{\ensuremath{\downarrow}}
|
|
126
|
+
\newunicodechar{⟨}{\ensuremath{\langle}}
|
|
127
|
+
\newunicodechar{⟩}{\ensuremath{\rangle}}
|
|
128
|
+
\newunicodechar{⋮}{\ensuremath{\vdots}}
|
|
129
|
+
\newunicodechar{√}{\ensuremath{\surd}}
|
|
130
|
+
\newunicodechar{∞}{\ensuremath{\infty}}
|
|
131
|
+
\newunicodechar{′}{\ensuremath{'}}
|
|
132
|
+
\newunicodechar{𝒩}{\ensuremath{\mathcal{N}}}
|
|
133
|
+
\newunicodechar{①}{\textcircled{\scriptsize 1}}
|
|
134
|
+
\newunicodechar{②}{\textcircled{\scriptsize 2}}
|
|
135
|
+
\newunicodechar{③}{\textcircled{\scriptsize 3}}
|
|
136
|
+
\newunicodechar{④}{\textcircled{\scriptsize 4}}
|
|
137
|
+
\newunicodechar{⑤}{\textcircled{\scriptsize 5}}
|
|
138
|
+
\newunicodechar{₀}{\textsubscript{0}}
|
|
139
|
+
\newunicodechar{₁}{\textsubscript{1}}
|
|
140
|
+
\newunicodechar{₂}{\textsubscript{2}}
|
|
141
|
+
\newunicodechar{₃}{\textsubscript{3}}
|
|
142
|
+
\newunicodechar{¹}{\textsuperscript{1}}
|
|
143
|
+
\newunicodechar{²}{\textsuperscript{2}}
|
|
144
|
+
\newunicodechar{³}{\textsuperscript{3}}
|
|
145
|
+
%% A Greek letter on its own is a symbol (the polar coordinates ρ and φ in a
|
|
146
|
+
%% caption, which holds no math): it is set as its math symbol. Inside a Greek
|
|
147
|
+
%% word, which the exporter marks \ExplicoGreekWord{…}, it is a letter, set
|
|
148
|
+
%% from the text font's Greek where one is loaded (`scripts`).
|
|
149
|
+
\newif\ifexplico@word
|
|
150
|
+
\DeclareRobustCommand{\ExplicoGreekWord}[1]{{\explico@wordtrue#1}}
|
|
151
|
+
%% Protected, as \ensuremath is: the text of an equation is expanded for its
|
|
152
|
+
%% tagging (alt text, MathML), and an expandable body breaks it there.
|
|
153
|
+
\protected\def\explico@greekletter#1#2{%
|
|
154
|
+
\ifmmode#2\else
|
|
155
|
+
\ifexplico@word
|
|
156
|
+
\ifexplico@greek\char#1\relax\else\ensuremath{#2}\fi
|
|
157
|
+
\else\ensuremath{#2}\fi
|
|
158
|
+
\fi}
|
|
159
|
+
\newunicodechar{α}{\explico@greekletter{"03B1}{\alpha}}
|
|
160
|
+
\newunicodechar{β}{\explico@greekletter{"03B2}{\beta}}
|
|
161
|
+
\newunicodechar{γ}{\explico@greekletter{"03B3}{\gamma}}
|
|
162
|
+
\newunicodechar{δ}{\explico@greekletter{"03B4}{\delta}}
|
|
163
|
+
\newunicodechar{ε}{\explico@greekletter{"03B5}{\varepsilon}}
|
|
164
|
+
\newunicodechar{θ}{\explico@greekletter{"03B8}{\theta}}
|
|
165
|
+
\newunicodechar{λ}{\explico@greekletter{"03BB}{\lambda}}
|
|
166
|
+
\newunicodechar{μ}{\explico@greekletter{"03BC}{\mu}}
|
|
167
|
+
\newunicodechar{π}{\explico@greekletter{"03C0}{\pi}}
|
|
168
|
+
\newunicodechar{ρ}{\explico@greekletter{"03C1}{\rho}}
|
|
169
|
+
\newunicodechar{σ}{\explico@greekletter{"03C3}{\sigma}}
|
|
170
|
+
\newunicodechar{τ}{\explico@greekletter{"03C4}{\tau}}
|
|
171
|
+
\newunicodechar{φ}{\explico@greekletter{"03C6}{\varphi}}
|
|
172
|
+
\newunicodechar{ω}{\explico@greekletter{"03C9}{\omega}}
|
|
173
|
+
\newunicodechar{Γ}{\explico@greekletter{"0393}{\Gamma}}
|
|
174
|
+
\newunicodechar{Δ}{\explico@greekletter{"0394}{\Delta}}
|
|
175
|
+
\newunicodechar{Θ}{\explico@greekletter{"0398}{\Theta}}
|
|
176
|
+
\newunicodechar{Λ}{\explico@greekletter{"039B}{\Lambda}}
|
|
177
|
+
\newunicodechar{Π}{\explico@greekletter{"03A0}{\Pi}}
|
|
178
|
+
\newunicodechar{Σ}{\explico@greekletter{"03A3}{\Sigma}}
|
|
179
|
+
\newunicodechar{Φ}{\explico@greekletter{"03A6}{\Phi}}
|
|
180
|
+
\newunicodechar{Ω}{\explico@greekletter{"03A9}{\Omega}}
|
|
181
|
+
|
|
182
|
+
%% --- Notation ---------------------------------------------------------------
|
|
183
|
+
%% \explain{key}{tex} and \slot{key}{tex} are explico's markers in math: the
|
|
184
|
+
%% website opens a card for the key; on paper they print their content.
|
|
185
|
+
\providecommand{\explain}[2]{#2}
|
|
186
|
+
\providecommand{\slot}[2]{#2}
|
|
187
|
+
%% \ExplicoNotation{target}{tex}: a formula's symbol that links to its notation entry
|
|
188
|
+
%% (the serializer writes it only where the book has that entry).
|
|
189
|
+
\providecommand{\ExplicoNotation}[2]{\hyperlink{#1}{#2}}
|
|
190
|
+
%% A term in prose with a notation card on the website.
|
|
191
|
+
\newcommand{\ExplicoTerm}[2]{#2}
|
|
192
|
+
|
|
193
|
+
%% --- Cross-references ---------------------------------------------------------
|
|
194
|
+
%% #1 label, #2 text before the number, #3 text after it.
|
|
195
|
+
\newcommand{\ExplicoRef}[3]{\hyperref[#1]{#2\ref*{#1}#3}}
|
|
196
|
+
\newcommand{\ExplicoEqRef}[1]{\hyperref[#1]{(\ref*{#1})}}
|
|
197
|
+
|
|
198
|
+
\endinput
|