explico 0.1.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/README.md +108 -0
- package/dist/analytics/AnalyticsEmitter.d.ts +22 -0
- package/dist/analytics/AnalyticsEmitter.js +5 -0
- package/dist/compiler/cli.d.ts +189 -0
- package/dist/compiler/cli.js +552 -0
- package/dist/compiler/collections.d.ts +70 -0
- package/dist/compiler/collections.js +383 -0
- package/dist/compiler/course-config.d.ts +31 -0
- package/dist/compiler/course-config.js +0 -0
- package/dist/compiler/lesson-derivation.d.ts +89 -0
- package/dist/compiler/lesson-derivation.js +91 -0
- package/dist/compiler/manifest.d.ts +178 -0
- package/dist/compiler/manifest.js +345 -0
- package/dist/compiler/resolution-report.d.ts +45 -0
- package/dist/compiler/resolution-report.js +97 -0
- package/dist/curriculum/validation.d.ts +76 -0
- package/dist/curriculum/validation.js +320 -0
- package/dist/progress/ProgressRepository.d.ts +53 -0
- package/dist/progress/ProgressRepository.js +12 -0
- package/dist/reference/base-library.mjs +27 -0
- package/dist/reference/citation-format.mjs +133 -0
- package/dist/reference/consistency.d.ts +64 -0
- package/dist/reference/consistency.js +234 -0
- package/dist/reference/curriculum-alignment.d.ts +7 -0
- package/dist/reference/curriculum-alignment.js +166 -0
- package/dist/reference/equations-validate.d.ts +50 -0
- package/dist/reference/equations-validate.js +90 -0
- package/dist/reference/equations.mjs +144 -0
- package/dist/reference/gate-math.d.ts +41 -0
- package/dist/reference/gate-math.js +151 -0
- package/dist/reference/katex-options.mjs +91 -0
- package/dist/reference/lab-math-scope.d.ts +21 -0
- package/dist/reference/lab-math-scope.js +28 -0
- package/dist/reference/label.d.ts +11 -0
- package/dist/reference/label.js +38 -0
- package/dist/reference/lint-ignore.d.ts +32 -0
- package/dist/reference/lint-ignore.js +39 -0
- package/dist/reference/math-glyphs.mjs +830 -0
- package/dist/reference/prose.d.ts +15 -0
- package/dist/reference/prose.js +12 -0
- package/dist/reference/references.d.ts +17 -0
- package/dist/reference/references.js +42 -0
- package/dist/reference/registry.d.ts +6 -0
- package/dist/reference/registry.js +548 -0
- package/dist/reference/rehype-fail-katex-errors.mjs +55 -0
- package/dist/reference/remark-citation.mjs +0 -0
- package/dist/reference/remark-notation.mjs +753 -0
- package/dist/reference/render-lab-math.d.ts +21 -0
- package/dist/reference/render-lab-math.js +58 -0
- package/dist/reference/types.d.ts +151 -0
- package/dist/reference/types.js +0 -0
- package/dist/reference/units-vocab.d.ts +26 -0
- package/dist/reference/units-vocab.js +119 -0
- package/dist/session/PreferenceStore.d.ts +28 -0
- package/dist/session/PreferenceStore.js +30 -0
- package/dist/session/user-context.d.ts +7 -0
- package/dist/session/user-context.js +10 -0
- package/dist/session/user.d.ts +21 -0
- package/dist/session/user.js +8 -0
- package/package.json +53 -0
- package/src/analytics/AnalyticsEmitter.ts +23 -0
- package/src/compiler/cli.ts +866 -0
- package/src/compiler/collections.ts +554 -0
- package/src/compiler/course-config.ts +32 -0
- package/src/compiler/lesson-derivation.ts +186 -0
- package/src/compiler/manifest.ts +601 -0
- package/src/compiler/resolution-report.ts +150 -0
- package/src/components/CurriculumMap.astro +208 -0
- package/src/components/HoverPanel.astro +67 -0
- package/src/components/assessments/AssessmentRunner.tsx +320 -0
- package/src/components/assessments/AssessmentSet.astro +54 -0
- package/src/components/citation/CitationLayer.astro +173 -0
- package/src/components/examples/CompactExample.astro +34 -0
- package/src/components/examples/CompactExamples.astro +253 -0
- package/src/components/hover-panel.ts +273 -0
- package/src/components/notation/NotationGlossary.astro +412 -0
- package/src/components/notation/NotationLayer.astro +594 -0
- package/src/components/notation/NotationMath.astro +24 -0
- package/src/components/notation/SafeMathNode.astro +23 -0
- package/src/components/notation/render-notation-math.ts +223 -0
- package/src/components/starlight/EquationEnhancer.astro +72 -0
- package/src/components/starlight/LayoutHeader.astro +102 -0
- package/src/components/starlight/LayoutPageSidebar.astro +33 -0
- package/src/components/starlight/LayoutSidebar.astro +29 -0
- package/src/components/starlight/LessonFooter.astro +58 -0
- package/src/content-config.ts +259 -0
- package/src/curriculum/validation.ts +481 -0
- package/src/progress/ProgressRepository.ts +64 -0
- package/src/reference/base-library.mjs +27 -0
- package/src/reference/citation-format.mjs +133 -0
- package/src/reference/consistency.ts +375 -0
- package/src/reference/curriculum-alignment.ts +228 -0
- package/src/reference/equations-validate.ts +167 -0
- package/src/reference/equations.mjs +144 -0
- package/src/reference/gate-math.ts +250 -0
- package/src/reference/katex-options.mjs +91 -0
- package/src/reference/lab-math-scope.ts +54 -0
- package/src/reference/label.ts +47 -0
- package/src/reference/lint-ignore.ts +75 -0
- package/src/reference/math-glyphs.mjs +830 -0
- package/src/reference/prose.ts +21 -0
- package/src/reference/references.ts +82 -0
- package/src/reference/registry.ts +723 -0
- package/src/reference/rehype-fail-katex-errors.mjs +55 -0
- package/src/reference/remark-citation.mjs +0 -0
- package/src/reference/remark-notation.mjs +753 -0
- package/src/reference/render-lab-math.ts +99 -0
- package/src/reference/types.ts +182 -0
- package/src/reference/units-vocab.ts +143 -0
- package/src/session/PreferenceStore.ts +54 -0
- package/src/session/user-context.ts +18 -0
- package/src/session/user.ts +24 -0
- package/src/styles/global.css +763 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
---
|
|
2
|
+
export type NotationAlignment =
|
|
3
|
+
| {
|
|
4
|
+
kind: 'competency';
|
|
5
|
+
introducedByCompetency: string;
|
|
6
|
+
introducedInLesson: string;
|
|
7
|
+
}
|
|
8
|
+
| { kind: 'general'; rationale: string };
|
|
9
|
+
|
|
10
|
+
export interface NotationLayerDefinition {
|
|
11
|
+
key: string;
|
|
12
|
+
notation: string;
|
|
13
|
+
label: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
formula?: string;
|
|
16
|
+
units?: string;
|
|
17
|
+
seeAlso?: readonly string[];
|
|
18
|
+
alignment?: NotationAlignment;
|
|
19
|
+
editorialStatus?: 'draft' | 'in-review' | 'reviewed';
|
|
20
|
+
scope?: 'local' | 'shared';
|
|
21
|
+
href?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Props {
|
|
25
|
+
definitions: NotationLayerDefinition[];
|
|
26
|
+
id?: string;
|
|
27
|
+
label?: string;
|
|
28
|
+
glossaryHref?: string;
|
|
29
|
+
/**
|
|
30
|
+
* `'full'` (default) renders the visible "Notation used on this page"
|
|
31
|
+
* disclosure plus the hover panel. `'panel'` renders only the hover panel
|
|
32
|
+
* and keeps the entry data as a hidden source, for pages that already show
|
|
33
|
+
* every definition in full (the glossary) but still want marked `[[key]]`
|
|
34
|
+
* links to reveal an explanation on hover.
|
|
35
|
+
*/
|
|
36
|
+
presentation?: 'full' | 'panel';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
import NotationMath from './NotationMath.astro';
|
|
40
|
+
import HoverPanel from '../HoverPanel.astro';
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
definitions,
|
|
44
|
+
id = 'page-notation',
|
|
45
|
+
label = 'Notation used on this page',
|
|
46
|
+
glossaryHref = `${import.meta.env.BASE_URL.replace(/\/$/, '')}/glossary/`,
|
|
47
|
+
presentation = 'full',
|
|
48
|
+
} = Astro.props;
|
|
49
|
+
|
|
50
|
+
const panelOnly = presentation === 'panel';
|
|
51
|
+
|
|
52
|
+
const keyPattern = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
|
|
53
|
+
const idPattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
54
|
+
|
|
55
|
+
if (!idPattern.test(id)) {
|
|
56
|
+
throw new Error(`Unsafe NotationLayer id: ${id}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const seen = new Set<string>();
|
|
60
|
+
const items = definitions.map((definition) => {
|
|
61
|
+
if (!keyPattern.test(definition.key)) {
|
|
62
|
+
throw new Error(`Unsafe notation key: ${definition.key}`);
|
|
63
|
+
}
|
|
64
|
+
if (seen.has(definition.key)) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Duplicate notation key in NotationLayer: ${definition.key}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
seen.add(definition.key);
|
|
70
|
+
|
|
71
|
+
const scope = definition.scope ?? 'shared';
|
|
72
|
+
const href =
|
|
73
|
+
definition.href ??
|
|
74
|
+
(scope === 'local'
|
|
75
|
+
? `#notation-${definition.key}`
|
|
76
|
+
: `${glossaryHref}#notation-${definition.key}`);
|
|
77
|
+
|
|
78
|
+
return { ...definition, scope, href };
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const panelId = `${id}-panel`;
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
{
|
|
85
|
+
items.length > 0 && (
|
|
86
|
+
<section
|
|
87
|
+
class:list={['notation-layer', panelOnly && 'notation-layer--panel-only']}
|
|
88
|
+
data-notation-layer
|
|
89
|
+
data-notation-layer-id={id}
|
|
90
|
+
aria-labelledby={panelOnly ? undefined : `${id}-heading`}
|
|
91
|
+
aria-label={panelOnly ? 'Notation explanations' : undefined}
|
|
92
|
+
>
|
|
93
|
+
{!panelOnly && (
|
|
94
|
+
<h2 id={`${id}-heading`} class="notation-layer-heading">
|
|
95
|
+
Page notation
|
|
96
|
+
</h2>
|
|
97
|
+
)}
|
|
98
|
+
|
|
99
|
+
<details class="notation-list" hidden={panelOnly}>
|
|
100
|
+
<summary>
|
|
101
|
+
{label} <span class="notation-count">({items.length})</span>
|
|
102
|
+
</summary>
|
|
103
|
+
<dl>
|
|
104
|
+
{items.map((definition) => (
|
|
105
|
+
<div
|
|
106
|
+
class="notation-entry"
|
|
107
|
+
id={panelOnly ? undefined : `notation-${definition.key}`}
|
|
108
|
+
data-notation-entry={definition.key}
|
|
109
|
+
data-notation-href={definition.href}
|
|
110
|
+
>
|
|
111
|
+
<dt>
|
|
112
|
+
<span
|
|
113
|
+
class="notation-symbol-trigger"
|
|
114
|
+
data-notation-symbol-trigger
|
|
115
|
+
data-notation-key={definition.key}
|
|
116
|
+
>
|
|
117
|
+
<NotationMath
|
|
118
|
+
class="notation-symbol"
|
|
119
|
+
latex={definition.notation}
|
|
120
|
+
/>
|
|
121
|
+
</span>
|
|
122
|
+
<a
|
|
123
|
+
href={definition.href}
|
|
124
|
+
data-notation-link={definition.key}
|
|
125
|
+
aria-label={`${definition.label}: open the full definition`}
|
|
126
|
+
>
|
|
127
|
+
{definition.label}
|
|
128
|
+
</a>
|
|
129
|
+
{definition.editorialStatus &&
|
|
130
|
+
definition.editorialStatus !== 'reviewed' && (
|
|
131
|
+
<span class="notation-status">
|
|
132
|
+
{definition.editorialStatus}
|
|
133
|
+
</span>
|
|
134
|
+
)}
|
|
135
|
+
</dt>
|
|
136
|
+
<dd>
|
|
137
|
+
<p data-notation-summary>{definition.summary}</p>
|
|
138
|
+
{definition.formula && (
|
|
139
|
+
<span class="notation-formula" data-notation-formula>
|
|
140
|
+
<NotationMath latex={definition.formula} />
|
|
141
|
+
</span>
|
|
142
|
+
)}
|
|
143
|
+
{definition.units && (
|
|
144
|
+
<p class="notation-units" data-notation-units>
|
|
145
|
+
<strong>Units:</strong> {definition.units}
|
|
146
|
+
</p>
|
|
147
|
+
)}
|
|
148
|
+
<button
|
|
149
|
+
class="notation-pin"
|
|
150
|
+
type="button"
|
|
151
|
+
data-notation-control={definition.key}
|
|
152
|
+
aria-controls={panelId}
|
|
153
|
+
aria-expanded="false"
|
|
154
|
+
aria-pressed="false"
|
|
155
|
+
hidden
|
|
156
|
+
>
|
|
157
|
+
Pin explanation
|
|
158
|
+
</button>
|
|
159
|
+
</dd>
|
|
160
|
+
</div>
|
|
161
|
+
))}
|
|
162
|
+
</dl>
|
|
163
|
+
</details>
|
|
164
|
+
|
|
165
|
+
{!panelOnly && (
|
|
166
|
+
<p class="notation-enhancement-help" data-notation-help hidden>
|
|
167
|
+
Point to a marked symbol for a quick explanation, or use the list
|
|
168
|
+
above. Select “Pin explanation” to keep one open; press Escape to
|
|
169
|
+
close it.
|
|
170
|
+
</p>
|
|
171
|
+
)}
|
|
172
|
+
|
|
173
|
+
<HoverPanel id={panelId} label="Notation explanation" data-notation-panel>
|
|
174
|
+
<div class="notation-panel-body">
|
|
175
|
+
<header class="notation-panel-heading">
|
|
176
|
+
<span class="notation-panel-symbol" data-panel-symbol />
|
|
177
|
+
<h3 data-panel-title />
|
|
178
|
+
</header>
|
|
179
|
+
<p data-panel-summary />
|
|
180
|
+
<div data-panel-formula hidden />
|
|
181
|
+
<p data-panel-units hidden />
|
|
182
|
+
<a data-panel-link>Open the full definition</a>
|
|
183
|
+
</div>
|
|
184
|
+
</HoverPanel>
|
|
185
|
+
</section>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
<script>
|
|
190
|
+
import { createHoverPanel, closestElement } from '../hover-panel';
|
|
191
|
+
|
|
192
|
+
const triggerSelector = '[data-notation-key]';
|
|
193
|
+
|
|
194
|
+
function setOptionalText(element: HTMLElement, value: string) {
|
|
195
|
+
element.textContent = value;
|
|
196
|
+
element.hidden = value.length === 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function setupNotationLayer(root: HTMLElement) {
|
|
200
|
+
if (root.dataset.notationEnhanced === 'true') return;
|
|
201
|
+
root.dataset.notationEnhanced = 'true';
|
|
202
|
+
|
|
203
|
+
const entryElements = Array.from(
|
|
204
|
+
root.querySelectorAll<HTMLElement>('[data-notation-entry]'),
|
|
205
|
+
);
|
|
206
|
+
const entries = new Map(
|
|
207
|
+
entryElements.map((entry) => [entry.dataset.notationEntry ?? '', entry]),
|
|
208
|
+
);
|
|
209
|
+
entries.delete('');
|
|
210
|
+
if (entries.size === 0) return;
|
|
211
|
+
|
|
212
|
+
const panel = root.querySelector<HTMLElement>('[data-notation-panel]');
|
|
213
|
+
const panelSymbol = root.querySelector<HTMLElement>('[data-panel-symbol]');
|
|
214
|
+
const panelTitle = root.querySelector<HTMLElement>('[data-panel-title]');
|
|
215
|
+
const panelSummary = root.querySelector<HTMLElement>(
|
|
216
|
+
'[data-panel-summary]',
|
|
217
|
+
);
|
|
218
|
+
const panelFormula = root.querySelector<HTMLElement>(
|
|
219
|
+
'[data-panel-formula]',
|
|
220
|
+
);
|
|
221
|
+
const panelUnits = root.querySelector<HTMLElement>('[data-panel-units]');
|
|
222
|
+
const panelLink =
|
|
223
|
+
root.querySelector<HTMLAnchorElement>('[data-panel-link]');
|
|
224
|
+
if (
|
|
225
|
+
!panel ||
|
|
226
|
+
!panelSymbol ||
|
|
227
|
+
!panelTitle ||
|
|
228
|
+
!panelSummary ||
|
|
229
|
+
!panelFormula ||
|
|
230
|
+
!panelUnits ||
|
|
231
|
+
!panelLink
|
|
232
|
+
) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const controls = Array.from(
|
|
237
|
+
root.querySelectorAll<HTMLButtonElement>('[data-notation-control]'),
|
|
238
|
+
);
|
|
239
|
+
const triggers = Array.from(
|
|
240
|
+
document.querySelectorAll<HTMLElement>(triggerSelector),
|
|
241
|
+
).filter(
|
|
242
|
+
(element) =>
|
|
243
|
+
!root.contains(element) &&
|
|
244
|
+
!element.closest('.katex-mathml') &&
|
|
245
|
+
entries.has(element.dataset.notationKey ?? ''),
|
|
246
|
+
);
|
|
247
|
+
const triggerSet = new Set(triggers);
|
|
248
|
+
|
|
249
|
+
// Symbol glyphs rendered inside the page-notation list. They live within
|
|
250
|
+
// `root`, so the external-marker filter above deliberately excludes them;
|
|
251
|
+
// wire them as their own trigger set so pointing at a listed symbol opens
|
|
252
|
+
// the same explanation as pointing at one embedded in prose or an equation.
|
|
253
|
+
const symbolTriggers = Array.from(
|
|
254
|
+
root.querySelectorAll<HTMLElement>('[data-notation-symbol-trigger]'),
|
|
255
|
+
).filter((element) => entries.has(element.dataset.notationKey ?? ''));
|
|
256
|
+
const symbolTriggerSet = new Set(symbolTriggers);
|
|
257
|
+
const highlightTargets = [...triggers, ...symbolTriggers];
|
|
258
|
+
|
|
259
|
+
for (const trigger of highlightTargets) {
|
|
260
|
+
trigger.classList.add('notation-trigger');
|
|
261
|
+
}
|
|
262
|
+
for (const control of controls) control.hidden = false;
|
|
263
|
+
const help = root.querySelector<HTMLElement>('[data-notation-help]');
|
|
264
|
+
if (help) help.hidden = false;
|
|
265
|
+
|
|
266
|
+
const keyFor = (element: HTMLElement | null) =>
|
|
267
|
+
element?.dataset.notationKey ??
|
|
268
|
+
element?.dataset.notationControl ??
|
|
269
|
+
element?.dataset.notationLink ??
|
|
270
|
+
null;
|
|
271
|
+
|
|
272
|
+
const externalTriggerFrom = (node: EventTarget | null) => {
|
|
273
|
+
const candidate = closestElement(node, triggerSelector);
|
|
274
|
+
return candidate && triggerSet.has(candidate) ? candidate : null;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const symbolTriggerFrom = (node: EventTarget | null) => {
|
|
278
|
+
const candidate = closestElement(node, '[data-notation-symbol-trigger]');
|
|
279
|
+
return candidate && symbolTriggerSet.has(candidate) ? candidate : null;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const controlFrom = (node: EventTarget | null) => {
|
|
283
|
+
const candidate = closestElement(node, '[data-notation-control]');
|
|
284
|
+
return candidate && root.contains(candidate) ? candidate : null;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const staticLinkFrom = (node: EventTarget | null) => {
|
|
288
|
+
const candidate = closestElement(node, '[data-notation-link]');
|
|
289
|
+
return candidate && root.contains(candidate) ? candidate : null;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const triggerFrom = (node: EventTarget | null) =>
|
|
293
|
+
controlFrom(node) ??
|
|
294
|
+
staticLinkFrom(node) ??
|
|
295
|
+
externalTriggerFrom(node) ??
|
|
296
|
+
symbolTriggerFrom(node);
|
|
297
|
+
|
|
298
|
+
const render = (_anchor: HTMLElement, key: string) => {
|
|
299
|
+
const entry = entries.get(key);
|
|
300
|
+
if (!entry) return false;
|
|
301
|
+
|
|
302
|
+
const sourceSymbol = entry.querySelector<HTMLElement>('.notation-symbol');
|
|
303
|
+
panelSymbol.replaceChildren(
|
|
304
|
+
...(sourceSymbol
|
|
305
|
+
? [sourceSymbol.cloneNode(true)]
|
|
306
|
+
: [document.createTextNode(entry.dataset.notationEntry ?? key)]),
|
|
307
|
+
);
|
|
308
|
+
panelTitle.textContent =
|
|
309
|
+
entry
|
|
310
|
+
.querySelector<HTMLAnchorElement>('[data-notation-link]')
|
|
311
|
+
?.textContent?.trim() ?? key;
|
|
312
|
+
panelSummary.textContent =
|
|
313
|
+
entry
|
|
314
|
+
.querySelector<HTMLElement>('[data-notation-summary]')
|
|
315
|
+
?.textContent?.trim() ?? '';
|
|
316
|
+
const sourceFormula = entry.querySelector<HTMLElement>(
|
|
317
|
+
'[data-notation-formula]',
|
|
318
|
+
);
|
|
319
|
+
panelFormula.replaceChildren(
|
|
320
|
+
...(sourceFormula ? [sourceFormula.cloneNode(true)] : []),
|
|
321
|
+
);
|
|
322
|
+
panelFormula.hidden = sourceFormula === null;
|
|
323
|
+
setOptionalText(
|
|
324
|
+
panelUnits,
|
|
325
|
+
entry
|
|
326
|
+
.querySelector<HTMLElement>('[data-notation-units]')
|
|
327
|
+
?.textContent?.trim() ?? '',
|
|
328
|
+
);
|
|
329
|
+
panelLink.href = entry.dataset.notationHref ?? `#notation-${key}`;
|
|
330
|
+
return true;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const updateControls = (pinnedKey: string | null, visible: boolean) => {
|
|
334
|
+
for (const control of controls) {
|
|
335
|
+
const selected = control.dataset.notationControl === pinnedKey;
|
|
336
|
+
control.setAttribute('aria-pressed', String(selected));
|
|
337
|
+
control.setAttribute('aria-expanded', String(selected && visible));
|
|
338
|
+
control.textContent = selected
|
|
339
|
+
? 'Unpin explanation'
|
|
340
|
+
: 'Pin explanation';
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
createHoverPanel({
|
|
345
|
+
panel,
|
|
346
|
+
root,
|
|
347
|
+
triggerFrom,
|
|
348
|
+
keyFor,
|
|
349
|
+
render,
|
|
350
|
+
pinControlFrom: controlFrom,
|
|
351
|
+
onShow: (key) => {
|
|
352
|
+
for (const trigger of highlightTargets) {
|
|
353
|
+
trigger.classList.toggle(
|
|
354
|
+
'is-notation-active',
|
|
355
|
+
trigger.dataset.notationKey === key,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
panel.dataset.activeNotationKey = key;
|
|
359
|
+
},
|
|
360
|
+
onClose: () => {
|
|
361
|
+
delete panel.dataset.activeNotationKey;
|
|
362
|
+
for (const trigger of highlightTargets) {
|
|
363
|
+
trigger.classList.remove('is-notation-active');
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
onSync: ({ pinnedKey, visible }) => updateControls(pinnedKey, visible),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
for (const root of document.querySelectorAll<HTMLElement>(
|
|
371
|
+
'[data-notation-layer]',
|
|
372
|
+
)) {
|
|
373
|
+
setupNotationLayer(root);
|
|
374
|
+
}
|
|
375
|
+
</script>
|
|
376
|
+
|
|
377
|
+
<style>
|
|
378
|
+
@layer starlight.components {
|
|
379
|
+
.notation-layer {
|
|
380
|
+
margin-block: 2.5rem 1rem;
|
|
381
|
+
color: var(--sl-color-text);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Panel-only mode contributes just the fixed-position explanation panel;
|
|
385
|
+
the section itself must take no layout space. */
|
|
386
|
+
.notation-layer--panel-only {
|
|
387
|
+
margin: 0;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.notation-layer-heading {
|
|
391
|
+
margin: 0 0 0.65rem;
|
|
392
|
+
font-size: var(--sl-text-lg);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.notation-list {
|
|
396
|
+
border: 1px solid var(--sl-color-gray-5);
|
|
397
|
+
border-radius: 0.6rem;
|
|
398
|
+
background: var(--sl-color-gray-7);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.notation-list > summary {
|
|
402
|
+
padding: 0.8rem 1rem;
|
|
403
|
+
cursor: pointer;
|
|
404
|
+
font-weight: 600;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.notation-count,
|
|
408
|
+
.notation-enhancement-help {
|
|
409
|
+
color: var(--sl-color-gray-3);
|
|
410
|
+
font-weight: 400;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.notation-list dl {
|
|
414
|
+
display: grid;
|
|
415
|
+
gap: 0;
|
|
416
|
+
margin: 0;
|
|
417
|
+
padding: 0 1rem 1rem;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.notation-entry {
|
|
421
|
+
display: grid;
|
|
422
|
+
grid-template-columns: minmax(9rem, 0.65fr) minmax(12rem, 1.35fr);
|
|
423
|
+
gap: 0.5rem 1rem;
|
|
424
|
+
padding-block: 0.85rem;
|
|
425
|
+
border-top: 1px solid var(--sl-color-gray-5);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.notation-entry dt,
|
|
429
|
+
.notation-entry dd,
|
|
430
|
+
.notation-entry p {
|
|
431
|
+
margin: 0;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.notation-entry dt {
|
|
435
|
+
display: flex;
|
|
436
|
+
flex-wrap: wrap;
|
|
437
|
+
align-items: baseline;
|
|
438
|
+
gap: 0.35rem 0.65rem;
|
|
439
|
+
font-weight: 600;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.notation-entry dd {
|
|
443
|
+
display: grid;
|
|
444
|
+
gap: 0.45rem;
|
|
445
|
+
color: var(--sl-color-gray-2);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.notation-symbol {
|
|
449
|
+
min-width: 2.25rem;
|
|
450
|
+
color: var(--sl-color-accent-high);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.notation-symbol-trigger {
|
|
454
|
+
border-radius: 0.16em;
|
|
455
|
+
cursor: help;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.notation-symbol-trigger:hover,
|
|
459
|
+
.notation-symbol-trigger.is-notation-active {
|
|
460
|
+
background: var(--sl-color-accent-low);
|
|
461
|
+
box-shadow: 0 0 0 0.12em var(--sl-color-accent-low);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
@media (forced-colors: active) {
|
|
465
|
+
.notation-symbol-trigger:hover,
|
|
466
|
+
.notation-symbol-trigger.is-notation-active {
|
|
467
|
+
outline: 2px solid Highlight;
|
|
468
|
+
color: HighlightText;
|
|
469
|
+
background: Highlight;
|
|
470
|
+
box-shadow: none;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.notation-status {
|
|
475
|
+
padding: 0.08rem 0.35rem;
|
|
476
|
+
border: 1px solid var(--sl-color-gray-5);
|
|
477
|
+
border-radius: 999px;
|
|
478
|
+
color: var(--sl-color-gray-3);
|
|
479
|
+
font-size: var(--sl-text-xs);
|
|
480
|
+
font-weight: 500;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.notation-formula,
|
|
484
|
+
.notation-units {
|
|
485
|
+
font-size: var(--sl-text-sm);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.notation-formula {
|
|
489
|
+
overflow-x: auto;
|
|
490
|
+
padding-block: 0.15rem;
|
|
491
|
+
color: var(--sl-color-text);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.notation-pin {
|
|
495
|
+
justify-self: start;
|
|
496
|
+
padding: 0.25rem 0.55rem;
|
|
497
|
+
border: 1px solid var(--sl-color-gray-5);
|
|
498
|
+
border-radius: 0.35rem;
|
|
499
|
+
color: var(--sl-color-text-accent);
|
|
500
|
+
background: var(--sl-color-bg);
|
|
501
|
+
font: inherit;
|
|
502
|
+
font-size: var(--sl-text-sm);
|
|
503
|
+
cursor: pointer;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.notation-pin:hover,
|
|
507
|
+
.notation-pin[aria-pressed='true'] {
|
|
508
|
+
border-color: var(--sl-color-accent);
|
|
509
|
+
background: var(--sl-color-accent-low);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.notation-enhancement-help {
|
|
513
|
+
margin: 0.55rem 0 0;
|
|
514
|
+
font-size: var(--sl-text-xs);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* The fixed-position shell is `HoverPanel`; these rules style only the
|
|
518
|
+
notation-owned fields slotted into it. */
|
|
519
|
+
.notation-panel-heading {
|
|
520
|
+
display: flex;
|
|
521
|
+
align-items: baseline;
|
|
522
|
+
gap: 0.7rem;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.notation-panel-heading h3,
|
|
526
|
+
.notation-panel-body p {
|
|
527
|
+
margin: 0;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.notation-panel-heading h3 {
|
|
531
|
+
font-size: var(--sl-text-base);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.notation-panel-body p {
|
|
535
|
+
margin-block: 0.65rem;
|
|
536
|
+
color: var(--sl-color-gray-2);
|
|
537
|
+
font-size: var(--sl-text-sm);
|
|
538
|
+
line-height: 1.5;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.notation-panel-body [data-panel-formula] {
|
|
542
|
+
overflow-x: auto;
|
|
543
|
+
margin-block: 0.65rem;
|
|
544
|
+
color: var(--sl-color-text);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.notation-panel-symbol {
|
|
548
|
+
color: var(--sl-color-accent-high);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.notation-layer :focus-visible {
|
|
552
|
+
outline: 3px solid var(--sl-color-accent);
|
|
553
|
+
outline-offset: 2px;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
:global(.katex-html [data-notation-key]),
|
|
558
|
+
:global(.notation-term[data-notation-key]) {
|
|
559
|
+
border-radius: 0.16em;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/* KaTeX represents vertical-list depth with a guaranteed-empty second
|
|
563
|
+
table row. Because that row is painted last, it can otherwise intercept
|
|
564
|
+
pointer hits meant for visible notation in fractions and subscripts. */
|
|
565
|
+
:global(.katex-html .vlist-t2 > .vlist-r:nth-child(2)) {
|
|
566
|
+
pointer-events: none;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
:global(.katex-html [data-notation-key]) {
|
|
570
|
+
cursor: help;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
:global(.katex-html [data-notation-key]:hover),
|
|
574
|
+
:global([data-notation-key].is-notation-active) {
|
|
575
|
+
background: var(--sl-color-accent-low);
|
|
576
|
+
box-shadow: 0 0 0 0.12em var(--sl-color-accent-low);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
@media (max-width: 40rem) {
|
|
580
|
+
.notation-entry {
|
|
581
|
+
grid-template-columns: 1fr;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
@media (forced-colors: active) {
|
|
586
|
+
:global(.katex-html [data-notation-key]:hover),
|
|
587
|
+
:global([data-notation-key].is-notation-active) {
|
|
588
|
+
outline: 2px solid Highlight;
|
|
589
|
+
color: HighlightText;
|
|
590
|
+
background: Highlight;
|
|
591
|
+
box-shadow: none;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { renderNotationMath } from './render-notation-math';
|
|
3
|
+
import SafeMathNode from './SafeMathNode.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
latex: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { latex, class: className } = Astro.props;
|
|
11
|
+
const nodes = renderNotationMath(latex);
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<span class:list={['notation-math', className]} data-notation-math>
|
|
15
|
+
{nodes.map((node) => <SafeMathNode node={node} />)}
|
|
16
|
+
</span>
|
|
17
|
+
|
|
18
|
+
<style>
|
|
19
|
+
.notation-math :global(.katex) {
|
|
20
|
+
/* Compact labels inherit their surrounding text size. KaTeX's own sizing
|
|
21
|
+
classes retain the smaller scale for subscripts and superscripts. */
|
|
22
|
+
font-size: 1em;
|
|
23
|
+
}
|
|
24
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SafeMathNode as Node } from './render-notation-math';
|
|
3
|
+
import SafeMathNode from './SafeMathNode.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
node: Node;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { node } = Astro.props;
|
|
10
|
+
const Element = node.type === 'element' ? node.tagName : undefined;
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
node.type === 'text'
|
|
15
|
+
? node.value
|
|
16
|
+
: Element && (
|
|
17
|
+
<Element {...node.properties}>
|
|
18
|
+
{node.children.map((child) => (
|
|
19
|
+
<SafeMathNode node={child} />
|
|
20
|
+
))}
|
|
21
|
+
</Element>
|
|
22
|
+
)
|
|
23
|
+
}
|