@pixodesk/svg-animator-core 1.0.22 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -55,6 +55,8 @@ __export(index_exports, {
55
55
  DEFAULT_DURATION_MS: () => DEFAULT_DURATION_MS,
56
56
  DISALLOWED_SVG_TAGS_LOWER: () => DISALLOWED_SVG_TAGS_LOWER,
57
57
  INTERNAL_ATTRS: () => INTERNAL_ATTRS,
58
+ LOOP_JUMP_SHIFT_MS: () => LOOP_JUMP_SHIFT_MS,
59
+ PCT_BASED_ATTR_NAMES: () => PCT_BASED_ATTR_NAMES,
58
60
  PX_ANIM_ATTR_NAME: () => PX_ANIM_ATTR_NAME,
59
61
  PX_ANIM_SRC_ATTR_NAME: () => PX_ANIM_SRC_ATTR_NAME,
60
62
  PX_TRANSFORM_PART_KEYS: () => PX_TRANSFORM_PART_KEYS,
@@ -72,12 +74,13 @@ __export(index_exports, {
72
74
  PxEffectsSchema: () => PxEffectsSchema,
73
75
  PxElementAnimationSchema: () => PxElementAnimationSchema,
74
76
  PxFillGradientEffectSchema: () => PxFillGradientEffectSchema,
75
- PxGradientGeometryAnimationSchema: () => PxGradientGeometryAnimationSchema,
76
77
  PxGradientSpreadMethod: () => PxGradientSpreadMethod,
77
78
  PxGradientStopSchema: () => PxGradientStopSchema,
78
79
  PxGradientType: () => PxGradientType,
79
80
  PxGradientUnits: () => PxGradientUnits,
80
81
  PxKeyframeSchema: () => PxKeyframeSchema,
82
+ PxKeyframeValueSchema: () => PxKeyframeValueSchema,
83
+ PxLoopExtend: () => PxLoopExtend,
81
84
  PxLoopSchema: () => PxLoopSchema,
82
85
  PxMaskedByEffectSchema: () => PxMaskedByEffectSchema,
83
86
  PxNodeBase: () => PxNodeBase,
@@ -89,11 +92,12 @@ __export(index_exports, {
89
92
  PxSvgNodeExtra: () => PxSvgNodeExtra,
90
93
  PxTextEffectSchema: () => PxTextEffectSchema,
91
94
  PxTextPathEffectSchema: () => PxTextPathEffectSchema,
95
+ PxTransformByEffectSchema: () => PxTransformByEffectSchema,
92
96
  PxTransformPartsSchema: () => PxTransformPartsSchema,
93
97
  PxTransformValueSchema: () => PxTransformValueSchema,
94
- PxTransformationEffectSchema: () => PxTransformationEffectSchema,
95
98
  PxTriggerSchema: () => PxTriggerSchema,
96
99
  PxTrimPathEffectSchema: () => PxTrimPathEffectSchema,
100
+ PxTrimSubPaths: () => PxTrimSubPaths,
97
101
  STYLE_ATTR_NAMES: () => STYLE_ATTR_NAMES,
98
102
  TEXT_ATTR: () => TEXT_ATTR,
99
103
  TEXT_CONTENT_ATTR: () => TEXT_CONTENT_ATTR,
@@ -274,8 +278,9 @@ var Union = class extends Base {
274
278
  }
275
279
  isValid(raw, ctx, path) {
276
280
  var _a;
277
- if (this.schemas.some((s) => s.isValid(raw))) return true;
278
- 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));
281
+ const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
282
+ if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
283
+ 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));
279
284
  return false;
280
285
  }
281
286
  _canSanitize(raw) {
@@ -351,6 +356,7 @@ var Obj = class extends Base {
351
356
  if (ctx == null ? void 0 : ctx.strict) {
352
357
  for (const key of Object.keys(obj)) {
353
358
  if (key in this._shape) continue;
359
+ if (obj[key] === void 0) continue;
354
360
  p.push(key);
355
361
  ctx.errors.push(pathStr(p) + ": unexpected extra key");
356
362
  p.pop();
@@ -491,6 +497,23 @@ var Any = class extends Base {
491
497
  return true;
492
498
  }
493
499
  };
500
+ var Defined = class extends Base {
501
+ constructor() {
502
+ super(...arguments);
503
+ this._default = void 0;
504
+ }
505
+ sanitize(raw) {
506
+ return raw;
507
+ }
508
+ isValid(raw, ctx, path) {
509
+ if (raw !== void 0) return true;
510
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": required value is missing");
511
+ return false;
512
+ }
513
+ _canSanitize(raw) {
514
+ return raw !== void 0;
515
+ }
516
+ };
494
517
  var Lazy = class extends Base {
495
518
  constructor(fn, _default) {
496
519
  super();
@@ -602,24 +625,65 @@ var px = {
602
625
  record: (value) => new Rec(value),
603
626
  /** Passes anything through unchanged — always valid. */
604
627
  any: () => new Any(),
628
+ /** Anything EXCEPT `undefined` — an open type whose presence is required (V6). */
629
+ defined: () => new Defined(),
605
630
  /** Fixed-length tuple — validates element count and each position individually. */
606
631
  tuple: (schemas) => new Tuple(schemas),
607
632
  /** Defers schema creation — required for recursive types. Must supply a default value. */
608
633
  lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
609
634
  };
610
635
 
611
- // src/PxAnimatorTypes.ts
636
+ // src/PxAnimatorConstants.ts
612
637
  var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
613
638
  var PX_ANIM_ATTR_NAME = "_px_animator";
614
639
  var PxAnimatorMode = {
615
640
  auto: "auto",
616
- webapi: "webapi",
641
+ waapi: "waapi",
617
642
  frames: "frames"
618
643
  };
619
644
  var PxAnimatorEngine = {
620
- webapi: PxAnimatorMode.webapi,
645
+ waapi: PxAnimatorMode.waapi,
621
646
  frames: PxAnimatorMode.frames
622
647
  };
648
+ var PxLoopExtend = {
649
+ /** Segment from the START; the animation is extended BEFORE the first keyframe
650
+ * (intro loops that run before the main timeline begins). */
651
+ before: "before",
652
+ /** DEFAULT — segment from the END; extended AFTER the last keyframe (idle/outro
653
+ * loops that continue once the main timeline has finished). */
654
+ after: "after"
655
+ };
656
+ var PxMaskType = {
657
+ luminance: "luminance",
658
+ alpha: "alpha"
659
+ };
660
+ var PxUnits = {
661
+ userSpaceOnUse: "userSpaceOnUse",
662
+ objectBoundingBox: "objectBoundingBox"
663
+ };
664
+ var PxCloneType = {
665
+ content: "content"
666
+ };
667
+ var PxPathOverflow = {
668
+ clip: "clip",
669
+ extend: "extend"
670
+ };
671
+ var PxLengthAdjust = {
672
+ spacing: "spacing",
673
+ spacingAndGlyphs: "spacingAndGlyphs"
674
+ };
675
+ var PxTextPathMethod = {
676
+ align: "align",
677
+ stretch: "stretch"
678
+ };
679
+ var PxTextPathSpacing = {
680
+ auto: "auto",
681
+ exact: "exact"
682
+ };
683
+ var PxTrimSubPaths = {
684
+ separate: "separate",
685
+ combined: "combined"
686
+ };
623
687
  var TEXT_ATTR = "text";
624
688
  var TEXT_CONTENT_ATTR = "textContent";
625
689
  var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
@@ -628,9 +692,51 @@ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
628
692
  "animator",
629
693
  "meta",
630
694
  "animate",
695
+ "effects",
631
696
  TEXT_ATTR,
632
697
  TEXT_CONTENT_ATTR
633
698
  ]);
699
+ var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
700
+ var PxGradientUnits = {
701
+ userSpaceOnUse: "userSpaceOnUse",
702
+ objectBoundingBox: "objectBoundingBox"
703
+ };
704
+ var PxGradientSpreadMethod = {
705
+ pad: "pad",
706
+ reflect: "reflect",
707
+ repeat: "repeat"
708
+ };
709
+ var PxGradientType = {
710
+ linear: "linear",
711
+ radial: "radial"
712
+ };
713
+ function isPxElementFileFormat(fileJson) {
714
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
715
+ return false;
716
+ }
717
+ return fileJson["type"] === "svg";
718
+ }
719
+ function getAnimatorConfig(doc) {
720
+ var _a;
721
+ return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
722
+ }
723
+ function getDefs(doc) {
724
+ var _a;
725
+ if (!doc) return void 0;
726
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
727
+ }
728
+ function getBindings(doc) {
729
+ var _a;
730
+ if (!doc) return void 0;
731
+ const animateById = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animateById;
732
+ if (!animateById) return void 0;
733
+ return Object.entries(animateById).map(([id, anim]) => ({ id, animate: anim }));
734
+ }
735
+ function getChildren(doc) {
736
+ return doc == null ? void 0 : doc.children;
737
+ }
738
+
739
+ // src/PxAnimatorTypes.ts
634
740
  var PxEasingOrRefSchema = px.union([
635
741
  px.string(),
636
742
  px.tuple([px.number(), px.number(), px.number(), px.number()])
@@ -642,13 +748,15 @@ var PxKeyframeValueSchema = implementsInterface()(px.union([
642
748
  px.array(px.number()),
643
749
  px.lazy(() => PxTransformPartsSchema, {}),
644
750
  px.object({ path: px.string() }),
645
- px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] })
751
+ px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
752
+ // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
753
+ px.lazy(() => px.array(PxGradientStopSchema), [])
646
754
  ]));
647
755
  var PxKeyframeSchema = implementsInterface()(px.object({
648
756
  time: px.number().optional(),
649
757
  t: px.number().optional(),
650
- value: px.any().optional(),
651
- v: px.any().optional(),
758
+ value: PxKeyframeValueSchema.optional(),
759
+ v: PxKeyframeValueSchema.optional(),
652
760
  easing: PxEasingOrRefSchema.optional(),
653
761
  e: PxEasingOrRefSchema.optional(),
654
762
  tangentOut: px.tuple([px.number(), px.number()]).optional(),
@@ -662,16 +770,16 @@ var PxKeyframeSchema = implementsInterface()(px.object({
662
770
  }));
663
771
  var PxLoopSchema = implementsInterface()(px.object({
664
772
  segmentCount: px.number().optional(),
665
- before: px.boolean().optional(),
773
+ extend: px.enum([PxLoopExtend.before, PxLoopExtend.after]).optional(),
666
774
  alternate: px.boolean().optional()
667
775
  }));
668
776
  var PxPropertyAnimationSchema = implementsInterface()(px.object({
777
+ value: PxKeyframeValueSchema.optional(),
669
778
  keyframes: px.array(PxKeyframeSchema).optional(),
670
779
  kfs: px.array(PxKeyframeSchema).optional(),
671
780
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
672
781
  autoOrient: px.boolean().optional()
673
782
  }));
674
- var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
675
783
  var PxTransformPartsSchema = implementsInterface()(px.object({
676
784
  translate: px.tuple([px.number(), px.number()]).optional(),
677
785
  rotate: px.number().optional(),
@@ -681,6 +789,7 @@ var PxTransformPartsSchema = implementsInterface()(px.object({
681
789
  }));
682
790
  var PxTransformValueSchema = px.union([
683
791
  px.string(),
792
+ PxTransformPartsSchema,
684
793
  px.object({ value: PxTransformPartsSchema }),
685
794
  PxPropertyAnimationSchema
686
795
  ]);
@@ -715,9 +824,11 @@ var PxDefsSchema = implementsInterface()(px.object({
715
824
  glyphs: px.record(PxGlyphFontSchema).optional()
716
825
  }));
717
826
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
718
- mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.webapi, PxAnimatorMode.frames]).optional(),
827
+ mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.waapi, PxAnimatorMode.frames]).optional(),
719
828
  duration: px.number().optional(),
720
829
  delay: px.number().optional(),
830
+ // LAW (SCHEMA-DESIGN R3, S10): the ONE sanctioned string-in-number union
831
+ // (CSS animation-iteration-count familiarity) — do not add more mixed unions.
721
832
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
722
833
  fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
723
834
  direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
@@ -725,9 +836,8 @@ var PxAnimatorConfigSchema = implementsInterface()(px.object({
725
836
  trigger: PxTriggerSchema.optional(),
726
837
  resetOnFinish: px.boolean().optional(),
727
838
  definitions: PxDefsSchema.optional(),
728
- animate: px.record(PxElementAnimationSchema).optional(),
729
- timeline: px.string().optional(),
730
- debug: px.boolean().optional(),
839
+ animateById: px.record(PxElementAnimationSchema).optional(),
840
+ timelineSource: px.string().optional(),
731
841
  debugInstName: px.string().optional()
732
842
  }));
733
843
  var PxBindingSchema = implementsInterface()(px.object({
@@ -737,26 +847,29 @@ var PxBindingSchema = implementsInterface()(px.object({
737
847
  var PxAttrValueSchema = px.union([
738
848
  px.string(),
739
849
  px.number(),
740
- px.object({ value: px.any() }),
741
- PxPropertyAnimationSchema
850
+ px.array(px.number()),
851
+ // Structured static — `{value: …}` (read-accepted transitional spelling, S1).
852
+ // `defined`, not `any`: the KEY's presence is what identifies this branch (V6).
853
+ px.object({ value: px.defined() }),
854
+ // Bare transform parts record — the canonical static `transform` on the wire (T2).
855
+ PxTransformPartsSchema
742
856
  ]);
743
857
  var PxAnimatableNumberSchema = px.union([
744
858
  px.number(),
745
859
  px.object({ value: px.number() }),
746
- px.object({
747
- keyframes: px.array(PxKeyframeSchema),
748
- autoOrient: px.boolean().optional()
749
- })
860
+ PxPropertyAnimationSchema
750
861
  ]);
751
862
  var PxAnimatableVec2Schema = px.union([
752
863
  px.tuple([px.number(), px.number()]),
753
864
  px.object({ value: px.tuple([px.number(), px.number()]) }),
754
- px.object({
755
- keyframes: px.array(PxKeyframeSchema),
756
- autoOrient: px.boolean().optional()
757
- })
865
+ PxPropertyAnimationSchema
866
+ ]);
867
+ var PxAnimatableStringSchema = px.union([
868
+ px.string(),
869
+ px.object({ value: px.string() }),
870
+ PxPropertyAnimationSchema
758
871
  ]);
759
- var PxTransformationEffectSchema = implementsInterface()(px.object({
872
+ var PxTransformByEffectSchema = implementsInterface()(px.object({
760
873
  translate: PxAnimatableVec2Schema.optional(),
761
874
  rotate: PxAnimatableNumberSchema.optional(),
762
875
  scale: PxAnimatableVec2Schema.optional(),
@@ -764,51 +877,46 @@ var PxTransformationEffectSchema = implementsInterface()(px.object({
764
877
  origin: PxAnimatableVec2Schema.optional()
765
878
  }));
766
879
  var PxRepeaterEffectSchema = implementsInterface()(px.object({
880
+ // STATIC config, not a channel (V2/SCHEMA-DESIGN R5): the copy COUNT is read
881
+ // once at expansion time and never sampled — plain number, no `keyframes`.
767
882
  copies: px.number().optional(),
768
883
  translate: PxAnimatableVec2Schema.optional(),
769
884
  rotate: PxAnimatableNumberSchema.optional(),
885
+ skew: PxAnimatableNumberSchema.optional(),
770
886
  scale: PxAnimatableVec2Schema.optional(),
771
887
  origin: PxAnimatableVec2Schema.optional()
772
888
  }));
773
889
  var PxMaskedByEffectSchema = implementsInterface()(px.object({
774
- href: px.string().optional(),
775
- maskType: px.string().optional(),
776
- maskUnits: px.string().optional(),
777
- maskContentUnits: px.string().optional()
890
+ sourceId: px.string().optional(),
891
+ maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha]).optional(),
892
+ maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
893
+ maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
894
+ x: px.number().optional(),
895
+ y: px.number().optional(),
896
+ width: px.number().optional(),
897
+ height: px.number().optional()
778
898
  }));
779
899
  var PxClipPathEffectSchema = implementsInterface()(px.object({
780
- d: px.string().optional(),
900
+ d: PxAnimatableStringSchema.optional(),
781
901
  animate: PxPropertyAnimationSchema.optional()
782
902
  }));
783
903
  var PxTrimPathEffectSchema = implementsInterface()(px.object({
784
904
  offset: PxAnimatableNumberSchema.optional(),
785
905
  range: PxAnimatableVec2Schema.optional(),
786
- trimAllAsOne: px.boolean().optional()
906
+ subPaths: px.enum([PxTrimSubPaths.separate, PxTrimSubPaths.combined]).optional()
787
907
  }));
788
908
  var PxRetimeEffectSchema = implementsInterface()(px.object({
789
- baseId: px.string().optional(),
909
+ sourceId: px.string().optional(),
790
910
  start: px.number().optional(),
791
911
  stretch: px.number().optional(),
792
912
  timeCrop: px.tuple([px.number(), px.number()]).optional()
793
913
  }));
794
914
  var PxCloneEffectSchema = implementsInterface()(px.object({
795
- type: px.string().optional(),
796
- baseId: px.string().optional(),
915
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
916
+ type: px.enum([PxCloneType.content]).optional(),
917
+ sourceId: px.string().optional(),
797
918
  retime: PxRetimeEffectSchema.optional()
798
919
  }));
799
- var PxGradientUnits = {
800
- userSpaceOnUse: "userSpaceOnUse",
801
- objectBoundingBox: "objectBoundingBox"
802
- };
803
- var PxGradientSpreadMethod = {
804
- pad: "pad",
805
- reflect: "reflect",
806
- repeat: "repeat"
807
- };
808
- var PxGradientType = {
809
- linear: "linear",
810
- radial: "radial"
811
- };
812
920
  var PxGradientStopSchema = implementsInterface()(px.object({
813
921
  offset: px.number(),
814
922
  color: px.string()
@@ -816,39 +924,28 @@ var PxGradientStopSchema = implementsInterface()(px.object({
816
924
  var PxAnimatableGradientStopsSchema = px.union([
817
925
  px.array(PxGradientStopSchema),
818
926
  px.object({ value: px.array(PxGradientStopSchema) }),
819
- px.object({ keyframes: px.array(PxKeyframeSchema) })
927
+ PxPropertyAnimationSchema
820
928
  ]);
821
- var PxGradientGeometryAnimationSchema = implementsInterface()(px.object({
822
- gradientX1: PxPropertyAnimationSchema.optional(),
823
- gradientY1: PxPropertyAnimationSchema.optional(),
824
- gradientX2: PxPropertyAnimationSchema.optional(),
825
- gradientY2: PxPropertyAnimationSchema.optional(),
826
- gradientCx: PxPropertyAnimationSchema.optional(),
827
- gradientCy: PxPropertyAnimationSchema.optional(),
828
- gradientFx: PxPropertyAnimationSchema.optional(),
829
- gradientFy: PxPropertyAnimationSchema.optional(),
830
- gradientR: PxPropertyAnimationSchema.optional()
831
- }));
832
929
  var PxFillGradientEffectSchema = implementsInterface()(px.object({
930
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
833
931
  type: px.enum([PxGradientType.linear, PxGradientType.radial]),
834
- p1: px.tuple([px.number(), px.number()]).optional(),
835
- p2: px.tuple([px.number(), px.number()]).optional(),
836
- c: px.tuple([px.number(), px.number()]).optional(),
837
- r: px.number().optional(),
838
- fp: px.tuple([px.number(), px.number()]).optional(),
932
+ p1: PxAnimatableVec2Schema.optional(),
933
+ p2: PxAnimatableVec2Schema.optional(),
934
+ c: PxAnimatableVec2Schema.optional(),
935
+ r: PxAnimatableNumberSchema.optional(),
936
+ fp: PxAnimatableVec2Schema.optional(),
839
937
  stops: PxAnimatableGradientStopsSchema.optional(),
840
- gradientUnits: px.string().optional(),
841
- spreadMethod: px.string().optional(),
842
- gradientTransform: px.string().optional(),
843
- animate: PxGradientGeometryAnimationSchema.optional()
938
+ gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
939
+ spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
940
+ gradientTransform: px.string().optional()
844
941
  }));
845
942
  var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
846
943
  var PxTextPathEffectSchema = implementsInterface()(px.object({
847
944
  path: px.string(),
848
- pathOverflow: px.string().optional(),
849
- lengthAdjust: px.string().optional(),
850
- method: px.string().optional(),
851
- spacing: px.string().optional(),
945
+ pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
946
+ lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
947
+ method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
948
+ spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
852
949
  startOffset: PxAnimatableNumberSchema.optional(),
853
950
  textLength: PxAnimatableNumberSchema.optional()
854
951
  }));
@@ -856,13 +953,12 @@ var PxTextEffectSchema = implementsInterface()(px.object({
856
953
  useGlyphs: px.boolean().optional()
857
954
  }));
858
955
  var PxEffectsSchema = implementsInterface()(px.object({
859
- transformation: PxTransformationEffectSchema.optional(),
956
+ transformBy: PxTransformByEffectSchema.optional(),
860
957
  repeater: PxRepeaterEffectSchema.optional(),
861
958
  maskedBy: PxMaskedByEffectSchema.optional(),
862
959
  clipPath: PxClipPathEffectSchema.optional(),
863
960
  trimPath: PxTrimPathEffectSchema.optional(),
864
961
  clone: PxCloneEffectSchema.optional(),
865
- isCombinedShape: px.boolean().optional(),
866
962
  fillGradient: PxFillGradientEffectSchema.optional(),
867
963
  strokeGradient: PxStrokeGradientEffectSchema.optional(),
868
964
  textPath: PxTextPathEffectSchema.optional(),
@@ -886,6 +982,14 @@ function validateNodeEffects(root, opts) {
886
982
  return warnings;
887
983
  }
888
984
  var PxNodeBase = px.openObject({
985
+ // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
986
+ // kind of thing is this", discriminated by its CARRIER — here the node TAG
987
+ // (`rect`, `text`), and inside a sub-object that object's kind (`clone.type`,
988
+ // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
989
+ // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
990
+ // would add words that all mean "type" and still need the carrier to read.
991
+ // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
992
+ // (issues V3), never of distinct key names.
889
993
  type: px.string(),
890
994
  id: px.string().optional(),
891
995
  meta: px.any().optional(),
@@ -895,7 +999,7 @@ var PxNodeBase = px.openObject({
895
999
  effects: PxEffectsSchema.optional(),
896
1000
  // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
897
1001
  // string ref / array of refs / inline definition / mixed array; mirrors
898
- // `animator.animate` map values and what `processNode` resolves at runtime.
1002
+ // `animator.animateById` map values and what `processNode` resolves at runtime.
899
1003
  animate: PxElementAnimationSchema.optional(),
900
1004
  style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
901
1005
  }, PxAttrValueSchema);
@@ -903,8 +1007,10 @@ var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._sh
903
1007
  children: px.lazy(() => px.array(PxNodeSchema), []).optional()
904
1008
  }), PxAttrValueSchema);
905
1009
  var PxSvgNodeExtra = px.object({
906
- width: px.number().optional(),
907
- height: px.number().optional(),
1010
+ // `"100%"` and other SVG length strings are legal here — a number-only slot rejected
1011
+ // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.
1012
+ width: px.union([px.number(), px.string()]).optional(),
1013
+ height: px.union([px.number(), px.string()]).optional(),
908
1014
  viewBox: px.string().optional(),
909
1015
  animator: PxAnimatorConfigSchema.optional()
910
1016
  });
@@ -919,35 +1025,10 @@ var PxBezierPathSchema = implementsInterface()(px.object({
919
1025
  o: px.array(px.array(px.number())).optional(),
920
1026
  c: px.boolean().optional()
921
1027
  }));
922
- function isPxElementFileFormat(fileJson) {
923
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
924
- return false;
925
- }
926
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
927
- }
928
1028
  function isPxElementFileFormatDeep(fileJson) {
929
1029
  const valid = PxAnimatedSvgDocumentSchema.isValid(fileJson);
930
1030
  return { valid, errors: valid ? [] : ["Document failed schema validation"] };
931
1031
  }
932
- function getAnimatorConfig(doc) {
933
- var _a, _b;
934
- 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);
935
- }
936
- function getDefs(doc) {
937
- var _a;
938
- if (!doc) return void 0;
939
- return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
940
- }
941
- function getBindings(doc) {
942
- var _a;
943
- if (!doc) return void 0;
944
- const animate = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animate;
945
- if (!animate) return void 0;
946
- return Object.entries(animate).map(([id, anim]) => ({ id, animate: anim }));
947
- }
948
- function getChildren(doc) {
949
- return doc == null ? void 0 : doc.children;
950
- }
951
1032
 
952
1033
  // src/PxAnimatorUtil.ts
953
1034
  function bezierToSvgPath(path, forceCurves = false) {
@@ -1402,7 +1483,7 @@ function generateNewIds(doc) {
1402
1483
  "flood-color",
1403
1484
  "lighting-color"
1404
1485
  ]);
1405
- const directIdRefAttrs = /* @__PURE__ */ new Set(["baseId", "targetId", "boundElementId"]);
1486
+ const directIdRefAttrs = /* @__PURE__ */ new Set(["sourceId", "targetId", "boundElementId"]);
1406
1487
  function collectIds(node) {
1407
1488
  if (!node || typeof node !== "object") return;
1408
1489
  if (node.id && typeof node.id === "string") {
@@ -1440,9 +1521,10 @@ function generateNewIds(doc) {
1440
1521
  } else if (urlRefAttrs.has(key)) {
1441
1522
  node[key] = replaceUrlRefs(value, idMap);
1442
1523
  } else if (directIdRefAttrs.has(key)) {
1443
- const newId = idMap.get(value);
1524
+ const hasHash = value.startsWith("#");
1525
+ const newId = idMap.get(hasHash ? value.slice(1) : value);
1444
1526
  if (newId) {
1445
- node[key] = newId;
1527
+ node[key] = hasHash ? "#" + newId : newId;
1446
1528
  }
1447
1529
  } else if (value.includes("url(#")) {
1448
1530
  node[key] = replaceUrlRefs(value, idMap);
@@ -1460,14 +1542,14 @@ function generateNewIds(doc) {
1460
1542
  }
1461
1543
  collectIds(cloned);
1462
1544
  updateRefs(cloned);
1463
- const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animate;
1545
+ const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animateById;
1464
1546
  if (docAnimate && typeof docAnimate === "object") {
1465
1547
  const updatedAnimate = {};
1466
1548
  for (const [id, anim] of Object.entries(docAnimate)) {
1467
1549
  const newId = (_b = idMap.get(id)) != null ? _b : id;
1468
1550
  updatedAnimate[newId] = anim;
1469
1551
  }
1470
- cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animate: updatedAnimate });
1552
+ cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animateById: updatedAnimate });
1471
1553
  }
1472
1554
  return cloned;
1473
1555
  }
@@ -1558,8 +1640,9 @@ function getNormalizedProps(props) {
1558
1640
  let value = props[rawKey];
1559
1641
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
1560
1642
  propsCopy[key] = toRGBA(value);
1561
- } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
1562
- propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
1643
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes && !value.kfs) {
1644
+ const parts = value.value && typeof value.value === "object" ? value.value : value;
1645
+ propsCopy["transform"] = composeTransformParts(parts, { withUnits: false });
1563
1646
  } else if (TRANSFORM_FN_NAMES.has(key)) {
1564
1647
  if (Array.isArray(value)) {
1565
1648
  if (key === "translate") value = value.map((v) => v + "px");
@@ -1567,6 +1650,8 @@ function getNormalizedProps(props) {
1567
1650
  }
1568
1651
  if (key === "rotate") value = value + "deg";
1569
1652
  propsCopy["transform"] = key + "(" + value + ")";
1653
+ } else if (Array.isArray(value)) {
1654
+ propsCopy[key] = value.join(",");
1570
1655
  } else if (value !== void 0 && value !== null) {
1571
1656
  propsCopy[key] = String(value);
1572
1657
  }
@@ -1968,6 +2053,16 @@ function walkAndMaterialise(node, opts) {
1968
2053
  }
1969
2054
 
1970
2055
  // src/PxDefinitions.ts
2056
+ var LOOP_JUMP_SHIFT_MS = 1;
2057
+ function deepEqualValue(a, b) {
2058
+ if (a === b) return true;
2059
+ if (typeof a !== typeof b || a === null || b === null || typeof a !== "object") return false;
2060
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
2061
+ const ka = Object.keys(a);
2062
+ const kb = Object.keys(b);
2063
+ if (ka.length !== kb.length) return false;
2064
+ return ka.every((k) => deepEqualValue(a[k], b[k]));
2065
+ }
1971
2066
  function parsePathCommands(d) {
1972
2067
  const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
1973
2068
  const commands = [];
@@ -2170,7 +2265,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2170
2265
  const totalIntervals = keyframes.length - 1;
2171
2266
  const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2172
2267
  let segKfs;
2173
- if (loop.before) {
2268
+ if (loop.extend === PxLoopExtend.before) {
2174
2269
  segKfs = keyframes.slice(0, segCount + 1);
2175
2270
  } else {
2176
2271
  segKfs = keyframes.slice(totalIntervals - segCount);
@@ -2178,7 +2273,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2178
2273
  const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2179
2274
  const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2180
2275
  let fillStart, fillEnd;
2181
- if (loop.before) {
2276
+ if (loop.extend === PxLoopExtend.before) {
2182
2277
  fillStart = 0;
2183
2278
  fillEnd = firstT;
2184
2279
  } else {
@@ -2205,7 +2300,12 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2205
2300
  const remainder = fillDuration - fullReps * segDuration;
2206
2301
  const partialFraction = remainder / segDuration;
2207
2302
  const looped = [];
2303
+ const separateBoundary = loop.extend !== PxLoopExtend.before;
2304
+ const originalTerminalKf = keyframes[keyframes.length - 1];
2305
+ let terminalEasingOverride;
2306
+ let hasTerminalEasingOverride = false;
2208
2307
  function appendRep(repStart, isReversed, partial) {
2308
+ var _a2;
2209
2309
  let entries;
2210
2310
  if (isReversed) {
2211
2311
  entries = [];
@@ -2241,8 +2341,26 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2241
2341
  looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2242
2342
  return;
2243
2343
  }
2344
+ const prevKf = looped.length > 0 ? looped[looped.length - 1] : originalTerminalKf;
2345
+ const isBoundary = separateBoundary && i === 0 && prevKf !== void 0 && Math.abs(((_a2 = prevKf.t) != null ? _a2 : 0) - (repStart + entry.relT * segDuration)) < 1e-9;
2346
+ if (isBoundary) {
2347
+ if (deepEqualValue(prevKf.v, entry.v)) {
2348
+ if (looped.length > 0) {
2349
+ prevKf.e = entry.e;
2350
+ prevKf.tangentOut = entry.tangentOut;
2351
+ } else {
2352
+ terminalEasingOverride = entry.e;
2353
+ hasTerminalEasingOverride = true;
2354
+ }
2355
+ continue;
2356
+ }
2357
+ if (looped.length > 0) {
2358
+ delete prevKf.tangentIn;
2359
+ delete prevKf.tangentOut;
2360
+ }
2361
+ }
2244
2362
  const pushed = {
2245
- t: repStart + entry.relT * segDuration,
2363
+ t: repStart + entry.relT * segDuration + (isBoundary ? LOOP_JUMP_SHIFT_MS : 0),
2246
2364
  v: entry.v,
2247
2365
  e: i < entries.length - 1 ? entry.e : void 0
2248
2366
  };
@@ -2290,7 +2408,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2290
2408
  looped.push(pushed);
2291
2409
  }
2292
2410
  }
2293
- if (loop.before) {
2411
+ if (loop.extend === PxLoopExtend.before) {
2294
2412
  if (partialFraction > 1e-9) {
2295
2413
  const isReversed = !!loop.alternate && fullReps % 2 === 0;
2296
2414
  appendRepTail(fillStart, isReversed, partialFraction);
@@ -2313,9 +2431,14 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2313
2431
  appendRep(repStart, isReversed, partialFraction);
2314
2432
  }
2315
2433
  }
2316
- if (loop.before) {
2434
+ if (loop.extend === PxLoopExtend.before) {
2317
2435
  return [...looped, ...keyframes];
2318
2436
  } else {
2437
+ if (hasTerminalEasingOverride && keyframes.length > 0) {
2438
+ const head = keyframes.slice(0, -1);
2439
+ const tail = __spreadProps(__spreadValues({}, keyframes[keyframes.length - 1]), { e: terminalEasingOverride });
2440
+ return [...head, tail, ...looped];
2441
+ }
2319
2442
  return [...keyframes, ...looped];
2320
2443
  }
2321
2444
  }
@@ -2429,20 +2552,23 @@ var _elementIdCounter = 0;
2429
2552
  function generateElementId() {
2430
2553
  return "_px_el_" + ++_elementIdCounter;
2431
2554
  }
2432
- function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.webapi) {
2555
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.waapi) {
2433
2556
  const normalized = {};
2434
2557
  for (const [propName, propAnim] of Object.entries(animDef)) {
2558
+ if (propName === "transform" && propAnim.alongPathMode === "offsetPath" && animDef["offsetDistance"] !== void 0) {
2559
+ continue;
2560
+ }
2435
2561
  const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2436
2562
  if (normalizedKfs.length > 0) {
2437
2563
  const out = { kfs: normalizedKfs };
2438
2564
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2439
2565
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2440
- normalized[propName] = engine === PxAnimatorEngine.webapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2566
+ normalized[propName] = engine === PxAnimatorEngine.waapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2441
2567
  }
2442
2568
  }
2443
2569
  return normalized;
2444
2570
  }
2445
- function getNormalisedBindings(doc, engine = PxAnimatorEngine.webapi) {
2571
+ function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2446
2572
  const animatorConfig = getAnimatorConfig(doc) || {};
2447
2573
  const defs = getDefs(doc);
2448
2574
  const duration = animatorConfig.duration || 1e3;
@@ -2853,20 +2979,41 @@ function partsRecord(part, value, origin) {
2853
2979
  return rec;
2854
2980
  }
2855
2981
  function readAnimatable(raw) {
2856
- var _a, _b;
2982
+ var _a, _b, _c;
2857
2983
  if (raw === void 0) return { kind: "absent" /* Absent */ };
2858
2984
  if (Array.isArray(raw)) return { kind: "static" /* Static */, value: raw };
2859
2985
  if (typeof raw === "object") {
2860
2986
  const obj = raw;
2861
2987
  const kfs = (_a = obj.keyframes) != null ? _a : obj.kfs;
2862
2988
  if (kfs) {
2863
- return { kind: "animated" /* Animated */, keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2989
+ const out = { kind: "animated" /* Animated */, keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2990
+ const base = (_b = obj.value) != null ? _b : obj.v;
2991
+ if (base !== void 0 && out.kind === "animated" /* Animated */) out.base = base;
2992
+ return out;
2864
2993
  }
2865
- const staticValue = (_b = obj.value) != null ? _b : obj.v;
2994
+ const staticValue = (_c = obj.value) != null ? _c : obj.v;
2866
2995
  if (staticValue !== void 0) return { kind: "static" /* Static */, value: staticValue };
2867
2996
  }
2868
2997
  return { kind: "static" /* Static */, value: raw };
2869
2998
  }
2999
+ function writeAnimatableChannel(node, attrName, read, opts) {
3000
+ var _a, _b, _c, _d;
3001
+ const toOut = (v) => (opts == null ? void 0 : opts.asString) && v !== void 0 && v !== null ? String(v) : v;
3002
+ if (read.kind === "absent" /* Absent */) return;
3003
+ if (read.kind === "static" /* Static */) {
3004
+ node[attrName] = toOut(read.value);
3005
+ return;
3006
+ }
3007
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3008
+ const animate = __spreadValues({}, prevAnimate || {});
3009
+ const block = { keyframes: read.keyframes };
3010
+ if (read.loop !== void 0) block.loop = read.loop;
3011
+ if (read.autoOrient !== void 0) block.autoOrient = read.autoOrient;
3012
+ animate[attrName] = block;
3013
+ node.animate = animate;
3014
+ 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;
3015
+ if (baseline !== void 0) node[attrName] = toOut(baseline);
3016
+ }
2870
3017
  function normaliseKeyframe(kf) {
2871
3018
  if (!kf || typeof kf !== "object") return kf;
2872
3019
  const k = kf;
@@ -2886,7 +3033,7 @@ function readStaticOrigin(raw, ctx) {
2886
3033
  const o = readAnimatable(raw);
2887
3034
  if (o.kind === "absent" /* Absent */) return void 0;
2888
3035
  if (o.kind === "static" /* Static */) return o.value;
2889
- ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
3036
+ ctx.warnings.push("transformBy.origin: animated origin approximated by its first keyframe");
2890
3037
  return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
2891
3038
  }
2892
3039
  function keyframeWith(kf, value) {
@@ -2899,7 +3046,7 @@ function keyframeWith(kf, value) {
2899
3046
  }
2900
3047
 
2901
3048
  // src/effects/transformationEffect.ts
2902
- function applyTransformationEffect(node, fx, ctx) {
3049
+ function applyTransformByEffect(node, fx, ctx) {
2903
3050
  if (!fx) return node;
2904
3051
  delete node.transform;
2905
3052
  let n = node;
@@ -2935,6 +3082,10 @@ function applyTransformationEffect(node, fx, ctx) {
2935
3082
  } else {
2936
3083
  n = wrapTransformPart(n, "translate" /* Translate */, fx.translate, ctx);
2937
3084
  }
3085
+ if (n !== node && node.id) {
3086
+ n.id = node.id;
3087
+ delete node.id;
3088
+ }
2938
3089
  return n;
2939
3090
  }
2940
3091
  function translateHasAutoOrient(translate) {
@@ -2944,8 +3095,6 @@ function translateHasAutoOrient(translate) {
2944
3095
  return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
2945
3096
  }
2946
3097
  function normalizeScale(raw) {
2947
- if (raw === void 0) return void 0;
2948
- if (Array.isArray(raw)) return [raw[0] / 100, raw[1] / 100];
2949
3098
  return raw;
2950
3099
  }
2951
3100
  function wrapTransformPart(inner, part, raw, ctx) {
@@ -2987,35 +3136,57 @@ function wrapOrigin(inner, raw, invert) {
2987
3136
  return inner;
2988
3137
  }
2989
3138
 
3139
+ // src/effects/util.ts
3140
+ function genId(ctx, prefix) {
3141
+ return "_lw_" + prefix + "_" + ctx.nextId++;
3142
+ }
3143
+ function stripHash2(href) {
3144
+ return typeof href === "string" ? href.replace(/^#/, "") : void 0;
3145
+ }
3146
+ function indexById(node, map) {
3147
+ var _a;
3148
+ if (typeof node.id === "string") map.set(node.id, node);
3149
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3150
+ }
3151
+ function spliceDefs(root, defs) {
3152
+ if (!defs.length) return;
3153
+ const existing = root.children || (root.children = []);
3154
+ existing.unshift({ type: "defs", children: defs });
3155
+ }
3156
+ var clone = deepClonePxNode;
3157
+ function regenerateIdsInClone(root, ctx) {
3158
+ return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3159
+ }
3160
+
2990
3161
  // src/effects/contentRefSplit.ts
2991
3162
  function identifyContentRefTargets(node, ctx, allocator) {
2992
3163
  var _a, _b, _c;
2993
3164
  if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.type) === "content") {
2994
- const baseId = node.effects.clone.baseId;
2995
- if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
2996
- ctx.contentRefInnerIds.set(baseId, allocator(baseId));
3165
+ const sourceId = stripHash2(node.effects.clone.sourceId);
3166
+ if (typeof sourceId === "string" && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {
3167
+ ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));
2997
3168
  }
2998
3169
  }
2999
3170
  (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
3000
3171
  }
3001
- function splitForContentRef(node, transformation, originalId, innerId, ctx) {
3002
- const outerBody = liftBodyTranslate(node, transformation);
3172
+ function splitForContentRef(node, transformBy, originalId, innerId, ctx) {
3173
+ const outerBody = liftBodyTranslate(node, transformBy);
3003
3174
  if (typeof node.id === "string") delete node.id;
3004
- const { outer: outerTr, inner: innerTr } = splitTransformationEffect(transformation);
3175
+ const { outer: outerTr, inner: innerTr } = splitTransformByEffect(transformBy);
3005
3176
  let innerNode = node;
3006
- innerNode = applyTransformationEffect(innerNode, innerTr, ctx);
3177
+ innerNode = applyTransformByEffect(innerNode, innerTr, ctx);
3007
3178
  const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
3008
3179
  let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
3009
3180
  if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
3010
3181
  if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
3011
3182
  if (outerTr) {
3012
3183
  delete outerWrapper.id;
3013
- outerWrapper = applyTransformationEffect(outerWrapper, outerTr, ctx);
3184
+ outerWrapper = applyTransformByEffect(outerWrapper, outerTr, ctx);
3014
3185
  outerWrapper.id = originalId;
3015
3186
  }
3016
3187
  return outerWrapper;
3017
3188
  }
3018
- function liftBodyTranslate(node, transformation) {
3189
+ function liftBodyTranslate(node, transformBy) {
3019
3190
  var _a, _b, _c;
3020
3191
  const out = {};
3021
3192
  let didLiftAnimate = false;
@@ -3069,7 +3240,7 @@ function liftBodyTranslate(node, transformation) {
3069
3240
  didLiftAnimate = true;
3070
3241
  }
3071
3242
  }
3072
- const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
3243
+ const transformationHasTranslate = (transformBy == null ? void 0 : transformBy.translate) !== void 0;
3073
3244
  const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
3074
3245
  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);
3075
3246
  if (typeof node.transform === "string") {
@@ -3088,6 +3259,24 @@ function liftBodyTranslate(node, transformation) {
3088
3259
  if (split.rest) node.transform = split.rest;
3089
3260
  else delete node.transform;
3090
3261
  }
3262
+ } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes && !node.transform.kfs) {
3263
+ const wrapped = node.transform.value;
3264
+ const isWrapped = !!(wrapped && typeof wrapped === "object");
3265
+ const value = isWrapped ? wrapped : node.transform;
3266
+ const rewrap = (rec) => isWrapped ? { value: rec } : rec;
3267
+ if (Array.isArray(value.translate)) {
3268
+ const rest = __spreadValues({}, value);
3269
+ delete rest.translate;
3270
+ const hasRest = Object.keys(rest).length > 0;
3271
+ if (stripBodyTranslateOnly) {
3272
+ if (hasRest) node.transform = rewrap(rest);
3273
+ else delete node.transform;
3274
+ } else {
3275
+ out.transform = rewrap({ translate: value.translate });
3276
+ if (hasRest) node.transform = rewrap(rest);
3277
+ else delete node.transform;
3278
+ }
3279
+ }
3091
3280
  }
3092
3281
  return out;
3093
3282
  }
@@ -3157,7 +3346,7 @@ function parseTranslateArgs(translateOp) {
3157
3346
  const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
3158
3347
  return [nums[0] || 0, nums[1] || 0];
3159
3348
  }
3160
- function splitTransformationEffect(fx) {
3349
+ function splitTransformByEffect(fx) {
3161
3350
  if (!fx) return {};
3162
3351
  const originOnOuter = needsOriginOnOuter(fx.translate);
3163
3352
  const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
@@ -3184,28 +3373,6 @@ function needsOriginOnOuter(translateAnim) {
3184
3373
  return false;
3185
3374
  }
3186
3375
 
3187
- // src/effects/util.ts
3188
- function genId(ctx, prefix) {
3189
- return "_lw_" + prefix + "_" + ctx.nextId++;
3190
- }
3191
- function stripHash2(href) {
3192
- return typeof href === "string" ? href.replace(/^#/, "") : void 0;
3193
- }
3194
- function indexById(node, map) {
3195
- var _a;
3196
- if (typeof node.id === "string") map.set(node.id, node);
3197
- (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3198
- }
3199
- function spliceDefs(root, defs) {
3200
- if (!defs.length) return;
3201
- const existing = root.children || (root.children = []);
3202
- existing.unshift({ type: "defs", children: defs });
3203
- }
3204
- var clone = deepClonePxNode;
3205
- function regenerateIdsInClone(root, ctx) {
3206
- return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3207
- }
3208
-
3209
3376
  // src/effects/gradientEffect.ts
3210
3377
  function applyFillGradientEffect(node, fx, ctx) {
3211
3378
  return applyGradient(node, fx, ctx, "fill");
@@ -3227,62 +3394,63 @@ function synthesiseGradientDef(fx, id, ctx) {
3227
3394
  id
3228
3395
  };
3229
3396
  if (fx.type === PxGradientType.linear) {
3230
- if (fx.p1) {
3231
- out.x1 = String(fx.p1[0]);
3232
- out.y1 = String(fx.p1[1]);
3233
- }
3234
- if (fx.p2) {
3235
- out.x2 = String(fx.p2[0]);
3236
- out.y2 = String(fx.p2[1]);
3237
- }
3397
+ applyGeomVec(out, "x1", "y1", fx.p1);
3398
+ applyGeomVec(out, "x2", "y2", fx.p2);
3238
3399
  } else {
3239
- if (fx.c) {
3240
- out.cx = String(fx.c[0]);
3241
- out.cy = String(fx.c[1]);
3242
- }
3243
- if (fx.r !== void 0) out.r = String(fx.r);
3244
- if (fx.fp) {
3245
- out.fx = String(fx.fp[0]);
3246
- out.fy = String(fx.fp[1]);
3247
- }
3400
+ applyGeomVec(out, "cx", "cy", fx.c);
3401
+ applyGeomNumber(out, "r", fx.r);
3402
+ applyGeomVec(out, "fx", "fy", fx.fp);
3248
3403
  }
3249
3404
  if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
3250
3405
  if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
3251
3406
  if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;
3252
- const animate = fx.animate;
3253
- if (animate) {
3254
- const mapped = {};
3255
- for (const wireKey of Object.keys(animate)) {
3256
- const attr = GRADIENT_ANIM_CHANNEL_TO_ATTR[wireKey];
3257
- if (attr) mapped[attr] = animate[wireKey];
3258
- }
3259
- if (Object.keys(mapped).length) out.animate = mapped;
3260
- }
3261
3407
  out.children = buildStopChildren(fx.stops, ctx);
3262
3408
  return out;
3263
3409
  }
3264
- var GRADIENT_ANIM_CHANNEL_TO_ATTR = {
3265
- gradientX1: "x1",
3266
- gradientY1: "y1",
3267
- gradientX2: "x2",
3268
- gradientY2: "y2",
3269
- gradientCx: "cx",
3270
- gradientCy: "cy",
3271
- gradientFx: "fx",
3272
- gradientFy: "fy",
3273
- gradientR: "r"
3274
- };
3410
+ function applyGeomVec(out, xAttr, yAttr, raw) {
3411
+ var _a, _b, _c;
3412
+ const read = readAnimatable(raw);
3413
+ if (read.kind === "absent" /* Absent */) return;
3414
+ if (read.kind === "static" /* Static */) {
3415
+ out[xAttr] = String(read.value[0]);
3416
+ out[yAttr] = String(read.value[1]);
3417
+ return;
3418
+ }
3419
+ const axisChannel = (idx) => {
3420
+ const block = {
3421
+ keyframes: read.keyframes.map((kf) => {
3422
+ const axisKf = { time: kf.time, value: Array.isArray(kf.value) ? kf.value[idx] : void 0 };
3423
+ if (kf.easing !== void 0) axisKf.easing = kf.easing;
3424
+ return axisKf;
3425
+ })
3426
+ };
3427
+ if (read.loop !== void 0) block.loop = read.loop;
3428
+ return block;
3429
+ };
3430
+ const animate = (_a = out.animate) != null ? _a : {};
3431
+ animate[xAttr] = axisChannel(0);
3432
+ animate[yAttr] = axisChannel(1);
3433
+ out.animate = animate;
3434
+ const baseline = (_c = read.base) != null ? _c : (_b = read.keyframes[0]) == null ? void 0 : _b.value;
3435
+ if (Array.isArray(baseline)) {
3436
+ out[xAttr] = String(baseline[0]);
3437
+ out[yAttr] = String(baseline[1]);
3438
+ }
3439
+ }
3440
+ function applyGeomNumber(out, attrName, raw) {
3441
+ const read = readAnimatable(raw);
3442
+ if (read.kind === "absent" /* Absent */) return;
3443
+ writeAnimatableChannel(out, attrName, read, { asString: true });
3444
+ }
3275
3445
  function buildStopChildren(stops, ctx) {
3276
3446
  var _a, _b, _c, _d;
3277
3447
  if (!stops) return [];
3278
- if (Array.isArray(stops)) return stops.map(staticStopNode);
3279
- if (typeof stops === "object" && Array.isArray(stops.value)) {
3280
- return stops.value.map(staticStopNode);
3281
- }
3282
- const animBlock = stops;
3283
- const kfs = animBlock.keyframes;
3284
- if (!Array.isArray(kfs) || !kfs.length) return [];
3285
- const loopFromSource = animBlock.loop;
3448
+ const read = readAnimatable(stops);
3449
+ if (read.kind === "absent" /* Absent */) return [];
3450
+ if (read.kind === "static" /* Static */) return Array.isArray(read.value) ? read.value.map(staticStopNode) : [];
3451
+ const kfs = read.keyframes;
3452
+ if (!kfs.length) return [];
3453
+ const loopFromSource = read.loop;
3286
3454
  let stopCount = 0;
3287
3455
  for (const kf of kfs) {
3288
3456
  const v = (_a = kf.value) != null ? _a : kf.v;
@@ -3359,38 +3527,62 @@ function formatOffset(o) {
3359
3527
 
3360
3528
  // src/effects/clipPathEffect.ts
3361
3529
  function applyClipPathEffect(node, fx, ctx) {
3530
+ var _a, _b;
3362
3531
  if (!fx || !fx.d && !fx.animate) return node;
3363
3532
  const clipId = genId(ctx, "clip");
3364
- const pathChild = { type: "path", d: fx.d };
3365
- if (fx.animate) pathChild.animate = { d: fx.animate };
3533
+ const pathChild = { type: "path" };
3534
+ const read = readAnimatable(fx.d);
3535
+ if (read.kind !== "absent" /* Absent */) {
3536
+ if (read.kind === "static" /* Static */) {
3537
+ pathChild.d = pathString(read.value);
3538
+ } else {
3539
+ writeAnimatableChannel(pathChild, "d", read);
3540
+ if (pathChild.d !== void 0) pathChild.d = pathString(pathChild.d);
3541
+ }
3542
+ }
3543
+ if (fx.animate && !((_a = pathChild.animate) == null ? void 0 : _a.d)) {
3544
+ const animate = (_b = pathChild.animate) != null ? _b : {};
3545
+ animate.d = fx.animate;
3546
+ pathChild.animate = animate;
3547
+ }
3366
3548
  ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
3367
3549
  node.clipPath = "url(#" + clipId + ")";
3368
3550
  return node;
3369
3551
  }
3552
+ function pathString(v) {
3553
+ if (typeof v === "string") return v;
3554
+ if (v && typeof v === "object" && typeof v.path === "string") return v.path;
3555
+ return void 0;
3556
+ }
3370
3557
 
3371
3558
  // src/effects/maskedByEffect.ts
3372
- function applyMaskedByEffect(node, fx, transformation, ctx) {
3559
+ function applyMaskedByEffect(node, fx, transformBy, ctx) {
3373
3560
  if (!fx) return node;
3374
- if (!fx.href) {
3375
- ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
3561
+ const sourceId = stripHash2(fx.sourceId);
3562
+ if (!sourceId) {
3563
+ ctx.errors.push("maskedBy.sourceId missing \u2014 cannot build mask");
3376
3564
  return node;
3377
3565
  }
3378
3566
  const maskId = genId(ctx, "mask");
3379
- let content = { type: "use", href: "#" + fx.href };
3380
- if (transformation) {
3381
- content = wrapInverseTransform(content, transformation, ctx);
3567
+ let content = { type: "use", href: "#" + sourceId };
3568
+ if (transformBy) {
3569
+ content = wrapInverseTransform(content, transformBy, ctx);
3382
3570
  } else if (hasAnimateTransform(node)) {
3383
3571
  content = wrapInverseAnimatedBodyTransform(content, node, ctx);
3384
3572
  } else {
3385
3573
  const bodyStatic = readTransformationFromBody(node);
3386
3574
  if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);
3387
3575
  }
3388
- const includeTargetOwn = transformation === void 0 && !nodeHasBodyTransform(node);
3389
- content = wrapAncestorChainCompensation(content, node, fx.href, ctx, includeTargetOwn);
3576
+ const includeTargetOwn = transformBy === void 0 && !nodeHasBodyTransform(node);
3577
+ content = wrapAncestorChainCompensation(content, node, sourceId, ctx, includeTargetOwn);
3390
3578
  const mask = { type: "mask", id: maskId, children: [content] };
3391
3579
  if (fx.maskType) mask.maskType = fx.maskType;
3392
3580
  if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
3393
3581
  if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
3582
+ if (fx.x !== void 0) mask.x = String(fx.x);
3583
+ if (fx.y !== void 0) mask.y = String(fx.y);
3584
+ if (fx.width !== void 0) mask.width = String(fx.width);
3585
+ if (fx.height !== void 0) mask.height = String(fx.height);
3394
3586
  ctx.defs.push(mask);
3395
3587
  node.mask = "url(#" + maskId + ")";
3396
3588
  return node;
@@ -3497,6 +3689,19 @@ function readTransformationFromBody(node) {
3497
3689
  if (parts.origin) out.origin = parts.origin;
3498
3690
  return Object.keys(out).length ? out : void 0;
3499
3691
  }
3692
+ if (node.transform && typeof node.transform === "object" && !node.transform.keyframes && !node.transform.kfs) {
3693
+ const wrapped = node.transform.value;
3694
+ const value = wrapped && typeof wrapped === "object" ? wrapped : node.transform;
3695
+ if (value && typeof value === "object") {
3696
+ const out = {};
3697
+ if (Array.isArray(value.translate)) out.translate = value.translate;
3698
+ if (typeof value.rotate === "number") out.rotate = value.rotate;
3699
+ if (typeof value.skew === "number") out.skew = value.skew;
3700
+ if (Array.isArray(value.scale)) out.scale = { value: value.scale };
3701
+ if (Array.isArray(value.origin)) out.origin = value.origin;
3702
+ return Object.keys(out).length ? out : void 0;
3703
+ }
3704
+ }
3500
3705
  return void 0;
3501
3706
  }
3502
3707
  function parseTransformStringToParts(s) {
@@ -3630,10 +3835,10 @@ function collectMaskAncestorChains(root, ctx) {
3630
3835
  const interestingNodes = /* @__PURE__ */ new Set();
3631
3836
  const collectInterestingNodes = (n) => {
3632
3837
  var _a, _b;
3633
- const href = (_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.href;
3634
- if (typeof href === "string") {
3838
+ const maskSourceId = stripHash2((_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.sourceId);
3839
+ if (typeof maskSourceId === "string") {
3635
3840
  interestingNodes.add(n);
3636
- const sourceNode = ctx.idMap.get(href);
3841
+ const sourceNode = ctx.idMap.get(maskSourceId);
3637
3842
  if (sourceNode) interestingNodes.add(sourceNode);
3638
3843
  }
3639
3844
  if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);
@@ -3659,8 +3864,9 @@ function extractTranslateOnly(node, ctx) {
3659
3864
  if (typeof tr === "string") {
3660
3865
  const parts = parseTranslateOnlyFromString(tr, ctx);
3661
3866
  if (parts) out.translate = parts;
3662
- } else if (tr && typeof tr === "object") {
3663
- const value = tr.value;
3867
+ } else if (tr && typeof tr === "object" && !tr.keyframes && !tr.kfs) {
3868
+ const wrapped = tr.value;
3869
+ const value = wrapped && typeof wrapped === "object" ? wrapped : tr;
3664
3870
  if (value && typeof value === "object" && Array.isArray(value.translate)) {
3665
3871
  out.translate = [value.translate[0] || 0, value.translate[1] || 0];
3666
3872
  }
@@ -3710,17 +3916,17 @@ function parseTranslateOnlyFromString(s, ctx) {
3710
3916
  var CONTENT_SUBREF = "content";
3711
3917
  function applyRefHref(node, clone2, ctx) {
3712
3918
  if (!clone2) return;
3713
- const baseId = clone2.baseId;
3714
- if (!baseId) {
3715
- if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing baseId");
3919
+ const sourceId = stripHash2(clone2.sourceId);
3920
+ if (!sourceId) {
3921
+ if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing sourceId");
3716
3922
  return;
3717
3923
  }
3718
- const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
3924
+ const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(sourceId) || sourceId : sourceId;
3719
3925
  node.href = "#" + targetId;
3720
3926
  }
3721
- function applyRefAndTransformationEffect(node, clone2, transformation, ctx) {
3927
+ function applyRefAndTransformationEffect(node, clone2, transformBy, ctx) {
3722
3928
  applyRefHref(node, clone2, ctx);
3723
- return applyTransformationEffect(node, transformation, ctx);
3929
+ return applyTransformByEffect(node, transformBy, ctx);
3724
3930
  }
3725
3931
 
3726
3932
  // src/effects/repeaterEffect.ts
@@ -3745,7 +3951,7 @@ function applyRepeaterEffect(node, fx, ctx) {
3745
3951
  for (let i = 1; i < copies; i++) {
3746
3952
  const baseClone = clone(base);
3747
3953
  const synthFx = synthesisePerCopyFx(fx, i);
3748
- const wrapped = synthFx ? applyTransformationEffect(baseClone, synthFx, ctx) : baseClone;
3954
+ const wrapped = synthFx ? applyTransformByEffect(baseClone, synthFx, ctx) : baseClone;
3749
3955
  children.push(wrapped);
3750
3956
  }
3751
3957
  const wrapper = { type: "g", children };
@@ -3757,10 +3963,13 @@ function applyRepeaterEffect(node, fx, ctx) {
3757
3963
  function synthesisePerCopyFx(fx, i) {
3758
3964
  const out = {};
3759
3965
  if (fx.translate !== void 0) {
3760
- out.translate = mapAnimatableVec2(fx.translate, (v) => [v[0] * i, v[1] * i]);
3966
+ out.translate = mapAnimatable(fx.translate, (v) => [v[0] * i, v[1] * i]);
3761
3967
  }
3762
3968
  if (fx.rotate !== void 0) {
3763
- out.rotate = mapAnimatableNumber(fx.rotate, (v) => v * i);
3969
+ out.rotate = mapAnimatable(fx.rotate, (v) => v * i);
3970
+ }
3971
+ if (fx.skew !== void 0) {
3972
+ out.skew = mapAnimatable(fx.skew, (v) => v * i);
3764
3973
  }
3765
3974
  if (fx.scale !== void 0) {
3766
3975
  out.scale = synthesiseScale(fx.scale, i);
@@ -3770,65 +3979,52 @@ function synthesisePerCopyFx(fx, i) {
3770
3979
  }
3771
3980
  return Object.keys(out).length ? out : void 0;
3772
3981
  }
3773
- function mapAnimatableVec2(raw, fn) {
3774
- if (Array.isArray(raw)) return fn(raw);
3775
- if (raw && typeof raw === "object") {
3776
- const obj = raw;
3777
- if (Array.isArray(obj.keyframes)) {
3778
- return __spreadProps(__spreadValues({}, obj), {
3779
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
3780
- });
3781
- }
3782
- if (obj.value !== void 0) {
3783
- return __spreadProps(__spreadValues({}, obj), { value: fn(obj.value) });
3784
- }
3982
+ function mapAnimatable(raw, fn, wrapStatic = false) {
3983
+ const read = readAnimatable(raw);
3984
+ if (read.kind === "absent" /* Absent */) return raw;
3985
+ if (read.kind === "static" /* Static */) {
3986
+ const mapped = fn(read.value);
3987
+ const wasRawStatic = typeof raw === "number" || Array.isArray(raw);
3988
+ return wasRawStatic && !wrapStatic ? mapped : { value: mapped };
3785
3989
  }
3786
- return raw;
3787
- }
3788
- function mapAnimatableNumber(raw, fn) {
3789
- if (typeof raw === "number") return fn(raw);
3790
- if (raw && typeof raw === "object") {
3791
- const obj = raw;
3792
- if (Array.isArray(obj.keyframes)) {
3793
- return __spreadProps(__spreadValues({}, obj), {
3794
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
3795
- });
3796
- }
3797
- if (obj.value !== void 0) {
3798
- return __spreadProps(__spreadValues({}, obj), { value: fn(obj.value) });
3799
- }
3800
- }
3801
- return raw;
3990
+ const out = {
3991
+ keyframes: read.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
3992
+ };
3993
+ if (read.loop !== void 0) out.loop = read.loop;
3994
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
3995
+ if (read.base !== void 0) out.value = fn(read.base);
3996
+ return out;
3802
3997
  }
3803
3998
  function synthesiseScale(raw, i) {
3804
- const scalePowerFromPercent = (v) => [Math.pow(v[0] / 100, i), Math.pow(v[1] / 100, i)];
3805
- const scalePowerFromUnits = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
3806
- if (Array.isArray(raw)) {
3807
- return { value: scalePowerFromPercent(raw) };
3808
- }
3809
- if (raw && typeof raw === "object") {
3810
- const obj = raw;
3811
- if (Array.isArray(obj.keyframes)) {
3812
- return __spreadProps(__spreadValues({}, obj), {
3813
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: scalePowerFromUnits(kf.value) }) : kf)
3814
- });
3815
- }
3816
- if (obj.value !== void 0) {
3817
- return __spreadProps(__spreadValues({}, obj), { value: scalePowerFromPercent(obj.value) });
3818
- }
3819
- }
3820
- return raw;
3999
+ const scalePower = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
4000
+ return mapAnimatable(raw, scalePower, true);
3821
4001
  }
3822
4002
 
3823
4003
  // src/effects/retimeEffect.ts
3824
4004
  var RETIME_MATERIALISATION_MODE_INLINE_G = false;
3825
4005
  function asRetime(r) {
3826
4006
  var _a, _b;
3827
- if (r.timeCrop) {
3828
- console.warn("retime: `timeCrop` is not supported yet and will be ignored");
3829
- }
3830
4007
  return { start: (_a = r.start) != null ? _a : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
3831
4008
  }
4009
+ var CROP_EDGE_MS = 1;
4010
+ function applyTimeCrop(useNode, crop, ctx) {
4011
+ const [start, end] = crop;
4012
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
4013
+ ctx.warnings.push("retime: timeCrop is not a pair of finite numbers \u2014 ignored");
4014
+ return;
4015
+ }
4016
+ const keyframes = end <= start ? [{ time: 0, value: 0 }] : [
4017
+ ...start > 0 ? [{ time: Math.max(0, start - CROP_EDGE_MS), value: 0 }] : [],
4018
+ { time: Math.max(0, start), value: 1 },
4019
+ { time: end, value: 1 },
4020
+ { time: end + CROP_EDGE_MS, value: 0 }
4021
+ ];
4022
+ const inner = __spreadValues({}, useNode);
4023
+ for (const k of Object.keys(useNode)) delete useNode[k];
4024
+ useNode.type = "g";
4025
+ useNode.children = [inner];
4026
+ useNode.animate = { opacity: { keyframes } };
4027
+ }
3832
4028
  function concatRetime(child, parent) {
3833
4029
  return {
3834
4030
  start: parent.start + parent.stretch * child.start,
@@ -3888,8 +4084,10 @@ function applyAllRetimeEffects(root, ctx) {
3888
4084
  for (const useNode of sites) {
3889
4085
  const retime = readCloneRetime(useNode);
3890
4086
  if (!retime) continue;
4087
+ const crop = retime.timeCrop;
3891
4088
  clearCloneRetime(useNode);
3892
4089
  materialiseRetime(useNode, asRetime(retime), ctx);
4090
+ if (crop) applyTimeCrop(useNode, crop, ctx);
3893
4091
  }
3894
4092
  }
3895
4093
  function materialiseRetime(useNode, retime, ctx) {
@@ -4136,14 +4334,11 @@ function createPathSampler(d) {
4136
4334
  // src/effects/textPathEffect.ts
4137
4335
  var EXTEND_MARGIN_FRAC = 0.15;
4138
4336
  function numRange(v) {
4139
- if (typeof v === "number") return { min: v, max: v };
4140
- if (v && typeof v === "object") {
4141
- const o = v;
4142
- if (typeof o.value === "number") return { min: o.value, max: o.value };
4143
- if (Array.isArray(o.keyframes) && o.keyframes.length) {
4144
- const vals = o.keyframes.map((k) => Number(k.value) || 0);
4145
- return { min: Math.min(...vals), max: Math.max(...vals) };
4146
- }
4337
+ const read = readAnimatable(v);
4338
+ if (read.kind === "static" /* Static */ && typeof read.value === "number") return { min: read.value, max: read.value };
4339
+ if (read.kind === "animated" /* Animated */ && read.keyframes.length) {
4340
+ const vals = read.keyframes.map((k) => Number(k.value) || 0);
4341
+ return { min: Math.min(...vals), max: Math.max(...vals) };
4147
4342
  }
4148
4343
  return { min: 0, max: 0 };
4149
4344
  }
@@ -4163,11 +4358,16 @@ function estimateTextAdvance(node) {
4163
4358
  var r3 = (n) => Math.round(n * 1e3) / 1e3;
4164
4359
  function shiftAnimatable(v, by) {
4165
4360
  if (!by || v === void 0 || v === null) return v;
4166
- if (typeof v === "number") return v + by;
4167
- const o = v;
4168
- if (typeof o.value === "number") return __spreadProps(__spreadValues({}, o), { value: o.value + by });
4169
- if (Array.isArray(o.keyframes)) return __spreadProps(__spreadValues({}, o), { keyframes: o.keyframes.map((k) => __spreadProps(__spreadValues({}, k), { value: (Number(k.value) || 0) + by })) });
4170
- return v;
4361
+ const read = readAnimatable(v);
4362
+ if (read.kind === "absent" /* Absent */) return v;
4363
+ if (read.kind === "static" /* Static */) return typeof v === "number" ? read.value + by : { value: read.value + by };
4364
+ const out = {
4365
+ keyframes: read.keyframes.map((k) => __spreadProps(__spreadValues({}, k), { value: (Number(k.value) || 0) + by }))
4366
+ };
4367
+ if (read.loop !== void 0) out.loop = read.loop;
4368
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
4369
+ if (read.base !== void 0) out.value = read.base + by;
4370
+ return out;
4171
4371
  }
4172
4372
  function extendedPathForBrowser(pathD, opts) {
4173
4373
  var _a;
@@ -4223,26 +4423,7 @@ function applyTextPathEffect(node, fx, ctx) {
4223
4423
  }
4224
4424
  function applyAnimatableNumber(node, attrName, raw) {
4225
4425
  if (raw === void 0 || raw === null) return;
4226
- if (typeof raw === "number") {
4227
- node[attrName] = String(raw);
4228
- return;
4229
- }
4230
- if (typeof raw === "object" && raw !== null) {
4231
- const obj = raw;
4232
- if (Array.isArray(obj.keyframes)) {
4233
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4234
- const animate = __spreadValues({}, prevAnimate || {});
4235
- const block = { keyframes: obj.keyframes };
4236
- if (obj.loop !== void 0) block.loop = obj.loop;
4237
- animate[attrName] = block;
4238
- node.animate = animate;
4239
- return;
4240
- }
4241
- if (typeof obj.value === "number") {
4242
- node[attrName] = String(obj.value);
4243
- return;
4244
- }
4245
- }
4426
+ writeAnimatableChannel(node, attrName, readAnimatable(raw), { asString: true });
4246
4427
  }
4247
4428
 
4248
4429
  // src/effects/elementFactory.ts
@@ -4436,13 +4617,13 @@ function materialiseGlyphTextHorizontal(node, opts) {
4436
4617
  if (y !== void 0) pen.y = y;
4437
4618
  pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
4438
4619
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4439
- const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
4620
+ const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4440
4621
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
4441
4622
  if (el.children) for (const ch of el.children) walk(ch, s);
4442
4623
  };
4443
4624
  const rootStyle = rootStyleOf(node);
4444
4625
  if (node.children) for (const ch of node.children) walk(ch, rootStyle);
4445
- const rootContent = (_c = str(node[TEXT_ATTR])) != null ? _c : str(node[TEXT_CONTENT_ATTR]);
4626
+ const rootContent = (_c = str(node[TEXT_CONTENT_ATTR])) != null ? _c : str(node[TEXT_ATTR]);
4446
4627
  if (rootContent && !((_d = node.children) == null ? void 0 : _d.length)) renderChars(rootContent, rootStyle);
4447
4628
  const anchor = str(node.textAnchor);
4448
4629
  if (anchor === "middle" || anchor === "end") {
@@ -4491,7 +4672,7 @@ function layoutGlyphTextChars(node, opts) {
4491
4672
  if (y !== void 0) pen.y = y;
4492
4673
  pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
4493
4674
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4494
- const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
4675
+ const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4495
4676
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
4496
4677
  if (el.children) for (const ch of el.children) walk(ch, s);
4497
4678
  };
@@ -4506,7 +4687,7 @@ function layoutGlyphTextChars(node, opts) {
4506
4687
  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 });
4507
4688
  }
4508
4689
  }
4509
- const rootContent = (_e = str(node[TEXT_ATTR])) != null ? _e : str(node[TEXT_CONTENT_ATTR]);
4690
+ const rootContent = (_e = str(node[TEXT_CONTENT_ATTR])) != null ? _e : str(node[TEXT_ATTR]);
4510
4691
  if (rootContent && !((_f = node.children) == null ? void 0 : _f.length)) renderChars(rootContent, rootStyle);
4511
4692
  const anchor = str(node.textAnchor);
4512
4693
  if (anchor === "middle" || anchor === "end") {
@@ -4534,7 +4715,7 @@ function layoutGlyphTextCharsAlongPath(node, pathD, opts) {
4534
4715
  const walk = (el, parentStyle) => {
4535
4716
  var _a2, _b2, _c;
4536
4717
  const s = resolveStyle2(el, parentStyle);
4537
- const content = (_a2 = str(el[TEXT_ATTR])) != null ? _a2 : str(el[TEXT_CONTENT_ATTR]);
4718
+ const content = (_a2 = str(el[TEXT_CONTENT_ATTR])) != null ? _a2 : str(el[TEXT_ATTR]);
4538
4719
  if (content && !((_b2 = el.children) == null ? void 0 : _b2.length)) {
4539
4720
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4540
4721
  const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
@@ -4588,7 +4769,7 @@ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
4588
4769
  const walk = (el, parentStyle) => {
4589
4770
  var _a, _b, _c;
4590
4771
  const s = resolveStyle2(el, parentStyle);
4591
- const content = (_a = str(el[TEXT_ATTR])) != null ? _a : str(el[TEXT_CONTENT_ATTR]);
4772
+ const content = (_a = str(el[TEXT_CONTENT_ATTR])) != null ? _a : str(el[TEXT_ATTR]);
4592
4773
  if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
4593
4774
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4594
4775
  if (gf) {
@@ -4812,9 +4993,9 @@ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pat
4812
4993
  }
4813
4994
 
4814
4995
  // src/effects/trimPathEffect.ts
4815
- function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4996
+ function applyTrimPathEffect(node, trimPath, ctx) {
4816
4997
  if (!trimPath) return node;
4817
- const trimAllAsOne = !!trimPath.trimAllAsOne;
4998
+ const combined = trimPath.subPaths === PxTrimSubPaths.combined;
4818
4999
  const leafEntries = [];
4819
5000
  const measure = (n) => {
4820
5001
  if (Array.isArray(n.children) && n.children.length > 0) {
@@ -4835,16 +5016,16 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4835
5016
  measure(node);
4836
5017
  if (!leafEntries.length) return node;
4837
5018
  let acc = 0;
4838
- const iterOrder = trimAllAsOne ? [...leafEntries].reverse() : leafEntries;
5019
+ const iterOrder = combined ? [...leafEntries].reverse() : leafEntries;
4839
5020
  for (const entry of iterOrder) {
4840
5021
  for (const sp of entry.subpaths) {
4841
- if (!trimAllAsOne) acc = 0;
5022
+ if (!combined) acc = 0;
4842
5023
  sp.startOffsetPx = acc;
4843
5024
  acc += sp.lengthPx;
4844
5025
  }
4845
5026
  }
4846
5027
  const chainLengthPx = acc;
4847
- if (trimAllAsOne && chainLengthPx < 1e-3) return node;
5028
+ if (combined && chainLengthPx < 1e-3) return node;
4848
5029
  const offsetReadRaw = readAnimatable(trimPath.offset);
4849
5030
  const offsetRead = offsetReadRaw.kind === "absent" /* Absent */ ? { kind: "static" /* Static */, value: 0 } : offsetReadRaw;
4850
5031
  const rangeReadRaw = readRangeWithCrossings(trimPath.range);
@@ -4856,18 +5037,18 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4856
5037
  if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {
4857
5038
  const entry = leafEntries[0];
4858
5039
  const sp = entry.subpaths[0];
4859
- const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
5040
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
4860
5041
  if (pathLengthPx < 1e-3) return node;
4861
- const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
5042
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
4862
5043
  return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);
4863
5044
  }
4864
5045
  const replacements = /* @__PURE__ */ new Map();
4865
5046
  for (const entry of leafEntries) {
4866
5047
  const newChildren = [];
4867
5048
  for (const sp of entry.subpaths) {
4868
- const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
5049
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
4869
5050
  if (pathLengthPx < 1e-3) continue;
4870
- const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
5051
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
4871
5052
  newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));
4872
5053
  }
4873
5054
  replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));
@@ -4974,22 +5155,8 @@ function computeAnimAttr(read, map) {
4974
5155
  };
4975
5156
  }
4976
5157
  function applyAttr(node, attrName, attr) {
4977
- var _a, _b;
4978
5158
  if (!attr) return;
4979
- if (attr.kind === "static" /* Static */) {
4980
- node[attrName] = attr.value;
4981
- return;
4982
- }
4983
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4984
- const animate = __spreadValues({}, prevAnimate || {});
4985
- const block = { keyframes: attr.keyframes };
4986
- if (attr.loop !== void 0) block.loop = attr.loop;
4987
- animate[attrName] = block;
4988
- node.animate = animate;
4989
- const firstKf = attr.keyframes[0];
4990
- if (firstKf && ((_a = firstKf.value) != null ? _a : firstKf.v) !== void 0) {
4991
- node[attrName] = (_b = firstKf.value) != null ? _b : firstKf.v;
4992
- }
5159
+ writeAnimatableChannel(node, attrName, attr);
4993
5160
  }
4994
5161
  var OPACITY_STEP_MS = 10;
4995
5162
  function computeOpacityFromRange(rangeRead) {
@@ -5173,9 +5340,9 @@ function applyPlayerEffects(root) {
5173
5340
  nextId: 0,
5174
5341
  contentRefInnerIds: /* @__PURE__ */ new Map(),
5175
5342
  maskAncestorChains: /* @__PURE__ */ new Map(),
5176
- // Resolved engine: `frames` ONLY when explicitly set; auto/webapi/unset →
5177
- // webapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5178
- engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi,
5343
+ // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →
5344
+ // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5345
+ engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.waapi,
5179
5346
  glyphs: (_b = getDefs(root)) == null ? void 0 : _b.glyphs
5180
5347
  };
5181
5348
  const working = clone(root);
@@ -5193,8 +5360,7 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5193
5360
  const originalId = typeof node.id === "string" ? node.id : void 0;
5194
5361
  const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
5195
5362
  if (!fx && !innerIdForContentRef) return node;
5196
- const { transformation, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5197
- const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
5363
+ const { transformBy, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5198
5364
  if (fx) delete node.effects;
5199
5365
  let n = node;
5200
5366
  let consumedByGlyphs = false;
@@ -5214,15 +5380,15 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5214
5380
  if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);
5215
5381
  n = applyFillGradientEffect(n, fillGradient, ctx);
5216
5382
  n = applyStrokeGradientEffect(n, strokeGradient, ctx);
5217
- n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
5383
+ n = applyTrimPathEffect(n, trimPath, ctx);
5218
5384
  n = applyRepeaterEffect(n, repeater, ctx);
5219
- n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
5385
+ n = applyMaskedByEffect(n, maskedBy, transformBy, ctx);
5220
5386
  n = applyClipPathEffect(n, clipPath, ctx);
5221
5387
  if (innerIdForContentRef) {
5222
5388
  applyRefHref(n, cloneFx, ctx);
5223
- n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
5389
+ n = splitForContentRef(n, transformBy, originalId, innerIdForContentRef, ctx);
5224
5390
  } else {
5225
- n = applyRefAndTransformationEffect(n, cloneFx, transformation, ctx);
5391
+ n = applyRefAndTransformationEffect(n, cloneFx, transformBy, ctx);
5226
5392
  }
5227
5393
  if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
5228
5394
  if (originalId) ctx.idMap.set(originalId, n);
@@ -5233,13 +5399,144 @@ function applyPlayerEffects_retime(node, ctx) {
5233
5399
  return node;
5234
5400
  }
5235
5401
 
5402
+ // src/PxOffsetPathMaterialiser.ts
5403
+ var kfTime = (kf) => {
5404
+ var _a, _b;
5405
+ return (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
5406
+ };
5407
+ var kfValue = (kf) => {
5408
+ var _a;
5409
+ return (_a = kf.v) != null ? _a : kf.value;
5410
+ };
5411
+ var kfEasing = (kf) => {
5412
+ var _a;
5413
+ return (_a = kf.e) != null ? _a : kf.easing;
5414
+ };
5415
+ var kfTangentIn = (kf) => {
5416
+ var _a;
5417
+ return (_a = kf.tangentIn) != null ? _a : kf.ti;
5418
+ };
5419
+ var kfTangentOut = (kf) => {
5420
+ var _a;
5421
+ return (_a = kf.tangentOut) != null ? _a : kf.to;
5422
+ };
5423
+ function cubicAt(p0, c1, c2, p1, t) {
5424
+ const u = 1 - t;
5425
+ const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;
5426
+ return [
5427
+ a * p0[0] + b * c1[0] + c * c2[0] + d * p1[0],
5428
+ a * p0[1] + b * c1[1] + c * c2[1] + d * p1[1]
5429
+ ];
5430
+ }
5431
+ function cubicLength(p0, c1, c2, p1, steps = 64) {
5432
+ let len = 0;
5433
+ let prev = p0;
5434
+ for (let i = 1; i <= steps; i++) {
5435
+ const pt = cubicAt(p0, c1, c2, p1, i / steps);
5436
+ len += Math.hypot(pt[0] - prev[0], pt[1] - prev[1]);
5437
+ prev = pt;
5438
+ }
5439
+ return len;
5440
+ }
5441
+ var fmt2 = (n) => {
5442
+ const r = Math.round(n * 1e4) / 1e4;
5443
+ return Object.is(r, -0) ? "0" : String(r);
5444
+ };
5445
+ function buildOffsetPath(propAnim) {
5446
+ var _a, _b, _c, _d;
5447
+ if (propAnim.alongPathMode !== "offsetPath") return void 0;
5448
+ const kfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
5449
+ if (!kfs || kfs.length < 2) return void 0;
5450
+ const first = kfValue(kfs[0]);
5451
+ const anchor = (first == null ? void 0 : first.origin) && first.origin.length >= 2 ? [first.origin[0], first.origin[1]] : [0, 0];
5452
+ const points = [];
5453
+ for (const kf of kfs) {
5454
+ const v = kfValue(kf);
5455
+ const tr = v == null ? void 0 : v.translate;
5456
+ if (!tr || tr.length < 2) return void 0;
5457
+ const parts = Object.keys(v);
5458
+ if (parts.some((p) => p !== "translate" && p !== "origin")) return void 0;
5459
+ const o = (_b = v == null ? void 0 : v.origin) != null ? _b : [0, 0];
5460
+ if (o[0] !== anchor[0] || o[1] !== anchor[1]) return void 0;
5461
+ points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);
5462
+ }
5463
+ if (!kfs.some((kf) => kfTangentIn(kf) || kfTangentOut(kf))) return void 0;
5464
+ let d = "M" + fmt2(points[0][0]) + "," + fmt2(points[0][1]);
5465
+ const segLens = [];
5466
+ for (let i = 0; i < points.length - 1; i++) {
5467
+ const p0 = points[i], p1 = points[i + 1];
5468
+ const to = (_c = kfTangentOut(kfs[i])) != null ? _c : [0, 0];
5469
+ const ti = (_d = kfTangentIn(kfs[i + 1])) != null ? _d : [0, 0];
5470
+ const c1 = [p0[0] + to[0], p0[1] + to[1]];
5471
+ const c2 = [p1[0] + ti[0], p1[1] + ti[1]];
5472
+ d += "C" + fmt2(c1[0]) + "," + fmt2(c1[1]) + "," + fmt2(c2[0]) + "," + fmt2(c2[1]) + "," + fmt2(p1[0]) + "," + fmt2(p1[1]);
5473
+ segLens.push(cubicLength(p0, c1, c2, p1));
5474
+ }
5475
+ const total = segLens.reduce((a, b) => a + b, 0);
5476
+ if (!(total > 0)) return void 0;
5477
+ const distanceKfs = [];
5478
+ let cum = 0;
5479
+ for (let i = 0; i < kfs.length; i++) {
5480
+ if (i > 0) cum += segLens[i - 1];
5481
+ const out = { t: kfTime(kfs[i]), v: cum / total };
5482
+ const e = kfEasing(kfs[i]);
5483
+ if (e !== void 0) out.e = e;
5484
+ distanceKfs.push(out);
5485
+ }
5486
+ return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor };
5487
+ }
5488
+ function materialiseOffsetPathsInTree(root) {
5489
+ const walk = (node) => {
5490
+ var _a;
5491
+ let out = node;
5492
+ const anim = node.animate;
5493
+ const transform = anim == null ? void 0 : anim["transform"];
5494
+ if (transform) {
5495
+ const built = buildOffsetPath(transform);
5496
+ if (built) {
5497
+ const newAnimate = __spreadValues({}, anim);
5498
+ delete newAnimate["transform"];
5499
+ const distance = { keyframes: built.distanceKfs };
5500
+ if (transform.loop !== void 0) distance.loop = transform.loop;
5501
+ newAnimate["offsetDistance"] = distance;
5502
+ const staticTr = node.transform;
5503
+ let newTransform = staticTr;
5504
+ if (staticTr && typeof staticTr === "object") {
5505
+ const t = __spreadValues({}, staticTr);
5506
+ delete t["translate"];
5507
+ delete t["origin"];
5508
+ newTransform = Object.keys(t).length ? t : void 0;
5509
+ }
5510
+ out = __spreadProps(__spreadValues({}, node), {
5511
+ animate: newAnimate,
5512
+ style: __spreadProps(__spreadValues({}, node.style), {
5513
+ offsetPath: "path('" + built.pathStr + "')",
5514
+ offsetAnchor: fmt2(built.anchor[0]) + "px " + fmt2(built.anchor[1]) + "px",
5515
+ offsetRotate: built.autoOrient ? "auto" : "0deg",
5516
+ offsetDistance: "0%"
5517
+ })
5518
+ });
5519
+ if (newTransform !== void 0) out.transform = newTransform;
5520
+ else delete out.transform;
5521
+ }
5522
+ }
5523
+ if ((_a = out.children) == null ? void 0 : _a.length) {
5524
+ const children = out.children.map(walk);
5525
+ if (children.some((c, i) => c !== out.children[i])) out = __spreadProps(__spreadValues({}, out), { children });
5526
+ }
5527
+ return out;
5528
+ };
5529
+ return walk(root);
5530
+ }
5531
+
5236
5532
  // src/PxAnimatorMaterialiseAll.ts
5237
5533
  function materialiseAllInTree(doc, engine, opts) {
5238
5534
  var _a, _b;
5239
5535
  let root = applyPlayerEffects(doc).root;
5536
+ root = materialiseOffsetPathsInTree(root);
5240
5537
  const duration = (_b = (_a = getAnimatorConfig(root)) == null ? void 0 : _a.duration) != null ? _b : DEFAULT_DURATION_MS;
5241
5538
  root = materialiseInternalLoopsInTree(root, duration);
5242
- if (engine === PxAnimatorEngine.webapi) {
5539
+ if (engine === PxAnimatorEngine.waapi) {
5243
5540
  root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
5244
5541
  root = materialiseAnimatedUseInstances(root);
5245
5542
  root = pruneUnreferencedDefs(root);
@@ -5628,6 +5925,7 @@ function evalTransformValue(v, t) {
5628
5925
  if (typeof v === "string") return parseTransformString(v);
5629
5926
  if (v.keyframes) return partsToMatrix(interpParts(v.keyframes, t));
5630
5927
  if (v.value) return partsToMatrix(v.value);
5928
+ if (typeof v === "object" && !Array.isArray(v)) return partsToMatrix(v);
5631
5929
  return IDENTITY;
5632
5930
  }
5633
5931
  function nodeMatrix(node, t) {
@@ -5768,6 +6066,8 @@ function subtractMultiset(a, b) {
5768
6066
  DEFAULT_DURATION_MS,
5769
6067
  DISALLOWED_SVG_TAGS_LOWER,
5770
6068
  INTERNAL_ATTRS,
6069
+ LOOP_JUMP_SHIFT_MS,
6070
+ PCT_BASED_ATTR_NAMES,
5771
6071
  PX_ANIM_ATTR_NAME,
5772
6072
  PX_ANIM_SRC_ATTR_NAME,
5773
6073
  PX_TRANSFORM_PART_KEYS,
@@ -5785,12 +6085,13 @@ function subtractMultiset(a, b) {
5785
6085
  PxEffectsSchema,
5786
6086
  PxElementAnimationSchema,
5787
6087
  PxFillGradientEffectSchema,
5788
- PxGradientGeometryAnimationSchema,
5789
6088
  PxGradientSpreadMethod,
5790
6089
  PxGradientStopSchema,
5791
6090
  PxGradientType,
5792
6091
  PxGradientUnits,
5793
6092
  PxKeyframeSchema,
6093
+ PxKeyframeValueSchema,
6094
+ PxLoopExtend,
5794
6095
  PxLoopSchema,
5795
6096
  PxMaskedByEffectSchema,
5796
6097
  PxNodeBase,
@@ -5802,11 +6103,12 @@ function subtractMultiset(a, b) {
5802
6103
  PxSvgNodeExtra,
5803
6104
  PxTextEffectSchema,
5804
6105
  PxTextPathEffectSchema,
6106
+ PxTransformByEffectSchema,
5805
6107
  PxTransformPartsSchema,
5806
6108
  PxTransformValueSchema,
5807
- PxTransformationEffectSchema,
5808
6109
  PxTriggerSchema,
5809
6110
  PxTrimPathEffectSchema,
6111
+ PxTrimSubPaths,
5810
6112
  STYLE_ATTR_NAMES,
5811
6113
  TEXT_ATTR,
5812
6114
  TEXT_CONTENT_ATTR,