explico 0.11.1 → 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 (298) hide show
  1. package/CHANGELOG.md +1292 -1
  2. package/README.md +91 -13
  3. package/dist/compiler/astro.d.ts +11 -4
  4. package/dist/compiler/astro.js +48 -15
  5. package/dist/compiler/cli.d.ts +27 -12
  6. package/dist/compiler/cli.js +96 -25
  7. package/dist/compiler/collections.d.ts +33 -2
  8. package/dist/compiler/collections.js +91 -19
  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 +15 -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 +115 -8
  51. package/dist/compiler/manifest.js +221 -39
  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 +3 -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 +8 -2
  61. package/dist/reference/base-operators.mjs +8 -10
  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/figure-symbols.d.ts +32 -0
  88. package/dist/reference/figure-symbols.js +55 -0
  89. package/dist/reference/figures-validate.d.ts +1 -1
  90. package/dist/reference/gate-abbreviations.d.ts +1 -1
  91. package/dist/reference/gate-abbreviations.js +1 -1
  92. package/dist/reference/gate-course-rules.d.ts +4 -3
  93. package/dist/reference/gate-course-rules.js +9 -4
  94. package/dist/reference/gate-heading-length.d.ts +2 -0
  95. package/dist/reference/gate-heading-length.js +7 -5
  96. package/dist/reference/gate-heading-structure.js +26 -0
  97. package/dist/reference/gate-indices.d.ts +35 -0
  98. package/dist/reference/gate-indices.js +299 -0
  99. package/dist/reference/gate-math.d.ts +32 -2
  100. package/dist/reference/gate-math.js +68 -12
  101. package/dist/reference/gate-mdx-expression.d.ts +17 -0
  102. package/dist/reference/gate-mdx-expression.js +29 -0
  103. package/dist/reference/gate-non-chapter-pages.d.ts +39 -0
  104. package/dist/reference/gate-non-chapter-pages.js +156 -0
  105. package/dist/reference/gate-part-pages.d.ts +4 -2
  106. package/dist/reference/gate-part-pages.js +2 -1
  107. package/dist/reference/gate-prop-text.d.ts +28 -0
  108. package/dist/reference/gate-prop-text.js +106 -0
  109. package/dist/reference/gate-prose-notation.d.ts +3 -1
  110. package/dist/reference/gate-prose-notation.js +4 -2
  111. package/dist/reference/gate-retired-keys.d.ts +35 -0
  112. package/dist/reference/gate-retired-keys.js +58 -0
  113. package/dist/reference/gloss.d.ts +2 -0
  114. package/dist/reference/katex-measure.d.ts +17 -0
  115. package/dist/reference/katex-measure.js +223 -0
  116. package/dist/reference/keyed-float-validate.js +12 -0
  117. package/dist/reference/keyed-jsx-floats.d.mts +2 -2
  118. package/dist/reference/keyed-jsx-floats.mjs +11 -14
  119. package/dist/reference/lab-katex-validate.d.ts +43 -0
  120. package/dist/reference/lab-katex-validate.js +96 -0
  121. package/dist/reference/lab-math-scope.d.ts +1 -0
  122. package/dist/reference/lab-math-scope.js +13 -2
  123. package/dist/reference/lint-ignore.js +1 -1
  124. package/dist/reference/math-glyphs.d.mts +94 -2
  125. package/dist/reference/math-glyphs.mjs +851 -46
  126. package/dist/reference/mixed-content.d.ts +8 -3
  127. package/dist/reference/mixed-content.js +4 -2
  128. package/dist/reference/page-notation.d.ts +39 -7
  129. package/dist/reference/page-notation.js +62 -10
  130. package/dist/reference/raw-floats.mjs +37 -13
  131. package/dist/reference/references.d.ts +11 -1
  132. package/dist/reference/references.js +6 -0
  133. package/dist/reference/registry.js +115 -11
  134. package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
  135. package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
  136. package/dist/reference/rehype-heading-anchors.mjs +11 -1
  137. package/dist/reference/remark-citation.mjs +0 -0
  138. package/dist/reference/remark-notation.mjs +275 -23
  139. package/dist/reference/render-lab-math.d.ts +13 -2
  140. package/dist/reference/render-lab-math.js +18 -6
  141. package/dist/reference/sections-validate.d.ts +28 -3
  142. package/dist/reference/sections-validate.js +31 -4
  143. package/dist/reference/sections.d.mts +24 -6
  144. package/dist/reference/sections.mjs +92 -17
  145. package/dist/reference/tables-validate.d.ts +1 -1
  146. package/dist/reference/tables-validate.js +24 -1
  147. package/dist/reference/text-measure.d.ts +10 -0
  148. package/dist/reference/text-measure.js +41 -0
  149. package/dist/reference/text-widths.d.ts +10 -0
  150. package/dist/reference/text-widths.js +1238 -0
  151. package/dist/reference/types.d.ts +64 -8
  152. package/dist/rules/catalog.js +223 -32
  153. package/dist/rules/in-effect.d.ts +1 -1
  154. package/dist/rules/rules.schema.json +100 -27
  155. package/dist/rules/settings.d.ts +1 -1
  156. package/dist/rules/settings.js +7 -0
  157. package/dist/session/PreferenceStore.d.ts +18 -1
  158. package/dist/session/PreferenceStore.js +29 -1
  159. package/docs/authoring/README.md +33 -21
  160. package/docs/authoring/agents.md +38 -7
  161. package/docs/authoring/components.md +115 -10
  162. package/docs/authoring/migrating.md +878 -0
  163. package/docs/authoring/notation.md +285 -30
  164. package/docs/authoring/retired.md +1 -0
  165. package/docs/authoring/structure-rules.md +84 -45
  166. package/docs/authoring/structure.md +67 -2
  167. package/docs/authoring/visualizations.md +167 -25
  168. package/package.json +12 -7
  169. package/src/compiler/astro.ts +72 -21
  170. package/src/compiler/cli.ts +143 -35
  171. package/src/compiler/collections.ts +158 -27
  172. package/src/compiler/content-schema.ts +108 -0
  173. package/src/compiler/content-schemas.ts +391 -0
  174. package/src/compiler/course-config.ts +86 -15
  175. package/src/compiler/exports/combined-html.ts +669 -0
  176. package/src/compiler/exports/download-index.ts +36 -0
  177. package/src/compiler/exports/export-chapters.ts +233 -0
  178. package/src/compiler/exports/export-pages.ts +44 -0
  179. package/src/compiler/exports/html-tree.ts +62 -0
  180. package/src/compiler/exports/latex/capture.ts +651 -0
  181. package/src/compiler/exports/latex/compile.ts +168 -0
  182. package/src/compiler/exports/latex/document.ts +479 -0
  183. package/src/compiler/exports/latex/explico.cls +415 -0
  184. package/src/compiler/exports/latex/numbering.ts +74 -0
  185. package/src/compiler/exports/latex/serialize.ts +787 -0
  186. package/src/compiler/exports/latex/texlive-packages.txt +76 -0
  187. package/src/compiler/exports/latex-export.ts +255 -0
  188. package/src/compiler/exports/markdown/serialize.ts +632 -0
  189. package/src/compiler/exports/markdown-export.ts +329 -0
  190. package/src/compiler/exports/standalone-html.ts +39 -70
  191. package/src/compiler/exports/static-tree.ts +944 -0
  192. package/src/compiler/exports/zip.ts +88 -0
  193. package/src/compiler/lesson-derivation.ts +13 -8
  194. package/src/compiler/manifest.ts +387 -40
  195. package/src/compiler/render-diagnostics.ts +111 -0
  196. package/src/components/CourseContents.astro +6 -0
  197. package/src/components/HoverPanel.astro +204 -6
  198. package/src/components/PartOverview.astro +18 -9
  199. package/src/components/abbreviation/AbbreviationGlossary.astro +6 -2
  200. package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
  201. package/src/components/assessments/AssessmentRunner.tsx +7 -1
  202. package/src/components/assessments/AssessmentSet.astro +14 -1
  203. package/src/components/block-heading.ts +38 -0
  204. package/src/components/card-arrow.ts +18 -4
  205. package/src/components/card-carry.ts +406 -0
  206. package/src/components/card-drag.ts +1 -30
  207. package/src/components/card-fetch.ts +87 -0
  208. package/src/components/card-manager.ts +1222 -38
  209. package/src/components/card-memory.ts +5 -1
  210. package/src/components/card-placement.ts +211 -13
  211. package/src/components/card-side.ts +281 -31
  212. package/src/components/citation/CitationLayer.astro +45 -40
  213. package/src/components/diagram/Diagram.astro +123 -44
  214. package/src/components/disclosure/print-as-shown.ts +42 -6
  215. package/src/components/figure/Figure.astro +17 -1
  216. package/src/components/figure/FloatCaption.astro +4 -17
  217. package/src/components/figure/figure-state.ts +26 -17
  218. package/src/components/float-toolbar.ts +274 -0
  219. package/src/components/memo/Memo.astro +21 -10
  220. package/src/components/memo/ShortNote.astro +4 -0
  221. package/src/components/notation/NotationGlossary.astro +22 -17
  222. package/src/components/notation/NotationLayer.astro +300 -79
  223. package/src/components/notation/NotationMath.astro +8 -1
  224. package/src/components/notation/render-notation-math.ts +51 -5
  225. package/src/components/readable-text.ts +25 -0
  226. package/src/components/reference/FloatRefLayer.astro +162 -89
  227. package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
  228. package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
  229. package/src/components/starlight/LayoutPageSidebar.astro +17 -3
  230. package/src/components/starlight/LayoutSocialIcons.astro +79 -29
  231. package/src/components/starlight/LessonExportActions.astro +384 -310
  232. package/src/components/starlight/LessonFooter.astro +66 -8
  233. package/src/components/starlight/PrintController.astro +28 -0
  234. package/src/components/starlight/TextSizeControl.astro +137 -0
  235. package/src/components/starlight/print-controller.ts +380 -0
  236. package/src/components/starlight/section-summary.ts +13 -5
  237. package/src/components/table/Table.astro +14 -22
  238. package/src/components/table/table-truncation.ts +15 -0
  239. package/src/content-config.ts +20 -312
  240. package/src/curriculum/validation.ts +72 -24
  241. package/src/reference/base-abbreviations.mjs +2 -2
  242. package/src/reference/base-operators.mjs +8 -10
  243. package/src/reference/base-roman-labels.mjs +4 -3
  244. package/src/reference/citation-format.mjs +74 -0
  245. package/src/reference/consistency.ts +47 -61
  246. package/src/reference/curriculum-alignment.ts +4 -41
  247. package/src/reference/derived-entries.ts +68 -0
  248. package/src/reference/derived-notation.mjs +121 -0
  249. package/src/reference/diagram-fit.ts +104 -0
  250. package/src/reference/diagram-layout.ts +182 -51
  251. package/src/reference/diagrams-validate.ts +2 -1
  252. package/src/reference/equations-validate.ts +41 -2
  253. package/src/reference/equations.mjs +120 -22
  254. package/src/reference/examples-validate.ts +1 -0
  255. package/src/reference/figure-fit.ts +139 -0
  256. package/src/reference/figure-pictures.ts +96 -0
  257. package/src/reference/figure-symbols.ts +117 -0
  258. package/src/reference/figures-validate.ts +2 -1
  259. package/src/reference/gate-abbreviations.ts +1 -1
  260. package/src/reference/gate-course-rules.ts +22 -7
  261. package/src/reference/gate-heading-length.ts +25 -7
  262. package/src/reference/gate-heading-structure.ts +40 -1
  263. package/src/reference/gate-indices.ts +470 -0
  264. package/src/reference/gate-math.ts +102 -13
  265. package/src/reference/gate-mdx-expression.ts +45 -0
  266. package/src/reference/gate-non-chapter-pages.ts +241 -0
  267. package/src/reference/gate-part-pages.ts +1 -1
  268. package/src/reference/gate-prop-text.ts +145 -0
  269. package/src/reference/gate-prose-notation.ts +19 -5
  270. package/src/reference/gate-retired-keys.ts +101 -0
  271. package/src/reference/gloss.ts +2 -0
  272. package/src/reference/katex-measure.ts +384 -0
  273. package/src/reference/keyed-float-validate.ts +22 -0
  274. package/src/reference/keyed-jsx-floats.mjs +11 -14
  275. package/src/reference/lab-katex-validate.ts +156 -0
  276. package/src/reference/lab-math-scope.ts +15 -1
  277. package/src/reference/lint-ignore.ts +4 -1
  278. package/src/reference/math-glyphs.mjs +851 -46
  279. package/src/reference/mixed-content.ts +11 -2
  280. package/src/reference/page-notation.ts +114 -14
  281. package/src/reference/raw-floats.mjs +37 -13
  282. package/src/reference/references.ts +14 -0
  283. package/src/reference/registry.ts +170 -9
  284. package/src/reference/rehype-fail-katex-errors.mjs +19 -0
  285. package/src/reference/rehype-heading-anchors.mjs +11 -1
  286. package/src/reference/remark-citation.mjs +0 -0
  287. package/src/reference/remark-notation.mjs +275 -23
  288. package/src/reference/render-lab-math.ts +40 -5
  289. package/src/reference/sections-validate.ts +67 -3
  290. package/src/reference/sections.mjs +92 -17
  291. package/src/reference/tables-validate.ts +40 -2
  292. package/src/reference/text-measure.ts +63 -0
  293. package/src/reference/text-widths.ts +133 -0
  294. package/src/reference/types.ts +75 -8
  295. package/src/rules/catalog.ts +241 -31
  296. package/src/rules/settings.ts +10 -0
  297. package/src/session/PreferenceStore.ts +53 -0
  298. package/src/styles/global.css +322 -83
@@ -2,7 +2,8 @@ import { effectiveAccentAllowlist } from "./base-accents.mjs";
2
2
  import { effectiveOperatorAllowlist } from "./base-operators.mjs";
3
3
  import { effectiveRomanLabelAllowlist } from "./base-roman-labels.mjs";
4
4
  import { stripEquationLabels } from "./equations.mjs";
5
- import { pageScope } from "./gate-math.js";
5
+ import { pageScope, scopeEntry } from "./gate-math.js";
6
+ import { isIndexEntryLatex } from "../compiler/content-schemas.js";
6
7
  import {
7
8
  collectDecorations,
8
9
  collectOperators,
@@ -12,12 +13,12 @@ import {
12
13
  resolveMathGlyphs
13
14
  } from "./math-glyphs.mjs";
14
15
  import { offVocabularyTokens } from "./units-vocab.js";
15
- const CONVENTION_RE = /\bsign convention\b|\bcash-flow perspective\b|\bholder(?:'s)?(?:\s+\w+){0,2}\s+perspective\b|\bfrom (?:a|one|the|its)(?:\s+\w+){0,3}\s+perspective\b|\breverses? every sign\b|\bpositive (?:means|amounts?)\b/i;
16
+ import { blankPropStrings } from "./gate-prop-text.js";
16
17
  function stripCode(text) {
17
18
  return text.replace(/```[\s\S]*?```/g, (block) => block.replace(/[^\n]/g, " ")).replace(/(^|[^`])`[^`\n]*`/g, (match) => match.replace(/[^\n]/g, " "));
18
19
  }
19
20
  function mathSpans(body) {
20
- const searchable = stripCode(body);
21
+ const searchable = stripCode(blankPropStrings(body));
21
22
  const spans = [];
22
23
  for (const match of searchable.matchAll(/\$\$([\s\S]+?)\$\$/g)) {
23
24
  spans.push({ latex: stripEquationLabels(match[1] ?? "") });
@@ -79,6 +80,7 @@ function checkGlyphUniqueness(input, out) {
79
80
  }
80
81
  function checkNotationUnits(input, out) {
81
82
  for (const spec of input.specs) {
83
+ if (isIndexEntryLatex(spec.latex)) continue;
82
84
  if (spec.units === void 0 && !spec.dimensionless) {
83
85
  if (input.lintIgnore.matches("notation-units", spec.key)) continue;
84
86
  out.push({
@@ -105,35 +107,6 @@ function checkNotationUnits(input, out) {
105
107
  });
106
108
  }
107
109
  }
108
- function checkConventionSingleDefinition(input, out) {
109
- const lessonById = new Map(
110
- input.catalog.lessons.map((lesson) => [lesson.id, lesson])
111
- );
112
- for (const lesson of input.notationInput.lessons) {
113
- const body = lesson.body;
114
- if (!CONVENTION_RE.test(body)) continue;
115
- const meta = lessonById.get(lesson.lessonId);
116
- const { signConventionKey, cashFlowPerspectiveCompetency } = input.conventions;
117
- const anchoredByCompetency = [
118
- ...meta?.teaches ?? [],
119
- ...meta?.requires ?? []
120
- ].includes(cashFlowPerspectiveCompetency);
121
- const anchoredByTerm = new RegExp(
122
- `\\[\\[\\s*${signConventionKey}\\s*\\]\\]`
123
- ).test(body);
124
- if (anchoredByCompetency || anchoredByTerm) continue;
125
- if (input.lintIgnore.matches("convention-single-definition", lesson.lessonId)) {
126
- continue;
127
- }
128
- out.push({
129
- code: "convention-single-definition",
130
- severity: "warning",
131
- lessonId: lesson.lessonId,
132
- file: lesson.source.file,
133
- message: `${lesson.lessonId} states a sign / cash-flow-perspective convention in prose but does not resolve it to a single declaration ([[${signConventionKey}]] or ${cashFlowPerspectiveCompetency})`
134
- });
135
- }
136
- }
137
110
  function checkWeakLocal(input, out) {
138
111
  for (const lesson of input.notationInput.lessons) {
139
112
  const spans = mathSpans(lesson.body).map((span) => span.latex);
@@ -295,7 +268,7 @@ function checkOperatorExplained(input, out) {
295
268
  lessonId: lesson.lessonId,
296
269
  file: lesson.source.file,
297
270
  glyph: operator.name,
298
- message: `${lesson.lessonId}: operator ${JSON.stringify(display)} is neither on the course operator allowlist nor explained by a notation entry; give it a card or notation.local entry (latex ${JSON.stringify(display)}) introduced with [[key]], or add ${JSON.stringify(operator.name)} to operators.allow in course.config.ts`
271
+ message: `${lesson.lessonId}: operator ${JSON.stringify(display)} is neither on the course operator allowlist nor explained by a notation entry; give it a card or notation.local entry (latex ${JSON.stringify(display)}) introduced with [[key]], or add ${JSON.stringify(operator.name)} to rules.N8.allow in the course config (course.config.ts, or the course.settings.json it reads)`
299
272
  });
300
273
  }
301
274
  }
@@ -339,7 +312,7 @@ function checkRomanLabelExplained(input, out) {
339
312
  lessonId: lesson.lessonId,
340
313
  file: lesson.source.file,
341
314
  glyph: label.name,
342
- message: `${lesson.lessonId}: roman label ${JSON.stringify(`\\mathrm{${label.name}}`)} is neither on the course roman-label allowlist nor explained by a notation entry; give it a card or notation.local entry (latex ${JSON.stringify(`\\mathrm{${label.name}}`)}) introduced with [[key]], or add ${JSON.stringify(label.name)} to romanLabels.allow in course.config.ts`
315
+ message: `${lesson.lessonId}: roman label ${JSON.stringify(`\\mathrm{${label.name}}`)} is neither on the course roman-label allowlist nor explained by a notation entry; give it a card or notation.local entry (latex ${JSON.stringify(`\\mathrm{${label.name}}`)}) introduced with [[key]], or add ${JSON.stringify(label.name)} to rules.N10.allow in the course config (course.config.ts, or the course.settings.json it reads)`
343
316
  });
344
317
  }
345
318
  }
@@ -349,7 +322,7 @@ function sharedScopeByKey(input) {
349
322
  return new Map(
350
323
  input.notationInput.sharedDefinitions.map((definition) => [
351
324
  definition.key,
352
- { key: definition.key, notation: definition.notation }
325
+ scopeEntry(definition)
353
326
  ])
354
327
  );
355
328
  }
@@ -363,11 +336,35 @@ function checkAccentExplained(input, out) {
363
336
  for (const span of mathSpans(lesson.body)) {
364
337
  let decorations;
365
338
  try {
366
- ({ decorations } = resolveMathGlyphs(span.latex, scope, { suspended }));
339
+ ({ decorations } = resolveMathGlyphs(span.latex, scope, {
340
+ suspended,
341
+ derive: true
342
+ }));
367
343
  } catch {
368
344
  continue;
369
345
  }
370
346
  for (const decoration of decorations) {
347
+ if (decoration.wholeForm !== null && decoration.wholeFormExplicit) {
348
+ const notation = scope.find((entry) => entry.key === decoration.wholeForm)?.notation ?? "";
349
+ const carries = collectDecorations(notation).some(
350
+ (item) => item.name === decoration.name
351
+ );
352
+ const id = `swallowed:${decoration.token}`;
353
+ if (!carries && !reported.has(id)) {
354
+ reported.add(id);
355
+ if (!input.lintIgnore.matches("accent-swallowed", decoration.name)) {
356
+ out.push({
357
+ code: "accent-swallowed",
358
+ severity: "warning",
359
+ lessonId: lesson.lessonId,
360
+ file: lesson.source.file,
361
+ glyph: decoration.name,
362
+ message: `${lesson.lessonId}: \\explain{${decoration.wholeForm}}{${decoration.token}} wraps the decoration ${JSON.stringify(decoration.name)} in ${decoration.wholeForm}, whose latex (${notation}) has none, so its card cannot explain it; wrap only the symbol under it and give the decoration an operator entry (latex ${JSON.stringify(operatorEntryLatex(decoration.name))}), or name an entry for the decorated form (latex ${JSON.stringify(decoration.token)})`
363
+ });
364
+ }
365
+ }
366
+ continue;
367
+ }
371
368
  if (decoration.wholeForm !== null || decoration.operator !== null) continue;
372
369
  if (suspended.includes(decoration.name)) continue;
373
370
  if (allowlist.has(decoration.name)) continue;
@@ -436,7 +433,6 @@ function checkOperatorSuspension(input, out) {
436
433
  const CHECKS = [
437
434
  checkGlyphUniqueness,
438
435
  checkNotationUnits,
439
- checkConventionSingleDefinition,
440
436
  checkWeakLocal,
441
437
  checkNotationSourceLocator,
442
438
  checkGlossWantsPromoting,
@@ -459,7 +455,6 @@ function consistencyCounts(diagnostics) {
459
455
  "glyph-unique-in-corpus": 0,
460
456
  "notation-units": 0,
461
457
  "units-vocab": 0,
462
- "convention-single-definition": 0,
463
458
  "weak-local": 0,
464
459
  "notation-source-locator": 0,
465
460
  "gloss-wants-promoting": 0,
@@ -468,6 +463,7 @@ function consistencyCounts(diagnostics) {
468
463
  "operator-explained": 0,
469
464
  "roman-label-explained": 0,
470
465
  "accent-explained": 0,
466
+ "accent-swallowed": 0,
471
467
  "operator-suspended": 0,
472
468
  "operator-suspension-reason": 0,
473
469
  "operator-shadowed": 0
@@ -2,13 +2,10 @@ import { compareDiagnostics } from "./references.js";
2
2
  function validateNotationAlignment(registry, catalog) {
3
3
  const diagnostics = [];
4
4
  const competencies = new Map(
5
- catalog.competencies.map((entry) => [
6
- entry.id,
7
- entry
8
- ])
5
+ catalog.competencies.map((entry) => [entry.id, entry])
9
6
  );
10
7
  const lessons = new Map(catalog.lessons.map((entry) => [entry.id, entry]));
11
- const sources = new Map(catalog.sources.map((entry) => [entry.id, entry]));
8
+ const sources = new Set(catalog.sources.map((entry) => entry.id));
12
9
  const trackPositions = buildTrackPositions(catalog, diagnostics);
13
10
  for (const definition of registry.definitions) {
14
11
  validateSources(definition, sources, diagnostics);
@@ -60,15 +57,6 @@ function validateNotationAlignment(registry, catalog) {
60
57
  trackPositions,
61
58
  diagnostics
62
59
  );
63
- if (definition.status === "reviewed" && (lesson.status !== "reviewed" || competency?.editorialStatus !== "reviewed")) {
64
- diagnostics.push(
65
- error(
66
- "alignment-review-state",
67
- `${definition.id} is reviewed while its owning lesson or competency is not reviewed`,
68
- definition
69
- )
70
- );
71
- }
72
60
  }
73
61
  return diagnostics.sort(compareDiagnostics);
74
62
  }
@@ -129,8 +117,7 @@ function validateIntroductionOrder(definition, introductionLessonId, registry, t
129
117
  }
130
118
  function validateSources(definition, sources, diagnostics) {
131
119
  for (const sourceId of definition.sources) {
132
- const source = sources.get(sourceId);
133
- if (!source) {
120
+ if (!sources.has(sourceId)) {
134
121
  diagnostics.push(
135
122
  error(
136
123
  "alignment-unavailable",
@@ -138,14 +125,6 @@ function validateSources(definition, sources, diagnostics) {
138
125
  definition
139
126
  )
140
127
  );
141
- } else if (definition.status === "reviewed" && source.editorialStatus !== "reviewed") {
142
- diagnostics.push(
143
- error(
144
- "alignment-review-state",
145
- `${definition.id} is reviewed while source ${sourceId} is ${source.editorialStatus}`,
146
- definition
147
- )
148
- );
149
128
  }
150
129
  }
151
130
  }
@@ -0,0 +1,45 @@
1
+ import { ManifestDerivedNotation } from '../compiler/manifest.js';
2
+ import { GlyphScopeEntry } from './gloss.js';
3
+ import { DerivedUse, NotationDefinitionRecord } from './types.js';
4
+ import '../curriculum/validation.js';
5
+ import './consistency.js';
6
+ import '../course-config-rRtME5UV.js';
7
+ import '../rules/catalog.js';
8
+ import '../rules/course-rules.js';
9
+ import '../compiler/collections.js';
10
+ import './gate-retired-keys.js';
11
+ import './lint-ignore.js';
12
+ import './equations-validate.js';
13
+ import './tables-validate.js';
14
+ import './keyed-float-validate.js';
15
+ import './figures-validate.js';
16
+ import './diagrams-validate.js';
17
+ import './examples-validate.js';
18
+ import './folds-validate.js';
19
+ import './knowledge-checks-validate.js';
20
+ import './raw-floats-validate.js';
21
+ import './lab-katex-validate.js';
22
+ import './gate-math.js';
23
+ import './mixed-content.js';
24
+ import '../rules/waivers.js';
25
+ import '../compiler/knowledge-version.js';
26
+ import '../compiler/lesson-derivation.js';
27
+
28
+ /**
29
+ * Derived cards (rule N23) as the page payload carries them: each derived
30
+ * card a page's math uses, composed from its base and index entries by
31
+ * `derived-notation.mjs`, with the glyph table its formula resolves against.
32
+ * The manifest builds the ones lesson-body and assessment math use; the page
33
+ * payload (`page-notation.ts`) builds the ones a lab or a diagram registers
34
+ * while it renders, the same way.
35
+ */
36
+
37
+ /**
38
+ * The derived cards for `uses`, sorted by key. `records` holds the page's
39
+ * entries by key, local ones winning; a use whose base or index entry is not
40
+ * there is skipped. A card's formula resolves against the derived symbol,
41
+ * the index entry's glosses, the base, and the cards the formula names.
42
+ */
43
+ declare function derivedNotationEntries(uses: readonly DerivedUse[], records: ReadonlyMap<string, NotationDefinitionRecord>, cardsByKey: ReadonlyMap<string, GlyphScopeEntry>): ManifestDerivedNotation[];
44
+
45
+ export { derivedNotationEntries };
@@ -0,0 +1,50 @@
1
+ import { composeDerivedEntry } from "./derived-notation.mjs";
2
+ import { scopeEntry } from "./gate-math.js";
3
+ import { formulaExplainKeys } from "./gloss.js";
4
+ import { baseSymbolLatex, derivedNotationLatex } from "./math-glyphs.mjs";
5
+ function derivedNotationEntries(uses, records, cardsByKey) {
6
+ const derived = /* @__PURE__ */ new Map();
7
+ for (const use of uses) {
8
+ if (derived.has(use.key)) continue;
9
+ const base = records.get(use.base);
10
+ const index = records.get(use.index);
11
+ if (!base || !index) continue;
12
+ const notation = derivedNotationLatex(base.notation, index.notation);
13
+ const entry = composeDerivedEntry({
14
+ key: use.key,
15
+ notation,
16
+ baseLatex: baseSymbolLatex(base.notation),
17
+ base,
18
+ index: {
19
+ ...index,
20
+ ...index.formulaTemplate === void 0 ? {} : { formula: index.formulaTemplate }
21
+ }
22
+ });
23
+ const formulaScope = [
24
+ { key: use.key, notation },
25
+ ...index.glosses.map((gloss) => ({ key: gloss.key, notation: gloss.notation })),
26
+ scopeEntry(base)
27
+ ];
28
+ for (const key of formulaExplainKeys(entry.formula ?? "")) {
29
+ if (formulaScope.some((row) => row.key === key)) continue;
30
+ const card = records.get(key) ?? cardsByKey.get(key);
31
+ if (card) formulaScope.push(scopeEntry(card));
32
+ }
33
+ derived.set(use.key, {
34
+ key: entry.key,
35
+ base: entry.base,
36
+ index: entry.index,
37
+ notation: entry.notation,
38
+ label: entry.label,
39
+ summary: entry.summary,
40
+ glosses: [...index.glosses],
41
+ formulaScope,
42
+ ...entry.units === void 0 ? {} : { units: entry.units },
43
+ ...entry.formula === void 0 ? {} : { formula: entry.formula }
44
+ });
45
+ }
46
+ return [...derived.values()].sort((left, right) => left.key.localeCompare(right.key));
47
+ }
48
+ export {
49
+ derivedNotationEntries
50
+ };
@@ -0,0 +1,125 @@
1
+ /**
2
+ * A label as it reads inside a sentence: its first letter lowercased unless
3
+ * the first word is an acronym (`OLS estimate`) — the same rule
4
+ * `composeHeaderLabel` applies to an operator's operand.
5
+ *
6
+ * @param {string} label
7
+ * @returns {string}
8
+ */
9
+ export function inSentence(label: string): string;
10
+ /**
11
+ * A derived card's title: the index entry's `reads` with `{base}` replaced by
12
+ * the base's label (lowercased in mid-sentence), or `"<index label> of
13
+ * <base label>"` when the index entry gives no `reads`.
14
+ *
15
+ * @param {{ label: string, reads?: string }} index
16
+ * @param {string} baseLabel
17
+ * @returns {string}
18
+ */
19
+ export function derivedLabel(index: {
20
+ label: string;
21
+ reads?: string;
22
+ }, baseLabel: string): string;
23
+ /**
24
+ * An index entry's formula with `{base}` bound: to the base symbol under its
25
+ * own `\explain`, for a derived card, so the reader opens the base's card
26
+ * from it.
27
+ *
28
+ * @param {string} formula
29
+ * @param {string} baseKey
30
+ * @param {string} baseLatex
31
+ * @returns {string}
32
+ */
33
+ export function derivedFormula(formula: string, baseKey: string, baseLatex: string): string;
34
+ /**
35
+ * An index entry's own formula, shown on the index entry's card and checked
36
+ * by the formula gate: `{base}` becomes a placeholder square bound to the
37
+ * entry itself, as an operator entry is shown over a square.
38
+ *
39
+ * @param {string} formula
40
+ * @param {string} ownKey
41
+ * @returns {string}
42
+ */
43
+ export function indexEntryFormula(formula: string, ownKey: string): string;
44
+ /**
45
+ * @typedef {object} DerivedSource
46
+ * @property {string} key
47
+ * @property {string} notation
48
+ * @property {string} label
49
+ * @property {string} [summary]
50
+ * @property {string} [units]
51
+ * @property {string} [reads]
52
+ * @property {string} [formula]
53
+ * @property {readonly { key: string, notation: string, label: string, units?: string, ownerKey: string }[]} [glosses]
54
+ */
55
+ /**
56
+ * The derived entry for one (base, index) pair. `notation` is the indexed
57
+ * symbol, from `derivedNotationLatex`.
58
+ *
59
+ * @param {{ key: string, notation: string, baseLatex: string, base: DerivedSource, index: DerivedSource }} input
60
+ */
61
+ export function composeDerivedEntry({ key, notation, baseLatex, base, index }: {
62
+ key: string;
63
+ notation: string;
64
+ baseLatex: string;
65
+ base: DerivedSource;
66
+ index: DerivedSource;
67
+ }): {
68
+ formula?: string | undefined;
69
+ units?: string | undefined;
70
+ key: string;
71
+ notation: string;
72
+ base: string;
73
+ index: string;
74
+ label: string;
75
+ summary: string;
76
+ glosses: {
77
+ key: string;
78
+ notation: string;
79
+ label: string;
80
+ units?: string;
81
+ ownerKey: string;
82
+ }[];
83
+ };
84
+ /**
85
+ * Derived notation entries (rule N23): the card for a symbol with a subscript
86
+ * that an **index entry** explains — `\mathbf{v}_0` from the base entry
87
+ * `velocity-vector` (`\mathbf{v}`) and the index entry `motion.initial`
88
+ * (`{}_0`, "Initial value").
89
+ *
90
+ * The resolver writes one `\explain` around the whole indexed symbol, with
91
+ * the key `derivedNotationKey(base, index)` (`math-glyphs.mjs`); the build
92
+ * materializes the entry from the two, here, so that everything downstream
93
+ * treats it as one more card. What the reader sees first is about the
94
+ * indexed symbol; the base's own card is one level deeper, through the base
95
+ * symbol on the card's formula line:
96
+ *
97
+ * | Field | From |
98
+ * | ------- | ------------------------------------------- |
99
+ * | symbol | the base's symbol plus the index |
100
+ * | title | the index entry's `reads`, the base's label |
101
+ * | summary | the index entry's `meaning`, never the base's |
102
+ * | formula | the index entry's `formula`, `{base}` bound |
103
+ * | units | the base entry |
104
+ *
105
+ * Plain JavaScript with no parser, so the remark pass and the manifest share
106
+ * it; `derivedNotationLatex` in `math-glyphs.mjs` builds the symbol.
107
+ */
108
+ /** The indexed symbol, in an index entry's `reads` and `formula`. */
109
+ export const BASE_PLACEHOLDER: "{base}";
110
+ export type DerivedSource = {
111
+ key: string;
112
+ notation: string;
113
+ label: string;
114
+ summary?: string | undefined;
115
+ units?: string | undefined;
116
+ reads?: string | undefined;
117
+ formula?: string | undefined;
118
+ glosses?: readonly {
119
+ key: string;
120
+ notation: string;
121
+ label: string;
122
+ units?: string;
123
+ ownerKey: string;
124
+ }[] | undefined;
125
+ };
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Derived notation entries (rule N23): the card for a symbol with a subscript
3
+ * that an **index entry** explains — `\mathbf{v}_0` from the base entry
4
+ * `velocity-vector` (`\mathbf{v}`) and the index entry `motion.initial`
5
+ * (`{}_0`, "Initial value").
6
+ *
7
+ * The resolver writes one `\explain` around the whole indexed symbol, with
8
+ * the key `derivedNotationKey(base, index)` (`math-glyphs.mjs`); the build
9
+ * materializes the entry from the two, here, so that everything downstream
10
+ * treats it as one more card. What the reader sees first is about the
11
+ * indexed symbol; the base's own card is one level deeper, through the base
12
+ * symbol on the card's formula line:
13
+ *
14
+ * | Field | From |
15
+ * | ------- | ------------------------------------------- |
16
+ * | symbol | the base's symbol plus the index |
17
+ * | title | the index entry's `reads`, the base's label |
18
+ * | summary | the index entry's `meaning`, never the base's |
19
+ * | formula | the index entry's `formula`, `{base}` bound |
20
+ * | units | the base entry |
21
+ *
22
+ * Plain JavaScript with no parser, so the remark pass and the manifest share
23
+ * it; `derivedNotationLatex` in `math-glyphs.mjs` builds the symbol.
24
+ */
25
+
26
+ /** The indexed symbol, in an index entry's `reads` and `formula`. */
27
+ export const BASE_PLACEHOLDER = '{base}';
28
+
29
+ /**
30
+ * A label as it reads inside a sentence: its first letter lowercased unless
31
+ * the first word is an acronym (`OLS estimate`) — the same rule
32
+ * `composeHeaderLabel` applies to an operator's operand.
33
+ *
34
+ * @param {string} label
35
+ * @returns {string}
36
+ */
37
+ export function inSentence(label) {
38
+ const [first = ''] = label.split(/\s+/u);
39
+ if (first.length > 1 && first === first.toUpperCase()) return label;
40
+ return label.charAt(0).toLowerCase() + label.slice(1);
41
+ }
42
+
43
+ /**
44
+ * A derived card's title: the index entry's `reads` with `{base}` replaced by
45
+ * the base's label (lowercased in mid-sentence), or `"<index label> of
46
+ * <base label>"` when the index entry gives no `reads`.
47
+ *
48
+ * @param {{ label: string, reads?: string }} index
49
+ * @param {string} baseLabel
50
+ * @returns {string}
51
+ */
52
+ export function derivedLabel(index, baseLabel) {
53
+ const reads = index.reads ?? `${index.label} of ${BASE_PLACEHOLDER}`;
54
+ const at = reads.indexOf(BASE_PLACEHOLDER);
55
+ const filled = reads
56
+ .split(BASE_PLACEHOLDER)
57
+ .join(at === 0 ? baseLabel : inSentence(baseLabel));
58
+ // `{base}` first in a later position, or a `reads` that starts lowercase.
59
+ return filled.charAt(0).toUpperCase() + filled.slice(1);
60
+ }
61
+
62
+ /**
63
+ * An index entry's formula with `{base}` bound: to the base symbol under its
64
+ * own `\explain`, for a derived card, so the reader opens the base's card
65
+ * from it.
66
+ *
67
+ * @param {string} formula
68
+ * @param {string} baseKey
69
+ * @param {string} baseLatex
70
+ * @returns {string}
71
+ */
72
+ export function derivedFormula(formula, baseKey, baseLatex) {
73
+ return formula.split(BASE_PLACEHOLDER).join(`\\explain{${baseKey}}{${baseLatex}}`);
74
+ }
75
+
76
+ /**
77
+ * An index entry's own formula, shown on the index entry's card and checked
78
+ * by the formula gate: `{base}` becomes a placeholder square bound to the
79
+ * entry itself, as an operator entry is shown over a square.
80
+ *
81
+ * @param {string} formula
82
+ * @param {string} ownKey
83
+ * @returns {string}
84
+ */
85
+ export function indexEntryFormula(formula, ownKey) {
86
+ return formula.split(BASE_PLACEHOLDER).join(`\\explain{${ownKey}}{\\square}`);
87
+ }
88
+
89
+ /**
90
+ * @typedef {object} DerivedSource
91
+ * @property {string} key
92
+ * @property {string} notation
93
+ * @property {string} label
94
+ * @property {string} [summary]
95
+ * @property {string} [units]
96
+ * @property {string} [reads]
97
+ * @property {string} [formula]
98
+ * @property {readonly { key: string, notation: string, label: string, units?: string, ownerKey: string }[]} [glosses]
99
+ */
100
+
101
+ /**
102
+ * The derived entry for one (base, index) pair. `notation` is the indexed
103
+ * symbol, from `derivedNotationLatex`.
104
+ *
105
+ * @param {{ key: string, notation: string, baseLatex: string, base: DerivedSource, index: DerivedSource }} input
106
+ */
107
+ export function composeDerivedEntry({ key, notation, baseLatex, base, index }) {
108
+ return {
109
+ key,
110
+ notation,
111
+ base: base.key,
112
+ index: index.key,
113
+ label: derivedLabel(index, base.label),
114
+ summary: index.summary ?? '',
115
+ glosses: [...(index.glosses ?? [])],
116
+ ...(base.units === undefined ? {} : { units: base.units }),
117
+ ...(index.formula === undefined
118
+ ? {}
119
+ : { formula: derivedFormula(index.formula, base.key, baseLatex) }),
120
+ };
121
+ }
@@ -0,0 +1,45 @@
1
+ import { M as ManifestRules } from '../course-config-rRtME5UV.js';
2
+ import '../rules/catalog.js';
3
+ import '../rules/course-rules.js';
4
+
5
+ /**
6
+ * Rule V8: a diagram is no wider than the lesson column.
7
+ *
8
+ * `Diagram.astro` lays a diagram out at build time and so knows its width, but
9
+ * not the page it is on or what the course allows, and a diagram drawn by a
10
+ * wrapper component (`<CourseDiagram kind="…">`) never shows its nodes to the
11
+ * manifest walk. So the check runs on what was rendered: the component marks
12
+ * a diagram wider than {@link diagramWidthLimit} with `data-diagram-too-wide`,
13
+ * `compiler/astro.ts` finds each mark with {@link oversizedDiagrams} on every
14
+ * built page and reports a `dia-too-wide` warning with the lesson file and the
15
+ * diagram id, and `content warnings` lists what the last build reported
16
+ * (`compiler/render-diagnostics.ts`). Under `astro dev` the component prints
17
+ * the same message as it renders.
18
+ */
19
+
20
+ /**
21
+ * The lesson column at a 1366px-wide window, the most common laptop screen
22
+ * (632px at 1280, 832px at 1920), unless the course sets
23
+ * `rules.V8.columnWidth`.
24
+ */
25
+ declare const DEFAULT_COLUMN_WIDTH = 707;
26
+ /**
27
+ * The widest a diagram may lay out, or `undefined` when nothing checks it: rule
28
+ * V8 is off, or the diagram says `warnAboveWidth={false}`. A number in
29
+ * `warnAboveWidth` replaces the course's column width for that diagram.
30
+ */
31
+ declare function diagramWidthLimit(warnAboveWidth: number | false | undefined, rules: Pick<ManifestRules, 'disabled' | 'settings'> | undefined): number | undefined;
32
+ /** What to tell the author about one diagram wider than its limit. */
33
+ declare function diagramTooWideMessage(id: string, width: number, limit: number): string;
34
+ /** A diagram on a built page that is wider than its limit. */
35
+ interface OversizedDiagram {
36
+ readonly code: 'dia-too-wide';
37
+ /** The diagram's `id`. */
38
+ readonly key: string;
39
+ readonly width: number;
40
+ readonly limit: number;
41
+ }
42
+ /** The diagrams `Diagram.astro` marked too wide in `html`. */
43
+ declare function oversizedDiagrams(html: string): OversizedDiagram[];
44
+
45
+ export { DEFAULT_COLUMN_WIDTH, type OversizedDiagram, diagramTooWideMessage, diagramWidthLimit, oversizedDiagrams };
@@ -0,0 +1,54 @@
1
+ import { parse } from "parse5";
2
+ const DEFAULT_COLUMN_WIDTH = 707;
3
+ function diagramWidthLimit(warnAboveWidth, rules) {
4
+ if (warnAboveWidth === false || rules?.disabled.includes("V8")) return void 0;
5
+ if (warnAboveWidth !== void 0) return warnAboveWidth;
6
+ const columnWidth = rules?.settings.V8?.columnWidth;
7
+ return typeof columnWidth === "number" ? columnWidth : DEFAULT_COLUMN_WIDTH;
8
+ }
9
+ function diagramTooWideMessage(id, width, limit) {
10
+ return `Diagram "${id}" lays out ${Math.round(width)}px wide, wider than the ${limit}px it may take (rule V8), so a reader has to scroll it or widen it to see it whole. Try direction="TB", a shorter label, or a narrower node \`width\` (its label wraps inside it); set warnAboveWidth={false} only when the diagram is meant to scroll.`;
11
+ }
12
+ function isElement(node) {
13
+ return "tagName" in node;
14
+ }
15
+ function attribute(node, name) {
16
+ return node.attrs.find((attr) => attr.name === name)?.value;
17
+ }
18
+ function viewportKey(node) {
19
+ for (const child of "childNodes" in node ? node.childNodes : []) {
20
+ if (!isElement(child)) continue;
21
+ const match = /^diagram-(.+)-viewport$/.exec(attribute(child, "id") ?? "");
22
+ if (match) return match[1];
23
+ const inner = viewportKey(child);
24
+ if (inner) return inner;
25
+ }
26
+ return void 0;
27
+ }
28
+ function oversizedDiagrams(html) {
29
+ if (!html.includes("data-diagram-too-wide")) return [];
30
+ const found = [];
31
+ const visit = (node) => {
32
+ for (const child of "childNodes" in node ? node.childNodes : []) {
33
+ if (!isElement(child)) continue;
34
+ const limit = attribute(child, "data-diagram-too-wide");
35
+ if (limit !== void 0) {
36
+ found.push({
37
+ code: "dia-too-wide",
38
+ key: viewportKey(child) ?? attribute(child, "id") ?? "",
39
+ width: Number(attribute(child, "data-diagram-width")),
40
+ limit: Number(limit)
41
+ });
42
+ }
43
+ visit(child);
44
+ }
45
+ };
46
+ visit(parse(html));
47
+ return found;
48
+ }
49
+ export {
50
+ DEFAULT_COLUMN_WIDTH,
51
+ diagramTooWideMessage,
52
+ diagramWidthLimit,
53
+ oversizedDiagrams
54
+ };