@pixodesk/svg-animator-web 1.0.35 → 1.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -50,7 +50,7 @@ var PixodeskAnimator = (() => {
50
50
  // src/index.prerendered-waapi.ts
51
51
  var index_prerendered_waapi_exports = {};
52
52
  __export(index_prerendered_waapi_exports, {
53
- PX_ANIMATOR_DATA_KEY: () => PX_ANIMATOR_DATA_KEY,
53
+ PX_ANIMATOR_DOC_KEY: () => PX_ANIMATOR_DOC_KEY,
54
54
  createAnimator: () => createPrerenderedWaapiAnimator,
55
55
  setupAnimationTriggers: () => setupAnimationTriggers
56
56
  });
@@ -280,7 +280,7 @@ var PixodeskAnimator = (() => {
280
280
  }
281
281
  return void 0;
282
282
  }
283
- var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
283
+ var COLOR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
284
284
  var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
285
285
  var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
286
286
  function composeTransformParts(parts, opts) {
@@ -587,6 +587,7 @@ var PixodeskAnimator = (() => {
587
587
  return false;
588
588
  }
589
589
  };
590
+ var UNION_MEMBER_ERROR_LIMIT = 4;
590
591
  var Union = class extends Base {
591
592
  constructor(schemas, defaultVal) {
592
593
  super();
@@ -605,7 +606,35 @@ var PixodeskAnimator = (() => {
605
606
  var _a;
606
607
  const probe = ctx && { errors: [], warnings: [], strict: ctx.strict };
607
608
  if (this.schemas.some((s) => s.isValid(raw, probe, path ? [...path] : void 0))) return true;
608
- 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));
609
+ if (!ctx) return false;
610
+ const base = pathStr(path != null ? path : []);
611
+ ctx.errors.push(base + ": no union member matched for value " + ((_a = JSON.stringify(raw)) != null ? _a : "").slice(0, 240));
612
+ let best;
613
+ let bestDepth = -1;
614
+ const leafExpectations = [];
615
+ for (const member of this.schemas) {
616
+ const sink = { errors: [], warnings: [], strict: ctx.strict };
617
+ member.isValid(raw, sink, path ? [...path] : void 0);
618
+ if (!sink.errors.length) continue;
619
+ const depth = Math.max(...sink.errors.map((e) => e.slice(0, e.indexOf(":")).length));
620
+ if (depth > bestDepth || depth === bestDepth && best && sink.errors.length < best.length) {
621
+ bestDepth = depth;
622
+ best = sink.errors;
623
+ }
624
+ if (depth <= base.length) {
625
+ for (const e of sink.errors) {
626
+ const m = /: expected (.+?), got /.exec(e);
627
+ if (m && !leafExpectations.includes(m[1])) leafExpectations.push(m[1]);
628
+ }
629
+ }
630
+ }
631
+ if (best && bestDepth > base.length) {
632
+ for (const e of best.slice(0, UNION_MEMBER_ERROR_LIMIT)) {
633
+ if (!ctx.errors.includes(e)) ctx.errors.push(e);
634
+ }
635
+ } else if (leafExpectations.length) {
636
+ ctx.errors.push(base + ": expected " + leafExpectations.join(" | "));
637
+ }
609
638
  return false;
610
639
  }
611
640
  _canSanitize(raw) {
@@ -957,6 +986,76 @@ var PixodeskAnimator = (() => {
957
986
  };
958
987
 
959
988
  // ../svg-animator-core/src/format/PxAnimatorConstants.ts
989
+ var PxFillMode = {
990
+ forwards: "forwards",
991
+ backwards: "backwards",
992
+ both: "both",
993
+ none: "none"
994
+ };
995
+ var PxPlaybackDirection = {
996
+ normal: "normal",
997
+ reverse: "reverse",
998
+ alternate: "alternate",
999
+ alternateReverse: "alternate-reverse"
1000
+ };
1001
+ var PxStartOn = {
1002
+ load: "load",
1003
+ mouseOver: "mouseOver",
1004
+ click: "click",
1005
+ scrollIntoView: "scrollIntoView",
1006
+ programmatic: "programmatic"
1007
+ };
1008
+ var PxOutAction = {
1009
+ continue: "continue",
1010
+ pause: "pause",
1011
+ reset: "reset",
1012
+ reverse: "reverse"
1013
+ };
1014
+ var PxFinishAction = {
1015
+ hold: "hold",
1016
+ reset: "reset"
1017
+ };
1018
+ var PxScrollKind = {
1019
+ view: "view",
1020
+ scroll: "scroll"
1021
+ };
1022
+ var PxScrollAxis = {
1023
+ block: "block",
1024
+ inline: "inline",
1025
+ x: "x",
1026
+ y: "y"
1027
+ };
1028
+ var PxScrollSource = {
1029
+ nearest: "nearest",
1030
+ root: "root"
1031
+ };
1032
+ var PxPinAlign = {
1033
+ top: "top",
1034
+ center: "center",
1035
+ bottom: "bottom"
1036
+ };
1037
+ var PxScrollPhase = {
1038
+ cover: "cover",
1039
+ contain: "contain",
1040
+ entry: "entry",
1041
+ exit: "exit",
1042
+ entryCrossing: "entry-crossing",
1043
+ exitCrossing: "exit-crossing"
1044
+ };
1045
+ var PxAlongPathMode = {
1046
+ sampled: "sampled",
1047
+ offsetPath: "offsetPath"
1048
+ };
1049
+ var PX_TIMELINE_SHARED_KEYS = ["duration", "iterations", "engine", "frameRate"];
1050
+ var PX_TIME_ONLY_TIMELINE_KEYS = ["trigger", "delay", "fillMode", "direction"];
1051
+ var PX_FLAT_RUNTIME_VIEW_KEYS = [
1052
+ ...PX_TIMELINE_SHARED_KEYS,
1053
+ ...PX_TIME_ONLY_TIMELINE_KEYS,
1054
+ "fill",
1055
+ "resetOnFinish",
1056
+ "timelineSource",
1057
+ "scroll"
1058
+ ];
960
1059
  var PxTimelineEngine = {
961
1060
  native: "native",
962
1061
  js: "js"
@@ -964,6 +1063,19 @@ var PixodeskAnimator = (() => {
964
1063
  var PxTimelineEngineExtra = __spreadProps(__spreadValues({}, PxTimelineEngine), {
965
1064
  auto: "auto"
966
1065
  });
1066
+ var PX_TRIGGER_DEFAULTS = {
1067
+ startOn: "load",
1068
+ outAction: "continue",
1069
+ scrollIntoViewThreshold: 0
1070
+ };
1071
+ function resolveTrigger(trigger) {
1072
+ var _a, _b, _c;
1073
+ return {
1074
+ startOn: (_a = trigger == null ? void 0 : trigger.startOn) != null ? _a : PX_TRIGGER_DEFAULTS.startOn,
1075
+ outAction: (_b = trigger == null ? void 0 : trigger.outAction) != null ? _b : PX_TRIGGER_DEFAULTS.outAction,
1076
+ scrollIntoViewThreshold: (_c = trigger == null ? void 0 : trigger.scrollIntoViewThreshold) != null ? _c : PX_TRIGGER_DEFAULTS.scrollIntoViewThreshold
1077
+ };
1078
+ }
967
1079
  var PxLoopRepeatAt = {
968
1080
  /** Segment from the START; the repetition runs BEFORE the first keyframe
969
1081
  * (intro loops that play until the main timeline begins). */
@@ -1023,10 +1135,6 @@ var PixodeskAnimator = (() => {
1023
1135
  TRANSFORM_PART.scale,
1024
1136
  TRANSFORM_PART.origin
1025
1137
  ];
1026
- var PxGradientUnits = {
1027
- userSpaceOnUse: "userSpaceOnUse",
1028
- objectBoundingBox: "objectBoundingBox"
1029
- };
1030
1138
  var PxGradientSpreadMethod = {
1031
1139
  pad: "pad",
1032
1140
  reflect: "reflect",
@@ -1039,8 +1147,18 @@ var PixodeskAnimator = (() => {
1039
1147
  function getAnimatorConfig(doc) {
1040
1148
  var _a;
1041
1149
  const cfg = (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator);
1042
- return cfg ? flattenAnimatorTimeline(cfg) : void 0;
1043
- }
1150
+ if (!cfg) return void 0;
1151
+ const memoised = wireViewMemo.get(cfg);
1152
+ if (memoised) return memoised;
1153
+ const wire = cfg;
1154
+ const stray = PX_FLAT_RUNTIME_VIEW_KEYS.filter((k) => wire[k] !== void 0);
1155
+ const source = stray.length ? __spreadValues({}, wire) : cfg;
1156
+ for (const k of stray) delete source[k];
1157
+ const view = flattenAnimatorTimeline(source);
1158
+ wireViewMemo.set(cfg, view);
1159
+ return view;
1160
+ }
1161
+ var wireViewMemo = /* @__PURE__ */ new WeakMap();
1044
1162
  var flattenMemo = /* @__PURE__ */ new WeakMap();
1045
1163
  function flattenAnimatorTimeline(cfg) {
1046
1164
  const timeline = cfg.timeline;
@@ -1066,7 +1184,7 @@ var PixodeskAnimator = (() => {
1066
1184
  else if (pin && typeof pin === "object") {
1067
1185
  scroll.pin = true;
1068
1186
  if (pin.align !== void 0) scroll.pinAlign = pin.align;
1069
- if (pin.top !== void 0) scroll.pinTop = pin.top;
1187
+ if (pin.offset !== void 0) scroll.pinOffset = pin.offset;
1070
1188
  if (pin.distance !== void 0) scroll.pinDistance = pin.distance;
1071
1189
  }
1072
1190
  flat.scroll = scroll;
@@ -1108,14 +1226,13 @@ var PixodeskAnimator = (() => {
1108
1226
  // e.g. for colors
1109
1227
  px.number(),
1110
1228
  px.array(px.number()),
1111
- // ORDER LAW: the key-discriminated object shapes (`{path}`, `{paths}`) come BEFORE the
1229
+ // ORDER LAW: the key-discriminated object shape (`{pathData}`) comes BEFORE the
1112
1230
  // all-optional transform-parts record. In default (non-strict) mode that record accepts
1113
1231
  // ANY object (every key optional, unknown keys ignored), so listing it earlier made
1114
- // Union.sanitize route `{path}`/`{paths}` values into it and strip them to `{}` —
1232
+ // Union.sanitize route `{pathData}` values into it and strip them to `{}` —
1115
1233
  // silent morph-data loss (repro: the editor's keyframeValueSanitize spec). Validity is
1116
1234
  // order-independent (`some()`); only sanitize routing depends on this order.
1117
- px.object({ path: px.string() }),
1118
- px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] }),
1235
+ px.object({ pathData: px.string() }),
1119
1236
  // Gradient `stops` timeline — each kf value is the full stops-array snapshot.
1120
1237
  px.lazy(() => px.array(PxGradientStopSchema), []),
1121
1238
  px.lazy(() => PxTransformPartsSchema, {})
@@ -1155,7 +1272,7 @@ var PixodeskAnimator = (() => {
1155
1272
  keyframes: px.array(PxKeyframeSchema).optional(),
1156
1273
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
1157
1274
  autoOrient: px.boolean().optional(),
1158
- alongPathMode: px.enum(["sampled", "offsetPath"]).optional()
1275
+ alongPathMode: px.enum([PxAlongPathMode.sampled, PxAlongPathMode.offsetPath]).optional()
1159
1276
  }));
1160
1277
  var PxTransformPartsSchema = implementsInterface()(px.object({
1161
1278
  translate: px.tuple([px.number(), px.number()]).optional(),
@@ -1179,17 +1296,17 @@ var PixodeskAnimator = (() => {
1179
1296
  PxAnimationDefinitionSchema
1180
1297
  ]));
1181
1298
  var PxTriggerSchema = implementsInterface()(px.object({
1182
- startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
1183
- outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
1299
+ startOn: px.enum([PxStartOn.load, PxStartOn.mouseOver, PxStartOn.click, PxStartOn.scrollIntoView, PxStartOn.programmatic], PX_TRIGGER_DEFAULTS.startOn).optional(),
1300
+ outAction: px.enum([PxOutAction.continue, PxOutAction.pause, PxOutAction.reset, PxOutAction.reverse], PX_TRIGGER_DEFAULTS.outAction).optional(),
1184
1301
  // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
1185
1302
  // `fill`) or `'reset'` (snap back to the start state). Pairs with `outAction` ("what
1186
1303
  // happens when the trigger condition ends"); both end-of-life knobs now read alike.
1187
- finishAction: px.enum(["hold", "reset"]).optional(),
1304
+ finishAction: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
1188
1305
  scrollIntoViewThreshold: px.number().optional()
1189
1306
  }));
1190
1307
  var PxGlyphSchema = implementsInterface()(px.object({
1191
1308
  width: px.number(),
1192
- d: px.string()
1309
+ pathData: px.string()
1193
1310
  }));
1194
1311
  var PxGlyphFontSchema = implementsInterface()(px.object({
1195
1312
  fontFamily: px.string(),
@@ -1206,9 +1323,15 @@ var PixodeskAnimator = (() => {
1206
1323
  styles: px.record(px.record(px.union([px.string(), px.number()]))).optional(),
1207
1324
  fonts: px.record(PxGlyphFontSchema).optional()
1208
1325
  }));
1209
- var PX_SCROLL_PHASES = ["cover", "contain", "entry", "exit", "entry-crossing", "exit-crossing"];
1210
1326
  var PxScrollRangePointSchema = implementsInterface()(px.object({
1211
- phase: px.enum(PX_SCROLL_PHASES).optional(),
1327
+ phase: px.enum([
1328
+ PxScrollPhase.cover,
1329
+ PxScrollPhase.contain,
1330
+ PxScrollPhase.entry,
1331
+ PxScrollPhase.exit,
1332
+ PxScrollPhase.entryCrossing,
1333
+ PxScrollPhase.exitCrossing
1334
+ ]).optional(),
1212
1335
  fraction: px.number().optional()
1213
1336
  }));
1214
1337
  var PxScrollRangeSchema = px.object({
@@ -1216,21 +1339,21 @@ var PixodeskAnimator = (() => {
1216
1339
  end: PxScrollRangePointSchema.optional()
1217
1340
  });
1218
1341
  var PxScrollSchema = implementsInterface()(px.object({
1219
- kind: px.enum(["view", "scroll"]).optional(),
1220
- axis: px.enum(["block", "inline", "x", "y"]).optional(),
1221
- source: px.enum(["nearest", "root"]).optional(),
1342
+ kind: px.enum([PxScrollKind.view, PxScrollKind.scroll]).optional(),
1343
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
1344
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1222
1345
  // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.
1223
1346
  subject: px.string().optional(),
1224
1347
  smoothing: px.number().optional(),
1225
1348
  pin: px.boolean().optional(),
1226
- pinAlign: px.enum(["top", "center", "bottom"]).optional(),
1227
- pinTop: px.number().optional(),
1349
+ pinAlign: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
1350
+ pinOffset: px.number().optional(),
1228
1351
  pinDistance: px.number().optional(),
1229
1352
  range: PxScrollRangeSchema.optional()
1230
1353
  }));
1231
1354
  var PxTimelinePinSchema = implementsInterface()(px.object({
1232
- align: px.enum(["top", "center", "bottom"]).optional(),
1233
- top: px.number().optional(),
1355
+ align: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
1356
+ offset: px.number().optional(),
1234
1357
  distance: px.number().optional()
1235
1358
  }));
1236
1359
  var PxTimelineEngineSchema = px.enum([PxTimelineEngineExtra.auto, PxTimelineEngineExtra.native, PxTimelineEngineExtra.js]).optional();
@@ -1245,8 +1368,8 @@ var PixodeskAnimator = (() => {
1245
1368
  iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1246
1369
  // `fillMode` on the wire (CSS `animation-fill-mode`; the runtime view calls it `fill`)
1247
1370
  // — never `fill`, which is paint everywhere else in the format.
1248
- fillMode: px.enum(["forwards", "backwards", "both", "none"]).optional(),
1249
- direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional()
1371
+ fillMode: px.enum([PxFillMode.forwards, PxFillMode.backwards, PxFillMode.both, PxFillMode.none]).optional(),
1372
+ direction: px.enum([PxPlaybackDirection.normal, PxPlaybackDirection.reverse, PxPlaybackDirection.alternate, PxPlaybackDirection.alternateReverse]).optional()
1250
1373
  }));
1251
1374
  var scrollishTimelineShape = {
1252
1375
  // §2.8: duration is a property of the TIMELINE — under scrubbing it is the keyframe
@@ -1257,8 +1380,8 @@ var PixodeskAnimator = (() => {
1257
1380
  iterations: px.number().optional(),
1258
1381
  engine: PxTimelineEngineSchema,
1259
1382
  frameRate: px.number().optional(),
1260
- axis: px.enum(["block", "inline", "x", "y"]).optional(),
1261
- source: px.enum(["nearest", "root"]).optional(),
1383
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
1384
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1262
1385
  subject: px.string().optional(),
1263
1386
  // 'parent' | 'scroller' | any CSS selector
1264
1387
  smoothing: px.number().optional(),
@@ -1347,7 +1470,7 @@ var PixodeskAnimator = (() => {
1347
1470
  height: px.number().optional()
1348
1471
  }));
1349
1472
  var PxClipPathEffectSchema = implementsInterface()(px.object({
1350
- d: PxAnimatableStringSchema.optional()
1473
+ pathData: PxAnimatableStringSchema.optional()
1351
1474
  }));
1352
1475
  var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1353
1476
  offset: PxAnimatableNumberSchema.optional(),
@@ -1384,7 +1507,7 @@ var PixodeskAnimator = (() => {
1384
1507
  radius: PxAnimatableNumberSchema.optional(),
1385
1508
  focal: PxAnimatableVec2Schema.optional(),
1386
1509
  stops: PxAnimatableGradientStopsSchema.optional(),
1387
- gradientUnits: px.enum([PxGradientUnits.userSpaceOnUse, PxGradientUnits.objectBoundingBox]).optional(),
1510
+ gradientUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1388
1511
  spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
1389
1512
  gradientTransform: px.string().optional()
1390
1513
  }));
@@ -1430,10 +1553,13 @@ var PixodeskAnimator = (() => {
1430
1553
  // documented — because a wire key that is not in a schema is invisible to the
1431
1554
  // minifier's reserve list and gets renamed (MINIFICATION-BOUNDARY-PLAN.md §1.1).
1432
1555
  domType: px.string().optional(),
1556
+ // Text content of a `<text>` / `<tspan>`. Declared, so a non-string value is a schema error
1557
+ // and the minifier reserves the key; `text` is NOT an alias for it and is not read anywhere.
1558
+ textContent: px.string().optional(),
1433
1559
  id: px.string().optional(),
1434
1560
  meta: px.any().optional(),
1435
1561
  // Player-effects bucket emitted by the Editor's lightweight design format.
1436
- // Consumed and removed by `applyPlayerEffects` before any other normalisation
1562
+ // Consumed and removed by `applyPlayerEffects` before any other normalization
1437
1563
  // (see `createAnimatorImpl`), so downstream code never sees it.
1438
1564
  effects: PxEffectsSchema.optional(),
1439
1565
  // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
@@ -1465,7 +1591,7 @@ var PixodeskAnimator = (() => {
1465
1591
  c: px.boolean().optional()
1466
1592
  }));
1467
1593
 
1468
- // ../svg-animator-core/src/materialise/PxMotionPath.ts
1594
+ // ../svg-animator-core/src/materialize/PxMotionPath.ts
1469
1595
  function getKfTranslate(kf) {
1470
1596
  const v = kfValue(kf);
1471
1597
  if (!v) return void 0;
@@ -1519,7 +1645,7 @@ var PixodeskAnimator = (() => {
1519
1645
  var DEFAULT_FLATNESS_TOL = 0.5;
1520
1646
  var DEFAULT_ROTATION_TOL = 5;
1521
1647
  var DEFAULT_MAX_SAMPLES = 32;
1522
- function materialiseMotionPathInPropAnim(anim, opts) {
1648
+ function materializeMotionPathInPropAnim(anim, opts) {
1523
1649
  var _a, _b, _c;
1524
1650
  if (!propAnimIsMotionPath(anim)) return anim;
1525
1651
  const kfs = anim.keyframes;
@@ -1551,7 +1677,7 @@ var PixodeskAnimator = (() => {
1551
1677
  if (autoOrient && i > 0) {
1552
1678
  insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol);
1553
1679
  }
1554
- materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
1680
+ materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
1555
1681
  }
1556
1682
  const lastInE = getKfEasing(kfs[kfs.length - 1]);
1557
1683
  if (lastInE) out[out.length - 1].e = lastInE;
@@ -1665,7 +1791,7 @@ var PixodeskAnimator = (() => {
1665
1791
  out.push(makeOutKf(stepTime, dupValue));
1666
1792
  }
1667
1793
  var CORNER_STEP_AFTER_BOUNDARY_MS = 0.05;
1668
- function materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
1794
+ function materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
1669
1795
  const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
1670
1796
  const prevTime = getKfTime(prevKf);
1671
1797
  const nextTime = getKfTime(nextKf);
@@ -1881,8 +2007,8 @@ var PixodeskAnimator = (() => {
1881
2007
  return typeof value === "string" && extractPathData(value) !== void 0;
1882
2008
  }
1883
2009
  function normalizePathValue(value) {
1884
- if (value && typeof value === "object" && typeof value.path === "string") {
1885
- const d = extractPathData(value.path);
2010
+ if (value && typeof value === "object" && typeof value.pathData === "string") {
2011
+ const d = extractPathData(value.pathData);
1886
2012
  return d ? { paths: parseSvgPathToBezier(d) } : value;
1887
2013
  }
1888
2014
  if (value && typeof value === "object" && "paths" in value) {
@@ -1966,7 +2092,7 @@ var PixodeskAnimator = (() => {
1966
2092
  const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
1967
2093
  return { paths: interpolateBeziers(aPaths, bPaths, t) };
1968
2094
  }
1969
- if (COLOUR_ATTR_NAMES.has(propName)) {
2095
+ if (COLOR_ATTR_NAMES.has(propName)) {
1970
2096
  return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
1971
2097
  }
1972
2098
  if (propName === "transform" && typeof a === "object" && a !== null && !Array.isArray(a) && typeof b === "object" && b !== null && !Array.isArray(b)) {
@@ -2188,7 +2314,7 @@ var PixodeskAnimator = (() => {
2188
2314
  value = normalizePathValue(value);
2189
2315
  }
2190
2316
  const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2191
- if (COLOUR_ATTR_NAMES.has(propNameKebab)) {
2317
+ if (COLOR_ATTR_NAMES.has(propNameKebab)) {
2192
2318
  value = (_a = parseColor(value)) != null ? _a : value;
2193
2319
  }
2194
2320
  const normKf = {
@@ -2267,12 +2393,12 @@ var PixodeskAnimator = (() => {
2267
2393
  const out = { keyframes: normalizedKfs };
2268
2394
  if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2269
2395
  if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2270
- normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
2396
+ normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materializeMotionPathInPropAnim(out) : out;
2271
2397
  }
2272
2398
  }
2273
2399
  return normalized;
2274
2400
  }
2275
- function getNormalisedBindings(doc, engine = PxTimelineEngine.native) {
2401
+ function getNormalizedBindings(doc, engine = PxTimelineEngine.native) {
2276
2402
  const animatorConfig = getAnimatorConfig(doc) || {};
2277
2403
  const defs = getDefs(doc);
2278
2404
  const duration = animatorConfig.duration || 1e3;
@@ -2318,13 +2444,116 @@ var PixodeskAnimator = (() => {
2318
2444
  return bindings;
2319
2445
  }
2320
2446
 
2447
+ // ../svg-animator-core/src/playback/PxPlaybackTime.ts
2448
+ var PX_RATE_REJECTED = "setPlaybackRate: rate must be finite and non-zero";
2449
+ function isValidPlaybackRate(rate) {
2450
+ return Number.isFinite(rate) && rate !== 0;
2451
+ }
2452
+ function seekCeilingMs(durationMs, iterations) {
2453
+ if (!(durationMs > 0)) return 0;
2454
+ if (iterations === Infinity) return Infinity;
2455
+ return durationMs * (iterations > 0 ? iterations : 1);
2456
+ }
2457
+ function progressSpanMs(durationMs, iterations) {
2458
+ if (!(durationMs > 0)) return 0;
2459
+ if (iterations === Infinity) return durationMs;
2460
+ return durationMs * (iterations > 0 ? iterations : 1);
2461
+ }
2462
+ function clampSeekMs(timeMs, ceilingMs) {
2463
+ if (Number.isNaN(timeMs) || timeMs < 0) return 0;
2464
+ if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;
2465
+ return Number.isFinite(timeMs) ? timeMs : 0;
2466
+ }
2467
+ function timeToProgress(timeMs, durationMs, iterations) {
2468
+ const span = progressSpanMs(durationMs, iterations);
2469
+ if (!(span > 0) || !Number.isFinite(timeMs)) return 0;
2470
+ if (timeMs <= 0) return 0;
2471
+ if (iterations === Infinity) return timeMs % span / span;
2472
+ return timeMs >= span ? 1 : timeMs / span;
2473
+ }
2474
+ function progressToTimeMs(progress, durationMs, iterations) {
2475
+ const span = progressSpanMs(durationMs, iterations);
2476
+ if (!(span > 0) || !Number.isFinite(progress)) return 0;
2477
+ if (progress <= 0) return 0;
2478
+ return progress >= 1 ? span : progress * span;
2479
+ }
2480
+
2481
+ // ../svg-animator-core/src/playback/PxDiagnostics.ts
2482
+ var PxDiagnosticKind = {
2483
+ /** The document is wrong — regenerate or repair the file. */
2484
+ document: "document",
2485
+ /** The page or app cannot provide what the document asks for — fix the mount. */
2486
+ host: "host",
2487
+ /** The platform cannot do it and the player degraded — usually nothing to fix. */
2488
+ platform: "platform",
2489
+ /** The call is wrong or self-contradictory — fix the options or props you passed. */
2490
+ usage: "usage",
2491
+ /** The player failed where it did not expect to — report it to us. */
2492
+ internal: "internal"
2493
+ };
2494
+ function asError(error) {
2495
+ return typeof error === "string" ? new Error(error) : error;
2496
+ }
2497
+ function isSilenced(silent, kind) {
2498
+ if (!silent) return false;
2499
+ return silent === true || silent.includes(kind);
2500
+ }
2501
+ function createDiagnostics(config, prefix) {
2502
+ const tag = prefix ? prefix + " " : "";
2503
+ return {
2504
+ warn: (kind, message, detail) => {
2505
+ if (config == null ? void 0 : config.onWarn) {
2506
+ config.onWarn({ kind, message, detail });
2507
+ return;
2508
+ }
2509
+ if (isSilenced(config == null ? void 0 : config.silent, kind)) return;
2510
+ const line = tag + kind + ": " + message;
2511
+ if (detail === void 0) console.warn(line);
2512
+ else console.warn(line, detail);
2513
+ },
2514
+ error: (kind, error) => {
2515
+ const err = asError(error);
2516
+ if (config == null ? void 0 : config.onError) {
2517
+ config.onError({ kind, message: err.message, error: err });
2518
+ return;
2519
+ }
2520
+ if (isSilenced(config == null ? void 0 : config.silent, kind)) return;
2521
+ console.error(tag + kind + ": " + err.message);
2522
+ }
2523
+ };
2524
+ }
2525
+
2526
+ // src/shared/PxAnimatorCallbacks.ts
2527
+ function toEngineCallbacks(inline) {
2528
+ const { onPlay, onPause, onCancel, onFinish, onRemove, onStop, onWarn, onError, silent } = inline != null ? inline : {};
2529
+ const withStop = (own) => own || onStop ? () => {
2530
+ own == null ? void 0 : own();
2531
+ onStop == null ? void 0 : onStop();
2532
+ } : void 0;
2533
+ return {
2534
+ onPlay,
2535
+ onPause: withStop(onPause),
2536
+ onCancel: withStop(onCancel),
2537
+ onFinish: withStop(onFinish),
2538
+ onRemove: withStop(onRemove),
2539
+ onWarn,
2540
+ onError,
2541
+ silent
2542
+ };
2543
+ }
2544
+
2321
2545
  // src/triggers/PxAnimatorTriggers.ts
2322
- function setupAnimationTriggers(api, config) {
2323
- const { startOn, outAction = "continue", scrollIntoViewThreshold = 0 } = config;
2546
+ function setupAnimationTriggers(api, config, diag) {
2547
+ const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
2548
+ const cleanups = [];
2549
+ const dispose = () => {
2550
+ for (const undo of cleanups.splice(0)) undo();
2551
+ };
2552
+ const { startOn, outAction, scrollIntoViewThreshold } = resolveTrigger(config);
2324
2553
  const root = api.getRootElement();
2325
2554
  if (!root) {
2326
- console.warn("setupAnimationTriggers: No root element found for animation.");
2327
- return api;
2555
+ report.warn(PxDiagnosticKind.host, "setupAnimationTriggers: No root element found for animation.");
2556
+ return dispose;
2328
2557
  }
2329
2558
  let reversed = false;
2330
2559
  const start = () => {
@@ -2359,6 +2588,7 @@ var PixodeskAnimator = (() => {
2359
2588
  startHandler();
2360
2589
  } else {
2361
2590
  window.addEventListener("load", startHandler, { once: true });
2591
+ cleanups.push(() => window.removeEventListener("load", startHandler));
2362
2592
  }
2363
2593
  break;
2364
2594
  }
@@ -2373,6 +2603,10 @@ var PixodeskAnimator = (() => {
2373
2603
  };
2374
2604
  root.addEventListener("mouseenter", mouseOverHandler);
2375
2605
  root.addEventListener("mouseleave", mouseOutHandler);
2606
+ cleanups.push(() => {
2607
+ root.removeEventListener("mouseenter", mouseOverHandler);
2608
+ root.removeEventListener("mouseleave", mouseOutHandler);
2609
+ });
2376
2610
  break;
2377
2611
  }
2378
2612
  case "click": {
@@ -2384,6 +2618,7 @@ var PixodeskAnimator = (() => {
2384
2618
  }
2385
2619
  };
2386
2620
  root.addEventListener("click", clickHandler);
2621
+ cleanups.push(() => root.removeEventListener("click", clickHandler));
2387
2622
  break;
2388
2623
  }
2389
2624
  case "scrollIntoView": {
@@ -2415,12 +2650,13 @@ var PixodeskAnimator = (() => {
2415
2650
  { threshold: thresholdSteps }
2416
2651
  );
2417
2652
  observer.observe(root);
2653
+ cleanups.push(() => observer.disconnect());
2418
2654
  break;
2419
2655
  }
2420
2656
  case "programmatic":
2421
2657
  break;
2422
2658
  }
2423
- return api;
2659
+ return dispose;
2424
2660
  }
2425
2661
 
2426
2662
  // src/engines/PxAnimatorFrameLoop.ts
@@ -2438,7 +2674,7 @@ var PixodeskAnimator = (() => {
2438
2674
  };
2439
2675
  let cssValue;
2440
2676
  let cssKey = propName;
2441
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
2677
+ if (COLOR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
2442
2678
  cssValue = toRGBA(value);
2443
2679
  } else if (propName === "transform" && value !== null && typeof value === "object" && !Array.isArray(value)) {
2444
2680
  cssValue = composeTransformParts(value, { withUnits: true });
@@ -2525,18 +2761,19 @@ var PixodeskAnimator = (() => {
2525
2761
  return result;
2526
2762
  }
2527
2763
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs, scrollTimeline) {
2528
- var _a;
2764
+ var _a, _b;
2529
2765
  const config = getAnimatorConfig(doc) || {};
2766
+ const diag = createDiagnostics(callbacks, "[PxAnimator]");
2530
2767
  if (!rootElement) {
2531
2768
  if (doc.id) {
2532
2769
  const rootSelector = getSelector(doc.id);
2533
2770
  rootElement = document.querySelector(rootSelector);
2534
- if (!rootElement) console.warn("createFrameLoopAnimator: No root element found for selector: ", rootSelector);
2771
+ if (!rootElement) diag.warn(PxDiagnosticKind.host, "createWebApiAnimator: No root element found for selector: " + rootSelector);
2535
2772
  } else {
2536
- console.warn("createFrameLoopAnimator: No root element provided");
2773
+ diag.warn(PxDiagnosticKind.host, "createWebApiAnimator: No root element provided");
2537
2774
  }
2538
2775
  }
2539
- const bindings = getNormalisedBindings(doc, PxTimelineEngine.native);
2776
+ const bindings = getNormalizedBindings(doc, PxTimelineEngine.native);
2540
2777
  const animations = [];
2541
2778
  const _iterations = config.iterations;
2542
2779
  let iterations;
@@ -2546,18 +2783,18 @@ var PixodeskAnimator = (() => {
2546
2783
  let finishNotified = false;
2547
2784
  let removeCalled = false;
2548
2785
  if (!(bindings == null ? void 0 : bindings.length)) {
2549
- console.warn("createWebApiAnimator: No animation bindings defined");
2786
+ diag.warn(PxDiagnosticKind.document, "createWebApiAnimator: No animation bindings defined");
2550
2787
  }
2551
2788
  for (const binding of bindings || []) {
2552
2789
  const animDef = binding.animate;
2553
2790
  if (!animDef || typeof animDef !== "object" || Array.isArray(animDef)) {
2554
- console.warn("createWebApiAnimator: Empty or unresolved binding", binding);
2791
+ diag.warn(PxDiagnosticKind.document, "createWebApiAnimator: Empty or unresolved binding", binding);
2555
2792
  continue;
2556
2793
  }
2557
2794
  const selector = getSelector(binding.id);
2558
2795
  const elements = (rootElement == null ? void 0 : rootElement.querySelectorAll(selector)) || document.querySelectorAll(selector);
2559
2796
  if (elements.length === 0) {
2560
- console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
2797
+ diag.warn(PxDiagnosticKind.host, 'createWebApiAnimator: No elements found for selector "' + selector + '"');
2561
2798
  }
2562
2799
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
2563
2800
  const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
@@ -2606,14 +2843,14 @@ var PixodeskAnimator = (() => {
2606
2843
  }
2607
2844
  animations.push(anim);
2608
2845
  } catch (e) {
2609
- console.warn(e);
2846
+ diag.warn(PxDiagnosticKind.internal, "createWebApiAnimator: could not build the animation", e);
2610
2847
  }
2611
2848
  }
2612
2849
  }
2613
2850
  }
2614
2851
  }
2615
2852
  if (!forceEvenIfHasUnsupportedAttrs && unsupportedSet.size) {
2616
- console.warn("Unsupported CSS attrs: " + [...unsupportedSet].join(", "));
2853
+ diag.warn(PxDiagnosticKind.platform, "Unsupported CSS attrs: " + [...unsupportedSet].join(", "));
2617
2854
  return null;
2618
2855
  }
2619
2856
  const api = {
@@ -2657,20 +2894,36 @@ var PixodeskAnimator = (() => {
2657
2894
  }
2658
2895
  },
2659
2896
  "setPlaybackRate": (rate) => {
2897
+ if (!isValidPlaybackRate(rate)) {
2898
+ diag.warn(PxDiagnosticKind.usage, PX_RATE_REJECTED);
2899
+ return api;
2900
+ }
2660
2901
  animations.forEach((a) => a.playbackRate = rate);
2661
2902
  return api;
2662
2903
  },
2663
2904
  "getCurrentTime": () => {
2664
- var _a2, _b;
2665
- const res = (_b = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b : null;
2905
+ var _a2, _b2;
2906
+ const res = (_b2 = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b2 : null;
2666
2907
  return res !== null ? +res : null;
2667
2908
  },
2668
2909
  "setCurrentTime": (time) => {
2910
+ var _a2;
2911
+ const ceiling = seekCeilingMs((_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1);
2912
+ const seek = ceiling > 0 ? clampSeekMs(time, ceiling) : Math.max(0, time);
2669
2913
  finishNotified = false;
2670
2914
  animations.forEach((a) => {
2671
- a.currentTime = time;
2915
+ a.currentTime = seek;
2672
2916
  });
2673
2917
  },
2918
+ "getCurrentProgress": () => {
2919
+ var _a2;
2920
+ const t = api.getCurrentTime();
2921
+ return t === null ? null : timeToProgress(t, (_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1);
2922
+ },
2923
+ "setCurrentProgress": (progress) => {
2924
+ var _a2;
2925
+ api.setCurrentTime(progressToTimeMs(progress, (_a2 = config.duration) != null ? _a2 : 0, iterations != null ? iterations : 1));
2926
+ },
2674
2927
  "destroy": () => {
2675
2928
  var _a2;
2676
2929
  api.cancel();
@@ -2681,12 +2934,15 @@ var PixodeskAnimator = (() => {
2681
2934
  }
2682
2935
  }
2683
2936
  };
2684
- if (config.trigger) {
2685
- if (config.timelineSource === "scroll") {
2686
- console.warn("scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
2687
- } else {
2688
- setupAnimationTriggers(api, config.trigger);
2689
- }
2937
+ if (config.timelineSource === "scroll") {
2938
+ if (config.trigger) diag.warn(PxDiagnosticKind.usage, "scroll timeline: `animator.trigger` is ignored (triggers do not apply to scroll-driven playback)");
2939
+ } else {
2940
+ const detachTriggers = setupAnimationTriggers(api, (_b = config.trigger) != null ? _b : {}, diag);
2941
+ const destroyEngine = api.destroy.bind(api);
2942
+ api.destroy = () => {
2943
+ detachTriggers();
2944
+ destroyEngine();
2945
+ };
2690
2946
  }
2691
2947
  if (scrollTimeline) {
2692
2948
  animations.forEach((a) => a.play());
@@ -2714,22 +2970,22 @@ var PixodeskAnimator = (() => {
2714
2970
  }
2715
2971
  return res;
2716
2972
  }
2717
- function requireData(options) {
2718
- if (!(options == null ? void 0 : options.data)) throw new Error("createAnimator: `data` is required");
2719
- return options.data;
2973
+ function requireDoc(options) {
2974
+ if (!(options == null ? void 0 : options.doc)) throw new Error("createAnimator: `doc` is required");
2975
+ return options.doc;
2720
2976
  }
2721
2977
  function createPrerenderedWaapiAnimator(options) {
2722
- const doc = requireData(options);
2978
+ const doc = requireDoc(options);
2723
2979
  const animatorConfig = getAnimatorConfig(doc) || {};
2724
2980
  return finaliseAnimator(
2725
2981
  animatorConfig,
2726
- options.callbacks,
2982
+ toEngineCallbacks(options),
2727
2983
  (cb) => createWebApiAnimator(doc, cb, null, true)
2728
2984
  );
2729
2985
  }
2730
2986
 
2731
2987
  // src/shared/PxAnimatorKeys.ts
2732
- var PX_ANIMATOR_DATA_KEY = "data";
2988
+ var PX_ANIMATOR_DOC_KEY = "doc";
2733
2989
  return __toCommonJS(index_prerendered_waapi_exports);
2734
2990
  })();
2735
2991
  //# sourceMappingURL=index.prerendered-waapi.umd.js.map