@pixodesk/svg-animator-react 1.0.8 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -1759,7 +1759,8 @@ var PixodeskAnimatorReact = (() => {
1759
1759
  // src/index.ts
1760
1760
  var index_exports = {};
1761
1761
  __export(index_exports, {
1762
- PixodeskSvgAnimator: () => PixodeskSvgAnimator_default
1762
+ PixodeskSvgAnimator: () => PixodeskSvgAnimator_default,
1763
+ PixodeskSvgCssAnimator: () => PixodeskSvgCssAnimator_default
1763
1764
  });
1764
1765
 
1765
1766
  // ../svg-animator-web/dist/index.js
@@ -1769,6 +1770,7 @@ var PixodeskAnimatorReact = (() => {
1769
1770
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
1770
1771
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
1771
1772
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
1773
+ var __pow = Math.pow;
1772
1774
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1773
1775
  var __spreadValues = (a, b) => {
1774
1776
  for (var prop in b || (b = {}))
@@ -1794,6 +1796,1258 @@ var PixodeskAnimatorReact = (() => {
1794
1796
  }
1795
1797
  return target;
1796
1798
  };
1799
+ function partsRecord(part, value, origin) {
1800
+ const rec = {};
1801
+ if (part === "translate") rec.translate = value;
1802
+ else if (part === "rotate") rec.rotate = value;
1803
+ else rec.scale = value;
1804
+ if (origin && part !== "translate") rec.origin = origin;
1805
+ return rec;
1806
+ }
1807
+ function readAnimatable(raw) {
1808
+ if (raw === void 0) return { kind: "absent" };
1809
+ if (Array.isArray(raw)) return { kind: "static", value: raw };
1810
+ if (typeof raw === "object") {
1811
+ const obj = raw;
1812
+ if (obj.keyframes) {
1813
+ return { kind: "animated", keyframes: obj.keyframes, autoOrient: obj.autoOrient };
1814
+ }
1815
+ if (obj.value !== void 0) return { kind: "static", value: obj.value };
1816
+ }
1817
+ return { kind: "static", value: raw };
1818
+ }
1819
+ function readStaticOrigin(raw, ctx) {
1820
+ var _a;
1821
+ const o = readAnimatable(raw);
1822
+ if (o.kind === "absent") return void 0;
1823
+ if (o.kind === "static") return o.value;
1824
+ ctx.warnings.push("transformation.origin: animated origin approximated by its first keyframe");
1825
+ return (_a = o.keyframes[0]) == null ? void 0 : _a.value;
1826
+ }
1827
+ function keyframeWith(kf, value) {
1828
+ const out = { value };
1829
+ if (kf.time !== void 0) out.time = kf.time;
1830
+ if (kf.easing !== void 0) out.easing = kf.easing;
1831
+ if (kf.tangentOut !== void 0) out.tangentOut = kf.tangentOut;
1832
+ if (kf.tangentIn !== void 0) out.tangentIn = kf.tangentIn;
1833
+ return out;
1834
+ }
1835
+ function applyTransformationEffect(node, fx, ctx) {
1836
+ if (!fx) return node;
1837
+ delete node.transform;
1838
+ let n = node;
1839
+ n = wrapTransformPart(n, "skew", fx.skew, ctx);
1840
+ n = wrapOrigin(
1841
+ n,
1842
+ fx.origin,
1843
+ /*invert=*/
1844
+ true
1845
+ );
1846
+ n = wrapTransformPart(n, "scale", normalizeScale(fx.scale), ctx);
1847
+ n = wrapTransformPart(n, "rotate", fx.rotate, ctx);
1848
+ n = wrapOrigin(
1849
+ n,
1850
+ fx.origin,
1851
+ /*invert=*/
1852
+ false
1853
+ );
1854
+ if (translateHasAutoOrient(fx.translate)) {
1855
+ n = wrapOrigin(
1856
+ n,
1857
+ fx.origin,
1858
+ /*invert=*/
1859
+ true
1860
+ );
1861
+ n = wrapTransformPart(n, "translate", fx.translate, ctx);
1862
+ n = wrapOrigin(
1863
+ n,
1864
+ fx.origin,
1865
+ /*invert=*/
1866
+ false
1867
+ );
1868
+ } else {
1869
+ n = wrapTransformPart(n, "translate", fx.translate, ctx);
1870
+ }
1871
+ return n;
1872
+ }
1873
+ function translateHasAutoOrient(translate) {
1874
+ if (!translate || typeof translate !== "object") return false;
1875
+ const obj = translate;
1876
+ if (obj.autoOrient) return true;
1877
+ return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
1878
+ }
1879
+ function normalizeScale(raw) {
1880
+ if (raw === void 0) return void 0;
1881
+ if (Array.isArray(raw)) return [raw[0] / 100, raw[1] / 100];
1882
+ return raw;
1883
+ }
1884
+ function wrapTransformPart(inner, part, raw, ctx) {
1885
+ if (raw === void 0) return inner;
1886
+ if (part === "skew") {
1887
+ const skew = readAnimatable(raw);
1888
+ if (skew.kind !== "static") {
1889
+ ctx.warnings.push("transformation.skew: only static skew is supported");
1890
+ return inner;
1891
+ }
1892
+ return { type: "g", transform: "skewX(" + skew.value[0] + ")skewY(" + skew.value[1] + ")", children: [inner] };
1893
+ }
1894
+ const v = readAnimatable(raw);
1895
+ if (v.kind === "static") {
1896
+ return { type: "g", transform: { value: partsRecord(part, v.value, void 0) }, children: [inner] };
1897
+ }
1898
+ if (v.kind === "animated") {
1899
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
1900
+ if (v.autoOrient) animTr.autoOrient = true;
1901
+ return {
1902
+ type: "g",
1903
+ animate: { transform: animTr },
1904
+ children: [inner]
1905
+ };
1906
+ }
1907
+ return inner;
1908
+ }
1909
+ function wrapOrigin(inner, raw, invert) {
1910
+ if (raw === void 0) return inner;
1911
+ const v = readAnimatable(raw);
1912
+ const sign = (value) => invert ? [-value[0], -value[1]] : value;
1913
+ if (v.kind === "absent") return inner;
1914
+ if (v.kind === "static") {
1915
+ if (v.value[0] === 0 && v.value[1] === 0) return inner;
1916
+ return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
1917
+ }
1918
+ if (v.kind === "animated") {
1919
+ return {
1920
+ type: "g",
1921
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) } },
1922
+ children: [inner]
1923
+ };
1924
+ }
1925
+ return inner;
1926
+ }
1927
+ function identifyContentRefTargets(node, ctx, allocator) {
1928
+ var _a, _b, _c;
1929
+ if (node.type === "use" && ((_b = (_a = node.effects) == null ? void 0 : _a.ref) == null ? void 0 : _b.type) === "content") {
1930
+ const baseId = node.effects.ref.baseId;
1931
+ if (typeof baseId === "string" && baseId && !ctx.contentRefInnerIds.has(baseId)) {
1932
+ ctx.contentRefInnerIds.set(baseId, allocator(baseId));
1933
+ }
1934
+ }
1935
+ (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
1936
+ }
1937
+ function splitForContentRef(node, transformation, originalId, innerId, ctx) {
1938
+ const outerBody = liftBodyTranslate(node, transformation);
1939
+ if (typeof node.id === "string") delete node.id;
1940
+ const { outer: outerTr, inner: innerTr } = splitTransformationEffect(transformation);
1941
+ let innerNode = node;
1942
+ innerNode = applyTransformationEffect(innerNode, innerTr, ctx);
1943
+ const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
1944
+ let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
1945
+ if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
1946
+ if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
1947
+ if (outerTr) {
1948
+ delete outerWrapper.id;
1949
+ outerWrapper = applyTransformationEffect(outerWrapper, outerTr, ctx);
1950
+ outerWrapper.id = originalId;
1951
+ }
1952
+ return outerWrapper;
1953
+ }
1954
+ function liftBodyTranslate(node, transformation) {
1955
+ var _a;
1956
+ const out = {};
1957
+ let didLiftAnimate = false;
1958
+ const animTr = (_a = node.animate) == null ? void 0 : _a.transform;
1959
+ if (animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes)) {
1960
+ const kfs = animTr.keyframes;
1961
+ const hasTranslate = kfs.some((kf) => kf.value && kf.value.translate);
1962
+ if (hasTranslate) {
1963
+ const outerHasOrigin = needsOriginOnOuter(animTr);
1964
+ const outerKfs = kfs.map((kf) => {
1965
+ const v = kf.value || {};
1966
+ const newValue = {};
1967
+ if (v.translate !== void 0) newValue.translate = v.translate;
1968
+ if (outerHasOrigin && v.origin !== void 0) newValue.origin = v.origin;
1969
+ const outerKf = { value: newValue };
1970
+ if (kf.time !== void 0) outerKf.time = kf.time;
1971
+ if (kf.easing !== void 0) outerKf.easing = kf.easing;
1972
+ if (kf.tangentOut !== void 0) outerKf.tangentOut = kf.tangentOut;
1973
+ if (kf.tangentIn !== void 0) outerKf.tangentIn = kf.tangentIn;
1974
+ return outerKf;
1975
+ });
1976
+ const outerAnimTr = { keyframes: outerKfs };
1977
+ if (animTr.autoOrient) outerAnimTr.autoOrient = true;
1978
+ out.animate = { transform: outerAnimTr };
1979
+ const innerHasPivotedPart = kfs.some((kf) => {
1980
+ const v = kf.value || {};
1981
+ return v.rotate !== void 0 || v.scale !== void 0;
1982
+ });
1983
+ const innerKfs = kfs.map((kf) => {
1984
+ const v = kf.value || {};
1985
+ const newValue = {};
1986
+ if (v.rotate !== void 0) newValue.rotate = v.rotate;
1987
+ if (v.scale !== void 0) newValue.scale = v.scale;
1988
+ if (v.origin !== void 0 && (!outerHasOrigin || innerHasPivotedPart)) newValue.origin = v.origin;
1989
+ const innerKf = { value: newValue };
1990
+ if (kf.time !== void 0) innerKf.time = kf.time;
1991
+ if (kf.easing !== void 0) innerKf.easing = kf.easing;
1992
+ return innerKf;
1993
+ });
1994
+ const allInnerEmpty = innerKfs.every((kf) => Object.keys(kf.value).length === 0);
1995
+ if (allInnerEmpty) {
1996
+ delete node.animate.transform;
1997
+ if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
1998
+ } else {
1999
+ node.animate.transform = { keyframes: innerKfs };
2000
+ }
2001
+ didLiftAnimate = true;
2002
+ }
2003
+ }
2004
+ const transformationHasTranslate = (transformation == null ? void 0 : transformation.translate) !== void 0;
2005
+ const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
2006
+ if (typeof node.transform === "string") {
2007
+ const split = splitTransformString(node.transform);
2008
+ if (stripBodyTranslateOnly) {
2009
+ if (split.translate !== void 0) {
2010
+ if (split.rest) node.transform = split.rest;
2011
+ else delete node.transform;
2012
+ } else if (isPureTranslateBody(node.transform)) {
2013
+ delete node.transform;
2014
+ }
2015
+ } else if (split.translate) {
2016
+ out.transform = split.translate;
2017
+ if (split.rest) node.transform = split.rest;
2018
+ else delete node.transform;
2019
+ }
2020
+ }
2021
+ return out;
2022
+ }
2023
+ function isPureTranslateBody(s) {
2024
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
2025
+ const ops = [];
2026
+ let m;
2027
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
2028
+ if (ops.length !== 1) return false;
2029
+ if (ops[0].name === "translate") return true;
2030
+ if (ops[0].name !== "matrix") return false;
2031
+ const args = /matrix\(([^)]*)\)/.exec(ops[0].full);
2032
+ if (!args) return false;
2033
+ const nums = args[1].split(/[\s,]+/).filter(Boolean).map(Number);
2034
+ return nums.length >= 4 && nums[0] === 1 && nums[1] === 0 && nums[2] === 0 && nums[3] === 1;
2035
+ }
2036
+ function splitTransformString(s) {
2037
+ const ops = [];
2038
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
2039
+ let m;
2040
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
2041
+ if (!ops.length) return { rest: s || void 0 };
2042
+ if (ops.every((o) => o.name === "translate")) {
2043
+ return { translate: ops.map((o) => o.full).join("") };
2044
+ }
2045
+ const leading = ops[0];
2046
+ const trailing = ops[ops.length - 1];
2047
+ if (trailing.name === "translate" && leading.name === "translate") {
2048
+ const trailingVec = parseTranslateArgs(trailing.full);
2049
+ const leadingVec = parseTranslateArgs(leading.full);
2050
+ const ox = -trailingVec[0];
2051
+ const oy = -trailingVec[1];
2052
+ const userTx = leadingVec[0] - ox;
2053
+ const userTy = leadingVec[1] - oy;
2054
+ if (userTx === 0 && userTy === 0) return { rest: s };
2055
+ const middleAndTrailing = "translate(" + ox + "," + oy + ")" + ops.slice(1).map((o) => o.full).join("");
2056
+ return { translate: "translate(" + userTx + "," + userTy + ")", rest: middleAndTrailing };
2057
+ }
2058
+ if (trailing.name === "translate") return { rest: s };
2059
+ const lifted = [];
2060
+ let i = 0;
2061
+ while (i < ops.length && ops[i].name === "translate") {
2062
+ lifted.push(ops[i].full);
2063
+ i++;
2064
+ }
2065
+ if (!lifted.length) return { rest: s };
2066
+ const rest = ops.slice(i).map((o) => o.full).join("");
2067
+ return {
2068
+ translate: lifted.join(""),
2069
+ rest: rest || void 0
2070
+ };
2071
+ }
2072
+ function parseTranslateArgs(translateOp) {
2073
+ const m = /translate\(([^)]*)\)/.exec(translateOp);
2074
+ if (!m) return [0, 0];
2075
+ const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
2076
+ return [nums[0] || 0, nums[1] || 0];
2077
+ }
2078
+ function splitTransformationEffect(fx) {
2079
+ if (!fx) return {};
2080
+ const originOnOuter = needsOriginOnOuter(fx.translate);
2081
+ const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
2082
+ const outer = {};
2083
+ const inner = {};
2084
+ if (fx.translate !== void 0) outer.translate = fx.translate;
2085
+ if (originOnOuter && fx.origin !== void 0) outer.origin = fx.origin;
2086
+ if (fx.rotate !== void 0) inner.rotate = fx.rotate;
2087
+ if (fx.scale !== void 0) inner.scale = fx.scale;
2088
+ if (fx.skew !== void 0) inner.skew = fx.skew;
2089
+ if (fx.origin !== void 0 && (!originOnOuter || innerHasPivotedPart)) inner.origin = fx.origin;
2090
+ return {
2091
+ outer: Object.keys(outer).length ? outer : void 0,
2092
+ inner: Object.keys(inner).length ? inner : void 0
2093
+ };
2094
+ }
2095
+ function needsOriginOnOuter(translateAnim) {
2096
+ if (!translateAnim || typeof translateAnim !== "object") return false;
2097
+ const obj = translateAnim;
2098
+ if (obj.autoOrient) return true;
2099
+ if (Array.isArray(obj.keyframes)) {
2100
+ return obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
2101
+ }
2102
+ return false;
2103
+ }
2104
+ function genId(ctx, prefix) {
2105
+ return "_lw_" + prefix + "_" + ctx.nextId++;
2106
+ }
2107
+ function indexById(node, map) {
2108
+ var _a;
2109
+ if (typeof node.id === "string") map.set(node.id, node);
2110
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => indexById(child, map));
2111
+ }
2112
+ function spliceDefs(root, defs) {
2113
+ if (!defs.length) return;
2114
+ const existing = root.children || (root.children = []);
2115
+ existing.unshift({ type: "defs", children: defs });
2116
+ }
2117
+ function clone(value) {
2118
+ if (value === null || typeof value !== "object") return value;
2119
+ if (Array.isArray(value)) return value.map(clone);
2120
+ const out = {};
2121
+ for (const k of Object.keys(value)) out[k] = clone(value[k]);
2122
+ return out;
2123
+ }
2124
+ function regenerateIdsInClone(root, ctx) {
2125
+ const oldToNew = /* @__PURE__ */ new Map();
2126
+ const walkAssign = (n) => {
2127
+ var _a;
2128
+ if (typeof n.id === "string") {
2129
+ const newId = genId(ctx, "retimed");
2130
+ oldToNew.set(n.id, newId);
2131
+ n.id = newId;
2132
+ }
2133
+ (_a = n.children) == null ? void 0 : _a.forEach(walkAssign);
2134
+ };
2135
+ walkAssign(root);
2136
+ const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
2137
+ const newId = oldToNew.get(oldId);
2138
+ return newId ? "url(#" + newId + ")" : m;
2139
+ });
2140
+ const walkRewrite = (n) => {
2141
+ var _a;
2142
+ if (typeof n.href === "string" && n.href.startsWith("#")) {
2143
+ const newId = oldToNew.get(n.href.slice(1));
2144
+ if (newId) n.href = "#" + newId;
2145
+ }
2146
+ for (const k of Object.keys(n)) {
2147
+ if (k === "children" || k === "effects" || k === "meta" || k === "href" || k === "id") continue;
2148
+ const v = n[k];
2149
+ if (typeof v === "string" && v.indexOf("url(#") !== -1) n[k] = rewriteUrl(v);
2150
+ }
2151
+ (_a = n.children) == null ? void 0 : _a.forEach(walkRewrite);
2152
+ };
2153
+ walkRewrite(root);
2154
+ return oldToNew;
2155
+ }
2156
+ function applyMaskedByEffect(node, fx, transformation, ctx) {
2157
+ if (!fx) return node;
2158
+ if (!fx.href) {
2159
+ ctx.errors.push("maskedBy.href missing \u2014 cannot build mask");
2160
+ return node;
2161
+ }
2162
+ const maskId = genId(ctx, "mask");
2163
+ let content = { type: "use", href: "#" + fx.href };
2164
+ content = wrapInverseTransform(content, transformation, ctx);
2165
+ const mask = { type: "mask", id: maskId, children: [content] };
2166
+ if (fx.maskType) mask.maskType = fx.maskType;
2167
+ if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
2168
+ if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
2169
+ ctx.defs.push(mask);
2170
+ node.mask = "url(#" + maskId + ")";
2171
+ return node;
2172
+ }
2173
+ function wrapInverseTransform(inner, fx, ctx) {
2174
+ if (!fx) return inner;
2175
+ const origin = readStaticOrigin(fx.origin, ctx);
2176
+ let n = inner;
2177
+ n = wrapInversePart(n, "translate", fx.translate, void 0, ctx);
2178
+ n = wrapInversePart(n, "rotate", fx.rotate, origin, ctx);
2179
+ n = wrapInversePart(n, "scale", fx.scale, origin, ctx);
2180
+ return n;
2181
+ }
2182
+ function wrapInversePart(inner, part, raw, origin, ctx) {
2183
+ if (raw === void 0) return inner;
2184
+ const v = readAnimatable(raw);
2185
+ if (v.kind === "static") {
2186
+ return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
2187
+ }
2188
+ if (v.kind === "animated") {
2189
+ return {
2190
+ type: "g",
2191
+ animate: { transform: { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin))) } },
2192
+ children: [inner]
2193
+ };
2194
+ }
2195
+ return inner;
2196
+ }
2197
+ function invertPartValue(part, value) {
2198
+ if (part === "translate") return [-value[0], -value[1]];
2199
+ if (part === "rotate") return -value;
2200
+ return [1 / value[0], 1 / value[1]];
2201
+ }
2202
+ var CONTENT_SUBREF = "content";
2203
+ function applyRefAndTransformationEffect(node, ref, transformation, ctx) {
2204
+ if (ref) {
2205
+ const baseId = ref.baseId;
2206
+ if (!baseId) {
2207
+ ctx.errors.push("ref: missing baseId");
2208
+ } else {
2209
+ const targetId = ref.type === CONTENT_SUBREF ? ctx.contentRefInnerIds.get(baseId) || baseId : baseId;
2210
+ node.href = "#" + targetId;
2211
+ }
2212
+ }
2213
+ return applyTransformationEffect(node, transformation, ctx);
2214
+ }
2215
+ function applyRepeaterEffect(node, fx, ctx) {
2216
+ var _a, _b;
2217
+ if (!fx) return node;
2218
+ const copies = (_a = fx.copies) != null ? _a : 1;
2219
+ if (copies < 1) {
2220
+ ctx.errors.push("repeater.copies invalid: " + fx.copies);
2221
+ return node;
2222
+ }
2223
+ const sharedTransform = node.transform;
2224
+ const sharedAnimTransform = (_b = node.animate) == null ? void 0 : _b.transform;
2225
+ const base = clone(node);
2226
+ delete base.transform;
2227
+ if (base.animate) {
2228
+ delete base.animate.transform;
2229
+ if (Object.keys(base.animate).length === 0) delete base.animate;
2230
+ }
2231
+ const children = [base];
2232
+ for (let i = 1; i < copies; i++) {
2233
+ const copy = clone(base);
2234
+ copy.transform = { value: perCopyParts(fx, i) };
2235
+ children.push(copy);
2236
+ }
2237
+ const wrapper = { type: "g", children };
2238
+ if (sharedTransform !== void 0) wrapper.transform = sharedTransform;
2239
+ if (sharedAnimTransform !== void 0) wrapper.animate = { transform: sharedAnimTransform };
2240
+ return wrapper;
2241
+ }
2242
+ function perCopyParts(fx, i) {
2243
+ const parts = {};
2244
+ if (fx.translate) parts.translate = [fx.translate[0] * i, fx.translate[1] * i];
2245
+ if (fx.rotate !== void 0) parts.rotate = fx.rotate * i;
2246
+ if (fx.scale) parts.scale = [__pow(fx.scale[0] / 100, i), __pow(fx.scale[1] / 100, i)];
2247
+ if (fx.origin) parts.origin = [fx.origin[0] * i, fx.origin[1] * i];
2248
+ return parts;
2249
+ }
2250
+ var RETIME_AS_SYMBOL = true;
2251
+ function applyRetimeEffect(node, retime, ctx) {
2252
+ var _a, _b, _c, _d;
2253
+ if (!retime) return node;
2254
+ const baseId = retime.baseId;
2255
+ if (!baseId) {
2256
+ ctx.errors.push("retime: missing baseId");
2257
+ return node;
2258
+ }
2259
+ const target = ctx.idMap.get(baseId);
2260
+ if (!target) {
2261
+ ctx.warnings.push('retime: target "' + baseId + '" not found');
2262
+ return node;
2263
+ }
2264
+ const start = (_a = retime.start) != null ? _a : 0;
2265
+ const stretch = (_b = retime.stretch) != null ? _b : 1;
2266
+ if (RETIME_AS_SYMBOL) {
2267
+ const symbolClone = clone(target);
2268
+ regenerateIdsInClone(symbolClone, ctx);
2269
+ const cloneId = genId(ctx, "retime");
2270
+ symbolClone.id = cloneId;
2271
+ remapKeyframeTimes(symbolClone, start, stretch);
2272
+ ctx.defs.push(symbolClone);
2273
+ node.href = "#" + cloneId;
2274
+ return node;
2275
+ }
2276
+ const sourceNodes = target.type === "symbol" ? target.children || [] : [target];
2277
+ const content = sourceNodes.map((child) => {
2278
+ const c = clone(child);
2279
+ regenerateIdsInClone(c, ctx);
2280
+ remapKeyframeTimes(c, start, stretch);
2281
+ return c;
2282
+ });
2283
+ const g = __spreadProps(__spreadValues({}, node), { type: "g", children: content });
2284
+ delete g.href;
2285
+ const x = Number((_c = node.x) != null ? _c : 0), y = Number((_d = node.y) != null ? _d : 0);
2286
+ if (x || y) {
2287
+ const offset = "translate(" + x + "," + y + ")";
2288
+ g.transform = typeof g.transform === "string" ? offset + g.transform : offset;
2289
+ delete g.x;
2290
+ delete g.y;
2291
+ }
2292
+ return g;
2293
+ }
2294
+ function remapKeyframeTimes(node, start, stretch) {
2295
+ var _a;
2296
+ const remap2 = (kfs) => {
2297
+ for (const kf of kfs) if (typeof kf.time === "number") kf.time = start + kf.time * stretch;
2298
+ };
2299
+ if (node.transform && typeof node.transform === "object" && Array.isArray(node.transform.keyframes)) {
2300
+ remap2(node.transform.keyframes);
2301
+ }
2302
+ if (node.animate && typeof node.animate === "object") {
2303
+ for (const prop of Object.keys(node.animate)) {
2304
+ const anim = node.animate[prop];
2305
+ if (anim && Array.isArray(anim.keyframes)) remap2(anim.keyframes);
2306
+ }
2307
+ }
2308
+ (_a = node.children) == null ? void 0 : _a.forEach((child) => remapKeyframeTimes(child, start, stretch));
2309
+ }
2310
+ function applyTrimPathEffect(node, trimPath, isCombinedShape) {
2311
+ var _a;
2312
+ if (!trimPath && !isCombinedShape) return node;
2313
+ const d = (_a = node.d) != null ? _a : rectToPathD(node);
2314
+ if (d === void 0) return node;
2315
+ const g = __spreadProps(__spreadValues({}, node), { type: "g" });
2316
+ delete g.d;
2317
+ const meta = g.meta = __spreadValues({}, g.meta || {});
2318
+ const effects = meta.effects = __spreadValues({}, meta.effects || {});
2319
+ if (trimPath) effects.trimPath = trimPath;
2320
+ effects.isCombinedShape = true;
2321
+ g.children = [{ type: "path", d }];
2322
+ return g;
2323
+ }
2324
+ function rectToPathD(node) {
2325
+ var _a, _b, _c, _d;
2326
+ if (node.type !== "rect") return void 0;
2327
+ const x = Number((_a = node.x) != null ? _a : 0), y = Number((_b = node.y) != null ? _b : 0);
2328
+ const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
2329
+ return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
2330
+ }
2331
+ function applyPlayerEffects(root) {
2332
+ const ctx = {
2333
+ defs: [],
2334
+ warnings: [],
2335
+ errors: [],
2336
+ idMap: /* @__PURE__ */ new Map(),
2337
+ nextId: 0,
2338
+ contentRefInnerIds: /* @__PURE__ */ new Map()
2339
+ };
2340
+ const working = clone(root);
2341
+ indexById(working, ctx.idMap);
2342
+ identifyContentRefTargets(working, ctx, () => genId(ctx, "inner"));
2343
+ const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);
2344
+ const out = applyPlayerEffects_retime(afterPass1, ctx);
2345
+ spliceDefs(out, ctx.defs);
2346
+ return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
2347
+ }
2348
+ function applyPlayerEffects_exceptRetime(node, ctx) {
2349
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
2350
+ const fx = node.effects;
2351
+ const originalId = typeof node.id === "string" ? node.id : void 0;
2352
+ const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
2353
+ if (!fx && !innerIdForContentRef) return node;
2354
+ const { transformation, repeater, maskedBy, trimPath, retime, ref } = fx != null ? fx : {};
2355
+ const isCombinedShape = fx == null ? void 0 : fx.isCombinedShape;
2356
+ if (fx) delete node.effects;
2357
+ let n = node;
2358
+ n = applyTrimPathEffect(n, trimPath, isCombinedShape);
2359
+ n = applyRepeaterEffect(n, repeater, ctx);
2360
+ n = applyMaskedByEffect(n, maskedBy, transformation, ctx);
2361
+ if (innerIdForContentRef) {
2362
+ n = splitForContentRef(n, transformation, originalId, innerIdForContentRef, ctx);
2363
+ } else {
2364
+ n = applyRefAndTransformationEffect(n, ref, transformation, ctx);
2365
+ }
2366
+ if (retime) node.effects = { retime };
2367
+ if (originalId) ctx.idMap.set(originalId, n);
2368
+ return n;
2369
+ }
2370
+ function applyPlayerEffects_retime(node, ctx) {
2371
+ var _a;
2372
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_retime(child, ctx));
2373
+ const retime = (_a = node.effects) == null ? void 0 : _a.retime;
2374
+ if (!retime) return node;
2375
+ delete node.effects;
2376
+ return applyRetimeEffect(node, retime, ctx);
2377
+ }
2378
+ function pathStr(path) {
2379
+ if (!path.length) return ".";
2380
+ let result = "";
2381
+ for (const seg of path) {
2382
+ if (seg.startsWith("[")) result += seg;
2383
+ else result += (result ? "." : "") + seg;
2384
+ }
2385
+ return result;
2386
+ }
2387
+ var Base = class {
2388
+ _canSanitize(raw) {
2389
+ return this.isValid(raw);
2390
+ }
2391
+ optional() {
2392
+ return new Optional(this);
2393
+ }
2394
+ };
2395
+ var Optional = class extends Base {
2396
+ constructor(inner) {
2397
+ super();
2398
+ this.inner = inner;
2399
+ this._default = void 0;
2400
+ }
2401
+ sanitize(raw) {
2402
+ if (raw === void 0 || raw === null) return void 0;
2403
+ return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
2404
+ }
2405
+ isValid(raw, ctx, path) {
2406
+ if (raw === void 0 || raw === null) return true;
2407
+ return this.inner.isValid(raw, ctx, path);
2408
+ }
2409
+ _canSanitize(raw) {
2410
+ return raw === void 0 || raw === null || this.inner._canSanitize(raw);
2411
+ }
2412
+ };
2413
+ var Str = class extends Base {
2414
+ constructor(_default = "") {
2415
+ super();
2416
+ this._default = _default;
2417
+ }
2418
+ sanitize(raw) {
2419
+ return typeof raw === "string" ? raw : this._default;
2420
+ }
2421
+ isValid(raw, ctx, path) {
2422
+ if (typeof raw === "string") return true;
2423
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected string, got " + typeof raw);
2424
+ return false;
2425
+ }
2426
+ };
2427
+ var Num = class extends Base {
2428
+ constructor(_default = 0) {
2429
+ super();
2430
+ this._default = _default;
2431
+ }
2432
+ sanitize(raw) {
2433
+ return typeof raw === "number" && isFinite(raw) ? raw : this._default;
2434
+ }
2435
+ isValid(raw, ctx, path) {
2436
+ if (typeof raw === "number" && isFinite(raw)) return true;
2437
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected finite number, got " + JSON.stringify(raw));
2438
+ return false;
2439
+ }
2440
+ };
2441
+ var Bool = class extends Base {
2442
+ constructor(_default = false) {
2443
+ super();
2444
+ this._default = _default;
2445
+ }
2446
+ sanitize(raw) {
2447
+ return typeof raw === "boolean" ? raw : this._default;
2448
+ }
2449
+ isValid(raw, ctx, path) {
2450
+ if (typeof raw === "boolean") return true;
2451
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected boolean, got " + typeof raw);
2452
+ return false;
2453
+ }
2454
+ };
2455
+ var Literal = class extends Base {
2456
+ constructor(value) {
2457
+ super();
2458
+ this.value = value;
2459
+ this._default = value;
2460
+ }
2461
+ sanitize(raw) {
2462
+ return raw === this.value ? this.value : this._default;
2463
+ }
2464
+ isValid(raw, ctx, path) {
2465
+ if (raw === this.value) return true;
2466
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected " + JSON.stringify(this.value) + ", got " + JSON.stringify(raw));
2467
+ return false;
2468
+ }
2469
+ };
2470
+ var Enum = class extends Base {
2471
+ constructor(values, defaultVal) {
2472
+ super();
2473
+ this.values = values;
2474
+ this._default = defaultVal != null ? defaultVal : values[0];
2475
+ }
2476
+ sanitize(raw) {
2477
+ return this.values.includes(raw) ? raw : this._default;
2478
+ }
2479
+ isValid(raw, ctx, path) {
2480
+ if (this.values.includes(raw)) return true;
2481
+ 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));
2482
+ return false;
2483
+ }
2484
+ };
2485
+ var Union = class extends Base {
2486
+ constructor(schemas, defaultVal) {
2487
+ super();
2488
+ this.schemas = schemas;
2489
+ this._default = defaultVal != null ? defaultVal : schemas[0]._default;
2490
+ }
2491
+ sanitize(raw) {
2492
+ for (const s of this.schemas) {
2493
+ if (s.isValid(raw)) return s.sanitize(raw);
2494
+ }
2495
+ return this._default;
2496
+ }
2497
+ isValid(raw, ctx, path) {
2498
+ var _a;
2499
+ if (this.schemas.some((s) => s.isValid(raw))) return true;
2500
+ 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));
2501
+ return false;
2502
+ }
2503
+ _canSanitize(raw) {
2504
+ return this.schemas.some((s) => s._canSanitize(raw));
2505
+ }
2506
+ };
2507
+ var DiscriminatedUnion = class extends Base {
2508
+ constructor(_key, _schemas, defaultVal) {
2509
+ super();
2510
+ this._key = _key;
2511
+ this._schemas = _schemas;
2512
+ this._default = defaultVal != null ? defaultVal : _schemas[0]._default;
2513
+ this._map = /* @__PURE__ */ new Map();
2514
+ for (const s of _schemas) {
2515
+ const keySchema = s._shape[_key];
2516
+ if (keySchema) this._map.set(keySchema._default, s);
2517
+ }
2518
+ }
2519
+ _findSchema(raw) {
2520
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
2521
+ const val = raw[this._key];
2522
+ if (val === void 0 || val === null) return void 0;
2523
+ return this._map.get(val);
2524
+ }
2525
+ sanitize(raw) {
2526
+ var _a;
2527
+ return ((_a = this._findSchema(raw)) != null ? _a : this._schemas[0]).sanitize(raw);
2528
+ }
2529
+ isValid(raw, ctx, path) {
2530
+ const schema = this._findSchema(raw);
2531
+ if (!schema) {
2532
+ const val = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw[this._key] : void 0;
2533
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": no discriminated union member matched " + this._key + "=" + JSON.stringify(val));
2534
+ return false;
2535
+ }
2536
+ return schema.isValid(raw, ctx, path);
2537
+ }
2538
+ _canSanitize(raw) {
2539
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return false;
2540
+ const schema = this._findSchema(raw);
2541
+ return schema ? schema._canSanitize(raw) : this._schemas[0]._canSanitize(raw);
2542
+ }
2543
+ };
2544
+ var Obj = class extends Base {
2545
+ constructor(_shape) {
2546
+ super();
2547
+ this._shape = _shape;
2548
+ const d = {};
2549
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
2550
+ this._default = d;
2551
+ }
2552
+ sanitize(raw) {
2553
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
2554
+ const out = {};
2555
+ for (const key of Object.keys(this._shape)) {
2556
+ out[key] = this._shape[key].sanitize(src[key]);
2557
+ }
2558
+ return out;
2559
+ }
2560
+ isValid(raw, ctx, path) {
2561
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
2562
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
2563
+ return false;
2564
+ }
2565
+ const obj = raw;
2566
+ const p = path != null ? path : [];
2567
+ let ok = true;
2568
+ for (const key of Object.keys(this._shape)) {
2569
+ p.push(key);
2570
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
2571
+ p.pop();
2572
+ }
2573
+ if (ctx == null ? void 0 : ctx.strict) {
2574
+ for (const key of Object.keys(obj)) {
2575
+ if (key in this._shape) continue;
2576
+ p.push(key);
2577
+ ctx.errors.push(pathStr(p) + ": unexpected extra key");
2578
+ p.pop();
2579
+ ok = false;
2580
+ }
2581
+ }
2582
+ return ok;
2583
+ }
2584
+ _canSanitize(raw) {
2585
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
2586
+ }
2587
+ };
2588
+ var OpenObj = class extends Base {
2589
+ constructor(_shape, _openSchema) {
2590
+ super();
2591
+ this._shape = _shape;
2592
+ this._openSchema = _openSchema;
2593
+ const d = {};
2594
+ for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
2595
+ this._default = d;
2596
+ }
2597
+ sanitize(raw) {
2598
+ const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
2599
+ const out = __spreadValues({}, src);
2600
+ for (const key of Object.keys(this._shape)) {
2601
+ out[key] = this._shape[key].sanitize(src[key]);
2602
+ }
2603
+ if (this._openSchema) {
2604
+ for (const key of Object.keys(src)) {
2605
+ if (!(key in this._shape)) out[key] = this._openSchema.sanitize(src[key]);
2606
+ }
2607
+ }
2608
+ return out;
2609
+ }
2610
+ isValid(raw, ctx, path) {
2611
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
2612
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object, got " + (Array.isArray(raw) ? "array" : typeof raw));
2613
+ return false;
2614
+ }
2615
+ const obj = raw;
2616
+ const p = path != null ? path : [];
2617
+ let ok = true;
2618
+ for (const key of Object.keys(this._shape)) {
2619
+ p.push(key);
2620
+ if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;
2621
+ p.pop();
2622
+ }
2623
+ if (this._openSchema) {
2624
+ for (const key of Object.keys(obj)) {
2625
+ if (key in this._shape) continue;
2626
+ p.push(key);
2627
+ if (!this._openSchema.isValid(obj[key], ctx, p)) ok = false;
2628
+ p.pop();
2629
+ }
2630
+ }
2631
+ return ok;
2632
+ }
2633
+ _canSanitize(raw) {
2634
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
2635
+ }
2636
+ };
2637
+ var Arr = class extends Base {
2638
+ constructor(item) {
2639
+ super();
2640
+ this.item = item;
2641
+ this._default = [];
2642
+ }
2643
+ sanitize(raw) {
2644
+ if (!Array.isArray(raw)) return [];
2645
+ const out = [];
2646
+ for (const el of raw) {
2647
+ if (this.item._canSanitize(el)) out.push(this.item.sanitize(el));
2648
+ }
2649
+ return out;
2650
+ }
2651
+ isValid(raw, ctx, path) {
2652
+ if (!Array.isArray(raw)) {
2653
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected array, got " + typeof raw);
2654
+ return false;
2655
+ }
2656
+ const p = path != null ? path : [];
2657
+ let ok = true;
2658
+ for (let i = 0; i < raw.length; i++) {
2659
+ p.push("[" + i + "]");
2660
+ if (!this.item.isValid(raw[i], ctx, p)) ok = false;
2661
+ p.pop();
2662
+ }
2663
+ return ok;
2664
+ }
2665
+ _canSanitize(raw) {
2666
+ return Array.isArray(raw);
2667
+ }
2668
+ };
2669
+ var Rec = class extends Base {
2670
+ constructor(value) {
2671
+ super();
2672
+ this.value = value;
2673
+ this._default = {};
2674
+ }
2675
+ sanitize(raw) {
2676
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
2677
+ const out = {};
2678
+ for (const [k, v] of Object.entries(raw)) {
2679
+ if (this.value._canSanitize(v)) out[k] = this.value.sanitize(v);
2680
+ }
2681
+ return out;
2682
+ }
2683
+ isValid(raw, ctx, path) {
2684
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
2685
+ ctx == null ? void 0 : ctx.errors.push(pathStr(path != null ? path : []) + ": expected object/record, got " + (Array.isArray(raw) ? "array" : typeof raw));
2686
+ return false;
2687
+ }
2688
+ const p = path != null ? path : [];
2689
+ let ok = true;
2690
+ for (const [k, v] of Object.entries(raw)) {
2691
+ p.push(k);
2692
+ if (!this.value.isValid(v, ctx, p)) ok = false;
2693
+ p.pop();
2694
+ }
2695
+ return ok;
2696
+ }
2697
+ _canSanitize(raw) {
2698
+ return !!raw && typeof raw === "object" && !Array.isArray(raw);
2699
+ }
2700
+ };
2701
+ var Any = class extends Base {
2702
+ constructor() {
2703
+ super(...arguments);
2704
+ this._default = void 0;
2705
+ }
2706
+ sanitize(raw) {
2707
+ return raw;
2708
+ }
2709
+ isValid(_raw, _ctx, _path) {
2710
+ return true;
2711
+ }
2712
+ _canSanitize(_raw) {
2713
+ return true;
2714
+ }
2715
+ };
2716
+ var Lazy = class extends Base {
2717
+ constructor(fn, _default) {
2718
+ super();
2719
+ this.fn = fn;
2720
+ this._default = _default;
2721
+ this.resolved = null;
2722
+ }
2723
+ get schema() {
2724
+ var _a;
2725
+ return (_a = this.resolved) != null ? _a : this.resolved = this.fn();
2726
+ }
2727
+ sanitize(raw) {
2728
+ return this.schema.sanitize(raw);
2729
+ }
2730
+ isValid(raw, ctx, path) {
2731
+ return this.schema.isValid(raw, ctx, path);
2732
+ }
2733
+ _canSanitize(raw) {
2734
+ return this.schema._canSanitize(raw);
2735
+ }
2736
+ };
2737
+ var Tuple = class extends Base {
2738
+ constructor(schemas) {
2739
+ super();
2740
+ this.schemas = schemas;
2741
+ this._default = schemas.map((s) => s._default);
2742
+ }
2743
+ sanitize(raw) {
2744
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) return this._default;
2745
+ return this.schemas.map((s, i) => s.sanitize(raw[i]));
2746
+ }
2747
+ isValid(raw, ctx, path) {
2748
+ if (!Array.isArray(raw) || raw.length !== this.schemas.length) {
2749
+ 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));
2750
+ return false;
2751
+ }
2752
+ const p = path != null ? path : [];
2753
+ let ok = true;
2754
+ for (let i = 0; i < this.schemas.length; i++) {
2755
+ p.push("[" + i + "]");
2756
+ if (!this.schemas[i].isValid(raw[i], ctx, p)) ok = false;
2757
+ p.pop();
2758
+ }
2759
+ return ok;
2760
+ }
2761
+ // Require exact length so wrong-length arrays are dropped rather than repaired to default.
2762
+ _canSanitize(raw) {
2763
+ return Array.isArray(raw) && raw.length === this.schemas.length;
2764
+ }
2765
+ };
2766
+ function implementsInterface() {
2767
+ return (schema) => schema;
2768
+ }
2769
+ var px = {
2770
+ /** Matches a string. Default: '' or provided value. */
2771
+ string: (defaultVal = "") => new Str(defaultVal),
2772
+ /** Matches a finite number. Default: 0 or provided value. */
2773
+ number: (defaultVal = 0) => new Num(defaultVal),
2774
+ /** Matches a boolean. Default: false or provided value. */
2775
+ boolean: (defaultVal = false) => new Bool(defaultVal),
2776
+ /** Matches one exact primitive value; its default is the value itself. */
2777
+ literal: (value) => new Literal(value),
2778
+ /** Matches one of a fixed set of string/number values. Default: first value. */
2779
+ enum: (values, defaultVal) => new Enum(values, defaultVal),
2780
+ /**
2781
+ * Returns the first schema whose isValid passes.
2782
+ * TypeScript infers the union of all member types automatically.
2783
+ */
2784
+ union: (schemas, defaultVal) => new Union(schemas, defaultVal),
2785
+ /**
2786
+ * Discriminated union — reads `raw[key]`, finds the member schema whose
2787
+ * literal at `key` matches, then delegates sanitize/isValid to that member.
2788
+ * Each member must be an object schema with a `px.literal(...)` at `key`.
2789
+ * TypeScript infers the union of all member types automatically.
2790
+ */
2791
+ discriminatedUnion: (key, schemas) => new DiscriminatedUnion(key, schemas),
2792
+ /** Typed object — unknown keys are stripped. Required fields fall back to their default. */
2793
+ object: (shape) => new Obj(shape),
2794
+ /**
2795
+ * Open object — validates known keys; passes unknown keys through as-is,
2796
+ * or validates/sanitizes them against `openSchema` when provided.
2797
+ */
2798
+ openObject: (shape, openSchema) => new OpenObj(shape, openSchema),
2799
+ /**
2800
+ * Creates a new closed object schema by merging a base schema's shape with additional fields.
2801
+ * The base can be the result of px.object() or px.openObject() — anything with a _shape property.
2802
+ *
2803
+ * @example
2804
+ * const PxSvgNodeSchema = px.extendedObject(PxNodeBase, { width: px.number().optional() });
2805
+ */
2806
+ extendedObject: (base, extra) => new Obj(__spreadValues(__spreadValues({}, base._shape), extra)),
2807
+ /** Array whose unrecoverable items are filtered out. Default: []. */
2808
+ array: (item) => new Arr(item),
2809
+ /** String-keyed record whose unrecoverable values are dropped. Default: {}. */
2810
+ record: (value) => new Rec(value),
2811
+ /** Passes anything through unchanged — always valid. */
2812
+ any: () => new Any(),
2813
+ /** Fixed-length tuple — validates element count and each position individually. */
2814
+ tuple: (schemas) => new Tuple(schemas),
2815
+ /** Defers schema creation — required for recursive types. Must supply a default value. */
2816
+ lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
2817
+ };
2818
+ var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
2819
+ var PX_ANIM_ATTR_NAME = "_px_animator";
2820
+ var TEXT_ATTR = "text";
2821
+ var TEXT_CONTENT_ATTR = "textContent";
2822
+ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
2823
+ "type",
2824
+ "children",
2825
+ "animator",
2826
+ "meta",
2827
+ "animate",
2828
+ TEXT_ATTR,
2829
+ TEXT_CONTENT_ATTR
2830
+ ]);
2831
+ var PxEasingOrRefSchema = px.union([
2832
+ px.string(),
2833
+ px.tuple([px.number(), px.number(), px.number(), px.number()])
2834
+ ]);
2835
+ var PxKeyframeValueSchema = implementsInterface()(px.union([
2836
+ px.string(),
2837
+ // e.g. for colors
2838
+ px.number(),
2839
+ px.array(px.number()),
2840
+ px.lazy(() => PxTransformPartsSchema, {}),
2841
+ px.object({ path: px.string() }),
2842
+ px.lazy(() => px.object({ paths: px.array(PxBezierPathSchema) }), { paths: [] })
2843
+ ]));
2844
+ var PxKeyframeSchema = implementsInterface()(px.object({
2845
+ time: px.number().optional(),
2846
+ t: px.number().optional(),
2847
+ value: px.any().optional(),
2848
+ v: px.any().optional(),
2849
+ easing: PxEasingOrRefSchema.optional(),
2850
+ e: PxEasingOrRefSchema.optional(),
2851
+ tangentOut: px.tuple([px.number(), px.number()]).optional(),
2852
+ to: px.tuple([px.number(), px.number()]).optional(),
2853
+ // short alias
2854
+ tangentIn: px.tuple([px.number(), px.number()]).optional(),
2855
+ ti: px.tuple([px.number(), px.number()]).optional(),
2856
+ // short alias
2857
+ selected: px.boolean().optional()
2858
+ // editor-side UI state (Player ignores it)
2859
+ }));
2860
+ var PxLoopSchema = implementsInterface()(px.object({
2861
+ segmentCount: px.number().optional(),
2862
+ before: px.boolean().optional(),
2863
+ alternate: px.boolean().optional()
2864
+ }));
2865
+ var PxPropertyAnimationSchema = implementsInterface()(px.object({
2866
+ keyframes: px.array(PxKeyframeSchema).optional(),
2867
+ kfs: px.array(PxKeyframeSchema).optional(),
2868
+ loop: px.union([PxLoopSchema, px.boolean()]).optional(),
2869
+ autoOrient: px.boolean().optional()
2870
+ }));
2871
+ var PxTransformPartsSchema = implementsInterface()(px.object({
2872
+ translate: px.tuple([px.number(), px.number()]).optional(),
2873
+ rotate: px.number().optional(),
2874
+ scale: px.tuple([px.number(), px.number()]).optional(),
2875
+ origin: px.tuple([px.number(), px.number()]).optional()
2876
+ }));
2877
+ var PxTransformValueSchema = px.union([
2878
+ px.string(),
2879
+ px.object({ value: PxTransformPartsSchema }),
2880
+ PxPropertyAnimationSchema
2881
+ ]);
2882
+ var PxAnimationDefinitionSchema = implementsInterface()(
2883
+ px.record(PxPropertyAnimationSchema)
2884
+ );
2885
+ var PxElementAnimationSchema = implementsInterface()(px.union([
2886
+ px.string(),
2887
+ px.array(px.union([px.string(), PxAnimationDefinitionSchema])),
2888
+ PxAnimationDefinitionSchema
2889
+ ]));
2890
+ var PxTriggerSchema = implementsInterface()(px.object({
2891
+ startOn: px.enum(["load", "mouseOver", "click", "scrollIntoView", "programmatic"]).optional(),
2892
+ outAction: px.enum(["continue", "pause", "reset", "reverse"]).optional(),
2893
+ scrollIntoViewThreshold: px.number().optional()
2894
+ }));
2895
+ var PxDefsSchema = implementsInterface()(px.object({
2896
+ easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()])).optional(),
2897
+ animations: px.record(PxAnimationDefinitionSchema).optional(),
2898
+ styles: px.record(px.any()).optional()
2899
+ }));
2900
+ var PxAnimatorConfigSchema = implementsInterface()(px.object({
2901
+ mode: px.enum(["auto", "webapi", "frames"]).optional(),
2902
+ duration: px.number().optional(),
2903
+ delay: px.number().optional(),
2904
+ iterations: px.union([px.number(), px.literal("infinite")]).optional(),
2905
+ fill: px.enum(["forwards", "backwards", "both", "none"]).optional(),
2906
+ direction: px.enum(["normal", "reverse", "alternate", "alternate-reverse"]).optional(),
2907
+ frameRate: px.number().optional(),
2908
+ trigger: PxTriggerSchema.optional(),
2909
+ definitions: PxDefsSchema.optional(),
2910
+ animate: px.record(PxElementAnimationSchema).optional(),
2911
+ debug: px.boolean().optional(),
2912
+ debugInstName: px.string().optional()
2913
+ }));
2914
+ var PxBindingSchema = implementsInterface()(px.object({
2915
+ id: px.string(),
2916
+ animate: PxElementAnimationSchema
2917
+ }));
2918
+ var PxAttrValueSchema = px.union([
2919
+ px.string(),
2920
+ px.number(),
2921
+ px.object({ value: px.any() }),
2922
+ PxPropertyAnimationSchema
2923
+ ]);
2924
+ var PxAnimatableNumberSchema = px.union([
2925
+ px.number(),
2926
+ px.object({ value: px.number() }),
2927
+ px.object({
2928
+ keyframes: px.array(PxKeyframeSchema),
2929
+ autoOrient: px.boolean().optional()
2930
+ })
2931
+ ]);
2932
+ var PxAnimatableVec2Schema = px.union([
2933
+ px.tuple([px.number(), px.number()]),
2934
+ px.object({ value: px.tuple([px.number(), px.number()]) }),
2935
+ px.object({
2936
+ keyframes: px.array(PxKeyframeSchema),
2937
+ autoOrient: px.boolean().optional()
2938
+ })
2939
+ ]);
2940
+ var PxTransformationEffectSchema = px.object({
2941
+ translate: PxAnimatableVec2Schema.optional(),
2942
+ rotate: PxAnimatableNumberSchema.optional(),
2943
+ scale: PxAnimatableVec2Schema.optional(),
2944
+ skew: PxAnimatableVec2Schema.optional(),
2945
+ origin: PxAnimatableVec2Schema.optional()
2946
+ });
2947
+ var PxRepeaterEffectSchema = px.object({
2948
+ copies: px.number().optional(),
2949
+ translate: px.tuple([px.number(), px.number()]).optional(),
2950
+ rotate: px.number().optional(),
2951
+ scale: px.tuple([px.number(), px.number()]).optional(),
2952
+ // per-copy scale, PERCENT (85 → 0.85)
2953
+ origin: px.tuple([px.number(), px.number()]).optional()
2954
+ });
2955
+ var PxMaskedByEffectSchema = px.object({
2956
+ href: px.string().optional(),
2957
+ maskType: px.string().optional(),
2958
+ maskUnits: px.string().optional(),
2959
+ maskContentUnits: px.string().optional()
2960
+ });
2961
+ var PxTrimPathEffectSchema = px.any();
2962
+ var PxRetimeEffectSchema = px.object({
2963
+ baseId: px.string().optional(),
2964
+ start: px.number().optional(),
2965
+ stretch: px.number().optional(),
2966
+ timeCrop: px.tuple([px.number(), px.number()]).optional()
2967
+ });
2968
+ var PxRefEffectSchema = px.object({
2969
+ baseId: px.string().optional(),
2970
+ type: px.string().optional()
2971
+ });
2972
+ var PxEffectsSchema = px.object({
2973
+ transformation: PxTransformationEffectSchema.optional(),
2974
+ repeater: PxRepeaterEffectSchema.optional(),
2975
+ maskedBy: PxMaskedByEffectSchema.optional(),
2976
+ trimPath: PxTrimPathEffectSchema.optional(),
2977
+ retime: PxRetimeEffectSchema.optional(),
2978
+ isCombinedShape: px.boolean().optional(),
2979
+ ref: PxRefEffectSchema.optional()
2980
+ });
2981
+ function validateNodeEffects(root, opts) {
2982
+ const warnings = [];
2983
+ const walk = (node, path) => {
2984
+ if (node && node.effects) {
2985
+ const ctx = { errors: [], warnings: [], strict: !!(opts == null ? void 0 : opts.strict) };
2986
+ const ok = PxEffectsSchema.isValid(node.effects, ctx, [path + ".effects"]);
2987
+ if (!ok) {
2988
+ for (const err of ctx.errors) warnings.push(err);
2989
+ }
2990
+ }
2991
+ if (node && Array.isArray(node.children)) {
2992
+ node.children.forEach((c, i) => walk(c, path + ".children[" + i + "]"));
2993
+ }
2994
+ };
2995
+ walk(root, "root");
2996
+ return warnings;
2997
+ }
2998
+ var PxNodeBase = px.openObject({
2999
+ type: px.string(),
3000
+ id: px.string().optional(),
3001
+ meta: px.any().optional(),
3002
+ // Player-effects bucket emitted by the Editor's lightweight design format.
3003
+ // Consumed and removed by `applyPlayerEffects` before any other normalisation
3004
+ // (see `createAnimatorImpl`), so downstream code never sees it.
3005
+ effects: PxEffectsSchema.optional(),
3006
+ animate: PxAnimationDefinitionSchema.optional(),
3007
+ style: px.union([px.string(), px.record(px.union([px.string(), px.number()]))]).optional()
3008
+ }, PxAttrValueSchema);
3009
+ var PxNodeSchema = px.openObject(__spreadProps(__spreadValues({}, PxNodeBase._shape), {
3010
+ children: px.lazy(() => px.array(PxNodeSchema), []).optional()
3011
+ }), PxAttrValueSchema);
3012
+ var PxSvgNodeExtra = px.object({
3013
+ width: px.number().optional(),
3014
+ height: px.number().optional(),
3015
+ viewBox: px.string().optional(),
3016
+ animator: PxAnimatorConfigSchema.optional()
3017
+ });
3018
+ var PxAnimatedSvgDocumentSchema = px.openObject(__spreadProps(__spreadValues(__spreadValues({}, PxNodeBase._shape), PxSvgNodeExtra._shape), {
3019
+ type: px.literal("svg"),
3020
+ // override string → literal to require 'svg'
3021
+ children: px.array(PxNodeSchema).optional()
3022
+ }), PxAttrValueSchema);
3023
+ var PxBezierPathSchema = implementsInterface()(px.object({
3024
+ v: px.array(px.array(px.number())),
3025
+ i: px.array(px.array(px.number())).optional(),
3026
+ o: px.array(px.array(px.number())).optional(),
3027
+ c: px.boolean().optional()
3028
+ }));
3029
+ function isPxElementFileFormat(fileJson) {
3030
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
3031
+ return false;
3032
+ }
3033
+ return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
3034
+ }
3035
+ function getAnimatorConfig(doc) {
3036
+ var _a, _b;
3037
+ return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
3038
+ }
3039
+ function getDefs(doc) {
3040
+ var _a;
3041
+ if (!doc) return void 0;
3042
+ return (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.definitions;
3043
+ }
3044
+ function getBindings(doc) {
3045
+ var _a;
3046
+ if (!doc) return void 0;
3047
+ const animate = (_a = getAnimatorConfig(doc)) == null ? void 0 : _a.animate;
3048
+ if (!animate) return void 0;
3049
+ return Object.entries(animate).map(([id, anim]) => ({ id, animate: anim }));
3050
+ }
1797
3051
  function bezierToSvgPath(path) {
1798
3052
  var _a, _b, _c, _d;
1799
3053
  const v = path.v;
@@ -1882,50 +3136,104 @@ var PixodeskAnimatorReact = (() => {
1882
3136
  const t = (value - inMin) / (inMax - inMin);
1883
3137
  return outMin + t * (outMax - outMin);
1884
3138
  }
1885
- function cubicBezier(easing) {
1886
- const [p1x, p1y, p2x, p2y] = easing;
3139
+ function solveCubicBezierX(p1x, p2x, x) {
3140
+ if (x <= 0) return 0;
3141
+ if (x >= 1) return 1;
1887
3142
  const cx = 3 * p1x;
1888
3143
  const bx = 3 * (p2x - p1x) - cx;
1889
3144
  const ax = 1 - cx - bx;
3145
+ function sampleX(t) {
3146
+ return ((ax * t + bx) * t + cx) * t;
3147
+ }
3148
+ function sampleDX(t) {
3149
+ return (3 * ax * t + 2 * bx) * t + cx;
3150
+ }
3151
+ let t2 = x;
3152
+ let t0 = 0;
3153
+ let t1 = 1;
3154
+ for (let i = 0; i < 8; i++) {
3155
+ const x2 = sampleX(t2) - x;
3156
+ if (Math.abs(x2) < 1e-6) return t2;
3157
+ const d2 = sampleDX(t2);
3158
+ if (Math.abs(d2) < 1e-6) break;
3159
+ t2 -= x2 / d2;
3160
+ }
3161
+ t2 = x;
3162
+ while (t0 < t1) {
3163
+ const x2 = sampleX(t2);
3164
+ if (Math.abs(x2 - x) < 1e-6) return t2;
3165
+ if (x > x2) t0 = t2;
3166
+ else t1 = t2;
3167
+ t2 = (t1 + t0) / 2;
3168
+ }
3169
+ return t2;
3170
+ }
3171
+ function cubicBezier(easing) {
3172
+ const [p1x, p1y, p2x, p2y] = easing;
1890
3173
  const cy = 3 * p1y;
1891
3174
  const by = 3 * (p2y - p1y) - cy;
1892
3175
  const ay = 1 - cy - by;
1893
- function sampleCurveX(t) {
1894
- return ((ax * t + bx) * t + cx) * t;
1895
- }
1896
3176
  function sampleCurveY(t) {
1897
3177
  return ((ay * t + by) * t + cy) * t;
1898
3178
  }
1899
- function sampleCurveDerivativeX(t) {
1900
- return (3 * ax * t + 2 * bx) * t + cx;
1901
- }
1902
- function solveCurveX(x) {
1903
- if (x <= 0) return 0;
1904
- if (x >= 1) return 1;
1905
- let t2 = x;
1906
- let t0 = 0;
1907
- let t1 = 1;
1908
- for (let i = 0; i < 8; i++) {
1909
- const x2 = sampleCurveX(t2) - x;
1910
- if (Math.abs(x2) < 1e-6) return t2;
1911
- const d2 = sampleCurveDerivativeX(t2);
1912
- if (Math.abs(d2) < 1e-6) break;
1913
- t2 -= x2 / d2;
1914
- }
1915
- t2 = x;
1916
- while (t0 < t1) {
1917
- const x2 = sampleCurveX(t2);
1918
- if (Math.abs(x2 - x) < 1e-6) return t2;
1919
- if (x > x2) t0 = t2;
1920
- else t1 = t2;
1921
- t2 = (t1 + t0) / 2;
1922
- }
1923
- return t2;
1924
- }
1925
3179
  return function(x) {
1926
- return sampleCurveY(solveCurveX(x));
3180
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
3181
+ };
3182
+ }
3183
+ function lerp2(a, b, t) {
3184
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
3185
+ }
3186
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
3187
+ const q0 = lerp2(p0, p1, t);
3188
+ const q1 = lerp2(p1, p2, t);
3189
+ const q2 = lerp2(p2, p3, t);
3190
+ const r0 = lerp2(q0, q1, t);
3191
+ const r1 = lerp2(q1, q2, t);
3192
+ const s = lerp2(r0, r1, t);
3193
+ return {
3194
+ left: [p0, q0, r0, s],
3195
+ right: [s, r1, q2, p3]
1927
3196
  };
1928
3197
  }
3198
+ function splitEasing(easing, xFraction) {
3199
+ if (!easing) return { left: void 0, right: void 0 };
3200
+ if (xFraction <= 0) return { left: void 0, right: easing };
3201
+ if (xFraction >= 1) return { left: easing, right: void 0 };
3202
+ const [x1, y1, x2, y2] = easing;
3203
+ const t = solveCubicBezierX(x1, x2, xFraction);
3204
+ const p0 = [0, 0];
3205
+ const p1 = [x1, y1];
3206
+ const p2 = [x2, y2];
3207
+ const p3 = [1, 1];
3208
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
3209
+ const sx = left[3][0];
3210
+ const sy = left[3][1];
3211
+ let leftEasing;
3212
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
3213
+ leftEasing = [
3214
+ left[1][0] / sx,
3215
+ left[1][1] / sy,
3216
+ left[2][0] / sx,
3217
+ left[2][1] / sy
3218
+ ];
3219
+ }
3220
+ let rightEasing;
3221
+ const rx = 1 - sx;
3222
+ const ry = 1 - sy;
3223
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
3224
+ rightEasing = [
3225
+ (right[1][0] - sx) / rx,
3226
+ (right[1][1] - sy) / ry,
3227
+ (right[2][0] - sx) / rx,
3228
+ (right[2][1] - sy) / ry
3229
+ ];
3230
+ }
3231
+ return { left: leftEasing, right: rightEasing };
3232
+ }
3233
+ function reverseEasing(easing) {
3234
+ if (!easing) return void 0;
3235
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
3236
+ }
1929
3237
  function toRGBA(color) {
1930
3238
  const r = Math.round(color[0] * 255);
1931
3239
  const g = Math.round(color[1] * 255);
@@ -1972,6 +3280,24 @@ var PixodeskAnimatorReact = (() => {
1972
3280
  var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
1973
3281
  var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
1974
3282
  var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
3283
+ function composeTransformParts(parts, opts) {
3284
+ var _a;
3285
+ if (!parts) return "";
3286
+ const withUnits = (_a = opts == null ? void 0 : opts.withUnits) != null ? _a : true;
3287
+ const segs = [];
3288
+ const t = parts.translate;
3289
+ const o = parts.origin;
3290
+ const r = parts.rotate;
3291
+ const s = parts.scale;
3292
+ const tu = withUnits ? "px" : "";
3293
+ const ru = withUnits ? "deg" : "";
3294
+ if (t) segs.push("translate(" + t[0] + tu + "," + t[1] + tu + ")");
3295
+ if (o) segs.push("translate(" + o[0] + tu + "," + o[1] + tu + ")");
3296
+ if (r !== void 0 && r !== null) segs.push("rotate(" + r + ru + ")");
3297
+ if (s) segs.push("scale(" + s[0] + "," + s[1] + ")");
3298
+ if (o) segs.push("translate(" + -o[0] + tu + "," + -o[1] + tu + ")");
3299
+ return segs.join("");
3300
+ }
1975
3301
  var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1976
3302
  var DEFAULT_DURATION_MS = 1e3;
1977
3303
  function kebabToCamelCaseWord(kebab) {
@@ -2031,12 +3357,258 @@ var PixodeskAnimatorReact = (() => {
2031
3357
  function clamp(value, min, max) {
2032
3358
  return Math.max(min, Math.min(value, max));
2033
3359
  }
3360
+ function bezier2D_pointAt(P0, P1, P2, P3, t) {
3361
+ if (t <= 0) return [P0[0], P0[1]];
3362
+ if (t >= 1) return [P3[0], P3[1]];
3363
+ const u = 1 - t;
3364
+ const u2 = u * u;
3365
+ const u3 = u2 * u;
3366
+ const t2 = t * t;
3367
+ const t3 = t2 * t;
3368
+ const w0 = u3;
3369
+ const w1 = 3 * t * u2;
3370
+ const w2 = 3 * t2 * u;
3371
+ const w3 = t3;
3372
+ return [
3373
+ w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],
3374
+ w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1]
3375
+ ];
3376
+ }
3377
+ var BEZIER_T_NUDGE = 1e-4;
3378
+ function bezier2D_derivativeAt(P0, P1, P2, P3, t) {
3379
+ const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);
3380
+ if (result[0] === 0 && result[1] === 0) {
3381
+ const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;
3382
+ return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);
3383
+ }
3384
+ return result;
3385
+ }
3386
+ function _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t) {
3387
+ const u = 1 - t;
3388
+ const a = 3 * u * u;
3389
+ const b = 6 * t * u;
3390
+ const c = 3 * t * t;
3391
+ return [
3392
+ a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),
3393
+ a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1])
3394
+ ];
3395
+ }
3396
+ function bezier2D_arcLengthLUT(P0, P1, P2, P3, steps = 100) {
3397
+ const n = steps + 1;
3398
+ const ts = new Float64Array(n);
3399
+ const ds = new Float64Array(n);
3400
+ let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);
3401
+ ts[0] = 0;
3402
+ ds[0] = 0;
3403
+ let cum = 0;
3404
+ for (let i = 1; i < n; i++) {
3405
+ const t = i / steps;
3406
+ const cur = bezier2D_pointAt(P0, P1, P2, P3, t);
3407
+ const dx = cur[0] - prev[0];
3408
+ const dy = cur[1] - prev[1];
3409
+ cum += Math.sqrt(dx * dx + dy * dy);
3410
+ ts[i] = t;
3411
+ ds[i] = cum;
3412
+ prev = cur;
3413
+ }
3414
+ return { ts, ds };
3415
+ }
3416
+ function bezier2D_tForDistance(lut, distance) {
3417
+ const { ts, ds } = lut;
3418
+ const last = ds.length - 1;
3419
+ if (distance <= 0) return ts[0];
3420
+ if (distance >= ds[last]) return ts[last];
3421
+ let lo = 1;
3422
+ let hi = last;
3423
+ while (lo < hi) {
3424
+ const mid = lo + hi >>> 1;
3425
+ if (ds[mid] < distance) lo = mid + 1;
3426
+ else hi = mid;
3427
+ }
3428
+ const dPrev = ds[hi - 1];
3429
+ const dCur = ds[hi];
3430
+ const span = dCur - dPrev;
3431
+ const frac = span > 0 ? (distance - dPrev) / span : 0;
3432
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
3433
+ }
2034
3434
  var SVG_NS = "http://www.w3.org/2000/svg";
2035
- var INTERNAL_ATTRS = /* @__PURE__ */ new Set(["type", "children", "animate", "style", "animator", "defs", "bindings"]);
2036
- function createElement(tagName, props, style, children) {
3435
+ var ALLOWED_SVG_TAGS_LOWER_CASE = new Set([
3436
+ "svg",
3437
+ "g",
3438
+ "path",
3439
+ "circle",
3440
+ "ellipse",
3441
+ "rect",
3442
+ "line",
3443
+ "polyline",
3444
+ "polygon",
3445
+ "text",
3446
+ "tspan",
3447
+ "textPath",
3448
+ "defs",
3449
+ "clipPath",
3450
+ "mask",
3451
+ "pattern",
3452
+ "linearGradient",
3453
+ "radialGradient",
3454
+ "stop",
3455
+ "use",
3456
+ "symbol",
3457
+ "marker",
3458
+ "filter",
3459
+ "feGaussianBlur",
3460
+ "feOffset",
3461
+ "feBlend",
3462
+ "feColorMatrix",
3463
+ "feMerge",
3464
+ "feMergeNode"
3465
+ ].map((tagName) => tagName.toLowerCase()));
3466
+ var ALLOWED_RESOURCE_ATTRIBUTES = [
3467
+ "href",
3468
+ // <use>
3469
+ "src",
3470
+ // <image>
3471
+ "filter",
3472
+ // url(#filterId)
3473
+ "clipPath",
3474
+ // clip-path="url(#clipPathId)"
3475
+ "mask",
3476
+ // url(#maskId)
3477
+ "markerStart",
3478
+ // marker-start="url(#markerId)"
3479
+ "markerMid",
3480
+ // marker-mid="url(#markerId)"
3481
+ "markerEnd"
3482
+ // marker-end="url(#markerId)"
3483
+ // 'fill', // url(#gradientId) or url(#patternId)
3484
+ // 'stroke', // url(#gradientId) or url(#patternId)
3485
+ // Don't allow 'cursor', can use external SVG, // url(cursor.svg)
3486
+ ];
3487
+ var ALLOWED_RESOURCE_ATTRIBUTES_SET = new Set(ALLOWED_RESOURCE_ATTRIBUTES);
3488
+ var ALLOWED_ATTRIBUTES = [
3489
+ "href",
3490
+ "src",
3491
+ // Presentation
3492
+ "fill",
3493
+ "fillOpacity",
3494
+ "fillRule",
3495
+ "stroke",
3496
+ "strokeWidth",
3497
+ "strokeOpacity",
3498
+ "strokeLinecap",
3499
+ "strokeLinejoin",
3500
+ "strokeMiterlimit",
3501
+ "strokeDasharray",
3502
+ "strokeDashoffset",
3503
+ "opacity",
3504
+ "transform",
3505
+ // Geometry
3506
+ "x",
3507
+ "y",
3508
+ "cx",
3509
+ "cy",
3510
+ "r",
3511
+ "rx",
3512
+ "ry",
3513
+ "width",
3514
+ "height",
3515
+ "d",
3516
+ "x1",
3517
+ "y1",
3518
+ "x2",
3519
+ "y2",
3520
+ "points",
3521
+ "dx",
3522
+ "dy",
3523
+ // Font
3524
+ "fontSize",
3525
+ "fontFamily",
3526
+ "fontWeight",
3527
+ "fontStyle",
3528
+ // Text
3529
+ "textAnchor",
3530
+ "letterSpacing",
3531
+ "wordSpacing",
3532
+ "space",
3533
+ "xml:space",
3534
+ "textDecoration",
3535
+ "textTransform",
3536
+ "whiteSpace",
3537
+ "white-space",
3538
+ "dominantBaseline",
3539
+ "alignmentBaseline",
3540
+ "rotate",
3541
+ // per-glyph rotation array, currently not supported
3542
+ // Structure
3543
+ "id",
3544
+ "class",
3545
+ "viewBox",
3546
+ "preserveAspectRatio",
3547
+ // Gradient/Pattern
3548
+ "offset",
3549
+ "stopColor",
3550
+ "stopOpacity",
3551
+ "gradientTransform",
3552
+ // Clippath/Mask
3553
+ "clipPath",
3554
+ "mask",
3555
+ // Motion path
3556
+ "offsetPath",
3557
+ "offsetDistance",
3558
+ "offsetRotate",
3559
+ "offsetAnchor",
3560
+ "offsetPosition",
3561
+ // Text path
3562
+ "startOffset",
3563
+ "textLength",
3564
+ "lengthAdjust",
3565
+ // Filter
3566
+ "filter",
3567
+ "stdDeviation",
3568
+ "in",
3569
+ "in2",
3570
+ "result",
3571
+ "mode",
3572
+ ...ALLOWED_RESOURCE_ATTRIBUTES
3573
+ ];
3574
+ var ALLOWED_ATTRIBUTES_LOW_SET = new Set(ALLOWED_ATTRIBUTES.map((str) => str.toLowerCase()));
3575
+ function sanitiseAttributeValue(name, value) {
3576
+ const nameLower = name.toLowerCase();
3577
+ if (!ALLOWED_ATTRIBUTES_LOW_SET.has(nameLower)) {
3578
+ console.warn("Attribute not in whitelist: ", nameLower);
3579
+ return void 0;
3580
+ }
3581
+ if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopColor") {
3582
+ const str = String(value);
3583
+ if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
3584
+ console.warn('Attribute "' + nameLower + '" blocked: url() references must be internal url(#id), got:', value);
3585
+ return void 0;
3586
+ }
3587
+ return value;
3588
+ }
3589
+ if (ALLOWED_RESOURCE_ATTRIBUTES_SET.has(nameLower)) {
3590
+ const str = String(value);
3591
+ if (str.startsWith("#")) {
3592
+ return value;
3593
+ }
3594
+ if (/^url\(#[^)]+\)$/.test(str)) {
3595
+ return value;
3596
+ }
3597
+ return void 0;
3598
+ }
3599
+ return value;
3600
+ }
3601
+ function createElement(tagName, normalisedProps, style, children, textContent) {
3602
+ if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) {
3603
+ console.warn("Attribute not in whitelist: ", tagName);
3604
+ return null;
3605
+ }
2037
3606
  const element = document.createElementNS(SVG_NS, tagName);
2038
- for (const propName in props) {
2039
- element.setAttribute(camelCaseToKebabWordIfNeeded(propName), props[propName]);
3607
+ for (const propName in normalisedProps) {
3608
+ element.setAttribute(
3609
+ camelCaseToKebabWordIfNeeded(propName),
3610
+ sanitiseAttributeValue(propName, normalisedProps[propName])
3611
+ );
2040
3612
  }
2041
3613
  if (style) {
2042
3614
  for (const styleProp in style) {
@@ -2048,6 +3620,7 @@ var PixodeskAnimatorReact = (() => {
2048
3620
  element.appendChild(child);
2049
3621
  }
2050
3622
  }
3623
+ if (textContent) element.textContent = textContent;
2051
3624
  return element;
2052
3625
  }
2053
3626
  function resolveStyle(style, defs) {
@@ -2062,9 +3635,12 @@ var PixodeskAnimatorReact = (() => {
2062
3635
  const propsCopy = {};
2063
3636
  for (const key of Object.keys(props)) {
2064
3637
  if (INTERNAL_ATTRS.has(key)) continue;
3638
+ if (key === "style") continue;
2065
3639
  let value = props[key];
2066
3640
  if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
2067
3641
  propsCopy[key] = toRGBA(value);
3642
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && value.value && typeof value.value === "object") {
3643
+ propsCopy["transform"] = composeTransformParts(value.value, { withUnits: false });
2068
3644
  } else if (TRANSFORM_FN_NAMES.has(key)) {
2069
3645
  if (Array.isArray(value)) {
2070
3646
  if (key === "translate") value = value.map((v) => v + "px");
@@ -2080,8 +3656,8 @@ var PixodeskAnimatorReact = (() => {
2080
3656
  }
2081
3657
  function renderNode(node, defs) {
2082
3658
  if (!node) return null;
2083
- const _a = node, { type, children, animate, style } = _a, props = __objRest(_a, ["type", "children", "animate", "style"]);
2084
- const nodeDefs = node.defs || defs;
3659
+ const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
3660
+ const nodeDefs = getDefs(node) || defs;
2085
3661
  const resolvedStyle = resolveStyle(style, nodeDefs);
2086
3662
  let childElements;
2087
3663
  if (children) {
@@ -2097,7 +3673,8 @@ var PixodeskAnimatorReact = (() => {
2097
3673
  type || "g",
2098
3674
  getNormalizedProps(props),
2099
3675
  resolvedStyle,
2100
- childElements
3676
+ childElements,
3677
+ props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
2101
3678
  );
2102
3679
  }
2103
3680
  function setupAnimationTriggers(api, config) {
@@ -2175,27 +3752,44 @@ var PixodeskAnimatorReact = (() => {
2175
3752
  }
2176
3753
  return api;
2177
3754
  }
2178
- var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
2179
- var PX_ANIM_ATTR_NAME = "_px_animator";
2180
- function isPxElementFileFormat(fileJson) {
2181
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
2182
- return false;
3755
+ function propAnimIsMotionPath(anim) {
3756
+ var _a;
3757
+ const kfs = (_a = anim.keyframes) != null ? _a : anim.kfs;
3758
+ if (!Array.isArray(kfs)) return false;
3759
+ if (anim.autoOrient) return true;
3760
+ for (const kf of kfs) {
3761
+ if (kf.tangentIn || kf.tangentOut) return true;
2183
3762
  }
2184
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
3763
+ return false;
2185
3764
  }
2186
- function getAnimatorConfig(doc) {
2187
- var _a, _b;
2188
- return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
2189
- }
2190
- function getDefs(doc) {
2191
- var _a;
2192
- if (!doc) return void 0;
2193
- return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
3765
+ var _segmentCache = /* @__PURE__ */ new WeakMap();
3766
+ function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
3767
+ const existing = _segmentCache.get(prevKf);
3768
+ if (existing) return existing;
3769
+ const to = prevKf.tangentOut;
3770
+ const ti = nextKf.tangentIn;
3771
+ const P1 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];
3772
+ const P2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];
3773
+ const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);
3774
+ const entry = {
3775
+ P0: prevPos,
3776
+ P1,
3777
+ P2,
3778
+ P3: nextPos,
3779
+ lut,
3780
+ totalArc: lut.ds[lut.ds.length - 1]
3781
+ };
3782
+ _segmentCache.set(prevKf, entry);
3783
+ return entry;
2194
3784
  }
2195
- function getBindings(doc) {
2196
- var _a;
2197
- if (!doc) return void 0;
2198
- return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
3785
+ function evaluateMotionPathSegment(prevKf, nextKf, prevPos, nextPos, localProgress, autoOrient) {
3786
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
3787
+ const t = seg.totalArc === 0 ? localProgress : bezier2D_tForDistance(seg.lut, localProgress * seg.totalArc);
3788
+ const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
3789
+ if (!autoOrient) return { translate: point };
3790
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
3791
+ const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
3792
+ return { translate: point, rotateDeg };
2199
3793
  }
2200
3794
  function parsePathCommands(d) {
2201
3795
  const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
@@ -2259,6 +3853,8 @@ var PixodeskAnimatorReact = (() => {
2259
3853
  currentPath.o.push([x2, y2]);
2260
3854
  } else if (type === "Z" || type === "z") {
2261
3855
  currentPath.c = true;
3856
+ } else {
3857
+ console.warn('Unsupported path command "' + type + '"');
2262
3858
  }
2263
3859
  }
2264
3860
  return res;
@@ -2276,13 +3872,17 @@ var PixodeskAnimatorReact = (() => {
2276
3872
  return typeof value === "string" && extractPathData(value) !== void 0;
2277
3873
  }
2278
3874
  function normalizePathValue(value) {
3875
+ if (value && typeof value === "object" && typeof value.path === "string") {
3876
+ const d = extractPathData(value.path);
3877
+ return d ? { paths: parseSvgPathToBezier(d) } : value;
3878
+ }
2279
3879
  if (value && typeof value === "object" && "paths" in value) {
2280
3880
  const pathsArray = value.paths;
2281
3881
  if (Array.isArray(pathsArray) && pathsArray.length > 0) {
2282
3882
  if (isPathString(pathsArray[0])) {
2283
3883
  const paths = [];
2284
- for (const pathStr of pathsArray) {
2285
- const d = extractPathData(pathStr);
3884
+ for (const pathStr2 of pathsArray) {
3885
+ const d = extractPathData(pathStr2);
2286
3886
  if (d) {
2287
3887
  paths.push(...parseSvgPathToBezier(d));
2288
3888
  }
@@ -2295,8 +3895,8 @@ var PixodeskAnimatorReact = (() => {
2295
3895
  if (Array.isArray(value)) {
2296
3896
  if (value.length > 0 && isPathString(value[0])) {
2297
3897
  const paths = [];
2298
- for (const pathStr of value) {
2299
- const d = extractPathData(pathStr);
3898
+ for (const pathStr2 of value) {
3899
+ const d = extractPathData(pathStr2);
2300
3900
  if (d) {
2301
3901
  paths.push(...parseSvgPathToBezier(d));
2302
3902
  }
@@ -2350,6 +3950,111 @@ var PixodeskAnimatorReact = (() => {
2350
3950
  }
2351
3951
  return results;
2352
3952
  }
3953
+ function interpolateValue(propName, a, b, t) {
3954
+ var _a, _b;
3955
+ if (propName === "d") {
3956
+ const aPaths = (_a = a == null ? void 0 : a.paths) != null ? _a : Array.isArray(a) ? a : [];
3957
+ const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
3958
+ return { paths: interpolateBeziers(aPaths, bPaths, t) };
3959
+ }
3960
+ if (COLOUR_ATTR_NAMES.has(propName)) {
3961
+ return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
3962
+ }
3963
+ if (TRANSFORM_FN_NAMES.has(propName) || propName === "stroke-dasharray" || propName === "strokeDasharray") {
3964
+ return interpolateVec(a || [], b || [], t);
3965
+ }
3966
+ return interpolateNum(+(a || 0), +(b || 0), t);
3967
+ }
3968
+ function expandLoopKeyframes(propName, keyframes, loop, duration) {
3969
+ var _a, _b, _c, _d, _e;
3970
+ const totalIntervals = keyframes.length - 1;
3971
+ const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
3972
+ let segKfs;
3973
+ if (loop.before) {
3974
+ segKfs = keyframes.slice(0, segCount + 1);
3975
+ } else {
3976
+ segKfs = keyframes.slice(totalIntervals - segCount);
3977
+ }
3978
+ const firstT = (_b = keyframes[0].t) != null ? _b : 0;
3979
+ const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
3980
+ let fillStart, fillEnd;
3981
+ if (loop.before) {
3982
+ fillStart = 0;
3983
+ fillEnd = firstT;
3984
+ } else {
3985
+ fillStart = lastT;
3986
+ fillEnd = duration;
3987
+ }
3988
+ const fillDuration = fillEnd - fillStart;
3989
+ if (fillDuration <= 0) return keyframes;
3990
+ const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
3991
+ const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
3992
+ const segDuration = segEndT - segStartT;
3993
+ if (segDuration <= 0) return keyframes;
3994
+ const template = segKfs.map((kf) => ({
3995
+ relT: (kf.t - segStartT) / segDuration,
3996
+ v: kf.v,
3997
+ e: kf.e
3998
+ }));
3999
+ const fullReps = Math.floor(fillDuration / segDuration);
4000
+ const remainder = fillDuration - fullReps * segDuration;
4001
+ const partialFraction = remainder / segDuration;
4002
+ const looped = [];
4003
+ function appendRep(repStart, isReversed, partial) {
4004
+ let entries;
4005
+ if (isReversed) {
4006
+ entries = [];
4007
+ for (let i = template.length - 1; i >= 0; i--) {
4008
+ entries.push({
4009
+ relT: 1 - template[i].relT,
4010
+ v: template[i].v,
4011
+ // Easing for reversed transition: use reversed easing from the forward "from" keyframe
4012
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0
4013
+ });
4014
+ }
4015
+ } else {
4016
+ entries = template;
4017
+ }
4018
+ const cutRelT = partial !== void 0 ? partial : 1;
4019
+ for (let i = 0; i < entries.length; i++) {
4020
+ const entry = entries[i];
4021
+ if (entry.relT > cutRelT + 1e-9) {
4022
+ const prev = entries[i - 1];
4023
+ const intervalSpan = entry.relT - prev.relT;
4024
+ const localFrac = (cutRelT - prev.relT) / intervalSpan;
4025
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
4026
+ const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
4027
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
4028
+ if (looped.length > 0 && prev.relT <= cutRelT) {
4029
+ looped[looped.length - 1].e = leftEasing;
4030
+ }
4031
+ looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
4032
+ return;
4033
+ }
4034
+ looped.push({
4035
+ t: repStart + entry.relT * segDuration,
4036
+ v: entry.v,
4037
+ e: i < entries.length - 1 ? entry.e : void 0
4038
+ });
4039
+ }
4040
+ }
4041
+ for (let rep = 0; rep < fullReps; rep++) {
4042
+ const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
4043
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
4044
+ const repStart = fillStart + rep * segDuration;
4045
+ appendRep(repStart, isReversed);
4046
+ }
4047
+ if (partialFraction > 1e-9) {
4048
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
4049
+ const repStart = fillStart + fullReps * segDuration;
4050
+ appendRep(repStart, isReversed, partialFraction);
4051
+ }
4052
+ if (loop.before) {
4053
+ return [...looped, ...keyframes];
4054
+ } else {
4055
+ return [...keyframes, ...looped];
4056
+ }
4057
+ }
2353
4058
  function normalizeKeyframes(propName, propAnim, duration, defs) {
2354
4059
  var _a, _b, _c, _d, _e;
2355
4060
  const keyframes = propAnim.keyframes || propAnim.kfs || [];
@@ -2374,6 +4079,11 @@ var PixodeskAnimatorReact = (() => {
2374
4079
  var _a2, _b2;
2375
4080
  return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
2376
4081
  });
4082
+ const loopRaw = propAnim.loop;
4083
+ const loop = loopRaw === true ? {} : loopRaw || void 0;
4084
+ if (loop && normalized.length >= 2) {
4085
+ return expandLoopKeyframes(propName, normalized, loop, duration);
4086
+ }
2377
4087
  return normalized;
2378
4088
  }
2379
4089
  function mergeAnimationDefinitions(animations) {
@@ -2424,10 +4134,11 @@ var PixodeskAnimatorReact = (() => {
2424
4134
  }
2425
4135
  }
2426
4136
  const processNode = (node) => {
2427
- if (node.animate) {
4137
+ const inlineAnim = node.animate;
4138
+ if (inlineAnim && Object.keys(inlineAnim).length > 0) {
2428
4139
  const nodeId = node.id || generateElementId();
2429
4140
  node.id = nodeId;
2430
- const normalized = processAnimation(nodeId, node.animate);
4141
+ const normalized = processAnimation(nodeId, inlineAnim);
2431
4142
  if (normalized) bindings.push(normalized);
2432
4143
  }
2433
4144
  if (node.children) {
@@ -2498,6 +4209,41 @@ var PixodeskAnimatorReact = (() => {
2498
4209
  localProgress
2499
4210
  ).join(" ");
2500
4211
  cssAttrName = propName;
4212
+ } else if (cssAttrName === "transform" && prevV !== null && typeof prevV === "object" && !Array.isArray(prevV)) {
4213
+ const partKeys = /* @__PURE__ */ new Set([
4214
+ ...prevV ? Object.keys(prevV) : [],
4215
+ ...nextV ? Object.keys(nextV) : []
4216
+ ]);
4217
+ const partsResult = {};
4218
+ for (const partKey of partKeys) {
4219
+ const prevPart = prevV == null ? void 0 : prevV[partKey];
4220
+ const nextPart = nextV == null ? void 0 : nextV[partKey];
4221
+ if (partKey === "rotate") {
4222
+ partsResult.rotate = interpolateNum(+(prevPart != null ? prevPart : 0), +(nextPart != null ? nextPart : 0), localProgress);
4223
+ } else if (partKey === "translate" || partKey === "scale" || partKey === "origin") {
4224
+ const fallback = partKey === "scale" ? [1, 1] : [0, 0];
4225
+ const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);
4226
+ partsResult[partKey] = interp;
4227
+ }
4228
+ }
4229
+ if (propAnimIsMotionPath(propAnim)) {
4230
+ const prevTr = prevV.translate;
4231
+ const nextTr = nextV.translate;
4232
+ if (Array.isArray(prevTr) && Array.isArray(nextTr)) {
4233
+ const sample = evaluateMotionPathSegment(
4234
+ prevKf,
4235
+ nextKf,
4236
+ [+prevTr[0], +prevTr[1]],
4237
+ [+nextTr[0], +nextTr[1]],
4238
+ localProgress,
4239
+ !!propAnim.autoOrient
4240
+ );
4241
+ partsResult.translate = [sample.translate[0], sample.translate[1]];
4242
+ if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg;
4243
+ }
4244
+ }
4245
+ cssValue = composeTransformParts(partsResult, { withUnits: false });
4246
+ cssAttrName = "transform";
2501
4247
  } else if (cssAttrName === "translate") {
2502
4248
  const v = interpolateVec(
2503
4249
  prevV || [0, 0],
@@ -2523,12 +4269,12 @@ var PixodeskAnimatorReact = (() => {
2523
4269
  cssValue = "scale(" + v.join(",") + ")";
2524
4270
  cssAttrName = "transform";
2525
4271
  } else {
2526
- const num = interpolateNum(
4272
+ const num2 = interpolateNum(
2527
4273
  +(prevV || 0),
2528
4274
  +(nextV || 0),
2529
4275
  localProgress
2530
4276
  );
2531
- cssValue = num;
4277
+ cssValue = num2;
2532
4278
  }
2533
4279
  if (PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
2534
4280
  cssValue = cssValue * 100 + "%";
@@ -2804,41 +4550,91 @@ var PixodeskAnimatorReact = (() => {
2804
4550
  };
2805
4551
  return adapter;
2806
4552
  }
4553
+ function createCssKf(kf, t, propName, unsupportedSet) {
4554
+ var _a, _b;
4555
+ let value = (_a = kf.v) != null ? _a : kf.value;
4556
+ const e = (_b = kf.e) != null ? _b : kf.easing;
4557
+ const cssKf = {
4558
+ offset: t,
4559
+ easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
4560
+ };
4561
+ let cssValue;
4562
+ let cssKey = propName;
4563
+ if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
4564
+ cssValue = toRGBA(value);
4565
+ } else if (propName === "transform" && value !== null && typeof value === "object" && !Array.isArray(value)) {
4566
+ cssValue = composeTransformParts(value, { withUnits: true });
4567
+ cssKey = "transform";
4568
+ } else if (TRANSFORM_FN_NAMES.has(propName)) {
4569
+ if (Array.isArray(value)) {
4570
+ if (propName === "translate") value = value.map((v) => v + "px");
4571
+ value = value.join(",");
4572
+ }
4573
+ if (propName === "rotate") value = value + "deg";
4574
+ cssValue = propName + "(" + value + ")";
4575
+ cssKey = "transform";
4576
+ } else {
4577
+ cssValue = "" + value;
4578
+ }
4579
+ if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
4580
+ cssKey = kebabToCamelCaseWord(cssKey);
4581
+ cssKf[cssKey] = cssValue;
4582
+ return cssKf;
4583
+ }
4584
+ function clipKeyframesToDuration(propName, keyframes, duration) {
4585
+ var _a, _b, _c, _d, _e;
4586
+ const result = [];
4587
+ for (let i = 0; i < keyframes.length; i++) {
4588
+ const kf = keyframes[i];
4589
+ const t = (_a = kf.t) != null ? _a : 0;
4590
+ if (t < 0) {
4591
+ const next = keyframes[i + 1];
4592
+ if (next && ((_b = next.t) != null ? _b : 0) >= 0) {
4593
+ const nextT = (_c = next.t) != null ? _c : 0;
4594
+ const localFrac = (0 - t) / (nextT - t);
4595
+ const easedFrac = kf.e ? cubicBezier(kf.e)(localFrac) : localFrac;
4596
+ const { right: rightEasing } = splitEasing(kf.e, localFrac);
4597
+ result.push({ t: 0, v: interpolateValue(propName, kf.v, next.v, easedFrac), e: rightEasing });
4598
+ }
4599
+ continue;
4600
+ }
4601
+ if (t > duration) {
4602
+ const prev = keyframes[i - 1];
4603
+ if (prev && ((_d = prev.t) != null ? _d : 0) <= duration) {
4604
+ const prevT = (_e = prev.t) != null ? _e : 0;
4605
+ const localFrac = (duration - prevT) / (t - prevT);
4606
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
4607
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
4608
+ if (result.length > 0) result[result.length - 1] = __spreadProps(__spreadValues({}, result[result.length - 1]), { e: leftEasing });
4609
+ result.push({ t: duration, v: interpolateValue(propName, prev.v, kf.v, easedFrac), e: void 0 });
4610
+ }
4611
+ break;
4612
+ }
4613
+ result.push(kf);
4614
+ }
4615
+ return result;
4616
+ }
2807
4617
  function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
2808
- var _a, _b, _c, _d;
4618
+ var _a;
2809
4619
  const result = /* @__PURE__ */ new Map();
2810
4620
  for (const [propName, propAnim] of Object.entries(animDef)) {
2811
- const keyframes = propAnim.kfs || propAnim.keyframes || [];
4621
+ const duration = config.duration || 1;
4622
+ const clippedKeyframes = clipKeyframesToDuration(propName, propAnim.kfs || propAnim.keyframes || [], duration);
2812
4623
  const cssKeyframes = [];
2813
- for (const kf of keyframes) {
2814
- let t = (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
2815
- t = clamp(t / (config.duration || 1), 0, 1);
2816
- let value = (_c = kf.v) != null ? _c : kf.value;
2817
- const e = (_d = kf.e) != null ? _d : kf.easing;
2818
- const cssKf = {
2819
- offset: t,
2820
- easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
2821
- };
2822
- let cssValue;
2823
- let cssKey = propName;
2824
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
2825
- cssValue = toRGBA(value);
2826
- } else if (TRANSFORM_FN_NAMES.has(propName)) {
2827
- if (Array.isArray(value)) {
2828
- if (propName === "translate") value = value.map((v) => v + "px");
2829
- value = value.join(",");
2830
- }
2831
- if (propName === "rotate") value = value + "deg";
2832
- cssValue = propName + "(" + value + ")";
2833
- cssKey = "transform";
2834
- } else {
2835
- cssValue = "" + value;
4624
+ for (let i = 0; i < clippedKeyframes.length; i++) {
4625
+ const kf = clippedKeyframes[i];
4626
+ const t = clamp(((_a = kf.t) != null ? _a : 0) / duration, 0, 1);
4627
+ const cssKf = createCssKf(kf, t, propName, unsupportedSet);
4628
+ if (i === 0 && (cssKf.offset || 0) > 0) {
4629
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), { offset: 0 }));
2836
4630
  }
2837
- if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
2838
- cssKey = kebabToCamelCaseWord(cssKey);
2839
- cssKf[cssKey] = cssValue;
2840
4631
  cssKeyframes.push(cssKf);
2841
4632
  }
4633
+ if (cssKeyframes.length > 0 && (cssKeyframes[cssKeyframes.length - 1].offset || 0) < 1) {
4634
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKeyframes[cssKeyframes.length - 1]), {
4635
+ offset: 1
4636
+ }));
4637
+ }
2842
4638
  if (cssKeyframes.length > 0) {
2843
4639
  result.set(propName, cssKeyframes);
2844
4640
  }
@@ -2846,6 +4642,7 @@ var PixodeskAnimatorReact = (() => {
2846
4642
  return result;
2847
4643
  }
2848
4644
  function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsupportedAttrs) {
4645
+ var _a;
2849
4646
  const config = getAnimatorConfig(doc) || {};
2850
4647
  const bindings = getNormalisedBindings(doc);
2851
4648
  if (!rootElement) {
@@ -2878,29 +4675,33 @@ var PixodeskAnimatorReact = (() => {
2878
4675
  console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
2879
4676
  }
2880
4677
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
4678
+ const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
4679
+ const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
4680
+ const effectOptions = {
4681
+ duration: config.duration,
4682
+ delay: positiveDelay,
4683
+ // Default to 'forwards' so elements hold their final state after the
4684
+ // animation ends — consistent with Lottie and other animation runtimes.
4685
+ // Without this, seeking to the last frame reverts elements to their
4686
+ // pre-animation state (the Web Animations API "after" phase with fill:'none').
4687
+ fill: (_a = config.fill) != null ? _a : "forwards",
4688
+ direction: config.direction,
4689
+ iterations
4690
+ };
2881
4691
  for (let i = 0; i < elements.length; i++) {
2882
4692
  const element = elements[i];
2883
- const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
2884
- const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
2885
- const effectOptions = {
2886
- duration: config.duration,
2887
- delay: positiveDelay,
2888
- fill: config.fill,
2889
- direction: config.direction,
2890
- iterations
2891
- };
2892
4693
  for (const [, keyframes] of keyframesMap) {
2893
4694
  if (keyframes.length > 0) {
2894
4695
  try {
2895
4696
  const effect = new KeyframeEffect(element, keyframes, effectOptions);
2896
4697
  const anim = new Animation(effect, document.timeline);
2897
4698
  if (callbacks == null ? void 0 : callbacks.onFinish) anim.onfinish = () => {
2898
- var _a;
2899
- return (_a = callbacks.onFinish) == null ? void 0 : _a.call(callbacks);
4699
+ var _a2;
4700
+ return (_a2 = callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
2900
4701
  };
2901
4702
  if (callbacks == null ? void 0 : callbacks.onRemove) anim.onremove = () => {
2902
- var _a;
2903
- return (_a = callbacks.onRemove) == null ? void 0 : _a.call(callbacks);
4703
+ var _a2;
4704
+ return (_a2 = callbacks.onRemove) == null ? void 0 : _a2.call(callbacks);
2904
4705
  };
2905
4706
  if (seekPosition) {
2906
4707
  anim.currentTime = seekPosition;
@@ -2921,34 +4722,47 @@ var PixodeskAnimatorReact = (() => {
2921
4722
  "isReady": () => true,
2922
4723
  "getRootElement": () => rootElement || null,
2923
4724
  "isPlaying": () => {
2924
- var _a;
2925
- return ((_a = animations[0]) == null ? void 0 : _a.playState) === "running";
4725
+ var _a2;
4726
+ return ((_a2 = animations[0]) == null ? void 0 : _a2.playState) === "running";
2926
4727
  },
2927
4728
  "play": () => {
2928
- var _a;
4729
+ var _a2;
2929
4730
  animations.forEach((a) => a.play());
2930
- (_a = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a.call(callbacks);
4731
+ (_a2 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a2.call(callbacks);
2931
4732
  },
2932
4733
  "pause": () => {
2933
- var _a;
4734
+ var _a2;
2934
4735
  animations.forEach((a) => a.pause());
2935
- (_a = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a.call(callbacks);
4736
+ (_a2 = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a2.call(callbacks);
2936
4737
  },
2937
4738
  "cancel": () => {
2938
- var _a;
4739
+ var _a2;
2939
4740
  animations.forEach((a) => a.cancel());
2940
- (_a = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a.call(callbacks);
4741
+ (_a2 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a2.call(callbacks);
2941
4742
  },
2942
4743
  "finish": () => {
2943
- animations.forEach((a) => a.finish());
4744
+ var _a2;
4745
+ for (const a of animations) {
4746
+ try {
4747
+ if (((_a2 = a.effect) == null ? void 0 : _a2.getTiming().iterations) === Infinity) {
4748
+ a.effect.updateTiming({ iterations: 1 });
4749
+ a.finish();
4750
+ a.effect.updateTiming({ iterations: Infinity });
4751
+ } else {
4752
+ a.finish();
4753
+ }
4754
+ } catch (e) {
4755
+ a.cancel();
4756
+ }
4757
+ }
2944
4758
  },
2945
4759
  "setPlaybackRate": (rate) => {
2946
4760
  animations.forEach((a) => a.playbackRate = rate);
2947
4761
  return api;
2948
4762
  },
2949
4763
  "getCurrentTime": () => {
2950
- var _a, _b;
2951
- const res = (_b = (_a = animations[0]) == null ? void 0 : _a.currentTime) != null ? _b : null;
4764
+ var _a2, _b;
4765
+ const res = (_b = (_a2 = animations[0]) == null ? void 0 : _a2.currentTime) != null ? _b : null;
2952
4766
  return res !== null ? +res : null;
2953
4767
  },
2954
4768
  "setCurrentTime": (time) => {
@@ -3003,6 +4817,7 @@ var PixodeskAnimatorReact = (() => {
3003
4817
  return cloned;
3004
4818
  }
3005
4819
  function generateNewIds(doc) {
4820
+ var _a, _b;
3006
4821
  const cloned = deepClone(doc);
3007
4822
  const idMap = /* @__PURE__ */ new Map();
3008
4823
  const hashRefAttrs = /* @__PURE__ */ new Set(["href", "xlink:href"]);
@@ -3028,6 +4843,8 @@ var PixodeskAnimatorReact = (() => {
3028
4843
  const newId = generateUniqueId();
3029
4844
  idMap.set(oldId, newId);
3030
4845
  node.id = newId;
4846
+ } else if (node.animate) {
4847
+ node.id = generateUniqueId();
3031
4848
  }
3032
4849
  if (Array.isArray(node.children)) {
3033
4850
  for (const child of node.children) {
@@ -3069,23 +4886,21 @@ var PixodeskAnimatorReact = (() => {
3069
4886
  value[styleProp] = replaceUrlRefs(styleValue, idMap);
3070
4887
  }
3071
4888
  }
3072
- } else if (typeof value === "object" && value !== null && key !== "animate") {
4889
+ } else if (typeof value === "object" && value !== null) {
3073
4890
  updateRefs(value);
3074
4891
  }
3075
4892
  }
3076
4893
  }
3077
4894
  collectIds(cloned);
3078
4895
  updateRefs(cloned);
3079
- const docBindings = cloned.bindings;
3080
- if (Array.isArray(docBindings)) {
3081
- for (const binding of docBindings) {
3082
- if (binding.id) {
3083
- const newId = idMap.get(binding.id);
3084
- if (newId) {
3085
- binding.id = newId;
3086
- }
3087
- }
4896
+ const docAnimate = (_a = cloned.animator) == null ? void 0 : _a.animate;
4897
+ if (docAnimate && typeof docAnimate === "object") {
4898
+ const updatedAnimate = {};
4899
+ for (const [id, anim] of Object.entries(docAnimate)) {
4900
+ const newId = (_b = idMap.get(id)) != null ? _b : id;
4901
+ updatedAnimate[newId] = anim;
3088
4902
  }
4903
+ cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { animate: updatedAnimate });
3089
4904
  }
3090
4905
  return cloned;
3091
4906
  }
@@ -3096,6 +4911,9 @@ var PixodeskAnimatorReact = (() => {
3096
4911
  });
3097
4912
  }
3098
4913
  function createAnimatorImpl(doc, adapter, callbacks, containerElement) {
4914
+ const effectsWarnings = validateNodeEffects(doc);
4915
+ for (const w of effectsWarnings) console.warn("[PxAnimator] effects shape warning:", w);
4916
+ doc = applyPlayerEffects(doc).root;
3099
4917
  const animatorConfig = getAnimatorConfig(doc) || {};
3100
4918
  animatorConfig.debug = true;
3101
4919
  let rootElement = null;
@@ -3111,14 +4929,21 @@ var PixodeskAnimatorReact = (() => {
3111
4929
  }
3112
4930
  return createAnimatorFromConfig(doc, adapter, callbacks, rootElement);
3113
4931
  }
3114
- function createAnimator(docOrUrl, adapter, callbacks, containerElement) {
3115
- if (typeof docOrUrl === "object") {
3116
- return createAnimatorImpl(docOrUrl, adapter, callbacks, containerElement);
4932
+ function createAnimator(options) {
4933
+ const { src, data, adapter, callbacks, container } = options;
4934
+ if (data !== void 0 && src !== void 0) {
4935
+ throw new Error("createAnimator: provide either `src` or `data`, not both");
4936
+ }
4937
+ if (data === void 0 && src === void 0) {
4938
+ throw new Error("createAnimator: either `src` or `data` is required");
4939
+ }
4940
+ if (data !== void 0) {
4941
+ return createAnimatorImpl(data, adapter, callbacks, container);
3117
4942
  }
3118
4943
  let animator = null;
3119
- fetch(docOrUrl).then((res) => res.json()).then((json) => {
4944
+ fetch(src).then((res) => res.json()).then((json) => {
3120
4945
  if (isPxElementFileFormat(json)) {
3121
- animator = createAnimatorImpl(json, adapter, callbacks, containerElement);
4946
+ animator = createAnimatorImpl(json, adapter, callbacks, container);
3122
4947
  } else {
3123
4948
  console.error("Invalid animation document format");
3124
4949
  }
@@ -3158,7 +4983,7 @@ var PixodeskAnimatorReact = (() => {
3158
4983
  if (!element[PX_ANIM_ATTR_NAME]) {
3159
4984
  const src = element.getAttribute(PX_ANIM_SRC_ATTR_NAME);
3160
4985
  if (src) {
3161
- element[PX_ANIM_ATTR_NAME] = createAnimator(src, void 0, void 0, element);
4986
+ element[PX_ANIM_ATTR_NAME] = createAnimator({ src, container: element });
3162
4987
  }
3163
4988
  }
3164
4989
  }
@@ -3284,7 +5109,7 @@ var PixodeskAnimatorReact = (() => {
3284
5109
  };
3285
5110
  const root = doc ? renderNode2(doc) : null;
3286
5111
  (0, import_react2.useEffect)(() => {
3287
- let api = createAnimator(doc, createReactAdapter(elementRefs));
5112
+ let api = createAnimator({ data: doc, adapter: createReactAdapter(elementRefs) });
3288
5113
  apiHolderRef.current = api;
3289
5114
  return () => {
3290
5115
  api?.destroy();
@@ -3431,6 +5256,41 @@ var PixodeskAnimatorReact = (() => {
3431
5256
  );
3432
5257
  };
3433
5258
  var PixodeskSvgAnimator_default = PixodeskSvgAnimator;
5259
+
5260
+ // src/PixodeskSvgCssAnimator.tsx
5261
+ var import_react3 = __toESM(require_react(), 1);
5262
+ var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
5263
+ var PixodeskSvgCssAnimator = ({ className, style, children, startOn = "load", outAction = "continue" }) => {
5264
+ const [state, setState] = (0, import_react3.useState)(startOn === "load" ? "playing" : "idle");
5265
+ const ref = (0, import_react3.useRef)(null);
5266
+ (0, import_react3.useEffect)(() => {
5267
+ if (startOn !== "scrollIntoView") return;
5268
+ const el = ref.current;
5269
+ if (!el) return;
5270
+ const outState = outAction === "reset" ? "idle" : outAction === "pause" ? "paused" : "playing";
5271
+ const observer = new IntersectionObserver(
5272
+ ([entry]) => setState(entry.isIntersecting ? "playing" : outState),
5273
+ { threshold: 0.1 }
5274
+ );
5275
+ observer.observe(el);
5276
+ return () => observer.disconnect();
5277
+ }, [startOn, outAction]);
5278
+ const goOut = () => setState(
5279
+ outAction === "reset" ? "idle" : outAction === "pause" ? "paused" : "playing"
5280
+ );
5281
+ const handlers = startOn === "mouseOver" ? { onMouseEnter: () => setState("playing"), onMouseLeave: goOut } : startOn === "click" ? { onClick: () => state === "playing" ? goOut() : setState("playing") } : {};
5282
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5283
+ "div",
5284
+ {
5285
+ ref,
5286
+ className: className + (state === "playing" ? "px-anim-enabled px-anim-playing" : state === "paused" ? "px-anim-enabled" : ""),
5287
+ style,
5288
+ ...handlers,
5289
+ children
5290
+ }
5291
+ );
5292
+ };
5293
+ var PixodeskSvgCssAnimator_default = PixodeskSvgCssAnimator;
3434
5294
  return __toCommonJS(index_exports);
3435
5295
  })();
3436
5296
  /*! Bundled license information: