@pixodesk/svg-animator-web 1.0.34 → 1.0.39

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.
@@ -50,12 +50,12 @@ var PixodeskAnimator = (() => {
50
50
  // src/index.prerendered-waapi.ts
51
51
  var index_prerendered_waapi_exports = {};
52
52
  __export(index_prerendered_waapi_exports, {
53
- PX_ANIMATOR_DATA_KEY: () => PX_ANIMATOR_DATA_KEY,
53
+ PX_ANIMATOR_DOC_KEY: () => PX_ANIMATOR_DOC_KEY,
54
54
  createAnimator: () => createPrerenderedWaapiAnimator,
55
55
  setupAnimationTriggers: () => setupAnimationTriggers
56
56
  });
57
57
 
58
- // ../svg-animator-core/src/PxAnimatorUtil.ts
58
+ // ../svg-animator-core/src/util/PxAnimatorUtil.ts
59
59
  function bezierToSvgPath(path, forceCurves = false) {
60
60
  var _a, _b, _c, _d;
61
61
  const v = path.v;
@@ -280,7 +280,7 @@ var PixodeskAnimator = (() => {
280
280
  }
281
281
  return void 0;
282
282
  }
283
- var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
283
+ var COLOR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
284
284
  var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
285
285
  var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
286
286
  function composeTransformParts(parts, opts) {
@@ -478,29 +478,687 @@ var PixodeskAnimator = (() => {
478
478
  return cubicBezier(flipped);
479
479
  }
480
480
 
481
- // ../svg-animator-core/src/PxAnimatorConstants.ts
482
- var PxAnimatorMode = {
481
+ // ../svg-animator-core/src/schema/PxSchema.ts
482
+ var PX_UNKNOWN_KEY_ERROR = "unexpected extra key";
483
+ function pathStr(path) {
484
+ if (!path.length) return ".";
485
+ let result = "";
486
+ for (const seg of path) {
487
+ if (seg.startsWith("[")) result += seg;
488
+ else result += (result ? "." : "") + seg;
489
+ }
490
+ return result;
491
+ }
492
+ var Base = class {
493
+ _canSanitize(raw) {
494
+ return this.isValid(raw);
495
+ }
496
+ optional() {
497
+ return new Optional(this);
498
+ }
499
+ };
500
+ var Optional = class extends Base {
501
+ constructor(inner) {
502
+ super();
503
+ this.inner = inner;
504
+ this._default = void 0;
505
+ }
506
+ sanitize(raw) {
507
+ if (raw === void 0 || raw === null) return void 0;
508
+ return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
509
+ }
510
+ isValid(raw, ctx, path) {
511
+ if (raw === void 0 || raw === null) return true;
512
+ return this.inner.isValid(raw, ctx, path);
513
+ }
514
+ _canSanitize(raw) {
515
+ return raw === void 0 || raw === null || this.inner._canSanitize(raw);
516
+ }
517
+ };
518
+ var Str = class extends Base {
519
+ constructor(_default = "") {
520
+ super();
521
+ this._default = _default;
522
+ }
523
+ sanitize(raw) {
524
+ return typeof raw === "string" ? raw : this._default;
525
+ }
526
+ isValid(raw, ctx, path) {
527
+ if (typeof raw === "string") return true;
528
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
529
+ return false;
530
+ }
531
+ };
532
+ var Num = class extends Base {
533
+ constructor(_default = 0) {
534
+ super();
535
+ this._default = _default;
536
+ }
537
+ sanitize(raw) {
538
+ return typeof raw === "number" && isFinite(raw) ? raw : this._default;
539
+ }
540
+ isValid(raw, ctx, path) {
541
+ if (typeof raw === "number" && isFinite(raw)) return true;
542
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
543
+ return false;
544
+ }
545
+ };
546
+ var Bool = class extends Base {
547
+ constructor(_default = false) {
548
+ super();
549
+ this._default = _default;
550
+ }
551
+ sanitize(raw) {
552
+ return typeof raw === "boolean" ? raw : this._default;
553
+ }
554
+ isValid(raw, ctx, path) {
555
+ if (typeof raw === "boolean") return true;
556
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
557
+ return false;
558
+ }
559
+ };
560
+ var Literal = class extends Base {
561
+ constructor(value) {
562
+ super();
563
+ this.value = value;
564
+ this._default = value;
565
+ }
566
+ sanitize(raw) {
567
+ return raw === this.value ? this.value : this._default;
568
+ }
569
+ isValid(raw, ctx, path) {
570
+ if (raw === this.value) return true;
571
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
572
+ return false;
573
+ }
574
+ };
575
+ var Enum = class extends Base {
576
+ constructor(values, defaultVal) {
577
+ super();
578
+ this.values = values;
579
+ this._default = defaultVal != null ? defaultVal : values[0];
580
+ }
581
+ sanitize(raw) {
582
+ return this.values.includes(raw) ? raw : this._default;
583
+ }
584
+ isValid(raw, ctx, path) {
585
+ if (this.values.includes(raw)) return true;
586
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected one of " + this.values.map((v) => JSON.stringify(v)).join(" | ") + ", got " + JSON.stringify(raw));
587
+ return false;
588
+ }
589
+ };
590
+ var UNION_MEMBER_ERROR_LIMIT = 4;
591
+ var Union = class extends Base {
592
+ constructor(schemas, defaultVal) {
593
+ super();
594
+ this.schemas = schemas;
595
+ /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Union from Tuple. */
596
+ this._kind = "union";
597
+ this._default = defaultVal != null ? defaultVal : schemas[0]._default;
598
+ }
599
+ sanitize(raw) {
600
+ for (const s of this.schemas) {
601
+ if (s.isValid(raw)) return s.sanitize(raw);
602
+ }
603
+ return this._default;
604
+ }
605
+ isValid(raw, ctx, path) {
606
+ var _a;
607
+ const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
608
+ if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
609
+ if (!ctx) return false;
610
+ const base = pathStr(path != null ? path : []);
611
+ ctx.errors.push(base + ": no union member matched for value " + ((_a = JSON.stringify(raw)) != null ? _a : "").slice(0, 240));
612
+ let best;
613
+ let bestDepth = -1;
614
+ const leafExpectations = [];
615
+ for (const member of this.schemas) {
616
+ const sink = { errors: [], warnings: [], strict: ctx.strict };
617
+ member.isValid(raw, sink, path ? [...path] : void 0);
618
+ if (!sink.errors.length) continue;
619
+ const depth = Math.max(...sink.errors.map((e) => e.slice(0, e.indexOf(":")).length));
620
+ if (depth > bestDepth || depth === bestDepth && best && sink.errors.length < best.length) {
621
+ bestDepth = depth;
622
+ best = sink.errors;
623
+ }
624
+ if (depth <= base.length) {
625
+ for (const e of sink.errors) {
626
+ const m = /: expected (.+?), got /.exec(e);
627
+ if (m && !leafExpectations.includes(m[1])) leafExpectations.push(m[1]);
628
+ }
629
+ }
630
+ }
631
+ if (best && bestDepth > base.length) {
632
+ for (const e of best.slice(0, UNION_MEMBER_ERROR_LIMIT)) {
633
+ if (!ctx.errors.includes(e)) ctx.errors.push(e);
634
+ }
635
+ } else if (leafExpectations.length) {
636
+ ctx.errors.push(base + ": expected " + leafExpectations.join(" | "));
637
+ }
638
+ return false;
639
+ }
640
+ _canSanitize(raw) {
641
+ return this.schemas.some((s) => s._canSanitize(raw));
642
+ }
643
+ };
644
+ var DiscriminatedUnion = class extends Base {
645
+ constructor(_key, _schemas, defaultVal) {
646
+ var _a;
647
+ super();
648
+ this._key = _key;
649
+ this._schemas = _schemas;
650
+ /** Structural tag read by {@link describeSchema}. */
651
+ this._kind = "discriminatedUnion";
652
+ this._default = defaultVal != null ? defaultVal : _schemas[0]._default;
653
+ this._map = /* @__PURE__ */ new Map();
654
+ for (const s of _schemas) {
655
+ const keySchema = s._shape[_key];
656
+ if (!keySchema) continue;
657
+ const literal = (_a = keySchema.inner) != null ? _a : keySchema;
658
+ this._map.set(literal._default, s);
659
+ if (keySchema.inner) this._absentMember = s;
660
+ }
661
+ }
662
+ _findSchema(raw) {
663
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
664
+ const val = raw[this._key];
665
+ if (val === void 0 || val === null) return this._absentMember;
666
+ return this._map.get(val);
667
+ }
668
+ sanitize(raw) {
669
+ var _a;
670
+ return ((_a = this._findSchema(raw)) != null ? _a : this._schemas[0]).sanitize(raw);
671
+ }
672
+ isValid(raw, ctx, path) {
673
+ const schema = this._findSchema(raw);
674
+ if (!schema) {
675
+ const val = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw[this._key] : void 0;
676
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no discriminated union member matched " + this._key + "=" + JSON.stringify(val));
677
+ return false;
678
+ }
679
+ return schema.isValid(raw, ctx, path);
680
+ }
681
+ _canSanitize(raw) {
682
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return false;
683
+ const schema = this._findSchema(raw);
684
+ return schema ? schema._canSanitize(raw) : this._schemas[0]._canSanitize(raw);
685
+ }
686
+ };
687
+ var Obj = class extends Base {
688
+ constructor(_shape) {
689
+ super();
690
+ this._shape = _shape;
691
+ const d = {};
692
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
693
+ this._default = d;
694
+ }
695
+ sanitize(raw) {
696
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
697
+ const out = {};
698
+ for (const key of Object.keys(this._shape)) {
699
+ const v = this._shape[key].sanitize(src[key]);
700
+ if (v !== void 0) out[key] = v;
701
+ }
702
+ return out;
703
+ }
704
+ isValid(raw, ctx, path) {
705
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
706
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
707
+ return false;
708
+ }
709
+ const obj = raw;
710
+ const p = path != null ? path : [];
711
+ let ok = true;
712
+ for (const key of Object.keys(this._shape)) {
713
+ p.push(key);
714
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
715
+ p.pop();
716
+ }
717
+ if (ctx == null ? void 0 : ctx.strict) {
718
+ for (const key of Object.keys(obj)) {
719
+ if (key in this._shape) continue;
720
+ if (obj[key] === void 0) continue;
721
+ p.push(key);
722
+ ctx.errors.push(pathStr(p) + ": " + PX_UNKNOWN_KEY_ERROR);
723
+ p.pop();
724
+ ok = false;
725
+ }
726
+ }
727
+ return ok;
728
+ }
729
+ _canSanitize(raw) {
730
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
731
+ }
732
+ };
733
+ var OpenObj = class extends Base {
734
+ constructor(_shape, _openSchema) {
735
+ super();
736
+ this._shape = _shape;
737
+ this._openSchema = _openSchema;
738
+ const d = {};
739
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
740
+ this._default = d;
741
+ }
742
+ sanitize(raw) {
743
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
744
+ const out = __spreadValues({}, src);
745
+ for (const key of Object.keys(this._shape)) {
746
+ const v = this._shape[key].sanitize(src[key]);
747
+ if (v !== void 0) out[key] = v;
748
+ }
749
+ if (this._openSchema) {
750
+ for (const key of Object.keys(src)) {
751
+ if (!(key in this._shape)) out[key] = this._openSchema.sanitize(src[key]);
752
+ }
753
+ }
754
+ return out;
755
+ }
756
+ isValid(raw, ctx, path) {
757
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
758
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
759
+ return false;
760
+ }
761
+ const obj = raw;
762
+ const p = path != null ? path : [];
763
+ let ok = true;
764
+ for (const key of Object.keys(this._shape)) {
765
+ p.push(key);
766
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
767
+ p.pop();
768
+ }
769
+ if (this._openSchema) {
770
+ for (const key of Object.keys(obj)) {
771
+ if (key in this._shape) continue;
772
+ p.push(key);
773
+ if (!this._openSchema.isValid(obj[key], ctx, p)) ok = false;
774
+ p.pop();
775
+ }
776
+ }
777
+ return ok;
778
+ }
779
+ _canSanitize(raw) {
780
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
781
+ }
782
+ };
783
+ var Arr = class extends Base {
784
+ constructor(item) {
785
+ super();
786
+ this.item = item;
787
+ this._default = [];
788
+ }
789
+ sanitize(raw) {
790
+ if (!Array.isArray(raw)) return [];
791
+ const out = [];
792
+ for (const el of raw) {
793
+ if (this.item._canSanitize(el)) out.push(this.item.sanitize(el));
794
+ }
795
+ return out;
796
+ }
797
+ isValid(raw, ctx, path) {
798
+ if (!Array.isArray(raw)) {
799
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected array, got " + typeof raw);
800
+ return false;
801
+ }
802
+ const p = path != null ? path : [];
803
+ let ok = true;
804
+ for (let i = 0; i < raw.length; i++) {
805
+ p.push("[" + i + "]");
806
+ if (!this.item.isValid(raw[i], ctx, p)) ok = false;
807
+ p.pop();
808
+ }
809
+ return ok;
810
+ }
811
+ _canSanitize(raw) {
812
+ return Array.isArray(raw);
813
+ }
814
+ };
815
+ var Rec = class extends Base {
816
+ constructor(value) {
817
+ super();
818
+ this.value = value;
819
+ /** Structural tag read by {@link describeSchema}. */
820
+ this._kind = "record";
821
+ this._default = {};
822
+ }
823
+ sanitize(raw) {
824
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
825
+ const out = {};
826
+ for (const [k, v] of Object.entries(raw)) {
827
+ if (this.value._canSanitize(v)) out[k] = this.value.sanitize(v);
828
+ }
829
+ return out;
830
+ }
831
+ isValid(raw, ctx, path) {
832
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
833
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object/record, got " + (Array.isArray(raw) ? "array" : typeof raw));
834
+ return false;
835
+ }
836
+ const p = path != null ? path : [];
837
+ let ok = true;
838
+ for (const [k, v] of Object.entries(raw)) {
839
+ p.push(k);
840
+ if (!this.value.isValid(v, ctx, p)) ok = false;
841
+ p.pop();
842
+ }
843
+ return ok;
844
+ }
845
+ _canSanitize(raw) {
846
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
847
+ }
848
+ };
849
+ var Any = class extends Base {
850
+ constructor() {
851
+ super(...arguments);
852
+ this._default = void 0;
853
+ }
854
+ sanitize(raw) {
855
+ return raw;
856
+ }
857
+ isValid(_raw, _ctx, _path) {
858
+ return true;
859
+ }
860
+ _canSanitize(_raw) {
861
+ return true;
862
+ }
863
+ };
864
+ var Defined = class extends Base {
865
+ constructor() {
866
+ super(...arguments);
867
+ this._default = void 0;
868
+ }
869
+ sanitize(raw) {
870
+ return raw;
871
+ }
872
+ isValid(raw, ctx, path) {
873
+ if (raw !== void 0) return true;
874
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": required value is missing");
875
+ return false;
876
+ }
877
+ _canSanitize(raw) {
878
+ return raw !== void 0;
879
+ }
880
+ };
881
+ var Lazy = class extends Base {
882
+ constructor(fn, _default) {
883
+ super();
884
+ this.fn = fn;
885
+ this._default = _default;
886
+ this.resolved = null;
887
+ }
888
+ get schema() {
889
+ var _a;
890
+ return (_a = this.resolved) != null ? _a : this.resolved = this.fn();
891
+ }
892
+ sanitize(raw) {
893
+ return this.schema.sanitize(raw);
894
+ }
895
+ isValid(raw, ctx, path) {
896
+ return this.schema.isValid(raw, ctx, path);
897
+ }
898
+ _canSanitize(raw) {
899
+ return this.schema._canSanitize(raw);
900
+ }
901
+ };
902
+ var Tuple = class extends Base {
903
+ constructor(schemas) {
904
+ super();
905
+ this.schemas = schemas;
906
+ /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Tuple from Union. */
907
+ this._kind = "tuple";
908
+ this._default = schemas.map((s) => s._default);
909
+ }
910
+ sanitize(raw) {
911
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) return this._default;
912
+ return this.schemas.map((s, i) => s.sanitize(raw[i]));
913
+ }
914
+ isValid(raw, ctx, path) {
915
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) {
916
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected tuple of length " + this.schemas.length + ", got " + (Array.isArray(raw) ? "array[" + raw.length + "]" : typeof raw));
917
+ return false;
918
+ }
919
+ const p = path != null ? path : [];
920
+ let ok = true;
921
+ for (let i = 0; i < this.schemas.length; i++) {
922
+ p.push("[" + i + "]");
923
+ if (!this.schemas[i].isValid(raw[i], ctx, p)) ok = false;
924
+ p.pop();
925
+ }
926
+ return ok;
927
+ }
928
+ // Require exact length so wrong-length arrays are dropped rather than repaired to default.
929
+ _canSanitize(raw) {
930
+ return Array.isArray(raw) && raw.length === this.schemas.length;
931
+ }
932
+ };
933
+ function implementsInterface() {
934
+ return (schema) => schema;
935
+ }
936
+ var px = {
937
+ /** Matches a string. Default: '' or provided value. */
938
+ string: (defaultVal = "") => new Str(defaultVal),
939
+ /** Matches a finite number. Default: 0 or provided value. */
940
+ number: (defaultVal = 0) => new Num(defaultVal),
941
+ /** Matches a boolean. Default: false or provided value. */
942
+ boolean: (defaultVal = false) => new Bool(defaultVal),
943
+ /** Matches one exact primitive value; its default is the value itself. */
944
+ literal: (value) => new Literal(value),
945
+ /** Matches one of a fixed set of string/number values. Default: first value. */
946
+ enum: (values, defaultVal) => new Enum(values, defaultVal),
947
+ /**
948
+ * Returns the first schema whose isValid passes.
949
+ * TypeScript infers the union of all member types automatically.
950
+ */
951
+ union: (schemas, defaultVal) => new Union(schemas, defaultVal),
952
+ /**
953
+ * Discriminated union — reads `raw[key]`, finds the member schema whose
954
+ * literal at `key` matches, then delegates sanitize/isValid to that member.
955
+ * Each member must be an object schema with a `px.literal(...)` at `key`.
956
+ * TypeScript infers the union of all member types automatically.
957
+ */
958
+ discriminatedUnion: (key, schemas) => new DiscriminatedUnion(key, schemas),
959
+ /** Typed object — unknown keys are stripped. Required fields fall back to their default. */
960
+ object: (shape) => new Obj(shape),
961
+ /**
962
+ * Open object — validates known keys; passes unknown keys through as-is,
963
+ * or validates/sanitizes them against `openSchema` when provided.
964
+ */
965
+ openObject: (shape, openSchema) => new OpenObj(shape, openSchema),
966
+ /**
967
+ * Creates a new closed object schema by merging a base schema's shape with additional fields.
968
+ * The base can be the result of px.object() or px.openObject() — anything with a _shape property.
969
+ *
970
+ * @example
971
+ * const PxSvgNodeSchema = px.extendedObject(PxNodeBase, { width: px.number().optional() });
972
+ */
973
+ extendedObject: (base, extra) => new Obj(__spreadValues(__spreadValues({}, base._shape), extra)),
974
+ /** Array whose unrecoverable items are filtered out. Default: []. */
975
+ array: (item) => new Arr(item),
976
+ /** String-keyed record whose unrecoverable values are dropped. Default: {}. */
977
+ record: (value) => new Rec(value),
978
+ /** Passes anything through unchanged — always valid. */
979
+ any: () => new Any(),
980
+ /** Anything EXCEPT `undefined` — an open type whose presence is required (V6). */
981
+ defined: () => new Defined(),
982
+ /** Fixed-length tuple — validates element count and each position individually. */
983
+ tuple: (schemas) => new Tuple(schemas),
984
+ /** Defers schema creation — required for recursive types. Must supply a default value. */
985
+ lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
986
+ };
987
+
988
+ // ../svg-animator-core/src/format/PxAnimatorConstants.ts
989
+ var PxFillMode = {
990
+ forwards: "forwards",
991
+ backwards: "backwards",
992
+ both: "both",
993
+ none: "none"
994
+ };
995
+ var PxPlaybackDirection = {
996
+ normal: "normal",
997
+ reverse: "reverse",
998
+ alternate: "alternate",
999
+ alternateReverse: "alternate-reverse"
1000
+ };
1001
+ var PxStartOn = {
1002
+ load: "load",
1003
+ mouseOver: "mouseOver",
1004
+ click: "click",
1005
+ scrollIntoView: "scrollIntoView",
1006
+ programmatic: "programmatic"
1007
+ };
1008
+ var PxOutAction = {
1009
+ continue: "continue",
1010
+ pause: "pause",
1011
+ reset: "reset",
1012
+ reverse: "reverse"
1013
+ };
1014
+ var PxFinishAction = {
1015
+ hold: "hold",
1016
+ reset: "reset"
1017
+ };
1018
+ var PxScrollKind = {
1019
+ view: "view",
1020
+ scroll: "scroll"
1021
+ };
1022
+ var PxScrollAxis = {
1023
+ block: "block",
1024
+ inline: "inline",
1025
+ x: "x",
1026
+ y: "y"
1027
+ };
1028
+ var PxScrollSource = {
1029
+ nearest: "nearest",
1030
+ root: "root"
1031
+ };
1032
+ var PxPinAlign = {
1033
+ top: "top",
1034
+ center: "center",
1035
+ bottom: "bottom"
1036
+ };
1037
+ var PxScrollPhase = {
1038
+ cover: "cover",
1039
+ contain: "contain",
1040
+ entry: "entry",
1041
+ exit: "exit",
1042
+ entryCrossing: "entry-crossing",
1043
+ exitCrossing: "exit-crossing"
1044
+ };
1045
+ var PxAlongPathMode = {
1046
+ sampled: "sampled",
1047
+ offsetPath: "offsetPath"
1048
+ };
1049
+ var PX_TIMELINE_SHARED_KEYS = ["duration", "iterations", "engine", "frameRate"];
1050
+ var PX_TIME_ONLY_TIMELINE_KEYS = ["trigger", "delay", "fillMode", "direction"];
1051
+ var PX_FLAT_RUNTIME_VIEW_KEYS = [
1052
+ ...PX_TIMELINE_SHARED_KEYS,
1053
+ ...PX_TIME_ONLY_TIMELINE_KEYS,
1054
+ "fill",
1055
+ "resetOnFinish",
1056
+ "timelineSource",
1057
+ "scroll"
1058
+ ];
1059
+ var PxTimelineEngine = {
1060
+ native: "native",
1061
+ js: "js"
1062
+ };
1063
+ var PxTimelineEngineExtra = __spreadProps(__spreadValues({}, PxTimelineEngine), {
1064
+ auto: "auto"
1065
+ });
1066
+ var PX_TRIGGER_DEFAULTS = {
1067
+ startOn: "load",
1068
+ outAction: "continue",
1069
+ scrollIntoViewThreshold: 0
1070
+ };
1071
+ function resolveTrigger(trigger) {
1072
+ var _a, _b, _c;
1073
+ return {
1074
+ startOn: (_a = trigger == null ? void 0 : trigger.startOn) != null ? _a : PX_TRIGGER_DEFAULTS.startOn,
1075
+ outAction: (_b = trigger == null ? void 0 : trigger.outAction) != null ? _b : PX_TRIGGER_DEFAULTS.outAction,
1076
+ scrollIntoViewThreshold: (_c = trigger == null ? void 0 : trigger.scrollIntoViewThreshold) != null ? _c : PX_TRIGGER_DEFAULTS.scrollIntoViewThreshold
1077
+ };
1078
+ }
1079
+ var PxLoopRepeatAt = {
1080
+ /** Segment from the START; the repetition runs BEFORE the first keyframe
1081
+ * (intro loops that play until the main timeline begins). */
1082
+ start: "start",
1083
+ /** DEFAULT — segment from the END; the repetition runs AFTER the last keyframe
1084
+ * (idle/outro loops that continue once the main timeline has finished). */
1085
+ end: "end"
1086
+ };
1087
+ var PxLoopDirection = {
1088
+ /** DEFAULT — cycle: every repetition replays the segment the same way round. */
1089
+ normal: "normal",
1090
+ /** Ping-pong: repetitions alternate forward / backward. */
1091
+ alternate: "alternate"
1092
+ };
1093
+ var PxMaskType = {
1094
+ luminance: "luminance",
1095
+ alpha: "alpha"
1096
+ };
1097
+ var PxUnits = {
1098
+ userSpaceOnUse: "userSpaceOnUse",
1099
+ objectBoundingBox: "objectBoundingBox"
1100
+ };
1101
+ var PxCloneWithout = {
1102
+ translate: "translate"
1103
+ // transform: 'transform', // future: drop rotate/scale too (content only)
1104
+ };
1105
+ var PxPathOverflow = {
1106
+ clip: "clip",
1107
+ extend: "extend"
1108
+ };
1109
+ var PxLengthAdjust = {
1110
+ spacing: "spacing",
1111
+ spacingAndGlyphs: "spacingAndGlyphs"
1112
+ };
1113
+ var PxTextPathMethod = {
1114
+ align: "align",
1115
+ stretch: "stretch"
1116
+ };
1117
+ var PxTextPathSpacing = {
483
1118
  auto: "auto",
484
- waapi: "waapi",
485
- frames: "frames"
1119
+ exact: "exact"
1120
+ };
1121
+ var PxStrokeTrimSubPaths = {
1122
+ separate: "separate",
1123
+ combined: "combined"
486
1124
  };
487
- var PxAnimatorEngine = {
488
- waapi: PxAnimatorMode.waapi,
489
- frames: PxAnimatorMode.frames
1125
+ var TRANSFORM_ATTR = "transform";
1126
+ var TRANSFORM_PART = {
1127
+ translate: "translate",
1128
+ rotate: "rotate",
1129
+ scale: "scale",
1130
+ origin: "origin"
490
1131
  };
491
- var PxLoopExtend = {
492
- /** Segment from the START; the animation is extended BEFORE the first keyframe
493
- * (intro loops that run before the main timeline begins). */
494
- before: "before",
495
- /** DEFAULT — segment from the END; extended AFTER the last keyframe (idle/outro
496
- * loops that continue once the main timeline has finished). */
497
- after: "after"
1132
+ var PX_TRANSFORM_PART_KEYS = [
1133
+ TRANSFORM_PART.translate,
1134
+ TRANSFORM_PART.rotate,
1135
+ TRANSFORM_PART.scale,
1136
+ TRANSFORM_PART.origin
1137
+ ];
1138
+ var PxGradientSpreadMethod = {
1139
+ pad: "pad",
1140
+ reflect: "reflect",
1141
+ repeat: "repeat"
1142
+ };
1143
+ var PxGradientType = {
1144
+ linear: "linear",
1145
+ radial: "radial"
498
1146
  };
499
1147
  function getAnimatorConfig(doc) {
500
1148
  var _a;
501
1149
  const cfg = (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
502
- return cfg ? flattenAnimatorTimeline(cfg) : void 0;
503
- }
1150
+ if (!cfg) return void 0;
1151
+ const memoised = wireViewMemo.get(cfg);
1152
+ if (memoised) return memoised;
1153
+ const wire = cfg;
1154
+ const stray = PX_FLAT_RUNTIME_VIEW_KEYS.filter((k) => wire[k] !== void 0);
1155
+ const source = stray.length ? __spreadValues({}, wire) : cfg;
1156
+ for (const k of stray) delete source[k];
1157
+ const view = flattenAnimatorTimeline(source);
1158
+ wireViewMemo.set(cfg, view);
1159
+ return view;
1160
+ }
1161
+ var wireViewMemo = /* @__PURE__ */ new WeakMap();
504
1162
  var flattenMemo = /* @__PURE__ */ new WeakMap();
505
1163
  function flattenAnimatorTimeline(cfg) {
506
1164
  const timeline = cfg.timeline;
@@ -508,13 +1166,14 @@ var PixodeskAnimator = (() => {
508
1166
  const memoised = flattenMemo.get(cfg);
509
1167
  if (memoised) return memoised;
510
1168
  const _a = cfg, { timeline: _dropped } = _a, flat = __objRest(_a, ["timeline"]);
1169
+ if (timeline.engine !== void 0) flat.engine = timeline.engine;
1170
+ if (timeline.frameRate !== void 0) flat.frameRate = timeline.frameRate;
511
1171
  if (timeline.type === "scroll" || timeline.type === "view") {
512
1172
  flat.timelineSource = "scroll";
513
1173
  if (timeline.duration !== void 0) flat.duration = timeline.duration;
514
1174
  if (timeline.iterations !== void 0) flat.iterations = timeline.iterations;
515
1175
  const scroll = __spreadValues({}, flat.scroll || {});
516
1176
  scroll.kind = timeline.type;
517
- if (timeline.engine !== void 0) scroll.driver = timeline.engine;
518
1177
  if (timeline.axis !== void 0) scroll.axis = timeline.axis;
519
1178
  if (timeline.source !== void 0) scroll.source = timeline.source;
520
1179
  if (timeline.subject !== void 0) scroll.subject = timeline.subject;
@@ -525,21 +1184,21 @@ var PixodeskAnimator = (() => {
525
1184
  else if (pin && typeof pin === "object") {
526
1185
  scroll.pin = true;
527
1186
  if (pin.align !== void 0) scroll.pinAlign = pin.align;
528
- if (pin.top !== void 0) scroll.pinTop = pin.top;
1187
+ if (pin.offset !== void 0) scroll.pinOffset = pin.offset;
529
1188
  if (pin.distance !== void 0) scroll.pinDistance = pin.distance;
530
1189
  }
531
1190
  flat.scroll = scroll;
532
1191
  } else {
533
1192
  if (timeline.duration !== void 0) flat.duration = timeline.duration;
534
1193
  if (timeline.trigger !== void 0) {
535
- const _b = timeline.trigger, { onFinish } = _b, restTrigger = __objRest(_b, ["onFinish"]);
1194
+ const _b = timeline.trigger, { finishAction } = _b, restTrigger = __objRest(_b, ["finishAction"]);
536
1195
  if (Object.keys(restTrigger).length) flat.trigger = restTrigger;
537
- if (onFinish !== void 0) flat.resetOnFinish = onFinish === "reset";
1196
+ if (finishAction !== void 0) flat.resetOnFinish = finishAction === "reset";
538
1197
  }
539
1198
  if (timeline.delay !== void 0) flat.delay = timeline.delay;
540
1199
  if (timeline.iterations !== void 0) flat.iterations = timeline.iterations;
541
1200
  if (timeline.direction !== void 0) flat.direction = timeline.direction;
542
- if (timeline.fill !== void 0) flat.fill = timeline.fill;
1201
+ if (timeline.fillMode !== void 0) flat.fill = timeline.fillMode;
543
1202
  }
544
1203
  flattenMemo.set(cfg, flat);
545
1204
  return flat;
@@ -557,10 +1216,384 @@ var PixodeskAnimator = (() => {
557
1216
  return Object.entries(animateById).map(([id, anim]) => ({ id: id.startsWith("#") ? id.slice(1) : id, animate: anim }));
558
1217
  }
559
1218
 
560
- // ../svg-animator-core/src/PxMotionPath.ts
561
- function getKfTranslate(kf) {
1219
+ // ../svg-animator-core/src/format/PxAnimatorTypes.ts
1220
+ var PxEasingOrRefSchema = px.union([
1221
+ px.string(),
1222
+ px.tuple([px.number(), px.number(), px.number(), px.number()])
1223
+ ]);
1224
+ var PxKeyframeValueSchema = implementsInterface()(px.union([
1225
+ px.string(),
1226
+ // e.g. for colors
1227
+ px.number(),
1228
+ px.array(px.number()),
1229
+ // ORDER LAW: the key-discriminated object shape (`{pathData}`) comes BEFORE the
1230
+ // all-optional transform-parts record. In default (non-strict) mode that record accepts
1231
+ // ANY object (every key optional, unknown keys ignored), so listing it earlier made
1232
+ // Union.sanitize route `{pathData}` values into it and strip them to `{}` —
1233
+ // silent morph-data loss (repro: the editor's keyframeValueSanitize spec). Validity is
1234
+ // order-independent (`some()`); only sanitize routing depends on this order.
1235
+ px.object({ pathData: px.string() }),
1236
+ // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
1237
+ px.lazy(() => px.array(PxGradientStopSchema), []),
1238
+ px.lazy(() => PxTransformPartsSchema, {})
1239
+ ]));
1240
+ var PxKeyframeSchema = implementsInterface()(px.object({
1241
+ time: px.number().optional(),
1242
+ value: PxKeyframeValueSchema.optional(),
1243
+ easing: PxEasingOrRefSchema.optional(),
1244
+ tangentOut: px.tuple([px.number(), px.number()]).optional(),
1245
+ tangentIn: px.tuple([px.number(), px.number()]).optional()
1246
+ // (`selected` — editor timeline-selection UI state — was REMOVED from the wire
1247
+ // (review §1.3): editor data lives under `meta`. The editor still carries it on
1248
+ // its internal COPY-PASTE payload, which never validates against this schema.)
1249
+ }));
1250
+ var anyKf = (kf) => kf;
1251
+ var kfTime = (kf) => {
1252
+ var _a, _b;
1253
+ return (_b = (_a = anyKf(kf).time) != null ? _a : anyKf(kf).t) != null ? _b : 0;
1254
+ };
1255
+ var kfValue = (kf) => {
1256
+ var _a;
1257
+ return (_a = anyKf(kf).value) != null ? _a : anyKf(kf).v;
1258
+ };
1259
+ var kfEasing = (kf) => {
562
1260
  var _a;
563
- const v = (_a = kf.value) != null ? _a : kf.v;
1261
+ return (_a = anyKf(kf).easing) != null ? _a : anyKf(kf).e;
1262
+ };
1263
+ var kfTangentIn = (kf) => anyKf(kf).tangentIn;
1264
+ var kfTangentOut = (kf) => anyKf(kf).tangentOut;
1265
+ var PxLoopSchema = implementsInterface()(px.object({
1266
+ segmentCount: px.number().optional(),
1267
+ repeatAt: px.enum([PxLoopRepeatAt.start, PxLoopRepeatAt.end]).optional(),
1268
+ direction: px.enum([PxLoopDirection.normal, PxLoopDirection.alternate]).optional()
1269
+ }));
1270
+ var PxPropertyAnimationSchema = implementsInterface()(px.object({
1271
+ value: PxKeyframeValueSchema.optional(),
1272
+ keyframes: px.array(PxKeyframeSchema).optional(),
1273
+ loop: px.union([PxLoopSchema, px.boolean()]).optional(),
1274
+ autoOrient: px.boolean().optional(),
1275
+ alongPathMode: px.enum([PxAlongPathMode.sampled, PxAlongPathMode.offsetPath]).optional()
1276
+ }));
1277
+ var PxTransformPartsSchema = implementsInterface()(px.object({
1278
+ translate: px.tuple([px.number(), px.number()]).optional(),
1279
+ rotate: px.number().optional(),
1280
+ skew: px.number().optional(),
1281
+ scale: px.tuple([px.number(), px.number()]).optional(),
1282
+ origin: px.tuple([px.number(), px.number()]).optional()
1283
+ }));
1284
+ var PxTransformValueSchema = px.union([
1285
+ px.string(),
1286
+ PxTransformPartsSchema,
1287
+ px.object({ value: PxTransformPartsSchema }),
1288
+ PxPropertyAnimationSchema
1289
+ ]);
1290
+ var PxAnimationDefinitionSchema = implementsInterface()(
1291
+ px.record(PxPropertyAnimationSchema)
1292
+ );
1293
+ var PxElementAnimationSchema = implementsInterface()(px.union([
1294
+ px.string(),
1295
+ px.array(px.union([px.string(), PxAnimationDefinitionSchema])),
1296
+ PxAnimationDefinitionSchema
1297
+ ]));
1298
+ var PxTriggerSchema = implementsInterface()(px.object({
1299
+ startOn: px.enum([PxStartOn.load, PxStartOn.mouseOver, PxStartOn.click, PxStartOn.scrollIntoView, PxStartOn.programmatic], PX_TRIGGER_DEFAULTS.startOn).optional(),
1300
+ outAction: px.enum([PxOutAction.continue, PxOutAction.pause, PxOutAction.reset, PxOutAction.reverse], PX_TRIGGER_DEFAULTS.outAction).optional(),
1301
+ // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
1302
+ // `fill`) or `'reset'` (snap back to the start state). Pairs with `outAction` ("what
1303
+ // happens when the trigger condition ends"); both end-of-life knobs now read alike.
1304
+ finishAction: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
1305
+ scrollIntoViewThreshold: px.number().optional()
1306
+ }));
1307
+ var PxGlyphSchema = implementsInterface()(px.object({
1308
+ width: px.number(),
1309
+ pathData: px.string()
1310
+ }));
1311
+ var PxGlyphFontSchema = implementsInterface()(px.object({
1312
+ fontFamily: px.string(),
1313
+ fontStyle: px.string(),
1314
+ ascent: px.number(),
1315
+ unitsPerEm: px.number(),
1316
+ glyphs: px.record(PxGlyphSchema)
1317
+ }));
1318
+ var PxDefsSchema = implementsInterface()(px.object({
1319
+ easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
1320
+ animations: px.record(PxAnimationDefinitionSchema).optional(),
1321
+ // Review §2.6: the schema now matches the declared type — a style preset is a flat
1322
+ // record of string|number attribute values, nothing nested.
1323
+ styles: px.record(px.record(px.union([px.string(), px.number()]))).optional(),
1324
+ fonts: px.record(PxGlyphFontSchema).optional()
1325
+ }));
1326
+ var PxScrollRangePointSchema = implementsInterface()(px.object({
1327
+ phase: px.enum([
1328
+ PxScrollPhase.cover,
1329
+ PxScrollPhase.contain,
1330
+ PxScrollPhase.entry,
1331
+ PxScrollPhase.exit,
1332
+ PxScrollPhase.entryCrossing,
1333
+ PxScrollPhase.exitCrossing
1334
+ ]).optional(),
1335
+ fraction: px.number().optional()
1336
+ }));
1337
+ var PxScrollRangeSchema = px.object({
1338
+ start: PxScrollRangePointSchema.optional(),
1339
+ end: PxScrollRangePointSchema.optional()
1340
+ });
1341
+ var PxScrollSchema = implementsInterface()(px.object({
1342
+ kind: px.enum([PxScrollKind.view, PxScrollKind.scroll]).optional(),
1343
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
1344
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1345
+ // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.
1346
+ subject: px.string().optional(),
1347
+ smoothing: px.number().optional(),
1348
+ pin: px.boolean().optional(),
1349
+ pinAlign: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
1350
+ pinOffset: px.number().optional(),
1351
+ pinDistance: px.number().optional(),
1352
+ range: PxScrollRangeSchema.optional()
1353
+ }));
1354
+ var PxTimelinePinSchema = implementsInterface()(px.object({
1355
+ align: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
1356
+ offset: px.number().optional(),
1357
+ distance: px.number().optional()
1358
+ }));
1359
+ var PxTimelineEngineSchema = px.enum([PxTimelineEngineExtra.auto, PxTimelineEngineExtra.native, PxTimelineEngineExtra.js]).optional();
1360
+ var PxTimeTimelineSchema = implementsInterface()(px.object({
1361
+ type: px.literal("time").optional(),
1362
+ engine: PxTimelineEngineSchema,
1363
+ frameRate: px.number().optional(),
1364
+ // §2.8: duration is a property of the TIMELINE — how long one pass takes.
1365
+ duration: px.number().optional(),
1366
+ trigger: PxTriggerSchema.optional(),
1367
+ delay: px.number().optional(),
1368
+ iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1369
+ // `fillMode` on the wire (CSS `animation-fill-mode`; the runtime view calls it `fill`)
1370
+ // — never `fill`, which is paint everywhere else in the format.
1371
+ fillMode: px.enum([PxFillMode.forwards, PxFillMode.backwards, PxFillMode.both, PxFillMode.none]).optional(),
1372
+ direction: px.enum([PxPlaybackDirection.normal, PxPlaybackDirection.reverse, PxPlaybackDirection.alternate, PxPlaybackDirection.alternateReverse]).optional()
1373
+ }));
1374
+ var scrollishTimelineShape = {
1375
+ // §2.8: duration is a property of the TIMELINE — under scrubbing it is the keyframe
1376
+ // span the scroll range maps onto.
1377
+ duration: px.number().optional(),
1378
+ // Finite repeat count IS meaningful when scrubbing — the scroll range maps onto
1379
+ // duration × iterations (rule D4; `'infinite'` cannot map to a range, so no literal here).
1380
+ iterations: px.number().optional(),
1381
+ engine: PxTimelineEngineSchema,
1382
+ frameRate: px.number().optional(),
1383
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
1384
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1385
+ subject: px.string().optional(),
1386
+ // 'parent' | 'scroller' | any CSS selector
1387
+ smoothing: px.number().optional(),
1388
+ // ms
1389
+ pin: px.union([px.boolean(), PxTimelinePinSchema]).optional(),
1390
+ range: PxScrollRangeSchema.optional()
1391
+ };
1392
+ var PxScrollTimelineSchema = implementsInterface()(
1393
+ px.object(__spreadValues({ type: px.literal("scroll") }, scrollishTimelineShape))
1394
+ );
1395
+ var PxViewTimelineSchema = implementsInterface()(
1396
+ px.object(__spreadValues({ type: px.literal("view") }, scrollishTimelineShape))
1397
+ );
1398
+ var PxTimelineSchema = px.discriminatedUnion("type", [
1399
+ PxTimeTimelineSchema,
1400
+ // first = the member an absent `type` selects
1401
+ PxScrollTimelineSchema,
1402
+ PxViewTimelineSchema
1403
+ ]);
1404
+ var PxAnimatorConfigSchema = implementsInterface()(px.object({
1405
+ // (`mode`, `duration` and `frameRate` live INSIDE `timeline` on the wire — §2.8; they exist
1406
+ // at this level only on the runtime view, like the rest of the playback dynamics.)
1407
+ // THE spelling of "what advances progress" — clock / scroll / view (review §2.1).
1408
+ timeline: PxTimelineSchema.optional(),
1409
+ definitions: PxDefsSchema.optional(),
1410
+ animateById: px.record(PxElementAnimationSchema).optional(),
1411
+ debugGlobalName: px.string().optional(),
1412
+ // Declared HERE because this is a closed object: an undeclared key would be stripped by
1413
+ // `sanitize` and flagged by strict validation on our own files.
1414
+ version: px.string().optional()
1415
+ }));
1416
+ var PxBindingSchema = implementsInterface()(px.object({
1417
+ id: px.string(),
1418
+ animate: PxElementAnimationSchema
1419
+ }));
1420
+ var PxAttrValueSchema = px.union([
1421
+ px.string(),
1422
+ px.number(),
1423
+ px.array(px.number()),
1424
+ // Structured static — `{value: …}` (read-accepted transitional spelling, S1).
1425
+ // `defined`, not `any`: the KEY's presence is what identifies this branch (V6).
1426
+ px.object({ value: px.defined() }),
1427
+ // Bare transform parts record — the canonical static `transform` on the wire (T2).
1428
+ PxTransformPartsSchema
1429
+ ]);
1430
+ var PxAnimatableNumberSchema = px.union([
1431
+ px.number(),
1432
+ PxPropertyAnimationSchema,
1433
+ px.object({ value: px.number() })
1434
+ ]);
1435
+ var PxAnimatableVec2Schema = px.union([
1436
+ px.tuple([px.number(), px.number()]),
1437
+ PxPropertyAnimationSchema,
1438
+ px.object({ value: px.tuple([px.number(), px.number()]) })
1439
+ ]);
1440
+ var PxAnimatableStringSchema = px.union([
1441
+ px.string(),
1442
+ PxPropertyAnimationSchema,
1443
+ px.object({ value: px.string() })
1444
+ ]);
1445
+ var PxTransformByEffectSchema = implementsInterface()(px.object({
1446
+ translate: PxAnimatableVec2Schema.optional(),
1447
+ rotate: PxAnimatableNumberSchema.optional(),
1448
+ scale: PxAnimatableVec2Schema.optional(),
1449
+ skew: PxAnimatableNumberSchema.optional(),
1450
+ origin: PxAnimatableVec2Schema.optional()
1451
+ }));
1452
+ var PxRepeaterEffectSchema = implementsInterface()(px.object({
1453
+ // STATIC config, not a channel (V2/SCHEMA-DESIGN R5): the copy COUNT is read
1454
+ // once at expansion time and never sampled — plain number, no `keyframes`.
1455
+ copies: px.number().optional(),
1456
+ translate: PxAnimatableVec2Schema.optional(),
1457
+ rotate: PxAnimatableNumberSchema.optional(),
1458
+ skew: PxAnimatableNumberSchema.optional(),
1459
+ scale: PxAnimatableVec2Schema.optional(),
1460
+ origin: PxAnimatableVec2Schema.optional()
1461
+ }));
1462
+ var PxMaskedByEffectSchema = implementsInterface()(px.object({
1463
+ source: px.string().optional(),
1464
+ maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha]).optional(),
1465
+ maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1466
+ maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1467
+ x: px.number().optional(),
1468
+ y: px.number().optional(),
1469
+ width: px.number().optional(),
1470
+ height: px.number().optional()
1471
+ }));
1472
+ var PxClipPathEffectSchema = implementsInterface()(px.object({
1473
+ pathData: PxAnimatableStringSchema.optional()
1474
+ }));
1475
+ var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1476
+ offset: PxAnimatableNumberSchema.optional(),
1477
+ range: PxAnimatableVec2Schema.optional(),
1478
+ subPaths: px.enum([PxStrokeTrimSubPaths.separate, PxStrokeTrimSubPaths.combined]).optional()
1479
+ }));
1480
+ var PxRetimeEffectSchema = implementsInterface()(px.object({
1481
+ start: px.number().optional(),
1482
+ stretch: px.number().optional(),
1483
+ timeCrop: px.tuple([px.number(), px.number()]).optional()
1484
+ }));
1485
+ var PxCloneEffectSchema = implementsInterface()(px.object({
1486
+ // Subtractive on purpose: the `<use>` can only point at one wrapper layer of the
1487
+ // source, so the choices form a ladder — 'translate' now, maybe 'transform' later.
1488
+ without: px.enum([PxCloneWithout.translate]).optional(),
1489
+ source: px.string().optional(),
1490
+ retime: PxRetimeEffectSchema.optional()
1491
+ }));
1492
+ var PxGradientStopSchema = implementsInterface()(px.object({
1493
+ offset: px.number(),
1494
+ color: px.string()
1495
+ }));
1496
+ var PxAnimatableGradientStopsSchema = px.union([
1497
+ px.array(PxGradientStopSchema),
1498
+ px.object({ value: px.array(PxGradientStopSchema) }),
1499
+ PxPropertyAnimationSchema
1500
+ ]);
1501
+ var PxFillGradientEffectSchema = implementsInterface()(px.object({
1502
+ // Contextual kind — the `type` convention, see `PxNodeBase.type`.
1503
+ type: px.enum([PxGradientType.linear, PxGradientType.radial]),
1504
+ start: PxAnimatableVec2Schema.optional(),
1505
+ end: PxAnimatableVec2Schema.optional(),
1506
+ center: PxAnimatableVec2Schema.optional(),
1507
+ radius: PxAnimatableNumberSchema.optional(),
1508
+ focal: PxAnimatableVec2Schema.optional(),
1509
+ stops: PxAnimatableGradientStopsSchema.optional(),
1510
+ gradientUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1511
+ spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
1512
+ gradientTransform: px.string().optional()
1513
+ }));
1514
+ var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
1515
+ var PxTextPathEffectSchema = implementsInterface()(px.object({
1516
+ pathData: px.string(),
1517
+ pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
1518
+ lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
1519
+ method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
1520
+ spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
1521
+ startOffset: PxAnimatableNumberSchema.optional(),
1522
+ textLength: PxAnimatableNumberSchema.optional()
1523
+ }));
1524
+ var PxTextEffectSchema = implementsInterface()(px.object({
1525
+ useGlyphs: px.boolean().optional()
1526
+ }));
1527
+ var PxEffectsSchema = implementsInterface()(px.object({
1528
+ transformBy: PxTransformByEffectSchema.optional(),
1529
+ repeater: PxRepeaterEffectSchema.optional(),
1530
+ maskedBy: PxMaskedByEffectSchema.optional(),
1531
+ clipPath: PxClipPathEffectSchema.optional(),
1532
+ strokeTrim: PxStrokeTrimEffectSchema.optional(),
1533
+ clone: PxCloneEffectSchema.optional(),
1534
+ fillGradient: PxFillGradientEffectSchema.optional(),
1535
+ strokeGradient: PxStrokeGradientEffectSchema.optional(),
1536
+ textPath: PxTextPathEffectSchema.optional(),
1537
+ text: PxTextEffectSchema.optional()
1538
+ }));
1539
+ var PxNodeBase = px.openObject({
1540
+ // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
1541
+ // kind of thing is this", discriminated by its CARRIER — here the node TAG
1542
+ // (`rect`, `text`), and inside a sub-object that object's kind (`fillGradient.type`,
1543
+ // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
1544
+ // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
1545
+ // would add words that all mean "type" and still need the carrier to read.
1546
+ // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
1547
+ // (issues V3), never of distinct key names.
1548
+ type: px.string(),
1549
+ // The escape hatch for elements that carry a REAL `type` attribute — `<feTurbulence
1550
+ // type="fractalNoise">`, `<feFuncR type="table">`, `<feColorMatrix type="saturate">`.
1551
+ // `type` is taken by the tag name, so the attribute travels here and the renderer puts
1552
+ // it back (`PxAnimatorDOM.renderNode`, `PxRnRender`). Declared here — not merely
1553
+ // documented — because a wire key that is not in a schema is invisible to the
1554
+ // minifier's reserve list and gets renamed (MINIFICATION-BOUNDARY-PLAN.md §1.1).
1555
+ domType: px.string().optional(),
1556
+ // Text content of a `<text>` / `<tspan>`. Declared, so a non-string value is a schema error
1557
+ // and the minifier reserves the key; `text` is NOT an alias for it and is not read anywhere.
1558
+ textContent: px.string().optional(),
1559
+ id: px.string().optional(),
1560
+ meta: px.any().optional(),
1561
+ // Player-effects bucket emitted by the Editor's lightweight design format.
1562
+ // Consumed and removed by `applyPlayerEffects` before any other normalization
1563
+ // (see `createAnimatorImpl`), so downstream code never sees it.
1564
+ effects: PxEffectsSchema.optional(),
1565
+ // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
1566
+ // string ref / array of refs / inline definition / mixed array; mirrors
1567
+ // `animator.animateById` map values and what `processNode` resolves at runtime.
1568
+ animate: PxElementAnimationSchema.optional(),
1569
+ style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
1570
+ }, PxAttrValueSchema);
1571
+ var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
1572
+ children: px.lazy(() => px.array(PxNodeSchema), []).optional()
1573
+ }), PxAttrValueSchema);
1574
+ var PxSvgNodeExtra = px.object({
1575
+ // `"100%"` and other SVG length strings are legal here — a number-only slot rejected
1576
+ // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.
1577
+ width: px.union([px.number(), px.string()]).optional(),
1578
+ height: px.union([px.number(), px.string()]).optional(),
1579
+ viewBox: px.string().optional(),
1580
+ animator: PxAnimatorConfigSchema.optional()
1581
+ });
1582
+ var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
1583
+ type: px.literal("svg"),
1584
+ // override string → literal to require 'svg'
1585
+ children: px.array(PxNodeSchema).optional()
1586
+ }), PxAttrValueSchema);
1587
+ var PxBezierPathSchema = implementsInterface()(px.object({
1588
+ v: px.array(px.array(px.number())),
1589
+ i: px.array(px.array(px.number())).optional(),
1590
+ o: px.array(px.array(px.number())).optional(),
1591
+ c: px.boolean().optional()
1592
+ }));
1593
+
1594
+ // ../svg-animator-core/src/materialize/PxMotionPath.ts
1595
+ function getKfTranslate(kf) {
1596
+ const v = kfValue(kf);
564
1597
  if (!v) return void 0;
565
1598
  if (Array.isArray(v) && v.length >= 2 && typeof v[0] === "number" && typeof v[1] === "number") {
566
1599
  return [v[0], v[1]];
@@ -570,31 +1603,27 @@ var PixodeskAnimator = (() => {
570
1603
  return void 0;
571
1604
  }
572
1605
  function getKfTime(kf) {
573
- var _a, _b;
574
- return (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
1606
+ return kfTime(kf);
575
1607
  }
576
1608
  function getKfEasing(kf) {
577
- var _a;
578
- return (_a = kf.easing) != null ? _a : kf.e;
1609
+ return kfEasing(kf);
579
1610
  }
580
1611
  function propAnimIsMotionPath(anim) {
581
- var _a, _b;
582
1612
  const kfs = anim.keyframes;
583
1613
  if (!Array.isArray(kfs)) return false;
584
1614
  if (anim.autoOrient) return true;
585
1615
  for (const kf of kfs) {
586
- if (((_a = kf.tangentIn) != null ? _a : kf.ti) || ((_b = kf.tangentOut) != null ? _b : kf.to)) return true;
1616
+ if (kfTangentIn(kf) || kfTangentOut(kf)) return true;
587
1617
  }
588
1618
  return false;
589
1619
  }
590
1620
  var _segmentCache = /* @__PURE__ */ new WeakMap();
591
1621
  function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
592
- var _a, _b;
593
1622
  let byNext = _segmentCache.get(prevKf);
594
1623
  const existing = byNext == null ? void 0 : byNext.get(nextKf);
595
1624
  if (existing) return existing;
596
- const to = (_a = prevKf.tangentOut) != null ? _a : prevKf.to;
597
- const ti = (_b = nextKf.tangentIn) != null ? _b : nextKf.ti;
1625
+ const to = kfTangentOut(prevKf);
1626
+ const ti = kfTangentIn(nextKf);
598
1627
  const P1 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];
599
1628
  const P2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];
600
1629
  const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);
@@ -616,7 +1645,7 @@ var PixodeskAnimator = (() => {
616
1645
  var DEFAULT_FLATNESS_TOL = 0.5;
617
1646
  var DEFAULT_ROTATION_TOL = 5;
618
1647
  var DEFAULT_MAX_SAMPLES = 32;
619
- function materialiseMotionPathInPropAnim(anim, opts) {
1648
+ function materializeMotionPathInPropAnim(anim, opts) {
620
1649
  var _a, _b, _c;
621
1650
  if (!propAnimIsMotionPath(anim)) return anim;
622
1651
  const kfs = anim.keyframes;
@@ -648,7 +1677,7 @@ var PixodeskAnimator = (() => {
648
1677
  if (autoOrient && i > 0) {
649
1678
  insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol);
650
1679
  }
651
- materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
1680
+ materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
652
1681
  }
653
1682
  const lastInE = getKfEasing(kfs[kfs.length - 1]);
654
1683
  if (lastInE) out[out.length - 1].e = lastInE;
@@ -658,10 +1687,9 @@ var PixodeskAnimator = (() => {
658
1687
  return result;
659
1688
  }
660
1689
  function unwrapAutoOrientRotations(kfs) {
661
- var _a;
662
1690
  let prev;
663
1691
  for (const kf of kfs) {
664
- const v = (_a = kf.v) != null ? _a : kf.value;
1692
+ const v = kfValue(kf);
665
1693
  if (!v || typeof v.rotate !== "number") continue;
666
1694
  if (prev === void 0) {
667
1695
  prev = v.rotate;
@@ -678,8 +1706,7 @@ var PixodeskAnimator = (() => {
678
1706
  return { t: time, v: value };
679
1707
  }
680
1708
  function getKfValueParts(kf) {
681
- var _a;
682
- const v = (_a = kf.value) != null ? _a : kf.v;
1709
+ const v = kfValue(kf);
683
1710
  if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
684
1711
  return v;
685
1712
  }
@@ -740,9 +1767,8 @@ var PixodeskAnimator = (() => {
740
1767
  return d;
741
1768
  }
742
1769
  function insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol) {
743
- var _a;
744
1770
  const lastKf = out[out.length - 1];
745
- const lastV = (_a = lastKf.v) != null ? _a : lastKf.value;
1771
+ const lastV = kfValue(lastKf);
746
1772
  const prevExit = lastV == null ? void 0 : lastV.rotate;
747
1773
  if (typeof prevExit !== "number") return;
748
1774
  const boundaryV = getKfValueParts(prevKf);
@@ -765,7 +1791,7 @@ var PixodeskAnimator = (() => {
765
1791
  out.push(makeOutKf(stepTime, dupValue));
766
1792
  }
767
1793
  var CORNER_STEP_AFTER_BOUNDARY_MS = 0.05;
768
- function materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
1794
+ function materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
769
1795
  const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
770
1796
  const prevTime = getKfTime(prevKf);
771
1797
  const nextTime = getKfTime(nextKf);
@@ -889,7 +1915,7 @@ var PixodeskAnimator = (() => {
889
1915
  return Math.abs(cross) / Math.sqrt(len2);
890
1916
  }
891
1917
 
892
- // ../svg-animator-core/src/PxDefinitions.ts
1918
+ // ../svg-animator-core/src/animation/PxDefinitions.ts
893
1919
  var LOOP_JUMP_SHIFT_MS = 1;
894
1920
  function deepEqualValue(a, b) {
895
1921
  if (a === b) return true;
@@ -981,8 +2007,8 @@ var PixodeskAnimator = (() => {
981
2007
  return typeof value === "string" && extractPathData(value) !== void 0;
982
2008
  }
983
2009
  function normalizePathValue(value) {
984
- if (value && typeof value === "object" && typeof value.path === "string") {
985
- const d = extractPathData(value.path);
2010
+ if (value && typeof value === "object" && typeof value.pathData === "string") {
2011
+ const d = extractPathData(value.pathData);
986
2012
  return d ? { paths: parseSvgPathToBezier(d) } : value;
987
2013
  }
988
2014
  if (value && typeof value === "object" && "paths" in value) {
@@ -990,8 +2016,8 @@ var PixodeskAnimator = (() => {
990
2016
  if (Array.isArray(pathsArray) && pathsArray.length > 0) {
991
2017
  if (isPathString(pathsArray[0])) {
992
2018
  const paths = [];
993
- for (const pathStr of pathsArray) {
994
- const d = extractPathData(pathStr);
2019
+ for (const pathStr2 of pathsArray) {
2020
+ const d = extractPathData(pathStr2);
995
2021
  if (d) {
996
2022
  paths.push(...parseSvgPathToBezier(d));
997
2023
  }
@@ -1004,8 +2030,8 @@ var PixodeskAnimator = (() => {
1004
2030
  if (Array.isArray(value)) {
1005
2031
  if (value.length > 0 && isPathString(value[0])) {
1006
2032
  const paths = [];
1007
- for (const pathStr of value) {
1008
- const d = extractPathData(pathStr);
2033
+ for (const pathStr2 of value) {
2034
+ const d = extractPathData(pathStr2);
1009
2035
  if (d) {
1010
2036
  paths.push(...parseSvgPathToBezier(d));
1011
2037
  }
@@ -1066,7 +2092,7 @@ var PixodeskAnimator = (() => {
1066
2092
  const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
1067
2093
  return { paths: interpolateBeziers(aPaths, bPaths, t) };
1068
2094
  }
1069
- if (COLOUR_ATTR_NAMES.has(propName)) {
2095
+ if (COLOR_ATTR_NAMES.has(propName)) {
1070
2096
  return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
1071
2097
  }
1072
2098
  if (propName === "transform" && typeof a === "object" && a !== null && !Array.isArray(a) && typeof b === "object" && b !== null && !Array.isArray(b)) {
@@ -1102,7 +2128,7 @@ var PixodeskAnimator = (() => {
1102
2128
  const totalIntervals = keyframes.length - 1;
1103
2129
  const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
1104
2130
  let segKfs;
1105
- if (loop.extend === PxLoopExtend.before) {
2131
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
1106
2132
  segKfs = keyframes.slice(0, segCount + 1);
1107
2133
  } else {
1108
2134
  segKfs = keyframes.slice(totalIntervals - segCount);
@@ -1110,7 +2136,7 @@ var PixodeskAnimator = (() => {
1110
2136
  const firstT = (_b = keyframes[0].t) != null ? _b : 0;
1111
2137
  const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
1112
2138
  let fillStart, fillEnd;
1113
- if (loop.extend === PxLoopExtend.before) {
2139
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
1114
2140
  fillStart = 0;
1115
2141
  fillEnd = firstT;
1116
2142
  } else {
@@ -1123,21 +2149,18 @@ var PixodeskAnimator = (() => {
1123
2149
  const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
1124
2150
  const segDuration = segEndT - segStartT;
1125
2151
  if (segDuration <= 0) return keyframes;
1126
- const template = segKfs.map((kf) => {
1127
- var _a2, _b2;
1128
- return {
1129
- relT: (kf.t - segStartT) / segDuration,
1130
- v: kf.v,
1131
- e: kf.e,
1132
- tangentIn: (_a2 = kf.tangentIn) != null ? _a2 : kf.ti,
1133
- tangentOut: (_b2 = kf.tangentOut) != null ? _b2 : kf.to
1134
- };
1135
- });
2152
+ const template = segKfs.map((kf) => ({
2153
+ relT: (kf.t - segStartT) / segDuration,
2154
+ v: kf.v,
2155
+ e: kf.e,
2156
+ tangentIn: kfTangentIn(kf),
2157
+ tangentOut: kfTangentOut(kf)
2158
+ }));
1136
2159
  const fullReps = Math.floor(fillDuration / segDuration);
1137
2160
  const remainder = fillDuration - fullReps * segDuration;
1138
2161
  const partialFraction = remainder / segDuration;
1139
2162
  const looped = [];
1140
- const separateBoundary = loop.extend !== PxLoopExtend.before;
2163
+ const separateBoundary = loop.repeatAt !== PxLoopRepeatAt.start;
1141
2164
  const originalTerminalKf = keyframes[keyframes.length - 1];
1142
2165
  let terminalEasingOverride;
1143
2166
  let hasTerminalEasingOverride = false;
@@ -1245,30 +2268,30 @@ var PixodeskAnimator = (() => {
1245
2268
  looped.push(pushed);
1246
2269
  }
1247
2270
  }
1248
- if (loop.extend === PxLoopExtend.before) {
2271
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
1249
2272
  if (partialFraction > 1e-9) {
1250
- const isReversed = !!loop.alternate && fullReps % 2 === 0;
2273
+ const isReversed = loop.direction === PxLoopDirection.alternate && fullReps % 2 === 0;
1251
2274
  appendRepTail(fillStart, isReversed, partialFraction);
1252
2275
  }
1253
2276
  for (let rep = 0; rep < fullReps; rep++) {
1254
2277
  const distFromBoundary = fullReps - 1 - rep;
1255
- const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
2278
+ const isReversed = loop.direction === PxLoopDirection.alternate && distFromBoundary % 2 === 0;
1256
2279
  const repStart = fillStart + remainder + rep * segDuration;
1257
2280
  appendRep(repStart, isReversed);
1258
2281
  }
1259
2282
  } else {
1260
2283
  for (let rep = 0; rep < fullReps; rep++) {
1261
- const isReversed = !!loop.alternate && rep % 2 === 0;
2284
+ const isReversed = loop.direction === PxLoopDirection.alternate && rep % 2 === 0;
1262
2285
  const repStart = fillStart + rep * segDuration;
1263
2286
  appendRep(repStart, isReversed);
1264
2287
  }
1265
2288
  if (partialFraction > 1e-9) {
1266
- const isReversed = !!loop.alternate && fullReps % 2 === 0;
2289
+ const isReversed = loop.direction === PxLoopDirection.alternate && fullReps % 2 === 0;
1267
2290
  const repStart = fillStart + fullReps * segDuration;
1268
2291
  appendRep(repStart, isReversed, partialFraction);
1269
2292
  }
1270
2293
  }
1271
- if (loop.extend === PxLoopExtend.before) {
2294
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
1272
2295
  return [...looped, ...keyframes];
1273
2296
  } else {
1274
2297
  if (hasTerminalEasingOverride && keyframes.length > 0) {
@@ -1280,34 +2303,34 @@ var PixodeskAnimator = (() => {
1280
2303
  }
1281
2304
  }
1282
2305
  function normalizeKeyframes(propName, propAnim, duration, defs) {
1283
- var _a, _b, _c, _d, _e, _f, _g;
2306
+ var _a;
1284
2307
  const keyframes = propAnim.keyframes || [];
1285
2308
  const normalized = [];
1286
2309
  for (const kf of keyframes) {
1287
- const timePct = (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
1288
- let value = (_c = kf.value) != null ? _c : kf.v;
1289
- const easing = (_d = kf.easing) != null ? _d : kf.e;
2310
+ const timePct = kfTime(kf);
2311
+ let value = kfValue(kf);
2312
+ const easing = kfEasing(kf);
1290
2313
  if (propName === "d") {
1291
2314
  value = normalizePathValue(value);
1292
2315
  }
1293
2316
  const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
1294
- if (COLOUR_ATTR_NAMES.has(propNameKebab)) {
1295
- value = (_e = parseColor(value)) != null ? _e : value;
2317
+ if (COLOR_ATTR_NAMES.has(propNameKebab)) {
2318
+ value = (_a = parseColor(value)) != null ? _a : value;
1296
2319
  }
1297
2320
  const normKf = {
1298
2321
  t: timePct,
1299
2322
  v: value,
1300
2323
  e: resolveEasing(easing, defs)
1301
2324
  };
1302
- const tIn = (_f = kf.tangentIn) != null ? _f : kf.ti;
1303
- const tOut = (_g = kf.tangentOut) != null ? _g : kf.to;
2325
+ const tIn = kfTangentIn(kf);
2326
+ const tOut = kfTangentOut(kf);
1304
2327
  if (tIn) normKf.tangentIn = tIn;
1305
2328
  if (tOut) normKf.tangentOut = tOut;
1306
2329
  normalized.push(normKf);
1307
2330
  }
1308
2331
  normalized.sort((a, b) => {
1309
- var _a2, _b2;
1310
- return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
2332
+ var _a2, _b;
2333
+ return ((_a2 = a.t) != null ? _a2 : 0) - ((_b = b.t) != null ? _b : 0);
1311
2334
  });
1312
2335
  const loopRaw = propAnim.loop;
1313
2336
  const loop = loopRaw === true ? {} : loopRaw || void 0;
@@ -1334,7 +2357,7 @@ var PixodeskAnimator = (() => {
1334
2357
  const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
1335
2358
  if (!staticParts || !Object.keys(staticParts).length) return animDef;
1336
2359
  const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues(__spreadValues({}, staticParts), v) : v;
1337
- const transformAnim = animDef["transform"];
2360
+ const transformAnim = animDef[TRANSFORM_ATTR];
1338
2361
  if (transformAnim && typeof transformAnim === "object") {
1339
2362
  const anim = transformAnim;
1340
2363
  if (Array.isArray(anim.keyframes)) {
@@ -1359,7 +2382,7 @@ var PixodeskAnimator = (() => {
1359
2382
  delete rest[ch];
1360
2383
  return __spreadProps(__spreadValues({}, rest), { transform: lifted });
1361
2384
  }
1362
- function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.waapi) {
2385
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxTimelineEngine.native) {
1363
2386
  const normalized = {};
1364
2387
  for (const [propName, propAnim] of Object.entries(animDef)) {
1365
2388
  if (propName === "transform" && propAnim.alongPathMode === "offsetPath" && animDef["offsetDistance"] !== void 0) {
@@ -1370,12 +2393,12 @@ var PixodeskAnimator = (() => {
1370
2393
  const out = { keyframes: normalizedKfs };
1371
2394
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
1372
2395
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
1373
- normalized[propName] = engine === PxAnimatorEngine.waapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2396
+ normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materializeMotionPathInPropAnim(out) : out;
1374
2397
  }
1375
2398
  }
1376
2399
  return normalized;
1377
2400
  }
1378
- function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2401
+ function getNormalizedBindings(doc, engine = PxTimelineEngine.native) {
1379
2402
  const animatorConfig = getAnimatorConfig(doc) || {};
1380
2403
  const defs = getDefs(doc);
1381
2404
  const duration = animatorConfig.duration || 1e3;
@@ -1421,13 +2444,116 @@ var PixodeskAnimator = (() => {
1421
2444
  return bindings;
1422
2445
  }
1423
2446
 
1424
- // src/PxAnimatorTriggers.ts
1425
- function setupAnimationTriggers(api, config) {
1426
- const { startOn, outAction = "continue", scrollIntoViewThreshold = 0 } = config;
2447
+ // ../svg-animator-core/src/playback/PxPlaybackTime.ts
2448
+ var PX_RATE_REJECTED = "setPlaybackRate: rate must be finite and non-zero";
2449
+ function isValidPlaybackRate(rate) {
2450
+ return Number.isFinite(rate) && rate !== 0;
2451
+ }
2452
+ function seekCeilingMs(durationMs, iterations) {
2453
+ if (!(durationMs > 0)) return 0;
2454
+ if (iterations === Infinity) return Infinity;
2455
+ return durationMs * (iterations > 0 ? iterations : 1);
2456
+ }
2457
+ function progressSpanMs(durationMs, iterations) {
2458
+ if (!(durationMs > 0)) return 0;
2459
+ if (iterations === Infinity) return durationMs;
2460
+ return durationMs * (iterations > 0 ? iterations : 1);
2461
+ }
2462
+ function clampSeekMs(timeMs, ceilingMs) {
2463
+ if (Number.isNaN(timeMs) || timeMs < 0) return 0;
2464
+ if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;
2465
+ return Number.isFinite(timeMs) ? timeMs : 0;
2466
+ }
2467
+ function timeToProgress(timeMs, durationMs, iterations) {
2468
+ const span = progressSpanMs(durationMs, iterations);
2469
+ if (!(span > 0) || !Number.isFinite(timeMs)) return 0;
2470
+ if (timeMs <= 0) return 0;
2471
+ if (iterations === Infinity) return timeMs % span / span;
2472
+ return timeMs >= span ? 1 : timeMs / span;
2473
+ }
2474
+ function progressToTimeMs(progress, durationMs, iterations) {
2475
+ const span = progressSpanMs(durationMs, iterations);
2476
+ if (!(span > 0) || !Number.isFinite(progress)) return 0;
2477
+ if (progress <= 0) return 0;
2478
+ return progress >= 1 ? span : progress * span;
2479
+ }
2480
+
2481
+ // ../svg-animator-core/src/playback/PxDiagnostics.ts
2482
+ var PxDiagnosticKind = {
2483
+ /** The document is wrong — regenerate or repair the file. */
2484
+ document: "document",
2485
+ /** The page or app cannot provide what the document asks for — fix the mount. */
2486
+ host: "host",
2487
+ /** The platform cannot do it and the player degraded — usually nothing to fix. */
2488
+ platform: "platform",
2489
+ /** The call is wrong or self-contradictory — fix the options or props you passed. */
2490
+ usage: "usage",
2491
+ /** The player failed where it did not expect to — report it to us. */
2492
+ internal: "internal"
2493
+ };
2494
+ function asError(error) {
2495
+ return typeof error === "string" ? new Error(error) : error;
2496
+ }
2497
+ function isSilenced(silent, kind) {
2498
+ if (!silent) return false;
2499
+ return silent === true || silent.includes(kind);
2500
+ }
2501
+ function createDiagnostics(config, prefix) {
2502
+ const tag = prefix ? prefix + " " : "";
2503
+ return {
2504
+ warn: (kind, message, detail) => {
2505
+ if (config == null ? void 0 : config.onWarn) {
2506
+ config.onWarn({ kind, message, detail });
2507
+ return;
2508
+ }
2509
+ if (isSilenced(config == null ? void 0 : config.silent, kind)) return;
2510
+ const line = tag + kind + ": " + message;
2511
+ if (detail === void 0) console.warn(line);
2512
+ else console.warn(line, detail);
2513
+ },
2514
+ error: (kind, error) => {
2515
+ const err = asError(error);
2516
+ if (config == null ? void 0 : config.onError) {
2517
+ config.onError({ kind, message: err.message, error: err });
2518
+ return;
2519
+ }
2520
+ if (isSilenced(config == null ? void 0 : config.silent, kind)) return;
2521
+ console.error(tag + kind + ": " + err.message);
2522
+ }
2523
+ };
2524
+ }
2525
+
2526
+ // src/shared/PxAnimatorCallbacks.ts
2527
+ function toEngineCallbacks(inline) {
2528
+ const { onPlay, onPause, onCancel, onFinish, onRemove, onStop, onWarn, onError, silent } = inline != null ? inline : {};
2529
+ const withStop = (own) => own || onStop ? () => {
2530
+ own == null ? void 0 : own();
2531
+ onStop == null ? void 0 : onStop();
2532
+ } : void 0;
2533
+ return {
2534
+ onPlay,
2535
+ onPause: withStop(onPause),
2536
+ onCancel: withStop(onCancel),
2537
+ onFinish: withStop(onFinish),
2538
+ onRemove: withStop(onRemove),
2539
+ onWarn,
2540
+ onError,
2541
+ silent
2542
+ };
2543
+ }
2544
+
2545
+ // src/triggers/PxAnimatorTriggers.ts
2546
+ function setupAnimationTriggers(api, config, diag) {
2547
+ const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
2548
+ const cleanups = [];
2549
+ const dispose = () => {
2550
+ for (const undo of cleanups.splice(0)) undo();
2551
+ };
2552
+ const { startOn, outAction, scrollIntoViewThreshold } = resolveTrigger(config);
1427
2553
  const root = api.getRootElement();
1428
2554
  if (!root) {
1429
- console.warn("setupAnimationTriggers: No root element found for animation.");
1430
- return api;
2555
+ report.warn(PxDiagnosticKind.host, "setupAnimationTriggers: No root element found for animation.");
2556
+ return dispose;
1431
2557
  }
1432
2558
  let reversed = false;
1433
2559
  const start = () => {
@@ -1462,6 +2588,7 @@ var PixodeskAnimator = (() => {
1462
2588
  startHandler();
1463
2589
  } else {
1464
2590
  window.addEventListener("load", startHandler, { once: true });
2591
+ cleanups.push(() => window.removeEventListener("load", startHandler));
1465
2592
  }
1466
2593
  break;
1467
2594
  }
@@ -1476,6 +2603,10 @@ var PixodeskAnimator = (() => {
1476
2603
  };
1477
2604
  root.addEventListener("mouseenter", mouseOverHandler);
1478
2605
  root.addEventListener("mouseleave", mouseOutHandler);
2606
+ cleanups.push(() => {
2607
+ root.removeEventListener("mouseenter", mouseOverHandler);
2608
+ root.removeEventListener("mouseleave", mouseOutHandler);
2609
+ });
1479
2610
  break;
1480
2611
  }
1481
2612
  case "click": {
@@ -1487,6 +2618,7 @@ var PixodeskAnimator = (() => {
1487
2618
  }
1488
2619
  };
1489
2620
  root.addEventListener("click", clickHandler);
2621
+ cleanups.push(() => root.removeEventListener("click", clickHandler));
1490
2622
  break;
1491
2623
  }
1492
2624
  case "scrollIntoView": {
@@ -1518,31 +2650,31 @@ var PixodeskAnimator = (() => {
1518
2650
  { threshold: thresholdSteps }
1519
2651
  );
1520
2652
  observer.observe(root);
2653
+ cleanups.push(() => observer.disconnect());
1521
2654
  break;
1522
2655
  }
1523
2656
  case "programmatic":
1524
2657
  break;
1525
2658
  }
1526
- return api;
2659
+ return dispose;
1527
2660
  }
1528
2661
 
1529
- // src/PxAnimatorFrameLoop.ts
2662
+ // src/engines/PxAnimatorFrameLoop.ts
1530
2663
  function getSelector(id) {
1531
2664
  return "#" + id;
1532
2665
  }
1533
2666
 
1534
- // src/PxAnimatorWebApi.ts
2667
+ // src/engines/PxAnimatorWebApi.ts
1535
2668
  function createCssKf(kf, t, propName, unsupportedSet) {
1536
- var _a, _b;
1537
- let value = (_a = kf.v) != null ? _a : kf.value;
1538
- const e = (_b = kf.e) != null ? _b : kf.easing;
2669
+ let value = kfValue(kf);
2670
+ const e = kfEasing(kf);
1539
2671
  const cssKf = {
1540
2672
  offset: t,
1541
2673
  easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
1542
2674
  };
1543
2675
  let cssValue;
1544
2676
  let cssKey = propName;
1545
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
2677
+ if (COLOR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
1546
2678
  cssValue = toRGBA(value);
1547
2679
  } else if (propName === "transform" && value !== null && typeof value === "object" && !Array.isArray(value)) {
1548
2680
  cssValue = composeTransformParts(value, { withUnits: true });
@@ -1629,18 +2761,19 @@ var PixodeskAnimator = (() => {
1629
2761
  return result;
1630
2762
  }
1631
2763
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs, scrollTimeline) {
1632
- var _a;
2764
+ var _a, _b;
1633
2765
  const config = getAnimatorConfig(doc) || {};
2766
+ const diag = createDiagnostics(callbacks, "[PxAnimator]");
1634
2767
  if (!rootElement) {
1635
2768
  if (doc.id) {
1636
2769
  const rootSelector = getSelector(doc.id);
1637
2770
  rootElement = document.querySelector(rootSelector);
1638
- if (!rootElement) console.warn("createFrameLoopAnimator: No root element found for selector: ", rootSelector);
2771
+ if (!rootElement) diag.warn(PxDiagnosticKind.host, "createWebApiAnimator: No root element found for selector: " + rootSelector);
1639
2772
  } else {
1640
- console.warn("createFrameLoopAnimator: No root element provided");
2773
+ diag.warn(PxDiagnosticKind.host, "createWebApiAnimator: No root element provided");
1641
2774
  }
1642
2775
  }
1643
- const bindings = getNormalisedBindings(doc, PxAnimatorEngine.waapi);
2776
+ const bindings = getNormalizedBindings(doc, PxTimelineEngine.native);
1644
2777
  const animations = [];
1645
2778
  const _iterations = config.iterations;
1646
2779
  let iterations;
@@ -1650,18 +2783,18 @@ var PixodeskAnimator = (() => {
1650
2783
  let finishNotified = false;
1651
2784
  let removeCalled = false;
1652
2785
  if (!(bindings == null ? void 0 : bindings.length)) {
1653
- console.warn("createWebApiAnimator: No animation bindings defined");
2786
+ diag.warn(PxDiagnosticKind.document, "createWebApiAnimator: No animation bindings defined");
1654
2787
  }
1655
2788
  for (const binding of bindings || []) {
1656
2789
  const animDef = binding.animate;
1657
2790
  if (!animDef || typeof animDef !== "object" || Array.isArray(animDef)) {
1658
- console.warn("createWebApiAnimator: Empty or unresolved binding", binding);
2791
+ diag.warn(PxDiagnosticKind.document, "createWebApiAnimator: Empty or unresolved binding", binding);
1659
2792
  continue;
1660
2793
  }
1661
2794
  const selector = getSelector(binding.id);
1662
2795
  const elements = (rootElement == null ? void 0 : rootElement.querySelectorAll(selector)) || document.querySelectorAll(selector);
1663
2796
  if (elements.length === 0) {
1664
- console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
2797
+ diag.warn(PxDiagnosticKind.host, 'createWebApiAnimator: No elements found for selector "' + selector + '"');
1665
2798
  }
1666
2799
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
1667
2800
  const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
@@ -1710,14 +2843,14 @@ var PixodeskAnimator = (() => {
1710
2843
  }
1711
2844
  animations.push(anim);
1712
2845
  } catch (e) {
1713
- console.warn(e);
2846
+ diag.warn(PxDiagnosticKind.internal, "createWebApiAnimator: could not build the animation", e);
1714
2847
  }
1715
2848
  }
1716
2849
  }
1717
2850
  }
1718
2851
  }
1719
2852
  if (!forceEvenIfHasUnsupportedAttrs && unsupportedSet.size) {
1720
- console.warn("Unsupported CSS attrs: " + [...unsupportedSet].join(", "));
2853
+ diag.warn(PxDiagnosticKind.platform, "Unsupported CSS attrs: " + [...unsupportedSet].join(", "));
1721
2854
  return null;
1722
2855
  }
1723
2856
  const api = {
@@ -1761,20 +2894,36 @@ var PixodeskAnimator = (() => {
1761
2894
  }
1762
2895
  },
1763
2896
  "setPlaybackRate": (rate) => {
2897
+ if (!isValidPlaybackRate(rate)) {
2898
+ diag.warn(PxDiagnosticKind.usage, PX_RATE_REJECTED);
2899
+ return api;
2900
+ }
1764
2901
  animations.forEach((a) => a.playbackRate = rate);
1765
2902
  return api;
1766
2903
  },
1767
2904
  "getCurrentTime": () => {
1768
- var _a2, _b;
1769
- const res = (_b = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b : null;
2905
+ var _a2, _b2;
2906
+ const res = (_b2 = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b2 : null;
1770
2907
  return res !== null ? +res : null;
1771
2908
  },
1772
2909
  "setCurrentTime": (time) => {
2910
+ var _a2;
2911
+ const ceiling = seekCeilingMs((_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1);
2912
+ const seek = ceiling > 0 ? clampSeekMs(time, ceiling) : Math.max(0, time);
1773
2913
  finishNotified = false;
1774
2914
  animations.forEach((a) => {
1775
- a.currentTime = time;
2915
+ a.currentTime = seek;
1776
2916
  });
1777
2917
  },
2918
+ "getCurrentProgress": () => {
2919
+ var _a2;
2920
+ const t = api.getCurrentTime();
2921
+ return t === null ? null : timeToProgress(t, (_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1);
2922
+ },
2923
+ "setCurrentProgress": (progress) => {
2924
+ var _a2;
2925
+ api.setCurrentTime(progressToTimeMs(progress, (_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1));
2926
+ },
1778
2927
  "destroy": () => {
1779
2928
  var _a2;
1780
2929
  api.cancel();
@@ -1785,12 +2934,15 @@ var PixodeskAnimator = (() => {
1785
2934
  }
1786
2935
  }
1787
2936
  };
1788
- if (config.trigger) {
1789
- if (config.timelineSource === "scroll") {
1790
- console.warn("scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
1791
- } else {
1792
- setupAnimationTriggers(api, config.trigger);
1793
- }
2937
+ if (config.timelineSource === "scroll") {
2938
+ if (config.trigger) diag.warn(PxDiagnosticKind.usage, "scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
2939
+ } else {
2940
+ const detachTriggers = setupAnimationTriggers(api, (_b = config.trigger) != null ? _b : {}, diag);
2941
+ const destroyEngine = api.destroy.bind(api);
2942
+ api.destroy = () => {
2943
+ detachTriggers();
2944
+ destroyEngine();
2945
+ };
1794
2946
  }
1795
2947
  if (scrollTimeline) {
1796
2948
  animations.forEach((a) => a.play());
@@ -1798,7 +2950,7 @@ var PixodeskAnimator = (() => {
1798
2950
  return api;
1799
2951
  }
1800
2952
 
1801
- // src/PxAnimatorBind.ts
2953
+ // src/engines/PxAnimatorBind.ts
1802
2954
  function finaliseAnimator(animatorConfig, callbacks, make) {
1803
2955
  let apiRef;
1804
2956
  let effectiveCallbacks = callbacks;
@@ -1813,27 +2965,27 @@ var PixodeskAnimator = (() => {
1813
2965
  }
1814
2966
  const res = make(effectiveCallbacks);
1815
2967
  apiRef = res;
1816
- if (animatorConfig.debugInstName) {
1817
- window[animatorConfig.debugInstName] = res;
2968
+ if (animatorConfig.debugGlobalName) {
2969
+ window[animatorConfig.debugGlobalName] = res;
1818
2970
  }
1819
2971
  return res;
1820
2972
  }
1821
- function requireData(options) {
1822
- if (!(options == null ? void 0 : options.data)) throw new Error("createAnimator: `data` is required");
1823
- return options.data;
2973
+ function requireDoc(options) {
2974
+ if (!(options == null ? void 0 : options.doc)) throw new Error("createAnimator: `doc` is required");
2975
+ return options.doc;
1824
2976
  }
1825
2977
  function createPrerenderedWaapiAnimator(options) {
1826
- const doc = requireData(options);
2978
+ const doc = requireDoc(options);
1827
2979
  const animatorConfig = getAnimatorConfig(doc) || {};
1828
2980
  return finaliseAnimator(
1829
2981
  animatorConfig,
1830
- options.callbacks,
2982
+ toEngineCallbacks(options),
1831
2983
  (cb) => createWebApiAnimator(doc, cb, null, true)
1832
2984
  );
1833
2985
  }
1834
2986
 
1835
- // src/PxAnimatorKeys.ts
1836
- var PX_ANIMATOR_DATA_KEY = "data";
2987
+ // src/shared/PxAnimatorKeys.ts
2988
+ var PX_ANIMATOR_DOC_KEY = "doc";
1837
2989
  return __toCommonJS(index_prerendered_waapi_exports);
1838
2990
  })();
1839
2991
  //# sourceMappingURL=index.prerendered-waapi.umd.js.map