@pixodesk/svg-animator-react 1.0.10 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -1795,22 +1795,1031 @@ var PixodeskAnimatorReact = (() => {
1795
1795
  }
1796
1796
  return target;
1797
1797
  };
1798
+ function partsRecord(part, value, origin) {
1799
+ const rec = {};
1800
+ if (part === "translate") rec.translate = value;
1801
+ else if (part === "rotate") rec.rotate = value;
1802
+ else rec.scale = value;
1803
+ if (origin && part !== "translate") rec.origin = origin;
1804
+ return rec;
1805
+ }
1806
+ function readAnimatable(raw) {
1807
+ if (raw === void 0) return {
1808
+ kind: "absent"
1809
+ /* Absent */
1810
+ };
1811
+ if (Array.isArray(raw)) return { kind: "static", value: raw };
1812
+ if (typeof raw === "object") {
1813
+ const obj = raw;
1814
+ if (obj.keyframes) {
1815
+ return { kind: "animated", keyframes: obj.keyframes, autoOrient: obj.autoOrient };
1816
+ }
1817
+ if (obj.value !== void 0) return { kind: "static", value: obj.value };
1818
+ }
1819
+ return { kind: "static", value: raw };
1820
+ }
1821
+ function readStaticOrigin(raw, ctx) {
1822
+ var _a;
1823
+ const o = readAnimatable(raw);
1824
+ if (o.kind === "absent") return void 0;
1825
+ if (o.kind === "static") return o.value;
1826
+ ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
1827
+ return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
1828
+ }
1829
+ function keyframeWith(kf, value) {
1830
+ const out = { value };
1831
+ if (kf.time !== void 0) out.time = kf.time;
1832
+ if (kf.easing !== void 0) out.easing = kf.easing;
1833
+ if (kf.tangentOut !== void 0) out.tangentOut = kf.tangentOut;
1834
+ if (kf.tangentIn !== void 0) out.tangentIn = kf.tangentIn;
1835
+ return out;
1836
+ }
1837
+ function applyTransformationEffect(node, fx, ctx) {
1838
+ if (!fx) return node;
1839
+ delete node.transform;
1840
+ let n = node;
1841
+ n = wrapTransformPart(n, "skew", fx.skew, ctx);
1842
+ n = wrapOrigin(
1843
+ n,
1844
+ fx.origin,
1845
+ /*invert=*/
1846
+ true
1847
+ );
1848
+ n = wrapTransformPart(n, "scale", normalizeScale(fx.scale), ctx);
1849
+ n = wrapTransformPart(n, "rotate", fx.rotate, ctx);
1850
+ n = wrapOrigin(
1851
+ n,
1852
+ fx.origin,
1853
+ /*invert=*/
1854
+ false
1855
+ );
1856
+ if (translateHasAutoOrient(fx.translate)) {
1857
+ n = wrapOrigin(
1858
+ n,
1859
+ fx.origin,
1860
+ /*invert=*/
1861
+ true
1862
+ );
1863
+ n = wrapTransformPart(n, "translate", fx.translate, ctx);
1864
+ n = wrapOrigin(
1865
+ n,
1866
+ fx.origin,
1867
+ /*invert=*/
1868
+ false
1869
+ );
1870
+ } else {
1871
+ n = wrapTransformPart(n, "translate", fx.translate, ctx);
1872
+ }
1873
+ return n;
1874
+ }
1875
+ function translateHasAutoOrient(translate) {
1876
+ if (!translate || typeof translate !== "object") return false;
1877
+ const obj = translate;
1878
+ if (obj.autoOrient) return true;
1879
+ return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
1880
+ }
1881
+ function normalizeScale(raw) {
1882
+ if (raw === void 0) return void 0;
1883
+ if (Array.isArray(raw)) return [raw[0] / 100, raw[1] / 100];
1884
+ return raw;
1885
+ }
1886
+ function wrapTransformPart(inner, part, raw, ctx) {
1887
+ if (raw === void 0) return inner;
1888
+ if (part === "skew") {
1889
+ const skew = readAnimatable(raw);
1890
+ if (skew.kind !== "static") {
1891
+ ctx.warnings.push("transformation.skew: only static skew is supported");
1892
+ return inner;
1893
+ }
1894
+ return { type: "g", transform: "skewX(" + skew.value[0] + ")skewY(" + skew.value[1] + ")", children: [inner] };
1895
+ }
1896
+ const v = readAnimatable(raw);
1897
+ if (v.kind === "static") {
1898
+ return { type: "g", transform: { value: partsRecord(part, v.value, void 0) }, children: [inner] };
1899
+ }
1900
+ if (v.kind === "animated") {
1901
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
1902
+ if (v.autoOrient) animTr.autoOrient = true;
1903
+ return {
1904
+ type: "g",
1905
+ animate: { transform: animTr },
1906
+ children: [inner]
1907
+ };
1908
+ }
1909
+ return inner;
1910
+ }
1911
+ function wrapOrigin(inner, raw, invert) {
1912
+ if (raw === void 0) return inner;
1913
+ const v = readAnimatable(raw);
1914
+ const sign = (value) => invert ? [-value[0], -value[1]] : value;
1915
+ if (v.kind === "absent") return inner;
1916
+ if (v.kind === "static") {
1917
+ if (v.value[0] === 0 && v.value[1] === 0) return inner;
1918
+ return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
1919
+ }
1920
+ if (v.kind === "animated") {
1921
+ return {
1922
+ type: "g",
1923
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) } },
1924
+ children: [inner]
1925
+ };
1926
+ }
1927
+ return inner;
1928
+ }
1929
+ function identifyContentRefTargets(node, ctx, allocator) {
1930
+ var _a, _b, _c;
1931
+ if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.ref) == null ? void 0 : _b.type) === "content") {
1932
+ const baseId = node.effects.ref.baseId;
1933
+ if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
1934
+ ctx.contentRefInnerIds.set(baseId, allocator(baseId));
1935
+ }
1936
+ }
1937
+ (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
1938
+ }
1939
+ function splitForContentRef(node, transformation, originalId, innerId, ctx) {
1940
+ const outerBody = liftBodyTranslate(node, transformation);
1941
+ if (typeof node.id === "string") delete node.id;
1942
+ const { outer: outerTr, inner: innerTr } = splitTransformationEffect(transformation);
1943
+ let innerNode = node;
1944
+ innerNode = applyTransformationEffect(innerNode, innerTr, ctx);
1945
+ const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
1946
+ let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
1947
+ if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
1948
+ if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
1949
+ if (outerTr) {
1950
+ delete outerWrapper.id;
1951
+ outerWrapper = applyTransformationEffect(outerWrapper, outerTr, ctx);
1952
+ outerWrapper.id = originalId;
1953
+ }
1954
+ return outerWrapper;
1955
+ }
1956
+ function liftBodyTranslate(node, transformation) {
1957
+ var _a, _b, _c;
1958
+ const out = {};
1959
+ let didLiftAnimate = false;
1960
+ const animTr = (_a = node.animate) == null ? void 0 : _a.transform;
1961
+ if (animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes)) {
1962
+ const kfs = animTr.keyframes;
1963
+ const hasTranslate = kfs.some((kf) => kf.value && kf.value.translate);
1964
+ if (hasTranslate) {
1965
+ const outerHasOrigin = needsOriginOnOuter(animTr);
1966
+ const outerKfs = kfs.map((kf) => {
1967
+ const v = kf.value || {};
1968
+ const newValue = {};
1969
+ if (v.translate !== void 0) newValue.translate = v.translate;
1970
+ if (outerHasOrigin && v.origin !== void 0) newValue.origin = v.origin;
1971
+ const outerKf = { value: newValue };
1972
+ if (kf.time !== void 0) outerKf.time = kf.time;
1973
+ if (kf.easing !== void 0) outerKf.easing = kf.easing;
1974
+ if (kf.tangentOut !== void 0) outerKf.tangentOut = kf.tangentOut;
1975
+ if (kf.tangentIn !== void 0) outerKf.tangentIn = kf.tangentIn;
1976
+ return outerKf;
1977
+ });
1978
+ const outerAnimTr = { keyframes: outerKfs };
1979
+ if (animTr.autoOrient) outerAnimTr.autoOrient = true;
1980
+ out.animate = { transform: outerAnimTr };
1981
+ const innerHasPivotedPart = kfs.some((kf) => {
1982
+ const v = kf.value || {};
1983
+ return v.rotate !== void 0 || v.scale !== void 0;
1984
+ });
1985
+ const innerKfs = kfs.map((kf) => {
1986
+ const v = kf.value || {};
1987
+ const newValue = {};
1988
+ if (v.rotate !== void 0) newValue.rotate = v.rotate;
1989
+ if (v.scale !== void 0) newValue.scale = v.scale;
1990
+ if (v.origin !== void 0 && (!outerHasOrigin || innerHasPivotedPart)) newValue.origin = v.origin;
1991
+ const innerKf = { value: newValue };
1992
+ if (kf.time !== void 0) innerKf.time = kf.time;
1993
+ if (kf.easing !== void 0) innerKf.easing = kf.easing;
1994
+ return innerKf;
1995
+ });
1996
+ const allInnerEmpty = innerKfs.every((kf) => Object.keys(kf.value).length === 0);
1997
+ if (allInnerEmpty) {
1998
+ delete node.animate.transform;
1999
+ if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
2000
+ } else {
2001
+ node.animate.transform = { keyframes: innerKfs };
2002
+ }
2003
+ didLiftAnimate = true;
2004
+ }
2005
+ }
2006
+ const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
2007
+ const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
2008
+ 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);
2009
+ if (typeof node.transform === "string") {
2010
+ const split = splitTransformString(node.transform);
2011
+ if (stripBodyTranslateOnly) {
2012
+ if (split.translate !== void 0) {
2013
+ if (split.rest) node.transform = split.rest;
2014
+ else delete node.transform;
2015
+ } else if (isPureTranslateBody(node.transform)) {
2016
+ delete node.transform;
2017
+ } else if (liftedAnimateIsAutoOriented && isSingleMatrixBody(node.transform)) {
2018
+ delete node.transform;
2019
+ }
2020
+ } else if (split.translate) {
2021
+ out.transform = split.translate;
2022
+ if (split.rest) node.transform = split.rest;
2023
+ else delete node.transform;
2024
+ }
2025
+ }
2026
+ return out;
2027
+ }
2028
+ function isSingleMatrixBody(s) {
2029
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
2030
+ let count = 0;
2031
+ let isMatrix = false;
2032
+ let m;
2033
+ while (m = re.exec(s)) {
2034
+ count++;
2035
+ if (m[1] === "matrix") isMatrix = true;
2036
+ }
2037
+ return count === 1 && isMatrix;
2038
+ }
2039
+ function isPureTranslateBody(s) {
2040
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
2041
+ const ops = [];
2042
+ let m;
2043
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
2044
+ if (ops.length !== 1) return false;
2045
+ if (ops[0].name === "translate") return true;
2046
+ if (ops[0].name !== "matrix") return false;
2047
+ const args = /matrix\(([^)]*)\)/.exec(ops[0].full);
2048
+ if (!args) return false;
2049
+ const nums = args[1].split(/[\s,]+/).filter(Boolean).map(Number);
2050
+ return nums.length >= 4 && nums[0] === 1 && nums[1] === 0 && nums[2] === 0 && nums[3] === 1;
2051
+ }
2052
+ function splitTransformString(s) {
2053
+ const ops = [];
2054
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
2055
+ let m;
2056
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
2057
+ if (!ops.length) return { rest: s || void 0 };
2058
+ if (ops.every((o) => o.name === "translate")) {
2059
+ return { translate: ops.map((o) => o.full).join("") };
2060
+ }
2061
+ const leading = ops[0];
2062
+ const trailing = ops[ops.length - 1];
2063
+ if (trailing.name === "translate" && leading.name === "translate") {
2064
+ const trailingVec = parseTranslateArgs(trailing.full);
2065
+ const leadingVec = parseTranslateArgs(leading.full);
2066
+ const ox = -trailingVec[0];
2067
+ const oy = -trailingVec[1];
2068
+ const userTx = leadingVec[0] - ox;
2069
+ const userTy = leadingVec[1] - oy;
2070
+ if (userTx === 0 && userTy === 0) return { rest: s };
2071
+ const middleAndTrailing = "translate(" + ox + "," + oy + ")" + ops.slice(1).map((o) => o.full).join("");
2072
+ return { translate: "translate(" + userTx + "," + userTy + ")", rest: middleAndTrailing };
2073
+ }
2074
+ if (trailing.name === "translate") return { rest: s };
2075
+ const lifted = [];
2076
+ let i = 0;
2077
+ while (i < ops.length && ops[i].name === "translate") {
2078
+ lifted.push(ops[i].full);
2079
+ i++;
2080
+ }
2081
+ if (!lifted.length) return { rest: s };
2082
+ const rest = ops.slice(i).map((o) => o.full).join("");
2083
+ return {
2084
+ translate: lifted.join(""),
2085
+ rest: rest || void 0
2086
+ };
2087
+ }
2088
+ function parseTranslateArgs(translateOp) {
2089
+ const m = /translate\(([^)]*)\)/.exec(translateOp);
2090
+ if (!m) return [0, 0];
2091
+ const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
2092
+ return [nums[0] || 0, nums[1] || 0];
2093
+ }
2094
+ function splitTransformationEffect(fx) {
2095
+ if (!fx) return {};
2096
+ const originOnOuter = needsOriginOnOuter(fx.translate);
2097
+ const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
2098
+ const outer = {};
2099
+ const inner = {};
2100
+ if (fx.translate !== void 0) outer.translate = fx.translate;
2101
+ if (originOnOuter && fx.origin !== void 0) outer.origin = fx.origin;
2102
+ if (fx.rotate !== void 0) inner.rotate = fx.rotate;
2103
+ if (fx.scale !== void 0) inner.scale = fx.scale;
2104
+ if (fx.skew !== void 0) inner.skew = fx.skew;
2105
+ if (fx.origin !== void 0 && (!originOnOuter || innerHasPivotedPart)) inner.origin = fx.origin;
2106
+ return {
2107
+ outer: Object.keys(outer).length ? outer : void 0,
2108
+ inner: Object.keys(inner).length ? inner : void 0
2109
+ };
2110
+ }
2111
+ function needsOriginOnOuter(translateAnim) {
2112
+ if (!translateAnim || typeof translateAnim !== "object") return false;
2113
+ const obj = translateAnim;
2114
+ if (obj.autoOrient) return true;
2115
+ if (Array.isArray(obj.keyframes)) {
2116
+ return obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
2117
+ }
2118
+ return false;
2119
+ }
2120
+ function pathStr(path) {
2121
+ if (!path.length) return ".";
2122
+ let result = "";
2123
+ for (const seg of path) {
2124
+ if (seg.startsWith("[")) result += seg;
2125
+ else result += (result ? "." : "") + seg;
2126
+ }
2127
+ return result;
2128
+ }
2129
+ var Base = class {
2130
+ _canSanitize(raw) {
2131
+ return this.isValid(raw);
2132
+ }
2133
+ optional() {
2134
+ return new Optional(this);
2135
+ }
2136
+ };
2137
+ var Optional = class extends Base {
2138
+ constructor(inner) {
2139
+ super();
2140
+ this.inner = inner;
2141
+ this._default = void 0;
2142
+ }
2143
+ sanitize(raw) {
2144
+ if (raw === void 0 || raw === null) return void 0;
2145
+ return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
2146
+ }
2147
+ isValid(raw, ctx, path) {
2148
+ if (raw === void 0 || raw === null) return true;
2149
+ return this.inner.isValid(raw, ctx, path);
2150
+ }
2151
+ _canSanitize(raw) {
2152
+ return raw === void 0 || raw === null || this.inner._canSanitize(raw);
2153
+ }
2154
+ };
2155
+ var Str = class extends Base {
2156
+ constructor(_default = "") {
2157
+ super();
2158
+ this._default = _default;
2159
+ }
2160
+ sanitize(raw) {
2161
+ return typeof raw === "string" ? raw : this._default;
2162
+ }
2163
+ isValid(raw, ctx, path) {
2164
+ if (typeof raw === "string") return true;
2165
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
2166
+ return false;
2167
+ }
2168
+ };
2169
+ var Num = class extends Base {
2170
+ constructor(_default = 0) {
2171
+ super();
2172
+ this._default = _default;
2173
+ }
2174
+ sanitize(raw) {
2175
+ return typeof raw === "number" && isFinite(raw) ? raw : this._default;
2176
+ }
2177
+ isValid(raw, ctx, path) {
2178
+ if (typeof raw === "number" && isFinite(raw)) return true;
2179
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
2180
+ return false;
2181
+ }
2182
+ };
2183
+ var Bool = class extends Base {
2184
+ constructor(_default = false) {
2185
+ super();
2186
+ this._default = _default;
2187
+ }
2188
+ sanitize(raw) {
2189
+ return typeof raw === "boolean" ? raw : this._default;
2190
+ }
2191
+ isValid(raw, ctx, path) {
2192
+ if (typeof raw === "boolean") return true;
2193
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
2194
+ return false;
2195
+ }
2196
+ };
2197
+ var Literal = class extends Base {
2198
+ constructor(value) {
2199
+ super();
2200
+ this.value = value;
2201
+ this._default = value;
2202
+ }
2203
+ sanitize(raw) {
2204
+ return raw === this.value ? this.value : this._default;
2205
+ }
2206
+ isValid(raw, ctx, path) {
2207
+ if (raw === this.value) return true;
2208
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
2209
+ return false;
2210
+ }
2211
+ };
2212
+ var Enum = class extends Base {
2213
+ constructor(values, defaultVal) {
2214
+ super();
2215
+ this.values = values;
2216
+ this._default = defaultVal != null ? defaultVal : values[0];
2217
+ }
2218
+ sanitize(raw) {
2219
+ return this.values.includes(raw) ? raw : this._default;
2220
+ }
2221
+ isValid(raw, ctx, path) {
2222
+ if (this.values.includes(raw)) return true;
2223
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected one of " + this.values.map((v) => JSON.stringify(v)).join(" | ") + ", got " + JSON.stringify(raw));
2224
+ return false;
2225
+ }
2226
+ };
2227
+ var Union = class extends Base {
2228
+ constructor(schemas, defaultVal) {
2229
+ super();
2230
+ this.schemas = schemas;
2231
+ this._default = defaultVal != null ? defaultVal : schemas[0]._default;
2232
+ }
2233
+ sanitize(raw) {
2234
+ for (const s of this.schemas) {
2235
+ if (s.isValid(raw)) return s.sanitize(raw);
2236
+ }
2237
+ return this._default;
2238
+ }
2239
+ isValid(raw, ctx, path) {
2240
+ var _a;
2241
+ if (this.schemas.some((s) => s.isValid(raw))) return true;
2242
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no union member matched for value " + ((_a = JSON.stringify(raw)) != null ? _a : "").slice(0, 60));
2243
+ return false;
2244
+ }
2245
+ _canSanitize(raw) {
2246
+ return this.schemas.some((s) => s._canSanitize(raw));
2247
+ }
2248
+ };
2249
+ var DiscriminatedUnion = class extends Base {
2250
+ constructor(_key, _schemas, defaultVal) {
2251
+ super();
2252
+ this._key = _key;
2253
+ this._schemas = _schemas;
2254
+ this._default = defaultVal != null ? defaultVal : _schemas[0]._default;
2255
+ this._map = /* @__PURE__ */ new Map();
2256
+ for (const s of _schemas) {
2257
+ const keySchema = s._shape[_key];
2258
+ if (keySchema) this._map.set(keySchema._default, s);
2259
+ }
2260
+ }
2261
+ _findSchema(raw) {
2262
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
2263
+ const val = raw[this._key];
2264
+ if (val === void 0 || val === null) return void 0;
2265
+ return this._map.get(val);
2266
+ }
2267
+ sanitize(raw) {
2268
+ var _a;
2269
+ return ((_a = this._findSchema(raw)) != null ? _a : this._schemas[0]).sanitize(raw);
2270
+ }
2271
+ isValid(raw, ctx, path) {
2272
+ const schema = this._findSchema(raw);
2273
+ if (!schema) {
2274
+ const val = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw[this._key] : void 0;
2275
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no discriminated union member matched " + this._key + "=" + JSON.stringify(val));
2276
+ return false;
2277
+ }
2278
+ return schema.isValid(raw, ctx, path);
2279
+ }
2280
+ _canSanitize(raw) {
2281
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return false;
2282
+ const schema = this._findSchema(raw);
2283
+ return schema ? schema._canSanitize(raw) : this._schemas[0]._canSanitize(raw);
2284
+ }
2285
+ };
2286
+ var Obj = class extends Base {
2287
+ constructor(_shape) {
2288
+ super();
2289
+ this._shape = _shape;
2290
+ const d = {};
2291
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
2292
+ this._default = d;
2293
+ }
2294
+ sanitize(raw) {
2295
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
2296
+ const out = {};
2297
+ for (const key of Object.keys(this._shape)) {
2298
+ out[key] = this._shape[key].sanitize(src[key]);
2299
+ }
2300
+ return out;
2301
+ }
2302
+ isValid(raw, ctx, path) {
2303
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
2304
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
2305
+ return false;
2306
+ }
2307
+ const obj = raw;
2308
+ const p = path != null ? path : [];
2309
+ let ok = true;
2310
+ for (const key of Object.keys(this._shape)) {
2311
+ p.push(key);
2312
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
2313
+ p.pop();
2314
+ }
2315
+ if (ctx == null ? void 0 : ctx.strict) {
2316
+ for (const key of Object.keys(obj)) {
2317
+ if (key in this._shape) continue;
2318
+ p.push(key);
2319
+ ctx.errors.push(pathStr(p) + ": unexpected extra key");
2320
+ p.pop();
2321
+ ok = false;
2322
+ }
2323
+ }
2324
+ return ok;
2325
+ }
2326
+ _canSanitize(raw) {
2327
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
2328
+ }
2329
+ };
2330
+ var OpenObj = class extends Base {
2331
+ constructor(_shape, _openSchema) {
2332
+ super();
2333
+ this._shape = _shape;
2334
+ this._openSchema = _openSchema;
2335
+ const d = {};
2336
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
2337
+ this._default = d;
2338
+ }
2339
+ sanitize(raw) {
2340
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
2341
+ const out = __spreadValues({}, src);
2342
+ for (const key of Object.keys(this._shape)) {
2343
+ out[key] = this._shape[key].sanitize(src[key]);
2344
+ }
2345
+ if (this._openSchema) {
2346
+ for (const key of Object.keys(src)) {
2347
+ if (!(key in this._shape)) out[key] = this._openSchema.sanitize(src[key]);
2348
+ }
2349
+ }
2350
+ return out;
2351
+ }
2352
+ isValid(raw, ctx, path) {
2353
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
2354
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
2355
+ return false;
2356
+ }
2357
+ const obj = raw;
2358
+ const p = path != null ? path : [];
2359
+ let ok = true;
2360
+ for (const key of Object.keys(this._shape)) {
2361
+ p.push(key);
2362
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
2363
+ p.pop();
2364
+ }
2365
+ if (this._openSchema) {
2366
+ for (const key of Object.keys(obj)) {
2367
+ if (key in this._shape) continue;
2368
+ p.push(key);
2369
+ if (!this._openSchema.isValid(obj[key], ctx, p)) ok = false;
2370
+ p.pop();
2371
+ }
2372
+ }
2373
+ return ok;
2374
+ }
2375
+ _canSanitize(raw) {
2376
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
2377
+ }
2378
+ };
2379
+ var Arr = class extends Base {
2380
+ constructor(item) {
2381
+ super();
2382
+ this.item = item;
2383
+ this._default = [];
2384
+ }
2385
+ sanitize(raw) {
2386
+ if (!Array.isArray(raw)) return [];
2387
+ const out = [];
2388
+ for (const el of raw) {
2389
+ if (this.item._canSanitize(el)) out.push(this.item.sanitize(el));
2390
+ }
2391
+ return out;
2392
+ }
2393
+ isValid(raw, ctx, path) {
2394
+ if (!Array.isArray(raw)) {
2395
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected array, got " + typeof raw);
2396
+ return false;
2397
+ }
2398
+ const p = path != null ? path : [];
2399
+ let ok = true;
2400
+ for (let i = 0; i < raw.length; i++) {
2401
+ p.push("[" + i + "]");
2402
+ if (!this.item.isValid(raw[i], ctx, p)) ok = false;
2403
+ p.pop();
2404
+ }
2405
+ return ok;
2406
+ }
2407
+ _canSanitize(raw) {
2408
+ return Array.isArray(raw);
2409
+ }
2410
+ };
2411
+ var Rec = class extends Base {
2412
+ constructor(value) {
2413
+ super();
2414
+ this.value = value;
2415
+ this._default = {};
2416
+ }
2417
+ sanitize(raw) {
2418
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
2419
+ const out = {};
2420
+ for (const [k, v] of Object.entries(raw)) {
2421
+ if (this.value._canSanitize(v)) out[k] = this.value.sanitize(v);
2422
+ }
2423
+ return out;
2424
+ }
2425
+ isValid(raw, ctx, path) {
2426
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
2427
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object/record, got " + (Array.isArray(raw) ? "array" : typeof raw));
2428
+ return false;
2429
+ }
2430
+ const p = path != null ? path : [];
2431
+ let ok = true;
2432
+ for (const [k, v] of Object.entries(raw)) {
2433
+ p.push(k);
2434
+ if (!this.value.isValid(v, ctx, p)) ok = false;
2435
+ p.pop();
2436
+ }
2437
+ return ok;
2438
+ }
2439
+ _canSanitize(raw) {
2440
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
2441
+ }
2442
+ };
2443
+ var Any = class extends Base {
2444
+ constructor() {
2445
+ super(...arguments);
2446
+ this._default = void 0;
2447
+ }
2448
+ sanitize(raw) {
2449
+ return raw;
2450
+ }
2451
+ isValid(_raw, _ctx, _path) {
2452
+ return true;
2453
+ }
2454
+ _canSanitize(_raw) {
2455
+ return true;
2456
+ }
2457
+ };
2458
+ var Lazy = class extends Base {
2459
+ constructor(fn, _default) {
2460
+ super();
2461
+ this.fn = fn;
2462
+ this._default = _default;
2463
+ this.resolved = null;
2464
+ }
2465
+ get schema() {
2466
+ var _a;
2467
+ return (_a = this.resolved) != null ? _a : this.resolved = this.fn();
2468
+ }
2469
+ sanitize(raw) {
2470
+ return this.schema.sanitize(raw);
2471
+ }
2472
+ isValid(raw, ctx, path) {
2473
+ return this.schema.isValid(raw, ctx, path);
2474
+ }
2475
+ _canSanitize(raw) {
2476
+ return this.schema._canSanitize(raw);
2477
+ }
2478
+ };
2479
+ var Tuple = class extends Base {
2480
+ constructor(schemas) {
2481
+ super();
2482
+ this.schemas = schemas;
2483
+ this._default = schemas.map((s) => s._default);
2484
+ }
2485
+ sanitize(raw) {
2486
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) return this._default;
2487
+ return this.schemas.map((s, i) => s.sanitize(raw[i]));
2488
+ }
2489
+ isValid(raw, ctx, path) {
2490
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) {
2491
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected tuple of length " + this.schemas.length + ", got " + (Array.isArray(raw) ? "array[" + raw.length + "]" : typeof raw));
2492
+ return false;
2493
+ }
2494
+ const p = path != null ? path : [];
2495
+ let ok = true;
2496
+ for (let i = 0; i < this.schemas.length; i++) {
2497
+ p.push("[" + i + "]");
2498
+ if (!this.schemas[i].isValid(raw[i], ctx, p)) ok = false;
2499
+ p.pop();
2500
+ }
2501
+ return ok;
2502
+ }
2503
+ // Require exact length so wrong-length arrays are dropped rather than repaired to default.
2504
+ _canSanitize(raw) {
2505
+ return Array.isArray(raw) && raw.length === this.schemas.length;
2506
+ }
2507
+ };
2508
+ function implementsInterface() {
2509
+ return (schema) => schema;
2510
+ }
2511
+ var px = {
2512
+ /** Matches a string. Default: '' or provided value. */
2513
+ string: (defaultVal = "") => new Str(defaultVal),
2514
+ /** Matches a finite number. Default: 0 or provided value. */
2515
+ number: (defaultVal = 0) => new Num(defaultVal),
2516
+ /** Matches a boolean. Default: false or provided value. */
2517
+ boolean: (defaultVal = false) => new Bool(defaultVal),
2518
+ /** Matches one exact primitive value; its default is the value itself. */
2519
+ literal: (value) => new Literal(value),
2520
+ /** Matches one of a fixed set of string/number values. Default: first value. */
2521
+ enum: (values, defaultVal) => new Enum(values, defaultVal),
2522
+ /**
2523
+ * Returns the first schema whose isValid passes.
2524
+ * TypeScript infers the union of all member types automatically.
2525
+ */
2526
+ union: (schemas, defaultVal) => new Union(schemas, defaultVal),
2527
+ /**
2528
+ * Discriminated union — reads `raw[key]`, finds the member schema whose
2529
+ * literal at `key` matches, then delegates sanitize/isValid to that member.
2530
+ * Each member must be an object schema with a `px.literal(...)` at `key`.
2531
+ * TypeScript infers the union of all member types automatically.
2532
+ */
2533
+ discriminatedUnion: (key, schemas) => new DiscriminatedUnion(key, schemas),
2534
+ /** Typed object — unknown keys are stripped. Required fields fall back to their default. */
2535
+ object: (shape) => new Obj(shape),
2536
+ /**
2537
+ * Open object — validates known keys; passes unknown keys through as-is,
2538
+ * or validates/sanitizes them against `openSchema` when provided.
2539
+ */
2540
+ openObject: (shape, openSchema) => new OpenObj(shape, openSchema),
2541
+ /**
2542
+ * Creates a new closed object schema by merging a base schema's shape with additional fields.
2543
+ * The base can be the result of px.object() or px.openObject() — anything with a _shape property.
2544
+ *
2545
+ * @example
2546
+ * const PxSvgNodeSchema = px.extendedObject(PxNodeBase, { width: px.number().optional() });
2547
+ */
2548
+ extendedObject: (base, extra) => new Obj(__spreadValues(__spreadValues({}, base._shape), extra)),
2549
+ /** Array whose unrecoverable items are filtered out. Default: []. */
2550
+ array: (item) => new Arr(item),
2551
+ /** String-keyed record whose unrecoverable values are dropped. Default: {}. */
2552
+ record: (value) => new Rec(value),
2553
+ /** Passes anything through unchanged — always valid. */
2554
+ any: () => new Any(),
2555
+ /** Fixed-length tuple — validates element count and each position individually. */
2556
+ tuple: (schemas) => new Tuple(schemas),
2557
+ /** Defers schema creation — required for recursive types. Must supply a default value. */
2558
+ lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
2559
+ };
1798
2560
  var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
1799
2561
  var PX_ANIM_ATTR_NAME = "_px_animator";
1800
- var ANIMATE_ATTR = "animate";
2562
+ var PxAnimatorMode = {
2563
+ auto: "auto",
2564
+ webapi: "webapi",
2565
+ frames: "frames"
2566
+ };
2567
+ var PxAnimatorEngine = {
2568
+ webapi: PxAnimatorMode.webapi,
2569
+ frames: PxAnimatorMode.frames
2570
+ };
1801
2571
  var TEXT_ATTR = "text";
1802
2572
  var TEXT_CONTENT_ATTR = "textContent";
1803
2573
  var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
1804
2574
  "type",
1805
2575
  "children",
1806
- ANIMATE_ATTR,
1807
2576
  "animator",
1808
2577
  "meta",
1809
- "defs",
1810
- "bindings",
2578
+ "animate",
1811
2579
  TEXT_ATTR,
1812
2580
  TEXT_CONTENT_ATTR
1813
2581
  ]);
2582
+ var PxEasingOrRefSchema = px.union([
2583
+ px.string(),
2584
+ px.tuple([px.number(), px.number(), px.number(), px.number()])
2585
+ ]);
2586
+ var PxKeyframeValueSchema = implementsInterface()(px.union([
2587
+ px.string(),
2588
+ // e.g. for colors
2589
+ px.number(),
2590
+ px.array(px.number()),
2591
+ px.lazy(() => PxTransformPartsSchema, {}),
2592
+ px.object({ path: px.string() }),
2593
+ px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] })
2594
+ ]));
2595
+ var PxKeyframeSchema = implementsInterface()(px.object({
2596
+ time: px.number().optional(),
2597
+ t: px.number().optional(),
2598
+ value: px.any().optional(),
2599
+ v: px.any().optional(),
2600
+ easing: PxEasingOrRefSchema.optional(),
2601
+ e: PxEasingOrRefSchema.optional(),
2602
+ tangentOut: px.tuple([px.number(), px.number()]).optional(),
2603
+ to: px.tuple([px.number(), px.number()]).optional(),
2604
+ // short alias
2605
+ tangentIn: px.tuple([px.number(), px.number()]).optional(),
2606
+ ti: px.tuple([px.number(), px.number()]).optional(),
2607
+ // short alias
2608
+ selected: px.boolean().optional()
2609
+ // editor-side UI state (Player ignores it)
2610
+ }));
2611
+ var PxLoopSchema = implementsInterface()(px.object({
2612
+ segmentCount: px.number().optional(),
2613
+ before: px.boolean().optional(),
2614
+ alternate: px.boolean().optional()
2615
+ }));
2616
+ var PxPropertyAnimationSchema = implementsInterface()(px.object({
2617
+ keyframes: px.array(PxKeyframeSchema).optional(),
2618
+ kfs: px.array(PxKeyframeSchema).optional(),
2619
+ loop: px.union([PxLoopSchema, px.boolean()]).optional(),
2620
+ autoOrient: px.boolean().optional()
2621
+ }));
2622
+ var PxTransformPartsSchema = implementsInterface()(px.object({
2623
+ translate: px.tuple([px.number(), px.number()]).optional(),
2624
+ rotate: px.number().optional(),
2625
+ scale: px.tuple([px.number(), px.number()]).optional(),
2626
+ origin: px.tuple([px.number(), px.number()]).optional()
2627
+ }));
2628
+ var PxTransformValueSchema = px.union([
2629
+ px.string(),
2630
+ px.object({ value: PxTransformPartsSchema }),
2631
+ PxPropertyAnimationSchema
2632
+ ]);
2633
+ var PxAnimationDefinitionSchema = implementsInterface()(
2634
+ px.record(PxPropertyAnimationSchema)
2635
+ );
2636
+ var PxElementAnimationSchema = implementsInterface()(px.union([
2637
+ px.string(),
2638
+ px.array(px.union([px.string(), PxAnimationDefinitionSchema])),
2639
+ PxAnimationDefinitionSchema
2640
+ ]));
2641
+ var PxTriggerSchema = implementsInterface()(px.object({
2642
+ startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
2643
+ outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
2644
+ scrollIntoViewThreshold: px.number().optional()
2645
+ }));
2646
+ var PxDefsSchema = implementsInterface()(px.object({
2647
+ easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
2648
+ animations: px.record(PxAnimationDefinitionSchema).optional(),
2649
+ styles: px.record(px.any()).optional()
2650
+ }));
2651
+ var PxAnimatorConfigSchema = implementsInterface()(px.object({
2652
+ mode: px.enum([PxAnimatorMode.auto, PxAnimatorMode.webapi, PxAnimatorMode.frames]).optional(),
2653
+ duration: px.number().optional(),
2654
+ delay: px.number().optional(),
2655
+ iterations: px.union([px.number(), px.literal("infinite")]).optional(),
2656
+ fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
2657
+ direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
2658
+ frameRate: px.number().optional(),
2659
+ trigger: PxTriggerSchema.optional(),
2660
+ definitions: PxDefsSchema.optional(),
2661
+ animate: px.record(PxElementAnimationSchema).optional(),
2662
+ debug: px.boolean().optional(),
2663
+ debugInstName: px.string().optional()
2664
+ }));
2665
+ var PxBindingSchema = implementsInterface()(px.object({
2666
+ id: px.string(),
2667
+ animate: PxElementAnimationSchema
2668
+ }));
2669
+ var PxAttrValueSchema = px.union([
2670
+ px.string(),
2671
+ px.number(),
2672
+ px.object({ value: px.any() }),
2673
+ PxPropertyAnimationSchema
2674
+ ]);
2675
+ var PxAnimatableNumberSchema = px.union([
2676
+ px.number(),
2677
+ px.object({ value: px.number() }),
2678
+ px.object({
2679
+ keyframes: px.array(PxKeyframeSchema),
2680
+ autoOrient: px.boolean().optional()
2681
+ })
2682
+ ]);
2683
+ var PxAnimatableVec2Schema = px.union([
2684
+ px.tuple([px.number(), px.number()]),
2685
+ px.object({ value: px.tuple([px.number(), px.number()]) }),
2686
+ px.object({
2687
+ keyframes: px.array(PxKeyframeSchema),
2688
+ autoOrient: px.boolean().optional()
2689
+ })
2690
+ ]);
2691
+ var PxTransformationEffectSchema = implementsInterface()(px.object({
2692
+ translate: PxAnimatableVec2Schema.optional(),
2693
+ rotate: PxAnimatableNumberSchema.optional(),
2694
+ scale: PxAnimatableVec2Schema.optional(),
2695
+ skew: PxAnimatableVec2Schema.optional(),
2696
+ origin: PxAnimatableVec2Schema.optional()
2697
+ }));
2698
+ var PxRepeaterEffectSchema = implementsInterface()(px.object({
2699
+ copies: px.number().optional(),
2700
+ translate: PxAnimatableVec2Schema.optional(),
2701
+ rotate: PxAnimatableNumberSchema.optional(),
2702
+ scale: PxAnimatableVec2Schema.optional(),
2703
+ origin: PxAnimatableVec2Schema.optional()
2704
+ }));
2705
+ var PxMaskedByEffectSchema = implementsInterface()(px.object({
2706
+ href: px.string().optional(),
2707
+ maskType: px.string().optional(),
2708
+ maskUnits: px.string().optional(),
2709
+ maskContentUnits: px.string().optional()
2710
+ }));
2711
+ var PxTrimPathEffectSchema = implementsInterface()(px.object({
2712
+ offset: PxAnimatableNumberSchema.optional(),
2713
+ range: PxAnimatableVec2Schema.optional(),
2714
+ trimAllAsOne: px.boolean().optional()
2715
+ }));
2716
+ var PxRetimeEffectSchema = implementsInterface()(px.object({
2717
+ baseId: px.string().optional(),
2718
+ start: px.number().optional(),
2719
+ stretch: px.number().optional(),
2720
+ timeCrop: px.tuple([px.number(), px.number()]).optional()
2721
+ }));
2722
+ var PxRefEffectSchema = implementsInterface()(px.object({
2723
+ baseId: px.string().optional(),
2724
+ type: px.string().optional()
2725
+ }));
2726
+ var PxGradientType = {
2727
+ linear: "linear",
2728
+ radial: "radial"
2729
+ };
2730
+ var PxGradientStopSchema = implementsInterface()(px.object({
2731
+ offset: px.number(),
2732
+ color: px.string()
2733
+ }));
2734
+ var PxAnimatableGradientStopsSchema = px.union([
2735
+ px.array(PxGradientStopSchema),
2736
+ px.object({ value: px.array(PxGradientStopSchema) }),
2737
+ px.object({ keyframes: px.array(PxKeyframeSchema) })
2738
+ ]);
2739
+ var PxFillGradientEffectSchema = implementsInterface()(px.object({
2740
+ type: px.enum([PxGradientType.linear, PxGradientType.radial]),
2741
+ p1: px.tuple([px.number(), px.number()]).optional(),
2742
+ p2: px.tuple([px.number(), px.number()]).optional(),
2743
+ c: px.tuple([px.number(), px.number()]).optional(),
2744
+ r: px.number().optional(),
2745
+ fp: px.tuple([px.number(), px.number()]).optional(),
2746
+ stops: PxAnimatableGradientStopsSchema.optional(),
2747
+ gradientUnits: px.string().optional(),
2748
+ spreadMethod: px.string().optional(),
2749
+ gradientTransform: px.string().optional()
2750
+ }));
2751
+ var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
2752
+ var PxTextAlongPathEffectSchema = implementsInterface()(px.object({
2753
+ href: px.string(),
2754
+ lengthAdjust: px.string().optional(),
2755
+ method: px.string().optional(),
2756
+ spacing: px.string().optional(),
2757
+ startOffset: PxAnimatableNumberSchema.optional(),
2758
+ textLength: PxAnimatableNumberSchema.optional()
2759
+ }));
2760
+ var PxEffectsSchema = implementsInterface()(px.object({
2761
+ transformation: PxTransformationEffectSchema.optional(),
2762
+ repeater: PxRepeaterEffectSchema.optional(),
2763
+ maskedBy: PxMaskedByEffectSchema.optional(),
2764
+ trimPath: PxTrimPathEffectSchema.optional(),
2765
+ retime: PxRetimeEffectSchema.optional(),
2766
+ isCombinedShape: px.boolean().optional(),
2767
+ ref: PxRefEffectSchema.optional(),
2768
+ fillGradient: PxFillGradientEffectSchema.optional(),
2769
+ strokeGradient: PxStrokeGradientEffectSchema.optional(),
2770
+ textAlongPath: PxTextAlongPathEffectSchema.optional()
2771
+ }));
2772
+ function validateNodeEffects(root, opts) {
2773
+ const warnings = [];
2774
+ const walk = (node, path) => {
2775
+ if (node && node.effects) {
2776
+ const ctx = { errors: [], warnings: [], strict: !!(opts == null ? void 0 : opts.strict) };
2777
+ const ok = PxEffectsSchema.isValid(node.effects, ctx, [path + ".effects"]);
2778
+ if (!ok) {
2779
+ for (const err of ctx.errors) warnings.push(err);
2780
+ }
2781
+ }
2782
+ if (node && Array.isArray(node.children)) {
2783
+ node.children.forEach((c, i) => walk(c, path + ".children[" + i + "]"));
2784
+ }
2785
+ };
2786
+ walk(root, "root");
2787
+ return warnings;
2788
+ }
2789
+ var PxNodeBase = px.openObject({
2790
+ type: px.string(),
2791
+ id: px.string().optional(),
2792
+ meta: px.any().optional(),
2793
+ // Player-effects bucket emitted by the Editor's lightweight design format.
2794
+ // Consumed and removed by `applyPlayerEffects` before any other normalisation
2795
+ // (see `createAnimatorImpl`), so downstream code never sees it.
2796
+ effects: PxEffectsSchema.optional(),
2797
+ // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts
2798
+ // string ref / array of refs / inline definition / mixed array; mirrors
2799
+ // `animator.animate` map values and what `processNode` resolves at runtime.
2800
+ animate: PxElementAnimationSchema.optional(),
2801
+ style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
2802
+ }, PxAttrValueSchema);
2803
+ var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
2804
+ children: px.lazy(() => px.array(PxNodeSchema), []).optional()
2805
+ }), PxAttrValueSchema);
2806
+ var PxSvgNodeExtra = px.object({
2807
+ width: px.number().optional(),
2808
+ height: px.number().optional(),
2809
+ viewBox: px.string().optional(),
2810
+ animator: PxAnimatorConfigSchema.optional()
2811
+ });
2812
+ var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
2813
+ type: px.literal("svg"),
2814
+ // override string → literal to require 'svg'
2815
+ children: px.array(PxNodeSchema).optional()
2816
+ }), PxAttrValueSchema);
2817
+ var PxBezierPathSchema = implementsInterface()(px.object({
2818
+ v: px.array(px.array(px.number())),
2819
+ i: px.array(px.array(px.number())).optional(),
2820
+ o: px.array(px.array(px.number())).optional(),
2821
+ c: px.boolean().optional()
2822
+ }));
1814
2823
  function isPxElementFileFormat(fileJson) {
1815
2824
  if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
1816
2825
  return false;
@@ -1824,69 +2833,840 @@ var PixodeskAnimatorReact = (() => {
1824
2833
  function getDefs(doc) {
1825
2834
  var _a;
1826
2835
  if (!doc) return void 0;
1827
- return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
2836
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
1828
2837
  }
1829
2838
  function getBindings(doc) {
1830
2839
  var _a;
1831
2840
  if (!doc) return void 0;
1832
- return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
2841
+ const animate = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animate;
2842
+ if (!animate) return void 0;
2843
+ return Object.entries(animate).map(([id, anim]) => ({ id, animate: anim }));
1833
2844
  }
1834
- function bezierToSvgPath(path) {
1835
- var _a, _b, _c, _d;
1836
- const v = path.v;
1837
- const i = path.i;
1838
- const o = path.o;
1839
- const c = path.c;
1840
- if (!v.length) return "";
1841
- const d = [];
1842
- const len = v.length;
1843
- d.push("M" + v[0][0] + "," + v[0][1]);
1844
- for (let idx = 1; idx < len; idx++) {
1845
- const prevV = v[idx - 1];
1846
- const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
1847
- const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
1848
- const currV = v[idx];
1849
- const isLine = prevO[0] === prevV[0] && prevO[1] === prevV[1] && (currI[0] === currV[0] && currI[1] === currV[1]);
1850
- if (isLine) {
1851
- d.push("L" + currV[0] + "," + currV[1]);
1852
- } else {
1853
- d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
2845
+ function deepClonePxNode(value) {
2846
+ if (value === null || typeof value !== "object") return value;
2847
+ if (Array.isArray(value)) return value.map(deepClonePxNode);
2848
+ const out = {};
2849
+ for (const k of Object.keys(value)) out[k] = deepClonePxNode(value[k]);
2850
+ return out;
2851
+ }
2852
+ function regenerateIdsAndRewriteRefs(root, genId3) {
2853
+ const oldToNew = /* @__PURE__ */ new Map();
2854
+ const walkAssign = (n) => {
2855
+ var _a;
2856
+ if (typeof n.id === "string") {
2857
+ const newId = genId3();
2858
+ oldToNew.set(n.id, newId);
2859
+ n.id = newId;
1854
2860
  }
1855
- }
1856
- if (c && len > 0) {
1857
- const lastV = v[len - 1];
1858
- const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
1859
- const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
1860
- const firstV = v[0];
1861
- const isLine = lastO[0] === lastV[0] && lastO[1] === lastV[1] && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
1862
- if (!isLine) {
1863
- d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
2861
+ (_a = n.children) == null ? void 0 : _a.forEach(walkAssign);
2862
+ };
2863
+ walkAssign(root);
2864
+ const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
2865
+ const newId = oldToNew.get(oldId);
2866
+ return newId ? "url(#" + newId + ")" : m;
2867
+ });
2868
+ const walkRewrite = (n) => {
2869
+ var _a;
2870
+ if (typeof n.href === "string" && n.href.startsWith("#")) {
2871
+ const newId = oldToNew.get(n.href.slice(1));
2872
+ if (newId) n.href = "#" + newId;
1864
2873
  }
1865
- d.push("z");
1866
- }
1867
- return d.join("");
2874
+ for (const k of Object.keys(n)) {
2875
+ if (k === "children" || k === "effects" || k === "meta" || k === "animate" || k === "href" || k === "id") continue;
2876
+ const v = n[k];
2877
+ if (typeof v === "string" && v.indexOf("url(#") !== -1) {
2878
+ n[k] = rewriteUrl(v);
2879
+ }
2880
+ }
2881
+ (_a = n.children) == null ? void 0 : _a.forEach(walkRewrite);
2882
+ };
2883
+ walkRewrite(root);
2884
+ return oldToNew;
1868
2885
  }
1869
- function interpolateNum(a, b, t) {
1870
- return a + (b - a) * t;
2886
+ function toFiniteNum(v) {
2887
+ const n = typeof v === "number" ? v : typeof v === "string" ? parseFloat(v) : NaN;
2888
+ return Number.isFinite(n) ? n : 0;
1871
2889
  }
1872
- function interpolateVec(a, b, t) {
1873
- const res = [];
1874
- const count = Math.max(a.length, b.length);
1875
- for (let i = 0; i < count; i++) {
1876
- res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
2890
+ function applyUseOffsetToG(gNode) {
2891
+ var _a;
2892
+ const x = toFiniteNum(gNode.x);
2893
+ const y = toFiniteNum(gNode.y);
2894
+ delete gNode.x;
2895
+ delete gNode.y;
2896
+ if (!x && !y) return gNode;
2897
+ const offset = "translate(" + x + "," + y + ")";
2898
+ const carriesTransform = gNode.transform !== void 0 || gNode.animate !== void 0;
2899
+ if (carriesTransform) {
2900
+ const inner = { type: "g", transform: offset, children: (_a = gNode.children) != null ? _a : [] };
2901
+ gNode.children = [inner];
2902
+ } else {
2903
+ gNode.transform = offset;
1877
2904
  }
1878
- return res;
2905
+ return gNode;
1879
2906
  }
1880
- function interpolateColor(a, b, t) {
1881
- return [
1882
- interpolateNum(a[0] || 0, b[0] || 0, t),
1883
- interpolateNum(a[1] || 0, b[1] || 0, t),
1884
- interpolateNum(a[2] || 0, b[2] || 0, t),
1885
- interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
1886
- ];
2907
+ function genId(ctx, prefix) {
2908
+ return "_lw_" + prefix + "_" + ctx.nextId++;
1887
2909
  }
1888
- function interpolateBeziers(paths1, paths2, progress) {
1889
- const count = Math.max(paths1.length, paths2.length);
2910
+ function stripHash(href) {
2911
+ return typeof href === "string" ? href.replace(/^#/, "") : void 0;
2912
+ }
2913
+ function indexById(node, map) {
2914
+ var _a;
2915
+ if (typeof node.id === "string") map.set(node.id, node);
2916
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
2917
+ }
2918
+ function spliceDefs(root, defs) {
2919
+ if (!defs.length) return;
2920
+ const existing = root.children || (root.children = []);
2921
+ existing.unshift({ type: "defs", children: defs });
2922
+ }
2923
+ var clone = deepClonePxNode;
2924
+ function regenerateIdsInClone(root, ctx) {
2925
+ return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
2926
+ }
2927
+ function applyFillGradientEffect(node, fx, ctx) {
2928
+ return applyGradient(node, fx, ctx, "fill");
2929
+ }
2930
+ function applyStrokeGradientEffect(node, fx, ctx) {
2931
+ return applyGradient(node, fx, ctx, "stroke");
2932
+ }
2933
+ function applyGradient(node, fx, ctx, attr) {
2934
+ if (!fx) return node;
2935
+ const id = genId(ctx, "grad");
2936
+ const def = synthesiseGradientDef(fx, id, ctx);
2937
+ ctx.defs.push(def);
2938
+ node[attr] = "url(#" + id + ")";
2939
+ return node;
2940
+ }
2941
+ function synthesiseGradientDef(fx, id, ctx) {
2942
+ const out = {
2943
+ type: fx.type === PxGradientType.radial ? "radialGradient" : "linearGradient",
2944
+ id
2945
+ };
2946
+ if (fx.type === PxGradientType.linear) {
2947
+ if (fx.p1) {
2948
+ out.x1 = String(fx.p1[0]);
2949
+ out.y1 = String(fx.p1[1]);
2950
+ }
2951
+ if (fx.p2) {
2952
+ out.x2 = String(fx.p2[0]);
2953
+ out.y2 = String(fx.p2[1]);
2954
+ }
2955
+ } else {
2956
+ if (fx.c) {
2957
+ out.cx = String(fx.c[0]);
2958
+ out.cy = String(fx.c[1]);
2959
+ }
2960
+ if (fx.r !== void 0) out.r = String(fx.r);
2961
+ if (fx.fp) {
2962
+ out.fx = String(fx.fp[0]);
2963
+ out.fy = String(fx.fp[1]);
2964
+ }
2965
+ }
2966
+ if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
2967
+ if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
2968
+ if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;
2969
+ out.children = buildStopChildren(fx.stops, ctx);
2970
+ return out;
2971
+ }
2972
+ function buildStopChildren(stops, ctx) {
2973
+ var _a, _b, _c, _d;
2974
+ if (!stops) return [];
2975
+ if (Array.isArray(stops)) return stops.map(staticStopNode);
2976
+ if (typeof stops === "object" && Array.isArray(stops.value)) {
2977
+ return stops.value.map(staticStopNode);
2978
+ }
2979
+ const animBlock = stops;
2980
+ const kfs = animBlock.keyframes;
2981
+ if (!Array.isArray(kfs) || !kfs.length) return [];
2982
+ let stopCount = 0;
2983
+ for (const kf of kfs) {
2984
+ const v = (_a = kf.value) != null ? _a : kf.v;
2985
+ if (Array.isArray(v) && v.length > stopCount) stopCount = v.length;
2986
+ }
2987
+ if (!stopCount) return [];
2988
+ const firstKfValue = (_b = kfs[0].value) != null ? _b : kfs[0].v;
2989
+ const baselineStops = [];
2990
+ for (let i = 0; i < stopCount; i++) {
2991
+ const s = (_d = (_c = firstKfValue == null ? void 0 : firstKfValue[i]) != null ? _c : prevDefinedStop(kfs, 0, i)) != null ? _d : { offset: i / Math.max(1, stopCount - 1), color: "#000000" };
2992
+ baselineStops.push({ offset: s.offset, color: s.color });
2993
+ }
2994
+ return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx));
2995
+ }
2996
+ function staticStopNode(s) {
2997
+ return {
2998
+ type: "stop",
2999
+ offset: formatOffset(s.offset),
3000
+ stopColor: s.color
3001
+ };
3002
+ }
3003
+ function animatedStopNode(baseline, kfs, stopIdx, _ctx) {
3004
+ var _a, _b, _c, _d, _e;
3005
+ const colorKfs = [];
3006
+ const offsetKfs = [];
3007
+ let offsetVaries = false;
3008
+ for (const kf of kfs) {
3009
+ const t = (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
3010
+ const arr = (_c = kf.value) != null ? _c : kf.v;
3011
+ const sliced = (_d = arr == null ? void 0 : arr[stopIdx]) != null ? _d : prevDefinedStop(kfs, kfs.indexOf(kf), stopIdx);
3012
+ if (!sliced) continue;
3013
+ const easing = (_e = kf.easing) != null ? _e : kf.e;
3014
+ const colorOut = { time: t, value: sliced.color };
3015
+ if (easing !== void 0) colorOut.easing = easing;
3016
+ colorKfs.push(colorOut);
3017
+ const offsetOut = { time: t, value: sliced.offset };
3018
+ if (easing !== void 0) offsetOut.easing = easing;
3019
+ offsetKfs.push(offsetOut);
3020
+ if (sliced.offset !== baseline.offset) offsetVaries = true;
3021
+ }
3022
+ const stop = {
3023
+ type: "stop",
3024
+ offset: formatOffset(baseline.offset),
3025
+ stopColor: baseline.color
3026
+ };
3027
+ const animate = {};
3028
+ if (colorKfs.length) animate.stopColor = { keyframes: colorKfs };
3029
+ if (offsetVaries && offsetKfs.length) animate.offset = { keyframes: offsetKfs };
3030
+ if (Object.keys(animate).length) stop.animate = animate;
3031
+ return stop;
3032
+ }
3033
+ function prevDefinedStop(kfs, fromIdx, stopIdx) {
3034
+ var _a, _b;
3035
+ for (let i = fromIdx; i >= 0; i--) {
3036
+ const arr = (_a = kfs[i].value) != null ? _a : kfs[i].v;
3037
+ if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
3038
+ }
3039
+ for (let i = fromIdx + 1; i < kfs.length; i++) {
3040
+ const arr = (_b = kfs[i].value) != null ? _b : kfs[i].v;
3041
+ if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
3042
+ }
3043
+ return void 0;
3044
+ }
3045
+ function formatOffset(o) {
3046
+ const pct = Math.round(o * 1e3) / 10;
3047
+ return pct + "%";
3048
+ }
3049
+ function applyMaskedByEffect(node, fx, transformation, ctx) {
3050
+ if (!fx) return node;
3051
+ if (!fx.href) {
3052
+ ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
3053
+ return node;
3054
+ }
3055
+ const maskId = genId(ctx, "mask");
3056
+ let content = { type: "use", href: "#" + fx.href };
3057
+ if (transformation) {
3058
+ content = wrapInverseTransform(content, transformation, ctx);
3059
+ } else if (hasAnimateTransform(node)) {
3060
+ content = wrapInverseAnimatedBodyTransform(content, node, ctx);
3061
+ } else {
3062
+ const bodyStatic = readTransformationFromBody(node);
3063
+ if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);
3064
+ }
3065
+ const includeTargetOwn = transformation === void 0 && !nodeHasBodyTransform(node);
3066
+ content = wrapAncestorChainCompensation(content, node, fx.href, ctx, includeTargetOwn);
3067
+ const mask = { type: "mask", id: maskId, children: [content] };
3068
+ if (fx.maskType) mask.maskType = fx.maskType;
3069
+ if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
3070
+ if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
3071
+ ctx.defs.push(mask);
3072
+ node.mask = "url(#" + maskId + ")";
3073
+ return node;
3074
+ }
3075
+ function wrapInverseTransform(inner, fx, ctx) {
3076
+ if (!fx) return inner;
3077
+ const origin = readStaticOrigin(fx.origin, ctx);
3078
+ let n = inner;
3079
+ n = wrapInversePart(n, "translate", fx.translate, void 0, ctx);
3080
+ n = wrapInversePart(n, "rotate", fx.rotate, origin, ctx);
3081
+ n = wrapInversePart(n, "scale", fx.scale, origin, ctx);
3082
+ return n;
3083
+ }
3084
+ function wrapInversePart(inner, part, raw, origin, ctx) {
3085
+ if (raw === void 0) return inner;
3086
+ const normalisedRaw = part === "scale" && Array.isArray(raw) ? [raw[0] / 100, raw[1] / 100] : raw;
3087
+ const v = readAnimatable(normalisedRaw);
3088
+ if (v.kind === "static") {
3089
+ return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
3090
+ }
3091
+ if (v.kind === "animated") {
3092
+ return {
3093
+ type: "g",
3094
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
3095
+ children: [inner]
3096
+ };
3097
+ }
3098
+ return inner;
3099
+ }
3100
+ function invertPartValue(part, value) {
3101
+ if (part === "translate") return [-value[0], -value[1]];
3102
+ if (part === "rotate") return -value;
3103
+ return [1 / value[0], 1 / value[1]];
3104
+ }
3105
+ function wrapInverseAnimatedBodyTransform(inner, node, _ctx) {
3106
+ var _a;
3107
+ const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3108
+ const animTr = animate == null ? void 0 : animate.transform;
3109
+ const kfs = animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes) ? animTr.keyframes : void 0;
3110
+ if (!kfs || !kfs.length) return inner;
3111
+ const translateKfs = [];
3112
+ const rotateKfs = [];
3113
+ const scaleKfs = [];
3114
+ for (const kf of kfs) {
3115
+ const v = ((_a = kf.value) != null ? _a : kf.v) || {};
3116
+ const baseKf = keyframeWith(kf, void 0);
3117
+ if (Array.isArray(v.translate)) {
3118
+ translateKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: { translate: [-v.translate[0], -v.translate[1]] } }));
3119
+ }
3120
+ if (typeof v.rotate === "number") {
3121
+ const rec = { rotate: -v.rotate };
3122
+ if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];
3123
+ rotateKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
3124
+ }
3125
+ if (Array.isArray(v.scale)) {
3126
+ const rec = { scale: [1 / v.scale[0], 1 / v.scale[1]] };
3127
+ if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];
3128
+ scaleKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
3129
+ }
3130
+ }
3131
+ let n = inner;
3132
+ if (translateKfs.length) n = { type: "g", animate: { transform: { keyframes: translateKfs } }, children: [n] };
3133
+ if (rotateKfs.length) n = { type: "g", animate: { transform: { keyframes: rotateKfs } }, children: [n] };
3134
+ if (scaleKfs.length) n = { type: "g", animate: { transform: { keyframes: scaleKfs } }, children: [n] };
3135
+ return n;
3136
+ }
3137
+ function nodeHasBodyTransform(node) {
3138
+ if (typeof node.transform === "string") return true;
3139
+ if (node.transform && typeof node.transform === "object") return true;
3140
+ return hasAnimateTransform(node);
3141
+ }
3142
+ function hasAnimateTransform(node) {
3143
+ const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3144
+ return !!(animate && animate.transform);
3145
+ }
3146
+ function readTransformationFromBody(node) {
3147
+ if (typeof node.transform === "string") {
3148
+ const parts = parseTransformStringToParts(node.transform);
3149
+ if (!parts) return void 0;
3150
+ const out = {};
3151
+ if (parts.translate) out.translate = parts.translate;
3152
+ if (parts.rotate !== void 0) out.rotate = parts.rotate;
3153
+ if (parts.scale) out.scale = { value: parts.scale };
3154
+ if (parts.origin) out.origin = parts.origin;
3155
+ return Object.keys(out).length ? out : void 0;
3156
+ }
3157
+ return void 0;
3158
+ }
3159
+ function parseTransformStringToParts(s) {
3160
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
3161
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
3162
+ let m;
3163
+ const ops = [];
3164
+ while ((m = re.exec(s)) !== null) {
3165
+ const args = m[2].split(/[\s,]+/).filter((a) => a.length > 0).map(Number);
3166
+ ops.push({ name: m[1], args });
3167
+ }
3168
+ if (!ops.length) return void 0;
3169
+ const last = ops[ops.length - 1];
3170
+ if (last.name === "translate") {
3171
+ for (let j = ops.length - 2; j >= 0; j--) {
3172
+ const cand = ops[j];
3173
+ if (cand.name !== "translate") continue;
3174
+ const ox = (_a = cand.args[0]) != null ? _a : 0;
3175
+ const oy = (_b = cand.args[1]) != null ? _b : 0;
3176
+ const lx = (_c = last.args[0]) != null ? _c : 0;
3177
+ const ly = (_d = last.args[1]) != null ? _d : 0;
3178
+ if (lx !== -ox || ly !== -oy) continue;
3179
+ const out2 = {};
3180
+ out2.origin = [ox, oy];
3181
+ for (let k = 0; k < j; k++) {
3182
+ if (ops[k].name === "translate") {
3183
+ const tx = (_e = ops[k].args[0]) != null ? _e : 0;
3184
+ const ty = (_f = ops[k].args[1]) != null ? _f : 0;
3185
+ out2.translate = out2.translate ? [out2.translate[0] + tx, out2.translate[1] + ty] : [tx, ty];
3186
+ }
3187
+ }
3188
+ for (let k = j + 1; k < ops.length - 1; k++) {
3189
+ const op = ops[k];
3190
+ if (op.name === "rotate") out2.rotate = ((_g = out2.rotate) != null ? _g : 0) + ((_h = op.args[0]) != null ? _h : 0);
3191
+ else if (op.name === "scale") {
3192
+ const sx = (_i = op.args[0]) != null ? _i : 1;
3193
+ const sy = op.args.length > 1 ? op.args[1] : sx;
3194
+ out2.scale = out2.scale ? [out2.scale[0] * sx, out2.scale[1] * sy] : [sx, sy];
3195
+ }
3196
+ }
3197
+ return out2;
3198
+ }
3199
+ }
3200
+ let translate;
3201
+ let rotate;
3202
+ let scale;
3203
+ for (const op of ops) {
3204
+ if (op.name === "translate") {
3205
+ const dx = (_j = op.args[0]) != null ? _j : 0;
3206
+ const dy = (_k = op.args[1]) != null ? _k : 0;
3207
+ translate = translate ? [translate[0] + dx, translate[1] + dy] : [dx, dy];
3208
+ } else if (op.name === "rotate") {
3209
+ rotate = (rotate != null ? rotate : 0) + ((_l = op.args[0]) != null ? _l : 0);
3210
+ } else if (op.name === "scale") {
3211
+ const sx = (_m = op.args[0]) != null ? _m : 1;
3212
+ const sy = op.args.length > 1 ? op.args[1] : sx;
3213
+ scale = scale ? [scale[0] * sx, scale[1] * sy] : [sx, sy];
3214
+ }
3215
+ }
3216
+ const out = {};
3217
+ if (translate) out.translate = translate;
3218
+ if (rotate !== void 0) out.rotate = rotate;
3219
+ if (scale) out.scale = scale;
3220
+ return Object.keys(out).length ? out : void 0;
3221
+ }
3222
+ function wrapAncestorChainCompensation(inner, maskedNode, sourceId, ctx, includeTargetOwn) {
3223
+ const sourceNode = ctx.idMap.get(sourceId);
3224
+ const targetAncestors = ctx.maskAncestorChains.get(maskedNode) || [];
3225
+ const targetOwn = includeTargetOwn ? extractTranslateOnly(maskedNode, ctx) : void 0;
3226
+ const targetChain = targetOwn ? [...targetAncestors, targetOwn] : targetAncestors;
3227
+ const sourceChain = sourceNode && ctx.maskAncestorChains.get(sourceNode) || [];
3228
+ if (!targetChain.length && !sourceChain.length) return inner;
3229
+ const times = /* @__PURE__ */ new Set();
3230
+ for (const a of targetChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);
3231
+ for (const a of sourceChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);
3232
+ const animated = times.size > 0;
3233
+ if (!animated) {
3234
+ const tgt = sumStaticTranslate(targetChain);
3235
+ const src = sumStaticTranslate(sourceChain);
3236
+ const dx = src[0] - tgt[0];
3237
+ const dy = src[1] - tgt[1];
3238
+ if (dx === 0 && dy === 0) return inner;
3239
+ return { type: "g", transform: "translate(" + dx + "," + dy + ")", children: [inner] };
3240
+ }
3241
+ const sortedTimes = Array.from(times).sort((a, b) => a - b);
3242
+ const keyframes = sortedTimes.map((t) => {
3243
+ const tgt = sumTranslateAt(targetChain, t);
3244
+ const src = sumTranslateAt(sourceChain, t);
3245
+ return { time: t, value: { translate: [src[0] - tgt[0], src[1] - tgt[1]] } };
3246
+ });
3247
+ return { type: "g", animate: { transform: { keyframes } }, children: [inner] };
3248
+ }
3249
+ function sumStaticTranslate(chain) {
3250
+ let x = 0, y = 0;
3251
+ for (const a of chain) {
3252
+ if (a.translate) {
3253
+ x += a.translate[0];
3254
+ y += a.translate[1];
3255
+ }
3256
+ }
3257
+ return [x, y];
3258
+ }
3259
+ function sumTranslateAt(chain, t) {
3260
+ let x = 0, y = 0;
3261
+ for (const a of chain) {
3262
+ if (a.translateKeyframes && a.translateKeyframes.length) {
3263
+ const v = interpKfs(a.translateKeyframes, t);
3264
+ x += v[0];
3265
+ y += v[1];
3266
+ } else if (a.translate) {
3267
+ x += a.translate[0];
3268
+ y += a.translate[1];
3269
+ }
3270
+ }
3271
+ return [x, y];
3272
+ }
3273
+ function interpKfs(kfs, t) {
3274
+ if (t <= kfs[0].time) return kfs[0].value;
3275
+ if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;
3276
+ for (let i = 1; i < kfs.length; i++) {
3277
+ if (t <= kfs[i].time) {
3278
+ const prev = kfs[i - 1];
3279
+ const cur = kfs[i];
3280
+ const a = (t - prev.time) / (cur.time - prev.time);
3281
+ return [prev.value[0] + (cur.value[0] - prev.value[0]) * a, prev.value[1] + (cur.value[1] - prev.value[1]) * a];
3282
+ }
3283
+ }
3284
+ return kfs[kfs.length - 1].value;
3285
+ }
3286
+ function collectMaskAncestorChains(root, ctx) {
3287
+ const interestingNodes = /* @__PURE__ */ new Set();
3288
+ const collectInterestingNodes = (n) => {
3289
+ var _a, _b;
3290
+ const href = (_b = (_a = n.effects) == null ? void 0 : _a.maskedBy) == null ? void 0 : _b.href;
3291
+ if (typeof href === "string") {
3292
+ interestingNodes.add(n);
3293
+ const sourceNode = ctx.idMap.get(href);
3294
+ if (sourceNode) interestingNodes.add(sourceNode);
3295
+ }
3296
+ if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);
3297
+ };
3298
+ collectInterestingNodes(root);
3299
+ if (interestingNodes.size === 0) return;
3300
+ const walk = (node, chain) => {
3301
+ if (interestingNodes.has(node)) ctx.maskAncestorChains.set(node, chain);
3302
+ if (Array.isArray(node.children)) {
3303
+ const own = extractTranslateOnly(node, ctx);
3304
+ const next = own ? [...chain, own] : chain;
3305
+ for (const ch of node.children) walk(ch, next);
3306
+ }
3307
+ };
3308
+ walk(root, []);
3309
+ }
3310
+ function extractTranslateOnly(node, ctx) {
3311
+ var _a, _b, _c;
3312
+ const tr = node.transform;
3313
+ const animateBlock = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate.transform : void 0;
3314
+ if (tr === void 0 && !animateBlock) return void 0;
3315
+ const out = {};
3316
+ if (typeof tr === "string") {
3317
+ const parts = parseTranslateOnlyFromString(tr, ctx);
3318
+ if (parts) out.translate = parts;
3319
+ } else if (tr && typeof tr === "object") {
3320
+ const value = tr.value;
3321
+ if (value && typeof value === "object" && Array.isArray(value.translate)) {
3322
+ out.translate = [value.translate[0] || 0, value.translate[1] || 0];
3323
+ }
3324
+ if (value && (value.rotate !== void 0 || value.scale !== void 0 || value.skew !== void 0)) {
3325
+ ctx.warnings.push("maskedBy ancestor: non-translate transform parts ignored (rotate/scale not yet supported)");
3326
+ }
3327
+ }
3328
+ if (animateBlock && Array.isArray(animateBlock.keyframes)) {
3329
+ const kfs = animateBlock.keyframes;
3330
+ const translateKfs = [];
3331
+ for (const kf of kfs) {
3332
+ const v = (_a = kf.value) != null ? _a : kf.v;
3333
+ const t = (_c = (_b = kf.time) != null ? _b : kf.t) != null ? _c : 0;
3334
+ if (v && typeof v === "object" && Array.isArray(v.translate)) {
3335
+ translateKfs.push({ time: t, value: [v.translate[0] || 0, v.translate[1] || 0] });
3336
+ if (v.rotate !== void 0 || v.scale !== void 0 || v.skew !== void 0) {
3337
+ ctx.warnings.push("maskedBy ancestor: animated non-translate parts ignored");
3338
+ }
3339
+ }
3340
+ }
3341
+ if (translateKfs.length) out.translateKeyframes = translateKfs;
3342
+ }
3343
+ return out.translate || out.translateKeyframes ? out : void 0;
3344
+ }
3345
+ function parseTranslateOnlyFromString(s, ctx) {
3346
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
3347
+ let m;
3348
+ let x = 0, y = 0;
3349
+ let seen = false;
3350
+ let droppedNonTranslate = false;
3351
+ while ((m = re.exec(s)) !== null) {
3352
+ const name = m[1];
3353
+ const args = m[2].split(/[\s,]+/).filter((a) => a.length > 0).map(Number);
3354
+ if (name === "translate") {
3355
+ x += args[0] || 0;
3356
+ y += args[1] || 0;
3357
+ seen = true;
3358
+ } else {
3359
+ droppedNonTranslate = true;
3360
+ }
3361
+ }
3362
+ if (droppedNonTranslate) ctx.warnings.push("maskedBy ancestor: non-translate transform in string ignored: " + s);
3363
+ return seen ? [x, y] : void 0;
3364
+ }
3365
+ var CONTENT_SUBREF = "content";
3366
+ function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
3367
+ if (ref) {
3368
+ const baseId = ref.baseId;
3369
+ if (!baseId) {
3370
+ ctx.errors.push("ref: missing baseId");
3371
+ } else {
3372
+ const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
3373
+ node.href = "#" + targetId;
3374
+ }
3375
+ }
3376
+ return applyTransformationEffect(node, transformation, ctx);
3377
+ }
3378
+ function applyRepeaterEffect(node, fx, ctx) {
3379
+ var _a, _b;
3380
+ if (!fx) return node;
3381
+ const copies = (_a = fx.copies) != null ? _a : 1;
3382
+ if (copies < 1) {
3383
+ ctx.errors.push("repeater.copies invalid: " + fx.copies);
3384
+ return node;
3385
+ }
3386
+ const sharedTransform = node.transform;
3387
+ const sharedAnimTransform = (_b = node.animate) == null ? void 0 : _b.transform;
3388
+ const base = clone(node);
3389
+ delete base.transform;
3390
+ if (base.animate) {
3391
+ delete base.animate.transform;
3392
+ if (Object.keys(base.animate).length === 0) delete base.animate;
3393
+ }
3394
+ const children = [base];
3395
+ for (let i = 1; i < copies; i++) {
3396
+ const baseClone = clone(base);
3397
+ const synthFx = synthesisePerCopyFx(fx, i);
3398
+ const wrapped = synthFx ? applyTransformationEffect(baseClone, synthFx, ctx) : baseClone;
3399
+ children.push(wrapped);
3400
+ }
3401
+ const wrapper = { type: "g", children };
3402
+ if (sharedTransform !== void 0) wrapper.transform = sharedTransform;
3403
+ if (sharedAnimTransform !== void 0) wrapper.animate = { transform: sharedAnimTransform };
3404
+ return wrapper;
3405
+ }
3406
+ function synthesisePerCopyFx(fx, i) {
3407
+ const out = {};
3408
+ if (fx.translate !== void 0) {
3409
+ out.translate = mapAnimatableVec2(fx.translate, (v) => [v[0] * i, v[1] * i]);
3410
+ }
3411
+ if (fx.rotate !== void 0) {
3412
+ out.rotate = mapAnimatableNumber(fx.rotate, (v) => v * i);
3413
+ }
3414
+ if (fx.scale !== void 0) {
3415
+ out.scale = synthesiseScale(fx.scale, i);
3416
+ }
3417
+ if (fx.origin !== void 0) {
3418
+ out.origin = fx.origin;
3419
+ }
3420
+ return Object.keys(out).length ? out : void 0;
3421
+ }
3422
+ function mapAnimatableVec2(raw, fn) {
3423
+ if (Array.isArray(raw)) return fn(raw);
3424
+ if (raw && typeof raw === "object") {
3425
+ const obj = raw;
3426
+ if (Array.isArray(obj.keyframes)) {
3427
+ return __spreadProps(__spreadValues({}, obj), {
3428
+ keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
3429
+ });
3430
+ }
3431
+ if (obj.value !== void 0) {
3432
+ return __spreadProps(__spreadValues({}, obj), { value: fn(obj.value) });
3433
+ }
3434
+ }
3435
+ return raw;
3436
+ }
3437
+ function mapAnimatableNumber(raw, fn) {
3438
+ if (typeof raw === "number") return fn(raw);
3439
+ if (raw && typeof raw === "object") {
3440
+ const obj = raw;
3441
+ if (Array.isArray(obj.keyframes)) {
3442
+ return __spreadProps(__spreadValues({}, obj), {
3443
+ keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
3444
+ });
3445
+ }
3446
+ if (obj.value !== void 0) {
3447
+ return __spreadProps(__spreadValues({}, obj), { value: fn(obj.value) });
3448
+ }
3449
+ }
3450
+ return raw;
3451
+ }
3452
+ function synthesiseScale(raw, i) {
3453
+ const scalePowerFromPercent = (v) => [Math.pow(v[0] / 100, i), Math.pow(v[1] / 100, i)];
3454
+ const scalePowerFromUnits = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
3455
+ if (Array.isArray(raw)) {
3456
+ return { value: scalePowerFromPercent(raw) };
3457
+ }
3458
+ if (raw && typeof raw === "object") {
3459
+ const obj = raw;
3460
+ if (Array.isArray(obj.keyframes)) {
3461
+ return __spreadProps(__spreadValues({}, obj), {
3462
+ keyframes: obj.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: scalePowerFromUnits(kf.value) }) : kf)
3463
+ });
3464
+ }
3465
+ if (obj.value !== void 0) {
3466
+ return __spreadProps(__spreadValues({}, obj), { value: scalePowerFromPercent(obj.value) });
3467
+ }
3468
+ }
3469
+ return raw;
3470
+ }
3471
+ var RETIME_MATERIALISATION_MODE_INLINE_G = false;
3472
+ function asRetime(r) {
3473
+ var _a, _b;
3474
+ return { start: (_a = r.start) != null ? _a : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
3475
+ }
3476
+ function concatRetime(child, parent) {
3477
+ return {
3478
+ start: parent.start + parent.stretch * child.start,
3479
+ stretch: parent.stretch * child.stretch
3480
+ };
3481
+ }
3482
+ function applyAllRetimeEffects(root, ctx) {
3483
+ var _a;
3484
+ ctx.idMap.clear();
3485
+ indexById(root, ctx.idMap);
3486
+ const sites = [];
3487
+ const collect = (n) => {
3488
+ var _a2, _b;
3489
+ if ((_a2 = n.effects) == null ? void 0 : _a2.retime) sites.push(n);
3490
+ (_b = n.children) == null ? void 0 : _b.forEach(collect);
3491
+ };
3492
+ collect(root);
3493
+ for (const useNode of sites) {
3494
+ const retime = (_a = useNode.effects) == null ? void 0 : _a.retime;
3495
+ if (!retime) continue;
3496
+ delete useNode.effects.retime;
3497
+ if (Object.keys(useNode.effects).length === 0) delete useNode.effects;
3498
+ materialiseRetime(useNode, asRetime(retime), ctx);
3499
+ }
3500
+ }
3501
+ function materialiseRetime(useNode, retime, ctx) {
3502
+ const targetId = stripHash(useNode.href);
3503
+ if (!targetId) {
3504
+ ctx.errors.push("retime: <use> has no href to follow");
3505
+ return;
3506
+ }
3507
+ const chainRootId = buildChainClone(targetId, retime, ctx, /* @__PURE__ */ new Set());
3508
+ if (!chainRootId) return;
3509
+ if (RETIME_MATERIALISATION_MODE_INLINE_G) {
3510
+ const cloneNode = ctx.idMap.get(chainRootId);
3511
+ useNode.type = "g";
3512
+ delete useNode.href;
3513
+ useNode.children = [cloneNode];
3514
+ applyUseOffsetToG(useNode);
3515
+ ctx.defs = ctx.defs.filter((d) => d !== cloneNode);
3516
+ } else {
3517
+ useNode.href = "#" + chainRootId;
3518
+ }
3519
+ }
3520
+ function buildChainClone(targetId, accum, ctx, chain) {
3521
+ var _a, _b;
3522
+ if (chain.has(targetId)) {
3523
+ ctx.errors.push('retime: loop via "' + targetId + '"');
3524
+ return void 0;
3525
+ }
3526
+ const target = ctx.idMap.get(targetId);
3527
+ if (!target) {
3528
+ ctx.warnings.push('retime: target "' + targetId + '" not found');
3529
+ return void 0;
3530
+ }
3531
+ const cloneNode = clone(target);
3532
+ regenerateIdsInClone(cloneNode, ctx);
3533
+ if (target.type === "use") {
3534
+ remapKeyframeTimesOnly(cloneNode, accum.start, accum.stretch);
3535
+ } else {
3536
+ remapKeyframeTimes(cloneNode, accum.start, accum.stretch);
3537
+ }
3538
+ if ((_a = cloneNode.effects) == null ? void 0 : _a.retime) {
3539
+ delete cloneNode.effects.retime;
3540
+ if (Object.keys(cloneNode.effects).length === 0) delete cloneNode.effects;
3541
+ }
3542
+ if (target.type === "use" && target.href) {
3543
+ const subId = stripHash(target.href);
3544
+ if (subId) {
3545
+ const innerRetime = (_b = target.effects) == null ? void 0 : _b.retime;
3546
+ const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;
3547
+ const subChain = new Set(chain);
3548
+ subChain.add(targetId);
3549
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
3550
+ if (subId2) cloneNode.href = "#" + subId2;
3551
+ }
3552
+ }
3553
+ ctx.defs.push(cloneNode);
3554
+ if (typeof cloneNode.id === "string") ctx.idMap.set(cloneNode.id, cloneNode);
3555
+ return typeof cloneNode.id === "string" ? cloneNode.id : void 0;
3556
+ }
3557
+ function remapKeyframeTimes(node, start, stretch) {
3558
+ var _a;
3559
+ remapKeyframeTimesOnly(node, start, stretch);
3560
+ (_a = node.children) == null ? void 0 : _a.forEach((c) => remapKeyframeTimes(c, start, stretch));
3561
+ }
3562
+ function remapKeyframeTimesOnly(node, start, stretch) {
3563
+ const remap2 = (kfs) => {
3564
+ for (const kf of kfs) if (typeof kf.time === "number") kf.time = start + kf.time * stretch;
3565
+ };
3566
+ if (node.transform && typeof node.transform === "object" && Array.isArray(node.transform.keyframes)) {
3567
+ remap2(node.transform.keyframes);
3568
+ }
3569
+ if (node.animate && typeof node.animate === "object") {
3570
+ for (const prop of Object.keys(node.animate)) {
3571
+ const anim = node.animate[prop];
3572
+ if (anim && Array.isArray(anim.keyframes)) remap2(anim.keyframes);
3573
+ }
3574
+ }
3575
+ }
3576
+ function applyTextAlongPathEffect(node, fx, _ctx) {
3577
+ var _a;
3578
+ if (!fx) return node;
3579
+ const href = typeof fx.href === "string" && !fx.href.startsWith("#") ? "#" + fx.href : fx.href;
3580
+ const textPath = {
3581
+ type: "textPath",
3582
+ href,
3583
+ children: (_a = node.children) != null ? _a : []
3584
+ };
3585
+ if (fx.lengthAdjust !== void 0) textPath.lengthAdjust = fx.lengthAdjust;
3586
+ if (fx.method !== void 0) textPath.method = fx.method;
3587
+ if (fx.spacing !== void 0) textPath.spacing = fx.spacing;
3588
+ applyAnimatableNumber(textPath, "startOffset", fx.startOffset);
3589
+ applyAnimatableNumber(textPath, "textLength", fx.textLength);
3590
+ node.children = [textPath];
3591
+ return node;
3592
+ }
3593
+ function applyAnimatableNumber(node, attrName, raw) {
3594
+ if (raw === void 0 || raw === null) return;
3595
+ if (typeof raw === "number") {
3596
+ node[attrName] = String(raw);
3597
+ return;
3598
+ }
3599
+ if (typeof raw === "object") {
3600
+ const obj = raw;
3601
+ if (Array.isArray(obj.keyframes)) {
3602
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3603
+ const animate = __spreadValues({}, prevAnimate || {});
3604
+ animate[attrName] = { keyframes: obj.keyframes };
3605
+ node.animate = animate;
3606
+ return;
3607
+ }
3608
+ if (typeof obj.value === "number") {
3609
+ node[attrName] = String(obj.value);
3610
+ return;
3611
+ }
3612
+ }
3613
+ }
3614
+ function bezierToSvgPath(path) {
3615
+ var _a, _b, _c, _d;
3616
+ const v = path.v;
3617
+ const i = path.i;
3618
+ const o = path.o;
3619
+ const c = path.c;
3620
+ if (!v.length) return "";
3621
+ const d = [];
3622
+ const len = v.length;
3623
+ d.push("M" + v[0][0] + "," + v[0][1]);
3624
+ for (let idx = 1; idx < len; idx++) {
3625
+ const prevV = v[idx - 1];
3626
+ const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
3627
+ const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
3628
+ const currV = v[idx];
3629
+ const isLine = prevO[0] === prevV[0] && prevO[1] === prevV[1] && (currI[0] === currV[0] && currI[1] === currV[1]);
3630
+ if (isLine) {
3631
+ d.push("L" + currV[0] + "," + currV[1]);
3632
+ } else {
3633
+ d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
3634
+ }
3635
+ }
3636
+ if (c && len > 0) {
3637
+ const lastV = v[len - 1];
3638
+ const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
3639
+ const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
3640
+ const firstV = v[0];
3641
+ const isLine = lastO[0] === lastV[0] && lastO[1] === lastV[1] && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
3642
+ if (!isLine) {
3643
+ d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
3644
+ }
3645
+ d.push("z");
3646
+ }
3647
+ return d.join("");
3648
+ }
3649
+ function interpolateNum(a, b, t) {
3650
+ return a + (b - a) * t;
3651
+ }
3652
+ function interpolateVec(a, b, t) {
3653
+ const res = [];
3654
+ const count = Math.max(a.length, b.length);
3655
+ for (let i = 0; i < count; i++) {
3656
+ res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
3657
+ }
3658
+ return res;
3659
+ }
3660
+ function interpolateColor(a, b, t) {
3661
+ return [
3662
+ interpolateNum(a[0] || 0, b[0] || 0, t),
3663
+ interpolateNum(a[1] || 0, b[1] || 0, t),
3664
+ interpolateNum(a[2] || 0, b[2] || 0, t),
3665
+ interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
3666
+ ];
3667
+ }
3668
+ function interpolateBeziers(paths1, paths2, progress) {
3669
+ const count = Math.max(paths1.length, paths2.length);
1890
3670
  const res = [];
1891
3671
  for (let i = 0; i < count; i++) {
1892
3672
  res.push(interpolateBezier(paths1[i], paths2[i], progress));
@@ -2063,6 +3843,24 @@ var PixodeskAnimatorReact = (() => {
2063
3843
  var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
2064
3844
  var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
2065
3845
  var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
3846
+ function composeTransformParts(parts, opts) {
3847
+ var _a;
3848
+ if (!parts) return "";
3849
+ const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
3850
+ const segs = [];
3851
+ const t = parts.translate;
3852
+ const o = parts.origin;
3853
+ const r = parts.rotate;
3854
+ const s = parts.scale;
3855
+ const tu = withUnits ? "px" : "";
3856
+ const ru = withUnits ? "deg" : "";
3857
+ if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
3858
+ if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
3859
+ if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
3860
+ if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
3861
+ if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
3862
+ return segs.join("");
3863
+ }
2066
3864
  var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
2067
3865
  var DEFAULT_DURATION_MS = 1e3;
2068
3866
  function kebabToCamelCaseWord(kebab) {
@@ -2087,6 +3885,12 @@ var PixodeskAnimatorReact = (() => {
2087
3885
  "clipPathUnits",
2088
3886
  "maskUnits",
2089
3887
  "maskContentUnits",
3888
+ // Marker (SVG spec keeps these camelCase, like viewBox)
3889
+ "markerUnits",
3890
+ "markerWidth",
3891
+ "markerHeight",
3892
+ "refX",
3893
+ "refY",
2090
3894
  // Text
2091
3895
  "textLength",
2092
3896
  "lengthAdjust",
@@ -2122,284 +3926,454 @@ var PixodeskAnimatorReact = (() => {
2122
3926
  function clamp(value, min, max) {
2123
3927
  return Math.max(min, Math.min(value, max));
2124
3928
  }
2125
- var SVG_NS = "http://www.w3.org/2000/svg";
2126
- var ALLOWED_SVG_TAGS_LOWER_CASE = new Set([
2127
- "svg",
2128
- "g",
2129
- "path",
2130
- "circle",
2131
- "ellipse",
2132
- "rect",
2133
- "line",
2134
- "polyline",
2135
- "polygon",
2136
- "text",
2137
- "tspan",
2138
- "defs",
2139
- "clipPath",
2140
- "mask",
2141
- "pattern",
2142
- "linearGradient",
2143
- "radialGradient",
2144
- "stop",
2145
- "use",
2146
- "symbol",
2147
- "marker",
2148
- "filter",
2149
- "feGaussianBlur",
2150
- "feOffset",
2151
- "feBlend",
2152
- "feColorMatrix",
2153
- "feMerge",
2154
- "feMergeNode"
2155
- ].map((tagName) => tagName.toLowerCase()));
2156
- var ALLOWED_RESOURCE_ATTRIBUTES = [
2157
- "href",
2158
- // <use>
2159
- "src",
2160
- // <image>
2161
- "filter",
2162
- // url(#filterId)
2163
- "clipPath",
2164
- // clip-path="url(#clipPathId)"
2165
- "mask",
2166
- // url(#maskId)
2167
- "markerStart",
2168
- // marker-start="url(#markerId)"
2169
- "markerMid",
2170
- // marker-mid="url(#markerId)"
2171
- "markerEnd"
2172
- // marker-end="url(#markerId)"
2173
- // 'fill', // url(#gradientId) or url(#patternId)
2174
- // 'stroke', // url(#gradientId) or url(#patternId)
2175
- // Don't allow 'cursor', can use external SVG, // url(cursor.svg)
2176
- ];
2177
- var ALLOWED_RESOURCE_ATTRIBUTES_SET = new Set(ALLOWED_RESOURCE_ATTRIBUTES);
2178
- var ALLOWED_ATTRIBUTES_SET = /* @__PURE__ */ new Set([
2179
- "href",
2180
- "src",
2181
- // Presentation
2182
- "fill",
2183
- "stroke",
2184
- "strokeWidth",
2185
- "opacity",
2186
- "transform",
2187
- // Geometry
2188
- "x",
2189
- "y",
2190
- "cx",
2191
- "cy",
2192
- "r",
2193
- "rx",
2194
- "ry",
2195
- "width",
2196
- "height",
2197
- "d",
2198
- "x1",
2199
- "y1",
2200
- "x2",
2201
- "y2",
2202
- "points",
2203
- // Text
2204
- "fontSize",
2205
- "fontFamily",
2206
- "textAnchor",
2207
- // Structure
2208
- "id",
2209
- "class",
2210
- "viewBox",
2211
- "preserveAspectRatio",
2212
- // Gradient/Pattern
2213
- "offset",
2214
- "stopColor",
2215
- "stopOpacity",
2216
- "gradientTransform",
2217
- // Clippath/Mask
2218
- "clipPath",
2219
- "mask",
2220
- // Filter
2221
- "filter",
2222
- "stdDeviation",
2223
- "in",
2224
- "in2",
2225
- "result",
2226
- "mode",
2227
- ...ALLOWED_RESOURCE_ATTRIBUTES
2228
- ]);
2229
- function sanitiseAttributeValue(name, value) {
2230
- const nameLower = name.toLowerCase();
2231
- if (!ALLOWED_ATTRIBUTES_SET.has(nameLower)) {
2232
- console.warn("Attribute not in whitelist: ", nameLower);
2233
- return void 0;
3929
+ function bezier2D_pointAt(P0, P1, P2, P3, t) {
3930
+ if (t <= 0) return [P0[0], P0[1]];
3931
+ if (t >= 1) return [P3[0], P3[1]];
3932
+ const u = 1 - t;
3933
+ const u2 = u * u;
3934
+ const u3 = u2 * u;
3935
+ const t2 = t * t;
3936
+ const t3 = t2 * t;
3937
+ const w0 = u3;
3938
+ const w1 = 3 * t * u2;
3939
+ const w2 = 3 * t2 * u;
3940
+ const w3 = t3;
3941
+ return [
3942
+ w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
3943
+ w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
3944
+ ];
3945
+ }
3946
+ var BEZIER_T_NUDGE = 1e-4;
3947
+ function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
3948
+ const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
3949
+ if (result[0] === 0 && result[1] === 0) {
3950
+ const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
3951
+ return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
2234
3952
  }
2235
- if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopColor") {
2236
- const str = String(value);
2237
- if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
2238
- console.warn('Attribute "' + nameLower + '" blocked: url() references must be internal url(#id), got:', value);
2239
- return void 0;
2240
- }
2241
- return value;
3953
+ return result;
3954
+ }
3955
+ function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
3956
+ const u = 1 - t;
3957
+ const a = 3 * u * u;
3958
+ const b = 6 * t * u;
3959
+ const c = 3 * t * t;
3960
+ return [
3961
+ a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
3962
+ a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
3963
+ ];
3964
+ }
3965
+ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
3966
+ const n = steps + 1;
3967
+ const ts = new Float64Array(n);
3968
+ const ds = new Float64Array(n);
3969
+ let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
3970
+ ts[0] = 0;
3971
+ ds[0] = 0;
3972
+ let cum = 0;
3973
+ for (let i = 1; i < n; i++) {
3974
+ const t = i / steps;
3975
+ const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
3976
+ const dx = cur[0] - prev[0];
3977
+ const dy = cur[1] - prev[1];
3978
+ cum += Math.sqrt(dx * dx + dy * dy);
3979
+ ts[i] = t;
3980
+ ds[i] = cum;
3981
+ prev = cur;
2242
3982
  }
2243
- if (ALLOWED_RESOURCE_ATTRIBUTES_SET.has(nameLower)) {
2244
- const str = String(value);
2245
- if (str.startsWith("#")) {
2246
- return value;
3983
+ return { ts, ds };
3984
+ }
3985
+ function bezier2D_arcAtT(lut, t) {
3986
+ const { ts, ds } = lut;
3987
+ const last = ts.length - 1;
3988
+ if (t <= ts[0]) return ds[0];
3989
+ if (t >= ts[last]) return ds[last];
3990
+ let lo = 1, hi = last;
3991
+ while (lo < hi) {
3992
+ const mid = lo + hi >>> 1;
3993
+ if (ts[mid] < t) lo = mid + 1;
3994
+ else hi = mid;
3995
+ }
3996
+ const tPrev = ts[hi - 1];
3997
+ const span = ts[hi] - tPrev;
3998
+ const frac = span > 0 ? (t - tPrev) / span : 0;
3999
+ return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);
4000
+ }
4001
+ function invertEasing(easing) {
4002
+ if (!easing) return (y) => y;
4003
+ const flipped = [easing[1], easing[0], easing[3], easing[2]];
4004
+ return cubicBezier(flipped);
4005
+ }
4006
+ function getKfTranslate(kf) {
4007
+ var _a;
4008
+ const v = (_a = kf.value) != null ? _a : kf.v;
4009
+ if (!v) return void 0;
4010
+ if (Array.isArray(v) && v.length >= 2 && typeof v[0] === "number" && typeof v[1] === "number") {
4011
+ return [v[0], v[1]];
4012
+ }
4013
+ const tr = v.translate;
4014
+ if (Array.isArray(tr) && tr.length >= 2) return [tr[0], tr[1]];
4015
+ return void 0;
4016
+ }
4017
+ function getKfTime(kf) {
4018
+ var _a, _b;
4019
+ return (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0;
4020
+ }
4021
+ function getKfEasing(kf) {
4022
+ var _a;
4023
+ return (_a = kf.easing) != null ? _a : kf.e;
4024
+ }
4025
+ function propAnimIsMotionPath(anim) {
4026
+ var _a, _b, _c;
4027
+ const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
4028
+ if (!Array.isArray(kfs)) return false;
4029
+ if (anim.autoOrient) return true;
4030
+ for (const kf of kfs) {
4031
+ if (((_b = kf.tangentIn) != null ? _b : kf.ti) || ((_c = kf.tangentOut) != null ? _c : kf.to)) return true;
4032
+ }
4033
+ return false;
4034
+ }
4035
+ var _segmentCache = /* @__PURE__ */ new WeakMap();
4036
+ function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
4037
+ var _a, _b;
4038
+ const existing = _segmentCache.get(prevKf);
4039
+ if (existing) return existing;
4040
+ const to = (_a = prevKf.tangentOut) != null ? _a : prevKf.to;
4041
+ const ti = (_b = nextKf.tangentIn) != null ? _b : nextKf.ti;
4042
+ const P1 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];
4043
+ const P2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];
4044
+ const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);
4045
+ const entry = {
4046
+ P0: prevPos,
4047
+ P1,
4048
+ P2,
4049
+ P3: nextPos,
4050
+ lut,
4051
+ totalArc: lut.ds[lut.ds.length - 1]
4052
+ };
4053
+ _segmentCache.set(prevKf, entry);
4054
+ return entry;
4055
+ }
4056
+ function evaluateMotionPathSegment(prevKf, nextKf, prevPos, nextPos, localProgress, autoOrient) {
4057
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
4058
+ const t = seg.totalArc === 0 ? localProgress : tFromArcFraction(seg.lut, localProgress);
4059
+ const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
4060
+ if (!autoOrient) return { translate: point };
4061
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
4062
+ const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
4063
+ return { translate: point, rotateDeg };
4064
+ }
4065
+ function tFromArcFraction(lut, arcFrac) {
4066
+ const total = lut.ds[lut.ds.length - 1];
4067
+ const target = arcFrac * total;
4068
+ const { ts, ds } = lut;
4069
+ const last = ds.length - 1;
4070
+ if (target <= 0) return ts[0];
4071
+ if (target >= ds[last]) return ts[last];
4072
+ let lo = 1, hi = last;
4073
+ while (lo < hi) {
4074
+ const mid = lo + hi >>> 1;
4075
+ if (ds[mid] < target) lo = mid + 1;
4076
+ else hi = mid;
4077
+ }
4078
+ const dPrev = ds[hi - 1];
4079
+ const span = ds[hi] - dPrev;
4080
+ const frac = span > 0 ? (target - dPrev) / span : 0;
4081
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
4082
+ }
4083
+ var DEFAULT_FLATNESS_TOL = 0.5;
4084
+ var DEFAULT_ROTATION_TOL = 5;
4085
+ var DEFAULT_MAX_SAMPLES = 32;
4086
+ function materialiseMotionPathInPropAnim(anim, opts) {
4087
+ var _a, _b, _c, _d;
4088
+ if (!propAnimIsMotionPath(anim)) return anim;
4089
+ const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
4090
+ if (!Array.isArray(kfs) || kfs.length < 2) return anim;
4091
+ const autoOrient = !!anim.autoOrient;
4092
+ const flatnessTol = (_b = opts == null ? void 0 : opts.flatnessTolerance) != null ? _b : DEFAULT_FLATNESS_TOL;
4093
+ const rotationTol = (_c = opts == null ? void 0 : opts.rotationTolerance) != null ? _c : DEFAULT_ROTATION_TOL;
4094
+ const maxSamples = (_d = opts == null ? void 0 : opts.maxSamplesPerSegment) != null ? _d : DEFAULT_MAX_SAMPLES;
4095
+ const out = [];
4096
+ const firstPos = getKfTranslate(kfs[0]);
4097
+ if (!firstPos) return anim;
4098
+ const firstRotate = autoOrient ? derivAngleForFirstKf(kfs[0], kfs[1]) : void 0;
4099
+ out.push(makeOutKf(
4100
+ getKfTime(kfs[0]),
4101
+ buildOutKfValue(getKfValueParts(kfs[0]), getKfValueParts(kfs[0]), 0, firstPos, firstRotate, autoOrient)
4102
+ ));
4103
+ for (let i = 0; i < kfs.length - 1; i++) {
4104
+ const prevKf = kfs[i];
4105
+ const nextKf = kfs[i + 1];
4106
+ const prevPos = getKfTranslate(prevKf);
4107
+ const nextPos = getKfTranslate(nextKf);
4108
+ if (!prevPos || !nextPos) {
4109
+ out.push(makeOutKf(
4110
+ getKfTime(nextKf),
4111
+ buildOutKfValue(getKfValueParts(nextKf), getKfValueParts(nextKf), 1, nextPos != null ? nextPos : [0, 0], void 0, autoOrient)
4112
+ ));
4113
+ continue;
2247
4114
  }
2248
- if (/^url\(#[^)]+\)$/.test(str)) {
2249
- return value;
4115
+ if (autoOrient && i > 0) {
4116
+ insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol);
2250
4117
  }
2251
- return void 0;
4118
+ materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);
2252
4119
  }
2253
- return value;
4120
+ const lastInE = getKfEasing(kfs[kfs.length - 1]);
4121
+ if (lastInE) out[out.length - 1].e = lastInE;
4122
+ if (autoOrient) unwrapAutoOrientRotations(out);
4123
+ const result = { kfs: out };
4124
+ if (anim.loop !== void 0) result.loop = anim.loop;
4125
+ return result;
2254
4126
  }
2255
- function createElement(tagName, normalisedProps, style, children, textContent) {
2256
- if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) return null;
2257
- const element = document.createElementNS(SVG_NS, tagName);
2258
- for (const propName in normalisedProps) {
2259
- element.setAttribute(
2260
- camelCaseToKebabWordIfNeeded(propName),
2261
- sanitiseAttributeValue(propName, normalisedProps[propName])
2262
- );
2263
- }
2264
- if (style) {
2265
- for (const styleProp in style) {
2266
- element.style[styleProp] = String(style[styleProp]);
4127
+ function unwrapAutoOrientRotations(kfs) {
4128
+ var _a;
4129
+ let prev;
4130
+ for (const kf of kfs) {
4131
+ const v = (_a = kf.v) != null ? _a : kf.value;
4132
+ if (!v || typeof v.rotate !== "number") continue;
4133
+ if (prev === void 0) {
4134
+ prev = v.rotate;
4135
+ continue;
2267
4136
  }
4137
+ let r = v.rotate;
4138
+ while (r - prev > 180) r -= 360;
4139
+ while (r - prev < -180) r += 360;
4140
+ v.rotate = r;
4141
+ prev = r;
2268
4142
  }
2269
- if (children) {
2270
- for (const child of children) {
2271
- element.appendChild(child);
4143
+ }
4144
+ function makeOutKf(time, value) {
4145
+ return { t: time, v: value };
4146
+ }
4147
+ function getKfValueParts(kf) {
4148
+ var _a;
4149
+ const v = (_a = kf.value) != null ? _a : kf.v;
4150
+ if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
4151
+ return v;
4152
+ }
4153
+ function interpolatePart(prev, next, p) {
4154
+ if (prev === void 0) return next;
4155
+ if (next === void 0) return prev;
4156
+ if (typeof prev === "number" && typeof next === "number") {
4157
+ return prev + (next - prev) * p;
4158
+ }
4159
+ if (Array.isArray(prev) && Array.isArray(next) && prev.length === next.length) {
4160
+ const out = new Array(prev.length);
4161
+ for (let i = 0; i < prev.length; i++) {
4162
+ const a = typeof prev[i] === "number" ? prev[i] : 0;
4163
+ const b = typeof next[i] === "number" ? next[i] : 0;
4164
+ out[i] = a + (b - a) * p;
2272
4165
  }
4166
+ return out;
2273
4167
  }
2274
- if (textContent) element.textContent = textContent;
2275
- return element;
4168
+ return p < 0.5 ? prev : next;
2276
4169
  }
2277
- function resolveStyle(style, defs) {
4170
+ function buildOutKfValue(prevV, nextV, p, translate, rotateDegFromAutoOrient, autoOrient) {
4171
+ const value = { translate };
4172
+ const keys = /* @__PURE__ */ new Set();
4173
+ if (prevV) for (const k of Object.keys(prevV)) keys.add(k);
4174
+ if (nextV) for (const k of Object.keys(nextV)) keys.add(k);
4175
+ for (const k of keys) {
4176
+ if (k === "translate") continue;
4177
+ if (k === "rotate" && autoOrient) continue;
4178
+ const pv = prevV == null ? void 0 : prevV[k];
4179
+ const nv = nextV == null ? void 0 : nextV[k];
4180
+ if (pv === void 0 && nv === void 0) continue;
4181
+ value[k] = interpolatePart(pv, nv, p);
4182
+ }
4183
+ if (rotateDegFromAutoOrient !== void 0) value.rotate = rotateDegFromAutoOrient;
4184
+ return value;
4185
+ }
4186
+ function derivAngleForFirstKf(kf0, kf1) {
4187
+ const p0 = getKfTranslate(kf0);
4188
+ const p1 = getKfTranslate(kf1);
4189
+ if (!p0 || !p1) return 0;
4190
+ const seg = getSegmentCache(kf0, kf1, p0, p1);
4191
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, 0);
4192
+ return Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
4193
+ }
4194
+ function wrappedAngleDelta(a, b) {
4195
+ let d = a - b;
4196
+ while (d > 180) d -= 360;
4197
+ while (d < -180) d += 360;
4198
+ return d;
4199
+ }
4200
+ function insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol) {
2278
4201
  var _a;
2279
- if (!style) return void 0;
2280
- if (typeof style === "string") {
2281
- return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
4202
+ const lastKf = out[out.length - 1];
4203
+ const lastV = (_a = lastKf.v) != null ? _a : lastKf.value;
4204
+ const prevExit = lastV == null ? void 0 : lastV.rotate;
4205
+ if (typeof prevExit !== "number") return;
4206
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
4207
+ const tanAtStart = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, 0);
4208
+ const nextEntry = Math.atan2(tanAtStart[1], tanAtStart[0]) * 180 / Math.PI;
4209
+ const delta = wrappedAngleDelta(nextEntry, prevExit);
4210
+ if (Math.abs(delta) <= rotationTol) return;
4211
+ const dupValue = buildOutKfValue(
4212
+ getKfValueParts(prevKf),
4213
+ getKfValueParts(prevKf),
4214
+ 0,
4215
+ prevPos,
4216
+ nextEntry,
4217
+ true
4218
+ );
4219
+ out.push(makeOutKf(getKfTime(prevKf), dupValue));
4220
+ }
4221
+ function materialiseSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples) {
4222
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
4223
+ const prevTime = getKfTime(prevKf);
4224
+ const nextTime = getKfTime(nextKf);
4225
+ const prevEasing = getKfEasing(prevKf);
4226
+ const invertFn = invertEasing(prevEasing);
4227
+ const prevV = getKfValueParts(prevKf);
4228
+ const nextV = getKfValueParts(nextKf);
4229
+ const interiorTs = computeSampleTs(seg, autoOrient, flatnessTol, rotationTol, maxSamples);
4230
+ const samples = [];
4231
+ for (const t of interiorTs) {
4232
+ const arc = bezier2D_arcAtT(seg.lut, t);
4233
+ const p = clamp(seg.totalArc > 0 ? arc / seg.totalArc : t, 0, 1);
4234
+ const u = clamp(invertFn(p), 0, 1);
4235
+ const pos = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
4236
+ const sample = { u, p, pos };
4237
+ if (autoOrient) {
4238
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
4239
+ sample.rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
4240
+ }
4241
+ samples.push(sample);
4242
+ }
4243
+ let remaining = prevEasing;
4244
+ let prevU = 0;
4245
+ const startIdx = out.length - 1;
4246
+ for (let i = 0; i < samples.length; i++) {
4247
+ const s = samples[i];
4248
+ const xFrac = prevU < 1 ? clamp((s.u - prevU) / (1 - prevU), 0, 1) : 1;
4249
+ const { left, right } = splitEasing(remaining, xFrac);
4250
+ const ownerIdx = i === 0 ? startIdx : out.length - 1;
4251
+ if (left) out[ownerIdx].e = left;
4252
+ else delete out[ownerIdx].e;
4253
+ const tGlobal = prevTime + s.u * (nextTime - prevTime);
4254
+ const value = buildOutKfValue(prevV, nextV, s.p, s.pos, s.rotateDeg, autoOrient);
4255
+ out.push(makeOutKf(tGlobal, value));
4256
+ remaining = right;
4257
+ prevU = s.u;
2282
4258
  }
2283
- return style;
2284
4259
  }
2285
- function getNormalizedProps(props) {
2286
- const propsCopy = {};
2287
- for (const key of Object.keys(props)) {
2288
- if (INTERNAL_ATTRS.has(key)) continue;
2289
- if (key === "style") continue;
2290
- let value = props[key];
2291
- if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
2292
- propsCopy[key] = toRGBA(value);
2293
- } else if (TRANSFORM_FN_NAMES.has(key)) {
2294
- if (Array.isArray(value)) {
2295
- if (key === "translate") value = value.map((v) => v + "px");
2296
- value = value.join(",");
2297
- }
2298
- if (key === "rotate") value = value + "deg";
2299
- propsCopy["transform"] = key + "(" + value + ")";
2300
- } else if (value !== void 0 && value !== null) {
2301
- propsCopy[key] = String(value);
4260
+ function computeSampleTs(seg, autoOrient, flatnessTol, rotationTol, maxSamples) {
4261
+ const extremes = [];
4262
+ addAxisExtremes(seg.P0[0], seg.P1[0], seg.P2[0], seg.P3[0], extremes);
4263
+ addAxisExtremes(seg.P0[1], seg.P1[1], seg.P2[1], seg.P3[1], extremes);
4264
+ extremes.sort((a, b) => a - b);
4265
+ const critical = [0];
4266
+ for (const t of extremes) {
4267
+ if (t > critical[critical.length - 1] + 1e-6 && t < 1 - 1e-6) {
4268
+ critical.push(t);
2302
4269
  }
2303
4270
  }
2304
- return propsCopy;
4271
+ critical.push(1);
4272
+ const out = [];
4273
+ const budget = { remaining: maxSamples - critical.length };
4274
+ for (let i = 0; i < critical.length - 1; i++) {
4275
+ bisect(critical[i], critical[i + 1], out, seg, autoOrient, flatnessTol, rotationTol, budget);
4276
+ }
4277
+ return out;
2305
4278
  }
2306
- function renderNode(node, defs) {
2307
- if (!node) return null;
2308
- const _a = node, { type, children, animate, style } = _a, props = __objRest(_a, ["type", "children", "animate", "style"]);
2309
- const nodeDefs = node.defs || defs;
2310
- const resolvedStyle = resolveStyle(style, nodeDefs);
2311
- let childElements;
2312
- if (children) {
2313
- for (const ch of children) {
2314
- const child = renderNode(ch, nodeDefs);
2315
- if (child) {
2316
- if (!childElements) childElements = [];
2317
- childElements.push(child);
2318
- }
4279
+ function addAxisExtremes(p0, p1, p2, p3, out) {
4280
+ const a = p1 - p0;
4281
+ const b = p2 - p1;
4282
+ const c = p3 - p2;
4283
+ const A = a - 2 * b + c;
4284
+ const B = 2 * (b - a);
4285
+ const C = a;
4286
+ if (Math.abs(A) < 1e-10) {
4287
+ if (Math.abs(B) > 1e-10) {
4288
+ const t = -C / B;
4289
+ if (t > 1e-6 && t < 1 - 1e-6) out.push(t);
2319
4290
  }
4291
+ return;
2320
4292
  }
2321
- return createElement(
2322
- type || "g",
2323
- getNormalizedProps(props),
2324
- resolvedStyle,
2325
- childElements,
2326
- props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
4293
+ const disc = B * B - 4 * A * C;
4294
+ if (disc < 0) return;
4295
+ const sq = Math.sqrt(disc);
4296
+ const t1 = (-B - sq) / (2 * A);
4297
+ const t2 = (-B + sq) / (2 * A);
4298
+ if (t1 > 1e-6 && t1 < 1 - 1e-6) out.push(t1);
4299
+ if (t2 > 1e-6 && t2 < 1 - 1e-6) out.push(t2);
4300
+ }
4301
+ function bisect(tA, tB, out, seg, autoOrient, flatnessTol, rotationTol, budget) {
4302
+ const tMid = (tA + tB) / 2;
4303
+ const pA = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA);
4304
+ const pB = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tB);
4305
+ const span = tB - tA;
4306
+ const p25 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA + span * 0.25);
4307
+ const p50 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tMid);
4308
+ const p75 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA + span * 0.75);
4309
+ const dev = Math.max(
4310
+ perpDist(p25, pA, pB),
4311
+ perpDist(p50, pA, pB),
4312
+ perpDist(p75, pA, pB)
2327
4313
  );
4314
+ let rotOk = true;
4315
+ if (autoOrient) {
4316
+ const tanA = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, tA);
4317
+ const tanB = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, tB);
4318
+ const angA = Math.atan2(tanA[1], tanA[0]) * 180 / Math.PI;
4319
+ const angB = Math.atan2(tanB[1], tanB[0]) * 180 / Math.PI;
4320
+ let delta = Math.abs(angA - angB);
4321
+ if (delta > 180) delta = 360 - delta;
4322
+ if (delta > rotationTol) rotOk = false;
4323
+ }
4324
+ if (dev <= flatnessTol && rotOk || budget.remaining <= 0 || span < 1e-6) {
4325
+ out.push(tB);
4326
+ return;
4327
+ }
4328
+ budget.remaining -= 1;
4329
+ bisect(tA, tMid, out, seg, autoOrient, flatnessTol, rotationTol, budget);
4330
+ bisect(tMid, tB, out, seg, autoOrient, flatnessTol, rotationTol, budget);
2328
4331
  }
2329
- function setupAnimationTriggers(api, config) {
2330
- const { startOn, outAction = "continue", scrollIntoViewThreshold = 0.5 } = config;
2331
- const root = api.getRootElement();
2332
- if (!root) {
2333
- console.warn("setupAnimationTriggers: No root element found for animation.");
2334
- return api;
4332
+ function perpDist(q, pA, pB) {
4333
+ const dx = pB[0] - pA[0];
4334
+ const dy = pB[1] - pA[1];
4335
+ const len2 = dx * dx + dy * dy;
4336
+ if (len2 < 1e-20) {
4337
+ const qdx = q[0] - pA[0];
4338
+ const qdy = q[1] - pA[1];
4339
+ return Math.sqrt(qdx * qdx + qdy * qdy);
2335
4340
  }
2336
- const start = () => {
2337
- api.play();
2338
- };
2339
- const handleEndAction = () => {
2340
- switch (outAction) {
2341
- case "pause":
2342
- api.pause();
2343
- break;
2344
- case "reset":
2345
- api.cancel();
2346
- break;
2347
- case "reverse":
2348
- api.play();
2349
- break;
2350
- case "continue":
2351
- default:
2352
- break;
2353
- }
2354
- };
2355
- switch (startOn) {
2356
- case "load": {
2357
- const startHandler = () => start();
2358
- if (document.readyState === "complete") {
2359
- startHandler();
2360
- } else {
2361
- window.addEventListener("load", startHandler, { once: true });
4341
+ const cross = (q[0] - pA[0]) * dy - (q[1] - pA[1]) * dx;
4342
+ return Math.abs(cross) / Math.sqrt(len2);
4343
+ }
4344
+ function materialiseMotionPathsInTree(root, opts) {
4345
+ const out = walkAndMaterialise(root, opts);
4346
+ return out != null ? out : root;
4347
+ }
4348
+ function walkAndMaterialise(node, opts) {
4349
+ let newChildren;
4350
+ if (node.children) {
4351
+ for (let i = 0; i < node.children.length; i++) {
4352
+ const ch = node.children[i];
4353
+ const ret = walkAndMaterialise(ch, opts);
4354
+ if (ret !== null) {
4355
+ if (!newChildren) newChildren = node.children.slice();
4356
+ newChildren[i] = ret;
2362
4357
  }
2363
- break;
2364
- }
2365
- case "mouseOver": {
2366
- const mouseOverHandler = () => start();
2367
- const mouseOutHandler = () => handleEndAction();
2368
- root.addEventListener("mouseenter", mouseOverHandler);
2369
- root.addEventListener("mouseleave", mouseOutHandler);
2370
- break;
2371
- }
2372
- case "click": {
2373
- const clickHandler = () => {
2374
- if (api.isPlaying()) {
2375
- handleEndAction();
2376
- } else {
2377
- start();
2378
- }
2379
- };
2380
- root.addEventListener("click", clickHandler);
2381
- break;
2382
4358
  }
2383
- case "scrollIntoView": {
2384
- const observer = new IntersectionObserver(
2385
- (entries) => {
2386
- entries.forEach((entry) => {
2387
- if (entry.isIntersecting && entry.intersectionRatio >= scrollIntoViewThreshold) {
2388
- start();
2389
- } else {
2390
- handleEndAction();
2391
- }
2392
- });
2393
- },
2394
- { threshold: scrollIntoViewThreshold }
2395
- );
2396
- observer.observe(root);
2397
- break;
4359
+ }
4360
+ let newAnimate;
4361
+ const animBucket = node.animate;
4362
+ if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
4363
+ const animDef = animBucket;
4364
+ const transformAnim = animDef.transform;
4365
+ if (transformAnim && typeof transformAnim === "object" && propAnimIsMotionPath(transformAnim)) {
4366
+ const materialised = materialiseMotionPathInPropAnim(transformAnim, opts);
4367
+ if (materialised !== transformAnim) {
4368
+ newAnimate = __spreadProps(__spreadValues({}, animDef), { transform: materialised });
4369
+ }
2398
4370
  }
2399
- case "programmatic":
2400
- break;
2401
4371
  }
2402
- return api;
4372
+ if (!newChildren && !newAnimate) return null;
4373
+ const cloned = __spreadValues({}, node);
4374
+ if (newChildren) cloned.children = newChildren;
4375
+ if (newAnimate) cloned.animate = newAnimate;
4376
+ return cloned;
2403
4377
  }
2404
4378
  function parsePathCommands(d) {
2405
4379
  const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
@@ -2463,6 +4437,8 @@ var PixodeskAnimatorReact = (() => {
2463
4437
  currentPath.o.push([x2, y2]);
2464
4438
  } else if (type === "Z" || type === "z") {
2465
4439
  currentPath.c = true;
4440
+ } else {
4441
+ console.warn('Unsupported path command "' + type + '"');
2466
4442
  }
2467
4443
  }
2468
4444
  return res;
@@ -2480,13 +4456,17 @@ var PixodeskAnimatorReact = (() => {
2480
4456
  return typeof value === "string" && extractPathData(value) !== void 0;
2481
4457
  }
2482
4458
  function normalizePathValue(value) {
4459
+ if (value && typeof value === "object" && typeof value.path === "string") {
4460
+ const d = extractPathData(value.path);
4461
+ return d ? { paths: parseSvgPathToBezier(d) } : value;
4462
+ }
2483
4463
  if (value && typeof value === "object" && "paths" in value) {
2484
4464
  const pathsArray = value.paths;
2485
4465
  if (Array.isArray(pathsArray) && pathsArray.length > 0) {
2486
4466
  if (isPathString(pathsArray[0])) {
2487
4467
  const paths = [];
2488
- for (const pathStr of pathsArray) {
2489
- const d = extractPathData(pathStr);
4468
+ for (const pathStr2 of pathsArray) {
4469
+ const d = extractPathData(pathStr2);
2490
4470
  if (d) {
2491
4471
  paths.push(...parseSvgPathToBezier(d));
2492
4472
  }
@@ -2499,8 +4479,8 @@ var PixodeskAnimatorReact = (() => {
2499
4479
  if (Array.isArray(value)) {
2500
4480
  if (value.length > 0 && isPathString(value[0])) {
2501
4481
  const paths = [];
2502
- for (const pathStr of value) {
2503
- const d = extractPathData(pathStr);
4482
+ for (const pathStr2 of value) {
4483
+ const d = extractPathData(pathStr2);
2504
4484
  if (d) {
2505
4485
  paths.push(...parseSvgPathToBezier(d));
2506
4486
  }
@@ -2564,11 +4544,34 @@ var PixodeskAnimatorReact = (() => {
2564
4544
  if (COLOUR_ATTR_NAMES.has(propName)) {
2565
4545
  return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
2566
4546
  }
4547
+ if (propName === "transform" && typeof a === "object" && a !== null && !Array.isArray(a) && typeof b === "object" && b !== null && !Array.isArray(b)) {
4548
+ return interpolateTransformParts(a, b, t);
4549
+ }
4550
+ if (propName === "rotate" && typeof a === "number" && typeof b === "number") {
4551
+ return interpolateNum(a, b, t);
4552
+ }
2567
4553
  if (TRANSFORM_FN_NAMES.has(propName) || propName === "stroke-dasharray" || propName === "strokeDasharray") {
2568
4554
  return interpolateVec(a || [], b || [], t);
2569
4555
  }
2570
4556
  return interpolateNum(+(a || 0), +(b || 0), t);
2571
4557
  }
4558
+ function interpolateTransformParts(a, b, t) {
4559
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a != null ? a : {}), ...Object.keys(b != null ? b : {})]);
4560
+ const out = {};
4561
+ for (const k of keys) {
4562
+ const av = a == null ? void 0 : a[k];
4563
+ const bv = b == null ? void 0 : b[k];
4564
+ if (k === "rotate") {
4565
+ out[k] = interpolateNum(+(av != null ? av : 0), +(bv != null ? bv : 0), t);
4566
+ } else if (k === "translate" || k === "scale" || k === "origin") {
4567
+ const fallback = k === "scale" ? [1, 1] : [0, 0];
4568
+ out[k] = interpolateVec(av || fallback, bv || fallback, t);
4569
+ } else {
4570
+ out[k] = bv != null ? bv : av;
4571
+ }
4572
+ }
4573
+ return out;
4574
+ }
2572
4575
  function expandLoopKeyframes(propName, keyframes, loop, duration) {
2573
4576
  var _a, _b, _c, _d, _e;
2574
4577
  const totalIntervals = keyframes.length - 1;
@@ -2595,11 +4598,16 @@ var PixodeskAnimatorReact = (() => {
2595
4598
  const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
2596
4599
  const segDuration = segEndT - segStartT;
2597
4600
  if (segDuration <= 0) return keyframes;
2598
- const template = segKfs.map((kf) => ({
2599
- relT: (kf.t - segStartT) / segDuration,
2600
- v: kf.v,
2601
- e: kf.e
2602
- }));
4601
+ const template = segKfs.map((kf) => {
4602
+ var _a2, _b2;
4603
+ return {
4604
+ relT: (kf.t - segStartT) / segDuration,
4605
+ v: kf.v,
4606
+ e: kf.e,
4607
+ tangentIn: (_a2 = kf.tangentIn) != null ? _a2 : kf.ti,
4608
+ tangentOut: (_b2 = kf.tangentOut) != null ? _b2 : kf.to
4609
+ };
4610
+ });
2603
4611
  const fullReps = Math.floor(fillDuration / segDuration);
2604
4612
  const remainder = fillDuration - fullReps * segDuration;
2605
4613
  const partialFraction = remainder / segDuration;
@@ -2613,7 +4621,12 @@ var PixodeskAnimatorReact = (() => {
2613
4621
  relT: 1 - template[i].relT,
2614
4622
  v: template[i].v,
2615
4623
  // Easing for reversed transition: use reversed easing from the forward "from" keyframe
2616
- e: i > 0 ? reverseEasing(template[i - 1].e) : void 0
4624
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
4625
+ // Reversed traversal swaps each vertex's in/out spatial tangents
4626
+ // (geometry is identical, walked backwards), so curvature and
4627
+ // auto-orientation survive the reversed rep.
4628
+ tangentIn: template[i].tangentOut,
4629
+ tangentOut: template[i].tangentIn
2617
4630
  });
2618
4631
  }
2619
4632
  } else {
@@ -2635,11 +4648,14 @@ var PixodeskAnimatorReact = (() => {
2635
4648
  looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2636
4649
  return;
2637
4650
  }
2638
- looped.push({
4651
+ const pushed = {
2639
4652
  t: repStart + entry.relT * segDuration,
2640
4653
  v: entry.v,
2641
4654
  e: i < entries.length - 1 ? entry.e : void 0
2642
- });
4655
+ };
4656
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
4657
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
4658
+ looped.push(pushed);
2643
4659
  }
2644
4660
  }
2645
4661
  for (let rep = 0; rep < fullReps; rep++) {
@@ -2660,7 +4676,7 @@ var PixodeskAnimatorReact = (() => {
2660
4676
  }
2661
4677
  }
2662
4678
  function normalizeKeyframes(propName, propAnim, duration, defs) {
2663
- var _a, _b, _c, _d, _e;
4679
+ var _a, _b, _c, _d, _e, _f, _g;
2664
4680
  const keyframes = propAnim.keyframes || propAnim.kfs || [];
2665
4681
  const normalized = [];
2666
4682
  for (const kf of keyframes) {
@@ -2670,14 +4686,20 @@ var PixodeskAnimatorReact = (() => {
2670
4686
  if (propName === "d") {
2671
4687
  value = normalizePathValue(value);
2672
4688
  }
2673
- if (COLOUR_ATTR_NAMES.has(propName)) {
4689
+ const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
4690
+ if (COLOUR_ATTR_NAMES.has(propNameKebab)) {
2674
4691
  value = (_e = parseColor(value)) != null ? _e : value;
2675
4692
  }
2676
- normalized.push({
4693
+ const normKf = {
2677
4694
  t: timePct,
2678
4695
  v: value,
2679
4696
  e: resolveEasing(easing, defs)
2680
- });
4697
+ };
4698
+ const tIn = (_f = kf.tangentIn) != null ? _f : kf.ti;
4699
+ const tOut = (_g = kf.tangentOut) != null ? _g : kf.to;
4700
+ if (tIn) normKf.tangentIn = tIn;
4701
+ if (tOut) normKf.tangentOut = tOut;
4702
+ normalized.push(normKf);
2681
4703
  }
2682
4704
  normalized.sort((a, b) => {
2683
4705
  var _a2, _b2;
@@ -2697,174 +4719,981 @@ var PixodeskAnimatorReact = (() => {
2697
4719
  merged[prop] = propAnim;
2698
4720
  }
2699
4721
  }
2700
- return merged;
2701
- }
2702
- var _elementIdCounter = 0;
2703
- function generateElementId() {
2704
- return "_px_el_" + ++_elementIdCounter;
4722
+ return merged;
4723
+ }
4724
+ function materialiseInternalLoopsInPropAnim(propName, propAnim, duration) {
4725
+ var _a;
4726
+ const loopRaw = propAnim.loop;
4727
+ if (loopRaw === void 0 || loopRaw === null || loopRaw === false) return propAnim;
4728
+ const loop = loopRaw === true ? {} : loopRaw;
4729
+ const rawKfs = (_a = propAnim.keyframes) != null ? _a : propAnim.kfs;
4730
+ if (!Array.isArray(rawKfs) || rawKfs.length < 2) return propAnim;
4731
+ const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
4732
+ const isColour = COLOUR_ATTR_NAMES.has(propNameKebab);
4733
+ const kfs = rawKfs.map((kf) => {
4734
+ var _a2, _b, _c, _d, _e, _f, _g, _h;
4735
+ const t = (_a2 = kf.t) != null ? _a2 : kf.time;
4736
+ let v = (_b = kf.v) != null ? _b : kf.value;
4737
+ if (propName === "d") v = normalizePathValue(v);
4738
+ if (isColour) v = (_c = parseColor(v)) != null ? _c : v;
4739
+ const e = (_d = kf.e) != null ? _d : kf.easing;
4740
+ const out2 = { t, v, e };
4741
+ if ((_e = kf.tangentIn) != null ? _e : kf.ti) out2.tangentIn = (_f = kf.tangentIn) != null ? _f : kf.ti;
4742
+ if ((_g = kf.tangentOut) != null ? _g : kf.to) out2.tangentOut = (_h = kf.tangentOut) != null ? _h : kf.to;
4743
+ return out2;
4744
+ });
4745
+ const expanded = expandLoopKeyframes(propName, kfs, loop, duration);
4746
+ const out = { kfs: expanded };
4747
+ if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
4748
+ return out;
4749
+ }
4750
+ function materialiseInternalLoopsInTree(root, duration) {
4751
+ const ret = walkAndMaterialiseLoops(root, duration);
4752
+ return ret != null ? ret : root;
4753
+ }
4754
+ function walkAndMaterialiseLoops(node, duration) {
4755
+ let newChildren;
4756
+ if (node.children) {
4757
+ for (let i = 0; i < node.children.length; i++) {
4758
+ const ret = walkAndMaterialiseLoops(node.children[i], duration);
4759
+ if (ret !== null) {
4760
+ if (!newChildren) newChildren = node.children.slice();
4761
+ newChildren[i] = ret;
4762
+ }
4763
+ }
4764
+ }
4765
+ let newAnimate;
4766
+ const animBucket = node.animate;
4767
+ if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
4768
+ const animDef = animBucket;
4769
+ for (const propName of Object.keys(animDef)) {
4770
+ const propAnim = animDef[propName];
4771
+ const materialised = materialiseInternalLoopsInPropAnim(propName, propAnim, duration);
4772
+ if (materialised !== propAnim) {
4773
+ if (!newAnimate) newAnimate = __spreadValues({}, animDef);
4774
+ newAnimate[propName] = materialised;
4775
+ }
4776
+ }
4777
+ }
4778
+ if (!newChildren && !newAnimate) return null;
4779
+ const cloned = __spreadValues({}, node);
4780
+ if (newChildren) cloned.children = newChildren;
4781
+ if (newAnimate) cloned.animate = newAnimate;
4782
+ return cloned;
4783
+ }
4784
+ var _elementIdCounter = 0;
4785
+ function generateElementId() {
4786
+ return "_px_el_" + ++_elementIdCounter;
4787
+ }
4788
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxAnimatorEngine.webapi) {
4789
+ const normalized = {};
4790
+ for (const [propName, propAnim] of Object.entries(animDef)) {
4791
+ const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
4792
+ if (normalizedKfs.length > 0) {
4793
+ const out = { kfs: normalizedKfs };
4794
+ if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
4795
+ if (propAnim.loop !== void 0) out.loop = propAnim.loop;
4796
+ normalized[propName] = engine === PxAnimatorEngine.webapi && propName === "transform" ? materialiseMotionPathInPropAnim(out) : out;
4797
+ }
4798
+ }
4799
+ return normalized;
4800
+ }
4801
+ function getNormalisedBindings(doc, engine = PxAnimatorEngine.webapi) {
4802
+ const animatorConfig = getAnimatorConfig(doc) || {};
4803
+ const defs = getDefs(doc);
4804
+ const duration = animatorConfig.duration || 1e3;
4805
+ const bindings = [];
4806
+ const processAnimation = (id, animate) => {
4807
+ if (!animate) return null;
4808
+ const animDefs = resolveElementAnimation(animate, defs);
4809
+ if (animDefs.length === 0) return null;
4810
+ const merged = mergeAnimationDefinitions(animDefs);
4811
+ const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);
4812
+ if (Object.keys(normalizedAnim).length === 0) return null;
4813
+ return {
4814
+ id,
4815
+ animate: normalizedAnim
4816
+ };
4817
+ };
4818
+ const docBindings = getBindings(doc);
4819
+ if (docBindings) {
4820
+ for (const binding of docBindings) {
4821
+ const normalized = processAnimation(binding.id, binding.animate);
4822
+ if (normalized) bindings.push(normalized);
4823
+ }
4824
+ }
4825
+ const processNode = (node) => {
4826
+ const inlineAnim = node.animate;
4827
+ if (inlineAnim && Object.keys(inlineAnim).length > 0) {
4828
+ const nodeId = node.id || generateElementId();
4829
+ node.id = nodeId;
4830
+ const normalized = processAnimation(nodeId, inlineAnim);
4831
+ if (normalized) bindings.push(normalized);
4832
+ }
4833
+ if (node.children) {
4834
+ for (let i = 0; i < node.children.length; i++) {
4835
+ processNode(node.children[i]);
4836
+ }
4837
+ }
4838
+ };
4839
+ if (doc.children) {
4840
+ for (let i = 0; i < doc.children.length; i++) {
4841
+ processNode(doc.children[i]);
4842
+ }
4843
+ }
4844
+ return bindings;
4845
+ }
4846
+ function getKeyframesPair(keyframes, progress) {
4847
+ var _a, _b;
4848
+ let prevKf = keyframes[0];
4849
+ let nextKf = keyframes[keyframes.length - 1];
4850
+ for (let j = 0; j < keyframes.length - 1; j++) {
4851
+ const aOff = (_a = keyframes[j].t) != null ? _a : 0;
4852
+ const bOff = (_b = keyframes[j + 1].t) != null ? _b : 0;
4853
+ if (aOff <= progress && progress <= bOff) {
4854
+ prevKf = keyframes[j];
4855
+ nextKf = keyframes[j + 1];
4856
+ break;
4857
+ }
4858
+ }
4859
+ return { prevKf, nextKf };
4860
+ }
4861
+ function calcPropertyValue(propName, propAnim, progress) {
4862
+ var _a, _b, _c, _d, _e, _f, _g;
4863
+ const keyframes = propAnim.kfs || propAnim.keyframes || [];
4864
+ if (keyframes.length === 0) return null;
4865
+ const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
4866
+ let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a = prevKf.t) != null ? _a : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
4867
+ localProgress = clamp(localProgress, 0, 1);
4868
+ const easing = (_c = prevKf.e) != null ? _c : prevKf.easing;
4869
+ if (easing && Array.isArray(easing)) {
4870
+ try {
4871
+ localProgress = cubicBezier(easing)(localProgress);
4872
+ } catch (e) {
4873
+ }
4874
+ }
4875
+ let cssAttrName = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
4876
+ let cssValue = null;
4877
+ const prevV = (_d = prevKf == null ? void 0 : prevKf.v) != null ? _d : prevKf == null ? void 0 : prevKf.value;
4878
+ const nextV = (_e = nextKf == null ? void 0 : nextKf.v) != null ? _e : nextKf == null ? void 0 : nextKf.value;
4879
+ if (cssAttrName === "d") {
4880
+ const prevPaths = (_f = prevV == null ? void 0 : prevV.paths) != null ? _f : Array.isArray(prevV) ? prevV : [];
4881
+ const nextPaths = (_g = nextV == null ? void 0 : nextV.paths) != null ? _g : Array.isArray(nextV) ? nextV : [];
4882
+ cssValue = interpolateBeziers(
4883
+ prevPaths,
4884
+ nextPaths,
4885
+ localProgress
4886
+ ).map((bz) => bezierToSvgPath(bz)).join("");
4887
+ } else if (COLOUR_ATTR_NAMES.has(cssAttrName)) {
4888
+ cssValue = toRGBA(interpolateColor(
4889
+ prevV || [0, 0, 0, 1],
4890
+ nextV || [0, 0, 0, 1],
4891
+ localProgress
4892
+ ));
4893
+ cssAttrName = propName;
4894
+ } else if (cssAttrName === "stroke-dasharray") {
4895
+ cssValue = interpolateVec(
4896
+ prevV || [],
4897
+ nextV || [],
4898
+ localProgress
4899
+ ).join(" ");
4900
+ cssAttrName = propName;
4901
+ } else if (cssAttrName === "transform" && prevV !== null && typeof prevV === "object" && !Array.isArray(prevV)) {
4902
+ const partKeys = /* @__PURE__ */ new Set([
4903
+ ...prevV ? Object.keys(prevV) : [],
4904
+ ...nextV ? Object.keys(nextV) : []
4905
+ ]);
4906
+ const partsResult = {};
4907
+ for (const partKey of partKeys) {
4908
+ const prevPart = prevV == null ? void 0 : prevV[partKey];
4909
+ const nextPart = nextV == null ? void 0 : nextV[partKey];
4910
+ if (partKey === "rotate") {
4911
+ partsResult.rotate = interpolateNum(+(prevPart != null ? prevPart : 0), +(nextPart != null ? nextPart : 0), localProgress);
4912
+ } else if (partKey === "translate" || partKey === "scale" || partKey === "origin") {
4913
+ const fallback = partKey === "scale" ? [1, 1] : [0, 0];
4914
+ const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);
4915
+ partsResult[partKey] = interp;
4916
+ }
4917
+ }
4918
+ if (propAnimIsMotionPath(propAnim)) {
4919
+ const prevTr = prevV.translate;
4920
+ const nextTr = nextV.translate;
4921
+ if (Array.isArray(prevTr) && Array.isArray(nextTr)) {
4922
+ const sample = evaluateMotionPathSegment(
4923
+ prevKf,
4924
+ nextKf,
4925
+ [+prevTr[0], +prevTr[1]],
4926
+ [+nextTr[0], +nextTr[1]],
4927
+ localProgress,
4928
+ !!propAnim.autoOrient
4929
+ );
4930
+ partsResult.translate = [sample.translate[0], sample.translate[1]];
4931
+ if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg;
4932
+ }
4933
+ }
4934
+ cssValue = composeTransformParts(partsResult, { withUnits: false });
4935
+ cssAttrName = "transform";
4936
+ } else if (cssAttrName === "translate") {
4937
+ const v = interpolateVec(
4938
+ prevV || [0, 0],
4939
+ nextV || [0, 0],
4940
+ localProgress
4941
+ );
4942
+ cssValue = "translate(" + v.join(",") + ")";
4943
+ cssAttrName = "transform";
4944
+ } else if (cssAttrName === "rotate") {
4945
+ const v = interpolateNum(
4946
+ +(prevV || 0),
4947
+ +(nextV || 0),
4948
+ localProgress
4949
+ );
4950
+ cssValue = "rotate(" + v + ")";
4951
+ cssAttrName = "transform";
4952
+ } else if (cssAttrName === "scale") {
4953
+ const v = interpolateVec(
4954
+ prevV || [1, 1],
4955
+ nextV || [1, 1],
4956
+ localProgress
4957
+ );
4958
+ cssValue = "scale(" + v.join(",") + ")";
4959
+ cssAttrName = "transform";
4960
+ } else {
4961
+ const num2 = interpolateNum(
4962
+ +(prevV || 0),
4963
+ +(nextV || 0),
4964
+ localProgress
4965
+ );
4966
+ cssValue = num2;
4967
+ }
4968
+ if (PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
4969
+ cssValue = cssValue * 100 + "%";
4970
+ }
4971
+ return { k: cssAttrName, v: cssValue === null ? "" : "" + cssValue };
4972
+ }
4973
+ function calcAnimationValues(animDef, progress) {
4974
+ const result = {};
4975
+ for (const [propName, propAnim] of Object.entries(animDef)) {
4976
+ const computed = calcPropertyValue(propName, propAnim, progress);
4977
+ if (computed) {
4978
+ result[computed.k] = computed.v;
4979
+ }
4980
+ }
4981
+ return result;
4982
+ }
4983
+ function applyTrimPathEffect(node, trimPath, isCombinedShape, ctx) {
4984
+ if (!trimPath) return node;
4985
+ const trimAllAsOne = !!trimPath.trimAllAsOne;
4986
+ const leafEntries = [];
4987
+ let acc = 0;
4988
+ const measure = (n) => {
4989
+ if (Array.isArray(n.children) && n.children.length > 0) {
4990
+ for (const ch of n.children) measure(ch);
4991
+ return;
4992
+ }
4993
+ const d = typeof n.d === "string" ? n.d : rectToPathD(n);
4994
+ if (d === void 0) return;
4995
+ const subpaths = parseSvgPathToBezier(d);
4996
+ if (!subpaths.length) return;
4997
+ const entry = { leaf: n, subpaths: [] };
4998
+ for (const sp of subpaths) {
4999
+ if (!trimAllAsOne) acc = 0;
5000
+ const lengthPx = pxBezierPathLength(sp);
5001
+ entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: acc });
5002
+ acc += lengthPx;
5003
+ }
5004
+ leafEntries.push(entry);
5005
+ };
5006
+ measure(node);
5007
+ if (!leafEntries.length) return node;
5008
+ const chainLengthPx = acc;
5009
+ if (trimAllAsOne && chainLengthPx < 1e-3) return node;
5010
+ const offsetReadRaw = readAnimatable(trimPath.offset);
5011
+ const offsetRead = offsetReadRaw.kind === "absent" ? { kind: "static", value: 0 } : offsetReadRaw;
5012
+ const rangeReadRaw = readRangeWithCrossings(trimPath.range);
5013
+ const rangeRead = rangeReadRaw.kind === "absent" ? { kind: "static", value: [0, 1] } : rangeReadRaw;
5014
+ const offsetValues = readScalarValues(offsetRead);
5015
+ const minOffset = offsetValues.length ? Math.min(...offsetValues) : 0;
5016
+ const maxOffset = offsetValues.length ? Math.max(...offsetValues) : 0;
5017
+ const minMaxOffset = [minOffset, maxOffset];
5018
+ if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {
5019
+ const entry = leafEntries[0];
5020
+ const sp = entry.subpaths[0];
5021
+ const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
5022
+ if (pathLengthPx < 1e-3) return node;
5023
+ const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
5024
+ return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);
5025
+ }
5026
+ const replacements = /* @__PURE__ */ new Map();
5027
+ for (const entry of leafEntries) {
5028
+ const newChildren = [];
5029
+ for (const sp of entry.subpaths) {
5030
+ const pathLengthPx = trimAllAsOne ? chainLengthPx : sp.lengthPx;
5031
+ if (pathLengthPx < 1e-3) continue;
5032
+ const startOffsetPct = trimAllAsOne ? sp.startOffsetPx / pathLengthPx : 0;
5033
+ newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));
5034
+ }
5035
+ replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));
5036
+ }
5037
+ const swap = (n) => {
5038
+ const r = replacements.get(n);
5039
+ if (r) return r;
5040
+ if (Array.isArray(n.children) && n.children.length > 0) {
5041
+ return __spreadProps(__spreadValues({}, n), { children: n.children.map(swap) });
5042
+ }
5043
+ return n;
5044
+ };
5045
+ return swap(node);
5046
+ }
5047
+ function wrapLeafAsGroup(leaf, children) {
5048
+ const wrapper = __spreadProps(__spreadValues({}, leaf), { type: "g", children });
5049
+ delete wrapper.d;
5050
+ delete wrapper.strokeDasharray;
5051
+ delete wrapper.strokeDashoffset;
5052
+ delete wrapper.effects;
5053
+ return wrapper;
5054
+ }
5055
+ function buildSubpathNodes(leaf, subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx) {
5056
+ const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);
5057
+ const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);
5058
+ const dashOffsetAttr = computeAnimAttr(offsetRead, offsetToDashOffset);
5059
+ const dashArrayAttr = computeAnimAttr(rangeRead, rangeToDasharray);
5060
+ const strokeOpacityAttr = computeOpacityFromRange(rangeRead);
5061
+ const dStr = bezierToSvgPath(subpath);
5062
+ const base = makeBareSubpath(dStr);
5063
+ applyAttr(base, "strokeDasharray", dashArrayAttr);
5064
+ applyAttr(base, "strokeDashoffset", dashOffsetAttr);
5065
+ applyAttr(base, "strokeOpacity", strokeOpacityAttr);
5066
+ return [base];
5067
+ }
5068
+ function collapseLeafWithTrim(leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead) {
5069
+ const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);
5070
+ const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);
5071
+ const node = __spreadValues({}, leaf);
5072
+ delete node.effects;
5073
+ applyAttr(node, "strokeDasharray", computeAnimAttr(rangeRead, rangeToDasharray));
5074
+ applyAttr(node, "strokeDashoffset", computeAnimAttr(offsetRead, offsetToDashOffset));
5075
+ applyAttr(node, "strokeOpacity", computeOpacityFromRange(rangeRead));
5076
+ return node;
5077
+ }
5078
+ function makeBareSubpath(dStr) {
5079
+ return { type: "path", d: dStr };
5080
+ }
5081
+ var SMALL_PADDING_PX = 1;
5082
+ function makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset) {
5083
+ const [minIdx] = getOffsetIndexRange(minMaxOffset);
5084
+ return (offsetVal) => pathLengthPx * (-offsetVal - minIdx + startOffsetPct) + SMALL_PADDING_PX;
5085
+ }
5086
+ function makeRangeToDasharray(pathLengthPx, minMaxOffset) {
5087
+ const [minIdx, maxIdx] = getOffsetIndexRange(minMaxOffset);
5088
+ const repeats = maxIdx - minIdx + 1;
5089
+ return (rangeVal) => {
5090
+ const a = clamp(rangeVal[0], 0, 1);
5091
+ const b = clamp(rangeVal[1], 0, 1);
5092
+ const minR = Math.min(a, b);
5093
+ const maxR = Math.max(a, b);
5094
+ const out = [0];
5095
+ let gap = SMALL_PADDING_PX;
5096
+ for (let i = 0; i < repeats; i++) {
5097
+ out.push(gap + minR * pathLengthPx);
5098
+ out.push((maxR - minR) * pathLengthPx);
5099
+ gap = (1 - maxR) * pathLengthPx;
5100
+ }
5101
+ out.push(gap + SMALL_PADDING_PX);
5102
+ return out;
5103
+ };
5104
+ }
5105
+ function getOffsetIndexRange(minMaxOffset) {
5106
+ return [
5107
+ Math.floor(Math.min(-minMaxOffset[0], -minMaxOffset[1])),
5108
+ Math.ceil(Math.max(-minMaxOffset[0], -minMaxOffset[1]))
5109
+ ];
5110
+ }
5111
+ function readScalarValues(r) {
5112
+ var _a;
5113
+ if (r.kind === "absent") return [];
5114
+ if (r.kind === "static") return [r.value];
5115
+ const out = [];
5116
+ for (const kf of r.keyframes) {
5117
+ const v = (_a = kf.value) != null ? _a : kf.v;
5118
+ if (typeof v === "number") out.push(v);
5119
+ }
5120
+ return out;
5121
+ }
5122
+ function computeAnimAttr(read, map) {
5123
+ if (read.kind === "absent") return void 0;
5124
+ if (read.kind === "static") return { kind: "static", value: map(read.value) };
5125
+ return {
5126
+ kind: "animated",
5127
+ keyframes: read.keyframes.map((kf) => {
5128
+ var _a, _b, _c, _d;
5129
+ return {
5130
+ time: (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0,
5131
+ value: map((_c = kf.value) != null ? _c : kf.v),
5132
+ easing: (_d = kf.easing) != null ? _d : kf.e
5133
+ };
5134
+ })
5135
+ };
5136
+ }
5137
+ function applyAttr(node, attrName, attr) {
5138
+ if (!attr) return;
5139
+ if (attr.kind === "static") {
5140
+ node[attrName] = attr.value;
5141
+ return;
5142
+ }
5143
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
5144
+ const animate = __spreadValues({}, prevAnimate || {});
5145
+ animate[attrName] = { keyframes: attr.keyframes };
5146
+ node.animate = animate;
5147
+ }
5148
+ function computeOpacityFromRange(rangeRead) {
5149
+ var _a, _b, _c, _d, _e, _f;
5150
+ const hide = (v) => v[0] === v[1];
5151
+ if (rangeRead.kind === "absent") return void 0;
5152
+ if (rangeRead.kind === "static") return hide(rangeRead.value) ? { kind: "static", value: 0 } : void 0;
5153
+ const kfs = rangeRead.keyframes;
5154
+ let anyHide = false;
5155
+ let allHide = true;
5156
+ for (const kf of kfs) {
5157
+ if (hide((_a = kf.value) != null ? _a : kf.v)) anyHide = true;
5158
+ else allHide = false;
5159
+ }
5160
+ if (!anyHide) return void 0;
5161
+ if (allHide) return { kind: "static", value: 0 };
5162
+ const out = [];
5163
+ for (let i = 0; i < kfs.length; i++) {
5164
+ const kf = kfs[i];
5165
+ const prevKf = i > 0 ? kfs[i - 1] : void 0;
5166
+ const nextKf = i < kfs.length - 1 ? kfs[i + 1] : void 0;
5167
+ const t = (_c = (_b = kf.time) != null ? _b : kf.t) != null ? _c : 0;
5168
+ const thisHide = hide((_d = kf.value) != null ? _d : kf.v);
5169
+ const prevHide = prevKf ? thisHide && hide((_e = prevKf.value) != null ? _e : prevKf.v) : thisHide;
5170
+ const nextHide = nextKf ? thisHide && hide((_f = nextKf.value) != null ? _f : nextKf.v) : thisHide;
5171
+ if (prevHide && !nextHide) {
5172
+ out.push({ time: t, value: 0 });
5173
+ out.push({ time: t + 1, value: 1 });
5174
+ } else if (!prevHide && nextHide) {
5175
+ out.push({ time: t - 1, value: 1 });
5176
+ out.push({ time: t, value: 0 });
5177
+ }
5178
+ }
5179
+ if (out.length <= 1) return void 0;
5180
+ return { kind: "animated", keyframes: out };
5181
+ }
5182
+ function readRangeWithCrossings(raw) {
5183
+ const r = readAnimatable(raw);
5184
+ if (r.kind !== "animated") return r;
5185
+ const kfs = r.keyframes.map((kf) => {
5186
+ var _a, _b, _c, _d;
5187
+ return {
5188
+ time: (_b = (_a = kf.time) != null ? _a : kf.t) != null ? _b : 0,
5189
+ value: (_c = kf.value) != null ? _c : kf.v,
5190
+ easing: (_d = kf.easing) != null ? _d : kf.e
5191
+ };
5192
+ });
5193
+ const hasReverse = kfs.some((kf) => kf.value[0] > kf.value[1]);
5194
+ if (!hasReverse) {
5195
+ return {
5196
+ kind: "animated",
5197
+ keyframes: kfs.map((kf) => ({ time: kf.time, value: kf.value, easing: kf.easing }))
5198
+ };
5199
+ }
5200
+ const crossingTimes = [];
5201
+ for (let i = 1; i < kfs.length; i++) {
5202
+ const prev = kfs[i - 1];
5203
+ const cur = kfs[i];
5204
+ const dPrev = prev.value[1] - prev.value[0];
5205
+ const dCur = cur.value[1] - cur.value[0];
5206
+ if (dPrev * dCur < 0) {
5207
+ const t = bisectionForRangeCrossing(prev, cur);
5208
+ if (t !== null && t > prev.time && t < cur.time) {
5209
+ crossingTimes.push(Math.round(t));
5210
+ }
5211
+ }
5212
+ }
5213
+ const uniqueTs = Array.from(new Set(crossingTimes)).sort((a, b) => a - b);
5214
+ const out = [];
5215
+ let j = 0;
5216
+ for (const kf of kfs) {
5217
+ while (j < uniqueTs.length && uniqueTs[j] < kf.time) {
5218
+ const t = uniqueTs[j++];
5219
+ const v2 = interpolateRangeAt(kfs, t);
5220
+ const m = (v2[0] + v2[1]) / 2;
5221
+ out.push({ time: t, value: [m, m] });
5222
+ }
5223
+ const v = kf.value[0] > kf.value[1] ? [kf.value[1], kf.value[0]] : kf.value;
5224
+ out.push({ time: kf.time, value: v, easing: kf.easing });
5225
+ }
5226
+ while (j < uniqueTs.length) {
5227
+ const t = uniqueTs[j++];
5228
+ const v = interpolateRangeAt(kfs, t);
5229
+ const m = (v[0] + v[1]) / 2;
5230
+ out.push({ time: t, value: [m, m] });
5231
+ }
5232
+ return { kind: "animated", keyframes: out };
5233
+ }
5234
+ function bisectionForRangeCrossing(prev, cur) {
5235
+ const f = (t) => {
5236
+ const a = (t - prev.time) / (cur.time - prev.time);
5237
+ const v0 = prev.value[0] + (cur.value[0] - prev.value[0]) * a;
5238
+ const v1 = prev.value[1] + (cur.value[1] - prev.value[1]) * a;
5239
+ return v1 - v0;
5240
+ };
5241
+ let lo = prev.time, hi = cur.time;
5242
+ let fLo = f(lo);
5243
+ if (fLo === 0) return lo;
5244
+ const fHi = f(hi);
5245
+ if (fHi === 0) return hi;
5246
+ if (fLo * fHi > 0) return null;
5247
+ for (let i = 0; i < 100; i++) {
5248
+ const mid = (lo + hi) / 2;
5249
+ const fMid = f(mid);
5250
+ if (fMid === 0 || Math.abs(hi - lo) < 1e-4) return mid;
5251
+ if (fLo * fMid < 0) {
5252
+ hi = mid;
5253
+ } else {
5254
+ lo = mid;
5255
+ fLo = fMid;
5256
+ }
5257
+ }
5258
+ return (lo + hi) / 2;
5259
+ }
5260
+ function interpolateRangeAt(kfs, t) {
5261
+ if (t <= kfs[0].time) return kfs[0].value;
5262
+ if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;
5263
+ for (let i = 1; i < kfs.length; i++) {
5264
+ if (t <= kfs[i].time) {
5265
+ const prev = kfs[i - 1];
5266
+ const cur = kfs[i];
5267
+ const a = (t - prev.time) / (cur.time - prev.time);
5268
+ return [
5269
+ prev.value[0] + (cur.value[0] - prev.value[0]) * a,
5270
+ prev.value[1] + (cur.value[1] - prev.value[1]) * a
5271
+ ];
5272
+ }
5273
+ }
5274
+ return kfs[kfs.length - 1].value;
5275
+ }
5276
+ function pxBezierPathLength(path) {
5277
+ const v = path.v;
5278
+ if (!v || v.length < 2) return 0;
5279
+ let total = 0;
5280
+ for (let i = 0; i < v.length - 1; i++) {
5281
+ total += segmentLength(path, i, i + 1);
5282
+ }
5283
+ if (path.c && v.length > 1) {
5284
+ total += segmentLength(path, v.length - 1, 0);
5285
+ }
5286
+ return total;
5287
+ }
5288
+ function segmentLength(path, from, to) {
5289
+ var _a, _b, _c, _d;
5290
+ const v = path.v;
5291
+ const p0 = v[from];
5292
+ const p3 = v[to];
5293
+ const p1 = (_b = (_a = path.o) == null ? void 0 : _a[from]) != null ? _b : p0;
5294
+ const p2 = (_d = (_c = path.i) == null ? void 0 : _c[to]) != null ? _d : p3;
5295
+ const lut = bezier2D_arcLengthLUT(p0, p1, p2, p3);
5296
+ return lut.ds[lut.ds.length - 1];
5297
+ }
5298
+ function rectToPathD(node) {
5299
+ var _a, _b, _c, _d;
5300
+ if (node.type !== "rect") return void 0;
5301
+ const x = Number((_a = node.x) != null ? _a : 0), y = Number((_b = node.y) != null ? _b : 0);
5302
+ const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
5303
+ return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
5304
+ }
5305
+ function applyPlayerEffects(root) {
5306
+ const ctx = {
5307
+ defs: [],
5308
+ warnings: [],
5309
+ errors: [],
5310
+ idMap: /* @__PURE__ */ new Map(),
5311
+ nextId: 0,
5312
+ contentRefInnerIds: /* @__PURE__ */ new Map(),
5313
+ maskAncestorChains: /* @__PURE__ */ new Map()
5314
+ };
5315
+ const working = clone(root);
5316
+ indexById(working, ctx.idMap);
5317
+ identifyContentRefTargets(working, ctx, () => genId(ctx, "inner"));
5318
+ collectMaskAncestorChains(working, ctx);
5319
+ const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);
5320
+ const out = applyPlayerEffects_retime(afterPass1, ctx);
5321
+ spliceDefs(out, ctx.defs);
5322
+ return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
5323
+ }
5324
+ function applyPlayerEffects_exceptRetime(node, ctx) {
5325
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
5326
+ const fx = node.effects;
5327
+ const originalId = typeof node.id === "string" ? node.id : void 0;
5328
+ const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
5329
+ if (!fx && !innerIdForContentRef) return node;
5330
+ const { transformation, repeater, maskedBy, trimPath, retime, ref, fillGradient, strokeGradient, textAlongPath } = fx != null ? fx : {};
5331
+ const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
5332
+ if (fx) delete node.effects;
5333
+ let n = node;
5334
+ n = applyTextAlongPathEffect(n, textAlongPath, ctx);
5335
+ n = applyFillGradientEffect(n, fillGradient, ctx);
5336
+ n = applyStrokeGradientEffect(n, strokeGradient, ctx);
5337
+ n = applyTrimPathEffect(n, trimPath, isCombinedShape, ctx);
5338
+ n = applyRepeaterEffect(n, repeater, ctx);
5339
+ n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
5340
+ if (innerIdForContentRef) {
5341
+ n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
5342
+ } else {
5343
+ n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
5344
+ }
5345
+ if (retime) node.effects = { retime };
5346
+ if (originalId) ctx.idMap.set(originalId, n);
5347
+ return n;
5348
+ }
5349
+ function applyPlayerEffects_retime(node, ctx) {
5350
+ applyAllRetimeEffects(node, ctx);
5351
+ return node;
5352
+ }
5353
+ function materialiseAnimatedUseInstances(root) {
5354
+ var _a;
5355
+ const idMap = buildIdMap(root);
5356
+ const animatedIds = computeAnimatedSubtreeIds(root, idMap);
5357
+ if (animatedIds.size === 0) return root;
5358
+ let idCounter = 0;
5359
+ const genId3 = () => "_lw_use_mat_" + ++idCounter;
5360
+ const defsCollector = [];
5361
+ const walked = walkAndMaterialise2(root, idMap, animatedIds, genId3, defsCollector);
5362
+ if (defsCollector.length === 0) return walked;
5363
+ const defsNode = { type: "defs", children: defsCollector };
5364
+ const newChildren = [...(_a = walked.children) != null ? _a : [], defsNode];
5365
+ return __spreadProps(__spreadValues({}, walked), { children: newChildren });
5366
+ }
5367
+ function buildIdMap(root) {
5368
+ const map = /* @__PURE__ */ new Map();
5369
+ const visit = (n) => {
5370
+ var _a;
5371
+ if (typeof n.id === "string") map.set(n.id, n);
5372
+ (_a = n.children) == null ? void 0 : _a.forEach(visit);
5373
+ };
5374
+ visit(root);
5375
+ return map;
5376
+ }
5377
+ function computeAnimatedSubtreeIds(root, idMap) {
5378
+ const cache = /* @__PURE__ */ new WeakMap();
5379
+ const result = /* @__PURE__ */ new Set();
5380
+ const hasAnim = (n, visiting) => {
5381
+ const cached = cache.get(n);
5382
+ if (cached !== void 0) return cached;
5383
+ if (visiting.has(n)) return false;
5384
+ visiting.add(n);
5385
+ let r = false;
5386
+ if (n.animate && typeof n.animate === "object" && !Array.isArray(n.animate)) {
5387
+ for (const _ in n.animate) {
5388
+ r = true;
5389
+ break;
5390
+ }
5391
+ }
5392
+ if (!r && n.children) {
5393
+ for (const ch of n.children) {
5394
+ if (hasAnim(ch, visiting)) {
5395
+ r = true;
5396
+ break;
5397
+ }
5398
+ }
5399
+ }
5400
+ if (!r && n.type === "use" && typeof n.href === "string") {
5401
+ const targetId = stripHash2(n.href);
5402
+ const target = targetId ? idMap.get(targetId) : void 0;
5403
+ if (target) r = hasAnim(target, visiting);
5404
+ }
5405
+ visiting.delete(n);
5406
+ cache.set(n, r);
5407
+ return r;
5408
+ };
5409
+ for (const [id, node] of idMap) {
5410
+ if (hasAnim(node, /* @__PURE__ */ new Set())) result.add(id);
5411
+ }
5412
+ return result;
5413
+ }
5414
+ function stripHash2(href) {
5415
+ if (typeof href !== "string") return void 0;
5416
+ return href.startsWith("#") ? href.slice(1) : href;
5417
+ }
5418
+ function materialiseOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector) {
5419
+ const clone2 = deepClonePxNode(target);
5420
+ regenerateIdsAndRewriteRefs(clone2, genId3);
5421
+ const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector) : clone2;
5422
+ const materialisedClone = walkAndMaterialise2(rewrittenClone, idMap, animatedIds, genId3, defsCollector);
5423
+ const newNode = __spreadProps(__spreadValues({}, useNode), { type: "g", children: [materialisedClone] });
5424
+ delete newNode.href;
5425
+ return applyUseOffsetToG(newNode);
5426
+ }
5427
+ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector) {
5428
+ const viewBox = parseViewBox(symbolNode.viewBox);
5429
+ const g = __spreadProps(__spreadValues({}, symbolNode), { type: "g" });
5430
+ delete g.viewBox;
5431
+ delete g.preserveAspectRatio;
5432
+ delete g.width;
5433
+ delete g.height;
5434
+ if (!viewBox) return g;
5435
+ const [vbX, vbY, vbW, vbH] = viewBox;
5436
+ if (vbX !== 0 || vbY !== 0) {
5437
+ g.transform = "translate(" + -vbX + "," + -vbY + ")";
5438
+ }
5439
+ const clipId = genId3();
5440
+ defsCollector.push({
5441
+ type: "clipPath",
5442
+ id: clipId,
5443
+ children: [{ type: "rect", x: vbX, y: vbY, width: vbW, height: vbH }]
5444
+ });
5445
+ g.clipPath = "url(#" + clipId + ")";
5446
+ return g;
5447
+ }
5448
+ function parseViewBox(v) {
5449
+ if (typeof v !== "string") return void 0;
5450
+ const parts = v.trim().split(/[\s,]+/).map(Number);
5451
+ if (parts.length < 4 || parts.some((n) => !Number.isFinite(n))) return void 0;
5452
+ return [parts[0], parts[1], parts[2], parts[3]];
5453
+ }
5454
+ function walkAndMaterialise2(node, idMap, animatedIds, genId3, defsCollector) {
5455
+ if (node.type === "use" && typeof node.href === "string") {
5456
+ const targetId = stripHash2(node.href);
5457
+ if (targetId && animatedIds.has(targetId)) {
5458
+ const target = idMap.get(targetId);
5459
+ if (target) return materialiseOneUse(node, target, idMap, animatedIds, genId3, defsCollector);
5460
+ }
5461
+ }
5462
+ if (!node.children) return node;
5463
+ let changed = false;
5464
+ const newChildren = node.children.map((ch) => {
5465
+ const m = walkAndMaterialise2(ch, idMap, animatedIds, genId3, defsCollector);
5466
+ if (m !== ch) changed = true;
5467
+ return m;
5468
+ });
5469
+ return changed ? __spreadProps(__spreadValues({}, node), { children: newChildren }) : node;
5470
+ }
5471
+ function materialiseAllInTree(doc, engine, opts) {
5472
+ var _a, _b;
5473
+ let root = applyPlayerEffects(doc).root;
5474
+ const duration = (_b = (_a = getAnimatorConfig(root)) == null ? void 0 : _a.duration) != null ? _b : DEFAULT_DURATION_MS;
5475
+ root = materialiseInternalLoopsInTree(root, duration);
5476
+ if (engine === PxAnimatorEngine.webapi) {
5477
+ root = materialiseMotionPathsInTree(root, opts == null ? void 0 : opts.motionPath);
5478
+ root = materialiseAnimatedUseInstances(root);
5479
+ }
5480
+ return root;
5481
+ }
5482
+ var SVG_NS = "http://www.w3.org/2000/svg";
5483
+ var DISALLOWED_SVG_TAGS_LOWER = /* @__PURE__ */ new Set([
5484
+ "script",
5485
+ "foreignobject"
5486
+ ]);
5487
+ var URL_VALUE_ATTRS_LOWER = /* @__PURE__ */ new Set([
5488
+ "href",
5489
+ // <use>, <image>
5490
+ "xlink:href",
5491
+ // legacy <use>
5492
+ "src",
5493
+ // <image>
5494
+ "filter",
5495
+ // url(#filterId)
5496
+ "clippath",
5497
+ // clip-path="url(#…)"
5498
+ "mask",
5499
+ // url(#maskId)
5500
+ "markerstart",
5501
+ // marker-start="url(#…)"
5502
+ "markermid",
5503
+ // marker-mid="url(#…)"
5504
+ "markerend"
5505
+ // marker-end="url(#…)"
5506
+ ]);
5507
+ var IMAGE_REF_ATTRS_LOWER = /* @__PURE__ */ new Set(["href", "xlink:href", "src"]);
5508
+ var DATA_RASTER_IMAGE_RE = /^data:image\/(?:png|jpe?g|gif|webp|bmp);base64,/i;
5509
+ function isDangerousAttrName(nameLower) {
5510
+ if (nameLower.startsWith("on")) return true;
5511
+ return false;
5512
+ }
5513
+ function sanitiseAttributeValue(name, value) {
5514
+ const nameLower = name.toLowerCase();
5515
+ if (isDangerousAttrName(nameLower)) {
5516
+ console.warn("Attribute blocked (event handler / dangerous): ", nameLower);
5517
+ return void 0;
5518
+ }
5519
+ if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopcolor") {
5520
+ const str = String(value);
5521
+ if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
5522
+ console.warn('Attribute "' + nameLower + '" blocked: url() must be internal url(#id), got:', value);
5523
+ return void 0;
5524
+ }
5525
+ return value;
5526
+ }
5527
+ if (URL_VALUE_ATTRS_LOWER.has(nameLower)) {
5528
+ const str = String(value);
5529
+ if (str.startsWith("#")) return value;
5530
+ if (/^url\(#[^)]+\)$/.test(str)) return value;
5531
+ if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && DATA_RASTER_IMAGE_RE.test(str)) return value;
5532
+ console.warn('Attribute "' + nameLower + '" blocked: must be #id, url(#id), or base64 raster data: URI, got:', value);
5533
+ return void 0;
5534
+ }
5535
+ return value;
5536
+ }
5537
+ function createElement(tagName, normalisedProps, style, children, textContent) {
5538
+ if (DISALLOWED_SVG_TAGS_LOWER.has(tagName.toLowerCase())) {
5539
+ console.warn("SVG tag blocked (dangerous): ", tagName);
5540
+ return null;
5541
+ }
5542
+ const element = document.createElementNS(SVG_NS, tagName);
5543
+ for (const propName in normalisedProps) {
5544
+ const sanitised = sanitiseAttributeValue(propName, normalisedProps[propName]);
5545
+ if (sanitised === void 0) continue;
5546
+ element.setAttribute(camelCaseToKebabWordIfNeeded(propName), sanitised);
5547
+ }
5548
+ if (style) {
5549
+ for (const styleProp in style) {
5550
+ element.style[styleProp] = String(style[styleProp]);
5551
+ }
5552
+ }
5553
+ if (children) {
5554
+ for (const child of children) {
5555
+ element.appendChild(child);
5556
+ }
5557
+ }
5558
+ if (textContent) element.textContent = textContent;
5559
+ return element;
5560
+ }
5561
+ function resolveStyle(style, defs) {
5562
+ var _a;
5563
+ if (!style) return void 0;
5564
+ if (typeof style === "string") {
5565
+ return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
5566
+ }
5567
+ return style;
5568
+ }
5569
+ function getNormalizedProps(props) {
5570
+ const propsCopy = {};
5571
+ for (const rawKey of Object.keys(props)) {
5572
+ const key = kebabToCamelCaseWord(rawKey);
5573
+ if (INTERNAL_ATTRS.has(key)) continue;
5574
+ if (key === "style") continue;
5575
+ let value = props[rawKey];
5576
+ if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
5577
+ propsCopy[key] = toRGBA(value);
5578
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
5579
+ propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
5580
+ } else if (TRANSFORM_FN_NAMES.has(key)) {
5581
+ if (Array.isArray(value)) {
5582
+ if (key === "translate") value = value.map((v) => v + "px");
5583
+ value = value.join(",");
5584
+ }
5585
+ if (key === "rotate") value = value + "deg";
5586
+ propsCopy["transform"] = key + "(" + value + ")";
5587
+ } else if (value !== void 0 && value !== null) {
5588
+ propsCopy[key] = String(value);
5589
+ }
5590
+ }
5591
+ return propsCopy;
2705
5592
  }
2706
- function normalizeAnimationDefinition(animDef, duration, defs) {
2707
- const normalized = {};
2708
- for (const [propName, propAnim] of Object.entries(animDef)) {
2709
- const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2710
- if (normalizedKfs.length > 0) {
2711
- normalized[propName] = { kfs: normalizedKfs };
5593
+ function renderNode(node, defs) {
5594
+ if (!node) return null;
5595
+ const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
5596
+ const nodeDefs = getDefs(node) || defs;
5597
+ const resolvedStyle = resolveStyle(style, nodeDefs);
5598
+ let childElements;
5599
+ if (children) {
5600
+ for (const ch of children) {
5601
+ const child = renderNode(ch, nodeDefs);
5602
+ if (child) {
5603
+ if (!childElements) childElements = [];
5604
+ childElements.push(child);
5605
+ }
2712
5606
  }
2713
5607
  }
2714
- return normalized;
5608
+ return createElement(
5609
+ type || "g",
5610
+ getNormalizedProps(props),
5611
+ resolvedStyle,
5612
+ childElements,
5613
+ props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
5614
+ );
2715
5615
  }
2716
- function getNormalisedBindings(doc) {
2717
- const animatorConfig = getAnimatorConfig(doc) || {};
2718
- const defs = getDefs(doc);
2719
- const duration = animatorConfig.duration || 1e3;
2720
- const bindings = [];
2721
- const processAnimation = (id, animate) => {
2722
- if (!animate) return null;
2723
- const animDefs = resolveElementAnimation(animate, defs);
2724
- if (animDefs.length === 0) return null;
2725
- const merged = mergeAnimationDefinitions(animDefs);
2726
- const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs);
2727
- if (Object.keys(normalizedAnim).length === 0) return null;
2728
- return {
2729
- id,
2730
- animate: normalizedAnim
2731
- };
2732
- };
2733
- const docBindings = getBindings(doc);
2734
- if (docBindings) {
2735
- for (const binding of docBindings) {
2736
- const normalized = processAnimation(binding.id, binding.animate);
2737
- if (normalized) bindings.push(normalized);
2738
- }
5616
+ function setupAnimationTriggers(api, config) {
5617
+ const { startOn, outAction = "continue", scrollIntoViewThreshold = 0.5 } = config;
5618
+ const root = api.getRootElement();
5619
+ if (!root) {
5620
+ console.warn("setupAnimationTriggers: No root element found for animation.");
5621
+ return api;
2739
5622
  }
2740
- const processNode = (node) => {
2741
- if (node.animate) {
2742
- const nodeId = node.id || generateElementId();
2743
- node.id = nodeId;
2744
- const normalized = processAnimation(nodeId, node.animate);
2745
- if (normalized) bindings.push(normalized);
2746
- }
2747
- if (node.children) {
2748
- for (let i = 0; i < node.children.length; i++) {
2749
- processNode(node.children[i]);
2750
- }
5623
+ const start = () => {
5624
+ api.play();
5625
+ };
5626
+ const handleEndAction = () => {
5627
+ switch (outAction) {
5628
+ case "pause":
5629
+ api.pause();
5630
+ break;
5631
+ case "reset":
5632
+ api.cancel();
5633
+ break;
5634
+ case "reverse":
5635
+ api.play();
5636
+ break;
5637
+ case "continue":
5638
+ default:
5639
+ break;
2751
5640
  }
2752
5641
  };
2753
- if (doc.children) {
2754
- for (let i = 0; i < doc.children.length; i++) {
2755
- processNode(doc.children[i]);
5642
+ switch (startOn) {
5643
+ case "load": {
5644
+ const startHandler = () => start();
5645
+ if (document.readyState === "complete") {
5646
+ startHandler();
5647
+ } else {
5648
+ window.addEventListener("load", startHandler, { once: true });
5649
+ }
5650
+ break;
2756
5651
  }
2757
- }
2758
- return bindings;
2759
- }
2760
- function getKeyframesPair(keyframes, progress) {
2761
- var _a, _b;
2762
- let prevKf = keyframes[0];
2763
- let nextKf = keyframes[keyframes.length - 1];
2764
- for (let j = 0; j < keyframes.length - 1; j++) {
2765
- const aOff = (_a = keyframes[j].t) != null ? _a : 0;
2766
- const bOff = (_b = keyframes[j + 1].t) != null ? _b : 0;
2767
- if (aOff <= progress && progress <= bOff) {
2768
- prevKf = keyframes[j];
2769
- nextKf = keyframes[j + 1];
5652
+ case "mouseOver": {
5653
+ const mouseOverHandler = () => start();
5654
+ const mouseOutHandler = () => handleEndAction();
5655
+ root.addEventListener("mouseenter", mouseOverHandler);
5656
+ root.addEventListener("mouseleave", mouseOutHandler);
2770
5657
  break;
2771
5658
  }
2772
- }
2773
- return { prevKf, nextKf };
2774
- }
2775
- function calcPropertyValue(propName, propAnim, progress) {
2776
- var _a, _b, _c, _d, _e, _f, _g;
2777
- const keyframes = propAnim.kfs || propAnim.keyframes || [];
2778
- if (keyframes.length === 0) return null;
2779
- const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
2780
- let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a = prevKf.t) != null ? _a : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
2781
- localProgress = clamp(localProgress, 0, 1);
2782
- const easing = (_c = prevKf.e) != null ? _c : prevKf.easing;
2783
- if (easing && Array.isArray(easing)) {
2784
- try {
2785
- localProgress = cubicBezier(easing)(localProgress);
2786
- } catch (e) {
5659
+ case "click": {
5660
+ const clickHandler = () => {
5661
+ if (api.isPlaying()) {
5662
+ handleEndAction();
5663
+ } else {
5664
+ start();
5665
+ }
5666
+ };
5667
+ root.addEventListener("click", clickHandler);
5668
+ break;
2787
5669
  }
2788
- }
2789
- let cssAttrName = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2790
- let cssValue = null;
2791
- const prevV = (_d = prevKf == null ? void 0 : prevKf.v) != null ? _d : prevKf == null ? void 0 : prevKf.value;
2792
- const nextV = (_e = nextKf == null ? void 0 : nextKf.v) != null ? _e : nextKf == null ? void 0 : nextKf.value;
2793
- if (cssAttrName === "d") {
2794
- const prevPaths = (_f = prevV == null ? void 0 : prevV.paths) != null ? _f : Array.isArray(prevV) ? prevV : [];
2795
- const nextPaths = (_g = nextV == null ? void 0 : nextV.paths) != null ? _g : Array.isArray(nextV) ? nextV : [];
2796
- cssValue = interpolateBeziers(
2797
- prevPaths,
2798
- nextPaths,
2799
- localProgress
2800
- ).map((bz) => bezierToSvgPath(bz)).join("");
2801
- } else if (COLOUR_ATTR_NAMES.has(cssAttrName)) {
2802
- cssValue = toRGBA(interpolateColor(
2803
- prevV || [0, 0, 0, 1],
2804
- nextV || [0, 0, 0, 1],
2805
- localProgress
2806
- ));
2807
- cssAttrName = propName;
2808
- } else if (cssAttrName === "stroke-dasharray") {
2809
- cssValue = interpolateVec(
2810
- prevV || [],
2811
- nextV || [],
2812
- localProgress
2813
- ).join(" ");
2814
- cssAttrName = propName;
2815
- } else if (cssAttrName === "translate") {
2816
- const v = interpolateVec(
2817
- prevV || [0, 0],
2818
- nextV || [0, 0],
2819
- localProgress
2820
- );
2821
- cssValue = "translate(" + v.join(",") + ")";
2822
- cssAttrName = "transform";
2823
- } else if (cssAttrName === "rotate") {
2824
- const v = interpolateNum(
2825
- +(prevV || 0),
2826
- +(nextV || 0),
2827
- localProgress
2828
- );
2829
- cssValue = "rotate(" + v + ")";
2830
- cssAttrName = "transform";
2831
- } else if (cssAttrName === "scale") {
2832
- const v = interpolateVec(
2833
- prevV || [1, 1],
2834
- nextV || [1, 1],
2835
- localProgress
2836
- );
2837
- cssValue = "scale(" + v.join(",") + ")";
2838
- cssAttrName = "transform";
2839
- } else {
2840
- const num = interpolateNum(
2841
- +(prevV || 0),
2842
- +(nextV || 0),
2843
- localProgress
2844
- );
2845
- cssValue = num;
2846
- }
2847
- if (PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
2848
- cssValue = cssValue * 100 + "%";
2849
- }
2850
- return { k: cssAttrName, v: cssValue === null ? "" : "" + cssValue };
2851
- }
2852
- function calcAnimationValues(animDef, progress) {
2853
- const result = {};
2854
- for (const [propName, propAnim] of Object.entries(animDef)) {
2855
- const computed = calcPropertyValue(propName, propAnim, progress);
2856
- if (computed) {
2857
- result[computed.k] = computed.v;
5670
+ case "scrollIntoView": {
5671
+ const observer = new IntersectionObserver(
5672
+ (entries) => {
5673
+ entries.forEach((entry) => {
5674
+ if (entry.isIntersecting && entry.intersectionRatio >= scrollIntoViewThreshold) {
5675
+ start();
5676
+ } else {
5677
+ handleEndAction();
5678
+ }
5679
+ });
5680
+ },
5681
+ { threshold: scrollIntoViewThreshold }
5682
+ );
5683
+ observer.observe(root);
5684
+ break;
2858
5685
  }
5686
+ case "programmatic":
5687
+ break;
2859
5688
  }
2860
- return result;
5689
+ return api;
2861
5690
  }
2862
5691
  function getSelector(id) {
2863
5692
  return "#" + id;
2864
5693
  }
2865
5694
  function createBasicFrameLoopAnimator(doc, adapter, callbacks) {
2866
5695
  const config = getAnimatorConfig(doc) || {};
2867
- const bindings = getNormalisedBindings(doc);
5696
+ const bindings = getNormalisedBindings(doc, PxAnimatorEngine.frames);
2868
5697
  const _iterations = config.iterations;
2869
5698
  let iterations = 1;
2870
5699
  if (typeof _iterations === "number") iterations = _iterations || 1;
@@ -2982,6 +5811,10 @@ var PixodeskAnimatorReact = (() => {
2982
5811
  };
2983
5812
  const startAnim = () => {
2984
5813
  if (playing) return;
5814
+ if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= totalDuration) {
5815
+ timeBeforeLastStartMs = 0;
5816
+ finishCalled = false;
5817
+ }
2985
5818
  playing = true;
2986
5819
  lastStartedTs = Date.now();
2987
5820
  loopAnim(true);
@@ -3130,6 +5963,9 @@ var PixodeskAnimatorReact = (() => {
3130
5963
  let cssKey = propName;
3131
5964
  if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
3132
5965
  cssValue = toRGBA(value);
5966
+ } else if (propName === "transform" && value !== null && typeof value === "object" && !Array.isArray(value)) {
5967
+ cssValue = composeTransformParts(value, { withUnits: true });
5968
+ cssKey = "transform";
3133
5969
  } else if (TRANSFORM_FN_NAMES.has(propName)) {
3134
5970
  if (Array.isArray(value)) {
3135
5971
  if (propName === "translate") value = value.map((v) => v + "px");
@@ -3146,28 +5982,59 @@ var PixodeskAnimatorReact = (() => {
3146
5982
  cssKf[cssKey] = cssValue;
3147
5983
  return cssKf;
3148
5984
  }
5985
+ function clipKeyframesToDuration(propName, keyframes, duration) {
5986
+ var _a, _b, _c, _d, _e;
5987
+ const result = [];
5988
+ for (let i = 0; i < keyframes.length; i++) {
5989
+ const kf = keyframes[i];
5990
+ const t = (_a = kf.t) != null ? _a : 0;
5991
+ if (t < 0) {
5992
+ const next = keyframes[i + 1];
5993
+ if (next && ((_b = next.t) != null ? _b : 0) >= 0) {
5994
+ const nextT = (_c = next.t) != null ? _c : 0;
5995
+ const localFrac = (0 - t) / (nextT - t);
5996
+ const easedFrac = kf.e ? cubicBezier(kf.e)(localFrac) : localFrac;
5997
+ const { right: rightEasing } = splitEasing(kf.e, localFrac);
5998
+ result.push({ t: 0, v: interpolateValue(propName, kf.v, next.v, easedFrac), e: rightEasing });
5999
+ }
6000
+ continue;
6001
+ }
6002
+ if (t > duration) {
6003
+ const prev = keyframes[i - 1];
6004
+ if (prev && ((_d = prev.t) != null ? _d : 0) <= duration) {
6005
+ const prevT = (_e = prev.t) != null ? _e : 0;
6006
+ const localFrac = (duration - prevT) / (t - prevT);
6007
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
6008
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
6009
+ if (result.length > 0) result[result.length - 1] = __spreadProps(__spreadValues({}, result[result.length - 1]), { e: leftEasing });
6010
+ result.push({ t: duration, v: interpolateValue(propName, prev.v, kf.v, easedFrac), e: void 0 });
6011
+ }
6012
+ break;
6013
+ }
6014
+ result.push(kf);
6015
+ }
6016
+ return result;
6017
+ }
3149
6018
  function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
3150
- var _a, _b;
6019
+ var _a;
3151
6020
  const result = /* @__PURE__ */ new Map();
3152
6021
  for (const [propName, propAnim] of Object.entries(animDef)) {
3153
- const keyframes = propAnim.kfs || propAnim.keyframes || [];
6022
+ const duration = config.duration || 1;
6023
+ const clippedKeyframes = clipKeyframesToDuration(propName, propAnim.kfs || propAnim.keyframes || [], duration);
3154
6024
  const cssKeyframes = [];
3155
- for (let i = 0; i < keyframes.length; i++) {
3156
- const kf = keyframes[i];
3157
- let t = (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
3158
- t = clamp(t / (config.duration || 1), 0, 1);
6025
+ for (let i = 0; i < clippedKeyframes.length; i++) {
6026
+ const kf = clippedKeyframes[i];
6027
+ const t = clamp(((_a = kf.t) != null ? _a : 0) / duration, 0, 1);
3159
6028
  const cssKf = createCssKf(kf, t, propName, unsupportedSet);
3160
6029
  if (i === 0 && (cssKf.offset || 0) > 0) {
3161
- cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
3162
- offset: 0
3163
- }));
6030
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), { offset: 0 }));
3164
6031
  }
3165
6032
  cssKeyframes.push(cssKf);
3166
- if (i === keyframes.length - 1 && (cssKf.offset || 0) < 1) {
3167
- cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
3168
- offset: 1
3169
- }));
3170
- }
6033
+ }
6034
+ if (cssKeyframes.length > 0 && (cssKeyframes[cssKeyframes.length - 1].offset || 0) < 1) {
6035
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKeyframes[cssKeyframes.length - 1]), {
6036
+ offset: 1
6037
+ }));
3171
6038
  }
3172
6039
  if (cssKeyframes.length > 0) {
3173
6040
  result.set(propName, cssKeyframes);
@@ -3176,8 +6043,8 @@ var PixodeskAnimatorReact = (() => {
3176
6043
  return result;
3177
6044
  }
3178
6045
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs) {
6046
+ var _a;
3179
6047
  const config = getAnimatorConfig(doc) || {};
3180
- const bindings = getNormalisedBindings(doc);
3181
6048
  if (!rootElement) {
3182
6049
  if (doc.id) {
3183
6050
  const rootSelector = getSelector(doc.id);
@@ -3187,6 +6054,7 @@ var PixodeskAnimatorReact = (() => {
3187
6054
  console.warn("createFrameLoopAnimator: No root element provided");
3188
6055
  }
3189
6056
  }
6057
+ const bindings = getNormalisedBindings(doc, PxAnimatorEngine.webapi);
3190
6058
  const animations = [];
3191
6059
  const _iterations = config.iterations;
3192
6060
  let iterations;
@@ -3213,7 +6081,11 @@ var PixodeskAnimatorReact = (() => {
3213
6081
  const effectOptions = {
3214
6082
  duration: config.duration,
3215
6083
  delay: positiveDelay,
3216
- fill: config.fill,
6084
+ // Default to 'forwards' so elements hold their final state after the
6085
+ // animation ends — consistent with Lottie and other animation runtimes.
6086
+ // Without this, seeking to the last frame reverts elements to their
6087
+ // pre-animation state (the Web Animations API "after" phase with fill:'none').
6088
+ fill: (_a = config.fill) != null ? _a : "forwards",
3217
6089
  direction: config.direction,
3218
6090
  iterations
3219
6091
  };
@@ -3225,12 +6097,12 @@ var PixodeskAnimatorReact = (() => {
3225
6097
  const effect = new KeyframeEffect(element, keyframes, effectOptions);
3226
6098
  const anim = new Animation(effect, document.timeline);
3227
6099
  if (callbacks == null ? void 0 : callbacks.onFinish) anim.onfinish = () => {
3228
- var _a;
3229
- return (_a = callbacks.onFinish) == null ? void 0 : _a.call(callbacks);
6100
+ var _a2;
6101
+ return (_a2 = callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
3230
6102
  };
3231
6103
  if (callbacks == null ? void 0 : callbacks.onRemove) anim.onremove = () => {
3232
- var _a;
3233
- return (_a = callbacks.onRemove) == null ? void 0 : _a.call(callbacks);
6104
+ var _a2;
6105
+ return (_a2 = callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
3234
6106
  };
3235
6107
  if (seekPosition) {
3236
6108
  anim.currentTime = seekPosition;
@@ -3251,29 +6123,29 @@ var PixodeskAnimatorReact = (() => {
3251
6123
  "isReady": () => true,
3252
6124
  "getRootElement": () => rootElement || null,
3253
6125
  "isPlaying": () => {
3254
- var _a;
3255
- return ((_a = animations[0]) == null ? void 0 : _a.playState) === "running";
6126
+ var _a2;
6127
+ return ((_a2 = animations[0]) == null ? void 0 : _a2.playState) === "running";
3256
6128
  },
3257
6129
  "play": () => {
3258
- var _a;
6130
+ var _a2;
3259
6131
  animations.forEach((a) => a.play());
3260
- (_a = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a.call(callbacks);
6132
+ (_a2 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a2.call(callbacks);
3261
6133
  },
3262
6134
  "pause": () => {
3263
- var _a;
6135
+ var _a2;
3264
6136
  animations.forEach((a) => a.pause());
3265
- (_a = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a.call(callbacks);
6137
+ (_a2 = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a2.call(callbacks);
3266
6138
  },
3267
6139
  "cancel": () => {
3268
- var _a;
6140
+ var _a2;
3269
6141
  animations.forEach((a) => a.cancel());
3270
- (_a = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a.call(callbacks);
6142
+ (_a2 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a2.call(callbacks);
3271
6143
  },
3272
6144
  "finish": () => {
3273
- var _a;
6145
+ var _a2;
3274
6146
  for (const a of animations) {
3275
6147
  try {
3276
- if (((_a = a.effect) == null ? void 0 : _a.getTiming().iterations) === Infinity) {
6148
+ if (((_a2 = a.effect) == null ? void 0 : _a2.getTiming().iterations) === Infinity) {
3277
6149
  a.effect.updateTiming({ iterations: 1 });
3278
6150
  a.finish();
3279
6151
  a.effect.updateTiming({ iterations: Infinity });
@@ -3290,8 +6162,8 @@ var PixodeskAnimatorReact = (() => {
3290
6162
  return api;
3291
6163
  },
3292
6164
  "getCurrentTime": () => {
3293
- var _a, _b;
3294
- const res = (_b = (_a = animations[0]) == null ? void 0 : _a.currentTime) != null ? _b : null;
6165
+ var _a2, _b;
6166
+ const res = (_b = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b : null;
3295
6167
  return res !== null ? +res : null;
3296
6168
  },
3297
6169
  "setCurrentTime": (time) => {
@@ -3312,15 +6184,15 @@ var PixodeskAnimatorReact = (() => {
3312
6184
  function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
3313
6185
  const animatorConfig = getAnimatorConfig(doc) || {};
3314
6186
  let res;
3315
- if (animatorConfig.mode === "frames") {
6187
+ if (animatorConfig.mode === PxAnimatorMode.frames) {
3316
6188
  res = createFrameLoopAnimator(doc, adapter, callbacks, rootElement);
3317
6189
  } else {
3318
6190
  res = createWebApiAnimator(
3319
6191
  doc,
3320
6192
  callbacks,
3321
6193
  rootElement,
3322
- animatorConfig.mode === "webapi"
3323
- // Forcing "webapi"
6194
+ animatorConfig.mode === PxAnimatorMode.webapi
6195
+ // forcing webapi
3324
6196
  ) || createFrameLoopAnimator(doc, adapter, callbacks, rootElement);
3325
6197
  }
3326
6198
  if (animatorConfig.debugInstName) {
@@ -3346,6 +6218,7 @@ var PixodeskAnimatorReact = (() => {
3346
6218
  return cloned;
3347
6219
  }
3348
6220
  function generateNewIds(doc) {
6221
+ var _a, _b;
3349
6222
  const cloned = deepClone(doc);
3350
6223
  const idMap = /* @__PURE__ */ new Map();
3351
6224
  const hashRefAttrs = /* @__PURE__ */ new Set(["href", "xlink:href"]);
@@ -3371,6 +6244,8 @@ var PixodeskAnimatorReact = (() => {
3371
6244
  const newId = generateUniqueId();
3372
6245
  idMap.set(oldId, newId);
3373
6246
  node.id = newId;
6247
+ } else if (node.animate) {
6248
+ node.id = generateUniqueId();
3374
6249
  }
3375
6250
  if (Array.isArray(node.children)) {
3376
6251
  for (const child of node.children) {
@@ -3412,23 +6287,21 @@ var PixodeskAnimatorReact = (() => {
3412
6287
  value[styleProp] = replaceUrlRefs(styleValue, idMap);
3413
6288
  }
3414
6289
  }
3415
- } else if (typeof value === "object" && value !== null && key !== ANIMATE_ATTR) {
6290
+ } else if (typeof value === "object" && value !== null) {
3416
6291
  updateRefs(value);
3417
6292
  }
3418
6293
  }
3419
6294
  }
3420
6295
  collectIds(cloned);
3421
6296
  updateRefs(cloned);
3422
- const docBindings = cloned.bindings;
3423
- if (Array.isArray(docBindings)) {
3424
- for (const binding of docBindings) {
3425
- if (binding.id) {
3426
- const newId = idMap.get(binding.id);
3427
- if (newId) {
3428
- binding.id = newId;
3429
- }
3430
- }
6297
+ const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animate;
6298
+ if (docAnimate && typeof docAnimate === "object") {
6299
+ const updatedAnimate = {};
6300
+ for (const [id, anim] of Object.entries(docAnimate)) {
6301
+ const newId = (_b = idMap.get(id)) != null ? _b : id;
6302
+ updatedAnimate[newId] = anim;
3431
6303
  }
6304
+ cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animate: updatedAnimate });
3432
6305
  }
3433
6306
  return cloned;
3434
6307
  }
@@ -3439,8 +6312,12 @@ var PixodeskAnimatorReact = (() => {
3439
6312
  });
3440
6313
  }
3441
6314
  function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
6315
+ const effectsWarnings = validateNodeEffects(doc);
6316
+ for (const w of effectsWarnings) console.warn("[PxAnimator] effects shape warning:", w);
3442
6317
  const animatorConfig = getAnimatorConfig(doc) || {};
3443
6318
  animatorConfig.debug = true;
6319
+ const engine = animatorConfig.mode === PxAnimatorMode.frames ? PxAnimatorEngine.frames : PxAnimatorEngine.webapi;
6320
+ doc = materialiseAllInTree(doc, engine);
3444
6321
  let rootElement = null;
3445
6322
  if (containerElement && doc.children) {
3446
6323
  doc = generateNewIds(doc);
@@ -3454,14 +6331,21 @@ var PixodeskAnimatorReact = (() => {
3454
6331
  }
3455
6332
  return createAnimatorFromConfig(doc, adapter, callbacks, rootElement);
3456
6333
  }
3457
- function createAnimator(docOrUrl, adapter, callbacks, containerElement) {
3458
- if (typeof docOrUrl === "object") {
3459
- return createAnimatorImpl(docOrUrl, adapter, callbacks, containerElement);
6334
+ function createAnimator(options) {
6335
+ const { src, data, adapter, callbacks, container } = options;
6336
+ if (data !== void 0 && src !== void 0) {
6337
+ throw new Error("createAnimator: provide either `src` or `data`, not both");
6338
+ }
6339
+ if (data === void 0 && src === void 0) {
6340
+ throw new Error("createAnimator: either `src` or `data` is required");
6341
+ }
6342
+ if (data !== void 0) {
6343
+ return createAnimatorImpl(data, adapter, callbacks, container);
3460
6344
  }
3461
6345
  let animator = null;
3462
- fetch(docOrUrl).then((res) => res.json()).then((json) => {
6346
+ fetch(src).then((res) => res.json()).then((json) => {
3463
6347
  if (isPxElementFileFormat(json)) {
3464
- animator = createAnimatorImpl(json, adapter, callbacks, containerElement);
6348
+ animator = createAnimatorImpl(json, adapter, callbacks, container);
3465
6349
  } else {
3466
6350
  console.error("Invalid animation document format");
3467
6351
  }
@@ -3501,7 +6385,7 @@ var PixodeskAnimatorReact = (() => {
3501
6385
  if (!element[PX_ANIM_ATTR_NAME]) {
3502
6386
  const src = element.getAttribute(PX_ANIM_SRC_ATTR_NAME);
3503
6387
  if (src) {
3504
- element[PX_ANIM_ATTR_NAME] = createAnimator(src, void 0, void 0, element);
6388
+ element[PX_ANIM_ATTR_NAME] = createAnimator({ src, container: element });
3505
6389
  }
3506
6390
  }
3507
6391
  }
@@ -3627,7 +6511,7 @@ var PixodeskAnimatorReact = (() => {
3627
6511
  };
3628
6512
  const root = doc ? renderNode2(doc) : null;
3629
6513
  (0, import_react2.useEffect)(() => {
3630
- let api = createAnimator(doc, createReactAdapter(elementRefs));
6514
+ let api = createAnimator({ data: doc, adapter: createReactAdapter(elementRefs) });
3631
6515
  apiHolderRef.current = api;
3632
6516
  return () => {
3633
6517
  api?.destroy();