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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Manifest, ManifestLessonNotation } from '../compiler/manifest.js';
|
|
2
2
|
import '../curriculum/validation.js';
|
|
3
3
|
import './consistency.js';
|
|
4
|
-
import '../
|
|
4
|
+
import '../course-config-Dsnri9Tk.js';
|
|
5
|
+
import '../rules/catalog.js';
|
|
6
|
+
import '../rules/course-rules.js';
|
|
5
7
|
import '../compiler/collections.js';
|
|
6
8
|
import './types.js';
|
|
7
9
|
import './lint-ignore.js';
|
|
@@ -10,11 +12,14 @@ import './tables-validate.js';
|
|
|
10
12
|
import './keyed-float-validate.js';
|
|
11
13
|
import './figures-validate.js';
|
|
12
14
|
import './diagrams-validate.js';
|
|
15
|
+
import './examples-validate.js';
|
|
16
|
+
import './folds-validate.js';
|
|
13
17
|
import './knowledge-checks-validate.js';
|
|
14
18
|
import './raw-floats-validate.js';
|
|
15
19
|
import './gate-math.js';
|
|
16
20
|
import './mixed-content.js';
|
|
17
21
|
import './gloss.js';
|
|
22
|
+
import '../rules/waivers.js';
|
|
18
23
|
import '../compiler/knowledge-version.js';
|
|
19
24
|
import '../compiler/lesson-derivation.js';
|
|
20
25
|
|
|
@@ -40,7 +40,7 @@ function validateRawFloats(lessons) {
|
|
|
40
40
|
lesson,
|
|
41
41
|
"diagram-nested",
|
|
42
42
|
found.nestedDiagramCount,
|
|
43
|
-
(count) => `${lesson.lessonId} has ${count} <Diagram> ${plural(count, "component")} nested inside another component; ${count === 1 ? "it is" : "they are"} invisible to diagram numbering. Move it to the top level (or inside <Memo>/<ShortNote>), or wrap it in <Figure> to give it identity.`
|
|
43
|
+
(count) => `${lesson.lessonId} has ${count} <Diagram> ${plural(count, "component")} nested inside another component; ${count === 1 ? "it is" : "they are"} invisible to diagram numbering. Move it to the top level (or inside <Memo>/<ShortNote>/<Fold>), or wrap it in <Figure> to give it identity.`
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
46
|
diagnostics.sort(
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - an Explico `<Diagram>` with no `id` (can't be referenced with
|
|
8
8
|
* `[[dia-…]]` even though it participates in diagram numbering);
|
|
9
9
|
* - an Explico `<Diagram>` nested inside some other, non-transparent
|
|
10
|
-
* component (anything but `<Memo>`/`<ShortNote>`, or `<Table>`/`<Figure>`
|
|
10
|
+
* component (anything but `<Memo>`/`<ShortNote>`/`<Fold>`, or `<Table>`/`<Figure>`
|
|
11
11
|
* which legitimately gives it identity) — invisible to
|
|
12
12
|
* `keyed-jsx-floats.mjs`'s top-level-only scan entirely, so it never even
|
|
13
13
|
* reaches diagram numbering.
|
|
@@ -1 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A heading's source text without its trailing markers: what the reader sees,
|
|
3
|
+
* and what the manifest slugs the heading's anchor from (`sections.mjs`) — so
|
|
4
|
+
* `### Explore the range [card]` is anchored `#explore-the-range`.
|
|
5
|
+
*/
|
|
6
|
+
export function stripHeadingMarker(text: any): string;
|
|
7
|
+
/**
|
|
8
|
+
* The card tier a heading's trailing markers ask for, read from its source
|
|
9
|
+
* text: `'accent'` for `[card]`, `'meta'` for `[card=meta]`, `null` for
|
|
10
|
+
* neither. The outline reads it (`sections.mjs`): a meta section is
|
|
11
|
+
* logistics, so it is not numbered. With both, the one written last wins, as
|
|
12
|
+
* in `stripMarkers`.
|
|
13
|
+
*/
|
|
14
|
+
export function headingCardTier(text: any): any;
|
|
1
15
|
export default function rehypeHeadingAnchors(options?: {}): (tree: any, file: any) => void;
|
|
@@ -43,6 +43,21 @@
|
|
|
43
43
|
* `data-default-open="true"|"false"` on the wrapper — `CollapsibleSections.astro`
|
|
44
44
|
* only wraps headings carrying `data-collapsible` and never needs to know
|
|
45
45
|
* about the config or the marker syntax itself.
|
|
46
|
+
*
|
|
47
|
+
* A second kind of marker cards the whole section — heading and body — in a
|
|
48
|
+
* tier from `surfaces.css` (docs/plans/titles-and-containers.md Part 2):
|
|
49
|
+
* `[card]` is the accent tier, `[card=meta]` the meta tier. It becomes
|
|
50
|
+
* `data-card="accent"|"meta"` on the wrapper, which `CollapsibleSections.astro`
|
|
51
|
+
* reads the same way. A heading may carry one marker of each kind, in either
|
|
52
|
+
* order (`### Explore the range [collapsed] [card]`).
|
|
53
|
+
*
|
|
54
|
+
* A lesson's top-level headings arrive here already carrying their `id` and,
|
|
55
|
+
* when the course shows section numbers, `data-sec-number="1.1.2"` — both put
|
|
56
|
+
* on the mdast node by `remark-notation.mjs` from the manifest (`sections.mjs`
|
|
57
|
+
* decides them), so a `[[sec-…]]` reference and the heading it points at read
|
|
58
|
+
* one table. `global.css` renders the number; this plugin leaves both alone.
|
|
59
|
+
* A heading that arrives without an id (one inside a component) still gets
|
|
60
|
+
* Astro's slug, renamed only if it would repeat an id given upstream.
|
|
46
61
|
*/
|
|
47
62
|
|
|
48
63
|
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
|
|
@@ -76,25 +91,72 @@ function headingText(node) {
|
|
|
76
91
|
// `[collapsed]` with no link/reference definition of that name is just
|
|
77
92
|
// literal text in both dialects. Deliberately not a general attribute syntax
|
|
78
93
|
// (e.g. `{#id .class}`) — this codebase has no remark-attr-style plugin, and
|
|
79
|
-
//
|
|
80
|
-
// about a heading here.
|
|
81
|
-
const
|
|
94
|
+
// a handful of purpose-built tokens are enough for what an author needs to
|
|
95
|
+
// say about a heading here.
|
|
96
|
+
const HEADING_MARKER = /\s*\[(collapsible|collapsed|no-collapsible|card|card=meta)\]\s*$/i;
|
|
97
|
+
|
|
98
|
+
const CARD_TIER = { card: 'accent', 'card=meta': 'meta' };
|
|
82
99
|
|
|
83
|
-
|
|
100
|
+
/**
|
|
101
|
+
* A heading's source text without its trailing markers: what the reader sees,
|
|
102
|
+
* and what the manifest slugs the heading's anchor from (`sections.mjs`) — so
|
|
103
|
+
* `### Explore the range [card]` is anchored `#explore-the-range`.
|
|
104
|
+
*/
|
|
105
|
+
export function stripHeadingMarker(text) {
|
|
106
|
+
let rest = String(text);
|
|
107
|
+
for (let match = HEADING_MARKER.exec(rest); match; match = HEADING_MARKER.exec(rest)) {
|
|
108
|
+
rest = rest.slice(0, match.index);
|
|
109
|
+
}
|
|
110
|
+
return rest;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The card tier a heading's trailing markers ask for, read from its source
|
|
115
|
+
* text: `'accent'` for `[card]`, `'meta'` for `[card=meta]`, `null` for
|
|
116
|
+
* neither. The outline reads it (`sections.mjs`): a meta section is
|
|
117
|
+
* logistics, so it is not numbered. With both, the one written last wins, as
|
|
118
|
+
* in `stripMarkers`.
|
|
119
|
+
*/
|
|
120
|
+
export function headingCardTier(text) {
|
|
121
|
+
let rest = String(text);
|
|
122
|
+
let tier = null;
|
|
123
|
+
for (let match = HEADING_MARKER.exec(rest); match; match = HEADING_MARKER.exec(rest)) {
|
|
124
|
+
const marker = match[1].toLowerCase();
|
|
125
|
+
if (marker in CARD_TIER) tier ??= CARD_TIER[marker];
|
|
126
|
+
rest = rest.slice(0, match.index);
|
|
127
|
+
}
|
|
128
|
+
return tier;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Strip every trailing marker off a hast heading and report them as
|
|
133
|
+
* `{ collapse, card }` — the collapse marker and the card tier, each `null`
|
|
134
|
+
* when absent. With two of one kind, the one written last wins.
|
|
135
|
+
*/
|
|
136
|
+
function stripMarkers(headingNode) {
|
|
137
|
+
const found = { collapse: null, card: null };
|
|
84
138
|
const children = headingNode.children;
|
|
85
139
|
for (let index = children.length - 1; index >= 0; index--) {
|
|
86
140
|
const node = children[index];
|
|
87
|
-
if (node.type !== 'text')
|
|
88
|
-
const match =
|
|
141
|
+
if (node.type !== 'text') break; // trailing non-text node: no more markers
|
|
142
|
+
const match = HEADING_MARKER.exec(node.value);
|
|
89
143
|
if (match) {
|
|
144
|
+
const marker = match[1].toLowerCase();
|
|
145
|
+
if (marker in CARD_TIER) found.card ??= CARD_TIER[marker];
|
|
146
|
+
else found.collapse ??= marker;
|
|
90
147
|
node.value = node.value.slice(0, match.index);
|
|
91
148
|
if (!node.value) children.splice(index, 1);
|
|
92
|
-
|
|
149
|
+
else index += 1; // look at the same node again: markers can stack
|
|
150
|
+
continue;
|
|
93
151
|
}
|
|
94
|
-
if (node.value.trim())
|
|
152
|
+
if (node.value.trim()) break; // real trailing text, no marker present
|
|
95
153
|
// trailing whitespace-only text node: keep looking further back
|
|
96
154
|
}
|
|
97
|
-
|
|
155
|
+
const last = children.at(-1);
|
|
156
|
+
if ((found.collapse || found.card) && last?.type === 'text') {
|
|
157
|
+
last.value = last.value.trimEnd();
|
|
158
|
+
}
|
|
159
|
+
return found;
|
|
98
160
|
}
|
|
99
161
|
|
|
100
162
|
/**
|
|
@@ -172,14 +234,20 @@ export default function rehypeHeadingAnchors(options = {}) {
|
|
|
172
234
|
const assignHeadingIds = rehypeHeadingIds();
|
|
173
235
|
|
|
174
236
|
return function transform(tree, file) {
|
|
175
|
-
//
|
|
237
|
+
// Markers must be stripped *before* `rehypeHeadingIds` runs — otherwise
|
|
176
238
|
// the generated slug is built from text that still contains it (e.g.
|
|
177
239
|
// `two-ways-to-name-the-same-point-collapsed` instead of the clean
|
|
178
240
|
// title). `collapsibleHeadings` records each heading node's *resolved*
|
|
179
|
-
// decision, since the marker text is
|
|
180
|
-
// walk below runs.
|
|
241
|
+
// decision and `cardHeadings` its card tier, since the marker text is
|
|
242
|
+
// gone by the time the main wrapping walk below runs.
|
|
181
243
|
const collapsibleHeadings = new WeakMap();
|
|
182
|
-
const
|
|
244
|
+
const cardHeadings = new WeakMap();
|
|
245
|
+
// Every heading in document order, and the ones whose id was given
|
|
246
|
+
// upstream (a lesson's top-level headings, from the manifest; the
|
|
247
|
+
// generated "References").
|
|
248
|
+
const headings = [];
|
|
249
|
+
const given = new Set();
|
|
250
|
+
const readMarkers = (node) => {
|
|
183
251
|
if (!node || typeof node !== 'object' || !Array.isArray(node.children)) {
|
|
184
252
|
return;
|
|
185
253
|
}
|
|
@@ -189,19 +257,48 @@ export default function rehypeHeadingAnchors(options = {}) {
|
|
|
189
257
|
typeof child.tagName === 'string' &&
|
|
190
258
|
HEADING_TAG.test(child.tagName)
|
|
191
259
|
) {
|
|
192
|
-
const
|
|
260
|
+
const markers = stripMarkers(child);
|
|
193
261
|
collapsibleHeadings.set(
|
|
194
262
|
child,
|
|
195
|
-
resolveCollapsible(
|
|
263
|
+
resolveCollapsible(markers.collapse, child.tagName, defaultCollapsible),
|
|
196
264
|
);
|
|
265
|
+
if (markers.card) cardHeadings.set(child, markers.card);
|
|
266
|
+
headings.push(child);
|
|
267
|
+
const id = child.properties?.id;
|
|
268
|
+
if (typeof id === 'string' && id.length > 0) given.add(child);
|
|
197
269
|
}
|
|
198
|
-
|
|
270
|
+
readMarkers(child);
|
|
199
271
|
}
|
|
200
272
|
};
|
|
201
|
-
|
|
273
|
+
readMarkers(tree);
|
|
202
274
|
|
|
203
275
|
assignHeadingIds(tree, file);
|
|
204
276
|
|
|
277
|
+
// Astro's slugger never saw the ids given upstream, so a heading inside a
|
|
278
|
+
// component that shares a top-level heading's text would get the same
|
|
279
|
+
// id. The upstream one keeps it (a `[[sec-…]]` points there); this one
|
|
280
|
+
// takes the next free `-n`, as the slugger itself would have. The second
|
|
281
|
+
// `assignHeadingIds` only rebuilds `file.data.astro.headings` from the
|
|
282
|
+
// ids as they now stand.
|
|
283
|
+
if (given.size > 0) {
|
|
284
|
+
const taken = new Set([...given].map((heading) => heading.properties.id));
|
|
285
|
+
let renamed = false;
|
|
286
|
+
for (const heading of headings) {
|
|
287
|
+
const id = heading.properties?.id;
|
|
288
|
+
if (given.has(heading) || typeof id !== 'string') continue;
|
|
289
|
+
let unique = id;
|
|
290
|
+
for (let suffix = 1; taken.has(unique); suffix += 1) {
|
|
291
|
+
unique = `${id}-${suffix}`;
|
|
292
|
+
}
|
|
293
|
+
if (unique !== id) {
|
|
294
|
+
heading.properties.id = unique;
|
|
295
|
+
renamed = true;
|
|
296
|
+
}
|
|
297
|
+
taken.add(unique);
|
|
298
|
+
}
|
|
299
|
+
if (renamed) assignHeadingIds(tree, file);
|
|
300
|
+
}
|
|
301
|
+
|
|
205
302
|
const walk = (node) => {
|
|
206
303
|
if (!node || typeof node !== 'object' || !Array.isArray(node.children)) {
|
|
207
304
|
return;
|
|
@@ -223,6 +320,7 @@ export default function rehypeHeadingAnchors(options = {}) {
|
|
|
223
320
|
) {
|
|
224
321
|
const id = child.properties.id;
|
|
225
322
|
const decision = collapsibleHeadings.get(child) ?? { collapsible: false };
|
|
323
|
+
const card = cardHeadings.get(child);
|
|
226
324
|
const label = `Section titled “${headingText(child)}”`;
|
|
227
325
|
node.children[index] = {
|
|
228
326
|
type: 'element',
|
|
@@ -235,6 +333,7 @@ export default function rehypeHeadingAnchors(options = {}) {
|
|
|
235
333
|
'data-default-open': String(decision.defaultOpen),
|
|
236
334
|
}
|
|
237
335
|
: {}),
|
|
336
|
+
...(card ? { 'data-card': card } : {}),
|
|
238
337
|
},
|
|
239
338
|
children: [child, anchorLink(id, label)],
|
|
240
339
|
};
|
|
@@ -17,8 +17,13 @@ export function explainKeysIn(sources: any): string[];
|
|
|
17
17
|
* more than one (a ratio like `\mathrm{KE}/m`, explain-wrapped on both
|
|
18
18
|
* sides). `undefined` when nothing resolved, so the caller leaves the
|
|
19
19
|
* header exactly as it was rather than inserting an empty/placeholder label.
|
|
20
|
+
*
|
|
21
|
+
* An operator entry (`latex: \hat{}`, rule N22) wraps the symbol it
|
|
22
|
+
* decorates, so its key comes just before that symbol's: the two read as one
|
|
23
|
+
* phrase, `"Estimate of regression coefficient"`. The symbol's units are
|
|
24
|
+
* dropped there, since the operator may change them (a time derivative does).
|
|
20
25
|
*/
|
|
21
|
-
export function composeHeaderLabel(keys: any, notationResolved: any):
|
|
26
|
+
export function composeHeaderLabel(keys: any, notationResolved: any): string | undefined;
|
|
22
27
|
/**
|
|
23
28
|
* See the module doc above. No plugin options — everything it needs is
|
|
24
29
|
* already on `file.data.notationResolved` by the time it runs.
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
* author typed them or `remark-notation.mjs` auto-inserted them).
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
+
import { decorationOfEntry } from './math-glyphs.mjs';
|
|
36
|
+
|
|
35
37
|
// Mirrors `gloss.ts`'s exported `formulaExplainKeys` regex. Not imported:
|
|
36
38
|
// this file is a plain `.mjs` reference module, copied verbatim into `dist/`
|
|
37
39
|
// by `scripts/copy-assets.mjs`, never touched by the tsup build that
|
|
@@ -108,12 +110,28 @@ export function explainKeysIn(sources) {
|
|
|
108
110
|
* more than one (a ratio like `\mathrm{KE}/m`, explain-wrapped on both
|
|
109
111
|
* sides). `undefined` when nothing resolved, so the caller leaves the
|
|
110
112
|
* header exactly as it was rather than inserting an empty/placeholder label.
|
|
113
|
+
*
|
|
114
|
+
* An operator entry (`latex: \hat{}`, rule N22) wraps the symbol it
|
|
115
|
+
* decorates, so its key comes just before that symbol's: the two read as one
|
|
116
|
+
* phrase, `"Estimate of regression coefficient"`. The symbol's units are
|
|
117
|
+
* dropped there, since the operator may change them (a time derivative does).
|
|
111
118
|
*/
|
|
112
119
|
export function composeHeaderLabel(keys, notationResolved) {
|
|
113
|
-
const
|
|
120
|
+
const entries = keys
|
|
114
121
|
.map((key) => notationResolved[key])
|
|
115
|
-
.filter((entry) => entry && typeof entry.label === 'string')
|
|
116
|
-
|
|
122
|
+
.filter((entry) => entry && typeof entry.label === 'string');
|
|
123
|
+
const parts = [];
|
|
124
|
+
for (let index = 0; index < entries.length; index++) {
|
|
125
|
+
const entry = entries[index];
|
|
126
|
+
const operand = entries[index + 1];
|
|
127
|
+
if (operand && decorationOfEntry(entry.notation) !== undefined) {
|
|
128
|
+
const name = operand.label.charAt(0).toLowerCase() + operand.label.slice(1);
|
|
129
|
+
parts.push(`${entry.label} of ${name}`);
|
|
130
|
+
index += 1;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
parts.push(entry.units ? `${entry.label} (${entry.units})` : entry.label);
|
|
134
|
+
}
|
|
117
135
|
return parts.length > 0 ? parts.join(' / ') : undefined;
|
|
118
136
|
}
|
|
119
137
|
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starlight asides in an explico course (docs/plans/titles-and-containers.md
|
|
3
|
+
* Q10):
|
|
4
|
+
*
|
|
5
|
+
* :::note[Optional title]{icon="heart"}
|
|
6
|
+
* Body Markdown.
|
|
7
|
+
* :::
|
|
8
|
+
*
|
|
9
|
+
* **Why explico renders them.** Starlight turns these directives into asides
|
|
10
|
+
* only for files under its own `src/content/docs/` (or a
|
|
11
|
+
* `markdown.processedDirs` entry). An explico course keeps its lessons in
|
|
12
|
+
* `content/docs/`, and adding that directory to `processedDirs` would also
|
|
13
|
+
* run Starlight's heading-link pass over headings `rehype-heading-anchors.mjs`
|
|
14
|
+
* has already wrapped, giving every heading two anchors. So this pass turns
|
|
15
|
+
* each `:::note` / `:::tip` / `:::caution` / `:::danger` into Starlight's own
|
|
16
|
+
* `<Aside>` component (importing it into the page): Starlight still draws the
|
|
17
|
+
* aside, its translated default title, its icon per type, and its check that
|
|
18
|
+
* a named icon exists. `surfaces.css` gives it the aside tier's look.
|
|
19
|
+
*
|
|
20
|
+
* A `.md` lesson has no JSX, so there the aside is written out as the same
|
|
21
|
+
* markup with no icon — Starlight's icon set is not a public export.
|
|
22
|
+
*
|
|
23
|
+
* **An empty icon drops it.** Starlight reads `icon=""` as "the type's
|
|
24
|
+
* default", so an author has no way to say "none". Here `:::note{icon=""}`
|
|
25
|
+
* and `<Aside icon="">` lose the attribute and are wrapped in
|
|
26
|
+
* `<div class="ex-aside-no-icon">`, which `surfaces.css` keys the icon's
|
|
27
|
+
* removal on.
|
|
28
|
+
*
|
|
29
|
+
* Runs last in the course's `remarkPlugins`, after `remarkNotation` has
|
|
30
|
+
* resolved the `[[…]]` references inside an aside's body. Starlight appends its
|
|
31
|
+
* own plugins after the course's, so if a course's lessons do sit under
|
|
32
|
+
* Starlight's path, its aside pass finds nothing left to convert.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const ASIDE_TYPES = new Set(['note', 'tip', 'caution', 'danger']);
|
|
36
|
+
const DEFAULT_TITLES = { note: 'Note', tip: 'Tip', caution: 'Caution', danger: 'Danger' };
|
|
37
|
+
export const NO_ICON_CLASS = 'ex-aside-no-icon';
|
|
38
|
+
export const ASIDE_COMPONENT = 'ExplicoStarlightAside';
|
|
39
|
+
|
|
40
|
+
function textOf(node) {
|
|
41
|
+
if (typeof node?.value === 'string') return node.value;
|
|
42
|
+
return (node?.children ?? []).map(textOf).join('');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isAsideDirective(node) {
|
|
46
|
+
return node?.type === 'containerDirective' && ASIDE_TYPES.has(node.name);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isEmptyIconComponent(node) {
|
|
50
|
+
return (
|
|
51
|
+
node?.type === 'mdxJsxFlowElement' &&
|
|
52
|
+
node.name === 'Aside' &&
|
|
53
|
+
(node.attributes ?? []).some(
|
|
54
|
+
(attribute) =>
|
|
55
|
+
attribute?.type === 'mdxJsxAttribute' &&
|
|
56
|
+
attribute.name === 'icon' &&
|
|
57
|
+
attribute.value === '',
|
|
58
|
+
)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function noIconWrapper(node) {
|
|
63
|
+
return {
|
|
64
|
+
type: 'explicoAsideNoIcon',
|
|
65
|
+
data: { hName: 'div', hProperties: { className: [NO_ICON_CLASS] } },
|
|
66
|
+
children: [node],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The directive's `[label]` paragraph, removed from its body. */
|
|
71
|
+
function takeLabel(node) {
|
|
72
|
+
const first = node.children?.[0];
|
|
73
|
+
if (first?.type === 'paragraph' && first.data?.directiveLabel) {
|
|
74
|
+
node.children.splice(0, 1);
|
|
75
|
+
return first.children;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function asComponent(node) {
|
|
81
|
+
const type = node.name;
|
|
82
|
+
const icon = node.attributes?.icon;
|
|
83
|
+
const label = takeLabel(node);
|
|
84
|
+
const attributes = [{ type: 'mdxJsxAttribute', name: 'type', value: type }];
|
|
85
|
+
if (label) {
|
|
86
|
+
attributes.push({ type: 'mdxJsxAttribute', name: 'title', value: label.map(textOf).join('') });
|
|
87
|
+
}
|
|
88
|
+
if (icon) attributes.push({ type: 'mdxJsxAttribute', name: 'icon', value: icon });
|
|
89
|
+
const aside = {
|
|
90
|
+
type: 'mdxJsxFlowElement',
|
|
91
|
+
name: ASIDE_COMPONENT,
|
|
92
|
+
attributes,
|
|
93
|
+
children: node.children,
|
|
94
|
+
position: node.position,
|
|
95
|
+
};
|
|
96
|
+
return icon === '' ? noIconWrapper(aside) : aside;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function asMarkup(node) {
|
|
100
|
+
const type = node.name;
|
|
101
|
+
const label = takeLabel(node);
|
|
102
|
+
const title = label ?? [{ type: 'text', value: DEFAULT_TITLES[type] }];
|
|
103
|
+
return {
|
|
104
|
+
type: 'explicoAside',
|
|
105
|
+
data: {
|
|
106
|
+
hName: 'aside',
|
|
107
|
+
hProperties: {
|
|
108
|
+
ariaLabel: label ? label.map(textOf).join('') : DEFAULT_TITLES[type],
|
|
109
|
+
className: ['starlight-aside', `starlight-aside--${type}`],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
position: node.position,
|
|
113
|
+
children: [
|
|
114
|
+
{
|
|
115
|
+
type: 'paragraph',
|
|
116
|
+
data: { hProperties: { className: ['starlight-aside__title'], ariaHidden: 'true' } },
|
|
117
|
+
children: title,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: 'explicoAsideContent',
|
|
121
|
+
data: { hName: 'div', hProperties: { className: ['starlight-aside__content'] } },
|
|
122
|
+
children: node.children,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** `import { Aside as ExplicoStarlightAside } from '@astrojs/starlight/components';` */
|
|
129
|
+
function asideImport() {
|
|
130
|
+
const source = '@astrojs/starlight/components';
|
|
131
|
+
return {
|
|
132
|
+
type: 'mdxjsEsm',
|
|
133
|
+
value: `import { Aside as ${ASIDE_COMPONENT} } from '${source}';`,
|
|
134
|
+
data: {
|
|
135
|
+
estree: {
|
|
136
|
+
type: 'Program',
|
|
137
|
+
sourceType: 'module',
|
|
138
|
+
body: [
|
|
139
|
+
{
|
|
140
|
+
type: 'ImportDeclaration',
|
|
141
|
+
specifiers: [
|
|
142
|
+
{
|
|
143
|
+
type: 'ImportSpecifier',
|
|
144
|
+
imported: { type: 'Identifier', name: 'Aside' },
|
|
145
|
+
local: { type: 'Identifier', name: ASIDE_COMPONENT },
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
source: { type: 'Literal', value: source, raw: `'${source}'` },
|
|
149
|
+
attributes: [],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default function remarkAsides() {
|
|
158
|
+
return function transform(tree, file) {
|
|
159
|
+
const mdx = !/\.md$/i.test(String(file?.path ?? ''));
|
|
160
|
+
let used = false;
|
|
161
|
+
const walk = (node) => {
|
|
162
|
+
if (!Array.isArray(node?.children)) return;
|
|
163
|
+
node.children.forEach((child, index) => {
|
|
164
|
+
walk(child); // an aside may hold another
|
|
165
|
+
if (isAsideDirective(child)) {
|
|
166
|
+
node.children[index] = mdx ? asComponent(child) : asMarkup(child);
|
|
167
|
+
used ||= mdx;
|
|
168
|
+
} else if (isEmptyIconComponent(child)) {
|
|
169
|
+
child.attributes = child.attributes.filter(
|
|
170
|
+
(attribute) =>
|
|
171
|
+
!(attribute?.type === 'mdxJsxAttribute' && attribute.name === 'icon'),
|
|
172
|
+
);
|
|
173
|
+
node.children[index] = noIconWrapper(child);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
walk(tree);
|
|
178
|
+
if (used) tree.children.unshift(asideImport());
|
|
179
|
+
};
|
|
180
|
+
}
|
|
@@ -7,8 +7,10 @@ export function parseMemoRef(raw: any): {
|
|
|
7
7
|
* retaining author attributes and body source verbatim.
|
|
8
8
|
*/
|
|
9
9
|
export function preprocessMemos(source: any): string;
|
|
10
|
+
/** The per-page memo letter: 1 → `a`, 26 → `z`, 27 → `aa`, … */
|
|
11
|
+
export function memoLetter(number: any): string;
|
|
10
12
|
/**
|
|
11
|
-
*
|
|
13
|
+
* Letter memo blocks from `a` on every page and resolve same-page
|
|
12
14
|
* `[[memo-id]]` references. Numbering is intentionally page-local.
|
|
13
15
|
*
|
|
14
16
|
* @this {any}
|
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The complete calculation is recorded in [[memo-derivation]].
|
|
7
7
|
*
|
|
8
|
-
* <Memo id="derivation"
|
|
8
|
+
* <Memo id="derivation" caption="The algebra behind the result." short="Derivation">
|
|
9
9
|
* Several paragraphs, equations, tables, or figures.
|
|
10
10
|
*
|
|
11
11
|
* </Memo>
|
|
12
12
|
*
|
|
13
|
+
* A memo is named like every float (docs/plans/titles-and-containers.md Part
|
|
14
|
+
* 3): a required `caption`, shown on its bar and in its preview, and an
|
|
15
|
+
* optional `short` caption. Its marker is a superscript letter — `ᵃ`, `ᵇ`, …
|
|
16
|
+
* numbered per page — so it never reads as a citation's `[1]` (decision Q7).
|
|
17
|
+
*
|
|
13
18
|
* MDX normally rejects this before remark transforms run: an inline JSX node
|
|
14
19
|
* cannot contain block Markdown. The attacher therefore wraps remark-parse's
|
|
15
20
|
* parser and tags each memo before parsing. Legacy inline placements are still
|
|
@@ -118,6 +123,25 @@ export function preprocessMemos(source) {
|
|
|
118
123
|
return output;
|
|
119
124
|
}
|
|
120
125
|
|
|
126
|
+
/** The per-page memo letter: 1 → `a`, 26 → `z`, 27 → `aa`, … */
|
|
127
|
+
export function memoLetter(number) {
|
|
128
|
+
let n = Number(number);
|
|
129
|
+
let letters = '';
|
|
130
|
+
while (n > 0) {
|
|
131
|
+
n -= 1;
|
|
132
|
+
letters = String.fromCharCode(97 + (n % 26)) + letters;
|
|
133
|
+
n = Math.floor(n / 26);
|
|
134
|
+
}
|
|
135
|
+
return letters;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Name props a memo no longer takes, with the fix each error gives.
|
|
139
|
+
const REMOVED_PROPS = {
|
|
140
|
+
title:
|
|
141
|
+
'a memo is named by its `caption` (shown on its bar and in its preview) and an optional `short` caption; move a short title to `short`.',
|
|
142
|
+
description: 'fold it into `caption`, which is shown on the bar and in the preview.',
|
|
143
|
+
};
|
|
144
|
+
|
|
121
145
|
function fail(file, message, node) {
|
|
122
146
|
if (typeof file?.fail === 'function') file.fail(message, node);
|
|
123
147
|
const location = file?.path ? `${file.path}: ` : '';
|
|
@@ -152,7 +176,7 @@ function markerNode(memo) {
|
|
|
152
176
|
children: [
|
|
153
177
|
{
|
|
154
178
|
type: 'text',
|
|
155
|
-
value: memo.label ??
|
|
179
|
+
value: memo.label ?? memo.number,
|
|
156
180
|
},
|
|
157
181
|
],
|
|
158
182
|
data: {
|
|
@@ -163,9 +187,9 @@ function markerNode(memo) {
|
|
|
163
187
|
'aria-controls': memo.anchorId,
|
|
164
188
|
'aria-expanded': 'false',
|
|
165
189
|
'aria-label': memo.label
|
|
166
|
-
? `${memo.label}: ${memo.
|
|
167
|
-
: `Memo ${memo.number}: ${memo.
|
|
168
|
-
title: memo.
|
|
190
|
+
? `${memo.label}: ${memo.name}`
|
|
191
|
+
: `Memo ${memo.number}: ${memo.name}`,
|
|
192
|
+
title: memo.name,
|
|
169
193
|
},
|
|
170
194
|
},
|
|
171
195
|
};
|
|
@@ -227,7 +251,7 @@ function rewriteMemoReferences(node, memos, file) {
|
|
|
227
251
|
}
|
|
228
252
|
|
|
229
253
|
/**
|
|
230
|
-
*
|
|
254
|
+
* Letter memo blocks from `a` on every page and resolve same-page
|
|
231
255
|
* `[[memo-id]]` references. Numbering is intentionally page-local.
|
|
232
256
|
*
|
|
233
257
|
* @this {any}
|
|
@@ -295,9 +319,14 @@ export default function remarkMemo() {
|
|
|
295
319
|
fail(file, '<Memo> did not become a block-level MDX element.', node);
|
|
296
320
|
}
|
|
297
321
|
|
|
322
|
+
for (const [prop, fix] of Object.entries(REMOVED_PROPS)) {
|
|
323
|
+
if (mdxAttributesNamed(node, prop).length > 0) {
|
|
324
|
+
fail(file, `<Memo> has no \`${prop}\` prop: ${fix}`, node);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
298
327
|
const id = literalAttribute(node, 'id', file);
|
|
299
|
-
const
|
|
300
|
-
literalAttribute(node, '
|
|
328
|
+
const caption = literalAttribute(node, 'caption', file);
|
|
329
|
+
const short = literalAttribute(node, 'short', file, { optional: true });
|
|
301
330
|
const label = literalAttribute(node, 'label', file, { optional: true });
|
|
302
331
|
if (!KEYED_FLOAT_KEY_PATTERN.test(id)) {
|
|
303
332
|
fail(
|
|
@@ -312,18 +341,20 @@ export default function remarkMemo() {
|
|
|
312
341
|
seenIds.add(id);
|
|
313
342
|
|
|
314
343
|
number += 1;
|
|
344
|
+
const letter = memoLetter(number);
|
|
315
345
|
const anchorId = `memo-${id}`;
|
|
316
346
|
removeMdxAttribute(node, BLOCK_ATTRIBUTE);
|
|
317
347
|
injectMdxStringAttributes(node, {
|
|
318
|
-
number:
|
|
348
|
+
number: letter,
|
|
319
349
|
anchorId,
|
|
320
350
|
});
|
|
321
351
|
|
|
322
352
|
const memo = {
|
|
323
353
|
id,
|
|
324
|
-
|
|
354
|
+
// What the marker's tooltip and accessible name call it.
|
|
355
|
+
name: short ?? caption,
|
|
325
356
|
label,
|
|
326
|
-
number:
|
|
357
|
+
number: letter,
|
|
327
358
|
anchorId,
|
|
328
359
|
};
|
|
329
360
|
memos.set(id, memo);
|