@pixodesk/svg-animator-core 1.0.44 → 1.0.46
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/README.md +12 -0
- package/dist/{PxDocumentDiagnostic-n8yiyL0k.d.cts → PxDocumentDiagnostic-DX8w42-I.d.cts} +385 -228
- package/dist/{PxDocumentDiagnostic-n8yiyL0k.d.ts → PxDocumentDiagnostic-DX8w42-I.d.ts} +385 -228
- package/dist/{chunk-EFQLDGFY.js → chunk-37OFJ3RX.js} +2100 -134
- package/dist/chunk-37OFJ3RX.js.map +1 -0
- package/dist/chunk-K2732IVS.min.js +1 -0
- package/dist/index.cjs +250 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +12 -1877
- package/dist/index.js.map +1 -1
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.js +1 -1
- package/dist/internal.cjs +3344 -213
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +61 -5
- package/dist/internal.d.ts +61 -5
- package/dist/internal.js +55 -1
- package/dist/internal.js.map +1 -1
- package/dist/internal.min.cjs +1 -1
- package/dist/internal.min.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-EFQLDGFY.js.map +0 -1
- package/dist/chunk-YWBPS6E4.min.js +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/effects/transform/transformationEffect.ts","../src/effects/reference/contentRefSplit.ts","../src/effects/paint/gradientEffect.ts","../src/effects/clipping/clipPathEffect.ts","../src/effects/clipping/maskedByEffect.ts","../src/effects/reference/refEffect.ts","../src/effects/transform/repeaterEffect.ts","../src/effects/reference/retimeEffect.ts","../src/effects/stroke/strokeTrimEffect.ts","../src/effects/PlayerEffectsUtil.ts","../src/materialize/PxOffsetPathMaterializer.ts","../src/materialize/PxAnimatorUseMaterializer.ts","../src/materialize/PxAnimatorMaterializeAll.ts","../src/playback/PxPlaybackTime.ts","../src/playback/PxFrameLoop.ts","../src/playback/PxDiagnosticCode.ts","../src/playback/PxAnimatorConfigPatch.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport { keyframeWith, partsRecord, ReadKind, readAnimatable, TransformPart } from '../shared/transformParts';\nimport type { PxAnimatable, PxNode, PxTransformByEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\n\n\n/**\n * TRANSFORMATION → one `<g>` wrapper per part (translate/rotate/scale/skew),\n * with origin emitted as SEPARATE `+origin` / `-origin` wrappers flanking the\n * rotate/scale pair.\n *\n * Per-wrapper kfs:\n * - each wrapper carries ONE animatable quantity, so animated origin / rotate /\n * scale all play correctly per their own keyframe timelines — nothing is\n * baked into another part's wrapper.\n * - the `+o · ... · -o` sandwich pivots rotate+scale around the (possibly\n * animated) origin; translates compose flat (commute).\n *\n * Wrapper nesting (outer → inner):\n *\n * translate → +origin → rotate → scale → -origin → skew → element\n *\n * Composition: `M = translate(t) · +origin(t) · rotate(t) · scale(t) · -origin(t) · skew`\n * (skew is appended innermost and not composed here).\n */\nexport function applyTransformByEffect(node: PxNode, fx: PxTransformByEffect | undefined, ctx: ApplyContext): PxNode {\n if (!fx) return node;\n\n // The element's own `transform` string is the frame-0 baseline; wrappers\n // carry the full (animated) transform, so the baseline is dropped.\n delete node.transform;\n\n // Innermost first. Order outer→inner: [+o, t, -o, +o, r, skew, s, -o] for\n // auto-orient (translate carries motion-path rotation that must pivot around\n // origin too), else [t, +o, r, skew, s, -o] (translate composes flat).\n // Skew sits BETWEEN rotate and scale, inside the origin sandwich — the canonical\n // slot shared with the unified body transform and Lottie (see skew-support.plan.md).\n let n = node;\n n = wrapOrigin(n, fx.origin, /*invert=*/true); // -origin (r/k/s sandwich)\n n = wrapTransformPart(n, TransformPart.Scale, normalizeScale(fx.scale), ctx);\n n = wrapTransformPart(n, TransformPart.Skew, fx.skew, ctx);\n n = wrapTransformPart(n, TransformPart.Rotate, fx.rotate, ctx);\n n = wrapOrigin(n, fx.origin, /*invert=*/false); // +origin (r/k/s sandwich)\n\n if (translateHasAutoOrient(fx.translate)) {\n // Sandwich translate with its own +o/-o so the motion-path tangent\n // rotation built into the translate wrapper pivots around origin\n // (the `[+o, t_path_ao, -o]` branch).\n n = wrapOrigin(n, fx.origin, /*invert=*/true); // -origin (translate sandwich)\n n = wrapTransformPart(n, TransformPart.Translate, fx.translate, ctx);\n n = wrapOrigin(n, fx.origin, /*invert=*/false); // +origin (translate sandwich)\n } else {\n n = wrapTransformPart(n, TransformPart.Translate, fx.translate, ctx);\n }\n\n // The OUTERMOST wrapper takes the element's id (B4/A1): an id names the WHOLE\n // transformed unit — same law as `repeaterEffect`, the editor's heavy render,\n // and `ctx.idMap` (which already points at the outer wrapper). Leaving it on\n // the core makes a live `<use href>` / the maskedBy-generated `<use>` resolve to\n // the UNtransformed element in the DOM.\n if (n !== node && node.id) {\n n.id = node.id;\n delete node.id;\n }\n return n;\n}\n\n/** True when the translate animation carries motion-path tangent handles or\n * `autoOrient` — the path-tangent rotation needs origin-sandwich to pivot. */\nfunction translateHasAutoOrient(translate: PxAnimatable<PxVec2> | undefined): boolean {\n if (!translate || typeof translate !== 'object') return false;\n const obj = translate as { autoOrient?: boolean; keyframes?: Array<{ tangentOut?: PxVec2; tangentIn?: PxVec2 }> };\n if (obj.autoOrient) return true;\n return Array.isArray(obj.keyframes) && obj.keyframes.some(kf => kf.tangentOut || kf.tangentIn);\n}\n\n/**\n * The wire `effects.transformBy.scale` is a FACTOR (1.5 = 150%) in every form —\n * bare static, `{value:…}` and `{keyframes:…}` alike (one convention, see\n * dev-docs/schema-design.md I-3; the old bare-static PERCENT form is gone) — so no\n * normalization is needed any more. Kept as a named identity so the call site\n * still documents the convention decision.\n */\nfunction normalizeScale(raw: PxAnimatable<PxVec2> | undefined): PxAnimatable<PxVec2> | undefined {\n return raw;\n}\n\n/** Wraps `inner` in a `<g>` carrying a single transform part, static or animated. */\nfunction wrapTransformPart(\n inner: PxNode, part: TransformPart,\n raw: PxAnimatable<any> | undefined, ctx: ApplyContext\n): PxNode {\n if (raw === undefined) return inner;\n\n const v = readAnimatable<any>(raw);\n if (v.kind === ReadKind.Static) {\n return { type: 'g', transform: { value: partsRecord(part, v.value, undefined) }, children: [inner] };\n }\n if (v.kind === ReadKind.Animated) {\n const animTr: any = { keyframes: v.keyframes.map(kf => keyframeWith(kf, partsRecord(part, kf.value, undefined))) };\n if (v.autoOrient) animTr.autoOrient = true;\n if (v.loop !== undefined) animTr.loop = v.loop;\n return {\n type: 'g',\n animate: { transform: animTr },\n children: [inner],\n };\n }\n return inner;\n}\n\n/**\n * Wraps `inner` in a `<g translate>` that shifts by `+origin` (invert=false) or\n * `-origin` (invert=true). Origin is animatable — keyframes are carried through.\n *\n * Emitted as a `{translate}` PartsRecord (not the `{origin}` field) so a `+o`\n * wrapper is just a plain translate in the walker's eyes: the walker composes\n * the origin-sandwich rotation/scale around origin by stacking the wrappers,\n * NOT by reading `origin` off the rotate/scale wrapper's parts record.\n */\nfunction wrapOrigin(inner: PxNode, raw: PxAnimatable<PxVec2> | undefined, invert: boolean): PxNode {\n if (raw === undefined) return inner;\n const v = readAnimatable<PxVec2>(raw);\n const sign = (value: PxVec2): PxVec2 => invert ? [-value[0], -value[1]] : value;\n\n if (v.kind === ReadKind.Absent) return inner;\n if (v.kind === ReadKind.Static) {\n if (v.value[0] === 0 && v.value[1] === 0) return inner; // identity — skip\n return { type: 'g', transform: { value: { translate: sign(v.value) } }, children: [inner] };\n }\n if (v.kind === ReadKind.Animated) {\n const animTr: any = { keyframes: v.keyframes.map(kf => keyframeWith(kf, { translate: sign(kf.value as PxVec2) })) };\n if (v.loop !== undefined) animTr.loop = v.loop;\n return {\n type: 'g',\n animate: { transform: animTr },\n children: [inner],\n };\n }\n return inner;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * CONTENT-REF SOURCE SPLIT.\n *\n * When a `<use>` references another element with `clone:{without:'translate'}` it wants\n * to render the source EXCLUDING the source's own translate (and along-path\n * positioning for auto-orient). The heavy form achieves this by materializing the\n * source as a wrapper tree\n *\n * <g translate> ← outer, holds translate (+ along-path/origin for autoOrient)\n * <g rotate scale +o-o> ← inner, holds rotate/scale (origin sandwich)\n * <element /> ← bare shape, no transform\n * </g>\n * </g>\n *\n * and points the `<use>` at the INNER `<g>`. The use's reference therefore\n * renders rotate/scale around the use's position — never the source's translate.\n *\n * In the lightweight format the source is emitted as a flat element (translate\n * baked onto the body or into `effects.transformBy`). The applier's\n * `splitForContentRef` re-creates the multi-layer structure on the fly:\n * 1. extract translate parts from the source body (`transform` string,\n * `animate.transform.keyframes` PartsRecord) and from\n * `effects.transformBy` → outer wrapper\n * 2. keep rotate / scale (with origin sandwich) on the inner wrapper\n * 3. assign the ORIGINAL id to the outer, a fresh id to the inner\n * 4. `applyRefAndTransformationEffect` then rewrites the use's `href`\n * to the inner id (see `ctx.contentRefInnerIds`).\n *\n * Auto-orient / motion-path: when the translate animation carries tangent\n * handles (`tangentOut`/`tangentIn`) or `autoOrient`, the path tangent produces\n * a rotation at the OUTER level — so the origin moves to the outer too, so the\n * tangent rotation pivots around it (the `[+o, t_path_ao, -o]` branch).\n *\n * \"Always-split\" simplicity: even when a layer would be empty (e.g. source has\n * no rotate/scale), the inner wrapper is still emitted as an identity `<g>`.\n * The use needs a stable target regardless of which transform parts the source\n * carries, and an extra empty `<g>` is render-neutral.\n */\n\nimport { applyTransformByEffect } from '../transform/transformationEffect';\nimport type { PxAnimatable, PxAnimationDefinition, PxKeyframe, PxNode, PxTransformByEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\nimport { stripHash } from '../shared/util';\n\n\n/** Walks the tree and collects every id referenced by a `<use>` with `clone:{without:'translate'}`. */\nexport function identifyContentRefTargets(node: PxNode, ctx: ApplyContext, allocator: (key: string) => string): void {\n if (node.type === 'use' && node.effects?.clone?.without === 'translate') {\n const sourceId = stripHash(node.effects.clone.source); // `#id` canonical, bare legacy (E-5)\n if (typeof sourceId === 'string' && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {\n ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));\n }\n }\n node.children?.forEach(c => identifyContentRefTargets(c, ctx, allocator));\n}\n\n\n/**\n * Splits `node` into outer-translate + inner-rest + bare element. `node` is\n * mutated in place: its body translate is moved to the outer wrapper, leaving\n * the rotate/scale parts (and the element body) inside the inner wrapper.\n *\n * Outer wrapper gets `originalId` (so `idMap` and whole-element refs still\n * resolve correctly). Inner wrapper gets `innerId` (the use's `ref:content`\n * target). The returned node is the OUTER wrapper.\n */\nexport function splitForContentRef(\n node: PxNode,\n transformBy: PxTransformByEffect | undefined,\n originalId: string,\n innerId: string,\n ctx: ApplyContext,\n): PxNode {\n\n // 1. Lift translate parts off the body (`transform` string + `animate.transform`)\n // onto a fresh outer-side bag. After this, `node` no longer carries them.\n // `transformBy` is passed in so that when its `translate` part will go to\n // the outer wrapper, the body's baked-in translate baseline is stripped too\n // (otherwise it'd double-up against the outer wrapper's first keyframe).\n const outerBody = liftBodyTranslate(node, transformBy);\n\n // Strip the bare element's `id` — the outer wrapper takes ownership of `originalId`,\n // and the inner wrapper carries `innerId`. The element itself doesn't need either;\n // leaving it would create a duplicate of `originalId` in the materialized tree.\n if (typeof node.id === 'string') delete node.id;\n\n // 2. Split `effects.transformBy` between outer (translate, plus origin for\n // auto-orient/motion-path) and inner (rotate / scale, with origin sandwich).\n const { outer: outerTr, inner: innerTr } = splitTransformByEffect(transformBy);\n\n // 3. Inner: apply inner transformation around the original element, then wrap\n // in an identity `<g>` with `innerId`. The extra wrapper guarantees the use\n // has a stable target regardless of what parts were present.\n let innerNode: PxNode = node;\n innerNode = applyTransformByEffect(innerNode, innerTr, ctx);\n const innerWrapper: PxNode = { type: 'g', id: innerId, children: [innerNode] };\n\n // 4. Outer: build a `<g>` carrying the lifted body translate, with `innerWrapper`\n // as its child. Then apply outer transformation (translate). Move id to the\n // outermost so whole-element refs still resolve to the full source.\n let outerWrapper: PxNode = { type: 'g', id: originalId, children: [innerWrapper] };\n if (outerBody.transform !== undefined) outerWrapper.transform = outerBody.transform;\n if (outerBody.animate !== undefined) outerWrapper.animate = outerBody.animate;\n if (outerTr) {\n delete outerWrapper.id;\n outerWrapper = applyTransformByEffect(outerWrapper, outerTr, ctx);\n outerWrapper.id = originalId;\n }\n return outerWrapper;\n}\n\n\n////////////////////////////////////////////////////////////////\n//// Body lifting (transform string + animate.transform)\n////////////////////////////////////////////////////////////////\n\ninterface OuterBody {\n transform?: string | { value?: any; keyframes?: Array<any> };\n animate?: { transform: any };\n}\n\n/** Removes translate parts from `node.transform` (string) and from `node.animate.transform`,\n * returning what was extracted (to live on the outer wrapper). When either\n * animate.transform is lifted OR `effects.transformBy.translate` will be lifted\n * via the outer wrapper, the body `transform` translate becomes a redundant t=0\n * baseline and is stripped from `node` (otherwise it'd compose on top of the\n * outer's first keyframe / static translate). */\nfunction liftBodyTranslate(node: PxNode, transformBy: PxTransformByEffect | undefined): OuterBody {\n const out: OuterBody = {};\n\n // 1. animate.transform — extract translate from every keyframe value.\n // `autoOrient` and `tangentOut`/`tangentIn` are motion-path metadata that\n // belong to translate; they go to the OUTER wrapper with translate, never\n // to the inner one (the inner has rotate/scale only).\n let didLiftAnimate = false;\n // In-place animations on a node body are always the record form\n // (`{propName: PxPropertyAnimation}`) at this point in the pipeline —\n // narrow the `PxElementAnimation` union accordingly.\n const animTr = (node.animate as PxAnimationDefinition | undefined)?.transform;\n if (animTr && typeof animTr === 'object' && Array.isArray(animTr.keyframes)) {\n const kfs: Array<PxKeyframe<any>> = animTr.keyframes;\n const hasTranslate = kfs.some(kf => kf.value && (kf.value as any).translate);\n if (hasTranslate) {\n const outerHasOrigin = needsOriginOnOuter(animTr as PxAnimatable<PxVec2>);\n const outerKfs = kfs.map(kf => {\n const v = (kf.value || {}) as any;\n const newValue: any = {};\n if (v.translate !== undefined) newValue.translate = v.translate;\n if (outerHasOrigin && v.origin !== undefined) newValue.origin = v.origin;\n const outerKf: any = { value: newValue };\n if (kf.time !== undefined) outerKf.time = kf.time;\n if (kf.easing !== undefined) outerKf.easing = kf.easing;\n if ((kf as any).tangentOut !== undefined) outerKf.tangentOut = (kf as any).tangentOut;\n if ((kf as any).tangentIn !== undefined) outerKf.tangentIn = (kf as any).tangentIn;\n return outerKf;\n });\n const outerAnimTr: any = { keyframes: outerKfs };\n if ((animTr as any).autoOrient) outerAnimTr.autoOrient = true;\n // Forward `loop` so the split outer/inner halves keep the source\n // alternate/cycle semantics; otherwise lifting a transform with\n // `loop.alternate` silently drops the loop on translate side.\n const srcLoop = (animTr as any).loop;\n if (srcLoop !== undefined) outerAnimTr.loop = srcLoop;\n out.animate = { transform: outerAnimTr };\n\n const innerHasPivotedPart = kfs.some(kf => {\n const v = (kf.value || {}) as any;\n return v.rotate !== undefined || v.scale !== undefined;\n });\n const innerKfs = kfs.map(kf => {\n const v = (kf.value || {}) as any;\n const newValue: any = {};\n if (v.rotate !== undefined) newValue.rotate = v.rotate;\n if (v.scale !== undefined) newValue.scale = v.scale;\n // Origin lives on inner kfs whenever rotate/scale need a pivot —\n // a separate origin sandwich at the inner layer (even when origin\n // is also on outer for the auto-orient sandwich).\n if (v.origin !== undefined && (!outerHasOrigin || innerHasPivotedPart)) newValue.origin = v.origin;\n // Inner kf intentionally drops tangentOut/tangentIn (translate-only).\n const innerKf: any = { value: newValue };\n if (kf.time !== undefined) innerKf.time = kf.time;\n if (kf.easing !== undefined) innerKf.easing = kf.easing;\n return innerKf;\n });\n const allInnerEmpty = innerKfs.every(kf => Object.keys(kf.value as object).length === 0);\n if (allInnerEmpty) {\n delete (node.animate as any).transform;\n if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;\n } else {\n // Inner animate keeps non-translate kfs; autoOrient flag is intentionally dropped.\n // `loop` is forwarded so the inner rotate/scale half also alternates/cycles.\n const innerAnimTr: any = { keyframes: innerKfs };\n if (srcLoop !== undefined) innerAnimTr.loop = srcLoop;\n (node.animate as any).transform = innerAnimTr;\n }\n didLiftAnimate = true;\n }\n }\n\n // 2. Body `transform` — the composed STRING (pre-rendered forms / legacy /\n // foreign SVG) or the STRUCTURED STATIC `{value: partsRecord}` (the\n // lightweight wire since SCHEMA-DESIGN S1). `{keyframes}` bodies come\n // from the writer's transformation-effect path and are handled via\n // `effects.transformBy`, not here.\n const transformationHasTranslate = transformBy?.translate !== undefined;\n const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;\n // Autoorient / motion-path lift: when the lifted animate.transform carries\n // tangents or `autoOrient`, the body baseline is the FULL t=0 matrix\n // (translate × path-tangent rotation), not just a translate. The outer\n // wrapper recomputes both at every frame (including t=0) via the lifted\n // keyframes + autoOrient, so the body baseline is redundant and would\n // double-apply on top of it.\n const liftedAnimateIsAutoOriented = didLiftAnimate && needsOriginOnOuter((node.animate as PxAnimationDefinition | undefined)?.transform as any || undefined)\n || didLiftAnimate && needsOriginOnOuter((out.animate as PxAnimationDefinition | undefined)?.transform as any || undefined);\n if (typeof node.transform === 'string') {\n const split = splitTransformString(node.transform);\n if (stripBodyTranslateOnly) {\n // Outer wrapper will carry the translate (via animate.transform or\n // effects.transformBy); the body string is just a t=0 baseline.\n // Strip body translates so they don't double-up.\n if (split.translate !== undefined) {\n if (split.rest) node.transform = split.rest;\n else delete node.transform;\n } else if (isPureTranslateBody(node.transform)) {\n // Body is a single `matrix(...)` representing pure translate — same\n // redundancy as a `translate(...)` string. Wipe.\n delete node.transform;\n } else if (liftedAnimateIsAutoOriented && isSingleMatrixBody(node.transform)) {\n // Body is a non-pure `matrix(…)` — the autoOrient-materialized t=0\n // value baked by the writer. The outer wrapper reproduces it via\n // `animate.transform` + `autoOrient`; wipe to avoid double-apply.\n delete node.transform;\n }\n } else if (split.translate) {\n out.transform = split.translate;\n if (split.rest) node.transform = split.rest;\n else delete node.transform;\n }\n } else if (node.transform && typeof node.transform === 'object' && !Array.isArray(node.transform)\n && !(node.transform as any).keyframes) {\n // STATIC RECORD — bare `{translate, …}` (canonical) or the legacy\n // `{value: {…}}` wrapper; the rewrite keeps the incoming spelling.\n const wrapped = (node.transform as { value?: Record<string, unknown> }).value;\n const isWrapped = !!(wrapped && typeof wrapped === 'object');\n const value = (isWrapped ? wrapped : node.transform) as Record<string, unknown>;\n const rewrap = (rec: Record<string, unknown>) => (isWrapped ? { value: rec } : rec) as any;\n if (Array.isArray((value as any).translate)) {\n const rest: Record<string, unknown> = { ...value };\n delete rest.translate;\n const hasRest = Object.keys(rest).length > 0;\n if (stripBodyTranslateOnly) {\n // Same redundancy rule as the string branch: the outer wrapper\n // carries the translate; drop the body's copy.\n if (hasRest) node.transform = rewrap(rest);\n else delete node.transform;\n } else {\n out.transform = rewrap({ translate: (value as any).translate });\n if (hasRest) node.transform = rewrap(rest);\n else delete node.transform;\n }\n }\n }\n\n return out;\n}\n\n/** True when the body string is a single `matrix(...)` op (any 6-arg matrix —\n * pure-translate is a more specific case handled by `isPureTranslateBody`). */\nfunction isSingleMatrixBody(s: string): boolean {\n const re = /(translate|rotate|scale|matrix|skewX|skewY)\\(([^)]*)\\)/g;\n let count = 0;\n let isMatrix = false;\n let m: RegExpExecArray | null;\n while ((m = re.exec(s))) {\n count++;\n if (m[1] === 'matrix') isMatrix = true;\n }\n return count === 1 && isMatrix;\n}\n\n/** True when the body transform is a single op equivalent to pure translate\n * (either `translate(...)` or a `matrix(1,0,0,1,e,f)`). Used to decide whether\n * to wipe the body when `animate.transform.translate` has already been lifted. */\nfunction isPureTranslateBody(s: string): boolean {\n const re = /(translate|rotate|scale|matrix|skewX|skewY)\\(([^)]*)\\)/g;\n const ops: Array<{ name: string; full: string }> = [];\n let m: RegExpExecArray | null;\n while ((m = re.exec(s))) ops.push({ name: m[1], full: m[0] });\n if (ops.length !== 1) return false;\n if (ops[0].name === 'translate') return true;\n if (ops[0].name !== 'matrix') return false;\n const args = /matrix\\(([^)]*)\\)/.exec(ops[0].full);\n if (!args) return false;\n const nums = args[1].split(/[\\s,]+/).filter(Boolean).map(Number);\n return nums.length >= 4 && nums[0] === 1 && nums[1] === 0 && nums[2] === 0 && nums[3] === 1;\n}\n\n/**\n * Body-string lift heuristic. The body `transform=\"\"` is the t=0 baseline that\n * the WRITER bakes in the canonical order `translate · +origin · rotate · scale · -origin`.\n *\n * Lift LEADING `translate(...)` ops ONLY when the string does NOT end with a\n * `translate(...)`. A trailing translate is the `-origin` half of an origin\n * sandwich — leaving any translate inside the sandwich would break the pivot,\n * so we leave the whole body alone in that case (the corresponding\n * `effects.transformBy` is the structured source we lift from instead).\n *\n * (`matrix(...)` and `skewX/Y` are not lifted — they don't carry \"user\n * translate\" semantics. If the leading op isn't `translate`, nothing is lifted.)\n */\nfunction splitTransformString(s: string): { translate?: string; rest?: string } {\n const ops: Array<{ name: string; full: string }> = [];\n const re = /(translate|rotate|scale|matrix|skewX|skewY)\\(([^)]*)\\)/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(s))) ops.push({ name: m[1], full: m[0] });\n\n if (!ops.length) return { rest: s || undefined };\n\n // All-translate string — no origin-sandwich without rotate/scale in the\n // middle. Lift everything.\n if (ops.every(o => o.name === 'translate')) {\n return { translate: ops.map(o => o.full).join('') };\n }\n\n const leading = ops[0];\n const trailing = ops[ops.length - 1];\n\n // Origin sandwich: writer's canonical order is `translate · +origin ·\n // rotate · scale · -origin`, with `translate + +origin` typically FUSED\n // into one leading translate. We can recover the user-translate by reading\n // the trailing `-origin` value and subtracting `+origin` from the leading.\n if (trailing.name === 'translate' && leading.name === 'translate') {\n const trailingVec = parseTranslateArgs(trailing.full);\n const leadingVec = parseTranslateArgs(leading.full);\n const ox = -trailingVec[0]; // -origin → +origin\n const oy = -trailingVec[1];\n const userTx = leadingVec[0] - ox;\n const userTy = leadingVec[1] - oy;\n\n // Pure origin sandwich (user-translate cancels to 0) — leave the body alone.\n if (userTx === 0 && userTy === 0) return { rest: s };\n\n // Replace the leading translate with the bare `+origin` (=ox,oy), keep\n // the rest of the sandwich unchanged, and lift `translate(userT)`.\n const middleAndTrailing = 'translate(' + ox + ',' + oy + ')' + ops.slice(1).map(o => o.full).join('');\n return { translate: 'translate(' + userTx + ',' + userTy + ')', rest: middleAndTrailing };\n }\n\n // Trailing translate without a matching leading translate — unusual. Be\n // conservative and don't lift.\n if (trailing.name === 'translate') return { rest: s };\n\n // No trailing translate → no origin sandwich; lift all leading translates.\n const lifted: Array<string> = [];\n let i = 0;\n while (i < ops.length && ops[i].name === 'translate') {\n lifted.push(ops[i].full);\n i++;\n }\n if (!lifted.length) return { rest: s };\n\n const rest = ops.slice(i).map(o => o.full).join('');\n return {\n translate: lifted.join(''),\n rest: rest || undefined,\n };\n}\n\nfunction parseTranslateArgs(translateOp: string): [number, number] {\n const m = /translate\\(([^)]*)\\)/.exec(translateOp);\n if (!m) return [0, 0];\n const nums = m[1].split(/[\\s,]+/).filter(Boolean).map(Number);\n return [nums[0] || 0, nums[1] || 0];\n}\n\n\n////////////////////////////////////////////////////////////////\n//// effects.transformBy split\n////////////////////////////////////////////////////////////////\n\nfunction splitTransformByEffect(fx: PxTransformByEffect | undefined): {\n outer?: PxTransformByEffect;\n inner?: PxTransformByEffect;\n} {\n if (!fx) return {};\n const originOnOuter = needsOriginOnOuter(fx.translate);\n const innerHasPivotedPart = fx.rotate !== undefined || fx.scale !== undefined;\n\n const outer: PxTransformByEffect = {};\n const inner: PxTransformByEffect = {};\n\n if (fx.translate !== undefined) outer.translate = fx.translate;\n // Origin lives on outer when translate carries auto-orient (so the\n // path-tangent rotation pivots around origin too).\n if (originOnOuter && fx.origin !== undefined) outer.origin = fx.origin;\n\n if (fx.rotate !== undefined) inner.rotate = fx.rotate;\n if (fx.scale !== undefined) inner.scale = fx.scale;\n if (fx.skew !== undefined) inner.skew = fx.skew;\n // Origin also lives on inner whenever rotate/scale need a pivot — duplicate\n // origin across outer + inner is fine: two separate origin sandwiches\n // ([+o, t, -o] outer + [+o, r, s, -o] inner).\n if (fx.origin !== undefined && (!originOnOuter || innerHasPivotedPart)) inner.origin = fx.origin;\n\n return {\n outer: Object.keys(outer).length ? outer : undefined,\n inner: Object.keys(inner).length ? inner : undefined,\n };\n}\n\n\n////////////////////////////////////////////////////////////////\n//// Auto-orient / motion-path detection\n////////////////////////////////////////////////////////////////\n\n/** True when the translate animation produces rotation at the outer level\n * (tangent handles or `autoOrient`) — meaning origin must sit on the outer\n * with translate so the path-tangent rotation pivots around it. */\nfunction needsOriginOnOuter(translateAnim: PxAnimatable<PxVec2> | undefined): boolean {\n if (!translateAnim || typeof translateAnim !== 'object') return false;\n const obj = translateAnim as { autoOrient?: boolean; keyframes?: Array<PxKeyframe<PxVec2> & { tangentOut?: PxVec2; tangentIn?: PxVec2 }> };\n if (obj.autoOrient) return true;\n if (Array.isArray(obj.keyframes)) {\n return obj.keyframes.some(kf => kf.tangentOut || kf.tangentIn);\n }\n return false;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport type { PxAnimatable, PxFillGradientEffect, PxGradientStop, PxKeyframe, PxLoop, PxNode, PxStrokeGradientEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport { PxGradientType } from '../../format/PxAnimatorConstants';\nimport { ReadKind, readAnimatable, writeAnimatableChannel } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\nimport { genId } from '../shared/util';\nimport { keyframeTime, keyframeValue, keyframeEasing } from '../../format/PxAnimatorTypes';\n\n\n/**\n * `effects.fillGradient` / `effects.strokeGradient` materializer.\n *\n * WHY AN EFFECT (not a `fill` value): no value of `fill` IS a gradient — the\n * browser can only render one through a `<linearGradient>`/`<radialGradient>`\n * def with `<stop>` children plus a `url(#id)` indirection. Per the format's\n * attribute-vs-effect law (see `_PxEffects`), anything that requires generating\n * structure is an effect; flat `fill` colors stay plain animated attributes.\n *\n * Mirrors `maskedByEffect`: generate a `<linearGradient>` or `<radialGradient>`\n * def into `ctx.defs`, push the host element's `fill` / `stroke` to\n * `url(#auto-id)`. Same shape used for both fill and stroke — the only\n * difference is which host attribute is rewritten.\n *\n * The wire shape is a gradient as one animatable stop timeline + static geometry\n * (see `_PxFillGradientEffect`). When materializing:\n * - geometry parts (`start`, `end`, `center`, `radius`, `focal`) become static body attrs\n * on the gradient def;\n * - the stops array is either static (each `<stop>` is bare) or animated\n * (each `<stop>` gets `animate.stopColor.keyframes` derived from the\n * single source timeline by SLICING each kf's full snapshot at this\n * stop's index).\n *\n * The per-stop slicing produces the standard `<linearGradient>` + `<stop>`\n * def chain, so the materialized tree round-trips through the usual reader.\n */\nexport function applyFillGradientEffect(node: PxNode, fx: PxFillGradientEffect | undefined, ctx: ApplyContext): PxNode {\n return applyGradient(node, fx, ctx, 'fill');\n}\n\nexport function applyStrokeGradientEffect(node: PxNode, fx: PxStrokeGradientEffect | undefined, ctx: ApplyContext): PxNode {\n return applyGradient(node, fx, ctx, 'stroke');\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internals\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction applyGradient(node: PxNode, fx: PxFillGradientEffect | undefined, ctx: ApplyContext, attr: 'fill' | 'stroke'): PxNode {\n if (!fx) return node;\n\n const id = genId(ctx, 'grad');\n const def = synthesiseGradientDef(fx, id, ctx);\n ctx.defs.push(def);\n node[attr] = 'url(#' + id + ')';\n return node;\n}\n\nfunction synthesiseGradientDef(fx: PxFillGradientEffect, id: string, ctx: ApplyContext): PxNode {\n const out: PxNode = {\n type: fx.type === PxGradientType.radial ? 'radialGradient' : 'linearGradient',\n id,\n };\n\n // Geometry — standard animatable slots. Static → body attrs; animated → the\n // def node's own `animate` channels under the real SVG attr names (a vec slot\n // splits into its two axis channels here, in the applier — the wire stays\n // `start: {keyframes:[{value:[x,y]}…]}`). The frames engine then drives the\n // def's attrs exactly like the stops' `stopColor` (CSS/WAAPI can't animate\n // gradient geometry, but this materializer feeds the JS frame loop).\n if (fx.type === PxGradientType.linear) {\n applyGeomVec(out, 'x1', 'y1', fx.start);\n applyGeomVec(out, 'x2', 'y2', fx.end);\n } else {\n applyGeomVec(out, 'cx', 'cy', fx.center);\n applyGeomNumber(out, 'r', fx.radius);\n applyGeomVec(out, 'fx', 'fy', fx.focal);\n }\n if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;\n if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;\n if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;\n\n // (The old per-scalar `fx.animate.gradientX1…` channels are NOT read any\n // more — geometry animates on the slots above. Backward compat dropped\n // deliberately; a leftover `animate` key is flagged by strict validation.)\n\n out.children = buildStopChildren(fx.stops, ctx);\n return out;\n}\n\n/** Animatable PxVec2 geometry slot → static `xAttr`/`yAttr` body attrs, or two\n * per-axis `animate` channels (times/easings preserved, `loop` carried) plus\n * static baseline attrs from the base/first kf. */\nfunction applyGeomVec(out: PxNode, xAttr: string, yAttr: string, raw: PxAnimatable<PxVec2> | undefined): void {\n const read = readAnimatable<PxVec2>(raw);\n if (read.kind === ReadKind.Absent) return;\n if (read.kind === ReadKind.Static) {\n out[xAttr] = String(read.value[0]);\n out[yAttr] = String(read.value[1]);\n return;\n }\n const axisChannel = (idx: 0 | 1): { keyframes: Array<PxKeyframe>; loop?: PxLoop | boolean } => {\n const block: { keyframes: Array<PxKeyframe>; loop?: PxLoop | boolean } = {\n keyframes: read.keyframes.map(kf => {\n const axisKf: PxKeyframe = { time: kf.time, value: Array.isArray(kf.value) ? kf.value[idx] : undefined };\n if (kf.easing !== undefined) axisKf.easing = kf.easing;\n return axisKf;\n }),\n };\n if (read.loop !== undefined) block.loop = read.loop;\n return block;\n };\n const animate = (out.animate as Record<string, unknown> | undefined) ?? {};\n animate[xAttr] = axisChannel(0);\n animate[yAttr] = axisChannel(1);\n out.animate = animate as PxNode['animate'];\n const baseline = read.base ?? read.keyframes[0]?.value;\n if (Array.isArray(baseline)) {\n out[xAttr] = String(baseline[0]);\n out[yAttr] = String(baseline[1]);\n }\n}\n\n/** Animatable number geometry slot (radial `r`) → static attr or `animate` channel. */\nfunction applyGeomNumber(out: PxNode, attrName: string, raw: PxAnimatable<number> | undefined): void {\n const read = readAnimatable<number>(raw);\n if (read.kind === ReadKind.Absent) return;\n writeAnimatableChannel(out, attrName, read, { asString: true });\n}\n\n/** Emits one `<stop>` per gradient stop. Stops come from EITHER the static\n * array form (`stops: [{offset, color}, …]`) or the animated form\n * (`stops: {keyframes:[{time, value:[stops], easing?}]}`). For the\n * animated form, each emitted `<stop>` gets `animate.stopColor.keyframes`\n * whose values are sliced out of the source timeline at this stop's\n * index — the standard `<stop>` def chain. */\nfunction buildStopChildren(stops: PxAnimatable<Array<PxGradientStop>> | undefined, ctx: ApplyContext): Array<PxNode> {\n if (!stops) return [];\n\n // Shared reader — bare array / `{value: […]}` statics, `{keyframes|kfs, loop?}` animated.\n const read = readAnimatable<Array<PxGradientStop>>(stops);\n if (read.kind === ReadKind.Absent) return [];\n if (read.kind === ReadKind.Static) return Array.isArray(read.value) ? read.value.map(staticStopNode) : [];\n\n const kfs = read.keyframes as Array<PxKeyframe>;\n if (!kfs.length) return [];\n // Per-stop animations inherit the timeline-level `loop` (alternate/cycle/etc.).\n // Without forwarding it, animating a gradient with `loop.alternate:true`\n // would slice each stop's colors into separate `animate.stopColor`\n // entries that lose the loop config → no reversal past the last kf,\n // even though every non-gradient animatable property loops fine. See\n // also: the gradient stop \"slice\" docstring above.\n const loopFromSource = read.loop;\n\n // Stop count: take the LARGEST across kfs (constraint says constant\n // count, but defensive — when missing, hold the last value).\n let stopCount = 0;\n for (const kf of kfs) {\n const v = keyframeValue(kf) as Array<PxGradientStop> | undefined;\n if (Array.isArray(v) && v.length > stopCount) stopCount = v.length;\n }\n if (!stopCount) return [];\n\n // Baseline stop info from kf[0] — offsets stay fixed across kfs, only\n // colors animate; offset rarely animates but if it does we sample at\n // each kf.\n const firstKfValue = keyframeValue(kfs[0]) as Array<PxGradientStop> | undefined;\n const baselineStops: Array<PxGradientStop> = [];\n for (let i = 0; i < stopCount; i++) {\n const s = firstKfValue?.[i] ?? prevDefinedStop(kfs, 0, i) ?? { offset: i / Math.max(1, stopCount - 1), color: '#000000' };\n baselineStops.push({ offset: s.offset, color: s.color });\n }\n\n return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx, loopFromSource));\n}\n\nfunction staticStopNode(s: PxGradientStop): PxNode {\n return {\n type: 'stop',\n offset: formatOffset(s.offset),\n stopColor: s.color,\n };\n}\n\nfunction animatedStopNode(baseline: PxGradientStop, kfs: Array<PxKeyframe>, stopIdx: number, _ctx: ApplyContext, loop: PxLoop | boolean | undefined): PxNode {\n const colorKfs: Array<PxKeyframe> = [];\n const offsetKfs: Array<PxKeyframe> = [];\n // Only emit an `offset` timeline when the offset actually moves across\n // kfs — most gradients animate color only, and a static offset attr is\n // cheaper than a runtime binding that recomputes the same value.\n let offsetVaries = false;\n for (const kf of kfs) {\n const t = keyframeTime(kf);\n const arr = keyframeValue(kf) as Array<PxGradientStop> | undefined;\n const sliced = arr?.[stopIdx] ?? prevDefinedStop(kfs, kfs.indexOf(kf), stopIdx);\n if (!sliced) continue;\n const easing = keyframeEasing(kf);\n\n const colorOut: PxKeyframe = { time: t, value: sliced.color };\n if (easing !== undefined) colorOut.easing = easing;\n colorKfs.push(colorOut);\n\n // Offset is a unitless 0..1 fraction (SVG `<stop offset>` accepts it\n // bare); the runtime interpolates it as a numeric attr.\n const offsetOut: PxKeyframe = { time: t, value: sliced.offset };\n if (easing !== undefined) offsetOut.easing = easing;\n offsetKfs.push(offsetOut);\n if (sliced.offset !== baseline.offset) offsetVaries = true;\n }\n\n const stop: PxNode = {\n type: 'stop',\n offset: formatOffset(baseline.offset),\n stopColor: baseline.color,\n };\n const animate: { [k: string]: { keyframes: Array<PxKeyframe>, loop?: PxLoop | boolean } } = {};\n if (colorKfs.length) {\n animate.stopColor = { keyframes: colorKfs };\n if (loop !== undefined) animate.stopColor.loop = loop;\n }\n if (offsetVaries && offsetKfs.length) {\n animate.offset = { keyframes: offsetKfs };\n if (loop !== undefined) animate.offset.loop = loop;\n }\n if (Object.keys(animate).length) stop.animate = animate;\n return stop;\n}\n\n/** Walks backwards from `fromIdx` looking for a kf whose stops array has\n * an entry at `stopIdx`. Used when a kf's stops array is shorter than the\n * global stop count (shouldn't happen if writer respects the constraint,\n * but degrades gracefully). */\nfunction prevDefinedStop(kfs: Array<PxKeyframe>, fromIdx: number, stopIdx: number): PxGradientStop | undefined {\n for (let i = fromIdx; i >= 0; i--) {\n const arr = keyframeValue(kfs[i]) as Array<PxGradientStop> | undefined;\n if (arr?.[stopIdx]) return arr[stopIdx];\n }\n for (let i = fromIdx + 1; i < kfs.length; i++) {\n const arr = keyframeValue(kfs[i]) as Array<PxGradientStop> | undefined;\n if (arr?.[stopIdx]) return arr[stopIdx];\n }\n return undefined;\n}\n\n/** `0.5` → `\"50%\"`; `1` → `\"100%\"`; `0.123` → `\"12.3%\"`. Matches SVG\n * convention for `<stop offset>`. */\nfunction formatOffset(o: number): string {\n const pct = Math.round(o * 1000) / 10;\n return pct + '%';\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport type { PxClipPathEffect, PxNode } from '../../format/PxAnimatorTypes';\nimport { ReadKind, readAnimatable, writeAnimatableChannel } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\nimport { genId } from '../shared/util';\n\n\n/**\n * CLIP-PATH → a `<clipPath>` in defs holding a single `<path>` built from the effect's\n * `d`, referenced via `clip-path=\"url(#…)\"` on the host element.\n *\n * Materializer pattern, mirroring `applyMaskedByEffect` (generate a def, set a URL ref on the\n * node) but far simpler — the clip geometry is a self-contained vector path (no ancestor\n * transform compensation, no source lookup).\n *\n * `pathData` is a standard animatable slot (static string / `{value}` / `{keyframes}` with\n * `{pathData}` values — same grammar as the body `d` attribute). An animated slot becomes the child\n * `<path>`'s `animate.d` block. The def is spliced into the walked tree, so `collectIds`\n * auto-assigns the animated path an id and the frame loop rewrites its `d` per frame.\n * `clip-path` is a live reference (verified: the browser re-clips on every `d` change\n * across SMIL/CSS/JS/WAAPI), so the clip animates without any per-frame re-binding on\n * the host.\n *\n */\nexport function applyClipPathEffect(\n node: PxNode,\n fx: PxClipPathEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n if (!fx?.pathData) return node;\n\n const clipId = genId(ctx, 'clip');\n const pathChild: PxNode = { type: 'path' };\n const read = readAnimatable<string>(fx.pathData);\n if (read.kind !== ReadKind.Absent) {\n // Static values may be the bare path string or a `{pathData}` object (the kf\n // value encoding) — normalize to the string for the body attr.\n if (read.kind === ReadKind.Static) {\n pathChild.d = pathString(read.value);\n } else {\n writeAnimatableChannel(pathChild, 'd', read);\n if (pathChild.d !== undefined) pathChild.d = pathString(pathChild.d as unknown);\n }\n }\n ctx.defs.push({ type: 'clipPath', id: clipId, children: [pathChild] });\n node.clipPath = 'url(#' + clipId + ')';\n return node;\n}\n\n/** Unwraps a `{pathData: \"M…\"}` kf-value object to its string; passes strings through. */\nfunction pathString(v: unknown): string | undefined {\n if (typeof v === 'string') return v;\n if (v && typeof v === 'object' && typeof (v as { pathData?: unknown }).pathData === 'string') return (v as { pathData: string }).pathData;\n return undefined;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport type { PxAnimatable, PxMaskedByEffect, PxNode, PxTransformByEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport { keyframeWith, partsRecord, ReadKind, readAnimatable, readStaticOrigin, TransformPart } from '../shared/transformParts';\nimport type { ApplyContext, MaskAncestorTransform } from '../shared/types';\nimport { genId, stripHash } from '../shared/util';\n\n\n/**\n * MASKED-BY → a `<mask>` in defs holding the source `<use>` wrapped in\n * (forward of mask source's ancestors) · (inverse of masked element's full\n * transform chain) · (inverse of masked element's own `effects.transformBy`)\n *\n * The mask source must paint at its ORIGINAL world position, but\n * `maskUnits=\"userSpaceOnUse\"` (default) interprets the mask in the masked\n * element's local coord system. The wrapper sequence above first cancels\n * out the masked element's accumulated ancestor / own transforms, then\n * re-applies the mask source's ancestor transforms so the `<use>` ends up\n * at the same world matrix it would have if rendered in place.\n *\n * Composes the masked element's own transform with the mask source's, so the\n * mask renders at the right world matrix.\n *\n * Implementation note — this first cut only composes TRANSLATE parts (static\n * and animated). Rotate / scale on the ancestor chains aren't supported yet\n * and will warn if present. `effects.transformBy` on the masked element\n * keeps working (passed in as `transformBy` and inverted separately).\n */\nexport function applyMaskedByEffect(\n node: PxNode,\n fx: PxMaskedByEffect | undefined,\n transformBy: PxTransformByEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n if (!fx) return node;\n // Canonical ref spelling is `#id` (SCHEMA-DESIGN §4 E-5); bare `id` is legacy.\n const sourceId = stripHash(fx.source);\n if (!sourceId) { ctx.errors.push('maskedBy.source missing — cannot build mask'); return node; }\n\n const maskId = genId(ctx, 'mask');\n\n let content: PxNode = { type: 'use', href: '#' + sourceId };\n // `effects.transformBy` (split-timing form) gets full per-part inversion\n // through `wrapInverseTransform`. With no `effects.transformBy` the\n // masked element's transform lives on the body — invert the STATIC part\n // (`node.transform` string) via the same per-part machinery, then on top\n // emit an animated inverse wrapper for `node.animate.transform.keyframes`\n // (per-kf parts-record inversion). Either way, the ancestor-chain\n // compensation runs on top with translate-only composition for now.\n if (transformBy) {\n content = wrapInverseTransform(content, transformBy, ctx);\n } else if (hasAnimateTransform(node)) {\n // `animate.transform` overrides `node.transform` per-frame in the\n // visualModel, so inverting BOTH would double-count. Animated wins.\n content = wrapInverseAnimatedBodyTransform(content, node, ctx);\n } else {\n const bodyStatic = readTransformationFromBody(node);\n if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);\n }\n // Skip `targetOwn` translate when we already inverted the body (otherwise\n // the translate would be subtracted twice).\n const includeTargetOwn = transformBy === undefined && !nodeHasBodyTransform(node);\n content = wrapAncestorChainCompensation(content, node, sourceId, ctx, includeTargetOwn);\n\n const mask: PxNode = { type: 'mask', id: maskId, children: [content] };\n if (fx.maskType) mask.maskType = fx.maskType;\n if (fx.maskUnits) mask.maskUnits = fx.maskUnits;\n if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;\n // Explicit mask viewport (`x/y/width/height` in `maskUnits` space). Absent →\n // SVG's implicit −10%…120% region, same as the editor's defaults.\n if (fx.x !== undefined) mask.x = String(fx.x);\n if (fx.y !== undefined) mask.y = String(fx.y);\n if (fx.width !== undefined) mask.width = String(fx.width);\n if (fx.height !== undefined) mask.height = String(fx.height);\n ctx.defs.push(mask);\n\n node.mask = 'url(#' + maskId + ')';\n return node;\n}\n\n/** Wraps `inner` in inverse-transform `<g>`s built from the masked element's\n * own `effects.transformBy` payload (translate / rotate / scale). The\n * ancestor-chain wrappers are added separately by\n * `wrapAncestorChainCompensation`. */\nfunction wrapInverseTransform(inner: PxNode, fx: PxTransformByEffect | undefined, ctx: ApplyContext): PxNode {\n if (!fx) return inner;\n const origin = readStaticOrigin(fx.origin, ctx);\n\n let n = inner;\n n = wrapInversePart(n, TransformPart.Translate, fx.translate, undefined, ctx);\n n = wrapInversePart(n, TransformPart.Rotate, fx.rotate, origin, ctx);\n n = wrapInversePart(n, TransformPart.Scale, fx.scale, origin, ctx);\n return n;\n}\n\nfunction wrapInversePart(\n inner: PxNode, part: TransformPart,\n raw: PxAnimatable<any> | undefined, origin: PxVec2 | undefined, ctx: ApplyContext\n): PxNode {\n if (raw === undefined) return inner;\n // `fx.scale` in BARE-ARRAY form is PERCENT (150 = 1.5×), matching\n // `applyTransformByEffect`'s forward `normalizeScale`. Convert to\n // 1.0-units before reading, so `invertPartValue([1.5,1.5])` produces\n // the right `[2/3, 2/3]` instead of `[1/150, 1/150]`. Keyframe / {value}\n // forms already use 1.0-units per the wire convention.\n const normalizedRaw: PxAnimatable<any> | undefined = (part === TransformPart.Scale && Array.isArray(raw))\n ? [raw[0] / 100, raw[1] / 100] as unknown as PxAnimatable<any>\n : raw;\n const v = readAnimatable<any>(normalizedRaw);\n if (v.kind === ReadKind.Static) {\n return { type: 'g', transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };\n }\n if (v.kind === ReadKind.Animated) {\n const animTr: any = { keyframes: v.keyframes.map(kf => {\n const out = keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin));\n return part === TransformPart.Translate ? { ...out, ...negatedSpatialTangents(kf) } : out;\n }) };\n if (v.loop !== undefined) animTr.loop = v.loop;\n return {\n type: 'g',\n animate: { transform: animTr },\n children: [inner],\n };\n }\n return inner;\n}\n\nfunction invertPartValue(part: TransformPart, value: any): any {\n if (part === TransformPart.Translate) return [-value[0], -value[1]];\n if (part === TransformPart.Rotate) return -value;\n return [1 / value[0], 1 / value[1]]; // scale\n}\n\n/**\n * Spatial tangents (`tangentOut`/`tangentIn`, wire aliases `to`/`ti`) are\n * RELATIVE control-point offsets (control = value + tangent), so an inverse\n * translate keyframe must negate them along with the value — copying them\n * verbatim keeps the ORIGINAL curve direction and the derived mask sags\n * mid-segment on a motion-along-path masked element (endpoints stay exact,\n * which is why only mid-frame sampling exposes it).\n */\nfunction negatedSpatialTangents(kf: Record<string, any>): Record<string, any> {\n const out: Record<string, any> = {};\n const to = kf.tangentOut ?? kf.to;\n const ti = kf.tangentIn ?? kf.ti;\n if (Array.isArray(to)) out.tangentOut = [-to[0], -to[1]];\n if (Array.isArray(ti)) out.tangentIn = [-ti[0], -ti[1]];\n return out;\n}\n\n\n/**\n * Wraps `inner` with the per-part INVERSE of the masked element's\n * `node.animate.transform.keyframes` records. Each kf carries a parts record\n * `{translate?, rotate?, scale?, origin?}` with matrix\n * `T(t)·T(o)·R·S·T(-o)`. The matrix inverse is\n * `T(o)·S^-1·R^-1·T(-o)·T(-t)` — which can't be expressed as ONE parts\n * record when both rotate and scale are present (S would have to precede R).\n *\n * Split into separate per-part wrappers, layered from innermost (translate)\n * outwards (rotate, then scale), so the overall composition matches the\n * matrix-level inverse:\n *\n * <g scale^-1> <g rotate^-1> <g translate^-1> {use} </g></g></g>\n *\n * Each wrapper carries an animated parts record over the input kf times.\n * The `origin` for rotate / scale wrappers comes from each kf (the wire emits\n * it alongside whenever rotate or scale is present), so an animated origin\n * sandwich pivots correctly per frame.\n */\nfunction wrapInverseAnimatedBodyTransform(inner: PxNode, node: PxNode, _ctx: ApplyContext): PxNode {\n const animate = node.animate && typeof node.animate === 'object' && !Array.isArray(node.animate)\n ? (node.animate as Record<string, any>) : undefined;\n const animTr = animate?.transform;\n const kfs = animTr && typeof animTr === 'object' && Array.isArray((animTr as Record<string, any>).keyframes)\n ? ((animTr as Record<string, any>).keyframes as Array<Record<string, any>>)\n : undefined;\n if (!kfs || !kfs.length) return inner;\n\n const translateKfs: Array<Record<string, any>> = [];\n const rotateKfs: Array<Record<string, any>> = [];\n const scaleKfs: Array<Record<string, any>> = [];\n\n for (const kf of kfs) {\n const v = (kf.value ?? kf.v) || {};\n const baseKf = keyframeWith(kf as any, undefined); // copies time / easing / tangents\n if (Array.isArray(v.translate)) {\n translateKfs.push({ ...baseKf, ...negatedSpatialTangents(kf), value: { translate: [-v.translate[0], -v.translate[1]] } });\n }\n if (typeof v.rotate === 'number') {\n const rec: Record<string, any> = { rotate: -v.rotate };\n if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];\n rotateKfs.push({ ...baseKf, value: rec });\n }\n if (Array.isArray(v.scale)) {\n const rec: Record<string, any> = { scale: [1 / v.scale[0], 1 / v.scale[1]] };\n if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];\n scaleKfs.push({ ...baseKf, value: rec });\n }\n }\n\n // Forward the source animate.transform's loop config (alternate/cycle/etc.)\n // onto each per-part inverse wrapper so a loop on the masked element's body\n // transform survives the inversion split.\n const srcLoop = (animTr as Record<string, any> | undefined)?.loop;\n const withLoop = (kfs: Array<Record<string, any>>): Record<string, any> => {\n const block: Record<string, any> = { keyframes: kfs };\n if (srcLoop !== undefined) block.loop = srcLoop;\n return block;\n };\n\n let n = inner;\n // Innermost = translate (matches `wrapInverseTransform`'s order).\n if (translateKfs.length) n = { type: 'g', animate: { transform: withLoop(translateKfs) }, children: [n] };\n if (rotateKfs.length) n = { type: 'g', animate: { transform: withLoop(rotateKfs) }, children: [n] };\n if (scaleKfs.length) n = { type: 'g', animate: { transform: withLoop(scaleKfs) }, children: [n] };\n return n;\n}\n\n\n/** True when the node carries any body-side transform (the static `transform`\n * string OR an `animate.transform` block). */\nfunction nodeHasBodyTransform(node: PxNode): boolean {\n if (typeof node.transform === 'string') return true;\n if (node.transform && typeof node.transform === 'object') return true;\n return hasAnimateTransform(node);\n}\n\n/** True when the node carries an `animate.transform` block (per-frame\n * override of `node.transform`). */\nfunction hasAnimateTransform(node: PxNode): boolean {\n const animate = node.animate && typeof node.animate === 'object' && !Array.isArray(node.animate)\n ? (node.animate as Record<string, any>) : undefined;\n return !!(animate && animate.transform);\n}\n\n/** Parses the masked element's body transform (`node.transform` string) into a\n * `PxTransformByEffect`-like static parts record so the existing\n * `wrapInverseTransform` machinery can produce its inverse `<g>` wrappers.\n *\n * ANIMATED body transforms (`node.animate.transform.keyframes`) aren't\n * supported yet — they'd require splitting a parts-record keyframe stream\n * into per-part animations to feed into `wrapInversePart`. Falls back with a\n * warning. (In practice the writer emits `effects.transformBy` whenever the\n * masked element is animated, so this caller path is reached only for static\n * cases anyway.) */\nfunction readTransformationFromBody(node: PxNode): PxTransformByEffect | undefined {\n if (typeof node.transform === 'string') {\n const parts = parseTransformStringToParts(node.transform);\n if (!parts) return undefined;\n const out: PxTransformByEffect = {};\n if (parts.translate) out.translate = parts.translate;\n if (parts.rotate !== undefined) out.rotate = parts.rotate;\n // Scale parsed from the string is in 1.0-units (e.g. `scale(1.5)`).\n // Pass it through the `{value}` form so `wrapInversePart`'s\n // bare-array → percent normalization doesn't re-divide by 100.\n if (parts.scale) out.scale = { value: parts.scale };\n if (parts.origin) out.origin = parts.origin;\n return Object.keys(out).length ? out : undefined;\n }\n // STATIC RECORD (SCHEMA-DESIGN S1): bare `{translate, …}` (canonical) or\n // the legacy `{value: partsRecord}` wrapper — the lightweight wire's static\n // form; values are already wire units (scale = factor), so scale passes\n // through `{value}` like the string branch.\n if (node.transform && typeof node.transform === 'object'\n && !(node.transform as any).keyframes) {\n const wrapped = (node.transform as { value?: Record<string, any> }).value;\n const value = (wrapped && typeof wrapped === 'object' ? wrapped : node.transform) as Record<string, any>;\n if (value && typeof value === 'object') {\n const out: PxTransformByEffect = {};\n if (Array.isArray(value.translate)) out.translate = value.translate as [number, number];\n if (typeof value.rotate === 'number') out.rotate = value.rotate;\n if (typeof value.skew === 'number') out.skew = value.skew;\n if (Array.isArray(value.scale)) out.scale = { value: value.scale as [number, number] };\n if (Array.isArray(value.origin)) out.origin = value.origin as [number, number];\n return Object.keys(out).length ? out : undefined;\n }\n }\n return undefined;\n}\n\n/** Parses the canonical body-transform string of the form\n * `translate(t)? translate(o)? rotate? scale? translate(-o)?`\n * — back into a `PxTransformParts`-style record. The origin sandwich\n * (`translate(o) … translate(-o)`) is recovered as `origin: o`; the leading\n * translate (if any) becomes `translate: t`. Returns `undefined` when no\n * recognized ops are found. */\nfunction parseTransformStringToParts(s: string): { translate?: PxVec2; rotate?: number; scale?: PxVec2; origin?: PxVec2 } | undefined {\n interface Op { name: string; args: Array<number>; }\n const re = /([a-zA-Z]+)\\s*\\(([^)]*)\\)/g;\n let m: RegExpExecArray | null;\n const ops: Array<Op> = [];\n while ((m = re.exec(s)) !== null) {\n const args = m[2].split(/[\\s,]+/).filter(a => a.length > 0).map(Number);\n ops.push({ name: m[1], args });\n }\n if (!ops.length) return undefined;\n\n // Detect origin sandwich: a trailing `translate(-ox,-oy)` matching an\n // earlier `translate(+ox,+oy)`. Recover origin / inner rotate / scale.\n const last = ops[ops.length - 1];\n if (last.name === 'translate') {\n for (let j = ops.length - 2; j >= 0; j--) {\n const cand = ops[j];\n if (cand.name !== 'translate') continue;\n const ox = cand.args[0] ?? 0;\n const oy = cand.args[1] ?? 0;\n const lx = last.args[0] ?? 0;\n const ly = last.args[1] ?? 0;\n if (lx !== -ox || ly !== -oy) continue;\n // `cand` = +origin, `last` = −origin. Anything BEFORE `cand` may\n // be a body translate; anything BETWEEN them is rotate / scale.\n const out: { translate?: PxVec2; rotate?: number; scale?: PxVec2; origin?: PxVec2 } = {};\n out.origin = [ox, oy];\n for (let k = 0; k < j; k++) {\n if (ops[k].name === 'translate') {\n const tx = ops[k].args[0] ?? 0;\n const ty = ops[k].args[1] ?? 0;\n out.translate = out.translate ? [out.translate[0] + tx, out.translate[1] + ty] : [tx, ty];\n }\n }\n for (let k = j + 1; k < ops.length - 1; k++) {\n const op = ops[k];\n if (op.name === 'rotate') out.rotate = (out.rotate ?? 0) + (op.args[0] ?? 0);\n else if (op.name === 'scale') {\n const sx = op.args[0] ?? 1;\n const sy = op.args.length > 1 ? op.args[1] : sx;\n out.scale = out.scale ? [out.scale[0] * sx, out.scale[1] * sy] : [sx, sy];\n }\n }\n return out;\n }\n }\n\n // No sandwich — flat sequence. `translate`s sum, `rotate`s sum, `scale`s\n // multiply. Order isn't preserved but it works for the cases emitted without\n // origin (translates commute; only one rotate or scale).\n let translate: PxVec2 | undefined;\n let rotate: number | undefined;\n let scale: PxVec2 | undefined;\n for (const op of ops) {\n if (op.name === 'translate') {\n const dx = op.args[0] ?? 0;\n const dy = op.args[1] ?? 0;\n translate = translate ? [translate[0] + dx, translate[1] + dy] : [dx, dy];\n } else if (op.name === 'rotate') {\n rotate = (rotate ?? 0) + (op.args[0] ?? 0);\n } else if (op.name === 'scale') {\n const sx = op.args[0] ?? 1;\n const sy = op.args.length > 1 ? op.args[1] : sx;\n scale = scale ? [scale[0] * sx, scale[1] * sy] : [sx, sy];\n }\n }\n const out: { translate?: PxVec2; rotate?: number; scale?: PxVec2 } = {};\n if (translate) out.translate = translate;\n if (rotate !== undefined) out.rotate = rotate;\n if (scale) out.scale = scale;\n return Object.keys(out).length ? out : undefined;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Ancestor-chain compensation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Wraps `inner` in a single `<g>` that carries\n * translate = sum(maskSource.ancestors) − sum(maskedElement.ancestors)\n * (separately for the static baseline + every keyframe time observed on any\n * animated ancestor in either chain).\n *\n * Translations commute, so a single wrapper suffices for the translate-only\n * case. When any chain entry has a non-translate part, this function emits a\n * warning and falls back to translate-only — visually wrong but a graceful\n * degradation until the rotate/scale composition is implemented.\n */\nfunction wrapAncestorChainCompensation(inner: PxNode, maskedNode: PxNode, sourceId: string, ctx: ApplyContext, includeTargetOwn: boolean): PxNode {\n const sourceNode = ctx.idMap.get(sourceId);\n\n // M_target = (ancestors) · (target's own). When `effects.transformBy`\n // is present, `wrapInverseTransform` already covers target's own; pass\n // `includeTargetOwn=false` to skip the duplicate. With no `effects.\n // transformation`, the baseline `node.transform` string is the element's\n // only transform — include it.\n const targetAncestors = ctx.maskAncestorChains.get(maskedNode) || [];\n const targetOwn = includeTargetOwn ? extractTranslateOnly(maskedNode, ctx) : undefined;\n const targetChain = targetOwn ? [...targetAncestors, targetOwn] : targetAncestors;\n const sourceChain = (sourceNode && ctx.maskAncestorChains.get(sourceNode)) || [];\n\n if (!targetChain.length && !sourceChain.length) return inner;\n\n // Union of all keyframe times across both chains. Static-only chains end\n // up with `times = []`, which short-circuits below to a static wrapper.\n const times = new Set<number>();\n for (const a of targetChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);\n for (const a of sourceChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);\n const animated = times.size > 0;\n\n if (!animated) {\n const tgt = sumStaticTranslate(targetChain);\n const src = sumStaticTranslate(sourceChain);\n const dx = src[0] - tgt[0];\n const dy = src[1] - tgt[1];\n if (dx === 0 && dy === 0) return inner;\n return { type: 'g', transform: 'translate(' + dx + ',' + dy + ')', children: [inner] };\n }\n\n const sortedTimes = Array.from(times).sort((a, b) => a - b);\n const keyframes = sortedTimes.map(t => {\n const tgt = sumTranslateAt(targetChain, t);\n const src = sumTranslateAt(sourceChain, t);\n return { time: t, value: { translate: [src[0] - tgt[0], src[1] - tgt[1]] as [number, number] } };\n });\n return { type: 'g', animate: { transform: { keyframes } }, children: [inner] };\n}\n\n/** Sums every translate (baseline) entry in the chain. Ignores animated kfs. */\nfunction sumStaticTranslate(chain: Array<MaskAncestorTransform>): [number, number] {\n let x = 0, y = 0;\n for (const a of chain) {\n if (a.translate) { x += a.translate[0]; y += a.translate[1]; }\n }\n return [x, y];\n}\n\n/** Sums every translate at time `t` in the chain. Animated entries are sampled\n * via linear interpolation between their kfs; static entries contribute their\n * baseline. */\nfunction sumTranslateAt(chain: Array<MaskAncestorTransform>, t: number): [number, number] {\n let x = 0, y = 0;\n for (const a of chain) {\n if (a.translateKeyframes && a.translateKeyframes.length) {\n const v = interpKfs(a.translateKeyframes, t);\n x += v[0]; y += v[1];\n } else if (a.translate) {\n x += a.translate[0]; y += a.translate[1];\n }\n }\n return [x, y];\n}\n\nfunction interpKfs(kfs: Array<{ time: number; value: [number, number] }>, t: number): [number, number] {\n if (t <= kfs[0].time) return kfs[0].value;\n if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;\n for (let i = 1; i < kfs.length; i++) {\n if (t <= kfs[i].time) {\n const prev = kfs[i - 1];\n const cur = kfs[i];\n const a = (t - prev.time) / (cur.time - prev.time);\n return [prev.value[0] + (cur.value[0] - prev.value[0]) * a, prev.value[1] + (cur.value[1] - prev.value[1]) * a];\n }\n }\n return kfs[kfs.length - 1].value;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Pre-pass: walk tree, record ancestor chains for every (target, source) pair\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Walks `root` top-down and records — for every element involved in an\n * `effects.maskedBy` pair (the masked element AND the mask source it\n * references) — the chain of translate transforms on its `<g>` ancestors.\n * Results land in `ctx.maskAncestorChains`.\n *\n * The walk runs BEFORE pass 1 so it sees the untouched lightweight tree:\n * ancestor `<g transform=\"...\">` strings + `animate.transform` kfs are still\n * intact and easy to parse.\n */\nexport function collectMaskAncestorChains(root: PxNode, ctx: ApplyContext): void {\n // Two-pass: first find the actual NODE references that are masked elements\n // or mask source elements (the source via idMap, the masked element by\n // walking the tree). Then on a second walk, store ancestor chains for\n // those nodes.\n const interestingNodes = new Set<PxNode>();\n const collectInterestingNodes = (n: PxNode): void => {\n const maskSourceId = stripHash(n.effects?.maskedBy?.source);\n if (typeof maskSourceId === 'string') {\n interestingNodes.add(n); // masked element\n const sourceNode = ctx.idMap.get(maskSourceId);\n if (sourceNode) interestingNodes.add(sourceNode); // mask source\n }\n if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);\n };\n collectInterestingNodes(root);\n if (interestingNodes.size === 0) return;\n\n const walk = (node: PxNode, chain: Array<MaskAncestorTransform>): void => {\n if (interestingNodes.has(node)) ctx.maskAncestorChains.set(node, chain);\n if (Array.isArray(node.children)) {\n const own = extractTranslateOnly(node, ctx);\n const next = own ? [...chain, own] : chain;\n for (const ch of node.children) walk(ch, next);\n }\n };\n walk(root, []);\n}\n\n/** Extracts only the TRANSLATE part from a node's `transform` + `animate.transform`.\n * Returns `undefined` when the node has no transform OR carries only\n * non-translate parts. Pushes a warning when a non-translate part is dropped. */\nfunction extractTranslateOnly(node: PxNode, ctx: ApplyContext): MaskAncestorTransform | undefined {\n const tr = node.transform;\n const animateBlock = node.animate && typeof node.animate === 'object' && !Array.isArray(node.animate)\n ? (node.animate as Record<string, any>).transform : undefined;\n if (tr === undefined && !animateBlock) return undefined;\n\n const out: MaskAncestorTransform = {};\n\n if (typeof tr === 'string') {\n const parts = parseTranslateOnlyFromString(tr, ctx);\n if (parts) out.translate = parts;\n } else if (tr && typeof tr === 'object' && !(tr as Record<string, any>).keyframes) {\n // bare parts record (canonical) or the legacy {value: record} wrapper\n const wrapped = (tr as Record<string, any>).value;\n const value = (wrapped && typeof wrapped === 'object') ? wrapped : (tr as Record<string, any>);\n if (value && typeof value === 'object' && Array.isArray(value.translate)) {\n out.translate = [value.translate[0] || 0, value.translate[1] || 0];\n }\n if (value && (value.rotate !== undefined || value.scale !== undefined || value.skew !== undefined)) {\n ctx.warnings.push('maskedBy ancestor: non-translate transform parts ignored (rotate/scale not yet supported)');\n }\n }\n\n if (animateBlock && Array.isArray((animateBlock as Record<string, any>).keyframes)) {\n const kfs = (animateBlock as Record<string, any>).keyframes as Array<Record<string, any>>;\n const translateKfs: Array<{ time: number; value: [number, number] }> = [];\n for (const kf of kfs) {\n const v = kf.value ?? kf.v;\n const t = (kf.time ?? kf.t ?? 0) as number;\n if (v && typeof v === 'object' && Array.isArray(v.translate)) {\n translateKfs.push({ time: t, value: [v.translate[0] || 0, v.translate[1] || 0] });\n if (v.rotate !== undefined || v.scale !== undefined || v.skew !== undefined) {\n ctx.warnings.push('maskedBy ancestor: animated non-translate parts ignored');\n }\n }\n }\n if (translateKfs.length) out.translateKeyframes = translateKfs;\n }\n\n return (out.translate || out.translateKeyframes) ? out : undefined;\n}\n\n/** Parses ONLY `translate(x[, y])` ops out of an SVG transform string, summing\n * multiple translates and ignoring anything else (with a one-shot warning).\n * The lightweight writer emits the masked / source ancestors' transforms as\n * these simple strings, so this stays a tiny single-purpose parser. */\nfunction parseTranslateOnlyFromString(s: string, ctx: ApplyContext): [number, number] | undefined {\n const re = /([a-zA-Z]+)\\s*\\(([^)]*)\\)/g;\n let m: RegExpExecArray | null;\n let x = 0, y = 0;\n let seen = false;\n let droppedNonTranslate = false;\n while ((m = re.exec(s)) !== null) {\n const name = m[1];\n const args = m[2].split(/[\\s,]+/).filter(a => a.length > 0).map(Number);\n if (name === 'translate') {\n x += args[0] || 0;\n y += args[1] || 0;\n seen = true;\n } else {\n droppedNonTranslate = true;\n }\n }\n if (droppedNonTranslate) ctx.warnings.push('maskedBy ancestor: non-translate transform in string ignored: ' + s);\n return seen ? [x, y] : undefined;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * REF + TRANSFORMATION (combined). The use's `href` is set BEFORE\n * `applyTransformByEffect` wraps the node so it lands on the actual `<use>`\n * rather than on an outer `<g>` wrapper.\n *\n * For `clone:{without:'translate'}` (a content-ref) the source is materialized as multi-layer by\n * `splitForContentRef` (see `contentRefSplit.ts`), and the use's `href` is\n * rewritten to point at the inner (no-translate) layer's id. No translate\n * cancellation is needed on the use side any more.\n */\n\nimport { PxCloneWithout } from '../../format/PxAnimatorConstants';\nimport { applyTransformByEffect } from '../transform/transformationEffect';\nimport type { PxCloneEffect, PxNode, PxTransformByEffect } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\nimport { stripHash } from '../shared/util';\n\n\n/**\n * Rewrites `node.href` from the clone's reference part (`without`/`source`) —\n * content-ref → the inner-layer id generated by `splitForContentRef`, whole-element\n * ref → `source`. Pure href mutation (no wrapping), so it can run even when `node`\n * is ALSO a content-ref SOURCE that gets split: a `<use>` that both references\n * content (consumer) and is itself referenced (source) must get its own href\n * rewritten BEFORE the split moves its body inward — otherwise its body keeps the\n * editor-side content id, which doesn't exist in the lightweight tree (dangling\n * href → retime can't follow the chain → the retimed instance renders nothing).\n *\n * A direct-link clone (no `source`, e.g. `clone:{retime}`) keeps its existing\n * `href` — there's nothing to redirect; only content-ref REQUIRES a `source`.\n */\nexport function applyRefHref(\n node: PxNode,\n clone: PxCloneEffect | undefined,\n ctx: ApplyContext,\n): void {\n if (!clone) return;\n // Canonical ref spelling is `#id` (SCHEMA-DESIGN §4 E-5); bare `id` is legacy.\n const sourceId = stripHash(clone.source);\n if (!sourceId) {\n if (clone.without === PxCloneWithout.translate) ctx.errors.push('clone: content ref missing `source`');\n return; // direct link → href already correct, nothing to rewrite\n }\n // For content-ref, redirect href to the inner-layer id produced by\n // `splitForContentRef`. For whole-element ref (or when no split has\n // happened, e.g. target not in the tree), fall back to sourceId.\n const targetId = clone.without === PxCloneWithout.translate\n ? (ctx.contentRefInnerIds.get(sourceId) || sourceId)\n : sourceId;\n node.href = '#' + targetId;\n}\n\nexport function applyRefAndTransformationEffect(\n node: PxNode,\n clone: PxCloneEffect | undefined,\n transformBy: PxTransformByEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n applyRefHref(node, clone, ctx);\n return applyTransformByEffect(node, transformBy, ctx);\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport { applyTransformByEffect } from './transformationEffect';\nimport type { PxAnimatable, PxAnimationDefinition, PxKeyframe, PxLoop, PxNode, PxRepeaterEffect, PxTransformByEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport { ReadKind, readAnimatable } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\nimport { clone } from '../shared/util';\n\n\n/**\n * REPEATER — \"clone this element N times, each copy stepped by a rule\".\n * Named `repeater`, not `repeat`: the repetition is SPATIAL, while in an animation\n * format `repeat` reads as TIME (`animator.iterations`, per-property `loop`,\n * SMIL `repeatCount`). See the naming note on `_PxRepeaterEffect`.\n *\n * REPEATER → N clones inside a `<g>` wrapper. Copy 0 is the unmodified base;\n * copies 1..N-1 are wrapped with a per-copy `PxTransformByEffect` synthesised\n * from the repeater parts:\n *\n * - translate × i\n * - rotate × i\n * - skew × i (skewX degrees)\n * - scale ^ i (per-axis geometric compound)\n * - origin CONSTANT — rotation/scale center stays put across copies\n *\n * The origin is the rotation/scale CENTER for the sandwich\n * `T(+o) · T(t·i) · R(r·i) · S(s^i) · T(-o)`. Scaling `o` by `i` would shift the\n * center per copy and produce a spiral instead of a repeated rotation.\n *\n * Each part is independently animatable:\n * - Static parts → emitted as structured `transform: {value:…}` on the per-copy wrapper.\n * - Animated parts → emitted as `animate.transform.keyframes` with each kf value\n * scaled by the rule above (time/easing preserved).\n *\n * Uses the same per-copy matrix formula as the heavy SVG render.\n */\nexport function applyRepeaterEffect(node: PxNode, fx: PxRepeaterEffect | undefined, ctx: ApplyContext): PxNode {\n if (!fx) return node;\n\n const copies = fx.copies ?? 1;\n if (copies < 1) { ctx.errors.push('repeater.copies invalid: ' + fx.copies); return node; }\n\n // The base element's SHARED transform (static baseline + any animation) is\n // lifted onto the wrapper so the per-copy increments compose in the wrapper's\n // coordinate space. Non-transform animations (opacity, fill) stay per copy.\n const sharedTransform = node.transform;\n // In-place animations on a node body are always the record form here —\n // narrow the `PxElementAnimation` union accordingly.\n const sharedAnimTransform = (node.animate as PxAnimationDefinition | undefined)?.transform;\n\n const base = clone(node);\n delete base.transform;\n if (base.animate) {\n delete (base.animate as PxAnimationDefinition).transform;\n if (Object.keys(base.animate).length === 0) delete base.animate;\n }\n // The WRAPPER owns the source id (assigned below): a whole-element `<use>` must\n // resolve to the FULL repeated result including the shared transform. Leaving the\n // id on the base would duplicate it across every copy-clone, and href resolution\n // would land on a bare, transform-stripped copy (rendered at the use's position\n // with no body translate — visibly mis-placed).\n delete base.id;\n\n const children: Array<PxNode> = [base];\n for (let i = 1; i < copies; i++) {\n const baseClone = clone(base);\n const synthFx = synthesisePerCopyFx(fx, i);\n // Run through the standard transformation-effect machinery — gets\n // origin sandwich, animated kfs, etc. for free.\n const wrapped = synthFx ? applyTransformByEffect(baseClone, synthFx, ctx) : baseClone;\n children.push(wrapped);\n }\n\n const wrapper: PxNode = { type: 'g', children };\n if (node.id) wrapper.id = node.id;\n if (sharedTransform !== undefined) wrapper.transform = sharedTransform;\n if (sharedAnimTransform !== undefined) wrapper.animate = { transform: sharedAnimTransform };\n return wrapper;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PER-COPY SYNTHESIS\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Builds a per-copy `PxTransformByEffect` for copy index `i`. */\nfunction synthesisePerCopyFx(fx: PxRepeaterEffect, i: number): PxTransformByEffect | undefined {\n const out: PxTransformByEffect = {};\n\n if (fx.translate !== undefined) {\n out.translate = mapAnimatable<PxVec2>(fx.translate, v => [v[0] * i, v[1] * i]);\n }\n if (fx.rotate !== undefined) {\n out.rotate = mapAnimatable<number>(fx.rotate, v => v * i);\n }\n if (fx.skew !== undefined) {\n out.skew = mapAnimatable<number>(fx.skew, v => v * i);\n }\n if (fx.scale !== undefined) {\n out.scale = synthesiseScale(fx.scale, i);\n }\n if (fx.origin !== undefined) {\n // CONSTANT — rotation/scale center stays put across copies. Scaling by `i`\n // would drift the center per copy (spiral instead of repeated rotation).\n out.origin = fx.origin;\n }\n\n return Object.keys(out).length ? out : undefined;\n}\n\n/**\n * Applies `fn` to every value of an animatable (base + all keyframes) via the\n * shared `readAnimatable` — one mapper for number and PxVec2 parts alike (the old\n * per-type copies missed the `kfs` alias, so an alias-authored part silently\n * skipped its ×i scaling). Re-emits the normalized unified form:\n * raw static in → raw static out (or `{value}` when `wrapStatic`); animated in →\n * `{keyframes, loop?, autoOrient?, value?}` with kf values mapped and\n * time/easing/tangents preserved.\n */\nfunction mapAnimatable<T>(raw: PxAnimatable<T>, fn: (v: T) => T, wrapStatic = false): PxAnimatable<T> {\n const read = readAnimatable<T>(raw);\n if (read.kind === ReadKind.Absent) return raw;\n if (read.kind === ReadKind.Static) {\n const mapped = fn(read.value);\n const wasRawStatic = typeof raw === 'number' || Array.isArray(raw);\n return (wasRawStatic && !wrapStatic) ? mapped : { value: mapped };\n }\n const out: { keyframes: Array<PxKeyframe<T>>; loop?: PxLoop | boolean; autoOrient?: boolean; value?: T } = {\n keyframes: read.keyframes.map(kf => kf && kf.value !== undefined ? { ...kf, value: fn(kf.value) } : kf),\n };\n if (read.loop !== undefined) out.loop = read.loop;\n if (read.autoOrient !== undefined) out.autoOrient = read.autoOrient;\n if (read.base !== undefined) out.value = fn(read.base);\n return out;\n}\n\n/**\n * Per-copy scale: per-axis geometric compounding `s^i`.\n *\n * The wire carries repeater.scale as a FACTOR (0.85 = 85%) in EVERY form — bare\n * static, `{value:…}` and `{keyframes:…}` alike (one convention, see\n * dev-docs/schema-design.md I-3; the old bare-static PERCENT form is gone). Output is\n * emitted as `{value:…}` / keyframes in the same 1.0-units, so it also bypasses\n * `applyTransformByEffect.normalizeScale` untouched.\n */\nfunction synthesiseScale(raw: PxAnimatable<PxVec2>, i: number): PxAnimatable<PxVec2> {\n const scalePower = (v: PxVec2): PxVec2 => [Math.pow(v[0], i), Math.pow(v[1], i)];\n return mapAnimatable<PxVec2>(raw, scalePower, true);\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * RETIME (`<use>` timeline remap) — runs AFTER every other pass-1 effect has\n * materialized, so the `<use>`'s `href` already points at the right post-pass-1\n * target (e.g. the inner-content layer that `contentRefSplit` produced).\n *\n * Retime lives nested under the merged `clone` effect (`effects.clone.retime`).\n * For every `<use>` carrying it we:\n * 1. follow `useNode.href` (no `sourceId` lookup — the editor-side id wouldn't\n * resolve in the lightweight tree);\n * 2. recursively materialize the chain target-side, preserving each\n * intermediate `<use>`'s offset/transform and accumulating retime at every\n * nested-retime hop (`concatRetime(child, parent)`);\n * 3. wire the chain root into the `<use>` site — see RETIME_MATERIALIZATION_MODE.\n *\n * Each chain link becomes its own defs entry with the right accumulated retime\n * applied to ITS OWN kfs, and the link's `href` rewritten to point at the\n * next-level materialization.\n *\n * NB: `clone.sourceId` is intentionally IGNORED by retime — it carries an\n * upstream core id that does not exist in the lightweight tree; retime\n * follows `href`.\n */\n\nimport type { PxNode, PxRetimeEffect } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\nimport { applyUseOffsetToG } from '../../util/PxNodeCloneUtil';\nimport { clone, genId, indexById, regenerateIdsInClone, stripHash } from '../shared/util';\n\n\n/** Mode A (true): replace `<use>` with `<g>` whose child IS the chain-root clone.\n * Mode B (false): push the chain-root clone into `<defs>`, rewrite `<use>.href`.\n *\n * Mode B is the default — it preserves the `<use>`'s native semantics (x/y\n * positioning, width/height — none of which `<g>` interprets). Mode A would\n * need to bake those into the new `<g>`'s transform to stay equivalent. */\nconst RETIME_MATERIALIZATION_MODE_INLINE_G = false;\n\n\ninterface Retime { start: number; stretch: number; }\n\nfunction asRetime(r: PxRetimeEffect): Retime {\n return { start: r.start ?? 0, stretch: r.stretch ?? 1 };\n}\n\n/**\n * `timeCrop: [start, end]` (ms, DOCUMENT time) — a VISIBILITY WINDOW on this\n * instance: the `<use>` shows only inside it. Independent of the retime remap,\n * which shifts/stretches the target's own timeline; the crop is about when this\n * instance exists on stage.\n *\n * Implemented as an opacity animation on a wrapper `<g>` rather than by clipping\n * the timeline: the target's animation must keep running (a Lottie layer inside\n * its `ip`/`op` window is mid-motion when it appears, not restarted). The wrapper\n * is a PLAYER-SIDE artifact — it never round-trips, so it cannot disturb the wire.\n *\n * A wrapper is used instead of writing opacity onto the `<use>` itself so an\n * authored opacity on the instance survives (they would otherwise overwrite each\n * other) — the same reason the Lottie converter wraps, see `TimeAttrsPartMC`.\n *\n * Keyframe shape mirrors that converter exactly: `[s-1 → 0][s → 1][e → 1][e+1 → 0]`.\n * The 1 ms shoulders make the edges effectively instant while keeping the element\n * fully visible AT both boundaries.\n */\nconst CROP_EDGE_MS = 1;\n\nfunction applyTimeCrop(useNode: PxNode, crop: [number, number], ctx: ApplyContext): void {\n const [start, end] = crop;\n if (!Number.isFinite(start) || !Number.isFinite(end)) {\n ctx.warnings.push('retime: timeCrop is not a pair of finite numbers — ignored');\n return;\n }\n\n // Empty (or inverted) window ⇒ never visible. A Lottie layer with ip >= op is\n // exactly this; without the explicit 0 it would render always-visible.\n const keyframes = end <= start\n ? [{ time: 0, value: 0 }]\n : [\n ...(start > 0 ? [{ time: Math.max(0, start - CROP_EDGE_MS), value: 0 }] : []),\n { time: Math.max(0, start), value: 1 },\n { time: end, value: 1 },\n { time: end + CROP_EDGE_MS, value: 0 },\n ];\n\n // Wrap: the `<g>` takes over the crop, the original node stays untouched inside.\n const inner: PxNode = { ...useNode } as PxNode;\n for (const k of Object.keys(useNode)) delete (useNode as Record<string, unknown>)[k];\n useNode.type = 'g';\n useNode.children = [inner];\n (useNode as Record<string, unknown>).animate = { opacity: { keyframes } };\n}\n\n/** parent applied OUTSIDE, child INSIDE → `t = parent.start + parent.stretch * (child.start + child.stretch * t_local)`. */\nfunction concatRetime(child: Retime, parent: Retime): Retime {\n return {\n start: parent.start + parent.stretch * child.start,\n stretch: parent.stretch * child.stretch,\n };\n}\n\n/** Retime lives nested under the `clone` effect (`effects.clone.retime`). */\nfunction readCloneRetime(n: PxNode): PxRetimeEffect | undefined {\n return n.effects?.clone?.retime;\n}\n\n/** Removes the retime slice and prunes now-empty `clone` / `effects` buckets. */\nfunction clearCloneRetime(n: PxNode): void {\n const clone = n.effects?.clone;\n if (!clone) return;\n delete clone.retime;\n if (Object.keys(clone).length === 0) delete n.effects!.clone;\n if (n.effects && Object.keys(n.effects).length === 0) delete n.effects;\n}\n\n\n/** Pass-2 driver. Re-indexes ids (pass-1 generates new ones like `_lw_inner_0`),\n * then materializes retime at every site. Order-independent: each site\n * recursively expands its own chain via the original retime layout. */\nexport function applyAllRetimeEffects(root: PxNode, ctx: ApplyContext): void {\n ctx.idMap.clear();\n indexById(root, ctx.idMap);\n\n const sites: Array<PxNode> = [];\n const collect = (n: PxNode): void => {\n if (readCloneRetime(n)) sites.push(n);\n n.children?.forEach(collect);\n };\n collect(root);\n\n // OUTER-most sites first. Materializing a site CONSUMES its retime in place (see the\n // load-bearing note below) — so when an outer use's chain later clones a subtree whose\n // inner retimed use was already processed, the inner retime is gone and never composes\n // with the outer one: a doubly-retimed chain started at +inner instead of\n // +inner∘outer. Document order only happens to work when the outer use serializes\n // first; a symbol-heavy document (e.g. one imported from Lottie precomps) puts the\n // template's inner use ahead of the outer site. Order by reachability instead: a site\n // whose chain can reach other sites materializes before them.\n const reachCount = new Map<PxNode, number>();\n for (const site of sites) {\n let count = 0;\n const visited = new Set<string>();\n const walk = (n: PxNode | undefined): void => {\n if (!n) return;\n if (n !== site && readCloneRetime(n)) count++;\n if (n.type === 'use' && n.href) {\n const id = stripHash(n.href);\n if (id && !visited.has(id)) { visited.add(id); walk(ctx.idMap.get(id)); }\n }\n n.children?.forEach(walk);\n };\n const rootId = stripHash(site.href);\n if (rootId) { visited.add(rootId); walk(ctx.idMap.get(rootId)); }\n reachCount.set(site, count);\n }\n // Stable sort: deeper reach first; equal reach keeps document order.\n sites.sort((a, b) => (reachCount.get(b) ?? 0) - (reachCount.get(a) ?? 0));\n\n for (const useNode of sites) {\n const retime = readCloneRetime(useNode);\n if (!retime) continue;\n // Per-site delete is LOAD-BEARING, not just cleanup: once this use is\n // materialized its `href` is rewritten to an already-time-shifted clone, so\n // a downstream use that references THIS one must NOT re-compose this retime\n // (buildChainClone reads `target`'s clone.retime). Deleting it here makes\n // that read return undefined → no double-count. Moving cleanup to a single\n // end-of-pipeline strip regresses nested retime to +750 instead of +500.\n const crop = retime.timeCrop;\n clearCloneRetime(useNode);\n materializeRetime(useNode, asRetime(retime), ctx);\n // AFTER materialization: `materializeRetime` may rewrite `useNode` in place\n // (inline-`<g>` mode), so cropping last wraps whatever it ended up being.\n if (crop) applyTimeCrop(useNode, crop, ctx);\n }\n}\n\n\n/** Materializes `retime` on `useNode` by cloning the chain rooted at\n * `useNode.href` and wiring the clone into the `<use>` site. */\nfunction materializeRetime(useNode: PxNode, retime: Retime, ctx: ApplyContext): void {\n const targetId = stripHash(useNode.href);\n if (!targetId) { ctx.errors.push('retime: <use> has no href to follow'); return; }\n\n const chainRootId = buildChainClone(targetId, retime, ctx, new Set());\n if (!chainRootId) return;\n\n if (RETIME_MATERIALIZATION_MODE_INLINE_G) {\n const cloneNode = ctx.idMap.get(chainRootId)!;\n useNode.type = 'g';\n delete useNode.href;\n useNode.children = [cloneNode];\n // `<g>` ignores `x`/`y`; preserve the use's position offset as a\n // `translate(x,y)` applied AFTER any transform (nested inner `<g>`).\n applyUseOffsetToG(useNode);\n ctx.defs = ctx.defs.filter(d => d !== cloneNode); // un-defs it since it's inline now\n } else {\n useNode.href = '#' + chainRootId;\n }\n}\n\n\n/** Clones `targetId`'s node, remaps its OWN kfs by `accum`, then — if the target\n * is a `<use>` — recursively materializes ITS target (folding any nested retime\n * into accum via `concatRetime`). Returns the clone's new id, or undefined on\n * dangling refs / loops. The clone is pushed to `ctx.defs` and indexed in\n * `ctx.idMap` so siblings can resolve it. */\nfunction buildChainClone(targetId: string, accum: Retime, ctx: ApplyContext, chain: Set<string>): string | undefined {\n if (chain.has(targetId)) { ctx.errors.push('retime: loop via \"' + targetId + '\"'); return undefined; }\n const target = ctx.idMap.get(targetId);\n if (!target) { ctx.warnings.push('retime: target \"' + targetId + '\" not found'); return undefined; }\n\n const cloneNode = clone(target);\n regenerateIdsInClone(cloneNode, ctx);\n\n // Clone's OWN body kfs (intermediate-use's animated transform, ball's animation, …).\n // For a use, this is usually a no-op (no kfs on the use itself); for a leaf it\n // remaps the entire reachable subtree.\n if (target.type === 'use') {\n remapKeyframeTimesOnly(cloneNode, accum.start, accum.stretch);\n } else {\n remapKeyframeTimes(cloneNode, accum.start, accum.stretch);\n }\n\n // Strip any retime carried into the clone (already consumed via the chain).\n clearCloneRetime(cloneNode);\n\n // If the target is a `<use>`, recurse on ITS href. Nested retime on the\n // intermediate use folds in via concat.\n if (target.type === 'use' && target.href) {\n const subId = stripHash(target.href);\n if (subId) {\n const innerRetime = readCloneRetime(target);\n const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;\n const subChain = new Set(chain); subChain.add(targetId);\n const subId2 = buildChainClone(subId, subAccum, ctx, subChain);\n if (subId2) cloneNode.href = '#' + subId2;\n }\n } else {\n // Container target (e.g. a content-ref split wrapper) may HOLD nested\n // `<use>` children that themselves carry retime — the nested content-ref\n // case `use → <g> → use(retime) → …`. Those uses aren't in the pass-2\n // site list (they only exist inside this fresh clone), so materialize\n // them here, composing their retime with `accum` (recurse into children).\n // Without this the inner use's retime stays dangling and the subtree\n // renders un-shifted.\n materializeNestedRetimeUses(cloneNode, accum, ctx, chain, targetId);\n }\n\n ctx.defs.push(cloneNode);\n if (typeof cloneNode.id === 'string') ctx.idMap.set(cloneNode.id, cloneNode);\n return typeof cloneNode.id === 'string' ? cloneNode.id : undefined;\n}\n\n\n/** Walks a freshly-cloned container subtree and materializes every nested\n * `<use>` that carries retime: composes its retime with `accum` and rewires its\n * href to a fresh chain clone (then drops the now-consumed retime). The\n * container analogue of `buildChainClone`'s use-target recursion — needed for\n * nested content-ref retime where the inner `<use retime>` lives INSIDE the\n * cloned wrapper rather than at its href root. */\nfunction materializeNestedRetimeUses(node: PxNode, accum: Retime, ctx: ApplyContext, chain: Set<string>, parentTargetId: string): void {\n const visit = (n: PxNode): void => {\n const retime = readCloneRetime(n);\n if (n.type === 'use' && retime && n.href) {\n const subId = stripHash(n.href);\n if (subId) {\n const subAccum = concatRetime(asRetime(retime), accum);\n const subChain = new Set(chain); subChain.add(parentTargetId);\n const subId2 = buildChainClone(subId, subAccum, ctx, subChain);\n if (subId2) n.href = '#' + subId2;\n }\n clearCloneRetime(n);\n }\n n.children?.forEach(visit);\n };\n visit(node);\n}\n\n\n/** Remaps every keyframe `time` in `node` and its subtree: `t' = start + t·stretch`. */\nfunction remapKeyframeTimes(node: PxNode, start: number, stretch: number): void {\n remapKeyframeTimesOnly(node, start, stretch);\n node.children?.forEach(c => remapKeyframeTimes(c, start, stretch));\n}\n\nfunction remapKeyframeTimesOnly(node: PxNode, start: number, stretch: number): void {\n const remap = (kfs: Array<any>): void => {\n for (const kf of kfs) if (typeof kf.time === 'number') kf.time = start + kf.time * stretch;\n };\n if (node.transform && typeof node.transform === 'object' && Array.isArray((node.transform as any).keyframes)) {\n remap((node.transform as any).keyframes);\n }\n if (node.animate && typeof node.animate === 'object') {\n for (const prop of Object.keys(node.animate)) {\n const anim = (node.animate as any)[prop];\n if (anim && Array.isArray(anim.keyframes)) remap(anim.keyframes);\n }\n }\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport { type PxAnimatable, type PxBezierPath, type PxKeyframe, type PxLoop, type PxNode, type PxVec2, type _PxStrokeTrimEffect, keyframeTime, keyframeValue, keyframeEasing } from '../../format/PxAnimatorTypes';\nimport { PxStrokeTrimSubPaths } from '../../format/PxAnimatorConstants';\nimport { bezier2D_arcLengthLUT, bezierToSvgPath, clamp } from '../../util/PxAnimatorUtil';\nimport { parseSvgPathToBezier } from '../../animation/PxDefinitions';\nimport { ReadKind, readAnimatable, writeAnimatableChannel, type ReadPart } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\n\n\n/**\n * Applies a `strokeTrim` effect: collects every descendant shape leaf, slices each\n * leaf's `d` into sub-paths, measures each in px, and converts the parametric\n * `offset` + `range` to per-sub-path `stroke-dasharray` / `stroke-dashoffset` (and\n * `opacity` for empty-range hide).\n *\n * - `subPaths: 'combined'` chains every sub-path's length end-to-end so the\n * window slides across siblings (ONE trim window over the chained\n * `totalLength`); `'separate'` (default) trims each sub-path on its own.\n * - Animated `offset` becomes `animate.strokeDashoffset.keyframes`;\n * animated `range` becomes `animate.strokeDasharray.keyframes`.\n * - The dasharray emits a repeated pattern so any dashoffset shift lands on a\n * valid dash segment.\n * - Empty-range moments are hidden with `stroke-opacity` 0 (STROKE only — the\n * fill stays visible).\n *\n * COLLAPSE: when the trim host is itself a single shape leaf with exactly ONE\n * sub-path, the trim materializes directly onto that leaf's own `<path>` (no\n * `<g>` split). Multi-subpath (or group/descendant) trims still expand to\n * `<g>` + one bare `<path>` per sub-path.\n */\nexport function applyStrokeTrimEffect(\n node: PxNode,\n strokeTrim: _PxStrokeTrimEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n if (!strokeTrim) return node;\n\n const combined = strokeTrim.subPaths === PxStrokeTrimSubPaths.combined;\n\n // Pass 1a — collect leaves with subpath lengths (no chain offset yet).\n const leafEntries: Array<LeafEntry> = [];\n const measure = (n: PxNode): void => {\n if (Array.isArray(n.children) && n.children.length > 0) {\n for (const ch of n.children) measure(ch);\n return;\n }\n const d = typeof n.d === 'string' ? n.d : shapeToPathD(n);\n if (d === undefined) return;\n const subpaths = parseSvgPathToBezier(d);\n if (!subpaths.length) return;\n const entry: LeafEntry = { leaf: n, subpaths: [] };\n for (const sp of subpaths) {\n const lengthPx = pxBezierPathLength(sp);\n entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: 0 });\n }\n leafEntries.push(entry);\n };\n measure(node);\n\n if (!leafEntries.length) return node;\n\n // Pass 1b — assign chain offsets.\n // `subPaths: 'combined'`: walk leaves in REVERSE doc order so the chain\n // starts at the visually-topmost leaf. Within each leaf, subpaths keep their\n // `d`-attribute order — without the reverse, a multi-leaf group emits the\n // leaves' dashoffsets SWAPPED, which renders the visible trim window on the\n // wrong subpath.\n // `subPaths: 'separate'`: each subpath gets its own independent chain\n // (offset 0), so the leaf iteration order doesn't matter.\n let acc = 0;\n const iterOrder = combined ? [...leafEntries].reverse() : leafEntries;\n for (const entry of iterOrder) {\n for (const sp of entry.subpaths) {\n if (!combined) acc = 0;\n sp.startOffsetPx = acc;\n acc += sp.lengthPx;\n }\n }\n\n const chainLengthPx = acc;\n if (combined && chainLengthPx < 0.001) return node;\n\n // Offset / range readers. Range is post-processed for cross-overs so the\n // dasharray emitter never sees `range[0] > range[1]`.\n //\n // Defaults: `offset = 0` and `range = [0, 1]`. Treating absent\n // inputs as those statics (rather than skipping the emit) keeps the\n // `+ SMALL_PADDING_PX` shift active — that 1-px buffer is what stops\n // `stroke-linecap=\"round\"` from painting a round dot at the zero-length\n // first dash. Missing-emit means no `stroke-dashoffset`, no shift, and\n // the dot reappears.\n const offsetReadRaw = readAnimatable<number>(strokeTrim.offset);\n const offsetRead: ReadPart<number> = offsetReadRaw.kind === ReadKind.Absent\n ? { kind: ReadKind.Static, value: 0 }\n : offsetReadRaw;\n const rangeReadRaw = readRangeWithCrossings(strokeTrim.range);\n const rangeRead: ReadPart<PxVec2> = rangeReadRaw.kind === ReadKind.Absent\n ? { kind: ReadKind.Static, value: [0, 1] }\n : rangeReadRaw;\n\n const offsetValues = readScalarValues(offsetRead);\n const minOffset = offsetValues.length ? Math.min(...offsetValues) : 0;\n const maxOffset = offsetValues.length ? Math.max(...offsetValues) : 0;\n const minMaxOffset: [number, number] = [minOffset, maxOffset];\n\n // COLLAPSE: the trim host is a single shape leaf with exactly one sub-path\n // → no `<g>` split. The trim stroke attrs go directly on the leaf's own\n // `<path>` (its `fill` / `stroke` / `transform` / `id` stay put).\n if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {\n const entry = leafEntries[0];\n const sp = entry.subpaths[0];\n const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;\n if (pathLengthPx < 0.001) return node;\n const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;\n return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);\n }\n\n // Pass 2 — walk the tree and replace each measured leaf with a <g>\n // containing one bare <path> per sub-path.\n const replacements = new Map<PxNode, PxNode>();\n for (const entry of leafEntries) {\n const newChildren: Array<PxNode> = [];\n for (const sp of entry.subpaths) {\n const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;\n if (pathLengthPx < 0.001) continue;\n const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;\n newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));\n }\n replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));\n }\n\n const swap = (n: PxNode): PxNode => {\n const r = replacements.get(n);\n if (r) return r;\n if (Array.isArray(n.children) && n.children.length > 0) {\n return { ...n, children: n.children.map(swap) };\n }\n return n;\n };\n return swap(node);\n}\n\n\n// ============================================================================\n// Leaf -> sub-path table\n// ============================================================================\n\ninterface LeafEntry {\n leaf: PxNode;\n subpaths: Array<{ subpath: PxBezierPath; lengthPx: number; startOffsetPx: number }>;\n}\n\n/** Wraps a leaf as a `<g>` with new sub-path children. Outer attrs of the leaf\n * move onto the wrapper (so its `id` / style anchors are preserved); `d`,\n * `strokeDasharray`, `strokeDashoffset`, and `effects` are stripped. */\nfunction wrapLeafAsGroup(leaf: PxNode, children: Array<PxNode>): PxNode {\n const wrapper: PxNode = { ...leaf, type: 'g', children };\n delete wrapper.d;\n delete wrapper.strokeDasharray;\n delete wrapper.strokeDashoffset;\n delete wrapper.effects;\n return wrapper;\n}\n\n\n// ============================================================================\n// Sub-path node builder\n// ============================================================================\n\nfunction buildSubpathNodes(\n leaf: PxNode,\n subpath: PxBezierPath,\n pathLengthPx: number,\n startOffsetPct: number,\n minMaxOffset: [number, number],\n offsetRead: ReadPart<number>,\n rangeRead: ReadPart<PxVec2>,\n ctx: ApplyContext,\n): Array<PxNode> {\n const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);\n const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);\n\n const dashOffsetAttr = computeAnimAttr(offsetRead, offsetToDashOffset);\n const dashArrayAttr = computeAnimAttr(rangeRead, rangeToDasharray);\n const strokeOpacityAttr = computeOpacityFromRange(rangeRead);\n\n const dStr = bezierToSvgPath(subpath);\n\n // Bare sub-path under the trim wrapper — fill / stroke / stroke-width are\n // inherited from the wrapper <g>. The empty-range hide uses `stroke-opacity`\n // (STROKE only), so the fill stays visible without a fill-only twin.\n const base = makeBareSubpath(dStr);\n applyAttr(base, 'strokeDasharray', dashArrayAttr);\n applyAttr(base, 'strokeDashoffset', dashOffsetAttr);\n applyAttr(base, 'strokeOpacity', strokeOpacityAttr);\n\n return [base];\n}\n\n/** Collapsed single-subpath form: the trim host stays a single `<path>` (its own\n * `fill` / `stroke` / `transform` / `id` preserved); the trim stroke attrs are\n * applied directly. */\nfunction collapseLeafWithTrim(\n leaf: PxNode,\n pathLengthPx: number,\n startOffsetPct: number,\n minMaxOffset: [number, number],\n offsetRead: ReadPart<number>,\n rangeRead: ReadPart<PxVec2>,\n): PxNode {\n const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);\n const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);\n\n const node: PxNode = { ...leaf };\n delete node.effects;\n applyAttr(node, 'strokeDasharray', computeAnimAttr(rangeRead, rangeToDasharray));\n applyAttr(node, 'strokeDashoffset', computeAnimAttr(offsetRead, offsetToDashOffset));\n applyAttr(node, 'strokeOpacity', computeOpacityFromRange(rangeRead));\n return node;\n}\n\n/** Fresh `<path>` carrying only the sub-path geometry. Presentation attrs\n * (`fill`, `stroke`, `stroke-width`, `transform`, `id`, …) stay on the wrapper\n * `<g>` and are inherited — bare `<path>`s under a single attrs-bearing `<g>`. */\nfunction makeBareSubpath(dStr: string): PxNode {\n return { type: 'path', d: dStr };\n}\n\n\n// ============================================================================\n// Math\n// ============================================================================\n\nconst SMALL_PADDING_PX = 1;\n\n/** Produces a px dashoffset from a parametric offset. */\nfunction makeOffsetToDashOffset(\n startOffsetPct: number,\n pathLengthPx: number,\n minMaxOffset: [number, number],\n): (offsetVal: number) => number {\n const [minIdx] = getOffsetIndexRange(minMaxOffset);\n return offsetVal => pathLengthPx * (-offsetVal - minIdx + startOffsetPct) + SMALL_PADDING_PX;\n}\n\n/** Produces a px dash pattern of the form `[0, gap, dash, gap, dash, ..., gap]`\n * repeated so the offset can wrap. */\nfunction makeRangeToDasharray(\n pathLengthPx: number,\n minMaxOffset: [number, number],\n): (rangeVal: PxVec2) => Array<number> {\n const [minIdx, maxIdx] = getOffsetIndexRange(minMaxOffset);\n const repeats = maxIdx - minIdx + 1;\n return (rangeVal: PxVec2) => {\n const a = clamp(rangeVal[0], 0, 1);\n const b = clamp(rangeVal[1], 0, 1);\n const minR = Math.min(a, b);\n const maxR = Math.max(a, b);\n const out: Array<number> = [0];\n let gap = SMALL_PADDING_PX;\n for (let i = 0; i < repeats; i++) {\n out.push(gap + minR * pathLengthPx); // GAP\n out.push((maxR - minR) * pathLengthPx); // DASH\n gap = (1 - maxR) * pathLengthPx;\n }\n out.push(gap + SMALL_PADDING_PX); // closing GAP\n return out;\n };\n}\n\n/** Floor/ceil of the negated min/max offset → integer dash-repeat index range. */\nfunction getOffsetIndexRange(minMaxOffset: [number, number]): [number, number] {\n return [\n Math.floor(Math.min(-minMaxOffset[0], -minMaxOffset[1])),\n Math.ceil(Math.max(-minMaxOffset[0], -minMaxOffset[1])),\n ];\n}\n\n\n// ============================================================================\n// Animatable plumbing\n// ============================================================================\n\ntype AnimAttrResult<TOut> =\n | { kind: ReadKind.Static; value: TOut }\n | { kind: ReadKind.Animated; keyframes: Array<PxKeyframe>; loop?: PxLoop | boolean }\n | undefined;\n\nfunction readScalarValues(r: ReadPart<number>): Array<number> {\n if (r.kind === ReadKind.Absent) return [];\n if (r.kind === ReadKind.Static) return [r.value];\n const out: Array<number> = [];\n for (const kf of r.keyframes) {\n const v = keyframeValue(kf);\n if (typeof v === 'number') out.push(v);\n }\n return out;\n}\n\nfunction computeAnimAttr<TIn, TOut>(read: ReadPart<TIn>, map: (v: TIn) => TOut): AnimAttrResult<TOut> {\n if (read.kind === ReadKind.Absent) return undefined;\n if (read.kind === ReadKind.Static) return { kind: ReadKind.Static, value: map(read.value) };\n return {\n kind: ReadKind.Animated,\n keyframes: read.keyframes.map(kf => ({\n time: keyframeTime(kf),\n value: map(keyframeValue(kf) as TIn),\n easing: keyframeEasing(kf),\n })),\n loop: read.loop,\n };\n}\n\n// Static + animated emit (incl. the first-kf static baseline for pre-tick DOM\n// correctness) is the shared `writeAnimatableChannel` — `AnimAttrResult` is a\n// `ReadPart` minus the Absent arm, so it passes straight through.\nfunction applyAttr<T>(node: PxNode, attrName: string, attr: AnimAttrResult<T>): void {\n if (!attr) return;\n writeAnimatableChannel(node, attrName, attr);\n}\n\n/** Width (ms) of the opacity transition emitted at each hide↔show boundary\n * (one SVGA frame = 10 ms at the 100 fps SVGA grid). */\nconst OPACITY_STEP_MS = 10;\n\n/** Empty-range opacity (hide when `startEnd[0] === startEnd[1]`).\n * Static: single 0 if hide always; undefined otherwise.\n * Animated: step-jump kfs at each hide↔show transition (~one SVGA frame\n * wide so the renderer doesn't briefly show the stroke between an empty\n * range and the first non-empty kf at wall-clock t≈0). */\nfunction computeOpacityFromRange(rangeRead: ReadPart<PxVec2>): AnimAttrResult<number> {\n const hide = (v: PxVec2): boolean => v[0] === v[1];\n\n if (rangeRead.kind === ReadKind.Absent) return undefined;\n if (rangeRead.kind === ReadKind.Static) return hide(rangeRead.value) ? { kind: ReadKind.Static, value: 0 } : undefined;\n\n const kfs = rangeRead.keyframes;\n let anyHide = false;\n let allHide = true;\n for (const kf of kfs) {\n if (hide(keyframeValue(kf) as PxVec2)) anyHide = true;\n else allHide = false;\n }\n if (!anyHide) return undefined;\n if (allHide) return { kind: ReadKind.Static, value: 0 };\n\n // Prev/next-aware emitter — only inserts kfs at the transitions to keep the\n // wire compact.\n const out: Array<PxKeyframe> = [];\n for (let i = 0; i < kfs.length; i++) {\n const kf = kfs[i];\n const prevKf = i > 0 ? kfs[i - 1] : undefined;\n const nextKf = i < kfs.length - 1 ? kfs[i + 1] : undefined;\n const t = keyframeTime(kf);\n const thisHide = hide(keyframeValue(kf) as PxVec2);\n const prevHide = prevKf ? thisHide && hide(keyframeValue(prevKf) as PxVec2) : thisHide;\n const nextHide = nextKf ? thisHide && hide(keyframeValue(nextKf) as PxVec2) : thisHide;\n\n if (prevHide && !nextHide) {\n out.push({ time: t, value: 0 });\n out.push({ time: t + OPACITY_STEP_MS, value: 1 });\n } else if (!prevHide && nextHide) {\n out.push({ time: t - OPACITY_STEP_MS, value: 1 });\n out.push({ time: t, value: 0 });\n }\n }\n if (out.length <= 1) return undefined;\n return { kind: ReadKind.Animated, keyframes: out };\n}\n\n\n// ============================================================================\n// Range cross-over\n// ============================================================================\n\ninterface SimpleKf { time: number; value: PxVec2; easing?: any; }\n\n/** Reads `range` and, when the kf sequence has `value[0] > value[1]` anywhere,\n * inserts crossing-point keyframes (bisection-located) and swaps any\n * remaining reversed kfs so every emitted range satisfies `range[0] ≤ range[1]`. */\nfunction readRangeWithCrossings(raw: PxAnimatable<PxVec2> | undefined): ReadPart<PxVec2> {\n const r = readAnimatable<PxVec2>(raw);\n if (r.kind !== ReadKind.Animated) return r;\n\n const kfs: Array<SimpleKf> = r.keyframes.map(kf => ({\n time: keyframeTime(kf),\n value: keyframeValue(kf) as PxVec2,\n easing: keyframeEasing(kf),\n }));\n\n const hasReverse = kfs.some(kf => kf.value[0] > kf.value[1]);\n if (!hasReverse) {\n return {\n kind: ReadKind.Animated,\n keyframes: kfs.map(kf => ({ time: kf.time, value: kf.value, easing: kf.easing })),\n };\n }\n\n const crossingTimes: Array<number> = [];\n for (let i = 1; i < kfs.length; i++) {\n const prev = kfs[i - 1];\n const cur = kfs[i];\n const dPrev = prev.value[1] - prev.value[0];\n const dCur = cur.value[1] - cur.value[0];\n if (dPrev * dCur < 0) {\n const t = bisectionForRangeCrossing(prev, cur);\n if (t !== null && t > prev.time && t < cur.time) {\n crossingTimes.push(Math.round(t));\n }\n }\n }\n const uniqueTs = Array.from(new Set(crossingTimes)).sort((a, b) => a - b);\n\n const out: Array<SimpleKf> = [];\n let j = 0;\n for (const kf of kfs) {\n while (j < uniqueTs.length && uniqueTs[j] < kf.time) {\n const t = uniqueTs[j++];\n const v = interpolateRangeAt(kfs, t);\n const m = (v[0] + v[1]) / 2;\n out.push({ time: t, value: [m, m] });\n }\n const v: PxVec2 = kf.value[0] > kf.value[1] ? [kf.value[1], kf.value[0]] : kf.value;\n out.push({ time: kf.time, value: v, easing: kf.easing });\n }\n while (j < uniqueTs.length) {\n const t = uniqueTs[j++];\n const v = interpolateRangeAt(kfs, t);\n const m = (v[0] + v[1]) / 2;\n out.push({ time: t, value: [m, m] });\n }\n\n return { kind: ReadKind.Animated, keyframes: out };\n}\n\nfunction bisectionForRangeCrossing(prev: SimpleKf, cur: SimpleKf): number | null {\n const f = (t: number): number => {\n const a = (t - prev.time) / (cur.time - prev.time);\n const v0 = prev.value[0] + (cur.value[0] - prev.value[0]) * a;\n const v1 = prev.value[1] + (cur.value[1] - prev.value[1]) * a;\n return v1 - v0;\n };\n let lo = prev.time, hi = cur.time;\n let fLo = f(lo);\n if (fLo === 0) return lo;\n const fHi = f(hi);\n if (fHi === 0) return hi;\n if (fLo * fHi > 0) return null;\n for (let i = 0; i < 100; i++) {\n const mid = (lo + hi) / 2;\n const fMid = f(mid);\n if (fMid === 0 || Math.abs(hi - lo) < 0.0001) return mid;\n if (fLo * fMid < 0) { hi = mid; }\n else { lo = mid; fLo = fMid; }\n }\n return (lo + hi) / 2;\n}\n\nfunction interpolateRangeAt(kfs: Array<SimpleKf>, t: number): PxVec2 {\n if (t <= kfs[0].time) return kfs[0].value;\n if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;\n for (let i = 1; i < kfs.length; i++) {\n if (t <= kfs[i].time) {\n const prev = kfs[i - 1];\n const cur = kfs[i];\n const a = (t - prev.time) / (cur.time - prev.time);\n return [\n prev.value[0] + (cur.value[0] - prev.value[0]) * a,\n prev.value[1] + (cur.value[1] - prev.value[1]) * a,\n ];\n }\n }\n return kfs[kfs.length - 1].value;\n}\n\n\n// ============================================================================\n// Geometry helpers\n// ============================================================================\n\n/** Total arc length of a `PxBezierPath` (sum of per-segment LUT lengths). */\nfunction pxBezierPathLength(path: PxBezierPath): number {\n const v = path.v;\n if (!v || v.length < 2) return 0;\n let total = 0;\n for (let i = 0; i < v.length - 1; i++) {\n total += segmentLength(path, i, i + 1);\n }\n if (path.c && v.length > 1) {\n total += segmentLength(path, v.length - 1, 0);\n }\n return total;\n}\n\nfunction segmentLength(path: PxBezierPath, from: number, to: number): number {\n const v = path.v;\n const p0 = v[from] as [number, number];\n const p3 = v[to] as [number, number];\n const p1 = (path.o?.[from] ?? p0) as [number, number];\n const p2 = (path.i?.[to] ?? p3) as [number, number];\n const lut = bezier2D_arcLengthLUT(p0, p1, p2, p3);\n return lut.ds[lut.ds.length - 1];\n}\n\n/**\n * Cubic-bezier control-point ratio for a quarter arc — the standard circle\n * approximation (max radial error ≈ 0.02%, far below stroke-dash precision).\n */\nconst ARC_KAPPA = 0.5522847498307936;\n\n/**\n * A primitive shape -> outline path `d`, for shapes that carry no `d` of their own.\n * Returns undefined for unsupported types.\n *\n * Start point and winding MATCH the shape's own SVG parameterisation, because the\n * measured length feeds a `stroke-dasharray` that the browser then walks along that\n * very parameterisation — a mismatched start would put the visible trim window in\n * the wrong place.\n *\n * - `<rect>` from `(x+w, y)`, clockwise. Emits the explicit closing-line\n * vertex (`L x0,y0`) before `z`.\n * - `<ellipse>`/`<circle>` from `(cx+rx, cy)` clockwise as four cubic quarters — the\n * SVG-spec equivalent path, which the UA also uses for dashing.\n */\nfunction shapeToPathD(node: PxNode): string | undefined {\n if (node.type === 'rect') {\n const x = Number(node.x ?? 0), y = Number(node.y ?? 0);\n const w = Number(node.width ?? 0), h = Number(node.height ?? 0);\n return 'M' + (x + w) + ',' + y +\n 'L' + (x + w) + ',' + (y + h) +\n 'L' + x + ',' + (y + h) +\n 'L' + x + ',' + y +\n 'L' + (x + w) + ',' + y + 'z';\n }\n\n if (node.type === 'ellipse' || node.type === 'circle') {\n const cx = Number(node.cx ?? 0), cy = Number(node.cy ?? 0);\n const rx = node.type === 'circle' ? Number(node.r ?? 0) : Number(node.rx ?? 0);\n const ry = node.type === 'circle' ? Number(node.r ?? 0) : Number(node.ry ?? 0);\n if (!(rx > 0) || !(ry > 0)) return undefined;\n const kx = rx * ARC_KAPPA, ky = ry * ARC_KAPPA;\n const c = (x1: number, y1: number, x2: number, y2: number, x: number, y: number) =>\n 'C' + x1 + ',' + y1 + ' ' + x2 + ',' + y2 + ' ' + x + ',' + y;\n return 'M' + (cx + rx) + ',' + cy +\n c(cx + rx, cy + ky, cx + kx, cy + ry, cx, cy + ry) +\n c(cx - kx, cy + ry, cx - rx, cy + ky, cx - rx, cy) +\n c(cx - rx, cy - ky, cx - kx, cy - ry, cx, cy - ry) +\n c(cx + kx, cy - ry, cx + rx, cy - ky, cx + rx, cy) + 'z';\n }\n\n return undefined;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * Lightweight, dependency-free applier for \"player-effects\" SVGA JSON.\n *\n * Input: the JSON produced by `SvgaJsonWithPlayerEffectsSerializationUtil`, where\n * structure-creating effects are left UN-applied on `node.effects`. This module\n * reads those effects and MATERIALIZES them into a plain node tree (extra `<g>`\n * wrappers, copies, mask defs) that renders identically to the heavy editor path.\n *\n * Design goals (intentional): minimal, transparent, no merging/optimization. It\n * is fine to emit more nodes than strictly necessary — the only contract is \"same\n * result on screen\". Each effect lives in its own file; this module only wires\n * them into the recursion. Nothing here imports outside `effects/`, so the whole\n * folder can move into the Player codebase verbatim.\n */\n\nimport { identifyContentRefTargets, splitForContentRef } from './reference/contentRefSplit';\nimport { applyFillGradientEffect, applyStrokeGradientEffect } from './paint/gradientEffect';\nimport { applyClipPathEffect } from './clipping/clipPathEffect';\nimport { applyMaskedByEffect, collectMaskAncestorChains } from './clipping/maskedByEffect';\nimport { applyRefAndTransformationEffect, applyRefHref } from './reference/refEffect';\nimport { applyRepeaterEffect } from './transform/repeaterEffect';\nimport { applyAllRetimeEffects } from './reference/retimeEffect';\nimport { applyTextPathEffect } from './text/textPathEffect';\nimport { applyTextGlyphsAlongPath, applyTextGlyphsEffect } from './text/textGlyphsEffect';\nimport { applyStrokeTrimEffect } from './stroke/strokeTrimEffect';\nimport { getDefinitions } from '../format/PxAnimatorConstants';\nimport { resolveTimelineEngine, getAnimatorConfig } from '../format/PxAnimatorConstants';\nimport type { PxNode } from '../format/PxAnimatorTypes';\nimport type { ApplyContext, ApplyResult } from './shared/types';\nimport { clone, genId, indexById, spliceDefs } from './shared/util';\n\nexport type { PxNode } from '../format/PxAnimatorTypes';\nexport type { ApplyResult } from './shared/types';\n\n\n/**\n * Applies all player-effects in `root` and returns a materialized copy plus any\n * generated <defs> nodes, warnings and errors. `root` is not mutated.\n *\n * Two passes:\n * 1. `applyPlayerEffects_exceptRetime` — materializes every effect except retime.\n * 2. `applyPlayerEffects_retime` — applies retime, cloning the NOW-materialized\n * subtrees so retimed `<use>`s see the same wrappers/animations the heavy\n * editor path would produce.\n *\n * Pre-pass identifies every element that is the target of a `<use>` with\n * `ref:{type:'content'}` and allocates a fresh \"inner\" id for it; pass 1 then\n * splits those sources into outer-translate + inner-content layers so the use\n * can target the inner layer.\n * @public @advanced\n */\nexport function materializeNodeEffects(root: PxNode): ApplyResult {\n const ctx: ApplyContext = {\n defs: [], warnings: [], errors: [],\n idMap: new Map(), nextId: 0,\n contentRefInnerIds: new Map(),\n maskAncestorChains: new Map(),\n // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →\n // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).\n engine: resolveTimelineEngine(getAnimatorConfig(root)?.engine),\n glyphs: getDefinitions(root)?.fonts,\n };\n\n const working = clone(root);\n indexById(working, ctx.idMap);\n identifyContentRefTargets(working, ctx, () => genId(ctx, 'inner'));\n collectMaskAncestorChains(working, ctx);\n\n const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);\n const out = applyPlayerEffects_retime(afterPass1, ctx);\n spliceDefs(out, ctx.defs);\n\n return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };\n}\n\n/**\n * Pass 1 — materialize every effect except retime. Retime is preserved on the\n * original (now wrapped-inner) node so pass 2 can find and apply it.\n *\n * After wrapping, the outer-most wrapper for a node with `originalId` is written\n * back into `ctx.idMap` so retime's clone target picks up the FULL materialized\n * subtree, not the bare un-wrapped original.\n *\n * If the node is a content-ref target, `splitForContentRef` re-shapes the\n * materialized result into outer-translate + inner-rest layers — the outer keeps\n * the original id, the inner gets the pre-allocated inner id so the `<use>` can\n * target it.\n */\nfunction applyPlayerEffects_exceptRetime(node: PxNode, ctx: ApplyContext): PxNode {\n if (node.children) node.children = node.children.map(child => applyPlayerEffects_exceptRetime(child, ctx));\n\n const fx = node.effects;\n const originalId = typeof node.id === 'string' ? node.id : undefined;\n const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : undefined;\n\n if (!fx && !innerIdForContentRef) return node;\n\n const { transformBy, repeater, maskedBy, clipPath, strokeTrim, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx ?? {};\n if (fx) delete node.effects;\n\n let n = node;\n // Glyph text: replace the <text>/<tspan> subtree with baked <path> outlines\n // BEFORE any wrapper. Along-path glyphs place+rotate each glyph to the\n // referenced path's tangent; plain glyphs lay out horizontally.\n let consumedByGlyphs = false;\n if (text?.useGlyphs) {\n if (textPath) {\n // Path geometry is INLINE (`textPath.pathData`) — no `<path>` def lookup.\n const pathD = typeof textPath.pathData === 'string' ? textPath.pathData : undefined;\n // textLength passes through as a full PxAnimatable — static stretches the\n // run; animated re-spaces the glyphs over time (same per-glyph sampled\n // keyframe machinery as animated startOffset).\n const glyphed = applyTextGlyphsAlongPath(n, ctx, pathD, textPath.startOffset, textPath.textLength, textPath.pathOverflow);\n if (glyphed) { n = glyphed; consumedByGlyphs = true; } // native textPath NOT applied\n } else {\n n = applyTextGlyphsEffect(n, text, ctx);\n consumedByGlyphs = true;\n }\n }\n // textPath wraps the host's own children in a `<textPath>` — must run BEFORE any\n // structural wrapper (trim/repeater/mask) so the wrapping happens on the un-cloned\n // content first. Skipped when glyphs consumed it.\n if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);\n // Paint-gradient defs are generated FIRST, before any structural wrapper —\n // the gradient effect sits on the innermost element (alongside its `fill`\n // / `stroke` body attrs), so it must materialize before trim/repeater/\n // mask wrap around it. `<linearGradient>` defs themselves don't get\n // wrapped — they live in `ctx.defs` independent of the structure walk.\n n = applyFillGradientEffect(n, fillGradient, ctx);\n n = applyStrokeGradientEffect(n, strokeGradient, ctx);\n n = applyStrokeTrimEffect(n, strokeTrim, ctx); // innermost shape\n n = applyRepeaterEffect(n, repeater, ctx);\n n = applyMaskedByEffect(n, maskedBy, transformBy, ctx); // mask sits on inner element\n n = applyClipPathEffect(n, clipPath, ctx); // clip-path ref on the element\n\n if (innerIdForContentRef) {\n // This node is the SOURCE of a content-ref `<use>` → split into\n // outer-translate + inner-rest + bare element so the use can target the\n // inner layer. But it may ALSO be a content-ref CONSUMER itself (a\n // `<use>` that both references content and is referenced — the nested\n // case): rewrite its OWN ref href first so the split moves a resolved\n // body inward, not the dangling editor-side content id.\n applyRefHref(n, cloneFx, ctx);\n n = splitForContentRef(n, transformBy, originalId!, innerIdForContentRef, ctx);\n } else {\n n = applyRefAndTransformationEffect(n, cloneFx, transformBy, ctx);\n }\n\n // Hand off the retime slice to pass 2 (keeps it nested under `clone`). The\n // ref part (type/source) was consumed above.\n if (cloneFx?.retime) node.effects = { clone: { retime: cloneFx.retime } };\n if (originalId) ctx.idMap.set(originalId, n); // outer wrapper is the clone target\n return n;\n}\n\n/** Pass 2 — apply retime to every `<use>` that carries it. Follows the\n * materialized `<use>.href` (not the editor-side `retime.source`). */\nfunction applyPlayerEffects_retime(node: PxNode, ctx: ApplyContext): PxNode {\n applyAllRetimeEffects(node, ctx);\n return node;\n}\n\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// Materializes `animate.transform` bindings marked `alongPathMode: 'offsetPath'` into\n// CSS Motion Path form:\n//\n// style: { offsetPath: \"path('M…C…')\", offsetAnchor: '0 0',\n// offsetRotate: 'auto' | '0deg', offsetDistance: '0%' }\n// animate.offsetDistance: { keyframes: [{ time, value: 0..1 arc-length fraction }] }\n//\n// Lightweight JSON is the DESIGN format — it carries only the tangented transform plus\n// the mode flag, exactly like `effects` carry recipes. The offset infrastructure the\n// Editor bakes into PRE-RENDERED SVG does not exist here, so the player must derive it,\n// the same way it derives effects. Without this stage the mode flag was silently ignored\n// (or, worse, the binding skipped) and a lightweight `offsetPath` document lost or\n// mis-rendered its motion.\n//\n// Mirrors the Editor's `TPositionVecValue.getOffsetAlongPathForCss`: same inline\n// `path('…')` syntax (the original Motion Path syntax — widest support), same\n// arc-length-fraction keyframe values, easing and loop carried over.\n\nimport { OFFSET_DISTANCE_ATTR, TRANSFORM_ATTR, TRANSFORM_PART } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxKeyframe, PxNode, PxPropertyAnimation } from '../format/PxAnimatorTypes';\nimport { keyframeTime, keyframeValue, keyframeEasing, keyframeTangentIn, keyframeTangentOut } from '../format/PxAnimatorTypes';\n\ntype PxVec2 = [number, number];\n\n\n/** Cubic-bezier point at parameter t. */\nfunction cubicAt(p0: PxVec2, c1: PxVec2, c2: PxVec2, p1: PxVec2, t: number): PxVec2 {\n const u = 1 - t;\n const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;\n return [a * p0[0] + b * c1[0] + c * c2[0] + d * p1[0],\n a * p0[1] + b * c1[1] + c * c2[1] + d * p1[1]];\n}\n\n/** Approximate cubic segment length by dense polyline sampling. */\nfunction cubicLength(p0: PxVec2, c1: PxVec2, c2: PxVec2, p1: PxVec2, steps = 64): number {\n let len = 0;\n let prev = p0;\n for (let i = 1; i <= steps; i++) {\n const pt = cubicAt(p0, c1, c2, p1, i / steps);\n len += Math.hypot(pt[0] - prev[0], pt[1] - prev[1]);\n prev = pt;\n }\n return len;\n}\n\nconst fmt = (n: number): string => {\n const r = Math.round(n * 10000) / 10000;\n return Object.is(r, -0) ? '0' : String(r);\n};\n\n/**\n * Attempts the rewrite for one transform binding. Returns undefined when this binding is\n * not an offset-path candidate (no explicit mode, no curve, or parts the encoding cannot\n * express) — the caller then leaves the binding for the ordinary pipeline.\n */\nfunction buildOffsetPath(propAnim: PxPropertyAnimation): {\n pathStr: string; distanceKfs: Array<PxKeyframe>; autoOrient: boolean; anchor: PxVec2;\n} | undefined {\n if ((propAnim as { alongPathMode?: string }).alongPathMode !== 'offsetPath') return undefined;\n\n const kfs = propAnim.keyframes as Array<PxKeyframe> | undefined;\n if (!kfs || kfs.length < 2) return undefined;\n\n // Every keyframe must supply a translate; other ANIMATED parts (rotate/scale\n // varying per keyframe) cannot ride the offset encoding — bail to the ordinary\n // pipeline rather than render them wrong. Static `origin` is tolerated: alone (no\n // rotate/scale around it) it composes to identity.\n // GEOMETRY: the model composes translate(t)·translate(o)·rotate·translate(-o), so the\n // point that RIDES the path — and the pivot `autoOrient` rotates about — is the ORIGIN\n // point of the element, located at t+o. Encode exactly that: the path traces t_i+o and\n // `offset-anchor` pins the element's own origin point (o, in its box) to the path.\n // Anchoring 0 0 on the raw translates put the element's CORNER on a corner-trajectory\n // and pivoted rotation about the corner — visibly off the path for centered origins.\n const first = keyframeValue(kfs[0]) as { origin?: PxVec2 } | undefined;\n const anchor: PxVec2 = first?.origin && first.origin.length >= 2\n ? [first.origin[0], first.origin[1]] : [0, 0];\n\n const points: Array<PxVec2> = [];\n for (const kf of kfs) {\n const v = keyframeValue(kf) as { translate?: PxVec2; origin?: PxVec2 } | undefined;\n const tr = v?.translate;\n if (!tr || tr.length < 2) return undefined;\n const parts = Object.keys(v as object);\n if (parts.some(p => p !== 'translate' && p !== 'origin')) return undefined;\n // An origin ANIMATED across keyframes shifts the pivot mid-flight — inexpressible\n // as a single offset-anchor; bail to the sampled pipeline.\n const o = v?.origin ?? [0, 0];\n if (o[0] !== anchor[0] || o[1] !== anchor[1]) return undefined;\n points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);\n }\n // No tangent anywhere ⇒ straight lines ⇒ a plain translate animation renders this\n // everywhere with no support floor; the offset encoding buys nothing.\n if (!kfs.some(kf => keyframeTangentIn(kf) || keyframeTangentOut(kf))) return undefined;\n\n // Path string + per-segment arc lengths, in one pass.\n let d = 'M' + fmt(points[0][0]) + ',' + fmt(points[0][1]);\n const segLens: Array<number> = [];\n for (let i = 0; i < points.length - 1; i++) {\n const p0 = points[i], p1 = points[i + 1];\n const to = keyframeTangentOut(kfs[i]) ?? [0, 0];\n const ti = keyframeTangentIn(kfs[i + 1]) ?? [0, 0];\n const c1: PxVec2 = [p0[0] + to[0], p0[1] + to[1]];\n const c2: PxVec2 = [p1[0] + ti[0], p1[1] + ti[1]];\n d += 'C' + fmt(c1[0]) + ',' + fmt(c1[1]) + ',' + fmt(c2[0]) + ',' + fmt(c2[1]) + ',' + fmt(p1[0]) + ',' + fmt(p1[1]);\n segLens.push(cubicLength(p0, c1, c2, p1));\n }\n const total = segLens.reduce((a, b) => a + b, 0);\n if (!(total > 0)) return undefined;\n\n // `offset-distance` percentages are ARC-LENGTH fractions — each keyframe lands at its\n // cumulative share of the path length (mirrors the Editor's `segments.endPct`).\n const distanceKfs: Array<PxKeyframe> = [];\n let cum = 0;\n for (let i = 0; i < kfs.length; i++) {\n if (i > 0) cum += segLens[i - 1];\n const out: PxKeyframe = { t: keyframeTime(kfs[i]), v: cum / total } as never;\n const e = keyframeEasing(kfs[i]);\n if (e !== undefined) (out as { e?: unknown }).e = e;\n distanceKfs.push(out);\n }\n\n return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor };\n}\n\n/**\n * Walks the tree and rewrites every `offsetPath`-marked transform binding into\n * offset-path styles + an `offsetDistance` binding. Non-candidates are left untouched.\n * Runs BEFORE loop expansion so a carried `loop` expands on the new binding.\n */\nexport function materializeOffsetPathsInTree(root: PxAnimatedSvgDocument): PxAnimatedSvgDocument {\n const walk = (node: PxNode): PxNode => {\n let out = node;\n const anim = node.animate as Record<string, PxPropertyAnimation> | undefined;\n const transform = anim?.['transform'];\n if (transform) {\n const built = buildOffsetPath(transform);\n if (built) {\n const newAnimate: Record<string, PxPropertyAnimation> = { ...anim };\n delete newAnimate[TRANSFORM_ATTR];\n const distance: PxPropertyAnimation = { keyframes: built.distanceKfs as never } as never;\n if (transform.loop !== undefined) distance.loop = transform.loop;\n newAnimate[OFFSET_DISTANCE_ATTR] = distance;\n\n // The element's position now comes from the path — a remaining static\n // `translate` (the design base value) would ADD to it. Other static parts\n // (rotate/scale/origin) survive; the candidate check above guarantees the\n // animation itself carried none.\n const staticTr = node.transform as Record<string, unknown> | string | undefined;\n let newTransform = staticTr;\n if (staticTr && typeof staticTr === 'object') {\n const t = { ...staticTr };\n delete t[TRANSFORM_PART.translate];\n delete t[TRANSFORM_PART.origin]; // pivot is offset-anchor now; alone it is identity\n newTransform = Object.keys(t).length ? t : undefined;\n }\n\n out = {\n ...node,\n animate: newAnimate,\n style: {\n ...(node.style as Record<string, unknown> | undefined),\n offsetPath: \"path('\" + built.pathStr + \"')\",\n offsetAnchor: fmt(built.anchor[0]) + 'px ' + fmt(built.anchor[1]) + 'px',\n offsetRotate: built.autoOrient ? 'auto' : '0deg',\n offsetDistance: '0%',\n },\n } as PxNode;\n if (newTransform !== undefined) (out as { transform?: unknown }).transform = newTransform;\n else delete (out as { transform?: unknown }).transform;\n }\n }\n if (out.children?.length) {\n const children = out.children.map(walk);\n if (children.some((c, i) => c !== out.children![i])) out = { ...out, children };\n }\n return out;\n };\n return walk(root) as PxAnimatedSvgDocument;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * `<use>` instance materializer.\n *\n * WAAPI / CSS animations applied to an SVG source element don't reliably\n * render through `<use>` shadow trees in Chrome and Safari — the source\n * animates, the `<use>` shows static. This module fixes that by deep-cloning\n * every animated target into the corresponding `<use>` site (with fresh ids\n * and rewritten internal refs), and replacing the `<use>` with a `<g>` that\n * carries the use's transform/x/y attributes. Both engines (frames + waapi)\n * then animate the cloned nodes directly.\n *\n * Shares the deep-clone + id-regen + ref-rewrite primitives with the retime\n * effect (see `PxNodeCloneUtil`).\n *\n * Immutable: returns the input by reference when no `<use>` needs\n * materialization; otherwise returns a new tree sharing untouched sub-trees.\n */\n\nimport { applyUseOffsetToG, deepClonePxNode, regenerateIdsAndRewriteRefs } from '../util/PxNodeCloneUtil';\nimport type { PxNode } from '../format/PxAnimatorTypes';\n\n\n/**\n * Walks `root`. For every `<use>` whose `href` target subtree contains any\n * `animate` bucket (recursively, following further `<use>` chains and\n * children), replaces the `<use>` with a `<g>` carrying a deep clone of the\n * target. The clone gets a fresh id on every node, and any internal `href` /\n * `url(#…)` refs that pointed at ids inside the cloned subtree are rewritten\n * to the new ids. Refs that point OUTSIDE the cloned subtree (e.g. a sibling\n * gradient) are left untouched.\n *\n * Recursive: if the materialized clone itself contains a `<use>` that needs\n * materialization, that's handled in the same pass.\n * @internal\n */\nexport function materializeAnimatedUseInstances(root: PxNode): PxNode {\n const idMap = buildIdMap(root);\n const animatedIds = computeAnimatedSubtreeIds(root, idMap);\n if (animatedIds.size === 0) return root;\n\n let idCounter = 0;\n const genId = (): string => '_lw_use_mat_' + (++idCounter);\n\n // The use's default width/height resolves against its viewport (= the\n // root `<svg>`). We snapshot the root's viewBox dimensions once so the\n // symbol-rewrite below can compute the viewBox-to-use-viewport scaling\n // without re-walking the tree for each use.\n const rootViewport = readRootViewport(root);\n\n // Collects clipPath / other defs generated by the symbol-rewrite path. After\n // the walk completes, these are spliced into the root tree's `<defs>`.\n const defsCollector: Array<PxNode> = [];\n const walked = walkAndMaterialize(root, idMap, animatedIds, genId, defsCollector, rootViewport);\n if (defsCollector.length === 0) return walked;\n\n // Append a `<defs>` child carrying every collected def. Browsers honor\n // multiple `<defs>` on the same SVG root, so we don't need to splice into\n // an existing one (which would also be more invasive — defs may have been\n // shared by reference with the original tree).\n const defsNode: PxNode = { type: 'defs', children: defsCollector };\n const newChildren = [...(walked.children ?? []), defsNode];\n return { ...walked, children: newChildren };\n}\n\n\n/** Reads the root `<svg>`'s effective viewport dimensions. Used as the\n * default for `<use>` width/height (which default to 100% of viewport per\n * SVG spec). Tries `viewBox` first (the typical authored shape from our\n * serializer), then falls back to explicit `width`/`height` attrs. */\nfunction readRootViewport(root: PxNode): [number, number] {\n const vb = parseViewBox((root as { viewBox?: unknown }).viewBox);\n if (vb) return [vb[2], vb[3]];\n const w = numericAttr((root as { width?: unknown }).width);\n const h = numericAttr((root as { height?: unknown }).height);\n if (w !== undefined && h !== undefined) return [w, h];\n // No info — fall back to a 1:1 viewport so the scale degrades to \"identity\n // along the limiting axis\" (= the historical no-scale behavior). Better\n // than NaN; the failing case is \"root has no viewport info at all\".\n return [1, 1];\n}\n\nfunction numericAttr(v: unknown): number | undefined {\n if (typeof v === 'number') return v;\n if (typeof v === 'string') {\n // Strip a trailing `px` or `%` — for percentages we don't have an\n // outer container to resolve against, so we treat the raw number as\n // the absolute size (correct when the user only authored a viewBox\n // and width/height match it).\n const n = parseFloat(v);\n return Number.isFinite(n) ? n : undefined;\n }\n return undefined;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internals\n// ─────────────────────────────────────────────────────────────────────────────\n\n\nfunction buildIdMap(root: PxNode): Map<string, PxNode> {\n const map = new Map<string, PxNode>();\n const visit = (n: PxNode): void => {\n if (typeof n.id === 'string') map.set(n.id, n);\n n.children?.forEach(visit);\n };\n visit(root);\n return map;\n}\n\n\n/** Returns the set of ids whose subtree (including descendants and any\n * `<use>`-referenced sub-subtrees) contains at least one `animate` bucket.\n * Uses a per-walk `visited` set to avoid infinite recursion on `<use>` cycles. */\nfunction computeAnimatedSubtreeIds(root: PxNode, idMap: Map<string, PxNode>): Set<string> {\n const cache = new WeakMap<PxNode, boolean>();\n const result = new Set<string>();\n\n const hasAnim = (n: PxNode, visiting: Set<PxNode>): boolean => {\n const cached = cache.get(n);\n if (cached !== undefined) return cached;\n if (visiting.has(n)) return false; // cycle guard\n visiting.add(n);\n\n let r = false;\n if (n.animate && typeof n.animate === 'object' && !Array.isArray(n.animate)) {\n for (const _ in n.animate) { r = true; break; }\n }\n if (!r && n.children) {\n for (const ch of n.children) {\n if (hasAnim(ch, visiting)) { r = true; break; }\n }\n }\n if (!r && n.type === 'use' && typeof n.href === 'string') {\n const targetId = stripHash(n.href);\n const target = targetId ? idMap.get(targetId) : undefined;\n if (target) r = hasAnim(target, visiting);\n }\n\n visiting.delete(n);\n cache.set(n, r);\n return r;\n };\n\n for (const [id, node] of idMap) {\n if (hasAnim(node, new Set())) result.add(id);\n }\n return result;\n}\n\n\nfunction stripHash(href: unknown): string | undefined {\n if (typeof href !== 'string') return undefined;\n return href.startsWith('#') ? href.slice(1) : href;\n}\n\n\n/** Builds the materialized `<g>` replacement for one `<use>` whose target's\n * subtree is animated. Carries the use's transform / position attributes. */\nfunction materializeOneUse(\n useNode: PxNode,\n target: PxNode,\n idMap: Map<string, PxNode>,\n animatedIds: Set<string>,\n genId: () => string,\n defsCollector: Array<PxNode>,\n rootViewport: [number, number],\n): PxNode {\n const clone = deepClonePxNode(target);\n regenerateIdsAndRewriteRefs(clone, genId);\n\n // Use's effective width/height — explicit attrs if set; else the referenced\n // `<symbol>`'s OWN viewBox size (a `<use>` on a symbol-with-viewBox renders at\n // the symbol's natural size, NOT 100% of the viewport — the latter scales the\n // symbol up, e.g. 80×80 → 139×139). Our serializer now always emits explicit\n // width/height, so this fallback is the safety net for inputs that omit them.\n // Falls back to the root viewport only when there's no viewBox to size from.\n const symbolViewBox = clone.type === 'symbol' ? parseViewBox((clone as { viewBox?: unknown }).viewBox) : undefined;\n const useW = numericAttr((useNode as { width?: unknown }).width) ?? symbolViewBox?.[2] ?? rootViewport[0];\n const useH = numericAttr((useNode as { height?: unknown }).height) ?? symbolViewBox?.[3] ?? rootViewport[1];\n\n // `<symbol>` is invisible unless instantiated by `<use>` — a bare clone\n // of it inside our wrapping `<g>` would render nothing. Rewrite the\n // clone-root `<symbol>` into a `<g>` carrying the symbol's viewBox-derived\n // transform + clip (mirroring the browser's `<use href=\"#symbol\">`\n // viewport mapping). Any clipPath defs created go into `defsCollector`.\n const rewrittenClone = clone.type === 'symbol'\n ? rewriteSymbolRootToGroup(clone, genId, defsCollector, useW, useH)\n : clone;\n\n // Recurse into the clone — any nested `<use>` it contains may itself\n // reference an animated subtree and need materialization.\n const materializedClone = walkAndMaterialize(rewrittenClone, idMap, animatedIds, genId, defsCollector, rootViewport);\n\n // Replace `<use>` with `<g>` wrapping the clone. Keep all of use's own\n // attrs except `href` (now meaningless). `<g>` has no `x`/`y`, so the use's\n // position offset is converted to a `translate(x,y)` (applied AFTER any\n // transform — see `applyUseOffsetToG`) rather than silently dropped.\n const newNode: PxNode = { ...useNode, type: 'g', children: [materializedClone] };\n delete (newNode as { href?: string }).href;\n // The viewport-mapping `<g>` we just emitted on the clone root already\n // carries its own width/height-derived scale/translate. Drop the use's\n // own `width`/`height` so they don't appear on the outer `<g>` (where\n // they'd be ignored anyway — `<g>` has no width/height — but emitting\n // them as attributes would be misleading).\n delete (newNode as { width?: unknown }).width;\n delete (newNode as { height?: unknown }).height;\n return applyUseOffsetToG(newNode);\n}\n\n\n/** `<symbol>` doesn't render directly. To keep the visual result when a\n * cloned `<symbol>` lands as the root of a materialized `<use>` target, we\n * rewrite it into a `<g>` whose transform + clip mirror the browser's\n * `<use href=\"#symbol-with-viewBox\">` viewport mapping:\n *\n * - `transform = \"translate(xOff, yOff) scale(s) translate(-vbX, -vbY)\"`\n * where `s` and the centering offsets are derived from the symbol's\n * `viewBox` and the use's effective width/height per `preserveAspectRatio`\n * (default `xMidYMid meet`). Without the scale the clone renders at 1:1\n * in symbol coords, which doesn't match `<use>`'s natural behavior.\n * - `clipPath = \"url(#<fresh-id>)\"` referencing a `<rect>` matching the\n * viewBox extent (in symbol coords). With the surrounding transform\n * above the clip rect maps to the visible viewport area, matching how\n * `<symbol>` viewport clipping works.\n *\n * Symbols without a `viewBox` rewrite to a plain `<g>` — no viewport\n * mapping to preserve.\n *\n * The created clipPath defs are appended to `defsCollector`; the caller\n * splices them into the root tree once materialization finishes.\n */\nfunction rewriteSymbolRootToGroup(\n symbolNode: PxNode,\n genId: () => string,\n defsCollector: Array<PxNode>,\n useW: number,\n useH: number,\n): PxNode {\n const viewBox = parseViewBox((symbolNode as { viewBox?: unknown }).viewBox);\n const g: PxNode = { ...symbolNode, type: 'g' };\n // Strip symbol-only attributes that don't belong on `<g>`.\n delete (g as { viewBox?: unknown }).viewBox;\n delete (g as { preserveAspectRatio?: unknown }).preserveAspectRatio;\n delete (g as { width?: unknown }).width;\n delete (g as { height?: unknown }).height;\n\n if (!viewBox) return g; // no viewBox → no transform / clip required\n\n const [vbX, vbY, vbW, vbH] = viewBox;\n // `xMidYMid meet` (SVG default) — uniform scale, fit the longer axis;\n // center the shorter axis. Mirrors what the browser does on `<use>` for\n // a symbol-with-viewBox without an explicit preserveAspectRatio override.\n const scale = vbW > 0 && vbH > 0 ? Math.min(useW / vbW, useH / vbH) : 1;\n const xOff = (useW - vbW * scale) / 2;\n const yOff = (useH - vbH * scale) / 2;\n\n const parts: Array<string> = [];\n if (xOff !== 0 || yOff !== 0) parts.push('translate(' + xOff + ',' + yOff + ')');\n if (scale !== 1) parts.push('scale(' + scale + ')');\n if (vbX !== 0 || vbY !== 0) parts.push('translate(' + (-vbX) + ',' + (-vbY) + ')');\n if (parts.length) (g as { transform?: string }).transform = parts.join('');\n\n const clipId = genId();\n defsCollector.push({\n type: 'clipPath',\n id: clipId,\n children: [{ type: 'rect', x: vbX, y: vbY, width: vbW, height: vbH }],\n } as PxNode);\n (g as { clipPath?: string }).clipPath = 'url(#' + clipId + ')';\n return g;\n}\n\n\nfunction parseViewBox(v: unknown): [number, number, number, number] | undefined {\n if (typeof v !== 'string') return undefined;\n const parts = v.trim().split(/[\\s,]+/).map(Number);\n if (parts.length < 4 || parts.some(n => !Number.isFinite(n))) return undefined;\n return [parts[0], parts[1], parts[2], parts[3]];\n}\n\n\nfunction walkAndMaterialize(\n node: PxNode,\n idMap: Map<string, PxNode>,\n animatedIds: Set<string>,\n genId: () => string,\n defsCollector: Array<PxNode>,\n rootViewport: [number, number],\n): PxNode {\n if (node.type === 'use' && typeof node.href === 'string') {\n const targetId = stripHash(node.href);\n if (targetId && animatedIds.has(targetId)) {\n const target = idMap.get(targetId);\n if (target) return materializeOneUse(node, target, idMap, animatedIds, genId, defsCollector, rootViewport);\n }\n }\n\n if (!node.children) return node;\n let changed = false;\n const newChildren = node.children.map(ch => {\n const m = walkAndMaterialize(ch, idMap, animatedIds, genId, defsCollector, rootViewport);\n if (m !== ch) changed = true;\n return m;\n });\n return changed ? { ...node, children: newChildren } : node;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * Single-call materialization pipeline.\n *\n * Runs the full sequence of document-level transformations that turn the\n * wire-format `PxAnimatedSvgDocument` into a flat tree any renderer can\n * consume. The player itself calls this from `createAnimatorImpl`; the same\n * function is exported for the Editor (or any external caller) so the\n * Editor's flat-export path is GUARANTEED to be byte-identical to what the\n * player sees internally — no parallel pipeline to drift.\n *\n * 1. `materializeNodeEffects` — `node.effects` (ref / transformation / repeater /\n * maskedBy / strokeTrim / retime) materialized into wrappers, defs, clones.\n * 2. `materializeInternalLoopsInTree` — every `propAnim.loop` expanded into\n * repeated keyframes filling the duration.\n * 3. `materializeMotionPathsInTree` — `transform` kfs with tangents +\n * `autoOrient` flattened into sampled `{translate, rotate}` kfs. Only\n * for `engine === waapi` — frames-mode keeps the parametric form and\n * evaluates per frame for max spatial fidelity.\n * 4. `materializeAnimatedUseInstances` — `<use>` referencing an animated\n * subtree replaced with a `<g>` carrying a deep clone (fresh ids).\n * Only for `engine === waapi` — frames-mode updates source attrs\n * per frame, which propagate through `<use>` shadow trees natively.\n *\n * Immutable: input doc is never mutated. Steps that didn't apply (the engine\n * gating or \"nothing to do\" early-outs) return the input by reference.\n */\n\nimport { materializeNodeEffects } from '../effects/PlayerEffectsUtil';\nimport { PX_DEFAULT_DURATION_MS } from '../util/PxAnimatorUtil';\nimport { materializeInternalLoopsInTree } from '../animation/PxDefinitions';\nimport { materializeOffsetPathsInTree } from './PxOffsetPathMaterializer';\nimport { materializeMotionPathsInTree } from './PxMotionPath';\nimport type { MotionPathMaterializationOptions } from './PxMotionPath';\nimport { getAnimatorConfig, PxTimelineEngine } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxNode } from '../format/PxAnimatorTypes';\nimport { materializeAnimatedUseInstances } from './PxAnimatorUseMaterializer';\n\n\n/** Options accepted by {@link materializeAllInTree}. Mostly forwarded to the\n * per-stage materializers; ordering is fixed (see module doc). * @internal\n */\nexport interface PxMaterializeAllOptions {\n /** Knobs forwarded to `materializeMotionPathsInTree`. Only consulted for\n * `engine === waapi` — frames-mode skips that stage entirely. */\n motionPath?: MotionPathMaterializationOptions;\n}\n\n\n/** @public @advanced */\nexport function materializeAllInTree(\n doc: PxAnimatedSvgDocument,\n engine: PxTimelineEngine,\n options?: PxMaterializeAllOptions,\n): PxAnimatedSvgDocument {\n // 1. Effects → structural materialization. Always runs; returns a fresh root.\n let root = materializeNodeEffects(doc).root as PxAnimatedSvgDocument;\n\n // 1b. `alongPathMode: 'offsetPath'` transforms → CSS Motion Path (offset-path style\n // + `offsetDistance` binding). Both engines: frames drives `offset-distance` per\n // rAF, waapi animates it natively (percent values). BEFORE loop expansion so a\n // carried `loop` expands on the rewritten binding.\n root = materializeOffsetPathsInTree(root);\n\n // 2. Loops → flat repeated keyframes. Always runs (both engines need flat\n // kfs covering the duration; per-binding expansion in\n // `normalizeKeyframes` becomes a no-op once the loop field is consumed).\n const duration = getAnimatorConfig(root)?.duration ?? PX_DEFAULT_DURATION_MS;\n root = materializeInternalLoopsInTree(root, duration);\n\n if (engine === PxTimelineEngine.native) {\n // 3. Motion-along-path → sampled `{translate, rotate}` kfs. WAAPI can't\n // evaluate parametric tangents; frames-mode does that per frame so\n // we skip this for frames.\n root = materializeMotionPathsInTree(root, options?.motionPath);\n\n // 4. <use> referencing animated subtrees → <g> wrapping a fresh clone.\n // WAAPI / CSS animations don't reliably propagate through SVG <use>\n // shadow trees in Chrome / Safari; frames-mode updates source\n // attributes per frame and the shadow tree picks those up natively.\n root = materializeAnimatedUseInstances(root);\n\n // 5. Prune <defs> `<g>`/`<symbol>` entries that step 4 orphaned — i.e. no\n // `<use>` references them any more (the animated uses that did got\n // inlined into `<g>`+clones). waapi-only: frames keeps `<use href>`,\n // so nothing is orphaned there.\n root = pruneUnreferencedDefs(root);\n }\n\n return root;\n}\n\n\n/**\n * Removes orphaned `<defs>` entries: direct `<defs>` children of type `<g>` /\n * `<symbol>` whose `id` is no longer targeted by ANY `<use href>` in the tree.\n * Runs after step 4 (`materializeAnimatedUseInstances`), which inlines animated\n * `<use>`s and thereby leaves their former defs targets unreferenced.\n *\n * Loops to a fixpoint so chains collapse fully: pruning an entry can drop the\n * `<use>`s inside it, which in turn orphans the entries THOSE referenced. The\n * loop also drops a `<defs>` node once pruning has emptied it.\n *\n * Scope is intentionally limited to `<g>`/`<symbol>` (the `<use>`-target element\n * types) so `url(#…)`-referenced defs (gradients / masks / clipPaths / filters)\n * are never touched. Mutates `root` in place — safe, as it's a freshly\n * materialized tree owned by {@link materializeAllInTree}.\n */\nfunction pruneUnreferencedDefs(root: PxAnimatedSvgDocument): PxAnimatedSvgDocument {\n const stripHash = (h: string): string => (h.startsWith('#') ? h.slice(1) : h);\n const walk = (n: PxNode, fn: (n: PxNode) => void): void => { fn(n); n.children?.forEach(c => walk(c, fn)); };\n\n let changed = true;\n while (changed) {\n changed = false;\n const referenced = new Set<string>();\n walk(root, n => {\n if (n.type === 'use' && typeof n.href === 'string') referenced.add(stripHash(n.href));\n });\n walk(root, n => {\n if (!n.children) return;\n let kept = n.children;\n // (a) inside <defs>: drop <g>/<symbol> entries no <use> targets any more\n if (n.type === 'defs') {\n kept = kept.filter(c =>\n !((c.type === 'g' || c.type === 'symbol') && typeof c.id === 'string' && !referenced.has(c.id)));\n }\n // (b) anywhere: drop a now-empty <defs> child\n kept = kept.filter(c => !(c.type === 'defs' && (!c.children || c.children.length === 0)));\n if (kept.length !== n.children.length) { n.children = kept; changed = true; }\n });\n }\n return root;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ONE time contract for every engine (API review §3). The same three calls used to mean three\n// different things:\n//\n// | engine | getCurrentTime() | setCurrentTime(t) | setPlaybackRate(0) |\n// |-----------------|-------------------------|-------------------|--------------------|\n// | web, WAAPI | ms across the whole run | NOT clamped | accepted |\n// | web, frame loop | ms across the whole run | clamped | rejected + warn |\n// | React Native | ms within ONE iteration | wrapped | rejected + warn |\n//\n// So a slider built on `getCurrentTime()` jumped back every iteration on React Native, and\n// `timeline.engine: 'auto'` meant two documents on one page could answer the same call\n// differently. The contract every player now implements:\n//\n// - time is ms from the start of the WHOLE run, iterations included — never per-iteration;\n// - a seek clamps to [0, seekCeilingMs];\n// - a rate of 0 is rejected everywhere, with this one message.\n\n/** The one message every engine prints for a rejected rate. @public @advanced */\nexport const PX_RATE_REJECTED = 'setPlaybackRate: rate must be finite and non-zero';\n\n/**\n * A playback rate is usable when it is finite and non-zero.\n *\n * 0 is rejected rather than accepted: it freezes the animation in a state indistinguishable\n * from a stuck player, and `pause()` already says that properly. Two of the three engines\n * rejected it already — this makes the third agree.\n * @public @advanced\n */\nexport function isValidPlaybackRate(rate: number): boolean {\n return Number.isFinite(rate) && rate !== 0;\n}\n\n/**\n * Highest seekable time, ms — `duration × iterations`.\n *\n * `Infinity` for an endless timeline, so callers must test `Number.isFinite` before using it\n * as an upper bound. This is the SEEK ceiling, which is deliberately not the same thing as the\n * span `progress` covers — see `progressSpanMs`.\n * @public @advanced\n */\nexport function seekCeilingMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return Infinity;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * The span `progress` 0→1 covers, ms.\n *\n * An endless timeline maps progress onto ONE iteration — the rule the components already\n * document for the `progress` prop (\"0–1 of duration × iterations, one iteration when\n * iterations is 'infinite'\"). Always finite, so it is safe as a divisor.\n * @public @advanced\n */\nexport function progressSpanMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return durationMs;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * Clamps a seek into [0, ceiling].\n *\n * `NaN` and anything below 0 land on 0. `Infinity` means \"the end\", so it clamps to the ceiling\n * like any other overshoot — except on an endless timeline, where there is no end to land on and\n * a non-finite playhead would poison every later read, so that reads as 0.\n * @public @advanced\n */\nexport function clampSeekMs(timeMs: number, ceilingMs: number): number {\n if (Number.isNaN(timeMs) || timeMs < 0) return 0;\n if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;\n return Number.isFinite(timeMs) ? timeMs : 0;\n}\n\n/**\n * Whole-run ms → 0–1.\n *\n * A finite timeline clamps at both ends. An endless one wraps within the current iteration,\n * so the value stays meaningful however long it has been running. A zero-length span reads as\n * 0 rather than NaN.\n * @public @advanced\n */\nexport function timeToProgress(timeMs: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(timeMs)) return 0;\n if (timeMs <= 0) return 0;\n if (iterations === Infinity) return (timeMs % span) / span;\n return timeMs >= span ? 1 : timeMs / span;\n}\n\n/** 0–1 → whole-run ms, clamped into the span. A non-finite progress reads as 0. @public @advanced */\nexport function progressToTimeMs(progress: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(progress)) return 0;\n if (progress <= 0) return 0;\n return progress >= 1 ? span : progress * span;\n}\n\n/** A playhead that keeps whole-run time across iterations. See `createRunClock`. @public @advanced */\nexport interface PxRunClock {\n /** Whole-run position right now, ms, clamped to the ceiling. */\n now(): number;\n /** True while time is advancing. */\n isRunning(): boolean;\n /** Start or resume at `rate`, optionally from a given whole-run time. */\n start(rate: number, atMs?: number): void;\n /** Stop advancing, freezing the current position. */\n stop(): void;\n /** Jump to a whole-run time; keeps running if it already was. */\n seek(ms: number): void;\n}\n\n/**\n * A whole-run playhead that survives repetition.\n *\n * React Native drives playback with Reanimated's `withRepeat`, whose shared value only ever\n * holds the position WITHIN one iteration and which never reports how many iterations have\n * elapsed. Whole-run time therefore cannot be recovered from it: under `alternate` the value\n * runs backwards rather than wrapping, which is indistinguishable from a negative rate. So the\n * time is kept on a clock of its own — the same thing the frame-loop engine does inline.\n *\n * `nowFn` is injectable so this is testable without real time passing.\n * @public @advanced\n */\nexport function createRunClock(ceilingMs: number, nowFn: () => number = Date.now): PxRunClock {\n let baseMs = 0; // whole-run ms as of the last start/seek/stop\n let startedAt = 0; // nowFn() when running began\n let running = false; // a FLAG, not `startedAt !== 0`: a time source may legitimately\n let rate = 1; // read 0, and `Date.now()` never does — so the bug would hide.\n\n const value = (): number => running\n ? clampSeekMs(baseMs + (nowFn() - startedAt) * rate, ceilingMs)\n : clampSeekMs(baseMs, ceilingMs);\n\n return {\n now: value,\n isRunning: () => running,\n start: (r: number, atMs?: number) => {\n baseMs = clampSeekMs(atMs ?? value(), ceilingMs);\n rate = isValidPlaybackRate(r) ? r : 1;\n startedAt = nowFn();\n running = true;\n },\n stop: () => {\n baseMs = value();\n startedAt = 0;\n running = false;\n },\n seek: (ms: number) => {\n baseMs = clampSeekMs(ms, ceilingMs);\n if (running) startedAt = nowFn();\n },\n };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { type PxAnimatedSvgDocument, type PxAnimatorApi, type PxEngineCallbacks } from '../format/PxAnimatorTypes';\nimport { getAnimatorConfig, PxTimelineEngine } from '../format/PxAnimatorConstants';\nimport { camelCaseToKebabWordIfNeeded, clamp, PX_DEFAULT_DURATION_MS, PX_STYLE_ATTR_NAMES } from '../util/PxAnimatorUtil';\nimport { calcAnimationValues, normalizeBindings } from '../animation/PxDefinitions';\nimport { clampSeekMs, isValidPlaybackRate, progressToTimeMs, PX_RATE_REJECTED, timeToProgress } from './PxPlaybackTime';\n\n\n// Frame scheduling — resolved LAZILY from globalThis on every call so test\n// harnesses that install fake timers (and hosts that polyfill rAF late) are\n// honored. Falls back to a ~60fps setTimeout when rAF is unavailable.\nfunction requestFrame(cb: () => void): number {\n const g: any = globalThis as any;\n if (typeof g.requestAnimationFrame === 'function') return g.requestAnimationFrame(cb);\n return g.setTimeout(cb, 16) as unknown as number;\n}\n\nfunction cancelFrame(handle: number): void {\n const g: any = globalThis as any;\n if (typeof g.cancelAnimationFrame === 'function') { g.cancelAnimationFrame(handle); return; }\n g.clearTimeout(handle);\n}\n\n/**\n * Platform adapter interface for abstracting platform-specific operations.\n * @public\n */\nexport interface PxPlatformAdapter {\n\n /** Check if the root element is still connected/mounted */\n isConnected(): boolean;\n\n /** Set an attribute on an element by id */\n setAttribute(id: string, attrName: string, value: string): void;\n}\n\n/**\n * Creates an animator instance that uses a frame loop for animations.\n * This is the abstract/platform-agnostic version.\n *\n * @param adapter Platform adapter for DOM/environment operations.\n * @param callbacks Optional lifecycle callbacks.\n * @returns A PxAnimatorApi instance.\n * @public @advanced\n */\nexport function createAdapterAnimator(\n doc: PxAnimatedSvgDocument,\n adapter: PxPlatformAdapter,\n callbacks?: PxEngineCallbacks\n): PxAnimatorApi {\n\n const config = getAnimatorConfig(doc) || {};\n\n const bindings = normalizeBindings(doc, PxTimelineEngine.js);\n\n // iterations: either number or Infinity\n const _iterations = config.iterations;\n let iterations: number = 1;\n if (typeof _iterations === 'number') iterations = _iterations || 1;\n if (_iterations === 'infinite') iterations = Infinity;\n if (iterations < 1) iterations = 1;\n\n const duration = +(config.duration || PX_DEFAULT_DURATION_MS); // per-iteration duration (ms), cannot be 0!\n const totalDuration = duration && iterations ?\n duration * (iterations === Infinity ? Infinity : iterations) :\n (duration ? (iterations ?? 1) * duration : 0);\n\n // direction handling\n const direction = config.direction || 'normal'; // 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'\n\n // fill handling — mirrors the Web Animations API `fill` semantics as closely\n // as a frame-writer can. Default 'forwards' (hold final state) matches the\n // waapi engine and the config doc.\n const fill = config.fill ?? 'forwards';\n const fillsForwards = fill === 'forwards' || fill === 'both';\n const fillsBackwards = fill === 'backwards' || fill === 'both';\n\n ////////////////////////////////////////////////////////////////\n\n let timerId: number | null = null;\n let playing = false;\n\n // Accumulated logical time before the current run (ms). A positive\n // config.delay means \"wait before starting\" → the animation starts at\n // NEGATIVE logical time and reaches 0 after `delay` ms (same convention as\n // the Web Animations API). A negative config.delay means \"seek into the\n // animation\" → positive start time.\n let timeBeforeLastStartMs = -(config.delay || 0);\n let lastStartedTs = 0; // timestamp when last started/resumed\n let playbackRate = 1;\n\n let finishCalled = false; // ensures onFinish called once\n\n // Frame rate throttling\n const frameRate = config.frameRate;\n const minFrameIntervalMs = frameRate && frameRate > 0 ? 1000 / frameRate : 0;\n let lastRenderTs = 0; // timestamp of last render\n\n // Raw logical time — may be negative during the delay phase; not clamped\n // to the [0, totalDuration] range at the top end either. Internal use only.\n const getRawAnimTime = () => {\n // compute effective elapsed time (ms), taking playbackRate into account\n const runningElapsed = lastStartedTs ? (Date.now() - lastStartedTs) * playbackRate : 0;\n return timeBeforeLastStartMs + runningElapsed;\n };\n\n const getAnimCurrentTime = () => {\n let time = getRawAnimTime();\n\n // clamp to [0, totalDuration]\n if (Number.isFinite(totalDuration) && time > (totalDuration as number)) time = totalDuration as number;\n if (time < 0) time = 0;\n return time;\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n // separate render function that uses a given currentTime (ms)\n function renderFrame(currentTimeMs: number) {\n\n function getEffectiveProgress() {\n // If no duration or duration === 0, set iteration/progress accordingly\n const safeDuration = duration > 0 ? duration : 1; // avoid division by zero\n\n let rawProgress = 0;\n let iteration = 0;\n if (duration > 0) {\n\n // rawProgress is normalized progress within the current iteration:\n // - first iteration: [0 .. 1]\n // - following iterations: (0 .. 1]\n //\n // iteration is the zero-based iteration index\n //\n // Examples (safeDuration = 3):\n // currentTimeMs = 0 → rawProgress = 0 , iteration = 0\n // currentTimeMs = 3 → rawProgress = 1 , iteration = 0\n // currentTimeMs = 3.0001 → rawProgress = 0.0001 , iteration = 1\n\n currentTimeMs = clamp(currentTimeMs, 0, iterations * safeDuration);\n\n iteration = Math.max(0, Math.ceil(currentTimeMs / safeDuration) - 1);\n iteration = Math.min(iteration, iterations - 1);\n\n // Time elapsed since the start of the current iteration\n const iterationTime = currentTimeMs - iteration * safeDuration;\n\n // Normalized progress (preserves fractional overflow after boundaries)\n rawProgress = clamp(iterationTime / safeDuration, 0, 1);\n } else {\n rawProgress = currentTimeMs; // We shouldn't be here\n }\n\n\n // compute per-iteration directional progress\n // start with baseProgress = rawProgress in [0,1)\n\n let baseProgress = rawProgress;\n // apply direction rules:\n // - normal: as is\n // - reverse: progress = 1 - baseProgress\n // - alternate: reverse on odd iterations\n // - alternate-reverse: reverse on even iterations\n const dir = direction || 'normal';\n let effectiveProgress = baseProgress;\n if (dir === 'reverse') {\n effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate') {\n if (iteration % 2 === 1) effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate-reverse') {\n // alternate-reverse: start reversed on iteration 0\n if (iteration % 2 === 0) effectiveProgress = 1 - baseProgress;\n } // else 'normal' -> keep\n return effectiveProgress;\n }\n let effectiveProgress = getEffectiveProgress(); // else 'normal' -> keep\n\n ////////////////////////////////////////////////////////////////\n\n // FIXME - pre-calc defs, then use\n\n for (const binding of bindings || []) {\n const animDef = binding.animate;\n if (!animDef || typeof animDef !== 'object' || Array.isArray(animDef)) {\n console.warn('Empty or unresolved binding', binding);\n continue;\n }\n\n // Calculate interpolated values for this binding's animation\n const computedValues = calcAnimationValues(animDef, effectiveProgress * duration);\n\n // Apply each computed attribute\n for (const [attrName, value] of Object.entries(computedValues)) {\n adapter.setAttribute(binding.id, attrName, value);\n }\n }\n }\n\n\n ////////////////////////////////////////////////////////////////\n\n const tick = () => {\n\n // if root element provided and detached, stop\n if (!adapter.isConnected()) {\n // stop animation loop (we'll let external code call play/resume)\n // do not call onFinish here. It's a detach situation.\n pauseAnim();\n return;\n }\n\n const currentTime = getAnimCurrentTime();\n\n // Delay phase (raw time < 0): the animation hasn't started yet.\n // With fill 'backwards'/'both' hold the first frame; otherwise leave\n // the element's static attributes untouched — mirrors WAAPI fill.\n // Checked BEFORE throttling so boundary states can't be skipped.\n const rawTime = getRawAnimTime();\n if (rawTime < 0 && playbackRate > 0) {\n if (fillsBackwards) renderFrame(0);\n return;\n }\n\n // Detect reverse playback reaching the start (natural end for rate < 0)\n // — mirrors WAAPI, where reverse playback fires `finish` at time 0.\n // pauseAnim runs FIRST (its trailing render must not clobber the\n // boundary frame rendered below).\n if (playbackRate < 0 && rawTime <= 0) {\n pauseAnim();\n renderFrame(0);\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Detect finished (natural end, forward playback)\n if (playbackRate > 0 && totalDuration && Number.isFinite(totalDuration) && currentTime >= (totalDuration as number)) {\n pauseAnim();\n // Render the end state — final frame when filling forwards, first\n // frame otherwise (closest frame-writer approximation of WAAPI\n // fill:'none'/'backwards', which reverts to the pre-animation state).\n renderFrame(fillsForwards ? (totalDuration as number) : 0);\n // call onFinish once\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Frame rate throttling: skip render if not enough time has passed.\n // Applies only to normal in-flight frames — boundary states above are\n // always rendered, so a throttled tick can never swallow the finish.\n if (minFrameIntervalMs > 0) {\n const now = Date.now();\n if (lastRenderTs && (now - lastRenderTs) < minFrameIntervalMs) {\n // Not enough time elapsed, skip this frame but continue the loop\n return;\n }\n lastRenderTs = now;\n }\n\n // Otherwise render normal frame\n renderFrame(currentTime);\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n if (config.delay) {\n if (config.delay < 0) {\n // Negative delay = seek into the animation; render the seeked frame.\n renderFrame(getAnimCurrentTime());\n } else if (fillsBackwards) {\n // Positive delay = wait before start; hold the first frame only\n // when filling backwards (WAAPI convention).\n renderFrame(0);\n }\n }\n\n ////////////////////////////////////////////////////////////////\n\n const _isPlaying = () => {\n if (!playing) return false;\n if (!adapter.isConnected()) { return false; }\n if (playbackRate < 0) {\n // Reverse playback finishes when it reaches the start.\n return getRawAnimTime() > 0;\n }\n if (Number.isFinite(totalDuration) && getAnimCurrentTime() >= (totalDuration as number)) return false;\n return true;\n };\n\n const loopAnim = (isFirst?: boolean) => {\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n\n if (!(isFirst || _isPlaying())) {\n // finished or not playing\n return;\n }\n\n timerId = requestFrame(() => {\n timerId = null;\n tick();\n loopAnim();\n });\n };\n\n const startAnim = () => {\n if (playing) return;\n\n // If the animation has already reached its natural end (for the current\n // playback direction), rewind to the opposite end before resuming —\n // mirrors the Web Animations API, where calling play() on a finished\n // animation auto-rewinds. Without this, resuming from the boundary\n // would re-finish on the first tick and leave the animation stuck.\n if (playbackRate >= 0) {\n if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= (totalDuration as number)) {\n timeBeforeLastStartMs = 0;\n }\n } else {\n // Reverse playback starting at (or before) the start: seek to the end.\n if (timeBeforeLastStartMs <= 0) {\n if (!Number.isFinite(totalDuration)) {\n // Cannot rewind to an infinite end (WAAPI throws here) —\n // warn and stay stopped instead of \"finishing\" instantly.\n console.warn('play: cannot start reverse playback of an infinite animation from time 0');\n return;\n }\n timeBeforeLastStartMs = totalDuration as number;\n }\n }\n\n // starting playback opens a new finish episode (mirrors WAAPI, where\n // play() always re-arms the finished promise / finish event)\n finishCalled = false;\n\n playing = true;\n lastStartedTs = Date.now();\n loopAnim(true);\n };\n\n const pauseAnim = () => {\n if (!playing) return;\n // Capture the RAW logical time — during the delay phase it is negative\n // and must stay negative, otherwise pausing would silently swallow the\n // remaining delay. Clamp only the top end.\n let raw = getRawAnimTime();\n if (Number.isFinite(totalDuration) && raw > (totalDuration as number)) raw = totalDuration as number;\n timeBeforeLastStartMs = raw;\n lastStartedTs = 0;\n playing = false;\n\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render a frame at the paused time to keep the DOM consistent —\n // except during the delay phase, where rendering would violate the\n // fill semantics (only 'backwards'/'both' show frame 0 before start).\n if (raw >= 0) {\n renderFrame(raw);\n } else if (fillsBackwards) {\n renderFrame(0);\n }\n };\n\n const cancelAnim = () => {\n pauseAnim();\n\n timeBeforeLastStartMs = 0;\n lastStartedTs = 0;\n playing = false;\n finishCalled = false;\n\n renderFrame(timeBeforeLastStartMs);\n\n callbacks?.onCancel?.();\n };\n\n const finishAnim = (callOnFinish = true) => {\n // jump to the end\n if (Number.isFinite(totalDuration)) {\n timeBeforeLastStartMs = totalDuration as number;\n } else {\n // infinity animations: set to current time (no-op)\n timeBeforeLastStartMs = getAnimCurrentTime();\n }\n lastStartedTs = 0;\n playing = false;\n // cancel any pending frame — pause/destroy early-return when not\n // playing, so a frame left queued here would never be cleaned up\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render the end state honoring `fill` (same rule as the natural\n // finish in `tick()`, and same as WAAPI where fill:'none' reverts even\n // after an explicit finish()).\n renderFrame(fillsForwards ? timeBeforeLastStartMs : 0);\n\n if (callOnFinish && !finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n const api: PxAnimatorApi = {\n\n \"isReady\": () => true,\n\n \"getRootElement\": () => null,\n\n \"isPlaying\": (): boolean => { return _isPlaying(); },\n\n \"play\": () => {\n startAnim();\n callbacks?.onPlay?.();\n },\n \"pause\": () => {\n pauseAnim();\n callbacks?.onPause?.();\n },\n \"cancel\": () => {\n cancelAnim();\n // onCancel already called in cancelAnim\n },\n \"finish\": () => {\n finishAnim(true);\n },\n\n \"setPlaybackRate\": (rate: number) => {\n if (!isValidPlaybackRate(rate)) {\n console.warn(PX_RATE_REJECTED);\n return;\n }\n // Preserve the RAW logical time when changing rate — during the\n // delay phase it is negative and clamping it to 0 would skip the\n // remaining delay. Clamp only the top end.\n let current = getRawAnimTime();\n if (Number.isFinite(totalDuration) && current > (totalDuration as number)) current = totalDuration as number;\n // a direction change opens a new finish episode\n if ((rate < 0) !== (playbackRate < 0)) finishCalled = false;\n playbackRate = rate;\n timeBeforeLastStartMs = current;\n if (playing) lastStartedTs = Date.now();\n },\n\n \"getCurrentTime\": (): number | null => { return getAnimCurrentTime(); },\n\n \"setCurrentTime\": (newTime: number) => {\n // One clamp rule for every engine (review §3).\n newTime = clampSeekMs(newTime, totalDuration as number);\n\n timeBeforeLastStartMs = newTime;\n if (playing) lastStartedTs = Date.now();\n // seeking re-arms the finish notification (mirrors WAAPI)\n if (!Number.isFinite(totalDuration) || newTime < (totalDuration as number)) finishCalled = false;\n // render immediately to reflect the change\n renderFrame(getAnimCurrentTime());\n },\n\n \"getCurrentProgress\": (): number | null => timeToProgress(getAnimCurrentTime(), duration, iterations),\n\n \"setCurrentProgress\": (progress: number) => {\n api.setCurrentTime(progressToTimeMs(progress, duration, iterations));\n },\n\n \"destroy\": () => {\n api.cancel();\n callbacks?.onRemove?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n return api;\n}\n\n\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// EVERY THING A PLAYER CAN SAY, AS A NUMBER.\n//\n// The text of a diagnostic is not shipped. Each code's description lives in the comment on its\n// member and nowhere else, so a build carries the bare integer — `1204` — and not one byte of\n// prose. The descriptions are compiled into docs/diagnostics.md by\n// `node scripts/gen-diagnostics-md.mjs`, which `pnpm build` runs, and every diagnostic links there.\n//\n// A PLAIN `enum`, deliberately, and it costs almost nothing. Measured through this repo's own\n// esbuild, a plain enum and a `const enum` emit byte-identical output (`o(1301),o(1302)…`): in\n// bundle mode every member access is inlined to its number, and the object is then tree-shaken\n// out of any bundle that does not re-export it. A `const enum` would additionally force\n// `isolatedModules: false` on every package that imports it and would land in the published\n// `.d.ts`, where it breaks consumers who have `isolatedModules` on.\n//\n// Core's entry exports it as a VALUE — the players read members at runtime, and a host needs the\n// enum to switch on a code it was handed. What that costs, measured on the shipped build:\n//\n// - core's own ESM/CJS dist carries the object (that is what a host imports);\n// - the web UMD carries NONE of it — the inlined numbers and one link to the page, and not a\n// single member name — because web's entry deliberately does not re-export it. Re-exporting\n// it there would pin the object into the UMD, so don't.\n//\n// RULES FOR EDITING THIS FILE\n//\n// - A NUMBER IS FOREVER. Codes are a public identity: a host switches on them and users search\n// for them. Never renumber, never reuse. To retire one, keep the member and mark it\n// `@deprecated` in its comment — the page then says so too.\n// - The first line of the comment is the description the page shows. Keep it one sentence, in\n// the voice of the person who has to act on it.\n// - `@data` lines name the values the call site passes after the code, in order. They reach a\n// handler as `diagnostic.data` and are printed after the code on the console. Put the\n// specifics there (a selector, a URL, an inner message) — never in prose, which does not exist.\n// - Ranges group by area, so a reader can tell roughly where a code came from without the page.\n\n/**\n * Every diagnostic a player can report, as a number a host can switch on.\n *\n * The words are not in the build: each code's description lives in the comment on its member and\n * is compiled into `docs/diagnostics.md`, which every diagnostic's `message` links to.\n * @public\n */\nexport enum PxDiagnosticCode {\n\n // ── 1000 · building a player ─────────────────────────────────────────────────────────────\n\n /**\n * The player could not be built from this document — a document that passed validation but\n * still broke the builder, or a bug in the player. Report it.\n * @data the Error\n */\n buildFailed = 1001,\n\n /**\n * The file at `src` loaded, but it is not a Pixodesk animation document.\n * @data the `src` URL\n */\n invalidDocumentAtSrc = 1002,\n\n /**\n * The page could not fetch `src`. The file may be perfect — this is the request failing.\n * @data the `src` URL · the fetch error's message\n */\n loadFailed = 1003,\n\n /**\n * One element's animation could not be built, so that element stays static; the rest plays.\n * @data the Error\n */\n animationBuildFailed = 1004,\n\n // ── 1100 · what the document says ────────────────────────────────────────────────────────\n\n /**\n * An `effects` bucket does not match the schema, so that effect is ignored or degraded.\n * @data the problem, with the node's path\n */\n effectsShape = 1101,\n\n /**\n * Part of the per-instance `timeline` override could not be applied — most often clock-only\n * keys aimed at a scroll timeline.\n * @data what could not be applied\n */\n timelineOverrideIgnored = 1102,\n\n /**\n * An SVG tag that can execute or load remote content was dropped from the rendered tree.\n * @data the tag name\n */\n blockedTag = 1103,\n\n /**\n * The browser will not animate these attributes, so the document fell back to the frame loop.\n * @data the attribute names\n */\n unsupportedAnimatedAttrs = 1104,\n\n /** A bind-by-id document carries no `animator.bindings`, so nothing is animated. */\n noBindings = 1105,\n\n /**\n * A binding names no element, or names animations that `definitions.animations` does not have.\n * @data the binding\n */\n unresolvedBinding = 1106,\n\n // ── 1200 · the mount: elements the player could not find ─────────────────────────────────\n\n /** `setupAnimationTriggers` was given no root element, so no trigger was wired. */\n triggersNoRoot = 1201,\n\n /**\n * The container selector matched nothing, so there is nothing to render into.\n * @data the selector\n */\n noRootForSelector = 1202,\n\n /** No container was given and the document's `id` matched no element already on the page. */\n noRootElement = 1203,\n\n /**\n * A binding's selector matched no element, so that binding animates nothing.\n * @data the selector\n */\n noElementsForSelector = 1206,\n\n /**\n * An attribute write found no element for this id — the rendered SVG was probably replaced.\n * @data the id or selector\n */\n setAttributeNoElement = 1207,\n\n // ── 1300 · scroll-driven playback ────────────────────────────────────────────────────────\n\n /** `smoothing` needs the player's own driver, so the browser's scroll timeline was not used. */\n scrollSmoothingNeedsOwnDriver = 1301,\n\n /** The browser refused to build a native scroll timeline; the player measures progress itself. */\n scrollNativeUnavailable = 1302,\n\n /**\n * `scroll.subject` is not a valid CSS selector, so the SVG itself is measured instead.\n * @data the subject\n */\n scrollSubjectInvalid = 1303,\n\n /**\n * `scroll.subject` matched no element, so the SVG itself is measured instead.\n * @data the subject\n */\n scrollSubjectNoMatch = 1304,\n\n /** There is no root element to observe, so a scroll-driven animation stays on its first frame. */\n scrollNoRootToObserve = 1305,\n\n /** `animator.trigger` does not apply to a scroll timeline — the scrollbar is the playhead. */\n scrollTriggerIgnored = 1306,\n\n // ── 1400 · playback control ──────────────────────────────────────────────────────────────\n\n /** A playback rate of `0`, or a non-finite one, is rejected everywhere — use `pause()`. */\n rateRejected = 1401,\n\n // ── 1500 · the props a component was given ───────────────────────────────────────────────\n\n /**\n * Two control tiers were set at once. The higher one wins and the lower is ignored — see the\n * control-mode rule.\n * @data which props conflicted, and which won\n */\n controlPropsConflict = 1501,\n\n // ── 1600 · React Native ──────────────────────────────────────────────────────────────────\n\n /**\n * The document could not be compiled into animation tracks, so `fallback` is shown.\n * @data the Error\n */\n rnCompileFailed = 1601,\n\n /**\n * Rendering the compiled document threw, so `fallback` is shown.\n * @data the Error\n */\n rnRenderFailed = 1602,\n\n /**\n * The error boundary caught a render failure below this component.\n * @data the Error · the React component stack\n */\n rnBoundaryCaught = 1603,\n\n /**\n * `react-native-svg` cannot express part of this document, so it was left out or simplified.\n * @data what was left out\n */\n rnUnsupported = 1604,\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * PER-INSTANCE CONFIG OVERRIDE.\n *\n * One document, played twice on a page with different timing — without editing the document.\n * The host supplies a partial `animator` config; this merges it over the document's own.\n *\n * Base semantics are JSON Merge Patch (RFC 7386): objects merge per level, primitives and\n * arrays REPLACE, and `null` DELETES. `null` is the right sentinel because it is not a legal\n * value anywhere in the schema, and deleting is the only way to restore a meaningful ABSENCE\n * (`timeline.type` absent = time, `mode` absent = auto, `fillMode` absent = forwards).\n *\n * Six places need more than that — see the RULE comments below.\n *\n * WHY THE WIRE FORM, not the flat runtime view: `nestAnimatorTimeline` is a no-op on anything\n * that already carries `timeline`, and the round-trip is lossy (`iterations:'infinite'` on the\n * scroll branch, `pin:false`, and an empty time block emitting no `timeline` at all). Merging\n * on the nested form is the only place those values mean what they say.\n */\nimport { PX_TIMELINE_SHARED_KEYS, PX_TIME_ONLY_TIMELINE_KEYS } from '../format/PxAnimatorConstants';\nimport type { PxTriggerStart } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxAnimatorConfig } from '../format/PxAnimatorTypes';\n\n/** A deep-partial of the WIRE animator config; `null` at any slot deletes it. @public */\nexport type PxAnimatorConfigPatch = Record<string, any> | null;\n\n/** @public */\nexport interface PxAnimatorConfigMergeResult {\n /** The merged WIRE config, or `undefined` when there is nothing left of it. */\n config: PxAnimatorConfig | undefined;\n /** Human-readable problems, `path: what is wrong`. Empty when the merge was clean. */\n warnings: Array<string>;\n}\n\n/** Keys that live only on the flat RUNTIME view and have no slot on the wire. */\nconst FLAT_ONLY_KEYS = [\n 'timelineSource', 'scroll', 'trigger', 'delay', 'iterations',\n 'direction', 'fill', 'resetOnFinish', 'duration', 'mode', 'frameRate',\n];\n\n/** The lookup tables and the bindings. They are animation CONTENT, not playback, and are never reset. */\nconst CONTENT_KEYS = ['definitions', 'bindings'];\n\nconst isPlainObject = (v: unknown): v is Record<string, any> =>\n !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Absent `type` means the time-driven timeline, so compare the normalized values (RULE 2). */\nconst timelineTypeOf = (t: unknown): string =>\n (isPlainObject(t) && typeof t.type === 'string') ? t.type : 'time';\n\nconst isScrollish = (type: string): boolean => type === 'scroll' || type === 'view';\n\n/**\n * RFC 7386 merge with no special cases. Used for every sub-object that has no rule of its own\n * (`trigger`, `range`, `definitions.fonts`, …), which is why patching one font leaves its\n * siblings alone. Arrays are values: a patched `bindings` list replaces the document's whole list.\n */\nfunction mergePlain(base: unknown, patch: Record<string, any>): Record<string, any> {\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; } // RULE 7: null deletes\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value; // RULE 6: arrays/primitives replace\n }\n return out;\n}\n\n/**\n * The timeline, which is a discriminated union and therefore cannot be merged blindly.\n *\n * RULE 1 — the patch names a DIFFERENT type: replace the timeline outright, carrying over only\n * the keys both members share. Merging instead would strand `trigger`/`delay` on a\n * scroll timeline, where the format has no slot for them.\n * RULE 2 — same type, or the patch does not mention one: merge. A patch that spells\n * `type: 'time'` against a document with an ABSENT type must count as a match, hence\n * the normalization above.\n * RULE 3 — time-only keys landing on a scroll/view timeline are dropped with a warning, the\n * same way the wire has no slot for them.\n * RULE 4 — `iterations: 'infinite'` cannot map onto a scroll range.\n */\nfunction mergeTimeline(base: unknown, patch: Record<string, any>, warn: (m: string) => void): Record<string, any> {\n const baseType = timelineTypeOf(base);\n const patchNamesType = isPlainObject(patch) && typeof patch.type === 'string';\n const patchType = patchNamesType ? String(patch.type) : baseType;\n\n let merged: Record<string, any>;\n if (patchType !== baseType) {\n // RULE 1\n const carried: Record<string, any> = {};\n if (isPlainObject(base)) {\n for (const k of PX_TIMELINE_SHARED_KEYS) {\n if (base[k] !== undefined) carried[k] = base[k];\n }\n }\n merged = mergePlain(carried, patch);\n } else {\n merged = mergePlain(base, patch); // RULE 2\n }\n\n if (isScrollish(patchType)) {\n for (const k of PX_TIME_ONLY_TIMELINE_KEYS) { // RULE 3\n if (merged[k] === undefined) continue;\n warn('animator.timeline.' + k + \": no slot on a '\" + patchType + \"' timeline — dropped\");\n delete merged[k];\n }\n if (merged.iterations === 'infinite') { // RULE 4\n warn(\"animator.timeline.iterations: 'infinite' cannot map onto a scroll range — dropped\");\n delete merged.iterations;\n }\n }\n return merged;\n}\n\n/** A deep-partial of the WIRE `timeline` block; `null` at any slot deletes it. @public */\nexport type PxTimelinePatch = Record<string, any> | null;\n\n/** The four flat shortcuts every surface offers for the keys people reach for most. @public */\nexport interface PxTimelineShortcuts {\n /** Shortcut for `timeline.duration` — one iteration, ms. Wins over the same key in `timeline`. */\n duration?: number;\n /** Shortcut for `timeline.delay` — the wait before the first iteration, ms. */\n delay?: number;\n /** Shortcut for `timeline.iterations`; `'infinite'` never stops. */\n iterations?: number | 'infinite';\n /**\n * Shortcut for `timeline.trigger.start`. Typed from the WIRE, so it includes\n * `'none'` — \"nothing starts this but a `play()` call\".\n */\n start?: PxTriggerStart;\n}\n\n/**\n * The playback-override props every surface takes — `createAnimator` and the three components:\n * the document's `timeline` as a patch, the reset flag, and the four shortcuts above.\n *\n * ONE definition (review §9): React and React Native extend it, Vue derives its internal shape\n * from it, `createAnimator`'s options extend it.\n * @public\n */\nexport interface PxPlaybackOverride extends PxTimelineShortcuts {\n /**\n * Per-instance override of the document's `animator.timeline` — the same shape as `timeline`\n * in docs/format/README.md, deep-merged over what the document says, so one file can play twice on a page\n * with different timing. `null` at any slot DELETES that key, restoring the default its\n * absence means. Also accepts a JSON STRING, which survives a build that mangles object keys.\n *\n * `timeline` is the whole useful override surface: the rest of the `animator` block is\n * content (`definitions`, `bindings`), a version stamp and a debug handle — none of which\n * a per-instance override should touch. That is why this is not a wrapper object.\n */\n timeline?: PxTimelinePatch | string;\n /**\n * Ignore the document's own timeline and start from the player's DEFAULT timeline, with\n * `timeline` on top. `definitions` and `bindings` are content and are kept either way.\n */\n resetTimeline?: boolean;\n}\n\n/**\n * Folds the four shortcuts into the `timeline` patch, accepts the JSON-STRING form of the patch\n * (immune to property mangling — see docs/library/minification.md), and returns it in the shape\n * `applyAnimatorConfig` takes: an animator-level patch `{ timeline: … }`.\n *\n * A shortcut WINS over the same key inside the object: more specific beats more general, the\n * way an inline style beats a stylesheet. One implementation so every surface agrees.\n * @public\n */\nexport function foldTimelineOverride(\n timeline: PxTimelinePatch | string | undefined,\n shortcuts: PxTimelineShortcuts,\n): PxAnimatorConfigPatch | undefined {\n let base: PxTimelinePatch | undefined;\n if (typeof timeline === 'string') {\n try {\n base = JSON.parse(timeline);\n } catch (e) {\n console.warn('timeline override: not valid JSON — ignored', e);\n base = undefined;\n }\n } else {\n base = timeline ?? undefined;\n }\n\n const { duration, delay, iterations, start } = shortcuts;\n if (duration === undefined && delay === undefined && iterations === undefined && start === undefined) {\n return base === undefined ? undefined : { timeline: base };\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n if (duration !== undefined) out.duration = duration;\n if (delay !== undefined) out.delay = delay;\n if (iterations !== undefined) out.iterations = iterations;\n if (start !== undefined) {\n out.trigger = { ...(out.trigger as Record<string, any> | undefined), start };\n }\n return { timeline: out };\n}\n\n/**\n * Merge a patch over an animator config. PURE — neither argument is mutated, and the result is\n * always a NEW object, which also matters because `flattenAnimatorTimeline` memoises on config\n * identity: mutating in place would hand every later reader the pre-merge view.\n * @public\n */\nexport function mergeAnimatorConfig(\n base: PxAnimatorConfig | undefined,\n patch: PxAnimatorConfigPatch,\n): PxAnimatorConfigMergeResult {\n const warnings: Array<string> = [];\n const warn = (m: string) => warnings.push(m);\n\n if (patch === null) return { config: undefined, warnings };\n if (!isPlainObject(patch) || Object.keys(patch).length === 0) {\n return { config: base, warnings }; // nothing to do: same identity\n }\n\n // RULE 5 — a base in the flat runtime spelling cannot take a wire patch soundly:\n // `flattenAnimatorTimeline` copies the flat keys first and then overwrites them from\n // `timeline`, so a flat `delay` would survive a `timeline.delay: null` deletion.\n const flatInBase = isPlainObject(base) ? FLAT_ONLY_KEYS.filter(k => (base as any)[k] !== undefined) : [];\n if (flatInBase.length) {\n warn('animator: the base carries the flat runtime spelling (' + flatInBase.join(', ') + '); '\n + 'the patch merges the wire spelling only');\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; }\n if (key === 'timeline') {\n out.timeline = isPlainObject(value) ? mergeTimeline(out.timeline, value, warn) : value;\n continue;\n }\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value;\n }\n return { config: out as PxAnimatorConfig, warnings };\n}\n\n/**\n * Document level: resolves the two canonical addresses of the animator config and returns a NEW\n * document that shares every untouched subtree by reference.\n *\n * `resetTimeline` starts from the player's own defaults instead of the document's playback\n * settings — \"play this file as if it said nothing about timing\". The lookup tables are kept\n * either way: resetting `definitions`/`bindings` would leave an animation with nothing to\n * animate, which is never what a caller means.\n * @public\n */\nexport function applyAnimatorConfig(\n doc: PxAnimatedSvgDocument,\n patch: PxAnimatorConfigPatch,\n options?: { resetTimeline?: boolean },\n): { doc: PxAnimatedSvgDocument; warnings: Array<string> } {\n const reset = !!options?.resetTimeline;\n if (!doc || (patch === undefined || (!reset && (patch === null || !isPlainObject(patch) || !Object.keys(patch).length)))) {\n return { doc, warnings: [] };\n }\n\n const anyDoc = doc as any;\n const atRoot = isPlainObject(anyDoc.animator);\n const atMeta = !atRoot && isPlainObject(anyDoc.meta?.animator);\n const current: PxAnimatorConfig | undefined = atRoot ? anyDoc.animator\n : atMeta ? anyDoc.meta.animator\n : undefined;\n\n const warnings: Array<string> = [];\n if (atRoot && isPlainObject(anyDoc.meta?.animator)) {\n warnings.push('animator: doc.meta.animator is shadowed by doc.animator and was not patched');\n }\n\n let base = current;\n if (reset) {\n // Keep only the content tables; everything else starts from the player's defaults.\n const kept: Record<string, any> = {};\n for (const k of CONTENT_KEYS) {\n if (isPlainObject(current) && (current as any)[k] !== undefined) kept[k] = (current as any)[k];\n }\n base = kept as PxAnimatorConfig;\n }\n\n const merged = mergeAnimatorConfig(base, patch ?? {});\n warnings.push(...merged.warnings);\n if (merged.config === current) return { doc, warnings };\n\n if (atMeta) {\n return {\n doc: { ...anyDoc, meta: { ...anyDoc.meta, animator: merged.config } } as PxAnimatedSvgDocument,\n warnings,\n };\n }\n return { doc: { ...anyDoc, animator: merged.config } as PxAnimatedSvgDocument, warnings };\n}\n"],"mappings":"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","names":["kfs","out","clone","clone","v","genId","stripHash","clone","stripHash","effectiveProgress","_a","PxDiagnosticCode"]}
|
|
1
|
+
{"version":3,"sources":["../src/playback/PxPlaybackTime.ts","../src/playback/PxFrameLoop.ts","../src/playback/PxAnimatorConfigPatch.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ONE time contract for every engine (API review §3). The same three calls used to mean three\n// different things:\n//\n// | engine | getCurrentTime() | setCurrentTime(t) | setPlaybackRate(0) |\n// |-----------------|-------------------------|-------------------|--------------------|\n// | web, WAAPI | ms across the whole run | NOT clamped | accepted |\n// | web, frame loop | ms across the whole run | clamped | rejected + warn |\n// | React Native | ms within ONE iteration | wrapped | rejected + warn |\n//\n// So a slider built on `getCurrentTime()` jumped back every iteration on React Native, and\n// `timeline.engine: 'auto'` meant two documents on one page could answer the same call\n// differently. The contract every player now implements:\n//\n// - time is ms from the start of the WHOLE run, iterations included — never per-iteration;\n// - a seek clamps to [0, seekCeilingMs];\n// - a rate of 0 is rejected everywhere, with this one message.\n\n/** The one message every engine prints for a rejected rate. @public @advanced */\nexport const PX_RATE_REJECTED = 'setPlaybackRate: rate must be finite and non-zero';\n\n/**\n * A playback rate is usable when it is finite and non-zero.\n *\n * 0 is rejected rather than accepted: it freezes the animation in a state indistinguishable\n * from a stuck player, and `pause()` already says that properly. Two of the three engines\n * rejected it already — this makes the third agree.\n * @public @advanced\n */\nexport function isValidPlaybackRate(rate: number): boolean {\n return Number.isFinite(rate) && rate !== 0;\n}\n\n/**\n * Highest seekable time, ms — `duration × iterations`.\n *\n * `Infinity` for an endless timeline, so callers must test `Number.isFinite` before using it\n * as an upper bound. This is the SEEK ceiling, which is deliberately not the same thing as the\n * span `progress` covers — see `progressSpanMs`.\n * @public @advanced\n */\nexport function seekCeilingMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return Infinity;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * The span `progress` 0→1 covers, ms.\n *\n * An endless timeline maps progress onto ONE iteration — the rule the components already\n * document for the `progress` prop (\"0–1 of duration × iterations, one iteration when\n * iterations is 'infinite'\"). Always finite, so it is safe as a divisor.\n * @public @advanced\n */\nexport function progressSpanMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return durationMs;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * Clamps a seek into [0, ceiling].\n *\n * `NaN` and anything below 0 land on 0. `Infinity` means \"the end\", so it clamps to the ceiling\n * like any other overshoot — except on an endless timeline, where there is no end to land on and\n * a non-finite playhead would poison every later read, so that reads as 0.\n * @public @advanced\n */\nexport function clampSeekMs(timeMs: number, ceilingMs: number): number {\n if (Number.isNaN(timeMs) || timeMs < 0) return 0;\n if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;\n return Number.isFinite(timeMs) ? timeMs : 0;\n}\n\n/**\n * Whole-run ms → 0–1.\n *\n * A finite timeline clamps at both ends. An endless one wraps within the current iteration,\n * so the value stays meaningful however long it has been running. A zero-length span reads as\n * 0 rather than NaN.\n * @public @advanced\n */\nexport function timeToProgress(timeMs: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(timeMs)) return 0;\n if (timeMs <= 0) return 0;\n if (iterations === Infinity) return (timeMs % span) / span;\n return timeMs >= span ? 1 : timeMs / span;\n}\n\n/** 0–1 → whole-run ms, clamped into the span. A non-finite progress reads as 0. @public @advanced */\nexport function progressToTimeMs(progress: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(progress)) return 0;\n if (progress <= 0) return 0;\n return progress >= 1 ? span : progress * span;\n}\n\n/** A playhead that keeps whole-run time across iterations. See `createRunClock`. @public @advanced */\nexport interface PxRunClock {\n /** Whole-run position right now, ms, clamped to the ceiling. */\n now(): number;\n /** True while time is advancing. */\n isRunning(): boolean;\n /** Start or resume at `rate`, optionally from a given whole-run time. */\n start(rate: number, atMs?: number): void;\n /** Stop advancing, freezing the current position. */\n stop(): void;\n /** Jump to a whole-run time; keeps running if it already was. */\n seek(ms: number): void;\n}\n\n/**\n * A whole-run playhead that survives repetition.\n *\n * React Native drives playback with Reanimated's `withRepeat`, whose shared value only ever\n * holds the position WITHIN one iteration and which never reports how many iterations have\n * elapsed. Whole-run time therefore cannot be recovered from it: under `alternate` the value\n * runs backwards rather than wrapping, which is indistinguishable from a negative rate. So the\n * time is kept on a clock of its own — the same thing the frame-loop engine does inline.\n *\n * `nowFn` is injectable so this is testable without real time passing.\n * @public @advanced\n */\nexport function createRunClock(ceilingMs: number, nowFn: () => number = Date.now): PxRunClock {\n let baseMs = 0; // whole-run ms as of the last start/seek/stop\n let startedAt = 0; // nowFn() when running began\n let running = false; // a FLAG, not `startedAt !== 0`: a time source may legitimately\n let rate = 1; // read 0, and `Date.now()` never does — so the bug would hide.\n\n const value = (): number => running\n ? clampSeekMs(baseMs + (nowFn() - startedAt) * rate, ceilingMs)\n : clampSeekMs(baseMs, ceilingMs);\n\n return {\n now: value,\n isRunning: () => running,\n start: (r: number, atMs?: number) => {\n baseMs = clampSeekMs(atMs ?? value(), ceilingMs);\n rate = isValidPlaybackRate(r) ? r : 1;\n startedAt = nowFn();\n running = true;\n },\n stop: () => {\n baseMs = value();\n startedAt = 0;\n running = false;\n },\n seek: (ms: number) => {\n baseMs = clampSeekMs(ms, ceilingMs);\n if (running) startedAt = nowFn();\n },\n };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { type PxAnimatedSvgDocument, type PxAnimatorApi, type PxEngineCallbacks } from '../format/PxAnimatorTypes';\nimport { getAnimatorConfig, PX_DEFAULT_ITERATIONS, PxTimelineEngine } from '../format/PxAnimatorConstants';\nimport { PxTimeTimelineSchema } from '../format/PxAnimatorTypes';\nimport { camelCaseToKebabWordIfNeeded, clamp, PX_DEFAULT_DURATION_MS, PX_STYLE_ATTR_NAMES } from '../util/PxAnimatorUtil';\nimport { calcAnimationValues, normalizeBindings } from '../animation/PxDefinitions';\nimport { clampSeekMs, isValidPlaybackRate, progressToTimeMs, PX_RATE_REJECTED, timeToProgress } from './PxPlaybackTime';\n\n\n// Frame scheduling — resolved LAZILY from globalThis on every call so test\n// harnesses that install fake timers (and hosts that polyfill rAF late) are\n// honored. Falls back to a ~60fps setTimeout when rAF is unavailable.\nfunction requestFrame(cb: () => void): number {\n const g: any = globalThis as any;\n if (typeof g.requestAnimationFrame === 'function') return g.requestAnimationFrame(cb);\n return g.setTimeout(cb, 16) as unknown as number;\n}\n\nfunction cancelFrame(handle: number): void {\n const g: any = globalThis as any;\n if (typeof g.cancelAnimationFrame === 'function') { g.cancelAnimationFrame(handle); return; }\n g.clearTimeout(handle);\n}\n\n/**\n * Platform adapter interface for abstracting platform-specific operations.\n * @public\n */\nexport interface PxPlatformAdapter {\n\n /** Check if the root element is still connected/mounted */\n isConnected(): boolean;\n\n /** Set an attribute on an element by id */\n setAttribute(id: string, attrName: string, value: string): void;\n}\n\n/**\n * Creates an animator instance that uses a frame loop for animations.\n * This is the abstract/platform-agnostic version.\n *\n * @param adapter Platform adapter for DOM/environment operations.\n * @param callbacks Optional lifecycle callbacks.\n * @returns A PxAnimatorApi instance.\n * @public @advanced\n */\nexport function createAdapterAnimator(\n doc: PxAnimatedSvgDocument,\n adapter: PxPlatformAdapter,\n callbacks?: PxEngineCallbacks\n): PxAnimatorApi {\n\n const config = getAnimatorConfig(doc) || {};\n\n const bindings = normalizeBindings(doc, PxTimelineEngine.js);\n\n // iterations: either number or Infinity\n const _iterations = config.iterations;\n let iterations: number = 1;\n if (typeof _iterations === 'number') iterations = _iterations || PX_DEFAULT_ITERATIONS;\n if (_iterations === 'infinite') iterations = Infinity;\n if (iterations < 1) iterations = 1;\n\n const duration = +(config.duration || PX_DEFAULT_DURATION_MS); // per-iteration duration (ms), cannot be 0!\n const totalDuration = duration && iterations ?\n duration * (iterations === Infinity ? Infinity : iterations) :\n (duration ? (iterations ?? PX_DEFAULT_ITERATIONS) * duration : 0);\n\n // direction handling\n // What an omitted field means is the SCHEMA's statement — read, not restated here.\n const direction = config.direction || PxTimeTimelineSchema.defaults.direction; // 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'\n\n // fill handling — mirrors the Web Animations API `fill` semantics as closely\n // as a frame-writer can. Default 'forwards' (hold final state) matches the\n // waapi engine and the config doc.\n const fill = config.fill ?? PxTimeTimelineSchema.defaults.fillMode;\n const fillsForwards = fill === 'forwards' || fill === 'both';\n const fillsBackwards = fill === 'backwards' || fill === 'both';\n\n ////////////////////////////////////////////////////////////////\n\n let timerId: number | null = null;\n let playing = false;\n\n // Accumulated logical time before the current run (ms). A positive\n // config.delay means \"wait before starting\" → the animation starts at\n // NEGATIVE logical time and reaches 0 after `delay` ms (same convention as\n // the Web Animations API). A negative config.delay means \"seek into the\n // animation\" → positive start time.\n let timeBeforeLastStartMs = -(config.delay || 0);\n let lastStartedTs = 0; // timestamp when last started/resumed\n let playbackRate = 1;\n\n let finishCalled = false; // ensures onFinish called once\n\n // Frame rate throttling\n const frameRate = config.frameRate;\n const minFrameIntervalMs = frameRate && frameRate > 0 ? 1000 / frameRate : 0;\n let lastRenderTs = 0; // timestamp of last render\n\n // Raw logical time — may be negative during the delay phase; not clamped\n // to the [0, totalDuration] range at the top end either. Internal use only.\n const getRawAnimTime = () => {\n // compute effective elapsed time (ms), taking playbackRate into account\n const runningElapsed = lastStartedTs ? (Date.now() - lastStartedTs) * playbackRate : 0;\n return timeBeforeLastStartMs + runningElapsed;\n };\n\n const getAnimCurrentTime = () => {\n let time = getRawAnimTime();\n\n // clamp to [0, totalDuration]\n if (Number.isFinite(totalDuration) && time > (totalDuration as number)) time = totalDuration as number;\n if (time < 0) time = 0;\n return time;\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n // separate render function that uses a given currentTime (ms)\n function renderFrame(currentTimeMs: number) {\n\n function getEffectiveProgress() {\n // If no duration or duration === 0, set iteration/progress accordingly\n const safeDuration = duration > 0 ? duration : 1; // avoid division by zero\n\n let rawProgress = 0;\n let iteration = 0;\n if (duration > 0) {\n\n // rawProgress is normalized progress within the current iteration:\n // - first iteration: [0 .. 1]\n // - following iterations: (0 .. 1]\n //\n // iteration is the zero-based iteration index\n //\n // Examples (safeDuration = 3):\n // currentTimeMs = 0 → rawProgress = 0 , iteration = 0\n // currentTimeMs = 3 → rawProgress = 1 , iteration = 0\n // currentTimeMs = 3.0001 → rawProgress = 0.0001 , iteration = 1\n\n currentTimeMs = clamp(currentTimeMs, 0, iterations * safeDuration);\n\n iteration = Math.max(0, Math.ceil(currentTimeMs / safeDuration) - 1);\n iteration = Math.min(iteration, iterations - 1);\n\n // Time elapsed since the start of the current iteration\n const iterationTime = currentTimeMs - iteration * safeDuration;\n\n // Normalized progress (preserves fractional overflow after boundaries)\n rawProgress = clamp(iterationTime / safeDuration, 0, 1);\n } else {\n rawProgress = currentTimeMs; // We shouldn't be here\n }\n\n\n // compute per-iteration directional progress\n // start with baseProgress = rawProgress in [0,1)\n\n let baseProgress = rawProgress;\n // apply direction rules:\n // - normal: as is\n // - reverse: progress = 1 - baseProgress\n // - alternate: reverse on odd iterations\n // - alternate-reverse: reverse on even iterations\n const dir = direction || 'normal';\n let effectiveProgress = baseProgress;\n if (dir === 'reverse') {\n effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate') {\n if (iteration % 2 === 1) effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate-reverse') {\n // alternate-reverse: start reversed on iteration 0\n if (iteration % 2 === 0) effectiveProgress = 1 - baseProgress;\n } // else 'normal' -> keep\n return effectiveProgress;\n }\n let effectiveProgress = getEffectiveProgress(); // else 'normal' -> keep\n\n ////////////////////////////////////////////////////////////////\n\n // FIXME - pre-calc defs, then use\n\n for (const binding of bindings || []) {\n const animDef = binding.animate;\n if (!animDef || typeof animDef !== 'object' || Array.isArray(animDef)) {\n console.warn('Empty or unresolved binding', binding);\n continue;\n }\n\n // Calculate interpolated values for this binding's animation\n const computedValues = calcAnimationValues(animDef, effectiveProgress * duration);\n\n // Apply each computed attribute\n for (const [attrName, value] of Object.entries(computedValues)) {\n adapter.setAttribute(binding.id, attrName, value);\n }\n }\n }\n\n\n ////////////////////////////////////////////////////////////////\n\n const tick = () => {\n\n // if root element provided and detached, stop\n if (!adapter.isConnected()) {\n // stop animation loop (we'll let external code call play/resume)\n // do not call onFinish here. It's a detach situation.\n pauseAnim();\n return;\n }\n\n const currentTime = getAnimCurrentTime();\n\n // Delay phase (raw time < 0): the animation hasn't started yet.\n // With fill 'backwards'/'both' hold the first frame; otherwise leave\n // the element's static attributes untouched — mirrors WAAPI fill.\n // Checked BEFORE throttling so boundary states can't be skipped.\n const rawTime = getRawAnimTime();\n if (rawTime < 0 && playbackRate > 0) {\n if (fillsBackwards) renderFrame(0);\n return;\n }\n\n // Detect reverse playback reaching the start (natural end for rate < 0)\n // — mirrors WAAPI, where reverse playback fires `finish` at time 0.\n // pauseAnim runs FIRST (its trailing render must not clobber the\n // boundary frame rendered below).\n if (playbackRate < 0 && rawTime <= 0) {\n pauseAnim();\n renderFrame(0);\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Detect finished (natural end, forward playback)\n if (playbackRate > 0 && totalDuration && Number.isFinite(totalDuration) && currentTime >= (totalDuration as number)) {\n pauseAnim();\n // Render the end state — final frame when filling forwards, first\n // frame otherwise (closest frame-writer approximation of WAAPI\n // fill:'none'/'backwards', which reverts to the pre-animation state).\n renderFrame(fillsForwards ? (totalDuration as number) : 0);\n // call onFinish once\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Frame rate throttling: skip render if not enough time has passed.\n // Applies only to normal in-flight frames — boundary states above are\n // always rendered, so a throttled tick can never swallow the finish.\n if (minFrameIntervalMs > 0) {\n const now = Date.now();\n if (lastRenderTs && (now - lastRenderTs) < minFrameIntervalMs) {\n // Not enough time elapsed, skip this frame but continue the loop\n return;\n }\n lastRenderTs = now;\n }\n\n // Otherwise render normal frame\n renderFrame(currentTime);\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n if (config.delay) {\n if (config.delay < 0) {\n // Negative delay = seek into the animation; render the seeked frame.\n renderFrame(getAnimCurrentTime());\n } else if (fillsBackwards) {\n // Positive delay = wait before start; hold the first frame only\n // when filling backwards (WAAPI convention).\n renderFrame(0);\n }\n }\n\n ////////////////////////////////////////////////////////////////\n\n const _isPlaying = () => {\n if (!playing) return false;\n if (!adapter.isConnected()) { return false; }\n if (playbackRate < 0) {\n // Reverse playback finishes when it reaches the start.\n return getRawAnimTime() > 0;\n }\n if (Number.isFinite(totalDuration) && getAnimCurrentTime() >= (totalDuration as number)) return false;\n return true;\n };\n\n const loopAnim = (isFirst?: boolean) => {\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n\n if (!(isFirst || _isPlaying())) {\n // finished or not playing\n return;\n }\n\n timerId = requestFrame(() => {\n timerId = null;\n tick();\n loopAnim();\n });\n };\n\n const startAnim = () => {\n if (playing) return;\n\n // If the animation has already reached its natural end (for the current\n // playback direction), rewind to the opposite end before resuming —\n // mirrors the Web Animations API, where calling play() on a finished\n // animation auto-rewinds. Without this, resuming from the boundary\n // would re-finish on the first tick and leave the animation stuck.\n if (playbackRate >= 0) {\n if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= (totalDuration as number)) {\n timeBeforeLastStartMs = 0;\n }\n } else {\n // Reverse playback starting at (or before) the start: seek to the end.\n if (timeBeforeLastStartMs <= 0) {\n if (!Number.isFinite(totalDuration)) {\n // Cannot rewind to an infinite end (WAAPI throws here) —\n // warn and stay stopped instead of \"finishing\" instantly.\n console.warn('play: cannot start reverse playback of an infinite animation from time 0');\n return;\n }\n timeBeforeLastStartMs = totalDuration as number;\n }\n }\n\n // starting playback opens a new finish episode (mirrors WAAPI, where\n // play() always re-arms the finished promise / finish event)\n finishCalled = false;\n\n playing = true;\n lastStartedTs = Date.now();\n loopAnim(true);\n };\n\n const pauseAnim = () => {\n if (!playing) return;\n // Capture the RAW logical time — during the delay phase it is negative\n // and must stay negative, otherwise pausing would silently swallow the\n // remaining delay. Clamp only the top end.\n let raw = getRawAnimTime();\n if (Number.isFinite(totalDuration) && raw > (totalDuration as number)) raw = totalDuration as number;\n timeBeforeLastStartMs = raw;\n lastStartedTs = 0;\n playing = false;\n\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render a frame at the paused time to keep the DOM consistent —\n // except during the delay phase, where rendering would violate the\n // fill semantics (only 'backwards'/'both' show frame 0 before start).\n if (raw >= 0) {\n renderFrame(raw);\n } else if (fillsBackwards) {\n renderFrame(0);\n }\n };\n\n const cancelAnim = () => {\n pauseAnim();\n\n timeBeforeLastStartMs = 0;\n lastStartedTs = 0;\n playing = false;\n finishCalled = false;\n\n renderFrame(timeBeforeLastStartMs);\n\n callbacks?.onCancel?.();\n };\n\n const finishAnim = (callOnFinish = true) => {\n // jump to the end\n if (Number.isFinite(totalDuration)) {\n timeBeforeLastStartMs = totalDuration as number;\n } else {\n // infinity animations: set to current time (no-op)\n timeBeforeLastStartMs = getAnimCurrentTime();\n }\n lastStartedTs = 0;\n playing = false;\n // cancel any pending frame — pause/destroy early-return when not\n // playing, so a frame left queued here would never be cleaned up\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render the end state honoring `fill` (same rule as the natural\n // finish in `tick()`, and same as WAAPI where fill:'none' reverts even\n // after an explicit finish()).\n renderFrame(fillsForwards ? timeBeforeLastStartMs : 0);\n\n if (callOnFinish && !finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n const api: PxAnimatorApi = {\n\n \"isReady\": () => true,\n\n \"getRootElement\": () => null,\n\n \"isPlaying\": (): boolean => { return _isPlaying(); },\n\n \"play\": () => {\n startAnim();\n callbacks?.onPlay?.();\n },\n \"pause\": () => {\n pauseAnim();\n callbacks?.onPause?.();\n },\n \"cancel\": () => {\n cancelAnim();\n // onCancel already called in cancelAnim\n },\n \"finish\": () => {\n finishAnim(true);\n },\n\n \"setPlaybackRate\": (rate: number) => {\n if (!isValidPlaybackRate(rate)) {\n console.warn(PX_RATE_REJECTED);\n return;\n }\n // Preserve the RAW logical time when changing rate — during the\n // delay phase it is negative and clamping it to 0 would skip the\n // remaining delay. Clamp only the top end.\n let current = getRawAnimTime();\n if (Number.isFinite(totalDuration) && current > (totalDuration as number)) current = totalDuration as number;\n // a direction change opens a new finish episode\n if ((rate < 0) !== (playbackRate < 0)) finishCalled = false;\n playbackRate = rate;\n timeBeforeLastStartMs = current;\n if (playing) lastStartedTs = Date.now();\n },\n\n \"getCurrentTime\": (): number | null => { return getAnimCurrentTime(); },\n\n \"setCurrentTime\": (newTime: number) => {\n // One clamp rule for every engine (review §3).\n newTime = clampSeekMs(newTime, totalDuration as number);\n\n timeBeforeLastStartMs = newTime;\n if (playing) lastStartedTs = Date.now();\n // seeking re-arms the finish notification (mirrors WAAPI)\n if (!Number.isFinite(totalDuration) || newTime < (totalDuration as number)) finishCalled = false;\n // render immediately to reflect the change\n renderFrame(getAnimCurrentTime());\n },\n\n \"getCurrentProgress\": (): number | null => timeToProgress(getAnimCurrentTime(), duration, iterations),\n\n \"setCurrentProgress\": (progress: number) => {\n api.setCurrentTime(progressToTimeMs(progress, duration, iterations));\n },\n\n \"destroy\": () => {\n api.cancel();\n callbacks?.onRemove?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n return api;\n}\n\n\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * PER-INSTANCE CONFIG OVERRIDE.\n *\n * One document, played twice on a page with different timing — without editing the document.\n * The host supplies a partial `animator` config; this merges it over the document's own.\n *\n * Base semantics are JSON Merge Patch (RFC 7386): objects merge per level, primitives and\n * arrays REPLACE, and `null` DELETES. `null` is the right sentinel because it is not a legal\n * value anywhere in the schema, and deleting is the only way to restore a meaningful ABSENCE\n * (`timeline.type` absent = time, `mode` absent = auto, `fillMode` absent = forwards).\n *\n * Six places need more than that — see the RULE comments below.\n *\n * WHY THE WIRE FORM, not the flat runtime view: `nestAnimatorTimeline` is a no-op on anything\n * that already carries `timeline`, and the round-trip is lossy (`iterations:'infinite'` on the\n * scroll branch, `pin:false`, and an empty time block emitting no `timeline` at all). Merging\n * on the nested form is the only place those values mean what they say.\n */\nimport { PX_TIMELINE_SHARED_KEYS, PX_TIME_ONLY_TIMELINE_KEYS } from '../format/PxAnimatorConstants';\nimport type { PxTriggerStart } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxAnimatorConfig } from '../format/PxAnimatorTypes';\n\n/** A deep-partial of the WIRE animator config; `null` at any slot deletes it. @public */\nexport type PxAnimatorConfigPatch = Record<string, any> | null;\n\n/** @public */\nexport interface PxAnimatorConfigMergeResult {\n /** The merged WIRE config, or `undefined` when there is nothing left of it. */\n config: PxAnimatorConfig | undefined;\n /** Human-readable problems, `path: what is wrong`. Empty when the merge was clean. */\n warnings: Array<string>;\n}\n\n/** Keys that live only on the flat RUNTIME view and have no slot on the wire. */\nconst FLAT_ONLY_KEYS = [\n 'timelineSource', 'scroll', 'trigger', 'delay', 'iterations',\n 'direction', 'fill', 'resetOnFinish', 'duration', 'mode', 'frameRate',\n];\n\n/** The lookup tables and the bindings. They are animation CONTENT, not playback, and are never reset. */\nconst CONTENT_KEYS = ['definitions', 'bindings'];\n\nconst isPlainObject = (v: unknown): v is Record<string, any> =>\n !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Absent `type` means the time-driven timeline, so compare the normalized values (RULE 2). */\nconst timelineTypeOf = (t: unknown): string =>\n (isPlainObject(t) && typeof t.type === 'string') ? t.type : 'time';\n\nconst isScrollish = (type: string): boolean => type === 'scroll' || type === 'view';\n\n/**\n * RFC 7386 merge with no special cases. Used for every sub-object that has no rule of its own\n * (`trigger`, `range`, `definitions.fonts`, …), which is why patching one font leaves its\n * siblings alone. Arrays are values: a patched `bindings` list replaces the document's whole list.\n */\nfunction mergePlain(base: unknown, patch: Record<string, any>): Record<string, any> {\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; } // RULE 7: null deletes\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value; // RULE 6: arrays/primitives replace\n }\n return out;\n}\n\n/**\n * The timeline, which is a discriminated union and therefore cannot be merged blindly.\n *\n * RULE 1 — the patch names a DIFFERENT type: replace the timeline outright, carrying over only\n * the keys both members share. Merging instead would strand `trigger`/`delay` on a\n * scroll timeline, where the format has no slot for them.\n * RULE 2 — same type, or the patch does not mention one: merge. A patch that spells\n * `type: 'time'` against a document with an ABSENT type must count as a match, hence\n * the normalization above.\n * RULE 3 — time-only keys landing on a scroll/view timeline are dropped with a warning, the\n * same way the wire has no slot for them.\n * RULE 4 — `iterations: 'infinite'` cannot map onto a scroll range.\n */\nfunction mergeTimeline(base: unknown, patch: Record<string, any>, warn: (m: string) => void): Record<string, any> {\n const baseType = timelineTypeOf(base);\n const patchNamesType = isPlainObject(patch) && typeof patch.type === 'string';\n const patchType = patchNamesType ? String(patch.type) : baseType;\n\n let merged: Record<string, any>;\n if (patchType !== baseType) {\n // RULE 1\n const carried: Record<string, any> = {};\n if (isPlainObject(base)) {\n for (const k of PX_TIMELINE_SHARED_KEYS) {\n if (base[k] !== undefined) carried[k] = base[k];\n }\n }\n merged = mergePlain(carried, patch);\n } else {\n merged = mergePlain(base, patch); // RULE 2\n }\n\n if (isScrollish(patchType)) {\n for (const k of PX_TIME_ONLY_TIMELINE_KEYS) { // RULE 3\n if (merged[k] === undefined) continue;\n warn('animator.timeline.' + k + \": no slot on a '\" + patchType + \"' timeline — dropped\");\n delete merged[k];\n }\n if (merged.iterations === 'infinite') { // RULE 4\n warn(\"animator.timeline.iterations: 'infinite' cannot map onto a scroll range — dropped\");\n delete merged.iterations;\n }\n }\n return merged;\n}\n\n/** A deep-partial of the WIRE `timeline` block; `null` at any slot deletes it. @public */\nexport type PxTimelinePatch = Record<string, any> | null;\n\n/** The four flat shortcuts every surface offers for the keys people reach for most. @public */\nexport interface PxTimelineShortcuts {\n /** Shortcut for `timeline.duration` — one iteration, ms. Wins over the same key in `timeline`. */\n duration?: number;\n /** Shortcut for `timeline.delay` — the wait before the first iteration, ms. */\n delay?: number;\n /** Shortcut for `timeline.iterations`; `'infinite'` never stops. */\n iterations?: number | 'infinite';\n /**\n * Shortcut for `timeline.trigger.start`. Typed from the WIRE, so it includes\n * `'none'` — \"nothing starts this but a `play()` call\".\n */\n start?: PxTriggerStart;\n}\n\n/**\n * The playback-override props every surface takes — `createAnimator` and the three components:\n * the document's `timeline` as a patch, the reset flag, and the four shortcuts above.\n *\n * ONE definition (review §9): React and React Native extend it, Vue derives its internal shape\n * from it, `createAnimator`'s options extend it.\n * @public\n */\nexport interface PxPlaybackOverride extends PxTimelineShortcuts {\n /**\n * Per-instance override of the document's `animator.timeline` — the same shape as `timeline`\n * in docs/format/README.md, deep-merged over what the document says, so one file can play twice on a page\n * with different timing. `null` at any slot DELETES that key, restoring the default its\n * absence means. Also accepts a JSON STRING, which survives a build that mangles object keys.\n *\n * `timeline` is the whole useful override surface: the rest of the `animator` block is\n * content (`definitions`, `bindings`), a version stamp and a debug handle — none of which\n * a per-instance override should touch. That is why this is not a wrapper object.\n */\n timeline?: PxTimelinePatch | string;\n /**\n * Ignore the document's own timeline and start from the player's DEFAULT timeline, with\n * `timeline` on top. `definitions` and `bindings` are content and are kept either way.\n */\n resetTimeline?: boolean;\n}\n\n/**\n * Folds the four shortcuts into the `timeline` patch, accepts the JSON-STRING form of the patch\n * (immune to property mangling — see docs/library/minification.md), and returns it in the shape\n * `applyAnimatorConfig` takes: an animator-level patch `{ timeline: … }`.\n *\n * A shortcut WINS over the same key inside the object: more specific beats more general, the\n * way an inline style beats a stylesheet. One implementation so every surface agrees.\n * @public\n */\nexport function foldTimelineOverride(\n timeline: PxTimelinePatch | string | undefined,\n shortcuts: PxTimelineShortcuts,\n): PxAnimatorConfigPatch | undefined {\n let base: PxTimelinePatch | undefined;\n if (typeof timeline === 'string') {\n try {\n base = JSON.parse(timeline);\n } catch (e) {\n console.warn('timeline override: not valid JSON — ignored', e);\n base = undefined;\n }\n } else {\n base = timeline ?? undefined;\n }\n\n const { duration, delay, iterations, start } = shortcuts;\n if (duration === undefined && delay === undefined && iterations === undefined && start === undefined) {\n return base === undefined ? undefined : { timeline: base };\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n if (duration !== undefined) out.duration = duration;\n if (delay !== undefined) out.delay = delay;\n if (iterations !== undefined) out.iterations = iterations;\n if (start !== undefined) {\n out.trigger = { ...(out.trigger as Record<string, any> | undefined), start };\n }\n return { timeline: out };\n}\n\n/**\n * Merge a patch over an animator config. PURE — neither argument is mutated, and the result is\n * always a NEW object, which also matters because `flattenAnimatorTimeline` memoises on config\n * identity: mutating in place would hand every later reader the pre-merge view.\n * @public\n */\nexport function mergeAnimatorConfig(\n base: PxAnimatorConfig | undefined,\n patch: PxAnimatorConfigPatch,\n): PxAnimatorConfigMergeResult {\n const warnings: Array<string> = [];\n const warn = (m: string) => warnings.push(m);\n\n if (patch === null) return { config: undefined, warnings };\n if (!isPlainObject(patch) || Object.keys(patch).length === 0) {\n return { config: base, warnings }; // nothing to do: same identity\n }\n\n // RULE 5 — a base in the flat runtime spelling cannot take a wire patch soundly:\n // `flattenAnimatorTimeline` copies the flat keys first and then overwrites them from\n // `timeline`, so a flat `delay` would survive a `timeline.delay: null` deletion.\n const flatInBase = isPlainObject(base) ? FLAT_ONLY_KEYS.filter(k => (base as any)[k] !== undefined) : [];\n if (flatInBase.length) {\n warn('animator: the base carries the flat runtime spelling (' + flatInBase.join(', ') + '); '\n + 'the patch merges the wire spelling only');\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; }\n if (key === 'timeline') {\n out.timeline = isPlainObject(value) ? mergeTimeline(out.timeline, value, warn) : value;\n continue;\n }\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value;\n }\n return { config: out as PxAnimatorConfig, warnings };\n}\n\n/**\n * Document level: resolves the two canonical addresses of the animator config and returns a NEW\n * document that shares every untouched subtree by reference.\n *\n * `resetTimeline` starts from the player's own defaults instead of the document's playback\n * settings — \"play this file as if it said nothing about timing\". The lookup tables are kept\n * either way: resetting `definitions`/`bindings` would leave an animation with nothing to\n * animate, which is never what a caller means.\n * @public\n */\nexport function applyAnimatorConfig(\n doc: PxAnimatedSvgDocument,\n patch: PxAnimatorConfigPatch,\n options?: { resetTimeline?: boolean },\n): { doc: PxAnimatedSvgDocument; warnings: Array<string> } {\n const reset = !!options?.resetTimeline;\n if (!doc || (patch === undefined || (!reset && (patch === null || !isPlainObject(patch) || !Object.keys(patch).length)))) {\n return { doc, warnings: [] };\n }\n\n const anyDoc = doc as any;\n const atRoot = isPlainObject(anyDoc.animator);\n const atMeta = !atRoot && isPlainObject(anyDoc.meta?.animator);\n const current: PxAnimatorConfig | undefined = atRoot ? anyDoc.animator\n : atMeta ? anyDoc.meta.animator\n : undefined;\n\n const warnings: Array<string> = [];\n if (atRoot && isPlainObject(anyDoc.meta?.animator)) {\n warnings.push('animator: doc.meta.animator is shadowed by doc.animator and was not patched');\n }\n\n let base = current;\n if (reset) {\n // Keep only the content tables; everything else starts from the player's defaults.\n const kept: Record<string, any> = {};\n for (const k of CONTENT_KEYS) {\n if (isPlainObject(current) && (current as any)[k] !== undefined) kept[k] = (current as any)[k];\n }\n base = kept as PxAnimatorConfig;\n }\n\n const merged = mergeAnimatorConfig(base, patch ?? {});\n warnings.push(...merged.warnings);\n if (merged.config === current) return { doc, warnings };\n\n if (atMeta) {\n return {\n doc: { ...anyDoc, meta: { ...anyDoc.meta, animator: merged.config } } as PxAnimatedSvgDocument,\n warnings,\n };\n }\n return { doc: { ...anyDoc, animator: merged.config } as PxAnimatedSvgDocument, warnings };\n}\n"],"mappings":"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","names":["effectiveProgress","_a"]}
|