@pixodesk/svg-animator-web 1.0.10 → 1.0.15

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.cjs CHANGED
@@ -7,6 +7,7 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __pow = Math.pow;
10
11
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
12
  var __spreadValues = (a, b) => {
12
13
  for (var prop in b || (b = {}))
@@ -50,17 +51,48 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
50
51
  var index_exports = {};
51
52
  __export(index_exports, {
52
53
  COLOUR_ATTR_NAMES: () => COLOUR_ATTR_NAMES,
54
+ PX_ANIMATOR_DATA_KEY: () => PX_ANIMATOR_DATA_KEY,
53
55
  PX_ANIM_ATTR_NAME: () => PX_ANIM_ATTR_NAME,
54
56
  PX_ANIM_SRC_ATTR_NAME: () => PX_ANIM_SRC_ATTR_NAME,
57
+ PX_TRANSFORM_PART_KEYS: () => PX_TRANSFORM_PART_KEYS,
58
+ PxAnimatedSvgDocumentSchema: () => PxAnimatedSvgDocumentSchema,
59
+ PxAnimationDefinitionSchema: () => PxAnimationDefinitionSchema,
60
+ PxAnimatorConfigSchema: () => PxAnimatorConfigSchema,
61
+ PxAttrValueSchema: () => PxAttrValueSchema,
62
+ PxBezierPathSchema: () => PxBezierPathSchema,
63
+ PxBindingSchema: () => PxBindingSchema,
64
+ PxDefsSchema: () => PxDefsSchema,
65
+ PxEasingOrRefSchema: () => PxEasingOrRefSchema,
66
+ PxEffectsSchema: () => PxEffectsSchema,
67
+ PxElementAnimationSchema: () => PxElementAnimationSchema,
68
+ PxKeyframeSchema: () => PxKeyframeSchema,
69
+ PxLoopSchema: () => PxLoopSchema,
70
+ PxMaskedByEffectSchema: () => PxMaskedByEffectSchema,
71
+ PxNodeBase: () => PxNodeBase,
72
+ PxNodeSchema: () => PxNodeSchema,
73
+ PxPropertyAnimationSchema: () => PxPropertyAnimationSchema,
74
+ PxRefEffectSchema: () => PxRefEffectSchema,
75
+ PxRepeaterEffectSchema: () => PxRepeaterEffectSchema,
76
+ PxRetimeEffectSchema: () => PxRetimeEffectSchema,
77
+ PxSvgNodeExtra: () => PxSvgNodeExtra,
78
+ PxTransformPartsSchema: () => PxTransformPartsSchema,
79
+ PxTransformValueSchema: () => PxTransformValueSchema,
80
+ PxTransformationEffectSchema: () => PxTransformationEffectSchema,
81
+ PxTriggerSchema: () => PxTriggerSchema,
82
+ PxTrimPathEffectSchema: () => PxTrimPathEffectSchema,
55
83
  STYLE_ATTR_NAMES: () => STYLE_ATTR_NAMES,
56
84
  TRANSFORM_FN_NAMES: () => TRANSFORM_FN_NAMES,
85
+ applyPlayerEffects: () => applyPlayerEffects,
57
86
  calcAnimationValues: () => calcAnimationValues,
58
87
  camelCaseToKebabWordIfNeeded: () => camelCaseToKebabWordIfNeeded,
88
+ collectSampleTimes: () => collectSampleTimes,
59
89
  createAnimator: () => createAnimator,
60
90
  createAnimatorImpl: () => createAnimatorImpl,
61
91
  createBasicFrameLoopAnimator: () => createBasicFrameLoopAnimator,
62
92
  createFrameLoopAnimator: () => createFrameLoopAnimator,
63
93
  createWebApiAnimator: () => createWebApiAnimator,
94
+ describeSchema: () => describeSchema,
95
+ diffInEffect: () => diffInEffect,
64
96
  generateNewIds: () => generateNewIds,
65
97
  getAnimatorConfig: () => getAnimatorConfig,
66
98
  getBindings: () => getBindings,
@@ -71,287 +103,1293 @@ __export(index_exports, {
71
103
  isPxElementFileFormatDeep: () => isPxElementFileFormatDeep,
72
104
  loadTagAnimators: () => loadTagAnimators,
73
105
  normalizeDocument: () => getNormalisedBindings,
106
+ px: () => px,
74
107
  renderNode: () => renderNode,
108
+ schemaKeys: () => schemaKeys,
75
109
  setupAnimationTriggers: () => setupAnimationTriggers,
76
- toRGBA: () => toRGBA
110
+ toRGBA: () => toRGBA,
111
+ validateNodeEffects: () => validateNodeEffects,
112
+ visualModelAt: () => visualModelAt
77
113
  });
78
114
  module.exports = __toCommonJS(index_exports);
79
115
 
80
- // src/PxAnimatorTypes.ts
81
- var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
82
- var PX_ANIM_ATTR_NAME = "_px_animator";
83
- var ANIMATE_ATTR = "animate";
84
- var TEXT_ATTR = "text";
85
- var TEXT_CONTENT_ATTR = "textContent";
86
- var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
87
- "type",
88
- "children",
89
- ANIMATE_ATTR,
90
- "animator",
91
- "meta",
92
- "defs",
93
- "bindings",
94
- TEXT_ATTR,
95
- TEXT_CONTENT_ATTR
96
- ]);
97
- function isPxElementFileFormat(fileJson) {
98
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
99
- return false;
116
+ // src/effects/transformParts.ts
117
+ function partsRecord(part, value, origin) {
118
+ const rec = {};
119
+ if (part === "translate") rec.translate = value;
120
+ else if (part === "rotate") rec.rotate = value;
121
+ else rec.scale = value;
122
+ if (origin && part !== "translate") rec.origin = origin;
123
+ return rec;
124
+ }
125
+ function readAnimatable(raw) {
126
+ if (raw === void 0) return { kind: "absent" };
127
+ if (Array.isArray(raw)) return { kind: "static", value: raw };
128
+ if (typeof raw === "object") {
129
+ const obj = raw;
130
+ if (obj.keyframes) {
131
+ return { kind: "animated", keyframes: obj.keyframes, autoOrient: obj.autoOrient };
132
+ }
133
+ if (obj.value !== void 0) return { kind: "static", value: obj.value };
134
+ }
135
+ return { kind: "static", value: raw };
136
+ }
137
+ function readStaticOrigin(raw, ctx) {
138
+ var _a;
139
+ const o = readAnimatable(raw);
140
+ if (o.kind === "absent") return void 0;
141
+ if (o.kind === "static") return o.value;
142
+ ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
143
+ return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
144
+ }
145
+ function keyframeWith(kf, value) {
146
+ const out = { value };
147
+ if (kf.time !== void 0) out.time = kf.time;
148
+ if (kf.easing !== void 0) out.easing = kf.easing;
149
+ if (kf.tangentOut !== void 0) out.tangentOut = kf.tangentOut;
150
+ if (kf.tangentIn !== void 0) out.tangentIn = kf.tangentIn;
151
+ return out;
152
+ }
153
+
154
+ // src/effects/transformationEffect.ts
155
+ function applyTransformationEffect(node, fx, ctx) {
156
+ if (!fx) return node;
157
+ delete node.transform;
158
+ let n = node;
159
+ n = wrapTransformPart(n, "skew", fx.skew, ctx);
160
+ n = wrapOrigin(
161
+ n,
162
+ fx.origin,
163
+ /*invert=*/
164
+ true
165
+ );
166
+ n = wrapTransformPart(n, "scale", normalizeScale(fx.scale), ctx);
167
+ n = wrapTransformPart(n, "rotate", fx.rotate, ctx);
168
+ n = wrapOrigin(
169
+ n,
170
+ fx.origin,
171
+ /*invert=*/
172
+ false
173
+ );
174
+ if (translateHasAutoOrient(fx.translate)) {
175
+ n = wrapOrigin(
176
+ n,
177
+ fx.origin,
178
+ /*invert=*/
179
+ true
180
+ );
181
+ n = wrapTransformPart(n, "translate", fx.translate, ctx);
182
+ n = wrapOrigin(
183
+ n,
184
+ fx.origin,
185
+ /*invert=*/
186
+ false
187
+ );
188
+ } else {
189
+ n = wrapTransformPart(n, "translate", fx.translate, ctx);
190
+ }
191
+ return n;
192
+ }
193
+ function translateHasAutoOrient(translate) {
194
+ if (!translate || typeof translate !== "object") return false;
195
+ const obj = translate;
196
+ if (obj.autoOrient) return true;
197
+ return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
198
+ }
199
+ function normalizeScale(raw) {
200
+ if (raw === void 0) return void 0;
201
+ if (Array.isArray(raw)) return [raw[0] / 100, raw[1] / 100];
202
+ return raw;
203
+ }
204
+ function wrapTransformPart(inner, part, raw, ctx) {
205
+ if (raw === void 0) return inner;
206
+ if (part === "skew") {
207
+ const skew = readAnimatable(raw);
208
+ if (skew.kind !== "static") {
209
+ ctx.warnings.push("transformation.skew: only static skew is supported");
210
+ return inner;
211
+ }
212
+ return { type: "g", transform: "skewX(" + skew.value[0] + ")skewY(" + skew.value[1] + ")", children: [inner] };
213
+ }
214
+ const v = readAnimatable(raw);
215
+ if (v.kind === "static") {
216
+ return { type: "g", transform: { value: partsRecord(part, v.value, void 0) }, children: [inner] };
217
+ }
218
+ if (v.kind === "animated") {
219
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
220
+ if (v.autoOrient) animTr.autoOrient = true;
221
+ return {
222
+ type: "g",
223
+ animate: { transform: animTr },
224
+ children: [inner]
225
+ };
100
226
  }
101
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
227
+ return inner;
102
228
  }
103
- function isObject(v) {
104
- return v && typeof v === "object" && !Array.isArray(v);
229
+ function wrapOrigin(inner, raw, invert) {
230
+ if (raw === void 0) return inner;
231
+ const v = readAnimatable(raw);
232
+ const sign = (value) => invert ? [-value[0], -value[1]] : value;
233
+ if (v.kind === "absent") return inner;
234
+ if (v.kind === "static") {
235
+ if (v.value[0] === 0 && v.value[1] === 0) return inner;
236
+ return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
237
+ }
238
+ if (v.kind === "animated") {
239
+ return {
240
+ type: "g",
241
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) } },
242
+ children: [inner]
243
+ };
244
+ }
245
+ return inner;
105
246
  }
106
- function validateFillMode(v, path, errors) {
107
- if (v === "forwards" || v === "backwards" || v === "both" || v === "none") return true;
108
- errors.push(path + ': invalid FillMode "' + v + `", expected 'forwards'|'backwards'|'both'|'none'`);
109
- return false;
247
+
248
+ // src/effects/contentRefSplit.ts
249
+ function identifyContentRefTargets(node, ctx, allocator) {
250
+ var _a, _b, _c;
251
+ if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.ref) == null ? void 0 : _b.type) === "content") {
252
+ const baseId = node.effects.ref.baseId;
253
+ if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
254
+ ctx.contentRefInnerIds.set(baseId, allocator(baseId));
255
+ }
256
+ }
257
+ (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
258
+ }
259
+ function splitForContentRef(node, transformation, originalId, innerId, ctx) {
260
+ const outerBody = liftBodyTranslate(node, transformation);
261
+ if (typeof node.id === "string") delete node.id;
262
+ const { outer: outerTr, inner: innerTr } = splitTransformationEffect(transformation);
263
+ let innerNode = node;
264
+ innerNode = applyTransformationEffect(innerNode, innerTr, ctx);
265
+ const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
266
+ let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
267
+ if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
268
+ if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
269
+ if (outerTr) {
270
+ delete outerWrapper.id;
271
+ outerWrapper = applyTransformationEffect(outerWrapper, outerTr, ctx);
272
+ outerWrapper.id = originalId;
273
+ }
274
+ return outerWrapper;
275
+ }
276
+ function liftBodyTranslate(node, transformation) {
277
+ var _a;
278
+ const out = {};
279
+ let didLiftAnimate = false;
280
+ const animTr = (_a = node.animate) == null ? void 0 : _a.transform;
281
+ if (animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes)) {
282
+ const kfs = animTr.keyframes;
283
+ const hasTranslate = kfs.some((kf) => kf.value && kf.value.translate);
284
+ if (hasTranslate) {
285
+ const outerHasOrigin = needsOriginOnOuter(animTr);
286
+ const outerKfs = kfs.map((kf) => {
287
+ const v = kf.value || {};
288
+ const newValue = {};
289
+ if (v.translate !== void 0) newValue.translate = v.translate;
290
+ if (outerHasOrigin && v.origin !== void 0) newValue.origin = v.origin;
291
+ const outerKf = { value: newValue };
292
+ if (kf.time !== void 0) outerKf.time = kf.time;
293
+ if (kf.easing !== void 0) outerKf.easing = kf.easing;
294
+ if (kf.tangentOut !== void 0) outerKf.tangentOut = kf.tangentOut;
295
+ if (kf.tangentIn !== void 0) outerKf.tangentIn = kf.tangentIn;
296
+ return outerKf;
297
+ });
298
+ const outerAnimTr = { keyframes: outerKfs };
299
+ if (animTr.autoOrient) outerAnimTr.autoOrient = true;
300
+ out.animate = { transform: outerAnimTr };
301
+ const innerHasPivotedPart = kfs.some((kf) => {
302
+ const v = kf.value || {};
303
+ return v.rotate !== void 0 || v.scale !== void 0;
304
+ });
305
+ const innerKfs = kfs.map((kf) => {
306
+ const v = kf.value || {};
307
+ const newValue = {};
308
+ if (v.rotate !== void 0) newValue.rotate = v.rotate;
309
+ if (v.scale !== void 0) newValue.scale = v.scale;
310
+ if (v.origin !== void 0 && (!outerHasOrigin || innerHasPivotedPart)) newValue.origin = v.origin;
311
+ const innerKf = { value: newValue };
312
+ if (kf.time !== void 0) innerKf.time = kf.time;
313
+ if (kf.easing !== void 0) innerKf.easing = kf.easing;
314
+ return innerKf;
315
+ });
316
+ const allInnerEmpty = innerKfs.every((kf) => Object.keys(kf.value).length === 0);
317
+ if (allInnerEmpty) {
318
+ delete node.animate.transform;
319
+ if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
320
+ } else {
321
+ node.animate.transform = { keyframes: innerKfs };
322
+ }
323
+ didLiftAnimate = true;
324
+ }
325
+ }
326
+ const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
327
+ const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
328
+ if (typeof node.transform === "string") {
329
+ const split = splitTransformString(node.transform);
330
+ if (stripBodyTranslateOnly) {
331
+ if (split.translate !== void 0) {
332
+ if (split.rest) node.transform = split.rest;
333
+ else delete node.transform;
334
+ } else if (isPureTranslateBody(node.transform)) {
335
+ delete node.transform;
336
+ }
337
+ } else if (split.translate) {
338
+ out.transform = split.translate;
339
+ if (split.rest) node.transform = split.rest;
340
+ else delete node.transform;
341
+ }
342
+ }
343
+ return out;
344
+ }
345
+ function isPureTranslateBody(s) {
346
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
347
+ const ops = [];
348
+ let m;
349
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
350
+ if (ops.length !== 1) return false;
351
+ if (ops[0].name === "translate") return true;
352
+ if (ops[0].name !== "matrix") return false;
353
+ const args = /matrix\(([^)]*)\)/.exec(ops[0].full);
354
+ if (!args) return false;
355
+ const nums = args[1].split(/[\s,]+/).filter(Boolean).map(Number);
356
+ return nums.length >= 4 && nums[0] === 1 && nums[1] === 0 && nums[2] === 0 && nums[3] === 1;
357
+ }
358
+ function splitTransformString(s) {
359
+ const ops = [];
360
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
361
+ let m;
362
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
363
+ if (!ops.length) return { rest: s || void 0 };
364
+ if (ops.every((o) => o.name === "translate")) {
365
+ return { translate: ops.map((o) => o.full).join("") };
366
+ }
367
+ const leading = ops[0];
368
+ const trailing = ops[ops.length - 1];
369
+ if (trailing.name === "translate" && leading.name === "translate") {
370
+ const trailingVec = parseTranslateArgs(trailing.full);
371
+ const leadingVec = parseTranslateArgs(leading.full);
372
+ const ox = -trailingVec[0];
373
+ const oy = -trailingVec[1];
374
+ const userTx = leadingVec[0] - ox;
375
+ const userTy = leadingVec[1] - oy;
376
+ if (userTx === 0 && userTy === 0) return { rest: s };
377
+ const middleAndTrailing = "translate(" + ox + "," + oy + ")" + ops.slice(1).map((o) => o.full).join("");
378
+ return { translate: "translate(" + userTx + "," + userTy + ")", rest: middleAndTrailing };
379
+ }
380
+ if (trailing.name === "translate") return { rest: s };
381
+ const lifted = [];
382
+ let i = 0;
383
+ while (i < ops.length && ops[i].name === "translate") {
384
+ lifted.push(ops[i].full);
385
+ i++;
386
+ }
387
+ if (!lifted.length) return { rest: s };
388
+ const rest = ops.slice(i).map((o) => o.full).join("");
389
+ return {
390
+ translate: lifted.join(""),
391
+ rest: rest || void 0
392
+ };
110
393
  }
111
- function validatePlaybackDirection(v, path, errors) {
112
- if (v === "normal" || v === "reverse" || v === "alternate" || v === "alternate-reverse") return true;
113
- errors.push(path + ': invalid PlaybackDirection "' + v + `", expected 'normal'|'reverse'|'alternate'|'alternate-reverse'`);
114
- return false;
394
+ function parseTranslateArgs(translateOp) {
395
+ const m = /translate\(([^)]*)\)/.exec(translateOp);
396
+ if (!m) return [0, 0];
397
+ const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
398
+ return [nums[0] || 0, nums[1] || 0];
399
+ }
400
+ function splitTransformationEffect(fx) {
401
+ if (!fx) return {};
402
+ const originOnOuter = needsOriginOnOuter(fx.translate);
403
+ const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
404
+ const outer = {};
405
+ const inner = {};
406
+ if (fx.translate !== void 0) outer.translate = fx.translate;
407
+ if (originOnOuter && fx.origin !== void 0) outer.origin = fx.origin;
408
+ if (fx.rotate !== void 0) inner.rotate = fx.rotate;
409
+ if (fx.scale !== void 0) inner.scale = fx.scale;
410
+ if (fx.skew !== void 0) inner.skew = fx.skew;
411
+ if (fx.origin !== void 0 && (!originOnOuter || innerHasPivotedPart)) inner.origin = fx.origin;
412
+ return {
413
+ outer: Object.keys(outer).length ? outer : void 0,
414
+ inner: Object.keys(inner).length ? inner : void 0
415
+ };
115
416
  }
116
- function validatePxEasingOrRef(v, path, errors) {
117
- if (typeof v === "string") return true;
118
- if (Array.isArray(v) && v.length === 4 && v.every((n) => typeof n === "number")) return true;
119
- errors.push(path + ": invalid easing, expected string or [number, number, number, number]");
417
+ function needsOriginOnOuter(translateAnim) {
418
+ if (!translateAnim || typeof translateAnim !== "object") return false;
419
+ const obj = translateAnim;
420
+ if (obj.autoOrient) return true;
421
+ if (Array.isArray(obj.keyframes)) {
422
+ return obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
423
+ }
120
424
  return false;
121
425
  }
122
- function validatePxKeyframe(v, path, errors) {
123
- if (!isObject(v)) {
124
- errors.push(path + ": expected object");
125
- return false;
126
- }
127
- let valid = true;
128
- if (v.time !== void 0 && typeof v.time !== "number") {
129
- errors.push(path + ".time: expected number, got " + typeof v.time);
130
- valid = false;
131
- }
132
- if (v.t !== void 0 && typeof v.t !== "number") {
133
- errors.push(path + ".t: expected number, got " + typeof v.t);
134
- valid = false;
135
- }
136
- if (v.easing !== void 0 && !validatePxEasingOrRef(v.easing, path + ".easing", errors)) valid = false;
137
- if (v.e !== void 0 && !validatePxEasingOrRef(v.e, path + ".e", errors)) valid = false;
138
- return valid;
426
+
427
+ // src/effects/util.ts
428
+ function genId(ctx, prefix) {
429
+ return "_lw_" + prefix + "_" + ctx.nextId++;
139
430
  }
140
- function validatePxPropertyAnimation(v, path, errors) {
141
- if (!isObject(v)) {
142
- errors.push(path + ": expected object");
143
- return false;
144
- }
145
- let valid = true;
146
- if (v.keyframes !== void 0) {
147
- if (!Array.isArray(v.keyframes)) {
148
- errors.push(path + ".keyframes: expected array");
149
- valid = false;
150
- } else {
151
- v.keyframes.forEach((kf, i) => {
152
- if (!validatePxKeyframe(kf, path + ".keyframes[" + i + "]", errors)) valid = false;
153
- });
431
+ function indexById(node, map) {
432
+ var _a;
433
+ if (typeof node.id === "string") map.set(node.id, node);
434
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
435
+ }
436
+ function spliceDefs(root, defs) {
437
+ if (!defs.length) return;
438
+ const existing = root.children || (root.children = []);
439
+ existing.unshift({ type: "defs", children: defs });
440
+ }
441
+ function clone(value) {
442
+ if (value === null || typeof value !== "object") return value;
443
+ if (Array.isArray(value)) return value.map(clone);
444
+ const out = {};
445
+ for (const k of Object.keys(value)) out[k] = clone(value[k]);
446
+ return out;
447
+ }
448
+ function regenerateIdsInClone(root, ctx) {
449
+ const oldToNew = /* @__PURE__ */ new Map();
450
+ const walkAssign = (n) => {
451
+ var _a;
452
+ if (typeof n.id === "string") {
453
+ const newId = genId(ctx, "retimed");
454
+ oldToNew.set(n.id, newId);
455
+ n.id = newId;
456
+ }
457
+ (_a = n.children) == null ? void 0 : _a.forEach(walkAssign);
458
+ };
459
+ walkAssign(root);
460
+ const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
461
+ const newId = oldToNew.get(oldId);
462
+ return newId ? "url(#" + newId + ")" : m;
463
+ });
464
+ const walkRewrite = (n) => {
465
+ var _a;
466
+ if (typeof n.href === "string" && n.href.startsWith("#")) {
467
+ const newId = oldToNew.get(n.href.slice(1));
468
+ if (newId) n.href = "#" + newId;
469
+ }
470
+ for (const k of Object.keys(n)) {
471
+ if (k === "children" || k === "effects" || k === "meta" || k === "href" || k === "id") continue;
472
+ const v = n[k];
473
+ if (typeof v === "string" && v.indexOf("url(#") !== -1) n[k] = rewriteUrl(v);
154
474
  }
475
+ (_a = n.children) == null ? void 0 : _a.forEach(walkRewrite);
476
+ };
477
+ walkRewrite(root);
478
+ return oldToNew;
479
+ }
480
+
481
+ // src/effects/maskedByEffect.ts
482
+ function applyMaskedByEffect(node, fx, transformation, ctx) {
483
+ if (!fx) return node;
484
+ if (!fx.href) {
485
+ ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
486
+ return node;
487
+ }
488
+ const maskId = genId(ctx, "mask");
489
+ let content = { type: "use", href: "#" + fx.href };
490
+ content = wrapInverseTransform(content, transformation, ctx);
491
+ const mask = { type: "mask", id: maskId, children: [content] };
492
+ if (fx.maskType) mask.maskType = fx.maskType;
493
+ if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
494
+ if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
495
+ ctx.defs.push(mask);
496
+ node.mask = "url(#" + maskId + ")";
497
+ return node;
498
+ }
499
+ function wrapInverseTransform(inner, fx, ctx) {
500
+ if (!fx) return inner;
501
+ const origin = readStaticOrigin(fx.origin, ctx);
502
+ let n = inner;
503
+ n = wrapInversePart(n, "translate", fx.translate, void 0, ctx);
504
+ n = wrapInversePart(n, "rotate", fx.rotate, origin, ctx);
505
+ n = wrapInversePart(n, "scale", fx.scale, origin, ctx);
506
+ return n;
507
+ }
508
+ function wrapInversePart(inner, part, raw, origin, ctx) {
509
+ if (raw === void 0) return inner;
510
+ const v = readAnimatable(raw);
511
+ if (v.kind === "static") {
512
+ return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
513
+ }
514
+ if (v.kind === "animated") {
515
+ return {
516
+ type: "g",
517
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
518
+ children: [inner]
519
+ };
155
520
  }
156
- if (v.kfs !== void 0) {
157
- if (!Array.isArray(v.kfs)) {
158
- errors.push(path + ".kfs: expected array");
159
- valid = false;
521
+ return inner;
522
+ }
523
+ function invertPartValue(part, value) {
524
+ if (part === "translate") return [-value[0], -value[1]];
525
+ if (part === "rotate") return -value;
526
+ return [1 / value[0], 1 / value[1]];
527
+ }
528
+
529
+ // src/effects/refEffect.ts
530
+ var CONTENT_SUBREF = "content";
531
+ function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
532
+ if (ref) {
533
+ const baseId = ref.baseId;
534
+ if (!baseId) {
535
+ ctx.errors.push("ref: missing baseId");
160
536
  } else {
161
- v.kfs.forEach((kf, i) => {
162
- if (!validatePxKeyframe(kf, path + ".kfs[" + i + "]", errors)) valid = false;
163
- });
537
+ const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
538
+ node.href = "#" + targetId;
164
539
  }
165
540
  }
166
- return valid;
541
+ return applyTransformationEffect(node, transformation, ctx);
167
542
  }
168
- function validatePxAnimationDefinition(v, path, errors) {
169
- if (!isObject(v)) {
170
- errors.push(path + ": expected object");
171
- return false;
543
+
544
+ // src/effects/repeaterEffect.ts
545
+ function applyRepeaterEffect(node, fx, ctx) {
546
+ var _a, _b;
547
+ if (!fx) return node;
548
+ const copies = (_a = fx.copies) != null ? _a : 1;
549
+ if (copies < 1) {
550
+ ctx.errors.push("repeater.copies invalid: " + fx.copies);
551
+ return node;
552
+ }
553
+ const sharedTransform = node.transform;
554
+ const sharedAnimTransform = (_b = node.animate) == null ? void 0 : _b.transform;
555
+ const base = clone(node);
556
+ delete base.transform;
557
+ if (base.animate) {
558
+ delete base.animate.transform;
559
+ if (Object.keys(base.animate).length === 0) delete base.animate;
560
+ }
561
+ const children = [base];
562
+ for (let i = 1; i < copies; i++) {
563
+ const copy = clone(base);
564
+ copy.transform = { value: perCopyParts(fx, i) };
565
+ children.push(copy);
566
+ }
567
+ const wrapper = { type: "g", children };
568
+ if (sharedTransform !== void 0) wrapper.transform = sharedTransform;
569
+ if (sharedAnimTransform !== void 0) wrapper.animate = { transform: sharedAnimTransform };
570
+ return wrapper;
571
+ }
572
+ function perCopyParts(fx, i) {
573
+ const parts = {};
574
+ if (fx.translate) parts.translate = [fx.translate[0] * i, fx.translate[1] * i];
575
+ if (fx.rotate !== void 0) parts.rotate = fx.rotate * i;
576
+ if (fx.scale) parts.scale = [__pow(fx.scale[0] / 100, i), __pow(fx.scale[1] / 100, i)];
577
+ if (fx.origin) parts.origin = [fx.origin[0] * i, fx.origin[1] * i];
578
+ return parts;
579
+ }
580
+
581
+ // src/effects/retimeEffect.ts
582
+ var RETIME_AS_SYMBOL = true;
583
+ function applyRetimeEffect(node, retime, ctx) {
584
+ var _a, _b, _c, _d;
585
+ if (!retime) return node;
586
+ const baseId = retime.baseId;
587
+ if (!baseId) {
588
+ ctx.errors.push("retime: missing baseId");
589
+ return node;
590
+ }
591
+ const target = ctx.idMap.get(baseId);
592
+ if (!target) {
593
+ ctx.warnings.push('retime: target "' + baseId + '" not found');
594
+ return node;
595
+ }
596
+ const start = (_a = retime.start) != null ? _a : 0;
597
+ const stretch = (_b = retime.stretch) != null ? _b : 1;
598
+ if (RETIME_AS_SYMBOL) {
599
+ const symbolClone = clone(target);
600
+ regenerateIdsInClone(symbolClone, ctx);
601
+ const cloneId = genId(ctx, "retime");
602
+ symbolClone.id = cloneId;
603
+ remapKeyframeTimes(symbolClone, start, stretch);
604
+ ctx.defs.push(symbolClone);
605
+ node.href = "#" + cloneId;
606
+ return node;
607
+ }
608
+ const sourceNodes = target.type === "symbol" ? target.children || [] : [target];
609
+ const content = sourceNodes.map((child) => {
610
+ const c = clone(child);
611
+ regenerateIdsInClone(c, ctx);
612
+ remapKeyframeTimes(c, start, stretch);
613
+ return c;
614
+ });
615
+ const g = __spreadProps(__spreadValues({}, node), { type: "g", children: content });
616
+ delete g.href;
617
+ const x = Number((_c = node.x) != null ? _c : 0), y = Number((_d = node.y) != null ? _d : 0);
618
+ if (x || y) {
619
+ const offset = "translate(" + x + "," + y + ")";
620
+ g.transform = typeof g.transform === "string" ? offset + g.transform : offset;
621
+ delete g.x;
622
+ delete g.y;
623
+ }
624
+ return g;
625
+ }
626
+ function remapKeyframeTimes(node, start, stretch) {
627
+ var _a;
628
+ const remap2 = (kfs) => {
629
+ for (const kf of kfs) if (typeof kf.time === "number") kf.time = start + kf.time * stretch;
630
+ };
631
+ if (node.transform && typeof node.transform === "object" && Array.isArray(node.transform.keyframes)) {
632
+ remap2(node.transform.keyframes);
172
633
  }
173
- let valid = true;
174
- for (const key of Object.keys(v)) {
175
- if (!validatePxPropertyAnimation(v[key], path + "." + key, errors)) valid = false;
634
+ if (node.animate && typeof node.animate === "object") {
635
+ for (const prop of Object.keys(node.animate)) {
636
+ const anim = node.animate[prop];
637
+ if (anim && Array.isArray(anim.keyframes)) remap2(anim.keyframes);
638
+ }
176
639
  }
177
- return valid;
640
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => remapKeyframeTimes(child, start, stretch));
178
641
  }
179
- function validatePxElementAnimation(v, path, errors) {
180
- if (typeof v === "string") return true;
181
- if (Array.isArray(v)) {
182
- let valid = true;
183
- v.forEach((item, i) => {
184
- if (typeof item !== "string" && !validatePxAnimationDefinition(item, path + "[" + i + "]", errors)) {
185
- valid = false;
186
- }
187
- });
188
- return valid;
642
+
643
+ // src/effects/trimPathEffect.ts
644
+ function applyTrimPathEffect(node, trimPath, isCombinedShape) {
645
+ var _a;
646
+ if (!trimPath && !isCombinedShape) return node;
647
+ const d = (_a = node.d) != null ? _a : rectToPathD(node);
648
+ if (d === void 0) return node;
649
+ const g = __spreadProps(__spreadValues({}, node), { type: "g" });
650
+ delete g.d;
651
+ const meta = g.meta = __spreadValues({}, g.meta || {});
652
+ const effects = meta.effects = __spreadValues({}, meta.effects || {});
653
+ if (trimPath) effects.trimPath = trimPath;
654
+ effects.isCombinedShape = true;
655
+ g.children = [{ type: "path", d }];
656
+ return g;
657
+ }
658
+ function rectToPathD(node) {
659
+ var _a, _b, _c, _d;
660
+ if (node.type !== "rect") return void 0;
661
+ const x = Number((_a = node.x) != null ? _a : 0), y = Number((_b = node.y) != null ? _b : 0);
662
+ const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
663
+ return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
664
+ }
665
+
666
+ // src/effects/PlayerEffectsUtil.ts
667
+ function applyPlayerEffects(root) {
668
+ const ctx = {
669
+ defs: [],
670
+ warnings: [],
671
+ errors: [],
672
+ idMap: /* @__PURE__ */ new Map(),
673
+ nextId: 0,
674
+ contentRefInnerIds: /* @__PURE__ */ new Map()
675
+ };
676
+ const working = clone(root);
677
+ indexById(working, ctx.idMap);
678
+ identifyContentRefTargets(working, ctx, () => genId(ctx, "inner"));
679
+ const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);
680
+ const out = applyPlayerEffects_retime(afterPass1, ctx);
681
+ spliceDefs(out, ctx.defs);
682
+ return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
683
+ }
684
+ function applyPlayerEffects_exceptRetime(node, ctx) {
685
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
686
+ const fx = node.effects;
687
+ const originalId = typeof node.id === "string" ? node.id : void 0;
688
+ const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
689
+ if (!fx && !innerIdForContentRef) return node;
690
+ const { transformation, repeater, maskedBy, trimPath, retime, ref } = fx != null ? fx : {};
691
+ const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
692
+ if (fx) delete node.effects;
693
+ let n = node;
694
+ n = applyTrimPathEffect(n, trimPath, isCombinedShape);
695
+ n = applyRepeaterEffect(n, repeater, ctx);
696
+ n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
697
+ if (innerIdForContentRef) {
698
+ n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
699
+ } else {
700
+ n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
189
701
  }
190
- if (isObject(v)) return validatePxAnimationDefinition(v, path, errors);
191
- errors.push(path + ": expected string, array, or PxAnimationDefinition object");
192
- return false;
702
+ if (retime) node.effects = { retime };
703
+ if (originalId) ctx.idMap.set(originalId, n);
704
+ return n;
193
705
  }
194
- function validatePxTrigger(v, path, errors) {
195
- if (!isObject(v)) {
196
- errors.push(path + ": expected object");
197
- return false;
706
+ function applyPlayerEffects_retime(node, ctx) {
707
+ var _a;
708
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_retime(child, ctx));
709
+ const retime = (_a = node.effects) == null ? void 0 : _a.retime;
710
+ if (!retime) return node;
711
+ delete node.effects;
712
+ return applyRetimeEffect(node, retime, ctx);
713
+ }
714
+
715
+ // src/PxSchema.ts
716
+ function pathStr(path) {
717
+ if (!path.length) return ".";
718
+ let result = "";
719
+ for (const seg of path) {
720
+ if (seg.startsWith("[")) result += seg;
721
+ else result += (result ? "." : "") + seg;
722
+ }
723
+ return result;
724
+ }
725
+ var Base = class {
726
+ _canSanitize(raw) {
727
+ return this.isValid(raw);
198
728
  }
199
- let valid = true;
200
- const validStartOn = ["load", "mouseOver", "click", "scrollIntoView", "programmatic"];
201
- if (!validStartOn.includes(v.startOn)) {
202
- errors.push(path + '.startOn: invalid value "' + v.startOn + '", expected ' + validStartOn.join("|"));
203
- valid = false;
729
+ optional() {
730
+ return new Optional(this);
204
731
  }
205
- if (v.outAction !== void 0) {
206
- const validOutAction = ["continue", "pause", "reset", "reverse"];
207
- if (!validOutAction.includes(v.outAction)) {
208
- errors.push(path + '.outAction: invalid value "' + v.outAction + '", expected ' + validOutAction.join("|"));
209
- valid = false;
210
- }
732
+ };
733
+ var Optional = class extends Base {
734
+ constructor(inner) {
735
+ super();
736
+ this.inner = inner;
737
+ this._default = void 0;
211
738
  }
212
- if (v.scrollIntoViewThreshold !== void 0 && typeof v.scrollIntoViewThreshold !== "number") {
213
- errors.push(path + ".scrollIntoViewThreshold: expected number, got " + typeof v.scrollIntoViewThreshold);
214
- valid = false;
739
+ sanitize(raw) {
740
+ if (raw === void 0 || raw === null) return void 0;
741
+ return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
215
742
  }
216
- return valid;
217
- }
218
- function validatePxAnimatorConfig(v, path, errors) {
219
- if (!isObject(v)) {
220
- errors.push(path + ": expected object");
743
+ isValid(raw, ctx, path) {
744
+ if (raw === void 0 || raw === null) return true;
745
+ return this.inner.isValid(raw, ctx, path);
746
+ }
747
+ _canSanitize(raw) {
748
+ return raw === void 0 || raw === null || this.inner._canSanitize(raw);
749
+ }
750
+ };
751
+ var Str = class extends Base {
752
+ constructor(_default = "") {
753
+ super();
754
+ this._default = _default;
755
+ }
756
+ sanitize(raw) {
757
+ return typeof raw === "string" ? raw : this._default;
758
+ }
759
+ isValid(raw, ctx, path) {
760
+ if (typeof raw === "string") return true;
761
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
221
762
  return false;
222
763
  }
223
- let valid = true;
224
- if (v.mode !== void 0 && !["auto", "webapi", "frames"].includes(v.mode)) {
225
- errors.push(path + '.mode: invalid value "' + v.mode + `", expected 'auto'|'webapi'|'frames'`);
226
- valid = false;
764
+ };
765
+ var Num = class extends Base {
766
+ constructor(_default = 0) {
767
+ super();
768
+ this._default = _default;
227
769
  }
228
- if (v.duration !== void 0 && typeof v.duration !== "number") {
229
- errors.push(path + ".duration: expected number, got " + typeof v.duration);
230
- valid = false;
770
+ sanitize(raw) {
771
+ return typeof raw === "number" && isFinite(raw) ? raw : this._default;
231
772
  }
232
- if (v.delay !== void 0 && typeof v.delay !== "number") {
233
- errors.push(path + ".delay: expected number, got " + typeof v.delay);
234
- valid = false;
773
+ isValid(raw, ctx, path) {
774
+ if (typeof raw === "number" && isFinite(raw)) return true;
775
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
776
+ return false;
235
777
  }
236
- if (v.iterations !== void 0 && typeof v.iterations !== "number" && v.iterations !== "infinite") {
237
- errors.push(path + ".iterations: expected number or 'infinite', got " + typeof v.iterations);
238
- valid = false;
778
+ };
779
+ var Bool = class extends Base {
780
+ constructor(_default = false) {
781
+ super();
782
+ this._default = _default;
239
783
  }
240
- if (v.fill !== void 0 && !validateFillMode(v.fill, path + ".fill", errors)) valid = false;
241
- if (v.direction !== void 0 && !validatePlaybackDirection(v.direction, path + ".direction", errors)) valid = false;
242
- if (v.frameRate !== void 0 && typeof v.frameRate !== "number") {
243
- errors.push(path + ".frameRate: expected number, got " + typeof v.frameRate);
244
- valid = false;
784
+ sanitize(raw) {
785
+ return typeof raw === "boolean" ? raw : this._default;
245
786
  }
246
- if (v.trigger !== void 0 && !validatePxTrigger(v.trigger, path + ".trigger", errors)) valid = false;
247
- return valid;
248
- }
249
- function validatePxDefs(v, path, errors) {
250
- if (!isObject(v)) {
251
- errors.push(path + ": expected object");
787
+ isValid(raw, ctx, path) {
788
+ if (typeof raw === "boolean") return true;
789
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
252
790
  return false;
253
791
  }
254
- let valid = true;
255
- if (v.easings !== void 0) {
256
- if (!isObject(v.easings)) {
257
- errors.push(path + ".easings: expected object");
258
- valid = false;
259
- } else {
260
- for (const key of Object.keys(v.easings)) {
261
- if (!validatePxEasingOrRef(v.easings[key], path + ".easings." + key, errors)) valid = false;
262
- }
792
+ };
793
+ var Literal = class extends Base {
794
+ constructor(value) {
795
+ super();
796
+ this.value = value;
797
+ this._default = value;
798
+ }
799
+ sanitize(raw) {
800
+ return raw === this.value ? this.value : this._default;
801
+ }
802
+ isValid(raw, ctx, path) {
803
+ if (raw === this.value) return true;
804
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
805
+ return false;
806
+ }
807
+ };
808
+ var Enum = class extends Base {
809
+ constructor(values, defaultVal) {
810
+ super();
811
+ this.values = values;
812
+ this._default = defaultVal != null ? defaultVal : values[0];
813
+ }
814
+ sanitize(raw) {
815
+ return this.values.includes(raw) ? raw : this._default;
816
+ }
817
+ isValid(raw, ctx, path) {
818
+ if (this.values.includes(raw)) return true;
819
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected one of " + this.values.map((v) => JSON.stringify(v)).join(" | ") + ", got " + JSON.stringify(raw));
820
+ return false;
821
+ }
822
+ };
823
+ var Union = class extends Base {
824
+ constructor(schemas, defaultVal) {
825
+ super();
826
+ this.schemas = schemas;
827
+ this._default = defaultVal != null ? defaultVal : schemas[0]._default;
828
+ }
829
+ sanitize(raw) {
830
+ for (const s of this.schemas) {
831
+ if (s.isValid(raw)) return s.sanitize(raw);
263
832
  }
833
+ return this._default;
264
834
  }
265
- if (v.animations !== void 0) {
266
- if (!isObject(v.animations)) {
267
- errors.push(path + ".animations: expected object");
268
- valid = false;
269
- } else {
270
- for (const key of Object.keys(v.animations)) {
271
- if (!validatePxAnimationDefinition(v.animations[key], path + ".animations." + key, errors)) valid = false;
835
+ isValid(raw, ctx, path) {
836
+ var _a;
837
+ if (this.schemas.some((s) => s.isValid(raw))) return true;
838
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no union member matched for value " + ((_a = JSON.stringify(raw)) != null ? _a : "").slice(0, 60));
839
+ return false;
840
+ }
841
+ _canSanitize(raw) {
842
+ return this.schemas.some((s) => s._canSanitize(raw));
843
+ }
844
+ };
845
+ var DiscriminatedUnion = class extends Base {
846
+ constructor(_key, _schemas, defaultVal) {
847
+ super();
848
+ this._key = _key;
849
+ this._schemas = _schemas;
850
+ this._default = defaultVal != null ? defaultVal : _schemas[0]._default;
851
+ this._map = /* @__PURE__ */ new Map();
852
+ for (const s of _schemas) {
853
+ const keySchema = s._shape[_key];
854
+ if (keySchema) this._map.set(keySchema._default, s);
855
+ }
856
+ }
857
+ _findSchema(raw) {
858
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
859
+ const val = raw[this._key];
860
+ if (val === void 0 || val === null) return void 0;
861
+ return this._map.get(val);
862
+ }
863
+ sanitize(raw) {
864
+ var _a;
865
+ return ((_a = this._findSchema(raw)) != null ? _a : this._schemas[0]).sanitize(raw);
866
+ }
867
+ isValid(raw, ctx, path) {
868
+ const schema = this._findSchema(raw);
869
+ if (!schema) {
870
+ const val = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw[this._key] : void 0;
871
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no discriminated union member matched " + this._key + "=" + JSON.stringify(val));
872
+ return false;
873
+ }
874
+ return schema.isValid(raw, ctx, path);
875
+ }
876
+ _canSanitize(raw) {
877
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return false;
878
+ const schema = this._findSchema(raw);
879
+ return schema ? schema._canSanitize(raw) : this._schemas[0]._canSanitize(raw);
880
+ }
881
+ };
882
+ var Obj = class extends Base {
883
+ constructor(_shape) {
884
+ super();
885
+ this._shape = _shape;
886
+ const d = {};
887
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
888
+ this._default = d;
889
+ }
890
+ sanitize(raw) {
891
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
892
+ const out = {};
893
+ for (const key of Object.keys(this._shape)) {
894
+ out[key] = this._shape[key].sanitize(src[key]);
895
+ }
896
+ return out;
897
+ }
898
+ isValid(raw, ctx, path) {
899
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
900
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
901
+ return false;
902
+ }
903
+ const obj = raw;
904
+ const p = path != null ? path : [];
905
+ let ok = true;
906
+ for (const key of Object.keys(this._shape)) {
907
+ p.push(key);
908
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
909
+ p.pop();
910
+ }
911
+ if (ctx == null ? void 0 : ctx.strict) {
912
+ for (const key of Object.keys(obj)) {
913
+ if (key in this._shape) continue;
914
+ p.push(key);
915
+ ctx.errors.push(pathStr(p) + ": unexpected extra key");
916
+ p.pop();
917
+ ok = false;
272
918
  }
273
919
  }
920
+ return ok;
274
921
  }
275
- if (v.styles !== void 0 && !isObject(v.styles)) {
276
- errors.push(path + ".styles: expected object");
277
- valid = false;
922
+ _canSanitize(raw) {
923
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
278
924
  }
279
- return valid;
280
- }
281
- function validatePxBinding(v, path, errors) {
282
- if (!isObject(v)) {
283
- errors.push(path + ": expected object");
284
- return false;
925
+ };
926
+ var OpenObj = class extends Base {
927
+ constructor(_shape, _openSchema) {
928
+ super();
929
+ this._shape = _shape;
930
+ this._openSchema = _openSchema;
931
+ const d = {};
932
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
933
+ this._default = d;
934
+ }
935
+ sanitize(raw) {
936
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
937
+ const out = __spreadValues({}, src);
938
+ for (const key of Object.keys(this._shape)) {
939
+ out[key] = this._shape[key].sanitize(src[key]);
940
+ }
941
+ if (this._openSchema) {
942
+ for (const key of Object.keys(src)) {
943
+ if (!(key in this._shape)) out[key] = this._openSchema.sanitize(src[key]);
944
+ }
945
+ }
946
+ return out;
285
947
  }
286
- let valid = true;
287
- if (typeof v.id !== "string") {
288
- errors.push(path + ".id: expected string, got " + typeof v.id);
289
- valid = false;
948
+ isValid(raw, ctx, path) {
949
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
950
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
951
+ return false;
952
+ }
953
+ const obj = raw;
954
+ const p = path != null ? path : [];
955
+ let ok = true;
956
+ for (const key of Object.keys(this._shape)) {
957
+ p.push(key);
958
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
959
+ p.pop();
960
+ }
961
+ if (this._openSchema) {
962
+ for (const key of Object.keys(obj)) {
963
+ if (key in this._shape) continue;
964
+ p.push(key);
965
+ if (!this._openSchema.isValid(obj[key], ctx, p)) ok = false;
966
+ p.pop();
967
+ }
968
+ }
969
+ return ok;
290
970
  }
291
- if (!validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
292
- return valid;
293
- }
294
- function validatePxNode(v, path, errors) {
295
- if (!isObject(v)) {
296
- errors.push(path + ": expected object");
297
- return false;
971
+ _canSanitize(raw) {
972
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
298
973
  }
299
- let valid = true;
300
- if (typeof v.type !== "string") {
301
- errors.push(path + ".type: expected string, got " + typeof v.type);
302
- valid = false;
974
+ };
975
+ var Arr = class extends Base {
976
+ constructor(item) {
977
+ super();
978
+ this.item = item;
979
+ this._default = [];
980
+ }
981
+ sanitize(raw) {
982
+ if (!Array.isArray(raw)) return [];
983
+ const out = [];
984
+ for (const el of raw) {
985
+ if (this.item._canSanitize(el)) out.push(this.item.sanitize(el));
986
+ }
987
+ return out;
988
+ }
989
+ isValid(raw, ctx, path) {
990
+ if (!Array.isArray(raw)) {
991
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected array, got " + typeof raw);
992
+ return false;
993
+ }
994
+ const p = path != null ? path : [];
995
+ let ok = true;
996
+ for (let i = 0; i < raw.length; i++) {
997
+ p.push("[" + i + "]");
998
+ if (!this.item.isValid(raw[i], ctx, p)) ok = false;
999
+ p.pop();
1000
+ }
1001
+ return ok;
303
1002
  }
304
- if (v.children !== void 0) {
305
- if (!Array.isArray(v.children)) {
306
- errors.push(path + ".children: expected array");
307
- valid = false;
308
- } else {
309
- v.children.forEach((child, i) => {
310
- if (!validatePxNode(child, path + ".children[" + i + "]", errors)) valid = false;
311
- });
1003
+ _canSanitize(raw) {
1004
+ return Array.isArray(raw);
1005
+ }
1006
+ };
1007
+ var Rec = class extends Base {
1008
+ constructor(value) {
1009
+ super();
1010
+ this.value = value;
1011
+ this._default = {};
1012
+ }
1013
+ sanitize(raw) {
1014
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
1015
+ const out = {};
1016
+ for (const [k, v] of Object.entries(raw)) {
1017
+ if (this.value._canSanitize(v)) out[k] = this.value.sanitize(v);
1018
+ }
1019
+ return out;
1020
+ }
1021
+ isValid(raw, ctx, path) {
1022
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
1023
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object/record, got " + (Array.isArray(raw) ? "array" : typeof raw));
1024
+ return false;
1025
+ }
1026
+ const p = path != null ? path : [];
1027
+ let ok = true;
1028
+ for (const [k, v] of Object.entries(raw)) {
1029
+ p.push(k);
1030
+ if (!this.value.isValid(v, ctx, p)) ok = false;
1031
+ p.pop();
312
1032
  }
1033
+ return ok;
313
1034
  }
314
- if (v.animate !== void 0 && !validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
315
- return valid;
316
- }
317
- function validatePxSvgNode(v, path, errors) {
318
- if (!validatePxNode(v, path, errors)) return false;
319
- let valid = true;
320
- if (v.type !== "svg") {
321
- errors.push(path + ".type: expected 'svg', got '" + v.type + "'");
322
- valid = false;
1035
+ _canSanitize(raw) {
1036
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
323
1037
  }
324
- if (v.width !== void 0 && typeof v.width !== "number") {
325
- errors.push(path + ".width: expected number, got " + typeof v.width);
326
- valid = false;
1038
+ };
1039
+ var Any = class extends Base {
1040
+ constructor() {
1041
+ super(...arguments);
1042
+ this._default = void 0;
327
1043
  }
328
- if (v.height !== void 0 && typeof v.height !== "number") {
329
- errors.push(path + ".height: expected number, got " + typeof v.height);
330
- valid = false;
1044
+ sanitize(raw) {
1045
+ return raw;
331
1046
  }
332
- if (v.viewBox !== void 0 && typeof v.viewBox !== "string") {
333
- errors.push(path + ".viewBox: expected string, got " + typeof v.viewBox);
334
- valid = false;
1047
+ isValid(_raw, _ctx, _path) {
1048
+ return true;
335
1049
  }
336
- if (v.animator !== void 0 && !validatePxAnimatorConfig(v.animator, path + ".animator", errors)) valid = false;
337
- if (v.defs !== void 0 && !validatePxDefs(v.defs, path + ".defs", errors)) valid = false;
338
- if (v.bindings !== void 0) {
339
- if (!Array.isArray(v.bindings)) {
340
- errors.push(path + ".bindings: expected array");
341
- valid = false;
342
- } else {
343
- v.bindings.forEach((binding, i) => {
344
- if (!validatePxBinding(binding, path + ".bindings[" + i + "]", errors)) valid = false;
345
- });
1050
+ _canSanitize(_raw) {
1051
+ return true;
1052
+ }
1053
+ };
1054
+ var Lazy = class extends Base {
1055
+ constructor(fn, _default) {
1056
+ super();
1057
+ this.fn = fn;
1058
+ this._default = _default;
1059
+ this.resolved = null;
1060
+ }
1061
+ get schema() {
1062
+ var _a;
1063
+ return (_a = this.resolved) != null ? _a : this.resolved = this.fn();
1064
+ }
1065
+ sanitize(raw) {
1066
+ return this.schema.sanitize(raw);
1067
+ }
1068
+ isValid(raw, ctx, path) {
1069
+ return this.schema.isValid(raw, ctx, path);
1070
+ }
1071
+ _canSanitize(raw) {
1072
+ return this.schema._canSanitize(raw);
1073
+ }
1074
+ };
1075
+ var Tuple = class extends Base {
1076
+ constructor(schemas) {
1077
+ super();
1078
+ this.schemas = schemas;
1079
+ this._default = schemas.map((s) => s._default);
1080
+ }
1081
+ sanitize(raw) {
1082
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) return this._default;
1083
+ return this.schemas.map((s, i) => s.sanitize(raw[i]));
1084
+ }
1085
+ isValid(raw, ctx, path) {
1086
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) {
1087
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected tuple of length " + this.schemas.length + ", got " + (Array.isArray(raw) ? "array[" + raw.length + "]" : typeof raw));
1088
+ return false;
1089
+ }
1090
+ const p = path != null ? path : [];
1091
+ let ok = true;
1092
+ for (let i = 0; i < this.schemas.length; i++) {
1093
+ p.push("[" + i + "]");
1094
+ if (!this.schemas[i].isValid(raw[i], ctx, p)) ok = false;
1095
+ p.pop();
1096
+ }
1097
+ return ok;
1098
+ }
1099
+ // Require exact length so wrong-length arrays are dropped rather than repaired to default.
1100
+ _canSanitize(raw) {
1101
+ return Array.isArray(raw) && raw.length === this.schemas.length;
1102
+ }
1103
+ };
1104
+ function implementsInterface() {
1105
+ return (schema) => schema;
1106
+ }
1107
+ function schemaKeys(schema) {
1108
+ return Object.fromEntries(
1109
+ Object.keys(schema["_shape"]).map((k) => [k, k])
1110
+ );
1111
+ }
1112
+ function describeSchema(schema) {
1113
+ var _a;
1114
+ const s = schema;
1115
+ if ("_shape" in s) return { kind: "shape", shape: s._shape };
1116
+ if ("item" in s) return { kind: "array", item: s.item };
1117
+ if ("inner" in s) return { kind: "optional", inner: s.inner };
1118
+ if ("fn" in s) return { kind: "lazy", resolved: (_a = s.resolved) != null ? _a : s.fn() };
1119
+ return { kind: "leaf" };
1120
+ }
1121
+ var px = {
1122
+ /** Matches a string. Default: '' or provided value. */
1123
+ string: (defaultVal = "") => new Str(defaultVal),
1124
+ /** Matches a finite number. Default: 0 or provided value. */
1125
+ number: (defaultVal = 0) => new Num(defaultVal),
1126
+ /** Matches a boolean. Default: false or provided value. */
1127
+ boolean: (defaultVal = false) => new Bool(defaultVal),
1128
+ /** Matches one exact primitive value; its default is the value itself. */
1129
+ literal: (value) => new Literal(value),
1130
+ /** Matches one of a fixed set of string/number values. Default: first value. */
1131
+ enum: (values, defaultVal) => new Enum(values, defaultVal),
1132
+ /**
1133
+ * Returns the first schema whose isValid passes.
1134
+ * TypeScript infers the union of all member types automatically.
1135
+ */
1136
+ union: (schemas, defaultVal) => new Union(schemas, defaultVal),
1137
+ /**
1138
+ * Discriminated union — reads `raw[key]`, finds the member schema whose
1139
+ * literal at `key` matches, then delegates sanitize/isValid to that member.
1140
+ * Each member must be an object schema with a `px.literal(...)` at `key`.
1141
+ * TypeScript infers the union of all member types automatically.
1142
+ */
1143
+ discriminatedUnion: (key, schemas) => new DiscriminatedUnion(key, schemas),
1144
+ /** Typed object — unknown keys are stripped. Required fields fall back to their default. */
1145
+ object: (shape) => new Obj(shape),
1146
+ /**
1147
+ * Open object — validates known keys; passes unknown keys through as-is,
1148
+ * or validates/sanitizes them against `openSchema` when provided.
1149
+ */
1150
+ openObject: (shape, openSchema) => new OpenObj(shape, openSchema),
1151
+ /**
1152
+ * Creates a new closed object schema by merging a base schema's shape with additional fields.
1153
+ * The base can be the result of px.object() or px.openObject() — anything with a _shape property.
1154
+ *
1155
+ * @example
1156
+ * const PxSvgNodeSchema = px.extendedObject(PxNodeBase, { width: px.number().optional() });
1157
+ */
1158
+ extendedObject: (base, extra) => new Obj(__spreadValues(__spreadValues({}, base._shape), extra)),
1159
+ /** Array whose unrecoverable items are filtered out. Default: []. */
1160
+ array: (item) => new Arr(item),
1161
+ /** String-keyed record whose unrecoverable values are dropped. Default: {}. */
1162
+ record: (value) => new Rec(value),
1163
+ /** Passes anything through unchanged — always valid. */
1164
+ any: () => new Any(),
1165
+ /** Fixed-length tuple — validates element count and each position individually. */
1166
+ tuple: (schemas) => new Tuple(schemas),
1167
+ /** Defers schema creation — required for recursive types. Must supply a default value. */
1168
+ lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
1169
+ };
1170
+
1171
+ // src/PxAnimatorTypes.ts
1172
+ var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
1173
+ var PX_ANIM_ATTR_NAME = "_px_animator";
1174
+ var TEXT_ATTR = "text";
1175
+ var TEXT_CONTENT_ATTR = "textContent";
1176
+ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
1177
+ "type",
1178
+ "children",
1179
+ "animator",
1180
+ "meta",
1181
+ "animate",
1182
+ TEXT_ATTR,
1183
+ TEXT_CONTENT_ATTR
1184
+ ]);
1185
+ var PxEasingOrRefSchema = px.union([
1186
+ px.string(),
1187
+ px.tuple([px.number(), px.number(), px.number(), px.number()])
1188
+ ]);
1189
+ var PxKeyframeValueSchema = implementsInterface()(px.union([
1190
+ px.string(),
1191
+ // e.g. for colors
1192
+ px.number(),
1193
+ px.array(px.number()),
1194
+ px.lazy(() => PxTransformPartsSchema, {}),
1195
+ px.object({ path: px.string() }),
1196
+ px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] })
1197
+ ]));
1198
+ var PxKeyframeSchema = implementsInterface()(px.object({
1199
+ time: px.number().optional(),
1200
+ t: px.number().optional(),
1201
+ value: px.any().optional(),
1202
+ v: px.any().optional(),
1203
+ easing: PxEasingOrRefSchema.optional(),
1204
+ e: PxEasingOrRefSchema.optional(),
1205
+ tangentOut: px.tuple([px.number(), px.number()]).optional(),
1206
+ to: px.tuple([px.number(), px.number()]).optional(),
1207
+ // short alias
1208
+ tangentIn: px.tuple([px.number(), px.number()]).optional(),
1209
+ ti: px.tuple([px.number(), px.number()]).optional(),
1210
+ // short alias
1211
+ selected: px.boolean().optional()
1212
+ // editor-side UI state (Player ignores it)
1213
+ }));
1214
+ var PxLoopSchema = implementsInterface()(px.object({
1215
+ segmentCount: px.number().optional(),
1216
+ before: px.boolean().optional(),
1217
+ alternate: px.boolean().optional()
1218
+ }));
1219
+ var PxPropertyAnimationSchema = implementsInterface()(px.object({
1220
+ keyframes: px.array(PxKeyframeSchema).optional(),
1221
+ kfs: px.array(PxKeyframeSchema).optional(),
1222
+ loop: px.union([PxLoopSchema, px.boolean()]).optional(),
1223
+ autoOrient: px.boolean().optional()
1224
+ }));
1225
+ var PX_TRANSFORM_PART_KEYS = ["translate", "rotate", "scale", "origin"];
1226
+ var PxTransformPartsSchema = implementsInterface()(px.object({
1227
+ translate: px.tuple([px.number(), px.number()]).optional(),
1228
+ rotate: px.number().optional(),
1229
+ scale: px.tuple([px.number(), px.number()]).optional(),
1230
+ origin: px.tuple([px.number(), px.number()]).optional()
1231
+ }));
1232
+ var PxTransformValueSchema = px.union([
1233
+ px.string(),
1234
+ px.object({ value: PxTransformPartsSchema }),
1235
+ PxPropertyAnimationSchema
1236
+ ]);
1237
+ var PxAnimationDefinitionSchema = implementsInterface()(
1238
+ px.record(PxPropertyAnimationSchema)
1239
+ );
1240
+ var PxElementAnimationSchema = implementsInterface()(px.union([
1241
+ px.string(),
1242
+ px.array(px.union([px.string(), PxAnimationDefinitionSchema])),
1243
+ PxAnimationDefinitionSchema
1244
+ ]));
1245
+ var PxTriggerSchema = implementsInterface()(px.object({
1246
+ startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
1247
+ outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
1248
+ scrollIntoViewThreshold: px.number().optional()
1249
+ }));
1250
+ var PxDefsSchema = implementsInterface()(px.object({
1251
+ easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
1252
+ animations: px.record(PxAnimationDefinitionSchema).optional(),
1253
+ styles: px.record(px.any()).optional()
1254
+ }));
1255
+ var PxAnimatorConfigSchema = implementsInterface()(px.object({
1256
+ mode: px.enum(["auto", "webapi", "frames"]).optional(),
1257
+ duration: px.number().optional(),
1258
+ delay: px.number().optional(),
1259
+ iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1260
+ fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
1261
+ direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
1262
+ frameRate: px.number().optional(),
1263
+ trigger: PxTriggerSchema.optional(),
1264
+ definitions: PxDefsSchema.optional(),
1265
+ animate: px.record(PxElementAnimationSchema).optional(),
1266
+ debug: px.boolean().optional(),
1267
+ debugInstName: px.string().optional()
1268
+ }));
1269
+ var PxBindingSchema = implementsInterface()(px.object({
1270
+ id: px.string(),
1271
+ animate: PxElementAnimationSchema
1272
+ }));
1273
+ var PxAttrValueSchema = px.union([
1274
+ px.string(),
1275
+ px.number(),
1276
+ px.object({ value: px.any() }),
1277
+ PxPropertyAnimationSchema
1278
+ ]);
1279
+ var PxAnimatableNumberSchema = px.union([
1280
+ px.number(),
1281
+ px.object({ value: px.number() }),
1282
+ px.object({
1283
+ keyframes: px.array(PxKeyframeSchema),
1284
+ autoOrient: px.boolean().optional()
1285
+ })
1286
+ ]);
1287
+ var PxAnimatableVec2Schema = px.union([
1288
+ px.tuple([px.number(), px.number()]),
1289
+ px.object({ value: px.tuple([px.number(), px.number()]) }),
1290
+ px.object({
1291
+ keyframes: px.array(PxKeyframeSchema),
1292
+ autoOrient: px.boolean().optional()
1293
+ })
1294
+ ]);
1295
+ var PxTransformationEffectSchema = px.object({
1296
+ translate: PxAnimatableVec2Schema.optional(),
1297
+ rotate: PxAnimatableNumberSchema.optional(),
1298
+ scale: PxAnimatableVec2Schema.optional(),
1299
+ skew: PxAnimatableVec2Schema.optional(),
1300
+ origin: PxAnimatableVec2Schema.optional()
1301
+ });
1302
+ var PxRepeaterEffectSchema = px.object({
1303
+ copies: px.number().optional(),
1304
+ translate: px.tuple([px.number(), px.number()]).optional(),
1305
+ rotate: px.number().optional(),
1306
+ scale: px.tuple([px.number(), px.number()]).optional(),
1307
+ // per-copy scale, PERCENT (85 → 0.85)
1308
+ origin: px.tuple([px.number(), px.number()]).optional()
1309
+ });
1310
+ var PxMaskedByEffectSchema = px.object({
1311
+ href: px.string().optional(),
1312
+ maskType: px.string().optional(),
1313
+ maskUnits: px.string().optional(),
1314
+ maskContentUnits: px.string().optional()
1315
+ });
1316
+ var PxTrimPathEffectSchema = px.any();
1317
+ var PxRetimeEffectSchema = px.object({
1318
+ baseId: px.string().optional(),
1319
+ start: px.number().optional(),
1320
+ stretch: px.number().optional(),
1321
+ timeCrop: px.tuple([px.number(), px.number()]).optional()
1322
+ });
1323
+ var PxRefEffectSchema = px.object({
1324
+ baseId: px.string().optional(),
1325
+ type: px.string().optional()
1326
+ });
1327
+ var PxEffectsSchema = px.object({
1328
+ transformation: PxTransformationEffectSchema.optional(),
1329
+ repeater: PxRepeaterEffectSchema.optional(),
1330
+ maskedBy: PxMaskedByEffectSchema.optional(),
1331
+ trimPath: PxTrimPathEffectSchema.optional(),
1332
+ retime: PxRetimeEffectSchema.optional(),
1333
+ isCombinedShape: px.boolean().optional(),
1334
+ ref: PxRefEffectSchema.optional()
1335
+ });
1336
+ function validateNodeEffects(root, opts) {
1337
+ const warnings = [];
1338
+ const walk = (node, path) => {
1339
+ if (node && node.effects) {
1340
+ const ctx = { errors: [], warnings: [], strict: !!(opts == null ? void 0 : opts.strict) };
1341
+ const ok = PxEffectsSchema.isValid(node.effects, ctx, [path + ".effects"]);
1342
+ if (!ok) {
1343
+ for (const err of ctx.errors) warnings.push(err);
1344
+ }
1345
+ }
1346
+ if (node && Array.isArray(node.children)) {
1347
+ node.children.forEach((c, i) => walk(c, path + ".children[" + i + "]"));
346
1348
  }
1349
+ };
1350
+ walk(root, "root");
1351
+ return warnings;
1352
+ }
1353
+ var PxNodeBase = px.openObject({
1354
+ type: px.string(),
1355
+ id: px.string().optional(),
1356
+ meta: px.any().optional(),
1357
+ // Player-effects bucket emitted by the Editor's lightweight design format.
1358
+ // Consumed and removed by `applyPlayerEffects` before any other normalisation
1359
+ // (see `createAnimatorImpl`), so downstream code never sees it.
1360
+ effects: PxEffectsSchema.optional(),
1361
+ animate: PxAnimationDefinitionSchema.optional(),
1362
+ style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
1363
+ }, PxAttrValueSchema);
1364
+ var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
1365
+ children: px.lazy(() => px.array(PxNodeSchema), []).optional()
1366
+ }), PxAttrValueSchema);
1367
+ var PxSvgNodeExtra = px.object({
1368
+ width: px.number().optional(),
1369
+ height: px.number().optional(),
1370
+ viewBox: px.string().optional(),
1371
+ animator: PxAnimatorConfigSchema.optional()
1372
+ });
1373
+ var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
1374
+ type: px.literal("svg"),
1375
+ // override string → literal to require 'svg'
1376
+ children: px.array(PxNodeSchema).optional()
1377
+ }), PxAttrValueSchema);
1378
+ var PxBezierPathSchema = implementsInterface()(px.object({
1379
+ v: px.array(px.array(px.number())),
1380
+ i: px.array(px.array(px.number())).optional(),
1381
+ o: px.array(px.array(px.number())).optional(),
1382
+ c: px.boolean().optional()
1383
+ }));
1384
+ function isPxElementFileFormat(fileJson) {
1385
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
1386
+ return false;
347
1387
  }
348
- if (v.design !== void 0 && !validatePxNode(v.design, path + ".design", errors)) valid = false;
349
- return valid;
1388
+ return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
350
1389
  }
351
1390
  function isPxElementFileFormatDeep(fileJson) {
352
- const errors = [];
353
- const valid = validatePxSvgNode(fileJson, "root", errors);
354
- return { valid, errors };
1391
+ const valid = PxAnimatedSvgDocumentSchema.isValid(fileJson);
1392
+ return { valid, errors: valid ? [] : ["Document failed schema validation"] };
355
1393
  }
356
1394
  function getAnimatorConfig(doc) {
357
1395
  var _a, _b;
@@ -360,12 +1398,14 @@ function getAnimatorConfig(doc) {
360
1398
  function getDefs(doc) {
361
1399
  var _a;
362
1400
  if (!doc) return void 0;
363
- return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
1401
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
364
1402
  }
365
1403
  function getBindings(doc) {
366
1404
  var _a;
367
1405
  if (!doc) return void 0;
368
- return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
1406
+ const animate = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animate;
1407
+ if (!animate) return void 0;
1408
+ return Object.entries(animate).map(([id, anim]) => ({ id, animate: anim }));
369
1409
  }
370
1410
  function getChildren(doc) {
371
1411
  return doc == null ? void 0 : doc.children;
@@ -604,6 +1644,24 @@ function parseColor(s) {
604
1644
  var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
605
1645
  var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
606
1646
  var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1647
+ function composeTransformParts(parts, opts) {
1648
+ var _a;
1649
+ if (!parts) return "";
1650
+ const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
1651
+ const segs = [];
1652
+ const t = parts.translate;
1653
+ const o = parts.origin;
1654
+ const r = parts.rotate;
1655
+ const s = parts.scale;
1656
+ const tu = withUnits ? "px" : "";
1657
+ const ru = withUnits ? "deg" : "";
1658
+ if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
1659
+ if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
1660
+ if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
1661
+ if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
1662
+ if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
1663
+ return segs.join("");
1664
+ }
607
1665
  var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
608
1666
  var DEFAULT_DURATION_MS = 1e3;
609
1667
  function kebabToCamelCaseWord(kebab) {
@@ -663,6 +1721,80 @@ function camelCaseToKebabWordIfNeeded(camel) {
663
1721
  function clamp(value, min, max) {
664
1722
  return Math.max(min, Math.min(value, max));
665
1723
  }
1724
+ function bezier2D_pointAt(P0, P1, P2, P3, t) {
1725
+ if (t <= 0) return [P0[0], P0[1]];
1726
+ if (t >= 1) return [P3[0], P3[1]];
1727
+ const u = 1 - t;
1728
+ const u2 = u * u;
1729
+ const u3 = u2 * u;
1730
+ const t2 = t * t;
1731
+ const t3 = t2 * t;
1732
+ const w0 = u3;
1733
+ const w1 = 3 * t * u2;
1734
+ const w2 = 3 * t2 * u;
1735
+ const w3 = t3;
1736
+ return [
1737
+ w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
1738
+ w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
1739
+ ];
1740
+ }
1741
+ var BEZIER_T_NUDGE = 1e-4;
1742
+ function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
1743
+ const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
1744
+ if (result[0] === 0 && result[1] === 0) {
1745
+ const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
1746
+ return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
1747
+ }
1748
+ return result;
1749
+ }
1750
+ function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
1751
+ const u = 1 - t;
1752
+ const a = 3 * u * u;
1753
+ const b = 6 * t * u;
1754
+ const c = 3 * t * t;
1755
+ return [
1756
+ a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
1757
+ a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
1758
+ ];
1759
+ }
1760
+ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
1761
+ const n = steps + 1;
1762
+ const ts = new Float64Array(n);
1763
+ const ds = new Float64Array(n);
1764
+ let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
1765
+ ts[0] = 0;
1766
+ ds[0] = 0;
1767
+ let cum = 0;
1768
+ for (let i = 1; i < n; i++) {
1769
+ const t = i / steps;
1770
+ const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
1771
+ const dx = cur[0] - prev[0];
1772
+ const dy = cur[1] - prev[1];
1773
+ cum += Math.sqrt(dx * dx + dy * dy);
1774
+ ts[i] = t;
1775
+ ds[i] = cum;
1776
+ prev = cur;
1777
+ }
1778
+ return { ts, ds };
1779
+ }
1780
+ function bezier2D_tForDistance(lut, distance) {
1781
+ const { ts, ds } = lut;
1782
+ const last = ds.length - 1;
1783
+ if (distance <= 0) return ts[0];
1784
+ if (distance >= ds[last]) return ts[last];
1785
+ let lo = 1;
1786
+ let hi = last;
1787
+ while (lo < hi) {
1788
+ const mid = lo + hi >>> 1;
1789
+ if (ds[mid] < distance) lo = mid + 1;
1790
+ else hi = mid;
1791
+ }
1792
+ const dPrev = ds[hi - 1];
1793
+ const dCur = ds[hi];
1794
+ const span = dCur - dPrev;
1795
+ const frac = span > 0 ? (distance - dPrev) / span : 0;
1796
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
1797
+ }
666
1798
 
667
1799
  // src/PxAnimatorDOM.ts
668
1800
  var SVG_NS = "http://www.w3.org/2000/svg";
@@ -678,6 +1810,7 @@ var ALLOWED_SVG_TAGS_LOWER_CASE = new Set([
678
1810
  "polygon",
679
1811
  "text",
680
1812
  "tspan",
1813
+ "textPath",
681
1814
  "defs",
682
1815
  "clipPath",
683
1816
  "mask",
@@ -718,13 +1851,21 @@ var ALLOWED_RESOURCE_ATTRIBUTES = [
718
1851
  // Don't allow 'cursor', can use external SVG, // url(cursor.svg)
719
1852
  ];
720
1853
  var ALLOWED_RESOURCE_ATTRIBUTES_SET = new Set(ALLOWED_RESOURCE_ATTRIBUTES);
721
- var ALLOWED_ATTRIBUTES_SET = /* @__PURE__ */ new Set([
1854
+ var ALLOWED_ATTRIBUTES = [
722
1855
  "href",
723
1856
  "src",
724
1857
  // Presentation
725
1858
  "fill",
1859
+ "fillOpacity",
1860
+ "fillRule",
726
1861
  "stroke",
727
1862
  "strokeWidth",
1863
+ "strokeOpacity",
1864
+ "strokeLinecap",
1865
+ "strokeLinejoin",
1866
+ "strokeMiterlimit",
1867
+ "strokeDasharray",
1868
+ "strokeDashoffset",
728
1869
  "opacity",
729
1870
  "transform",
730
1871
  // Geometry
@@ -743,10 +1884,27 @@ var ALLOWED_ATTRIBUTES_SET = /* @__PURE__ */ new Set([
743
1884
  "x2",
744
1885
  "y2",
745
1886
  "points",
746
- // Text
1887
+ "dx",
1888
+ "dy",
1889
+ // Font
747
1890
  "fontSize",
748
1891
  "fontFamily",
1892
+ "fontWeight",
1893
+ "fontStyle",
1894
+ // Text
749
1895
  "textAnchor",
1896
+ "letterSpacing",
1897
+ "wordSpacing",
1898
+ "space",
1899
+ "xml:space",
1900
+ "textDecoration",
1901
+ "textTransform",
1902
+ "whiteSpace",
1903
+ "white-space",
1904
+ "dominantBaseline",
1905
+ "alignmentBaseline",
1906
+ "rotate",
1907
+ // per-glyph rotation array, currently not supported
750
1908
  // Structure
751
1909
  "id",
752
1910
  "class",
@@ -760,6 +1918,16 @@ var ALLOWED_ATTRIBUTES_SET = /* @__PURE__ */ new Set([
760
1918
  // Clippath/Mask
761
1919
  "clipPath",
762
1920
  "mask",
1921
+ // Motion path
1922
+ "offsetPath",
1923
+ "offsetDistance",
1924
+ "offsetRotate",
1925
+ "offsetAnchor",
1926
+ "offsetPosition",
1927
+ // Text path
1928
+ "startOffset",
1929
+ "textLength",
1930
+ "lengthAdjust",
763
1931
  // Filter
764
1932
  "filter",
765
1933
  "stdDeviation",
@@ -768,10 +1936,11 @@ var ALLOWED_ATTRIBUTES_SET = /* @__PURE__ */ new Set([
768
1936
  "result",
769
1937
  "mode",
770
1938
  ...ALLOWED_RESOURCE_ATTRIBUTES
771
- ]);
1939
+ ];
1940
+ var ALLOWED_ATTRIBUTES_LOW_SET = new Set(ALLOWED_ATTRIBUTES.map((str) => str.toLowerCase()));
772
1941
  function sanitiseAttributeValue(name, value) {
773
1942
  const nameLower = name.toLowerCase();
774
- if (!ALLOWED_ATTRIBUTES_SET.has(nameLower)) {
1943
+ if (!ALLOWED_ATTRIBUTES_LOW_SET.has(nameLower)) {
775
1944
  console.warn("Attribute not in whitelist: ", nameLower);
776
1945
  return void 0;
777
1946
  }
@@ -796,7 +1965,10 @@ function sanitiseAttributeValue(name, value) {
796
1965
  return value;
797
1966
  }
798
1967
  function createElement(tagName, normalisedProps, style, children, textContent) {
799
- if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) return null;
1968
+ if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) {
1969
+ console.warn("Attribute not in whitelist: ", tagName);
1970
+ return null;
1971
+ }
800
1972
  const element = document.createElementNS(SVG_NS, tagName);
801
1973
  for (const propName in normalisedProps) {
802
1974
  element.setAttribute(
@@ -833,6 +2005,8 @@ function getNormalizedProps(props) {
833
2005
  let value = props[key];
834
2006
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
835
2007
  propsCopy[key] = toRGBA(value);
2008
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
2009
+ propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
836
2010
  } else if (TRANSFORM_FN_NAMES.has(key)) {
837
2011
  if (Array.isArray(value)) {
838
2012
  if (key === "translate") value = value.map((v) => v + "px");
@@ -848,8 +2022,8 @@ function getNormalizedProps(props) {
848
2022
  }
849
2023
  function renderNode(node, defs) {
850
2024
  if (!node) return null;
851
- const _a = node, { type, children, animate, style } = _a, props = __objRest(_a, ["type", "children", "animate", "style"]);
852
- const nodeDefs = node.defs || defs;
2025
+ const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
2026
+ const nodeDefs = getDefs(node) || defs;
853
2027
  const resolvedStyle = resolveStyle(style, nodeDefs);
854
2028
  let childElements;
855
2029
  if (children) {
@@ -947,6 +2121,47 @@ function setupAnimationTriggers(api, config) {
947
2121
  return api;
948
2122
  }
949
2123
 
2124
+ // src/PxMotionPath.ts
2125
+ function propAnimIsMotionPath(anim) {
2126
+ var _a;
2127
+ const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
2128
+ if (!Array.isArray(kfs)) return false;
2129
+ if (anim.autoOrient) return true;
2130
+ for (const kf of kfs) {
2131
+ if (kf.tangentIn || kf.tangentOut) return true;
2132
+ }
2133
+ return false;
2134
+ }
2135
+ var _segmentCache = /* @__PURE__ */ new WeakMap();
2136
+ function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
2137
+ const existing = _segmentCache.get(prevKf);
2138
+ if (existing) return existing;
2139
+ const to = prevKf.tangentOut;
2140
+ const ti = nextKf.tangentIn;
2141
+ const P1 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];
2142
+ const P2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];
2143
+ const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);
2144
+ const entry = {
2145
+ P0: prevPos,
2146
+ P1,
2147
+ P2,
2148
+ P3: nextPos,
2149
+ lut,
2150
+ totalArc: lut.ds[lut.ds.length - 1]
2151
+ };
2152
+ _segmentCache.set(prevKf, entry);
2153
+ return entry;
2154
+ }
2155
+ function evaluateMotionPathSegment(prevKf, nextKf, prevPos, nextPos, localProgress, autoOrient) {
2156
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
2157
+ const t = seg.totalArc === 0 ? localProgress : bezier2D_tForDistance(seg.lut, localProgress * seg.totalArc);
2158
+ const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2159
+ if (!autoOrient) return { translate: point };
2160
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
2161
+ const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
2162
+ return { translate: point, rotateDeg };
2163
+ }
2164
+
950
2165
  // src/PxDefinitions.ts
951
2166
  function parsePathCommands(d) {
952
2167
  const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
@@ -1010,6 +2225,8 @@ function parseSvgPathToBezier(d) {
1010
2225
  currentPath.o.push([x2, y2]);
1011
2226
  } else if (type === "Z" || type === "z") {
1012
2227
  currentPath.c = true;
2228
+ } else {
2229
+ console.warn('Unsupported path command "' + type + '"');
1013
2230
  }
1014
2231
  }
1015
2232
  return res;
@@ -1027,13 +2244,17 @@ function isPathString(value) {
1027
2244
  return typeof value === "string" && extractPathData(value) !== void 0;
1028
2245
  }
1029
2246
  function normalizePathValue(value) {
2247
+ if (value && typeof value === "object" && typeof value.path === "string") {
2248
+ const d = extractPathData(value.path);
2249
+ return d ? { paths: parseSvgPathToBezier(d) } : value;
2250
+ }
1030
2251
  if (value && typeof value === "object" && "paths" in value) {
1031
2252
  const pathsArray = value.paths;
1032
2253
  if (Array.isArray(pathsArray) && pathsArray.length > 0) {
1033
2254
  if (isPathString(pathsArray[0])) {
1034
2255
  const paths = [];
1035
- for (const pathStr of pathsArray) {
1036
- const d = extractPathData(pathStr);
2256
+ for (const pathStr2 of pathsArray) {
2257
+ const d = extractPathData(pathStr2);
1037
2258
  if (d) {
1038
2259
  paths.push(...parseSvgPathToBezier(d));
1039
2260
  }
@@ -1046,8 +2267,8 @@ function normalizePathValue(value) {
1046
2267
  if (Array.isArray(value)) {
1047
2268
  if (value.length > 0 && isPathString(value[0])) {
1048
2269
  const paths = [];
1049
- for (const pathStr of value) {
1050
- const d = extractPathData(pathStr);
2270
+ for (const pathStr2 of value) {
2271
+ const d = extractPathData(pathStr2);
1051
2272
  if (d) {
1052
2273
  paths.push(...parseSvgPathToBezier(d));
1053
2274
  }
@@ -1285,10 +2506,11 @@ function getNormalisedBindings(doc) {
1285
2506
  }
1286
2507
  }
1287
2508
  const processNode = (node) => {
1288
- if (node.animate) {
2509
+ const inlineAnim = node.animate;
2510
+ if (inlineAnim && Object.keys(inlineAnim).length > 0) {
1289
2511
  const nodeId = node.id || generateElementId();
1290
2512
  node.id = nodeId;
1291
- const normalized = processAnimation(nodeId, node.animate);
2513
+ const normalized = processAnimation(nodeId, inlineAnim);
1292
2514
  if (normalized) bindings.push(normalized);
1293
2515
  }
1294
2516
  if (node.children) {
@@ -1359,6 +2581,41 @@ function calcPropertyValue(propName, propAnim, progress) {
1359
2581
  localProgress
1360
2582
  ).join(" ");
1361
2583
  cssAttrName = propName;
2584
+ } else if (cssAttrName === "transform" && prevV !== null && typeof prevV === "object" && !Array.isArray(prevV)) {
2585
+ const partKeys = /* @__PURE__ */ new Set([
2586
+ ...prevV ? Object.keys(prevV) : [],
2587
+ ...nextV ? Object.keys(nextV) : []
2588
+ ]);
2589
+ const partsResult = {};
2590
+ for (const partKey of partKeys) {
2591
+ const prevPart = prevV == null ? void 0 : prevV[partKey];
2592
+ const nextPart = nextV == null ? void 0 : nextV[partKey];
2593
+ if (partKey === "rotate") {
2594
+ partsResult.rotate = interpolateNum(+(prevPart != null ? prevPart : 0), +(nextPart != null ? nextPart : 0), localProgress);
2595
+ } else if (partKey === "translate" || partKey === "scale" || partKey === "origin") {
2596
+ const fallback = partKey === "scale" ? [1, 1] : [0, 0];
2597
+ const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);
2598
+ partsResult[partKey] = interp;
2599
+ }
2600
+ }
2601
+ if (propAnimIsMotionPath(propAnim)) {
2602
+ const prevTr = prevV.translate;
2603
+ const nextTr = nextV.translate;
2604
+ if (Array.isArray(prevTr) && Array.isArray(nextTr)) {
2605
+ const sample = evaluateMotionPathSegment(
2606
+ prevKf,
2607
+ nextKf,
2608
+ [+prevTr[0], +prevTr[1]],
2609
+ [+nextTr[0], +nextTr[1]],
2610
+ localProgress,
2611
+ !!propAnim.autoOrient
2612
+ );
2613
+ partsResult.translate = [sample.translate[0], sample.translate[1]];
2614
+ if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg;
2615
+ }
2616
+ }
2617
+ cssValue = composeTransformParts(partsResult, { withUnits: false });
2618
+ cssAttrName = "transform";
1362
2619
  } else if (cssAttrName === "translate") {
1363
2620
  const v = interpolateVec(
1364
2621
  prevV || [0, 0],
@@ -1384,12 +2641,12 @@ function calcPropertyValue(propName, propAnim, progress) {
1384
2641
  cssValue = "scale(" + v.join(",") + ")";
1385
2642
  cssAttrName = "transform";
1386
2643
  } else {
1387
- const num = interpolateNum(
2644
+ const num2 = interpolateNum(
1388
2645
  +(prevV || 0),
1389
2646
  +(nextV || 0),
1390
2647
  localProgress
1391
2648
  );
1392
- cssValue = num;
2649
+ cssValue = num2;
1393
2650
  }
1394
2651
  if (PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
1395
2652
  cssValue = cssValue * 100 + "%";
@@ -1681,6 +2938,9 @@ function createCssKf(kf, t, propName, unsupportedSet) {
1681
2938
  let cssKey = propName;
1682
2939
  if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
1683
2940
  cssValue = toRGBA(value);
2941
+ } else if (propName === "transform" && value !== null && typeof value === "object" && !Array.isArray(value)) {
2942
+ cssValue = composeTransformParts(value, { withUnits: true });
2943
+ cssKey = "transform";
1684
2944
  } else if (TRANSFORM_FN_NAMES.has(propName)) {
1685
2945
  if (Array.isArray(value)) {
1686
2946
  if (propName === "translate") value = value.map((v) => v + "px");
@@ -1697,28 +2957,59 @@ function createCssKf(kf, t, propName, unsupportedSet) {
1697
2957
  cssKf[cssKey] = cssValue;
1698
2958
  return cssKf;
1699
2959
  }
2960
+ function clipKeyframesToDuration(propName, keyframes, duration) {
2961
+ var _a, _b, _c, _d, _e;
2962
+ const result = [];
2963
+ for (let i = 0; i < keyframes.length; i++) {
2964
+ const kf = keyframes[i];
2965
+ const t = (_a = kf.t) != null ? _a : 0;
2966
+ if (t < 0) {
2967
+ const next = keyframes[i + 1];
2968
+ if (next && ((_b = next.t) != null ? _b : 0) >= 0) {
2969
+ const nextT = (_c = next.t) != null ? _c : 0;
2970
+ const localFrac = (0 - t) / (nextT - t);
2971
+ const easedFrac = kf.e ? cubicBezier(kf.e)(localFrac) : localFrac;
2972
+ const { right: rightEasing } = splitEasing(kf.e, localFrac);
2973
+ result.push({ t: 0, v: interpolateValue(propName, kf.v, next.v, easedFrac), e: rightEasing });
2974
+ }
2975
+ continue;
2976
+ }
2977
+ if (t > duration) {
2978
+ const prev = keyframes[i - 1];
2979
+ if (prev && ((_d = prev.t) != null ? _d : 0) <= duration) {
2980
+ const prevT = (_e = prev.t) != null ? _e : 0;
2981
+ const localFrac = (duration - prevT) / (t - prevT);
2982
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2983
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
2984
+ if (result.length > 0) result[result.length - 1] = __spreadProps(__spreadValues({}, result[result.length - 1]), { e: leftEasing });
2985
+ result.push({ t: duration, v: interpolateValue(propName, prev.v, kf.v, easedFrac), e: void 0 });
2986
+ }
2987
+ break;
2988
+ }
2989
+ result.push(kf);
2990
+ }
2991
+ return result;
2992
+ }
1700
2993
  function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
1701
- var _a, _b;
2994
+ var _a;
1702
2995
  const result = /* @__PURE__ */ new Map();
1703
2996
  for (const [propName, propAnim] of Object.entries(animDef)) {
1704
- const keyframes = propAnim.kfs || propAnim.keyframes || [];
2997
+ const duration = config.duration || 1;
2998
+ const clippedKeyframes = clipKeyframesToDuration(propName, propAnim.kfs || propAnim.keyframes || [], duration);
1705
2999
  const cssKeyframes = [];
1706
- for (let i = 0; i < keyframes.length; i++) {
1707
- const kf = keyframes[i];
1708
- let t = (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
1709
- t = clamp(t / (config.duration || 1), 0, 1);
3000
+ for (let i = 0; i < clippedKeyframes.length; i++) {
3001
+ const kf = clippedKeyframes[i];
3002
+ const t = clamp(((_a = kf.t) != null ? _a : 0) / duration, 0, 1);
1710
3003
  const cssKf = createCssKf(kf, t, propName, unsupportedSet);
1711
3004
  if (i === 0 && (cssKf.offset || 0) > 0) {
1712
- cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
1713
- offset: 0
1714
- }));
3005
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), { offset: 0 }));
1715
3006
  }
1716
3007
  cssKeyframes.push(cssKf);
1717
- if (i === keyframes.length - 1 && (cssKf.offset || 0) < 1) {
1718
- cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
1719
- offset: 1
1720
- }));
1721
- }
3008
+ }
3009
+ if (cssKeyframes.length > 0 && (cssKeyframes[cssKeyframes.length - 1].offset || 0) < 1) {
3010
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKeyframes[cssKeyframes.length - 1]), {
3011
+ offset: 1
3012
+ }));
1722
3013
  }
1723
3014
  if (cssKeyframes.length > 0) {
1724
3015
  result.set(propName, cssKeyframes);
@@ -1727,6 +3018,7 @@ function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
1727
3018
  return result;
1728
3019
  }
1729
3020
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs) {
3021
+ var _a;
1730
3022
  const config = getAnimatorConfig(doc) || {};
1731
3023
  const bindings = getNormalisedBindings(doc);
1732
3024
  if (!rootElement) {
@@ -1764,7 +3056,11 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1764
3056
  const effectOptions = {
1765
3057
  duration: config.duration,
1766
3058
  delay: positiveDelay,
1767
- fill: config.fill,
3059
+ // Default to 'forwards' so elements hold their final state after the
3060
+ // animation ends — consistent with Lottie and other animation runtimes.
3061
+ // Without this, seeking to the last frame reverts elements to their
3062
+ // pre-animation state (the Web Animations API "after" phase with fill:'none').
3063
+ fill: (_a = config.fill) != null ? _a : "forwards",
1768
3064
  direction: config.direction,
1769
3065
  iterations
1770
3066
  };
@@ -1776,12 +3072,12 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1776
3072
  const effect = new KeyframeEffect(element, keyframes, effectOptions);
1777
3073
  const anim = new Animation(effect, document.timeline);
1778
3074
  if (callbacks == null ? void 0 : callbacks.onFinish) anim.onfinish = () => {
1779
- var _a;
1780
- return (_a = callbacks.onFinish) == null ? void 0 : _a.call(callbacks);
3075
+ var _a2;
3076
+ return (_a2 = callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
1781
3077
  };
1782
3078
  if (callbacks == null ? void 0 : callbacks.onRemove) anim.onremove = () => {
1783
- var _a;
1784
- return (_a = callbacks.onRemove) == null ? void 0 : _a.call(callbacks);
3079
+ var _a2;
3080
+ return (_a2 = callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
1785
3081
  };
1786
3082
  if (seekPosition) {
1787
3083
  anim.currentTime = seekPosition;
@@ -1802,29 +3098,29 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1802
3098
  "isReady": () => true,
1803
3099
  "getRootElement": () => rootElement || null,
1804
3100
  "isPlaying": () => {
1805
- var _a;
1806
- return ((_a = animations[0]) == null ? void 0 : _a.playState) === "running";
3101
+ var _a2;
3102
+ return ((_a2 = animations[0]) == null ? void 0 : _a2.playState) === "running";
1807
3103
  },
1808
3104
  "play": () => {
1809
- var _a;
3105
+ var _a2;
1810
3106
  animations.forEach((a) => a.play());
1811
- (_a = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a.call(callbacks);
3107
+ (_a2 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a2.call(callbacks);
1812
3108
  },
1813
3109
  "pause": () => {
1814
- var _a;
3110
+ var _a2;
1815
3111
  animations.forEach((a) => a.pause());
1816
- (_a = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a.call(callbacks);
3112
+ (_a2 = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a2.call(callbacks);
1817
3113
  },
1818
3114
  "cancel": () => {
1819
- var _a;
3115
+ var _a2;
1820
3116
  animations.forEach((a) => a.cancel());
1821
- (_a = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a.call(callbacks);
3117
+ (_a2 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a2.call(callbacks);
1822
3118
  },
1823
3119
  "finish": () => {
1824
- var _a;
3120
+ var _a2;
1825
3121
  for (const a of animations) {
1826
3122
  try {
1827
- if (((_a = a.effect) == null ? void 0 : _a.getTiming().iterations) === Infinity) {
3123
+ if (((_a2 = a.effect) == null ? void 0 : _a2.getTiming().iterations) === Infinity) {
1828
3124
  a.effect.updateTiming({ iterations: 1 });
1829
3125
  a.finish();
1830
3126
  a.effect.updateTiming({ iterations: Infinity });
@@ -1841,8 +3137,8 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1841
3137
  return api;
1842
3138
  },
1843
3139
  "getCurrentTime": () => {
1844
- var _a, _b;
1845
- const res = (_b = (_a = animations[0]) == null ? void 0 : _a.currentTime) != null ? _b : null;
3140
+ var _a2, _b;
3141
+ const res = (_b = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b : null;
1846
3142
  return res !== null ? +res : null;
1847
3143
  },
1848
3144
  "setCurrentTime": (time) => {
@@ -1899,6 +3195,7 @@ function deepClone(value) {
1899
3195
  return cloned;
1900
3196
  }
1901
3197
  function generateNewIds(doc) {
3198
+ var _a, _b;
1902
3199
  const cloned = deepClone(doc);
1903
3200
  const idMap = /* @__PURE__ */ new Map();
1904
3201
  const hashRefAttrs = /* @__PURE__ */ new Set(["href", "xlink:href"]);
@@ -1924,6 +3221,8 @@ function generateNewIds(doc) {
1924
3221
  const newId = generateUniqueId();
1925
3222
  idMap.set(oldId, newId);
1926
3223
  node.id = newId;
3224
+ } else if (node.animate) {
3225
+ node.id = generateUniqueId();
1927
3226
  }
1928
3227
  if (Array.isArray(node.children)) {
1929
3228
  for (const child of node.children) {
@@ -1965,23 +3264,21 @@ function generateNewIds(doc) {
1965
3264
  value[styleProp] = replaceUrlRefs(styleValue, idMap);
1966
3265
  }
1967
3266
  }
1968
- } else if (typeof value === "object" && value !== null && key !== ANIMATE_ATTR) {
3267
+ } else if (typeof value === "object" && value !== null) {
1969
3268
  updateRefs(value);
1970
3269
  }
1971
3270
  }
1972
3271
  }
1973
3272
  collectIds(cloned);
1974
3273
  updateRefs(cloned);
1975
- const docBindings = cloned.bindings;
1976
- if (Array.isArray(docBindings)) {
1977
- for (const binding of docBindings) {
1978
- if (binding.id) {
1979
- const newId = idMap.get(binding.id);
1980
- if (newId) {
1981
- binding.id = newId;
1982
- }
1983
- }
3274
+ const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animate;
3275
+ if (docAnimate && typeof docAnimate === "object") {
3276
+ const updatedAnimate = {};
3277
+ for (const [id, anim] of Object.entries(docAnimate)) {
3278
+ const newId = (_b = idMap.get(id)) != null ? _b : id;
3279
+ updatedAnimate[newId] = anim;
1984
3280
  }
3281
+ cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animate: updatedAnimate });
1985
3282
  }
1986
3283
  return cloned;
1987
3284
  }
@@ -1992,6 +3289,9 @@ function replaceUrlRefs(value, idMap) {
1992
3289
  });
1993
3290
  }
1994
3291
  function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
3292
+ const effectsWarnings = validateNodeEffects(doc);
3293
+ for (const w of effectsWarnings) console.warn("[PxAnimator] effects shape warning:", w);
3294
+ doc = applyPlayerEffects(doc).root;
1995
3295
  const animatorConfig = getAnimatorConfig(doc) || {};
1996
3296
  animatorConfig.debug = true;
1997
3297
  let rootElement = null;
@@ -2007,14 +3307,22 @@ function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
2007
3307
  }
2008
3308
  return createAnimatorFromConfig(doc, adapter, callbacks, rootElement);
2009
3309
  }
2010
- function createAnimator(docOrUrl, adapter, callbacks, containerElement) {
2011
- if (typeof docOrUrl === "object") {
2012
- return createAnimatorImpl(docOrUrl, adapter, callbacks, containerElement);
3310
+ var PX_ANIMATOR_DATA_KEY = "data";
3311
+ function createAnimator(options) {
3312
+ const { src, data, adapter, callbacks, container } = options;
3313
+ if (data !== void 0 && src !== void 0) {
3314
+ throw new Error("createAnimator: provide either `src` or `data`, not both");
3315
+ }
3316
+ if (data === void 0 && src === void 0) {
3317
+ throw new Error("createAnimator: either `src` or `data` is required");
3318
+ }
3319
+ if (data !== void 0) {
3320
+ return createAnimatorImpl(data, adapter, callbacks, container);
2013
3321
  }
2014
3322
  let animator = null;
2015
- fetch(docOrUrl).then((res) => res.json()).then((json) => {
3323
+ fetch(src).then((res) => res.json()).then((json) => {
2016
3324
  if (isPxElementFileFormat(json)) {
2017
- animator = createAnimatorImpl(json, adapter, callbacks, containerElement);
3325
+ animator = createAnimatorImpl(json, adapter, callbacks, container);
2018
3326
  } else {
2019
3327
  console.error("Invalid animation document format");
2020
3328
  }
@@ -2054,7 +3362,7 @@ function loadTagAnimators() {
2054
3362
  if (!element[PX_ANIM_ATTR_NAME]) {
2055
3363
  const src = element.getAttribute(PX_ANIM_SRC_ATTR_NAME);
2056
3364
  if (src) {
2057
- element[PX_ANIM_ATTR_NAME] = createAnimator(src, void 0, void 0, element);
3365
+ element[PX_ANIM_ATTR_NAME] = createAnimator({ src, container: element });
2058
3366
  }
2059
3367
  }
2060
3368
  }
@@ -2064,20 +3372,259 @@ if (typeof window !== "undefined") {
2064
3372
  window["createAnimator"] = createAnimator;
2065
3373
  window["setupAnimationTriggers"] = setupAnimationTriggers;
2066
3374
  }
3375
+
3376
+ // src/effects/PlayerEffectsUtil.visualModel.ts
3377
+ var IDENTITY = [1, 0, 0, 1, 0, 0];
3378
+ function mul(m, n) {
3379
+ return [
3380
+ m[0] * n[0] + m[2] * n[1],
3381
+ m[1] * n[0] + m[3] * n[1],
3382
+ m[0] * n[2] + m[2] * n[3],
3383
+ m[1] * n[2] + m[3] * n[3],
3384
+ m[0] * n[4] + m[2] * n[5] + m[4],
3385
+ m[1] * n[4] + m[3] * n[5] + m[5]
3386
+ ];
3387
+ }
3388
+ var translateM = (x, y) => [1, 0, 0, 1, x, y];
3389
+ var scaleM = (sx, sy) => [sx, 0, 0, sy, 0, 0];
3390
+ function rotateM(deg) {
3391
+ const r = deg * Math.PI / 180;
3392
+ return [Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0];
3393
+ }
3394
+ var skewXM = (deg) => [1, 0, Math.tan(deg * Math.PI / 180), 1, 0, 0];
3395
+ var skewYM = (deg) => [1, Math.tan(deg * Math.PI / 180), 0, 1, 0, 0];
3396
+ function parseTransformString(s) {
3397
+ let m = IDENTITY;
3398
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
3399
+ let hit;
3400
+ while (hit = re.exec(s)) {
3401
+ const fn = hit[1];
3402
+ const a = hit[2].split(/[\s,]+/).filter(Boolean).map(Number);
3403
+ if (fn === "translate") m = mul(m, translateM(a[0] || 0, a[1] || 0));
3404
+ else if (fn === "scale") m = mul(m, scaleM(a[0], a.length > 1 ? a[1] : a[0]));
3405
+ else if (fn === "rotate") m = mul(m, rotateM(a[0]));
3406
+ else if (fn === "skewX") m = mul(m, skewXM(a[0]));
3407
+ else if (fn === "skewY") m = mul(m, skewYM(a[0]));
3408
+ else if (fn === "matrix") m = mul(m, [a[0], a[1], a[2], a[3], a[4], a[5]]);
3409
+ }
3410
+ return m;
3411
+ }
3412
+ function partsToMatrix(p) {
3413
+ let m = IDENTITY;
3414
+ if (p.translate) m = mul(m, translateM(p.translate[0], p.translate[1]));
3415
+ const pivot = p.origin && (p.rotate !== void 0 || p.scale);
3416
+ if (pivot) m = mul(m, translateM(p.origin[0], p.origin[1]));
3417
+ if (p.rotate !== void 0) m = mul(m, rotateM(p.rotate));
3418
+ if (p.scale) m = mul(m, scaleM(p.scale[0], p.scale[1]));
3419
+ if (pivot) m = mul(m, translateM(-p.origin[0], -p.origin[1]));
3420
+ return m;
3421
+ }
3422
+ function lerp(a, b, f) {
3423
+ return a + (b - a) * f;
3424
+ }
3425
+ function interpParts(kfs, t) {
3426
+ var _a, _b, _c, _d, _e, _f, _g;
3427
+ if (!kfs.length) return {};
3428
+ if (t <= ((_a = kfs[0].time) != null ? _a : 0)) return kfs[0].value || {};
3429
+ if (t >= ((_b = kfs[kfs.length - 1].time) != null ? _b : 0)) return kfs[kfs.length - 1].value || {};
3430
+ let i = 0;
3431
+ while (i < kfs.length - 1 && ((_c = kfs[i + 1].time) != null ? _c : 0) < t) i++;
3432
+ const a = kfs[i], b = kfs[i + 1];
3433
+ const f = (t - ((_d = a.time) != null ? _d : 0)) / (((_e = b.time) != null ? _e : 0) - ((_f = a.time) != null ? _f : 0) || 1);
3434
+ const va = a.value || {}, vb = b.value || {};
3435
+ const out = {};
3436
+ if (va.translate && vb.translate) out.translate = [lerp(va.translate[0], vb.translate[0], f), lerp(va.translate[1], vb.translate[1], f)];
3437
+ else out.translate = va.translate || vb.translate;
3438
+ if (va.rotate !== void 0 && vb.rotate !== void 0) out.rotate = lerp(va.rotate, vb.rotate, f);
3439
+ else out.rotate = (_g = va.rotate) != null ? _g : vb.rotate;
3440
+ if (va.scale && vb.scale) out.scale = [lerp(va.scale[0], vb.scale[0], f), lerp(va.scale[1], vb.scale[1], f)];
3441
+ else out.scale = va.scale || vb.scale;
3442
+ out.origin = va.origin || vb.origin;
3443
+ return out;
3444
+ }
3445
+ function evalTransformValue(v, t) {
3446
+ if (v === void 0 || v === null) return IDENTITY;
3447
+ if (typeof v === "string") return parseTransformString(v);
3448
+ if (v.keyframes) return partsToMatrix(interpParts(v.keyframes, t));
3449
+ if (v.value) return partsToMatrix(v.value);
3450
+ return IDENTITY;
3451
+ }
3452
+ function nodeMatrix(node, t) {
3453
+ if (node.animate && node.animate.transform) return evalTransformValue(node.animate.transform, t);
3454
+ if (node.transform !== void 0) return evalTransformValue(node.transform, t);
3455
+ return IDENTITY;
3456
+ }
3457
+ function evalScalar(animated, staticVal, fallback, t) {
3458
+ var _a, _b, _c, _d, _e, _f;
3459
+ if (animated && animated.keyframes && animated.keyframes.length) {
3460
+ const kfs = animated.keyframes;
3461
+ if (t <= ((_a = kfs[0].time) != null ? _a : 0)) return kfs[0].value;
3462
+ if (t >= ((_b = kfs[kfs.length - 1].time) != null ? _b : 0)) return kfs[kfs.length - 1].value;
3463
+ let i = 0;
3464
+ while (i < kfs.length - 1 && ((_c = kfs[i + 1].time) != null ? _c : 0) < t) i++;
3465
+ const a = kfs[i], b = kfs[i + 1];
3466
+ const f = (t - ((_d = a.time) != null ? _d : 0)) / (((_e = b.time) != null ? _e : 0) - ((_f = a.time) != null ? _f : 0) || 1);
3467
+ return lerp(a.value, b.value, f);
3468
+ }
3469
+ return staticVal !== void 0 ? Number(staticVal) : fallback;
3470
+ }
3471
+ var CONTAINER_TYPES = /* @__PURE__ */ new Set(["svg", "g", "symbol"]);
3472
+ var SKIP_TYPES = /* @__PURE__ */ new Set(["defs", "mask", "clipPath", "title"]);
3473
+ function buildIdMap(node, map) {
3474
+ var _a;
3475
+ if (typeof node.id === "string") map.set(node.id, node);
3476
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => buildIdMap(c, map));
3477
+ }
3478
+ function num(v) {
3479
+ return v === void 0 || v === null ? 0 : Number(v);
3480
+ }
3481
+ function round(n) {
3482
+ return Math.round(n * 100) / 100 + 0;
3483
+ }
3484
+ function geomKey(node) {
3485
+ var _a;
3486
+ switch (node.type) {
3487
+ case "rect":
3488
+ return num(node.width) + "," + num(node.height) + "," + num(node.x) + "," + num(node.y);
3489
+ case "ellipse":
3490
+ return num(node.rx) + "," + num(node.ry) + "," + num(node.cx) + "," + num(node.cy);
3491
+ case "circle":
3492
+ return num(node.r) + "," + num(node.cx) + "," + num(node.cy);
3493
+ case "path":
3494
+ return String((_a = node.d) != null ? _a : "");
3495
+ default:
3496
+ return "";
3497
+ }
3498
+ }
3499
+ function describePrimitive(node, m, t) {
3500
+ var _a, _b, _c, _d, _e;
3501
+ const fill = (_a = node.fill) != null ? _a : "";
3502
+ const stroke = (_b = node.stroke) != null ? _b : "";
3503
+ const sw = (_d = (_c = node["stroke-width"]) != null ? _c : node.strokeWidth) != null ? _d : "";
3504
+ const opacity = round(evalScalar((_e = node.animate) == null ? void 0 : _e.opacity, node.opacity, 1, t));
3505
+ const masked = node.mask ? 1 : 0;
3506
+ const mat = m.map(round).join(",");
3507
+ return node.type + "|" + geomKey(node) + "|[" + mat + "]|f:" + fill + "|s:" + stroke + "|sw:" + (num(sw) || "") + "|o:" + opacity + "|m:" + masked;
3508
+ }
3509
+ function flatten(node, parent, t, idMap, out) {
3510
+ var _a;
3511
+ const type = node.type || "";
3512
+ if (SKIP_TYPES.has(type)) return;
3513
+ const m = mul(parent, nodeMatrix(node, t));
3514
+ if (type === "use") {
3515
+ const targetId = typeof node.href === "string" ? node.href.replace(/^#/, "") : "";
3516
+ const target = idMap.get(targetId);
3517
+ const useM = mul(m, translateM(num(node.x), num(node.y)));
3518
+ if (target) flatten(target, useM, t, idMap, out);
3519
+ else out.push("UNRESOLVED_USE:#" + targetId);
3520
+ return;
3521
+ }
3522
+ if (CONTAINER_TYPES.has(type)) {
3523
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => flatten(c, m, t, idMap, out));
3524
+ return;
3525
+ }
3526
+ out.push(describePrimitive(node, m, t));
3527
+ }
3528
+ function collectSampleTimes(node, into) {
3529
+ var _a;
3530
+ into.add(0);
3531
+ const scanAnim = (anim) => {
3532
+ var _a2;
3533
+ if (!anim || typeof anim !== "object") return;
3534
+ for (const key of Object.keys(anim)) {
3535
+ const kfs = (_a2 = anim[key]) == null ? void 0 : _a2.keyframes;
3536
+ if (Array.isArray(kfs)) kfs.forEach((kf) => {
3537
+ var _a3;
3538
+ return into.add((_a3 = kf.time) != null ? _a3 : 0);
3539
+ });
3540
+ }
3541
+ };
3542
+ scanAnim(node.animate);
3543
+ if (node.transform && typeof node.transform === "object" && node.transform.keyframes) {
3544
+ node.transform.keyframes.forEach((kf) => {
3545
+ var _a2;
3546
+ return into.add((_a2 = kf.time) != null ? _a2 : 0);
3547
+ });
3548
+ }
3549
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => collectSampleTimes(c, into));
3550
+ }
3551
+ function visualModelAt(root, t) {
3552
+ const idMap = /* @__PURE__ */ new Map();
3553
+ buildIdMap(root, idMap);
3554
+ const out = [];
3555
+ flatten(root, IDENTITY, t, idMap, out);
3556
+ return out.sort();
3557
+ }
3558
+ function diffInEffect(a, b) {
3559
+ const times = /* @__PURE__ */ new Set();
3560
+ collectSampleTimes(a, times);
3561
+ collectSampleTimes(b, times);
3562
+ const diffs = [];
3563
+ for (const t of Array.from(times).sort((x, y) => x - y)) {
3564
+ const ma = visualModelAt(a, t);
3565
+ const mb = visualModelAt(b, t);
3566
+ const onlyInA = subtractMultiset(ma, mb);
3567
+ const onlyInB = subtractMultiset(mb, ma);
3568
+ if (onlyInA.length || onlyInB.length) diffs.push({ time: t, onlyInA, onlyInB });
3569
+ }
3570
+ return diffs;
3571
+ }
3572
+ function subtractMultiset(a, b) {
3573
+ const counts = /* @__PURE__ */ new Map();
3574
+ for (const x of b) counts.set(x, (counts.get(x) || 0) + 1);
3575
+ const extra = [];
3576
+ for (const x of a) {
3577
+ const c = counts.get(x) || 0;
3578
+ if (c > 0) counts.set(x, c - 1);
3579
+ else extra.push(x);
3580
+ }
3581
+ return extra;
3582
+ }
2067
3583
  // Annotate the CommonJS export names for ESM import in node:
2068
3584
  0 && (module.exports = {
2069
3585
  COLOUR_ATTR_NAMES,
3586
+ PX_ANIMATOR_DATA_KEY,
2070
3587
  PX_ANIM_ATTR_NAME,
2071
3588
  PX_ANIM_SRC_ATTR_NAME,
3589
+ PX_TRANSFORM_PART_KEYS,
3590
+ PxAnimatedSvgDocumentSchema,
3591
+ PxAnimationDefinitionSchema,
3592
+ PxAnimatorConfigSchema,
3593
+ PxAttrValueSchema,
3594
+ PxBezierPathSchema,
3595
+ PxBindingSchema,
3596
+ PxDefsSchema,
3597
+ PxEasingOrRefSchema,
3598
+ PxEffectsSchema,
3599
+ PxElementAnimationSchema,
3600
+ PxKeyframeSchema,
3601
+ PxLoopSchema,
3602
+ PxMaskedByEffectSchema,
3603
+ PxNodeBase,
3604
+ PxNodeSchema,
3605
+ PxPropertyAnimationSchema,
3606
+ PxRefEffectSchema,
3607
+ PxRepeaterEffectSchema,
3608
+ PxRetimeEffectSchema,
3609
+ PxSvgNodeExtra,
3610
+ PxTransformPartsSchema,
3611
+ PxTransformValueSchema,
3612
+ PxTransformationEffectSchema,
3613
+ PxTriggerSchema,
3614
+ PxTrimPathEffectSchema,
2072
3615
  STYLE_ATTR_NAMES,
2073
3616
  TRANSFORM_FN_NAMES,
3617
+ applyPlayerEffects,
2074
3618
  calcAnimationValues,
2075
3619
  camelCaseToKebabWordIfNeeded,
3620
+ collectSampleTimes,
2076
3621
  createAnimator,
2077
3622
  createAnimatorImpl,
2078
3623
  createBasicFrameLoopAnimator,
2079
3624
  createFrameLoopAnimator,
2080
3625
  createWebApiAnimator,
3626
+ describeSchema,
3627
+ diffInEffect,
2081
3628
  generateNewIds,
2082
3629
  getAnimatorConfig,
2083
3630
  getBindings,
@@ -2088,8 +3635,12 @@ if (typeof window !== "undefined") {
2088
3635
  isPxElementFileFormatDeep,
2089
3636
  loadTagAnimators,
2090
3637
  normalizeDocument,
3638
+ px,
2091
3639
  renderNode,
3640
+ schemaKeys,
2092
3641
  setupAnimationTriggers,
2093
- toRGBA
3642
+ toRGBA,
3643
+ validateNodeEffects,
3644
+ visualModelAt
2094
3645
  });
2095
3646
  //# sourceMappingURL=index.cjs.map