explico 0.6.0 → 0.8.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 +350 -1
- 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 +30 -4
- package/dist/compiler/cli.js +79 -10
- package/dist/compiler/collections.js +16 -3
- package/dist/compiler/exports/standalone-html.js +13 -0
- package/dist/compiler/logging.d.ts +119 -0
- package/dist/compiler/logging.js +108 -0
- package/dist/compiler/manifest.d.ts +7 -3
- package/dist/compiler/manifest.js +1 -1
- 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 +48 -0
- package/dist/reference/delimiter-size.js +153 -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/equations.d.mts +9 -3
- package/dist/reference/equations.mjs +7 -1
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.js +2 -2
- package/dist/reference/gate-heading-length.js +2 -2
- package/dist/reference/gate-math.d.ts +25 -1
- package/dist/reference/gate-math.js +50 -9
- package/dist/reference/gate-prose-notation.d.ts +12 -1
- package/dist/reference/gate-prose-notation.js +5 -1
- package/dist/reference/katex-delimiters.d.mts +15 -0
- package/dist/reference/katex-delimiters.mjs +125 -0
- package/dist/reference/katex-options.mjs +4 -0
- package/dist/reference/keyed-float-validate.d.ts +2 -2
- package/dist/reference/keyed-float-validate.js +1 -1
- package/dist/reference/math-glyphs.mjs +16 -2
- 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.mjs +0 -0
- package/dist/reference/render-lab-math.d.ts +18 -1
- package/dist/reference/render-lab-math.js +11 -4
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/types.d.ts +8 -1
- package/docs/VERSIONING.md +2 -2
- package/docs/authoring/README.md +12 -2
- package/docs/authoring/agents.md +7 -3
- package/docs/authoring/migrating.md +514 -0
- package/docs/authoring/notation.md +157 -7
- package/docs/authoring/prose.md +64 -0
- package/docs/katex-bracket-scaling.md +191 -0
- package/package.json +2 -1
- package/src/compiler/astro.ts +89 -0
- package/src/compiler/cli.ts +167 -12
- package/src/compiler/collections.ts +33 -3
- package/src/compiler/exports/standalone-html.ts +15 -0
- package/src/compiler/logging.ts +259 -0
- package/src/compiler/manifest.ts +7 -3
- 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 +100 -58
- package/src/components/notation/NotationLayer.astro +83 -81
- package/src/components/notation/render-notation-math.ts +111 -10
- package/src/components/scroll-animation.ts +160 -0
- package/src/components/starlight/AboutPanel.astro +17 -2
- 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 +2 -0
- package/src/reference/base-abbreviations.mjs +8 -2
- package/src/reference/consistency.ts +10 -66
- package/src/reference/delimiter-size.ts +279 -0
- package/src/reference/diagrams-validate.ts +1 -1
- package/src/reference/equations-validate.ts +3 -3
- package/src/reference/equations.mjs +7 -1
- package/src/reference/figures-validate.ts +1 -1
- package/src/reference/gate-abbreviations.ts +2 -2
- package/src/reference/gate-heading-length.ts +2 -2
- package/src/reference/gate-math.ts +91 -8
- package/src/reference/gate-prose-notation.ts +15 -1
- package/src/reference/katex-delimiters.mjs +125 -0
- package/src/reference/katex-options.mjs +4 -0
- package/src/reference/keyed-float-validate.ts +3 -3
- package/src/reference/math-glyphs.mjs +16 -2
- 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 +25 -2
- package/src/reference/tables-validate.ts +1 -1
- package/src/reference/types.ts +8 -1
- package/src/styles/global.css +119 -14
|
@@ -116,7 +116,7 @@ if (!anchorId) {
|
|
|
116
116
|
</style>
|
|
117
117
|
|
|
118
118
|
<script>
|
|
119
|
-
|
|
119
|
+
import { animatedScrollIntoView } from '../scroll-animation';
|
|
120
120
|
|
|
121
121
|
document
|
|
122
122
|
.querySelectorAll<HTMLElement>('.short-note:not([data-short-note-wired])')
|
|
@@ -185,10 +185,7 @@ if (!anchorId) {
|
|
|
185
185
|
history.pushState(null, '', `#${note.id}`);
|
|
186
186
|
}
|
|
187
187
|
requestAnimationFrame(() => {
|
|
188
|
-
note
|
|
189
|
-
behavior: reduceMotion.matches ? 'auto' : 'smooth',
|
|
190
|
-
block: 'nearest',
|
|
191
|
-
});
|
|
188
|
+
animatedScrollIntoView(note, { block: 'nearest' });
|
|
192
189
|
});
|
|
193
190
|
};
|
|
194
191
|
|
|
@@ -33,6 +33,10 @@ export interface Props {
|
|
|
33
33
|
glossLabel?: string;
|
|
34
34
|
/** The owning entry's display label, used to name the scrollable region. */
|
|
35
35
|
entryLabel?: string;
|
|
36
|
+
/** `G.<n>` — the glossary's own equation numbering. Omitted renders no number. */
|
|
37
|
+
number?: string;
|
|
38
|
+
/** Same-page anchor the number links to; ignored unless `number` is set. */
|
|
39
|
+
numberHref?: string;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
const {
|
|
@@ -41,6 +45,8 @@ const {
|
|
|
41
45
|
glosses = [],
|
|
42
46
|
glossLabel = 'Symbols',
|
|
43
47
|
entryLabel,
|
|
48
|
+
number,
|
|
49
|
+
numberHref,
|
|
44
50
|
} = Astro.props;
|
|
45
51
|
const { nodes } = renderNotationFormula(formula, scope);
|
|
46
52
|
registerPageNotationDependencies(
|
|
@@ -48,20 +54,49 @@ registerPageNotationDependencies(
|
|
|
48
54
|
notationKeysInMathNodes(nodes),
|
|
49
55
|
`NotationFormula formula "${formula}"`,
|
|
50
56
|
);
|
|
57
|
+
const latex = stripNotationMarkers(formula);
|
|
58
|
+
const numbered = Boolean(number && numberHref);
|
|
51
59
|
---
|
|
52
60
|
|
|
53
61
|
<div class="notation-formula-block">
|
|
54
62
|
<div
|
|
55
|
-
class=
|
|
63
|
+
class:list={['notation-formula-math', numbered && 'notation-formula-math--numbered']}
|
|
56
64
|
data-notation-formula
|
|
57
|
-
data-latex={
|
|
65
|
+
data-latex={latex}
|
|
58
66
|
role="group"
|
|
59
67
|
aria-label={entryLabel ? `${entryLabel} formula` : 'Formula'}
|
|
60
|
-
tabindex="0"
|
|
61
68
|
>
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
<div class="notation-formula-scroll" tabindex="0">
|
|
70
|
+
<span class="notation-math">
|
|
71
|
+
{nodes.map((node) => <SafeMathNode node={node} />)}
|
|
72
|
+
</span>
|
|
73
|
+
</div>
|
|
74
|
+
{numbered && (
|
|
75
|
+
<div class="notation-formula-controls">
|
|
76
|
+
<a
|
|
77
|
+
class="notation-formula-number"
|
|
78
|
+
href={numberHref}
|
|
79
|
+
aria-label={`Equation ${number}`}
|
|
80
|
+
title="Anchor to this equation"
|
|
81
|
+
>
|
|
82
|
+
({number})
|
|
83
|
+
</a>
|
|
84
|
+
<button
|
|
85
|
+
class="notation-formula-copy"
|
|
86
|
+
type="button"
|
|
87
|
+
data-notation-formula-copy
|
|
88
|
+
data-latex={latex}
|
|
89
|
+
aria-label={`Copy LaTeX for Equation ${number}`}
|
|
90
|
+
title="Copy LaTeX"
|
|
91
|
+
>
|
|
92
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
93
|
+
<rect x="9" y="9" width="10" height="10" rx="1"></rect>
|
|
94
|
+
<path d="M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1"
|
|
95
|
+
></path>
|
|
96
|
+
</svg>
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
65
100
|
</div>
|
|
66
101
|
|
|
67
102
|
{
|
|
@@ -95,25 +130,179 @@ registerPageNotationDependencies(
|
|
|
95
130
|
gap: 0.5rem;
|
|
96
131
|
}
|
|
97
132
|
|
|
133
|
+
/* The outer box is deliberately never a scroll clip itself — it hosts the
|
|
134
|
+
absolutely-positioned number/copy controls and their "Copied!" flash,
|
|
135
|
+
which a clipping ancestor would cut off. Mirrors `.keyed-equation` in
|
|
136
|
+
`global.css`, which splits the same way (its own overflow lives on the
|
|
137
|
+
nested `.katex-display`, not on `.keyed-equation` itself). Its cursor,
|
|
138
|
+
hover highlight, and "Copied!" flash are the generic
|
|
139
|
+
`[data-notation-formula]` rules in `global.css` — shared with every
|
|
140
|
+
other producer of that attribute, not reimplemented here. */
|
|
141
|
+
.notation-formula-math {
|
|
142
|
+
color: var(--sl-color-text);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.notation-formula-math--numbered {
|
|
146
|
+
padding-inline-end: 5.25rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
98
149
|
/* A wide equation scrolls inside its own box, which makes the box a
|
|
99
150
|
scrollable region — and a scrollable region a keyboard user cannot reach
|
|
100
151
|
is unreachable content, not just an awkward one. `tabindex` on the
|
|
101
|
-
|
|
102
|
-
|
|
152
|
+
scrolling element itself (not an ancestor) is the fix, same convention
|
|
153
|
+
as `.table-overflow-scroll` in `global.css`; this is its focus ring. */
|
|
154
|
+
.notation-formula-scroll {
|
|
103
155
|
overflow-x: auto;
|
|
104
156
|
/* `overflow-x: auto` alone makes the block axis compute to `auto` too
|
|
105
157
|
(CSS Overflow §3) — equations must never scroll vertically on their
|
|
106
158
|
own, only sideways. */
|
|
107
159
|
overflow-y: hidden;
|
|
108
160
|
padding-block: 0.15rem;
|
|
109
|
-
color: var(--sl-color-text);
|
|
110
161
|
}
|
|
111
162
|
|
|
112
|
-
.notation-formula-
|
|
163
|
+
.notation-formula-scroll:focus-visible {
|
|
113
164
|
outline: 3px solid var(--sl-color-accent);
|
|
114
165
|
outline-offset: 2px;
|
|
115
166
|
}
|
|
116
167
|
|
|
168
|
+
/* Numbered controls — a glossary entry's own equation number and copy
|
|
169
|
+
button, styled to match `.keyed-equation__controls`/`__number`/`__copy`
|
|
170
|
+
in `global.css` exactly (same values), so a reader sees one consistent
|
|
171
|
+
"numbered equation" affordance across lessons and the glossary. */
|
|
172
|
+
.notation-formula-controls {
|
|
173
|
+
position: absolute;
|
|
174
|
+
inset-inline-end: 0.25rem;
|
|
175
|
+
inset-block-start: 50%;
|
|
176
|
+
transform: translateY(-50%);
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: 0.4rem;
|
|
180
|
+
/* Starlight's generic prose "sibling spacing" rule adds a margin-top
|
|
181
|
+
to any block-level element that follows another (here, following
|
|
182
|
+
`.notation-formula-scroll`) — harmless for flow content, but it
|
|
183
|
+
shifts an absolutely-positioned box's `top: 50%` off-center, since
|
|
184
|
+
margin still applies to an out-of-flow element's own offset. */
|
|
185
|
+
margin: 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.notation-formula-number {
|
|
189
|
+
font-variant-numeric: tabular-nums;
|
|
190
|
+
font-size: var(--sl-text-sm);
|
|
191
|
+
color: var(--sl-color-text-accent);
|
|
192
|
+
text-decoration: none;
|
|
193
|
+
/* Without this it would inherit the container's `cursor: copy` above
|
|
194
|
+
instead of the link's usual pointer, since an inherited value beats
|
|
195
|
+
the browser's own "auto → hand cursor for a link" special case. */
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
opacity: 0.45;
|
|
198
|
+
transition: opacity 120ms ease-out;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.notation-formula-copy {
|
|
202
|
+
display: inline-flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
justify-content: center;
|
|
205
|
+
width: 1.5rem;
|
|
206
|
+
height: 1.5rem;
|
|
207
|
+
padding: 0;
|
|
208
|
+
border: 1px solid var(--sl-color-gray-5);
|
|
209
|
+
border-radius: 999px;
|
|
210
|
+
color: var(--sl-color-gray-1);
|
|
211
|
+
background: transparent;
|
|
212
|
+
font: inherit;
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
opacity: 0.45;
|
|
215
|
+
transition:
|
|
216
|
+
opacity 120ms ease-out,
|
|
217
|
+
border-color 120ms ease-out,
|
|
218
|
+
color 120ms ease-out;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.notation-formula-copy svg {
|
|
222
|
+
width: 0.85rem;
|
|
223
|
+
height: 0.85rem;
|
|
224
|
+
fill: none;
|
|
225
|
+
stroke: currentColor;
|
|
226
|
+
stroke-width: 1.75;
|
|
227
|
+
stroke-linecap: round;
|
|
228
|
+
stroke-linejoin: round;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@media (hover: none) {
|
|
232
|
+
.notation-formula-number,
|
|
233
|
+
.notation-formula-copy {
|
|
234
|
+
opacity: 1;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Hovering (or focusing) anywhere in the equation reveals the number and
|
|
239
|
+
copy button together — the "highlight" that signals the whole box is
|
|
240
|
+
interactive, not just its own small controls corner. */
|
|
241
|
+
.notation-formula-math:hover .notation-formula-number,
|
|
242
|
+
.notation-formula-math:focus-within .notation-formula-number,
|
|
243
|
+
.notation-formula-number:focus-visible {
|
|
244
|
+
opacity: 1;
|
|
245
|
+
text-decoration: underline;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.notation-formula-math:hover .notation-formula-copy,
|
|
249
|
+
.notation-formula-math:focus-within .notation-formula-copy,
|
|
250
|
+
.notation-formula-copy:focus-visible {
|
|
251
|
+
opacity: 1;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.notation-formula-copy:hover,
|
|
255
|
+
.notation-formula-copy:focus-visible {
|
|
256
|
+
border-color: var(--sl-color-accent);
|
|
257
|
+
color: var(--sl-color-text-accent);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.notation-formula-copy:focus-visible {
|
|
261
|
+
outline: 2px solid var(--sl-color-accent);
|
|
262
|
+
outline-offset: 2px;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* `data-copied` is set briefly by the click handler in
|
|
266
|
+
`NotationLayer.astro` — same "flash a tooltip, then remove the
|
|
267
|
+
attribute" pattern as `.keyed-equation__copy[data-copied]`. */
|
|
268
|
+
.notation-formula-copy[data-copied] {
|
|
269
|
+
opacity: 1;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.notation-formula-copy[data-copied]::after {
|
|
273
|
+
content: 'Copied!';
|
|
274
|
+
position: absolute;
|
|
275
|
+
inset-block-end: calc(100% + 0.4rem);
|
|
276
|
+
inset-inline-end: 0;
|
|
277
|
+
padding: 0.15rem 0.45rem;
|
|
278
|
+
border-radius: 0.35rem;
|
|
279
|
+
background: var(--sl-color-accent-low);
|
|
280
|
+
color: var(--sl-color-text-accent);
|
|
281
|
+
font-size: var(--sl-text-xs);
|
|
282
|
+
white-space: nowrap;
|
|
283
|
+
pointer-events: none;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* The non-numbered case (a formula inside a hover popup, the "Notation
|
|
287
|
+
used on this page" list, a `Diagram`, or a lab's interactive equation)
|
|
288
|
+
has no controls to flash against, so the box itself flashes instead —
|
|
289
|
+
the generic `[data-notation-formula][data-copied]::after` rule in
|
|
290
|
+
`global.css`, not reimplemented here. */
|
|
291
|
+
|
|
292
|
+
@media (prefers-reduced-motion: reduce) {
|
|
293
|
+
.notation-formula-number,
|
|
294
|
+
.notation-formula-copy {
|
|
295
|
+
transition: none;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@media (forced-colors: active) {
|
|
300
|
+
.notation-formula-number,
|
|
301
|
+
.notation-formula-copy {
|
|
302
|
+
opacity: 1;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
117
306
|
.notation-gloss-list {
|
|
118
307
|
display: flex;
|
|
119
308
|
flex-wrap: wrap;
|
|
@@ -19,6 +19,15 @@ const [notationEntries, lessonEntries, manifest] = await Promise.all([
|
|
|
19
19
|
getCollection('docs', ({ id }) => isLessonSlug(id)),
|
|
20
20
|
loadManifest(),
|
|
21
21
|
]);
|
|
22
|
+
// Starlight's `link-alt` icon — the same glyph its own heading anchors use
|
|
23
|
+
// (`rehype-heading-anchors.mjs`). Reimplemented here rather than reused via
|
|
24
|
+
// `.sl-anchor-link`/`.sl-heading-wrapper` because that CSS assumes a single
|
|
25
|
+
// inline prose heading, not this entry's three-column grid header (symbol,
|
|
26
|
+
// name, editorial status) — the glyph carries over, the layout doesn't, so
|
|
27
|
+
// this file supplies its own reveal styling (below).
|
|
28
|
+
const HEADING_LINK_ICON_PATH =
|
|
29
|
+
'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';
|
|
30
|
+
|
|
22
31
|
const labelOf = (data: { key: string; label?: string }) =>
|
|
23
32
|
resolveLabel(data.key, data.label);
|
|
24
33
|
const notationLabelByKey = new Map(
|
|
@@ -83,6 +92,18 @@ const renderedEntries = await Promise.all(
|
|
|
83
92
|
})),
|
|
84
93
|
);
|
|
85
94
|
|
|
95
|
+
// Every entry with a rigorous `formula` gets its own equation number, `G.<n>`
|
|
96
|
+
// in page order — the glossary's own numbering, distinct from a lesson's
|
|
97
|
+
// chapter-qualified `(2.1)` (`equations-validate.ts`), since a glossary
|
|
98
|
+
// formula has no lesson/part to qualify it against.
|
|
99
|
+
let equationCounter = 0;
|
|
100
|
+
const equationNumberByKey = new Map<string, string>();
|
|
101
|
+
for (const { entry } of renderedEntries) {
|
|
102
|
+
if (!entry.data.formula) continue;
|
|
103
|
+
equationCounter += 1;
|
|
104
|
+
equationNumberByKey.set(entry.data.key, `G.${equationCounter}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
86
107
|
// Drives the hover explanation for `[[key]]` links inside the definition
|
|
87
108
|
// bodies. Every entry is already shown in full on this page, so the layer
|
|
88
109
|
// renders in panel-only mode: no visible list, just the shared panel.
|
|
@@ -138,17 +159,32 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
138
159
|
<NotationMath class="glossary-symbol" latex={entry.data.latex} />
|
|
139
160
|
</span>
|
|
140
161
|
<div>
|
|
141
|
-
|
|
162
|
+
{/* The anchor is a sibling of the heading, not a child — the
|
|
163
|
+
same shape `rehype-heading-anchors` gives a lesson heading.
|
|
164
|
+
Inside the `h2` its `aria-label` joins the heading's
|
|
165
|
+
accessible name ("Velocity Link to “Velocity”"). */}
|
|
166
|
+
<div class="glossary-heading">
|
|
167
|
+
<h2>
|
|
168
|
+
<a
|
|
169
|
+
class="glossary-term-link"
|
|
170
|
+
href={`#notation-${entry.data.key}`}
|
|
171
|
+
>
|
|
172
|
+
{labelOf(entry.data)}
|
|
173
|
+
</a>
|
|
174
|
+
</h2>
|
|
175
|
+
<a
|
|
176
|
+
class="glossary-anchor"
|
|
177
|
+
href={`#notation-${entry.data.key}`}
|
|
178
|
+
aria-label={`Link to “${labelOf(entry.data)}”`}
|
|
179
|
+
>
|
|
180
|
+
<span class="glossary-anchor-icon" aria-hidden="true">
|
|
181
|
+
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
182
|
+
<path d={HEADING_LINK_ICON_PATH}></path>
|
|
183
|
+
</svg>
|
|
184
|
+
</span>
|
|
185
|
+
</a>
|
|
186
|
+
</div>
|
|
142
187
|
<code>{entry.data.key}</code>
|
|
143
|
-
<a
|
|
144
|
-
class="glossary-anchor"
|
|
145
|
-
href={`#notation-${entry.data.key}`}
|
|
146
|
-
data-glossary-anchor
|
|
147
|
-
aria-label={`Copy link to “${labelOf(entry.data)}”`}
|
|
148
|
-
title="Copy link to this entry"
|
|
149
|
-
>
|
|
150
|
-
<span aria-hidden="true">#</span>
|
|
151
|
-
</a>
|
|
152
188
|
</div>
|
|
153
189
|
<span class="glossary-status">{entry.data.editorialStatus}</span>
|
|
154
190
|
</header>
|
|
@@ -193,6 +229,8 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
193
229
|
scope={formulaScopeByKey.get(entry.data.key) ?? []}
|
|
194
230
|
glosses={glossesByKey.get(entry.data.key)}
|
|
195
231
|
entryLabel={labelOf(entry.data)}
|
|
232
|
+
number={equationNumberByKey.get(entry.data.key)}
|
|
233
|
+
numberHref={`#notation-${entry.data.key}`}
|
|
196
234
|
/>
|
|
197
235
|
</div>
|
|
198
236
|
)}
|
|
@@ -264,34 +302,6 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
264
302
|
definitions={panelDefinitions}
|
|
265
303
|
/>
|
|
266
304
|
|
|
267
|
-
<script>
|
|
268
|
-
const RESET_MS = 1200;
|
|
269
|
-
|
|
270
|
-
for (const anchor of document.querySelectorAll<HTMLAnchorElement>(
|
|
271
|
-
'[data-glossary-anchor]',
|
|
272
|
-
)) {
|
|
273
|
-
anchor.addEventListener('click', () => {
|
|
274
|
-
const href = anchor.getAttribute('href');
|
|
275
|
-
if (!href || !navigator.clipboard) return;
|
|
276
|
-
|
|
277
|
-
// Keep the native hash navigation (scroll + history) and copy the
|
|
278
|
-
// absolute URL alongside it.
|
|
279
|
-
const url = new URL(href, window.location.href).href;
|
|
280
|
-
navigator.clipboard.writeText(url).then(
|
|
281
|
-
() => {
|
|
282
|
-
anchor.dataset.copied = 'true';
|
|
283
|
-
window.setTimeout(() => {
|
|
284
|
-
delete anchor.dataset.copied;
|
|
285
|
-
}, RESET_MS);
|
|
286
|
-
},
|
|
287
|
-
() => {
|
|
288
|
-
/* Clipboard blocked: the plain anchor link still works. */
|
|
289
|
-
},
|
|
290
|
-
);
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
</script>
|
|
294
|
-
|
|
295
305
|
<style>
|
|
296
306
|
.glossary-index ul {
|
|
297
307
|
display: grid;
|
|
@@ -326,6 +336,22 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
326
336
|
font-size: var(--sl-text-xl);
|
|
327
337
|
}
|
|
328
338
|
|
|
339
|
+
.glossary-heading {
|
|
340
|
+
display: flex;
|
|
341
|
+
align-items: center;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.glossary-term-link {
|
|
345
|
+
color: inherit;
|
|
346
|
+
text-decoration: none;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.glossary-term-link:hover,
|
|
350
|
+
.glossary-term-link:focus-visible {
|
|
351
|
+
color: var(--sl-color-text-accent);
|
|
352
|
+
text-decoration: underline;
|
|
353
|
+
}
|
|
354
|
+
|
|
329
355
|
.glossary-symbol {
|
|
330
356
|
color: var(--sl-color-accent-high);
|
|
331
357
|
font-size: 1.2rem;
|
|
@@ -353,14 +379,22 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
353
379
|
}
|
|
354
380
|
}
|
|
355
381
|
|
|
382
|
+
/* Mirrors the hover/focus-reveal treatment of a content heading's own
|
|
383
|
+
`.sl-anchor-link` (`rehype-heading-anchors.mjs`), reimplemented here
|
|
384
|
+
rather than reused because that class's CSS assumes a single inline
|
|
385
|
+
prose heading, not this entry's grid-based header. */
|
|
356
386
|
.glossary-anchor {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
387
|
+
display: inline-flex;
|
|
388
|
+
align-items: center;
|
|
389
|
+
margin-inline-start: 0.4rem;
|
|
360
390
|
color: var(--sl-color-gray-3);
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
391
|
+
vertical-align: middle;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.glossary-anchor-icon svg {
|
|
395
|
+
width: 0.85em;
|
|
396
|
+
height: 0.85em;
|
|
397
|
+
display: block;
|
|
364
398
|
}
|
|
365
399
|
|
|
366
400
|
.glossary-anchor:hover,
|
|
@@ -368,20 +402,28 @@ const panelDefinitions: NotationLayerDefinition[] = notationEntries.map(
|
|
|
368
402
|
color: var(--sl-color-text-accent);
|
|
369
403
|
}
|
|
370
404
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
405
|
+
@media (hover: hover) {
|
|
406
|
+
.glossary-anchor {
|
|
407
|
+
opacity: 0;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.glossary-heading:hover .glossary-anchor,
|
|
411
|
+
.glossary-heading:focus-within .glossary-anchor,
|
|
412
|
+
.glossary-anchor:focus-visible {
|
|
413
|
+
opacity: 1;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
418
|
+
.glossary-anchor {
|
|
419
|
+
transition: opacity 120ms ease-out, color 120ms ease-out;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
@media (forced-colors: active) {
|
|
424
|
+
.glossary-anchor {
|
|
425
|
+
opacity: 1;
|
|
426
|
+
}
|
|
385
427
|
}
|
|
386
428
|
|
|
387
429
|
.glossary-status {
|