@pixerate/flair 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @pixerate/flair
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7542ad5: Interaction: pointer uniforms, `SvgPlayer` triggers and 21 interaction effects.
8
+
9
+ - Shaders get two new built-ins: `iPointer` (xy while over the canvas, eased hover amount, seconds held or minus seconds since release) and `iClicks[8]` (the latest clicks: xy, `iTime` of the click, 1). `ShaderRenderer`, standalone pages and hovered `ThumbnailPool` thumbnails feed them; `ShaderFrames.render` takes `pointer` and `clicks` for scripted recordings. `PointerTracker`, `clicksUniform`, `MAX_CLICKS` and `HOVER_SECONDS` are exported.
10
+ - `SvgPlayer` takes `trigger: "hover" | "press" | "click"` (and `triggerTarget`): hover and press play forward while active and back after; click plays the next step on each click, wrapping round. `setActive(on)` and `advance()` let hosts drive it. Vector effects declare `trigger`.
11
+ - **interaction** (new category): click ripple, click burst, cursor glow, hover liquid, X-ray reveal, magnifier, holo card, press charge and hover swap shaders (each with an idle demo), and like button, toggle switch, checkbox, menu toggle, bookmark, copy, send and download buttons, link hover, button hover fill, tooltip pop and press button vector effects.
12
+
3
13
  ## 0.9.0
4
14
 
5
15
  ### Minor Changes
package/README.md CHANGED
@@ -19,13 +19,14 @@ Flair gives products a library of good-looking, configurable visual effects (her
19
19
 
20
20
  ### Core Capabilities
21
21
 
22
- - 🎨 **Effect catalog (`@pixerate/flair/catalog`)**: 111 effects, each with controls and presets. There are 75 WebGL2 shaders (gradients, organic textures, patterns, light, space, retro, image treatments, transparent overlays, text fills, image morphs, transitions) and 36 animated SVG effects (loaders, status icons, animated titles, shape morphs, charts, counters, node graphs). [Browse them live](https://pixerate.github.io/flair/).
22
+ - 🎨 **Effect catalog (`@pixerate/flair/catalog`)**: 132 effects, each with controls and presets. There are 84 WebGL2 shaders (gradients, organic textures, patterns, light, space, retro, image treatments, transparent overlays, text fills, image morphs, pointer interactions, transitions) and 48 animated SVG effects (loaders, status icons, animated titles, shape morphs, UI micro-interactions, charts, counters, node graphs). [Browse them live](https://pixerate.github.io/flair/).
23
23
  - ⚡ **Shader engine**:
24
24
  - Shadertoy-style GLSL ES 3.00 fragment shaders, with controls declared in the source (`// @control`), including image inputs.
25
25
  - Compile errors come back with line numbers that match the source.
26
+ - Pointer input beyond Shadertoy's `iMouse`: `iPointer` (position while over the canvas, eased hover amount, seconds held or since release) and `iClicks[8]` (the latest clicks' positions and times), so effects answer hovers, presses and taps. `ShaderRenderer` and standalone pages feed them; `ShaderFrames` takes scripted `pointer` and `clicks` for recordings.
26
27
  - Live previews (`ShaderRenderer`, with optional `{ alpha: true }` for transparent overlays) and many animated or hover-activated thumbnails on one context (`ThumbnailPool`).
27
28
  - Frames at exact times (`ShaderFrames`), looping GIFs, and standalone HTML pages.
28
- - ✒️ **Vector motion (`@pixerate/flair/vector`)**: animated SVG described as data. Catalog vector effects declare their controls in the picture (`{{name: type …}}`), text included. It covers attribute tweens, draw-on strokes, motion along paths, counters (`animateCount` / `formatCount` for DOM elements too), text written letter by letter (`write`, with `scramble` and `caret`), shape morphs between any paths (`morphPaths`, `morphSequence`) and view zooms. Every frame is a pure function of time (`svgFrame`), and `SvgPlayer` plays it (with `scrollTrigger`, `hideWhenPaused`, and `reset()`).
29
+ - ✒️ **Vector motion (`@pixerate/flair/vector`)**: animated SVG described as data. Catalog vector effects declare their controls in the picture (`{{name: type …}}`), text included. It covers attribute tweens, draw-on strokes, motion along paths, counters (`animateCount` / `formatCount` for DOM elements too), text written letter by letter (`write`, with `scramble` and `caret`), shape morphs between any paths (`morphPaths`, `morphSequence`) and view zooms. Every frame is a pure function of time (`svgFrame`), and `SvgPlayer` plays it (with `scrollTrigger`, `hideWhenPaused`, `reset()`, and `trigger: "hover" | "press" | "click"` to play in answer to the pointer).
29
30
  - 🎛️ **Effect editor (`@pixerate/flair/editor`)**:
30
31
  - A headless editor for an effect's source, values, keyframes, presets and timeline.
31
32
  - Built on [`@pixerate/editor`](https://github.com/Pixerate/editor)'s undo history and command registry, with no UI framework assumed.
@@ -55,7 +56,7 @@ The `flair` CLI inspects shaders, writes standalone pages and directs scenes (se
55
56
  ```mermaid
56
57
  graph TD
57
58
  subgraph Effects["Effects (no dependencies)"]
58
- Catalog["catalog: 111 effects + presets"]
59
+ Catalog["catalog: 132 effects + presets"]
59
60
  Shader["shader engine: controls, compile, render, GIF, HTML"]
60
61
  Vector["vector motion: SvgMotion, SvgPlayer"]
61
62
  Time["time: clocks, easing, keyframes, noise"]
@@ -131,7 +132,9 @@ animateCount({
131
132
 
132
133
  Find effects with `findEffects({ text: "glow", category: "light", kind: "shader" })`. Each effect has an `id`, `name`, `description`, `category`, `tags` and optional `presets`. Image effects default to built-in sample pictures, so they look right before you set `uImage` to your own URL.
133
134
 
134
- Overlay shaders (`category: "overlay"`) have `transparent: true`: they write coverage to `fragColor.a` and draw nothing elsewhere, so render them with `new ShaderRenderer(host, { alpha: true })` (or `new ThumbnailPool({ alpha: true })`) and lay the canvas over a picture, video or page. To morph between any two SVG shapes, `morphPaths(from, to)` (from `@pixerate/flair/vector`) returns two paths with one structure, so `attrs: { d: [a, b] }` blends them point by point: curves and arcs are read, pieces are paired by position and size, and a piece with no partner grows from or shrinks to a point (draw them with `fill-rule="evenodd"`). `morphSequence` does the same for three or more shapes. `feature-morph` morphs two photos from five matching points, and `silhouette-morph` morphs any two silhouettes or logos (or words, through `textImage`).
135
+ Overlay shaders (`category: "overlay"`) have `transparent: true`: they write coverage to `fragColor.a` and draw nothing elsewhere, so render them with `new ShaderRenderer(host, { alpha: true })` (or `new ThumbnailPool({ alpha: true })`) and lay the canvas over a picture, video or page. Interaction effects answer the pointer. Interactive shaders read `iPointer` and `iClicks`, so they work in any `ShaderRenderer` with no wiring, and play an idle demo when nobody is pointing (turn off "Demo when idle" to stop it). Vector micro-interactions carry a `trigger`: pass it to the player, `new SvgPlayer(host, renderVectorEffect(effect), { trigger: effect.trigger })`. A hover or press trigger plays to the end while hovered or held and back again after; a click trigger plays the next step on each click, so toggles turn on and off. Hosts can drive them too, with `player.setActive(on)` and `player.advance()` (from a keyboard handler, say).
136
+
137
+ To morph between any two SVG shapes, `morphPaths(from, to)` (from `@pixerate/flair/vector`) returns two paths with one structure, so `attrs: { d: [a, b] }` blends them point by point: curves and arcs are read, pieces are paired by position and size, and a piece with no partner grows from or shrinks to a point (draw them with `fill-rule="evenodd"`). `morphSequence` does the same for three or more shapes. `feature-morph` morphs two photos from five matching points, and `silhouette-morph` morphs any two silhouettes or logos (or words, through `textImage`).
135
138
 
136
139
  `shader-text` fills letters from a text image: set its `uText` control to `textImage("Your title", { weight: 900 })`, a transparent SVG `data:` URL of any Unicode text.
137
140
 
@@ -273,6 +276,7 @@ The exporters carry the camera too:
273
276
  | overlay | rain on glass, snowfall, lens flare, vignette & grain, scanline sweep, spotlight, embers, shine sweep, frost creep, viewfinder |
274
277
  | text | shader text, drip text, typewriter, scramble text, kinetic stagger, neon sign, stroke draw text, split reveal, highlighter, word cycle, liquid fill, glitch text, extruded text |
275
278
  | morph | feature morph, silhouette morph, shape morph, icon toggle, morph sequence, gooey morph, particle morph, digit morph |
279
+ | interaction | click ripple, click burst, cursor glow, hover liquid, X-ray reveal, magnifier, holo card, press charge, hover swap, like button, toggle switch, checkbox, menu toggle, bookmark, copy button, send button, download button, link hover, button hover fill, tooltip pop, press button |
276
280
  | transition | liquid morph, displacement, luma fade, particle swap, ink bleed, wipe, iris, block dissolve, grid wipe, glass wipe, twister, light leak, cross zoom |
277
281
  | loader / icon / data | spinner, bouncing dots, pulse ping, equalizer, progress ring, bar chart, line chart, stat counter, route, timeline track, success check, error cross, heartbeat, signature, confetti, flow network |
278
282
 
@@ -1,7 +1,7 @@
1
1
  import { a as ControlDef, C as ControlValue } from '../controls-Syr6drzO.js';
2
- import { E as EffectDefinition, a as EffectCategory, S as ShaderEffect } from '../types-CgHG5JLv.js';
3
- export { b as EFFECT_CATEGORIES, c as EffectPreset, V as VECTOR_CONTROL_TYPES, d as VectorControl, e as VectorEffect, f as colorControl, n as numberControl, r as renderVectorEffect, v as vectorControlDef, g as vectorControlIssues, h as vectorControls, i as vectorEffectControls } from '../types-CgHG5JLv.js';
4
- import '../motion-Ce0lmzm3.js';
2
+ import { E as EffectDefinition, a as EffectCategory, S as ShaderEffect } from '../types-D8uhzDyN.js';
3
+ export { b as EFFECT_CATEGORIES, c as EffectPreset, V as VECTOR_CONTROL_TYPES, d as VectorControl, e as VectorEffect, f as colorControl, n as numberControl, r as renderVectorEffect, v as vectorControlDef, g as vectorControlIssues, h as vectorControls, i as vectorEffectControls } from '../types-D8uhzDyN.js';
4
+ import '../svg-CpeanaE_.js';
5
5
 
6
6
  /**
7
7
  * Sample pictures for the catalog's image effects: small SVGs as `data:` URLs,
@@ -24,7 +24,7 @@ import {
24
24
  vectorControlIssues,
25
25
  vectorControls,
26
26
  vectorEffectControls
27
- } from "../chunk-F754SFRY.js";
27
+ } from "../chunk-STGGN4MR.js";
28
28
  import "../chunk-OHW7UG6D.js";
29
29
  import "../chunk-EHDJ3CPJ.js";
30
30
  import "../chunk-WC3VADZC.js";
@@ -1,14 +1,18 @@
1
1
  import {
2
+ HOVER_SECONDS,
3
+ MAX_CLICKS,
4
+ PointerTracker,
2
5
  SVG_EDGE,
3
6
  ShaderCompileError,
4
7
  TextureCache,
5
8
  VERTEX_SOURCE,
9
+ clicksUniform,
6
10
  compileShader,
7
11
  createContext,
8
12
  drawFrame,
9
13
  resetGlCaches,
10
14
  wrapSource
11
- } from "./chunk-EZRRKQFV.js";
15
+ } from "./chunk-YLJWC4N2.js";
12
16
  import {
13
17
  CAMERA_MS,
14
18
  CAMERA_STEP_MS,
@@ -45,6 +49,8 @@ var ShaderRenderer = class {
45
49
  wasPlayingBeforeHidden = false;
46
50
  hasEnteredViewport = false;
47
51
  mouse = [0, 0, 0, 0];
52
+ /** `iPointer` and `iClicks`. */
53
+ pointer = new PointerTracker();
48
54
  disposed = false;
49
55
  /** The latest source's status. */
50
56
  status = { ok: true, errors: [] };
@@ -112,6 +118,8 @@ var ShaderRenderer = class {
112
118
  canvas.addEventListener("webglcontextrestored", this.onRestored);
113
119
  canvas.addEventListener("pointerdown", this.onPointerDown);
114
120
  canvas.addEventListener("pointermove", this.onPointerMove);
121
+ canvas.addEventListener("pointerenter", this.onPointerEnter);
122
+ canvas.addEventListener("pointerleave", this.onPointerLeave);
115
123
  window.addEventListener("pointerup", this.onPointerUp);
116
124
  this.resize();
117
125
  this.raf = requestAnimationFrame(this.tick);
@@ -172,12 +180,14 @@ var ShaderRenderer = class {
172
180
  /** Back to `iTime` 0 and frame 0. */
173
181
  restart() {
174
182
  this.time = 0;
183
+ this.pointer.clear();
175
184
  this.frame = 0;
176
185
  this.render();
177
186
  this.emit();
178
187
  }
179
188
  /** Jumps to `time` seconds (not below 0) and draws it. */
180
189
  seek(time) {
190
+ if (time < this.time) this.pointer.clear();
181
191
  this.time = Math.max(0, time);
182
192
  this.render();
183
193
  this.emit();
@@ -217,6 +227,8 @@ var ShaderRenderer = class {
217
227
  this.canvas.removeEventListener("webglcontextrestored", this.onRestored);
218
228
  this.canvas.removeEventListener("pointerdown", this.onPointerDown);
219
229
  this.canvas.removeEventListener("pointermove", this.onPointerMove);
230
+ this.canvas.removeEventListener("pointerenter", this.onPointerEnter);
231
+ this.canvas.removeEventListener("pointerleave", this.onPointerLeave);
220
232
  window.removeEventListener("pointerup", this.onPointerUp);
221
233
  if (this.gl && this.shader) this.gl.deleteProgram(this.shader.program);
222
234
  this.textures?.dispose();
@@ -247,7 +259,9 @@ var ShaderRenderer = class {
247
259
  time: this.time,
248
260
  delta,
249
261
  frame: this.frame,
250
- mouse: this.mouse
262
+ mouse: this.mouse,
263
+ pointer: this.pointer.pointer(this.time),
264
+ clicks: this.pointer.clicksAt(this.time)
251
265
  },
252
266
  this.textures ?? void 0
253
267
  );
@@ -303,15 +317,28 @@ var ShaderRenderer = class {
303
317
  onPointerDown = (e) => {
304
318
  const [x, y] = this.toPixels(e);
305
319
  this.mouse = [x, y, x, y];
320
+ this.pointer.down(x, y, this.time);
306
321
  if (!this.playing) this.render();
307
322
  };
308
323
  onPointerMove = (e) => {
309
- if (this.mouse[2] <= 0 || e.buttons === 0) return;
310
324
  const [x, y] = this.toPixels(e);
325
+ this.pointer.move(x, y);
326
+ if (this.mouse[2] <= 0 || e.buttons === 0) {
327
+ if (!this.playing) this.render();
328
+ return;
329
+ }
311
330
  this.mouse = [x, y, this.mouse[2], this.mouse[3]];
312
331
  if (!this.playing) this.render();
313
332
  };
333
+ onPointerEnter = (e) => {
334
+ this.pointer.move(...this.toPixels(e));
335
+ this.pointer.hover(true, this.time);
336
+ };
337
+ onPointerLeave = () => {
338
+ this.pointer.hover(false, this.time);
339
+ };
314
340
  onPointerUp = () => {
341
+ this.pointer.up(this.time);
315
342
  if (this.mouse[2] > 0)
316
343
  this.mouse = [this.mouse[0], this.mouse[1], -this.mouse[2], -Math.abs(this.mouse[3])];
317
344
  };
@@ -495,7 +522,9 @@ var ThumbnailPool = class {
495
522
  time,
496
523
  delta: this.interval / 1e3,
497
524
  frame: Math.floor(time * this.fps),
498
- mouse: [0, 0, 0, 0]
525
+ mouse: [0, 0, 0, 0],
526
+ // A hovered thumbnail is pointed at in its middle, for effects that answer hovers.
527
+ pointer: [w / 2, h / 2, entry.hovered ? 1 : 0, 0]
499
528
  },
500
529
  this.textures ?? void 0
501
530
  );
@@ -618,7 +647,9 @@ var ShaderFrames = class {
618
647
  time,
619
648
  delta,
620
649
  frame: options.frame ?? this.rendered,
621
- mouse: options.mouse ?? [0, 0, 0, 0]
650
+ mouse: options.mouse ?? [0, 0, 0, 0],
651
+ pointer: options.pointer,
652
+ clicks: options.clicks && clicksUniform(options.clicks, time)
622
653
  },
623
654
  this.textures
624
655
  );
@@ -793,6 +824,16 @@ canvas.addEventListener("pointerdown", (e) => { const [x, y] = px(e); mouse = [x
793
824
  canvas.addEventListener("pointermove", (e) => { if (e.buttons && mouse[2] > 0) { const [x, y] = px(e); mouse = [x, y, mouse[2], mouse[3]]; } });
794
825
  addEventListener("pointerup", () => { if (mouse[2] > 0) mouse = [mouse[0], mouse[1], -mouse[2], -Math.abs(mouse[3])]; });
795
826
  let start = performance.now(), last = start, frame = 0;
827
+ // iPointer (where, how hovered, seconds pressed) and iClicks (the latest clicks, newest first).
828
+ const seconds = () => (performance.now() - start) / 1000;
829
+ let at = [0, 0], over = false, hoverFrom = 0, hoverAt = -1e9, pressedAt = -1, releasedAt = -1, clicks = [];
830
+ const hovered = (t) => { const k = Math.min(1, Math.max(0, (t - hoverAt) / ${HOVER_SECONDS})); return hoverFrom + ((over ? 1 : 0) - hoverFrom) * k * k * (3 - 2 * k); };
831
+ const hover = (on) => { const t = seconds(); hoverFrom = hovered(t); hoverAt = t; over = on; };
832
+ canvas.addEventListener("pointerenter", (e) => { at = px(e); hover(true); });
833
+ canvas.addEventListener("pointerleave", () => hover(false));
834
+ canvas.addEventListener("pointermove", (e) => { at = px(e); });
835
+ canvas.addEventListener("pointerdown", (e) => { at = px(e); pressedAt = seconds(); clicks.unshift([at[0], at[1], pressedAt, 1]); clicks = clicks.slice(0, ${MAX_CLICKS}); });
836
+ addEventListener("pointerup", () => { if (pressedAt >= 0) releasedAt = seconds(); pressedAt = -1; });
796
837
  function draw(now) {
797
838
  const dpr = Math.min(devicePixelRatio || 1, 2);
798
839
  const w = Math.round(canvas.clientWidth * dpr), h = Math.round(canvas.clientHeight * dpr);
@@ -803,6 +844,11 @@ function draw(now) {
803
844
  gl.uniform1f(u("iTimeDelta"), (now - last) / 1000);
804
845
  gl.uniform1i(u("iFrame"), frame++);
805
846
  gl.uniform4f(u("iMouse"), mouse[0], mouse[1], mouse[2], mouse[3]);
847
+ const t = (now - start) / 1000;
848
+ gl.uniform4f(u("iPointer"), at[0], at[1], hovered(t), pressedAt >= 0 ? Math.max(0, t - pressedAt) : releasedAt >= 0 ? -Math.max(1e-3, t - releasedAt) : 0);
849
+ const c = new Float32Array(${MAX_CLICKS * 4});
850
+ clicks.forEach((k, i) => c.set(k, i * 4));
851
+ gl.uniform4fv(u("iClicks"), c);
806
852
  gl.drawArrays(gl.TRIANGLES, 0, 3);
807
853
  last = now;
808
854
  requestAnimationFrame(draw);
@@ -1563,4 +1609,4 @@ export {
1563
1609
  validateScene,
1564
1610
  standaloneSceneHtml
1565
1611
  };
1566
- //# sourceMappingURL=chunk-EQS4EXNM.js.map
1612
+ //# sourceMappingURL=chunk-SH66CNLC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shader/renderer.ts","../src/shader/thumbnails.ts","../src/shader/frames.ts","../src/shader/gif.ts","../src/shader/standalone.ts","../src/shader/blend.ts","../src/scene/validate.ts","../src/scene/standalone.ts"],"sourcesContent":["import { imageUrls, parseControls, type ControlDef, type ControlValue } from \"./controls.js\";\nimport {\n compileShader,\n createContext,\n drawFrame,\n resetGlCaches,\n type CompileError,\n type CompiledShader,\n} from \"./program.js\";\nimport { PointerTracker } from \"./pointer.js\";\nimport { TextureCache, type ImageLoadError } from \"./textures.js\";\n\n/** Whether the latest source compiled, and if not, why. */\nexport interface CompileStatus {\n ok: boolean;\n errors: CompileError[];\n}\n\n/** What `onStats` reports: on play, pause, seek and restart, and twice a second while running. */\nexport interface RendererStats {\n /** `iTime`, in seconds. */\n time: number;\n /** Frames drawn per second over the last half second (0 while paused). */\n fps: number;\n playing: boolean;\n /** The drawing buffer's size in device pixels. */\n width: number;\n height: number;\n}\n\nexport interface ShaderRendererOptions {\n className?: string;\n /** Enable a transparent WebGL drawing buffer so shader alpha (`fragColor.a`) composites over the page (default false). */\n alpha?: boolean;\n /** Pause rendering when scrolled out of view via IntersectionObserver (default false). */\n pauseWhenHidden?: boolean;\n /** Start playing at once (default true). Set to false to start paused. */\n autoplay?: boolean;\n /** Wait to start playing until the canvas enters the viewport via IntersectionObserver (default false). When true, autoplay is deferred until first visible. */\n playOnEnter?: boolean;\n}\n\n/**\n * Runs one shader on a canvas: the editor's live preview. A source that does\n * not compile leaves the last working program on screen, so a typo never\n * blanks the preview; the errors are reported instead.\n *\n * The renderer owns its canvas (added to `host`, removed on dispose): a\n * canvas keeps its WebGL context for life, so reusing one after disposing\n * (a remount, hot reload) would get the released, lost context back.\n */\nexport class ShaderRenderer {\n readonly canvas: HTMLCanvasElement;\n private gl: WebGL2RenderingContext | null;\n private readonly alpha: boolean;\n private shader: CompiledShader | null = null;\n private textures: TextureCache | null = null;\n private controls: ControlDef[] = [];\n private values: Record<string, ControlValue> | undefined;\n private source = \"\";\n private raf = 0;\n private lastNow = 0;\n private frame = 0;\n private fpsFrames = 0;\n private fpsSince = 0;\n private ownsCanvas = false;\n private resizeObserver: ResizeObserver;\n private intersectionObserver?: IntersectionObserver;\n private wasPlayingBeforeHidden = false;\n private hasEnteredViewport = false;\n private mouse: [number, number, number, number] = [0, 0, 0, 0];\n /** `iPointer` and `iClicks`. */\n private pointer = new PointerTracker();\n private disposed = false;\n /** The latest source's status. */\n status: CompileStatus = { ok: true, errors: [] };\n /** `iTime`, in seconds. Use `seek` to change it. */\n time = 0;\n /** Playback speed: 1 is real time. */\n speed = 1;\n /** Device pixels per CSS pixel; lower renders faster. Use `setPixelRatio` to change it. */\n pixelRatio = Math.min(window.devicePixelRatio || 1, 2);\n playing = true;\n fps = 0;\n onStats?: (stats: RendererStats) => void;\n /** Called with `true` when the GL context is lost and `false` once it is restored and recompiled. */\n onContextLost?: (lost: boolean) => void;\n /** Called when an image control's URL cannot be loaded; the control shows black. */\n onImageError?: (error: ImageLoadError) => void;\n\n /**\n * Creates a canvas in `target` (with `className` or options), or attaches to an existing\n * `HTMLCanvasElement`, and starts the animation loop. Size the canvas with CSS.\n */\n constructor(target: HTMLElement | HTMLCanvasElement, options?: string | ShaderRendererOptions) {\n const opts: ShaderRendererOptions =\n typeof options === \"string\" ? { className: options } : (options ?? {});\n let canvas: HTMLCanvasElement;\n if (target instanceof HTMLCanvasElement || (\"tagName\" in target && target.tagName === \"CANVAS\")) {\n canvas = target as HTMLCanvasElement;\n if (opts.className) canvas.className = opts.className;\n } else {\n canvas = document.createElement(\"canvas\");\n if (opts.className) canvas.className = opts.className;\n target.appendChild(canvas);\n this.ownsCanvas = true;\n }\n this.canvas = canvas;\n this.alpha = Boolean(opts.alpha);\n this.gl = createContext(canvas, true, this.alpha);\n this.textures = this.gl && this.textureCache(this.gl);\n this.resizeObserver = new ResizeObserver(() => this.resize());\n this.resizeObserver.observe(canvas);\n\n const needsObserver =\n (opts.pauseWhenHidden || opts.playOnEnter) && typeof IntersectionObserver !== \"undefined\";\n\n if (needsObserver) {\n this.intersectionObserver = new IntersectionObserver(([entry]) => {\n if (!entry) return;\n if (entry.isIntersecting) {\n if (opts.playOnEnter && !this.hasEnteredViewport) {\n this.hasEnteredViewport = true;\n if (opts.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 && opts.pauseWhenHidden) {\n this.wasPlayingBeforeHidden = true;\n this.pause();\n }\n }\n });\n this.intersectionObserver.observe(canvas);\n }\n\n const shouldAutoplay = (opts.autoplay ?? true) && !opts.playOnEnter;\n this.playing = shouldAutoplay;\n\n canvas.addEventListener(\"webglcontextlost\", this.onLost);\n canvas.addEventListener(\"webglcontextrestored\", this.onRestored);\n canvas.addEventListener(\"pointerdown\", this.onPointerDown);\n canvas.addEventListener(\"pointermove\", this.onPointerMove);\n canvas.addEventListener(\"pointerenter\", this.onPointerEnter);\n canvas.addEventListener(\"pointerleave\", this.onPointerLeave);\n window.addEventListener(\"pointerup\", this.onPointerUp);\n this.resize();\n this.raf = requestAnimationFrame(this.tick);\n }\n\n /** False when the browser has no WebGL2. */\n get supported(): boolean {\n return this.gl !== null;\n }\n\n /** Compiles a new source; returns its status. Keeps the previous program when it fails. */\n setSource(source: string): CompileStatus {\n this.source = source;\n const controls = parseControls(source);\n if (!this.gl) {\n this.status = {\n ok: false,\n errors: [{ line: 0, message: \"WebGL2 is not available in this browser.\" }],\n };\n return this.status;\n }\n const result = compileShader(this.gl, source, controls);\n if (result.ok) {\n if (this.shader) this.gl.deleteProgram(this.shader.program);\n this.shader = result.shader;\n this.controls = controls;\n this.status = { ok: true, errors: [] };\n } else {\n this.status = { ok: false, errors: result.errors };\n }\n this.syncTextures();\n if (!this.playing) this.render();\n return this.status;\n }\n\n /**\n * Sets control values (see `controlValue`); a paused preview redraws.\n * Images load in the background and show once they arrive: await\n * `ready()` before a `capture` that needs them.\n */\n setValues(values: Record<string, ControlValue> | undefined) {\n this.values = values;\n this.syncTextures();\n if (!this.playing) this.render();\n }\n\n /**\n * Settles once the images the current controls show have loaded. Rejects\n * with an {@link ImageLoadError} when one cannot be (it shows black).\n */\n async ready(): Promise<void> {\n await this.textures?.ready(imageUrls(this.controls, this.values));\n }\n\n play() {\n this.playing = true;\n this.emit();\n }\n\n pause() {\n this.playing = false;\n this.emit();\n }\n\n /** Back to `iTime` 0 and frame 0. */\n restart() {\n this.time = 0;\n this.pointer.clear();\n this.frame = 0;\n this.render();\n this.emit();\n }\n\n /** Jumps to `time` seconds (not below 0) and draws it. */\n seek(time: number) {\n // Clicks are stamped with iTime: going back would replay them.\n if (time < this.time) this.pointer.clear();\n this.time = Math.max(0, time);\n this.render();\n this.emit();\n }\n\n setPixelRatio(ratio: number) {\n this.pixelRatio = ratio;\n this.resize();\n }\n\n /**\n * Captures the current frame (optionally at another time) as a data URL\n * (JPEG unless `type` says PNG), at most `maxEdge` pixels on the long side\n * (default 1024). Playback time is left as it was. Null when nothing has\n * compiled yet.\n */\n capture(\n options: { time?: number; maxEdge?: number; type?: \"image/jpeg\" | \"image/png\" } = {},\n ): string | null {\n if (!this.gl || !this.shader) return null;\n const saved = this.time;\n if (options.time !== undefined) this.time = Math.max(0, options.time);\n this.render();\n const { width, height } = this.canvas;\n const scale = Math.min(1, (options.maxEdge ?? 1024) / Math.max(width, height));\n const out = document.createElement(\"canvas\");\n out.width = Math.max(1, Math.round(width * scale));\n out.height = Math.max(1, Math.round(height * scale));\n out.getContext(\"2d\")!.drawImage(this.canvas, 0, 0, out.width, out.height);\n this.time = saved;\n if (options.time !== undefined) this.render();\n return out.toDataURL(options.type ?? \"image/jpeg\", 0.85);\n }\n\n /** Stops the loop, releases the context and removes the canvas. The renderer cannot be used again. */\n dispose() {\n this.disposed = true;\n cancelAnimationFrame(this.raf);\n this.resizeObserver.disconnect();\n this.intersectionObserver?.disconnect();\n this.canvas.removeEventListener(\"webglcontextlost\", this.onLost);\n this.canvas.removeEventListener(\"webglcontextrestored\", this.onRestored);\n this.canvas.removeEventListener(\"pointerdown\", this.onPointerDown);\n this.canvas.removeEventListener(\"pointermove\", this.onPointerMove);\n this.canvas.removeEventListener(\"pointerenter\", this.onPointerEnter);\n this.canvas.removeEventListener(\"pointerleave\", this.onPointerLeave);\n window.removeEventListener(\"pointerup\", this.onPointerUp);\n if (this.gl && this.shader) this.gl.deleteProgram(this.shader.program);\n this.textures?.dispose();\n this.textures = null;\n // Free the context now rather than waiting for GC (browsers cap live contexts).\n this.gl?.getExtension(\"WEBGL_lose_context\")?.loseContext();\n this.gl = null;\n if (this.ownsCanvas) this.canvas.remove();\n }\n\n private resize() {\n // The layout size, not the bounding box: a rotated or scaled canvas keeps its own proportions.\n const w = Math.max(1, Math.round(this.canvas.clientWidth * this.pixelRatio));\n const h = Math.max(1, Math.round(this.canvas.clientHeight * this.pixelRatio));\n if (this.canvas.width !== w || this.canvas.height !== h) {\n this.canvas.width = w;\n this.canvas.height = h;\n this.render();\n }\n }\n\n private render(delta = 0) {\n if (!this.gl || !this.shader || this.gl.isContextLost()) return;\n drawFrame(\n this.gl,\n this.shader,\n this.controls,\n this.values,\n {\n width: this.canvas.width,\n height: this.canvas.height,\n time: this.time,\n delta,\n frame: this.frame,\n mouse: this.mouse,\n pointer: this.pointer.pointer(this.time),\n clicks: this.pointer.clicksAt(this.time),\n },\n this.textures ?? undefined,\n );\n }\n\n private textureCache(gl: WebGL2RenderingContext): TextureCache {\n return new TextureCache(gl, (_url, error) => {\n if (error) this.onImageError?.(error);\n else if (!this.playing) this.render();\n });\n }\n\n /** Starts loading the images now shown, and frees the ones no longer shown. */\n private syncTextures() {\n if (!this.textures) return;\n const urls = imageUrls(this.controls, this.values);\n this.textures.retain(urls);\n for (const url of urls) this.textures.request(url);\n }\n\n private tick = (now: number) => {\n if (this.disposed) return;\n this.raf = requestAnimationFrame(this.tick);\n const delta = this.lastNow ? Math.min((now - this.lastNow) / 1000, 0.1) : 0;\n this.lastNow = now;\n if (this.playing) {\n this.time += delta * this.speed;\n this.frame += 1;\n this.render(delta * this.speed);\n this.fpsFrames += 1;\n }\n if (now - this.fpsSince >= 500) {\n this.fps = this.playing\n ? Math.round((this.fpsFrames * 1000) / (now - this.fpsSince || 1))\n : 0;\n this.fpsFrames = 0;\n this.fpsSince = now;\n this.emit();\n }\n };\n\n private emit() {\n this.onStats?.({\n time: this.time,\n fps: this.fps,\n playing: this.playing,\n width: this.canvas.width,\n height: this.canvas.height,\n });\n }\n\n /** Pointer position in shader pixels (origin bottom-left, like gl_FragCoord). */\n private toPixels(e: PointerEvent): [number, number] {\n const rect = this.canvas.getBoundingClientRect();\n return [\n (e.clientX - rect.left) * (this.canvas.width / rect.width),\n (rect.bottom - e.clientY) * (this.canvas.height / rect.height),\n ];\n }\n\n private onPointerDown = (e: PointerEvent) => {\n const [x, y] = this.toPixels(e);\n this.mouse = [x, y, x, y];\n this.pointer.down(x, y, this.time);\n if (!this.playing) this.render();\n };\n\n private onPointerMove = (e: PointerEvent) => {\n const [x, y] = this.toPixels(e);\n this.pointer.move(x, y);\n if (this.mouse[2] <= 0 || e.buttons === 0) {\n if (!this.playing) this.render();\n return;\n }\n this.mouse = [x, y, this.mouse[2], this.mouse[3]];\n if (!this.playing) this.render();\n };\n\n private onPointerEnter = (e: PointerEvent) => {\n this.pointer.move(...this.toPixels(e));\n this.pointer.hover(true, this.time);\n };\n\n private onPointerLeave = () => {\n this.pointer.hover(false, this.time);\n };\n\n private onPointerUp = () => {\n this.pointer.up(this.time);\n if (this.mouse[2] > 0)\n this.mouse = [this.mouse[0], this.mouse[1], -this.mouse[2], -Math.abs(this.mouse[3])];\n };\n\n private onLost = (e: Event) => {\n e.preventDefault();\n this.shader = null;\n this.textures?.dispose();\n this.textures = null;\n this.onContextLost?.(true);\n };\n\n private onRestored = () => {\n resetGlCaches();\n this.gl = createContext(this.canvas, true, this.alpha);\n this.textures = this.gl && this.textureCache(this.gl);\n this.setSource(this.source);\n this.onContextLost?.(false);\n };\n}\n","import { imageUrls, parseControls, type ControlDef, type ControlValue } from \"./controls.js\";\nimport {\n compileShader,\n createContext,\n drawFrame,\n resetGlCaches,\n type CompiledShader,\n} from \"./program.js\";\nimport { TextureCache } from \"./textures.js\";\n\n/** Controls a registered thumbnail. */\nexport interface ThumbnailHandle {\n /** Shows another source or other values. A new source is recompiled; new values only redraw. */\n update(source: string, values?: Record<string, ControlValue>): void;\n /** With reduced motion, a hovered thumbnail animates and the others hold a still frame. */\n setHovered(hovered: boolean): void;\n /** Stops drawing into the canvas and frees its program. */\n dispose(): void;\n}\n\nexport interface ThumbnailPoolOptions {\n /** Size of the shared render canvas (default 480 × 270). Thumbnails are drawn at most this large, then scaled. */\n renderWidth?: number;\n renderHeight?: number;\n /** Animation rate (default 30). */\n fps?: number;\n /** `iTime` of the still frame shown with reduced motion or hover-only mode (default 2). */\n stillTime?: number;\n /** Hold still frames unless hovered (default: the `prefers-reduced-motion` media query). */\n reducedMotion?: boolean;\n /** Only animate hovered thumbnails, holding `stillTime` on all others (default false). */\n hoverOnly?: boolean;\n /** Animation policy: `\"always\"` (animate all visible), `\"hover\"` (only animate hovered), or `\"reduced-motion\"` (default: follow `reducedMotion` / `prefers-reduced-motion`). */\n animateMode?: \"always\" | \"hover\" | \"reduced-motion\";\n /** Enable a transparent WebGL drawing buffer and clear each thumbnail canvas before copying (default false). */\n alpha?: boolean;\n}\n\ninterface Entry {\n canvas: HTMLCanvasElement;\n ctx: CanvasRenderingContext2D;\n source: string;\n values?: Record<string, ControlValue>;\n controls: ControlDef[];\n shader: CompiledShader | null;\n failed: boolean;\n dirty: boolean;\n visible: boolean;\n hovered: boolean;\n onStatus?: (ok: boolean) => void;\n}\n\n/**\n * Animated thumbnails from a single WebGL context. Browsers cap live WebGL\n * contexts (about 16), so a gallery cannot give every card its own: the pool\n * renders each visible thumbnail in turn on one hidden canvas and copies the\n * frame into the thumbnail's own 2D canvas, cropped to its aspect ratio like\n * `object-fit: cover`. Thumbnails off screen (by `IntersectionObserver`) cost\n * nothing, and programs compile once per source.\n *\n * Nothing is created until the first `register`.\n */\nexport class ThumbnailPool {\n private readonly renderWidth: number;\n private readonly renderHeight: number;\n private readonly interval: number;\n private readonly fps: number;\n private readonly stillTime: number;\n private readonly reducedMotion: boolean;\n private readonly alpha: boolean;\n private readonly entries = new Set<Entry>();\n private readonly byCanvas = new WeakMap<Element, Entry>();\n private observer: IntersectionObserver | null | undefined;\n private glCanvas: HTMLCanvasElement | null = null;\n private gl: WebGL2RenderingContext | null = null;\n private textures: TextureCache | null = null;\n private raf = 0;\n private last = 0;\n private start: number | null = null;\n\n constructor(options: ThumbnailPoolOptions = {}) {\n this.renderWidth = options.renderWidth ?? 480;\n this.renderHeight = options.renderHeight ?? 270;\n this.fps = options.fps ?? 30;\n this.interval = 1000 / this.fps;\n this.stillTime = options.stillTime ?? 2;\n this.alpha = Boolean(options.alpha);\n const mode = options.animateMode ?? (options.hoverOnly ? \"hover\" : \"reduced-motion\");\n this.reducedMotion =\n mode === \"hover\"\n ? true\n : mode === \"always\"\n ? false\n : (options.reducedMotion ??\n (typeof matchMedia !== \"undefined\" &&\n matchMedia(\"(prefers-reduced-motion: reduce)\").matches));\n }\n\n /**\n * Starts drawing `source` into `canvas`, which should be sized with CSS; its\n * drawing buffer follows its CSS size and the device pixel ratio (up to 2).\n * `onStatus` hears whether the source compiled (and `false` without WebGL2).\n */\n register(\n canvas: HTMLCanvasElement,\n source: string,\n values?: Record<string, ControlValue>,\n onStatus?: (ok: boolean) => void,\n ): ThumbnailHandle {\n const observer = this.intersection();\n const entry: Entry = {\n canvas,\n ctx: canvas.getContext(\"2d\")!,\n source,\n values,\n controls: [],\n shader: null,\n failed: false,\n dirty: true,\n visible: observer === null,\n hovered: false,\n onStatus,\n };\n this.entries.add(entry);\n this.byCanvas.set(canvas, entry);\n observer?.observe(canvas);\n if (!this.context()) onStatus?.(false);\n this.schedule();\n return {\n update: (nextSource, nextValues) => {\n if (nextSource !== entry.source) {\n this.release(entry);\n entry.source = nextSource;\n entry.failed = false;\n }\n entry.values = nextValues;\n entry.dirty = true;\n this.retainTextures();\n this.schedule();\n },\n setHovered: (hovered) => {\n entry.hovered = hovered;\n entry.dirty = true;\n this.schedule();\n },\n dispose: () => {\n observer?.unobserve(canvas);\n this.entries.delete(entry);\n this.byCanvas.delete(canvas);\n this.release(entry);\n this.retainTextures();\n },\n };\n }\n\n /** The number of registered thumbnails. */\n get size(): number {\n return this.entries.size;\n }\n\n private intersection(): IntersectionObserver | null {\n if (this.observer !== undefined) return this.observer;\n this.observer =\n typeof IntersectionObserver === \"undefined\"\n ? null\n : new IntersectionObserver(\n (records) => {\n for (const r of records) {\n const entry = this.byCanvas.get(r.target);\n if (!entry) continue;\n entry.visible = r.isIntersecting;\n if (entry.visible) entry.dirty = true;\n }\n this.schedule();\n },\n { rootMargin: \"100px\" },\n );\n return this.observer;\n }\n\n private context(): WebGL2RenderingContext | null {\n if (this.gl && !this.gl.isContextLost()) return this.gl;\n const canvas = document.createElement(\"canvas\");\n canvas.width = this.renderWidth;\n canvas.height = this.renderHeight;\n canvas.addEventListener(\"webglcontextlost\", (e) => {\n e.preventDefault();\n for (const entry of this.entries) entry.shader = null;\n this.textures?.dispose();\n });\n canvas.addEventListener(\"webglcontextrestored\", () => {\n resetGlCaches();\n if (this.gl) this.textures = this.textureCache(this.gl);\n for (const entry of this.entries) entry.dirty = true;\n this.schedule();\n });\n resetGlCaches();\n this.glCanvas = canvas;\n this.gl = createContext(canvas, false, this.alpha);\n this.textures = this.gl && this.textureCache(this.gl);\n return this.gl;\n }\n\n private textureCache(gl: WebGL2RenderingContext): TextureCache {\n return new TextureCache(gl, (url) => {\n // A thumbnail showing the image redraws with it.\n for (const entry of this.entries) {\n if (imageUrls(entry.controls, entry.values).includes(url)) entry.dirty = true;\n }\n this.schedule();\n });\n }\n\n /** Frees the textures no thumbnail shows any more. */\n private retainTextures() {\n this.textures?.retain(\n [...this.entries].flatMap((entry) => imageUrls(entry.controls, entry.values)),\n );\n }\n\n private release(entry: Entry) {\n if (entry.shader) this.gl?.deleteProgram(entry.shader.program);\n entry.shader = null;\n }\n\n private compile(entry: Entry, gl: WebGL2RenderingContext) {\n entry.controls = parseControls(entry.source);\n const result = compileShader(gl, entry.source, entry.controls);\n entry.shader = result.ok ? result.shader : null;\n entry.failed = !result.ok;\n entry.onStatus?.(result.ok);\n }\n\n private renderEntry(entry: Entry, gl: WebGL2RenderingContext, time: number) {\n if (!entry.shader && !entry.failed) this.compile(entry, gl);\n if (!entry.shader) return;\n sizeCanvas(entry.canvas);\n // Fill the thumbnail the way `object-fit: cover` would, at its aspect ratio.\n const aspect = entry.canvas.width / entry.canvas.height;\n const w = Math.min(this.renderWidth, Math.round(this.renderHeight * aspect));\n const h = Math.min(this.renderHeight, Math.round(w / aspect));\n drawFrame(\n gl,\n entry.shader,\n entry.controls,\n entry.values,\n {\n width: w,\n height: h,\n time,\n delta: this.interval / 1000,\n frame: Math.floor(time * this.fps),\n mouse: [0, 0, 0, 0],\n // A hovered thumbnail is pointed at in its middle, for effects that answer hovers.\n pointer: [w / 2, h / 2, entry.hovered ? 1 : 0, 0],\n },\n this.textures ?? undefined,\n );\n // The GL canvas's drawing buffer is valid until this task ends; copy it now.\n // WebGL draws from the bottom left, so the frame sits at the bottom of the canvas.\n if (this.alpha) {\n entry.ctx.clearRect(0, 0, entry.canvas.width, entry.canvas.height);\n }\n entry.ctx.drawImage(\n this.glCanvas!,\n 0,\n this.renderHeight - h,\n w,\n h,\n 0,\n 0,\n entry.canvas.width,\n entry.canvas.height,\n );\n entry.dirty = false;\n }\n\n private schedule() {\n if (!this.raf && this.entries.size) this.raf = requestAnimationFrame(this.tick);\n }\n\n private tick = (now: number) => {\n this.raf = 0;\n const gl = this.context();\n if (!gl) return;\n this.start ??= now;\n const animate = now - this.last >= this.interval;\n const time = (now - this.start) / 1000;\n let keepGoing = false;\n for (const entry of this.entries) {\n if (!entry.visible) continue;\n const moving = !this.reducedMotion || entry.hovered;\n if (entry.dirty || (moving && animate)) {\n this.renderEntry(entry, gl, moving ? time : this.stillTime);\n }\n if (moving && entry.shader) keepGoing = true;\n }\n if (animate) this.last = now;\n if (keepGoing) this.schedule();\n };\n}\n\nfunction sizeCanvas(canvas: HTMLCanvasElement) {\n // The layout size, not the bounding box: a rotated or scaled canvas keeps its own proportions.\n const ratio = Math.min(window.devicePixelRatio || 1, 2);\n const w = Math.max(1, Math.round(canvas.clientWidth * ratio));\n const h = Math.max(1, Math.round(canvas.clientHeight * ratio));\n if (canvas.width !== w || canvas.height !== h) {\n canvas.width = w;\n canvas.height = h;\n }\n}\n\nlet shared: ThumbnailPool | null = null;\n\n/** The pool `registerThumbnail` uses, created on first use. */\nexport function sharedThumbnailPool(): ThumbnailPool {\n shared ??= new ThumbnailPool();\n return shared;\n}\n\n/** Registers a thumbnail with the shared pool. See {@link ThumbnailPool.register}. */\nexport function registerThumbnail(\n canvas: HTMLCanvasElement,\n source: string,\n values?: Record<string, ControlValue>,\n onStatus?: (ok: boolean) => void,\n): ThumbnailHandle {\n return sharedThumbnailPool().register(canvas, source, values, onStatus);\n}\n","import { imageUrls, parseControls, type ControlDef, type ControlValue } from \"./controls.js\";\nimport {\n compileShader,\n createContext,\n drawFrame,\n ShaderCompileError,\n type CompiledShader,\n} from \"./program.js\";\nimport { clicksUniform, type ShaderClick } from \"./pointer.js\";\nimport { TextureCache } from \"./textures.js\";\n\nexport interface ShaderFramesOptions {\n source: string;\n values?: Record<string, ControlValue>;\n width: number;\n height: number;\n /** Draw on this canvas instead of a new, detached one. It is resized to `width` × `height`. */\n canvas?: HTMLCanvasElement;\n}\n\nexport interface RenderOptions {\n /** `iFrame` (default: the number of frames rendered so far). */\n frame?: number;\n /** `iTimeDelta` in seconds (default: the time since the previous `render`, else 0). */\n delta?: number;\n /** `iMouse` (default: all zero, as on a page nobody has touched). */\n mouse?: [number, number, number, number];\n /** `iPointer`: x, y, how hovered (0–1), the press (seconds held, or minus seconds since release; default all zero). */\n pointer?: [number, number, number, number];\n /** Clicks so far, for `iClicks` (the latest `MAX_CLICKS` at or before the frame's time count). */\n clicks?: readonly ShaderClick[];\n}\n\n/**\n * Renders a shader at exact times you choose, off screen. There is no\n * animation loop and no clock: frame `n` of a recording at `fps` is\n * `render(start + n / fps)`, so the output is the same on any machine. It\n * suits recorders, video pipelines and anything else stepped by its own\n * clock. The constructor throws a {@link ShaderCompileError} when the source\n * does not compile.\n *\n * Image controls load in the background: `await frames.ready()` before the\n * first `render`, or the frames show black where the images go.\n */\nexport class ShaderFrames {\n readonly canvas: HTMLCanvasElement;\n readonly width: number;\n readonly height: number;\n readonly controls: ControlDef[];\n private gl: WebGL2RenderingContext | null;\n private shader: CompiledShader;\n private textures: TextureCache;\n private values: Record<string, ControlValue> | undefined;\n private rendered = 0;\n private lastTime: number | null = null;\n private pixels: Uint8Array;\n\n constructor(options: ShaderFramesOptions) {\n const { source, values, width, height } = options;\n this.width = Math.max(1, Math.round(width));\n this.height = Math.max(1, Math.round(height));\n this.canvas = options.canvas ?? document.createElement(\"canvas\");\n this.canvas.width = this.width;\n this.canvas.height = this.height;\n this.values = values;\n const gl = createContext(this.canvas, true);\n if (!gl) throw new Error(\"WebGL2 is not available in this browser.\");\n this.gl = gl;\n this.controls = parseControls(source);\n const compiled = compileShader(gl, source, this.controls);\n if (!compiled.ok) {\n this.dispose();\n throw new ShaderCompileError(compiled.errors);\n }\n this.shader = compiled.shader;\n this.textures = new TextureCache(gl);\n this.syncTextures();\n this.pixels = new Uint8Array(this.width * this.height * 4);\n }\n\n /** Changes control values for the frames that follow. */\n setValues(values: Record<string, ControlValue> | undefined) {\n this.values = values;\n this.syncTextures();\n }\n\n /**\n * Settles once every image the controls show has loaded. Rejects with an\n * {@link ImageLoadError} when one cannot be.\n */\n async ready(): Promise<void> {\n await this.textures.ready(imageUrls(this.controls, this.values));\n }\n\n /** Draws the frame at `time` seconds onto `canvas`. */\n render(time: number, options: RenderOptions = {}) {\n const gl = this.context();\n const delta = options.delta ?? (this.lastTime === null ? 0 : time - this.lastTime);\n drawFrame(\n gl,\n this.shader,\n this.controls,\n this.values,\n {\n width: this.width,\n height: this.height,\n time,\n delta,\n frame: options.frame ?? this.rendered,\n mouse: options.mouse ?? [0, 0, 0, 0],\n pointer: options.pointer,\n clicks: options.clicks && clicksUniform(options.clicks, time),\n },\n this.textures,\n );\n this.rendered += 1;\n this.lastTime = time;\n }\n\n /**\n * The last rendered frame as RGBA bytes, rows top down (as in `ImageData`;\n * WebGL itself reads bottom up). Pass `out` to reuse a buffer of\n * `width * height * 4` bytes.\n */\n readPixels(out?: Uint8Array | Uint8ClampedArray): Uint8Array | Uint8ClampedArray {\n const gl = this.context();\n const { width, height } = this;\n const target = out ?? new Uint8Array(width * height * 4);\n if (target.length !== width * height * 4) {\n throw new RangeError(`readPixels needs a buffer of ${width * height * 4} bytes.`);\n }\n gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, this.pixels);\n const row = width * 4;\n for (let y = 0; y < height; y++) {\n target.set(this.pixels.subarray((height - 1 - y) * row, (height - y) * row), y * row);\n }\n return target;\n }\n\n /** Releases the GL context now rather than at garbage collection (browsers cap live contexts). */\n dispose() {\n if (!this.gl) return;\n if (this.shader) this.gl.deleteProgram(this.shader.program);\n this.textures?.dispose();\n this.gl.getExtension(\"WEBGL_lose_context\")?.loseContext();\n this.gl = null;\n }\n\n private syncTextures() {\n const urls = imageUrls(this.controls, this.values);\n this.textures.retain(urls);\n for (const url of urls) this.textures.request(url);\n }\n\n private context(): WebGL2RenderingContext {\n if (!this.gl) throw new Error(\"These frames have been disposed.\");\n if (this.gl.isContextLost()) throw new Error(\"The graphics context was lost.\");\n return this.gl;\n }\n}\n","// gifenc ships without types. The reference (not an import) brings them along when a host\n// type-checks these sources through the `workspace` condition.\n// eslint-disable-next-line @typescript-eslint/triple-slash-reference\n/// <reference path=\"./gifenc.d.ts\" />\nimport type { ControlValue } from \"./controls.js\";\nimport { ShaderFrames } from \"./frames.js\";\n\nexport interface GifOptions {\n source: string;\n values?: Record<string, ControlValue>;\n width: number;\n height: number;\n /** Frames per second. GIF delays are whole hundredths of a second, so 10, 20, 25 and 50 play back exactly. */\n fps: number;\n /** Seconds of animation to record. */\n duration: number;\n /** `iTime` of the first frame (default 0). */\n start?: number;\n /** Called after each frame with the frames done and the total. */\n onProgress?: (done: number, total: number) => void;\n /** Aborting rejects with an `AbortError` `DOMException`. */\n signal?: AbortSignal;\n}\n\n/** Lets the page repaint between frames so progress shows and Cancel works. */\nconst yieldToBrowser = () => new Promise<void>((resolve) => setTimeout(resolve, 0));\n\n/**\n * Records a shader as an animated GIF that loops forever. Frames are rendered\n * off screen at exact time steps (`start + i / fps`), so the result is smooth\n * whatever the page's frame rate, and nothing on screen is disturbed. Each\n * frame gets its own 256-colour palette. The encoder,\n * [gifenc](https://github.com/mattdesl/gifenc), is loaded on first use.\n *\n * Image controls are loaded before the first frame. Rejects with a\n * `ShaderCompileError` when the source does not compile, and an\n * `ImageLoadError` when an image cannot be loaded.\n */\nexport async function recordGif(options: GifOptions): Promise<Blob> {\n const { source, values, width, height, fps, duration, start = 0, onProgress, signal } = options;\n const { GIFEncoder, quantize, applyPalette } = await import(\"gifenc\");\n\n const frames = new ShaderFrames({ source, values, width, height });\n try {\n await frames.ready();\n const total = Math.max(1, Math.round(duration * fps));\n const delay = 1000 / fps;\n const rgba = new Uint8Array(frames.width * frames.height * 4);\n const gif = GIFEncoder();\n\n for (let i = 0; i < total; i++) {\n if (signal?.aborted) throw new DOMException(\"Cancelled\", \"AbortError\");\n frames.render(start + i / fps, { frame: i, delta: 1 / fps });\n frames.readPixels(rgba);\n const palette = quantize(rgba, 256);\n gif.writeFrame(applyPalette(rgba, palette), frames.width, frames.height, {\n palette,\n delay,\n ...(i === 0 ? { repeat: 0 } : {}),\n });\n onProgress?.(i + 1, total);\n if (i % 2 === 1) await yieldToBrowser();\n }\n\n gif.finish();\n return new Blob([gif.bytes()], { type: \"image/gif\" });\n } finally {\n frames.dispose();\n }\n}\n","import { controlValue, parseControls, type ControlValue } from \"./controls.js\";\nimport { HOVER_SECONDS, MAX_CLICKS } from \"./pointer.js\";\nimport { VERTEX_SOURCE, wrapSource } from \"./program.js\";\nimport { SVG_EDGE } from \"./textures.js\";\n\nconst escapeHtml = (s: string) =>\n s.replace(/[&<>\"]/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\" })[c]!);\n\n/** JSON that is safe inside a `<script>`: a `</script>` or `<!--` in a shader comment cannot end it. */\nconst scriptJson = (value: unknown) => JSON.stringify(value).replace(/</g, \"\\\\u003c\");\n\nexport interface StandaloneOptions {\n /** The comment at the top of the script, saying where the page came from. */\n credit?: string;\n}\n\n/**\n * A self-contained HTML page that runs the shader full-window, with the\n * control values baked in. It has no dependencies and opens from disk. Like\n * the live preview, it feeds `iMouse` from pointer drags and caps the pixel\n * ratio at 2.\n *\n * Images are loaded by URL when the page opens: a `data:` URL travels with\n * the page, an `https:` one needs the network and CORS, and a `blob:` one\n * only lives as long as the page that made it. SVGs are sized to\n * {@link SVG_EDGE} pixels before they are decoded, as `loadImage` does.\n */\nexport function standaloneHtml(\n title: string,\n source: string,\n values?: Record<string, ControlValue>,\n options: StandaloneOptions = {},\n): string {\n const controls = parseControls(source).map((c) => ({\n name: c.name,\n type: c.type,\n value: controlValue(c, values),\n ...(c.texture ? { texture: c.texture } : {}),\n }));\n const credit = (options.credit ?? \"Exported with Flair.\").replace(/\\n/g, \" \");\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeHtml(title)}</title>\n<style>html,body{margin:0;height:100%;background:#000}canvas{display:block;width:100%;height:100%}</style>\n</head>\n<body>\n<canvas id=\"c\"></canvas>\n<script>\n// ${credit.replace(/</g, \"\\\\u003c\")} Shadertoy-style fragment shader on WebGL2.\nconst VERTEX = ${scriptJson(VERTEX_SOURCE)};\nconst FRAGMENT = ${scriptJson(wrapSource(source))};\nconst CONTROLS = ${scriptJson(controls)};\nconst canvas = document.getElementById(\"c\");\nconst gl = canvas.getContext(\"webgl2\");\nif (!gl) document.body.textContent = \"WebGL2 is not available.\";\nfunction compile(type, src) {\n const s = gl.createShader(type);\n gl.shaderSource(s, src);\n gl.compileShader(s);\n if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));\n return s;\n}\nconst program = gl.createProgram();\ngl.attachShader(program, compile(gl.VERTEX_SHADER, VERTEX));\ngl.attachShader(program, compile(gl.FRAGMENT_SHADER, FRAGMENT));\ngl.linkProgram(program);\ngl.useProgram(program);\nconst u = (n) => gl.getUniformLocation(program, n);\nlet unit = 0;\n// An SVG decodes at its own, often icon, size: give it ${SVG_EDGE} px on its long edge (and a\n// viewBox, so the drawing scales) to keep it sharp.\nasync function sizedSvg(url) {\n const doc = new DOMParser().parseFromString(await (await fetch(url)).text(), \"image/svg+xml\");\n const svg = doc.documentElement;\n const len = (n) => { const m = (svg.getAttribute(n) || \"\").trim().match(/^([\\\\d.]+)(px)?$/); return m && +m[1] > 0 ? +m[1] : 0; };\n const box = (svg.getAttribute(\"viewBox\") || \"\").trim().split(/[\\\\s,]+/).map(Number);\n const hasBox = box.length === 4 && box[2] > 0 && box[3] > 0;\n const w = len(\"width\") || (hasBox ? box[2] : 300), h = len(\"height\") || (hasBox ? box[3] : 150);\n const scale = ${SVG_EDGE} / Math.max(w, h);\n if (!hasBox) svg.setAttribute(\"viewBox\", \"0 0 \" + w + \" \" + h);\n svg.setAttribute(\"width\", Math.max(1, Math.round(w * scale)));\n svg.setAttribute(\"height\", Math.max(1, Math.round(h * scale)));\n return URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(svg)], { type: \"image/svg+xml\" }));\n}\nfunction image(loc, url, texture) {\n const i = unit++, t = gl.createTexture();\n const bind = () => { gl.activeTexture(gl.TEXTURE0 + i); gl.bindTexture(gl.TEXTURE_2D, t); };\n const filter = { mipmap: gl.LINEAR_MIPMAP_LINEAR, linear: gl.LINEAR, nearest: gl.NEAREST }[texture.filter];\n const wrap = { repeat: gl.REPEAT, clamp: gl.CLAMP_TO_EDGE, mirror: gl.MIRRORED_REPEAT }[texture.wrap];\n bind();\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 255]));\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.filter === \"nearest\" ? gl.NEAREST : gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);\n gl.uniform1i(loc, i);\n if (!url) return;\n const img = new Image();\n if (!/^(data|blob):/i.test(url)) img.crossOrigin = \"anonymous\";\n img.onload = () => {\n bind();\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);\n gl.generateMipmap(gl.TEXTURE_2D);\n };\n if (/^data:image\\\\/svg\\\\+xml[;,]/i.test(url) || /^(?!data:)[^?#]*\\\\.svg([?#]|$)/i.test(url)) {\n sizedSvg(url).then((src) => { img.src = src; }, () => {});\n } else img.src = url;\n}\nfor (const c of CONTROLS) {\n const loc = u(c.name);\n const v = c.value;\n if (c.type === \"float\") gl.uniform1f(loc, v);\n else if (c.type === \"int\" || c.type === \"bool\") gl.uniform1i(loc, Number(v));\n else if (c.type === \"sampler2D\") image(loc, v, c.texture);\n else gl[\"uniform\" + v.length + \"fv\"](loc, v);\n}\nlet mouse = [0, 0, 0, 0];\nconst px = (e) => { const r = canvas.getBoundingClientRect(); return [(e.clientX - r.left) * canvas.width / r.width, (r.bottom - e.clientY) * canvas.height / r.height]; };\ncanvas.addEventListener(\"pointerdown\", (e) => { const [x, y] = px(e); mouse = [x, y, x, y]; });\ncanvas.addEventListener(\"pointermove\", (e) => { if (e.buttons && mouse[2] > 0) { const [x, y] = px(e); mouse = [x, y, mouse[2], mouse[3]]; } });\naddEventListener(\"pointerup\", () => { if (mouse[2] > 0) mouse = [mouse[0], mouse[1], -mouse[2], -Math.abs(mouse[3])]; });\nlet start = performance.now(), last = start, frame = 0;\n// iPointer (where, how hovered, seconds pressed) and iClicks (the latest clicks, newest first).\nconst seconds = () => (performance.now() - start) / 1000;\nlet at = [0, 0], over = false, hoverFrom = 0, hoverAt = -1e9, pressedAt = -1, releasedAt = -1, clicks = [];\nconst hovered = (t) => { const k = Math.min(1, Math.max(0, (t - hoverAt) / ${HOVER_SECONDS})); return hoverFrom + ((over ? 1 : 0) - hoverFrom) * k * k * (3 - 2 * k); };\nconst hover = (on) => { const t = seconds(); hoverFrom = hovered(t); hoverAt = t; over = on; };\ncanvas.addEventListener(\"pointerenter\", (e) => { at = px(e); hover(true); });\ncanvas.addEventListener(\"pointerleave\", () => hover(false));\ncanvas.addEventListener(\"pointermove\", (e) => { at = px(e); });\ncanvas.addEventListener(\"pointerdown\", (e) => { at = px(e); pressedAt = seconds(); clicks.unshift([at[0], at[1], pressedAt, 1]); clicks = clicks.slice(0, ${MAX_CLICKS}); });\naddEventListener(\"pointerup\", () => { if (pressedAt >= 0) releasedAt = seconds(); pressedAt = -1; });\nfunction draw(now) {\n const dpr = Math.min(devicePixelRatio || 1, 2);\n const w = Math.round(canvas.clientWidth * dpr), h = Math.round(canvas.clientHeight * dpr);\n if (canvas.width !== w || canvas.height !== h) { canvas.width = w; canvas.height = h; }\n gl.viewport(0, 0, w, h);\n gl.uniform3f(u(\"iResolution\"), w, h, 1);\n gl.uniform1f(u(\"iTime\"), (now - start) / 1000);\n gl.uniform1f(u(\"iTimeDelta\"), (now - last) / 1000);\n gl.uniform1i(u(\"iFrame\"), frame++);\n gl.uniform4f(u(\"iMouse\"), mouse[0], mouse[1], mouse[2], mouse[3]);\n const t = (now - start) / 1000;\n gl.uniform4f(u(\"iPointer\"), at[0], at[1], hovered(t), pressedAt >= 0 ? Math.max(0, t - pressedAt) : releasedAt >= 0 ? -Math.max(1e-3, t - releasedAt) : 0);\n const c = new Float32Array(${MAX_CLICKS * 4});\n clicks.forEach((k, i) => c.set(k, i * 4));\n gl.uniform4fv(u(\"iClicks\"), c);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n last = now;\n requestAnimationFrame(draw);\n}\nrequestAnimationFrame(draw);\n</script>\n</body>\n</html>\n`;\n}\n","/**\n * 38 Layer Blend Modes for Compositing and WebGL Shader Generation.\n * Mathematically matching Adobe After Effects & EffectCraft blending standards.\n */\n\nexport type BlendMode =\n | 'normal'\n | 'dissolve'\n | 'dancingDissolve'\n | 'darken'\n | 'multiply'\n | 'colorBurn'\n | 'classicColorBurn'\n | 'linearBurn'\n | 'darkerColor'\n | 'add'\n | 'lighten'\n | 'screen'\n | 'colorDodge'\n | 'classicColorDodge'\n | 'linearDodge'\n | 'lighterColor'\n | 'overlay'\n | 'softLight'\n | 'hardLight'\n | 'linearLight'\n | 'vividLight'\n | 'pinLight'\n | 'hardMix'\n | 'difference'\n | 'classicDifference'\n | 'exclusion'\n | 'subtract'\n | 'divide'\n | 'hue'\n | 'saturation'\n | 'color'\n | 'luminosity'\n | 'stencilAlpha'\n | 'stencilLuma'\n | 'silhouetteAlpha'\n | 'silhouetteLuma'\n | 'alphaAdd'\n | 'luminescentPremul';\n\nexport const ALL_BLEND_MODES: readonly BlendMode[] = [\n 'normal',\n 'dissolve',\n 'dancingDissolve',\n 'darken',\n 'multiply',\n 'colorBurn',\n 'classicColorBurn',\n 'linearBurn',\n 'darkerColor',\n 'add',\n 'lighten',\n 'screen',\n 'colorDodge',\n 'classicColorDodge',\n 'linearDodge',\n 'lighterColor',\n 'overlay',\n 'softLight',\n 'hardLight',\n 'linearLight',\n 'vividLight',\n 'pinLight',\n 'hardMix',\n 'difference',\n 'classicDifference',\n 'exclusion',\n 'subtract',\n 'divide',\n 'hue',\n 'saturation',\n 'color',\n 'luminosity',\n 'stencilAlpha',\n 'stencilLuma',\n 'silhouetteAlpha',\n 'silhouetteLuma',\n 'alphaAdd',\n 'luminescentPremul',\n];\n\nexport type ColorRgba = [number, number, number, number];\n\nfunction lum(r: number, g: number, b: number): number {\n return 0.3 * r + 0.59 * g + 0.11 * b;\n}\n\nfunction sat(r: number, g: number, b: number): number {\n return Math.max(r, g, b) - Math.min(r, g, b);\n}\n\nfunction clipColor(r: number, g: number, b: number): [number, number, number] {\n const l = lum(r, g, b);\n const n = Math.min(r, g, b);\n const x = Math.max(r, g, b);\n let cr = r;\n let cg = g;\n let cb = b;\n if (n < 0.0) {\n const denom = Math.max(1e-9, l - n);\n cr = l + ((cr - l) * l) / denom;\n cg = l + ((cg - l) * l) / denom;\n cb = l + ((cb - l) * l) / denom;\n }\n if (x > 1.0) {\n const denom = Math.max(1e-9, x - l);\n cr = l + ((cr - l) * (1.0 - l)) / denom;\n cg = l + ((cg - l) * (1.0 - l)) / denom;\n cb = l + ((cb - l) * (1.0 - l)) / denom;\n }\n return [cr, cg, cb];\n}\n\nfunction setLum(r: number, g: number, b: number, l: number): [number, number, number] {\n const d = l - lum(r, g, b);\n return clipColor(r + d, g + d, b + d);\n}\n\nfunction setSat(r: number, g: number, b: number, s: number): [number, number, number] {\n const mx = Math.max(r, g, b);\n const mn = Math.min(r, g, b);\n if (mx - mn <= 1e-9) return [0, 0, 0];\n const f = (v: number) => ((v - mn) * s) / (mx - mn);\n return [f(r), f(g), f(b)];\n}\n\nfunction burn(cb: number, cs: number): number {\n if (cb >= 1.0) return 1.0;\n if (cs <= 0.0) return 0.0;\n return 1.0 - Math.min(1.0, (1.0 - cb) / cs);\n}\n\nfunction dodge(cb: number, cs: number): number {\n if (cb <= 0.0) return 0.0;\n if (cs >= 1.0) return 1.0;\n return Math.min(1.0, cb / (1.0 - cs));\n}\n\nfunction hardLight(cb: number, cs: number): number {\n if (cs <= 0.5) {\n return cb * 2.0 * cs;\n }\n const s = 2.0 * cs - 1.0;\n return cb + s - cb * s;\n}\n\nfunction softLight(cb: number, cs: number): number {\n if (cs <= 0.5) {\n return cb - (1.0 - 2.0 * cs) * cb * (1.0 - cb);\n }\n const d = cb <= 0.25 ? ((16.0 * cb - 12.0) * cb + 4.0) * cb : Math.sqrt(Math.max(0.0, cb));\n return cb + (2.0 * cs - 1.0) * (d - cb);\n}\n\nfunction vividLight(cb: number, cs: number): number {\n return cs <= 0.5 ? burn(cb, 2.0 * cs) : dodge(cb, 2.0 * (cs - 0.5));\n}\n\nfunction separableBlend(mode: BlendMode, cb: number, cs: number): number {\n switch (mode) {\n case 'darken':\n return Math.min(cb, cs);\n case 'multiply':\n return cb * cs;\n case 'colorBurn':\n return burn(cb, cs);\n case 'classicColorBurn':\n return cs <= 0.0 ? 0.0 : 1.0 - (1.0 - cb) / cs;\n case 'linearBurn':\n return Math.max(0.0, cb + cs - 1.0);\n case 'add':\n case 'linearDodge':\n return cb + cs;\n case 'lighten':\n return Math.max(cb, cs);\n case 'screen':\n return cb + cs - cb * cs;\n case 'colorDodge':\n return dodge(cb, cs);\n case 'classicColorDodge':\n return cs >= 1.0 ? 1.0 : Math.min(1.0, cb / (1.0 - cs));\n case 'overlay':\n return hardLight(cs, cb);\n case 'softLight':\n return softLight(cb, cs);\n case 'hardLight':\n return hardLight(cb, cs);\n case 'linearLight':\n return Math.min(1.0, Math.max(0.0, cb + 2.0 * cs - 1.0));\n case 'vividLight':\n return vividLight(cb, cs);\n case 'pinLight':\n return cs <= 0.5 ? Math.min(cb, 2.0 * cs) : Math.max(cb, 2.0 * cs - 1.0);\n case 'hardMix':\n return cb + cs >= 1.0 ? 1.0 : 0.0;\n case 'difference':\n case 'classicDifference':\n return Math.abs(cb - cs);\n case 'exclusion':\n return cb + cs - 2.0 * cb * cs;\n case 'subtract':\n return Math.max(0.0, cb - cs);\n case 'divide':\n return cs <= 0.0 ? (cb > 0.0 ? 1.0 : 0.0) : cb / cs;\n default:\n return cs;\n }\n}\n\n/**\n * Evaluates CPU compositing of premultiplied RGBA source onto premultiplied RGBA destination.\n *\n * @param mode Blend mode to apply.\n * @param dst Destination premultiplied RGBA [r, g, b, a] in [0, 1].\n * @param src Source premultiplied RGBA [r, g, b, a] in [0, 1].\n * @param noise Per-pixel noise parameter in [0, 1] for Dissolve modes (default: 0).\n */\nexport function blendRgba(\n mode: BlendMode,\n dst: ColorRgba,\n src: ColorRgba,\n noise = 0,\n): ColorRgba {\n const sa = src[3];\n const da = dst[3];\n\n switch (mode) {\n case 'normal': {\n const k = 1.0 - sa;\n return [src[0] + dst[0] * k, src[1] + dst[1] * k, src[2] + dst[2] * k, sa + da * k];\n }\n case 'dissolve':\n case 'dancingDissolve': {\n if (sa <= 0.0 || noise >= sa) return [...dst];\n const inv = 1.0 / sa;\n return [src[0] * inv, src[1] * inv, src[2] * inv, 1.0];\n }\n case 'stencilAlpha':\n return [dst[0] * sa, dst[1] * sa, dst[2] * sa, dst[3] * sa];\n case 'silhouetteAlpha': {\n const k = 1.0 - sa;\n return [dst[0] * k, dst[1] * k, dst[2] * k, dst[3] * k];\n }\n case 'stencilLuma':\n case 'silhouetteLuma': {\n const l = lum(src[0], src[1], src[2]);\n const k = mode === 'stencilLuma' ? l : 1.0 - l;\n return [dst[0] * k, dst[1] * k, dst[2] * k, dst[3] * k];\n }\n case 'alphaAdd': {\n const k = 1.0 - sa;\n return [src[0] + dst[0] * k, src[1] + dst[1] * k, src[2] + dst[2] * k, Math.min(1.0, sa + da)];\n }\n case 'luminescentPremul': {\n const k = 1.0 - sa;\n return [src[0] + dst[0] * k, src[1] + dst[1] * k, src[2] + dst[2] * k, sa + da - sa * da];\n }\n }\n\n if (sa <= 0.0) return [...dst];\n if (da <= 0.0) return [...src];\n\n const cs: [number, number, number] = [src[0] / sa, src[1] / sa, src[2] / sa];\n const cb: [number, number, number] = [dst[0] / da, dst[1] / da, dst[2] / da];\n\n let b: [number, number, number];\n switch (mode) {\n case 'hue':\n b = setLum(...setSat(cs[0], cs[1], cs[2], sat(...cb)), lum(...cb));\n break;\n case 'saturation':\n b = setLum(...setSat(cb[0], cb[1], cb[2], sat(...cs)), lum(...cb));\n break;\n case 'color':\n b = setLum(cs[0], cs[1], cs[2], lum(...cb));\n break;\n case 'luminosity':\n b = setLum(cb[0], cb[1], cb[2], lum(...cs));\n break;\n case 'darkerColor':\n b = lum(...cs) < lum(...cb) ? cs : cb;\n break;\n case 'lighterColor':\n b = lum(...cs) > lum(...cb) ? cs : cb;\n break;\n default:\n b = [\n separableBlend(mode, cb[0], cs[0]),\n separableBlend(mode, cb[1], cs[1]),\n separableBlend(mode, cb[2], cs[2]),\n ];\n break;\n }\n\n const ao = sa + da - sa * da;\n return [\n (1.0 - da) * src[0] + (1.0 - sa) * dst[0] + sa * da * b[0],\n (1.0 - da) * src[1] + (1.0 - sa) * dst[1] + sa * da * b[1],\n (1.0 - da) * src[2] + (1.0 - sa) * dst[2] + sa * da * b[2],\n ao,\n ];\n}\n\n/**\n * Returns a self-contained GLSL shader function for blending two vec4 colors.\n * Useful for runtime GPU compositing across multiple shader layers or passes.\n */\nexport function getGlslBlendFunction(mode: BlendMode): string {\n switch (mode) {\n case 'multiply':\n return `vec4 blend(vec4 dst, vec4 src) {\n return vec4(src.rgb * dst.rgb * src.a + dst.rgb * (1.0 - src.a), src.a + dst.a * (1.0 - src.a));\n}`;\n case 'screen':\n return `vec4 blend(vec4 dst, vec4 src) {\n vec3 res = src.rgb + dst.rgb - src.rgb * dst.rgb;\n return vec4(mix(dst.rgb, res, src.a), src.a + dst.a * (1.0 - src.a));\n}`;\n case 'add':\n case 'linearDodge':\n return `vec4 blend(vec4 dst, vec4 src) {\n return vec4(min(vec3(1.0), dst.rgb + src.rgb * src.a), min(1.0, dst.a + src.a));\n}`;\n case 'overlay':\n return `vec4 blend(vec4 dst, vec4 src) {\n vec3 res = mix(2.0 * dst.rgb * src.rgb, 1.0 - 2.0 * (1.0 - dst.rgb) * (1.0 - src.rgb), step(vec3(0.5), dst.rgb));\n return vec4(mix(dst.rgb, res, src.a), src.a + dst.a * (1.0 - src.a));\n}`;\n case 'difference':\n return `vec4 blend(vec4 dst, vec4 src) {\n return vec4(mix(dst.rgb, abs(dst.rgb - src.rgb), src.a), src.a + dst.a * (1.0 - src.a));\n}`;\n case 'normal':\n default:\n return `vec4 blend(vec4 dst, vec4 src) {\n return src + dst * (1.0 - src.a);\n}`;\n }\n}\n","import { MAX_TILT } from './camera.js';\nimport type { Scene, Shot } from './types.js';\nimport { EFFECT_TYPES } from './effects.js';\n\nexport interface SceneIssue {\n /** 1-based shot index (0 indicates whole scene level). */\n shot: number;\n message: string;\n}\n\nconst LONG_CAPTION = 120;\nconst LONG_TITLE = 60;\nconst MAX_EFFECTS_PER_SHOT = 5;\n\n/** Validates a Scene before running or rendering it. */\nexport function validateScene(scene: Scene): SceneIssue[] {\n const issues: SceneIssue[] = [];\n const say = (shot: number, message: string) => issues.push({ shot, message });\n\n if (!scene.name || typeof scene.name !== 'string') {\n say(0, 'Scene needs a string name.');\n } else if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(scene.name)) {\n say(0, `Scene name \"${scene.name}\" should be lowercase words and dashes.`);\n }\n\n if (!Array.isArray(scene.shots) || scene.shots.length === 0) {\n say(0, 'Scene needs at least one shot.');\n return issues;\n }\n\n const mediaKeys = new Set(Object.keys(scene.media ?? {}));\n const effectIds = new Set<string>();\n\n // Check media\n if (scene.media) {\n for (const [id, m] of Object.entries(scene.media)) {\n if (!m || typeof m !== 'object') {\n say(0, `Media \"${id}\" must be an object.`);\n } else if (!m.type) {\n say(0, `Media \"${id}\" needs a type.`);\n } else if (m.type === 'shader' && !m.source) {\n say(0, `Shader media \"${id}\" needs a source string.`);\n }\n }\n }\n\n for (const [idx, shot] of scene.shots.entries()) {\n const shotNum = idx + 1;\n\n // Check camera\n if (shot.camera) {\n if ('frame' in shot.camera) {\n if (!Array.isArray(shot.camera.frame) || shot.camera.frame.length === 0) {\n say(shotNum, 'Dynamic camera framing requires a non-empty frame array of element IDs.');\n }\n } else if ('width' in shot.camera && (shot.camera.width <= 0 || shot.camera.height <= 0)) {\n say(shotNum, 'Camera rectangle width and height must be positive.');\n }\n const cam = shot.camera;\n const angle = (name: 'tilt' | 'turn' | 'roll', max: number) => {\n const v = cam[name];\n if (v !== undefined && !(typeof v === 'number' && Math.abs(v) <= max))\n say(shotNum, `Camera ${name} is degrees between -${max} and ${max}.`);\n };\n angle('tilt', MAX_TILT);\n angle('turn', MAX_TILT);\n angle('roll', 180);\n if (cam.perspective !== undefined && !(typeof cam.perspective === 'number' && cam.perspective >= 0.3))\n say(shotNum, 'Camera perspective is the lens distance as a multiple of the stage width, 0.3 or more (1.2 by default).');\n if (cam.motionBlur !== undefined && !(typeof cam.motionBlur === 'number' && cam.motionBlur >= 0 && cam.motionBlur <= 3))\n say(shotNum, 'Camera motionBlur is 0 (none) to 3 (a whip); 1 is natural.');\n }\n\n // Check hold\n if (shot.hold !== undefined && shot.hold < 0) {\n say(shotNum, 'Shot hold cannot be negative.');\n }\n\n // Check text lengths\n if (shot.caption && shot.caption.length > LONG_CAPTION) {\n say(shotNum, `Caption is long (${shot.caption.length} chars); consider breaking into multiple shots.`);\n }\n if (shot.title && shot.title.length > LONG_TITLE) {\n say(shotNum, `Title is long (${shot.title.length} chars); titles should be concise headings.`);\n }\n\n // Check effects\n const effects = shot.effects ?? [];\n if (effects.length > MAX_EFFECTS_PER_SHOT) {\n say(shotNum, `Shot starts ${effects.length} effects at once; consider keeping effects focused.`);\n }\n\n for (const eff of effects) {\n if (!eff.id || typeof eff.id !== 'string') {\n say(shotNum, 'Every effect needs a unique string id.');\n continue;\n }\n if (effectIds.has(eff.id)) {\n say(shotNum, `Duplicate effect id \"${eff.id}\".`);\n }\n effectIds.add(eff.id);\n\n if (!EFFECT_TYPES.includes(eff.type as any)) {\n say(shotNum, `Unknown effect type \"${(eff as any).type}\".`);\n }\n\n if (eff.type === 'media') {\n if (!eff.media) {\n say(shotNum, `Media effect \"${eff.id}\" needs a media id.`);\n } else if (!mediaKeys.has(eff.media)) {\n say(shotNum, `Media effect \"${eff.id}\" references \"${eff.media}\" which is not defined in scene.media.`);\n }\n }\n\n if (eff.type === 'flow' && !eff.along) {\n say(shotNum, `Flow effect \"${eff.id}\" needs an \"along\" element id.`);\n }\n\n if (['glow', 'ripple', 'spotlight', 'shimmer', 'timer', 'blur', 'burst'].includes(eff.type)) {\n const withIds = eff as { ids?: string[] };\n if (!Array.isArray(withIds.ids) || withIds.ids.length === 0) {\n say(shotNum, `${eff.type} effect \"${eff.id}\" needs a non-empty ids array.`);\n }\n }\n }\n }\n\n return issues;\n}\n","import { CAMERA_MS, type Stage, type View } from './camera.js';\nimport { CAMERA_STEP_MS, cameraAt, cameraTrack, sceneStage } from './cameratrack.js';\nimport type { SceneElement } from './geometry.js';\nimport type { Scene } from './types.js';\n\n/** What the standalone player shows for one shot: the camera's move into it and the elements on stage. */\ninterface ShotPlan {\n /** The camera at each step of its move into the shot (one view when it does not move). */\n move: View[];\n elements: SceneElement[];\n}\n\n/** Each shot's camera move and elements, worked out the way `playScene` plays them. */\nfunction planShots(scene: Scene, stage: Stage): ShotPlan[] {\n return cameraTrack(scene).map((entry) => {\n // The player's steps: the last one lands on the shot's view, with no blur.\n const steps = Math.round(entry.moveMs / CAMERA_STEP_MS);\n const move = Array.from({ length: Math.max(0, steps - 1) }, (_, i) => cameraAt(entry, (i + 1) * CAMERA_STEP_MS, stage));\n move.push(entry.to);\n return { move, elements: entry.elements };\n });\n}\n\n/**\n * Generates a self-contained, standalone HTML player for an animated scene.\n * Includes interactive playback controls, camera moves (panning, zooming,\n * 3D angles and motion blur), title and caption rendering, and embedded\n * WebGL fragment shaders for bespoke motion graphics.\n */\nexport function standaloneSceneHtml(scene: Scene): string {\n const stage = sceneStage(scene);\n const serializedScene = JSON.stringify(scene).replace(/</g, '\\\\u003c');\n const serializedPlan = JSON.stringify({ stage, cameraMs: CAMERA_MS, shots: planShots(scene, stage) }).replace(/</g, '\\\\u003c');\n const title = scene.title || scene.name;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeHtml(title)} - Flair</title>\n <style>\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n background: #0f1117;\n color: #e2e8f0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 16px;\n user-select: none;\n }\n .player-container {\n position: relative;\n width: 100%;\n max-width: 1200px;\n aspect-ratio: ${stage.width} / ${stage.height};\n background: #000;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 20px 50px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(255, 255, 255, 0.1);\n display: flex;\n flex-direction: column;\n }\n .viewport {\n position: relative;\n flex: 1;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n .board {\n position: absolute;\n inset: 0;\n transform-origin: 50% 50%;\n will-change: transform, filter;\n }\n canvas.layer {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n }\n .overlay-title {\n position: absolute;\n top: 24px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(15, 17, 23, 0.85);\n backdrop-filter: blur(8px);\n border: 1px solid rgba(255, 255, 255, 0.15);\n padding: 8px 24px;\n border-radius: 9999px;\n font-size: 1.15rem;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: #fff;\n pointer-events: none;\n transition: opacity 0.3s ease;\n opacity: 0;\n }\n .overlay-caption {\n position: absolute;\n bottom: 64px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(15, 17, 23, 0.88);\n backdrop-filter: blur(8px);\n border: 1px solid rgba(255, 255, 255, 0.15);\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 1.1rem;\n color: #f1f5f9;\n max-width: 80%;\n text-align: center;\n pointer-events: none;\n transition: opacity 0.3s ease;\n opacity: 0;\n }\n .controls-bar {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 52px;\n background: linear-gradient(to top, rgba(0, 0, 0, 0.9), transparent);\n display: flex;\n align-items: center;\n padding: 0 16px;\n gap: 12px;\n z-index: 10;\n }\n .btn {\n background: rgba(255, 255, 255, 0.15);\n border: none;\n color: #fff;\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.9rem;\n font-weight: 500;\n transition: background 0.15s;\n }\n .btn:hover { background: rgba(255, 255, 255, 0.25); }\n .timeline {\n flex: 1;\n height: 6px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 3px;\n cursor: pointer;\n position: relative;\n }\n .timeline-progress {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: #3b82f6;\n border-radius: 3px;\n width: 0%;\n }\n .shot-badge {\n font-size: 0.85rem;\n color: #94a3b8;\n font-variant-numeric: tabular-nums;\n }\n </style>\n</head>\n<body>\n <div class=\"player-container\">\n <div class=\"viewport\" id=\"viewport\">\n <canvas id=\"shaderCanvas\" class=\"layer\"></canvas>\n <div id=\"board\" class=\"board\"><canvas id=\"sceneCanvas\" class=\"layer\"></canvas></div>\n <svg width=\"0\" height=\"0\" style=\"position:absolute\" aria-hidden=\"true\"><filter id=\"motionBlur\" x=\"-10%\" y=\"-10%\" width=\"120%\" height=\"120%\"><feGaussianBlur id=\"motionBlurAmount\" stdDeviation=\"0 0\"/></filter></svg>\n <div id=\"titleEl\" class=\"overlay-title\"></div>\n <div id=\"captionEl\" class=\"overlay-caption\"></div>\n </div>\n <div class=\"controls-bar\">\n <button id=\"playBtn\" class=\"btn\">Play</button>\n <button id=\"prevBtn\" class=\"btn\">⏮</button>\n <button id=\"nextBtn\" class=\"btn\">⏭</button>\n <div id=\"timeline\" class=\"timeline\">\n <div id=\"progress\" class=\"timeline-progress\"></div>\n </div>\n <div id=\"shotBadge\" class=\"shot-badge\">Shot 1 / 1</div>\n </div>\n </div>\n\n <script>\n const scene = ${serializedScene};\n // Each shot's camera move (zoom, scroll, 3D angle and blur at each step) and elements, worked out in advance.\n const plan = ${serializedPlan};\n const boardEl = document.getElementById('board');\n const sceneCanvas = document.getElementById('sceneCanvas');\n const shaderCanvas = document.getElementById('shaderCanvas');\n const ctx = sceneCanvas.getContext('2d');\n const titleEl = document.getElementById('titleEl');\n const captionEl = document.getElementById('captionEl');\n const playBtn = document.getElementById('playBtn');\n const prevBtn = document.getElementById('prevBtn');\n const nextBtn = document.getElementById('nextBtn');\n const progressEl = document.getElementById('progress');\n const shotBadge = document.getElementById('shotBadge');\n\n let currentShotIdx = 0;\n let isPlaying = false;\n let shotStartTime = performance.now();\n let moveFrom = performance.now();\n let gl = null;\n let shaderProgram = null;\n\n function resize() {\n const rect = sceneCanvas.parentElement.getBoundingClientRect();\n const dpr = window.devicePixelRatio || 1;\n sceneCanvas.width = rect.width * dpr;\n sceneCanvas.height = rect.height * dpr;\n shaderCanvas.width = rect.width * dpr;\n shaderCanvas.height = rect.height * dpr;\n }\n window.addEventListener('resize', resize);\n resize();\n\n // Setup WebGL if shader media is present\n const firstShaderKey = Object.keys(scene.media || {}).find(k => scene.media[k].type === 'shader');\n if (firstShaderKey && scene.media[firstShaderKey].source) {\n try {\n gl = shaderCanvas.getContext('webgl2');\n if (gl) {\n const vs = gl.createShader(gl.VERTEX_SHADER);\n gl.shaderSource(vs, '#version 300 es\\\\nin vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); }');\n gl.compileShader(vs);\n\n const fs = gl.createShader(gl.FRAGMENT_SHADER);\n const fullFrag = '#version 300 es\\\\nprecision highp float;\\\\nout vec4 fragColor;\\\\nuniform vec2 u_resolution;\\\\nuniform float u_time;\\\\n' + scene.media[firstShaderKey].source + '\\\\nvoid main() { mainImage(fragColor, gl_FragCoord.xy); }';\n gl.shaderSource(fs, fullFrag);\n gl.compileShader(fs);\n\n shaderProgram = gl.createProgram();\n gl.attachShader(shaderProgram, vs);\n gl.attachShader(shaderProgram, fs);\n gl.linkProgram(shaderProgram);\n\n const quad = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, quad);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 3,-1, -1,3]), gl.STATIC_DRAW);\n const pos = gl.getAttribLocation(shaderProgram, 'position');\n gl.enableVertexAttribArray(pos);\n gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);\n }\n } catch (e) {\n console.warn('Shader setup failed:', e);\n }\n }\n\n function renderShader(timeSeconds) {\n if (!gl || !shaderProgram) return;\n gl.viewport(0, 0, shaderCanvas.width, shaderCanvas.height);\n gl.useProgram(shaderProgram);\n const resLoc = gl.getUniformLocation(shaderProgram, 'u_resolution');\n const timeLoc = gl.getUniformLocation(shaderProgram, 'u_time');\n if (resLoc) gl.uniform2f(resLoc, shaderCanvas.width, shaderCanvas.height);\n if (timeLoc) gl.uniform1f(timeLoc, timeSeconds);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n }\n\n function updateShotUI() {\n const shot = scene.shots[currentShotIdx];\n if (!shot) return;\n\n shotBadge.textContent = 'Shot ' + (currentShotIdx + 1) + ' / ' + scene.shots.length;\n\n if (shot.title) {\n titleEl.textContent = shot.title;\n titleEl.style.opacity = '1';\n } else {\n titleEl.style.opacity = '0';\n }\n\n if (shot.caption) {\n captionEl.textContent = shot.caption;\n captionEl.style.opacity = '1';\n } else {\n captionEl.style.opacity = '0';\n }\n\n const pct = (currentShotIdx / scene.shots.length) * 100;\n progressEl.style.width = pct + '%';\n }\n\n // The camera at time 'now': a step of the current shot's move, or where it settled.\n function cameraNow(now) {\n const move = plan.shots[currentShotIdx].move;\n const u = Math.min(1, (now - moveFrom) / plan.cameraMs);\n return move[Math.min(move.length - 1, Math.floor(u * move.length))];\n }\n\n function applyCamera(view) {\n const w = boardEl.clientWidth || plan.stage.width;\n const angled = view.tilt || view.turn || view.roll;\n boardEl.style.transform = angled\n ? 'perspective(' + (view.perspective || 1.2) * w + 'px) rotateX(' + (view.tilt || 0) + 'deg) rotateY(' + (view.turn || 0) + 'deg) rotateZ(' + (view.roll || 0) + 'deg)'\n : 'none';\n // Motion blur streaks along a pan (taken back through the roll, as the board is blurred before it turns).\n const blur = (view.blur || 0) * w / plan.stage.width;\n if (blur >= 0.05) {\n let sx = blur, sy = blur;\n if (view.blurAngle !== undefined) {\n const a = (view.blurAngle - (view.roll || 0)) * Math.PI / 180;\n sx = Math.max(blur * 0.15, blur * Math.abs(Math.cos(a)));\n sy = Math.max(blur * 0.15, blur * Math.abs(Math.sin(a)));\n }\n document.getElementById('motionBlurAmount').setAttribute('stdDeviation', sx.toFixed(2) + ' ' + sy.toFixed(2));\n boardEl.style.filter = 'url(#motionBlur)';\n } else {\n boardEl.style.filter = 'none';\n }\n }\n\n function draw() {\n const now = performance.now();\n renderShader(now / 1000);\n\n const view = cameraNow(now);\n applyCamera(view);\n ctx.setTransform(1, 0, 0, 1, 0, 0);\n ctx.clearRect(0, 0, sceneCanvas.width, sceneCanvas.height);\n const k = (sceneCanvas.width / plan.stage.width) * view.zoom;\n ctx.setTransform(k, 0, 0, k, k * view.scrollX, k * view.scrollY);\n\n for (const el of plan.shots[currentShotIdx].elements) {\n ctx.fillStyle = el.backgroundColor || 'rgba(59, 130, 246, 0.4)';\n ctx.strokeStyle = el.strokeColor || '#3b82f6';\n ctx.lineWidth = 3;\n ctx.fillRect(el.x, el.y, el.width, el.height);\n ctx.strokeRect(el.x, el.y, el.width, el.height);\n if (el.text) {\n ctx.fillStyle = '#fff';\n ctx.font = '24px sans-serif';\n ctx.fillText(el.text, el.x + 16, el.y + 40);\n }\n }\n\n // Playing moves on to the next shot once this one's move and hold are over.\n const shot = scene.shots[currentShotIdx];\n if (isPlaying && now - shotStartTime > plan.cameraMs + (shot.hold ?? 800) && currentShotIdx < scene.shots.length - 1) {\n goTo(currentShotIdx + 1, true);\n }\n if (isPlaying || now - moveFrom < plan.cameraMs) {\n requestAnimationFrame(draw);\n }\n }\n\n // Shows a shot: forwards it plays the camera's move into it, otherwise it cuts.\n function goTo(idx, animate) {\n currentShotIdx = idx;\n shotStartTime = performance.now();\n moveFrom = animate ? shotStartTime : shotStartTime - plan.cameraMs;\n updateShotUI();\n requestAnimationFrame(draw);\n }\n\n playBtn.addEventListener('click', () => {\n isPlaying = !isPlaying;\n playBtn.textContent = isPlaying ? 'Pause' : 'Play';\n // The current shot counts as settled, so its hold runs from now.\n if (isPlaying) {\n shotStartTime = performance.now() - plan.cameraMs;\n requestAnimationFrame(draw);\n }\n });\n\n prevBtn.addEventListener('click', () => {\n if (currentShotIdx > 0) goTo(currentShotIdx - 1, false);\n });\n\n nextBtn.addEventListener('click', () => {\n if (currentShotIdx < scene.shots.length - 1) goTo(currentShotIdx + 1, true);\n });\n\n window.addEventListener('resize', () => requestAnimationFrame(draw));\n goTo(0, false);\n </script>\n</body>\n</html>`;\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n"],"mappings":"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","names":["escapeHtml"]}