@pixerate/flair 0.13.1 → 0.15.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 +22 -0
- package/README.md +32 -1
- package/dist/catalog/index.d.ts +3 -3
- package/dist/catalog/index.js +1 -1
- package/dist/{chunk-UJDQJK2O.js → chunk-4RGHZWQC.js} +1 -1
- package/dist/{chunk-UJDQJK2O.js.map → chunk-4RGHZWQC.js.map} +1 -1
- package/dist/chunk-DVVQRZJ5.js +137 -0
- package/dist/chunk-DVVQRZJ5.js.map +1 -0
- package/dist/{chunk-L4NFQM5N.js → chunk-KWNFZ5NC.js} +10 -4
- package/dist/{chunk-L4NFQM5N.js.map → chunk-KWNFZ5NC.js.map} +1 -1
- package/dist/{chunk-KVKJCD6A.js → chunk-SHHAIWUR.js} +6 -6
- package/dist/{chunk-H355R6U4.js → chunk-VP6IEWIK.js} +1 -1
- package/dist/cli.js +4 -4
- package/dist/editor/index.d.ts +2 -2
- package/dist/editor/index.js +5 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +24 -16
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +82 -0
- package/dist/react/index.js +132 -0
- package/dist/react/index.js.map +1 -0
- package/dist/{svg-DNEreR2p.d.ts → svg-XugR-6Ec.d.ts} +5 -2
- package/dist/{types-B23zI_jp.d.ts → types-9J6NWaN3.d.ts} +2 -2
- package/dist/vector/index.d.ts +35 -3
- package/dist/vector/index.js +10 -2
- package/package.json +16 -3
- /package/dist/{chunk-KVKJCD6A.js.map → chunk-SHHAIWUR.js.map} +0 -0
- /package/dist/{chunk-H355R6U4.js.map → chunk-VP6IEWIK.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @pixerate/flair
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 61b2ed6: New `@pixerate/flair/react` entry point, with `react` (>= 18) as an optional peer dependency:
|
|
8
|
+
|
|
9
|
+
- `<VectorEffect id="drip-text" values={{ word: "Slop" }} loop={false} />` plays a catalog vector effect; `<SvgPlayerView motion={...} />` plays any `SvgMotion`. Each makes an `SvgPlayer` after mount and disposes it on unmount.
|
|
10
|
+
- Server-renders a still of the last frame, sized from the viewBox, so hydration neither shifts the layout nor flashes blank. The still stays under `prefers-reduced-motion` (`reducedMotion` overrides).
|
|
11
|
+
- `inline` (or an effect's `inline` control, like drip text's) sizes it as a word in a line: `inline-block`, 0.72em high, with the viewBox's aspect ratio.
|
|
12
|
+
|
|
13
|
+
`@pixerate/flair/vector` adds `svgFrameMarkup(motion, { at })`, a frame as safe `<svg>` markup with no DOM (for server rendering and reduced motion), and `svgViewBox` / `parseViewBox`.
|
|
14
|
+
|
|
15
|
+
## 0.14.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 3ee7554: Drip text sits inside headings and takes your font:
|
|
20
|
+
|
|
21
|
+
- `inline: true` frames just the word, from cap height to baseline, with `overflow="visible"`, so the outline and drips spill over the text around it without adding leading. Give its container `display: inline-block; height: 0.72em` and the viewBox's aspect ratio.
|
|
22
|
+
- `font` (default `Arial Black, Impact, system-ui, sans-serif`; `inherit` takes the surrounding text's) and `weight` (100–900, default 900) controls replace the hard-coded font.
|
|
23
|
+
- Documented that `SvgPlayer` with `loop: false` plays once and stays on its last frame.
|
|
24
|
+
|
|
3
25
|
## 0.13.1
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -43,7 +43,8 @@ Flair gives products a library of good-looking, configurable visual effects (her
|
|
|
43
43
|
| :--- | :--- | :--- |
|
|
44
44
|
| `@pixerate/flair` | Shader engine, vector motion, time and easing, colour, blending, transitions, scenes, interchange exporters | None (browser-safe) |
|
|
45
45
|
| `@pixerate/flair/catalog` | `EFFECTS`, `getEffect`, `findEffects`, `effectDefaults`, `renderVectorEffect`, `vectorControls`, GLSL helpers, sample images, `textImage` | None |
|
|
46
|
-
| `@pixerate/flair/vector` | `SvgMotion`, `svgFrame`, `svgView`, `SvgPlayer`, `animateCount`, `formatCount`, `morphPaths`, `morphSequence`, `svgMotionIssues` | None |
|
|
46
|
+
| `@pixerate/flair/vector` | `SvgMotion`, `svgFrame`, `svgView`, `SvgPlayer`, `svgFrameMarkup`, `svgViewBox`, `animateCount`, `formatCount`, `morphPaths`, `morphSequence`, `svgMotionIssues` | None |
|
|
47
|
+
| `@pixerate/flair/react` | `<VectorEffect>`, `<SvgPlayerView>`: vector effects as React components, server-rendered as a still | `react` ≥ 18 (optional peer) |
|
|
47
48
|
| `@pixerate/flair/editor` | `EffectEditor`, effect documents, keyframe tracks, renderer bindings, parameter-panel binding, agent tools and instructions | `@pixerate/editor` ≥ 0.29 (optional peer; only its dependency-free `/history`, `/commands`, `/tools`, `/animation` and `/parameters` subpaths) |
|
|
48
49
|
| `@pixerate/flair/node` | `exportMotionGraphic`: renders a shader to GIF/MP4 through headless Chrome and ffmpeg | Node.js |
|
|
49
50
|
|
|
@@ -130,6 +131,17 @@ animateCount({
|
|
|
130
131
|
});
|
|
131
132
|
```
|
|
132
133
|
|
|
134
|
+
With `loop: false` a player plays once and stays on its last frame (like CSS `animation-fill-mode: forwards`); add `hideWhenPaused` to hide it at the end instead.
|
|
135
|
+
|
|
136
|
+
Drip text can sit inside a heading. Set `inline: true` and the picture frames just the word, from cap height to baseline, while its outline and drips spill over the text around it. Give its container `display: inline-block`, `height: 0.72em` and the viewBox's aspect ratio. `font` and `weight` set its typeface, and `font: "inherit"` takes the heading's:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
const motion = renderVectorEffect(getEffect("drip-text") as VectorEffect, { word: "Slop", inline: true, font: "inherit" });
|
|
140
|
+
const [, , w, h] = motion.svg.match(/viewBox="([^"]+)"/)![1]!.split(" ").map(Number);
|
|
141
|
+
Object.assign(host.style, { display: "inline-block", height: "0.72em", aspectRatio: `${w} / ${h}` });
|
|
142
|
+
new SvgPlayer(host, motion, { loop: false });
|
|
143
|
+
```
|
|
144
|
+
|
|
133
145
|
Find effects with `findEffects({ text: "glow", category: "light", kind: "shader" })`. Each effect has an `id`, `name`, `description`, `category`, `tags` and optional `presets`. Image effects default to built-in sample pictures, so they look right before you set `uImage` to your own URL.
|
|
134
146
|
|
|
135
147
|
Effects that show a picture (image, transition, morph, time-warp and the like) take transparent pictures too, such as SVG icons (Lucide and similar sets draw black `currentColor` strokes on a clear background). They lay the picture over a backdrop: `uBackdrop` (white by default) shows through its clear parts. Set `uBackdropAlpha` to 0 to keep the picture's alpha instead, and render with `{ alpha: true }` to lay the result over the page. `SAMPLE_IMAGES.icon` is a Lucide-style sample icon. SVGs are rasterised at 2048 px on the long edge, so icons stay sharp.
|
|
@@ -138,6 +150,25 @@ Overlay shaders (`category: "overlay"`) have `transparent: true`: they write cov
|
|
|
138
150
|
|
|
139
151
|
To morph between any two SVG shapes, `morphPaths(from, to)` (from `@pixerate/flair/vector`) returns two paths with one structure, so `attrs: { d: [a, b] }` blends them point by point: curves and arcs are read, pieces are paired by position and size, and a piece with no partner grows from or shrinks to a point (draw them with `fill-rule="evenodd"`). `morphSequence` does the same for three or more shapes. `feature-morph` morphs two photos from five matching points, and `silhouette-morph` morphs any two silhouettes or logos (or words, through `textImage`).
|
|
140
152
|
|
|
153
|
+
### React
|
|
154
|
+
|
|
155
|
+
`@pixerate/flair/react` wraps the player, so a page needs no ref, effect or dispose of its own. `<VectorEffect>` plays a catalog vector effect by id, `<SvgPlayerView>` any `SvgMotion`:
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { VectorEffect } from "@pixerate/flair/react";
|
|
159
|
+
|
|
160
|
+
<h1>
|
|
161
|
+
Stop the <VectorEffect id="drip-text" values={{ word: "Slop", inline: true, font: "inherit" }} loop={false} />
|
|
162
|
+
</h1>;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- **Server rendering**: both render a still of the last frame as markup (`"first"` for hover and press triggers; set `poster` for another), sized from the viewBox, so the page hydrates with the picture in place. The `SvgPlayer` takes over after mount and is disposed on unmount.
|
|
166
|
+
- **Reduced motion**: under `prefers-reduced-motion: reduce` the still stays. `reducedMotion="always"` or `"never"` overrides it.
|
|
167
|
+
- **Size**: a block as wide as its container, with the viewBox's aspect ratio. With `inline` (or an effect whose `inline` control is on, like drip text's) it is an `inline-block` 0.72em high, sitting on the baseline like a capital letter; pass a CSS length to `inline` for another height.
|
|
168
|
+
- **Props**: player options (`loop`, `autoplay`, `speed`, `hold`, `trigger`, `steps`, `pauseWhenHidden`, `playOnEnter`, `clock` …), `values` and `preset` for an effect, `onPlayer` for the player itself, and any `<span>` attribute (`elementId` sets the host's `id` on `<VectorEffect>`). Values are compared by content, so an inline object does not restart it; pass `<SvgPlayerView>` a memoised `motion`.
|
|
169
|
+
|
|
170
|
+
Without React, `svgFrameMarkup(motion, { at: "last" })` (from `@pixerate/flair/vector`) gives the same still as safe `<svg>` markup with no DOM, for any server or framework.
|
|
171
|
+
|
|
141
172
|
`shader-text` fills letters from a text image: set its `uText` control to `textImage("Your title", { weight: 900 })`, a transparent SVG `data:` URL of any Unicode text.
|
|
142
173
|
|
|
143
174
|
A vector effect declares its controls in its picture, the way a shader declares its uniforms: the first `{{name: type …}}` declares a control (`float`, `int`, `bool`, `color` or `text`, with `@control`'s attributes; quote values with spaces in single quotes), and every `{{name}}` is filled with its value. Declarations can sit in an XML comment, which is dropped when the picture is filled in, and text is escaped, so any words are safe:
|
package/dist/catalog/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as ControlDef, C as ControlValue } from '../controls-Syr6drzO.js';
|
|
2
|
-
import { E as EffectDefinition, a as EffectCategory, S as ShaderEffect } from '../types-
|
|
3
|
-
export { b as EFFECT_CATEGORIES, c as EffectPreset,
|
|
4
|
-
import '../svg-
|
|
2
|
+
import { E as EffectDefinition, a as EffectCategory, S as ShaderEffect } from '../types-9J6NWaN3.js';
|
|
3
|
+
export { b as EFFECT_CATEGORIES, c as EffectPreset, d as VECTOR_CONTROL_TYPES, e as VectorControl, V as VectorEffect, f as colorControl, n as numberControl, r as renderVectorEffect, v as vectorControlDef, g as vectorControlIssues, h as vectorControls, i as vectorEffectControls } from '../types-9J6NWaN3.js';
|
|
4
|
+
import '../svg-XugR-6Ec.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Sample pictures for the catalog's image effects: small SVGs as `data:` URLs,
|
package/dist/catalog/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vector/motion.ts","../src/vector/svg.ts"],"sourcesContent":["/**\n * Vector motion: animated SVG pictures described as data. A picture changes in\n * steps (step 0 when it starts, step n at `steps[n - 1]` ms), and everything\n * here is a pure function of its arguments, so a frame comes out the same\n * however it is reached (played, stepped for a recording, or seeked).\n */\n\n/** Easing curves by name. */\nexport const EASES = {\n linear: (t: number) => t,\n \"in-out\": (t: number) => (t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2),\n out: (t: number) => 1 - (1 - t) ** 3,\n} as const;\n\nexport type EaseName = keyof typeof EASES;\n\n/** How long a change takes by default, in ms. */\nexport const STEP_MS = 800;\n\nconst clamp01 = (t: number) => (t < 0 ? 0 : t > 1 ? 1 : t);\n\n/**\n * When `step` starts, in ms since the media started (step 0 at 0, step n at\n * `steps[n - 1]`), or undefined if it has not started yet.\n */\nexport function stepStart(steps: readonly number[], step: number): number | undefined {\n if (step <= 0) return 0;\n return steps[step - 1];\n}\n\n/**\n * How far a change that plays on `step` (after `delay` ms, over `ms`) has\n * got at `timeMs`, eased: 0 before it starts, 1 once it is over.\n */\nexport function stepProgress(\n steps: readonly number[],\n timeMs: number,\n step: number,\n options: { delay?: number; ms?: number; ease?: EaseName } = {},\n): number {\n const start = stepStart(steps, step);\n if (start === undefined) return 0;\n const from = start + (options.delay ?? 0);\n const ms = options.ms ?? STEP_MS;\n const t = ms > 0 ? clamp01((timeMs - from) / ms) : timeMs >= from ? 1 : 0;\n return EASES[options.ease ?? \"in-out\"](t);\n}\n\n/** The key an item in a list of data keeps from one state to the next. */\nexport type DataKey = (item: unknown, index: number) => string;\n\n/**\n * Keys items by their `id`, `key`, `name` or `label` (or a link's `source`\n * and `target`), else by position.\n */\nexport const defaultDataKey: DataKey = (item, index) => {\n if (item && typeof item === \"object\") {\n const o = item as Record<string, unknown>;\n if (o.source !== undefined && o.target !== undefined)\n return `${String(o.source)}→${String(o.target)}`;\n for (const k of [\"id\", \"key\", \"name\", \"label\"])\n if (typeof o[k] === \"string\" || typeof o[k] === \"number\") return String(o[k]);\n }\n return String(index);\n};\n\n/** Every number in `value` times `k`, so an item can grow from or shrink to nothing. */\nfunction scaled(value: unknown, k: number): unknown {\n if (typeof value === \"number\") return value * k;\n if (Array.isArray(value)) return value.map((v) => scaled(v, k));\n if (value && typeof value === \"object\")\n return Object.fromEntries(Object.entries(value).map(([key, v]) => [key, scaled(v, k)]));\n return value;\n}\n\n/**\n * Data between two states, `u` of the way from `a` to `b`. Numbers blend;\n * lists match their items by key (`keyOf`), so each keeps its identity and\n * moves rather than jumping, with items only in `b` growing from nothing and\n * items only in `a` shrinking away; objects blend field by field; anything\n * else changes halfway.\n */\nexport function lerpData<D>(a: D, b: D, u: number, keyOf: DataKey = defaultDataKey): D {\n if (u <= 0) return a;\n if (u >= 1) return b;\n return blend(a, b, u, keyOf) as D;\n}\n\nfunction blend(a: unknown, b: unknown, u: number, keyOf: DataKey): unknown {\n if (typeof a === \"number\" && typeof b === \"number\") return a + (b - a) * u;\n if (Array.isArray(a) && Array.isArray(b)) {\n const before = new Map(a.map((item, i) => [keyOf(item, i), item]));\n const after = new Set(b.map((item, i) => keyOf(item, i)));\n const out: unknown[] = b.map((item, i) => {\n const key = keyOf(item, i);\n return before.has(key) ? blend(before.get(key), item, u, keyOf) : scaled(item, u);\n });\n a.forEach((item, i) => {\n if (!after.has(keyOf(item, i))) out.push(scaled(item, 1 - u));\n });\n return out;\n }\n if (a && b && typeof a === \"object\" && typeof b === \"object\") {\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n return Object.fromEntries(\n [...new Set([...Object.keys(ao), ...Object.keys(bo)])].map((k) => [\n k,\n k in ao && k in bo\n ? blend(ao[k], bo[k], u, keyOf)\n : k in bo\n ? scaled(bo[k], u)\n : scaled(ao[k], 1 - u),\n ]),\n );\n }\n return u < 0.5 ? a : b;\n}\n\n/**\n * The data to show at `timeMs`, for media whose data has a state per step:\n * `states[n]` is shown from step n (the last one stays), reached by moving\n * from the state before it over `ms`.\n */\nexport function stepData<D>(\n states: readonly D[],\n steps: readonly number[],\n timeMs: number,\n options: { ms?: number; ease?: EaseName; keyOf?: DataKey } = {},\n): D {\n const last = Math.min(steps.length, states.length - 1);\n const to = states[last]!;\n if (last === 0) return to;\n const u = stepProgress(steps, timeMs, last, options);\n return lerpData(states[last - 1]!, to, u, options.keyOf);\n}\n\n// ── SVG media ──────────────────────────────────────────────────────────\n\n/**\n * An animated SVG picture: markup plus changes to its elements. Draw it with\n * `SvgPlayer` (or `svgView`), or compute frames yourself with `svgFrame`.\n */\nexport interface SvgMotion {\n /** One self-contained `<svg>` with a `viewBox`: no scripts, no outside links. */\n svg: string;\n /** Changes to its elements, each on a step. */\n animate?: SvgAnimation[];\n}\n\n/** A box in an SVG's `viewBox` units: `[x, y, width, height]`. */\nexport type SvgBox = readonly [number, number, number, number];\n\n/** A change to one element of an {@link SvgMotion}, or (with `view`) to the whole picture. */\nexport interface SvgAnimation {\n /** The element's `id` in the SVG (not needed for `view`). */\n id?: string;\n /** The step it plays on: 0 (default) when the media starts, 1 with the next shot, and so on. */\n step?: number;\n /** Ms after the step starts. */\n delay?: number;\n /** How long it takes, in ms (default {@link STEP_MS}). */\n ms?: number;\n ease?: EaseName;\n /**\n * Attributes from one value to another: numbers, colours (`#rrggbb`), or\n * strings whose numbers change while the rest stays the same (a path's\n * `d`, a `transform`, `points`).\n */\n attrs?: Record<string, readonly [string | number, string | number]>;\n /** Draws a line or outline on along its length, from 0 to 1 (or between two fractions). */\n draw?: boolean | readonly [number, number];\n /**\n * Zooms and pans the whole picture to frame a box (`[x, y, width, height]`\n * in the `viewBox`'s units) or an element by id (with a margin round it),\n * from where the last `view` left it (at first, the whole `viewBox`).\n */\n view?: SvgBox | string;\n /**\n * Moves the element along a path (by id) from its start to its end, or\n * between the fractions in `at`; draw the element round (0, 0), like a\n * circle at `cx=\"0\" cy=\"0\"`.\n */\n along?: string;\n /** How far along the path `along` moves the element, from and to (default [0, 1]). */\n at?: readonly [number, number];\n /**\n * Sets the active child index in a solo container (data-solo group or element with multiple children).\n * Hides all siblings except the one at this index (0-based).\n */\n solo?: number;\n /** Counts the element's text from one number to the other. */\n count?: readonly [number, number];\n /** How a count is written: `{n}` stands for the number (\"{n}%\", \"$ {n}M\"). */\n format?: string;\n /** Decimals in a count (default 0). */\n decimals?: number;\n /**\n * Writes this text into the element letter by letter: none of it when the\n * change starts, all of it when it ends (a typewriter).\n */\n write?: string;\n /**\n * With `write`: letters not yet written show characters picked from this\n * string, changing every {@link SCRAMBLE_MS} ms, so the text decodes\n * rather than types. The same time always picks the same characters.\n */\n scramble?: string;\n /** With `write`: shown after the written letters while it writes (\"▌\", \"|\"). */\n caret?: string;\n}\n\n/** How often scrambled letters change, in ms (see `SvgAnimation.scramble`). */\nexport const SCRAMBLE_MS = 50;\n\n/**\n * `text` with `u` (0–1) of its letters written (see `SvgAnimation.write`):\n * the rest scrambled from `scramble` (picked by `timeMs`) or left off, then\n * the caret while it is unfinished. Letters are code points, so emoji and\n * accents are never split.\n */\nexport function writtenText(\n text: string,\n u: number,\n options: { scramble?: string; caret?: string; timeMs?: number } = {},\n): string {\n const letters = Array.from(text);\n const shown = Math.round(clamp01(u) * letters.length);\n let out = letters.slice(0, shown).join(\"\");\n const pool = Array.from(options.scramble ?? \"\");\n if (pool.length) {\n const tick = Math.floor((options.timeMs ?? 0) / SCRAMBLE_MS);\n for (let i = shown; i < letters.length; i++)\n out += /\\s/.test(letters[i]!) ? letters[i] : pool[Math.floor(noise(i, tick) * pool.length)];\n }\n return shown < letters.length && options.caret ? out + options.caret : out;\n}\n\n/** A repeatable 0–1 number for a letter at a tick. */\nfunction noise(i: number, tick: number): number {\n const x = Math.sin(i * 127.1 + tick * 311.7) * 43758.5453;\n return x - Math.floor(x);\n}\n\n/** What one element of an animated SVG shows at a time. */\nexport interface SvgElementFrame {\n attrs: Record<string, string>;\n /** How much of its length is drawn, 0–1. */\n draw?: number;\n text?: string;\n /** Active child index when targeting a solo container. */\n solo?: number;\n /** Where it is along a path: the path's id and the fraction of its length. */\n along?: { path: string; at: number };\n /**\n * The whole picture's framing (on the frame for {@link SVG_PICTURE}): `u`\n * of the way from one view to the next, `null` being the whole `viewBox`.\n */\n view?: { from: SvgBox | string | null; to: SvgBox | string; u: number };\n}\n\n/** The key of the whole picture's frame in {@link svgFrame}'s result (for `view`). */\nexport const SVG_PICTURE = \"\";\n\nconst NUMBER = /-?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[-+]?\\d+)?/gi;\nconst HEX = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;\n\nfunction rgb(hex: string): [number, number, number] {\n let h = hex.slice(1);\n if (h.length === 3) h = [...h].map((c) => c + c).join(\"\");\n return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16)) as [number, number, number];\n}\n\nconst round = (n: number) => Math.round(n * 1000) / 1000;\n\n/** A value `u` of the way between two attribute values (see `SvgAnimation.attrs`). */\nexport function lerpAttr(a: string | number, b: string | number, u: number): string {\n if (typeof a === \"number\" && typeof b === \"number\") return String(round(a + (b - a) * u));\n const sa = String(a);\n const sb = String(b);\n if (HEX.test(sa) && HEX.test(sb)) {\n const [ca, cb] = [rgb(sa), rgb(sb)];\n return `#${ca\n .map((c, i) =>\n Math.round(c + (cb[i]! - c) * u)\n .toString(16)\n .padStart(2, \"0\"),\n )\n .join(\"\")}`;\n }\n const na = sa.match(NUMBER) ?? [];\n const nb = sb.match(NUMBER) ?? [];\n if (na.length && na.length === nb.length && sa.replace(NUMBER, \"#\") === sb.replace(NUMBER, \"#\")) {\n let i = 0;\n return sa.replace(NUMBER, () => {\n const v = Number(na[i]) + (Number(nb[i]) - Number(na[i])) * u;\n i++;\n return String(round(v));\n });\n }\n return u < 0.5 ? sa : sb;\n}\n\n/** Formats a number for a count animation (`{n}` stands for the rounded number). */\nexport function formatCount(\n value: number,\n options: { decimals?: number; format?: string } = {},\n): string {\n const n = value.toFixed(options.decimals ?? 0);\n return (options.format ?? \"{n}\").replace(\"{n}\", n);\n}\n\nconst startOf = (a: SvgAnimation, steps: readonly number[]) => {\n const s = stepStart(steps, a.step ?? 0);\n return s === undefined ? Infinity : s + (a.delay ?? 0);\n};\n\n/**\n * What each animated element shows at `timeMs`. For each attribute (or\n * line, count or writing) the latest change to have started wins; before the first\n * one starts, the element shows that change's starting value, so something\n * that grows in later is hidden until then.\n */\nexport function svgFrame(\n animations: readonly SvgAnimation[],\n steps: readonly number[],\n timeMs: number,\n): Map<string, SvgElementFrame> {\n // Each property's changes in the order they start (ties: list order).\n const tracks = new Map<string, { anim: SvgAnimation; start: number; index: number }[]>();\n const add = (prop: string, anim: SvgAnimation, index: number) => {\n const key = `${prop === \"view\" ? SVG_PICTURE : anim.id}\\u0000${prop}`;\n const list = tracks.get(key) ?? [];\n list.push({ anim, start: startOf(anim, steps), index });\n tracks.set(key, list);\n };\n animations.forEach((a, i) => {\n for (const name of Object.keys(a.attrs ?? {})) add(`@${name}`, a, i);\n if (a.draw) add(\"draw\", a, i);\n if (a.count) add(\"count\", a, i);\n if (a.write !== undefined) add(\"count\", a, i);\n if (a.solo !== undefined) add(\"solo\", a, i);\n if (a.view !== undefined) add(\"view\", a, i);\n if (a.along) add(\"along\", a, i);\n });\n const frames = new Map<string, SvgElementFrame>();\n for (const [key, list] of tracks) {\n list.sort((x, y) => x.start - y.start || x.index - y.index);\n const [id, prop] = key.split(\"\\u0000\") as [string, string];\n const started = list.filter((c) => c.start <= timeMs);\n const current = started[started.length - 1];\n const anim = (current ?? list[0]!).anim;\n const u = current\n ? stepProgress(steps, timeMs, anim.step ?? 0, {\n delay: anim.delay,\n ms: anim.ms,\n ease: anim.ease,\n })\n : 0;\n const frame = frames.get(id) ?? { attrs: {} };\n if (prop === \"view\") {\n // From where the change before it left the picture.\n const before = current ? list[list.indexOf(current) - 1] : undefined;\n frame.view = { from: before?.anim.view ?? null, to: anim.view!, u };\n } else if (prop === \"along\") {\n const [a, b] = anim.at ?? [0, 1];\n frame.along = { path: anim.along!, at: a + (b - a) * u };\n } else if (prop === \"solo\") {\n frame.solo = anim.solo;\n } else if (prop.startsWith(\"@\")) {\n const [a, b] = anim.attrs![prop.slice(1)]!;\n frame.attrs[prop.slice(1)] = lerpAttr(a, b, u);\n } else if (prop === \"draw\") {\n const [a, b] = anim.draw === true ? [0, 1] : (anim.draw as readonly [number, number]);\n frame.draw = a + (b - a) * u;\n } else if (anim.write !== undefined) {\n frame.text = writtenText(anim.write, u, { scramble: anim.scramble, caret: anim.caret, timeMs });\n } else {\n const [a, b] = anim.count!;\n frame.text = formatCount(a + (b - a) * u, anim);\n }\n frames.set(id, frame);\n }\n return frames;\n}\n\n/**\n * The opening tag of the root element at the start of `markup` (up to its\n * first `>`), cut out without a regular expression, so checks on it stay\n * linear however long or odd the markup is.\n */\nexport function svgOpeningTag(markup: string): string {\n const end = markup.indexOf(\">\");\n return end < 0 ? markup : markup.slice(0, end + 1);\n}\n\n/** Problems with an {@link SvgMotion}, as sentences; none when it can be drawn. */\nexport function svgMotionIssues(motion: unknown): string[] {\n const issues: string[] = [];\n const m = (motion ?? {}) as Partial<SvgMotion>;\n const svg = typeof m.svg === \"string\" ? m.svg.trim() : \"\";\n if (!svg) return [\"`svg` needs the picture's markup: one <svg> element\"];\n if (!/^<svg[\\s>]/i.test(svg) || !svg.toLowerCase().endsWith(\"</svg>\"))\n issues.push(\"`svg` must be one <svg>…</svg> element\");\n if (!/\\sviewBox\\s*=/i.test(svgOpeningTag(svg)))\n issues.push(\"the <svg> needs a `viewBox`, so it scales to where it is placed\");\n if (/<script|<foreignObject|\\son\\w+\\s*=/i.test(svg))\n issues.push(\"`svg` cannot have scripts, event handlers or <foreignObject>\");\n if (/(?:href|src)\\s*=\\s*[\"'](?!#|data:image\\/)/i.test(svg))\n issues.push(\"`svg` cannot link outside itself (only #ids and data:image URLs)\");\n const ids = new Set([...svg.matchAll(/\\sid\\s*=\\s*[\"']([^\"']+)[\"']/g)].map((x) => x[1]));\n if (m.animate !== undefined && !Array.isArray(m.animate)) issues.push(\"`animate` must be a list\");\n (Array.isArray(m.animate) ? m.animate : []).forEach((a, i) => {\n const where = `animate[${i}]`;\n if (!a || typeof a !== \"object\") return void issues.push(`${where} is not an animation`);\n if (typeof a.id !== \"string\") {\n if (a.view === undefined) return void issues.push(`${where} needs an \\`id\\``);\n } else if (!ids.has(a.id))\n issues.push(`${where}: there is no element with id \"${a.id}\" in the SVG`);\n if (a.step !== undefined && !(Number.isInteger(a.step) && a.step >= 0))\n issues.push(`${where}: \\`step\\` is a whole number, 0 or more`);\n if (a.ms !== undefined && !(a.ms >= 0)) issues.push(`${where}: \\`ms\\` must be 0 or more`);\n if (a.delay !== undefined && !(a.delay >= 0))\n issues.push(`${where}: \\`delay\\` must be 0 or more`);\n if (a.ease !== undefined && !(a.ease in EASES))\n issues.push(`${where}: \\`ease\\` is one of ${Object.keys(EASES).join(\", \")}`);\n if (!a.attrs && !a.draw && !a.count && a.write === undefined && a.view === undefined && !a.along && a.solo === undefined)\n issues.push(\n `${where} changes nothing: give \\`attrs\\`, \\`draw\\`, \\`count\\`, \\`write\\`, \\`view\\`, \\`along\\` or \\`solo\\``,\n );\n if (a.solo !== undefined && !(Number.isInteger(a.solo) && a.solo >= 0))\n issues.push(`${where}: \\`solo\\` is an index (whole number, 0 or more)`);\n if (a.view !== undefined) {\n const box = a.view as unknown;\n if (typeof box === \"string\") {\n if (!ids.has(box))\n issues.push(`${where}: \\`view\\` frames \"${box}\", which is not an id in the SVG`);\n } else if (\n !Array.isArray(box) ||\n box.length !== 4 ||\n !box.every((n) => Number.isFinite(n)) ||\n !(box[2] > 0 && box[3] > 0)\n )\n issues.push(\n `${where}: \\`view\\` is [x, y, width, height] (width and height above 0) or an element's id`,\n );\n }\n if (a.along !== undefined) {\n if (typeof a.along !== \"string\" || !ids.has(a.along))\n issues.push(`${where}: \\`along\\` is the id of a path in the SVG`);\n if (typeof a.id !== \"string\")\n issues.push(`${where}: \\`along\\` moves an element: give its \\`id\\``);\n }\n if (\n a.at !== undefined &&\n !(Array.isArray(a.at) && a.at.length === 2 && a.at.every((v) => v >= 0 && v <= 1))\n )\n issues.push(`${where}: \\`at\\` is [from, to] between 0 and 1`);\n for (const [name, pair] of Object.entries(a.attrs ?? {}))\n if (!Array.isArray(pair) || pair.length !== 2)\n issues.push(`${where}: \\`attrs.${name}\\` is [from, to]`);\n if (Array.isArray(a.draw) && a.draw.some((v) => !(v >= 0 && v <= 1)))\n issues.push(`${where}: \\`draw\\` is true or [from, to] between 0 and 1`);\n if (a.count !== undefined && !(Array.isArray(a.count) && a.count.length === 2))\n issues.push(`${where}: \\`count\\` is [from, to]`);\n if (a.write !== undefined && typeof a.write !== \"string\")\n issues.push(`${where}: \\`write\\` is the text to write`);\n if ((a.scramble !== undefined || a.caret !== undefined) && a.write === undefined)\n issues.push(`${where}: \\`scramble\\` and \\`caret\\` go with \\`write\\``);\n });\n return issues;\n}\n","/**\n * Drawing animated SVG pictures (see `SvgMotion`) in the DOM: a sanitising\n * parser, a view that applies frames, and a player that runs on a `Clock`.\n */\n\nimport { realClock, type Clock } from \"../time/clock.js\";\nimport {\n EASES,\n formatCount,\n STEP_MS,\n SVG_PICTURE,\n svgFrame,\n svgOpeningTag,\n type EaseName,\n type SvgBox,\n type SvgMotion,\n} from \"./motion.js\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\n/**\n * The `<svg>` in `markup`, parsed and made safe to show: no scripts, event\n * handlers, `<foreignObject>` or links outside itself. Undefined when it is\n * not an SVG.\n */\nexport function parseSvg(markup: string): SVGSVGElement | undefined {\n // Parsed as XML, an <svg> without its namespace is not an SVG at all.\n let source = markup.trim();\n const open = svgOpeningTag(source);\n if (!/\\sxmlns\\s*=/i.test(open)) source = source.replace(/^<svg/i, `<svg xmlns=\"${SVG_NS}\"`);\n if (/xlink:/.test(source) && !/\\sxmlns:xlink\\s*=/i.test(open))\n source = source.replace(/^<svg/i, '<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\"');\n const doc = new DOMParser().parseFromString(source, \"image/svg+xml\");\n const svg = doc.documentElement;\n if (svg.namespaceURI !== SVG_NS || svg.localName !== \"svg\" || doc.querySelector(\"parsererror\"))\n return undefined;\n for (const bad of svg.querySelectorAll(\"script, foreignObject\")) bad.remove();\n for (const el of [svg, ...svg.querySelectorAll(\"*\")]) {\n for (const attr of [...el.attributes]) {\n const name = attr.name.toLowerCase();\n const link = name === \"href\" || name === \"xlink:href\" || name === \"src\";\n if (name.startsWith(\"on\") || (link && !/^(#|data:image\\/)/i.test(attr.value.trim())))\n el.removeAttribute(attr.name);\n }\n }\n return document.importNode(svg, true) as unknown as SVGSVGElement;\n}\n\n/** Share of an element's size left round it when a `view` frames it. */\nconst VIEW_MARGIN = 0.12;\n\n/**\n * The box a `view` frames, in viewBox units: a box as given, an element's\n * box with a margin, widened to the picture's shape so the zoom does not\n * skew, or the whole picture for `null`.\n */\nfunction viewBox(view: SvgBox | string | null, whole: SvgBox, byId: Map<string, Element>): SvgBox {\n if (view === null) return whole;\n if (typeof view !== \"string\") return view;\n const el = byId.get(view) as SVGGraphicsElement | undefined;\n let box: DOMRect | undefined;\n try {\n box = el?.getBBox?.();\n } catch {\n // Not laid out (or not drawable): keep the whole picture.\n }\n if (!box || !(box.width > 0 || box.height > 0)) return whole;\n // In the picture's own units: through the element's transforms (a point\n // placed with one), which its box leaves out.\n try {\n const svg = el!.ownerSVGElement;\n const m = svg?.getScreenCTM()?.inverse().multiply(el!.getScreenCTM()!);\n if (m) {\n const corners = [\n [box.x, box.y],\n [box.x + box.width, box.y],\n [box.x, box.y + box.height],\n [box.x + box.width, box.y + box.height],\n ].map(([x, y]) => new DOMPoint(x, y).matrixTransform(m));\n const xs = corners.map((p) => p.x);\n const ys = corners.map((p) => p.y);\n box = new DOMRect(\n Math.min(...xs),\n Math.min(...ys),\n Math.max(...xs) - Math.min(...xs),\n Math.max(...ys) - Math.min(...ys),\n );\n }\n } catch {\n // Not laid out: keep its own box.\n }\n const aspect = whole[2] / whole[3];\n let w = box.width * (1 + 2 * VIEW_MARGIN);\n let h = box.height * (1 + 2 * VIEW_MARGIN);\n if (w / h > aspect) h = w / aspect;\n else w = h * aspect;\n return [box.x + box.width / 2 - w / 2, box.y + box.height / 2 - h / 2, w, h];\n}\n\n/**\n * Keeps the picture's `data-nonscaling` elements (a map's pins and labels)\n * the same size on screen as a `view` zooms: each is scaled round its own\n * origin by how far the picture is zoomed (so draw it round (0, 0) inside a\n * group that places it).\n */\nfunction keepSize(svg: SVGSVGElement, whole: SvgBox, nonscaling: Element[]) {\n if (!nonscaling.length) return;\n const width = Number((svg.getAttribute(\"viewBox\") ?? \"\").trim().split(/[\\s,]+/)[2]);\n const zoom = width > 0 ? width / whole[2] : 1;\n const transform = Math.abs(zoom - 1) < 1e-4 ? \"\" : `scale(${+zoom.toFixed(4)})`;\n for (const el of nonscaling)\n if ((el.getAttribute(\"transform\") ?? \"\") !== transform)\n if (transform) el.setAttribute(\"transform\", transform);\n else el.removeAttribute(\"transform\");\n}\n\n/** Applies a frame of the media's animations to `svg`. */\nfunction apply(\n svg: SVGSVGElement,\n whole: SvgBox,\n byId: Map<string, Element>,\n motion: SvgMotion,\n timeMs: number,\n steps: readonly number[],\n) {\n for (const [id, frame] of svgFrame(motion.animate ?? [], steps, timeMs)) {\n if (id === SVG_PICTURE && frame.view) {\n const a = viewBox(frame.view.from, whole, byId);\n const b = viewBox(frame.view.to, whole, byId);\n const u = frame.view.u;\n svg.setAttribute(\"viewBox\", a.map((n, i) => +(n + (b[i]! - n) * u).toFixed(2)).join(\" \"));\n continue;\n }\n const el = byId.get(id);\n if (!el) continue;\n for (const [name, value] of Object.entries(frame.attrs)) el.setAttribute(name, value);\n if (frame.draw !== undefined) {\n // A path length of 1 lets the dash be a fraction, with nothing measured.\n el.setAttribute(\"pathLength\", \"1\");\n el.setAttribute(\"stroke-dasharray\", \"1 1\");\n el.setAttribute(\"stroke-dashoffset\", String(1 - frame.draw));\n }\n if (frame.text !== undefined) el.textContent = frame.text;\n if (frame.solo !== undefined) {\n const children = Array.from(el.children);\n children.forEach((child, idx) => {\n if (child instanceof SVGElement || child instanceof HTMLElement) {\n if (idx === frame.solo) {\n child.removeAttribute(\"display\");\n } else {\n child.setAttribute(\"display\", \"none\");\n }\n }\n });\n }\n if (frame.along) {\n const path = byId.get(frame.along.path) as SVGGeometryElement | undefined;\n try {\n const length = path?.getTotalLength?.();\n if (path && length !== undefined) {\n const p = path.getPointAtLength(frame.along.at * length);\n el.setAttribute(\"transform\", `translate(${+p.x.toFixed(2)} ${+p.y.toFixed(2)})`);\n }\n } catch {\n // Not laid out: leave it where it is.\n }\n }\n }\n}\n\nconst VAR = /var\\(\\s*(--[\\w-]+)\\s*(?:,\\s*([^()]*(?:\\([^()]*\\)[^()]*)*))?\\)/g;\n\n/**\n * `copy` with the CSS variables in its attributes and styles replaced by\n * their values on `live` (its original, in the page), or their fallbacks:\n * an image of the SVG is drawn on its own, where the page's variables (a\n * host's palette) mean nothing.\n */\nfunction resolveVariables(live: SVGSVGElement, copy: SVGSVGElement) {\n const originals = [live, ...live.querySelectorAll(\"*\")];\n const copies = [copy, ...copy.querySelectorAll(\"*\")];\n copies.forEach((el, i) => {\n let style: CSSStyleDeclaration | undefined;\n for (const attr of [...el.attributes]) {\n if (!attr.value.includes(\"var(\")) continue;\n style ??= getComputedStyle(originals[i] ?? live);\n el.setAttribute(\n attr.name,\n attr.value.replace(\n VAR,\n (_, name: string, fallback?: string) =>\n style!.getPropertyValue(name).trim() || fallback?.trim() || \"\",\n ),\n );\n }\n });\n}\n\n/** An image of the picture as drawn now (its layers in order), at `pixels`. */\nasync function rasterize(layers: SVGSVGElement[], pixels: { width: number; height: number }): Promise<HTMLCanvasElement> {\n const canvas = document.createElement(\"canvas\");\n canvas.width = pixels.width;\n canvas.height = pixels.height;\n const defs = layers[0]?.querySelector(\"defs\");\n for (const [i, svg] of layers.entries()) {\n const copy = svg.cloneNode(true) as SVGSVGElement;\n resolveVariables(svg, copy);\n // A fixed layer's gradients and markers live in the picture's <defs>.\n if (i > 0 && defs) copy.prepend(defs.cloneNode(true));\n copy.setAttribute(\"xmlns\", SVG_NS);\n copy.setAttribute(\"width\", String(pixels.width));\n copy.setAttribute(\"height\", String(pixels.height));\n const url = URL.createObjectURL(\n new Blob([new XMLSerializer().serializeToString(copy)], { type: \"image/svg+xml\" }),\n );\n try {\n const image = new Image();\n image.src = url;\n await image.decode();\n canvas.getContext(\"2d\")!.drawImage(image, 0, 0, pixels.width, pixels.height);\n } finally {\n URL.revokeObjectURL(url);\n }\n }\n return canvas;\n}\n\n/**\n * Moves the picture's `data-fixed` elements (titles, legends, notes) into a\n * layer over it that keeps the original `viewBox`, so a `view` zooms the\n * picture under them while they stay put. Undefined when it has none.\n */\nfunction fixedLayer(svg: SVGSVGElement): SVGSVGElement | undefined {\n const fixed = [...svg.querySelectorAll(\"[data-fixed]\")].filter(\n (el) => !el.parentElement?.closest(\"[data-fixed]\"),\n );\n if (!fixed.length) return undefined;\n // The picture's own attributes (viewBox, fonts, fills) without its id.\n const layer = svg.cloneNode(false) as SVGSVGElement;\n layer.removeAttribute(\"id\");\n layer.setAttribute(\"aria-hidden\", \"true\");\n layer.style.cssText = \"position:absolute;inset:0;overflow:visible\";\n for (const el of fixed) layer.appendChild(el);\n return layer;\n}\n\nexport interface SvgViewOptions {\n /** Called with the id of the innermost element with an id that is clicked. */\n onPick?: (id: string) => void;\n}\n\n/** An {@link SvgMotion} drawn into an element; call `draw` with a time to show it then. */\nexport interface SvgView {\n /** A `div` filled by the picture; put it anywhere. */\n readonly element: HTMLDivElement;\n /** Shows the picture at `timeMs`, with steps starting at `steps` (ms). */\n draw(timeMs: number, steps?: readonly number[]): void;\n /** An image of the picture as drawn now. */\n capture(pixels: { width: number; height: number }): Promise<HTMLCanvasElement | undefined>;\n}\n\n/** Shows an {@link SvgMotion} as a live view; with `onPick`, clicks report the id of the element clicked. */\nexport function svgView(motion: SvgMotion, options: SvgViewOptions = {}): SvgView {\n const element = document.createElement(\"div\");\n element.style.width = \"100%\";\n element.style.height = \"100%\";\n const { onPick } = options;\n if (onPick)\n element.addEventListener(\"click\", (event) => {\n // The innermost element with an id, up to the picture itself.\n for (let el = event.target as Element | null; el && el !== element; el = el.parentElement)\n if (el.id && el.localName !== \"svg\") return onPick(el.id);\n });\n let svg: SVGSVGElement | undefined | null = null;\n let fixed: SVGSVGElement | undefined;\n let nonscaling: Element[] = [];\n let whole: SvgBox = [0, 0, 100, 100];\n const byId = new Map<string, Element>();\n return {\n element,\n draw(timeMs, steps = []) {\n if (svg === null) {\n // Parsed on first use, so a view made and dropped costs nothing.\n svg = parseSvg(motion.svg);\n if (svg) {\n svg.setAttribute(\"width\", \"100%\");\n svg.setAttribute(\"height\", \"100%\");\n element.appendChild(svg);\n fixed = fixedLayer(svg);\n if (fixed) {\n element.style.position = \"relative\";\n element.appendChild(fixed);\n }\n for (const layer of [svg, fixed])\n for (const el of layer?.querySelectorAll(\"[id]\") ?? []) byId.set(el.id, el);\n nonscaling = [...svg.querySelectorAll(\"[data-nonscaling]\")];\n const box = (svg.getAttribute(\"viewBox\") ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .map(Number);\n if (box.length === 4 && box.every(Number.isFinite)) whole = box as unknown as SvgBox;\n }\n }\n if (svg) {\n apply(svg, whole, byId, motion, timeMs, steps);\n keepSize(svg, whole, nonscaling);\n }\n },\n capture: async (pixels) => (svg ? rasterize(fixed ? [svg, fixed] : [svg], pixels) : undefined),\n };\n}\n\n/** How long an {@link SvgMotion} runs: its last change's end, in ms (at least 1). */\nexport function svgDuration(motion: SvgMotion, steps: readonly number[] = []): number {\n let end = 0;\n for (const a of motion.animate ?? []) {\n const step = a.step ?? 0;\n const start = step <= 0 ? 0 : steps[step - 1];\n if (start === undefined) continue;\n end = Math.max(end, start + (a.delay ?? 0) + (a.ms ?? 800));\n }\n return Math.max(1, end);\n}\n\nexport interface SvgPlayerOptions extends SvgViewOptions {\n /** Time source (default: animation frames). Pass a `ManualClock` to step frames yourself. */\n clock?: Clock;\n /** Start playing at once (default `true`). */\n autoplay?: boolean;\n /** Start again at the end (default `true`). */\n loop?: boolean;\n /** Loop length in ms (default: the picture's own, see {@link svgDuration}, plus `hold`). */\n duration?: number;\n /** Ms to rest on the last frame before looping (default 600). */\n hold?: number;\n /** Playback speed (default 1). */\n speed?: number;\n /** Step start times in ms, for pictures that change on steps (default none). */\n steps?: readonly number[];\n /** Pause the animation loop when scrolled out of view via IntersectionObserver (default false). */\n pauseWhenHidden?: boolean;\n /** Wait to start playing until the element enters the viewport via IntersectionObserver (default false). When true, autoplay is deferred until first visible. */\n playOnEnter?: boolean;\n /** Hide the view (`visibility: hidden`) whenever playback is paused or reset (default false). */\n hideWhenPaused?: boolean;\n /**\n * Plays in answer to the pointer instead of on its own (see {@link SvgTrigger}):\n * `\"hover\"` and `\"press\"` play to the end while hovered or held and back to\n * the start after; `\"click\"` plays the next step on each click, starting\n * over after the last. `autoplay`, `loop` and `steps` are ignored.\n */\n trigger?: SvgTrigger;\n /** The element whose pointer events trigger it (default the container). */\n triggerTarget?: HTMLElement;\n}\n\n/** What makes a triggered {@link SvgPlayer} play. */\nexport type SvgTrigger = \"hover\" | \"press\" | \"click\";\n\n/**\n * Plays an {@link SvgMotion} inside `container`: loops by default, and can be\n * paused, sought and sped up. Dispose it to stop and remove the picture.\n */\nexport class SvgPlayer {\n readonly view: SvgView;\n private readonly clock: Clock;\n private steps: readonly number[];\n private readonly loop: boolean;\n private length: number;\n private readonly motion: SvgMotion;\n private readonly trigger?: SvgTrigger;\n private readonly triggerTarget?: HTMLElement;\n /** With a hover or press trigger: 1 playing to the end, -1 back to the start. */\n private direction = 1;\n /** With a click trigger: when each click came, in ms of playback. */\n private clicks: number[] = [];\n private readonly hideWhenPaused: boolean;\n private speed: number;\n private time = 0;\n private last = 0;\n private playing = false;\n private disposed = false;\n private wasPlayingBeforeHidden = false;\n private hasEnteredViewport = false;\n private intersectionObserver?: IntersectionObserver;\n\n constructor(\n private readonly container: HTMLElement,\n motion: SvgMotion,\n options: SvgPlayerOptions = {},\n ) {\n this.clock = options.clock ?? realClock;\n this.motion = motion;\n this.trigger = options.trigger;\n this.steps = this.trigger ? [] : (options.steps ?? []);\n this.loop = this.trigger ? false : (options.loop ?? true);\n this.speed = options.speed ?? 1;\n this.hideWhenPaused = Boolean(options.hideWhenPaused);\n this.length = options.duration ?? svgDuration(motion, this.steps) + (options.hold ?? 600);\n this.view = svgView(motion, options);\n container.appendChild(this.view.element);\n this.view.draw(0, this.steps);\n\n const needsObserver =\n (options.pauseWhenHidden || options.playOnEnter) && typeof IntersectionObserver !== \"undefined\";\n\n if (needsObserver) {\n this.intersectionObserver = new IntersectionObserver(([entry]) => {\n if (!entry) return;\n if (entry.isIntersecting) {\n if (options.playOnEnter && !this.hasEnteredViewport) {\n this.hasEnteredViewport = true;\n if (options.autoplay ?? true) {\n this.play();\n }\n } else if (this.wasPlayingBeforeHidden) {\n this.wasPlayingBeforeHidden = false;\n this.play();\n }\n } else {\n if (this.playing && options.pauseWhenHidden) {\n this.wasPlayingBeforeHidden = true;\n this.pause();\n }\n }\n });\n this.intersectionObserver.observe(this.view.element);\n }\n\n if (this.trigger) {\n // A click-triggered picture runs until its latest step is over, however many clicks.\n // Hovers and presses run to the last change and back, with no rest to wait through.\n this.length = this.trigger === \"click\" ? Infinity : (options.duration ?? svgDuration(motion));\n this.triggerTarget = options.triggerTarget ?? container;\n this.listen(true);\n return;\n }\n\n const shouldAutoplay = (options.autoplay ?? true) && !options.playOnEnter;\n if (shouldAutoplay) {\n this.play();\n } else if (this.hideWhenPaused) {\n this.view.element.style.visibility = \"hidden\";\n }\n }\n\n /** The loop length in ms. */\n get duration(): number {\n return this.length;\n }\n\n /** The time shown, in ms. */\n get currentTime(): number {\n return this.time;\n }\n\n get isPlaying(): boolean {\n return this.playing;\n }\n\n play(): void {\n if (this.playing || this.disposed) return;\n this.playing = true;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"visible\";\n this.last = this.clock.now();\n void this.run();\n }\n\n pause(): void {\n this.playing = false;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n }\n\n /** Stops playback, rewinds to `0 ms`, and hides the view if `hideWhenPaused` is enabled. */\n reset(): void {\n this.playing = false;\n this.wasPlayingBeforeHidden = false;\n this.seek(0);\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n }\n\n /** Shows the picture at `ms` (wrapped into the loop when looping). */\n seek(ms: number): void {\n this.time = this.loop\n ? ((ms % this.length) + this.length) % this.length\n : Math.min(Math.max(ms, 0), this.length);\n this.view.draw(this.time, this.steps);\n }\n\n setSpeed(speed: number): void {\n this.speed = speed;\n }\n\n /**\n * Plays to the end (`true`) or back to the start (`false`) from wherever it\n * is: what a hover or press trigger does, for hosts that drive it themselves\n * (a hovered card, a focused button).\n */\n setActive(active: boolean): void {\n this.direction = active ? 1 : -1;\n this.play();\n }\n\n /**\n * Plays the picture's next step (what a click trigger does): steps 1, 2, …\n * in turn, then step 1 again, so a picture whose last step leads back to\n * how it started toggles; one with no steps plays again from the start.\n */\n advance(): void {\n const last = Math.max(0, ...(this.motion.animate ?? []).map((a) => a.step ?? 0));\n if (last === 0) {\n this.clicks = [];\n this.time = 0;\n } else {\n this.clicks.push(this.time);\n // Only the clicks of the current round count, so each round starts from how the last ended.\n this.steps = this.clicks.slice(Math.floor((this.clicks.length - 1) / last) * last);\n }\n this.view.draw(this.time, this.steps);\n this.play();\n }\n\n dispose(): void {\n this.disposed = true;\n this.playing = false;\n if (this.trigger) this.listen(false);\n this.intersectionObserver?.disconnect();\n this.view.element.remove();\n }\n\n private onEnter = () => this.setActive(true);\n private onLeave = () => this.setActive(false);\n private onClick = () => this.advance();\n\n private listen(on: boolean) {\n const target = this.triggerTarget!;\n const method = on ? \"addEventListener\" : \"removeEventListener\";\n if (this.trigger === \"hover\") {\n target[method](\"pointerenter\", this.onEnter);\n target[method](\"pointerleave\", this.onLeave);\n } else if (this.trigger === \"press\") {\n target[method](\"pointerdown\", this.onEnter);\n window[method](\"pointerup\", this.onLeave);\n window[method](\"pointercancel\", this.onLeave);\n } else target[method](\"click\", this.onClick);\n }\n\n private async run(): Promise<void> {\n while (this.playing && !this.disposed) {\n await this.clock.frame();\n if (!this.playing || this.disposed) return;\n const now = this.clock.now();\n const dt = (now - this.last) * this.speed;\n this.last = now;\n if (this.trigger === \"click\") {\n // Rest once the latest step is over, until the next click.\n const end = svgDuration(this.motion, this.steps);\n this.time = Math.min(this.time + dt, Math.max(end, this.time));\n this.view.draw(this.time, this.steps);\n if (this.time >= end) this.playing = false;\n continue;\n }\n if (this.trigger) {\n const next = Math.min(Math.max(this.time + dt * this.direction, 0), this.length);\n this.seek(next);\n if (next === 0 || next === this.length) this.playing = false;\n continue;\n }\n const next = this.time + dt;\n if (!this.loop && next >= this.length) {\n this.seek(this.length);\n this.playing = false;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n return;\n }\n this.seek(next);\n }\n }\n}\n\nexport interface AnimateCountOptions {\n /** Starting number. */\n from: number;\n /** Ending number. */\n to: number;\n /** Duration in ms (default {@link STEP_MS}). */\n ms?: number;\n /** Delay before starting in ms (default 0). */\n delay?: number;\n /** Easing curve (default `\"out\"`). */\n ease?: EaseName;\n /** Number of decimal places (default 0). */\n decimals?: number;\n /** Format template where `{n}` is replaced by the formatted number (default `\"{n}\"`). */\n format?: string;\n /** Time source (default: animation frames). */\n clock?: Clock;\n /** Called with each formatted text frame (and raw numeric value). */\n onUpdate: (text: string, value: number) => void;\n}\n\n/**\n * Counts a number from `from` to `to` over `ms` using Flair's easing curves\n * and `Clock`, calling `onUpdate` with the formatted string on each frame.\n * Returns a cancel function that stops the animation.\n */\nexport function animateCount(options: AnimateCountOptions): () => void {\n const clock = options.clock ?? realClock;\n const ms = options.ms ?? STEP_MS;\n const delay = options.delay ?? 0;\n const ease = EASES[options.ease ?? \"out\"];\n const start = clock.now();\n let stopped = false;\n\n const emit = (u: number) => {\n const v = options.from + (options.to - options.from) * ease(u);\n options.onUpdate(formatCount(v, options), v);\n };\n\n emit(ms <= 0 && delay <= 0 ? 1 : 0);\n if (ms <= 0 && delay <= 0) return () => {};\n\n void (async () => {\n while (!stopped) {\n await clock.frame();\n if (stopped) return;\n const elapsed = clock.now() - start - delay;\n if (elapsed < 0) continue;\n if (ms <= 0 || elapsed >= ms) {\n emit(1);\n return;\n }\n emit(elapsed / ms);\n }\n })();\n\n return () => {\n stopped = true;\n };\n}\n"],"mappings":"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","names":["next"]}
|
|
1
|
+
{"version":3,"sources":["../src/vector/motion.ts","../src/vector/svg.ts"],"sourcesContent":["/**\n * Vector motion: animated SVG pictures described as data. A picture changes in\n * steps (step 0 when it starts, step n at `steps[n - 1]` ms), and everything\n * here is a pure function of its arguments, so a frame comes out the same\n * however it is reached (played, stepped for a recording, or seeked).\n */\n\n/** Easing curves by name. */\nexport const EASES = {\n linear: (t: number) => t,\n \"in-out\": (t: number) => (t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2),\n out: (t: number) => 1 - (1 - t) ** 3,\n} as const;\n\nexport type EaseName = keyof typeof EASES;\n\n/** How long a change takes by default, in ms. */\nexport const STEP_MS = 800;\n\nconst clamp01 = (t: number) => (t < 0 ? 0 : t > 1 ? 1 : t);\n\n/**\n * When `step` starts, in ms since the media started (step 0 at 0, step n at\n * `steps[n - 1]`), or undefined if it has not started yet.\n */\nexport function stepStart(steps: readonly number[], step: number): number | undefined {\n if (step <= 0) return 0;\n return steps[step - 1];\n}\n\n/**\n * How far a change that plays on `step` (after `delay` ms, over `ms`) has\n * got at `timeMs`, eased: 0 before it starts, 1 once it is over.\n */\nexport function stepProgress(\n steps: readonly number[],\n timeMs: number,\n step: number,\n options: { delay?: number; ms?: number; ease?: EaseName } = {},\n): number {\n const start = stepStart(steps, step);\n if (start === undefined) return 0;\n const from = start + (options.delay ?? 0);\n const ms = options.ms ?? STEP_MS;\n const t = ms > 0 ? clamp01((timeMs - from) / ms) : timeMs >= from ? 1 : 0;\n return EASES[options.ease ?? \"in-out\"](t);\n}\n\n/** The key an item in a list of data keeps from one state to the next. */\nexport type DataKey = (item: unknown, index: number) => string;\n\n/**\n * Keys items by their `id`, `key`, `name` or `label` (or a link's `source`\n * and `target`), else by position.\n */\nexport const defaultDataKey: DataKey = (item, index) => {\n if (item && typeof item === \"object\") {\n const o = item as Record<string, unknown>;\n if (o.source !== undefined && o.target !== undefined)\n return `${String(o.source)}→${String(o.target)}`;\n for (const k of [\"id\", \"key\", \"name\", \"label\"])\n if (typeof o[k] === \"string\" || typeof o[k] === \"number\") return String(o[k]);\n }\n return String(index);\n};\n\n/** Every number in `value` times `k`, so an item can grow from or shrink to nothing. */\nfunction scaled(value: unknown, k: number): unknown {\n if (typeof value === \"number\") return value * k;\n if (Array.isArray(value)) return value.map((v) => scaled(v, k));\n if (value && typeof value === \"object\")\n return Object.fromEntries(Object.entries(value).map(([key, v]) => [key, scaled(v, k)]));\n return value;\n}\n\n/**\n * Data between two states, `u` of the way from `a` to `b`. Numbers blend;\n * lists match their items by key (`keyOf`), so each keeps its identity and\n * moves rather than jumping, with items only in `b` growing from nothing and\n * items only in `a` shrinking away; objects blend field by field; anything\n * else changes halfway.\n */\nexport function lerpData<D>(a: D, b: D, u: number, keyOf: DataKey = defaultDataKey): D {\n if (u <= 0) return a;\n if (u >= 1) return b;\n return blend(a, b, u, keyOf) as D;\n}\n\nfunction blend(a: unknown, b: unknown, u: number, keyOf: DataKey): unknown {\n if (typeof a === \"number\" && typeof b === \"number\") return a + (b - a) * u;\n if (Array.isArray(a) && Array.isArray(b)) {\n const before = new Map(a.map((item, i) => [keyOf(item, i), item]));\n const after = new Set(b.map((item, i) => keyOf(item, i)));\n const out: unknown[] = b.map((item, i) => {\n const key = keyOf(item, i);\n return before.has(key) ? blend(before.get(key), item, u, keyOf) : scaled(item, u);\n });\n a.forEach((item, i) => {\n if (!after.has(keyOf(item, i))) out.push(scaled(item, 1 - u));\n });\n return out;\n }\n if (a && b && typeof a === \"object\" && typeof b === \"object\") {\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n return Object.fromEntries(\n [...new Set([...Object.keys(ao), ...Object.keys(bo)])].map((k) => [\n k,\n k in ao && k in bo\n ? blend(ao[k], bo[k], u, keyOf)\n : k in bo\n ? scaled(bo[k], u)\n : scaled(ao[k], 1 - u),\n ]),\n );\n }\n return u < 0.5 ? a : b;\n}\n\n/**\n * The data to show at `timeMs`, for media whose data has a state per step:\n * `states[n]` is shown from step n (the last one stays), reached by moving\n * from the state before it over `ms`.\n */\nexport function stepData<D>(\n states: readonly D[],\n steps: readonly number[],\n timeMs: number,\n options: { ms?: number; ease?: EaseName; keyOf?: DataKey } = {},\n): D {\n const last = Math.min(steps.length, states.length - 1);\n const to = states[last]!;\n if (last === 0) return to;\n const u = stepProgress(steps, timeMs, last, options);\n return lerpData(states[last - 1]!, to, u, options.keyOf);\n}\n\n// ── SVG media ──────────────────────────────────────────────────────────\n\n/**\n * An animated SVG picture: markup plus changes to its elements. Draw it with\n * `SvgPlayer` (or `svgView`), or compute frames yourself with `svgFrame`.\n */\nexport interface SvgMotion {\n /** One self-contained `<svg>` with a `viewBox`: no scripts, no outside links. */\n svg: string;\n /** Changes to its elements, each on a step. */\n animate?: SvgAnimation[];\n}\n\n/** A box in an SVG's `viewBox` units: `[x, y, width, height]`. */\nexport type SvgBox = readonly [number, number, number, number];\n\n/** A change to one element of an {@link SvgMotion}, or (with `view`) to the whole picture. */\nexport interface SvgAnimation {\n /** The element's `id` in the SVG (not needed for `view`). */\n id?: string;\n /** The step it plays on: 0 (default) when the media starts, 1 with the next shot, and so on. */\n step?: number;\n /** Ms after the step starts. */\n delay?: number;\n /** How long it takes, in ms (default {@link STEP_MS}). */\n ms?: number;\n ease?: EaseName;\n /**\n * Attributes from one value to another: numbers, colours (`#rrggbb`), or\n * strings whose numbers change while the rest stays the same (a path's\n * `d`, a `transform`, `points`).\n */\n attrs?: Record<string, readonly [string | number, string | number]>;\n /** Draws a line or outline on along its length, from 0 to 1 (or between two fractions). */\n draw?: boolean | readonly [number, number];\n /**\n * Zooms and pans the whole picture to frame a box (`[x, y, width, height]`\n * in the `viewBox`'s units) or an element by id (with a margin round it),\n * from where the last `view` left it (at first, the whole `viewBox`).\n */\n view?: SvgBox | string;\n /**\n * Moves the element along a path (by id) from its start to its end, or\n * between the fractions in `at`; draw the element round (0, 0), like a\n * circle at `cx=\"0\" cy=\"0\"`.\n */\n along?: string;\n /** How far along the path `along` moves the element, from and to (default [0, 1]). */\n at?: readonly [number, number];\n /**\n * Sets the active child index in a solo container (data-solo group or element with multiple children).\n * Hides all siblings except the one at this index (0-based).\n */\n solo?: number;\n /** Counts the element's text from one number to the other. */\n count?: readonly [number, number];\n /** How a count is written: `{n}` stands for the number (\"{n}%\", \"$ {n}M\"). */\n format?: string;\n /** Decimals in a count (default 0). */\n decimals?: number;\n /**\n * Writes this text into the element letter by letter: none of it when the\n * change starts, all of it when it ends (a typewriter).\n */\n write?: string;\n /**\n * With `write`: letters not yet written show characters picked from this\n * string, changing every {@link SCRAMBLE_MS} ms, so the text decodes\n * rather than types. The same time always picks the same characters.\n */\n scramble?: string;\n /** With `write`: shown after the written letters while it writes (\"▌\", \"|\"). */\n caret?: string;\n}\n\n/** How often scrambled letters change, in ms (see `SvgAnimation.scramble`). */\nexport const SCRAMBLE_MS = 50;\n\n/**\n * `text` with `u` (0–1) of its letters written (see `SvgAnimation.write`):\n * the rest scrambled from `scramble` (picked by `timeMs`) or left off, then\n * the caret while it is unfinished. Letters are code points, so emoji and\n * accents are never split.\n */\nexport function writtenText(\n text: string,\n u: number,\n options: { scramble?: string; caret?: string; timeMs?: number } = {},\n): string {\n const letters = Array.from(text);\n const shown = Math.round(clamp01(u) * letters.length);\n let out = letters.slice(0, shown).join(\"\");\n const pool = Array.from(options.scramble ?? \"\");\n if (pool.length) {\n const tick = Math.floor((options.timeMs ?? 0) / SCRAMBLE_MS);\n for (let i = shown; i < letters.length; i++)\n out += /\\s/.test(letters[i]!) ? letters[i] : pool[Math.floor(noise(i, tick) * pool.length)];\n }\n return shown < letters.length && options.caret ? out + options.caret : out;\n}\n\n/** A repeatable 0–1 number for a letter at a tick. */\nfunction noise(i: number, tick: number): number {\n const x = Math.sin(i * 127.1 + tick * 311.7) * 43758.5453;\n return x - Math.floor(x);\n}\n\n/** What one element of an animated SVG shows at a time. */\nexport interface SvgElementFrame {\n attrs: Record<string, string>;\n /** How much of its length is drawn, 0–1. */\n draw?: number;\n text?: string;\n /** Active child index when targeting a solo container. */\n solo?: number;\n /** Where it is along a path: the path's id and the fraction of its length. */\n along?: { path: string; at: number };\n /**\n * The whole picture's framing (on the frame for {@link SVG_PICTURE}): `u`\n * of the way from one view to the next, `null` being the whole `viewBox`.\n */\n view?: { from: SvgBox | string | null; to: SvgBox | string; u: number };\n}\n\n/** The key of the whole picture's frame in {@link svgFrame}'s result (for `view`). */\nexport const SVG_PICTURE = \"\";\n\nconst NUMBER = /-?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[-+]?\\d+)?/gi;\nconst HEX = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;\n\nfunction rgb(hex: string): [number, number, number] {\n let h = hex.slice(1);\n if (h.length === 3) h = [...h].map((c) => c + c).join(\"\");\n return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16)) as [number, number, number];\n}\n\nconst round = (n: number) => Math.round(n * 1000) / 1000;\n\n/** A value `u` of the way between two attribute values (see `SvgAnimation.attrs`). */\nexport function lerpAttr(a: string | number, b: string | number, u: number): string {\n if (typeof a === \"number\" && typeof b === \"number\") return String(round(a + (b - a) * u));\n const sa = String(a);\n const sb = String(b);\n if (HEX.test(sa) && HEX.test(sb)) {\n const [ca, cb] = [rgb(sa), rgb(sb)];\n return `#${ca\n .map((c, i) =>\n Math.round(c + (cb[i]! - c) * u)\n .toString(16)\n .padStart(2, \"0\"),\n )\n .join(\"\")}`;\n }\n const na = sa.match(NUMBER) ?? [];\n const nb = sb.match(NUMBER) ?? [];\n if (na.length && na.length === nb.length && sa.replace(NUMBER, \"#\") === sb.replace(NUMBER, \"#\")) {\n let i = 0;\n return sa.replace(NUMBER, () => {\n const v = Number(na[i]) + (Number(nb[i]) - Number(na[i])) * u;\n i++;\n return String(round(v));\n });\n }\n return u < 0.5 ? sa : sb;\n}\n\n/** Formats a number for a count animation (`{n}` stands for the rounded number). */\nexport function formatCount(\n value: number,\n options: { decimals?: number; format?: string } = {},\n): string {\n const n = value.toFixed(options.decimals ?? 0);\n return (options.format ?? \"{n}\").replace(\"{n}\", n);\n}\n\nconst startOf = (a: SvgAnimation, steps: readonly number[]) => {\n const s = stepStart(steps, a.step ?? 0);\n return s === undefined ? Infinity : s + (a.delay ?? 0);\n};\n\n/**\n * What each animated element shows at `timeMs`. For each attribute (or\n * line, count or writing) the latest change to have started wins; before the first\n * one starts, the element shows that change's starting value, so something\n * that grows in later is hidden until then.\n */\nexport function svgFrame(\n animations: readonly SvgAnimation[],\n steps: readonly number[],\n timeMs: number,\n): Map<string, SvgElementFrame> {\n // Each property's changes in the order they start (ties: list order).\n const tracks = new Map<string, { anim: SvgAnimation; start: number; index: number }[]>();\n const add = (prop: string, anim: SvgAnimation, index: number) => {\n const key = `${prop === \"view\" ? SVG_PICTURE : anim.id}\\u0000${prop}`;\n const list = tracks.get(key) ?? [];\n list.push({ anim, start: startOf(anim, steps), index });\n tracks.set(key, list);\n };\n animations.forEach((a, i) => {\n for (const name of Object.keys(a.attrs ?? {})) add(`@${name}`, a, i);\n if (a.draw) add(\"draw\", a, i);\n if (a.count) add(\"count\", a, i);\n if (a.write !== undefined) add(\"count\", a, i);\n if (a.solo !== undefined) add(\"solo\", a, i);\n if (a.view !== undefined) add(\"view\", a, i);\n if (a.along) add(\"along\", a, i);\n });\n const frames = new Map<string, SvgElementFrame>();\n for (const [key, list] of tracks) {\n list.sort((x, y) => x.start - y.start || x.index - y.index);\n const [id, prop] = key.split(\"\\u0000\") as [string, string];\n const started = list.filter((c) => c.start <= timeMs);\n const current = started[started.length - 1];\n const anim = (current ?? list[0]!).anim;\n const u = current\n ? stepProgress(steps, timeMs, anim.step ?? 0, {\n delay: anim.delay,\n ms: anim.ms,\n ease: anim.ease,\n })\n : 0;\n const frame = frames.get(id) ?? { attrs: {} };\n if (prop === \"view\") {\n // From where the change before it left the picture.\n const before = current ? list[list.indexOf(current) - 1] : undefined;\n frame.view = { from: before?.anim.view ?? null, to: anim.view!, u };\n } else if (prop === \"along\") {\n const [a, b] = anim.at ?? [0, 1];\n frame.along = { path: anim.along!, at: a + (b - a) * u };\n } else if (prop === \"solo\") {\n frame.solo = anim.solo;\n } else if (prop.startsWith(\"@\")) {\n const [a, b] = anim.attrs![prop.slice(1)]!;\n frame.attrs[prop.slice(1)] = lerpAttr(a, b, u);\n } else if (prop === \"draw\") {\n const [a, b] = anim.draw === true ? [0, 1] : (anim.draw as readonly [number, number]);\n frame.draw = a + (b - a) * u;\n } else if (anim.write !== undefined) {\n frame.text = writtenText(anim.write, u, { scramble: anim.scramble, caret: anim.caret, timeMs });\n } else {\n const [a, b] = anim.count!;\n frame.text = formatCount(a + (b - a) * u, anim);\n }\n frames.set(id, frame);\n }\n return frames;\n}\n\n/**\n * The opening tag of the root element at the start of `markup` (up to its\n * first `>`), cut out without a regular expression, so checks on it stay\n * linear however long or odd the markup is.\n */\nexport function svgOpeningTag(markup: string): string {\n const end = markup.indexOf(\">\");\n return end < 0 ? markup : markup.slice(0, end + 1);\n}\n\n/** Problems with an {@link SvgMotion}, as sentences; none when it can be drawn. */\nexport function svgMotionIssues(motion: unknown): string[] {\n const issues: string[] = [];\n const m = (motion ?? {}) as Partial<SvgMotion>;\n const svg = typeof m.svg === \"string\" ? m.svg.trim() : \"\";\n if (!svg) return [\"`svg` needs the picture's markup: one <svg> element\"];\n if (!/^<svg[\\s>]/i.test(svg) || !svg.toLowerCase().endsWith(\"</svg>\"))\n issues.push(\"`svg` must be one <svg>…</svg> element\");\n if (!/\\sviewBox\\s*=/i.test(svgOpeningTag(svg)))\n issues.push(\"the <svg> needs a `viewBox`, so it scales to where it is placed\");\n if (/<script|<foreignObject|\\son\\w+\\s*=/i.test(svg))\n issues.push(\"`svg` cannot have scripts, event handlers or <foreignObject>\");\n if (/(?:href|src)\\s*=\\s*[\"'](?!#|data:image\\/)/i.test(svg))\n issues.push(\"`svg` cannot link outside itself (only #ids and data:image URLs)\");\n const ids = new Set([...svg.matchAll(/\\sid\\s*=\\s*[\"']([^\"']+)[\"']/g)].map((x) => x[1]));\n if (m.animate !== undefined && !Array.isArray(m.animate)) issues.push(\"`animate` must be a list\");\n (Array.isArray(m.animate) ? m.animate : []).forEach((a, i) => {\n const where = `animate[${i}]`;\n if (!a || typeof a !== \"object\") return void issues.push(`${where} is not an animation`);\n if (typeof a.id !== \"string\") {\n if (a.view === undefined) return void issues.push(`${where} needs an \\`id\\``);\n } else if (!ids.has(a.id))\n issues.push(`${where}: there is no element with id \"${a.id}\" in the SVG`);\n if (a.step !== undefined && !(Number.isInteger(a.step) && a.step >= 0))\n issues.push(`${where}: \\`step\\` is a whole number, 0 or more`);\n if (a.ms !== undefined && !(a.ms >= 0)) issues.push(`${where}: \\`ms\\` must be 0 or more`);\n if (a.delay !== undefined && !(a.delay >= 0))\n issues.push(`${where}: \\`delay\\` must be 0 or more`);\n if (a.ease !== undefined && !(a.ease in EASES))\n issues.push(`${where}: \\`ease\\` is one of ${Object.keys(EASES).join(\", \")}`);\n if (!a.attrs && !a.draw && !a.count && a.write === undefined && a.view === undefined && !a.along && a.solo === undefined)\n issues.push(\n `${where} changes nothing: give \\`attrs\\`, \\`draw\\`, \\`count\\`, \\`write\\`, \\`view\\`, \\`along\\` or \\`solo\\``,\n );\n if (a.solo !== undefined && !(Number.isInteger(a.solo) && a.solo >= 0))\n issues.push(`${where}: \\`solo\\` is an index (whole number, 0 or more)`);\n if (a.view !== undefined) {\n const box = a.view as unknown;\n if (typeof box === \"string\") {\n if (!ids.has(box))\n issues.push(`${where}: \\`view\\` frames \"${box}\", which is not an id in the SVG`);\n } else if (\n !Array.isArray(box) ||\n box.length !== 4 ||\n !box.every((n) => Number.isFinite(n)) ||\n !(box[2] > 0 && box[3] > 0)\n )\n issues.push(\n `${where}: \\`view\\` is [x, y, width, height] (width and height above 0) or an element's id`,\n );\n }\n if (a.along !== undefined) {\n if (typeof a.along !== \"string\" || !ids.has(a.along))\n issues.push(`${where}: \\`along\\` is the id of a path in the SVG`);\n if (typeof a.id !== \"string\")\n issues.push(`${where}: \\`along\\` moves an element: give its \\`id\\``);\n }\n if (\n a.at !== undefined &&\n !(Array.isArray(a.at) && a.at.length === 2 && a.at.every((v) => v >= 0 && v <= 1))\n )\n issues.push(`${where}: \\`at\\` is [from, to] between 0 and 1`);\n for (const [name, pair] of Object.entries(a.attrs ?? {}))\n if (!Array.isArray(pair) || pair.length !== 2)\n issues.push(`${where}: \\`attrs.${name}\\` is [from, to]`);\n if (Array.isArray(a.draw) && a.draw.some((v) => !(v >= 0 && v <= 1)))\n issues.push(`${where}: \\`draw\\` is true or [from, to] between 0 and 1`);\n if (a.count !== undefined && !(Array.isArray(a.count) && a.count.length === 2))\n issues.push(`${where}: \\`count\\` is [from, to]`);\n if (a.write !== undefined && typeof a.write !== \"string\")\n issues.push(`${where}: \\`write\\` is the text to write`);\n if ((a.scramble !== undefined || a.caret !== undefined) && a.write === undefined)\n issues.push(`${where}: \\`scramble\\` and \\`caret\\` go with \\`write\\``);\n });\n return issues;\n}\n","/**\n * Drawing animated SVG pictures (see `SvgMotion`) in the DOM: a sanitising\n * parser, a view that applies frames, and a player that runs on a `Clock`.\n */\n\nimport { realClock, type Clock } from \"../time/clock.js\";\nimport {\n EASES,\n formatCount,\n STEP_MS,\n SVG_PICTURE,\n svgFrame,\n svgOpeningTag,\n type EaseName,\n type SvgBox,\n type SvgMotion,\n} from \"./motion.js\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\n/**\n * The `<svg>` in `markup`, parsed and made safe to show: no scripts, event\n * handlers, `<foreignObject>` or links outside itself. Undefined when it is\n * not an SVG.\n */\nexport function parseSvg(markup: string): SVGSVGElement | undefined {\n // Parsed as XML, an <svg> without its namespace is not an SVG at all.\n let source = markup.trim();\n const open = svgOpeningTag(source);\n if (!/\\sxmlns\\s*=/i.test(open)) source = source.replace(/^<svg/i, `<svg xmlns=\"${SVG_NS}\"`);\n if (/xlink:/.test(source) && !/\\sxmlns:xlink\\s*=/i.test(open))\n source = source.replace(/^<svg/i, '<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\"');\n const doc = new DOMParser().parseFromString(source, \"image/svg+xml\");\n const svg = doc.documentElement;\n if (svg.namespaceURI !== SVG_NS || svg.localName !== \"svg\" || doc.querySelector(\"parsererror\"))\n return undefined;\n for (const bad of svg.querySelectorAll(\"script, foreignObject\")) bad.remove();\n for (const el of [svg, ...svg.querySelectorAll(\"*\")]) {\n for (const attr of [...el.attributes]) {\n const name = attr.name.toLowerCase();\n const link = name === \"href\" || name === \"xlink:href\" || name === \"src\";\n if (name.startsWith(\"on\") || (link && !/^(#|data:image\\/)/i.test(attr.value.trim())))\n el.removeAttribute(attr.name);\n }\n }\n return document.importNode(svg, true) as unknown as SVGSVGElement;\n}\n\n/** Share of an element's size left round it when a `view` frames it. */\nconst VIEW_MARGIN = 0.12;\n\n/**\n * The box a `view` frames, in viewBox units: a box as given, an element's\n * box with a margin, widened to the picture's shape so the zoom does not\n * skew, or the whole picture for `null`.\n */\nfunction viewBox(view: SvgBox | string | null, whole: SvgBox, byId: Map<string, Element>): SvgBox {\n if (view === null) return whole;\n if (typeof view !== \"string\") return view;\n const el = byId.get(view) as SVGGraphicsElement | undefined;\n let box: DOMRect | undefined;\n try {\n box = el?.getBBox?.();\n } catch {\n // Not laid out (or not drawable): keep the whole picture.\n }\n if (!box || !(box.width > 0 || box.height > 0)) return whole;\n // In the picture's own units: through the element's transforms (a point\n // placed with one), which its box leaves out.\n try {\n const svg = el!.ownerSVGElement;\n const m = svg?.getScreenCTM()?.inverse().multiply(el!.getScreenCTM()!);\n if (m) {\n const corners = [\n [box.x, box.y],\n [box.x + box.width, box.y],\n [box.x, box.y + box.height],\n [box.x + box.width, box.y + box.height],\n ].map(([x, y]) => new DOMPoint(x, y).matrixTransform(m));\n const xs = corners.map((p) => p.x);\n const ys = corners.map((p) => p.y);\n box = new DOMRect(\n Math.min(...xs),\n Math.min(...ys),\n Math.max(...xs) - Math.min(...xs),\n Math.max(...ys) - Math.min(...ys),\n );\n }\n } catch {\n // Not laid out: keep its own box.\n }\n const aspect = whole[2] / whole[3];\n let w = box.width * (1 + 2 * VIEW_MARGIN);\n let h = box.height * (1 + 2 * VIEW_MARGIN);\n if (w / h > aspect) h = w / aspect;\n else w = h * aspect;\n return [box.x + box.width / 2 - w / 2, box.y + box.height / 2 - h / 2, w, h];\n}\n\n/**\n * Keeps the picture's `data-nonscaling` elements (a map's pins and labels)\n * the same size on screen as a `view` zooms: each is scaled round its own\n * origin by how far the picture is zoomed (so draw it round (0, 0) inside a\n * group that places it).\n */\nfunction keepSize(svg: SVGSVGElement, whole: SvgBox, nonscaling: Element[]) {\n if (!nonscaling.length) return;\n const width = Number((svg.getAttribute(\"viewBox\") ?? \"\").trim().split(/[\\s,]+/)[2]);\n const zoom = width > 0 ? width / whole[2] : 1;\n const transform = Math.abs(zoom - 1) < 1e-4 ? \"\" : `scale(${+zoom.toFixed(4)})`;\n for (const el of nonscaling)\n if ((el.getAttribute(\"transform\") ?? \"\") !== transform)\n if (transform) el.setAttribute(\"transform\", transform);\n else el.removeAttribute(\"transform\");\n}\n\n/** Applies a frame of the media's animations to `svg`. */\nfunction apply(\n svg: SVGSVGElement,\n whole: SvgBox,\n byId: Map<string, Element>,\n motion: SvgMotion,\n timeMs: number,\n steps: readonly number[],\n) {\n for (const [id, frame] of svgFrame(motion.animate ?? [], steps, timeMs)) {\n if (id === SVG_PICTURE && frame.view) {\n const a = viewBox(frame.view.from, whole, byId);\n const b = viewBox(frame.view.to, whole, byId);\n const u = frame.view.u;\n svg.setAttribute(\"viewBox\", a.map((n, i) => +(n + (b[i]! - n) * u).toFixed(2)).join(\" \"));\n continue;\n }\n const el = byId.get(id);\n if (!el) continue;\n for (const [name, value] of Object.entries(frame.attrs)) el.setAttribute(name, value);\n if (frame.draw !== undefined) {\n // A path length of 1 lets the dash be a fraction, with nothing measured.\n el.setAttribute(\"pathLength\", \"1\");\n el.setAttribute(\"stroke-dasharray\", \"1 1\");\n el.setAttribute(\"stroke-dashoffset\", String(1 - frame.draw));\n }\n if (frame.text !== undefined) el.textContent = frame.text;\n if (frame.solo !== undefined) {\n const children = Array.from(el.children);\n children.forEach((child, idx) => {\n if (child instanceof SVGElement || child instanceof HTMLElement) {\n if (idx === frame.solo) {\n child.removeAttribute(\"display\");\n } else {\n child.setAttribute(\"display\", \"none\");\n }\n }\n });\n }\n if (frame.along) {\n const path = byId.get(frame.along.path) as SVGGeometryElement | undefined;\n try {\n const length = path?.getTotalLength?.();\n if (path && length !== undefined) {\n const p = path.getPointAtLength(frame.along.at * length);\n el.setAttribute(\"transform\", `translate(${+p.x.toFixed(2)} ${+p.y.toFixed(2)})`);\n }\n } catch {\n // Not laid out: leave it where it is.\n }\n }\n }\n}\n\nconst VAR = /var\\(\\s*(--[\\w-]+)\\s*(?:,\\s*([^()]*(?:\\([^()]*\\)[^()]*)*))?\\)/g;\n\n/**\n * `copy` with the CSS variables in its attributes and styles replaced by\n * their values on `live` (its original, in the page), or their fallbacks:\n * an image of the SVG is drawn on its own, where the page's variables (a\n * host's palette) mean nothing.\n */\nfunction resolveVariables(live: SVGSVGElement, copy: SVGSVGElement) {\n const originals = [live, ...live.querySelectorAll(\"*\")];\n const copies = [copy, ...copy.querySelectorAll(\"*\")];\n copies.forEach((el, i) => {\n let style: CSSStyleDeclaration | undefined;\n for (const attr of [...el.attributes]) {\n if (!attr.value.includes(\"var(\")) continue;\n style ??= getComputedStyle(originals[i] ?? live);\n el.setAttribute(\n attr.name,\n attr.value.replace(\n VAR,\n (_, name: string, fallback?: string) =>\n style!.getPropertyValue(name).trim() || fallback?.trim() || \"\",\n ),\n );\n }\n });\n}\n\n/** An image of the picture as drawn now (its layers in order), at `pixels`. */\nasync function rasterize(layers: SVGSVGElement[], pixels: { width: number; height: number }): Promise<HTMLCanvasElement> {\n const canvas = document.createElement(\"canvas\");\n canvas.width = pixels.width;\n canvas.height = pixels.height;\n const defs = layers[0]?.querySelector(\"defs\");\n for (const [i, svg] of layers.entries()) {\n const copy = svg.cloneNode(true) as SVGSVGElement;\n resolveVariables(svg, copy);\n // A fixed layer's gradients and markers live in the picture's <defs>.\n if (i > 0 && defs) copy.prepend(defs.cloneNode(true));\n copy.setAttribute(\"xmlns\", SVG_NS);\n copy.setAttribute(\"width\", String(pixels.width));\n copy.setAttribute(\"height\", String(pixels.height));\n const url = URL.createObjectURL(\n new Blob([new XMLSerializer().serializeToString(copy)], { type: \"image/svg+xml\" }),\n );\n try {\n const image = new Image();\n image.src = url;\n await image.decode();\n canvas.getContext(\"2d\")!.drawImage(image, 0, 0, pixels.width, pixels.height);\n } finally {\n URL.revokeObjectURL(url);\n }\n }\n return canvas;\n}\n\n/**\n * Moves the picture's `data-fixed` elements (titles, legends, notes) into a\n * layer over it that keeps the original `viewBox`, so a `view` zooms the\n * picture under them while they stay put. Undefined when it has none.\n */\nfunction fixedLayer(svg: SVGSVGElement): SVGSVGElement | undefined {\n const fixed = [...svg.querySelectorAll(\"[data-fixed]\")].filter(\n (el) => !el.parentElement?.closest(\"[data-fixed]\"),\n );\n if (!fixed.length) return undefined;\n // The picture's own attributes (viewBox, fonts, fills) without its id.\n const layer = svg.cloneNode(false) as SVGSVGElement;\n layer.removeAttribute(\"id\");\n layer.setAttribute(\"aria-hidden\", \"true\");\n layer.style.cssText = \"position:absolute;inset:0;overflow:visible\";\n for (const el of fixed) layer.appendChild(el);\n return layer;\n}\n\nexport interface SvgViewOptions {\n /** Called with the id of the innermost element with an id that is clicked. */\n onPick?: (id: string) => void;\n}\n\n/** An {@link SvgMotion} drawn into an element; call `draw` with a time to show it then. */\nexport interface SvgView {\n /** A `div` filled by the picture; put it anywhere. */\n readonly element: HTMLDivElement;\n /** Shows the picture at `timeMs`, with steps starting at `steps` (ms). */\n draw(timeMs: number, steps?: readonly number[]): void;\n /** An image of the picture as drawn now. */\n capture(pixels: { width: number; height: number }): Promise<HTMLCanvasElement | undefined>;\n}\n\n/** Shows an {@link SvgMotion} as a live view; with `onPick`, clicks report the id of the element clicked. */\nexport function svgView(motion: SvgMotion, options: SvgViewOptions = {}): SvgView {\n const element = document.createElement(\"div\");\n element.style.width = \"100%\";\n element.style.height = \"100%\";\n const { onPick } = options;\n if (onPick)\n element.addEventListener(\"click\", (event) => {\n // The innermost element with an id, up to the picture itself.\n for (let el = event.target as Element | null; el && el !== element; el = el.parentElement)\n if (el.id && el.localName !== \"svg\") return onPick(el.id);\n });\n let svg: SVGSVGElement | undefined | null = null;\n let fixed: SVGSVGElement | undefined;\n let nonscaling: Element[] = [];\n let whole: SvgBox = [0, 0, 100, 100];\n const byId = new Map<string, Element>();\n return {\n element,\n draw(timeMs, steps = []) {\n if (svg === null) {\n // Parsed on first use, so a view made and dropped costs nothing.\n svg = parseSvg(motion.svg);\n if (svg) {\n svg.setAttribute(\"width\", \"100%\");\n svg.setAttribute(\"height\", \"100%\");\n element.appendChild(svg);\n fixed = fixedLayer(svg);\n if (fixed) {\n element.style.position = \"relative\";\n element.appendChild(fixed);\n }\n for (const layer of [svg, fixed])\n for (const el of layer?.querySelectorAll(\"[id]\") ?? []) byId.set(el.id, el);\n nonscaling = [...svg.querySelectorAll(\"[data-nonscaling]\")];\n const box = (svg.getAttribute(\"viewBox\") ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .map(Number);\n if (box.length === 4 && box.every(Number.isFinite)) whole = box as unknown as SvgBox;\n }\n }\n if (svg) {\n apply(svg, whole, byId, motion, timeMs, steps);\n keepSize(svg, whole, nonscaling);\n }\n },\n capture: async (pixels) => (svg ? rasterize(fixed ? [svg, fixed] : [svg], pixels) : undefined),\n };\n}\n\n/** How long an {@link SvgMotion} runs: its last change's end, in ms (at least 1). */\nexport function svgDuration(motion: SvgMotion, steps: readonly number[] = []): number {\n let end = 0;\n for (const a of motion.animate ?? []) {\n const step = a.step ?? 0;\n const start = step <= 0 ? 0 : steps[step - 1];\n if (start === undefined) continue;\n end = Math.max(end, start + (a.delay ?? 0) + (a.ms ?? 800));\n }\n return Math.max(1, end);\n}\n\nexport interface SvgPlayerOptions extends SvgViewOptions {\n /** Time source (default: animation frames). Pass a `ManualClock` to step frames yourself. */\n clock?: Clock;\n /** Start playing at once (default `true`). */\n autoplay?: boolean;\n /**\n * Start again at the end (default `true`). When `false` it plays once and\n * stays on the last frame (hidden instead with `hideWhenPaused`).\n */\n loop?: boolean;\n /** Loop length in ms (default: the picture's own, see {@link svgDuration}, plus `hold`). */\n duration?: number;\n /** Ms to rest on the last frame before looping (default 600). */\n hold?: number;\n /** Playback speed (default 1). */\n speed?: number;\n /** Step start times in ms, for pictures that change on steps (default none). */\n steps?: readonly number[];\n /** Pause the animation loop when scrolled out of view via IntersectionObserver (default false). */\n pauseWhenHidden?: boolean;\n /** Wait to start playing until the element enters the viewport via IntersectionObserver (default false). When true, autoplay is deferred until first visible. */\n playOnEnter?: boolean;\n /** Hide the view (`visibility: hidden`) whenever playback is paused or reset (default false). */\n hideWhenPaused?: boolean;\n /**\n * Plays in answer to the pointer instead of on its own (see {@link SvgTrigger}):\n * `\"hover\"` and `\"press\"` play to the end while hovered or held and back to\n * the start after; `\"click\"` plays the next step on each click, starting\n * over after the last. `autoplay`, `loop` and `steps` are ignored.\n */\n trigger?: SvgTrigger;\n /** The element whose pointer events trigger it (default the container). */\n triggerTarget?: HTMLElement;\n}\n\n/** What makes a triggered {@link SvgPlayer} play. */\nexport type SvgTrigger = \"hover\" | \"press\" | \"click\";\n\n/**\n * Plays an {@link SvgMotion} inside `container`: loops by default, and can be\n * paused, sought and sped up. Dispose it to stop and remove the picture.\n */\nexport class SvgPlayer {\n readonly view: SvgView;\n private readonly clock: Clock;\n private steps: readonly number[];\n private readonly loop: boolean;\n private length: number;\n private readonly motion: SvgMotion;\n private readonly trigger?: SvgTrigger;\n private readonly triggerTarget?: HTMLElement;\n /** With a hover or press trigger: 1 playing to the end, -1 back to the start. */\n private direction = 1;\n /** With a click trigger: when each click came, in ms of playback. */\n private clicks: number[] = [];\n private readonly hideWhenPaused: boolean;\n private speed: number;\n private time = 0;\n private last = 0;\n private playing = false;\n private disposed = false;\n private wasPlayingBeforeHidden = false;\n private hasEnteredViewport = false;\n private intersectionObserver?: IntersectionObserver;\n\n constructor(\n private readonly container: HTMLElement,\n motion: SvgMotion,\n options: SvgPlayerOptions = {},\n ) {\n this.clock = options.clock ?? realClock;\n this.motion = motion;\n this.trigger = options.trigger;\n this.steps = this.trigger ? [] : (options.steps ?? []);\n this.loop = this.trigger ? false : (options.loop ?? true);\n this.speed = options.speed ?? 1;\n this.hideWhenPaused = Boolean(options.hideWhenPaused);\n this.length = options.duration ?? svgDuration(motion, this.steps) + (options.hold ?? 600);\n this.view = svgView(motion, options);\n container.appendChild(this.view.element);\n this.view.draw(0, this.steps);\n\n const needsObserver =\n (options.pauseWhenHidden || options.playOnEnter) && typeof IntersectionObserver !== \"undefined\";\n\n if (needsObserver) {\n this.intersectionObserver = new IntersectionObserver(([entry]) => {\n if (!entry) return;\n if (entry.isIntersecting) {\n if (options.playOnEnter && !this.hasEnteredViewport) {\n this.hasEnteredViewport = true;\n if (options.autoplay ?? true) {\n this.play();\n }\n } else if (this.wasPlayingBeforeHidden) {\n this.wasPlayingBeforeHidden = false;\n this.play();\n }\n } else {\n if (this.playing && options.pauseWhenHidden) {\n this.wasPlayingBeforeHidden = true;\n this.pause();\n }\n }\n });\n this.intersectionObserver.observe(this.view.element);\n }\n\n if (this.trigger) {\n // A click-triggered picture runs until its latest step is over, however many clicks.\n // Hovers and presses run to the last change and back, with no rest to wait through.\n this.length = this.trigger === \"click\" ? Infinity : (options.duration ?? svgDuration(motion));\n this.triggerTarget = options.triggerTarget ?? container;\n this.listen(true);\n return;\n }\n\n const shouldAutoplay = (options.autoplay ?? true) && !options.playOnEnter;\n if (shouldAutoplay) {\n this.play();\n } else if (this.hideWhenPaused) {\n this.view.element.style.visibility = \"hidden\";\n }\n }\n\n /** The loop length in ms. */\n get duration(): number {\n return this.length;\n }\n\n /** The time shown, in ms. */\n get currentTime(): number {\n return this.time;\n }\n\n get isPlaying(): boolean {\n return this.playing;\n }\n\n play(): void {\n if (this.playing || this.disposed) return;\n this.playing = true;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"visible\";\n this.last = this.clock.now();\n void this.run();\n }\n\n pause(): void {\n this.playing = false;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n }\n\n /** Stops playback, rewinds to `0 ms`, and hides the view if `hideWhenPaused` is enabled. */\n reset(): void {\n this.playing = false;\n this.wasPlayingBeforeHidden = false;\n this.seek(0);\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n }\n\n /** Shows the picture at `ms` (wrapped into the loop when looping). */\n seek(ms: number): void {\n this.time = this.loop\n ? ((ms % this.length) + this.length) % this.length\n : Math.min(Math.max(ms, 0), this.length);\n this.view.draw(this.time, this.steps);\n }\n\n setSpeed(speed: number): void {\n this.speed = speed;\n }\n\n /**\n * Plays to the end (`true`) or back to the start (`false`) from wherever it\n * is: what a hover or press trigger does, for hosts that drive it themselves\n * (a hovered card, a focused button).\n */\n setActive(active: boolean): void {\n this.direction = active ? 1 : -1;\n this.play();\n }\n\n /**\n * Plays the picture's next step (what a click trigger does): steps 1, 2, …\n * in turn, then step 1 again, so a picture whose last step leads back to\n * how it started toggles; one with no steps plays again from the start.\n */\n advance(): void {\n const last = Math.max(0, ...(this.motion.animate ?? []).map((a) => a.step ?? 0));\n if (last === 0) {\n this.clicks = [];\n this.time = 0;\n } else {\n this.clicks.push(this.time);\n // Only the clicks of the current round count, so each round starts from how the last ended.\n this.steps = this.clicks.slice(Math.floor((this.clicks.length - 1) / last) * last);\n }\n this.view.draw(this.time, this.steps);\n this.play();\n }\n\n dispose(): void {\n this.disposed = true;\n this.playing = false;\n if (this.trigger) this.listen(false);\n this.intersectionObserver?.disconnect();\n this.view.element.remove();\n }\n\n private onEnter = () => this.setActive(true);\n private onLeave = () => this.setActive(false);\n private onClick = () => this.advance();\n\n private listen(on: boolean) {\n const target = this.triggerTarget!;\n const method = on ? \"addEventListener\" : \"removeEventListener\";\n if (this.trigger === \"hover\") {\n target[method](\"pointerenter\", this.onEnter);\n target[method](\"pointerleave\", this.onLeave);\n } else if (this.trigger === \"press\") {\n target[method](\"pointerdown\", this.onEnter);\n window[method](\"pointerup\", this.onLeave);\n window[method](\"pointercancel\", this.onLeave);\n } else target[method](\"click\", this.onClick);\n }\n\n private async run(): Promise<void> {\n while (this.playing && !this.disposed) {\n await this.clock.frame();\n if (!this.playing || this.disposed) return;\n const now = this.clock.now();\n const dt = (now - this.last) * this.speed;\n this.last = now;\n if (this.trigger === \"click\") {\n // Rest once the latest step is over, until the next click.\n const end = svgDuration(this.motion, this.steps);\n this.time = Math.min(this.time + dt, Math.max(end, this.time));\n this.view.draw(this.time, this.steps);\n if (this.time >= end) this.playing = false;\n continue;\n }\n if (this.trigger) {\n const next = Math.min(Math.max(this.time + dt * this.direction, 0), this.length);\n this.seek(next);\n if (next === 0 || next === this.length) this.playing = false;\n continue;\n }\n const next = this.time + dt;\n if (!this.loop && next >= this.length) {\n this.seek(this.length);\n this.playing = false;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n return;\n }\n this.seek(next);\n }\n }\n}\n\nexport interface AnimateCountOptions {\n /** Starting number. */\n from: number;\n /** Ending number. */\n to: number;\n /** Duration in ms (default {@link STEP_MS}). */\n ms?: number;\n /** Delay before starting in ms (default 0). */\n delay?: number;\n /** Easing curve (default `\"out\"`). */\n ease?: EaseName;\n /** Number of decimal places (default 0). */\n decimals?: number;\n /** Format template where `{n}` is replaced by the formatted number (default `\"{n}\"`). */\n format?: string;\n /** Time source (default: animation frames). */\n clock?: Clock;\n /** Called with each formatted text frame (and raw numeric value). */\n onUpdate: (text: string, value: number) => void;\n}\n\n/**\n * Counts a number from `from` to `to` over `ms` using Flair's easing curves\n * and `Clock`, calling `onUpdate` with the formatted string on each frame.\n * Returns a cancel function that stops the animation.\n */\nexport function animateCount(options: AnimateCountOptions): () => void {\n const clock = options.clock ?? realClock;\n const ms = options.ms ?? STEP_MS;\n const delay = options.delay ?? 0;\n const ease = EASES[options.ease ?? \"out\"];\n const start = clock.now();\n let stopped = false;\n\n const emit = (u: number) => {\n const v = options.from + (options.to - options.from) * ease(u);\n options.onUpdate(formatCount(v, options), v);\n };\n\n emit(ms <= 0 && delay <= 0 ? 1 : 0);\n if (ms <= 0 && delay <= 0) return () => {};\n\n void (async () => {\n while (!stopped) {\n await clock.frame();\n if (stopped) return;\n const elapsed = clock.now() - start - delay;\n if (elapsed < 0) continue;\n if (ms <= 0 || elapsed >= ms) {\n emit(1);\n return;\n }\n emit(elapsed / ms);\n }\n })();\n\n return () => {\n stopped = true;\n };\n}\n"],"mappings":"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","names":["next"]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SVG_PICTURE,
|
|
3
|
+
svgDuration,
|
|
4
|
+
svgFrame
|
|
5
|
+
} from "./chunk-4RGHZWQC.js";
|
|
6
|
+
|
|
7
|
+
// src/vector/static.ts
|
|
8
|
+
var DROPPED = /* @__PURE__ */ new Set(["script", "foreignobject", "iframe", "object", "embed"]);
|
|
9
|
+
var ATTR = /([^\s=/>"']+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>"']+)))?/g;
|
|
10
|
+
var TOKEN = /<!--[\s\S]*?(?:-->|$)|<!\[CDATA\[([\s\S]*?)(?:\]\]>|$)|<[!?][^>]*>|<\/\s*([\w:.-]+)\s*>|<([\w:.-]+)((?:\s+[^\s=/>"']+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>"']+))?)*)\s*(\/?)>/g;
|
|
11
|
+
var ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: "\xA0" };
|
|
12
|
+
var decode = (s) => s.replace(/&(#x[0-9a-f]+|#\d+|\w+);/gi, (match, e) => {
|
|
13
|
+
if (e[0] !== "#") return ENTITIES[e.toLowerCase()] ?? match;
|
|
14
|
+
const code = e[1] === "x" || e[1] === "X" ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10);
|
|
15
|
+
return code > 0 && code <= 1114111 ? String.fromCodePoint(code) : "";
|
|
16
|
+
});
|
|
17
|
+
var escapeText = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
18
|
+
var escapeAttr = (s) => escapeText(s).replace(/"/g, """);
|
|
19
|
+
function safeAttr(name, value) {
|
|
20
|
+
const n = name.toLowerCase();
|
|
21
|
+
if (n.startsWith("on") || !/^[a-z_][\w:.-]*$/i.test(n)) return false;
|
|
22
|
+
if (n === "href" || n === "xlink:href" || n === "src") return /^(#|data:image\/)/i.test(value.trim());
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
function parse(markup) {
|
|
26
|
+
const root = { tag: "", attrs: [], children: [] };
|
|
27
|
+
const stack = [root];
|
|
28
|
+
let dropping = 0;
|
|
29
|
+
let at = 0;
|
|
30
|
+
const text = (s) => {
|
|
31
|
+
if (!dropping && s) stack[stack.length - 1].children.push(decode(s));
|
|
32
|
+
};
|
|
33
|
+
for (const m of markup.matchAll(TOKEN)) {
|
|
34
|
+
text(markup.slice(at, m.index));
|
|
35
|
+
at = m.index + m[0].length;
|
|
36
|
+
const [, cdata, close, open, attrs = "", selfClosing] = m;
|
|
37
|
+
if (cdata !== void 0) {
|
|
38
|
+
if (!dropping) stack[stack.length - 1].children.push(cdata);
|
|
39
|
+
} else if (close) {
|
|
40
|
+
const tag = close.toLowerCase();
|
|
41
|
+
if (dropping) {
|
|
42
|
+
if (DROPPED.has(tag)) dropping--;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
let i = stack.length - 1;
|
|
46
|
+
while (i > 0 && stack[i].tag.toLowerCase() !== tag) i--;
|
|
47
|
+
if (i > 0) stack.length = i;
|
|
48
|
+
} else if (open) {
|
|
49
|
+
if (DROPPED.has(open.toLowerCase())) {
|
|
50
|
+
if (!selfClosing) dropping++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (dropping) continue;
|
|
54
|
+
const node = { tag: open, attrs: [], children: [] };
|
|
55
|
+
for (const a of attrs.matchAll(ATTR)) {
|
|
56
|
+
const value = decode(a[2] ?? a[3] ?? a[4] ?? "");
|
|
57
|
+
if (safeAttr(a[1], value)) node.attrs.push([a[1], value]);
|
|
58
|
+
}
|
|
59
|
+
stack[stack.length - 1].children.push(node);
|
|
60
|
+
if (!selfClosing) stack.push(node);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
text(markup.slice(at));
|
|
64
|
+
const svg = root.children.find((c) => typeof c !== "string");
|
|
65
|
+
return svg && svg.tag.toLowerCase() === "svg" ? svg : void 0;
|
|
66
|
+
}
|
|
67
|
+
function serialize(node) {
|
|
68
|
+
if (typeof node === "string") return escapeText(node);
|
|
69
|
+
const attrs = node.attrs.map(([k, v]) => ` ${k}="${escapeAttr(v)}"`).join("");
|
|
70
|
+
return `<${node.tag}${attrs}>${node.children.map(serialize).join("")}</${node.tag}>`;
|
|
71
|
+
}
|
|
72
|
+
function setAttr(node, name, value) {
|
|
73
|
+
const i = node.attrs.findIndex(([k]) => k === name);
|
|
74
|
+
if (value === void 0) {
|
|
75
|
+
if (i >= 0) node.attrs.splice(i, 1);
|
|
76
|
+
} else if (!safeAttr(name, value)) return;
|
|
77
|
+
else if (i >= 0) node.attrs[i][1] = value;
|
|
78
|
+
else node.attrs.push([name, value]);
|
|
79
|
+
}
|
|
80
|
+
var getAttr = (node, name) => node.attrs.find(([k]) => k === name)?.[1];
|
|
81
|
+
function parseViewBox(value) {
|
|
82
|
+
const box = (value ?? "").trim().split(/[\s,]+/).map(Number);
|
|
83
|
+
return box.length === 4 && box.every(Number.isFinite) && box[2] > 0 && box[3] > 0 ? box : void 0;
|
|
84
|
+
}
|
|
85
|
+
function svgViewBox(markup) {
|
|
86
|
+
const open = /<svg\b[^>]*>/i.exec(markup)?.[0] ?? "";
|
|
87
|
+
return parseViewBox(/\sviewBox\s*=\s*(?:"([^"]*)"|'([^']*)')/i.exec(open)?.slice(1).find((v) => v !== void 0));
|
|
88
|
+
}
|
|
89
|
+
function svgFrameMarkup(motion, options = {}) {
|
|
90
|
+
const svg = parse(motion.svg.trim());
|
|
91
|
+
if (!svg) return void 0;
|
|
92
|
+
const steps = options.steps ?? [];
|
|
93
|
+
const at = options.at ?? "last";
|
|
94
|
+
const time = at === "first" ? 0 : at === "last" ? svgDuration(motion, steps) : at;
|
|
95
|
+
const byId = /* @__PURE__ */ new Map();
|
|
96
|
+
const index = (node) => {
|
|
97
|
+
const id = getAttr(node, "id");
|
|
98
|
+
if (id && !byId.has(id)) byId.set(id, node);
|
|
99
|
+
for (const c of node.children) if (typeof c !== "string") index(c);
|
|
100
|
+
};
|
|
101
|
+
index(svg);
|
|
102
|
+
const whole = parseViewBox(getAttr(svg, "viewBox"));
|
|
103
|
+
for (const [id, frame] of svgFrame(motion.animate ?? [], steps, time)) {
|
|
104
|
+
if (id === SVG_PICTURE && frame.view) {
|
|
105
|
+
const { from, to, u } = frame.view;
|
|
106
|
+
const a = from === null ? whole : typeof from === "string" ? void 0 : from;
|
|
107
|
+
const b = typeof to === "string" ? void 0 : to;
|
|
108
|
+
if (a && b) setAttr(svg, "viewBox", a.map((n, i) => +(n + (b[i] - n) * u).toFixed(2)).join(" "));
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const el = byId.get(id);
|
|
112
|
+
if (!el) continue;
|
|
113
|
+
for (const [name, value] of Object.entries(frame.attrs)) setAttr(el, name, value);
|
|
114
|
+
if (frame.draw !== void 0) {
|
|
115
|
+
setAttr(el, "pathLength", "1");
|
|
116
|
+
setAttr(el, "stroke-dasharray", "1 1");
|
|
117
|
+
setAttr(el, "stroke-dashoffset", String(1 - frame.draw));
|
|
118
|
+
}
|
|
119
|
+
if (frame.text !== void 0) el.children = [frame.text];
|
|
120
|
+
if (frame.solo !== void 0) {
|
|
121
|
+
let i = 0;
|
|
122
|
+
for (const child of el.children)
|
|
123
|
+
if (typeof child !== "string") setAttr(child, "display", i++ === frame.solo ? void 0 : "none");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
setAttr(svg, "width", "100%");
|
|
127
|
+
setAttr(svg, "height", "100%");
|
|
128
|
+
if (!getAttr(svg, "xmlns")) setAttr(svg, "xmlns", "http://www.w3.org/2000/svg");
|
|
129
|
+
return serialize(svg);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
parseViewBox,
|
|
134
|
+
svgViewBox,
|
|
135
|
+
svgFrameMarkup
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=chunk-DVVQRZJ5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vector/static.ts"],"sourcesContent":["/**\n * An {@link SvgMotion}'s frame as markup, with no DOM: for server rendering\n * (a still that hydrates without a blank flash), reduced motion and other\n * places a live player is not wanted.\n */\n\nimport { SVG_PICTURE, svgFrame, type SvgBox, type SvgMotion } from \"./motion.js\";\nimport { svgDuration } from \"./svg.js\";\n\ninterface Node {\n tag: string;\n attrs: [string, string][];\n children: (Node | string)[];\n}\n\n/** Elements dropped with all they hold: they run code or show outside markup. */\nconst DROPPED = new Set([\"script\", \"foreignobject\", \"iframe\", \"object\", \"embed\"]);\nconst ATTR = /([^\\s=/>\"']+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s>\"']+)))?/g;\nconst TOKEN = /<!--[\\s\\S]*?(?:-->|$)|<!\\[CDATA\\[([\\s\\S]*?)(?:\\]\\]>|$)|<[!?][^>]*>|<\\/\\s*([\\w:.-]+)\\s*>|<([\\w:.-]+)((?:\\s+[^\\s=/>\"']+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>\"']+))?)*)\\s*(\\/?)>/g;\n\nconst ENTITIES: Record<string, string> = { amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\", nbsp: \" \" };\nconst decode = (s: string) =>\n s.replace(/&(#x[0-9a-f]+|#\\d+|\\w+);/gi, (match, e: string) => {\n if (e[0] !== \"#\") return ENTITIES[e.toLowerCase()] ?? match;\n const code = e[1] === \"x\" || e[1] === \"X\" ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10);\n return code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : \"\";\n });\nconst escapeText = (s: string) => s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\nconst escapeAttr = (s: string) => escapeText(s).replace(/\"/g, \""\");\n\n/** Safe as `parseSvg` keeps it: no handlers, and links only within the picture or to data images. */\nfunction safeAttr(name: string, value: string): boolean {\n const n = name.toLowerCase();\n if (n.startsWith(\"on\") || !/^[a-z_][\\w:.-]*$/i.test(n)) return false;\n if (n === \"href\" || n === \"xlink:href\" || n === \"src\") return /^(#|data:image\\/)/i.test(value.trim());\n return true;\n}\n\n/** The `<svg>` in `markup` as a tree, without anything `parseSvg` would remove. */\nfunction parse(markup: string): Node | undefined {\n const root: Node = { tag: \"\", attrs: [], children: [] };\n const stack = [root];\n let dropping = 0;\n let at = 0;\n const text = (s: string) => {\n if (!dropping && s) stack[stack.length - 1]!.children.push(decode(s));\n };\n for (const m of markup.matchAll(TOKEN)) {\n text(markup.slice(at, m.index));\n at = m.index + m[0].length;\n const [, cdata, close, open, attrs = \"\", selfClosing] = m;\n if (cdata !== undefined) {\n if (!dropping) stack[stack.length - 1]!.children.push(cdata);\n } else if (close) {\n const tag = close.toLowerCase();\n if (dropping) {\n if (DROPPED.has(tag)) dropping--;\n continue;\n }\n // Close up to the matching element; a stray closing tag is ignored.\n let i = stack.length - 1;\n while (i > 0 && stack[i]!.tag.toLowerCase() !== tag) i--;\n if (i > 0) stack.length = i;\n } else if (open) {\n if (DROPPED.has(open.toLowerCase())) {\n if (!selfClosing) dropping++;\n continue;\n }\n if (dropping) continue;\n const node: Node = { tag: open, attrs: [], children: [] };\n for (const a of attrs.matchAll(ATTR)) {\n const value = decode(a[2] ?? a[3] ?? a[4] ?? \"\");\n if (safeAttr(a[1]!, value)) node.attrs.push([a[1]!, value]);\n }\n stack[stack.length - 1]!.children.push(node);\n if (!selfClosing) stack.push(node);\n }\n }\n text(markup.slice(at));\n const svg = root.children.find((c): c is Node => typeof c !== \"string\");\n return svg && svg.tag.toLowerCase() === \"svg\" ? svg : undefined;\n}\n\nfunction serialize(node: Node | string): string {\n if (typeof node === \"string\") return escapeText(node);\n const attrs = node.attrs.map(([k, v]) => ` ${k}=\"${escapeAttr(v)}\"`).join(\"\");\n return `<${node.tag}${attrs}>${node.children.map(serialize).join(\"\")}</${node.tag}>`;\n}\n\nfunction setAttr(node: Node, name: string, value: string | undefined) {\n const i = node.attrs.findIndex(([k]) => k === name);\n if (value === undefined) {\n if (i >= 0) node.attrs.splice(i, 1);\n } else if (!safeAttr(name, value)) return;\n else if (i >= 0) node.attrs[i]![1] = value;\n else node.attrs.push([name, value]);\n}\n\nconst getAttr = (node: Node, name: string) => node.attrs.find(([k]) => k === name)?.[1];\n\n/** A `viewBox` attribute's box, or undefined when it has none or it is malformed. */\nexport function parseViewBox(value: string | null | undefined): SvgBox | undefined {\n const box = (value ?? \"\").trim().split(/[\\s,]+/).map(Number);\n return box.length === 4 && box.every(Number.isFinite) && box[2]! > 0 && box[3]! > 0\n ? (box as unknown as SvgBox)\n : undefined;\n}\n\n/** The `viewBox` on an SVG's opening tag (see {@link parseViewBox}). */\nexport function svgViewBox(markup: string): SvgBox | undefined {\n const open = /<svg\\b[^>]*>/i.exec(markup)?.[0] ?? \"\";\n return parseViewBox(/\\sviewBox\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/i.exec(open)?.slice(1).find((v) => v !== undefined));\n}\n\nexport interface SvgFrameMarkupOptions {\n /**\n * The time shown, in ms: a number, `\"first\"` (0) or `\"last\"` (the end of\n * its last change, see `svgDuration`; the default).\n */\n at?: number | \"first\" | \"last\";\n /** Step start times in ms, as a player's `steps` (default none). */\n steps?: readonly number[];\n}\n\n/**\n * The picture of an {@link SvgMotion} at a moment, as `<svg>` markup made safe\n * the way `parseSvg` makes it (no scripts, handlers, `<foreignObject>` or\n * outside links), with no DOM needed: render it on a server, show it under\n * `prefers-reduced-motion`, or anywhere a still will do. It is sized to fill\n * its container, as `SvgPlayer` draws it.\n *\n * It matches the player's frame except where that needs layout: a change\n * `along` a path, and a `view` that frames an element by id, stay where the\n * markup puts them. Undefined when `motion.svg` is not an `<svg>`.\n */\nexport function svgFrameMarkup(motion: SvgMotion, options: SvgFrameMarkupOptions = {}): string | undefined {\n const svg = parse(motion.svg.trim());\n if (!svg) return undefined;\n const steps = options.steps ?? [];\n const at = options.at ?? \"last\";\n const time = at === \"first\" ? 0 : at === \"last\" ? svgDuration(motion, steps) : at;\n const byId = new Map<string, Node>();\n const index = (node: Node) => {\n const id = getAttr(node, \"id\");\n if (id && !byId.has(id)) byId.set(id, node);\n for (const c of node.children) if (typeof c !== \"string\") index(c);\n };\n index(svg);\n const whole = parseViewBox(getAttr(svg, \"viewBox\"));\n for (const [id, frame] of svgFrame(motion.animate ?? [], steps, time)) {\n if (id === SVG_PICTURE && frame.view) {\n const { from, to, u } = frame.view;\n const a = from === null ? whole : typeof from === \"string\" ? undefined : from;\n const b = typeof to === \"string\" ? undefined : to;\n if (a && b) setAttr(svg, \"viewBox\", a.map((n, i) => +(n + (b[i]! - n) * u).toFixed(2)).join(\" \"));\n continue;\n }\n const el = byId.get(id);\n if (!el) continue;\n for (const [name, value] of Object.entries(frame.attrs)) setAttr(el, name, value);\n if (frame.draw !== undefined) {\n setAttr(el, \"pathLength\", \"1\");\n setAttr(el, \"stroke-dasharray\", \"1 1\");\n setAttr(el, \"stroke-dashoffset\", String(1 - frame.draw));\n }\n if (frame.text !== undefined) el.children = [frame.text];\n if (frame.solo !== undefined) {\n let i = 0;\n for (const child of el.children)\n if (typeof child !== \"string\") setAttr(child, \"display\", i++ === frame.solo ? undefined : \"none\");\n }\n }\n setAttr(svg, \"width\", \"100%\");\n setAttr(svg, \"height\", \"100%\");\n if (!getAttr(svg, \"xmlns\")) setAttr(svg, \"xmlns\", \"http://www.w3.org/2000/svg\");\n return serialize(svg);\n}\n"],"mappings":"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","names":[]}
|