explico 0.12.0 → 0.13.1
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 +1137 -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 +707 -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 +321 -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
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The toolbar on a float's number. Hovering or focusing the number of an
|
|
3
|
+
* equation, a figure, a table or a diagram shows Pin and Dock just above the
|
|
4
|
+
* number and Copy link just below it, none of them over the number. Pin and
|
|
5
|
+
* Dock open the float's own reference card already kept, hung from the
|
|
6
|
+
* number (`card-manager.ts`, `keepCardFor`): pinned next to the float, or
|
|
7
|
+
* docked beside the article. Copy link is a `data-copy-link` link, so
|
|
8
|
+
* `HeadingAnchorEnhancer.astro` copies it, names it and confirms it, as it
|
|
9
|
+
* did for the chain icon this replaced, and the click still puts the anchor
|
|
10
|
+
* in the address bar. A click on the number itself only does that.
|
|
11
|
+
*
|
|
12
|
+
* There is one toolbar, moved next to the number in use, so Tab runs from
|
|
13
|
+
* the number through Pin, Dock and Copy link. A screen with no hover gets
|
|
14
|
+
* none: no pinning or docking there, and a long press on the number already
|
|
15
|
+
* offers the browser's own "Copy link".
|
|
16
|
+
*
|
|
17
|
+
* An equation's Copy LaTeX button is its own (`remark-notation.mjs`) and
|
|
18
|
+
* sits on the bottom border of the equation, which this module measures:
|
|
19
|
+
* under the number when the link button fits between the two, else slid
|
|
20
|
+
* along the border, clear of the number (`--eq-copy-inline-end`,
|
|
21
|
+
* `data-eq-compact`). The geometry is `copyLatexPlace`, pure, so
|
|
22
|
+
* `tests/unit/float-toolbar.test.ts` checks it in Node.
|
|
23
|
+
*/
|
|
24
|
+
import { keepCardFor } from './card-manager';
|
|
25
|
+
|
|
26
|
+
/** A float's own number: an equation's, or a figure's, table's or diagram's caption number. */
|
|
27
|
+
export const FLOAT_NUMBER = 'a.keyed-equation__number, a.float-caption__number';
|
|
28
|
+
|
|
29
|
+
/** Between the number and a button, in px. */
|
|
30
|
+
const GAP = 3;
|
|
31
|
+
const HIDE_DELAY_MS = 250;
|
|
32
|
+
|
|
33
|
+
const PIN_ICON =
|
|
34
|
+
'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M6 1.75h4v4.25l2.25 2.75h-8.5L6 6z"/><path d="M8 8.75V14.5"/></svg>';
|
|
35
|
+
const DOCK_ICON =
|
|
36
|
+
'<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="1.75" y="2.75" width="12.5" height="10.5" rx="1.5"/><path d="M10.25 2.75v10.5"/></svg>';
|
|
37
|
+
const LINK_ICON =
|
|
38
|
+
'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"/></svg>';
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// The geometry, pure.
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
export interface CopyLatexInput {
|
|
45
|
+
/** The equation's box: its inline-end edge and its bottom, in px along each axis. */
|
|
46
|
+
readonly blockEnd: number;
|
|
47
|
+
readonly blockBottom: number;
|
|
48
|
+
/** The number's box. */
|
|
49
|
+
readonly numberStart: number;
|
|
50
|
+
readonly numberEnd: number;
|
|
51
|
+
readonly numberBottom: number;
|
|
52
|
+
/** The buttons' size, and the gap between the number and a button. */
|
|
53
|
+
readonly size: number;
|
|
54
|
+
readonly gap: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CopyLatexPlace {
|
|
58
|
+
/** From the block's inline-end edge to the button's. */
|
|
59
|
+
readonly inlineEnd: number;
|
|
60
|
+
/** Too short for the link button between the number and the border. */
|
|
61
|
+
readonly compact: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Where an equation's Copy LaTeX button sits on its bottom border. Its
|
|
66
|
+
* centre is on the border. Under the number when the number's Copy link
|
|
67
|
+
* button (right below the number) fits above it; otherwise just past the
|
|
68
|
+
* number's inline-start edge, so neither button covers the number or the
|
|
69
|
+
* other. Coordinates run along the inline axis from the block's start, so
|
|
70
|
+
* the same arithmetic serves a right-to-left page.
|
|
71
|
+
*/
|
|
72
|
+
export function copyLatexPlace(input: CopyLatexInput): CopyLatexPlace {
|
|
73
|
+
const { blockEnd, blockBottom, numberStart, numberEnd, numberBottom, size, gap } = input;
|
|
74
|
+
const linkBottom = numberBottom + gap + size + gap;
|
|
75
|
+
const copyTop = blockBottom - size / 2;
|
|
76
|
+
const compact = linkBottom > copyTop;
|
|
77
|
+
const numberCentre = (numberStart + numberEnd) / 2;
|
|
78
|
+
const inlineEnd = compact
|
|
79
|
+
? blockEnd - numberStart + gap
|
|
80
|
+
: blockEnd - numberCentre - size / 2;
|
|
81
|
+
return { inlineEnd: Math.max(0, Math.round(inlineEnd)), compact };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// The DOM layer.
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
interface Toolbar {
|
|
89
|
+
readonly root: HTMLElement;
|
|
90
|
+
readonly above: HTMLElement;
|
|
91
|
+
readonly pin: HTMLButtonElement;
|
|
92
|
+
readonly dock: HTMLButtonElement;
|
|
93
|
+
readonly link: HTMLAnchorElement;
|
|
94
|
+
number: HTMLAnchorElement | null;
|
|
95
|
+
hideTimer: number | null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function button(icon: string, label: string, title: string): HTMLButtonElement {
|
|
99
|
+
const el = document.createElement('button');
|
|
100
|
+
el.type = 'button';
|
|
101
|
+
el.className = 'float-toolbar__button';
|
|
102
|
+
el.innerHTML = icon;
|
|
103
|
+
el.setAttribute('aria-label', label);
|
|
104
|
+
el.title = title;
|
|
105
|
+
return el;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function build(): Toolbar {
|
|
109
|
+
const root = document.createElement('span');
|
|
110
|
+
// `not-content`: Starlight's article styles (the flow margin between
|
|
111
|
+
// siblings) stay off the buttons.
|
|
112
|
+
root.className = 'float-toolbar not-content';
|
|
113
|
+
root.hidden = true;
|
|
114
|
+
const above = document.createElement('span');
|
|
115
|
+
above.className = 'float-toolbar__above';
|
|
116
|
+
const pin = button(PIN_ICON, 'Pin', 'Keep this card open');
|
|
117
|
+
pin.dataset.floatPin = '';
|
|
118
|
+
const dock = button(DOCK_ICON, 'Beside the article', 'Keep this card beside the article');
|
|
119
|
+
dock.dataset.floatDock = '';
|
|
120
|
+
const link = document.createElement('a');
|
|
121
|
+
link.className = 'float-toolbar__link';
|
|
122
|
+
link.innerHTML = LINK_ICON;
|
|
123
|
+
link.title = 'Copy link';
|
|
124
|
+
above.append(pin, dock);
|
|
125
|
+
root.append(above, link);
|
|
126
|
+
return { root, above, pin, dock, link, number: null, hideTimer: null };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** "Equation (2.1.1)", "Figure 2.1.1": what Copy link names the float. */
|
|
130
|
+
function nameOf(number: HTMLElement): string {
|
|
131
|
+
const text = (number.textContent ?? '').trim();
|
|
132
|
+
return number.matches('a.keyed-equation__number') ? `Equation ${text}` : text;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Each part against the number, in the coordinates of the box they are placed in. */
|
|
136
|
+
function place(toolbar: Toolbar, number: HTMLElement): void {
|
|
137
|
+
const parent = (toolbar.above.offsetParent as HTMLElement | null) ?? document.body;
|
|
138
|
+
const box = parent.getBoundingClientRect();
|
|
139
|
+
const n = number.getBoundingClientRect();
|
|
140
|
+
const centre = n.left + n.width / 2 - box.left - parent.clientLeft + parent.scrollLeft;
|
|
141
|
+
const top = n.top - box.top - parent.clientTop + parent.scrollTop;
|
|
142
|
+
toolbar.above.style.left = `${Math.round(centre - toolbar.above.offsetWidth / 2)}px`;
|
|
143
|
+
toolbar.above.style.top = `${Math.round(top - toolbar.above.offsetHeight - GAP)}px`;
|
|
144
|
+
toolbar.link.style.left = `${Math.round(centre - toolbar.link.offsetWidth / 2)}px`;
|
|
145
|
+
toolbar.link.style.top = `${Math.round(top + n.height + GAP)}px`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function layoutEquation(equation: HTMLElement): void {
|
|
149
|
+
const number = equation.querySelector<HTMLElement>('a.keyed-equation__number');
|
|
150
|
+
const copy = equation.querySelector<HTMLElement>('.keyed-equation__copy');
|
|
151
|
+
if (!number || !copy) return;
|
|
152
|
+
const e = equation.getBoundingClientRect();
|
|
153
|
+
const n = number.getBoundingClientRect();
|
|
154
|
+
if (e.height === 0 || n.width === 0) return;
|
|
155
|
+
const rtl = getComputedStyle(equation).direction === 'rtl';
|
|
156
|
+
// Along the inline axis, from the block's start edge.
|
|
157
|
+
const along = (x: number) => (rtl ? e.right - x : x - e.left);
|
|
158
|
+
const { inlineEnd, compact } = copyLatexPlace({
|
|
159
|
+
blockEnd: e.width,
|
|
160
|
+
blockBottom: e.height,
|
|
161
|
+
numberStart: Math.min(along(n.left), along(n.right)),
|
|
162
|
+
numberEnd: Math.max(along(n.left), along(n.right)),
|
|
163
|
+
numberBottom: n.bottom - e.top,
|
|
164
|
+
size: copy.offsetWidth || 22,
|
|
165
|
+
gap: GAP,
|
|
166
|
+
});
|
|
167
|
+
equation.style.setProperty('--eq-copy-inline-end', `${inlineEnd}px`);
|
|
168
|
+
equation.toggleAttribute('data-eq-compact', compact);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Place every equation's Copy LaTeX button, and again whenever an equation changes size. */
|
|
172
|
+
function watchEquations(root: ParentNode): void {
|
|
173
|
+
const equations = Array.from(root.querySelectorAll<HTMLElement>('.keyed-equation'));
|
|
174
|
+
if (equations.length === 0) return;
|
|
175
|
+
const observer = new ResizeObserver((entries) => {
|
|
176
|
+
for (const entry of entries) layoutEquation(entry.target as HTMLElement);
|
|
177
|
+
});
|
|
178
|
+
for (const equation of equations) {
|
|
179
|
+
layoutEquation(equation);
|
|
180
|
+
observer.observe(equation);
|
|
181
|
+
}
|
|
182
|
+
document.fonts?.ready.then(() => equations.forEach(layoutEquation), () => {});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Wire the toolbar for every float number in `root`, the article. */
|
|
186
|
+
export function startFloatToolbar(root: HTMLElement): void {
|
|
187
|
+
watchEquations(root);
|
|
188
|
+
|
|
189
|
+
const canHover = window.matchMedia('(hover: hover)');
|
|
190
|
+
const toolbar = build();
|
|
191
|
+
|
|
192
|
+
const numberFrom = (node: EventTarget | null): HTMLAnchorElement | null => {
|
|
193
|
+
if (!(node instanceof Element)) return null;
|
|
194
|
+
const number = node.closest<HTMLAnchorElement>(FLOAT_NUMBER);
|
|
195
|
+
return number && root.contains(number) ? number : null;
|
|
196
|
+
};
|
|
197
|
+
const inToolbar = (node: EventTarget | null) =>
|
|
198
|
+
node instanceof Node && toolbar.root.contains(node);
|
|
199
|
+
const keeps = (node: EventTarget | null) =>
|
|
200
|
+
inToolbar(node) || (node instanceof Node && !!toolbar.number?.contains(node));
|
|
201
|
+
|
|
202
|
+
const cancelHide = () => {
|
|
203
|
+
if (toolbar.hideTimer !== null) window.clearTimeout(toolbar.hideTimer);
|
|
204
|
+
toolbar.hideTimer = null;
|
|
205
|
+
};
|
|
206
|
+
const hide = () => {
|
|
207
|
+
cancelHide();
|
|
208
|
+
toolbar.root.hidden = true;
|
|
209
|
+
};
|
|
210
|
+
const scheduleHide = () => {
|
|
211
|
+
cancelHide();
|
|
212
|
+
toolbar.hideTimer = window.setTimeout(hide, HIDE_DELAY_MS);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const show = (number: HTMLAnchorElement) => {
|
|
216
|
+
cancelHide();
|
|
217
|
+
if (toolbar.number !== number || !toolbar.root.isConnected) {
|
|
218
|
+
const name = nameOf(number);
|
|
219
|
+
number.after(toolbar.root);
|
|
220
|
+
toolbar.number = number;
|
|
221
|
+
toolbar.link.href = number.getAttribute('href') ?? '';
|
|
222
|
+
toolbar.link.dataset.copyLink = name;
|
|
223
|
+
toolbar.link.setAttribute('aria-label', `Copy the link to ${name}`);
|
|
224
|
+
toolbar.pin.setAttribute('aria-label', `Pin ${name}`);
|
|
225
|
+
toolbar.dock.setAttribute('aria-label', `Keep ${name} beside the article`);
|
|
226
|
+
}
|
|
227
|
+
toolbar.root.hidden = false;
|
|
228
|
+
place(toolbar, number);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
root.addEventListener('pointerover', (event) => {
|
|
232
|
+
if (event.pointerType === 'touch' || !canHover.matches) return;
|
|
233
|
+
const number = numberFrom(event.target);
|
|
234
|
+
if (number) show(number);
|
|
235
|
+
else if (inToolbar(event.target)) cancelHide();
|
|
236
|
+
});
|
|
237
|
+
root.addEventListener('pointerout', (event) => {
|
|
238
|
+
if (toolbar.root.hidden || !keeps(event.target)) return;
|
|
239
|
+
if (keeps(event.relatedTarget)) return;
|
|
240
|
+
scheduleHide();
|
|
241
|
+
});
|
|
242
|
+
// Escape gives focus back to the number without showing the toolbar again.
|
|
243
|
+
let quietFocus = false;
|
|
244
|
+
root.addEventListener('focusin', (event) => {
|
|
245
|
+
if (quietFocus) return;
|
|
246
|
+
const number = numberFrom(event.target);
|
|
247
|
+
if (number && canHover.matches && number.matches(':focus-visible')) show(number);
|
|
248
|
+
else if (inToolbar(event.target)) cancelHide();
|
|
249
|
+
});
|
|
250
|
+
root.addEventListener('focusout', (event) => {
|
|
251
|
+
if (toolbar.root.hidden || keeps(event.relatedTarget)) return;
|
|
252
|
+
scheduleHide();
|
|
253
|
+
});
|
|
254
|
+
root.addEventListener('keydown', (event) => {
|
|
255
|
+
if (event.key !== 'Escape' || toolbar.root.hidden) return;
|
|
256
|
+
const inside = inToolbar(document.activeElement);
|
|
257
|
+
hide();
|
|
258
|
+
if (inside && toolbar.number) {
|
|
259
|
+
quietFocus = true;
|
|
260
|
+
toolbar.number.focus();
|
|
261
|
+
quietFocus = false;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const keep = (mode: 'pinned' | 'side') => {
|
|
266
|
+
const number = toolbar.number;
|
|
267
|
+
hide();
|
|
268
|
+
if (number) keepCardFor('reference', number, mode);
|
|
269
|
+
};
|
|
270
|
+
toolbar.pin.addEventListener('click', () => keep('pinned'));
|
|
271
|
+
toolbar.dock.addEventListener('click', () => keep('side'));
|
|
272
|
+
// Copy link copies and navigates through `HeadingAnchorEnhancer.astro`.
|
|
273
|
+
toolbar.link.addEventListener('click', () => window.setTimeout(hide, 0));
|
|
274
|
+
}
|
|
@@ -358,16 +358,10 @@ const name = `Memo ${number}: ${short ?? caption}`;
|
|
|
358
358
|
|
|
359
359
|
// The marker opens the memo where it is and goes there, and closes
|
|
360
360
|
// it again: the section's own summary does the folding, so the
|
|
361
|
-
// animation, the peek and the saved choice stay the section's.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if (!details || !summary) return;
|
|
366
|
-
event.preventDefault();
|
|
367
|
-
const opening = !isOpen();
|
|
368
|
-
summary.click();
|
|
369
|
-
if (!opening) return;
|
|
370
|
-
|
|
361
|
+
// animation, the peek and the saved choice stay the section's. An
|
|
362
|
+
// open memo the reader cannot see is gone to, not folded: closing it
|
|
363
|
+
// off-screen would look as if the click did nothing.
|
|
364
|
+
const goTo = () => {
|
|
371
365
|
closeHoverPeek(marker);
|
|
372
366
|
if (location.hash !== `#${heading.id}`) {
|
|
373
367
|
history.pushState(null, '', `#${heading.id}`);
|
|
@@ -375,6 +369,23 @@ const name = `Memo ${number}: ${short ?? caption}`;
|
|
|
375
369
|
requestAnimationFrame(() => {
|
|
376
370
|
animatedScrollIntoView(frame, { block: 'start' });
|
|
377
371
|
});
|
|
372
|
+
};
|
|
373
|
+
const onScreen = () => {
|
|
374
|
+
const box = frame.getBoundingClientRect();
|
|
375
|
+
return box.bottom > 0 && box.top < window.innerHeight;
|
|
376
|
+
};
|
|
377
|
+
marker.addEventListener('click', (event) => {
|
|
378
|
+
const details = sectionOf(heading);
|
|
379
|
+
const summary = details?.querySelector<HTMLElement>(':scope > summary');
|
|
380
|
+
if (!details || !summary) return;
|
|
381
|
+
event.preventDefault();
|
|
382
|
+
const opening = !isOpen();
|
|
383
|
+
if (!opening && !onScreen()) {
|
|
384
|
+
goTo();
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
summary.click();
|
|
388
|
+
if (opening) goTo();
|
|
378
389
|
});
|
|
379
390
|
}
|
|
380
391
|
});
|
|
@@ -216,5 +216,9 @@ if (!anchorId) {
|
|
|
216
216
|
};
|
|
217
217
|
window.addEventListener('hashchange', revealForHash);
|
|
218
218
|
revealForHash();
|
|
219
|
+
// A number alias (`#eq-2.1.1`) is rewritten to its named anchor by
|
|
220
|
+
// `KeyedFloatEnhancer.astro`, whose script runs after this one and
|
|
221
|
+
// fires no event; look again once every script has run.
|
|
222
|
+
document.addEventListener('DOMContentLoaded', revealForHash, { once: true });
|
|
219
223
|
});
|
|
220
224
|
</script>
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
lessonIdFromSlug,
|
|
14
14
|
} from '../../compiler/lesson-derivation';
|
|
15
15
|
import { loadManifest } from '../../compiler/manifest';
|
|
16
|
+
import { isIndexEntryLatex } from '../../compiler/content-schemas';
|
|
17
|
+
import { indexEntryFormula } from '../../reference/derived-notation.mjs';
|
|
16
18
|
|
|
17
19
|
const [notationEntries, lessonEntries, manifest] = await Promise.all([
|
|
18
20
|
getCollection('notation'),
|
|
@@ -22,14 +24,20 @@ const [notationEntries, lessonEntries, manifest] = await Promise.all([
|
|
|
22
24
|
// Starlight's `link-alt` icon — the same glyph its own heading anchors use
|
|
23
25
|
// (`rehype-heading-anchors.mjs`). Reimplemented here rather than reused via
|
|
24
26
|
// `.sl-anchor-link`/`.sl-heading-wrapper` because that CSS assumes a single
|
|
25
|
-
// inline prose heading, not this entry's
|
|
26
|
-
// name
|
|
27
|
-
//
|
|
27
|
+
// inline prose heading, not this entry's two-column grid header (symbol,
|
|
28
|
+
// name) — the glyph carries over, the layout doesn't, so this file supplies
|
|
29
|
+
// its own reveal styling (below).
|
|
28
30
|
const HEADING_LINK_ICON_PATH =
|
|
29
31
|
'm12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42';
|
|
30
32
|
|
|
31
33
|
const labelOf = (data: { key: string; label?: string }) =>
|
|
32
34
|
resolveLabel(data.key, data.label);
|
|
35
|
+
// An index entry's formula names what it indexes as `{base}` (rule N23); on
|
|
36
|
+
// its own card that is a square bound to the entry.
|
|
37
|
+
const formulaOf = (data: { key: string; latex: string; formula?: string }) =>
|
|
38
|
+
data.formula !== undefined && isIndexEntryLatex(data.latex)
|
|
39
|
+
? indexEntryFormula(data.formula, data.key)
|
|
40
|
+
: data.formula;
|
|
33
41
|
const notationLabelByKey = new Map(
|
|
34
42
|
notationEntries.map((entry) => [entry.data.key, labelOf(entry.data)]),
|
|
35
43
|
);
|
|
@@ -72,7 +80,7 @@ const formulaScopeByKey = new Map<string, GlyphScopeEntry[]>(
|
|
|
72
80
|
formulaGlyphScope({
|
|
73
81
|
key: entry.data.key,
|
|
74
82
|
notation: entry.data.latex,
|
|
75
|
-
formula: entry.data
|
|
83
|
+
formula: formulaOf(entry.data),
|
|
76
84
|
glosses: glossesByKey.get(entry.data.key) ?? [],
|
|
77
85
|
cardsByKey,
|
|
78
86
|
}),
|
|
@@ -113,12 +121,12 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
113
121
|
notation: entry.data.latex,
|
|
114
122
|
label: labelOf(entry.data),
|
|
115
123
|
summary: entry.data.meaning,
|
|
116
|
-
formula: entry.data
|
|
124
|
+
formula: formulaOf(entry.data),
|
|
117
125
|
formulaScope: formulaScopeByKey.get(entry.data.key),
|
|
118
126
|
glosses: glossesByKey.get(entry.data.key),
|
|
119
127
|
units: entry.data.units,
|
|
128
|
+
range: entry.data.range,
|
|
120
129
|
seeAlso: entry.data.seeAlso,
|
|
121
|
-
editorialStatus: entry.data.editorialStatus,
|
|
122
130
|
scope: 'shared',
|
|
123
131
|
href: `#notation-${entry.data.key}`,
|
|
124
132
|
}),
|
|
@@ -186,7 +194,6 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
186
194
|
</div>
|
|
187
195
|
<code>{entry.data.key}</code>
|
|
188
196
|
</div>
|
|
189
|
-
<span class="glossary-status">{entry.data.editorialStatus}</span>
|
|
190
197
|
</header>
|
|
191
198
|
|
|
192
199
|
<dl class="glossary-metadata">
|
|
@@ -200,6 +207,12 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
200
207
|
<dd>{entry.data.units}</dd>
|
|
201
208
|
</div>
|
|
202
209
|
)}
|
|
210
|
+
{entry.data.range && (
|
|
211
|
+
<div>
|
|
212
|
+
<dt>Runs over</dt>
|
|
213
|
+
<dd><NotationMath latex={entry.data.range} /></dd>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
203
216
|
{entry.data.aliases.length > 0 && (
|
|
204
217
|
<div>
|
|
205
218
|
<dt>Aliases</dt>
|
|
@@ -225,7 +238,7 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
225
238
|
{entry.data.formula && (
|
|
226
239
|
<div class="glossary-formula">
|
|
227
240
|
<NotationFormula
|
|
228
|
-
formula={entry.data
|
|
241
|
+
formula={formulaOf(entry.data) ?? ''}
|
|
229
242
|
scope={formulaScopeByKey.get(entry.data.key) ?? []}
|
|
230
243
|
glosses={glossesByKey.get(entry.data.key)}
|
|
231
244
|
entryLabel={labelOf(entry.data)}
|
|
@@ -325,7 +338,7 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
325
338
|
|
|
326
339
|
article > header {
|
|
327
340
|
display: grid;
|
|
328
|
-
grid-template-columns: minmax(4rem, auto) 1fr
|
|
341
|
+
grid-template-columns: minmax(4rem, auto) 1fr;
|
|
329
342
|
align-items: center;
|
|
330
343
|
gap: 1rem;
|
|
331
344
|
}
|
|
@@ -425,14 +438,6 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
425
438
|
}
|
|
426
439
|
}
|
|
427
440
|
|
|
428
|
-
.glossary-status {
|
|
429
|
-
padding: 0.15rem 0.5rem;
|
|
430
|
-
border: 1px solid var(--sl-color-gray-5);
|
|
431
|
-
border-radius: 999px;
|
|
432
|
-
color: var(--sl-color-gray-3);
|
|
433
|
-
font-size: var(--sl-text-xs);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
441
|
.glossary-metadata {
|
|
437
442
|
display: grid;
|
|
438
443
|
gap: 0.45rem;
|