explico 0.3.0 → 0.4.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 +72 -1
- package/README.md +21 -2
- package/dist/compiler/astro.d.ts +30 -0
- package/dist/compiler/astro.js +79 -0
- package/dist/compiler/cli.js +5 -9
- package/dist/compiler/course-config.d.ts +29 -4
- package/dist/compiler/course-config.js +12 -0
- package/dist/compiler/lesson-derivation.d.ts +1 -1
- package/dist/compiler/lesson-derivation.js +2 -2
- package/dist/compiler/manifest.d.ts +25 -4
- package/dist/compiler/manifest.js +59 -27
- package/dist/reference/equations-validate.d.ts +8 -3
- package/dist/reference/equations-validate.js +1 -1
- package/dist/reference/equations.d.mts +58 -22
- package/dist/reference/equations.mjs +75 -26
- package/dist/reference/keyed-float-validate.d.ts +9 -3
- package/dist/reference/keyed-float-validate.js +1 -1
- package/dist/reference/keyed-jsx-floats.d.mts +18 -10
- package/dist/reference/keyed-jsx-floats.mjs +42 -18
- package/dist/reference/remark-notation.d.mts +54 -5
- package/dist/reference/remark-notation.mjs +161 -17
- package/docs/authoring/README.md +4 -0
- package/docs/authoring/migrating.md +62 -0
- package/docs/authoring/notation.md +8 -7
- package/docs/authoring/structure.md +139 -0
- package/package.json +1 -1
- package/src/compiler/astro.ts +127 -0
- package/src/compiler/cli.ts +5 -9
- package/src/compiler/course-config.ts +39 -3
- package/src/compiler/lesson-derivation.ts +2 -2
- package/src/compiler/manifest.ts +108 -36
- package/src/components/notation/NotationLayer.astro +11 -0
- package/src/components/starlight/LayoutSidebar.astro +62 -1
- package/src/components/starlight/LessonFooter.astro +11 -0
- package/src/components/table/Table.astro +3 -2
- package/src/reference/equations-validate.ts +6 -4
- package/src/reference/equations.mjs +75 -26
- package/src/reference/keyed-float-validate.ts +7 -4
- package/src/reference/keyed-jsx-floats.mjs +42 -18
- package/src/reference/remark-notation.mjs +161 -17
- package/src/styles/global.css +129 -0
|
@@ -9,12 +9,28 @@ export function parseEquationRef(raw: any): {
|
|
|
9
9
|
/** Remove every `\label{eq:…}` (and the whitespace it leaves) from a math source. */
|
|
10
10
|
export function stripEquationLabels(latex: any): string;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Normalise the numbering input to `{ prefix, offset }`.
|
|
13
|
+
*
|
|
14
|
+
* `prefix` is the number before the dot — the **part**'s ordinal (its position
|
|
15
|
+
* among the sidebar groups). `offset` is how many display equations already
|
|
16
|
+
* appeared in earlier lessons of that same part, so `n` runs continuously
|
|
17
|
+
* across a part instead of restarting on every page. A bare number is the
|
|
18
|
+
* legacy per-lesson form (`prefix = n`, `offset = 0`) and is still accepted so
|
|
19
|
+
* hand-built trees and unit tests keep working. `undefined`/`null`/`0` ⇒ the
|
|
20
|
+
* `##`-section fallback for non-lesson pages.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveNumbering(input: any): {
|
|
23
|
+
prefix: any;
|
|
24
|
+
offset: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The visible number for one display equation. With a `prefix` (the part's
|
|
28
|
+
* ordinal) the scheme is `‹part›.‹offset + indexInLesson›` and `##` headings
|
|
14
29
|
* are ignored; without it, the legacy `##`-section scheme.
|
|
15
30
|
*/
|
|
16
|
-
export function displayEquationNumber({
|
|
17
|
-
|
|
31
|
+
export function displayEquationNumber({ prefix, offset, section, indexInSection, indexInLesson, }: {
|
|
32
|
+
prefix: any;
|
|
33
|
+
offset?: number | undefined;
|
|
18
34
|
section: any;
|
|
19
35
|
indexInSection: any;
|
|
20
36
|
indexInLesson: any;
|
|
@@ -38,28 +54,43 @@ export function stripFencedCode(markdown: any): string;
|
|
|
38
54
|
*/
|
|
39
55
|
export function stripJsxComponents(markdown: any, keep: any): string;
|
|
40
56
|
/**
|
|
41
|
-
* Every
|
|
42
|
-
* its assigned number. `$$…$$` blocks only. With `
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
57
|
+
* Every display equation in a Markdown body, keyed or not, in appearance order
|
|
58
|
+
* with its assigned number. `$$…$$` blocks only. With `numbering`
|
|
59
|
+
* (`{ prefix, offset }`, the part's ordinal and the count of display equations
|
|
60
|
+
* in earlier lessons of that part) the number is `‹part›.‹offset + n›`, `n`
|
|
61
|
+
* sequential over every display block in the lesson; without it, `##` headings
|
|
62
|
+
* advance a section counter and the number is `‹section›.‹indexInSection›`.
|
|
63
|
+
* Either way every display block advances the index, so a keyed equation's
|
|
64
|
+
* number reflects its position among *all* display equations, not only the
|
|
65
|
+
* keyed ones.
|
|
48
66
|
*/
|
|
49
|
-
export function
|
|
50
|
-
key: string;
|
|
67
|
+
export function scanDisplayEquations(markdown: any, numbering: any): {
|
|
68
|
+
key: string | null | undefined;
|
|
69
|
+
number: string;
|
|
70
|
+
section: number;
|
|
71
|
+
indexInSection: number;
|
|
72
|
+
}[];
|
|
73
|
+
/** The keyed subset of `scanDisplayEquations` — the manifest builder's view. */
|
|
74
|
+
export function scanEquationLabels(markdown: any, numbering: any): {
|
|
75
|
+
key: string | null | undefined;
|
|
51
76
|
number: string;
|
|
52
77
|
section: number;
|
|
53
78
|
indexInSection: number;
|
|
54
79
|
}[];
|
|
80
|
+
/**
|
|
81
|
+
* How many display equations a lesson body holds. This is what makes `n` run
|
|
82
|
+
* continuously across a part: a lesson's `offset` is the sum of this over every
|
|
83
|
+
* earlier lesson in the same part.
|
|
84
|
+
*/
|
|
85
|
+
export function countDisplayEquations(markdown: any): number;
|
|
55
86
|
/**
|
|
56
87
|
* The mdast equivalent of `scanEquationLabels` for *every* display equation:
|
|
57
88
|
* walk the tree in document order, advancing the section counter on every
|
|
58
89
|
* depth-2 heading and emitting a record for every `math` node. `key` is the
|
|
59
|
-
* `\label{eq:…}` payload when present, else `null`. `
|
|
90
|
+
* `\label{eq:…}` payload when present, else `null`. `numbering` is threaded the
|
|
60
91
|
* same way as in `scanEquationLabels`.
|
|
61
92
|
*/
|
|
62
|
-
export function collectDisplayEquations(tree: any,
|
|
93
|
+
export function collectDisplayEquations(tree: any, numbering: any): {
|
|
63
94
|
key: string | null;
|
|
64
95
|
number: string;
|
|
65
96
|
section: number;
|
|
@@ -70,7 +101,7 @@ export function collectDisplayEquations(tree: any, chapter: any): {
|
|
|
70
101
|
* The keyed subset of `collectDisplayEquations` — the tree-walk counterpart of
|
|
71
102
|
* `scanEquationLabels`. Numbers reflect position among all display equations.
|
|
72
103
|
*/
|
|
73
|
-
export function collectEquationLabels(tree: any,
|
|
104
|
+
export function collectEquationLabels(tree: any, numbering: any): {
|
|
74
105
|
key: string | null;
|
|
75
106
|
number: string;
|
|
76
107
|
section: number;
|
|
@@ -81,12 +112,17 @@ export function collectEquationLabels(tree: any, chapter: any): {
|
|
|
81
112
|
* Equation identity (Step D7, extended F3) — shared, framework-free helpers.
|
|
82
113
|
*
|
|
83
114
|
* Every display equation (`$$…$$`) in a lesson is numbered and given an `id`
|
|
84
|
-
* from appearance order. When the caller supplies
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* the
|
|
89
|
-
*
|
|
115
|
+
* from appearance order. When the caller supplies `numbering`
|
|
116
|
+
* (`{ prefix, offset }` — see `resolveNumbering`), the number is
|
|
117
|
+
* `‹part›.‹offset + n›`: the prefix is the **part**'s ordinal among the sidebar
|
|
118
|
+
* groups, `n` counts every display equation in the lesson, and `offset` carries
|
|
119
|
+
* the count from earlier lessons of the same part so the sequence runs
|
|
120
|
+
* continuously across a part rather than restarting on every page. `##` and
|
|
121
|
+
* `###` headings never affect it — `(2.4)` is the fourth display equation of
|
|
122
|
+
* part 2, wherever in that part it sits. See `docs/authoring/structure.md` for
|
|
123
|
+
* the level names. With no `numbering` the number falls back to the
|
|
124
|
+
* `##`-section scheme `‹section›.‹indexInSection›` (used by hand-built test
|
|
125
|
+
* trees and non-lesson pages). An author additionally
|
|
90
126
|
* marks an equation with a trailing `\label{eq:<key>}` inside its `$$…$$`
|
|
91
127
|
* block (inside math, so the `:` is safe and the form matches amsmath) when
|
|
92
128
|
* they want a *stable* anchor that survives edits and cross-page references:
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
* Equation identity (Step D7, extended F3) — shared, framework-free helpers.
|
|
3
3
|
*
|
|
4
4
|
* Every display equation (`$$…$$`) in a lesson is numbered and given an `id`
|
|
5
|
-
* from appearance order. When the caller supplies
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* the
|
|
10
|
-
*
|
|
5
|
+
* from appearance order. When the caller supplies `numbering`
|
|
6
|
+
* (`{ prefix, offset }` — see `resolveNumbering`), the number is
|
|
7
|
+
* `‹part›.‹offset + n›`: the prefix is the **part**'s ordinal among the sidebar
|
|
8
|
+
* groups, `n` counts every display equation in the lesson, and `offset` carries
|
|
9
|
+
* the count from earlier lessons of the same part so the sequence runs
|
|
10
|
+
* continuously across a part rather than restarting on every page. `##` and
|
|
11
|
+
* `###` headings never affect it — `(2.4)` is the fourth display equation of
|
|
12
|
+
* part 2, wherever in that part it sits. See `docs/authoring/structure.md` for
|
|
13
|
+
* the level names. With no `numbering` the number falls back to the
|
|
14
|
+
* `##`-section scheme `‹section›.‹indexInSection›` (used by hand-built test
|
|
15
|
+
* trees and non-lesson pages). An author additionally
|
|
11
16
|
* marks an equation with a trailing `\label{eq:<key>}` inside its `$$…$$`
|
|
12
17
|
* block (inside math, so the `:` is safe and the form matches amsmath) when
|
|
13
18
|
* they want a *stable* anchor that survives edits and cross-page references:
|
|
@@ -63,19 +68,42 @@ function equationNumber(section, index) {
|
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
71
|
+
* Normalise the numbering input to `{ prefix, offset }`.
|
|
72
|
+
*
|
|
73
|
+
* `prefix` is the number before the dot — the **part**'s ordinal (its position
|
|
74
|
+
* among the sidebar groups). `offset` is how many display equations already
|
|
75
|
+
* appeared in earlier lessons of that same part, so `n` runs continuously
|
|
76
|
+
* across a part instead of restarting on every page. A bare number is the
|
|
77
|
+
* legacy per-lesson form (`prefix = n`, `offset = 0`) and is still accepted so
|
|
78
|
+
* hand-built trees and unit tests keep working. `undefined`/`null`/`0` ⇒ the
|
|
79
|
+
* `##`-section fallback for non-lesson pages.
|
|
80
|
+
*/
|
|
81
|
+
export function resolveNumbering(input) {
|
|
82
|
+
if (input == null) return { prefix: undefined, offset: 0 };
|
|
83
|
+
if (typeof input === 'number') {
|
|
84
|
+
return { prefix: input || undefined, offset: 0 };
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
prefix: input.prefix || undefined,
|
|
88
|
+
offset: Number(input.offset) || 0,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The visible number for one display equation. With a `prefix` (the part's
|
|
94
|
+
* ordinal) the scheme is `‹part›.‹offset + indexInLesson›` and `##` headings
|
|
68
95
|
* are ignored; without it, the legacy `##`-section scheme.
|
|
69
96
|
*/
|
|
70
97
|
export function displayEquationNumber({
|
|
71
|
-
|
|
98
|
+
prefix,
|
|
99
|
+
offset = 0,
|
|
72
100
|
section,
|
|
73
101
|
indexInSection,
|
|
74
102
|
indexInLesson,
|
|
75
103
|
}) {
|
|
76
|
-
return
|
|
104
|
+
return prefix == null
|
|
77
105
|
? equationNumber(section, indexInSection)
|
|
78
|
-
: `${
|
|
106
|
+
: `${prefix}.${offset + indexInLesson}`;
|
|
79
107
|
}
|
|
80
108
|
|
|
81
109
|
/** The positional anchor id for an unkeyed equation, e.g. `2.4` → `eq-2-4`. */
|
|
@@ -109,15 +137,18 @@ export function stripJsxComponents(markdown, keep) {
|
|
|
109
137
|
}
|
|
110
138
|
|
|
111
139
|
/**
|
|
112
|
-
* Every
|
|
113
|
-
* its assigned number. `$$…$$` blocks only. With `
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
140
|
+
* Every display equation in a Markdown body, keyed or not, in appearance order
|
|
141
|
+
* with its assigned number. `$$…$$` blocks only. With `numbering`
|
|
142
|
+
* (`{ prefix, offset }`, the part's ordinal and the count of display equations
|
|
143
|
+
* in earlier lessons of that part) the number is `‹part›.‹offset + n›`, `n`
|
|
144
|
+
* sequential over every display block in the lesson; without it, `##` headings
|
|
145
|
+
* advance a section counter and the number is `‹section›.‹indexInSection›`.
|
|
146
|
+
* Either way every display block advances the index, so a keyed equation's
|
|
147
|
+
* number reflects its position among *all* display equations, not only the
|
|
148
|
+
* keyed ones.
|
|
119
149
|
*/
|
|
120
|
-
export function
|
|
150
|
+
export function scanDisplayEquations(markdown, numbering) {
|
|
151
|
+
const { prefix, offset } = resolveNumbering(numbering);
|
|
121
152
|
const text = stripJsxComponents(stripFencedCode(markdown));
|
|
122
153
|
const tokens = [];
|
|
123
154
|
const heading = /^[ \t]{0,3}##[ \t]+\S/gm;
|
|
@@ -148,11 +179,11 @@ export function scanEquationLabels(markdown, chapter) {
|
|
|
148
179
|
}
|
|
149
180
|
indexInSection += 1;
|
|
150
181
|
indexInLesson += 1;
|
|
151
|
-
if (token.key == null) continue;
|
|
152
182
|
labels.push({
|
|
153
183
|
key: token.key,
|
|
154
184
|
number: displayEquationNumber({
|
|
155
|
-
|
|
185
|
+
prefix,
|
|
186
|
+
offset,
|
|
156
187
|
section,
|
|
157
188
|
indexInSection,
|
|
158
189
|
indexInLesson,
|
|
@@ -164,14 +195,31 @@ export function scanEquationLabels(markdown, chapter) {
|
|
|
164
195
|
return labels;
|
|
165
196
|
}
|
|
166
197
|
|
|
198
|
+
/** The keyed subset of `scanDisplayEquations` — the manifest builder's view. */
|
|
199
|
+
export function scanEquationLabels(markdown, numbering) {
|
|
200
|
+
return scanDisplayEquations(markdown, numbering).filter(
|
|
201
|
+
(equation) => equation.key != null,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* How many display equations a lesson body holds. This is what makes `n` run
|
|
207
|
+
* continuously across a part: a lesson's `offset` is the sum of this over every
|
|
208
|
+
* earlier lesson in the same part.
|
|
209
|
+
*/
|
|
210
|
+
export function countDisplayEquations(markdown) {
|
|
211
|
+
return scanDisplayEquations(markdown).length;
|
|
212
|
+
}
|
|
213
|
+
|
|
167
214
|
/**
|
|
168
215
|
* The mdast equivalent of `scanEquationLabels` for *every* display equation:
|
|
169
216
|
* walk the tree in document order, advancing the section counter on every
|
|
170
217
|
* depth-2 heading and emitting a record for every `math` node. `key` is the
|
|
171
|
-
* `\label{eq:…}` payload when present, else `null`. `
|
|
218
|
+
* `\label{eq:…}` payload when present, else `null`. `numbering` is threaded the
|
|
172
219
|
* same way as in `scanEquationLabels`.
|
|
173
220
|
*/
|
|
174
|
-
export function collectDisplayEquations(tree,
|
|
221
|
+
export function collectDisplayEquations(tree, numbering) {
|
|
222
|
+
const { prefix, offset } = resolveNumbering(numbering);
|
|
175
223
|
const equations = [];
|
|
176
224
|
let section = 0;
|
|
177
225
|
let indexInSection = 0;
|
|
@@ -196,7 +244,8 @@ export function collectDisplayEquations(tree, chapter) {
|
|
|
196
244
|
equations.push({
|
|
197
245
|
key: label ? label[1] : null,
|
|
198
246
|
number: displayEquationNumber({
|
|
199
|
-
|
|
247
|
+
prefix,
|
|
248
|
+
offset,
|
|
200
249
|
section,
|
|
201
250
|
indexInSection,
|
|
202
251
|
indexInLesson,
|
|
@@ -214,8 +263,8 @@ export function collectDisplayEquations(tree, chapter) {
|
|
|
214
263
|
* The keyed subset of `collectDisplayEquations` — the tree-walk counterpart of
|
|
215
264
|
* `scanEquationLabels`. Numbers reflect position among all display equations.
|
|
216
265
|
*/
|
|
217
|
-
export function collectEquationLabels(tree,
|
|
218
|
-
return collectDisplayEquations(tree,
|
|
266
|
+
export function collectEquationLabels(tree, numbering) {
|
|
267
|
+
return collectDisplayEquations(tree, numbering).filter(
|
|
219
268
|
(equation) => equation.key != null,
|
|
220
269
|
);
|
|
221
270
|
}
|
|
@@ -57,10 +57,16 @@ interface KeyedFloatLessonInput {
|
|
|
57
57
|
readonly file: string;
|
|
58
58
|
readonly body: string;
|
|
59
59
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
60
|
+
* `{ prefix, offset }` — the part's 1-based ordinal among the sidebar groups,
|
|
61
|
+
* and how many floats of this kind earlier lessons of that part already used.
|
|
62
|
+
* When set, floats are numbered `‹part›.‹offset + n›`; when omitted, the
|
|
63
|
+
* legacy `##`-section scheme. Each kind counts on its own, so the caller
|
|
64
|
+
* passes a different `offset` for `<Table>` than for `<Figure>`.
|
|
62
65
|
*/
|
|
63
|
-
readonly
|
|
66
|
+
readonly numbering?: {
|
|
67
|
+
readonly prefix: number;
|
|
68
|
+
readonly offset: number;
|
|
69
|
+
};
|
|
64
70
|
}
|
|
65
71
|
declare function validateKeyedFloats(lessons: readonly KeyedFloatLessonInput[], kind: KeyedFloatKind): KeyedFloatValidation;
|
|
66
72
|
|
|
@@ -32,7 +32,7 @@ function validateKeyedFloats(lessons, kind) {
|
|
|
32
32
|
const table = {};
|
|
33
33
|
for (const raw of scanKeyedJsxFloats(lesson.body, {
|
|
34
34
|
tagName,
|
|
35
|
-
|
|
35
|
+
numbering: lesson.numbering
|
|
36
36
|
})) {
|
|
37
37
|
if (raw.key == null) continue;
|
|
38
38
|
if (!KEYED_FLOAT_KEY_PATTERN.test(raw.key)) {
|
|
@@ -20,31 +20,39 @@ export function parseKeyedFloatRef(raw: any, { prefix }: {
|
|
|
20
20
|
/**
|
|
21
21
|
* The raw-text equivalent of `collectKeyedJsxFloats`, mirroring
|
|
22
22
|
* `scanEquationLabels`: every top-level `<TagName …>` in appearance order.
|
|
23
|
-
* With `
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
23
|
+
* With `numbering` (`{ prefix, offset }` — the part's ordinal and the count of
|
|
24
|
+
* this kind of float in earlier lessons of that part) the number is
|
|
25
|
+
* `‹part›.‹offset + n›`, `n` sequential over the whole lesson; without it, `##`
|
|
26
|
+
* headings advance a section counter. Other JSX components' bodies are blanked
|
|
27
|
+
* first so a `<Table>` nested inside e.g. `<CompactExample>` (and any `##`-like
|
|
28
|
+
* text inside an unrelated component) is never counted.
|
|
28
29
|
*/
|
|
29
|
-
export function scanKeyedJsxFloats(markdown: any, { tagName,
|
|
30
|
+
export function scanKeyedJsxFloats(markdown: any, { tagName, numbering }: {
|
|
30
31
|
tagName: any;
|
|
31
|
-
|
|
32
|
+
numbering: any;
|
|
32
33
|
}): {
|
|
33
34
|
key: string | null | undefined;
|
|
34
35
|
number: string;
|
|
35
36
|
section: number;
|
|
36
37
|
indexInSection: number;
|
|
37
38
|
}[];
|
|
39
|
+
/**
|
|
40
|
+
* How many floats of this kind a lesson body holds — the input to the next
|
|
41
|
+
* lesson's `offset` within the same part.
|
|
42
|
+
*/
|
|
43
|
+
export function countKeyedJsxFloats(markdown: any, { tagName }: {
|
|
44
|
+
tagName: any;
|
|
45
|
+
}): number;
|
|
38
46
|
/**
|
|
39
47
|
* The mdast equivalent of `scanKeyedJsxFloats`: walk the tree's *top-level*
|
|
40
48
|
* children in document order, advancing the section counter on every
|
|
41
49
|
* depth-2 heading and emitting a record for every `mdxJsxFlowElement` named
|
|
42
|
-
* `tagName` — keyed or not, exactly like `collectDisplayEquations`. `
|
|
50
|
+
* `tagName` — keyed or not, exactly like `collectDisplayEquations`. `numbering`
|
|
43
51
|
* is threaded the same way as in `scanKeyedJsxFloats`.
|
|
44
52
|
*/
|
|
45
|
-
export function collectKeyedJsxFloats(tree: any, { tagName,
|
|
53
|
+
export function collectKeyedJsxFloats(tree: any, { tagName, numbering }: {
|
|
46
54
|
tagName: any;
|
|
47
|
-
|
|
55
|
+
numbering: any;
|
|
48
56
|
}): {
|
|
49
57
|
key: any;
|
|
50
58
|
number: string;
|
|
@@ -22,7 +22,11 @@
|
|
|
22
22
|
* `equations.test.ts` holds equations to.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
resolveNumbering,
|
|
27
|
+
stripFencedCode,
|
|
28
|
+
stripJsxComponents,
|
|
29
|
+
} from './equations.mjs';
|
|
26
30
|
|
|
27
31
|
/** Same shape as `EQ_KEY_PATTERN` — the notation-key grammar without dots. */
|
|
28
32
|
export const KEYED_FLOAT_KEY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -33,15 +37,22 @@ function floatNumber(section, index) {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
|
-
* The visible number for one float. With `
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* The visible number for one float. With a `prefix` (the part's ordinal) the
|
|
41
|
+
* scheme is `‹part›.‹offset + indexInLesson›` and `##` headings are ignored;
|
|
42
|
+
* without it, the legacy `##`-section scheme. Mirrors `displayEquationNumber`
|
|
43
|
+
* in `equations.mjs`. Each kind of float counts on its own — a `<Table>` never
|
|
44
|
+
* advances the `<Figure>` sequence — so `offset` is per kind too.
|
|
40
45
|
*/
|
|
41
|
-
function keyedFloatNumber({
|
|
42
|
-
|
|
46
|
+
function keyedFloatNumber({
|
|
47
|
+
prefix,
|
|
48
|
+
offset = 0,
|
|
49
|
+
section,
|
|
50
|
+
indexInSection,
|
|
51
|
+
indexInLesson,
|
|
52
|
+
}) {
|
|
53
|
+
return prefix == null
|
|
43
54
|
? floatNumber(section, indexInSection)
|
|
44
|
-
: `${
|
|
55
|
+
: `${prefix}.${offset + indexInLesson}`;
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
/** The anchor id for a float, e.g. `{prefix:'tab', key:'foo'}` → `tab-foo`. */
|
|
@@ -71,13 +82,15 @@ function readIdAttribute(attributesSource) {
|
|
|
71
82
|
/**
|
|
72
83
|
* The raw-text equivalent of `collectKeyedJsxFloats`, mirroring
|
|
73
84
|
* `scanEquationLabels`: every top-level `<TagName …>` in appearance order.
|
|
74
|
-
* With `
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
85
|
+
* With `numbering` (`{ prefix, offset }` — the part's ordinal and the count of
|
|
86
|
+
* this kind of float in earlier lessons of that part) the number is
|
|
87
|
+
* `‹part›.‹offset + n›`, `n` sequential over the whole lesson; without it, `##`
|
|
88
|
+
* headings advance a section counter. Other JSX components' bodies are blanked
|
|
89
|
+
* first so a `<Table>` nested inside e.g. `<CompactExample>` (and any `##`-like
|
|
90
|
+
* text inside an unrelated component) is never counted.
|
|
79
91
|
*/
|
|
80
|
-
export function scanKeyedJsxFloats(markdown, { tagName,
|
|
92
|
+
export function scanKeyedJsxFloats(markdown, { tagName, numbering }) {
|
|
93
|
+
const { prefix, offset } = resolveNumbering(numbering);
|
|
81
94
|
const text = stripJsxComponents(stripFencedCode(markdown), tagName);
|
|
82
95
|
const tokens = [];
|
|
83
96
|
const heading = /^[ \t]{0,3}##[ \t]+\S/gm;
|
|
@@ -106,7 +119,8 @@ export function scanKeyedJsxFloats(markdown, { tagName, chapter }) {
|
|
|
106
119
|
labels.push({
|
|
107
120
|
key: token.key,
|
|
108
121
|
number: keyedFloatNumber({
|
|
109
|
-
|
|
122
|
+
prefix,
|
|
123
|
+
offset,
|
|
110
124
|
section,
|
|
111
125
|
indexInSection,
|
|
112
126
|
indexInLesson,
|
|
@@ -118,14 +132,23 @@ export function scanKeyedJsxFloats(markdown, { tagName, chapter }) {
|
|
|
118
132
|
return labels;
|
|
119
133
|
}
|
|
120
134
|
|
|
135
|
+
/**
|
|
136
|
+
* How many floats of this kind a lesson body holds — the input to the next
|
|
137
|
+
* lesson's `offset` within the same part.
|
|
138
|
+
*/
|
|
139
|
+
export function countKeyedJsxFloats(markdown, { tagName }) {
|
|
140
|
+
return scanKeyedJsxFloats(markdown, { tagName }).length;
|
|
141
|
+
}
|
|
142
|
+
|
|
121
143
|
/**
|
|
122
144
|
* The mdast equivalent of `scanKeyedJsxFloats`: walk the tree's *top-level*
|
|
123
145
|
* children in document order, advancing the section counter on every
|
|
124
146
|
* depth-2 heading and emitting a record for every `mdxJsxFlowElement` named
|
|
125
|
-
* `tagName` — keyed or not, exactly like `collectDisplayEquations`. `
|
|
147
|
+
* `tagName` — keyed or not, exactly like `collectDisplayEquations`. `numbering`
|
|
126
148
|
* is threaded the same way as in `scanKeyedJsxFloats`.
|
|
127
149
|
*/
|
|
128
|
-
export function collectKeyedJsxFloats(tree, { tagName,
|
|
150
|
+
export function collectKeyedJsxFloats(tree, { tagName, numbering }) {
|
|
151
|
+
const { prefix, offset } = resolveNumbering(numbering);
|
|
129
152
|
const floats = [];
|
|
130
153
|
let section = 0;
|
|
131
154
|
let indexInSection = 0;
|
|
@@ -151,7 +174,8 @@ export function collectKeyedJsxFloats(tree, { tagName, chapter }) {
|
|
|
151
174
|
floats.push({
|
|
152
175
|
key: key ?? null,
|
|
153
176
|
number: keyedFloatNumber({
|
|
154
|
-
|
|
177
|
+
prefix,
|
|
178
|
+
offset,
|
|
155
179
|
section,
|
|
156
180
|
indexInSection,
|
|
157
181
|
indexInLesson,
|
|
@@ -1,29 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The numbering inputs a lesson page needs, read off one manifest.
|
|
3
|
+
*
|
|
4
|
+
* Every number on a page comes from two places that MUST agree: the page's own
|
|
5
|
+
* counters (`numbering`) and the manifest tables a cross-page `[[slug#eq-key]]`
|
|
6
|
+
* is resolved against (`equations` / `tables` / `figures`). Deriving all four
|
|
7
|
+
* from the same manifest in one step is what stops a course config from
|
|
8
|
+
* supplying one without the other — the omission that produced page-local
|
|
9
|
+
* `(2.2)` and cross-page `(4.5)` for the same equation.
|
|
10
|
+
*
|
|
11
|
+
* `numbering` maps a lesson slug to `{ part, equations, tables, figures }`:
|
|
12
|
+
* the part's ordinal (the number before the dot) and, per kind of float, how
|
|
13
|
+
* many of them the earlier lessons of that part already used.
|
|
14
|
+
*/
|
|
15
|
+
export function numberingFromManifest(manifest: any): {
|
|
16
|
+
equations?: undefined;
|
|
17
|
+
tables?: undefined;
|
|
18
|
+
figures?: undefined;
|
|
19
|
+
numbering?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
equations: any;
|
|
22
|
+
tables: any;
|
|
23
|
+
figures: any;
|
|
24
|
+
numbering: {
|
|
25
|
+
[k: string]: any;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
1
28
|
/**
|
|
2
29
|
* @param {{
|
|
3
30
|
* definitions?: Map<string, object> | readonly object[] | Record<string, object> | (() => Map<string, object> | readonly object[] | Record<string, object>),
|
|
4
31
|
* resolve?: (key: string, context: object) => object | undefined,
|
|
5
32
|
* unknown?: 'error' | 'warn' | 'ignore',
|
|
6
33
|
* base?: string,
|
|
34
|
+
* manifest?: object,
|
|
7
35
|
* equations?: Record<string, Record<string, string>>,
|
|
8
36
|
* tables?: Record<string, Record<string, string>>,
|
|
9
37
|
* figures?: Record<string, Record<string, string>>,
|
|
38
|
+
* numbering?: Record<string, { part: number, equations: number, tables: number, figures: number }>,
|
|
10
39
|
* chapters?: Record<string, number>,
|
|
11
40
|
* }} [options]
|
|
12
41
|
*
|
|
13
|
-
* `
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
42
|
+
* `manifest` is the preferred wiring: pass the compiled manifest and the four
|
|
43
|
+
* numbering inputs below are read off it together, so they cannot drift apart.
|
|
44
|
+
* They stay available individually for hand-built trees, and an explicitly
|
|
45
|
+
* supplied one still wins over the manifest.
|
|
46
|
+
*
|
|
47
|
+
* `numbering` maps a lesson slug to its **part** (the number before the dot)
|
|
48
|
+
* and, per kind of float, how many the earlier lessons of that part already
|
|
49
|
+
* used. Every display equation / `<Table>` / `<Figure>` on the page is then
|
|
50
|
+
* numbered `‹part›.‹offset + n›`, with `##` and `###` headings ignored; with no
|
|
51
|
+
* entry the legacy `##`-section scheme applies. It must agree with
|
|
52
|
+
* `manifest.lessons[]`, which is what feeds `equations`/`tables`/`figures`
|
|
53
|
+
* above — supplying the tables without `numbering` numbers the same equation
|
|
54
|
+
* twice, and `assertManifestNumberParity` turns that into a build error rather
|
|
55
|
+
* than a page whose references disagree with their targets.
|
|
56
|
+
*
|
|
57
|
+
* `chapters` is the superseded per-lesson form (slug → the lesson's course
|
|
58
|
+
* ordinal, no running offset). Still read when `numbering` has no entry, so a
|
|
59
|
+
* config written against the old option keeps building.
|
|
18
60
|
*/
|
|
19
61
|
export default function remarkNotation(options?: {
|
|
20
62
|
definitions?: Map<string, object> | readonly object[] | Record<string, object> | (() => Map<string, object> | readonly object[] | Record<string, object>);
|
|
21
63
|
resolve?: (key: string, context: object) => object | undefined;
|
|
22
64
|
unknown?: "error" | "warn" | "ignore";
|
|
23
65
|
base?: string;
|
|
66
|
+
manifest?: object;
|
|
24
67
|
equations?: Record<string, Record<string, string>>;
|
|
25
68
|
tables?: Record<string, Record<string, string>>;
|
|
26
69
|
figures?: Record<string, Record<string, string>>;
|
|
70
|
+
numbering?: Record<string, {
|
|
71
|
+
part: number;
|
|
72
|
+
equations: number;
|
|
73
|
+
tables: number;
|
|
74
|
+
figures: number;
|
|
75
|
+
}>;
|
|
27
76
|
chapters?: Record<string, number>;
|
|
28
77
|
}): (tree: any, file: any) => void;
|
|
29
78
|
/**
|