explico 0.4.0 → 0.6.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 (150) hide show
  1. package/CHANGELOG.md +382 -1
  2. package/README.md +103 -8
  3. package/dist/compiler/astro.d.ts +9 -1
  4. package/dist/compiler/astro.js +182 -3
  5. package/dist/compiler/cli.d.ts +13 -0
  6. package/dist/compiler/cli.js +28 -4
  7. package/dist/compiler/collections.d.ts +3 -2
  8. package/dist/compiler/collections.js +28 -1
  9. package/dist/compiler/course-config.d.ts +88 -1
  10. package/dist/compiler/course-config.js +35 -1
  11. package/dist/compiler/exports/standalone-html.d.ts +34 -0
  12. package/dist/compiler/exports/standalone-html.js +803 -0
  13. package/dist/compiler/manifest.d.ts +98 -6
  14. package/dist/compiler/manifest.js +154 -17
  15. package/dist/compiler/resolution-report.d.ts +4 -0
  16. package/dist/reference/base-abbreviations.d.mts +30 -0
  17. package/dist/reference/base-abbreviations.mjs +60 -0
  18. package/dist/reference/base-operators.d.mts +15 -0
  19. package/dist/reference/base-operators.mjs +40 -0
  20. package/dist/reference/base-roman-labels.d.mts +35 -0
  21. package/dist/reference/base-roman-labels.mjs +43 -0
  22. package/dist/reference/consistency.d.ts +8 -2
  23. package/dist/reference/consistency.js +50 -3
  24. package/dist/reference/diagrams-validate.d.ts +21 -0
  25. package/dist/reference/diagrams-validate.js +13 -0
  26. package/dist/reference/equations.d.mts +10 -15
  27. package/dist/reference/equations.mjs +67 -14
  28. package/dist/reference/gate-abbreviations.d.ts +17 -0
  29. package/dist/reference/gate-abbreviations.js +64 -0
  30. package/dist/reference/gate-heading-length.d.ts +12 -0
  31. package/dist/reference/gate-heading-length.js +79 -0
  32. package/dist/reference/gate-math.d.ts +32 -5
  33. package/dist/reference/gate-math.js +6 -2
  34. package/dist/reference/gate-prose-notation.d.ts +60 -0
  35. package/dist/reference/gate-prose-notation.js +125 -0
  36. package/dist/reference/keyed-float-validate.d.ts +5 -5
  37. package/dist/reference/keyed-jsx-floats.d.mts +7 -12
  38. package/dist/reference/keyed-jsx-floats.mjs +50 -25
  39. package/dist/reference/knowledge-checks-validate.d.ts +35 -0
  40. package/dist/reference/knowledge-checks-validate.js +78 -0
  41. package/dist/reference/knowledge-checks.d.mts +25 -0
  42. package/dist/reference/knowledge-checks.mjs +45 -0
  43. package/dist/reference/lab-math-scope.d.ts +9 -1
  44. package/dist/reference/lab-math-scope.js +32 -4
  45. package/dist/reference/math-glyphs.d.mts +42 -0
  46. package/dist/reference/math-glyphs.mjs +548 -40
  47. package/dist/reference/mdx-attributes.d.mts +13 -0
  48. package/dist/reference/mdx-attributes.mjs +38 -0
  49. package/dist/reference/mixed-content.d.ts +17 -0
  50. package/dist/reference/mixed-content.js +29 -0
  51. package/dist/reference/page-notation.d.ts +52 -0
  52. package/dist/reference/page-notation.js +171 -0
  53. package/dist/reference/raw-floats-validate.d.ts +18 -0
  54. package/dist/reference/raw-floats-validate.js +53 -0
  55. package/dist/reference/raw-floats.d.mts +18 -0
  56. package/dist/reference/raw-floats.mjs +129 -0
  57. package/dist/reference/rehype-heading-anchors.d.mts +1 -1
  58. package/dist/reference/rehype-heading-anchors.mjs +107 -1
  59. package/dist/reference/rehype-table-header-labels.d.mts +26 -0
  60. package/dist/reference/rehype-table-header-labels.mjs +152 -0
  61. package/dist/reference/remark-abbreviations.d.mts +7 -0
  62. package/dist/reference/remark-abbreviations.mjs +174 -0
  63. package/dist/reference/remark-citation.d.mts +5 -1
  64. package/dist/reference/remark-citation.mjs +0 -0
  65. package/dist/reference/remark-memo.d.mts +16 -0
  66. package/dist/reference/remark-memo.mjs +339 -0
  67. package/dist/reference/remark-notation.d.mts +57 -9
  68. package/dist/reference/remark-notation.mjs +354 -42
  69. package/dist/reference/remark-short-note.d.mts +4 -0
  70. package/dist/reference/remark-short-note.mjs +237 -0
  71. package/dist/reference/types.d.ts +16 -1
  72. package/dist/reference/units-vocab.js +4 -0
  73. package/docs/VERSIONING.md +53 -4
  74. package/docs/authoring/README.md +10 -0
  75. package/docs/authoring/agents.md +17 -2
  76. package/docs/authoring/components.md +138 -0
  77. package/docs/authoring/migrating.md +275 -7
  78. package/docs/authoring/notation.md +615 -1
  79. package/docs/authoring/structure-rules.md +69 -0
  80. package/docs/authoring/structure.md +120 -8
  81. package/docs/authoring/visualizations.md +36 -3
  82. package/package.json +5 -2
  83. package/src/compiler/astro.ts +208 -3
  84. package/src/compiler/cli.ts +29 -2
  85. package/src/compiler/collections.ts +45 -4
  86. package/src/compiler/course-config.ts +132 -0
  87. package/src/compiler/exports/standalone-html.ts +963 -0
  88. package/src/compiler/manifest.ts +281 -22
  89. package/src/components/HoverPanel.astro +45 -4
  90. package/src/components/abbreviation/AbbreviationGlossary.astro +70 -0
  91. package/src/components/abbreviation/AbbreviationLayer.astro +127 -0
  92. package/src/components/assessments/AssessmentRunner.tsx +52 -17
  93. package/src/components/assessments/AssessmentSet.astro +98 -14
  94. package/src/components/assessments/RenderedContent.tsx +55 -0
  95. package/src/components/assessments/assessment-randomization.ts +16 -0
  96. package/src/components/diagram/Diagram.astro +409 -103
  97. package/src/components/disclosure/hover-preview.ts +202 -0
  98. package/src/components/disclosure/persisted-disclosure.ts +66 -0
  99. package/src/components/examples/CompactExamples.astro +2 -0
  100. package/src/components/figure/Figure.astro +279 -10
  101. package/src/components/memo/Memo.astro +453 -0
  102. package/src/components/memo/ShortNote.astro +223 -0
  103. package/src/components/notation/NotationFormula.astro +18 -1
  104. package/src/components/notation/NotationLayer.astro +260 -83
  105. package/src/components/notation/render-notation-math.ts +39 -2
  106. package/src/components/notation/safe-math-to-react.ts +50 -0
  107. package/src/components/reference/FloatRefLayer.astro +233 -15
  108. package/src/components/starlight/CollapsibleSections.astro +461 -0
  109. package/src/components/starlight/DiagramEnhancer.astro +385 -0
  110. package/src/components/starlight/HeadingAnchorEnhancer.astro +5 -17
  111. package/src/components/starlight/KeyedFloatEnhancer.astro +81 -3
  112. package/src/components/starlight/LayoutHeader.astro +50 -0
  113. package/src/components/starlight/LayoutSocialIcons.astro +41 -2
  114. package/src/components/starlight/LessonExportActions.astro +436 -0
  115. package/src/components/starlight/LessonFooter.astro +30 -3
  116. package/src/components/starlight/section-parts.ts +31 -0
  117. package/src/components/table/Table.astro +293 -7
  118. package/src/components/table/TableOverflowExpander.astro +489 -0
  119. package/src/content-config.ts +36 -1
  120. package/src/reference/base-abbreviations.mjs +60 -0
  121. package/src/reference/base-operators.mjs +40 -0
  122. package/src/reference/base-roman-labels.mjs +43 -0
  123. package/src/reference/consistency.ts +73 -2
  124. package/src/reference/diagrams-validate.ts +40 -0
  125. package/src/reference/equations.mjs +67 -14
  126. package/src/reference/gate-abbreviations.ts +128 -0
  127. package/src/reference/gate-heading-length.ts +131 -0
  128. package/src/reference/gate-math.ts +32 -7
  129. package/src/reference/gate-prose-notation.ts +224 -0
  130. package/src/reference/keyed-float-validate.ts +5 -5
  131. package/src/reference/keyed-jsx-floats.mjs +50 -25
  132. package/src/reference/knowledge-checks-validate.ts +130 -0
  133. package/src/reference/knowledge-checks.mjs +45 -0
  134. package/src/reference/lab-math-scope.ts +45 -3
  135. package/src/reference/math-glyphs.mjs +548 -40
  136. package/src/reference/mdx-attributes.mjs +38 -0
  137. package/src/reference/mixed-content.ts +69 -0
  138. package/src/reference/page-notation.ts +252 -0
  139. package/src/reference/raw-floats-validate.ts +103 -0
  140. package/src/reference/raw-floats.mjs +129 -0
  141. package/src/reference/rehype-heading-anchors.mjs +107 -1
  142. package/src/reference/rehype-table-header-labels.mjs +152 -0
  143. package/src/reference/remark-abbreviations.mjs +174 -0
  144. package/src/reference/remark-citation.mjs +0 -0
  145. package/src/reference/remark-memo.mjs +339 -0
  146. package/src/reference/remark-notation.mjs +354 -42
  147. package/src/reference/remark-short-note.mjs +237 -0
  148. package/src/reference/types.ts +16 -0
  149. package/src/reference/units-vocab.ts +4 -0
  150. package/src/styles/global.css +996 -50
@@ -17,7 +17,7 @@
17
17
  export interface Props {
18
18
  /** Stable key, e.g. `id="kinematics-summary"`. Omit for a positional-only table. */
19
19
  id?: string;
20
- /** Visible caption, shown below the table and in the hover-preview card. */
20
+ /** Visible caption, shown above the table (the LaTeX convention — the reverse of a figure caption, which goes below) and in the hover-preview card. */
21
21
  caption: string;
22
22
  /** Computed by `remark-notation.mjs` — never author this directly. */
23
23
  number?: string;
@@ -43,9 +43,6 @@ if (!number || !anchorId) {
43
43
  data-tab-number={number}
44
44
  data-tab-key={id}
45
45
  >
46
- <div class="keyed-table__body">
47
- <slot />
48
- </div>
49
46
  <figcaption class="keyed-table__caption">
50
47
  <a
51
48
  class="keyed-table__number"
@@ -54,6 +51,9 @@ if (!number || !anchorId) {
54
51
  >
55
52
  <span class="keyed-table__caption-text">{caption}</span>
56
53
  </figcaption>
54
+ <div class="keyed-table__body">
55
+ <slot />
56
+ </div>
57
57
  </figure>
58
58
 
59
59
  <style>
@@ -67,13 +67,100 @@ if (!number || !anchorId) {
67
67
  }
68
68
 
69
69
  .keyed-table__body :global(table) {
70
- margin: 0;
70
+ /* Centered in the content column when narrower than it, same as a
71
+ centered figure — not pinned to the start edge just because that's
72
+ where block layout would otherwise leave it. Auto margins resolve to
73
+ 0 (left-aligned, scrollable) once the table is wider than its
74
+ container, so this is safe unconditionally. */
75
+ margin-block: 0;
76
+ margin-inline: auto;
71
77
  }
72
78
 
73
79
  .keyed-table__caption {
74
- margin-block-start: 0.5rem;
75
- font-size: var(--sl-text-sm);
80
+ position: relative;
81
+ margin-block-end: 0.5rem;
82
+ font-size: var(--sl-text-base);
83
+ color: var(--sl-color-gray-3);
84
+ }
85
+
86
+ /* Added by the row-pagination script below for every table (it always
87
+ gets a minimize-to-header-row toggle, regardless of row count) —
88
+ reserves room so caption text never runs under the absolutely-positioned
89
+ toggle button in the corner. */
90
+ .keyed-table__caption--paginated {
91
+ padding-inline-end: 1.75em;
92
+ }
93
+
94
+ /* The toggle button and everything else this rule block styles are
95
+ created at runtime (see the `<script>` below), so they never carry this
96
+ component's `data-astro-cid-*` scoping attribute — `:global()` is
97
+ required for the same reason `.keyed-table__body :global(table)` above
98
+ needs it. Reuses `.table-overflow-expand-btn` from
99
+ `TableOverflowExpander.astro` (Part 4) for its base look
100
+ (border/hover/focus/print-hidden), just repositioned into the caption's
101
+ top-right corner instead of a flex-row side column. */
102
+ :global(.keyed-table__pagination-toggle) {
103
+ position: absolute;
104
+ top: 0;
105
+ inset-inline-end: 0;
106
+ border: 1px solid var(--sl-color-gray-5);
107
+ border-radius: 0.3rem;
108
+ }
109
+
110
+ :global(.keyed-table__more-row td) {
111
+ padding-block: 0.4rem;
76
112
  color: var(--sl-color-gray-3);
113
+ font-size: var(--sl-text-sm);
114
+ font-style: italic;
115
+ text-align: center;
116
+ }
117
+
118
+ /* Only the live table's own more-row gets this class — a hover-card
119
+ preview clone (`FloatRefLayer.astro`) strips it so the same row reads
120
+ as an inert truncation marker there instead of a dead-looking link. */
121
+ :global(.keyed-table__more-row--interactive) {
122
+ cursor: pointer;
123
+ }
124
+
125
+ :global(.keyed-table__more-row--interactive:hover td),
126
+ :global(.keyed-table__more-row--interactive:focus-visible td) {
127
+ color: var(--sl-color-text-accent);
128
+ text-decoration: underline;
129
+ }
130
+
131
+ :global(.keyed-table__more-row--interactive:focus-visible) {
132
+ outline: 2px solid var(--sl-color-accent);
133
+ outline-offset: -0.15rem;
134
+ }
135
+
136
+ :global(.keyed-table__pagination-links) {
137
+ margin-block-start: 0.75rem;
138
+ text-align: center;
139
+ }
140
+
141
+ :global(.keyed-table__pagination-link) {
142
+ padding: 0;
143
+ border: 0;
144
+ background: none;
145
+ color: var(--sl-color-text-accent);
146
+ font: inherit;
147
+ font-size: var(--sl-text-sm);
148
+ text-decoration: underline;
149
+ cursor: pointer;
150
+ }
151
+
152
+ :global(.keyed-table__pagination-link:hover),
153
+ :global(.keyed-table__pagination-link:focus-visible) {
154
+ color: var(--sl-color-white);
155
+ }
156
+
157
+ :global(.keyed-table__pagination-link:focus-visible) {
158
+ outline: 2px solid var(--sl-color-accent);
159
+ outline-offset: 0.15rem;
160
+ }
161
+
162
+ :global(.keyed-table__pagination-link + .keyed-table__pagination-link) {
163
+ margin-inline-start: 1.25rem;
77
164
  }
78
165
 
79
166
  .keyed-table__number {
@@ -101,5 +188,204 @@ if (!number || !anchorId) {
101
188
  .keyed-table__number {
102
189
  color: inherit;
103
190
  }
191
+
192
+ :global(.keyed-table__pagination-links),
193
+ :global(.keyed-table__more-row) {
194
+ display: none !important;
195
+ }
196
+
197
+ /* A minimized/truncated table must still print in full regardless of
198
+ its on-screen state — same pattern every other collapsible in
199
+ docs/plans/collapsible-disclosure.md follows. */
200
+ .keyed-table__body :global(tr[hidden]) {
201
+ display: table-row !important;
202
+ }
104
203
  }
105
204
  </style>
205
+
206
+ <script>
207
+ /**
208
+ * Table row show/hide pagination
209
+ * (docs/plans/collapsible-disclosure.md Part 5) — three states per table,
210
+ * not two independent toggles: `minimized` (header row only) ->
211
+ * `truncated` (first/last few rows, the default once a table is long
212
+ * enough to bother) -> `expanded` (every row). Every table gets the
213
+ * minimize-to-header-row toggle in its caption, regardless of row count —
214
+ * a short table (`totalRows <= PAGINATE_ABOVE_ROWS`) just never grows the
215
+ * "… N more rows …" truncation row or the "Show less"/"Minimize" links
216
+ * below it, so it only ever has two reachable states, `minimized` and
217
+ * `expanded`. No persisted state here, same as `TableOverflowExpander.astro`'s
218
+ * expand/collapse (Part 4) — this is transient per-view UI, not a reader
219
+ * preference worth remembering across visits.
220
+ *
221
+ * Independent of `TableOverflowExpander.astro` otherwise: that script
222
+ * measures/reacts to horizontal overflow, this one only ever hides/shows
223
+ * `<tr>`s inside whatever `<table>` ends up in `.keyed-table__body` — it
224
+ * doesn't matter which of the two scripts wires up first. Hiding rows can
225
+ * change whether the table still overflows horizontally though (fewer
226
+ * visible rows can mean narrower content), so every state change
227
+ * dispatches a plain `resize` event afterward — `TableOverflowExpander`
228
+ * already listens for that to re-check its own expand button, which
229
+ * avoids these two files needing to import from each other.
230
+ */
231
+
232
+ type PaginationState = 'minimized' | 'truncated' | 'expanded';
233
+
234
+ const KEEP_EACH_END = 3;
235
+ const PAGINATE_ABOVE_ROWS = 6;
236
+
237
+ // Mirrors `TableOverflowExpander.astro`'s own `captionFor` exactly, for
238
+ // the same aria-label wording across both tables' controls.
239
+ function captionFor(figure: HTMLElement): string {
240
+ const text = figure
241
+ .querySelector('.keyed-table__caption-text')
242
+ ?.textContent?.trim();
243
+ const number = figure.dataset.tabNumber;
244
+ if (number && text) return `Table ${number} — ${text}`;
245
+ return text || 'this table';
246
+ }
247
+
248
+ function wireTable(figure: HTMLElement) {
249
+ figure.dataset.paginationWired = 'true';
250
+
251
+ const body = figure.querySelector<HTMLElement>(
252
+ ':scope > .keyed-table__body',
253
+ );
254
+ const table = body?.querySelector('table');
255
+ const tbody = table?.tBodies[0];
256
+ if (!body || !table || !tbody) return;
257
+
258
+ const rows = Array.from(tbody.rows);
259
+ const totalRows = rows.length;
260
+ if (totalRows === 0) return;
261
+
262
+ // Long enough to also need truncation, on top of the minimize toggle
263
+ // every table gets below.
264
+ const paginated = totalRows > PAGINATE_ABOVE_ROWS;
265
+
266
+ const caption = figure.querySelector<HTMLElement>(
267
+ ':scope > .keyed-table__caption',
268
+ );
269
+ caption?.classList.add('keyed-table__caption--paginated');
270
+ const toggle = document.createElement('button');
271
+ toggle.type = 'button';
272
+ toggle.className =
273
+ 'table-overflow-expand-btn keyed-table__pagination-toggle';
274
+ caption?.append(toggle);
275
+
276
+ let moreRow: HTMLTableRowElement | null = null;
277
+ let links: HTMLElement | null = null;
278
+ let showLess: HTMLButtonElement | null = null;
279
+ let minimizeAll: HTMLButtonElement | null = null;
280
+
281
+ if (paginated) {
282
+ const hiddenCount = totalRows - KEEP_EACH_END * 2;
283
+ const headerCells = table.tHead?.rows[0]?.cells ?? rows[0].cells;
284
+ const colCount =
285
+ Array.from(headerCells).reduce(
286
+ (sum, cell) => sum + (cell.colSpan || 1),
287
+ 0,
288
+ ) || 1;
289
+
290
+ // The "… N more rows …" separator is itself the control that reveals
291
+ // the rest of the table — the whole row is clickable (and, via
292
+ // `role="button"`/`tabIndex`, focusable/keyboard-activatable), not just
293
+ // its text, so there's no separate "Show all N rows" link to also make
294
+ // room for. `--interactive` carries the pointer cursor/hover-underline
295
+ // styling; `FloatRefLayer.astro`'s cloned preview strips both that class
296
+ // and the role/tabindex so the same markup reads as an inert truncation
297
+ // marker there instead.
298
+ moreRow = document.createElement('tr');
299
+ moreRow.className = 'keyed-table__more-row keyed-table__more-row--interactive';
300
+ moreRow.tabIndex = 0;
301
+ moreRow.setAttribute('role', 'button');
302
+ moreRow.setAttribute('aria-label', `Show all ${totalRows} rows`);
303
+ const moreCell = document.createElement('td');
304
+ moreCell.colSpan = colCount;
305
+ moreCell.textContent =
306
+ hiddenCount === 1 ? '… 1 more row …' : `… ${hiddenCount} more rows …`;
307
+ moreRow.append(moreCell);
308
+ tbody.insertBefore(moreRow, rows[KEEP_EACH_END]);
309
+
310
+ // Centered below the table body, and only ever shown once every row is
311
+ // — "Show less" un-truncates back down, "Minimize to header row"
312
+ // duplicates the caption toggle's own minimize action for a reader who
313
+ // has scrolled well past that corner button by the time all the rows
314
+ // are on screen.
315
+ links = document.createElement('p');
316
+ links.className = 'keyed-table__pagination-links';
317
+ showLess = document.createElement('button');
318
+ showLess.type = 'button';
319
+ showLess.className = 'keyed-table__pagination-link';
320
+ showLess.textContent = 'Show less';
321
+ minimizeAll = document.createElement('button');
322
+ minimizeAll.type = 'button';
323
+ minimizeAll.className = 'keyed-table__pagination-link';
324
+ minimizeAll.textContent = 'Minimize to header row';
325
+ links.append(showLess, minimizeAll);
326
+ body.after(links);
327
+ }
328
+
329
+ // A short table only ever reaches `minimized`/`expanded` — `truncated`
330
+ // is unreachable for it since there's nothing to truncate.
331
+ let state: PaginationState = paginated ? 'truncated' : 'expanded';
332
+
333
+ function render() {
334
+ const minimized = state === 'minimized';
335
+ const expanded = state === 'expanded';
336
+ rows.forEach((row, index) => {
337
+ const inMiddle =
338
+ paginated &&
339
+ index >= KEEP_EACH_END &&
340
+ index < totalRows - KEEP_EACH_END;
341
+ row.hidden = minimized || (inMiddle && !expanded);
342
+ });
343
+ if (moreRow) moreRow.hidden = minimized || expanded;
344
+
345
+ toggle.textContent = minimized ? '▾' : '▴';
346
+ toggle.setAttribute('aria-expanded', String(!minimized));
347
+ toggle.setAttribute(
348
+ 'aria-label',
349
+ minimized
350
+ ? `Show rows of ${captionFor(figure)}`
351
+ : `Minimize ${captionFor(figure)} to its header row`,
352
+ );
353
+ toggle.title = minimized ? 'Show rows' : 'Minimize to header row';
354
+
355
+ // Only meaningful once every row is already shown — while truncated,
356
+ // the more-row link above is itself the only control needed.
357
+ if (links) links.hidden = !expanded;
358
+
359
+ // Hiding/showing rows can change whether the table still overflows
360
+ // horizontally — let `TableOverflowExpander.astro`'s own debounced
361
+ // resize handler re-check its expand button.
362
+ window.dispatchEvent(new Event('resize'));
363
+ }
364
+
365
+ function setState(next: PaginationState) {
366
+ if (state === next) return;
367
+ state = next;
368
+ render();
369
+ }
370
+
371
+ toggle.addEventListener('click', () => {
372
+ setState(
373
+ state === 'minimized' ? (paginated ? 'truncated' : 'expanded') : 'minimized',
374
+ );
375
+ });
376
+ moreRow?.addEventListener('click', () => setState('expanded'));
377
+ moreRow?.addEventListener('keydown', (event) => {
378
+ if (event.key !== 'Enter' && event.key !== ' ') return;
379
+ event.preventDefault();
380
+ setState('expanded');
381
+ });
382
+ showLess?.addEventListener('click', () => setState('truncated'));
383
+ minimizeAll?.addEventListener('click', () => setState('minimized'));
384
+
385
+ render();
386
+ }
387
+
388
+ document
389
+ .querySelectorAll<HTMLElement>('.keyed-table:not([data-pagination-wired])')
390
+ .forEach(wireTable);
391
+ </script>