@pixodesk/svg-animator-react 1.0.16 → 1.0.17

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.umd.js CHANGED
@@ -1812,7 +1812,7 @@ var PixodeskAnimatorReact = (() => {
1812
1812
  if (typeof raw === "object") {
1813
1813
  const obj = raw;
1814
1814
  if (obj.keyframes) {
1815
- return { kind: "animated", keyframes: obj.keyframes, autoOrient: obj.autoOrient };
1815
+ return { kind: "animated", keyframes: obj.keyframes, autoOrient: obj.autoOrient, loop: obj.loop };
1816
1816
  }
1817
1817
  if (obj.value !== void 0) return { kind: "static", value: obj.value };
1818
1818
  }
@@ -1900,6 +1900,7 @@ var PixodeskAnimatorReact = (() => {
1900
1900
  if (v.kind === "animated") {
1901
1901
  const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
1902
1902
  if (v.autoOrient) animTr.autoOrient = true;
1903
+ if (v.loop !== void 0) animTr.loop = v.loop;
1903
1904
  return {
1904
1905
  type: "g",
1905
1906
  animate: { transform: animTr },
@@ -1918,9 +1919,11 @@ var PixodeskAnimatorReact = (() => {
1918
1919
  return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
1919
1920
  }
1920
1921
  if (v.kind === "animated") {
1922
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) };
1923
+ if (v.loop !== void 0) animTr.loop = v.loop;
1921
1924
  return {
1922
1925
  type: "g",
1923
- animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) } },
1926
+ animate: { transform: animTr },
1924
1927
  children: [inner]
1925
1928
  };
1926
1929
  }
@@ -1928,8 +1931,8 @@ var PixodeskAnimatorReact = (() => {
1928
1931
  }
1929
1932
  function identifyContentRefTargets(node, ctx, allocator) {
1930
1933
  var _a, _b, _c;
1931
- if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.ref) == null ? void 0 : _b.type) === "content") {
1932
- const baseId = node.effects.ref.baseId;
1934
+ if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.type) === "content") {
1935
+ const baseId = node.effects.clone.baseId;
1933
1936
  if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
1934
1937
  ctx.contentRefInnerIds.set(baseId, allocator(baseId));
1935
1938
  }
@@ -1977,6 +1980,8 @@ var PixodeskAnimatorReact = (() => {
1977
1980
  });
1978
1981
  const outerAnimTr = { keyframes: outerKfs };
1979
1982
  if (animTr.autoOrient) outerAnimTr.autoOrient = true;
1983
+ const srcLoop = animTr.loop;
1984
+ if (srcLoop !== void 0) outerAnimTr.loop = srcLoop;
1980
1985
  out.animate = { transform: outerAnimTr };
1981
1986
  const innerHasPivotedPart = kfs.some((kf) => {
1982
1987
  const v = kf.value || {};
@@ -1998,7 +2003,9 @@ var PixodeskAnimatorReact = (() => {
1998
2003
  delete node.animate.transform;
1999
2004
  if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
2000
2005
  } else {
2001
- node.animate.transform = { keyframes: innerKfs };
2006
+ const innerAnimTr = { keyframes: innerKfs };
2007
+ if (srcLoop !== void 0) innerAnimTr.loop = srcLoop;
2008
+ node.animate.transform = innerAnimTr;
2002
2009
  }
2003
2010
  didLiftAnimate = true;
2004
2011
  }
@@ -2719,9 +2726,10 @@ var PixodeskAnimatorReact = (() => {
2719
2726
  stretch: px.number().optional(),
2720
2727
  timeCrop: px.tuple([px.number(), px.number()]).optional()
2721
2728
  }));
2722
- var PxRefEffectSchema = implementsInterface()(px.object({
2729
+ var PxCloneEffectSchema = implementsInterface()(px.object({
2730
+ type: px.string().optional(),
2723
2731
  baseId: px.string().optional(),
2724
- type: px.string().optional()
2732
+ retime: PxRetimeEffectSchema.optional()
2725
2733
  }));
2726
2734
  var PxGradientType = {
2727
2735
  linear: "linear",
@@ -2762,9 +2770,8 @@ var PixodeskAnimatorReact = (() => {
2762
2770
  repeater: PxRepeaterEffectSchema.optional(),
2763
2771
  maskedBy: PxMaskedByEffectSchema.optional(),
2764
2772
  trimPath: PxTrimPathEffectSchema.optional(),
2765
- retime: PxRetimeEffectSchema.optional(),
2773
+ clone: PxCloneEffectSchema.optional(),
2766
2774
  isCombinedShape: px.boolean().optional(),
2767
- ref: PxRefEffectSchema.optional(),
2768
2775
  fillGradient: PxFillGradientEffectSchema.optional(),
2769
2776
  strokeGradient: PxStrokeGradientEffectSchema.optional(),
2770
2777
  textAlongPath: PxTextAlongPathEffectSchema.optional()
@@ -2979,6 +2986,7 @@ var PixodeskAnimatorReact = (() => {
2979
2986
  const animBlock = stops;
2980
2987
  const kfs = animBlock.keyframes;
2981
2988
  if (!Array.isArray(kfs) || !kfs.length) return [];
2989
+ const loopFromSource = animBlock.loop;
2982
2990
  let stopCount = 0;
2983
2991
  for (const kf of kfs) {
2984
2992
  const v = (_a = kf.value) != null ? _a : kf.v;
@@ -2991,7 +2999,7 @@ var PixodeskAnimatorReact = (() => {
2991
2999
  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" };
2992
3000
  baselineStops.push({ offset: s.offset, color: s.color });
2993
3001
  }
2994
- return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx));
3002
+ return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx, loopFromSource));
2995
3003
  }
2996
3004
  function staticStopNode(s) {
2997
3005
  return {
@@ -3000,7 +3008,7 @@ var PixodeskAnimatorReact = (() => {
3000
3008
  stopColor: s.color
3001
3009
  };
3002
3010
  }
3003
- function animatedStopNode(baseline, kfs, stopIdx, _ctx) {
3011
+ function animatedStopNode(baseline, kfs, stopIdx, _ctx, loop) {
3004
3012
  var _a, _b, _c, _d, _e;
3005
3013
  const colorKfs = [];
3006
3014
  const offsetKfs = [];
@@ -3025,8 +3033,14 @@ var PixodeskAnimatorReact = (() => {
3025
3033
  stopColor: baseline.color
3026
3034
  };
3027
3035
  const animate = {};
3028
- if (colorKfs.length) animate.stopColor = { keyframes: colorKfs };
3029
- if (offsetVaries && offsetKfs.length) animate.offset = { keyframes: offsetKfs };
3036
+ if (colorKfs.length) {
3037
+ animate.stopColor = { keyframes: colorKfs };
3038
+ if (loop !== void 0) animate.stopColor.loop = loop;
3039
+ }
3040
+ if (offsetVaries && offsetKfs.length) {
3041
+ animate.offset = { keyframes: offsetKfs };
3042
+ if (loop !== void 0) animate.offset.loop = loop;
3043
+ }
3030
3044
  if (Object.keys(animate).length) stop.animate = animate;
3031
3045
  return stop;
3032
3046
  }
@@ -3089,9 +3103,11 @@ var PixodeskAnimatorReact = (() => {
3089
3103
  return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
3090
3104
  }
3091
3105
  if (v.kind === "animated") {
3106
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) };
3107
+ if (v.loop !== void 0) animTr.loop = v.loop;
3092
3108
  return {
3093
3109
  type: "g",
3094
- animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
3110
+ animate: { transform: animTr },
3095
3111
  children: [inner]
3096
3112
  };
3097
3113
  }
@@ -3128,10 +3144,16 @@ var PixodeskAnimatorReact = (() => {
3128
3144
  scaleKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
3129
3145
  }
3130
3146
  }
3147
+ const srcLoop = animTr == null ? void 0 : animTr.loop;
3148
+ const withLoop = (kfs2) => {
3149
+ const block = { keyframes: kfs2 };
3150
+ if (srcLoop !== void 0) block.loop = srcLoop;
3151
+ return block;
3152
+ };
3131
3153
  let n = inner;
3132
- if (translateKfs.length) n = { type: "g", animate: { transform: { keyframes: translateKfs } }, children: [n] };
3133
- if (rotateKfs.length) n = { type: "g", animate: { transform: { keyframes: rotateKfs } }, children: [n] };
3134
- if (scaleKfs.length) n = { type: "g", animate: { transform: { keyframes: scaleKfs } }, children: [n] };
3154
+ if (translateKfs.length) n = { type: "g", animate: { transform: withLoop(translateKfs) }, children: [n] };
3155
+ if (rotateKfs.length) n = { type: "g", animate: { transform: withLoop(rotateKfs) }, children: [n] };
3156
+ if (scaleKfs.length) n = { type: "g", animate: { transform: withLoop(scaleKfs) }, children: [n] };
3135
3157
  return n;
3136
3158
  }
3137
3159
  function nodeHasBodyTransform(node) {
@@ -3363,16 +3385,18 @@ var PixodeskAnimatorReact = (() => {
3363
3385
  return seen ? [x, y] : void 0;
3364
3386
  }
3365
3387
  var CONTENT_SUBREF = "content";
3366
- function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
3367
- if (ref) {
3368
- const baseId = ref.baseId;
3369
- if (!baseId) {
3370
- ctx.errors.push("ref: missing baseId");
3371
- } else {
3372
- const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
3373
- node.href = "#" + targetId;
3374
- }
3388
+ function applyRefHref(node, clone2, ctx) {
3389
+ if (!clone2) return;
3390
+ const baseId = clone2.baseId;
3391
+ if (!baseId) {
3392
+ if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing baseId");
3393
+ return;
3375
3394
  }
3395
+ const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
3396
+ node.href = "#" + targetId;
3397
+ }
3398
+ function applyRefAndTransformationEffect(node, clone2, transformation, ctx) {
3399
+ applyRefHref(node, clone2, ctx);
3376
3400
  return applyTransformationEffect(node, transformation, ctx);
3377
3401
  }
3378
3402
  function applyRepeaterEffect(node, fx, ctx) {
@@ -3479,22 +3503,32 @@ var PixodeskAnimatorReact = (() => {
3479
3503
  stretch: parent.stretch * child.stretch
3480
3504
  };
3481
3505
  }
3482
- function applyAllRetimeEffects(root, ctx) {
3506
+ function readCloneRetime(n) {
3507
+ var _a, _b;
3508
+ return (_b = (_a = n.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.retime;
3509
+ }
3510
+ function clearCloneRetime(n) {
3483
3511
  var _a;
3512
+ const clone2 = (_a = n.effects) == null ? void 0 : _a.clone;
3513
+ if (!clone2) return;
3514
+ delete clone2.retime;
3515
+ if (Object.keys(clone2).length === 0) delete n.effects.clone;
3516
+ if (n.effects && Object.keys(n.effects).length === 0) delete n.effects;
3517
+ }
3518
+ function applyAllRetimeEffects(root, ctx) {
3484
3519
  ctx.idMap.clear();
3485
3520
  indexById(root, ctx.idMap);
3486
3521
  const sites = [];
3487
3522
  const collect = (n) => {
3488
- var _a2, _b;
3489
- if ((_a2 = n.effects) == null ? void 0 : _a2.retime) sites.push(n);
3490
- (_b = n.children) == null ? void 0 : _b.forEach(collect);
3523
+ var _a;
3524
+ if (readCloneRetime(n)) sites.push(n);
3525
+ (_a = n.children) == null ? void 0 : _a.forEach(collect);
3491
3526
  };
3492
3527
  collect(root);
3493
3528
  for (const useNode of sites) {
3494
- const retime = (_a = useNode.effects) == null ? void 0 : _a.retime;
3529
+ const retime = readCloneRetime(useNode);
3495
3530
  if (!retime) continue;
3496
- delete useNode.effects.retime;
3497
- if (Object.keys(useNode.effects).length === 0) delete useNode.effects;
3531
+ clearCloneRetime(useNode);
3498
3532
  materialiseRetime(useNode, asRetime(retime), ctx);
3499
3533
  }
3500
3534
  }
@@ -3518,7 +3552,6 @@ var PixodeskAnimatorReact = (() => {
3518
3552
  }
3519
3553
  }
3520
3554
  function buildChainClone(targetId, accum, ctx, chain) {
3521
- var _a, _b;
3522
3555
  if (chain.has(targetId)) {
3523
3556
  ctx.errors.push('retime: loop via "' + targetId + '"');
3524
3557
  return void 0;
@@ -3535,25 +3568,43 @@ var PixodeskAnimatorReact = (() => {
3535
3568
  } else {
3536
3569
  remapKeyframeTimes(cloneNode, accum.start, accum.stretch);
3537
3570
  }
3538
- if ((_a = cloneNode.effects) == null ? void 0 : _a.retime) {
3539
- delete cloneNode.effects.retime;
3540
- if (Object.keys(cloneNode.effects).length === 0) delete cloneNode.effects;
3541
- }
3571
+ clearCloneRetime(cloneNode);
3542
3572
  if (target.type === "use" && target.href) {
3543
3573
  const subId = stripHash(target.href);
3544
3574
  if (subId) {
3545
- const innerRetime = (_b = target.effects) == null ? void 0 : _b.retime;
3575
+ const innerRetime = readCloneRetime(target);
3546
3576
  const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;
3547
3577
  const subChain = new Set(chain);
3548
3578
  subChain.add(targetId);
3549
3579
  const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
3550
3580
  if (subId2) cloneNode.href = "#" + subId2;
3551
3581
  }
3582
+ } else {
3583
+ materialiseNestedRetimeUses(cloneNode, accum, ctx, chain, targetId);
3552
3584
  }
3553
3585
  ctx.defs.push(cloneNode);
3554
3586
  if (typeof cloneNode.id === "string") ctx.idMap.set(cloneNode.id, cloneNode);
3555
3587
  return typeof cloneNode.id === "string" ? cloneNode.id : void 0;
3556
3588
  }
3589
+ function materialiseNestedRetimeUses(node, accum, ctx, chain, parentTargetId) {
3590
+ const visit = (n) => {
3591
+ var _a;
3592
+ const retime = readCloneRetime(n);
3593
+ if (n.type === "use" && retime && n.href) {
3594
+ const subId = stripHash(n.href);
3595
+ if (subId) {
3596
+ const subAccum = concatRetime(asRetime(retime), accum);
3597
+ const subChain = new Set(chain);
3598
+ subChain.add(parentTargetId);
3599
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
3600
+ if (subId2) n.href = "#" + subId2;
3601
+ }
3602
+ clearCloneRetime(n);
3603
+ }
3604
+ (_a = n.children) == null ? void 0 : _a.forEach(visit);
3605
+ };
3606
+ visit(node);
3607
+ }
3557
3608
  function remapKeyframeTimes(node, start, stretch) {
3558
3609
  var _a;
3559
3610
  remapKeyframeTimesOnly(node, start, stretch);
@@ -3596,12 +3647,14 @@ var PixodeskAnimatorReact = (() => {
3596
3647
  node[attrName] = String(raw);
3597
3648
  return;
3598
3649
  }
3599
- if (typeof raw === "object") {
3650
+ if (typeof raw === "object" && raw !== null) {
3600
3651
  const obj = raw;
3601
3652
  if (Array.isArray(obj.keyframes)) {
3602
3653
  const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3603
3654
  const animate = __spreadValues({}, prevAnimate || {});
3604
- animate[attrName] = { keyframes: obj.keyframes };
3655
+ const block = { keyframes: obj.keyframes };
3656
+ if (obj.loop !== void 0) block.loop = obj.loop;
3657
+ animate[attrName] = block;
3605
3658
  node.animate = animate;
3606
3659
  return;
3607
3660
  }
@@ -4658,16 +4711,67 @@ var PixodeskAnimatorReact = (() => {
4658
4711
  looped.push(pushed);
4659
4712
  }
4660
4713
  }
4661
- for (let rep = 0; rep < fullReps; rep++) {
4662
- const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
4663
- const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
4664
- const repStart = fillStart + rep * segDuration;
4665
- appendRep(repStart, isReversed);
4714
+ function appendRepTail(repStart, isReversed, tailFraction) {
4715
+ let entries;
4716
+ if (isReversed) {
4717
+ entries = [];
4718
+ for (let i = template.length - 1; i >= 0; i--) {
4719
+ entries.push({
4720
+ relT: 1 - template[i].relT,
4721
+ v: template[i].v,
4722
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
4723
+ tangentIn: template[i].tangentOut,
4724
+ tangentOut: template[i].tangentIn
4725
+ });
4726
+ }
4727
+ } else {
4728
+ entries = template;
4729
+ }
4730
+ const startRelT = 1 - tailFraction;
4731
+ for (let i = 0; i < entries.length; i++) {
4732
+ const entry = entries[i];
4733
+ if (entry.relT < startRelT - 1e-9) continue;
4734
+ const prev = entries[i - 1];
4735
+ if (prev && prev.relT < startRelT - 1e-9 && entry.relT > startRelT + 1e-9) {
4736
+ const intervalSpan = entry.relT - prev.relT;
4737
+ const localFrac = (startRelT - prev.relT) / intervalSpan;
4738
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
4739
+ const startValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
4740
+ const { right: rightEasing } = splitEasing(prev.e, localFrac);
4741
+ looped.push({ t: repStart, v: startValue, e: rightEasing });
4742
+ }
4743
+ const pushed = {
4744
+ t: repStart + (entry.relT - startRelT) * segDuration,
4745
+ v: entry.v,
4746
+ e: i < entries.length - 1 ? entry.e : void 0
4747
+ };
4748
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
4749
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
4750
+ looped.push(pushed);
4751
+ }
4666
4752
  }
4667
- if (partialFraction > 1e-9) {
4668
- const isReversed = !!loop.alternate && fullReps % 2 === 0;
4669
- const repStart = fillStart + fullReps * segDuration;
4670
- appendRep(repStart, isReversed, partialFraction);
4753
+ if (loop.before) {
4754
+ if (partialFraction > 1e-9) {
4755
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
4756
+ appendRepTail(fillStart, isReversed, partialFraction);
4757
+ }
4758
+ for (let rep = 0; rep < fullReps; rep++) {
4759
+ const distFromBoundary = fullReps - 1 - rep;
4760
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
4761
+ const repStart = fillStart + remainder + rep * segDuration;
4762
+ appendRep(repStart, isReversed);
4763
+ }
4764
+ } else {
4765
+ for (let rep = 0; rep < fullReps; rep++) {
4766
+ const isReversed = !!loop.alternate && rep % 2 === 0;
4767
+ const repStart = fillStart + rep * segDuration;
4768
+ appendRep(repStart, isReversed);
4769
+ }
4770
+ if (partialFraction > 1e-9) {
4771
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
4772
+ const repStart = fillStart + fullReps * segDuration;
4773
+ appendRep(repStart, isReversed, partialFraction);
4774
+ }
4671
4775
  }
4672
4776
  if (loop.before) {
4673
4777
  return [...looped, ...keyframes];
@@ -4984,7 +5088,6 @@ var PixodeskAnimatorReact = (() => {
4984
5088
  if (!trimPath) return node;
4985
5089
  const trimAllAsOne = !!trimPath.trimAllAsOne;
4986
5090
  const leafEntries = [];
4987
- let acc = 0;
4988
5091
  const measure = (n) => {
4989
5092
  if (Array.isArray(n.children) && n.children.length > 0) {
4990
5093
  for (const ch of n.children) measure(ch);
@@ -4996,15 +5099,22 @@ var PixodeskAnimatorReact = (() => {
4996
5099
  if (!subpaths.length) return;
4997
5100
  const entry = { leaf: n, subpaths: [] };
4998
5101
  for (const sp of subpaths) {
4999
- if (!trimAllAsOne) acc = 0;
5000
5102
  const lengthPx = pxBezierPathLength(sp);
5001
- entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: acc });
5002
- acc += lengthPx;
5103
+ entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: 0 });
5003
5104
  }
5004
5105
  leafEntries.push(entry);
5005
5106
  };
5006
5107
  measure(node);
5007
5108
  if (!leafEntries.length) return node;
5109
+ let acc = 0;
5110
+ const iterOrder = trimAllAsOne ? [...leafEntries].reverse() : leafEntries;
5111
+ for (const entry of iterOrder) {
5112
+ for (const sp of entry.subpaths) {
5113
+ if (!trimAllAsOne) acc = 0;
5114
+ sp.startOffsetPx = acc;
5115
+ acc += sp.lengthPx;
5116
+ }
5117
+ }
5008
5118
  const chainLengthPx = acc;
5009
5119
  if (trimAllAsOne && chainLengthPx < 1e-3) return node;
5010
5120
  const offsetReadRaw = readAnimatable(trimPath.offset);
@@ -5131,10 +5241,12 @@ var PixodeskAnimatorReact = (() => {
5131
5241
  value: map((_c = kf.value) != null ? _c : kf.v),
5132
5242
  easing: (_d = kf.easing) != null ? _d : kf.e
5133
5243
  };
5134
- })
5244
+ }),
5245
+ loop: read.loop
5135
5246
  };
5136
5247
  }
5137
5248
  function applyAttr(node, attrName, attr) {
5249
+ var _a, _b;
5138
5250
  if (!attr) return;
5139
5251
  if (attr.kind === "static") {
5140
5252
  node[attrName] = attr.value;
@@ -5142,9 +5254,16 @@ var PixodeskAnimatorReact = (() => {
5142
5254
  }
5143
5255
  const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
5144
5256
  const animate = __spreadValues({}, prevAnimate || {});
5145
- animate[attrName] = { keyframes: attr.keyframes };
5257
+ const block = { keyframes: attr.keyframes };
5258
+ if (attr.loop !== void 0) block.loop = attr.loop;
5259
+ animate[attrName] = block;
5146
5260
  node.animate = animate;
5261
+ const firstKf = attr.keyframes[0];
5262
+ if (firstKf && ((_a = firstKf.value) != null ? _a : firstKf.v) !== void 0) {
5263
+ node[attrName] = (_b = firstKf.value) != null ? _b : firstKf.v;
5264
+ }
5147
5265
  }
5266
+ var OPACITY_STEP_MS = 10;
5148
5267
  function computeOpacityFromRange(rangeRead) {
5149
5268
  var _a, _b, _c, _d, _e, _f;
5150
5269
  const hide = (v) => v[0] === v[1];
@@ -5170,9 +5289,9 @@ var PixodeskAnimatorReact = (() => {
5170
5289
  const nextHide = nextKf ? thisHide && hide((_f = nextKf.value) != null ? _f : nextKf.v) : thisHide;
5171
5290
  if (prevHide && !nextHide) {
5172
5291
  out.push({ time: t, value: 0 });
5173
- out.push({ time: t + 1, value: 1 });
5292
+ out.push({ time: t + OPACITY_STEP_MS, value: 1 });
5174
5293
  } else if (!prevHide && nextHide) {
5175
- out.push({ time: t - 1, value: 1 });
5294
+ out.push({ time: t - OPACITY_STEP_MS, value: 1 });
5176
5295
  out.push({ time: t, value: 0 });
5177
5296
  }
5178
5297
  }
@@ -5303,6 +5422,7 @@ var PixodeskAnimatorReact = (() => {
5303
5422
  return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
5304
5423
  }
5305
5424
  function applyPlayerEffects(root) {
5425
+ var _a;
5306
5426
  const ctx = {
5307
5427
  defs: [],
5308
5428
  warnings: [],
@@ -5310,7 +5430,10 @@ var PixodeskAnimatorReact = (() => {
5310
5430
  idMap: /* @__PURE__ */ new Map(),
5311
5431
  nextId: 0,
5312
5432
  contentRefInnerIds: /* @__PURE__ */ new Map(),
5313
- maskAncestorChains: /* @__PURE__ */ new Map()
5433
+ maskAncestorChains: /* @__PURE__ */ new Map(),
5434
+ // Resolved engine: `frames` ONLY when explicitly set; auto/webapi/unset →
5435
+ // webapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5436
+ engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi
5314
5437
  };
5315
5438
  const working = clone(root);
5316
5439
  indexById(working, ctx.idMap);
@@ -5327,7 +5450,7 @@ var PixodeskAnimatorReact = (() => {
5327
5450
  const originalId = typeof node.id === "string" ? node.id : void 0;
5328
5451
  const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
5329
5452
  if (!fx && !innerIdForContentRef) return node;
5330
- const { transformation, repeater, maskedBy, trimPath, retime, ref, fillGradient, strokeGradient, textAlongPath } = fx != null ? fx : {};
5453
+ const { transformation, repeater, maskedBy, trimPath, clone: cloneFx, fillGradient, strokeGradient, textAlongPath } = fx != null ? fx : {};
5331
5454
  const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
5332
5455
  if (fx) delete node.effects;
5333
5456
  let n = node;
@@ -5338,11 +5461,12 @@ var PixodeskAnimatorReact = (() => {
5338
5461
  n = applyRepeaterEffect(n, repeater, ctx);
5339
5462
  n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
5340
5463
  if (innerIdForContentRef) {
5464
+ applyRefHref(n, cloneFx, ctx);
5341
5465
  n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
5342
5466
  } else {
5343
- n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
5467
+ n = applyRefAndTransformationEffect(n, cloneFx, transformation, ctx);
5344
5468
  }
5345
- if (retime) node.effects = { retime };
5469
+ if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
5346
5470
  if (originalId) ctx.idMap.set(originalId, n);
5347
5471
  return n;
5348
5472
  }
@@ -5357,13 +5481,30 @@ var PixodeskAnimatorReact = (() => {
5357
5481
  if (animatedIds.size === 0) return root;
5358
5482
  let idCounter = 0;
5359
5483
  const genId3 = () => "_lw_use_mat_" + ++idCounter;
5484
+ const rootViewport = readRootViewport(root);
5360
5485
  const defsCollector = [];
5361
- const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector);
5486
+ const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector, rootViewport);
5362
5487
  if (defsCollector.length === 0) return walked;
5363
5488
  const defsNode = { type: "defs", children: defsCollector };
5364
5489
  const newChildren = [...(_a = walked.children) != null ? _a : [], defsNode];
5365
5490
  return __spreadProps(__spreadValues({}, walked), { children: newChildren });
5366
5491
  }
5492
+ function readRootViewport(root) {
5493
+ const vb = parseViewBox(root.viewBox);
5494
+ if (vb) return [vb[2], vb[3]];
5495
+ const w = numericAttr(root.width);
5496
+ const h = numericAttr(root.height);
5497
+ if (w !== void 0 && h !== void 0) return [w, h];
5498
+ return [1, 1];
5499
+ }
5500
+ function numericAttr(v) {
5501
+ if (typeof v === "number") return v;
5502
+ if (typeof v === "string") {
5503
+ const n = parseFloat(v);
5504
+ return Number.isFinite(n) ? n : void 0;
5505
+ }
5506
+ return void 0;
5507
+ }
5367
5508
  function buildIdMap(root) {
5368
5509
  const map = /* @__PURE__ */ new Map();
5369
5510
  const visit = (n) => {
@@ -5415,16 +5556,22 @@ var PixodeskAnimatorReact = (() => {
5415
5556
  if (typeof href !== "string") return void 0;
5416
5557
  return href.startsWith("#") ? href.slice(1) : href;
5417
5558
  }
5418
- function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector) {
5559
+ function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector, rootViewport) {
5560
+ var _a, _b, _c, _d;
5419
5561
  const clone2 = deepClonePxNode(target);
5420
5562
  regenerateIdsAndRewriteRefs(clone2, genId3);
5421
- const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector) : clone2;
5422
- const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector);
5563
+ const symbolViewBox = clone2.type === "symbol" ? parseViewBox(clone2.viewBox) : void 0;
5564
+ const useW = (_b = (_a = numericAttr(useNode.width)) != null ? _a : symbolViewBox == null ? void 0 : symbolViewBox[2]) != null ? _b : rootViewport[0];
5565
+ const useH = (_d = (_c = numericAttr(useNode.height)) != null ? _c : symbolViewBox == null ? void 0 : symbolViewBox[3]) != null ? _d : rootViewport[1];
5566
+ const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector, useW, useH) : clone2;
5567
+ const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector, rootViewport);
5423
5568
  const newNode = __spreadProps(__spreadValues({}, useNode), { type: "g", children: [materialisedClone] });
5424
5569
  delete newNode.href;
5570
+ delete newNode.width;
5571
+ delete newNode.height;
5425
5572
  return applyUseOffsetToG(newNode);
5426
5573
  }
5427
- function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector) {
5574
+ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector, useW, useH) {
5428
5575
  const viewBox = parseViewBox(symbolNode.viewBox);
5429
5576
  const g = __spreadProps(__spreadValues({}, symbolNode), { type: "g" });
5430
5577
  delete g.viewBox;
@@ -5433,9 +5580,14 @@ var PixodeskAnimatorReact = (() => {
5433
5580
  delete g.height;
5434
5581
  if (!viewBox) return g;
5435
5582
  const [vbX, vbY, vbW, vbH] = viewBox;
5436
- if (vbX !== 0 || vbY !== 0) {
5437
- g.transform = "translate(" + -vbX + "," + -vbY + ")";
5438
- }
5583
+ const scale = vbW > 0 && vbH > 0 ? Math.min(useW / vbW, useH / vbH) : 1;
5584
+ const xOff = (useW - vbW * scale) / 2;
5585
+ const yOff = (useH - vbH * scale) / 2;
5586
+ const parts = [];
5587
+ if (xOff !== 0 || yOff !== 0) parts.push("translate(" + xOff + "," + yOff + ")");
5588
+ if (scale !== 1) parts.push("scale(" + scale + ")");
5589
+ if (vbX !== 0 || vbY !== 0) parts.push("translate(" + -vbX + "," + -vbY + ")");
5590
+ if (parts.length) g.transform = parts.join("");
5439
5591
  const clipId = genId3();
5440
5592
  defsCollector.push({
5441
5593
  type: "clipPath",
@@ -5451,18 +5603,18 @@ var PixodeskAnimatorReact = (() => {
5451
5603
  if (parts.length < 4 || parts.some((n) => !Number.isFinite(n))) return void 0;
5452
5604
  return [parts[0], parts[1], parts[2], parts[3]];
5453
5605
  }
5454
- function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector) {
5606
+ function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector, rootViewport) {
5455
5607
  if (node.type === "use" && typeof node.href === "string") {
5456
5608
  const targetId = stripHash2(node.href);
5457
5609
  if (targetId && animatedIds.has(targetId)) {
5458
5610
  const target = idMap.get(targetId);
5459
- if (target) return materialiseOneUse(node, target, idMap, animatedIds, genId3, defsCollector);
5611
+ if (target) return materialiseOneUse(node, target, idMap, animatedIds, genId3, defsCollector, rootViewport);
5460
5612
  }
5461
5613
  }
5462
5614
  if (!node.children) return node;
5463
5615
  let changed = false;
5464
5616
  const newChildren = node.children.map((ch) => {
5465
- const m = walkAndMaterialise2(ch, idMap, animatedIds, genId3, defsCollector);
5617
+ const m = walkAndMaterialise2(ch, idMap, animatedIds, genId3, defsCollector, rootViewport);
5466
5618
  if (m !== ch) changed = true;
5467
5619
  return m;
5468
5620
  });
@@ -5476,6 +5628,36 @@ var PixodeskAnimatorReact = (() => {
5476
5628
  if (engine === PxAnimatorEngine.webapi) {
5477
5629
  root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
5478
5630
  root = materialiseAnimatedUseInstances(root);
5631
+ root = pruneUnreferencedDefs(root);
5632
+ }
5633
+ return root;
5634
+ }
5635
+ function pruneUnreferencedDefs(root) {
5636
+ const stripHash3 = (h) => h.startsWith("#") ? h.slice(1) : h;
5637
+ const walk = (n, fn) => {
5638
+ var _a;
5639
+ fn(n);
5640
+ (_a = n.children) == null ? void 0 : _a.forEach((c) => walk(c, fn));
5641
+ };
5642
+ let changed = true;
5643
+ while (changed) {
5644
+ changed = false;
5645
+ const referenced = /* @__PURE__ */ new Set();
5646
+ walk(root, (n) => {
5647
+ if (n.type === "use" && typeof n.href === "string") referenced.add(stripHash3(n.href));
5648
+ });
5649
+ walk(root, (n) => {
5650
+ if (!n.children) return;
5651
+ let kept = n.children;
5652
+ if (n.type === "defs") {
5653
+ kept = kept.filter((c) => !((c.type === "g" || c.type === "symbol") && typeof c.id === "string" && !referenced.has(c.id)));
5654
+ }
5655
+ kept = kept.filter((c) => !(c.type === "defs" && (!c.children || c.children.length === 0)));
5656
+ if (kept.length !== n.children.length) {
5657
+ n.children = kept;
5658
+ changed = true;
5659
+ }
5660
+ });
5479
5661
  }
5480
5662
  return root;
5481
5663
  }
@@ -5506,6 +5688,13 @@ var PixodeskAnimatorReact = (() => {
5506
5688
  ]);
5507
5689
  var IMAGE_REF_ATTRS_LOWER = /* @__PURE__ */ new Set(["href", "xlink:href", "src"]);
5508
5690
  var DATA_RASTER_IMAGE_RE = /^data:image\/(?:png|jpe?g|gif|webp|bmp);base64,/i;
5691
+ var DATA_SVG_IMAGE_RE = /^data:image\/svg\+xml(?:;[^,]*)?,/i;
5692
+ var DATA_IMAGE_BASE64_PAYLOAD_RE = /^data:image\/[^;,]*;base64,([A-Za-z0-9+/]{8})/i;
5693
+ var BASE64_RASTER_MAGICS = ["iVBORw0K", "/9j/", "R0lGOD", "UklGR", "Qk"];
5694
+ function isContentSniffedRasterImage(str) {
5695
+ const m = DATA_IMAGE_BASE64_PAYLOAD_RE.exec(str);
5696
+ return !!m && BASE64_RASTER_MAGICS.some((magic) => m[1].startsWith(magic));
5697
+ }
5509
5698
  function isDangerousAttrName(nameLower) {
5510
5699
  if (nameLower.startsWith("on")) return true;
5511
5700
  return false;
@@ -5528,8 +5717,8 @@ var PixodeskAnimatorReact = (() => {
5528
5717
  const str = String(value);
5529
5718
  if (str.startsWith("#")) return value;
5530
5719
  if (/^url\(#[^)]+\)$/.test(str)) return value;
5531
- if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && DATA_RASTER_IMAGE_RE.test(str)) return value;
5532
- console.warn('Attribute "' + nameLower + '" blocked: must be #id, url(#id), or base64 raster data: URI, got:', value);
5720
+ if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && (DATA_RASTER_IMAGE_RE.test(str) || DATA_SVG_IMAGE_RE.test(str) || isContentSniffedRasterImage(str))) return value;
5721
+ console.warn('Attribute "' + nameLower + '" blocked: must be #id, url(#id), or data:image/\u2026 URI, got:', value);
5533
5722
  return void 0;
5534
5723
  }
5535
5724
  return value;