explico 0.4.0 → 0.5.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 +273 -1
- package/README.md +103 -8
- package/dist/compiler/astro.d.ts +9 -1
- package/dist/compiler/astro.js +182 -3
- package/dist/compiler/cli.d.ts +13 -0
- package/dist/compiler/cli.js +28 -4
- package/dist/compiler/collections.d.ts +3 -2
- package/dist/compiler/collections.js +28 -1
- package/dist/compiler/course-config.d.ts +58 -1
- package/dist/compiler/course-config.js +28 -1
- package/dist/compiler/exports/standalone-html.d.ts +34 -0
- package/dist/compiler/exports/standalone-html.js +803 -0
- package/dist/compiler/manifest.d.ts +90 -6
- package/dist/compiler/manifest.js +150 -17
- package/dist/compiler/resolution-report.d.ts +4 -0
- package/dist/reference/base-abbreviations.d.mts +30 -0
- package/dist/reference/base-abbreviations.mjs +60 -0
- package/dist/reference/base-operators.d.mts +15 -0
- package/dist/reference/base-operators.mjs +40 -0
- package/dist/reference/base-roman-labels.d.mts +35 -0
- package/dist/reference/base-roman-labels.mjs +43 -0
- package/dist/reference/consistency.d.ts +8 -2
- package/dist/reference/consistency.js +50 -3
- package/dist/reference/diagrams-validate.d.ts +21 -0
- package/dist/reference/diagrams-validate.js +13 -0
- package/dist/reference/equations.d.mts +10 -15
- package/dist/reference/equations.mjs +67 -14
- package/dist/reference/gate-abbreviations.d.ts +17 -0
- package/dist/reference/gate-abbreviations.js +64 -0
- package/dist/reference/gate-math.d.ts +28 -5
- package/dist/reference/gate-math.js +5 -2
- package/dist/reference/gate-prose-notation.d.ts +60 -0
- package/dist/reference/gate-prose-notation.js +125 -0
- package/dist/reference/keyed-float-validate.d.ts +5 -5
- package/dist/reference/keyed-jsx-floats.d.mts +7 -12
- package/dist/reference/keyed-jsx-floats.mjs +50 -25
- package/dist/reference/knowledge-checks-validate.d.ts +35 -0
- package/dist/reference/knowledge-checks-validate.js +78 -0
- package/dist/reference/knowledge-checks.d.mts +25 -0
- package/dist/reference/knowledge-checks.mjs +45 -0
- package/dist/reference/lab-math-scope.d.ts +9 -1
- package/dist/reference/lab-math-scope.js +32 -4
- package/dist/reference/math-glyphs.d.mts +30 -0
- package/dist/reference/math-glyphs.mjs +367 -0
- package/dist/reference/mdx-attributes.d.mts +13 -0
- package/dist/reference/mdx-attributes.mjs +38 -0
- package/dist/reference/mixed-content.d.ts +17 -0
- package/dist/reference/mixed-content.js +29 -0
- package/dist/reference/page-notation.d.ts +52 -0
- package/dist/reference/page-notation.js +171 -0
- package/dist/reference/raw-floats-validate.d.ts +18 -0
- package/dist/reference/raw-floats-validate.js +53 -0
- package/dist/reference/raw-floats.d.mts +18 -0
- package/dist/reference/raw-floats.mjs +129 -0
- package/dist/reference/rehype-heading-anchors.d.mts +1 -1
- package/dist/reference/rehype-heading-anchors.mjs +107 -1
- package/dist/reference/rehype-table-header-labels.d.mts +26 -0
- package/dist/reference/rehype-table-header-labels.mjs +152 -0
- package/dist/reference/remark-abbreviations.d.mts +7 -0
- package/dist/reference/remark-abbreviations.mjs +174 -0
- package/dist/reference/remark-memo.d.mts +16 -0
- package/dist/reference/remark-memo.mjs +339 -0
- package/dist/reference/remark-notation.d.mts +57 -9
- package/dist/reference/remark-notation.mjs +354 -42
- package/dist/reference/remark-short-note.d.mts +4 -0
- package/dist/reference/remark-short-note.mjs +237 -0
- package/dist/reference/types.d.ts +16 -1
- package/dist/reference/units-vocab.js +4 -0
- package/docs/VERSIONING.md +33 -4
- package/docs/authoring/README.md +8 -0
- package/docs/authoring/agents.md +17 -2
- package/docs/authoring/components.md +135 -0
- package/docs/authoring/migrating.md +92 -7
- package/docs/authoring/notation.md +560 -1
- package/docs/authoring/structure.md +120 -8
- package/docs/authoring/visualizations.md +36 -3
- package/package.json +5 -2
- package/src/compiler/astro.ts +208 -3
- package/src/compiler/cli.ts +29 -2
- package/src/compiler/collections.ts +45 -4
- package/src/compiler/course-config.ts +92 -0
- package/src/compiler/exports/standalone-html.ts +963 -0
- package/src/compiler/manifest.ts +268 -22
- package/src/components/HoverPanel.astro +45 -4
- package/src/components/abbreviation/AbbreviationGlossary.astro +70 -0
- package/src/components/abbreviation/AbbreviationLayer.astro +127 -0
- package/src/components/assessments/AssessmentRunner.tsx +52 -17
- package/src/components/assessments/AssessmentSet.astro +98 -14
- package/src/components/assessments/RenderedContent.tsx +55 -0
- package/src/components/assessments/assessment-randomization.ts +16 -0
- package/src/components/diagram/Diagram.astro +444 -83
- package/src/components/disclosure/hover-preview.ts +202 -0
- package/src/components/disclosure/persisted-disclosure.ts +66 -0
- package/src/components/examples/CompactExamples.astro +2 -0
- package/src/components/figure/Figure.astro +279 -10
- package/src/components/memo/Memo.astro +453 -0
- package/src/components/memo/ShortNote.astro +223 -0
- package/src/components/notation/NotationFormula.astro +18 -1
- package/src/components/notation/NotationLayer.astro +105 -8
- package/src/components/notation/render-notation-math.ts +39 -2
- package/src/components/notation/safe-math-to-react.ts +50 -0
- package/src/components/reference/FloatRefLayer.astro +233 -15
- package/src/components/starlight/CollapsibleSections.astro +461 -0
- package/src/components/starlight/DiagramEnhancer.astro +385 -0
- package/src/components/starlight/HeadingAnchorEnhancer.astro +5 -17
- package/src/components/starlight/KeyedFloatEnhancer.astro +81 -3
- package/src/components/starlight/LayoutHeader.astro +50 -0
- package/src/components/starlight/LayoutSocialIcons.astro +41 -2
- package/src/components/starlight/LessonExportActions.astro +436 -0
- package/src/components/starlight/LessonFooter.astro +27 -2
- package/src/components/starlight/section-parts.ts +31 -0
- package/src/components/table/Table.astro +293 -7
- package/src/components/table/TableOverflowExpander.astro +489 -0
- package/src/content-config.ts +20 -0
- package/src/reference/base-abbreviations.mjs +60 -0
- package/src/reference/base-operators.mjs +40 -0
- package/src/reference/base-roman-labels.mjs +43 -0
- package/src/reference/consistency.ts +73 -2
- package/src/reference/diagrams-validate.ts +40 -0
- package/src/reference/equations.mjs +67 -14
- package/src/reference/gate-abbreviations.ts +128 -0
- package/src/reference/gate-math.ts +27 -7
- package/src/reference/gate-prose-notation.ts +224 -0
- package/src/reference/keyed-float-validate.ts +5 -5
- package/src/reference/keyed-jsx-floats.mjs +50 -25
- package/src/reference/knowledge-checks-validate.ts +130 -0
- package/src/reference/knowledge-checks.mjs +45 -0
- package/src/reference/lab-math-scope.ts +45 -3
- package/src/reference/math-glyphs.mjs +367 -0
- package/src/reference/mdx-attributes.mjs +38 -0
- package/src/reference/mixed-content.ts +69 -0
- package/src/reference/page-notation.ts +252 -0
- package/src/reference/raw-floats-validate.ts +103 -0
- package/src/reference/raw-floats.mjs +129 -0
- package/src/reference/rehype-heading-anchors.mjs +107 -1
- package/src/reference/rehype-table-header-labels.mjs +152 -0
- package/src/reference/remark-abbreviations.mjs +174 -0
- package/src/reference/remark-memo.mjs +339 -0
- package/src/reference/remark-notation.mjs +354 -42
- package/src/reference/remark-short-note.mjs +237 -0
- package/src/reference/types.ts +16 -0
- package/src/reference/units-vocab.ts +4 -0
- package/src/styles/global.css +996 -50
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remark adapter for reveal-in-place short notes.
|
|
3
|
+
*
|
|
4
|
+
* A `<ShortNote>` may sit inside a sentence while its body contains block
|
|
5
|
+
* Markdown. Before mdast parsing, the adapter extracts that body and leaves an
|
|
6
|
+
* ordinary inline link placeholder behind. After parsing, it inserts the real
|
|
7
|
+
* flow component immediately after the intact paragraph. The browser enhancer
|
|
8
|
+
* can then split/rejoin that paragraph only while the note is open, preserving
|
|
9
|
+
* valid and genuinely inline collapsed markup.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { stripFencedCode } from './equations.mjs';
|
|
13
|
+
import { KEYED_FLOAT_KEY_PATTERN } from './keyed-jsx-floats.mjs';
|
|
14
|
+
import {
|
|
15
|
+
injectMdxStringAttributes,
|
|
16
|
+
mdxAttributesNamed,
|
|
17
|
+
removeMdxAttribute,
|
|
18
|
+
} from './mdx-attributes.mjs';
|
|
19
|
+
|
|
20
|
+
const BLOCK_ATTRIBUTE = 'data-explico-short-note-block';
|
|
21
|
+
const BLOCKS_DATA = 'explicoShortNoteBlocks';
|
|
22
|
+
const MARKER_URL_PREFIX = '#explico-internal-short-note-marker-';
|
|
23
|
+
const ATTRIBUTE_SOURCE = String.raw`(?:"[^"]*"|'[^']*'|\{(?:[^{}]|\{[^{}]*\})*\}|[^>"'])*`;
|
|
24
|
+
const SHORT_NOTE_PAIR = new RegExp(
|
|
25
|
+
String.raw`<ShortNote\b(${ATTRIBUTE_SOURCE})>([\s\S]*?)<\/ShortNote\s*>`,
|
|
26
|
+
'g',
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
function maskBlock(block) {
|
|
30
|
+
return block.replace(/[^\n]/g, ' ');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function searchableSource(source) {
|
|
34
|
+
return stripFencedCode(source)
|
|
35
|
+
.replace(/<!--[\s\S]*?-->/g, maskBlock)
|
|
36
|
+
.replace(/\{\/\*[\s\S]*?\*\/\}/g, maskBlock);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function extractShortNotes(source) {
|
|
40
|
+
const input = String(source);
|
|
41
|
+
const searchable = searchableSource(input);
|
|
42
|
+
const matches = [...searchable.matchAll(SHORT_NOTE_PAIR)];
|
|
43
|
+
if (matches.length === 0) return { source: input, notes: [] };
|
|
44
|
+
|
|
45
|
+
const notes = [];
|
|
46
|
+
let cursor = 0;
|
|
47
|
+
let output = '';
|
|
48
|
+
for (let index = 0; index < matches.length; index++) {
|
|
49
|
+
const match = matches[index];
|
|
50
|
+
const start = match.index ?? 0;
|
|
51
|
+
if (start < cursor) continue;
|
|
52
|
+
|
|
53
|
+
const whole = match[0];
|
|
54
|
+
const maskedAttributes = match[1] ?? '';
|
|
55
|
+
const openLength = '<ShortNote'.length + maskedAttributes.length + 1;
|
|
56
|
+
const bodyStart = start + openLength;
|
|
57
|
+
const closeStart = start + whole.lastIndexOf('</ShortNote');
|
|
58
|
+
const searchableBody = searchable.slice(bodyStart, closeStart);
|
|
59
|
+
if (/<ShortNote\b/.test(searchableBody)) {
|
|
60
|
+
throw new Error('A <ShortNote> cannot be nested inside another <ShortNote>.');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
notes.push({
|
|
64
|
+
token: String(index),
|
|
65
|
+
attributes: input.slice(start + '<ShortNote'.length, bodyStart - 1),
|
|
66
|
+
body: input.slice(bodyStart, closeStart),
|
|
67
|
+
});
|
|
68
|
+
output += input.slice(cursor, start);
|
|
69
|
+
output += `[short note](${MARKER_URL_PREFIX}${index})`;
|
|
70
|
+
cursor = start + whole.length;
|
|
71
|
+
}
|
|
72
|
+
output += input.slice(cursor);
|
|
73
|
+
return { source: output, notes };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Return the parseable source form, primarily for adapter tests. */
|
|
77
|
+
export function preprocessShortNotes(source) {
|
|
78
|
+
return extractShortNotes(source).source;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function fail(file, message, node) {
|
|
82
|
+
if (typeof file?.fail === 'function') file.fail(message, node);
|
|
83
|
+
const location = file?.path ? `${file.path}: ` : '';
|
|
84
|
+
throw new Error(`${location}${message}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function literalAttribute(node, name, file) {
|
|
88
|
+
const matches = mdxAttributesNamed(node, name);
|
|
89
|
+
if (matches.length > 1) {
|
|
90
|
+
fail(file, `<ShortNote> has more than one \`${name}\` prop.`, node);
|
|
91
|
+
}
|
|
92
|
+
if (matches.length === 0) {
|
|
93
|
+
fail(file, `<ShortNote> requires a non-empty literal \`${name}\` prop.`, node);
|
|
94
|
+
}
|
|
95
|
+
const value = matches[0]?.value;
|
|
96
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
97
|
+
fail(
|
|
98
|
+
file,
|
|
99
|
+
`<ShortNote> \`${name}\` must be a non-empty literal string; expressions are not supported for inline note triggers.`,
|
|
100
|
+
matches[0] ?? node,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function markerNode(note) {
|
|
107
|
+
return {
|
|
108
|
+
type: 'link',
|
|
109
|
+
url: `#${note.anchorId}`,
|
|
110
|
+
children: [{ type: 'text', value: note.title }],
|
|
111
|
+
data: {
|
|
112
|
+
hProperties: {
|
|
113
|
+
className: ['short-note-marker'],
|
|
114
|
+
'data-short-note-key': note.id,
|
|
115
|
+
'aria-controls': note.anchorId,
|
|
116
|
+
'aria-expanded': 'false',
|
|
117
|
+
'aria-label': `${note.title}: ${note.description}`,
|
|
118
|
+
title: note.description,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function visit(node, callback, parent) {
|
|
125
|
+
if (!node || typeof node !== 'object') return;
|
|
126
|
+
callback(node, parent);
|
|
127
|
+
if (!Array.isArray(node.children)) return;
|
|
128
|
+
for (const child of node.children) visit(child, callback, node);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** @this {any} */
|
|
132
|
+
export default function remarkShortNote() {
|
|
133
|
+
const processor = this;
|
|
134
|
+
const parse = processor.parser;
|
|
135
|
+
if (typeof parse !== 'function') {
|
|
136
|
+
throw new Error(
|
|
137
|
+
'remarkShortNote must be registered after the Markdown parser (and after remark-mdx when configured explicitly).',
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
Reflect.set(processor, 'parser', (document, file) => {
|
|
142
|
+
const extracted = extractShortNotes(document);
|
|
143
|
+
const tree = parse(extracted.source, file);
|
|
144
|
+
if (extracted.notes.length === 0) return tree;
|
|
145
|
+
|
|
146
|
+
const blocks = extracted.notes.map((note) => {
|
|
147
|
+
const source = `<ShortNote ${BLOCK_ATTRIBUTE}="${note.token}"${note.attributes}>\n\n${note.body}\n\n</ShortNote>`;
|
|
148
|
+
const parsed = parse(source, file);
|
|
149
|
+
const block = parsed.children?.find((node) => node?.name === 'ShortNote');
|
|
150
|
+
if (!block) {
|
|
151
|
+
fail(file, 'Unable to parse the extracted <ShortNote> body.', parsed);
|
|
152
|
+
}
|
|
153
|
+
return block;
|
|
154
|
+
});
|
|
155
|
+
tree.data ??= {};
|
|
156
|
+
tree.data[BLOCKS_DATA] = blocks;
|
|
157
|
+
return tree;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return function transform(tree, file) {
|
|
161
|
+
const blocks = tree.data?.[BLOCKS_DATA] ?? [];
|
|
162
|
+
if (blocks.length === 0) return;
|
|
163
|
+
|
|
164
|
+
const markers = new Map();
|
|
165
|
+
const parents = new Map();
|
|
166
|
+
visit(tree, (node, parent) => {
|
|
167
|
+
if (parent) parents.set(node, parent);
|
|
168
|
+
if (
|
|
169
|
+
node.type === 'link' &&
|
|
170
|
+
typeof node.url === 'string' &&
|
|
171
|
+
node.url.startsWith(MARKER_URL_PREFIX)
|
|
172
|
+
) {
|
|
173
|
+
const token = node.url.slice(MARKER_URL_PREFIX.length);
|
|
174
|
+
if (markers.has(token)) fail(file, 'Internal short-note marker collision.', node);
|
|
175
|
+
markers.set(token, { node, parent });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (blocks.length !== markers.size) {
|
|
180
|
+
fail(file, 'Internal short-note marker/block pairing failed.', tree);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const seenIds = new Set();
|
|
184
|
+
const insertions = new Map();
|
|
185
|
+
for (const block of blocks) {
|
|
186
|
+
const token = literalAttribute(block, BLOCK_ATTRIBUTE, file);
|
|
187
|
+
const marker = markers.get(token);
|
|
188
|
+
if (!marker?.parent || marker.parent.type !== 'paragraph') {
|
|
189
|
+
fail(file, '<ShortNote> must be placed in ordinary prose.', marker?.node ?? block);
|
|
190
|
+
}
|
|
191
|
+
const container = parents.get(marker.parent);
|
|
192
|
+
if (!container || !Array.isArray(container.children)) {
|
|
193
|
+
fail(file, 'A <ShortNote> paragraph has no flow parent.', marker.parent);
|
|
194
|
+
}
|
|
195
|
+
if (block.type !== 'mdxJsxFlowElement') {
|
|
196
|
+
fail(file, '<ShortNote> did not become a block-level MDX element.', block);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const id = literalAttribute(block, 'id', file);
|
|
200
|
+
const title = literalAttribute(block, 'title', file);
|
|
201
|
+
const description = literalAttribute(block, 'description', file);
|
|
202
|
+
if (!KEYED_FLOAT_KEY_PATTERN.test(id)) {
|
|
203
|
+
fail(
|
|
204
|
+
file,
|
|
205
|
+
`Invalid <ShortNote id="${id}">; use lowercase letters/numbers separated by single hyphens.`,
|
|
206
|
+
block,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (seenIds.has(id)) {
|
|
210
|
+
fail(file, `Duplicate <ShortNote id="${id}"> on this page.`, block);
|
|
211
|
+
}
|
|
212
|
+
seenIds.add(id);
|
|
213
|
+
|
|
214
|
+
const anchorId = `short-note-${id}`;
|
|
215
|
+
removeMdxAttribute(block, BLOCK_ATTRIBUTE);
|
|
216
|
+
injectMdxStringAttributes(block, { anchorId });
|
|
217
|
+
|
|
218
|
+
const markerIndex = marker.parent.children.indexOf(marker.node);
|
|
219
|
+
marker.parent.children[markerIndex] = markerNode({
|
|
220
|
+
id,
|
|
221
|
+
title,
|
|
222
|
+
description,
|
|
223
|
+
anchorId,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const existing = insertions.get(marker.parent) ?? { container, blocks: [] };
|
|
227
|
+
existing.blocks.push(block);
|
|
228
|
+
insertions.set(marker.parent, existing);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (const [paragraph, insertion] of insertions) {
|
|
232
|
+
const paragraphIndex = insertion.container.children.indexOf(paragraph);
|
|
233
|
+
insertion.container.children.splice(paragraphIndex + 1, 0, ...insertion.blocks);
|
|
234
|
+
}
|
|
235
|
+
delete tree.data[BLOCKS_DATA];
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -84,6 +84,21 @@ interface NotationRegistryInput {
|
|
|
84
84
|
readonly sharedDefinitions: readonly SharedNotationDefinitionInput[];
|
|
85
85
|
readonly lessons: readonly NotationLessonInput[];
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* One entry in the abbreviations table (N14) — deliberately a lighter tier
|
|
89
|
+
* than {@link SharedNotationDefinitionInput}: a name resolution only, no
|
|
90
|
+
* `formula`, `units`, `alignment`, or `sources`. `abbr` is the canonical
|
|
91
|
+
* written form (`OLS`); `aliases` are other written forms of the same
|
|
92
|
+
* abbreviation (`w.r.t.` might alias `wrt`) that should resolve the same way.
|
|
93
|
+
*/
|
|
94
|
+
interface AbbreviationDefinitionInput {
|
|
95
|
+
readonly key: string;
|
|
96
|
+
readonly abbr: string;
|
|
97
|
+
readonly expansion: string;
|
|
98
|
+
readonly aliases: readonly string[];
|
|
99
|
+
readonly domain?: string;
|
|
100
|
+
readonly source: SourceSpan;
|
|
101
|
+
}
|
|
87
102
|
type NotationDiagnosticCode = 'invalid-key' | 'duplicate-page' | 'duplicate-definition' | 'conflicting-definition' | 'undefined-reference' | 'reference-cycle' | 'unused-definition' | 'alignment-unknown-competency' | 'alignment-unknown-lesson' | 'alignment-introduction' | 'alignment-unavailable' | 'alignment-review-state' | 'gloss-collides-with-card';
|
|
88
103
|
type DiagnosticSeverity = 'error' | 'warning';
|
|
89
104
|
interface NotationDiagnostic {
|
|
@@ -171,4 +186,4 @@ interface NotationRegistry {
|
|
|
171
186
|
readonly diagnostics: readonly NotationDiagnostic[];
|
|
172
187
|
}
|
|
173
188
|
|
|
174
|
-
export type { DiagnosticSeverity, EditorialStatus, LocalNotationDefinitionInput, LocalNotationDefinitionRecord, NotationAlignment, NotationBacklink, NotationBinding, NotationDefinitionRecord, NotationDefinitionScope, NotationDiagnostic, NotationDiagnosticCode, NotationGlossEntry, NotationLessonInput, NotationPageBundle, NotationReferenceInput, NotationReferenceKind, NotationRegistry, NotationRegistryInput, ResolvedNotationReference, SharedNotationDefinitionInput, SharedNotationDefinitionRecord, SourceSpan };
|
|
189
|
+
export type { AbbreviationDefinitionInput, DiagnosticSeverity, EditorialStatus, LocalNotationDefinitionInput, LocalNotationDefinitionRecord, NotationAlignment, NotationBacklink, NotationBinding, NotationDefinitionRecord, NotationDefinitionScope, NotationDiagnostic, NotationDiagnosticCode, NotationGlossEntry, NotationLessonInput, NotationPageBundle, NotationReferenceInput, NotationReferenceKind, NotationRegistry, NotationRegistryInput, ResolvedNotationReference, SharedNotationDefinitionInput, SharedNotationDefinitionRecord, SourceSpan };
|
package/docs/VERSIONING.md
CHANGED
|
@@ -48,15 +48,44 @@ every one is listed under a `### Breaking` heading in `CHANGELOG.md` for that
|
|
|
48
48
|
version. A **PATCH** never breaks. Once the API settles this clause is dropped
|
|
49
49
|
and normal semver applies.
|
|
50
50
|
|
|
51
|
+
## The migration artifact
|
|
52
|
+
|
|
53
|
+
Every published engine version carries agent-readable upgrade instructions in
|
|
54
|
+
[`authoring/migrating.md`](authoring/migrating.md). That file is part of the npm
|
|
55
|
+
package, so the instructions travel with the exact implementation they describe
|
|
56
|
+
and remain available at
|
|
57
|
+
`node_modules/explico/docs/authoring/migrating.md` after installation.
|
|
58
|
+
|
|
59
|
+
A release must add a newest-first `## <previous range> → <new version>` section,
|
|
60
|
+
whether the bump is MAJOR, MINOR, or PATCH. The section must do one of two things:
|
|
61
|
+
|
|
62
|
+
- give concrete, ordered changes for every affected consumer-owned surface —
|
|
63
|
+
dependency declarations, `content/`, `content/course.config.ts`, Astro/source
|
|
64
|
+
wiring, course scripts, and generated artifacts — including conditional cases
|
|
65
|
+
and verification commands; or
|
|
66
|
+
- state explicitly that no course changes are required, followed by the normal
|
|
67
|
+
verification commands.
|
|
68
|
+
|
|
69
|
+
The changelog explains what changed and why; it is not a substitute for the
|
|
70
|
+
migration section. The migration section, changelog entry, version bump, and
|
|
71
|
+
package are one atomic release. An agent upgrading across several versions
|
|
72
|
+
applies every intervening section in order.
|
|
73
|
+
|
|
51
74
|
## Cutting a release
|
|
52
75
|
|
|
53
76
|
1. Decide the next version from the rules above and the `## [Unreleased]`
|
|
54
77
|
section of `CHANGELOG.md`.
|
|
55
|
-
2.
|
|
78
|
+
2. Add the section for `x.y.z` to `docs/authoring/migrating.md`, derived from the
|
|
79
|
+
complete release diff. Write explicit no-action instructions when nothing in
|
|
80
|
+
a downstream course needs to change.
|
|
81
|
+
3. `CHANGELOG.md`: rename `## [Unreleased]` to `## [x.y.z] - YYYY-MM-DD`, add a
|
|
56
82
|
fresh empty `## [Unreleased]`, and fix up the compare links at the bottom.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
83
|
+
4. Bump `package.json` `version` (`npm version x.y.z --no-git-tag-version`).
|
|
84
|
+
5. Run `pnpm check:release-migration`. It fails when the new version has no
|
|
85
|
+
migration section or the shipped-file configuration excludes the guide.
|
|
86
|
+
6. Run `pnpm build && pnpm test`, then inspect `pnpm pack --dry-run` and confirm
|
|
87
|
+
that `docs/authoring/migrating.md` and `docs/authoring/agents.md` are listed.
|
|
88
|
+
7. Commit, then `git tag vx.y.z`.
|
|
60
89
|
|
|
61
90
|
## When you change the engine
|
|
62
91
|
|
package/docs/authoring/README.md
CHANGED
|
@@ -74,6 +74,7 @@ file table.
|
|
|
74
74
|
| [notation.md](notation.md) | `N` | how math symbols are written and bound |
|
|
75
75
|
| [visualizations.md](visualizations.md) | `V` | diagrams, charts, interactive figures, randomness |
|
|
76
76
|
| [versioning.md](versioning.md) | `C` | the knowledge version and `content/CHANGELOG.md` |
|
|
77
|
+
| [components.md](components.md) | — | _catalog_: inline/block features available in a lesson body — tooltips, tables, figures, diagrams, memos, abbreviations, citations |
|
|
77
78
|
| [agents.md](agents.md) | — | _guide_: working on a course repo (with an AI agent) |
|
|
78
79
|
| [migrating.md](migrating.md) | — | _guide_: per-version steps when bumping the `explico` dep |
|
|
79
80
|
|
|
@@ -89,6 +90,13 @@ file table.
|
|
|
89
90
|
| [N6](notation.md#n6) | Probability is `\mathbb{P}` (double-struck `P`) | Build-enforced (completeness gate) + review |
|
|
90
91
|
| [N7](notation.md#n7) | The differential `d` is upright `\mathrm{d}` | Build-enforced (completeness gate) |
|
|
91
92
|
| [N8](notation.md#n8) | A named operator is universal (shipped/course list) or explained | Tier-3 warning (`operator-explained`) |
|
|
93
|
+
| [N9](notation.md#n9) | A quantity/unit/operator is typeset in `$…$`, never as plain text | Build-enforced (`gate-prose-notation.ts`) |
|
|
94
|
+
| [N10](notation.md#n10) | A `\mathrm{…}` multi-letter label is universal, or it is explained | Tier-3 warning (`roman-label-explained`) |
|
|
95
|
+
| [N11](notation.md#n11) | A worked example never drops a unit from a substituted value | Review-only |
|
|
96
|
+
| [N12](notation.md#n12) | Math syntax never carries the sentence's own trailing punctuation | Review-only |
|
|
97
|
+
| [N13](notation.md#n13) | A nonlinear function always takes a parenthesized argument | Build-enforced (`resolveMathGlyphs`) |
|
|
98
|
+
| [N14](notation.md#n14) | An abbreviation-shaped word in prose is universal, or explained | Build-enforced (`gate-abbreviations.ts`) |
|
|
99
|
+
| [N15](notation.md#n15) | A nested `(`, `[`, `{`, or `\|` must use `\left`/`\right` sizing | Build-enforced (`resolveMathGlyphs`) |
|
|
92
100
|
| [V1](visualizations.md#v1) | Graphs and flowcharts use the `Diagram` component | Review-only |
|
|
93
101
|
| [V2](visualizations.md#v2) | Static charts: SVG → D3 → Observable Plot, chosen per figure | Review-only |
|
|
94
102
|
| [V3](visualizations.md#v3) | Interactive figures: island + `renderLabMath` seam + text alt | Review-only |
|
package/docs/authoring/agents.md
CHANGED
|
@@ -30,6 +30,9 @@ and a rule disagree, the engine wins — see [`README.md`](README.md).
|
|
|
30
30
|
- Grep for a symbol and open a line range; don't read a whole engine module out
|
|
31
31
|
of `node_modules/explico/`. The parts you actually need are
|
|
32
32
|
[`notation.md`](notation.md), this file, and the `content` CLI output.
|
|
33
|
+
- Not sure what's available to put in a lesson body — a tooltip, a table, a
|
|
34
|
+
diagram, an aside? [`components.md`](components.md) is a syntax-first
|
|
35
|
+
catalog, not another rule file to read cover to cover.
|
|
33
36
|
- If a page 500s or a change doesn't show, the dev server's content store is
|
|
34
37
|
stale — a **new file in a `content/` collection needs a dev-server restart**,
|
|
35
38
|
not just a save. Restart it; don't diagnose it with `ps` / `curl` scrapes.
|
|
@@ -59,5 +62,17 @@ sets `about.version` to match, in the same commit. Rules C1–C4,
|
|
|
59
62
|
|
|
60
63
|
## Upgrading the engine
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
Treat every `explico` dependency bump as a migration task, including PATCH
|
|
66
|
+
updates. Before changing the dependency, record the installed version with
|
|
67
|
+
`node -p "require('explico/package.json').version"`. Install the target version,
|
|
68
|
+
then read the target package's
|
|
69
|
+
`node_modules/explico/docs/authoring/migrating.md`; the migration instructions
|
|
70
|
+
ship with the code they describe.
|
|
71
|
+
|
|
72
|
+
Apply every version section after the old version through the installed version,
|
|
73
|
+
oldest first. Follow required and conditional changes across `content/`,
|
|
74
|
+
`content/course.config.ts`, Astro/source wiring, course scripts, dependencies,
|
|
75
|
+
and generated files. Do not use `CHANGELOG.md` alone as the procedure and do not
|
|
76
|
+
assume that a release without a **Breaking** heading needs no work; each migration
|
|
77
|
+
section either gives exact work or explicitly says no course changes are
|
|
78
|
+
required. Finish with the validation sequence in [`migrating.md`](migrating.md).
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Inline & block features — a catalog
|
|
2
|
+
|
|
3
|
+
A fast index of what can go in a lesson body, for an AI (or a person) drafting
|
|
4
|
+
content without reading every rule file first. This file is not a rule area —
|
|
5
|
+
nothing here is build-enforced by *this* file. Where a feature has a rule
|
|
6
|
+
area (`N`, `V`, `C`), the enforcement and the rationale live there; this file
|
|
7
|
+
only says what exists, its minimal syntax, and which of several
|
|
8
|
+
similar-looking options to reach for.
|
|
9
|
+
|
|
10
|
+
See [README.md](README.md) for what "Build-enforced" / "Review-only" means,
|
|
11
|
+
[structure.md](structure.md) for the five structural levels and how floats
|
|
12
|
+
are numbered, and [agents.md](agents.md) for the day-to-day workflow (CLI,
|
|
13
|
+
validation loop).
|
|
14
|
+
|
|
15
|
+
## Inline, zero-registration
|
|
16
|
+
|
|
17
|
+
**A native browser tooltip: `title="…"` on any element.**
|
|
18
|
+
|
|
19
|
+
```mdx
|
|
20
|
+
The fit uses <span title="Ordinary least squares run on the demeaned series">OLS</span> here.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This is plain HTML passed straight through MDX/JSX to the DOM — no `id`, no
|
|
24
|
+
content-collection entry, no build-time registration, no hover-card styling,
|
|
25
|
+
no click-to-pin behaviour. The browser renders its own default tooltip on
|
|
26
|
+
hover/focus, exactly like a native `<abbr title="…">` or an image `alt`
|
|
27
|
+
tooltip would. Use it for a throwaway aside that doesn't deserve a citable,
|
|
28
|
+
numbered, or glossary-linked definition — a wording nudge, a "why this value"
|
|
29
|
+
note, a translation of a variable name.
|
|
30
|
+
|
|
31
|
+
_How to apply._ Any element takes `title`; `<span>` is the usual wrapper for
|
|
32
|
+
bare prose text that has no other markup reason to exist. `title` is a plain
|
|
33
|
+
DOM string: no `$…$` math rendering happens inside it (same carve-out
|
|
34
|
+
[N9](notation.md#n9) documents for `<Figure caption="…">` / `<Table
|
|
35
|
+
caption="…">`) — write a unit as a word or plain Unicode, not LaTeX.
|
|
36
|
+
|
|
37
|
+
_What it does **not** replace:_
|
|
38
|
+
|
|
39
|
+
- **An abbreviation-shaped word in visible prose still has to satisfy
|
|
40
|
+
[N14](notation.md#n14).** The completeness gates blank out JSX *tags*
|
|
41
|
+
(`<span title="…">…</span>` → the `<span …>` and `</span>` markup) but not
|
|
42
|
+
the *text a reader sees between them* — so wrapping `OLS` in a tooltip span
|
|
43
|
+
does not exempt that visible `OLS` from needing a real
|
|
44
|
+
`content/abbreviations/*.md` entry, a notation binding, or a
|
|
45
|
+
`course.config.ts` allowlist entry. `title="…"` adds a hover hint on top of
|
|
46
|
+
an otherwise-explained term; it is not itself an explanation the gate
|
|
47
|
+
recognizes. If the word only ever needs to exist inside the tooltip string
|
|
48
|
+
(never as the visible text), the gate never sees it and there is nothing to
|
|
49
|
+
satisfy.
|
|
50
|
+
- **A math quantity still goes in `$…$`, not a `title`.** A hover *card* with
|
|
51
|
+
the definition, one per glyph, page-wide — that's `[[key]]` / notation
|
|
52
|
+
entries ([N4](notation.md#n4)), unrelated machinery.
|
|
53
|
+
- **A citable, reusable, or numbered explanation** — something another page
|
|
54
|
+
should be able to link to, or that belongs in the glossary — is a
|
|
55
|
+
[`<ShortNote>`/`<Memo>`](#short-asides-and-detours) or an
|
|
56
|
+
[abbreviation entry](#reference-systems), not a tooltip. A `title` string
|
|
57
|
+
is invisible to search, screen-reader users relying on non-hover
|
|
58
|
+
interaction, and print/PDF export.
|
|
59
|
+
|
|
60
|
+
_Enforcement._ None — this is unregistered, ungated HTML. The only thing that
|
|
61
|
+
still applies is N9/N14 against the *visible* text, as above.
|
|
62
|
+
|
|
63
|
+
## Short asides and detours
|
|
64
|
+
|
|
65
|
+
| Component | Import | What it's for | Doc |
|
|
66
|
+
| --- | --- | --- | --- |
|
|
67
|
+
| `<ShortNote id="…" title="…" description="…">` | `explico/components/memo/ShortNote.astro` | An inline, click-to-expand aside that replaces its own trigger text in place — "the smaller, genuinely inline interaction." No number, no cross-page reference. | [structure.md](structure.md#detailed-memos-and-inline-short-notes) |
|
|
68
|
+
| `<Memo id="…" title="…" description="…">` | `explico/components/memo/Memo.astro` | A collapsible, independently-numbered card for optional depth (derivation, proof, edge case) that would otherwise bloat the main argument. Cited with `[[memo-id]]`. | [structure.md](structure.md#detailed-memos-and-inline-short-notes) |
|
|
69
|
+
|
|
70
|
+
Both differ from a bare `title` tooltip in the same way: they hold real,
|
|
71
|
+
possibly multi-paragraph Markdown content (including equations, tables,
|
|
72
|
+
figures), are addressable (`id`, a permalink), and are meant to be found
|
|
73
|
+
again — a `title` tooltip is disposable and invisible outside a hover/focus
|
|
74
|
+
event.
|
|
75
|
+
|
|
76
|
+
## Numbered floats
|
|
77
|
+
|
|
78
|
+
Each of these gets a `‹part›.‹n›` number (its own counter, independent of the
|
|
79
|
+
others — see [structure.md § Numbering](structure.md#numbering)), a stable
|
|
80
|
+
`#anchor`, and same-page/cross-page reference syntax `[[prefix-id]]` /
|
|
81
|
+
`[[lesson/slug#prefix-id]]`.
|
|
82
|
+
|
|
83
|
+
| Component | Import | Prefix | Doc |
|
|
84
|
+
| --- | --- | --- | --- |
|
|
85
|
+
| `<Table id="…" caption="…">` | `explico/components/table/Table.astro` | `tab-` | [structure.md](structure.md#numbering), [V rules](visualizations.md) |
|
|
86
|
+
| `<Figure id="…" title="…" caption="…" previewable>` | `explico/components/figure/Figure.astro` | `fig-` | [structure.md](structure.md#numbering), [V2](visualizations.md#v2)/[V3](visualizations.md#v3) |
|
|
87
|
+
| `<Diagram id="…" label="…" nodes={…} edges={…}>` | `explico/components/diagram/Diagram.astro` | `dia-` | [V1](visualizations.md#v1) |
|
|
88
|
+
| A display equation with `\label{eq:key}` | `remark-math` (built in) | `eq-` | [N5](notation.md#n5), [structure.md](structure.md#numbering) |
|
|
89
|
+
|
|
90
|
+
Two naming traps worth flagging for an AI author:
|
|
91
|
+
|
|
92
|
+
- **`<Figure title="…">` is a visible short label in the collapsible title
|
|
93
|
+
bar, not the HTML `title` attribute's hover tooltip.** It always renders on
|
|
94
|
+
the page; it is a different feature from the [native tooltip](#inline-zero-registration)
|
|
95
|
+
above even though it reuses the same prop name.
|
|
96
|
+
- **A raw Markdown pipe table or a raw `<table>`, or a `<Diagram>` with no
|
|
97
|
+
`id`,** renders fine but never enters keyed-float validation — no number,
|
|
98
|
+
no `[[…]]` target. Wrap it (`<Table>`/`<Figure>`) or give the diagram an
|
|
99
|
+
`id` if it needs to be referenced. See [structure.md § Raw and unwrapped
|
|
100
|
+
floats](structure.md#raw-and-unwrapped-floats).
|
|
101
|
+
|
|
102
|
+
## Reference systems
|
|
103
|
+
|
|
104
|
+
These bind a term to a definition that a reader can hover *and* look up
|
|
105
|
+
elsewhere — the machinery a bare `title` tooltip deliberately skips.
|
|
106
|
+
|
|
107
|
+
| Syntax | Registers where | Gives | Doc |
|
|
108
|
+
| --- | --- | --- | --- |
|
|
109
|
+
| `[[key]]` in prose, before first math use | `content/notation/*.md` entry | One-meaning-per-glyph hover card everywhere `$key$`-bound math appears on the page | [N4](notation.md#n4) |
|
|
110
|
+
| `\explain{key}{latex}` inside `$…$` | Same notation entry | Escape hatch for a compound/colliding symbol only, not the default bind | [N4](notation.md#n4) |
|
|
111
|
+
| A bare all-caps/dotted term (`OLS`, `w.r.t.`) in prose | `content/abbreviations/*.md`, or `course.config.ts` `abbreviations.allow` | Hover/click card on first occurrence, linked to the glossary page | [N14](notation.md#n14) |
|
|
112
|
+
| `[@source-id]` or `[@source-id; §1.2]` | `content/sources/*.json` | Numbered `[n]` superscript linking to a References section, with a hover/pin panel | `src/reference/remark-citation.mjs` |
|
|
113
|
+
|
|
114
|
+
## Knowledge checks and worked examples
|
|
115
|
+
|
|
116
|
+
| Component | Import | Notes | Doc |
|
|
117
|
+
| --- | --- | --- | --- |
|
|
118
|
+
| `<AssessmentSet />` | `explico/components/assessments/AssessmentSet.astro` | Renders every check listed in the lesson's colocated `.checks.yml`; do not add a separate heading for it | [structure.md § Knowledge checks](structure.md#knowledge-checks) |
|
|
119
|
+
| `<CompactExample>` / `<CompactExamples>` | `explico/components/examples/CompactExample.astro` | Worked-example cards; a `<Diagram>` nested inside one is invisible to diagram numbering (see the raw/unwrapped note above) | [structure.md](structure.md#raw-and-unwrapped-floats) |
|
|
120
|
+
|
|
121
|
+
## Choosing between the "explain a term" options
|
|
122
|
+
|
|
123
|
+
From lightest to heaviest — pick the first one that's actually enough:
|
|
124
|
+
|
|
125
|
+
1. **Native `title="…"` tooltip** — disposable, hover-only, nothing to
|
|
126
|
+
register. Use when the aside doesn't need to be found again.
|
|
127
|
+
2. **`<ShortNote>`** — inline, click-to-expand, can hold Markdown, no number.
|
|
128
|
+
Use when the aside is worth more than a one-line hover but still belongs
|
|
129
|
+
inline, not as its own citable unit.
|
|
130
|
+
3. **Abbreviation entry / notation `[[key]]`** — page-wide hover card, build
|
|
131
|
+
gated (N14/N4), listed in the glossary. Use when the term recurs, is
|
|
132
|
+
acronym-shaped, or is genuine math notation.
|
|
133
|
+
4. **`<Memo>`** — numbered, permalinked, holds real sub-content (proofs,
|
|
134
|
+
figures, tables). Use when the aside is long enough to be its own small
|
|
135
|
+
section.
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
# Upgrading the explico engine
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
This is the agent-readable migration artifact shipped with `explico`. It lives
|
|
4
|
+
at `node_modules/explico/docs/authoring/migrating.md` in an installed course, so
|
|
5
|
+
the instructions always accompany the engine version they describe.
|
|
6
|
+
|
|
7
|
+
There is one newest-first section for **every** published version, not only for a
|
|
8
|
+
version whose changelog contains **Breaking** notes. A section contains the
|
|
9
|
+
concrete work for consumer-owned content, configuration, source wiring, scripts,
|
|
10
|
+
dependencies, and generated artifacts. When a release needs none of those, its
|
|
11
|
+
section says **No course changes required** explicitly. The matching
|
|
12
|
+
`CHANGELOG.md` entry (also shipped in the package) remains the full list of what
|
|
13
|
+
moved and why; it is context, not a replacement for these steps.
|
|
7
14
|
|
|
8
15
|
The pattern every time:
|
|
9
16
|
|
|
10
|
-
1.
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
1. Record the currently installed version before changing it:
|
|
18
|
+
`node -p "require('explico/package.json').version"`.
|
|
19
|
+
2. `pnpm add -D explico@<version>`.
|
|
20
|
+
3. Read this file from the newly installed package and apply **every** section
|
|
21
|
+
after the old version through the target version, oldest first. Do not skip a
|
|
22
|
+
section merely because its changelog entry is not labelled **Breaking**.
|
|
23
|
+
4. Make every required course-side change named by those sections, including
|
|
24
|
+
changes under `content/`, Astro/source configuration, and course scripts; do
|
|
25
|
+
not stop after changing the dependency declaration.
|
|
26
|
+
5. `pnpm validate:content` (run it **twice** — the first run regenerates
|
|
13
27
|
`build/resolution/*` and reports it stale; the second is clean), then
|
|
14
28
|
`pnpm content check --changed`, then `astro check`, then `astro build`.
|
|
15
29
|
Each stage names what it doesn't like; fix and re-run.
|
|
@@ -19,6 +33,77 @@ required config block throws with the section of this file to read.
|
|
|
19
33
|
|
|
20
34
|
---
|
|
21
35
|
|
|
36
|
+
## 0.4.x → 0.5.0
|
|
37
|
+
|
|
38
|
+
### 1. Rule N14: catalogue every abbreviation used in lesson prose
|
|
39
|
+
|
|
40
|
+
Every acronym (`OLS`, `MSE`) or dotted abbreviation (`w.r.t.`, `i.i.d.`) used in
|
|
41
|
+
plain prose must now be on the shipped baseline
|
|
42
|
+
(`base-abbreviations.mjs`), listed in `course.config.ts`'s new
|
|
43
|
+
`abbreviations.allow` / `abbreviations.deny`, given an entry in the new
|
|
44
|
+
`content/abbreviations/*.md` collection, or already explained as a math
|
|
45
|
+
quantity via N8/N10. This is build-enforced, same error-severity and
|
|
46
|
+
no-`lint-ignore.yml`-escape-hatch shape as N9.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export const courseConfig = {
|
|
50
|
+
// …existing fields…
|
|
51
|
+
abbreviations: {
|
|
52
|
+
allow: ['GDP'], // treat as universal, skip the check
|
|
53
|
+
deny: ['ETA'], // demote a baseline entry back to "must be explained"
|
|
54
|
+
},
|
|
55
|
+
} satisfies CourseConfig;
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```markdown
|
|
59
|
+
---
|
|
60
|
+
key: ols
|
|
61
|
+
abbr: OLS
|
|
62
|
+
expansion: Ordinary least squares
|
|
63
|
+
aliases: []
|
|
64
|
+
---
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Run `pnpm validate:content` (twice) and `pnpm content check --changed`. Fix
|
|
68
|
+
every `abbreviation-explained` diagnostic by adding a
|
|
69
|
+
`content/abbreviations/<key>.md` entry or an `allow` entry in
|
|
70
|
+
`course.config.ts`.
|
|
71
|
+
|
|
72
|
+
### 2. Manifest schema is v13 (additive: v10 → v11 → v12 → v13)
|
|
73
|
+
|
|
74
|
+
`lessons[].lessonExports`-derived fields (v11), `manifest.abbreviations` (v12),
|
|
75
|
+
and `manifest.diagnostics.rawFloats` (v13) are new. A client asserting on
|
|
76
|
+
`MANIFEST_SCHEMA_VERSION` by exact equality needs the bump; nothing existing
|
|
77
|
+
changed shape.
|
|
78
|
+
|
|
79
|
+
### 3. Optional: enable standalone HTML / print lesson exports
|
|
80
|
+
|
|
81
|
+
No action required to keep building. To opt in, add to
|
|
82
|
+
`content/course.config.ts`:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
lessonExports: {
|
|
86
|
+
standaloneHtml: true,
|
|
87
|
+
print: true,
|
|
88
|
+
},
|
|
89
|
+
canonicalBaseUrl: 'https://your-course.example.com',
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 4. Optional: turn on strict numbering cross-checks
|
|
93
|
+
|
|
94
|
+
`remarkNotation`'s new `strict: true` option — recommended via
|
|
95
|
+
`createRemarkNotationOptions(manifest, { strict: true })` — fails the build on
|
|
96
|
+
any catalogued lesson that has an equation, `<Table>`, or `<Figure>` (keyed or
|
|
97
|
+
not) with no placement, instead of silently falling back to legacy `##`-section
|
|
98
|
+
numbering. Off by default; turn it on once existing content is clean.
|
|
99
|
+
|
|
100
|
+
### Verification
|
|
101
|
+
|
|
102
|
+
`pnpm validate:content` (run it twice), then `pnpm content check --changed`,
|
|
103
|
+
then `astro check`, then `astro build`.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
22
107
|
## 0.3.x → 0.4.0
|
|
23
108
|
|
|
24
109
|
### 1. Rename `sections` to `parts` in `content/course.config.ts`
|