explico 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1122 -1
- package/README.md +91 -13
- package/dist/compiler/astro.d.ts +4 -3
- package/dist/compiler/astro.js +45 -15
- package/dist/compiler/cli.d.ts +26 -12
- package/dist/compiler/cli.js +96 -25
- package/dist/compiler/collections.d.ts +9 -1
- package/dist/compiler/collections.js +55 -16
- package/dist/compiler/content-schema.d.ts +31 -0
- package/dist/compiler/content-schema.js +82 -0
- package/dist/compiler/content-schemas.d.ts +422 -0
- package/dist/compiler/content-schemas.js +229 -0
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +30 -2
- package/dist/compiler/exports/combined-html.d.ts +97 -0
- package/dist/compiler/exports/combined-html.js +492 -0
- package/dist/compiler/exports/download-index.d.ts +9 -0
- package/dist/compiler/exports/download-index.js +33 -0
- package/dist/compiler/exports/export-chapters.d.ts +98 -0
- package/dist/compiler/exports/export-chapters.js +164 -0
- package/dist/compiler/exports/export-pages.d.ts +48 -0
- package/dist/compiler/exports/export-pages.js +22 -0
- package/dist/compiler/exports/html-tree.d.ts +22 -0
- package/dist/compiler/exports/html-tree.js +47 -0
- package/dist/compiler/exports/latex/capture.d.ts +106 -0
- package/dist/compiler/exports/latex/capture.js +435 -0
- package/dist/compiler/exports/latex/compile.d.ts +73 -0
- package/dist/compiler/exports/latex/compile.js +115 -0
- package/dist/compiler/exports/latex/document.d.ts +102 -0
- package/dist/compiler/exports/latex/document.js +349 -0
- package/dist/compiler/exports/latex/explico.cls +415 -0
- package/dist/compiler/exports/latex/numbering.d.ts +18 -0
- package/dist/compiler/exports/latex/numbering.js +57 -0
- package/dist/compiler/exports/latex/serialize.d.ts +112 -0
- package/dist/compiler/exports/latex/serialize.js +606 -0
- package/dist/compiler/exports/latex-export.d.ts +44 -0
- package/dist/compiler/exports/latex-export.js +169 -0
- package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
- package/dist/compiler/exports/markdown/serialize.js +527 -0
- package/dist/compiler/exports/markdown-export.d.ts +39 -0
- package/dist/compiler/exports/markdown-export.js +240 -0
- package/dist/compiler/exports/standalone-html.d.ts +14 -5
- package/dist/compiler/exports/standalone-html.js +23 -51
- package/dist/compiler/exports/static-tree.d.ts +253 -0
- package/dist/compiler/exports/static-tree.js +625 -0
- package/dist/compiler/exports/zip.d.ts +16 -0
- package/dist/compiler/exports/zip.js +72 -0
- package/dist/compiler/lesson-derivation.d.ts +8 -2
- package/dist/compiler/lesson-derivation.js +6 -7
- package/dist/compiler/manifest.d.ts +87 -6
- package/dist/compiler/manifest.js +174 -31
- package/dist/compiler/render-diagnostics.d.ts +65 -0
- package/dist/compiler/render-diagnostics.js +56 -0
- package/dist/compiler/resolution-report.d.ts +2 -1
- package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
- package/dist/curriculum/validation.d.ts +9 -3
- package/dist/curriculum/validation.js +35 -17
- package/dist/reference/base-abbreviations.d.mts +2 -2
- package/dist/reference/base-abbreviations.mjs +2 -2
- package/dist/reference/base-operators.d.mts +2 -2
- package/dist/reference/base-operators.mjs +2 -2
- package/dist/reference/base-roman-labels.d.mts +4 -3
- package/dist/reference/base-roman-labels.mjs +4 -3
- package/dist/reference/citation-format.d.mts +35 -0
- package/dist/reference/citation-format.mjs +74 -0
- package/dist/reference/consistency.d.ts +5 -9
- package/dist/reference/consistency.js +34 -38
- package/dist/reference/curriculum-alignment.js +3 -24
- package/dist/reference/derived-entries.d.ts +45 -0
- package/dist/reference/derived-entries.js +50 -0
- package/dist/reference/derived-notation.d.mts +125 -0
- package/dist/reference/derived-notation.mjs +121 -0
- package/dist/reference/diagram-fit.d.ts +45 -0
- package/dist/reference/diagram-fit.js +54 -0
- package/dist/reference/diagram-layout.d.ts +59 -10
- package/dist/reference/diagram-layout.js +85 -24
- package/dist/reference/diagrams-validate.d.ts +1 -1
- package/dist/reference/equations-validate.d.ts +1 -1
- package/dist/reference/equations-validate.js +26 -1
- package/dist/reference/equations.d.mts +27 -0
- package/dist/reference/equations.mjs +120 -22
- package/dist/reference/examples-validate.d.ts +1 -1
- package/dist/reference/figure-fit.d.ts +70 -0
- package/dist/reference/figure-fit.js +60 -0
- package/dist/reference/figure-pictures.d.ts +27 -0
- package/dist/reference/figure-pictures.js +49 -0
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-abbreviations.js +1 -1
- package/dist/reference/gate-course-rules.d.ts +4 -3
- package/dist/reference/gate-course-rules.js +9 -4
- package/dist/reference/gate-heading-length.d.ts +2 -0
- package/dist/reference/gate-heading-length.js +7 -5
- package/dist/reference/gate-heading-structure.js +26 -0
- package/dist/reference/gate-indices.d.ts +35 -0
- package/dist/reference/gate-indices.js +299 -0
- package/dist/reference/gate-math.d.ts +32 -2
- package/dist/reference/gate-math.js +68 -12
- package/dist/reference/gate-mdx-expression.d.ts +17 -0
- package/dist/reference/gate-mdx-expression.js +29 -0
- package/dist/reference/gate-part-pages.d.ts +1 -1
- package/dist/reference/gate-prop-text.d.ts +28 -0
- package/dist/reference/gate-prop-text.js +106 -0
- package/dist/reference/gate-prose-notation.d.ts +3 -1
- package/dist/reference/gate-prose-notation.js +4 -2
- package/dist/reference/gate-retired-keys.d.ts +35 -0
- package/dist/reference/gate-retired-keys.js +58 -0
- package/dist/reference/gloss.d.ts +2 -0
- package/dist/reference/katex-measure.d.ts +17 -0
- package/dist/reference/katex-measure.js +223 -0
- package/dist/reference/keyed-float-validate.js +12 -0
- package/dist/reference/keyed-jsx-floats.d.mts +2 -2
- package/dist/reference/keyed-jsx-floats.mjs +11 -14
- package/dist/reference/lab-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-scope.js +13 -2
- package/dist/reference/math-glyphs.d.mts +94 -2
- package/dist/reference/math-glyphs.mjs +851 -46
- package/dist/reference/mixed-content.d.ts +8 -3
- package/dist/reference/mixed-content.js +4 -2
- package/dist/reference/page-notation.d.ts +34 -4
- package/dist/reference/page-notation.js +54 -2
- package/dist/reference/references.d.ts +11 -1
- package/dist/reference/references.js +6 -0
- package/dist/reference/registry.js +104 -10
- package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
- package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
- package/dist/reference/rehype-heading-anchors.mjs +11 -1
- package/dist/reference/remark-citation.mjs +0 -0
- package/dist/reference/remark-notation.mjs +84 -20
- package/dist/reference/render-lab-math.d.ts +2 -0
- package/dist/reference/render-lab-math.js +17 -5
- package/dist/reference/sections-validate.d.ts +28 -3
- package/dist/reference/sections-validate.js +31 -4
- package/dist/reference/sections.d.mts +24 -6
- package/dist/reference/sections.mjs +92 -17
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/tables-validate.js +24 -1
- package/dist/reference/text-measure.d.ts +10 -0
- package/dist/reference/text-measure.js +41 -0
- package/dist/reference/text-widths.d.ts +10 -0
- package/dist/reference/text-widths.js +1238 -0
- package/dist/reference/types.d.ts +64 -8
- package/dist/rules/catalog.js +191 -30
- package/dist/rules/in-effect.d.ts +1 -1
- package/dist/rules/rules.schema.json +79 -24
- package/dist/rules/settings.d.ts +1 -1
- package/dist/rules/settings.js +7 -0
- package/dist/session/PreferenceStore.d.ts +18 -1
- package/dist/session/PreferenceStore.js +29 -1
- package/docs/authoring/README.md +32 -21
- package/docs/authoring/agents.md +38 -7
- package/docs/authoring/components.md +111 -6
- package/docs/authoring/migrating.md +663 -0
- package/docs/authoring/notation.md +281 -29
- package/docs/authoring/retired.md +1 -0
- package/docs/authoring/structure-rules.md +10 -41
- package/docs/authoring/structure.md +53 -2
- package/docs/authoring/visualizations.md +144 -18
- package/package.json +12 -7
- package/src/compiler/astro.ts +57 -17
- package/src/compiler/cli.ts +143 -35
- package/src/compiler/collections.ts +92 -19
- package/src/compiler/content-schema.ts +108 -0
- package/src/compiler/content-schemas.ts +391 -0
- package/src/compiler/course-config.ts +86 -15
- package/src/compiler/exports/combined-html.ts +669 -0
- package/src/compiler/exports/download-index.ts +36 -0
- package/src/compiler/exports/export-chapters.ts +233 -0
- package/src/compiler/exports/export-pages.ts +44 -0
- package/src/compiler/exports/html-tree.ts +62 -0
- package/src/compiler/exports/latex/capture.ts +651 -0
- package/src/compiler/exports/latex/compile.ts +168 -0
- package/src/compiler/exports/latex/document.ts +479 -0
- package/src/compiler/exports/latex/explico.cls +415 -0
- package/src/compiler/exports/latex/numbering.ts +74 -0
- package/src/compiler/exports/latex/serialize.ts +787 -0
- package/src/compiler/exports/latex/texlive-packages.txt +76 -0
- package/src/compiler/exports/latex-export.ts +255 -0
- package/src/compiler/exports/markdown/serialize.ts +632 -0
- package/src/compiler/exports/markdown-export.ts +329 -0
- package/src/compiler/exports/standalone-html.ts +39 -70
- package/src/compiler/exports/static-tree.ts +944 -0
- package/src/compiler/exports/zip.ts +88 -0
- package/src/compiler/lesson-derivation.ts +13 -8
- package/src/compiler/manifest.ts +299 -28
- package/src/compiler/render-diagnostics.ts +111 -0
- package/src/components/HoverPanel.astro +204 -6
- package/src/components/PartOverview.astro +12 -9
- package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
- package/src/components/assessments/AssessmentRunner.tsx +7 -1
- package/src/components/assessments/AssessmentSet.astro +14 -1
- package/src/components/block-heading.ts +38 -0
- package/src/components/card-arrow.ts +18 -4
- package/src/components/card-carry.ts +406 -0
- package/src/components/card-drag.ts +1 -30
- package/src/components/card-fetch.ts +87 -0
- package/src/components/card-manager.ts +1222 -38
- package/src/components/card-memory.ts +5 -1
- package/src/components/card-placement.ts +211 -13
- package/src/components/card-side.ts +281 -31
- package/src/components/citation/CitationLayer.astro +45 -40
- package/src/components/diagram/Diagram.astro +123 -44
- package/src/components/figure/Figure.astro +11 -0
- package/src/components/figure/FloatCaption.astro +4 -17
- package/src/components/float-toolbar.ts +274 -0
- package/src/components/memo/Memo.astro +21 -10
- package/src/components/memo/ShortNote.astro +4 -0
- package/src/components/notation/NotationGlossary.astro +22 -17
- package/src/components/notation/NotationLayer.astro +300 -79
- package/src/components/notation/NotationMath.astro +8 -1
- package/src/components/notation/render-notation-math.ts +51 -5
- package/src/components/readable-text.ts +25 -0
- package/src/components/reference/FloatRefLayer.astro +162 -89
- package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
- package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
- package/src/components/starlight/LayoutPageSidebar.astro +17 -3
- package/src/components/starlight/LayoutSocialIcons.astro +79 -29
- package/src/components/starlight/LessonExportActions.astro +384 -310
- package/src/components/starlight/LessonFooter.astro +47 -9
- package/src/components/starlight/PrintController.astro +28 -0
- package/src/components/starlight/TextSizeControl.astro +137 -0
- package/src/components/starlight/print-controller.ts +380 -0
- package/src/components/starlight/section-summary.ts +13 -5
- package/src/components/table/Table.astro +13 -21
- package/src/components/table/table-truncation.ts +15 -0
- package/src/content-config.ts +20 -312
- package/src/curriculum/validation.ts +72 -24
- package/src/reference/base-abbreviations.mjs +2 -2
- package/src/reference/base-operators.mjs +2 -2
- package/src/reference/base-roman-labels.mjs +4 -3
- package/src/reference/citation-format.mjs +74 -0
- package/src/reference/consistency.ts +47 -61
- package/src/reference/curriculum-alignment.ts +4 -41
- package/src/reference/derived-entries.ts +68 -0
- package/src/reference/derived-notation.mjs +121 -0
- package/src/reference/diagram-fit.ts +104 -0
- package/src/reference/diagram-layout.ts +182 -51
- package/src/reference/diagrams-validate.ts +2 -1
- package/src/reference/equations-validate.ts +41 -2
- package/src/reference/equations.mjs +120 -22
- package/src/reference/examples-validate.ts +1 -0
- package/src/reference/figure-fit.ts +139 -0
- package/src/reference/figure-pictures.ts +96 -0
- package/src/reference/figures-validate.ts +2 -1
- package/src/reference/gate-abbreviations.ts +1 -1
- package/src/reference/gate-course-rules.ts +22 -7
- package/src/reference/gate-heading-length.ts +25 -7
- package/src/reference/gate-heading-structure.ts +40 -1
- package/src/reference/gate-indices.ts +470 -0
- package/src/reference/gate-math.ts +102 -13
- package/src/reference/gate-mdx-expression.ts +45 -0
- package/src/reference/gate-prop-text.ts +145 -0
- package/src/reference/gate-prose-notation.ts +19 -5
- package/src/reference/gate-retired-keys.ts +101 -0
- package/src/reference/gloss.ts +2 -0
- package/src/reference/katex-measure.ts +384 -0
- package/src/reference/keyed-float-validate.ts +22 -0
- package/src/reference/keyed-jsx-floats.mjs +11 -14
- package/src/reference/lab-math-scope.ts +15 -1
- package/src/reference/math-glyphs.mjs +851 -46
- package/src/reference/mixed-content.ts +11 -2
- package/src/reference/page-notation.ts +100 -3
- package/src/reference/references.ts +14 -0
- package/src/reference/registry.ts +154 -8
- package/src/reference/rehype-fail-katex-errors.mjs +19 -0
- package/src/reference/rehype-heading-anchors.mjs +11 -1
- package/src/reference/remark-citation.mjs +0 -0
- package/src/reference/remark-notation.mjs +84 -20
- package/src/reference/render-lab-math.ts +29 -5
- package/src/reference/sections-validate.ts +67 -3
- package/src/reference/sections.mjs +92 -17
- package/src/reference/tables-validate.ts +40 -2
- package/src/reference/text-measure.ts +63 -0
- package/src/reference/text-widths.ts +133 -0
- package/src/reference/types.ts +75 -8
- package/src/rules/catalog.ts +205 -29
- package/src/rules/settings.ts +10 -0
- package/src/session/PreferenceStore.ts +53 -0
- package/src/styles/global.css +291 -75
|
@@ -48,8 +48,8 @@ an independent diagram number, a `#dia-<id>` anchor, and references of the form
|
|
|
48
48
|
equation, table, or figure sequences.
|
|
49
49
|
|
|
50
50
|
A diagram is named like every float: a long `caption`, shown below the graph
|
|
51
|
-
in the same caption line a figure has (`Diagram 2.1.1`,
|
|
52
|
-
copies the diagram's link, the caption), and an optional `short` caption for
|
|
51
|
+
in the same caption line a figure has (`Diagram 2.1.1`, whose toolbar
|
|
52
|
+
copies the diagram's link, then the caption), and an optional `short` caption for
|
|
53
53
|
the `[[dia-…]]` preview card
|
|
54
54
|
([components.md § The caption pair](components.md#the-caption-pair)).
|
|
55
55
|
|
|
@@ -63,14 +63,16 @@ or Escape, restores its original width. A diagram never folds by itself: it
|
|
|
63
63
|
is its graph and then its caption line, in every state. To let a reader fold
|
|
64
64
|
it away, put it in a collapsible section.
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
Each node's box is sized from what it holds: its `text` measured in DejaVu
|
|
67
|
+
Sans, the widest common system font, and its `formula` measured from KaTeX's
|
|
68
|
+
own layout, so neither spills out of the box in any reader's font. A `text`
|
|
69
|
+
wider than about 190px wraps onto more lines and the box grows taller; a
|
|
70
|
+
`formula` never wraps, so a wide one widens its box and the text wraps to it.
|
|
71
|
+
Give a node a `width` to narrow it (its text wraps inside it and the height
|
|
72
|
+
follows) or a `height` to make it taller. A diagram that comes out wider than
|
|
73
|
+
the lesson column is [V8](#v8)'s concern.
|
|
72
74
|
|
|
73
|
-
_Enforcement._ Review-only
|
|
75
|
+
_Enforcement._ Review-only.
|
|
74
76
|
|
|
75
77
|
_Exceptions._ A one-off illustrative schematic that is not a graph (a labelled
|
|
76
78
|
photo, a geometric figure) is a [V2](#v2) figure, not a `Diagram`.
|
|
@@ -128,7 +130,10 @@ An interactive figure is a React island, hydrated `client:visible` (or
|
|
|
128
130
|
`labMathScope` argument; that is the declaration that ships those definitions
|
|
129
131
|
in this page's popup payload. See
|
|
130
132
|
`playground/content/labs/ProjectileRangeExplorer.tsx` +
|
|
131
|
-
`ProjectileRangeMath.astro`.
|
|
133
|
+
`ProjectileRangeMath.astro`. Pass the table `labMathScope` returned to
|
|
134
|
+
`renderLabMath` unchanged: the page's index entries reach the lab through
|
|
135
|
+
it, so `\mathbf{v}_0` gets its derived card ([N23](notation.md#n23)), and a copy
|
|
136
|
+
(`[...scope]`) gets neither the cards nor the index check.
|
|
132
137
|
- **Labelled controls.** Every control is a labelled `<input>` — the visible
|
|
133
138
|
label is how any reader knows what a slider sets; where precision
|
|
134
139
|
matters, pair a `range` slider with a `number` field (see `RangeField` in
|
|
@@ -166,11 +171,20 @@ An interactive figure is a React island, hydrated `client:visible` (or
|
|
|
166
171
|
the one thing to notice. The argument drawn from the figure is lesson
|
|
167
172
|
text, after the figure. Instructions for the controls belong to the
|
|
168
173
|
controls: the tools' first line, not the caption.
|
|
169
|
-
- **
|
|
170
|
-
|
|
171
|
-
plot
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
- **The build draws the default picture.** The island draws its plot in
|
|
175
|
+
the browser, after the page loads. So the lab's Astro wrapper draws the
|
|
176
|
+
same plot at the island's default values at build time and hands it to
|
|
177
|
+
the island as its children, which the island shows until it has drawn
|
|
178
|
+
its own. The built page then has the picture before hydration, with
|
|
179
|
+
JavaScript off, in print and in the standalone HTML export. With
|
|
180
|
+
Observable Plot that is one options function the island and the wrapper
|
|
181
|
+
share, and `Plot.plot({ ...options, document })` on a server-side
|
|
182
|
+
document in the wrapper; see `playground/content/labs/still-plot.ts`,
|
|
183
|
+
`LaunchAnglePicture.astro` and `LaunchAngleExplorer.tsx`.
|
|
184
|
+
- **A preview shows the plot.** A `[[fig-…]]` reference to a lab previews a
|
|
185
|
+
still copy of its plot, from this page or another: as drawn once the
|
|
186
|
+
island has run, else the build's default picture. The tools are never
|
|
187
|
+
copied, so a lab needs no `previewable={false}`.
|
|
174
188
|
|
|
175
189
|
The figure itself never hides; a lab that should fold away whole, with the
|
|
176
190
|
prose around it, goes in a `<Fold>` or a collapsible section.
|
|
@@ -198,9 +212,14 @@ every figure it rendered and fails a symbol KaTeX drew outside a notation
|
|
|
198
212
|
binding (`figure-symbols.ts`, rule [N4](notation.md#n4)); a course's browser
|
|
199
213
|
test runs the same `unboundFigureSymbols` on a hydrated figure's
|
|
200
214
|
`outerHTML`, which also holds what an island draws only in the browser, as
|
|
201
|
-
the playground's `labs.spec.ts` does.
|
|
202
|
-
|
|
203
|
-
|
|
215
|
+
the playground's `labs.spec.ts` does. The same build warns about a figure
|
|
216
|
+
whose picture is an island with nothing drawn in the built page
|
|
217
|
+
(`figure-pictures.ts`, `undrawnFigurePictures`, `lab-picture-undrawn`, a
|
|
218
|
+
warning). `renderLabMath` and a `<Diagram>` formula fail `astro build` on
|
|
219
|
+
an index no card explains (rule [N23](notation.md#n23)), as they render.
|
|
220
|
+
`setFigureState` takes no symbol typed as text, so `astro check` fails an
|
|
221
|
+
italic or a `sub` run. That a lab labels its controls and names
|
|
222
|
+
nothing of its own is review-only.
|
|
204
223
|
|
|
205
224
|
_Exceptions._ None.
|
|
206
225
|
|
|
@@ -364,3 +383,110 @@ _Exceptions._
|
|
|
364
383
|
are only two.
|
|
365
384
|
- A sketch that draws axes is a chart. If the axes are worth drawing, their
|
|
366
385
|
scale is worth labelling.
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## V7
|
|
390
|
+
|
|
391
|
+
**A figure’s picture takes at most `maxPercent` of the screen height.**
|
|
392
|
+
|
|
393
|
+
The picture is the default slot of a `<Figure>`: the plot, its key and any
|
|
394
|
+
data tables, without the caption line or the tools. On the screens the course
|
|
395
|
+
targets it stays within `maxPercent` (80% unless the course sets
|
|
396
|
+
`rules.V7.maxPercent`) of the viewport height. Then a reader who scrolls to the
|
|
397
|
+
figure sees the whole picture and its caption line at once, with a line of the
|
|
398
|
+
text that points to it.
|
|
399
|
+
|
|
400
|
+
- **A square or tall plot gets a width, not the column.** A chart drawn with
|
|
401
|
+
`aspectRatio: 1` (Observable Plot) or a height derived from the container
|
|
402
|
+
width grows with the column: a square in a 760px column is 760px tall, almost
|
|
403
|
+
all of a laptop screen. Give such a plot a fixed width (`width: 460`,
|
|
404
|
+
`aspectRatio: 1`) and centre it, or cap it with
|
|
405
|
+
`Math.min(containerWidth, 460)`.
|
|
406
|
+
- **Stacked panels share the budget.** Two panels of 300px each are one 600px
|
|
407
|
+
picture. Shorten both, put them side by side on a wide screen, or keep the
|
|
408
|
+
secondary one in the tools.
|
|
409
|
+
- **Measure, don't estimate.** Font size, the column width and a lab's
|
|
410
|
+
hydrated layout all change the height, so judge it from the rendered page.
|
|
411
|
+
|
|
412
|
+
_Rationale._ A figure is read with its caption and with the sentence that
|
|
413
|
+
sends the reader to it. A picture taller than the screen has to be scrolled
|
|
414
|
+
past to reach its caption, and a control in the tools that redraws it can no
|
|
415
|
+
longer be watched while it is moved.
|
|
416
|
+
|
|
417
|
+
_How to apply._ In the course’s Playwright suite, open every lesson that has
|
|
418
|
+
a figure, at each viewport the course supports, and expect no findings:
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
import { readFileSync } from 'node:fs';
|
|
422
|
+
import { expect, test } from '@playwright/test';
|
|
423
|
+
import { checkFigureFit, lessonsWithFigures } from 'explico/reference/figure-fit';
|
|
424
|
+
|
|
425
|
+
const manifest = JSON.parse(readFileSync('build/manifest.json', 'utf8'));
|
|
426
|
+
|
|
427
|
+
for (const path of lessonsWithFigures(manifest)) {
|
|
428
|
+
test(`${path} figures fit on one screen (rule V7)`, async ({ page }) => {
|
|
429
|
+
await page.setViewportSize({ width: 1280, height: 800 });
|
|
430
|
+
await page.goto(path);
|
|
431
|
+
const findings = await checkFigureFit(page);
|
|
432
|
+
expect(findings.map((finding) => finding.message)).toEqual([]);
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Pass `{ maxPercent }` when the course sets `rules.V7.maxPercent`. The check
|
|
438
|
+
opens a figure inside a closed `<Fold>` before it measures, because a reader
|
|
439
|
+
can open it, and waits for each figure’s islands to hydrate and its height to
|
|
440
|
+
settle.
|
|
441
|
+
|
|
442
|
+
_Enforcement._ Partial. `checkFigureFit` in `src/reference/figure-fit.ts`
|
|
443
|
+
reports `figure-too-tall` for each picture over the budget, on the pages and
|
|
444
|
+
viewports a course’s browser tests open; the build cannot measure a laid-out
|
|
445
|
+
page.
|
|
446
|
+
|
|
447
|
+
_Exceptions._ A figure whose picture is itself the subject at full size — a
|
|
448
|
+
long table of values, a tall timeline the reader is meant to scroll — may be
|
|
449
|
+
exempted in the test with a stated reason,
|
|
450
|
+
`checkFigureFit(page, { allow: { 'fig-timeline': 'read by scrolling, one row per year' } })`,
|
|
451
|
+
or kept in a `<Fold>` with a
|
|
452
|
+
`<Table>` instead of a figure.
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## V8
|
|
457
|
+
|
|
458
|
+
**A diagram is no wider than the lesson column.**
|
|
459
|
+
|
|
460
|
+
A `<Diagram>` lays out to at most `columnWidth` px (707 unless the course sets
|
|
461
|
+
`rules.V8.columnWidth`): the lesson column at a 1366px-wide window, the most
|
|
462
|
+
common laptop screen (the column is 632px at 1280 and 832px at 1920). Then a
|
|
463
|
+
reader sees the whole graph where the text sends them to it, without scrolling
|
|
464
|
+
it sideways or pressing its widen button ↔.
|
|
465
|
+
|
|
466
|
+
The engine does the first part itself. A diagram that comes out wider than
|
|
467
|
+
its limit is laid out again with its labels wrapped narrower, down to 120px
|
|
468
|
+
boxes, and only one that still does not fit is reported. Then:
|
|
469
|
+
|
|
470
|
+
- **Turn a long chain.** A left-to-right chain of five or more nodes rarely
|
|
471
|
+
fits; `direction="TB"` stacks it.
|
|
472
|
+
- **Shorten a label or a formula.** A node names a step; the explanation goes
|
|
473
|
+
in the prose or the caption. A formula never wraps, so a long one sets its
|
|
474
|
+
box's width.
|
|
475
|
+
- **Narrow a node.** A node's `width` makes its text wrap inside it, and its
|
|
476
|
+
height follows.
|
|
477
|
+
|
|
478
|
+
_Rationale._ A diagram is a picture of how steps connect. One that scrolls
|
|
479
|
+
shows only some of them at a time, and the reader has to remember the rest.
|
|
480
|
+
|
|
481
|
+
_How to apply._ Build the course. Each diagram wider than its limit is a
|
|
482
|
+
`dia-too-wide` warning naming the lesson file and the diagram `id`, printed by
|
|
483
|
+
`astro build` (and by `astro dev` as the page renders) and listed by
|
|
484
|
+
`pnpm content warnings` until the next build. A diagram meant to be read by
|
|
485
|
+
scrolling — a long timeline — says `warnAboveWidth={false}`; a number in
|
|
486
|
+
`warnAboveWidth` sets that one diagram's limit instead of the column.
|
|
487
|
+
|
|
488
|
+
_Enforcement._ Tier-3 warning `dia-too-wide`, for every rendered diagram:
|
|
489
|
+
`Diagram.astro` marks one wider than its limit, `oversizedDiagrams` in
|
|
490
|
+
`src/reference/diagram-fit.ts` finds the mark on each page `astro build`
|
|
491
|
+
writes, and `build/render-diagnostics.json` keeps the findings for
|
|
492
|
+
`content warnings`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "explico",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Build-time engine for prerequisite-aware, notation-rigorous course content: one validated content model, a semantic notation/citation/equation system with a completeness gate, and a deterministic JSON manifest.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -36,13 +36,17 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@dagrejs/dagre": "3.1.1",
|
|
38
38
|
"esbuild": "0.27.7",
|
|
39
|
-
"
|
|
39
|
+
"mdast-util-gfm-table": "2.0.0",
|
|
40
|
+
"mdast-util-math": "3.0.0",
|
|
41
|
+
"mdast-util-to-markdown": "2.1.2",
|
|
42
|
+
"parse5": "7.3.0",
|
|
43
|
+
"remark-directive": "^4.0.0"
|
|
40
44
|
},
|
|
41
45
|
"peerDependencies": {
|
|
42
|
-
"@astrojs/markdown-remark": "7.
|
|
43
|
-
"@astrojs/react": "
|
|
44
|
-
"@astrojs/starlight": "0.
|
|
45
|
-
"astro": "7.
|
|
46
|
+
"@astrojs/markdown-remark": "7.3.1",
|
|
47
|
+
"@astrojs/react": "7.0.0",
|
|
48
|
+
"@astrojs/starlight": "0.42.4",
|
|
49
|
+
"astro": "7.3.5",
|
|
46
50
|
"katex": "0.16.47",
|
|
47
51
|
"react": "19.2.8",
|
|
48
52
|
"react-dom": "19.2.8",
|
|
@@ -51,7 +55,8 @@
|
|
|
51
55
|
"remark-mdx": "3.1.1"
|
|
52
56
|
},
|
|
53
57
|
"devDependencies": {
|
|
54
|
-
"@astrojs/markdown-remark": "7.
|
|
58
|
+
"@astrojs/markdown-remark": "7.3.1",
|
|
59
|
+
"@types/mdast": "4.0.4",
|
|
55
60
|
"@types/node": "24.13.3",
|
|
56
61
|
"@types/react": "19.2.18",
|
|
57
62
|
"@types/react-dom": "19.2.5",
|
package/src/compiler/astro.ts
CHANGED
|
@@ -13,7 +13,19 @@ import {
|
|
|
13
13
|
} from './manifest';
|
|
14
14
|
import { missingNotationPayloadKeys } from '../reference/page-notation';
|
|
15
15
|
import { assertFigureSymbolsBound } from '../reference/figure-symbols';
|
|
16
|
+
import { undrawnFigurePictures, undrawnFigureWarning } from '../reference/figure-pictures';
|
|
17
|
+
import {
|
|
18
|
+
pageRenderDiagnostics,
|
|
19
|
+
type RenderDiagnostic,
|
|
20
|
+
writeRenderDiagnostics,
|
|
21
|
+
} from './render-diagnostics';
|
|
16
22
|
import { writeStandaloneLessonExports } from './exports/standalone-html';
|
|
23
|
+
import { writeCombinedExports } from './exports/combined-html';
|
|
24
|
+
import { writeLatexExports } from './exports/latex-export';
|
|
25
|
+
import { writeMarkdownExports } from './exports/markdown-export';
|
|
26
|
+
import { prepareChapters } from './exports/export-chapters';
|
|
27
|
+
import { writeDownloadIndex } from './exports/download-index';
|
|
28
|
+
import { standaloneFilename, standalonePageSlugs } from './exports/export-pages';
|
|
17
29
|
import remarkAbbreviations from '../reference/remark-abbreviations.mjs';
|
|
18
30
|
import remarkCitation from '../reference/remark-citation.mjs';
|
|
19
31
|
import remarkNotation from '../reference/remark-notation.mjs';
|
|
@@ -27,21 +39,21 @@ function normalizedBasePath(base: string): string {
|
|
|
27
39
|
return segment ? `/${segment}/` : '/';
|
|
28
40
|
}
|
|
29
41
|
|
|
30
|
-
/** Match only a configured
|
|
42
|
+
/** Match only a configured page artifact below Astro's deployment base. */
|
|
31
43
|
export function matchStandaloneDownload(
|
|
32
44
|
pathname: string,
|
|
33
45
|
base: string,
|
|
34
|
-
|
|
46
|
+
pageSlugs: readonly string[],
|
|
35
47
|
): string | undefined {
|
|
36
48
|
const prefix = `${normalizedBasePath(base)}downloads/`;
|
|
37
49
|
if (!pathname.startsWith(prefix) || !pathname.endsWith('.html')) return undefined;
|
|
38
50
|
const slug = pathname.slice(prefix.length, -'.html'.length);
|
|
39
|
-
return
|
|
51
|
+
return pageSlugs.includes(slug) ? slug : undefined;
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
async function buildDevelopmentDownloads(
|
|
43
55
|
config: Pick<AstroConfig, 'root'>,
|
|
44
|
-
|
|
56
|
+
pageSlugs: readonly string[],
|
|
45
57
|
): Promise<ReadonlyMap<string, Buffer>> {
|
|
46
58
|
const temporaryRoot = await mkdtemp(join(tmpdir(), 'explico-standalone-'));
|
|
47
59
|
const outputRoot = join(temporaryRoot, 'dist');
|
|
@@ -89,7 +101,7 @@ async function buildDevelopmentDownloads(
|
|
|
89
101
|
});
|
|
90
102
|
return new Map(
|
|
91
103
|
await Promise.all(
|
|
92
|
-
|
|
104
|
+
pageSlugs.map(async (slug) => [
|
|
93
105
|
slug,
|
|
94
106
|
await readFile(join(outputRoot, 'downloads', `${slug}.html`)),
|
|
95
107
|
] as const),
|
|
@@ -229,8 +241,17 @@ export function assertRemarkPluginsWired(
|
|
|
229
241
|
);
|
|
230
242
|
}
|
|
231
243
|
|
|
232
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Check every built page, and return the findings only a rendered page gives
|
|
246
|
+
* (`render-diagnostics.ts`) for `content warnings`.
|
|
247
|
+
*/
|
|
248
|
+
async function validateGeneratedPages(
|
|
249
|
+
directory: URL,
|
|
250
|
+
manifest: Manifest,
|
|
251
|
+
warn: (message: string) => void,
|
|
252
|
+
): Promise<RenderDiagnostic[]> {
|
|
233
253
|
const root = fileURLToPath(directory);
|
|
254
|
+
const rendered: RenderDiagnostic[] = [];
|
|
234
255
|
const walk = async (path: string): Promise<void> => {
|
|
235
256
|
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
236
257
|
const child = join(path, entry.name);
|
|
@@ -238,12 +259,17 @@ async function validateGeneratedNotationPayloads(directory: URL): Promise<void>
|
|
|
238
259
|
await walk(child);
|
|
239
260
|
} else if (entry.isFile() && entry.name.endsWith('.html')) {
|
|
240
261
|
const html = await readFile(child, 'utf8');
|
|
241
|
-
|
|
242
|
-
|
|
262
|
+
const page = relative(root, child);
|
|
263
|
+
assertNotationPayloadHtml(page, html);
|
|
264
|
+
assertFigureSymbolsBound(page, html);
|
|
265
|
+
const undrawn = undrawnFigurePictures(html);
|
|
266
|
+
if (undrawn.length > 0) warn(undrawnFigureWarning(page, undrawn));
|
|
267
|
+
rendered.push(...pageRenderDiagnostics(manifest, page, html));
|
|
243
268
|
}
|
|
244
269
|
}
|
|
245
270
|
};
|
|
246
271
|
await walk(root);
|
|
272
|
+
return rendered;
|
|
247
273
|
}
|
|
248
274
|
|
|
249
275
|
function replaceInPlace(target: unknown, source: unknown): void {
|
|
@@ -381,19 +407,19 @@ export function explicoContentRefresh(
|
|
|
381
407
|
const slug = matchStandaloneDownload(
|
|
382
408
|
pathname,
|
|
383
409
|
configuredDevelopment.base,
|
|
384
|
-
manifest
|
|
410
|
+
standalonePageSlugs(manifest),
|
|
385
411
|
);
|
|
386
412
|
if (!slug) return next();
|
|
387
413
|
|
|
388
414
|
try {
|
|
389
415
|
logger.info(
|
|
390
416
|
developmentDownloads
|
|
391
|
-
? `Serving cached standalone
|
|
392
|
-
: 'Building standalone
|
|
417
|
+
? `Serving cached standalone page ${slug}.html`
|
|
418
|
+
: 'Building standalone page downloads for local development…',
|
|
393
419
|
);
|
|
394
420
|
developmentDownloads ??= buildDevelopmentDownloads(
|
|
395
421
|
configuredDevelopment,
|
|
396
|
-
manifest
|
|
422
|
+
standalonePageSlugs(manifest),
|
|
397
423
|
);
|
|
398
424
|
const currentDownloads = developmentDownloads;
|
|
399
425
|
currentDownloads.catch(() => {
|
|
@@ -403,7 +429,7 @@ export function explicoContentRefresh(
|
|
|
403
429
|
});
|
|
404
430
|
const artifact = (await currentDownloads).get(slug);
|
|
405
431
|
if (!artifact) return next();
|
|
406
|
-
const filename =
|
|
432
|
+
const filename = standaloneFilename(slug);
|
|
407
433
|
response.statusCode = 200;
|
|
408
434
|
response.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
409
435
|
response.setHeader('Content-Length', artifact.byteLength);
|
|
@@ -415,17 +441,31 @@ export function explicoContentRefresh(
|
|
|
415
441
|
response.end(request.method === 'HEAD' ? undefined : artifact);
|
|
416
442
|
} catch (error) {
|
|
417
443
|
const message = error instanceof Error ? error.message : String(error);
|
|
418
|
-
logger.error(`Could not build standalone
|
|
444
|
+
logger.error(`Could not build standalone page downloads: ${message}`);
|
|
419
445
|
response.statusCode = 500;
|
|
420
446
|
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
421
|
-
response.end(`Could not build standalone
|
|
447
|
+
response.end(`Could not build standalone page download.\n${message}\n`);
|
|
422
448
|
}
|
|
423
449
|
});
|
|
424
450
|
}
|
|
425
451
|
},
|
|
426
|
-
'astro:build:done': async ({ dir }) => {
|
|
427
|
-
await
|
|
452
|
+
'astro:build:done': async ({ dir, logger }) => {
|
|
453
|
+
const rendered = await validateGeneratedPages(dir, manifest, (message) =>
|
|
454
|
+
logger.warn(message),
|
|
455
|
+
);
|
|
456
|
+
for (const diagnostic of writeRenderDiagnostics(rendered)) {
|
|
457
|
+
logger.warn(`[${diagnostic.code}] ${diagnostic.file}: ${diagnostic.message}`);
|
|
458
|
+
}
|
|
428
459
|
await writeStandaloneLessonExports(dir, manifest);
|
|
460
|
+
await writeCombinedExports(dir, manifest);
|
|
461
|
+
// The print exports share one reading of the chapters and one
|
|
462
|
+
// capture of their figures.
|
|
463
|
+
const { latex, markdown } = manifest.lessonExports;
|
|
464
|
+
const prepared =
|
|
465
|
+
latex || markdown ? await prepareChapters(fileURLToPath(dir), manifest, { logger }) : undefined;
|
|
466
|
+
await writeLatexExports(dir, manifest, { logger, ...(prepared ? { prepared } : {}) });
|
|
467
|
+
await writeMarkdownExports(dir, manifest, { logger, ...(prepared ? { prepared } : {}) });
|
|
468
|
+
await writeDownloadIndex(dir);
|
|
429
469
|
},
|
|
430
470
|
},
|
|
431
471
|
};
|