@pixodesk/svg-animator-core 1.0.30 → 1.0.35

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
@@ -31,9 +31,9 @@ var __objRest = (source, exclude) => {
31
31
  return target;
32
32
  };
33
33
 
34
- // src/PxAnimatorUtil.ts
34
+ // src/util/PxAnimatorUtil.ts
35
35
  function bezierToSvgPath(path, forceCurves = false) {
36
- var _a, _b, _c, _d;
36
+ var _a2, _b, _c, _d;
37
37
  const v = path.v;
38
38
  const i = path.i;
39
39
  const o = path.o;
@@ -44,7 +44,7 @@ function bezierToSvgPath(path, forceCurves = false) {
44
44
  d.push("M" + v[0][0] + "," + v[0][1]);
45
45
  for (let idx = 1; idx < len; idx++) {
46
46
  const prevV = v[idx - 1];
47
- const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
47
+ const prevO = (_a2 = o == null ? void 0 : o[idx - 1]) != null ? _a2 : prevV;
48
48
  const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
49
49
  const currV = v[idx];
50
50
  const isLine = !forceCurves && (prevO[0] === prevV[0] && prevO[1] === prevV[1]) && (currI[0] === currV[0] && currI[1] === currV[1]);
@@ -95,7 +95,7 @@ function interpolateBeziers(paths1, paths2, progress) {
95
95
  return res;
96
96
  }
97
97
  function interpolateBezier(path1, path2, progress) {
98
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
98
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
99
99
  if (!path1 || !path2) return path1 || path2 || { v: [] };
100
100
  const t = Math.min(Math.max(progress, 0), 1);
101
101
  const len = Math.min(path1.v.length, path2.v.length);
@@ -106,7 +106,7 @@ function interpolateBezier(path1, path2, progress) {
106
106
  const v1 = path1.v[idx];
107
107
  const v2 = path2.v[idx];
108
108
  v.push(interpolateVec(v1, v2, t));
109
- const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
109
+ const i1 = (_b = (_a2 = path1.i) == null ? void 0 : _a2[idx]) != null ? _b : v1;
110
110
  const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
111
111
  i.push(interpolateVec(i1, i2, t));
112
112
  const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
@@ -225,8 +225,8 @@ function toRGBA(color) {
225
225
  return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
226
226
  }
227
227
  function parseRgba(s) {
228
- var _a;
229
- const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
228
+ var _a2;
229
+ const inner = (_a2 = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a2[1];
230
230
  if (!inner) throw new Error("Invalid rgb/rgba format");
231
231
  const parts = inner.split(",").map((v) => +v.trim());
232
232
  return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
@@ -265,9 +265,9 @@ var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color",
265
265
  var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
266
266
  var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
267
267
  function composeTransformParts(parts, opts) {
268
- var _a;
268
+ var _a2;
269
269
  if (!parts) return "";
270
- const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
270
+ const withUnits = (_a2 = opts == null ? void 0 : opts.withUnits) != null ? _a2 : true;
271
271
  const segs = [];
272
272
  const t = parts.translate;
273
273
  const o = parts.origin;
@@ -284,6 +284,38 @@ function composeTransformParts(parts, opts) {
284
284
  if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
285
285
  return segs.join("");
286
286
  }
287
+ function parseTransformParts(str2) {
288
+ var _a2, _b;
289
+ if (!str2 || typeof str2 !== "string") return void 0;
290
+ const out = {};
291
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
292
+ const order = ["translate", "rotate", "skewX", "scale"];
293
+ let lastIdx = -1;
294
+ let m;
295
+ while ((m = re.exec(str2)) !== null) {
296
+ const fn = m[1];
297
+ const idx = order.indexOf(fn);
298
+ if (idx < 0 || idx <= lastIdx) return void 0;
299
+ lastIdx = idx;
300
+ const nums = m[2].split(/[\s,]+/).filter(Boolean).map(Number);
301
+ if (nums.some((n) => Number.isNaN(n))) return void 0;
302
+ if (fn === "translate") {
303
+ if (nums.length < 1 || nums.length > 2) return void 0;
304
+ out.translate = [nums[0], (_a2 = nums[1]) != null ? _a2 : 0];
305
+ } else if (fn === "rotate") {
306
+ if (nums.length !== 1) return void 0;
307
+ out.rotate = nums[0];
308
+ } else if (fn === "skewX") {
309
+ if (nums.length !== 1) return void 0;
310
+ out.skew = nums[0];
311
+ } else {
312
+ if (nums.length < 1 || nums.length > 2) return void 0;
313
+ out.scale = [nums[0], (_b = nums[1]) != null ? _b : nums[0]];
314
+ }
315
+ }
316
+ if (str2.replace(/([a-zA-Z]+)\s*\(([^)]*)\)/g, "").replace(/[\s,]/g, "").length) return void 0;
317
+ return Object.keys(out).length ? out : void 0;
318
+ }
287
319
  var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
288
320
  var DEFAULT_DURATION_MS = 1e3;
289
321
  function kebabToCamelCaseWord(kebab) {
@@ -447,7 +479,7 @@ function invertEasing(easing) {
447
479
  return cubicBezier(flipped);
448
480
  }
449
481
 
450
- // src/PxScrollMath.ts
482
+ // src/playback/PxScrollMath.ts
451
483
  function isScrollTimeline(config) {
452
484
  return (config == null ? void 0 : config.timelineSource) === "scroll";
453
485
  }
@@ -475,8 +507,8 @@ function scrollPhaseInterval(phase, subjectSize, scrollportSize) {
475
507
  }
476
508
  var DEFAULT_PHASE = "cover";
477
509
  function resolveRangePointU(point, defaultFraction, subjectSize, scrollportSize) {
478
- var _a;
479
- const [u0, u1] = scrollPhaseInterval((_a = point == null ? void 0 : point.phase) != null ? _a : DEFAULT_PHASE, subjectSize, scrollportSize);
510
+ var _a2;
511
+ const [u0, u1] = scrollPhaseInterval((_a2 = point == null ? void 0 : point.phase) != null ? _a2 : DEFAULT_PHASE, subjectSize, scrollportSize);
480
512
  const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
481
513
  return u0 + fraction * (u1 - u0);
482
514
  }
@@ -488,9 +520,9 @@ function scrollViewProgress(subjectStart, subjectSize, scrollportSize, range) {
488
520
  return clamp((u - uStart) / (uEnd - uStart), 0, 1);
489
521
  }
490
522
  function scrollOffsetProgress(offset, maxOffset, range) {
491
- var _a, _b;
523
+ var _a2, _b;
492
524
  const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;
493
- const start = typeof ((_a = range == null ? void 0 : range.start) == null ? void 0 : _a.fraction) === "number" ? range.start.fraction : 0;
525
+ const start = typeof ((_a2 = range == null ? void 0 : range.start) == null ? void 0 : _a2.fraction) === "number" ? range.start.fraction : 0;
494
526
  const end = typeof ((_b = range == null ? void 0 : range.end) == null ? void 0 : _b.fraction) === "number" ? range.end.fraction : 1;
495
527
  if (end <= start) return raw >= end ? 1 : 0;
496
528
  return clamp((raw - start) / (end - start), 0, 1);
@@ -503,7 +535,8 @@ function scrollResolveAxis(axis, writingMode) {
503
535
  return vertical ? "x" : "y";
504
536
  }
505
537
 
506
- // src/PxSchema.ts
538
+ // src/schema/PxSchema.ts
539
+ var PX_UNKNOWN_KEY_ERROR = "unexpected extra key";
507
540
  function pathStr(path) {
508
541
  if (!path.length) return ".";
509
542
  let result = "";
@@ -615,6 +648,8 @@ var Union = class extends Base {
615
648
  constructor(schemas, defaultVal) {
616
649
  super();
617
650
  this.schemas = schemas;
651
+ /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Union from Tuple. */
652
+ this._kind = "union";
618
653
  this._default = defaultVal != null ? defaultVal : schemas[0]._default;
619
654
  }
620
655
  sanitize(raw) {
@@ -624,10 +659,10 @@ var Union = class extends Base {
624
659
  return this._default;
625
660
  }
626
661
  isValid(raw, ctx, path) {
627
- var _a;
662
+ var _a2;
628
663
  const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
629
664
  if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
630
- 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));
665
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no union member matched for value " + ((_a2 = JSON.stringify(raw)) != null ? _a2 : "").slice(0, 240));
631
666
  return false;
632
667
  }
633
668
  _canSanitize(raw) {
@@ -636,25 +671,31 @@ var Union = class extends Base {
636
671
  };
637
672
  var DiscriminatedUnion = class extends Base {
638
673
  constructor(_key, _schemas, defaultVal) {
674
+ var _a2;
639
675
  super();
640
676
  this._key = _key;
641
677
  this._schemas = _schemas;
678
+ /** Structural tag read by {@link describeSchema}. */
679
+ this._kind = "discriminatedUnion";
642
680
  this._default = defaultVal != null ? defaultVal : _schemas[0]._default;
643
681
  this._map = /* @__PURE__ */ new Map();
644
682
  for (const s of _schemas) {
645
683
  const keySchema = s._shape[_key];
646
- if (keySchema) this._map.set(keySchema._default, s);
684
+ if (!keySchema) continue;
685
+ const literal = (_a2 = keySchema.inner) != null ? _a2 : keySchema;
686
+ this._map.set(literal._default, s);
687
+ if (keySchema.inner) this._absentMember = s;
647
688
  }
648
689
  }
649
690
  _findSchema(raw) {
650
691
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
651
692
  const val = raw[this._key];
652
- if (val === void 0 || val === null) return void 0;
693
+ if (val === void 0 || val === null) return this._absentMember;
653
694
  return this._map.get(val);
654
695
  }
655
696
  sanitize(raw) {
656
- var _a;
657
- return ((_a = this._findSchema(raw)) != null ? _a : this._schemas[0]).sanitize(raw);
697
+ var _a2;
698
+ return ((_a2 = this._findSchema(raw)) != null ? _a2 : this._schemas[0]).sanitize(raw);
658
699
  }
659
700
  isValid(raw, ctx, path) {
660
701
  const schema = this._findSchema(raw);
@@ -683,7 +724,8 @@ var Obj = class extends Base {
683
724
  const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
684
725
  const out = {};
685
726
  for (const key of Object.keys(this._shape)) {
686
- out[key] = this._shape[key].sanitize(src[key]);
727
+ const v = this._shape[key].sanitize(src[key]);
728
+ if (v !== void 0) out[key] = v;
687
729
  }
688
730
  return out;
689
731
  }
@@ -705,7 +747,7 @@ var Obj = class extends Base {
705
747
  if (key in this._shape) continue;
706
748
  if (obj[key] === void 0) continue;
707
749
  p.push(key);
708
- ctx.errors.push(pathStr(p) + ": unexpected extra key");
750
+ ctx.errors.push(pathStr(p) + ": " + PX_UNKNOWN_KEY_ERROR);
709
751
  p.pop();
710
752
  ok = false;
711
753
  }
@@ -729,7 +771,8 @@ var OpenObj = class extends Base {
729
771
  const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
730
772
  const out = __spreadValues({}, src);
731
773
  for (const key of Object.keys(this._shape)) {
732
- out[key] = this._shape[key].sanitize(src[key]);
774
+ const v = this._shape[key].sanitize(src[key]);
775
+ if (v !== void 0) out[key] = v;
733
776
  }
734
777
  if (this._openSchema) {
735
778
  for (const key of Object.keys(src)) {
@@ -801,6 +844,8 @@ var Rec = class extends Base {
801
844
  constructor(value) {
802
845
  super();
803
846
  this.value = value;
847
+ /** Structural tag read by {@link describeSchema}. */
848
+ this._kind = "record";
804
849
  this._default = {};
805
850
  }
806
851
  sanitize(raw) {
@@ -869,8 +914,8 @@ var Lazy = class extends Base {
869
914
  this.resolved = null;
870
915
  }
871
916
  get schema() {
872
- var _a;
873
- return (_a = this.resolved) != null ? _a : this.resolved = this.fn();
917
+ var _a2;
918
+ return (_a2 = this.resolved) != null ? _a2 : this.resolved = this.fn();
874
919
  }
875
920
  sanitize(raw) {
876
921
  return this.schema.sanitize(raw);
@@ -886,6 +931,8 @@ var Tuple = class extends Base {
886
931
  constructor(schemas) {
887
932
  super();
888
933
  this.schemas = schemas;
934
+ /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Tuple from Union. */
935
+ this._kind = "tuple";
889
936
  this._default = schemas.map((s) => s._default);
890
937
  }
891
938
  sanitize(raw) {
@@ -920,12 +967,22 @@ function schemaKeys(schema) {
920
967
  );
921
968
  }
922
969
  function describeSchema(schema) {
923
- var _a;
970
+ var _a2;
924
971
  const s = schema;
925
- if ("_shape" in s) return { kind: "shape", shape: s._shape };
972
+ switch (s._kind) {
973
+ case "union":
974
+ return { kind: "union", members: s.schemas };
975
+ case "discriminatedUnion":
976
+ return { kind: "discriminatedUnion", key: s._key, members: s._schemas };
977
+ case "record":
978
+ return { kind: "record", value: s.value };
979
+ case "tuple":
980
+ return { kind: "tuple", items: s.schemas };
981
+ }
982
+ if ("_shape" in s) return { kind: "shape", shape: s._shape, openValue: s._openSchema };
926
983
  if ("item" in s) return { kind: "array", item: s.item };
927
984
  if ("inner" in s) return { kind: "optional", inner: s.inner };
928
- if ("fn" in s) return { kind: "lazy", resolved: (_a = s.resolved) != null ? _a : s.fn() };
985
+ if ("fn" in s) return { kind: "lazy", resolved: (_a2 = s.resolved) != null ? _a2 : s.resolved = s.fn() };
929
986
  return { kind: "leaf" };
930
987
  }
931
988
  var px = {
@@ -980,25 +1037,343 @@ var px = {
980
1037
  lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
981
1038
  };
982
1039
 
983
- // src/PxAnimatorConstants.ts
1040
+ // src/version/PxSchemaVersion.ts
1041
+ var PX_PLAYER_SCHEMA_VERSION = "1.1";
1042
+
1043
+ // src/version/PxWireVersion.ts
1044
+ var WIRE_VERSION_KEY = "version";
1045
+ var ANIMATOR_KEY = "animator";
1046
+ var META_KEY = "meta";
1047
+ var WireVersionRelation = /* @__PURE__ */ ((WireVersionRelation2) => {
1048
+ WireVersionRelation2["unstamped"] = "unstamped";
1049
+ WireVersionRelation2["same"] = "same";
1050
+ WireVersionRelation2["older"] = "older";
1051
+ WireVersionRelation2["newer"] = "newer";
1052
+ WireVersionRelation2["otherGeneration"] = "otherGeneration";
1053
+ return WireVersionRelation2;
1054
+ })(WireVersionRelation || {});
1055
+ var VERSION_RE = /^(\d+)\.(\d+)(?:\.(\d+))?$/;
1056
+ function parseWireVersion(raw) {
1057
+ if (typeof raw !== "string") return void 0;
1058
+ const m = VERSION_RE.exec(raw.trim());
1059
+ if (!m) return void 0;
1060
+ return { a: Number(m[1]), b: Number(m[2]), c: m[3] === void 0 ? 0 : Number(m[3]) };
1061
+ }
1062
+ function formatWireVersion(v) {
1063
+ return v.a + "." + v.b + "." + v.c;
1064
+ }
1065
+ var _a;
1066
+ var PLAYER_WIRE_VERSION = (_a = parseWireVersion(PX_PLAYER_SCHEMA_VERSION)) != null ? _a : { a: 1, b: 1, c: 0 };
1067
+ function getAnimatorBlock(doc) {
1068
+ if (!doc || typeof doc !== "object") return void 0;
1069
+ const atRoot = readObjectProp(doc, ANIMATOR_KEY);
1070
+ if (atRoot) return atRoot;
1071
+ const meta = readObjectProp(doc, META_KEY);
1072
+ return meta ? readObjectProp(meta, ANIMATOR_KEY) : void 0;
1073
+ }
1074
+ function readObjectProp(obj, key) {
1075
+ const value = obj[key];
1076
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1077
+ }
1078
+ function readWireVersion(doc) {
1079
+ const animator = getAnimatorBlock(doc);
1080
+ return animator ? parseWireVersion(animator[WIRE_VERSION_KEY]) : void 0;
1081
+ }
1082
+ function compareWireVersion(file, mine, readerReadsEditorPart) {
1083
+ if (!file) return "unstamped" /* unstamped */;
1084
+ if (file.a !== mine.a) return "otherGeneration" /* otherGeneration */;
1085
+ if (file.b !== mine.b) return file.b > mine.b ? "newer" /* newer */ : "older" /* older */;
1086
+ if (!readerReadsEditorPart || file.c === mine.c) return "same" /* same */;
1087
+ return file.c > mine.c ? "newer" /* newer */ : "older" /* older */;
1088
+ }
1089
+ function versionAdvice(relation, file, mine, isPlayer) {
1090
+ if (!file) return void 0;
1091
+ const target = isPlayer ? "player" : "editor";
1092
+ const gap = "written for schema " + formatWireVersion(file) + ", this " + target + " reads " + formatWireVersion(mine);
1093
+ switch (relation) {
1094
+ case "newer" /* newer */:
1095
+ return "This file is " + gap + ". Update the " + target + " to open it fully.";
1096
+ case "older" /* older */:
1097
+ return "This file is " + gap + ". Saving it from this " + target + " rewrites it in the current format.";
1098
+ case "otherGeneration" /* otherGeneration */:
1099
+ return "This file is " + gap + " \u2014 a different format generation, which no conversion bridges. Open it in a " + target + " of that generation, or accept this document without the parts named above.";
1100
+ default:
1101
+ return void 0;
1102
+ }
1103
+ }
1104
+ var WireStepKind = /* @__PURE__ */ ((WireStepKind2) => {
1105
+ WireStepKind2["additive"] = "additive";
1106
+ WireStepKind2["converted"] = "converted";
1107
+ return WireStepKind2;
1108
+ })(WireStepKind || {});
1109
+ var BASELINE_PLAYER_VERSION = "1.1";
1110
+ var PLAYER_WIRE_STEPS = [];
1111
+ function applyWireSteps(doc, cfg) {
1112
+ const from = readWireVersion(doc);
1113
+ const relation = compareWireVersion(from, cfg.target, cfg.readerReadsEditorPart);
1114
+ if (!from || relation !== "older" /* older */) {
1115
+ return {
1116
+ doc,
1117
+ from,
1118
+ relation,
1119
+ applied: [],
1120
+ advice: versionAdvice(relation, from, cfg.target, !cfg.readerReadsEditorPart)
1121
+ };
1122
+ }
1123
+ const due = [];
1124
+ for (const step of cfg.steps) {
1125
+ const stepTo = parseWireVersion(step.to);
1126
+ if (!stepTo) continue;
1127
+ if (compareWireVersion(from, stepTo, cfg.readerReadsEditorPart) !== "older" /* older */) continue;
1128
+ if (!step.up) continue;
1129
+ due.push(step);
1130
+ }
1131
+ if (!due.length || !doc || typeof doc !== "object") return { doc, from, relation, applied: [] };
1132
+ const target = clonePlain(doc);
1133
+ const applied = [];
1134
+ for (const step of due) {
1135
+ try {
1136
+ step.up(target);
1137
+ applied.push(step);
1138
+ } catch (e) {
1139
+ break;
1140
+ }
1141
+ }
1142
+ if (!applied.length) return { doc, from, relation, applied: [] };
1143
+ stampVersion(target, cfg.target, cfg.readerReadsEditorPart);
1144
+ return { doc: target, from, relation, applied };
1145
+ }
1146
+ function convertPlayerDocument(doc) {
1147
+ return applyWireSteps(doc, {
1148
+ steps: PLAYER_WIRE_STEPS,
1149
+ target: PLAYER_WIRE_VERSION,
1150
+ readerReadsEditorPart: false
1151
+ });
1152
+ }
1153
+ function clonePlain(value) {
1154
+ const structured = globalThis.structuredClone;
1155
+ return structured ? structured(value) : JSON.parse(JSON.stringify(value));
1156
+ }
1157
+ function stampVersion(doc, target, readerReadsEditorPart) {
1158
+ const animator = getAnimatorBlock(doc);
1159
+ if (!animator) return;
1160
+ const previous = parseWireVersion(animator[WIRE_VERSION_KEY]);
1161
+ animator[WIRE_VERSION_KEY] = formatWireVersion({
1162
+ a: target.a,
1163
+ b: target.b,
1164
+ c: readerReadsEditorPart ? target.c : previous ? previous.c : 0
1165
+ });
1166
+ }
1167
+ function applyWireStepsDown(doc, cfg) {
1168
+ const from = readWireVersion(doc);
1169
+ if (!from) {
1170
+ return { ok: false, blocking: [], reason: "The document carries no version, so there is nothing to convert down from." };
1171
+ }
1172
+ const relation = compareWireVersion(from, cfg.target, cfg.readerReadsEditorPart);
1173
+ if (relation === "otherGeneration" /* otherGeneration */) {
1174
+ return {
1175
+ ok: false,
1176
+ blocking: [],
1177
+ reason: "Schema " + formatWireVersion(from) + " and " + formatWireVersion(cfg.target) + " are different generations; no conversion bridges them."
1178
+ };
1179
+ }
1180
+ if (relation !== "newer" /* newer */) return { ok: true, doc, applied: [] };
1181
+ const toUndo = [];
1182
+ for (const step of cfg.steps) {
1183
+ const stepTo = parseWireVersion(step.to);
1184
+ if (!stepTo) continue;
1185
+ if (compareWireVersion(stepTo, cfg.target, cfg.readerReadsEditorPart) !== "newer" /* newer */) continue;
1186
+ if (compareWireVersion(stepTo, from, cfg.readerReadsEditorPart) === "newer" /* newer */) continue;
1187
+ toUndo.push(step);
1188
+ }
1189
+ toUndo.reverse();
1190
+ const blocking = toUndo.filter((s) => s.kind === "converted" /* converted */ && !s.down);
1191
+ if (blocking.length) {
1192
+ return {
1193
+ ok: false,
1194
+ blocking,
1195
+ reason: "Cannot convert down to " + formatWireVersion(cfg.target) + ": " + blocking.map((s) => s.from + " \u2192 " + s.to + " (" + s.reason + ")").join("; ") + " cannot be undone."
1196
+ };
1197
+ }
1198
+ const target = clonePlain(doc);
1199
+ const applied = [];
1200
+ for (const step of toUndo) {
1201
+ if (!step.down) continue;
1202
+ try {
1203
+ step.down(target);
1204
+ applied.push(step);
1205
+ } catch (e) {
1206
+ return {
1207
+ ok: false,
1208
+ blocking: [step],
1209
+ reason: "Undoing " + step.from + " \u2192 " + step.to + " failed: " + String(e)
1210
+ };
1211
+ }
1212
+ }
1213
+ stampVersion(target, cfg.target, cfg.readerReadsEditorPart);
1214
+ return { ok: true, doc: target, applied };
1215
+ }
1216
+ function downgradePlayerDocument(doc, target) {
1217
+ return applyWireStepsDown(doc, { steps: PLAYER_WIRE_STEPS, target, readerReadsEditorPart: false });
1218
+ }
1219
+
1220
+ // src/version/PxSchemaFieldUniverse.ts
1221
+ function schemaFieldUniverse(root) {
1222
+ const fields = /* @__PURE__ */ new Set();
1223
+ const enumerated = /* @__PURE__ */ new Set();
1224
+ const queue = [{ schema: root, path: "" }];
1225
+ while (queue.length) {
1226
+ const { schema, path } = queue.shift();
1227
+ const d = describeSchema(schema);
1228
+ switch (d.kind) {
1229
+ case "shape": {
1230
+ if (enumerated.has(schema)) break;
1231
+ enumerated.add(schema);
1232
+ for (const key of Object.keys(d.shape)) {
1233
+ const id = path ? path + "." + key : key;
1234
+ fields.add(id);
1235
+ queue.push({ schema: d.shape[key], path: id });
1236
+ }
1237
+ if (d.openValue) queue.push({ schema: d.openValue, path: path + "{*}" });
1238
+ break;
1239
+ }
1240
+ case "optional":
1241
+ queue.push({ schema: d.inner, path });
1242
+ break;
1243
+ case "array":
1244
+ queue.push({ schema: d.item, path: path + "[]" });
1245
+ break;
1246
+ case "lazy":
1247
+ queue.push({ schema: d.resolved, path });
1248
+ break;
1249
+ case "record":
1250
+ queue.push({ schema: d.value, path: path + "{*}" });
1251
+ break;
1252
+ case "union":
1253
+ d.members.forEach((member, i) => queue.push({ schema: member, path: path + "|" + i }));
1254
+ break;
1255
+ case "discriminatedUnion":
1256
+ for (const member of d.members) queue.push({ schema: member, path: path + "|" + discriminantOf(member, d.key) });
1257
+ break;
1258
+ default:
1259
+ break;
1260
+ }
1261
+ }
1262
+ return [...fields].sort();
1263
+ }
1264
+ function discriminantOf(member, key) {
1265
+ const d = describeSchema(member);
1266
+ if (d.kind !== "shape") return "?";
1267
+ const keySchema = d.shape[key];
1268
+ if (keySchema === void 0) return "?";
1269
+ const kd = describeSchema(keySchema);
1270
+ return String((kd.kind === "optional" ? kd.inner : keySchema)._default);
1271
+ }
1272
+
1273
+ // src/version/PxSchemaRelease.ts
1274
+ function diffFieldUniverse(previous, current) {
1275
+ const prev = new Set(previous);
1276
+ const cur = new Set(current);
1277
+ return {
1278
+ added: current.filter((k) => !prev.has(k)).sort(),
1279
+ removed: previous.filter((k) => !cur.has(k)).sort()
1280
+ };
1281
+ }
1282
+ function planSchemaRelease(p) {
1283
+ const changed = p.added.length > 0 || p.removed.length > 0;
1284
+ const last = parseWireVersion(p.lastReleased);
1285
+ const declared = parseWireVersion(p.declared);
1286
+ if (!last || !declared) return { changed, refuse: "Unparseable version: " + p.lastReleased + " / " + p.declared + "." };
1287
+ const requiredVersion = last.a + "." + (last.b + 1);
1288
+ if (!changed) {
1289
+ if (declared.b === last.b && declared.a === last.a) return { changed };
1290
+ const step2 = p.steps.find((s) => s.to === p.declared);
1291
+ return step2 ? { changed, requiredVersion: p.declared } : { changed, refuse: "The version moved to " + p.declared + " with no key change and no step explaining it." };
1292
+ }
1293
+ const requiredKind = p.removed.length ? "converted" /* converted */ : "additive" /* additive */;
1294
+ const summary = p.added.length + " key(s) added, " + p.removed.length + " removed";
1295
+ if (declared.a !== last.a || declared.b !== last.b + 1) {
1296
+ return {
1297
+ changed,
1298
+ requiredKind,
1299
+ requiredVersion,
1300
+ refuse: "The player schema changed (" + summary + ") but PX_PLAYER_SCHEMA_VERSION is " + p.declared + ". Set it to " + requiredVersion + " and add the PLAYER_WIRE_STEPS entry."
1301
+ };
1302
+ }
1303
+ const step = p.steps.find((s) => s.to === p.declared);
1304
+ if (!step) {
1305
+ return { changed, requiredKind, requiredVersion, refuse: "No PLAYER_WIRE_STEPS entry reaches " + p.declared + "." };
1306
+ }
1307
+ if (requiredKind === "converted" /* converted */ && step.kind !== "converted" /* converted */) {
1308
+ return {
1309
+ changed,
1310
+ requiredKind,
1311
+ requiredVersion,
1312
+ refuse: "Keys were REMOVED (" + p.removed.join(", ") + "), which is never additive \u2014 the " + p.declared + " step must be `converted`, with an up()."
1313
+ };
1314
+ }
1315
+ return { changed, requiredKind, requiredVersion };
1316
+ }
1317
+ function releaseLogProblems(releases, steps, declared, baseline) {
1318
+ const problems = [];
1319
+ if (!releases.length) return ["The release log is empty."];
1320
+ if (!releases[0].baseline || releases[0].version !== baseline) {
1321
+ problems.push("The first release must be the baseline " + baseline + ".");
1322
+ }
1323
+ for (let i = 1; i < releases.length; i++) {
1324
+ const prev = parseWireVersion(releases[i - 1].version);
1325
+ const cur = parseWireVersion(releases[i].version);
1326
+ if (!prev || !cur || cur.a !== prev.a || cur.b !== prev.b + 1) {
1327
+ problems.push("Release " + releases[i].version + " does not follow " + releases[i - 1].version + " by one `b` step.");
1328
+ }
1329
+ if (releases[i].date < releases[i - 1].date) problems.push("Release " + releases[i].version + " is dated before its predecessor.");
1330
+ const step = steps.find((s) => s.to === releases[i].version);
1331
+ if (!step) problems.push("Release " + releases[i].version + " has no PLAYER_WIRE_STEPS entry.");
1332
+ else if (releases[i].removed.length && step.kind !== "converted" /* converted */) {
1333
+ problems.push("Release " + releases[i].version + " removed keys, but its step is not `converted`.");
1334
+ }
1335
+ }
1336
+ const latest = releases[releases.length - 1].version;
1337
+ if (latest !== declared) {
1338
+ problems.push("PX_PLAYER_SCHEMA_VERSION is " + declared + " but the last release record is " + latest + " \u2014 a bump needs its changelog entry (run scripts/schema-release.mjs --apply).");
1339
+ }
1340
+ return problems;
1341
+ }
1342
+
1343
+ // src/format/PxAnimatorConstants.ts
984
1344
  var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
985
1345
  var PX_ANIM_ATTR_NAME = "_px_animator";
986
- var PxAnimatorMode = {
987
- auto: "auto",
988
- waapi: "waapi",
989
- frames: "frames"
1346
+ var PX_TIMELINE_SHARED_KEYS = ["duration", "iterations", "engine", "frameRate"];
1347
+ var PX_TIME_ONLY_TIMELINE_KEYS = ["trigger", "delay", "fillMode", "direction"];
1348
+ var PxTimelineEngine = {
1349
+ native: "native",
1350
+ js: "js"
990
1351
  };
991
- var PxAnimatorEngine = {
992
- waapi: PxAnimatorMode.waapi,
993
- frames: PxAnimatorMode.frames
1352
+ var PxTimelineEngineExtra = __spreadProps(__spreadValues({}, PxTimelineEngine), {
1353
+ auto: "auto"
1354
+ });
1355
+ function resolveTimelineEngine(engine) {
1356
+ return engine === PxTimelineEngineExtra.js ? PxTimelineEngine.js : PxTimelineEngine.native;
1357
+ }
1358
+ function isNativeForced(engine) {
1359
+ return engine === PxTimelineEngineExtra.native;
1360
+ }
1361
+ function mayUseNativeScrollTimeline(engine) {
1362
+ return engine !== PxTimelineEngineExtra.js;
1363
+ }
1364
+ var PxLoopRepeatAt = {
1365
+ /** Segment from the START; the repetition runs BEFORE the first keyframe
1366
+ * (intro loops that play until the main timeline begins). */
1367
+ start: "start",
1368
+ /** DEFAULT — segment from the END; the repetition runs AFTER the last keyframe
1369
+ * (idle/outro loops that continue once the main timeline has finished). */
1370
+ end: "end"
994
1371
  };
995
- var PxLoopExtend = {
996
- /** Segment from the START; the animation is extended BEFORE the first keyframe
997
- * (intro loops that run before the main timeline begins). */
998
- before: "before",
999
- /** DEFAULT — segment from the END; extended AFTER the last keyframe (idle/outro
1000
- * loops that continue once the main timeline has finished). */
1001
- after: "after"
1372
+ var PxLoopDirection = {
1373
+ /** DEFAULT — cycle: every repetition replays the segment the same way round. */
1374
+ normal: "normal",
1375
+ /** Ping-pong: repetitions alternate forward / backward. */
1376
+ alternate: "alternate"
1002
1377
  };
1003
1378
  var PxMaskType = {
1004
1379
  luminance: "luminance",
@@ -1008,8 +1383,9 @@ var PxUnits = {
1008
1383
  userSpaceOnUse: "userSpaceOnUse",
1009
1384
  objectBoundingBox: "objectBoundingBox"
1010
1385
  };
1011
- var PxCloneType = {
1012
- content: "content"
1386
+ var PxCloneWithout = {
1387
+ translate: "translate"
1388
+ // transform: 'transform', // future: drop rotate/scale too (content only)
1013
1389
  };
1014
1390
  var PxPathOverflow = {
1015
1391
  clip: "clip",
@@ -1033,6 +1409,9 @@ var PxStrokeTrimSubPaths = {
1033
1409
  };
1034
1410
  var TEXT_ATTR = "text";
1035
1411
  var TEXT_CONTENT_ATTR = "textContent";
1412
+ var CLASS_ATTR = "class";
1413
+ var TRANSFORM_ATTR = "transform";
1414
+ var OFFSET_DISTANCE_ATTR = "offsetDistance";
1036
1415
  var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
1037
1416
  "type",
1038
1417
  "children",
@@ -1043,7 +1422,18 @@ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
1043
1422
  TEXT_ATTR,
1044
1423
  TEXT_CONTENT_ATTR
1045
1424
  ]);
1046
- var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
1425
+ var TRANSFORM_PART = {
1426
+ translate: "translate",
1427
+ rotate: "rotate",
1428
+ scale: "scale",
1429
+ origin: "origin"
1430
+ };
1431
+ var PX_TRANSFORM_PART_KEYS = [
1432
+ TRANSFORM_PART.translate,
1433
+ TRANSFORM_PART.rotate,
1434
+ TRANSFORM_PART.scale,
1435
+ TRANSFORM_PART.origin
1436
+ ];
1047
1437
  var PxGradientUnits = {
1048
1438
  userSpaceOnUse: "userSpaceOnUse",
1049
1439
  objectBoundingBox: "objectBoundingBox"
@@ -1061,29 +1451,140 @@ function isPxElementFileFormat(fileJson) {
1061
1451
  if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
1062
1452
  return false;
1063
1453
  }
1064
- return fileJson["type"] === "svg";
1454
+ return fileJson.type === "svg";
1065
1455
  }
1066
1456
  function getAnimatorConfig(doc) {
1067
- var _a;
1068
- return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
1457
+ var _a2;
1458
+ const cfg = (doc == null ? void 0 : doc.animator) || ((_a2 = doc == null ? void 0 : doc.meta) == null ? void 0 : _a2.animator);
1459
+ return cfg ? flattenAnimatorTimeline(cfg) : void 0;
1460
+ }
1461
+ var flattenMemo = /* @__PURE__ */ new WeakMap();
1462
+ function flattenAnimatorTimeline(cfg) {
1463
+ const timeline = cfg.timeline;
1464
+ if (timeline === void 0 || timeline === null || typeof timeline !== "object") return cfg;
1465
+ const memoised = flattenMemo.get(cfg);
1466
+ if (memoised) return memoised;
1467
+ const _a2 = cfg, { timeline: _dropped } = _a2, flat = __objRest(_a2, ["timeline"]);
1468
+ if (timeline.engine !== void 0) flat.engine = timeline.engine;
1469
+ if (timeline.frameRate !== void 0) flat.frameRate = timeline.frameRate;
1470
+ if (timeline.type === "scroll" || timeline.type === "view") {
1471
+ flat.timelineSource = "scroll";
1472
+ if (timeline.duration !== void 0) flat.duration = timeline.duration;
1473
+ if (timeline.iterations !== void 0) flat.iterations = timeline.iterations;
1474
+ const scroll = __spreadValues({}, flat.scroll || {});
1475
+ scroll.kind = timeline.type;
1476
+ if (timeline.axis !== void 0) scroll.axis = timeline.axis;
1477
+ if (timeline.source !== void 0) scroll.source = timeline.source;
1478
+ if (timeline.subject !== void 0) scroll.subject = timeline.subject;
1479
+ if (timeline.smoothing !== void 0) scroll.smoothing = timeline.smoothing;
1480
+ if (timeline.range !== void 0) scroll.range = timeline.range;
1481
+ const pin = timeline.pin;
1482
+ if (typeof pin === "boolean") scroll.pin = pin;
1483
+ else if (pin && typeof pin === "object") {
1484
+ scroll.pin = true;
1485
+ if (pin.align !== void 0) scroll.pinAlign = pin.align;
1486
+ if (pin.top !== void 0) scroll.pinTop = pin.top;
1487
+ if (pin.distance !== void 0) scroll.pinDistance = pin.distance;
1488
+ }
1489
+ flat.scroll = scroll;
1490
+ } else {
1491
+ if (timeline.duration !== void 0) flat.duration = timeline.duration;
1492
+ if (timeline.trigger !== void 0) {
1493
+ const _b = timeline.trigger, { finishAction } = _b, restTrigger = __objRest(_b, ["finishAction"]);
1494
+ if (Object.keys(restTrigger).length) flat.trigger = restTrigger;
1495
+ if (finishAction !== void 0) flat.resetOnFinish = finishAction === "reset";
1496
+ }
1497
+ if (timeline.delay !== void 0) flat.delay = timeline.delay;
1498
+ if (timeline.iterations !== void 0) flat.iterations = timeline.iterations;
1499
+ if (timeline.direction !== void 0) flat.direction = timeline.direction;
1500
+ if (timeline.fillMode !== void 0) flat.fill = timeline.fillMode;
1501
+ }
1502
+ flattenMemo.set(cfg, flat);
1503
+ return flat;
1504
+ }
1505
+ function scrollKindOrDefault(kind) {
1506
+ return kind === "scroll" ? "scroll" : "view";
1507
+ }
1508
+ function nestAnimatorTimeline(cfg) {
1509
+ if (!cfg || cfg.timeline !== void 0) return cfg;
1510
+ const _a2 = cfg, {
1511
+ timelineSource,
1512
+ scroll,
1513
+ trigger,
1514
+ delay,
1515
+ iterations,
1516
+ direction,
1517
+ fill,
1518
+ resetOnFinish,
1519
+ duration,
1520
+ engine,
1521
+ frameRate
1522
+ } = _a2, shared = __objRest(_a2, [
1523
+ "timelineSource",
1524
+ "scroll",
1525
+ "trigger",
1526
+ "delay",
1527
+ "iterations",
1528
+ "direction",
1529
+ "fill",
1530
+ "resetOnFinish",
1531
+ "duration",
1532
+ "engine",
1533
+ "frameRate"
1534
+ ]);
1535
+ if (timelineSource === "scroll") {
1536
+ const timeline2 = { type: scrollKindOrDefault(scroll == null ? void 0 : scroll.kind) };
1537
+ if (engine !== void 0) timeline2.engine = engine;
1538
+ if (frameRate !== void 0) timeline2.frameRate = frameRate;
1539
+ if (duration !== void 0) timeline2.duration = duration;
1540
+ if (typeof iterations === "number") timeline2.iterations = iterations;
1541
+ if (scroll) {
1542
+ if (scroll.axis !== void 0) timeline2.axis = scroll.axis;
1543
+ if (scroll.source !== void 0) timeline2.source = scroll.source;
1544
+ if (scroll.subject !== void 0) timeline2.subject = scroll.subject;
1545
+ if (scroll.smoothing !== void 0) timeline2.smoothing = scroll.smoothing;
1546
+ if (scroll.range !== void 0) timeline2.range = scroll.range;
1547
+ const hasPinParams = scroll.pinAlign !== void 0 || scroll.pinTop !== void 0 || scroll.pinDistance !== void 0;
1548
+ if (hasPinParams) {
1549
+ timeline2.pin = __spreadValues(__spreadValues(__spreadValues({}, scroll.pinAlign !== void 0 ? { align: scroll.pinAlign } : {}), scroll.pinTop !== void 0 ? { top: scroll.pinTop } : {}), scroll.pinDistance !== void 0 ? { distance: scroll.pinDistance } : {});
1550
+ } else if (scroll.pin !== void 0) {
1551
+ timeline2.pin = scroll.pin;
1552
+ }
1553
+ }
1554
+ return __spreadProps(__spreadValues({}, shared), { timeline: timeline2 });
1555
+ }
1556
+ const timeline = {};
1557
+ if (engine !== void 0) timeline.engine = engine;
1558
+ if (frameRate !== void 0) timeline.frameRate = frameRate;
1559
+ if (duration !== void 0) timeline.duration = duration;
1560
+ if (trigger !== void 0 || resetOnFinish) {
1561
+ const t = __spreadValues({}, trigger || {});
1562
+ if (resetOnFinish) t.finishAction = "reset";
1563
+ timeline.trigger = t;
1564
+ }
1565
+ if (delay !== void 0) timeline.delay = delay;
1566
+ if (iterations !== void 0) timeline.iterations = iterations;
1567
+ if (direction !== void 0) timeline.direction = direction;
1568
+ if (fill !== void 0) timeline.fillMode = fill;
1569
+ return Object.keys(timeline).length > 0 ? __spreadProps(__spreadValues({}, shared), { timeline }) : shared;
1069
1570
  }
1070
1571
  function getDefs(doc) {
1071
- var _a;
1572
+ var _a2;
1072
1573
  if (!doc) return void 0;
1073
- return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
1574
+ return (_a2 = getAnimatorConfig(doc)) == null ? void 0 : _a2.definitions;
1074
1575
  }
1075
1576
  function getBindings(doc) {
1076
- var _a;
1577
+ var _a2;
1077
1578
  if (!doc) return void 0;
1078
- const animateById = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animateById;
1579
+ const animateById = (_a2 = getAnimatorConfig(doc)) == null ? void 0 : _a2.animateById;
1079
1580
  if (!animateById) return void 0;
1080
- return Object.entries(animateById).map(([id, anim]) => ({ id, animate: anim }));
1581
+ return Object.entries(animateById).map(([id, anim]) => ({ id: id.startsWith("#") ? id.slice(1) : id, animate: anim }));
1081
1582
  }
1082
1583
  function getChildren(doc) {
1083
1584
  return doc == null ? void 0 : doc.children;
1084
1585
  }
1085
1586
 
1086
- // src/PxAnimatorTypes.ts
1587
+ // src/format/PxAnimatorTypes.ts
1087
1588
  var PxEasingOrRefSchema = px.union([
1088
1589
  px.string(),
1089
1590
  px.tuple([px.number(), px.number(), px.number(), px.number()])
@@ -1093,39 +1594,54 @@ var PxKeyframeValueSchema = implementsInterface()(px.union([
1093
1594
  // e.g. for colors
1094
1595
  px.number(),
1095
1596
  px.array(px.number()),
1096
- px.lazy(() => PxTransformPartsSchema, {}),
1597
+ // ORDER LAW: the key-discriminated object shapes (`{path}`, `{paths}`) come BEFORE the
1598
+ // all-optional transform-parts record. In default (non-strict) mode that record accepts
1599
+ // ANY object (every key optional, unknown keys ignored), so listing it earlier made
1600
+ // Union.sanitize route `{path}`/`{paths}` values into it and strip them to `{}` —
1601
+ // silent morph-data loss (repro: the editor's keyframeValueSanitize spec). Validity is
1602
+ // order-independent (`some()`); only sanitize routing depends on this order.
1097
1603
  px.object({ path: px.string() }),
1098
1604
  px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
1099
1605
  // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
1100
- px.lazy(() => px.array(PxGradientStopSchema), [])
1606
+ px.lazy(() => px.array(PxGradientStopSchema), []),
1607
+ px.lazy(() => PxTransformPartsSchema, {})
1101
1608
  ]));
1102
1609
  var PxKeyframeSchema = implementsInterface()(px.object({
1103
1610
  time: px.number().optional(),
1104
- t: px.number().optional(),
1105
1611
  value: PxKeyframeValueSchema.optional(),
1106
- v: PxKeyframeValueSchema.optional(),
1107
1612
  easing: PxEasingOrRefSchema.optional(),
1108
- e: PxEasingOrRefSchema.optional(),
1109
1613
  tangentOut: px.tuple([px.number(), px.number()]).optional(),
1110
- to: px.tuple([px.number(), px.number()]).optional(),
1111
- // short alias
1112
- tangentIn: px.tuple([px.number(), px.number()]).optional(),
1113
- ti: px.tuple([px.number(), px.number()]).optional(),
1114
- // short alias
1115
- selected: px.boolean().optional()
1116
- // editor-side UI state (Player ignores it)
1614
+ tangentIn: px.tuple([px.number(), px.number()]).optional()
1615
+ // (`selected` — editor timeline-selection UI state — was REMOVED from the wire
1616
+ // (review §1.3): editor data lives under `meta`. The editor still carries it on
1617
+ // its internal COPY-PASTE payload, which never validates against this schema.)
1117
1618
  }));
1619
+ var anyKf = (kf) => kf;
1620
+ var kfTime = (kf) => {
1621
+ var _a2, _b;
1622
+ return (_b = (_a2 = anyKf(kf).time) != null ? _a2 : anyKf(kf).t) != null ? _b : 0;
1623
+ };
1624
+ var kfValue = (kf) => {
1625
+ var _a2;
1626
+ return (_a2 = anyKf(kf).value) != null ? _a2 : anyKf(kf).v;
1627
+ };
1628
+ var kfEasing = (kf) => {
1629
+ var _a2;
1630
+ return (_a2 = anyKf(kf).easing) != null ? _a2 : anyKf(kf).e;
1631
+ };
1632
+ var kfTangentIn = (kf) => anyKf(kf).tangentIn;
1633
+ var kfTangentOut = (kf) => anyKf(kf).tangentOut;
1118
1634
  var PxLoopSchema = implementsInterface()(px.object({
1119
1635
  segmentCount: px.number().optional(),
1120
- extend: px.enum([PxLoopExtend.before, PxLoopExtend.after]).optional(),
1121
- alternate: px.boolean().optional()
1636
+ repeatAt: px.enum([PxLoopRepeatAt.start, PxLoopRepeatAt.end]).optional(),
1637
+ direction: px.enum([PxLoopDirection.normal, PxLoopDirection.alternate]).optional()
1122
1638
  }));
1123
1639
  var PxPropertyAnimationSchema = implementsInterface()(px.object({
1124
1640
  value: PxKeyframeValueSchema.optional(),
1125
1641
  keyframes: px.array(PxKeyframeSchema).optional(),
1126
- kfs: px.array(PxKeyframeSchema).optional(),
1127
1642
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
1128
- autoOrient: px.boolean().optional()
1643
+ autoOrient: px.boolean().optional(),
1644
+ alongPathMode: px.enum(["sampled", "offsetPath"]).optional()
1129
1645
  }));
1130
1646
  var PxTransformPartsSchema = implementsInterface()(px.object({
1131
1647
  translate: px.tuple([px.number(), px.number()]).optional(),
@@ -1151,6 +1667,10 @@ var PxElementAnimationSchema = implementsInterface()(px.union([
1151
1667
  var PxTriggerSchema = implementsInterface()(px.object({
1152
1668
  startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
1153
1669
  outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
1670
+ // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
1671
+ // `fill`) or `'reset'` (snap back to the start state). Pairs with `outAction` ("what
1672
+ // happens when the trigger condition ends"); both end-of-life knobs now read alike.
1673
+ finishAction: px.enum(["hold", "reset"]).optional(),
1154
1674
  scrollIntoViewThreshold: px.number().optional()
1155
1675
  }));
1156
1676
  var PxGlyphSchema = implementsInterface()(px.object({
@@ -1158,8 +1678,8 @@ var PxGlyphSchema = implementsInterface()(px.object({
1158
1678
  d: px.string()
1159
1679
  }));
1160
1680
  var PxGlyphFontSchema = implementsInterface()(px.object({
1161
- fFamily: px.string(),
1162
- style: px.string(),
1681
+ fontFamily: px.string(),
1682
+ fontStyle: px.string(),
1163
1683
  ascent: px.number(),
1164
1684
  unitsPerEm: px.number(),
1165
1685
  glyphs: px.record(PxGlyphSchema)
@@ -1167,8 +1687,10 @@ var PxGlyphFontSchema = implementsInterface()(px.object({
1167
1687
  var PxDefsSchema = implementsInterface()(px.object({
1168
1688
  easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
1169
1689
  animations: px.record(PxAnimationDefinitionSchema).optional(),
1170
- styles: px.record(px.any()).optional(),
1171
- glyphs: px.record(PxGlyphFontSchema).optional()
1690
+ // Review §2.6: the schema now matches the declared type — a style preset is a flat
1691
+ // record of string|number attribute values, nothing nested.
1692
+ styles: px.record(px.record(px.union([px.string(), px.number()]))).optional(),
1693
+ fonts: px.record(PxGlyphFontSchema).optional()
1172
1694
  }));
1173
1695
  var PX_SCROLL_PHASES = ["cover", "contain", "entry", "exit", "entry-crossing", "exit-crossing"];
1174
1696
  var PxScrollRangePointSchema = implementsInterface()(px.object({
@@ -1180,7 +1702,6 @@ var PxScrollRangeSchema = px.object({
1180
1702
  end: PxScrollRangePointSchema.optional()
1181
1703
  });
1182
1704
  var PxScrollSchema = implementsInterface()(px.object({
1183
- driver: px.enum(["custom", "native"]).optional(),
1184
1705
  kind: px.enum(["view", "scroll"]).optional(),
1185
1706
  axis: px.enum(["block", "inline", "x", "y"]).optional(),
1186
1707
  source: px.enum(["nearest", "root"]).optional(),
@@ -1193,23 +1714,67 @@ var PxScrollSchema = implementsInterface()(px.object({
1193
1714
  pinDistance: px.number().optional(),
1194
1715
  range: PxScrollRangeSchema.optional()
1195
1716
  }));
1196
- var PxAnimatorConfigSchema = implementsInterface()(px.object({
1197
- mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.waapi, PxAnimatorMode.frames]).optional(),
1717
+ var PxTimelinePinSchema = implementsInterface()(px.object({
1718
+ align: px.enum(["top", "center", "bottom"]).optional(),
1719
+ top: px.number().optional(),
1720
+ distance: px.number().optional()
1721
+ }));
1722
+ var PxTimelineEngineSchema = px.enum([PxTimelineEngineExtra.auto, PxTimelineEngineExtra.native, PxTimelineEngineExtra.js]).optional();
1723
+ var PxTimeTimelineSchema = implementsInterface()(px.object({
1724
+ type: px.literal("time").optional(),
1725
+ engine: PxTimelineEngineSchema,
1726
+ frameRate: px.number().optional(),
1727
+ // §2.8: duration is a property of the TIMELINE — how long one pass takes.
1198
1728
  duration: px.number().optional(),
1729
+ trigger: PxTriggerSchema.optional(),
1199
1730
  delay: px.number().optional(),
1200
- // LAW (SCHEMA-DESIGN R3, S10): the ONE sanctioned string-in-number union
1201
- // (CSS animation-iteration-count familiarity) — do not add more mixed unions.
1202
1731
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1203
- fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
1204
- direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
1732
+ // `fillMode` on the wire (CSS `animation-fill-mode`; the runtime view calls it `fill`)
1733
+ // — never `fill`, which is paint everywhere else in the format.
1734
+ fillMode: px.enum(["forwards", "backwards", "both", "none"]).optional(),
1735
+ direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional()
1736
+ }));
1737
+ var scrollishTimelineShape = {
1738
+ // §2.8: duration is a property of the TIMELINE — under scrubbing it is the keyframe
1739
+ // span the scroll range maps onto.
1740
+ duration: px.number().optional(),
1741
+ // Finite repeat count IS meaningful when scrubbing — the scroll range maps onto
1742
+ // duration × iterations (rule D4; `'infinite'` cannot map to a range, so no literal here).
1743
+ iterations: px.number().optional(),
1744
+ engine: PxTimelineEngineSchema,
1205
1745
  frameRate: px.number().optional(),
1206
- trigger: PxTriggerSchema.optional(),
1207
- resetOnFinish: px.boolean().optional(),
1746
+ axis: px.enum(["block", "inline", "x", "y"]).optional(),
1747
+ source: px.enum(["nearest", "root"]).optional(),
1748
+ subject: px.string().optional(),
1749
+ // 'parent' | 'scroller' | any CSS selector
1750
+ smoothing: px.number().optional(),
1751
+ // ms
1752
+ pin: px.union([px.boolean(), PxTimelinePinSchema]).optional(),
1753
+ range: PxScrollRangeSchema.optional()
1754
+ };
1755
+ var PxScrollTimelineSchema = implementsInterface()(
1756
+ px.object(__spreadValues({ type: px.literal("scroll") }, scrollishTimelineShape))
1757
+ );
1758
+ var PxViewTimelineSchema = implementsInterface()(
1759
+ px.object(__spreadValues({ type: px.literal("view") }, scrollishTimelineShape))
1760
+ );
1761
+ var PxTimelineSchema = px.discriminatedUnion("type", [
1762
+ PxTimeTimelineSchema,
1763
+ // first = the member an absent `type` selects
1764
+ PxScrollTimelineSchema,
1765
+ PxViewTimelineSchema
1766
+ ]);
1767
+ var PxAnimatorConfigSchema = implementsInterface()(px.object({
1768
+ // (`mode`, `duration` and `frameRate` live INSIDE `timeline` on the wire — §2.8; they exist
1769
+ // at this level only on the runtime view, like the rest of the playback dynamics.)
1770
+ // THE spelling of "what advances progress" — clock / scroll / view (review §2.1).
1771
+ timeline: PxTimelineSchema.optional(),
1208
1772
  definitions: PxDefsSchema.optional(),
1209
1773
  animateById: px.record(PxElementAnimationSchema).optional(),
1210
- timelineSource: px.string().optional(),
1211
- scroll: PxScrollSchema.optional(),
1212
- debugInstName: px.string().optional()
1774
+ debugGlobalName: px.string().optional(),
1775
+ // Declared HERE because this is a closed object: an undeclared key would be stripped by
1776
+ // `sanitize` and flagged by strict validation on our own files.
1777
+ version: px.string().optional()
1213
1778
  }));
1214
1779
  var PxBindingSchema = implementsInterface()(px.object({
1215
1780
  id: px.string(),
@@ -1227,18 +1792,18 @@ var PxAttrValueSchema = px.union([
1227
1792
  ]);
1228
1793
  var PxAnimatableNumberSchema = px.union([
1229
1794
  px.number(),
1230
- px.object({ value: px.number() }),
1231
- PxPropertyAnimationSchema
1795
+ PxPropertyAnimationSchema,
1796
+ px.object({ value: px.number() })
1232
1797
  ]);
1233
1798
  var PxAnimatableVec2Schema = px.union([
1234
1799
  px.tuple([px.number(), px.number()]),
1235
- px.object({ value: px.tuple([px.number(), px.number()]) }),
1236
- PxPropertyAnimationSchema
1800
+ PxPropertyAnimationSchema,
1801
+ px.object({ value: px.tuple([px.number(), px.number()]) })
1237
1802
  ]);
1238
1803
  var PxAnimatableStringSchema = px.union([
1239
1804
  px.string(),
1240
- px.object({ value: px.string() }),
1241
- PxPropertyAnimationSchema
1805
+ PxPropertyAnimationSchema,
1806
+ px.object({ value: px.string() })
1242
1807
  ]);
1243
1808
  var PxTransformByEffectSchema = implementsInterface()(px.object({
1244
1809
  translate: PxAnimatableVec2Schema.optional(),
@@ -1258,7 +1823,7 @@ var PxRepeaterEffectSchema = implementsInterface()(px.object({
1258
1823
  origin: PxAnimatableVec2Schema.optional()
1259
1824
  }));
1260
1825
  var PxMaskedByEffectSchema = implementsInterface()(px.object({
1261
- sourceId: px.string().optional(),
1826
+ source: px.string().optional(),
1262
1827
  maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha]).optional(),
1263
1828
  maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1264
1829
  maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
@@ -1268,8 +1833,7 @@ var PxMaskedByEffectSchema = implementsInterface()(px.object({
1268
1833
  height: px.number().optional()
1269
1834
  }));
1270
1835
  var PxClipPathEffectSchema = implementsInterface()(px.object({
1271
- d: PxAnimatableStringSchema.optional(),
1272
- animate: PxPropertyAnimationSchema.optional()
1836
+ d: PxAnimatableStringSchema.optional()
1273
1837
  }));
1274
1838
  var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1275
1839
  offset: PxAnimatableNumberSchema.optional(),
@@ -1277,15 +1841,15 @@ var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1277
1841
  subPaths: px.enum([PxStrokeTrimSubPaths.separate, PxStrokeTrimSubPaths.combined]).optional()
1278
1842
  }));
1279
1843
  var PxRetimeEffectSchema = implementsInterface()(px.object({
1280
- sourceId: px.string().optional(),
1281
1844
  start: px.number().optional(),
1282
1845
  stretch: px.number().optional(),
1283
1846
  timeCrop: px.tuple([px.number(), px.number()]).optional()
1284
1847
  }));
1285
1848
  var PxCloneEffectSchema = implementsInterface()(px.object({
1286
- // Contextual kind — the `type` convention, see `PxNodeBase.type`.
1287
- type: px.enum([PxCloneType.content]).optional(),
1288
- sourceId: px.string().optional(),
1849
+ // Subtractive on purpose: the `<use>` can only point at one wrapper layer of the
1850
+ // source, so the choices form a ladder — 'translate' now, maybe 'transform' later.
1851
+ without: px.enum([PxCloneWithout.translate]).optional(),
1852
+ source: px.string().optional(),
1289
1853
  retime: PxRetimeEffectSchema.optional()
1290
1854
  }));
1291
1855
  var PxGradientStopSchema = implementsInterface()(px.object({
@@ -1300,11 +1864,11 @@ var PxAnimatableGradientStopsSchema = px.union([
1300
1864
  var PxFillGradientEffectSchema = implementsInterface()(px.object({
1301
1865
  // Contextual kind — the `type` convention, see `PxNodeBase.type`.
1302
1866
  type: px.enum([PxGradientType.linear, PxGradientType.radial]),
1303
- p1: PxAnimatableVec2Schema.optional(),
1304
- p2: PxAnimatableVec2Schema.optional(),
1305
- c: PxAnimatableVec2Schema.optional(),
1306
- r: PxAnimatableNumberSchema.optional(),
1307
- fp: PxAnimatableVec2Schema.optional(),
1867
+ start: PxAnimatableVec2Schema.optional(),
1868
+ end: PxAnimatableVec2Schema.optional(),
1869
+ center: PxAnimatableVec2Schema.optional(),
1870
+ radius: PxAnimatableNumberSchema.optional(),
1871
+ focal: PxAnimatableVec2Schema.optional(),
1308
1872
  stops: PxAnimatableGradientStopsSchema.optional(),
1309
1873
  gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
1310
1874
  spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
@@ -1312,7 +1876,7 @@ var PxFillGradientEffectSchema = implementsInterface()(px.object({
1312
1876
  }));
1313
1877
  var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
1314
1878
  var PxTextPathEffectSchema = implementsInterface()(px.object({
1315
- path: px.string(),
1879
+ pathData: px.string(),
1316
1880
  pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
1317
1881
  lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
1318
1882
  method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
@@ -1352,16 +1916,33 @@ function validateNodeEffects(root, opts) {
1352
1916
  walk(root, "root");
1353
1917
  return warnings;
1354
1918
  }
1919
+ function validateDocument(doc) {
1920
+ const ctx = { errors: [], warnings: [], strict: true };
1921
+ const problems = PxAnimatedSvgDocumentSchema.isValid(doc, ctx, ["root"]) ? [] : [...ctx.errors];
1922
+ if (doc && typeof doc === "object") {
1923
+ for (const w of validateNodeEffects(doc, { strict: true })) {
1924
+ if (!problems.includes(w)) problems.push(w);
1925
+ }
1926
+ }
1927
+ return problems;
1928
+ }
1355
1929
  var PxNodeBase = px.openObject({
1356
1930
  // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for "what
1357
1931
  // kind of thing is this", discriminated by its CARRIER — here the node TAG
1358
- // (`rect`, `text`), and inside a sub-object that object's kind (`clone.type`,
1932
+ // (`rect`, `text`), and inside a sub-object that object's kind (`fillGradient.type`,
1359
1933
  // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so
1360
1934
  // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)
1361
1935
  // would add words that all mean "type" and still need the carrier to read.
1362
1936
  // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums
1363
1937
  // (issues V3), never of distinct key names.
1364
1938
  type: px.string(),
1939
+ // The escape hatch for elements that carry a REAL `type` attribute — `<feTurbulence
1940
+ // type="fractalNoise">`, `<feFuncR type="table">`, `<feColorMatrix type="saturate">`.
1941
+ // `type` is taken by the tag name, so the attribute travels here and the renderer puts
1942
+ // it back (`PxAnimatorDOM.renderNode`, `PxRnRender`). Declared here — not merely
1943
+ // documented — because a wire key that is not in a schema is invisible to the
1944
+ // minifier's reserve list and gets renamed (MINIFICATION-BOUNDARY-PLAN.md §1.1).
1945
+ domType: px.string().optional(),
1365
1946
  id: px.string().optional(),
1366
1947
  meta: px.any().optional(),
1367
1948
  // Player-effects bucket emitted by the Editor's lightweight design format.
@@ -1401,7 +1982,7 @@ function isPxElementFileFormatDeep(fileJson) {
1401
1982
  return { valid, errors: valid ? [] : ["Document failed schema validation"] };
1402
1983
  }
1403
1984
 
1404
- // src/PxIdUtil.ts
1985
+ // src/util/PxIdUtil.ts
1405
1986
  var _idCounter = 0;
1406
1987
  function generateUniqueId() {
1407
1988
  const timestamp = Date.now().toString(36);
@@ -1420,7 +2001,7 @@ function deepClone(value) {
1420
2001
  return cloned;
1421
2002
  }
1422
2003
  function generateNewIds(doc) {
1423
- var _a, _b;
2004
+ var _a2, _b;
1424
2005
  const cloned = deepClone(doc);
1425
2006
  const idMap = /* @__PURE__ */ new Map();
1426
2007
  const hashRefAttrs = /* @__PURE__ */ new Set(["href", "xlink:href"]);
@@ -1438,7 +2019,8 @@ function generateNewIds(doc) {
1438
2019
  "flood-color",
1439
2020
  "lighting-color"
1440
2021
  ]);
1441
- const directIdRefAttrs = /* @__PURE__ */ new Set(["sourceId", "targetId", "boundElementId"]);
2022
+ const directIdRefAttrs = /* @__PURE__ */ new Set(["targetId", "boundElementId"]);
2023
+ const isEffectSourceRef = (key, parentKey) => key === "source" && (parentKey === "maskedBy" || parentKey === "clone");
1442
2024
  function collectIds(node) {
1443
2025
  if (!node || typeof node !== "object") return;
1444
2026
  if (node.id && typeof node.id === "string") {
@@ -1455,7 +2037,7 @@ function generateNewIds(doc) {
1455
2037
  }
1456
2038
  }
1457
2039
  }
1458
- function updateRefs(node) {
2040
+ function updateRefs(node, parentKey) {
1459
2041
  if (!node || typeof node !== "object") return;
1460
2042
  for (const [key, value] of Object.entries(node)) {
1461
2043
  if (key === "children") {
@@ -1475,7 +2057,7 @@ function generateNewIds(doc) {
1475
2057
  }
1476
2058
  } else if (urlRefAttrs.has(key)) {
1477
2059
  node[key] = replaceUrlRefs(value, idMap);
1478
- } else if (directIdRefAttrs.has(key)) {
2060
+ } else if (directIdRefAttrs.has(key) || isEffectSourceRef(key, parentKey)) {
1479
2061
  const hasHash = value.startsWith("#");
1480
2062
  const newId = idMap.get(hasHash ? value.slice(1) : value);
1481
2063
  if (newId) {
@@ -1491,18 +2073,20 @@ function generateNewIds(doc) {
1491
2073
  }
1492
2074
  }
1493
2075
  } else if (typeof value === "object" && value !== null) {
1494
- updateRefs(value);
2076
+ updateRefs(value, key);
1495
2077
  }
1496
2078
  }
1497
2079
  }
1498
2080
  collectIds(cloned);
1499
2081
  updateRefs(cloned);
1500
- const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animateById;
2082
+ const docAnimate = (_a2 = cloned.animator) == null ? void 0 : _a2.animateById;
1501
2083
  if (docAnimate && typeof docAnimate === "object") {
1502
2084
  const updatedAnimate = {};
1503
- for (const [id, anim] of Object.entries(docAnimate)) {
2085
+ for (const [key, anim] of Object.entries(docAnimate)) {
2086
+ const hashed = key.startsWith("#");
2087
+ const id = hashed ? key.slice(1) : key;
1504
2088
  const newId = (_b = idMap.get(id)) != null ? _b : id;
1505
- updatedAnimate[newId] = anim;
2089
+ updatedAnimate[hashed ? "#" + newId : newId] = anim;
1506
2090
  }
1507
2091
  cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animateById: updatedAnimate });
1508
2092
  }
@@ -1515,7 +2099,7 @@ function replaceUrlRefs(value, idMap) {
1515
2099
  });
1516
2100
  }
1517
2101
 
1518
- // src/PxNodeProps.ts
2102
+ // src/util/PxNodeProps.ts
1519
2103
  var DISALLOWED_SVG_TAGS_LOWER = /* @__PURE__ */ new Set([
1520
2104
  "script",
1521
2105
  "foreignobject"
@@ -1579,10 +2163,10 @@ function sanitiseAttributeValue(name, value) {
1579
2163
  return value;
1580
2164
  }
1581
2165
  function resolveStyle(style, defs) {
1582
- var _a;
2166
+ var _a2;
1583
2167
  if (!style) return void 0;
1584
2168
  if (typeof style === "string") {
1585
- return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
2169
+ return (_a2 = defs == null ? void 0 : defs.styles) == null ? void 0 : _a2[style];
1586
2170
  }
1587
2171
  return style;
1588
2172
  }
@@ -1595,16 +2179,16 @@ function getNormalizedProps(props) {
1595
2179
  let value = props[rawKey];
1596
2180
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
1597
2181
  propsCopy[key] = toRGBA(value);
1598
- } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes && !value.kfs) {
2182
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes) {
1599
2183
  const parts = value.value && typeof value.value === "object" ? value.value : value;
1600
- propsCopy["transform"] = composeTransformParts(parts, { withUnits: false });
2184
+ propsCopy[TRANSFORM_ATTR] = composeTransformParts(parts, { withUnits: false });
1601
2185
  } else if (TRANSFORM_FN_NAMES.has(key)) {
1602
2186
  if (Array.isArray(value)) {
1603
2187
  if (key === "translate") value = value.map((v) => v + "px");
1604
2188
  value = value.join(",");
1605
2189
  }
1606
2190
  if (key === "rotate") value = value + "deg";
1607
- propsCopy["transform"] = key + "(" + value + ")";
2191
+ propsCopy[TRANSFORM_ATTR] = key + "(" + value + ")";
1608
2192
  } else if (Array.isArray(value)) {
1609
2193
  propsCopy[key] = value.join(",");
1610
2194
  } else if (value !== void 0 && value !== null) {
@@ -1614,10 +2198,9 @@ function getNormalizedProps(props) {
1614
2198
  return propsCopy;
1615
2199
  }
1616
2200
 
1617
- // src/PxMotionPath.ts
2201
+ // src/materialise/PxMotionPath.ts
1618
2202
  function getKfTranslate(kf) {
1619
- var _a;
1620
- const v = (_a = kf.value) != null ? _a : kf.v;
2203
+ const v = kfValue(kf);
1621
2204
  if (!v) return void 0;
1622
2205
  if (Array.isArray(v) && v.length >= 2 && typeof v[0] === "number" && typeof v[1] === "number") {
1623
2206
  return [v[0], v[1]];
@@ -1627,31 +2210,27 @@ function getKfTranslate(kf) {
1627
2210
  return void 0;
1628
2211
  }
1629
2212
  function getKfTime(kf) {
1630
- var _a, _b;
1631
- return (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
2213
+ return kfTime(kf);
1632
2214
  }
1633
2215
  function getKfEasing(kf) {
1634
- var _a;
1635
- return (_a = kf.easing) != null ? _a : kf.e;
2216
+ return kfEasing(kf);
1636
2217
  }
1637
2218
  function propAnimIsMotionPath(anim) {
1638
- var _a, _b, _c;
1639
- const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
2219
+ const kfs = anim.keyframes;
1640
2220
  if (!Array.isArray(kfs)) return false;
1641
2221
  if (anim.autoOrient) return true;
1642
2222
  for (const kf of kfs) {
1643
- if (((_b = kf.tangentIn) != null ? _b : kf.ti) || ((_c = kf.tangentOut) != null ? _c : kf.to)) return true;
2223
+ if (kfTangentIn(kf) || kfTangentOut(kf)) return true;
1644
2224
  }
1645
2225
  return false;
1646
2226
  }
1647
2227
  var _segmentCache = /* @__PURE__ */ new WeakMap();
1648
2228
  function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
1649
- var _a, _b;
1650
2229
  let byNext = _segmentCache.get(prevKf);
1651
2230
  const existing = byNext == null ? void 0 : byNext.get(nextKf);
1652
2231
  if (existing) return existing;
1653
- const to = (_a = prevKf.tangentOut) != null ? _a : prevKf.to;
1654
- const ti = (_b = nextKf.tangentIn) != null ? _b : nextKf.ti;
2232
+ const to = kfTangentOut(prevKf);
2233
+ const ti = kfTangentIn(nextKf);
1655
2234
  const P1 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];
1656
2235
  const P2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];
1657
2236
  const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);
@@ -1701,14 +2280,14 @@ var DEFAULT_FLATNESS_TOL = 0.5;
1701
2280
  var DEFAULT_ROTATION_TOL = 5;
1702
2281
  var DEFAULT_MAX_SAMPLES = 32;
1703
2282
  function materialiseMotionPathInPropAnim(anim, opts) {
1704
- var _a, _b, _c, _d;
2283
+ var _a2, _b, _c;
1705
2284
  if (!propAnimIsMotionPath(anim)) return anim;
1706
- const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
2285
+ const kfs = anim.keyframes;
1707
2286
  if (!Array.isArray(kfs) || kfs.length < 2) return anim;
1708
2287
  const autoOrient = !!anim.autoOrient;
1709
- const flatnessTol = (_b = opts == null ? void 0 : opts.flatnessTolerance) != null ? _b : DEFAULT_FLATNESS_TOL;
1710
- const rotationTol = (_c = opts == null ? void 0 : opts.rotationTolerance) != null ? _c : DEFAULT_ROTATION_TOL;
1711
- const maxSamples = (_d = opts == null ? void 0 : opts.maxSamplesPerSegment) != null ? _d : DEFAULT_MAX_SAMPLES;
2288
+ const flatnessTol = (_a2 = opts == null ? void 0 : opts.flatnessTolerance) != null ? _a2 : DEFAULT_FLATNESS_TOL;
2289
+ const rotationTol = (_b = opts == null ? void 0 : opts.rotationTolerance) != null ? _b : DEFAULT_ROTATION_TOL;
2290
+ const maxSamples = (_c = opts == null ? void 0 : opts.maxSamplesPerSegment) != null ? _c : DEFAULT_MAX_SAMPLES;
1712
2291
  const out = [];
1713
2292
  const firstPos = getKfTranslate(kfs[0]);
1714
2293
  if (!firstPos) return anim;
@@ -1737,15 +2316,14 @@ function materialiseMotionPathInPropAnim(anim, opts) {
1737
2316
  const lastInE = getKfEasing(kfs[kfs.length - 1]);
1738
2317
  if (lastInE) out[out.length - 1].e = lastInE;
1739
2318
  if (autoOrient) unwrapAutoOrientRotations(out);
1740
- const result = { kfs: out };
2319
+ const result = { keyframes: out };
1741
2320
  if (anim.loop !== void 0) result.loop = anim.loop;
1742
2321
  return result;
1743
2322
  }
1744
2323
  function unwrapAutoOrientRotations(kfs) {
1745
- var _a;
1746
2324
  let prev;
1747
2325
  for (const kf of kfs) {
1748
- const v = (_a = kf.v) != null ? _a : kf.value;
2326
+ const v = kfValue(kf);
1749
2327
  if (!v || typeof v.rotate !== "number") continue;
1750
2328
  if (prev === void 0) {
1751
2329
  prev = v.rotate;
@@ -1762,8 +2340,7 @@ function makeOutKf(time, value) {
1762
2340
  return { t: time, v: value };
1763
2341
  }
1764
2342
  function getKfValueParts(kf) {
1765
- var _a;
1766
- const v = (_a = kf.value) != null ? _a : kf.v;
2343
+ const v = kfValue(kf);
1767
2344
  if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
1768
2345
  return v;
1769
2346
  }
@@ -1824,9 +2401,8 @@ function wrappedAngleDelta(a, b) {
1824
2401
  return d;
1825
2402
  }
1826
2403
  function insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol) {
1827
- var _a;
1828
2404
  const lastKf = out[out.length - 1];
1829
- const lastV = (_a = lastKf.v) != null ? _a : lastKf.value;
2405
+ const lastV = kfValue(lastKf);
1830
2406
  const prevExit = lastV == null ? void 0 : lastV.rotate;
1831
2407
  if (typeof prevExit !== "number") return;
1832
2408
  const boundaryV = getKfValueParts(prevKf);
@@ -2007,7 +2583,7 @@ function walkAndMaterialise(node, opts) {
2007
2583
  return cloned;
2008
2584
  }
2009
2585
 
2010
- // src/PxDefinitions.ts
2586
+ // src/animation/PxDefinitions.ts
2011
2587
  var LOOP_JUMP_SHIFT_MS = 1;
2012
2588
  function deepEqualValue(a, b) {
2013
2589
  if (a === b) return true;
@@ -2139,21 +2715,21 @@ function normalizePathValue(value) {
2139
2715
  return value;
2140
2716
  }
2141
2717
  function resolveEasing(easing, defs) {
2142
- var _a;
2718
+ var _a2;
2143
2719
  if (!easing) return void 0;
2144
2720
  if (Array.isArray(easing)) {
2145
2721
  return easing;
2146
2722
  }
2147
- if ((_a = defs == null ? void 0 : defs.easings) == null ? void 0 : _a[easing]) {
2723
+ if ((_a2 = defs == null ? void 0 : defs.easings) == null ? void 0 : _a2[easing]) {
2148
2724
  return defs.easings[easing];
2149
2725
  }
2150
2726
  console.warn("Unknown easing name: " + easing);
2151
2727
  return void 0;
2152
2728
  }
2153
2729
  function resolveAnimation(animRef, defs) {
2154
- var _a;
2730
+ var _a2;
2155
2731
  if (typeof animRef === "string") {
2156
- const resolved = (_a = defs == null ? void 0 : defs.animations) == null ? void 0 : _a[animRef];
2732
+ const resolved = (_a2 = defs == null ? void 0 : defs.animations) == null ? void 0 : _a2[animRef];
2157
2733
  if (!resolved) {
2158
2734
  console.warn("Unknown animation name: " + animRef);
2159
2735
  }
@@ -2178,9 +2754,9 @@ function resolveElementAnimation(animate, defs) {
2178
2754
  return results;
2179
2755
  }
2180
2756
  function interpolateValue(propName, a, b, t) {
2181
- var _a, _b;
2757
+ var _a2, _b;
2182
2758
  if (propName === "d") {
2183
- const aPaths = (_a = a == null ? void 0 : a.paths) != null ? _a : Array.isArray(a) ? a : [];
2759
+ const aPaths = (_a2 = a == null ? void 0 : a.paths) != null ? _a2 : Array.isArray(a) ? a : [];
2184
2760
  const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
2185
2761
  return { paths: interpolateBeziers(aPaths, bPaths, t) };
2186
2762
  }
@@ -2216,11 +2792,11 @@ function interpolateTransformParts(a, b, t) {
2216
2792
  return out;
2217
2793
  }
2218
2794
  function expandLoopKeyframes(propName, keyframes, loop, duration) {
2219
- var _a, _b, _c, _d, _e;
2795
+ var _a2, _b, _c, _d, _e;
2220
2796
  const totalIntervals = keyframes.length - 1;
2221
- const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
2797
+ const segCount = clamp((_a2 = loop.segmentCount) != null ? _a2 : totalIntervals, 1, totalIntervals);
2222
2798
  let segKfs;
2223
- if (loop.extend === PxLoopExtend.before) {
2799
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2224
2800
  segKfs = keyframes.slice(0, segCount + 1);
2225
2801
  } else {
2226
2802
  segKfs = keyframes.slice(totalIntervals - segCount);
@@ -2228,7 +2804,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2228
2804
  const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2229
2805
  const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2230
2806
  let fillStart, fillEnd;
2231
- if (loop.extend === PxLoopExtend.before) {
2807
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2232
2808
  fillStart = 0;
2233
2809
  fillEnd = firstT;
2234
2810
  } else {
@@ -2241,26 +2817,23 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2241
2817
  const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
2242
2818
  const segDuration = segEndT - segStartT;
2243
2819
  if (segDuration <= 0) return keyframes;
2244
- const template = segKfs.map((kf) => {
2245
- var _a2, _b2;
2246
- return {
2247
- relT: (kf.t - segStartT) / segDuration,
2248
- v: kf.v,
2249
- e: kf.e,
2250
- tangentIn: (_a2 = kf.tangentIn) != null ? _a2 : kf.ti,
2251
- tangentOut: (_b2 = kf.tangentOut) != null ? _b2 : kf.to
2252
- };
2253
- });
2820
+ const template = segKfs.map((kf) => ({
2821
+ relT: (kf.t - segStartT) / segDuration,
2822
+ v: kf.v,
2823
+ e: kf.e,
2824
+ tangentIn: kfTangentIn(kf),
2825
+ tangentOut: kfTangentOut(kf)
2826
+ }));
2254
2827
  const fullReps = Math.floor(fillDuration / segDuration);
2255
2828
  const remainder = fillDuration - fullReps * segDuration;
2256
2829
  const partialFraction = remainder / segDuration;
2257
2830
  const looped = [];
2258
- const separateBoundary = loop.extend !== PxLoopExtend.before;
2831
+ const separateBoundary = loop.repeatAt !== PxLoopRepeatAt.start;
2259
2832
  const originalTerminalKf = keyframes[keyframes.length - 1];
2260
2833
  let terminalEasingOverride;
2261
2834
  let hasTerminalEasingOverride = false;
2262
2835
  function appendRep(repStart, isReversed, partial) {
2263
- var _a2;
2836
+ var _a3;
2264
2837
  let entries;
2265
2838
  if (isReversed) {
2266
2839
  entries = [];
@@ -2297,7 +2870,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2297
2870
  return;
2298
2871
  }
2299
2872
  const prevKf = looped.length > 0 ? looped[looped.length - 1] : originalTerminalKf;
2300
- const isBoundary = separateBoundary && i === 0 && prevKf !== void 0 && Math.abs(((_a2 = prevKf.t) != null ? _a2 : 0) - (repStart + entry.relT * segDuration)) < 1e-9;
2873
+ const isBoundary = separateBoundary && i === 0 && prevKf !== void 0 && Math.abs(((_a3 = prevKf.t) != null ? _a3 : 0) - (repStart + entry.relT * segDuration)) < 1e-9;
2301
2874
  if (isBoundary) {
2302
2875
  if (deepEqualValue(prevKf.v, entry.v)) {
2303
2876
  if (looped.length > 0) {
@@ -2363,30 +2936,30 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2363
2936
  looped.push(pushed);
2364
2937
  }
2365
2938
  }
2366
- if (loop.extend === PxLoopExtend.before) {
2939
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2367
2940
  if (partialFraction > 1e-9) {
2368
- const isReversed = !!loop.alternate && fullReps % 2 === 0;
2941
+ const isReversed = loop.direction === PxLoopDirection.alternate && fullReps % 2 === 0;
2369
2942
  appendRepTail(fillStart, isReversed, partialFraction);
2370
2943
  }
2371
2944
  for (let rep = 0; rep < fullReps; rep++) {
2372
2945
  const distFromBoundary = fullReps - 1 - rep;
2373
- const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
2946
+ const isReversed = loop.direction === PxLoopDirection.alternate && distFromBoundary % 2 === 0;
2374
2947
  const repStart = fillStart + remainder + rep * segDuration;
2375
2948
  appendRep(repStart, isReversed);
2376
2949
  }
2377
2950
  } else {
2378
2951
  for (let rep = 0; rep < fullReps; rep++) {
2379
- const isReversed = !!loop.alternate && rep % 2 === 0;
2952
+ const isReversed = loop.direction === PxLoopDirection.alternate && rep % 2 === 0;
2380
2953
  const repStart = fillStart + rep * segDuration;
2381
2954
  appendRep(repStart, isReversed);
2382
2955
  }
2383
2956
  if (partialFraction > 1e-9) {
2384
- const isReversed = !!loop.alternate && fullReps % 2 === 0;
2957
+ const isReversed = loop.direction === PxLoopDirection.alternate && fullReps % 2 === 0;
2385
2958
  const repStart = fillStart + fullReps * segDuration;
2386
2959
  appendRep(repStart, isReversed, partialFraction);
2387
2960
  }
2388
2961
  }
2389
- if (loop.extend === PxLoopExtend.before) {
2962
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2390
2963
  return [...looped, ...keyframes];
2391
2964
  } else {
2392
2965
  if (hasTerminalEasingOverride && keyframes.length > 0) {
@@ -2398,34 +2971,34 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2398
2971
  }
2399
2972
  }
2400
2973
  function normalizeKeyframes(propName, propAnim, duration, defs) {
2401
- var _a, _b, _c, _d, _e, _f, _g;
2402
- const keyframes = propAnim.keyframes || propAnim.kfs || [];
2974
+ var _a2;
2975
+ const keyframes = propAnim.keyframes || [];
2403
2976
  const normalized = [];
2404
2977
  for (const kf of keyframes) {
2405
- const timePct = (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
2406
- let value = (_c = kf.value) != null ? _c : kf.v;
2407
- const easing = (_d = kf.easing) != null ? _d : kf.e;
2978
+ const timePct = kfTime(kf);
2979
+ let value = kfValue(kf);
2980
+ const easing = kfEasing(kf);
2408
2981
  if (propName === "d") {
2409
2982
  value = normalizePathValue(value);
2410
2983
  }
2411
2984
  const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2412
2985
  if (COLOUR_ATTR_NAMES.has(propNameKebab)) {
2413
- value = (_e = parseColor(value)) != null ? _e : value;
2986
+ value = (_a2 = parseColor(value)) != null ? _a2 : value;
2414
2987
  }
2415
2988
  const normKf = {
2416
2989
  t: timePct,
2417
2990
  v: value,
2418
2991
  e: resolveEasing(easing, defs)
2419
2992
  };
2420
- const tIn = (_f = kf.tangentIn) != null ? _f : kf.ti;
2421
- const tOut = (_g = kf.tangentOut) != null ? _g : kf.to;
2993
+ const tIn = kfTangentIn(kf);
2994
+ const tOut = kfTangentOut(kf);
2422
2995
  if (tIn) normKf.tangentIn = tIn;
2423
2996
  if (tOut) normKf.tangentOut = tOut;
2424
2997
  normalized.push(normKf);
2425
2998
  }
2426
2999
  normalized.sort((a, b) => {
2427
- var _a2, _b2;
2428
- return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
3000
+ var _a3, _b;
3001
+ return ((_a3 = a.t) != null ? _a3 : 0) - ((_b = b.t) != null ? _b : 0);
2429
3002
  });
2430
3003
  const loopRaw = propAnim.loop;
2431
3004
  const loop = loopRaw === true ? {} : loopRaw || void 0;
@@ -2444,28 +3017,29 @@ function mergeAnimationDefinitions(animations) {
2444
3017
  return merged;
2445
3018
  }
2446
3019
  function materialiseInternalLoopsInPropAnim(propName, propAnim, duration) {
2447
- var _a;
2448
3020
  const loopRaw = propAnim.loop;
2449
3021
  if (loopRaw === void 0 || loopRaw === null || loopRaw === false) return propAnim;
2450
3022
  const loop = loopRaw === true ? {} : loopRaw;
2451
- const rawKfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
3023
+ const rawKfs = propAnim.keyframes;
2452
3024
  if (!Array.isArray(rawKfs) || rawKfs.length < 2) return propAnim;
2453
3025
  const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2454
3026
  const isColour = COLOUR_ATTR_NAMES.has(propNameKebab);
2455
3027
  const kfs = rawKfs.map((kf) => {
2456
- var _a2, _b, _c, _d, _e, _f, _g, _h;
2457
- const t = (_a2 = kf.t) != null ? _a2 : kf.time;
2458
- let v = (_b = kf.v) != null ? _b : kf.value;
3028
+ var _a2;
3029
+ const t = kfTime(kf);
3030
+ let v = kfValue(kf);
2459
3031
  if (propName === "d") v = normalizePathValue(v);
2460
- if (isColour) v = (_c = parseColor(v)) != null ? _c : v;
2461
- const e = (_d = kf.e) != null ? _d : kf.easing;
3032
+ if (isColour) v = (_a2 = parseColor(v)) != null ? _a2 : v;
3033
+ const e = kfEasing(kf);
2462
3034
  const out2 = { t, v, e };
2463
- if ((_e = kf.tangentIn) != null ? _e : kf.ti) out2.tangentIn = (_f = kf.tangentIn) != null ? _f : kf.ti;
2464
- if ((_g = kf.tangentOut) != null ? _g : kf.to) out2.tangentOut = (_h = kf.tangentOut) != null ? _h : kf.to;
3035
+ const tIn = kfTangentIn(kf);
3036
+ const tOut = kfTangentOut(kf);
3037
+ if (tIn) out2.tangentIn = tIn;
3038
+ if (tOut) out2.tangentOut = tOut;
2465
3039
  return out2;
2466
3040
  });
2467
3041
  const expanded = expandLoopKeyframes(propName, kfs, loop, duration);
2468
- const out = { kfs: expanded };
3042
+ const out = { keyframes: expanded };
2469
3043
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2470
3044
  return out;
2471
3045
  }
@@ -2507,7 +3081,37 @@ var _elementIdCounter = 0;
2507
3081
  function generateElementId() {
2508
3082
  return "_px_el_" + ++_elementIdCounter;
2509
3083
  }
2510
- function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.waapi) {
3084
+ function mergeStaticTransformIntoAnimDef(animDef, staticTransform) {
3085
+ if (!animDef) return animDef;
3086
+ const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
3087
+ if (!staticParts || !Object.keys(staticParts).length) return animDef;
3088
+ const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues(__spreadValues({}, staticParts), v) : v;
3089
+ const transformAnim = animDef[TRANSFORM_ATTR];
3090
+ if (transformAnim && typeof transformAnim === "object") {
3091
+ const anim = transformAnim;
3092
+ if (Array.isArray(anim.keyframes)) {
3093
+ const out = __spreadProps(__spreadValues({}, anim), {
3094
+ keyframes: anim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: mergeKfValue(kf.value) }))
3095
+ });
3096
+ if (out.value !== void 0) out.value = mergeKfValue(out.value);
3097
+ return __spreadProps(__spreadValues({}, animDef), { transform: out });
3098
+ }
3099
+ return animDef;
3100
+ }
3101
+ const channels = Object.keys(animDef).filter((k) => TRANSFORM_FN_NAMES.has(k));
3102
+ if (channels.length !== 1) return animDef;
3103
+ const ch = channels[0];
3104
+ const chAnim = animDef[ch];
3105
+ if (!chAnim || typeof chAnim !== "object" || !Array.isArray(chAnim.keyframes)) return animDef;
3106
+ const lifted = __spreadProps(__spreadValues({}, chAnim), {
3107
+ keyframes: chAnim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: __spreadProps(__spreadValues({}, staticParts), { [ch]: kf.value }) }))
3108
+ });
3109
+ if (lifted.value !== void 0) lifted.value = __spreadProps(__spreadValues({}, staticParts), { [ch]: lifted.value });
3110
+ const rest = __spreadValues({}, animDef);
3111
+ delete rest[ch];
3112
+ return __spreadProps(__spreadValues({}, rest), { transform: lifted });
3113
+ }
3114
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxTimelineEngine.native) {
2511
3115
  const normalized = {};
2512
3116
  for (const [propName, propAnim] of Object.entries(animDef)) {
2513
3117
  if (propName === "transform" && propAnim.alongPathMode === "offsetPath" && animDef["offsetDistance"] !== void 0) {
@@ -2515,24 +3119,24 @@ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimat
2515
3119
  }
2516
3120
  const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2517
3121
  if (normalizedKfs.length > 0) {
2518
- const out = { kfs: normalizedKfs };
3122
+ const out = { keyframes: normalizedKfs };
2519
3123
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2520
3124
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2521
- normalized[propName] = engine === PxAnimatorEngine.waapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
3125
+ normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2522
3126
  }
2523
3127
  }
2524
3128
  return normalized;
2525
3129
  }
2526
- function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
3130
+ function getNormalisedBindings(doc, engine = PxTimelineEngine.native) {
2527
3131
  const animatorConfig = getAnimatorConfig(doc) || {};
2528
3132
  const defs = getDefs(doc);
2529
3133
  const duration = animatorConfig.duration || 1e3;
2530
3134
  const bindings = [];
2531
- const processAnimation = (id, animate) => {
3135
+ const processAnimation = (id, animate, staticTransform) => {
2532
3136
  if (!animate) return null;
2533
3137
  const animDefs = resolveElementAnimation(animate, defs);
2534
3138
  if (animDefs.length === 0) return null;
2535
- const merged = mergeAnimationDefinitions(animDefs);
3139
+ const merged = mergeStaticTransformIntoAnimDef(mergeAnimationDefinitions(animDefs), staticTransform);
2536
3140
  const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);
2537
3141
  if (Object.keys(normalizedAnim).length === 0) return null;
2538
3142
  return {
@@ -2552,7 +3156,7 @@ function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2552
3156
  if (inlineAnim && Object.keys(inlineAnim).length > 0) {
2553
3157
  const nodeId = node.id || generateElementId();
2554
3158
  node.id = nodeId;
2555
- const normalized = processAnimation(nodeId, inlineAnim);
3159
+ const normalized = processAnimation(nodeId, inlineAnim, node.transform);
2556
3160
  if (normalized) bindings.push(normalized);
2557
3161
  }
2558
3162
  if (node.children) {
@@ -2569,12 +3173,12 @@ function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2569
3173
  return bindings;
2570
3174
  }
2571
3175
  function getKeyframesPair(keyframes, progress) {
2572
- var _a, _b;
3176
+ var _a2, _b;
2573
3177
  const last = keyframes.length - 1;
2574
3178
  let prevKf = keyframes[0];
2575
3179
  let nextKf = keyframes[last > 0 ? 1 : 0];
2576
3180
  for (let j = 0; j < last; j++) {
2577
- const aOff = (_a = keyframes[j].t) != null ? _a : 0;
3181
+ const aOff = (_a2 = keyframes[j].t) != null ? _a2 : 0;
2578
3182
  const bOff = (_b = keyframes[j + 1].t) != null ? _b : 0;
2579
3183
  if (aOff <= progress && progress <= bOff) {
2580
3184
  prevKf = keyframes[j];
@@ -2589,13 +3193,13 @@ function getKeyframesPair(keyframes, progress) {
2589
3193
  return { prevKf, nextKf };
2590
3194
  }
2591
3195
  function calcPropertyValue(propName, propAnim, progress) {
2592
- var _a, _b, _c, _d, _e, _f, _g;
2593
- const keyframes = propAnim.kfs || propAnim.keyframes || [];
3196
+ var _a2, _b, _c, _d;
3197
+ const keyframes = propAnim.keyframes || [];
2594
3198
  if (keyframes.length === 0) return null;
2595
3199
  const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
2596
- let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a = prevKf.t) != null ? _a : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
3200
+ let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a2 = prevKf.t) != null ? _a2 : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
2597
3201
  localProgress = clamp(localProgress, 0, 1);
2598
- const easing = (_c = prevKf.e) != null ? _c : prevKf.easing;
3202
+ const easing = kfEasing(prevKf);
2599
3203
  if (easing && Array.isArray(easing)) {
2600
3204
  try {
2601
3205
  localProgress = cubicBezier(easing)(localProgress);
@@ -2604,11 +3208,11 @@ function calcPropertyValue(propName, propAnim, progress) {
2604
3208
  }
2605
3209
  let cssAttrName = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2606
3210
  let cssValue = null;
2607
- const prevV = (_d = prevKf == null ? void 0 : prevKf.v) != null ? _d : prevKf == null ? void 0 : prevKf.value;
2608
- const nextV = (_e = nextKf == null ? void 0 : nextKf.v) != null ? _e : nextKf == null ? void 0 : nextKf.value;
3211
+ const prevV = prevKf == null ? void 0 : prevKf.v;
3212
+ const nextV = nextKf == null ? void 0 : nextKf.v;
2609
3213
  if (cssAttrName === "d") {
2610
- const prevPaths = (_f = prevV == null ? void 0 : prevV.paths) != null ? _f : Array.isArray(prevV) ? prevV : [];
2611
- const nextPaths = (_g = nextV == null ? void 0 : nextV.paths) != null ? _g : Array.isArray(nextV) ? nextV : [];
3214
+ const prevPaths = (_c = prevV == null ? void 0 : prevV.paths) != null ? _c : Array.isArray(prevV) ? prevV : [];
3215
+ const nextPaths = (_d = nextV == null ? void 0 : nextV.paths) != null ? _d : Array.isArray(nextV) ? nextV : [];
2612
3216
  cssValue = interpolateBeziers(
2613
3217
  prevPaths,
2614
3218
  nextPaths,
@@ -2711,7 +3315,7 @@ function calcAnimationValues(animDef, progress) {
2711
3315
  return result;
2712
3316
  }
2713
3317
 
2714
- // src/PxNodeCloneUtil.ts
3318
+ // src/util/PxNodeCloneUtil.ts
2715
3319
  function deepClonePxNode(value) {
2716
3320
  if (value === null || typeof value !== "object") return value;
2717
3321
  if (Array.isArray(value)) return value.map(deepClonePxNode);
@@ -2722,13 +3326,13 @@ function deepClonePxNode(value) {
2722
3326
  function regenerateIdsAndRewriteRefs(root, genId3) {
2723
3327
  const oldToNew = /* @__PURE__ */ new Map();
2724
3328
  const walkAssign = (n) => {
2725
- var _a;
3329
+ var _a2;
2726
3330
  if (typeof n.id === "string") {
2727
3331
  const newId = genId3();
2728
3332
  oldToNew.set(n.id, newId);
2729
3333
  n.id = newId;
2730
3334
  }
2731
- (_a = n.children) == null ? void 0 : _a.forEach(walkAssign);
3335
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walkAssign);
2732
3336
  };
2733
3337
  walkAssign(root);
2734
3338
  const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
@@ -2736,7 +3340,7 @@ function regenerateIdsAndRewriteRefs(root, genId3) {
2736
3340
  return newId ? "url(#" + newId + ")" : m;
2737
3341
  });
2738
3342
  const walkRewrite = (n) => {
2739
- var _a;
3343
+ var _a2;
2740
3344
  if (typeof n.href === "string" && n.href.startsWith("#")) {
2741
3345
  const newId = oldToNew.get(n.href.slice(1));
2742
3346
  if (newId) n.href = "#" + newId;
@@ -2748,7 +3352,7 @@ function regenerateIdsAndRewriteRefs(root, genId3) {
2748
3352
  n[k] = rewriteUrl(v);
2749
3353
  }
2750
3354
  }
2751
- (_a = n.children) == null ? void 0 : _a.forEach(walkRewrite);
3355
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walkRewrite);
2752
3356
  };
2753
3357
  walkRewrite(root);
2754
3358
  return oldToNew;
@@ -2758,7 +3362,7 @@ function toFiniteNum(v) {
2758
3362
  return Number.isFinite(n) ? n : 0;
2759
3363
  }
2760
3364
  function applyUseOffsetToG(gNode) {
2761
- var _a;
3365
+ var _a2;
2762
3366
  const x = toFiniteNum(gNode.x);
2763
3367
  const y = toFiniteNum(gNode.y);
2764
3368
  delete gNode.x;
@@ -2767,7 +3371,7 @@ function applyUseOffsetToG(gNode) {
2767
3371
  const offset = "translate(" + x + "," + y + ")";
2768
3372
  const carriesTransform = gNode.transform !== void 0 || gNode.animate !== void 0;
2769
3373
  if (carriesTransform) {
2770
- const inner = { type: "g", transform: offset, children: (_a = gNode.children) != null ? _a : [] };
3374
+ const inner = { type: "g", transform: offset, children: (_a2 = gNode.children) != null ? _a2 : [] };
2771
3375
  gNode.children = [inner];
2772
3376
  } else {
2773
3377
  gNode.transform = offset;
@@ -2775,9 +3379,9 @@ function applyUseOffsetToG(gNode) {
2775
3379
  return gNode;
2776
3380
  }
2777
3381
 
2778
- // src/PxAnimatorUseMaterialiser.ts
3382
+ // src/materialise/PxAnimatorUseMaterialiser.ts
2779
3383
  function materialiseAnimatedUseInstances(root) {
2780
- var _a;
3384
+ var _a2;
2781
3385
  const idMap = buildIdMap(root);
2782
3386
  const animatedIds = computeAnimatedSubtreeIds(root, idMap);
2783
3387
  if (animatedIds.size === 0) return root;
@@ -2788,7 +3392,7 @@ function materialiseAnimatedUseInstances(root) {
2788
3392
  const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector, rootViewport);
2789
3393
  if (defsCollector.length === 0) return walked;
2790
3394
  const defsNode = { type: "defs", children: defsCollector };
2791
- const newChildren = [...(_a = walked.children) != null ? _a : [], defsNode];
3395
+ const newChildren = [...(_a2 = walked.children) != null ? _a2 : [], defsNode];
2792
3396
  return __spreadProps(__spreadValues({}, walked), { children: newChildren });
2793
3397
  }
2794
3398
  function readRootViewport(root) {
@@ -2810,9 +3414,9 @@ function numericAttr(v) {
2810
3414
  function buildIdMap(root) {
2811
3415
  const map = /* @__PURE__ */ new Map();
2812
3416
  const visit = (n) => {
2813
- var _a;
3417
+ var _a2;
2814
3418
  if (typeof n.id === "string") map.set(n.id, n);
2815
- (_a = n.children) == null ? void 0 : _a.forEach(visit);
3419
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(visit);
2816
3420
  };
2817
3421
  visit(root);
2818
3422
  return map;
@@ -2859,11 +3463,11 @@ function stripHash(href) {
2859
3463
  return href.startsWith("#") ? href.slice(1) : href;
2860
3464
  }
2861
3465
  function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector, rootViewport) {
2862
- var _a, _b, _c, _d;
3466
+ var _a2, _b, _c, _d;
2863
3467
  const clone2 = deepClonePxNode(target);
2864
3468
  regenerateIdsAndRewriteRefs(clone2, genId3);
2865
3469
  const symbolViewBox = clone2.type === "symbol" ? parseViewBox(clone2.viewBox) : void 0;
2866
- const useW = (_b = (_a = numericAttr(useNode.width)) != null ? _a : symbolViewBox == null ? void 0 : symbolViewBox[2]) != null ? _b : rootViewport[0];
3470
+ const useW = (_b = (_a2 = numericAttr(useNode.width)) != null ? _a2 : symbolViewBox == null ? void 0 : symbolViewBox[2]) != null ? _b : rootViewport[0];
2867
3471
  const useH = (_d = (_c = numericAttr(useNode.height)) != null ? _c : symbolViewBox == null ? void 0 : symbolViewBox[3]) != null ? _d : rootViewport[1];
2868
3472
  const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector, useW, useH) : clone2;
2869
3473
  const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector, rootViewport);
@@ -2923,7 +3527,7 @@ function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector, ro
2923
3527
  return changed ? __spreadProps(__spreadValues({}, node), { children: newChildren }) : node;
2924
3528
  }
2925
3529
 
2926
- // src/effects/transformParts.ts
3530
+ // src/effects/shared/transformParts.ts
2927
3531
  function partsRecord(part, value, origin) {
2928
3532
  const rec = {};
2929
3533
  if (part === "translate" /* Translate */) rec.translate = value;
@@ -2934,25 +3538,25 @@ function partsRecord(part, value, origin) {
2934
3538
  return rec;
2935
3539
  }
2936
3540
  function readAnimatable(raw) {
2937
- var _a, _b, _c;
3541
+ var _a2, _b;
2938
3542
  if (raw === void 0) return { kind: "absent" /* Absent */ };
2939
3543
  if (Array.isArray(raw)) return { kind: "static" /* Static */, value: raw };
2940
3544
  if (typeof raw === "object") {
2941
3545
  const obj = raw;
2942
- const kfs = (_a = obj.keyframes) != null ? _a : obj.kfs;
3546
+ const kfs = obj.keyframes;
2943
3547
  if (kfs) {
2944
3548
  const out = { kind: "animated" /* Animated */, keyframes: kfs.map(normaliseKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };
2945
- const base = (_b = obj.value) != null ? _b : obj.v;
3549
+ const base = (_a2 = obj.value) != null ? _a2 : obj.v;
2946
3550
  if (base !== void 0 && out.kind === "animated" /* Animated */) out.base = base;
2947
3551
  return out;
2948
3552
  }
2949
- const staticValue = (_c = obj.value) != null ? _c : obj.v;
3553
+ const staticValue = (_b = obj.value) != null ? _b : obj.v;
2950
3554
  if (staticValue !== void 0) return { kind: "static" /* Static */, value: staticValue };
2951
3555
  }
2952
3556
  return { kind: "static" /* Static */, value: raw };
2953
3557
  }
2954
3558
  function writeAnimatableChannel(node, attrName, read, opts) {
2955
- var _a, _b, _c, _d;
3559
+ var _a2, _b, _c, _d;
2956
3560
  const toOut = (v) => (opts == null ? void 0 : opts.asString) && v !== void 0 && v !== null ? String(v) : v;
2957
3561
  if (read.kind === "absent" /* Absent */) return;
2958
3562
  if (read.kind === "static" /* Static */) {
@@ -2966,7 +3570,7 @@ function writeAnimatableChannel(node, attrName, read, opts) {
2966
3570
  if (read.autoOrient !== void 0) block.autoOrient = read.autoOrient;
2967
3571
  animate[attrName] = block;
2968
3572
  node.animate = animate;
2969
- 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;
3573
+ const baseline = (_d = (_b = read.base) != null ? _b : (_a2 = read.keyframes[0]) == null ? void 0 : _a2.value) != null ? _d : (_c = read.keyframes[0]) == null ? void 0 : _c.v;
2970
3574
  if (baseline !== void 0) node[attrName] = toOut(baseline);
2971
3575
  }
2972
3576
  function normaliseKeyframe(kf) {
@@ -2984,12 +3588,12 @@ function normaliseKeyframe(kf) {
2984
3588
  return out;
2985
3589
  }
2986
3590
  function readStaticOrigin(raw, ctx) {
2987
- var _a;
3591
+ var _a2;
2988
3592
  const o = readAnimatable(raw);
2989
3593
  if (o.kind === "absent" /* Absent */) return void 0;
2990
3594
  if (o.kind === "static" /* Static */) return o.value;
2991
3595
  ctx.warnings.push("transformBy.origin: animated origin approximated by its first keyframe");
2992
- return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
3596
+ return (_a2 = o.keyframes[0]) == null ? void 0 : _a2.value;
2993
3597
  }
2994
3598
  function keyframeWith(kf, value) {
2995
3599
  const out = { value };
@@ -3000,7 +3604,7 @@ function keyframeWith(kf, value) {
3000
3604
  return out;
3001
3605
  }
3002
3606
 
3003
- // src/effects/transformationEffect.ts
3607
+ // src/effects/transform/transformationEffect.ts
3004
3608
  function applyTransformByEffect(node, fx, ctx) {
3005
3609
  if (!fx) return node;
3006
3610
  delete node.transform;
@@ -3091,7 +3695,7 @@ function wrapOrigin(inner, raw, invert) {
3091
3695
  return inner;
3092
3696
  }
3093
3697
 
3094
- // src/effects/util.ts
3698
+ // src/effects/shared/util.ts
3095
3699
  function genId(ctx, prefix) {
3096
3700
  return "_lw_" + prefix + "_" + ctx.nextId++;
3097
3701
  }
@@ -3099,9 +3703,9 @@ function stripHash2(href) {
3099
3703
  return typeof href === "string" ? href.replace(/^#/, "") : void 0;
3100
3704
  }
3101
3705
  function indexById(node, map) {
3102
- var _a;
3706
+ var _a2;
3103
3707
  if (typeof node.id === "string") map.set(node.id, node);
3104
- (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
3708
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((child) => indexById(child, map));
3105
3709
  }
3106
3710
  function spliceDefs(root, defs) {
3107
3711
  if (!defs.length) return;
@@ -3113,11 +3717,11 @@ function regenerateIdsInClone(root, ctx) {
3113
3717
  return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
3114
3718
  }
3115
3719
 
3116
- // src/effects/contentRefSplit.ts
3720
+ // src/effects/reference/contentRefSplit.ts
3117
3721
  function identifyContentRefTargets(node, ctx, allocator) {
3118
- var _a, _b, _c;
3119
- if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.type) === "content") {
3120
- const sourceId = stripHash2(node.effects.clone.sourceId);
3722
+ var _a2, _b, _c;
3723
+ if (node.type === "use" && ((_b = (_a2 = node.effects) == null ? void 0 : _a2.clone) == null ? void 0 : _b.without) === "translate") {
3724
+ const sourceId = stripHash2(node.effects.clone.source);
3121
3725
  if (typeof sourceId === "string" && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {
3122
3726
  ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));
3123
3727
  }
@@ -3142,10 +3746,10 @@ function splitForContentRef(node, transformBy, originalId, innerId, ctx) {
3142
3746
  return outerWrapper;
3143
3747
  }
3144
3748
  function liftBodyTranslate(node, transformBy) {
3145
- var _a, _b, _c;
3749
+ var _a2, _b, _c;
3146
3750
  const out = {};
3147
3751
  let didLiftAnimate = false;
3148
- const animTr = (_a = node.animate) == null ? void 0 : _a.transform;
3752
+ const animTr = (_a2 = node.animate) == null ? void 0 : _a2.transform;
3149
3753
  if (animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes)) {
3150
3754
  const kfs = animTr.keyframes;
3151
3755
  const hasTranslate = kfs.some((kf) => kf.value && kf.value.translate);
@@ -3214,7 +3818,7 @@ function liftBodyTranslate(node, transformBy) {
3214
3818
  if (split.rest) node.transform = split.rest;
3215
3819
  else delete node.transform;
3216
3820
  }
3217
- } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes && !node.transform.kfs) {
3821
+ } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes) {
3218
3822
  const wrapped = node.transform.value;
3219
3823
  const isWrapped = !!(wrapped && typeof wrapped === "object");
3220
3824
  const value = isWrapped ? wrapped : node.transform;
@@ -3328,7 +3932,7 @@ function needsOriginOnOuter(translateAnim) {
3328
3932
  return false;
3329
3933
  }
3330
3934
 
3331
- // src/effects/gradientEffect.ts
3935
+ // src/effects/paint/gradientEffect.ts
3332
3936
  function applyFillGradientEffect(node, fx, ctx) {
3333
3937
  return applyGradient(node, fx, ctx, "fill");
3334
3938
  }
@@ -3349,12 +3953,12 @@ function synthesiseGradientDef(fx, id, ctx) {
3349
3953
  id
3350
3954
  };
3351
3955
  if (fx.type === PxGradientType.linear) {
3352
- applyGeomVec(out, "x1", "y1", fx.p1);
3353
- applyGeomVec(out, "x2", "y2", fx.p2);
3956
+ applyGeomVec(out, "x1", "y1", fx.start);
3957
+ applyGeomVec(out, "x2", "y2", fx.end);
3354
3958
  } else {
3355
- applyGeomVec(out, "cx", "cy", fx.c);
3356
- applyGeomNumber(out, "r", fx.r);
3357
- applyGeomVec(out, "fx", "fy", fx.fp);
3959
+ applyGeomVec(out, "cx", "cy", fx.center);
3960
+ applyGeomNumber(out, "r", fx.radius);
3961
+ applyGeomVec(out, "fx", "fy", fx.focal);
3358
3962
  }
3359
3963
  if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
3360
3964
  if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
@@ -3363,7 +3967,7 @@ function synthesiseGradientDef(fx, id, ctx) {
3363
3967
  return out;
3364
3968
  }
3365
3969
  function applyGeomVec(out, xAttr, yAttr, raw) {
3366
- var _a, _b, _c;
3970
+ var _a2, _b, _c;
3367
3971
  const read = readAnimatable(raw);
3368
3972
  if (read.kind === "absent" /* Absent */) return;
3369
3973
  if (read.kind === "static" /* Static */) {
@@ -3382,7 +3986,7 @@ function applyGeomVec(out, xAttr, yAttr, raw) {
3382
3986
  if (read.loop !== void 0) block.loop = read.loop;
3383
3987
  return block;
3384
3988
  };
3385
- const animate = (_a = out.animate) != null ? _a : {};
3989
+ const animate = (_a2 = out.animate) != null ? _a2 : {};
3386
3990
  animate[xAttr] = axisChannel(0);
3387
3991
  animate[yAttr] = axisChannel(1);
3388
3992
  out.animate = animate;
@@ -3398,7 +4002,7 @@ function applyGeomNumber(out, attrName, raw) {
3398
4002
  writeAnimatableChannel(out, attrName, read, { asString: true });
3399
4003
  }
3400
4004
  function buildStopChildren(stops, ctx) {
3401
- var _a, _b, _c, _d;
4005
+ var _a2, _b;
3402
4006
  if (!stops) return [];
3403
4007
  const read = readAnimatable(stops);
3404
4008
  if (read.kind === "absent" /* Absent */) return [];
@@ -3408,14 +4012,14 @@ function buildStopChildren(stops, ctx) {
3408
4012
  const loopFromSource = read.loop;
3409
4013
  let stopCount = 0;
3410
4014
  for (const kf of kfs) {
3411
- const v = (_a = kf.value) != null ? _a : kf.v;
4015
+ const v = kfValue(kf);
3412
4016
  if (Array.isArray(v) && v.length > stopCount) stopCount = v.length;
3413
4017
  }
3414
4018
  if (!stopCount) return [];
3415
- const firstKfValue = (_b = kfs[0].value) != null ? _b : kfs[0].v;
4019
+ const firstKfValue = kfValue(kfs[0]);
3416
4020
  const baselineStops = [];
3417
4021
  for (let i = 0; i < stopCount; i++) {
3418
- const s = (_d = (_c = firstKfValue == null ? void 0 : firstKfValue[i]) != null ? _c : prevDefinedStop(kfs, 0, i)) != null ? _d : { offset: i / Math.max(1, stopCount - 1), color: "#000000" };
4022
+ const s = (_b = (_a2 = firstKfValue == null ? void 0 : firstKfValue[i]) != null ? _a2 : prevDefinedStop(kfs, 0, i)) != null ? _b : { offset: i / Math.max(1, stopCount - 1), color: "#000000" };
3419
4023
  baselineStops.push({ offset: s.offset, color: s.color });
3420
4024
  }
3421
4025
  return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx, loopFromSource));
@@ -3428,16 +4032,16 @@ function staticStopNode(s) {
3428
4032
  };
3429
4033
  }
3430
4034
  function animatedStopNode(baseline, kfs, stopIdx, _ctx, loop) {
3431
- var _a, _b, _c, _d, _e;
4035
+ var _a2;
3432
4036
  const colorKfs = [];
3433
4037
  const offsetKfs = [];
3434
4038
  let offsetVaries = false;
3435
4039
  for (const kf of kfs) {
3436
- const t = (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
3437
- const arr = (_c = kf.value) != null ? _c : kf.v;
3438
- const sliced = (_d = arr == null ? void 0 : arr[stopIdx]) != null ? _d : prevDefinedStop(kfs, kfs.indexOf(kf), stopIdx);
4040
+ const t = kfTime(kf);
4041
+ const arr = kfValue(kf);
4042
+ const sliced = (_a2 = arr == null ? void 0 : arr[stopIdx]) != null ? _a2 : prevDefinedStop(kfs, kfs.indexOf(kf), stopIdx);
3439
4043
  if (!sliced) continue;
3440
- const easing = (_e = kf.easing) != null ? _e : kf.e;
4044
+ const easing = kfEasing(kf);
3441
4045
  const colorOut = { time: t, value: sliced.color };
3442
4046
  if (easing !== void 0) colorOut.easing = easing;
3443
4047
  colorKfs.push(colorOut);
@@ -3464,13 +4068,12 @@ function animatedStopNode(baseline, kfs, stopIdx, _ctx, loop) {
3464
4068
  return stop;
3465
4069
  }
3466
4070
  function prevDefinedStop(kfs, fromIdx, stopIdx) {
3467
- var _a, _b;
3468
4071
  for (let i = fromIdx; i >= 0; i--) {
3469
- const arr = (_a = kfs[i].value) != null ? _a : kfs[i].v;
4072
+ const arr = kfValue(kfs[i]);
3470
4073
  if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
3471
4074
  }
3472
4075
  for (let i = fromIdx + 1; i < kfs.length; i++) {
3473
- const arr = (_b = kfs[i].value) != null ? _b : kfs[i].v;
4076
+ const arr = kfValue(kfs[i]);
3474
4077
  if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
3475
4078
  }
3476
4079
  return void 0;
@@ -3480,10 +4083,9 @@ function formatOffset(o) {
3480
4083
  return pct + "%";
3481
4084
  }
3482
4085
 
3483
- // src/effects/clipPathEffect.ts
4086
+ // src/effects/clipping/clipPathEffect.ts
3484
4087
  function applyClipPathEffect(node, fx, ctx) {
3485
- var _a, _b;
3486
- if (!fx || !fx.d && !fx.animate) return node;
4088
+ if (!(fx == null ? void 0 : fx.d)) return node;
3487
4089
  const clipId = genId(ctx, "clip");
3488
4090
  const pathChild = { type: "path" };
3489
4091
  const read = readAnimatable(fx.d);
@@ -3495,11 +4097,6 @@ function applyClipPathEffect(node, fx, ctx) {
3495
4097
  if (pathChild.d !== void 0) pathChild.d = pathString(pathChild.d);
3496
4098
  }
3497
4099
  }
3498
- if (fx.animate && !((_a = pathChild.animate) == null ? void 0 : _a.d)) {
3499
- const animate = (_b = pathChild.animate) != null ? _b : {};
3500
- animate.d = fx.animate;
3501
- pathChild.animate = animate;
3502
- }
3503
4100
  ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
3504
4101
  node.clipPath = "url(#" + clipId + ")";
3505
4102
  return node;
@@ -3510,12 +4107,12 @@ function pathString(v) {
3510
4107
  return void 0;
3511
4108
  }
3512
4109
 
3513
- // src/effects/maskedByEffect.ts
4110
+ // src/effects/clipping/maskedByEffect.ts
3514
4111
  function applyMaskedByEffect(node, fx, transformBy, ctx) {
3515
4112
  if (!fx) return node;
3516
- const sourceId = stripHash2(fx.sourceId);
4113
+ const sourceId = stripHash2(fx.source);
3517
4114
  if (!sourceId) {
3518
- ctx.errors.push("maskedBy.sourceId missing \u2014 cannot build mask");
4115
+ ctx.errors.push("maskedBy.source missing \u2014 cannot build mask");
3519
4116
  return node;
3520
4117
  }
3521
4118
  const maskId = genId(ctx, "mask");
@@ -3578,16 +4175,16 @@ function invertPartValue(part, value) {
3578
4175
  return [1 / value[0], 1 / value[1]];
3579
4176
  }
3580
4177
  function negatedSpatialTangents(kf) {
3581
- var _a, _b;
4178
+ var _a2, _b;
3582
4179
  const out = {};
3583
- const to = (_a = kf.tangentOut) != null ? _a : kf.to;
4180
+ const to = (_a2 = kf.tangentOut) != null ? _a2 : kf.to;
3584
4181
  const ti = (_b = kf.tangentIn) != null ? _b : kf.ti;
3585
4182
  if (Array.isArray(to)) out.tangentOut = [-to[0], -to[1]];
3586
4183
  if (Array.isArray(ti)) out.tangentIn = [-ti[0], -ti[1]];
3587
4184
  return out;
3588
4185
  }
3589
4186
  function wrapInverseAnimatedBodyTransform(inner, node, _ctx) {
3590
- var _a;
4187
+ var _a2;
3591
4188
  const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3592
4189
  const animTr = animate == null ? void 0 : animate.transform;
3593
4190
  const kfs = animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes) ? animTr.keyframes : void 0;
@@ -3596,7 +4193,7 @@ function wrapInverseAnimatedBodyTransform(inner, node, _ctx) {
3596
4193
  const rotateKfs = [];
3597
4194
  const scaleKfs = [];
3598
4195
  for (const kf of kfs) {
3599
- const v = ((_a = kf.value) != null ? _a : kf.v) || {};
4196
+ const v = ((_a2 = kf.value) != null ? _a2 : kf.v) || {};
3600
4197
  const baseKf = keyframeWith(kf, void 0);
3601
4198
  if (Array.isArray(v.translate)) {
3602
4199
  translateKfs.push(__spreadProps(__spreadValues(__spreadValues({}, baseKf), negatedSpatialTangents(kf)), { value: { translate: [-v.translate[0], -v.translate[1]] } }));
@@ -3644,7 +4241,7 @@ function readTransformationFromBody(node) {
3644
4241
  if (parts.origin) out.origin = parts.origin;
3645
4242
  return Object.keys(out).length ? out : void 0;
3646
4243
  }
3647
- if (node.transform && typeof node.transform === "object" && !node.transform.keyframes && !node.transform.kfs) {
4244
+ if (node.transform && typeof node.transform === "object" && !node.transform.keyframes) {
3648
4245
  const wrapped = node.transform.value;
3649
4246
  const value = wrapped && typeof wrapped === "object" ? wrapped : node.transform;
3650
4247
  if (value && typeof value === "object") {
@@ -3660,7 +4257,7 @@ function readTransformationFromBody(node) {
3660
4257
  return void 0;
3661
4258
  }
3662
4259
  function parseTransformStringToParts(s) {
3663
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
4260
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
3664
4261
  const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
3665
4262
  let m;
3666
4263
  const ops = [];
@@ -3674,7 +4271,7 @@ function parseTransformStringToParts(s) {
3674
4271
  for (let j = ops.length - 2; j >= 0; j--) {
3675
4272
  const cand = ops[j];
3676
4273
  if (cand.name !== "translate") continue;
3677
- const ox = (_a = cand.args[0]) != null ? _a : 0;
4274
+ const ox = (_a2 = cand.args[0]) != null ? _a2 : 0;
3678
4275
  const oy = (_b = cand.args[1]) != null ? _b : 0;
3679
4276
  const lx = (_c = last.args[0]) != null ? _c : 0;
3680
4277
  const ly = (_d = last.args[1]) != null ? _d : 0;
@@ -3789,8 +4386,8 @@ function interpKfs(kfs, t) {
3789
4386
  function collectMaskAncestorChains(root, ctx) {
3790
4387
  const interestingNodes = /* @__PURE__ */ new Set();
3791
4388
  const collectInterestingNodes = (n) => {
3792
- var _a, _b;
3793
- const maskSourceId = stripHash2((_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.sourceId);
4389
+ var _a2, _b;
4390
+ const maskSourceId = stripHash2((_b = (_a2 = n.effects) == null ? void 0 : _a2.maskedBy) == null ? void 0 : _b.source);
3794
4391
  if (typeof maskSourceId === "string") {
3795
4392
  interestingNodes.add(n);
3796
4393
  const sourceNode = ctx.idMap.get(maskSourceId);
@@ -3811,7 +4408,7 @@ function collectMaskAncestorChains(root, ctx) {
3811
4408
  walk(root, []);
3812
4409
  }
3813
4410
  function extractTranslateOnly(node, ctx) {
3814
- var _a, _b, _c;
4411
+ var _a2, _b, _c;
3815
4412
  const tr = node.transform;
3816
4413
  const animateBlock = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate.transform : void 0;
3817
4414
  if (tr === void 0 && !animateBlock) return void 0;
@@ -3819,7 +4416,7 @@ function extractTranslateOnly(node, ctx) {
3819
4416
  if (typeof tr === "string") {
3820
4417
  const parts = parseTranslateOnlyFromString(tr, ctx);
3821
4418
  if (parts) out.translate = parts;
3822
- } else if (tr && typeof tr === "object" && !tr.keyframes && !tr.kfs) {
4419
+ } else if (tr && typeof tr === "object" && !tr.keyframes) {
3823
4420
  const wrapped = tr.value;
3824
4421
  const value = wrapped && typeof wrapped === "object" ? wrapped : tr;
3825
4422
  if (value && typeof value === "object" && Array.isArray(value.translate)) {
@@ -3833,7 +4430,7 @@ function extractTranslateOnly(node, ctx) {
3833
4430
  const kfs = animateBlock.keyframes;
3834
4431
  const translateKfs = [];
3835
4432
  for (const kf of kfs) {
3836
- const v = (_a = kf.value) != null ? _a : kf.v;
4433
+ const v = (_a2 = kf.value) != null ? _a2 : kf.v;
3837
4434
  const t = (_c = (_b = kf.time) != null ? _b : kf.t) != null ? _c : 0;
3838
4435
  if (v && typeof v === "object" && Array.isArray(v.translate)) {
3839
4436
  translateKfs.push({ time: t, value: [v.translate[0] || 0, v.translate[1] || 0] });
@@ -3867,16 +4464,15 @@ function parseTranslateOnlyFromString(s, ctx) {
3867
4464
  return seen ? [x, y] : void 0;
3868
4465
  }
3869
4466
 
3870
- // src/effects/refEffect.ts
3871
- var CONTENT_SUBREF = "content";
4467
+ // src/effects/reference/refEffect.ts
3872
4468
  function applyRefHref(node, clone2, ctx) {
3873
4469
  if (!clone2) return;
3874
- const sourceId = stripHash2(clone2.sourceId);
4470
+ const sourceId = stripHash2(clone2.source);
3875
4471
  if (!sourceId) {
3876
- if (clone2.type === CONTENT_SUBREF) ctx.errors.push("clone: content ref missing sourceId");
4472
+ if (clone2.without === PxCloneWithout.translate) ctx.errors.push("clone: content ref missing `source`");
3877
4473
  return;
3878
4474
  }
3879
- const targetId = clone2.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(sourceId) || sourceId : sourceId;
4475
+ const targetId = clone2.without === PxCloneWithout.translate ? ctx.contentRefInnerIds.get(sourceId) || sourceId : sourceId;
3880
4476
  node.href = "#" + targetId;
3881
4477
  }
3882
4478
  function applyRefAndTransformationEffect(node, clone2, transformBy, ctx) {
@@ -3884,11 +4480,11 @@ function applyRefAndTransformationEffect(node, clone2, transformBy, ctx) {
3884
4480
  return applyTransformByEffect(node, transformBy, ctx);
3885
4481
  }
3886
4482
 
3887
- // src/effects/repeaterEffect.ts
4483
+ // src/effects/transform/repeaterEffect.ts
3888
4484
  function applyRepeaterEffect(node, fx, ctx) {
3889
- var _a, _b;
4485
+ var _a2, _b;
3890
4486
  if (!fx) return node;
3891
- const copies = (_a = fx.copies) != null ? _a : 1;
4487
+ const copies = (_a2 = fx.copies) != null ? _a2 : 1;
3892
4488
  if (copies < 1) {
3893
4489
  ctx.errors.push("repeater.copies invalid: " + fx.copies);
3894
4490
  return node;
@@ -3955,11 +4551,11 @@ function synthesiseScale(raw, i) {
3955
4551
  return mapAnimatable(raw, scalePower, true);
3956
4552
  }
3957
4553
 
3958
- // src/effects/retimeEffect.ts
4554
+ // src/effects/reference/retimeEffect.ts
3959
4555
  var RETIME_MATERIALISATION_MODE_INLINE_G = false;
3960
4556
  function asRetime(r) {
3961
- var _a, _b;
3962
- return { start: (_a = r.start) != null ? _a : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
4557
+ var _a2, _b;
4558
+ return { start: (_a2 = r.start) != null ? _a2 : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
3963
4559
  }
3964
4560
  var CROP_EDGE_MS = 1;
3965
4561
  function applyTimeCrop(useNode, crop, ctx) {
@@ -3987,12 +4583,12 @@ function concatRetime(child, parent) {
3987
4583
  };
3988
4584
  }
3989
4585
  function readCloneRetime(n) {
3990
- var _a, _b;
3991
- return (_b = (_a = n.effects) == null ? void 0 : _a.clone) == null ? void 0 : _b.retime;
4586
+ var _a2, _b;
4587
+ return (_b = (_a2 = n.effects) == null ? void 0 : _a2.clone) == null ? void 0 : _b.retime;
3992
4588
  }
3993
4589
  function clearCloneRetime(n) {
3994
- var _a;
3995
- const clone2 = (_a = n.effects) == null ? void 0 : _a.clone;
4590
+ var _a2;
4591
+ const clone2 = (_a2 = n.effects) == null ? void 0 : _a2.clone;
3996
4592
  if (!clone2) return;
3997
4593
  delete clone2.retime;
3998
4594
  if (Object.keys(clone2).length === 0) delete n.effects.clone;
@@ -4003,9 +4599,9 @@ function applyAllRetimeEffects(root, ctx) {
4003
4599
  indexById(root, ctx.idMap);
4004
4600
  const sites = [];
4005
4601
  const collect = (n) => {
4006
- var _a;
4602
+ var _a2;
4007
4603
  if (readCloneRetime(n)) sites.push(n);
4008
- (_a = n.children) == null ? void 0 : _a.forEach(collect);
4604
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(collect);
4009
4605
  };
4010
4606
  collect(root);
4011
4607
  const reachCount = /* @__PURE__ */ new Map();
@@ -4013,7 +4609,7 @@ function applyAllRetimeEffects(root, ctx) {
4013
4609
  let count = 0;
4014
4610
  const visited = /* @__PURE__ */ new Set();
4015
4611
  const walk = (n) => {
4016
- var _a;
4612
+ var _a2;
4017
4613
  if (!n) return;
4018
4614
  if (n !== site && readCloneRetime(n)) count++;
4019
4615
  if (n.type === "use" && n.href) {
@@ -4023,7 +4619,7 @@ function applyAllRetimeEffects(root, ctx) {
4023
4619
  walk(ctx.idMap.get(id));
4024
4620
  }
4025
4621
  }
4026
- (_a = n.children) == null ? void 0 : _a.forEach(walk);
4622
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walk);
4027
4623
  };
4028
4624
  const rootId = stripHash2(site.href);
4029
4625
  if (rootId) {
@@ -4033,8 +4629,8 @@ function applyAllRetimeEffects(root, ctx) {
4033
4629
  reachCount.set(site, count);
4034
4630
  }
4035
4631
  sites.sort((a, b) => {
4036
- var _a, _b;
4037
- return ((_a = reachCount.get(b)) != null ? _a : 0) - ((_b = reachCount.get(a)) != null ? _b : 0);
4632
+ var _a2, _b;
4633
+ return ((_a2 = reachCount.get(b)) != null ? _a2 : 0) - ((_b = reachCount.get(a)) != null ? _b : 0);
4038
4634
  });
4039
4635
  for (const useNode of sites) {
4040
4636
  const retime = readCloneRetime(useNode);
@@ -4101,7 +4697,7 @@ function buildChainClone(targetId, accum, ctx, chain) {
4101
4697
  }
4102
4698
  function materialiseNestedRetimeUses(node, accum, ctx, chain, parentTargetId) {
4103
4699
  const visit = (n) => {
4104
- var _a;
4700
+ var _a2;
4105
4701
  const retime = readCloneRetime(n);
4106
4702
  if (n.type === "use" && retime && n.href) {
4107
4703
  const subId = stripHash2(n.href);
@@ -4114,14 +4710,14 @@ function materialiseNestedRetimeUses(node, accum, ctx, chain, parentTargetId) {
4114
4710
  }
4115
4711
  clearCloneRetime(n);
4116
4712
  }
4117
- (_a = n.children) == null ? void 0 : _a.forEach(visit);
4713
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(visit);
4118
4714
  };
4119
4715
  visit(node);
4120
4716
  }
4121
4717
  function remapKeyframeTimes(node, start, stretch) {
4122
- var _a;
4718
+ var _a2;
4123
4719
  remapKeyframeTimesOnly(node, start, stretch);
4124
- (_a = node.children) == null ? void 0 : _a.forEach((c) => remapKeyframeTimes(c, start, stretch));
4720
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((c) => remapKeyframeTimes(c, start, stretch));
4125
4721
  }
4126
4722
  function remapKeyframeTimesOnly(node, start, stretch) {
4127
4723
  const remap2 = (kfs) => {
@@ -4138,7 +4734,7 @@ function remapKeyframeTimesOnly(node, start, stretch) {
4138
4734
  }
4139
4735
  }
4140
4736
 
4141
- // src/effects/pathSampler.ts
4737
+ // src/effects/text/pathSampler.ts
4142
4738
  var LUT_STEPS = 48;
4143
4739
  var CMD_RE = /[MmLlHhVvCcSsQqTtAaZz]/;
4144
4740
  function tokenize(d) {
@@ -4286,7 +4882,7 @@ function createPathSampler(d) {
4286
4882
  };
4287
4883
  }
4288
4884
 
4289
- // src/effects/textPathEffect.ts
4885
+ // src/effects/text/textPathEffect.ts
4290
4886
  var EXTEND_MARGIN_FRAC = 0.15;
4291
4887
  function numRange(v) {
4292
4888
  const read = readAnimatable(v);
@@ -4300,8 +4896,8 @@ function numRange(v) {
4300
4896
  function estimateTextAdvance(node) {
4301
4897
  let chars = 0, maxFont = 16;
4302
4898
  const walk = (el) => {
4303
- var _a, _b;
4304
- const fs = parseFloat(String((_a = el.fontSize) != null ? _a : "")) || 0;
4899
+ var _a2, _b;
4900
+ const fs = parseFloat(String((_a2 = el.fontSize) != null ? _a2 : "")) || 0;
4305
4901
  if (fs) maxFont = Math.max(maxFont, fs);
4306
4902
  const t = (_b = el.text) != null ? _b : el.textContent;
4307
4903
  if (typeof t === "string") chars += t.length;
@@ -4325,14 +4921,14 @@ function shiftAnimatable(v, by) {
4325
4921
  return out;
4326
4922
  }
4327
4923
  function extendedPathForBrowser(pathD, opts) {
4328
- var _a;
4924
+ var _a2;
4329
4925
  if (opts.pathOverflow === "clip") return { d: pathD, startShift: 0 };
4330
4926
  const sampler = createPathSampler(pathD);
4331
4927
  if (!sampler || sampler.closed || sampler.totalLength <= 0) return { d: pathD, startShift: 0 };
4332
4928
  const L = sampler.totalLength;
4333
4929
  const margin = EXTEND_MARGIN_FRAC * L;
4334
4930
  const so = numRange(opts.startOffset);
4335
- const runWidth = numRange(opts.textLength).max || ((_a = opts.advance) != null ? _a : 0);
4931
+ const runWidth = numRange(opts.textLength).max || ((_a2 = opts.advance) != null ? _a2 : 0);
4336
4932
  const startOverflow = Math.max(0, -so.min);
4337
4933
  const endOverflow = Math.max(0, so.max + runWidth - L);
4338
4934
  const startExt = startOverflow > 0 ? startOverflow + margin : 0;
@@ -4353,10 +4949,10 @@ function extendedPathForBrowser(pathD, opts) {
4353
4949
  return { d, startShift: startExt };
4354
4950
  }
4355
4951
  function applyTextPathEffect(node, fx, ctx) {
4356
- var _a;
4357
- if (!fx || typeof fx.path !== "string" || !fx.path) return node;
4952
+ var _a2;
4953
+ if (!fx || typeof fx.pathData !== "string" || !fx.pathData) return node;
4358
4954
  const pathId = genId(ctx, "tpath");
4359
- const { d, startShift } = extendedPathForBrowser(fx.path, {
4955
+ const { d, startShift } = extendedPathForBrowser(fx.pathData, {
4360
4956
  pathOverflow: fx.pathOverflow,
4361
4957
  startOffset: fx.startOffset,
4362
4958
  textLength: fx.textLength,
@@ -4366,7 +4962,7 @@ function applyTextPathEffect(node, fx, ctx) {
4366
4962
  const textPath = {
4367
4963
  type: "textPath",
4368
4964
  href: "#" + pathId,
4369
- children: (_a = node.children) != null ? _a : []
4965
+ children: (_a2 = node.children) != null ? _a2 : []
4370
4966
  };
4371
4967
  if (fx.lengthAdjust !== void 0) textPath.lengthAdjust = fx.lengthAdjust;
4372
4968
  if (fx.method !== void 0) textPath.method = fx.method;
@@ -4381,7 +4977,7 @@ function applyAnimatableNumber(node, attrName, raw) {
4381
4977
  writeAnimatableChannel(node, attrName, readAnimatable(raw), { asString: true });
4382
4978
  }
4383
4979
 
4384
- // src/effects/elementFactory.ts
4980
+ // src/effects/text/elementFactory.ts
4385
4981
  var jsonElementFactory = (type, props, children) => {
4386
4982
  const node = { type };
4387
4983
  for (const k in props) if (props[k] !== void 0) node[k] = props[k];
@@ -4390,7 +4986,7 @@ var jsonElementFactory = (type, props, children) => {
4390
4986
  return node;
4391
4987
  };
4392
4988
 
4393
- // src/effects/glyphPathBake.ts
4989
+ // src/effects/text/glyphPathBake.ts
4394
4990
  function fmt(v, decimals) {
4395
4991
  return Math.round(v) === v ? "" + Math.round(v) : v.toFixed(decimals);
4396
4992
  }
@@ -4436,7 +5032,7 @@ function transformPathData(d, m, decimals = 2) {
4436
5032
  return out;
4437
5033
  }
4438
5034
 
4439
- // src/effects/textGlyphsEffect.ts
5035
+ // src/effects/text/textGlyphsEffect.ts
4440
5036
  var DEFAULT_FONT_SIZE = 16;
4441
5037
  var TEXT_ATTR_KEYS = [
4442
5038
  "fontFamily",
@@ -4496,15 +5092,15 @@ function paintAnimateOf(node) {
4496
5092
  return out;
4497
5093
  }
4498
5094
  function resolveStyle2(node, parent) {
4499
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
5095
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
4500
5096
  const isTextRoot = node.type === "text";
4501
5097
  const nodeStyle = node.style;
4502
5098
  const own = (key) => {
4503
- var _a2;
4504
- return isTextRoot ? void 0 : (_a2 = node[key]) != null ? _a2 : nodeStyle == null ? void 0 : nodeStyle[key];
5099
+ var _a3;
5100
+ return isTextRoot ? void 0 : (_a3 = node[key]) != null ? _a3 : nodeStyle == null ? void 0 : nodeStyle[key];
4505
5101
  };
4506
5102
  const res = {
4507
- fontFamily: (_a = str(node.fontFamily)) != null ? _a : parent.fontFamily,
5103
+ fontFamily: (_a2 = str(node.fontFamily)) != null ? _a2 : parent.fontFamily,
4508
5104
  fontSize: (_b = parseLen(node.fontSize)) != null ? _b : parent.fontSize,
4509
5105
  fill: (_c = node.fill) != null ? _c : parent.fill,
4510
5106
  stroke: (_d = node.stroke) != null ? _d : parent.stroke,
@@ -4521,10 +5117,10 @@ function resolveStyle2(node, parent) {
4521
5117
  return res;
4522
5118
  }
4523
5119
  function rootStyleOf(node) {
4524
- var _a, _b, _c;
5120
+ var _a2, _b, _c;
4525
5121
  return {
4526
5122
  fontFamily: str(node.fontFamily),
4527
- fontSize: (_a = parseLen(node.fontSize)) != null ? _a : DEFAULT_FONT_SIZE,
5123
+ fontSize: (_a2 = parseLen(node.fontSize)) != null ? _a2 : DEFAULT_FONT_SIZE,
4528
5124
  fill: node.fill,
4529
5125
  stroke: node.stroke,
4530
5126
  strokeWidth: node.strokeWidth,
@@ -4545,9 +5141,9 @@ function paintOf(s) {
4545
5141
  return p;
4546
5142
  }
4547
5143
  function glyphFontFor(s, glyphs, soleFont, warnings) {
4548
- var _a;
5144
+ var _a2;
4549
5145
  const gf = s.fontFamily ? glyphs[s.fontFamily] : soleFont;
4550
- if (!gf) warnings == null ? void 0 : warnings.push('textGlyphs: no glyphs for font "' + ((_a = s.fontFamily) != null ? _a : "") + '"');
5146
+ if (!gf) warnings == null ? void 0 : warnings.push('textGlyphs: no glyphs for font "' + ((_a2 = s.fontFamily) != null ? _a2 : "") + '"');
4551
5147
  return gf;
4552
5148
  }
4553
5149
  function soleFontOf(glyphs) {
@@ -4569,19 +5165,19 @@ function missingGlyphBoxEm(advanceEm, ascentEm) {
4569
5165
  return outer + "M" + ix0 + " " + iyb + "L" + ix0 + " " + iyt + "L" + ix1 + " " + iyt + "L" + ix1 + " " + iyb + "Z";
4570
5166
  }
4571
5167
  function materialiseGlyphTextHorizontal(node, opts) {
4572
- var _a, _b, _c, _d;
5168
+ var _a2, _b, _c, _d;
4573
5169
  const { glyphs, create = jsonElementFactory, warnings } = opts;
4574
5170
  const soleFont = soleFontOf(glyphs);
4575
- const pen = { x: (_a = parseLen(node.x)) != null ? _a : 0, y: (_b = parseLen(node.y)) != null ? _b : 0 };
5171
+ const pen = { x: (_a2 = parseLen(node.x)) != null ? _a2 : 0, y: (_b = parseLen(node.y)) != null ? _b : 0 };
4576
5172
  const placements = [];
4577
5173
  const lines = [{ start: pen.x, end: pen.x }];
4578
5174
  let line = 0;
4579
5175
  const renderChars = (content, s) => {
4580
- var _a2;
5176
+ var _a3;
4581
5177
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4582
5178
  const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
4583
5179
  const scale = s.fontSize / upm;
4584
- const ascentEm = (_a2 = gf == null ? void 0 : gf.ascent) != null ? _a2 : 0.9 * upm;
5180
+ const ascentEm = (_a3 = gf == null ? void 0 : gf.ascent) != null ? _a3 : 0.9 * upm;
4585
5181
  const paint = paintOf(s);
4586
5182
  for (let i = 0; i < content.length; i++) {
4587
5183
  const ch = content.charAt(i);
@@ -4601,7 +5197,7 @@ function materialiseGlyphTextHorizontal(node, opts) {
4601
5197
  }
4602
5198
  };
4603
5199
  const walk = (el, parentStyle) => {
4604
- var _a2, _b2, _c2, _d2;
5200
+ var _a3, _b2, _c2, _d2;
4605
5201
  const s = resolveStyle2(el, parentStyle);
4606
5202
  const x = parseLen(el.x);
4607
5203
  const y = parseLen(el.y);
@@ -4611,7 +5207,7 @@ function materialiseGlyphTextHorizontal(node, opts) {
4611
5207
  lines.push({ start: pen.x, end: pen.x });
4612
5208
  }
4613
5209
  if (y !== void 0) pen.y = y;
4614
- pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
5210
+ pen.x += (_a3 = parseLen(el.dx)) != null ? _a3 : 0;
4615
5211
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4616
5212
  const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4617
5213
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
@@ -4632,8 +5228,8 @@ function materialiseGlyphTextHorizontal(node, opts) {
4632
5228
  return toGroup(node, buildPaths(placements, create, warnings), create);
4633
5229
  }
4634
5230
  function layoutGlyphTextChars(node, opts) {
4635
- var _a, _b, _c, _d, _e, _f;
4636
- if ((_a = opts.alongPath) == null ? void 0 : _a.pathD) return layoutGlyphTextCharsAlongPath(node, opts.alongPath.pathD, opts);
5231
+ var _a2, _b, _c, _d, _e, _f;
5232
+ if ((_a2 = opts.alongPath) == null ? void 0 : _a2.pathD) return layoutGlyphTextCharsAlongPath(node, opts.alongPath.pathD, opts);
4637
5233
  const { glyphs, warnings } = opts;
4638
5234
  const soleFont = soleFontOf(glyphs);
4639
5235
  const pen = { x: (_b = parseLen(node.x)) != null ? _b : 0, y: (_c = parseLen(node.y)) != null ? _c : 0 };
@@ -4641,11 +5237,11 @@ function layoutGlyphTextChars(node, opts) {
4641
5237
  const lines = [{ start: pen.x, end: pen.x }];
4642
5238
  let line = 0;
4643
5239
  const renderChars = (content, s) => {
4644
- var _a2;
5240
+ var _a3;
4645
5241
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4646
5242
  const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
4647
5243
  const scale = s.fontSize / upm;
4648
- const ascent = ((_a2 = gf == null ? void 0 : gf.ascent) != null ? _a2 : 0.9 * upm) * scale;
5244
+ const ascent = ((_a3 = gf == null ? void 0 : gf.ascent) != null ? _a3 : 0.9 * upm) * scale;
4649
5245
  for (let i = 0; i < content.length; i++) {
4650
5246
  const ch = content.charAt(i);
4651
5247
  const g = gf == null ? void 0 : gf.glyphs[ch];
@@ -4656,7 +5252,7 @@ function layoutGlyphTextChars(node, opts) {
4656
5252
  }
4657
5253
  };
4658
5254
  const walk = (el, parentStyle) => {
4659
- var _a2, _b2, _c2, _d2;
5255
+ var _a3, _b2, _c2, _d2;
4660
5256
  const s = resolveStyle2(el, parentStyle);
4661
5257
  const x = parseLen(el.x);
4662
5258
  const y = parseLen(el.y);
@@ -4666,7 +5262,7 @@ function layoutGlyphTextChars(node, opts) {
4666
5262
  lines.push({ start: pen.x, end: pen.x });
4667
5263
  }
4668
5264
  if (y !== void 0) pen.y = y;
4669
- pen.x += (_a2 = parseLen(el.dx)) != null ? _a2 : 0;
5265
+ pen.x += (_a3 = parseLen(el.dx)) != null ? _a3 : 0;
4670
5266
  pen.y += (_b2 = parseLen(el.dy)) != null ? _b2 : 0;
4671
5267
  const content = (_c2 = str(el[TEXT_CONTENT_ATTR])) != null ? _c2 : str(el[TEXT_ATTR]);
4672
5268
  if (content && !((_d2 = el.children) == null ? void 0 : _d2.length)) renderChars(content, s);
@@ -4698,7 +5294,7 @@ function layoutGlyphTextChars(node, opts) {
4698
5294
  });
4699
5295
  }
4700
5296
  function layoutGlyphTextCharsAlongPath(node, pathD, opts) {
4701
- var _a, _b;
5297
+ var _a2, _b;
4702
5298
  const { glyphs, warnings, alongPath } = opts;
4703
5299
  const sampler = createPathSampler(pathD);
4704
5300
  if (!sampler) {
@@ -4709,9 +5305,9 @@ function layoutGlyphTextCharsAlongPath(node, pathD, opts) {
4709
5305
  const chars = [];
4710
5306
  let adv = 0;
4711
5307
  const walk = (el, parentStyle) => {
4712
- var _a2, _b2, _c;
5308
+ var _a3, _b2, _c;
4713
5309
  const s = resolveStyle2(el, parentStyle);
4714
- const content = (_a2 = str(el[TEXT_CONTENT_ATTR])) != null ? _a2 : str(el[TEXT_ATTR]);
5310
+ const content = (_a3 = str(el[TEXT_CONTENT_ATTR])) != null ? _a3 : str(el[TEXT_ATTR]);
4715
5311
  if (content && !((_b2 = el.children) == null ? void 0 : _b2.length)) {
4716
5312
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4717
5313
  const upm = (gf == null ? void 0 : gf.unitsPerEm) || 1e3;
@@ -4731,7 +5327,7 @@ function layoutGlyphTextCharsAlongPath(node, pathD, opts) {
4731
5327
  walk(node, rootStyleOf(node));
4732
5328
  const width = adv;
4733
5329
  const tlr = readAnimatable(alongPath == null ? void 0 : alongPath.textLength);
4734
- const tlv = tlr.kind === "animated" /* Animated */ ? Number((_a = tlr.keyframes[0]) == null ? void 0 : _a.value) || 0 : tlr.kind === "static" /* Static */ ? Number(tlr.value) || 0 : 0;
5330
+ const tlv = tlr.kind === "animated" /* Animated */ ? Number((_a2 = tlr.keyframes[0]) == null ? void 0 : _a2.value) || 0 : tlr.kind === "static" /* Static */ ? Number(tlr.value) || 0 : 0;
4735
5331
  const k = tlv > 0 && width > 0 ? tlv / width : 1;
4736
5332
  const so = readAnimatable(alongPath == null ? void 0 : alongPath.startOffset);
4737
5333
  const { along: alongOffset, perp } = alongPathNodeOffsets(node);
@@ -4763,9 +5359,9 @@ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
4763
5359
  const cells = [];
4764
5360
  let adv = 0;
4765
5361
  const walk = (el, parentStyle) => {
4766
- var _a, _b, _c;
5362
+ var _a2, _b, _c;
4767
5363
  const s = resolveStyle2(el, parentStyle);
4768
- const content = (_a = str(el[TEXT_CONTENT_ATTR])) != null ? _a : str(el[TEXT_ATTR]);
5364
+ const content = (_a2 = str(el[TEXT_CONTENT_ATTR])) != null ? _a2 : str(el[TEXT_ATTR]);
4769
5365
  if (content && !((_b = el.children) == null ? void 0 : _b.length)) {
4770
5366
  const gf = glyphFontFor(s, glyphs, soleFont, warnings);
4771
5367
  if (gf) {
@@ -4802,9 +5398,9 @@ function alongAffine(sampler, dist, scale, widthEm, perp = 0) {
4802
5398
  return [scale * cos, scale * sin, -scale * sin, scale * cos, x - scale * cos * hw - perp * sin, y - scale * sin * hw + perp * cos];
4803
5399
  }
4804
5400
  function alongPathNodeOffsets(node) {
4805
- var _a, _b, _c;
5401
+ var _a2, _b, _c;
4806
5402
  return {
4807
- along: ((_a = parseLen(node.x)) != null ? _a : 0) + ((_b = parseLen(node.dx)) != null ? _b : 0),
5403
+ along: ((_a2 = parseLen(node.x)) != null ? _a2 : 0) + ((_b = parseLen(node.dx)) != null ? _b : 0),
4808
5404
  perp: (_c = parseLen(node.dy)) != null ? _c : 0
4809
5405
  };
4810
5406
  }
@@ -4844,7 +5440,7 @@ function materialiseGlyphTextAlongPath(node, pathD, startOffset, opts, textLengt
4844
5440
  return toGroup(node, buildPaths(placements, create, warnings), create);
4845
5441
  }
4846
5442
  function numTrackOf(raw) {
4847
- var _a;
5443
+ var _a2;
4848
5444
  const r = readAnimatable(raw);
4849
5445
  if (r.kind === "animated" /* Animated */ && r.keyframes.length >= 2) {
4850
5446
  const kfs = [...r.keyframes].sort((k1, k2) => (Number(k1.time) || 0) - (Number(k2.time) || 0));
@@ -4867,7 +5463,7 @@ function numTrackOf(raw) {
4867
5463
  }
4868
5464
  };
4869
5465
  }
4870
- const v = r.kind === "animated" /* Animated */ ? Number((_a = r.keyframes[0]) == null ? void 0 : _a.value) || 0 : r.kind === "static" /* Static */ ? Number(r.value) || 0 : 0;
5466
+ const v = r.kind === "animated" /* Animated */ ? Number((_a2 = r.keyframes[0]) == null ? void 0 : _a2.value) || 0 : r.kind === "static" /* Static */ ? Number(r.value) || 0 : 0;
4871
5467
  return { animated: false, times: [], at: () => v };
4872
5468
  }
4873
5469
  function mergeTrackTimes(a, b) {
@@ -4943,7 +5539,7 @@ function paintProps(paint) {
4943
5539
  return p;
4944
5540
  }
4945
5541
  function missingGlyphProps(isMissing) {
4946
- return isMissing ? { class: MISSING_GLYPH_CLASS_NAME } : {};
5542
+ return isMissing ? { [CLASS_ATTR]: MISSING_GLYPH_CLASS_NAME } : {};
4947
5543
  }
4948
5544
  function buildPaths(placements, create, warnings) {
4949
5545
  if (!placements.length) {
@@ -4987,20 +5583,20 @@ function materialiseGlyphText(node, opts) {
4987
5583
  function applyTextGlyphsEffect(node, fx, ctx) {
4988
5584
  if (!(fx == null ? void 0 : fx.useGlyphs)) return node;
4989
5585
  if (!ctx.glyphs) {
4990
- ctx.warnings.push("textGlyphs: no definitions.glyphs \u2014 left as native <text>");
5586
+ ctx.warnings.push("textGlyphs: no definitions.fonts \u2014 left as native <text>");
4991
5587
  return node;
4992
5588
  }
4993
5589
  return materialiseGlyphTextHorizontal(node, { glyphs: ctx.glyphs, warnings: ctx.warnings });
4994
5590
  }
4995
5591
  function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pathOverflow) {
4996
5592
  if (!ctx.glyphs) {
4997
- ctx.warnings.push("textGlyphs: no definitions.glyphs");
5593
+ ctx.warnings.push("textGlyphs: no definitions.fonts");
4998
5594
  return null;
4999
5595
  }
5000
5596
  return materialiseGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength, pathOverflow);
5001
5597
  }
5002
5598
 
5003
- // src/effects/strokeTrimEffect.ts
5599
+ // src/effects/stroke/strokeTrimEffect.ts
5004
5600
  function applyStrokeTrimEffect(node, strokeTrim, ctx) {
5005
5601
  if (!strokeTrim) return node;
5006
5602
  const combined = strokeTrim.subPaths === PxStrokeTrimSubPaths.combined;
@@ -5136,12 +5732,11 @@ function getOffsetIndexRange(minMaxOffset) {
5136
5732
  ];
5137
5733
  }
5138
5734
  function readScalarValues(r) {
5139
- var _a;
5140
5735
  if (r.kind === "absent" /* Absent */) return [];
5141
5736
  if (r.kind === "static" /* Static */) return [r.value];
5142
5737
  const out = [];
5143
5738
  for (const kf of r.keyframes) {
5144
- const v = (_a = kf.value) != null ? _a : kf.v;
5739
+ const v = kfValue(kf);
5145
5740
  if (typeof v === "number") out.push(v);
5146
5741
  }
5147
5742
  return out;
@@ -5151,14 +5746,11 @@ function computeAnimAttr(read, map) {
5151
5746
  if (read.kind === "static" /* Static */) return { kind: "static" /* Static */, value: map(read.value) };
5152
5747
  return {
5153
5748
  kind: "animated" /* Animated */,
5154
- keyframes: read.keyframes.map((kf) => {
5155
- var _a, _b, _c, _d;
5156
- return {
5157
- time: (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0,
5158
- value: map((_c = kf.value) != null ? _c : kf.v),
5159
- easing: (_d = kf.easing) != null ? _d : kf.e
5160
- };
5161
- }),
5749
+ keyframes: read.keyframes.map((kf) => ({
5750
+ time: kfTime(kf),
5751
+ value: map(kfValue(kf)),
5752
+ easing: kfEasing(kf)
5753
+ })),
5162
5754
  loop: read.loop
5163
5755
  };
5164
5756
  }
@@ -5168,7 +5760,6 @@ function applyAttr(node, attrName, attr) {
5168
5760
  }
5169
5761
  var OPACITY_STEP_MS = 10;
5170
5762
  function computeOpacityFromRange(rangeRead) {
5171
- var _a, _b, _c, _d, _e, _f;
5172
5763
  const hide = (v) => v[0] === v[1];
5173
5764
  if (rangeRead.kind === "absent" /* Absent */) return void 0;
5174
5765
  if (rangeRead.kind === "static" /* Static */) return hide(rangeRead.value) ? { kind: "static" /* Static */, value: 0 } : void 0;
@@ -5176,7 +5767,7 @@ function computeOpacityFromRange(rangeRead) {
5176
5767
  let anyHide = false;
5177
5768
  let allHide = true;
5178
5769
  for (const kf of kfs) {
5179
- if (hide((_a = kf.value) != null ? _a : kf.v)) anyHide = true;
5770
+ if (hide(kfValue(kf))) anyHide = true;
5180
5771
  else allHide = false;
5181
5772
  }
5182
5773
  if (!anyHide) return void 0;
@@ -5186,10 +5777,10 @@ function computeOpacityFromRange(rangeRead) {
5186
5777
  const kf = kfs[i];
5187
5778
  const prevKf = i > 0 ? kfs[i - 1] : void 0;
5188
5779
  const nextKf = i < kfs.length - 1 ? kfs[i + 1] : void 0;
5189
- const t = (_c = (_b = kf.time) != null ? _b : kf.t) != null ? _c : 0;
5190
- const thisHide = hide((_d = kf.value) != null ? _d : kf.v);
5191
- const prevHide = prevKf ? thisHide && hide((_e = prevKf.value) != null ? _e : prevKf.v) : thisHide;
5192
- const nextHide = nextKf ? thisHide && hide((_f = nextKf.value) != null ? _f : nextKf.v) : thisHide;
5780
+ const t = kfTime(kf);
5781
+ const thisHide = hide(kfValue(kf));
5782
+ const prevHide = prevKf ? thisHide && hide(kfValue(prevKf)) : thisHide;
5783
+ const nextHide = nextKf ? thisHide && hide(kfValue(nextKf)) : thisHide;
5193
5784
  if (prevHide && !nextHide) {
5194
5785
  out.push({ time: t, value: 0 });
5195
5786
  out.push({ time: t + OPACITY_STEP_MS, value: 1 });
@@ -5204,14 +5795,11 @@ function computeOpacityFromRange(rangeRead) {
5204
5795
  function readRangeWithCrossings(raw) {
5205
5796
  const r = readAnimatable(raw);
5206
5797
  if (r.kind !== "animated" /* Animated */) return r;
5207
- const kfs = r.keyframes.map((kf) => {
5208
- var _a, _b, _c, _d;
5209
- return {
5210
- time: (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0,
5211
- value: (_c = kf.value) != null ? _c : kf.v,
5212
- easing: (_d = kf.easing) != null ? _d : kf.e
5213
- };
5214
- });
5798
+ const kfs = r.keyframes.map((kf) => ({
5799
+ time: kfTime(kf),
5800
+ value: kfValue(kf),
5801
+ easing: kfEasing(kf)
5802
+ }));
5215
5803
  const hasReverse = kfs.some((kf) => kf.value[0] > kf.value[1]);
5216
5804
  if (!hasReverse) {
5217
5805
  return {
@@ -5308,20 +5896,20 @@ function pxBezierPathLength(path) {
5308
5896
  return total;
5309
5897
  }
5310
5898
  function segmentLength(path, from, to) {
5311
- var _a, _b, _c, _d;
5899
+ var _a2, _b, _c, _d;
5312
5900
  const v = path.v;
5313
5901
  const p0 = v[from];
5314
5902
  const p3 = v[to];
5315
- const p1 = (_b = (_a = path.o) == null ? void 0 : _a[from]) != null ? _b : p0;
5903
+ const p1 = (_b = (_a2 = path.o) == null ? void 0 : _a2[from]) != null ? _b : p0;
5316
5904
  const p2 = (_d = (_c = path.i) == null ? void 0 : _c[to]) != null ? _d : p3;
5317
5905
  const lut = bezier2D_arcLengthLUT(p0, p1, p2, p3);
5318
5906
  return lut.ds[lut.ds.length - 1];
5319
5907
  }
5320
5908
  var ARC_KAPPA = 0.5522847498307936;
5321
5909
  function shapeToPathD(node) {
5322
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
5910
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
5323
5911
  if (node.type === "rect") {
5324
- const x = Number((_a = node.x) != null ? _a : 0), y = Number((_b = node.y) != null ? _b : 0);
5912
+ const x = Number((_a2 = node.x) != null ? _a2 : 0), y = Number((_b = node.y) != null ? _b : 0);
5325
5913
  const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
5326
5914
  return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
5327
5915
  }
@@ -5339,7 +5927,7 @@ function shapeToPathD(node) {
5339
5927
 
5340
5928
  // src/effects/PlayerEffectsUtil.ts
5341
5929
  function applyPlayerEffects(root) {
5342
- var _a, _b;
5930
+ var _a2, _b;
5343
5931
  const ctx = {
5344
5932
  defs: [],
5345
5933
  warnings: [],
@@ -5350,8 +5938,8 @@ function applyPlayerEffects(root) {
5350
5938
  maskAncestorChains: /* @__PURE__ */ new Map(),
5351
5939
  // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →
5352
5940
  // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5353
- engine: ((_a = getAnimatorConfig(root)) == null ? void 0 : _a.mode) === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.waapi,
5354
- glyphs: (_b = getDefs(root)) == null ? void 0 : _b.glyphs
5941
+ engine: resolveTimelineEngine((_a2 = getAnimatorConfig(root)) == null ? void 0 : _a2.engine),
5942
+ glyphs: (_b = getDefs(root)) == null ? void 0 : _b.fonts
5355
5943
  };
5356
5944
  const working = clone(root);
5357
5945
  indexById(working, ctx.idMap);
@@ -5374,7 +5962,7 @@ function applyPlayerEffects_exceptRetime(node, ctx) {
5374
5962
  let consumedByGlyphs = false;
5375
5963
  if (text == null ? void 0 : text.useGlyphs) {
5376
5964
  if (textPath) {
5377
- const pathD = typeof textPath.path === "string" ? textPath.path : void 0;
5965
+ const pathD = typeof textPath.pathData === "string" ? textPath.pathData : void 0;
5378
5966
  const glyphed = applyTextGlyphsAlongPath(n, ctx, pathD, textPath.startOffset, textPath.textLength, textPath.pathOverflow);
5379
5967
  if (glyphed) {
5380
5968
  n = glyphed;
@@ -5407,27 +5995,7 @@ function applyPlayerEffects_retime(node, ctx) {
5407
5995
  return node;
5408
5996
  }
5409
5997
 
5410
- // src/PxOffsetPathMaterialiser.ts
5411
- var kfTime = (kf) => {
5412
- var _a, _b;
5413
- return (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
5414
- };
5415
- var kfValue = (kf) => {
5416
- var _a;
5417
- return (_a = kf.v) != null ? _a : kf.value;
5418
- };
5419
- var kfEasing = (kf) => {
5420
- var _a;
5421
- return (_a = kf.e) != null ? _a : kf.easing;
5422
- };
5423
- var kfTangentIn = (kf) => {
5424
- var _a;
5425
- return (_a = kf.tangentIn) != null ? _a : kf.ti;
5426
- };
5427
- var kfTangentOut = (kf) => {
5428
- var _a;
5429
- return (_a = kf.tangentOut) != null ? _a : kf.to;
5430
- };
5998
+ // src/materialise/PxOffsetPathMaterialiser.ts
5431
5999
  function cubicAt(p0, c1, c2, p1, t) {
5432
6000
  const u = 1 - t;
5433
6001
  const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;
@@ -5451,9 +6019,9 @@ var fmt2 = (n) => {
5451
6019
  return Object.is(r, -0) ? "0" : String(r);
5452
6020
  };
5453
6021
  function buildOffsetPath(propAnim) {
5454
- var _a, _b, _c, _d;
6022
+ var _a2, _b, _c;
5455
6023
  if (propAnim.alongPathMode !== "offsetPath") return void 0;
5456
- const kfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
6024
+ const kfs = propAnim.keyframes;
5457
6025
  if (!kfs || kfs.length < 2) return void 0;
5458
6026
  const first = kfValue(kfs[0]);
5459
6027
  const anchor = (first == null ? void 0 : first.origin) && first.origin.length >= 2 ? [first.origin[0], first.origin[1]] : [0, 0];
@@ -5464,7 +6032,7 @@ function buildOffsetPath(propAnim) {
5464
6032
  if (!tr || tr.length < 2) return void 0;
5465
6033
  const parts = Object.keys(v);
5466
6034
  if (parts.some((p) => p !== "translate" && p !== "origin")) return void 0;
5467
- const o = (_b = v == null ? void 0 : v.origin) != null ? _b : [0, 0];
6035
+ const o = (_a2 = v == null ? void 0 : v.origin) != null ? _a2 : [0, 0];
5468
6036
  if (o[0] !== anchor[0] || o[1] !== anchor[1]) return void 0;
5469
6037
  points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);
5470
6038
  }
@@ -5473,8 +6041,8 @@ function buildOffsetPath(propAnim) {
5473
6041
  const segLens = [];
5474
6042
  for (let i = 0; i < points.length - 1; i++) {
5475
6043
  const p0 = points[i], p1 = points[i + 1];
5476
- const to = (_c = kfTangentOut(kfs[i])) != null ? _c : [0, 0];
5477
- const ti = (_d = kfTangentIn(kfs[i + 1])) != null ? _d : [0, 0];
6044
+ const to = (_b = kfTangentOut(kfs[i])) != null ? _b : [0, 0];
6045
+ const ti = (_c = kfTangentIn(kfs[i + 1])) != null ? _c : [0, 0];
5478
6046
  const c1 = [p0[0] + to[0], p0[1] + to[1]];
5479
6047
  const c2 = [p1[0] + ti[0], p1[1] + ti[1]];
5480
6048
  d += "C" + fmt2(c1[0]) + "," + fmt2(c1[1]) + "," + fmt2(c2[0]) + "," + fmt2(c2[1]) + "," + fmt2(p1[0]) + "," + fmt2(p1[1]);
@@ -5495,7 +6063,7 @@ function buildOffsetPath(propAnim) {
5495
6063
  }
5496
6064
  function materialiseOffsetPathsInTree(root) {
5497
6065
  const walk = (node) => {
5498
- var _a;
6066
+ var _a2;
5499
6067
  let out = node;
5500
6068
  const anim = node.animate;
5501
6069
  const transform = anim == null ? void 0 : anim["transform"];
@@ -5503,16 +6071,16 @@ function materialiseOffsetPathsInTree(root) {
5503
6071
  const built = buildOffsetPath(transform);
5504
6072
  if (built) {
5505
6073
  const newAnimate = __spreadValues({}, anim);
5506
- delete newAnimate["transform"];
6074
+ delete newAnimate[TRANSFORM_ATTR];
5507
6075
  const distance = { keyframes: built.distanceKfs };
5508
6076
  if (transform.loop !== void 0) distance.loop = transform.loop;
5509
- newAnimate["offsetDistance"] = distance;
6077
+ newAnimate[OFFSET_DISTANCE_ATTR] = distance;
5510
6078
  const staticTr = node.transform;
5511
6079
  let newTransform = staticTr;
5512
6080
  if (staticTr && typeof staticTr === "object") {
5513
6081
  const t = __spreadValues({}, staticTr);
5514
- delete t["translate"];
5515
- delete t["origin"];
6082
+ delete t[TRANSFORM_PART.translate];
6083
+ delete t[TRANSFORM_PART.origin];
5516
6084
  newTransform = Object.keys(t).length ? t : void 0;
5517
6085
  }
5518
6086
  out = __spreadProps(__spreadValues({}, node), {
@@ -5528,7 +6096,7 @@ function materialiseOffsetPathsInTree(root) {
5528
6096
  else delete out.transform;
5529
6097
  }
5530
6098
  }
5531
- if ((_a = out.children) == null ? void 0 : _a.length) {
6099
+ if ((_a2 = out.children) == null ? void 0 : _a2.length) {
5532
6100
  const children = out.children.map(walk);
5533
6101
  if (children.some((c, i) => c !== out.children[i])) out = __spreadProps(__spreadValues({}, out), { children });
5534
6102
  }
@@ -5537,14 +6105,14 @@ function materialiseOffsetPathsInTree(root) {
5537
6105
  return walk(root);
5538
6106
  }
5539
6107
 
5540
- // src/PxAnimatorMaterialiseAll.ts
6108
+ // src/materialise/PxAnimatorMaterialiseAll.ts
5541
6109
  function materialiseAllInTree(doc, engine, opts) {
5542
- var _a, _b;
6110
+ var _a2, _b;
5543
6111
  let root = applyPlayerEffects(doc).root;
5544
6112
  root = materialiseOffsetPathsInTree(root);
5545
- const duration = (_b = (_a = getAnimatorConfig(root)) == null ? void 0 : _a.duration) != null ? _b : DEFAULT_DURATION_MS;
6113
+ const duration = (_b = (_a2 = getAnimatorConfig(root)) == null ? void 0 : _a2.duration) != null ? _b : DEFAULT_DURATION_MS;
5546
6114
  root = materialiseInternalLoopsInTree(root, duration);
5547
- if (engine === PxAnimatorEngine.waapi) {
6115
+ if (engine === PxTimelineEngine.native) {
5548
6116
  root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
5549
6117
  root = materialiseAnimatedUseInstances(root);
5550
6118
  root = pruneUnreferencedDefs(root);
@@ -5554,9 +6122,9 @@ function materialiseAllInTree(doc, engine, opts) {
5554
6122
  function pruneUnreferencedDefs(root) {
5555
6123
  const stripHash3 = (h) => h.startsWith("#") ? h.slice(1) : h;
5556
6124
  const walk = (n, fn) => {
5557
- var _a;
6125
+ var _a2;
5558
6126
  fn(n);
5559
- (_a = n.children) == null ? void 0 : _a.forEach((c) => walk(c, fn));
6127
+ (_a2 = n.children) == null ? void 0 : _a2.forEach((c) => walk(c, fn));
5560
6128
  };
5561
6129
  let changed = true;
5562
6130
  while (changed) {
@@ -5581,7 +6149,7 @@ function pruneUnreferencedDefs(root) {
5581
6149
  return root;
5582
6150
  }
5583
6151
 
5584
- // src/PxFrameLoop.ts
6152
+ // src/playback/PxFrameLoop.ts
5585
6153
  function requestFrame(cb) {
5586
6154
  const g = globalThis;
5587
6155
  if (typeof g.requestAnimationFrame === "function") return g.requestAnimationFrame(cb);
@@ -5596,9 +6164,9 @@ function cancelFrame(handle) {
5596
6164
  g.clearTimeout(handle);
5597
6165
  }
5598
6166
  function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5599
- var _a;
6167
+ var _a2;
5600
6168
  const config = getAnimatorConfig(doc) || {};
5601
- const bindings = getNormalisedBindings(doc, PxAnimatorEngine.frames);
6169
+ const bindings = getNormalisedBindings(doc, PxTimelineEngine.js);
5602
6170
  const _iterations = config.iterations;
5603
6171
  let iterations = 1;
5604
6172
  if (typeof _iterations === "number") iterations = _iterations || 1;
@@ -5607,7 +6175,7 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5607
6175
  const duration = +(config.duration || DEFAULT_DURATION_MS);
5608
6176
  const totalDuration = duration && iterations ? duration * (iterations === Infinity ? Infinity : iterations) : duration ? (iterations != null ? iterations : 1) * duration : 0;
5609
6177
  const direction = config.direction || "normal";
5610
- const fill = (_a = config.fill) != null ? _a : "forwards";
6178
+ const fill = (_a2 = config.fill) != null ? _a2 : "forwards";
5611
6179
  const fillsForwards = fill === "forwards" || fill === "both";
5612
6180
  const fillsBackwards = fill === "backwards" || fill === "both";
5613
6181
  let timerId = null;
@@ -5669,7 +6237,7 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5669
6237
  }
5670
6238
  }
5671
6239
  const tick = () => {
5672
- var _a2, _b;
6240
+ var _a3, _b;
5673
6241
  if (!adapter.isConnected()) {
5674
6242
  pauseAnim();
5675
6243
  return;
@@ -5685,7 +6253,7 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5685
6253
  renderFrame(0);
5686
6254
  if (!finishCalled) {
5687
6255
  finishCalled = true;
5688
- (_a2 = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
6256
+ (_a3 = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a3.call(callbacks);
5689
6257
  }
5690
6258
  return;
5691
6259
  }
@@ -5777,17 +6345,17 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5777
6345
  }
5778
6346
  };
5779
6347
  const cancelAnim = () => {
5780
- var _a2;
6348
+ var _a3;
5781
6349
  pauseAnim();
5782
6350
  timeBeforeLastStartMs = 0;
5783
6351
  lastStartedTs = 0;
5784
6352
  playing = false;
5785
6353
  finishCalled = false;
5786
6354
  renderFrame(timeBeforeLastStartMs);
5787
- (_a2 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a2.call(callbacks);
6355
+ (_a3 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a3.call(callbacks);
5788
6356
  };
5789
6357
  const finishAnim = (callOnFinish = true) => {
5790
- var _a2;
6358
+ var _a3;
5791
6359
  if (Number.isFinite(totalDuration)) {
5792
6360
  timeBeforeLastStartMs = totalDuration;
5793
6361
  } else {
@@ -5802,7 +6370,7 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5802
6370
  renderFrame(fillsForwards ? timeBeforeLastStartMs : 0);
5803
6371
  if (callOnFinish && !finishCalled) {
5804
6372
  finishCalled = true;
5805
- (_a2 = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
6373
+ (_a3 = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a3.call(callbacks);
5806
6374
  }
5807
6375
  };
5808
6376
  const api = {
@@ -5812,14 +6380,14 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5812
6380
  return _isPlaying();
5813
6381
  },
5814
6382
  "play": () => {
5815
- var _a2;
6383
+ var _a3;
5816
6384
  startAnim();
5817
- (_a2 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a2.call(callbacks);
6385
+ (_a3 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a3.call(callbacks);
5818
6386
  },
5819
6387
  "pause": () => {
5820
- var _a2;
6388
+ var _a3;
5821
6389
  pauseAnim();
5822
- (_a2 = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a2.call(callbacks);
6390
+ (_a3 = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a3.call(callbacks);
5823
6391
  },
5824
6392
  "cancel": () => {
5825
6393
  cancelAnim();
@@ -5851,9 +6419,9 @@ function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
5851
6419
  renderFrame(getAnimCurrentTime());
5852
6420
  },
5853
6421
  "destroy": () => {
5854
- var _a2;
6422
+ var _a3;
5855
6423
  api.cancel();
5856
- (_a2 = callbacks == null ? void 0 : callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
6424
+ (_a3 = callbacks == null ? void 0 : callbacks.onRemove) == null ? void 0 : _a3.call(callbacks);
5857
6425
  }
5858
6426
  };
5859
6427
  return api;
@@ -5909,9 +6477,9 @@ function lerp(a, b, f) {
5909
6477
  return a + (b - a) * f;
5910
6478
  }
5911
6479
  function interpParts(kfs, t) {
5912
- var _a, _b, _c, _d, _e, _f, _g;
6480
+ var _a2, _b, _c, _d, _e, _f, _g;
5913
6481
  if (!kfs.length) return {};
5914
- if (t <= ((_a = kfs[0].time) != null ? _a : 0)) return kfs[0].value || {};
6482
+ if (t <= ((_a2 = kfs[0].time) != null ? _a2 : 0)) return kfs[0].value || {};
5915
6483
  if (t >= ((_b = kfs[kfs.length - 1].time) != null ? _b : 0)) return kfs[kfs.length - 1].value || {};
5916
6484
  let i = 0;
5917
6485
  while (i < kfs.length - 1 && ((_c = kfs[i + 1].time) != null ? _c : 0) < t) i++;
@@ -5942,10 +6510,10 @@ function nodeMatrix(node, t) {
5942
6510
  return IDENTITY;
5943
6511
  }
5944
6512
  function evalScalar(animated, staticVal, fallback, t) {
5945
- var _a, _b, _c, _d, _e, _f;
6513
+ var _a2, _b, _c, _d, _e, _f;
5946
6514
  if (animated && animated.keyframes && animated.keyframes.length) {
5947
6515
  const kfs = animated.keyframes;
5948
- if (t <= ((_a = kfs[0].time) != null ? _a : 0)) return kfs[0].value;
6516
+ if (t <= ((_a2 = kfs[0].time) != null ? _a2 : 0)) return kfs[0].value;
5949
6517
  if (t >= ((_b = kfs[kfs.length - 1].time) != null ? _b : 0)) return kfs[kfs.length - 1].value;
5950
6518
  let i = 0;
5951
6519
  while (i < kfs.length - 1 && ((_c = kfs[i + 1].time) != null ? _c : 0) < t) i++;
@@ -5958,9 +6526,9 @@ function evalScalar(animated, staticVal, fallback, t) {
5958
6526
  var CONTAINER_TYPES = /* @__PURE__ */ new Set(["svg", "g", "symbol"]);
5959
6527
  var SKIP_TYPES = /* @__PURE__ */ new Set(["defs", "mask", "clipPath", "title"]);
5960
6528
  function buildIdMap2(node, map) {
5961
- var _a;
6529
+ var _a2;
5962
6530
  if (typeof node.id === "string") map.set(node.id, node);
5963
- (_a = node.children) == null ? void 0 : _a.forEach((c) => buildIdMap2(c, map));
6531
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((c) => buildIdMap2(c, map));
5964
6532
  }
5965
6533
  function num(v) {
5966
6534
  return v === void 0 || v === null ? 0 : Number(v);
@@ -5969,7 +6537,7 @@ function round(n) {
5969
6537
  return Math.round(n * 100) / 100 + 0;
5970
6538
  }
5971
6539
  function geomKey(node) {
5972
- var _a;
6540
+ var _a2;
5973
6541
  switch (node.type) {
5974
6542
  case "rect":
5975
6543
  return num(node.width) + "," + num(node.height) + "," + num(node.x) + "," + num(node.y);
@@ -5978,14 +6546,14 @@ function geomKey(node) {
5978
6546
  case "circle":
5979
6547
  return num(node.r) + "," + num(node.cx) + "," + num(node.cy);
5980
6548
  case "path":
5981
- return String((_a = node.d) != null ? _a : "");
6549
+ return String((_a2 = node.d) != null ? _a2 : "");
5982
6550
  default:
5983
6551
  return "";
5984
6552
  }
5985
6553
  }
5986
6554
  function describePrimitive(node, m, t) {
5987
- var _a, _b, _c, _d, _e;
5988
- const fill = (_a = node.fill) != null ? _a : "";
6555
+ var _a2, _b, _c, _d, _e;
6556
+ const fill = (_a2 = node.fill) != null ? _a2 : "";
5989
6557
  const stroke = (_b = node.stroke) != null ? _b : "";
5990
6558
  const sw = (_d = (_c = node["stroke-width"]) != null ? _c : node.strokeWidth) != null ? _d : "";
5991
6559
  const opacity = round(evalScalar((_e = node.animate) == null ? void 0 : _e.opacity, node.opacity, 1, t));
@@ -5994,7 +6562,7 @@ function describePrimitive(node, m, t) {
5994
6562
  return node.type + "|" + geomKey(node) + "|[" + mat + "]|f:" + fill + "|s:" + stroke + "|sw:" + (num(sw) || "") + "|o:" + opacity + "|m:" + masked;
5995
6563
  }
5996
6564
  function flatten(node, parent, t, idMap, out) {
5997
- var _a;
6565
+ var _a2;
5998
6566
  const type = node.type || "";
5999
6567
  if (SKIP_TYPES.has(type)) return;
6000
6568
  const m = mul(parent, nodeMatrix(node, t));
@@ -6007,33 +6575,33 @@ function flatten(node, parent, t, idMap, out) {
6007
6575
  return;
6008
6576
  }
6009
6577
  if (CONTAINER_TYPES.has(type)) {
6010
- (_a = node.children) == null ? void 0 : _a.forEach((c) => flatten(c, m, t, idMap, out));
6578
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((c) => flatten(c, m, t, idMap, out));
6011
6579
  return;
6012
6580
  }
6013
6581
  out.push(describePrimitive(node, m, t));
6014
6582
  }
6015
6583
  function collectSampleTimes(node, into) {
6016
- var _a;
6584
+ var _a2;
6017
6585
  into.add(0);
6018
6586
  const scanAnim = (anim) => {
6019
- var _a2;
6587
+ var _a3;
6020
6588
  if (!anim || typeof anim !== "object") return;
6021
6589
  for (const key of Object.keys(anim)) {
6022
- const kfs = (_a2 = anim[key]) == null ? void 0 : _a2.keyframes;
6590
+ const kfs = (_a3 = anim[key]) == null ? void 0 : _a3.keyframes;
6023
6591
  if (Array.isArray(kfs)) kfs.forEach((kf) => {
6024
- var _a3;
6025
- return into.add((_a3 = kf.time) != null ? _a3 : 0);
6592
+ var _a4;
6593
+ return into.add((_a4 = kf.time) != null ? _a4 : 0);
6026
6594
  });
6027
6595
  }
6028
6596
  };
6029
6597
  scanAnim(node.animate);
6030
6598
  if (node.transform && typeof node.transform === "object" && node.transform.keyframes) {
6031
6599
  node.transform.keyframes.forEach((kf) => {
6032
- var _a2;
6033
- return into.add((_a2 = kf.time) != null ? _a2 : 0);
6600
+ var _a3;
6601
+ return into.add((_a3 = kf.time) != null ? _a3 : 0);
6034
6602
  });
6035
6603
  }
6036
- (_a = node.children) == null ? void 0 : _a.forEach((c) => collectSampleTimes(c, into));
6604
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((c) => collectSampleTimes(c, into));
6037
6605
  }
6038
6606
  function visualModelAt(root, t) {
6039
6607
  const idMap = /* @__PURE__ */ new Map();
@@ -6067,7 +6635,195 @@ function subtractMultiset(a, b) {
6067
6635
  }
6068
6636
  return extra;
6069
6637
  }
6638
+
6639
+ // src/playback/PxAnimatorConfigPatch.ts
6640
+ var FLAT_ONLY_KEYS = [
6641
+ "timelineSource",
6642
+ "scroll",
6643
+ "trigger",
6644
+ "delay",
6645
+ "iterations",
6646
+ "direction",
6647
+ "fill",
6648
+ "resetOnFinish",
6649
+ "duration",
6650
+ "mode",
6651
+ "frameRate"
6652
+ ];
6653
+ var CONTENT_KEYS = ["definitions", "animateById"];
6654
+ var isPlainObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
6655
+ var timelineTypeOf = (t) => isPlainObject(t) && typeof t.type === "string" ? t.type : "time";
6656
+ var isScrollish = (type) => type === "scroll" || type === "view";
6657
+ function mergePlain(base, patch) {
6658
+ const out = isPlainObject(base) ? __spreadValues({}, base) : {};
6659
+ for (const key of Object.keys(patch)) {
6660
+ const value = patch[key];
6661
+ if (value === null) {
6662
+ delete out[key];
6663
+ continue;
6664
+ }
6665
+ if (isPlainObject(value)) {
6666
+ out[key] = mergePlain(out[key], value);
6667
+ continue;
6668
+ }
6669
+ out[key] = value;
6670
+ }
6671
+ return out;
6672
+ }
6673
+ function mergeTimeline(base, patch, warn) {
6674
+ const baseType = timelineTypeOf(base);
6675
+ const patchNamesType = isPlainObject(patch) && typeof patch.type === "string";
6676
+ const patchType = patchNamesType ? String(patch.type) : baseType;
6677
+ let merged;
6678
+ if (patchType !== baseType) {
6679
+ const carried = {};
6680
+ if (isPlainObject(base)) {
6681
+ for (const k of PX_TIMELINE_SHARED_KEYS) {
6682
+ if (base[k] !== void 0) carried[k] = base[k];
6683
+ }
6684
+ }
6685
+ merged = mergePlain(carried, patch);
6686
+ } else {
6687
+ merged = mergePlain(base, patch);
6688
+ }
6689
+ if (isScrollish(patchType)) {
6690
+ for (const k of PX_TIME_ONLY_TIMELINE_KEYS) {
6691
+ if (merged[k] === void 0) continue;
6692
+ warn("animator.timeline." + k + ": no slot on a '" + patchType + "' timeline \u2014 dropped");
6693
+ delete merged[k];
6694
+ }
6695
+ if (merged.iterations === "infinite") {
6696
+ warn("animator.timeline.iterations: 'infinite' cannot map onto a scroll range \u2014 dropped");
6697
+ delete merged.iterations;
6698
+ }
6699
+ }
6700
+ return merged;
6701
+ }
6702
+ function foldAnimatorConfigShortcuts(config, shortcuts) {
6703
+ let base;
6704
+ if (typeof config === "string") {
6705
+ try {
6706
+ base = JSON.parse(config);
6707
+ } catch (e) {
6708
+ console.warn("animator config: not valid JSON \u2014 ignored", e);
6709
+ base = void 0;
6710
+ }
6711
+ } else {
6712
+ base = config != null ? config : void 0;
6713
+ }
6714
+ const { duration, delay, iterations, startOn } = shortcuts;
6715
+ if (duration === void 0 && delay === void 0 && iterations === void 0 && startOn === void 0) {
6716
+ return base;
6717
+ }
6718
+ const out = isPlainObject(base) ? __spreadValues({}, base) : {};
6719
+ const timeline = __spreadValues({}, out.timeline);
6720
+ if (duration !== void 0) timeline.duration = duration;
6721
+ if (delay !== void 0) timeline.delay = delay;
6722
+ if (iterations !== void 0) timeline.iterations = iterations;
6723
+ if (startOn !== void 0) {
6724
+ timeline.trigger = __spreadProps(__spreadValues({}, timeline.trigger), { startOn });
6725
+ }
6726
+ out.timeline = timeline;
6727
+ return out;
6728
+ }
6729
+ function mergeAnimatorConfig(base, patch) {
6730
+ const warnings = [];
6731
+ const warn = (m) => warnings.push(m);
6732
+ if (patch === null) return { config: void 0, warnings };
6733
+ if (!isPlainObject(patch) || Object.keys(patch).length === 0) {
6734
+ return { config: base, warnings };
6735
+ }
6736
+ const flatInBase = isPlainObject(base) ? FLAT_ONLY_KEYS.filter((k) => base[k] !== void 0) : [];
6737
+ if (flatInBase.length) {
6738
+ warn("animator: the base carries the flat runtime spelling (" + flatInBase.join(", ") + "); the patch merges the wire spelling only");
6739
+ }
6740
+ const out = isPlainObject(base) ? __spreadValues({}, base) : {};
6741
+ for (const key of Object.keys(patch)) {
6742
+ const value = patch[key];
6743
+ if (value === null) {
6744
+ delete out[key];
6745
+ continue;
6746
+ }
6747
+ if (key === "timeline") {
6748
+ out.timeline = isPlainObject(value) ? mergeTimeline(out.timeline, value, warn) : value;
6749
+ continue;
6750
+ }
6751
+ if (isPlainObject(value)) {
6752
+ out[key] = mergePlain(out[key], value);
6753
+ continue;
6754
+ }
6755
+ out[key] = value;
6756
+ }
6757
+ return { config: out, warnings };
6758
+ }
6759
+ function applyAnimatorConfig(doc, patch, opts) {
6760
+ var _a2, _b;
6761
+ const reset = !!(opts == null ? void 0 : opts.resetDefaults);
6762
+ if (!doc || (patch === void 0 || !reset && (patch === null || !isPlainObject(patch) || !Object.keys(patch).length))) {
6763
+ return { doc, warnings: [] };
6764
+ }
6765
+ const anyDoc = doc;
6766
+ const atRoot = isPlainObject(anyDoc.animator);
6767
+ const atMeta = !atRoot && isPlainObject((_a2 = anyDoc.meta) == null ? void 0 : _a2.animator);
6768
+ const current = atRoot ? anyDoc.animator : atMeta ? anyDoc.meta.animator : void 0;
6769
+ const warnings = [];
6770
+ if (atRoot && isPlainObject((_b = anyDoc.meta) == null ? void 0 : _b.animator)) {
6771
+ warnings.push("animator: doc.meta.animator is shadowed by doc.animator and was not patched");
6772
+ }
6773
+ let base = current;
6774
+ if (reset) {
6775
+ const kept = {};
6776
+ for (const k of CONTENT_KEYS) {
6777
+ if (isPlainObject(current) && current[k] !== void 0) kept[k] = current[k];
6778
+ }
6779
+ base = kept;
6780
+ }
6781
+ const merged = mergeAnimatorConfig(base, patch != null ? patch : {});
6782
+ warnings.push(...merged.warnings);
6783
+ if (merged.config === current) return { doc, warnings };
6784
+ if (atMeta) {
6785
+ return {
6786
+ doc: __spreadProps(__spreadValues({}, anyDoc), { meta: __spreadProps(__spreadValues({}, anyDoc.meta), { animator: merged.config }) }),
6787
+ warnings
6788
+ };
6789
+ }
6790
+ return { doc: __spreadProps(__spreadValues({}, anyDoc), { animator: merged.config }), warnings };
6791
+ }
6792
+
6793
+ // src/format/PxDocumentDiagnostic.ts
6794
+ var MAX_REPORTED = 6;
6795
+ var LEGACY_FLAT_KEYS = [
6796
+ ...PX_TIMELINE_SHARED_KEYS,
6797
+ ...PX_TIME_ONLY_TIMELINE_KEYS,
6798
+ "fill",
6799
+ "resetOnFinish",
6800
+ "timelineSource",
6801
+ "scroll"
6802
+ ];
6803
+ var isLegacyFlatAnimatorKey = (w) => LEGACY_FLAT_KEYS.some((k) => w.indexOf("animator." + k + ":") >= 0);
6804
+ function diagnoseDocument(doc) {
6805
+ let all;
6806
+ try {
6807
+ all = validateDocument(doc);
6808
+ } catch (e) {
6809
+ return { problems: [], legacy: [] };
6810
+ }
6811
+ const problems = [];
6812
+ const legacy = [];
6813
+ for (const w of all) (isLegacyFlatAnimatorKey(w) ? legacy : problems).push(w);
6814
+ return { problems, legacy };
6815
+ }
6816
+ function reportDocumentDiagnostics(doc, where) {
6817
+ const { problems } = diagnoseDocument(doc);
6818
+ if (!problems.length) return;
6819
+ const shown = problems.slice(0, MAX_REPORTED);
6820
+ const more = problems.length - shown.length;
6821
+ console.warn(
6822
+ where + ": this document does not match the animation schema in " + problems.length + " place" + (problems.length === 1 ? "" : "s") + ".\n" + shown.map((p) => " - " + p).join("\n") + (more > 0 ? "\n \u2026 and " + more + " more" : "") + "\n\nIf you did not author these keys, the usual cause is a build that MANGLES PROPERTY NAMES. An animation document is data loaded at runtime, so renaming the property reads inside the player stops them matching the keys in the JSON, and the animation silently does nothing. Feed the published reserved-name list to your minifier \u2014 @pixodesk/svg-animator-web/mangle-reserved.json \u2014 see docs/library/minification.md."
6823
+ );
6824
+ }
6070
6825
  export {
6826
+ BASELINE_PLAYER_VERSION,
6071
6827
  COLOUR_ATTR_NAMES,
6072
6828
  CSS_ONLY_STYLE_PROPS,
6073
6829
  DEFAULT_DURATION_MS,
@@ -6076,18 +6832,21 @@ export {
6076
6832
  LOOP_JUMP_SHIFT_MS,
6077
6833
  MISSING_GLYPH_CLASS_NAME,
6078
6834
  PCT_BASED_ATTR_NAMES,
6835
+ PLAYER_WIRE_STEPS,
6836
+ PLAYER_WIRE_VERSION,
6079
6837
  PX_ANIM_ATTR_NAME,
6080
6838
  PX_ANIM_SRC_ATTR_NAME,
6839
+ PX_PLAYER_SCHEMA_VERSION,
6081
6840
  PX_TRANSFORM_PART_KEYS,
6841
+ PX_UNKNOWN_KEY_ERROR,
6082
6842
  PxAnimatedSvgDocumentSchema,
6083
6843
  PxAnimationDefinitionSchema,
6084
6844
  PxAnimatorConfigSchema,
6085
- PxAnimatorEngine,
6086
- PxAnimatorMode,
6087
6845
  PxAttrValueSchema,
6088
6846
  PxBezierPathSchema,
6089
6847
  PxBindingSchema,
6090
6848
  PxCloneEffectSchema,
6849
+ PxCloneWithout,
6091
6850
  PxDefsSchema,
6092
6851
  PxEasingOrRefSchema,
6093
6852
  PxEffectsSchema,
@@ -6099,7 +6858,8 @@ export {
6099
6858
  PxGradientUnits,
6100
6859
  PxKeyframeSchema,
6101
6860
  PxKeyframeValueSchema,
6102
- PxLoopExtend,
6861
+ PxLoopDirection,
6862
+ PxLoopRepeatAt,
6103
6863
  PxLoopSchema,
6104
6864
  PxMaskedByEffectSchema,
6105
6865
  PxNodeBase,
@@ -6116,6 +6876,10 @@ export {
6116
6876
  PxSvgNodeExtra,
6117
6877
  PxTextEffectSchema,
6118
6878
  PxTextPathEffectSchema,
6879
+ PxTimelineEngine,
6880
+ PxTimelineEngineExtra,
6881
+ PxTimelinePinSchema,
6882
+ PxTimelineSchema,
6119
6883
  PxTransformByEffectSchema,
6120
6884
  PxTransformPartsSchema,
6121
6885
  PxTransformValueSchema,
@@ -6124,21 +6888,35 @@ export {
6124
6888
  TEXT_ATTR,
6125
6889
  TEXT_CONTENT_ATTR,
6126
6890
  TRANSFORM_FN_NAMES,
6891
+ WIRE_VERSION_KEY,
6892
+ WireStepKind,
6893
+ WireVersionRelation,
6894
+ applyAnimatorConfig,
6127
6895
  applyPlayerEffects,
6896
+ applyWireSteps,
6897
+ applyWireStepsDown,
6128
6898
  bezierToSvgPath,
6129
6899
  calcAnimationValues,
6130
6900
  camelCaseToKebabWordIfNeeded,
6131
6901
  clamp,
6132
6902
  collectSampleTimes,
6903
+ compareWireVersion,
6133
6904
  composeTransformParts,
6905
+ convertPlayerDocument,
6134
6906
  createBasicFrameLoopAnimator,
6135
6907
  createPathSampler,
6136
6908
  cubicBezier,
6137
6909
  deepClone,
6138
6910
  describeSchema,
6911
+ diagnoseDocument,
6912
+ diffFieldUniverse,
6139
6913
  diffInEffect,
6914
+ downgradePlayerDocument,
6140
6915
  evaluateMotionPathSegment,
6141
6916
  extendedPathForBrowser,
6917
+ flattenAnimatorTimeline,
6918
+ foldAnimatorConfigShortcuts,
6919
+ formatWireVersion,
6142
6920
  generateNewIds,
6143
6921
  generateUniqueId,
6144
6922
  getAnimatorConfig,
@@ -6149,11 +6927,17 @@ export {
6149
6927
  getNormalizedProps,
6150
6928
  interpolateBeziers,
6151
6929
  interpolateValue,
6930
+ isNativeForced,
6152
6931
  isPxElementFileFormat,
6153
6932
  isPxElementFileFormatDeep,
6154
6933
  isScrollTimeline,
6155
6934
  jsonElementFactory,
6156
6935
  kebabToCamelCaseWord,
6936
+ kfEasing,
6937
+ kfTangentIn,
6938
+ kfTangentOut,
6939
+ kfTime,
6940
+ kfValue,
6157
6941
  layoutGlyphTextChars,
6158
6942
  materialiseAllInTree,
6159
6943
  materialiseAnimatedUseInstances,
@@ -6164,11 +6948,23 @@ export {
6164
6948
  materialiseInternalLoopsInTree,
6165
6949
  materialiseMotionPathInPropAnim,
6166
6950
  materialiseMotionPathsInTree,
6951
+ mayUseNativeScrollTimeline,
6952
+ mergeAnimatorConfig,
6953
+ mergeStaticTransformIntoAnimDef,
6954
+ nestAnimatorTimeline,
6955
+ parseTransformParts,
6956
+ parseWireVersion,
6957
+ planSchemaRelease,
6167
6958
  propAnimIsMotionPath,
6168
6959
  px,
6960
+ readWireVersion,
6961
+ releaseLogProblems,
6962
+ reportDocumentDiagnostics,
6169
6963
  resolveStyle,
6964
+ resolveTimelineEngine,
6170
6965
  reverseEasing,
6171
6966
  sanitiseAttributeValue,
6967
+ schemaFieldUniverse,
6172
6968
  schemaKeys,
6173
6969
  scrollOffsetProgress,
6174
6970
  scrollPhaseInterval,
@@ -6179,7 +6975,9 @@ export {
6179
6975
  splitEasing,
6180
6976
  subdivideCubicBezier,
6181
6977
  toRGBA,
6978
+ validateDocument,
6182
6979
  validateNodeEffects,
6980
+ versionAdvice,
6183
6981
  visualModelAt
6184
6982
  };
6185
6983
  //# sourceMappingURL=index.js.map