@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.js CHANGED
@@ -184,8 +184,9 @@ var Union = class extends Base {
184
184
  }
185
185
  isValid(raw, ctx, path) {
186
186
  var _a;
187
- if (this.schemas.some((s) => s.isValid(raw))) return true;
188
- 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));
187
+ const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
188
+ if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
189
+ 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));
189
190
  return false;
190
191
  }
191
192
  _canSanitize(raw) {
@@ -261,6 +262,7 @@ var Obj = class extends Base {
261
262
  if (ctx == null ? void 0 : ctx.strict) {
262
263
  for (const key of Object.keys(obj)) {
263
264
  if (key in this._shape) continue;
265
+ if (obj[key] === void 0) continue;
264
266
  p.push(key);
265
267
  ctx.errors.push(pathStr(p) + ": unexpected extra key");
266
268
  p.pop();
@@ -401,6 +403,23 @@ var Any = class extends Base {
401
403
  return true;
402
404
  }
403
405
  };
406
+ var Defined = class extends Base {
407
+ constructor() {
408
+ super(...arguments);
409
+ this._default = void 0;
410
+ }
411
+ sanitize(raw) {
412
+ return raw;
413
+ }
414
+ isValid(raw, ctx, path) {
415
+ if (raw !== void 0) return true;
416
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": required value is missing");
417
+ return false;
418
+ }
419
+ _canSanitize(raw) {
420
+ return raw !== void 0;
421
+ }
422
+ };
404
423
  var Lazy = class extends Base {
405
424
  constructor(fn, _default) {
406
425
  super();
@@ -512,6 +531,8 @@ var px = {
512
531
  record: (value) => new Rec(value),
513
532
  /** Passes anything through unchanged — always valid. */
514
533
  any: () => new Any(),
534
+ /** Anything EXCEPT `undefined` — an open type whose presence is required (V6). */
535
+ defined: () => new Defined(),
515
536
  /** Fixed-length tuple — validates element count and each position individually. */
516
537
  tuple: (schemas) => new Tuple(schemas),
517
538
  /** Defers schema creation — required for recursive types. Must supply a default value. */
@@ -521,13 +542,52 @@ var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
521
542
  var PX_ANIM_ATTR_NAME = "_px_animator";
522
543
  var PxAnimatorMode = {
523
544
  auto: "auto",
524
- webapi: "webapi",
545
+ waapi: "waapi",
525
546
  frames: "frames"
526
547
  };
527
548
  var PxAnimatorEngine = {
528
- webapi: PxAnimatorMode.webapi,
549
+ waapi: PxAnimatorMode.waapi,
529
550
  frames: PxAnimatorMode.frames
530
551
  };
552
+ var PxLoopExtend = {
553
+ /** Segment from the START; the animation is extended BEFORE the first keyframe
554
+ * (intro loops that run before the main timeline begins). */
555
+ before: "before",
556
+ /** DEFAULT — segment from the END; extended AFTER the last keyframe (idle/outro
557
+ * loops that continue once the main timeline has finished). */
558
+ after: "after"
559
+ };
560
+ var PxMaskType = {
561
+ luminance: "luminance",
562
+ alpha: "alpha"
563
+ };
564
+ var PxUnits = {
565
+ userSpaceOnUse: "userSpaceOnUse",
566
+ objectBoundingBox: "objectBoundingBox"
567
+ };
568
+ var PxCloneType = {
569
+ content: "content"
570
+ };
571
+ var PxPathOverflow = {
572
+ clip: "clip",
573
+ extend: "extend"
574
+ };
575
+ var PxLengthAdjust = {
576
+ spacing: "spacing",
577
+ spacingAndGlyphs: "spacingAndGlyphs"
578
+ };
579
+ var PxTextPathMethod = {
580
+ align: "align",
581
+ stretch: "stretch"
582
+ };
583
+ var PxTextPathSpacing = {
584
+ auto: "auto",
585
+ exact: "exact"
586
+ };
587
+ var PxTrimSubPaths = {
588
+ separate: "separate",
589
+ combined: "combined"
590
+ };
531
591
  var TEXT_ATTR = "text";
532
592
  var TEXT_CONTENT_ATTR = "textContent";
533
593
  var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
@@ -536,9 +596,49 @@ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
536
596
  "animator",
537
597
  "meta",
538
598
  "animate",
599
+ "effects",
539
600
  TEXT_ATTR,
540
601
  TEXT_CONTENT_ATTR
541
602
  ]);
603
+ var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
604
+ var PxGradientUnits = {
605
+ userSpaceOnUse: "userSpaceOnUse",
606
+ objectBoundingBox: "objectBoundingBox"
607
+ };
608
+ var PxGradientSpreadMethod = {
609
+ pad: "pad",
610
+ reflect: "reflect",
611
+ repeat: "repeat"
612
+ };
613
+ var PxGradientType = {
614
+ linear: "linear",
615
+ radial: "radial"
616
+ };
617
+ function isPxElementFileFormat(fileJson) {
618
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
619
+ return false;
620
+ }
621
+ return fileJson["type"] === "svg";
622
+ }
623
+ function getAnimatorConfig(doc) {
624
+ var _a;
625
+ return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
626
+ }
627
+ function getDefs(doc) {
628
+ var _a;
629
+ if (!doc) return void 0;
630
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
631
+ }
632
+ function getBindings(doc) {
633
+ var _a;
634
+ if (!doc) return void 0;
635
+ const animateById = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animateById;
636
+ if (!animateById) return void 0;
637
+ return Object.entries(animateById).map(([id, anim]) => ({ id, animate: anim }));
638
+ }
639
+ function getChildren(doc) {
640
+ return doc == null ? void 0 : doc.children;
641
+ }
542
642
  var PxEasingOrRefSchema = px.union([
543
643
  px.string(),
544
644
  px.tuple([px.number(), px.number(), px.number(), px.number()])
@@ -550,13 +650,15 @@ var PxKeyframeValueSchema = implementsInterface()(px.union([
550
650
  px.array(px.number()),
551
651
  px.lazy(() => PxTransformPartsSchema, {}),
552
652
  px.object({ path: px.string() }),
553
- px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] })
653
+ px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
654
+ // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
655
+ px.lazy(() => px.array(PxGradientStopSchema), [])
554
656
  ]));
555
657
  var PxKeyframeSchema = implementsInterface()(px.object({
556
658
  time: px.number().optional(),
557
659
  t: px.number().optional(),
558
- value: px.any().optional(),
559
- v: px.any().optional(),
660
+ value: PxKeyframeValueSchema.optional(),
661
+ v: PxKeyframeValueSchema.optional(),
560
662
  easing: PxEasingOrRefSchema.optional(),
561
663
  e: PxEasingOrRefSchema.optional(),
562
664
  tangentOut: px.tuple([px.number(), px.number()]).optional(),
@@ -570,16 +672,16 @@ var PxKeyframeSchema = implementsInterface()(px.object({
570
672
  }));
571
673
  var PxLoopSchema = implementsInterface()(px.object({
572
674
  segmentCount: px.number().optional(),
573
- before: px.boolean().optional(),
675
+ extend: px.enum([PxLoopExtend.before, PxLoopExtend.after]).optional(),
574
676
  alternate: px.boolean().optional()
575
677
  }));
576
678
  var PxPropertyAnimationSchema = implementsInterface()(px.object({
679
+ value: PxKeyframeValueSchema.optional(),
577
680
  keyframes: px.array(PxKeyframeSchema).optional(),
578
681
  kfs: px.array(PxKeyframeSchema).optional(),
579
682
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
580
683
  autoOrient: px.boolean().optional()
581
684
  }));
582
- var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
583
685
  var PxTransformPartsSchema = implementsInterface()(px.object({
584
686
  translate: px.tuple([px.number(), px.number()]).optional(),
585
687
  rotate: px.number().optional(),
@@ -589,6 +691,7 @@ var PxTransformPartsSchema = implementsInterface()(px.object({
589
691
  }));
590
692
  var PxTransformValueSchema = px.union([
591
693
  px.string(),
694
+ PxTransformPartsSchema,
592
695
  px.object({ value: PxTransformPartsSchema }),
593
696
  PxPropertyAnimationSchema
594
697
  ]);
@@ -623,9 +726,11 @@ var PxDefsSchema = implementsInterface()(px.object({
623
726
  glyphs: px.record(PxGlyphFontSchema).optional()
624
727
  }));
625
728
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
626
- mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.webapi, PxAnimatorMode.frames]).optional(),
729
+ mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.waapi, PxAnimatorMode.frames]).optional(),
627
730
  duration: px.number().optional(),
628
731
  delay: px.number().optional(),
732
+ // LAW (SCHEMA-DESIGN R3, S10): the ONE sanctioned string-in-number union
733
+ // (CSS animation-iteration-count familiarity) — do not add more mixed unions.
629
734
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
630
735
  fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
631
736
  direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
@@ -633,9 +738,8 @@ var PxAnimatorConfigSchema = implementsInterface()(px.object({
633
738
  trigger: PxTriggerSchema.optional(),
634
739
  resetOnFinish: px.boolean().optional(),
635
740
  definitions: PxDefsSchema.optional(),
636
- animate: px.record(PxElementAnimationSchema).optional(),
637
- timeline: px.string().optional(),
638
- debug: px.boolean().optional(),
741
+ animateById: px.record(PxElementAnimationSchema).optional(),
742
+ timelineSource: px.string().optional(),
639
743
  debugInstName: px.string().optional()
640
744
  }));
641
745
  var PxBindingSchema = implementsInterface()(px.object({
@@ -645,26 +749,29 @@ var PxBindingSchema = implementsInterface()(px.object({
645
749
  var PxAttrValueSchema = px.union([
646
750
  px.string(),
647
751
  px.number(),
648
- px.object({ value: px.any() }),
649
- PxPropertyAnimationSchema
752
+ px.array(px.number()),
753
+ // Structured static — `{value: …}` (read-accepted transitional spelling, S1).
754
+ // `defined`, not `any`: the KEY's presence is what identifies this branch (V6).
755
+ px.object({ value: px.defined() }),
756
+ // Bare transform parts record — the canonical static `transform` on the wire (T2).
757
+ PxTransformPartsSchema
650
758
  ]);
651
759
  var PxAnimatableNumberSchema = px.union([
652
760
  px.number(),
653
761
  px.object({ value: px.number() }),
654
- px.object({
655
- keyframes: px.array(PxKeyframeSchema),
656
- autoOrient: px.boolean().optional()
657
- })
762
+ PxPropertyAnimationSchema
658
763
  ]);
659
764
  var PxAnimatableVec2Schema = px.union([
660
765
  px.tuple([px.number(), px.number()]),
661
766
  px.object({ value: px.tuple([px.number(), px.number()]) }),
662
- px.object({
663
- keyframes: px.array(PxKeyframeSchema),
664
- autoOrient: px.boolean().optional()
665
- })
767
+ PxPropertyAnimationSchema
666
768
  ]);
667
- var PxTransformationEffectSchema = implementsInterface()(px.object({
769
+ var PxAnimatableStringSchema = px.union([
770
+ px.string(),
771
+ px.object({ value: px.string() }),
772
+ PxPropertyAnimationSchema
773
+ ]);
774
+ var PxTransformByEffectSchema = implementsInterface()(px.object({
668
775
  translate: PxAnimatableVec2Schema.optional(),
669
776
  rotate: PxAnimatableNumberSchema.optional(),
670
777
  scale: PxAnimatableVec2Schema.optional(),
@@ -672,51 +779,46 @@ var PxTransformationEffectSchema = implementsInterface()(px.object({
672
779
  origin: PxAnimatableVec2Schema.optional()
673
780
  }));
674
781
  var PxRepeaterEffectSchema = implementsInterface()(px.object({
782
+ // STATIC config, not a channel (V2/SCHEMA-DESIGN R5): the copy COUNT is read
783
+ // once at expansion time and never sampled — plain number, no `keyframes`.
675
784
  copies: px.number().optional(),
676
785
  translate: PxAnimatableVec2Schema.optional(),
677
786
  rotate: PxAnimatableNumberSchema.optional(),
787
+ skew: PxAnimatableNumberSchema.optional(),
678
788
  scale: PxAnimatableVec2Schema.optional(),
679
789
  origin: PxAnimatableVec2Schema.optional()
680
790
  }));
681
791
  var PxMaskedByEffectSchema = implementsInterface()(px.object({
682
- href: px.string().optional(),
683
- maskType: px.string().optional(),
684
- maskUnits: px.string().optional(),
685
- maskContentUnits: px.string().optional()
792
+ sourceId: px.string().optional(),
793
+ maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha]).optional(),
794
+ maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
795
+ maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
796
+ x: px.number().optional(),
797
+ y: px.number().optional(),
798
+ width: px.number().optional(),
799
+ height: px.number().optional()
686
800
  }));
687
801
  var PxClipPathEffectSchema = implementsInterface()(px.object({
688
- d: px.string().optional(),
802
+ d: PxAnimatableStringSchema.optional(),
689
803
  animate: PxPropertyAnimationSchema.optional()
690
804
  }));
691
805
  var PxTrimPathEffectSchema = implementsInterface()(px.object({
692
806
  offset: PxAnimatableNumberSchema.optional(),
693
807
  range: PxAnimatableVec2Schema.optional(),
694
- trimAllAsOne: px.boolean().optional()
808
+ subPaths: px.enum([PxTrimSubPaths.separate, PxTrimSubPaths.combined]).optional()
695
809
  }));
696
810
  var PxRetimeEffectSchema = implementsInterface()(px.object({
697
- baseId: px.string().optional(),
811
+ sourceId: px.string().optional(),
698
812
  start: px.number().optional(),
699
813
  stretch: px.number().optional(),
700
814
  timeCrop: px.tuple([px.number(), px.number()]).optional()
701
815
  }));
702
816
  var PxCloneEffectSchema = implementsInterface()(px.object({
703
- type: px.string().optional(),
704
- baseId: px.string().optional(),
817
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
818
+ type: px.enum([PxCloneType.content]).optional(),
819
+ sourceId: px.string().optional(),
705
820
  retime: PxRetimeEffectSchema.optional()
706
821
  }));
707
- var PxGradientUnits = {
708
- userSpaceOnUse: "userSpaceOnUse",
709
- objectBoundingBox: "objectBoundingBox"
710
- };
711
- var PxGradientSpreadMethod = {
712
- pad: "pad",
713
- reflect: "reflect",
714
- repeat: "repeat"
715
- };
716
- var PxGradientType = {
717
- linear: "linear",
718
- radial: "radial"
719
- };
720
822
  var PxGradientStopSchema = implementsInterface()(px.object({
721
823
  offset: px.number(),
722
824
  color: px.string()
@@ -724,39 +826,28 @@ var PxGradientStopSchema = implementsInterface()(px.object({
724
826
  var PxAnimatableGradientStopsSchema = px.union([
725
827
  px.array(PxGradientStopSchema),
726
828
  px.object({ value: px.array(PxGradientStopSchema) }),
727
- px.object({ keyframes: px.array(PxKeyframeSchema) })
829
+ PxPropertyAnimationSchema
728
830
  ]);
729
- var PxGradientGeometryAnimationSchema = implementsInterface()(px.object({
730
- gradientX1: PxPropertyAnimationSchema.optional(),
731
- gradientY1: PxPropertyAnimationSchema.optional(),
732
- gradientX2: PxPropertyAnimationSchema.optional(),
733
- gradientY2: PxPropertyAnimationSchema.optional(),
734
- gradientCx: PxPropertyAnimationSchema.optional(),
735
- gradientCy: PxPropertyAnimationSchema.optional(),
736
- gradientFx: PxPropertyAnimationSchema.optional(),
737
- gradientFy: PxPropertyAnimationSchema.optional(),
738
- gradientR: PxPropertyAnimationSchema.optional()
739
- }));
740
831
  var PxFillGradientEffectSchema = implementsInterface()(px.object({
832
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
741
833
  type: px.enum([PxGradientType.linear, PxGradientType.radial]),
742
- p1: px.tuple([px.number(), px.number()]).optional(),
743
- p2: px.tuple([px.number(), px.number()]).optional(),
744
- c: px.tuple([px.number(), px.number()]).optional(),
745
- r: px.number().optional(),
746
- fp: px.tuple([px.number(), px.number()]).optional(),
834
+ p1: PxAnimatableVec2Schema.optional(),
835
+ p2: PxAnimatableVec2Schema.optional(),
836
+ c: PxAnimatableVec2Schema.optional(),
837
+ r: PxAnimatableNumberSchema.optional(),
838
+ fp: PxAnimatableVec2Schema.optional(),
747
839
  stops: PxAnimatableGradientStopsSchema.optional(),
748
- gradientUnits: px.string().optional(),
749
- spreadMethod: px.string().optional(),
750
- gradientTransform: px.string().optional(),
751
- animate: PxGradientGeometryAnimationSchema.optional()
840
+ gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
841
+ spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
842
+ gradientTransform: px.string().optional()
752
843
  }));
753
844
  var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
754
845
  var PxTextPathEffectSchema = implementsInterface()(px.object({
755
846
  path: px.string(),
756
- pathOverflow: px.string().optional(),
757
- lengthAdjust: px.string().optional(),
758
- method: px.string().optional(),
759
- spacing: px.string().optional(),
847
+ pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
848
+ lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
849
+ method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
850
+ spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
760
851
  startOffset: PxAnimatableNumberSchema.optional(),
761
852
  textLength: PxAnimatableNumberSchema.optional()
762
853
  }));
@@ -764,13 +855,12 @@ var PxTextEffectSchema = implementsInterface()(px.object({
764
855
  useGlyphs: px.boolean().optional()
765
856
  }));
766
857
  var PxEffectsSchema = implementsInterface()(px.object({
767
- transformation: PxTransformationEffectSchema.optional(),
858
+ transformBy: PxTransformByEffectSchema.optional(),
768
859
  repeater: PxRepeaterEffectSchema.optional(),
769
860
  maskedBy: PxMaskedByEffectSchema.optional(),
770
861
  clipPath: PxClipPathEffectSchema.optional(),
771
862
  trimPath: PxTrimPathEffectSchema.optional(),
772
863
  clone: PxCloneEffectSchema.optional(),
773
- isCombinedShape: px.boolean().optional(),
774
864
  fillGradient: PxFillGradientEffectSchema.optional(),
775
865
  strokeGradient: PxStrokeGradientEffectSchema.optional(),
776
866
  textPath: PxTextPathEffectSchema.optional(),
@@ -794,6 +884,14 @@ function validateNodeEffects(root, opts) {
794
884
  return warnings;
795
885
  }
796
886
  var PxNodeBase = px.openObject({
887
+ // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
888
+ // kind of thing is this", discriminated by its CARRIER — here the node TAG
889
+ // (`rect`, `text`), and inside a sub-object that object's kind (`clone.type`,
890
+ // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
891
+ // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
892
+ // would add words that all mean "type" and still need the carrier to read.
893
+ // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
894
+ // (issues V3), never of distinct key names.
797
895
  type: px.string(),
798
896
  id: px.string().optional(),
799
897
  meta: px.any().optional(),
@@ -803,7 +901,7 @@ var PxNodeBase = px.openObject({
803
901
  effects: PxEffectsSchema.optional(),
804
902
  // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
805
903
  // string ref / array of refs / inline definition / mixed array; mirrors
806
- // `animator.animate` map values and what `processNode` resolves at runtime.
904
+ // `animator.animateById` map values and what `processNode` resolves at runtime.
807
905
  animate: PxElementAnimationSchema.optional(),
808
906
  style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
809
907
  }, PxAttrValueSchema);
@@ -811,8 +909,10 @@ var PxNodeSchema = px.openObject(__spreadProps2(__spreadValues2({}, PxNodeBase._
811
909
  children: px.lazy(() => px.array(PxNodeSchema), []).optional()
812
910
  }), PxAttrValueSchema);
813
911
  var PxSvgNodeExtra = px.object({
814
- width: px.number().optional(),
815
- height: px.number().optional(),
912
+ // `"100%"` and other SVG length strings are legal here — a number-only slot rejected
913
+ // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.
914
+ width: px.union([px.number(), px.string()]).optional(),
915
+ height: px.union([px.number(), px.string()]).optional(),
816
916
  viewBox: px.string().optional(),
817
917
  animator: PxAnimatorConfigSchema.optional()
818
918
  });
@@ -827,35 +927,10 @@ var PxBezierPathSchema = implementsInterface()(px.object({
827
927
  o: px.array(px.array(px.number())).optional(),
828
928
  c: px.boolean().optional()
829
929
  }));
830
- function isPxElementFileFormat(fileJson) {
831
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
832
- return false;
833
- }
834
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
835
- }
836
930
  function isPxElementFileFormatDeep(fileJson) {
837
931
  const valid = PxAnimatedSvgDocumentSchema.isValid(fileJson);
838
932
  return { valid, errors: valid ? [] : ["Document failed schema validation"] };
839
933
  }
840
- function getAnimatorConfig(doc) {
841
- var _a, _b;
842
- 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);
843
- }
844
- function getDefs(doc) {
845
- var _a;
846
- if (!doc) return void 0;
847
- return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
848
- }
849
- function getBindings(doc) {
850
- var _a;
851
- if (!doc) return void 0;
852
- const animate = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animate;
853
- if (!animate) return void 0;
854
- return Object.entries(animate).map(([id, anim]) => ({ id, animate: anim }));
855
- }
856
- function getChildren(doc) {
857
- return doc == null ? void 0 : doc.children;
858
- }
859
934
  function bezierToSvgPath(path, forceCurves = false) {
860
935
  var _a, _b, _c, _d;
861
936
  const v = path.v;
@@ -1306,7 +1381,7 @@ function generateNewIds(doc) {
1306
1381
  "flood-color",
1307
1382
  "lighting-color"
1308
1383
  ]);
1309
- const directIdRefAttrs = /* @__PURE__ */ new Set(["baseId", "targetId", "boundElementId"]);
1384
+ const directIdRefAttrs = /* @__PURE__ */ new Set(["sourceId", "targetId", "boundElementId"]);
1310
1385
  function collectIds(node) {
1311
1386
  if (!node || typeof node !== "object") return;
1312
1387
  if (node.id && typeof node.id === "string") {
@@ -1344,9 +1419,10 @@ function generateNewIds(doc) {
1344
1419
  } else if (urlRefAttrs.has(key)) {
1345
1420
  node[key] = replaceUrlRefs(value, idMap);
1346
1421
  } else if (directIdRefAttrs.has(key)) {
1347
- const newId = idMap.get(value);
1422
+ const hasHash = value.startsWith("#");
1423
+ const newId = idMap.get(hasHash ? value.slice(1) : value);
1348
1424
  if (newId) {
1349
- node[key] = newId;
1425
+ node[key] = hasHash ? "#" + newId : newId;
1350
1426
  }
1351
1427
  } else if (value.includes("url(#")) {
1352
1428
  node[key] = replaceUrlRefs(value, idMap);
@@ -1364,14 +1440,14 @@ function generateNewIds(doc) {
1364
1440
  }
1365
1441
  collectIds(cloned);
1366
1442
  updateRefs(cloned);
1367
- const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animate;
1443
+ const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animateById;
1368
1444
  if (docAnimate && typeof docAnimate === "object") {
1369
1445
  const updatedAnimate = {};
1370
1446
  for (const [id, anim] of Object.entries(docAnimate)) {
1371
1447
  const newId = (_b = idMap.get(id)) != null ? _b : id;
1372
1448
  updatedAnimate[newId] = anim;
1373
1449
  }
1374
- cloned.animator = __spreadProps2(__spreadValues2({}, cloned.animator), { animate: updatedAnimate });
1450
+ cloned.animator = __spreadProps2(__spreadValues2({}, cloned.animator), { animateById: updatedAnimate });
1375
1451
  }
1376
1452
  return cloned;
1377
1453
  }
@@ -1460,8 +1536,9 @@ function getNormalizedProps(props) {
1460
1536
  let value = props[rawKey];
1461
1537
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
1462
1538
  propsCopy[key] = toRGBA(value);
1463
- } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
1464
- propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
1539
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes && !value.kfs) {
1540
+ const parts = value.value && typeof value.value === "object" ? value.value : value;
1541
+ propsCopy["transform"] = composeTransformParts(parts, { withUnits: false });
1465
1542
  } else if (TRANSFORM_FN_NAMES.has(key)) {
1466
1543
  if (Array.isArray(value)) {
1467
1544
  if (key === "translate") value = value.map((v) => v + "px");
@@ -1469,6 +1546,8 @@ function getNormalizedProps(props) {
1469
1546
  }
1470
1547
  if (key === "rotate") value = value + "deg";
1471
1548
  propsCopy["transform"] = key + "(" + value + ")";
1549
+ } else if (Array.isArray(value)) {
1550
+ propsCopy[key] = value.join(",");
1472
1551
  } else if (value !== void 0 && value !== null) {
1473
1552
  propsCopy[key] = String(value);
1474
1553
  }
@@ -1866,6 +1945,16 @@ function walkAndMaterialise(node, opts) {
1866
1945
  if (newAnimate) cloned.animate = newAnimate;
1867
1946
  return cloned;
1868
1947
  }
1948
+ var LOOP_JUMP_SHIFT_MS = 1;
1949
+ function deepEqualValue(a, b) {
1950
+ if (a === b) return true;
1951
+ if (typeof a !== typeof b || a === null || b === null || typeof a !== "object") return false;
1952
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
1953
+ const ka = Object.keys(a);
1954
+ const kb = Object.keys(b);
1955
+ if (ka.length !== kb.length) return false;
1956
+ return ka.every((k) => deepEqualValue(a[k], b[k]));
1957
+ }
1869
1958
  function parsePathCommands(d) {
1870
1959
  const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
1871
1960
  const commands = [];
@@ -2068,7 +2157,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2068
2157
  const totalIntervals = keyframes.length - 1;
2069
2158
  const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2070
2159
  let segKfs;
2071
- if (loop.before) {
2160
+ if (loop.extend === PxLoopExtend.before) {
2072
2161
  segKfs = keyframes.slice(0, segCount + 1);
2073
2162
  } else {
2074
2163
  segKfs = keyframes.slice(totalIntervals - segCount);
@@ -2076,7 +2165,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2076
2165
  const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2077
2166
  const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2078
2167
  let fillStart, fillEnd;
2079
- if (loop.before) {
2168
+ if (loop.extend === PxLoopExtend.before) {
2080
2169
  fillStart = 0;
2081
2170
  fillEnd = firstT;
2082
2171
  } else {
@@ -2103,7 +2192,12 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2103
2192
  const remainder = fillDuration - fullReps * segDuration;
2104
2193
  const partialFraction = remainder / segDuration;
2105
2194
  const looped = [];
2195
+ const separateBoundary = loop.extend !== PxLoopExtend.before;
2196
+ const originalTerminalKf = keyframes[keyframes.length - 1];
2197
+ let terminalEasingOverride;
2198
+ let hasTerminalEasingOverride = false;
2106
2199
  function appendRep(repStart, isReversed, partial) {
2200
+ var _a2;
2107
2201
  let entries;
2108
2202
  if (isReversed) {
2109
2203
  entries = [];
@@ -2139,8 +2233,26 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2139
2233
  looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2140
2234
  return;
2141
2235
  }
2236
+ const prevKf = looped.length > 0 ? looped[looped.length - 1] : originalTerminalKf;
2237
+ const isBoundary = separateBoundary && i === 0 && prevKf !== void 0 && Math.abs(((_a2 = prevKf.t) != null ? _a2 : 0) - (repStart + entry.relT * segDuration)) < 1e-9;
2238
+ if (isBoundary) {
2239
+ if (deepEqualValue(prevKf.v, entry.v)) {
2240
+ if (looped.length > 0) {
2241
+ prevKf.e = entry.e;
2242
+ prevKf.tangentOut = entry.tangentOut;
2243
+ } else {
2244
+ terminalEasingOverride = entry.e;
2245
+ hasTerminalEasingOverride = true;
2246
+ }
2247
+ continue;
2248
+ }
2249
+ if (looped.length > 0) {
2250
+ delete prevKf.tangentIn;
2251
+ delete prevKf.tangentOut;
2252
+ }
2253
+ }
2142
2254
  const pushed = {
2143
- t: repStart + entry.relT * segDuration,
2255
+ t: repStart + entry.relT * segDuration + (isBoundary ? LOOP_JUMP_SHIFT_MS : 0),
2144
2256
  v: entry.v,
2145
2257
  e: i < entries.length - 1 ? entry.e : void 0
2146
2258
  };
@@ -2188,7 +2300,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2188
2300
  looped.push(pushed);
2189
2301
  }
2190
2302
  }
2191
- if (loop.before) {
2303
+ if (loop.extend === PxLoopExtend.before) {
2192
2304
  if (partialFraction > 1e-9) {
2193
2305
  const isReversed = !!loop.alternate && fullReps % 2 === 0;
2194
2306
  appendRepTail(fillStart, isReversed, partialFraction);
@@ -2211,9 +2323,14 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2211
2323
  appendRep(repStart, isReversed, partialFraction);
2212
2324
  }
2213
2325
  }
2214
- if (loop.before) {
2326
+ if (loop.extend === PxLoopExtend.before) {
2215
2327
  return [...looped, ...keyframes];
2216
2328
  } else {
2329
+ if (hasTerminalEasingOverride && keyframes.length > 0) {
2330
+ const head = keyframes.slice(0, -1);
2331
+ const tail = __spreadProps2(__spreadValues2({}, keyframes[keyframes.length - 1]), { e: terminalEasingOverride });
2332
+ return [...head, tail, ...looped];
2333
+ }
2217
2334
  return [...keyframes, ...looped];
2218
2335
  }
2219
2336
  }
@@ -2327,20 +2444,23 @@ var _elementIdCounter = 0;
2327
2444
  function generateElementId() {
2328
2445
  return "_px_el_" + ++_elementIdCounter;
2329
2446
  }
2330
- function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.webapi) {
2447
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.waapi) {
2331
2448
  const normalized = {};
2332
2449
  for (const [propName, propAnim] of Object.entries(animDef)) {
2450
+ if (propName === "transform" && propAnim.alongPathMode === "offsetPath" && animDef["offsetDistance"] !== void 0) {
2451
+ continue;
2452
+ }
2333
2453
  const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2334
2454
  if (normalizedKfs.length > 0) {
2335
2455
  const out = { kfs: normalizedKfs };
2336
2456
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2337
2457
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2338
- normalized[propName] = engine === PxAnimatorEngine.webapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2458
+ normalized[propName] = engine === PxAnimatorEngine.waapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2339
2459
  }
2340
2460
  }
2341
2461
  return normalized;
2342
2462
  }
2343
- function getNormalisedBindings(doc, engine = PxAnimatorEngine.webapi) {
2463
+ function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2344
2464
  const animatorConfig = getAnimatorConfig(doc) || {};
2345
2465
  const defs = getDefs(doc);
2346
2466
  const duration = animatorConfig.duration || 1e3;
@@ -2745,7 +2865,7 @@ function partsRecord(part, value, origin) {
2745
2865
  return rec;
2746
2866
  }
2747
2867
  function readAnimatable(raw) {
2748
- var _a, _b;
2868
+ var _a, _b, _c;
2749
2869
  if (raw === void 0) return {
2750
2870
  kind: "absent"
2751
2871
  /* Absent */
@@ -2755,13 +2875,34 @@ function readAnimatable(raw) {
2755
2875
  const obj = raw;
2756
2876
  const kfs = (_a = obj.keyframes) != null ? _a : obj.kfs;
2757
2877
  if (kfs) {
2758
- return { kind: "animated", keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2878
+ const out = { kind: "animated", keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2879
+ const base = (_b = obj.value) != null ? _b : obj.v;
2880
+ if (base !== void 0 && out.kind === "animated") out.base = base;
2881
+ return out;
2759
2882
  }
2760
- const staticValue = (_b = obj.value) != null ? _b : obj.v;
2883
+ const staticValue = (_c = obj.value) != null ? _c : obj.v;
2761
2884
  if (staticValue !== void 0) return { kind: "static", value: staticValue };
2762
2885
  }
2763
2886
  return { kind: "static", value: raw };
2764
2887
  }
2888
+ function writeAnimatableChannel(node, attrName, read, opts) {
2889
+ var _a, _b, _c, _d;
2890
+ const toOut = (v) => (opts == null ? void 0 : opts.asString) && v !== void 0 && v !== null ? String(v) : v;
2891
+ if (read.kind === "absent") return;
2892
+ if (read.kind === "static") {
2893
+ node[attrName] = toOut(read.value);
2894
+ return;
2895
+ }
2896
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
2897
+ const animate = __spreadValues2({}, prevAnimate || {});
2898
+ const block = { keyframes: read.keyframes };
2899
+ if (read.loop !== void 0) block.loop = read.loop;
2900
+ if (read.autoOrient !== void 0) block.autoOrient = read.autoOrient;
2901
+ animate[attrName] = block;
2902
+ node.animate = animate;
2903
+ 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;
2904
+ if (baseline !== void 0) node[attrName] = toOut(baseline);
2905
+ }
2765
2906
  function normaliseKeyframe(kf) {
2766
2907
  if (!kf || typeof kf !== "object") return kf;
2767
2908
  const k = kf;
@@ -2781,7 +2922,7 @@ function readStaticOrigin(raw, ctx) {
2781
2922
  const o = readAnimatable(raw);
2782
2923
  if (o.kind === "absent") return void 0;
2783
2924
  if (o.kind === "static") return o.value;
2784
- ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
2925
+ ctx.warnings.push("transformBy.origin: animated origin approximated by its first keyframe");
2785
2926
  return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
2786
2927
  }
2787
2928
  function keyframeWith(kf, value) {
@@ -2792,7 +2933,7 @@ function keyframeWith(kf, value) {
2792
2933
  if (kf.tangentIn !== void 0) out.tangentIn = kf.tangentIn;
2793
2934
  return out;
2794
2935
  }
2795
- function applyTransformationEffect(node, fx, ctx) {
2936
+ function applyTransformByEffect(node, fx, ctx) {
2796
2937
  if (!fx) return node;
2797
2938
  delete node.transform;
2798
2939
  let n = node;
@@ -2828,6 +2969,10 @@ function applyTransformationEffect(node, fx, ctx) {
2828
2969
  } else {
2829
2970
  n = wrapTransformPart(n, "translate", fx.translate, ctx);
2830
2971
  }
2972
+ if (n !== node && node.id) {
2973
+ n.id = node.id;
2974
+ delete node.id;
2975
+ }
2831
2976
  return n;
2832
2977
  }
2833
2978
  function translateHasAutoOrient(translate) {
@@ -2837,8 +2982,6 @@ function translateHasAutoOrient(translate) {
2837
2982
  return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
2838
2983
  }
2839
2984
  function normalizeScale(raw) {
2840
- if (raw === void 0) return void 0;
2841
- if (Array.isArray(raw)) return [raw[0] / 100, raw[1] / 100];
2842
2985
  return raw;
2843
2986
  }
2844
2987
  function wrapTransformPart(inner, part, raw, ctx) {
@@ -2879,34 +3022,54 @@ function wrapOrigin(inner, raw, invert) {
2879
3022
  }
2880
3023
  return inner;
2881
3024
  }
3025
+ function genId(ctx, prefix) {
3026
+ return "_lw_" + prefix + "_" + ctx.nextId++;
3027
+ }
3028
+ function stripHash2(href) {
3029
+ return typeof href === "string" ? href.replace(/^#/, "") : void 0;
3030
+ }
3031
+ function indexById(node, map) {
3032
+ var _a;
3033
+ if (typeof node.id === "string") map.set(node.id, node);
3034
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3035
+ }
3036
+ function spliceDefs(root, defs) {
3037
+ if (!defs.length) return;
3038
+ const existing = root.children || (root.children = []);
3039
+ existing.unshift({ type: "defs", children: defs });
3040
+ }
3041
+ var clone = deepClonePxNode;
3042
+ function regenerateIdsInClone(root, ctx) {
3043
+ return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3044
+ }
2882
3045
  function identifyContentRefTargets(node, ctx, allocator) {
2883
3046
  var _a, _b, _c;
2884
3047
  if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.type) === "content") {
2885
- const baseId = node.effects.clone.baseId;
2886
- if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
2887
- ctx.contentRefInnerIds.set(baseId, allocator(baseId));
3048
+ const sourceId = stripHash2(node.effects.clone.sourceId);
3049
+ if (typeof sourceId === "string" && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {
3050
+ ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));
2888
3051
  }
2889
3052
  }
2890
3053
  (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
2891
3054
  }
2892
- function splitForContentRef(node, transformation, originalId, innerId, ctx) {
2893
- const outerBody = liftBodyTranslate(node, transformation);
3055
+ function splitForContentRef(node, transformBy, originalId, innerId, ctx) {
3056
+ const outerBody = liftBodyTranslate(node, transformBy);
2894
3057
  if (typeof node.id === "string") delete node.id;
2895
- const { outer: outerTr, inner: innerTr } = splitTransformationEffect(transformation);
3058
+ const { outer: outerTr, inner: innerTr } = splitTransformByEffect(transformBy);
2896
3059
  let innerNode = node;
2897
- innerNode = applyTransformationEffect(innerNode, innerTr, ctx);
3060
+ innerNode = applyTransformByEffect(innerNode, innerTr, ctx);
2898
3061
  const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
2899
3062
  let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
2900
3063
  if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
2901
3064
  if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
2902
3065
  if (outerTr) {
2903
3066
  delete outerWrapper.id;
2904
- outerWrapper = applyTransformationEffect(outerWrapper, outerTr, ctx);
3067
+ outerWrapper = applyTransformByEffect(outerWrapper, outerTr, ctx);
2905
3068
  outerWrapper.id = originalId;
2906
3069
  }
2907
3070
  return outerWrapper;
2908
3071
  }
2909
- function liftBodyTranslate(node, transformation) {
3072
+ function liftBodyTranslate(node, transformBy) {
2910
3073
  var _a, _b, _c;
2911
3074
  const out = {};
2912
3075
  let didLiftAnimate = false;
@@ -2960,7 +3123,7 @@ function liftBodyTranslate(node, transformation) {
2960
3123
  didLiftAnimate = true;
2961
3124
  }
2962
3125
  }
2963
- const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
3126
+ const transformationHasTranslate = (transformBy == null ? void 0 : transformBy.translate) !== void 0;
2964
3127
  const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
2965
3128
  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);
2966
3129
  if (typeof node.transform === "string") {
@@ -2979,6 +3142,24 @@ function liftBodyTranslate(node, transformation) {
2979
3142
  if (split.rest) node.transform = split.rest;
2980
3143
  else delete node.transform;
2981
3144
  }
3145
+ } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes && !node.transform.kfs) {
3146
+ const wrapped = node.transform.value;
3147
+ const isWrapped = !!(wrapped && typeof wrapped === "object");
3148
+ const value = isWrapped ? wrapped : node.transform;
3149
+ const rewrap = (rec) => isWrapped ? { value: rec } : rec;
3150
+ if (Array.isArray(value.translate)) {
3151
+ const rest = __spreadValues2({}, value);
3152
+ delete rest.translate;
3153
+ const hasRest = Object.keys(rest).length > 0;
3154
+ if (stripBodyTranslateOnly) {
3155
+ if (hasRest) node.transform = rewrap(rest);
3156
+ else delete node.transform;
3157
+ } else {
3158
+ out.transform = rewrap({ translate: value.translate });
3159
+ if (hasRest) node.transform = rewrap(rest);
3160
+ else delete node.transform;
3161
+ }
3162
+ }
2982
3163
  }
2983
3164
  return out;
2984
3165
  }
@@ -3048,7 +3229,7 @@ function parseTranslateArgs(translateOp) {
3048
3229
  const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
3049
3230
  return [nums[0] || 0, nums[1] || 0];
3050
3231
  }
3051
- function splitTransformationEffect(fx) {
3232
+ function splitTransformByEffect(fx) {
3052
3233
  if (!fx) return {};
3053
3234
  const originOnOuter = needsOriginOnOuter(fx.translate);
3054
3235
  const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
@@ -3074,26 +3255,6 @@ function needsOriginOnOuter(translateAnim) {
3074
3255
  }
3075
3256
  return false;
3076
3257
  }
3077
- function genId(ctx, prefix) {
3078
- return "_lw_" + prefix + "_" + ctx.nextId++;
3079
- }
3080
- function stripHash2(href) {
3081
- return typeof href === "string" ? href.replace(/^#/, "") : void 0;
3082
- }
3083
- function indexById(node, map) {
3084
- var _a;
3085
- if (typeof node.id === "string") map.set(node.id, node);
3086
- (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3087
- }
3088
- function spliceDefs(root, defs) {
3089
- if (!defs.length) return;
3090
- const existing = root.children || (root.children = []);
3091
- existing.unshift({ type: "defs", children: defs });
3092
- }
3093
- var clone = deepClonePxNode;
3094
- function regenerateIdsInClone(root, ctx) {
3095
- return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3096
- }
3097
3258
  function applyFillGradientEffect(node, fx, ctx) {
3098
3259
  return applyGradient(node, fx, ctx, "fill");
3099
3260
  }
@@ -3114,62 +3275,63 @@ function synthesiseGradientDef(fx, id, ctx) {
3114
3275
  id
3115
3276
  };
3116
3277
  if (fx.type === PxGradientType.linear) {
3117
- if (fx.p1) {
3118
- out.x1 = String(fx.p1[0]);
3119
- out.y1 = String(fx.p1[1]);
3120
- }
3121
- if (fx.p2) {
3122
- out.x2 = String(fx.p2[0]);
3123
- out.y2 = String(fx.p2[1]);
3124
- }
3278
+ applyGeomVec(out, "x1", "y1", fx.p1);
3279
+ applyGeomVec(out, "x2", "y2", fx.p2);
3125
3280
  } else {
3126
- if (fx.c) {
3127
- out.cx = String(fx.c[0]);
3128
- out.cy = String(fx.c[1]);
3129
- }
3130
- if (fx.r !== void 0) out.r = String(fx.r);
3131
- if (fx.fp) {
3132
- out.fx = String(fx.fp[0]);
3133
- out.fy = String(fx.fp[1]);
3134
- }
3281
+ applyGeomVec(out, "cx", "cy", fx.c);
3282
+ applyGeomNumber(out, "r", fx.r);
3283
+ applyGeomVec(out, "fx", "fy", fx.fp);
3135
3284
  }
3136
3285
  if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
3137
3286
  if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
3138
3287
  if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;
3139
- const animate = fx.animate;
3140
- if (animate) {
3141
- const mapped = {};
3142
- for (const wireKey of Object.keys(animate)) {
3143
- const attr = GRADIENT_ANIM_CHANNEL_TO_ATTR[wireKey];
3144
- if (attr) mapped[attr] = animate[wireKey];
3145
- }
3146
- if (Object.keys(mapped).length) out.animate = mapped;
3147
- }
3148
3288
  out.children = buildStopChildren(fx.stops, ctx);
3149
3289
  return out;
3150
3290
  }
3151
- var GRADIENT_ANIM_CHANNEL_TO_ATTR = {
3152
- gradientX1: "x1",
3153
- gradientY1: "y1",
3154
- gradientX2: "x2",
3155
- gradientY2: "y2",
3156
- gradientCx: "cx",
3157
- gradientCy: "cy",
3158
- gradientFx: "fx",
3159
- gradientFy: "fy",
3160
- gradientR: "r"
3161
- };
3291
+ function applyGeomVec(out, xAttr, yAttr, raw) {
3292
+ var _a, _b, _c;
3293
+ const read = readAnimatable(raw);
3294
+ if (read.kind === "absent") return;
3295
+ if (read.kind === "static") {
3296
+ out[xAttr] = String(read.value[0]);
3297
+ out[yAttr] = String(read.value[1]);
3298
+ return;
3299
+ }
3300
+ const axisChannel = (idx) => {
3301
+ const block = {
3302
+ keyframes: read.keyframes.map((kf) => {
3303
+ const axisKf = { time: kf.time, value: Array.isArray(kf.value) ? kf.value[idx] : void 0 };
3304
+ if (kf.easing !== void 0) axisKf.easing = kf.easing;
3305
+ return axisKf;
3306
+ })
3307
+ };
3308
+ if (read.loop !== void 0) block.loop = read.loop;
3309
+ return block;
3310
+ };
3311
+ const animate = (_a = out.animate) != null ? _a : {};
3312
+ animate[xAttr] = axisChannel(0);
3313
+ animate[yAttr] = axisChannel(1);
3314
+ out.animate = animate;
3315
+ const baseline = (_c = read.base) != null ? _c : (_b = read.keyframes[0]) == null ? void 0 : _b.value;
3316
+ if (Array.isArray(baseline)) {
3317
+ out[xAttr] = String(baseline[0]);
3318
+ out[yAttr] = String(baseline[1]);
3319
+ }
3320
+ }
3321
+ function applyGeomNumber(out, attrName, raw) {
3322
+ const read = readAnimatable(raw);
3323
+ if (read.kind === "absent") return;
3324
+ writeAnimatableChannel(out, attrName, read, { asString: true });
3325
+ }
3162
3326
  function buildStopChildren(stops, ctx) {
3163
3327
  var _a, _b, _c, _d;
3164
3328
  if (!stops) return [];
3165
- if (Array.isArray(stops)) return stops.map(staticStopNode);
3166
- if (typeof stops === "object" && Array.isArray(stops.value)) {
3167
- return stops.value.map(staticStopNode);
3168
- }
3169
- const animBlock = stops;
3170
- const kfs = animBlock.keyframes;
3171
- if (!Array.isArray(kfs) || !kfs.length) return [];
3172
- const loopFromSource = animBlock.loop;
3329
+ const read = readAnimatable(stops);
3330
+ if (read.kind === "absent") return [];
3331
+ if (read.kind === "static") return Array.isArray(read.value) ? read.value.map(staticStopNode) : [];
3332
+ const kfs = read.keyframes;
3333
+ if (!kfs.length) return [];
3334
+ const loopFromSource = read.loop;
3173
3335
  let stopCount = 0;
3174
3336
  for (const kf of kfs) {
3175
3337
  const v = (_a = kf.value) != null ? _a : kf.v;
@@ -3244,36 +3406,60 @@ function formatOffset(o) {
3244
3406
  return pct + "%";
3245
3407
  }
3246
3408
  function applyClipPathEffect(node, fx, ctx) {
3409
+ var _a, _b;
3247
3410
  if (!fx || !fx.d && !fx.animate) return node;
3248
3411
  const clipId = genId(ctx, "clip");
3249
- const pathChild = { type: "path", d: fx.d };
3250
- if (fx.animate) pathChild.animate = { d: fx.animate };
3412
+ const pathChild = { type: "path" };
3413
+ const read = readAnimatable(fx.d);
3414
+ if (read.kind !== "absent") {
3415
+ if (read.kind === "static") {
3416
+ pathChild.d = pathString(read.value);
3417
+ } else {
3418
+ writeAnimatableChannel(pathChild, "d", read);
3419
+ if (pathChild.d !== void 0) pathChild.d = pathString(pathChild.d);
3420
+ }
3421
+ }
3422
+ if (fx.animate && !((_a = pathChild.animate) == null ? void 0 : _a.d)) {
3423
+ const animate = (_b = pathChild.animate) != null ? _b : {};
3424
+ animate.d = fx.animate;
3425
+ pathChild.animate = animate;
3426
+ }
3251
3427
  ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
3252
3428
  node.clipPath = "url(#" + clipId + ")";
3253
3429
  return node;
3254
3430
  }
3255
- function applyMaskedByEffect(node, fx, transformation, ctx) {
3431
+ function pathString(v) {
3432
+ if (typeof v === "string") return v;
3433
+ if (v && typeof v === "object" && typeof v.path === "string") return v.path;
3434
+ return void 0;
3435
+ }
3436
+ function applyMaskedByEffect(node, fx, transformBy, ctx) {
3256
3437
  if (!fx) return node;
3257
- if (!fx.href) {
3258
- ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
3438
+ const sourceId = stripHash2(fx.sourceId);
3439
+ if (!sourceId) {
3440
+ ctx.errors.push("maskedBy.sourceId missing \u2014 cannot build mask");
3259
3441
  return node;
3260
3442
  }
3261
3443
  const maskId = genId(ctx, "mask");
3262
- let content = { type: "use", href: "#" + fx.href };
3263
- if (transformation) {
3264
- content = wrapInverseTransform(content, transformation, ctx);
3444
+ let content = { type: "use", href: "#" + sourceId };
3445
+ if (transformBy) {
3446
+ content = wrapInverseTransform(content, transformBy, ctx);
3265
3447
  } else if (hasAnimateTransform(node)) {
3266
3448
  content = wrapInverseAnimatedBodyTransform(content, node, ctx);
3267
3449
  } else {
3268
3450
  const bodyStatic = readTransformationFromBody(node);
3269
3451
  if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);
3270
3452
  }
3271
- const includeTargetOwn = transformation === void 0 && !nodeHasBodyTransform(node);
3272
- content = wrapAncestorChainCompensation(content, node, fx.href, ctx, includeTargetOwn);
3453
+ const includeTargetOwn = transformBy === void 0 && !nodeHasBodyTransform(node);
3454
+ content = wrapAncestorChainCompensation(content, node, sourceId, ctx, includeTargetOwn);
3273
3455
  const mask = { type: "mask", id: maskId, children: [content] };
3274
3456
  if (fx.maskType) mask.maskType = fx.maskType;
3275
3457
  if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
3276
3458
  if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
3459
+ if (fx.x !== void 0) mask.x = String(fx.x);
3460
+ if (fx.y !== void 0) mask.y = String(fx.y);
3461
+ if (fx.width !== void 0) mask.width = String(fx.width);
3462
+ if (fx.height !== void 0) mask.height = String(fx.height);
3277
3463
  ctx.defs.push(mask);
3278
3464
  node.mask = "url(#" + maskId + ")";
3279
3465
  return node;
@@ -3380,6 +3566,19 @@ function readTransformationFromBody(node) {
3380
3566
  if (parts.origin) out.origin = parts.origin;
3381
3567
  return Object.keys(out).length ? out : void 0;
3382
3568
  }
3569
+ if (node.transform && typeof node.transform === "object" && !node.transform.keyframes && !node.transform.kfs) {
3570
+ const wrapped = node.transform.value;
3571
+ const value = wrapped && typeof wrapped === "object" ? wrapped : node.transform;
3572
+ if (value && typeof value === "object") {
3573
+ const out = {};
3574
+ if (Array.isArray(value.translate)) out.translate = value.translate;
3575
+ if (typeof value.rotate === "number") out.rotate = value.rotate;
3576
+ if (typeof value.skew === "number") out.skew = value.skew;
3577
+ if (Array.isArray(value.scale)) out.scale = { value: value.scale };
3578
+ if (Array.isArray(value.origin)) out.origin = value.origin;
3579
+ return Object.keys(out).length ? out : void 0;
3580
+ }
3581
+ }
3383
3582
  return void 0;
3384
3583
  }
3385
3584
  function parseTransformStringToParts(s) {
@@ -3513,10 +3712,10 @@ function collectMaskAncestorChains(root, ctx) {
3513
3712
  const interestingNodes = /* @__PURE__ */ new Set();
3514
3713
  const collectInterestingNodes = (n) => {
3515
3714
  var _a, _b;
3516
- const href = (_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.href;
3517
- if (typeof href === "string") {
3715
+ const maskSourceId = stripHash2((_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.sourceId);
3716
+ if (typeof maskSourceId === "string") {
3518
3717
  interestingNodes.add(n);
3519
- const sourceNode = ctx.idMap.get(href);
3718
+ const sourceNode = ctx.idMap.get(maskSourceId);
3520
3719
  if (sourceNode) interestingNodes.add(sourceNode);
3521
3720
  }
3522
3721
  if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);
@@ -3542,8 +3741,9 @@ function extractTranslateOnly(node, ctx) {
3542
3741
  if (typeof tr === "string") {
3543
3742
  const parts = parseTranslateOnlyFromString(tr, ctx);
3544
3743
  if (parts) out.translate = parts;
3545
- } else if (tr && typeof tr === "object") {
3546
- const value = tr.value;
3744
+ } else if (tr && typeof tr === "object" && !tr.keyframes && !tr.kfs) {
3745
+ const wrapped = tr.value;
3746
+ const value = wrapped && typeof wrapped === "object" ? wrapped : tr;
3547
3747
  if (value && typeof value === "object" && Array.isArray(value.translate)) {
3548
3748
  out.translate = [value.translate[0] || 0, value.translate[1] || 0];
3549
3749
  }
@@ -3591,17 +3791,17 @@ function parseTranslateOnlyFromString(s, ctx) {
3591
3791
  var CONTENT_SUBREF = "content";
3592
3792
  function applyRefHref(node, clone2, ctx) {
3593
3793
  if (!clone2) return;
3594
- const baseId = clone2.baseId;
3595
- if (!baseId) {
3596
- if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing baseId");
3794
+ const sourceId = stripHash2(clone2.sourceId);
3795
+ if (!sourceId) {
3796
+ if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing sourceId");
3597
3797
  return;
3598
3798
  }
3599
- const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
3799
+ const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(sourceId) || sourceId : sourceId;
3600
3800
  node.href = "#" + targetId;
3601
3801
  }
3602
- function applyRefAndTransformationEffect(node, clone2, transformation, ctx) {
3802
+ function applyRefAndTransformationEffect(node, clone2, transformBy, ctx) {
3603
3803
  applyRefHref(node, clone2, ctx);
3604
- return applyTransformationEffect(node, transformation, ctx);
3804
+ return applyTransformByEffect(node, transformBy, ctx);
3605
3805
  }
3606
3806
  function applyRepeaterEffect(node, fx, ctx) {
3607
3807
  var _a, _b;
@@ -3624,7 +3824,7 @@ function applyRepeaterEffect(node, fx, ctx) {
3624
3824
  for (let i = 1; i < copies; i++) {
3625
3825
  const baseClone = clone(base);
3626
3826
  const synthFx = synthesisePerCopyFx(fx, i);
3627
- const wrapped = synthFx ? applyTransformationEffect(baseClone, synthFx, ctx) : baseClone;
3827
+ const wrapped = synthFx ? applyTransformByEffect(baseClone, synthFx, ctx) : baseClone;
3628
3828
  children.push(wrapped);
3629
3829
  }
3630
3830
  const wrapper = { type: "g", children };
@@ -3636,10 +3836,13 @@ function applyRepeaterEffect(node, fx, ctx) {
3636
3836
  function synthesisePerCopyFx(fx, i) {
3637
3837
  const out = {};
3638
3838
  if (fx.translate !== void 0) {
3639
- out.translate = mapAnimatableVec2(fx.translate, (v) => [v[0] * i, v[1] * i]);
3839
+ out.translate = mapAnimatable(fx.translate, (v) => [v[0] * i, v[1] * i]);
3640
3840
  }
3641
3841
  if (fx.rotate !== void 0) {
3642
- out.rotate = mapAnimatableNumber(fx.rotate, (v) => v * i);
3842
+ out.rotate = mapAnimatable(fx.rotate, (v) => v * i);
3843
+ }
3844
+ if (fx.skew !== void 0) {
3845
+ out.skew = mapAnimatable(fx.skew, (v) => v * i);
3643
3846
  }
3644
3847
  if (fx.scale !== void 0) {
3645
3848
  out.scale = synthesiseScale(fx.scale, i);
@@ -3649,63 +3852,50 @@ function synthesisePerCopyFx(fx, i) {
3649
3852
  }
3650
3853
  return Object.keys(out).length ? out : void 0;
3651
3854
  }
3652
- function mapAnimatableVec2(raw, fn) {
3653
- if (Array.isArray(raw)) return fn(raw);
3654
- if (raw && typeof raw === "object") {
3655
- const obj = raw;
3656
- if (Array.isArray(obj.keyframes)) {
3657
- return __spreadProps2(__spreadValues2({}, obj), {
3658
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps2(__spreadValues2({}, kf), { value: fn(kf.value) }) : kf)
3659
- });
3660
- }
3661
- if (obj.value !== void 0) {
3662
- return __spreadProps2(__spreadValues2({}, obj), { value: fn(obj.value) });
3663
- }
3855
+ function mapAnimatable(raw, fn, wrapStatic = false) {
3856
+ const read = readAnimatable(raw);
3857
+ if (read.kind === "absent") return raw;
3858
+ if (read.kind === "static") {
3859
+ const mapped = fn(read.value);
3860
+ const wasRawStatic = typeof raw === "number" || Array.isArray(raw);
3861
+ return wasRawStatic && !wrapStatic ? mapped : { value: mapped };
3664
3862
  }
3665
- return raw;
3666
- }
3667
- function mapAnimatableNumber(raw, fn) {
3668
- if (typeof raw === "number") return fn(raw);
3669
- if (raw && typeof raw === "object") {
3670
- const obj = raw;
3671
- if (Array.isArray(obj.keyframes)) {
3672
- return __spreadProps2(__spreadValues2({}, obj), {
3673
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps2(__spreadValues2({}, kf), { value: fn(kf.value) }) : kf)
3674
- });
3675
- }
3676
- if (obj.value !== void 0) {
3677
- return __spreadProps2(__spreadValues2({}, obj), { value: fn(obj.value) });
3678
- }
3679
- }
3680
- return raw;
3863
+ const out = {
3864
+ keyframes: read.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps2(__spreadValues2({}, kf), { value: fn(kf.value) }) : kf)
3865
+ };
3866
+ if (read.loop !== void 0) out.loop = read.loop;
3867
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
3868
+ if (read.base !== void 0) out.value = fn(read.base);
3869
+ return out;
3681
3870
  }
3682
3871
  function synthesiseScale(raw, i) {
3683
- const scalePowerFromPercent = (v) => [Math.pow(v[0] / 100, i), Math.pow(v[1] / 100, i)];
3684
- const scalePowerFromUnits = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
3685
- if (Array.isArray(raw)) {
3686
- return { value: scalePowerFromPercent(raw) };
3687
- }
3688
- if (raw && typeof raw === "object") {
3689
- const obj = raw;
3690
- if (Array.isArray(obj.keyframes)) {
3691
- return __spreadProps2(__spreadValues2({}, obj), {
3692
- keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps2(__spreadValues2({}, kf), { value: scalePowerFromUnits(kf.value) }) : kf)
3693
- });
3694
- }
3695
- if (obj.value !== void 0) {
3696
- return __spreadProps2(__spreadValues2({}, obj), { value: scalePowerFromPercent(obj.value) });
3697
- }
3698
- }
3699
- return raw;
3872
+ const scalePower = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
3873
+ return mapAnimatable(raw, scalePower, true);
3700
3874
  }
3701
3875
  var RETIME_MATERIALISATION_MODE_INLINE_G = false;
3702
3876
  function asRetime(r) {
3703
3877
  var _a, _b;
3704
- if (r.timeCrop) {
3705
- console.warn("retime: `timeCrop` is not supported yet and will be ignored");
3706
- }
3707
3878
  return { start: (_a = r.start) != null ? _a : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
3708
3879
  }
3880
+ var CROP_EDGE_MS = 1;
3881
+ function applyTimeCrop(useNode, crop, ctx) {
3882
+ const [start, end] = crop;
3883
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
3884
+ ctx.warnings.push("retime: timeCrop is not a pair of finite numbers \u2014 ignored");
3885
+ return;
3886
+ }
3887
+ const keyframes = end <= start ? [{ time: 0, value: 0 }] : [
3888
+ ...start > 0 ? [{ time: Math.max(0, start - CROP_EDGE_MS), value: 0 }] : [],
3889
+ { time: Math.max(0, start), value: 1 },
3890
+ { time: end, value: 1 },
3891
+ { time: end + CROP_EDGE_MS, value: 0 }
3892
+ ];
3893
+ const inner = __spreadValues2({}, useNode);
3894
+ for (const k of Object.keys(useNode)) delete useNode[k];
3895
+ useNode.type = "g";
3896
+ useNode.children = [inner];
3897
+ useNode.animate = { opacity: { keyframes } };
3898
+ }
3709
3899
  function concatRetime(child, parent) {
3710
3900
  return {
3711
3901
  start: parent.start + parent.stretch * child.start,
@@ -3765,8 +3955,10 @@ function applyAllRetimeEffects(root, ctx) {
3765
3955
  for (const useNode of sites) {
3766
3956
  const retime = readCloneRetime(useNode);
3767
3957
  if (!retime) continue;
3958
+ const crop = retime.timeCrop;
3768
3959
  clearCloneRetime(useNode);
3769
3960
  materialiseRetime(useNode, asRetime(retime), ctx);
3961
+ if (crop) applyTimeCrop(useNode, crop, ctx);
3770
3962
  }
3771
3963
  }
3772
3964
  function materialiseRetime(useNode, retime, ctx) {
@@ -4009,14 +4201,11 @@ function createPathSampler(d) {
4009
4201
  }
4010
4202
  var EXTEND_MARGIN_FRAC = 0.15;
4011
4203
  function numRange(v) {
4012
- if (typeof v === "number") return { min: v, max: v };
4013
- if (v && typeof v === "object") {
4014
- const o = v;
4015
- if (typeof o.value === "number") return { min: o.value, max: o.value };
4016
- if (Array.isArray(o.keyframes) && o.keyframes.length) {
4017
- const vals = o.keyframes.map((k) => Number(k.value) || 0);
4018
- return { min: Math.min(...vals), max: Math.max(...vals) };
4019
- }
4204
+ const read = readAnimatable(v);
4205
+ if (read.kind === "static" && typeof read.value === "number") return { min: read.value, max: read.value };
4206
+ if (read.kind === "animated" && read.keyframes.length) {
4207
+ const vals = read.keyframes.map((k) => Number(k.value) || 0);
4208
+ return { min: Math.min(...vals), max: Math.max(...vals) };
4020
4209
  }
4021
4210
  return { min: 0, max: 0 };
4022
4211
  }
@@ -4036,11 +4225,16 @@ function estimateTextAdvance(node) {
4036
4225
  var r3 = (n) => Math.round(n * 1e3) / 1e3;
4037
4226
  function shiftAnimatable(v, by) {
4038
4227
  if (!by || v === void 0 || v === null) return v;
4039
- if (typeof v === "number") return v + by;
4040
- const o = v;
4041
- if (typeof o.value === "number") return __spreadProps2(__spreadValues2({}, o), { value: o.value + by });
4042
- if (Array.isArray(o.keyframes)) return __spreadProps2(__spreadValues2({}, o), { keyframes: o.keyframes.map((k) => __spreadProps2(__spreadValues2({}, k), { value: (Number(k.value) || 0) + by })) });
4043
- return v;
4228
+ const read = readAnimatable(v);
4229
+ if (read.kind === "absent") return v;
4230
+ if (read.kind === "static") return typeof v === "number" ? read.value + by : { value: read.value + by };
4231
+ const out = {
4232
+ keyframes: read.keyframes.map((k) => __spreadProps2(__spreadValues2({}, k), { value: (Number(k.value) || 0) + by }))
4233
+ };
4234
+ if (read.loop !== void 0) out.loop = read.loop;
4235
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
4236
+ if (read.base !== void 0) out.value = read.base + by;
4237
+ return out;
4044
4238
  }
4045
4239
  function extendedPathForBrowser(pathD, opts) {
4046
4240
  var _a;
@@ -4096,26 +4290,7 @@ function applyTextPathEffect(node, fx, ctx) {
4096
4290
  }
4097
4291
  function applyAnimatableNumber(node, attrName, raw) {
4098
4292
  if (raw === void 0 || raw === null) return;
4099
- if (typeof raw === "number") {
4100
- node[attrName] = String(raw);
4101
- return;
4102
- }
4103
- if (typeof raw === "object" && raw !== null) {
4104
- const obj = raw;
4105
- if (Array.isArray(obj.keyframes)) {
4106
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4107
- const animate = __spreadValues2({}, prevAnimate || {});
4108
- const block = { keyframes: obj.keyframes };
4109
- if (obj.loop !== void 0) block.loop = obj.loop;
4110
- animate[attrName] = block;
4111
- node.animate = animate;
4112
- return;
4113
- }
4114
- if (typeof obj.value === "number") {
4115
- node[attrName] = String(obj.value);
4116
- return;
4117
- }
4118
- }
4293
+ writeAnimatableChannel(node, attrName, readAnimatable(raw), { asString: true });
4119
4294
  }
4120
4295
  var jsonElementFactory = (type, props, children) => {
4121
4296
  const node = { type };
@@ -4303,13 +4478,13 @@ function materialiseGlyphTextHorizontal(node, opts) {
4303
4478
  if (y !== void 0) pen.y = y;
4304
4479
  pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
4305
4480
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4306
- const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
4481
+ const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4307
4482
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
4308
4483
  if (el.children) for (const ch of el.children) walk(ch, s);
4309
4484
  };
4310
4485
  const rootStyle = rootStyleOf(node);
4311
4486
  if (node.children) for (const ch of node.children) walk(ch, rootStyle);
4312
- const rootContent = (_c = str(node[TEXT_ATTR])) != null ? _c : str(node[TEXT_CONTENT_ATTR]);
4487
+ const rootContent = (_c = str(node[TEXT_CONTENT_ATTR])) != null ? _c : str(node[TEXT_ATTR]);
4313
4488
  if (rootContent && !((_d = node.children) == null ? void 0 : _d.length)) renderChars(rootContent, rootStyle);
4314
4489
  const anchor = str(node.textAnchor);
4315
4490
  if (anchor === "middle" || anchor === "end") {
@@ -4358,7 +4533,7 @@ function layoutGlyphTextChars(node, opts) {
4358
4533
  if (y !== void 0) pen.y = y;
4359
4534
  pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
4360
4535
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4361
- const content = (_c2 = str(el[TEXT_ATTR])) != null ? _c2 : str(el[TEXT_CONTENT_ATTR]);
4536
+ const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4362
4537
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
4363
4538
  if (el.children) for (const ch of el.children) walk(ch, s);
4364
4539
  };
@@ -4373,7 +4548,7 @@ function layoutGlyphTextChars(node, opts) {
4373
4548
  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 });
4374
4549
  }
4375
4550
  }
4376
- const rootContent = (_e = str(node[TEXT_ATTR])) != null ? _e : str(node[TEXT_CONTENT_ATTR]);
4551
+ const rootContent = (_e = str(node[TEXT_CONTENT_ATTR])) != null ? _e : str(node[TEXT_ATTR]);
4377
4552
  if (rootContent && !((_f = node.children) == null ? void 0 : _f.length)) renderChars(rootContent, rootStyle);
4378
4553
  const anchor = str(node.textAnchor);
4379
4554
  if (anchor === "middle" || anchor === "end") {
@@ -4401,7 +4576,7 @@ function layoutGlyphTextCharsAlongPath(node, pathD, opts) {
4401
4576
  const walk = (el, parentStyle) => {
4402
4577
  var _a2, _b2, _c;
4403
4578
  const s = resolveStyle2(el, parentStyle);
4404
- const content = (_a2 = str(el[TEXT_ATTR])) != null ? _a2 : str(el[TEXT_CONTENT_ATTR]);
4579
+ const content = (_a2 = str(el[TEXT_CONTENT_ATTR])) != null ? _a2 : str(el[TEXT_ATTR]);
4405
4580
  if (content && !((_b2 = el.children) == null ? void 0 : _b2.length)) {
4406
4581
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4407
4582
  const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
@@ -4455,7 +4630,7 @@ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
4455
4630
  const walk = (el, parentStyle) => {
4456
4631
  var _a, _b, _c;
4457
4632
  const s = resolveStyle2(el, parentStyle);
4458
- const content = (_a = str(el[TEXT_ATTR])) != null ? _a : str(el[TEXT_CONTENT_ATTR]);
4633
+ const content = (_a = str(el[TEXT_CONTENT_ATTR])) != null ? _a : str(el[TEXT_ATTR]);
4459
4634
  if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
4460
4635
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4461
4636
  if (gf) {
@@ -4683,9 +4858,9 @@ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pat
4683
4858
  }
4684
4859
  return materialiseGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength, pathOverflow);
4685
4860
  }
4686
- function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4861
+ function applyTrimPathEffect(node, trimPath, ctx) {
4687
4862
  if (!trimPath) return node;
4688
- const trimAllAsOne = !!trimPath.trimAllAsOne;
4863
+ const combined = trimPath.subPaths === PxTrimSubPaths.combined;
4689
4864
  const leafEntries = [];
4690
4865
  const measure = (n) => {
4691
4866
  if (Array.isArray(n.children) && n.children.length > 0) {
@@ -4706,16 +4881,16 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4706
4881
  measure(node);
4707
4882
  if (!leafEntries.length) return node;
4708
4883
  let acc = 0;
4709
- const iterOrder = trimAllAsOne ? [...leafEntries].reverse() : leafEntries;
4884
+ const iterOrder = combined ? [...leafEntries].reverse() : leafEntries;
4710
4885
  for (const entry of iterOrder) {
4711
4886
  for (const sp of entry.subpaths) {
4712
- if (!trimAllAsOne) acc = 0;
4887
+ if (!combined) acc = 0;
4713
4888
  sp.startOffsetPx = acc;
4714
4889
  acc += sp.lengthPx;
4715
4890
  }
4716
4891
  }
4717
4892
  const chainLengthPx = acc;
4718
- if (trimAllAsOne && chainLengthPx < 1e-3) return node;
4893
+ if (combined && chainLengthPx < 1e-3) return node;
4719
4894
  const offsetReadRaw = readAnimatable(trimPath.offset);
4720
4895
  const offsetRead = offsetReadRaw.kind === "absent" ? { kind: "static", value: 0 } : offsetReadRaw;
4721
4896
  const rangeReadRaw = readRangeWithCrossings(trimPath.range);
@@ -4727,18 +4902,18 @@ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4727
4902
  if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {
4728
4903
  const entry = leafEntries[0];
4729
4904
  const sp = entry.subpaths[0];
4730
- const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
4905
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
4731
4906
  if (pathLengthPx < 1e-3) return node;
4732
- const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
4907
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
4733
4908
  return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);
4734
4909
  }
4735
4910
  const replacements = /* @__PURE__ */ new Map();
4736
4911
  for (const entry of leafEntries) {
4737
4912
  const newChildren = [];
4738
4913
  for (const sp of entry.subpaths) {
4739
- const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
4914
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
4740
4915
  if (pathLengthPx < 1e-3) continue;
4741
- const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
4916
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
4742
4917
  newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));
4743
4918
  }
4744
4919
  replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));
@@ -4845,22 +5020,8 @@ function computeAnimAttr(read, map) {
4845
5020
  };
4846
5021
  }
4847
5022
  function applyAttr(node, attrName, attr) {
4848
- var _a, _b;
4849
5023
  if (!attr) return;
4850
- if (attr.kind === "static") {
4851
- node[attrName] = attr.value;
4852
- return;
4853
- }
4854
- const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4855
- const animate = __spreadValues2({}, prevAnimate || {});
4856
- const block = { keyframes: attr.keyframes };
4857
- if (attr.loop !== void 0) block.loop = attr.loop;
4858
- animate[attrName] = block;
4859
- node.animate = animate;
4860
- const firstKf = attr.keyframes[0];
4861
- if (firstKf && ((_a = firstKf.value) != null ? _a : firstKf.v) !== void 0) {
4862
- node[attrName] = (_b = firstKf.value) != null ? _b : firstKf.v;
4863
- }
5024
+ writeAnimatableChannel(node, attrName, attr);
4864
5025
  }
4865
5026
  var OPACITY_STEP_MS = 10;
4866
5027
  function computeOpacityFromRange(rangeRead) {
@@ -5042,9 +5203,9 @@ function applyPlayerEffects(root) {
5042
5203
  nextId: 0,
5043
5204
  contentRefInnerIds: /* @__PURE__ */ new Map(),
5044
5205
  maskAncestorChains: /* @__PURE__ */ new Map(),
5045
- // Resolved engine: `frames` ONLY when explicitly set; auto/webapi/unset →
5046
- // webapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5047
- engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi,
5206
+ // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →
5207
+ // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5208
+ engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.waapi,
5048
5209
  glyphs: (_b = getDefs(root)) == null ? void 0 : _b.glyphs
5049
5210
  };
5050
5211
  const working = clone(root);
@@ -5062,8 +5223,7 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5062
5223
  const originalId = typeof node.id === "string" ? node.id : void 0;
5063
5224
  const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
5064
5225
  if (!fx && !innerIdForContentRef) return node;
5065
- const { transformation, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5066
- const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
5226
+ const { transformBy, repeater, maskedBy, clipPath, trimPath, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5067
5227
  if (fx) delete node.effects;
5068
5228
  let n = node;
5069
5229
  let consumedByGlyphs = false;
@@ -5083,15 +5243,15 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5083
5243
  if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);
5084
5244
  n = applyFillGradientEffect(n, fillGradient, ctx);
5085
5245
  n = applyStrokeGradientEffect(n, strokeGradient, ctx);
5086
- n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
5246
+ n = applyTrimPathEffect(n, trimPath, ctx);
5087
5247
  n = applyRepeaterEffect(n, repeater, ctx);
5088
- n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
5248
+ n = applyMaskedByEffect(n, maskedBy, transformBy, ctx);
5089
5249
  n = applyClipPathEffect(n, clipPath, ctx);
5090
5250
  if (innerIdForContentRef) {
5091
5251
  applyRefHref(n, cloneFx, ctx);
5092
- n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
5252
+ n = splitForContentRef(n, transformBy, originalId, innerIdForContentRef, ctx);
5093
5253
  } else {
5094
- n = applyRefAndTransformationEffect(n, cloneFx, transformation, ctx);
5254
+ n = applyRefAndTransformationEffect(n, cloneFx, transformBy, ctx);
5095
5255
  }
5096
5256
  if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
5097
5257
  if (originalId) ctx.idMap.set(originalId, n);
@@ -5101,12 +5261,141 @@ function applyPlayerEffects_retime(node, ctx) {
5101
5261
  applyAllRetimeEffects(node, ctx);
5102
5262
  return node;
5103
5263
  }
5264
+ var kfTime = (kf) => {
5265
+ var _a, _b;
5266
+ return (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
5267
+ };
5268
+ var kfValue = (kf) => {
5269
+ var _a;
5270
+ return (_a = kf.v) != null ? _a : kf.value;
5271
+ };
5272
+ var kfEasing = (kf) => {
5273
+ var _a;
5274
+ return (_a = kf.e) != null ? _a : kf.easing;
5275
+ };
5276
+ var kfTangentIn = (kf) => {
5277
+ var _a;
5278
+ return (_a = kf.tangentIn) != null ? _a : kf.ti;
5279
+ };
5280
+ var kfTangentOut = (kf) => {
5281
+ var _a;
5282
+ return (_a = kf.tangentOut) != null ? _a : kf.to;
5283
+ };
5284
+ function cubicAt(p0, c1, c2, p1, t) {
5285
+ const u = 1 - t;
5286
+ const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;
5287
+ return [
5288
+ a * p0[0] + b * c1[0] + c * c2[0] + d * p1[0],
5289
+ a * p0[1] + b * c1[1] + c * c2[1] + d * p1[1]
5290
+ ];
5291
+ }
5292
+ function cubicLength(p0, c1, c2, p1, steps = 64) {
5293
+ let len = 0;
5294
+ let prev = p0;
5295
+ for (let i = 1; i <= steps; i++) {
5296
+ const pt = cubicAt(p0, c1, c2, p1, i / steps);
5297
+ len += Math.hypot(pt[0] - prev[0], pt[1] - prev[1]);
5298
+ prev = pt;
5299
+ }
5300
+ return len;
5301
+ }
5302
+ var fmt2 = (n) => {
5303
+ const r = Math.round(n * 1e4) / 1e4;
5304
+ return Object.is(r, -0) ? "0" : String(r);
5305
+ };
5306
+ function buildOffsetPath(propAnim) {
5307
+ var _a, _b, _c, _d;
5308
+ if (propAnim.alongPathMode !== "offsetPath") return void 0;
5309
+ const kfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
5310
+ if (!kfs || kfs.length < 2) return void 0;
5311
+ const first = kfValue(kfs[0]);
5312
+ const anchor = (first == null ? void 0 : first.origin) && first.origin.length >= 2 ? [first.origin[0], first.origin[1]] : [0, 0];
5313
+ const points = [];
5314
+ for (const kf of kfs) {
5315
+ const v = kfValue(kf);
5316
+ const tr = v == null ? void 0 : v.translate;
5317
+ if (!tr || tr.length < 2) return void 0;
5318
+ const parts = Object.keys(v);
5319
+ if (parts.some((p) => p !== "translate" && p !== "origin")) return void 0;
5320
+ const o = (_b = v == null ? void 0 : v.origin) != null ? _b : [0, 0];
5321
+ if (o[0] !== anchor[0] || o[1] !== anchor[1]) return void 0;
5322
+ points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);
5323
+ }
5324
+ if (!kfs.some((kf) => kfTangentIn(kf) || kfTangentOut(kf))) return void 0;
5325
+ let d = "M" + fmt2(points[0][0]) + "," + fmt2(points[0][1]);
5326
+ const segLens = [];
5327
+ for (let i = 0; i < points.length - 1; i++) {
5328
+ const p0 = points[i], p1 = points[i + 1];
5329
+ const to = (_c = kfTangentOut(kfs[i])) != null ? _c : [0, 0];
5330
+ const ti = (_d = kfTangentIn(kfs[i + 1])) != null ? _d : [0, 0];
5331
+ const c1 = [p0[0] + to[0], p0[1] + to[1]];
5332
+ const c2 = [p1[0] + ti[0], p1[1] + ti[1]];
5333
+ d += "C" + fmt2(c1[0]) + "," + fmt2(c1[1]) + "," + fmt2(c2[0]) + "," + fmt2(c2[1]) + "," + fmt2(p1[0]) + "," + fmt2(p1[1]);
5334
+ segLens.push(cubicLength(p0, c1, c2, p1));
5335
+ }
5336
+ const total = segLens.reduce((a, b) => a + b, 0);
5337
+ if (!(total > 0)) return void 0;
5338
+ const distanceKfs = [];
5339
+ let cum = 0;
5340
+ for (let i = 0; i < kfs.length; i++) {
5341
+ if (i > 0) cum += segLens[i - 1];
5342
+ const out = { t: kfTime(kfs[i]), v: cum / total };
5343
+ const e = kfEasing(kfs[i]);
5344
+ if (e !== void 0) out.e = e;
5345
+ distanceKfs.push(out);
5346
+ }
5347
+ return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor };
5348
+ }
5349
+ function materialiseOffsetPathsInTree(root) {
5350
+ const walk = (node) => {
5351
+ var _a;
5352
+ let out = node;
5353
+ const anim = node.animate;
5354
+ const transform = anim == null ? void 0 : anim["transform"];
5355
+ if (transform) {
5356
+ const built = buildOffsetPath(transform);
5357
+ if (built) {
5358
+ const newAnimate = __spreadValues2({}, anim);
5359
+ delete newAnimate["transform"];
5360
+ const distance = { keyframes: built.distanceKfs };
5361
+ if (transform.loop !== void 0) distance.loop = transform.loop;
5362
+ newAnimate["offsetDistance"] = distance;
5363
+ const staticTr = node.transform;
5364
+ let newTransform = staticTr;
5365
+ if (staticTr && typeof staticTr === "object") {
5366
+ const t = __spreadValues2({}, staticTr);
5367
+ delete t["translate"];
5368
+ delete t["origin"];
5369
+ newTransform = Object.keys(t).length ? t : void 0;
5370
+ }
5371
+ out = __spreadProps2(__spreadValues2({}, node), {
5372
+ animate: newAnimate,
5373
+ style: __spreadProps2(__spreadValues2({}, node.style), {
5374
+ offsetPath: "path('" + built.pathStr + "')",
5375
+ offsetAnchor: fmt2(built.anchor[0]) + "px " + fmt2(built.anchor[1]) + "px",
5376
+ offsetRotate: built.autoOrient ? "auto" : "0deg",
5377
+ offsetDistance: "0%"
5378
+ })
5379
+ });
5380
+ if (newTransform !== void 0) out.transform = newTransform;
5381
+ else delete out.transform;
5382
+ }
5383
+ }
5384
+ if ((_a = out.children) == null ? void 0 : _a.length) {
5385
+ const children = out.children.map(walk);
5386
+ if (children.some((c, i) => c !== out.children[i])) out = __spreadProps2(__spreadValues2({}, out), { children });
5387
+ }
5388
+ return out;
5389
+ };
5390
+ return walk(root);
5391
+ }
5104
5392
  function materialiseAllInTree(doc, engine, opts) {
5105
5393
  var _a, _b;
5106
5394
  let root = applyPlayerEffects(doc).root;
5395
+ root = materialiseOffsetPathsInTree(root);
5107
5396
  const duration = (_b = (_a = getAnimatorConfig(root)) == null ? void 0 : _a.duration) != null ? _b : DEFAULT_DURATION_MS;
5108
5397
  root = materialiseInternalLoopsInTree(root, duration);
5109
- if (engine === PxAnimatorEngine.webapi) {
5398
+ if (engine === PxAnimatorEngine.waapi) {
5110
5399
  root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
5111
5400
  root = materialiseAnimatedUseInstances(root);
5112
5401
  root = pruneUnreferencedDefs(root);
@@ -5491,6 +5780,7 @@ function evalTransformValue(v, t) {
5491
5780
  if (typeof v === "string") return parseTransformString(v);
5492
5781
  if (v.keyframes) return partsToMatrix(interpParts(v.keyframes, t));
5493
5782
  if (v.value) return partsToMatrix(v.value);
5783
+ if (typeof v === "object" && !Array.isArray(v)) return partsToMatrix(v);
5494
5784
  return IDENTITY;
5495
5785
  }
5496
5786
  function nodeMatrix(node, t) {
@@ -5625,64 +5915,6 @@ function subtractMultiset(a, b) {
5625
5915
  return extra;
5626
5916
  }
5627
5917
 
5628
- // src/PxAnimatorDOM.ts
5629
- var SVG_NS = "http://www.w3.org/2000/svg";
5630
- function createElement(tagName, normalisedProps, style, children, textContent) {
5631
- if (DISALLOWED_SVG_TAGS_LOWER.has(tagName.toLowerCase())) {
5632
- console.warn("SVG tag blocked (dangerous): ", tagName);
5633
- return null;
5634
- }
5635
- const element = document.createElementNS(SVG_NS, tagName);
5636
- for (const propName in normalisedProps) {
5637
- const sanitised = sanitiseAttributeValue(propName, normalisedProps[propName]);
5638
- if (sanitised === void 0) continue;
5639
- if (CSS_ONLY_STYLE_PROPS.has(propName)) {
5640
- element.style[propName] = String(sanitised);
5641
- continue;
5642
- }
5643
- element.setAttribute(camelCaseToKebabWordIfNeeded(propName), sanitised);
5644
- }
5645
- if (style) {
5646
- for (const styleProp in style) {
5647
- element.style[styleProp] = String(style[styleProp]);
5648
- }
5649
- }
5650
- if (children) {
5651
- for (const child of children) {
5652
- element.appendChild(child);
5653
- }
5654
- }
5655
- if (textContent) element.textContent = textContent;
5656
- return element;
5657
- }
5658
- function renderNode(node, defs) {
5659
- if (!node) return null;
5660
- const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
5661
- const feFuncType = props.funcType;
5662
- if (feFuncType !== void 0) delete props.funcType;
5663
- const nodeDefs = getDefs(node) || defs;
5664
- const resolvedStyle = resolveStyle(style, nodeDefs);
5665
- let childElements;
5666
- if (children) {
5667
- for (const ch of children) {
5668
- const child = renderNode(ch, nodeDefs);
5669
- if (child) {
5670
- if (!childElements) childElements = [];
5671
- childElements.push(child);
5672
- }
5673
- }
5674
- }
5675
- const element = createElement(
5676
- type || "g",
5677
- getNormalizedProps(props),
5678
- resolvedStyle,
5679
- childElements,
5680
- props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
5681
- );
5682
- if (element && feFuncType !== void 0) element.setAttribute("type", feFuncType);
5683
- return element;
5684
- }
5685
-
5686
5918
  // src/PxAnimatorTriggers.ts
5687
5919
  function setupAnimationTriggers(api, config) {
5688
5920
  const { startOn, outAction = "continue", scrollIntoViewThreshold = 0 } = config;
@@ -5869,10 +6101,12 @@ function createCssKf(kf, t, propName, unsupportedSet) {
5869
6101
  } else if (propName === "d") {
5870
6102
  const paths = value && typeof value === "object" && Array.isArray(value.paths) ? value.paths : [];
5871
6103
  cssValue = 'path("' + paths.map((bz) => bezierToSvgPath(bz, true)).join("") + '")';
6104
+ } else if (PCT_BASED_ATTR_NAMES.has(propName) && typeof value === "number") {
6105
+ cssValue = value * 100 + "%";
5872
6106
  } else {
5873
6107
  cssValue = "" + value;
5874
6108
  }
5875
- if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
6109
+ if (!CSS.supports(camelCaseToKebabWordIfNeeded(cssKey), cssValue)) unsupportedSet.add(cssKey);
5876
6110
  cssKey = kebabToCamelCaseWord(cssKey);
5877
6111
  cssKf[cssKey] = cssValue;
5878
6112
  return cssKf;
@@ -5949,7 +6183,7 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
5949
6183
  console.warn("createFrameLoopAnimator: No root element provided");
5950
6184
  }
5951
6185
  }
5952
- const bindings = getNormalisedBindings(doc, PxAnimatorEngine.webapi);
6186
+ const bindings = getNormalisedBindings(doc, PxAnimatorEngine.waapi);
5953
6187
  const animations = [];
5954
6188
  const _iterations = config.iterations;
5955
6189
  let iterations;
@@ -6095,9 +6329,8 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
6095
6329
  return api;
6096
6330
  }
6097
6331
 
6098
- // src/PxAnimator.ts
6099
- function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
6100
- const animatorConfig = getAnimatorConfig(doc) || {};
6332
+ // src/PxAnimatorBind.ts
6333
+ function finaliseAnimator(animatorConfig, callbacks, make) {
6101
6334
  let apiRef;
6102
6335
  let effectiveCallbacks = callbacks;
6103
6336
  if (animatorConfig.resetOnFinish) {
@@ -6109,29 +6342,99 @@ function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
6109
6342
  }
6110
6343
  });
6111
6344
  }
6112
- let res;
6113
- if (animatorConfig.mode === PxAnimatorMode.frames) {
6114
- res = createFrameLoopAnimator(doc, adapter, effectiveCallbacks, rootElement);
6115
- } else {
6116
- res = createWebApiAnimator(
6117
- doc,
6118
- effectiveCallbacks,
6119
- rootElement,
6120
- animatorConfig.mode === PxAnimatorMode.webapi
6121
- // forcing webapi
6122
- ) || createFrameLoopAnimator(doc, adapter, effectiveCallbacks, rootElement);
6123
- }
6345
+ const res = make(effectiveCallbacks);
6124
6346
  apiRef = res;
6125
6347
  if (animatorConfig.debugInstName) {
6126
6348
  window[animatorConfig.debugInstName] = res;
6127
6349
  }
6128
6350
  return res;
6129
6351
  }
6352
+ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6353
+ const animatorConfig = getAnimatorConfig(doc) || {};
6354
+ return finaliseAnimator(animatorConfig, callbacks, (cb) => {
6355
+ if (animatorConfig.mode === PxAnimatorMode.frames) {
6356
+ return createFrameLoopAnimator(doc, adapter, cb, rootElement);
6357
+ }
6358
+ return createWebApiAnimator(
6359
+ doc,
6360
+ cb,
6361
+ rootElement,
6362
+ animatorConfig.mode === PxAnimatorMode.waapi
6363
+ // forcing waapi
6364
+ ) || createFrameLoopAnimator(doc, adapter, cb, rootElement);
6365
+ });
6366
+ }
6367
+
6368
+ // src/PxAnimatorDOM.ts
6369
+ var SVG_NS = "http://www.w3.org/2000/svg";
6370
+ function createElement(tagName, normalisedProps, style, children, textContent) {
6371
+ if (DISALLOWED_SVG_TAGS_LOWER.has(tagName.toLowerCase())) {
6372
+ console.warn("SVG tag blocked (dangerous): ", tagName);
6373
+ return null;
6374
+ }
6375
+ const element = document.createElementNS(SVG_NS, tagName);
6376
+ for (const propName in normalisedProps) {
6377
+ const sanitised = sanitiseAttributeValue(propName, normalisedProps[propName]);
6378
+ if (sanitised === void 0) continue;
6379
+ if (CSS_ONLY_STYLE_PROPS.has(propName)) {
6380
+ element.style[propName] = String(sanitised);
6381
+ continue;
6382
+ }
6383
+ element.setAttribute(camelCaseToKebabWordIfNeeded(propName), sanitised);
6384
+ }
6385
+ if (style) {
6386
+ for (const styleProp in style) {
6387
+ element.style[styleProp] = String(style[styleProp]);
6388
+ }
6389
+ }
6390
+ if (children) {
6391
+ for (const child of children) {
6392
+ element.appendChild(child);
6393
+ }
6394
+ }
6395
+ if (textContent) element.textContent = textContent;
6396
+ return element;
6397
+ }
6398
+ function renderNode(node, defs) {
6399
+ if (!node) return null;
6400
+ const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
6401
+ const domType = props.domType;
6402
+ if (domType !== void 0) delete props.domType;
6403
+ const nodeDefs = getDefs(node) || defs;
6404
+ const resolvedStyle = resolveStyle(style, nodeDefs);
6405
+ let childElements;
6406
+ if (children) {
6407
+ for (const ch of children) {
6408
+ const child = renderNode(ch, nodeDefs);
6409
+ if (child) {
6410
+ if (!childElements) childElements = [];
6411
+ childElements.push(child);
6412
+ }
6413
+ }
6414
+ }
6415
+ const element = createElement(
6416
+ type || "g",
6417
+ getNormalizedProps(props),
6418
+ resolvedStyle,
6419
+ childElements,
6420
+ props[TEXT_CONTENT_ATTR] || props[TEXT_ATTR]
6421
+ );
6422
+ if (element && domType !== void 0) element.setAttribute("type", domType);
6423
+ return element;
6424
+ }
6425
+
6426
+ // src/PxAnimatorKeys.ts
6427
+ var PX_ANIMATOR_DATA_KEY = "data";
6428
+
6429
+ // src/PxAnimator.ts
6430
+ function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
6431
+ return bindWithEngineChoice(doc, adapter, callbacks, rootElement);
6432
+ }
6130
6433
  function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
6131
6434
  const effectsWarnings = validateNodeEffects(doc);
6132
6435
  for (const w of effectsWarnings) console.warn("[PxAnimator] effects shape warning:", w);
6133
6436
  const animatorConfig = getAnimatorConfig(doc) || {};
6134
- const engine = animatorConfig.mode === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi;
6437
+ const engine = animatorConfig.mode === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.waapi;
6135
6438
  doc = materialiseAllInTree(doc, engine);
6136
6439
  let rootElement = null;
6137
6440
  if (containerElement) {
@@ -6146,7 +6449,6 @@ function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
6146
6449
  }
6147
6450
  return createAnimatorFromConfig(doc, adapter, callbacks, rootElement);
6148
6451
  }
6149
- var PX_ANIMATOR_DATA_KEY = "data";
6150
6452
  function createAnimator(options) {
6151
6453
  const { src, data, adapter, callbacks, container } = options;
6152
6454
  if (data !== void 0 && src !== void 0) {
@@ -6254,6 +6556,7 @@ export {
6254
6556
  PxGradientType,
6255
6557
  PxGradientUnits,
6256
6558
  PxKeyframeSchema,
6559
+ PxKeyframeValueSchema,
6257
6560
  PxLoopSchema,
6258
6561
  PxMaskedByEffectSchema,
6259
6562
  PxNodeBase,
@@ -6265,9 +6568,9 @@ export {
6265
6568
  PxSvgNodeExtra,
6266
6569
  PxTextEffectSchema,
6267
6570
  PxTextPathEffectSchema,
6571
+ PxTransformByEffectSchema,
6268
6572
  PxTransformPartsSchema,
6269
6573
  PxTransformValueSchema,
6270
- PxTransformationEffectSchema,
6271
6574
  PxTriggerSchema,
6272
6575
  PxTrimPathEffectSchema,
6273
6576
  STYLE_ATTR_NAMES,