Skip to content

feat(studio): keyframes flag, gesture recording + timeline/selection refinements#1561

Open
miguel-heygen wants to merge 1 commit into
feat/studio-motion-path-overlay-uifrom
feat/studio-keyframes-flag-refinements
Open

feat(studio): keyframes flag, gesture recording + timeline/selection refinements#1561
miguel-heygen wants to merge 1 commit into
feat/studio-motion-path-overlay-uifrom
feat/studio-keyframes-flag-refinements

Conversation

@miguel-heygen

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

Copy link
Copy Markdown
Collaborator

Stack: GSAP keyframe + motion-path editing — feature flag, gesture recording, timeline polish (top of #1553#1561).

What

Ties the feature together and adds the remaining tooling:

  • Feature flag gating keyframe editing (useEnableKeyframes).
  • Gesture recording — record pointer motion over time and commit it as keyframes.
  • Keyframe navigation UI (prev/next + add/remove/convert diamond per property).
  • Razor split for clips, plus timeline / selection refinements.

Why

The keyframe/motion-path surface ships behind a flag; gesture recording gives a fast way to author motion; the navigation + razor tooling rounds out the timeline editing experience.

How

  • useEnableKeyframes.ts: flag + enablement gate.
  • useGestureRecording.ts: samples pointer position over the timeline, simplifies (RDP), and commits keyframes (merge into an overlapping tween, else add a new range).
  • KeyframeNavigation.tsx: per-property diamond (add / remove / convert) + prev/next seek.
  • useRazorSplit.ts: split a clip at the playhead.
  • useDomSelection.ts / useStudioContextValue.ts / StudioHeader.tsx / NLELayout.tsx / RenderQueue.tsx: selection + timeline refinements (selection survives inspector collapse, etc.).

Test plan

  • Unit tests added (keyframe navigation, enable-keyframes)
  • Manual studio: gesture record → keyframes, navigation, razor split, selection persistence

@terencecho terencecho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stack-wide process note (Rames covered structure / 2 concerns on this PR)

Endorsing @rames-jusso's #1561 take in full — playerStore.ts:374-379 exposing the full Zustand store via window.__playerStore in prod is a real footgun (anyone with devtools access can call __playerStore.setState({...}) to mutate selection/elements/playing); and the clipToTweenPercentage 1-keyframe edge using a clipPct fallback IS the same pattern this PR fixes elsewhere. Both should land before merge.

Stack-wide process observation (non-blocking, easy fix for next stack)

All 9 PR descriptions across this stack are the unfilled PR template. For a stack with several PRs north of +500 LOC (and the +651/-4 core-mutations PR #1554 being load-bearing), the empty bodies mean reviewers (human and bot) and future archaeologists have nothing to anchor to — no "what / why / how / test plan" content. Commit messages cover the what well, but PR-body framing of why doesn't survive a bisect via the GitHub UI.

This stack is heavily test-backed and Graphite-anchored, so structural intent is recoverable from diff + commit messages. Not blocking — just a process nudge for the next stack.

Test-coverage map across the 9-PR stack (from my axis pass)

PR Files Test files Notes
#1553 2 0 banner shim, n/a
#1554 5 2 strong (parity + parser units)
#1555 4 1 adequate
#1556 3 0 CLI surface, typical no-unit
#1557 7 3 strong
#1558 8 3 strong on new infra
#1559 6 2 strong on pure-logic helpers
#1560 5 0 UI overlay, typical no-unit
#1561 19 5 very strong

— Review by tai (pr-review)

@miguel-heygen miguel-heygen force-pushed the feat/studio-motion-path-overlay-ui branch from ba99ae0 to 676ba06 Compare June 18, 2026 15:47
@miguel-heygen miguel-heygen force-pushed the feat/studio-keyframes-flag-refinements branch from b56fbc8 to 878d25a Compare June 18, 2026 15:48
@miguel-heygen miguel-heygen force-pushed the feat/studio-motion-path-overlay-ui branch from 676ba06 to f9e5f4f Compare June 18, 2026 15:57
@miguel-heygen miguel-heygen force-pushed the feat/studio-keyframes-flag-refinements branch from 878d25a to bcdfd8e Compare June 18, 2026 15:57

@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.

Concurring with the consolidated review's two author-response gates at HEAD 878d25a2 (both verified still open):

  1. packages/studio/src/player/store/playerStore.ts:377-379window.__playerStore ships unguarded in prod. Verified at HEAD: only the SSR typeof window !== "undefined" guard wraps it; there is no import.meta.env.DEV or process.env.NODE_ENV === "development" gate. Production users get the full Zustand mutating surface via __playerStore.setState({...}).
  2. packages/studio/src/components/editor/KeyframeNavigation.tsx:32-46clipToTweenPercentage 1-anchor edge. Verified at HEAD: if (mapped.length < 2) return clipPct; returns clip-relative clipPct when only one keyframe has a tweenPercentage, which is exactly the off-by-tween-offset silent no-op the PR's own comment block (:115-126) calls out as the bug pattern this PR is fixing. Identity-slope at one anchor (mapped[0].tweenPercentage! + (clipPct - mapped[0].percentage)) is the right fallback.

Two depth additions from my pass:

  1. useEnableKeyframes behavioural change is migration-safe. The diff narrows scope to the "no existing animation" branch only — the kfAnim branch at useEnableKeyframes.ts:126-137 and the flat-anim branch are untouched. New behaviour: a single keyframe at the playhead via resolveNewTweenRange-clamped start, matching the stated intent ("creating 0%+100% up front showed two diamonds for a single add keyframe"). Existing compositions with keyframes already enabled take the unchanged kfAnim branch — they are not migrated. resolveNewTweenRange ships with regression coverage in useEnableKeyframes.test.ts including the auto-stamp data-start="0"/data-duration=14 scenario. Ungating is appropriate here because the regression risk is one-way (better, not worse) for a brand-new gesture.

  2. Drive-by behavioural changes verified non-regressive. (a) Timeline-element key-form fix — buildTimelineElementKey recomputed with domId/selector to match the store's hash form; backed by useExpandedTimelineElements.test.ts (+34) and studioHelpers.test.ts (+32); a pure fix to a selection-highlight bug. (b) setSelectedElementId clearing activeKeyframePct — comment explains the diamond-click ordering invariant; sensible and contained. (c) findTimelineIdByAncestor — additive, tried only when findMatchingTimelineElementId returns null; tested. (d) StudioHeader no longer clears DOM selection on inspector-panel collapse — strictly less destructive than before; the orphaned clearDomSelection import is correctly removed. None of (a-d) is a regression for prior muscle memory.

Title note stands: STUDIO_KEYFRAMES_ENABLED is pre-existing on main at manualEditingAvailability.ts:73; this PR adds use sites, not the flag itself.

Review by Via

@miguel-heygen miguel-heygen force-pushed the feat/studio-motion-path-overlay-ui branch from f9e5f4f to 789f860 Compare June 18, 2026 16:03
@miguel-heygen miguel-heygen force-pushed the feat/studio-keyframes-flag-refinements branch from bcdfd8e to cd69359 Compare June 18, 2026 16:03
…refinements

Enable-keyframes gate, recorded-gesture-stays-in-place, clipToTweenPercentage
keyframe-nav, inline-expand timeline elements, and minor header/layout/store touches.
@miguel-heygen miguel-heygen force-pushed the feat/studio-motion-path-overlay-ui branch from 789f860 to 80f1a0f Compare June 18, 2026 17:49
@miguel-heygen miguel-heygen force-pushed the feat/studio-keyframes-flag-refinements branch from cd69359 to be3c288 Compare June 18, 2026 17:49
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