@pixodesk/svg-animator-web 1.0.8 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -7,6 +7,7 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __pow = Math.pow;
10
11
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
12
  var __spreadValues = (a, b) => {
12
13
  for (var prop in b || (b = {}))
@@ -50,17 +51,48 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
50
51
  var index_exports = {};
51
52
  __export(index_exports, {
52
53
  COLOUR_ATTR_NAMES: () => COLOUR_ATTR_NAMES,
54
+ PX_ANIMATOR_DATA_KEY: () => PX_ANIMATOR_DATA_KEY,
53
55
  PX_ANIM_ATTR_NAME: () => PX_ANIM_ATTR_NAME,
54
56
  PX_ANIM_SRC_ATTR_NAME: () => PX_ANIM_SRC_ATTR_NAME,
57
+ PX_TRANSFORM_PART_KEYS: () => PX_TRANSFORM_PART_KEYS,
58
+ PxAnimatedSvgDocumentSchema: () => PxAnimatedSvgDocumentSchema,
59
+ PxAnimationDefinitionSchema: () => PxAnimationDefinitionSchema,
60
+ PxAnimatorConfigSchema: () => PxAnimatorConfigSchema,
61
+ PxAttrValueSchema: () => PxAttrValueSchema,
62
+ PxBezierPathSchema: () => PxBezierPathSchema,
63
+ PxBindingSchema: () => PxBindingSchema,
64
+ PxDefsSchema: () => PxDefsSchema,
65
+ PxEasingOrRefSchema: () => PxEasingOrRefSchema,
66
+ PxEffectsSchema: () => PxEffectsSchema,
67
+ PxElementAnimationSchema: () => PxElementAnimationSchema,
68
+ PxKeyframeSchema: () => PxKeyframeSchema,
69
+ PxLoopSchema: () => PxLoopSchema,
70
+ PxMaskedByEffectSchema: () => PxMaskedByEffectSchema,
71
+ PxNodeBase: () => PxNodeBase,
72
+ PxNodeSchema: () => PxNodeSchema,
73
+ PxPropertyAnimationSchema: () => PxPropertyAnimationSchema,
74
+ PxRefEffectSchema: () => PxRefEffectSchema,
75
+ PxRepeaterEffectSchema: () => PxRepeaterEffectSchema,
76
+ PxRetimeEffectSchema: () => PxRetimeEffectSchema,
77
+ PxSvgNodeExtra: () => PxSvgNodeExtra,
78
+ PxTransformPartsSchema: () => PxTransformPartsSchema,
79
+ PxTransformValueSchema: () => PxTransformValueSchema,
80
+ PxTransformationEffectSchema: () => PxTransformationEffectSchema,
81
+ PxTriggerSchema: () => PxTriggerSchema,
82
+ PxTrimPathEffectSchema: () => PxTrimPathEffectSchema,
55
83
  STYLE_ATTR_NAMES: () => STYLE_ATTR_NAMES,
56
84
  TRANSFORM_FN_NAMES: () => TRANSFORM_FN_NAMES,
85
+ applyPlayerEffects: () => applyPlayerEffects,
57
86
  calcAnimationValues: () => calcAnimationValues,
58
87
  camelCaseToKebabWordIfNeeded: () => camelCaseToKebabWordIfNeeded,
88
+ collectSampleTimes: () => collectSampleTimes,
59
89
  createAnimator: () => createAnimator,
60
90
  createAnimatorImpl: () => createAnimatorImpl,
61
91
  createBasicFrameLoopAnimator: () => createBasicFrameLoopAnimator,
62
92
  createFrameLoopAnimator: () => createFrameLoopAnimator,
63
93
  createWebApiAnimator: () => createWebApiAnimator,
94
+ describeSchema: () => describeSchema,
95
+ diffInEffect: () => diffInEffect,
64
96
  generateNewIds: () => generateNewIds,
65
97
  getAnimatorConfig: () => getAnimatorConfig,
66
98
  getBindings: () => getBindings,
@@ -71,12 +103,1314 @@ __export(index_exports, {
71
103
  isPxElementFileFormatDeep: () => isPxElementFileFormatDeep,
72
104
  loadTagAnimators: () => loadTagAnimators,
73
105
  normalizeDocument: () => getNormalisedBindings,
106
+ px: () => px,
74
107
  renderNode: () => renderNode,
108
+ schemaKeys: () => schemaKeys,
75
109
  setupAnimationTriggers: () => setupAnimationTriggers,
76
- toRGBA: () => toRGBA
110
+ toRGBA: () => toRGBA,
111
+ validateNodeEffects: () => validateNodeEffects,
112
+ visualModelAt: () => visualModelAt
77
113
  });
78
114
  module.exports = __toCommonJS(index_exports);
79
115
 
116
+ // src/effects/transformParts.ts
117
+ function partsRecord(part, value, origin) {
118
+ const rec = {};
119
+ if (part === "translate") rec.translate = value;
120
+ else if (part === "rotate") rec.rotate = value;
121
+ else rec.scale = value;
122
+ if (origin && part !== "translate") rec.origin = origin;
123
+ return rec;
124
+ }
125
+ function readAnimatable(raw) {
126
+ if (raw === void 0) return { kind: "absent" };
127
+ if (Array.isArray(raw)) return { kind: "static", value: raw };
128
+ if (typeof raw === "object") {
129
+ const obj = raw;
130
+ if (obj.keyframes) {
131
+ return { kind: "animated", keyframes: obj.keyframes, autoOrient: obj.autoOrient };
132
+ }
133
+ if (obj.value !== void 0) return { kind: "static", value: obj.value };
134
+ }
135
+ return { kind: "static", value: raw };
136
+ }
137
+ function readStaticOrigin(raw, ctx) {
138
+ var _a;
139
+ const o = readAnimatable(raw);
140
+ if (o.kind === "absent") return void 0;
141
+ if (o.kind === "static") return o.value;
142
+ ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
143
+ return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
144
+ }
145
+ function keyframeWith(kf, value) {
146
+ const out = { value };
147
+ if (kf.time !== void 0) out.time = kf.time;
148
+ if (kf.easing !== void 0) out.easing = kf.easing;
149
+ if (kf.tangentOut !== void 0) out.tangentOut = kf.tangentOut;
150
+ if (kf.tangentIn !== void 0) out.tangentIn = kf.tangentIn;
151
+ return out;
152
+ }
153
+
154
+ // src/effects/transformationEffect.ts
155
+ function applyTransformationEffect(node, fx, ctx) {
156
+ if (!fx) return node;
157
+ delete node.transform;
158
+ let n = node;
159
+ n = wrapTransformPart(n, "skew", fx.skew, ctx);
160
+ n = wrapOrigin(
161
+ n,
162
+ fx.origin,
163
+ /*invert=*/
164
+ true
165
+ );
166
+ n = wrapTransformPart(n, "scale", normalizeScale(fx.scale), ctx);
167
+ n = wrapTransformPart(n, "rotate", fx.rotate, ctx);
168
+ n = wrapOrigin(
169
+ n,
170
+ fx.origin,
171
+ /*invert=*/
172
+ false
173
+ );
174
+ if (translateHasAutoOrient(fx.translate)) {
175
+ n = wrapOrigin(
176
+ n,
177
+ fx.origin,
178
+ /*invert=*/
179
+ true
180
+ );
181
+ n = wrapTransformPart(n, "translate", fx.translate, ctx);
182
+ n = wrapOrigin(
183
+ n,
184
+ fx.origin,
185
+ /*invert=*/
186
+ false
187
+ );
188
+ } else {
189
+ n = wrapTransformPart(n, "translate", fx.translate, ctx);
190
+ }
191
+ return n;
192
+ }
193
+ function translateHasAutoOrient(translate) {
194
+ if (!translate || typeof translate !== "object") return false;
195
+ const obj = translate;
196
+ if (obj.autoOrient) return true;
197
+ return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
198
+ }
199
+ function normalizeScale(raw) {
200
+ if (raw === void 0) return void 0;
201
+ if (Array.isArray(raw)) return [raw[0] / 100, raw[1] / 100];
202
+ return raw;
203
+ }
204
+ function wrapTransformPart(inner, part, raw, ctx) {
205
+ if (raw === void 0) return inner;
206
+ if (part === "skew") {
207
+ const skew = readAnimatable(raw);
208
+ if (skew.kind !== "static") {
209
+ ctx.warnings.push("transformation.skew: only static skew is supported");
210
+ return inner;
211
+ }
212
+ return { type: "g", transform: "skewX(" + skew.value[0] + ")skewY(" + skew.value[1] + ")", children: [inner] };
213
+ }
214
+ const v = readAnimatable(raw);
215
+ if (v.kind === "static") {
216
+ return { type: "g", transform: { value: partsRecord(part, v.value, void 0) }, children: [inner] };
217
+ }
218
+ if (v.kind === "animated") {
219
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
220
+ if (v.autoOrient) animTr.autoOrient = true;
221
+ return {
222
+ type: "g",
223
+ animate: { transform: animTr },
224
+ children: [inner]
225
+ };
226
+ }
227
+ return inner;
228
+ }
229
+ function wrapOrigin(inner, raw, invert) {
230
+ if (raw === void 0) return inner;
231
+ const v = readAnimatable(raw);
232
+ const sign = (value) => invert ? [-value[0], -value[1]] : value;
233
+ if (v.kind === "absent") return inner;
234
+ if (v.kind === "static") {
235
+ if (v.value[0] === 0 && v.value[1] === 0) return inner;
236
+ return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
237
+ }
238
+ if (v.kind === "animated") {
239
+ return {
240
+ type: "g",
241
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) } },
242
+ children: [inner]
243
+ };
244
+ }
245
+ return inner;
246
+ }
247
+
248
+ // src/effects/contentRefSplit.ts
249
+ function identifyContentRefTargets(node, ctx, allocator) {
250
+ var _a, _b, _c;
251
+ if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.ref) == null ? void 0 : _b.type) === "content") {
252
+ const baseId = node.effects.ref.baseId;
253
+ if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
254
+ ctx.contentRefInnerIds.set(baseId, allocator(baseId));
255
+ }
256
+ }
257
+ (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
258
+ }
259
+ function splitForContentRef(node, transformation, originalId, innerId, ctx) {
260
+ const outerBody = liftBodyTranslate(node, transformation);
261
+ if (typeof node.id === "string") delete node.id;
262
+ const { outer: outerTr, inner: innerTr } = splitTransformationEffect(transformation);
263
+ let innerNode = node;
264
+ innerNode = applyTransformationEffect(innerNode, innerTr, ctx);
265
+ const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
266
+ let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
267
+ if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
268
+ if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
269
+ if (outerTr) {
270
+ delete outerWrapper.id;
271
+ outerWrapper = applyTransformationEffect(outerWrapper, outerTr, ctx);
272
+ outerWrapper.id = originalId;
273
+ }
274
+ return outerWrapper;
275
+ }
276
+ function liftBodyTranslate(node, transformation) {
277
+ var _a;
278
+ const out = {};
279
+ let didLiftAnimate = false;
280
+ const animTr = (_a = node.animate) == null ? void 0 : _a.transform;
281
+ if (animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes)) {
282
+ const kfs = animTr.keyframes;
283
+ const hasTranslate = kfs.some((kf) => kf.value && kf.value.translate);
284
+ if (hasTranslate) {
285
+ const outerHasOrigin = needsOriginOnOuter(animTr);
286
+ const outerKfs = kfs.map((kf) => {
287
+ const v = kf.value || {};
288
+ const newValue = {};
289
+ if (v.translate !== void 0) newValue.translate = v.translate;
290
+ if (outerHasOrigin && v.origin !== void 0) newValue.origin = v.origin;
291
+ const outerKf = { value: newValue };
292
+ if (kf.time !== void 0) outerKf.time = kf.time;
293
+ if (kf.easing !== void 0) outerKf.easing = kf.easing;
294
+ if (kf.tangentOut !== void 0) outerKf.tangentOut = kf.tangentOut;
295
+ if (kf.tangentIn !== void 0) outerKf.tangentIn = kf.tangentIn;
296
+ return outerKf;
297
+ });
298
+ const outerAnimTr = { keyframes: outerKfs };
299
+ if (animTr.autoOrient) outerAnimTr.autoOrient = true;
300
+ out.animate = { transform: outerAnimTr };
301
+ const innerHasPivotedPart = kfs.some((kf) => {
302
+ const v = kf.value || {};
303
+ return v.rotate !== void 0 || v.scale !== void 0;
304
+ });
305
+ const innerKfs = kfs.map((kf) => {
306
+ const v = kf.value || {};
307
+ const newValue = {};
308
+ if (v.rotate !== void 0) newValue.rotate = v.rotate;
309
+ if (v.scale !== void 0) newValue.scale = v.scale;
310
+ if (v.origin !== void 0 && (!outerHasOrigin || innerHasPivotedPart)) newValue.origin = v.origin;
311
+ const innerKf = { value: newValue };
312
+ if (kf.time !== void 0) innerKf.time = kf.time;
313
+ if (kf.easing !== void 0) innerKf.easing = kf.easing;
314
+ return innerKf;
315
+ });
316
+ const allInnerEmpty = innerKfs.every((kf) => Object.keys(kf.value).length === 0);
317
+ if (allInnerEmpty) {
318
+ delete node.animate.transform;
319
+ if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
320
+ } else {
321
+ node.animate.transform = { keyframes: innerKfs };
322
+ }
323
+ didLiftAnimate = true;
324
+ }
325
+ }
326
+ const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
327
+ const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
328
+ if (typeof node.transform === "string") {
329
+ const split = splitTransformString(node.transform);
330
+ if (stripBodyTranslateOnly) {
331
+ if (split.translate !== void 0) {
332
+ if (split.rest) node.transform = split.rest;
333
+ else delete node.transform;
334
+ } else if (isPureTranslateBody(node.transform)) {
335
+ delete node.transform;
336
+ }
337
+ } else if (split.translate) {
338
+ out.transform = split.translate;
339
+ if (split.rest) node.transform = split.rest;
340
+ else delete node.transform;
341
+ }
342
+ }
343
+ return out;
344
+ }
345
+ function isPureTranslateBody(s) {
346
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
347
+ const ops = [];
348
+ let m;
349
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
350
+ if (ops.length !== 1) return false;
351
+ if (ops[0].name === "translate") return true;
352
+ if (ops[0].name !== "matrix") return false;
353
+ const args = /matrix\(([^)]*)\)/.exec(ops[0].full);
354
+ if (!args) return false;
355
+ const nums = args[1].split(/[\s,]+/).filter(Boolean).map(Number);
356
+ return nums.length >= 4 && nums[0] === 1 && nums[1] === 0 && nums[2] === 0 && nums[3] === 1;
357
+ }
358
+ function splitTransformString(s) {
359
+ const ops = [];
360
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
361
+ let m;
362
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
363
+ if (!ops.length) return { rest: s || void 0 };
364
+ if (ops.every((o) => o.name === "translate")) {
365
+ return { translate: ops.map((o) => o.full).join("") };
366
+ }
367
+ const leading = ops[0];
368
+ const trailing = ops[ops.length - 1];
369
+ if (trailing.name === "translate" && leading.name === "translate") {
370
+ const trailingVec = parseTranslateArgs(trailing.full);
371
+ const leadingVec = parseTranslateArgs(leading.full);
372
+ const ox = -trailingVec[0];
373
+ const oy = -trailingVec[1];
374
+ const userTx = leadingVec[0] - ox;
375
+ const userTy = leadingVec[1] - oy;
376
+ if (userTx === 0 && userTy === 0) return { rest: s };
377
+ const middleAndTrailing = "translate(" + ox + "," + oy + ")" + ops.slice(1).map((o) => o.full).join("");
378
+ return { translate: "translate(" + userTx + "," + userTy + ")", rest: middleAndTrailing };
379
+ }
380
+ if (trailing.name === "translate") return { rest: s };
381
+ const lifted = [];
382
+ let i = 0;
383
+ while (i < ops.length && ops[i].name === "translate") {
384
+ lifted.push(ops[i].full);
385
+ i++;
386
+ }
387
+ if (!lifted.length) return { rest: s };
388
+ const rest = ops.slice(i).map((o) => o.full).join("");
389
+ return {
390
+ translate: lifted.join(""),
391
+ rest: rest || void 0
392
+ };
393
+ }
394
+ function parseTranslateArgs(translateOp) {
395
+ const m = /translate\(([^)]*)\)/.exec(translateOp);
396
+ if (!m) return [0, 0];
397
+ const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
398
+ return [nums[0] || 0, nums[1] || 0];
399
+ }
400
+ function splitTransformationEffect(fx) {
401
+ if (!fx) return {};
402
+ const originOnOuter = needsOriginOnOuter(fx.translate);
403
+ const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
404
+ const outer = {};
405
+ const inner = {};
406
+ if (fx.translate !== void 0) outer.translate = fx.translate;
407
+ if (originOnOuter && fx.origin !== void 0) outer.origin = fx.origin;
408
+ if (fx.rotate !== void 0) inner.rotate = fx.rotate;
409
+ if (fx.scale !== void 0) inner.scale = fx.scale;
410
+ if (fx.skew !== void 0) inner.skew = fx.skew;
411
+ if (fx.origin !== void 0 && (!originOnOuter || innerHasPivotedPart)) inner.origin = fx.origin;
412
+ return {
413
+ outer: Object.keys(outer).length ? outer : void 0,
414
+ inner: Object.keys(inner).length ? inner : void 0
415
+ };
416
+ }
417
+ function needsOriginOnOuter(translateAnim) {
418
+ if (!translateAnim || typeof translateAnim !== "object") return false;
419
+ const obj = translateAnim;
420
+ if (obj.autoOrient) return true;
421
+ if (Array.isArray(obj.keyframes)) {
422
+ return obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
423
+ }
424
+ return false;
425
+ }
426
+
427
+ // src/effects/util.ts
428
+ function genId(ctx, prefix) {
429
+ return "_lw_" + prefix + "_" + ctx.nextId++;
430
+ }
431
+ function indexById(node, map) {
432
+ var _a;
433
+ if (typeof node.id === "string") map.set(node.id, node);
434
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
435
+ }
436
+ function spliceDefs(root, defs) {
437
+ if (!defs.length) return;
438
+ const existing = root.children || (root.children = []);
439
+ existing.unshift({ type: "defs", children: defs });
440
+ }
441
+ function clone(value) {
442
+ if (value === null || typeof value !== "object") return value;
443
+ if (Array.isArray(value)) return value.map(clone);
444
+ const out = {};
445
+ for (const k of Object.keys(value)) out[k] = clone(value[k]);
446
+ return out;
447
+ }
448
+ function regenerateIdsInClone(root, ctx) {
449
+ const oldToNew = /* @__PURE__ */ new Map();
450
+ const walkAssign = (n) => {
451
+ var _a;
452
+ if (typeof n.id === "string") {
453
+ const newId = genId(ctx, "retimed");
454
+ oldToNew.set(n.id, newId);
455
+ n.id = newId;
456
+ }
457
+ (_a = n.children) == null ? void 0 : _a.forEach(walkAssign);
458
+ };
459
+ walkAssign(root);
460
+ const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
461
+ const newId = oldToNew.get(oldId);
462
+ return newId ? "url(#" + newId + ")" : m;
463
+ });
464
+ const walkRewrite = (n) => {
465
+ var _a;
466
+ if (typeof n.href === "string" && n.href.startsWith("#")) {
467
+ const newId = oldToNew.get(n.href.slice(1));
468
+ if (newId) n.href = "#" + newId;
469
+ }
470
+ for (const k of Object.keys(n)) {
471
+ if (k === "children" || k === "effects" || k === "meta" || k === "href" || k === "id") continue;
472
+ const v = n[k];
473
+ if (typeof v === "string" && v.indexOf("url(#") !== -1) n[k] = rewriteUrl(v);
474
+ }
475
+ (_a = n.children) == null ? void 0 : _a.forEach(walkRewrite);
476
+ };
477
+ walkRewrite(root);
478
+ return oldToNew;
479
+ }
480
+
481
+ // src/effects/maskedByEffect.ts
482
+ function applyMaskedByEffect(node, fx, transformation, ctx) {
483
+ if (!fx) return node;
484
+ if (!fx.href) {
485
+ ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
486
+ return node;
487
+ }
488
+ const maskId = genId(ctx, "mask");
489
+ let content = { type: "use", href: "#" + fx.href };
490
+ content = wrapInverseTransform(content, transformation, ctx);
491
+ const mask = { type: "mask", id: maskId, children: [content] };
492
+ if (fx.maskType) mask.maskType = fx.maskType;
493
+ if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
494
+ if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
495
+ ctx.defs.push(mask);
496
+ node.mask = "url(#" + maskId + ")";
497
+ return node;
498
+ }
499
+ function wrapInverseTransform(inner, fx, ctx) {
500
+ if (!fx) return inner;
501
+ const origin = readStaticOrigin(fx.origin, ctx);
502
+ let n = inner;
503
+ n = wrapInversePart(n, "translate", fx.translate, void 0, ctx);
504
+ n = wrapInversePart(n, "rotate", fx.rotate, origin, ctx);
505
+ n = wrapInversePart(n, "scale", fx.scale, origin, ctx);
506
+ return n;
507
+ }
508
+ function wrapInversePart(inner, part, raw, origin, ctx) {
509
+ if (raw === void 0) return inner;
510
+ const v = readAnimatable(raw);
511
+ if (v.kind === "static") {
512
+ return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
513
+ }
514
+ if (v.kind === "animated") {
515
+ return {
516
+ type: "g",
517
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
518
+ children: [inner]
519
+ };
520
+ }
521
+ return inner;
522
+ }
523
+ function invertPartValue(part, value) {
524
+ if (part === "translate") return [-value[0], -value[1]];
525
+ if (part === "rotate") return -value;
526
+ return [1 / value[0], 1 / value[1]];
527
+ }
528
+
529
+ // src/effects/refEffect.ts
530
+ var CONTENT_SUBREF = "content";
531
+ function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
532
+ if (ref) {
533
+ const baseId = ref.baseId;
534
+ if (!baseId) {
535
+ ctx.errors.push("ref: missing baseId");
536
+ } else {
537
+ const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
538
+ node.href = "#" + targetId;
539
+ }
540
+ }
541
+ return applyTransformationEffect(node, transformation, ctx);
542
+ }
543
+
544
+ // src/effects/repeaterEffect.ts
545
+ function applyRepeaterEffect(node, fx, ctx) {
546
+ var _a, _b;
547
+ if (!fx) return node;
548
+ const copies = (_a = fx.copies) != null ? _a : 1;
549
+ if (copies < 1) {
550
+ ctx.errors.push("repeater.copies invalid: " + fx.copies);
551
+ return node;
552
+ }
553
+ const sharedTransform = node.transform;
554
+ const sharedAnimTransform = (_b = node.animate) == null ? void 0 : _b.transform;
555
+ const base = clone(node);
556
+ delete base.transform;
557
+ if (base.animate) {
558
+ delete base.animate.transform;
559
+ if (Object.keys(base.animate).length === 0) delete base.animate;
560
+ }
561
+ const children = [base];
562
+ for (let i = 1; i < copies; i++) {
563
+ const copy = clone(base);
564
+ copy.transform = { value: perCopyParts(fx, i) };
565
+ children.push(copy);
566
+ }
567
+ const wrapper = { type: "g", children };
568
+ if (sharedTransform !== void 0) wrapper.transform = sharedTransform;
569
+ if (sharedAnimTransform !== void 0) wrapper.animate = { transform: sharedAnimTransform };
570
+ return wrapper;
571
+ }
572
+ function perCopyParts(fx, i) {
573
+ const parts = {};
574
+ if (fx.translate) parts.translate = [fx.translate[0] * i, fx.translate[1] * i];
575
+ if (fx.rotate !== void 0) parts.rotate = fx.rotate * i;
576
+ if (fx.scale) parts.scale = [__pow(fx.scale[0] / 100, i), __pow(fx.scale[1] / 100, i)];
577
+ if (fx.origin) parts.origin = [fx.origin[0] * i, fx.origin[1] * i];
578
+ return parts;
579
+ }
580
+
581
+ // src/effects/retimeEffect.ts
582
+ var RETIME_AS_SYMBOL = true;
583
+ function applyRetimeEffect(node, retime, ctx) {
584
+ var _a, _b, _c, _d;
585
+ if (!retime) return node;
586
+ const baseId = retime.baseId;
587
+ if (!baseId) {
588
+ ctx.errors.push("retime: missing baseId");
589
+ return node;
590
+ }
591
+ const target = ctx.idMap.get(baseId);
592
+ if (!target) {
593
+ ctx.warnings.push('retime: target "' + baseId + '" not found');
594
+ return node;
595
+ }
596
+ const start = (_a = retime.start) != null ? _a : 0;
597
+ const stretch = (_b = retime.stretch) != null ? _b : 1;
598
+ if (RETIME_AS_SYMBOL) {
599
+ const symbolClone = clone(target);
600
+ regenerateIdsInClone(symbolClone, ctx);
601
+ const cloneId = genId(ctx, "retime");
602
+ symbolClone.id = cloneId;
603
+ remapKeyframeTimes(symbolClone, start, stretch);
604
+ ctx.defs.push(symbolClone);
605
+ node.href = "#" + cloneId;
606
+ return node;
607
+ }
608
+ const sourceNodes = target.type === "symbol" ? target.children || [] : [target];
609
+ const content = sourceNodes.map((child) => {
610
+ const c = clone(child);
611
+ regenerateIdsInClone(c, ctx);
612
+ remapKeyframeTimes(c, start, stretch);
613
+ return c;
614
+ });
615
+ const g = __spreadProps(__spreadValues({}, node), { type: "g", children: content });
616
+ delete g.href;
617
+ const x = Number((_c = node.x) != null ? _c : 0), y = Number((_d = node.y) != null ? _d : 0);
618
+ if (x || y) {
619
+ const offset = "translate(" + x + "," + y + ")";
620
+ g.transform = typeof g.transform === "string" ? offset + g.transform : offset;
621
+ delete g.x;
622
+ delete g.y;
623
+ }
624
+ return g;
625
+ }
626
+ function remapKeyframeTimes(node, start, stretch) {
627
+ var _a;
628
+ const remap2 = (kfs) => {
629
+ for (const kf of kfs) if (typeof kf.time === "number") kf.time = start + kf.time * stretch;
630
+ };
631
+ if (node.transform && typeof node.transform === "object" && Array.isArray(node.transform.keyframes)) {
632
+ remap2(node.transform.keyframes);
633
+ }
634
+ if (node.animate && typeof node.animate === "object") {
635
+ for (const prop of Object.keys(node.animate)) {
636
+ const anim = node.animate[prop];
637
+ if (anim && Array.isArray(anim.keyframes)) remap2(anim.keyframes);
638
+ }
639
+ }
640
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => remapKeyframeTimes(child, start, stretch));
641
+ }
642
+
643
+ // src/effects/trimPathEffect.ts
644
+ function applyTrimPathEffect(node, trimPath, isCombinedShape) {
645
+ var _a;
646
+ if (!trimPath && !isCombinedShape) return node;
647
+ const d = (_a = node.d) != null ? _a : rectToPathD(node);
648
+ if (d === void 0) return node;
649
+ const g = __spreadProps(__spreadValues({}, node), { type: "g" });
650
+ delete g.d;
651
+ const meta = g.meta = __spreadValues({}, g.meta || {});
652
+ const effects = meta.effects = __spreadValues({}, meta.effects || {});
653
+ if (trimPath) effects.trimPath = trimPath;
654
+ effects.isCombinedShape = true;
655
+ g.children = [{ type: "path", d }];
656
+ return g;
657
+ }
658
+ function rectToPathD(node) {
659
+ var _a, _b, _c, _d;
660
+ if (node.type !== "rect") return void 0;
661
+ const x = Number((_a = node.x) != null ? _a : 0), y = Number((_b = node.y) != null ? _b : 0);
662
+ const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
663
+ return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
664
+ }
665
+
666
+ // src/effects/PlayerEffectsUtil.ts
667
+ function applyPlayerEffects(root) {
668
+ const ctx = {
669
+ defs: [],
670
+ warnings: [],
671
+ errors: [],
672
+ idMap: /* @__PURE__ */ new Map(),
673
+ nextId: 0,
674
+ contentRefInnerIds: /* @__PURE__ */ new Map()
675
+ };
676
+ const working = clone(root);
677
+ indexById(working, ctx.idMap);
678
+ identifyContentRefTargets(working, ctx, () => genId(ctx, "inner"));
679
+ const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);
680
+ const out = applyPlayerEffects_retime(afterPass1, ctx);
681
+ spliceDefs(out, ctx.defs);
682
+ return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
683
+ }
684
+ function applyPlayerEffects_exceptRetime(node, ctx) {
685
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
686
+ const fx = node.effects;
687
+ const originalId = typeof node.id === "string" ? node.id : void 0;
688
+ const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
689
+ if (!fx && !innerIdForContentRef) return node;
690
+ const { transformation, repeater, maskedBy, trimPath, retime, ref } = fx != null ? fx : {};
691
+ const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
692
+ if (fx) delete node.effects;
693
+ let n = node;
694
+ n = applyTrimPathEffect(n, trimPath, isCombinedShape);
695
+ n = applyRepeaterEffect(n, repeater, ctx);
696
+ n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
697
+ if (innerIdForContentRef) {
698
+ n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
699
+ } else {
700
+ n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
701
+ }
702
+ if (retime) node.effects = { retime };
703
+ if (originalId) ctx.idMap.set(originalId, n);
704
+ return n;
705
+ }
706
+ function applyPlayerEffects_retime(node, ctx) {
707
+ var _a;
708
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_retime(child, ctx));
709
+ const retime = (_a = node.effects) == null ? void 0 : _a.retime;
710
+ if (!retime) return node;
711
+ delete node.effects;
712
+ return applyRetimeEffect(node, retime, ctx);
713
+ }
714
+
715
+ // src/PxSchema.ts
716
+ function pathStr(path) {
717
+ if (!path.length) return ".";
718
+ let result = "";
719
+ for (const seg of path) {
720
+ if (seg.startsWith("[")) result += seg;
721
+ else result += (result ? "." : "") + seg;
722
+ }
723
+ return result;
724
+ }
725
+ var Base = class {
726
+ _canSanitize(raw) {
727
+ return this.isValid(raw);
728
+ }
729
+ optional() {
730
+ return new Optional(this);
731
+ }
732
+ };
733
+ var Optional = class extends Base {
734
+ constructor(inner) {
735
+ super();
736
+ this.inner = inner;
737
+ this._default = void 0;
738
+ }
739
+ sanitize(raw) {
740
+ if (raw === void 0 || raw === null) return void 0;
741
+ return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
742
+ }
743
+ isValid(raw, ctx, path) {
744
+ if (raw === void 0 || raw === null) return true;
745
+ return this.inner.isValid(raw, ctx, path);
746
+ }
747
+ _canSanitize(raw) {
748
+ return raw === void 0 || raw === null || this.inner._canSanitize(raw);
749
+ }
750
+ };
751
+ var Str = class extends Base {
752
+ constructor(_default = "") {
753
+ super();
754
+ this._default = _default;
755
+ }
756
+ sanitize(raw) {
757
+ return typeof raw === "string" ? raw : this._default;
758
+ }
759
+ isValid(raw, ctx, path) {
760
+ if (typeof raw === "string") return true;
761
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
762
+ return false;
763
+ }
764
+ };
765
+ var Num = class extends Base {
766
+ constructor(_default = 0) {
767
+ super();
768
+ this._default = _default;
769
+ }
770
+ sanitize(raw) {
771
+ return typeof raw === "number" && isFinite(raw) ? raw : this._default;
772
+ }
773
+ isValid(raw, ctx, path) {
774
+ if (typeof raw === "number" && isFinite(raw)) return true;
775
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
776
+ return false;
777
+ }
778
+ };
779
+ var Bool = class extends Base {
780
+ constructor(_default = false) {
781
+ super();
782
+ this._default = _default;
783
+ }
784
+ sanitize(raw) {
785
+ return typeof raw === "boolean" ? raw : this._default;
786
+ }
787
+ isValid(raw, ctx, path) {
788
+ if (typeof raw === "boolean") return true;
789
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
790
+ return false;
791
+ }
792
+ };
793
+ var Literal = class extends Base {
794
+ constructor(value) {
795
+ super();
796
+ this.value = value;
797
+ this._default = value;
798
+ }
799
+ sanitize(raw) {
800
+ return raw === this.value ? this.value : this._default;
801
+ }
802
+ isValid(raw, ctx, path) {
803
+ if (raw === this.value) return true;
804
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
805
+ return false;
806
+ }
807
+ };
808
+ var Enum = class extends Base {
809
+ constructor(values, defaultVal) {
810
+ super();
811
+ this.values = values;
812
+ this._default = defaultVal != null ? defaultVal : values[0];
813
+ }
814
+ sanitize(raw) {
815
+ return this.values.includes(raw) ? raw : this._default;
816
+ }
817
+ isValid(raw, ctx, path) {
818
+ if (this.values.includes(raw)) return true;
819
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected one of " + this.values.map((v) => JSON.stringify(v)).join(" | ") + ", got " + JSON.stringify(raw));
820
+ return false;
821
+ }
822
+ };
823
+ var Union = class extends Base {
824
+ constructor(schemas, defaultVal) {
825
+ super();
826
+ this.schemas = schemas;
827
+ this._default = defaultVal != null ? defaultVal : schemas[0]._default;
828
+ }
829
+ sanitize(raw) {
830
+ for (const s of this.schemas) {
831
+ if (s.isValid(raw)) return s.sanitize(raw);
832
+ }
833
+ return this._default;
834
+ }
835
+ isValid(raw, ctx, path) {
836
+ var _a;
837
+ if (this.schemas.some((s) => s.isValid(raw))) return true;
838
+ 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, 60));
839
+ return false;
840
+ }
841
+ _canSanitize(raw) {
842
+ return this.schemas.some((s) => s._canSanitize(raw));
843
+ }
844
+ };
845
+ var DiscriminatedUnion = class extends Base {
846
+ constructor(_key, _schemas, defaultVal) {
847
+ super();
848
+ this._key = _key;
849
+ this._schemas = _schemas;
850
+ this._default = defaultVal != null ? defaultVal : _schemas[0]._default;
851
+ this._map = /* @__PURE__ */ new Map();
852
+ for (const s of _schemas) {
853
+ const keySchema = s._shape[_key];
854
+ if (keySchema) this._map.set(keySchema._default, s);
855
+ }
856
+ }
857
+ _findSchema(raw) {
858
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
859
+ const val = raw[this._key];
860
+ if (val === void 0 || val === null) return void 0;
861
+ return this._map.get(val);
862
+ }
863
+ sanitize(raw) {
864
+ var _a;
865
+ return ((_a = this._findSchema(raw)) != null ? _a : this._schemas[0]).sanitize(raw);
866
+ }
867
+ isValid(raw, ctx, path) {
868
+ const schema = this._findSchema(raw);
869
+ if (!schema) {
870
+ const val = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw[this._key] : void 0;
871
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no discriminated union member matched " + this._key + "=" + JSON.stringify(val));
872
+ return false;
873
+ }
874
+ return schema.isValid(raw, ctx, path);
875
+ }
876
+ _canSanitize(raw) {
877
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return false;
878
+ const schema = this._findSchema(raw);
879
+ return schema ? schema._canSanitize(raw) : this._schemas[0]._canSanitize(raw);
880
+ }
881
+ };
882
+ var Obj = class extends Base {
883
+ constructor(_shape) {
884
+ super();
885
+ this._shape = _shape;
886
+ const d = {};
887
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
888
+ this._default = d;
889
+ }
890
+ sanitize(raw) {
891
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
892
+ const out = {};
893
+ for (const key of Object.keys(this._shape)) {
894
+ out[key] = this._shape[key].sanitize(src[key]);
895
+ }
896
+ return out;
897
+ }
898
+ isValid(raw, ctx, path) {
899
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
900
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
901
+ return false;
902
+ }
903
+ const obj = raw;
904
+ const p = path != null ? path : [];
905
+ let ok = true;
906
+ for (const key of Object.keys(this._shape)) {
907
+ p.push(key);
908
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
909
+ p.pop();
910
+ }
911
+ if (ctx == null ? void 0 : ctx.strict) {
912
+ for (const key of Object.keys(obj)) {
913
+ if (key in this._shape) continue;
914
+ p.push(key);
915
+ ctx.errors.push(pathStr(p) + ": unexpected extra key");
916
+ p.pop();
917
+ ok = false;
918
+ }
919
+ }
920
+ return ok;
921
+ }
922
+ _canSanitize(raw) {
923
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
924
+ }
925
+ };
926
+ var OpenObj = class extends Base {
927
+ constructor(_shape, _openSchema) {
928
+ super();
929
+ this._shape = _shape;
930
+ this._openSchema = _openSchema;
931
+ const d = {};
932
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
933
+ this._default = d;
934
+ }
935
+ sanitize(raw) {
936
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
937
+ const out = __spreadValues({}, src);
938
+ for (const key of Object.keys(this._shape)) {
939
+ out[key] = this._shape[key].sanitize(src[key]);
940
+ }
941
+ if (this._openSchema) {
942
+ for (const key of Object.keys(src)) {
943
+ if (!(key in this._shape)) out[key] = this._openSchema.sanitize(src[key]);
944
+ }
945
+ }
946
+ return out;
947
+ }
948
+ isValid(raw, ctx, path) {
949
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
950
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
951
+ return false;
952
+ }
953
+ const obj = raw;
954
+ const p = path != null ? path : [];
955
+ let ok = true;
956
+ for (const key of Object.keys(this._shape)) {
957
+ p.push(key);
958
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
959
+ p.pop();
960
+ }
961
+ if (this._openSchema) {
962
+ for (const key of Object.keys(obj)) {
963
+ if (key in this._shape) continue;
964
+ p.push(key);
965
+ if (!this._openSchema.isValid(obj[key], ctx, p)) ok = false;
966
+ p.pop();
967
+ }
968
+ }
969
+ return ok;
970
+ }
971
+ _canSanitize(raw) {
972
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
973
+ }
974
+ };
975
+ var Arr = class extends Base {
976
+ constructor(item) {
977
+ super();
978
+ this.item = item;
979
+ this._default = [];
980
+ }
981
+ sanitize(raw) {
982
+ if (!Array.isArray(raw)) return [];
983
+ const out = [];
984
+ for (const el of raw) {
985
+ if (this.item._canSanitize(el)) out.push(this.item.sanitize(el));
986
+ }
987
+ return out;
988
+ }
989
+ isValid(raw, ctx, path) {
990
+ if (!Array.isArray(raw)) {
991
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected array, got " + typeof raw);
992
+ return false;
993
+ }
994
+ const p = path != null ? path : [];
995
+ let ok = true;
996
+ for (let i = 0; i < raw.length; i++) {
997
+ p.push("[" + i + "]");
998
+ if (!this.item.isValid(raw[i], ctx, p)) ok = false;
999
+ p.pop();
1000
+ }
1001
+ return ok;
1002
+ }
1003
+ _canSanitize(raw) {
1004
+ return Array.isArray(raw);
1005
+ }
1006
+ };
1007
+ var Rec = class extends Base {
1008
+ constructor(value) {
1009
+ super();
1010
+ this.value = value;
1011
+ this._default = {};
1012
+ }
1013
+ sanitize(raw) {
1014
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
1015
+ const out = {};
1016
+ for (const [k, v] of Object.entries(raw)) {
1017
+ if (this.value._canSanitize(v)) out[k] = this.value.sanitize(v);
1018
+ }
1019
+ return out;
1020
+ }
1021
+ isValid(raw, ctx, path) {
1022
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
1023
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object/record, got " + (Array.isArray(raw) ? "array" : typeof raw));
1024
+ return false;
1025
+ }
1026
+ const p = path != null ? path : [];
1027
+ let ok = true;
1028
+ for (const [k, v] of Object.entries(raw)) {
1029
+ p.push(k);
1030
+ if (!this.value.isValid(v, ctx, p)) ok = false;
1031
+ p.pop();
1032
+ }
1033
+ return ok;
1034
+ }
1035
+ _canSanitize(raw) {
1036
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
1037
+ }
1038
+ };
1039
+ var Any = class extends Base {
1040
+ constructor() {
1041
+ super(...arguments);
1042
+ this._default = void 0;
1043
+ }
1044
+ sanitize(raw) {
1045
+ return raw;
1046
+ }
1047
+ isValid(_raw, _ctx, _path) {
1048
+ return true;
1049
+ }
1050
+ _canSanitize(_raw) {
1051
+ return true;
1052
+ }
1053
+ };
1054
+ var Lazy = class extends Base {
1055
+ constructor(fn, _default) {
1056
+ super();
1057
+ this.fn = fn;
1058
+ this._default = _default;
1059
+ this.resolved = null;
1060
+ }
1061
+ get schema() {
1062
+ var _a;
1063
+ return (_a = this.resolved) != null ? _a : this.resolved = this.fn();
1064
+ }
1065
+ sanitize(raw) {
1066
+ return this.schema.sanitize(raw);
1067
+ }
1068
+ isValid(raw, ctx, path) {
1069
+ return this.schema.isValid(raw, ctx, path);
1070
+ }
1071
+ _canSanitize(raw) {
1072
+ return this.schema._canSanitize(raw);
1073
+ }
1074
+ };
1075
+ var Tuple = class extends Base {
1076
+ constructor(schemas) {
1077
+ super();
1078
+ this.schemas = schemas;
1079
+ this._default = schemas.map((s) => s._default);
1080
+ }
1081
+ sanitize(raw) {
1082
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) return this._default;
1083
+ return this.schemas.map((s, i) => s.sanitize(raw[i]));
1084
+ }
1085
+ isValid(raw, ctx, path) {
1086
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) {
1087
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected tuple of length " + this.schemas.length + ", got " + (Array.isArray(raw) ? "array[" + raw.length + "]" : typeof raw));
1088
+ return false;
1089
+ }
1090
+ const p = path != null ? path : [];
1091
+ let ok = true;
1092
+ for (let i = 0; i < this.schemas.length; i++) {
1093
+ p.push("[" + i + "]");
1094
+ if (!this.schemas[i].isValid(raw[i], ctx, p)) ok = false;
1095
+ p.pop();
1096
+ }
1097
+ return ok;
1098
+ }
1099
+ // Require exact length so wrong-length arrays are dropped rather than repaired to default.
1100
+ _canSanitize(raw) {
1101
+ return Array.isArray(raw) && raw.length === this.schemas.length;
1102
+ }
1103
+ };
1104
+ function implementsInterface() {
1105
+ return (schema) => schema;
1106
+ }
1107
+ function schemaKeys(schema) {
1108
+ return Object.fromEntries(
1109
+ Object.keys(schema["_shape"]).map((k) => [k, k])
1110
+ );
1111
+ }
1112
+ function describeSchema(schema) {
1113
+ var _a;
1114
+ const s = schema;
1115
+ if ("_shape" in s) return { kind: "shape", shape: s._shape };
1116
+ if ("item" in s) return { kind: "array", item: s.item };
1117
+ if ("inner" in s) return { kind: "optional", inner: s.inner };
1118
+ if ("fn" in s) return { kind: "lazy", resolved: (_a = s.resolved) != null ? _a : s.fn() };
1119
+ return { kind: "leaf" };
1120
+ }
1121
+ var px = {
1122
+ /** Matches a string. Default: '' or provided value. */
1123
+ string: (defaultVal = "") => new Str(defaultVal),
1124
+ /** Matches a finite number. Default: 0 or provided value. */
1125
+ number: (defaultVal = 0) => new Num(defaultVal),
1126
+ /** Matches a boolean. Default: false or provided value. */
1127
+ boolean: (defaultVal = false) => new Bool(defaultVal),
1128
+ /** Matches one exact primitive value; its default is the value itself. */
1129
+ literal: (value) => new Literal(value),
1130
+ /** Matches one of a fixed set of string/number values. Default: first value. */
1131
+ enum: (values, defaultVal) => new Enum(values, defaultVal),
1132
+ /**
1133
+ * Returns the first schema whose isValid passes.
1134
+ * TypeScript infers the union of all member types automatically.
1135
+ */
1136
+ union: (schemas, defaultVal) => new Union(schemas, defaultVal),
1137
+ /**
1138
+ * Discriminated union — reads `raw[key]`, finds the member schema whose
1139
+ * literal at `key` matches, then delegates sanitize/isValid to that member.
1140
+ * Each member must be an object schema with a `px.literal(...)` at `key`.
1141
+ * TypeScript infers the union of all member types automatically.
1142
+ */
1143
+ discriminatedUnion: (key, schemas) => new DiscriminatedUnion(key, schemas),
1144
+ /** Typed object — unknown keys are stripped. Required fields fall back to their default. */
1145
+ object: (shape) => new Obj(shape),
1146
+ /**
1147
+ * Open object — validates known keys; passes unknown keys through as-is,
1148
+ * or validates/sanitizes them against `openSchema` when provided.
1149
+ */
1150
+ openObject: (shape, openSchema) => new OpenObj(shape, openSchema),
1151
+ /**
1152
+ * Creates a new closed object schema by merging a base schema's shape with additional fields.
1153
+ * The base can be the result of px.object() or px.openObject() — anything with a _shape property.
1154
+ *
1155
+ * @example
1156
+ * const PxSvgNodeSchema = px.extendedObject(PxNodeBase, { width: px.number().optional() });
1157
+ */
1158
+ extendedObject: (base, extra) => new Obj(__spreadValues(__spreadValues({}, base._shape), extra)),
1159
+ /** Array whose unrecoverable items are filtered out. Default: []. */
1160
+ array: (item) => new Arr(item),
1161
+ /** String-keyed record whose unrecoverable values are dropped. Default: {}. */
1162
+ record: (value) => new Rec(value),
1163
+ /** Passes anything through unchanged — always valid. */
1164
+ any: () => new Any(),
1165
+ /** Fixed-length tuple — validates element count and each position individually. */
1166
+ tuple: (schemas) => new Tuple(schemas),
1167
+ /** Defers schema creation — required for recursive types. Must supply a default value. */
1168
+ lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
1169
+ };
1170
+
1171
+ // src/PxAnimatorTypes.ts
1172
+ var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
1173
+ var PX_ANIM_ATTR_NAME = "_px_animator";
1174
+ var TEXT_ATTR = "text";
1175
+ var TEXT_CONTENT_ATTR = "textContent";
1176
+ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
1177
+ "type",
1178
+ "children",
1179
+ "animator",
1180
+ "meta",
1181
+ "animate",
1182
+ TEXT_ATTR,
1183
+ TEXT_CONTENT_ATTR
1184
+ ]);
1185
+ var PxEasingOrRefSchema = px.union([
1186
+ px.string(),
1187
+ px.tuple([px.number(), px.number(), px.number(), px.number()])
1188
+ ]);
1189
+ var PxKeyframeValueSchema = implementsInterface()(px.union([
1190
+ px.string(),
1191
+ // e.g. for colors
1192
+ px.number(),
1193
+ px.array(px.number()),
1194
+ px.lazy(() => PxTransformPartsSchema, {}),
1195
+ px.object({ path: px.string() }),
1196
+ px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] })
1197
+ ]));
1198
+ var PxKeyframeSchema = implementsInterface()(px.object({
1199
+ time: px.number().optional(),
1200
+ t: px.number().optional(),
1201
+ value: px.any().optional(),
1202
+ v: px.any().optional(),
1203
+ easing: PxEasingOrRefSchema.optional(),
1204
+ e: PxEasingOrRefSchema.optional(),
1205
+ tangentOut: px.tuple([px.number(), px.number()]).optional(),
1206
+ to: px.tuple([px.number(), px.number()]).optional(),
1207
+ // short alias
1208
+ tangentIn: px.tuple([px.number(), px.number()]).optional(),
1209
+ ti: px.tuple([px.number(), px.number()]).optional(),
1210
+ // short alias
1211
+ selected: px.boolean().optional()
1212
+ // editor-side UI state (Player ignores it)
1213
+ }));
1214
+ var PxLoopSchema = implementsInterface()(px.object({
1215
+ segmentCount: px.number().optional(),
1216
+ before: px.boolean().optional(),
1217
+ alternate: px.boolean().optional()
1218
+ }));
1219
+ var PxPropertyAnimationSchema = implementsInterface()(px.object({
1220
+ keyframes: px.array(PxKeyframeSchema).optional(),
1221
+ kfs: px.array(PxKeyframeSchema).optional(),
1222
+ loop: px.union([PxLoopSchema, px.boolean()]).optional(),
1223
+ autoOrient: px.boolean().optional()
1224
+ }));
1225
+ var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
1226
+ var PxTransformPartsSchema = implementsInterface()(px.object({
1227
+ translate: px.tuple([px.number(), px.number()]).optional(),
1228
+ rotate: px.number().optional(),
1229
+ scale: px.tuple([px.number(), px.number()]).optional(),
1230
+ origin: px.tuple([px.number(), px.number()]).optional()
1231
+ }));
1232
+ var PxTransformValueSchema = px.union([
1233
+ px.string(),
1234
+ px.object({ value: PxTransformPartsSchema }),
1235
+ PxPropertyAnimationSchema
1236
+ ]);
1237
+ var PxAnimationDefinitionSchema = implementsInterface()(
1238
+ px.record(PxPropertyAnimationSchema)
1239
+ );
1240
+ var PxElementAnimationSchema = implementsInterface()(px.union([
1241
+ px.string(),
1242
+ px.array(px.union([px.string(), PxAnimationDefinitionSchema])),
1243
+ PxAnimationDefinitionSchema
1244
+ ]));
1245
+ var PxTriggerSchema = implementsInterface()(px.object({
1246
+ startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
1247
+ outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
1248
+ scrollIntoViewThreshold: px.number().optional()
1249
+ }));
1250
+ var PxDefsSchema = implementsInterface()(px.object({
1251
+ easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
1252
+ animations: px.record(PxAnimationDefinitionSchema).optional(),
1253
+ styles: px.record(px.any()).optional()
1254
+ }));
1255
+ var PxAnimatorConfigSchema = implementsInterface()(px.object({
1256
+ mode: px.enum(["auto", "webapi", "frames"]).optional(),
1257
+ duration: px.number().optional(),
1258
+ delay: px.number().optional(),
1259
+ iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1260
+ fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
1261
+ direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
1262
+ frameRate: px.number().optional(),
1263
+ trigger: PxTriggerSchema.optional(),
1264
+ definitions: PxDefsSchema.optional(),
1265
+ animate: px.record(PxElementAnimationSchema).optional(),
1266
+ debug: px.boolean().optional(),
1267
+ debugInstName: px.string().optional()
1268
+ }));
1269
+ var PxBindingSchema = implementsInterface()(px.object({
1270
+ id: px.string(),
1271
+ animate: PxElementAnimationSchema
1272
+ }));
1273
+ var PxAttrValueSchema = px.union([
1274
+ px.string(),
1275
+ px.number(),
1276
+ px.object({ value: px.any() }),
1277
+ PxPropertyAnimationSchema
1278
+ ]);
1279
+ var PxAnimatableNumberSchema = px.union([
1280
+ px.number(),
1281
+ px.object({ value: px.number() }),
1282
+ px.object({
1283
+ keyframes: px.array(PxKeyframeSchema),
1284
+ autoOrient: px.boolean().optional()
1285
+ })
1286
+ ]);
1287
+ var PxAnimatableVec2Schema = px.union([
1288
+ px.tuple([px.number(), px.number()]),
1289
+ px.object({ value: px.tuple([px.number(), px.number()]) }),
1290
+ px.object({
1291
+ keyframes: px.array(PxKeyframeSchema),
1292
+ autoOrient: px.boolean().optional()
1293
+ })
1294
+ ]);
1295
+ var PxTransformationEffectSchema = px.object({
1296
+ translate: PxAnimatableVec2Schema.optional(),
1297
+ rotate: PxAnimatableNumberSchema.optional(),
1298
+ scale: PxAnimatableVec2Schema.optional(),
1299
+ skew: PxAnimatableVec2Schema.optional(),
1300
+ origin: PxAnimatableVec2Schema.optional()
1301
+ });
1302
+ var PxRepeaterEffectSchema = px.object({
1303
+ copies: px.number().optional(),
1304
+ translate: px.tuple([px.number(), px.number()]).optional(),
1305
+ rotate: px.number().optional(),
1306
+ scale: px.tuple([px.number(), px.number()]).optional(),
1307
+ // per-copy scale, PERCENT (85 → 0.85)
1308
+ origin: px.tuple([px.number(), px.number()]).optional()
1309
+ });
1310
+ var PxMaskedByEffectSchema = px.object({
1311
+ href: px.string().optional(),
1312
+ maskType: px.string().optional(),
1313
+ maskUnits: px.string().optional(),
1314
+ maskContentUnits: px.string().optional()
1315
+ });
1316
+ var PxTrimPathEffectSchema = px.any();
1317
+ var PxRetimeEffectSchema = px.object({
1318
+ baseId: px.string().optional(),
1319
+ start: px.number().optional(),
1320
+ stretch: px.number().optional(),
1321
+ timeCrop: px.tuple([px.number(), px.number()]).optional()
1322
+ });
1323
+ var PxRefEffectSchema = px.object({
1324
+ baseId: px.string().optional(),
1325
+ type: px.string().optional()
1326
+ });
1327
+ var PxEffectsSchema = px.object({
1328
+ transformation: PxTransformationEffectSchema.optional(),
1329
+ repeater: PxRepeaterEffectSchema.optional(),
1330
+ maskedBy: PxMaskedByEffectSchema.optional(),
1331
+ trimPath: PxTrimPathEffectSchema.optional(),
1332
+ retime: PxRetimeEffectSchema.optional(),
1333
+ isCombinedShape: px.boolean().optional(),
1334
+ ref: PxRefEffectSchema.optional()
1335
+ });
1336
+ function validateNodeEffects(root, opts) {
1337
+ const warnings = [];
1338
+ const walk = (node, path) => {
1339
+ if (node && node.effects) {
1340
+ const ctx = { errors: [], warnings: [], strict: !!(opts == null ? void 0 : opts.strict) };
1341
+ const ok = PxEffectsSchema.isValid(node.effects, ctx, [path + ".effects"]);
1342
+ if (!ok) {
1343
+ for (const err of ctx.errors) warnings.push(err);
1344
+ }
1345
+ }
1346
+ if (node && Array.isArray(node.children)) {
1347
+ node.children.forEach((c, i) => walk(c, path + ".children[" + i + "]"));
1348
+ }
1349
+ };
1350
+ walk(root, "root");
1351
+ return warnings;
1352
+ }
1353
+ var PxNodeBase = px.openObject({
1354
+ type: px.string(),
1355
+ id: px.string().optional(),
1356
+ meta: px.any().optional(),
1357
+ // Player-effects bucket emitted by the Editor's lightweight design format.
1358
+ // Consumed and removed by `applyPlayerEffects` before any other normalisation
1359
+ // (see `createAnimatorImpl`), so downstream code never sees it.
1360
+ effects: PxEffectsSchema.optional(),
1361
+ animate: PxAnimationDefinitionSchema.optional(),
1362
+ style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
1363
+ }, PxAttrValueSchema);
1364
+ var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
1365
+ children: px.lazy(() => px.array(PxNodeSchema), []).optional()
1366
+ }), PxAttrValueSchema);
1367
+ var PxSvgNodeExtra = px.object({
1368
+ width: px.number().optional(),
1369
+ height: px.number().optional(),
1370
+ viewBox: px.string().optional(),
1371
+ animator: PxAnimatorConfigSchema.optional()
1372
+ });
1373
+ var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
1374
+ type: px.literal("svg"),
1375
+ // override string → literal to require 'svg'
1376
+ children: px.array(PxNodeSchema).optional()
1377
+ }), PxAttrValueSchema);
1378
+ var PxBezierPathSchema = implementsInterface()(px.object({
1379
+ v: px.array(px.array(px.number())),
1380
+ i: px.array(px.array(px.number())).optional(),
1381
+ o: px.array(px.array(px.number())).optional(),
1382
+ c: px.boolean().optional()
1383
+ }));
1384
+ function isPxElementFileFormat(fileJson) {
1385
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
1386
+ return false;
1387
+ }
1388
+ return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
1389
+ }
1390
+ function isPxElementFileFormatDeep(fileJson) {
1391
+ const valid = PxAnimatedSvgDocumentSchema.isValid(fileJson);
1392
+ return { valid, errors: valid ? [] : ["Document failed schema validation"] };
1393
+ }
1394
+ function getAnimatorConfig(doc) {
1395
+ var _a, _b;
1396
+ return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
1397
+ }
1398
+ function getDefs(doc) {
1399
+ var _a;
1400
+ if (!doc) return void 0;
1401
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
1402
+ }
1403
+ function getBindings(doc) {
1404
+ var _a;
1405
+ if (!doc) return void 0;
1406
+ const animate = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animate;
1407
+ if (!animate) return void 0;
1408
+ return Object.entries(animate).map(([id, anim]) => ({ id, animate: anim }));
1409
+ }
1410
+ function getChildren(doc) {
1411
+ return doc == null ? void 0 : doc.children;
1412
+ }
1413
+
80
1414
  // src/PxAnimatorUtil.ts
81
1415
  function bezierToSvgPath(path) {
82
1416
  var _a, _b, _c, _d;
@@ -166,50 +1500,104 @@ function remap(value, inMin, inMax, outMin, outMax) {
166
1500
  const t = (value - inMin) / (inMax - inMin);
167
1501
  return outMin + t * (outMax - outMin);
168
1502
  }
169
- function cubicBezier(easing) {
170
- const [p1x, p1y, p2x, p2y] = easing;
1503
+ function solveCubicBezierX(p1x, p2x, x) {
1504
+ if (x <= 0) return 0;
1505
+ if (x >= 1) return 1;
171
1506
  const cx = 3 * p1x;
172
1507
  const bx = 3 * (p2x - p1x) - cx;
173
1508
  const ax = 1 - cx - bx;
1509
+ function sampleX(t) {
1510
+ return ((ax * t + bx) * t + cx) * t;
1511
+ }
1512
+ function sampleDX(t) {
1513
+ return (3 * ax * t + 2 * bx) * t + cx;
1514
+ }
1515
+ let t2 = x;
1516
+ let t0 = 0;
1517
+ let t1 = 1;
1518
+ for (let i = 0; i < 8; i++) {
1519
+ const x2 = sampleX(t2) - x;
1520
+ if (Math.abs(x2) < 1e-6) return t2;
1521
+ const d2 = sampleDX(t2);
1522
+ if (Math.abs(d2) < 1e-6) break;
1523
+ t2 -= x2 / d2;
1524
+ }
1525
+ t2 = x;
1526
+ while (t0 < t1) {
1527
+ const x2 = sampleX(t2);
1528
+ if (Math.abs(x2 - x) < 1e-6) return t2;
1529
+ if (x > x2) t0 = t2;
1530
+ else t1 = t2;
1531
+ t2 = (t1 + t0) / 2;
1532
+ }
1533
+ return t2;
1534
+ }
1535
+ function cubicBezier(easing) {
1536
+ const [p1x, p1y, p2x, p2y] = easing;
174
1537
  const cy = 3 * p1y;
175
1538
  const by = 3 * (p2y - p1y) - cy;
176
1539
  const ay = 1 - cy - by;
177
- function sampleCurveX(t) {
178
- return ((ax * t + bx) * t + cx) * t;
179
- }
180
1540
  function sampleCurveY(t) {
181
1541
  return ((ay * t + by) * t + cy) * t;
182
1542
  }
183
- function sampleCurveDerivativeX(t) {
184
- return (3 * ax * t + 2 * bx) * t + cx;
185
- }
186
- function solveCurveX(x) {
187
- if (x <= 0) return 0;
188
- if (x >= 1) return 1;
189
- let t2 = x;
190
- let t0 = 0;
191
- let t1 = 1;
192
- for (let i = 0; i < 8; i++) {
193
- const x2 = sampleCurveX(t2) - x;
194
- if (Math.abs(x2) < 1e-6) return t2;
195
- const d2 = sampleCurveDerivativeX(t2);
196
- if (Math.abs(d2) < 1e-6) break;
197
- t2 -= x2 / d2;
198
- }
199
- t2 = x;
200
- while (t0 < t1) {
201
- const x2 = sampleCurveX(t2);
202
- if (Math.abs(x2 - x) < 1e-6) return t2;
203
- if (x > x2) t0 = t2;
204
- else t1 = t2;
205
- t2 = (t1 + t0) / 2;
206
- }
207
- return t2;
208
- }
209
1543
  return function(x) {
210
- return sampleCurveY(solveCurveX(x));
1544
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
211
1545
  };
212
1546
  }
1547
+ function lerp2(a, b, t) {
1548
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
1549
+ }
1550
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
1551
+ const q0 = lerp2(p0, p1, t);
1552
+ const q1 = lerp2(p1, p2, t);
1553
+ const q2 = lerp2(p2, p3, t);
1554
+ const r0 = lerp2(q0, q1, t);
1555
+ const r1 = lerp2(q1, q2, t);
1556
+ const s = lerp2(r0, r1, t);
1557
+ return {
1558
+ left: [p0, q0, r0, s],
1559
+ right: [s, r1, q2, p3]
1560
+ };
1561
+ }
1562
+ function splitEasing(easing, xFraction) {
1563
+ if (!easing) return { left: void 0, right: void 0 };
1564
+ if (xFraction <= 0) return { left: void 0, right: easing };
1565
+ if (xFraction >= 1) return { left: easing, right: void 0 };
1566
+ const [x1, y1, x2, y2] = easing;
1567
+ const t = solveCubicBezierX(x1, x2, xFraction);
1568
+ const p0 = [0, 0];
1569
+ const p1 = [x1, y1];
1570
+ const p2 = [x2, y2];
1571
+ const p3 = [1, 1];
1572
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
1573
+ const sx = left[3][0];
1574
+ const sy = left[3][1];
1575
+ let leftEasing;
1576
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
1577
+ leftEasing = [
1578
+ left[1][0] / sx,
1579
+ left[1][1] / sy,
1580
+ left[2][0] / sx,
1581
+ left[2][1] / sy
1582
+ ];
1583
+ }
1584
+ let rightEasing;
1585
+ const rx = 1 - sx;
1586
+ const ry = 1 - sy;
1587
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
1588
+ rightEasing = [
1589
+ (right[1][0] - sx) / rx,
1590
+ (right[1][1] - sy) / ry,
1591
+ (right[2][0] - sx) / rx,
1592
+ (right[2][1] - sy) / ry
1593
+ ];
1594
+ }
1595
+ return { left: leftEasing, right: rightEasing };
1596
+ }
1597
+ function reverseEasing(easing) {
1598
+ if (!easing) return void 0;
1599
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
1600
+ }
213
1601
  function toRGBA(color) {
214
1602
  const r = Math.round(color[0] * 255);
215
1603
  const g = Math.round(color[1] * 255);
@@ -256,6 +1644,24 @@ function parseColor(s) {
256
1644
  var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
257
1645
  var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
258
1646
  var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1647
+ function composeTransformParts(parts, opts) {
1648
+ var _a;
1649
+ if (!parts) return "";
1650
+ const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
1651
+ const segs = [];
1652
+ const t = parts.translate;
1653
+ const o = parts.origin;
1654
+ const r = parts.rotate;
1655
+ const s = parts.scale;
1656
+ const tu = withUnits ? "px" : "";
1657
+ const ru = withUnits ? "deg" : "";
1658
+ if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
1659
+ if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
1660
+ if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
1661
+ if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
1662
+ if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
1663
+ return segs.join("");
1664
+ }
259
1665
  var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
260
1666
  var DEFAULT_DURATION_MS = 1e3;
261
1667
  function kebabToCamelCaseWord(kebab) {
@@ -315,14 +1721,260 @@ function camelCaseToKebabWordIfNeeded(camel) {
315
1721
  function clamp(value, min, max) {
316
1722
  return Math.max(min, Math.min(value, max));
317
1723
  }
1724
+ function bezier2D_pointAt(P0, P1, P2, P3, t) {
1725
+ if (t <= 0) return [P0[0], P0[1]];
1726
+ if (t >= 1) return [P3[0], P3[1]];
1727
+ const u = 1 - t;
1728
+ const u2 = u * u;
1729
+ const u3 = u2 * u;
1730
+ const t2 = t * t;
1731
+ const t3 = t2 * t;
1732
+ const w0 = u3;
1733
+ const w1 = 3 * t * u2;
1734
+ const w2 = 3 * t2 * u;
1735
+ const w3 = t3;
1736
+ return [
1737
+ w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
1738
+ w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
1739
+ ];
1740
+ }
1741
+ var BEZIER_T_NUDGE = 1e-4;
1742
+ function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
1743
+ const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
1744
+ if (result[0] === 0 && result[1] === 0) {
1745
+ const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
1746
+ return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
1747
+ }
1748
+ return result;
1749
+ }
1750
+ function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
1751
+ const u = 1 - t;
1752
+ const a = 3 * u * u;
1753
+ const b = 6 * t * u;
1754
+ const c = 3 * t * t;
1755
+ return [
1756
+ a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
1757
+ a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
1758
+ ];
1759
+ }
1760
+ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
1761
+ const n = steps + 1;
1762
+ const ts = new Float64Array(n);
1763
+ const ds = new Float64Array(n);
1764
+ let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
1765
+ ts[0] = 0;
1766
+ ds[0] = 0;
1767
+ let cum = 0;
1768
+ for (let i = 1; i < n; i++) {
1769
+ const t = i / steps;
1770
+ const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
1771
+ const dx = cur[0] - prev[0];
1772
+ const dy = cur[1] - prev[1];
1773
+ cum += Math.sqrt(dx * dx + dy * dy);
1774
+ ts[i] = t;
1775
+ ds[i] = cum;
1776
+ prev = cur;
1777
+ }
1778
+ return { ts, ds };
1779
+ }
1780
+ function bezier2D_tForDistance(lut, distance) {
1781
+ const { ts, ds } = lut;
1782
+ const last = ds.length - 1;
1783
+ if (distance <= 0) return ts[0];
1784
+ if (distance >= ds[last]) return ts[last];
1785
+ let lo = 1;
1786
+ let hi = last;
1787
+ while (lo < hi) {
1788
+ const mid = lo + hi >>> 1;
1789
+ if (ds[mid] < distance) lo = mid + 1;
1790
+ else hi = mid;
1791
+ }
1792
+ const dPrev = ds[hi - 1];
1793
+ const dCur = ds[hi];
1794
+ const span = dCur - dPrev;
1795
+ const frac = span > 0 ? (distance - dPrev) / span : 0;
1796
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
1797
+ }
318
1798
 
319
1799
  // src/PxAnimatorDOM.ts
320
1800
  var SVG_NS = "http://www.w3.org/2000/svg";
321
- var INTERNAL_ATTRS = /* @__PURE__ */ new Set(["type", "children", "animate", "style", "animator", "defs", "bindings"]);
322
- function createElement(tagName, props, style, children) {
1801
+ var ALLOWED_SVG_TAGS_LOWER_CASE = new Set([
1802
+ "svg",
1803
+ "g",
1804
+ "path",
1805
+ "circle",
1806
+ "ellipse",
1807
+ "rect",
1808
+ "line",
1809
+ "polyline",
1810
+ "polygon",
1811
+ "text",
1812
+ "tspan",
1813
+ "textPath",
1814
+ "defs",
1815
+ "clipPath",
1816
+ "mask",
1817
+ "pattern",
1818
+ "linearGradient",
1819
+ "radialGradient",
1820
+ "stop",
1821
+ "use",
1822
+ "symbol",
1823
+ "marker",
1824
+ "filter",
1825
+ "feGaussianBlur",
1826
+ "feOffset",
1827
+ "feBlend",
1828
+ "feColorMatrix",
1829
+ "feMerge",
1830
+ "feMergeNode"
1831
+ ].map((tagName) => tagName.toLowerCase()));
1832
+ var ALLOWED_RESOURCE_ATTRIBUTES = [
1833
+ "href",
1834
+ // <use>
1835
+ "src",
1836
+ // <image>
1837
+ "filter",
1838
+ // url(#filterId)
1839
+ "clipPath",
1840
+ // clip-path="url(#clipPathId)"
1841
+ "mask",
1842
+ // url(#maskId)
1843
+ "markerStart",
1844
+ // marker-start="url(#markerId)"
1845
+ "markerMid",
1846
+ // marker-mid="url(#markerId)"
1847
+ "markerEnd"
1848
+ // marker-end="url(#markerId)"
1849
+ // 'fill', // url(#gradientId) or url(#patternId)
1850
+ // 'stroke', // url(#gradientId) or url(#patternId)
1851
+ // Don't allow 'cursor', can use external SVG, // url(cursor.svg)
1852
+ ];
1853
+ var ALLOWED_RESOURCE_ATTRIBUTES_SET = new Set(ALLOWED_RESOURCE_ATTRIBUTES);
1854
+ var ALLOWED_ATTRIBUTES = [
1855
+ "href",
1856
+ "src",
1857
+ // Presentation
1858
+ "fill",
1859
+ "fillOpacity",
1860
+ "fillRule",
1861
+ "stroke",
1862
+ "strokeWidth",
1863
+ "strokeOpacity",
1864
+ "strokeLinecap",
1865
+ "strokeLinejoin",
1866
+ "strokeMiterlimit",
1867
+ "strokeDasharray",
1868
+ "strokeDashoffset",
1869
+ "opacity",
1870
+ "transform",
1871
+ // Geometry
1872
+ "x",
1873
+ "y",
1874
+ "cx",
1875
+ "cy",
1876
+ "r",
1877
+ "rx",
1878
+ "ry",
1879
+ "width",
1880
+ "height",
1881
+ "d",
1882
+ "x1",
1883
+ "y1",
1884
+ "x2",
1885
+ "y2",
1886
+ "points",
1887
+ "dx",
1888
+ "dy",
1889
+ // Font
1890
+ "fontSize",
1891
+ "fontFamily",
1892
+ "fontWeight",
1893
+ "fontStyle",
1894
+ // Text
1895
+ "textAnchor",
1896
+ "letterSpacing",
1897
+ "wordSpacing",
1898
+ "space",
1899
+ "xml:space",
1900
+ "textDecoration",
1901
+ "textTransform",
1902
+ "whiteSpace",
1903
+ "white-space",
1904
+ "dominantBaseline",
1905
+ "alignmentBaseline",
1906
+ "rotate",
1907
+ // per-glyph rotation array, currently not supported
1908
+ // Structure
1909
+ "id",
1910
+ "class",
1911
+ "viewBox",
1912
+ "preserveAspectRatio",
1913
+ // Gradient/Pattern
1914
+ "offset",
1915
+ "stopColor",
1916
+ "stopOpacity",
1917
+ "gradientTransform",
1918
+ // Clippath/Mask
1919
+ "clipPath",
1920
+ "mask",
1921
+ // Motion path
1922
+ "offsetPath",
1923
+ "offsetDistance",
1924
+ "offsetRotate",
1925
+ "offsetAnchor",
1926
+ "offsetPosition",
1927
+ // Text path
1928
+ "startOffset",
1929
+ "textLength",
1930
+ "lengthAdjust",
1931
+ // Filter
1932
+ "filter",
1933
+ "stdDeviation",
1934
+ "in",
1935
+ "in2",
1936
+ "result",
1937
+ "mode",
1938
+ ...ALLOWED_RESOURCE_ATTRIBUTES
1939
+ ];
1940
+ var ALLOWED_ATTRIBUTES_LOW_SET = new Set(ALLOWED_ATTRIBUTES.map((str) => str.toLowerCase()));
1941
+ function sanitiseAttributeValue(name, value) {
1942
+ const nameLower = name.toLowerCase();
1943
+ if (!ALLOWED_ATTRIBUTES_LOW_SET.has(nameLower)) {
1944
+ console.warn("Attribute not in whitelist: ", nameLower);
1945
+ return void 0;
1946
+ }
1947
+ if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopColor") {
1948
+ const str = String(value);
1949
+ if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
1950
+ console.warn('Attribute "' + nameLower + '" blocked: url() references must be internal url(#id), got:', value);
1951
+ return void 0;
1952
+ }
1953
+ return value;
1954
+ }
1955
+ if (ALLOWED_RESOURCE_ATTRIBUTES_SET.has(nameLower)) {
1956
+ const str = String(value);
1957
+ if (str.startsWith("#")) {
1958
+ return value;
1959
+ }
1960
+ if (/^url\(#[^)]+\)$/.test(str)) {
1961
+ return value;
1962
+ }
1963
+ return void 0;
1964
+ }
1965
+ return value;
1966
+ }
1967
+ function createElement(tagName, normalisedProps, style, children, textContent) {
1968
+ if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) {
1969
+ console.warn("Attribute not in whitelist: ", tagName);
1970
+ return null;
1971
+ }
323
1972
  const element = document.createElementNS(SVG_NS, tagName);
324
- for (const propName in props) {
325
- element.setAttribute(camelCaseToKebabWordIfNeeded(propName), props[propName]);
1973
+ for (const propName in normalisedProps) {
1974
+ element.setAttribute(
1975
+ camelCaseToKebabWordIfNeeded(propName),
1976
+ sanitiseAttributeValue(propName, normalisedProps[propName])
1977
+ );
326
1978
  }
327
1979
  if (style) {
328
1980
  for (const styleProp in style) {
@@ -334,6 +1986,7 @@ function createElement(tagName, props, style, children) {
334
1986
  element.appendChild(child);
335
1987
  }
336
1988
  }
1989
+ if (textContent) element.textContent = textContent;
337
1990
  return element;
338
1991
  }
339
1992
  function resolveStyle(style, defs) {
@@ -348,9 +2001,12 @@ function getNormalizedProps(props) {
348
2001
  const propsCopy = {};
349
2002
  for (const key of Object.keys(props)) {
350
2003
  if (INTERNAL_ATTRS.has(key)) continue;
2004
+ if (key === "style") continue;
351
2005
  let value = props[key];
352
2006
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
353
2007
  propsCopy[key] = toRGBA(value);
2008
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
2009
+ propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
354
2010
  } else if (TRANSFORM_FN_NAMES.has(key)) {
355
2011
  if (Array.isArray(value)) {
356
2012
  if (key === "translate") value = value.map((v) => v + "px");
@@ -366,8 +2022,8 @@ function getNormalizedProps(props) {
366
2022
  }
367
2023
  function renderNode(node, defs) {
368
2024
  if (!node) return null;
369
- const _a = node, { type, children, animate, style } = _a, props = __objRest(_a, ["type", "children", "animate", "style"]);
370
- const nodeDefs = node.defs || defs;
2025
+ const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
2026
+ const nodeDefs = getDefs(node) || defs;
371
2027
  const resolvedStyle = resolveStyle(style, nodeDefs);
372
2028
  let childElements;
373
2029
  if (children) {
@@ -383,7 +2039,8 @@ function renderNode(node, defs) {
383
2039
  type || "g",
384
2040
  getNormalizedProps(props),
385
2041
  resolvedStyle,
386
- childElements
2042
+ childElements,
2043
+ props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
387
2044
  );
388
2045
  }
389
2046
 
@@ -464,284 +2121,45 @@ function setupAnimationTriggers(api, config) {
464
2121
  return api;
465
2122
  }
466
2123
 
467
- // src/PxAnimatorTypes.ts
468
- var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
469
- var PX_ANIM_ATTR_NAME = "_px_animator";
470
- function isPxElementFileFormat(fileJson) {
471
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
472
- return false;
473
- }
474
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
475
- }
476
- function isObject(v) {
477
- return v && typeof v === "object" && !Array.isArray(v);
478
- }
479
- function validateFillMode(v, path, errors) {
480
- if (v === "forwards" || v === "backwards" || v === "both" || v === "none") return true;
481
- errors.push(path + ': invalid FillMode "' + v + `", expected 'forwards'|'backwards'|'both'|'none'`);
482
- return false;
483
- }
484
- function validatePlaybackDirection(v, path, errors) {
485
- if (v === "normal" || v === "reverse" || v === "alternate" || v === "alternate-reverse") return true;
486
- errors.push(path + ': invalid PlaybackDirection "' + v + `", expected 'normal'|'reverse'|'alternate'|'alternate-reverse'`);
487
- return false;
488
- }
489
- function validatePxEasingOrRef(v, path, errors) {
490
- if (typeof v === "string") return true;
491
- if (Array.isArray(v) && v.length === 4 && v.every((n) => typeof n === "number")) return true;
492
- errors.push(path + ": invalid easing, expected string or [number, number, number, number]");
493
- return false;
494
- }
495
- function validatePxKeyframe(v, path, errors) {
496
- if (!isObject(v)) {
497
- errors.push(path + ": expected object");
498
- return false;
499
- }
500
- let valid = true;
501
- if (v.time !== void 0 && typeof v.time !== "number") {
502
- errors.push(path + ".time: expected number, got " + typeof v.time);
503
- valid = false;
504
- }
505
- if (v.t !== void 0 && typeof v.t !== "number") {
506
- errors.push(path + ".t: expected number, got " + typeof v.t);
507
- valid = false;
508
- }
509
- if (v.easing !== void 0 && !validatePxEasingOrRef(v.easing, path + ".easing", errors)) valid = false;
510
- if (v.e !== void 0 && !validatePxEasingOrRef(v.e, path + ".e", errors)) valid = false;
511
- return valid;
512
- }
513
- function validatePxPropertyAnimation(v, path, errors) {
514
- if (!isObject(v)) {
515
- errors.push(path + ": expected object");
516
- return false;
517
- }
518
- let valid = true;
519
- if (v.keyframes !== void 0) {
520
- if (!Array.isArray(v.keyframes)) {
521
- errors.push(path + ".keyframes: expected array");
522
- valid = false;
523
- } else {
524
- v.keyframes.forEach((kf, i) => {
525
- if (!validatePxKeyframe(kf, path + ".keyframes[" + i + "]", errors)) valid = false;
526
- });
527
- }
528
- }
529
- if (v.kfs !== void 0) {
530
- if (!Array.isArray(v.kfs)) {
531
- errors.push(path + ".kfs: expected array");
532
- valid = false;
533
- } else {
534
- v.kfs.forEach((kf, i) => {
535
- if (!validatePxKeyframe(kf, path + ".kfs[" + i + "]", errors)) valid = false;
536
- });
537
- }
538
- }
539
- return valid;
540
- }
541
- function validatePxAnimationDefinition(v, path, errors) {
542
- if (!isObject(v)) {
543
- errors.push(path + ": expected object");
544
- return false;
545
- }
546
- let valid = true;
547
- for (const key of Object.keys(v)) {
548
- if (!validatePxPropertyAnimation(v[key], path + "." + key, errors)) valid = false;
549
- }
550
- return valid;
551
- }
552
- function validatePxElementAnimation(v, path, errors) {
553
- if (typeof v === "string") return true;
554
- if (Array.isArray(v)) {
555
- let valid = true;
556
- v.forEach((item, i) => {
557
- if (typeof item !== "string" && !validatePxAnimationDefinition(item, path + "[" + i + "]", errors)) {
558
- valid = false;
559
- }
560
- });
561
- return valid;
2124
+ // src/PxMotionPath.ts
2125
+ function propAnimIsMotionPath(anim) {
2126
+ var _a;
2127
+ const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
2128
+ if (!Array.isArray(kfs)) return false;
2129
+ if (anim.autoOrient) return true;
2130
+ for (const kf of kfs) {
2131
+ if (kf.tangentIn || kf.tangentOut) return true;
562
2132
  }
563
- if (isObject(v)) return validatePxAnimationDefinition(v, path, errors);
564
- errors.push(path + ": expected string, array, or PxAnimationDefinition object");
565
2133
  return false;
566
2134
  }
567
- function validatePxTrigger(v, path, errors) {
568
- if (!isObject(v)) {
569
- errors.push(path + ": expected object");
570
- return false;
571
- }
572
- let valid = true;
573
- const validStartOn = ["load", "mouseOver", "click", "scrollIntoView", "programmatic"];
574
- if (!validStartOn.includes(v.startOn)) {
575
- errors.push(path + '.startOn: invalid value "' + v.startOn + '", expected ' + validStartOn.join("|"));
576
- valid = false;
577
- }
578
- if (v.outAction !== void 0) {
579
- const validOutAction = ["continue", "pause", "reset", "reverse"];
580
- if (!validOutAction.includes(v.outAction)) {
581
- errors.push(path + '.outAction: invalid value "' + v.outAction + '", expected ' + validOutAction.join("|"));
582
- valid = false;
583
- }
584
- }
585
- if (v.scrollIntoViewThreshold !== void 0 && typeof v.scrollIntoViewThreshold !== "number") {
586
- errors.push(path + ".scrollIntoViewThreshold: expected number, got " + typeof v.scrollIntoViewThreshold);
587
- valid = false;
588
- }
589
- return valid;
590
- }
591
- function validatePxAnimatorConfig(v, path, errors) {
592
- if (!isObject(v)) {
593
- errors.push(path + ": expected object");
594
- return false;
595
- }
596
- let valid = true;
597
- if (v.mode !== void 0 && !["auto", "webapi", "frames"].includes(v.mode)) {
598
- errors.push(path + '.mode: invalid value "' + v.mode + `", expected 'auto'|'webapi'|'frames'`);
599
- valid = false;
600
- }
601
- if (v.duration !== void 0 && typeof v.duration !== "number") {
602
- errors.push(path + ".duration: expected number, got " + typeof v.duration);
603
- valid = false;
604
- }
605
- if (v.delay !== void 0 && typeof v.delay !== "number") {
606
- errors.push(path + ".delay: expected number, got " + typeof v.delay);
607
- valid = false;
608
- }
609
- if (v.iterations !== void 0 && typeof v.iterations !== "number" && v.iterations !== "infinite") {
610
- errors.push(path + ".iterations: expected number or 'infinite', got " + typeof v.iterations);
611
- valid = false;
612
- }
613
- if (v.fill !== void 0 && !validateFillMode(v.fill, path + ".fill", errors)) valid = false;
614
- if (v.direction !== void 0 && !validatePlaybackDirection(v.direction, path + ".direction", errors)) valid = false;
615
- if (v.frameRate !== void 0 && typeof v.frameRate !== "number") {
616
- errors.push(path + ".frameRate: expected number, got " + typeof v.frameRate);
617
- valid = false;
618
- }
619
- if (v.trigger !== void 0 && !validatePxTrigger(v.trigger, path + ".trigger", errors)) valid = false;
620
- return valid;
621
- }
622
- function validatePxDefs(v, path, errors) {
623
- if (!isObject(v)) {
624
- errors.push(path + ": expected object");
625
- return false;
626
- }
627
- let valid = true;
628
- if (v.easings !== void 0) {
629
- if (!isObject(v.easings)) {
630
- errors.push(path + ".easings: expected object");
631
- valid = false;
632
- } else {
633
- for (const key of Object.keys(v.easings)) {
634
- if (!validatePxEasingOrRef(v.easings[key], path + ".easings." + key, errors)) valid = false;
635
- }
636
- }
637
- }
638
- if (v.animations !== void 0) {
639
- if (!isObject(v.animations)) {
640
- errors.push(path + ".animations: expected object");
641
- valid = false;
642
- } else {
643
- for (const key of Object.keys(v.animations)) {
644
- if (!validatePxAnimationDefinition(v.animations[key], path + ".animations." + key, errors)) valid = false;
645
- }
646
- }
647
- }
648
- if (v.styles !== void 0 && !isObject(v.styles)) {
649
- errors.push(path + ".styles: expected object");
650
- valid = false;
651
- }
652
- return valid;
653
- }
654
- function validatePxBinding(v, path, errors) {
655
- if (!isObject(v)) {
656
- errors.push(path + ": expected object");
657
- return false;
658
- }
659
- let valid = true;
660
- if (typeof v.id !== "string") {
661
- errors.push(path + ".id: expected string, got " + typeof v.id);
662
- valid = false;
663
- }
664
- if (!validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
665
- return valid;
666
- }
667
- function validatePxNode(v, path, errors) {
668
- if (!isObject(v)) {
669
- errors.push(path + ": expected object");
670
- return false;
671
- }
672
- let valid = true;
673
- if (typeof v.type !== "string") {
674
- errors.push(path + ".type: expected string, got " + typeof v.type);
675
- valid = false;
676
- }
677
- if (v.children !== void 0) {
678
- if (!Array.isArray(v.children)) {
679
- errors.push(path + ".children: expected array");
680
- valid = false;
681
- } else {
682
- v.children.forEach((child, i) => {
683
- if (!validatePxNode(child, path + ".children[" + i + "]", errors)) valid = false;
684
- });
685
- }
686
- }
687
- if (v.animate !== void 0 && !validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
688
- return valid;
689
- }
690
- function validatePxSvgNode(v, path, errors) {
691
- if (!validatePxNode(v, path, errors)) return false;
692
- let valid = true;
693
- if (v.type !== "svg") {
694
- errors.push(path + ".type: expected 'svg', got '" + v.type + "'");
695
- valid = false;
696
- }
697
- if (v.width !== void 0 && typeof v.width !== "number") {
698
- errors.push(path + ".width: expected number, got " + typeof v.width);
699
- valid = false;
700
- }
701
- if (v.height !== void 0 && typeof v.height !== "number") {
702
- errors.push(path + ".height: expected number, got " + typeof v.height);
703
- valid = false;
704
- }
705
- if (v.viewBox !== void 0 && typeof v.viewBox !== "string") {
706
- errors.push(path + ".viewBox: expected string, got " + typeof v.viewBox);
707
- valid = false;
708
- }
709
- if (v.animator !== void 0 && !validatePxAnimatorConfig(v.animator, path + ".animator", errors)) valid = false;
710
- if (v.defs !== void 0 && !validatePxDefs(v.defs, path + ".defs", errors)) valid = false;
711
- if (v.bindings !== void 0) {
712
- if (!Array.isArray(v.bindings)) {
713
- errors.push(path + ".bindings: expected array");
714
- valid = false;
715
- } else {
716
- v.bindings.forEach((binding, i) => {
717
- if (!validatePxBinding(binding, path + ".bindings[" + i + "]", errors)) valid = false;
718
- });
719
- }
720
- }
721
- if (v.design !== void 0 && !validatePxNode(v.design, path + ".design", errors)) valid = false;
722
- return valid;
723
- }
724
- function isPxElementFileFormatDeep(fileJson) {
725
- const errors = [];
726
- const valid = validatePxSvgNode(fileJson, "root", errors);
727
- return { valid, errors };
728
- }
729
- function getAnimatorConfig(doc) {
730
- var _a, _b;
731
- return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
732
- }
733
- function getDefs(doc) {
734
- var _a;
735
- if (!doc) return void 0;
736
- return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
737
- }
738
- function getBindings(doc) {
739
- var _a;
740
- if (!doc) return void 0;
741
- return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
742
- }
743
- function getChildren(doc) {
744
- return doc == null ? void 0 : doc.children;
2135
+ var _segmentCache = /* @__PURE__ */ new WeakMap();
2136
+ function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
2137
+ const existing = _segmentCache.get(prevKf);
2138
+ if (existing) return existing;
2139
+ const to = prevKf.tangentOut;
2140
+ const ti = nextKf.tangentIn;
2141
+ const P1 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];
2142
+ const P2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];
2143
+ const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);
2144
+ const entry = {
2145
+ P0: prevPos,
2146
+ P1,
2147
+ P2,
2148
+ P3: nextPos,
2149
+ lut,
2150
+ totalArc: lut.ds[lut.ds.length - 1]
2151
+ };
2152
+ _segmentCache.set(prevKf, entry);
2153
+ return entry;
2154
+ }
2155
+ function evaluateMotionPathSegment(prevKf, nextKf, prevPos, nextPos, localProgress, autoOrient) {
2156
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
2157
+ const t = seg.totalArc === 0 ? localProgress : bezier2D_tForDistance(seg.lut, localProgress * seg.totalArc);
2158
+ const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2159
+ if (!autoOrient) return { translate: point };
2160
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2161
+ const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
2162
+ return { translate: point, rotateDeg };
745
2163
  }
746
2164
 
747
2165
  // src/PxDefinitions.ts
@@ -807,6 +2225,8 @@ function parseSvgPathToBezier(d) {
807
2225
  currentPath.o.push([x2, y2]);
808
2226
  } else if (type === "Z" || type === "z") {
809
2227
  currentPath.c = true;
2228
+ } else {
2229
+ console.warn('Unsupported path command "' + type + '"');
810
2230
  }
811
2231
  }
812
2232
  return res;
@@ -824,13 +2244,17 @@ function isPathString(value) {
824
2244
  return typeof value === "string" && extractPathData(value) !== void 0;
825
2245
  }
826
2246
  function normalizePathValue(value) {
2247
+ if (value && typeof value === "object" && typeof value.path === "string") {
2248
+ const d = extractPathData(value.path);
2249
+ return d ? { paths: parseSvgPathToBezier(d) } : value;
2250
+ }
827
2251
  if (value && typeof value === "object" && "paths" in value) {
828
2252
  const pathsArray = value.paths;
829
2253
  if (Array.isArray(pathsArray) && pathsArray.length > 0) {
830
2254
  if (isPathString(pathsArray[0])) {
831
2255
  const paths = [];
832
- for (const pathStr of pathsArray) {
833
- const d = extractPathData(pathStr);
2256
+ for (const pathStr2 of pathsArray) {
2257
+ const d = extractPathData(pathStr2);
834
2258
  if (d) {
835
2259
  paths.push(...parseSvgPathToBezier(d));
836
2260
  }
@@ -843,8 +2267,8 @@ function normalizePathValue(value) {
843
2267
  if (Array.isArray(value)) {
844
2268
  if (value.length > 0 && isPathString(value[0])) {
845
2269
  const paths = [];
846
- for (const pathStr of value) {
847
- const d = extractPathData(pathStr);
2270
+ for (const pathStr2 of value) {
2271
+ const d = extractPathData(pathStr2);
848
2272
  if (d) {
849
2273
  paths.push(...parseSvgPathToBezier(d));
850
2274
  }
@@ -898,6 +2322,111 @@ function resolveElementAnimation(animate, defs) {
898
2322
  }
899
2323
  return results;
900
2324
  }
2325
+ function interpolateValue(propName, a, b, t) {
2326
+ var _a, _b;
2327
+ if (propName === "d") {
2328
+ const aPaths = (_a = a == null ? void 0 : a.paths) != null ? _a : Array.isArray(a) ? a : [];
2329
+ const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
2330
+ return { paths: interpolateBeziers(aPaths, bPaths, t) };
2331
+ }
2332
+ if (COLOUR_ATTR_NAMES.has(propName)) {
2333
+ return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
2334
+ }
2335
+ if (TRANSFORM_FN_NAMES.has(propName) || propName === "stroke-dasharray" || propName === "strokeDasharray") {
2336
+ return interpolateVec(a || [], b || [], t);
2337
+ }
2338
+ return interpolateNum(+(a || 0), +(b || 0), t);
2339
+ }
2340
+ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2341
+ var _a, _b, _c, _d, _e;
2342
+ const totalIntervals = keyframes.length - 1;
2343
+ const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2344
+ let segKfs;
2345
+ if (loop.before) {
2346
+ segKfs = keyframes.slice(0, segCount + 1);
2347
+ } else {
2348
+ segKfs = keyframes.slice(totalIntervals - segCount);
2349
+ }
2350
+ const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2351
+ const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2352
+ let fillStart, fillEnd;
2353
+ if (loop.before) {
2354
+ fillStart = 0;
2355
+ fillEnd = firstT;
2356
+ } else {
2357
+ fillStart = lastT;
2358
+ fillEnd = duration;
2359
+ }
2360
+ const fillDuration = fillEnd - fillStart;
2361
+ if (fillDuration <= 0) return keyframes;
2362
+ const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
2363
+ const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
2364
+ const segDuration = segEndT - segStartT;
2365
+ if (segDuration <= 0) return keyframes;
2366
+ const template = segKfs.map((kf) => ({
2367
+ relT: (kf.t - segStartT) / segDuration,
2368
+ v: kf.v,
2369
+ e: kf.e
2370
+ }));
2371
+ const fullReps = Math.floor(fillDuration / segDuration);
2372
+ const remainder = fillDuration - fullReps * segDuration;
2373
+ const partialFraction = remainder / segDuration;
2374
+ const looped = [];
2375
+ function appendRep(repStart, isReversed, partial) {
2376
+ let entries;
2377
+ if (isReversed) {
2378
+ entries = [];
2379
+ for (let i = template.length - 1; i >= 0; i--) {
2380
+ entries.push({
2381
+ relT: 1 - template[i].relT,
2382
+ v: template[i].v,
2383
+ // Easing for reversed transition: use reversed easing from the forward "from" keyframe
2384
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0
2385
+ });
2386
+ }
2387
+ } else {
2388
+ entries = template;
2389
+ }
2390
+ const cutRelT = partial !== void 0 ? partial : 1;
2391
+ for (let i = 0; i < entries.length; i++) {
2392
+ const entry = entries[i];
2393
+ if (entry.relT > cutRelT + 1e-9) {
2394
+ const prev = entries[i - 1];
2395
+ const intervalSpan = entry.relT - prev.relT;
2396
+ const localFrac = (cutRelT - prev.relT) / intervalSpan;
2397
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2398
+ const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
2399
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
2400
+ if (looped.length > 0 && prev.relT <= cutRelT) {
2401
+ looped[looped.length - 1].e = leftEasing;
2402
+ }
2403
+ looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2404
+ return;
2405
+ }
2406
+ looped.push({
2407
+ t: repStart + entry.relT * segDuration,
2408
+ v: entry.v,
2409
+ e: i < entries.length - 1 ? entry.e : void 0
2410
+ });
2411
+ }
2412
+ }
2413
+ for (let rep = 0; rep < fullReps; rep++) {
2414
+ const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
2415
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
2416
+ const repStart = fillStart + rep * segDuration;
2417
+ appendRep(repStart, isReversed);
2418
+ }
2419
+ if (partialFraction > 1e-9) {
2420
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
2421
+ const repStart = fillStart + fullReps * segDuration;
2422
+ appendRep(repStart, isReversed, partialFraction);
2423
+ }
2424
+ if (loop.before) {
2425
+ return [...looped, ...keyframes];
2426
+ } else {
2427
+ return [...keyframes, ...looped];
2428
+ }
2429
+ }
901
2430
  function normalizeKeyframes(propName, propAnim, duration, defs) {
902
2431
  var _a, _b, _c, _d, _e;
903
2432
  const keyframes = propAnim.keyframes || propAnim.kfs || [];
@@ -922,6 +2451,11 @@ function normalizeKeyframes(propName, propAnim, duration, defs) {
922
2451
  var _a2, _b2;
923
2452
  return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
924
2453
  });
2454
+ const loopRaw = propAnim.loop;
2455
+ const loop = loopRaw === true ? {} : loopRaw || void 0;
2456
+ if (loop && normalized.length >= 2) {
2457
+ return expandLoopKeyframes(propName, normalized, loop, duration);
2458
+ }
925
2459
  return normalized;
926
2460
  }
927
2461
  function mergeAnimationDefinitions(animations) {
@@ -972,10 +2506,11 @@ function getNormalisedBindings(doc) {
972
2506
  }
973
2507
  }
974
2508
  const processNode = (node) => {
975
- if (node.animate) {
2509
+ const inlineAnim = node.animate;
2510
+ if (inlineAnim && Object.keys(inlineAnim).length > 0) {
976
2511
  const nodeId = node.id || generateElementId();
977
2512
  node.id = nodeId;
978
- const normalized = processAnimation(nodeId, node.animate);
2513
+ const normalized = processAnimation(nodeId, inlineAnim);
979
2514
  if (normalized) bindings.push(normalized);
980
2515
  }
981
2516
  if (node.children) {
@@ -1046,6 +2581,41 @@ function calcPropertyValue(propName, propAnim, progress) {
1046
2581
  localProgress
1047
2582
  ).join(" ");
1048
2583
  cssAttrName = propName;
2584
+ } else if (cssAttrName === "transform" && prevV !== null && typeof prevV === "object" && !Array.isArray(prevV)) {
2585
+ const partKeys = /* @__PURE__ */ new Set([
2586
+ ...prevV ? Object.keys(prevV) : [],
2587
+ ...nextV ? Object.keys(nextV) : []
2588
+ ]);
2589
+ const partsResult = {};
2590
+ for (const partKey of partKeys) {
2591
+ const prevPart = prevV == null ? void 0 : prevV[partKey];
2592
+ const nextPart = nextV == null ? void 0 : nextV[partKey];
2593
+ if (partKey === "rotate") {
2594
+ partsResult.rotate = interpolateNum(+(prevPart != null ? prevPart : 0), +(nextPart != null ? nextPart : 0), localProgress);
2595
+ } else if (partKey === "translate" || partKey === "scale" || partKey === "origin") {
2596
+ const fallback = partKey === "scale" ? [1, 1] : [0, 0];
2597
+ const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);
2598
+ partsResult[partKey] = interp;
2599
+ }
2600
+ }
2601
+ if (propAnimIsMotionPath(propAnim)) {
2602
+ const prevTr = prevV.translate;
2603
+ const nextTr = nextV.translate;
2604
+ if (Array.isArray(prevTr) && Array.isArray(nextTr)) {
2605
+ const sample = evaluateMotionPathSegment(
2606
+ prevKf,
2607
+ nextKf,
2608
+ [+prevTr[0], +prevTr[1]],
2609
+ [+nextTr[0], +nextTr[1]],
2610
+ localProgress,
2611
+ !!propAnim.autoOrient
2612
+ );
2613
+ partsResult.translate = [sample.translate[0], sample.translate[1]];
2614
+ if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg;
2615
+ }
2616
+ }
2617
+ cssValue = composeTransformParts(partsResult, { withUnits: false });
2618
+ cssAttrName = "transform";
1049
2619
  } else if (cssAttrName === "translate") {
1050
2620
  const v = interpolateVec(
1051
2621
  prevV || [0, 0],
@@ -1071,12 +2641,12 @@ function calcPropertyValue(propName, propAnim, progress) {
1071
2641
  cssValue = "scale(" + v.join(",") + ")";
1072
2642
  cssAttrName = "transform";
1073
2643
  } else {
1074
- const num = interpolateNum(
2644
+ const num2 = interpolateNum(
1075
2645
  +(prevV || 0),
1076
2646
  +(nextV || 0),
1077
2647
  localProgress
1078
2648
  );
1079
- cssValue = num;
2649
+ cssValue = num2;
1080
2650
  }
1081
2651
  if (PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
1082
2652
  cssValue = cssValue * 100 + "%";
@@ -1356,41 +2926,91 @@ function createDomAdapter(rootElement) {
1356
2926
  }
1357
2927
 
1358
2928
  // src/PxAnimatorWebApi.ts
2929
+ function createCssKf(kf, t, propName, unsupportedSet) {
2930
+ var _a, _b;
2931
+ let value = (_a = kf.v) != null ? _a : kf.value;
2932
+ const e = (_b = kf.e) != null ? _b : kf.easing;
2933
+ const cssKf = {
2934
+ offset: t,
2935
+ easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
2936
+ };
2937
+ let cssValue;
2938
+ let cssKey = propName;
2939
+ if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
2940
+ cssValue = toRGBA(value);
2941
+ } else if (propName === "transform" && value !== null && typeof value === "object" && !Array.isArray(value)) {
2942
+ cssValue = composeTransformParts(value, { withUnits: true });
2943
+ cssKey = "transform";
2944
+ } else if (TRANSFORM_FN_NAMES.has(propName)) {
2945
+ if (Array.isArray(value)) {
2946
+ if (propName === "translate") value = value.map((v) => v + "px");
2947
+ value = value.join(",");
2948
+ }
2949
+ if (propName === "rotate") value = value + "deg";
2950
+ cssValue = propName + "(" + value + ")";
2951
+ cssKey = "transform";
2952
+ } else {
2953
+ cssValue = "" + value;
2954
+ }
2955
+ if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
2956
+ cssKey = kebabToCamelCaseWord(cssKey);
2957
+ cssKf[cssKey] = cssValue;
2958
+ return cssKf;
2959
+ }
2960
+ function clipKeyframesToDuration(propName, keyframes, duration) {
2961
+ var _a, _b, _c, _d, _e;
2962
+ const result = [];
2963
+ for (let i = 0; i < keyframes.length; i++) {
2964
+ const kf = keyframes[i];
2965
+ const t = (_a = kf.t) != null ? _a : 0;
2966
+ if (t < 0) {
2967
+ const next = keyframes[i + 1];
2968
+ if (next && ((_b = next.t) != null ? _b : 0) >= 0) {
2969
+ const nextT = (_c = next.t) != null ? _c : 0;
2970
+ const localFrac = (0 - t) / (nextT - t);
2971
+ const easedFrac = kf.e ? cubicBezier(kf.e)(localFrac) : localFrac;
2972
+ const { right: rightEasing } = splitEasing(kf.e, localFrac);
2973
+ result.push({ t: 0, v: interpolateValue(propName, kf.v, next.v, easedFrac), e: rightEasing });
2974
+ }
2975
+ continue;
2976
+ }
2977
+ if (t > duration) {
2978
+ const prev = keyframes[i - 1];
2979
+ if (prev && ((_d = prev.t) != null ? _d : 0) <= duration) {
2980
+ const prevT = (_e = prev.t) != null ? _e : 0;
2981
+ const localFrac = (duration - prevT) / (t - prevT);
2982
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2983
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
2984
+ if (result.length > 0) result[result.length - 1] = __spreadProps(__spreadValues({}, result[result.length - 1]), { e: leftEasing });
2985
+ result.push({ t: duration, v: interpolateValue(propName, prev.v, kf.v, easedFrac), e: void 0 });
2986
+ }
2987
+ break;
2988
+ }
2989
+ result.push(kf);
2990
+ }
2991
+ return result;
2992
+ }
1359
2993
  function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
1360
- var _a, _b, _c, _d;
2994
+ var _a;
1361
2995
  const result = /* @__PURE__ */ new Map();
1362
2996
  for (const [propName, propAnim] of Object.entries(animDef)) {
1363
- const keyframes = propAnim.kfs || propAnim.keyframes || [];
2997
+ const duration = config.duration || 1;
2998
+ const clippedKeyframes = clipKeyframesToDuration(propName, propAnim.kfs || propAnim.keyframes || [], duration);
1364
2999
  const cssKeyframes = [];
1365
- for (const kf of keyframes) {
1366
- let t = (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
1367
- t = clamp(t / (config.duration || 1), 0, 1);
1368
- let value = (_c = kf.v) != null ? _c : kf.value;
1369
- const e = (_d = kf.e) != null ? _d : kf.easing;
1370
- const cssKf = {
1371
- offset: t,
1372
- easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
1373
- };
1374
- let cssValue;
1375
- let cssKey = propName;
1376
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
1377
- cssValue = toRGBA(value);
1378
- } else if (TRANSFORM_FN_NAMES.has(propName)) {
1379
- if (Array.isArray(value)) {
1380
- if (propName === "translate") value = value.map((v) => v + "px");
1381
- value = value.join(",");
1382
- }
1383
- if (propName === "rotate") value = value + "deg";
1384
- cssValue = propName + "(" + value + ")";
1385
- cssKey = "transform";
1386
- } else {
1387
- cssValue = "" + value;
3000
+ for (let i = 0; i < clippedKeyframes.length; i++) {
3001
+ const kf = clippedKeyframes[i];
3002
+ const t = clamp(((_a = kf.t) != null ? _a : 0) / duration, 0, 1);
3003
+ const cssKf = createCssKf(kf, t, propName, unsupportedSet);
3004
+ if (i === 0 && (cssKf.offset || 0) > 0) {
3005
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), { offset: 0 }));
1388
3006
  }
1389
- if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
1390
- cssKey = kebabToCamelCaseWord(cssKey);
1391
- cssKf[cssKey] = cssValue;
1392
3007
  cssKeyframes.push(cssKf);
1393
3008
  }
3009
+ if (cssKeyframes.length > 0 && (cssKeyframes[cssKeyframes.length - 1].offset || 0) < 1) {
3010
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKeyframes[cssKeyframes.length - 1]), {
3011
+ offset: 1
3012
+ }));
3013
+ }
1394
3014
  if (cssKeyframes.length > 0) {
1395
3015
  result.set(propName, cssKeyframes);
1396
3016
  }
@@ -1398,6 +3018,7 @@ function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
1398
3018
  return result;
1399
3019
  }
1400
3020
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs) {
3021
+ var _a;
1401
3022
  const config = getAnimatorConfig(doc) || {};
1402
3023
  const bindings = getNormalisedBindings(doc);
1403
3024
  if (!rootElement) {
@@ -1430,29 +3051,33 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1430
3051
  console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
1431
3052
  }
1432
3053
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
3054
+ const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
3055
+ const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
3056
+ const effectOptions = {
3057
+ duration: config.duration,
3058
+ delay: positiveDelay,
3059
+ // Default to 'forwards' so elements hold their final state after the
3060
+ // animation ends — consistent with Lottie and other animation runtimes.
3061
+ // Without this, seeking to the last frame reverts elements to their
3062
+ // pre-animation state (the Web Animations API "after" phase with fill:'none').
3063
+ fill: (_a = config.fill) != null ? _a : "forwards",
3064
+ direction: config.direction,
3065
+ iterations
3066
+ };
1433
3067
  for (let i = 0; i < elements.length; i++) {
1434
3068
  const element = elements[i];
1435
- const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
1436
- const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
1437
- const effectOptions = {
1438
- duration: config.duration,
1439
- delay: positiveDelay,
1440
- fill: config.fill,
1441
- direction: config.direction,
1442
- iterations
1443
- };
1444
3069
  for (const [, keyframes] of keyframesMap) {
1445
3070
  if (keyframes.length > 0) {
1446
3071
  try {
1447
3072
  const effect = new KeyframeEffect(element, keyframes, effectOptions);
1448
3073
  const anim = new Animation(effect, document.timeline);
1449
3074
  if (callbacks == null ? void 0 : callbacks.onFinish) anim.onfinish = () => {
1450
- var _a;
1451
- return (_a = callbacks.onFinish) == null ? void 0 : _a.call(callbacks);
3075
+ var _a2;
3076
+ return (_a2 = callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
1452
3077
  };
1453
3078
  if (callbacks == null ? void 0 : callbacks.onRemove) anim.onremove = () => {
1454
- var _a;
1455
- return (_a = callbacks.onRemove) == null ? void 0 : _a.call(callbacks);
3079
+ var _a2;
3080
+ return (_a2 = callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
1456
3081
  };
1457
3082
  if (seekPosition) {
1458
3083
  anim.currentTime = seekPosition;
@@ -1473,34 +3098,47 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1473
3098
  "isReady": () => true,
1474
3099
  "getRootElement": () => rootElement || null,
1475
3100
  "isPlaying": () => {
1476
- var _a;
1477
- return ((_a = animations[0]) == null ? void 0 : _a.playState) === "running";
3101
+ var _a2;
3102
+ return ((_a2 = animations[0]) == null ? void 0 : _a2.playState) === "running";
1478
3103
  },
1479
3104
  "play": () => {
1480
- var _a;
3105
+ var _a2;
1481
3106
  animations.forEach((a) => a.play());
1482
- (_a = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a.call(callbacks);
3107
+ (_a2 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a2.call(callbacks);
1483
3108
  },
1484
3109
  "pause": () => {
1485
- var _a;
3110
+ var _a2;
1486
3111
  animations.forEach((a) => a.pause());
1487
- (_a = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a.call(callbacks);
3112
+ (_a2 = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a2.call(callbacks);
1488
3113
  },
1489
3114
  "cancel": () => {
1490
- var _a;
3115
+ var _a2;
1491
3116
  animations.forEach((a) => a.cancel());
1492
- (_a = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a.call(callbacks);
3117
+ (_a2 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a2.call(callbacks);
1493
3118
  },
1494
3119
  "finish": () => {
1495
- animations.forEach((a) => a.finish());
3120
+ var _a2;
3121
+ for (const a of animations) {
3122
+ try {
3123
+ if (((_a2 = a.effect) == null ? void 0 : _a2.getTiming().iterations) === Infinity) {
3124
+ a.effect.updateTiming({ iterations: 1 });
3125
+ a.finish();
3126
+ a.effect.updateTiming({ iterations: Infinity });
3127
+ } else {
3128
+ a.finish();
3129
+ }
3130
+ } catch (e) {
3131
+ a.cancel();
3132
+ }
3133
+ }
1496
3134
  },
1497
3135
  "setPlaybackRate": (rate) => {
1498
3136
  animations.forEach((a) => a.playbackRate = rate);
1499
3137
  return api;
1500
3138
  },
1501
3139
  "getCurrentTime": () => {
1502
- var _a, _b;
1503
- const res = (_b = (_a = animations[0]) == null ? void 0 : _a.currentTime) != null ? _b : null;
3140
+ var _a2, _b;
3141
+ const res = (_b = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b : null;
1504
3142
  return res !== null ? +res : null;
1505
3143
  },
1506
3144
  "setCurrentTime": (time) => {
@@ -1557,6 +3195,7 @@ function deepClone(value) {
1557
3195
  return cloned;
1558
3196
  }
1559
3197
  function generateNewIds(doc) {
3198
+ var _a, _b;
1560
3199
  const cloned = deepClone(doc);
1561
3200
  const idMap = /* @__PURE__ */ new Map();
1562
3201
  const hashRefAttrs = /* @__PURE__ */ new Set(["href", "xlink:href"]);
@@ -1582,6 +3221,8 @@ function generateNewIds(doc) {
1582
3221
  const newId = generateUniqueId();
1583
3222
  idMap.set(oldId, newId);
1584
3223
  node.id = newId;
3224
+ } else if (node.animate) {
3225
+ node.id = generateUniqueId();
1585
3226
  }
1586
3227
  if (Array.isArray(node.children)) {
1587
3228
  for (const child of node.children) {
@@ -1623,23 +3264,21 @@ function generateNewIds(doc) {
1623
3264
  value[styleProp] = replaceUrlRefs(styleValue, idMap);
1624
3265
  }
1625
3266
  }
1626
- } else if (typeof value === "object" && value !== null && key !== "animate") {
3267
+ } else if (typeof value === "object" && value !== null) {
1627
3268
  updateRefs(value);
1628
3269
  }
1629
3270
  }
1630
3271
  }
1631
3272
  collectIds(cloned);
1632
3273
  updateRefs(cloned);
1633
- const docBindings = cloned.bindings;
1634
- if (Array.isArray(docBindings)) {
1635
- for (const binding of docBindings) {
1636
- if (binding.id) {
1637
- const newId = idMap.get(binding.id);
1638
- if (newId) {
1639
- binding.id = newId;
1640
- }
1641
- }
3274
+ const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animate;
3275
+ if (docAnimate && typeof docAnimate === "object") {
3276
+ const updatedAnimate = {};
3277
+ for (const [id, anim] of Object.entries(docAnimate)) {
3278
+ const newId = (_b = idMap.get(id)) != null ? _b : id;
3279
+ updatedAnimate[newId] = anim;
1642
3280
  }
3281
+ cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animate: updatedAnimate });
1643
3282
  }
1644
3283
  return cloned;
1645
3284
  }
@@ -1650,6 +3289,9 @@ function replaceUrlRefs(value, idMap) {
1650
3289
  });
1651
3290
  }
1652
3291
  function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
3292
+ const effectsWarnings = validateNodeEffects(doc);
3293
+ for (const w of effectsWarnings) console.warn("[PxAnimator] effects shape warning:", w);
3294
+ doc = applyPlayerEffects(doc).root;
1653
3295
  const animatorConfig = getAnimatorConfig(doc) || {};
1654
3296
  animatorConfig.debug = true;
1655
3297
  let rootElement = null;
@@ -1665,14 +3307,22 @@ function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
1665
3307
  }
1666
3308
  return createAnimatorFromConfig(doc, adapter, callbacks, rootElement);
1667
3309
  }
1668
- function createAnimator(docOrUrl, adapter, callbacks, containerElement) {
1669
- if (typeof docOrUrl === "object") {
1670
- return createAnimatorImpl(docOrUrl, adapter, callbacks, containerElement);
3310
+ var PX_ANIMATOR_DATA_KEY = "data";
3311
+ function createAnimator(options) {
3312
+ const { src, data, adapter, callbacks, container } = options;
3313
+ if (data !== void 0 && src !== void 0) {
3314
+ throw new Error("createAnimator: provide either `src` or `data`, not both");
3315
+ }
3316
+ if (data === void 0 && src === void 0) {
3317
+ throw new Error("createAnimator: either `src` or `data` is required");
3318
+ }
3319
+ if (data !== void 0) {
3320
+ return createAnimatorImpl(data, adapter, callbacks, container);
1671
3321
  }
1672
3322
  let animator = null;
1673
- fetch(docOrUrl).then((res) => res.json()).then((json) => {
3323
+ fetch(src).then((res) => res.json()).then((json) => {
1674
3324
  if (isPxElementFileFormat(json)) {
1675
- animator = createAnimatorImpl(json, adapter, callbacks, containerElement);
3325
+ animator = createAnimatorImpl(json, adapter, callbacks, container);
1676
3326
  } else {
1677
3327
  console.error("Invalid animation document format");
1678
3328
  }
@@ -1712,7 +3362,7 @@ function loadTagAnimators() {
1712
3362
  if (!element[PX_ANIM_ATTR_NAME]) {
1713
3363
  const src = element.getAttribute(PX_ANIM_SRC_ATTR_NAME);
1714
3364
  if (src) {
1715
- element[PX_ANIM_ATTR_NAME] = createAnimator(src, void 0, void 0, element);
3365
+ element[PX_ANIM_ATTR_NAME] = createAnimator({ src, container: element });
1716
3366
  }
1717
3367
  }
1718
3368
  }
@@ -1722,20 +3372,259 @@ if (typeof window !== "undefined") {
1722
3372
  window["createAnimator"] = createAnimator;
1723
3373
  window["setupAnimationTriggers"] = setupAnimationTriggers;
1724
3374
  }
3375
+
3376
+ // src/effects/PlayerEffectsUtil.visualModel.ts
3377
+ var IDENTITY = [1, 0, 0, 1, 0, 0];
3378
+ function mul(m, n) {
3379
+ return [
3380
+ m[0] * n[0] + m[2] * n[1],
3381
+ m[1] * n[0] + m[3] * n[1],
3382
+ m[0] * n[2] + m[2] * n[3],
3383
+ m[1] * n[2] + m[3] * n[3],
3384
+ m[0] * n[4] + m[2] * n[5] + m[4],
3385
+ m[1] * n[4] + m[3] * n[5] + m[5]
3386
+ ];
3387
+ }
3388
+ var translateM = (x, y) => [1, 0, 0, 1, x, y];
3389
+ var scaleM = (sx, sy) => [sx, 0, 0, sy, 0, 0];
3390
+ function rotateM(deg) {
3391
+ const r = deg * Math.PI / 180;
3392
+ return [Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0];
3393
+ }
3394
+ var skewXM = (deg) => [1, 0, Math.tan(deg * Math.PI / 180), 1, 0, 0];
3395
+ var skewYM = (deg) => [1, Math.tan(deg * Math.PI / 180), 0, 1, 0, 0];
3396
+ function parseTransformString(s) {
3397
+ let m = IDENTITY;
3398
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
3399
+ let hit;
3400
+ while (hit = re.exec(s)) {
3401
+ const fn = hit[1];
3402
+ const a = hit[2].split(/[\s,]+/).filter(Boolean).map(Number);
3403
+ if (fn === "translate") m = mul(m, translateM(a[0] || 0, a[1] || 0));
3404
+ else if (fn === "scale") m = mul(m, scaleM(a[0], a.length > 1 ? a[1] : a[0]));
3405
+ else if (fn === "rotate") m = mul(m, rotateM(a[0]));
3406
+ else if (fn === "skewX") m = mul(m, skewXM(a[0]));
3407
+ else if (fn === "skewY") m = mul(m, skewYM(a[0]));
3408
+ else if (fn === "matrix") m = mul(m, [a[0], a[1], a[2], a[3], a[4], a[5]]);
3409
+ }
3410
+ return m;
3411
+ }
3412
+ function partsToMatrix(p) {
3413
+ let m = IDENTITY;
3414
+ if (p.translate) m = mul(m, translateM(p.translate[0], p.translate[1]));
3415
+ const pivot = p.origin && (p.rotate !== void 0 || p.scale);
3416
+ if (pivot) m = mul(m, translateM(p.origin[0], p.origin[1]));
3417
+ if (p.rotate !== void 0) m = mul(m, rotateM(p.rotate));
3418
+ if (p.scale) m = mul(m, scaleM(p.scale[0], p.scale[1]));
3419
+ if (pivot) m = mul(m, translateM(-p.origin[0], -p.origin[1]));
3420
+ return m;
3421
+ }
3422
+ function lerp(a, b, f) {
3423
+ return a + (b - a) * f;
3424
+ }
3425
+ function interpParts(kfs, t) {
3426
+ var _a, _b, _c, _d, _e, _f, _g;
3427
+ if (!kfs.length) return {};
3428
+ if (t <= ((_a = kfs[0].time) != null ? _a : 0)) return kfs[0].value || {};
3429
+ if (t >= ((_b = kfs[kfs.length - 1].time) != null ? _b : 0)) return kfs[kfs.length - 1].value || {};
3430
+ let i = 0;
3431
+ while (i < kfs.length - 1 && ((_c = kfs[i + 1].time) != null ? _c : 0) < t) i++;
3432
+ const a = kfs[i], b = kfs[i + 1];
3433
+ const f = (t - ((_d = a.time) != null ? _d : 0)) / (((_e = b.time) != null ? _e : 0) - ((_f = a.time) != null ? _f : 0) || 1);
3434
+ const va = a.value || {}, vb = b.value || {};
3435
+ const out = {};
3436
+ if (va.translate && vb.translate) out.translate = [lerp(va.translate[0], vb.translate[0], f), lerp(va.translate[1], vb.translate[1], f)];
3437
+ else out.translate = va.translate || vb.translate;
3438
+ if (va.rotate !== void 0 && vb.rotate !== void 0) out.rotate = lerp(va.rotate, vb.rotate, f);
3439
+ else out.rotate = (_g = va.rotate) != null ? _g : vb.rotate;
3440
+ if (va.scale && vb.scale) out.scale = [lerp(va.scale[0], vb.scale[0], f), lerp(va.scale[1], vb.scale[1], f)];
3441
+ else out.scale = va.scale || vb.scale;
3442
+ out.origin = va.origin || vb.origin;
3443
+ return out;
3444
+ }
3445
+ function evalTransformValue(v, t) {
3446
+ if (v === void 0 || v === null) return IDENTITY;
3447
+ if (typeof v === "string") return parseTransformString(v);
3448
+ if (v.keyframes) return partsToMatrix(interpParts(v.keyframes, t));
3449
+ if (v.value) return partsToMatrix(v.value);
3450
+ return IDENTITY;
3451
+ }
3452
+ function nodeMatrix(node, t) {
3453
+ if (node.animate && node.animate.transform) return evalTransformValue(node.animate.transform, t);
3454
+ if (node.transform !== void 0) return evalTransformValue(node.transform, t);
3455
+ return IDENTITY;
3456
+ }
3457
+ function evalScalar(animated, staticVal, fallback, t) {
3458
+ var _a, _b, _c, _d, _e, _f;
3459
+ if (animated && animated.keyframes && animated.keyframes.length) {
3460
+ const kfs = animated.keyframes;
3461
+ if (t <= ((_a = kfs[0].time) != null ? _a : 0)) return kfs[0].value;
3462
+ if (t >= ((_b = kfs[kfs.length - 1].time) != null ? _b : 0)) return kfs[kfs.length - 1].value;
3463
+ let i = 0;
3464
+ while (i < kfs.length - 1 && ((_c = kfs[i + 1].time) != null ? _c : 0) < t) i++;
3465
+ const a = kfs[i], b = kfs[i + 1];
3466
+ const f = (t - ((_d = a.time) != null ? _d : 0)) / (((_e = b.time) != null ? _e : 0) - ((_f = a.time) != null ? _f : 0) || 1);
3467
+ return lerp(a.value, b.value, f);
3468
+ }
3469
+ return staticVal !== void 0 ? Number(staticVal) : fallback;
3470
+ }
3471
+ var CONTAINER_TYPES = /* @__PURE__ */ new Set(["svg", "g", "symbol"]);
3472
+ var SKIP_TYPES = /* @__PURE__ */ new Set(["defs", "mask", "clipPath", "title"]);
3473
+ function buildIdMap(node, map) {
3474
+ var _a;
3475
+ if (typeof node.id === "string") map.set(node.id, node);
3476
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => buildIdMap(c, map));
3477
+ }
3478
+ function num(v) {
3479
+ return v === void 0 || v === null ? 0 : Number(v);
3480
+ }
3481
+ function round(n) {
3482
+ return Math.round(n * 100) / 100 + 0;
3483
+ }
3484
+ function geomKey(node) {
3485
+ var _a;
3486
+ switch (node.type) {
3487
+ case "rect":
3488
+ return num(node.width) + "," + num(node.height) + "," + num(node.x) + "," + num(node.y);
3489
+ case "ellipse":
3490
+ return num(node.rx) + "," + num(node.ry) + "," + num(node.cx) + "," + num(node.cy);
3491
+ case "circle":
3492
+ return num(node.r) + "," + num(node.cx) + "," + num(node.cy);
3493
+ case "path":
3494
+ return String((_a = node.d) != null ? _a : "");
3495
+ default:
3496
+ return "";
3497
+ }
3498
+ }
3499
+ function describePrimitive(node, m, t) {
3500
+ var _a, _b, _c, _d, _e;
3501
+ const fill = (_a = node.fill) != null ? _a : "";
3502
+ const stroke = (_b = node.stroke) != null ? _b : "";
3503
+ const sw = (_d = (_c = node["stroke-width"]) != null ? _c : node.strokeWidth) != null ? _d : "";
3504
+ const opacity = round(evalScalar((_e = node.animate) == null ? void 0 : _e.opacity, node.opacity, 1, t));
3505
+ const masked = node.mask ? 1 : 0;
3506
+ const mat = m.map(round).join(",");
3507
+ return node.type + "|" + geomKey(node) + "|[" + mat + "]|f:" + fill + "|s:" + stroke + "|sw:" + (num(sw) || "") + "|o:" + opacity + "|m:" + masked;
3508
+ }
3509
+ function flatten(node, parent, t, idMap, out) {
3510
+ var _a;
3511
+ const type = node.type || "";
3512
+ if (SKIP_TYPES.has(type)) return;
3513
+ const m = mul(parent, nodeMatrix(node, t));
3514
+ if (type === "use") {
3515
+ const targetId = typeof node.href === "string" ? node.href.replace(/^#/, "") : "";
3516
+ const target = idMap.get(targetId);
3517
+ const useM = mul(m, translateM(num(node.x), num(node.y)));
3518
+ if (target) flatten(target, useM, t, idMap, out);
3519
+ else out.push("UNRESOLVED_USE:#" + targetId);
3520
+ return;
3521
+ }
3522
+ if (CONTAINER_TYPES.has(type)) {
3523
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => flatten(c, m, t, idMap, out));
3524
+ return;
3525
+ }
3526
+ out.push(describePrimitive(node, m, t));
3527
+ }
3528
+ function collectSampleTimes(node, into) {
3529
+ var _a;
3530
+ into.add(0);
3531
+ const scanAnim = (anim) => {
3532
+ var _a2;
3533
+ if (!anim || typeof anim !== "object") return;
3534
+ for (const key of Object.keys(anim)) {
3535
+ const kfs = (_a2 = anim[key]) == null ? void 0 : _a2.keyframes;
3536
+ if (Array.isArray(kfs)) kfs.forEach((kf) => {
3537
+ var _a3;
3538
+ return into.add((_a3 = kf.time) != null ? _a3 : 0);
3539
+ });
3540
+ }
3541
+ };
3542
+ scanAnim(node.animate);
3543
+ if (node.transform && typeof node.transform === "object" && node.transform.keyframes) {
3544
+ node.transform.keyframes.forEach((kf) => {
3545
+ var _a2;
3546
+ return into.add((_a2 = kf.time) != null ? _a2 : 0);
3547
+ });
3548
+ }
3549
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => collectSampleTimes(c, into));
3550
+ }
3551
+ function visualModelAt(root, t) {
3552
+ const idMap = /* @__PURE__ */ new Map();
3553
+ buildIdMap(root, idMap);
3554
+ const out = [];
3555
+ flatten(root, IDENTITY, t, idMap, out);
3556
+ return out.sort();
3557
+ }
3558
+ function diffInEffect(a, b) {
3559
+ const times = /* @__PURE__ */ new Set();
3560
+ collectSampleTimes(a, times);
3561
+ collectSampleTimes(b, times);
3562
+ const diffs = [];
3563
+ for (const t of Array.from(times).sort((x, y) => x - y)) {
3564
+ const ma = visualModelAt(a, t);
3565
+ const mb = visualModelAt(b, t);
3566
+ const onlyInA = subtractMultiset(ma, mb);
3567
+ const onlyInB = subtractMultiset(mb, ma);
3568
+ if (onlyInA.length || onlyInB.length) diffs.push({ time: t, onlyInA, onlyInB });
3569
+ }
3570
+ return diffs;
3571
+ }
3572
+ function subtractMultiset(a, b) {
3573
+ const counts = /* @__PURE__ */ new Map();
3574
+ for (const x of b) counts.set(x, (counts.get(x) || 0) + 1);
3575
+ const extra = [];
3576
+ for (const x of a) {
3577
+ const c = counts.get(x) || 0;
3578
+ if (c > 0) counts.set(x, c - 1);
3579
+ else extra.push(x);
3580
+ }
3581
+ return extra;
3582
+ }
1725
3583
  // Annotate the CommonJS export names for ESM import in node:
1726
3584
  0 && (module.exports = {
1727
3585
  COLOUR_ATTR_NAMES,
3586
+ PX_ANIMATOR_DATA_KEY,
1728
3587
  PX_ANIM_ATTR_NAME,
1729
3588
  PX_ANIM_SRC_ATTR_NAME,
3589
+ PX_TRANSFORM_PART_KEYS,
3590
+ PxAnimatedSvgDocumentSchema,
3591
+ PxAnimationDefinitionSchema,
3592
+ PxAnimatorConfigSchema,
3593
+ PxAttrValueSchema,
3594
+ PxBezierPathSchema,
3595
+ PxBindingSchema,
3596
+ PxDefsSchema,
3597
+ PxEasingOrRefSchema,
3598
+ PxEffectsSchema,
3599
+ PxElementAnimationSchema,
3600
+ PxKeyframeSchema,
3601
+ PxLoopSchema,
3602
+ PxMaskedByEffectSchema,
3603
+ PxNodeBase,
3604
+ PxNodeSchema,
3605
+ PxPropertyAnimationSchema,
3606
+ PxRefEffectSchema,
3607
+ PxRepeaterEffectSchema,
3608
+ PxRetimeEffectSchema,
3609
+ PxSvgNodeExtra,
3610
+ PxTransformPartsSchema,
3611
+ PxTransformValueSchema,
3612
+ PxTransformationEffectSchema,
3613
+ PxTriggerSchema,
3614
+ PxTrimPathEffectSchema,
1730
3615
  STYLE_ATTR_NAMES,
1731
3616
  TRANSFORM_FN_NAMES,
3617
+ applyPlayerEffects,
1732
3618
  calcAnimationValues,
1733
3619
  camelCaseToKebabWordIfNeeded,
3620
+ collectSampleTimes,
1734
3621
  createAnimator,
1735
3622
  createAnimatorImpl,
1736
3623
  createBasicFrameLoopAnimator,
1737
3624
  createFrameLoopAnimator,
1738
3625
  createWebApiAnimator,
3626
+ describeSchema,
3627
+ diffInEffect,
1739
3628
  generateNewIds,
1740
3629
  getAnimatorConfig,
1741
3630
  getBindings,
@@ -1746,8 +3635,12 @@ if (typeof window !== "undefined") {
1746
3635
  isPxElementFileFormatDeep,
1747
3636
  loadTagAnimators,
1748
3637
  normalizeDocument,
3638
+ px,
1749
3639
  renderNode,
3640
+ schemaKeys,
1750
3641
  setupAnimationTriggers,
1751
- toRGBA
3642
+ toRGBA,
3643
+ validateNodeEffects,
3644
+ visualModelAt
1752
3645
  });
1753
3646
  //# sourceMappingURL=index.cjs.map