@pixodesk/svg-animator-core 1.0.22 → 1.0.24

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
@@ -153,8 +153,9 @@ var Union = class extends Base {
153
153
  }
154
154
  isValid(raw, ctx, path) {
155
155
  var _a;
156
- if (this.schemas.some((s) => s.isValid(raw))) return true;
157
- ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no union member matched for value " + ((_a = JSON.stringify(raw)) != null ? _a : "").slice(0, 60));
156
+ const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
157
+ if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
158
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no union member matched for value " + ((_a = JSON.stringify(raw)) != null ? _a : "").slice(0, 240));
158
159
  return false;
159
160
  }
160
161
  _canSanitize(raw) {
@@ -230,6 +231,7 @@ var Obj = class extends Base {
230
231
  if (ctx == null ? void 0 : ctx.strict) {
231
232
  for (const key of Object.keys(obj)) {
232
233
  if (key in this._shape) continue;
234
+ if (obj[key] === void 0) continue;
233
235
  p.push(key);
234
236
  ctx.errors.push(pathStr(p) + ": unexpected extra key");
235
237
  p.pop();
@@ -370,6 +372,23 @@ var Any = class extends Base {
370
372
  return true;
371
373
  }
372
374
  };
375
+ var Defined = class extends Base {
376
+ constructor() {
377
+ super(...arguments);
378
+ this._default = void 0;
379
+ }
380
+ sanitize(raw) {
381
+ return raw;
382
+ }
383
+ isValid(raw, ctx, path) {
384
+ if (raw !== void 0) return true;
385
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": required value is missing");
386
+ return false;
387
+ }
388
+ _canSanitize(raw) {
389
+ return raw !== void 0;
390
+ }
391
+ };
373
392
  var Lazy = class extends Base {
374
393
  constructor(fn, _default) {
375
394
  super();
@@ -481,24 +500,65 @@ var px = {
481
500
  record: (value) => new Rec(value),
482
501
  /** Passes anything through unchanged — always valid. */
483
502
  any: () => new Any(),
503
+ /** Anything EXCEPT `undefined` — an open type whose presence is required (V6). */
504
+ defined: () => new Defined(),
484
505
  /** Fixed-length tuple — validates element count and each position individually. */
485
506
  tuple: (schemas) => new Tuple(schemas),
486
507
  /** Defers schema creation — required for recursive types. Must supply a default value. */
487
508
  lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
488
509
  };
489
510
 
490
- // src/PxAnimatorTypes.ts
511
+ // src/PxAnimatorConstants.ts
491
512
  var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
492
513
  var PX_ANIM_ATTR_NAME = "_px_animator";
493
514
  var PxAnimatorMode = {
494
515
  auto: "auto",
495
- webapi: "webapi",
516
+ waapi: "waapi",
496
517
  frames: "frames"
497
518
  };
498
519
  var PxAnimatorEngine = {
499
- webapi: PxAnimatorMode.webapi,
520
+ waapi: PxAnimatorMode.waapi,
500
521
  frames: PxAnimatorMode.frames
501
522
  };
523
+ var PxLoopExtend = {
524
+ /** Segment from the START; the animation is extended BEFORE the first keyframe
525
+ * (intro loops that run before the main timeline begins). */
526
+ before: "before",
527
+ /** DEFAULT — segment from the END; extended AFTER the last keyframe (idle/outro
528
+ * loops that continue once the main timeline has finished). */
529
+ after: "after"
530
+ };
531
+ var PxMaskType = {
532
+ luminance: "luminance",
533
+ alpha: "alpha"
534
+ };
535
+ var PxUnits = {
536
+ userSpaceOnUse: "userSpaceOnUse",
537
+ objectBoundingBox: "objectBoundingBox"
538
+ };
539
+ var PxCloneType = {
540
+ content: "content"
541
+ };
542
+ var PxPathOverflow = {
543
+ clip: "clip",
544
+ extend: "extend"
545
+ };
546
+ var PxLengthAdjust = {
547
+ spacing: "spacing",
548
+ spacingAndGlyphs: "spacingAndGlyphs"
549
+ };
550
+ var PxTextPathMethod = {
551
+ align: "align",
552
+ stretch: "stretch"
553
+ };
554
+ var PxTextPathSpacing = {
555
+ auto: "auto",
556
+ exact: "exact"
557
+ };
558
+ var PxTrimSubPaths = {
559
+ separate: "separate",
560
+ combined: "combined"
561
+ };
502
562
  var TEXT_ATTR = "text";
503
563
  var TEXT_CONTENT_ATTR = "textContent";
504
564
  var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
@@ -507,9 +567,51 @@ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
507
567
  "animator",
508
568
  "meta",
509
569
  "animate",
570
+ "effects",
510
571
  TEXT_ATTR,
511
572
  TEXT_CONTENT_ATTR
512
573
  ]);
574
+ var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
575
+ var PxGradientUnits = {
576
+ userSpaceOnUse: "userSpaceOnUse",
577
+ objectBoundingBox: "objectBoundingBox"
578
+ };
579
+ var PxGradientSpreadMethod = {
580
+ pad: "pad",
581
+ reflect: "reflect",
582
+ repeat: "repeat"
583
+ };
584
+ var PxGradientType = {
585
+ linear: "linear",
586
+ radial: "radial"
587
+ };
588
+ function isPxElementFileFormat(fileJson) {
589
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
590
+ return false;
591
+ }
592
+ return fileJson["type"] === "svg";
593
+ }
594
+ function getAnimatorConfig(doc) {
595
+ var _a;
596
+ return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
597
+ }
598
+ function getDefs(doc) {
599
+ var _a;
600
+ if (!doc) return void 0;
601
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
602
+ }
603
+ function getBindings(doc) {
604
+ var _a;
605
+ if (!doc) return void 0;
606
+ const animateById = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animateById;
607
+ if (!animateById) return void 0;
608
+ return Object.entries(animateById).map(([id, anim]) => ({ id, animate: anim }));
609
+ }
610
+ function getChildren(doc) {
611
+ return doc == null ? void 0 : doc.children;
612
+ }
613
+
614
+ // src/PxAnimatorTypes.ts
513
615
  var PxEasingOrRefSchema = px.union([
514
616
  px.string(),
515
617
  px.tuple([px.number(), px.number(), px.number(), px.number()])
@@ -521,13 +623,15 @@ var PxKeyframeValueSchema = implementsInterface()(px.union([
521
623
  px.array(px.number()),
522
624
  px.lazy(() => PxTransformPartsSchema, {}),
523
625
  px.object({ path: px.string() }),
524
- px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] })
626
+ px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
627
+ // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
628
+ px.lazy(() => px.array(PxGradientStopSchema), [])
525
629
  ]));
526
630
  var PxKeyframeSchema = implementsInterface()(px.object({
527
631
  time: px.number().optional(),
528
632
  t: px.number().optional(),
529
- value: px.any().optional(),
530
- v: px.any().optional(),
633
+ value: PxKeyframeValueSchema.optional(),
634
+ v: PxKeyframeValueSchema.optional(),
531
635
  easing: PxEasingOrRefSchema.optional(),
532
636
  e: PxEasingOrRefSchema.optional(),
533
637
  tangentOut: px.tuple([px.number(), px.number()]).optional(),
@@ -541,16 +645,16 @@ var PxKeyframeSchema = implementsInterface()(px.object({
541
645
  }));
542
646
  var PxLoopSchema = implementsInterface()(px.object({
543
647
  segmentCount: px.number().optional(),
544
- before: px.boolean().optional(),
648
+ extend: px.enum([PxLoopExtend.before, PxLoopExtend.after]).optional(),
545
649
  alternate: px.boolean().optional()
546
650
  }));
547
651
  var PxPropertyAnimationSchema = implementsInterface()(px.object({
652
+ value: PxKeyframeValueSchema.optional(),
548
653
  keyframes: px.array(PxKeyframeSchema).optional(),
549
654
  kfs: px.array(PxKeyframeSchema).optional(),
550
655
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
551
656
  autoOrient: px.boolean().optional()
552
657
  }));
553
- var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
554
658
  var PxTransformPartsSchema = implementsInterface()(px.object({
555
659
  translate: px.tuple([px.number(), px.number()]).optional(),
556
660
  rotate: px.number().optional(),
@@ -560,6 +664,7 @@ var PxTransformPartsSchema = implementsInterface()(px.object({
560
664
  }));
561
665
  var PxTransformValueSchema = px.union([
562
666
  px.string(),
667
+ PxTransformPartsSchema,
563
668
  px.object({ value: PxTransformPartsSchema }),
564
669
  PxPropertyAnimationSchema
565
670
  ]);
@@ -594,9 +699,11 @@ var PxDefsSchema = implementsInterface()(px.object({
594
699
  glyphs: px.record(PxGlyphFontSchema).optional()
595
700
  }));
596
701
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
597
- mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.webapi, PxAnimatorMode.frames]).optional(),
702
+ mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.waapi, PxAnimatorMode.frames]).optional(),
598
703
  duration: px.number().optional(),
599
704
  delay: px.number().optional(),
705
+ // LAW (SCHEMA-DESIGN R3, S10): the ONE sanctioned string-in-number union
706
+ // (CSS animation-iteration-count familiarity) — do not add more mixed unions.
600
707
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
601
708
  fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
602
709
  direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
@@ -604,9 +711,8 @@ var PxAnimatorConfigSchema = implementsInterface()(px.object({
604
711
  trigger: PxTriggerSchema.optional(),
605
712
  resetOnFinish: px.boolean().optional(),
606
713
  definitions: PxDefsSchema.optional(),
607
- animate: px.record(PxElementAnimationSchema).optional(),
608
- timeline: px.string().optional(),
609
- debug: px.boolean().optional(),
714
+ animateById: px.record(PxElementAnimationSchema).optional(),
715
+ timelineSource: px.string().optional(),
610
716
  debugInstName: px.string().optional()
611
717
  }));
612
718
  var PxBindingSchema = implementsInterface()(px.object({
@@ -616,26 +722,29 @@ var PxBindingSchema = implementsInterface()(px.object({
616
722
  var PxAttrValueSchema = px.union([
617
723
  px.string(),
618
724
  px.number(),
619
- px.object({ value: px.any() }),
620
- PxPropertyAnimationSchema
725
+ px.array(px.number()),
726
+ // Structured static — `{value: …}` (read-accepted transitional spelling, S1).
727
+ // `defined`, not `any`: the KEY's presence is what identifies this branch (V6).
728
+ px.object({ value: px.defined() }),
729
+ // Bare transform parts record — the canonical static `transform` on the wire (T2).
730
+ PxTransformPartsSchema
621
731
  ]);
622
732
  var PxAnimatableNumberSchema = px.union([
623
733
  px.number(),
624
734
  px.object({ value: px.number() }),
625
- px.object({
626
- keyframes: px.array(PxKeyframeSchema),
627
- autoOrient: px.boolean().optional()
628
- })
735
+ PxPropertyAnimationSchema
629
736
  ]);
630
737
  var PxAnimatableVec2Schema = px.union([
631
738
  px.tuple([px.number(), px.number()]),
632
739
  px.object({ value: px.tuple([px.number(), px.number()]) }),
633
- px.object({
634
- keyframes: px.array(PxKeyframeSchema),
635
- autoOrient: px.boolean().optional()
636
- })
740
+ PxPropertyAnimationSchema
741
+ ]);
742
+ var PxAnimatableStringSchema = px.union([
743
+ px.string(),
744
+ px.object({ value: px.string() }),
745
+ PxPropertyAnimationSchema
637
746
  ]);
638
- var PxTransformationEffectSchema = implementsInterface()(px.object({
747
+ var PxTransformByEffectSchema = implementsInterface()(px.object({
639
748
  translate: PxAnimatableVec2Schema.optional(),
640
749
  rotate: PxAnimatableNumberSchema.optional(),
641
750
  scale: PxAnimatableVec2Schema.optional(),
@@ -643,51 +752,46 @@ var PxTransformationEffectSchema = implementsInterface()(px.object({
643
752
  origin: PxAnimatableVec2Schema.optional()
644
753
  }));
645
754
  var PxRepeaterEffectSchema = implementsInterface()(px.object({
755
+ // STATIC config, not a channel (V2/SCHEMA-DESIGN R5): the copy COUNT is read
756
+ // once at expansion time and never sampled — plain number, no `keyframes`.
646
757
  copies: px.number().optional(),
647
758
  translate: PxAnimatableVec2Schema.optional(),
648
759
  rotate: PxAnimatableNumberSchema.optional(),
760
+ skew: PxAnimatableNumberSchema.optional(),
649
761
  scale: PxAnimatableVec2Schema.optional(),
650
762
  origin: PxAnimatableVec2Schema.optional()
651
763
  }));
652
764
  var PxMaskedByEffectSchema = implementsInterface()(px.object({
653
- href: px.string().optional(),
654
- maskType: px.string().optional(),
655
- maskUnits: px.string().optional(),
656
- maskContentUnits: px.string().optional()
765
+ sourceId: px.string().optional(),
766
+ maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha]).optional(),
767
+ maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
768
+ maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
769
+ x: px.number().optional(),
770
+ y: px.number().optional(),
771
+ width: px.number().optional(),
772
+ height: px.number().optional()
657
773
  }));
658
774
  var PxClipPathEffectSchema = implementsInterface()(px.object({
659
- d: px.string().optional(),
775
+ d: PxAnimatableStringSchema.optional(),
660
776
  animate: PxPropertyAnimationSchema.optional()
661
777
  }));
662
778
  var PxTrimPathEffectSchema = implementsInterface()(px.object({
663
779
  offset: PxAnimatableNumberSchema.optional(),
664
780
  range: PxAnimatableVec2Schema.optional(),
665
- trimAllAsOne: px.boolean().optional()
781
+ subPaths: px.enum([PxTrimSubPaths.separate, PxTrimSubPaths.combined]).optional()
666
782
  }));
667
783
  var PxRetimeEffectSchema = implementsInterface()(px.object({
668
- baseId: px.string().optional(),
784
+ sourceId: px.string().optional(),
669
785
  start: px.number().optional(),
670
786
  stretch: px.number().optional(),
671
787
  timeCrop: px.tuple([px.number(), px.number()]).optional()
672
788
  }));
673
789
  var PxCloneEffectSchema = implementsInterface()(px.object({
674
- type: px.string().optional(),
675
- baseId: px.string().optional(),
790
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
791
+ type: px.enum([PxCloneType.content]).optional(),
792
+ sourceId: px.string().optional(),
676
793
  retime: PxRetimeEffectSchema.optional()
677
794
  }));
678
- var PxGradientUnits = {
679
- userSpaceOnUse: "userSpaceOnUse",
680
- objectBoundingBox: "objectBoundingBox"
681
- };
682
- var PxGradientSpreadMethod = {
683
- pad: "pad",
684
- reflect: "reflect",
685
- repeat: "repeat"
686
- };
687
- var PxGradientType = {
688
- linear: "linear",
689
- radial: "radial"
690
- };
691
795
  var PxGradientStopSchema = implementsInterface()(px.object({
692
796
  offset: px.number(),
693
797
  color: px.string()
@@ -695,39 +799,28 @@ var PxGradientStopSchema = implementsInterface()(px.object({
695
799
  var PxAnimatableGradientStopsSchema = px.union([
696
800
  px.array(PxGradientStopSchema),
697
801
  px.object({ value: px.array(PxGradientStopSchema) }),
698
- px.object({ keyframes: px.array(PxKeyframeSchema) })
802
+ PxPropertyAnimationSchema
699
803
  ]);
700
- var PxGradientGeometryAnimationSchema = implementsInterface()(px.object({
701
- gradientX1: PxPropertyAnimationSchema.optional(),
702
- gradientY1: PxPropertyAnimationSchema.optional(),
703
- gradientX2: PxPropertyAnimationSchema.optional(),
704
- gradientY2: PxPropertyAnimationSchema.optional(),
705
- gradientCx: PxPropertyAnimationSchema.optional(),
706
- gradientCy: PxPropertyAnimationSchema.optional(),
707
- gradientFx: PxPropertyAnimationSchema.optional(),
708
- gradientFy: PxPropertyAnimationSchema.optional(),
709
- gradientR: PxPropertyAnimationSchema.optional()
710
- }));
711
804
  var PxFillGradientEffectSchema = implementsInterface()(px.object({
805
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
712
806
  type: px.enum([PxGradientType.linear, PxGradientType.radial]),
713
- p1: px.tuple([px.number(), px.number()]).optional(),
714
- p2: px.tuple([px.number(), px.number()]).optional(),
715
- c: px.tuple([px.number(), px.number()]).optional(),
716
- r: px.number().optional(),
717
- fp: px.tuple([px.number(), px.number()]).optional(),
807
+ p1: PxAnimatableVec2Schema.optional(),
808
+ p2: PxAnimatableVec2Schema.optional(),
809
+ c: PxAnimatableVec2Schema.optional(),
810
+ r: PxAnimatableNumberSchema.optional(),
811
+ fp: PxAnimatableVec2Schema.optional(),
718
812
  stops: PxAnimatableGradientStopsSchema.optional(),
719
- gradientUnits: px.string().optional(),
720
- spreadMethod: px.string().optional(),
721
- gradientTransform: px.string().optional(),
722
- animate: PxGradientGeometryAnimationSchema.optional()
813
+ gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
814
+ spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
815
+ gradientTransform: px.string().optional()
723
816
  }));
724
817
  var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
725
818
  var PxTextPathEffectSchema = implementsInterface()(px.object({
726
819
  path: px.string(),
727
- pathOverflow: px.string().optional(),
728
- lengthAdjust: px.string().optional(),
729
- method: px.string().optional(),
730
- spacing: px.string().optional(),
820
+ pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
821
+ lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
822
+ method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
823
+ spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
731
824
  startOffset: PxAnimatableNumberSchema.optional(),
732
825
  textLength: PxAnimatableNumberSchema.optional()
733
826
  }));
@@ -735,13 +828,12 @@ var PxTextEffectSchema = implementsInterface()(px.object({
735
828
  useGlyphs: px.boolean().optional()
736
829
  }));
737
830
  var PxEffectsSchema = implementsInterface()(px.object({
738
- transformation: PxTransformationEffectSchema.optional(),
831
+ transformBy: PxTransformByEffectSchema.optional(),
739
832
  repeater: PxRepeaterEffectSchema.optional(),
740
833
  maskedBy: PxMaskedByEffectSchema.optional(),
741
834
  clipPath: PxClipPathEffectSchema.optional(),
742
835
  trimPath: PxTrimPathEffectSchema.optional(),
743
836
  clone: PxCloneEffectSchema.optional(),
744
- isCombinedShape: px.boolean().optional(),
745
837
  fillGradient: PxFillGradientEffectSchema.optional(),
746
838
  strokeGradient: PxStrokeGradientEffectSchema.optional(),
747
839
  textPath: PxTextPathEffectSchema.optional(),
@@ -765,6 +857,14 @@ function validateNodeEffects(root, opts) {
765
857
  return warnings;
766
858
  }
767
859
  var PxNodeBase = px.openObject({
860
+ // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
861
+ // kind of thing is this", discriminated by its CARRIER — here the node TAG
862
+ // (`rect`, `text`), and inside a sub-object that object's kind (`clone.type`,
863
+ // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
864
+ // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
865
+ // would add words that all mean "type" and still need the carrier to read.
866
+ // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
867
+ // (issues V3), never of distinct key names.
768
868
  type: px.string(),
769
869
  id: px.string().optional(),
770
870
  meta: px.any().optional(),
@@ -774,7 +874,7 @@ var PxNodeBase = px.openObject({
774
874
  effects: PxEffectsSchema.optional(),
775
875
  // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
776
876
  // string ref / array of refs / inline definition / mixed array; mirrors
777
- // `animator.animate` map values and what `processNode` resolves at runtime.
877
+ // `animator.animateById` map values and what `processNode` resolves at runtime.
778
878
  animate: PxElementAnimationSchema.optional(),
779
879
  style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
780
880
  }, PxAttrValueSchema);
@@ -798,35 +898,10 @@ var PxBezierPathSchema = implementsInterface()(px.object({
798
898
  o: px.array(px.array(px.number())).optional(),
799
899
  c: px.boolean().optional()
800
900
  }));
801
- function isPxElementFileFormat(fileJson) {
802
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
803
- return false;
804
- }
805
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
806
- }
807
901
  function isPxElementFileFormatDeep(fileJson) {
808
902
  const valid = PxAnimatedSvgDocumentSchema.isValid(fileJson);
809
903
  return { valid, errors: valid ? [] : ["Document failed schema validation"] };
810
904
  }
811
- function getAnimatorConfig(doc) {
812
- var _a, _b;
813
- return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
814
- }
815
- function getDefs(doc) {
816
- var _a;
817
- if (!doc) return void 0;
818
- return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
819
- }
820
- function getBindings(doc) {
821
- var _a;
822
- if (!doc) return void 0;
823
- const animate = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animate;
824
- if (!animate) return void 0;
825
- return Object.entries(animate).map(([id, anim]) => ({ id, animate: anim }));
826
- }
827
- function getChildren(doc) {
828
- return doc == null ? void 0 : doc.children;
829
- }
830
905
 
831
906
  // src/PxAnimatorUtil.ts
832
907
  function bezierToSvgPath(path, forceCurves = false) {
@@ -1281,7 +1356,7 @@ function generateNewIds(doc) {
1281
1356
  "flood-color",
1282
1357
  "lighting-color"
1283
1358
  ]);
1284
- const directIdRefAttrs = /* @__PURE__ */ new Set(["baseId", "targetId", "boundElementId"]);
1359
+ const directIdRefAttrs = /* @__PURE__ */ new Set(["sourceId", "targetId", "boundElementId"]);
1285
1360
  function collectIds(node) {
1286
1361
  if (!node || typeof node !== "object") return;
1287
1362
  if (node.id && typeof node.id === "string") {
@@ -1319,9 +1394,10 @@ function generateNewIds(doc) {
1319
1394
  } else if (urlRefAttrs.has(key)) {
1320
1395
  node[key] = replaceUrlRefs(value, idMap);
1321
1396
  } else if (directIdRefAttrs.has(key)) {
1322
- const newId = idMap.get(value);
1397
+ const hasHash = value.startsWith("#");
1398
+ const newId = idMap.get(hasHash ? value.slice(1) : value);
1323
1399
  if (newId) {
1324
- node[key] = newId;
1400
+ node[key] = hasHash ? "#" + newId : newId;
1325
1401
  }
1326
1402
  } else if (value.includes("url(#")) {
1327
1403
  node[key] = replaceUrlRefs(value, idMap);
@@ -1339,14 +1415,14 @@ function generateNewIds(doc) {
1339
1415
  }
1340
1416
  collectIds(cloned);
1341
1417
  updateRefs(cloned);
1342
- const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animate;
1418
+ const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animateById;
1343
1419
  if (docAnimate && typeof docAnimate === "object") {
1344
1420
  const updatedAnimate = {};
1345
1421
  for (const [id, anim] of Object.entries(docAnimate)) {
1346
1422
  const newId = (_b = idMap.get(id)) != null ? _b : id;
1347
1423
  updatedAnimate[newId] = anim;
1348
1424
  }
1349
- cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animate: updatedAnimate });
1425
+ cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animateById: updatedAnimate });
1350
1426
  }
1351
1427
  return cloned;
1352
1428
  }
@@ -1437,8 +1513,9 @@ function getNormalizedProps(props) {
1437
1513
  let value = props[rawKey];
1438
1514
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
1439
1515
  propsCopy[key] = toRGBA(value);
1440
- } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
1441
- propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
1516
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes && !value.kfs) {
1517
+ const parts = value.value && typeof value.value === "object" ? value.value : value;
1518
+ propsCopy["transform"] = composeTransformParts(parts, { withUnits: false });
1442
1519
  } else if (TRANSFORM_FN_NAMES.has(key)) {
1443
1520
  if (Array.isArray(value)) {
1444
1521
  if (key === "translate") value = value.map((v) => v + "px");
@@ -1446,6 +1523,8 @@ function getNormalizedProps(props) {
1446
1523
  }
1447
1524
  if (key === "rotate") value = value + "deg";
1448
1525
  propsCopy["transform"] = key + "(" + value + ")";
1526
+ } else if (Array.isArray(value)) {
1527
+ propsCopy[key] = value.join(",");
1449
1528
  } else if (value !== void 0 && value !== null) {
1450
1529
  propsCopy[key] = String(value);
1451
1530
  }
@@ -1847,6 +1926,16 @@ function walkAndMaterialise(node, opts) {
1847
1926
  }
1848
1927
 
1849
1928
  // src/PxDefinitions.ts
1929
+ var LOOP_JUMP_SHIFT_MS = 10;
1930
+ function deepEqualValue(a, b) {
1931
+ if (a === b) return true;
1932
+ if (typeof a !== typeof b || a === null || b === null || typeof a !== "object") return false;
1933
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
1934
+ const ka = Object.keys(a);
1935
+ const kb = Object.keys(b);
1936
+ if (ka.length !== kb.length) return false;
1937
+ return ka.every((k) => deepEqualValue(a[k], b[k]));
1938
+ }
1850
1939
  function parsePathCommands(d) {
1851
1940
  const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
1852
1941
  const commands = [];
@@ -2049,7 +2138,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2049
2138
  const totalIntervals = keyframes.length - 1;
2050
2139
  const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2051
2140
  let segKfs;
2052
- if (loop.before) {
2141
+ if (loop.extend === PxLoopExtend.before) {
2053
2142
  segKfs = keyframes.slice(0, segCount + 1);
2054
2143
  } else {
2055
2144
  segKfs = keyframes.slice(totalIntervals - segCount);
@@ -2057,7 +2146,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2057
2146
  const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2058
2147
  const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2059
2148
  let fillStart, fillEnd;
2060
- if (loop.before) {
2149
+ if (loop.extend === PxLoopExtend.before) {
2061
2150
  fillStart = 0;
2062
2151
  fillEnd = firstT;
2063
2152
  } else {
@@ -2084,7 +2173,10 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2084
2173
  const remainder = fillDuration - fullReps * segDuration;
2085
2174
  const partialFraction = remainder / segDuration;
2086
2175
  const looped = [];
2176
+ const separateBoundary = loop.extend !== PxLoopExtend.before;
2177
+ const originalTerminalKf = keyframes[keyframes.length - 1];
2087
2178
  function appendRep(repStart, isReversed, partial) {
2179
+ var _a2;
2088
2180
  let entries;
2089
2181
  if (isReversed) {
2090
2182
  entries = [];
@@ -2120,8 +2212,17 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2120
2212
  looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2121
2213
  return;
2122
2214
  }
2215
+ const prevKf = looped.length > 0 ? looped[looped.length - 1] : originalTerminalKf;
2216
+ const isBoundary = separateBoundary && i === 0 && prevKf !== void 0 && Math.abs(((_a2 = prevKf.t) != null ? _a2 : 0) - (repStart + entry.relT * segDuration)) < 1e-9;
2217
+ if (isBoundary) {
2218
+ if (deepEqualValue(prevKf.v, entry.v)) continue;
2219
+ if (looped.length > 0) {
2220
+ delete prevKf.tangentIn;
2221
+ delete prevKf.tangentOut;
2222
+ }
2223
+ }
2123
2224
  const pushed = {
2124
- t: repStart + entry.relT * segDuration,
2225
+ t: repStart + entry.relT * segDuration + (isBoundary ? LOOP_JUMP_SHIFT_MS : 0),
2125
2226
  v: entry.v,
2126
2227
  e: i < entries.length - 1 ? entry.e : void 0
2127
2228
  };
@@ -2169,7 +2270,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2169
2270
  looped.push(pushed);
2170
2271
  }
2171
2272
  }
2172
- if (loop.before) {
2273
+ if (loop.extend === PxLoopExtend.before) {
2173
2274
  if (partialFraction > 1e-9) {
2174
2275
  const isReversed = !!loop.alternate && fullReps % 2 === 0;
2175
2276
  appendRepTail(fillStart, isReversed, partialFraction);
@@ -2192,7 +2293,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2192
2293
  appendRep(repStart, isReversed, partialFraction);
2193
2294
  }
2194
2295
  }
2195
- if (loop.before) {
2296
+ if (loop.extend === PxLoopExtend.before) {
2196
2297
  return [...looped, ...keyframes];
2197
2298
  } else {
2198
2299
  return [...keyframes, ...looped];
@@ -2308,7 +2409,7 @@ var _elementIdCounter = 0;
2308
2409
  function generateElementId() {
2309
2410
  return "_px_el_" + ++_elementIdCounter;
2310
2411
  }
2311
- function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.webapi) {
2412
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.waapi) {
2312
2413
  const normalized = {};
2313
2414
  for (const [propName, propAnim] of Object.entries(animDef)) {
2314
2415
  const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
@@ -2316,12 +2417,12 @@ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimat
2316
2417
  const out = { kfs: normalizedKfs };
2317
2418
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2318
2419
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2319
- normalized[propName] = engine === PxAnimatorEngine.webapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2420
+ normalized[propName] = engine === PxAnimatorEngine.waapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2320
2421
  }
2321
2422
  }
2322
2423
  return normalized;
2323
2424
  }
2324
- function getNormalisedBindings(doc, engine = PxAnimatorEngine.webapi) {
2425
+ function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2325
2426
  const animatorConfig = getAnimatorConfig(doc) || {};
2326
2427
  const defs = getDefs(doc);
2327
2428
  const duration = animatorConfig.duration || 1e3;
@@ -2732,20 +2833,41 @@ function partsRecord(part, value, origin) {
2732
2833
  return rec;
2733
2834
  }
2734
2835
  function readAnimatable(raw) {
2735
- var _a, _b;
2836
+ var _a, _b, _c;
2736
2837
  if (raw === void 0) return { kind: "absent" /* Absent */ };
2737
2838
  if (Array.isArray(raw)) return { kind: "static" /* Static */, value: raw };
2738
2839
  if (typeof raw === "object") {
2739
2840
  const obj = raw;
2740
2841
  const kfs = (_a = obj.keyframes) != null ? _a : obj.kfs;
2741
2842
  if (kfs) {
2742
- return { kind: "animated" /* Animated */, keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2843
+ const out = { kind: "animated" /* Animated */, keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2844
+ const base = (_b = obj.value) != null ? _b : obj.v;
2845
+ if (base !== void 0 && out.kind === "animated" /* Animated */) out.base = base;
2846
+ return out;
2743
2847
  }
2744
- const staticValue = (_b = obj.value) != null ? _b : obj.v;
2848
+ const staticValue = (_c = obj.value) != null ? _c : obj.v;
2745
2849
  if (staticValue !== void 0) return { kind: "static" /* Static */, value: staticValue };
2746
2850
  }
2747
2851
  return { kind: "static" /* Static */, value: raw };
2748
2852
  }
2853
+ function writeAnimatableChannel(node, attrName, read, opts) {
2854
+ var _a, _b, _c, _d;
2855
+ const toOut = (v) => (opts == null ? void 0 : opts.asString) && v !== void 0 && v !== null ? String(v) : v;
2856
+ if (read.kind === "absent" /* Absent */) return;
2857
+ if (read.kind === "static" /* Static */) {
2858
+ node[attrName] = toOut(read.value);
2859
+ return;
2860
+ }
2861
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
2862
+ const animate = __spreadValues({}, prevAnimate || {});
2863
+ const block = { keyframes: read.keyframes };
2864
+ if (read.loop !== void 0) block.loop = read.loop;
2865
+ if (read.autoOrient !== void 0) block.autoOrient = read.autoOrient;
2866
+ animate[attrName] = block;
2867
+ node.animate = animate;
2868
+ const baseline = (_d = (_b = read.base) != null ? _b : (_a = read.keyframes[0]) == null ? void 0 : _a.value) != null ? _d : (_c = read.keyframes[0]) == null ? void 0 : _c.v;
2869
+ if (baseline !== void 0) node[attrName] = toOut(baseline);
2870
+ }
2749
2871
  function normaliseKeyframe(kf) {
2750
2872
  if (!kf || typeof kf !== "object") return kf;
2751
2873
  const k = kf;
@@ -2765,7 +2887,7 @@ function readStaticOrigin(raw, ctx) {
2765
2887
  const o = readAnimatable(raw);
2766
2888
  if (o.kind === "absent" /* Absent */) return void 0;
2767
2889
  if (o.kind === "static" /* Static */) return o.value;
2768
- ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
2890
+ ctx.warnings.push("transformBy.origin: animated origin approximated by its first keyframe");
2769
2891
  return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
2770
2892
  }
2771
2893
  function keyframeWith(kf, value) {
@@ -2778,7 +2900,7 @@ function keyframeWith(kf, value) {
2778
2900
  }
2779
2901
 
2780
2902
  // src/effects/transformationEffect.ts
2781
- function applyTransformationEffect(node, fx, ctx) {
2903
+ function applyTransformByEffect(node, fx, ctx) {
2782
2904
  if (!fx) return node;
2783
2905
  delete node.transform;
2784
2906
  let n = node;
@@ -2814,6 +2936,10 @@ function applyTransformationEffect(node, fx, ctx) {
2814
2936
  } else {
2815
2937
  n = wrapTransformPart(n, "translate" /* Translate */, fx.translate, ctx);
2816
2938
  }
2939
+ if (n !== node && node.id) {
2940
+ n.id = node.id;
2941
+ delete node.id;
2942
+ }
2817
2943
  return n;
2818
2944
  }
2819
2945
  function translateHasAutoOrient(translate) {
@@ -2823,8 +2949,6 @@ function translateHasAutoOrient(translate) {
2823
2949
  return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
2824
2950
  }
2825
2951
  function normalizeScale(raw) {
2826
- if (raw === void 0) return void 0;
2827
- if (Array.isArray(raw)) return [raw[0] / 100, raw[1] / 100];
2828
2952
  return raw;
2829
2953
  }
2830
2954
  function wrapTransformPart(inner, part, raw, ctx) {
@@ -2866,35 +2990,57 @@ function wrapOrigin(inner, raw, invert) {
2866
2990
  return inner;
2867
2991
  }
2868
2992
 
2993
+ // src/effects/util.ts
2994
+ function genId(ctx, prefix) {
2995
+ return "_lw_" + prefix + "_" + ctx.nextId++;
2996
+ }
2997
+ function stripHash2(href) {
2998
+ return typeof href === "string" ? href.replace(/^#/, "") : void 0;
2999
+ }
3000
+ function indexById(node, map) {
3001
+ var _a;
3002
+ if (typeof node.id === "string") map.set(node.id, node);
3003
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3004
+ }
3005
+ function spliceDefs(root, defs) {
3006
+ if (!defs.length) return;
3007
+ const existing = root.children || (root.children = []);
3008
+ existing.unshift({ type: "defs", children: defs });
3009
+ }
3010
+ var clone = deepClonePxNode;
3011
+ function regenerateIdsInClone(root, ctx) {
3012
+ return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3013
+ }
3014
+
2869
3015
  // src/effects/contentRefSplit.ts
2870
3016
  function identifyContentRefTargets(node, ctx, allocator) {
2871
3017
  var _a, _b, _c;
2872
3018
  if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.type) === "content") {
2873
- const baseId = node.effects.clone.baseId;
2874
- if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
2875
- ctx.contentRefInnerIds.set(baseId, allocator(baseId));
3019
+ const sourceId = stripHash2(node.effects.clone.sourceId);
3020
+ if (typeof sourceId === "string" && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {
3021
+ ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));
2876
3022
  }
2877
3023
  }
2878
3024
  (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
2879
3025
  }
2880
- function splitForContentRef(node, transformation, originalId, innerId, ctx) {
2881
- const outerBody = liftBodyTranslate(node, transformation);
3026
+ function splitForContentRef(node, transformBy, originalId, innerId, ctx) {
3027
+ const outerBody = liftBodyTranslate(node, transformBy);
2882
3028
  if (typeof node.id === "string") delete node.id;
2883
- const { outer: outerTr, inner: innerTr } = splitTransformationEffect(transformation);
3029
+ const { outer: outerTr, inner: innerTr } = splitTransformByEffect(transformBy);
2884
3030
  let innerNode = node;
2885
- innerNode = applyTransformationEffect(innerNode, innerTr, ctx);
3031
+ innerNode = applyTransformByEffect(innerNode, innerTr, ctx);
2886
3032
  const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
2887
3033
  let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
2888
3034
  if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
2889
3035
  if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
2890
3036
  if (outerTr) {
2891
3037
  delete outerWrapper.id;
2892
- outerWrapper = applyTransformationEffect(outerWrapper, outerTr, ctx);
3038
+ outerWrapper = applyTransformByEffect(outerWrapper, outerTr, ctx);
2893
3039
  outerWrapper.id = originalId;
2894
3040
  }
2895
3041
  return outerWrapper;
2896
3042
  }
2897
- function liftBodyTranslate(node, transformation) {
3043
+ function liftBodyTranslate(node, transformBy) {
2898
3044
  var _a, _b, _c;
2899
3045
  const out = {};
2900
3046
  let didLiftAnimate = false;
@@ -2948,7 +3094,7 @@ function liftBodyTranslate(node, transformation) {
2948
3094
  didLiftAnimate = true;
2949
3095
  }
2950
3096
  }
2951
- const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
3097
+ const transformationHasTranslate = (transformBy == null ? void 0 : transformBy.translate) !== void 0;
2952
3098
  const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
2953
3099
  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);
2954
3100
  if (typeof node.transform === "string") {
@@ -2967,6 +3113,24 @@ function liftBodyTranslate(node, transformation) {
2967
3113
  if (split.rest) node.transform = split.rest;
2968
3114
  else delete node.transform;
2969
3115
  }
3116
+ } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes && !node.transform.kfs) {
3117
+ const wrapped = node.transform.value;
3118
+ const isWrapped = !!(wrapped && typeof wrapped === "object");
3119
+ const value = isWrapped ? wrapped : node.transform;
3120
+ const rewrap = (rec) => isWrapped ? { value: rec } : rec;
3121
+ if (Array.isArray(value.translate)) {
3122
+ const rest = __spreadValues({}, value);
3123
+ delete rest.translate;
3124
+ const hasRest = Object.keys(rest).length > 0;
3125
+ if (stripBodyTranslateOnly) {
3126
+ if (hasRest) node.transform = rewrap(rest);
3127
+ else delete node.transform;
3128
+ } else {
3129
+ out.transform = rewrap({ translate: value.translate });
3130
+ if (hasRest) node.transform = rewrap(rest);
3131
+ else delete node.transform;
3132
+ }
3133
+ }
2970
3134
  }
2971
3135
  return out;
2972
3136
  }
@@ -3036,7 +3200,7 @@ function parseTranslateArgs(translateOp) {
3036
3200
  const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
3037
3201
  return [nums[0] || 0, nums[1] || 0];
3038
3202
  }
3039
- function splitTransformationEffect(fx) {
3203
+ function splitTransformByEffect(fx) {
3040
3204
  if (!fx) return {};
3041
3205
  const originOnOuter = needsOriginOnOuter(fx.translate);
3042
3206
  const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
@@ -3063,28 +3227,6 @@ function needsOriginOnOuter(translateAnim) {
3063
3227
  return false;
3064
3228
  }
3065
3229
 
3066
- // src/effects/util.ts
3067
- function genId(ctx, prefix) {
3068
- return "_lw_" + prefix + "_" + ctx.nextId++;
3069
- }
3070
- function stripHash2(href) {
3071
- return typeof href === "string" ? href.replace(/^#/, "") : void 0;
3072
- }
3073
- function indexById(node, map) {
3074
- var _a;
3075
- if (typeof node.id === "string") map.set(node.id, node);
3076
- (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3077
- }
3078
- function spliceDefs(root, defs) {
3079
- if (!defs.length) return;
3080
- const existing = root.children || (root.children = []);
3081
- existing.unshift({ type: "defs", children: defs });
3082
- }
3083
- var clone = deepClonePxNode;
3084
- function regenerateIdsInClone(root, ctx) {
3085
- return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3086
- }
3087
-
3088
3230
  // src/effects/gradientEffect.ts
3089
3231
  function applyFillGradientEffect(node, fx, ctx) {
3090
3232
  return applyGradient(node, fx, ctx, "fill");
@@ -3106,62 +3248,63 @@ function synthesiseGradientDef(fx, id, ctx) {
3106
3248
  id
3107
3249
  };
3108
3250
  if (fx.type === PxGradientType.linear) {
3109
- if (fx.p1) {
3110
- out.x1 = String(fx.p1[0]);
3111
- out.y1 = String(fx.p1[1]);
3112
- }
3113
- if (fx.p2) {
3114
- out.x2 = String(fx.p2[0]);
3115
- out.y2 = String(fx.p2[1]);
3116
- }
3251
+ applyGeomVec(out, "x1", "y1", fx.p1);
3252
+ applyGeomVec(out, "x2", "y2", fx.p2);
3117
3253
  } else {
3118
- if (fx.c) {
3119
- out.cx = String(fx.c[0]);
3120
- out.cy = String(fx.c[1]);
3121
- }
3122
- if (fx.r !== void 0) out.r = String(fx.r);
3123
- if (fx.fp) {
3124
- out.fx = String(fx.fp[0]);
3125
- out.fy = String(fx.fp[1]);
3126
- }
3254
+ applyGeomVec(out, "cx", "cy", fx.c);
3255
+ applyGeomNumber(out, "r", fx.r);
3256
+ applyGeomVec(out, "fx", "fy", fx.fp);
3127
3257
  }
3128
3258
  if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
3129
3259
  if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
3130
3260
  if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;
3131
- const animate = fx.animate;
3132
- if (animate) {
3133
- const mapped = {};
3134
- for (const wireKey of Object.keys(animate)) {
3135
- const attr = GRADIENT_ANIM_CHANNEL_TO_ATTR[wireKey];
3136
- if (attr) mapped[attr] = animate[wireKey];
3137
- }
3138
- if (Object.keys(mapped).length) out.animate = mapped;
3139
- }
3140
3261
  out.children = buildStopChildren(fx.stops, ctx);
3141
3262
  return out;
3142
3263
  }
3143
- var GRADIENT_ANIM_CHANNEL_TO_ATTR = {
3144
- gradientX1: "x1",
3145
- gradientY1: "y1",
3146
- gradientX2: "x2",
3147
- gradientY2: "y2",
3148
- gradientCx: "cx",
3149
- gradientCy: "cy",
3150
- gradientFx: "fx",
3151
- gradientFy: "fy",
3152
- gradientR: "r"
3153
- };
3264
+ function applyGeomVec(out, xAttr, yAttr, raw) {
3265
+ var _a, _b, _c;
3266
+ const read = readAnimatable(raw);
3267
+ if (read.kind === "absent" /* Absent */) return;
3268
+ if (read.kind === "static" /* Static */) {
3269
+ out[xAttr] = String(read.value[0]);
3270
+ out[yAttr] = String(read.value[1]);
3271
+ return;
3272
+ }
3273
+ const axisChannel = (idx) => {
3274
+ const block = {
3275
+ keyframes: read.keyframes.map((kf) => {
3276
+ const axisKf = { time: kf.time, value: Array.isArray(kf.value) ? kf.value[idx] : void 0 };
3277
+ if (kf.easing !== void 0) axisKf.easing = kf.easing;
3278
+ return axisKf;
3279
+ })
3280
+ };
3281
+ if (read.loop !== void 0) block.loop = read.loop;
3282
+ return block;
3283
+ };
3284
+ const animate = (_a = out.animate) != null ? _a : {};
3285
+ animate[xAttr] = axisChannel(0);
3286
+ animate[yAttr] = axisChannel(1);
3287
+ out.animate = animate;
3288
+ const baseline = (_c = read.base) != null ? _c : (_b = read.keyframes[0]) == null ? void 0 : _b.value;
3289
+ if (Array.isArray(baseline)) {
3290
+ out[xAttr] = String(baseline[0]);
3291
+ out[yAttr] = String(baseline[1]);
3292
+ }
3293
+ }
3294
+ function applyGeomNumber(out, attrName, raw) {
3295
+ const read = readAnimatable(raw);
3296
+ if (read.kind === "absent" /* Absent */) return;
3297
+ writeAnimatableChannel(out, attrName, read, { asString: true });
3298
+ }
3154
3299
  function buildStopChildren(stops, ctx) {
3155
3300
  var _a, _b, _c, _d;
3156
3301
  if (!stops) return [];
3157
- if (Array.isArray(stops)) return stops.map(staticStopNode);
3158
- if (typeof stops === "object" && Array.isArray(stops.value)) {
3159
- return stops.value.map(staticStopNode);
3160
- }
3161
- const animBlock = stops;
3162
- const kfs = animBlock.keyframes;
3163
- if (!Array.isArray(kfs) || !kfs.length) return [];
3164
- const loopFromSource = animBlock.loop;
3302
+ const read = readAnimatable(stops);
3303
+ if (read.kind === "absent" /* Absent */) return [];
3304
+ if (read.kind === "static" /* Static */) return Array.isArray(read.value) ? read.value.map(staticStopNode) : [];
3305
+ const kfs = read.keyframes;
3306
+ if (!kfs.length) return [];
3307
+ const loopFromSource = read.loop;
3165
3308
  let stopCount = 0;
3166
3309
  for (const kf of kfs) {
3167
3310
  const v = (_a = kf.value) != null ? _a : kf.v;
@@ -3238,38 +3381,62 @@ function formatOffset(o) {
3238
3381
 
3239
3382
  // src/effects/clipPathEffect.ts
3240
3383
  function applyClipPathEffect(node, fx, ctx) {
3384
+ var _a, _b;
3241
3385
  if (!fx || !fx.d && !fx.animate) return node;
3242
3386
  const clipId = genId(ctx, "clip");
3243
- const pathChild = { type: "path", d: fx.d };
3244
- if (fx.animate) pathChild.animate = { d: fx.animate };
3387
+ const pathChild = { type: "path" };
3388
+ const read = readAnimatable(fx.d);
3389
+ if (read.kind !== "absent" /* Absent */) {
3390
+ if (read.kind === "static" /* Static */) {
3391
+ pathChild.d = pathString(read.value);
3392
+ } else {
3393
+ writeAnimatableChannel(pathChild, "d", read);
3394
+ if (pathChild.d !== void 0) pathChild.d = pathString(pathChild.d);
3395
+ }
3396
+ }
3397
+ if (fx.animate && !((_a = pathChild.animate) == null ? void 0 : _a.d)) {
3398
+ const animate = (_b = pathChild.animate) != null ? _b : {};
3399
+ animate.d = fx.animate;
3400
+ pathChild.animate = animate;
3401
+ }
3245
3402
  ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
3246
3403
  node.clipPath = "url(#" + clipId + ")";
3247
3404
  return node;
3248
3405
  }
3406
+ function pathString(v) {
3407
+ if (typeof v === "string") return v;
3408
+ if (v && typeof v === "object" && typeof v.path === "string") return v.path;
3409
+ return void 0;
3410
+ }
3249
3411
 
3250
3412
  // src/effects/maskedByEffect.ts
3251
- function applyMaskedByEffect(node, fx, transformation, ctx) {
3413
+ function applyMaskedByEffect(node, fx, transformBy, ctx) {
3252
3414
  if (!fx) return node;
3253
- if (!fx.href) {
3254
- ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
3415
+ const sourceId = stripHash2(fx.sourceId);
3416
+ if (!sourceId) {
3417
+ ctx.errors.push("maskedBy.sourceId missing \u2014 cannot build mask");
3255
3418
  return node;
3256
3419
  }
3257
3420
  const maskId = genId(ctx, "mask");
3258
- let content = { type: "use", href: "#" + fx.href };
3259
- if (transformation) {
3260
- content = wrapInverseTransform(content, transformation, ctx);
3421
+ let content = { type: "use", href: "#" + sourceId };
3422
+ if (transformBy) {
3423
+ content = wrapInverseTransform(content, transformBy, ctx);
3261
3424
  } else if (hasAnimateTransform(node)) {
3262
3425
  content = wrapInverseAnimatedBodyTransform(content, node, ctx);
3263
3426
  } else {
3264
3427
  const bodyStatic = readTransformationFromBody(node);
3265
3428
  if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);
3266
3429
  }
3267
- const includeTargetOwn = transformation === void 0 && !nodeHasBodyTransform(node);
3268
- content = wrapAncestorChainCompensation(content, node, fx.href, ctx, includeTargetOwn);
3430
+ const includeTargetOwn = transformBy === void 0 && !nodeHasBodyTransform(node);
3431
+ content = wrapAncestorChainCompensation(content, node, sourceId, ctx, includeTargetOwn);
3269
3432
  const mask = { type: "mask", id: maskId, children: [content] };
3270
3433
  if (fx.maskType) mask.maskType = fx.maskType;
3271
3434
  if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
3272
3435
  if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
3436
+ if (fx.x !== void 0) mask.x = String(fx.x);
3437
+ if (fx.y !== void 0) mask.y = String(fx.y);
3438
+ if (fx.width !== void 0) mask.width = String(fx.width);
3439
+ if (fx.height !== void 0) mask.height = String(fx.height);
3273
3440
  ctx.defs.push(mask);
3274
3441
  node.mask = "url(#" + maskId + ")";
3275
3442
  return node;
@@ -3376,6 +3543,19 @@ function readTransformationFromBody(node) {
3376
3543
  if (parts.origin) out.origin = parts.origin;
3377
3544
  return Object.keys(out).length ? out : void 0;
3378
3545
  }
3546
+ if (node.transform && typeof node.transform === "object" && !node.transform.keyframes && !node.transform.kfs) {
3547
+ const wrapped = node.transform.value;
3548
+ const value = wrapped && typeof wrapped === "object" ? wrapped : node.transform;
3549
+ if (value && typeof value === "object") {
3550
+ const out = {};
3551
+ if (Array.isArray(value.translate)) out.translate = value.translate;
3552
+ if (typeof value.rotate === "number") out.rotate = value.rotate;
3553
+ if (typeof value.skew === "number") out.skew = value.skew;
3554
+ if (Array.isArray(value.scale)) out.scale = { value: value.scale };
3555
+ if (Array.isArray(value.origin)) out.origin = value.origin;
3556
+ return Object.keys(out).length ? out : void 0;
3557
+ }
3558
+ }
3379
3559
  return void 0;
3380
3560
  }
3381
3561
  function parseTransformStringToParts(s) {
@@ -3509,10 +3689,10 @@ function collectMaskAncestorChains(root, ctx) {
3509
3689
  const interestingNodes = /* @__PURE__ */ new Set();
3510
3690
  const collectInterestingNodes = (n) => {
3511
3691
  var _a, _b;
3512
- const href = (_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.href;
3513
- if (typeof href === "string") {
3692
+ const maskSourceId = stripHash2((_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.sourceId);
3693
+ if (typeof maskSourceId === "string") {
3514
3694
  interestingNodes.add(n);
3515
- const sourceNode = ctx.idMap.get(href);
3695
+ const sourceNode = ctx.idMap.get(maskSourceId);
3516
3696
  if (sourceNode) interestingNodes.add(sourceNode);
3517
3697
  }
3518
3698
  if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);
@@ -3538,8 +3718,9 @@ function extractTranslateOnly(node, ctx) {
3538
3718
  if (typeof tr === "string") {
3539
3719
  const parts = parseTranslateOnlyFromString(tr, ctx);
3540
3720
  if (parts) out.translate = parts;
3541
- } else if (tr && typeof tr === "object") {
3542
- const value = tr.value;
3721
+ } else if (tr && typeof tr === "object" && !tr.keyframes && !tr.kfs) {
3722
+ const wrapped = tr.value;
3723
+ const value = wrapped && typeof wrapped === "object" ? wrapped : tr;
3543
3724
  if (value && typeof value === "object" && Array.isArray(value.translate)) {
3544
3725
  out.translate = [value.translate[0] || 0, value.translate[1] || 0];
3545
3726
  }
@@ -3589,17 +3770,17 @@ function parseTranslateOnlyFromString(s, ctx) {
3589
3770
  var CONTENT_SUBREF = "content";
3590
3771
  function applyRefHref(node, clone2, ctx) {
3591
3772
  if (!clone2) return;
3592
- const baseId = clone2.baseId;
3593
- if (!baseId) {
3594
- if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing baseId");
3773
+ const sourceId = stripHash2(clone2.sourceId);
3774
+ if (!sourceId) {
3775
+ if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing sourceId");
3595
3776
  return;
3596
3777
  }
3597
- const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
3778
+ const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(sourceId) || sourceId : sourceId;
3598
3779
  node.href = "#" + targetId;
3599
3780
  }
3600
- function applyRefAndTransformationEffect(node, clone2, transformation, ctx) {
3781
+ function applyRefAndTransformationEffect(node, clone2, transformBy, ctx) {
3601
3782
  applyRefHref(node, clone2, ctx);
3602
- return applyTransformationEffect(node, transformation, ctx);
3783
+ return applyTransformByEffect(node, transformBy, ctx);
3603
3784
  }
3604
3785
 
3605
3786
  // src/effects/repeaterEffect.ts
@@ -3624,7 +3805,7 @@ function applyRepeaterEffect(node, fx, ctx) {
3624
3805
  for (let i = 1; i < copies; i++) {
3625
3806
  const baseClone = clone(base);
3626
3807
  const synthFx = synthesisePerCopyFx(fx, i);
3627
- const wrapped = synthFx ? applyTransformationEffect(baseClone, synthFx, ctx) : baseClone;
3808
+ const wrapped = synthFx ? applyTransformByEffect(baseClone, synthFx, ctx) : baseClone;
3628
3809
  children.push(wrapped);
3629
3810
  }
3630
3811
  const wrapper = { type: "g", children };
@@ -3636,10 +3817,13 @@ function applyRepeaterEffect(node, fx, ctx) {
3636
3817
  function synthesisePerCopyFx(fx, i) {
3637
3818
  const out = {};
3638
3819
  if (fx.translate !== void 0) {
3639
- out.translate = mapAnimatableVec2(fx.translate, (v) => [v[0] * i, v[1] * i]);
3820
+ out.translate = mapAnimatable(fx.translate, (v) => [v[0] * i, v[1] * i]);
3640
3821
  }
3641
3822
  if (fx.rotate !== void 0) {
3642
- out.rotate = mapAnimatableNumber(fx.rotate, (v) => v * i);
3823
+ out.rotate = mapAnimatable(fx.rotate, (v) => v * i);
3824
+ }
3825
+ if (fx.skew !== void 0) {
3826
+ out.skew = mapAnimatable(fx.skew, (v) => v * i);
3643
3827
  }
3644
3828
  if (fx.scale !== void 0) {
3645
3829
  out.scale = synthesiseScale(fx.scale, i);
@@ -3649,65 +3833,52 @@ function synthesisePerCopyFx(fx, i) {
3649
3833
  }
3650
3834
  return Object.keys(out).length ? out : void 0;
3651
3835
  }
3652
- function mapAnimatableVec2(raw, fn) {
3653
- if (Array.isArray(raw)) return fn(raw);
3654
- if (raw && typeof raw === "object") {
3655
- const obj = raw;
3656
- if (Array.isArray(obj.keyframes)) {
3657
- return __spreadProps(__spreadValues({}, obj), {
3658
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
3659
- });
3660
- }
3661
- if (obj.value !== void 0) {
3662
- return __spreadProps(__spreadValues({}, obj), { value: fn(obj.value) });
3663
- }
3664
- }
3665
- return raw;
3666
- }
3667
- function mapAnimatableNumber(raw, fn) {
3668
- if (typeof raw === "number") return fn(raw);
3669
- if (raw && typeof raw === "object") {
3670
- const obj = raw;
3671
- if (Array.isArray(obj.keyframes)) {
3672
- return __spreadProps(__spreadValues({}, obj), {
3673
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
3674
- });
3675
- }
3676
- if (obj.value !== void 0) {
3677
- return __spreadProps(__spreadValues({}, obj), { value: fn(obj.value) });
3678
- }
3836
+ function mapAnimatable(raw, fn, wrapStatic = false) {
3837
+ const read = readAnimatable(raw);
3838
+ if (read.kind === "absent" /* Absent */) return raw;
3839
+ if (read.kind === "static" /* Static */) {
3840
+ const mapped = fn(read.value);
3841
+ const wasRawStatic = typeof raw === "number" || Array.isArray(raw);
3842
+ return wasRawStatic && !wrapStatic ? mapped : { value: mapped };
3679
3843
  }
3680
- return raw;
3844
+ const out = {
3845
+ keyframes: read.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
3846
+ };
3847
+ if (read.loop !== void 0) out.loop = read.loop;
3848
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
3849
+ if (read.base !== void 0) out.value = fn(read.base);
3850
+ return out;
3681
3851
  }
3682
3852
  function synthesiseScale(raw, i) {
3683
- const scalePowerFromPercent = (v) => [Math.pow(v[0] / 100, i), Math.pow(v[1] / 100, i)];
3684
- const scalePowerFromUnits = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
3685
- if (Array.isArray(raw)) {
3686
- return { value: scalePowerFromPercent(raw) };
3687
- }
3688
- if (raw && typeof raw === "object") {
3689
- const obj = raw;
3690
- if (Array.isArray(obj.keyframes)) {
3691
- return __spreadProps(__spreadValues({}, obj), {
3692
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: scalePowerFromUnits(kf.value) }) : kf)
3693
- });
3694
- }
3695
- if (obj.value !== void 0) {
3696
- return __spreadProps(__spreadValues({}, obj), { value: scalePowerFromPercent(obj.value) });
3697
- }
3698
- }
3699
- return raw;
3853
+ const scalePower = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
3854
+ return mapAnimatable(raw, scalePower, true);
3700
3855
  }
3701
3856
 
3702
3857
  // src/effects/retimeEffect.ts
3703
3858
  var RETIME_MATERIALISATION_MODE_INLINE_G = false;
3704
3859
  function asRetime(r) {
3705
3860
  var _a, _b;
3706
- if (r.timeCrop) {
3707
- console.warn("retime: `timeCrop` is not supported yet and will be ignored");
3708
- }
3709
3861
  return { start: (_a = r.start) != null ? _a : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
3710
3862
  }
3863
+ var CROP_EDGE_MS = 1;
3864
+ function applyTimeCrop(useNode, crop, ctx) {
3865
+ const [start, end] = crop;
3866
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
3867
+ ctx.warnings.push("retime: timeCrop is not a pair of finite numbers \u2014 ignored");
3868
+ return;
3869
+ }
3870
+ const keyframes = end <= start ? [{ time: 0, value: 0 }] : [
3871
+ ...start > 0 ? [{ time: Math.max(0, start - CROP_EDGE_MS), value: 0 }] : [],
3872
+ { time: Math.max(0, start), value: 1 },
3873
+ { time: end, value: 1 },
3874
+ { time: end + CROP_EDGE_MS, value: 0 }
3875
+ ];
3876
+ const inner = __spreadValues({}, useNode);
3877
+ for (const k of Object.keys(useNode)) delete useNode[k];
3878
+ useNode.type = "g";
3879
+ useNode.children = [inner];
3880
+ useNode.animate = { opacity: { keyframes } };
3881
+ }
3711
3882
  function concatRetime(child, parent) {
3712
3883
  return {
3713
3884
  start: parent.start + parent.stretch * child.start,
@@ -3767,8 +3938,10 @@ function applyAllRetimeEffects(root, ctx) {
3767
3938
  for (const useNode of sites) {
3768
3939
  const retime = readCloneRetime(useNode);
3769
3940
  if (!retime) continue;
3941
+ const crop = retime.timeCrop;
3770
3942
  clearCloneRetime(useNode);
3771
3943
  materialiseRetime(useNode, asRetime(retime), ctx);
3944
+ if (crop) applyTimeCrop(useNode, crop, ctx);
3772
3945
  }
3773
3946
  }
3774
3947
  function materialiseRetime(useNode, retime, ctx) {
@@ -4015,14 +4188,11 @@ function createPathSampler(d) {
4015
4188
  // src/effects/textPathEffect.ts
4016
4189
  var EXTEND_MARGIN_FRAC = 0.15;
4017
4190
  function numRange(v) {
4018
- if (typeof v === "number") return { min: v, max: v };
4019
- if (v && typeof v === "object") {
4020
- const o = v;
4021
- if (typeof o.value === "number") return { min: o.value, max: o.value };
4022
- if (Array.isArray(o.keyframes) && o.keyframes.length) {
4023
- const vals = o.keyframes.map((k) => Number(k.value) || 0);
4024
- return { min: Math.min(...vals), max: Math.max(...vals) };
4025
- }
4191
+ const read = readAnimatable(v);
4192
+ if (read.kind === "static" /* Static */ && typeof read.value === "number") return { min: read.value, max: read.value };
4193
+ if (read.kind === "animated" /* Animated */ && read.keyframes.length) {
4194
+ const vals = read.keyframes.map((k) => Number(k.value) || 0);
4195
+ return { min: Math.min(...vals), max: Math.max(...vals) };
4026
4196
  }
4027
4197
  return { min: 0, max: 0 };
4028
4198
  }
@@ -4042,11 +4212,16 @@ function estimateTextAdvance(node) {
4042
4212
  var r3 = (n) => Math.round(n * 1e3) / 1e3;
4043
4213
  function shiftAnimatable(v, by) {
4044
4214
  if (!by || v === void 0 || v === null) return v;
4045
- if (typeof v === "number") return v + by;
4046
- const o = v;
4047
- if (typeof o.value === "number") return __spreadProps(__spreadValues({}, o), { value: o.value + by });
4048
- if (Array.isArray(o.keyframes)) return __spreadProps(__spreadValues({}, o), { keyframes: o.keyframes.map((k) => __spreadProps(__spreadValues({}, k), { value: (Number(k.value) || 0) + by })) });
4049
- return v;
4215
+ const read = readAnimatable(v);
4216
+ if (read.kind === "absent" /* Absent */) return v;
4217
+ if (read.kind === "static" /* Static */) return typeof v === "number" ? read.value + by : { value: read.value + by };
4218
+ const out = {
4219
+ keyframes: read.keyframes.map((k) => __spreadProps(__spreadValues({}, k), { value: (Number(k.value) || 0) + by }))
4220
+ };
4221
+ if (read.loop !== void 0) out.loop = read.loop;
4222
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
4223
+ if (read.base !== void 0) out.value = read.base + by;
4224
+ return out;
4050
4225
  }
4051
4226
  function extendedPathForBrowser(pathD, opts) {
4052
4227
  var _a;
@@ -4102,26 +4277,7 @@ function applyTextPathEffect(node, fx, ctx) {
4102
4277
  }
4103
4278
  function applyAnimatableNumber(node, attrName, raw) {
4104
4279
  if (raw === void 0 || raw === null) return;
4105
- if (typeof raw === "number") {
4106
- node[attrName] = String(raw);
4107
- return;
4108
- }
4109
- if (typeof raw === "object" && raw !== null) {
4110
- const obj = raw;
4111
- if (Array.isArray(obj.keyframes)) {
4112
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4113
- const animate = __spreadValues({}, prevAnimate || {});
4114
- const block = { keyframes: obj.keyframes };
4115
- if (obj.loop !== void 0) block.loop = obj.loop;
4116
- animate[attrName] = block;
4117
- node.animate = animate;
4118
- return;
4119
- }
4120
- if (typeof obj.value === "number") {
4121
- node[attrName] = String(obj.value);
4122
- return;
4123
- }
4124
- }
4280
+ writeAnimatableChannel(node, attrName, readAnimatable(raw), { asString: true });
4125
4281
  }
4126
4282
 
4127
4283
  // src/effects/elementFactory.ts
@@ -4315,13 +4471,13 @@ function materialiseGlyphTextHorizontal(node, opts) {
4315
4471
  if (y !== void 0) pen.y = y;
4316
4472
  pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
4317
4473
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4318
- const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
4474
+ const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4319
4475
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
4320
4476
  if (el.children) for (const ch of el.children) walk(ch, s);
4321
4477
  };
4322
4478
  const rootStyle = rootStyleOf(node);
4323
4479
  if (node.children) for (const ch of node.children) walk(ch, rootStyle);
4324
- const rootContent = (_c = str(node[TEXT_ATTR])) != null ? _c : str(node[TEXT_CONTENT_ATTR]);
4480
+ const rootContent = (_c = str(node[TEXT_CONTENT_ATTR])) != null ? _c : str(node[TEXT_ATTR]);
4325
4481
  if (rootContent && !((_d = node.children) == null ? void 0 : _d.length)) renderChars(rootContent, rootStyle);
4326
4482
  const anchor = str(node.textAnchor);
4327
4483
  if (anchor === "middle" || anchor === "end") {
@@ -4370,7 +4526,7 @@ function layoutGlyphTextChars(node, opts) {
4370
4526
  if (y !== void 0) pen.y = y;
4371
4527
  pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
4372
4528
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4373
- const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
4529
+ const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4374
4530
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
4375
4531
  if (el.children) for (const ch of el.children) walk(ch, s);
4376
4532
  };
@@ -4385,7 +4541,7 @@ function layoutGlyphTextChars(node, opts) {
4385
4541
  boxes.push({ x: pen.x, y: pen.y, width: 0, ascent: ((_d = gf == null ? void 0 : gf.ascent) != null ? _d : 0.9 * upm) * (s.fontSize / upm), fontSize: s.fontSize, line });
4386
4542
  }
4387
4543
  }
4388
- const rootContent = (_e = str(node[TEXT_ATTR])) != null ? _e : str(node[TEXT_CONTENT_ATTR]);
4544
+ const rootContent = (_e = str(node[TEXT_CONTENT_ATTR])) != null ? _e : str(node[TEXT_ATTR]);
4389
4545
  if (rootContent && !((_f = node.children) == null ? void 0 : _f.length)) renderChars(rootContent, rootStyle);
4390
4546
  const anchor = str(node.textAnchor);
4391
4547
  if (anchor === "middle" || anchor === "end") {
@@ -4413,7 +4569,7 @@ function layoutGlyphTextCharsAlongPath(node, pathD, opts) {
4413
4569
  const walk = (el, parentStyle) => {
4414
4570
  var _a2, _b2, _c;
4415
4571
  const s = resolveStyle2(el, parentStyle);
4416
- const content = (_a2 = str(el[TEXT_ATTR])) != null ? _a2 : str(el[TEXT_CONTENT_ATTR]);
4572
+ const content = (_a2 = str(el[TEXT_CONTENT_ATTR])) != null ? _a2 : str(el[TEXT_ATTR]);
4417
4573
  if (content && !((_b2 = el.children) == null ? void 0 : _b2.length)) {
4418
4574
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4419
4575
  const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
@@ -4467,7 +4623,7 @@ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
4467
4623
  const walk = (el, parentStyle) => {
4468
4624
  var _a, _b, _c;
4469
4625
  const s = resolveStyle2(el, parentStyle);
4470
- const content = (_a = str(el[TEXT_ATTR])) != null ? _a : str(el[TEXT_CONTENT_ATTR]);
4626
+ const content = (_a = str(el[TEXT_CONTENT_ATTR])) != null ? _a : str(el[TEXT_ATTR]);
4471
4627
  if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
4472
4628
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4473
4629
  if (gf) {
@@ -4691,9 +4847,9 @@ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pat
4691
4847
  }
4692
4848
 
4693
4849
  // src/effects/trimPathEffect.ts
4694
- function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4850
+ function applyTrimPathEffect(node, trimPath, ctx) {
4695
4851
  if (!trimPath) return node;
4696
- const trimAllAsOne = !!trimPath.trimAllAsOne;
4852
+ const combined = trimPath.subPaths === PxTrimSubPaths.combined;
4697
4853
  const leafEntries = [];
4698
4854
  const measure = (n) => {
4699
4855
  if (Array.isArray(n.children) && n.children.length > 0) {
@@ -4714,16 +4870,16 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4714
4870
  measure(node);
4715
4871
  if (!leafEntries.length) return node;
4716
4872
  let acc = 0;
4717
- const iterOrder = trimAllAsOne ? [...leafEntries].reverse() : leafEntries;
4873
+ const iterOrder = combined ? [...leafEntries].reverse() : leafEntries;
4718
4874
  for (const entry of iterOrder) {
4719
4875
  for (const sp of entry.subpaths) {
4720
- if (!trimAllAsOne) acc = 0;
4876
+ if (!combined) acc = 0;
4721
4877
  sp.startOffsetPx = acc;
4722
4878
  acc += sp.lengthPx;
4723
4879
  }
4724
4880
  }
4725
4881
  const chainLengthPx = acc;
4726
- if (trimAllAsOne && chainLengthPx < 1e-3) return node;
4882
+ if (combined && chainLengthPx < 1e-3) return node;
4727
4883
  const offsetReadRaw = readAnimatable(trimPath.offset);
4728
4884
  const offsetRead = offsetReadRaw.kind === "absent" /* Absent */ ? { kind: "static" /* Static */, value: 0 } : offsetReadRaw;
4729
4885
  const rangeReadRaw = readRangeWithCrossings(trimPath.range);
@@ -4735,18 +4891,18 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4735
4891
  if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {
4736
4892
  const entry = leafEntries[0];
4737
4893
  const sp = entry.subpaths[0];
4738
- const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
4894
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
4739
4895
  if (pathLengthPx < 1e-3) return node;
4740
- const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
4896
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
4741
4897
  return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);
4742
4898
  }
4743
4899
  const replacements = /* @__PURE__ */ new Map();
4744
4900
  for (const entry of leafEntries) {
4745
4901
  const newChildren = [];
4746
4902
  for (const sp of entry.subpaths) {
4747
- const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
4903
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
4748
4904
  if (pathLengthPx < 1e-3) continue;
4749
- const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
4905
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
4750
4906
  newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));
4751
4907
  }
4752
4908
  replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));
@@ -4853,22 +5009,8 @@ function computeAnimAttr(read, map) {
4853
5009
  };
4854
5010
  }
4855
5011
  function applyAttr(node, attrName, attr) {
4856
- var _a, _b;
4857
5012
  if (!attr) return;
4858
- if (attr.kind === "static" /* Static */) {
4859
- node[attrName] = attr.value;
4860
- return;
4861
- }
4862
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4863
- const animate = __spreadValues({}, prevAnimate || {});
4864
- const block = { keyframes: attr.keyframes };
4865
- if (attr.loop !== void 0) block.loop = attr.loop;
4866
- animate[attrName] = block;
4867
- node.animate = animate;
4868
- const firstKf = attr.keyframes[0];
4869
- if (firstKf && ((_a = firstKf.value) != null ? _a : firstKf.v) !== void 0) {
4870
- node[attrName] = (_b = firstKf.value) != null ? _b : firstKf.v;
4871
- }
5013
+ writeAnimatableChannel(node, attrName, attr);
4872
5014
  }
4873
5015
  var OPACITY_STEP_MS = 10;
4874
5016
  function computeOpacityFromRange(rangeRead) {
@@ -5052,9 +5194,9 @@ function applyPlayerEffects(root) {
5052
5194
  nextId: 0,
5053
5195
  contentRefInnerIds: /* @__PURE__ */ new Map(),
5054
5196
  maskAncestorChains: /* @__PURE__ */ new Map(),
5055
- // Resolved engine: `frames` ONLY when explicitly set; auto/webapi/unset →
5056
- // webapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5057
- engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi,
5197
+ // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →
5198
+ // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5199
+ engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.waapi,
5058
5200
  glyphs: (_b = getDefs(root)) == null ? void 0 : _b.glyphs
5059
5201
  };
5060
5202
  const working = clone(root);
@@ -5072,8 +5214,7 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5072
5214
  const originalId = typeof node.id === "string" ? node.id : void 0;
5073
5215
  const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
5074
5216
  if (!fx && !innerIdForContentRef) return node;
5075
- const { transformation, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5076
- const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
5217
+ const { transformBy, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5077
5218
  if (fx) delete node.effects;
5078
5219
  let n = node;
5079
5220
  let consumedByGlyphs = false;
@@ -5093,15 +5234,15 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5093
5234
  if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);
5094
5235
  n = applyFillGradientEffect(n, fillGradient, ctx);
5095
5236
  n = applyStrokeGradientEffect(n, strokeGradient, ctx);
5096
- n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
5237
+ n = applyTrimPathEffect(n, trimPath, ctx);
5097
5238
  n = applyRepeaterEffect(n, repeater, ctx);
5098
- n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
5239
+ n = applyMaskedByEffect(n, maskedBy, transformBy, ctx);
5099
5240
  n = applyClipPathEffect(n, clipPath, ctx);
5100
5241
  if (innerIdForContentRef) {
5101
5242
  applyRefHref(n, cloneFx, ctx);
5102
- n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
5243
+ n = splitForContentRef(n, transformBy, originalId, innerIdForContentRef, ctx);
5103
5244
  } else {
5104
- n = applyRefAndTransformationEffect(n, cloneFx, transformation, ctx);
5245
+ n = applyRefAndTransformationEffect(n, cloneFx, transformBy, ctx);
5105
5246
  }
5106
5247
  if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
5107
5248
  if (originalId) ctx.idMap.set(originalId, n);
@@ -5118,7 +5259,7 @@ function materialiseAllInTree(doc, engine, opts) {
5118
5259
  let root = applyPlayerEffects(doc).root;
5119
5260
  const duration = (_b = (_a = getAnimatorConfig(root)) == null ? void 0 : _a.duration) != null ? _b : DEFAULT_DURATION_MS;
5120
5261
  root = materialiseInternalLoopsInTree(root, duration);
5121
- if (engine === PxAnimatorEngine.webapi) {
5262
+ if (engine === PxAnimatorEngine.waapi) {
5122
5263
  root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
5123
5264
  root = materialiseAnimatedUseInstances(root);
5124
5265
  root = pruneUnreferencedDefs(root);
@@ -5507,6 +5648,7 @@ function evalTransformValue(v, t) {
5507
5648
  if (typeof v === "string") return parseTransformString(v);
5508
5649
  if (v.keyframes) return partsToMatrix(interpParts(v.keyframes, t));
5509
5650
  if (v.value) return partsToMatrix(v.value);
5651
+ if (typeof v === "object" && !Array.isArray(v)) return partsToMatrix(v);
5510
5652
  return IDENTITY;
5511
5653
  }
5512
5654
  function nodeMatrix(node, t) {
@@ -5663,12 +5805,13 @@ export {
5663
5805
  PxEffectsSchema,
5664
5806
  PxElementAnimationSchema,
5665
5807
  PxFillGradientEffectSchema,
5666
- PxGradientGeometryAnimationSchema,
5667
5808
  PxGradientSpreadMethod,
5668
5809
  PxGradientStopSchema,
5669
5810
  PxGradientType,
5670
5811
  PxGradientUnits,
5671
5812
  PxKeyframeSchema,
5813
+ PxKeyframeValueSchema,
5814
+ PxLoopExtend,
5672
5815
  PxLoopSchema,
5673
5816
  PxMaskedByEffectSchema,
5674
5817
  PxNodeBase,
@@ -5680,11 +5823,12 @@ export {
5680
5823
  PxSvgNodeExtra,
5681
5824
  PxTextEffectSchema,
5682
5825
  PxTextPathEffectSchema,
5826
+ PxTransformByEffectSchema,
5683
5827
  PxTransformPartsSchema,
5684
5828
  PxTransformValueSchema,
5685
- PxTransformationEffectSchema,
5686
5829
  PxTriggerSchema,
5687
5830
  PxTrimPathEffectSchema,
5831
+ PxTrimSubPaths,
5688
5832
  STYLE_ATTR_NAMES,
5689
5833
  TEXT_ATTR,
5690
5834
  TEXT_CONTENT_ATTR,