explico 0.7.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1409 -1
- package/README.md +60 -15
- package/dist/compiler/astro.d.ts +6 -1
- package/dist/compiler/cli.d.ts +54 -3
- package/dist/compiler/cli.js +80 -6
- package/dist/compiler/collections.d.ts +35 -2
- package/dist/compiler/collections.js +84 -8
- package/dist/compiler/course-config.d.ts +3 -194
- package/dist/compiler/course-config.js +78 -5
- package/dist/compiler/exports/standalone-html.d.ts +6 -1
- package/dist/compiler/knowledge-version.js +1 -1
- package/dist/compiler/lesson-derivation.d.ts +27 -10
- package/dist/compiler/lesson-derivation.js +23 -8
- package/dist/compiler/logging.d.ts +14 -1
- package/dist/compiler/logging.js +6 -2
- package/dist/compiler/manifest.d.ts +204 -31
- package/dist/compiler/manifest.js +270 -55
- package/dist/compiler/resolution-report.d.ts +6 -1
- package/dist/course-config-Dsnri9Tk.d.ts +464 -0
- package/dist/reference/base-accents.d.mts +26 -0
- package/dist/reference/base-accents.mjs +31 -0
- package/dist/reference/base-roman-labels.d.mts +2 -2
- package/dist/reference/base-roman-labels.mjs +2 -2
- package/dist/reference/consistency.d.ts +14 -2
- package/dist/reference/consistency.js +108 -4
- package/dist/reference/delimiter-size.d.ts +21 -11
- package/dist/reference/delimiter-size.js +15 -39
- package/dist/reference/diagrams-validate.d.ts +7 -2
- package/dist/reference/diagrams-validate.js +9 -1
- package/dist/reference/equations-validate.d.ts +8 -5
- package/dist/reference/equations-validate.js +1 -0
- package/dist/reference/equations.d.mts +69 -34
- package/dist/reference/equations.mjs +90 -44
- package/dist/reference/examples-validate.d.ts +31 -0
- package/dist/reference/examples-validate.js +48 -0
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/figures-validate.js +4 -1
- package/dist/reference/folds-validate.d.ts +33 -0
- package/dist/reference/folds-validate.js +32 -0
- package/dist/reference/gate-abbreviations.d.ts +3 -1
- package/dist/reference/gate-abbreviations.js +3 -2
- package/dist/reference/gate-course-rules.d.ts +32 -0
- package/dist/reference/gate-course-rules.js +85 -0
- package/dist/reference/gate-heading-length.d.ts +5 -2
- package/dist/reference/gate-heading-length.js +47 -23
- package/dist/reference/gate-heading-structure.d.ts +41 -0
- package/dist/reference/gate-heading-structure.js +113 -0
- package/dist/reference/gate-math-punctuation.d.ts +71 -0
- package/dist/reference/gate-math-punctuation.js +251 -0
- package/dist/reference/gate-math.d.ts +44 -6
- package/dist/reference/gate-math.js +63 -10
- package/dist/reference/gate-part-pages.d.ts +50 -0
- package/dist/reference/gate-part-pages.js +118 -0
- package/dist/reference/gate-prose-notation.d.ts +32 -11
- package/dist/reference/gate-prose-notation.js +30 -8
- package/dist/reference/katex-delimiters.d.mts +15 -0
- package/dist/reference/katex-delimiters.mjs +138 -0
- package/dist/reference/katex-options.mjs +4 -0
- package/dist/reference/keyed-float-validate.d.ts +27 -10
- package/dist/reference/keyed-float-validate.js +15 -1
- package/dist/reference/keyed-jsx-floats.d.mts +11 -5
- package/dist/reference/keyed-jsx-floats.mjs +73 -20
- package/dist/reference/knowledge-checks-validate.d.ts +1 -1
- package/dist/reference/knowledge-checks.d.mts +3 -1
- package/dist/reference/knowledge-checks.mjs +9 -4
- package/dist/reference/lab-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-slot.d.ts +34 -0
- package/dist/reference/lab-math-slot.js +6 -0
- package/dist/reference/math-glyphs.d.mts +80 -4
- package/dist/reference/math-glyphs.mjs +588 -39
- package/dist/reference/page-notation.d.ts +6 -1
- package/dist/reference/raw-floats-validate.js +1 -1
- package/dist/reference/raw-floats.mjs +1 -1
- package/dist/reference/rehype-heading-anchors.d.mts +14 -0
- package/dist/reference/rehype-heading-anchors.mjs +116 -17
- package/dist/reference/rehype-table-header-labels.d.mts +6 -1
- package/dist/reference/rehype-table-header-labels.mjs +21 -3
- package/dist/reference/remark-asides.d.mts +3 -0
- package/dist/reference/remark-asides.mjs +180 -0
- package/dist/reference/remark-memo.d.mts +3 -1
- package/dist/reference/remark-memo.mjs +42 -11
- package/dist/reference/remark-notation.d.mts +60 -10
- package/dist/reference/remark-notation.mjs +387 -36
- package/dist/reference/render-lab-math.d.ts +4 -26
- package/dist/reference/render-lab-math.js +5 -5
- package/dist/reference/sections-validate.d.ts +27 -0
- package/dist/reference/sections-validate.js +41 -0
- package/dist/reference/sections.d.mts +97 -0
- package/dist/reference/sections.mjs +306 -0
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/types.d.ts +24 -1
- package/dist/reference/unit-words.d.ts +37 -0
- package/dist/reference/unit-words.js +170 -0
- package/dist/rules/catalog.d.ts +176 -0
- package/dist/rules/catalog.js +983 -0
- package/dist/rules/course-rules.d.ts +115 -0
- package/dist/rules/course-rules.js +292 -0
- package/dist/rules/course-rules.schema.json +138 -0
- package/dist/rules/in-effect.d.ts +53 -0
- package/dist/rules/in-effect.js +142 -0
- package/dist/rules/rules.schema.json +1072 -0
- package/dist/rules/settings.d.ts +3 -0
- package/dist/rules/settings.js +290 -0
- package/dist/rules/waivers.d.ts +53 -0
- package/dist/rules/waivers.js +132 -0
- package/dist/session/PreferenceStore.d.ts +5 -0
- package/dist/session/PreferenceStore.js +9 -0
- package/docs/VERSIONING.md +2 -2
- package/docs/authoring/README.md +232 -40
- package/docs/authoring/agents.md +55 -9
- package/docs/authoring/components.md +178 -22
- package/docs/authoring/migrating.md +1324 -2
- package/docs/authoring/notation.md +616 -344
- package/docs/authoring/prose.md +551 -0
- package/docs/authoring/retired.md +35 -0
- package/docs/authoring/structure-rules.md +422 -40
- package/docs/authoring/structure.md +445 -62
- package/docs/authoring/versioning.md +20 -28
- package/docs/authoring/visualizations.md +142 -20
- package/docs/katex-bracket-scaling.md +191 -0
- package/package.json +3 -2
- package/src/compiler/cli.ts +139 -6
- package/src/compiler/collections.ts +144 -10
- package/src/compiler/course-config.ts +273 -34
- package/src/compiler/knowledge-version.ts +1 -1
- package/src/compiler/lesson-derivation.ts +54 -15
- package/src/compiler/logging.ts +21 -4
- package/src/compiler/manifest.ts +573 -104
- package/src/components/BlockHeading.astro +113 -0
- package/src/components/CourseContents.astro +221 -0
- package/src/components/CurriculumMap.astro +5 -6
- package/src/components/FloatIndex.astro +200 -0
- package/src/components/HoverPanel.astro +431 -79
- package/src/components/PartBridge.astro +27 -0
- package/src/components/PartOverview.astro +401 -0
- package/src/components/PartScope.astro +27 -0
- package/src/components/abbreviation/AbbreviationLayer.astro +49 -51
- package/src/components/assessments/AssessmentRunner.tsx +8 -5
- package/src/components/assessments/AssessmentSet.astro +23 -26
- package/src/components/block-heading.ts +146 -0
- package/src/components/card-arrow.ts +243 -0
- package/src/components/card-drag.ts +631 -0
- package/src/components/card-jump.ts +45 -0
- package/src/components/card-manager.ts +1589 -0
- package/src/components/card-memory.ts +177 -0
- package/src/components/card-placement.ts +504 -0
- package/src/components/card-side.ts +336 -0
- package/src/components/card-travel.ts +197 -0
- package/src/components/citation/CitationLayer.astro +77 -76
- package/src/components/diagram/Diagram.astro +161 -316
- package/src/components/disclosure/course-disclosure.ts +24 -0
- package/src/components/disclosure/hover-preview.ts +122 -42
- package/src/components/disclosure/persisted-disclosure.ts +3 -3
- package/src/components/disclosure/print-as-shown.ts +252 -0
- package/src/components/examples/CompactExample.astro +3 -1
- package/src/components/examples/CompactExamples.astro +101 -94
- package/src/components/figure/Figure.astro +174 -274
- package/src/components/figure/FloatCaption.astro +87 -0
- package/src/components/figure/figure-state.ts +58 -0
- package/src/components/figure/figure-tools.ts +87 -0
- package/src/components/float-name.ts +101 -0
- package/src/components/fold/Fold.astro +125 -0
- package/src/components/fold/fold-id.ts +40 -0
- package/src/components/memo/Memo.astro +162 -231
- package/src/components/memo/ShortNote.astro +6 -6
- package/src/components/notation/NotationFormula.astro +5 -1
- package/src/components/notation/NotationGlossary.astro +24 -14
- package/src/components/notation/NotationLayer.astro +169 -162
- package/src/components/notation/NotationMath.astro +10 -1
- package/src/components/notation/render-notation-math.ts +111 -10
- package/src/components/reference/FloatRefLayer.astro +485 -423
- package/src/components/starlight/AboutPanel.astro +22 -6
- package/src/components/starlight/CollapsibleSections.astro +207 -93
- package/src/components/starlight/DiagramEnhancer.astro +15 -138
- package/src/components/starlight/HeadingAnchorEnhancer.astro +193 -1
- package/src/components/starlight/KeyedFloatEnhancer.astro +100 -9
- package/src/components/starlight/LayoutHeader.astro +298 -19
- package/src/components/starlight/LayoutPageSidebar.astro +99 -12
- package/src/components/starlight/LayoutPageTitle.astro +120 -0
- package/src/components/starlight/LayoutSidebar.astro +229 -11
- package/src/components/starlight/LessonExportActions.astro +53 -72
- package/src/components/starlight/LessonFooter.astro +26 -4
- package/src/components/starlight/section-parts.ts +15 -2
- package/src/components/starlight/section-summary.ts +181 -0
- package/src/components/table/Table.astro +95 -76
- package/src/components/table/TableOverflowExpander.astro +65 -28
- package/src/content-config.ts +40 -4
- package/src/reference/base-accents.mjs +31 -0
- package/src/reference/base-roman-labels.mjs +2 -2
- package/src/reference/consistency.ts +176 -5
- package/src/reference/delimiter-size.ts +40 -61
- package/src/reference/diagrams-validate.ts +17 -1
- package/src/reference/equations-validate.ts +10 -5
- package/src/reference/equations.mjs +90 -44
- package/src/reference/examples-validate.ts +97 -0
- package/src/reference/figures-validate.ts +6 -1
- package/src/reference/folds-validate.ts +65 -0
- package/src/reference/gate-abbreviations.ts +3 -2
- package/src/reference/gate-course-rules.ts +134 -0
- package/src/reference/gate-heading-length.ts +68 -37
- package/src/reference/gate-heading-structure.ts +193 -0
- package/src/reference/gate-math-punctuation.ts +381 -0
- package/src/reference/gate-math.ts +119 -12
- package/src/reference/gate-part-pages.ts +217 -0
- package/src/reference/gate-prose-notation.ts +74 -13
- package/src/reference/katex-delimiters.mjs +138 -0
- package/src/reference/katex-options.mjs +4 -0
- package/src/reference/keyed-float-validate.ts +45 -11
- package/src/reference/keyed-jsx-floats.mjs +73 -20
- package/src/reference/knowledge-checks-validate.ts +1 -1
- package/src/reference/knowledge-checks.mjs +9 -4
- package/src/reference/lab-math-slot.ts +36 -0
- package/src/reference/math-glyphs.mjs +588 -39
- package/src/reference/raw-floats-validate.ts +1 -1
- package/src/reference/raw-floats.mjs +1 -1
- package/src/reference/rehype-heading-anchors.mjs +116 -17
- package/src/reference/rehype-table-header-labels.mjs +21 -3
- package/src/reference/remark-asides.mjs +180 -0
- package/src/reference/remark-memo.mjs +42 -11
- package/src/reference/remark-notation.mjs +387 -36
- package/src/reference/render-lab-math.ts +17 -32
- package/src/reference/sections-validate.ts +81 -0
- package/src/reference/sections.mjs +306 -0
- package/src/reference/tables-validate.ts +2 -1
- package/src/reference/types.ts +24 -0
- package/src/reference/unit-words.ts +233 -0
- package/src/rules/catalog.ts +1298 -0
- package/src/rules/course-rules.ts +456 -0
- package/src/rules/in-effect.ts +235 -0
- package/src/rules/settings.ts +448 -0
- package/src/rules/waivers.ts +249 -0
- package/src/session/PreferenceStore.ts +15 -0
- package/src/styles/global.css +1471 -163
- package/src/styles/surfaces.css +182 -0
- package/src/components/hover-panel.ts +0 -339
- package/src/components/popup-stack.ts +0 -345
|
@@ -2,23 +2,44 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* A collapsible side-note authored inline and moved between the surrounding
|
|
4
4
|
* paragraphs by `remark-memo.mjs`. The remark adapter owns `number` and
|
|
5
|
-
* `anchorId`; this component owns the block,
|
|
6
|
-
* click
|
|
5
|
+
* `anchorId`; this component owns the block, its marker's hover preview, and
|
|
6
|
+
* the marker's click behavior.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Named like every float (docs/plans/titles-and-containers.md Part 3): the
|
|
9
|
+
* required `caption` is the memo's bar and heads its preview; the optional
|
|
10
|
+
* `short` caption is what its marker's tooltip calls it. The marker is the
|
|
11
|
+
* memo's per-page letter (`a`, `b`, …), set as a superscript in the prose so
|
|
12
|
+
* it never reads as a citation's `[1]` (Q7); the bar shows the same letter.
|
|
13
|
+
*
|
|
14
|
+
* **A memo is a `######`-rank section** (Part 4): its bar is an `<h6>` in the
|
|
15
|
+
* same `.sl-heading-wrapper` a Markdown heading gets, marked collapsible, so
|
|
16
|
+
* `CollapsibleSections.astro` folds it exactly like a section — the gutter
|
|
17
|
+
* chevron, the hover peek, the "Hidden here: …" line, the animation, the
|
|
18
|
+
* chain icon that copies its link, persistence and print-as-shown. Its
|
|
19
|
+
* first-visit state is the course's `disclosure.memos` (closed by default),
|
|
20
|
+
* independent of `subsections`; `defaultOpen` overrides it at one memo. The
|
|
21
|
+
* `<h6>` is drawn by this component, not written in the lesson, so it is not
|
|
22
|
+
* in the outline, "On this page", or the heading rules — nor in the
|
|
23
|
+
* accessibility tree's headings (`role="none"`: a memo after a `##` is not a
|
|
24
|
+
* sixth-level heading of the page, and axe's heading order says so; the
|
|
25
|
+
* summary it sits in still reads its caption). The frame is marked
|
|
26
|
+
* `data-section-contained`, so neither its own heading nor any in its body
|
|
27
|
+
* ends a section around it (`section-parts.ts`), and its wrapper
|
|
28
|
+
* `data-section-to-end`, so its section is the whole body.
|
|
12
29
|
*/
|
|
30
|
+
import { courseDisclosure } from '../disclosure/course-disclosure';
|
|
31
|
+
|
|
13
32
|
export interface Props {
|
|
14
33
|
/** Stable lowercase/hyphenated key used by the marker and persistence. */
|
|
15
34
|
id: string;
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
/** Optional prose label for the marker; omission gives the
|
|
35
|
+
/** The memo's caption: its bar, and the head of its marker's hover preview. */
|
|
36
|
+
caption: string;
|
|
37
|
+
/** Optional short caption: the marker's tooltip; rule S1 measures it. */
|
|
38
|
+
short?: string;
|
|
39
|
+
/** Optional prose label for the marker; omission gives the lettered form. */
|
|
21
40
|
label?: string;
|
|
41
|
+
/** State for a reader with nothing saved yet. Default: the course's `disclosure.memos`. */
|
|
42
|
+
defaultOpen?: boolean;
|
|
22
43
|
/** Persist the reader's open/closed choice across visits, per page. Default: `true`. */
|
|
23
44
|
rememberState?: boolean;
|
|
24
45
|
/** Computed by `remark-memo.mjs` — never author this directly. */
|
|
@@ -29,8 +50,9 @@ export interface Props {
|
|
|
29
50
|
|
|
30
51
|
const {
|
|
31
52
|
id,
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
caption,
|
|
54
|
+
short,
|
|
55
|
+
defaultOpen,
|
|
34
56
|
rememberState = true,
|
|
35
57
|
number,
|
|
36
58
|
anchorId,
|
|
@@ -39,149 +61,107 @@ const {
|
|
|
39
61
|
if (typeof id !== 'string' || !id.trim()) {
|
|
40
62
|
throw new Error('Memo requires a non-empty `id` prop.');
|
|
41
63
|
}
|
|
42
|
-
|
|
43
|
-
|
|
64
|
+
for (const removed of ['title', 'description'] as const) {
|
|
65
|
+
if (removed in Astro.props) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`<Memo id="${id}"> has no \`${removed}\` prop: a memo is named by its \`caption\` (its bar and preview) and an optional \`short\` caption.`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
44
70
|
}
|
|
45
|
-
if (typeof
|
|
46
|
-
throw new Error('Memo requires a non-empty `
|
|
71
|
+
if (typeof caption !== 'string' || !caption.trim()) {
|
|
72
|
+
throw new Error('Memo requires a non-empty `caption` prop.');
|
|
47
73
|
}
|
|
48
74
|
if (!number || !anchorId) {
|
|
49
75
|
throw new Error(
|
|
50
76
|
'Memo is missing its computed `number`/`anchorId` — register remark-memo after remark-mdx in the Markdown remark pipeline.',
|
|
51
77
|
);
|
|
52
78
|
}
|
|
79
|
+
|
|
80
|
+
const open = defaultOpen ?? (await courseDisclosure()).memos.defaultOpen;
|
|
81
|
+
const name = `Memo ${number}: ${short ?? caption}`;
|
|
53
82
|
---
|
|
54
83
|
|
|
55
|
-
<div
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
class="memo-
|
|
64
|
-
|
|
65
|
-
data-
|
|
66
|
-
data-memo-number={number}
|
|
67
|
-
data-memo-description={description}
|
|
84
|
+
<div
|
|
85
|
+
class="memo-block memo-block-frame ex-tier-aside ex-tier-aside--tinted"
|
|
86
|
+
data-memo-key={id}
|
|
87
|
+
data-memo-number={number}
|
|
88
|
+
data-memo-short={short}
|
|
89
|
+
data-section-contained
|
|
90
|
+
>
|
|
91
|
+
<div
|
|
92
|
+
class="sl-heading-wrapper level-h6 memo-block__titlebar"
|
|
93
|
+
data-collapsible="true"
|
|
94
|
+
data-default-open={String(open)}
|
|
68
95
|
data-remember-state={String(rememberState)}
|
|
96
|
+
data-section-to-end
|
|
69
97
|
>
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<svg
|
|
82
|
-
aria-hidden="true"
|
|
83
|
-
width="16"
|
|
84
|
-
height="16"
|
|
85
|
-
viewBox="0 0 24 24"
|
|
86
|
-
fill="currentColor"
|
|
87
|
-
><path
|
|
88
|
-
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"
|
|
89
|
-
></path></svg
|
|
98
|
+
<h6 id={anchorId} class="memo-block__heading" data-memo-number={number} role="none"
|
|
99
|
+
>{caption}</h6
|
|
100
|
+
>
|
|
101
|
+
<a class="sl-anchor-link" href={`#${anchorId}`}
|
|
102
|
+
><span aria-hidden="true" class="sl-anchor-icon"
|
|
103
|
+
><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"
|
|
104
|
+
><path
|
|
105
|
+
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"
|
|
106
|
+
></path></svg
|
|
107
|
+
></span
|
|
108
|
+
><span class="sr-only" data-pagefind-ignore>{name}</span></a
|
|
90
109
|
>
|
|
91
|
-
</
|
|
110
|
+
</div>
|
|
111
|
+
<div class="memo-block__body">
|
|
112
|
+
<slot />
|
|
113
|
+
</div>
|
|
92
114
|
</div>
|
|
93
115
|
|
|
94
116
|
<style>
|
|
117
|
+
/* The aside tier, tinted (`ex-tier-aside ex-tier-aside--tinted`,
|
|
118
|
+
`surfaces.css`): a left rule and the accent tint, no box around it. Its
|
|
119
|
+
inline-start padding is the gutter the section chevron centres in, as
|
|
120
|
+
a `[card]` section's padding is. The folding itself — chevron, peek,
|
|
121
|
+
"Hidden here" line, animation — is the section wrapper's
|
|
122
|
+
(`CollapsibleSections.astro`, `global.css`). */
|
|
95
123
|
.memo-block-frame {
|
|
96
|
-
|
|
124
|
+
--ex-section-gutter: 1.75rem;
|
|
97
125
|
margin-block: 1rem;
|
|
98
|
-
|
|
99
|
-
border-inline-start: 4px solid var(--sl-color-accent);
|
|
100
|
-
border-radius: 0.35rem;
|
|
101
|
-
background: color-mix(in srgb, var(--sl-color-bg) 95%, var(--sl-color-accent) 5%);
|
|
126
|
+
padding: 0.45rem 1rem 0.45rem var(--ex-section-gutter);
|
|
102
127
|
}
|
|
103
128
|
|
|
104
|
-
.memo-block {
|
|
105
|
-
margin: 0;
|
|
106
|
-
padding: 0;
|
|
107
|
-
scroll-margin-block-start: 5rem;
|
|
129
|
+
.memo-block-frame > :global(*) {
|
|
130
|
+
margin-block: 0;
|
|
108
131
|
}
|
|
109
132
|
|
|
110
|
-
.memo-
|
|
111
|
-
|
|
112
|
-
color: var(--sl-color-white);
|
|
113
|
-
font-weight: 600;
|
|
133
|
+
.memo-block__heading {
|
|
134
|
+
scroll-margin-block-start: 5rem;
|
|
114
135
|
}
|
|
115
136
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
137
|
+
/* The memo's letter before its caption: the same italic accent letter the
|
|
138
|
+
marker in the prose shows. Generated content, so the heading's text —
|
|
139
|
+
its accessible name and what a preview copies — stays the caption. */
|
|
140
|
+
.memo-block__heading::before {
|
|
141
|
+
content: attr(data-memo-number);
|
|
142
|
+
margin-inline-end: 0.45em;
|
|
121
143
|
color: var(--sl-color-text-accent);
|
|
144
|
+
font-style: italic;
|
|
122
145
|
font-weight: 600;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
.memo-block__anchor:hover,
|
|
127
|
-
.memo-block__anchor:focus-visible {
|
|
128
|
-
text-decoration: underline;
|
|
129
|
-
text-underline-offset: 0.15em;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.memo-block__title-anchor {
|
|
133
|
-
position: absolute;
|
|
134
|
-
z-index: 1;
|
|
135
|
-
inset-block-start: 0.55rem;
|
|
136
|
-
inset-inline-end: 0.65rem;
|
|
137
|
-
display: inline-flex;
|
|
138
|
-
align-items: center;
|
|
139
|
-
justify-content: center;
|
|
140
|
-
width: 1.75rem;
|
|
141
|
-
height: 1.75rem;
|
|
142
|
-
color: var(--sl-color-gray-3);
|
|
143
|
-
text-decoration: none;
|
|
144
|
-
-webkit-user-select: none;
|
|
145
|
-
user-select: none;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.memo-block__title-anchor svg {
|
|
149
|
-
width: 1rem;
|
|
150
|
-
height: 1rem;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
.memo-block__title-anchor:hover,
|
|
154
|
-
.memo-block__title-anchor:focus-visible {
|
|
155
|
-
color: var(--sl-color-text-accent);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.memo-block__title-anchor:focus-visible {
|
|
159
|
-
outline: 2px solid var(--sl-color-accent);
|
|
160
|
-
outline-offset: 0.1rem;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
@media (hover: hover) {
|
|
164
|
-
.memo-block__title-anchor {
|
|
165
|
-
opacity: 0;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.memo-block__title-anchor:focus-visible,
|
|
169
|
-
.memo-block-frame:hover .memo-block__title-anchor {
|
|
170
|
-
opacity: 1;
|
|
171
|
-
}
|
|
146
|
+
font-size: 0.85em;
|
|
147
|
+
vertical-align: 0.3em;
|
|
172
148
|
}
|
|
173
149
|
|
|
174
150
|
.memo-block__body {
|
|
175
|
-
padding: 0
|
|
151
|
+
padding-block-start: 0.5rem;
|
|
176
152
|
}
|
|
177
153
|
|
|
178
|
-
.memo-block__body > :first-child {
|
|
154
|
+
.memo-block__body > :global(:first-child) {
|
|
179
155
|
margin-block-start: 0;
|
|
180
156
|
}
|
|
181
157
|
|
|
158
|
+
/* A superscript italic letter, the footnote convention — never the
|
|
159
|
+
bracketed `[1]` a citation prints. */
|
|
182
160
|
:global(.sl-markdown-content .memo-marker) {
|
|
161
|
+
padding-inline: 0.1em;
|
|
183
162
|
border-radius: 0.2em;
|
|
184
163
|
color: var(--sl-color-text-accent);
|
|
164
|
+
font-style: italic;
|
|
185
165
|
font-weight: 600;
|
|
186
166
|
text-decoration: underline;
|
|
187
167
|
text-underline-offset: 0.15em;
|
|
@@ -202,7 +182,7 @@ if (!number || !anchorId) {
|
|
|
202
182
|
vertical-align: super;
|
|
203
183
|
}
|
|
204
184
|
|
|
205
|
-
:global(.memo-
|
|
185
|
+
:global(.memo-preview__caption) {
|
|
206
186
|
margin: 0 0 0.65rem;
|
|
207
187
|
color: var(--sl-color-gray-2);
|
|
208
188
|
}
|
|
@@ -216,29 +196,13 @@ if (!number || !anchorId) {
|
|
|
216
196
|
|
|
217
197
|
@media print {
|
|
218
198
|
.memo-block-frame {
|
|
219
|
-
border-color: currentColor;
|
|
220
199
|
background: transparent;
|
|
221
200
|
}
|
|
222
|
-
|
|
223
|
-
.memo-block__summary::before {
|
|
224
|
-
display: none !important;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.memo-block__title-anchor {
|
|
228
|
-
display: none;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/* The script opens the native disclosure while print media is active.
|
|
232
|
-
Keep the body explicit in case author CSS would otherwise hide it. */
|
|
233
|
-
.memo-block__body {
|
|
234
|
-
display: block !important;
|
|
235
|
-
}
|
|
236
201
|
}
|
|
237
202
|
</style>
|
|
238
203
|
|
|
239
204
|
<script>
|
|
240
205
|
import { attachHoverPeek, closeHoverPeek } from '../disclosure/hover-preview';
|
|
241
|
-
import { wirePersistedDetails } from '../disclosure/persisted-disclosure';
|
|
242
206
|
import { animatedScrollIntoView } from '../scroll-animation';
|
|
243
207
|
|
|
244
208
|
let memoPreviewSerial = 0;
|
|
@@ -317,132 +281,99 @@ if (!number || !anchorId) {
|
|
|
317
281
|
return clone;
|
|
318
282
|
}
|
|
319
283
|
|
|
320
|
-
function targetInside(details: HTMLDetailsElement): boolean {
|
|
321
|
-
if (!location.hash) return false;
|
|
322
|
-
let id = location.hash.slice(1);
|
|
323
|
-
try {
|
|
324
|
-
id = decodeURIComponent(id);
|
|
325
|
-
} catch {
|
|
326
|
-
/* keep the raw fragment */
|
|
327
|
-
}
|
|
328
|
-
const target = document.getElementById(id);
|
|
329
|
-
return target === details || (!!target && details.contains(target));
|
|
330
|
-
}
|
|
331
284
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
);
|
|
347
|
-
|
|
285
|
+
// The section wrapper (`CollapsibleSections.astro`) builds the memo's
|
|
286
|
+
// `<details>` at runtime, possibly after this script: find it lazily.
|
|
287
|
+
const sectionOf = (heading: HTMLElement) =>
|
|
288
|
+
heading.closest<HTMLDetailsElement>('details.sl-section-collapsible');
|
|
289
|
+
const whenSection = (
|
|
290
|
+
frame: HTMLElement,
|
|
291
|
+
heading: HTMLElement,
|
|
292
|
+
ready: (details: HTMLDetailsElement) => void,
|
|
293
|
+
) => {
|
|
294
|
+
const found = sectionOf(heading);
|
|
295
|
+
if (found) return ready(found);
|
|
296
|
+
const observer = new MutationObserver(() => {
|
|
297
|
+
const details = sectionOf(heading);
|
|
298
|
+
if (!details) return;
|
|
299
|
+
observer.disconnect();
|
|
300
|
+
ready(details);
|
|
301
|
+
});
|
|
302
|
+
observer.observe(frame, { childList: true, subtree: true });
|
|
303
|
+
};
|
|
348
304
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
305
|
+
document
|
|
306
|
+
.querySelectorAll<HTMLElement>('.memo-block[data-memo-key]:not([data-memo-wired])')
|
|
307
|
+
.forEach((frame) => {
|
|
308
|
+
frame.dataset.memoWired = 'true';
|
|
309
|
+
const key = frame.dataset.memoKey;
|
|
310
|
+
const number = frame.dataset.memoNumber;
|
|
311
|
+
const heading = frame.querySelector<HTMLElement>('.memo-block__heading');
|
|
312
|
+
const body = frame.querySelector<HTMLElement>('.memo-block__body');
|
|
313
|
+
if (!key || !number || !heading || !body) return;
|
|
354
314
|
|
|
355
315
|
const markers = Array.from(
|
|
356
316
|
document.querySelectorAll<HTMLAnchorElement>(
|
|
357
317
|
`.memo-marker[data-memo-key="${key}"]`,
|
|
358
318
|
),
|
|
359
|
-
).filter(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
details.addEventListener('toggle', syncMarkers);
|
|
370
|
-
syncMarkers();
|
|
371
|
-
|
|
372
|
-
const revealForHash = () => {
|
|
373
|
-
if (targetInside(details)) details.open = true;
|
|
374
|
-
};
|
|
375
|
-
revealForHash();
|
|
376
|
-
window.addEventListener('hashchange', revealForHash);
|
|
377
|
-
|
|
378
|
-
for (const selfLink of selfLinks) {
|
|
379
|
-
selfLink.addEventListener('click', (event) => {
|
|
380
|
-
event.preventDefault();
|
|
381
|
-
event.stopPropagation();
|
|
382
|
-
details.open = true;
|
|
383
|
-
if (location.hash !== `#${details.id}`) {
|
|
384
|
-
history.pushState(null, '', `#${details.id}`);
|
|
319
|
+
).filter((marker) => marker.getAttribute('aria-controls') === heading.id);
|
|
320
|
+
const isOpen = () => sectionOf(heading)?.dataset.state === 'open';
|
|
321
|
+
|
|
322
|
+
whenSection(frame, heading, (details) => {
|
|
323
|
+
const syncMarkers = () => {
|
|
324
|
+
for (const marker of markers) {
|
|
325
|
+
marker.setAttribute('aria-expanded', String(isOpen()));
|
|
326
|
+
if (isOpen()) closeHoverPeek(marker);
|
|
385
327
|
}
|
|
328
|
+
};
|
|
329
|
+
new MutationObserver(syncMarkers).observe(details, {
|
|
330
|
+
attributes: true,
|
|
331
|
+
attributeFilter: ['data-state'],
|
|
386
332
|
});
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
// A closed native <details> suppresses its body at the UA rendering
|
|
390
|
-
// layer, beyond what an author stylesheet can reliably override. Open it
|
|
391
|
-
// while print media is active, then put the reader's exact screen state
|
|
392
|
-
// back when the print dialog/export ends (the same approach used by
|
|
393
|
-
// CollapsibleSections for its native disclosures).
|
|
394
|
-
const printMedia = window.matchMedia('print');
|
|
395
|
-
let screenOpen: boolean | null = null;
|
|
396
|
-
const syncPrintState = () => {
|
|
397
|
-
if (document.querySelector('[data-explico-print-controller]')) return;
|
|
398
|
-
if (printMedia.matches) {
|
|
399
|
-
if (screenOpen === null) screenOpen = details.open;
|
|
400
|
-
details.open = true;
|
|
401
|
-
} else if (screenOpen !== null) {
|
|
402
|
-
details.open = screenOpen;
|
|
403
|
-
screenOpen = null;
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
printMedia.addEventListener('change', syncPrintState);
|
|
407
|
-
window.addEventListener('beforeprint', syncPrintState);
|
|
408
|
-
window.addEventListener('afterprint', syncPrintState);
|
|
409
|
-
syncPrintState();
|
|
333
|
+
syncMarkers();
|
|
334
|
+
});
|
|
410
335
|
|
|
411
336
|
for (const marker of markers) {
|
|
412
337
|
const suppressWhenOpen = (event: Event) => {
|
|
413
|
-
if (
|
|
338
|
+
if (isOpen()) event.stopImmediatePropagation();
|
|
414
339
|
};
|
|
415
340
|
marker.addEventListener('pointerenter', suppressWhenOpen);
|
|
416
341
|
marker.addEventListener('focus', suppressWhenOpen);
|
|
417
342
|
|
|
418
343
|
attachHoverPeek(marker, {
|
|
419
344
|
label: `Memo ${number} preview`,
|
|
420
|
-
// Keep the
|
|
345
|
+
// Keep the caption visible above up to 20em of body content.
|
|
421
346
|
maxHeightEm: 24,
|
|
422
347
|
getContent: () => {
|
|
423
348
|
const wrap = document.createElement('div');
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
wrap.append(
|
|
349
|
+
const caption = document.createElement('p');
|
|
350
|
+
caption.className = 'memo-preview__caption';
|
|
351
|
+
const letter = document.createElement('strong');
|
|
352
|
+
letter.textContent = `Memo ${number}`;
|
|
353
|
+
caption.append(letter, ` ${heading.textContent?.trim() ?? ''}`);
|
|
354
|
+
wrap.append(caption, cloneMemoBody(body));
|
|
430
355
|
return wrap;
|
|
431
356
|
},
|
|
432
357
|
});
|
|
433
358
|
|
|
359
|
+
// The marker opens the memo where it is and goes there, and closes
|
|
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.
|
|
434
362
|
marker.addEventListener('click', (event) => {
|
|
363
|
+
const details = sectionOf(heading);
|
|
364
|
+
const summary = details?.querySelector<HTMLElement>(':scope > summary');
|
|
365
|
+
if (!details || !summary) return;
|
|
435
366
|
event.preventDefault();
|
|
436
|
-
const opening = !
|
|
437
|
-
|
|
367
|
+
const opening = !isOpen();
|
|
368
|
+
summary.click();
|
|
438
369
|
if (!opening) return;
|
|
439
370
|
|
|
440
371
|
closeHoverPeek(marker);
|
|
441
|
-
if (location.hash !== `#${
|
|
442
|
-
history.pushState(null, '', `#${
|
|
372
|
+
if (location.hash !== `#${heading.id}`) {
|
|
373
|
+
history.pushState(null, '', `#${heading.id}`);
|
|
443
374
|
}
|
|
444
375
|
requestAnimationFrame(() => {
|
|
445
|
-
animatedScrollIntoView(
|
|
376
|
+
animatedScrollIntoView(frame, { block: 'start' });
|
|
446
377
|
});
|
|
447
378
|
});
|
|
448
379
|
}
|
|
@@ -30,7 +30,7 @@ if (!anchorId) {
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
32
|
<aside
|
|
33
|
-
class="short-note"
|
|
33
|
+
class="short-note ex-tier-aside"
|
|
34
34
|
id={anchorId}
|
|
35
35
|
data-short-note-key={id}
|
|
36
36
|
aria-label={description}
|
|
@@ -43,11 +43,12 @@ if (!anchorId) {
|
|
|
43
43
|
</aside>
|
|
44
44
|
|
|
45
45
|
<style>
|
|
46
|
+
/* Opened, the note is the aside tier untinted (`ex-tier-aside`,
|
|
47
|
+
`surfaces.css`): the shared left rule and nothing else. */
|
|
46
48
|
.short-note {
|
|
47
49
|
margin-block: 0.65rem;
|
|
48
50
|
padding-inline-start: 0.9rem;
|
|
49
51
|
scroll-margin-block-start: 5rem;
|
|
50
|
-
border-inline-start: 2px solid var(--sl-color-accent);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
.short-note[hidden] {
|
|
@@ -105,10 +106,9 @@ if (!anchorId) {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
@media print {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
/* No `.short-note[hidden]` override: print follows the screen
|
|
110
|
+
(`../disclosure/print-as-shown.ts`), so a note the reader left closed
|
|
111
|
+
stays closed and gets a "(content collapsed …)" note instead. */
|
|
112
112
|
.short-note__collapse {
|
|
113
113
|
display: none;
|
|
114
114
|
}
|
|
@@ -56,6 +56,9 @@ registerPageNotationDependencies(
|
|
|
56
56
|
);
|
|
57
57
|
const latex = stripNotationMarkers(formula);
|
|
58
58
|
const numbered = Boolean(number && numberHref);
|
|
59
|
+
// The copy tooltip names what it copies, the way a lesson equation's does
|
|
60
|
+
// with its `\caption{}`: here the owning entry's label.
|
|
61
|
+
const copyTitle = entryLabel ? `Copy LaTeX — ${entryLabel}` : 'Copy LaTeX';
|
|
59
62
|
---
|
|
60
63
|
|
|
61
64
|
<div class="notation-formula-block">
|
|
@@ -63,6 +66,7 @@ const numbered = Boolean(number && numberHref);
|
|
|
63
66
|
class:list={['notation-formula-math', numbered && 'notation-formula-math--numbered']}
|
|
64
67
|
data-notation-formula
|
|
65
68
|
data-latex={latex}
|
|
69
|
+
data-copy-title={copyTitle}
|
|
66
70
|
role="group"
|
|
67
71
|
aria-label={entryLabel ? `${entryLabel} formula` : 'Formula'}
|
|
68
72
|
>
|
|
@@ -87,7 +91,7 @@ const numbered = Boolean(number && numberHref);
|
|
|
87
91
|
data-notation-formula-copy
|
|
88
92
|
data-latex={latex}
|
|
89
93
|
aria-label={`Copy LaTeX for Equation ${number}`}
|
|
90
|
-
title=
|
|
94
|
+
title={copyTitle}
|
|
91
95
|
>
|
|
92
96
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
93
97
|
<rect x="9" y="9" width="10" height="10" rx="1"></rect>
|
|
@@ -150,7 +150,7 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
150
150
|
(left, right) => left.data.title.localeCompare(right.data.title),
|
|
151
151
|
);
|
|
152
152
|
return (
|
|
153
|
-
<article id={`notation-${entry.data.key}`}>
|
|
153
|
+
<article id={`notation-${entry.data.key}`} class="ex-tier-meta">
|
|
154
154
|
<header>
|
|
155
155
|
<span
|
|
156
156
|
class="glossary-symbol-trigger"
|
|
@@ -159,13 +159,19 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
159
159
|
<NotationMath class="glossary-symbol" latex={entry.data.latex} />
|
|
160
160
|
</span>
|
|
161
161
|
<div>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
{/* The anchor is a sibling of the heading, not a child — the
|
|
163
|
+
same shape `rehype-heading-anchors` gives a lesson heading.
|
|
164
|
+
Inside the `h2` its `aria-label` joins the heading's
|
|
165
|
+
accessible name ("Velocity Link to “Velocity”"). */}
|
|
166
|
+
<div class="glossary-heading">
|
|
167
|
+
<h2>
|
|
168
|
+
<a
|
|
169
|
+
class="glossary-term-link"
|
|
170
|
+
href={`#notation-${entry.data.key}`}
|
|
171
|
+
>
|
|
172
|
+
{labelOf(entry.data)}
|
|
173
|
+
</a>
|
|
174
|
+
</h2>
|
|
169
175
|
<a
|
|
170
176
|
class="glossary-anchor"
|
|
171
177
|
href={`#notation-${entry.data.key}`}
|
|
@@ -177,7 +183,7 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
177
183
|
</svg>
|
|
178
184
|
</span>
|
|
179
185
|
</a>
|
|
180
|
-
</
|
|
186
|
+
</div>
|
|
181
187
|
<code>{entry.data.key}</code>
|
|
182
188
|
</div>
|
|
183
189
|
<span class="glossary-status">{entry.data.editorialStatus}</span>
|
|
@@ -310,12 +316,11 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
310
316
|
margin-block-start: 2rem;
|
|
311
317
|
}
|
|
312
318
|
|
|
319
|
+
/* A generated reference entry: the meta tier (`ex-tier-meta`,
|
|
320
|
+
`surfaces.css`) draws its surface, border and radius. */
|
|
313
321
|
article {
|
|
314
322
|
scroll-margin-block-start: 5rem;
|
|
315
323
|
padding: 1.25rem;
|
|
316
|
-
border: 1px solid var(--sl-color-gray-5);
|
|
317
|
-
border-radius: 0.7rem;
|
|
318
|
-
background: var(--sl-color-gray-7);
|
|
319
324
|
}
|
|
320
325
|
|
|
321
326
|
article > header {
|
|
@@ -330,6 +335,11 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
330
335
|
font-size: var(--sl-text-xl);
|
|
331
336
|
}
|
|
332
337
|
|
|
338
|
+
.glossary-heading {
|
|
339
|
+
display: flex;
|
|
340
|
+
align-items: center;
|
|
341
|
+
}
|
|
342
|
+
|
|
333
343
|
.glossary-term-link {
|
|
334
344
|
color: inherit;
|
|
335
345
|
text-decoration: none;
|
|
@@ -396,8 +406,8 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
396
406
|
opacity: 0;
|
|
397
407
|
}
|
|
398
408
|
|
|
399
|
-
|
|
400
|
-
|
|
409
|
+
.glossary-heading:hover .glossary-anchor,
|
|
410
|
+
.glossary-heading:focus-within .glossary-anchor,
|
|
401
411
|
.glossary-anchor:focus-visible {
|
|
402
412
|
opacity: 1;
|
|
403
413
|
}
|