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.
Files changed (279) hide show
  1. package/CHANGELOG.md +1122 -1
  2. package/README.md +91 -13
  3. package/dist/compiler/astro.d.ts +4 -3
  4. package/dist/compiler/astro.js +45 -15
  5. package/dist/compiler/cli.d.ts +26 -12
  6. package/dist/compiler/cli.js +96 -25
  7. package/dist/compiler/collections.d.ts +9 -1
  8. package/dist/compiler/collections.js +55 -16
  9. package/dist/compiler/content-schema.d.ts +31 -0
  10. package/dist/compiler/content-schema.js +82 -0
  11. package/dist/compiler/content-schemas.d.ts +422 -0
  12. package/dist/compiler/content-schemas.js +229 -0
  13. package/dist/compiler/course-config.d.ts +1 -1
  14. package/dist/compiler/course-config.js +30 -2
  15. package/dist/compiler/exports/combined-html.d.ts +97 -0
  16. package/dist/compiler/exports/combined-html.js +492 -0
  17. package/dist/compiler/exports/download-index.d.ts +9 -0
  18. package/dist/compiler/exports/download-index.js +33 -0
  19. package/dist/compiler/exports/export-chapters.d.ts +98 -0
  20. package/dist/compiler/exports/export-chapters.js +164 -0
  21. package/dist/compiler/exports/export-pages.d.ts +48 -0
  22. package/dist/compiler/exports/export-pages.js +22 -0
  23. package/dist/compiler/exports/html-tree.d.ts +22 -0
  24. package/dist/compiler/exports/html-tree.js +47 -0
  25. package/dist/compiler/exports/latex/capture.d.ts +106 -0
  26. package/dist/compiler/exports/latex/capture.js +435 -0
  27. package/dist/compiler/exports/latex/compile.d.ts +73 -0
  28. package/dist/compiler/exports/latex/compile.js +115 -0
  29. package/dist/compiler/exports/latex/document.d.ts +102 -0
  30. package/dist/compiler/exports/latex/document.js +349 -0
  31. package/dist/compiler/exports/latex/explico.cls +415 -0
  32. package/dist/compiler/exports/latex/numbering.d.ts +18 -0
  33. package/dist/compiler/exports/latex/numbering.js +57 -0
  34. package/dist/compiler/exports/latex/serialize.d.ts +112 -0
  35. package/dist/compiler/exports/latex/serialize.js +606 -0
  36. package/dist/compiler/exports/latex-export.d.ts +44 -0
  37. package/dist/compiler/exports/latex-export.js +169 -0
  38. package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
  39. package/dist/compiler/exports/markdown/serialize.js +527 -0
  40. package/dist/compiler/exports/markdown-export.d.ts +39 -0
  41. package/dist/compiler/exports/markdown-export.js +240 -0
  42. package/dist/compiler/exports/standalone-html.d.ts +14 -5
  43. package/dist/compiler/exports/standalone-html.js +23 -51
  44. package/dist/compiler/exports/static-tree.d.ts +253 -0
  45. package/dist/compiler/exports/static-tree.js +625 -0
  46. package/dist/compiler/exports/zip.d.ts +16 -0
  47. package/dist/compiler/exports/zip.js +72 -0
  48. package/dist/compiler/lesson-derivation.d.ts +8 -2
  49. package/dist/compiler/lesson-derivation.js +6 -7
  50. package/dist/compiler/manifest.d.ts +87 -6
  51. package/dist/compiler/manifest.js +174 -31
  52. package/dist/compiler/render-diagnostics.d.ts +65 -0
  53. package/dist/compiler/render-diagnostics.js +56 -0
  54. package/dist/compiler/resolution-report.d.ts +2 -1
  55. package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
  56. package/dist/curriculum/validation.d.ts +9 -3
  57. package/dist/curriculum/validation.js +35 -17
  58. package/dist/reference/base-abbreviations.d.mts +2 -2
  59. package/dist/reference/base-abbreviations.mjs +2 -2
  60. package/dist/reference/base-operators.d.mts +2 -2
  61. package/dist/reference/base-operators.mjs +2 -2
  62. package/dist/reference/base-roman-labels.d.mts +4 -3
  63. package/dist/reference/base-roman-labels.mjs +4 -3
  64. package/dist/reference/citation-format.d.mts +35 -0
  65. package/dist/reference/citation-format.mjs +74 -0
  66. package/dist/reference/consistency.d.ts +5 -9
  67. package/dist/reference/consistency.js +34 -38
  68. package/dist/reference/curriculum-alignment.js +3 -24
  69. package/dist/reference/derived-entries.d.ts +45 -0
  70. package/dist/reference/derived-entries.js +50 -0
  71. package/dist/reference/derived-notation.d.mts +125 -0
  72. package/dist/reference/derived-notation.mjs +121 -0
  73. package/dist/reference/diagram-fit.d.ts +45 -0
  74. package/dist/reference/diagram-fit.js +54 -0
  75. package/dist/reference/diagram-layout.d.ts +59 -10
  76. package/dist/reference/diagram-layout.js +85 -24
  77. package/dist/reference/diagrams-validate.d.ts +1 -1
  78. package/dist/reference/equations-validate.d.ts +1 -1
  79. package/dist/reference/equations-validate.js +26 -1
  80. package/dist/reference/equations.d.mts +27 -0
  81. package/dist/reference/equations.mjs +120 -22
  82. package/dist/reference/examples-validate.d.ts +1 -1
  83. package/dist/reference/figure-fit.d.ts +70 -0
  84. package/dist/reference/figure-fit.js +60 -0
  85. package/dist/reference/figure-pictures.d.ts +27 -0
  86. package/dist/reference/figure-pictures.js +49 -0
  87. package/dist/reference/figures-validate.d.ts +1 -1
  88. package/dist/reference/gate-abbreviations.d.ts +1 -1
  89. package/dist/reference/gate-abbreviations.js +1 -1
  90. package/dist/reference/gate-course-rules.d.ts +4 -3
  91. package/dist/reference/gate-course-rules.js +9 -4
  92. package/dist/reference/gate-heading-length.d.ts +2 -0
  93. package/dist/reference/gate-heading-length.js +7 -5
  94. package/dist/reference/gate-heading-structure.js +26 -0
  95. package/dist/reference/gate-indices.d.ts +35 -0
  96. package/dist/reference/gate-indices.js +299 -0
  97. package/dist/reference/gate-math.d.ts +32 -2
  98. package/dist/reference/gate-math.js +68 -12
  99. package/dist/reference/gate-mdx-expression.d.ts +17 -0
  100. package/dist/reference/gate-mdx-expression.js +29 -0
  101. package/dist/reference/gate-part-pages.d.ts +1 -1
  102. package/dist/reference/gate-prop-text.d.ts +28 -0
  103. package/dist/reference/gate-prop-text.js +106 -0
  104. package/dist/reference/gate-prose-notation.d.ts +3 -1
  105. package/dist/reference/gate-prose-notation.js +4 -2
  106. package/dist/reference/gate-retired-keys.d.ts +35 -0
  107. package/dist/reference/gate-retired-keys.js +58 -0
  108. package/dist/reference/gloss.d.ts +2 -0
  109. package/dist/reference/katex-measure.d.ts +17 -0
  110. package/dist/reference/katex-measure.js +223 -0
  111. package/dist/reference/keyed-float-validate.js +12 -0
  112. package/dist/reference/keyed-jsx-floats.d.mts +2 -2
  113. package/dist/reference/keyed-jsx-floats.mjs +11 -14
  114. package/dist/reference/lab-math-scope.d.ts +1 -0
  115. package/dist/reference/lab-math-scope.js +13 -2
  116. package/dist/reference/math-glyphs.d.mts +94 -2
  117. package/dist/reference/math-glyphs.mjs +851 -46
  118. package/dist/reference/mixed-content.d.ts +8 -3
  119. package/dist/reference/mixed-content.js +4 -2
  120. package/dist/reference/page-notation.d.ts +34 -4
  121. package/dist/reference/page-notation.js +54 -2
  122. package/dist/reference/references.d.ts +11 -1
  123. package/dist/reference/references.js +6 -0
  124. package/dist/reference/registry.js +104 -10
  125. package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
  126. package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
  127. package/dist/reference/rehype-heading-anchors.mjs +11 -1
  128. package/dist/reference/remark-citation.mjs +0 -0
  129. package/dist/reference/remark-notation.mjs +84 -20
  130. package/dist/reference/render-lab-math.d.ts +2 -0
  131. package/dist/reference/render-lab-math.js +17 -5
  132. package/dist/reference/sections-validate.d.ts +28 -3
  133. package/dist/reference/sections-validate.js +31 -4
  134. package/dist/reference/sections.d.mts +24 -6
  135. package/dist/reference/sections.mjs +92 -17
  136. package/dist/reference/tables-validate.d.ts +1 -1
  137. package/dist/reference/tables-validate.js +24 -1
  138. package/dist/reference/text-measure.d.ts +10 -0
  139. package/dist/reference/text-measure.js +41 -0
  140. package/dist/reference/text-widths.d.ts +10 -0
  141. package/dist/reference/text-widths.js +1238 -0
  142. package/dist/reference/types.d.ts +64 -8
  143. package/dist/rules/catalog.js +191 -30
  144. package/dist/rules/in-effect.d.ts +1 -1
  145. package/dist/rules/rules.schema.json +79 -24
  146. package/dist/rules/settings.d.ts +1 -1
  147. package/dist/rules/settings.js +7 -0
  148. package/dist/session/PreferenceStore.d.ts +18 -1
  149. package/dist/session/PreferenceStore.js +29 -1
  150. package/docs/authoring/README.md +32 -21
  151. package/docs/authoring/agents.md +38 -7
  152. package/docs/authoring/components.md +111 -6
  153. package/docs/authoring/migrating.md +663 -0
  154. package/docs/authoring/notation.md +281 -29
  155. package/docs/authoring/retired.md +1 -0
  156. package/docs/authoring/structure-rules.md +10 -41
  157. package/docs/authoring/structure.md +53 -2
  158. package/docs/authoring/visualizations.md +144 -18
  159. package/package.json +12 -7
  160. package/src/compiler/astro.ts +57 -17
  161. package/src/compiler/cli.ts +143 -35
  162. package/src/compiler/collections.ts +92 -19
  163. package/src/compiler/content-schema.ts +108 -0
  164. package/src/compiler/content-schemas.ts +391 -0
  165. package/src/compiler/course-config.ts +86 -15
  166. package/src/compiler/exports/combined-html.ts +669 -0
  167. package/src/compiler/exports/download-index.ts +36 -0
  168. package/src/compiler/exports/export-chapters.ts +233 -0
  169. package/src/compiler/exports/export-pages.ts +44 -0
  170. package/src/compiler/exports/html-tree.ts +62 -0
  171. package/src/compiler/exports/latex/capture.ts +651 -0
  172. package/src/compiler/exports/latex/compile.ts +168 -0
  173. package/src/compiler/exports/latex/document.ts +479 -0
  174. package/src/compiler/exports/latex/explico.cls +415 -0
  175. package/src/compiler/exports/latex/numbering.ts +74 -0
  176. package/src/compiler/exports/latex/serialize.ts +787 -0
  177. package/src/compiler/exports/latex/texlive-packages.txt +76 -0
  178. package/src/compiler/exports/latex-export.ts +255 -0
  179. package/src/compiler/exports/markdown/serialize.ts +632 -0
  180. package/src/compiler/exports/markdown-export.ts +329 -0
  181. package/src/compiler/exports/standalone-html.ts +39 -70
  182. package/src/compiler/exports/static-tree.ts +944 -0
  183. package/src/compiler/exports/zip.ts +88 -0
  184. package/src/compiler/lesson-derivation.ts +13 -8
  185. package/src/compiler/manifest.ts +299 -28
  186. package/src/compiler/render-diagnostics.ts +111 -0
  187. package/src/components/HoverPanel.astro +204 -6
  188. package/src/components/PartOverview.astro +12 -9
  189. package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
  190. package/src/components/assessments/AssessmentRunner.tsx +7 -1
  191. package/src/components/assessments/AssessmentSet.astro +14 -1
  192. package/src/components/block-heading.ts +38 -0
  193. package/src/components/card-arrow.ts +18 -4
  194. package/src/components/card-carry.ts +406 -0
  195. package/src/components/card-drag.ts +1 -30
  196. package/src/components/card-fetch.ts +87 -0
  197. package/src/components/card-manager.ts +1222 -38
  198. package/src/components/card-memory.ts +5 -1
  199. package/src/components/card-placement.ts +211 -13
  200. package/src/components/card-side.ts +281 -31
  201. package/src/components/citation/CitationLayer.astro +45 -40
  202. package/src/components/diagram/Diagram.astro +123 -44
  203. package/src/components/figure/Figure.astro +11 -0
  204. package/src/components/figure/FloatCaption.astro +4 -17
  205. package/src/components/float-toolbar.ts +274 -0
  206. package/src/components/memo/Memo.astro +21 -10
  207. package/src/components/memo/ShortNote.astro +4 -0
  208. package/src/components/notation/NotationGlossary.astro +22 -17
  209. package/src/components/notation/NotationLayer.astro +300 -79
  210. package/src/components/notation/NotationMath.astro +8 -1
  211. package/src/components/notation/render-notation-math.ts +51 -5
  212. package/src/components/readable-text.ts +25 -0
  213. package/src/components/reference/FloatRefLayer.astro +162 -89
  214. package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
  215. package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
  216. package/src/components/starlight/LayoutPageSidebar.astro +17 -3
  217. package/src/components/starlight/LayoutSocialIcons.astro +79 -29
  218. package/src/components/starlight/LessonExportActions.astro +384 -310
  219. package/src/components/starlight/LessonFooter.astro +47 -9
  220. package/src/components/starlight/PrintController.astro +28 -0
  221. package/src/components/starlight/TextSizeControl.astro +137 -0
  222. package/src/components/starlight/print-controller.ts +380 -0
  223. package/src/components/starlight/section-summary.ts +13 -5
  224. package/src/components/table/Table.astro +13 -21
  225. package/src/components/table/table-truncation.ts +15 -0
  226. package/src/content-config.ts +20 -312
  227. package/src/curriculum/validation.ts +72 -24
  228. package/src/reference/base-abbreviations.mjs +2 -2
  229. package/src/reference/base-operators.mjs +2 -2
  230. package/src/reference/base-roman-labels.mjs +4 -3
  231. package/src/reference/citation-format.mjs +74 -0
  232. package/src/reference/consistency.ts +47 -61
  233. package/src/reference/curriculum-alignment.ts +4 -41
  234. package/src/reference/derived-entries.ts +68 -0
  235. package/src/reference/derived-notation.mjs +121 -0
  236. package/src/reference/diagram-fit.ts +104 -0
  237. package/src/reference/diagram-layout.ts +182 -51
  238. package/src/reference/diagrams-validate.ts +2 -1
  239. package/src/reference/equations-validate.ts +41 -2
  240. package/src/reference/equations.mjs +120 -22
  241. package/src/reference/examples-validate.ts +1 -0
  242. package/src/reference/figure-fit.ts +139 -0
  243. package/src/reference/figure-pictures.ts +96 -0
  244. package/src/reference/figures-validate.ts +2 -1
  245. package/src/reference/gate-abbreviations.ts +1 -1
  246. package/src/reference/gate-course-rules.ts +22 -7
  247. package/src/reference/gate-heading-length.ts +25 -7
  248. package/src/reference/gate-heading-structure.ts +40 -1
  249. package/src/reference/gate-indices.ts +470 -0
  250. package/src/reference/gate-math.ts +102 -13
  251. package/src/reference/gate-mdx-expression.ts +45 -0
  252. package/src/reference/gate-prop-text.ts +145 -0
  253. package/src/reference/gate-prose-notation.ts +19 -5
  254. package/src/reference/gate-retired-keys.ts +101 -0
  255. package/src/reference/gloss.ts +2 -0
  256. package/src/reference/katex-measure.ts +384 -0
  257. package/src/reference/keyed-float-validate.ts +22 -0
  258. package/src/reference/keyed-jsx-floats.mjs +11 -14
  259. package/src/reference/lab-math-scope.ts +15 -1
  260. package/src/reference/math-glyphs.mjs +851 -46
  261. package/src/reference/mixed-content.ts +11 -2
  262. package/src/reference/page-notation.ts +100 -3
  263. package/src/reference/references.ts +14 -0
  264. package/src/reference/registry.ts +154 -8
  265. package/src/reference/rehype-fail-katex-errors.mjs +19 -0
  266. package/src/reference/rehype-heading-anchors.mjs +11 -1
  267. package/src/reference/remark-citation.mjs +0 -0
  268. package/src/reference/remark-notation.mjs +84 -20
  269. package/src/reference/render-lab-math.ts +29 -5
  270. package/src/reference/sections-validate.ts +67 -3
  271. package/src/reference/sections.mjs +92 -17
  272. package/src/reference/tables-validate.ts +40 -2
  273. package/src/reference/text-measure.ts +63 -0
  274. package/src/reference/text-widths.ts +133 -0
  275. package/src/reference/types.ts +75 -8
  276. package/src/rules/catalog.ts +205 -29
  277. package/src/rules/settings.ts +10 -0
  278. package/src/session/PreferenceStore.ts +53 -0
  279. package/src/styles/global.css +291 -75
@@ -64,10 +64,13 @@ const { aliases = false } = Astro.props;
64
64
 
65
65
  <script>
66
66
  import { animatedScrollIntoView } from '../scroll-animation';
67
+ import { startFloatToolbar } from '../float-toolbar';
67
68
 
68
69
  (() => {
69
- const root = document.querySelector('.sl-markdown-content');
70
+ const root = document.querySelector<HTMLElement>('.sl-markdown-content');
70
71
  if (!root) return;
72
+ // The number's toolbar: Pin, Dock, Copy link; and Copy LaTeX's place.
73
+ startFloatToolbar(root);
71
74
 
72
75
  // `#sec-1.1.2` → the element printing that number, if this page has one.
73
76
  const ALIAS = /^#(sec|eq|tab|fig|dia|check|ex)-(\d+(?:\.\d+)+)$/;
@@ -94,27 +97,27 @@ const { aliases = false } = Astro.props;
94
97
  const KINDS: readonly Kind[] = [
95
98
  {
96
99
  prefix: '#eq-',
97
- triggerSelector: 'a.equation-ref, a.keyed-equation__number',
100
+ triggerSelector: 'a.equation-ref, a.keyed-equation__number, a.float-toolbar__link',
98
101
  numberSelector: '.keyed-equation__number',
99
102
  reveal: true,
100
103
  },
101
- // A float's caption line links to it twice: its number, and the chain
102
- // icon beside it (which also copies the link, `HeadingAnchorEnhancer`).
104
+ // A float is linked by its number, and by its number's toolbar's Copy
105
+ // link, which also copies the link (`float-toolbar.ts`).
103
106
  {
104
107
  prefix: '#tab-',
105
- triggerSelector: 'a.table-ref, a.keyed-table__number, a.float-caption__anchor',
108
+ triggerSelector: 'a.table-ref, a.keyed-table__number, a.float-toolbar__link',
106
109
  numberSelector: '.keyed-table__number',
107
110
  reveal: false,
108
111
  },
109
112
  {
110
113
  prefix: '#fig-',
111
- triggerSelector: 'a.figure-ref, a.keyed-figure__number, a.float-caption__anchor',
114
+ triggerSelector: 'a.figure-ref, a.keyed-figure__number, a.float-toolbar__link',
112
115
  numberSelector: '.keyed-figure__number',
113
116
  reveal: false,
114
117
  },
115
118
  {
116
119
  prefix: '#dia-',
117
- triggerSelector: 'a.diagram-ref, a.diagram__number, a.float-caption__anchor',
120
+ triggerSelector: 'a.diagram-ref, a.diagram__number, a.float-toolbar__link',
118
121
  numberSelector: '.diagram__number',
119
122
  reveal: false,
120
123
  },
@@ -183,7 +186,7 @@ const { aliases = false } = Astro.props;
183
186
  root.addEventListener('click', (event) => {
184
187
  const origin = event.target instanceof Element ? event.target : null;
185
188
  if (!origin) return;
186
- if (origin.closest('a.keyed-equation__number, [data-notation-key]')) {
189
+ if (origin.closest('a.keyed-equation__number, [data-notation-key], .float-toolbar')) {
187
190
  return;
188
191
  }
189
192
 
@@ -228,7 +231,7 @@ const { aliases = false } = Astro.props;
228
231
  if (!origin || origin.getAttribute('title')?.startsWith('Copy LaTeX')) return;
229
232
  if (
230
233
  origin.closest(
231
- '[data-notation-key], [data-eq-copy], a.keyed-equation__number',
234
+ '[data-notation-key], [data-eq-copy], a.keyed-equation__number, .float-toolbar',
232
235
  )
233
236
  ) {
234
237
  return;
@@ -260,7 +263,22 @@ const { aliases = false } = Astro.props;
260
263
  // `ResizeObserver` catches every case that changes that: first layout, a
261
264
  // viewport change, and a collapsed section opening (no layout box while
262
265
  // closed, so no overflow either).
266
+ // Which edge an overflowing equation is cut at: `data-overflow` is
267
+ // `end`, `start`, or `start end`, and `global.css` fades that edge, so a
268
+ // reader on a phone sees the formula goes on (there is no scrollbar).
269
+ const updateEquationEdges = (display: HTMLElement) => {
270
+ const scroll = Math.abs(display.scrollLeft);
271
+ const edges = [
272
+ scroll > 1 ? 'start' : '',
273
+ scroll + display.clientWidth < display.scrollWidth - 1 ? 'end' : '',
274
+ ]
275
+ .filter(Boolean)
276
+ .join(' ');
277
+ if (edges) display.setAttribute('data-overflow', edges);
278
+ else display.removeAttribute('data-overflow');
279
+ };
263
280
  const updateEquationScroll = (display: HTMLElement) => {
281
+ updateEquationEdges(display);
264
282
  if (display.scrollWidth > display.clientWidth + 1) {
265
283
  const number = display
266
284
  .closest<HTMLElement>('.keyed-equation')
@@ -282,6 +300,7 @@ const { aliases = false } = Astro.props;
282
300
  });
283
301
  for (const display of root.querySelectorAll<HTMLElement>('.katex-display')) {
284
302
  equationResize.observe(display);
303
+ display.addEventListener('scroll', () => updateEquationEdges(display), { passive: true });
285
304
  }
286
305
 
287
306
  window.addEventListener('hashchange', (event) => {
@@ -1,7 +1,13 @@
1
1
  ---
2
2
  import DefaultPageSidebar from '@astrojs/starlight/components/PageSidebar.astro';
3
3
  import { isLessonSlug, lessonIdFromSlug } from '../../compiler/lesson-derivation';
4
- import { titledBlocksOf, tocWithTitledBlocks } from '../block-heading';
4
+ import {
5
+ mathHeadingTextsOf,
6
+ titledBlocksOf,
7
+ tocWithHeadingTexts,
8
+ tocWithTitledBlocks,
9
+ withHeadingTexts,
10
+ } from '../block-heading';
5
11
 
6
12
  // "On this page" lists the `<h2>` a knowledge check and a worked-example set
7
13
  // draw (docs/plans/titles-and-containers.md Part 6): a component renders
@@ -11,17 +17,25 @@ import { titledBlocksOf, tocWithTitledBlocks } from '../block-heading';
11
17
  // mobile alike) renders it. Each is listed by its title, as a section is;
12
18
  // `data-titled-blocks` tells the script below which entries they are, so
13
19
  // `global.css` draws the badge's icon before them.
20
+ //
21
+ // A heading with inline math is listed by the manifest's text for it: Astro
22
+ // lists a heading by all the text in it, which for a formula is its MathML,
23
+ // its LaTeX and its glyphs run together.
14
24
  const route = Astro.locals.starlightRoute;
15
25
  const routeSlug = route.entry.id;
16
26
  let blockKinds: Record<string, string> | undefined;
17
27
  if (route.toc && isLessonSlug(routeSlug)) {
18
- const blocks = await titledBlocksOf(lessonIdFromSlug(routeSlug));
28
+ const lessonId = lessonIdFromSlug(routeSlug);
29
+ const texts = await mathHeadingTextsOf(lessonId);
30
+ const blocks = await titledBlocksOf(lessonId);
19
31
  if (blocks.length > 0) {
20
32
  route.toc = {
21
33
  ...route.toc,
22
- items: tocWithTitledBlocks(route.toc, route.headings, blocks),
34
+ items: tocWithTitledBlocks(route.toc, withHeadingTexts(route.headings, texts), blocks),
23
35
  };
24
36
  blockKinds = Object.fromEntries(blocks.map((block) => [block.anchor, block.kind]));
37
+ } else if (texts.size > 0) {
38
+ route.toc = { ...route.toc, items: tocWithHeadingTexts(route.toc.items, texts) };
25
39
  }
26
40
  }
27
41
  ---
@@ -1,38 +1,88 @@
1
1
  ---
2
2
  /**
3
3
  * Starlight allows one `SocialIcons` override, so this composes the stock
4
- * social-link row with lesson export and `[?]` About controls. They sit in the
5
- * header's right cluster; the About panel is present on every route.
4
+ * social-link row with page export and `[?]` About controls. They sit in the
5
+ * header's right cluster; the About panel is present on every route, the
6
+ * export actions on every page the manifest knows (each chapter, the landing
7
+ * page, a part's page, the glossary, … — not the 404 route). On a touch
8
+ * screen the reader's text size follows (`TextSizeControl.astro`); in the
9
+ * phone menu it sits beside the theme switch.
10
+ *
11
+ * The Download menu lists the scopes that contain the page (D1, D4): "This
12
+ * chapter" on a chapter, "This part" on a chapter or its part's page, and
13
+ * "Whole course" on every page. Each scope downloads as standalone HTML (a
14
+ * part and the course as one file each); with `latex`, as PDF and LaTeX
15
+ * source (a part and the course as a book); with `markdown`, as Markdown. A page that is not a chapter has no download of its own (D8):
16
+ * its content is in its part's and the course's files.
6
17
  */
7
18
  import DefaultSocialIcons from '@astrojs/starlight/components/SocialIcons.astro';
8
19
  import { loadManifest } from '../../compiler/manifest';
20
+ import { docsSlugsForRoute } from '../../compiler/lesson-derivation';
9
21
  import {
10
- isLessonSlug,
11
- lessonIdFromSlug,
12
- } from '../../compiler/lesson-derivation';
22
+ courseFileStem,
23
+ exportPageSlugs,
24
+ standaloneFilename,
25
+ } from '../../compiler/exports/export-pages';
13
26
  import AboutPanel from './AboutPanel.astro';
14
- import LessonExportActions from './LessonExportActions.astro';
27
+ import LessonExportActions, {
28
+ type DownloadCell,
29
+ type DownloadRow,
30
+ } from './LessonExportActions.astro';
31
+ import TextSizeControl from './TextSizeControl.astro';
15
32
 
16
- const routeSlug = Astro.locals.starlightRoute.entry.id;
17
- const lessonId = isLessonSlug(routeSlug)
18
- ? lessonIdFromSlug(routeSlug)
19
- : undefined;
20
- let standaloneDownload:
21
- | { readonly href: string; readonly filename: string }
22
- | undefined;
23
- let printEnabled = false;
24
- let canonicalBaseUrl: string | undefined;
25
- if (lessonId) {
26
- const manifest = await loadManifest();
27
- const lesson = manifest.lessons.find((entry) => entry.id === lessonId);
28
- printEnabled = lesson !== undefined && manifest.lessonExports.print;
29
- canonicalBaseUrl = manifest.lessonExports.canonicalBaseUrl;
30
- if (lesson && manifest.lessonExports.standaloneHtml) {
31
- const base = import.meta.env.BASE_URL.replace(/\/$/, '');
32
- standaloneDownload = {
33
- href: `${base}/downloads/${lesson.slug}.html`,
34
- filename: `${lesson.slug.split('/').at(-1) ?? 'lesson'}.html`,
35
- };
33
+ const manifest = await loadManifest();
34
+ const routeSlugs = docsSlugsForRoute(Astro.locals.starlightRoute.entry.id);
35
+ const pageSlug = exportPageSlugs(manifest).find((slug) =>
36
+ routeSlugs.includes(slug),
37
+ );
38
+ const exports = manifest.lessonExports;
39
+ const printEnabled = pageSlug !== undefined && exports.print;
40
+ const lesson = manifest.lessons.find((entry) => entry.slug === pageSlug);
41
+ const part = lesson
42
+ ? manifest.parts.find((entry) => entry.ordinal === lesson.part)
43
+ : manifest.parts.find((entry) => `${entry.dir}/index` === pageSlug);
44
+
45
+ const downloads: DownloadRow[] = [];
46
+ if (lesson) {
47
+ const name = lesson.slug.split('/').at(-1) ?? lesson.slug;
48
+ const cells: DownloadCell[] = [];
49
+ if (exports.standaloneHtml) {
50
+ cells.push({ format: 'html', path: `${lesson.slug}.html`, filename: standaloneFilename(lesson.slug) });
51
+ }
52
+ if (exports.latex) {
53
+ cells.push(
54
+ { format: 'pdf', path: `${lesson.slug}.pdf`, filename: `${name}.pdf` },
55
+ { format: 'latex', path: `${lesson.slug}-latex.zip`, filename: `${name}-latex.zip` },
56
+ );
57
+ }
58
+ if (exports.markdown) {
59
+ cells.push({ format: 'markdown', path: `${lesson.slug}-markdown.zip`, filename: `${name}-markdown.zip` });
60
+ }
61
+ if (cells.length > 0) {
62
+ downloads.push({ scope: 'chapter', label: 'This chapter', title: lesson.title, cells });
63
+ }
64
+ }
65
+ if (pageSlug !== undefined) {
66
+ /** A part's or the course's downloads: `stem` names the files a reader saves. */
67
+ const wholeCells = (path: string, stem: string): DownloadCell[] => [
68
+ ...(exports.standaloneHtml ? [{ format: 'html' as const, path: `${path}.html`, filename: `${stem}.html` }] : []),
69
+ ...(exports.latex
70
+ ? [
71
+ { format: 'pdf' as const, path: `${path}.pdf`, filename: `${stem}.pdf` },
72
+ { format: 'latex' as const, path: `${path}-latex.zip`, filename: `${stem}-latex.zip` },
73
+ ]
74
+ : []),
75
+ ...(exports.markdown
76
+ ? [{ format: 'markdown' as const, path: `${path}-markdown.zip`, filename: `${stem}-markdown.zip` }]
77
+ : []),
78
+ ];
79
+ const partCells = part && part.lessons.length > 0 ? wholeCells(`part/${part.dir}`, part.dir) : [];
80
+ if (part && partCells.length > 0) {
81
+ downloads.push({ scope: 'part', label: 'This part', title: part.label, cells: partCells });
82
+ }
83
+ const courseCells = wholeCells('course', courseFileStem(manifest));
84
+ if (courseCells.length > 0) {
85
+ downloads.push({ scope: 'course', label: 'Whole course', title: manifest.about.material.name, cells: courseCells });
36
86
  }
37
87
  }
38
88
  ---
@@ -40,12 +90,12 @@ if (lessonId) {
40
90
  <DefaultSocialIcons />
41
91
  <AboutPanel />
42
92
  {
43
- (standaloneDownload || printEnabled) && (
93
+ (downloads.length > 0 || printEnabled) && (
44
94
  <LessonExportActions
45
- standaloneDownload={standaloneDownload}
95
+ downloads={downloads}
46
96
  print={printEnabled}
47
- canonicalBaseUrl={canonicalBaseUrl}
48
97
  siteBaseUrl={import.meta.env.BASE_URL}
49
98
  />
50
99
  )
51
100
  }
101
+ <TextSizeControl />