pixi-effects 0.26.2 → 0.27.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 (68) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +8 -10
  3. package/ai/CHAT.md +1 -1
  4. package/ai/chat-template.html +4 -6
  5. package/ai/tools/render.mjs +16 -11
  6. package/dist/{Base-CpdDs4HT.d.ts → Base-CVezIEdo.d.ts} +2 -4
  7. package/dist/{Base-TbBhHPrG.d.cts → Base-DzC3i919.d.cts} +2 -4
  8. package/dist/Composition-4TYDRRRY.js +7 -0
  9. package/dist/{Composition-H4QA24MV.js.map → Composition-4TYDRRRY.js.map} +1 -1
  10. package/dist/Composition-QCOWQSDB.cjs +16 -0
  11. package/dist/{Composition-HM4YT5L7.cjs.map → Composition-QCOWQSDB.cjs.map} +1 -1
  12. package/dist/Controller.d.cts +2 -3
  13. package/dist/Controller.d.ts +2 -3
  14. package/dist/{Movie-i05Oh9mJ.d.cts → Movie-CQ0Nz62a.d.cts} +12 -6
  15. package/dist/{Movie-DVeDTdnc.d.ts → Movie-M9dKm9dI.d.ts} +12 -6
  16. package/dist/Presenter.d.cts +2 -3
  17. package/dist/Presenter.d.ts +2 -3
  18. package/dist/{blendModes-YV43K6E7.cjs → blendModes-HDOP6R7T.cjs} +5 -5
  19. package/dist/{blendModes-YV43K6E7.cjs.map → blendModes-HDOP6R7T.cjs.map} +1 -1
  20. package/dist/{blendModes-G3NOAU3T.js → blendModes-PAVTW3BO.js} +4 -4
  21. package/dist/{blendModes-G3NOAU3T.js.map → blendModes-PAVTW3BO.js.map} +1 -1
  22. package/dist/{chunk-TLYTYBWX.js → chunk-45QKLD6M.js} +9 -11
  23. package/dist/chunk-45QKLD6M.js.map +1 -0
  24. package/dist/{chunk-GBUM7X2S.js → chunk-CKTS6G6I.js} +7 -11
  25. package/dist/chunk-CKTS6G6I.js.map +1 -0
  26. package/dist/{chunk-555URQHQ.cjs → chunk-NDR2VQFV.cjs} +91 -95
  27. package/dist/chunk-NDR2VQFV.cjs.map +1 -0
  28. package/dist/{chunk-3DLYAPK7.cjs → chunk-P4GY3Z4G.cjs} +5 -5
  29. package/dist/{chunk-3DLYAPK7.cjs.map → chunk-P4GY3Z4G.cjs.map} +1 -1
  30. package/dist/{chunk-WPXDRSGN.cjs → chunk-RQMI7EJT.cjs} +1170 -280
  31. package/dist/chunk-RQMI7EJT.cjs.map +1 -0
  32. package/dist/{chunk-X3HB4DSR.js → chunk-TGJACVLD.js} +1150 -263
  33. package/dist/chunk-TGJACVLD.js.map +1 -0
  34. package/dist/{chunk-AXZUKNXF.js → chunk-TYWFKCPY.js} +4 -4
  35. package/dist/{chunk-AXZUKNXF.js.map → chunk-TYWFKCPY.js.map} +1 -1
  36. package/dist/{chunk-ECA2WNRX.cjs → chunk-ZMAD3KJU.cjs} +9 -11
  37. package/dist/chunk-ZMAD3KJU.cjs.map +1 -0
  38. package/dist/index.cjs +189 -116
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +16 -14
  41. package/dist/index.d.ts +16 -14
  42. package/dist/index.js +136 -45
  43. package/dist/index.js.map +1 -1
  44. package/dist/loader.css +1 -1
  45. package/dist/three.cjs +6 -6
  46. package/dist/three.d.cts +2 -3
  47. package/dist/three.d.ts +2 -3
  48. package/dist/three.js +2 -2
  49. package/dist/{types-DSGwN5w1.d.ts → types-DUVefWiB.d.cts} +128 -3
  50. package/dist/{types-DSGwN5w1.d.cts → types-DUVefWiB.d.ts} +128 -3
  51. package/docs/api.md +16 -6
  52. package/docs/dsl.md +14 -14
  53. package/llms-full.txt +38 -28
  54. package/llms.txt +1 -1
  55. package/package.json +3 -4
  56. package/skills/pixi-effects/SKILL.md +1 -1
  57. package/skills/pixi-effects/reference/cheatsheet.md +3 -3
  58. package/skills/pixi-effects/reference/pitfalls.md +3 -3
  59. package/skills/pixi-effects/reference/recipes.md +1 -1
  60. package/skills/pixi-effects/template.html +6 -8
  61. package/dist/Composition-H4QA24MV.js +0 -7
  62. package/dist/Composition-HM4YT5L7.cjs +0 -16
  63. package/dist/chunk-555URQHQ.cjs.map +0 -1
  64. package/dist/chunk-ECA2WNRX.cjs.map +0 -1
  65. package/dist/chunk-GBUM7X2S.js.map +0 -1
  66. package/dist/chunk-TLYTYBWX.js.map +0 -1
  67. package/dist/chunk-WPXDRSGN.cjs.map +0 -1
  68. package/dist/chunk-X3HB4DSR.js.map +0 -1
@@ -1,43 +1,9 @@
1
- import { collectPropKeys, describeLayer } from './chunk-TLYTYBWX.js';
1
+ import { collectPropKeys, describeLayer } from './chunk-45QKLD6M.js';
2
2
  import { suggestName, warnUnknownOptions } from './chunk-BXOCEZ4U.js';
3
3
  import * as PIXI from 'pixi.js';
4
4
  import { Rectangle, Container, Color, GraphicsPath, Sprite, Texture, Graphics, Assets, FillGradient, Text, Filter, GlProgram, defaultFilterVert, GpuProgram, UniformGroup } from 'pixi.js';
5
- import { gsap } from 'gsap';
6
5
 
7
- // src/expr/random.ts
8
- function mix(h) {
9
- h = Math.imul(h ^ h >>> 16, 2246822507);
10
- h = Math.imul(h ^ h >>> 13, 3266489909);
11
- return (h ^ h >>> 16) >>> 0;
12
- }
13
- function fold(n) {
14
- if (!Number.isFinite(n)) return 0;
15
- const i = Math.floor(n);
16
- const f = n - i;
17
- return (i | 0) ^ Math.imul(Math.floor(f * 1048576), 2654435761);
18
- }
19
- function rand(seed) {
20
- return mix(fold(seed) + 2654435769) / 4294967296;
21
- }
22
- function random(seed) {
23
- let a = fold(seed) + 1831565813 | 0;
24
- return () => {
25
- a = a + 1831565813 | 0;
26
- let t = Math.imul(a ^ a >>> 15, 1 | a);
27
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
28
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
29
- };
30
- }
31
- function noise(x, seed = 0) {
32
- if (!Number.isFinite(x)) return 0;
33
- const i = Math.floor(x);
34
- const f = x - i;
35
- const s = fold(seed) * 668265261;
36
- const a = mix((i | 0) + s) / 2147483648 - 1;
37
- const b = mix((i + 1 | 0) + s) / 2147483648 - 1;
38
- const u = f * f * f * (f * (f * 6 - 15) + 10);
39
- return a + (b - a) * u;
40
- }
6
+ // src/core/spring.ts
41
7
  var SETTLE_TOL = 5e-3;
42
8
  var DEFAULTS = { mass: 1, stiffness: 100, damping: 10 };
43
9
  function springResponse(t, { mass: m, stiffness: k, damping: c }) {
@@ -119,19 +85,6 @@ function parseSpring(name) {
119
85
  const [mass, stiffness, damping] = (m[2] ?? "").split(",").map((s) => s.trim()).filter(Boolean).map(Number);
120
86
  return complete({ mass, stiffness, damping });
121
87
  }
122
- var registered = false;
123
- function registerSpringEases() {
124
- if (registered) return;
125
- registered = true;
126
- const base = springEase();
127
- const ease = (p) => base(p);
128
- ease.config = (mass, stiffness, damping) => {
129
- const bad = [mass, stiffness, damping].some((v) => v !== void 0 && !(typeof v === "number" && v > 0 && Number.isFinite(v)));
130
- return springEase(bad ? {} : { mass, stiffness, damping });
131
- };
132
- gsap.registerEase("spring", ease);
133
- for (const [name, p] of Object.entries(SPRING_PRESETS)) gsap.registerEase(`spring.${name}`, springEase(p));
134
- }
135
88
  var autoWarned = /* @__PURE__ */ new Set();
136
89
  function kfDuration(kf) {
137
90
  if (kf.duration !== "auto") return kf.duration ?? 0;
@@ -144,7 +97,837 @@ function kfDuration(kf) {
144
97
  }
145
98
  return 0.5;
146
99
  }
147
- registerSpringEases();
100
+
101
+ // src/core/cubicBezier.ts
102
+ function cubicBezierEase(x1, y1, x2, y2) {
103
+ const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
104
+ const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
105
+ const X = (t) => ((ax * t + bx) * t + cx) * t;
106
+ const Y = (t) => ((ay * t + by) * t + cy) * t;
107
+ const dX = (t) => (3 * ax * t + 2 * bx) * t + cx;
108
+ return (p) => {
109
+ if (p <= 0) return 0;
110
+ if (p >= 1) return 1;
111
+ let t = p;
112
+ for (let i = 0; i < 8; i++) {
113
+ const err = X(t) - p;
114
+ if (Math.abs(err) < 1e-9) return Y(t);
115
+ const d = dX(t);
116
+ if (Math.abs(d) < 1e-6) break;
117
+ t -= err / d;
118
+ }
119
+ let lo = 0, hi = 1;
120
+ t = p;
121
+ for (let i = 0; i < 60; i++) {
122
+ const x = X(t);
123
+ if (Math.abs(x - p) < 1e-10) break;
124
+ if (x < p) lo = t;
125
+ else hi = t;
126
+ t = (lo + hi) / 2;
127
+ }
128
+ return Y(t);
129
+ };
130
+ }
131
+ var NUM = String.raw`\s*(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s*`;
132
+ var RE = new RegExp(`^\\s*cubic-bezier\\(${NUM},${NUM},${NUM},${NUM}\\)\\s*$`, "i");
133
+ function parseCubicBezier(name) {
134
+ const m = RE.exec(name);
135
+ return m && !cubicBezierProblem(name) ? [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])] : null;
136
+ }
137
+ function cubicBezierProblem(name) {
138
+ const m = RE.exec(name);
139
+ if (!m) return 'expected "cubic-bezier(x1, y1, x2, y2)" with four numbers, like CSS';
140
+ const x1 = Number(m[1]), x2 = Number(m[3]);
141
+ if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) return "x1 and x2 must be between 0 and 1 (y1 and y2 may overshoot)";
142
+ return null;
143
+ }
144
+
145
+ // src/core/pure/ease.ts
146
+ var PI = Math.PI;
147
+ var power = (strength) => ({
148
+ in: (p) => Math.pow(p, strength),
149
+ out: (p) => 1 - Math.pow(1 - p, strength),
150
+ inOut: (p) => p < 0.5 ? 0.5 * Math.pow(2 * p, strength) : 1 - 0.5 * Math.pow(2 * (1 - p), strength)
151
+ });
152
+ var fromOut = (out) => ({
153
+ in: (p) => 1 - out(1 - p),
154
+ out,
155
+ inOut: (p) => p < 0.5 ? 0.5 * (1 - out(1 - 2 * p)) : 0.5 + 0.5 * out(2 * p - 1)
156
+ });
157
+ var mirrored = (easeIn) => ({
158
+ in: easeIn,
159
+ out: (p) => 1 - easeIn(1 - p),
160
+ inOut: (p) => p < 0.5 ? easeIn(p * 2) / 2 : 1 - easeIn((1 - p) * 2) / 2
161
+ });
162
+ var sine = mirrored((p) => p === 1 ? 1 : -Math.cos(p * PI / 2) + 1);
163
+ var circ = mirrored((p) => -(Math.sqrt(1 - p * p) - 1));
164
+ var expoIn = (p) => Math.pow(2, 10 * (p - 1)) * p + p * p * p * p * p * p * (1 - p);
165
+ var expo = {
166
+ in: expoIn,
167
+ out: (p) => 1 - expoIn(1 - p),
168
+ inOut: (p) => p < 0.5 ? expoIn(p * 2) / 2 : 1 - expoIn((1 - p) * 2) / 2
169
+ };
170
+ var bounceOut = (p) => {
171
+ if (p < 1 / 2.75) return 7.5625 * p * p;
172
+ if (p < 2 / 2.75) {
173
+ p -= 1.5 / 2.75;
174
+ return 7.5625 * p * p + 0.75;
175
+ }
176
+ if (p < 2.5 / 2.75) {
177
+ p -= 2.25 / 2.75;
178
+ return 7.5625 * p * p + 0.9375;
179
+ }
180
+ p -= 2.625 / 2.75;
181
+ return 7.5625 * p * p + 0.984375;
182
+ };
183
+ var bounce = fromOut(bounceOut);
184
+ function back(s = 1.70158) {
185
+ const out = (p) => p ? (p -= 1, p * p * ((s + 1) * p + s) + 1) : 0;
186
+ return fromOut(out);
187
+ }
188
+ function elastic(amplitude, period) {
189
+ const make = (per0) => {
190
+ const amp = amplitude ?? 1;
191
+ const a = amp >= 1 ? amp : 1;
192
+ const per = per0 / (amp < 1 ? amp : 1);
193
+ const s = per / (2 * PI) * Math.asin(1 / a);
194
+ const k = 2 * PI / per;
195
+ return fromOut((p) => p === 0 ? 0 : p === 1 ? 1 : a * Math.pow(2, -10 * p) * Math.sin((p - s) * k) + 1);
196
+ };
197
+ const usual = make(period ?? 0.3);
198
+ return { in: usual.in, out: usual.out, inOut: period === void 0 ? make(0.45).inOut : usual.inOut };
199
+ }
200
+ function steps(n) {
201
+ const count = Math.max(1, Math.floor(n) || 1);
202
+ return (p) => p <= 0 ? 0 : p >= 1 ? 1 : Math.min(1, Math.floor(p * (count + 1)) / count);
203
+ }
204
+ var FAMILY = {
205
+ power1: () => power(2),
206
+ power2: () => power(3),
207
+ power3: () => power(4),
208
+ power4: () => power(5),
209
+ quad: () => power(2),
210
+ cubic: () => power(3),
211
+ quart: () => power(4),
212
+ quint: () => power(5),
213
+ strong: () => power(5),
214
+ sine: () => sine,
215
+ circ: () => circ,
216
+ expo: () => expo,
217
+ bounce: () => bounce,
218
+ back: (a) => back(a[0]),
219
+ elastic: (a) => elastic(a[0], a[1])
220
+ };
221
+ var LINEAR = (p) => p;
222
+ var DEFAULT = power(2).out;
223
+ var cache = /* @__PURE__ */ new Map();
224
+ function lookup(name) {
225
+ let hit = cache.get(name);
226
+ if (hit === void 0) {
227
+ hit = parse(name.trim());
228
+ cache.set(name, hit);
229
+ }
230
+ return hit;
231
+ }
232
+ function pureEase(name) {
233
+ return (typeof name === "string" ? lookup(name) : null) ?? DEFAULT;
234
+ }
235
+ function isEaseName(name) {
236
+ return typeof name === "string" && lookup(name) !== null;
237
+ }
238
+ function parse(raw) {
239
+ const s = raw.toLowerCase();
240
+ if (s === "none" || s === "linear" || s === "linear.easenone") return LINEAR;
241
+ if (/^spring/.test(s)) {
242
+ const sp = parseSpring(raw);
243
+ return sp ? springEase(sp) : null;
244
+ }
245
+ if (/^cubic-bezier/.test(s)) {
246
+ const c = parseCubicBezier(raw);
247
+ return c ? cubicBezierEase(...c) : LINEAR;
248
+ }
249
+ const st = /^steps\(\s*([^)]*)\)$/.exec(s);
250
+ if (st) return steps(Number(st[1]));
251
+ if (/^[a-z][a-z0-9]*$/.test(raw) && FAMILY[raw]) return FAMILY[raw]([]).out;
252
+ const m = /^([a-z0-9]+)\.(?:ease)?(in|out|inout)(?:\(([^)]*)\))?$/.exec(s);
253
+ if (!m) return null;
254
+ const make = FAMILY[m[1]];
255
+ if (!make) return null;
256
+ const args = (m[3] ?? "").split(",").map((x) => x.trim()).filter(Boolean).map(Number).filter((n) => Number.isFinite(n));
257
+ const trio = make(args);
258
+ return m[2] === "in" ? trio.in : m[2] === "out" ? trio.out : trio.inOut;
259
+ }
260
+
261
+ // src/core/pure/colorLerp.ts
262
+ function parseCssColor(s) {
263
+ const v = s.trim();
264
+ let m = /^#([0-9a-f]{3,4})$/i.exec(v);
265
+ if (m) {
266
+ const h = m[1];
267
+ const d = (i) => parseInt(h[i] + h[i], 16);
268
+ return [d(0), d(1), d(2), h.length === 4 ? d(3) / 255 : 1];
269
+ }
270
+ m = /^#([0-9a-f]{6})([0-9a-f]{2})?$/i.exec(v);
271
+ if (m) {
272
+ const n = parseInt(m[1], 16);
273
+ return [n >> 16 & 255, n >> 8 & 255, n & 255, m[2] ? parseInt(m[2], 16) / 255 : 1];
274
+ }
275
+ m = /^rgba?\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*(?:,\s*(-?[\d.]+)\s*)?\)$/i.exec(v);
276
+ if (m) return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] === void 0 ? 1 : Number(m[4])];
277
+ return null;
278
+ }
279
+ function rgbInterp(a, b, fallback = () => null) {
280
+ const ca = parseCssColor(a) ?? fallback(a), cb = parseCssColor(b) ?? fallback(b);
281
+ if (!ca || !cb) return (p) => p >= 1 ? b : a;
282
+ return (p) => {
283
+ if (p === 0) return a;
284
+ if (p === 1) return b;
285
+ const ch = (i) => Math.round(ca[i] + (cb[i] - ca[i]) * p);
286
+ const al = Math.round((ca[3] + (cb[3] - ca[3]) * p) * 1e4) / 1e4;
287
+ return `rgba(${ch(0)},${ch(1)},${ch(2)},${al})`;
288
+ };
289
+ }
290
+
291
+ // src/core/pure/props.ts
292
+ var DEG = Math.PI / 180;
293
+ var num = (obj, key, id, unit = 1) => ({
294
+ id,
295
+ kind: "num",
296
+ unit,
297
+ round: 1e6,
298
+ get: () => Number(obj()?.[key]),
299
+ set: (v) => {
300
+ const o = obj();
301
+ if (o) o[key] = v;
302
+ }
303
+ });
304
+ function xy(target, base, which, unit = 1) {
305
+ const o = () => target[base];
306
+ const ax = num(o, "x", `${base}.x`, unit), ay = num(o, "y", `${base}.y`, unit);
307
+ return which === "x" ? [ax] : which === "y" ? [ay] : [ax, ay];
308
+ }
309
+ var VEC = [
310
+ ["scale", "scale", 1],
311
+ ["anchor", "anchor", 1],
312
+ ["pivot", "pivot", 1],
313
+ ["skew", "skew", DEG],
314
+ ["position", "position", 1],
315
+ ["tilePosition", "tilePosition", 1],
316
+ ["tileScale", "tileScale", 1]
317
+ ];
318
+ var PIXI_INERT = /* @__PURE__ */ new Set(["fillColor", "fillAlpha"]);
319
+ function pixiAccessors(target, key) {
320
+ for (const [name, base, unit] of VEC) {
321
+ if (key === name) return xy(target, base, "both", unit);
322
+ if (key === `${name}X`) return xy(target, base, "x", unit);
323
+ if (key === `${name}Y`) return xy(target, base, "y", unit);
324
+ }
325
+ switch (key) {
326
+ case "rotation":
327
+ return [{ id: "rotation", kind: "num", unit: DEG, round: 1e5, exactEnd: true, get: () => Number(target.rotation), set: (v) => {
328
+ target.rotation = v;
329
+ } }];
330
+ case "tint":
331
+ return [{ id: "tint", kind: "color", unit: 1, round: 1, get: () => Number(target.tint), set: (v) => {
332
+ target.tint = v;
333
+ } }];
334
+ case "autoAlpha":
335
+ return [
336
+ { id: "alpha", kind: "num", unit: 1, round: 1e6, get: () => Number(target.alpha), set: (v) => {
337
+ target.alpha = v;
338
+ } },
339
+ // `visible` follows the autoAlpha tweens only, not a plain `alpha` tween on the same layer: a channel of its own
340
+ { id: "visible#auto", kind: "num", unit: 1, round: 1e6, always: true, get: () => Number(target.alpha), set: (v) => {
341
+ target.visible = v !== 0;
342
+ } }
343
+ ];
344
+ default:
345
+ return null;
346
+ }
347
+ }
348
+ function rawAccessor(target, key) {
349
+ return { id: key, kind: "raw", unit: 1, round: 1, get: () => target[key], set: (v) => {
350
+ target[key] = v;
351
+ } };
352
+ }
353
+ function cssAccessor(target, key) {
354
+ return { id: key, kind: "css", unit: 1, round: 1, get: () => target[key], set: (v) => {
355
+ target[key] = v;
356
+ } };
357
+ }
358
+ function plainAccessor(target, key) {
359
+ return { id: key, kind: "num", unit: 1, round: 1e6, get: () => Number(target[key]), set: (v) => {
360
+ target[key] = v;
361
+ } };
362
+ }
363
+ function parseColor(v) {
364
+ if (typeof v === "number") return v;
365
+ if (typeof v !== "string") return null;
366
+ const s = v.trim();
367
+ let m = /^#([0-9a-f]{3})$/i.exec(s);
368
+ if (m) {
369
+ const h = m[1];
370
+ return parseInt(h[0] + h[0] + h[1] + h[1] + h[2] + h[2], 16);
371
+ }
372
+ m = /^#([0-9a-f]{6})(?:[0-9a-f]{2})?$/i.exec(s);
373
+ if (m) return parseInt(m[1], 16);
374
+ m = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i.exec(s);
375
+ if (m) return Number(m[1]) << 16 | Number(m[2]) << 8 | Number(m[3]);
376
+ return null;
377
+ }
378
+ function lerpColor(from, to, p) {
379
+ const r = lerpByte(from >> 16 & 255, to >> 16 & 255, p);
380
+ const g = lerpByte(from >> 8 & 255, to >> 8 & 255, p);
381
+ const b = lerpByte(from & 255, to & 255, p);
382
+ return r << 16 | g << 8 | b;
383
+ }
384
+ function lerpByte(a, b, p) {
385
+ return Math.trunc(a + (b - a) * p);
386
+ }
387
+
388
+ // src/core/pure/PureTimeline.ts
389
+ var quantize = (x) => Math.round(x * 1e7) / 1e7 || 0;
390
+ var TINY = 1e-8;
391
+ var spacer = (duration) => ({ spacer: true, duration });
392
+ var parseRel = (v) => {
393
+ if (typeof v === "number") return { rel: false, n: v };
394
+ if (typeof v === "string") {
395
+ const m = /^([+-])=\s*(-?\d*\.?\d+(?:e[+-]?\d+)?)$/i.exec(v.trim());
396
+ if (m) return { rel: true, n: (m[1] === "-" ? -1 : 1) * parseFloat(m[2]) };
397
+ const n = parseFloat(v);
398
+ if (Number.isFinite(n) && String(n) === v.trim()) return { rel: false, n };
399
+ }
400
+ return null;
401
+ };
402
+ var RESERVED = /* @__PURE__ */ new Set(["duration", "ease", "repeat", "yoyo", "repeatDelay", "onUpdate", "delay"]);
403
+ var CALLBACKS = ["onStart", "onComplete", "onRepeat", "onReverseComplete"];
404
+ var saidCallbacks = false;
405
+ var PureTimeline = class _PureTimeline {
406
+ defaultEase;
407
+ channels = /* @__PURE__ */ new Map();
408
+ channelList = [];
409
+ children = [];
410
+ spans = 0;
411
+ now = 0;
412
+ order = 0;
413
+ killed = false;
414
+ prepared = false;
415
+ constructor(opts = {}) {
416
+ this.defaultEase = opts.defaults?.ease ?? "none";
417
+ }
418
+ // --- building ------------------------------------------------------------------------------------------
419
+ /** Adds a child timeline (played with its local time) or a spacer that lengthens this one. */
420
+ add(child, at) {
421
+ const start = at ?? this.spans;
422
+ if ("spacer" in child) {
423
+ this.spans = Math.max(this.spans, start + child.duration);
424
+ } else if (!(child instanceof _PureTimeline)) {
425
+ this.spans = Math.max(this.spans, start + child.duration());
426
+ } else {
427
+ this.children.push({ child, start: quantize(start), order: this.order++, exclusive: false, last: NaN });
428
+ this.prepared = false;
429
+ this.spans = Math.max(this.spans, start + child.duration());
430
+ }
431
+ return this;
432
+ }
433
+ set(target, vars, at) {
434
+ return this.build(target, null, { ...vars, duration: 0 }, at, "set");
435
+ }
436
+ to(target, vars, at) {
437
+ return this.build(target, null, vars, at, "to");
438
+ }
439
+ from(target, vars, at) {
440
+ return this.build(target, null, vars, at, "from");
441
+ }
442
+ fromTo(target, fromVars, toVars, at) {
443
+ return this.build(target, fromVars, toVars, at, "fromTo");
444
+ }
445
+ build(target, fromVars, vars, at, kind) {
446
+ const t = target;
447
+ const start = quantize((at ?? this.spans) + (Number(vars.delay) || 0));
448
+ const dur = quantize(Math.max(0, vars.duration ?? 0));
449
+ for (const cb of CALLBACKS) if (cb in vars) {
450
+ if (!saidCallbacks) {
451
+ saidCallbacks = true;
452
+ console.warn(`pixi-effects: the timeline does not run ${cb}: what a tween does must be a value of the time (use onUpdate for a redraw); it is ignored`);
453
+ }
454
+ }
455
+ const repeat = Math.max(0, Math.floor(vars.repeat ?? 0));
456
+ const yoyo = !!vars.yoyo;
457
+ const repeatDelay = Math.max(0, vars.repeatDelay ?? 0);
458
+ const easeName = vars.ease ?? this.defaultEase;
459
+ const ease = typeof easeName === "function" ? easeName : pureEase(easeName);
460
+ const given = (v) => {
461
+ const out = /* @__PURE__ */ new Map();
462
+ for (const key of Object.keys(v)) {
463
+ if (RESERVED.has(key) || key === "pixi" || CALLBACKS.includes(key)) continue;
464
+ const value = v[key];
465
+ const acc = parseRel(value) ? plainAccessor(t, key) : typeof value === "string" && parseCssColor(value) ? cssAccessor(t, key) : rawAccessor(t, key);
466
+ out.set(acc.id, { acc, value });
467
+ }
468
+ if (v.pixi) for (const key of Object.keys(v.pixi)) {
469
+ const accs = pixiAccessors(t, key);
470
+ if (!accs) {
471
+ if (!PIXI_INERT.has(key)) console.warn(`pixi-effects: the timeline has no "${key}" shorthand yet; it is ignored`);
472
+ continue;
473
+ }
474
+ for (const acc of accs) out.set(acc.id, { acc, value: v.pixi[key] });
475
+ }
476
+ return out;
477
+ };
478
+ const ends = given(vars);
479
+ const starts = fromVars ? given(fromVars) : /* @__PURE__ */ new Map();
480
+ const ids = kind === "fromTo" ? [.../* @__PURE__ */ new Set([...starts.keys(), ...ends.keys()])] : [...ends.keys()];
481
+ let lastAlpha;
482
+ for (const id of ids) {
483
+ const e = ends.get(id), f = starts.get(id);
484
+ const acc = (e ?? f).acc;
485
+ const ch = this.channel(t, acc);
486
+ const cur = acc.get();
487
+ const color = acc.kind === "color";
488
+ const endSpec = e ? read(e.value, acc) : null;
489
+ if (e && !endSpec && acc.kind === "color") console.warn(`pixi-effects: the timeline cannot read the colour ${JSON.stringify(e.value)} (use #rgb, #rrggbb, rgb(...) or a number); this tween is skipped`);
490
+ const fromSpec = f ? read(f.value, acc) : null;
491
+ let from = null, to, toRel = false;
492
+ if (kind === "from") {
493
+ const g = endSpec ?? { rel: false, n: cur };
494
+ from = g.rel ? cur + g.n : g.n;
495
+ to = cur;
496
+ acc.set(from);
497
+ } else if (kind === "fromTo") {
498
+ if (fromSpec) from = fromSpec.rel ? cur + fromSpec.n : fromSpec.n;
499
+ if (endSpec) {
500
+ to = endSpec.n;
501
+ toRel = endSpec.rel;
502
+ } else to = cur;
503
+ if (from !== null) acc.set(from);
504
+ } else {
505
+ if (!endSpec) continue;
506
+ to = endSpec.n;
507
+ toRel = endSpec.rel;
508
+ }
509
+ if (color && toRel) toRel = false;
510
+ const seg = {
511
+ channel: ch,
512
+ kind,
513
+ createCur: cur,
514
+ start,
515
+ dur,
516
+ ease,
517
+ repeat,
518
+ yoyo,
519
+ repeatDelay,
520
+ from,
521
+ to,
522
+ toRel,
523
+ from0: from,
524
+ to0: to,
525
+ toRel0: toRel,
526
+ onUpdate: vars.onUpdate,
527
+ order: this.order++
528
+ };
529
+ if (id === "visible#auto") seg.partner = lastAlpha;
530
+ else if (id === "alpha") lastAlpha = seg;
531
+ ch.segs.push(seg);
532
+ ch.dirty = true;
533
+ this.prepared = false;
534
+ }
535
+ const total = dur * (repeat + 1) + repeatDelay * repeat;
536
+ this.spans = Math.max(this.spans, start + total);
537
+ return this;
538
+ }
539
+ channel(target, acc) {
540
+ let m = this.channels.get(target);
541
+ if (!m) this.channels.set(target, m = /* @__PURE__ */ new Map());
542
+ let ch = m.get(acc.id);
543
+ if (!ch) {
544
+ ch = { target, acc, pre: NaN, last: NaN, segs: [], dirty: false };
545
+ m.set(acc.id, ch);
546
+ this.channelList.push(ch);
547
+ }
548
+ return ch;
549
+ }
550
+ /**
551
+ * A property moved by a function of the caller (a colour in OKLab, a gradient): `to` and `from` say how to get the two ends from
552
+ * the value before the segment, `make` the interpolator between them. The value is only written while the segment is the last
553
+ * one that has started, so nothing is read or kept between two frames.
554
+ */
555
+ tweenValue(spec) {
556
+ const acc = { id: spec.id, kind: "fn", unit: 1, round: 1, get: spec.get, set: spec.set, make: spec.make };
557
+ const ch = this.channel(spec.holder, acc);
558
+ const start = quantize(spec.at);
559
+ const dur = quantize(Math.max(0, spec.duration));
560
+ const repeat = Math.max(0, Math.floor(spec.repeat ?? 0));
561
+ const repeatDelay = Math.max(0, spec.repeatDelay ?? 0);
562
+ const ease = typeof spec.ease === "function" ? spec.ease : pureEase(spec.ease);
563
+ ch.segs.push({
564
+ channel: ch,
565
+ kind: "to",
566
+ createCur: null,
567
+ start,
568
+ dur,
569
+ ease,
570
+ repeat,
571
+ yoyo: !!spec.yoyo,
572
+ repeatDelay,
573
+ from: null,
574
+ to: null,
575
+ toRel: false,
576
+ from0: null,
577
+ to0: null,
578
+ toRel0: false,
579
+ order: this.order++,
580
+ fnFrom: spec.from,
581
+ fnTo: spec.to,
582
+ fnMake: spec.make,
583
+ write: spec.set
584
+ });
585
+ ch.dirty = true;
586
+ this.prepared = false;
587
+ this.spans = Math.max(this.spans, start + dur * (repeat + 1) + repeatDelay * repeat);
588
+ return this;
589
+ }
590
+ /**
591
+ * A write that is undone when time goes back before it: the property is `value` from `at` on, and what it was before otherwise.
592
+ * `holder` and `id` name the property, so a set and the tweens of the same property are one channel; `write` is the way the caller
593
+ * writes it (it may do more than assign: redraw), and runs again whenever this set is what the property shows.
594
+ */
595
+ setValue(holder, id, at, read2, write2, value) {
596
+ const acc = { id, kind: typeof value === "number" ? "num" : "raw", unit: 1, round: 1e6, get: read2, set: write2 };
597
+ const ch = this.channel(holder, acc);
598
+ ch.segs.push({
599
+ channel: ch,
600
+ kind: "set",
601
+ createCur: null,
602
+ start: quantize(at),
603
+ dur: 0,
604
+ ease: pureEase("none"),
605
+ repeat: 0,
606
+ yoyo: false,
607
+ repeatDelay: 0,
608
+ from: null,
609
+ to: value,
610
+ toRel: false,
611
+ from0: null,
612
+ to0: value,
613
+ toRel0: false,
614
+ order: this.order++,
615
+ step: true,
616
+ write: write2,
617
+ onUpdate: () => write2(read2())
618
+ });
619
+ ch.dirty = true;
620
+ this.prepared = false;
621
+ this.spans = Math.max(this.spans, quantize(at));
622
+ return this;
623
+ }
624
+ // --- reading -------------------------------------------------------------------------------------------
625
+ /** What the timeline will write, for tools and tests: every segment of this timeline and of the ones under it, starts in this timeline's time. */
626
+ segments(offset = 0) {
627
+ const out = [];
628
+ for (const ch of this.channelList) {
629
+ for (const s of ch.segs) {
630
+ out.push({ target: ch.target, id: ch.acc.id, start: s.start + offset, duration: s.dur, repeat: s.repeat, yoyo: s.yoyo, repeatDelay: s.repeatDelay, kind: s.step ? "set" : s.kind });
631
+ }
632
+ }
633
+ for (const c of this.children) out.push(...c.child.segments(offset + c.start));
634
+ return out;
635
+ }
636
+ /** How many timelines are directly under this one. */
637
+ timelines() {
638
+ return this.children.length;
639
+ }
640
+ duration() {
641
+ return this.spans;
642
+ }
643
+ time(value) {
644
+ if (value === void 0) return this.now;
645
+ if (this.killed) return this;
646
+ this.now = value;
647
+ if (!this.prepared) {
648
+ this.prepareDeep();
649
+ const counts = /* @__PURE__ */ new Map();
650
+ this.countChannels(counts);
651
+ this.markExclusive(counts);
652
+ }
653
+ this.evaluate(quantize(value));
654
+ return this;
655
+ }
656
+ /** GSAP's name for `time(t)`. */
657
+ seek(t) {
658
+ return this.time(t);
659
+ }
660
+ progress(p) {
661
+ if (p === void 0) return this.spans > 0 ? this.now / this.spans : 0;
662
+ return this.time(p * this.spans);
663
+ }
664
+ kill() {
665
+ this.killed = true;
666
+ this.channels.clear();
667
+ this.channelList.length = 0;
668
+ this.children.length = 0;
669
+ }
670
+ // --- evaluation ----------------------------------------------------------------------------------------
671
+ /**
672
+ * What GSAP ended up with once it had been played to the end and back (what Movie used to do at build), as rules that depend
673
+ * on the segments of a property only:
674
+ * - a `from` / `fromTo` shows its start from the moment it is built, so a `to` that comes first starts at the start of the
675
+ * `from` built last, else at the property's own value;
676
+ * - a `to` (and a `set`) starts where the segment before it ended;
677
+ * - a `from` ends where the segment before it ended, or, with none before, where the property was when it was built;
678
+ * - before the first segment the property shows that segment's start.
679
+ */
680
+ prepare(ch) {
681
+ if (!ch.dirty) return;
682
+ ch.segs.sort((a, b) => a.start - b.start || a.order - b.order);
683
+ ch.dirty = false;
684
+ let latest = null;
685
+ for (const s of ch.segs) if ((s.kind === "from" || s.kind === "fromTo") && s.from0 !== null && (!latest || s.order > latest.order)) latest = s;
686
+ ch.base0 ??= { v: ch.acc.get() };
687
+ let cur = latest ? latest.from0 : ch.base0.v;
688
+ const base = cur;
689
+ let first = true;
690
+ for (const s of ch.segs) {
691
+ const before = cur;
692
+ s.from = s.from0;
693
+ s.to = s.to0;
694
+ s.toRel = s.toRel0;
695
+ if (s.fnTo) {
696
+ s.from = s.fnFrom ? s.fnFrom(cur) : cur;
697
+ s.to = s.fnTo(s.from, before, base);
698
+ s.interp = void 0;
699
+ } else {
700
+ if (s.partner) {
701
+ s.from = s.partner.from;
702
+ s.to = s.partner.to;
703
+ }
704
+ if (s.from === null) s.from = cur;
705
+ if (s.kind === "from") s.to = first ? s.createCur : cur;
706
+ if (s.toRel) {
707
+ s.to = s.from + s.to;
708
+ s.toRel = false;
709
+ }
710
+ }
711
+ if (first) ch.pre = s.fnTo ? before : s.from;
712
+ cur = endValue(s);
713
+ first = false;
714
+ }
715
+ }
716
+ /** Every channel of this timeline and of its children reads the property it starts from before any of them writes. */
717
+ prepareDeep() {
718
+ if (this.prepared) return;
719
+ this.prepared = true;
720
+ for (const ch of this.channelList) this.prepare(ch);
721
+ this.children.sort((a, b) => a.start - b.start || a.order - b.order);
722
+ for (const c of this.children) {
723
+ c.last = NaN;
724
+ c.child.prepareDeep();
725
+ }
726
+ }
727
+ /** How many channels write each (target, property) in this timeline and everything under it. */
728
+ countChannels(into) {
729
+ for (const ch of this.channelList) {
730
+ let m = into.get(ch.target);
731
+ if (!m) into.set(ch.target, m = /* @__PURE__ */ new Map());
732
+ m.set(ch.acc.id, (m.get(ch.acc.id) ?? 0) + 1);
733
+ }
734
+ for (const c of this.children) c.child.countChannels(into);
735
+ }
736
+ isExclusive(counts) {
737
+ for (const ch of this.channelList) if ((counts.get(ch.target)?.get(ch.acc.id) ?? 0) > 1) return false;
738
+ return this.children.every((c) => c.child.isExclusive(counts));
739
+ }
740
+ markExclusive(counts) {
741
+ for (const c of this.children) {
742
+ c.exclusive = c.child.isExclusive(counts);
743
+ c.child.markExclusive(counts);
744
+ }
745
+ }
746
+ evaluate(t) {
747
+ this.prepareDeep();
748
+ for (const ch of this.channelList) {
749
+ const segs = ch.segs;
750
+ let lo = 0, hi = segs.length;
751
+ while (lo < hi) {
752
+ const mid = lo + hi >> 1;
753
+ if (segs[mid].start <= t + TINY) lo = mid + 1;
754
+ else hi = mid;
755
+ }
756
+ let k = lo - 1;
757
+ while (k >= 0 && segs[k].repeat > 0 && t - segs[k].start <= TINY) k--;
758
+ const seg = k >= 0 ? segs[k] : null;
759
+ if (!seg) {
760
+ const pre = settle(ch.acc, ch.pre);
761
+ write(ch, segs[0], pre);
762
+ const head = segs[0];
763
+ if (pre !== ch.last) {
764
+ ch.last = pre;
765
+ head?.onUpdate?.();
766
+ }
767
+ continue;
768
+ }
769
+ const v = mix(seg, ratioAt(seg, t), over);
770
+ write(ch, seg, v);
771
+ if (v !== ch.last) {
772
+ ch.last = v;
773
+ seg.onUpdate?.();
774
+ }
775
+ }
776
+ for (const c of this.children) {
777
+ const local = Math.min(Math.max(t - c.start, 0), c.child.duration());
778
+ if (c.exclusive && local === c.last) continue;
779
+ c.last = local;
780
+ c.child.now = local;
781
+ c.child.evaluate(quantize(local));
782
+ }
783
+ }
784
+ };
785
+ function write(ch, seg, v) {
786
+ if (!ch.acc.always && ch.acc.get() === v) return;
787
+ (seg?.write ?? ch.acc.set)(v);
788
+ }
789
+ function endValue(s) {
790
+ if (s.dur === 0 && s.repeat === 0) return s.to;
791
+ return s.yoyo && s.repeat % 2 === 1 ? s.from : s.to;
792
+ }
793
+ function read(value, acc) {
794
+ if (acc.kind === "raw" || acc.kind === "css") return { rel: false, n: value };
795
+ if (acc.kind === "color") {
796
+ const c = parseColor(value);
797
+ return c === null ? null : { rel: false, n: c };
798
+ }
799
+ const r = parseRel(value);
800
+ return r ? { rel: r.rel, n: r.n * acc.unit } : null;
801
+ }
802
+ function settle(acc, v) {
803
+ return acc.kind !== "num" ? v : Math.round(v * acc.round) / acc.round;
804
+ }
805
+ var over = false;
806
+ function mix(s, r, ended) {
807
+ const acc = s.channel.acc;
808
+ if (s.step) return s.to;
809
+ if (s.fnMake) {
810
+ const interp = s.interp ??= s.fnMake(s.from, s.to);
811
+ return interp(Math.round(r * 1e6) / 1e6);
812
+ }
813
+ if (acc.kind === "raw") return s.to;
814
+ if (acc.kind === "css") return (s.interp ??= rgbInterp(String(s.from), String(s.to)))(r);
815
+ if (acc.kind === "color") return ended ? endValue(s) : lerpColor(s.from, s.to, r);
816
+ if ((ended || r === 1) && acc.exactEnd) return endValue(s);
817
+ const k = acc.round;
818
+ return Math.round((s.from + (s.to - s.from) * r) * k) / k;
819
+ }
820
+ function ratioAt(s, t) {
821
+ const lt = t - s.start;
822
+ over = true;
823
+ if (s.dur === 0) return 1;
824
+ const total = s.dur * (s.repeat + 1) + s.repeatDelay * s.repeat;
825
+ if (quantize(lt) >= quantize(total) - TINY) return endRatio(s);
826
+ over = false;
827
+ let i = 0;
828
+ let within = lt;
829
+ if (s.repeat > 0) {
830
+ const period = s.dur + s.repeatDelay;
831
+ const q = quantize(lt);
832
+ i = Math.min(s.repeat, Math.max(0, Math.floor(lt / period)));
833
+ while (i > 0 && q <= quantize(i * period)) i--;
834
+ while (i < s.repeat && q > quantize((i + 1) * period)) i++;
835
+ within = quantize(lt - i * period);
836
+ }
837
+ let p = within >= s.dur ? 1 : within <= 0 ? 0 : within / s.dur;
838
+ if (s.yoyo && i % 2 === 1) p = 1 - p;
839
+ return s.ease(p);
840
+ }
841
+ function endRatio(s) {
842
+ return s.yoyo && s.repeat % 2 === 1 ? 0 : 1;
843
+ }
844
+
845
+ // src/core/timelineEngine.ts
846
+ function createTimeline(opts = {}) {
847
+ return new PureTimeline({ paused: opts.paused, defaults: { ease: opts.defaults?.ease ?? "power1.out" } });
848
+ }
849
+ function lengthen(timeline, duration) {
850
+ timeline.add(spacer(duration));
851
+ }
852
+ function parseEase(name) {
853
+ return pureEase(name);
854
+ }
855
+ function setNow(target, vars) {
856
+ const t = target;
857
+ for (const key of Object.keys(vars)) {
858
+ if (key === "pixi") {
859
+ for (const k of Object.keys(vars.pixi)) {
860
+ const accs = pixiAccessors(t, k);
861
+ if (!accs) {
862
+ if (!PIXI_INERT.has(k)) console.warn(`pixi-effects: "${k}" is not a property a keyframe or initial can set; it is ignored`);
863
+ continue;
864
+ }
865
+ const raw = vars.pixi[k];
866
+ for (const acc of accs) {
867
+ const n = acc.kind === "color" ? colorNumber(raw) : Number(raw) * acc.unit;
868
+ if (!Number.isNaN(n)) acc.set(n);
869
+ }
870
+ }
871
+ } else {
872
+ const v = vars[key];
873
+ plainAccessor(t, key).set(typeof v === "number" ? Math.round(v * 1e6) / 1e6 : v);
874
+ }
875
+ }
876
+ }
877
+ function colorNumber(v) {
878
+ const c = parseColor(v);
879
+ if (c === null) {
880
+ console.warn(`pixi-effects: cannot read the colour ${JSON.stringify(v)} (use #rgb, #rrggbb, rgb(...) or a number); it is left as it was`);
881
+ return NaN;
882
+ }
883
+ return c;
884
+ }
885
+ function viaPixi(s) {
886
+ try {
887
+ const [r, g, b, a] = new Color(s).toArray();
888
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a];
889
+ } catch {
890
+ return null;
891
+ }
892
+ }
893
+ function interpolateColors(a, b) {
894
+ return rgbInterp(a, b, viaPixi);
895
+ }
896
+
897
+ // src/expr/random.ts
898
+ function mix2(h) {
899
+ h = Math.imul(h ^ h >>> 16, 2246822507);
900
+ h = Math.imul(h ^ h >>> 13, 3266489909);
901
+ return (h ^ h >>> 16) >>> 0;
902
+ }
903
+ function fold(n) {
904
+ if (!Number.isFinite(n)) return 0;
905
+ const i = Math.floor(n);
906
+ const f = n - i;
907
+ return (i | 0) ^ Math.imul(Math.floor(f * 1048576), 2654435761);
908
+ }
909
+ function rand(seed) {
910
+ return mix2(fold(seed) + 2654435769) / 4294967296;
911
+ }
912
+ function random(seed) {
913
+ let a = fold(seed) + 1831565813 | 0;
914
+ return () => {
915
+ a = a + 1831565813 | 0;
916
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
917
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
918
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
919
+ };
920
+ }
921
+ function noise(x, seed = 0) {
922
+ if (!Number.isFinite(x)) return 0;
923
+ const i = Math.floor(x);
924
+ const f = x - i;
925
+ const s = fold(seed) * 668265261;
926
+ const a = mix2((i | 0) + s) / 2147483648 - 1;
927
+ const b = mix2((i + 1 | 0) + s) / 2147483648 - 1;
928
+ const u = f * f * f * (f * (f * 6 - 15) + 10);
929
+ return a + (b - a) * u;
930
+ }
148
931
 
149
932
  // src/expr/Parser.ts
150
933
  var FUNCS = {
@@ -305,7 +1088,7 @@ function parsePrimary(c) {
305
1088
  }
306
1089
  throw new Error(`unexpected token "${t.type}"`);
307
1090
  }
308
- function parse(src) {
1091
+ function parse2(src) {
309
1092
  const c = new Cursor(tokenize(src));
310
1093
  const node = parseExpr(c);
311
1094
  if (c.peek()) throw new Error("trailing input");
@@ -355,7 +1138,7 @@ function evaluateExpr(source, scope) {
355
1138
  let ast = astCache.get(source);
356
1139
  if (ast === void 0) {
357
1140
  try {
358
- ast = parse(source);
1141
+ ast = parse2(source);
359
1142
  } catch (e) {
360
1143
  console.warn(`pixi-effects: expression failed: ${source}`, e);
361
1144
  ast = ZERO_NODE;
@@ -373,19 +1156,29 @@ function evaluateExpr(source, scope) {
373
1156
  // src/expr/normalizeProps.ts
374
1157
  function normalizeProps(input, scope, options = {}) {
375
1158
  const skip = new Set(options.skipKeys ?? []);
376
- return walk(input, scope, skip, null);
1159
+ return walk(input, scope, skip, null, options);
377
1160
  }
378
- function walk(value, scope, skip, currentKey) {
1161
+ var RELATIVE = /^\s*([+-])=([\s\S]*)$/;
1162
+ function walk(value, scope, skip, currentKey, options) {
379
1163
  if (Array.isArray(value)) {
380
- return value.map((v) => walk(v, scope, skip, currentKey));
1164
+ return value.map((v) => walk(v, scope, skip, currentKey, options));
381
1165
  }
382
1166
  if (value !== null && typeof value === "object") {
383
1167
  const out = {};
384
1168
  for (const k of Object.keys(value)) {
385
- out[k] = walk(value[k], scope, skip, k);
1169
+ out[k] = walk(value[k], scope, skip, k, options);
386
1170
  }
387
1171
  return out;
388
1172
  }
1173
+ if (typeof value === "string" && !(currentKey !== null && skip.has(currentKey))) {
1174
+ const rel = RELATIVE.exec(value);
1175
+ if (rel) {
1176
+ const n = evaluateExpr(rel[2].trim(), scope);
1177
+ if (options.relative === "allow") return `${rel[1]}=${n}`;
1178
+ console.warn(`pixi-effects: "${value.trim()}"${currentKey ? ` (${currentKey})` : ""} is a relative value (measured from where the layer is): it works in a keyframe's to / from / set, not in ${options.where ?? "an initial or a style"}; it is read as the number ${rel[1] === "-" ? -n : n}`);
1179
+ return rel[1] === "-" ? -n : n;
1180
+ }
1181
+ }
389
1182
  if (isExpr(value) && !(currentKey !== null && skip.has(currentKey))) {
390
1183
  if (looksLikeColorString(value)) return value;
391
1184
  return evaluateExpr(value, scope);
@@ -397,56 +1190,6 @@ function looksLikeColorString(s) {
397
1190
  if (s.charCodeAt(0) === 35) return true;
398
1191
  return /^(rgb|hsl|oklab|oklch|lab|lch)\b/i.test(s);
399
1192
  }
400
- function cubicBezierEase(x1, y1, x2, y2) {
401
- const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
402
- const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
403
- const X = (t) => ((ax * t + bx) * t + cx) * t;
404
- const Y = (t) => ((ay * t + by) * t + cy) * t;
405
- const dX = (t) => (3 * ax * t + 2 * bx) * t + cx;
406
- return (p) => {
407
- if (p <= 0) return 0;
408
- if (p >= 1) return 1;
409
- let t = p;
410
- for (let i = 0; i < 8; i++) {
411
- const err = X(t) - p;
412
- if (Math.abs(err) < 1e-9) return Y(t);
413
- const d = dX(t);
414
- if (Math.abs(d) < 1e-6) break;
415
- t -= err / d;
416
- }
417
- let lo = 0, hi = 1;
418
- t = p;
419
- for (let i = 0; i < 60; i++) {
420
- const x = X(t);
421
- if (Math.abs(x - p) < 1e-10) break;
422
- if (x < p) lo = t;
423
- else hi = t;
424
- t = (lo + hi) / 2;
425
- }
426
- return Y(t);
427
- };
428
- }
429
- var NUM = String.raw`\s*(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s*`;
430
- var RE = new RegExp(`^\\s*cubic-bezier\\(${NUM},${NUM},${NUM},${NUM}\\)\\s*$`, "i");
431
- function cubicBezierProblem(name) {
432
- const m = RE.exec(name);
433
- if (!m) return 'expected "cubic-bezier(x1, y1, x2, y2)" with four numbers, like CSS';
434
- const x1 = Number(m[1]), x2 = Number(m[3]);
435
- if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) return "x1 and x2 must be between 0 and 1 (y1 and y2 may overshoot)";
436
- return null;
437
- }
438
- var registered2 = false;
439
- function registerCubicBezierEase() {
440
- if (registered2) return;
441
- registered2 = true;
442
- const base = ((p) => p);
443
- base.config = (x1, y1, x2, y2) => {
444
- const bad = [x1, y1, x2, y2].some((v) => typeof v !== "number" || !Number.isFinite(v)) || x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1;
445
- return bad ? (p) => p : cubicBezierEase(x1, y1, x2, y2);
446
- };
447
- gsap.registerEase("cubic-bezier", base);
448
- }
449
- registerCubicBezierEase();
450
1193
 
451
1194
  // src/core/ease.ts
452
1195
  var FAMILIES = ["power1", "power2", "power3", "power4", "back", "elastic", "bounce", "circ", "expo", "sine"];
@@ -458,7 +1201,7 @@ function checkEase(ease, where) {
458
1201
  const problem = springProblem(ease);
459
1202
  if (!problem || warned.has(ease)) return;
460
1203
  warned.add(ease);
461
- console.warn(`pixi-effects: ${where}: unknown ease "${ease}" \u2014 ${problem}. GSAP would run it as its default ease, power1.out.`);
1204
+ console.warn(`pixi-effects: ${where}: unknown ease "${ease}" \u2014 ${problem}. It runs as its default ease, power1.out.`);
462
1205
  return;
463
1206
  }
464
1207
  if (/^\s*cubic-bezier/i.test(ease)) {
@@ -468,20 +1211,19 @@ function checkEase(ease, where) {
468
1211
  console.warn(`pixi-effects: ${where}: unknown ease "${ease}" \u2014 ${problem}. It runs as 'none' (a straight line).`);
469
1212
  return;
470
1213
  }
471
- const found = gsap.parseEase(ease);
472
- if (typeof found === "function") return;
1214
+ if (isEaseName(ease)) return;
473
1215
  if (warned.has(ease)) return;
474
1216
  warned.add(ease);
475
1217
  if (ease === "") {
476
- console.warn(`pixi-effects: ${where}: an empty ease (GSAP would run its default, power1.out): write 'none' for no easing, or a name such as 'power2.out'`);
1218
+ console.warn(`pixi-effects: ${where}: an empty ease (it would run as power1.out): write 'none' for no easing, or a name such as 'power2.out'`);
477
1219
  return;
478
1220
  }
479
- if (found && /^power[1-4]$/i.test(ease)) {
1221
+ if (/^(power[1-4]|quad|cubic|quart|quint|strong|back|elastic|bounce|circ|expo|sine)$/i.test(ease)) {
480
1222
  console.warn(`pixi-effects: ${where}: "${ease}" is a family, not an ease \u2014 write ${ease.toLowerCase()}.out, ${ease.toLowerCase()}.in or ${ease.toLowerCase()}.inOut`);
481
1223
  return;
482
1224
  }
483
1225
  const guess = suggestName(ease, EASE_NAMES);
484
- console.warn(`pixi-effects: ${where}: unknown ease "${ease}"${guess ? ` \u2014 did you mean "${guess}"?` : ""} (GSAP would run it as its default ease, power1.out). Examples: ${EASE_NAMES.slice(0, 7).join(", ")}, back.out(1.7), elastic.out(1, 0.3), steps(8)`);
1226
+ console.warn(`pixi-effects: ${where}: unknown ease "${ease}"${guess ? ` \u2014 did you mean "${guess}"?` : ""} (it runs as its default ease, power1.out). Examples: ${EASE_NAMES.slice(0, 7).join(", ")}, back.out(1.7), elastic.out(1, 0.3), steps(8)`);
485
1227
  }
486
1228
 
487
1229
  // src/core/Timeline.ts
@@ -583,16 +1325,7 @@ var PIXI_SHORTHANDS = /* @__PURE__ */ new Set([
583
1325
  "tileScaleX",
584
1326
  "tileScaleY",
585
1327
  "tint",
586
- "colorize",
587
- "colorizeAmount",
588
- "colorMatrixFilter",
589
- "blur",
590
- "blurX",
591
- "blurY",
592
- "blurPadding",
593
1328
  "autoAlpha",
594
- "lineColor",
595
- "lineAlpha",
596
1329
  "fillColor",
597
1330
  "fillAlpha"
598
1331
  ]);
@@ -616,7 +1349,7 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
616
1349
  const kf = normalizeKeyframe(raw, parentDuration);
617
1350
  const at = offset + kf.at;
618
1351
  if (kf.kind === "set") {
619
- const resolved = normalizeProps(kf.set, scope, { skipKeys });
1352
+ const resolved = normalizeProps(kf.set, scope, { skipKeys, relative: "allow" });
620
1353
  const { rest, routed } = splitRouted(resolved, routers);
621
1354
  for (const r of routed) timeline.set(r.target, { [r.prop]: r.value }, at);
622
1355
  const { ownProps, filterProps } = partitionProps(rest);
@@ -627,7 +1360,7 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
627
1360
  timeline.set(f, props, at);
628
1361
  }
629
1362
  } else if (kf.kind === "to") {
630
- const resolved = normalizeProps(kf.to, scope, { skipKeys });
1363
+ const resolved = normalizeProps(kf.to, scope, { skipKeys, relative: "allow" });
631
1364
  const { rest, routed } = splitRouted(resolved, routers);
632
1365
  for (const r of routed)
633
1366
  timeline.to(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
@@ -640,7 +1373,7 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
640
1373
  timeline.to(f, { ...props, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
641
1374
  }
642
1375
  } else if (kf.kind === "from") {
643
- const resolved = normalizeProps(kf.from, scope, { skipKeys });
1376
+ const resolved = normalizeProps(kf.from, scope, { skipKeys, relative: "allow" });
644
1377
  const { rest, routed } = splitRouted(resolved, routers);
645
1378
  for (const r of routed)
646
1379
  timeline.from(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
@@ -653,8 +1386,8 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
653
1386
  timeline.from(f, { ...props, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
654
1387
  }
655
1388
  } else {
656
- const fromResolved = normalizeProps(kf.from, scope, { skipKeys });
657
- const toResolved = normalizeProps(kf.to, scope, { skipKeys });
1389
+ const fromResolved = normalizeProps(kf.from, scope, { skipKeys, where: "the from side of a from + to keyframe" });
1390
+ const toResolved = normalizeProps(kf.to, scope, { skipKeys, relative: "allow" });
658
1391
  const fromRouted = splitRouted(fromResolved, routers);
659
1392
  const toRouted = splitRouted(toResolved, routers);
660
1393
  const fromByKey = new Map(fromRouted.routed.map((r) => [r.key, r]));
@@ -704,15 +1437,15 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
704
1437
  }
705
1438
  function applyInitial(target, initial, scope, skipKeys = [], routers) {
706
1439
  if (!initial) return;
707
- const resolved = normalizeProps(initial, scope, { skipKeys });
1440
+ const resolved = normalizeProps(initial, scope, { skipKeys, where: "initial" });
708
1441
  const { rest, routed } = splitRouted(resolved, routers);
709
- for (const r of routed) gsap.set(r.target, { [r.prop]: r.value });
1442
+ for (const r of routed) setNow(r.target, { [r.prop]: r.value });
710
1443
  const { ownProps, filterProps } = partitionProps(rest);
711
- if (Object.keys(ownProps).length > 0) gsap.set(target, pixiwrap(ownProps));
1444
+ if (Object.keys(ownProps).length > 0) setNow(target, pixiwrap(ownProps));
712
1445
  for (const [name, props] of Object.entries(filterProps)) {
713
1446
  const f = findFilter(target, name);
714
1447
  if (!f) continue;
715
- gsap.set(f, props);
1448
+ setNow(f, props);
716
1449
  }
717
1450
  }
718
1451
 
@@ -1147,7 +1880,7 @@ var GrainFilter = class extends Filter {
1147
1880
  };
1148
1881
  var WARP_KINDS = ["wave", "haze"];
1149
1882
  var OPTION_KEYS = ["kind", "strength", "scale", "speed", "angle", "seed"];
1150
- var DEG = Math.PI / 180;
1883
+ var DEG2 = Math.PI / 180;
1151
1884
  var HASH_GL2 = `
1152
1885
  uint hash(uint x) { x ^= x >> 16; x *= 0x7feb352du; x ^= x >> 15; x *= 0x846ca68bu; x ^= x >> 16; return x; }
1153
1886
  float lattice(ivec2 p, uint k) {
@@ -1319,7 +2052,7 @@ var WarpFilter = class extends Filter {
1319
2052
  uStrength: { value: strength, type: "f32" },
1320
2053
  uScale: { value: scale, type: "f32" },
1321
2054
  uSpeed: { value: speed, type: "f32" },
1322
- uAngle: { value: angle * DEG, type: "f32" },
2055
+ uAngle: { value: angle * DEG2, type: "f32" },
1323
2056
  uSeed: { value: Math.floor(seed), type: "f32" },
1324
2057
  uMode: { value: kind === "haze" ? 1 : 0, type: "f32" },
1325
2058
  uTime: { value: 0, type: "f32" }
@@ -1357,10 +2090,10 @@ var WarpFilter = class extends Filter {
1357
2090
  this.u.uSpeed = v;
1358
2091
  }
1359
2092
  get angle() {
1360
- return this.u.uAngle / DEG;
2093
+ return this.u.uAngle / DEG2;
1361
2094
  }
1362
2095
  set angle(v) {
1363
- this.u.uAngle = v * DEG;
2096
+ this.u.uAngle = v * DEG2;
1364
2097
  }
1365
2098
  get seed() {
1366
2099
  return this.u.uSeed;
@@ -1638,6 +2371,8 @@ var NullSequence = class extends Sequence {
1638
2371
  this.buildFilters();
1639
2372
  }
1640
2373
  };
2374
+
2375
+ // src/core/remap.ts
1641
2376
  var BAGS = ["set", "to", "from"];
1642
2377
  function timeRemapOf(spec, prop = "time") {
1643
2378
  const hasInitial = !!spec.initial && "time" in spec.initial;
@@ -1718,7 +2453,7 @@ function bindClock(timeline, clock, prop, remap, duration, at, scope) {
1718
2453
  timeline.fromTo(clock, { [prop]: from }, { [prop]: from + s * duration, duration, ease: "none" }, at);
1719
2454
  }
1720
2455
  function clockTable(remap, prop, duration, at, scope, to) {
1721
- const probe = gsap.timeline({ paused: true });
2456
+ const probe = createTimeline({ paused: true });
1722
2457
  const clock = { [prop]: remap.start ?? 0 };
1723
2458
  bindClock(probe, clock, prop, remap, duration, at, scope);
1724
2459
  probe.time(probe.duration());
@@ -1750,7 +2485,7 @@ function clockTable(remap, prop, duration, at, scope, to) {
1750
2485
  }
1751
2486
 
1752
2487
  // src/space/math.ts
1753
- var DEG2 = Math.PI / 180;
2488
+ var DEG3 = Math.PI / 180;
1754
2489
  var DEFAULT_FOV = 40;
1755
2490
  var MIN_FOV = 1;
1756
2491
  var MAX_FOV = 179;
@@ -1760,7 +2495,7 @@ function clampFov(fov) {
1760
2495
  return Math.min(MAX_FOV, Math.max(MIN_FOV, fov));
1761
2496
  }
1762
2497
  function homeDistance(compHeight, fovDeg) {
1763
- return compHeight / 2 / Math.tan(clampFov(fovDeg) * DEG2 / 2);
2498
+ return compHeight / 2 / Math.tan(clampFov(fovDeg) * DEG3 / 2);
1764
2499
  }
1765
2500
  function homeCamera(compW, compH, fov = DEFAULT_FOV) {
1766
2501
  return {
@@ -1814,7 +2549,7 @@ function cameraBasis(cam, compW, compH) {
1814
2549
  fx,
1815
2550
  fy,
1816
2551
  fz,
1817
- focal: compH / 2 / Math.tan(clampFov(cam.fov) * DEG2 / 2),
2552
+ focal: compH / 2 / Math.tan(clampFov(cam.fov) * DEG3 / 2),
1818
2553
  halfW: compW / 2,
1819
2554
  halfH: compH / 2
1820
2555
  };
@@ -1878,12 +2613,12 @@ function looksLikeLayerName(s) {
1878
2613
  var isName = (v, known) => typeof v === "string" && (known.has(v) || looksLikeLayerName(v));
1879
2614
  function focusNames(props, known) {
1880
2615
  const out = [];
1881
- const read = (bag) => {
2616
+ const read2 = (bag) => {
1882
2617
  const v = bag?.focus;
1883
2618
  if (isName(v, known)) out.push(v);
1884
2619
  };
1885
- read(props.initial);
1886
- for (const kf of props.keyframes ?? []) for (const bag of [kf.set, kf.to, kf.from]) read(bag);
2620
+ read2(props.initial);
2621
+ for (const kf of props.keyframes ?? []) for (const bag of [kf.set, kf.to, kf.from]) read2(bag);
1887
2622
  return out;
1888
2623
  }
1889
2624
  function withFocusResolved(props, zOf, known = /* @__PURE__ */ new Set()) {
@@ -2048,7 +2783,7 @@ function smoothstep(e0, e1, x) {
2048
2783
  const t = Math.min(1, Math.max(0, (x - e0) / (e1 - e0)));
2049
2784
  return t * t * (3 - 2 * t);
2050
2785
  }
2051
- var DEG3 = Math.PI / 180;
2786
+ var DEG4 = Math.PI / 180;
2052
2787
  function norm(v) {
2053
2788
  const l = Math.hypot(v.x, v.y, v.z) || 1;
2054
2789
  return { x: v.x / l, y: v.y / l, z: v.z / l };
@@ -2056,7 +2791,7 @@ function norm(v) {
2056
2791
  var dot = (a, b) => a.x * b.x + a.y * b.y + a.z * b.z;
2057
2792
  var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y, z: a.z - b.z });
2058
2793
  function coneCos(l) {
2059
- const half = Math.min(179, Math.max(0, l.coneAngle)) * DEG3 / 2;
2794
+ const half = Math.min(179, Math.max(0, l.coneAngle)) * DEG4 / 2;
2060
2795
  const inner = half * (1 - Math.min(1, Math.max(0, l.coneFeather)));
2061
2796
  const cosOuter = Math.cos(half);
2062
2797
  const cosInner = Math.max(Math.cos(inner), cosOuter + 1e-4);
@@ -2190,24 +2925,8 @@ var LightSequence = class extends Sequence {
2190
2925
  };
2191
2926
 
2192
2927
  // src/core/revertibleSet.ts
2193
- function revertibleSet(timeline, at, read, write, value) {
2194
- let p = 0;
2195
- let before;
2196
- const proxy = {
2197
- get p() {
2198
- return p;
2199
- },
2200
- set p(next) {
2201
- if (next >= 1 && p < 1) {
2202
- before = read();
2203
- write(value);
2204
- } else if (next < 1 && p >= 1) {
2205
- write(before);
2206
- }
2207
- p = next;
2208
- }
2209
- };
2210
- timeline.set(proxy, { p: 1 }, at);
2928
+ function revertibleSet(timeline, at, read2, write2, value, property) {
2929
+ timeline.setValue(property?.[0] ?? read2, property?.[1] ?? "set", at, read2, write2, value);
2211
2930
  }
2212
2931
 
2213
2932
  // src/expr/colorInterp.ts
@@ -2346,30 +3065,26 @@ function shortestHueDelta(from, to) {
2346
3065
  // src/expr/colorTween.ts
2347
3066
  var css = (c) => typeof c === "number" ? "#" + c.toString(16).padStart(6, "0") : c;
2348
3067
  function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, at, colorSpace, onUpdate, loop = {}) {
2349
- let interp = null;
2350
- const proxy = { p: 0 };
2351
- timeline.fromTo(
2352
- proxy,
2353
- { p: 0 },
2354
- {
2355
- p: 1,
2356
- duration,
2357
- ease,
2358
- ...loop,
2359
- onStart: () => {
2360
- const start = fromValue !== void 0 ? fromValue : target[key];
2361
- if (start === void 0) return;
2362
- interp = colorSpace === "rgb" ? gsap.utils.interpolate(css(start), css(toValue)) : buildColorInterp(start, toValue, colorSpace);
2363
- },
2364
- onUpdate: () => {
2365
- if (interp) {
2366
- target[key] = interp(proxy.p);
2367
- if (onUpdate) onUpdate();
2368
- }
2369
- }
3068
+ const none = () => void 0;
3069
+ timeline.tweenValue({
3070
+ holder: target,
3071
+ id: key,
3072
+ get: () => target[key],
3073
+ set: (v) => {
3074
+ if (v === void 0) return;
3075
+ target[key] = v;
3076
+ if (onUpdate) onUpdate();
2370
3077
  },
2371
- at
2372
- );
3078
+ from: fromValue !== void 0 ? () => fromValue : void 0,
3079
+ to: () => toValue,
3080
+ make: (a, b) => a === void 0 ? none : colorSpace === "rgb" ? interpolateColors(css(a), css(b)) : buildColorInterp(a, b, colorSpace),
3081
+ duration,
3082
+ ease,
3083
+ at,
3084
+ repeat: loop.repeat,
3085
+ yoyo: loop.yoyo,
3086
+ repeatDelay: loop.repeatDelay
3087
+ });
2373
3088
  }
2374
3089
 
2375
3090
  // src/sequences/Image.ts
@@ -2412,7 +3127,7 @@ var ImageSequence = class extends Sequence {
2412
3127
  const sprite = this.target;
2413
3128
  revertibleSet(timeline, at, () => sprite.tint, (c) => {
2414
3129
  sprite.tint = c;
2415
- }, setTint);
3130
+ }, setTint, [sprite, "tint"]);
2416
3131
  }
2417
3132
  if (toTint !== void 0) {
2418
3133
  tweenColor(
@@ -2567,37 +3282,37 @@ function validateGradientKeyframes(spec, who, warn = (m) => console.warn(m), kin
2567
3282
  if (animated && !start) say("a keyframe animates fillGradient but the layer has none to start from: give it a fillGradient first (top level or in initial)");
2568
3283
  }
2569
3284
  function tweenGradient(timeline, holder, fromPatch, toPatch, duration, ease, at, colorSpace, onChange, loop = {}) {
2570
- let a = null, b = null;
2571
- let resting = null;
2572
- let colors = [];
2573
- const proxy = { p: 0 };
2574
- timeline.fromTo(proxy, { p: 0 }, {
2575
- p: 1,
2576
- duration,
2577
- ease,
2578
- ...loop,
2579
- onStart: () => {
2580
- const live = holder.grad;
2581
- a = fromPatch !== void 0 ? mergeGrad(live, fromPatch) : live;
2582
- if (toPatch === void 0) resting ??= live;
2583
- b = toPatch !== void 0 ? mergeGrad(a, toPatch) : resting;
2584
- colors = a.stops.map((s, i) => colorSpace === "rgb" ? gsap.utils.interpolate(s.color, b.stops[i].color) : buildColorInterp(s.color, b.stops[i].color, colorSpace));
3285
+ timeline.tweenValue({
3286
+ holder,
3287
+ id: "grad",
3288
+ get: () => holder.grad,
3289
+ set: (g) => {
3290
+ holder.grad = g;
3291
+ onChange();
2585
3292
  },
2586
- onUpdate: () => {
2587
- if (!a || !b) return;
2588
- const t = proxy.p;
2589
- const l = (x, y) => x + (y - x) * t;
2590
- holder.grad = {
2591
- type: a.type,
2592
- angle: l(a.angle, b.angle),
2593
- center: [l(a.center[0], b.center[0]), l(a.center[1], b.center[1])],
2594
- innerRadius: l(a.innerRadius, b.innerRadius),
2595
- radius: l(a.radius, b.radius),
2596
- stops: a.stops.map((s, i) => ({ offset: l(s.offset, b.stops[i].offset), color: colors[i](t) }))
3293
+ from: fromPatch !== void 0 ? (prev) => mergeGrad(prev, fromPatch) : void 0,
3294
+ to: (start, prev) => toPatch !== void 0 ? mergeGrad(start, toPatch) : prev,
3295
+ make: (a, b) => {
3296
+ const colors = a.stops.map((s, i) => colorSpace === "rgb" ? interpolateColors(s.color, b.stops[i].color) : buildColorInterp(s.color, b.stops[i].color, colorSpace));
3297
+ return (t) => {
3298
+ const l = (x, y) => x + (y - x) * t;
3299
+ return {
3300
+ type: a.type,
3301
+ angle: l(a.angle, b.angle),
3302
+ center: [l(a.center[0], b.center[0]), l(a.center[1], b.center[1])],
3303
+ innerRadius: l(a.innerRadius, b.innerRadius),
3304
+ radius: l(a.radius, b.radius),
3305
+ stops: a.stops.map((s, i) => ({ offset: l(s.offset, b.stops[i].offset), color: colors[i](t) }))
3306
+ };
2597
3307
  };
2598
- onChange();
2599
- }
2600
- }, at);
3308
+ },
3309
+ duration,
3310
+ ease,
3311
+ at,
3312
+ repeat: loop.repeat,
3313
+ yoyo: loop.yoyo,
3314
+ repeatDelay: loop.repeatDelay
3315
+ });
2601
3316
  }
2602
3317
  var PAINT_SIZE = 512;
2603
3318
  var GradientPainter = class {
@@ -2642,16 +3357,20 @@ function bindGradientKeyframes(timeline, holder, keyframes, duration, offset, co
2642
3357
  const at = offset + resolveAt(kf.at, duration);
2643
3358
  const setV = bag(kf.set), fromV = bag(kf.from), toV = bag(kf.to);
2644
3359
  if (setV !== void 0) {
2645
- revertibleSet(
2646
- timeline,
2647
- at,
2648
- () => ({ state: holder.grad }),
2649
- (v) => {
2650
- holder.grad = v.state ?? mergeGrad(holder.grad, v.patch);
3360
+ timeline.tweenValue({
3361
+ holder,
3362
+ id: "grad",
3363
+ get: () => holder.grad,
3364
+ set: (g) => {
3365
+ holder.grad = g;
2651
3366
  onChange();
2652
3367
  },
2653
- { patch: setV }
2654
- );
3368
+ to: (start) => mergeGrad(start, setV),
3369
+ make: (_a, b) => () => b,
3370
+ duration: 0,
3371
+ ease: "none",
3372
+ at
3373
+ });
2655
3374
  }
2656
3375
  if (fromV !== void 0 || toV !== void 0) {
2657
3376
  tweenGradient(timeline, holder, fromV, toV, kfDuration(kf), kf.ease ?? "none", at, colorSpace, onChange, loopVars(kf));
@@ -2843,7 +3562,7 @@ function bindCounterKeyframes(timeline, state, key, refresh, keyframes, parentDu
2843
3562
  revertibleSet(timeline, at, () => state[key], (n) => {
2844
3563
  state[key] = n;
2845
3564
  refresh();
2846
- }, v);
3565
+ }, v, [state, key]);
2847
3566
  }
2848
3567
  if (toV !== void 0 && fromV !== void 0) {
2849
3568
  timeline.fromTo(
@@ -2859,7 +3578,7 @@ function bindCounterKeyframes(timeline, state, key, refresh, keyframes, parentDu
2859
3578
  }
2860
3579
  }
2861
3580
  }
2862
- function bindTextKeyframes(timeline, keyframes, parentDuration, origin, read, write, who) {
3581
+ function bindTextKeyframes(timeline, keyframes, parentDuration, origin, read2, write2, who) {
2863
3582
  for (const kf of keyframes) {
2864
3583
  for (const [what, props] of [["to", kf.to], ["from", kf.from]]) {
2865
3584
  if (props && props.text !== void 0) {
@@ -2868,7 +3587,7 @@ function bindTextKeyframes(timeline, keyframes, parentDuration, origin, read, wr
2868
3587
  }
2869
3588
  const t = kf.set?.text;
2870
3589
  if (t === void 0) continue;
2871
- revertibleSet(timeline, origin + resolveAt(kf.at, parentDuration), read, write, String(t));
3590
+ revertibleSet(timeline, origin + resolveAt(kf.at, parentDuration), read2, write2, String(t));
2872
3591
  }
2873
3592
  }
2874
3593
  function animates(keyframes, key) {
@@ -2890,7 +3609,7 @@ function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, co
2890
3609
  revertibleSet(timeline, at, () => state.fill, (c) => {
2891
3610
  state.fill = c;
2892
3611
  writeFill();
2893
- }, setFill);
3612
+ }, setFill, [state, "fill"]);
2894
3613
  }
2895
3614
  if (toFill !== void 0) {
2896
3615
  tweenColor(
@@ -3046,6 +3765,9 @@ var BUDGET_BYTES = 96 * 1024 * 1024;
3046
3765
  var MIN_HELD = 3;
3047
3766
  var JUMP_SECONDS = 1;
3048
3767
  var EPS = 1e-6;
3768
+ var LOW_BEHIND = 14;
3769
+ var MIN_HELD_TO_READ_BACK = 20;
3770
+ var MIN_HELD_TO_READ_START = 9;
3049
3771
  function reportDecodeError(err, time) {
3050
3772
  const isTransient = err instanceof DOMException && (err.name === "EncodingError" || err.name === "NotSupportedError");
3051
3773
  if (isTransient) console.debug("pixi-effects: transient video decode failure at", time, "s \u2014", err.name);
@@ -3064,9 +3786,22 @@ var FrameCache = class {
3064
3786
  _held = 0;
3065
3787
  _lock = Promise.resolve();
3066
3788
  _lastTime = null;
3789
+ _dir = 0;
3790
+ _gen = 0;
3791
+ _backBusy = false;
3792
+ _backGen = 0;
3793
+ _standbyBusy = false;
3794
+ /** A second pass at the start of the file, ready for the moment a looping video comes round. */
3795
+ _standby = null;
3796
+ _duration;
3797
+ _loop;
3798
+ /** How often it had to wait for a decoder (`restarts`, `singles`) and how many reads ahead it made: for tests and for `check`. */
3799
+ stats = { restarts: 0, singles: 0, prefetches: 0 };
3067
3800
  constructor(sink, options = {}) {
3068
3801
  this.sink = sink;
3069
3802
  this.capacity = options.capacity ?? 30;
3803
+ this._duration = options.duration ?? null;
3804
+ this._loop = !!options.loop;
3070
3805
  }
3071
3806
  /** How many decoded frames are held right now (the sequential reader's own list, plus the exact-time cache of the old path). */
3072
3807
  get heldFrames() {
@@ -3136,24 +3871,157 @@ var FrameCache = class {
3136
3871
  return null;
3137
3872
  }
3138
3873
  async _sequentialAt(time) {
3874
+ const last = this._lastTime;
3875
+ this._lastTime = time;
3876
+ if (last !== null && Math.abs(time - last) <= JUMP_SECONDS) this._dir = time > last + EPS ? 1 : time < last - EPS ? -1 : this._dir;
3877
+ let frame;
3139
3878
  try {
3140
- const last = this._lastTime;
3141
- this._lastTime = time;
3142
- const hit = this._covering(time);
3143
- if (hit) return hit.frame;
3144
- if (last === null || Math.abs(time - last) > JUMP_SECONDS) return await this._single(time);
3145
- const cur = this._cur;
3146
- if (this._iter && cur && time >= cur.ts && time - cur.ts <= JUMP_SECONDS) return await this._advance(time);
3147
- const backwards = !!cur && time < cur.ts;
3148
- return await this._restart(time, backwards ? Math.max(0, time - this._held / 30) : time);
3879
+ frame = await this._serve(time, last);
3149
3880
  } catch (err) {
3150
3881
  reportDecodeError(err, time);
3151
3882
  await this._closeIter();
3152
3883
  return null;
3153
3884
  }
3885
+ this._maybeReadAhead(time);
3886
+ return frame;
3887
+ }
3888
+ async _serve(time, last) {
3889
+ const hit = this._covering(time);
3890
+ if (hit) return hit.frame;
3891
+ const sb = this._standby;
3892
+ if (sb && time >= sb.cur.ts - EPS && time - sb.cur.ts <= JUMP_SECONDS && (!this._iter || !this._cur || this._cur.ts > time + JUMP_SECONDS)) return await this._adoptStandby(time);
3893
+ if (last === null || Math.abs(time - last) > JUMP_SECONDS) return await this._single(time);
3894
+ const cur = this._cur;
3895
+ if (this._iter && cur && time >= cur.ts && time - cur.ts <= JUMP_SECONDS) return await this._advance(time);
3896
+ const backwards = !!cur && time < cur.ts;
3897
+ return await this._restart(time, backwards ? Math.max(0, time - Math.max(1, this._held - 1) / 30) : time);
3898
+ }
3899
+ // ── reading ahead ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
3900
+ _maybeReadAhead(time) {
3901
+ if (this._held === 0) return;
3902
+ if (this._dir === -1) {
3903
+ this._dropStandby();
3904
+ let behind = 0, passed = 0, earliest = Infinity;
3905
+ for (const e of this._frames) {
3906
+ if (e.ts < time - EPS) behind++;
3907
+ else if (e.ts > time + EPS) passed++;
3908
+ if (e.ts < earliest) earliest = e.ts;
3909
+ }
3910
+ if (this._held >= MIN_HELD_TO_READ_BACK && behind < LOW_BEHIND && passed >= 3 && earliest > EPS && !this._backBusy) void this._readBack(earliest, passed);
3911
+ } else if (this._held >= MIN_HELD_TO_READ_START && this._dir === 1 && this._loop && this._duration !== null && time >= this._duration - JUMP_SECONDS && !this._standby && !this._standbyBusy) {
3912
+ void this._readStart();
3913
+ }
3914
+ }
3915
+ /** In the background: the window of frames before the earliest one held (reverse play comes to it next). */
3916
+ async _readBack(earliest, room) {
3917
+ this._backBusy = true;
3918
+ this.stats.prefetches++;
3919
+ const gen = this._gen, bg = this._backGen;
3920
+ const want = Math.max(3, Math.min(Math.floor(this._held * 0.6), room));
3921
+ let iter = null;
3922
+ const got = [];
3923
+ try {
3924
+ iter = this.sink.samples(Math.max(0, earliest - want / 30));
3925
+ for (; ; ) {
3926
+ const r = await iter.next();
3927
+ if (r.done) break;
3928
+ if (gen !== this._gen || r.value.timestamp >= earliest - EPS) {
3929
+ r.value.close?.();
3930
+ break;
3931
+ }
3932
+ got.push(r.value);
3933
+ if (bg !== this._backGen) break;
3934
+ }
3935
+ if (gen === this._gen && bg === this._backGen) {
3936
+ for (let k = 0; k < got.length; k++) {
3937
+ const s = got[k];
3938
+ const frame = s.toVideoFrame();
3939
+ this._keep({ ts: s.timestamp, end: got[k + 1]?.timestamp ?? earliest, frame }, frame);
3940
+ }
3941
+ }
3942
+ } catch (err) {
3943
+ reportDecodeError(err, earliest);
3944
+ } finally {
3945
+ for (const s of got) s.close?.();
3946
+ this._backBusy = false;
3947
+ try {
3948
+ await iter?.return?.(void 0);
3949
+ } catch {
3950
+ }
3951
+ }
3952
+ }
3953
+ /** In the background: the first frames of the file and a pass that stands behind them; held (pinned) until the loop comes round. */
3954
+ async _readStart() {
3955
+ this._standbyBusy = true;
3956
+ this.stats.prefetches++;
3957
+ const gen = this._gen;
3958
+ const want = Math.max(3, Math.min(10, Math.floor(this._held / 3)));
3959
+ let iter = null;
3960
+ const got = [];
3961
+ let keep = false;
3962
+ try {
3963
+ iter = this.sink.samples(0);
3964
+ for (; got.length < want + 1; ) {
3965
+ const r = await iter.next();
3966
+ if (r.done) break;
3967
+ if (gen !== this._gen) {
3968
+ r.value.close?.();
3969
+ break;
3970
+ }
3971
+ got.push(r.value);
3972
+ }
3973
+ if (gen === this._gen && got.length >= 2) {
3974
+ const next = got.pop();
3975
+ let cur = null;
3976
+ got.forEach((s, k) => {
3977
+ const frame = s.toVideoFrame();
3978
+ s.close?.();
3979
+ const entry = { ts: s.timestamp, end: (got[k + 1] ?? next).timestamp, frame, pinned: true };
3980
+ this._keep(entry, frame);
3981
+ cur = entry;
3982
+ });
3983
+ got.length = 0;
3984
+ if (cur) {
3985
+ this._standby = { iter, cur, next };
3986
+ keep = true;
3987
+ }
3988
+ }
3989
+ } catch (err) {
3990
+ reportDecodeError(err, 0);
3991
+ } finally {
3992
+ for (const s of got) s.close?.();
3993
+ this._standbyBusy = false;
3994
+ if (!keep) {
3995
+ try {
3996
+ await iter?.return?.(void 0);
3997
+ } catch {
3998
+ }
3999
+ }
4000
+ }
4001
+ }
4002
+ async _adoptStandby(time) {
4003
+ const sb = this._standby;
4004
+ this._standby = null;
4005
+ await this._closeIter();
4006
+ for (const e of this._frames) e.pinned = false;
4007
+ this._iter = sb.iter;
4008
+ this._next = sb.next;
4009
+ this._cur = sb.cur;
4010
+ return this._advance(time);
4011
+ }
4012
+ _dropStandby() {
4013
+ const sb = this._standby;
4014
+ if (!sb) return;
4015
+ this._standby = null;
4016
+ sb.next?.close?.();
4017
+ for (const e of this._frames.filter((x) => x.pinned)) {
4018
+ e.pinned = false;
4019
+ }
4020
+ void sb.iter.return?.(void 0);
3154
4021
  }
3155
4022
  /** One frame by itself (a seek): getSample, kept so that asking again for the same time costs nothing. The running pass, if any, is closed: it no longer follows the playhead. */
3156
4023
  async _single(time) {
4024
+ this.stats.singles++;
3157
4025
  await this._closeIter();
3158
4026
  this._cur = null;
3159
4027
  const sample = await this.sink.getSample(time);
@@ -3165,6 +4033,8 @@ var FrameCache = class {
3165
4033
  }
3166
4034
  /** Start a pass at `time` (a seek and the frames of one group of pictures), keeping what is still held for a step back. */
3167
4035
  async _restart(time, from = time) {
4036
+ this.stats.restarts++;
4037
+ this._backGen++;
3168
4038
  await this._closeIter();
3169
4039
  this._cur = null;
3170
4040
  const iter = this.sink.samples(from);
@@ -3228,12 +4098,27 @@ var FrameCache = class {
3228
4098
  }
3229
4099
  this._frames.push(entry);
3230
4100
  while (this._frames.length > this._held) {
3231
- const oldest = this._frames.find((e) => e !== this._cur && e !== entry);
3232
- if (!oldest) break;
3233
- this._frames.splice(this._frames.indexOf(oldest), 1);
3234
- oldest.frame.close?.();
4101
+ const victim = this._victim(entry);
4102
+ if (!victim) break;
4103
+ this._frames.splice(this._frames.indexOf(victim), 1);
4104
+ victim.frame.close?.();
3235
4105
  }
3236
4106
  }
4107
+ /**
4108
+ * The frame to let go: one the playhead has already passed (going forward those before it, going back those after it), the furthest
4109
+ * behind first; when there is none, the one furthest ahead of the playhead (the last to be needed). Least recently used when the
4110
+ * direction is not known. Frames of the standby (pinned) and the frame the pass stands on are never chosen.
4111
+ */
4112
+ _victim(except) {
4113
+ const cand = this._frames.filter((e) => e !== this._cur && e !== except && !e.pinned);
4114
+ if (cand.length === 0) return null;
4115
+ if (this._dir === 0) return cand[0];
4116
+ const at = this._lastTime ?? 0;
4117
+ const passed = cand.filter((e) => this._dir === 1 ? e.ts < at - EPS : e.ts > at + EPS);
4118
+ const farthest = (list, sign) => list.reduce((a, b) => sign * (b.ts - a.ts) > 0 ? b : a);
4119
+ if (passed.length) return farthest(passed, this._dir === 1 ? -1 : 1);
4120
+ return farthest(cand, this._dir === 1 ? 1 : -1);
4121
+ }
3237
4122
  async _closeIter() {
3238
4123
  const iter = this._iter;
3239
4124
  this._iter = null;
@@ -3256,6 +4141,8 @@ var FrameCache = class {
3256
4141
  }
3257
4142
  }
3258
4143
  dispose() {
4144
+ this._gen++;
4145
+ this._dropStandby();
3259
4146
  for (const f of this.cache.values()) f?.close?.();
3260
4147
  this.cache.clear();
3261
4148
  this._pending.clear();
@@ -3297,7 +4184,7 @@ var VideoSequence = class extends Sequence {
3297
4184
  this._sourceDuration = data.duration;
3298
4185
  this._remap = remapOf(this.spec, "currentTime", this._sourceDuration);
3299
4186
  this._audioBuffer = this.spec.audio !== false ? data.audioBuffer : null;
3300
- this._cache = new FrameCache(data.sink, { capacity: 30 });
4187
+ this._cache = new FrameCache(data.sink, { capacity: 30, duration: data.duration, loop: !!this.spec.loop });
3301
4188
  const probeFrame = await this._cache.getFrameAt(0);
3302
4189
  const w = probeFrame?.displayWidth ?? 1920;
3303
4190
  const h = probeFrame?.displayHeight ?? 1080;
@@ -3327,8 +4214,8 @@ var VideoSequence = class extends Sequence {
3327
4214
  currentTime = v;
3328
4215
  seq._cur = v;
3329
4216
  const mySeq = ++seq._drawSeq;
3330
- const lookup = sourceLookup(v, seq._sourceDuration, !!seq.spec.loop);
3331
- seq._cache.getFrameAt(lookup).then((frame) => {
4217
+ const lookup2 = sourceLookup(v, seq._sourceDuration, !!seq.spec.loop);
4218
+ seq._cache.getFrameAt(lookup2).then((frame) => {
3332
4219
  if (mySeq !== seq._drawSeq) return;
3333
4220
  if (!frame || !seq._ctx) return;
3334
4221
  seq._ctx.drawImage(frame, 0, 0);
@@ -3382,10 +4269,10 @@ var VideoSequence = class extends Sequence {
3382
4269
  const time = this._cur;
3383
4270
  if (!this.keepHidden && !this.target?.renderable) return;
3384
4271
  const mySeq = ++this._drawSeq;
3385
- const lookup = sourceLookup(time, this._sourceDuration, !!this.spec.loop);
4272
+ const lookup2 = sourceLookup(time, this._sourceDuration, !!this.spec.loop);
3386
4273
  let frame = null;
3387
4274
  try {
3388
- frame = await this._cache.getFrameAt(lookup);
4275
+ frame = await this._cache.getFrameAt(lookup2);
3389
4276
  } catch (err) {
3390
4277
  console.warn("pixi-effects: awaitFrameAt failed at", time, "s \u2014", err);
3391
4278
  return;
@@ -3793,7 +4680,7 @@ function seedGeometry(state, spec, scope) {
3793
4680
  const initial = spec.initial ?? {};
3794
4681
  for (const k of keys) {
3795
4682
  const v = spec[k] ?? initial[k];
3796
- if (v !== void 0) state[k] = num(v, scope);
4683
+ if (v !== void 0) state[k] = num2(v, scope);
3797
4684
  }
3798
4685
  }
3799
4686
  var STROKE_CAPS = ["butt", "round", "square"];
@@ -3911,7 +4798,7 @@ function drawEllipseFromState(g, s) {
3911
4798
  const ay = s.anchorY ?? 0.5;
3912
4799
  g.ellipse((0.5 - ax) * 2 * rx, (0.5 - ay) * 2 * ry, rx, ry);
3913
4800
  }
3914
- var DEG4 = Math.PI / 180;
4801
+ var DEG5 = Math.PI / 180;
3915
4802
  function drawArcFromState(g, s) {
3916
4803
  const r = s.radius ?? 0;
3917
4804
  const ri = Math.max(0, Math.min(s.innerRadius ?? 0, r));
@@ -3926,7 +4813,7 @@ function drawArcFromState(g, s) {
3926
4813
  }
3927
4814
  endDeg = startDeg + Math.sign(endDeg - startDeg) * 359.999;
3928
4815
  }
3929
- const a0 = startDeg * DEG4, a1 = endDeg * DEG4;
4816
+ const a0 = startDeg * DEG5, a1 = endDeg * DEG5;
3930
4817
  const ccw = a1 < a0;
3931
4818
  if (filled) g.moveTo(r * Math.cos(a0), r * Math.sin(a0));
3932
4819
  g.arc(0, 0, r, a0, a1, ccw);
@@ -3940,10 +4827,10 @@ function drawArcFromState(g, s) {
3940
4827
  g.closePath();
3941
4828
  }
3942
4829
  function makeLineDraw(spec, scope) {
3943
- const fx = num(spec.from[0], scope);
3944
- const fy = num(spec.from[1], scope);
3945
- const tx = num(spec.to[0], scope);
3946
- const ty = num(spec.to[1], scope);
4830
+ const fx = num2(spec.from[0], scope);
4831
+ const fy = num2(spec.from[1], scope);
4832
+ const tx = num2(spec.to[0], scope);
4833
+ const ty = num2(spec.to[1], scope);
3947
4834
  return (g) => {
3948
4835
  g.moveTo(fx, fy);
3949
4836
  g.lineTo(tx, ty);
@@ -3953,7 +4840,7 @@ function makePolygonDraw(spec, scope) {
3953
4840
  if (spec.points.length === 0) return () => {
3954
4841
  };
3955
4842
  const flat = [];
3956
- for (const [x, y] of spec.points) flat.push(num(x, scope), num(y, scope));
4843
+ for (const [x, y] of spec.points) flat.push(num2(x, scope), num2(y, scope));
3957
4844
  const closed = !spec.open;
3958
4845
  return (g) => {
3959
4846
  g.poly(flat, closed);
@@ -4019,12 +4906,12 @@ function makeOutline(spec, scope, s, morph) {
4019
4906
  return [ellipseOutline((0.5 - (s.anchorX ?? 0.5)) * 2 * rx, (0.5 - (s.anchorY ?? 0.5)) * 2 * ry, rx, ry)];
4020
4907
  };
4021
4908
  case "line": {
4022
- const lines = [{ pts: [num(spec.from[0], scope), num(spec.from[1], scope), num(spec.to[0], scope), num(spec.to[1], scope)], closed: false }];
4909
+ const lines = [{ pts: [num2(spec.from[0], scope), num2(spec.from[1], scope), num2(spec.to[0], scope), num2(spec.to[1], scope)], closed: false }];
4023
4910
  return () => lines;
4024
4911
  }
4025
4912
  case "polygon": {
4026
4913
  const pts = [];
4027
- for (const [x, y] of spec.points) pts.push(num(x, scope), num(y, scope));
4914
+ for (const [x, y] of spec.points) pts.push(num2(x, scope), num2(y, scope));
4028
4915
  const lines = pts.length >= 4 ? [{ pts, closed: !spec.open }] : [];
4029
4916
  return () => lines;
4030
4917
  }
@@ -4052,7 +4939,7 @@ function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration,
4052
4939
  const live2 = state;
4053
4940
  revertibleSet(timeline, at, () => live2[k], (v2) => {
4054
4941
  live2[k] = v2;
4055
- }, resolved);
4942
+ }, resolved, [live2, k]);
4056
4943
  }
4057
4944
  }
4058
4945
  }
@@ -4096,7 +4983,7 @@ function tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease,
4096
4983
  }
4097
4984
  }
4098
4985
  }
4099
- function num(v, scope) {
4986
+ function num2(v, scope) {
4100
4987
  if (typeof v === "number") return v;
4101
4988
  if (isExpr(v)) return evaluateExpr(v, scope);
4102
4989
  const parsed = parseFloat(v);
@@ -4104,7 +4991,7 @@ function num(v, scope) {
4104
4991
  }
4105
4992
  function numOrZero(v, scope) {
4106
4993
  if (typeof v === "number") return v;
4107
- if (typeof v === "string") return num(v, scope);
4994
+ if (typeof v === "string") return num2(v, scope);
4108
4995
  return 0;
4109
4996
  }
4110
4997
 
@@ -4153,13 +5040,13 @@ var SHADER_BUILTINS = ["iResolution", "iTime", "iFrame"];
4153
5040
  var SHADER_MISSING = ["iMouse", "iChannel0", "iChannel1", "iChannel2", "iChannel3", "iDate", "iTimeDelta", "iSampleRate"];
4154
5041
  var IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
4155
5042
  var HEX = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
4156
- var RESERVED = new Set(`attribute const uniform varying break continue do for while if else in out inout float int void bool true false discard return
5043
+ var RESERVED2 = new Set(`attribute const uniform varying break continue do for while if else in out inout float int void bool true false discard return
4157
5044
  mat2 mat3 mat4 mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat4x2 mat4x3 mat4x4 vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 uvec2 uvec3 uvec4 uint
4158
5045
  sampler2D sampler3D samplerCube sampler2DShadow samplerCubeShadow sampler2DArray sampler2DArrayShadow isampler2D isampler3D isamplerCube isampler2DArray
4159
5046
  usampler2D usampler3D usamplerCube usampler2DArray struct layout centroid flat smooth precision invariant highp mediump lowp
4160
5047
  sample patch subroutine common partition active asm class union enum typedef template this resource goto inline noinline public static extern external
4161
5048
  interface long short double half fixed unsigned superp input output hvec2 hvec3 hvec4 fvec2 fvec3 fvec4 dvec2 dvec3 dvec4 filter sizeof cast namespace using`.split(/\s+/));
4162
- var isIdent = (name) => IDENT.test(name) && !name.startsWith("gl_") && !name.includes("__") && !RESERVED.has(name) && !SHADER_BUILTINS.includes(name);
5049
+ var isIdent = (name) => IDENT.test(name) && !name.startsWith("gl_") && !name.includes("__") && !RESERVED2.has(name) && !SHADER_BUILTINS.includes(name);
4163
5050
  function uniformType(v) {
4164
5051
  if (typeof v === "number") return Number.isFinite(v) ? "float" : null;
4165
5052
  if (typeof v === "string") return HEX.test(v) ? "vec3" : null;
@@ -4497,7 +5384,7 @@ function resolveDim(v, scope) {
4497
5384
  var _CompositionSequence = null;
4498
5385
  async function getCompositionSequence() {
4499
5386
  if (!_CompositionSequence) {
4500
- _CompositionSequence = (await import('./Composition-H4QA24MV.js')).CompositionSequence;
5387
+ _CompositionSequence = (await import('./Composition-4TYDRRRY.js')).CompositionSequence;
4501
5388
  }
4502
5389
  return _CompositionSequence;
4503
5390
  }
@@ -4539,6 +5426,6 @@ async function buildSequenceTree(specs, parent, root) {
4539
5426
  return out;
4540
5427
  }
4541
5428
 
4542
- export { CameraSequence, DEFAULT_FOV, DEG2 as DEG, FALLOFFS, LIGHT_KINDS, LIGHT_PROPS, LightSequence, MANY_BLURRED, MAX_BLUR, MAX_CASTERS, MAX_LIGHTS, MIN_BLUR, NEAR, NullSequence, SHADOW_TAPS, Sequence, bindClock, blurRadius, buildScope, buildSequenceTree, cameraBasis, checkEase, clockTable, contentLength, ensureFilterLibrary, evaluateExpr, flattenSvgPath, focusProblems, homeCamera, homeDistance, layerInitialZ, layerToWorld, lightLevel, noise, packLights, projectLayer, projectPoint, rand, random, registerSequenceType, remapOf, resolveAt, timeRemapOf };
4543
- //# sourceMappingURL=chunk-X3HB4DSR.js.map
4544
- //# sourceMappingURL=chunk-X3HB4DSR.js.map
5429
+ export { CameraSequence, DEFAULT_FOV, DEG3 as DEG, FALLOFFS, LIGHT_KINDS, LIGHT_PROPS, LightSequence, MANY_BLURRED, MAX_BLUR, MAX_CASTERS, MAX_LIGHTS, MIN_BLUR, NEAR, NullSequence, SHADOW_TAPS, Sequence, bindClock, blurRadius, buildScope, buildSequenceTree, cameraBasis, checkEase, clockTable, contentLength, createTimeline, ensureFilterLibrary, evaluateExpr, flattenSvgPath, focusProblems, homeCamera, homeDistance, layerInitialZ, layerToWorld, lengthen, lightLevel, noise, packLights, parseEase, projectLayer, projectPoint, rand, random, registerSequenceType, remapOf, resolveAt, timeRemapOf };
5430
+ //# sourceMappingURL=chunk-TGJACVLD.js.map
5431
+ //# sourceMappingURL=chunk-TGJACVLD.js.map