/** * @file draw/serialize-animate.ts — IR Timeline → animated SVG/JSX * @scope apps/studio/draw/serialize-animate.ts * @purpose Time-generalize the DDR-067 single-source invariant: ONE node tree * + ONE {@link Timeline} emits BOTH an animated SVG (on-disk `.svg` * form) AND an animated JSX string (inline canvas-preview form). Both * render the SAME tree — `primitivesToNodes` from `serialize.ts` with * SMIL animation children injected onto each track's target — so the * two forms can never structurally drift (the test asserts this). * * Mechanism (per the DDR-094 conversion rules + `_draw-motion-rules`): * • `d` shape morph → `` * (NEVER CSS `d:path()`, which doesn't animate live). * • opacity / fill / stroke → ``. * • translate / scale / rotate → `` * — the position-vs-animation split: animated transforms compose * with (never clobber) a static `transform=` attribute. * • Per-segment easing → `keySplines` + `calcMode="spline"`, taken * verbatim from the IR `cubic-bezier` handles (overshoot survives). * * Reduced motion: emitted as a `