explico 0.5.0 → 0.7.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 +295 -7
- package/README.md +16 -2
- package/dist/compiler/astro.d.ts +13 -2
- package/dist/compiler/astro.js +39 -0
- package/dist/compiler/cli.d.ts +22 -3
- package/dist/compiler/cli.js +71 -10
- package/dist/compiler/course-config.d.ts +31 -1
- package/dist/compiler/course-config.js +7 -0
- package/dist/compiler/exports/standalone-html.js +13 -0
- package/dist/compiler/logging.d.ts +109 -0
- package/dist/compiler/logging.js +107 -0
- package/dist/compiler/manifest.d.ts +16 -4
- package/dist/compiler/manifest.js +7 -3
- package/dist/reference/base-abbreviations.mjs +8 -2
- package/dist/reference/consistency.d.ts +8 -10
- package/dist/reference/consistency.js +3 -47
- package/dist/reference/delimiter-size.d.ts +39 -0
- package/dist/reference/delimiter-size.js +177 -0
- package/dist/reference/diagrams-validate.d.ts +1 -1
- package/dist/reference/equations-validate.d.ts +2 -2
- package/dist/reference/equations-validate.js +1 -1
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/gate-heading-length.d.ts +12 -0
- package/dist/reference/gate-heading-length.js +79 -0
- package/dist/reference/gate-math.d.ts +8 -2
- package/dist/reference/gate-math.js +30 -7
- package/dist/reference/keyed-float-validate.d.ts +2 -2
- package/dist/reference/keyed-float-validate.js +1 -1
- package/dist/reference/math-glyphs.d.mts +12 -0
- package/dist/reference/math-glyphs.mjs +183 -42
- package/dist/reference/references.js +2 -1
- package/dist/reference/registry.js +1 -1
- package/dist/reference/rehype-base-links.d.mts +23 -0
- package/dist/reference/rehype-base-links.mjs +40 -0
- package/dist/reference/remark-abbreviations.mjs +10 -12
- package/dist/reference/remark-citation.d.mts +5 -1
- package/dist/reference/remark-citation.mjs +0 -0
- package/dist/reference/render-lab-math.d.ts +18 -1
- package/dist/reference/render-lab-math.js +10 -3
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/types.d.ts +1 -1
- package/docs/VERSIONING.md +24 -4
- package/docs/authoring/README.md +14 -2
- package/docs/authoring/agents.md +7 -3
- package/docs/authoring/components.md +4 -1
- package/docs/authoring/migrating.md +477 -8
- package/docs/authoring/notation.md +194 -7
- package/docs/authoring/prose.md +64 -0
- package/docs/authoring/structure-rules.md +69 -0
- package/docs/authoring/visualizations.md +2 -2
- package/package.json +1 -1
- package/src/compiler/astro.ts +89 -0
- package/src/compiler/cli.ts +144 -12
- package/src/compiler/course-config.ts +40 -0
- package/src/compiler/exports/standalone-html.ts +15 -0
- package/src/compiler/logging.ts +248 -0
- package/src/compiler/manifest.ts +20 -3
- package/src/components/diagram/Diagram.astro +0 -55
- package/src/components/memo/Memo.astro +2 -5
- package/src/components/memo/ShortNote.astro +2 -5
- package/src/components/notation/NotationFormula.astro +199 -10
- package/src/components/notation/NotationGlossary.astro +89 -58
- package/src/components/notation/NotationLayer.astro +238 -156
- package/src/components/scroll-animation.ts +160 -0
- package/src/components/starlight/AnchorScrollEnhancer.astro +102 -0
- package/src/components/starlight/HeadingAnchorEnhancer.astro +2 -4
- package/src/components/starlight/KeyedFloatEnhancer.astro +17 -16
- package/src/components/starlight/LessonFooter.astro +5 -1
- package/src/content-config.ts +16 -1
- package/src/reference/base-abbreviations.mjs +8 -2
- package/src/reference/consistency.ts +10 -66
- package/src/reference/delimiter-size.ts +301 -0
- package/src/reference/diagrams-validate.ts +1 -1
- package/src/reference/equations-validate.ts +3 -3
- package/src/reference/figures-validate.ts +1 -1
- package/src/reference/gate-heading-length.ts +131 -0
- package/src/reference/gate-math.ts +48 -8
- package/src/reference/keyed-float-validate.ts +3 -3
- package/src/reference/math-glyphs.mjs +183 -42
- package/src/reference/references.ts +2 -1
- package/src/reference/registry.ts +1 -1
- package/src/reference/rehype-base-links.mjs +40 -0
- package/src/reference/remark-abbreviations.mjs +10 -12
- package/src/reference/remark-citation.mjs +0 -0
- package/src/reference/render-lab-math.ts +24 -1
- package/src/reference/tables-validate.ts +1 -1
- package/src/reference/types.ts +1 -1
- package/src/styles/global.css +120 -15
|
@@ -42,6 +42,10 @@ export interface Props {
|
|
|
42
42
|
* links to reveal an explanation on hover.
|
|
43
43
|
*/
|
|
44
44
|
presentation?: 'full' | 'panel';
|
|
45
|
+
/** Author-chosen state for a reader with nothing saved yet. Default: `false` (closed) — see `docs/authoring` on `CourseConfig.disclosureDefaults`. */
|
|
46
|
+
defaultOpen?: boolean;
|
|
47
|
+
/** Persist the reader's open/closed choice across visits, per page. Default: `true`. */
|
|
48
|
+
rememberState?: boolean;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
import NotationMath from './NotationMath.astro';
|
|
@@ -55,6 +59,8 @@ const {
|
|
|
55
59
|
label = 'Notation used on this page',
|
|
56
60
|
glossaryHref = `${import.meta.env.BASE_URL.replace(/\/$/, '')}/glossary/`,
|
|
57
61
|
presentation = 'full',
|
|
62
|
+
defaultOpen = false,
|
|
63
|
+
rememberState = true,
|
|
58
64
|
} = Astro.props;
|
|
59
65
|
|
|
60
66
|
const panelOnly = presentation === 'panel';
|
|
@@ -111,16 +117,16 @@ for (const gloss of glossItems) {
|
|
|
111
117
|
class:list={['notation-layer', panelOnly && 'notation-layer--panel-only']}
|
|
112
118
|
data-notation-layer
|
|
113
119
|
data-notation-layer-id={id}
|
|
114
|
-
aria-
|
|
115
|
-
aria-label={panelOnly ? 'Notation explanations' : undefined}
|
|
120
|
+
aria-label={panelOnly ? 'Notation explanations' : label}
|
|
116
121
|
>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
<div class="notation-list-titlebar">
|
|
123
|
+
<details
|
|
124
|
+
class="notation-list"
|
|
125
|
+
id={id}
|
|
126
|
+
hidden={panelOnly}
|
|
127
|
+
data-remember-state={String(rememberState)}
|
|
128
|
+
open={defaultOpen}
|
|
129
|
+
>
|
|
124
130
|
<summary>
|
|
125
131
|
{label} <span class="notation-count">({items.length})</span>
|
|
126
132
|
</summary>
|
|
@@ -183,16 +189,6 @@ for (const gloss of glossItems) {
|
|
|
183
189
|
<strong>Units:</strong> {definition.units}
|
|
184
190
|
</p>
|
|
185
191
|
)}
|
|
186
|
-
<button
|
|
187
|
-
class="notation-pin"
|
|
188
|
-
type="button"
|
|
189
|
-
data-notation-control={definition.key}
|
|
190
|
-
aria-expanded="false"
|
|
191
|
-
aria-pressed="false"
|
|
192
|
-
hidden
|
|
193
|
-
>
|
|
194
|
-
Pin explanation
|
|
195
|
-
</button>
|
|
196
192
|
</dd>
|
|
197
193
|
</div>
|
|
198
194
|
))}
|
|
@@ -216,14 +212,32 @@ for (const gloss of glossItems) {
|
|
|
216
212
|
</div>
|
|
217
213
|
))}
|
|
218
214
|
</details>
|
|
215
|
+
{!panelOnly && (
|
|
216
|
+
<a
|
|
217
|
+
class="notation-list-anchor"
|
|
218
|
+
href={`#${id}`}
|
|
219
|
+
aria-label={`${label}, titled section`}
|
|
220
|
+
>
|
|
221
|
+
<svg
|
|
222
|
+
aria-hidden="true"
|
|
223
|
+
width="16"
|
|
224
|
+
height="16"
|
|
225
|
+
viewBox="0 0 24 24"
|
|
226
|
+
fill="currentColor"
|
|
227
|
+
><path
|
|
228
|
+
d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"
|
|
229
|
+
></path></svg
|
|
230
|
+
>
|
|
231
|
+
</a>
|
|
232
|
+
)}
|
|
233
|
+
</div>
|
|
219
234
|
|
|
220
235
|
{!panelOnly && (
|
|
221
236
|
<p class="notation-enhancement-help" data-notation-help hidden>
|
|
222
237
|
Point to a marked symbol for a quick explanation, or use the list
|
|
223
238
|
above — pointing at a symbol inside an open card explains that one
|
|
224
|
-
too, alongside it. Click a symbol
|
|
225
|
-
|
|
226
|
-
Escape.
|
|
239
|
+
too, alongside it. Click a symbol to keep its card open; click the
|
|
240
|
+
card (or its “×”) to close it, or press Escape.
|
|
227
241
|
</p>
|
|
228
242
|
)}
|
|
229
243
|
|
|
@@ -249,14 +263,6 @@ for (const gloss of glossItems) {
|
|
|
249
263
|
</header>
|
|
250
264
|
<p data-slot-summary hidden />
|
|
251
265
|
<div data-slot-formula hidden />
|
|
252
|
-
<button
|
|
253
|
-
class="notation-popup-copy"
|
|
254
|
-
type="button"
|
|
255
|
-
data-popup-copy
|
|
256
|
-
hidden
|
|
257
|
-
>
|
|
258
|
-
Copy LaTeX
|
|
259
|
-
</button>
|
|
260
266
|
<p class="notation-units" data-slot-units hidden />
|
|
261
267
|
<a data-slot-link hidden>
|
|
262
268
|
Open the full definition
|
|
@@ -270,6 +276,7 @@ for (const gloss of glossItems) {
|
|
|
270
276
|
|
|
271
277
|
<script>
|
|
272
278
|
import { createPopupStack, closestElement } from '../popup-stack';
|
|
279
|
+
import { wirePersistedDetails } from '../disclosure/persisted-disclosure';
|
|
273
280
|
|
|
274
281
|
const triggerSelector = '[data-notation-key]';
|
|
275
282
|
|
|
@@ -296,10 +303,6 @@ for (const gloss of glossItems) {
|
|
|
296
303
|
);
|
|
297
304
|
if (!template) return;
|
|
298
305
|
|
|
299
|
-
const controls = Array.from(
|
|
300
|
-
root.querySelectorAll<HTMLButtonElement>('[data-notation-control]'),
|
|
301
|
-
);
|
|
302
|
-
|
|
303
306
|
// Static triggers, computed once: markers embedded in prose or an
|
|
304
307
|
// equation, and the symbol glyphs in the "Notation used on this page"
|
|
305
308
|
// list. These get the always-on `.notation-trigger` cursor/hover
|
|
@@ -324,20 +327,32 @@ for (const gloss of glossItems) {
|
|
|
324
327
|
for (const trigger of highlightTargets) {
|
|
325
328
|
trigger.classList.add('notation-trigger');
|
|
326
329
|
}
|
|
327
|
-
for (const control of controls) control.hidden = false;
|
|
328
330
|
const help = root.querySelector<HTMLElement>('[data-notation-help]');
|
|
329
331
|
if (help) help.hidden = false;
|
|
330
332
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
333
|
+
// The visible list's own symbol glyphs are the only keyboard path to
|
|
334
|
+
// pinning a card from this list (the old "Pin explanation" button was
|
|
335
|
+
// the other one, and is gone) — make them a real, labelled, Enter/Space-
|
|
336
|
+
// operable control rather than a mouse-only `<span>`. `symbolTriggers`
|
|
337
|
+
// (defined above) is already scoped to entries inside this list.
|
|
338
|
+
for (const trigger of symbolTriggers) {
|
|
339
|
+
const key = trigger.dataset.notationKey ?? '';
|
|
340
|
+
const entryLabel =
|
|
341
|
+
entries.get(key)?.querySelector<HTMLElement>('[data-notation-link]')
|
|
342
|
+
?.textContent?.trim() ?? key;
|
|
343
|
+
trigger.setAttribute('role', 'button');
|
|
344
|
+
trigger.tabIndex = 0;
|
|
345
|
+
trigger.setAttribute('aria-pressed', 'false');
|
|
346
|
+
trigger.setAttribute('aria-label', `Pin the ${entryLabel} explanation`);
|
|
347
|
+
trigger.addEventListener('keydown', (event) => {
|
|
348
|
+
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
349
|
+
event.preventDefault();
|
|
350
|
+
trigger.click();
|
|
351
|
+
});
|
|
352
|
+
}
|
|
336
353
|
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
return candidate && root.contains(candidate) ? candidate : null;
|
|
340
|
-
};
|
|
354
|
+
const keyFor = (element: HTMLElement | null) =>
|
|
355
|
+
element?.dataset.notationKey ?? element?.dataset.notationLink ?? null;
|
|
341
356
|
|
|
342
357
|
const staticLinkFrom = (node: EventTarget | null) => {
|
|
343
358
|
const candidate = closestElement(node, '[data-notation-link]');
|
|
@@ -357,7 +372,7 @@ for (const gloss of glossItems) {
|
|
|
357
372
|
};
|
|
358
373
|
|
|
359
374
|
const triggerFrom = (node: EventTarget | null) =>
|
|
360
|
-
|
|
375
|
+
staticLinkFrom(node) ?? symbolFrom(node);
|
|
361
376
|
|
|
362
377
|
const labelOf = (entry: HTMLElement, key: string) =>
|
|
363
378
|
entry
|
|
@@ -382,18 +397,7 @@ for (const gloss of glossItems) {
|
|
|
382
397
|
const units = fragment.querySelector<HTMLElement>('[data-slot-units]');
|
|
383
398
|
const link =
|
|
384
399
|
fragment.querySelector<HTMLAnchorElement>('[data-slot-link]');
|
|
385
|
-
|
|
386
|
-
'[data-popup-copy]',
|
|
387
|
-
);
|
|
388
|
-
if (
|
|
389
|
-
!el ||
|
|
390
|
-
!symbolHost ||
|
|
391
|
-
!title ||
|
|
392
|
-
!summary ||
|
|
393
|
-
!formulaHost ||
|
|
394
|
-
!units ||
|
|
395
|
-
!copyButton
|
|
396
|
-
) {
|
|
400
|
+
if (!el || !symbolHost || !title || !summary || !formulaHost || !units) {
|
|
397
401
|
return null;
|
|
398
402
|
}
|
|
399
403
|
|
|
@@ -420,34 +424,6 @@ for (const gloss of glossItems) {
|
|
|
420
424
|
);
|
|
421
425
|
formulaHost.hidden = sourceFormula === null;
|
|
422
426
|
|
|
423
|
-
// The formula's own `data-latex` carries the plain, authored LaTeX —
|
|
424
|
-
// not the `\explain{key}{…}`-wrapped source KaTeX actually rendered —
|
|
425
|
-
// so what a reader copies is what they'd write themselves, not an
|
|
426
|
-
// internal marker macro. See `render-notation-math.ts:renderNotationFormula`.
|
|
427
|
-
const formulaLatex = sourceFormula?.dataset.latex ?? '';
|
|
428
|
-
copyButton.hidden = formulaLatex.length === 0;
|
|
429
|
-
if (formulaLatex.length > 0) {
|
|
430
|
-
copyButton.addEventListener('click', (event) => {
|
|
431
|
-
// Otherwise this bubbles to the document click handler in
|
|
432
|
-
// `popup-stack.ts`, which treats any click inside a popup that
|
|
433
|
-
// isn't a trigger or a link as "close it" — that would remove the
|
|
434
|
-
// card before the "Copied!" flash below ever paints.
|
|
435
|
-
event.stopPropagation();
|
|
436
|
-
if (!navigator.clipboard) return;
|
|
437
|
-
navigator.clipboard.writeText(formulaLatex).then(
|
|
438
|
-
() => {
|
|
439
|
-
copyButton.dataset.copied = 'true';
|
|
440
|
-
window.setTimeout(() => {
|
|
441
|
-
delete copyButton.dataset.copied;
|
|
442
|
-
}, 1200);
|
|
443
|
-
},
|
|
444
|
-
() => {
|
|
445
|
-
/* Clipboard blocked: nothing more we can do here. */
|
|
446
|
-
},
|
|
447
|
-
);
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
|
|
451
427
|
setOptionalText(
|
|
452
428
|
units,
|
|
453
429
|
entry
|
|
@@ -465,21 +441,6 @@ for (const gloss of glossItems) {
|
|
|
465
441
|
return el;
|
|
466
442
|
};
|
|
467
443
|
|
|
468
|
-
const updateControls = (
|
|
469
|
-
open: ReadonlySet<string>,
|
|
470
|
-
pinned: ReadonlySet<string>,
|
|
471
|
-
) => {
|
|
472
|
-
for (const control of controls) {
|
|
473
|
-
const key = control.dataset.notationControl ?? '';
|
|
474
|
-
const selected = pinned.has(key);
|
|
475
|
-
control.setAttribute('aria-pressed', String(selected));
|
|
476
|
-
control.setAttribute('aria-expanded', String(open.has(key)));
|
|
477
|
-
control.textContent = selected
|
|
478
|
-
? 'Unpin explanation'
|
|
479
|
-
: 'Pin explanation';
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
|
|
483
444
|
createPopupStack({
|
|
484
445
|
// Popups are `position: fixed` and float above everything regardless
|
|
485
446
|
// of DOM placement — *except* z-index only ever wins within its own
|
|
@@ -493,7 +454,6 @@ for (const gloss of glossItems) {
|
|
|
493
454
|
triggerFrom,
|
|
494
455
|
keyFor,
|
|
495
456
|
createPopup,
|
|
496
|
-
pinControlFrom: controlFrom,
|
|
497
457
|
onChange: (open, pinned) => {
|
|
498
458
|
for (const trigger of highlightTargets) {
|
|
499
459
|
trigger.classList.toggle(
|
|
@@ -501,16 +461,123 @@ for (const gloss of glossItems) {
|
|
|
501
461
|
open.has(trigger.dataset.notationKey ?? ''),
|
|
502
462
|
);
|
|
503
463
|
}
|
|
504
|
-
|
|
464
|
+
for (const trigger of symbolTriggers) {
|
|
465
|
+
trigger.setAttribute(
|
|
466
|
+
'aria-pressed',
|
|
467
|
+
String(pinned.has(trigger.dataset.notationKey ?? '')),
|
|
468
|
+
);
|
|
469
|
+
}
|
|
505
470
|
},
|
|
506
471
|
});
|
|
507
472
|
}
|
|
508
473
|
|
|
474
|
+
function setupNotationDisclosure(details: HTMLDetailsElement) {
|
|
475
|
+
if (details.dataset.disclosureWired === 'true') return;
|
|
476
|
+
details.dataset.disclosureWired = 'true';
|
|
477
|
+
|
|
478
|
+
// Same convention as `Figure.astro`: the SSR-rendered `open` attribute
|
|
479
|
+
// already carries the author's `defaultOpen` prop verbatim.
|
|
480
|
+
const defaultOpen = details.open;
|
|
481
|
+
if (details.dataset.rememberState === 'true') {
|
|
482
|
+
wirePersistedDetails(details, `notation:${details.id}`, defaultOpen);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Deep-linking to `#page-notation` (the anchor link, or a shared URL)
|
|
486
|
+
// must reveal it even if a reader left it collapsed.
|
|
487
|
+
const openIfTargeted = () => {
|
|
488
|
+
if (location.hash === `#${details.id}`) details.open = true;
|
|
489
|
+
};
|
|
490
|
+
openIfTargeted();
|
|
491
|
+
window.addEventListener('hashchange', openIfTargeted);
|
|
492
|
+
}
|
|
493
|
+
|
|
509
494
|
for (const root of document.querySelectorAll<HTMLElement>(
|
|
510
495
|
'[data-notation-layer]',
|
|
511
496
|
)) {
|
|
512
497
|
setupNotationLayer(root);
|
|
498
|
+
const details = root.querySelector<HTMLDetailsElement>(
|
|
499
|
+
':scope > .notation-list-titlebar > .notation-list[id]',
|
|
500
|
+
);
|
|
501
|
+
if (details) setupNotationDisclosure(details);
|
|
513
502
|
}
|
|
503
|
+
|
|
504
|
+
// "Copy LaTeX" for a notation formula (a glossary entry's own numbered
|
|
505
|
+
// formula, or one named inline via `\explain{}` inside a popup/`Diagram`),
|
|
506
|
+
// two ways: its own button (only a numbered formula has one —
|
|
507
|
+
// `data-notation-formula-copy`, `NotationFormula.astro`), or a click
|
|
508
|
+
// anywhere else in the box that isn't the number link or an explained
|
|
509
|
+
// symbol — the same region `.notation-formula-math:hover` highlights
|
|
510
|
+
// (`NotationFormula.astro`), which includes the reserved gutter between
|
|
511
|
+
// the equation and its number, not just the rendered `.katex-html`. This
|
|
512
|
+
// listener lives here, not in `NotationFormula.astro` or
|
|
513
|
+
// `KeyedFloatEnhancer.astro`, because this component loads on every page —
|
|
514
|
+
// it's the Starlight `Footer` override — while `KeyedFloatEnhancer` is
|
|
515
|
+
// lesson-only and a formula can appear inside a hover popup mounted
|
|
516
|
+
// outside `.sl-markdown-content`.
|
|
517
|
+
document.addEventListener('click', (event) => {
|
|
518
|
+
const origin = event.target instanceof Element ? event.target : null;
|
|
519
|
+
if (!origin) return;
|
|
520
|
+
|
|
521
|
+
const ownButton = origin.closest<HTMLButtonElement>(
|
|
522
|
+
'[data-notation-formula-copy]',
|
|
523
|
+
);
|
|
524
|
+
const container = ownButton
|
|
525
|
+
? null
|
|
526
|
+
: origin.closest<HTMLElement>('[data-notation-formula]');
|
|
527
|
+
if (!ownButton) {
|
|
528
|
+
if (origin.closest('[data-notation-key], .notation-formula-number')) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (!container) return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const feedbackTarget =
|
|
535
|
+
ownButton ??
|
|
536
|
+
container?.querySelector<HTMLElement>('[data-notation-formula-copy]') ??
|
|
537
|
+
container;
|
|
538
|
+
const latex =
|
|
539
|
+
ownButton?.dataset.latex ?? feedbackTarget?.dataset.latex ?? '';
|
|
540
|
+
if (!feedbackTarget || latex.length === 0 || !navigator.clipboard) return;
|
|
541
|
+
|
|
542
|
+
navigator.clipboard.writeText(latex).then(
|
|
543
|
+
() => {
|
|
544
|
+
feedbackTarget.dataset.copied = 'true';
|
|
545
|
+
window.setTimeout(() => {
|
|
546
|
+
delete feedbackTarget.dataset.copied;
|
|
547
|
+
}, 1200);
|
|
548
|
+
},
|
|
549
|
+
() => {
|
|
550
|
+
/* Clipboard blocked: nothing more we can do here. */
|
|
551
|
+
},
|
|
552
|
+
);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// A tooltip for the click gesture above, and only for the part of the
|
|
556
|
+
// equation it actually applies to — an explained symbol has its own hover
|
|
557
|
+
// card (created above) and needs no tooltip layered on top of it, and the
|
|
558
|
+
// copy button already carries a static `title`. `mouseover`/`mouseout` (not
|
|
559
|
+
// `mouseenter`/`mouseleave`) because they bubble, which this document-level
|
|
560
|
+
// delegation needs.
|
|
561
|
+
document.addEventListener('mouseover', (event) => {
|
|
562
|
+
const origin = event.target instanceof Element ? event.target : null;
|
|
563
|
+
if (!origin || origin.getAttribute('title') === 'Copy LaTeX') return;
|
|
564
|
+
if (
|
|
565
|
+
origin.closest(
|
|
566
|
+
'[data-notation-key], [data-notation-formula-copy], .notation-formula-number',
|
|
567
|
+
)
|
|
568
|
+
) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (!origin.closest('[data-notation-formula]')) return;
|
|
572
|
+
origin.setAttribute('title', 'Copy LaTeX');
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
document.addEventListener('mouseout', (event) => {
|
|
576
|
+
const origin = event.target instanceof Element ? event.target : null;
|
|
577
|
+
if (origin?.getAttribute('title') === 'Copy LaTeX') {
|
|
578
|
+
origin.removeAttribute('title');
|
|
579
|
+
}
|
|
580
|
+
});
|
|
514
581
|
</script>
|
|
515
582
|
|
|
516
583
|
<style>
|
|
@@ -526,9 +593,13 @@ for (const gloss of glossItems) {
|
|
|
526
593
|
margin: 0;
|
|
527
594
|
}
|
|
528
595
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
596
|
+
/* Positioning context for `.notation-list-anchor`, mirroring
|
|
597
|
+
`Figure.astro`'s `.keyed-figure__titlebar` — the anchor link is a DOM
|
|
598
|
+
sibling of `<details>`, not nested inside `<summary>` (a focusable `<a>`
|
|
599
|
+
inside `<summary>` trips axe's `nested-interactive` check the same way
|
|
600
|
+
it would there). */
|
|
601
|
+
.notation-list-titlebar {
|
|
602
|
+
position: relative;
|
|
532
603
|
}
|
|
533
604
|
|
|
534
605
|
.notation-list {
|
|
@@ -539,8 +610,52 @@ for (const gloss of glossItems) {
|
|
|
539
610
|
|
|
540
611
|
.notation-list > summary {
|
|
541
612
|
padding: 0.8rem 1rem;
|
|
613
|
+
padding-inline-end: 2.2rem;
|
|
542
614
|
cursor: pointer;
|
|
543
615
|
font-weight: 600;
|
|
616
|
+
font-size: var(--sl-text-lg);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.notation-list-anchor {
|
|
620
|
+
position: absolute;
|
|
621
|
+
top: 0.9rem;
|
|
622
|
+
inset-inline-end: 1rem;
|
|
623
|
+
display: inline-flex;
|
|
624
|
+
align-items: center;
|
|
625
|
+
justify-content: center;
|
|
626
|
+
width: 1em;
|
|
627
|
+
height: 1em;
|
|
628
|
+
font-size: var(--sl-text-lg);
|
|
629
|
+
color: var(--sl-color-gray-3);
|
|
630
|
+
text-decoration: none;
|
|
631
|
+
-webkit-user-select: none;
|
|
632
|
+
user-select: none;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.notation-list-anchor svg {
|
|
636
|
+
width: 0.7em;
|
|
637
|
+
height: 0.7em;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.notation-list-anchor:hover,
|
|
641
|
+
.notation-list-anchor:focus-visible {
|
|
642
|
+
color: var(--sl-color-text-accent);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.notation-list-anchor:focus-visible {
|
|
646
|
+
outline: 2px solid var(--sl-color-accent);
|
|
647
|
+
outline-offset: 0.15rem;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
@media (hover: hover) {
|
|
651
|
+
.notation-list-anchor {
|
|
652
|
+
opacity: 0;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.notation-list-anchor:focus-visible,
|
|
656
|
+
.notation-list-titlebar:hover .notation-list-anchor {
|
|
657
|
+
opacity: 1;
|
|
658
|
+
}
|
|
544
659
|
}
|
|
545
660
|
|
|
546
661
|
.notation-count,
|
|
@@ -594,14 +709,28 @@ for (const gloss of glossItems) {
|
|
|
594
709
|
cursor: help;
|
|
595
710
|
}
|
|
596
711
|
|
|
712
|
+
/* Entries in the visible list are keyboard-operable (Enter/Space pins
|
|
713
|
+
the same card a click would) now that the list has no separate "Pin
|
|
714
|
+
explanation" button of its own — see the setup script below. */
|
|
715
|
+
.notation-entry .notation-symbol-trigger {
|
|
716
|
+
cursor: pointer;
|
|
717
|
+
}
|
|
718
|
+
|
|
597
719
|
.notation-symbol-trigger:hover,
|
|
720
|
+
.notation-symbol-trigger:focus-visible,
|
|
598
721
|
.notation-symbol-trigger.is-notation-active {
|
|
599
722
|
background: var(--sl-color-accent-low);
|
|
600
723
|
box-shadow: 0 0 0 0.12em var(--sl-color-accent-low);
|
|
601
724
|
}
|
|
602
725
|
|
|
726
|
+
.notation-symbol-trigger:focus-visible {
|
|
727
|
+
outline: 2px solid var(--sl-color-accent);
|
|
728
|
+
outline-offset: 0.1em;
|
|
729
|
+
}
|
|
730
|
+
|
|
603
731
|
@media (forced-colors: active) {
|
|
604
732
|
.notation-symbol-trigger:hover,
|
|
733
|
+
.notation-symbol-trigger:focus-visible,
|
|
605
734
|
.notation-symbol-trigger.is-notation-active {
|
|
606
735
|
outline: 2px solid Highlight;
|
|
607
736
|
color: HighlightText;
|
|
@@ -624,6 +753,10 @@ for (const gloss of glossItems) {
|
|
|
624
753
|
font-size: var(--sl-text-sm);
|
|
625
754
|
}
|
|
626
755
|
|
|
756
|
+
/* Cursor, hover highlight, and "Copied!" flash for this plain-span
|
|
757
|
+
formula fallback (a definition with no `formulaScope`) come from the
|
|
758
|
+
generic `[data-notation-formula]` rules in `global.css` — shared with
|
|
759
|
+
every other producer of that attribute, not reimplemented here. */
|
|
627
760
|
.notation-formula {
|
|
628
761
|
overflow-x: auto;
|
|
629
762
|
/* `overflow-x: auto` alone makes the block axis compute to `auto` too
|
|
@@ -634,24 +767,6 @@ for (const gloss of glossItems) {
|
|
|
634
767
|
color: var(--sl-color-text);
|
|
635
768
|
}
|
|
636
769
|
|
|
637
|
-
.notation-pin {
|
|
638
|
-
justify-self: start;
|
|
639
|
-
padding: 0.25rem 0.55rem;
|
|
640
|
-
border: 1px solid var(--sl-color-gray-5);
|
|
641
|
-
border-radius: 0.35rem;
|
|
642
|
-
color: var(--sl-color-text-accent);
|
|
643
|
-
background: var(--sl-color-bg);
|
|
644
|
-
font: inherit;
|
|
645
|
-
font-size: var(--sl-text-sm);
|
|
646
|
-
cursor: pointer;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
.notation-pin:hover,
|
|
650
|
-
.notation-pin[aria-pressed='true'] {
|
|
651
|
-
border-color: var(--sl-color-accent);
|
|
652
|
-
background: var(--sl-color-accent-low);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
770
|
.notation-enhancement-help {
|
|
656
771
|
margin: 0.55rem 0 0;
|
|
657
772
|
font-size: var(--sl-text-xs);
|
|
@@ -743,39 +858,6 @@ for (const gloss of glossItems) {
|
|
|
743
858
|
line-height: 1.5;
|
|
744
859
|
}
|
|
745
860
|
|
|
746
|
-
.notation-popup-copy {
|
|
747
|
-
position: relative;
|
|
748
|
-
margin-block: 0.4rem 0.6rem;
|
|
749
|
-
padding: 0.2rem 0.55rem;
|
|
750
|
-
border: 1px solid var(--sl-color-gray-5);
|
|
751
|
-
border-radius: 0.35rem;
|
|
752
|
-
color: var(--sl-color-text-accent);
|
|
753
|
-
background: var(--sl-color-bg);
|
|
754
|
-
font: inherit;
|
|
755
|
-
font-size: var(--sl-text-xs);
|
|
756
|
-
cursor: pointer;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
.notation-popup-copy:hover {
|
|
760
|
-
border-color: var(--sl-color-accent);
|
|
761
|
-
background: var(--sl-color-accent-low);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
.notation-popup-copy[data-copied]::after {
|
|
765
|
-
content: 'Copied!';
|
|
766
|
-
position: absolute;
|
|
767
|
-
inset-block-end: calc(100% + 0.35rem);
|
|
768
|
-
inset-inline-start: 50%;
|
|
769
|
-
translate: -50% 0;
|
|
770
|
-
padding: 0.15rem 0.45rem;
|
|
771
|
-
border-radius: 0.35rem;
|
|
772
|
-
background: var(--sl-color-accent-low);
|
|
773
|
-
color: var(--sl-color-text-accent);
|
|
774
|
-
font-size: var(--sl-text-xs);
|
|
775
|
-
white-space: nowrap;
|
|
776
|
-
pointer-events: none;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
861
|
.notation-panel-body [data-slot-formula] {
|
|
780
862
|
overflow-x: auto;
|
|
781
863
|
/* See `.notation-formula` above: pairs with `overflow-x` to stop the
|