explico 0.7.0 → 0.11.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 (236) hide show
  1. package/CHANGELOG.md +1409 -1
  2. package/README.md +60 -15
  3. package/dist/compiler/astro.d.ts +6 -1
  4. package/dist/compiler/cli.d.ts +54 -3
  5. package/dist/compiler/cli.js +80 -6
  6. package/dist/compiler/collections.d.ts +35 -2
  7. package/dist/compiler/collections.js +84 -8
  8. package/dist/compiler/course-config.d.ts +3 -194
  9. package/dist/compiler/course-config.js +78 -5
  10. package/dist/compiler/exports/standalone-html.d.ts +6 -1
  11. package/dist/compiler/knowledge-version.js +1 -1
  12. package/dist/compiler/lesson-derivation.d.ts +27 -10
  13. package/dist/compiler/lesson-derivation.js +23 -8
  14. package/dist/compiler/logging.d.ts +14 -1
  15. package/dist/compiler/logging.js +6 -2
  16. package/dist/compiler/manifest.d.ts +204 -31
  17. package/dist/compiler/manifest.js +270 -55
  18. package/dist/compiler/resolution-report.d.ts +6 -1
  19. package/dist/course-config-Dsnri9Tk.d.ts +464 -0
  20. package/dist/reference/base-accents.d.mts +26 -0
  21. package/dist/reference/base-accents.mjs +31 -0
  22. package/dist/reference/base-roman-labels.d.mts +2 -2
  23. package/dist/reference/base-roman-labels.mjs +2 -2
  24. package/dist/reference/consistency.d.ts +14 -2
  25. package/dist/reference/consistency.js +108 -4
  26. package/dist/reference/delimiter-size.d.ts +21 -11
  27. package/dist/reference/delimiter-size.js +15 -39
  28. package/dist/reference/diagrams-validate.d.ts +7 -2
  29. package/dist/reference/diagrams-validate.js +9 -1
  30. package/dist/reference/equations-validate.d.ts +8 -5
  31. package/dist/reference/equations-validate.js +1 -0
  32. package/dist/reference/equations.d.mts +69 -34
  33. package/dist/reference/equations.mjs +90 -44
  34. package/dist/reference/examples-validate.d.ts +31 -0
  35. package/dist/reference/examples-validate.js +48 -0
  36. package/dist/reference/figures-validate.d.ts +1 -1
  37. package/dist/reference/figures-validate.js +4 -1
  38. package/dist/reference/folds-validate.d.ts +33 -0
  39. package/dist/reference/folds-validate.js +32 -0
  40. package/dist/reference/gate-abbreviations.d.ts +3 -1
  41. package/dist/reference/gate-abbreviations.js +3 -2
  42. package/dist/reference/gate-course-rules.d.ts +32 -0
  43. package/dist/reference/gate-course-rules.js +85 -0
  44. package/dist/reference/gate-heading-length.d.ts +5 -2
  45. package/dist/reference/gate-heading-length.js +47 -23
  46. package/dist/reference/gate-heading-structure.d.ts +41 -0
  47. package/dist/reference/gate-heading-structure.js +113 -0
  48. package/dist/reference/gate-math-punctuation.d.ts +71 -0
  49. package/dist/reference/gate-math-punctuation.js +251 -0
  50. package/dist/reference/gate-math.d.ts +44 -6
  51. package/dist/reference/gate-math.js +63 -10
  52. package/dist/reference/gate-part-pages.d.ts +50 -0
  53. package/dist/reference/gate-part-pages.js +118 -0
  54. package/dist/reference/gate-prose-notation.d.ts +32 -11
  55. package/dist/reference/gate-prose-notation.js +30 -8
  56. package/dist/reference/katex-delimiters.d.mts +15 -0
  57. package/dist/reference/katex-delimiters.mjs +138 -0
  58. package/dist/reference/katex-options.mjs +4 -0
  59. package/dist/reference/keyed-float-validate.d.ts +27 -10
  60. package/dist/reference/keyed-float-validate.js +15 -1
  61. package/dist/reference/keyed-jsx-floats.d.mts +11 -5
  62. package/dist/reference/keyed-jsx-floats.mjs +73 -20
  63. package/dist/reference/knowledge-checks-validate.d.ts +1 -1
  64. package/dist/reference/knowledge-checks.d.mts +3 -1
  65. package/dist/reference/knowledge-checks.mjs +9 -4
  66. package/dist/reference/lab-math-scope.d.ts +1 -0
  67. package/dist/reference/lab-math-slot.d.ts +34 -0
  68. package/dist/reference/lab-math-slot.js +6 -0
  69. package/dist/reference/math-glyphs.d.mts +80 -4
  70. package/dist/reference/math-glyphs.mjs +588 -39
  71. package/dist/reference/page-notation.d.ts +6 -1
  72. package/dist/reference/raw-floats-validate.js +1 -1
  73. package/dist/reference/raw-floats.mjs +1 -1
  74. package/dist/reference/rehype-heading-anchors.d.mts +14 -0
  75. package/dist/reference/rehype-heading-anchors.mjs +116 -17
  76. package/dist/reference/rehype-table-header-labels.d.mts +6 -1
  77. package/dist/reference/rehype-table-header-labels.mjs +21 -3
  78. package/dist/reference/remark-asides.d.mts +3 -0
  79. package/dist/reference/remark-asides.mjs +180 -0
  80. package/dist/reference/remark-memo.d.mts +3 -1
  81. package/dist/reference/remark-memo.mjs +42 -11
  82. package/dist/reference/remark-notation.d.mts +60 -10
  83. package/dist/reference/remark-notation.mjs +387 -36
  84. package/dist/reference/render-lab-math.d.ts +4 -26
  85. package/dist/reference/render-lab-math.js +5 -5
  86. package/dist/reference/sections-validate.d.ts +27 -0
  87. package/dist/reference/sections-validate.js +41 -0
  88. package/dist/reference/sections.d.mts +97 -0
  89. package/dist/reference/sections.mjs +306 -0
  90. package/dist/reference/tables-validate.d.ts +1 -1
  91. package/dist/reference/types.d.ts +24 -1
  92. package/dist/reference/unit-words.d.ts +37 -0
  93. package/dist/reference/unit-words.js +170 -0
  94. package/dist/rules/catalog.d.ts +176 -0
  95. package/dist/rules/catalog.js +983 -0
  96. package/dist/rules/course-rules.d.ts +115 -0
  97. package/dist/rules/course-rules.js +292 -0
  98. package/dist/rules/course-rules.schema.json +138 -0
  99. package/dist/rules/in-effect.d.ts +53 -0
  100. package/dist/rules/in-effect.js +142 -0
  101. package/dist/rules/rules.schema.json +1072 -0
  102. package/dist/rules/settings.d.ts +3 -0
  103. package/dist/rules/settings.js +290 -0
  104. package/dist/rules/waivers.d.ts +53 -0
  105. package/dist/rules/waivers.js +132 -0
  106. package/dist/session/PreferenceStore.d.ts +5 -0
  107. package/dist/session/PreferenceStore.js +9 -0
  108. package/docs/VERSIONING.md +2 -2
  109. package/docs/authoring/README.md +232 -40
  110. package/docs/authoring/agents.md +55 -9
  111. package/docs/authoring/components.md +178 -22
  112. package/docs/authoring/migrating.md +1324 -2
  113. package/docs/authoring/notation.md +616 -344
  114. package/docs/authoring/prose.md +551 -0
  115. package/docs/authoring/retired.md +35 -0
  116. package/docs/authoring/structure-rules.md +422 -40
  117. package/docs/authoring/structure.md +445 -62
  118. package/docs/authoring/versioning.md +20 -28
  119. package/docs/authoring/visualizations.md +142 -20
  120. package/docs/katex-bracket-scaling.md +191 -0
  121. package/package.json +3 -2
  122. package/src/compiler/cli.ts +139 -6
  123. package/src/compiler/collections.ts +144 -10
  124. package/src/compiler/course-config.ts +273 -34
  125. package/src/compiler/knowledge-version.ts +1 -1
  126. package/src/compiler/lesson-derivation.ts +54 -15
  127. package/src/compiler/logging.ts +21 -4
  128. package/src/compiler/manifest.ts +573 -104
  129. package/src/components/BlockHeading.astro +113 -0
  130. package/src/components/CourseContents.astro +221 -0
  131. package/src/components/CurriculumMap.astro +5 -6
  132. package/src/components/FloatIndex.astro +200 -0
  133. package/src/components/HoverPanel.astro +431 -79
  134. package/src/components/PartBridge.astro +27 -0
  135. package/src/components/PartOverview.astro +401 -0
  136. package/src/components/PartScope.astro +27 -0
  137. package/src/components/abbreviation/AbbreviationLayer.astro +49 -51
  138. package/src/components/assessments/AssessmentRunner.tsx +8 -5
  139. package/src/components/assessments/AssessmentSet.astro +23 -26
  140. package/src/components/block-heading.ts +146 -0
  141. package/src/components/card-arrow.ts +243 -0
  142. package/src/components/card-drag.ts +631 -0
  143. package/src/components/card-jump.ts +45 -0
  144. package/src/components/card-manager.ts +1589 -0
  145. package/src/components/card-memory.ts +177 -0
  146. package/src/components/card-placement.ts +504 -0
  147. package/src/components/card-side.ts +336 -0
  148. package/src/components/card-travel.ts +197 -0
  149. package/src/components/citation/CitationLayer.astro +77 -76
  150. package/src/components/diagram/Diagram.astro +161 -316
  151. package/src/components/disclosure/course-disclosure.ts +24 -0
  152. package/src/components/disclosure/hover-preview.ts +122 -42
  153. package/src/components/disclosure/persisted-disclosure.ts +3 -3
  154. package/src/components/disclosure/print-as-shown.ts +252 -0
  155. package/src/components/examples/CompactExample.astro +3 -1
  156. package/src/components/examples/CompactExamples.astro +101 -94
  157. package/src/components/figure/Figure.astro +174 -274
  158. package/src/components/figure/FloatCaption.astro +87 -0
  159. package/src/components/figure/figure-state.ts +58 -0
  160. package/src/components/figure/figure-tools.ts +87 -0
  161. package/src/components/float-name.ts +101 -0
  162. package/src/components/fold/Fold.astro +125 -0
  163. package/src/components/fold/fold-id.ts +40 -0
  164. package/src/components/memo/Memo.astro +162 -231
  165. package/src/components/memo/ShortNote.astro +6 -6
  166. package/src/components/notation/NotationFormula.astro +5 -1
  167. package/src/components/notation/NotationGlossary.astro +24 -14
  168. package/src/components/notation/NotationLayer.astro +169 -162
  169. package/src/components/notation/NotationMath.astro +10 -1
  170. package/src/components/notation/render-notation-math.ts +111 -10
  171. package/src/components/reference/FloatRefLayer.astro +485 -423
  172. package/src/components/starlight/AboutPanel.astro +22 -6
  173. package/src/components/starlight/CollapsibleSections.astro +207 -93
  174. package/src/components/starlight/DiagramEnhancer.astro +15 -138
  175. package/src/components/starlight/HeadingAnchorEnhancer.astro +193 -1
  176. package/src/components/starlight/KeyedFloatEnhancer.astro +100 -9
  177. package/src/components/starlight/LayoutHeader.astro +298 -19
  178. package/src/components/starlight/LayoutPageSidebar.astro +99 -12
  179. package/src/components/starlight/LayoutPageTitle.astro +120 -0
  180. package/src/components/starlight/LayoutSidebar.astro +229 -11
  181. package/src/components/starlight/LessonExportActions.astro +53 -72
  182. package/src/components/starlight/LessonFooter.astro +26 -4
  183. package/src/components/starlight/section-parts.ts +15 -2
  184. package/src/components/starlight/section-summary.ts +181 -0
  185. package/src/components/table/Table.astro +95 -76
  186. package/src/components/table/TableOverflowExpander.astro +65 -28
  187. package/src/content-config.ts +40 -4
  188. package/src/reference/base-accents.mjs +31 -0
  189. package/src/reference/base-roman-labels.mjs +2 -2
  190. package/src/reference/consistency.ts +176 -5
  191. package/src/reference/delimiter-size.ts +40 -61
  192. package/src/reference/diagrams-validate.ts +17 -1
  193. package/src/reference/equations-validate.ts +10 -5
  194. package/src/reference/equations.mjs +90 -44
  195. package/src/reference/examples-validate.ts +97 -0
  196. package/src/reference/figures-validate.ts +6 -1
  197. package/src/reference/folds-validate.ts +65 -0
  198. package/src/reference/gate-abbreviations.ts +3 -2
  199. package/src/reference/gate-course-rules.ts +134 -0
  200. package/src/reference/gate-heading-length.ts +68 -37
  201. package/src/reference/gate-heading-structure.ts +193 -0
  202. package/src/reference/gate-math-punctuation.ts +381 -0
  203. package/src/reference/gate-math.ts +119 -12
  204. package/src/reference/gate-part-pages.ts +217 -0
  205. package/src/reference/gate-prose-notation.ts +74 -13
  206. package/src/reference/katex-delimiters.mjs +138 -0
  207. package/src/reference/katex-options.mjs +4 -0
  208. package/src/reference/keyed-float-validate.ts +45 -11
  209. package/src/reference/keyed-jsx-floats.mjs +73 -20
  210. package/src/reference/knowledge-checks-validate.ts +1 -1
  211. package/src/reference/knowledge-checks.mjs +9 -4
  212. package/src/reference/lab-math-slot.ts +36 -0
  213. package/src/reference/math-glyphs.mjs +588 -39
  214. package/src/reference/raw-floats-validate.ts +1 -1
  215. package/src/reference/raw-floats.mjs +1 -1
  216. package/src/reference/rehype-heading-anchors.mjs +116 -17
  217. package/src/reference/rehype-table-header-labels.mjs +21 -3
  218. package/src/reference/remark-asides.mjs +180 -0
  219. package/src/reference/remark-memo.mjs +42 -11
  220. package/src/reference/remark-notation.mjs +387 -36
  221. package/src/reference/render-lab-math.ts +17 -32
  222. package/src/reference/sections-validate.ts +81 -0
  223. package/src/reference/sections.mjs +306 -0
  224. package/src/reference/tables-validate.ts +2 -1
  225. package/src/reference/types.ts +24 -0
  226. package/src/reference/unit-words.ts +233 -0
  227. package/src/rules/catalog.ts +1298 -0
  228. package/src/rules/course-rules.ts +456 -0
  229. package/src/rules/in-effect.ts +235 -0
  230. package/src/rules/settings.ts +448 -0
  231. package/src/rules/waivers.ts +249 -0
  232. package/src/session/PreferenceStore.ts +15 -0
  233. package/src/styles/global.css +1471 -163
  234. package/src/styles/surfaces.css +182 -0
  235. package/src/components/hover-panel.ts +0 -339
  236. package/src/components/popup-stack.ts +0 -345
@@ -11,8 +11,9 @@
11
11
  * `#eq-key` — `keyedFloatAnchorId` prepends `tab-`/`fig-`/`dia-`.
12
12
  *
13
13
  * Only top-level occurrences count, exactly like `collectDisplayEquations`,
14
- * except that `<Memo>` is transparent: a float in a memo remains a side-note
15
- * in the lesson's reading order. A keyed float nested inside a
14
+ * except that the `TRANSPARENT_COMPONENTS` (`<Memo>`, `<ShortNote>`,
15
+ * `<Fold>`) are see-through: a float in a memo or a fold stays in the
16
+ * lesson's reading order. A keyed float nested inside a
16
17
  * `<CompactExample>` (or any other component) is illustrative, not part of the
17
18
  * official sequence.
18
19
  *
@@ -26,6 +27,7 @@ import {
26
27
  resolveNumbering,
27
28
  stripFencedCode,
28
29
  stripJsxComponents,
30
+ TRANSPARENT_COMPONENTS,
29
31
  } from './equations.mjs';
30
32
 
31
33
  /** Same shape as `EQ_KEY_PATTERN` — the notation-key grammar without dots. */
@@ -37,10 +39,11 @@ function floatNumber(section, index) {
37
39
  }
38
40
 
39
41
  /**
40
- * The visible number for one float. With a `prefix` (the part's ordinal) the
41
- * scheme is `‹part›.‹offset + indexInLesson›` and `##` headings are ignored;
42
- * without it, the legacy `##`-section scheme. Mirrors `displayEquationNumber`
43
- * in `equations.mjs`. Each kind of float counts on its own — a `<Table>` never
42
+ * The visible number for one float. With a `prefix` (`‹part›.‹chapter›`, or
43
+ * the part alone under `numbering.floats: "part"`) the scheme is
44
+ * `‹prefix›.‹offset + indexInLesson›` and `##` headings are ignored; without
45
+ * it, the legacy `##`-section scheme. Mirrors `displayEquationNumber` in
46
+ * `equations.mjs`. Each kind of float counts on its own — a `<Table>` never
44
47
  * advances the `<Figure>` or `<Diagram>` sequence — so `offset` is per kind too.
45
48
  */
46
49
  function keyedFloatNumber({
@@ -79,22 +82,60 @@ function readIdAttribute(attributesSource) {
79
82
  return match ? match[2] : undefined;
80
83
  }
81
84
 
85
+ /**
86
+ * A literal string attribute (`name="…"` / `name='…'`) of a JSX opening tag's
87
+ * attribute source, trimmed; `null` when absent, empty, or an expression.
88
+ */
89
+ function readStringAttribute(attributesSource, name) {
90
+ const match = new RegExp(
91
+ String.raw`(?:^|\s)${name}\s*=\s*(?:"([^"]*)"|'([^']*)')`,
92
+ ).exec(attributesSource);
93
+ const value = (match?.[1] ?? match?.[2])?.trim();
94
+ return value ? value : null;
95
+ }
96
+
97
+ /** Every prop name a JSX opening tag's attribute source sets (`name=`). */
98
+ function attributeNames(attributesSource) {
99
+ const bare = attributesSource.replace(
100
+ /"[^"]*"|'[^']*'|\{(?:[^{}]|\{[^{}]*\})*\}/g,
101
+ ' ',
102
+ );
103
+ return [...bare.matchAll(/(?:^|\s)([A-Za-z_][\w:.-]*)\s*=/g)].map(
104
+ (match) => match[1],
105
+ );
106
+ }
107
+
108
+ /** A literal string value of an mdast JSX attribute, trimmed, else `null`. */
109
+ function mdxStringAttribute(node, name) {
110
+ const attribute = (node.attributes ?? []).find(
111
+ (candidate) =>
112
+ candidate?.type === 'mdxJsxAttribute' && candidate.name === name,
113
+ );
114
+ const value =
115
+ typeof attribute?.value === 'string' ? attribute.value.trim() : '';
116
+ return value ? value : null;
117
+ }
118
+
82
119
  /**
83
120
  * The raw-text equivalent of `collectKeyedJsxFloats`, mirroring
84
- * `scanEquationLabels`: every top-level `<TagName …>` in appearance order.
85
- * With `numbering` (`{ prefix, offset }` — the part's ordinal and the count of
86
- * this kind of float in earlier lessons of that part) the number is
87
- * `‹part›.‹offset + n›`, `n` sequential over the whole lesson; without it, `##`
121
+ * `scanEquationLabels`: every top-level `<TagName …>` in appearance order,
122
+ * with its literal `short` / `caption` (the caption pair every float takes;
123
+ * `null` when absent or an expression) and `props`, the names of every prop
124
+ * the tag sets, for `keyed-float-validate.ts`'s removed-prop check.
125
+ * With `numbering` (`{ prefix, offset }` — the lesson's float-number prefix
126
+ * and the count of this kind of float before it in that scope) the number is
127
+ * `‹prefix›.‹offset + n›`, `n` sequential over the whole lesson; without it, `##`
88
128
  * headings advance a section counter. Other JSX components' bodies are blanked
89
129
  * first so a `<Table>` nested inside e.g. `<CompactExample>` (and any `##`-like
90
130
  * text inside an unrelated component) is never counted.
91
131
  */
92
132
  export function scanKeyedJsxFloats(markdown, { tagName, numbering }) {
93
133
  const { prefix, offset } = resolveNumbering(numbering);
94
- const text = stripJsxComponents(stripFencedCode(markdown), tagName, [
95
- 'Memo',
96
- 'ShortNote',
97
- ]);
134
+ const text = stripJsxComponents(
135
+ stripFencedCode(markdown),
136
+ tagName,
137
+ TRANSPARENT_COMPONENTS,
138
+ );
98
139
  const tokens = [];
99
140
  const heading = /^[ \t]{0,3}##[ \t]+\S/gm;
100
141
  const opening = new RegExp(`<${tagName}\\b([^>]*)>`, 'g');
@@ -102,8 +143,16 @@ export function scanKeyedJsxFloats(markdown, { tagName, numbering }) {
102
143
  tokens.push({ kind: 'section', index: match.index });
103
144
  }
104
145
  for (const match of text.matchAll(opening)) {
105
- const key = readIdAttribute(match[1] ?? '');
106
- tokens.push({ kind: 'float', index: match.index, key: key ?? null });
146
+ const attributes = match[1] ?? '';
147
+ const key = readIdAttribute(attributes);
148
+ tokens.push({
149
+ kind: 'float',
150
+ index: match.index,
151
+ key: key ?? null,
152
+ short: readStringAttribute(attributes, 'short'),
153
+ caption: readStringAttribute(attributes, 'caption'),
154
+ props: attributeNames(attributes),
155
+ });
107
156
  }
108
157
  tokens.sort((a, b) => a.index - b.index);
109
158
 
@@ -128,6 +177,9 @@ export function scanKeyedJsxFloats(markdown, { tagName, numbering }) {
128
177
  indexInSection,
129
178
  indexInLesson,
130
179
  }),
180
+ short: token.short,
181
+ caption: token.caption,
182
+ props: token.props,
131
183
  section,
132
184
  indexInSection,
133
185
  });
@@ -145,7 +197,7 @@ export function countKeyedJsxFloats(markdown, { tagName }) {
145
197
 
146
198
  /**
147
199
  * The mdast equivalent of `scanKeyedJsxFloats`: walk the tree's top-level
148
- * children (and through `<Memo>` bodies) in document order, advancing the
200
+ * children (and through the `TRANSPARENT_COMPONENTS`' bodies) in document order, advancing the
149
201
  * section counter on every top-level depth-2 heading and emitting a record for
150
202
  * every `mdxJsxFlowElement` named `tagName` — keyed or not, exactly like
151
203
  * `collectDisplayEquations`. `numbering` is threaded the same way as in
@@ -171,6 +223,8 @@ export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
171
223
  typeof idAttribute?.value === 'string' ? idAttribute.value : undefined;
172
224
  floats.push({
173
225
  key: key ?? null,
226
+ short: mdxStringAttribute(node, 'short'),
227
+ caption: mdxStringAttribute(node, 'caption'),
174
228
  number: keyedFloatNumber({
175
229
  prefix,
176
230
  offset,
@@ -186,8 +240,7 @@ export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
186
240
  }
187
241
  if (
188
242
  node.type === 'mdxJsxFlowElement' &&
189
- node.name !== 'Memo' &&
190
- node.name !== 'ShortNote'
243
+ !TRANSPARENT_COMPONENTS.includes(node.name)
191
244
  ) {
192
245
  return;
193
246
  }
@@ -207,7 +260,7 @@ export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
207
260
  collectFromDetail(node);
208
261
  } else if (
209
262
  node.type === 'mdxJsxFlowElement' &&
210
- (node.name === 'Memo' || node.name === 'ShortNote')
263
+ TRANSPARENT_COMPONENTS.includes(node.name)
211
264
  ) {
212
265
  collectFromDetail(node);
213
266
  }
@@ -12,7 +12,7 @@ export interface KnowledgeCheckLessonInput {
12
12
  readonly file: string;
13
13
  readonly body: string;
14
14
  readonly assessmentIds: readonly string[];
15
- readonly numbering?: { readonly prefix: number; readonly offset: number };
15
+ readonly numbering?: { readonly prefix: number | string; readonly offset: number };
16
16
  }
17
17
 
18
18
  export interface KnowledgeCheckLabelRecord {
@@ -28,12 +28,17 @@ export function knowledgeCheckAnchorId(key) {
28
28
 
29
29
  /**
30
30
  * Number an ordered assessment-id list. With manifest placement, the number
31
- * is `part.offset + index`; without it, the list remains usefully page-local.
31
+ * is `‹prefix›.‹offset + index›` — `prefix` is `‹part›.‹chapter›`, or the part
32
+ * alone under `numbering.floats: "part"`; without it, the list remains
33
+ * usefully page-local.
32
34
  */
33
35
  export function collectKnowledgeChecks(assessmentIds, numbering) {
34
- const prefix = Number.isInteger(numbering?.prefix) && numbering.prefix > 0
35
- ? numbering.prefix
36
- : undefined;
36
+ const rawPrefix = numbering?.prefix;
37
+ const prefix =
38
+ (Number.isInteger(rawPrefix) && rawPrefix > 0) ||
39
+ (typeof rawPrefix === 'string' && /^\d+(?:\.\d+)*$/.test(rawPrefix))
40
+ ? rawPrefix
41
+ : undefined;
37
42
  const offset = Number.isInteger(numbering?.offset) && numbering.offset >= 0
38
43
  ? numbering.offset
39
44
  : 0;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The half of lab math an interactive island needs in the browser: the shape
3
+ * of a compiled template and its slot placeholder. It imports nothing, so an
4
+ * island that uses it stays clear of the KaTeX build chain behind
5
+ * `render-lab-math.ts`, which is server-only (`katex-delimiters.mjs` reads
6
+ * KaTeX's bundle from disk).
7
+ */
8
+
9
+ export interface RenderedLabMath {
10
+ /** KaTeX HTML+MathML with `data-notation-key` and `data-lab-slot` spans. */
11
+ readonly html: string;
12
+ /** Slot names, in template order, the island must supply at runtime. */
13
+ readonly slots: readonly string[];
14
+ /** Notation keys the template resolved to, for diagnostics. */
15
+ readonly keys: readonly string[];
16
+ /**
17
+ * Plain, copyable LaTeX — every explained glyph resolved to its bare
18
+ * symbol, every `\slot{name}{…}` replaced by `slotPlaceholder(name)` — so
19
+ * the island can substitute each slot's *current* live value in and keep
20
+ * `[data-notation-formula]`'s `data-latex` accurate as the reader drags a
21
+ * control, the same "click the equation to copy it" gesture a static
22
+ * lesson or glossary equation gets (`NotationLayer.astro`).
23
+ */
24
+ readonly plainLatexTemplate: string;
25
+ }
26
+
27
+ /**
28
+ * A slot's placeholder inside `plainLatexTemplate` — distinct from the
29
+ * numeric sentinel `render-lab-math.ts` uses internally (that one only has to
30
+ * survive a single notation-binding pass at build time; this one is the
31
+ * stable, name-keyed token an island substitutes with a live value at
32
+ * runtime, so it's exported for that purpose).
33
+ */
34
+ export function slotPlaceholder(name: string): string {
35
+ return `⟦${name}⟧`;
36
+ }