@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,584 +50,98 @@ var PixodeskAnimator = (() => {
50
50
  // src/index.prerendered.ts
51
51
  var index_prerendered_exports = {};
52
52
  __export(index_prerendered_exports, {
53
- PX_ANIMATOR_DATA_KEY: () => PX_ANIMATOR_DATA_KEY,
53
+ PX_ANIMATOR_DOC_KEY: () => PX_ANIMATOR_DOC_KEY,
54
54
  createAnimator: () => createPrerenderedAnimator,
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");
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;
91
66
  }
92
- return d.join("");
93
- }
94
- function interpolateNum(a, b, t) {
95
- return a + (b - a) * t;
67
+ return result;
96
68
  }
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);
69
+ var Base = class {
70
+ _canSanitize(raw) {
71
+ return this.isValid(raw);
102
72
  }
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
- ];
112
- }
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));
73
+ optional() {
74
+ return new Optional(this);
118
75
  }
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));
76
+ };
77
+ var Optional = class extends Base {
78
+ constructor(inner) {
79
+ super();
80
+ this.inner = inner;
81
+ this._default = void 0;
139
82
  }
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 remap(value, inMin, inMax, outMin, outMax) {
143
- if (inMax === inMin) return outMin;
144
- const t = (value - inMin) / (inMax - inMin);
145
- return outMin + t * (outMax - outMin);
146
- }
147
- function solveCubicBezierX(p1x, p2x, x) {
148
- if (x <= 0) return 0;
149
- if (x >= 1) return 1;
150
- const cx = 3 * p1x;
151
- const bx = 3 * (p2x - p1x) - cx;
152
- const ax = 1 - cx - bx;
153
- function sampleX(t) {
154
- return ((ax * t + bx) * t + cx) * t;
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;
155
86
  }
156
- function sampleDX(t) {
157
- return (3 * ax * t + 2 * bx) * t + cx;
87
+ isValid(raw, ctx, path) {
88
+ if (raw === void 0 || raw === null) return true;
89
+ return this.inner.isValid(raw, ctx, path);
158
90
  }
159
- let t2 = x;
160
- let t0 = 0;
161
- let t1 = 1;
162
- for (let i = 0; i < 8; i++) {
163
- const x2 = sampleX(t2) - x;
164
- if (Math.abs(x2) < 1e-6) return t2;
165
- const d2 = sampleDX(t2);
166
- if (Math.abs(d2) < 1e-6) break;
167
- t2 -= x2 / d2;
91
+ _canSanitize(raw) {
92
+ return raw === void 0 || raw === null || this.inner._canSanitize(raw);
168
93
  }
169
- t2 = x;
170
- while (t0 < t1) {
171
- const x2 = sampleX(t2);
172
- if (Math.abs(x2 - x) < 1e-6) return t2;
173
- if (x > x2) t0 = t2;
174
- else t1 = t2;
175
- t2 = (t1 + t0) / 2;
94
+ };
95
+ var Str = class extends Base {
96
+ constructor(_default = "") {
97
+ super();
98
+ this._default = _default;
176
99
  }
177
- return t2;
178
- }
179
- function cubicBezier(easing) {
180
- const [p1x, p1y, p2x, p2y] = easing;
181
- const cy = 3 * p1y;
182
- const by = 3 * (p2y - p1y) - cy;
183
- const ay = 1 - cy - by;
184
- function sampleCurveY(t) {
185
- return ((ay * t + by) * t + cy) * t;
100
+ sanitize(raw) {
101
+ return typeof raw === "string" ? raw : this._default;
186
102
  }
187
- return function(x) {
188
- return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
189
- };
190
- }
191
- function lerp2(a, b, t) {
192
- return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
193
- }
194
- function subdivideCubicBezier(p0, p1, p2, p3, t) {
195
- const q0 = lerp2(p0, p1, t);
196
- const q1 = lerp2(p1, p2, t);
197
- const q2 = lerp2(p2, p3, t);
198
- const r0 = lerp2(q0, q1, t);
199
- const r1 = lerp2(q1, q2, t);
200
- const s = lerp2(r0, r1, t);
201
- return {
202
- left: [p0, q0, r0, s],
203
- right: [s, r1, q2, p3]
204
- };
205
- }
206
- function splitEasing(easing, xFraction) {
207
- if (!easing) return { left: void 0, right: void 0 };
208
- if (xFraction <= 0) return { left: void 0, right: easing };
209
- if (xFraction >= 1) return { left: easing, right: void 0 };
210
- const [x1, y1, x2, y2] = easing;
211
- const t = solveCubicBezierX(x1, x2, xFraction);
212
- const p0 = [0, 0];
213
- const p1 = [x1, y1];
214
- const p2 = [x2, y2];
215
- const p3 = [1, 1];
216
- const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
217
- const sx = left[3][0];
218
- const sy = left[3][1];
219
- let leftEasing;
220
- if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
221
- leftEasing = [
222
- left[1][0] / sx,
223
- left[1][1] / sy,
224
- left[2][0] / sx,
225
- left[2][1] / sy
226
- ];
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;
227
107
  }
228
- let rightEasing;
229
- const rx = 1 - sx;
230
- const ry = 1 - sy;
231
- if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
232
- rightEasing = [
233
- (right[1][0] - sx) / rx,
234
- (right[1][1] - sy) / ry,
235
- (right[2][0] - sx) / rx,
236
- (right[2][1] - sy) / ry
237
- ];
108
+ };
109
+ var Num = class extends Base {
110
+ constructor(_default = 0) {
111
+ super();
112
+ this._default = _default;
238
113
  }
239
- return { left: leftEasing, right: rightEasing };
240
- }
241
- function reverseEasing(easing) {
242
- if (!easing) return void 0;
243
- return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
244
- }
245
- function toRGBA(color) {
246
- const r = Math.round(color[0] * 255);
247
- const g = Math.round(color[1] * 255);
248
- const b = Math.round(color[2] * 255);
249
- return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
250
- }
251
- function parseRgba(s) {
252
- var _a;
253
- const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
254
- if (!inner) throw new Error("Invalid rgb/rgba format");
255
- const parts = inner.split(",").map((v) => +v.trim());
256
- return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
257
- }
258
- function parseHex(s) {
259
- const hex = s.slice(1);
260
- const isShort = hex.length <= 4;
261
- const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
262
- const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
263
- const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
264
- const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
265
- const result = [
266
- parseInt(r, 16) / 255,
267
- parseInt(g, 16) / 255,
268
- parseInt(b, 16) / 255
269
- ];
270
- if (a !== null) {
271
- result.push(parseInt(a, 16) / 255);
114
+ sanitize(raw) {
115
+ return typeof raw === "number" && isFinite(raw) ? raw : this._default;
272
116
  }
273
- return result;
274
- }
275
- function parseColor(s) {
276
- if (!s) return void 0;
277
- if (Array.isArray(s)) return s;
278
- if (typeof s !== "string") return void 0;
279
- if (s.startsWith("#")) {
280
- return parseHex(s);
281
- } else if (s.startsWith("rgb")) {
282
- return parseRgba(s);
283
- } else {
284
- console.warn("Unsupported color format: " + s);
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;
285
121
  }
286
- return void 0;
287
- }
288
- var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
289
- var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
290
- var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
291
- function composeTransformParts(parts, opts) {
292
- var _a;
293
- if (!parts) return "";
294
- const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
295
- const segs = [];
296
- const t = parts.translate;
297
- const o = parts.origin;
298
- const r = parts.rotate;
299
- const k = parts.skew;
300
- const s = parts.scale;
301
- const tu = withUnits ? "px" : "";
302
- const ru = withUnits ? "deg" : "";
303
- if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
304
- if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
305
- if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
306
- if (k !== void 0 && k !== null) segs.push("skewX(" + k + ru + ")");
307
- if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
308
- if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
309
- return segs.join("");
310
- }
311
- function parseTransformParts(str) {
312
- var _a, _b;
313
- if (!str || typeof str !== "string") return void 0;
314
- const out = {};
315
- const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
316
- const order = ["translate", "rotate", "skewX", "scale"];
317
- let lastIdx = -1;
318
- let m;
319
- while ((m = re.exec(str)) !== null) {
320
- const fn = m[1];
321
- const idx = order.indexOf(fn);
322
- if (idx < 0 || idx <= lastIdx) return void 0;
323
- lastIdx = idx;
324
- const nums = m[2].split(/[\s,]+/).filter(Boolean).map(Number);
325
- if (nums.some((n) => Number.isNaN(n))) return void 0;
326
- if (fn === "translate") {
327
- if (nums.length < 1 || nums.length > 2) return void 0;
328
- out.translate = [nums[0], (_a = nums[1]) != null ? _a : 0];
329
- } else if (fn === "rotate") {
330
- if (nums.length !== 1) return void 0;
331
- out.rotate = nums[0];
332
- } else if (fn === "skewX") {
333
- if (nums.length !== 1) return void 0;
334
- out.skew = nums[0];
335
- } else {
336
- if (nums.length < 1 || nums.length > 2) return void 0;
337
- out.scale = [nums[0], (_b = nums[1]) != null ? _b : nums[0]];
338
- }
122
+ };
123
+ var Bool = class extends Base {
124
+ constructor(_default = false) {
125
+ super();
126
+ this._default = _default;
339
127
  }
340
- if (str.replace(/([a-zA-Z]+)\s*\(([^)]*)\)/g, "").replace(/[\s,]/g, "").length) return void 0;
341
- return Object.keys(out).length ? out : void 0;
342
- }
343
- var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
344
- var DEFAULT_DURATION_MS = 1e3;
345
- function kebabToCamelCaseWord(kebab) {
346
- return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
347
- }
348
- function isCamelCaseWord(word) {
349
- return !word.includes("-") && /[a-z][A-Z]/.test(word);
350
- }
351
- var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
352
- // Transform/positioning
353
- "viewBox",
354
- "preserveAspectRatio",
355
- // Gradient
356
- "gradientUnits",
357
- "gradientTransform",
358
- "spreadMethod",
359
- // Pattern
360
- "patternUnits",
361
- "patternContentUnits",
362
- "patternTransform",
363
- // Clipping/masking
364
- "clipPathUnits",
365
- "maskUnits",
366
- "maskContentUnits",
367
- // Marker (SVG spec keeps these camelCase, like viewBox)
368
- "markerUnits",
369
- "markerWidth",
370
- "markerHeight",
371
- "refX",
372
- "refY",
373
- // Text
374
- "textLength",
375
- "lengthAdjust",
376
- "startOffset",
377
- // Filter
378
- "filterUnits",
379
- "primitiveUnits",
380
- "tableValues",
381
- // feFuncR/G/B/A transfer table (type="table")
382
- "stdDeviation",
383
- "baseFrequency",
384
- "numOctaves",
385
- "surfaceScale",
386
- "diffuseConstant",
387
- "specularConstant",
388
- "specularExponent",
389
- "kernelMatrix",
390
- "kernelUnitLength",
391
- "edgeMode",
392
- "preserveAlpha",
393
- "targetX",
394
- "targetY"
395
- // // Animation
396
- // 'attributeName',
397
- // 'attributeType',
398
- // 'calcMode',
399
- // 'keyTimes',
400
- // 'keySplines',
401
- // 'repeatCount',
402
- // 'repeatDur'
403
- ]);
404
- function camelCaseToKebabWordIfNeeded(camel) {
405
- return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
406
- }
407
- function clamp(value, min, max) {
408
- return Math.max(min, Math.min(value, max));
409
- }
410
- function bezier2D_pointAt(P0, P1, P2, P3, t) {
411
- if (t <= 0) return [P0[0], P0[1]];
412
- if (t >= 1) return [P3[0], P3[1]];
413
- const u = 1 - t;
414
- const u2 = u * u;
415
- const u3 = u2 * u;
416
- const t2 = t * t;
417
- const t3 = t2 * t;
418
- const w0 = u3;
419
- const w1 = 3 * t * u2;
420
- const w2 = 3 * t2 * u;
421
- const w3 = t3;
422
- return [
423
- w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
424
- w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
425
- ];
426
- }
427
- var BEZIER_T_NUDGE = 1e-4;
428
- function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
429
- const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
430
- if (result[0] === 0 && result[1] === 0) {
431
- const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
432
- return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
128
+ sanitize(raw) {
129
+ return typeof raw === "boolean" ? raw : this._default;
433
130
  }
434
- return result;
435
- }
436
- function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
437
- const u = 1 - t;
438
- const a = 3 * u * u;
439
- const b = 6 * t * u;
440
- const c = 3 * t * t;
441
- return [
442
- a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
443
- a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
444
- ];
445
- }
446
- function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
447
- const n = steps + 1;
448
- const ts = new Float64Array(n);
449
- const ds = new Float64Array(n);
450
- let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
451
- ts[0] = 0;
452
- ds[0] = 0;
453
- let cum = 0;
454
- for (let i = 1; i < n; i++) {
455
- const t = i / steps;
456
- const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
457
- const dx = cur[0] - prev[0];
458
- const dy = cur[1] - prev[1];
459
- cum += Math.sqrt(dx * dx + dy * dy);
460
- ts[i] = t;
461
- ds[i] = cum;
462
- prev = cur;
131
+ isValid(raw, ctx, path) {
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;
463
135
  }
464
- return { ts, ds };
465
- }
466
- function bezier2D_arcAtT(lut, t) {
467
- const { ts, ds } = lut;
468
- const last = ts.length - 1;
469
- if (t <= ts[0]) return ds[0];
470
- if (t >= ts[last]) return ds[last];
471
- let lo = 1, hi = last;
472
- while (lo < hi) {
473
- const mid = lo + hi >>> 1;
474
- if (ts[mid] < t) lo = mid + 1;
475
- else hi = mid;
136
+ };
137
+ var Literal = class extends Base {
138
+ constructor(value) {
139
+ super();
140
+ this.value = value;
141
+ this._default = value;
476
142
  }
477
- const tPrev = ts[hi - 1];
478
- const span = ts[hi] - tPrev;
479
- const frac = span > 0 ? (t - tPrev) / span : 0;
480
- return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);
481
- }
482
- function invertEasing(easing) {
483
- if (!easing) return (y) => y;
484
- const flipped = [easing[1], easing[0], easing[3], easing[2]];
485
- return cubicBezier(flipped);
486
- }
487
-
488
- // ../svg-animator-core/src/playback/PxScrollMath.ts
489
- function isScrollTimeline(config) {
490
- return (config == null ? void 0 : config.timelineSource) === "scroll";
491
- }
492
- function scrollTotalDurationMs(config) {
493
- const duration = typeof (config == null ? void 0 : config.duration) === "number" && config.duration > 0 ? config.duration : DEFAULT_DURATION_MS;
494
- const iterations = typeof (config == null ? void 0 : config.iterations) === "number" && config.iterations > 0 ? config.iterations : 1;
495
- return duration * iterations;
496
- }
497
- function scrollPhaseInterval(phase, subjectSize, scrollportSize) {
498
- const s = subjectSize, vp = scrollportSize;
499
- switch (phase) {
500
- case "cover":
501
- return [0, s + vp];
502
- case "entry":
503
- return [0, Math.min(s, vp)];
504
- case "contain":
505
- return [Math.min(s, vp), Math.max(s, vp)];
506
- case "exit":
507
- return [Math.max(s, vp), s + vp];
508
- case "entry-crossing":
509
- return [0, s];
510
- case "exit-crossing":
511
- return [vp, s + vp];
512
- }
513
- }
514
- var DEFAULT_PHASE = "cover";
515
- function resolveRangePointU(point, defaultFraction, subjectSize, scrollportSize) {
516
- var _a;
517
- const [u0, u1] = scrollPhaseInterval((_a = point == null ? void 0 : point.phase) != null ? _a : DEFAULT_PHASE, subjectSize, scrollportSize);
518
- const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
519
- return u0 + fraction * (u1 - u0);
520
- }
521
- function scrollViewProgress(subjectStart, subjectSize, scrollportSize, range) {
522
- const u = scrollportSize - subjectStart;
523
- const uStart = resolveRangePointU(range == null ? void 0 : range.start, 0, subjectSize, scrollportSize);
524
- const uEnd = resolveRangePointU(range == null ? void 0 : range.end, 1, subjectSize, scrollportSize);
525
- if (uEnd <= uStart) return u >= uEnd ? 1 : 0;
526
- return clamp((u - uStart) / (uEnd - uStart), 0, 1);
527
- }
528
- function scrollOffsetProgress(offset, maxOffset, range) {
529
- var _a, _b;
530
- const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;
531
- const start = typeof ((_a = range == null ? void 0 : range.start) == null ? void 0 : _a.fraction) === "number" ? range.start.fraction : 0;
532
- const end = typeof ((_b = range == null ? void 0 : range.end) == null ? void 0 : _b.fraction) === "number" ? range.end.fraction : 1;
533
- if (end <= start) return raw >= end ? 1 : 0;
534
- return clamp((raw - start) / (end - start), 0, 1);
535
- }
536
- function scrollResolveAxis(axis, writingMode) {
537
- const a = axis != null ? axis : "block";
538
- if (a === "x" || a === "y") return a;
539
- const vertical = !!writingMode && writingMode.startsWith("vertical");
540
- if (a === "inline") return vertical ? "y" : "x";
541
- return vertical ? "x" : "y";
542
- }
543
-
544
- // ../svg-animator-core/src/schema/PxSchema.ts
545
- var PX_UNKNOWN_KEY_ERROR = "unexpected extra key";
546
- function pathStr(path) {
547
- if (!path.length) return ".";
548
- let result = "";
549
- for (const seg of path) {
550
- if (seg.startsWith("[")) result += seg;
551
- else result += (result ? "." : "") + seg;
552
- }
553
- return result;
554
- }
555
- var Base = class {
556
- _canSanitize(raw) {
557
- return this.isValid(raw);
558
- }
559
- optional() {
560
- return new Optional(this);
561
- }
562
- };
563
- var Optional = class extends Base {
564
- constructor(inner) {
565
- super();
566
- this.inner = inner;
567
- this._default = void 0;
568
- }
569
- sanitize(raw) {
570
- if (raw === void 0 || raw === null) return void 0;
571
- return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
572
- }
573
- isValid(raw, ctx, path) {
574
- if (raw === void 0 || raw === null) return true;
575
- return this.inner.isValid(raw, ctx, path);
576
- }
577
- _canSanitize(raw) {
578
- return raw === void 0 || raw === null || this.inner._canSanitize(raw);
579
- }
580
- };
581
- var Str = class extends Base {
582
- constructor(_default = "") {
583
- super();
584
- this._default = _default;
585
- }
586
- sanitize(raw) {
587
- return typeof raw === "string" ? raw : this._default;
588
- }
589
- isValid(raw, ctx, path) {
590
- if (typeof raw === "string") return true;
591
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
592
- return false;
593
- }
594
- };
595
- var Num = class extends Base {
596
- constructor(_default = 0) {
597
- super();
598
- this._default = _default;
599
- }
600
- sanitize(raw) {
601
- return typeof raw === "number" && isFinite(raw) ? raw : this._default;
602
- }
603
- isValid(raw, ctx, path) {
604
- if (typeof raw === "number" && isFinite(raw)) return true;
605
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
606
- return false;
607
- }
608
- };
609
- var Bool = class extends Base {
610
- constructor(_default = false) {
611
- super();
612
- this._default = _default;
613
- }
614
- sanitize(raw) {
615
- return typeof raw === "boolean" ? raw : this._default;
616
- }
617
- isValid(raw, ctx, path) {
618
- if (typeof raw === "boolean") return true;
619
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
620
- return false;
621
- }
622
- };
623
- var Literal = class extends Base {
624
- constructor(value) {
625
- super();
626
- this.value = value;
627
- this._default = value;
628
- }
629
- sanitize(raw) {
630
- return raw === this.value ? this.value : this._default;
143
+ sanitize(raw) {
144
+ return raw === this.value ? this.value : this._default;
631
145
  }
632
146
  isValid(raw, ctx, path) {
633
147
  if (raw === this.value) return true;
@@ -650,6 +164,7 @@ var PixodeskAnimator = (() => {
650
164
  return false;
651
165
  }
652
166
  };
167
+ var UNION_MEMBER_ERROR_LIMIT = 4;
653
168
  var Union = class extends Base {
654
169
  constructor(schemas, defaultVal) {
655
170
  super();
@@ -668,7 +183,35 @@ var PixodeskAnimator = (() => {
668
183
  var _a;
669
184
  const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
670
185
  if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
671
- 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
+ }
672
215
  return false;
673
216
  }
674
217
  _canSanitize(raw) {
@@ -1020,6 +563,76 @@ var PixodeskAnimator = (() => {
1020
563
  };
1021
564
 
1022
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
+ ];
1023
636
  var PxTimelineEngine = {
1024
637
  native: "native",
1025
638
  js: "js"
@@ -1033,6 +646,19 @@ var PixodeskAnimator = (() => {
1033
646
  function mayUseNativeScrollTimeline(engine) {
1034
647
  return engine !== PxTimelineEngineExtra.js;
1035
648
  }
649
+ var PX_TRIGGER_DEFAULTS = {
650
+ startOn: "load",
651
+ outAction: "continue",
652
+ scrollIntoViewThreshold: 0
653
+ };
654
+ function resolveTrigger(trigger) {
655
+ var _a, _b, _c;
656
+ return {
657
+ startOn: (_a = trigger == null ? void 0 : trigger.startOn) != null ? _a : PX_TRIGGER_DEFAULTS.startOn,
658
+ outAction: (_b = trigger == null ? void 0 : trigger.outAction) != null ? _b : PX_TRIGGER_DEFAULTS.outAction,
659
+ scrollIntoViewThreshold: (_c = trigger == null ? void 0 : trigger.scrollIntoViewThreshold) != null ? _c : PX_TRIGGER_DEFAULTS.scrollIntoViewThreshold
660
+ };
661
+ }
1036
662
  var PxLoopRepeatAt = {
1037
663
  /** Segment from the START; the repetition runs BEFORE the first keyframe
1038
664
  * (intro loops that play until the main timeline begins). */
@@ -1092,10 +718,6 @@ var PixodeskAnimator = (() => {
1092
718
  TRANSFORM_PART.scale,
1093
719
  TRANSFORM_PART.origin
1094
720
  ];
1095
- var PxGradientUnits = {
1096
- userSpaceOnUse: "userSpaceOnUse",
1097
- objectBoundingBox: "objectBoundingBox"
1098
- };
1099
721
  var PxGradientSpreadMethod = {
1100
722
  pad: "pad",
1101
723
  reflect: "reflect",
@@ -1108,8 +730,18 @@ var PixodeskAnimator = (() => {
1108
730
  function getAnimatorConfig(doc) {
1109
731
  var _a;
1110
732
  const cfg = (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
1111
- return cfg ? flattenAnimatorTimeline(cfg) : void 0;
1112
- }
733
+ if (!cfg) return void 0;
734
+ const memoised = wireViewMemo.get(cfg);
735
+ if (memoised) return memoised;
736
+ const wire = cfg;
737
+ const stray = PX_FLAT_RUNTIME_VIEW_KEYS.filter((k) => wire[k] !== void 0);
738
+ const source = stray.length ? __spreadValues({}, wire) : cfg;
739
+ for (const k of stray) delete source[k];
740
+ const view = flattenAnimatorTimeline(source);
741
+ wireViewMemo.set(cfg, view);
742
+ return view;
743
+ }
744
+ var wireViewMemo = /* @__PURE__ */ new WeakMap();
1113
745
  var flattenMemo = /* @__PURE__ */ new WeakMap();
1114
746
  function flattenAnimatorTimeline(cfg) {
1115
747
  const timeline = cfg.timeline;
@@ -1135,7 +767,7 @@ var PixodeskAnimator = (() => {
1135
767
  else if (pin && typeof pin === "object") {
1136
768
  scroll.pin = true;
1137
769
  if (pin.align !== void 0) scroll.pinAlign = pin.align;
1138
- if (pin.top !== void 0) scroll.pinTop = pin.top;
770
+ if (pin.offset !== void 0) scroll.pinOffset = pin.offset;
1139
771
  if (pin.distance !== void 0) scroll.pinDistance = pin.distance;
1140
772
  }
1141
773
  flat.scroll = scroll;
@@ -1162,9 +794,7 @@ var PixodeskAnimator = (() => {
1162
794
  function getBindings(doc) {
1163
795
  var _a;
1164
796
  if (!doc) return void 0;
1165
- const animateById = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animateById;
1166
- if (!animateById) return void 0;
1167
- return Object.entries(animateById).map(([id, anim]) => ({ id: id.startsWith("#") ? id.slice(1) : id, animate: anim }));
797
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.bindings;
1168
798
  }
1169
799
 
1170
800
  // ../svg-animator-core/src/format/PxAnimatorTypes.ts
@@ -1177,14 +807,13 @@ var PixodeskAnimator = (() => {
1177
807
  // e.g. for colors
1178
808
  px.number(),
1179
809
  px.array(px.number()),
1180
- // ORDER LAW: the key-discriminated object shapes (`{path}`, `{paths}`) come BEFORE the
810
+ // ORDER LAW: the key-discriminated object shape (`{pathData}`) comes BEFORE the
1181
811
  // all-optional transform-parts record. In default (non-strict) mode that record accepts
1182
812
  // ANY object (every key optional, unknown keys ignored), so listing it earlier made
1183
- // Union.sanitize route `{path}`/`{paths}` values into it and strip them to `{}` —
813
+ // Union.sanitize route `{pathData}` values into it and strip them to `{}` —
1184
814
  // silent morph-data loss (repro: the editor's keyframeValueSanitize spec). Validity is
1185
815
  // order-independent (`some()`); only sanitize routing depends on this order.
1186
- px.object({ path: px.string() }),
1187
- px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
816
+ px.object({ pathData: px.string() }),
1188
817
  // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
1189
818
  px.lazy(() => px.array(PxGradientStopSchema), []),
1190
819
  px.lazy(() => PxTransformPartsSchema, {})
@@ -1224,7 +853,7 @@ var PixodeskAnimator = (() => {
1224
853
  keyframes: px.array(PxKeyframeSchema).optional(),
1225
854
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
1226
855
  autoOrient: px.boolean().optional(),
1227
- alongPathMode: px.enum(["sampled", "offsetPath"]).optional()
856
+ alongPathMode: px.enum([PxAlongPathMode.sampled, PxAlongPathMode.offsetPath]).optional()
1228
857
  }));
1229
858
  var PxTransformPartsSchema = implementsInterface()(px.object({
1230
859
  translate: px.tuple([px.number(), px.number()]).optional(),
@@ -1248,17 +877,17 @@ var PixodeskAnimator = (() => {
1248
877
  PxAnimationDefinitionSchema
1249
878
  ]));
1250
879
  var PxTriggerSchema = implementsInterface()(px.object({
1251
- startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
1252
- outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
880
+ startOn: px.enum([PxStartOn.load, PxStartOn.mouseOver, PxStartOn.click, PxStartOn.scrollIntoView, PxStartOn.programmatic], PX_TRIGGER_DEFAULTS.startOn).optional(),
881
+ outAction: px.enum([PxOutAction.continue, PxOutAction.pause, PxOutAction.reset, PxOutAction.reverse], PX_TRIGGER_DEFAULTS.outAction).optional(),
1253
882
  // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
1254
883
  // `fill`) or `'reset'` (snap back to the start state). Pairs with `outAction` ("what
1255
884
  // happens when the trigger condition ends"); both end-of-life knobs now read alike.
1256
- finishAction: px.enum(["hold", "reset"]).optional(),
885
+ finishAction: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
1257
886
  scrollIntoViewThreshold: px.number().optional()
1258
887
  }));
1259
888
  var PxGlyphSchema = implementsInterface()(px.object({
1260
889
  width: px.number(),
1261
- d: px.string()
890
+ pathData: px.string()
1262
891
  }));
1263
892
  var PxGlyphFontSchema = implementsInterface()(px.object({
1264
893
  fontFamily: px.string(),
@@ -1270,14 +899,17 @@ var PixodeskAnimator = (() => {
1270
899
  var PxDefsSchema = implementsInterface()(px.object({
1271
900
  easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
1272
901
  animations: px.record(PxAnimationDefinitionSchema).optional(),
1273
- // Review §2.6: the schema now matches the declared type — a style preset is a flat
1274
- // record of string|number attribute values, nothing nested.
1275
- styles: px.record(px.record(px.union([px.string(), px.number()]))).optional(),
1276
902
  fonts: px.record(PxGlyphFontSchema).optional()
1277
903
  }));
1278
- var PX_SCROLL_PHASES = ["cover", "contain", "entry", "exit", "entry-crossing", "exit-crossing"];
1279
904
  var PxScrollRangePointSchema = implementsInterface()(px.object({
1280
- phase: px.enum(PX_SCROLL_PHASES).optional(),
905
+ phase: px.enum([
906
+ PxScrollPhase.cover,
907
+ PxScrollPhase.contain,
908
+ PxScrollPhase.entry,
909
+ PxScrollPhase.exit,
910
+ PxScrollPhase.entryCrossing,
911
+ PxScrollPhase.exitCrossing
912
+ ]).optional(),
1281
913
  fraction: px.number().optional()
1282
914
  }));
1283
915
  var PxScrollRangeSchema = px.object({
@@ -1285,21 +917,21 @@ var PixodeskAnimator = (() => {
1285
917
  end: PxScrollRangePointSchema.optional()
1286
918
  });
1287
919
  var PxScrollSchema = implementsInterface()(px.object({
1288
- kind: px.enum(["view", "scroll"]).optional(),
1289
- axis: px.enum(["block", "inline", "x", "y"]).optional(),
1290
- source: px.enum(["nearest", "root"]).optional(),
920
+ kind: px.enum([PxScrollKind.view, PxScrollKind.scroll]).optional(),
921
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
922
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1291
923
  // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.
1292
924
  subject: px.string().optional(),
1293
925
  smoothing: px.number().optional(),
1294
926
  pin: px.boolean().optional(),
1295
- pinAlign: px.enum(["top", "center", "bottom"]).optional(),
1296
- pinTop: px.number().optional(),
927
+ pinAlign: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
928
+ pinOffset: px.number().optional(),
1297
929
  pinDistance: px.number().optional(),
1298
930
  range: PxScrollRangeSchema.optional()
1299
931
  }));
1300
932
  var PxTimelinePinSchema = implementsInterface()(px.object({
1301
- align: px.enum(["top", "center", "bottom"]).optional(),
1302
- top: px.number().optional(),
933
+ align: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
934
+ offset: px.number().optional(),
1303
935
  distance: px.number().optional()
1304
936
  }));
1305
937
  var PxTimelineEngineSchema = px.enum([PxTimelineEngineExtra.auto, PxTimelineEngineExtra.native, PxTimelineEngineExtra.js]).optional();
@@ -1314,8 +946,8 @@ var PixodeskAnimator = (() => {
1314
946
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1315
947
  // `fillMode` on the wire (CSS `animation-fill-mode`; the runtime view calls it `fill`)
1316
948
  // — never `fill`, which is paint everywhere else in the format.
1317
- fillMode: px.enum(["forwards", "backwards", "both", "none"]).optional(),
1318
- direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional()
949
+ fillMode: px.enum([PxFillMode.forwards, PxFillMode.backwards, PxFillMode.both, PxFillMode.none]).optional(),
950
+ direction: px.enum([PxPlaybackDirection.normal, PxPlaybackDirection.reverse, PxPlaybackDirection.alternate, PxPlaybackDirection.alternateReverse]).optional()
1319
951
  }));
1320
952
  var scrollishTimelineShape = {
1321
953
  // §2.8: duration is a property of the TIMELINE — under scrubbing it is the keyframe
@@ -1326,8 +958,8 @@ var PixodeskAnimator = (() => {
1326
958
  iterations: px.number().optional(),
1327
959
  engine: PxTimelineEngineSchema,
1328
960
  frameRate: px.number().optional(),
1329
- axis: px.enum(["block", "inline", "x", "y"]).optional(),
1330
- source: px.enum(["nearest", "root"]).optional(),
961
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
962
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1331
963
  subject: px.string().optional(),
1332
964
  // 'parent' | 'scroller' | any CSS selector
1333
965
  smoothing: px.number().optional(),
@@ -1347,22 +979,22 @@ var PixodeskAnimator = (() => {
1347
979
  PxScrollTimelineSchema,
1348
980
  PxViewTimelineSchema
1349
981
  ]);
982
+ var PxBindingSchema = implementsInterface()(px.object({
983
+ target: px.string(),
984
+ animateWith: px.array(px.string())
985
+ }));
1350
986
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
1351
987
  // (`mode`, `duration` and `frameRate` live INSIDE `timeline` on the wire — §2.8; they exist
1352
988
  // at this level only on the runtime view, like the rest of the playback dynamics.)
1353
989
  // THE spelling of "what advances progress" — clock / scroll / view (review §2.1).
1354
990
  timeline: PxTimelineSchema.optional(),
1355
991
  definitions: PxDefsSchema.optional(),
1356
- animateById: px.record(PxElementAnimationSchema).optional(),
992
+ bindings: px.array(PxBindingSchema).optional(),
1357
993
  debugGlobalName: px.string().optional(),
1358
994
  // Declared HERE because this is a closed object: an undeclared key would be stripped by
1359
995
  // `sanitize` and flagged by strict validation on our own files.
1360
996
  version: px.string().optional()
1361
997
  }));
1362
- var PxBindingSchema = implementsInterface()(px.object({
1363
- id: px.string(),
1364
- animate: PxElementAnimationSchema
1365
- }));
1366
998
  var PxAttrValueSchema = px.union([
1367
999
  px.string(),
1368
1000
  px.number(),
@@ -1416,7 +1048,7 @@ var PixodeskAnimator = (() => {
1416
1048
  height: px.number().optional()
1417
1049
  }));
1418
1050
  var PxClipPathEffectSchema = implementsInterface()(px.object({
1419
- d: PxAnimatableStringSchema.optional()
1051
+ pathData: PxAnimatableStringSchema.optional()
1420
1052
  }));
1421
1053
  var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1422
1054
  offset: PxAnimatableNumberSchema.optional(),
@@ -1453,7 +1085,7 @@ var PixodeskAnimator = (() => {
1453
1085
  radius: PxAnimatableNumberSchema.optional(),
1454
1086
  focal: PxAnimatableVec2Schema.optional(),
1455
1087
  stops: PxAnimatableGradientStopsSchema.optional(),
1456
- gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
1088
+ gradientUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1457
1089
  spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
1458
1090
  gradientTransform: px.string().optional()
1459
1091
  }));
@@ -1499,17 +1131,20 @@ var PixodeskAnimator = (() => {
1499
1131
  // documented — because a wire key that is not in a schema is invisible to the
1500
1132
  // minifier's reserve list and gets renamed (MINIFICATION-BOUNDARY-PLAN.md §1.1).
1501
1133
  domType: px.string().optional(),
1134
+ // Text content of a `<text>` / `<tspan>`. Declared, so a non-string value is a schema error
1135
+ // and the minifier reserves the key; `text` is NOT an alias for it and is not read anywhere.
1136
+ textContent: px.string().optional(),
1502
1137
  id: px.string().optional(),
1503
1138
  meta: px.any().optional(),
1504
1139
  // Player-effects bucket emitted by the Editor's lightweight design format.
1505
- // Consumed and removed by `applyPlayerEffects` before any other normalisation
1140
+ // Consumed and removed by `applyPlayerEffects` before any other normalization
1506
1141
  // (see `createAnimatorImpl`), so downstream code never sees it.
1507
1142
  effects: PxEffectsSchema.optional(),
1508
1143
  // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
1509
1144
  // string ref / array of refs / inline definition / mixed array; mirrors
1510
- // `animator.animateById` map values and what `processNode` resolves at runtime.
1145
+ // `node.animate` values and what `processNode` resolves at runtime.
1511
1146
  animate: PxElementAnimationSchema.optional(),
1512
- style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
1147
+ style: px.record(px.union([px.string(), px.number()])).optional()
1513
1148
  }, PxAttrValueSchema);
1514
1149
  var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
1515
1150
  children: px.lazy(() => px.array(PxNodeSchema), []).optional()
@@ -1534,7 +1169,437 @@ var PixodeskAnimator = (() => {
1534
1169
  c: px.boolean().optional()
1535
1170
  }));
1536
1171
 
1537
- // ../svg-animator-core/src/materialise/PxMotionPath.ts
1172
+ // ../svg-animator-core/src/util/PxAnimatorUtil.ts
1173
+ function bezierToSvgPath(path, forceCurves = false) {
1174
+ var _a, _b, _c, _d;
1175
+ const v = path.v;
1176
+ const i = path.i;
1177
+ const o = path.o;
1178
+ const c = path.c;
1179
+ if (!v.length) return "";
1180
+ const d = [];
1181
+ const len = v.length;
1182
+ d.push("M" + v[0][0] + "," + v[0][1]);
1183
+ for (let idx = 1; idx < len; idx++) {
1184
+ const prevV = v[idx - 1];
1185
+ const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
1186
+ const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
1187
+ const currV = v[idx];
1188
+ const isLine = !forceCurves && (prevO[0] === prevV[0] && prevO[1] === prevV[1]) && (currI[0] === currV[0] && currI[1] === currV[1]);
1189
+ if (isLine) {
1190
+ d.push("L" + currV[0] + "," + currV[1]);
1191
+ } else {
1192
+ d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
1193
+ }
1194
+ }
1195
+ if (c && len > 0) {
1196
+ const lastV = v[len - 1];
1197
+ const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
1198
+ const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
1199
+ const firstV = v[0];
1200
+ const isLine = !forceCurves && (lastO[0] === lastV[0] && lastO[1] === lastV[1]) && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
1201
+ if (!isLine) {
1202
+ d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
1203
+ }
1204
+ d.push("z");
1205
+ }
1206
+ return d.join("");
1207
+ }
1208
+ function interpolateNum(a, b, t) {
1209
+ return a + (b - a) * t;
1210
+ }
1211
+ function interpolateVec(a, b, t) {
1212
+ const res = [];
1213
+ const count = Math.max(a.length, b.length);
1214
+ for (let i = 0; i < count; i++) {
1215
+ res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
1216
+ }
1217
+ return res;
1218
+ }
1219
+ function interpolateColor(a, b, t) {
1220
+ return [
1221
+ interpolateNum(a[0] || 0, b[0] || 0, t),
1222
+ interpolateNum(a[1] || 0, b[1] || 0, t),
1223
+ interpolateNum(a[2] || 0, b[2] || 0, t),
1224
+ interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
1225
+ ];
1226
+ }
1227
+ function interpolateBeziers(paths1, paths2, progress) {
1228
+ const count = Math.max(paths1.length, paths2.length);
1229
+ const res = [];
1230
+ for (let i = 0; i < count; i++) {
1231
+ res.push(interpolateBezier(paths1[i], paths2[i], progress));
1232
+ }
1233
+ return res;
1234
+ }
1235
+ function interpolateBezier(path1, path2, progress) {
1236
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1237
+ if (!path1 || !path2) return path1 || path2 || { v: [] };
1238
+ const t = Math.min(Math.max(progress, 0), 1);
1239
+ const len = Math.min(path1.v.length, path2.v.length);
1240
+ const v = [];
1241
+ const i = [];
1242
+ const o = [];
1243
+ for (let idx = 0; idx < len; idx++) {
1244
+ const v1 = path1.v[idx];
1245
+ const v2 = path2.v[idx];
1246
+ v.push(interpolateVec(v1, v2, t));
1247
+ const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
1248
+ const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
1249
+ i.push(interpolateVec(i1, i2, t));
1250
+ const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
1251
+ const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
1252
+ o.push(interpolateVec(o1, o2, t));
1253
+ }
1254
+ return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
1255
+ }
1256
+ function remap(value, inMin, inMax, outMin, outMax) {
1257
+ if (inMax === inMin) return outMin;
1258
+ const t = (value - inMin) / (inMax - inMin);
1259
+ return outMin + t * (outMax - outMin);
1260
+ }
1261
+ function solveCubicBezierX(p1x, p2x, x) {
1262
+ if (x <= 0) return 0;
1263
+ if (x >= 1) return 1;
1264
+ const cx = 3 * p1x;
1265
+ const bx = 3 * (p2x - p1x) - cx;
1266
+ const ax = 1 - cx - bx;
1267
+ function sampleX(t) {
1268
+ return ((ax * t + bx) * t + cx) * t;
1269
+ }
1270
+ function sampleDX(t) {
1271
+ return (3 * ax * t + 2 * bx) * t + cx;
1272
+ }
1273
+ let t2 = x;
1274
+ let t0 = 0;
1275
+ let t1 = 1;
1276
+ for (let i = 0; i < 8; i++) {
1277
+ const x2 = sampleX(t2) - x;
1278
+ if (Math.abs(x2) < 1e-6) return t2;
1279
+ const d2 = sampleDX(t2);
1280
+ if (Math.abs(d2) < 1e-6) break;
1281
+ t2 -= x2 / d2;
1282
+ }
1283
+ t2 = x;
1284
+ while (t0 < t1) {
1285
+ const x2 = sampleX(t2);
1286
+ if (Math.abs(x2 - x) < 1e-6) return t2;
1287
+ if (x > x2) t0 = t2;
1288
+ else t1 = t2;
1289
+ t2 = (t1 + t0) / 2;
1290
+ }
1291
+ return t2;
1292
+ }
1293
+ function cubicBezier(easing) {
1294
+ const [p1x, p1y, p2x, p2y] = easing;
1295
+ const cy = 3 * p1y;
1296
+ const by = 3 * (p2y - p1y) - cy;
1297
+ const ay = 1 - cy - by;
1298
+ function sampleCurveY(t) {
1299
+ return ((ay * t + by) * t + cy) * t;
1300
+ }
1301
+ return function(x) {
1302
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
1303
+ };
1304
+ }
1305
+ function lerp2(a, b, t) {
1306
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
1307
+ }
1308
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
1309
+ const q0 = lerp2(p0, p1, t);
1310
+ const q1 = lerp2(p1, p2, t);
1311
+ const q2 = lerp2(p2, p3, t);
1312
+ const r0 = lerp2(q0, q1, t);
1313
+ const r1 = lerp2(q1, q2, t);
1314
+ const s = lerp2(r0, r1, t);
1315
+ return {
1316
+ left: [p0, q0, r0, s],
1317
+ right: [s, r1, q2, p3]
1318
+ };
1319
+ }
1320
+ function splitEasing(easing, xFraction) {
1321
+ if (!easing) return { left: void 0, right: void 0 };
1322
+ if (xFraction <= 0) return { left: void 0, right: easing };
1323
+ if (xFraction >= 1) return { left: easing, right: void 0 };
1324
+ const [x1, y1, x2, y2] = easing;
1325
+ const t = solveCubicBezierX(x1, x2, xFraction);
1326
+ const p0 = [0, 0];
1327
+ const p1 = [x1, y1];
1328
+ const p2 = [x2, y2];
1329
+ const p3 = [1, 1];
1330
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
1331
+ const sx = left[3][0];
1332
+ const sy = left[3][1];
1333
+ let leftEasing;
1334
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
1335
+ leftEasing = [
1336
+ left[1][0] / sx,
1337
+ left[1][1] / sy,
1338
+ left[2][0] / sx,
1339
+ left[2][1] / sy
1340
+ ];
1341
+ }
1342
+ let rightEasing;
1343
+ const rx = 1 - sx;
1344
+ const ry = 1 - sy;
1345
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
1346
+ rightEasing = [
1347
+ (right[1][0] - sx) / rx,
1348
+ (right[1][1] - sy) / ry,
1349
+ (right[2][0] - sx) / rx,
1350
+ (right[2][1] - sy) / ry
1351
+ ];
1352
+ }
1353
+ return { left: leftEasing, right: rightEasing };
1354
+ }
1355
+ function reverseEasing(easing) {
1356
+ if (!easing) return void 0;
1357
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
1358
+ }
1359
+ function toRGBA(color) {
1360
+ const r = Math.round(color[0] * 255);
1361
+ const g = Math.round(color[1] * 255);
1362
+ const b = Math.round(color[2] * 255);
1363
+ return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
1364
+ }
1365
+ function parseRgba(s) {
1366
+ var _a;
1367
+ const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
1368
+ if (!inner) throw new Error("Invalid rgb/rgba format");
1369
+ const parts = inner.split(",").map((v) => +v.trim());
1370
+ return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
1371
+ }
1372
+ function parseHex(s) {
1373
+ const hex = s.slice(1);
1374
+ const isShort = hex.length <= 4;
1375
+ const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
1376
+ const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
1377
+ const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
1378
+ const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
1379
+ const result = [
1380
+ parseInt(r, 16) / 255,
1381
+ parseInt(g, 16) / 255,
1382
+ parseInt(b, 16) / 255
1383
+ ];
1384
+ if (a !== null) {
1385
+ result.push(parseInt(a, 16) / 255);
1386
+ }
1387
+ return result;
1388
+ }
1389
+ function parseColor(s) {
1390
+ if (!s) return void 0;
1391
+ if (Array.isArray(s)) return s;
1392
+ if (typeof s !== "string") return void 0;
1393
+ if (s.startsWith("#")) {
1394
+ return parseHex(s);
1395
+ } else if (s.startsWith("rgb")) {
1396
+ return parseRgba(s);
1397
+ } else {
1398
+ console.warn("Unsupported color format: " + s);
1399
+ }
1400
+ return void 0;
1401
+ }
1402
+ var COLOR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
1403
+ var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
1404
+ var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1405
+ function composeTransformParts(parts, opts) {
1406
+ var _a;
1407
+ if (!parts) return "";
1408
+ const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
1409
+ const segs = [];
1410
+ const t = parts.translate;
1411
+ const o = parts.origin;
1412
+ const r = parts.rotate;
1413
+ const k = parts.skew;
1414
+ const s = parts.scale;
1415
+ const tu = withUnits ? "px" : "";
1416
+ const ru = withUnits ? "deg" : "";
1417
+ if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
1418
+ if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
1419
+ if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
1420
+ if (k !== void 0 && k !== null) segs.push("skewX(" + k + ru + ")");
1421
+ if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
1422
+ if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
1423
+ return segs.join("");
1424
+ }
1425
+ function parseTransformParts(str) {
1426
+ var _a, _b;
1427
+ if (!str || typeof str !== "string") return void 0;
1428
+ const out = {};
1429
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
1430
+ const order = ["translate", "rotate", "skewX", "scale"];
1431
+ let lastIdx = -1;
1432
+ let m;
1433
+ while ((m = re.exec(str)) !== null) {
1434
+ const fn = m[1];
1435
+ const idx = order.indexOf(fn);
1436
+ if (idx < 0 || idx <= lastIdx) return void 0;
1437
+ lastIdx = idx;
1438
+ const nums = m[2].split(/[\s,]+/).filter(Boolean).map(Number);
1439
+ if (nums.some((n) => Number.isNaN(n))) return void 0;
1440
+ if (fn === "translate") {
1441
+ if (nums.length < 1 || nums.length > 2) return void 0;
1442
+ out.translate = [nums[0], (_a = nums[1]) != null ? _a : 0];
1443
+ } else if (fn === "rotate") {
1444
+ if (nums.length !== 1) return void 0;
1445
+ out.rotate = nums[0];
1446
+ } else if (fn === "skewX") {
1447
+ if (nums.length !== 1) return void 0;
1448
+ out.skew = nums[0];
1449
+ } else {
1450
+ if (nums.length < 1 || nums.length > 2) return void 0;
1451
+ out.scale = [nums[0], (_b = nums[1]) != null ? _b : nums[0]];
1452
+ }
1453
+ }
1454
+ if (str.replace(/([a-zA-Z]+)\s*\(([^)]*)\)/g, "").replace(/[\s,]/g, "").length) return void 0;
1455
+ return Object.keys(out).length ? out : void 0;
1456
+ }
1457
+ var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1458
+ var DEFAULT_DURATION_MS = 1e3;
1459
+ function kebabToCamelCaseWord(kebab) {
1460
+ return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
1461
+ }
1462
+ function isCamelCaseWord(word) {
1463
+ return !word.includes("-") && /[a-z][A-Z]/.test(word);
1464
+ }
1465
+ var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
1466
+ // Transform/positioning
1467
+ "viewBox",
1468
+ "preserveAspectRatio",
1469
+ // Gradient
1470
+ "gradientUnits",
1471
+ "gradientTransform",
1472
+ "spreadMethod",
1473
+ // Pattern
1474
+ "patternUnits",
1475
+ "patternContentUnits",
1476
+ "patternTransform",
1477
+ // Clipping/masking
1478
+ "clipPathUnits",
1479
+ "maskUnits",
1480
+ "maskContentUnits",
1481
+ // Marker (SVG spec keeps these camelCase, like viewBox)
1482
+ "markerUnits",
1483
+ "markerWidth",
1484
+ "markerHeight",
1485
+ "refX",
1486
+ "refY",
1487
+ // Text
1488
+ "textLength",
1489
+ "lengthAdjust",
1490
+ "startOffset",
1491
+ // Filter
1492
+ "filterUnits",
1493
+ "primitiveUnits",
1494
+ "tableValues",
1495
+ // feFuncR/G/B/A transfer table (type="table")
1496
+ "stdDeviation",
1497
+ "baseFrequency",
1498
+ "numOctaves",
1499
+ "surfaceScale",
1500
+ "diffuseConstant",
1501
+ "specularConstant",
1502
+ "specularExponent",
1503
+ "kernelMatrix",
1504
+ "kernelUnitLength",
1505
+ "edgeMode",
1506
+ "preserveAlpha",
1507
+ "targetX",
1508
+ "targetY"
1509
+ // // Animation
1510
+ // 'attributeName',
1511
+ // 'attributeType',
1512
+ // 'calcMode',
1513
+ // 'keyTimes',
1514
+ // 'keySplines',
1515
+ // 'repeatCount',
1516
+ // 'repeatDur'
1517
+ ]);
1518
+ function camelCaseToKebabWordIfNeeded(camel) {
1519
+ return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1520
+ }
1521
+ function clamp(value, min, max) {
1522
+ return Math.max(min, Math.min(value, max));
1523
+ }
1524
+ function bezier2D_pointAt(P0, P1, P2, P3, t) {
1525
+ if (t <= 0) return [P0[0], P0[1]];
1526
+ if (t >= 1) return [P3[0], P3[1]];
1527
+ const u = 1 - t;
1528
+ const u2 = u * u;
1529
+ const u3 = u2 * u;
1530
+ const t2 = t * t;
1531
+ const t3 = t2 * t;
1532
+ const w0 = u3;
1533
+ const w1 = 3 * t * u2;
1534
+ const w2 = 3 * t2 * u;
1535
+ const w3 = t3;
1536
+ return [
1537
+ w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
1538
+ w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
1539
+ ];
1540
+ }
1541
+ var BEZIER_T_NUDGE = 1e-4;
1542
+ function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
1543
+ const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
1544
+ if (result[0] === 0 && result[1] === 0) {
1545
+ const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
1546
+ return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
1547
+ }
1548
+ return result;
1549
+ }
1550
+ function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
1551
+ const u = 1 - t;
1552
+ const a = 3 * u * u;
1553
+ const b = 6 * t * u;
1554
+ const c = 3 * t * t;
1555
+ return [
1556
+ a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
1557
+ a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
1558
+ ];
1559
+ }
1560
+ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
1561
+ const n = steps + 1;
1562
+ const ts = new Float64Array(n);
1563
+ const ds = new Float64Array(n);
1564
+ let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
1565
+ ts[0] = 0;
1566
+ ds[0] = 0;
1567
+ let cum = 0;
1568
+ for (let i = 1; i < n; i++) {
1569
+ const t = i / steps;
1570
+ const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
1571
+ const dx = cur[0] - prev[0];
1572
+ const dy = cur[1] - prev[1];
1573
+ cum += Math.sqrt(dx * dx + dy * dy);
1574
+ ts[i] = t;
1575
+ ds[i] = cum;
1576
+ prev = cur;
1577
+ }
1578
+ return { ts, ds };
1579
+ }
1580
+ function bezier2D_arcAtT(lut, t) {
1581
+ const { ts, ds } = lut;
1582
+ const last = ts.length - 1;
1583
+ if (t <= ts[0]) return ds[0];
1584
+ if (t >= ts[last]) return ds[last];
1585
+ let lo = 1, hi = last;
1586
+ while (lo < hi) {
1587
+ const mid = lo + hi >>> 1;
1588
+ if (ts[mid] < t) lo = mid + 1;
1589
+ else hi = mid;
1590
+ }
1591
+ const tPrev = ts[hi - 1];
1592
+ const span = ts[hi] - tPrev;
1593
+ const frac = span > 0 ? (t - tPrev) / span : 0;
1594
+ return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);
1595
+ }
1596
+ function invertEasing(easing) {
1597
+ if (!easing) return (y) => y;
1598
+ const flipped = [easing[1], easing[0], easing[3], easing[2]];
1599
+ return cubicBezier(flipped);
1600
+ }
1601
+
1602
+ // ../svg-animator-core/src/materialize/PxMotionPath.ts
1538
1603
  function getKfTranslate(kf) {
1539
1604
  const v = kfValue(kf);
1540
1605
  if (!v) return void 0;
@@ -1615,7 +1680,7 @@ var PixodeskAnimator = (() => {
1615
1680
  var DEFAULT_FLATNESS_TOL = 0.5;
1616
1681
  var DEFAULT_ROTATION_TOL = 5;
1617
1682
  var DEFAULT_MAX_SAMPLES = 32;
1618
- function materialiseMotionPathInPropAnim(anim, opts) {
1683
+ function materializeMotionPathInPropAnim(anim, opts) {
1619
1684
  var _a, _b, _c;
1620
1685
  if (!propAnimIsMotionPath(anim)) return anim;
1621
1686
  const kfs = anim.keyframes;
@@ -1647,7 +1712,7 @@ var PixodeskAnimator = (() => {
1647
1712
  if (autoOrient && i > 0) {
1648
1713
  insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol);
1649
1714
  }
1650
- materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
1715
+ materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
1651
1716
  }
1652
1717
  const lastInE = getKfEasing(kfs[kfs.length - 1]);
1653
1718
  if (lastInE) out[out.length - 1].e = lastInE;
@@ -1761,7 +1826,7 @@ var PixodeskAnimator = (() => {
1761
1826
  out.push(makeOutKf(stepTime, dupValue));
1762
1827
  }
1763
1828
  var CORNER_STEP_AFTER_BOUNDARY_MS = 0.05;
1764
- function materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
1829
+ function materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
1765
1830
  const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
1766
1831
  const prevTime = getKfTime(prevKf);
1767
1832
  const nextTime = getKfTime(nextKf);
@@ -1977,8 +2042,8 @@ var PixodeskAnimator = (() => {
1977
2042
  return typeof value === "string" && extractPathData(value) !== void 0;
1978
2043
  }
1979
2044
  function normalizePathValue(value) {
1980
- if (value && typeof value === "object" && typeof value.path === "string") {
1981
- const d = extractPathData(value.path);
2045
+ if (value && typeof value === "object" && typeof value.pathData === "string") {
2046
+ const d = extractPathData(value.pathData);
1982
2047
  return d ? { paths: parseSvgPathToBezier(d) } : value;
1983
2048
  }
1984
2049
  if (value && typeof value === "object" && "paths" in value) {
@@ -2062,7 +2127,7 @@ var PixodeskAnimator = (() => {
2062
2127
  const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
2063
2128
  return { paths: interpolateBeziers(aPaths, bPaths, t) };
2064
2129
  }
2065
- if (COLOUR_ATTR_NAMES.has(propName)) {
2130
+ if (COLOR_ATTR_NAMES.has(propName)) {
2066
2131
  return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
2067
2132
  }
2068
2133
  if (propName === "transform" && typeof a === "object" && a !== null && !Array.isArray(a) && typeof b === "object" && b !== null && !Array.isArray(b)) {
@@ -2284,7 +2349,7 @@ var PixodeskAnimator = (() => {
2284
2349
  value = normalizePathValue(value);
2285
2350
  }
2286
2351
  const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2287
- if (COLOUR_ATTR_NAMES.has(propNameKebab)) {
2352
+ if (COLOR_ATTR_NAMES.has(propNameKebab)) {
2288
2353
  value = (_a = parseColor(value)) != null ? _a : value;
2289
2354
  }
2290
2355
  const normKf = {
@@ -2363,19 +2428,17 @@ var PixodeskAnimator = (() => {
2363
2428
  const out = { keyframes: normalizedKfs };
2364
2429
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2365
2430
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2366
- normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2431
+ normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materializeMotionPathInPropAnim(out) : out;
2367
2432
  }
2368
2433
  }
2369
2434
  return normalized;
2370
2435
  }
2371
- function getNormalisedBindings(doc, engine = PxTimelineEngine.native) {
2436
+ function getNormalizedBindings(doc, engine = PxTimelineEngine.native) {
2372
2437
  const animatorConfig = getAnimatorConfig(doc) || {};
2373
2438
  const defs = getDefs(doc);
2374
2439
  const duration = animatorConfig.duration || 1e3;
2375
2440
  const bindings = [];
2376
- const processAnimation = (id, animate, staticTransform) => {
2377
- if (!animate) return null;
2378
- const animDefs = resolveElementAnimation(animate, defs);
2441
+ const processAnimation = (id, animDefs, staticTransform) => {
2379
2442
  if (animDefs.length === 0) return null;
2380
2443
  const merged = mergeStaticTransformIntoAnimDef(mergeAnimationDefinitions(animDefs), staticTransform);
2381
2444
  const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);
@@ -2388,7 +2451,9 @@ var PixodeskAnimator = (() => {
2388
2451
  const docBindings = getBindings(doc);
2389
2452
  if (docBindings) {
2390
2453
  for (const binding of docBindings) {
2391
- const normalized = processAnimation(binding.id, binding.animate);
2454
+ const id = binding.target.startsWith("#") ? binding.target.slice(1) : binding.target;
2455
+ const animDefs = binding.animateWith.map((name) => resolveAnimation(name, defs)).filter((d) => !!d);
2456
+ const normalized = processAnimation(id, animDefs);
2392
2457
  if (normalized) bindings.push(normalized);
2393
2458
  }
2394
2459
  }
@@ -2397,7 +2462,7 @@ var PixodeskAnimator = (() => {
2397
2462
  if (inlineAnim && Object.keys(inlineAnim).length > 0) {
2398
2463
  const nodeId = node.id || generateElementId();
2399
2464
  node.id = nodeId;
2400
- const normalized = processAnimation(nodeId, inlineAnim, node.transform);
2465
+ const normalized = processAnimation(nodeId, resolveElementAnimation(inlineAnim, defs), node.transform);
2401
2466
  if (normalized) bindings.push(normalized);
2402
2467
  }
2403
2468
  if (node.children) {
@@ -2459,7 +2524,7 @@ var PixodeskAnimator = (() => {
2459
2524
  nextPaths,
2460
2525
  localProgress
2461
2526
  ).map((bz) => bezierToSvgPath(bz)).join("");
2462
- } else if (COLOUR_ATTR_NAMES.has(cssAttrName)) {
2527
+ } else if (COLOR_ATTR_NAMES.has(cssAttrName)) {
2463
2528
  cssValue = toRGBA(interpolateColor(
2464
2529
  prevV || [0, 0, 0, 1],
2465
2530
  nextV || [0, 0, 0, 1],
@@ -2556,6 +2621,40 @@ var PixodeskAnimator = (() => {
2556
2621
  return result;
2557
2622
  }
2558
2623
 
2624
+ // ../svg-animator-core/src/playback/PxPlaybackTime.ts
2625
+ var PX_RATE_REJECTED = "setPlaybackRate: rate must be finite and non-zero";
2626
+ function isValidPlaybackRate(rate) {
2627
+ return Number.isFinite(rate) && rate !== 0;
2628
+ }
2629
+ function seekCeilingMs(durationMs, iterations) {
2630
+ if (!(durationMs > 0)) return 0;
2631
+ if (iterations === Infinity) return Infinity;
2632
+ return durationMs * (iterations > 0 ? iterations : 1);
2633
+ }
2634
+ function progressSpanMs(durationMs, iterations) {
2635
+ if (!(durationMs > 0)) return 0;
2636
+ if (iterations === Infinity) return durationMs;
2637
+ return durationMs * (iterations > 0 ? iterations : 1);
2638
+ }
2639
+ function clampSeekMs(timeMs, ceilingMs) {
2640
+ if (Number.isNaN(timeMs) || timeMs < 0) return 0;
2641
+ if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;
2642
+ return Number.isFinite(timeMs) ? timeMs : 0;
2643
+ }
2644
+ function timeToProgress(timeMs, durationMs, iterations) {
2645
+ const span = progressSpanMs(durationMs, iterations);
2646
+ if (!(span > 0) || !Number.isFinite(timeMs)) return 0;
2647
+ if (timeMs <= 0) return 0;
2648
+ if (iterations === Infinity) return timeMs % span / span;
2649
+ return timeMs >= span ? 1 : timeMs / span;
2650
+ }
2651
+ function progressToTimeMs(progress, durationMs, iterations) {
2652
+ const span = progressSpanMs(durationMs, iterations);
2653
+ if (!(span > 0) || !Number.isFinite(progress)) return 0;
2654
+ if (progress <= 0) return 0;
2655
+ return progress >= 1 ? span : progress * span;
2656
+ }
2657
+
2559
2658
  // ../svg-animator-core/src/playback/PxFrameLoop.ts
2560
2659
  function requestFrame(cb) {
2561
2660
  const g = globalThis;
@@ -2573,7 +2672,7 @@ var PixodeskAnimator = (() => {
2573
2672
  function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
2574
2673
  var _a;
2575
2674
  const config = getAnimatorConfig(doc) || {};
2576
- const bindings = getNormalisedBindings(doc, PxTimelineEngine.js);
2675
+ const bindings = getNormalizedBindings(doc, PxTimelineEngine.js);
2577
2676
  const _iterations = config.iterations;
2578
2677
  let iterations = 1;
2579
2678
  if (typeof _iterations === "number") iterations = _iterations || 1;
@@ -2803,8 +2902,8 @@ var PixodeskAnimator = (() => {
2803
2902
  finishAnim(true);
2804
2903
  },
2805
2904
  "setPlaybackRate": (rate) => {
2806
- if (!isFinite(rate) || rate === 0) {
2807
- console.warn("setPlaybackRate: rate must be finite and non-zero");
2905
+ if (!isValidPlaybackRate(rate)) {
2906
+ console.warn(PX_RATE_REJECTED);
2808
2907
  return;
2809
2908
  }
2810
2909
  let current = getRawAnimTime();
@@ -2818,13 +2917,16 @@ var PixodeskAnimator = (() => {
2818
2917
  return getAnimCurrentTime();
2819
2918
  },
2820
2919
  "setCurrentTime": (newTime) => {
2821
- if (newTime < 0) newTime = 0;
2822
- if (Number.isFinite(totalDuration) && newTime > totalDuration) newTime = totalDuration;
2920
+ newTime = clampSeekMs(newTime, totalDuration);
2823
2921
  timeBeforeLastStartMs = newTime;
2824
2922
  if (playing) lastStartedTs = Date.now();
2825
2923
  if (!Number.isFinite(totalDuration) || newTime < totalDuration) finishCalled = false;
2826
2924
  renderFrame(getAnimCurrentTime());
2827
2925
  },
2926
+ "getCurrentProgress": () => timeToProgress(getAnimCurrentTime(), duration, iterations),
2927
+ "setCurrentProgress": (progress) => {
2928
+ api.setCurrentTime(progressToTimeMs(progress, duration, iterations));
2929
+ },
2828
2930
  "destroy": () => {
2829
2931
  var _a2;
2830
2932
  api.cancel();
@@ -2834,13 +2936,165 @@ var PixodeskAnimator = (() => {
2834
2936
  return api;
2835
2937
  }
2836
2938
 
2939
+ // ../svg-animator-core/src/playback/PxDiagnostics.ts
2940
+ var PxDiagnosticKind = {
2941
+ /** The document is wrong — regenerate or repair the file. */
2942
+ document: "document",
2943
+ /** The page or app cannot provide what the document asks for — fix the mount. */
2944
+ host: "host",
2945
+ /** The platform cannot do it and the player degraded — usually nothing to fix. */
2946
+ platform: "platform",
2947
+ /** The call is wrong or self-contradictory — fix the options or props you passed. */
2948
+ usage: "usage",
2949
+ /** The player failed where it did not expect to — report it to us. */
2950
+ internal: "internal"
2951
+ };
2952
+ function asError(error) {
2953
+ return typeof error === "string" ? new Error(error) : error;
2954
+ }
2955
+ function createDiagnostics(config, prefix) {
2956
+ const tag = prefix ? prefix + " " : "";
2957
+ return {
2958
+ warn: (kind, message, detail) => {
2959
+ if (config == null ? void 0 : config.onWarn) {
2960
+ config.onWarn({ kind, message, detail });
2961
+ return;
2962
+ }
2963
+ if (config == null ? void 0 : config.muteWarn) return;
2964
+ const line = tag + kind + ": " + message;
2965
+ if (detail === void 0) console.warn(line);
2966
+ else console.warn(line, detail);
2967
+ },
2968
+ error: (kind, error, detail) => {
2969
+ const err = asError(error);
2970
+ if (config == null ? void 0 : config.onError) {
2971
+ config.onError({ kind, message: err.message, error: err, detail });
2972
+ return;
2973
+ }
2974
+ if (config == null ? void 0 : config.muteError) return;
2975
+ const line = tag + kind + ": " + err.message;
2976
+ if (detail === void 0) console.error(line);
2977
+ else console.error(line, detail);
2978
+ }
2979
+ };
2980
+ }
2981
+
2982
+ // ../svg-animator-core/src/playback/PxScrollMath.ts
2983
+ function isScrollTimeline(config) {
2984
+ return (config == null ? void 0 : config.timelineSource) === "scroll";
2985
+ }
2986
+ function scrollTotalDurationMs(config) {
2987
+ const duration = typeof (config == null ? void 0 : config.duration) === "number" && config.duration > 0 ? config.duration : DEFAULT_DURATION_MS;
2988
+ const iterations = typeof (config == null ? void 0 : config.iterations) === "number" && config.iterations > 0 ? config.iterations : 1;
2989
+ return duration * iterations;
2990
+ }
2991
+ function scrollPhaseInterval(phase, subjectSize, scrollportSize) {
2992
+ const s = subjectSize, vp = scrollportSize;
2993
+ switch (phase) {
2994
+ case "cover":
2995
+ return [0, s + vp];
2996
+ case "entry":
2997
+ return [0, Math.min(s, vp)];
2998
+ case "contain":
2999
+ return [Math.min(s, vp), Math.max(s, vp)];
3000
+ case "exit":
3001
+ return [Math.max(s, vp), s + vp];
3002
+ case "entry-crossing":
3003
+ return [0, s];
3004
+ case "exit-crossing":
3005
+ return [vp, s + vp];
3006
+ }
3007
+ }
3008
+ var DEFAULT_PHASE = "cover";
3009
+ function resolveRangePointU(point, defaultFraction, subjectSize, scrollportSize) {
3010
+ var _a;
3011
+ const [u0, u1] = scrollPhaseInterval((_a = point == null ? void 0 : point.phase) != null ? _a : DEFAULT_PHASE, subjectSize, scrollportSize);
3012
+ const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
3013
+ return u0 + fraction * (u1 - u0);
3014
+ }
3015
+ function scrollViewProgress(subjectStart, subjectSize, scrollportSize, range) {
3016
+ const u = scrollportSize - subjectStart;
3017
+ const uStart = resolveRangePointU(range == null ? void 0 : range.start, 0, subjectSize, scrollportSize);
3018
+ const uEnd = resolveRangePointU(range == null ? void 0 : range.end, 1, subjectSize, scrollportSize);
3019
+ if (uEnd <= uStart) return u >= uEnd ? 1 : 0;
3020
+ return clamp((u - uStart) / (uEnd - uStart), 0, 1);
3021
+ }
3022
+ function scrollOffsetProgress(offset, maxOffset, range) {
3023
+ var _a, _b;
3024
+ const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;
3025
+ const start = typeof ((_a = range == null ? void 0 : range.start) == null ? void 0 : _a.fraction) === "number" ? range.start.fraction : 0;
3026
+ const end = typeof ((_b = range == null ? void 0 : range.end) == null ? void 0 : _b.fraction) === "number" ? range.end.fraction : 1;
3027
+ if (end <= start) return raw >= end ? 1 : 0;
3028
+ return clamp((raw - start) / (end - start), 0, 1);
3029
+ }
3030
+ function scrollResolveAxis(axis, writingMode) {
3031
+ const a = axis != null ? axis : "block";
3032
+ if (a === "x" || a === "y") return a;
3033
+ const vertical = !!writingMode && writingMode.startsWith("vertical");
3034
+ if (a === "inline") return vertical ? "y" : "x";
3035
+ return vertical ? "x" : "y";
3036
+ }
3037
+
3038
+ // src/shared/PxAnimatorCallbacks.ts
3039
+ function toEngineCallbacks(inline) {
3040
+ const { onPlay, onPause, onCancel, onFinish, onRemove, onStop, onWarn, onError, muteWarn, muteError } = inline != null ? inline : {};
3041
+ const withStop = (own) => own || onStop ? () => {
3042
+ own == null ? void 0 : own();
3043
+ onStop == null ? void 0 : onStop();
3044
+ } : void 0;
3045
+ return {
3046
+ onPlay,
3047
+ onPause: withStop(onPause),
3048
+ onCancel: withStop(onCancel),
3049
+ onFinish: withStop(onFinish),
3050
+ onRemove: withStop(onRemove),
3051
+ onWarn,
3052
+ onError,
3053
+ muteWarn,
3054
+ muteError
3055
+ };
3056
+ }
3057
+ function createInertAnimator() {
3058
+ return {
3059
+ isReady: () => false,
3060
+ getRootElement: () => null,
3061
+ isPlaying: () => false,
3062
+ play: () => {
3063
+ },
3064
+ pause: () => {
3065
+ },
3066
+ cancel: () => {
3067
+ },
3068
+ finish: () => {
3069
+ },
3070
+ setPlaybackRate: () => {
3071
+ },
3072
+ getCurrentTime: () => null,
3073
+ setCurrentTime: () => {
3074
+ },
3075
+ getCurrentProgress: () => null,
3076
+ setCurrentProgress: () => {
3077
+ },
3078
+ destroy: () => {
3079
+ }
3080
+ };
3081
+ }
3082
+ function asThrownError(e) {
3083
+ return e instanceof Error ? e : new Error(String(e));
3084
+ }
3085
+
2837
3086
  // src/triggers/PxAnimatorTriggers.ts
2838
- function setupAnimationTriggers(api, config) {
2839
- const { startOn, outAction = "continue", scrollIntoViewThreshold = 0 } = config;
3087
+ function setupAnimationTriggers(api, config, diag) {
3088
+ const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
3089
+ const cleanups = [];
3090
+ const dispose = () => {
3091
+ for (const undo of cleanups.splice(0)) undo();
3092
+ };
3093
+ const { startOn, outAction, scrollIntoViewThreshold } = resolveTrigger(config);
2840
3094
  const root = api.getRootElement();
2841
3095
  if (!root) {
2842
- console.warn("setupAnimationTriggers: No root element found for animation.");
2843
- return api;
3096
+ report.warn(PxDiagnosticKind.host, "setupAnimationTriggers: No root element found for animation.");
3097
+ return dispose;
2844
3098
  }
2845
3099
  let reversed = false;
2846
3100
  const start = () => {
@@ -2875,6 +3129,7 @@ var PixodeskAnimator = (() => {
2875
3129
  startHandler();
2876
3130
  } else {
2877
3131
  window.addEventListener("load", startHandler, { once: true });
3132
+ cleanups.push(() => window.removeEventListener("load", startHandler));
2878
3133
  }
2879
3134
  break;
2880
3135
  }
@@ -2889,6 +3144,10 @@ var PixodeskAnimator = (() => {
2889
3144
  };
2890
3145
  root.addEventListener("mouseenter", mouseOverHandler);
2891
3146
  root.addEventListener("mouseleave", mouseOutHandler);
3147
+ cleanups.push(() => {
3148
+ root.removeEventListener("mouseenter", mouseOverHandler);
3149
+ root.removeEventListener("mouseleave", mouseOutHandler);
3150
+ });
2892
3151
  break;
2893
3152
  }
2894
3153
  case "click": {
@@ -2900,6 +3159,7 @@ var PixodeskAnimator = (() => {
2900
3159
  }
2901
3160
  };
2902
3161
  root.addEventListener("click", clickHandler);
3162
+ cleanups.push(() => root.removeEventListener("click", clickHandler));
2903
3163
  break;
2904
3164
  }
2905
3165
  case "scrollIntoView": {
@@ -2931,12 +3191,13 @@ var PixodeskAnimator = (() => {
2931
3191
  { threshold: thresholdSteps }
2932
3192
  );
2933
3193
  observer.observe(root);
3194
+ cleanups.push(() => observer.disconnect());
2934
3195
  break;
2935
3196
  }
2936
3197
  case "programmatic":
2937
3198
  break;
2938
3199
  }
2939
- return api;
3200
+ return dispose;
2940
3201
  }
2941
3202
 
2942
3203
  // src/engines/PxAnimatorFrameLoop.ts
@@ -2944,35 +3205,41 @@ var PixodeskAnimator = (() => {
2944
3205
  return "#" + id;
2945
3206
  }
2946
3207
  function createFrameLoopAnimator(doc, adapter, callbacks, rootElement) {
3208
+ var _a;
2947
3209
  const config = getAnimatorConfig(doc) || {};
3210
+ const diag = createDiagnostics(callbacks, "[PxAnimator]");
2948
3211
  if (!rootElement) {
2949
3212
  if (doc.id) {
2950
3213
  const rootSelector = getSelector(doc.id);
2951
3214
  rootElement = document.querySelector(rootSelector);
2952
- if (!rootElement) console.warn("createFrameLoopAnimator: No root element found for selector: ", rootSelector);
3215
+ if (!rootElement) diag.warn(PxDiagnosticKind.host, "createFrameLoopAnimator: No root element found for selector: " + rootSelector);
2953
3216
  } else {
2954
- console.warn("createFrameLoopAnimator: No root element provided");
3217
+ diag.warn(PxDiagnosticKind.host, "createFrameLoopAnimator: No root element provided");
2955
3218
  }
2956
3219
  }
2957
3220
  const basicApi = createBasicFrameLoopAnimator(
2958
3221
  doc,
2959
- adapter || createDomAdapter(rootElement),
3222
+ adapter || createDomAdapter(rootElement, diag),
2960
3223
  callbacks
2961
3224
  );
2962
3225
  const api = __spreadProps(__spreadValues({}, basicApi), {
2963
3226
  "getRootElement": () => rootElement || null
2964
3227
  });
2965
- if (config.trigger) {
2966
- if (isScrollTimeline(config)) {
2967
- console.warn("scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
2968
- } else {
2969
- setupAnimationTriggers(api, config.trigger);
2970
- }
3228
+ if (isScrollTimeline(config)) {
3229
+ if (config.trigger) diag.warn(PxDiagnosticKind.usage, "scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
3230
+ } else {
3231
+ const detachTriggers = setupAnimationTriggers(api, (_a = config.trigger) != null ? _a : {}, diag);
3232
+ const destroyEngine = api.destroy.bind(api);
3233
+ api.destroy = () => {
3234
+ detachTriggers();
3235
+ destroyEngine();
3236
+ };
2971
3237
  }
2972
3238
  return api;
2973
3239
  }
2974
- function createDomAdapter(rootElement) {
3240
+ function createDomAdapter(rootElement, diag) {
2975
3241
  const warnedSelectors = /* @__PURE__ */ new Set();
3242
+ const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
2976
3243
  const adapter = {
2977
3244
  isConnected: () => {
2978
3245
  if (!rootElement) return true;
@@ -2984,7 +3251,7 @@ var PixodeskAnimator = (() => {
2984
3251
  const elements = (rootElement == null ? void 0 : rootElement.querySelectorAll(selector)) || document.querySelectorAll(selector);
2985
3252
  if (elements.length === 0 && !warnedSelectors.has(selector)) {
2986
3253
  warnedSelectors.add(selector);
2987
- console.warn('setAttribute: No elements found for selector "' + selector + '"');
3254
+ report.warn(PxDiagnosticKind.host, 'setAttribute: No elements found for selector "' + selector + '"');
2988
3255
  }
2989
3256
  for (let i = 0; i < elements.length; i++) {
2990
3257
  const element = elements[i];
@@ -3009,7 +3276,7 @@ var PixodeskAnimator = (() => {
3009
3276
  };
3010
3277
  let cssValue;
3011
3278
  let cssKey = propName;
3012
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
3279
+ if (COLOR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
3013
3280
  cssValue = toRGBA(value);
3014
3281
  } else if (propName === "transform" && value !== null && typeof value === "object" && !Array.isArray(value)) {
3015
3282
  cssValue = composeTransformParts(value, { withUnits: true });
@@ -3096,18 +3363,19 @@ var PixodeskAnimator = (() => {
3096
3363
  return result;
3097
3364
  }
3098
3365
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs, scrollTimeline) {
3099
- var _a;
3366
+ var _a, _b;
3100
3367
  const config = getAnimatorConfig(doc) || {};
3368
+ const diag = createDiagnostics(callbacks, "[PxAnimator]");
3101
3369
  if (!rootElement) {
3102
3370
  if (doc.id) {
3103
3371
  const rootSelector = getSelector(doc.id);
3104
3372
  rootElement = document.querySelector(rootSelector);
3105
- if (!rootElement) console.warn("createFrameLoopAnimator: No root element found for selector: ", rootSelector);
3373
+ if (!rootElement) diag.warn(PxDiagnosticKind.host, "createWebApiAnimator: No root element found for selector: " + rootSelector);
3106
3374
  } else {
3107
- console.warn("createFrameLoopAnimator: No root element provided");
3375
+ diag.warn(PxDiagnosticKind.host, "createWebApiAnimator: No root element provided");
3108
3376
  }
3109
3377
  }
3110
- const bindings = getNormalisedBindings(doc, PxTimelineEngine.native);
3378
+ const bindings = getNormalizedBindings(doc, PxTimelineEngine.native);
3111
3379
  const animations = [];
3112
3380
  const _iterations = config.iterations;
3113
3381
  let iterations;
@@ -3117,18 +3385,18 @@ var PixodeskAnimator = (() => {
3117
3385
  let finishNotified = false;
3118
3386
  let removeCalled = false;
3119
3387
  if (!(bindings == null ? void 0 : bindings.length)) {
3120
- console.warn("createWebApiAnimator: No animation bindings defined");
3388
+ diag.warn(PxDiagnosticKind.document, "createWebApiAnimator: No animation bindings defined");
3121
3389
  }
3122
3390
  for (const binding of bindings || []) {
3123
3391
  const animDef = binding.animate;
3124
3392
  if (!animDef || typeof animDef !== "object" || Array.isArray(animDef)) {
3125
- console.warn("createWebApiAnimator: Empty or unresolved binding", binding);
3393
+ diag.warn(PxDiagnosticKind.document, "createWebApiAnimator: Empty or unresolved binding", binding);
3126
3394
  continue;
3127
3395
  }
3128
3396
  const selector = getSelector(binding.id);
3129
3397
  const elements = (rootElement == null ? void 0 : rootElement.querySelectorAll(selector)) || document.querySelectorAll(selector);
3130
3398
  if (elements.length === 0) {
3131
- console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
3399
+ diag.warn(PxDiagnosticKind.host, 'createWebApiAnimator: No elements found for selector "' + selector + '"');
3132
3400
  }
3133
3401
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
3134
3402
  const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
@@ -3177,14 +3445,14 @@ var PixodeskAnimator = (() => {
3177
3445
  }
3178
3446
  animations.push(anim);
3179
3447
  } catch (e) {
3180
- console.warn(e);
3448
+ diag.warn(PxDiagnosticKind.internal, "createWebApiAnimator: could not build the animation", e);
3181
3449
  }
3182
3450
  }
3183
3451
  }
3184
3452
  }
3185
3453
  }
3186
3454
  if (!forceEvenIfHasUnsupportedAttrs && unsupportedSet.size) {
3187
- console.warn("Unsupported CSS attrs: " + [...unsupportedSet].join(", "));
3455
+ diag.warn(PxDiagnosticKind.platform, "Unsupported CSS attrs: " + [...unsupportedSet].join(", "));
3188
3456
  return null;
3189
3457
  }
3190
3458
  const api = {
@@ -3228,20 +3496,36 @@ var PixodeskAnimator = (() => {
3228
3496
  }
3229
3497
  },
3230
3498
  "setPlaybackRate": (rate) => {
3499
+ if (!isValidPlaybackRate(rate)) {
3500
+ diag.warn(PxDiagnosticKind.usage, PX_RATE_REJECTED);
3501
+ return api;
3502
+ }
3231
3503
  animations.forEach((a) => a.playbackRate = rate);
3232
3504
  return api;
3233
3505
  },
3234
3506
  "getCurrentTime": () => {
3235
- var _a2, _b;
3236
- const res = (_b = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b : null;
3507
+ var _a2, _b2;
3508
+ const res = (_b2 = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b2 : null;
3237
3509
  return res !== null ? +res : null;
3238
3510
  },
3239
3511
  "setCurrentTime": (time) => {
3512
+ var _a2;
3513
+ const ceiling = seekCeilingMs((_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1);
3514
+ const seek = ceiling > 0 ? clampSeekMs(time, ceiling) : Math.max(0, time);
3240
3515
  finishNotified = false;
3241
3516
  animations.forEach((a) => {
3242
- a.currentTime = time;
3517
+ a.currentTime = seek;
3243
3518
  });
3244
3519
  },
3520
+ "getCurrentProgress": () => {
3521
+ var _a2;
3522
+ const t = api.getCurrentTime();
3523
+ return t === null ? null : timeToProgress(t, (_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1);
3524
+ },
3525
+ "setCurrentProgress": (progress) => {
3526
+ var _a2;
3527
+ api.setCurrentTime(progressToTimeMs(progress, (_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1));
3528
+ },
3245
3529
  "destroy": () => {
3246
3530
  var _a2;
3247
3531
  api.cancel();
@@ -3252,12 +3536,15 @@ var PixodeskAnimator = (() => {
3252
3536
  }
3253
3537
  }
3254
3538
  };
3255
- if (config.trigger) {
3256
- if (config.timelineSource === "scroll") {
3257
- console.warn("scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
3258
- } else {
3259
- setupAnimationTriggers(api, config.trigger);
3260
- }
3539
+ if (config.timelineSource === "scroll") {
3540
+ if (config.trigger) diag.warn(PxDiagnosticKind.usage, "scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
3541
+ } else {
3542
+ const detachTriggers = setupAnimationTriggers(api, (_b = config.trigger) != null ? _b : {}, diag);
3543
+ const destroyEngine = api.destroy.bind(api);
3544
+ api.destroy = () => {
3545
+ detachTriggers();
3546
+ destroyEngine();
3547
+ };
3261
3548
  }
3262
3549
  if (scrollTimeline) {
3263
3550
  animations.forEach((a) => a.play());
@@ -3273,13 +3560,14 @@ var PixodeskAnimator = (() => {
3273
3560
  if (pct === void 0) return void 0;
3274
3561
  return view ? { rangeName: (_c = point == null ? void 0 : point.phase) != null ? _c : "cover", offset: pct } : { offset: pct };
3275
3562
  }
3276
- function createNativeScrollTimeline(subject, config) {
3563
+ function createNativeScrollTimeline(subject, config, diag) {
3277
3564
  var _a, _b, _c, _d;
3565
+ const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
3278
3566
  if (!config || !isScrollTimeline(config)) return null;
3279
3567
  const scroll = config.scroll || {};
3280
3568
  const kind = (_a = scroll.kind) != null ? _a : "view";
3281
3569
  if (scroll.smoothing) {
3282
- console.warn("scroll timeline: `smoothing` needs the built-in driver \u2014 using the built-in driver instead of the browser timeline");
3570
+ report.warn(PxDiagnosticKind.platform, "scroll timeline: `smoothing` needs the built-in driver \u2014 using the built-in driver instead of the browser timeline");
3283
3571
  return null;
3284
3572
  }
3285
3573
  const g = globalThis;
@@ -3290,13 +3578,13 @@ var PixodeskAnimator = (() => {
3290
3578
  let timeline;
3291
3579
  try {
3292
3580
  if (view) {
3293
- timeline = new Ctor({ subject: resolveScrollSubject(subject, scroll.subject), axis });
3581
+ timeline = new Ctor({ subject: resolveScrollSubject(subject, scroll.subject, report), axis });
3294
3582
  } else {
3295
3583
  const source = scroll.source === "root" ? documentScroller() : findNearestScroller(subject, "y") || findNearestScroller(subject, "x") || documentScroller();
3296
3584
  timeline = new Ctor({ source, axis });
3297
3585
  }
3298
3586
  } catch (e) {
3299
- console.warn("scroll timeline: native timeline construction failed \u2014 falling back to the player measuring progress itself", e);
3587
+ report.warn(PxDiagnosticKind.platform, "scroll timeline: native timeline construction failed \u2014 falling back to the player measuring progress itself", e);
3300
3588
  return null;
3301
3589
  }
3302
3590
  return {
@@ -3323,8 +3611,9 @@ var PixodeskAnimator = (() => {
3323
3611
  }
3324
3612
  var SUBJECT_PARENT = "parent";
3325
3613
  var SUBJECT_SCROLLER = "scroller";
3326
- function resolveScrollSubject(svgRoot, subject) {
3614
+ function resolveScrollSubject(svgRoot, subject, diag) {
3327
3615
  var _a, _b;
3616
+ const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
3328
3617
  const spec = subject == null ? void 0 : subject.trim();
3329
3618
  if (!spec) return svgRoot;
3330
3619
  if (spec === SUBJECT_PARENT) {
@@ -3342,21 +3631,22 @@ var PixodeskAnimator = (() => {
3342
3631
  try {
3343
3632
  found = document.querySelector(spec);
3344
3633
  } catch (e) {
3345
- console.warn('scroll timeline: subject "' + spec + '" is not a valid selector \u2014 measuring the SVG itself');
3634
+ report.warn(PxDiagnosticKind.document, 'scroll timeline: subject "' + spec + '" is not a valid selector \u2014 measuring the SVG itself');
3346
3635
  return svgRoot;
3347
3636
  }
3348
3637
  if (!found) {
3349
- console.warn('scroll timeline: subject "' + spec + '" matched no element \u2014 measuring the SVG itself');
3638
+ report.warn(PxDiagnosticKind.host, 'scroll timeline: subject "' + spec + '" matched no element \u2014 measuring the SVG itself');
3350
3639
  return svgRoot;
3351
3640
  }
3352
3641
  return found;
3353
3642
  }
3354
- function createScrollDriver(subject, config, onProgress) {
3643
+ function createScrollDriver(subject, config, onProgress, diag) {
3355
3644
  var _a, _b;
3356
3645
  if (!config || !isScrollTimeline(config)) return null;
3646
+ const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
3357
3647
  const scroll = config.scroll || {};
3358
3648
  const kind = (_a = scroll.kind) != null ? _a : "view";
3359
- const measured = resolveScrollSubject(subject, scroll.subject);
3649
+ const measured = resolveScrollSubject(subject, scroll.subject, report);
3360
3650
  const nearest = findNearestScroller(subject, "y") || findNearestScroller(subject, "x");
3361
3651
  const scroller = kind === "scroll" && scroll.source === "root" ? documentScroller() : nearest || documentScroller();
3362
3652
  const isRootScroller = scroller === documentScroller();
@@ -3475,7 +3765,7 @@ var PixodeskAnimator = (() => {
3475
3765
  const alignFactor = scroll.pinAlign === "center" ? 0.5 : scroll.pinAlign === "bottom" ? 1 : 0;
3476
3766
  const applyTop = () => {
3477
3767
  var _a;
3478
- const extra = (_a = scroll.pinTop) != null ? _a : 0;
3768
+ const extra = (_a = scroll.pinOffset) != null ? _a : 0;
3479
3769
  if (!alignFactor) {
3480
3770
  style.top = extra + "px";
3481
3771
  return;
@@ -3537,6 +3827,7 @@ var PixodeskAnimator = (() => {
3537
3827
  }
3538
3828
  function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
3539
3829
  const animatorConfig = getAnimatorConfig(doc) || {};
3830
+ const diag = createDiagnostics(callbacks, "[PxAnimator]");
3540
3831
  if (isScrollTimeline(animatorConfig)) {
3541
3832
  return finaliseAnimator(animatorConfig, callbacks, (cb) => {
3542
3833
  var _a;
@@ -3544,7 +3835,7 @@ var PixodeskAnimator = (() => {
3544
3835
  };
3545
3836
  if (mayUseNativeScrollTimeline(animatorConfig.engine) && rootElement) {
3546
3837
  unpin = applyScrollPin(rootElement, animatorConfig.scroll);
3547
- const native = createNativeScrollTimeline(rootElement, animatorConfig);
3838
+ const native = createNativeScrollTimeline(rootElement, animatorConfig, diag);
3548
3839
  if (native) {
3549
3840
  const api2 = createWebApiAnimator(
3550
3841
  doc,
@@ -3574,7 +3865,8 @@ var PixodeskAnimator = (() => {
3574
3865
  const driver = createScrollDriver(
3575
3866
  subject,
3576
3867
  animatorConfig,
3577
- (progress) => api.setCurrentTime(progress * totalMs)
3868
+ (progress) => api.setCurrentTime(progress * totalMs),
3869
+ diag
3578
3870
  );
3579
3871
  if (driver) {
3580
3872
  const destroy = api.destroy.bind(api);
@@ -3585,7 +3877,7 @@ var PixodeskAnimator = (() => {
3585
3877
  };
3586
3878
  }
3587
3879
  } else {
3588
- console.warn("scroll timeline: no root element to observe \u2014 animation will stay at frame 0");
3880
+ diag.warn(PxDiagnosticKind.host, "scroll timeline: no root element to observe \u2014 animation will stay at frame 0");
3589
3881
  }
3590
3882
  return api;
3591
3883
  });
@@ -3602,16 +3894,26 @@ var PixodeskAnimator = (() => {
3602
3894
  ) || createFrameLoopAnimator(doc, adapter, cb, rootElement);
3603
3895
  });
3604
3896
  }
3605
- function requireData(options) {
3606
- if (!(options == null ? void 0 : options.data)) throw new Error("createAnimator: `data` is required");
3607
- return options.data;
3897
+ function requireDoc(options) {
3898
+ if (!(options == null ? void 0 : options.doc)) throw new Error("createAnimator: `doc` is required");
3899
+ return options.doc;
3900
+ }
3901
+ function buildOrReport(options, build) {
3902
+ try {
3903
+ return build();
3904
+ } catch (e) {
3905
+ const err = asThrownError(e);
3906
+ createDiagnostics(options, "[PxAnimator]").error(PxDiagnosticKind.internal, "createAnimator: could not build the player \u2014 " + err.message, err);
3907
+ return createInertAnimator();
3908
+ }
3608
3909
  }
3609
3910
  function createPrerenderedAnimator(options) {
3610
- return bindWithEngineChoice(requireData(options), options.adapter, options.callbacks, null);
3911
+ const doc = requireDoc(options);
3912
+ return buildOrReport(options, () => bindWithEngineChoice(doc, void 0, toEngineCallbacks(options), null));
3611
3913
  }
3612
3914
 
3613
3915
  // src/shared/PxAnimatorKeys.ts
3614
- var PX_ANIMATOR_DATA_KEY = "data";
3916
+ var PX_ANIMATOR_DOC_KEY = "doc";
3615
3917
  return __toCommonJS(index_prerendered_exports);
3616
3918
  })();
3617
3919
  //# sourceMappingURL=index.prerendered.umd.js.map