@pixodesk/svg-animator-core 1.0.43 → 1.0.45

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/internal.cjs CHANGED
@@ -85,7 +85,9 @@ __export(internal_exports, {
85
85
  materializeMotionPathInPropAnim: () => materializeMotionPathInPropAnim,
86
86
  mergeStaticTransformIntoAnimDef: () => mergeStaticTransformIntoAnimDef,
87
87
  planSchemaRelease: () => planSchemaRelease,
88
+ prepareDocumentForRender: () => prepareDocumentForRender,
88
89
  releaseLogProblems: () => releaseLogProblems,
90
+ renderPxTree: () => renderPxTree,
89
91
  reportDocumentDiagnostics: () => reportDocumentDiagnostics,
90
92
  reverseEasing: () => reverseEasing,
91
93
  sanitizeAttributeValue: () => sanitizeAttributeValue,
@@ -642,14 +644,18 @@ var PxPlaybackDirection = {
642
644
  };
643
645
  var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
644
646
  var PX_ANIM_ATTR_NAME = "_px_animator";
645
- var PxStartOn = {
647
+ var PxTriggerStart = {
646
648
  load: "load",
647
649
  mouseOver: "mouseOver",
648
650
  click: "click",
649
- scrollIntoView: "scrollIntoView",
650
- programmatic: "programmatic"
651
+ none: "none"
652
+ };
653
+ var PxOffScreenAction = {
654
+ pause: "pause",
655
+ continue: "continue",
656
+ reset: "reset"
651
657
  };
652
- var PxOutAction = {
658
+ var PxMouseOutAction = {
653
659
  continue: "continue",
654
660
  pause: "pause",
655
661
  reset: "reset",
@@ -707,10 +713,15 @@ var PxTimelineEngine = {
707
713
  var PxTimelineEngineSetting = __spreadProps(__spreadValues({}, PxTimelineEngine), {
708
714
  auto: "auto"
709
715
  });
716
+ function resolveTimelineEngine(engine) {
717
+ return engine === PxTimelineEngineSetting.js ? PxTimelineEngine.js : PxTimelineEngine.native;
718
+ }
710
719
  var PX_TRIGGER_DEFAULTS = {
711
- startOn: "load",
712
- outAction: "continue",
713
- scrollIntoViewThreshold: 0
720
+ start: "load",
721
+ offScreen: "pause",
722
+ mouseOut: "continue",
723
+ visibilityThreshold: 0.5,
724
+ visibilityDebounce: 150
714
725
  };
715
726
  var PxLoopRepeatAt = {
716
727
  /** Segment from the START; the repetition runs BEFORE the first keyframe
@@ -761,6 +772,16 @@ var PxStrokeTrimSubPaths = {
761
772
  var PX_TEXT_CONTENT_ATTR = "textContent";
762
773
  var CLASS_ATTR = "class";
763
774
  var TRANSFORM_ATTR = "transform";
775
+ var OFFSET_DISTANCE_ATTR = "offsetDistance";
776
+ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
777
+ "type",
778
+ "children",
779
+ "animator",
780
+ "meta",
781
+ "animate",
782
+ "effects",
783
+ PX_TEXT_CONTENT_ATTR
784
+ ]);
764
785
  var TRANSFORM_PART = {
765
786
  translate: "translate",
766
787
  rotate: "rotate",
@@ -829,9 +850,9 @@ function flattenAnimatorTimeline(cfg) {
829
850
  } else {
830
851
  if (timeline.duration !== void 0) flat.duration = timeline.duration;
831
852
  if (timeline.trigger !== void 0) {
832
- const _b = timeline.trigger, { finishAction } = _b, restTrigger = __objRest(_b, ["finishAction"]);
853
+ const _b = timeline.trigger, { finish } = _b, restTrigger = __objRest(_b, ["finish"]);
833
854
  if (Object.keys(restTrigger).length) flat.trigger = restTrigger;
834
- if (finishAction !== void 0) flat.resetOnFinish = finishAction === "reset";
855
+ if (finish !== void 0) flat.resetOnFinish = finish === "reset";
835
856
  }
836
857
  if (timeline.delay !== void 0) flat.delay = timeline.delay;
837
858
  if (timeline.iterations !== void 0) flat.iterations = timeline.iterations;
@@ -841,9 +862,19 @@ function flattenAnimatorTimeline(cfg) {
841
862
  flattenMemo.set(cfg, flat);
842
863
  return flat;
843
864
  }
865
+ function getDefinitions(doc) {
866
+ var _a2;
867
+ if (!doc) return void 0;
868
+ return (_a2 = getAnimatorConfig(doc)) == null ? void 0 : _a2.definitions;
869
+ }
870
+ function getBindings(doc) {
871
+ var _a2;
872
+ if (!doc) return void 0;
873
+ return (_a2 = getAnimatorConfig(doc)) == null ? void 0 : _a2.bindings;
874
+ }
844
875
 
845
876
  // src/version/PxSchemaVersion.ts
846
- var PX_WIRE_SCHEMA_VERSION = "1.1";
877
+ var PX_WIRE_SCHEMA_VERSION = "1.2";
847
878
 
848
879
  // src/version/PxWireVersion.ts
849
880
  var VERSION_RE = /^(\d+)\.(\d+)(?:\.(\d+))?$/;
@@ -936,13 +967,15 @@ var PxElementAnimationSchema = implementsInterface()(px.union([
936
967
  PxAnimationDefinitionSchema
937
968
  ]));
938
969
  var PxTriggerSchema = implementsInterface()(px.object({
939
- startOn: px.enum([PxStartOn.load, PxStartOn.mouseOver, PxStartOn.click, PxStartOn.scrollIntoView, PxStartOn.programmatic], PX_TRIGGER_DEFAULTS.startOn).optional(),
940
- outAction: px.enum([PxOutAction.continue, PxOutAction.pause, PxOutAction.reset, PxOutAction.reverse], PX_TRIGGER_DEFAULTS.outAction).optional(),
970
+ start: px.enum([PxTriggerStart.load, PxTriggerStart.mouseOver, PxTriggerStart.click, PxTriggerStart.none], PX_TRIGGER_DEFAULTS.start).optional(),
971
+ offScreen: px.enum([PxOffScreenAction.pause, PxOffScreenAction.continue, PxOffScreenAction.reset], PX_TRIGGER_DEFAULTS.offScreen).optional(),
972
+ mouseOut: px.enum([PxMouseOutAction.continue, PxMouseOutAction.pause, PxMouseOutAction.reset, PxMouseOutAction.reverse], PX_TRIGGER_DEFAULTS.mouseOut).optional(),
941
973
  // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
942
- // `fill`) or `'reset'` (snap back to the start state). Pairs with `outAction` ("what
943
- // happens when the trigger condition ends"); both end-of-life knobs now read alike.
944
- finishAction: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
945
- scrollIntoViewThreshold: px.number().optional()
974
+ // `fill`) or `'reset'` (snap back to the start state). One of four occasion keys
975
+ // (`start`, `offScreen`, `mouseOut`, `finish`), all named the same way.
976
+ finish: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
977
+ visibilityThreshold: px.number().optional(),
978
+ visibilityDebounce: px.number().optional()
946
979
  }));
947
980
  var PxGlyphSchema = implementsInterface()(px.object({
948
981
  width: px.number(),
@@ -1399,6 +1432,11 @@ function interpolateBezier(path1, path2, progress) {
1399
1432
  }
1400
1433
  return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
1401
1434
  }
1435
+ function remap(value, inMin, inMax, outMin, outMax) {
1436
+ if (inMax === inMin) return outMin;
1437
+ const t = (value - inMin) / (inMax - inMin);
1438
+ return outMin + t * (outMax - outMin);
1439
+ }
1402
1440
  function solveCubicBezierX(p1x, p2x, x) {
1403
1441
  if (x <= 0) return 0;
1404
1442
  if (x >= 1) return 1;
@@ -1503,6 +1541,43 @@ function toRGBA(color) {
1503
1541
  const b = Math.round(color[2] * 255);
1504
1542
  return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
1505
1543
  }
1544
+ function parseRgba(s) {
1545
+ var _a2;
1546
+ const inner = (_a2 = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a2[1];
1547
+ if (!inner) throw new Error("Invalid rgb/rgba format");
1548
+ const parts = inner.split(",").map((v) => +v.trim());
1549
+ return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
1550
+ }
1551
+ function parseHex(s) {
1552
+ const hex = s.slice(1);
1553
+ const isShort = hex.length <= 4;
1554
+ const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
1555
+ const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
1556
+ const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
1557
+ const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
1558
+ const result = [
1559
+ parseInt(r, 16) / 255,
1560
+ parseInt(g, 16) / 255,
1561
+ parseInt(b, 16) / 255
1562
+ ];
1563
+ if (a !== null) {
1564
+ result.push(parseInt(a, 16) / 255);
1565
+ }
1566
+ return result;
1567
+ }
1568
+ function parseColor(s) {
1569
+ if (!s) return void 0;
1570
+ if (Array.isArray(s)) return s;
1571
+ if (typeof s !== "string") return void 0;
1572
+ if (s.startsWith("#")) {
1573
+ return parseHex(s);
1574
+ } else if (s.startsWith("rgb")) {
1575
+ return parseRgba(s);
1576
+ } else {
1577
+ console.warn("Unsupported color format: " + s);
1578
+ }
1579
+ return void 0;
1580
+ }
1506
1581
  var PX_COLOR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
1507
1582
  var PX_TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
1508
1583
  var PX_PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
@@ -1563,6 +1638,9 @@ var PX_DEFAULT_DURATION_MS = 1e3;
1563
1638
  function kebabToCamelCaseWord(kebab) {
1564
1639
  return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
1565
1640
  }
1641
+ function isCamelCaseWord(word) {
1642
+ return !word.includes("-") && /[a-z][A-Z]/.test(word);
1643
+ }
1566
1644
  var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
1567
1645
  // Transform/positioning
1568
1646
  "viewBox",
@@ -1718,6 +1796,38 @@ function invertEasing(easing) {
1718
1796
  return cubicBezier(flipped);
1719
1797
  }
1720
1798
 
1799
+ // src/animation/PxStaticTransformMerge.ts
1800
+ function mergeStaticTransformIntoAnimDef(animDef, staticTransform) {
1801
+ if (!animDef) return animDef;
1802
+ const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
1803
+ if (!staticParts || !Object.keys(staticParts).length) return animDef;
1804
+ const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues(__spreadValues({}, staticParts), v) : v;
1805
+ const transformAnim = animDef[TRANSFORM_ATTR];
1806
+ if (transformAnim && typeof transformAnim === "object") {
1807
+ const anim = transformAnim;
1808
+ if (Array.isArray(anim.keyframes)) {
1809
+ const out = __spreadProps(__spreadValues({}, anim), {
1810
+ keyframes: anim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: mergeKfValue(kf.value) }))
1811
+ });
1812
+ if (out.value !== void 0) out.value = mergeKfValue(out.value);
1813
+ return __spreadProps(__spreadValues({}, animDef), { transform: out });
1814
+ }
1815
+ return animDef;
1816
+ }
1817
+ const channels = Object.keys(animDef).filter((k) => PX_TRANSFORM_FN_NAMES.has(k));
1818
+ if (channels.length !== 1) return animDef;
1819
+ const ch = channels[0];
1820
+ const chAnim = animDef[ch];
1821
+ if (!chAnim || typeof chAnim !== "object" || !Array.isArray(chAnim.keyframes)) return animDef;
1822
+ const lifted = __spreadProps(__spreadValues({}, chAnim), {
1823
+ keyframes: chAnim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: __spreadProps(__spreadValues({}, staticParts), { [ch]: kf.value }) }))
1824
+ });
1825
+ if (lifted.value !== void 0) lifted.value = __spreadProps(__spreadValues({}, staticParts), { [ch]: lifted.value });
1826
+ const rest = __spreadValues({}, animDef);
1827
+ delete rest[ch];
1828
+ return __spreadProps(__spreadValues({}, rest), { transform: lifted });
1829
+ }
1830
+
1721
1831
  // src/materialize/PxMotionPath.ts
1722
1832
  function getKfTranslate(kf) {
1723
1833
  const v = keyframeValue(kf);
@@ -1769,6 +1879,33 @@ function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
1769
1879
  byNext.set(nextKf, entry);
1770
1880
  return entry;
1771
1881
  }
1882
+ function evaluateMotionPathSegment(prevKf, nextKf, prevPos, nextPos, localProgress, autoOrient) {
1883
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
1884
+ const t = seg.totalArc === 0 ? localProgress : tFromArcFraction(seg.lut, localProgress);
1885
+ const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
1886
+ if (!autoOrient) return { translate: point };
1887
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
1888
+ const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
1889
+ return { translate: point, rotateDeg };
1890
+ }
1891
+ function tFromArcFraction(lut, arcFrac) {
1892
+ const total = lut.ds[lut.ds.length - 1];
1893
+ const target = arcFrac * total;
1894
+ const { ts, ds } = lut;
1895
+ const last = ds.length - 1;
1896
+ if (target <= 0) return ts[0];
1897
+ if (target >= ds[last]) return ts[last];
1898
+ let lo = 1, hi = last;
1899
+ while (lo < hi) {
1900
+ const mid = lo + hi >>> 1;
1901
+ if (ds[mid] < target) lo = mid + 1;
1902
+ else hi = mid;
1903
+ }
1904
+ const dPrev = ds[hi - 1];
1905
+ const span = ds[hi] - dPrev;
1906
+ const frac = span > 0 ? (target - dPrev) / span : 0;
1907
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
1908
+ }
1772
1909
  var DEFAULT_FLATNESS_TOL = 0.5;
1773
1910
  var DEFAULT_ROTATION_TOL = 5;
1774
1911
  var DEFAULT_MAX_SAMPLES = 32;
@@ -2041,9 +2178,211 @@ function perpDist(q, pA, pB) {
2041
2178
  const cross = (q[0] - pA[0]) * dy - (q[1] - pA[1]) * dx;
2042
2179
  return Math.abs(cross) / Math.sqrt(len2);
2043
2180
  }
2181
+ function materializeMotionPathsInTree(root, opts) {
2182
+ const out = walkAndMaterialize(root, opts);
2183
+ return out != null ? out : root;
2184
+ }
2185
+ function walkAndMaterialize(node, opts) {
2186
+ let newChildren;
2187
+ if (node.children) {
2188
+ for (let i = 0; i < node.children.length; i++) {
2189
+ const ch = node.children[i];
2190
+ const ret = walkAndMaterialize(ch, opts);
2191
+ if (ret !== null) {
2192
+ if (!newChildren) newChildren = node.children.slice();
2193
+ newChildren[i] = ret;
2194
+ }
2195
+ }
2196
+ }
2197
+ let newAnimate;
2198
+ const animBucket = node.animate;
2199
+ if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
2200
+ const animDef = mergeStaticTransformIntoAnimDef(animBucket, node.transform);
2201
+ const transformAnim = animDef.transform;
2202
+ if (transformAnim && typeof transformAnim === "object" && propAnimIsMotionPath(transformAnim)) {
2203
+ const materialized = materializeMotionPathInPropAnim(transformAnim, opts);
2204
+ if (materialized !== transformAnim) {
2205
+ newAnimate = __spreadProps(__spreadValues({}, animDef), { transform: materialized });
2206
+ }
2207
+ }
2208
+ }
2209
+ if (!newChildren && !newAnimate) return null;
2210
+ const cloned = __spreadValues({}, node);
2211
+ if (newChildren) cloned.children = newChildren;
2212
+ if (newAnimate) cloned.animate = newAnimate;
2213
+ return cloned;
2214
+ }
2044
2215
 
2045
2216
  // src/animation/PxDefinitions.ts
2046
2217
  var PX_LOOP_JUMP_SHIFT_MS = 1;
2218
+ function deepEqualValue(a, b) {
2219
+ if (a === b) return true;
2220
+ if (typeof a !== typeof b || a === null || b === null || typeof a !== "object") return false;
2221
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
2222
+ const ka = Object.keys(a);
2223
+ const kb = Object.keys(b);
2224
+ if (ka.length !== kb.length) return false;
2225
+ return ka.every((k) => deepEqualValue(a[k], b[k]));
2226
+ }
2227
+ function parsePathCommands(d) {
2228
+ const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
2229
+ const commands = [];
2230
+ let currentCommand = null;
2231
+ for (const token of tokens) {
2232
+ if (/[MLCZmlcz]/.test(token)) {
2233
+ currentCommand = { type: token, values: [] };
2234
+ commands.push(currentCommand);
2235
+ } else if (currentCommand) {
2236
+ const value = +token;
2237
+ currentCommand.values.push(Number.isNaN(value) ? 0 : value);
2238
+ }
2239
+ }
2240
+ return commands;
2241
+ }
2242
+ function parseSvgPathToBezier(d) {
2243
+ const res = [];
2244
+ let currentPath;
2245
+ const commands = parsePathCommands(d);
2246
+ for (const command of commands) {
2247
+ const type = command.type;
2248
+ const values = command.values;
2249
+ if (type === "M" || type === "m") {
2250
+ const x = values[0] || 0;
2251
+ const y = values[1] || 0;
2252
+ currentPath = {
2253
+ v: [[x, y]],
2254
+ i: [[x, y]],
2255
+ o: [[x, y]],
2256
+ c: false
2257
+ };
2258
+ res.push(currentPath);
2259
+ continue;
2260
+ }
2261
+ if (!currentPath) {
2262
+ currentPath = {
2263
+ v: [[0, 0]],
2264
+ i: [[0, 0]],
2265
+ o: [[0, 0]],
2266
+ c: false
2267
+ };
2268
+ res.push(currentPath);
2269
+ }
2270
+ if (type === "L") {
2271
+ const x = values[0] || 0;
2272
+ const y = values[1] || 0;
2273
+ currentPath.v.push([x, y]);
2274
+ currentPath.i.push([x, y]);
2275
+ currentPath.o.push([x, y]);
2276
+ } else if (type === "C") {
2277
+ const outX = values[0] || 0;
2278
+ const outY = values[1] || 0;
2279
+ const inX2 = values[2] || 0;
2280
+ const inY2 = values[3] || 0;
2281
+ const x2 = values[4] || 0;
2282
+ const y2 = values[5] || 0;
2283
+ currentPath.o[currentPath.o.length - 1] = [outX, outY];
2284
+ currentPath.v.push([x2, y2]);
2285
+ currentPath.i.push([inX2, inY2]);
2286
+ currentPath.o.push([x2, y2]);
2287
+ } else if (type === "Z" || type === "z") {
2288
+ currentPath.c = true;
2289
+ } else {
2290
+ console.warn('Unsupported path command "' + type + '"');
2291
+ }
2292
+ }
2293
+ return res;
2294
+ }
2295
+ function extractPathData(str2) {
2296
+ if (str2.startsWith("path(") && str2.endsWith(")")) {
2297
+ return str2.slice(5, -1);
2298
+ }
2299
+ if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str2)) {
2300
+ return str2;
2301
+ }
2302
+ return void 0;
2303
+ }
2304
+ function isPathString(value) {
2305
+ return typeof value === "string" && extractPathData(value) !== void 0;
2306
+ }
2307
+ function normalizePathValue(value) {
2308
+ if (value && typeof value === "object" && typeof value.pathData === "string") {
2309
+ const d = extractPathData(value.pathData);
2310
+ return d ? { paths: parseSvgPathToBezier(d) } : value;
2311
+ }
2312
+ if (value && typeof value === "object" && "paths" in value) {
2313
+ const pathsArray = value.paths;
2314
+ if (Array.isArray(pathsArray) && pathsArray.length > 0) {
2315
+ if (isPathString(pathsArray[0])) {
2316
+ const paths = [];
2317
+ for (const pathStr2 of pathsArray) {
2318
+ const d = extractPathData(pathStr2);
2319
+ if (d) {
2320
+ paths.push(...parseSvgPathToBezier(d));
2321
+ }
2322
+ }
2323
+ return { paths };
2324
+ }
2325
+ }
2326
+ return value;
2327
+ }
2328
+ if (Array.isArray(value)) {
2329
+ if (value.length > 0 && isPathString(value[0])) {
2330
+ const paths = [];
2331
+ for (const pathStr2 of value) {
2332
+ const d = extractPathData(pathStr2);
2333
+ if (d) {
2334
+ paths.push(...parseSvgPathToBezier(d));
2335
+ }
2336
+ }
2337
+ return { paths };
2338
+ }
2339
+ return { paths: value };
2340
+ }
2341
+ if (isPathString(value)) {
2342
+ const d = extractPathData(value);
2343
+ return { paths: parseSvgPathToBezier(d) };
2344
+ }
2345
+ return value;
2346
+ }
2347
+ function resolveEasing(easing, defs) {
2348
+ var _a2;
2349
+ if (!easing) return void 0;
2350
+ if (Array.isArray(easing)) {
2351
+ return easing;
2352
+ }
2353
+ if ((_a2 = defs == null ? void 0 : defs.easings) == null ? void 0 : _a2[easing]) {
2354
+ return defs.easings[easing];
2355
+ }
2356
+ console.warn("Unknown easing name: " + easing);
2357
+ return void 0;
2358
+ }
2359
+ function resolveAnimation(animRef, defs) {
2360
+ var _a2;
2361
+ if (typeof animRef === "string") {
2362
+ const resolved = (_a2 = defs == null ? void 0 : defs.animations) == null ? void 0 : _a2[animRef];
2363
+ if (!resolved) {
2364
+ console.warn("Unknown animation name: " + animRef);
2365
+ }
2366
+ return resolved;
2367
+ }
2368
+ return animRef;
2369
+ }
2370
+ function resolveElementAnimation(animate, defs) {
2371
+ if (!animate) return [];
2372
+ const results = [];
2373
+ if (typeof animate === "string") {
2374
+ const resolved = resolveAnimation(animate, defs);
2375
+ if (resolved) results.push(resolved);
2376
+ } else if (Array.isArray(animate)) {
2377
+ for (const item of animate) {
2378
+ const resolved = resolveAnimation(item, defs);
2379
+ if (resolved) results.push(resolved);
2380
+ }
2381
+ } else {
2382
+ results.push(animate);
2383
+ }
2384
+ return results;
2385
+ }
2047
2386
  function interpolateValue(propName, a, b, t) {
2048
2387
  var _a2, _b;
2049
2388
  if (propName === "d") {
@@ -2082,35 +2421,498 @@ function interpolateTransformParts(a, b, t) {
2082
2421
  }
2083
2422
  return out;
2084
2423
  }
2085
- function mergeStaticTransformIntoAnimDef(animDef, staticTransform) {
2086
- if (!animDef) return animDef;
2087
- const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
2088
- if (!staticParts || !Object.keys(staticParts).length) return animDef;
2089
- const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues(__spreadValues({}, staticParts), v) : v;
2090
- const transformAnim = animDef[TRANSFORM_ATTR];
2091
- if (transformAnim && typeof transformAnim === "object") {
2092
- const anim = transformAnim;
2093
- if (Array.isArray(anim.keyframes)) {
2094
- const out = __spreadProps(__spreadValues({}, anim), {
2095
- keyframes: anim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: mergeKfValue(kf.value) }))
2096
- });
2097
- if (out.value !== void 0) out.value = mergeKfValue(out.value);
2098
- return __spreadProps(__spreadValues({}, animDef), { transform: out });
2424
+ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2425
+ var _a2, _b, _c, _d, _e;
2426
+ const totalIntervals = keyframes.length - 1;
2427
+ const segCount = clamp((_a2 = loop.segmentCount) != null ? _a2 : totalIntervals, 1, totalIntervals);
2428
+ let segKfs;
2429
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2430
+ segKfs = keyframes.slice(0, segCount + 1);
2431
+ } else {
2432
+ segKfs = keyframes.slice(totalIntervals - segCount);
2433
+ }
2434
+ const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2435
+ const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2436
+ let fillStart, fillEnd;
2437
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2438
+ fillStart = 0;
2439
+ fillEnd = firstT;
2440
+ } else {
2441
+ fillStart = lastT;
2442
+ fillEnd = duration;
2443
+ }
2444
+ const fillDuration = fillEnd - fillStart;
2445
+ if (fillDuration <= 0) return keyframes;
2446
+ const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
2447
+ const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
2448
+ const segDuration = segEndT - segStartT;
2449
+ if (segDuration <= 0) return keyframes;
2450
+ const template = segKfs.map((kf) => ({
2451
+ relT: (kf.t - segStartT) / segDuration,
2452
+ v: kf.v,
2453
+ e: kf.e,
2454
+ tangentIn: keyframeTangentIn(kf),
2455
+ tangentOut: keyframeTangentOut(kf)
2456
+ }));
2457
+ const fullReps = Math.floor(fillDuration / segDuration);
2458
+ const remainder = fillDuration - fullReps * segDuration;
2459
+ const partialFraction = remainder / segDuration;
2460
+ const looped = [];
2461
+ const separateBoundary = loop.repeatAt !== PxLoopRepeatAt.start;
2462
+ const originalTerminalKf = keyframes[keyframes.length - 1];
2463
+ let terminalEasingOverride;
2464
+ let hasTerminalEasingOverride = false;
2465
+ function appendRep(repStart, isReversed, partial) {
2466
+ var _a3;
2467
+ let entries;
2468
+ if (isReversed) {
2469
+ entries = [];
2470
+ for (let i = template.length - 1; i >= 0; i--) {
2471
+ entries.push({
2472
+ relT: 1 - template[i].relT,
2473
+ v: template[i].v,
2474
+ // Easing for reversed transition: use reversed easing from the forward "from" keyframe
2475
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
2476
+ // Reversed traversal swaps each vertex's in/out spatial tangents
2477
+ // (geometry is identical, walked backwards), so curvature and
2478
+ // auto-orientation survive the reversed rep.
2479
+ tangentIn: template[i].tangentOut,
2480
+ tangentOut: template[i].tangentIn
2481
+ });
2482
+ }
2483
+ } else {
2484
+ entries = template;
2485
+ }
2486
+ const cutRelT = partial !== void 0 ? partial : 1;
2487
+ for (let i = 0; i < entries.length; i++) {
2488
+ const entry = entries[i];
2489
+ if (entry.relT > cutRelT + 1e-9) {
2490
+ const prev = entries[i - 1];
2491
+ const intervalSpan = entry.relT - prev.relT;
2492
+ const localFrac = (cutRelT - prev.relT) / intervalSpan;
2493
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2494
+ const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
2495
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
2496
+ if (looped.length > 0 && prev.relT <= cutRelT) {
2497
+ looped[looped.length - 1].e = leftEasing;
2498
+ }
2499
+ looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2500
+ return;
2501
+ }
2502
+ const prevKf = looped.length > 0 ? looped[looped.length - 1] : originalTerminalKf;
2503
+ const isBoundary = separateBoundary && i === 0 && prevKf !== void 0 && Math.abs(((_a3 = prevKf.t) != null ? _a3 : 0) - (repStart + entry.relT * segDuration)) < 1e-9;
2504
+ if (isBoundary) {
2505
+ if (deepEqualValue(prevKf.v, entry.v)) {
2506
+ if (looped.length > 0) {
2507
+ prevKf.e = entry.e;
2508
+ prevKf.tangentOut = entry.tangentOut;
2509
+ } else {
2510
+ terminalEasingOverride = entry.e;
2511
+ hasTerminalEasingOverride = true;
2512
+ }
2513
+ continue;
2514
+ }
2515
+ if (looped.length > 0) {
2516
+ delete prevKf.tangentIn;
2517
+ delete prevKf.tangentOut;
2518
+ }
2519
+ }
2520
+ const pushed = {
2521
+ t: repStart + entry.relT * segDuration + (isBoundary ? PX_LOOP_JUMP_SHIFT_MS : 0),
2522
+ v: entry.v,
2523
+ e: i < entries.length - 1 ? entry.e : void 0
2524
+ };
2525
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
2526
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
2527
+ looped.push(pushed);
2099
2528
  }
2100
- return animDef;
2101
2529
  }
2102
- const channels = Object.keys(animDef).filter((k) => PX_TRANSFORM_FN_NAMES.has(k));
2103
- if (channels.length !== 1) return animDef;
2104
- const ch = channels[0];
2105
- const chAnim = animDef[ch];
2106
- if (!chAnim || typeof chAnim !== "object" || !Array.isArray(chAnim.keyframes)) return animDef;
2107
- const lifted = __spreadProps(__spreadValues({}, chAnim), {
2108
- keyframes: chAnim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: __spreadProps(__spreadValues({}, staticParts), { [ch]: kf.value }) }))
2530
+ function appendRepTail(repStart, isReversed, tailFraction) {
2531
+ let entries;
2532
+ if (isReversed) {
2533
+ entries = [];
2534
+ for (let i = template.length - 1; i >= 0; i--) {
2535
+ entries.push({
2536
+ relT: 1 - template[i].relT,
2537
+ v: template[i].v,
2538
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
2539
+ tangentIn: template[i].tangentOut,
2540
+ tangentOut: template[i].tangentIn
2541
+ });
2542
+ }
2543
+ } else {
2544
+ entries = template;
2545
+ }
2546
+ const startRelT = 1 - tailFraction;
2547
+ for (let i = 0; i < entries.length; i++) {
2548
+ const entry = entries[i];
2549
+ if (entry.relT < startRelT - 1e-9) continue;
2550
+ const prev = entries[i - 1];
2551
+ if (prev && prev.relT < startRelT - 1e-9 && entry.relT > startRelT + 1e-9) {
2552
+ const intervalSpan = entry.relT - prev.relT;
2553
+ const localFrac = (startRelT - prev.relT) / intervalSpan;
2554
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2555
+ const startValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
2556
+ const { right: rightEasing } = splitEasing(prev.e, localFrac);
2557
+ looped.push({ t: repStart, v: startValue, e: rightEasing });
2558
+ }
2559
+ const pushed = {
2560
+ t: repStart + (entry.relT - startRelT) * segDuration,
2561
+ v: entry.v,
2562
+ e: i < entries.length - 1 ? entry.e : void 0
2563
+ };
2564
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
2565
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
2566
+ looped.push(pushed);
2567
+ }
2568
+ }
2569
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2570
+ if (partialFraction > 1e-9) {
2571
+ const isReversed = loop.direction === PxLoopDirection.alternate && fullReps % 2 === 0;
2572
+ appendRepTail(fillStart, isReversed, partialFraction);
2573
+ }
2574
+ for (let rep = 0; rep < fullReps; rep++) {
2575
+ const distFromBoundary = fullReps - 1 - rep;
2576
+ const isReversed = loop.direction === PxLoopDirection.alternate && distFromBoundary % 2 === 0;
2577
+ const repStart = fillStart + remainder + rep * segDuration;
2578
+ appendRep(repStart, isReversed);
2579
+ }
2580
+ } else {
2581
+ for (let rep = 0; rep < fullReps; rep++) {
2582
+ const isReversed = loop.direction === PxLoopDirection.alternate && rep % 2 === 0;
2583
+ const repStart = fillStart + rep * segDuration;
2584
+ appendRep(repStart, isReversed);
2585
+ }
2586
+ if (partialFraction > 1e-9) {
2587
+ const isReversed = loop.direction === PxLoopDirection.alternate && fullReps % 2 === 0;
2588
+ const repStart = fillStart + fullReps * segDuration;
2589
+ appendRep(repStart, isReversed, partialFraction);
2590
+ }
2591
+ }
2592
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2593
+ return [...looped, ...keyframes];
2594
+ } else {
2595
+ if (hasTerminalEasingOverride && keyframes.length > 0) {
2596
+ const head = keyframes.slice(0, -1);
2597
+ const tail = __spreadProps(__spreadValues({}, keyframes[keyframes.length - 1]), { e: terminalEasingOverride });
2598
+ return [...head, tail, ...looped];
2599
+ }
2600
+ return [...keyframes, ...looped];
2601
+ }
2602
+ }
2603
+ function normalizeKeyframes(propName, propAnim, duration, defs) {
2604
+ var _a2;
2605
+ const keyframes = propAnim.keyframes || [];
2606
+ const normalized = [];
2607
+ for (const kf of keyframes) {
2608
+ const timePct = keyframeTime(kf);
2609
+ let value = keyframeValue(kf);
2610
+ const easing = keyframeEasing(kf);
2611
+ if (propName === "d") {
2612
+ value = normalizePathValue(value);
2613
+ }
2614
+ const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2615
+ if (PX_COLOR_ATTR_NAMES.has(propNameKebab)) {
2616
+ value = (_a2 = parseColor(value)) != null ? _a2 : value;
2617
+ }
2618
+ const normKf = {
2619
+ t: timePct,
2620
+ v: value,
2621
+ e: resolveEasing(easing, defs)
2622
+ };
2623
+ const tIn = keyframeTangentIn(kf);
2624
+ const tOut = keyframeTangentOut(kf);
2625
+ if (tIn) normKf.tangentIn = tIn;
2626
+ if (tOut) normKf.tangentOut = tOut;
2627
+ normalized.push(normKf);
2628
+ }
2629
+ normalized.sort((a, b) => {
2630
+ var _a3, _b;
2631
+ return ((_a3 = a.t) != null ? _a3 : 0) - ((_b = b.t) != null ? _b : 0);
2109
2632
  });
2110
- if (lifted.value !== void 0) lifted.value = __spreadProps(__spreadValues({}, staticParts), { [ch]: lifted.value });
2111
- const rest = __spreadValues({}, animDef);
2112
- delete rest[ch];
2113
- return __spreadProps(__spreadValues({}, rest), { transform: lifted });
2633
+ const loopRaw = propAnim.loop;
2634
+ const loop = loopRaw === true ? {} : loopRaw || void 0;
2635
+ if (loop && normalized.length >= 2) {
2636
+ return expandLoopKeyframes(propName, normalized, loop, duration);
2637
+ }
2638
+ return normalized;
2639
+ }
2640
+ function mergeAnimationDefinitions(animations) {
2641
+ const merged = {};
2642
+ for (const anim of animations) {
2643
+ for (const [prop, propAnim] of Object.entries(anim)) {
2644
+ merged[prop] = propAnim;
2645
+ }
2646
+ }
2647
+ return merged;
2648
+ }
2649
+ function materializeInternalLoopsInPropAnim(propName, propAnim, duration) {
2650
+ const loopRaw = propAnim.loop;
2651
+ if (loopRaw === void 0 || loopRaw === null || loopRaw === false) return propAnim;
2652
+ const loop = loopRaw === true ? {} : loopRaw;
2653
+ const rawKfs = propAnim.keyframes;
2654
+ if (!Array.isArray(rawKfs) || rawKfs.length < 2) return propAnim;
2655
+ const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2656
+ const isColor = PX_COLOR_ATTR_NAMES.has(propNameKebab);
2657
+ const kfs = rawKfs.map((kf) => {
2658
+ var _a2;
2659
+ const t = keyframeTime(kf);
2660
+ let v = keyframeValue(kf);
2661
+ if (propName === "d") v = normalizePathValue(v);
2662
+ if (isColor) v = (_a2 = parseColor(v)) != null ? _a2 : v;
2663
+ const e = keyframeEasing(kf);
2664
+ const out2 = { t, v, e };
2665
+ const tIn = keyframeTangentIn(kf);
2666
+ const tOut = keyframeTangentOut(kf);
2667
+ if (tIn) out2.tangentIn = tIn;
2668
+ if (tOut) out2.tangentOut = tOut;
2669
+ return out2;
2670
+ });
2671
+ const expanded = expandLoopKeyframes(propName, kfs, loop, duration);
2672
+ const out = { keyframes: expanded };
2673
+ if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2674
+ return out;
2675
+ }
2676
+ function materializeInternalLoopsInTree(root, duration) {
2677
+ const ret = walkAndMaterializeLoops(root, duration);
2678
+ return ret != null ? ret : root;
2679
+ }
2680
+ function walkAndMaterializeLoops(node, duration) {
2681
+ let newChildren;
2682
+ if (node.children) {
2683
+ for (let i = 0; i < node.children.length; i++) {
2684
+ const ret = walkAndMaterializeLoops(node.children[i], duration);
2685
+ if (ret !== null) {
2686
+ if (!newChildren) newChildren = node.children.slice();
2687
+ newChildren[i] = ret;
2688
+ }
2689
+ }
2690
+ }
2691
+ let newAnimate;
2692
+ const animBucket = node.animate;
2693
+ if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
2694
+ const animDef = animBucket;
2695
+ for (const propName of Object.keys(animDef)) {
2696
+ const propAnim = animDef[propName];
2697
+ const materialized = materializeInternalLoopsInPropAnim(propName, propAnim, duration);
2698
+ if (materialized !== propAnim) {
2699
+ if (!newAnimate) newAnimate = __spreadValues({}, animDef);
2700
+ newAnimate[propName] = materialized;
2701
+ }
2702
+ }
2703
+ }
2704
+ if (!newChildren && !newAnimate) return null;
2705
+ const cloned = __spreadValues({}, node);
2706
+ if (newChildren) cloned.children = newChildren;
2707
+ if (newAnimate) cloned.animate = newAnimate;
2708
+ return cloned;
2709
+ }
2710
+ var _elementIdCounter = 0;
2711
+ function generateElementId() {
2712
+ return "_px_el_" + ++_elementIdCounter;
2713
+ }
2714
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxTimelineEngine.native) {
2715
+ const normalized = {};
2716
+ for (const [propName, propAnim] of Object.entries(animDef)) {
2717
+ if (propName === "transform" && propAnim.alongPathMode === "offsetPath" && animDef["offsetDistance"] !== void 0) {
2718
+ continue;
2719
+ }
2720
+ const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2721
+ if (normalizedKfs.length > 0) {
2722
+ const out = { keyframes: normalizedKfs };
2723
+ if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2724
+ if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2725
+ normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materializeMotionPathInPropAnim(out) : out;
2726
+ }
2727
+ }
2728
+ return normalized;
2729
+ }
2730
+ function normalizeBindings(doc, engine = PxTimelineEngine.native) {
2731
+ const animatorConfig = getAnimatorConfig(doc) || {};
2732
+ const defs = getDefinitions(doc);
2733
+ const duration = animatorConfig.duration || 1e3;
2734
+ const bindings = [];
2735
+ const processAnimation = (id, animDefs, staticTransform) => {
2736
+ if (animDefs.length === 0) return null;
2737
+ const merged = mergeStaticTransformIntoAnimDef(mergeAnimationDefinitions(animDefs), staticTransform);
2738
+ const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);
2739
+ if (Object.keys(normalizedAnim).length === 0) return null;
2740
+ return {
2741
+ id,
2742
+ animate: normalizedAnim
2743
+ };
2744
+ };
2745
+ const docBindings = getBindings(doc);
2746
+ if (docBindings) {
2747
+ for (const binding of docBindings) {
2748
+ const id = binding.target.startsWith("#") ? binding.target.slice(1) : binding.target;
2749
+ const animDefs = binding.animateWith.map((name) => resolveAnimation(name, defs)).filter((d) => !!d);
2750
+ const normalized = processAnimation(id, animDefs);
2751
+ if (normalized) bindings.push(normalized);
2752
+ }
2753
+ }
2754
+ const processNode = (node) => {
2755
+ const inlineAnim = node.animate;
2756
+ if (inlineAnim && Object.keys(inlineAnim).length > 0) {
2757
+ const nodeId = node.id || generateElementId();
2758
+ node.id = nodeId;
2759
+ const normalized = processAnimation(nodeId, resolveElementAnimation(inlineAnim, defs), node.transform);
2760
+ if (normalized) bindings.push(normalized);
2761
+ }
2762
+ if (node.children) {
2763
+ for (let i = 0; i < node.children.length; i++) {
2764
+ processNode(node.children[i]);
2765
+ }
2766
+ }
2767
+ };
2768
+ if (doc.children) {
2769
+ for (let i = 0; i < doc.children.length; i++) {
2770
+ processNode(doc.children[i]);
2771
+ }
2772
+ }
2773
+ return bindings;
2774
+ }
2775
+ function getKeyframesPair(keyframes, progress) {
2776
+ var _a2, _b;
2777
+ const last = keyframes.length - 1;
2778
+ let prevKf = keyframes[0];
2779
+ let nextKf = keyframes[last > 0 ? 1 : 0];
2780
+ for (let j = 0; j < last; j++) {
2781
+ const aOff = (_a2 = keyframes[j].t) != null ? _a2 : 0;
2782
+ const bOff = (_b = keyframes[j + 1].t) != null ? _b : 0;
2783
+ if (aOff <= progress && progress <= bOff) {
2784
+ prevKf = keyframes[j];
2785
+ nextKf = keyframes[j + 1];
2786
+ break;
2787
+ }
2788
+ if (progress > bOff && j === last - 1) {
2789
+ prevKf = keyframes[last > 0 ? last - 1 : 0];
2790
+ nextKf = keyframes[last];
2791
+ }
2792
+ }
2793
+ return { prevKf, nextKf };
2794
+ }
2795
+ function calcPropertyValue(propName, propAnim, progress) {
2796
+ var _a2, _b, _c, _d, _e;
2797
+ const keyframes = propAnim.keyframes || [];
2798
+ if (keyframes.length === 0) return null;
2799
+ const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
2800
+ let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a2 = prevKf.t) != null ? _a2 : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
2801
+ localProgress = clamp(localProgress, 0, 1);
2802
+ const easing = keyframeEasing(prevKf);
2803
+ if (easing && Array.isArray(easing)) {
2804
+ try {
2805
+ localProgress = cubicBezier(easing)(localProgress);
2806
+ } catch (e) {
2807
+ }
2808
+ }
2809
+ let cssAttrName = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2810
+ let cssValue = null;
2811
+ const prevV = prevKf == null ? void 0 : prevKf.v;
2812
+ const nextV = nextKf == null ? void 0 : nextKf.v;
2813
+ if (cssAttrName === "d") {
2814
+ const prevPaths = (_c = prevV == null ? void 0 : prevV.paths) != null ? _c : Array.isArray(prevV) ? prevV : [];
2815
+ const nextPaths = (_d = nextV == null ? void 0 : nextV.paths) != null ? _d : Array.isArray(nextV) ? nextV : [];
2816
+ cssValue = interpolateBeziers(
2817
+ prevPaths,
2818
+ nextPaths,
2819
+ localProgress
2820
+ ).map((bz) => bezierToSvgPath(bz)).join("");
2821
+ } else if (PX_COLOR_ATTR_NAMES.has(cssAttrName)) {
2822
+ cssValue = toRGBA(interpolateColor(
2823
+ prevV || [0, 0, 0, 1],
2824
+ nextV || [0, 0, 0, 1],
2825
+ localProgress
2826
+ ));
2827
+ cssAttrName = propName;
2828
+ } else if (cssAttrName === "stroke-dasharray") {
2829
+ cssValue = interpolateVec(
2830
+ prevV || [],
2831
+ nextV || [],
2832
+ localProgress
2833
+ ).join(" ");
2834
+ cssAttrName = propName;
2835
+ } else if (cssAttrName === "transform" && prevV !== null && typeof prevV === "object" && !Array.isArray(prevV)) {
2836
+ const partKeys = /* @__PURE__ */ new Set([
2837
+ ...prevV ? Object.keys(prevV) : [],
2838
+ ...nextV ? Object.keys(nextV) : []
2839
+ ]);
2840
+ const partsResult = {};
2841
+ for (const partKey of partKeys) {
2842
+ const prevPart = prevV == null ? void 0 : prevV[partKey];
2843
+ const nextPart = nextV == null ? void 0 : nextV[partKey];
2844
+ if (partKey === "rotate" || partKey === "skew") {
2845
+ partsResult[partKey] = interpolateNum(+(prevPart != null ? prevPart : 0), +(nextPart != null ? nextPart : 0), localProgress);
2846
+ } else if (partKey === "translate" || partKey === "scale" || partKey === "origin") {
2847
+ const fallback = partKey === "scale" ? [1, 1] : [0, 0];
2848
+ const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);
2849
+ partsResult[partKey] = interp;
2850
+ }
2851
+ }
2852
+ if (propAnimIsMotionPath(propAnim)) {
2853
+ const prevTr = prevV.translate;
2854
+ const nextTr = nextV.translate;
2855
+ if (Array.isArray(prevTr) && Array.isArray(nextTr)) {
2856
+ const sample = evaluateMotionPathSegment(
2857
+ prevKf,
2858
+ nextKf,
2859
+ [+prevTr[0], +prevTr[1]],
2860
+ [+nextTr[0], +nextTr[1]],
2861
+ localProgress,
2862
+ !!propAnim.autoOrient
2863
+ );
2864
+ partsResult.translate = [sample.translate[0], sample.translate[1]];
2865
+ if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg + ((_e = partsResult.rotate) != null ? _e : 0);
2866
+ }
2867
+ }
2868
+ cssValue = composeTransformParts(partsResult, { withUnits: false });
2869
+ cssAttrName = "transform";
2870
+ } else if (cssAttrName === "translate") {
2871
+ const v = interpolateVec(
2872
+ prevV || [0, 0],
2873
+ nextV || [0, 0],
2874
+ localProgress
2875
+ );
2876
+ cssValue = "translate(" + v.join(",") + ")";
2877
+ cssAttrName = "transform";
2878
+ } else if (cssAttrName === "rotate") {
2879
+ const v = interpolateNum(
2880
+ +(prevV || 0),
2881
+ +(nextV || 0),
2882
+ localProgress
2883
+ );
2884
+ cssValue = "rotate(" + v + ")";
2885
+ cssAttrName = "transform";
2886
+ } else if (cssAttrName === "scale") {
2887
+ const v = interpolateVec(
2888
+ prevV || [1, 1],
2889
+ nextV || [1, 1],
2890
+ localProgress
2891
+ );
2892
+ cssValue = "scale(" + v.join(",") + ")";
2893
+ cssAttrName = "transform";
2894
+ } else {
2895
+ const num2 = interpolateNum(
2896
+ +(prevV || 0),
2897
+ +(nextV || 0),
2898
+ localProgress
2899
+ );
2900
+ cssValue = num2;
2901
+ }
2902
+ if (PX_PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
2903
+ cssValue = cssValue * 100 + "%";
2904
+ }
2905
+ return { k: cssAttrName, v: cssValue === null ? "" : "" + cssValue };
2906
+ }
2907
+ function calcAnimationValues(animDef, progress) {
2908
+ const result = {};
2909
+ for (const [propName, propAnim] of Object.entries(animDef)) {
2910
+ const computed = calcPropertyValue(propName, propAnim, progress);
2911
+ if (computed) {
2912
+ result[computed.k] = computed.v;
2913
+ }
2914
+ }
2915
+ return result;
2114
2916
  }
2115
2917
 
2116
2918
  // src/effects/PlayerEffectsUtil.visualModel.ts
@@ -2526,6 +3328,15 @@ function transformPathData(d, m, decimals = 2) {
2526
3328
  }
2527
3329
 
2528
3330
  // src/effects/shared/transformParts.ts
3331
+ function partsRecord(part, value, origin) {
3332
+ const rec = {};
3333
+ if (part === "translate" /* Translate */) rec.translate = value;
3334
+ else if (part === "rotate" /* Rotate */) rec.rotate = value;
3335
+ else if (part === "skew" /* Skew */) rec.skew = value;
3336
+ else rec.scale = value;
3337
+ if (origin && part !== "translate" /* Translate */) rec.origin = origin;
3338
+ return rec;
3339
+ }
2529
3340
  function readAnimatable(raw) {
2530
3341
  var _a2, _b;
2531
3342
  if (raw === void 0) return { kind: "absent" /* Absent */ };
@@ -2544,6 +3355,24 @@ function readAnimatable(raw) {
2544
3355
  }
2545
3356
  return { kind: "static" /* Static */, value: raw };
2546
3357
  }
3358
+ function writeAnimatableChannel(node, attrName, read, opts) {
3359
+ var _a2, _b, _c, _d;
3360
+ const toOut = (v) => (opts == null ? void 0 : opts.asString) && v !== void 0 && v !== null ? String(v) : v;
3361
+ if (read.kind === "absent" /* Absent */) return;
3362
+ if (read.kind === "static" /* Static */) {
3363
+ node[attrName] = toOut(read.value);
3364
+ return;
3365
+ }
3366
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3367
+ const animate = __spreadValues({}, prevAnimate || {});
3368
+ const block = { keyframes: read.keyframes };
3369
+ if (read.loop !== void 0) block.loop = read.loop;
3370
+ if (read.autoOrient !== void 0) block.autoOrient = read.autoOrient;
3371
+ animate[attrName] = block;
3372
+ node.animate = animate;
3373
+ const baseline = (_d = (_b = read.base) != null ? _b : (_a2 = read.keyframes[0]) == null ? void 0 : _a2.value) != null ? _d : (_c = read.keyframes[0]) == null ? void 0 : _c.v;
3374
+ if (baseline !== void 0) node[attrName] = toOut(baseline);
3375
+ }
2547
3376
  function normalizeKeyframe(kf) {
2548
3377
  if (!kf || typeof kf !== "object") return kf;
2549
3378
  const k = kf;
@@ -2558,6 +3387,22 @@ function normalizeKeyframe(kf) {
2558
3387
  if (out.tangentIn === void 0 && k.ti !== void 0) out.tangentIn = k.ti;
2559
3388
  return out;
2560
3389
  }
3390
+ function readStaticOrigin(raw, ctx) {
3391
+ var _a2;
3392
+ const o = readAnimatable(raw);
3393
+ if (o.kind === "absent" /* Absent */) return void 0;
3394
+ if (o.kind === "static" /* Static */) return o.value;
3395
+ ctx.warnings.push("transformBy.origin: animated origin approximated by its first keyframe");
3396
+ return (_a2 = o.keyframes[0]) == null ? void 0 : _a2.value;
3397
+ }
3398
+ function keyframeWith(kf, value) {
3399
+ const out = { value };
3400
+ if (kf.time !== void 0) out.time = kf.time;
3401
+ if (kf.easing !== void 0) out.easing = kf.easing;
3402
+ if (kf.tangentOut !== void 0) out.tangentOut = kf.tangentOut;
3403
+ if (kf.tangentIn !== void 0) out.tangentIn = kf.tangentIn;
3404
+ return out;
3405
+ }
2561
3406
 
2562
3407
  // src/effects/text/textGlyphsEffect.ts
2563
3408
  var DEFAULT_FONT_SIZE = 16;
@@ -3106,22 +3951,149 @@ function materializeGlyphText(node, opts) {
3106
3951
  if (opts.alongPath) return materializeGlyphTextAlongPath(node, opts.alongPath.pathD, opts.alongPath.startOffset, opts, opts.alongPath.textLength, opts.alongPath.pathOverflow);
3107
3952
  return materializeGlyphTextHorizontal(node, opts);
3108
3953
  }
3109
-
3110
- // src/effects/text/textPathEffect.ts
3111
- var EXTEND_MARGIN_FRAC = 0.15;
3112
- function numRange(v) {
3113
- const read = readAnimatable(v);
3114
- if (read.kind === "static" /* Static */ && typeof read.value === "number") return { min: read.value, max: read.value };
3115
- if (read.kind === "animated" /* Animated */ && read.keyframes.length) {
3116
- const vals = read.keyframes.map((k) => Number(k.value) || 0);
3117
- return { min: Math.min(...vals), max: Math.max(...vals) };
3954
+ function applyTextGlyphsEffect(node, fx, ctx) {
3955
+ if (!(fx == null ? void 0 : fx.useGlyphs)) return node;
3956
+ if (!ctx.glyphs) {
3957
+ ctx.warnings.push("textGlyphs: no definitions.fonts \u2014 left as native <text>");
3958
+ return node;
3118
3959
  }
3119
- return { min: 0, max: 0 };
3960
+ return materializeGlyphTextHorizontal(node, { glyphs: ctx.glyphs, warnings: ctx.warnings });
3120
3961
  }
3121
- var r3 = (n) => Math.round(n * 1e3) / 1e3;
3122
- function extendedPathForBrowser(pathD, opts) {
3123
- var _a2;
3124
- if (opts.pathOverflow === "clip") return { d: pathD, startShift: 0 };
3962
+ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pathOverflow) {
3963
+ if (!ctx.glyphs) {
3964
+ ctx.warnings.push("textGlyphs: no definitions.fonts");
3965
+ return null;
3966
+ }
3967
+ return materializeGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength, pathOverflow);
3968
+ }
3969
+
3970
+ // src/util/PxNodeCloneUtil.ts
3971
+ function deepClonePxNode(value) {
3972
+ if (value === null || typeof value !== "object") return value;
3973
+ if (Array.isArray(value)) return value.map(deepClonePxNode);
3974
+ const out = {};
3975
+ for (const k of Object.keys(value)) out[k] = deepClonePxNode(value[k]);
3976
+ return out;
3977
+ }
3978
+ function regenerateIdsAndRewriteRefs(root, genId3) {
3979
+ const oldToNew = /* @__PURE__ */ new Map();
3980
+ const walkAssign = (n) => {
3981
+ var _a2;
3982
+ if (typeof n.id === "string") {
3983
+ const newId = genId3();
3984
+ oldToNew.set(n.id, newId);
3985
+ n.id = newId;
3986
+ }
3987
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walkAssign);
3988
+ };
3989
+ walkAssign(root);
3990
+ const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
3991
+ const newId = oldToNew.get(oldId);
3992
+ return newId ? "url(#" + newId + ")" : m;
3993
+ });
3994
+ const walkRewrite = (n) => {
3995
+ var _a2;
3996
+ if (typeof n.href === "string" && n.href.startsWith("#")) {
3997
+ const newId = oldToNew.get(n.href.slice(1));
3998
+ if (newId) n.href = "#" + newId;
3999
+ }
4000
+ for (const k of Object.keys(n)) {
4001
+ if (k === "children" || k === "effects" || k === "meta" || k === "animate" || k === "href" || k === "id") continue;
4002
+ const v = n[k];
4003
+ if (typeof v === "string" && v.indexOf("url(#") !== -1) {
4004
+ n[k] = rewriteUrl(v);
4005
+ }
4006
+ }
4007
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walkRewrite);
4008
+ };
4009
+ walkRewrite(root);
4010
+ return oldToNew;
4011
+ }
4012
+ function toFiniteNum(v) {
4013
+ const n = typeof v === "number" ? v : typeof v === "string" ? parseFloat(v) : NaN;
4014
+ return Number.isFinite(n) ? n : 0;
4015
+ }
4016
+ function applyUseOffsetToG(gNode) {
4017
+ var _a2;
4018
+ const x = toFiniteNum(gNode.x);
4019
+ const y = toFiniteNum(gNode.y);
4020
+ delete gNode.x;
4021
+ delete gNode.y;
4022
+ if (!x && !y) return gNode;
4023
+ const offset = "translate(" + x + "," + y + ")";
4024
+ const carriesTransform = gNode.transform !== void 0 || gNode.animate !== void 0;
4025
+ if (carriesTransform) {
4026
+ const inner = { type: "g", transform: offset, children: (_a2 = gNode.children) != null ? _a2 : [] };
4027
+ gNode.children = [inner];
4028
+ } else {
4029
+ gNode.transform = offset;
4030
+ }
4031
+ return gNode;
4032
+ }
4033
+
4034
+ // src/effects/shared/util.ts
4035
+ function genId(ctx, prefix) {
4036
+ return "_lw_" + prefix + "_" + ctx.nextId++;
4037
+ }
4038
+ function stripHash(href) {
4039
+ return typeof href === "string" ? href.replace(/^#/, "") : void 0;
4040
+ }
4041
+ function indexById(node, map) {
4042
+ var _a2;
4043
+ if (typeof node.id === "string") map.set(node.id, node);
4044
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((child) => indexById(child, map));
4045
+ }
4046
+ function spliceDefs(root, defs) {
4047
+ if (!defs.length) return;
4048
+ const existing = root.children || (root.children = []);
4049
+ existing.unshift({ type: "defs", children: defs });
4050
+ }
4051
+ var clone = deepClonePxNode;
4052
+ function regenerateIdsInClone(root, ctx) {
4053
+ return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
4054
+ }
4055
+
4056
+ // src/effects/text/textPathEffect.ts
4057
+ var EXTEND_MARGIN_FRAC = 0.15;
4058
+ function numRange(v) {
4059
+ const read = readAnimatable(v);
4060
+ if (read.kind === "static" /* Static */ && typeof read.value === "number") return { min: read.value, max: read.value };
4061
+ if (read.kind === "animated" /* Animated */ && read.keyframes.length) {
4062
+ const vals = read.keyframes.map((k) => Number(k.value) || 0);
4063
+ return { min: Math.min(...vals), max: Math.max(...vals) };
4064
+ }
4065
+ return { min: 0, max: 0 };
4066
+ }
4067
+ function estimateTextAdvance(node) {
4068
+ let chars = 0, maxFont = 16;
4069
+ const walk = (el) => {
4070
+ var _a2;
4071
+ const fs = parseFloat(String((_a2 = el.fontSize) != null ? _a2 : "")) || 0;
4072
+ if (fs) maxFont = Math.max(maxFont, fs);
4073
+ const t = el.textContent;
4074
+ if (typeof t === "string") chars += t.length;
4075
+ if (el.children) for (const c of el.children) walk(c);
4076
+ };
4077
+ walk(node);
4078
+ return chars * maxFont;
4079
+ }
4080
+ var r3 = (n) => Math.round(n * 1e3) / 1e3;
4081
+ function shiftAnimatable(v, by) {
4082
+ if (!by || v === void 0 || v === null) return v;
4083
+ const read = readAnimatable(v);
4084
+ if (read.kind === "absent" /* Absent */) return v;
4085
+ if (read.kind === "static" /* Static */) return typeof v === "number" ? read.value + by : { value: read.value + by };
4086
+ const out = {
4087
+ keyframes: read.keyframes.map((k) => __spreadProps(__spreadValues({}, k), { value: (Number(k.value) || 0) + by }))
4088
+ };
4089
+ if (read.loop !== void 0) out.loop = read.loop;
4090
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
4091
+ if (read.base !== void 0) out.value = read.base + by;
4092
+ return out;
4093
+ }
4094
+ function extendedPathForBrowser(pathD, opts) {
4095
+ var _a2;
4096
+ if (opts.pathOverflow === "clip") return { d: pathD, startShift: 0 };
3125
4097
  const sampler = createPathSampler(pathD);
3126
4098
  if (!sampler || sampler.closed || sampler.totalLength <= 0) return { d: pathD, startShift: 0 };
3127
4099
  const L = sampler.totalLength;
@@ -3141,33 +4113,2083 @@ function extendedPathForBrowser(pathD, opts) {
3141
4113
  const rest = pathD.replace(/^\s*[Mm]\s*-?[\d.]+[\s,]+-?[\d.]+/, "");
3142
4114
  d = "M" + r3(sx) + "," + r3(sy) + "L" + r3(s.x) + "," + r3(s.y) + rest;
3143
4115
  }
3144
- if (endExt > 0) {
3145
- const ex = e.x + Math.cos(e.angle) * endExt, ey = e.y + Math.sin(e.angle) * endExt;
3146
- d += "L" + r3(ex) + "," + r3(ey);
4116
+ if (endExt > 0) {
4117
+ const ex = e.x + Math.cos(e.angle) * endExt, ey = e.y + Math.sin(e.angle) * endExt;
4118
+ d += "L" + r3(ex) + "," + r3(ey);
4119
+ }
4120
+ return { d, startShift: startExt };
4121
+ }
4122
+ function applyTextPathEffect(node, fx, ctx) {
4123
+ var _a2;
4124
+ if (!fx || typeof fx.pathData !== "string" || !fx.pathData) return node;
4125
+ const pathId = genId(ctx, "tpath");
4126
+ const { d, startShift } = extendedPathForBrowser(fx.pathData, {
4127
+ pathOverflow: fx.pathOverflow,
4128
+ startOffset: fx.startOffset,
4129
+ textLength: fx.textLength,
4130
+ advance: estimateTextAdvance(node)
4131
+ });
4132
+ ctx.defs.push({ type: "path", id: pathId, d });
4133
+ const textPath = {
4134
+ type: "textPath",
4135
+ href: "#" + pathId,
4136
+ children: (_a2 = node.children) != null ? _a2 : []
4137
+ };
4138
+ if (fx.lengthAdjust !== void 0) textPath.lengthAdjust = fx.lengthAdjust;
4139
+ if (fx.method !== void 0) textPath.method = fx.method;
4140
+ if (fx.spacing !== void 0) textPath.spacing = fx.spacing;
4141
+ applyAnimatableNumber(textPath, "startOffset", shiftAnimatable(fx.startOffset, startShift));
4142
+ applyAnimatableNumber(textPath, "textLength", fx.textLength);
4143
+ node.children = [textPath];
4144
+ return node;
4145
+ }
4146
+ function applyAnimatableNumber(node, attrName, raw) {
4147
+ if (raw === void 0 || raw === null) return;
4148
+ writeAnimatableChannel(node, attrName, readAnimatable(raw), { asString: true });
4149
+ }
4150
+
4151
+ // src/format/PxDocumentDiagnostic.ts
4152
+ var MAX_REPORTED = 6;
4153
+ function diagnoseDocument(doc) {
4154
+ try {
4155
+ return { problems: validateDocument(doc) };
4156
+ } catch (e) {
4157
+ return { problems: [] };
4158
+ }
4159
+ }
4160
+ function reportDocumentDiagnostics(doc, where) {
4161
+ const { problems } = diagnoseDocument(doc);
4162
+ if (!problems.length) return;
4163
+ const shown = problems.slice(0, MAX_REPORTED);
4164
+ const more = problems.length - shown.length;
4165
+ console.warn(
4166
+ where + ": this document does not match the animation schema in " + problems.length + " place" + (problems.length === 1 ? "" : "s") + ".\n" + shown.map((p) => " - " + p).join("\n") + (more > 0 ? "\n \u2026 and " + more + " more" : "") + "\n\nIf you did not author these keys, the usual cause is a build that MANGLES PROPERTY NAMES. An animation document is data loaded at runtime, so renaming the property reads inside the player stops them matching the keys in the JSON, and the animation silently does nothing. Feed the published reserved-name list to your minifier \u2014 @pixodesk/svg-animator-web/mangle-reserved.json \u2014 see docs/library/minification.md."
4167
+ );
4168
+ }
4169
+
4170
+ // src/effects/transform/transformationEffect.ts
4171
+ function applyTransformByEffect(node, fx, ctx) {
4172
+ if (!fx) return node;
4173
+ delete node.transform;
4174
+ let n = node;
4175
+ n = wrapOrigin(
4176
+ n,
4177
+ fx.origin,
4178
+ /*invert=*/
4179
+ true
4180
+ );
4181
+ n = wrapTransformPart(n, "scale" /* Scale */, normalizeScale(fx.scale), ctx);
4182
+ n = wrapTransformPart(n, "skew" /* Skew */, fx.skew, ctx);
4183
+ n = wrapTransformPart(n, "rotate" /* Rotate */, fx.rotate, ctx);
4184
+ n = wrapOrigin(
4185
+ n,
4186
+ fx.origin,
4187
+ /*invert=*/
4188
+ false
4189
+ );
4190
+ if (translateHasAutoOrient(fx.translate)) {
4191
+ n = wrapOrigin(
4192
+ n,
4193
+ fx.origin,
4194
+ /*invert=*/
4195
+ true
4196
+ );
4197
+ n = wrapTransformPart(n, "translate" /* Translate */, fx.translate, ctx);
4198
+ n = wrapOrigin(
4199
+ n,
4200
+ fx.origin,
4201
+ /*invert=*/
4202
+ false
4203
+ );
4204
+ } else {
4205
+ n = wrapTransformPart(n, "translate" /* Translate */, fx.translate, ctx);
4206
+ }
4207
+ if (n !== node && node.id) {
4208
+ n.id = node.id;
4209
+ delete node.id;
4210
+ }
4211
+ return n;
4212
+ }
4213
+ function translateHasAutoOrient(translate) {
4214
+ if (!translate || typeof translate !== "object") return false;
4215
+ const obj = translate;
4216
+ if (obj.autoOrient) return true;
4217
+ return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
4218
+ }
4219
+ function normalizeScale(raw) {
4220
+ return raw;
4221
+ }
4222
+ function wrapTransformPart(inner, part, raw, ctx) {
4223
+ if (raw === void 0) return inner;
4224
+ const v = readAnimatable(raw);
4225
+ if (v.kind === "static" /* Static */) {
4226
+ return { type: "g", transform: { value: partsRecord(part, v.value, void 0) }, children: [inner] };
4227
+ }
4228
+ if (v.kind === "animated" /* Animated */) {
4229
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
4230
+ if (v.autoOrient) animTr.autoOrient = true;
4231
+ if (v.loop !== void 0) animTr.loop = v.loop;
4232
+ return {
4233
+ type: "g",
4234
+ animate: { transform: animTr },
4235
+ children: [inner]
4236
+ };
4237
+ }
4238
+ return inner;
4239
+ }
4240
+ function wrapOrigin(inner, raw, invert) {
4241
+ if (raw === void 0) return inner;
4242
+ const v = readAnimatable(raw);
4243
+ const sign = (value) => invert ? [-value[0], -value[1]] : value;
4244
+ if (v.kind === "absent" /* Absent */) return inner;
4245
+ if (v.kind === "static" /* Static */) {
4246
+ if (v.value[0] === 0 && v.value[1] === 0) return inner;
4247
+ return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
4248
+ }
4249
+ if (v.kind === "animated" /* Animated */) {
4250
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) };
4251
+ if (v.loop !== void 0) animTr.loop = v.loop;
4252
+ return {
4253
+ type: "g",
4254
+ animate: { transform: animTr },
4255
+ children: [inner]
4256
+ };
4257
+ }
4258
+ return inner;
4259
+ }
4260
+
4261
+ // src/effects/reference/contentRefSplit.ts
4262
+ function identifyContentRefTargets(node, ctx, allocator) {
4263
+ var _a2, _b, _c;
4264
+ if (node.type === "use" && ((_b = (_a2 = node.effects) == null ? void 0 : _a2.clone) == null ? void 0 : _b.without) === "translate") {
4265
+ const sourceId = stripHash(node.effects.clone.source);
4266
+ if (typeof sourceId === "string" && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {
4267
+ ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));
4268
+ }
4269
+ }
4270
+ (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
4271
+ }
4272
+ function splitForContentRef(node, transformBy, originalId, innerId, ctx) {
4273
+ const outerBody = liftBodyTranslate(node, transformBy);
4274
+ if (typeof node.id === "string") delete node.id;
4275
+ const { outer: outerTr, inner: innerTr } = splitTransformByEffect(transformBy);
4276
+ let innerNode = node;
4277
+ innerNode = applyTransformByEffect(innerNode, innerTr, ctx);
4278
+ const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
4279
+ let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
4280
+ if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
4281
+ if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
4282
+ if (outerTr) {
4283
+ delete outerWrapper.id;
4284
+ outerWrapper = applyTransformByEffect(outerWrapper, outerTr, ctx);
4285
+ outerWrapper.id = originalId;
4286
+ }
4287
+ return outerWrapper;
4288
+ }
4289
+ function liftBodyTranslate(node, transformBy) {
4290
+ var _a2, _b, _c;
4291
+ const out = {};
4292
+ let didLiftAnimate = false;
4293
+ const animTr = (_a2 = node.animate) == null ? void 0 : _a2.transform;
4294
+ if (animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes)) {
4295
+ const kfs = animTr.keyframes;
4296
+ const hasTranslate = kfs.some((kf) => kf.value && kf.value.translate);
4297
+ if (hasTranslate) {
4298
+ const outerHasOrigin = needsOriginOnOuter(animTr);
4299
+ const outerKfs = kfs.map((kf) => {
4300
+ const v = kf.value || {};
4301
+ const newValue = {};
4302
+ if (v.translate !== void 0) newValue.translate = v.translate;
4303
+ if (outerHasOrigin && v.origin !== void 0) newValue.origin = v.origin;
4304
+ const outerKf = { value: newValue };
4305
+ if (kf.time !== void 0) outerKf.time = kf.time;
4306
+ if (kf.easing !== void 0) outerKf.easing = kf.easing;
4307
+ if (kf.tangentOut !== void 0) outerKf.tangentOut = kf.tangentOut;
4308
+ if (kf.tangentIn !== void 0) outerKf.tangentIn = kf.tangentIn;
4309
+ return outerKf;
4310
+ });
4311
+ const outerAnimTr = { keyframes: outerKfs };
4312
+ if (animTr.autoOrient) outerAnimTr.autoOrient = true;
4313
+ const srcLoop = animTr.loop;
4314
+ if (srcLoop !== void 0) outerAnimTr.loop = srcLoop;
4315
+ out.animate = { transform: outerAnimTr };
4316
+ const innerHasPivotedPart = kfs.some((kf) => {
4317
+ const v = kf.value || {};
4318
+ return v.rotate !== void 0 || v.scale !== void 0;
4319
+ });
4320
+ const innerKfs = kfs.map((kf) => {
4321
+ const v = kf.value || {};
4322
+ const newValue = {};
4323
+ if (v.rotate !== void 0) newValue.rotate = v.rotate;
4324
+ if (v.scale !== void 0) newValue.scale = v.scale;
4325
+ if (v.origin !== void 0 && (!outerHasOrigin || innerHasPivotedPart)) newValue.origin = v.origin;
4326
+ const innerKf = { value: newValue };
4327
+ if (kf.time !== void 0) innerKf.time = kf.time;
4328
+ if (kf.easing !== void 0) innerKf.easing = kf.easing;
4329
+ return innerKf;
4330
+ });
4331
+ const allInnerEmpty = innerKfs.every((kf) => Object.keys(kf.value).length === 0);
4332
+ if (allInnerEmpty) {
4333
+ delete node.animate.transform;
4334
+ if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
4335
+ } else {
4336
+ const innerAnimTr = { keyframes: innerKfs };
4337
+ if (srcLoop !== void 0) innerAnimTr.loop = srcLoop;
4338
+ node.animate.transform = innerAnimTr;
4339
+ }
4340
+ didLiftAnimate = true;
4341
+ }
4342
+ }
4343
+ const transformationHasTranslate = (transformBy == null ? void 0 : transformBy.translate) !== void 0;
4344
+ const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
4345
+ const liftedAnimateIsAutoOriented = didLiftAnimate && needsOriginOnOuter(((_b = node.animate) == null ? void 0 : _b.transform) || void 0) || didLiftAnimate && needsOriginOnOuter(((_c = out.animate) == null ? void 0 : _c.transform) || void 0);
4346
+ if (typeof node.transform === "string") {
4347
+ const split = splitTransformString(node.transform);
4348
+ if (stripBodyTranslateOnly) {
4349
+ if (split.translate !== void 0) {
4350
+ if (split.rest) node.transform = split.rest;
4351
+ else delete node.transform;
4352
+ } else if (isPureTranslateBody(node.transform)) {
4353
+ delete node.transform;
4354
+ } else if (liftedAnimateIsAutoOriented && isSingleMatrixBody(node.transform)) {
4355
+ delete node.transform;
4356
+ }
4357
+ } else if (split.translate) {
4358
+ out.transform = split.translate;
4359
+ if (split.rest) node.transform = split.rest;
4360
+ else delete node.transform;
4361
+ }
4362
+ } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes) {
4363
+ const wrapped = node.transform.value;
4364
+ const isWrapped = !!(wrapped && typeof wrapped === "object");
4365
+ const value = isWrapped ? wrapped : node.transform;
4366
+ const rewrap = (rec) => isWrapped ? { value: rec } : rec;
4367
+ if (Array.isArray(value.translate)) {
4368
+ const rest = __spreadValues({}, value);
4369
+ delete rest.translate;
4370
+ const hasRest = Object.keys(rest).length > 0;
4371
+ if (stripBodyTranslateOnly) {
4372
+ if (hasRest) node.transform = rewrap(rest);
4373
+ else delete node.transform;
4374
+ } else {
4375
+ out.transform = rewrap({ translate: value.translate });
4376
+ if (hasRest) node.transform = rewrap(rest);
4377
+ else delete node.transform;
4378
+ }
4379
+ }
4380
+ }
4381
+ return out;
4382
+ }
4383
+ function isSingleMatrixBody(s) {
4384
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
4385
+ let count = 0;
4386
+ let isMatrix = false;
4387
+ let m;
4388
+ while (m = re.exec(s)) {
4389
+ count++;
4390
+ if (m[1] === "matrix") isMatrix = true;
4391
+ }
4392
+ return count === 1 && isMatrix;
4393
+ }
4394
+ function isPureTranslateBody(s) {
4395
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
4396
+ const ops = [];
4397
+ let m;
4398
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
4399
+ if (ops.length !== 1) return false;
4400
+ if (ops[0].name === "translate") return true;
4401
+ if (ops[0].name !== "matrix") return false;
4402
+ const args = /matrix\(([^)]*)\)/.exec(ops[0].full);
4403
+ if (!args) return false;
4404
+ const nums = args[1].split(/[\s,]+/).filter(Boolean).map(Number);
4405
+ return nums.length >= 4 && nums[0] === 1 && nums[1] === 0 && nums[2] === 0 && nums[3] === 1;
4406
+ }
4407
+ function splitTransformString(s) {
4408
+ const ops = [];
4409
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
4410
+ let m;
4411
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
4412
+ if (!ops.length) return { rest: s || void 0 };
4413
+ if (ops.every((o) => o.name === "translate")) {
4414
+ return { translate: ops.map((o) => o.full).join("") };
4415
+ }
4416
+ const leading = ops[0];
4417
+ const trailing = ops[ops.length - 1];
4418
+ if (trailing.name === "translate" && leading.name === "translate") {
4419
+ const trailingVec = parseTranslateArgs(trailing.full);
4420
+ const leadingVec = parseTranslateArgs(leading.full);
4421
+ const ox = -trailingVec[0];
4422
+ const oy = -trailingVec[1];
4423
+ const userTx = leadingVec[0] - ox;
4424
+ const userTy = leadingVec[1] - oy;
4425
+ if (userTx === 0 && userTy === 0) return { rest: s };
4426
+ const middleAndTrailing = "translate(" + ox + "," + oy + ")" + ops.slice(1).map((o) => o.full).join("");
4427
+ return { translate: "translate(" + userTx + "," + userTy + ")", rest: middleAndTrailing };
4428
+ }
4429
+ if (trailing.name === "translate") return { rest: s };
4430
+ const lifted = [];
4431
+ let i = 0;
4432
+ while (i < ops.length && ops[i].name === "translate") {
4433
+ lifted.push(ops[i].full);
4434
+ i++;
4435
+ }
4436
+ if (!lifted.length) return { rest: s };
4437
+ const rest = ops.slice(i).map((o) => o.full).join("");
4438
+ return {
4439
+ translate: lifted.join(""),
4440
+ rest: rest || void 0
4441
+ };
4442
+ }
4443
+ function parseTranslateArgs(translateOp) {
4444
+ const m = /translate\(([^)]*)\)/.exec(translateOp);
4445
+ if (!m) return [0, 0];
4446
+ const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
4447
+ return [nums[0] || 0, nums[1] || 0];
4448
+ }
4449
+ function splitTransformByEffect(fx) {
4450
+ if (!fx) return {};
4451
+ const originOnOuter = needsOriginOnOuter(fx.translate);
4452
+ const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
4453
+ const outer = {};
4454
+ const inner = {};
4455
+ if (fx.translate !== void 0) outer.translate = fx.translate;
4456
+ if (originOnOuter && fx.origin !== void 0) outer.origin = fx.origin;
4457
+ if (fx.rotate !== void 0) inner.rotate = fx.rotate;
4458
+ if (fx.scale !== void 0) inner.scale = fx.scale;
4459
+ if (fx.skew !== void 0) inner.skew = fx.skew;
4460
+ if (fx.origin !== void 0 && (!originOnOuter || innerHasPivotedPart)) inner.origin = fx.origin;
4461
+ return {
4462
+ outer: Object.keys(outer).length ? outer : void 0,
4463
+ inner: Object.keys(inner).length ? inner : void 0
4464
+ };
4465
+ }
4466
+ function needsOriginOnOuter(translateAnim) {
4467
+ if (!translateAnim || typeof translateAnim !== "object") return false;
4468
+ const obj = translateAnim;
4469
+ if (obj.autoOrient) return true;
4470
+ if (Array.isArray(obj.keyframes)) {
4471
+ return obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
4472
+ }
4473
+ return false;
4474
+ }
4475
+
4476
+ // src/effects/paint/gradientEffect.ts
4477
+ function applyFillGradientEffect(node, fx, ctx) {
4478
+ return applyGradient(node, fx, ctx, "fill");
4479
+ }
4480
+ function applyStrokeGradientEffect(node, fx, ctx) {
4481
+ return applyGradient(node, fx, ctx, "stroke");
4482
+ }
4483
+ function applyGradient(node, fx, ctx, attr) {
4484
+ if (!fx) return node;
4485
+ const id = genId(ctx, "grad");
4486
+ const def = synthesiseGradientDef(fx, id, ctx);
4487
+ ctx.defs.push(def);
4488
+ node[attr] = "url(#" + id + ")";
4489
+ return node;
4490
+ }
4491
+ function synthesiseGradientDef(fx, id, ctx) {
4492
+ const out = {
4493
+ type: fx.type === PxGradientType.radial ? "radialGradient" : "linearGradient",
4494
+ id
4495
+ };
4496
+ if (fx.type === PxGradientType.linear) {
4497
+ applyGeomVec(out, "x1", "y1", fx.start);
4498
+ applyGeomVec(out, "x2", "y2", fx.end);
4499
+ } else {
4500
+ applyGeomVec(out, "cx", "cy", fx.center);
4501
+ applyGeomNumber(out, "r", fx.radius);
4502
+ applyGeomVec(out, "fx", "fy", fx.focal);
4503
+ }
4504
+ if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
4505
+ if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
4506
+ if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;
4507
+ out.children = buildStopChildren(fx.stops, ctx);
4508
+ return out;
4509
+ }
4510
+ function applyGeomVec(out, xAttr, yAttr, raw) {
4511
+ var _a2, _b, _c;
4512
+ const read = readAnimatable(raw);
4513
+ if (read.kind === "absent" /* Absent */) return;
4514
+ if (read.kind === "static" /* Static */) {
4515
+ out[xAttr] = String(read.value[0]);
4516
+ out[yAttr] = String(read.value[1]);
4517
+ return;
4518
+ }
4519
+ const axisChannel = (idx) => {
4520
+ const block = {
4521
+ keyframes: read.keyframes.map((kf) => {
4522
+ const axisKf = { time: kf.time, value: Array.isArray(kf.value) ? kf.value[idx] : void 0 };
4523
+ if (kf.easing !== void 0) axisKf.easing = kf.easing;
4524
+ return axisKf;
4525
+ })
4526
+ };
4527
+ if (read.loop !== void 0) block.loop = read.loop;
4528
+ return block;
4529
+ };
4530
+ const animate = (_a2 = out.animate) != null ? _a2 : {};
4531
+ animate[xAttr] = axisChannel(0);
4532
+ animate[yAttr] = axisChannel(1);
4533
+ out.animate = animate;
4534
+ const baseline = (_c = read.base) != null ? _c : (_b = read.keyframes[0]) == null ? void 0 : _b.value;
4535
+ if (Array.isArray(baseline)) {
4536
+ out[xAttr] = String(baseline[0]);
4537
+ out[yAttr] = String(baseline[1]);
4538
+ }
4539
+ }
4540
+ function applyGeomNumber(out, attrName, raw) {
4541
+ const read = readAnimatable(raw);
4542
+ if (read.kind === "absent" /* Absent */) return;
4543
+ writeAnimatableChannel(out, attrName, read, { asString: true });
4544
+ }
4545
+ function buildStopChildren(stops, ctx) {
4546
+ var _a2, _b;
4547
+ if (!stops) return [];
4548
+ const read = readAnimatable(stops);
4549
+ if (read.kind === "absent" /* Absent */) return [];
4550
+ if (read.kind === "static" /* Static */) return Array.isArray(read.value) ? read.value.map(staticStopNode) : [];
4551
+ const kfs = read.keyframes;
4552
+ if (!kfs.length) return [];
4553
+ const loopFromSource = read.loop;
4554
+ let stopCount = 0;
4555
+ for (const kf of kfs) {
4556
+ const v = keyframeValue(kf);
4557
+ if (Array.isArray(v) && v.length > stopCount) stopCount = v.length;
4558
+ }
4559
+ if (!stopCount) return [];
4560
+ const firstKfValue = keyframeValue(kfs[0]);
4561
+ const baselineStops = [];
4562
+ for (let i = 0; i < stopCount; i++) {
4563
+ const s = (_b = (_a2 = firstKfValue == null ? void 0 : firstKfValue[i]) != null ? _a2 : prevDefinedStop(kfs, 0, i)) != null ? _b : { offset: i / Math.max(1, stopCount - 1), color: "#000000" };
4564
+ baselineStops.push({ offset: s.offset, color: s.color });
4565
+ }
4566
+ return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx, loopFromSource));
4567
+ }
4568
+ function staticStopNode(s) {
4569
+ return {
4570
+ type: "stop",
4571
+ offset: formatOffset(s.offset),
4572
+ stopColor: s.color
4573
+ };
4574
+ }
4575
+ function animatedStopNode(baseline, kfs, stopIdx, _ctx, loop) {
4576
+ var _a2;
4577
+ const colorKfs = [];
4578
+ const offsetKfs = [];
4579
+ let offsetVaries = false;
4580
+ for (const kf of kfs) {
4581
+ const t = keyframeTime(kf);
4582
+ const arr = keyframeValue(kf);
4583
+ const sliced = (_a2 = arr == null ? void 0 : arr[stopIdx]) != null ? _a2 : prevDefinedStop(kfs, kfs.indexOf(kf), stopIdx);
4584
+ if (!sliced) continue;
4585
+ const easing = keyframeEasing(kf);
4586
+ const colorOut = { time: t, value: sliced.color };
4587
+ if (easing !== void 0) colorOut.easing = easing;
4588
+ colorKfs.push(colorOut);
4589
+ const offsetOut = { time: t, value: sliced.offset };
4590
+ if (easing !== void 0) offsetOut.easing = easing;
4591
+ offsetKfs.push(offsetOut);
4592
+ if (sliced.offset !== baseline.offset) offsetVaries = true;
4593
+ }
4594
+ const stop = {
4595
+ type: "stop",
4596
+ offset: formatOffset(baseline.offset),
4597
+ stopColor: baseline.color
4598
+ };
4599
+ const animate = {};
4600
+ if (colorKfs.length) {
4601
+ animate.stopColor = { keyframes: colorKfs };
4602
+ if (loop !== void 0) animate.stopColor.loop = loop;
4603
+ }
4604
+ if (offsetVaries && offsetKfs.length) {
4605
+ animate.offset = { keyframes: offsetKfs };
4606
+ if (loop !== void 0) animate.offset.loop = loop;
4607
+ }
4608
+ if (Object.keys(animate).length) stop.animate = animate;
4609
+ return stop;
4610
+ }
4611
+ function prevDefinedStop(kfs, fromIdx, stopIdx) {
4612
+ for (let i = fromIdx; i >= 0; i--) {
4613
+ const arr = keyframeValue(kfs[i]);
4614
+ if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
4615
+ }
4616
+ for (let i = fromIdx + 1; i < kfs.length; i++) {
4617
+ const arr = keyframeValue(kfs[i]);
4618
+ if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
4619
+ }
4620
+ return void 0;
4621
+ }
4622
+ function formatOffset(o) {
4623
+ const pct = Math.round(o * 1e3) / 10;
4624
+ return pct + "%";
4625
+ }
4626
+
4627
+ // src/effects/clipping/clipPathEffect.ts
4628
+ function applyClipPathEffect(node, fx, ctx) {
4629
+ if (!(fx == null ? void 0 : fx.pathData)) return node;
4630
+ const clipId = genId(ctx, "clip");
4631
+ const pathChild = { type: "path" };
4632
+ const read = readAnimatable(fx.pathData);
4633
+ if (read.kind !== "absent" /* Absent */) {
4634
+ if (read.kind === "static" /* Static */) {
4635
+ pathChild.d = pathString(read.value);
4636
+ } else {
4637
+ writeAnimatableChannel(pathChild, "d", read);
4638
+ if (pathChild.d !== void 0) pathChild.d = pathString(pathChild.d);
4639
+ }
4640
+ }
4641
+ ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
4642
+ node.clipPath = "url(#" + clipId + ")";
4643
+ return node;
4644
+ }
4645
+ function pathString(v) {
4646
+ if (typeof v === "string") return v;
4647
+ if (v && typeof v === "object" && typeof v.pathData === "string") return v.pathData;
4648
+ return void 0;
4649
+ }
4650
+
4651
+ // src/effects/clipping/maskedByEffect.ts
4652
+ function applyMaskedByEffect(node, fx, transformBy, ctx) {
4653
+ if (!fx) return node;
4654
+ const sourceId = stripHash(fx.source);
4655
+ if (!sourceId) {
4656
+ ctx.errors.push("maskedBy.source missing \u2014 cannot build mask");
4657
+ return node;
4658
+ }
4659
+ const maskId = genId(ctx, "mask");
4660
+ let content = { type: "use", href: "#" + sourceId };
4661
+ if (transformBy) {
4662
+ content = wrapInverseTransform(content, transformBy, ctx);
4663
+ } else if (hasAnimateTransform(node)) {
4664
+ content = wrapInverseAnimatedBodyTransform(content, node, ctx);
4665
+ } else {
4666
+ const bodyStatic = readTransformationFromBody(node);
4667
+ if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);
4668
+ }
4669
+ const includeTargetOwn = transformBy === void 0 && !nodeHasBodyTransform(node);
4670
+ content = wrapAncestorChainCompensation(content, node, sourceId, ctx, includeTargetOwn);
4671
+ const mask = { type: "mask", id: maskId, children: [content] };
4672
+ if (fx.maskType) mask.maskType = fx.maskType;
4673
+ if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
4674
+ if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
4675
+ if (fx.x !== void 0) mask.x = String(fx.x);
4676
+ if (fx.y !== void 0) mask.y = String(fx.y);
4677
+ if (fx.width !== void 0) mask.width = String(fx.width);
4678
+ if (fx.height !== void 0) mask.height = String(fx.height);
4679
+ ctx.defs.push(mask);
4680
+ node.mask = "url(#" + maskId + ")";
4681
+ return node;
4682
+ }
4683
+ function wrapInverseTransform(inner, fx, ctx) {
4684
+ if (!fx) return inner;
4685
+ const origin = readStaticOrigin(fx.origin, ctx);
4686
+ let n = inner;
4687
+ n = wrapInversePart(n, "translate" /* Translate */, fx.translate, void 0, ctx);
4688
+ n = wrapInversePart(n, "rotate" /* Rotate */, fx.rotate, origin, ctx);
4689
+ n = wrapInversePart(n, "scale" /* Scale */, fx.scale, origin, ctx);
4690
+ return n;
4691
+ }
4692
+ function wrapInversePart(inner, part, raw, origin, ctx) {
4693
+ if (raw === void 0) return inner;
4694
+ const normalizedRaw = part === "scale" /* Scale */ && Array.isArray(raw) ? [raw[0] / 100, raw[1] / 100] : raw;
4695
+ const v = readAnimatable(normalizedRaw);
4696
+ if (v.kind === "static" /* Static */) {
4697
+ return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
4698
+ }
4699
+ if (v.kind === "animated" /* Animated */) {
4700
+ const animTr = { keyframes: v.keyframes.map((kf) => {
4701
+ const out = keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin));
4702
+ return part === "translate" /* Translate */ ? __spreadValues(__spreadValues({}, out), negatedSpatialTangents(kf)) : out;
4703
+ }) };
4704
+ if (v.loop !== void 0) animTr.loop = v.loop;
4705
+ return {
4706
+ type: "g",
4707
+ animate: { transform: animTr },
4708
+ children: [inner]
4709
+ };
4710
+ }
4711
+ return inner;
4712
+ }
4713
+ function invertPartValue(part, value) {
4714
+ if (part === "translate" /* Translate */) return [-value[0], -value[1]];
4715
+ if (part === "rotate" /* Rotate */) return -value;
4716
+ return [1 / value[0], 1 / value[1]];
4717
+ }
4718
+ function negatedSpatialTangents(kf) {
4719
+ var _a2, _b;
4720
+ const out = {};
4721
+ const to = (_a2 = kf.tangentOut) != null ? _a2 : kf.to;
4722
+ const ti = (_b = kf.tangentIn) != null ? _b : kf.ti;
4723
+ if (Array.isArray(to)) out.tangentOut = [-to[0], -to[1]];
4724
+ if (Array.isArray(ti)) out.tangentIn = [-ti[0], -ti[1]];
4725
+ return out;
4726
+ }
4727
+ function wrapInverseAnimatedBodyTransform(inner, node, _ctx) {
4728
+ var _a2;
4729
+ const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4730
+ const animTr = animate == null ? void 0 : animate.transform;
4731
+ const kfs = animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes) ? animTr.keyframes : void 0;
4732
+ if (!kfs || !kfs.length) return inner;
4733
+ const translateKfs = [];
4734
+ const rotateKfs = [];
4735
+ const scaleKfs = [];
4736
+ for (const kf of kfs) {
4737
+ const v = ((_a2 = kf.value) != null ? _a2 : kf.v) || {};
4738
+ const baseKf = keyframeWith(kf, void 0);
4739
+ if (Array.isArray(v.translate)) {
4740
+ translateKfs.push(__spreadProps(__spreadValues(__spreadValues({}, baseKf), negatedSpatialTangents(kf)), { value: { translate: [-v.translate[0], -v.translate[1]] } }));
4741
+ }
4742
+ if (typeof v.rotate === "number") {
4743
+ const rec = { rotate: -v.rotate };
4744
+ if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];
4745
+ rotateKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
4746
+ }
4747
+ if (Array.isArray(v.scale)) {
4748
+ const rec = { scale: [1 / v.scale[0], 1 / v.scale[1]] };
4749
+ if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];
4750
+ scaleKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
4751
+ }
4752
+ }
4753
+ const srcLoop = animTr == null ? void 0 : animTr.loop;
4754
+ const withLoop = (kfs2) => {
4755
+ const block = { keyframes: kfs2 };
4756
+ if (srcLoop !== void 0) block.loop = srcLoop;
4757
+ return block;
4758
+ };
4759
+ let n = inner;
4760
+ if (translateKfs.length) n = { type: "g", animate: { transform: withLoop(translateKfs) }, children: [n] };
4761
+ if (rotateKfs.length) n = { type: "g", animate: { transform: withLoop(rotateKfs) }, children: [n] };
4762
+ if (scaleKfs.length) n = { type: "g", animate: { transform: withLoop(scaleKfs) }, children: [n] };
4763
+ return n;
4764
+ }
4765
+ function nodeHasBodyTransform(node) {
4766
+ if (typeof node.transform === "string") return true;
4767
+ if (node.transform && typeof node.transform === "object") return true;
4768
+ return hasAnimateTransform(node);
4769
+ }
4770
+ function hasAnimateTransform(node) {
4771
+ const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4772
+ return !!(animate && animate.transform);
4773
+ }
4774
+ function readTransformationFromBody(node) {
4775
+ if (typeof node.transform === "string") {
4776
+ const parts = parseTransformStringToParts(node.transform);
4777
+ if (!parts) return void 0;
4778
+ const out = {};
4779
+ if (parts.translate) out.translate = parts.translate;
4780
+ if (parts.rotate !== void 0) out.rotate = parts.rotate;
4781
+ if (parts.scale) out.scale = { value: parts.scale };
4782
+ if (parts.origin) out.origin = parts.origin;
4783
+ return Object.keys(out).length ? out : void 0;
4784
+ }
4785
+ if (node.transform && typeof node.transform === "object" && !node.transform.keyframes) {
4786
+ const wrapped = node.transform.value;
4787
+ const value = wrapped && typeof wrapped === "object" ? wrapped : node.transform;
4788
+ if (value && typeof value === "object") {
4789
+ const out = {};
4790
+ if (Array.isArray(value.translate)) out.translate = value.translate;
4791
+ if (typeof value.rotate === "number") out.rotate = value.rotate;
4792
+ if (typeof value.skew === "number") out.skew = value.skew;
4793
+ if (Array.isArray(value.scale)) out.scale = { value: value.scale };
4794
+ if (Array.isArray(value.origin)) out.origin = value.origin;
4795
+ return Object.keys(out).length ? out : void 0;
4796
+ }
4797
+ }
4798
+ return void 0;
4799
+ }
4800
+ function parseTransformStringToParts(s) {
4801
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
4802
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
4803
+ let m;
4804
+ const ops = [];
4805
+ while ((m = re.exec(s)) !== null) {
4806
+ const args = m[2].split(/[\s,]+/).filter((a) => a.length > 0).map(Number);
4807
+ ops.push({ name: m[1], args });
4808
+ }
4809
+ if (!ops.length) return void 0;
4810
+ const last = ops[ops.length - 1];
4811
+ if (last.name === "translate") {
4812
+ for (let j = ops.length - 2; j >= 0; j--) {
4813
+ const cand = ops[j];
4814
+ if (cand.name !== "translate") continue;
4815
+ const ox = (_a2 = cand.args[0]) != null ? _a2 : 0;
4816
+ const oy = (_b = cand.args[1]) != null ? _b : 0;
4817
+ const lx = (_c = last.args[0]) != null ? _c : 0;
4818
+ const ly = (_d = last.args[1]) != null ? _d : 0;
4819
+ if (lx !== -ox || ly !== -oy) continue;
4820
+ const out2 = {};
4821
+ out2.origin = [ox, oy];
4822
+ for (let k = 0; k < j; k++) {
4823
+ if (ops[k].name === "translate") {
4824
+ const tx = (_e = ops[k].args[0]) != null ? _e : 0;
4825
+ const ty = (_f = ops[k].args[1]) != null ? _f : 0;
4826
+ out2.translate = out2.translate ? [out2.translate[0] + tx, out2.translate[1] + ty] : [tx, ty];
4827
+ }
4828
+ }
4829
+ for (let k = j + 1; k < ops.length - 1; k++) {
4830
+ const op = ops[k];
4831
+ if (op.name === "rotate") out2.rotate = ((_g = out2.rotate) != null ? _g : 0) + ((_h = op.args[0]) != null ? _h : 0);
4832
+ else if (op.name === "scale") {
4833
+ const sx = (_i = op.args[0]) != null ? _i : 1;
4834
+ const sy = op.args.length > 1 ? op.args[1] : sx;
4835
+ out2.scale = out2.scale ? [out2.scale[0] * sx, out2.scale[1] * sy] : [sx, sy];
4836
+ }
4837
+ }
4838
+ return out2;
4839
+ }
4840
+ }
4841
+ let translate;
4842
+ let rotate;
4843
+ let scale;
4844
+ for (const op of ops) {
4845
+ if (op.name === "translate") {
4846
+ const dx = (_j = op.args[0]) != null ? _j : 0;
4847
+ const dy = (_k = op.args[1]) != null ? _k : 0;
4848
+ translate = translate ? [translate[0] + dx, translate[1] + dy] : [dx, dy];
4849
+ } else if (op.name === "rotate") {
4850
+ rotate = (rotate != null ? rotate : 0) + ((_l = op.args[0]) != null ? _l : 0);
4851
+ } else if (op.name === "scale") {
4852
+ const sx = (_m = op.args[0]) != null ? _m : 1;
4853
+ const sy = op.args.length > 1 ? op.args[1] : sx;
4854
+ scale = scale ? [scale[0] * sx, scale[1] * sy] : [sx, sy];
4855
+ }
4856
+ }
4857
+ const out = {};
4858
+ if (translate) out.translate = translate;
4859
+ if (rotate !== void 0) out.rotate = rotate;
4860
+ if (scale) out.scale = scale;
4861
+ return Object.keys(out).length ? out : void 0;
4862
+ }
4863
+ function wrapAncestorChainCompensation(inner, maskedNode, sourceId, ctx, includeTargetOwn) {
4864
+ const sourceNode = ctx.idMap.get(sourceId);
4865
+ const targetAncestors = ctx.maskAncestorChains.get(maskedNode) || [];
4866
+ const targetOwn = includeTargetOwn ? extractTranslateOnly(maskedNode, ctx) : void 0;
4867
+ const targetChain = targetOwn ? [...targetAncestors, targetOwn] : targetAncestors;
4868
+ const sourceChain = sourceNode && ctx.maskAncestorChains.get(sourceNode) || [];
4869
+ if (!targetChain.length && !sourceChain.length) return inner;
4870
+ const times = /* @__PURE__ */ new Set();
4871
+ for (const a of targetChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);
4872
+ for (const a of sourceChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);
4873
+ const animated = times.size > 0;
4874
+ if (!animated) {
4875
+ const tgt = sumStaticTranslate(targetChain);
4876
+ const src = sumStaticTranslate(sourceChain);
4877
+ const dx = src[0] - tgt[0];
4878
+ const dy = src[1] - tgt[1];
4879
+ if (dx === 0 && dy === 0) return inner;
4880
+ return { type: "g", transform: "translate(" + dx + "," + dy + ")", children: [inner] };
4881
+ }
4882
+ const sortedTimes = Array.from(times).sort((a, b) => a - b);
4883
+ const keyframes = sortedTimes.map((t) => {
4884
+ const tgt = sumTranslateAt(targetChain, t);
4885
+ const src = sumTranslateAt(sourceChain, t);
4886
+ return { time: t, value: { translate: [src[0] - tgt[0], src[1] - tgt[1]] } };
4887
+ });
4888
+ return { type: "g", animate: { transform: { keyframes } }, children: [inner] };
4889
+ }
4890
+ function sumStaticTranslate(chain) {
4891
+ let x = 0, y = 0;
4892
+ for (const a of chain) {
4893
+ if (a.translate) {
4894
+ x += a.translate[0];
4895
+ y += a.translate[1];
4896
+ }
4897
+ }
4898
+ return [x, y];
4899
+ }
4900
+ function sumTranslateAt(chain, t) {
4901
+ let x = 0, y = 0;
4902
+ for (const a of chain) {
4903
+ if (a.translateKeyframes && a.translateKeyframes.length) {
4904
+ const v = interpKfs(a.translateKeyframes, t);
4905
+ x += v[0];
4906
+ y += v[1];
4907
+ } else if (a.translate) {
4908
+ x += a.translate[0];
4909
+ y += a.translate[1];
4910
+ }
4911
+ }
4912
+ return [x, y];
4913
+ }
4914
+ function interpKfs(kfs, t) {
4915
+ if (t <= kfs[0].time) return kfs[0].value;
4916
+ if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;
4917
+ for (let i = 1; i < kfs.length; i++) {
4918
+ if (t <= kfs[i].time) {
4919
+ const prev = kfs[i - 1];
4920
+ const cur = kfs[i];
4921
+ const a = (t - prev.time) / (cur.time - prev.time);
4922
+ return [prev.value[0] + (cur.value[0] - prev.value[0]) * a, prev.value[1] + (cur.value[1] - prev.value[1]) * a];
4923
+ }
4924
+ }
4925
+ return kfs[kfs.length - 1].value;
4926
+ }
4927
+ function collectMaskAncestorChains(root, ctx) {
4928
+ const interestingNodes = /* @__PURE__ */ new Set();
4929
+ const collectInterestingNodes = (n) => {
4930
+ var _a2, _b;
4931
+ const maskSourceId = stripHash((_b = (_a2 = n.effects) == null ? void 0 : _a2.maskedBy) == null ? void 0 : _b.source);
4932
+ if (typeof maskSourceId === "string") {
4933
+ interestingNodes.add(n);
4934
+ const sourceNode = ctx.idMap.get(maskSourceId);
4935
+ if (sourceNode) interestingNodes.add(sourceNode);
4936
+ }
4937
+ if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);
4938
+ };
4939
+ collectInterestingNodes(root);
4940
+ if (interestingNodes.size === 0) return;
4941
+ const walk = (node, chain) => {
4942
+ if (interestingNodes.has(node)) ctx.maskAncestorChains.set(node, chain);
4943
+ if (Array.isArray(node.children)) {
4944
+ const own = extractTranslateOnly(node, ctx);
4945
+ const next = own ? [...chain, own] : chain;
4946
+ for (const ch of node.children) walk(ch, next);
4947
+ }
4948
+ };
4949
+ walk(root, []);
4950
+ }
4951
+ function extractTranslateOnly(node, ctx) {
4952
+ var _a2, _b, _c;
4953
+ const tr = node.transform;
4954
+ const animateBlock = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate.transform : void 0;
4955
+ if (tr === void 0 && !animateBlock) return void 0;
4956
+ const out = {};
4957
+ if (typeof tr === "string") {
4958
+ const parts = parseTranslateOnlyFromString(tr, ctx);
4959
+ if (parts) out.translate = parts;
4960
+ } else if (tr && typeof tr === "object" && !tr.keyframes) {
4961
+ const wrapped = tr.value;
4962
+ const value = wrapped && typeof wrapped === "object" ? wrapped : tr;
4963
+ if (value && typeof value === "object" && Array.isArray(value.translate)) {
4964
+ out.translate = [value.translate[0] || 0, value.translate[1] || 0];
4965
+ }
4966
+ if (value && (value.rotate !== void 0 || value.scale !== void 0 || value.skew !== void 0)) {
4967
+ ctx.warnings.push("maskedBy ancestor: non-translate transform parts ignored (rotate/scale not yet supported)");
4968
+ }
4969
+ }
4970
+ if (animateBlock && Array.isArray(animateBlock.keyframes)) {
4971
+ const kfs = animateBlock.keyframes;
4972
+ const translateKfs = [];
4973
+ for (const kf of kfs) {
4974
+ const v = (_a2 = kf.value) != null ? _a2 : kf.v;
4975
+ const t = (_c = (_b = kf.time) != null ? _b : kf.t) != null ? _c : 0;
4976
+ if (v && typeof v === "object" && Array.isArray(v.translate)) {
4977
+ translateKfs.push({ time: t, value: [v.translate[0] || 0, v.translate[1] || 0] });
4978
+ if (v.rotate !== void 0 || v.scale !== void 0 || v.skew !== void 0) {
4979
+ ctx.warnings.push("maskedBy ancestor: animated non-translate parts ignored");
4980
+ }
4981
+ }
4982
+ }
4983
+ if (translateKfs.length) out.translateKeyframes = translateKfs;
4984
+ }
4985
+ return out.translate || out.translateKeyframes ? out : void 0;
4986
+ }
4987
+ function parseTranslateOnlyFromString(s, ctx) {
4988
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
4989
+ let m;
4990
+ let x = 0, y = 0;
4991
+ let seen = false;
4992
+ let droppedNonTranslate = false;
4993
+ while ((m = re.exec(s)) !== null) {
4994
+ const name = m[1];
4995
+ const args = m[2].split(/[\s,]+/).filter((a) => a.length > 0).map(Number);
4996
+ if (name === "translate") {
4997
+ x += args[0] || 0;
4998
+ y += args[1] || 0;
4999
+ seen = true;
5000
+ } else {
5001
+ droppedNonTranslate = true;
5002
+ }
5003
+ }
5004
+ if (droppedNonTranslate) ctx.warnings.push("maskedBy ancestor: non-translate transform in string ignored: " + s);
5005
+ return seen ? [x, y] : void 0;
5006
+ }
5007
+
5008
+ // src/effects/reference/refEffect.ts
5009
+ function applyRefHref(node, clone2, ctx) {
5010
+ if (!clone2) return;
5011
+ const sourceId = stripHash(clone2.source);
5012
+ if (!sourceId) {
5013
+ if (clone2.without === PxCloneWithout.translate) ctx.errors.push("clone: content ref missing `source`");
5014
+ return;
5015
+ }
5016
+ const targetId = clone2.without === PxCloneWithout.translate ? ctx.contentRefInnerIds.get(sourceId) || sourceId : sourceId;
5017
+ node.href = "#" + targetId;
5018
+ }
5019
+ function applyRefAndTransformationEffect(node, clone2, transformBy, ctx) {
5020
+ applyRefHref(node, clone2, ctx);
5021
+ return applyTransformByEffect(node, transformBy, ctx);
5022
+ }
5023
+
5024
+ // src/effects/transform/repeaterEffect.ts
5025
+ function applyRepeaterEffect(node, fx, ctx) {
5026
+ var _a2, _b;
5027
+ if (!fx) return node;
5028
+ const copies = (_a2 = fx.copies) != null ? _a2 : 1;
5029
+ if (copies < 1) {
5030
+ ctx.errors.push("repeater.copies invalid: " + fx.copies);
5031
+ return node;
5032
+ }
5033
+ const sharedTransform = node.transform;
5034
+ const sharedAnimTransform = (_b = node.animate) == null ? void 0 : _b.transform;
5035
+ const base = clone(node);
5036
+ delete base.transform;
5037
+ if (base.animate) {
5038
+ delete base.animate.transform;
5039
+ if (Object.keys(base.animate).length === 0) delete base.animate;
5040
+ }
5041
+ delete base.id;
5042
+ const children = [base];
5043
+ for (let i = 1; i < copies; i++) {
5044
+ const baseClone = clone(base);
5045
+ const synthFx = synthesisePerCopyFx(fx, i);
5046
+ const wrapped = synthFx ? applyTransformByEffect(baseClone, synthFx, ctx) : baseClone;
5047
+ children.push(wrapped);
5048
+ }
5049
+ const wrapper = { type: "g", children };
5050
+ if (node.id) wrapper.id = node.id;
5051
+ if (sharedTransform !== void 0) wrapper.transform = sharedTransform;
5052
+ if (sharedAnimTransform !== void 0) wrapper.animate = { transform: sharedAnimTransform };
5053
+ return wrapper;
5054
+ }
5055
+ function synthesisePerCopyFx(fx, i) {
5056
+ const out = {};
5057
+ if (fx.translate !== void 0) {
5058
+ out.translate = mapAnimatable(fx.translate, (v) => [v[0] * i, v[1] * i]);
5059
+ }
5060
+ if (fx.rotate !== void 0) {
5061
+ out.rotate = mapAnimatable(fx.rotate, (v) => v * i);
5062
+ }
5063
+ if (fx.skew !== void 0) {
5064
+ out.skew = mapAnimatable(fx.skew, (v) => v * i);
5065
+ }
5066
+ if (fx.scale !== void 0) {
5067
+ out.scale = synthesiseScale(fx.scale, i);
5068
+ }
5069
+ if (fx.origin !== void 0) {
5070
+ out.origin = fx.origin;
5071
+ }
5072
+ return Object.keys(out).length ? out : void 0;
5073
+ }
5074
+ function mapAnimatable(raw, fn, wrapStatic = false) {
5075
+ const read = readAnimatable(raw);
5076
+ if (read.kind === "absent" /* Absent */) return raw;
5077
+ if (read.kind === "static" /* Static */) {
5078
+ const mapped = fn(read.value);
5079
+ const wasRawStatic = typeof raw === "number" || Array.isArray(raw);
5080
+ return wasRawStatic && !wrapStatic ? mapped : { value: mapped };
5081
+ }
5082
+ const out = {
5083
+ keyframes: read.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
5084
+ };
5085
+ if (read.loop !== void 0) out.loop = read.loop;
5086
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
5087
+ if (read.base !== void 0) out.value = fn(read.base);
5088
+ return out;
5089
+ }
5090
+ function synthesiseScale(raw, i) {
5091
+ const scalePower = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
5092
+ return mapAnimatable(raw, scalePower, true);
5093
+ }
5094
+
5095
+ // src/effects/reference/retimeEffect.ts
5096
+ var RETIME_MATERIALIZATION_MODE_INLINE_G = false;
5097
+ function asRetime(r) {
5098
+ var _a2, _b;
5099
+ return { start: (_a2 = r.start) != null ? _a2 : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
5100
+ }
5101
+ var CROP_EDGE_MS = 1;
5102
+ function applyTimeCrop(useNode, crop, ctx) {
5103
+ const [start, end] = crop;
5104
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
5105
+ ctx.warnings.push("retime: timeCrop is not a pair of finite numbers \u2014 ignored");
5106
+ return;
5107
+ }
5108
+ const keyframes = end <= start ? [{ time: 0, value: 0 }] : [
5109
+ ...start > 0 ? [{ time: Math.max(0, start - CROP_EDGE_MS), value: 0 }] : [],
5110
+ { time: Math.max(0, start), value: 1 },
5111
+ { time: end, value: 1 },
5112
+ { time: end + CROP_EDGE_MS, value: 0 }
5113
+ ];
5114
+ const inner = __spreadValues({}, useNode);
5115
+ for (const k of Object.keys(useNode)) delete useNode[k];
5116
+ useNode.type = "g";
5117
+ useNode.children = [inner];
5118
+ useNode.animate = { opacity: { keyframes } };
5119
+ }
5120
+ function concatRetime(child, parent) {
5121
+ return {
5122
+ start: parent.start + parent.stretch * child.start,
5123
+ stretch: parent.stretch * child.stretch
5124
+ };
5125
+ }
5126
+ function readCloneRetime(n) {
5127
+ var _a2, _b;
5128
+ return (_b = (_a2 = n.effects) == null ? void 0 : _a2.clone) == null ? void 0 : _b.retime;
5129
+ }
5130
+ function clearCloneRetime(n) {
5131
+ var _a2;
5132
+ const clone2 = (_a2 = n.effects) == null ? void 0 : _a2.clone;
5133
+ if (!clone2) return;
5134
+ delete clone2.retime;
5135
+ if (Object.keys(clone2).length === 0) delete n.effects.clone;
5136
+ if (n.effects && Object.keys(n.effects).length === 0) delete n.effects;
5137
+ }
5138
+ function applyAllRetimeEffects(root, ctx) {
5139
+ ctx.idMap.clear();
5140
+ indexById(root, ctx.idMap);
5141
+ const sites = [];
5142
+ const collect = (n) => {
5143
+ var _a2;
5144
+ if (readCloneRetime(n)) sites.push(n);
5145
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(collect);
5146
+ };
5147
+ collect(root);
5148
+ const reachCount = /* @__PURE__ */ new Map();
5149
+ for (const site of sites) {
5150
+ let count = 0;
5151
+ const visited = /* @__PURE__ */ new Set();
5152
+ const walk = (n) => {
5153
+ var _a2;
5154
+ if (!n) return;
5155
+ if (n !== site && readCloneRetime(n)) count++;
5156
+ if (n.type === "use" && n.href) {
5157
+ const id = stripHash(n.href);
5158
+ if (id && !visited.has(id)) {
5159
+ visited.add(id);
5160
+ walk(ctx.idMap.get(id));
5161
+ }
5162
+ }
5163
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walk);
5164
+ };
5165
+ const rootId = stripHash(site.href);
5166
+ if (rootId) {
5167
+ visited.add(rootId);
5168
+ walk(ctx.idMap.get(rootId));
5169
+ }
5170
+ reachCount.set(site, count);
5171
+ }
5172
+ sites.sort((a, b) => {
5173
+ var _a2, _b;
5174
+ return ((_a2 = reachCount.get(b)) != null ? _a2 : 0) - ((_b = reachCount.get(a)) != null ? _b : 0);
5175
+ });
5176
+ for (const useNode of sites) {
5177
+ const retime = readCloneRetime(useNode);
5178
+ if (!retime) continue;
5179
+ const crop = retime.timeCrop;
5180
+ clearCloneRetime(useNode);
5181
+ materializeRetime(useNode, asRetime(retime), ctx);
5182
+ if (crop) applyTimeCrop(useNode, crop, ctx);
5183
+ }
5184
+ }
5185
+ function materializeRetime(useNode, retime, ctx) {
5186
+ const targetId = stripHash(useNode.href);
5187
+ if (!targetId) {
5188
+ ctx.errors.push("retime: <use> has no href to follow");
5189
+ return;
5190
+ }
5191
+ const chainRootId = buildChainClone(targetId, retime, ctx, /* @__PURE__ */ new Set());
5192
+ if (!chainRootId) return;
5193
+ if (RETIME_MATERIALIZATION_MODE_INLINE_G) {
5194
+ const cloneNode = ctx.idMap.get(chainRootId);
5195
+ useNode.type = "g";
5196
+ delete useNode.href;
5197
+ useNode.children = [cloneNode];
5198
+ applyUseOffsetToG(useNode);
5199
+ ctx.defs = ctx.defs.filter((d) => d !== cloneNode);
5200
+ } else {
5201
+ useNode.href = "#" + chainRootId;
5202
+ }
5203
+ }
5204
+ function buildChainClone(targetId, accum, ctx, chain) {
5205
+ if (chain.has(targetId)) {
5206
+ ctx.errors.push('retime: loop via "' + targetId + '"');
5207
+ return void 0;
5208
+ }
5209
+ const target = ctx.idMap.get(targetId);
5210
+ if (!target) {
5211
+ ctx.warnings.push('retime: target "' + targetId + '" not found');
5212
+ return void 0;
5213
+ }
5214
+ const cloneNode = clone(target);
5215
+ regenerateIdsInClone(cloneNode, ctx);
5216
+ if (target.type === "use") {
5217
+ remapKeyframeTimesOnly(cloneNode, accum.start, accum.stretch);
5218
+ } else {
5219
+ remapKeyframeTimes(cloneNode, accum.start, accum.stretch);
5220
+ }
5221
+ clearCloneRetime(cloneNode);
5222
+ if (target.type === "use" && target.href) {
5223
+ const subId = stripHash(target.href);
5224
+ if (subId) {
5225
+ const innerRetime = readCloneRetime(target);
5226
+ const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;
5227
+ const subChain = new Set(chain);
5228
+ subChain.add(targetId);
5229
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
5230
+ if (subId2) cloneNode.href = "#" + subId2;
5231
+ }
5232
+ } else {
5233
+ materializeNestedRetimeUses(cloneNode, accum, ctx, chain, targetId);
5234
+ }
5235
+ ctx.defs.push(cloneNode);
5236
+ if (typeof cloneNode.id === "string") ctx.idMap.set(cloneNode.id, cloneNode);
5237
+ return typeof cloneNode.id === "string" ? cloneNode.id : void 0;
5238
+ }
5239
+ function materializeNestedRetimeUses(node, accum, ctx, chain, parentTargetId) {
5240
+ const visit = (n) => {
5241
+ var _a2;
5242
+ const retime = readCloneRetime(n);
5243
+ if (n.type === "use" && retime && n.href) {
5244
+ const subId = stripHash(n.href);
5245
+ if (subId) {
5246
+ const subAccum = concatRetime(asRetime(retime), accum);
5247
+ const subChain = new Set(chain);
5248
+ subChain.add(parentTargetId);
5249
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
5250
+ if (subId2) n.href = "#" + subId2;
5251
+ }
5252
+ clearCloneRetime(n);
5253
+ }
5254
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(visit);
5255
+ };
5256
+ visit(node);
5257
+ }
5258
+ function remapKeyframeTimes(node, start, stretch) {
5259
+ var _a2;
5260
+ remapKeyframeTimesOnly(node, start, stretch);
5261
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((c) => remapKeyframeTimes(c, start, stretch));
5262
+ }
5263
+ function remapKeyframeTimesOnly(node, start, stretch) {
5264
+ const remap2 = (kfs) => {
5265
+ for (const kf of kfs) if (typeof kf.time === "number") kf.time = start + kf.time * stretch;
5266
+ };
5267
+ if (node.transform && typeof node.transform === "object" && Array.isArray(node.transform.keyframes)) {
5268
+ remap2(node.transform.keyframes);
5269
+ }
5270
+ if (node.animate && typeof node.animate === "object") {
5271
+ for (const prop of Object.keys(node.animate)) {
5272
+ const anim = node.animate[prop];
5273
+ if (anim && Array.isArray(anim.keyframes)) remap2(anim.keyframes);
5274
+ }
5275
+ }
5276
+ }
5277
+
5278
+ // src/effects/stroke/strokeTrimEffect.ts
5279
+ function applyStrokeTrimEffect(node, strokeTrim, ctx) {
5280
+ if (!strokeTrim) return node;
5281
+ const combined = strokeTrim.subPaths === PxStrokeTrimSubPaths.combined;
5282
+ const leafEntries = [];
5283
+ const measure = (n) => {
5284
+ if (Array.isArray(n.children) && n.children.length > 0) {
5285
+ for (const ch of n.children) measure(ch);
5286
+ return;
5287
+ }
5288
+ const d = typeof n.d === "string" ? n.d : shapeToPathD(n);
5289
+ if (d === void 0) return;
5290
+ const subpaths = parseSvgPathToBezier(d);
5291
+ if (!subpaths.length) return;
5292
+ const entry = { leaf: n, subpaths: [] };
5293
+ for (const sp of subpaths) {
5294
+ const lengthPx = pxBezierPathLength(sp);
5295
+ entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: 0 });
5296
+ }
5297
+ leafEntries.push(entry);
5298
+ };
5299
+ measure(node);
5300
+ if (!leafEntries.length) return node;
5301
+ let acc = 0;
5302
+ const iterOrder = combined ? [...leafEntries].reverse() : leafEntries;
5303
+ for (const entry of iterOrder) {
5304
+ for (const sp of entry.subpaths) {
5305
+ if (!combined) acc = 0;
5306
+ sp.startOffsetPx = acc;
5307
+ acc += sp.lengthPx;
5308
+ }
5309
+ }
5310
+ const chainLengthPx = acc;
5311
+ if (combined && chainLengthPx < 1e-3) return node;
5312
+ const offsetReadRaw = readAnimatable(strokeTrim.offset);
5313
+ const offsetRead = offsetReadRaw.kind === "absent" /* Absent */ ? { kind: "static" /* Static */, value: 0 } : offsetReadRaw;
5314
+ const rangeReadRaw = readRangeWithCrossings(strokeTrim.range);
5315
+ const rangeRead = rangeReadRaw.kind === "absent" /* Absent */ ? { kind: "static" /* Static */, value: [0, 1] } : rangeReadRaw;
5316
+ const offsetValues = readScalarValues(offsetRead);
5317
+ const minOffset = offsetValues.length ? Math.min(...offsetValues) : 0;
5318
+ const maxOffset = offsetValues.length ? Math.max(...offsetValues) : 0;
5319
+ const minMaxOffset = [minOffset, maxOffset];
5320
+ if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {
5321
+ const entry = leafEntries[0];
5322
+ const sp = entry.subpaths[0];
5323
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
5324
+ if (pathLengthPx < 1e-3) return node;
5325
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
5326
+ return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);
5327
+ }
5328
+ const replacements = /* @__PURE__ */ new Map();
5329
+ for (const entry of leafEntries) {
5330
+ const newChildren = [];
5331
+ for (const sp of entry.subpaths) {
5332
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
5333
+ if (pathLengthPx < 1e-3) continue;
5334
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
5335
+ newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));
5336
+ }
5337
+ replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));
5338
+ }
5339
+ const swap = (n) => {
5340
+ const r = replacements.get(n);
5341
+ if (r) return r;
5342
+ if (Array.isArray(n.children) && n.children.length > 0) {
5343
+ return __spreadProps(__spreadValues({}, n), { children: n.children.map(swap) });
5344
+ }
5345
+ return n;
5346
+ };
5347
+ return swap(node);
5348
+ }
5349
+ function wrapLeafAsGroup(leaf, children) {
5350
+ const wrapper = __spreadProps(__spreadValues({}, leaf), { type: "g", children });
5351
+ delete wrapper.d;
5352
+ delete wrapper.strokeDasharray;
5353
+ delete wrapper.strokeDashoffset;
5354
+ delete wrapper.effects;
5355
+ return wrapper;
5356
+ }
5357
+ function buildSubpathNodes(leaf, subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx) {
5358
+ const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);
5359
+ const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);
5360
+ const dashOffsetAttr = computeAnimAttr(offsetRead, offsetToDashOffset);
5361
+ const dashArrayAttr = computeAnimAttr(rangeRead, rangeToDasharray);
5362
+ const strokeOpacityAttr = computeOpacityFromRange(rangeRead);
5363
+ const dStr = bezierToSvgPath(subpath);
5364
+ const base = makeBareSubpath(dStr);
5365
+ applyAttr(base, "strokeDasharray", dashArrayAttr);
5366
+ applyAttr(base, "strokeDashoffset", dashOffsetAttr);
5367
+ applyAttr(base, "strokeOpacity", strokeOpacityAttr);
5368
+ return [base];
5369
+ }
5370
+ function collapseLeafWithTrim(leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead) {
5371
+ const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);
5372
+ const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);
5373
+ const node = __spreadValues({}, leaf);
5374
+ delete node.effects;
5375
+ applyAttr(node, "strokeDasharray", computeAnimAttr(rangeRead, rangeToDasharray));
5376
+ applyAttr(node, "strokeDashoffset", computeAnimAttr(offsetRead, offsetToDashOffset));
5377
+ applyAttr(node, "strokeOpacity", computeOpacityFromRange(rangeRead));
5378
+ return node;
5379
+ }
5380
+ function makeBareSubpath(dStr) {
5381
+ return { type: "path", d: dStr };
5382
+ }
5383
+ var SMALL_PADDING_PX = 1;
5384
+ function makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset) {
5385
+ const [minIdx] = getOffsetIndexRange(minMaxOffset);
5386
+ return (offsetVal) => pathLengthPx * (-offsetVal - minIdx + startOffsetPct) + SMALL_PADDING_PX;
5387
+ }
5388
+ function makeRangeToDasharray(pathLengthPx, minMaxOffset) {
5389
+ const [minIdx, maxIdx] = getOffsetIndexRange(minMaxOffset);
5390
+ const repeats = maxIdx - minIdx + 1;
5391
+ return (rangeVal) => {
5392
+ const a = clamp(rangeVal[0], 0, 1);
5393
+ const b = clamp(rangeVal[1], 0, 1);
5394
+ const minR = Math.min(a, b);
5395
+ const maxR = Math.max(a, b);
5396
+ const out = [0];
5397
+ let gap = SMALL_PADDING_PX;
5398
+ for (let i = 0; i < repeats; i++) {
5399
+ out.push(gap + minR * pathLengthPx);
5400
+ out.push((maxR - minR) * pathLengthPx);
5401
+ gap = (1 - maxR) * pathLengthPx;
5402
+ }
5403
+ out.push(gap + SMALL_PADDING_PX);
5404
+ return out;
5405
+ };
5406
+ }
5407
+ function getOffsetIndexRange(minMaxOffset) {
5408
+ return [
5409
+ Math.floor(Math.min(-minMaxOffset[0], -minMaxOffset[1])),
5410
+ Math.ceil(Math.max(-minMaxOffset[0], -minMaxOffset[1]))
5411
+ ];
5412
+ }
5413
+ function readScalarValues(r) {
5414
+ if (r.kind === "absent" /* Absent */) return [];
5415
+ if (r.kind === "static" /* Static */) return [r.value];
5416
+ const out = [];
5417
+ for (const kf of r.keyframes) {
5418
+ const v = keyframeValue(kf);
5419
+ if (typeof v === "number") out.push(v);
5420
+ }
5421
+ return out;
5422
+ }
5423
+ function computeAnimAttr(read, map) {
5424
+ if (read.kind === "absent" /* Absent */) return void 0;
5425
+ if (read.kind === "static" /* Static */) return { kind: "static" /* Static */, value: map(read.value) };
5426
+ return {
5427
+ kind: "animated" /* Animated */,
5428
+ keyframes: read.keyframes.map((kf) => ({
5429
+ time: keyframeTime(kf),
5430
+ value: map(keyframeValue(kf)),
5431
+ easing: keyframeEasing(kf)
5432
+ })),
5433
+ loop: read.loop
5434
+ };
5435
+ }
5436
+ function applyAttr(node, attrName, attr) {
5437
+ if (!attr) return;
5438
+ writeAnimatableChannel(node, attrName, attr);
5439
+ }
5440
+ var OPACITY_STEP_MS = 10;
5441
+ function computeOpacityFromRange(rangeRead) {
5442
+ const hide = (v) => v[0] === v[1];
5443
+ if (rangeRead.kind === "absent" /* Absent */) return void 0;
5444
+ if (rangeRead.kind === "static" /* Static */) return hide(rangeRead.value) ? { kind: "static" /* Static */, value: 0 } : void 0;
5445
+ const kfs = rangeRead.keyframes;
5446
+ let anyHide = false;
5447
+ let allHide = true;
5448
+ for (const kf of kfs) {
5449
+ if (hide(keyframeValue(kf))) anyHide = true;
5450
+ else allHide = false;
5451
+ }
5452
+ if (!anyHide) return void 0;
5453
+ if (allHide) return { kind: "static" /* Static */, value: 0 };
5454
+ const out = [];
5455
+ for (let i = 0; i < kfs.length; i++) {
5456
+ const kf = kfs[i];
5457
+ const prevKf = i > 0 ? kfs[i - 1] : void 0;
5458
+ const nextKf = i < kfs.length - 1 ? kfs[i + 1] : void 0;
5459
+ const t = keyframeTime(kf);
5460
+ const thisHide = hide(keyframeValue(kf));
5461
+ const prevHide = prevKf ? thisHide && hide(keyframeValue(prevKf)) : thisHide;
5462
+ const nextHide = nextKf ? thisHide && hide(keyframeValue(nextKf)) : thisHide;
5463
+ if (prevHide && !nextHide) {
5464
+ out.push({ time: t, value: 0 });
5465
+ out.push({ time: t + OPACITY_STEP_MS, value: 1 });
5466
+ } else if (!prevHide && nextHide) {
5467
+ out.push({ time: t - OPACITY_STEP_MS, value: 1 });
5468
+ out.push({ time: t, value: 0 });
5469
+ }
5470
+ }
5471
+ if (out.length <= 1) return void 0;
5472
+ return { kind: "animated" /* Animated */, keyframes: out };
5473
+ }
5474
+ function readRangeWithCrossings(raw) {
5475
+ const r = readAnimatable(raw);
5476
+ if (r.kind !== "animated" /* Animated */) return r;
5477
+ const kfs = r.keyframes.map((kf) => ({
5478
+ time: keyframeTime(kf),
5479
+ value: keyframeValue(kf),
5480
+ easing: keyframeEasing(kf)
5481
+ }));
5482
+ const hasReverse = kfs.some((kf) => kf.value[0] > kf.value[1]);
5483
+ if (!hasReverse) {
5484
+ return {
5485
+ kind: "animated" /* Animated */,
5486
+ keyframes: kfs.map((kf) => ({ time: kf.time, value: kf.value, easing: kf.easing }))
5487
+ };
5488
+ }
5489
+ const crossingTimes = [];
5490
+ for (let i = 1; i < kfs.length; i++) {
5491
+ const prev = kfs[i - 1];
5492
+ const cur = kfs[i];
5493
+ const dPrev = prev.value[1] - prev.value[0];
5494
+ const dCur = cur.value[1] - cur.value[0];
5495
+ if (dPrev * dCur < 0) {
5496
+ const t = bisectionForRangeCrossing(prev, cur);
5497
+ if (t !== null && t > prev.time && t < cur.time) {
5498
+ crossingTimes.push(Math.round(t));
5499
+ }
5500
+ }
5501
+ }
5502
+ const uniqueTs = Array.from(new Set(crossingTimes)).sort((a, b) => a - b);
5503
+ const out = [];
5504
+ let j = 0;
5505
+ for (const kf of kfs) {
5506
+ while (j < uniqueTs.length && uniqueTs[j] < kf.time) {
5507
+ const t = uniqueTs[j++];
5508
+ const v2 = interpolateRangeAt(kfs, t);
5509
+ const m = (v2[0] + v2[1]) / 2;
5510
+ out.push({ time: t, value: [m, m] });
5511
+ }
5512
+ const v = kf.value[0] > kf.value[1] ? [kf.value[1], kf.value[0]] : kf.value;
5513
+ out.push({ time: kf.time, value: v, easing: kf.easing });
5514
+ }
5515
+ while (j < uniqueTs.length) {
5516
+ const t = uniqueTs[j++];
5517
+ const v = interpolateRangeAt(kfs, t);
5518
+ const m = (v[0] + v[1]) / 2;
5519
+ out.push({ time: t, value: [m, m] });
5520
+ }
5521
+ return { kind: "animated" /* Animated */, keyframes: out };
5522
+ }
5523
+ function bisectionForRangeCrossing(prev, cur) {
5524
+ const f = (t) => {
5525
+ const a = (t - prev.time) / (cur.time - prev.time);
5526
+ const v0 = prev.value[0] + (cur.value[0] - prev.value[0]) * a;
5527
+ const v1 = prev.value[1] + (cur.value[1] - prev.value[1]) * a;
5528
+ return v1 - v0;
5529
+ };
5530
+ let lo = prev.time, hi = cur.time;
5531
+ let fLo = f(lo);
5532
+ if (fLo === 0) return lo;
5533
+ const fHi = f(hi);
5534
+ if (fHi === 0) return hi;
5535
+ if (fLo * fHi > 0) return null;
5536
+ for (let i = 0; i < 100; i++) {
5537
+ const mid = (lo + hi) / 2;
5538
+ const fMid = f(mid);
5539
+ if (fMid === 0 || Math.abs(hi - lo) < 1e-4) return mid;
5540
+ if (fLo * fMid < 0) {
5541
+ hi = mid;
5542
+ } else {
5543
+ lo = mid;
5544
+ fLo = fMid;
5545
+ }
5546
+ }
5547
+ return (lo + hi) / 2;
5548
+ }
5549
+ function interpolateRangeAt(kfs, t) {
5550
+ if (t <= kfs[0].time) return kfs[0].value;
5551
+ if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;
5552
+ for (let i = 1; i < kfs.length; i++) {
5553
+ if (t <= kfs[i].time) {
5554
+ const prev = kfs[i - 1];
5555
+ const cur = kfs[i];
5556
+ const a = (t - prev.time) / (cur.time - prev.time);
5557
+ return [
5558
+ prev.value[0] + (cur.value[0] - prev.value[0]) * a,
5559
+ prev.value[1] + (cur.value[1] - prev.value[1]) * a
5560
+ ];
5561
+ }
5562
+ }
5563
+ return kfs[kfs.length - 1].value;
5564
+ }
5565
+ function pxBezierPathLength(path) {
5566
+ const v = path.v;
5567
+ if (!v || v.length < 2) return 0;
5568
+ let total = 0;
5569
+ for (let i = 0; i < v.length - 1; i++) {
5570
+ total += segmentLength(path, i, i + 1);
5571
+ }
5572
+ if (path.c && v.length > 1) {
5573
+ total += segmentLength(path, v.length - 1, 0);
5574
+ }
5575
+ return total;
5576
+ }
5577
+ function segmentLength(path, from, to) {
5578
+ var _a2, _b, _c, _d;
5579
+ const v = path.v;
5580
+ const p0 = v[from];
5581
+ const p3 = v[to];
5582
+ const p1 = (_b = (_a2 = path.o) == null ? void 0 : _a2[from]) != null ? _b : p0;
5583
+ const p2 = (_d = (_c = path.i) == null ? void 0 : _c[to]) != null ? _d : p3;
5584
+ const lut = bezier2D_arcLengthLUT(p0, p1, p2, p3);
5585
+ return lut.ds[lut.ds.length - 1];
5586
+ }
5587
+ var ARC_KAPPA = 0.5522847498307936;
5588
+ function shapeToPathD(node) {
5589
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
5590
+ if (node.type === "rect") {
5591
+ const x = Number((_a2 = node.x) != null ? _a2 : 0), y = Number((_b = node.y) != null ? _b : 0);
5592
+ const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
5593
+ return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
5594
+ }
5595
+ if (node.type === "ellipse" || node.type === "circle") {
5596
+ const cx = Number((_e = node.cx) != null ? _e : 0), cy = Number((_f = node.cy) != null ? _f : 0);
5597
+ const rx = node.type === "circle" ? Number((_g = node.r) != null ? _g : 0) : Number((_h = node.rx) != null ? _h : 0);
5598
+ const ry = node.type === "circle" ? Number((_i = node.r) != null ? _i : 0) : Number((_j = node.ry) != null ? _j : 0);
5599
+ if (!(rx > 0) || !(ry > 0)) return void 0;
5600
+ const kx = rx * ARC_KAPPA, ky = ry * ARC_KAPPA;
5601
+ const c = (x1, y1, x2, y2, x, y) => "C" + x1 + "," + y1 + " " + x2 + "," + y2 + " " + x + "," + y;
5602
+ return "M" + (cx + rx) + "," + cy + c(cx + rx, cy + ky, cx + kx, cy + ry, cx, cy + ry) + c(cx - kx, cy + ry, cx - rx, cy + ky, cx - rx, cy) + c(cx - rx, cy - ky, cx - kx, cy - ry, cx, cy - ry) + c(cx + kx, cy - ry, cx + rx, cy - ky, cx + rx, cy) + "z";
5603
+ }
5604
+ return void 0;
5605
+ }
5606
+
5607
+ // src/effects/PlayerEffectsUtil.ts
5608
+ function materializeNodeEffects(root) {
5609
+ var _a2, _b;
5610
+ const ctx = {
5611
+ defs: [],
5612
+ warnings: [],
5613
+ errors: [],
5614
+ idMap: /* @__PURE__ */ new Map(),
5615
+ nextId: 0,
5616
+ contentRefInnerIds: /* @__PURE__ */ new Map(),
5617
+ maskAncestorChains: /* @__PURE__ */ new Map(),
5618
+ // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →
5619
+ // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5620
+ engine: resolveTimelineEngine((_a2 = getAnimatorConfig(root)) == null ? void 0 : _a2.engine),
5621
+ glyphs: (_b = getDefinitions(root)) == null ? void 0 : _b.fonts
5622
+ };
5623
+ const working = clone(root);
5624
+ indexById(working, ctx.idMap);
5625
+ identifyContentRefTargets(working, ctx, () => genId(ctx, "inner"));
5626
+ collectMaskAncestorChains(working, ctx);
5627
+ const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);
5628
+ const out = applyPlayerEffects_retime(afterPass1, ctx);
5629
+ spliceDefs(out, ctx.defs);
5630
+ return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
5631
+ }
5632
+ function applyPlayerEffects_exceptRetime(node, ctx) {
5633
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
5634
+ const fx = node.effects;
5635
+ const originalId = typeof node.id === "string" ? node.id : void 0;
5636
+ const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
5637
+ if (!fx && !innerIdForContentRef) return node;
5638
+ const { transformBy, repeater, maskedBy, clipPath, strokeTrim, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5639
+ if (fx) delete node.effects;
5640
+ let n = node;
5641
+ let consumedByGlyphs = false;
5642
+ if (text == null ? void 0 : text.useGlyphs) {
5643
+ if (textPath) {
5644
+ const pathD = typeof textPath.pathData === "string" ? textPath.pathData : void 0;
5645
+ const glyphed = applyTextGlyphsAlongPath(n, ctx, pathD, textPath.startOffset, textPath.textLength, textPath.pathOverflow);
5646
+ if (glyphed) {
5647
+ n = glyphed;
5648
+ consumedByGlyphs = true;
5649
+ }
5650
+ } else {
5651
+ n = applyTextGlyphsEffect(n, text, ctx);
5652
+ consumedByGlyphs = true;
5653
+ }
5654
+ }
5655
+ if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);
5656
+ n = applyFillGradientEffect(n, fillGradient, ctx);
5657
+ n = applyStrokeGradientEffect(n, strokeGradient, ctx);
5658
+ n = applyStrokeTrimEffect(n, strokeTrim, ctx);
5659
+ n = applyRepeaterEffect(n, repeater, ctx);
5660
+ n = applyMaskedByEffect(n, maskedBy, transformBy, ctx);
5661
+ n = applyClipPathEffect(n, clipPath, ctx);
5662
+ if (innerIdForContentRef) {
5663
+ applyRefHref(n, cloneFx, ctx);
5664
+ n = splitForContentRef(n, transformBy, originalId, innerIdForContentRef, ctx);
5665
+ } else {
5666
+ n = applyRefAndTransformationEffect(n, cloneFx, transformBy, ctx);
5667
+ }
5668
+ if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
5669
+ if (originalId) ctx.idMap.set(originalId, n);
5670
+ return n;
5671
+ }
5672
+ function applyPlayerEffects_retime(node, ctx) {
5673
+ applyAllRetimeEffects(node, ctx);
5674
+ return node;
5675
+ }
5676
+
5677
+ // src/materialize/PxOffsetPathMaterializer.ts
5678
+ function cubicAt(p0, c1, c2, p1, t) {
5679
+ const u = 1 - t;
5680
+ const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;
5681
+ return [
5682
+ a * p0[0] + b * c1[0] + c * c2[0] + d * p1[0],
5683
+ a * p0[1] + b * c1[1] + c * c2[1] + d * p1[1]
5684
+ ];
5685
+ }
5686
+ function cubicLength(p0, c1, c2, p1, steps = 64) {
5687
+ let len = 0;
5688
+ let prev = p0;
5689
+ for (let i = 1; i <= steps; i++) {
5690
+ const pt = cubicAt(p0, c1, c2, p1, i / steps);
5691
+ len += Math.hypot(pt[0] - prev[0], pt[1] - prev[1]);
5692
+ prev = pt;
5693
+ }
5694
+ return len;
5695
+ }
5696
+ var fmt2 = (n) => {
5697
+ const r = Math.round(n * 1e4) / 1e4;
5698
+ return Object.is(r, -0) ? "0" : String(r);
5699
+ };
5700
+ function buildOffsetPath(propAnim) {
5701
+ var _a2, _b, _c;
5702
+ if (propAnim.alongPathMode !== "offsetPath") return void 0;
5703
+ const kfs = propAnim.keyframes;
5704
+ if (!kfs || kfs.length < 2) return void 0;
5705
+ const first = keyframeValue(kfs[0]);
5706
+ const anchor = (first == null ? void 0 : first.origin) && first.origin.length >= 2 ? [first.origin[0], first.origin[1]] : [0, 0];
5707
+ const points = [];
5708
+ let keyframesCarryRotate = false;
5709
+ for (const kf of kfs) {
5710
+ const v = keyframeValue(kf);
5711
+ const tr = v == null ? void 0 : v.translate;
5712
+ if (!tr || tr.length < 2) return void 0;
5713
+ const parts = Object.keys(v);
5714
+ if (parts.some((p) => p !== TRANSFORM_PART.translate && p !== TRANSFORM_PART.origin && p !== TRANSFORM_PART.rotate)) return void 0;
5715
+ if ((v == null ? void 0 : v.rotate) !== void 0) {
5716
+ if (v.rotate !== 0) return void 0;
5717
+ keyframesCarryRotate = true;
5718
+ }
5719
+ const o = (_a2 = v == null ? void 0 : v.origin) != null ? _a2 : [0, 0];
5720
+ if (o[0] !== anchor[0] || o[1] !== anchor[1]) return void 0;
5721
+ points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);
5722
+ }
5723
+ if (!kfs.some((kf) => keyframeTangentIn(kf) || keyframeTangentOut(kf))) return void 0;
5724
+ let d = "M" + fmt2(points[0][0]) + "," + fmt2(points[0][1]);
5725
+ const segLens = [];
5726
+ for (let i = 0; i < points.length - 1; i++) {
5727
+ const p0 = points[i], p1 = points[i + 1];
5728
+ const to = (_b = keyframeTangentOut(kfs[i])) != null ? _b : [0, 0];
5729
+ const ti = (_c = keyframeTangentIn(kfs[i + 1])) != null ? _c : [0, 0];
5730
+ const c1 = [p0[0] + to[0], p0[1] + to[1]];
5731
+ const c2 = [p1[0] + ti[0], p1[1] + ti[1]];
5732
+ d += "C" + fmt2(c1[0]) + "," + fmt2(c1[1]) + "," + fmt2(c2[0]) + "," + fmt2(c2[1]) + "," + fmt2(p1[0]) + "," + fmt2(p1[1]);
5733
+ segLens.push(cubicLength(p0, c1, c2, p1));
5734
+ }
5735
+ const total = segLens.reduce((a, b) => a + b, 0);
5736
+ if (!(total > 0)) return void 0;
5737
+ const distanceKfs = [];
5738
+ let cum = 0;
5739
+ for (let i = 0; i < kfs.length; i++) {
5740
+ if (i > 0) cum += segLens[i - 1];
5741
+ const out = { t: keyframeTime(kfs[i]), v: cum / total };
5742
+ const e = keyframeEasing(kfs[i]);
5743
+ if (e !== void 0) out.e = e;
5744
+ distanceKfs.push(out);
5745
+ }
5746
+ return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor, keyframesCarryRotate };
5747
+ }
5748
+ function materializeOffsetPathsInTree(root) {
5749
+ const walk = (node) => {
5750
+ var _a2;
5751
+ let out = node;
5752
+ const anim = node.animate;
5753
+ const transform = anim == null ? void 0 : anim["transform"];
5754
+ if (transform) {
5755
+ const built = buildOffsetPath(transform);
5756
+ if (built) {
5757
+ const newAnimate = __spreadValues({}, anim);
5758
+ delete newAnimate[TRANSFORM_ATTR];
5759
+ const distance = { keyframes: built.distanceKfs };
5760
+ if (transform.loop !== void 0) distance.loop = transform.loop;
5761
+ newAnimate[OFFSET_DISTANCE_ATTR] = distance;
5762
+ const staticTr = node.transform;
5763
+ let newTransform = staticTr;
5764
+ if (staticTr && typeof staticTr === "object") {
5765
+ const t = __spreadValues({}, staticTr);
5766
+ delete t[TRANSFORM_PART.translate];
5767
+ delete t[TRANSFORM_PART.origin];
5768
+ if (built.keyframesCarryRotate) delete t[TRANSFORM_PART.rotate];
5769
+ newTransform = Object.keys(t).length ? t : void 0;
5770
+ }
5771
+ out = __spreadProps(__spreadValues({}, node), {
5772
+ animate: newAnimate,
5773
+ style: __spreadProps(__spreadValues({}, node.style), {
5774
+ offsetPath: "path('" + built.pathStr + "')",
5775
+ offsetAnchor: fmt2(built.anchor[0]) + "px " + fmt2(built.anchor[1]) + "px",
5776
+ offsetRotate: built.autoOrient ? "auto" : "0deg",
5777
+ offsetDistance: "0%"
5778
+ })
5779
+ });
5780
+ if (newTransform !== void 0) out.transform = newTransform;
5781
+ else delete out.transform;
5782
+ }
5783
+ }
5784
+ if ((_a2 = out.children) == null ? void 0 : _a2.length) {
5785
+ const children = out.children.map(walk);
5786
+ if (children.some((c, i) => c !== out.children[i])) out = __spreadProps(__spreadValues({}, out), { children });
5787
+ }
5788
+ return out;
5789
+ };
5790
+ return walk(root);
5791
+ }
5792
+
5793
+ // src/materialize/PxAnimatorUseMaterializer.ts
5794
+ function materializeAnimatedUseInstances(root) {
5795
+ var _a2;
5796
+ const idMap = buildIdMap2(root);
5797
+ const animatedIds = computeAnimatedSubtreeIds(root, idMap);
5798
+ if (animatedIds.size === 0) return root;
5799
+ let idCounter = 0;
5800
+ const genId3 = () => "_lw_use_mat_" + ++idCounter;
5801
+ const rootViewport = readRootViewport(root);
5802
+ const defsCollector = [];
5803
+ const walked = walkAndMaterialize2(root, idMap, animatedIds, genId3, defsCollector, rootViewport);
5804
+ if (defsCollector.length === 0) return walked;
5805
+ const defsNode = { type: "defs", children: defsCollector };
5806
+ const newChildren = [...(_a2 = walked.children) != null ? _a2 : [], defsNode];
5807
+ return __spreadProps(__spreadValues({}, walked), { children: newChildren });
5808
+ }
5809
+ function readRootViewport(root) {
5810
+ const vb = parseViewBox(root.viewBox);
5811
+ if (vb) return [vb[2], vb[3]];
5812
+ const w = numericAttr(root.width);
5813
+ const h = numericAttr(root.height);
5814
+ if (w !== void 0 && h !== void 0) return [w, h];
5815
+ return [1, 1];
5816
+ }
5817
+ function numericAttr(v) {
5818
+ if (typeof v === "number") return v;
5819
+ if (typeof v === "string") {
5820
+ const n = parseFloat(v);
5821
+ return Number.isFinite(n) ? n : void 0;
5822
+ }
5823
+ return void 0;
5824
+ }
5825
+ function buildIdMap2(root) {
5826
+ const map = /* @__PURE__ */ new Map();
5827
+ const visit = (n) => {
5828
+ var _a2;
5829
+ if (typeof n.id === "string") map.set(n.id, n);
5830
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(visit);
5831
+ };
5832
+ visit(root);
5833
+ return map;
5834
+ }
5835
+ function computeAnimatedSubtreeIds(root, idMap) {
5836
+ const cache = /* @__PURE__ */ new WeakMap();
5837
+ const result = /* @__PURE__ */ new Set();
5838
+ const hasAnim = (n, visiting) => {
5839
+ const cached = cache.get(n);
5840
+ if (cached !== void 0) return cached;
5841
+ if (visiting.has(n)) return false;
5842
+ visiting.add(n);
5843
+ let r = false;
5844
+ if (n.animate && typeof n.animate === "object" && !Array.isArray(n.animate)) {
5845
+ for (const _ in n.animate) {
5846
+ r = true;
5847
+ break;
5848
+ }
5849
+ }
5850
+ if (!r && n.children) {
5851
+ for (const ch of n.children) {
5852
+ if (hasAnim(ch, visiting)) {
5853
+ r = true;
5854
+ break;
5855
+ }
5856
+ }
5857
+ }
5858
+ if (!r && n.type === "use" && typeof n.href === "string") {
5859
+ const targetId = stripHash2(n.href);
5860
+ const target = targetId ? idMap.get(targetId) : void 0;
5861
+ if (target) r = hasAnim(target, visiting);
5862
+ }
5863
+ visiting.delete(n);
5864
+ cache.set(n, r);
5865
+ return r;
5866
+ };
5867
+ for (const [id, node] of idMap) {
5868
+ if (hasAnim(node, /* @__PURE__ */ new Set())) result.add(id);
5869
+ }
5870
+ return result;
5871
+ }
5872
+ function stripHash2(href) {
5873
+ if (typeof href !== "string") return void 0;
5874
+ return href.startsWith("#") ? href.slice(1) : href;
5875
+ }
5876
+ function materializeOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector, rootViewport) {
5877
+ var _a2, _b, _c, _d;
5878
+ const clone2 = deepClonePxNode(target);
5879
+ regenerateIdsAndRewriteRefs(clone2, genId3);
5880
+ const symbolViewBox = clone2.type === "symbol" ? parseViewBox(clone2.viewBox) : void 0;
5881
+ const useW = (_b = (_a2 = numericAttr(useNode.width)) != null ? _a2 : symbolViewBox == null ? void 0 : symbolViewBox[2]) != null ? _b : rootViewport[0];
5882
+ const useH = (_d = (_c = numericAttr(useNode.height)) != null ? _c : symbolViewBox == null ? void 0 : symbolViewBox[3]) != null ? _d : rootViewport[1];
5883
+ const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector, useW, useH) : clone2;
5884
+ const materializedClone = walkAndMaterialize2(rewrittenClone, idMap, animatedIds, genId3, defsCollector, rootViewport);
5885
+ const newNode = __spreadProps(__spreadValues({}, useNode), { type: "g", children: [materializedClone] });
5886
+ delete newNode.href;
5887
+ delete newNode.width;
5888
+ delete newNode.height;
5889
+ return applyUseOffsetToG(newNode);
5890
+ }
5891
+ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector, useW, useH) {
5892
+ const viewBox = parseViewBox(symbolNode.viewBox);
5893
+ const g = __spreadProps(__spreadValues({}, symbolNode), { type: "g" });
5894
+ delete g.viewBox;
5895
+ delete g.preserveAspectRatio;
5896
+ delete g.width;
5897
+ delete g.height;
5898
+ if (!viewBox) return g;
5899
+ const [vbX, vbY, vbW, vbH] = viewBox;
5900
+ const scale = vbW > 0 && vbH > 0 ? Math.min(useW / vbW, useH / vbH) : 1;
5901
+ const xOff = (useW - vbW * scale) / 2;
5902
+ const yOff = (useH - vbH * scale) / 2;
5903
+ const parts = [];
5904
+ if (xOff !== 0 || yOff !== 0) parts.push("translate(" + xOff + "," + yOff + ")");
5905
+ if (scale !== 1) parts.push("scale(" + scale + ")");
5906
+ if (vbX !== 0 || vbY !== 0) parts.push("translate(" + -vbX + "," + -vbY + ")");
5907
+ if (parts.length) g.transform = parts.join("");
5908
+ const clipId = genId3();
5909
+ defsCollector.push({
5910
+ type: "clipPath",
5911
+ id: clipId,
5912
+ children: [{ type: "rect", x: vbX, y: vbY, width: vbW, height: vbH }]
5913
+ });
5914
+ g.clipPath = "url(#" + clipId + ")";
5915
+ return g;
5916
+ }
5917
+ function parseViewBox(v) {
5918
+ if (typeof v !== "string") return void 0;
5919
+ const parts = v.trim().split(/[\s,]+/).map(Number);
5920
+ if (parts.length < 4 || parts.some((n) => !Number.isFinite(n))) return void 0;
5921
+ return [parts[0], parts[1], parts[2], parts[3]];
5922
+ }
5923
+ function walkAndMaterialize2(node, idMap, animatedIds, genId3, defsCollector, rootViewport) {
5924
+ if (node.type === "use" && typeof node.href === "string") {
5925
+ const targetId = stripHash2(node.href);
5926
+ if (targetId && animatedIds.has(targetId)) {
5927
+ const target = idMap.get(targetId);
5928
+ if (target) return materializeOneUse(node, target, idMap, animatedIds, genId3, defsCollector, rootViewport);
5929
+ }
3147
5930
  }
3148
- return { d, startShift: startExt };
5931
+ if (!node.children) return node;
5932
+ let changed = false;
5933
+ const newChildren = node.children.map((ch) => {
5934
+ const m = walkAndMaterialize2(ch, idMap, animatedIds, genId3, defsCollector, rootViewport);
5935
+ if (m !== ch) changed = true;
5936
+ return m;
5937
+ });
5938
+ return changed ? __spreadProps(__spreadValues({}, node), { children: newChildren }) : node;
3149
5939
  }
3150
5940
 
3151
- // src/format/PxDocumentDiagnostic.ts
3152
- var MAX_REPORTED = 6;
3153
- function diagnoseDocument(doc) {
3154
- try {
3155
- return { problems: validateDocument(doc) };
3156
- } catch (e) {
3157
- return { problems: [] };
5941
+ // src/util/PxIdUtil.ts
5942
+ var _idCounter = 0;
5943
+ function generateUniqueId() {
5944
+ const timestamp = Date.now().toString(36);
5945
+ const counter = (++_idCounter).toString(36);
5946
+ const random = Math.random().toString(36).substring(2, 6);
5947
+ return "_px_" + timestamp + counter + random;
5948
+ }
5949
+ function deepClone(value) {
5950
+ if (value === null || typeof value !== "object") return value;
5951
+ if (Array.isArray(value)) return value.map((item) => deepClone(item));
5952
+ const obj = value;
5953
+ const cloned = {};
5954
+ for (const key of Object.keys(obj)) {
5955
+ cloned[key] = deepClone(obj[key]);
3158
5956
  }
5957
+ return cloned;
3159
5958
  }
3160
- function reportDocumentDiagnostics(doc, where) {
3161
- const { problems } = diagnoseDocument(doc);
3162
- if (!problems.length) return;
3163
- const shown = problems.slice(0, MAX_REPORTED);
3164
- const more = problems.length - shown.length;
3165
- console.warn(
3166
- where + ": this document does not match the animation schema in " + problems.length + " place" + (problems.length === 1 ? "" : "s") + ".\n" + shown.map((p) => " - " + p).join("\n") + (more > 0 ? "\n \u2026 and " + more + " more" : "") + "\n\nIf you did not author these keys, the usual cause is a build that MANGLES PROPERTY NAMES. An animation document is data loaded at runtime, so renaming the property reads inside the player stops them matching the keys in the JSON, and the animation silently does nothing. Feed the published reserved-name list to your minifier \u2014 @pixodesk/svg-animator-web/mangle-reserved.json \u2014 see docs/library/minification.md."
3167
- );
5959
+ function generateNewIds(doc) {
5960
+ var _a2;
5961
+ const cloned = deepClone(doc);
5962
+ const idMap = /* @__PURE__ */ new Map();
5963
+ const hashRefAttrs = /* @__PURE__ */ new Set(["href", "xlink:href"]);
5964
+ const urlRefAttrs = /* @__PURE__ */ new Set([
5965
+ "fill",
5966
+ "stroke",
5967
+ "clip-path",
5968
+ "clipPath",
5969
+ "mask",
5970
+ "marker",
5971
+ "marker-start",
5972
+ "marker-mid",
5973
+ "marker-end",
5974
+ "filter",
5975
+ "flood-color",
5976
+ "lighting-color"
5977
+ ]);
5978
+ const directIdRefAttrs = /* @__PURE__ */ new Set(["targetId", "boundElementId"]);
5979
+ const isEffectSourceRef = (key, parentKey) => key === "source" && (parentKey === "maskedBy" || parentKey === "clone");
5980
+ function collectIds(node) {
5981
+ if (!node || typeof node !== "object") return;
5982
+ if (node.id && typeof node.id === "string") {
5983
+ const oldId = node.id;
5984
+ const newId = generateUniqueId();
5985
+ idMap.set(oldId, newId);
5986
+ node.id = newId;
5987
+ } else if (node.animate) {
5988
+ node.id = generateUniqueId();
5989
+ }
5990
+ if (Array.isArray(node.children)) {
5991
+ for (const child of node.children) {
5992
+ collectIds(child);
5993
+ }
5994
+ }
5995
+ }
5996
+ function updateRefs(node, parentKey) {
5997
+ if (!node || typeof node !== "object") return;
5998
+ for (const [key, value] of Object.entries(node)) {
5999
+ if (key === "children") {
6000
+ if (Array.isArray(value)) {
6001
+ for (const child of value) {
6002
+ updateRefs(child);
6003
+ }
6004
+ }
6005
+ continue;
6006
+ }
6007
+ if (typeof value === "string") {
6008
+ if (hashRefAttrs.has(key) && value.startsWith("#")) {
6009
+ const oldId = value.slice(1);
6010
+ const newId = idMap.get(oldId);
6011
+ if (newId) {
6012
+ node[key] = "#" + newId;
6013
+ }
6014
+ } else if (urlRefAttrs.has(key)) {
6015
+ node[key] = replaceUrlRefs(value, idMap);
6016
+ } else if (directIdRefAttrs.has(key) || isEffectSourceRef(key, parentKey)) {
6017
+ const hasHash = value.startsWith("#");
6018
+ const newId = idMap.get(hasHash ? value.slice(1) : value);
6019
+ if (newId) {
6020
+ node[key] = hasHash ? "#" + newId : newId;
6021
+ }
6022
+ } else if (value.includes("url(#")) {
6023
+ node[key] = replaceUrlRefs(value, idMap);
6024
+ }
6025
+ } else if (key === "style" && typeof value === "object" && value !== null) {
6026
+ for (const [styleProp, styleValue] of Object.entries(value)) {
6027
+ if (typeof styleValue === "string") {
6028
+ value[styleProp] = replaceUrlRefs(styleValue, idMap);
6029
+ }
6030
+ }
6031
+ } else if (typeof value === "object" && value !== null) {
6032
+ updateRefs(value, key);
6033
+ }
6034
+ }
6035
+ }
6036
+ collectIds(cloned);
6037
+ updateRefs(cloned);
6038
+ const docBindings = (_a2 = cloned.animator) == null ? void 0 : _a2.bindings;
6039
+ if (Array.isArray(docBindings)) {
6040
+ const updatedBindings = docBindings.map((binding) => {
6041
+ var _a3;
6042
+ const hashed = binding.target.startsWith("#");
6043
+ const id = hashed ? binding.target.slice(1) : binding.target;
6044
+ const newId = (_a3 = idMap.get(id)) != null ? _a3 : id;
6045
+ return __spreadProps(__spreadValues({}, binding), { target: hashed ? "#" + newId : newId });
6046
+ });
6047
+ cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { bindings: updatedBindings });
6048
+ }
6049
+ return cloned;
6050
+ }
6051
+ function replaceUrlRefs(value, idMap) {
6052
+ return value.replace(/url\(#([^)]+)\)/g, (match, oldId) => {
6053
+ const newId = idMap.get(oldId);
6054
+ return newId ? "url(#" + newId + ")" : match;
6055
+ });
6056
+ }
6057
+
6058
+ // src/materialize/PxRestPose.ts
6059
+ var TRANSFORM_CHANNEL = "transform";
6060
+ var REVERSED_DIRECTIONS = /* @__PURE__ */ new Set(["reverse", "alternate-reverse"]);
6061
+ var SCRATCH_ID_PREFIX = "__px_rest_";
6062
+ function materializeRestPosesInTree(root, engine) {
6063
+ var _a2;
6064
+ const out = deepClone(root);
6065
+ const config = getAnimatorConfig(out) || {};
6066
+ const duration = +(config.duration || PX_DEFAULT_DURATION_MS);
6067
+ const firstFrameTime = REVERSED_DIRECTIONS.has(String(config.direction)) ? duration : 0;
6068
+ const nodes = animatedNodes(out);
6069
+ if (!nodes.size) return out;
6070
+ const before = bindingsOf(out, engine);
6071
+ const added = /* @__PURE__ */ new Map();
6072
+ for (const [key, animate] of before) {
6073
+ const node = nodes.get(key);
6074
+ if (!node) continue;
6075
+ for (const channel of Object.keys(animate)) {
6076
+ if (!mayCarryRestPose(node, channel)) continue;
6077
+ const value = firstFrameValue(animate, channel, firstFrameTime);
6078
+ if (value === void 0) continue;
6079
+ node[channel] = value;
6080
+ added.set(key, [...(_a2 = added.get(key)) != null ? _a2 : [], channel]);
6081
+ }
6082
+ }
6083
+ if (added.size) {
6084
+ const after = bindingsOf(out, engine);
6085
+ for (const [key, channels] of added) {
6086
+ if (JSON.stringify(after.get(key)) === JSON.stringify(before.get(key))) continue;
6087
+ const node = nodes.get(key);
6088
+ if (node) for (const channel of channels) delete node[channel];
6089
+ }
6090
+ }
6091
+ return out;
6092
+ }
6093
+ function mayCarryRestPose(node, channel) {
6094
+ if (node[channel] !== void 0) return false;
6095
+ if (PX_TRANSFORM_FN_NAMES.has(channel)) return false;
6096
+ if (channel === TRANSFORM_CHANNEL) {
6097
+ for (const part of PX_TRANSFORM_FN_NAMES) if (node[part] !== void 0) return false;
6098
+ }
6099
+ return true;
6100
+ }
6101
+ function firstFrameValue(animate, channel, timeMs) {
6102
+ const values = Object.values(calcAnimationValues({ [channel]: animate[channel] }, timeMs));
6103
+ return values.length === 1 && values[0] !== "" ? values[0] : void 0;
6104
+ }
6105
+ function bindingsOf(tree, engine) {
6106
+ const scratch = deepClone(tree);
6107
+ const keyById = /* @__PURE__ */ new Map();
6108
+ for (const [key, node] of animatedNodes(scratch)) {
6109
+ if (node.id === void 0) node.id = SCRATCH_ID_PREFIX + key;
6110
+ keyById.set(String(node.id), key);
6111
+ }
6112
+ const out = /* @__PURE__ */ new Map();
6113
+ for (const binding of normalizeBindings(scratch, engine)) {
6114
+ const key = keyById.get(binding.id);
6115
+ if (key !== void 0) out.set(key, binding.animate);
6116
+ }
6117
+ return out;
6118
+ }
6119
+ function animatedNodes(tree) {
6120
+ const out = /* @__PURE__ */ new Map();
6121
+ let counter = 0;
6122
+ const visit = (node) => {
6123
+ if (node.animate) out.set(String(counter++), node);
6124
+ if (node.children) for (const child of node.children) visit(child);
6125
+ };
6126
+ if (tree.children) for (const child of tree.children) visit(child);
6127
+ return out;
6128
+ }
6129
+
6130
+ // src/materialize/PxAnimatorMaterializeAll.ts
6131
+ function materializeAllInTree(doc, engine, options) {
6132
+ var _a2, _b;
6133
+ let root = materializeNodeEffects(doc).root;
6134
+ root = materializeOffsetPathsInTree(root);
6135
+ const duration = (_b = (_a2 = getAnimatorConfig(root)) == null ? void 0 : _a2.duration) != null ? _b : PX_DEFAULT_DURATION_MS;
6136
+ root = materializeInternalLoopsInTree(root, duration);
6137
+ if (engine === PxTimelineEngine.native) {
6138
+ root = materializeMotionPathsInTree(root, options == null ? void 0 : options.motionPath);
6139
+ root = materializeAnimatedUseInstances(root);
6140
+ root = pruneUnreferencedDefs(root);
6141
+ }
6142
+ root = materializeRestPosesInTree(root, engine);
6143
+ return root;
6144
+ }
6145
+ function prepareDocumentForRender(doc) {
6146
+ var _a2;
6147
+ const engine = resolveTimelineEngine((_a2 = getAnimatorConfig(doc)) == null ? void 0 : _a2.engine);
6148
+ return generateNewIds(materializeAllInTree(doc, engine));
6149
+ }
6150
+ function pruneUnreferencedDefs(root) {
6151
+ const stripHash3 = (h) => h.startsWith("#") ? h.slice(1) : h;
6152
+ const walk = (n, fn) => {
6153
+ var _a2;
6154
+ fn(n);
6155
+ (_a2 = n.children) == null ? void 0 : _a2.forEach((c) => walk(c, fn));
6156
+ };
6157
+ let changed = true;
6158
+ while (changed) {
6159
+ changed = false;
6160
+ const referenced = /* @__PURE__ */ new Set();
6161
+ walk(root, (n) => {
6162
+ if (n.type === "use" && typeof n.href === "string") referenced.add(stripHash3(n.href));
6163
+ });
6164
+ walk(root, (n) => {
6165
+ if (!n.children) return;
6166
+ let kept = n.children;
6167
+ if (n.type === "defs") {
6168
+ kept = kept.filter((c) => !((c.type === "g" || c.type === "symbol") && typeof c.id === "string" && !referenced.has(c.id)));
6169
+ }
6170
+ kept = kept.filter((c) => !(c.type === "defs" && (!c.children || c.children.length === 0)));
6171
+ if (kept.length !== n.children.length) {
6172
+ n.children = kept;
6173
+ changed = true;
6174
+ }
6175
+ });
6176
+ }
6177
+ return root;
3168
6178
  }
3169
6179
 
3170
6180
  // src/playback/PxDiagnostics.ts
6181
+ var PxDiagnosticKind = {
6182
+ /** The document is wrong — regenerate or repair the file. */
6183
+ document: "document",
6184
+ /** The page or app cannot provide what the document asks for — fix the mount. */
6185
+ host: "host",
6186
+ /** The platform cannot do it and the player degraded — usually nothing to fix. */
6187
+ platform: "platform",
6188
+ /** The call is wrong or self-contradictory — fix the options or props you passed. */
6189
+ usage: "usage",
6190
+ /** The player failed where it did not expect to — report it to us. */
6191
+ internal: "internal"
6192
+ };
3171
6193
  var DOCS_URL = "https://github.com/pixodesk/pixodesk-svg-animator/blob/main/docs/diagnostics.md";
3172
6194
  function codeLine(code) {
3173
6195
  return "PX" + code + " " + DOCS_URL + "#px" + code;
@@ -3200,81 +6222,6 @@ function createDiagnostics(config, prefix) {
3200
6222
  };
3201
6223
  }
3202
6224
 
3203
- // src/playback/PxScrollMath.ts
3204
- function isScrollTimeline(config) {
3205
- return (config == null ? void 0 : config.timelineSource) === "scroll";
3206
- }
3207
- function scrollTotalDurationMs(config) {
3208
- const duration = typeof (config == null ? void 0 : config.duration) === "number" && config.duration > 0 ? config.duration : PX_DEFAULT_DURATION_MS;
3209
- const iterations = typeof (config == null ? void 0 : config.iterations) === "number" && config.iterations > 0 ? config.iterations : 1;
3210
- return duration * iterations;
3211
- }
3212
- function scrollPhaseInterval(phase, subjectSize, scrollportSize) {
3213
- const s = subjectSize, vp = scrollportSize;
3214
- switch (phase) {
3215
- case "cover":
3216
- return [0, s + vp];
3217
- case "entry":
3218
- return [0, Math.min(s, vp)];
3219
- case "contain":
3220
- return [Math.min(s, vp), Math.max(s, vp)];
3221
- case "exit":
3222
- return [Math.max(s, vp), s + vp];
3223
- case "entry-crossing":
3224
- return [0, s];
3225
- case "exit-crossing":
3226
- return [vp, s + vp];
3227
- }
3228
- }
3229
- var DEFAULT_PHASE = "cover";
3230
- function resolveRangePointU(point, defaultFraction, subjectSize, scrollportSize) {
3231
- var _a2;
3232
- const [u0, u1] = scrollPhaseInterval((_a2 = point == null ? void 0 : point.phase) != null ? _a2 : DEFAULT_PHASE, subjectSize, scrollportSize);
3233
- const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
3234
- return u0 + fraction * (u1 - u0);
3235
- }
3236
- function scrollViewProgress(subjectStart, subjectSize, scrollportSize, range) {
3237
- const u = scrollportSize - subjectStart;
3238
- const uStart = resolveRangePointU(range == null ? void 0 : range.start, 0, subjectSize, scrollportSize);
3239
- const uEnd = resolveRangePointU(range == null ? void 0 : range.end, 1, subjectSize, scrollportSize);
3240
- if (uEnd <= uStart) return u >= uEnd ? 1 : 0;
3241
- return clamp((u - uStart) / (uEnd - uStart), 0, 1);
3242
- }
3243
- function scrollOffsetProgress(offset, maxOffset, range) {
3244
- var _a2, _b;
3245
- const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;
3246
- const start = typeof ((_a2 = range == null ? void 0 : range.start) == null ? void 0 : _a2.fraction) === "number" ? range.start.fraction : 0;
3247
- const end = typeof ((_b = range == null ? void 0 : range.end) == null ? void 0 : _b.fraction) === "number" ? range.end.fraction : 1;
3248
- if (end <= start) return raw >= end ? 1 : 0;
3249
- return clamp((raw - start) / (end - start), 0, 1);
3250
- }
3251
- function scrollResolveAxis(axis, writingMode) {
3252
- const a = axis != null ? axis : "block";
3253
- if (a === "x" || a === "y") return a;
3254
- const vertical = !!writingMode && writingMode.startsWith("vertical");
3255
- if (a === "inline") return vertical ? "y" : "x";
3256
- return vertical ? "x" : "y";
3257
- }
3258
-
3259
- // src/util/PxIdUtil.ts
3260
- var _idCounter = 0;
3261
- function generateUniqueId() {
3262
- const timestamp = Date.now().toString(36);
3263
- const counter = (++_idCounter).toString(36);
3264
- const random = Math.random().toString(36).substring(2, 6);
3265
- return "_px_" + timestamp + counter + random;
3266
- }
3267
- function deepClone(value) {
3268
- if (value === null || typeof value !== "object") return value;
3269
- if (Array.isArray(value)) return value.map((item) => deepClone(item));
3270
- const obj = value;
3271
- const cloned = {};
3272
- for (const key of Object.keys(obj)) {
3273
- cloned[key] = deepClone(obj[key]);
3274
- }
3275
- return cloned;
3276
- }
3277
-
3278
6225
  // src/util/PxNodeProps.ts
3279
6226
  var PX_DISALLOWED_SVG_TAGS_LOWER = /* @__PURE__ */ new Set([
3280
6227
  "script",
@@ -3338,6 +6285,135 @@ function sanitizeAttributeValue(name, value) {
3338
6285
  }
3339
6286
  return value;
3340
6287
  }
6288
+ function toDomProps(props) {
6289
+ const propsCopy = {};
6290
+ for (const rawKey of Object.keys(props)) {
6291
+ const key = kebabToCamelCaseWord(rawKey);
6292
+ if (INTERNAL_ATTRS.has(key)) continue;
6293
+ if (key === "style") continue;
6294
+ let value = props[rawKey];
6295
+ if (PX_COLOR_ATTR_NAMES.has(key) && Array.isArray(value)) {
6296
+ propsCopy[key] = toRGBA(value);
6297
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes) {
6298
+ const parts = value.value && typeof value.value === "object" ? value.value : value;
6299
+ propsCopy[TRANSFORM_ATTR] = composeTransformParts(parts, { withUnits: false });
6300
+ } else if (PX_TRANSFORM_FN_NAMES.has(key)) {
6301
+ if (Array.isArray(value)) {
6302
+ if (key === "translate") value = value.map((v) => v + "px");
6303
+ value = value.join(",");
6304
+ }
6305
+ if (key === "rotate") value = value + "deg";
6306
+ propsCopy[TRANSFORM_ATTR] = key + "(" + value + ")";
6307
+ } else if (Array.isArray(value)) {
6308
+ propsCopy[key] = value.join(",");
6309
+ } else if (value !== void 0 && value !== null) {
6310
+ propsCopy[key] = String(value);
6311
+ }
6312
+ }
6313
+ return propsCopy;
6314
+ }
6315
+
6316
+ // src/render/PxRenderTree.ts
6317
+ var CLASS_NAME_KEY = "className";
6318
+ var CLASS_ATTR2 = "class";
6319
+ var DOM_TYPE_KEY = "domType";
6320
+ var TYPE_ATTR = "type";
6321
+ var DEFAULT_TAG = "g";
6322
+ function renderPxTree(node, factory, diag) {
6323
+ return node ? renderOne(node, factory, diag, true, 0) : null;
6324
+ }
6325
+ function renderOne(node, factory, diag, isRoot, index) {
6326
+ const _a2 = node, { type, children, style } = _a2, props = __objRest(_a2, ["type", "children", "style"]);
6327
+ const tag = type || DEFAULT_TAG;
6328
+ if (PX_DISALLOWED_SVG_TAGS_LOWER.has(tag.toLowerCase())) {
6329
+ (diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]")).warn(PxDiagnosticKind.document, 1103 /* blockedTag */, tag);
6330
+ return null;
6331
+ }
6332
+ const domType = props[DOM_TYPE_KEY];
6333
+ if (domType !== void 0) delete props[DOM_TYPE_KEY];
6334
+ const attrs = {};
6335
+ let inlineStyle;
6336
+ const domProps = toDomProps(props);
6337
+ for (const propName of Object.keys(domProps)) {
6338
+ const sanitized = sanitizeAttributeValue(propName, domProps[propName]);
6339
+ if (sanitized === void 0) continue;
6340
+ if (PX_CSS_ONLY_STYLE_PROPS.has(propName)) {
6341
+ (inlineStyle != null ? inlineStyle : inlineStyle = {})[propName] = String(sanitized);
6342
+ continue;
6343
+ }
6344
+ attrs[propName === CLASS_NAME_KEY ? CLASS_ATTR2 : camelCaseToKebabWordIfNeeded(propName)] = sanitized;
6345
+ }
6346
+ if (domType !== void 0) attrs[TYPE_ATTR] = String(domType);
6347
+ if (style) {
6348
+ for (const styleProp of Object.keys(style)) (inlineStyle != null ? inlineStyle : inlineStyle = {})[styleProp] = String(style[styleProp]);
6349
+ }
6350
+ const rendered = [];
6351
+ if (children) {
6352
+ children.forEach((child, i) => {
6353
+ const el = renderOne(child, factory, diag, false, i);
6354
+ if (el !== null) rendered.push(el);
6355
+ });
6356
+ }
6357
+ const ownText = props[PX_TEXT_CONTENT_ATTR];
6358
+ const text = !rendered.length && typeof ownText === "string" && ownText ? ownText : void 0;
6359
+ return factory({ tag, attrs, style: inlineStyle, children: rendered, text, node, isRoot, index });
6360
+ }
6361
+
6362
+ // src/playback/PxScrollMath.ts
6363
+ function isScrollTimeline(config) {
6364
+ return (config == null ? void 0 : config.timelineSource) === "scroll";
6365
+ }
6366
+ function scrollTotalDurationMs(config) {
6367
+ const duration = typeof (config == null ? void 0 : config.duration) === "number" && config.duration > 0 ? config.duration : PX_DEFAULT_DURATION_MS;
6368
+ const iterations = typeof (config == null ? void 0 : config.iterations) === "number" && config.iterations > 0 ? config.iterations : 1;
6369
+ return duration * iterations;
6370
+ }
6371
+ function scrollPhaseInterval(phase, subjectSize, scrollportSize) {
6372
+ const s = subjectSize, vp = scrollportSize;
6373
+ switch (phase) {
6374
+ case "cover":
6375
+ return [0, s + vp];
6376
+ case "entry":
6377
+ return [0, Math.min(s, vp)];
6378
+ case "contain":
6379
+ return [Math.min(s, vp), Math.max(s, vp)];
6380
+ case "exit":
6381
+ return [Math.max(s, vp), s + vp];
6382
+ case "entry-crossing":
6383
+ return [0, s];
6384
+ case "exit-crossing":
6385
+ return [vp, s + vp];
6386
+ }
6387
+ }
6388
+ var DEFAULT_PHASE = "cover";
6389
+ function resolveRangePointU(point, defaultFraction, subjectSize, scrollportSize) {
6390
+ var _a2;
6391
+ const [u0, u1] = scrollPhaseInterval((_a2 = point == null ? void 0 : point.phase) != null ? _a2 : DEFAULT_PHASE, subjectSize, scrollportSize);
6392
+ const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
6393
+ return u0 + fraction * (u1 - u0);
6394
+ }
6395
+ function scrollViewProgress(subjectStart, subjectSize, scrollportSize, range) {
6396
+ const u = scrollportSize - subjectStart;
6397
+ const uStart = resolveRangePointU(range == null ? void 0 : range.start, 0, subjectSize, scrollportSize);
6398
+ const uEnd = resolveRangePointU(range == null ? void 0 : range.end, 1, subjectSize, scrollportSize);
6399
+ if (uEnd <= uStart) return u >= uEnd ? 1 : 0;
6400
+ return clamp((u - uStart) / (uEnd - uStart), 0, 1);
6401
+ }
6402
+ function scrollOffsetProgress(offset, maxOffset, range) {
6403
+ var _a2, _b;
6404
+ const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;
6405
+ const start = typeof ((_a2 = range == null ? void 0 : range.start) == null ? void 0 : _a2.fraction) === "number" ? range.start.fraction : 0;
6406
+ const end = typeof ((_b = range == null ? void 0 : range.end) == null ? void 0 : _b.fraction) === "number" ? range.end.fraction : 1;
6407
+ if (end <= start) return raw >= end ? 1 : 0;
6408
+ return clamp((raw - start) / (end - start), 0, 1);
6409
+ }
6410
+ function scrollResolveAxis(axis, writingMode) {
6411
+ const a = axis != null ? axis : "block";
6412
+ if (a === "x" || a === "y") return a;
6413
+ const vertical = !!writingMode && writingMode.startsWith("vertical");
6414
+ if (a === "inline") return vertical ? "y" : "x";
6415
+ return vertical ? "x" : "y";
6416
+ }
3341
6417
 
3342
6418
  // src/version/PxSchemaFieldUniverse.ts
3343
6419
  function schemaFieldUniverse(root) {
@@ -3498,7 +6574,9 @@ function releaseLogProblems(releases, steps, declared, baseline) {
3498
6574
  materializeMotionPathInPropAnim,
3499
6575
  mergeStaticTransformIntoAnimDef,
3500
6576
  planSchemaRelease,
6577
+ prepareDocumentForRender,
3501
6578
  releaseLogProblems,
6579
+ renderPxTree,
3502
6580
  reportDocumentDiagnostics,
3503
6581
  reverseEasing,
3504
6582
  sanitizeAttributeValue,