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.
Files changed (256) hide show
  1. package/CHANGELOG.md +990 -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 +100 -0
  6. package/dist/compiler/cli-main.js +433 -0
  7. package/dist/compiler/cli-reference.d.ts +50 -0
  8. package/dist/compiler/cli-reference.js +213 -0
  9. package/dist/compiler/cli-verbs.d.ts +45 -0
  10. package/dist/compiler/cli-verbs.js +224 -0
  11. package/dist/compiler/cli-verify.d.ts +51 -0
  12. package/dist/compiler/cli-verify.js +151 -0
  13. package/dist/compiler/cli.d.ts +408 -24
  14. package/dist/compiler/cli.js +1328 -8
  15. package/dist/compiler/collections.d.ts +88 -1
  16. package/dist/compiler/collections.js +170 -25
  17. package/dist/compiler/content-release.d.ts +49 -0
  18. package/dist/compiler/content-release.js +63 -0
  19. package/dist/compiler/content-schemas.d.ts +2 -2
  20. package/dist/compiler/course-config.d.ts +1 -1
  21. package/dist/compiler/course-config.js +105 -1
  22. package/dist/compiler/decks.d.ts +161 -0
  23. package/dist/compiler/decks.js +235 -0
  24. package/dist/compiler/exports/combined-html.d.ts +5 -1
  25. package/dist/compiler/exports/deck-fit.d.ts +55 -0
  26. package/dist/compiler/exports/deck-fit.js +90 -0
  27. package/dist/compiler/exports/export-chapters.d.ts +37 -6
  28. package/dist/compiler/exports/export-chapters.js +64 -5
  29. package/dist/compiler/exports/export-pages.d.ts +30 -3
  30. package/dist/compiler/exports/export-pages.js +24 -1
  31. package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  32. package/dist/compiler/exports/latex/cache.d.ts +78 -0
  33. package/dist/compiler/exports/latex/cache.js +129 -0
  34. package/dist/compiler/exports/latex/capture.d.ts +10 -1
  35. package/dist/compiler/exports/latex/capture.js +3 -1
  36. package/dist/compiler/exports/latex/compile.d.ts +13 -2
  37. package/dist/compiler/exports/latex/compile.js +12 -1
  38. package/dist/compiler/exports/latex/deck.d.ts +92 -0
  39. package/dist/compiler/exports/latex/deck.js +349 -0
  40. package/dist/compiler/exports/latex/document.d.ts +51 -6
  41. package/dist/compiler/exports/latex/document.js +71 -12
  42. package/dist/compiler/exports/latex/explico-common.sty +198 -0
  43. package/dist/compiler/exports/latex/explico.cls +13 -183
  44. package/dist/compiler/exports/latex/serialize.d.ts +60 -4
  45. package/dist/compiler/exports/latex/serialize.js +4 -2
  46. package/dist/compiler/exports/latex-export.d.ts +24 -2
  47. package/dist/compiler/exports/latex-export.js +107 -26
  48. package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
  49. package/dist/compiler/exports/markdown/serialize.js +85 -8
  50. package/dist/compiler/exports/markdown-export.d.ts +6 -1
  51. package/dist/compiler/exports/markdown-export.js +50 -11
  52. package/dist/compiler/exports/standalone-html.d.ts +10 -5
  53. package/dist/compiler/exports/standalone-html.js +3 -3
  54. package/dist/compiler/exports/static-tree.d.ts +38 -1
  55. package/dist/compiler/exports/static-tree.js +120 -4
  56. package/dist/compiler/knowledge-version.d.ts +26 -2
  57. package/dist/compiler/knowledge-version.js +27 -17
  58. package/dist/compiler/lesson-derivation.d.ts +143 -6
  59. package/dist/compiler/lesson-derivation.js +145 -13
  60. package/dist/compiler/manifest.d.ts +133 -4
  61. package/dist/compiler/manifest.js +364 -58
  62. package/dist/compiler/page-versions.d.ts +184 -0
  63. package/dist/compiler/page-versions.js +217 -0
  64. package/dist/compiler/render-diagnostics.d.ts +5 -1
  65. package/dist/compiler/resolution-report.d.ts +24 -6
  66. package/dist/compiler/resolution-report.js +22 -4
  67. package/dist/compiler/validate-content.d.ts +7 -0
  68. package/dist/compiler/validate-content.js +160 -0
  69. package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
  70. package/dist/curriculum/competency-graph.d.ts +54 -0
  71. package/dist/curriculum/competency-graph.js +41 -0
  72. package/dist/reference/base-library.d.mts +8 -0
  73. package/dist/reference/base-library.mjs +19 -0
  74. package/dist/reference/consistency.d.ts +8 -4
  75. package/dist/reference/consistency.js +82 -26
  76. package/dist/reference/deck-remark.d.mts +123 -0
  77. package/dist/reference/deck-remark.mjs +600 -0
  78. package/dist/reference/deck-source.d.ts +107 -0
  79. package/dist/reference/deck-source.js +140 -0
  80. package/dist/reference/decks-validate.d.ts +133 -0
  81. package/dist/reference/decks-validate.js +359 -0
  82. package/dist/reference/derived-entries.d.ts +5 -1
  83. package/dist/reference/diagram-fit.d.ts +6 -2
  84. package/dist/reference/diagram-fit.js +5 -1
  85. package/dist/reference/figure-symbols.d.ts +23 -6
  86. package/dist/reference/figure-symbols.js +51 -15
  87. package/dist/reference/float-name.d.ts +43 -0
  88. package/dist/reference/float-name.js +58 -0
  89. package/dist/reference/folds-validate.d.ts +1 -1
  90. package/dist/reference/gate-abbreviations.d.ts +1 -1
  91. package/dist/reference/gate-indices.js +5 -4
  92. package/dist/reference/gate-math.d.ts +29 -2
  93. package/dist/reference/gate-math.js +99 -18
  94. package/dist/reference/gate-part-pages.d.ts +1 -1
  95. package/dist/reference/lab-katex-validate.d.ts +10 -6
  96. package/dist/reference/lab-katex-validate.js +17 -9
  97. package/dist/reference/lab-math-scope.js +29 -4
  98. package/dist/reference/math-glyphs.d.mts +25 -0
  99. package/dist/reference/math-glyphs.mjs +189 -25
  100. package/dist/reference/page-notation.d.ts +22 -3
  101. package/dist/reference/page-notation.js +32 -10
  102. package/dist/reference/references.d.ts +14 -1
  103. package/dist/reference/references.js +13 -0
  104. package/dist/reference/registry-scope.d.mts +33 -0
  105. package/dist/reference/registry-scope.mjs +34 -0
  106. package/dist/reference/registry.js +29 -10
  107. package/dist/reference/rehype-deck.d.mts +6 -0
  108. package/dist/reference/rehype-deck.mjs +286 -0
  109. package/dist/reference/rehype-heading-anchors.d.mts +20 -0
  110. package/dist/reference/rehype-heading-anchors.mjs +48 -6
  111. package/dist/reference/remark-notation.mjs +249 -40
  112. package/dist/reference/source-lines.d.mts +12 -0
  113. package/dist/reference/source-lines.mjs +65 -0
  114. package/dist/reference/types.d.ts +28 -0
  115. package/dist/rules/catalog.d.ts +21 -4
  116. package/dist/rules/catalog.js +237 -2
  117. package/dist/rules/course-rules.d.ts +5 -1
  118. package/dist/rules/course-rules.js +17 -3
  119. package/dist/rules/course-rules.schema.json +12 -0
  120. package/dist/rules/in-effect.d.ts +13 -3
  121. package/dist/rules/in-effect.js +34 -10
  122. package/dist/rules/rules.schema.json +280 -1
  123. package/dist/rules/settings.d.ts +1 -1
  124. package/dist/session/PreferenceStore.d.ts +6 -0
  125. package/dist/session/PreferenceStore.js +18 -1
  126. package/dist/session/deck-memory.d.ts +116 -0
  127. package/dist/session/deck-memory.js +143 -0
  128. package/dist/session/reading-memory.d.ts +96 -0
  129. package/dist/session/reading-memory.js +133 -0
  130. package/docs/VERSIONING.md +21 -18
  131. package/docs/authoring/README.md +31 -9
  132. package/docs/authoring/agents.md +72 -18
  133. package/docs/authoring/cli-reference.json +2172 -0
  134. package/docs/authoring/cli.md +887 -0
  135. package/docs/authoring/components.md +15 -0
  136. package/docs/authoring/deck.md +612 -0
  137. package/docs/authoring/migrating.md +1195 -16
  138. package/docs/authoring/notation.md +49 -6
  139. package/docs/authoring/structure.md +80 -1
  140. package/docs/authoring/versioning.md +56 -3
  141. package/docs/authoring/visualizations.md +7 -2
  142. package/docs/course-agent/AGENTS.md +40 -0
  143. package/docs/course-agent/agents/rules-reviewer.md +33 -0
  144. package/docs/course-agent/skills/draft-lesson/SKILL.md +38 -0
  145. package/docs/course-agent/skills/migrate-course/SKILL.md +32 -0
  146. package/docs/course-agent/skills/release-content/SKILL.md +26 -0
  147. package/docs/diagram-scaling.md +148 -0
  148. package/docs/privacy.md +59 -0
  149. package/package.json +8 -2
  150. package/src/compiler/astro.ts +60 -21
  151. package/src/compiler/cli-main.ts +557 -0
  152. package/src/compiler/cli-reference.ts +285 -0
  153. package/src/compiler/cli-verbs.ts +270 -0
  154. package/src/compiler/cli-verify.ts +241 -0
  155. package/src/compiler/cli.ts +2119 -170
  156. package/src/compiler/collections.ts +284 -25
  157. package/src/compiler/content-release.ts +91 -0
  158. package/src/compiler/course-config.ts +267 -0
  159. package/src/compiler/decks.ts +455 -0
  160. package/src/compiler/exports/deck-fit.ts +127 -0
  161. package/src/compiler/exports/export-chapters.ts +105 -6
  162. package/src/compiler/exports/export-pages.ts +46 -2
  163. package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  164. package/src/compiler/exports/latex/cache.ts +183 -0
  165. package/src/compiler/exports/latex/capture.ts +5 -2
  166. package/src/compiler/exports/latex/compile.ts +18 -0
  167. package/src/compiler/exports/latex/deck.ts +419 -0
  168. package/src/compiler/exports/latex/document.ts +117 -21
  169. package/src/compiler/exports/latex/explico-common.sty +198 -0
  170. package/src/compiler/exports/latex/explico.cls +13 -183
  171. package/src/compiler/exports/latex/serialize.ts +25 -21
  172. package/src/compiler/exports/latex/texlive-packages.txt +6 -2
  173. package/src/compiler/exports/latex-export.ts +187 -45
  174. package/src/compiler/exports/markdown/serialize.ts +118 -9
  175. package/src/compiler/exports/markdown-export.ts +56 -11
  176. package/src/compiler/exports/standalone-html.ts +8 -7
  177. package/src/compiler/exports/static-tree.ts +175 -3
  178. package/src/compiler/knowledge-version.ts +56 -21
  179. package/src/compiler/lesson-derivation.ts +282 -16
  180. package/src/compiler/manifest.ts +568 -63
  181. package/src/compiler/page-versions.ts +401 -0
  182. package/src/compiler/resolution-report.ts +38 -7
  183. package/src/compiler/validate-content.ts +215 -0
  184. package/src/components/CourseContents.astro +124 -6
  185. package/src/components/CurriculumMap.astro +49 -1
  186. package/src/components/FloatIndex.astro +1 -1
  187. package/src/components/PartOverview.astro +51 -5
  188. package/src/components/assessments/AssessmentSet.astro +41 -15
  189. package/src/components/block-heading.ts +15 -6
  190. package/src/components/card-carry.ts +7 -4
  191. package/src/components/card-manager.ts +14 -2
  192. package/src/components/card-memory.ts +13 -9
  193. package/src/components/deck/DeckEnhancer.astro +13 -0
  194. package/src/components/deck/deck-context.ts +104 -0
  195. package/src/components/deck/deck-controller.ts +1287 -0
  196. package/src/components/deck/deck-map.ts +91 -0
  197. package/src/components/deck-lane.ts +163 -0
  198. package/src/components/diagram/Diagram.astro +131 -86
  199. package/src/components/disclosure/persisted-disclosure.ts +9 -1
  200. package/src/components/figure/Figure.astro +7 -2
  201. package/src/components/figure/FloatCaption.astro +5 -3
  202. package/src/components/fold/Fold.astro +2 -2
  203. package/src/components/notation/NotationFormula.astro +81 -11
  204. package/src/components/notation/NotationGlossary.astro +132 -1
  205. package/src/components/notation/NotationLayer.astro +15 -7
  206. package/src/components/starlight/AboutPanel.astro +186 -3
  207. package/src/components/starlight/CollapsibleSections.astro +24 -3
  208. package/src/components/starlight/DiagramEnhancer.astro +5 -1
  209. package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
  210. package/src/components/starlight/LayoutHeader.astro +17 -0
  211. package/src/components/starlight/LayoutPageTitle.astro +110 -0
  212. package/src/components/starlight/LayoutSidebar.astro +807 -21
  213. package/src/components/starlight/LayoutSocialIcons.astro +62 -6
  214. package/src/components/starlight/LessonExportActions.astro +27 -9
  215. package/src/components/starlight/LessonFooter.astro +59 -0
  216. package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
  217. package/src/components/starlight/TextSizeControl.astro +24 -13
  218. package/src/components/starlight/page-marks.ts +139 -0
  219. package/src/components/starlight/section-summary.ts +1 -1
  220. package/src/components/starlight/sidebar-editions.ts +245 -0
  221. package/src/components/table/Table.astro +15 -7
  222. package/src/content-config.ts +87 -1
  223. package/src/curriculum/competency-graph.ts +95 -0
  224. package/src/reference/base-library.mjs +19 -0
  225. package/src/reference/consistency.ts +137 -39
  226. package/src/reference/deck-remark.mjs +600 -0
  227. package/src/reference/deck-source.ts +273 -0
  228. package/src/reference/decks-validate.ts +604 -0
  229. package/src/reference/diagram-fit.ts +18 -4
  230. package/src/reference/diagram-layout.ts +3 -1
  231. package/src/reference/figure-symbols.ts +108 -31
  232. package/src/reference/folds-validate.ts +1 -1
  233. package/src/reference/gate-indices.ts +5 -3
  234. package/src/reference/gate-math.ts +169 -25
  235. package/src/reference/lab-katex-validate.ts +30 -15
  236. package/src/reference/lab-math-scope.ts +34 -3
  237. package/src/reference/math-glyphs.mjs +189 -25
  238. package/src/reference/page-notation.ts +69 -15
  239. package/src/reference/references.ts +28 -0
  240. package/src/reference/registry-scope.mjs +34 -0
  241. package/src/reference/registry.ts +41 -11
  242. package/src/reference/rehype-deck.mjs +286 -0
  243. package/src/reference/rehype-heading-anchors.mjs +48 -6
  244. package/src/reference/remark-notation.mjs +249 -40
  245. package/src/reference/source-lines.mjs +65 -0
  246. package/src/reference/types.ts +28 -0
  247. package/src/rules/catalog.ts +274 -4
  248. package/src/rules/course-rules.ts +21 -1
  249. package/src/rules/in-effect.ts +58 -11
  250. package/src/session/PreferenceStore.ts +23 -0
  251. package/src/session/deck-memory.ts +263 -0
  252. package/src/session/reading-memory.ts +243 -0
  253. package/src/styles/deck.css +1150 -0
  254. package/src/styles/global.css +958 -27
  255. package/src/styles/zones.css +277 -0
  256. /package/src/{components → reference}/float-name.ts +0 -0
@@ -63,14 +63,23 @@ export async function titledBlocksOf(
63
63
  * The text a reader sees for each outline heading with inline math, by its
64
64
  * anchor (`manifest.lessons[].headings`, v30). Astro lists a heading by all
65
65
  * the text in it, and KaTeX writes a formula three ways — MathML, its LaTeX
66
- * annotation and its glyphs — so its own list reads `R\explain{k}{R}R`.
66
+ * annotation and its glyphs — so its own list reads `R\explain{k}{R}R`. A
67
+ * deck beside its chapter, or a part's, is no lesson: its page lists its
68
+ * frames by their titles as the reader sees them (`decks[].frames`).
67
69
  */
68
- export async function mathHeadingTextsOf(lessonId: string): Promise<ReadonlyMap<string, string>> {
69
- const lesson = (await manifestOnce()).lessons.find((entry) => entry.id === lessonId);
70
+ export async function mathHeadingTextsOf(pageId: string): Promise<ReadonlyMap<string, string>> {
71
+ const manifest = await manifestOnce();
72
+ const lesson = manifest.lessons.find((entry) => entry.id === pageId);
73
+ if (lesson) {
74
+ return new Map(
75
+ lesson.headings.filter((heading) => heading.math).map((heading) => [heading.slug, heading.text]),
76
+ );
77
+ }
78
+ const deck = (manifest.decks ?? []).find((entry) => entry.pageId === pageId);
70
79
  return new Map(
71
- (lesson?.headings ?? [])
72
- .filter((heading) => heading.math)
73
- .map((heading) => [heading.slug, heading.text]),
80
+ (deck?.frames ?? []).flatMap((frame): [string, string][] =>
81
+ frame.slug === undefined ? [] : [[frame.slug, frame.title]],
82
+ ),
74
83
  );
75
84
  }
76
85
 
@@ -63,6 +63,7 @@ import {
63
63
  createBrowserPreferenceStore,
64
64
  createSessionPreferenceStore,
65
65
  } from '../session/PreferenceStore';
66
+ import { rememberReading } from '../session/reading-memory';
66
67
  import { decodeCard, keyOf, type RememberedCard } from './card-memory';
67
68
  import type { HoldEdge } from './card-placement';
68
69
 
@@ -305,21 +306,23 @@ const setKey = (base: string) => `explico:cards:carried:${base}`;
305
306
 
306
307
  /**
307
308
  * K5, D9: this tab's own set; a tab with none takes the course's last set,
308
- * which is its own from then on.
309
+ * which is its own from then on. The course's last set is reading memory
310
+ * (`../session/reading-memory.ts`): it is read and kept only with the
311
+ * reader's yes, so without it a new tab starts with no cards.
309
312
  */
310
313
  export function readCarried(base: string): CarriedSet {
311
314
  const own = decodeCarried(tab.get(setKey(base)));
312
315
  if (own) return own;
313
- const last = decodeCarried(preferences.get(setKey(base))) ?? EMPTY_SET;
316
+ const last = (rememberReading(base) ? decodeCarried(preferences.get(setKey(base))) : null) ?? EMPTY_SET;
314
317
  tab.set(setKey(base), encodeCarried(last));
315
318
  return last;
316
319
  }
317
320
 
318
- /** K5: this tab's set, written as the course's last set too. Returns what was stored. */
321
+ /** K5: this tab's set, written as the course's last set too with the reader's yes. Returns what was stored. */
319
322
  export function writeCarried(base: string, set: CarriedSet): string {
320
323
  const text = encodeCarried(set);
321
324
  tab.set(setKey(base), text);
322
- preferences.set(setKey(base), text);
325
+ if (rememberReading(base)) preferences.set(setKey(base), text);
323
326
  return text;
324
327
  }
325
328
 
@@ -75,6 +75,10 @@ import {
75
75
  type CarriedSet,
76
76
  type OpenedAt,
77
77
  } from './card-carry';
78
+ import {
79
+ READING_MEMORY_EVENT,
80
+ type ReadingMemoryChange,
81
+ } from '../session/reading-memory';
78
82
  import { isThisPage, pageTitleOf } from './card-fetch';
79
83
  import { wireJumpArrow } from './card-jump';
80
84
  import { readableText } from './readable-text';
@@ -2051,7 +2055,7 @@ function rememberCards(): void {
2051
2055
  width: card.width,
2052
2056
  });
2053
2057
  }
2054
- writeRememberedCards({
2058
+ writeRememberedCards(base, {
2055
2059
  cards: saved,
2056
2060
  pointed: pointedKey !== null && keys.has(pointedKey) ? pointedKey : null,
2057
2061
  });
@@ -2599,11 +2603,19 @@ function install(): void {
2599
2603
  if (carry) {
2600
2604
  restoreCarriedSet(readCarried(base));
2601
2605
  } else {
2602
- const page = readRememberedCards();
2606
+ const page = readRememberedCards(base);
2603
2607
  remembered = [...page.cards];
2604
2608
  rememberedPointed = page.pointed;
2605
2609
  }
2606
2610
  window.addEventListener('pagehide', leavePage);
2611
+ // A yes to reading memory keeps what is open now beyond the tab; a no has
2612
+ // already deleted the kept copies, and the tab's own go on.
2613
+ window.addEventListener(READING_MEMORY_EVENT, (event) => {
2614
+ const { answer } = (event as CustomEvent<ReadingMemoryChange>).detail;
2615
+ if (answer !== 'yes') return;
2616
+ lastStored = null;
2617
+ leavePage();
2618
+ });
2607
2619
  document.addEventListener('visibilitychange', () => {
2608
2620
  if (document.visibilityState === 'hidden') leavePage();
2609
2621
  });
@@ -2,7 +2,10 @@
2
2
  * Remembering a page's open explanation cards. A reader who leaves a lesson
3
3
  * with cards pinned, docked or moved finds them where they were on coming
4
4
  * back to it: through a link, Back or a reload, in another tab, or after the
5
- * browser restarts. Closing the last card forgets the page.
5
+ * browser restarts. Closing the last card forgets the page. This is reading
6
+ * memory (`../session/reading-memory.ts`): kept in the browser only with the
7
+ * reader's yes; without it, in the tab's session, so Back, a reload and a
8
+ * link still bring the cards back until the tab is closed.
6
9
  *
7
10
  * The card manager (`./card-manager.ts`) saves the kept cards as the page is
8
11
  * left (`pagehide`) and restores them as each layer registers. A card is
@@ -17,7 +20,7 @@
17
20
  * `decodeCards` and `restorable` are pure, so `tests/unit/card-memory.test.ts`
18
21
  * checks them in Node.
19
22
  */
20
- import { createBrowserPreferenceStore } from '../session/PreferenceStore';
23
+ import { readingMemoryStore } from '../session/reading-memory';
21
24
  import type { MovedSpot } from './card-drag';
22
25
  import type { CardAnchor } from './card-placement';
23
26
 
@@ -159,21 +162,22 @@ export function restorable(
159
162
  // ---------------------------------------------------------------------------
160
163
 
161
164
  // The one seam for browser storage; this module never touches it itself.
162
- // Local storage, so the cards outlive the tab and a browser restart; tabs
163
- // open on the same page share one entry, and the last one left wins.
164
- const store = createBrowserPreferenceStore();
165
-
165
+ // With the reader's yes, local storage, so the cards outlive the tab and a
166
+ // browser restart (tabs open on the same page share one entry, and the last
167
+ // one left wins); without it, the tab's session storage.
166
168
  /**
167
169
  * One entry per page, kept only while the page has cards to bring back. The
168
170
  * path includes a course's base, so courses on one origin never share one.
169
171
  */
170
172
  const storageKey = () => `explico:cards:${window.location.pathname}`;
171
173
 
172
- /** The cards this page had open when the reader last left it. */
173
- export const readRememberedCards = (): RememberedPage => decodeCards(store.get(storageKey()));
174
+ /** The cards this page had open when the reader last left it, in the course at `base`. */
175
+ export const readRememberedCards = (base: string): RememberedPage =>
176
+ decodeCards(readingMemoryStore(base).get(storageKey()));
174
177
 
175
178
  /** Remember this page's cards; with none, the page is forgotten. */
176
- export function writeRememberedCards(page: RememberedPage): void {
179
+ export function writeRememberedCards(base: string, page: RememberedPage): void {
180
+ const store = readingMemoryStore(base);
177
181
  const key = storageKey();
178
182
  if (page.cards.length > 0) store.set(key, encodeCards(page));
179
183
  else if (store.remove) store.remove(key);
@@ -0,0 +1,13 @@
1
+ ---
2
+ /**
3
+ * A deck page's behaviour in the browser: the scaled frames, Present mode,
4
+ * the presenter window and the steps' effects (`deck-controller.ts`).
5
+ * `LessonFooter.astro` includes it on a deck's page only.
6
+ */
7
+ ---
8
+
9
+ <script>
10
+ import { installDecks } from './deck-controller';
11
+
12
+ installDecks();
13
+ </script>
@@ -0,0 +1,104 @@
1
+ /**
2
+ * What a component on a deck page (`<lesson>.deck.mdx`, `<part>/index.deck.mdx`,
3
+ * docs/authoring/deck.md) needs to know about it, read from the one manifest:
4
+ * a float on a deck carries no number of its own, and one the deck recalls
5
+ * from a chapter keeps the chapter's number, which links to the chapter. A
6
+ * course's own wrapper around `<Figure>`, `<Table>` or `<Diagram>` forwards
7
+ * `number` and `anchorId` as it always did, and nothing more.
8
+ */
9
+ import { loadManifest, type Manifest } from '../../compiler/manifest';
10
+ import type { ManifestDeck } from '../../compiler/manifest';
11
+ import { routeIdFromLocals } from '../../reference/page-notation';
12
+
13
+ /** The manifest's deck the page renders, if the page is one. */
14
+ export function deckOfRoute(manifest: Pick<Manifest, 'decks'>, routeId: string | undefined): ManifestDeck | undefined {
15
+ if (routeId === undefined) return undefined;
16
+ return (manifest.decks ?? []).find((deck) => deck.slug === routeId);
17
+ }
18
+
19
+ /** A part's deck, by the part's directory. */
20
+ export function partDeckOf(manifest: Pick<Manifest, 'decks'>, dir: string): ManifestDeck | undefined {
21
+ return (manifest.decks ?? []).find((deck) => deck.part === dir);
22
+ }
23
+
24
+ /**
25
+ * Where a chapter's page sends a reader for its slides ("Slides for this
26
+ * chapter", and the sidebar's icon): its own deck, or else the section of
27
+ * its part's deck that covers it, at the section's divider (`#slide-N`). A
28
+ * lesson's own deck wins. `path` is below the site's base: `motion/x/slides/`.
29
+ */
30
+ export function chapterSlides(
31
+ manifest: Pick<Manifest, 'decks'>,
32
+ chapterSlug: string,
33
+ ): { readonly deck: ManifestDeck; readonly path: string } | undefined {
34
+ const decks = manifest.decks ?? [];
35
+ const own = decks.find((deck) => deck.chapter === chapterSlug);
36
+ if (own) return { deck: own, path: `${own.slug}/` };
37
+ for (const deck of decks) {
38
+ const cover = deck.part === undefined ? undefined : deck.covers.find((entry) => entry.chapter === chapterSlug);
39
+ if (cover) return { deck, path: `${deck.slug}/#slide-${cover.frame}` };
40
+ }
41
+ return undefined;
42
+ }
43
+
44
+ /** A sidebar link as Starlight hands it to the footer's pagination. */
45
+ export interface PaginationLink {
46
+ readonly type: string;
47
+ readonly label?: string;
48
+ readonly href?: string;
49
+ readonly attrs?: Readonly<Record<string, unknown>>;
50
+ readonly isCurrent?: boolean;
51
+ }
52
+
53
+ /**
54
+ * Next and Previous on a deck's page: the decks before and after it in the
55
+ * sidebar's order — a lesson's deck, a slides-first lesson, a part's deck —
56
+ * each named "Slides: *title*". `links` is the sidebar flattened, `current`
57
+ * the deck's own place in it; `titleOf` gives a deck's title by its link.
58
+ */
59
+ export function deckNeighbours<T extends PaginationLink>(
60
+ links: readonly T[],
61
+ current: number,
62
+ titleOf: (link: T) => string | undefined,
63
+ ): { readonly prev?: T; readonly next?: T } {
64
+ const isDeck = (link: T | undefined) => link?.attrs?.['data-edition'] === 'slides';
65
+ const named = (link: T): T => {
66
+ const title = titleOf(link);
67
+ return title === undefined ? link : { ...link, label: `Slides: ${title}` };
68
+ };
69
+ const step = (direction: 1 | -1): T | undefined => {
70
+ for (let index = current + direction; index >= 0 && index < links.length; index += direction) {
71
+ if (isDeck(links[index])) return named(links[index]!);
72
+ }
73
+ return undefined;
74
+ };
75
+ const prev = step(-1);
76
+ const next = step(1);
77
+ return { ...(prev === undefined ? {} : { prev }), ...(next === undefined ? {} : { next }) };
78
+ }
79
+
80
+ export interface DeckFloat {
81
+ /** The float is on a deck page, where a float of the deck's own has no number. */
82
+ readonly onDeck: boolean;
83
+ /** Where a recalled float's number links: the float on its chapter's page. */
84
+ readonly numberHref?: string;
85
+ }
86
+
87
+ /**
88
+ * A float on the page `locals` renders, anchored at `anchorId`. A recalled
89
+ * one links to its chapter: a lesson's deck's, or on a part's deck the
90
+ * chapter the recall named (`recalledFrom`).
91
+ */
92
+ export async function deckFloat(locals: object, anchorId: string | undefined, base: string): Promise<DeckFloat> {
93
+ const deck = deckOfRoute(await loadManifest(), routeIdFromLocals(locals));
94
+ if (!deck) return { onDeck: false };
95
+ const chapter = anchorId === undefined ? undefined : (deck.recalledFrom?.[anchorId] ?? deck.chapter);
96
+ const recalled =
97
+ anchorId !== undefined &&
98
+ chapter !== undefined &&
99
+ deck.frames.some((frame) => frame.recalls?.includes(anchorId));
100
+ return {
101
+ onDeck: true,
102
+ ...(recalled ? { numberHref: `${base.replace(/\/$/, '')}/${chapter}/#${anchorId}` } : {}),
103
+ };
104
+ }