@pixodesk/svg-animator-core 1.0.30 → 1.0.34

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
@@ -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 _a, _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], (_a = nums[1]) != null ? _a : 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) {
@@ -615,6 +647,8 @@ var Union = class extends Base {
615
647
  constructor(schemas, defaultVal) {
616
648
  super();
617
649
  this.schemas = schemas;
650
+ /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Union from Tuple. */
651
+ this._kind = "union";
618
652
  this._default = defaultVal != null ? defaultVal : schemas[0]._default;
619
653
  }
620
654
  sanitize(raw) {
@@ -639,6 +673,8 @@ var DiscriminatedUnion = class extends Base {
639
673
  super();
640
674
  this._key = _key;
641
675
  this._schemas = _schemas;
676
+ /** Structural tag read by {@link describeSchema}. */
677
+ this._kind = "discriminatedUnion";
642
678
  this._default = defaultVal != null ? defaultVal : _schemas[0]._default;
643
679
  this._map = /* @__PURE__ */ new Map();
644
680
  for (const s of _schemas) {
@@ -801,6 +837,8 @@ var Rec = class extends Base {
801
837
  constructor(value) {
802
838
  super();
803
839
  this.value = value;
840
+ /** Structural tag read by {@link describeSchema}. */
841
+ this._kind = "record";
804
842
  this._default = {};
805
843
  }
806
844
  sanitize(raw) {
@@ -886,6 +924,8 @@ var Tuple = class extends Base {
886
924
  constructor(schemas) {
887
925
  super();
888
926
  this.schemas = schemas;
927
+ /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Tuple from Union. */
928
+ this._kind = "tuple";
889
929
  this._default = schemas.map((s) => s._default);
890
930
  }
891
931
  sanitize(raw) {
@@ -922,10 +962,20 @@ function schemaKeys(schema) {
922
962
  function describeSchema(schema) {
923
963
  var _a;
924
964
  const s = schema;
925
- if ("_shape" in s) return { kind: "shape", shape: s._shape };
965
+ switch (s._kind) {
966
+ case "union":
967
+ return { kind: "union", members: s.schemas };
968
+ case "discriminatedUnion":
969
+ return { kind: "discriminatedUnion", key: s._key, members: s._schemas };
970
+ case "record":
971
+ return { kind: "record", value: s.value };
972
+ case "tuple":
973
+ return { kind: "tuple", items: s.schemas };
974
+ }
975
+ if ("_shape" in s) return { kind: "shape", shape: s._shape, openValue: s._openSchema };
926
976
  if ("item" in s) return { kind: "array", item: s.item };
927
977
  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() };
978
+ if ("fn" in s) return { kind: "lazy", resolved: (_a = s.resolved) != null ? _a : s.resolved = s.fn() };
929
979
  return { kind: "leaf" };
930
980
  }
931
981
  var px = {
@@ -1065,7 +1115,107 @@ function isPxElementFileFormat(fileJson) {
1065
1115
  }
1066
1116
  function getAnimatorConfig(doc) {
1067
1117
  var _a;
1068
- return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
1118
+ const cfg = (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
1119
+ return cfg ? flattenAnimatorTimeline(cfg) : void 0;
1120
+ }
1121
+ var flattenMemo = /* @__PURE__ */ new WeakMap();
1122
+ function flattenAnimatorTimeline(cfg) {
1123
+ const timeline = cfg.timeline;
1124
+ if (timeline === void 0 || timeline === null || typeof timeline !== "object") return cfg;
1125
+ const memoised = flattenMemo.get(cfg);
1126
+ if (memoised) return memoised;
1127
+ const _a = cfg, { timeline: _dropped } = _a, flat = __objRest(_a, ["timeline"]);
1128
+ if (timeline.type === "scroll" || timeline.type === "view") {
1129
+ flat.timelineSource = "scroll";
1130
+ if (timeline.duration !== void 0) flat.duration = timeline.duration;
1131
+ if (timeline.iterations !== void 0) flat.iterations = timeline.iterations;
1132
+ const scroll = __spreadValues({}, flat.scroll || {});
1133
+ scroll.kind = timeline.type;
1134
+ if (timeline.engine !== void 0) scroll.driver = timeline.engine;
1135
+ if (timeline.axis !== void 0) scroll.axis = timeline.axis;
1136
+ if (timeline.source !== void 0) scroll.source = timeline.source;
1137
+ if (timeline.subject !== void 0) scroll.subject = timeline.subject;
1138
+ if (timeline.smoothing !== void 0) scroll.smoothing = timeline.smoothing;
1139
+ if (timeline.range !== void 0) scroll.range = timeline.range;
1140
+ const pin = timeline.pin;
1141
+ if (typeof pin === "boolean") scroll.pin = pin;
1142
+ else if (pin && typeof pin === "object") {
1143
+ scroll.pin = true;
1144
+ if (pin.align !== void 0) scroll.pinAlign = pin.align;
1145
+ if (pin.top !== void 0) scroll.pinTop = pin.top;
1146
+ if (pin.distance !== void 0) scroll.pinDistance = pin.distance;
1147
+ }
1148
+ flat.scroll = scroll;
1149
+ } else {
1150
+ if (timeline.duration !== void 0) flat.duration = timeline.duration;
1151
+ if (timeline.trigger !== void 0) {
1152
+ const _b = timeline.trigger, { onFinish } = _b, restTrigger = __objRest(_b, ["onFinish"]);
1153
+ if (Object.keys(restTrigger).length) flat.trigger = restTrigger;
1154
+ if (onFinish !== void 0) flat.resetOnFinish = onFinish === "reset";
1155
+ }
1156
+ if (timeline.delay !== void 0) flat.delay = timeline.delay;
1157
+ if (timeline.iterations !== void 0) flat.iterations = timeline.iterations;
1158
+ if (timeline.direction !== void 0) flat.direction = timeline.direction;
1159
+ if (timeline.fill !== void 0) flat.fill = timeline.fill;
1160
+ }
1161
+ flattenMemo.set(cfg, flat);
1162
+ return flat;
1163
+ }
1164
+ function nestAnimatorTimeline(cfg) {
1165
+ if (!cfg || cfg.timeline !== void 0) return cfg;
1166
+ const _a = cfg, {
1167
+ timelineSource,
1168
+ scroll,
1169
+ trigger,
1170
+ delay,
1171
+ iterations,
1172
+ direction,
1173
+ fill,
1174
+ resetOnFinish,
1175
+ duration
1176
+ } = _a, shared = __objRest(_a, [
1177
+ "timelineSource",
1178
+ "scroll",
1179
+ "trigger",
1180
+ "delay",
1181
+ "iterations",
1182
+ "direction",
1183
+ "fill",
1184
+ "resetOnFinish",
1185
+ "duration"
1186
+ ]);
1187
+ if (timelineSource === "scroll") {
1188
+ const timeline2 = { type: (scroll == null ? void 0 : scroll.kind) === "view" ? "view" : "scroll" };
1189
+ if (duration !== void 0) timeline2.duration = duration;
1190
+ if (typeof iterations === "number") timeline2.iterations = iterations;
1191
+ if (scroll) {
1192
+ if (scroll.driver !== void 0) timeline2.engine = scroll.driver;
1193
+ if (scroll.axis !== void 0) timeline2.axis = scroll.axis;
1194
+ if (scroll.source !== void 0) timeline2.source = scroll.source;
1195
+ if (scroll.subject !== void 0) timeline2.subject = scroll.subject;
1196
+ if (scroll.smoothing !== void 0) timeline2.smoothing = scroll.smoothing;
1197
+ if (scroll.range !== void 0) timeline2.range = scroll.range;
1198
+ const hasPinParams = scroll.pinAlign !== void 0 || scroll.pinTop !== void 0 || scroll.pinDistance !== void 0;
1199
+ if (hasPinParams) {
1200
+ 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 } : {});
1201
+ } else if (scroll.pin !== void 0) {
1202
+ timeline2.pin = scroll.pin;
1203
+ }
1204
+ }
1205
+ return __spreadProps(__spreadValues({}, shared), { timeline: timeline2 });
1206
+ }
1207
+ const timeline = { type: "clock" };
1208
+ if (duration !== void 0) timeline.duration = duration;
1209
+ if (trigger !== void 0 || resetOnFinish) {
1210
+ const t = __spreadValues({}, trigger || {});
1211
+ if (resetOnFinish) t.onFinish = "reset";
1212
+ timeline.trigger = t;
1213
+ }
1214
+ if (delay !== void 0) timeline.delay = delay;
1215
+ if (iterations !== void 0) timeline.iterations = iterations;
1216
+ if (direction !== void 0) timeline.direction = direction;
1217
+ if (fill !== void 0) timeline.fill = fill;
1218
+ return Object.keys(timeline).length > 1 ? __spreadProps(__spreadValues({}, shared), { timeline }) : shared;
1069
1219
  }
1070
1220
  function getDefs(doc) {
1071
1221
  var _a;
@@ -1077,7 +1227,7 @@ function getBindings(doc) {
1077
1227
  if (!doc) return void 0;
1078
1228
  const animateById = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animateById;
1079
1229
  if (!animateById) return void 0;
1080
- return Object.entries(animateById).map(([id, anim]) => ({ id, animate: anim }));
1230
+ return Object.entries(animateById).map(([id, anim]) => ({ id: id.startsWith("#") ? id.slice(1) : id, animate: anim }));
1081
1231
  }
1082
1232
  function getChildren(doc) {
1083
1233
  return doc == null ? void 0 : doc.children;
@@ -1093,27 +1243,27 @@ var PxKeyframeValueSchema = implementsInterface()(px.union([
1093
1243
  // e.g. for colors
1094
1244
  px.number(),
1095
1245
  px.array(px.number()),
1096
- px.lazy(() => PxTransformPartsSchema, {}),
1246
+ // ORDER LAW: the key-discriminated object shapes (`{path}`, `{paths}`) come BEFORE the
1247
+ // all-optional transform-parts record. In default (non-strict) mode that record accepts
1248
+ // ANY object (every key optional, unknown keys ignored), so listing it earlier made
1249
+ // Union.sanitize route `{path}`/`{paths}` values into it and strip them to `{}` —
1250
+ // silent morph-data loss (repro: the editor's keyframeValueSanitize spec). Validity is
1251
+ // order-independent (`some()`); only sanitize routing depends on this order.
1097
1252
  px.object({ path: px.string() }),
1098
1253
  px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
1099
1254
  // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
1100
- px.lazy(() => px.array(PxGradientStopSchema), [])
1255
+ px.lazy(() => px.array(PxGradientStopSchema), []),
1256
+ px.lazy(() => PxTransformPartsSchema, {})
1101
1257
  ]));
1102
1258
  var PxKeyframeSchema = implementsInterface()(px.object({
1103
1259
  time: px.number().optional(),
1104
- t: px.number().optional(),
1105
1260
  value: PxKeyframeValueSchema.optional(),
1106
- v: PxKeyframeValueSchema.optional(),
1107
1261
  easing: PxEasingOrRefSchema.optional(),
1108
- e: PxEasingOrRefSchema.optional(),
1109
1262
  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)
1263
+ tangentIn: px.tuple([px.number(), px.number()]).optional()
1264
+ // (`selected` — editor timeline-selection UI state — was REMOVED from the wire
1265
+ // (review §1.3): editor data lives under `meta`. The editor still carries it on
1266
+ // its internal COPY-PASTE payload, which never validates against this schema.)
1117
1267
  }));
1118
1268
  var PxLoopSchema = implementsInterface()(px.object({
1119
1269
  segmentCount: px.number().optional(),
@@ -1123,7 +1273,6 @@ var PxLoopSchema = implementsInterface()(px.object({
1123
1273
  var PxPropertyAnimationSchema = implementsInterface()(px.object({
1124
1274
  value: PxKeyframeValueSchema.optional(),
1125
1275
  keyframes: px.array(PxKeyframeSchema).optional(),
1126
- kfs: px.array(PxKeyframeSchema).optional(),
1127
1276
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
1128
1277
  autoOrient: px.boolean().optional()
1129
1278
  }));
@@ -1151,6 +1300,11 @@ var PxElementAnimationSchema = implementsInterface()(px.union([
1151
1300
  var PxTriggerSchema = implementsInterface()(px.object({
1152
1301
  startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
1153
1302
  outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
1303
+ // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
1304
+ // `fill`) or `'reset'` (snap back to the start state). The trigger-vocabulary home of
1305
+ // the old top-level `resetOnFinish: boolean` (read-legacy) — review §2.3: both
1306
+ // "what happens at the end" knobs now sit side by side and share one style of value.
1307
+ onFinish: px.enum(["hold", "reset"]).optional(),
1154
1308
  scrollIntoViewThreshold: px.number().optional()
1155
1309
  }));
1156
1310
  var PxGlyphSchema = implementsInterface()(px.object({
@@ -1158,8 +1312,8 @@ var PxGlyphSchema = implementsInterface()(px.object({
1158
1312
  d: px.string()
1159
1313
  }));
1160
1314
  var PxGlyphFontSchema = implementsInterface()(px.object({
1161
- fFamily: px.string(),
1162
- style: px.string(),
1315
+ fontFamily: px.string(),
1316
+ fontStyle: px.string(),
1163
1317
  ascent: px.number(),
1164
1318
  unitsPerEm: px.number(),
1165
1319
  glyphs: px.record(PxGlyphSchema)
@@ -1167,7 +1321,9 @@ var PxGlyphFontSchema = implementsInterface()(px.object({
1167
1321
  var PxDefsSchema = implementsInterface()(px.object({
1168
1322
  easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
1169
1323
  animations: px.record(PxAnimationDefinitionSchema).optional(),
1170
- styles: px.record(px.any()).optional(),
1324
+ // Review §2.6: the schema now matches the declared type — a style preset is a flat
1325
+ // record of string|number attribute values, nothing nested.
1326
+ styles: px.record(px.record(px.union([px.string(), px.number()]))).optional(),
1171
1327
  glyphs: px.record(PxGlyphFontSchema).optional()
1172
1328
  }));
1173
1329
  var PX_SCROLL_PHASES = ["cover", "contain", "entry", "exit", "entry-crossing", "exit-crossing"];
@@ -1193,22 +1349,54 @@ var PxScrollSchema = implementsInterface()(px.object({
1193
1349
  pinDistance: px.number().optional(),
1194
1350
  range: PxScrollRangeSchema.optional()
1195
1351
  }));
1196
- var PxAnimatorConfigSchema = implementsInterface()(px.object({
1197
- mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.waapi, PxAnimatorMode.frames]).optional(),
1352
+ var PxTimelinePinSchema = px.object({
1353
+ align: px.enum(["top", "center", "bottom"]).optional(),
1354
+ top: px.number().optional(),
1355
+ distance: px.number().optional()
1356
+ });
1357
+ var PxClockTimelineSchema = px.object({
1358
+ type: px.literal("clock"),
1359
+ // §2.8: duration is a property of the TIMELINE — how long one pass takes.
1198
1360
  duration: px.number().optional(),
1361
+ trigger: PxTriggerSchema.optional(),
1199
1362
  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
1363
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1203
1364
  fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
1204
- direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
1365
+ direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional()
1366
+ });
1367
+ var scrollishTimelineShape = {
1368
+ // §2.8: duration is a property of the TIMELINE — under scrubbing it is the keyframe
1369
+ // span the scroll range maps onto.
1370
+ duration: px.number().optional(),
1371
+ // Finite repeat count IS meaningful when scrubbing — the scroll range maps onto
1372
+ // duration × iterations (rule D4; `'infinite'` cannot map to a range, so no literal here).
1373
+ iterations: px.number().optional(),
1374
+ engine: px.enum(["custom", "native"]).optional(),
1375
+ axis: px.enum(["block", "inline", "x", "y"]).optional(),
1376
+ source: px.enum(["nearest", "root"]).optional(),
1377
+ subject: px.string().optional(),
1378
+ // 'parent' | 'scroller' | any CSS selector
1379
+ smoothing: px.number().optional(),
1380
+ // ms
1381
+ pin: px.union([px.boolean(), PxTimelinePinSchema]).optional(),
1382
+ range: PxScrollRangeSchema.optional()
1383
+ };
1384
+ var PxScrollTimelineSchema = px.object(__spreadValues({ type: px.literal("scroll") }, scrollishTimelineShape));
1385
+ var PxViewTimelineSchema = px.object(__spreadValues({ type: px.literal("view") }, scrollishTimelineShape));
1386
+ var PxTimelineSchema = px.discriminatedUnion("type", [
1387
+ PxClockTimelineSchema,
1388
+ PxScrollTimelineSchema,
1389
+ PxViewTimelineSchema
1390
+ ]);
1391
+ var PxAnimatorConfigSchema = implementsInterface()(px.object({
1392
+ mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.waapi, PxAnimatorMode.frames]).optional(),
1393
+ // (`duration` lives INSIDE `timeline` on the wire — §2.8; the flat field below
1394
+ // exists only on the runtime view, like the rest of the playback dynamics.)
1205
1395
  frameRate: px.number().optional(),
1206
- trigger: PxTriggerSchema.optional(),
1207
- resetOnFinish: px.boolean().optional(),
1396
+ // THE spelling of "what advances progress" — clock / scroll / view (review §2.1).
1397
+ timeline: PxTimelineSchema.optional(),
1208
1398
  definitions: PxDefsSchema.optional(),
1209
1399
  animateById: px.record(PxElementAnimationSchema).optional(),
1210
- timelineSource: px.string().optional(),
1211
- scroll: PxScrollSchema.optional(),
1212
1400
  debugInstName: px.string().optional()
1213
1401
  }));
1214
1402
  var PxBindingSchema = implementsInterface()(px.object({
@@ -1227,18 +1415,18 @@ var PxAttrValueSchema = px.union([
1227
1415
  ]);
1228
1416
  var PxAnimatableNumberSchema = px.union([
1229
1417
  px.number(),
1230
- px.object({ value: px.number() }),
1231
- PxPropertyAnimationSchema
1418
+ PxPropertyAnimationSchema,
1419
+ px.object({ value: px.number() })
1232
1420
  ]);
1233
1421
  var PxAnimatableVec2Schema = px.union([
1234
1422
  px.tuple([px.number(), px.number()]),
1235
- px.object({ value: px.tuple([px.number(), px.number()]) }),
1236
- PxPropertyAnimationSchema
1423
+ PxPropertyAnimationSchema,
1424
+ px.object({ value: px.tuple([px.number(), px.number()]) })
1237
1425
  ]);
1238
1426
  var PxAnimatableStringSchema = px.union([
1239
1427
  px.string(),
1240
- px.object({ value: px.string() }),
1241
- PxPropertyAnimationSchema
1428
+ PxPropertyAnimationSchema,
1429
+ px.object({ value: px.string() })
1242
1430
  ]);
1243
1431
  var PxTransformByEffectSchema = implementsInterface()(px.object({
1244
1432
  translate: PxAnimatableVec2Schema.optional(),
@@ -1268,8 +1456,7 @@ var PxMaskedByEffectSchema = implementsInterface()(px.object({
1268
1456
  height: px.number().optional()
1269
1457
  }));
1270
1458
  var PxClipPathEffectSchema = implementsInterface()(px.object({
1271
- d: PxAnimatableStringSchema.optional(),
1272
- animate: PxPropertyAnimationSchema.optional()
1459
+ d: PxAnimatableStringSchema.optional()
1273
1460
  }));
1274
1461
  var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1275
1462
  offset: PxAnimatableNumberSchema.optional(),
@@ -1277,7 +1464,6 @@ var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1277
1464
  subPaths: px.enum([PxStrokeTrimSubPaths.separate, PxStrokeTrimSubPaths.combined]).optional()
1278
1465
  }));
1279
1466
  var PxRetimeEffectSchema = implementsInterface()(px.object({
1280
- sourceId: px.string().optional(),
1281
1467
  start: px.number().optional(),
1282
1468
  stretch: px.number().optional(),
1283
1469
  timeCrop: px.tuple([px.number(), px.number()]).optional()
@@ -1300,11 +1486,11 @@ var PxAnimatableGradientStopsSchema = px.union([
1300
1486
  var PxFillGradientEffectSchema = implementsInterface()(px.object({
1301
1487
  // Contextual kind — the `type` convention, see `PxNodeBase.type`.
1302
1488
  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(),
1489
+ start: PxAnimatableVec2Schema.optional(),
1490
+ end: PxAnimatableVec2Schema.optional(),
1491
+ center: PxAnimatableVec2Schema.optional(),
1492
+ radius: PxAnimatableNumberSchema.optional(),
1493
+ focal: PxAnimatableVec2Schema.optional(),
1308
1494
  stops: PxAnimatableGradientStopsSchema.optional(),
1309
1495
  gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
1310
1496
  spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
@@ -1500,9 +1686,11 @@ function generateNewIds(doc) {
1500
1686
  const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animateById;
1501
1687
  if (docAnimate && typeof docAnimate === "object") {
1502
1688
  const updatedAnimate = {};
1503
- for (const [id, anim] of Object.entries(docAnimate)) {
1689
+ for (const [key, anim] of Object.entries(docAnimate)) {
1690
+ const hashed = key.startsWith("#");
1691
+ const id = hashed ? key.slice(1) : key;
1504
1692
  const newId = (_b = idMap.get(id)) != null ? _b : id;
1505
- updatedAnimate[newId] = anim;
1693
+ updatedAnimate[hashed ? "#" + newId : newId] = anim;
1506
1694
  }
1507
1695
  cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animateById: updatedAnimate });
1508
1696
  }
@@ -1595,7 +1783,7 @@ function getNormalizedProps(props) {
1595
1783
  let value = props[rawKey];
1596
1784
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
1597
1785
  propsCopy[key] = toRGBA(value);
1598
- } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes && !value.kfs) {
1786
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes) {
1599
1787
  const parts = value.value && typeof value.value === "object" ? value.value : value;
1600
1788
  propsCopy["transform"] = composeTransformParts(parts, { withUnits: false });
1601
1789
  } else if (TRANSFORM_FN_NAMES.has(key)) {
@@ -1635,12 +1823,12 @@ function getKfEasing(kf) {
1635
1823
  return (_a = kf.easing) != null ? _a : kf.e;
1636
1824
  }
1637
1825
  function propAnimIsMotionPath(anim) {
1638
- var _a, _b, _c;
1639
- const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
1826
+ var _a, _b;
1827
+ const kfs = anim.keyframes;
1640
1828
  if (!Array.isArray(kfs)) return false;
1641
1829
  if (anim.autoOrient) return true;
1642
1830
  for (const kf of kfs) {
1643
- if (((_b = kf.tangentIn) != null ? _b : kf.ti) || ((_c = kf.tangentOut) != null ? _c : kf.to)) return true;
1831
+ if (((_a = kf.tangentIn) != null ? _a : kf.ti) || ((_b = kf.tangentOut) != null ? _b : kf.to)) return true;
1644
1832
  }
1645
1833
  return false;
1646
1834
  }
@@ -1701,14 +1889,14 @@ var DEFAULT_FLATNESS_TOL = 0.5;
1701
1889
  var DEFAULT_ROTATION_TOL = 5;
1702
1890
  var DEFAULT_MAX_SAMPLES = 32;
1703
1891
  function materialiseMotionPathInPropAnim(anim, opts) {
1704
- var _a, _b, _c, _d;
1892
+ var _a, _b, _c;
1705
1893
  if (!propAnimIsMotionPath(anim)) return anim;
1706
- const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
1894
+ const kfs = anim.keyframes;
1707
1895
  if (!Array.isArray(kfs) || kfs.length < 2) return anim;
1708
1896
  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;
1897
+ const flatnessTol = (_a = opts == null ? void 0 : opts.flatnessTolerance) != null ? _a : DEFAULT_FLATNESS_TOL;
1898
+ const rotationTol = (_b = opts == null ? void 0 : opts.rotationTolerance) != null ? _b : DEFAULT_ROTATION_TOL;
1899
+ const maxSamples = (_c = opts == null ? void 0 : opts.maxSamplesPerSegment) != null ? _c : DEFAULT_MAX_SAMPLES;
1712
1900
  const out = [];
1713
1901
  const firstPos = getKfTranslate(kfs[0]);
1714
1902
  if (!firstPos) return anim;
@@ -1737,7 +1925,7 @@ function materialiseMotionPathInPropAnim(anim, opts) {
1737
1925
  const lastInE = getKfEasing(kfs[kfs.length - 1]);
1738
1926
  if (lastInE) out[out.length - 1].e = lastInE;
1739
1927
  if (autoOrient) unwrapAutoOrientRotations(out);
1740
- const result = { kfs: out };
1928
+ const result = { keyframes: out };
1741
1929
  if (anim.loop !== void 0) result.loop = anim.loop;
1742
1930
  return result;
1743
1931
  }
@@ -2399,7 +2587,7 @@ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2399
2587
  }
2400
2588
  function normalizeKeyframes(propName, propAnim, duration, defs) {
2401
2589
  var _a, _b, _c, _d, _e, _f, _g;
2402
- const keyframes = propAnim.keyframes || propAnim.kfs || [];
2590
+ const keyframes = propAnim.keyframes || [];
2403
2591
  const normalized = [];
2404
2592
  for (const kf of keyframes) {
2405
2593
  const timePct = (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
@@ -2444,17 +2632,16 @@ function mergeAnimationDefinitions(animations) {
2444
2632
  return merged;
2445
2633
  }
2446
2634
  function materialiseInternalLoopsInPropAnim(propName, propAnim, duration) {
2447
- var _a;
2448
2635
  const loopRaw = propAnim.loop;
2449
2636
  if (loopRaw === void 0 || loopRaw === null || loopRaw === false) return propAnim;
2450
2637
  const loop = loopRaw === true ? {} : loopRaw;
2451
- const rawKfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
2638
+ const rawKfs = propAnim.keyframes;
2452
2639
  if (!Array.isArray(rawKfs) || rawKfs.length < 2) return propAnim;
2453
2640
  const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2454
2641
  const isColour = COLOUR_ATTR_NAMES.has(propNameKebab);
2455
2642
  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;
2643
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2644
+ const t = (_a = kf.t) != null ? _a : kf.time;
2458
2645
  let v = (_b = kf.v) != null ? _b : kf.value;
2459
2646
  if (propName === "d") v = normalizePathValue(v);
2460
2647
  if (isColour) v = (_c = parseColor(v)) != null ? _c : v;
@@ -2465,7 +2652,7 @@ function materialiseInternalLoopsInPropAnim(propName, propAnim, duration) {
2465
2652
  return out2;
2466
2653
  });
2467
2654
  const expanded = expandLoopKeyframes(propName, kfs, loop, duration);
2468
- const out = { kfs: expanded };
2655
+ const out = { keyframes: expanded };
2469
2656
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2470
2657
  return out;
2471
2658
  }
@@ -2507,6 +2694,36 @@ var _elementIdCounter = 0;
2507
2694
  function generateElementId() {
2508
2695
  return "_px_el_" + ++_elementIdCounter;
2509
2696
  }
2697
+ function mergeStaticTransformIntoAnimDef(animDef, staticTransform) {
2698
+ if (!animDef) return animDef;
2699
+ const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
2700
+ if (!staticParts || !Object.keys(staticParts).length) return animDef;
2701
+ const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues(__spreadValues({}, staticParts), v) : v;
2702
+ const transformAnim = animDef["transform"];
2703
+ if (transformAnim && typeof transformAnim === "object") {
2704
+ const anim = transformAnim;
2705
+ if (Array.isArray(anim.keyframes)) {
2706
+ const out = __spreadProps(__spreadValues({}, anim), {
2707
+ keyframes: anim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: mergeKfValue(kf.value) }))
2708
+ });
2709
+ if (out.value !== void 0) out.value = mergeKfValue(out.value);
2710
+ return __spreadProps(__spreadValues({}, animDef), { transform: out });
2711
+ }
2712
+ return animDef;
2713
+ }
2714
+ const channels = Object.keys(animDef).filter((k) => TRANSFORM_FN_NAMES.has(k));
2715
+ if (channels.length !== 1) return animDef;
2716
+ const ch = channels[0];
2717
+ const chAnim = animDef[ch];
2718
+ if (!chAnim || typeof chAnim !== "object" || !Array.isArray(chAnim.keyframes)) return animDef;
2719
+ const lifted = __spreadProps(__spreadValues({}, chAnim), {
2720
+ keyframes: chAnim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: __spreadProps(__spreadValues({}, staticParts), { [ch]: kf.value }) }))
2721
+ });
2722
+ if (lifted.value !== void 0) lifted.value = __spreadProps(__spreadValues({}, staticParts), { [ch]: lifted.value });
2723
+ const rest = __spreadValues({}, animDef);
2724
+ delete rest[ch];
2725
+ return __spreadProps(__spreadValues({}, rest), { transform: lifted });
2726
+ }
2510
2727
  function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.waapi) {
2511
2728
  const normalized = {};
2512
2729
  for (const [propName, propAnim] of Object.entries(animDef)) {
@@ -2515,7 +2732,7 @@ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimat
2515
2732
  }
2516
2733
  const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2517
2734
  if (normalizedKfs.length > 0) {
2518
- const out = { kfs: normalizedKfs };
2735
+ const out = { keyframes: normalizedKfs };
2519
2736
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2520
2737
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2521
2738
  normalized[propName] = engine === PxAnimatorEngine.waapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
@@ -2528,11 +2745,11 @@ function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2528
2745
  const defs = getDefs(doc);
2529
2746
  const duration = animatorConfig.duration || 1e3;
2530
2747
  const bindings = [];
2531
- const processAnimation = (id, animate) => {
2748
+ const processAnimation = (id, animate, staticTransform) => {
2532
2749
  if (!animate) return null;
2533
2750
  const animDefs = resolveElementAnimation(animate, defs);
2534
2751
  if (animDefs.length === 0) return null;
2535
- const merged = mergeAnimationDefinitions(animDefs);
2752
+ const merged = mergeStaticTransformIntoAnimDef(mergeAnimationDefinitions(animDefs), staticTransform);
2536
2753
  const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);
2537
2754
  if (Object.keys(normalizedAnim).length === 0) return null;
2538
2755
  return {
@@ -2552,7 +2769,7 @@ function getNormalisedBindings(doc, engine = PxAnimatorEngine.waapi) {
2552
2769
  if (inlineAnim && Object.keys(inlineAnim).length > 0) {
2553
2770
  const nodeId = node.id || generateElementId();
2554
2771
  node.id = nodeId;
2555
- const normalized = processAnimation(nodeId, inlineAnim);
2772
+ const normalized = processAnimation(nodeId, inlineAnim, node.transform);
2556
2773
  if (normalized) bindings.push(normalized);
2557
2774
  }
2558
2775
  if (node.children) {
@@ -2590,7 +2807,7 @@ function getKeyframesPair(keyframes, progress) {
2590
2807
  }
2591
2808
  function calcPropertyValue(propName, propAnim, progress) {
2592
2809
  var _a, _b, _c, _d, _e, _f, _g;
2593
- const keyframes = propAnim.kfs || propAnim.keyframes || [];
2810
+ const keyframes = propAnim.keyframes || [];
2594
2811
  if (keyframes.length === 0) return null;
2595
2812
  const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
2596
2813
  let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a = prevKf.t) != null ? _a : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
@@ -3214,7 +3431,7 @@ function liftBodyTranslate(node, transformBy) {
3214
3431
  if (split.rest) node.transform = split.rest;
3215
3432
  else delete node.transform;
3216
3433
  }
3217
- } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes && !node.transform.kfs) {
3434
+ } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes) {
3218
3435
  const wrapped = node.transform.value;
3219
3436
  const isWrapped = !!(wrapped && typeof wrapped === "object");
3220
3437
  const value = isWrapped ? wrapped : node.transform;
@@ -3349,12 +3566,12 @@ function synthesiseGradientDef(fx, id, ctx) {
3349
3566
  id
3350
3567
  };
3351
3568
  if (fx.type === PxGradientType.linear) {
3352
- applyGeomVec(out, "x1", "y1", fx.p1);
3353
- applyGeomVec(out, "x2", "y2", fx.p2);
3569
+ applyGeomVec(out, "x1", "y1", fx.start);
3570
+ applyGeomVec(out, "x2", "y2", fx.end);
3354
3571
  } else {
3355
- applyGeomVec(out, "cx", "cy", fx.c);
3356
- applyGeomNumber(out, "r", fx.r);
3357
- applyGeomVec(out, "fx", "fy", fx.fp);
3572
+ applyGeomVec(out, "cx", "cy", fx.center);
3573
+ applyGeomNumber(out, "r", fx.radius);
3574
+ applyGeomVec(out, "fx", "fy", fx.focal);
3358
3575
  }
3359
3576
  if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
3360
3577
  if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
@@ -3482,8 +3699,7 @@ function formatOffset(o) {
3482
3699
 
3483
3700
  // src/effects/clipPathEffect.ts
3484
3701
  function applyClipPathEffect(node, fx, ctx) {
3485
- var _a, _b;
3486
- if (!fx || !fx.d && !fx.animate) return node;
3702
+ if (!(fx == null ? void 0 : fx.d)) return node;
3487
3703
  const clipId = genId(ctx, "clip");
3488
3704
  const pathChild = { type: "path" };
3489
3705
  const read = readAnimatable(fx.d);
@@ -3495,11 +3711,6 @@ function applyClipPathEffect(node, fx, ctx) {
3495
3711
  if (pathChild.d !== void 0) pathChild.d = pathString(pathChild.d);
3496
3712
  }
3497
3713
  }
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
3714
  ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
3504
3715
  node.clipPath = "url(#" + clipId + ")";
3505
3716
  return node;
@@ -3644,7 +3855,7 @@ function readTransformationFromBody(node) {
3644
3855
  if (parts.origin) out.origin = parts.origin;
3645
3856
  return Object.keys(out).length ? out : void 0;
3646
3857
  }
3647
- if (node.transform && typeof node.transform === "object" && !node.transform.keyframes && !node.transform.kfs) {
3858
+ if (node.transform && typeof node.transform === "object" && !node.transform.keyframes) {
3648
3859
  const wrapped = node.transform.value;
3649
3860
  const value = wrapped && typeof wrapped === "object" ? wrapped : node.transform;
3650
3861
  if (value && typeof value === "object") {
@@ -5451,9 +5662,9 @@ var fmt2 = (n) => {
5451
5662
  return Object.is(r, -0) ? "0" : String(r);
5452
5663
  };
5453
5664
  function buildOffsetPath(propAnim) {
5454
- var _a, _b, _c, _d;
5665
+ var _a, _b, _c;
5455
5666
  if (propAnim.alongPathMode !== "offsetPath") return void 0;
5456
- const kfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
5667
+ const kfs = propAnim.keyframes;
5457
5668
  if (!kfs || kfs.length < 2) return void 0;
5458
5669
  const first = kfValue(kfs[0]);
5459
5670
  const anchor = (first == null ? void 0 : first.origin) && first.origin.length >= 2 ? [first.origin[0], first.origin[1]] : [0, 0];
@@ -5464,7 +5675,7 @@ function buildOffsetPath(propAnim) {
5464
5675
  if (!tr || tr.length < 2) return void 0;
5465
5676
  const parts = Object.keys(v);
5466
5677
  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];
5678
+ const o = (_a = v == null ? void 0 : v.origin) != null ? _a : [0, 0];
5468
5679
  if (o[0] !== anchor[0] || o[1] !== anchor[1]) return void 0;
5469
5680
  points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);
5470
5681
  }
@@ -5473,8 +5684,8 @@ function buildOffsetPath(propAnim) {
5473
5684
  const segLens = [];
5474
5685
  for (let i = 0; i < points.length - 1; i++) {
5475
5686
  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];
5687
+ const to = (_b = kfTangentOut(kfs[i])) != null ? _b : [0, 0];
5688
+ const ti = (_c = kfTangentIn(kfs[i + 1])) != null ? _c : [0, 0];
5478
5689
  const c1 = [p0[0] + to[0], p0[1] + to[1]];
5479
5690
  const c2 = [p1[0] + ti[0], p1[1] + ti[1]];
5480
5691
  d += "C" + fmt2(c1[0]) + "," + fmt2(c1[1]) + "," + fmt2(c2[0]) + "," + fmt2(c2[1]) + "," + fmt2(p1[0]) + "," + fmt2(p1[1]);
@@ -6116,6 +6327,8 @@ export {
6116
6327
  PxSvgNodeExtra,
6117
6328
  PxTextEffectSchema,
6118
6329
  PxTextPathEffectSchema,
6330
+ PxTimelinePinSchema,
6331
+ PxTimelineSchema,
6119
6332
  PxTransformByEffectSchema,
6120
6333
  PxTransformPartsSchema,
6121
6334
  PxTransformValueSchema,
@@ -6139,6 +6352,7 @@ export {
6139
6352
  diffInEffect,
6140
6353
  evaluateMotionPathSegment,
6141
6354
  extendedPathForBrowser,
6355
+ flattenAnimatorTimeline,
6142
6356
  generateNewIds,
6143
6357
  generateUniqueId,
6144
6358
  getAnimatorConfig,
@@ -6164,6 +6378,9 @@ export {
6164
6378
  materialiseInternalLoopsInTree,
6165
6379
  materialiseMotionPathInPropAnim,
6166
6380
  materialiseMotionPathsInTree,
6381
+ mergeStaticTransformIntoAnimDef,
6382
+ nestAnimatorTimeline,
6383
+ parseTransformParts,
6167
6384
  propAnimIsMotionPath,
6168
6385
  px,
6169
6386
  resolveStyle,