Commit Graph
3 Commits
Author SHA1 Message Date
Miguel Ángel f0b499b582 fix(studio): keyframe bug fixes — gate delete hooks, fix value corruption, gesture recording (#1314)
- Gate stripStudioEditsFromTarget/bakeVisibilityOnDelete behind a
  stripStudioEdits flag on the delete mutation type so they only fire on
  user-initiated deletes, not on internal delete-then-recreate drags.

- Add bakeVisibilityOnDelete to the remove-all-keyframes handler so
  elements with CSS opacity:0 stay visible after collapsing keyframes.

- Fix integer rounding in readAllAnimatedProperties: use 3-decimal
  precision for visual properties (opacity, scale, rotation) instead of
  Math.round which corrupted mid-fade values to 0.

- Guard VISUAL_BASELINE against cross-tween contamination by querying
  __timelines for properties animated by other tweens on the same element.

- Harden bakeVisibilityOnDelete: reverse-scan keyframes for the last one
  containing opacity, guard against relative values (+=/-=/*=), and add
  Number.isFinite check.

- Fix falsy-zero doubling in drag commit: replace || fallback with
  Number.isFinite so a base GSAP position of 0 is correctly preserved.

- Fix gesture recording sign inversion: remove pointerElementOffset
  subtraction from dx/dy formula and instead apply it once to basePosition
  so the element center tracks the pointer.

- Fix TypeScript build errors in gsapSoftReload.ts (6 double-casts).

- Strip all diagnostic logs from production code.
2026-06-10 18:53:06 -04:00
Miguel Ángel a468550f82 feat(studio): keyframe system — parser, runtime, timeline UI, design panel, gesture recording (#1311)
* feat(studio): runtime hooks — global time compiler + keyframe runtime

Add the runtime bridge layer: global time compilation (tween % → clip %),
soft reload after mutations, runtime keyframe preview, and keyframe
commit helper.

* feat(studio): runtime hooks — global time compiler + keyframe runtime

Add the runtime bridge layer: global time compilation (tween % → clip %),
soft reload after mutations, runtime keyframe preview, and keyframe
commit helper.

* feat(studio): keyframe cache + commit hooks

Add hooks for keyframe cache population (tween → clip-relative %),
mutation dispatch, keyframe snapping, and audio beat detection.

* feat(studio): timeline UI — dopesheet diamonds + keyboard nav

Add dopesheet strip with diamond keyframe indicators, timeline property
rows, keyboard navigation (J/Shift+J/Delete/K), and feature gate
(STUDIO_KEYFRAMES_ENABLED defaults to false).

* feat(studio): design panel — arc controls + ease curve + stagger

Add arc path controls (curviness slider, auto-rotate), motion path SVG
overlay, ease curve visualization, stagger controls, and expanded
animation card. Includes border-radius editor dependency from #1217.

* feat(studio): gesture recording core

Add gesture recording engine with RAF sampling, modifier key property
mapping (Shift→rotationXY, Alt→rotation, Cmd→opacity),
Ramer-Douglas-Peucker simplification, and ghost trail SVG overlay.

* fix(studio): keyframe drag + recording bug bash

21 fixes: capture GSAP base at drag start, translate:none before
gsap.set, skip reapplyPathOffsets for GSAP elements, clamp recording
seek, _auto flag for 100% keyframes, overlay flash fix, block edits
during recording.

* feat(studio): keyframe integration wiring + docs

Wire App.tsx recording orchestration, TimelineToolbar K/R buttons,
PropertyPanel per-property diamonds, shortcuts panel, toast
notifications, and keyframes guide documentation. All gated on
STUDIO_KEYFRAMES_ENABLED (default false).
2026-06-09 18:30:23 -04:00
Miguel Ángel ae717331ce fix(studio): soft-reload GSAP property edits, preserve shader cache (#1129)
* fix(studio): soft-reload GSAP property edits without iframe reload

GSAP property value edits (opacity, x, scale, etc.) now update the live
timeline inside the preview iframe without triggering a full iframe
reload. This preserves the WebGL context and shader transition cache,
eliminating the loading overlay that appeared on every property edit.

Implementation:
- New gsapSoftReload.ts: kills the old GSAP timeline, re-executes the
  updated script, calls __hfForceTimelineRebind(), and re-seeks to the
  current time. Falls back to full reload on failure.
- useGsapScriptCommits: passes softReload: true for property value edits
  via the existing (previously unused) softReload flag on commitMutation.
- hyper-shader.ts: exposes __hfSuppressSceneMutations on the window so
  the soft-reload can suppress the MutationObserver during re-execution.
- hyper-shader.ts: getDocumentScriptSignature now excludes pure GSAP
  animation scripts from the cache key hash, so full reloads (undo,
  external changes) don't invalidate transition caches when only
  animation values changed.

* fix(studio): wrap soft-reload script in IIFE to avoid const redeclaration

The new script ran in the same global scope as the old one, causing
Identifier tl has already been declared errors from const/let
re-declarations. Wrapping in an IIFE creates a new lexical scope.
Also remove the old script element before inserting the new one.

* fix(studio): return scriptText from mutation API, drop client-side HTML parsing

The mutation API already has the extracted GSAP script text (newScript)
after rewriting. Return it as scriptText in the response so
applySoftReload receives the script directly instead of parsing HTML
client-side. This avoids DOMParser compatibility issues across test
environments and is more reliable than regex-based extraction.

* fix(studio): align soft-reload script heuristic with server-side parser

The client's findGsapScriptElement only matched gsap.timeline and
__timelines. The server's extractGsapScriptBlock also matches .to( and
.set(. Aligned the client heuristic to prevent silent fallback to full
reload for compositions that use tl.to() without gsap.timeline in the
same script.

* fix(studio): address hf#1129 review — multi-script guard, scope docs

- Return false (fallback to full reload) when multiple GSAP scripts
  exist in the document, since it's ambiguous which one to replace
- Add docstring scoping the optimization to root-document scripts
  (template-wrapped sub-compositions fall back to full reload)
- Add code comment explaining the IIFE scope constraint
- Add test for the multi-script guard

* fix(studio): align cache key filter with soft-reload script heuristic

isGsapAnimationOnlyScript now also matches .to( and .set( patterns,
matching findGsapScriptElement. Scripts using only tl.to() without
gsap.timeline were excluded from soft-reload but still busted the
shader cache on full-reload paths (undo, external changes).
2026-05-29 23:34:57 -04:00