@pixodesk/svg-animator-web 1.0.22 → 1.0.26

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
@@ -73,6 +73,7 @@ __export(index_exports, {
73
73
  PxGradientType: () => PxGradientType,
74
74
  PxGradientUnits: () => PxGradientUnits,
75
75
  PxKeyframeSchema: () => PxKeyframeSchema,
76
+ PxKeyframeValueSchema: () => PxKeyframeValueSchema,
76
77
  PxLoopSchema: () => PxLoopSchema,
77
78
  PxMaskedByEffectSchema: () => PxMaskedByEffectSchema,
78
79
  PxNodeBase: () => PxNodeBase,
@@ -84,9 +85,9 @@ __export(index_exports, {
84
85
  PxSvgNodeExtra: () => PxSvgNodeExtra,
85
86
  PxTextEffectSchema: () => PxTextEffectSchema,
86
87
  PxTextPathEffectSchema: () => PxTextPathEffectSchema,
88
+ PxTransformByEffectSchema: () => PxTransformByEffectSchema,
87
89
  PxTransformPartsSchema: () => PxTransformPartsSchema,
88
90
  PxTransformValueSchema: () => PxTransformValueSchema,
89
- PxTransformationEffectSchema: () => PxTransformationEffectSchema,
90
91
  PxTriggerSchema: () => PxTriggerSchema,
91
92
  PxTrimPathEffectSchema: () => PxTrimPathEffectSchema,
92
93
  STYLE_ATTR_NAMES: () => STYLE_ATTR_NAMES,
@@ -292,8 +293,9 @@ var Union = class extends Base {
292
293
  }
293
294
  isValid(raw, ctx, path) {
294
295
  var _a;
295
- if (this.schemas.some((s) => s.isValid(raw))) return true;
296
- 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));
296
+ const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
297
+ if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
298
+ 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));
297
299
  return false;
298
300
  }
299
301
  _canSanitize(raw) {
@@ -369,6 +371,7 @@ var Obj = class extends Base {
369
371
  if (ctx == null ? void 0 : ctx.strict) {
370
372
  for (const key of Object.keys(obj)) {
371
373
  if (key in this._shape) continue;
374
+ if (obj[key] === void 0) continue;
372
375
  p.push(key);
373
376
  ctx.errors.push(pathStr(p) + ": unexpected extra key");
374
377
  p.pop();
@@ -509,6 +512,23 @@ var Any = class extends Base {
509
512
  return true;
510
513
  }
511
514
  };
515
+ var Defined = class extends Base {
516
+ constructor() {
517
+ super(...arguments);
518
+ this._default = void 0;
519
+ }
520
+ sanitize(raw) {
521
+ return raw;
522
+ }
523
+ isValid(raw, ctx, path) {
524
+ if (raw !== void 0) return true;
525
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": required value is missing");
526
+ return false;
527
+ }
528
+ _canSanitize(raw) {
529
+ return raw !== void 0;
530
+ }
531
+ };
512
532
  var Lazy = class extends Base {
513
533
  constructor(fn, _default) {
514
534
  super();
@@ -620,6 +640,8 @@ var px = {
620
640
  record: (value) => new Rec(value),
621
641
  /** Passes anything through unchanged — always valid. */
622
642
  any: () => new Any(),
643
+ /** Anything EXCEPT `undefined` — an open type whose presence is required (V6). */
644
+ defined: () => new Defined(),
623
645
  /** Fixed-length tuple — validates element count and each position individually. */
624
646
  tuple: (schemas) => new Tuple(schemas),
625
647
  /** Defers schema creation — required for recursive types. Must supply a default value. */
@@ -629,13 +651,52 @@ var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
629
651
  var PX_ANIM_ATTR_NAME = "_px_animator";
630
652
  var PxAnimatorMode = {
631
653
  auto: "auto",
632
- webapi: "webapi",
654
+ waapi: "waapi",
633
655
  frames: "frames"
634
656
  };
635
657
  var PxAnimatorEngine = {
636
- webapi: PxAnimatorMode.webapi,
658
+ waapi: PxAnimatorMode.waapi,
637
659
  frames: PxAnimatorMode.frames
638
660
  };
661
+ var PxLoopExtend = {
662
+ /** Segment from the START; the animation is extended BEFORE the first keyframe
663
+ * (intro loops that run before the main timeline begins). */
664
+ before: "before",
665
+ /** DEFAULT — segment from the END; extended AFTER the last keyframe (idle/outro
666
+ * loops that continue once the main timeline has finished). */
667
+ after: "after"
668
+ };
669
+ var PxMaskType = {
670
+ luminance: "luminance",
671
+ alpha: "alpha"
672
+ };
673
+ var PxUnits = {
674
+ userSpaceOnUse: "userSpaceOnUse",
675
+ objectBoundingBox: "objectBoundingBox"
676
+ };
677
+ var PxCloneType = {
678
+ content: "content"
679
+ };
680
+ var PxPathOverflow = {
681
+ clip: "clip",
682
+ extend: "extend"
683
+ };
684
+ var PxLengthAdjust = {
685
+ spacing: "spacing",
686
+ spacingAndGlyphs: "spacingAndGlyphs"
687
+ };
688
+ var PxTextPathMethod = {
689
+ align: "align",
690
+ stretch: "stretch"
691
+ };
692
+ var PxTextPathSpacing = {
693
+ auto: "auto",
694
+ exact: "exact"
695
+ };
696
+ var PxTrimSubPaths = {
697
+ separate: "separate",
698
+ combined: "combined"
699
+ };
639
700
  var TEXT_ATTR = "text";
640
701
  var TEXT_CONTENT_ATTR = "textContent";
641
702
  var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
@@ -644,9 +705,49 @@ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
644
705
  "animator",
645
706
  "meta",
646
707
  "animate",
708
+ "effects",
647
709
  TEXT_ATTR,
648
710
  TEXT_CONTENT_ATTR
649
711
  ]);
712
+ var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
713
+ var PxGradientUnits = {
714
+ userSpaceOnUse: "userSpaceOnUse",
715
+ objectBoundingBox: "objectBoundingBox"
716
+ };
717
+ var PxGradientSpreadMethod = {
718
+ pad: "pad",
719
+ reflect: "reflect",
720
+ repeat: "repeat"
721
+ };
722
+ var PxGradientType = {
723
+ linear: "linear",
724
+ radial: "radial"
725
+ };
726
+ function isPxElementFileFormat(fileJson) {
727
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
728
+ return false;
729
+ }
730
+ return fileJson["type"] === "svg";
731
+ }
732
+ function getAnimatorConfig(doc) {
733
+ var _a;
734
+ return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
735
+ }
736
+ function getDefs(doc) {
737
+ var _a;
738
+ if (!doc) return void 0;
739
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
740
+ }
741
+ function getBindings(doc) {
742
+ var _a;
743
+ if (!doc) return void 0;
744
+ const animateById = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animateById;
745
+ if (!animateById) return void 0;
746
+ return Object.entries(animateById).map(([id, anim]) => ({ id, animate: anim }));
747
+ }
748
+ function getChildren(doc) {
749
+ return doc == null ? void 0 : doc.children;
750
+ }
650
751
  var PxEasingOrRefSchema = px.union([
651
752
  px.string(),
652
753
  px.tuple([px.number(), px.number(), px.number(), px.number()])
@@ -658,13 +759,15 @@ var PxKeyframeValueSchema = implementsInterface()(px.union([
658
759
  px.array(px.number()),
659
760
  px.lazy(() => PxTransformPartsSchema, {}),
660
761
  px.object({ path: px.string() }),
661
- px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] })
762
+ px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
763
+ // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
764
+ px.lazy(() => px.array(PxGradientStopSchema), [])
662
765
  ]));
663
766
  var PxKeyframeSchema = implementsInterface()(px.object({
664
767
  time: px.number().optional(),
665
768
  t: px.number().optional(),
666
- value: px.any().optional(),
667
- v: px.any().optional(),
769
+ value: PxKeyframeValueSchema.optional(),
770
+ v: PxKeyframeValueSchema.optional(),
668
771
  easing: PxEasingOrRefSchema.optional(),
669
772
  e: PxEasingOrRefSchema.optional(),
670
773
  tangentOut: px.tuple([px.number(), px.number()]).optional(),
@@ -678,16 +781,16 @@ var PxKeyframeSchema = implementsInterface()(px.object({
678
781
  }));
679
782
  var PxLoopSchema = implementsInterface()(px.object({
680
783
  segmentCount: px.number().optional(),
681
- before: px.boolean().optional(),
784
+ extend: px.enum([PxLoopExtend.before, PxLoopExtend.after]).optional(),
682
785
  alternate: px.boolean().optional()
683
786
  }));
684
787
  var PxPropertyAnimationSchema = implementsInterface()(px.object({
788
+ value: PxKeyframeValueSchema.optional(),
685
789
  keyframes: px.array(PxKeyframeSchema).optional(),
686
790
  kfs: px.array(PxKeyframeSchema).optional(),
687
791
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
688
792
  autoOrient: px.boolean().optional()
689
793
  }));
690
- var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
691
794
  var PxTransformPartsSchema = implementsInterface()(px.object({
692
795
  translate: px.tuple([px.number(), px.number()]).optional(),
693
796
  rotate: px.number().optional(),
@@ -697,6 +800,7 @@ var PxTransformPartsSchema = implementsInterface()(px.object({
697
800
  }));
698
801
  var PxTransformValueSchema = px.union([
699
802
  px.string(),
803
+ PxTransformPartsSchema,
700
804
  px.object({ value: PxTransformPartsSchema }),
701
805
  PxPropertyAnimationSchema
702
806
  ]);
@@ -731,9 +835,11 @@ var PxDefsSchema = implementsInterface()(px.object({
731
835
  glyphs: px.record(PxGlyphFontSchema).optional()
732
836
  }));
733
837
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
734
- mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.webapi, PxAnimatorMode.frames]).optional(),
838
+ mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.waapi, PxAnimatorMode.frames]).optional(),
735
839
  duration: px.number().optional(),
736
840
  delay: px.number().optional(),
841
+ // LAW (SCHEMA-DESIGN R3, S10): the ONE sanctioned string-in-number union
842
+ // (CSS animation-iteration-count familiarity) — do not add more mixed unions.
737
843
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
738
844
  fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
739
845
  direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
@@ -741,9 +847,8 @@ var PxAnimatorConfigSchema = implementsInterface()(px.object({
741
847
  trigger: PxTriggerSchema.optional(),
742
848
  resetOnFinish: px.boolean().optional(),
743
849
  definitions: PxDefsSchema.optional(),
744
- animate: px.record(PxElementAnimationSchema).optional(),
745
- timeline: px.string().optional(),
746
- debug: px.boolean().optional(),
850
+ animateById: px.record(PxElementAnimationSchema).optional(),
851
+ timelineSource: px.string().optional(),
747
852
  debugInstName: px.string().optional()
748
853
  }));
749
854
  var PxBindingSchema = implementsInterface()(px.object({
@@ -753,26 +858,29 @@ var PxBindingSchema = implementsInterface()(px.object({
753
858
  var PxAttrValueSchema = px.union([
754
859
  px.string(),
755
860
  px.number(),
756
- px.object({ value: px.any() }),
757
- PxPropertyAnimationSchema
861
+ px.array(px.number()),
862
+ // Structured static — `{value: …}` (read-accepted transitional spelling, S1).
863
+ // `defined`, not `any`: the KEY's presence is what identifies this branch (V6).
864
+ px.object({ value: px.defined() }),
865
+ // Bare transform parts record — the canonical static `transform` on the wire (T2).
866
+ PxTransformPartsSchema
758
867
  ]);
759
868
  var PxAnimatableNumberSchema = px.union([
760
869
  px.number(),
761
870
  px.object({ value: px.number() }),
762
- px.object({
763
- keyframes: px.array(PxKeyframeSchema),
764
- autoOrient: px.boolean().optional()
765
- })
871
+ PxPropertyAnimationSchema
766
872
  ]);
767
873
  var PxAnimatableVec2Schema = px.union([
768
874
  px.tuple([px.number(), px.number()]),
769
875
  px.object({ value: px.tuple([px.number(), px.number()]) }),
770
- px.object({
771
- keyframes: px.array(PxKeyframeSchema),
772
- autoOrient: px.boolean().optional()
773
- })
876
+ PxPropertyAnimationSchema
774
877
  ]);
775
- var PxTransformationEffectSchema = implementsInterface()(px.object({
878
+ var PxAnimatableStringSchema = px.union([
879
+ px.string(),
880
+ px.object({ value: px.string() }),
881
+ PxPropertyAnimationSchema
882
+ ]);
883
+ var PxTransformByEffectSchema = implementsInterface()(px.object({
776
884
  translate: PxAnimatableVec2Schema.optional(),
777
885
  rotate: PxAnimatableNumberSchema.optional(),
778
886
  scale: PxAnimatableVec2Schema.optional(),
@@ -780,51 +888,46 @@ var PxTransformationEffectSchema = implementsInterface()(px.object({
780
888
  origin: PxAnimatableVec2Schema.optional()
781
889
  }));
782
890
  var PxRepeaterEffectSchema = implementsInterface()(px.object({
891
+ // STATIC config, not a channel (V2/SCHEMA-DESIGN R5): the copy COUNT is read
892
+ // once at expansion time and never sampled — plain number, no `keyframes`.
783
893
  copies: px.number().optional(),
784
894
  translate: PxAnimatableVec2Schema.optional(),
785
895
  rotate: PxAnimatableNumberSchema.optional(),
896
+ skew: PxAnimatableNumberSchema.optional(),
786
897
  scale: PxAnimatableVec2Schema.optional(),
787
898
  origin: PxAnimatableVec2Schema.optional()
788
899
  }));
789
900
  var PxMaskedByEffectSchema = implementsInterface()(px.object({
790
- href: px.string().optional(),
791
- maskType: px.string().optional(),
792
- maskUnits: px.string().optional(),
793
- maskContentUnits: px.string().optional()
901
+ sourceId: px.string().optional(),
902
+ maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha]).optional(),
903
+ maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
904
+ maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
905
+ x: px.number().optional(),
906
+ y: px.number().optional(),
907
+ width: px.number().optional(),
908
+ height: px.number().optional()
794
909
  }));
795
910
  var PxClipPathEffectSchema = implementsInterface()(px.object({
796
- d: px.string().optional(),
911
+ d: PxAnimatableStringSchema.optional(),
797
912
  animate: PxPropertyAnimationSchema.optional()
798
913
  }));
799
914
  var PxTrimPathEffectSchema = implementsInterface()(px.object({
800
915
  offset: PxAnimatableNumberSchema.optional(),
801
916
  range: PxAnimatableVec2Schema.optional(),
802
- trimAllAsOne: px.boolean().optional()
917
+ subPaths: px.enum([PxTrimSubPaths.separate, PxTrimSubPaths.combined]).optional()
803
918
  }));
804
919
  var PxRetimeEffectSchema = implementsInterface()(px.object({
805
- baseId: px.string().optional(),
920
+ sourceId: px.string().optional(),
806
921
  start: px.number().optional(),
807
922
  stretch: px.number().optional(),
808
923
  timeCrop: px.tuple([px.number(), px.number()]).optional()
809
924
  }));
810
925
  var PxCloneEffectSchema = implementsInterface()(px.object({
811
- type: px.string().optional(),
812
- baseId: px.string().optional(),
926
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
927
+ type: px.enum([PxCloneType.content]).optional(),
928
+ sourceId: px.string().optional(),
813
929
  retime: PxRetimeEffectSchema.optional()
814
930
  }));
815
- var PxGradientUnits = {
816
- userSpaceOnUse: "userSpaceOnUse",
817
- objectBoundingBox: "objectBoundingBox"
818
- };
819
- var PxGradientSpreadMethod = {
820
- pad: "pad",
821
- reflect: "reflect",
822
- repeat: "repeat"
823
- };
824
- var PxGradientType = {
825
- linear: "linear",
826
- radial: "radial"
827
- };
828
931
  var PxGradientStopSchema = implementsInterface()(px.object({
829
932
  offset: px.number(),
830
933
  color: px.string()
@@ -832,39 +935,28 @@ var PxGradientStopSchema = implementsInterface()(px.object({
832
935
  var PxAnimatableGradientStopsSchema = px.union([
833
936
  px.array(PxGradientStopSchema),
834
937
  px.object({ value: px.array(PxGradientStopSchema) }),
835
- px.object({ keyframes: px.array(PxKeyframeSchema) })
938
+ PxPropertyAnimationSchema
836
939
  ]);
837
- var PxGradientGeometryAnimationSchema = implementsInterface()(px.object({
838
- gradientX1: PxPropertyAnimationSchema.optional(),
839
- gradientY1: PxPropertyAnimationSchema.optional(),
840
- gradientX2: PxPropertyAnimationSchema.optional(),
841
- gradientY2: PxPropertyAnimationSchema.optional(),
842
- gradientCx: PxPropertyAnimationSchema.optional(),
843
- gradientCy: PxPropertyAnimationSchema.optional(),
844
- gradientFx: PxPropertyAnimationSchema.optional(),
845
- gradientFy: PxPropertyAnimationSchema.optional(),
846
- gradientR: PxPropertyAnimationSchema.optional()
847
- }));
848
940
  var PxFillGradientEffectSchema = implementsInterface()(px.object({
941
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
849
942
  type: px.enum([PxGradientType.linear, PxGradientType.radial]),
850
- p1: px.tuple([px.number(), px.number()]).optional(),
851
- p2: px.tuple([px.number(), px.number()]).optional(),
852
- c: px.tuple([px.number(), px.number()]).optional(),
853
- r: px.number().optional(),
854
- fp: px.tuple([px.number(), px.number()]).optional(),
943
+ p1: PxAnimatableVec2Schema.optional(),
944
+ p2: PxAnimatableVec2Schema.optional(),
945
+ c: PxAnimatableVec2Schema.optional(),
946
+ r: PxAnimatableNumberSchema.optional(),
947
+ fp: PxAnimatableVec2Schema.optional(),
855
948
  stops: PxAnimatableGradientStopsSchema.optional(),
856
- gradientUnits: px.string().optional(),
857
- spreadMethod: px.string().optional(),
858
- gradientTransform: px.string().optional(),
859
- animate: PxGradientGeometryAnimationSchema.optional()
949
+ gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
950
+ spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
951
+ gradientTransform: px.string().optional()
860
952
  }));
861
953
  var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
862
954
  var PxTextPathEffectSchema = implementsInterface()(px.object({
863
955
  path: px.string(),
864
- pathOverflow: px.string().optional(),
865
- lengthAdjust: px.string().optional(),
866
- method: px.string().optional(),
867
- spacing: px.string().optional(),
956
+ pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
957
+ lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
958
+ method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
959
+ spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
868
960
  startOffset: PxAnimatableNumberSchema.optional(),
869
961
  textLength: PxAnimatableNumberSchema.optional()
870
962
  }));
@@ -872,13 +964,12 @@ var PxTextEffectSchema = implementsInterface()(px.object({
872
964
  useGlyphs: px.boolean().optional()
873
965
  }));
874
966
  var PxEffectsSchema = implementsInterface()(px.object({
875
- transformation: PxTransformationEffectSchema.optional(),
967
+ transformBy: PxTransformByEffectSchema.optional(),
876
968
  repeater: PxRepeaterEffectSchema.optional(),
877
969
  maskedBy: PxMaskedByEffectSchema.optional(),
878
970
  clipPath: PxClipPathEffectSchema.optional(),
879
971
  trimPath: PxTrimPathEffectSchema.optional(),
880
972
  clone: PxCloneEffectSchema.optional(),
881
- isCombinedShape: px.boolean().optional(),
882
973
  fillGradient: PxFillGradientEffectSchema.optional(),
883
974
  strokeGradient: PxStrokeGradientEffectSchema.optional(),
884
975
  textPath: PxTextPathEffectSchema.optional(),
@@ -902,6 +993,14 @@ function validateNodeEffects(root, opts) {
902
993
  return warnings;
903
994
  }
904
995
  var PxNodeBase = px.openObject({
996
+ // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
997
+ // kind of thing is this", discriminated by its CARRIER — here the node TAG
998
+ // (`rect`, `text`), and inside a sub-object that object's kind (`clone.type`,
999
+ // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
1000
+ // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
1001
+ // would add words that all mean "type" and still need the carrier to read.
1002
+ // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
1003
+ // (issues V3), never of distinct key names.
905
1004
  type: px.string(),
906
1005
  id: px.string().optional(),
907
1006
  meta: px.any().optional(),
@@ -911,7 +1010,7 @@ var PxNodeBase = px.openObject({
911
1010
  effects: PxEffectsSchema.optional(),
912
1011
  // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
913
1012
  // string ref / array of refs / inline definition / mixed array; mirrors
914
- // `animator.animate` map values and what `processNode` resolves at runtime.
1013
+ // `animator.animateById` map values and what `processNode` resolves at runtime.
915
1014
  animate: PxElementAnimationSchema.optional(),
916
1015
  style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
917
1016
  }, PxAttrValueSchema);
@@ -919,8 +1018,10 @@ var PxNodeSchema = px.openObject(__spreadProps2(__spreadValues2({}, PxNodeBase._
919
1018
  children: px.lazy(() => px.array(PxNodeSchema), []).optional()
920
1019
  }), PxAttrValueSchema);
921
1020
  var PxSvgNodeExtra = px.object({
922
- width: px.number().optional(),
923
- height: px.number().optional(),
1021
+ // `"100%"` and other SVG length strings are legal here — a number-only slot rejected
1022
+ // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.
1023
+ width: px.union([px.number(), px.string()]).optional(),
1024
+ height: px.union([px.number(), px.string()]).optional(),
924
1025
  viewBox: px.string().optional(),
925
1026
  animator: PxAnimatorConfigSchema.optional()
926
1027
  });
@@ -935,35 +1036,10 @@ var PxBezierPathSchema = implementsInterface()(px.object({
935
1036
  o: px.array(px.array(px.number())).optional(),
936
1037
  c: px.boolean().optional()
937
1038
  }));
938
- function isPxElementFileFormat(fileJson) {
939
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
940
- return false;
941
- }
942
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
943
- }
944
1039
  function isPxElementFileFormatDeep(fileJson) {
945
1040
  const valid = PxAnimatedSvgDocumentSchema.isValid(fileJson);
946
1041
  return { valid, errors: valid ? [] : ["Document failed schema validation"] };
947
1042
  }
948
- function getAnimatorConfig(doc) {
949
- var _a, _b;
950
- 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);
951
- }
952
- function getDefs(doc) {
953
- var _a;
954
- if (!doc) return void 0;
955
- return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
956
- }
957
- function getBindings(doc) {
958
- var _a;
959
- if (!doc) return void 0;
960
- const animate = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animate;
961
- if (!animate) return void 0;
962
- return Object.entries(animate).map(([id, anim]) => ({ id, animate: anim }));
963
- }
964
- function getChildren(doc) {
965
- return doc == null ? void 0 : doc.children;
966
- }
967
1043
  function bezierToSvgPath(path, forceCurves = false) {
968
1044
  var _a, _b, _c, _d;
969
1045
  const v = path.v;
@@ -1414,7 +1490,7 @@ function generateNewIds(doc) {
1414
1490
  "flood-color",
1415
1491
  "lighting-color"
1416
1492
  ]);
1417
- const directIdRefAttrs = /* @__PURE__ */ new Set(["baseId", "targetId", "boundElementId"]);
1493
+ const directIdRefAttrs = /* @__PURE__ */ new Set(["sourceId", "targetId", "boundElementId"]);
1418
1494
  function collectIds(node) {
1419
1495
  if (!node || typeof node !== "object") return;
1420
1496
  if (node.id && typeof node.id === "string") {
@@ -1452,9 +1528,10 @@ function generateNewIds(doc) {
1452
1528
  } else if (urlRefAttrs.has(key)) {
1453
1529
  node[key] = replaceUrlRefs(value, idMap);
1454
1530
  } else if (directIdRefAttrs.has(key)) {
1455
- const newId = idMap.get(value);
1531
+ const hasHash = value.startsWith("#");
1532
+ const newId = idMap.get(hasHash ? value.slice(1) : value);
1456
1533
  if (newId) {
1457
- node[key] = newId;
1534
+ node[key] = hasHash ? "#" + newId : newId;
1458
1535
  }
1459
1536
  } else if (value.includes("url(#")) {
1460
1537
  node[key] = replaceUrlRefs(value, idMap);
@@ -1472,14 +1549,14 @@ function generateNewIds(doc) {
1472
1549
  }
1473
1550
  collectIds(cloned);
1474
1551
  updateRefs(cloned);
1475
- const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animate;
1552
+ const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animateById;
1476
1553
  if (docAnimate && typeof docAnimate === "object") {
1477
1554
  const updatedAnimate = {};
1478
1555
  for (const [id, anim] of Object.entries(docAnimate)) {
1479
1556
  const newId = (_b = idMap.get(id)) != null ? _b : id;
1480
1557
  updatedAnimate[newId] = anim;
1481
1558
  }
1482
- cloned.animator = __spreadProps2(__spreadValues2({}, cloned.animator), { animate: updatedAnimate });
1559
+ cloned.animator = __spreadProps2(__spreadValues2({}, cloned.animator), { animateById: updatedAnimate });
1483
1560
  }
1484
1561
  return cloned;
1485
1562
  }
@@ -1568,8 +1645,9 @@ function getNormalizedProps(props) {
1568
1645
  let value = props[rawKey];
1569
1646
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
1570
1647
  propsCopy[key] = toRGBA(value);
1571
- } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
1572
- propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
1648
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes && !value.kfs) {
1649
+ const parts = value.value && typeof value.value === "object" ? value.value : value;
1650
+ propsCopy["transform"] = composeTransformParts(parts, { withUnits: false });
1573
1651
  } else if (TRANSFORM_FN_NAMES.has(key)) {
1574
1652
  if (Array.isArray(value)) {
1575
1653
  if (key === "translate") value = value.map((v) => v + "px");
@@ -1577,6 +1655,8 @@ function getNormalizedProps(props) {
1577
1655
  }
1578
1656
  if (key === "rotate") value = value + "deg";
1579
1657
  propsCopy["transform"] = key + "(" + value + ")";
1658
+ } else if (Array.isArray(value)) {
1659
+ propsCopy[key] = value.join(",");
1580
1660
  } else if (value !== void 0 && value !== null) {
1581
1661
  propsCopy[key] = String(value);
1582
1662
  }
@@ -1974,6 +2054,16 @@ function walkAndMaterialise(node, opts) {
1974
2054
  if (newAnimate) cloned.animate = newAnimate;
1975
2055
  return cloned;
1976
2056
  }
2057
+ var LOOP_JUMP_SHIFT_MS = 1;
2058
+ function deepEqualValue(a, b) {
2059
+ if (a === b) return true;
2060
+ if (typeof a !== typeof b || a === null || b === null || typeof a !== "object") return false;
2061
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
2062
+ const ka = Object.keys(a);
2063
+ const kb = Object.keys(b);
2064
+ if (ka.length !== kb.length) return false;
2065
+ return ka.every((k) => deepEqualValue(a[k], b[k]));
2066
+ }
1977
2067
  function parsePathCommands(d) {
1978
2068
  const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
1979
2069
  const commands = [];
@@ -2176,7 +2266,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2176
2266
  const totalIntervals = keyframes.length - 1;
2177
2267
  const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2178
2268
  let segKfs;
2179
- if (loop.before) {
2269
+ if (loop.extend === PxLoopExtend.before) {
2180
2270
  segKfs = keyframes.slice(0, segCount + 1);
2181
2271
  } else {
2182
2272
  segKfs = keyframes.slice(totalIntervals - segCount);
@@ -2184,7 +2274,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2184
2274
  const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2185
2275
  const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2186
2276
  let fillStart, fillEnd;
2187
- if (loop.before) {
2277
+ if (loop.extend === PxLoopExtend.before) {
2188
2278
  fillStart = 0;
2189
2279
  fillEnd = firstT;
2190
2280
  } else {
@@ -2211,7 +2301,12 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2211
2301
  const remainder = fillDuration - fullReps * segDuration;
2212
2302
  const partialFraction = remainder / segDuration;
2213
2303
  const looped = [];
2304
+ const separateBoundary = loop.extend !== PxLoopExtend.before;
2305
+ const originalTerminalKf = keyframes[keyframes.length - 1];
2306
+ let terminalEasingOverride;
2307
+ let hasTerminalEasingOverride = false;
2214
2308
  function appendRep(repStart, isReversed, partial) {
2309
+ var _a2;
2215
2310
  let entries;
2216
2311
  if (isReversed) {
2217
2312
  entries = [];
@@ -2247,8 +2342,26 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2247
2342
  looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2248
2343
  return;
2249
2344
  }
2345
+ const prevKf = looped.length > 0 ? looped[looped.length - 1] : originalTerminalKf;
2346
+ const isBoundary = separateBoundary && i === 0 && prevKf !== void 0 && Math.abs(((_a2 = prevKf.t) != null ? _a2 : 0) - (repStart + entry.relT * segDuration)) < 1e-9;
2347
+ if (isBoundary) {
2348
+ if (deepEqualValue(prevKf.v, entry.v)) {
2349
+ if (looped.length > 0) {
2350
+ prevKf.e = entry.e;
2351
+ prevKf.tangentOut = entry.tangentOut;
2352
+ } else {
2353
+ terminalEasingOverride = entry.e;
2354
+ hasTerminalEasingOverride = true;
2355
+ }
2356
+ continue;
2357
+ }
2358
+ if (looped.length > 0) {
2359
+ delete prevKf.tangentIn;
2360
+ delete prevKf.tangentOut;
2361
+ }
2362
+ }
2250
2363
  const pushed = {
2251
- t: repStart + entry.relT * segDuration,
2364
+ t: repStart + entry.relT * segDuration + (isBoundary ? LOOP_JUMP_SHIFT_MS : 0),
2252
2365
  v: entry.v,
2253
2366
  e: i < entries.length - 1 ? entry.e : void 0
2254
2367
  };
@@ -2296,7 +2409,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2296
2409
  looped.push(pushed);
2297
2410
  }
2298
2411
  }
2299
- if (loop.before) {
2412
+ if (loop.extend === PxLoopExtend.before) {
2300
2413
  if (partialFraction > 1e-9) {
2301
2414
  const isReversed = !!loop.alternate && fullReps % 2 === 0;
2302
2415
  appendRepTail(fillStart, isReversed, partialFraction);
@@ -2319,9 +2432,14 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2319
2432
  appendRep(repStart, isReversed, partialFraction);
2320
2433
  }
2321
2434
  }
2322
- if (loop.before) {
2435
+ if (loop.extend === PxLoopExtend.before) {
2323
2436
  return [...looped, ...keyframes];
2324
2437
  } else {
2438
+ if (hasTerminalEasingOverride && keyframes.length > 0) {
2439
+ const head = keyframes.slice(0, -1);
2440
+ const tail = __spreadProps2(__spreadValues2({}, keyframes[keyframes.length - 1]), { e: terminalEasingOverride });
2441
+ return [...head, tail, ...looped];
2442
+ }
2325
2443
  return [...keyframes, ...looped];
2326
2444
  }
2327
2445
  }
@@ -2435,20 +2553,23 @@ var _elementIdCounter = 0;
2435
2553
  function generateElementId() {
2436
2554
  return "_px_el_" + ++_elementIdCounter;
2437
2555
  }
2438
- function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.webapi) {
2556
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.waapi) {
2439
2557
  const normalized = {};
2440
2558
  for (const [propName, propAnim] of Object.entries(animDef)) {
2559
+ if (propName === "transform" && propAnim.alongPathMode === "offsetPath" && animDef["offsetDistance"] !== void 0) {
2560
+ continue;
2561
+ }
2441
2562
  const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2442
2563
  if (normalizedKfs.length > 0) {
2443
2564
  const out = { kfs: normalizedKfs };
2444
2565
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2445
2566
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2446
- normalized[propName] = engine === PxAnimatorEngine.webapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2567
+ normalized[propName] = engine === PxAnimatorEngine.waapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2447
2568
  }
2448
2569
  }
2449
2570
  return normalized;
2450
2571
  }
2451
- function getNormalisedBindings(doc, engine = PxAnimatorEngine.webapi) {
2572
+ function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2452
2573
  const animatorConfig = getAnimatorConfig(doc) || {};
2453
2574
  const defs = getDefs(doc);
2454
2575
  const duration = animatorConfig.duration || 1e3;
@@ -2853,7 +2974,7 @@ function partsRecord(part, value, origin) {
2853
2974
  return rec;
2854
2975
  }
2855
2976
  function readAnimatable(raw) {
2856
- var _a, _b;
2977
+ var _a, _b, _c;
2857
2978
  if (raw === void 0) return {
2858
2979
  kind: "absent"
2859
2980
  /* Absent */
@@ -2863,13 +2984,34 @@ function readAnimatable(raw) {
2863
2984
  const obj = raw;
2864
2985
  const kfs = (_a = obj.keyframes) != null ? _a : obj.kfs;
2865
2986
  if (kfs) {
2866
- return { kind: "animated", keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2987
+ const out = { kind: "animated", keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2988
+ const base = (_b = obj.value) != null ? _b : obj.v;
2989
+ if (base !== void 0 && out.kind === "animated") out.base = base;
2990
+ return out;
2867
2991
  }
2868
- const staticValue = (_b = obj.value) != null ? _b : obj.v;
2992
+ const staticValue = (_c = obj.value) != null ? _c : obj.v;
2869
2993
  if (staticValue !== void 0) return { kind: "static", value: staticValue };
2870
2994
  }
2871
2995
  return { kind: "static", value: raw };
2872
2996
  }
2997
+ function writeAnimatableChannel(node, attrName, read, opts) {
2998
+ var _a, _b, _c, _d;
2999
+ const toOut = (v) => (opts == null ? void 0 : opts.asString) && v !== void 0 && v !== null ? String(v) : v;
3000
+ if (read.kind === "absent") return;
3001
+ if (read.kind === "static") {
3002
+ node[attrName] = toOut(read.value);
3003
+ return;
3004
+ }
3005
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3006
+ const animate = __spreadValues2({}, prevAnimate || {});
3007
+ const block = { keyframes: read.keyframes };
3008
+ if (read.loop !== void 0) block.loop = read.loop;
3009
+ if (read.autoOrient !== void 0) block.autoOrient = read.autoOrient;
3010
+ animate[attrName] = block;
3011
+ node.animate = animate;
3012
+ 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;
3013
+ if (baseline !== void 0) node[attrName] = toOut(baseline);
3014
+ }
2873
3015
  function normaliseKeyframe(kf) {
2874
3016
  if (!kf || typeof kf !== "object") return kf;
2875
3017
  const k = kf;
@@ -2889,7 +3031,7 @@ function readStaticOrigin(raw, ctx) {
2889
3031
  const o = readAnimatable(raw);
2890
3032
  if (o.kind === "absent") return void 0;
2891
3033
  if (o.kind === "static") return o.value;
2892
- ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
3034
+ ctx.warnings.push("transformBy.origin: animated origin approximated by its first keyframe");
2893
3035
  return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
2894
3036
  }
2895
3037
  function keyframeWith(kf, value) {
@@ -2900,7 +3042,7 @@ function keyframeWith(kf, value) {
2900
3042
  if (kf.tangentIn !== void 0) out.tangentIn = kf.tangentIn;
2901
3043
  return out;
2902
3044
  }
2903
- function applyTransformationEffect(node, fx, ctx) {
3045
+ function applyTransformByEffect(node, fx, ctx) {
2904
3046
  if (!fx) return node;
2905
3047
  delete node.transform;
2906
3048
  let n = node;
@@ -2936,6 +3078,10 @@ function applyTransformationEffect(node, fx, ctx) {
2936
3078
  } else {
2937
3079
  n = wrapTransformPart(n, "translate", fx.translate, ctx);
2938
3080
  }
3081
+ if (n !== node && node.id) {
3082
+ n.id = node.id;
3083
+ delete node.id;
3084
+ }
2939
3085
  return n;
2940
3086
  }
2941
3087
  function translateHasAutoOrient(translate) {
@@ -2945,8 +3091,6 @@ function translateHasAutoOrient(translate) {
2945
3091
  return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
2946
3092
  }
2947
3093
  function normalizeScale(raw) {
2948
- if (raw === void 0) return void 0;
2949
- if (Array.isArray(raw)) return [raw[0] / 100, raw[1] / 100];
2950
3094
  return raw;
2951
3095
  }
2952
3096
  function wrapTransformPart(inner, part, raw, ctx) {
@@ -2987,34 +3131,54 @@ function wrapOrigin(inner, raw, invert) {
2987
3131
  }
2988
3132
  return inner;
2989
3133
  }
3134
+ function genId(ctx, prefix) {
3135
+ return "_lw_" + prefix + "_" + ctx.nextId++;
3136
+ }
3137
+ function stripHash2(href) {
3138
+ return typeof href === "string" ? href.replace(/^#/, "") : void 0;
3139
+ }
3140
+ function indexById(node, map) {
3141
+ var _a;
3142
+ if (typeof node.id === "string") map.set(node.id, node);
3143
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3144
+ }
3145
+ function spliceDefs(root, defs) {
3146
+ if (!defs.length) return;
3147
+ const existing = root.children || (root.children = []);
3148
+ existing.unshift({ type: "defs", children: defs });
3149
+ }
3150
+ var clone = deepClonePxNode;
3151
+ function regenerateIdsInClone(root, ctx) {
3152
+ return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3153
+ }
2990
3154
  function identifyContentRefTargets(node, ctx, allocator) {
2991
3155
  var _a, _b, _c;
2992
3156
  if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.type) === "content") {
2993
- const baseId = node.effects.clone.baseId;
2994
- if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
2995
- ctx.contentRefInnerIds.set(baseId, allocator(baseId));
3157
+ const sourceId = stripHash2(node.effects.clone.sourceId);
3158
+ if (typeof sourceId === "string" && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {
3159
+ ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));
2996
3160
  }
2997
3161
  }
2998
3162
  (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
2999
3163
  }
3000
- function splitForContentRef(node, transformation, originalId, innerId, ctx) {
3001
- const outerBody = liftBodyTranslate(node, transformation);
3164
+ function splitForContentRef(node, transformBy, originalId, innerId, ctx) {
3165
+ const outerBody = liftBodyTranslate(node, transformBy);
3002
3166
  if (typeof node.id === "string") delete node.id;
3003
- const { outer: outerTr, inner: innerTr } = splitTransformationEffect(transformation);
3167
+ const { outer: outerTr, inner: innerTr } = splitTransformByEffect(transformBy);
3004
3168
  let innerNode = node;
3005
- innerNode = applyTransformationEffect(innerNode, innerTr, ctx);
3169
+ innerNode = applyTransformByEffect(innerNode, innerTr, ctx);
3006
3170
  const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
3007
3171
  let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
3008
3172
  if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
3009
3173
  if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
3010
3174
  if (outerTr) {
3011
3175
  delete outerWrapper.id;
3012
- outerWrapper = applyTransformationEffect(outerWrapper, outerTr, ctx);
3176
+ outerWrapper = applyTransformByEffect(outerWrapper, outerTr, ctx);
3013
3177
  outerWrapper.id = originalId;
3014
3178
  }
3015
3179
  return outerWrapper;
3016
3180
  }
3017
- function liftBodyTranslate(node, transformation) {
3181
+ function liftBodyTranslate(node, transformBy) {
3018
3182
  var _a, _b, _c;
3019
3183
  const out = {};
3020
3184
  let didLiftAnimate = false;
@@ -3068,7 +3232,7 @@ function liftBodyTranslate(node, transformation) {
3068
3232
  didLiftAnimate = true;
3069
3233
  }
3070
3234
  }
3071
- const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
3235
+ const transformationHasTranslate = (transformBy == null ? void 0 : transformBy.translate) !== void 0;
3072
3236
  const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
3073
3237
  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);
3074
3238
  if (typeof node.transform === "string") {
@@ -3087,6 +3251,24 @@ function liftBodyTranslate(node, transformation) {
3087
3251
  if (split.rest) node.transform = split.rest;
3088
3252
  else delete node.transform;
3089
3253
  }
3254
+ } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes && !node.transform.kfs) {
3255
+ const wrapped = node.transform.value;
3256
+ const isWrapped = !!(wrapped && typeof wrapped === "object");
3257
+ const value = isWrapped ? wrapped : node.transform;
3258
+ const rewrap = (rec) => isWrapped ? { value: rec } : rec;
3259
+ if (Array.isArray(value.translate)) {
3260
+ const rest = __spreadValues2({}, value);
3261
+ delete rest.translate;
3262
+ const hasRest = Object.keys(rest).length > 0;
3263
+ if (stripBodyTranslateOnly) {
3264
+ if (hasRest) node.transform = rewrap(rest);
3265
+ else delete node.transform;
3266
+ } else {
3267
+ out.transform = rewrap({ translate: value.translate });
3268
+ if (hasRest) node.transform = rewrap(rest);
3269
+ else delete node.transform;
3270
+ }
3271
+ }
3090
3272
  }
3091
3273
  return out;
3092
3274
  }
@@ -3156,7 +3338,7 @@ function parseTranslateArgs(translateOp) {
3156
3338
  const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
3157
3339
  return [nums[0] || 0, nums[1] || 0];
3158
3340
  }
3159
- function splitTransformationEffect(fx) {
3341
+ function splitTransformByEffect(fx) {
3160
3342
  if (!fx) return {};
3161
3343
  const originOnOuter = needsOriginOnOuter(fx.translate);
3162
3344
  const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
@@ -3182,26 +3364,6 @@ function needsOriginOnOuter(translateAnim) {
3182
3364
  }
3183
3365
  return false;
3184
3366
  }
3185
- function genId(ctx, prefix) {
3186
- return "_lw_" + prefix + "_" + ctx.nextId++;
3187
- }
3188
- function stripHash2(href) {
3189
- return typeof href === "string" ? href.replace(/^#/, "") : void 0;
3190
- }
3191
- function indexById(node, map) {
3192
- var _a;
3193
- if (typeof node.id === "string") map.set(node.id, node);
3194
- (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3195
- }
3196
- function spliceDefs(root, defs) {
3197
- if (!defs.length) return;
3198
- const existing = root.children || (root.children = []);
3199
- existing.unshift({ type: "defs", children: defs });
3200
- }
3201
- var clone = deepClonePxNode;
3202
- function regenerateIdsInClone(root, ctx) {
3203
- return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3204
- }
3205
3367
  function applyFillGradientEffect(node, fx, ctx) {
3206
3368
  return applyGradient(node, fx, ctx, "fill");
3207
3369
  }
@@ -3222,62 +3384,63 @@ function synthesiseGradientDef(fx, id, ctx) {
3222
3384
  id
3223
3385
  };
3224
3386
  if (fx.type === PxGradientType.linear) {
3225
- if (fx.p1) {
3226
- out.x1 = String(fx.p1[0]);
3227
- out.y1 = String(fx.p1[1]);
3228
- }
3229
- if (fx.p2) {
3230
- out.x2 = String(fx.p2[0]);
3231
- out.y2 = String(fx.p2[1]);
3232
- }
3387
+ applyGeomVec(out, "x1", "y1", fx.p1);
3388
+ applyGeomVec(out, "x2", "y2", fx.p2);
3233
3389
  } else {
3234
- if (fx.c) {
3235
- out.cx = String(fx.c[0]);
3236
- out.cy = String(fx.c[1]);
3237
- }
3238
- if (fx.r !== void 0) out.r = String(fx.r);
3239
- if (fx.fp) {
3240
- out.fx = String(fx.fp[0]);
3241
- out.fy = String(fx.fp[1]);
3242
- }
3390
+ applyGeomVec(out, "cx", "cy", fx.c);
3391
+ applyGeomNumber(out, "r", fx.r);
3392
+ applyGeomVec(out, "fx", "fy", fx.fp);
3243
3393
  }
3244
3394
  if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
3245
3395
  if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
3246
3396
  if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;
3247
- const animate = fx.animate;
3248
- if (animate) {
3249
- const mapped = {};
3250
- for (const wireKey of Object.keys(animate)) {
3251
- const attr = GRADIENT_ANIM_CHANNEL_TO_ATTR[wireKey];
3252
- if (attr) mapped[attr] = animate[wireKey];
3253
- }
3254
- if (Object.keys(mapped).length) out.animate = mapped;
3255
- }
3256
3397
  out.children = buildStopChildren(fx.stops, ctx);
3257
3398
  return out;
3258
3399
  }
3259
- var GRADIENT_ANIM_CHANNEL_TO_ATTR = {
3260
- gradientX1: "x1",
3261
- gradientY1: "y1",
3262
- gradientX2: "x2",
3263
- gradientY2: "y2",
3264
- gradientCx: "cx",
3265
- gradientCy: "cy",
3266
- gradientFx: "fx",
3267
- gradientFy: "fy",
3268
- gradientR: "r"
3269
- };
3400
+ function applyGeomVec(out, xAttr, yAttr, raw) {
3401
+ var _a, _b, _c;
3402
+ const read = readAnimatable(raw);
3403
+ if (read.kind === "absent") return;
3404
+ if (read.kind === "static") {
3405
+ out[xAttr] = String(read.value[0]);
3406
+ out[yAttr] = String(read.value[1]);
3407
+ return;
3408
+ }
3409
+ const axisChannel = (idx) => {
3410
+ const block = {
3411
+ keyframes: read.keyframes.map((kf) => {
3412
+ const axisKf = { time: kf.time, value: Array.isArray(kf.value) ? kf.value[idx] : void 0 };
3413
+ if (kf.easing !== void 0) axisKf.easing = kf.easing;
3414
+ return axisKf;
3415
+ })
3416
+ };
3417
+ if (read.loop !== void 0) block.loop = read.loop;
3418
+ return block;
3419
+ };
3420
+ const animate = (_a = out.animate) != null ? _a : {};
3421
+ animate[xAttr] = axisChannel(0);
3422
+ animate[yAttr] = axisChannel(1);
3423
+ out.animate = animate;
3424
+ const baseline = (_c = read.base) != null ? _c : (_b = read.keyframes[0]) == null ? void 0 : _b.value;
3425
+ if (Array.isArray(baseline)) {
3426
+ out[xAttr] = String(baseline[0]);
3427
+ out[yAttr] = String(baseline[1]);
3428
+ }
3429
+ }
3430
+ function applyGeomNumber(out, attrName, raw) {
3431
+ const read = readAnimatable(raw);
3432
+ if (read.kind === "absent") return;
3433
+ writeAnimatableChannel(out, attrName, read, { asString: true });
3434
+ }
3270
3435
  function buildStopChildren(stops, ctx) {
3271
3436
  var _a, _b, _c, _d;
3272
3437
  if (!stops) return [];
3273
- if (Array.isArray(stops)) return stops.map(staticStopNode);
3274
- if (typeof stops === "object" && Array.isArray(stops.value)) {
3275
- return stops.value.map(staticStopNode);
3276
- }
3277
- const animBlock = stops;
3278
- const kfs = animBlock.keyframes;
3279
- if (!Array.isArray(kfs) || !kfs.length) return [];
3280
- const loopFromSource = animBlock.loop;
3438
+ const read = readAnimatable(stops);
3439
+ if (read.kind === "absent") return [];
3440
+ if (read.kind === "static") return Array.isArray(read.value) ? read.value.map(staticStopNode) : [];
3441
+ const kfs = read.keyframes;
3442
+ if (!kfs.length) return [];
3443
+ const loopFromSource = read.loop;
3281
3444
  let stopCount = 0;
3282
3445
  for (const kf of kfs) {
3283
3446
  const v = (_a = kf.value) != null ? _a : kf.v;
@@ -3352,36 +3515,60 @@ function formatOffset(o) {
3352
3515
  return pct + "%";
3353
3516
  }
3354
3517
  function applyClipPathEffect(node, fx, ctx) {
3518
+ var _a, _b;
3355
3519
  if (!fx || !fx.d && !fx.animate) return node;
3356
3520
  const clipId = genId(ctx, "clip");
3357
- const pathChild = { type: "path", d: fx.d };
3358
- if (fx.animate) pathChild.animate = { d: fx.animate };
3521
+ const pathChild = { type: "path" };
3522
+ const read = readAnimatable(fx.d);
3523
+ if (read.kind !== "absent") {
3524
+ if (read.kind === "static") {
3525
+ pathChild.d = pathString(read.value);
3526
+ } else {
3527
+ writeAnimatableChannel(pathChild, "d", read);
3528
+ if (pathChild.d !== void 0) pathChild.d = pathString(pathChild.d);
3529
+ }
3530
+ }
3531
+ if (fx.animate && !((_a = pathChild.animate) == null ? void 0 : _a.d)) {
3532
+ const animate = (_b = pathChild.animate) != null ? _b : {};
3533
+ animate.d = fx.animate;
3534
+ pathChild.animate = animate;
3535
+ }
3359
3536
  ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
3360
3537
  node.clipPath = "url(#" + clipId + ")";
3361
3538
  return node;
3362
3539
  }
3363
- function applyMaskedByEffect(node, fx, transformation, ctx) {
3540
+ function pathString(v) {
3541
+ if (typeof v === "string") return v;
3542
+ if (v && typeof v === "object" && typeof v.path === "string") return v.path;
3543
+ return void 0;
3544
+ }
3545
+ function applyMaskedByEffect(node, fx, transformBy, ctx) {
3364
3546
  if (!fx) return node;
3365
- if (!fx.href) {
3366
- ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
3547
+ const sourceId = stripHash2(fx.sourceId);
3548
+ if (!sourceId) {
3549
+ ctx.errors.push("maskedBy.sourceId missing \u2014 cannot build mask");
3367
3550
  return node;
3368
3551
  }
3369
3552
  const maskId = genId(ctx, "mask");
3370
- let content = { type: "use", href: "#" + fx.href };
3371
- if (transformation) {
3372
- content = wrapInverseTransform(content, transformation, ctx);
3553
+ let content = { type: "use", href: "#" + sourceId };
3554
+ if (transformBy) {
3555
+ content = wrapInverseTransform(content, transformBy, ctx);
3373
3556
  } else if (hasAnimateTransform(node)) {
3374
3557
  content = wrapInverseAnimatedBodyTransform(content, node, ctx);
3375
3558
  } else {
3376
3559
  const bodyStatic = readTransformationFromBody(node);
3377
3560
  if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);
3378
3561
  }
3379
- const includeTargetOwn = transformation === void 0 && !nodeHasBodyTransform(node);
3380
- content = wrapAncestorChainCompensation(content, node, fx.href, ctx, includeTargetOwn);
3562
+ const includeTargetOwn = transformBy === void 0 && !nodeHasBodyTransform(node);
3563
+ content = wrapAncestorChainCompensation(content, node, sourceId, ctx, includeTargetOwn);
3381
3564
  const mask = { type: "mask", id: maskId, children: [content] };
3382
3565
  if (fx.maskType) mask.maskType = fx.maskType;
3383
3566
  if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
3384
3567
  if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
3568
+ if (fx.x !== void 0) mask.x = String(fx.x);
3569
+ if (fx.y !== void 0) mask.y = String(fx.y);
3570
+ if (fx.width !== void 0) mask.width = String(fx.width);
3571
+ if (fx.height !== void 0) mask.height = String(fx.height);
3385
3572
  ctx.defs.push(mask);
3386
3573
  node.mask = "url(#" + maskId + ")";
3387
3574
  return node;
@@ -3488,6 +3675,19 @@ function readTransformationFromBody(node) {
3488
3675
  if (parts.origin) out.origin = parts.origin;
3489
3676
  return Object.keys(out).length ? out : void 0;
3490
3677
  }
3678
+ if (node.transform && typeof node.transform === "object" && !node.transform.keyframes && !node.transform.kfs) {
3679
+ const wrapped = node.transform.value;
3680
+ const value = wrapped && typeof wrapped === "object" ? wrapped : node.transform;
3681
+ if (value && typeof value === "object") {
3682
+ const out = {};
3683
+ if (Array.isArray(value.translate)) out.translate = value.translate;
3684
+ if (typeof value.rotate === "number") out.rotate = value.rotate;
3685
+ if (typeof value.skew === "number") out.skew = value.skew;
3686
+ if (Array.isArray(value.scale)) out.scale = { value: value.scale };
3687
+ if (Array.isArray(value.origin)) out.origin = value.origin;
3688
+ return Object.keys(out).length ? out : void 0;
3689
+ }
3690
+ }
3491
3691
  return void 0;
3492
3692
  }
3493
3693
  function parseTransformStringToParts(s) {
@@ -3621,10 +3821,10 @@ function collectMaskAncestorChains(root, ctx) {
3621
3821
  const interestingNodes = /* @__PURE__ */ new Set();
3622
3822
  const collectInterestingNodes = (n) => {
3623
3823
  var _a, _b;
3624
- const href = (_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.href;
3625
- if (typeof href === "string") {
3824
+ const maskSourceId = stripHash2((_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.sourceId);
3825
+ if (typeof maskSourceId === "string") {
3626
3826
  interestingNodes.add(n);
3627
- const sourceNode = ctx.idMap.get(href);
3827
+ const sourceNode = ctx.idMap.get(maskSourceId);
3628
3828
  if (sourceNode) interestingNodes.add(sourceNode);
3629
3829
  }
3630
3830
  if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);
@@ -3650,8 +3850,9 @@ function extractTranslateOnly(node, ctx) {
3650
3850
  if (typeof tr === "string") {
3651
3851
  const parts = parseTranslateOnlyFromString(tr, ctx);
3652
3852
  if (parts) out.translate = parts;
3653
- } else if (tr && typeof tr === "object") {
3654
- const value = tr.value;
3853
+ } else if (tr && typeof tr === "object" && !tr.keyframes && !tr.kfs) {
3854
+ const wrapped = tr.value;
3855
+ const value = wrapped && typeof wrapped === "object" ? wrapped : tr;
3655
3856
  if (value && typeof value === "object" && Array.isArray(value.translate)) {
3656
3857
  out.translate = [value.translate[0] || 0, value.translate[1] || 0];
3657
3858
  }
@@ -3699,17 +3900,17 @@ function parseTranslateOnlyFromString(s, ctx) {
3699
3900
  var CONTENT_SUBREF = "content";
3700
3901
  function applyRefHref(node, clone2, ctx) {
3701
3902
  if (!clone2) return;
3702
- const baseId = clone2.baseId;
3703
- if (!baseId) {
3704
- if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing baseId");
3903
+ const sourceId = stripHash2(clone2.sourceId);
3904
+ if (!sourceId) {
3905
+ if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing sourceId");
3705
3906
  return;
3706
3907
  }
3707
- const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
3908
+ const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(sourceId) || sourceId : sourceId;
3708
3909
  node.href = "#" + targetId;
3709
3910
  }
3710
- function applyRefAndTransformationEffect(node, clone2, transformation, ctx) {
3911
+ function applyRefAndTransformationEffect(node, clone2, transformBy, ctx) {
3711
3912
  applyRefHref(node, clone2, ctx);
3712
- return applyTransformationEffect(node, transformation, ctx);
3913
+ return applyTransformByEffect(node, transformBy, ctx);
3713
3914
  }
3714
3915
  function applyRepeaterEffect(node, fx, ctx) {
3715
3916
  var _a, _b;
@@ -3732,7 +3933,7 @@ function applyRepeaterEffect(node, fx, ctx) {
3732
3933
  for (let i = 1; i < copies; i++) {
3733
3934
  const baseClone = clone(base);
3734
3935
  const synthFx = synthesisePerCopyFx(fx, i);
3735
- const wrapped = synthFx ? applyTransformationEffect(baseClone, synthFx, ctx) : baseClone;
3936
+ const wrapped = synthFx ? applyTransformByEffect(baseClone, synthFx, ctx) : baseClone;
3736
3937
  children.push(wrapped);
3737
3938
  }
3738
3939
  const wrapper = { type: "g", children };
@@ -3744,10 +3945,13 @@ function applyRepeaterEffect(node, fx, ctx) {
3744
3945
  function synthesisePerCopyFx(fx, i) {
3745
3946
  const out = {};
3746
3947
  if (fx.translate !== void 0) {
3747
- out.translate = mapAnimatableVec2(fx.translate, (v) => [v[0] * i, v[1] * i]);
3948
+ out.translate = mapAnimatable(fx.translate, (v) => [v[0] * i, v[1] * i]);
3748
3949
  }
3749
3950
  if (fx.rotate !== void 0) {
3750
- out.rotate = mapAnimatableNumber(fx.rotate, (v) => v * i);
3951
+ out.rotate = mapAnimatable(fx.rotate, (v) => v * i);
3952
+ }
3953
+ if (fx.skew !== void 0) {
3954
+ out.skew = mapAnimatable(fx.skew, (v) => v * i);
3751
3955
  }
3752
3956
  if (fx.scale !== void 0) {
3753
3957
  out.scale = synthesiseScale(fx.scale, i);
@@ -3757,63 +3961,50 @@ function synthesisePerCopyFx(fx, i) {
3757
3961
  }
3758
3962
  return Object.keys(out).length ? out : void 0;
3759
3963
  }
3760
- function mapAnimatableVec2(raw, fn) {
3761
- if (Array.isArray(raw)) return fn(raw);
3762
- if (raw && typeof raw === "object") {
3763
- const obj = raw;
3764
- if (Array.isArray(obj.keyframes)) {
3765
- return __spreadProps2(__spreadValues2({}, obj), {
3766
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps2(__spreadValues2({}, kf), { value: fn(kf.value) }) : kf)
3767
- });
3768
- }
3769
- if (obj.value !== void 0) {
3770
- return __spreadProps2(__spreadValues2({}, obj), { value: fn(obj.value) });
3771
- }
3964
+ function mapAnimatable(raw, fn, wrapStatic = false) {
3965
+ const read = readAnimatable(raw);
3966
+ if (read.kind === "absent") return raw;
3967
+ if (read.kind === "static") {
3968
+ const mapped = fn(read.value);
3969
+ const wasRawStatic = typeof raw === "number" || Array.isArray(raw);
3970
+ return wasRawStatic && !wrapStatic ? mapped : { value: mapped };
3772
3971
  }
3773
- return raw;
3774
- }
3775
- function mapAnimatableNumber(raw, fn) {
3776
- if (typeof raw === "number") return fn(raw);
3777
- if (raw && typeof raw === "object") {
3778
- const obj = raw;
3779
- if (Array.isArray(obj.keyframes)) {
3780
- return __spreadProps2(__spreadValues2({}, obj), {
3781
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps2(__spreadValues2({}, kf), { value: fn(kf.value) }) : kf)
3782
- });
3783
- }
3784
- if (obj.value !== void 0) {
3785
- return __spreadProps2(__spreadValues2({}, obj), { value: fn(obj.value) });
3786
- }
3787
- }
3788
- return raw;
3972
+ const out = {
3973
+ keyframes: read.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps2(__spreadValues2({}, kf), { value: fn(kf.value) }) : kf)
3974
+ };
3975
+ if (read.loop !== void 0) out.loop = read.loop;
3976
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
3977
+ if (read.base !== void 0) out.value = fn(read.base);
3978
+ return out;
3789
3979
  }
3790
3980
  function synthesiseScale(raw, i) {
3791
- const scalePowerFromPercent = (v) => [Math.pow(v[0] / 100, i), Math.pow(v[1] / 100, i)];
3792
- const scalePowerFromUnits = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
3793
- if (Array.isArray(raw)) {
3794
- return { value: scalePowerFromPercent(raw) };
3795
- }
3796
- if (raw && typeof raw === "object") {
3797
- const obj = raw;
3798
- if (Array.isArray(obj.keyframes)) {
3799
- return __spreadProps2(__spreadValues2({}, obj), {
3800
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps2(__spreadValues2({}, kf), { value: scalePowerFromUnits(kf.value) }) : kf)
3801
- });
3802
- }
3803
- if (obj.value !== void 0) {
3804
- return __spreadProps2(__spreadValues2({}, obj), { value: scalePowerFromPercent(obj.value) });
3805
- }
3806
- }
3807
- return raw;
3981
+ const scalePower = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
3982
+ return mapAnimatable(raw, scalePower, true);
3808
3983
  }
3809
3984
  var RETIME_MATERIALISATION_MODE_INLINE_G = false;
3810
3985
  function asRetime(r) {
3811
3986
  var _a, _b;
3812
- if (r.timeCrop) {
3813
- console.warn("retime: `timeCrop` is not supported yet and will be ignored");
3814
- }
3815
3987
  return { start: (_a = r.start) != null ? _a : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
3816
3988
  }
3989
+ var CROP_EDGE_MS = 1;
3990
+ function applyTimeCrop(useNode, crop, ctx) {
3991
+ const [start, end] = crop;
3992
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
3993
+ ctx.warnings.push("retime: timeCrop is not a pair of finite numbers \u2014 ignored");
3994
+ return;
3995
+ }
3996
+ const keyframes = end <= start ? [{ time: 0, value: 0 }] : [
3997
+ ...start > 0 ? [{ time: Math.max(0, start - CROP_EDGE_MS), value: 0 }] : [],
3998
+ { time: Math.max(0, start), value: 1 },
3999
+ { time: end, value: 1 },
4000
+ { time: end + CROP_EDGE_MS, value: 0 }
4001
+ ];
4002
+ const inner = __spreadValues2({}, useNode);
4003
+ for (const k of Object.keys(useNode)) delete useNode[k];
4004
+ useNode.type = "g";
4005
+ useNode.children = [inner];
4006
+ useNode.animate = { opacity: { keyframes } };
4007
+ }
3817
4008
  function concatRetime(child, parent) {
3818
4009
  return {
3819
4010
  start: parent.start + parent.stretch * child.start,
@@ -3873,8 +4064,10 @@ function applyAllRetimeEffects(root, ctx) {
3873
4064
  for (const useNode of sites) {
3874
4065
  const retime = readCloneRetime(useNode);
3875
4066
  if (!retime) continue;
4067
+ const crop = retime.timeCrop;
3876
4068
  clearCloneRetime(useNode);
3877
4069
  materialiseRetime(useNode, asRetime(retime), ctx);
4070
+ if (crop) applyTimeCrop(useNode, crop, ctx);
3878
4071
  }
3879
4072
  }
3880
4073
  function materialiseRetime(useNode, retime, ctx) {
@@ -4117,14 +4310,11 @@ function createPathSampler(d) {
4117
4310
  }
4118
4311
  var EXTEND_MARGIN_FRAC = 0.15;
4119
4312
  function numRange(v) {
4120
- if (typeof v === "number") return { min: v, max: v };
4121
- if (v && typeof v === "object") {
4122
- const o = v;
4123
- if (typeof o.value === "number") return { min: o.value, max: o.value };
4124
- if (Array.isArray(o.keyframes) && o.keyframes.length) {
4125
- const vals = o.keyframes.map((k) => Number(k.value) || 0);
4126
- return { min: Math.min(...vals), max: Math.max(...vals) };
4127
- }
4313
+ const read = readAnimatable(v);
4314
+ if (read.kind === "static" && typeof read.value === "number") return { min: read.value, max: read.value };
4315
+ if (read.kind === "animated" && read.keyframes.length) {
4316
+ const vals = read.keyframes.map((k) => Number(k.value) || 0);
4317
+ return { min: Math.min(...vals), max: Math.max(...vals) };
4128
4318
  }
4129
4319
  return { min: 0, max: 0 };
4130
4320
  }
@@ -4144,11 +4334,16 @@ function estimateTextAdvance(node) {
4144
4334
  var r3 = (n) => Math.round(n * 1e3) / 1e3;
4145
4335
  function shiftAnimatable(v, by) {
4146
4336
  if (!by || v === void 0 || v === null) return v;
4147
- if (typeof v === "number") return v + by;
4148
- const o = v;
4149
- if (typeof o.value === "number") return __spreadProps2(__spreadValues2({}, o), { value: o.value + by });
4150
- if (Array.isArray(o.keyframes)) return __spreadProps2(__spreadValues2({}, o), { keyframes: o.keyframes.map((k) => __spreadProps2(__spreadValues2({}, k), { value: (Number(k.value) || 0) + by })) });
4151
- return v;
4337
+ const read = readAnimatable(v);
4338
+ if (read.kind === "absent") return v;
4339
+ if (read.kind === "static") return typeof v === "number" ? read.value + by : { value: read.value + by };
4340
+ const out = {
4341
+ keyframes: read.keyframes.map((k) => __spreadProps2(__spreadValues2({}, k), { value: (Number(k.value) || 0) + by }))
4342
+ };
4343
+ if (read.loop !== void 0) out.loop = read.loop;
4344
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
4345
+ if (read.base !== void 0) out.value = read.base + by;
4346
+ return out;
4152
4347
  }
4153
4348
  function extendedPathForBrowser(pathD, opts) {
4154
4349
  var _a;
@@ -4204,26 +4399,7 @@ function applyTextPathEffect(node, fx, ctx) {
4204
4399
  }
4205
4400
  function applyAnimatableNumber(node, attrName, raw) {
4206
4401
  if (raw === void 0 || raw === null) return;
4207
- if (typeof raw === "number") {
4208
- node[attrName] = String(raw);
4209
- return;
4210
- }
4211
- if (typeof raw === "object" && raw !== null) {
4212
- const obj = raw;
4213
- if (Array.isArray(obj.keyframes)) {
4214
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4215
- const animate = __spreadValues2({}, prevAnimate || {});
4216
- const block = { keyframes: obj.keyframes };
4217
- if (obj.loop !== void 0) block.loop = obj.loop;
4218
- animate[attrName] = block;
4219
- node.animate = animate;
4220
- return;
4221
- }
4222
- if (typeof obj.value === "number") {
4223
- node[attrName] = String(obj.value);
4224
- return;
4225
- }
4226
- }
4402
+ writeAnimatableChannel(node, attrName, readAnimatable(raw), { asString: true });
4227
4403
  }
4228
4404
  var jsonElementFactory = (type, props, children) => {
4229
4405
  const node = { type };
@@ -4411,13 +4587,13 @@ function materialiseGlyphTextHorizontal(node, opts) {
4411
4587
  if (y !== void 0) pen.y = y;
4412
4588
  pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
4413
4589
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4414
- const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
4590
+ const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4415
4591
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
4416
4592
  if (el.children) for (const ch of el.children) walk(ch, s);
4417
4593
  };
4418
4594
  const rootStyle = rootStyleOf(node);
4419
4595
  if (node.children) for (const ch of node.children) walk(ch, rootStyle);
4420
- const rootContent = (_c = str(node[TEXT_ATTR])) != null ? _c : str(node[TEXT_CONTENT_ATTR]);
4596
+ const rootContent = (_c = str(node[TEXT_CONTENT_ATTR])) != null ? _c : str(node[TEXT_ATTR]);
4421
4597
  if (rootContent && !((_d = node.children) == null ? void 0 : _d.length)) renderChars(rootContent, rootStyle);
4422
4598
  const anchor = str(node.textAnchor);
4423
4599
  if (anchor === "middle" || anchor === "end") {
@@ -4466,7 +4642,7 @@ function layoutGlyphTextChars(node, opts) {
4466
4642
  if (y !== void 0) pen.y = y;
4467
4643
  pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
4468
4644
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4469
- const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
4645
+ const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4470
4646
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
4471
4647
  if (el.children) for (const ch of el.children) walk(ch, s);
4472
4648
  };
@@ -4481,7 +4657,7 @@ function layoutGlyphTextChars(node, opts) {
4481
4657
  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 });
4482
4658
  }
4483
4659
  }
4484
- const rootContent = (_e = str(node[TEXT_ATTR])) != null ? _e : str(node[TEXT_CONTENT_ATTR]);
4660
+ const rootContent = (_e = str(node[TEXT_CONTENT_ATTR])) != null ? _e : str(node[TEXT_ATTR]);
4485
4661
  if (rootContent && !((_f = node.children) == null ? void 0 : _f.length)) renderChars(rootContent, rootStyle);
4486
4662
  const anchor = str(node.textAnchor);
4487
4663
  if (anchor === "middle" || anchor === "end") {
@@ -4509,7 +4685,7 @@ function layoutGlyphTextCharsAlongPath(node, pathD, opts) {
4509
4685
  const walk = (el, parentStyle) => {
4510
4686
  var _a2, _b2, _c;
4511
4687
  const s = resolveStyle2(el, parentStyle);
4512
- const content = (_a2 = str(el[TEXT_ATTR])) != null ? _a2 : str(el[TEXT_CONTENT_ATTR]);
4688
+ const content = (_a2 = str(el[TEXT_CONTENT_ATTR])) != null ? _a2 : str(el[TEXT_ATTR]);
4513
4689
  if (content && !((_b2 = el.children) == null ? void 0 : _b2.length)) {
4514
4690
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4515
4691
  const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
@@ -4563,7 +4739,7 @@ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
4563
4739
  const walk = (el, parentStyle) => {
4564
4740
  var _a, _b, _c;
4565
4741
  const s = resolveStyle2(el, parentStyle);
4566
- const content = (_a = str(el[TEXT_ATTR])) != null ? _a : str(el[TEXT_CONTENT_ATTR]);
4742
+ const content = (_a = str(el[TEXT_CONTENT_ATTR])) != null ? _a : str(el[TEXT_ATTR]);
4567
4743
  if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
4568
4744
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4569
4745
  if (gf) {
@@ -4791,9 +4967,9 @@ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pat
4791
4967
  }
4792
4968
  return materialiseGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength, pathOverflow);
4793
4969
  }
4794
- function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4970
+ function applyTrimPathEffect(node, trimPath, ctx) {
4795
4971
  if (!trimPath) return node;
4796
- const trimAllAsOne = !!trimPath.trimAllAsOne;
4972
+ const combined = trimPath.subPaths === PxTrimSubPaths.combined;
4797
4973
  const leafEntries = [];
4798
4974
  const measure = (n) => {
4799
4975
  if (Array.isArray(n.children) && n.children.length > 0) {
@@ -4814,16 +4990,16 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4814
4990
  measure(node);
4815
4991
  if (!leafEntries.length) return node;
4816
4992
  let acc = 0;
4817
- const iterOrder = trimAllAsOne ? [...leafEntries].reverse() : leafEntries;
4993
+ const iterOrder = combined ? [...leafEntries].reverse() : leafEntries;
4818
4994
  for (const entry of iterOrder) {
4819
4995
  for (const sp of entry.subpaths) {
4820
- if (!trimAllAsOne) acc = 0;
4996
+ if (!combined) acc = 0;
4821
4997
  sp.startOffsetPx = acc;
4822
4998
  acc += sp.lengthPx;
4823
4999
  }
4824
5000
  }
4825
5001
  const chainLengthPx = acc;
4826
- if (trimAllAsOne && chainLengthPx < 1e-3) return node;
5002
+ if (combined && chainLengthPx < 1e-3) return node;
4827
5003
  const offsetReadRaw = readAnimatable(trimPath.offset);
4828
5004
  const offsetRead = offsetReadRaw.kind === "absent" ? { kind: "static", value: 0 } : offsetReadRaw;
4829
5005
  const rangeReadRaw = readRangeWithCrossings(trimPath.range);
@@ -4835,18 +5011,18 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4835
5011
  if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {
4836
5012
  const entry = leafEntries[0];
4837
5013
  const sp = entry.subpaths[0];
4838
- const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
5014
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
4839
5015
  if (pathLengthPx < 1e-3) return node;
4840
- const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
5016
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
4841
5017
  return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);
4842
5018
  }
4843
5019
  const replacements = /* @__PURE__ */ new Map();
4844
5020
  for (const entry of leafEntries) {
4845
5021
  const newChildren = [];
4846
5022
  for (const sp of entry.subpaths) {
4847
- const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
5023
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
4848
5024
  if (pathLengthPx < 1e-3) continue;
4849
- const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
5025
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
4850
5026
  newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));
4851
5027
  }
4852
5028
  replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));
@@ -4953,22 +5129,8 @@ function computeAnimAttr(read, map) {
4953
5129
  };
4954
5130
  }
4955
5131
  function applyAttr(node, attrName, attr) {
4956
- var _a, _b;
4957
5132
  if (!attr) return;
4958
- if (attr.kind === "static") {
4959
- node[attrName] = attr.value;
4960
- return;
4961
- }
4962
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4963
- const animate = __spreadValues2({}, prevAnimate || {});
4964
- const block = { keyframes: attr.keyframes };
4965
- if (attr.loop !== void 0) block.loop = attr.loop;
4966
- animate[attrName] = block;
4967
- node.animate = animate;
4968
- const firstKf = attr.keyframes[0];
4969
- if (firstKf && ((_a = firstKf.value) != null ? _a : firstKf.v) !== void 0) {
4970
- node[attrName] = (_b = firstKf.value) != null ? _b : firstKf.v;
4971
- }
5133
+ writeAnimatableChannel(node, attrName, attr);
4972
5134
  }
4973
5135
  var OPACITY_STEP_MS = 10;
4974
5136
  function computeOpacityFromRange(rangeRead) {
@@ -5150,9 +5312,9 @@ function applyPlayerEffects(root) {
5150
5312
  nextId: 0,
5151
5313
  contentRefInnerIds: /* @__PURE__ */ new Map(),
5152
5314
  maskAncestorChains: /* @__PURE__ */ new Map(),
5153
- // Resolved engine: `frames` ONLY when explicitly set; auto/webapi/unset →
5154
- // webapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5155
- engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi,
5315
+ // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →
5316
+ // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5317
+ engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.waapi,
5156
5318
  glyphs: (_b = getDefs(root)) == null ? void 0 : _b.glyphs
5157
5319
  };
5158
5320
  const working = clone(root);
@@ -5170,8 +5332,7 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5170
5332
  const originalId = typeof node.id === "string" ? node.id : void 0;
5171
5333
  const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
5172
5334
  if (!fx && !innerIdForContentRef) return node;
5173
- const { transformation, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5174
- const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
5335
+ const { transformBy, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5175
5336
  if (fx) delete node.effects;
5176
5337
  let n = node;
5177
5338
  let consumedByGlyphs = false;
@@ -5191,15 +5352,15 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5191
5352
  if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);
5192
5353
  n = applyFillGradientEffect(n, fillGradient, ctx);
5193
5354
  n = applyStrokeGradientEffect(n, strokeGradient, ctx);
5194
- n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
5355
+ n = applyTrimPathEffect(n, trimPath, ctx);
5195
5356
  n = applyRepeaterEffect(n, repeater, ctx);
5196
- n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
5357
+ n = applyMaskedByEffect(n, maskedBy, transformBy, ctx);
5197
5358
  n = applyClipPathEffect(n, clipPath, ctx);
5198
5359
  if (innerIdForContentRef) {
5199
5360
  applyRefHref(n, cloneFx, ctx);
5200
- n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
5361
+ n = splitForContentRef(n, transformBy, originalId, innerIdForContentRef, ctx);
5201
5362
  } else {
5202
- n = applyRefAndTransformationEffect(n, cloneFx, transformation, ctx);
5363
+ n = applyRefAndTransformationEffect(n, cloneFx, transformBy, ctx);
5203
5364
  }
5204
5365
  if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
5205
5366
  if (originalId) ctx.idMap.set(originalId, n);
@@ -5209,12 +5370,141 @@ function applyPlayerEffects_retime(node, ctx) {
5209
5370
  applyAllRetimeEffects(node, ctx);
5210
5371
  return node;
5211
5372
  }
5373
+ var kfTime = (kf) => {
5374
+ var _a, _b;
5375
+ return (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
5376
+ };
5377
+ var kfValue = (kf) => {
5378
+ var _a;
5379
+ return (_a = kf.v) != null ? _a : kf.value;
5380
+ };
5381
+ var kfEasing = (kf) => {
5382
+ var _a;
5383
+ return (_a = kf.e) != null ? _a : kf.easing;
5384
+ };
5385
+ var kfTangentIn = (kf) => {
5386
+ var _a;
5387
+ return (_a = kf.tangentIn) != null ? _a : kf.ti;
5388
+ };
5389
+ var kfTangentOut = (kf) => {
5390
+ var _a;
5391
+ return (_a = kf.tangentOut) != null ? _a : kf.to;
5392
+ };
5393
+ function cubicAt(p0, c1, c2, p1, t) {
5394
+ const u = 1 - t;
5395
+ const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;
5396
+ return [
5397
+ a * p0[0] + b * c1[0] + c * c2[0] + d * p1[0],
5398
+ a * p0[1] + b * c1[1] + c * c2[1] + d * p1[1]
5399
+ ];
5400
+ }
5401
+ function cubicLength(p0, c1, c2, p1, steps = 64) {
5402
+ let len = 0;
5403
+ let prev = p0;
5404
+ for (let i = 1; i <= steps; i++) {
5405
+ const pt = cubicAt(p0, c1, c2, p1, i / steps);
5406
+ len += Math.hypot(pt[0] - prev[0], pt[1] - prev[1]);
5407
+ prev = pt;
5408
+ }
5409
+ return len;
5410
+ }
5411
+ var fmt2 = (n) => {
5412
+ const r = Math.round(n * 1e4) / 1e4;
5413
+ return Object.is(r, -0) ? "0" : String(r);
5414
+ };
5415
+ function buildOffsetPath(propAnim) {
5416
+ var _a, _b, _c, _d;
5417
+ if (propAnim.alongPathMode !== "offsetPath") return void 0;
5418
+ const kfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
5419
+ if (!kfs || kfs.length < 2) return void 0;
5420
+ const first = kfValue(kfs[0]);
5421
+ const anchor = (first == null ? void 0 : first.origin) && first.origin.length >= 2 ? [first.origin[0], first.origin[1]] : [0, 0];
5422
+ const points = [];
5423
+ for (const kf of kfs) {
5424
+ const v = kfValue(kf);
5425
+ const tr = v == null ? void 0 : v.translate;
5426
+ if (!tr || tr.length < 2) return void 0;
5427
+ const parts = Object.keys(v);
5428
+ if (parts.some((p) => p !== "translate" && p !== "origin")) return void 0;
5429
+ const o = (_b = v == null ? void 0 : v.origin) != null ? _b : [0, 0];
5430
+ if (o[0] !== anchor[0] || o[1] !== anchor[1]) return void 0;
5431
+ points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);
5432
+ }
5433
+ if (!kfs.some((kf) => kfTangentIn(kf) || kfTangentOut(kf))) return void 0;
5434
+ let d = "M" + fmt2(points[0][0]) + "," + fmt2(points[0][1]);
5435
+ const segLens = [];
5436
+ for (let i = 0; i < points.length - 1; i++) {
5437
+ const p0 = points[i], p1 = points[i + 1];
5438
+ const to = (_c = kfTangentOut(kfs[i])) != null ? _c : [0, 0];
5439
+ const ti = (_d = kfTangentIn(kfs[i + 1])) != null ? _d : [0, 0];
5440
+ const c1 = [p0[0] + to[0], p0[1] + to[1]];
5441
+ const c2 = [p1[0] + ti[0], p1[1] + ti[1]];
5442
+ d += "C" + fmt2(c1[0]) + "," + fmt2(c1[1]) + "," + fmt2(c2[0]) + "," + fmt2(c2[1]) + "," + fmt2(p1[0]) + "," + fmt2(p1[1]);
5443
+ segLens.push(cubicLength(p0, c1, c2, p1));
5444
+ }
5445
+ const total = segLens.reduce((a, b) => a + b, 0);
5446
+ if (!(total > 0)) return void 0;
5447
+ const distanceKfs = [];
5448
+ let cum = 0;
5449
+ for (let i = 0; i < kfs.length; i++) {
5450
+ if (i > 0) cum += segLens[i - 1];
5451
+ const out = { t: kfTime(kfs[i]), v: cum / total };
5452
+ const e = kfEasing(kfs[i]);
5453
+ if (e !== void 0) out.e = e;
5454
+ distanceKfs.push(out);
5455
+ }
5456
+ return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor };
5457
+ }
5458
+ function materialiseOffsetPathsInTree(root) {
5459
+ const walk = (node) => {
5460
+ var _a;
5461
+ let out = node;
5462
+ const anim = node.animate;
5463
+ const transform = anim == null ? void 0 : anim["transform"];
5464
+ if (transform) {
5465
+ const built = buildOffsetPath(transform);
5466
+ if (built) {
5467
+ const newAnimate = __spreadValues2({}, anim);
5468
+ delete newAnimate["transform"];
5469
+ const distance = { keyframes: built.distanceKfs };
5470
+ if (transform.loop !== void 0) distance.loop = transform.loop;
5471
+ newAnimate["offsetDistance"] = distance;
5472
+ const staticTr = node.transform;
5473
+ let newTransform = staticTr;
5474
+ if (staticTr && typeof staticTr === "object") {
5475
+ const t = __spreadValues2({}, staticTr);
5476
+ delete t["translate"];
5477
+ delete t["origin"];
5478
+ newTransform = Object.keys(t).length ? t : void 0;
5479
+ }
5480
+ out = __spreadProps2(__spreadValues2({}, node), {
5481
+ animate: newAnimate,
5482
+ style: __spreadProps2(__spreadValues2({}, node.style), {
5483
+ offsetPath: "path('" + built.pathStr + "')",
5484
+ offsetAnchor: fmt2(built.anchor[0]) + "px " + fmt2(built.anchor[1]) + "px",
5485
+ offsetRotate: built.autoOrient ? "auto" : "0deg",
5486
+ offsetDistance: "0%"
5487
+ })
5488
+ });
5489
+ if (newTransform !== void 0) out.transform = newTransform;
5490
+ else delete out.transform;
5491
+ }
5492
+ }
5493
+ if ((_a = out.children) == null ? void 0 : _a.length) {
5494
+ const children = out.children.map(walk);
5495
+ if (children.some((c, i) => c !== out.children[i])) out = __spreadProps2(__spreadValues2({}, out), { children });
5496
+ }
5497
+ return out;
5498
+ };
5499
+ return walk(root);
5500
+ }
5212
5501
  function materialiseAllInTree(doc, engine, opts) {
5213
5502
  var _a, _b;
5214
5503
  let root = applyPlayerEffects(doc).root;
5504
+ root = materialiseOffsetPathsInTree(root);
5215
5505
  const duration = (_b = (_a = getAnimatorConfig(root)) == null ? void 0 : _a.duration) != null ? _b : DEFAULT_DURATION_MS;
5216
5506
  root = materialiseInternalLoopsInTree(root, duration);
5217
- if (engine === PxAnimatorEngine.webapi) {
5507
+ if (engine === PxAnimatorEngine.waapi) {
5218
5508
  root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
5219
5509
  root = materialiseAnimatedUseInstances(root);
5220
5510
  root = pruneUnreferencedDefs(root);
@@ -5599,6 +5889,7 @@ function evalTransformValue(v, t) {
5599
5889
  if (typeof v === "string") return parseTransformString(v);
5600
5890
  if (v.keyframes) return partsToMatrix(interpParts(v.keyframes, t));
5601
5891
  if (v.value) return partsToMatrix(v.value);
5892
+ if (typeof v === "object" && !Array.isArray(v)) return partsToMatrix(v);
5602
5893
  return IDENTITY;
5603
5894
  }
5604
5895
  function nodeMatrix(node, t) {
@@ -5733,64 +6024,6 @@ function subtractMultiset(a, b) {
5733
6024
  return extra;
5734
6025
  }
5735
6026
 
5736
- // src/PxAnimatorDOM.ts
5737
- var SVG_NS = "http://www.w3.org/2000/svg";
5738
- function createElement(tagName, normalisedProps, style, children, textContent) {
5739
- if (DISALLOWED_SVG_TAGS_LOWER.has(tagName.toLowerCase())) {
5740
- console.warn("SVG tag blocked (dangerous): ", tagName);
5741
- return null;
5742
- }
5743
- const element = document.createElementNS(SVG_NS, tagName);
5744
- for (const propName in normalisedProps) {
5745
- const sanitised = sanitiseAttributeValue(propName, normalisedProps[propName]);
5746
- if (sanitised === void 0) continue;
5747
- if (CSS_ONLY_STYLE_PROPS.has(propName)) {
5748
- element.style[propName] = String(sanitised);
5749
- continue;
5750
- }
5751
- element.setAttribute(camelCaseToKebabWordIfNeeded(propName), sanitised);
5752
- }
5753
- if (style) {
5754
- for (const styleProp in style) {
5755
- element.style[styleProp] = String(style[styleProp]);
5756
- }
5757
- }
5758
- if (children) {
5759
- for (const child of children) {
5760
- element.appendChild(child);
5761
- }
5762
- }
5763
- if (textContent) element.textContent = textContent;
5764
- return element;
5765
- }
5766
- function renderNode(node, defs) {
5767
- if (!node) return null;
5768
- const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
5769
- const feFuncType = props.funcType;
5770
- if (feFuncType !== void 0) delete props.funcType;
5771
- const nodeDefs = getDefs(node) || defs;
5772
- const resolvedStyle = resolveStyle(style, nodeDefs);
5773
- let childElements;
5774
- if (children) {
5775
- for (const ch of children) {
5776
- const child = renderNode(ch, nodeDefs);
5777
- if (child) {
5778
- if (!childElements) childElements = [];
5779
- childElements.push(child);
5780
- }
5781
- }
5782
- }
5783
- const element = createElement(
5784
- type || "g",
5785
- getNormalizedProps(props),
5786
- resolvedStyle,
5787
- childElements,
5788
- props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
5789
- );
5790
- if (element && feFuncType !== void 0) element.setAttribute("type", feFuncType);
5791
- return element;
5792
- }
5793
-
5794
6027
  // src/PxAnimatorTriggers.ts
5795
6028
  function setupAnimationTriggers(api, config) {
5796
6029
  const { startOn, outAction = "continue", scrollIntoViewThreshold = 0 } = config;
@@ -5977,10 +6210,12 @@ function createCssKf(kf, t, propName, unsupportedSet) {
5977
6210
  } else if (propName === "d") {
5978
6211
  const paths = value && typeof value === "object" && Array.isArray(value.paths) ? value.paths : [];
5979
6212
  cssValue = 'path("' + paths.map((bz) => bezierToSvgPath(bz, true)).join("") + '")';
6213
+ } else if (PCT_BASED_ATTR_NAMES.has(propName) && typeof value === "number") {
6214
+ cssValue = value * 100 + "%";
5980
6215
  } else {
5981
6216
  cssValue = "" + value;
5982
6217
  }
5983
- if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
6218
+ if (!CSS.supports(camelCaseToKebabWordIfNeeded(cssKey), cssValue)) unsupportedSet.add(cssKey);
5984
6219
  cssKey = kebabToCamelCaseWord(cssKey);
5985
6220
  cssKf[cssKey] = cssValue;
5986
6221
  return cssKf;
@@ -6057,7 +6292,7 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
6057
6292
  console.warn("createFrameLoopAnimator: No root element provided");
6058
6293
  }
6059
6294
  }
6060
- const bindings = getNormalisedBindings(doc, PxAnimatorEngine.webapi);
6295
+ const bindings = getNormalisedBindings(doc, PxAnimatorEngine.waapi);
6061
6296
  const animations = [];
6062
6297
  const _iterations = config.iterations;
6063
6298
  let iterations;
@@ -6203,9 +6438,8 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
6203
6438
  return api;
6204
6439
  }
6205
6440
 
6206
- // src/PxAnimator.ts
6207
- function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
6208
- const animatorConfig = getAnimatorConfig(doc) || {};
6441
+ // src/PxAnimatorBind.ts
6442
+ function finaliseAnimator(animatorConfig, callbacks, make) {
6209
6443
  let apiRef;
6210
6444
  let effectiveCallbacks = callbacks;
6211
6445
  if (animatorConfig.resetOnFinish) {
@@ -6217,29 +6451,99 @@ function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
6217
6451
  }
6218
6452
  });
6219
6453
  }
6220
- let res;
6221
- if (animatorConfig.mode === PxAnimatorMode.frames) {
6222
- res = createFrameLoopAnimator(doc, adapter, effectiveCallbacks, rootElement);
6223
- } else {
6224
- res = createWebApiAnimator(
6225
- doc,
6226
- effectiveCallbacks,
6227
- rootElement,
6228
- animatorConfig.mode === PxAnimatorMode.webapi
6229
- // forcing webapi
6230
- ) || createFrameLoopAnimator(doc, adapter, effectiveCallbacks, rootElement);
6231
- }
6454
+ const res = make(effectiveCallbacks);
6232
6455
  apiRef = res;
6233
6456
  if (animatorConfig.debugInstName) {
6234
6457
  window[animatorConfig.debugInstName] = res;
6235
6458
  }
6236
6459
  return res;
6237
6460
  }
6461
+ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6462
+ const animatorConfig = getAnimatorConfig(doc) || {};
6463
+ return finaliseAnimator(animatorConfig, callbacks, (cb) => {
6464
+ if (animatorConfig.mode === PxAnimatorMode.frames) {
6465
+ return createFrameLoopAnimator(doc, adapter, cb, rootElement);
6466
+ }
6467
+ return createWebApiAnimator(
6468
+ doc,
6469
+ cb,
6470
+ rootElement,
6471
+ animatorConfig.mode === PxAnimatorMode.waapi
6472
+ // forcing waapi
6473
+ ) || createFrameLoopAnimator(doc, adapter, cb, rootElement);
6474
+ });
6475
+ }
6476
+
6477
+ // src/PxAnimatorDOM.ts
6478
+ var SVG_NS = "http://www.w3.org/2000/svg";
6479
+ function createElement(tagName, normalisedProps, style, children, textContent) {
6480
+ if (DISALLOWED_SVG_TAGS_LOWER.has(tagName.toLowerCase())) {
6481
+ console.warn("SVG tag blocked (dangerous): ", tagName);
6482
+ return null;
6483
+ }
6484
+ const element = document.createElementNS(SVG_NS, tagName);
6485
+ for (const propName in normalisedProps) {
6486
+ const sanitised = sanitiseAttributeValue(propName, normalisedProps[propName]);
6487
+ if (sanitised === void 0) continue;
6488
+ if (CSS_ONLY_STYLE_PROPS.has(propName)) {
6489
+ element.style[propName] = String(sanitised);
6490
+ continue;
6491
+ }
6492
+ element.setAttribute(camelCaseToKebabWordIfNeeded(propName), sanitised);
6493
+ }
6494
+ if (style) {
6495
+ for (const styleProp in style) {
6496
+ element.style[styleProp] = String(style[styleProp]);
6497
+ }
6498
+ }
6499
+ if (children) {
6500
+ for (const child of children) {
6501
+ element.appendChild(child);
6502
+ }
6503
+ }
6504
+ if (textContent) element.textContent = textContent;
6505
+ return element;
6506
+ }
6507
+ function renderNode(node, defs) {
6508
+ if (!node) return null;
6509
+ const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
6510
+ const domType = props.domType;
6511
+ if (domType !== void 0) delete props.domType;
6512
+ const nodeDefs = getDefs(node) || defs;
6513
+ const resolvedStyle = resolveStyle(style, nodeDefs);
6514
+ let childElements;
6515
+ if (children) {
6516
+ for (const ch of children) {
6517
+ const child = renderNode(ch, nodeDefs);
6518
+ if (child) {
6519
+ if (!childElements) childElements = [];
6520
+ childElements.push(child);
6521
+ }
6522
+ }
6523
+ }
6524
+ const element = createElement(
6525
+ type || "g",
6526
+ getNormalizedProps(props),
6527
+ resolvedStyle,
6528
+ childElements,
6529
+ props[TEXT_CONTENT_ATTR] || props[TEXT_ATTR]
6530
+ );
6531
+ if (element && domType !== void 0) element.setAttribute("type", domType);
6532
+ return element;
6533
+ }
6534
+
6535
+ // src/PxAnimatorKeys.ts
6536
+ var PX_ANIMATOR_DATA_KEY = "data";
6537
+
6538
+ // src/PxAnimator.ts
6539
+ function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
6540
+ return bindWithEngineChoice(doc, adapter, callbacks, rootElement);
6541
+ }
6238
6542
  function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
6239
6543
  const effectsWarnings = validateNodeEffects(doc);
6240
6544
  for (const w of effectsWarnings) console.warn("[PxAnimator] effects shape warning:", w);
6241
6545
  const animatorConfig = getAnimatorConfig(doc) || {};
6242
- const engine = animatorConfig.mode === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi;
6546
+ const engine = animatorConfig.mode === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.waapi;
6243
6547
  doc = materialiseAllInTree(doc, engine);
6244
6548
  let rootElement = null;
6245
6549
  if (containerElement) {
@@ -6254,7 +6558,6 @@ function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
6254
6558
  }
6255
6559
  return createAnimatorFromConfig(doc, adapter, callbacks, rootElement);
6256
6560
  }
6257
- var PX_ANIMATOR_DATA_KEY = "data";
6258
6561
  function createAnimator(options) {
6259
6562
  const { src, data, adapter, callbacks, container } = options;
6260
6563
  if (data !== void 0 && src !== void 0) {
@@ -6363,6 +6666,7 @@ if (typeof window !== "undefined") {
6363
6666
  PxGradientType,
6364
6667
  PxGradientUnits,
6365
6668
  PxKeyframeSchema,
6669
+ PxKeyframeValueSchema,
6366
6670
  PxLoopSchema,
6367
6671
  PxMaskedByEffectSchema,
6368
6672
  PxNodeBase,
@@ -6374,9 +6678,9 @@ if (typeof window !== "undefined") {
6374
6678
  PxSvgNodeExtra,
6375
6679
  PxTextEffectSchema,
6376
6680
  PxTextPathEffectSchema,
6681
+ PxTransformByEffectSchema,
6377
6682
  PxTransformPartsSchema,
6378
6683
  PxTransformValueSchema,
6379
- PxTransformationEffectSchema,
6380
6684
  PxTriggerSchema,
6381
6685
  PxTrimPathEffectSchema,
6382
6686
  STYLE_ATTR_NAMES,