explico 0.13.0 → 0.14.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.
Files changed (239) hide show
  1. package/CHANGELOG.md +894 -1
  2. package/README.md +200 -30
  3. package/dist/compiler/astro.d.ts +15 -2
  4. package/dist/compiler/astro.js +40 -11
  5. package/dist/compiler/cli-main.d.ts +43 -0
  6. package/dist/compiler/cli-main.js +392 -0
  7. package/dist/compiler/cli.d.ts +424 -9
  8. package/dist/compiler/cli.js +1289 -8
  9. package/dist/compiler/collections.d.ts +88 -1
  10. package/dist/compiler/collections.js +170 -25
  11. package/dist/compiler/course-config.d.ts +1 -1
  12. package/dist/compiler/course-config.js +105 -1
  13. package/dist/compiler/decks.d.ts +161 -0
  14. package/dist/compiler/decks.js +235 -0
  15. package/dist/compiler/exports/combined-html.d.ts +5 -1
  16. package/dist/compiler/exports/deck-fit.d.ts +55 -0
  17. package/dist/compiler/exports/deck-fit.js +90 -0
  18. package/dist/compiler/exports/export-chapters.d.ts +37 -6
  19. package/dist/compiler/exports/export-chapters.js +64 -5
  20. package/dist/compiler/exports/export-pages.d.ts +30 -3
  21. package/dist/compiler/exports/export-pages.js +24 -1
  22. package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  23. package/dist/compiler/exports/latex/cache.d.ts +78 -0
  24. package/dist/compiler/exports/latex/cache.js +129 -0
  25. package/dist/compiler/exports/latex/capture.d.ts +10 -1
  26. package/dist/compiler/exports/latex/capture.js +3 -1
  27. package/dist/compiler/exports/latex/compile.d.ts +13 -2
  28. package/dist/compiler/exports/latex/compile.js +12 -1
  29. package/dist/compiler/exports/latex/deck.d.ts +92 -0
  30. package/dist/compiler/exports/latex/deck.js +349 -0
  31. package/dist/compiler/exports/latex/document.d.ts +51 -6
  32. package/dist/compiler/exports/latex/document.js +71 -12
  33. package/dist/compiler/exports/latex/explico-common.sty +198 -0
  34. package/dist/compiler/exports/latex/explico.cls +13 -183
  35. package/dist/compiler/exports/latex/serialize.d.ts +60 -4
  36. package/dist/compiler/exports/latex/serialize.js +4 -2
  37. package/dist/compiler/exports/latex-export.d.ts +24 -2
  38. package/dist/compiler/exports/latex-export.js +107 -26
  39. package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
  40. package/dist/compiler/exports/markdown/serialize.js +85 -8
  41. package/dist/compiler/exports/markdown-export.d.ts +6 -1
  42. package/dist/compiler/exports/markdown-export.js +50 -11
  43. package/dist/compiler/exports/standalone-html.d.ts +10 -5
  44. package/dist/compiler/exports/standalone-html.js +3 -3
  45. package/dist/compiler/exports/static-tree.d.ts +38 -1
  46. package/dist/compiler/exports/static-tree.js +120 -4
  47. package/dist/compiler/knowledge-version.d.ts +26 -2
  48. package/dist/compiler/knowledge-version.js +27 -17
  49. package/dist/compiler/lesson-derivation.d.ts +143 -6
  50. package/dist/compiler/lesson-derivation.js +145 -13
  51. package/dist/compiler/manifest.d.ts +133 -4
  52. package/dist/compiler/manifest.js +359 -58
  53. package/dist/compiler/page-versions.d.ts +160 -0
  54. package/dist/compiler/page-versions.js +198 -0
  55. package/dist/compiler/render-diagnostics.d.ts +5 -1
  56. package/dist/compiler/resolution-report.d.ts +24 -6
  57. package/dist/compiler/resolution-report.js +22 -4
  58. package/dist/compiler/validate-content.d.ts +7 -0
  59. package/dist/compiler/validate-content.js +160 -0
  60. package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
  61. package/dist/curriculum/competency-graph.d.ts +54 -0
  62. package/dist/curriculum/competency-graph.js +41 -0
  63. package/dist/reference/base-library.d.mts +8 -0
  64. package/dist/reference/base-library.mjs +19 -0
  65. package/dist/reference/consistency.d.ts +8 -4
  66. package/dist/reference/consistency.js +76 -26
  67. package/dist/reference/deck-remark.d.mts +123 -0
  68. package/dist/reference/deck-remark.mjs +600 -0
  69. package/dist/reference/deck-source.d.ts +107 -0
  70. package/dist/reference/deck-source.js +140 -0
  71. package/dist/reference/decks-validate.d.ts +133 -0
  72. package/dist/reference/decks-validate.js +359 -0
  73. package/dist/reference/derived-entries.d.ts +5 -1
  74. package/dist/reference/diagram-fit.d.ts +1 -1
  75. package/dist/reference/figure-symbols.d.ts +23 -6
  76. package/dist/reference/figure-symbols.js +51 -15
  77. package/dist/reference/float-name.d.ts +43 -0
  78. package/dist/reference/float-name.js +58 -0
  79. package/dist/reference/folds-validate.d.ts +1 -1
  80. package/dist/reference/gate-abbreviations.d.ts +1 -1
  81. package/dist/reference/gate-indices.js +5 -4
  82. package/dist/reference/gate-math.d.ts +29 -2
  83. package/dist/reference/gate-math.js +99 -18
  84. package/dist/reference/gate-part-pages.d.ts +1 -1
  85. package/dist/reference/lab-katex-validate.d.ts +10 -6
  86. package/dist/reference/lab-katex-validate.js +17 -9
  87. package/dist/reference/lab-math-scope.js +29 -4
  88. package/dist/reference/math-glyphs.d.mts +25 -0
  89. package/dist/reference/math-glyphs.mjs +189 -25
  90. package/dist/reference/page-notation.d.ts +22 -3
  91. package/dist/reference/page-notation.js +32 -10
  92. package/dist/reference/references.d.ts +14 -1
  93. package/dist/reference/references.js +13 -0
  94. package/dist/reference/registry-scope.d.mts +33 -0
  95. package/dist/reference/registry-scope.mjs +34 -0
  96. package/dist/reference/registry.js +29 -10
  97. package/dist/reference/rehype-deck.d.mts +6 -0
  98. package/dist/reference/rehype-deck.mjs +286 -0
  99. package/dist/reference/rehype-heading-anchors.d.mts +20 -0
  100. package/dist/reference/rehype-heading-anchors.mjs +48 -6
  101. package/dist/reference/remark-notation.mjs +249 -40
  102. package/dist/reference/source-lines.d.mts +12 -0
  103. package/dist/reference/source-lines.mjs +65 -0
  104. package/dist/reference/types.d.ts +28 -0
  105. package/dist/rules/catalog.d.ts +21 -4
  106. package/dist/rules/catalog.js +221 -2
  107. package/dist/rules/course-rules.d.ts +5 -1
  108. package/dist/rules/course-rules.js +17 -3
  109. package/dist/rules/course-rules.schema.json +12 -0
  110. package/dist/rules/in-effect.d.ts +13 -3
  111. package/dist/rules/in-effect.js +30 -7
  112. package/dist/rules/rules.schema.json +259 -1
  113. package/dist/rules/settings.d.ts +1 -1
  114. package/dist/session/PreferenceStore.d.ts +6 -0
  115. package/dist/session/PreferenceStore.js +18 -1
  116. package/dist/session/deck-memory.d.ts +116 -0
  117. package/dist/session/deck-memory.js +143 -0
  118. package/dist/session/reading-memory.d.ts +96 -0
  119. package/dist/session/reading-memory.js +133 -0
  120. package/docs/VERSIONING.md +21 -18
  121. package/docs/authoring/README.md +29 -9
  122. package/docs/authoring/agents.md +63 -16
  123. package/docs/authoring/components.md +15 -0
  124. package/docs/authoring/deck.md +588 -0
  125. package/docs/authoring/migrating.md +1087 -14
  126. package/docs/authoring/notation.md +49 -6
  127. package/docs/authoring/structure.md +80 -1
  128. package/docs/authoring/versioning.md +54 -2
  129. package/docs/authoring/visualizations.md +7 -2
  130. package/docs/course-agent/AGENTS.md +37 -0
  131. package/docs/course-agent/agents/rules-reviewer.md +33 -0
  132. package/docs/course-agent/skills/draft-lesson/SKILL.md +37 -0
  133. package/docs/course-agent/skills/migrate-course/SKILL.md +31 -0
  134. package/docs/course-agent/skills/release-content/SKILL.md +24 -0
  135. package/docs/diagram-scaling.md +148 -0
  136. package/docs/privacy.md +59 -0
  137. package/package.json +8 -2
  138. package/src/compiler/astro.ts +60 -21
  139. package/src/compiler/cli-main.ts +469 -0
  140. package/src/compiler/cli.ts +1905 -16
  141. package/src/compiler/collections.ts +284 -25
  142. package/src/compiler/course-config.ts +267 -0
  143. package/src/compiler/decks.ts +455 -0
  144. package/src/compiler/exports/deck-fit.ts +127 -0
  145. package/src/compiler/exports/export-chapters.ts +105 -6
  146. package/src/compiler/exports/export-pages.ts +46 -2
  147. package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  148. package/src/compiler/exports/latex/cache.ts +183 -0
  149. package/src/compiler/exports/latex/capture.ts +5 -2
  150. package/src/compiler/exports/latex/compile.ts +18 -0
  151. package/src/compiler/exports/latex/deck.ts +419 -0
  152. package/src/compiler/exports/latex/document.ts +117 -21
  153. package/src/compiler/exports/latex/explico-common.sty +198 -0
  154. package/src/compiler/exports/latex/explico.cls +13 -183
  155. package/src/compiler/exports/latex/serialize.ts +25 -21
  156. package/src/compiler/exports/latex/texlive-packages.txt +6 -2
  157. package/src/compiler/exports/latex-export.ts +187 -45
  158. package/src/compiler/exports/markdown/serialize.ts +118 -9
  159. package/src/compiler/exports/markdown-export.ts +56 -11
  160. package/src/compiler/exports/standalone-html.ts +8 -7
  161. package/src/compiler/exports/static-tree.ts +175 -3
  162. package/src/compiler/knowledge-version.ts +56 -21
  163. package/src/compiler/lesson-derivation.ts +282 -16
  164. package/src/compiler/manifest.ts +558 -63
  165. package/src/compiler/page-versions.ts +359 -0
  166. package/src/compiler/resolution-report.ts +38 -7
  167. package/src/compiler/validate-content.ts +212 -0
  168. package/src/components/CourseContents.astro +124 -6
  169. package/src/components/CurriculumMap.astro +49 -1
  170. package/src/components/FloatIndex.astro +1 -1
  171. package/src/components/PartOverview.astro +51 -5
  172. package/src/components/assessments/AssessmentSet.astro +41 -15
  173. package/src/components/block-heading.ts +15 -6
  174. package/src/components/card-carry.ts +7 -4
  175. package/src/components/card-manager.ts +14 -2
  176. package/src/components/card-memory.ts +13 -9
  177. package/src/components/deck/DeckEnhancer.astro +13 -0
  178. package/src/components/deck/deck-context.ts +104 -0
  179. package/src/components/deck/deck-controller.ts +1287 -0
  180. package/src/components/deck/deck-map.ts +91 -0
  181. package/src/components/deck-lane.ts +163 -0
  182. package/src/components/diagram/Diagram.astro +131 -86
  183. package/src/components/disclosure/persisted-disclosure.ts +9 -1
  184. package/src/components/figure/Figure.astro +7 -2
  185. package/src/components/figure/FloatCaption.astro +5 -3
  186. package/src/components/fold/Fold.astro +2 -2
  187. package/src/components/notation/NotationFormula.astro +81 -11
  188. package/src/components/notation/NotationGlossary.astro +132 -1
  189. package/src/components/notation/NotationLayer.astro +29 -0
  190. package/src/components/starlight/AboutPanel.astro +186 -3
  191. package/src/components/starlight/CollapsibleSections.astro +24 -3
  192. package/src/components/starlight/DiagramEnhancer.astro +5 -1
  193. package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
  194. package/src/components/starlight/LayoutHeader.astro +17 -0
  195. package/src/components/starlight/LayoutPageTitle.astro +110 -0
  196. package/src/components/starlight/LayoutSidebar.astro +807 -21
  197. package/src/components/starlight/LayoutSocialIcons.astro +62 -6
  198. package/src/components/starlight/LessonExportActions.astro +27 -9
  199. package/src/components/starlight/LessonFooter.astro +59 -0
  200. package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
  201. package/src/components/starlight/TextSizeControl.astro +24 -13
  202. package/src/components/starlight/page-marks.ts +139 -0
  203. package/src/components/starlight/section-summary.ts +1 -1
  204. package/src/components/starlight/sidebar-editions.ts +245 -0
  205. package/src/components/table/Table.astro +15 -7
  206. package/src/content-config.ts +87 -1
  207. package/src/curriculum/competency-graph.ts +95 -0
  208. package/src/reference/base-library.mjs +19 -0
  209. package/src/reference/consistency.ts +126 -39
  210. package/src/reference/deck-remark.mjs +600 -0
  211. package/src/reference/deck-source.ts +273 -0
  212. package/src/reference/decks-validate.ts +604 -0
  213. package/src/reference/diagram-layout.ts +3 -1
  214. package/src/reference/figure-symbols.ts +108 -31
  215. package/src/reference/folds-validate.ts +1 -1
  216. package/src/reference/gate-indices.ts +5 -3
  217. package/src/reference/gate-math.ts +169 -25
  218. package/src/reference/lab-katex-validate.ts +30 -15
  219. package/src/reference/lab-math-scope.ts +34 -3
  220. package/src/reference/math-glyphs.mjs +189 -25
  221. package/src/reference/page-notation.ts +69 -15
  222. package/src/reference/references.ts +28 -0
  223. package/src/reference/registry-scope.mjs +34 -0
  224. package/src/reference/registry.ts +41 -11
  225. package/src/reference/rehype-deck.mjs +286 -0
  226. package/src/reference/rehype-heading-anchors.mjs +48 -6
  227. package/src/reference/remark-notation.mjs +249 -40
  228. package/src/reference/source-lines.mjs +65 -0
  229. package/src/reference/types.ts +28 -0
  230. package/src/rules/catalog.ts +257 -4
  231. package/src/rules/course-rules.ts +21 -1
  232. package/src/rules/in-effect.ts +54 -8
  233. package/src/session/PreferenceStore.ts +23 -0
  234. package/src/session/deck-memory.ts +263 -0
  235. package/src/session/reading-memory.ts +243 -0
  236. package/src/styles/deck.css +1150 -0
  237. package/src/styles/global.css +958 -27
  238. package/src/styles/zones.css +277 -0
  239. /package/src/{components → reference}/float-name.ts +0 -0
@@ -13,85 +13,24 @@
13
13
  %% (‹part›.‹n›). LaTeX does the counting; explico checks every number against
14
14
  %% the website when it builds the PDF.
15
15
  \NeedsTeXFormat{LaTeX2e}
16
- \ProvidesClass{explico}[2026/10/04 explico course book class]
16
+ \ProvidesClass{explico}[2026/10/10 explico course book class]
17
17
 
18
18
  %% `scripts`: the document holds Greek, Cyrillic, CJK or emoji (the exporter
19
19
  %% sets it when it does), so LuaLaTeX loads the fallback fonts below.
20
- \newif\ifexplico@scripts
21
- \DeclareOption{scripts}{\explico@scriptstrue}
20
+ \DeclareOption{scripts}{\PassOptionsToPackage{scripts}{explico-common}}
22
21
  \DeclareOption*{\PassOptionsToClass{\CurrentOption}{book}}
23
22
  \ProcessOptions\relax
24
23
  \LoadClass[11pt,oneside,openany]{book}
25
24
 
26
- %% --- Engine and fonts ------------------------------------------------------
27
- \RequirePackage{iftex}
28
- \RequirePackage{amsmath,amssymb,mathtools}
29
- %% Rule N17's engine half, as on the website (src/reference/katex-delimiters.mjs):
30
- %% a negative \delimitershortfall makes every \left...\right pair strictly taller
31
- %% than its content, so a nest grows outward at each level. TeX's default (5pt)
32
- %% lets nested pairs round to the same glyph.
33
- \delimitershortfall=-1pt
34
- %% Whether a text font sets Greek; if none does, a Greek letter in prose is
35
- %% set as its math symbol (below, with the other Unicode characters).
36
- \newif\ifexplico@greek
37
- \ifPDFTeX
38
- \RequirePackage[T1]{fontenc}
39
- \RequirePackage{lmodern}
40
- \RequirePackage{textcomp}
41
- \RequirePackage{bm}
42
- %% A character no pdfTeX font sets (accented Greek, Chinese, an emoji) stays
43
- %% blank instead of stopping the compile, and the log says so in LuaLaTeX's
44
- %% words, which the export turns into its warning about blank characters.
45
- \def\UTFviii@undefined@err#1{%
46
- \typeout{Missing character: There is no
47
- \expandafter\UTFviii@splitcsname\string#1\relax\space in font pdftex!}}
48
- \else
49
- %% Latin Modern for text and math. unicode-math is also what lets a tagged
50
- %% PDF carry MathML for every formula (luamml needs it); bm does not work
51
- %% with it, so \bm becomes \boldsymbol.
52
- \RequirePackage{unicode-math}
53
- \providecommand{\bm}{\boldsymbol}
54
- %% Latin Modern has no Greek, Cyrillic, Chinese, Japanese, Korean or emoji.
55
- %% With `scripts`, prose in those scripts comes from fallback fonts: for each
56
- %% script, the first of the fonts below this machine has (CMU Serif, from
57
- %% TeX Live's cm-unicode, matches Latin Modern). A script none of them
58
- %% covers stays blank, and the export warns about it.
59
- \ifexplico@scripts
60
- \def\explico@fallbacks{}
61
- \def\explico@fallback#1#2{%
62
- \let\explico@found\@empty
63
- \@for\explico@candidate:=#1\do{%
64
- \ifx\explico@found\@empty
65
- \IfFontExistsTF{\explico@candidate}{%
66
- \edef\explico@fallbacks{\explico@fallbacks"\explico@candidate:mode=harf;",}%
67
- \let\explico@found\explico@candidate}{}%
68
- \fi}%
69
- \ifx\explico@found\@empty\else#2\fi}
70
- \explico@fallback{CMU Serif,Noto Serif,DejaVu Serif,FreeSerif}{\explico@greektrue}
71
- \explico@fallback{Noto Serif CJK SC,Noto Sans CJK SC,FandolSong,Source Han Serif SC}{}
72
- \explico@fallback{Noto Color Emoji,Twemoji Mozilla,Symbola}{}
73
- \ifx\explico@fallbacks\@empty\else
74
- \directlua{luaotfload.add_fallback("explico", {\explico@fallbacks})}%
75
- \setmainfont{Latin Modern Roman}[RawFeature={fallback=explico}]%
76
- \fi
77
- \fi
78
- \fi
79
- \RequirePackage{microtype}
25
+ %% What the deck theme shares: engine, fonts, colours, figures, TikZ, boxes,
26
+ %% Unicode, notation markers and cross-references (explico-common.sty).
27
+ \RequirePackage{explico-common}
80
28
 
81
- %% --- Page and colour --------------------------------------------------------
29
+ %% --- Page ---------------------------------------------------------------------
82
30
  \RequirePackage[margin=1in]{geometry}
83
- \RequirePackage{xcolor}
84
- \definecolor{explicoaccent}{HTML}{12775C}
85
- \definecolor{explicoframe}{HTML}{9DB8AD}
86
- \definecolor{explicoback}{HTML}{F3F8F6}
87
- \definecolor{explicoasidecaution}{HTML}{B5651D}
88
- \definecolor{explicoasidecautionback}{HTML}{FBF3EA}
89
- \definecolor{explicorule}{HTML}{8A9A93}
90
31
 
91
32
  %% --- Floats, tables and captions -------------------------------------------
92
- \RequirePackage{graphicx}
93
- \RequirePackage{array,booktabs,tabularx,longtable}
94
- \RequirePackage[export]{adjustbox}
33
+ \RequirePackage{longtable}
95
34
  \RequirePackage{caption}
96
35
  \RequirePackage{newfloat}
97
36
  \DeclareFloatingEnvironment[
@@ -155,105 +94,10 @@
155
94
  \captionsetup{type=#1}%
156
95
  }{\end{minipage}\par\medskip}
157
96
  \fi
158
- %% explico's short caption, run in bold before the long one.
159
- \newcommand*{\ExplicoCaptionTitle}[1]{\textbf{#1}}
160
-
161
- %% --- Diagrams ---------------------------------------------------------------
162
- \RequirePackage{tikz}
163
- \usetikzlibrary{arrows.meta}
164
- \tikzset{
165
- explico diagram node/.style={
166
- draw=explicorule, fill=explicoback, rounded corners=3pt, line width=0.5pt,
167
- align=center, inner sep=3pt, font=\small,
168
- },
169
- explico diagram edge/.style={
170
- draw=explicorule, line width=0.6pt, -{Stealth[length=5pt,width=4pt]},
171
- },
172
- }
173
97
 
174
- %% --- Boxes: memos, notes, asides, worked examples, checks ------------------
175
- \RequirePackage[most]{tcolorbox}
176
- \tcbset{
177
- explico/.style={
178
- enhanced, breakable, colback=explicoback, colframe=explicoframe,
179
- boxrule=0.5pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt,
180
- fonttitle=\bfseries\small, coltitle=black, colbacktitle=explicoback!60!white,
181
- titlerule=0.4pt, before skip=10pt plus 2pt, after skip=10pt plus 2pt,
182
- },
183
- }
184
98
  \RequirePackage{alphalph}
185
99
  \RequirePackage{enumitem}
186
100
 
187
- %% --- Unicode in text that the default fonts lack ---------------------------
188
- \RequirePackage{newunicodechar}
189
- \newunicodechar{°}{\ifmmode^\circ\else\textdegree\fi}
190
- \newunicodechar{−}{\ensuremath{-}}
191
- \newunicodechar{×}{\ensuremath{\times}}
192
- \newunicodechar{±}{\ensuremath{\pm}}
193
- \newunicodechar{≤}{\ensuremath{\le}}
194
- \newunicodechar{≥}{\ensuremath{\ge}}
195
- \newunicodechar{≈}{\ensuremath{\approx}}
196
- \newunicodechar{≠}{\ensuremath{\neq}}
197
- \newunicodechar{→}{\ensuremath{\rightarrow}}
198
- \newunicodechar{←}{\ensuremath{\leftarrow}}
199
- \newunicodechar{↑}{\ensuremath{\uparrow}}
200
- \newunicodechar{↓}{\ensuremath{\downarrow}}
201
- \newunicodechar{⟨}{\ensuremath{\langle}}
202
- \newunicodechar{⟩}{\ensuremath{\rangle}}
203
- \newunicodechar{⋮}{\ensuremath{\vdots}}
204
- \newunicodechar{√}{\ensuremath{\surd}}
205
- \newunicodechar{∞}{\ensuremath{\infty}}
206
- \newunicodechar{′}{\ensuremath{'}}
207
- \newunicodechar{𝒩}{\ensuremath{\mathcal{N}}}
208
- \newunicodechar{①}{\textcircled{\scriptsize 1}}
209
- \newunicodechar{②}{\textcircled{\scriptsize 2}}
210
- \newunicodechar{③}{\textcircled{\scriptsize 3}}
211
- \newunicodechar{④}{\textcircled{\scriptsize 4}}
212
- \newunicodechar{⑤}{\textcircled{\scriptsize 5}}
213
- \newunicodechar{₀}{\textsubscript{0}}
214
- \newunicodechar{₁}{\textsubscript{1}}
215
- \newunicodechar{₂}{\textsubscript{2}}
216
- \newunicodechar{₃}{\textsubscript{3}}
217
- \newunicodechar{¹}{\textsuperscript{1}}
218
- \newunicodechar{²}{\textsuperscript{2}}
219
- \newunicodechar{³}{\textsuperscript{3}}
220
- %% A Greek letter on its own is a symbol (the polar coordinates ρ and φ in a
221
- %% caption, which holds no math): it is set as its math symbol. Inside a Greek
222
- %% word, which the exporter marks \ExplicoGreekWord{…}, it is a letter, set
223
- %% from the text font's Greek where one is loaded (`scripts`).
224
- \newif\ifexplico@word
225
- \DeclareRobustCommand{\ExplicoGreekWord}[1]{{\explico@wordtrue#1}}
226
- %% Protected, as \ensuremath is: the text of an equation is expanded for its
227
- %% tagging (alt text, MathML), and an expandable body breaks it there.
228
- \protected\def\explico@greekletter#1#2{%
229
- \ifmmode#2\else
230
- \ifexplico@word
231
- \ifexplico@greek\char#1\relax\else\ensuremath{#2}\fi
232
- \else\ensuremath{#2}\fi
233
- \fi}
234
- \newunicodechar{α}{\explico@greekletter{"03B1}{\alpha}}
235
- \newunicodechar{β}{\explico@greekletter{"03B2}{\beta}}
236
- \newunicodechar{γ}{\explico@greekletter{"03B3}{\gamma}}
237
- \newunicodechar{δ}{\explico@greekletter{"03B4}{\delta}}
238
- \newunicodechar{ε}{\explico@greekletter{"03B5}{\varepsilon}}
239
- \newunicodechar{θ}{\explico@greekletter{"03B8}{\theta}}
240
- \newunicodechar{λ}{\explico@greekletter{"03BB}{\lambda}}
241
- \newunicodechar{μ}{\explico@greekletter{"03BC}{\mu}}
242
- \newunicodechar{π}{\explico@greekletter{"03C0}{\pi}}
243
- \newunicodechar{ρ}{\explico@greekletter{"03C1}{\rho}}
244
- \newunicodechar{σ}{\explico@greekletter{"03C3}{\sigma}}
245
- \newunicodechar{τ}{\explico@greekletter{"03C4}{\tau}}
246
- \newunicodechar{φ}{\explico@greekletter{"03C6}{\varphi}}
247
- \newunicodechar{ω}{\explico@greekletter{"03C9}{\omega}}
248
- \newunicodechar{Γ}{\explico@greekletter{"0393}{\Gamma}}
249
- \newunicodechar{Δ}{\explico@greekletter{"0394}{\Delta}}
250
- \newunicodechar{Θ}{\explico@greekletter{"0398}{\Theta}}
251
- \newunicodechar{Λ}{\explico@greekletter{"039B}{\Lambda}}
252
- \newunicodechar{Π}{\explico@greekletter{"03A0}{\Pi}}
253
- \newunicodechar{Σ}{\explico@greekletter{"03A3}{\Sigma}}
254
- \newunicodechar{Φ}{\explico@greekletter{"03A6}{\Phi}}
255
- \newunicodechar{Ω}{\explico@greekletter{"03A9}{\Omega}}
256
-
257
101
  %% --- Links ------------------------------------------------------------------
258
102
  \RequirePackage[colorlinks,linkcolor=explicoaccent,urlcolor=explicoaccent,citecolor=explicoaccent,bookmarksnumbered]{hyperref}
259
103
  \pdfstringdefDisableCommands{\let\ExplicoGreekWord\@firstofone}
@@ -309,18 +153,20 @@
309
153
  }
310
154
 
311
155
  %% --- Chapter front line -----------------------------------------------------
312
- %% Under a chapter's title: where it comes from and where its live version is.
156
+ %% Under a chapter's title: where it comes from, the version its content is at
157
+ %% (#2, "unchanged since version 0.2.0") and where its live version is.
313
158
  \newcommand{\ExplicoSource}[3]{%
314
159
  \begingroup\small\color{black!65}%
315
160
  \noindent From \emph{#1}%
316
- \if\relax\detokenize{#2}\relax\else, version~#2\fi.%
161
+ \if\relax\detokenize{#2}\relax\else, #2\fi.%
317
162
  \if\relax\detokenize{#3}\relax\else\ Interactive version: \url{#3}\fi
318
163
  \par\endgroup\medskip
319
164
  }
320
165
 
321
166
  %% --- Books: a part's or the course's PDF ------------------------------------
322
167
  %% The title page. #1 title, #2 what it belongs to (empty for the course),
323
- %% #3 content version, #4 authors, #5 website.
168
+ %% #3 the version the content is at ("unchanged since version 0.2.0"),
169
+ %% #4 authors, #5 website.
324
170
  \newcommand{\ExplicoTitlePage}[5]{%
325
171
  %% The title page shares page number i with the contents: no anchor of its own.
326
172
  \hypersetup{pageanchor=false}%
@@ -331,7 +177,7 @@
331
177
  \if\relax\detokenize{#2}\relax\else\vspace{0.8em}{\Large #2\par}\fi
332
178
  \if\relax\detokenize{#4}\relax\else\vspace{2.5em}{\large #4\par}\fi
333
179
  \vfill
334
- {Content version~#3\par}
180
+ {Content #3\par}
335
181
  \if\relax\detokenize{#5}\relax\else\vspace{0.4em}{\small\url{#5}\par}\fi
336
182
  \end{titlepage}%
337
183
  \hypersetup{pageanchor=true}%
@@ -345,22 +191,6 @@
345
191
  %% A part opens on its own page, numbered as on the website.
346
192
  \newcommand{\ExplicoPart}[2]{\setcounter{part}{\numexpr#1-1\relax}\part{#2}}
347
193
 
348
- %% --- Notation ---------------------------------------------------------------
349
- %% \explain{key}{tex} and \slot{key}{tex} are explico's markers in math: the
350
- %% website opens a card for the key; on paper they print their content.
351
- \providecommand{\explain}[2]{#2}
352
- \providecommand{\slot}[2]{#2}
353
- %% \ExplicoNotation{target}{tex}: a formula's symbol that links to its notation entry
354
- %% (the serializer writes it only where the book has that entry).
355
- \providecommand{\ExplicoNotation}[2]{\hyperlink{#1}{#2}}
356
- %% A term in prose with a notation card on the website.
357
- \newcommand{\ExplicoTerm}[2]{#2}
358
-
359
- %% --- Cross-references ---------------------------------------------------------
360
- %% #1 label, #2 text before the number, #3 text after it.
361
- \newcommand{\ExplicoRef}[3]{\hyperref[#1]{#2\ref*{#1}#3}}
362
- \newcommand{\ExplicoEqRef}[1]{\hyperref[#1]{(\ref*{#1})}}
363
-
364
194
  %% --- Memos --------------------------------------------------------------------
365
195
  \newenvironment{ExplicoMemo}[1]{%
366
196
  \refstepcounter{explicomemo}%
@@ -1,4 +1,4 @@
1
- import { captionTitle } from '../../../components/float-name';
1
+ import { captionTitle } from '../../../reference/float-name';
2
2
  import type { ContentSegment } from '../../../reference/mixed-content';
3
3
  import type {
4
4
  Block,
@@ -30,8 +30,11 @@ export interface ChapterContext {
30
30
  readonly pictures: ReadonlyMap<string, string>;
31
31
  /** Answers of this chapter's checks, by assessment id, in authored order. */
32
32
  readonly checkItems: ReadonlyMap<string, readonly CheckItem[]>;
33
- /** Course title, version and the chapter's live URL for the front line. */
34
- readonly source?: { readonly course: string; readonly version?: string; readonly url?: string };
33
+ /**
34
+ * The front line under the title: the course, the version the chapter's
35
+ * content is at (`versionPhrase`, rule C5) and its live URL.
36
+ */
37
+ readonly source?: { readonly course: string; readonly since?: string; readonly url?: string };
35
38
  /**
36
39
  * A chapter of a part's or the course's book: its citation keys gain the
37
40
  * chapter (`projectiles/projectile-range/openstax`), since each chapter
@@ -95,7 +98,8 @@ export function escapeText(text: string): string {
95
98
  .replace(GREEK_WORD, (word) => `\\ExplicoGreekWord{${word}}`);
96
99
  }
97
100
 
98
- function escapeUrl(url: string): string {
101
+ /** A URL as `\url` and `\href` take it: no braces or backslashes, `%` and `#` escaped. */
102
+ export function escapeUrl(url: string): string {
99
103
  return url.replace(/[\\{}]/gu, '').replace(/[%#]/gu, (char) => `\\${char}`);
100
104
  }
101
105
 
@@ -237,19 +241,19 @@ export function linkExplains(tex: string, linkTo: (key: string) => string | unde
237
241
  }
238
242
  }
239
243
 
240
- class ChapterWriter {
244
+ export class ChapterWriter {
241
245
  private readonly route: string;
242
246
  private readonly citationKeys: Map<string, string>;
243
247
  /** Above zero inside a moving argument (a heading, a caption, a title) or a link: no link goes there. */
244
248
  private unlinked = 0;
245
249
 
246
- constructor(private readonly context: ChapterContext) {
250
+ constructor(protected readonly context: ChapterContext) {
247
251
  this.route = context.page.route;
248
252
  this.citationKeys = entryKeys(citationKeys(context.page.blocks));
249
253
  }
250
254
 
251
255
  /** A reference-list entry's citation key in this document, by its anchor. */
252
- private citeKey(anchor: string): string {
256
+ protected citeKey(anchor: string): string {
253
257
  const key = this.citationKeys.get(anchor) ?? anchor;
254
258
  return this.context.inBook ? `${this.route}/${key}` : key;
255
259
  }
@@ -259,7 +263,7 @@ class ChapterWriter {
259
263
  }
260
264
 
261
265
  /** Renders `write` with links to notation off: a link cannot nest, or sit in a moving argument. */
262
- private unlinkedWhile<T>(write: () => T): T {
266
+ protected unlinkedWhile<T>(write: () => T): T {
263
267
  this.unlinked++;
264
268
  try {
265
269
  return write();
@@ -269,22 +273,22 @@ class ChapterWriter {
269
273
  }
270
274
 
271
275
  /** Inline content with no notation links inside it. */
272
- private plain(inlines: readonly Inline[]): string {
276
+ protected plain(inlines: readonly Inline[]): string {
273
277
  return this.unlinkedWhile(() => this.inlines(inlines));
274
278
  }
275
279
 
276
280
  /** A formula's TeX, with its notation markers linked to the appendix where the document has the entry. */
277
- private math(tex: string): string {
281
+ protected math(tex: string): string {
278
282
  const targets = this.context.notationTargets;
279
283
  if (this.unlinked > 0 || !targets || targets.size === 0) return tex;
280
284
  return linkExplains(tex, (key) => (targets.has(key) ? labelOf('notation', key) : undefined));
281
285
  }
282
286
 
283
- private inDocument(target: CourseTarget): boolean {
287
+ protected inDocument(target: CourseTarget): boolean {
284
288
  return this.context.documentRoutes.has(target.route);
285
289
  }
286
290
 
287
- private canonicalUrl(target: CourseTarget): string | undefined {
291
+ protected canonicalUrl(target: CourseTarget): string | undefined {
288
292
  const base = this.context.canonicalBaseUrl;
289
293
  if (!base) return undefined;
290
294
  const path = target.route ? `${target.route}/` : '';
@@ -292,7 +296,7 @@ class ChapterWriter {
292
296
  }
293
297
 
294
298
  /** A cross-reference: LaTeX's number inside the document, the website's outside it. */
295
- private reference(refKind: string, target: CourseTarget, children: readonly Inline[]): string {
299
+ protected reference(refKind: string, target: CourseTarget, children: readonly Inline[]): string {
296
300
  const text = plainText(children);
297
301
  if (this.inDocument(target) && target.anchor) {
298
302
  const label = labelOf(target.route, target.anchor);
@@ -352,14 +356,14 @@ class ChapterWriter {
352
356
  return inlines.map((inline) => this.inline(inline)).join('');
353
357
  }
354
358
 
355
- private segments(segments: readonly ContentSegment[]): string {
359
+ protected segments(segments: readonly ContentSegment[]): string {
356
360
  return segments
357
361
  .map((segment) => (segment.type === 'math' ? `$${this.math(segment.latex)}$` : escapeText(segment.value)))
358
362
  .join('');
359
363
  }
360
364
 
361
365
  /** A float's caption: its short caption for the lists, bold before the long one. */
362
- private caption(block: { short?: string; caption: readonly Inline[]; anchor: string }, extra: readonly Inline[] = []): string {
366
+ protected caption(block: { short?: string; caption: readonly Inline[]; anchor: string }, extra: readonly Inline[] = []): string {
363
367
  const short = block.short?.trim();
364
368
  const long = [this.plain(block.caption), this.plain(extra)].filter(Boolean).join(' ');
365
369
  const title = short ? `\\ExplicoCaptionTitle{${escapeText(captionTitle(short))}} ` : '';
@@ -371,13 +375,13 @@ class ChapterWriter {
371
375
  * A float. Inside a box (a memo, a worked example) a float cannot float, so
372
376
  * it is set where it stands (`ExplicoHere`, explico.cls) and keeps its number.
373
377
  */
374
- private float(kind: string, inPlace: boolean, content: string, caption: string, captionFirst = false): string {
378
+ protected float(kind: string, inPlace: boolean, content: string, caption: string, captionFirst = false): string {
375
379
  const parts = captionFirst ? [caption, content] : [content, caption];
376
380
  if (inPlace) return [`\\begin{ExplicoHere}{${kind}}`, ...parts, '\\end{ExplicoHere}'].join('\n');
377
381
  return [`\\begin{${kind}}[htbp]`, '\\centering', ...parts, `\\end{${kind}}`].join('\n');
378
382
  }
379
383
 
380
- private picture(anchor: string, picture: FigurePicture, alt: string): string {
384
+ protected picture(anchor: string, picture: FigurePicture, alt: string): string {
381
385
  if (picture.kind === 'diagram') return diagramTikz(picture.geometry, this);
382
386
  const file = this.context.pictures.get(anchor);
383
387
  if (file) {
@@ -392,7 +396,7 @@ class ChapterWriter {
392
396
  )}: see the interactive version on the course website.}}`;
393
397
  }
394
398
 
395
- private table(block: Extract<Block, { kind: 'table' }>, inBox = false): string {
399
+ protected table(block: Extract<Block, { kind: 'table' }>, inBox = false): string {
396
400
  const rows = [...block.head, ...block.body];
397
401
  const columns = Math.max(0, ...rows.map((row) => row.reduce((sum, cell) => sum + cell.colspan, 0)));
398
402
  if (columns === 0) return '';
@@ -454,11 +458,11 @@ class ChapterWriter {
454
458
  }
455
459
 
456
460
  /** A choice's label, its math typeset like the prompt's. */
457
- private optionLabel(option: NonNullable<CheckItem['options']>[number]): string {
461
+ protected optionLabel(option: NonNullable<CheckItem['options']>[number]): string {
458
462
  return option.labelSegments ? this.segments(option.labelSegments) : escapeText(option.label);
459
463
  }
460
464
 
461
- private check(block: Extract<Block, { kind: 'check' }>): string {
465
+ protected check(block: Extract<Block, { kind: 'check' }>): string {
462
466
  const items = this.context.checkItems.get(block.assessmentId) ?? [];
463
467
  const questions = items.map((item) => {
464
468
  const options =
@@ -688,7 +692,7 @@ export function serializeChapter(context: ChapterContext): string {
688
692
  ? [`\\ExplicoInteractive{${escapeUrl(source.url)}}`]
689
693
  : []
690
694
  : [
691
- `\\ExplicoSource{${escapeText(source.course)}}{${escapeText(source.version ?? '')}}{${
695
+ `\\ExplicoSource{${escapeText(source.course)}}{${escapeText(source.since ?? '')}}{${
692
696
  source.url ? escapeUrl(source.url) : ''
693
697
  }}`,
694
698
  ];
@@ -1,7 +1,7 @@
1
1
  # TeX Live 2026 packages for explico's LaTeX export, for the package_file of
2
2
  # zauguin/install-texlive; tlmgr adds each one's dependencies. This is what a
3
- # chapter reads under LuaLaTeX and pdfLaTeX (latexmk -recorder), less what
4
- # latex-bin brings. Package names change between TeX Live years: check the
3
+ # chapter and a deck read under LuaLaTeX and pdfLaTeX (latexmk -recorder),
4
+ # less what latex-bin brings. Package names change between TeX Live years: check the
5
5
  # list again when moving texlive_version.
6
6
 
7
7
  # Engines, formats, build tool
@@ -41,6 +41,10 @@ tcolorbox
41
41
  unicode-math
42
42
  xcolor
43
43
 
44
+ # beamerthemeexplico.sty: a deck's slides and handout
45
+ beamer
46
+ translator
47
+
44
48
  # What those load
45
49
  amscls
46
50
  bigintcalc