@pixerate/flair 0.27.0 → 0.29.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +52 -10
  3. package/dist/catalog/index.d.ts +11 -6
  4. package/dist/catalog/index.js +1 -1
  5. package/dist/{chunk-DBNSI4MF.js → chunk-6GFOLHCT.js} +952 -35
  6. package/dist/chunk-6GFOLHCT.js.map +1 -0
  7. package/dist/{chunk-JL6TI6II.js → chunk-7KYQ4R76.js} +2 -2
  8. package/dist/chunk-7KYQ4R76.js.map +1 -0
  9. package/dist/{chunk-STKEKXCK.js → chunk-AUH3PEFW.js} +2 -2
  10. package/dist/{chunk-PFGGPIMT.js → chunk-DA4UUSDO.js} +58 -3
  11. package/dist/chunk-DA4UUSDO.js.map +1 -0
  12. package/dist/{chunk-RDU63CDO.js → chunk-HDYBY3PQ.js} +380 -1
  13. package/dist/chunk-HDYBY3PQ.js.map +1 -0
  14. package/dist/{chunk-FJ5ZJGOL.js → chunk-KZYZX3V5.js} +260 -33
  15. package/dist/chunk-KZYZX3V5.js.map +1 -0
  16. package/dist/{chunk-PFUTUR7T.js → chunk-MBKNTXQX.js} +9 -9
  17. package/dist/chunk-MBKNTXQX.js.map +1 -0
  18. package/dist/{chunk-H6VDFBIO.js → chunk-SMR2MJXB.js} +156 -5
  19. package/dist/chunk-SMR2MJXB.js.map +1 -0
  20. package/dist/{chunk-YBGRXVAE.js → chunk-X3C2VFTM.js} +3 -3
  21. package/dist/{chunk-CK6EV37I.js → chunk-ZULFHADW.js} +69 -6
  22. package/dist/chunk-ZULFHADW.js.map +1 -0
  23. package/dist/cli.js +6 -6
  24. package/dist/editor/index.d.ts +3 -3
  25. package/dist/editor/index.js +3 -3
  26. package/dist/{effect-M5T4ICIP.js → effect-YBWDNIYG.js} +10 -10
  27. package/dist/embed/index.d.ts +36 -8
  28. package/dist/embed/index.js +5 -5
  29. package/dist/{frames-VEcqa40g.d.ts → frames-CGJudAbu.d.ts} +11 -1
  30. package/dist/index.d.ts +69 -6
  31. package/dist/index.js +35 -7
  32. package/dist/index.js.map +1 -1
  33. package/dist/node.d.ts +4 -4
  34. package/dist/node.js +9 -9
  35. package/dist/react/index.d.ts +4 -4
  36. package/dist/react/index.js +7 -7
  37. package/dist/react/index.js.map +1 -1
  38. package/dist/{recorder-CDPPTJMG.js → recorder-TYMEZHOT.js} +3 -3
  39. package/dist/{renderer-teaa98RE.d.ts → renderer-tk1nbVkW.d.ts} +252 -2
  40. package/dist/svelte/index.d.ts +4 -4
  41. package/dist/svelte/index.js +7 -7
  42. package/dist/svelte/index.js.map +1 -1
  43. package/dist/{svg-BhRboN9q.d.ts → svg-DaKRPA9v.d.ts} +39 -7
  44. package/dist/{types-DUOMxZaq.d.ts → types-wh28fXW4.d.ts} +2 -2
  45. package/dist/vector/index.d.ts +2 -2
  46. package/dist/vector/index.js +5 -3
  47. package/package.json +1 -1
  48. package/dist/chunk-CK6EV37I.js.map +0 -1
  49. package/dist/chunk-DBNSI4MF.js.map +0 -1
  50. package/dist/chunk-FJ5ZJGOL.js.map +0 -1
  51. package/dist/chunk-H6VDFBIO.js.map +0 -1
  52. package/dist/chunk-JL6TI6II.js.map +0 -1
  53. package/dist/chunk-PFGGPIMT.js.map +0 -1
  54. package/dist/chunk-PFUTUR7T.js.map +0 -1
  55. package/dist/chunk-RDU63CDO.js.map +0 -1
  56. /package/dist/{chunk-STKEKXCK.js.map → chunk-AUH3PEFW.js.map} +0 -0
  57. /package/dist/{chunk-YBGRXVAE.js.map → chunk-X3C2VFTM.js.map} +0 -0
  58. /package/dist/{effect-M5T4ICIP.js.map → effect-YBWDNIYG.js.map} +0 -0
  59. /package/dist/{recorder-CDPPTJMG.js.map → recorder-TYMEZHOT.js.map} +0 -0
@@ -1 +0,0 @@
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 (/<(animate|animateTransform|animateMotion|set)[\\s/>]/i.test(svg))\n issues.push(\n \"`svg` cannot animate itself (<animate>, <animateTransform>, <animateMotion>, <set>): give changes in `animate`, so every frame is a function of time\",\n );\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/** Live views made so far, for ids unique on the page (see {@link scopeIds}). */\nlet views = 0;\n\nconst URL_REF = /url\\(\\s*(['\"]?)#([^'\")\\s]+)\\1\\s*\\)/g;\n\n/**\n * Gives the ids a live picture refers to (by `url(#…)` in attributes and\n * styles, or `href=\"#…\"`: its gradients, filters, clips, masks, `<use>`d\n * shapes) `suffix`, and points the references at the new ids, so two\n * pictures on one page that both define `id=\"glow\"` each use their own.\n * Other ids stay as written. The original id is kept in `data-flair-id`;\n * returns how ids map.\n */\nfunction scopeIds(svg: Element, suffix: string): Map<string, string> {\n const all = [svg, ...svg.querySelectorAll(\"*\")];\n const referred = new Set<string>();\n for (const el of all) {\n const texts = [...el.attributes].map((a) => [a.name.toLowerCase(), a.value] as const);\n if (el.localName === \"style\") texts.push([\"\", el.textContent ?? \"\"]);\n for (const [name, value] of texts) {\n if ((name === \"href\" || name === \"xlink:href\") && value.startsWith(\"#\")) referred.add(value.slice(1));\n for (const m of value.matchAll(URL_REF)) referred.add(m[2]!);\n }\n }\n const ids = new Map<string, string>();\n for (const el of all) {\n if (!el.id || !referred.has(el.id) || ids.has(el.id)) continue;\n ids.set(el.id, `${el.id}-${suffix}`);\n el.setAttribute(\"data-flair-id\", el.id);\n el.id = `${el.id}-${suffix}`;\n }\n if (ids.size)\n for (const el of [svg, ...svg.querySelectorAll(\"*\")]) {\n for (const attr of [...el.attributes]) {\n const scoped = scopedRefs(attr.value, ids, attr.name);\n if (scoped !== attr.value) attr.value = scoped;\n }\n if (el.localName === \"style\" && el.textContent) el.textContent = scopedRefs(el.textContent, ids);\n }\n return ids;\n}\n\n/** `value` with its references to ids in `ids` pointed at their scoped ids. */\nfunction scopedRefs(value: string, ids: ReadonlyMap<string, string>, attr = \"\"): string {\n const name = attr.toLowerCase();\n if (name === \"href\" || name === \"xlink:href\") {\n const to = value.startsWith(\"#\") ? ids.get(value.slice(1)) : undefined;\n return to ? `#${to}` : value;\n }\n if (!value.includes(\"url(\")) return value;\n return value.replace(URL_REF, (whole, quote: string, id: string) => {\n const to = ids.get(id);\n return to ? `url(${quote}#${to}${quote})` : whole;\n });\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 ids: ReadonlyMap<string, string> = new Map(),\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, scopedRefs(value, ids, name));\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 /**\n * Called with the id of the innermost element with an id that is clicked:\n * its id in the motion's markup (in the page, ids carry a suffix that keeps\n * them unique, so several pictures can use the same ones).\n */\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.getAttribute(\"data-flair-id\") ?? 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 let ids: ReadonlyMap<string, string> = new Map();\n const suffix = `f${++views}`;\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 ids = scopeIds(svg, suffix);\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]\") ?? [])\n byId.set(el.getAttribute(\"data-flair-id\") ?? 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, ids);\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 // Without IntersectionObserver (a server, a test), nothing would say it entered: play at once.\n const shouldAutoplay = (options.autoplay ?? true) && !(options.playOnEnter && needsObserver);\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 +0,0 @@
1
- {"version":3,"sources":["../src/export/effect.ts"],"sourcesContent":["/**\n * Renders a catalog effect, applied to a subject, to a file in Node: video\n * (MP4, WebM, MOV), GIF, PNG frames, or (vector effects) an animated SVG.\n */\n\nimport { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport {\n EFFECT_CATEGORIES,\n EFFECT_INPUTS,\n EFFECT_INTERACTIONS,\n effectControls,\n effectDefaults,\n effectInputs,\n getEffect,\n renderVectorEffect,\n type EffectCategory,\n type EffectDefinition,\n type EffectInput,\n type EffectInteraction,\n type VectorEffect,\n} from '../catalog/index.js';\nimport { subjectValues, type EffectSubject } from '../embed/index.js';\nimport { checkControlValue, parseColor, type ControlDef, type ControlValue } from '../shader/controls.js';\nimport { wrapSource } from '../shader/program.js';\nimport { svgAnimatedMarkup } from '../vector/animated.js';\nimport type { SvgMotion } from '../vector/motion.js';\nimport { svgFrameMarkup, svgViewBox } from '../vector/static.js';\nimport { svgDuration } from '../vector/svg.js';\nimport {\n ffmpegPath,\n frameStates,\n imageDataUrl,\n pageControls,\n planOutput,\n runRender,\n type FrameState,\n type PointerMode,\n} from './recorder.js';\n\n/**\n * What an effect is applied to, in Node:\n * - `{ image }`: a picture's file path or URL (or a `data:` URL).\n * - `{ svg }`: an SVG's file path, URL, or markup (`\"<svg …>\"`).\n * - `{ text }`: words, for a vector effect's text, or drawn with `textImage` for a shader's text or mask.\n * - `{ path }`: SVG path data (`d`), for a shape control.\n * - `{ video }`: a video file's path, decoded to frames with ffmpeg and shown one per frame.\n */\nexport type NodeSubject =\n | { image: string }\n | { svg: string }\n | { text: string; font?: string; weight?: number | string }\n | { path: string }\n | { video: string };\n\nexport interface EffectExportOptions {\n /** What to apply it to; several fill several controls in order (a transition's from and to). */\n subject?: NodeSubject | readonly NodeSubject[];\n /** A preset by name, under `values`. */\n preset?: string;\n /** Control values over the defaults (and the subject). An image control also takes a file path. */\n values?: Record<string, ControlValue>;\n /** .mp4, .webm, .mov, .gif, .svg (vector effects), or PNG frames (a folder, or `out_%04d.png`). */\n outputPath: string;\n /** Pixels (default: 1280 × 720 for effects that show a picture, 720 × 720 for other shaders, a vector's own shape 720 px on its long edge). */\n width?: number;\n height?: number;\n fps?: number;\n /**\n * Seconds. Default: a shader's loop (when it loops in 12 s or less), a\n * video subject's length (up to 10 s), else 3; a vector effect's one\n * play-through (with its trigger acted out).\n */\n duration?: number;\n /** Keep transparency (WebM, MOV, GIF, PNG and SVG; MP4 is always opaque). */\n alpha?: boolean;\n /** CSS colour under transparent pixels without `alpha` (default black for shaders, white for vector effects). */\n background?: string;\n /**\n * The pointer, for effects that answer it: `auto` (the default for them),\n * a scripted pointer that hovers, wanders and clicks; `none` leaves it\n * still. Vector effects with a trigger have it acted out (clicked through\n * their steps, or hovered and let go).\n */\n pointer?: PointerMode;\n onProgress?: (frame: number, total: number) => void;\n}\n\nconst isUrl = (s: string) => /^(https?|data):/i.test(s);\n\n/** An SVG subject as markup: given markup stays, a URL is fetched, a path read. */\nasync function svgMarkup(svg: string): Promise<string> {\n const text = svg.trim();\n if (text.startsWith('<')) return text;\n if (/^data:image\\/svg\\+xml/i.test(text)) {\n const comma = text.indexOf(',');\n const meta = text.slice(0, comma);\n const body = text.slice(comma + 1);\n return /;base64/i.test(meta) ? Buffer.from(body, 'base64').toString('utf8') : decodeURIComponent(body);\n }\n if (/^https?:/i.test(text)) {\n const res = await fetch(text);\n if (!res.ok) throw new Error(`Could not download the SVG at ${text} (HTTP ${res.status}).`);\n return res.text();\n }\n const file = path.resolve(text);\n if (!fs.existsSync(file)) throw new Error(`There is no SVG at ${file}; give a file path, a URL, or markup starting with \"<svg\".`);\n return fs.readFileSync(file, 'utf8');\n}\n\n/** What a Node subject becomes: control values, and video files by image control. */\nexport interface NodeSubjectValues {\n values: Record<string, ControlValue>;\n /** Image control → video file. */\n video: Record<string, string>;\n}\n\n/**\n * The control values that put Node subjects in `effect` (see\n * `subjectValues` in `@pixerate/flair/embed`, which picks the controls):\n * image files and URLs become `data:` URLs, SVG files and URLs are read as\n * markup, and a video is kept as its file for its control, to be decoded to\n * frames. Throws a sentence when the effect takes no such thing.\n */\nexport async function nodeSubjectValues(effect: EffectDefinition, subject: NodeSubject | readonly NodeSubject[]): Promise<NodeSubjectValues> {\n const list = (Array.isArray(subject) ? subject : [subject]) as readonly NodeSubject[];\n const browser: EffectSubject[] = [];\n const videoFiles: string[] = [];\n for (const s of list) {\n if ('image' in s) browser.push({ image: await imageDataUrl(s.image) });\n else if ('svg' in s) browser.push({ svg: await svgMarkup(s.svg) });\n else if ('video' in s) {\n const file = path.resolve(s.video);\n if (!isUrl(s.video) && !fs.existsSync(file)) throw new Error(`There is no video at ${file}.`);\n videoFiles.push(isUrl(s.video) ? s.video : file);\n // A stand-in: subjectValues only needs to know it is a video to pick its control.\n browser.push({ video: { readyState: 0, currentTime: 0 } as unknown as HTMLVideoElement });\n } else browser.push(s);\n }\n const { values, live } = subjectValues(effect, browser);\n const video: Record<string, string> = {};\n Object.keys(live).forEach((name, i) => (video[name] = videoFiles[i]!));\n return { values, video };\n}\n\n/** The effect for an id (or the definition itself); a sentence when there is none. */\nexport function resolveEffect(effect: string | EffectDefinition): EffectDefinition {\n if (typeof effect !== 'string') return effect;\n const def = getEffect(effect);\n if (!def) throw new Error(`There is no effect \"${effect}\" in the catalog; \"flair effects\" (or findEffects()) lists them.`);\n return def;\n}\n\n/**\n * Checks control values against an effect's controls: unknown names, and\n * values a control cannot take, are sentences. Image controls take file\n * paths too (read into `data:` URLs).\n */\nexport async function checkEffectValues(effect: EffectDefinition, values: Record<string, ControlValue>): Promise<Record<string, ControlValue>> {\n const controls = new Map(effectControls(effect).map((c) => [c.name, c]));\n const out: Record<string, ControlValue> = {};\n for (const [name, raw] of Object.entries(values)) {\n const control = controls.get(name);\n if (!control) throw new Error(`\"${effect.id}\" has no control \"${name}\"; it has ${[...controls.keys()].join(', ') || 'none'}.`);\n if (control.type === 'sampler2D' && typeof raw === 'string') {\n out[name] = await imageDataUrl(raw);\n continue;\n }\n if (effect.kind === 'vector' && control.color && typeof raw === 'string' && raw.trim().toLowerCase() === 'currentcolor') {\n out[name] = raw;\n continue;\n }\n const check = checkControlValue(control, raw);\n if (!check.ok) throw new Error(check.message);\n out[name] = check.value;\n }\n return out;\n}\n\n/** How a vector effect plays over time in a recording: the frame at each ms, and its loop. */\nexport interface VectorTimeline {\n /** One play-through, ms. */\n length: number;\n /** Step start times (ms) for the whole loop, for an animated SVG. */\n steps: number[];\n /** The picture's time and steps at `ms` into the recording. */\n at(ms: number): { time: number; steps: number[] };\n}\n\n/** Ms an effect rests before its trigger is acted out. */\nconst REST = 300;\n\n/**\n * A vector effect's timeline: a loop plays and rests `hold` ms; a hover or\n * press trigger plays to the end, rests, and plays back; a click trigger is\n * clicked through each of its steps (or, with none, clicked again after\n * each play-through).\n */\nexport function vectorTimeline(effect: Pick<VectorEffect, 'hold' | 'trigger'>, motion: SvgMotion): VectorTimeline {\n const hold = effect.hold ?? 600;\n const d = svgDuration(motion);\n if (effect.trigger === 'hover' || effect.trigger === 'press') {\n const length = REST + d + hold + d;\n return {\n length,\n steps: [],\n at(ms) {\n const p = ms % length;\n const time = p < REST ? 0 : p < REST + d ? p - REST : p < REST + d + hold ? d : Math.max(0, d - (p - REST - d - hold));\n return { time, steps: [] };\n },\n };\n }\n if (effect.trigger === 'click') {\n const anims = motion.animate ?? [];\n const last = anims.reduce((n, a) => Math.max(n, a.step ?? 0), 0);\n if (last === 0) {\n const length = REST + d + hold;\n return { length, steps: [], at: (ms) => ({ time: Math.max(0, (ms % length) - REST), steps: [] }) };\n }\n // One click per step, each when the step before has played and rested.\n const clicks: number[] = [];\n let at = REST;\n for (let step = 1; step <= last; step++) {\n clicks.push(at);\n const ends = anims.filter((a) => (a.step ?? 0) === step).map((a) => (a.delay ?? 0) + (a.ms ?? 800));\n at += Math.max(0, ...ends) + hold;\n }\n const length = at;\n return {\n length,\n steps: clicks,\n at(ms) {\n const p = ms % length;\n return { time: p, steps: clicks.filter((c) => c <= p) };\n },\n };\n }\n const length = d + hold;\n return { length, steps: [], at: (ms) => ({ time: ms % length, steps: [] }) };\n}\n\nconst even = (n: number) => Math.max(2, Math.round(n / 2) * 2);\n\n/** Default pixel size for an effect (see {@link EffectExportOptions.width}). */\nexport function defaultSize(effect: EffectDefinition, motion?: SvgMotion): { width: number; height: number } {\n if (effect.kind === 'vector') {\n const box = svgViewBox((motion ?? effect.motion).svg);\n const aspect = box && box[2] > 0 && box[3] > 0 ? box[2] / box[3] : 1;\n return aspect >= 1 ? { width: 720, height: even(720 / aspect) } : { width: even(720 * aspect), height: 720 };\n }\n return effectInputs(effect).images.length ? { width: 1280, height: 720 } : { width: 720, height: 720 };\n}\n\nfunction run(bin: string, args: string[]): Promise<{ code: number; stderr: string }> {\n return new Promise((resolve, reject) => {\n const proc = spawn(bin, args, { stdio: ['ignore', 'ignore', 'pipe'] });\n let stderr = '';\n proc.stderr!.on('data', (d) => (stderr += d.toString()));\n proc.on('error', (err: NodeJS.ErrnoException) =>\n reject(err.code === 'ENOENT' ? new Error('A video subject needs ffmpeg to decode it, and it is not installed (or not on the PATH); install it, or set FFMPEG_PATH.') : err),\n );\n proc.on('close', (code) => resolve({ code: code ?? 1, stderr }));\n });\n}\n\n/** A video's length in seconds, from ffmpeg's report (undefined when it does not say). */\nexport async function videoSeconds(file: string): Promise<number | undefined> {\n const { stderr } = await run(ffmpegPath(), ['-hide_banner', '-i', file]);\n const m = /Duration:\\s*(\\d+):(\\d+):(\\d+(?:\\.\\d+)?)/.exec(stderr);\n return m ? Number(m[1]) * 3600 + Number(m[2]) * 60 + Number(m[3]) : undefined;\n}\n\n/**\n * Decodes `frames` frames of a video at `fps` into PNGs in `dir` (looping a\n * short video), at most 2048 px on the long edge. Returns their paths.\n */\nexport async function extractVideoFrames(file: string, fps: number, frames: number, dir: string): Promise<string[]> {\n fs.mkdirSync(dir, { recursive: true });\n const { code, stderr } = await run(ffmpegPath(), [\n '-hide_banner', '-loglevel', 'error', '-y',\n '-stream_loop', '-1', '-i', file,\n '-vf', `fps=${fps},scale='if(gte(iw,ih),min(2048,iw),-2)':'if(gte(iw,ih),-2,min(2048,ih))'`,\n '-frames:v', String(frames), '-start_number', '0',\n path.join(dir, '%06d.png'),\n ]);\n const files = fs.readdirSync(dir).filter((f) => f.endsWith('.png')).sort().map((f) => path.join(dir, f));\n if (code !== 0 || !files.length) throw new Error(`ffmpeg could not decode the video at ${file}: ${stderr.trim() || `exit ${code}`}`);\n return files;\n}\n\n/**\n * Renders a catalog effect (by id or definition), applied to `subject`, to\n * a file: a shader through WebGL2 in headless Chrome, a vector effect by\n * drawing each frame's SVG there, or (to `.svg`) a vector effect as one\n * animated SVG with no browser at all. MP4, WebM, MOV and video subjects\n * need ffmpeg. Throws a sentence when the effect is unknown, takes no such\n * subject, or cannot be written in that format.\n *\n * ```ts\n * await exportEffect(\"image-duotone\", { subject: { image: \"team.jpg\" }, preset: \"Spotify\", outputPath: \"team.mp4\" });\n * await exportEffect(\"click-ripple\", { alpha: true, outputPath: \"ripple.webm\" });\n * await exportEffect(\"typewriter\", { subject: { text: \"Hello\" }, outputPath: \"hello.gif\" });\n * await exportEffect(\"toggle-switch\", { outputPath: \"toggle.svg\" });\n * ```\n */\nexport async function exportEffect(effect: string | EffectDefinition, options: EffectExportOptions): Promise<string> {\n const def = resolveEffect(effect);\n if (options.preset && !def.presets?.some((p) => p.name === options.preset)) {\n const names = def.presets?.map((p) => `\"${p.name}\"`).join(', ');\n throw new Error(`\"${def.id}\" has no preset \"${options.preset}\"; ${names ? `its presets are ${names}` : 'it has no presets'}.`);\n }\n const fromSubject = options.subject ? await nodeSubjectValues(def, options.subject) : { values: {}, video: {} };\n const explicit = await checkEffectValues(def, options.values ?? {});\n const values = { ...effectDefaults(def, options.preset ? { preset: options.preset } : {}), ...fromSubject.values, ...explicit };\n const fps = options.fps ?? 30;\n const background = options.background ?? (def.kind === 'vector' ? '#ffffff' : '#000000');\n\n if (def.kind === 'vector') {\n const motion = renderVectorEffect(def, values);\n const timeline = vectorTimeline(def, motion);\n const duration = options.duration ?? timeline.length / 1000;\n const frames = Math.max(1, Math.round(duration * fps));\n const plan = planOutput(options.outputPath, { alpha: options.alpha, frames });\n if (plan.format === 'svg') {\n const markup = svgAnimatedMarkup(motion, {\n ...(def.hold !== undefined ? { hold: def.hold } : {}),\n ...(timeline.steps.length ? { steps: timeline.steps, duration: timeline.length } : {}),\n ...(options.duration !== undefined ? { duration: options.duration * 1000 } : {}),\n fps,\n });\n if (!markup) throw new Error(`\"${def.id}\" has no <svg> to write.`);\n // A file gets a size of its own (it fills its container otherwise); a background only when asked for.\n const size = defaultSize(def, motion);\n const w = Math.round(options.width ?? size.width);\n const h = Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height));\n const style = !plan.alpha && options.background ? ` style=\"background:${options.background.replace(/[\"<>&]/g, '')}\"` : '';\n const out = markup.replace(/^<svg\\b[^>]*>/, (open) =>\n open.replace(/\\swidth=\"100%\"/, ` width=\"${w}\"`).replace(/\\sheight=\"100%\"/, ` height=\"${h}\"`).replace(/>$/, `${style}>`),\n );\n fs.mkdirSync(path.dirname(plan.path), { recursive: true });\n fs.writeFileSync(plan.path, out, 'utf8');\n options.onProgress?.(1, 1);\n return plan.path;\n }\n const size = defaultSize(def, motion);\n const width = Math.max(1, Math.round(options.width ?? size.width));\n const height = Math.max(1, Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height)));\n const base = frameStates(frames, fps, width, height, 'none');\n const pointer = options.pointer ?? 'auto';\n const state = (i: number): FrameState => {\n const ms = (i / fps) * 1000;\n const { time, steps } = pointer === 'none' && def.trigger ? { time: 0, steps: [] } : timeline.at(ms);\n return { ...base[i]!, svg: svgFrameMarkup(motion, { at: time, steps })! };\n };\n return runRender({\n page: { width, height, frames, alpha: plan.alpha, background, send: plan.format === 'gif' ? 'rgba' : 'png' },\n state,\n plan,\n fps,\n ...(options.onProgress ? { onProgress: options.onProgress } : {}),\n });\n }\n\n // A shader.\n const videoEntries = Object.entries(fromSubject.video);\n let duration = options.duration;\n if (duration === undefined && videoEntries.length) {\n const seconds = await videoSeconds(videoEntries[0]![1]);\n duration = seconds ? Math.min(10, seconds) : undefined;\n }\n duration ??= def.loop && def.loop <= 12 ? def.loop : 3;\n const frames = Math.max(1, Math.round(duration * fps));\n const plan = planOutput(options.outputPath, { alpha: options.alpha, frames });\n if (plan.format === 'svg') {\n throw new Error(`\"${def.id}\" is a shader effect, which draws pixels; write .mp4, .webm, .mov, .gif or .png frames (only vector effects become SVG).`);\n }\n const size = defaultSize(def);\n const width = Math.max(1, Math.round(options.width ?? size.width));\n const height = Math.max(1, Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height)));\n const interactive = effectInputs(def).interactions.length > 0;\n const states = frameStates(frames, fps, width, height, options.pointer ?? (interactive ? 'auto' : 'none'));\n\n const tmp = videoEntries.length ? fs.mkdtempSync(path.join(os.tmpdir(), 'flair-video-')) : undefined;\n try {\n const videoFrames: Record<string, string[]> = {};\n for (const [name, file] of videoEntries) videoFrames[name] = await extractVideoFrames(file, fps, frames, path.join(tmp!, name));\n const state = (i: number): FrameState => {\n if (!videoEntries.length) return states[i]!;\n const video = Object.fromEntries(Object.entries(videoFrames).map(([name, files]) => [name, files[Math.min(i, files.length - 1)]!]));\n return { ...states[i]!, video };\n };\n return await runRender({\n page: {\n width,\n height,\n frames,\n alpha: plan.alpha,\n background,\n send: plan.format === 'gif' ? 'rgba' : 'png',\n shader: {\n fragment: wrapSource(def.source),\n controls: await pageControls(def.source, values),\n opaque: !plan.alpha && !def.transparent,\n },\n },\n state,\n plan,\n fps,\n ...(options.onProgress ? { onProgress: options.onProgress } : {}),\n });\n } finally {\n if (tmp) fs.rmSync(tmp, { recursive: true, force: true });\n }\n}\n\n// ==========================================\n// Command-line helpers\n// ==========================================\n\n/** `\"1280x720\"` → `{ width: 1280, height: 720 }`; a sentence for anything else. */\nexport function parseSize(size: string): { width: number; height: number } {\n const m = /^\\s*(\\d+)\\s*[x×X]\\s*(\\d+)\\s*$/.exec(size);\n if (!m || Number(m[1]) < 1 || Number(m[2]) < 1) throw new Error(`--size takes width x height in pixels, like 1280x720, not \"${size}\".`);\n return { width: Number(m[1]), height: Number(m[2]) };\n}\n\n/**\n * A `--set` value read for its control: numbers for `float` and `int`,\n * `true`/`false` (or 1/0) for `bool`, a colour (`#77C16A`, `rgb(…)`) or\n * numbers separated by commas or spaces for vectors, and the text itself\n * for images (a path or URL) and text.\n */\nexport function parseSetValue(control: ControlDef, raw: string): ControlValue {\n const text = raw.trim();\n switch (control.type) {\n case 'float':\n case 'int': {\n const n = Number(text);\n if (text === '' || !Number.isFinite(n)) throw new Error(`${control.name} takes a number, not \"${raw}\".`);\n return n;\n }\n case 'bool': {\n if (/^(true|1|yes|on)$/i.test(text)) return true;\n if (/^(false|0|no|off)$/i.test(text)) return false;\n throw new Error(`${control.name} takes true or false, not \"${raw}\".`);\n }\n case 'sampler2D':\n case 'text':\n return raw;\n default: {\n if (control.color && (parseColor(text) || /^currentcolor$/i.test(text)) && !/^[-\\d.]+([\\s,]+[-\\d.]+)*$/.test(text)) return text;\n const parts = text.split(/[\\s,]+/).filter(Boolean).map(Number);\n if (!parts.length || parts.some((n) => !Number.isFinite(n))) {\n throw new Error(`${control.name} takes ${control.type === 'vec2' ? 2 : control.type === 'vec3' ? 3 : 4} numbers separated by commas${control.color ? ', or a colour like \"#77C16A\"' : ''}, not \"${raw}\".`);\n }\n return parts;\n }\n }\n}\n\n/** `--set name=value` flags as control values for an effect; sentences for unknown names and bad values. */\nexport function parseSets(effect: EffectDefinition, sets: readonly string[]): Record<string, ControlValue> {\n const controls = new Map(effectControls(effect).map((c) => [c.name, c]));\n const out: Record<string, ControlValue> = {};\n for (const set of sets) {\n const eq = set.indexOf('=');\n if (eq <= 0) throw new Error(`--set takes name=value, like --set uSpeed=2, not \"${set}\".`);\n const name = set.slice(0, eq).trim();\n const control = controls.get(name);\n if (!control) throw new Error(`\"${effect.id}\" has no control \"${name}\"; it has ${[...controls.keys()].join(', ') || 'none'}.`);\n out[name] = parseSetValue(control, set.slice(eq + 1));\n }\n return out;\n}\n\n/** The render command's subject flags. */\nexport interface SubjectFlags {\n image?: readonly string[];\n svg?: readonly string[];\n text?: readonly string[];\n path?: readonly string[];\n video?: readonly string[];\n font?: string;\n weight?: string;\n}\n\n/** Subjects from the render command's flags, in the order images, SVGs, videos, texts, paths. */\nexport function subjectsFromFlags(flags: SubjectFlags): NodeSubject[] {\n const weight = flags.weight === undefined ? undefined : /^\\d+$/.test(flags.weight) ? Number(flags.weight) : flags.weight;\n return [\n ...(flags.image ?? []).map((image) => ({ image })),\n ...(flags.svg ?? []).map((svg) => ({ svg })),\n ...(flags.video ?? []).map((video) => ({ video })),\n ...(flags.text ?? []).map((text) => ({ text, ...(flags.font ? { font: flags.font } : {}), ...(weight !== undefined ? { weight } : {}) })),\n ...(flags.path ?? []).map((p) => ({ path: p })),\n ];\n}\n\n/** Comma-separated (or repeated) flag values checked against a list; a sentence for the rest. */\nexport function parseChoices<T extends string>(flag: string, values: readonly string[] | undefined, allowed: readonly T[]): T[] | undefined {\n if (!values?.length) return undefined;\n const list = values.flatMap((v) => v.split(',')).map((v) => v.trim()).filter(Boolean);\n const bad = list.filter((v) => !(allowed as readonly string[]).includes(v));\n if (bad.length) throw new Error(`${flag} takes ${allowed.join(', ')}; not ${bad.map((b) => `\"${b}\"`).join(', ')}.`);\n return list as T[];\n}\n\n/** The effects command's filters, checked. */\nexport function effectQueryFromFlags(flags: { accepts?: readonly string[]; interaction?: readonly string[]; category?: string; kind?: string; search?: string; transparent?: boolean }) {\n const accepts = parseChoices<EffectInput>('--accepts', flags.accepts, EFFECT_INPUTS);\n const interaction = parseChoices<EffectInteraction>('--interaction', flags.interaction, EFFECT_INTERACTIONS);\n if (flags.category && !(EFFECT_CATEGORIES as readonly string[]).includes(flags.category)) {\n throw new Error(`--category takes one of ${EFFECT_CATEGORIES.join(', ')}; not \"${flags.category}\".`);\n }\n if (flags.kind && flags.kind !== 'shader' && flags.kind !== 'vector') throw new Error(`--kind takes shader or vector; not \"${flags.kind}\".`);\n return {\n ...(accepts ? { accepts } : {}),\n ...(interaction ? { interaction } : {}),\n ...(flags.category ? { category: flags.category as EffectCategory } : {}),\n ...(flags.kind ? { kind: flags.kind as 'shader' | 'vector' } : {}),\n ...(flags.search ? { text: flags.search } : {}),\n ...(flags.transparent !== undefined ? { transparent: flags.transparent } : {}),\n };\n}\n\n/** One line about an effect for the effects command: id, kind, category, and what it takes. */\nexport function describeEffect(effect: EffectDefinition): string {\n const inputs = effectInputs(effect);\n const parts = [\n `${effect.kind}/${effect.category}`,\n `takes ${inputs.accepts.join(', ') || 'nothing'}`,\n ...(inputs.video ? [`video: ${inputs.video}`] : []),\n ...(inputs.interactions.length ? [`answers ${inputs.interactions.join(', ')}`] : []),\n ...(inputs.transparent ? ['overlay'] : []),\n ...(effect.presets?.length ? [`presets: ${effect.presets.map((p) => p.name).join(', ')}`] : []),\n ];\n return `${effect.id.padEnd(24)} ${parts.join(' · ')}`;\n}\n"],"mappings":"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","names":["length","duration","frames","plan","size","width","height"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shader/textures.ts","../src/shader/pointer.ts","../src/shader/program.ts"],"sourcesContent":["import type { TextureSettings } from \"./controls.js\";\n\n/** Why an image control's URL could not be shown. */\nexport class ImageLoadError extends Error {\n readonly url: string;\n\n constructor(url: string, cause?: unknown, message?: string) {\n super(\n message ??\n `Could not load the image at ${shortUrl(url)}. If it is on another site, that site must allow cross-origin use (CORS).`,\n { cause },\n );\n this.name = \"ImageLoadError\";\n this.url = url;\n }\n}\n\n/** A URL short enough for a message: a data URL is cut after its type. */\nexport function shortUrl(url: string): string {\n if (url.startsWith(\"data:\")) return `${url.slice(0, Math.min(url.indexOf(\",\") + 1 || 40, 40))}…`;\n return url.length > 120 ? `${url.slice(0, 119)}…` : url;\n}\n\n/**\n * The long edge, in pixels, SVG images are rasterised at. WebGL2 guarantees\n * textures this large on every device.\n */\nexport const SVG_EDGE = 2048;\n\nconst SVG_TYPE = \"image/svg+xml\";\n\n/** Whether `url` is an SVG: a `data:image/svg+xml` URL, or a path ending in `.svg`. */\nexport function isSvgUrl(url: string): boolean {\n return /^data:image\\/svg\\+xml[;,]/i.test(url) || /^(?!data:)[^?#]*\\.svg([?#]|$)/i.test(url);\n}\n\n/** A length attribute in user units, or undefined for a percentage, `auto` or nothing. */\nfunction svgLength(value: string | null): number | undefined {\n const match = value?.trim().match(/^([\\d.]+)(px)?$/);\n const n = match ? Number(match[1]) : NaN;\n return n > 0 ? n : undefined;\n}\n\n/**\n * An SVG's markup with `width` and `height` set so its long edge is `edge`\n * pixels, keeping its aspect ratio. An image decodes an SVG at its own size\n * (often icon-sized, or 300 × 150 when it has none), so it would make a\n * small, blurry texture; at this size it is rasterised crisply. A `viewBox`\n * is added where missing, so the drawing scales with the new size rather\n * than staying in the corner. Throws for markup that is not an SVG.\n */\nexport function sizeSvg(markup: string, edge: number): string {\n const doc = new DOMParser().parseFromString(markup, SVG_TYPE);\n const svg = doc.documentElement;\n if (svg.localName !== \"svg\" || doc.getElementsByTagName(\"parsererror\").length) {\n throw new Error(\"Not an SVG document.\");\n }\n const box = (svg.getAttribute(\"viewBox\") ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .map(Number);\n const hasBox = box.length === 4 && box[2]! > 0 && box[3]! > 0;\n const width = svgLength(svg.getAttribute(\"width\")) ?? (hasBox ? box[2]! : 300);\n const height = svgLength(svg.getAttribute(\"height\")) ?? (hasBox ? box[3]! : 150);\n if (!hasBox) svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n const scale = edge / Math.max(width, height);\n svg.setAttribute(\"width\", String(Math.max(1, Math.round(width * scale))));\n svg.setAttribute(\"height\", String(Math.max(1, Math.round(height * scale))));\n return new XMLSerializer().serializeToString(svg);\n}\n\n/**\n * Loads and decodes an image for WebGL. Images from other origins are\n * requested with CORS (`crossOrigin = \"anonymous\"`): WebGL refuses pixels a\n * page is not allowed to read. SVGs (see {@link isSvgUrl}) are fetched, also\n * with CORS, and sized to {@link SVG_EDGE} pixels on their long edge before\n * they are decoded, so they stay sharp however small they were drawn.\n */\nexport async function loadImage(url: string): Promise<HTMLImageElement> {\n if (isSvgUrl(url)) return loadSvg(url);\n return decode(url, url, !/^(data|blob):/i.test(url));\n}\n\nasync function loadSvg(url: string): Promise<HTMLImageElement> {\n let markup: string;\n try {\n const response = await fetch(url);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n markup = sizeSvg(await response.text(), SVG_EDGE);\n } catch (e) {\n throw new ImageLoadError(url, e);\n }\n const sized = URL.createObjectURL(new Blob([markup], { type: SVG_TYPE }));\n try {\n return await decode(sized, url, false);\n } finally {\n URL.revokeObjectURL(sized);\n }\n}\n\nasync function decode(src: string, url: string, cors: boolean): Promise<HTMLImageElement> {\n const image = new Image();\n if (cors) image.crossOrigin = \"anonymous\";\n image.src = src;\n try {\n await image.decode();\n } catch (e) {\n throw new ImageLoadError(url, e);\n }\n return image;\n}\n\n/** Images decoded by {@link preloadImages}, by URL, for every context on the page. */\nconst preloaded = new Map<string, Promise<HTMLImageElement>>();\nconst decoded = new Map<string, HTMLImageElement>();\n\n/**\n * Loads and decodes images ahead of time, for every WebGL context on the\n * page: a texture cache that meets one of them afterwards (in a\n * `ShaderRenderer`, `ShaderFrames` or thumbnail) uploads it at once, so even\n * its first frame shows the image. Otherwise an image arrives a moment after\n * the first frame, which is black where it goes. Use it before drawing frames\n * that cannot wait, such as a video player asking a new `ShaderFrames` for\n * frame _n_.\n *\n * Each URL loads once, however often it is preloaded. Rejects with the first\n * {@link ImageLoadError}; the other images still load. Preloaded images stay\n * in memory until {@link releaseImages}.\n */\nexport async function preloadImages(urls: Iterable<string>): Promise<void> {\n const loads = [...new Set(urls)].filter(Boolean).map((url) => {\n let load = preloaded.get(url);\n if (!load) {\n load = loadImage(url).then(\n (image) => {\n if (preloaded.get(url) === load) decoded.set(url, image);\n return image;\n },\n (e: unknown) => {\n if (preloaded.get(url) === load) preloaded.delete(url);\n throw e;\n },\n );\n preloaded.set(url, load);\n }\n return load;\n });\n const results = await Promise.allSettled(loads);\n const failed = results.find((r) => r.status === \"rejected\");\n if (failed) throw failed.reason;\n}\n\n/** Forgets preloaded images (all of them without `urls`). Textures already made from them stay. */\nexport function releaseImages(urls?: Iterable<string>) {\n for (const url of urls ?? [...preloaded.keys()]) {\n preloaded.delete(url);\n decoded.delete(url);\n }\n}\n\n/** Something that hands `drawFrame` a texture for an image URL. */\nexport interface TextureSource {\n /** The texture for `url`: the image once loaded, a 1 × 1 opaque black placeholder until then. */\n texture(url: string): WebGLTexture | null;\n /**\n * The texture of the live source set for the image control `name` (see\n * {@link TextureCache.setLive}), with its current frame; undefined when\n * the control has none, so its URL is drawn instead.\n */\n liveTexture?(name: string): WebGLTexture | null | undefined;\n}\n\n/**\n * A picture that changes while it is shown, for an image control: a playing\n * `<video>`, or a canvas something else draws on. Videos re-upload when\n * their frame changes; canvases on every frame drawn.\n */\nexport type LiveSource = HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas;\n\ninterface Live {\n source: LiveSource;\n texture: WebGLTexture | null;\n /** The video time last uploaded (NaN before the first upload). */\n uploaded: number;\n /** Set once the source could not be uploaded: it shows black, and is not tried again. */\n error: ImageLoadError | null;\n}\n\n/** Whether `source` is a video element (without touching `HTMLVideoElement`, which tests and workers lack). */\nfunction isVideo(source: LiveSource): source is HTMLVideoElement {\n return \"readyState\" in source && \"currentTime\" in source;\n}\n\n/** `HAVE_CURRENT_DATA`: a video has a frame to show. */\nconst HAVE_CURRENT_DATA = 2;\n\n/** A name for a live source in messages: its video URL, or the control's name. */\nfunction liveName(name: string, source: LiveSource): string {\n return (isVideo(source) && (source.currentSrc || source.src)) || name;\n}\n\n/**\n * Seeks a video to `time` seconds and settles once the frame there can be\n * drawn, for stepping a video in time with `ShaderFrames`: `await\n * seekVideo(video, t); frames.render(t)`. Pause the video first, or it\n * moves on. The time is clamped to the video's length.\n */\nexport async function seekVideo(video: HTMLVideoElement, time: number): Promise<void> {\n if (video.readyState < 1) {\n await new Promise<void>((resolve, reject) => {\n video.addEventListener(\"loadedmetadata\", () => resolve(), { once: true });\n video.addEventListener(\"error\", () => reject(new Error(`Could not load the video ${shortUrl(video.currentSrc || video.src)}.`)), { once: true });\n });\n }\n const target = Math.max(0, Math.min(time, Number.isFinite(video.duration) ? video.duration : time));\n if (Math.abs(video.currentTime - target) < 1e-6 && video.readyState >= HAVE_CURRENT_DATA && !video.seeking) return;\n await new Promise<void>((resolve) => {\n video.addEventListener(\"seeked\", () => resolve(), { once: true });\n video.currentTime = target;\n });\n}\n\ninterface Entry {\n texture: WebGLTexture | null;\n /** Settles when the image is uploaded or has failed. */\n loaded: Promise<void>;\n error: ImageLoadError | null;\n}\n\n/**\n * The textures of one WebGL context, by image URL. A URL starts loading the\n * first time it is asked for; until it arrives the placeholder is drawn, and\n * `onLoad` hears when it does (or fails) so the owner can redraw. An image\n * already decoded by {@link preloadImages} is uploaded at once instead. Images are\n * uploaded upright (`fragCoord / iResolution` samples them the right way up)\n * with mipmaps.\n */\nexport class TextureCache implements TextureSource {\n private readonly gl: WebGL2RenderingContext;\n private readonly entries = new Map<string, Entry>();\n private readonly live = new Map<string, Live>();\n private readonly onLoad: (url: string, error: ImageLoadError | null) => void;\n private readonly load: (url: string) => Promise<TexImageSource>;\n private placeholder: WebGLTexture | null = null;\n private disposed = false;\n\n constructor(\n gl: WebGL2RenderingContext,\n onLoad: (url: string, error: ImageLoadError | null) => void = () => {},\n load: (url: string) => Promise<TexImageSource> = loadImage,\n ) {\n this.gl = gl;\n this.onLoad = onLoad;\n this.load = load;\n }\n\n texture(url: string): WebGLTexture | null {\n return (url && this.entry(url).texture) || this.blank();\n }\n\n /** Starts loading `url` if it is new. */\n request(url: string) {\n if (url) this.entry(url);\n }\n\n private entry(url: string): Entry {\n const existing = this.entries.get(url);\n if (existing) return existing;\n const created: Entry = { texture: null, loaded: Promise.resolve(), error: null };\n const ready = decoded.get(url);\n if (ready && !this.gl.isContextLost()) {\n created.texture = this.upload(ready);\n this.entries.set(url, created);\n return created;\n }\n created.loaded = this.load(url).then(\n (image) => {\n if (this.entries.get(url) !== created || this.disposed || this.gl.isContextLost()) return;\n created.texture = this.upload(image);\n this.onLoad(url, null);\n },\n (e: unknown) => {\n if (this.entries.get(url) !== created || this.disposed) return;\n created.error = e instanceof ImageLoadError ? e : new ImageLoadError(url, e);\n this.onLoad(url, created.error);\n },\n );\n this.entries.set(url, created);\n return created;\n }\n\n /**\n * Shows `source` in the image control `name` instead of its URL, frame by\n * frame (`null` goes back to the URL). Its texture is uploaded as it is\n * drawn: a video's whenever it has moved to another frame, a canvas's on\n * every frame. Until a video has a frame it shows black. A video from\n * another site needs CORS (`crossOrigin = \"anonymous\"` and the site's\n * consent); without it the upload fails, `onLoad` hears an\n * {@link ImageLoadError}, and the control shows black.\n */\n setLive(name: string, source: LiveSource | null) {\n const existing = this.live.get(name);\n if (existing?.source === source) return;\n if (existing?.texture) this.gl.deleteTexture(existing.texture);\n this.live.delete(name);\n if (source) this.live.set(name, { source, texture: null, uploaded: NaN, error: null });\n }\n\n liveTexture(name: string): WebGLTexture | null | undefined {\n const live = this.live.get(name);\n if (!live) return undefined;\n if (live.error || this.gl.isContextLost()) return this.blank();\n const { source } = live;\n if (isVideo(source)) {\n if (source.readyState < HAVE_CURRENT_DATA) return live.texture ?? this.blank();\n if (live.texture && live.uploaded === source.currentTime) return live.texture;\n live.uploaded = source.currentTime;\n }\n const gl = this.gl;\n live.texture ??= gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, live.texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n try {\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);\n } catch (e) {\n const url = liveName(name, source);\n live.error = new ImageLoadError(\n url,\n e,\n `Could not draw the video at ${shortUrl(url)}. If it is on another site, set crossOrigin = \"anonymous\" on the <video> and make sure that site allows cross-origin use (CORS).`,\n );\n this.onLoad(url, live.error);\n return this.blank();\n } finally {\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n }\n gl.generateMipmap(gl.TEXTURE_2D);\n return live.texture;\n }\n\n /**\n * Whether a live video has moved to a frame not yet drawn (or has its\n * first frame ready), so a paused preview knows to draw again.\n */\n liveChanged(): boolean {\n for (const live of this.live.values()) {\n const { source } = live;\n if (live.error || !isVideo(source) || source.readyState < HAVE_CURRENT_DATA) continue;\n if (!live.texture || live.uploaded !== source.currentTime) return true;\n }\n return false;\n }\n\n /**\n * Settles once every live video has a frame to show. Rejects with an\n * {@link ImageLoadError} when one fails to load.\n */\n async liveReady(): Promise<void> {\n await Promise.all(\n [...this.live.entries()].map(([name, { source }]) => {\n if (!isVideo(source) || source.readyState >= HAVE_CURRENT_DATA) return undefined;\n return new Promise<void>((resolve, reject) => {\n source.addEventListener(\"loadeddata\", () => resolve(), { once: true });\n source.addEventListener(\n \"error\",\n () => reject(new ImageLoadError(liveName(name, source), source.error, `Could not load the video at ${shortUrl(liveName(name, source))}.`)),\n { once: true },\n );\n });\n }),\n );\n }\n\n /** The error for `url`, if it failed to load. */\n error(url: string): ImageLoadError | null {\n return this.entries.get(url)?.error ?? null;\n }\n\n /**\n * Settles once every one of `urls` has loaded or failed (starting any that\n * are new). Rejects with the first {@link ImageLoadError}.\n */\n async ready(urls: Iterable<string>): Promise<void> {\n const entries = [...urls].filter(Boolean).map((url) => this.entry(url));\n await Promise.all(entries.map((e) => e.loaded));\n const failed = entries.find((e) => e.error);\n if (failed) throw failed.error;\n }\n\n /** Deletes the textures of every URL not in `urls`; a later request loads them again. */\n retain(urls: Iterable<string>) {\n const keep = new Set(urls);\n for (const [url, entry] of this.entries) {\n if (keep.has(url)) continue;\n if (entry.texture) this.gl.deleteTexture(entry.texture);\n this.entries.delete(url);\n }\n }\n\n /** Deletes every texture; loads still in flight are dropped. */\n dispose() {\n this.retain([]);\n for (const name of [...this.live.keys()]) this.setLive(name, null);\n if (this.placeholder) this.gl.deleteTexture(this.placeholder);\n this.placeholder = null;\n this.disposed = true;\n }\n\n private blank(): WebGLTexture | null {\n if (this.placeholder) return this.placeholder;\n const gl = this.gl;\n this.placeholder = gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, this.placeholder);\n const black = new Uint8Array([0, 0, 0, 255]);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, black);\n return this.placeholder;\n }\n\n private upload(image: TexImageSource): WebGLTexture | null {\n const gl = this.gl;\n const texture = gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n gl.generateMipmap(gl.TEXTURE_2D);\n return texture;\n }\n}\n\n/** Sets the bound `TEXTURE_2D`'s filter and wrap. */\nexport function applyTextureSettings(gl: WebGL2RenderingContext, settings: TextureSettings) {\n const min = { mipmap: gl.LINEAR_MIPMAP_LINEAR, linear: gl.LINEAR, nearest: gl.NEAREST }[\n settings.filter\n ];\n const wrap = { repeat: gl.REPEAT, clamp: gl.CLAMP_TO_EDGE, mirror: gl.MIRRORED_REPEAT }[\n settings.wrap\n ];\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, min);\n gl.texParameteri(\n gl.TEXTURE_2D,\n gl.TEXTURE_MAG_FILTER,\n settings.filter === \"nearest\" ? gl.NEAREST : gl.LINEAR,\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);\n}\n","/**\n * The pointer as shaders see it, beyond Shadertoy's `iMouse`: where it is\n * while it is over the canvas, how hovered the canvas is (eased in and out),\n * how long it has been pressed, the last few clicks, and where it has just\n * been (its trail), so effects can answer hovers, presses, taps and moves.\n * Times are in `iTime` seconds, so a recording can script them (see\n * `ShaderFrames`' `pointer`, `clicks` and `trail`).\n */\n\n/** How many clicks `iClicks` holds; older ones drop off. */\nexport const MAX_CLICKS = 8;\n\n/** How many of the pointer's recent positions `iTrail` holds; older ones drop off. */\nexport const MAX_TRAIL = 32;\n\n/**\n * The least time between two positions in the trail, in seconds: moves in\n * between update the newest. With {@link MAX_TRAIL}, about a second of path.\n */\nexport const TRAIL_SECONDS = 1 / 30;\n\n/** How long hovering eases in or out, in seconds. */\nexport const HOVER_SECONDS = 0.25;\n\n/** A click, or a point of the trail: where (pixels from the bottom left, like `iMouse`) and when (`iTime` seconds). */\nexport interface ShaderClick {\n x: number;\n y: number;\n time: number;\n}\n\nconst smooth = (t: number) => (t <= 0 ? 0 : t >= 1 ? 1 : t * t * (3 - 2 * t));\n\n/**\n * `iClicks` from a list of clicks, newest first: `vec4(x, y, time, 1)` for\n * each click at or before `time`, zeros for the rest.\n */\nexport function clicksUniform(clicks: readonly ShaderClick[], time: number): Float32Array {\n return latest(clicks, time, MAX_CLICKS);\n}\n\n/**\n * `iTrail` from the pointer's positions, oldest first in the list: the\n * newest {@link MAX_TRAIL} at or before `time`, newest first, as\n * `vec4(x, y, time, 1)`, zeros for the rest.\n */\nexport function trailUniform(trail: readonly ShaderClick[], time: number): Float32Array {\n return latest(trail, time, MAX_TRAIL);\n}\n\nfunction latest(events: readonly ShaderClick[], time: number, max: number): Float32Array {\n const out = new Float32Array(max * 4);\n let i = 0;\n for (let k = events.length - 1; k >= 0 && i < max; k--) {\n const c = events[k]!;\n if (c.time > time) continue;\n out.set([c.x, c.y, c.time, 1], i * 4);\n i++;\n }\n return out;\n}\n\n/**\n * Follows one canvas's pointer for `iPointer` and `iClicks`: feed it pointer\n * events with the `iTime` they happen at, and read the uniforms for a frame.\n */\nexport class PointerTracker {\n private x = 0;\n private y = 0;\n private over = false;\n private hoverFrom = 0;\n private changedAt = -Infinity;\n private pressedAt: number | null = null;\n private releasedAt: number | null = null;\n private clicks: ShaderClick[] = [];\n private trail: ShaderClick[] = [];\n\n /**\n * The pointer moved to (x, y), in pixels from the bottom left. With a\n * `time`, the move joins the trail: a new point at most every\n * {@link TRAIL_SECONDS}, the newest point following the pointer between.\n */\n move(x: number, y: number, time?: number) {\n this.x = x;\n this.y = y;\n if (time === undefined) return;\n const last = this.trail[this.trail.length - 1];\n if (last && time >= last.time && time - last.time < TRAIL_SECONDS) {\n last.x = x;\n last.y = y;\n return;\n }\n this.trail.push({ x, y, time });\n if (this.trail.length > MAX_TRAIL) this.trail.shift();\n }\n\n /** The pointer came over the canvas (`true`) or left it. */\n hover(over: boolean, time: number) {\n if (over === this.over) return;\n this.hoverFrom = this.hovered(time);\n this.changedAt = time;\n this.over = over;\n }\n\n /** A press at (x, y): counts as a click. */\n down(x: number, y: number, time: number) {\n this.move(x, y);\n this.pressedAt = time;\n this.clicks.push({ x, y, time });\n if (this.clicks.length > MAX_CLICKS) this.clicks.shift();\n }\n\n up(time: number) {\n if (this.pressedAt !== null) this.releasedAt = time;\n this.pressedAt = null;\n }\n\n /** Forgets clicks, presses and the trail (when time jumps back, say). */\n clear() {\n this.clicks = [];\n this.trail = [];\n this.pressedAt = null;\n this.releasedAt = null;\n }\n\n /** How hovered the canvas is at `time`, 0–1, eased. */\n hovered(time: number): number {\n const t = smooth((time - this.changedAt) / HOVER_SECONDS);\n return this.hoverFrom + ((this.over ? 1 : 0) - this.hoverFrom) * t;\n }\n\n /**\n * `iPointer` at `time`: x, y, how hovered (0–1), and the press: seconds\n * held while pressed, minus the seconds since it was let go after, 0\n * before the first press.\n */\n pointer(time: number): [number, number, number, number] {\n let press = 0;\n if (this.pressedAt !== null) press = Math.max(0, time - this.pressedAt);\n else if (this.releasedAt !== null) press = -Math.max(1e-3, time - this.releasedAt);\n return [this.x, this.y, this.hovered(time), press];\n }\n\n /** `iClicks` at `time` (see {@link clicksUniform}). */\n clicksAt(time: number): Float32Array {\n return clicksUniform(this.clicks, time);\n }\n\n /** `iTrail` at `time` (see {@link trailUniform}). */\n trailAt(time: number): Float32Array {\n return trailUniform(this.trail, time);\n }\n}\n","import { controlValue, type ControlDef, type ControlValue } from \"./controls.js\";\nimport { MAX_CLICKS, MAX_TRAIL } from \"./pointer.js\";\nimport { applyTextureSettings, type TextureSource } from \"./textures.js\";\n\n/**\n * Shaders are written Shadertoy-style: the source defines\n * `void mainImage(out vec4 fragColor, in vec2 fragCoord)` and may use the\n * built-in uniforms below. It is compiled as GLSL ES 3.00 (WebGL2) inside a\n * small wrapper; `#line 1` keeps compiler line numbers matching the source.\n */\nexport const BUILTIN_UNIFORMS = [\n { name: \"iResolution\", type: \"vec3\", doc: \"viewport size in pixels (z = 1)\" },\n { name: \"iTime\", type: \"float\", doc: \"seconds since the shader started\" },\n { name: \"iTimeDelta\", type: \"float\", doc: \"seconds since the previous frame\" },\n { name: \"iFrame\", type: \"int\", doc: \"frame number\" },\n {\n name: \"iMouse\",\n type: \"vec4\",\n doc: \"xy = pointer while pressed (pixels), zw = press position (negative once released)\",\n },\n {\n name: \"iPointer\",\n type: \"vec4\",\n doc: \"xy = pointer while over the canvas (pixels), z = how hovered (0–1, eased), w = seconds held while pressed, minus seconds since release after (0 before any press)\",\n },\n {\n name: \"iClicks\",\n type: \"vec4\",\n size: MAX_CLICKS,\n doc: \"the latest clicks, newest first: xy = where (pixels), z = when (iTime seconds), w = 1 for a click, 0 for an empty slot\",\n },\n {\n name: \"iTrail\",\n type: \"vec4\",\n size: MAX_TRAIL,\n doc: \"where the pointer has just been over the canvas, newest first (about 30 points a second, so a second of path): xy = where (pixels), z = when (iTime seconds), w = 1 for a point, 0 for an empty slot\",\n },\n] as const;\n\nconst PRELUDE = `#version 300 es\nprecision highp float;\nprecision highp int;\nuniform vec3 iResolution;\nuniform float iTime;\nuniform float iTimeDelta;\nuniform int iFrame;\nuniform vec4 iMouse;\nuniform vec4 iPointer;\nuniform vec4 iClicks[${MAX_CLICKS}];\nuniform vec4 iTrail[${MAX_TRAIL}];\nout vec4 flair_FragColor;\n`;\n\nconst EPILOGUE = `\nvoid main() {\n vec4 flair_color = vec4(0.0, 0.0, 0.0, 1.0);\n mainImage(flair_color, gl_FragCoord.xy);\n flair_FragColor = vec4(flair_color.rgb, clamp(flair_color.a, 0.0, 1.0));\n}\n`;\n\n/** A full-screen triangle from `gl_VertexID` alone: no buffers or attributes. */\nexport const VERTEX_SOURCE = `#version 300 es\nvoid main() {\n vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));\n gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);\n}\n`;\n\n/** One compiler message, mapped back to the user's source. */\nexport interface CompileError {\n /** 1-based line in the user's source (0 when the driver gave none, or it is outside the source). */\n line: number;\n message: string;\n}\n\n/** A linked program and the uniform locations `drawFrame` sets. */\nexport interface CompiledShader {\n program: WebGLProgram;\n builtins: Record<string, WebGLUniformLocation | null>;\n uniforms: Map<string, WebGLUniformLocation | null>;\n}\n\nexport type CompileResult =\n { ok: true; shader: CompiledShader } | { ok: false; errors: CompileError[] };\n\n/** Thrown by the APIs that cannot carry on without a program (`ShaderFrames`, `recordGif`). */\nexport class ShaderCompileError extends Error {\n readonly errors: CompileError[];\n\n constructor(errors: CompileError[]) {\n super(formatCompileError(errors));\n this.name = \"ShaderCompileError\";\n this.errors = errors;\n }\n}\n\n/** \"The shader does not compile (line 3): 'x' : undeclared identifier\", from the first error. */\nexport function formatCompileError(errors: CompileError[]): string {\n const first = errors[0];\n return `The shader does not compile${first?.line ? ` (line ${first.line})` : \"\"}: ${first?.message ?? \"unknown error\"}`;\n}\n\n/** The complete GLSL ES 3.00 fragment shader for a `mainImage` source. */\nexport function wrapSource(source: string): string {\n // `#line 1` must follow the prelude; drop a user `#version` (the wrapper owns it).\n return `${PRELUDE}#line 1\\n${source.replace(/^\\s*#version[^\\n]*/m, \"\")}\\n${EPILOGUE}`;\n}\n\n/** Turns a driver log (\"ERROR: 0:12: 'foo' : undeclared identifier\") into line-numbered errors. */\nexport function parseCompileLog(log: string, lineCount: number): CompileError[] {\n const errors: CompileError[] = [];\n for (const raw of log.split(\"\\n\")) {\n const text = raw.trim();\n if (!text) continue;\n const m = /^(?:ERROR|WARNING):\\s*\\d+:(\\d+):\\s*(.*)$/.exec(text);\n if (m) {\n const line = Number(m[1]);\n errors.push({ line: line >= 1 && line <= lineCount ? line : 0, message: m[2]!.trim() });\n } else if (!/^\\W*$/.test(text)) {\n errors.push({ line: 0, message: text.replace(/^ERROR:\\s*/, \"\") });\n }\n }\n if (!errors.length) errors.push({ line: 0, message: \"The shader did not compile.\" });\n // Drivers often repeat the same error.\n const seen = new Set<string>();\n return errors.filter((e) => {\n const key = `${e.line}:${e.message}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nlet vertexCache = new WeakMap<WebGL2RenderingContext, WebGLShader>();\n\nfunction vertexShader(gl: WebGL2RenderingContext): WebGLShader {\n let vs = vertexCache.get(gl);\n if (!vs) {\n vs = gl.createShader(gl.VERTEX_SHADER)!;\n gl.shaderSource(vs, VERTEX_SOURCE);\n gl.compileShader(vs);\n vertexCache.set(gl, vs);\n }\n return vs;\n}\n\n/** Forgets cached GL objects. Call it after a context loss, before compiling on the restored context. */\nexport function resetGlCaches() {\n vertexCache = new WeakMap();\n}\n\n/**\n * Compiles and links a `mainImage` source. Errors come back with lines in\n * the user's source, never thrown. `controls` (from `parseControls`) says\n * which uniform locations to look up.\n */\nexport function compileShader(\n gl: WebGL2RenderingContext,\n source: string,\n controls: ControlDef[],\n): CompileResult {\n const lineCount = source.split(\"\\n\").length;\n if (gl.isContextLost()) {\n return {\n ok: false,\n errors: [\n {\n line: 0,\n message:\n \"The graphics context was lost; reload the page if the preview does not come back.\",\n },\n ],\n };\n }\n if (!/\\bmainImage\\s*\\(/.test(source)) {\n return {\n ok: false,\n errors: [\n { line: 0, message: \"Define void mainImage(out vec4 fragColor, in vec2 fragCoord).\" },\n ],\n };\n }\n const fs = gl.createShader(gl.FRAGMENT_SHADER);\n if (!fs) {\n return {\n ok: false,\n errors: [{ line: 0, message: \"WebGL could not create a shader (the context may be lost).\" }],\n };\n }\n gl.shaderSource(fs, wrapSource(source));\n gl.compileShader(fs);\n if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {\n const errors = parseCompileLog(gl.getShaderInfoLog(fs) ?? \"\", lineCount);\n gl.deleteShader(fs);\n return { ok: false, errors };\n }\n const program = gl.createProgram()!;\n gl.attachShader(program, vertexShader(gl));\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.deleteShader(fs);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const errors = parseCompileLog(gl.getProgramInfoLog(program) ?? \"\", lineCount);\n gl.deleteProgram(program);\n return { ok: false, errors };\n }\n const builtins: CompiledShader[\"builtins\"] = {};\n for (const u of BUILTIN_UNIFORMS) builtins[u.name] = gl.getUniformLocation(program, u.name);\n const uniforms = new Map(\n controls.map((c) => [c.name, gl.getUniformLocation(program, c.name)] as const),\n );\n return { ok: true, shader: { program, builtins, uniforms } };\n}\n\nconst NO_CLICKS = new Float32Array(MAX_CLICKS * 4);\nconst NO_TRAIL = new Float32Array(MAX_TRAIL * 4);\n\n/** What one frame shows: its size in pixels, its time and the pointer. */\nexport interface FrameState {\n width: number;\n height: number;\n /** `iTime`, in seconds. */\n time: number;\n /** `iTimeDelta`, in seconds. */\n delta: number;\n /** `iFrame`. */\n frame: number;\n /** `iMouse`, Shadertoy semantics. */\n mouse: [number, number, number, number];\n /** `iPointer` (default all zero: nobody is pointing). */\n pointer?: readonly [number, number, number, number];\n /** `iClicks`: {@link MAX_CLICKS} × (x, y, time, 1), newest first (default none; see `clicksUniform`). */\n clicks?: Float32Array;\n /** `iTrail`: {@link MAX_TRAIL} × (x, y, time, 1), newest first (default none; see `trailUniform`). */\n trail?: Float32Array;\n}\n\n/**\n * Draws one frame of a compiled shader into the bound framebuffer, from (0, 0).\n * Image controls are bound to texture units 0, 1, … in source order, with\n * textures from `textures` (a {@link TextureCache} for the same context): a\n * control's live source when it has one, else its URL's image. Without\n * `textures` they sample nothing.\n */\nexport function drawFrame(\n gl: WebGL2RenderingContext,\n shader: CompiledShader,\n controls: ControlDef[],\n values: Record<string, ControlValue> | undefined,\n state: FrameState,\n textures?: TextureSource,\n) {\n gl.viewport(0, 0, state.width, state.height);\n gl.useProgram(shader.program);\n const b = shader.builtins;\n if (b.iResolution) gl.uniform3f(b.iResolution, state.width, state.height, 1);\n if (b.iTime) gl.uniform1f(b.iTime, state.time);\n if (b.iTimeDelta) gl.uniform1f(b.iTimeDelta, state.delta);\n if (b.iFrame) gl.uniform1i(b.iFrame, state.frame);\n if (b.iMouse) gl.uniform4f(b.iMouse, ...state.mouse);\n if (b.iPointer) gl.uniform4f(b.iPointer, ...(state.pointer ?? [0, 0, 0, 0]));\n if (b.iClicks) gl.uniform4fv(b.iClicks, state.clicks ?? NO_CLICKS);\n if (b.iTrail) gl.uniform4fv(b.iTrail, state.trail ?? NO_TRAIL);\n let unit = 0;\n for (const control of controls) {\n const loc = shader.uniforms.get(control.name);\n if (!loc) continue;\n const v = controlValue(control, values);\n switch (control.type) {\n case \"float\":\n gl.uniform1f(loc, v as number);\n break;\n case \"int\":\n gl.uniform1i(loc, Math.round(v as number));\n break;\n case \"bool\":\n gl.uniform1i(loc, v ? 1 : 0);\n break;\n case \"vec2\":\n gl.uniform2fv(loc, v as number[]);\n break;\n case \"vec3\":\n gl.uniform3fv(loc, v as number[]);\n break;\n case \"vec4\":\n gl.uniform4fv(loc, v as number[]);\n break;\n case \"sampler2D\": {\n gl.activeTexture(gl.TEXTURE0 + unit);\n const live = textures?.liveTexture?.(control.name);\n const texture = (live === undefined ? textures?.texture(v as string) : live) ?? null;\n gl.bindTexture(gl.TEXTURE_2D, texture);\n if (texture && control.texture) applyTextureSettings(gl, control.texture);\n gl.uniform1i(loc, unit++);\n break;\n }\n }\n }\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n}\n\n/**\n * A WebGL2 context set up for full-screen fragment shaders: opaque by default,\n * no depth, stencil or antialiasing. `preserveDrawingBuffer` keeps the last\n * frame readable after the task that drew it (for captures). `alpha` enables a\n * transparent drawing buffer for overlay shaders. Null without WebGL2.\n */\nexport function createContext(\n canvas: HTMLCanvasElement | OffscreenCanvas,\n preserveDrawingBuffer = false,\n alpha = false,\n): WebGL2RenderingContext | null {\n return canvas.getContext(\"webgl2\", {\n alpha,\n antialias: false,\n depth: false,\n stencil: false,\n premultipliedAlpha: false,\n preserveDrawingBuffer,\n powerPreference: \"high-performance\",\n }) as WebGL2RenderingContext | null;\n}\n"],"mappings":"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","names":[]}