@pixodesk/svg-animator-web 1.0.15 → 1.0.16

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.js CHANGED
@@ -4,7 +4,6 @@ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
6
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
- var __pow = Math.pow;
8
7
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
8
  var __spreadValues = (a, b) => {
10
9
  for (var prop in b || (b = {}))
@@ -34,29 +33,29 @@ var __objRest = (source, exclude) => {
34
33
  // src/effects/transformParts.ts
35
34
  function partsRecord(part, value, origin) {
36
35
  const rec = {};
37
- if (part === "translate") rec.translate = value;
38
- else if (part === "rotate") rec.rotate = value;
36
+ if (part === "translate" /* Translate */) rec.translate = value;
37
+ else if (part === "rotate" /* Rotate */) rec.rotate = value;
39
38
  else rec.scale = value;
40
- if (origin && part !== "translate") rec.origin = origin;
39
+ if (origin && part !== "translate" /* Translate */) rec.origin = origin;
41
40
  return rec;
42
41
  }
43
42
  function readAnimatable(raw) {
44
- if (raw === void 0) return { kind: "absent" };
45
- if (Array.isArray(raw)) return { kind: "static", value: raw };
43
+ if (raw === void 0) return { kind: "absent" /* Absent */ };
44
+ if (Array.isArray(raw)) return { kind: "static" /* Static */, value: raw };
46
45
  if (typeof raw === "object") {
47
46
  const obj = raw;
48
47
  if (obj.keyframes) {
49
- return { kind: "animated", keyframes: obj.keyframes, autoOrient: obj.autoOrient };
48
+ return { kind: "animated" /* Animated */, keyframes: obj.keyframes, autoOrient: obj.autoOrient };
50
49
  }
51
- if (obj.value !== void 0) return { kind: "static", value: obj.value };
50
+ if (obj.value !== void 0) return { kind: "static" /* Static */, value: obj.value };
52
51
  }
53
- return { kind: "static", value: raw };
52
+ return { kind: "static" /* Static */, value: raw };
54
53
  }
55
54
  function readStaticOrigin(raw, ctx) {
56
55
  var _a;
57
56
  const o = readAnimatable(raw);
58
- if (o.kind === "absent") return void 0;
59
- if (o.kind === "static") return o.value;
57
+ if (o.kind === "absent" /* Absent */) return void 0;
58
+ if (o.kind === "static" /* Static */) return o.value;
60
59
  ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
61
60
  return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
62
61
  }
@@ -74,15 +73,15 @@ function applyTransformationEffect(node, fx, ctx) {
74
73
  if (!fx) return node;
75
74
  delete node.transform;
76
75
  let n = node;
77
- n = wrapTransformPart(n, "skew", fx.skew, ctx);
76
+ n = wrapTransformPart(n, "skew" /* Skew */, fx.skew, ctx);
78
77
  n = wrapOrigin(
79
78
  n,
80
79
  fx.origin,
81
80
  /*invert=*/
82
81
  true
83
82
  );
84
- n = wrapTransformPart(n, "scale", normalizeScale(fx.scale), ctx);
85
- n = wrapTransformPart(n, "rotate", fx.rotate, ctx);
83
+ n = wrapTransformPart(n, "scale" /* Scale */, normalizeScale(fx.scale), ctx);
84
+ n = wrapTransformPart(n, "rotate" /* Rotate */, fx.rotate, ctx);
86
85
  n = wrapOrigin(
87
86
  n,
88
87
  fx.origin,
@@ -96,7 +95,7 @@ function applyTransformationEffect(node, fx, ctx) {
96
95
  /*invert=*/
97
96
  true
98
97
  );
99
- n = wrapTransformPart(n, "translate", fx.translate, ctx);
98
+ n = wrapTransformPart(n, "translate" /* Translate */, fx.translate, ctx);
100
99
  n = wrapOrigin(
101
100
  n,
102
101
  fx.origin,
@@ -104,7 +103,7 @@ function applyTransformationEffect(node, fx, ctx) {
104
103
  false
105
104
  );
106
105
  } else {
107
- n = wrapTransformPart(n, "translate", fx.translate, ctx);
106
+ n = wrapTransformPart(n, "translate" /* Translate */, fx.translate, ctx);
108
107
  }
109
108
  return n;
110
109
  }
@@ -121,19 +120,19 @@ function normalizeScale(raw) {
121
120
  }
122
121
  function wrapTransformPart(inner, part, raw, ctx) {
123
122
  if (raw === void 0) return inner;
124
- if (part === "skew") {
123
+ if (part === "skew" /* Skew */) {
125
124
  const skew = readAnimatable(raw);
126
- if (skew.kind !== "static") {
125
+ if (skew.kind !== "static" /* Static */) {
127
126
  ctx.warnings.push("transformation.skew: only static skew is supported");
128
127
  return inner;
129
128
  }
130
129
  return { type: "g", transform: "skewX(" + skew.value[0] + ")skewY(" + skew.value[1] + ")", children: [inner] };
131
130
  }
132
131
  const v = readAnimatable(raw);
133
- if (v.kind === "static") {
132
+ if (v.kind === "static" /* Static */) {
134
133
  return { type: "g", transform: { value: partsRecord(part, v.value, void 0) }, children: [inner] };
135
134
  }
136
- if (v.kind === "animated") {
135
+ if (v.kind === "animated" /* Animated */) {
137
136
  const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
138
137
  if (v.autoOrient) animTr.autoOrient = true;
139
138
  return {
@@ -148,12 +147,12 @@ function wrapOrigin(inner, raw, invert) {
148
147
  if (raw === void 0) return inner;
149
148
  const v = readAnimatable(raw);
150
149
  const sign = (value) => invert ? [-value[0], -value[1]] : value;
151
- if (v.kind === "absent") return inner;
152
- if (v.kind === "static") {
150
+ if (v.kind === "absent" /* Absent */) return inner;
151
+ if (v.kind === "static" /* Static */) {
153
152
  if (v.value[0] === 0 && v.value[1] === 0) return inner;
154
153
  return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
155
154
  }
156
- if (v.kind === "animated") {
155
+ if (v.kind === "animated" /* Animated */) {
157
156
  return {
158
157
  type: "g",
159
158
  animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) } },
@@ -192,7 +191,7 @@ function splitForContentRef(node, transformation, originalId, innerId, ctx) {
192
191
  return outerWrapper;
193
192
  }
194
193
  function liftBodyTranslate(node, transformation) {
195
- var _a;
194
+ var _a, _b, _c;
196
195
  const out = {};
197
196
  let didLiftAnimate = false;
198
197
  const animTr = (_a = node.animate) == null ? void 0 : _a.transform;
@@ -243,6 +242,7 @@ function liftBodyTranslate(node, transformation) {
243
242
  }
244
243
  const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
245
244
  const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
245
+ const liftedAnimateIsAutoOriented = didLiftAnimate && needsOriginOnOuter(((_b = node.animate) == null ? void 0 : _b.transform) || void 0) || didLiftAnimate && needsOriginOnOuter(((_c = out.animate) == null ? void 0 : _c.transform) || void 0);
246
246
  if (typeof node.transform === "string") {
247
247
  const split = splitTransformString(node.transform);
248
248
  if (stripBodyTranslateOnly) {
@@ -251,6 +251,8 @@ function liftBodyTranslate(node, transformation) {
251
251
  else delete node.transform;
252
252
  } else if (isPureTranslateBody(node.transform)) {
253
253
  delete node.transform;
254
+ } else if (liftedAnimateIsAutoOriented && isSingleMatrixBody(node.transform)) {
255
+ delete node.transform;
254
256
  }
255
257
  } else if (split.translate) {
256
258
  out.transform = split.translate;
@@ -260,6 +262,17 @@ function liftBodyTranslate(node, transformation) {
260
262
  }
261
263
  return out;
262
264
  }
265
+ function isSingleMatrixBody(s) {
266
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
267
+ let count = 0;
268
+ let isMatrix = false;
269
+ let m;
270
+ while (m = re.exec(s)) {
271
+ count++;
272
+ if (m[1] === "matrix") isMatrix = true;
273
+ }
274
+ return count === 1 && isMatrix;
275
+ }
263
276
  function isPureTranslateBody(s) {
264
277
  const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
265
278
  const ops = [];
@@ -342,294 +355,6 @@ function needsOriginOnOuter(translateAnim) {
342
355
  return false;
343
356
  }
344
357
 
345
- // src/effects/util.ts
346
- function genId(ctx, prefix) {
347
- return "_lw_" + prefix + "_" + ctx.nextId++;
348
- }
349
- function indexById(node, map) {
350
- var _a;
351
- if (typeof node.id === "string") map.set(node.id, node);
352
- (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
353
- }
354
- function spliceDefs(root, defs) {
355
- if (!defs.length) return;
356
- const existing = root.children || (root.children = []);
357
- existing.unshift({ type: "defs", children: defs });
358
- }
359
- function clone(value) {
360
- if (value === null || typeof value !== "object") return value;
361
- if (Array.isArray(value)) return value.map(clone);
362
- const out = {};
363
- for (const k of Object.keys(value)) out[k] = clone(value[k]);
364
- return out;
365
- }
366
- function regenerateIdsInClone(root, ctx) {
367
- const oldToNew = /* @__PURE__ */ new Map();
368
- const walkAssign = (n) => {
369
- var _a;
370
- if (typeof n.id === "string") {
371
- const newId = genId(ctx, "retimed");
372
- oldToNew.set(n.id, newId);
373
- n.id = newId;
374
- }
375
- (_a = n.children) == null ? void 0 : _a.forEach(walkAssign);
376
- };
377
- walkAssign(root);
378
- const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
379
- const newId = oldToNew.get(oldId);
380
- return newId ? "url(#" + newId + ")" : m;
381
- });
382
- const walkRewrite = (n) => {
383
- var _a;
384
- if (typeof n.href === "string" && n.href.startsWith("#")) {
385
- const newId = oldToNew.get(n.href.slice(1));
386
- if (newId) n.href = "#" + newId;
387
- }
388
- for (const k of Object.keys(n)) {
389
- if (k === "children" || k === "effects" || k === "meta" || k === "href" || k === "id") continue;
390
- const v = n[k];
391
- if (typeof v === "string" && v.indexOf("url(#") !== -1) n[k] = rewriteUrl(v);
392
- }
393
- (_a = n.children) == null ? void 0 : _a.forEach(walkRewrite);
394
- };
395
- walkRewrite(root);
396
- return oldToNew;
397
- }
398
-
399
- // src/effects/maskedByEffect.ts
400
- function applyMaskedByEffect(node, fx, transformation, ctx) {
401
- if (!fx) return node;
402
- if (!fx.href) {
403
- ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
404
- return node;
405
- }
406
- const maskId = genId(ctx, "mask");
407
- let content = { type: "use", href: "#" + fx.href };
408
- content = wrapInverseTransform(content, transformation, ctx);
409
- const mask = { type: "mask", id: maskId, children: [content] };
410
- if (fx.maskType) mask.maskType = fx.maskType;
411
- if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
412
- if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
413
- ctx.defs.push(mask);
414
- node.mask = "url(#" + maskId + ")";
415
- return node;
416
- }
417
- function wrapInverseTransform(inner, fx, ctx) {
418
- if (!fx) return inner;
419
- const origin = readStaticOrigin(fx.origin, ctx);
420
- let n = inner;
421
- n = wrapInversePart(n, "translate", fx.translate, void 0, ctx);
422
- n = wrapInversePart(n, "rotate", fx.rotate, origin, ctx);
423
- n = wrapInversePart(n, "scale", fx.scale, origin, ctx);
424
- return n;
425
- }
426
- function wrapInversePart(inner, part, raw, origin, ctx) {
427
- if (raw === void 0) return inner;
428
- const v = readAnimatable(raw);
429
- if (v.kind === "static") {
430
- return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
431
- }
432
- if (v.kind === "animated") {
433
- return {
434
- type: "g",
435
- animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
436
- children: [inner]
437
- };
438
- }
439
- return inner;
440
- }
441
- function invertPartValue(part, value) {
442
- if (part === "translate") return [-value[0], -value[1]];
443
- if (part === "rotate") return -value;
444
- return [1 / value[0], 1 / value[1]];
445
- }
446
-
447
- // src/effects/refEffect.ts
448
- var CONTENT_SUBREF = "content";
449
- function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
450
- if (ref) {
451
- const baseId = ref.baseId;
452
- if (!baseId) {
453
- ctx.errors.push("ref: missing baseId");
454
- } else {
455
- const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
456
- node.href = "#" + targetId;
457
- }
458
- }
459
- return applyTransformationEffect(node, transformation, ctx);
460
- }
461
-
462
- // src/effects/repeaterEffect.ts
463
- function applyRepeaterEffect(node, fx, ctx) {
464
- var _a, _b;
465
- if (!fx) return node;
466
- const copies = (_a = fx.copies) != null ? _a : 1;
467
- if (copies < 1) {
468
- ctx.errors.push("repeater.copies invalid: " + fx.copies);
469
- return node;
470
- }
471
- const sharedTransform = node.transform;
472
- const sharedAnimTransform = (_b = node.animate) == null ? void 0 : _b.transform;
473
- const base = clone(node);
474
- delete base.transform;
475
- if (base.animate) {
476
- delete base.animate.transform;
477
- if (Object.keys(base.animate).length === 0) delete base.animate;
478
- }
479
- const children = [base];
480
- for (let i = 1; i < copies; i++) {
481
- const copy = clone(base);
482
- copy.transform = { value: perCopyParts(fx, i) };
483
- children.push(copy);
484
- }
485
- const wrapper = { type: "g", children };
486
- if (sharedTransform !== void 0) wrapper.transform = sharedTransform;
487
- if (sharedAnimTransform !== void 0) wrapper.animate = { transform: sharedAnimTransform };
488
- return wrapper;
489
- }
490
- function perCopyParts(fx, i) {
491
- const parts = {};
492
- if (fx.translate) parts.translate = [fx.translate[0] * i, fx.translate[1] * i];
493
- if (fx.rotate !== void 0) parts.rotate = fx.rotate * i;
494
- if (fx.scale) parts.scale = [__pow(fx.scale[0] / 100, i), __pow(fx.scale[1] / 100, i)];
495
- if (fx.origin) parts.origin = [fx.origin[0] * i, fx.origin[1] * i];
496
- return parts;
497
- }
498
-
499
- // src/effects/retimeEffect.ts
500
- var RETIME_AS_SYMBOL = true;
501
- function applyRetimeEffect(node, retime, ctx) {
502
- var _a, _b, _c, _d;
503
- if (!retime) return node;
504
- const baseId = retime.baseId;
505
- if (!baseId) {
506
- ctx.errors.push("retime: missing baseId");
507
- return node;
508
- }
509
- const target = ctx.idMap.get(baseId);
510
- if (!target) {
511
- ctx.warnings.push('retime: target "' + baseId + '" not found');
512
- return node;
513
- }
514
- const start = (_a = retime.start) != null ? _a : 0;
515
- const stretch = (_b = retime.stretch) != null ? _b : 1;
516
- if (RETIME_AS_SYMBOL) {
517
- const symbolClone = clone(target);
518
- regenerateIdsInClone(symbolClone, ctx);
519
- const cloneId = genId(ctx, "retime");
520
- symbolClone.id = cloneId;
521
- remapKeyframeTimes(symbolClone, start, stretch);
522
- ctx.defs.push(symbolClone);
523
- node.href = "#" + cloneId;
524
- return node;
525
- }
526
- const sourceNodes = target.type === "symbol" ? target.children || [] : [target];
527
- const content = sourceNodes.map((child) => {
528
- const c = clone(child);
529
- regenerateIdsInClone(c, ctx);
530
- remapKeyframeTimes(c, start, stretch);
531
- return c;
532
- });
533
- const g = __spreadProps(__spreadValues({}, node), { type: "g", children: content });
534
- delete g.href;
535
- const x = Number((_c = node.x) != null ? _c : 0), y = Number((_d = node.y) != null ? _d : 0);
536
- if (x || y) {
537
- const offset = "translate(" + x + "," + y + ")";
538
- g.transform = typeof g.transform === "string" ? offset + g.transform : offset;
539
- delete g.x;
540
- delete g.y;
541
- }
542
- return g;
543
- }
544
- function remapKeyframeTimes(node, start, stretch) {
545
- var _a;
546
- const remap2 = (kfs) => {
547
- for (const kf of kfs) if (typeof kf.time === "number") kf.time = start + kf.time * stretch;
548
- };
549
- if (node.transform && typeof node.transform === "object" && Array.isArray(node.transform.keyframes)) {
550
- remap2(node.transform.keyframes);
551
- }
552
- if (node.animate && typeof node.animate === "object") {
553
- for (const prop of Object.keys(node.animate)) {
554
- const anim = node.animate[prop];
555
- if (anim && Array.isArray(anim.keyframes)) remap2(anim.keyframes);
556
- }
557
- }
558
- (_a = node.children) == null ? void 0 : _a.forEach((child) => remapKeyframeTimes(child, start, stretch));
559
- }
560
-
561
- // src/effects/trimPathEffect.ts
562
- function applyTrimPathEffect(node, trimPath, isCombinedShape) {
563
- var _a;
564
- if (!trimPath && !isCombinedShape) return node;
565
- const d = (_a = node.d) != null ? _a : rectToPathD(node);
566
- if (d === void 0) return node;
567
- const g = __spreadProps(__spreadValues({}, node), { type: "g" });
568
- delete g.d;
569
- const meta = g.meta = __spreadValues({}, g.meta || {});
570
- const effects = meta.effects = __spreadValues({}, meta.effects || {});
571
- if (trimPath) effects.trimPath = trimPath;
572
- effects.isCombinedShape = true;
573
- g.children = [{ type: "path", d }];
574
- return g;
575
- }
576
- function rectToPathD(node) {
577
- var _a, _b, _c, _d;
578
- if (node.type !== "rect") return void 0;
579
- const x = Number((_a = node.x) != null ? _a : 0), y = Number((_b = node.y) != null ? _b : 0);
580
- const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
581
- return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
582
- }
583
-
584
- // src/effects/PlayerEffectsUtil.ts
585
- function applyPlayerEffects(root) {
586
- const ctx = {
587
- defs: [],
588
- warnings: [],
589
- errors: [],
590
- idMap: /* @__PURE__ */ new Map(),
591
- nextId: 0,
592
- contentRefInnerIds: /* @__PURE__ */ new Map()
593
- };
594
- const working = clone(root);
595
- indexById(working, ctx.idMap);
596
- identifyContentRefTargets(working, ctx, () => genId(ctx, "inner"));
597
- const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);
598
- const out = applyPlayerEffects_retime(afterPass1, ctx);
599
- spliceDefs(out, ctx.defs);
600
- return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
601
- }
602
- function applyPlayerEffects_exceptRetime(node, ctx) {
603
- if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
604
- const fx = node.effects;
605
- const originalId = typeof node.id === "string" ? node.id : void 0;
606
- const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
607
- if (!fx && !innerIdForContentRef) return node;
608
- const { transformation, repeater, maskedBy, trimPath, retime, ref } = fx != null ? fx : {};
609
- const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
610
- if (fx) delete node.effects;
611
- let n = node;
612
- n = applyTrimPathEffect(n, trimPath, isCombinedShape);
613
- n = applyRepeaterEffect(n, repeater, ctx);
614
- n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
615
- if (innerIdForContentRef) {
616
- n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
617
- } else {
618
- n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
619
- }
620
- if (retime) node.effects = { retime };
621
- if (originalId) ctx.idMap.set(originalId, n);
622
- return n;
623
- }
624
- function applyPlayerEffects_retime(node, ctx) {
625
- var _a;
626
- if (node.children) node.children = node.children.map((child) => applyPlayerEffects_retime(child, ctx));
627
- const retime = (_a = node.effects) == null ? void 0 : _a.retime;
628
- if (!retime) return node;
629
- delete node.effects;
630
- return applyRetimeEffect(node, retime, ctx);
631
- }
632
-
633
358
  // src/PxSchema.ts
634
359
  function pathStr(path) {
635
360
  if (!path.length) return ".";
@@ -1089,6 +814,15 @@ var px = {
1089
814
  // src/PxAnimatorTypes.ts
1090
815
  var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
1091
816
  var PX_ANIM_ATTR_NAME = "_px_animator";
817
+ var PxAnimatorMode = {
818
+ auto: "auto",
819
+ webapi: "webapi",
820
+ frames: "frames"
821
+ };
822
+ var PxAnimatorEngine = {
823
+ webapi: PxAnimatorMode.webapi,
824
+ frames: PxAnimatorMode.frames
825
+ };
1092
826
  var TEXT_ATTR = "text";
1093
827
  var TEXT_CONTENT_ATTR = "textContent";
1094
828
  var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
@@ -1171,7 +905,7 @@ var PxDefsSchema = implementsInterface()(px.object({
1171
905
  styles: px.record(px.any()).optional()
1172
906
  }));
1173
907
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
1174
- mode: px.enum(["auto", "webapi", "frames"]).optional(),
908
+ mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.webapi, PxAnimatorMode.frames]).optional(),
1175
909
  duration: px.number().optional(),
1176
910
  delay: px.number().optional(),
1177
911
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
@@ -1210,47 +944,96 @@ var PxAnimatableVec2Schema = px.union([
1210
944
  autoOrient: px.boolean().optional()
1211
945
  })
1212
946
  ]);
1213
- var PxTransformationEffectSchema = px.object({
947
+ var PxTransformationEffectSchema = implementsInterface()(px.object({
1214
948
  translate: PxAnimatableVec2Schema.optional(),
1215
949
  rotate: PxAnimatableNumberSchema.optional(),
1216
950
  scale: PxAnimatableVec2Schema.optional(),
1217
951
  skew: PxAnimatableVec2Schema.optional(),
1218
952
  origin: PxAnimatableVec2Schema.optional()
1219
- });
1220
- var PxRepeaterEffectSchema = px.object({
953
+ }));
954
+ var PxRepeaterEffectSchema = implementsInterface()(px.object({
1221
955
  copies: px.number().optional(),
1222
- translate: px.tuple([px.number(), px.number()]).optional(),
1223
- rotate: px.number().optional(),
1224
- scale: px.tuple([px.number(), px.number()]).optional(),
1225
- // per-copy scale, PERCENT (85 → 0.85)
1226
- origin: px.tuple([px.number(), px.number()]).optional()
1227
- });
1228
- var PxMaskedByEffectSchema = px.object({
956
+ translate: PxAnimatableVec2Schema.optional(),
957
+ rotate: PxAnimatableNumberSchema.optional(),
958
+ scale: PxAnimatableVec2Schema.optional(),
959
+ origin: PxAnimatableVec2Schema.optional()
960
+ }));
961
+ var PxMaskedByEffectSchema = implementsInterface()(px.object({
1229
962
  href: px.string().optional(),
1230
963
  maskType: px.string().optional(),
1231
964
  maskUnits: px.string().optional(),
1232
965
  maskContentUnits: px.string().optional()
1233
- });
1234
- var PxTrimPathEffectSchema = px.any();
1235
- var PxRetimeEffectSchema = px.object({
966
+ }));
967
+ var PxTrimPathEffectSchema = implementsInterface()(px.object({
968
+ offset: PxAnimatableNumberSchema.optional(),
969
+ range: PxAnimatableVec2Schema.optional(),
970
+ trimAllAsOne: px.boolean().optional()
971
+ }));
972
+ var PxRetimeEffectSchema = implementsInterface()(px.object({
1236
973
  baseId: px.string().optional(),
1237
974
  start: px.number().optional(),
1238
975
  stretch: px.number().optional(),
1239
976
  timeCrop: px.tuple([px.number(), px.number()]).optional()
1240
- });
1241
- var PxRefEffectSchema = px.object({
977
+ }));
978
+ var PxRefEffectSchema = implementsInterface()(px.object({
1242
979
  baseId: px.string().optional(),
1243
980
  type: px.string().optional()
1244
- });
1245
- var PxEffectsSchema = px.object({
981
+ }));
982
+ var PxGradientUnits = {
983
+ userSpaceOnUse: "userSpaceOnUse",
984
+ objectBoundingBox: "objectBoundingBox"
985
+ };
986
+ var PxGradientSpreadMethod = {
987
+ pad: "pad",
988
+ reflect: "reflect",
989
+ repeat: "repeat"
990
+ };
991
+ var PxGradientType = {
992
+ linear: "linear",
993
+ radial: "radial"
994
+ };
995
+ var PxGradientStopSchema = implementsInterface()(px.object({
996
+ offset: px.number(),
997
+ color: px.string()
998
+ }));
999
+ var PxAnimatableGradientStopsSchema = px.union([
1000
+ px.array(PxGradientStopSchema),
1001
+ px.object({ value: px.array(PxGradientStopSchema) }),
1002
+ px.object({ keyframes: px.array(PxKeyframeSchema) })
1003
+ ]);
1004
+ var PxFillGradientEffectSchema = implementsInterface()(px.object({
1005
+ type: px.enum([PxGradientType.linear, PxGradientType.radial]),
1006
+ p1: px.tuple([px.number(), px.number()]).optional(),
1007
+ p2: px.tuple([px.number(), px.number()]).optional(),
1008
+ c: px.tuple([px.number(), px.number()]).optional(),
1009
+ r: px.number().optional(),
1010
+ fp: px.tuple([px.number(), px.number()]).optional(),
1011
+ stops: PxAnimatableGradientStopsSchema.optional(),
1012
+ gradientUnits: px.string().optional(),
1013
+ spreadMethod: px.string().optional(),
1014
+ gradientTransform: px.string().optional()
1015
+ }));
1016
+ var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
1017
+ var PxTextAlongPathEffectSchema = implementsInterface()(px.object({
1018
+ href: px.string(),
1019
+ lengthAdjust: px.string().optional(),
1020
+ method: px.string().optional(),
1021
+ spacing: px.string().optional(),
1022
+ startOffset: PxAnimatableNumberSchema.optional(),
1023
+ textLength: PxAnimatableNumberSchema.optional()
1024
+ }));
1025
+ var PxEffectsSchema = implementsInterface()(px.object({
1246
1026
  transformation: PxTransformationEffectSchema.optional(),
1247
1027
  repeater: PxRepeaterEffectSchema.optional(),
1248
1028
  maskedBy: PxMaskedByEffectSchema.optional(),
1249
1029
  trimPath: PxTrimPathEffectSchema.optional(),
1250
1030
  retime: PxRetimeEffectSchema.optional(),
1251
1031
  isCombinedShape: px.boolean().optional(),
1252
- ref: PxRefEffectSchema.optional()
1253
- });
1032
+ ref: PxRefEffectSchema.optional(),
1033
+ fillGradient: PxFillGradientEffectSchema.optional(),
1034
+ strokeGradient: PxStrokeGradientEffectSchema.optional(),
1035
+ textAlongPath: PxTextAlongPathEffectSchema.optional()
1036
+ }));
1254
1037
  function validateNodeEffects(root, opts) {
1255
1038
  const warnings = [];
1256
1039
  const walk = (node, path) => {
@@ -1276,7 +1059,10 @@ var PxNodeBase = px.openObject({
1276
1059
  // Consumed and removed by `applyPlayerEffects` before any other normalisation
1277
1060
  // (see `createAnimatorImpl`), so downstream code never sees it.
1278
1061
  effects: PxEffectsSchema.optional(),
1279
- animate: PxAnimationDefinitionSchema.optional(),
1062
+ // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
1063
+ // string ref / array of refs / inline definition / mixed array; mirrors
1064
+ // `animator.animate` map values and what `processNode` resolves at runtime.
1065
+ animate: PxElementAnimationSchema.optional(),
1280
1066
  style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
1281
1067
  }, PxAttrValueSchema);
1282
1068
  var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
@@ -1329,784 +1115,1586 @@ function getChildren(doc) {
1329
1115
  return doc == null ? void 0 : doc.children;
1330
1116
  }
1331
1117
 
1332
- // src/PxAnimatorUtil.ts
1333
- function bezierToSvgPath(path) {
1334
- var _a, _b, _c, _d;
1335
- const v = path.v;
1336
- const i = path.i;
1337
- const o = path.o;
1338
- const c = path.c;
1339
- if (!v.length) return "";
1340
- const d = [];
1341
- const len = v.length;
1342
- d.push("M" + v[0][0] + "," + v[0][1]);
1343
- for (let idx = 1; idx < len; idx++) {
1344
- const prevV = v[idx - 1];
1345
- const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
1346
- const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
1347
- const currV = v[idx];
1348
- const isLine = prevO[0] === prevV[0] && prevO[1] === prevV[1] && (currI[0] === currV[0] && currI[1] === currV[1]);
1349
- if (isLine) {
1350
- d.push("L" + currV[0] + "," + currV[1]);
1351
- } else {
1352
- d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
1118
+ // src/PxNodeCloneUtil.ts
1119
+ function deepClonePxNode(value) {
1120
+ if (value === null || typeof value !== "object") return value;
1121
+ if (Array.isArray(value)) return value.map(deepClonePxNode);
1122
+ const out = {};
1123
+ for (const k of Object.keys(value)) out[k] = deepClonePxNode(value[k]);
1124
+ return out;
1125
+ }
1126
+ function regenerateIdsAndRewriteRefs(root, genId3) {
1127
+ const oldToNew = /* @__PURE__ */ new Map();
1128
+ const walkAssign = (n) => {
1129
+ var _a;
1130
+ if (typeof n.id === "string") {
1131
+ const newId = genId3();
1132
+ oldToNew.set(n.id, newId);
1133
+ n.id = newId;
1353
1134
  }
1354
- }
1355
- if (c && len > 0) {
1356
- const lastV = v[len - 1];
1357
- const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
1358
- const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
1359
- const firstV = v[0];
1360
- const isLine = lastO[0] === lastV[0] && lastO[1] === lastV[1] && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
1361
- if (!isLine) {
1362
- d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
1135
+ (_a = n.children) == null ? void 0 : _a.forEach(walkAssign);
1136
+ };
1137
+ walkAssign(root);
1138
+ const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
1139
+ const newId = oldToNew.get(oldId);
1140
+ return newId ? "url(#" + newId + ")" : m;
1141
+ });
1142
+ const walkRewrite = (n) => {
1143
+ var _a;
1144
+ if (typeof n.href === "string" && n.href.startsWith("#")) {
1145
+ const newId = oldToNew.get(n.href.slice(1));
1146
+ if (newId) n.href = "#" + newId;
1363
1147
  }
1364
- d.push("z");
1365
- }
1366
- return d.join("");
1148
+ for (const k of Object.keys(n)) {
1149
+ if (k === "children" || k === "effects" || k === "meta" || k === "animate" || k === "href" || k === "id") continue;
1150
+ const v = n[k];
1151
+ if (typeof v === "string" && v.indexOf("url(#") !== -1) {
1152
+ n[k] = rewriteUrl(v);
1153
+ }
1154
+ }
1155
+ (_a = n.children) == null ? void 0 : _a.forEach(walkRewrite);
1156
+ };
1157
+ walkRewrite(root);
1158
+ return oldToNew;
1367
1159
  }
1368
- function interpolateNum(a, b, t) {
1369
- return a + (b - a) * t;
1160
+ function toFiniteNum(v) {
1161
+ const n = typeof v === "number" ? v : typeof v === "string" ? parseFloat(v) : NaN;
1162
+ return Number.isFinite(n) ? n : 0;
1370
1163
  }
1371
- function interpolateVec(a, b, t) {
1372
- const res = [];
1373
- const count = Math.max(a.length, b.length);
1374
- for (let i = 0; i < count; i++) {
1375
- res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
1164
+ function applyUseOffsetToG(gNode) {
1165
+ var _a;
1166
+ const x = toFiniteNum(gNode.x);
1167
+ const y = toFiniteNum(gNode.y);
1168
+ delete gNode.x;
1169
+ delete gNode.y;
1170
+ if (!x && !y) return gNode;
1171
+ const offset = "translate(" + x + "," + y + ")";
1172
+ const carriesTransform = gNode.transform !== void 0 || gNode.animate !== void 0;
1173
+ if (carriesTransform) {
1174
+ const inner = { type: "g", transform: offset, children: (_a = gNode.children) != null ? _a : [] };
1175
+ gNode.children = [inner];
1176
+ } else {
1177
+ gNode.transform = offset;
1376
1178
  }
1377
- return res;
1179
+ return gNode;
1378
1180
  }
1379
- function interpolateColor(a, b, t) {
1380
- return [
1381
- interpolateNum(a[0] || 0, b[0] || 0, t),
1382
- interpolateNum(a[1] || 0, b[1] || 0, t),
1383
- interpolateNum(a[2] || 0, b[2] || 0, t),
1384
- interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
1385
- ];
1181
+
1182
+ // src/effects/util.ts
1183
+ function genId(ctx, prefix) {
1184
+ return "_lw_" + prefix + "_" + ctx.nextId++;
1386
1185
  }
1387
- function interpolateBeziers(paths1, paths2, progress) {
1388
- const count = Math.max(paths1.length, paths2.length);
1389
- const res = [];
1390
- for (let i = 0; i < count; i++) {
1391
- res.push(interpolateBezier(paths1[i], paths2[i], progress));
1392
- }
1393
- return res;
1186
+ function stripHash(href) {
1187
+ return typeof href === "string" ? href.replace(/^#/, "") : void 0;
1394
1188
  }
1395
- function interpolateBezier(path1, path2, progress) {
1396
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1397
- if (!path1 || !path2) return path1 || path2 || { v: [] };
1398
- const t = Math.min(Math.max(progress, 0), 1);
1399
- const len = Math.min(path1.v.length, path2.v.length);
1400
- const v = [];
1401
- const i = [];
1402
- const o = [];
1403
- for (let idx = 0; idx < len; idx++) {
1404
- const v1 = path1.v[idx];
1405
- const v2 = path2.v[idx];
1406
- v.push(interpolateVec(v1, v2, t));
1407
- const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
1408
- const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
1409
- i.push(interpolateVec(i1, i2, t));
1410
- const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
1411
- const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
1412
- o.push(interpolateVec(o1, o2, t));
1413
- }
1414
- return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
1189
+ function indexById(node, map) {
1190
+ var _a;
1191
+ if (typeof node.id === "string") map.set(node.id, node);
1192
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
1415
1193
  }
1416
- function remap(value, inMin, inMax, outMin, outMax) {
1417
- if (inMax === inMin) return outMin;
1418
- const t = (value - inMin) / (inMax - inMin);
1419
- return outMin + t * (outMax - outMin);
1194
+ function spliceDefs(root, defs) {
1195
+ if (!defs.length) return;
1196
+ const existing = root.children || (root.children = []);
1197
+ existing.unshift({ type: "defs", children: defs });
1420
1198
  }
1421
- function solveCubicBezierX(p1x, p2x, x) {
1422
- if (x <= 0) return 0;
1423
- if (x >= 1) return 1;
1424
- const cx = 3 * p1x;
1425
- const bx = 3 * (p2x - p1x) - cx;
1426
- const ax = 1 - cx - bx;
1427
- function sampleX(t) {
1428
- return ((ax * t + bx) * t + cx) * t;
1429
- }
1430
- function sampleDX(t) {
1431
- return (3 * ax * t + 2 * bx) * t + cx;
1199
+ var clone = deepClonePxNode;
1200
+ function regenerateIdsInClone(root, ctx) {
1201
+ return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
1202
+ }
1203
+
1204
+ // src/effects/gradientEffect.ts
1205
+ function applyFillGradientEffect(node, fx, ctx) {
1206
+ return applyGradient(node, fx, ctx, "fill");
1207
+ }
1208
+ function applyStrokeGradientEffect(node, fx, ctx) {
1209
+ return applyGradient(node, fx, ctx, "stroke");
1210
+ }
1211
+ function applyGradient(node, fx, ctx, attr) {
1212
+ if (!fx) return node;
1213
+ const id = genId(ctx, "grad");
1214
+ const def = synthesiseGradientDef(fx, id, ctx);
1215
+ ctx.defs.push(def);
1216
+ node[attr] = "url(#" + id + ")";
1217
+ return node;
1218
+ }
1219
+ function synthesiseGradientDef(fx, id, ctx) {
1220
+ const out = {
1221
+ type: fx.type === PxGradientType.radial ? "radialGradient" : "linearGradient",
1222
+ id
1223
+ };
1224
+ if (fx.type === PxGradientType.linear) {
1225
+ if (fx.p1) {
1226
+ out.x1 = String(fx.p1[0]);
1227
+ out.y1 = String(fx.p1[1]);
1228
+ }
1229
+ if (fx.p2) {
1230
+ out.x2 = String(fx.p2[0]);
1231
+ out.y2 = String(fx.p2[1]);
1232
+ }
1233
+ } else {
1234
+ if (fx.c) {
1235
+ out.cx = String(fx.c[0]);
1236
+ out.cy = String(fx.c[1]);
1237
+ }
1238
+ if (fx.r !== void 0) out.r = String(fx.r);
1239
+ if (fx.fp) {
1240
+ out.fx = String(fx.fp[0]);
1241
+ out.fy = String(fx.fp[1]);
1242
+ }
1432
1243
  }
1433
- let t2 = x;
1434
- let t0 = 0;
1435
- let t1 = 1;
1436
- for (let i = 0; i < 8; i++) {
1437
- const x2 = sampleX(t2) - x;
1438
- if (Math.abs(x2) < 1e-6) return t2;
1439
- const d2 = sampleDX(t2);
1440
- if (Math.abs(d2) < 1e-6) break;
1441
- t2 -= x2 / d2;
1244
+ if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
1245
+ if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
1246
+ if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;
1247
+ out.children = buildStopChildren(fx.stops, ctx);
1248
+ return out;
1249
+ }
1250
+ function buildStopChildren(stops, ctx) {
1251
+ var _a, _b, _c, _d;
1252
+ if (!stops) return [];
1253
+ if (Array.isArray(stops)) return stops.map(staticStopNode);
1254
+ if (typeof stops === "object" && Array.isArray(stops.value)) {
1255
+ return stops.value.map(staticStopNode);
1256
+ }
1257
+ const animBlock = stops;
1258
+ const kfs = animBlock.keyframes;
1259
+ if (!Array.isArray(kfs) || !kfs.length) return [];
1260
+ let stopCount = 0;
1261
+ for (const kf of kfs) {
1262
+ const v = (_a = kf.value) != null ? _a : kf.v;
1263
+ if (Array.isArray(v) && v.length > stopCount) stopCount = v.length;
1442
1264
  }
1443
- t2 = x;
1444
- while (t0 < t1) {
1445
- const x2 = sampleX(t2);
1446
- if (Math.abs(x2 - x) < 1e-6) return t2;
1447
- if (x > x2) t0 = t2;
1448
- else t1 = t2;
1449
- t2 = (t1 + t0) / 2;
1265
+ if (!stopCount) return [];
1266
+ const firstKfValue = (_b = kfs[0].value) != null ? _b : kfs[0].v;
1267
+ const baselineStops = [];
1268
+ for (let i = 0; i < stopCount; i++) {
1269
+ const s = (_d = (_c = firstKfValue == null ? void 0 : firstKfValue[i]) != null ? _c : prevDefinedStop(kfs, 0, i)) != null ? _d : { offset: i / Math.max(1, stopCount - 1), color: "#000000" };
1270
+ baselineStops.push({ offset: s.offset, color: s.color });
1450
1271
  }
1451
- return t2;
1272
+ return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx));
1452
1273
  }
1453
- function cubicBezier(easing) {
1454
- const [p1x, p1y, p2x, p2y] = easing;
1455
- const cy = 3 * p1y;
1456
- const by = 3 * (p2y - p1y) - cy;
1457
- const ay = 1 - cy - by;
1458
- function sampleCurveY(t) {
1459
- return ((ay * t + by) * t + cy) * t;
1460
- }
1461
- return function(x) {
1462
- return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
1274
+ function staticStopNode(s) {
1275
+ return {
1276
+ type: "stop",
1277
+ offset: formatOffset(s.offset),
1278
+ stopColor: s.color
1463
1279
  };
1464
1280
  }
1465
- function lerp2(a, b, t) {
1466
- return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
1467
- }
1468
- function subdivideCubicBezier(p0, p1, p2, p3, t) {
1469
- const q0 = lerp2(p0, p1, t);
1470
- const q1 = lerp2(p1, p2, t);
1471
- const q2 = lerp2(p2, p3, t);
1472
- const r0 = lerp2(q0, q1, t);
1473
- const r1 = lerp2(q1, q2, t);
1474
- const s = lerp2(r0, r1, t);
1475
- return {
1476
- left: [p0, q0, r0, s],
1477
- right: [s, r1, q2, p3]
1281
+ function animatedStopNode(baseline, kfs, stopIdx, _ctx) {
1282
+ var _a, _b, _c, _d, _e;
1283
+ const colorKfs = [];
1284
+ const offsetKfs = [];
1285
+ let offsetVaries = false;
1286
+ for (const kf of kfs) {
1287
+ const t = (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
1288
+ const arr = (_c = kf.value) != null ? _c : kf.v;
1289
+ const sliced = (_d = arr == null ? void 0 : arr[stopIdx]) != null ? _d : prevDefinedStop(kfs, kfs.indexOf(kf), stopIdx);
1290
+ if (!sliced) continue;
1291
+ const easing = (_e = kf.easing) != null ? _e : kf.e;
1292
+ const colorOut = { time: t, value: sliced.color };
1293
+ if (easing !== void 0) colorOut.easing = easing;
1294
+ colorKfs.push(colorOut);
1295
+ const offsetOut = { time: t, value: sliced.offset };
1296
+ if (easing !== void 0) offsetOut.easing = easing;
1297
+ offsetKfs.push(offsetOut);
1298
+ if (sliced.offset !== baseline.offset) offsetVaries = true;
1299
+ }
1300
+ const stop = {
1301
+ type: "stop",
1302
+ offset: formatOffset(baseline.offset),
1303
+ stopColor: baseline.color
1478
1304
  };
1305
+ const animate = {};
1306
+ if (colorKfs.length) animate.stopColor = { keyframes: colorKfs };
1307
+ if (offsetVaries && offsetKfs.length) animate.offset = { keyframes: offsetKfs };
1308
+ if (Object.keys(animate).length) stop.animate = animate;
1309
+ return stop;
1479
1310
  }
1480
- function splitEasing(easing, xFraction) {
1481
- if (!easing) return { left: void 0, right: void 0 };
1482
- if (xFraction <= 0) return { left: void 0, right: easing };
1483
- if (xFraction >= 1) return { left: easing, right: void 0 };
1484
- const [x1, y1, x2, y2] = easing;
1485
- const t = solveCubicBezierX(x1, x2, xFraction);
1486
- const p0 = [0, 0];
1487
- const p1 = [x1, y1];
1488
- const p2 = [x2, y2];
1489
- const p3 = [1, 1];
1490
- const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
1491
- const sx = left[3][0];
1492
- const sy = left[3][1];
1493
- let leftEasing;
1494
- if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
1495
- leftEasing = [
1496
- left[1][0] / sx,
1497
- left[1][1] / sy,
1498
- left[2][0] / sx,
1499
- left[2][1] / sy
1500
- ];
1311
+ function prevDefinedStop(kfs, fromIdx, stopIdx) {
1312
+ var _a, _b;
1313
+ for (let i = fromIdx; i >= 0; i--) {
1314
+ const arr = (_a = kfs[i].value) != null ? _a : kfs[i].v;
1315
+ if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
1501
1316
  }
1502
- let rightEasing;
1503
- const rx = 1 - sx;
1504
- const ry = 1 - sy;
1505
- if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
1506
- rightEasing = [
1507
- (right[1][0] - sx) / rx,
1508
- (right[1][1] - sy) / ry,
1509
- (right[2][0] - sx) / rx,
1510
- (right[2][1] - sy) / ry
1511
- ];
1317
+ for (let i = fromIdx + 1; i < kfs.length; i++) {
1318
+ const arr = (_b = kfs[i].value) != null ? _b : kfs[i].v;
1319
+ if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
1512
1320
  }
1513
- return { left: leftEasing, right: rightEasing };
1321
+ return void 0;
1514
1322
  }
1515
- function reverseEasing(easing) {
1516
- if (!easing) return void 0;
1517
- return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
1323
+ function formatOffset(o) {
1324
+ const pct = Math.round(o * 1e3) / 10;
1325
+ return pct + "%";
1518
1326
  }
1519
- function toRGBA(color) {
1520
- const r = Math.round(color[0] * 255);
1521
- const g = Math.round(color[1] * 255);
1522
- const b = Math.round(color[2] * 255);
1523
- return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
1327
+
1328
+ // src/effects/maskedByEffect.ts
1329
+ function applyMaskedByEffect(node, fx, transformation, ctx) {
1330
+ if (!fx) return node;
1331
+ if (!fx.href) {
1332
+ ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
1333
+ return node;
1334
+ }
1335
+ const maskId = genId(ctx, "mask");
1336
+ let content = { type: "use", href: "#" + fx.href };
1337
+ if (transformation) {
1338
+ content = wrapInverseTransform(content, transformation, ctx);
1339
+ } else if (hasAnimateTransform(node)) {
1340
+ content = wrapInverseAnimatedBodyTransform(content, node, ctx);
1341
+ } else {
1342
+ const bodyStatic = readTransformationFromBody(node);
1343
+ if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);
1344
+ }
1345
+ const includeTargetOwn = transformation === void 0 && !nodeHasBodyTransform(node);
1346
+ content = wrapAncestorChainCompensation(content, node, fx.href, ctx, includeTargetOwn);
1347
+ const mask = { type: "mask", id: maskId, children: [content] };
1348
+ if (fx.maskType) mask.maskType = fx.maskType;
1349
+ if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
1350
+ if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
1351
+ ctx.defs.push(mask);
1352
+ node.mask = "url(#" + maskId + ")";
1353
+ return node;
1524
1354
  }
1525
- function parseRgba(s) {
1526
- var _a;
1527
- const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
1528
- if (!inner) throw new Error("Invalid rgb/rgba format");
1529
- const parts = inner.split(",").map((v) => +v.trim());
1530
- return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
1355
+ function wrapInverseTransform(inner, fx, ctx) {
1356
+ if (!fx) return inner;
1357
+ const origin = readStaticOrigin(fx.origin, ctx);
1358
+ let n = inner;
1359
+ n = wrapInversePart(n, "translate" /* Translate */, fx.translate, void 0, ctx);
1360
+ n = wrapInversePart(n, "rotate" /* Rotate */, fx.rotate, origin, ctx);
1361
+ n = wrapInversePart(n, "scale" /* Scale */, fx.scale, origin, ctx);
1362
+ return n;
1531
1363
  }
1532
- function parseHex(s) {
1533
- const hex = s.slice(1);
1534
- const isShort = hex.length <= 4;
1535
- const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
1536
- const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
1537
- const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
1538
- const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
1539
- const result = [
1540
- parseInt(r, 16) / 255,
1541
- parseInt(g, 16) / 255,
1542
- parseInt(b, 16) / 255
1543
- ];
1544
- if (a !== null) {
1545
- result.push(parseInt(a, 16) / 255);
1364
+ function wrapInversePart(inner, part, raw, origin, ctx) {
1365
+ if (raw === void 0) return inner;
1366
+ const normalisedRaw = part === "scale" /* Scale */ && Array.isArray(raw) ? [raw[0] / 100, raw[1] / 100] : raw;
1367
+ const v = readAnimatable(normalisedRaw);
1368
+ if (v.kind === "static" /* Static */) {
1369
+ return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
1546
1370
  }
1547
- return result;
1548
- }
1549
- function parseColor(s) {
1550
- if (!s) return void 0;
1551
- if (Array.isArray(s)) return s;
1552
- if (typeof s !== "string") return void 0;
1553
- if (s.startsWith("#")) {
1554
- return parseHex(s);
1555
- } else if (s.startsWith("rgb")) {
1556
- return parseRgba(s);
1557
- } else {
1558
- console.warn("Unsupported color format: " + s);
1371
+ if (v.kind === "animated" /* Animated */) {
1372
+ return {
1373
+ type: "g",
1374
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
1375
+ children: [inner]
1376
+ };
1559
1377
  }
1560
- return void 0;
1561
- }
1562
- var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
1563
- var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
1564
- var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1565
- function composeTransformParts(parts, opts) {
1566
- var _a;
1567
- if (!parts) return "";
1568
- const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
1569
- const segs = [];
1570
- const t = parts.translate;
1571
- const o = parts.origin;
1572
- const r = parts.rotate;
1573
- const s = parts.scale;
1574
- const tu = withUnits ? "px" : "";
1575
- const ru = withUnits ? "deg" : "";
1576
- if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
1577
- if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
1578
- if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
1579
- if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
1580
- if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
1581
- return segs.join("");
1582
- }
1583
- var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1584
- var DEFAULT_DURATION_MS = 1e3;
1585
- function kebabToCamelCaseWord(kebab) {
1586
- return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
1587
- }
1588
- function isCamelCaseWord(word) {
1589
- return !word.includes("-") && /[a-z][A-Z]/.test(word);
1590
- }
1591
- var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
1592
- // Transform/positioning
1593
- "viewBox",
1594
- "preserveAspectRatio",
1595
- // Gradient
1596
- "gradientUnits",
1597
- "gradientTransform",
1598
- "spreadMethod",
1599
- // Pattern
1600
- "patternUnits",
1601
- "patternContentUnits",
1602
- "patternTransform",
1603
- // Clipping/masking
1604
- "clipPathUnits",
1605
- "maskUnits",
1606
- "maskContentUnits",
1607
- // Text
1608
- "textLength",
1609
- "lengthAdjust",
1610
- "startOffset",
1611
- // Filter
1612
- "filterUnits",
1613
- "primitiveUnits",
1614
- "stdDeviation",
1615
- "baseFrequency",
1616
- "numOctaves",
1617
- "surfaceScale",
1618
- "diffuseConstant",
1619
- "specularConstant",
1620
- "specularExponent",
1621
- "kernelMatrix",
1622
- "kernelUnitLength",
1623
- "edgeMode",
1624
- "preserveAlpha",
1625
- "targetX",
1626
- "targetY"
1627
- // // Animation
1628
- // 'attributeName',
1629
- // 'attributeType',
1630
- // 'calcMode',
1631
- // 'keyTimes',
1632
- // 'keySplines',
1633
- // 'repeatCount',
1634
- // 'repeatDur'
1635
- ]);
1636
- function camelCaseToKebabWordIfNeeded(camel) {
1637
- return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1638
- }
1639
- function clamp(value, min, max) {
1640
- return Math.max(min, Math.min(value, max));
1378
+ return inner;
1641
1379
  }
1642
- function bezier2D_pointAt(P0, P1, P2, P3, t) {
1643
- if (t <= 0) return [P0[0], P0[1]];
1644
- if (t >= 1) return [P3[0], P3[1]];
1645
- const u = 1 - t;
1646
- const u2 = u * u;
1647
- const u3 = u2 * u;
1648
- const t2 = t * t;
1649
- const t3 = t2 * t;
1650
- const w0 = u3;
1651
- const w1 = 3 * t * u2;
1652
- const w2 = 3 * t2 * u;
1653
- const w3 = t3;
1654
- return [
1655
- w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
1656
- w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
1657
- ];
1380
+ function invertPartValue(part, value) {
1381
+ if (part === "translate" /* Translate */) return [-value[0], -value[1]];
1382
+ if (part === "rotate" /* Rotate */) return -value;
1383
+ return [1 / value[0], 1 / value[1]];
1658
1384
  }
1659
- var BEZIER_T_NUDGE = 1e-4;
1660
- function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
1661
- const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
1662
- if (result[0] === 0 && result[1] === 0) {
1663
- const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
1664
- return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
1385
+ function wrapInverseAnimatedBodyTransform(inner, node, _ctx) {
1386
+ var _a;
1387
+ const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
1388
+ const animTr = animate == null ? void 0 : animate.transform;
1389
+ const kfs = animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes) ? animTr.keyframes : void 0;
1390
+ if (!kfs || !kfs.length) return inner;
1391
+ const translateKfs = [];
1392
+ const rotateKfs = [];
1393
+ const scaleKfs = [];
1394
+ for (const kf of kfs) {
1395
+ const v = ((_a = kf.value) != null ? _a : kf.v) || {};
1396
+ const baseKf = keyframeWith(kf, void 0);
1397
+ if (Array.isArray(v.translate)) {
1398
+ translateKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: { translate: [-v.translate[0], -v.translate[1]] } }));
1399
+ }
1400
+ if (typeof v.rotate === "number") {
1401
+ const rec = { rotate: -v.rotate };
1402
+ if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];
1403
+ rotateKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
1404
+ }
1405
+ if (Array.isArray(v.scale)) {
1406
+ const rec = { scale: [1 / v.scale[0], 1 / v.scale[1]] };
1407
+ if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];
1408
+ scaleKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
1409
+ }
1665
1410
  }
1666
- return result;
1411
+ let n = inner;
1412
+ if (translateKfs.length) n = { type: "g", animate: { transform: { keyframes: translateKfs } }, children: [n] };
1413
+ if (rotateKfs.length) n = { type: "g", animate: { transform: { keyframes: rotateKfs } }, children: [n] };
1414
+ if (scaleKfs.length) n = { type: "g", animate: { transform: { keyframes: scaleKfs } }, children: [n] };
1415
+ return n;
1667
1416
  }
1668
- function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
1669
- const u = 1 - t;
1670
- const a = 3 * u * u;
1671
- const b = 6 * t * u;
1672
- const c = 3 * t * t;
1673
- return [
1674
- a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
1675
- a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
1676
- ];
1417
+ function nodeHasBodyTransform(node) {
1418
+ if (typeof node.transform === "string") return true;
1419
+ if (node.transform && typeof node.transform === "object") return true;
1420
+ return hasAnimateTransform(node);
1677
1421
  }
1678
- function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
1679
- const n = steps + 1;
1680
- const ts = new Float64Array(n);
1681
- const ds = new Float64Array(n);
1682
- let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
1683
- ts[0] = 0;
1684
- ds[0] = 0;
1685
- let cum = 0;
1686
- for (let i = 1; i < n; i++) {
1687
- const t = i / steps;
1688
- const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
1689
- const dx = cur[0] - prev[0];
1690
- const dy = cur[1] - prev[1];
1691
- cum += Math.sqrt(dx * dx + dy * dy);
1692
- ts[i] = t;
1693
- ds[i] = cum;
1694
- prev = cur;
1695
- }
1696
- return { ts, ds };
1422
+ function hasAnimateTransform(node) {
1423
+ const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
1424
+ return !!(animate && animate.transform);
1697
1425
  }
1698
- function bezier2D_tForDistance(lut, distance) {
1699
- const { ts, ds } = lut;
1700
- const last = ds.length - 1;
1701
- if (distance <= 0) return ts[0];
1702
- if (distance >= ds[last]) return ts[last];
1703
- let lo = 1;
1704
- let hi = last;
1705
- while (lo < hi) {
1706
- const mid = lo + hi >>> 1;
1707
- if (ds[mid] < distance) lo = mid + 1;
1708
- else hi = mid;
1426
+ function readTransformationFromBody(node) {
1427
+ if (typeof node.transform === "string") {
1428
+ const parts = parseTransformStringToParts(node.transform);
1429
+ if (!parts) return void 0;
1430
+ const out = {};
1431
+ if (parts.translate) out.translate = parts.translate;
1432
+ if (parts.rotate !== void 0) out.rotate = parts.rotate;
1433
+ if (parts.scale) out.scale = { value: parts.scale };
1434
+ if (parts.origin) out.origin = parts.origin;
1435
+ return Object.keys(out).length ? out : void 0;
1709
1436
  }
1710
- const dPrev = ds[hi - 1];
1711
- const dCur = ds[hi];
1712
- const span = dCur - dPrev;
1713
- const frac = span > 0 ? (distance - dPrev) / span : 0;
1714
- return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
1437
+ return void 0;
1715
1438
  }
1716
-
1717
- // src/PxAnimatorDOM.ts
1718
- var SVG_NS = "http://www.w3.org/2000/svg";
1719
- var ALLOWED_SVG_TAGS_LOWER_CASE = new Set([
1720
- "svg",
1721
- "g",
1722
- "path",
1723
- "circle",
1724
- "ellipse",
1725
- "rect",
1726
- "line",
1727
- "polyline",
1728
- "polygon",
1729
- "text",
1730
- "tspan",
1731
- "textPath",
1732
- "defs",
1733
- "clipPath",
1734
- "mask",
1735
- "pattern",
1736
- "linearGradient",
1737
- "radialGradient",
1738
- "stop",
1739
- "use",
1740
- "symbol",
1741
- "marker",
1742
- "filter",
1743
- "feGaussianBlur",
1744
- "feOffset",
1745
- "feBlend",
1746
- "feColorMatrix",
1747
- "feMerge",
1748
- "feMergeNode"
1749
- ].map((tagName) => tagName.toLowerCase()));
1750
- var ALLOWED_RESOURCE_ATTRIBUTES = [
1751
- "href",
1752
- // <use>
1753
- "src",
1754
- // <image>
1755
- "filter",
1756
- // url(#filterId)
1757
- "clipPath",
1758
- // clip-path="url(#clipPathId)"
1759
- "mask",
1760
- // url(#maskId)
1761
- "markerStart",
1762
- // marker-start="url(#markerId)"
1763
- "markerMid",
1764
- // marker-mid="url(#markerId)"
1765
- "markerEnd"
1766
- // marker-end="url(#markerId)"
1767
- // 'fill', // url(#gradientId) or url(#patternId)
1768
- // 'stroke', // url(#gradientId) or url(#patternId)
1769
- // Don't allow 'cursor', can use external SVG, // url(cursor.svg)
1770
- ];
1771
- var ALLOWED_RESOURCE_ATTRIBUTES_SET = new Set(ALLOWED_RESOURCE_ATTRIBUTES);
1772
- var ALLOWED_ATTRIBUTES = [
1773
- "href",
1774
- "src",
1775
- // Presentation
1776
- "fill",
1777
- "fillOpacity",
1778
- "fillRule",
1779
- "stroke",
1780
- "strokeWidth",
1781
- "strokeOpacity",
1782
- "strokeLinecap",
1783
- "strokeLinejoin",
1784
- "strokeMiterlimit",
1785
- "strokeDasharray",
1786
- "strokeDashoffset",
1787
- "opacity",
1788
- "transform",
1789
- // Geometry
1790
- "x",
1791
- "y",
1792
- "cx",
1793
- "cy",
1794
- "r",
1795
- "rx",
1796
- "ry",
1797
- "width",
1798
- "height",
1799
- "d",
1800
- "x1",
1801
- "y1",
1802
- "x2",
1803
- "y2",
1804
- "points",
1805
- "dx",
1806
- "dy",
1807
- // Font
1808
- "fontSize",
1809
- "fontFamily",
1810
- "fontWeight",
1811
- "fontStyle",
1812
- // Text
1813
- "textAnchor",
1814
- "letterSpacing",
1815
- "wordSpacing",
1816
- "space",
1817
- "xml:space",
1818
- "textDecoration",
1819
- "textTransform",
1820
- "whiteSpace",
1821
- "white-space",
1822
- "dominantBaseline",
1823
- "alignmentBaseline",
1824
- "rotate",
1825
- // per-glyph rotation array, currently not supported
1826
- // Structure
1827
- "id",
1828
- "class",
1829
- "viewBox",
1830
- "preserveAspectRatio",
1831
- // Gradient/Pattern
1832
- "offset",
1833
- "stopColor",
1834
- "stopOpacity",
1835
- "gradientTransform",
1836
- // Clippath/Mask
1837
- "clipPath",
1838
- "mask",
1839
- // Motion path
1840
- "offsetPath",
1841
- "offsetDistance",
1842
- "offsetRotate",
1843
- "offsetAnchor",
1844
- "offsetPosition",
1845
- // Text path
1846
- "startOffset",
1847
- "textLength",
1848
- "lengthAdjust",
1849
- // Filter
1850
- "filter",
1851
- "stdDeviation",
1852
- "in",
1853
- "in2",
1854
- "result",
1855
- "mode",
1856
- ...ALLOWED_RESOURCE_ATTRIBUTES
1857
- ];
1858
- var ALLOWED_ATTRIBUTES_LOW_SET = new Set(ALLOWED_ATTRIBUTES.map((str) => str.toLowerCase()));
1859
- function sanitiseAttributeValue(name, value) {
1860
- const nameLower = name.toLowerCase();
1861
- if (!ALLOWED_ATTRIBUTES_LOW_SET.has(nameLower)) {
1862
- console.warn("Attribute not in whitelist: ", nameLower);
1863
- return void 0;
1864
- }
1865
- if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopColor") {
1866
- const str = String(value);
1867
- if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
1868
- console.warn('Attribute "' + nameLower + '" blocked: url() references must be internal url(#id), got:', value);
1869
- return void 0;
1439
+ function parseTransformStringToParts(s) {
1440
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
1441
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
1442
+ let m;
1443
+ const ops = [];
1444
+ while ((m = re.exec(s)) !== null) {
1445
+ const args = m[2].split(/[\s,]+/).filter((a) => a.length > 0).map(Number);
1446
+ ops.push({ name: m[1], args });
1447
+ }
1448
+ if (!ops.length) return void 0;
1449
+ const last = ops[ops.length - 1];
1450
+ if (last.name === "translate") {
1451
+ for (let j = ops.length - 2; j >= 0; j--) {
1452
+ const cand = ops[j];
1453
+ if (cand.name !== "translate") continue;
1454
+ const ox = (_a = cand.args[0]) != null ? _a : 0;
1455
+ const oy = (_b = cand.args[1]) != null ? _b : 0;
1456
+ const lx = (_c = last.args[0]) != null ? _c : 0;
1457
+ const ly = (_d = last.args[1]) != null ? _d : 0;
1458
+ if (lx !== -ox || ly !== -oy) continue;
1459
+ const out2 = {};
1460
+ out2.origin = [ox, oy];
1461
+ for (let k = 0; k < j; k++) {
1462
+ if (ops[k].name === "translate") {
1463
+ const tx = (_e = ops[k].args[0]) != null ? _e : 0;
1464
+ const ty = (_f = ops[k].args[1]) != null ? _f : 0;
1465
+ out2.translate = out2.translate ? [out2.translate[0] + tx, out2.translate[1] + ty] : [tx, ty];
1466
+ }
1467
+ }
1468
+ for (let k = j + 1; k < ops.length - 1; k++) {
1469
+ const op = ops[k];
1470
+ if (op.name === "rotate") out2.rotate = ((_g = out2.rotate) != null ? _g : 0) + ((_h = op.args[0]) != null ? _h : 0);
1471
+ else if (op.name === "scale") {
1472
+ const sx = (_i = op.args[0]) != null ? _i : 1;
1473
+ const sy = op.args.length > 1 ? op.args[1] : sx;
1474
+ out2.scale = out2.scale ? [out2.scale[0] * sx, out2.scale[1] * sy] : [sx, sy];
1475
+ }
1476
+ }
1477
+ return out2;
1870
1478
  }
1871
- return value;
1872
1479
  }
1873
- if (ALLOWED_RESOURCE_ATTRIBUTES_SET.has(nameLower)) {
1874
- const str = String(value);
1875
- if (str.startsWith("#")) {
1876
- return value;
1877
- }
1878
- if (/^url\(#[^)]+\)$/.test(str)) {
1879
- return value;
1480
+ let translate;
1481
+ let rotate;
1482
+ let scale;
1483
+ for (const op of ops) {
1484
+ if (op.name === "translate") {
1485
+ const dx = (_j = op.args[0]) != null ? _j : 0;
1486
+ const dy = (_k = op.args[1]) != null ? _k : 0;
1487
+ translate = translate ? [translate[0] + dx, translate[1] + dy] : [dx, dy];
1488
+ } else if (op.name === "rotate") {
1489
+ rotate = (rotate != null ? rotate : 0) + ((_l = op.args[0]) != null ? _l : 0);
1490
+ } else if (op.name === "scale") {
1491
+ const sx = (_m = op.args[0]) != null ? _m : 1;
1492
+ const sy = op.args.length > 1 ? op.args[1] : sx;
1493
+ scale = scale ? [scale[0] * sx, scale[1] * sy] : [sx, sy];
1880
1494
  }
1881
- return void 0;
1882
- }
1883
- return value;
1884
- }
1885
- function createElement(tagName, normalisedProps, style, children, textContent) {
1886
- if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) {
1887
- console.warn("Attribute not in whitelist: ", tagName);
1888
- return null;
1889
1495
  }
1890
- const element = document.createElementNS(SVG_NS, tagName);
1891
- for (const propName in normalisedProps) {
1892
- element.setAttribute(
1893
- camelCaseToKebabWordIfNeeded(propName),
1894
- sanitiseAttributeValue(propName, normalisedProps[propName])
1895
- );
1496
+ const out = {};
1497
+ if (translate) out.translate = translate;
1498
+ if (rotate !== void 0) out.rotate = rotate;
1499
+ if (scale) out.scale = scale;
1500
+ return Object.keys(out).length ? out : void 0;
1501
+ }
1502
+ function wrapAncestorChainCompensation(inner, maskedNode, sourceId, ctx, includeTargetOwn) {
1503
+ const sourceNode = ctx.idMap.get(sourceId);
1504
+ const targetAncestors = ctx.maskAncestorChains.get(maskedNode) || [];
1505
+ const targetOwn = includeTargetOwn ? extractTranslateOnly(maskedNode, ctx) : void 0;
1506
+ const targetChain = targetOwn ? [...targetAncestors, targetOwn] : targetAncestors;
1507
+ const sourceChain = sourceNode && ctx.maskAncestorChains.get(sourceNode) || [];
1508
+ if (!targetChain.length && !sourceChain.length) return inner;
1509
+ const times = /* @__PURE__ */ new Set();
1510
+ for (const a of targetChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);
1511
+ for (const a of sourceChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);
1512
+ const animated = times.size > 0;
1513
+ if (!animated) {
1514
+ const tgt = sumStaticTranslate(targetChain);
1515
+ const src = sumStaticTranslate(sourceChain);
1516
+ const dx = src[0] - tgt[0];
1517
+ const dy = src[1] - tgt[1];
1518
+ if (dx === 0 && dy === 0) return inner;
1519
+ return { type: "g", transform: "translate(" + dx + "," + dy + ")", children: [inner] };
1520
+ }
1521
+ const sortedTimes = Array.from(times).sort((a, b) => a - b);
1522
+ const keyframes = sortedTimes.map((t) => {
1523
+ const tgt = sumTranslateAt(targetChain, t);
1524
+ const src = sumTranslateAt(sourceChain, t);
1525
+ return { time: t, value: { translate: [src[0] - tgt[0], src[1] - tgt[1]] } };
1526
+ });
1527
+ return { type: "g", animate: { transform: { keyframes } }, children: [inner] };
1528
+ }
1529
+ function sumStaticTranslate(chain) {
1530
+ let x = 0, y = 0;
1531
+ for (const a of chain) {
1532
+ if (a.translate) {
1533
+ x += a.translate[0];
1534
+ y += a.translate[1];
1535
+ }
1896
1536
  }
1897
- if (style) {
1898
- for (const styleProp in style) {
1899
- element.style[styleProp] = String(style[styleProp]);
1537
+ return [x, y];
1538
+ }
1539
+ function sumTranslateAt(chain, t) {
1540
+ let x = 0, y = 0;
1541
+ for (const a of chain) {
1542
+ if (a.translateKeyframes && a.translateKeyframes.length) {
1543
+ const v = interpKfs(a.translateKeyframes, t);
1544
+ x += v[0];
1545
+ y += v[1];
1546
+ } else if (a.translate) {
1547
+ x += a.translate[0];
1548
+ y += a.translate[1];
1900
1549
  }
1901
1550
  }
1902
- if (children) {
1903
- for (const child of children) {
1904
- element.appendChild(child);
1551
+ return [x, y];
1552
+ }
1553
+ function interpKfs(kfs, t) {
1554
+ if (t <= kfs[0].time) return kfs[0].value;
1555
+ if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;
1556
+ for (let i = 1; i < kfs.length; i++) {
1557
+ if (t <= kfs[i].time) {
1558
+ const prev = kfs[i - 1];
1559
+ const cur = kfs[i];
1560
+ const a = (t - prev.time) / (cur.time - prev.time);
1561
+ return [prev.value[0] + (cur.value[0] - prev.value[0]) * a, prev.value[1] + (cur.value[1] - prev.value[1]) * a];
1905
1562
  }
1906
1563
  }
1907
- if (textContent) element.textContent = textContent;
1908
- return element;
1909
- }
1910
- function resolveStyle(style, defs) {
1911
- var _a;
1912
- if (!style) return void 0;
1913
- if (typeof style === "string") {
1914
- return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
1915
- }
1916
- return style;
1564
+ return kfs[kfs.length - 1].value;
1565
+ }
1566
+ function collectMaskAncestorChains(root, ctx) {
1567
+ const interestingNodes = /* @__PURE__ */ new Set();
1568
+ const collectInterestingNodes = (n) => {
1569
+ var _a, _b;
1570
+ const href = (_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.href;
1571
+ if (typeof href === "string") {
1572
+ interestingNodes.add(n);
1573
+ const sourceNode = ctx.idMap.get(href);
1574
+ if (sourceNode) interestingNodes.add(sourceNode);
1575
+ }
1576
+ if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);
1577
+ };
1578
+ collectInterestingNodes(root);
1579
+ if (interestingNodes.size === 0) return;
1580
+ const walk = (node, chain) => {
1581
+ if (interestingNodes.has(node)) ctx.maskAncestorChains.set(node, chain);
1582
+ if (Array.isArray(node.children)) {
1583
+ const own = extractTranslateOnly(node, ctx);
1584
+ const next = own ? [...chain, own] : chain;
1585
+ for (const ch of node.children) walk(ch, next);
1586
+ }
1587
+ };
1588
+ walk(root, []);
1917
1589
  }
1918
- function getNormalizedProps(props) {
1919
- const propsCopy = {};
1920
- for (const key of Object.keys(props)) {
1921
- if (INTERNAL_ATTRS.has(key)) continue;
1922
- if (key === "style") continue;
1923
- let value = props[key];
1924
- if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
1925
- propsCopy[key] = toRGBA(value);
1926
- } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
1927
- propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
1928
- } else if (TRANSFORM_FN_NAMES.has(key)) {
1929
- if (Array.isArray(value)) {
1930
- if (key === "translate") value = value.map((v) => v + "px");
1931
- value = value.join(",");
1590
+ function extractTranslateOnly(node, ctx) {
1591
+ var _a, _b, _c;
1592
+ const tr = node.transform;
1593
+ const animateBlock = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate.transform : void 0;
1594
+ if (tr === void 0 && !animateBlock) return void 0;
1595
+ const out = {};
1596
+ if (typeof tr === "string") {
1597
+ const parts = parseTranslateOnlyFromString(tr, ctx);
1598
+ if (parts) out.translate = parts;
1599
+ } else if (tr && typeof tr === "object") {
1600
+ const value = tr.value;
1601
+ if (value && typeof value === "object" && Array.isArray(value.translate)) {
1602
+ out.translate = [value.translate[0] || 0, value.translate[1] || 0];
1603
+ }
1604
+ if (value && (value.rotate !== void 0 || value.scale !== void 0 || value.skew !== void 0)) {
1605
+ ctx.warnings.push("maskedBy ancestor: non-translate transform parts ignored (rotate/scale not yet supported)");
1606
+ }
1607
+ }
1608
+ if (animateBlock && Array.isArray(animateBlock.keyframes)) {
1609
+ const kfs = animateBlock.keyframes;
1610
+ const translateKfs = [];
1611
+ for (const kf of kfs) {
1612
+ const v = (_a = kf.value) != null ? _a : kf.v;
1613
+ const t = (_c = (_b = kf.time) != null ? _b : kf.t) != null ? _c : 0;
1614
+ if (v && typeof v === "object" && Array.isArray(v.translate)) {
1615
+ translateKfs.push({ time: t, value: [v.translate[0] || 0, v.translate[1] || 0] });
1616
+ if (v.rotate !== void 0 || v.scale !== void 0 || v.skew !== void 0) {
1617
+ ctx.warnings.push("maskedBy ancestor: animated non-translate parts ignored");
1618
+ }
1932
1619
  }
1933
- if (key === "rotate") value = value + "deg";
1934
- propsCopy["transform"] = key + "(" + value + ")";
1935
- } else if (value !== void 0 && value !== null) {
1936
- propsCopy[key] = String(value);
1937
1620
  }
1621
+ if (translateKfs.length) out.translateKeyframes = translateKfs;
1938
1622
  }
1939
- return propsCopy;
1623
+ return out.translate || out.translateKeyframes ? out : void 0;
1940
1624
  }
1941
- function renderNode(node, defs) {
1942
- if (!node) return null;
1943
- const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
1944
- const nodeDefs = getDefs(node) || defs;
1945
- const resolvedStyle = resolveStyle(style, nodeDefs);
1946
- let childElements;
1947
- if (children) {
1948
- for (const ch of children) {
1949
- const child = renderNode(ch, nodeDefs);
1950
- if (child) {
1951
- if (!childElements) childElements = [];
1952
- childElements.push(child);
1953
- }
1625
+ function parseTranslateOnlyFromString(s, ctx) {
1626
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
1627
+ let m;
1628
+ let x = 0, y = 0;
1629
+ let seen = false;
1630
+ let droppedNonTranslate = false;
1631
+ while ((m = re.exec(s)) !== null) {
1632
+ const name = m[1];
1633
+ const args = m[2].split(/[\s,]+/).filter((a) => a.length > 0).map(Number);
1634
+ if (name === "translate") {
1635
+ x += args[0] || 0;
1636
+ y += args[1] || 0;
1637
+ seen = true;
1638
+ } else {
1639
+ droppedNonTranslate = true;
1954
1640
  }
1955
1641
  }
1956
- return createElement(
1957
- type || "g",
1958
- getNormalizedProps(props),
1959
- resolvedStyle,
1960
- childElements,
1961
- props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
1962
- );
1642
+ if (droppedNonTranslate) ctx.warnings.push("maskedBy ancestor: non-translate transform in string ignored: " + s);
1643
+ return seen ? [x, y] : void 0;
1963
1644
  }
1964
1645
 
1965
- // src/PxAnimatorTriggers.ts
1966
- function setupAnimationTriggers(api, config) {
1967
- const { startOn, outAction = "continue", scrollIntoViewThreshold = 0.5 } = config;
1968
- const root = api.getRootElement();
1969
- if (!root) {
1970
- console.warn("setupAnimationTriggers: No root element found for animation.");
1971
- return api;
1646
+ // src/effects/refEffect.ts
1647
+ var CONTENT_SUBREF = "content";
1648
+ function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
1649
+ if (ref) {
1650
+ const baseId = ref.baseId;
1651
+ if (!baseId) {
1652
+ ctx.errors.push("ref: missing baseId");
1653
+ } else {
1654
+ const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
1655
+ node.href = "#" + targetId;
1656
+ }
1972
1657
  }
1973
- const start = () => {
1974
- api.play();
1975
- };
1976
- const handleEndAction = () => {
1977
- switch (outAction) {
1978
- case "pause":
1979
- api.pause();
1980
- break;
1981
- case "reset":
1982
- api.cancel();
1983
- break;
1984
- case "reverse":
1985
- api.play();
1986
- break;
1987
- case "continue":
1988
- default:
1989
- break;
1658
+ return applyTransformationEffect(node, transformation, ctx);
1659
+ }
1660
+
1661
+ // src/effects/repeaterEffect.ts
1662
+ function applyRepeaterEffect(node, fx, ctx) {
1663
+ var _a, _b;
1664
+ if (!fx) return node;
1665
+ const copies = (_a = fx.copies) != null ? _a : 1;
1666
+ if (copies < 1) {
1667
+ ctx.errors.push("repeater.copies invalid: " + fx.copies);
1668
+ return node;
1669
+ }
1670
+ const sharedTransform = node.transform;
1671
+ const sharedAnimTransform = (_b = node.animate) == null ? void 0 : _b.transform;
1672
+ const base = clone(node);
1673
+ delete base.transform;
1674
+ if (base.animate) {
1675
+ delete base.animate.transform;
1676
+ if (Object.keys(base.animate).length === 0) delete base.animate;
1677
+ }
1678
+ const children = [base];
1679
+ for (let i = 1; i < copies; i++) {
1680
+ const baseClone = clone(base);
1681
+ const synthFx = synthesisePerCopyFx(fx, i);
1682
+ const wrapped = synthFx ? applyTransformationEffect(baseClone, synthFx, ctx) : baseClone;
1683
+ children.push(wrapped);
1684
+ }
1685
+ const wrapper = { type: "g", children };
1686
+ if (sharedTransform !== void 0) wrapper.transform = sharedTransform;
1687
+ if (sharedAnimTransform !== void 0) wrapper.animate = { transform: sharedAnimTransform };
1688
+ return wrapper;
1689
+ }
1690
+ function synthesisePerCopyFx(fx, i) {
1691
+ const out = {};
1692
+ if (fx.translate !== void 0) {
1693
+ out.translate = mapAnimatableVec2(fx.translate, (v) => [v[0] * i, v[1] * i]);
1694
+ }
1695
+ if (fx.rotate !== void 0) {
1696
+ out.rotate = mapAnimatableNumber(fx.rotate, (v) => v * i);
1697
+ }
1698
+ if (fx.scale !== void 0) {
1699
+ out.scale = synthesiseScale(fx.scale, i);
1700
+ }
1701
+ if (fx.origin !== void 0) {
1702
+ out.origin = fx.origin;
1703
+ }
1704
+ return Object.keys(out).length ? out : void 0;
1705
+ }
1706
+ function mapAnimatableVec2(raw, fn) {
1707
+ if (Array.isArray(raw)) return fn(raw);
1708
+ if (raw && typeof raw === "object") {
1709
+ const obj = raw;
1710
+ if (Array.isArray(obj.keyframes)) {
1711
+ return __spreadProps(__spreadValues({}, obj), {
1712
+ keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
1713
+ });
1990
1714
  }
1991
- };
1992
- switch (startOn) {
1993
- case "load": {
1994
- const startHandler = () => start();
1995
- if (document.readyState === "complete") {
1996
- startHandler();
1997
- } else {
1998
- window.addEventListener("load", startHandler, { once: true });
1999
- }
2000
- break;
1715
+ if (obj.value !== void 0) {
1716
+ return __spreadProps(__spreadValues({}, obj), { value: fn(obj.value) });
2001
1717
  }
2002
- case "mouseOver": {
2003
- const mouseOverHandler = () => start();
2004
- const mouseOutHandler = () => handleEndAction();
2005
- root.addEventListener("mouseenter", mouseOverHandler);
2006
- root.addEventListener("mouseleave", mouseOutHandler);
2007
- break;
1718
+ }
1719
+ return raw;
1720
+ }
1721
+ function mapAnimatableNumber(raw, fn) {
1722
+ if (typeof raw === "number") return fn(raw);
1723
+ if (raw && typeof raw === "object") {
1724
+ const obj = raw;
1725
+ if (Array.isArray(obj.keyframes)) {
1726
+ return __spreadProps(__spreadValues({}, obj), {
1727
+ keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
1728
+ });
2008
1729
  }
2009
- case "click": {
2010
- const clickHandler = () => {
2011
- if (api.isPlaying()) {
2012
- handleEndAction();
2013
- } else {
2014
- start();
2015
- }
2016
- };
2017
- root.addEventListener("click", clickHandler);
2018
- break;
1730
+ if (obj.value !== void 0) {
1731
+ return __spreadProps(__spreadValues({}, obj), { value: fn(obj.value) });
2019
1732
  }
2020
- case "scrollIntoView": {
2021
- const observer = new IntersectionObserver(
2022
- (entries) => {
2023
- entries.forEach((entry) => {
2024
- if (entry.isIntersecting && entry.intersectionRatio >= scrollIntoViewThreshold) {
2025
- start();
2026
- } else {
2027
- handleEndAction();
2028
- }
2029
- });
2030
- },
2031
- { threshold: scrollIntoViewThreshold }
2032
- );
2033
- observer.observe(root);
2034
- break;
1733
+ }
1734
+ return raw;
1735
+ }
1736
+ function synthesiseScale(raw, i) {
1737
+ const scalePowerFromPercent = (v) => [Math.pow(v[0] / 100, i), Math.pow(v[1] / 100, i)];
1738
+ const scalePowerFromUnits = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
1739
+ if (Array.isArray(raw)) {
1740
+ return { value: scalePowerFromPercent(raw) };
1741
+ }
1742
+ if (raw && typeof raw === "object") {
1743
+ const obj = raw;
1744
+ if (Array.isArray(obj.keyframes)) {
1745
+ return __spreadProps(__spreadValues({}, obj), {
1746
+ keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: scalePowerFromUnits(kf.value) }) : kf)
1747
+ });
1748
+ }
1749
+ if (obj.value !== void 0) {
1750
+ return __spreadProps(__spreadValues({}, obj), { value: scalePowerFromPercent(obj.value) });
2035
1751
  }
2036
- case "programmatic":
2037
- break;
2038
1752
  }
2039
- return api;
1753
+ return raw;
2040
1754
  }
2041
1755
 
2042
- // src/PxMotionPath.ts
2043
- function propAnimIsMotionPath(anim) {
2044
- var _a;
2045
- const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
2046
- if (!Array.isArray(kfs)) return false;
2047
- if (anim.autoOrient) return true;
2048
- for (const kf of kfs) {
2049
- if (kf.tangentIn || kf.tangentOut) return true;
2050
- }
2051
- return false;
1756
+ // src/effects/retimeEffect.ts
1757
+ var RETIME_MATERIALISATION_MODE_INLINE_G = false;
1758
+ function asRetime(r) {
1759
+ var _a, _b;
1760
+ return { start: (_a = r.start) != null ? _a : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
2052
1761
  }
2053
- var _segmentCache = /* @__PURE__ */ new WeakMap();
2054
- function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
2055
- const existing = _segmentCache.get(prevKf);
2056
- if (existing) return existing;
2057
- const to = prevKf.tangentOut;
2058
- const ti = nextKf.tangentIn;
2059
- const P1 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];
2060
- const P2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];
2061
- const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);
2062
- const entry = {
2063
- P0: prevPos,
2064
- P1,
2065
- P2,
2066
- P3: nextPos,
2067
- lut,
2068
- totalArc: lut.ds[lut.ds.length - 1]
1762
+ function concatRetime(child, parent) {
1763
+ return {
1764
+ start: parent.start + parent.stretch * child.start,
1765
+ stretch: parent.stretch * child.stretch
2069
1766
  };
2070
- _segmentCache.set(prevKf, entry);
2071
- return entry;
2072
1767
  }
2073
- function evaluateMotionPathSegment(prevKf, nextKf, prevPos, nextPos, localProgress, autoOrient) {
2074
- const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
2075
- const t = seg.totalArc === 0 ? localProgress : bezier2D_tForDistance(seg.lut, localProgress * seg.totalArc);
2076
- const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2077
- if (!autoOrient) return { translate: point };
2078
- const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2079
- const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
2080
- return { translate: point, rotateDeg };
1768
+ function applyAllRetimeEffects(root, ctx) {
1769
+ var _a;
1770
+ ctx.idMap.clear();
1771
+ indexById(root, ctx.idMap);
1772
+ const sites = [];
1773
+ const collect = (n) => {
1774
+ var _a2, _b;
1775
+ if ((_a2 = n.effects) == null ? void 0 : _a2.retime) sites.push(n);
1776
+ (_b = n.children) == null ? void 0 : _b.forEach(collect);
1777
+ };
1778
+ collect(root);
1779
+ for (const useNode of sites) {
1780
+ const retime = (_a = useNode.effects) == null ? void 0 : _a.retime;
1781
+ if (!retime) continue;
1782
+ delete useNode.effects.retime;
1783
+ if (Object.keys(useNode.effects).length === 0) delete useNode.effects;
1784
+ materialiseRetime(useNode, asRetime(retime), ctx);
1785
+ }
1786
+ }
1787
+ function materialiseRetime(useNode, retime, ctx) {
1788
+ const targetId = stripHash(useNode.href);
1789
+ if (!targetId) {
1790
+ ctx.errors.push("retime: <use> has no href to follow");
1791
+ return;
1792
+ }
1793
+ const chainRootId = buildChainClone(targetId, retime, ctx, /* @__PURE__ */ new Set());
1794
+ if (!chainRootId) return;
1795
+ if (RETIME_MATERIALISATION_MODE_INLINE_G) {
1796
+ const cloneNode = ctx.idMap.get(chainRootId);
1797
+ useNode.type = "g";
1798
+ delete useNode.href;
1799
+ useNode.children = [cloneNode];
1800
+ applyUseOffsetToG(useNode);
1801
+ ctx.defs = ctx.defs.filter((d) => d !== cloneNode);
1802
+ } else {
1803
+ useNode.href = "#" + chainRootId;
1804
+ }
2081
1805
  }
2082
-
2083
- // src/PxDefinitions.ts
2084
- function parsePathCommands(d) {
2085
- const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
2086
- const commands = [];
2087
- let currentCommand = null;
2088
- for (const token of tokens) {
2089
- if (/[MLCZmlcz]/.test(token)) {
2090
- currentCommand = { type: token, values: [] };
2091
- commands.push(currentCommand);
2092
- } else if (currentCommand) {
2093
- const value = +token;
2094
- currentCommand.values.push(Number.isNaN(value) ? 0 : value);
1806
+ function buildChainClone(targetId, accum, ctx, chain) {
1807
+ var _a, _b;
1808
+ if (chain.has(targetId)) {
1809
+ ctx.errors.push('retime: loop via "' + targetId + '"');
1810
+ return void 0;
1811
+ }
1812
+ const target = ctx.idMap.get(targetId);
1813
+ if (!target) {
1814
+ ctx.warnings.push('retime: target "' + targetId + '" not found');
1815
+ return void 0;
1816
+ }
1817
+ const cloneNode = clone(target);
1818
+ regenerateIdsInClone(cloneNode, ctx);
1819
+ if (target.type === "use") {
1820
+ remapKeyframeTimesOnly(cloneNode, accum.start, accum.stretch);
1821
+ } else {
1822
+ remapKeyframeTimes(cloneNode, accum.start, accum.stretch);
1823
+ }
1824
+ if ((_a = cloneNode.effects) == null ? void 0 : _a.retime) {
1825
+ delete cloneNode.effects.retime;
1826
+ if (Object.keys(cloneNode.effects).length === 0) delete cloneNode.effects;
1827
+ }
1828
+ if (target.type === "use" && target.href) {
1829
+ const subId = stripHash(target.href);
1830
+ if (subId) {
1831
+ const innerRetime = (_b = target.effects) == null ? void 0 : _b.retime;
1832
+ const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;
1833
+ const subChain = new Set(chain);
1834
+ subChain.add(targetId);
1835
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
1836
+ if (subId2) cloneNode.href = "#" + subId2;
2095
1837
  }
2096
1838
  }
2097
- return commands;
1839
+ ctx.defs.push(cloneNode);
1840
+ if (typeof cloneNode.id === "string") ctx.idMap.set(cloneNode.id, cloneNode);
1841
+ return typeof cloneNode.id === "string" ? cloneNode.id : void 0;
2098
1842
  }
2099
- function parseSvgPathToBezier(d) {
2100
- const res = [];
2101
- let currentPath;
2102
- const commands = parsePathCommands(d);
2103
- for (const command of commands) {
2104
- const type = command.type;
2105
- const values = command.values;
2106
- if (type === "M" || type === "m") {
2107
- const x = values[0] || 0;
2108
- const y = values[1] || 0;
2109
- currentPath = {
1843
+ function remapKeyframeTimes(node, start, stretch) {
1844
+ var _a;
1845
+ remapKeyframeTimesOnly(node, start, stretch);
1846
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => remapKeyframeTimes(c, start, stretch));
1847
+ }
1848
+ function remapKeyframeTimesOnly(node, start, stretch) {
1849
+ const remap2 = (kfs) => {
1850
+ for (const kf of kfs) if (typeof kf.time === "number") kf.time = start + kf.time * stretch;
1851
+ };
1852
+ if (node.transform && typeof node.transform === "object" && Array.isArray(node.transform.keyframes)) {
1853
+ remap2(node.transform.keyframes);
1854
+ }
1855
+ if (node.animate && typeof node.animate === "object") {
1856
+ for (const prop of Object.keys(node.animate)) {
1857
+ const anim = node.animate[prop];
1858
+ if (anim && Array.isArray(anim.keyframes)) remap2(anim.keyframes);
1859
+ }
1860
+ }
1861
+ }
1862
+
1863
+ // src/effects/textAlongPathEffect.ts
1864
+ function applyTextAlongPathEffect(node, fx, _ctx) {
1865
+ var _a;
1866
+ if (!fx) return node;
1867
+ const href = typeof fx.href === "string" && !fx.href.startsWith("#") ? "#" + fx.href : fx.href;
1868
+ const textPath = {
1869
+ type: "textPath",
1870
+ href,
1871
+ children: (_a = node.children) != null ? _a : []
1872
+ };
1873
+ if (fx.lengthAdjust !== void 0) textPath.lengthAdjust = fx.lengthAdjust;
1874
+ if (fx.method !== void 0) textPath.method = fx.method;
1875
+ if (fx.spacing !== void 0) textPath.spacing = fx.spacing;
1876
+ applyAnimatableNumber(textPath, "startOffset", fx.startOffset);
1877
+ applyAnimatableNumber(textPath, "textLength", fx.textLength);
1878
+ node.children = [textPath];
1879
+ return node;
1880
+ }
1881
+ function applyAnimatableNumber(node, attrName, raw) {
1882
+ if (raw === void 0 || raw === null) return;
1883
+ if (typeof raw === "number") {
1884
+ node[attrName] = String(raw);
1885
+ return;
1886
+ }
1887
+ if (typeof raw === "object") {
1888
+ const obj = raw;
1889
+ if (Array.isArray(obj.keyframes)) {
1890
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
1891
+ const animate = __spreadValues({}, prevAnimate || {});
1892
+ animate[attrName] = { keyframes: obj.keyframes };
1893
+ node.animate = animate;
1894
+ return;
1895
+ }
1896
+ if (typeof obj.value === "number") {
1897
+ node[attrName] = String(obj.value);
1898
+ return;
1899
+ }
1900
+ }
1901
+ }
1902
+
1903
+ // src/PxAnimatorUtil.ts
1904
+ function bezierToSvgPath(path) {
1905
+ var _a, _b, _c, _d;
1906
+ const v = path.v;
1907
+ const i = path.i;
1908
+ const o = path.o;
1909
+ const c = path.c;
1910
+ if (!v.length) return "";
1911
+ const d = [];
1912
+ const len = v.length;
1913
+ d.push("M" + v[0][0] + "," + v[0][1]);
1914
+ for (let idx = 1; idx < len; idx++) {
1915
+ const prevV = v[idx - 1];
1916
+ const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
1917
+ const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
1918
+ const currV = v[idx];
1919
+ const isLine = prevO[0] === prevV[0] && prevO[1] === prevV[1] && (currI[0] === currV[0] && currI[1] === currV[1]);
1920
+ if (isLine) {
1921
+ d.push("L" + currV[0] + "," + currV[1]);
1922
+ } else {
1923
+ d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
1924
+ }
1925
+ }
1926
+ if (c && len > 0) {
1927
+ const lastV = v[len - 1];
1928
+ const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
1929
+ const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
1930
+ const firstV = v[0];
1931
+ const isLine = lastO[0] === lastV[0] && lastO[1] === lastV[1] && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
1932
+ if (!isLine) {
1933
+ d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
1934
+ }
1935
+ d.push("z");
1936
+ }
1937
+ return d.join("");
1938
+ }
1939
+ function interpolateNum(a, b, t) {
1940
+ return a + (b - a) * t;
1941
+ }
1942
+ function interpolateVec(a, b, t) {
1943
+ const res = [];
1944
+ const count = Math.max(a.length, b.length);
1945
+ for (let i = 0; i < count; i++) {
1946
+ res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
1947
+ }
1948
+ return res;
1949
+ }
1950
+ function interpolateColor(a, b, t) {
1951
+ return [
1952
+ interpolateNum(a[0] || 0, b[0] || 0, t),
1953
+ interpolateNum(a[1] || 0, b[1] || 0, t),
1954
+ interpolateNum(a[2] || 0, b[2] || 0, t),
1955
+ interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
1956
+ ];
1957
+ }
1958
+ function interpolateBeziers(paths1, paths2, progress) {
1959
+ const count = Math.max(paths1.length, paths2.length);
1960
+ const res = [];
1961
+ for (let i = 0; i < count; i++) {
1962
+ res.push(interpolateBezier(paths1[i], paths2[i], progress));
1963
+ }
1964
+ return res;
1965
+ }
1966
+ function interpolateBezier(path1, path2, progress) {
1967
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1968
+ if (!path1 || !path2) return path1 || path2 || { v: [] };
1969
+ const t = Math.min(Math.max(progress, 0), 1);
1970
+ const len = Math.min(path1.v.length, path2.v.length);
1971
+ const v = [];
1972
+ const i = [];
1973
+ const o = [];
1974
+ for (let idx = 0; idx < len; idx++) {
1975
+ const v1 = path1.v[idx];
1976
+ const v2 = path2.v[idx];
1977
+ v.push(interpolateVec(v1, v2, t));
1978
+ const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
1979
+ const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
1980
+ i.push(interpolateVec(i1, i2, t));
1981
+ const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
1982
+ const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
1983
+ o.push(interpolateVec(o1, o2, t));
1984
+ }
1985
+ return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
1986
+ }
1987
+ function remap(value, inMin, inMax, outMin, outMax) {
1988
+ if (inMax === inMin) return outMin;
1989
+ const t = (value - inMin) / (inMax - inMin);
1990
+ return outMin + t * (outMax - outMin);
1991
+ }
1992
+ function solveCubicBezierX(p1x, p2x, x) {
1993
+ if (x <= 0) return 0;
1994
+ if (x >= 1) return 1;
1995
+ const cx = 3 * p1x;
1996
+ const bx = 3 * (p2x - p1x) - cx;
1997
+ const ax = 1 - cx - bx;
1998
+ function sampleX(t) {
1999
+ return ((ax * t + bx) * t + cx) * t;
2000
+ }
2001
+ function sampleDX(t) {
2002
+ return (3 * ax * t + 2 * bx) * t + cx;
2003
+ }
2004
+ let t2 = x;
2005
+ let t0 = 0;
2006
+ let t1 = 1;
2007
+ for (let i = 0; i < 8; i++) {
2008
+ const x2 = sampleX(t2) - x;
2009
+ if (Math.abs(x2) < 1e-6) return t2;
2010
+ const d2 = sampleDX(t2);
2011
+ if (Math.abs(d2) < 1e-6) break;
2012
+ t2 -= x2 / d2;
2013
+ }
2014
+ t2 = x;
2015
+ while (t0 < t1) {
2016
+ const x2 = sampleX(t2);
2017
+ if (Math.abs(x2 - x) < 1e-6) return t2;
2018
+ if (x > x2) t0 = t2;
2019
+ else t1 = t2;
2020
+ t2 = (t1 + t0) / 2;
2021
+ }
2022
+ return t2;
2023
+ }
2024
+ function cubicBezier(easing) {
2025
+ const [p1x, p1y, p2x, p2y] = easing;
2026
+ const cy = 3 * p1y;
2027
+ const by = 3 * (p2y - p1y) - cy;
2028
+ const ay = 1 - cy - by;
2029
+ function sampleCurveY(t) {
2030
+ return ((ay * t + by) * t + cy) * t;
2031
+ }
2032
+ return function(x) {
2033
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
2034
+ };
2035
+ }
2036
+ function lerp2(a, b, t) {
2037
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
2038
+ }
2039
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
2040
+ const q0 = lerp2(p0, p1, t);
2041
+ const q1 = lerp2(p1, p2, t);
2042
+ const q2 = lerp2(p2, p3, t);
2043
+ const r0 = lerp2(q0, q1, t);
2044
+ const r1 = lerp2(q1, q2, t);
2045
+ const s = lerp2(r0, r1, t);
2046
+ return {
2047
+ left: [p0, q0, r0, s],
2048
+ right: [s, r1, q2, p3]
2049
+ };
2050
+ }
2051
+ function splitEasing(easing, xFraction) {
2052
+ if (!easing) return { left: void 0, right: void 0 };
2053
+ if (xFraction <= 0) return { left: void 0, right: easing };
2054
+ if (xFraction >= 1) return { left: easing, right: void 0 };
2055
+ const [x1, y1, x2, y2] = easing;
2056
+ const t = solveCubicBezierX(x1, x2, xFraction);
2057
+ const p0 = [0, 0];
2058
+ const p1 = [x1, y1];
2059
+ const p2 = [x2, y2];
2060
+ const p3 = [1, 1];
2061
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
2062
+ const sx = left[3][0];
2063
+ const sy = left[3][1];
2064
+ let leftEasing;
2065
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
2066
+ leftEasing = [
2067
+ left[1][0] / sx,
2068
+ left[1][1] / sy,
2069
+ left[2][0] / sx,
2070
+ left[2][1] / sy
2071
+ ];
2072
+ }
2073
+ let rightEasing;
2074
+ const rx = 1 - sx;
2075
+ const ry = 1 - sy;
2076
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
2077
+ rightEasing = [
2078
+ (right[1][0] - sx) / rx,
2079
+ (right[1][1] - sy) / ry,
2080
+ (right[2][0] - sx) / rx,
2081
+ (right[2][1] - sy) / ry
2082
+ ];
2083
+ }
2084
+ return { left: leftEasing, right: rightEasing };
2085
+ }
2086
+ function reverseEasing(easing) {
2087
+ if (!easing) return void 0;
2088
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
2089
+ }
2090
+ function toRGBA(color) {
2091
+ const r = Math.round(color[0] * 255);
2092
+ const g = Math.round(color[1] * 255);
2093
+ const b = Math.round(color[2] * 255);
2094
+ return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
2095
+ }
2096
+ function parseRgba(s) {
2097
+ var _a;
2098
+ const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
2099
+ if (!inner) throw new Error("Invalid rgb/rgba format");
2100
+ const parts = inner.split(",").map((v) => +v.trim());
2101
+ return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
2102
+ }
2103
+ function parseHex(s) {
2104
+ const hex = s.slice(1);
2105
+ const isShort = hex.length <= 4;
2106
+ const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
2107
+ const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
2108
+ const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
2109
+ const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
2110
+ const result = [
2111
+ parseInt(r, 16) / 255,
2112
+ parseInt(g, 16) / 255,
2113
+ parseInt(b, 16) / 255
2114
+ ];
2115
+ if (a !== null) {
2116
+ result.push(parseInt(a, 16) / 255);
2117
+ }
2118
+ return result;
2119
+ }
2120
+ function parseColor(s) {
2121
+ if (!s) return void 0;
2122
+ if (Array.isArray(s)) return s;
2123
+ if (typeof s !== "string") return void 0;
2124
+ if (s.startsWith("#")) {
2125
+ return parseHex(s);
2126
+ } else if (s.startsWith("rgb")) {
2127
+ return parseRgba(s);
2128
+ } else {
2129
+ console.warn("Unsupported color format: " + s);
2130
+ }
2131
+ return void 0;
2132
+ }
2133
+ var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
2134
+ var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
2135
+ var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
2136
+ function composeTransformParts(parts, opts) {
2137
+ var _a;
2138
+ if (!parts) return "";
2139
+ const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
2140
+ const segs = [];
2141
+ const t = parts.translate;
2142
+ const o = parts.origin;
2143
+ const r = parts.rotate;
2144
+ const s = parts.scale;
2145
+ const tu = withUnits ? "px" : "";
2146
+ const ru = withUnits ? "deg" : "";
2147
+ if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
2148
+ if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
2149
+ if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
2150
+ if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
2151
+ if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
2152
+ return segs.join("");
2153
+ }
2154
+ var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
2155
+ var DEFAULT_DURATION_MS = 1e3;
2156
+ function kebabToCamelCaseWord(kebab) {
2157
+ return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
2158
+ }
2159
+ function isCamelCaseWord(word) {
2160
+ return !word.includes("-") && /[a-z][A-Z]/.test(word);
2161
+ }
2162
+ var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
2163
+ // Transform/positioning
2164
+ "viewBox",
2165
+ "preserveAspectRatio",
2166
+ // Gradient
2167
+ "gradientUnits",
2168
+ "gradientTransform",
2169
+ "spreadMethod",
2170
+ // Pattern
2171
+ "patternUnits",
2172
+ "patternContentUnits",
2173
+ "patternTransform",
2174
+ // Clipping/masking
2175
+ "clipPathUnits",
2176
+ "maskUnits",
2177
+ "maskContentUnits",
2178
+ // Marker (SVG spec keeps these camelCase, like viewBox)
2179
+ "markerUnits",
2180
+ "markerWidth",
2181
+ "markerHeight",
2182
+ "refX",
2183
+ "refY",
2184
+ // Text
2185
+ "textLength",
2186
+ "lengthAdjust",
2187
+ "startOffset",
2188
+ // Filter
2189
+ "filterUnits",
2190
+ "primitiveUnits",
2191
+ "stdDeviation",
2192
+ "baseFrequency",
2193
+ "numOctaves",
2194
+ "surfaceScale",
2195
+ "diffuseConstant",
2196
+ "specularConstant",
2197
+ "specularExponent",
2198
+ "kernelMatrix",
2199
+ "kernelUnitLength",
2200
+ "edgeMode",
2201
+ "preserveAlpha",
2202
+ "targetX",
2203
+ "targetY"
2204
+ // // Animation
2205
+ // 'attributeName',
2206
+ // 'attributeType',
2207
+ // 'calcMode',
2208
+ // 'keyTimes',
2209
+ // 'keySplines',
2210
+ // 'repeatCount',
2211
+ // 'repeatDur'
2212
+ ]);
2213
+ function camelCaseToKebabWordIfNeeded(camel) {
2214
+ return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
2215
+ }
2216
+ function clamp(value, min, max) {
2217
+ return Math.max(min, Math.min(value, max));
2218
+ }
2219
+ function bezier2D_pointAt(P0, P1, P2, P3, t) {
2220
+ if (t <= 0) return [P0[0], P0[1]];
2221
+ if (t >= 1) return [P3[0], P3[1]];
2222
+ const u = 1 - t;
2223
+ const u2 = u * u;
2224
+ const u3 = u2 * u;
2225
+ const t2 = t * t;
2226
+ const t3 = t2 * t;
2227
+ const w0 = u3;
2228
+ const w1 = 3 * t * u2;
2229
+ const w2 = 3 * t2 * u;
2230
+ const w3 = t3;
2231
+ return [
2232
+ w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
2233
+ w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
2234
+ ];
2235
+ }
2236
+ var BEZIER_T_NUDGE = 1e-4;
2237
+ function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
2238
+ const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
2239
+ if (result[0] === 0 && result[1] === 0) {
2240
+ const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
2241
+ return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
2242
+ }
2243
+ return result;
2244
+ }
2245
+ function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
2246
+ const u = 1 - t;
2247
+ const a = 3 * u * u;
2248
+ const b = 6 * t * u;
2249
+ const c = 3 * t * t;
2250
+ return [
2251
+ a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
2252
+ a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
2253
+ ];
2254
+ }
2255
+ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
2256
+ const n = steps + 1;
2257
+ const ts = new Float64Array(n);
2258
+ const ds = new Float64Array(n);
2259
+ let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
2260
+ ts[0] = 0;
2261
+ ds[0] = 0;
2262
+ let cum = 0;
2263
+ for (let i = 1; i < n; i++) {
2264
+ const t = i / steps;
2265
+ const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
2266
+ const dx = cur[0] - prev[0];
2267
+ const dy = cur[1] - prev[1];
2268
+ cum += Math.sqrt(dx * dx + dy * dy);
2269
+ ts[i] = t;
2270
+ ds[i] = cum;
2271
+ prev = cur;
2272
+ }
2273
+ return { ts, ds };
2274
+ }
2275
+ function bezier2D_arcAtT(lut, t) {
2276
+ const { ts, ds } = lut;
2277
+ const last = ts.length - 1;
2278
+ if (t <= ts[0]) return ds[0];
2279
+ if (t >= ts[last]) return ds[last];
2280
+ let lo = 1, hi = last;
2281
+ while (lo < hi) {
2282
+ const mid = lo + hi >>> 1;
2283
+ if (ts[mid] < t) lo = mid + 1;
2284
+ else hi = mid;
2285
+ }
2286
+ const tPrev = ts[hi - 1];
2287
+ const span = ts[hi] - tPrev;
2288
+ const frac = span > 0 ? (t - tPrev) / span : 0;
2289
+ return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);
2290
+ }
2291
+ function invertEasing(easing) {
2292
+ if (!easing) return (y) => y;
2293
+ const flipped = [easing[1], easing[0], easing[3], easing[2]];
2294
+ return cubicBezier(flipped);
2295
+ }
2296
+
2297
+ // src/PxMotionPath.ts
2298
+ function getKfTranslate(kf) {
2299
+ var _a;
2300
+ const v = (_a = kf.value) != null ? _a : kf.v;
2301
+ if (!v) return void 0;
2302
+ if (Array.isArray(v) && v.length >= 2 && typeof v[0] === "number" && typeof v[1] === "number") {
2303
+ return [v[0], v[1]];
2304
+ }
2305
+ const tr = v.translate;
2306
+ if (Array.isArray(tr) && tr.length >= 2) return [tr[0], tr[1]];
2307
+ return void 0;
2308
+ }
2309
+ function getKfTime(kf) {
2310
+ var _a, _b;
2311
+ return (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
2312
+ }
2313
+ function getKfEasing(kf) {
2314
+ var _a;
2315
+ return (_a = kf.easing) != null ? _a : kf.e;
2316
+ }
2317
+ function propAnimIsMotionPath(anim) {
2318
+ var _a, _b, _c;
2319
+ const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
2320
+ if (!Array.isArray(kfs)) return false;
2321
+ if (anim.autoOrient) return true;
2322
+ for (const kf of kfs) {
2323
+ if (((_b = kf.tangentIn) != null ? _b : kf.ti) || ((_c = kf.tangentOut) != null ? _c : kf.to)) return true;
2324
+ }
2325
+ return false;
2326
+ }
2327
+ var _segmentCache = /* @__PURE__ */ new WeakMap();
2328
+ function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
2329
+ var _a, _b;
2330
+ const existing = _segmentCache.get(prevKf);
2331
+ if (existing) return existing;
2332
+ const to = (_a = prevKf.tangentOut) != null ? _a : prevKf.to;
2333
+ const ti = (_b = nextKf.tangentIn) != null ? _b : nextKf.ti;
2334
+ const P1 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];
2335
+ const P2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];
2336
+ const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);
2337
+ const entry = {
2338
+ P0: prevPos,
2339
+ P1,
2340
+ P2,
2341
+ P3: nextPos,
2342
+ lut,
2343
+ totalArc: lut.ds[lut.ds.length - 1]
2344
+ };
2345
+ _segmentCache.set(prevKf, entry);
2346
+ return entry;
2347
+ }
2348
+ function evaluateMotionPathSegment(prevKf, nextKf, prevPos, nextPos, localProgress, autoOrient) {
2349
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
2350
+ const t = seg.totalArc === 0 ? localProgress : tFromArcFraction(seg.lut, localProgress);
2351
+ const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2352
+ if (!autoOrient) return { translate: point };
2353
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2354
+ const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
2355
+ return { translate: point, rotateDeg };
2356
+ }
2357
+ function tFromArcFraction(lut, arcFrac) {
2358
+ const total = lut.ds[lut.ds.length - 1];
2359
+ const target = arcFrac * total;
2360
+ const { ts, ds } = lut;
2361
+ const last = ds.length - 1;
2362
+ if (target <= 0) return ts[0];
2363
+ if (target >= ds[last]) return ts[last];
2364
+ let lo = 1, hi = last;
2365
+ while (lo < hi) {
2366
+ const mid = lo + hi >>> 1;
2367
+ if (ds[mid] < target) lo = mid + 1;
2368
+ else hi = mid;
2369
+ }
2370
+ const dPrev = ds[hi - 1];
2371
+ const span = ds[hi] - dPrev;
2372
+ const frac = span > 0 ? (target - dPrev) / span : 0;
2373
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
2374
+ }
2375
+ var DEFAULT_FLATNESS_TOL = 0.5;
2376
+ var DEFAULT_ROTATION_TOL = 5;
2377
+ var DEFAULT_MAX_SAMPLES = 32;
2378
+ function materialiseMotionPathInPropAnim(anim, opts) {
2379
+ var _a, _b, _c, _d;
2380
+ if (!propAnimIsMotionPath(anim)) return anim;
2381
+ const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
2382
+ if (!Array.isArray(kfs) || kfs.length < 2) return anim;
2383
+ const autoOrient = !!anim.autoOrient;
2384
+ const flatnessTol = (_b = opts == null ? void 0 : opts.flatnessTolerance) != null ? _b : DEFAULT_FLATNESS_TOL;
2385
+ const rotationTol = (_c = opts == null ? void 0 : opts.rotationTolerance) != null ? _c : DEFAULT_ROTATION_TOL;
2386
+ const maxSamples = (_d = opts == null ? void 0 : opts.maxSamplesPerSegment) != null ? _d : DEFAULT_MAX_SAMPLES;
2387
+ const out = [];
2388
+ const firstPos = getKfTranslate(kfs[0]);
2389
+ if (!firstPos) return anim;
2390
+ const firstRotate = autoOrient ? derivAngleForFirstKf(kfs[0], kfs[1]) : void 0;
2391
+ out.push(makeOutKf(
2392
+ getKfTime(kfs[0]),
2393
+ buildOutKfValue(getKfValueParts(kfs[0]), getKfValueParts(kfs[0]), 0, firstPos, firstRotate, autoOrient)
2394
+ ));
2395
+ for (let i = 0; i < kfs.length - 1; i++) {
2396
+ const prevKf = kfs[i];
2397
+ const nextKf = kfs[i + 1];
2398
+ const prevPos = getKfTranslate(prevKf);
2399
+ const nextPos = getKfTranslate(nextKf);
2400
+ if (!prevPos || !nextPos) {
2401
+ out.push(makeOutKf(
2402
+ getKfTime(nextKf),
2403
+ buildOutKfValue(getKfValueParts(nextKf), getKfValueParts(nextKf), 1, nextPos != null ? nextPos : [0, 0], void 0, autoOrient)
2404
+ ));
2405
+ continue;
2406
+ }
2407
+ if (autoOrient && i > 0) {
2408
+ insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol);
2409
+ }
2410
+ materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
2411
+ }
2412
+ const lastInE = getKfEasing(kfs[kfs.length - 1]);
2413
+ if (lastInE) out[out.length - 1].e = lastInE;
2414
+ if (autoOrient) unwrapAutoOrientRotations(out);
2415
+ const result = { kfs: out };
2416
+ if (anim.loop !== void 0) result.loop = anim.loop;
2417
+ return result;
2418
+ }
2419
+ function unwrapAutoOrientRotations(kfs) {
2420
+ var _a;
2421
+ let prev;
2422
+ for (const kf of kfs) {
2423
+ const v = (_a = kf.v) != null ? _a : kf.value;
2424
+ if (!v || typeof v.rotate !== "number") continue;
2425
+ if (prev === void 0) {
2426
+ prev = v.rotate;
2427
+ continue;
2428
+ }
2429
+ let r = v.rotate;
2430
+ while (r - prev > 180) r -= 360;
2431
+ while (r - prev < -180) r += 360;
2432
+ v.rotate = r;
2433
+ prev = r;
2434
+ }
2435
+ }
2436
+ function makeOutKf(time, value) {
2437
+ return { t: time, v: value };
2438
+ }
2439
+ function getKfValueParts(kf) {
2440
+ var _a;
2441
+ const v = (_a = kf.value) != null ? _a : kf.v;
2442
+ if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
2443
+ return v;
2444
+ }
2445
+ function interpolatePart(prev, next, p) {
2446
+ if (prev === void 0) return next;
2447
+ if (next === void 0) return prev;
2448
+ if (typeof prev === "number" && typeof next === "number") {
2449
+ return prev + (next - prev) * p;
2450
+ }
2451
+ if (Array.isArray(prev) && Array.isArray(next) && prev.length === next.length) {
2452
+ const out = new Array(prev.length);
2453
+ for (let i = 0; i < prev.length; i++) {
2454
+ const a = typeof prev[i] === "number" ? prev[i] : 0;
2455
+ const b = typeof next[i] === "number" ? next[i] : 0;
2456
+ out[i] = a + (b - a) * p;
2457
+ }
2458
+ return out;
2459
+ }
2460
+ return p < 0.5 ? prev : next;
2461
+ }
2462
+ function buildOutKfValue(prevV, nextV, p, translate, rotateDegFromAutoOrient, autoOrient) {
2463
+ const value = { translate };
2464
+ const keys = /* @__PURE__ */ new Set();
2465
+ if (prevV) for (const k of Object.keys(prevV)) keys.add(k);
2466
+ if (nextV) for (const k of Object.keys(nextV)) keys.add(k);
2467
+ for (const k of keys) {
2468
+ if (k === "translate") continue;
2469
+ if (k === "rotate" && autoOrient) continue;
2470
+ const pv = prevV == null ? void 0 : prevV[k];
2471
+ const nv = nextV == null ? void 0 : nextV[k];
2472
+ if (pv === void 0 && nv === void 0) continue;
2473
+ value[k] = interpolatePart(pv, nv, p);
2474
+ }
2475
+ if (rotateDegFromAutoOrient !== void 0) value.rotate = rotateDegFromAutoOrient;
2476
+ return value;
2477
+ }
2478
+ function derivAngleForFirstKf(kf0, kf1) {
2479
+ const p0 = getKfTranslate(kf0);
2480
+ const p1 = getKfTranslate(kf1);
2481
+ if (!p0 || !p1) return 0;
2482
+ const seg = getSegmentCache(kf0, kf1, p0, p1);
2483
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, 0);
2484
+ return Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
2485
+ }
2486
+ function wrappedAngleDelta(a, b) {
2487
+ let d = a - b;
2488
+ while (d > 180) d -= 360;
2489
+ while (d < -180) d += 360;
2490
+ return d;
2491
+ }
2492
+ function insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol) {
2493
+ var _a;
2494
+ const lastKf = out[out.length - 1];
2495
+ const lastV = (_a = lastKf.v) != null ? _a : lastKf.value;
2496
+ const prevExit = lastV == null ? void 0 : lastV.rotate;
2497
+ if (typeof prevExit !== "number") return;
2498
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
2499
+ const tanAtStart = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, 0);
2500
+ const nextEntry = Math.atan2(tanAtStart[1], tanAtStart[0]) * 180 / Math.PI;
2501
+ const delta = wrappedAngleDelta(nextEntry, prevExit);
2502
+ if (Math.abs(delta) <= rotationTol) return;
2503
+ const dupValue = buildOutKfValue(
2504
+ getKfValueParts(prevKf),
2505
+ getKfValueParts(prevKf),
2506
+ 0,
2507
+ prevPos,
2508
+ nextEntry,
2509
+ true
2510
+ );
2511
+ out.push(makeOutKf(getKfTime(prevKf), dupValue));
2512
+ }
2513
+ function materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
2514
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
2515
+ const prevTime = getKfTime(prevKf);
2516
+ const nextTime = getKfTime(nextKf);
2517
+ const prevEasing = getKfEasing(prevKf);
2518
+ const invertFn = invertEasing(prevEasing);
2519
+ const prevV = getKfValueParts(prevKf);
2520
+ const nextV = getKfValueParts(nextKf);
2521
+ const interiorTs = computeSampleTs(seg, autoOrient, flatnessTol, rotationTol, maxSamples);
2522
+ const samples = [];
2523
+ for (const t of interiorTs) {
2524
+ const arc = bezier2D_arcAtT(seg.lut, t);
2525
+ const p = clamp(seg.totalArc > 0 ? arc / seg.totalArc : t, 0, 1);
2526
+ const u = clamp(invertFn(p), 0, 1);
2527
+ const pos = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2528
+ const sample = { u, p, pos };
2529
+ if (autoOrient) {
2530
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2531
+ sample.rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
2532
+ }
2533
+ samples.push(sample);
2534
+ }
2535
+ let remaining = prevEasing;
2536
+ let prevU = 0;
2537
+ const startIdx = out.length - 1;
2538
+ for (let i = 0; i < samples.length; i++) {
2539
+ const s = samples[i];
2540
+ const xFrac = prevU < 1 ? clamp((s.u - prevU) / (1 - prevU), 0, 1) : 1;
2541
+ const { left, right } = splitEasing(remaining, xFrac);
2542
+ const ownerIdx = i === 0 ? startIdx : out.length - 1;
2543
+ if (left) out[ownerIdx].e = left;
2544
+ else delete out[ownerIdx].e;
2545
+ const tGlobal = prevTime + s.u * (nextTime - prevTime);
2546
+ const value = buildOutKfValue(prevV, nextV, s.p, s.pos, s.rotateDeg, autoOrient);
2547
+ out.push(makeOutKf(tGlobal, value));
2548
+ remaining = right;
2549
+ prevU = s.u;
2550
+ }
2551
+ }
2552
+ function computeSampleTs(seg, autoOrient, flatnessTol, rotationTol, maxSamples) {
2553
+ const extremes = [];
2554
+ addAxisExtremes(seg.P0[0], seg.P1[0], seg.P2[0], seg.P3[0], extremes);
2555
+ addAxisExtremes(seg.P0[1], seg.P1[1], seg.P2[1], seg.P3[1], extremes);
2556
+ extremes.sort((a, b) => a - b);
2557
+ const critical = [0];
2558
+ for (const t of extremes) {
2559
+ if (t > critical[critical.length - 1] + 1e-6 && t < 1 - 1e-6) {
2560
+ critical.push(t);
2561
+ }
2562
+ }
2563
+ critical.push(1);
2564
+ const out = [];
2565
+ const budget = { remaining: maxSamples - critical.length };
2566
+ for (let i = 0; i < critical.length - 1; i++) {
2567
+ bisect(critical[i], critical[i + 1], out, seg, autoOrient, flatnessTol, rotationTol, budget);
2568
+ }
2569
+ return out;
2570
+ }
2571
+ function addAxisExtremes(p0, p1, p2, p3, out) {
2572
+ const a = p1 - p0;
2573
+ const b = p2 - p1;
2574
+ const c = p3 - p2;
2575
+ const A = a - 2 * b + c;
2576
+ const B = 2 * (b - a);
2577
+ const C = a;
2578
+ if (Math.abs(A) < 1e-10) {
2579
+ if (Math.abs(B) > 1e-10) {
2580
+ const t = -C / B;
2581
+ if (t > 1e-6 && t < 1 - 1e-6) out.push(t);
2582
+ }
2583
+ return;
2584
+ }
2585
+ const disc = B * B - 4 * A * C;
2586
+ if (disc < 0) return;
2587
+ const sq = Math.sqrt(disc);
2588
+ const t1 = (-B - sq) / (2 * A);
2589
+ const t2 = (-B + sq) / (2 * A);
2590
+ if (t1 > 1e-6 && t1 < 1 - 1e-6) out.push(t1);
2591
+ if (t2 > 1e-6 && t2 < 1 - 1e-6) out.push(t2);
2592
+ }
2593
+ function bisect(tA, tB, out, seg, autoOrient, flatnessTol, rotationTol, budget) {
2594
+ const tMid = (tA + tB) / 2;
2595
+ const pA = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA);
2596
+ const pB = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tB);
2597
+ const span = tB - tA;
2598
+ const p25 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA + span * 0.25);
2599
+ const p50 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tMid);
2600
+ const p75 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA + span * 0.75);
2601
+ const dev = Math.max(
2602
+ perpDist(p25, pA, pB),
2603
+ perpDist(p50, pA, pB),
2604
+ perpDist(p75, pA, pB)
2605
+ );
2606
+ let rotOk = true;
2607
+ if (autoOrient) {
2608
+ const tanA = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, tA);
2609
+ const tanB = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, tB);
2610
+ const angA = Math.atan2(tanA[1], tanA[0]) * 180 / Math.PI;
2611
+ const angB = Math.atan2(tanB[1], tanB[0]) * 180 / Math.PI;
2612
+ let delta = Math.abs(angA - angB);
2613
+ if (delta > 180) delta = 360 - delta;
2614
+ if (delta > rotationTol) rotOk = false;
2615
+ }
2616
+ if (dev <= flatnessTol && rotOk || budget.remaining <= 0 || span < 1e-6) {
2617
+ out.push(tB);
2618
+ return;
2619
+ }
2620
+ budget.remaining -= 1;
2621
+ bisect(tA, tMid, out, seg, autoOrient, flatnessTol, rotationTol, budget);
2622
+ bisect(tMid, tB, out, seg, autoOrient, flatnessTol, rotationTol, budget);
2623
+ }
2624
+ function perpDist(q, pA, pB) {
2625
+ const dx = pB[0] - pA[0];
2626
+ const dy = pB[1] - pA[1];
2627
+ const len2 = dx * dx + dy * dy;
2628
+ if (len2 < 1e-20) {
2629
+ const qdx = q[0] - pA[0];
2630
+ const qdy = q[1] - pA[1];
2631
+ return Math.sqrt(qdx * qdx + qdy * qdy);
2632
+ }
2633
+ const cross = (q[0] - pA[0]) * dy - (q[1] - pA[1]) * dx;
2634
+ return Math.abs(cross) / Math.sqrt(len2);
2635
+ }
2636
+ function materialiseMotionPathsInTree(root, opts) {
2637
+ const out = walkAndMaterialise(root, opts);
2638
+ return out != null ? out : root;
2639
+ }
2640
+ function walkAndMaterialise(node, opts) {
2641
+ let newChildren;
2642
+ if (node.children) {
2643
+ for (let i = 0; i < node.children.length; i++) {
2644
+ const ch = node.children[i];
2645
+ const ret = walkAndMaterialise(ch, opts);
2646
+ if (ret !== null) {
2647
+ if (!newChildren) newChildren = node.children.slice();
2648
+ newChildren[i] = ret;
2649
+ }
2650
+ }
2651
+ }
2652
+ let newAnimate;
2653
+ const animBucket = node.animate;
2654
+ if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
2655
+ const animDef = animBucket;
2656
+ const transformAnim = animDef.transform;
2657
+ if (transformAnim && typeof transformAnim === "object" && propAnimIsMotionPath(transformAnim)) {
2658
+ const materialised = materialiseMotionPathInPropAnim(transformAnim, opts);
2659
+ if (materialised !== transformAnim) {
2660
+ newAnimate = __spreadProps(__spreadValues({}, animDef), { transform: materialised });
2661
+ }
2662
+ }
2663
+ }
2664
+ if (!newChildren && !newAnimate) return null;
2665
+ const cloned = __spreadValues({}, node);
2666
+ if (newChildren) cloned.children = newChildren;
2667
+ if (newAnimate) cloned.animate = newAnimate;
2668
+ return cloned;
2669
+ }
2670
+
2671
+ // src/PxDefinitions.ts
2672
+ function parsePathCommands(d) {
2673
+ const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
2674
+ const commands = [];
2675
+ let currentCommand = null;
2676
+ for (const token of tokens) {
2677
+ if (/[MLCZmlcz]/.test(token)) {
2678
+ currentCommand = { type: token, values: [] };
2679
+ commands.push(currentCommand);
2680
+ } else if (currentCommand) {
2681
+ const value = +token;
2682
+ currentCommand.values.push(Number.isNaN(value) ? 0 : value);
2683
+ }
2684
+ }
2685
+ return commands;
2686
+ }
2687
+ function parseSvgPathToBezier(d) {
2688
+ const res = [];
2689
+ let currentPath;
2690
+ const commands = parsePathCommands(d);
2691
+ for (const command of commands) {
2692
+ const type = command.type;
2693
+ const values = command.values;
2694
+ if (type === "M" || type === "m") {
2695
+ const x = values[0] || 0;
2696
+ const y = values[1] || 0;
2697
+ currentPath = {
2110
2698
  v: [[x, y]],
2111
2699
  i: [[x, y]],
2112
2700
  o: [[x, y]],
@@ -2115,471 +2703,1296 @@ function parseSvgPathToBezier(d) {
2115
2703
  res.push(currentPath);
2116
2704
  continue;
2117
2705
  }
2118
- if (!currentPath) {
2119
- currentPath = {
2120
- v: [[0, 0]],
2121
- i: [[0, 0]],
2122
- o: [[0, 0]],
2123
- c: false
2124
- };
2125
- res.push(currentPath);
2706
+ if (!currentPath) {
2707
+ currentPath = {
2708
+ v: [[0, 0]],
2709
+ i: [[0, 0]],
2710
+ o: [[0, 0]],
2711
+ c: false
2712
+ };
2713
+ res.push(currentPath);
2714
+ }
2715
+ if (type === "L") {
2716
+ const x = values[0] || 0;
2717
+ const y = values[1] || 0;
2718
+ currentPath.v.push([x, y]);
2719
+ currentPath.i.push([x, y]);
2720
+ currentPath.o.push([x, y]);
2721
+ } else if (type === "C") {
2722
+ const outX = values[0] || 0;
2723
+ const outY = values[1] || 0;
2724
+ const inX2 = values[2] || 0;
2725
+ const inY2 = values[3] || 0;
2726
+ const x2 = values[4] || 0;
2727
+ const y2 = values[5] || 0;
2728
+ currentPath.o[currentPath.o.length - 1] = [outX, outY];
2729
+ currentPath.v.push([x2, y2]);
2730
+ currentPath.i.push([inX2, inY2]);
2731
+ currentPath.o.push([x2, y2]);
2732
+ } else if (type === "Z" || type === "z") {
2733
+ currentPath.c = true;
2734
+ } else {
2735
+ console.warn('Unsupported path command "' + type + '"');
2736
+ }
2737
+ }
2738
+ return res;
2739
+ }
2740
+ function extractPathData(str) {
2741
+ if (str.startsWith("path(") && str.endsWith(")")) {
2742
+ return str.slice(5, -1);
2743
+ }
2744
+ if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str)) {
2745
+ return str;
2746
+ }
2747
+ return void 0;
2748
+ }
2749
+ function isPathString(value) {
2750
+ return typeof value === "string" && extractPathData(value) !== void 0;
2751
+ }
2752
+ function normalizePathValue(value) {
2753
+ if (value && typeof value === "object" && typeof value.path === "string") {
2754
+ const d = extractPathData(value.path);
2755
+ return d ? { paths: parseSvgPathToBezier(d) } : value;
2756
+ }
2757
+ if (value && typeof value === "object" && "paths" in value) {
2758
+ const pathsArray = value.paths;
2759
+ if (Array.isArray(pathsArray) && pathsArray.length > 0) {
2760
+ if (isPathString(pathsArray[0])) {
2761
+ const paths = [];
2762
+ for (const pathStr2 of pathsArray) {
2763
+ const d = extractPathData(pathStr2);
2764
+ if (d) {
2765
+ paths.push(...parseSvgPathToBezier(d));
2766
+ }
2767
+ }
2768
+ return { paths };
2769
+ }
2770
+ }
2771
+ return value;
2772
+ }
2773
+ if (Array.isArray(value)) {
2774
+ if (value.length > 0 && isPathString(value[0])) {
2775
+ const paths = [];
2776
+ for (const pathStr2 of value) {
2777
+ const d = extractPathData(pathStr2);
2778
+ if (d) {
2779
+ paths.push(...parseSvgPathToBezier(d));
2780
+ }
2781
+ }
2782
+ return { paths };
2783
+ }
2784
+ return { paths: value };
2785
+ }
2786
+ if (isPathString(value)) {
2787
+ const d = extractPathData(value);
2788
+ return { paths: parseSvgPathToBezier(d) };
2789
+ }
2790
+ return value;
2791
+ }
2792
+ function resolveEasing(easing, defs) {
2793
+ var _a;
2794
+ if (!easing) return void 0;
2795
+ if (Array.isArray(easing)) {
2796
+ return easing;
2797
+ }
2798
+ if ((_a = defs == null ? void 0 : defs.easings) == null ? void 0 : _a[easing]) {
2799
+ return defs.easings[easing];
2800
+ }
2801
+ console.warn("Unknown easing name: " + easing);
2802
+ return void 0;
2803
+ }
2804
+ function resolveAnimation(animRef, defs) {
2805
+ var _a;
2806
+ if (typeof animRef === "string") {
2807
+ const resolved = (_a = defs == null ? void 0 : defs.animations) == null ? void 0 : _a[animRef];
2808
+ if (!resolved) {
2809
+ console.warn("Unknown animation name: " + animRef);
2810
+ }
2811
+ return resolved;
2812
+ }
2813
+ return animRef;
2814
+ }
2815
+ function resolveElementAnimation(animate, defs) {
2816
+ if (!animate) return [];
2817
+ const results = [];
2818
+ if (typeof animate === "string") {
2819
+ const resolved = resolveAnimation(animate, defs);
2820
+ if (resolved) results.push(resolved);
2821
+ } else if (Array.isArray(animate)) {
2822
+ for (const item of animate) {
2823
+ const resolved = resolveAnimation(item, defs);
2824
+ if (resolved) results.push(resolved);
2126
2825
  }
2127
- if (type === "L") {
2128
- const x = values[0] || 0;
2129
- const y = values[1] || 0;
2130
- currentPath.v.push([x, y]);
2131
- currentPath.i.push([x, y]);
2132
- currentPath.o.push([x, y]);
2133
- } else if (type === "C") {
2134
- const outX = values[0] || 0;
2135
- const outY = values[1] || 0;
2136
- const inX2 = values[2] || 0;
2137
- const inY2 = values[3] || 0;
2138
- const x2 = values[4] || 0;
2139
- const y2 = values[5] || 0;
2140
- currentPath.o[currentPath.o.length - 1] = [outX, outY];
2141
- currentPath.v.push([x2, y2]);
2142
- currentPath.i.push([inX2, inY2]);
2143
- currentPath.o.push([x2, y2]);
2144
- } else if (type === "Z" || type === "z") {
2145
- currentPath.c = true;
2826
+ } else {
2827
+ results.push(animate);
2828
+ }
2829
+ return results;
2830
+ }
2831
+ function interpolateValue(propName, a, b, t) {
2832
+ var _a, _b;
2833
+ if (propName === "d") {
2834
+ const aPaths = (_a = a == null ? void 0 : a.paths) != null ? _a : Array.isArray(a) ? a : [];
2835
+ const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
2836
+ return { paths: interpolateBeziers(aPaths, bPaths, t) };
2837
+ }
2838
+ if (COLOUR_ATTR_NAMES.has(propName)) {
2839
+ return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
2840
+ }
2841
+ if (propName === "transform" && typeof a === "object" && a !== null && !Array.isArray(a) && typeof b === "object" && b !== null && !Array.isArray(b)) {
2842
+ return interpolateTransformParts(a, b, t);
2843
+ }
2844
+ if (propName === "rotate" && typeof a === "number" && typeof b === "number") {
2845
+ return interpolateNum(a, b, t);
2846
+ }
2847
+ if (TRANSFORM_FN_NAMES.has(propName) || propName === "stroke-dasharray" || propName === "strokeDasharray") {
2848
+ return interpolateVec(a || [], b || [], t);
2849
+ }
2850
+ return interpolateNum(+(a || 0), +(b || 0), t);
2851
+ }
2852
+ function interpolateTransformParts(a, b, t) {
2853
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a != null ? a : {}), ...Object.keys(b != null ? b : {})]);
2854
+ const out = {};
2855
+ for (const k of keys) {
2856
+ const av = a == null ? void 0 : a[k];
2857
+ const bv = b == null ? void 0 : b[k];
2858
+ if (k === "rotate") {
2859
+ out[k] = interpolateNum(+(av != null ? av : 0), +(bv != null ? bv : 0), t);
2860
+ } else if (k === "translate" || k === "scale" || k === "origin") {
2861
+ const fallback = k === "scale" ? [1, 1] : [0, 0];
2862
+ out[k] = interpolateVec(av || fallback, bv || fallback, t);
2146
2863
  } else {
2147
- console.warn('Unsupported path command "' + type + '"');
2864
+ out[k] = bv != null ? bv : av;
2865
+ }
2866
+ }
2867
+ return out;
2868
+ }
2869
+ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2870
+ var _a, _b, _c, _d, _e;
2871
+ const totalIntervals = keyframes.length - 1;
2872
+ const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2873
+ let segKfs;
2874
+ if (loop.before) {
2875
+ segKfs = keyframes.slice(0, segCount + 1);
2876
+ } else {
2877
+ segKfs = keyframes.slice(totalIntervals - segCount);
2878
+ }
2879
+ const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2880
+ const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2881
+ let fillStart, fillEnd;
2882
+ if (loop.before) {
2883
+ fillStart = 0;
2884
+ fillEnd = firstT;
2885
+ } else {
2886
+ fillStart = lastT;
2887
+ fillEnd = duration;
2888
+ }
2889
+ const fillDuration = fillEnd - fillStart;
2890
+ if (fillDuration <= 0) return keyframes;
2891
+ const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
2892
+ const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
2893
+ const segDuration = segEndT - segStartT;
2894
+ if (segDuration <= 0) return keyframes;
2895
+ const template = segKfs.map((kf) => {
2896
+ var _a2, _b2;
2897
+ return {
2898
+ relT: (kf.t - segStartT) / segDuration,
2899
+ v: kf.v,
2900
+ e: kf.e,
2901
+ tangentIn: (_a2 = kf.tangentIn) != null ? _a2 : kf.ti,
2902
+ tangentOut: (_b2 = kf.tangentOut) != null ? _b2 : kf.to
2903
+ };
2904
+ });
2905
+ const fullReps = Math.floor(fillDuration / segDuration);
2906
+ const remainder = fillDuration - fullReps * segDuration;
2907
+ const partialFraction = remainder / segDuration;
2908
+ const looped = [];
2909
+ function appendRep(repStart, isReversed, partial) {
2910
+ let entries;
2911
+ if (isReversed) {
2912
+ entries = [];
2913
+ for (let i = template.length - 1; i >= 0; i--) {
2914
+ entries.push({
2915
+ relT: 1 - template[i].relT,
2916
+ v: template[i].v,
2917
+ // Easing for reversed transition: use reversed easing from the forward "from" keyframe
2918
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
2919
+ // Reversed traversal swaps each vertex's in/out spatial tangents
2920
+ // (geometry is identical, walked backwards), so curvature and
2921
+ // auto-orientation survive the reversed rep.
2922
+ tangentIn: template[i].tangentOut,
2923
+ tangentOut: template[i].tangentIn
2924
+ });
2925
+ }
2926
+ } else {
2927
+ entries = template;
2928
+ }
2929
+ const cutRelT = partial !== void 0 ? partial : 1;
2930
+ for (let i = 0; i < entries.length; i++) {
2931
+ const entry = entries[i];
2932
+ if (entry.relT > cutRelT + 1e-9) {
2933
+ const prev = entries[i - 1];
2934
+ const intervalSpan = entry.relT - prev.relT;
2935
+ const localFrac = (cutRelT - prev.relT) / intervalSpan;
2936
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2937
+ const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
2938
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
2939
+ if (looped.length > 0 && prev.relT <= cutRelT) {
2940
+ looped[looped.length - 1].e = leftEasing;
2941
+ }
2942
+ looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2943
+ return;
2944
+ }
2945
+ const pushed = {
2946
+ t: repStart + entry.relT * segDuration,
2947
+ v: entry.v,
2948
+ e: i < entries.length - 1 ? entry.e : void 0
2949
+ };
2950
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
2951
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
2952
+ looped.push(pushed);
2953
+ }
2954
+ }
2955
+ for (let rep = 0; rep < fullReps; rep++) {
2956
+ const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
2957
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
2958
+ const repStart = fillStart + rep * segDuration;
2959
+ appendRep(repStart, isReversed);
2960
+ }
2961
+ if (partialFraction > 1e-9) {
2962
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
2963
+ const repStart = fillStart + fullReps * segDuration;
2964
+ appendRep(repStart, isReversed, partialFraction);
2965
+ }
2966
+ if (loop.before) {
2967
+ return [...looped, ...keyframes];
2968
+ } else {
2969
+ return [...keyframes, ...looped];
2970
+ }
2971
+ }
2972
+ function normalizeKeyframes(propName, propAnim, duration, defs) {
2973
+ var _a, _b, _c, _d, _e, _f, _g;
2974
+ const keyframes = propAnim.keyframes || propAnim.kfs || [];
2975
+ const normalized = [];
2976
+ for (const kf of keyframes) {
2977
+ const timePct = (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
2978
+ let value = (_c = kf.value) != null ? _c : kf.v;
2979
+ const easing = (_d = kf.easing) != null ? _d : kf.e;
2980
+ if (propName === "d") {
2981
+ value = normalizePathValue(value);
2982
+ }
2983
+ const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2984
+ if (COLOUR_ATTR_NAMES.has(propNameKebab)) {
2985
+ value = (_e = parseColor(value)) != null ? _e : value;
2986
+ }
2987
+ const normKf = {
2988
+ t: timePct,
2989
+ v: value,
2990
+ e: resolveEasing(easing, defs)
2991
+ };
2992
+ const tIn = (_f = kf.tangentIn) != null ? _f : kf.ti;
2993
+ const tOut = (_g = kf.tangentOut) != null ? _g : kf.to;
2994
+ if (tIn) normKf.tangentIn = tIn;
2995
+ if (tOut) normKf.tangentOut = tOut;
2996
+ normalized.push(normKf);
2997
+ }
2998
+ normalized.sort((a, b) => {
2999
+ var _a2, _b2;
3000
+ return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
3001
+ });
3002
+ const loopRaw = propAnim.loop;
3003
+ const loop = loopRaw === true ? {} : loopRaw || void 0;
3004
+ if (loop && normalized.length >= 2) {
3005
+ return expandLoopKeyframes(propName, normalized, loop, duration);
3006
+ }
3007
+ return normalized;
3008
+ }
3009
+ function mergeAnimationDefinitions(animations) {
3010
+ const merged = {};
3011
+ for (const anim of animations) {
3012
+ for (const [prop, propAnim] of Object.entries(anim)) {
3013
+ merged[prop] = propAnim;
3014
+ }
3015
+ }
3016
+ return merged;
3017
+ }
3018
+ function materialiseInternalLoopsInPropAnim(propName, propAnim, duration) {
3019
+ var _a;
3020
+ const loopRaw = propAnim.loop;
3021
+ if (loopRaw === void 0 || loopRaw === null || loopRaw === false) return propAnim;
3022
+ const loop = loopRaw === true ? {} : loopRaw;
3023
+ const rawKfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
3024
+ if (!Array.isArray(rawKfs) || rawKfs.length < 2) return propAnim;
3025
+ const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
3026
+ const isColour = COLOUR_ATTR_NAMES.has(propNameKebab);
3027
+ const kfs = rawKfs.map((kf) => {
3028
+ var _a2, _b, _c, _d, _e, _f, _g, _h;
3029
+ const t = (_a2 = kf.t) != null ? _a2 : kf.time;
3030
+ let v = (_b = kf.v) != null ? _b : kf.value;
3031
+ if (propName === "d") v = normalizePathValue(v);
3032
+ if (isColour) v = (_c = parseColor(v)) != null ? _c : v;
3033
+ const e = (_d = kf.e) != null ? _d : kf.easing;
3034
+ const out2 = { t, v, e };
3035
+ if ((_e = kf.tangentIn) != null ? _e : kf.ti) out2.tangentIn = (_f = kf.tangentIn) != null ? _f : kf.ti;
3036
+ if ((_g = kf.tangentOut) != null ? _g : kf.to) out2.tangentOut = (_h = kf.tangentOut) != null ? _h : kf.to;
3037
+ return out2;
3038
+ });
3039
+ const expanded = expandLoopKeyframes(propName, kfs, loop, duration);
3040
+ const out = { kfs: expanded };
3041
+ if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
3042
+ return out;
3043
+ }
3044
+ function materialiseInternalLoopsInTree(root, duration) {
3045
+ const ret = walkAndMaterialiseLoops(root, duration);
3046
+ return ret != null ? ret : root;
3047
+ }
3048
+ function walkAndMaterialiseLoops(node, duration) {
3049
+ let newChildren;
3050
+ if (node.children) {
3051
+ for (let i = 0; i < node.children.length; i++) {
3052
+ const ret = walkAndMaterialiseLoops(node.children[i], duration);
3053
+ if (ret !== null) {
3054
+ if (!newChildren) newChildren = node.children.slice();
3055
+ newChildren[i] = ret;
3056
+ }
3057
+ }
3058
+ }
3059
+ let newAnimate;
3060
+ const animBucket = node.animate;
3061
+ if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
3062
+ const animDef = animBucket;
3063
+ for (const propName of Object.keys(animDef)) {
3064
+ const propAnim = animDef[propName];
3065
+ const materialised = materialiseInternalLoopsInPropAnim(propName, propAnim, duration);
3066
+ if (materialised !== propAnim) {
3067
+ if (!newAnimate) newAnimate = __spreadValues({}, animDef);
3068
+ newAnimate[propName] = materialised;
3069
+ }
3070
+ }
3071
+ }
3072
+ if (!newChildren && !newAnimate) return null;
3073
+ const cloned = __spreadValues({}, node);
3074
+ if (newChildren) cloned.children = newChildren;
3075
+ if (newAnimate) cloned.animate = newAnimate;
3076
+ return cloned;
3077
+ }
3078
+ var _elementIdCounter = 0;
3079
+ function generateElementId() {
3080
+ return "_px_el_" + ++_elementIdCounter;
3081
+ }
3082
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.webapi) {
3083
+ const normalized = {};
3084
+ for (const [propName, propAnim] of Object.entries(animDef)) {
3085
+ const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
3086
+ if (normalizedKfs.length > 0) {
3087
+ const out = { kfs: normalizedKfs };
3088
+ if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
3089
+ if (propAnim.loop !== void 0) out.loop = propAnim.loop;
3090
+ normalized[propName] = engine === PxAnimatorEngine.webapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
3091
+ }
3092
+ }
3093
+ return normalized;
3094
+ }
3095
+ function getNormalisedBindings(doc, engine = PxAnimatorEngine.webapi) {
3096
+ const animatorConfig = getAnimatorConfig(doc) || {};
3097
+ const defs = getDefs(doc);
3098
+ const duration = animatorConfig.duration || 1e3;
3099
+ const bindings = [];
3100
+ const processAnimation = (id, animate) => {
3101
+ if (!animate) return null;
3102
+ const animDefs = resolveElementAnimation(animate, defs);
3103
+ if (animDefs.length === 0) return null;
3104
+ const merged = mergeAnimationDefinitions(animDefs);
3105
+ const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);
3106
+ if (Object.keys(normalizedAnim).length === 0) return null;
3107
+ return {
3108
+ id,
3109
+ animate: normalizedAnim
3110
+ };
3111
+ };
3112
+ const docBindings = getBindings(doc);
3113
+ if (docBindings) {
3114
+ for (const binding of docBindings) {
3115
+ const normalized = processAnimation(binding.id, binding.animate);
3116
+ if (normalized) bindings.push(normalized);
3117
+ }
3118
+ }
3119
+ const processNode = (node) => {
3120
+ const inlineAnim = node.animate;
3121
+ if (inlineAnim && Object.keys(inlineAnim).length > 0) {
3122
+ const nodeId = node.id || generateElementId();
3123
+ node.id = nodeId;
3124
+ const normalized = processAnimation(nodeId, inlineAnim);
3125
+ if (normalized) bindings.push(normalized);
3126
+ }
3127
+ if (node.children) {
3128
+ for (let i = 0; i < node.children.length; i++) {
3129
+ processNode(node.children[i]);
3130
+ }
3131
+ }
3132
+ };
3133
+ if (doc.children) {
3134
+ for (let i = 0; i < doc.children.length; i++) {
3135
+ processNode(doc.children[i]);
2148
3136
  }
2149
3137
  }
2150
- return res;
3138
+ return bindings;
2151
3139
  }
2152
- function extractPathData(str) {
2153
- if (str.startsWith("path(") && str.endsWith(")")) {
2154
- return str.slice(5, -1);
2155
- }
2156
- if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str)) {
2157
- return str;
3140
+ function getKeyframesPair(keyframes, progress) {
3141
+ var _a, _b;
3142
+ let prevKf = keyframes[0];
3143
+ let nextKf = keyframes[keyframes.length - 1];
3144
+ for (let j = 0; j < keyframes.length - 1; j++) {
3145
+ const aOff = (_a = keyframes[j].t) != null ? _a : 0;
3146
+ const bOff = (_b = keyframes[j + 1].t) != null ? _b : 0;
3147
+ if (aOff <= progress && progress <= bOff) {
3148
+ prevKf = keyframes[j];
3149
+ nextKf = keyframes[j + 1];
3150
+ break;
3151
+ }
2158
3152
  }
2159
- return void 0;
2160
- }
2161
- function isPathString(value) {
2162
- return typeof value === "string" && extractPathData(value) !== void 0;
3153
+ return { prevKf, nextKf };
2163
3154
  }
2164
- function normalizePathValue(value) {
2165
- if (value && typeof value === "object" && typeof value.path === "string") {
2166
- const d = extractPathData(value.path);
2167
- return d ? { paths: parseSvgPathToBezier(d) } : value;
3155
+ function calcPropertyValue(propName, propAnim, progress) {
3156
+ var _a, _b, _c, _d, _e, _f, _g;
3157
+ const keyframes = propAnim.kfs || propAnim.keyframes || [];
3158
+ if (keyframes.length === 0) return null;
3159
+ const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
3160
+ let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a = prevKf.t) != null ? _a : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
3161
+ localProgress = clamp(localProgress, 0, 1);
3162
+ const easing = (_c = prevKf.e) != null ? _c : prevKf.easing;
3163
+ if (easing && Array.isArray(easing)) {
3164
+ try {
3165
+ localProgress = cubicBezier(easing)(localProgress);
3166
+ } catch (e) {
3167
+ }
2168
3168
  }
2169
- if (value && typeof value === "object" && "paths" in value) {
2170
- const pathsArray = value.paths;
2171
- if (Array.isArray(pathsArray) && pathsArray.length > 0) {
2172
- if (isPathString(pathsArray[0])) {
2173
- const paths = [];
2174
- for (const pathStr2 of pathsArray) {
2175
- const d = extractPathData(pathStr2);
2176
- if (d) {
2177
- paths.push(...parseSvgPathToBezier(d));
2178
- }
2179
- }
2180
- return { paths };
3169
+ let cssAttrName = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
3170
+ let cssValue = null;
3171
+ const prevV = (_d = prevKf == null ? void 0 : prevKf.v) != null ? _d : prevKf == null ? void 0 : prevKf.value;
3172
+ const nextV = (_e = nextKf == null ? void 0 : nextKf.v) != null ? _e : nextKf == null ? void 0 : nextKf.value;
3173
+ if (cssAttrName === "d") {
3174
+ const prevPaths = (_f = prevV == null ? void 0 : prevV.paths) != null ? _f : Array.isArray(prevV) ? prevV : [];
3175
+ const nextPaths = (_g = nextV == null ? void 0 : nextV.paths) != null ? _g : Array.isArray(nextV) ? nextV : [];
3176
+ cssValue = interpolateBeziers(
3177
+ prevPaths,
3178
+ nextPaths,
3179
+ localProgress
3180
+ ).map((bz) => bezierToSvgPath(bz)).join("");
3181
+ } else if (COLOUR_ATTR_NAMES.has(cssAttrName)) {
3182
+ cssValue = toRGBA(interpolateColor(
3183
+ prevV || [0, 0, 0, 1],
3184
+ nextV || [0, 0, 0, 1],
3185
+ localProgress
3186
+ ));
3187
+ cssAttrName = propName;
3188
+ } else if (cssAttrName === "stroke-dasharray") {
3189
+ cssValue = interpolateVec(
3190
+ prevV || [],
3191
+ nextV || [],
3192
+ localProgress
3193
+ ).join(" ");
3194
+ cssAttrName = propName;
3195
+ } else if (cssAttrName === "transform" && prevV !== null && typeof prevV === "object" && !Array.isArray(prevV)) {
3196
+ const partKeys = /* @__PURE__ */ new Set([
3197
+ ...prevV ? Object.keys(prevV) : [],
3198
+ ...nextV ? Object.keys(nextV) : []
3199
+ ]);
3200
+ const partsResult = {};
3201
+ for (const partKey of partKeys) {
3202
+ const prevPart = prevV == null ? void 0 : prevV[partKey];
3203
+ const nextPart = nextV == null ? void 0 : nextV[partKey];
3204
+ if (partKey === "rotate") {
3205
+ partsResult.rotate = interpolateNum(+(prevPart != null ? prevPart : 0), +(nextPart != null ? nextPart : 0), localProgress);
3206
+ } else if (partKey === "translate" || partKey === "scale" || partKey === "origin") {
3207
+ const fallback = partKey === "scale" ? [1, 1] : [0, 0];
3208
+ const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);
3209
+ partsResult[partKey] = interp;
2181
3210
  }
2182
3211
  }
2183
- return value;
2184
- }
2185
- if (Array.isArray(value)) {
2186
- if (value.length > 0 && isPathString(value[0])) {
2187
- const paths = [];
2188
- for (const pathStr2 of value) {
2189
- const d = extractPathData(pathStr2);
2190
- if (d) {
2191
- paths.push(...parseSvgPathToBezier(d));
2192
- }
3212
+ if (propAnimIsMotionPath(propAnim)) {
3213
+ const prevTr = prevV.translate;
3214
+ const nextTr = nextV.translate;
3215
+ if (Array.isArray(prevTr) && Array.isArray(nextTr)) {
3216
+ const sample = evaluateMotionPathSegment(
3217
+ prevKf,
3218
+ nextKf,
3219
+ [+prevTr[0], +prevTr[1]],
3220
+ [+nextTr[0], +nextTr[1]],
3221
+ localProgress,
3222
+ !!propAnim.autoOrient
3223
+ );
3224
+ partsResult.translate = [sample.translate[0], sample.translate[1]];
3225
+ if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg;
2193
3226
  }
2194
- return { paths };
2195
3227
  }
2196
- return { paths: value };
3228
+ cssValue = composeTransformParts(partsResult, { withUnits: false });
3229
+ cssAttrName = "transform";
3230
+ } else if (cssAttrName === "translate") {
3231
+ const v = interpolateVec(
3232
+ prevV || [0, 0],
3233
+ nextV || [0, 0],
3234
+ localProgress
3235
+ );
3236
+ cssValue = "translate(" + v.join(",") + ")";
3237
+ cssAttrName = "transform";
3238
+ } else if (cssAttrName === "rotate") {
3239
+ const v = interpolateNum(
3240
+ +(prevV || 0),
3241
+ +(nextV || 0),
3242
+ localProgress
3243
+ );
3244
+ cssValue = "rotate(" + v + ")";
3245
+ cssAttrName = "transform";
3246
+ } else if (cssAttrName === "scale") {
3247
+ const v = interpolateVec(
3248
+ prevV || [1, 1],
3249
+ nextV || [1, 1],
3250
+ localProgress
3251
+ );
3252
+ cssValue = "scale(" + v.join(",") + ")";
3253
+ cssAttrName = "transform";
3254
+ } else {
3255
+ const num2 = interpolateNum(
3256
+ +(prevV || 0),
3257
+ +(nextV || 0),
3258
+ localProgress
3259
+ );
3260
+ cssValue = num2;
2197
3261
  }
2198
- if (isPathString(value)) {
2199
- const d = extractPathData(value);
2200
- return { paths: parseSvgPathToBezier(d) };
3262
+ if (PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
3263
+ cssValue = cssValue * 100 + "%";
2201
3264
  }
2202
- return value;
3265
+ return { k: cssAttrName, v: cssValue === null ? "" : "" + cssValue };
2203
3266
  }
2204
- function resolveEasing(easing, defs) {
2205
- var _a;
2206
- if (!easing) return void 0;
2207
- if (Array.isArray(easing)) {
2208
- return easing;
2209
- }
2210
- if ((_a = defs == null ? void 0 : defs.easings) == null ? void 0 : _a[easing]) {
2211
- return defs.easings[easing];
3267
+ function calcAnimationValues(animDef, progress) {
3268
+ const result = {};
3269
+ for (const [propName, propAnim] of Object.entries(animDef)) {
3270
+ const computed = calcPropertyValue(propName, propAnim, progress);
3271
+ if (computed) {
3272
+ result[computed.k] = computed.v;
3273
+ }
2212
3274
  }
2213
- console.warn("Unknown easing name: " + easing);
2214
- return void 0;
3275
+ return result;
2215
3276
  }
2216
- function resolveAnimation(animRef, defs) {
2217
- var _a;
2218
- if (typeof animRef === "string") {
2219
- const resolved = (_a = defs == null ? void 0 : defs.animations) == null ? void 0 : _a[animRef];
2220
- if (!resolved) {
2221
- console.warn("Unknown animation name: " + animRef);
3277
+
3278
+ // src/effects/trimPathEffect.ts
3279
+ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
3280
+ if (!trimPath) return node;
3281
+ const trimAllAsOne = !!trimPath.trimAllAsOne;
3282
+ const leafEntries = [];
3283
+ let acc = 0;
3284
+ const measure = (n) => {
3285
+ if (Array.isArray(n.children) && n.children.length > 0) {
3286
+ for (const ch of n.children) measure(ch);
3287
+ return;
2222
3288
  }
2223
- return resolved;
3289
+ const d = typeof n.d === "string" ? n.d : rectToPathD(n);
3290
+ if (d === void 0) return;
3291
+ const subpaths = parseSvgPathToBezier(d);
3292
+ if (!subpaths.length) return;
3293
+ const entry = { leaf: n, subpaths: [] };
3294
+ for (const sp of subpaths) {
3295
+ if (!trimAllAsOne) acc = 0;
3296
+ const lengthPx = pxBezierPathLength(sp);
3297
+ entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: acc });
3298
+ acc += lengthPx;
3299
+ }
3300
+ leafEntries.push(entry);
3301
+ };
3302
+ measure(node);
3303
+ if (!leafEntries.length) return node;
3304
+ const chainLengthPx = acc;
3305
+ if (trimAllAsOne && chainLengthPx < 1e-3) return node;
3306
+ const offsetReadRaw = readAnimatable(trimPath.offset);
3307
+ const offsetRead = offsetReadRaw.kind === "absent" /* Absent */ ? { kind: "static" /* Static */, value: 0 } : offsetReadRaw;
3308
+ const rangeReadRaw = readRangeWithCrossings(trimPath.range);
3309
+ const rangeRead = rangeReadRaw.kind === "absent" /* Absent */ ? { kind: "static" /* Static */, value: [0, 1] } : rangeReadRaw;
3310
+ const offsetValues = readScalarValues(offsetRead);
3311
+ const minOffset = offsetValues.length ? Math.min(...offsetValues) : 0;
3312
+ const maxOffset = offsetValues.length ? Math.max(...offsetValues) : 0;
3313
+ const minMaxOffset = [minOffset, maxOffset];
3314
+ if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {
3315
+ const entry = leafEntries[0];
3316
+ const sp = entry.subpaths[0];
3317
+ const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
3318
+ if (pathLengthPx < 1e-3) return node;
3319
+ const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
3320
+ return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);
3321
+ }
3322
+ const replacements = /* @__PURE__ */ new Map();
3323
+ for (const entry of leafEntries) {
3324
+ const newChildren = [];
3325
+ for (const sp of entry.subpaths) {
3326
+ const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
3327
+ if (pathLengthPx < 1e-3) continue;
3328
+ const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
3329
+ newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));
3330
+ }
3331
+ replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));
2224
3332
  }
2225
- return animRef;
3333
+ const swap = (n) => {
3334
+ const r = replacements.get(n);
3335
+ if (r) return r;
3336
+ if (Array.isArray(n.children) && n.children.length > 0) {
3337
+ return __spreadProps(__spreadValues({}, n), { children: n.children.map(swap) });
3338
+ }
3339
+ return n;
3340
+ };
3341
+ return swap(node);
3342
+ }
3343
+ function wrapLeafAsGroup(leaf, children) {
3344
+ const wrapper = __spreadProps(__spreadValues({}, leaf), { type: "g", children });
3345
+ delete wrapper.d;
3346
+ delete wrapper.strokeDasharray;
3347
+ delete wrapper.strokeDashoffset;
3348
+ delete wrapper.effects;
3349
+ return wrapper;
2226
3350
  }
2227
- function resolveElementAnimation(animate, defs) {
2228
- if (!animate) return [];
2229
- const results = [];
2230
- if (typeof animate === "string") {
2231
- const resolved = resolveAnimation(animate, defs);
2232
- if (resolved) results.push(resolved);
2233
- } else if (Array.isArray(animate)) {
2234
- for (const item of animate) {
2235
- const resolved = resolveAnimation(item, defs);
2236
- if (resolved) results.push(resolved);
3351
+ function buildSubpathNodes(leaf, subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx) {
3352
+ const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);
3353
+ const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);
3354
+ const dashOffsetAttr = computeAnimAttr(offsetRead, offsetToDashOffset);
3355
+ const dashArrayAttr = computeAnimAttr(rangeRead, rangeToDasharray);
3356
+ const strokeOpacityAttr = computeOpacityFromRange(rangeRead);
3357
+ const dStr = bezierToSvgPath(subpath);
3358
+ const base = makeBareSubpath(dStr);
3359
+ applyAttr(base, "strokeDasharray", dashArrayAttr);
3360
+ applyAttr(base, "strokeDashoffset", dashOffsetAttr);
3361
+ applyAttr(base, "strokeOpacity", strokeOpacityAttr);
3362
+ return [base];
3363
+ }
3364
+ function collapseLeafWithTrim(leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead) {
3365
+ const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);
3366
+ const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);
3367
+ const node = __spreadValues({}, leaf);
3368
+ delete node.effects;
3369
+ applyAttr(node, "strokeDasharray", computeAnimAttr(rangeRead, rangeToDasharray));
3370
+ applyAttr(node, "strokeDashoffset", computeAnimAttr(offsetRead, offsetToDashOffset));
3371
+ applyAttr(node, "strokeOpacity", computeOpacityFromRange(rangeRead));
3372
+ return node;
3373
+ }
3374
+ function makeBareSubpath(dStr) {
3375
+ return { type: "path", d: dStr };
3376
+ }
3377
+ var SMALL_PADDING_PX = 1;
3378
+ function makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset) {
3379
+ const [minIdx] = getOffsetIndexRange(minMaxOffset);
3380
+ return (offsetVal) => pathLengthPx * (-offsetVal - minIdx + startOffsetPct) + SMALL_PADDING_PX;
3381
+ }
3382
+ function makeRangeToDasharray(pathLengthPx, minMaxOffset) {
3383
+ const [minIdx, maxIdx] = getOffsetIndexRange(minMaxOffset);
3384
+ const repeats = maxIdx - minIdx + 1;
3385
+ return (rangeVal) => {
3386
+ const a = clamp(rangeVal[0], 0, 1);
3387
+ const b = clamp(rangeVal[1], 0, 1);
3388
+ const minR = Math.min(a, b);
3389
+ const maxR = Math.max(a, b);
3390
+ const out = [0];
3391
+ let gap = SMALL_PADDING_PX;
3392
+ for (let i = 0; i < repeats; i++) {
3393
+ out.push(gap + minR * pathLengthPx);
3394
+ out.push((maxR - minR) * pathLengthPx);
3395
+ gap = (1 - maxR) * pathLengthPx;
2237
3396
  }
2238
- } else {
2239
- results.push(animate);
2240
- }
2241
- return results;
3397
+ out.push(gap + SMALL_PADDING_PX);
3398
+ return out;
3399
+ };
2242
3400
  }
2243
- function interpolateValue(propName, a, b, t) {
2244
- var _a, _b;
2245
- if (propName === "d") {
2246
- const aPaths = (_a = a == null ? void 0 : a.paths) != null ? _a : Array.isArray(a) ? a : [];
2247
- const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
2248
- return { paths: interpolateBeziers(aPaths, bPaths, t) };
2249
- }
2250
- if (COLOUR_ATTR_NAMES.has(propName)) {
2251
- return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
2252
- }
2253
- if (TRANSFORM_FN_NAMES.has(propName) || propName === "stroke-dasharray" || propName === "strokeDasharray") {
2254
- return interpolateVec(a || [], b || [], t);
3401
+ function getOffsetIndexRange(minMaxOffset) {
3402
+ return [
3403
+ Math.floor(Math.min(-minMaxOffset[0], -minMaxOffset[1])),
3404
+ Math.ceil(Math.max(-minMaxOffset[0], -minMaxOffset[1]))
3405
+ ];
3406
+ }
3407
+ function readScalarValues(r) {
3408
+ var _a;
3409
+ if (r.kind === "absent" /* Absent */) return [];
3410
+ if (r.kind === "static" /* Static */) return [r.value];
3411
+ const out = [];
3412
+ for (const kf of r.keyframes) {
3413
+ const v = (_a = kf.value) != null ? _a : kf.v;
3414
+ if (typeof v === "number") out.push(v);
2255
3415
  }
2256
- return interpolateNum(+(a || 0), +(b || 0), t);
3416
+ return out;
2257
3417
  }
2258
- function expandLoopKeyframes(propName, keyframes, loop, duration) {
2259
- var _a, _b, _c, _d, _e;
2260
- const totalIntervals = keyframes.length - 1;
2261
- const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2262
- let segKfs;
2263
- if (loop.before) {
2264
- segKfs = keyframes.slice(0, segCount + 1);
2265
- } else {
2266
- segKfs = keyframes.slice(totalIntervals - segCount);
3418
+ function computeAnimAttr(read, map) {
3419
+ if (read.kind === "absent" /* Absent */) return void 0;
3420
+ if (read.kind === "static" /* Static */) return { kind: "static" /* Static */, value: map(read.value) };
3421
+ return {
3422
+ kind: "animated" /* Animated */,
3423
+ keyframes: read.keyframes.map((kf) => {
3424
+ var _a, _b, _c, _d;
3425
+ return {
3426
+ time: (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0,
3427
+ value: map((_c = kf.value) != null ? _c : kf.v),
3428
+ easing: (_d = kf.easing) != null ? _d : kf.e
3429
+ };
3430
+ })
3431
+ };
3432
+ }
3433
+ function applyAttr(node, attrName, attr) {
3434
+ if (!attr) return;
3435
+ if (attr.kind === "static" /* Static */) {
3436
+ node[attrName] = attr.value;
3437
+ return;
2267
3438
  }
2268
- const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2269
- const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2270
- let fillStart, fillEnd;
2271
- if (loop.before) {
2272
- fillStart = 0;
2273
- fillEnd = firstT;
2274
- } else {
2275
- fillStart = lastT;
2276
- fillEnd = duration;
3439
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3440
+ const animate = __spreadValues({}, prevAnimate || {});
3441
+ animate[attrName] = { keyframes: attr.keyframes };
3442
+ node.animate = animate;
3443
+ }
3444
+ function computeOpacityFromRange(rangeRead) {
3445
+ var _a, _b, _c, _d, _e, _f;
3446
+ const hide = (v) => v[0] === v[1];
3447
+ if (rangeRead.kind === "absent" /* Absent */) return void 0;
3448
+ if (rangeRead.kind === "static" /* Static */) return hide(rangeRead.value) ? { kind: "static" /* Static */, value: 0 } : void 0;
3449
+ const kfs = rangeRead.keyframes;
3450
+ let anyHide = false;
3451
+ let allHide = true;
3452
+ for (const kf of kfs) {
3453
+ if (hide((_a = kf.value) != null ? _a : kf.v)) anyHide = true;
3454
+ else allHide = false;
2277
3455
  }
2278
- const fillDuration = fillEnd - fillStart;
2279
- if (fillDuration <= 0) return keyframes;
2280
- const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
2281
- const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
2282
- const segDuration = segEndT - segStartT;
2283
- if (segDuration <= 0) return keyframes;
2284
- const template = segKfs.map((kf) => ({
2285
- relT: (kf.t - segStartT) / segDuration,
2286
- v: kf.v,
2287
- e: kf.e
2288
- }));
2289
- const fullReps = Math.floor(fillDuration / segDuration);
2290
- const remainder = fillDuration - fullReps * segDuration;
2291
- const partialFraction = remainder / segDuration;
2292
- const looped = [];
2293
- function appendRep(repStart, isReversed, partial) {
2294
- let entries;
2295
- if (isReversed) {
2296
- entries = [];
2297
- for (let i = template.length - 1; i >= 0; i--) {
2298
- entries.push({
2299
- relT: 1 - template[i].relT,
2300
- v: template[i].v,
2301
- // Easing for reversed transition: use reversed easing from the forward "from" keyframe
2302
- e: i > 0 ? reverseEasing(template[i - 1].e) : void 0
2303
- });
3456
+ if (!anyHide) return void 0;
3457
+ if (allHide) return { kind: "static" /* Static */, value: 0 };
3458
+ const out = [];
3459
+ for (let i = 0; i < kfs.length; i++) {
3460
+ const kf = kfs[i];
3461
+ const prevKf = i > 0 ? kfs[i - 1] : void 0;
3462
+ const nextKf = i < kfs.length - 1 ? kfs[i + 1] : void 0;
3463
+ const t = (_c = (_b = kf.time) != null ? _b : kf.t) != null ? _c : 0;
3464
+ const thisHide = hide((_d = kf.value) != null ? _d : kf.v);
3465
+ const prevHide = prevKf ? thisHide && hide((_e = prevKf.value) != null ? _e : prevKf.v) : thisHide;
3466
+ const nextHide = nextKf ? thisHide && hide((_f = nextKf.value) != null ? _f : nextKf.v) : thisHide;
3467
+ if (prevHide && !nextHide) {
3468
+ out.push({ time: t, value: 0 });
3469
+ out.push({ time: t + 1, value: 1 });
3470
+ } else if (!prevHide && nextHide) {
3471
+ out.push({ time: t - 1, value: 1 });
3472
+ out.push({ time: t, value: 0 });
3473
+ }
3474
+ }
3475
+ if (out.length <= 1) return void 0;
3476
+ return { kind: "animated" /* Animated */, keyframes: out };
3477
+ }
3478
+ function readRangeWithCrossings(raw) {
3479
+ const r = readAnimatable(raw);
3480
+ if (r.kind !== "animated" /* Animated */) return r;
3481
+ const kfs = r.keyframes.map((kf) => {
3482
+ var _a, _b, _c, _d;
3483
+ return {
3484
+ time: (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0,
3485
+ value: (_c = kf.value) != null ? _c : kf.v,
3486
+ easing: (_d = kf.easing) != null ? _d : kf.e
3487
+ };
3488
+ });
3489
+ const hasReverse = kfs.some((kf) => kf.value[0] > kf.value[1]);
3490
+ if (!hasReverse) {
3491
+ return {
3492
+ kind: "animated" /* Animated */,
3493
+ keyframes: kfs.map((kf) => ({ time: kf.time, value: kf.value, easing: kf.easing }))
3494
+ };
3495
+ }
3496
+ const crossingTimes = [];
3497
+ for (let i = 1; i < kfs.length; i++) {
3498
+ const prev = kfs[i - 1];
3499
+ const cur = kfs[i];
3500
+ const dPrev = prev.value[1] - prev.value[0];
3501
+ const dCur = cur.value[1] - cur.value[0];
3502
+ if (dPrev * dCur < 0) {
3503
+ const t = bisectionForRangeCrossing(prev, cur);
3504
+ if (t !== null && t > prev.time && t < cur.time) {
3505
+ crossingTimes.push(Math.round(t));
2304
3506
  }
3507
+ }
3508
+ }
3509
+ const uniqueTs = Array.from(new Set(crossingTimes)).sort((a, b) => a - b);
3510
+ const out = [];
3511
+ let j = 0;
3512
+ for (const kf of kfs) {
3513
+ while (j < uniqueTs.length && uniqueTs[j] < kf.time) {
3514
+ const t = uniqueTs[j++];
3515
+ const v2 = interpolateRangeAt(kfs, t);
3516
+ const m = (v2[0] + v2[1]) / 2;
3517
+ out.push({ time: t, value: [m, m] });
3518
+ }
3519
+ const v = kf.value[0] > kf.value[1] ? [kf.value[1], kf.value[0]] : kf.value;
3520
+ out.push({ time: kf.time, value: v, easing: kf.easing });
3521
+ }
3522
+ while (j < uniqueTs.length) {
3523
+ const t = uniqueTs[j++];
3524
+ const v = interpolateRangeAt(kfs, t);
3525
+ const m = (v[0] + v[1]) / 2;
3526
+ out.push({ time: t, value: [m, m] });
3527
+ }
3528
+ return { kind: "animated" /* Animated */, keyframes: out };
3529
+ }
3530
+ function bisectionForRangeCrossing(prev, cur) {
3531
+ const f = (t) => {
3532
+ const a = (t - prev.time) / (cur.time - prev.time);
3533
+ const v0 = prev.value[0] + (cur.value[0] - prev.value[0]) * a;
3534
+ const v1 = prev.value[1] + (cur.value[1] - prev.value[1]) * a;
3535
+ return v1 - v0;
3536
+ };
3537
+ let lo = prev.time, hi = cur.time;
3538
+ let fLo = f(lo);
3539
+ if (fLo === 0) return lo;
3540
+ const fHi = f(hi);
3541
+ if (fHi === 0) return hi;
3542
+ if (fLo * fHi > 0) return null;
3543
+ for (let i = 0; i < 100; i++) {
3544
+ const mid = (lo + hi) / 2;
3545
+ const fMid = f(mid);
3546
+ if (fMid === 0 || Math.abs(hi - lo) < 1e-4) return mid;
3547
+ if (fLo * fMid < 0) {
3548
+ hi = mid;
2305
3549
  } else {
2306
- entries = template;
3550
+ lo = mid;
3551
+ fLo = fMid;
2307
3552
  }
2308
- const cutRelT = partial !== void 0 ? partial : 1;
2309
- for (let i = 0; i < entries.length; i++) {
2310
- const entry = entries[i];
2311
- if (entry.relT > cutRelT + 1e-9) {
2312
- const prev = entries[i - 1];
2313
- const intervalSpan = entry.relT - prev.relT;
2314
- const localFrac = (cutRelT - prev.relT) / intervalSpan;
2315
- const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2316
- const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
2317
- const { left: leftEasing } = splitEasing(prev.e, localFrac);
2318
- if (looped.length > 0 && prev.relT <= cutRelT) {
2319
- looped[looped.length - 1].e = leftEasing;
2320
- }
2321
- looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2322
- return;
2323
- }
2324
- looped.push({
2325
- t: repStart + entry.relT * segDuration,
2326
- v: entry.v,
2327
- e: i < entries.length - 1 ? entry.e : void 0
2328
- });
3553
+ }
3554
+ return (lo + hi) / 2;
3555
+ }
3556
+ function interpolateRangeAt(kfs, t) {
3557
+ if (t <= kfs[0].time) return kfs[0].value;
3558
+ if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;
3559
+ for (let i = 1; i < kfs.length; i++) {
3560
+ if (t <= kfs[i].time) {
3561
+ const prev = kfs[i - 1];
3562
+ const cur = kfs[i];
3563
+ const a = (t - prev.time) / (cur.time - prev.time);
3564
+ return [
3565
+ prev.value[0] + (cur.value[0] - prev.value[0]) * a,
3566
+ prev.value[1] + (cur.value[1] - prev.value[1]) * a
3567
+ ];
2329
3568
  }
2330
3569
  }
2331
- for (let rep = 0; rep < fullReps; rep++) {
2332
- const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
2333
- const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
2334
- const repStart = fillStart + rep * segDuration;
2335
- appendRep(repStart, isReversed);
3570
+ return kfs[kfs.length - 1].value;
3571
+ }
3572
+ function pxBezierPathLength(path) {
3573
+ const v = path.v;
3574
+ if (!v || v.length < 2) return 0;
3575
+ let total = 0;
3576
+ for (let i = 0; i < v.length - 1; i++) {
3577
+ total += segmentLength(path, i, i + 1);
2336
3578
  }
2337
- if (partialFraction > 1e-9) {
2338
- const isReversed = !!loop.alternate && fullReps % 2 === 0;
2339
- const repStart = fillStart + fullReps * segDuration;
2340
- appendRep(repStart, isReversed, partialFraction);
3579
+ if (path.c && v.length > 1) {
3580
+ total += segmentLength(path, v.length - 1, 0);
2341
3581
  }
2342
- if (loop.before) {
2343
- return [...looped, ...keyframes];
3582
+ return total;
3583
+ }
3584
+ function segmentLength(path, from, to) {
3585
+ var _a, _b, _c, _d;
3586
+ const v = path.v;
3587
+ const p0 = v[from];
3588
+ const p3 = v[to];
3589
+ const p1 = (_b = (_a = path.o) == null ? void 0 : _a[from]) != null ? _b : p0;
3590
+ const p2 = (_d = (_c = path.i) == null ? void 0 : _c[to]) != null ? _d : p3;
3591
+ const lut = bezier2D_arcLengthLUT(p0, p1, p2, p3);
3592
+ return lut.ds[lut.ds.length - 1];
3593
+ }
3594
+ function rectToPathD(node) {
3595
+ var _a, _b, _c, _d;
3596
+ if (node.type !== "rect") return void 0;
3597
+ const x = Number((_a = node.x) != null ? _a : 0), y = Number((_b = node.y) != null ? _b : 0);
3598
+ const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
3599
+ return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
3600
+ }
3601
+
3602
+ // src/effects/PlayerEffectsUtil.ts
3603
+ function applyPlayerEffects(root) {
3604
+ const ctx = {
3605
+ defs: [],
3606
+ warnings: [],
3607
+ errors: [],
3608
+ idMap: /* @__PURE__ */ new Map(),
3609
+ nextId: 0,
3610
+ contentRefInnerIds: /* @__PURE__ */ new Map(),
3611
+ maskAncestorChains: /* @__PURE__ */ new Map()
3612
+ };
3613
+ const working = clone(root);
3614
+ indexById(working, ctx.idMap);
3615
+ identifyContentRefTargets(working, ctx, () => genId(ctx, "inner"));
3616
+ collectMaskAncestorChains(working, ctx);
3617
+ const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);
3618
+ const out = applyPlayerEffects_retime(afterPass1, ctx);
3619
+ spliceDefs(out, ctx.defs);
3620
+ return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
3621
+ }
3622
+ function applyPlayerEffects_exceptRetime(node, ctx) {
3623
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
3624
+ const fx = node.effects;
3625
+ const originalId = typeof node.id === "string" ? node.id : void 0;
3626
+ const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
3627
+ if (!fx && !innerIdForContentRef) return node;
3628
+ const { transformation, repeater, maskedBy, trimPath, retime, ref, fillGradient, strokeGradient, textAlongPath } = fx != null ? fx : {};
3629
+ const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
3630
+ if (fx) delete node.effects;
3631
+ let n = node;
3632
+ n = applyTextAlongPathEffect(n, textAlongPath, ctx);
3633
+ n = applyFillGradientEffect(n, fillGradient, ctx);
3634
+ n = applyStrokeGradientEffect(n, strokeGradient, ctx);
3635
+ n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
3636
+ n = applyRepeaterEffect(n, repeater, ctx);
3637
+ n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
3638
+ if (innerIdForContentRef) {
3639
+ n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
2344
3640
  } else {
2345
- return [...keyframes, ...looped];
3641
+ n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
2346
3642
  }
3643
+ if (retime) node.effects = { retime };
3644
+ if (originalId) ctx.idMap.set(originalId, n);
3645
+ return n;
2347
3646
  }
2348
- function normalizeKeyframes(propName, propAnim, duration, defs) {
2349
- var _a, _b, _c, _d, _e;
2350
- const keyframes = propAnim.keyframes || propAnim.kfs || [];
2351
- const normalized = [];
2352
- for (const kf of keyframes) {
2353
- const timePct = (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
2354
- let value = (_c = kf.value) != null ? _c : kf.v;
2355
- const easing = (_d = kf.easing) != null ? _d : kf.e;
2356
- if (propName === "d") {
2357
- value = normalizePathValue(value);
3647
+ function applyPlayerEffects_retime(node, ctx) {
3648
+ applyAllRetimeEffects(node, ctx);
3649
+ return node;
3650
+ }
3651
+
3652
+ // src/PxAnimatorUseMaterialiser.ts
3653
+ function materialiseAnimatedUseInstances(root) {
3654
+ var _a;
3655
+ const idMap = buildIdMap(root);
3656
+ const animatedIds = computeAnimatedSubtreeIds(root, idMap);
3657
+ if (animatedIds.size === 0) return root;
3658
+ let idCounter = 0;
3659
+ const genId3 = () => "_lw_use_mat_" + ++idCounter;
3660
+ const defsCollector = [];
3661
+ const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector);
3662
+ if (defsCollector.length === 0) return walked;
3663
+ const defsNode = { type: "defs", children: defsCollector };
3664
+ const newChildren = [...(_a = walked.children) != null ? _a : [], defsNode];
3665
+ return __spreadProps(__spreadValues({}, walked), { children: newChildren });
3666
+ }
3667
+ function buildIdMap(root) {
3668
+ const map = /* @__PURE__ */ new Map();
3669
+ const visit = (n) => {
3670
+ var _a;
3671
+ if (typeof n.id === "string") map.set(n.id, n);
3672
+ (_a = n.children) == null ? void 0 : _a.forEach(visit);
3673
+ };
3674
+ visit(root);
3675
+ return map;
3676
+ }
3677
+ function computeAnimatedSubtreeIds(root, idMap) {
3678
+ const cache = /* @__PURE__ */ new WeakMap();
3679
+ const result = /* @__PURE__ */ new Set();
3680
+ const hasAnim = (n, visiting) => {
3681
+ const cached = cache.get(n);
3682
+ if (cached !== void 0) return cached;
3683
+ if (visiting.has(n)) return false;
3684
+ visiting.add(n);
3685
+ let r = false;
3686
+ if (n.animate && typeof n.animate === "object" && !Array.isArray(n.animate)) {
3687
+ for (const _ in n.animate) {
3688
+ r = true;
3689
+ break;
3690
+ }
2358
3691
  }
2359
- if (COLOUR_ATTR_NAMES.has(propName)) {
2360
- value = (_e = parseColor(value)) != null ? _e : value;
3692
+ if (!r && n.children) {
3693
+ for (const ch of n.children) {
3694
+ if (hasAnim(ch, visiting)) {
3695
+ r = true;
3696
+ break;
3697
+ }
3698
+ }
2361
3699
  }
2362
- normalized.push({
2363
- t: timePct,
2364
- v: value,
2365
- e: resolveEasing(easing, defs)
2366
- });
3700
+ if (!r && n.type === "use" && typeof n.href === "string") {
3701
+ const targetId = stripHash2(n.href);
3702
+ const target = targetId ? idMap.get(targetId) : void 0;
3703
+ if (target) r = hasAnim(target, visiting);
3704
+ }
3705
+ visiting.delete(n);
3706
+ cache.set(n, r);
3707
+ return r;
3708
+ };
3709
+ for (const [id, node] of idMap) {
3710
+ if (hasAnim(node, /* @__PURE__ */ new Set())) result.add(id);
2367
3711
  }
2368
- normalized.sort((a, b) => {
2369
- var _a2, _b2;
2370
- return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
3712
+ return result;
3713
+ }
3714
+ function stripHash2(href) {
3715
+ if (typeof href !== "string") return void 0;
3716
+ return href.startsWith("#") ? href.slice(1) : href;
3717
+ }
3718
+ function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector) {
3719
+ const clone2 = deepClonePxNode(target);
3720
+ regenerateIdsAndRewriteRefs(clone2, genId3);
3721
+ const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector) : clone2;
3722
+ const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector);
3723
+ const newNode = __spreadProps(__spreadValues({}, useNode), { type: "g", children: [materialisedClone] });
3724
+ delete newNode.href;
3725
+ return applyUseOffsetToG(newNode);
3726
+ }
3727
+ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector) {
3728
+ const viewBox = parseViewBox(symbolNode.viewBox);
3729
+ const g = __spreadProps(__spreadValues({}, symbolNode), { type: "g" });
3730
+ delete g.viewBox;
3731
+ delete g.preserveAspectRatio;
3732
+ delete g.width;
3733
+ delete g.height;
3734
+ if (!viewBox) return g;
3735
+ const [vbX, vbY, vbW, vbH] = viewBox;
3736
+ if (vbX !== 0 || vbY !== 0) {
3737
+ g.transform = "translate(" + -vbX + "," + -vbY + ")";
3738
+ }
3739
+ const clipId = genId3();
3740
+ defsCollector.push({
3741
+ type: "clipPath",
3742
+ id: clipId,
3743
+ children: [{ type: "rect", x: vbX, y: vbY, width: vbW, height: vbH }]
2371
3744
  });
2372
- const loopRaw = propAnim.loop;
2373
- const loop = loopRaw === true ? {} : loopRaw || void 0;
2374
- if (loop && normalized.length >= 2) {
2375
- return expandLoopKeyframes(propName, normalized, loop, duration);
2376
- }
2377
- return normalized;
3745
+ g.clipPath = "url(#" + clipId + ")";
3746
+ return g;
2378
3747
  }
2379
- function mergeAnimationDefinitions(animations) {
2380
- const merged = {};
2381
- for (const anim of animations) {
2382
- for (const [prop, propAnim] of Object.entries(anim)) {
2383
- merged[prop] = propAnim;
3748
+ function parseViewBox(v) {
3749
+ if (typeof v !== "string") return void 0;
3750
+ const parts = v.trim().split(/[\s,]+/).map(Number);
3751
+ if (parts.length < 4 || parts.some((n) => !Number.isFinite(n))) return void 0;
3752
+ return [parts[0], parts[1], parts[2], parts[3]];
3753
+ }
3754
+ function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector) {
3755
+ if (node.type === "use" && typeof node.href === "string") {
3756
+ const targetId = stripHash2(node.href);
3757
+ if (targetId && animatedIds.has(targetId)) {
3758
+ const target = idMap.get(targetId);
3759
+ if (target) return materialiseOneUse(node, target, idMap, animatedIds, genId3, defsCollector);
2384
3760
  }
2385
3761
  }
2386
- return merged;
3762
+ if (!node.children) return node;
3763
+ let changed = false;
3764
+ const newChildren = node.children.map((ch) => {
3765
+ const m = walkAndMaterialise2(ch, idMap, animatedIds, genId3, defsCollector);
3766
+ if (m !== ch) changed = true;
3767
+ return m;
3768
+ });
3769
+ return changed ? __spreadProps(__spreadValues({}, node), { children: newChildren }) : node;
2387
3770
  }
2388
- var _elementIdCounter = 0;
2389
- function generateElementId() {
2390
- return "_px_el_" + ++_elementIdCounter;
3771
+
3772
+ // src/PxAnimatorMaterialiseAll.ts
3773
+ function materialiseAllInTree(doc, engine, opts) {
3774
+ var _a, _b;
3775
+ let root = applyPlayerEffects(doc).root;
3776
+ const duration = (_b = (_a = getAnimatorConfig(root)) == null ? void 0 : _a.duration) != null ? _b : DEFAULT_DURATION_MS;
3777
+ root = materialiseInternalLoopsInTree(root, duration);
3778
+ if (engine === PxAnimatorEngine.webapi) {
3779
+ root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
3780
+ root = materialiseAnimatedUseInstances(root);
3781
+ }
3782
+ return root;
2391
3783
  }
2392
- function normalizeAnimationDefinition(animDef, duration, defs) {
2393
- const normalized = {};
2394
- for (const [propName, propAnim] of Object.entries(animDef)) {
2395
- const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2396
- if (normalizedKfs.length > 0) {
2397
- normalized[propName] = { kfs: normalizedKfs };
3784
+
3785
+ // src/PxAnimatorDOM.ts
3786
+ var SVG_NS = "http://www.w3.org/2000/svg";
3787
+ var DISALLOWED_SVG_TAGS_LOWER = /* @__PURE__ */ new Set([
3788
+ "script",
3789
+ "foreignobject"
3790
+ ]);
3791
+ var URL_VALUE_ATTRS_LOWER = /* @__PURE__ */ new Set([
3792
+ "href",
3793
+ // <use>, <image>
3794
+ "xlink:href",
3795
+ // legacy <use>
3796
+ "src",
3797
+ // <image>
3798
+ "filter",
3799
+ // url(#filterId)
3800
+ "clippath",
3801
+ // clip-path="url(#…)"
3802
+ "mask",
3803
+ // url(#maskId)
3804
+ "markerstart",
3805
+ // marker-start="url(#…)"
3806
+ "markermid",
3807
+ // marker-mid="url(#…)"
3808
+ "markerend"
3809
+ // marker-end="url(#…)"
3810
+ ]);
3811
+ var IMAGE_REF_ATTRS_LOWER = /* @__PURE__ */ new Set(["href", "xlink:href", "src"]);
3812
+ var DATA_RASTER_IMAGE_RE = /^data:image\/(?:png|jpe?g|gif|webp|bmp);base64,/i;
3813
+ function isDangerousAttrName(nameLower) {
3814
+ if (nameLower.startsWith("on")) return true;
3815
+ return false;
3816
+ }
3817
+ function sanitiseAttributeValue(name, value) {
3818
+ const nameLower = name.toLowerCase();
3819
+ if (isDangerousAttrName(nameLower)) {
3820
+ console.warn("Attribute blocked (event handler / dangerous): ", nameLower);
3821
+ return void 0;
3822
+ }
3823
+ if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopcolor") {
3824
+ const str = String(value);
3825
+ if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
3826
+ console.warn('Attribute "' + nameLower + '" blocked: url() must be internal url(#id), got:', value);
3827
+ return void 0;
2398
3828
  }
3829
+ return value;
2399
3830
  }
2400
- return normalized;
3831
+ if (URL_VALUE_ATTRS_LOWER.has(nameLower)) {
3832
+ const str = String(value);
3833
+ if (str.startsWith("#")) return value;
3834
+ if (/^url\(#[^)]+\)$/.test(str)) return value;
3835
+ if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && DATA_RASTER_IMAGE_RE.test(str)) return value;
3836
+ console.warn('Attribute "' + nameLower + '" blocked: must be #id, url(#id), or base64 raster data: URI, got:', value);
3837
+ return void 0;
3838
+ }
3839
+ return value;
2401
3840
  }
2402
- function getNormalisedBindings(doc) {
2403
- const animatorConfig = getAnimatorConfig(doc) || {};
2404
- const defs = getDefs(doc);
2405
- const duration = animatorConfig.duration || 1e3;
2406
- const bindings = [];
2407
- const processAnimation = (id, animate) => {
2408
- if (!animate) return null;
2409
- const animDefs = resolveElementAnimation(animate, defs);
2410
- if (animDefs.length === 0) return null;
2411
- const merged = mergeAnimationDefinitions(animDefs);
2412
- const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs);
2413
- if (Object.keys(normalizedAnim).length === 0) return null;
2414
- return {
2415
- id,
2416
- animate: normalizedAnim
2417
- };
2418
- };
2419
- const docBindings = getBindings(doc);
2420
- if (docBindings) {
2421
- for (const binding of docBindings) {
2422
- const normalized = processAnimation(binding.id, binding.animate);
2423
- if (normalized) bindings.push(normalized);
2424
- }
3841
+ function createElement(tagName, normalisedProps, style, children, textContent) {
3842
+ if (DISALLOWED_SVG_TAGS_LOWER.has(tagName.toLowerCase())) {
3843
+ console.warn("SVG tag blocked (dangerous): ", tagName);
3844
+ return null;
2425
3845
  }
2426
- const processNode = (node) => {
2427
- const inlineAnim = node.animate;
2428
- if (inlineAnim && Object.keys(inlineAnim).length > 0) {
2429
- const nodeId = node.id || generateElementId();
2430
- node.id = nodeId;
2431
- const normalized = processAnimation(nodeId, inlineAnim);
2432
- if (normalized) bindings.push(normalized);
2433
- }
2434
- if (node.children) {
2435
- for (let i = 0; i < node.children.length; i++) {
2436
- processNode(node.children[i]);
2437
- }
2438
- }
2439
- };
2440
- if (doc.children) {
2441
- for (let i = 0; i < doc.children.length; i++) {
2442
- processNode(doc.children[i]);
3846
+ const element = document.createElementNS(SVG_NS, tagName);
3847
+ for (const propName in normalisedProps) {
3848
+ const sanitised = sanitiseAttributeValue(propName, normalisedProps[propName]);
3849
+ if (sanitised === void 0) continue;
3850
+ element.setAttribute(camelCaseToKebabWordIfNeeded(propName), sanitised);
3851
+ }
3852
+ if (style) {
3853
+ for (const styleProp in style) {
3854
+ element.style[styleProp] = String(style[styleProp]);
2443
3855
  }
2444
3856
  }
2445
- return bindings;
2446
- }
2447
- function getKeyframesPair(keyframes, progress) {
2448
- var _a, _b;
2449
- let prevKf = keyframes[0];
2450
- let nextKf = keyframes[keyframes.length - 1];
2451
- for (let j = 0; j < keyframes.length - 1; j++) {
2452
- const aOff = (_a = keyframes[j].t) != null ? _a : 0;
2453
- const bOff = (_b = keyframes[j + 1].t) != null ? _b : 0;
2454
- if (aOff <= progress && progress <= bOff) {
2455
- prevKf = keyframes[j];
2456
- nextKf = keyframes[j + 1];
2457
- break;
3857
+ if (children) {
3858
+ for (const child of children) {
3859
+ element.appendChild(child);
2458
3860
  }
2459
3861
  }
2460
- return { prevKf, nextKf };
3862
+ if (textContent) element.textContent = textContent;
3863
+ return element;
2461
3864
  }
2462
- function calcPropertyValue(propName, propAnim, progress) {
2463
- var _a, _b, _c, _d, _e, _f, _g;
2464
- const keyframes = propAnim.kfs || propAnim.keyframes || [];
2465
- if (keyframes.length === 0) return null;
2466
- const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
2467
- let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a = prevKf.t) != null ? _a : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
2468
- localProgress = clamp(localProgress, 0, 1);
2469
- const easing = (_c = prevKf.e) != null ? _c : prevKf.easing;
2470
- if (easing && Array.isArray(easing)) {
2471
- try {
2472
- localProgress = cubicBezier(easing)(localProgress);
2473
- } catch (e) {
2474
- }
3865
+ function resolveStyle(style, defs) {
3866
+ var _a;
3867
+ if (!style) return void 0;
3868
+ if (typeof style === "string") {
3869
+ return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
2475
3870
  }
2476
- let cssAttrName = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2477
- let cssValue = null;
2478
- const prevV = (_d = prevKf == null ? void 0 : prevKf.v) != null ? _d : prevKf == null ? void 0 : prevKf.value;
2479
- const nextV = (_e = nextKf == null ? void 0 : nextKf.v) != null ? _e : nextKf == null ? void 0 : nextKf.value;
2480
- if (cssAttrName === "d") {
2481
- const prevPaths = (_f = prevV == null ? void 0 : prevV.paths) != null ? _f : Array.isArray(prevV) ? prevV : [];
2482
- const nextPaths = (_g = nextV == null ? void 0 : nextV.paths) != null ? _g : Array.isArray(nextV) ? nextV : [];
2483
- cssValue = interpolateBeziers(
2484
- prevPaths,
2485
- nextPaths,
2486
- localProgress
2487
- ).map((bz) => bezierToSvgPath(bz)).join("");
2488
- } else if (COLOUR_ATTR_NAMES.has(cssAttrName)) {
2489
- cssValue = toRGBA(interpolateColor(
2490
- prevV || [0, 0, 0, 1],
2491
- nextV || [0, 0, 0, 1],
2492
- localProgress
2493
- ));
2494
- cssAttrName = propName;
2495
- } else if (cssAttrName === "stroke-dasharray") {
2496
- cssValue = interpolateVec(
2497
- prevV || [],
2498
- nextV || [],
2499
- localProgress
2500
- ).join(" ");
2501
- cssAttrName = propName;
2502
- } else if (cssAttrName === "transform" && prevV !== null && typeof prevV === "object" && !Array.isArray(prevV)) {
2503
- const partKeys = /* @__PURE__ */ new Set([
2504
- ...prevV ? Object.keys(prevV) : [],
2505
- ...nextV ? Object.keys(nextV) : []
2506
- ]);
2507
- const partsResult = {};
2508
- for (const partKey of partKeys) {
2509
- const prevPart = prevV == null ? void 0 : prevV[partKey];
2510
- const nextPart = nextV == null ? void 0 : nextV[partKey];
2511
- if (partKey === "rotate") {
2512
- partsResult.rotate = interpolateNum(+(prevPart != null ? prevPart : 0), +(nextPart != null ? nextPart : 0), localProgress);
2513
- } else if (partKey === "translate" || partKey === "scale" || partKey === "origin") {
2514
- const fallback = partKey === "scale" ? [1, 1] : [0, 0];
2515
- const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);
2516
- partsResult[partKey] = interp;
3871
+ return style;
3872
+ }
3873
+ function getNormalizedProps(props) {
3874
+ const propsCopy = {};
3875
+ for (const rawKey of Object.keys(props)) {
3876
+ const key = kebabToCamelCaseWord(rawKey);
3877
+ if (INTERNAL_ATTRS.has(key)) continue;
3878
+ if (key === "style") continue;
3879
+ let value = props[rawKey];
3880
+ if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
3881
+ propsCopy[key] = toRGBA(value);
3882
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
3883
+ propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
3884
+ } else if (TRANSFORM_FN_NAMES.has(key)) {
3885
+ if (Array.isArray(value)) {
3886
+ if (key === "translate") value = value.map((v) => v + "px");
3887
+ value = value.join(",");
2517
3888
  }
3889
+ if (key === "rotate") value = value + "deg";
3890
+ propsCopy["transform"] = key + "(" + value + ")";
3891
+ } else if (value !== void 0 && value !== null) {
3892
+ propsCopy[key] = String(value);
2518
3893
  }
2519
- if (propAnimIsMotionPath(propAnim)) {
2520
- const prevTr = prevV.translate;
2521
- const nextTr = nextV.translate;
2522
- if (Array.isArray(prevTr) && Array.isArray(nextTr)) {
2523
- const sample = evaluateMotionPathSegment(
2524
- prevKf,
2525
- nextKf,
2526
- [+prevTr[0], +prevTr[1]],
2527
- [+nextTr[0], +nextTr[1]],
2528
- localProgress,
2529
- !!propAnim.autoOrient
2530
- );
2531
- partsResult.translate = [sample.translate[0], sample.translate[1]];
2532
- if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg;
3894
+ }
3895
+ return propsCopy;
3896
+ }
3897
+ function renderNode(node, defs) {
3898
+ if (!node) return null;
3899
+ const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
3900
+ const nodeDefs = getDefs(node) || defs;
3901
+ const resolvedStyle = resolveStyle(style, nodeDefs);
3902
+ let childElements;
3903
+ if (children) {
3904
+ for (const ch of children) {
3905
+ const child = renderNode(ch, nodeDefs);
3906
+ if (child) {
3907
+ if (!childElements) childElements = [];
3908
+ childElements.push(child);
2533
3909
  }
2534
3910
  }
2535
- cssValue = composeTransformParts(partsResult, { withUnits: false });
2536
- cssAttrName = "transform";
2537
- } else if (cssAttrName === "translate") {
2538
- const v = interpolateVec(
2539
- prevV || [0, 0],
2540
- nextV || [0, 0],
2541
- localProgress
2542
- );
2543
- cssValue = "translate(" + v.join(",") + ")";
2544
- cssAttrName = "transform";
2545
- } else if (cssAttrName === "rotate") {
2546
- const v = interpolateNum(
2547
- +(prevV || 0),
2548
- +(nextV || 0),
2549
- localProgress
2550
- );
2551
- cssValue = "rotate(" + v + ")";
2552
- cssAttrName = "transform";
2553
- } else if (cssAttrName === "scale") {
2554
- const v = interpolateVec(
2555
- prevV || [1, 1],
2556
- nextV || [1, 1],
2557
- localProgress
2558
- );
2559
- cssValue = "scale(" + v.join(",") + ")";
2560
- cssAttrName = "transform";
2561
- } else {
2562
- const num2 = interpolateNum(
2563
- +(prevV || 0),
2564
- +(nextV || 0),
2565
- localProgress
2566
- );
2567
- cssValue = num2;
2568
- }
2569
- if (PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
2570
- cssValue = cssValue * 100 + "%";
2571
3911
  }
2572
- return { k: cssAttrName, v: cssValue === null ? "" : "" + cssValue };
3912
+ return createElement(
3913
+ type || "g",
3914
+ getNormalizedProps(props),
3915
+ resolvedStyle,
3916
+ childElements,
3917
+ props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
3918
+ );
2573
3919
  }
2574
- function calcAnimationValues(animDef, progress) {
2575
- const result = {};
2576
- for (const [propName, propAnim] of Object.entries(animDef)) {
2577
- const computed = calcPropertyValue(propName, propAnim, progress);
2578
- if (computed) {
2579
- result[computed.k] = computed.v;
3920
+
3921
+ // src/PxAnimatorTriggers.ts
3922
+ function setupAnimationTriggers(api, config) {
3923
+ const { startOn, outAction = "continue", scrollIntoViewThreshold = 0.5 } = config;
3924
+ const root = api.getRootElement();
3925
+ if (!root) {
3926
+ console.warn("setupAnimationTriggers: No root element found for animation.");
3927
+ return api;
3928
+ }
3929
+ const start = () => {
3930
+ api.play();
3931
+ };
3932
+ const handleEndAction = () => {
3933
+ switch (outAction) {
3934
+ case "pause":
3935
+ api.pause();
3936
+ break;
3937
+ case "reset":
3938
+ api.cancel();
3939
+ break;
3940
+ case "reverse":
3941
+ api.play();
3942
+ break;
3943
+ case "continue":
3944
+ default:
3945
+ break;
3946
+ }
3947
+ };
3948
+ switch (startOn) {
3949
+ case "load": {
3950
+ const startHandler = () => start();
3951
+ if (document.readyState === "complete") {
3952
+ startHandler();
3953
+ } else {
3954
+ window.addEventListener("load", startHandler, { once: true });
3955
+ }
3956
+ break;
3957
+ }
3958
+ case "mouseOver": {
3959
+ const mouseOverHandler = () => start();
3960
+ const mouseOutHandler = () => handleEndAction();
3961
+ root.addEventListener("mouseenter", mouseOverHandler);
3962
+ root.addEventListener("mouseleave", mouseOutHandler);
3963
+ break;
2580
3964
  }
3965
+ case "click": {
3966
+ const clickHandler = () => {
3967
+ if (api.isPlaying()) {
3968
+ handleEndAction();
3969
+ } else {
3970
+ start();
3971
+ }
3972
+ };
3973
+ root.addEventListener("click", clickHandler);
3974
+ break;
3975
+ }
3976
+ case "scrollIntoView": {
3977
+ const observer = new IntersectionObserver(
3978
+ (entries) => {
3979
+ entries.forEach((entry) => {
3980
+ if (entry.isIntersecting && entry.intersectionRatio >= scrollIntoViewThreshold) {
3981
+ start();
3982
+ } else {
3983
+ handleEndAction();
3984
+ }
3985
+ });
3986
+ },
3987
+ { threshold: scrollIntoViewThreshold }
3988
+ );
3989
+ observer.observe(root);
3990
+ break;
3991
+ }
3992
+ case "programmatic":
3993
+ break;
2581
3994
  }
2582
- return result;
3995
+ return api;
2583
3996
  }
2584
3997
 
2585
3998
  // src/PxAnimatorFrameLoop.ts
@@ -2588,7 +4001,7 @@ function getSelector(id) {
2588
4001
  }
2589
4002
  function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
2590
4003
  const config = getAnimatorConfig(doc) || {};
2591
- const bindings = getNormalisedBindings(doc);
4004
+ const bindings = getNormalisedBindings(doc, PxAnimatorEngine.frames);
2592
4005
  const _iterations = config.iterations;
2593
4006
  let iterations = 1;
2594
4007
  if (typeof _iterations === "number") iterations = _iterations || 1;
@@ -2706,6 +4119,10 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
2706
4119
  };
2707
4120
  const startAnim = () => {
2708
4121
  if (playing) return;
4122
+ if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= totalDuration) {
4123
+ timeBeforeLastStartMs = 0;
4124
+ finishCalled = false;
4125
+ }
2709
4126
  playing = true;
2710
4127
  lastStartedTs = Date.now();
2711
4128
  loopAnim(true);
@@ -2938,7 +4355,6 @@ function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
2938
4355
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs) {
2939
4356
  var _a;
2940
4357
  const config = getAnimatorConfig(doc) || {};
2941
- const bindings = getNormalisedBindings(doc);
2942
4358
  if (!rootElement) {
2943
4359
  if (doc.id) {
2944
4360
  const rootSelector = getSelector(doc.id);
@@ -2948,6 +4364,7 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
2948
4364
  console.warn("createFrameLoopAnimator: No root element provided");
2949
4365
  }
2950
4366
  }
4367
+ const bindings = getNormalisedBindings(doc, PxAnimatorEngine.webapi);
2951
4368
  const animations = [];
2952
4369
  const _iterations = config.iterations;
2953
4370
  let iterations;
@@ -3079,15 +4496,15 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
3079
4496
  function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
3080
4497
  const animatorConfig = getAnimatorConfig(doc) || {};
3081
4498
  let res;
3082
- if (animatorConfig.mode === "frames") {
4499
+ if (animatorConfig.mode === PxAnimatorMode.frames) {
3083
4500
  res = createFrameLoopAnimator(doc, adapter, callbacks, rootElement);
3084
4501
  } else {
3085
4502
  res = createWebApiAnimator(
3086
4503
  doc,
3087
4504
  callbacks,
3088
4505
  rootElement,
3089
- animatorConfig.mode === "webapi"
3090
- // Forcing "webapi"
4506
+ animatorConfig.mode === PxAnimatorMode.webapi
4507
+ // forcing webapi
3091
4508
  ) || createFrameLoopAnimator(doc, adapter, callbacks, rootElement);
3092
4509
  }
3093
4510
  if (animatorConfig.debugInstName) {
@@ -3209,9 +4626,10 @@ function replaceUrlRefs(value, idMap) {
3209
4626
  function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
3210
4627
  const effectsWarnings = validateNodeEffects(doc);
3211
4628
  for (const w of effectsWarnings) console.warn("[PxAnimator] effects shape warning:", w);
3212
- doc = applyPlayerEffects(doc).root;
3213
4629
  const animatorConfig = getAnimatorConfig(doc) || {};
3214
4630
  animatorConfig.debug = true;
4631
+ const engine = animatorConfig.mode === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi;
4632
+ doc = materialiseAllInTree(doc, engine);
3215
4633
  let rootElement = null;
3216
4634
  if (containerElement && doc.children) {
3217
4635
  doc = generateNewIds(doc);
@@ -3388,10 +4806,10 @@ function evalScalar(animated, staticVal, fallback, t) {
3388
4806
  }
3389
4807
  var CONTAINER_TYPES = /* @__PURE__ */ new Set(["svg", "g", "symbol"]);
3390
4808
  var SKIP_TYPES = /* @__PURE__ */ new Set(["defs", "mask", "clipPath", "title"]);
3391
- function buildIdMap(node, map) {
4809
+ function buildIdMap2(node, map) {
3392
4810
  var _a;
3393
4811
  if (typeof node.id === "string") map.set(node.id, node);
3394
- (_a = node.children) == null ? void 0 : _a.forEach((c) => buildIdMap(c, map));
4812
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => buildIdMap2(c, map));
3395
4813
  }
3396
4814
  function num(v) {
3397
4815
  return v === void 0 || v === null ? 0 : Number(v);
@@ -3468,7 +4886,7 @@ function collectSampleTimes(node, into) {
3468
4886
  }
3469
4887
  function visualModelAt(root, t) {
3470
4888
  const idMap = /* @__PURE__ */ new Map();
3471
- buildIdMap(root, idMap);
4889
+ buildIdMap2(root, idMap);
3472
4890
  const out = [];
3473
4891
  flatten(root, IDENTITY, t, idMap, out);
3474
4892
  return out.sort();
@@ -3507,6 +4925,8 @@ export {
3507
4925
  PxAnimatedSvgDocumentSchema,
3508
4926
  PxAnimationDefinitionSchema,
3509
4927
  PxAnimatorConfigSchema,
4928
+ PxAnimatorEngine,
4929
+ PxAnimatorMode,
3510
4930
  PxAttrValueSchema,
3511
4931
  PxBezierPathSchema,
3512
4932
  PxBindingSchema,
@@ -3514,6 +4934,11 @@ export {
3514
4934
  PxEasingOrRefSchema,
3515
4935
  PxEffectsSchema,
3516
4936
  PxElementAnimationSchema,
4937
+ PxFillGradientEffectSchema,
4938
+ PxGradientSpreadMethod,
4939
+ PxGradientStopSchema,
4940
+ PxGradientType,
4941
+ PxGradientUnits,
3517
4942
  PxKeyframeSchema,
3518
4943
  PxLoopSchema,
3519
4944
  PxMaskedByEffectSchema,
@@ -3523,7 +4948,9 @@ export {
3523
4948
  PxRefEffectSchema,
3524
4949
  PxRepeaterEffectSchema,
3525
4950
  PxRetimeEffectSchema,
4951
+ PxStrokeGradientEffectSchema,
3526
4952
  PxSvgNodeExtra,
4953
+ PxTextAlongPathEffectSchema,
3527
4954
  PxTransformPartsSchema,
3528
4955
  PxTransformValueSchema,
3529
4956
  PxTransformationEffectSchema,
@@ -3542,6 +4969,7 @@ export {
3542
4969
  createWebApiAnimator,
3543
4970
  describeSchema,
3544
4971
  diffInEffect,
4972
+ evaluateMotionPathSegment,
3545
4973
  generateNewIds,
3546
4974
  getAnimatorConfig,
3547
4975
  getBindings,
@@ -3551,7 +4979,14 @@ export {
3551
4979
  isPxElementFileFormat,
3552
4980
  isPxElementFileFormatDeep,
3553
4981
  loadTagAnimators,
4982
+ materialiseAllInTree,
4983
+ materialiseAnimatedUseInstances,
4984
+ materialiseInternalLoopsInPropAnim,
4985
+ materialiseInternalLoopsInTree,
4986
+ materialiseMotionPathInPropAnim,
4987
+ materialiseMotionPathsInTree,
3554
4988
  getNormalisedBindings as normalizeDocument,
4989
+ propAnimIsMotionPath,
3555
4990
  px,
3556
4991
  renderNode,
3557
4992
  schemaKeys,