@pixodesk/svg-animator-web 1.0.35 → 1.0.40

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.
@@ -50,530 +50,107 @@ var PixodeskAnimator = (() => {
50
50
  // src/index.prerendered-waapi.ts
51
51
  var index_prerendered_waapi_exports = {};
52
52
  __export(index_prerendered_waapi_exports, {
53
- PX_ANIMATOR_DATA_KEY: () => PX_ANIMATOR_DATA_KEY,
53
+ PX_ANIMATOR_DOC_KEY: () => PX_ANIMATOR_DOC_KEY,
54
54
  createAnimator: () => createPrerenderedWaapiAnimator,
55
55
  setupAnimationTriggers: () => setupAnimationTriggers
56
56
  });
57
57
 
58
- // ../svg-animator-core/src/util/PxAnimatorUtil.ts
59
- function bezierToSvgPath(path, forceCurves = false) {
60
- var _a, _b, _c, _d;
61
- const v = path.v;
62
- const i = path.i;
63
- const o = path.o;
64
- const c = path.c;
65
- if (!v.length) return "";
66
- const d = [];
67
- const len = v.length;
68
- d.push("M" + v[0][0] + "," + v[0][1]);
69
- for (let idx = 1; idx < len; idx++) {
70
- const prevV = v[idx - 1];
71
- const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
72
- const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
73
- const currV = v[idx];
74
- const isLine = !forceCurves && (prevO[0] === prevV[0] && prevO[1] === prevV[1]) && (currI[0] === currV[0] && currI[1] === currV[1]);
75
- if (isLine) {
76
- d.push("L" + currV[0] + "," + currV[1]);
77
- } else {
78
- d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
79
- }
80
- }
81
- if (c && len > 0) {
82
- const lastV = v[len - 1];
83
- const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
84
- const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
85
- const firstV = v[0];
86
- const isLine = !forceCurves && (lastO[0] === lastV[0] && lastO[1] === lastV[1]) && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
87
- if (!isLine) {
88
- d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
89
- }
90
- d.push("z");
91
- }
92
- return d.join("");
93
- }
94
- function interpolateNum(a, b, t) {
95
- return a + (b - a) * t;
96
- }
97
- function interpolateVec(a, b, t) {
98
- const res = [];
99
- const count = Math.max(a.length, b.length);
100
- for (let i = 0; i < count; i++) {
101
- res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
58
+ // ../svg-animator-core/src/schema/PxSchema.ts
59
+ var PX_UNKNOWN_KEY_ERROR = "unexpected extra key";
60
+ function pathStr(path) {
61
+ if (!path.length) return ".";
62
+ let result = "";
63
+ for (const seg of path) {
64
+ if (seg.startsWith("[")) result += seg;
65
+ else result += (result ? "." : "") + seg;
102
66
  }
103
- return res;
104
- }
105
- function interpolateColor(a, b, t) {
106
- return [
107
- interpolateNum(a[0] || 0, b[0] || 0, t),
108
- interpolateNum(a[1] || 0, b[1] || 0, t),
109
- interpolateNum(a[2] || 0, b[2] || 0, t),
110
- interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
111
- ];
67
+ return result;
112
68
  }
113
- function interpolateBeziers(paths1, paths2, progress) {
114
- const count = Math.max(paths1.length, paths2.length);
115
- const res = [];
116
- for (let i = 0; i < count; i++) {
117
- res.push(interpolateBezier(paths1[i], paths2[i], progress));
69
+ var Base = class {
70
+ _canSanitize(raw) {
71
+ return this.isValid(raw);
118
72
  }
119
- return res;
120
- }
121
- function interpolateBezier(path1, path2, progress) {
122
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
123
- if (!path1 || !path2) return path1 || path2 || { v: [] };
124
- const t = Math.min(Math.max(progress, 0), 1);
125
- const len = Math.min(path1.v.length, path2.v.length);
126
- const v = [];
127
- const i = [];
128
- const o = [];
129
- for (let idx = 0; idx < len; idx++) {
130
- const v1 = path1.v[idx];
131
- const v2 = path2.v[idx];
132
- v.push(interpolateVec(v1, v2, t));
133
- const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
134
- const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
135
- i.push(interpolateVec(i1, i2, t));
136
- const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
137
- const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
138
- o.push(interpolateVec(o1, o2, t));
73
+ optional() {
74
+ return new Optional(this);
139
75
  }
140
- return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
141
- }
142
- function solveCubicBezierX(p1x, p2x, x) {
143
- if (x <= 0) return 0;
144
- if (x >= 1) return 1;
145
- const cx = 3 * p1x;
146
- const bx = 3 * (p2x - p1x) - cx;
147
- const ax = 1 - cx - bx;
148
- function sampleX(t) {
149
- return ((ax * t + bx) * t + cx) * t;
76
+ };
77
+ var Optional = class extends Base {
78
+ constructor(inner) {
79
+ super();
80
+ this.inner = inner;
81
+ this._default = void 0;
150
82
  }
151
- function sampleDX(t) {
152
- return (3 * ax * t + 2 * bx) * t + cx;
83
+ sanitize(raw) {
84
+ if (raw === void 0 || raw === null) return void 0;
85
+ return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
153
86
  }
154
- let t2 = x;
155
- let t0 = 0;
156
- let t1 = 1;
157
- for (let i = 0; i < 8; i++) {
158
- const x2 = sampleX(t2) - x;
159
- if (Math.abs(x2) < 1e-6) return t2;
160
- const d2 = sampleDX(t2);
161
- if (Math.abs(d2) < 1e-6) break;
162
- t2 -= x2 / d2;
87
+ isValid(raw, ctx, path) {
88
+ if (raw === void 0 || raw === null) return true;
89
+ return this.inner.isValid(raw, ctx, path);
163
90
  }
164
- t2 = x;
165
- while (t0 < t1) {
166
- const x2 = sampleX(t2);
167
- if (Math.abs(x2 - x) < 1e-6) return t2;
168
- if (x > x2) t0 = t2;
169
- else t1 = t2;
170
- t2 = (t1 + t0) / 2;
91
+ _canSanitize(raw) {
92
+ return raw === void 0 || raw === null || this.inner._canSanitize(raw);
171
93
  }
172
- return t2;
173
- }
174
- function cubicBezier(easing) {
175
- const [p1x, p1y, p2x, p2y] = easing;
176
- const cy = 3 * p1y;
177
- const by = 3 * (p2y - p1y) - cy;
178
- const ay = 1 - cy - by;
179
- function sampleCurveY(t) {
180
- return ((ay * t + by) * t + cy) * t;
94
+ };
95
+ var Str = class extends Base {
96
+ constructor(_default = "") {
97
+ super();
98
+ this._default = _default;
181
99
  }
182
- return function(x) {
183
- return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
184
- };
185
- }
186
- function lerp2(a, b, t) {
187
- return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
188
- }
189
- function subdivideCubicBezier(p0, p1, p2, p3, t) {
190
- const q0 = lerp2(p0, p1, t);
191
- const q1 = lerp2(p1, p2, t);
192
- const q2 = lerp2(p2, p3, t);
193
- const r0 = lerp2(q0, q1, t);
194
- const r1 = lerp2(q1, q2, t);
195
- const s = lerp2(r0, r1, t);
196
- return {
197
- left: [p0, q0, r0, s],
198
- right: [s, r1, q2, p3]
199
- };
200
- }
201
- function splitEasing(easing, xFraction) {
202
- if (!easing) return { left: void 0, right: void 0 };
203
- if (xFraction <= 0) return { left: void 0, right: easing };
204
- if (xFraction >= 1) return { left: easing, right: void 0 };
205
- const [x1, y1, x2, y2] = easing;
206
- const t = solveCubicBezierX(x1, x2, xFraction);
207
- const p0 = [0, 0];
208
- const p1 = [x1, y1];
209
- const p2 = [x2, y2];
210
- const p3 = [1, 1];
211
- const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
212
- const sx = left[3][0];
213
- const sy = left[3][1];
214
- let leftEasing;
215
- if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
216
- leftEasing = [
217
- left[1][0] / sx,
218
- left[1][1] / sy,
219
- left[2][0] / sx,
220
- left[2][1] / sy
221
- ];
100
+ sanitize(raw) {
101
+ return typeof raw === "string" ? raw : this._default;
222
102
  }
223
- let rightEasing;
224
- const rx = 1 - sx;
225
- const ry = 1 - sy;
226
- if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
227
- rightEasing = [
228
- (right[1][0] - sx) / rx,
229
- (right[1][1] - sy) / ry,
230
- (right[2][0] - sx) / rx,
231
- (right[2][1] - sy) / ry
232
- ];
103
+ isValid(raw, ctx, path) {
104
+ if (typeof raw === "string") return true;
105
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
106
+ return false;
233
107
  }
234
- return { left: leftEasing, right: rightEasing };
235
- }
236
- function reverseEasing(easing) {
237
- if (!easing) return void 0;
238
- return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
239
- }
240
- function toRGBA(color) {
241
- const r = Math.round(color[0] * 255);
242
- const g = Math.round(color[1] * 255);
243
- const b = Math.round(color[2] * 255);
244
- return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
245
- }
246
- function parseRgba(s) {
247
- var _a;
248
- const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
249
- if (!inner) throw new Error("Invalid rgb/rgba format");
250
- const parts = inner.split(",").map((v) => +v.trim());
251
- return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
252
- }
253
- function parseHex(s) {
254
- const hex = s.slice(1);
255
- const isShort = hex.length <= 4;
256
- const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
257
- const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
258
- const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
259
- const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
260
- const result = [
261
- parseInt(r, 16) / 255,
262
- parseInt(g, 16) / 255,
263
- parseInt(b, 16) / 255
264
- ];
265
- if (a !== null) {
266
- result.push(parseInt(a, 16) / 255);
108
+ };
109
+ var Num = class extends Base {
110
+ constructor(_default = 0) {
111
+ super();
112
+ this._default = _default;
267
113
  }
268
- return result;
269
- }
270
- function parseColor(s) {
271
- if (!s) return void 0;
272
- if (Array.isArray(s)) return s;
273
- if (typeof s !== "string") return void 0;
274
- if (s.startsWith("#")) {
275
- return parseHex(s);
276
- } else if (s.startsWith("rgb")) {
277
- return parseRgba(s);
278
- } else {
279
- console.warn("Unsupported color format: " + s);
114
+ sanitize(raw) {
115
+ return typeof raw === "number" && isFinite(raw) ? raw : this._default;
280
116
  }
281
- return void 0;
282
- }
283
- var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
284
- var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
285
- var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
286
- function composeTransformParts(parts, opts) {
287
- var _a;
288
- if (!parts) return "";
289
- const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
290
- const segs = [];
291
- const t = parts.translate;
292
- const o = parts.origin;
293
- const r = parts.rotate;
294
- const k = parts.skew;
295
- const s = parts.scale;
296
- const tu = withUnits ? "px" : "";
297
- const ru = withUnits ? "deg" : "";
298
- if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
299
- if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
300
- if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
301
- if (k !== void 0 && k !== null) segs.push("skewX(" + k + ru + ")");
302
- if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
303
- if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
304
- return segs.join("");
305
- }
306
- function parseTransformParts(str) {
307
- var _a, _b;
308
- if (!str || typeof str !== "string") return void 0;
309
- const out = {};
310
- const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
311
- const order = ["translate", "rotate", "skewX", "scale"];
312
- let lastIdx = -1;
313
- let m;
314
- while ((m = re.exec(str)) !== null) {
315
- const fn = m[1];
316
- const idx = order.indexOf(fn);
317
- if (idx < 0 || idx <= lastIdx) return void 0;
318
- lastIdx = idx;
319
- const nums = m[2].split(/[\s,]+/).filter(Boolean).map(Number);
320
- if (nums.some((n) => Number.isNaN(n))) return void 0;
321
- if (fn === "translate") {
322
- if (nums.length < 1 || nums.length > 2) return void 0;
323
- out.translate = [nums[0], (_a = nums[1]) != null ? _a : 0];
324
- } else if (fn === "rotate") {
325
- if (nums.length !== 1) return void 0;
326
- out.rotate = nums[0];
327
- } else if (fn === "skewX") {
328
- if (nums.length !== 1) return void 0;
329
- out.skew = nums[0];
330
- } else {
331
- if (nums.length < 1 || nums.length > 2) return void 0;
332
- out.scale = [nums[0], (_b = nums[1]) != null ? _b : nums[0]];
333
- }
334
- }
335
- if (str.replace(/([a-zA-Z]+)\s*\(([^)]*)\)/g, "").replace(/[\s,]/g, "").length) return void 0;
336
- return Object.keys(out).length ? out : void 0;
337
- }
338
- function kebabToCamelCaseWord(kebab) {
339
- return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
340
- }
341
- function isCamelCaseWord(word) {
342
- return !word.includes("-") && /[a-z][A-Z]/.test(word);
343
- }
344
- var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
345
- // Transform/positioning
346
- "viewBox",
347
- "preserveAspectRatio",
348
- // Gradient
349
- "gradientUnits",
350
- "gradientTransform",
351
- "spreadMethod",
352
- // Pattern
353
- "patternUnits",
354
- "patternContentUnits",
355
- "patternTransform",
356
- // Clipping/masking
357
- "clipPathUnits",
358
- "maskUnits",
359
- "maskContentUnits",
360
- // Marker (SVG spec keeps these camelCase, like viewBox)
361
- "markerUnits",
362
- "markerWidth",
363
- "markerHeight",
364
- "refX",
365
- "refY",
366
- // Text
367
- "textLength",
368
- "lengthAdjust",
369
- "startOffset",
370
- // Filter
371
- "filterUnits",
372
- "primitiveUnits",
373
- "tableValues",
374
- // feFuncR/G/B/A transfer table (type="table")
375
- "stdDeviation",
376
- "baseFrequency",
377
- "numOctaves",
378
- "surfaceScale",
379
- "diffuseConstant",
380
- "specularConstant",
381
- "specularExponent",
382
- "kernelMatrix",
383
- "kernelUnitLength",
384
- "edgeMode",
385
- "preserveAlpha",
386
- "targetX",
387
- "targetY"
388
- // // Animation
389
- // 'attributeName',
390
- // 'attributeType',
391
- // 'calcMode',
392
- // 'keyTimes',
393
- // 'keySplines',
394
- // 'repeatCount',
395
- // 'repeatDur'
396
- ]);
397
- function camelCaseToKebabWordIfNeeded(camel) {
398
- return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
399
- }
400
- function clamp(value, min, max) {
401
- return Math.max(min, Math.min(value, max));
402
- }
403
- function bezier2D_pointAt(P0, P1, P2, P3, t) {
404
- if (t <= 0) return [P0[0], P0[1]];
405
- if (t >= 1) return [P3[0], P3[1]];
406
- const u = 1 - t;
407
- const u2 = u * u;
408
- const u3 = u2 * u;
409
- const t2 = t * t;
410
- const t3 = t2 * t;
411
- const w0 = u3;
412
- const w1 = 3 * t * u2;
413
- const w2 = 3 * t2 * u;
414
- const w3 = t3;
415
- return [
416
- w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
417
- w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
418
- ];
419
- }
420
- var BEZIER_T_NUDGE = 1e-4;
421
- function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
422
- const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
423
- if (result[0] === 0 && result[1] === 0) {
424
- const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
425
- return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
426
- }
427
- return result;
428
- }
429
- function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
430
- const u = 1 - t;
431
- const a = 3 * u * u;
432
- const b = 6 * t * u;
433
- const c = 3 * t * t;
434
- return [
435
- a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
436
- a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
437
- ];
438
- }
439
- function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
440
- const n = steps + 1;
441
- const ts = new Float64Array(n);
442
- const ds = new Float64Array(n);
443
- let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
444
- ts[0] = 0;
445
- ds[0] = 0;
446
- let cum = 0;
447
- for (let i = 1; i < n; i++) {
448
- const t = i / steps;
449
- const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
450
- const dx = cur[0] - prev[0];
451
- const dy = cur[1] - prev[1];
452
- cum += Math.sqrt(dx * dx + dy * dy);
453
- ts[i] = t;
454
- ds[i] = cum;
455
- prev = cur;
456
- }
457
- return { ts, ds };
458
- }
459
- function bezier2D_arcAtT(lut, t) {
460
- const { ts, ds } = lut;
461
- const last = ts.length - 1;
462
- if (t <= ts[0]) return ds[0];
463
- if (t >= ts[last]) return ds[last];
464
- let lo = 1, hi = last;
465
- while (lo < hi) {
466
- const mid = lo + hi >>> 1;
467
- if (ts[mid] < t) lo = mid + 1;
468
- else hi = mid;
469
- }
470
- const tPrev = ts[hi - 1];
471
- const span = ts[hi] - tPrev;
472
- const frac = span > 0 ? (t - tPrev) / span : 0;
473
- return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);
474
- }
475
- function invertEasing(easing) {
476
- if (!easing) return (y) => y;
477
- const flipped = [easing[1], easing[0], easing[3], easing[2]];
478
- return cubicBezier(flipped);
479
- }
480
-
481
- // ../svg-animator-core/src/schema/PxSchema.ts
482
- var PX_UNKNOWN_KEY_ERROR = "unexpected extra key";
483
- function pathStr(path) {
484
- if (!path.length) return ".";
485
- let result = "";
486
- for (const seg of path) {
487
- if (seg.startsWith("[")) result += seg;
488
- else result += (result ? "." : "") + seg;
489
- }
490
- return result;
491
- }
492
- var Base = class {
493
- _canSanitize(raw) {
494
- return this.isValid(raw);
495
- }
496
- optional() {
497
- return new Optional(this);
117
+ isValid(raw, ctx, path) {
118
+ if (typeof raw === "number" && isFinite(raw)) return true;
119
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
120
+ return false;
498
121
  }
499
122
  };
500
- var Optional = class extends Base {
501
- constructor(inner) {
123
+ var Bool = class extends Base {
124
+ constructor(_default = false) {
502
125
  super();
503
- this.inner = inner;
504
- this._default = void 0;
126
+ this._default = _default;
505
127
  }
506
128
  sanitize(raw) {
507
- if (raw === void 0 || raw === null) return void 0;
508
- return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
129
+ return typeof raw === "boolean" ? raw : this._default;
509
130
  }
510
131
  isValid(raw, ctx, path) {
511
- if (raw === void 0 || raw === null) return true;
512
- return this.inner.isValid(raw, ctx, path);
513
- }
514
- _canSanitize(raw) {
515
- return raw === void 0 || raw === null || this.inner._canSanitize(raw);
132
+ if (typeof raw === "boolean") return true;
133
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
134
+ return false;
516
135
  }
517
136
  };
518
- var Str = class extends Base {
519
- constructor(_default = "") {
137
+ var Literal = class extends Base {
138
+ constructor(value) {
520
139
  super();
521
- this._default = _default;
140
+ this.value = value;
141
+ this._default = value;
522
142
  }
523
143
  sanitize(raw) {
524
- return typeof raw === "string" ? raw : this._default;
144
+ return raw === this.value ? this.value : this._default;
525
145
  }
526
146
  isValid(raw, ctx, path) {
527
- if (typeof raw === "string") return true;
528
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
147
+ if (raw === this.value) return true;
148
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
529
149
  return false;
530
150
  }
531
151
  };
532
- var Num = class extends Base {
533
- constructor(_default = 0) {
534
- super();
535
- this._default = _default;
536
- }
537
- sanitize(raw) {
538
- return typeof raw === "number" && isFinite(raw) ? raw : this._default;
539
- }
540
- isValid(raw, ctx, path) {
541
- if (typeof raw === "number" && isFinite(raw)) return true;
542
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
543
- return false;
544
- }
545
- };
546
- var Bool = class extends Base {
547
- constructor(_default = false) {
548
- super();
549
- this._default = _default;
550
- }
551
- sanitize(raw) {
552
- return typeof raw === "boolean" ? raw : this._default;
553
- }
554
- isValid(raw, ctx, path) {
555
- if (typeof raw === "boolean") return true;
556
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
557
- return false;
558
- }
559
- };
560
- var Literal = class extends Base {
561
- constructor(value) {
562
- super();
563
- this.value = value;
564
- this._default = value;
565
- }
566
- sanitize(raw) {
567
- return raw === this.value ? this.value : this._default;
568
- }
569
- isValid(raw, ctx, path) {
570
- if (raw === this.value) return true;
571
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
572
- return false;
573
- }
574
- };
575
- var Enum = class extends Base {
576
- constructor(values, defaultVal) {
152
+ var Enum = class extends Base {
153
+ constructor(values, defaultVal) {
577
154
  super();
578
155
  this.values = values;
579
156
  this._default = defaultVal != null ? defaultVal : values[0];
@@ -587,6 +164,7 @@ var PixodeskAnimator = (() => {
587
164
  return false;
588
165
  }
589
166
  };
167
+ var UNION_MEMBER_ERROR_LIMIT = 4;
590
168
  var Union = class extends Base {
591
169
  constructor(schemas, defaultVal) {
592
170
  super();
@@ -605,7 +183,35 @@ var PixodeskAnimator = (() => {
605
183
  var _a;
606
184
  const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
607
185
  if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
608
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no union member matched for value " + ((_a = JSON.stringify(raw)) != null ? _a : "").slice(0, 240));
186
+ if (!ctx) return false;
187
+ const base = pathStr(path != null ? path : []);
188
+ ctx.errors.push(base + ": no union member matched for value " + ((_a = JSON.stringify(raw)) != null ? _a : "").slice(0, 240));
189
+ let best;
190
+ let bestDepth = -1;
191
+ const leafExpectations = [];
192
+ for (const member of this.schemas) {
193
+ const sink = { errors: [], warnings: [], strict: ctx.strict };
194
+ member.isValid(raw, sink, path ? [...path] : void 0);
195
+ if (!sink.errors.length) continue;
196
+ const depth = Math.max(...sink.errors.map((e) => e.slice(0, e.indexOf(":")).length));
197
+ if (depth > bestDepth || depth === bestDepth && best && sink.errors.length < best.length) {
198
+ bestDepth = depth;
199
+ best = sink.errors;
200
+ }
201
+ if (depth <= base.length) {
202
+ for (const e of sink.errors) {
203
+ const m = /: expected (.+?), got /.exec(e);
204
+ if (m && !leafExpectations.includes(m[1])) leafExpectations.push(m[1]);
205
+ }
206
+ }
207
+ }
208
+ if (best && bestDepth > base.length) {
209
+ for (const e of best.slice(0, UNION_MEMBER_ERROR_LIMIT)) {
210
+ if (!ctx.errors.includes(e)) ctx.errors.push(e);
211
+ }
212
+ } else if (leafExpectations.length) {
213
+ ctx.errors.push(base + ": expected " + leafExpectations.join(" | "));
214
+ }
609
215
  return false;
610
216
  }
611
217
  _canSanitize(raw) {
@@ -957,6 +563,76 @@ var PixodeskAnimator = (() => {
957
563
  };
958
564
 
959
565
  // ../svg-animator-core/src/format/PxAnimatorConstants.ts
566
+ var PxFillMode = {
567
+ forwards: "forwards",
568
+ backwards: "backwards",
569
+ both: "both",
570
+ none: "none"
571
+ };
572
+ var PxPlaybackDirection = {
573
+ normal: "normal",
574
+ reverse: "reverse",
575
+ alternate: "alternate",
576
+ alternateReverse: "alternate-reverse"
577
+ };
578
+ var PxStartOn = {
579
+ load: "load",
580
+ mouseOver: "mouseOver",
581
+ click: "click",
582
+ scrollIntoView: "scrollIntoView",
583
+ programmatic: "programmatic"
584
+ };
585
+ var PxOutAction = {
586
+ continue: "continue",
587
+ pause: "pause",
588
+ reset: "reset",
589
+ reverse: "reverse"
590
+ };
591
+ var PxFinishAction = {
592
+ hold: "hold",
593
+ reset: "reset"
594
+ };
595
+ var PxScrollKind = {
596
+ view: "view",
597
+ scroll: "scroll"
598
+ };
599
+ var PxScrollAxis = {
600
+ block: "block",
601
+ inline: "inline",
602
+ x: "x",
603
+ y: "y"
604
+ };
605
+ var PxScrollSource = {
606
+ nearest: "nearest",
607
+ root: "root"
608
+ };
609
+ var PxPinAlign = {
610
+ top: "top",
611
+ center: "center",
612
+ bottom: "bottom"
613
+ };
614
+ var PxScrollPhase = {
615
+ cover: "cover",
616
+ contain: "contain",
617
+ entry: "entry",
618
+ exit: "exit",
619
+ entryCrossing: "entry-crossing",
620
+ exitCrossing: "exit-crossing"
621
+ };
622
+ var PxAlongPathMode = {
623
+ sampled: "sampled",
624
+ offsetPath: "offsetPath"
625
+ };
626
+ var PX_TIMELINE_SHARED_KEYS = ["duration", "iterations", "engine", "frameRate"];
627
+ var PX_TIME_ONLY_TIMELINE_KEYS = ["trigger", "delay", "fillMode", "direction"];
628
+ var PX_FLAT_RUNTIME_VIEW_KEYS = [
629
+ ...PX_TIMELINE_SHARED_KEYS,
630
+ ...PX_TIME_ONLY_TIMELINE_KEYS,
631
+ "fill",
632
+ "resetOnFinish",
633
+ "timelineSource",
634
+ "scroll"
635
+ ];
960
636
  var PxTimelineEngine = {
961
637
  native: "native",
962
638
  js: "js"
@@ -964,6 +640,19 @@ var PixodeskAnimator = (() => {
964
640
  var PxTimelineEngineExtra = __spreadProps(__spreadValues({}, PxTimelineEngine), {
965
641
  auto: "auto"
966
642
  });
643
+ var PX_TRIGGER_DEFAULTS = {
644
+ startOn: "load",
645
+ outAction: "continue",
646
+ scrollIntoViewThreshold: 0
647
+ };
648
+ function resolveTrigger(trigger) {
649
+ var _a, _b, _c;
650
+ return {
651
+ startOn: (_a = trigger == null ? void 0 : trigger.startOn) != null ? _a : PX_TRIGGER_DEFAULTS.startOn,
652
+ outAction: (_b = trigger == null ? void 0 : trigger.outAction) != null ? _b : PX_TRIGGER_DEFAULTS.outAction,
653
+ scrollIntoViewThreshold: (_c = trigger == null ? void 0 : trigger.scrollIntoViewThreshold) != null ? _c : PX_TRIGGER_DEFAULTS.scrollIntoViewThreshold
654
+ };
655
+ }
967
656
  var PxLoopRepeatAt = {
968
657
  /** Segment from the START; the repetition runs BEFORE the first keyframe
969
658
  * (intro loops that play until the main timeline begins). */
@@ -1023,10 +712,6 @@ var PixodeskAnimator = (() => {
1023
712
  TRANSFORM_PART.scale,
1024
713
  TRANSFORM_PART.origin
1025
714
  ];
1026
- var PxGradientUnits = {
1027
- userSpaceOnUse: "userSpaceOnUse",
1028
- objectBoundingBox: "objectBoundingBox"
1029
- };
1030
715
  var PxGradientSpreadMethod = {
1031
716
  pad: "pad",
1032
717
  reflect: "reflect",
@@ -1039,8 +724,18 @@ var PixodeskAnimator = (() => {
1039
724
  function getAnimatorConfig(doc) {
1040
725
  var _a;
1041
726
  const cfg = (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
1042
- return cfg ? flattenAnimatorTimeline(cfg) : void 0;
1043
- }
727
+ if (!cfg) return void 0;
728
+ const memoised = wireViewMemo.get(cfg);
729
+ if (memoised) return memoised;
730
+ const wire = cfg;
731
+ const stray = PX_FLAT_RUNTIME_VIEW_KEYS.filter((k) => wire[k] !== void 0);
732
+ const source = stray.length ? __spreadValues({}, wire) : cfg;
733
+ for (const k of stray) delete source[k];
734
+ const view = flattenAnimatorTimeline(source);
735
+ wireViewMemo.set(cfg, view);
736
+ return view;
737
+ }
738
+ var wireViewMemo = /* @__PURE__ */ new WeakMap();
1044
739
  var flattenMemo = /* @__PURE__ */ new WeakMap();
1045
740
  function flattenAnimatorTimeline(cfg) {
1046
741
  const timeline = cfg.timeline;
@@ -1066,7 +761,7 @@ var PixodeskAnimator = (() => {
1066
761
  else if (pin && typeof pin === "object") {
1067
762
  scroll.pin = true;
1068
763
  if (pin.align !== void 0) scroll.pinAlign = pin.align;
1069
- if (pin.top !== void 0) scroll.pinTop = pin.top;
764
+ if (pin.offset !== void 0) scroll.pinOffset = pin.offset;
1070
765
  if (pin.distance !== void 0) scroll.pinDistance = pin.distance;
1071
766
  }
1072
767
  flat.scroll = scroll;
@@ -1093,9 +788,7 @@ var PixodeskAnimator = (() => {
1093
788
  function getBindings(doc) {
1094
789
  var _a;
1095
790
  if (!doc) return void 0;
1096
- const animateById = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animateById;
1097
- if (!animateById) return void 0;
1098
- return Object.entries(animateById).map(([id, anim]) => ({ id: id.startsWith("#") ? id.slice(1) : id, animate: anim }));
791
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.bindings;
1099
792
  }
1100
793
 
1101
794
  // ../svg-animator-core/src/format/PxAnimatorTypes.ts
@@ -1108,14 +801,13 @@ var PixodeskAnimator = (() => {
1108
801
  // e.g. for colors
1109
802
  px.number(),
1110
803
  px.array(px.number()),
1111
- // ORDER LAW: the key-discriminated object shapes (`{path}`, `{paths}`) come BEFORE the
804
+ // ORDER LAW: the key-discriminated object shape (`{pathData}`) comes BEFORE the
1112
805
  // all-optional transform-parts record. In default (non-strict) mode that record accepts
1113
806
  // ANY object (every key optional, unknown keys ignored), so listing it earlier made
1114
- // Union.sanitize route `{path}`/`{paths}` values into it and strip them to `{}` —
807
+ // Union.sanitize route `{pathData}` values into it and strip them to `{}` —
1115
808
  // silent morph-data loss (repro: the editor's keyframeValueSanitize spec). Validity is
1116
809
  // order-independent (`some()`); only sanitize routing depends on this order.
1117
- px.object({ path: px.string() }),
1118
- px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
810
+ px.object({ pathData: px.string() }),
1119
811
  // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
1120
812
  px.lazy(() => px.array(PxGradientStopSchema), []),
1121
813
  px.lazy(() => PxTransformPartsSchema, {})
@@ -1155,7 +847,7 @@ var PixodeskAnimator = (() => {
1155
847
  keyframes: px.array(PxKeyframeSchema).optional(),
1156
848
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
1157
849
  autoOrient: px.boolean().optional(),
1158
- alongPathMode: px.enum(["sampled", "offsetPath"]).optional()
850
+ alongPathMode: px.enum([PxAlongPathMode.sampled, PxAlongPathMode.offsetPath]).optional()
1159
851
  }));
1160
852
  var PxTransformPartsSchema = implementsInterface()(px.object({
1161
853
  translate: px.tuple([px.number(), px.number()]).optional(),
@@ -1179,17 +871,17 @@ var PixodeskAnimator = (() => {
1179
871
  PxAnimationDefinitionSchema
1180
872
  ]));
1181
873
  var PxTriggerSchema = implementsInterface()(px.object({
1182
- startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
1183
- outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
874
+ startOn: px.enum([PxStartOn.load, PxStartOn.mouseOver, PxStartOn.click, PxStartOn.scrollIntoView, PxStartOn.programmatic], PX_TRIGGER_DEFAULTS.startOn).optional(),
875
+ outAction: px.enum([PxOutAction.continue, PxOutAction.pause, PxOutAction.reset, PxOutAction.reverse], PX_TRIGGER_DEFAULTS.outAction).optional(),
1184
876
  // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
1185
877
  // `fill`) or `'reset'` (snap back to the start state). Pairs with `outAction` ("what
1186
878
  // happens when the trigger condition ends"); both end-of-life knobs now read alike.
1187
- finishAction: px.enum(["hold", "reset"]).optional(),
879
+ finishAction: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
1188
880
  scrollIntoViewThreshold: px.number().optional()
1189
881
  }));
1190
882
  var PxGlyphSchema = implementsInterface()(px.object({
1191
883
  width: px.number(),
1192
- d: px.string()
884
+ pathData: px.string()
1193
885
  }));
1194
886
  var PxGlyphFontSchema = implementsInterface()(px.object({
1195
887
  fontFamily: px.string(),
@@ -1201,14 +893,17 @@ var PixodeskAnimator = (() => {
1201
893
  var PxDefsSchema = implementsInterface()(px.object({
1202
894
  easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
1203
895
  animations: px.record(PxAnimationDefinitionSchema).optional(),
1204
- // Review §2.6: the schema now matches the declared type — a style preset is a flat
1205
- // record of string|number attribute values, nothing nested.
1206
- styles: px.record(px.record(px.union([px.string(), px.number()]))).optional(),
1207
896
  fonts: px.record(PxGlyphFontSchema).optional()
1208
897
  }));
1209
- var PX_SCROLL_PHASES = ["cover", "contain", "entry", "exit", "entry-crossing", "exit-crossing"];
1210
898
  var PxScrollRangePointSchema = implementsInterface()(px.object({
1211
- phase: px.enum(PX_SCROLL_PHASES).optional(),
899
+ phase: px.enum([
900
+ PxScrollPhase.cover,
901
+ PxScrollPhase.contain,
902
+ PxScrollPhase.entry,
903
+ PxScrollPhase.exit,
904
+ PxScrollPhase.entryCrossing,
905
+ PxScrollPhase.exitCrossing
906
+ ]).optional(),
1212
907
  fraction: px.number().optional()
1213
908
  }));
1214
909
  var PxScrollRangeSchema = px.object({
@@ -1216,21 +911,21 @@ var PixodeskAnimator = (() => {
1216
911
  end: PxScrollRangePointSchema.optional()
1217
912
  });
1218
913
  var PxScrollSchema = implementsInterface()(px.object({
1219
- kind: px.enum(["view", "scroll"]).optional(),
1220
- axis: px.enum(["block", "inline", "x", "y"]).optional(),
1221
- source: px.enum(["nearest", "root"]).optional(),
914
+ kind: px.enum([PxScrollKind.view, PxScrollKind.scroll]).optional(),
915
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
916
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1222
917
  // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.
1223
918
  subject: px.string().optional(),
1224
919
  smoothing: px.number().optional(),
1225
920
  pin: px.boolean().optional(),
1226
- pinAlign: px.enum(["top", "center", "bottom"]).optional(),
1227
- pinTop: px.number().optional(),
921
+ pinAlign: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
922
+ pinOffset: px.number().optional(),
1228
923
  pinDistance: px.number().optional(),
1229
924
  range: PxScrollRangeSchema.optional()
1230
925
  }));
1231
926
  var PxTimelinePinSchema = implementsInterface()(px.object({
1232
- align: px.enum(["top", "center", "bottom"]).optional(),
1233
- top: px.number().optional(),
927
+ align: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
928
+ offset: px.number().optional(),
1234
929
  distance: px.number().optional()
1235
930
  }));
1236
931
  var PxTimelineEngineSchema = px.enum([PxTimelineEngineExtra.auto, PxTimelineEngineExtra.native, PxTimelineEngineExtra.js]).optional();
@@ -1245,8 +940,8 @@ var PixodeskAnimator = (() => {
1245
940
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1246
941
  // `fillMode` on the wire (CSS `animation-fill-mode`; the runtime view calls it `fill`)
1247
942
  // — never `fill`, which is paint everywhere else in the format.
1248
- fillMode: px.enum(["forwards", "backwards", "both", "none"]).optional(),
1249
- direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional()
943
+ fillMode: px.enum([PxFillMode.forwards, PxFillMode.backwards, PxFillMode.both, PxFillMode.none]).optional(),
944
+ direction: px.enum([PxPlaybackDirection.normal, PxPlaybackDirection.reverse, PxPlaybackDirection.alternate, PxPlaybackDirection.alternateReverse]).optional()
1250
945
  }));
1251
946
  var scrollishTimelineShape = {
1252
947
  // §2.8: duration is a property of the TIMELINE — under scrubbing it is the keyframe
@@ -1257,8 +952,8 @@ var PixodeskAnimator = (() => {
1257
952
  iterations: px.number().optional(),
1258
953
  engine: PxTimelineEngineSchema,
1259
954
  frameRate: px.number().optional(),
1260
- axis: px.enum(["block", "inline", "x", "y"]).optional(),
1261
- source: px.enum(["nearest", "root"]).optional(),
955
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
956
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1262
957
  subject: px.string().optional(),
1263
958
  // 'parent' | 'scroller' | any CSS selector
1264
959
  smoothing: px.number().optional(),
@@ -1278,22 +973,22 @@ var PixodeskAnimator = (() => {
1278
973
  PxScrollTimelineSchema,
1279
974
  PxViewTimelineSchema
1280
975
  ]);
976
+ var PxBindingSchema = implementsInterface()(px.object({
977
+ target: px.string(),
978
+ animateWith: px.array(px.string())
979
+ }));
1281
980
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
1282
981
  // (`mode`, `duration` and `frameRate` live INSIDE `timeline` on the wire — §2.8; they exist
1283
982
  // at this level only on the runtime view, like the rest of the playback dynamics.)
1284
983
  // THE spelling of "what advances progress" — clock / scroll / view (review §2.1).
1285
984
  timeline: PxTimelineSchema.optional(),
1286
985
  definitions: PxDefsSchema.optional(),
1287
- animateById: px.record(PxElementAnimationSchema).optional(),
986
+ bindings: px.array(PxBindingSchema).optional(),
1288
987
  debugGlobalName: px.string().optional(),
1289
988
  // Declared HERE because this is a closed object: an undeclared key would be stripped by
1290
989
  // `sanitize` and flagged by strict validation on our own files.
1291
990
  version: px.string().optional()
1292
991
  }));
1293
- var PxBindingSchema = implementsInterface()(px.object({
1294
- id: px.string(),
1295
- animate: PxElementAnimationSchema
1296
- }));
1297
992
  var PxAttrValueSchema = px.union([
1298
993
  px.string(),
1299
994
  px.number(),
@@ -1347,7 +1042,7 @@ var PixodeskAnimator = (() => {
1347
1042
  height: px.number().optional()
1348
1043
  }));
1349
1044
  var PxClipPathEffectSchema = implementsInterface()(px.object({
1350
- d: PxAnimatableStringSchema.optional()
1045
+ pathData: PxAnimatableStringSchema.optional()
1351
1046
  }));
1352
1047
  var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1353
1048
  offset: PxAnimatableNumberSchema.optional(),
@@ -1375,97 +1070,523 @@ var PixodeskAnimator = (() => {
1375
1070
  px.object({ value: px.array(PxGradientStopSchema) }),
1376
1071
  PxPropertyAnimationSchema
1377
1072
  ]);
1378
- var PxFillGradientEffectSchema = implementsInterface()(px.object({
1379
- // Contextual kind — the `type` convention, see `PxNodeBase.type`.
1380
- type: px.enum([PxGradientType.linear, PxGradientType.radial]),
1381
- start: PxAnimatableVec2Schema.optional(),
1382
- end: PxAnimatableVec2Schema.optional(),
1383
- center: PxAnimatableVec2Schema.optional(),
1384
- radius: PxAnimatableNumberSchema.optional(),
1385
- focal: PxAnimatableVec2Schema.optional(),
1386
- stops: PxAnimatableGradientStopsSchema.optional(),
1387
- gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
1388
- spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
1389
- gradientTransform: px.string().optional()
1390
- }));
1391
- var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
1392
- var PxTextPathEffectSchema = implementsInterface()(px.object({
1393
- pathData: px.string(),
1394
- pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
1395
- lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
1396
- method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
1397
- spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
1398
- startOffset: PxAnimatableNumberSchema.optional(),
1399
- textLength: PxAnimatableNumberSchema.optional()
1400
- }));
1401
- var PxTextEffectSchema = implementsInterface()(px.object({
1402
- useGlyphs: px.boolean().optional()
1403
- }));
1404
- var PxEffectsSchema = implementsInterface()(px.object({
1405
- transformBy: PxTransformByEffectSchema.optional(),
1406
- repeater: PxRepeaterEffectSchema.optional(),
1407
- maskedBy: PxMaskedByEffectSchema.optional(),
1408
- clipPath: PxClipPathEffectSchema.optional(),
1409
- strokeTrim: PxStrokeTrimEffectSchema.optional(),
1410
- clone: PxCloneEffectSchema.optional(),
1411
- fillGradient: PxFillGradientEffectSchema.optional(),
1412
- strokeGradient: PxStrokeGradientEffectSchema.optional(),
1413
- textPath: PxTextPathEffectSchema.optional(),
1414
- text: PxTextEffectSchema.optional()
1415
- }));
1416
- var PxNodeBase = px.openObject({
1417
- // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
1418
- // kind of thing is this", discriminated by its CARRIER — here the node TAG
1419
- // (`rect`, `text`), and inside a sub-object that object's kind (`fillGradient.type`,
1420
- // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
1421
- // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
1422
- // would add words that all mean "type" and still need the carrier to read.
1423
- // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
1424
- // (issues V3), never of distinct key names.
1425
- type: px.string(),
1426
- // The escape hatch for elements that carry a REAL `type` attribute — `<feTurbulence
1427
- // type="fractalNoise">`, `<feFuncR type="table">`, `<feColorMatrix type="saturate">`.
1428
- // `type` is taken by the tag name, so the attribute travels here and the renderer puts
1429
- // it back (`PxAnimatorDOM.renderNode`, `PxRnRender`). Declared here — not merely
1430
- // documented — because a wire key that is not in a schema is invisible to the
1431
- // minifier's reserve list and gets renamed (MINIFICATION-BOUNDARY-PLAN.md §1.1).
1432
- domType: px.string().optional(),
1433
- id: px.string().optional(),
1434
- meta: px.any().optional(),
1435
- // Player-effects bucket emitted by the Editor's lightweight design format.
1436
- // Consumed and removed by `applyPlayerEffects` before any other normalisation
1437
- // (see `createAnimatorImpl`), so downstream code never sees it.
1438
- effects: PxEffectsSchema.optional(),
1439
- // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
1440
- // string ref / array of refs / inline definition / mixed array; mirrors
1441
- // `animator.animateById` map values and what `processNode` resolves at runtime.
1442
- animate: PxElementAnimationSchema.optional(),
1443
- style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
1444
- }, PxAttrValueSchema);
1445
- var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
1446
- children: px.lazy(() => px.array(PxNodeSchema), []).optional()
1447
- }), PxAttrValueSchema);
1448
- var PxSvgNodeExtra = px.object({
1449
- // `"100%"` and other SVG length strings are legal here — a number-only slot rejected
1450
- // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.
1451
- width: px.union([px.number(), px.string()]).optional(),
1452
- height: px.union([px.number(), px.string()]).optional(),
1453
- viewBox: px.string().optional(),
1454
- animator: PxAnimatorConfigSchema.optional()
1455
- });
1456
- var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
1457
- type: px.literal("svg"),
1458
- // override string → literal to require 'svg'
1459
- children: px.array(PxNodeSchema).optional()
1460
- }), PxAttrValueSchema);
1461
- var PxBezierPathSchema = implementsInterface()(px.object({
1462
- v: px.array(px.array(px.number())),
1463
- i: px.array(px.array(px.number())).optional(),
1464
- o: px.array(px.array(px.number())).optional(),
1465
- c: px.boolean().optional()
1466
- }));
1073
+ var PxFillGradientEffectSchema = implementsInterface()(px.object({
1074
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
1075
+ type: px.enum([PxGradientType.linear, PxGradientType.radial]),
1076
+ start: PxAnimatableVec2Schema.optional(),
1077
+ end: PxAnimatableVec2Schema.optional(),
1078
+ center: PxAnimatableVec2Schema.optional(),
1079
+ radius: PxAnimatableNumberSchema.optional(),
1080
+ focal: PxAnimatableVec2Schema.optional(),
1081
+ stops: PxAnimatableGradientStopsSchema.optional(),
1082
+ gradientUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1083
+ spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
1084
+ gradientTransform: px.string().optional()
1085
+ }));
1086
+ var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
1087
+ var PxTextPathEffectSchema = implementsInterface()(px.object({
1088
+ pathData: px.string(),
1089
+ pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
1090
+ lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
1091
+ method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
1092
+ spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
1093
+ startOffset: PxAnimatableNumberSchema.optional(),
1094
+ textLength: PxAnimatableNumberSchema.optional()
1095
+ }));
1096
+ var PxTextEffectSchema = implementsInterface()(px.object({
1097
+ useGlyphs: px.boolean().optional()
1098
+ }));
1099
+ var PxEffectsSchema = implementsInterface()(px.object({
1100
+ transformBy: PxTransformByEffectSchema.optional(),
1101
+ repeater: PxRepeaterEffectSchema.optional(),
1102
+ maskedBy: PxMaskedByEffectSchema.optional(),
1103
+ clipPath: PxClipPathEffectSchema.optional(),
1104
+ strokeTrim: PxStrokeTrimEffectSchema.optional(),
1105
+ clone: PxCloneEffectSchema.optional(),
1106
+ fillGradient: PxFillGradientEffectSchema.optional(),
1107
+ strokeGradient: PxStrokeGradientEffectSchema.optional(),
1108
+ textPath: PxTextPathEffectSchema.optional(),
1109
+ text: PxTextEffectSchema.optional()
1110
+ }));
1111
+ var PxNodeBase = px.openObject({
1112
+ // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
1113
+ // kind of thing is this", discriminated by its CARRIER — here the node TAG
1114
+ // (`rect`, `text`), and inside a sub-object that object's kind (`fillGradient.type`,
1115
+ // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
1116
+ // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
1117
+ // would add words that all mean "type" and still need the carrier to read.
1118
+ // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
1119
+ // (issues V3), never of distinct key names.
1120
+ type: px.string(),
1121
+ // The escape hatch for elements that carry a REAL `type` attribute — `<feTurbulence
1122
+ // type="fractalNoise">`, `<feFuncR type="table">`, `<feColorMatrix type="saturate">`.
1123
+ // `type` is taken by the tag name, so the attribute travels here and the renderer puts
1124
+ // it back (`PxAnimatorDOM.renderNode`, `PxRnRender`). Declared here — not merely
1125
+ // documented — because a wire key that is not in a schema is invisible to the
1126
+ // minifier's reserve list and gets renamed (MINIFICATION-BOUNDARY-PLAN.md §1.1).
1127
+ domType: px.string().optional(),
1128
+ // Text content of a `<text>` / `<tspan>`. Declared, so a non-string value is a schema error
1129
+ // and the minifier reserves the key; `text` is NOT an alias for it and is not read anywhere.
1130
+ textContent: px.string().optional(),
1131
+ id: px.string().optional(),
1132
+ meta: px.any().optional(),
1133
+ // Player-effects bucket emitted by the Editor's lightweight design format.
1134
+ // Consumed and removed by `applyPlayerEffects` before any other normalization
1135
+ // (see `createAnimatorImpl`), so downstream code never sees it.
1136
+ effects: PxEffectsSchema.optional(),
1137
+ // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
1138
+ // string ref / array of refs / inline definition / mixed array; mirrors
1139
+ // `node.animate` values and what `processNode` resolves at runtime.
1140
+ animate: PxElementAnimationSchema.optional(),
1141
+ style: px.record(px.union([px.string(), px.number()])).optional()
1142
+ }, PxAttrValueSchema);
1143
+ var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
1144
+ children: px.lazy(() => px.array(PxNodeSchema), []).optional()
1145
+ }), PxAttrValueSchema);
1146
+ var PxSvgNodeExtra = px.object({
1147
+ // `"100%"` and other SVG length strings are legal here — a number-only slot rejected
1148
+ // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.
1149
+ width: px.union([px.number(), px.string()]).optional(),
1150
+ height: px.union([px.number(), px.string()]).optional(),
1151
+ viewBox: px.string().optional(),
1152
+ animator: PxAnimatorConfigSchema.optional()
1153
+ });
1154
+ var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
1155
+ type: px.literal("svg"),
1156
+ // override string → literal to require 'svg'
1157
+ children: px.array(PxNodeSchema).optional()
1158
+ }), PxAttrValueSchema);
1159
+ var PxBezierPathSchema = implementsInterface()(px.object({
1160
+ v: px.array(px.array(px.number())),
1161
+ i: px.array(px.array(px.number())).optional(),
1162
+ o: px.array(px.array(px.number())).optional(),
1163
+ c: px.boolean().optional()
1164
+ }));
1165
+
1166
+ // ../svg-animator-core/src/util/PxAnimatorUtil.ts
1167
+ function bezierToSvgPath(path, forceCurves = false) {
1168
+ var _a, _b, _c, _d;
1169
+ const v = path.v;
1170
+ const i = path.i;
1171
+ const o = path.o;
1172
+ const c = path.c;
1173
+ if (!v.length) return "";
1174
+ const d = [];
1175
+ const len = v.length;
1176
+ d.push("M" + v[0][0] + "," + v[0][1]);
1177
+ for (let idx = 1; idx < len; idx++) {
1178
+ const prevV = v[idx - 1];
1179
+ const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
1180
+ const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
1181
+ const currV = v[idx];
1182
+ const isLine = !forceCurves && (prevO[0] === prevV[0] && prevO[1] === prevV[1]) && (currI[0] === currV[0] && currI[1] === currV[1]);
1183
+ if (isLine) {
1184
+ d.push("L" + currV[0] + "," + currV[1]);
1185
+ } else {
1186
+ d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
1187
+ }
1188
+ }
1189
+ if (c && len > 0) {
1190
+ const lastV = v[len - 1];
1191
+ const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
1192
+ const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
1193
+ const firstV = v[0];
1194
+ const isLine = !forceCurves && (lastO[0] === lastV[0] && lastO[1] === lastV[1]) && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
1195
+ if (!isLine) {
1196
+ d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
1197
+ }
1198
+ d.push("z");
1199
+ }
1200
+ return d.join("");
1201
+ }
1202
+ function interpolateNum(a, b, t) {
1203
+ return a + (b - a) * t;
1204
+ }
1205
+ function interpolateVec(a, b, t) {
1206
+ const res = [];
1207
+ const count = Math.max(a.length, b.length);
1208
+ for (let i = 0; i < count; i++) {
1209
+ res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
1210
+ }
1211
+ return res;
1212
+ }
1213
+ function interpolateColor(a, b, t) {
1214
+ return [
1215
+ interpolateNum(a[0] || 0, b[0] || 0, t),
1216
+ interpolateNum(a[1] || 0, b[1] || 0, t),
1217
+ interpolateNum(a[2] || 0, b[2] || 0, t),
1218
+ interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
1219
+ ];
1220
+ }
1221
+ function interpolateBeziers(paths1, paths2, progress) {
1222
+ const count = Math.max(paths1.length, paths2.length);
1223
+ const res = [];
1224
+ for (let i = 0; i < count; i++) {
1225
+ res.push(interpolateBezier(paths1[i], paths2[i], progress));
1226
+ }
1227
+ return res;
1228
+ }
1229
+ function interpolateBezier(path1, path2, progress) {
1230
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1231
+ if (!path1 || !path2) return path1 || path2 || { v: [] };
1232
+ const t = Math.min(Math.max(progress, 0), 1);
1233
+ const len = Math.min(path1.v.length, path2.v.length);
1234
+ const v = [];
1235
+ const i = [];
1236
+ const o = [];
1237
+ for (let idx = 0; idx < len; idx++) {
1238
+ const v1 = path1.v[idx];
1239
+ const v2 = path2.v[idx];
1240
+ v.push(interpolateVec(v1, v2, t));
1241
+ const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
1242
+ const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
1243
+ i.push(interpolateVec(i1, i2, t));
1244
+ const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
1245
+ const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
1246
+ o.push(interpolateVec(o1, o2, t));
1247
+ }
1248
+ return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
1249
+ }
1250
+ function solveCubicBezierX(p1x, p2x, x) {
1251
+ if (x <= 0) return 0;
1252
+ if (x >= 1) return 1;
1253
+ const cx = 3 * p1x;
1254
+ const bx = 3 * (p2x - p1x) - cx;
1255
+ const ax = 1 - cx - bx;
1256
+ function sampleX(t) {
1257
+ return ((ax * t + bx) * t + cx) * t;
1258
+ }
1259
+ function sampleDX(t) {
1260
+ return (3 * ax * t + 2 * bx) * t + cx;
1261
+ }
1262
+ let t2 = x;
1263
+ let t0 = 0;
1264
+ let t1 = 1;
1265
+ for (let i = 0; i < 8; i++) {
1266
+ const x2 = sampleX(t2) - x;
1267
+ if (Math.abs(x2) < 1e-6) return t2;
1268
+ const d2 = sampleDX(t2);
1269
+ if (Math.abs(d2) < 1e-6) break;
1270
+ t2 -= x2 / d2;
1271
+ }
1272
+ t2 = x;
1273
+ while (t0 < t1) {
1274
+ const x2 = sampleX(t2);
1275
+ if (Math.abs(x2 - x) < 1e-6) return t2;
1276
+ if (x > x2) t0 = t2;
1277
+ else t1 = t2;
1278
+ t2 = (t1 + t0) / 2;
1279
+ }
1280
+ return t2;
1281
+ }
1282
+ function cubicBezier(easing) {
1283
+ const [p1x, p1y, p2x, p2y] = easing;
1284
+ const cy = 3 * p1y;
1285
+ const by = 3 * (p2y - p1y) - cy;
1286
+ const ay = 1 - cy - by;
1287
+ function sampleCurveY(t) {
1288
+ return ((ay * t + by) * t + cy) * t;
1289
+ }
1290
+ return function(x) {
1291
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
1292
+ };
1293
+ }
1294
+ function lerp2(a, b, t) {
1295
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
1296
+ }
1297
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
1298
+ const q0 = lerp2(p0, p1, t);
1299
+ const q1 = lerp2(p1, p2, t);
1300
+ const q2 = lerp2(p2, p3, t);
1301
+ const r0 = lerp2(q0, q1, t);
1302
+ const r1 = lerp2(q1, q2, t);
1303
+ const s = lerp2(r0, r1, t);
1304
+ return {
1305
+ left: [p0, q0, r0, s],
1306
+ right: [s, r1, q2, p3]
1307
+ };
1308
+ }
1309
+ function splitEasing(easing, xFraction) {
1310
+ if (!easing) return { left: void 0, right: void 0 };
1311
+ if (xFraction <= 0) return { left: void 0, right: easing };
1312
+ if (xFraction >= 1) return { left: easing, right: void 0 };
1313
+ const [x1, y1, x2, y2] = easing;
1314
+ const t = solveCubicBezierX(x1, x2, xFraction);
1315
+ const p0 = [0, 0];
1316
+ const p1 = [x1, y1];
1317
+ const p2 = [x2, y2];
1318
+ const p3 = [1, 1];
1319
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
1320
+ const sx = left[3][0];
1321
+ const sy = left[3][1];
1322
+ let leftEasing;
1323
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
1324
+ leftEasing = [
1325
+ left[1][0] / sx,
1326
+ left[1][1] / sy,
1327
+ left[2][0] / sx,
1328
+ left[2][1] / sy
1329
+ ];
1330
+ }
1331
+ let rightEasing;
1332
+ const rx = 1 - sx;
1333
+ const ry = 1 - sy;
1334
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
1335
+ rightEasing = [
1336
+ (right[1][0] - sx) / rx,
1337
+ (right[1][1] - sy) / ry,
1338
+ (right[2][0] - sx) / rx,
1339
+ (right[2][1] - sy) / ry
1340
+ ];
1341
+ }
1342
+ return { left: leftEasing, right: rightEasing };
1343
+ }
1344
+ function reverseEasing(easing) {
1345
+ if (!easing) return void 0;
1346
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
1347
+ }
1348
+ function toRGBA(color) {
1349
+ const r = Math.round(color[0] * 255);
1350
+ const g = Math.round(color[1] * 255);
1351
+ const b = Math.round(color[2] * 255);
1352
+ return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
1353
+ }
1354
+ function parseRgba(s) {
1355
+ var _a;
1356
+ const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
1357
+ if (!inner) throw new Error("Invalid rgb/rgba format");
1358
+ const parts = inner.split(",").map((v) => +v.trim());
1359
+ return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
1360
+ }
1361
+ function parseHex(s) {
1362
+ const hex = s.slice(1);
1363
+ const isShort = hex.length <= 4;
1364
+ const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
1365
+ const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
1366
+ const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
1367
+ const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
1368
+ const result = [
1369
+ parseInt(r, 16) / 255,
1370
+ parseInt(g, 16) / 255,
1371
+ parseInt(b, 16) / 255
1372
+ ];
1373
+ if (a !== null) {
1374
+ result.push(parseInt(a, 16) / 255);
1375
+ }
1376
+ return result;
1377
+ }
1378
+ function parseColor(s) {
1379
+ if (!s) return void 0;
1380
+ if (Array.isArray(s)) return s;
1381
+ if (typeof s !== "string") return void 0;
1382
+ if (s.startsWith("#")) {
1383
+ return parseHex(s);
1384
+ } else if (s.startsWith("rgb")) {
1385
+ return parseRgba(s);
1386
+ } else {
1387
+ console.warn("Unsupported color format: " + s);
1388
+ }
1389
+ return void 0;
1390
+ }
1391
+ var COLOR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
1392
+ var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
1393
+ var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1394
+ function composeTransformParts(parts, opts) {
1395
+ var _a;
1396
+ if (!parts) return "";
1397
+ const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
1398
+ const segs = [];
1399
+ const t = parts.translate;
1400
+ const o = parts.origin;
1401
+ const r = parts.rotate;
1402
+ const k = parts.skew;
1403
+ const s = parts.scale;
1404
+ const tu = withUnits ? "px" : "";
1405
+ const ru = withUnits ? "deg" : "";
1406
+ if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
1407
+ if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
1408
+ if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
1409
+ if (k !== void 0 && k !== null) segs.push("skewX(" + k + ru + ")");
1410
+ if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
1411
+ if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
1412
+ return segs.join("");
1413
+ }
1414
+ function parseTransformParts(str) {
1415
+ var _a, _b;
1416
+ if (!str || typeof str !== "string") return void 0;
1417
+ const out = {};
1418
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
1419
+ const order = ["translate", "rotate", "skewX", "scale"];
1420
+ let lastIdx = -1;
1421
+ let m;
1422
+ while ((m = re.exec(str)) !== null) {
1423
+ const fn = m[1];
1424
+ const idx = order.indexOf(fn);
1425
+ if (idx < 0 || idx <= lastIdx) return void 0;
1426
+ lastIdx = idx;
1427
+ const nums = m[2].split(/[\s,]+/).filter(Boolean).map(Number);
1428
+ if (nums.some((n) => Number.isNaN(n))) return void 0;
1429
+ if (fn === "translate") {
1430
+ if (nums.length < 1 || nums.length > 2) return void 0;
1431
+ out.translate = [nums[0], (_a = nums[1]) != null ? _a : 0];
1432
+ } else if (fn === "rotate") {
1433
+ if (nums.length !== 1) return void 0;
1434
+ out.rotate = nums[0];
1435
+ } else if (fn === "skewX") {
1436
+ if (nums.length !== 1) return void 0;
1437
+ out.skew = nums[0];
1438
+ } else {
1439
+ if (nums.length < 1 || nums.length > 2) return void 0;
1440
+ out.scale = [nums[0], (_b = nums[1]) != null ? _b : nums[0]];
1441
+ }
1442
+ }
1443
+ if (str.replace(/([a-zA-Z]+)\s*\(([^)]*)\)/g, "").replace(/[\s,]/g, "").length) return void 0;
1444
+ return Object.keys(out).length ? out : void 0;
1445
+ }
1446
+ function kebabToCamelCaseWord(kebab) {
1447
+ return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
1448
+ }
1449
+ function isCamelCaseWord(word) {
1450
+ return !word.includes("-") && /[a-z][A-Z]/.test(word);
1451
+ }
1452
+ var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
1453
+ // Transform/positioning
1454
+ "viewBox",
1455
+ "preserveAspectRatio",
1456
+ // Gradient
1457
+ "gradientUnits",
1458
+ "gradientTransform",
1459
+ "spreadMethod",
1460
+ // Pattern
1461
+ "patternUnits",
1462
+ "patternContentUnits",
1463
+ "patternTransform",
1464
+ // Clipping/masking
1465
+ "clipPathUnits",
1466
+ "maskUnits",
1467
+ "maskContentUnits",
1468
+ // Marker (SVG spec keeps these camelCase, like viewBox)
1469
+ "markerUnits",
1470
+ "markerWidth",
1471
+ "markerHeight",
1472
+ "refX",
1473
+ "refY",
1474
+ // Text
1475
+ "textLength",
1476
+ "lengthAdjust",
1477
+ "startOffset",
1478
+ // Filter
1479
+ "filterUnits",
1480
+ "primitiveUnits",
1481
+ "tableValues",
1482
+ // feFuncR/G/B/A transfer table (type="table")
1483
+ "stdDeviation",
1484
+ "baseFrequency",
1485
+ "numOctaves",
1486
+ "surfaceScale",
1487
+ "diffuseConstant",
1488
+ "specularConstant",
1489
+ "specularExponent",
1490
+ "kernelMatrix",
1491
+ "kernelUnitLength",
1492
+ "edgeMode",
1493
+ "preserveAlpha",
1494
+ "targetX",
1495
+ "targetY"
1496
+ // // Animation
1497
+ // 'attributeName',
1498
+ // 'attributeType',
1499
+ // 'calcMode',
1500
+ // 'keyTimes',
1501
+ // 'keySplines',
1502
+ // 'repeatCount',
1503
+ // 'repeatDur'
1504
+ ]);
1505
+ function camelCaseToKebabWordIfNeeded(camel) {
1506
+ return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1507
+ }
1508
+ function clamp(value, min, max) {
1509
+ return Math.max(min, Math.min(value, max));
1510
+ }
1511
+ function bezier2D_pointAt(P0, P1, P2, P3, t) {
1512
+ if (t <= 0) return [P0[0], P0[1]];
1513
+ if (t >= 1) return [P3[0], P3[1]];
1514
+ const u = 1 - t;
1515
+ const u2 = u * u;
1516
+ const u3 = u2 * u;
1517
+ const t2 = t * t;
1518
+ const t3 = t2 * t;
1519
+ const w0 = u3;
1520
+ const w1 = 3 * t * u2;
1521
+ const w2 = 3 * t2 * u;
1522
+ const w3 = t3;
1523
+ return [
1524
+ w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
1525
+ w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
1526
+ ];
1527
+ }
1528
+ var BEZIER_T_NUDGE = 1e-4;
1529
+ function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
1530
+ const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
1531
+ if (result[0] === 0 && result[1] === 0) {
1532
+ const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
1533
+ return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
1534
+ }
1535
+ return result;
1536
+ }
1537
+ function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
1538
+ const u = 1 - t;
1539
+ const a = 3 * u * u;
1540
+ const b = 6 * t * u;
1541
+ const c = 3 * t * t;
1542
+ return [
1543
+ a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
1544
+ a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
1545
+ ];
1546
+ }
1547
+ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
1548
+ const n = steps + 1;
1549
+ const ts = new Float64Array(n);
1550
+ const ds = new Float64Array(n);
1551
+ let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
1552
+ ts[0] = 0;
1553
+ ds[0] = 0;
1554
+ let cum = 0;
1555
+ for (let i = 1; i < n; i++) {
1556
+ const t = i / steps;
1557
+ const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
1558
+ const dx = cur[0] - prev[0];
1559
+ const dy = cur[1] - prev[1];
1560
+ cum += Math.sqrt(dx * dx + dy * dy);
1561
+ ts[i] = t;
1562
+ ds[i] = cum;
1563
+ prev = cur;
1564
+ }
1565
+ return { ts, ds };
1566
+ }
1567
+ function bezier2D_arcAtT(lut, t) {
1568
+ const { ts, ds } = lut;
1569
+ const last = ts.length - 1;
1570
+ if (t <= ts[0]) return ds[0];
1571
+ if (t >= ts[last]) return ds[last];
1572
+ let lo = 1, hi = last;
1573
+ while (lo < hi) {
1574
+ const mid = lo + hi >>> 1;
1575
+ if (ts[mid] < t) lo = mid + 1;
1576
+ else hi = mid;
1577
+ }
1578
+ const tPrev = ts[hi - 1];
1579
+ const span = ts[hi] - tPrev;
1580
+ const frac = span > 0 ? (t - tPrev) / span : 0;
1581
+ return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);
1582
+ }
1583
+ function invertEasing(easing) {
1584
+ if (!easing) return (y) => y;
1585
+ const flipped = [easing[1], easing[0], easing[3], easing[2]];
1586
+ return cubicBezier(flipped);
1587
+ }
1467
1588
 
1468
- // ../svg-animator-core/src/materialise/PxMotionPath.ts
1589
+ // ../svg-animator-core/src/materialize/PxMotionPath.ts
1469
1590
  function getKfTranslate(kf) {
1470
1591
  const v = kfValue(kf);
1471
1592
  if (!v) return void 0;
@@ -1519,7 +1640,7 @@ var PixodeskAnimator = (() => {
1519
1640
  var DEFAULT_FLATNESS_TOL = 0.5;
1520
1641
  var DEFAULT_ROTATION_TOL = 5;
1521
1642
  var DEFAULT_MAX_SAMPLES = 32;
1522
- function materialiseMotionPathInPropAnim(anim, opts) {
1643
+ function materializeMotionPathInPropAnim(anim, opts) {
1523
1644
  var _a, _b, _c;
1524
1645
  if (!propAnimIsMotionPath(anim)) return anim;
1525
1646
  const kfs = anim.keyframes;
@@ -1551,7 +1672,7 @@ var PixodeskAnimator = (() => {
1551
1672
  if (autoOrient && i > 0) {
1552
1673
  insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol);
1553
1674
  }
1554
- materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
1675
+ materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
1555
1676
  }
1556
1677
  const lastInE = getKfEasing(kfs[kfs.length - 1]);
1557
1678
  if (lastInE) out[out.length - 1].e = lastInE;
@@ -1665,7 +1786,7 @@ var PixodeskAnimator = (() => {
1665
1786
  out.push(makeOutKf(stepTime, dupValue));
1666
1787
  }
1667
1788
  var CORNER_STEP_AFTER_BOUNDARY_MS = 0.05;
1668
- function materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
1789
+ function materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
1669
1790
  const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
1670
1791
  const prevTime = getKfTime(prevKf);
1671
1792
  const nextTime = getKfTime(nextKf);
@@ -1881,8 +2002,8 @@ var PixodeskAnimator = (() => {
1881
2002
  return typeof value === "string" && extractPathData(value) !== void 0;
1882
2003
  }
1883
2004
  function normalizePathValue(value) {
1884
- if (value && typeof value === "object" && typeof value.path === "string") {
1885
- const d = extractPathData(value.path);
2005
+ if (value && typeof value === "object" && typeof value.pathData === "string") {
2006
+ const d = extractPathData(value.pathData);
1886
2007
  return d ? { paths: parseSvgPathToBezier(d) } : value;
1887
2008
  }
1888
2009
  if (value && typeof value === "object" && "paths" in value) {
@@ -1966,7 +2087,7 @@ var PixodeskAnimator = (() => {
1966
2087
  const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
1967
2088
  return { paths: interpolateBeziers(aPaths, bPaths, t) };
1968
2089
  }
1969
- if (COLOUR_ATTR_NAMES.has(propName)) {
2090
+ if (COLOR_ATTR_NAMES.has(propName)) {
1970
2091
  return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
1971
2092
  }
1972
2093
  if (propName === "transform" && typeof a === "object" && a !== null && !Array.isArray(a) && typeof b === "object" && b !== null && !Array.isArray(b)) {
@@ -2188,7 +2309,7 @@ var PixodeskAnimator = (() => {
2188
2309
  value = normalizePathValue(value);
2189
2310
  }
2190
2311
  const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2191
- if (COLOUR_ATTR_NAMES.has(propNameKebab)) {
2312
+ if (COLOR_ATTR_NAMES.has(propNameKebab)) {
2192
2313
  value = (_a = parseColor(value)) != null ? _a : value;
2193
2314
  }
2194
2315
  const normKf = {
@@ -2267,19 +2388,17 @@ var PixodeskAnimator = (() => {
2267
2388
  const out = { keyframes: normalizedKfs };
2268
2389
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2269
2390
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2270
- normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2391
+ normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materializeMotionPathInPropAnim(out) : out;
2271
2392
  }
2272
2393
  }
2273
2394
  return normalized;
2274
2395
  }
2275
- function getNormalisedBindings(doc, engine = PxTimelineEngine.native) {
2396
+ function getNormalizedBindings(doc, engine = PxTimelineEngine.native) {
2276
2397
  const animatorConfig = getAnimatorConfig(doc) || {};
2277
2398
  const defs = getDefs(doc);
2278
2399
  const duration = animatorConfig.duration || 1e3;
2279
2400
  const bindings = [];
2280
- const processAnimation = (id, animate, staticTransform) => {
2281
- if (!animate) return null;
2282
- const animDefs = resolveElementAnimation(animate, defs);
2401
+ const processAnimation = (id, animDefs, staticTransform) => {
2283
2402
  if (animDefs.length === 0) return null;
2284
2403
  const merged = mergeStaticTransformIntoAnimDef(mergeAnimationDefinitions(animDefs), staticTransform);
2285
2404
  const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);
@@ -2292,7 +2411,9 @@ var PixodeskAnimator = (() => {
2292
2411
  const docBindings = getBindings(doc);
2293
2412
  if (docBindings) {
2294
2413
  for (const binding of docBindings) {
2295
- const normalized = processAnimation(binding.id, binding.animate);
2414
+ const id = binding.target.startsWith("#") ? binding.target.slice(1) : binding.target;
2415
+ const animDefs = binding.animateWith.map((name) => resolveAnimation(name, defs)).filter((d) => !!d);
2416
+ const normalized = processAnimation(id, animDefs);
2296
2417
  if (normalized) bindings.push(normalized);
2297
2418
  }
2298
2419
  }
@@ -2301,7 +2422,7 @@ var PixodeskAnimator = (() => {
2301
2422
  if (inlineAnim && Object.keys(inlineAnim).length > 0) {
2302
2423
  const nodeId = node.id || generateElementId();
2303
2424
  node.id = nodeId;
2304
- const normalized = processAnimation(nodeId, inlineAnim, node.transform);
2425
+ const normalized = processAnimation(nodeId, resolveElementAnimation(inlineAnim, defs), node.transform);
2305
2426
  if (normalized) bindings.push(normalized);
2306
2427
  }
2307
2428
  if (node.children) {
@@ -2318,13 +2439,143 @@ var PixodeskAnimator = (() => {
2318
2439
  return bindings;
2319
2440
  }
2320
2441
 
2442
+ // ../svg-animator-core/src/playback/PxPlaybackTime.ts
2443
+ var PX_RATE_REJECTED = "setPlaybackRate: rate must be finite and non-zero";
2444
+ function isValidPlaybackRate(rate) {
2445
+ return Number.isFinite(rate) && rate !== 0;
2446
+ }
2447
+ function seekCeilingMs(durationMs, iterations) {
2448
+ if (!(durationMs > 0)) return 0;
2449
+ if (iterations === Infinity) return Infinity;
2450
+ return durationMs * (iterations > 0 ? iterations : 1);
2451
+ }
2452
+ function progressSpanMs(durationMs, iterations) {
2453
+ if (!(durationMs > 0)) return 0;
2454
+ if (iterations === Infinity) return durationMs;
2455
+ return durationMs * (iterations > 0 ? iterations : 1);
2456
+ }
2457
+ function clampSeekMs(timeMs, ceilingMs) {
2458
+ if (Number.isNaN(timeMs) || timeMs < 0) return 0;
2459
+ if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;
2460
+ return Number.isFinite(timeMs) ? timeMs : 0;
2461
+ }
2462
+ function timeToProgress(timeMs, durationMs, iterations) {
2463
+ const span = progressSpanMs(durationMs, iterations);
2464
+ if (!(span > 0) || !Number.isFinite(timeMs)) return 0;
2465
+ if (timeMs <= 0) return 0;
2466
+ if (iterations === Infinity) return timeMs % span / span;
2467
+ return timeMs >= span ? 1 : timeMs / span;
2468
+ }
2469
+ function progressToTimeMs(progress, durationMs, iterations) {
2470
+ const span = progressSpanMs(durationMs, iterations);
2471
+ if (!(span > 0) || !Number.isFinite(progress)) return 0;
2472
+ if (progress <= 0) return 0;
2473
+ return progress >= 1 ? span : progress * span;
2474
+ }
2475
+
2476
+ // ../svg-animator-core/src/playback/PxDiagnostics.ts
2477
+ var PxDiagnosticKind = {
2478
+ /** The document is wrong — regenerate or repair the file. */
2479
+ document: "document",
2480
+ /** The page or app cannot provide what the document asks for — fix the mount. */
2481
+ host: "host",
2482
+ /** The platform cannot do it and the player degraded — usually nothing to fix. */
2483
+ platform: "platform",
2484
+ /** The call is wrong or self-contradictory — fix the options or props you passed. */
2485
+ usage: "usage",
2486
+ /** The player failed where it did not expect to — report it to us. */
2487
+ internal: "internal"
2488
+ };
2489
+ function asError(error) {
2490
+ return typeof error === "string" ? new Error(error) : error;
2491
+ }
2492
+ function createDiagnostics(config, prefix) {
2493
+ const tag = prefix ? prefix + " " : "";
2494
+ return {
2495
+ warn: (kind, message, detail) => {
2496
+ if (config == null ? void 0 : config.onWarn) {
2497
+ config.onWarn({ kind, message, detail });
2498
+ return;
2499
+ }
2500
+ if (config == null ? void 0 : config.muteWarn) return;
2501
+ const line = tag + kind + ": " + message;
2502
+ if (detail === void 0) console.warn(line);
2503
+ else console.warn(line, detail);
2504
+ },
2505
+ error: (kind, error, detail) => {
2506
+ const err = asError(error);
2507
+ if (config == null ? void 0 : config.onError) {
2508
+ config.onError({ kind, message: err.message, error: err, detail });
2509
+ return;
2510
+ }
2511
+ if (config == null ? void 0 : config.muteError) return;
2512
+ const line = tag + kind + ": " + err.message;
2513
+ if (detail === void 0) console.error(line);
2514
+ else console.error(line, detail);
2515
+ }
2516
+ };
2517
+ }
2518
+
2519
+ // src/shared/PxAnimatorCallbacks.ts
2520
+ function toEngineCallbacks(inline) {
2521
+ const { onPlay, onPause, onCancel, onFinish, onRemove, onStop, onWarn, onError, muteWarn, muteError } = inline != null ? inline : {};
2522
+ const withStop = (own) => own || onStop ? () => {
2523
+ own == null ? void 0 : own();
2524
+ onStop == null ? void 0 : onStop();
2525
+ } : void 0;
2526
+ return {
2527
+ onPlay,
2528
+ onPause: withStop(onPause),
2529
+ onCancel: withStop(onCancel),
2530
+ onFinish: withStop(onFinish),
2531
+ onRemove: withStop(onRemove),
2532
+ onWarn,
2533
+ onError,
2534
+ muteWarn,
2535
+ muteError
2536
+ };
2537
+ }
2538
+ function createInertAnimator() {
2539
+ return {
2540
+ isReady: () => false,
2541
+ getRootElement: () => null,
2542
+ isPlaying: () => false,
2543
+ play: () => {
2544
+ },
2545
+ pause: () => {
2546
+ },
2547
+ cancel: () => {
2548
+ },
2549
+ finish: () => {
2550
+ },
2551
+ setPlaybackRate: () => {
2552
+ },
2553
+ getCurrentTime: () => null,
2554
+ setCurrentTime: () => {
2555
+ },
2556
+ getCurrentProgress: () => null,
2557
+ setCurrentProgress: () => {
2558
+ },
2559
+ destroy: () => {
2560
+ }
2561
+ };
2562
+ }
2563
+ function asThrownError(e) {
2564
+ return e instanceof Error ? e : new Error(String(e));
2565
+ }
2566
+
2321
2567
  // src/triggers/PxAnimatorTriggers.ts
2322
- function setupAnimationTriggers(api, config) {
2323
- const { startOn, outAction = "continue", scrollIntoViewThreshold = 0 } = config;
2568
+ function setupAnimationTriggers(api, config, diag) {
2569
+ const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
2570
+ const cleanups = [];
2571
+ const dispose = () => {
2572
+ for (const undo of cleanups.splice(0)) undo();
2573
+ };
2574
+ const { startOn, outAction, scrollIntoViewThreshold } = resolveTrigger(config);
2324
2575
  const root = api.getRootElement();
2325
2576
  if (!root) {
2326
- console.warn("setupAnimationTriggers: No root element found for animation.");
2327
- return api;
2577
+ report.warn(PxDiagnosticKind.host, "setupAnimationTriggers: No root element found for animation.");
2578
+ return dispose;
2328
2579
  }
2329
2580
  let reversed = false;
2330
2581
  const start = () => {
@@ -2359,6 +2610,7 @@ var PixodeskAnimator = (() => {
2359
2610
  startHandler();
2360
2611
  } else {
2361
2612
  window.addEventListener("load", startHandler, { once: true });
2613
+ cleanups.push(() => window.removeEventListener("load", startHandler));
2362
2614
  }
2363
2615
  break;
2364
2616
  }
@@ -2373,6 +2625,10 @@ var PixodeskAnimator = (() => {
2373
2625
  };
2374
2626
  root.addEventListener("mouseenter", mouseOverHandler);
2375
2627
  root.addEventListener("mouseleave", mouseOutHandler);
2628
+ cleanups.push(() => {
2629
+ root.removeEventListener("mouseenter", mouseOverHandler);
2630
+ root.removeEventListener("mouseleave", mouseOutHandler);
2631
+ });
2376
2632
  break;
2377
2633
  }
2378
2634
  case "click": {
@@ -2384,6 +2640,7 @@ var PixodeskAnimator = (() => {
2384
2640
  }
2385
2641
  };
2386
2642
  root.addEventListener("click", clickHandler);
2643
+ cleanups.push(() => root.removeEventListener("click", clickHandler));
2387
2644
  break;
2388
2645
  }
2389
2646
  case "scrollIntoView": {
@@ -2415,12 +2672,13 @@ var PixodeskAnimator = (() => {
2415
2672
  { threshold: thresholdSteps }
2416
2673
  );
2417
2674
  observer.observe(root);
2675
+ cleanups.push(() => observer.disconnect());
2418
2676
  break;
2419
2677
  }
2420
2678
  case "programmatic":
2421
2679
  break;
2422
2680
  }
2423
- return api;
2681
+ return dispose;
2424
2682
  }
2425
2683
 
2426
2684
  // src/engines/PxAnimatorFrameLoop.ts
@@ -2438,7 +2696,7 @@ var PixodeskAnimator = (() => {
2438
2696
  };
2439
2697
  let cssValue;
2440
2698
  let cssKey = propName;
2441
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
2699
+ if (COLOR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
2442
2700
  cssValue = toRGBA(value);
2443
2701
  } else if (propName === "transform" && value !== null && typeof value === "object" && !Array.isArray(value)) {
2444
2702
  cssValue = composeTransformParts(value, { withUnits: true });
@@ -2525,18 +2783,19 @@ var PixodeskAnimator = (() => {
2525
2783
  return result;
2526
2784
  }
2527
2785
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs, scrollTimeline) {
2528
- var _a;
2786
+ var _a, _b;
2529
2787
  const config = getAnimatorConfig(doc) || {};
2788
+ const diag = createDiagnostics(callbacks, "[PxAnimator]");
2530
2789
  if (!rootElement) {
2531
2790
  if (doc.id) {
2532
2791
  const rootSelector = getSelector(doc.id);
2533
2792
  rootElement = document.querySelector(rootSelector);
2534
- if (!rootElement) console.warn("createFrameLoopAnimator: No root element found for selector: ", rootSelector);
2793
+ if (!rootElement) diag.warn(PxDiagnosticKind.host, "createWebApiAnimator: No root element found for selector: " + rootSelector);
2535
2794
  } else {
2536
- console.warn("createFrameLoopAnimator: No root element provided");
2795
+ diag.warn(PxDiagnosticKind.host, "createWebApiAnimator: No root element provided");
2537
2796
  }
2538
2797
  }
2539
- const bindings = getNormalisedBindings(doc, PxTimelineEngine.native);
2798
+ const bindings = getNormalizedBindings(doc, PxTimelineEngine.native);
2540
2799
  const animations = [];
2541
2800
  const _iterations = config.iterations;
2542
2801
  let iterations;
@@ -2546,18 +2805,18 @@ var PixodeskAnimator = (() => {
2546
2805
  let finishNotified = false;
2547
2806
  let removeCalled = false;
2548
2807
  if (!(bindings == null ? void 0 : bindings.length)) {
2549
- console.warn("createWebApiAnimator: No animation bindings defined");
2808
+ diag.warn(PxDiagnosticKind.document, "createWebApiAnimator: No animation bindings defined");
2550
2809
  }
2551
2810
  for (const binding of bindings || []) {
2552
2811
  const animDef = binding.animate;
2553
2812
  if (!animDef || typeof animDef !== "object" || Array.isArray(animDef)) {
2554
- console.warn("createWebApiAnimator: Empty or unresolved binding", binding);
2813
+ diag.warn(PxDiagnosticKind.document, "createWebApiAnimator: Empty or unresolved binding", binding);
2555
2814
  continue;
2556
2815
  }
2557
2816
  const selector = getSelector(binding.id);
2558
2817
  const elements = (rootElement == null ? void 0 : rootElement.querySelectorAll(selector)) || document.querySelectorAll(selector);
2559
2818
  if (elements.length === 0) {
2560
- console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
2819
+ diag.warn(PxDiagnosticKind.host, 'createWebApiAnimator: No elements found for selector "' + selector + '"');
2561
2820
  }
2562
2821
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
2563
2822
  const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
@@ -2606,14 +2865,14 @@ var PixodeskAnimator = (() => {
2606
2865
  }
2607
2866
  animations.push(anim);
2608
2867
  } catch (e) {
2609
- console.warn(e);
2868
+ diag.warn(PxDiagnosticKind.internal, "createWebApiAnimator: could not build the animation", e);
2610
2869
  }
2611
2870
  }
2612
2871
  }
2613
2872
  }
2614
2873
  }
2615
2874
  if (!forceEvenIfHasUnsupportedAttrs && unsupportedSet.size) {
2616
- console.warn("Unsupported CSS attrs: " + [...unsupportedSet].join(", "));
2875
+ diag.warn(PxDiagnosticKind.platform, "Unsupported CSS attrs: " + [...unsupportedSet].join(", "));
2617
2876
  return null;
2618
2877
  }
2619
2878
  const api = {
@@ -2657,20 +2916,36 @@ var PixodeskAnimator = (() => {
2657
2916
  }
2658
2917
  },
2659
2918
  "setPlaybackRate": (rate) => {
2919
+ if (!isValidPlaybackRate(rate)) {
2920
+ diag.warn(PxDiagnosticKind.usage, PX_RATE_REJECTED);
2921
+ return api;
2922
+ }
2660
2923
  animations.forEach((a) => a.playbackRate = rate);
2661
2924
  return api;
2662
2925
  },
2663
2926
  "getCurrentTime": () => {
2664
- var _a2, _b;
2665
- const res = (_b = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b : null;
2927
+ var _a2, _b2;
2928
+ const res = (_b2 = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b2 : null;
2666
2929
  return res !== null ? +res : null;
2667
2930
  },
2668
2931
  "setCurrentTime": (time) => {
2932
+ var _a2;
2933
+ const ceiling = seekCeilingMs((_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1);
2934
+ const seek = ceiling > 0 ? clampSeekMs(time, ceiling) : Math.max(0, time);
2669
2935
  finishNotified = false;
2670
2936
  animations.forEach((a) => {
2671
- a.currentTime = time;
2937
+ a.currentTime = seek;
2672
2938
  });
2673
2939
  },
2940
+ "getCurrentProgress": () => {
2941
+ var _a2;
2942
+ const t = api.getCurrentTime();
2943
+ return t === null ? null : timeToProgress(t, (_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1);
2944
+ },
2945
+ "setCurrentProgress": (progress) => {
2946
+ var _a2;
2947
+ api.setCurrentTime(progressToTimeMs(progress, (_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1));
2948
+ },
2674
2949
  "destroy": () => {
2675
2950
  var _a2;
2676
2951
  api.cancel();
@@ -2681,12 +2956,15 @@ var PixodeskAnimator = (() => {
2681
2956
  }
2682
2957
  }
2683
2958
  };
2684
- if (config.trigger) {
2685
- if (config.timelineSource === "scroll") {
2686
- console.warn("scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
2687
- } else {
2688
- setupAnimationTriggers(api, config.trigger);
2689
- }
2959
+ if (config.timelineSource === "scroll") {
2960
+ if (config.trigger) diag.warn(PxDiagnosticKind.usage, "scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
2961
+ } else {
2962
+ const detachTriggers = setupAnimationTriggers(api, (_b = config.trigger) != null ? _b : {}, diag);
2963
+ const destroyEngine = api.destroy.bind(api);
2964
+ api.destroy = () => {
2965
+ detachTriggers();
2966
+ destroyEngine();
2967
+ };
2690
2968
  }
2691
2969
  if (scrollTimeline) {
2692
2970
  animations.forEach((a) => a.play());
@@ -2714,22 +2992,33 @@ var PixodeskAnimator = (() => {
2714
2992
  }
2715
2993
  return res;
2716
2994
  }
2717
- function requireData(options) {
2718
- if (!(options == null ? void 0 : options.data)) throw new Error("createAnimator: `data` is required");
2719
- return options.data;
2995
+ function requireDoc(options) {
2996
+ if (!(options == null ? void 0 : options.doc)) throw new Error("createAnimator: `doc` is required");
2997
+ return options.doc;
2998
+ }
2999
+ function buildOrReport(options, build) {
3000
+ try {
3001
+ return build();
3002
+ } catch (e) {
3003
+ const err = asThrownError(e);
3004
+ createDiagnostics(options, "[PxAnimator]").error(PxDiagnosticKind.internal, "createAnimator: could not build the player \u2014 " + err.message, err);
3005
+ return createInertAnimator();
3006
+ }
2720
3007
  }
2721
3008
  function createPrerenderedWaapiAnimator(options) {
2722
- const doc = requireData(options);
2723
- const animatorConfig = getAnimatorConfig(doc) || {};
2724
- return finaliseAnimator(
2725
- animatorConfig,
2726
- options.callbacks,
2727
- (cb) => createWebApiAnimator(doc, cb, null, true)
2728
- );
3009
+ const doc = requireDoc(options);
3010
+ return buildOrReport(options, () => {
3011
+ const animatorConfig = getAnimatorConfig(doc) || {};
3012
+ return finaliseAnimator(
3013
+ animatorConfig,
3014
+ toEngineCallbacks(options),
3015
+ (cb) => createWebApiAnimator(doc, cb, null, true)
3016
+ );
3017
+ });
2729
3018
  }
2730
3019
 
2731
3020
  // src/shared/PxAnimatorKeys.ts
2732
- var PX_ANIMATOR_DATA_KEY = "data";
3021
+ var PX_ANIMATOR_DOC_KEY = "doc";
2733
3022
  return __toCommonJS(index_prerendered_waapi_exports);
2734
3023
  })();
2735
3024
  //# sourceMappingURL=index.prerendered-waapi.umd.js.map