Skip to content

fix(studio): gesture recording replaces existing position keyframes#1359

Merged
miguel-heygen merged 6 commits into
mainfrom
fix/kf-gesture-recording
Jun 12, 2026
Merged

fix(studio): gesture recording replaces existing position keyframes#1359
miguel-heygen merged 6 commits into
mainfrom
fix/kf-gesture-recording

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Gesture recording uses replace-with-keyframes mutation to replace existing position-group tween
  • Fix N1: sign inversion in gesture delta computation
  • Fix N9: wheel startPointer with pointerElementOffset subtraction

Test plan

  • Build passes
  • Typecheck clean
  • Gesture recording on element with existing position tween replaces it cleanly

Stack: 6/7 — depends on #1358

Add PropertyGroupName type system (position/scale/size/rotation/visual/other),
PROPERTY_GROUPS constant, classifyPropertyGroup/classifyTweenPropertyGroup
functions. Parser generates group-aware animation IDs, resolves position strings
(+=, -=, <, >), uses numeric matching with 2% tolerance, and preserves IDs
across all mutations.
…mutations

Server-side mutations for atomic property-group splitting and keyframe
replacement. Client commitMutation returns early on changed:false instead
of throwing.
Rewire GSAP runtime bridge for property-group routing: drag sends only {x,y}
to position group, resize routes to scale group via data-hf-studio-original-width,
rotation routes to rotation group. Add resolveGroupTween helper, from-extend
with split-first-then-position-only pattern, autoKeyframeEnabled guards,
GSAP base + delta fix in drag draft, cancel-restores-GSAP-x/y from data attrs.
Tag cached keyframes with propertyGroup for group-aware operations.
Add tweenPercentage for accurate keyframe matching, activeKeyframePct
for diamond-click targeting, context menu offset, selected diamond z-index,
clearProps after kill in soft reload.
Add animIdForProp helper routing keyframe diamonds to correct property-group
animation. Wire StudioPreviewArea delete/move/toggle handlers to use
propertyGroup for routing. Fix per-property epsilon in rdpSimplify.
@miguel-heygen miguel-heygen force-pushed the fix/kf-property-panel branch from c664e48 to 57ac327 Compare June 11, 2026 23:54
@miguel-heygen miguel-heygen force-pushed the fix/kf-gesture-recording branch from 9dbe446 to da4a238 Compare June 11, 2026 23:54
@miguel-heygen miguel-heygen marked this pull request as ready for review June 12, 2026 00:18

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: approve-with-nits — one architectural Q for the author.

R2 carry-over for Vance's "should edit, not append" bug is partially addressed via a different mechanism than expected.

Findings

  • useGestureCommit.ts:120-122 — uses selectedGsapAnimations.find(propertyGroup === "position" && targetSelector === selector) rather than the activeKeyframePct plumbing in gsapRuntimeBridge.ts:307 / gsapDragCommit.ts:151. Different shape: gesture-record fixes the duplicate-tween hazard (one position tween per element), not the replace-keyframe-at-active-pct hazard.

    Vance's monolith #1344 complaint was specifically about clicking a keyframe diamond then editing — this PR doesn't wire the diamond-click path into gesture recording. If this is intentional split (gesture = replace whole tween; drag = edit at active pct), call it out in the PR body. Otherwise there's a coverage gap on the specific UX symptom Vance reported.

  • useGestureCommit.ts:128-135replace-with-keyframes mutation shape matches packages/core/src/studio-api/routes/files.ts:413 exactly (animationId, targetSelector, position, duration, keyframes). Wire-coherent.

  • useGestureCommit.ts:67 + :180 — capturing sel at start via capturedSelectionRef is the right call (commit always targets the recorded element). But liveSession.selectedGsapAnimations at line 120 is still read live — if user reselects mid-record, the find() returns nothing (different selector) and falls through to add-with-keyframes. Correct fallback, just worth a comment.

  • useGestureRecording.ts:130-134 — sign fix on pointerElementOffset / scale looks right. But the wheel-handler pointerElementOffset at :317-321 is computed from r.pointer which only updates on pointermove. If the first event is a wheel with no prior pointermove, r.pointer is its init value (0,0?) and elCenterViewport is element center → offset is huge-negative. Verify r.pointer is seeded on pointerenter, not just pointermove.

Test coverage

Zero new tests for the replace-vs-append branch or the per-property epsilon. Behavior is timing-sensitive and the failure mode (duplicate tweens) is exactly what manual-test #1344 surfaced — a unit test for the branch logic is cheap.

Cross-stack

Consumes replace-with-keyframes from #1355 (verified wire-coherent), selectedGsapAnimations.propertyGroup from earlier stack work.

Review by Rames D Jusso

Gesture recording uses replace-with-keyframes mutation to replace existing
position-group tween. Fix N1 sign inversion and N9 wheel startPointer
with pointerElementOffset subtraction.
@miguel-heygen miguel-heygen force-pushed the fix/kf-gesture-recording branch from da4a238 to b4906c9 Compare June 12, 2026 01:04

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R3 verification — approve-with-nits. Main R2 staleness concern resolved.

useGestureCommit.ts:48-51capturedSelectionRef introduced. Selection snapshotted at recording start and used by commit. Via's staleness concern cleanly addressed.

Architectural Q still open. Gesture commit at useGestureCommit.ts:74 still uses propertyGroup === "position" find, not activeKeyframePct. With #1357's activeKeyframePct state landing in the same stack, a reader will reasonably ask why gesture doesn't consult it. One sentence in PR body or a comment at line 72 ("gesture records the position-group tween for this element — activeKeyframePct is a UI-cursor concept and not the right axis here") would close the question.

Per-property epsilons still inline at useGestureCommit.ts:46-49opacity → 0.01, scale* → 0.01, default → 5. Now isolated in one callback (better than scattered), but the next reviewer adding rotation/skew will have to hunt. const RDP_EPSILON_BY_KEY: Record<string, number> two lines above lands it.

Review by Rames D Jusso

vanceingalls
vanceingalls previously approved these changes Jun 12, 2026

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — Via reviewed, stamp applied.

— Vai

Base automatically changed from fix/kf-property-panel to main June 12, 2026 04:19
@miguel-heygen miguel-heygen dismissed vanceingalls’s stale review June 12, 2026 04:19

The base branch was changed.

@miguel-heygen miguel-heygen merged commit 191a33d into main Jun 12, 2026
19 of 20 checks passed
@miguel-heygen miguel-heygen deleted the fix/kf-gesture-recording branch June 12, 2026 04:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants