Skip to content

feat(studio): GSAP runtime read layer + shared helpers#1557

Merged
miguel-heygen merged 0 commit into
feat/cli-keyframes-commandfrom
feat/studio-gsap-read-layer
Jun 18, 2026
Merged

feat(studio): GSAP runtime read layer + shared helpers#1557
miguel-heygen merged 0 commit into
feat/cli-keyframes-commandfrom
feat/studio-gsap-read-layer

Conversation

@miguel-heygen

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

Copy link
Copy Markdown
Collaborator

Stack: GSAP keyframe + motion-path editing — studio runtime read layer (#1553#1561).

What

A read layer that reads live GSAP keyframes / motion-paths from the preview iframe runtime, plus shared time helpers, a parsed-animation fetch fallback, and a per-element tween cache.

Why

The static parser can't always resolve keyframes (data-driven loops, computed selectors, fetched values). Reading the live __timelines runtime fills the gap; a cold-parse fetch fallback covers the initial-load race where the parse endpoint isn't warm yet.

How

  • gsapRuntimeKeyframes.ts: readRuntimeKeyframes / scanAllRuntimeKeyframes read the live timeline (skipping zero-duration hold sets).
  • gsapShared.ts: tween-relative ↔ clip-relative percentage conversion.
  • useGsapAnimationFetchFallback.ts: retry the parse endpoint on a cold read.
  • useGsapTweenCache.ts: memoize parsed animations per element.

Test plan

  • Unit tests added (runtime read, shared helpers, fetch fallback)
  • bun run test (studio) green

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

Approved at 74c37f3a per Rames D Jusso + Via stack review. GSAP runtime read layer + shared helpers — focused, isolated, no concerns.

Reminder: PR body is the unfilled template (stack-wide process flag from Rames D Jusso + Via); please fill in before merge.

@miguel-heygen miguel-heygen force-pushed the feat/cli-keyframes-command branch from 5e8cf5e to f6a0cbb Compare June 18, 2026 15:47
@miguel-heygen miguel-heygen force-pushed the feat/studio-gsap-read-layer branch from 74c37f3 to 52dfaa9 Compare June 18, 2026 15:48

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

Re-approved at 52dfaa9e post-restack. GSAP runtime read layer unchanged in substance; clean.

@miguel-heygen miguel-heygen force-pushed the feat/studio-gsap-read-layer branch from 52dfaa9 to 06aecb2 Compare June 18, 2026 17:49
@miguel-heygen miguel-heygen force-pushed the feat/cli-keyframes-command branch from f6a0cbb to a682a91 Compare June 18, 2026 21:43
@miguel-heygen miguel-heygen force-pushed the feat/studio-gsap-read-layer branch 2 times, most recently from 079beeb to c4717a3 Compare June 18, 2026 21:47
@miguel-heygen miguel-heygen merged commit c4717a3 into feat/cli-keyframes-command Jun 18, 2026
3 of 4 checks passed
@miguel-heygen miguel-heygen force-pushed the feat/cli-keyframes-command branch from a682a91 to 41ca837 Compare June 18, 2026 21:47
@miguel-heygen miguel-heygen deleted the feat/studio-gsap-read-layer branch June 18, 2026 21:47
miguel-heygen added a commit that referenced this pull request Jun 18, 2026
Consolidates the studio side of the GSAP keyframe/motion-path work into one
PR: runtime read layer + shared helpers, drag/commit/bridge editing infra,
motion-path geometry + commit helpers, on-canvas motion-path overlay, and the
keyframes flag with gesture recording + timeline/selection refinements.

Also fixes "Add keyframe at playhead" for array-form keyframe tweens
(keyframes: [{x,y},…]):
- readElementPosition derived the captured props from anim.properties, which
  is empty for array-form keyframes, so the position came back empty and the
  add silently no-oped. It now falls back to the union of the keyframe stops'
  property keys (then x/y).
- The add site and the toolbar button state computed the playhead percentage
  from the element clip range, not the tween range, so keyframes landed at the
  wrong percentage. Both now use the tween-relative basis.
- When the playhead is outside the tween range, the button is disabled instead
  of silently no-oping (or, post-basis-fix, deleting the boundary keyframe).

Supersedes the separately-reviewed studio PRs #1557, #1558, #1559, #1560, #1561.
miguel-heygen added a commit that referenced this pull request Jun 18, 2026
Consolidates the studio side of the GSAP keyframe/motion-path work into one
PR: runtime read layer + shared helpers, drag/commit/bridge editing infra,
motion-path geometry + commit helpers, on-canvas motion-path overlay, and the
keyframes flag with gesture recording + timeline/selection refinements.

Also fixes "Add keyframe at playhead" for array-form keyframe tweens
(keyframes: [{x,y},…]):
- readElementPosition derived the captured props from anim.properties, which
  is empty for array-form keyframes, so the position came back empty and the
  add silently no-oped. It now falls back to the union of the keyframe stops'
  property keys (then x/y).
- The add site and the toolbar button state computed the playhead percentage
  from the element clip range, not the tween range, so keyframes landed at the
  wrong percentage. Both now use the tween-relative basis.
- When the playhead is outside the tween range, "add keyframe at playhead" now
  extends the tween to reach it and adds a keyframe there, keeping the existing
  keyframes at their absolute times (percentages rescale into the new range),
  instead of disabling or no-oping.

Supersedes the separately-reviewed studio PRs #1557, #1558, #1559, #1560, #1561.
miguel-heygen added a commit that referenced this pull request Jun 18, 2026
Consolidates the studio side of the GSAP keyframe/motion-path work into one
PR: runtime read layer + shared helpers, drag/commit/bridge editing infra,
motion-path geometry + commit helpers, on-canvas motion-path overlay, and the
keyframes flag with gesture recording + timeline/selection refinements.

Also makes "Add keyframe at playhead" do the right thing for every GSAP
animation shape, never disabling or silently no-oping:
- Array-form keyframe tweens (keyframes: [{x,y},…]): readElementPosition now
  derives the captured props from the keyframe stops (top-level properties is
  empty for array form), and the percentage uses the tween range, not the clip
  range — so the add lands at the right spot instead of no-oping.
- Out of the tween range, the action extends the tween to reach the playhead
  and adds a hold there, rescaling existing keyframes to keep their absolute
  timing (was: disabled / destructive).
- Flat tweens (to/from/fromTo) convert to their natural keyframes, then take
  the same add/extend path — so an out-of-range playhead extends them too.
- set() is promoted to a two-stop tween from the set's time to the playhead.
- motionPath/arc tweens add a waypoint at the on-path position (matching
  segment, so the curve is preserved) instead of being linearized; outside the
  range they extend their duration. A small merge threshold avoids duplicate
  waypoints at the path endpoints.

Supersedes the separately-reviewed studio PRs #1557, #1558, #1559, #1560, #1561.
miguel-heygen added a commit that referenced this pull request Jun 18, 2026
Consolidates the studio side of the GSAP keyframe/motion-path work into one
PR: runtime read layer + shared helpers, drag/commit/bridge editing infra,
motion-path geometry + commit helpers, on-canvas motion-path overlay, and the
keyframes flag with gesture recording + timeline/selection refinements.

Makes "Add keyframe at playhead" do the right thing for every GSAP animation
shape, never disabling or silently no-oping:
- Array-form keyframe tweens (keyframes: [{x,y},…]): readElementPosition now
  derives the captured props from the keyframe stops (top-level properties is
  empty for array form), and the percentage uses the tween range, not the clip
  range — so the add lands at the right spot instead of no-oping.
- Out of the tween range, the action extends the tween to reach the playhead
  and adds a hold there, rescaling existing keyframes to keep their absolute
  timing (was: disabled / destructive).
- Flat tweens (to/from/fromTo) convert to their natural keyframes, then take
  the same add/extend path.
- set() is promoted to a two-stop tween from the set's time to the playhead.
- motionPath/arc tweens add a waypoint at the on-path position (matching
  segment, so the curve is preserved) instead of being linearized; outside the
  range they extend their duration, with a merge threshold against duplicates.

Also fixes deep-link hydration: on a fresh full-page load the player runtime
isn't ready to honor the first requestSeek, so the one-shot seek latched without
moving the playhead, and the selection hydration (gated on the seek settling)
never ran — a URL with ?t=…&selId=… restored neither. A bounded heartbeat now
retries the seek until the player honors it, then the selection restores.

Supersedes the separately-reviewed studio PRs #1557, #1558, #1559, #1560, #1561.
miguel-heygen added a commit that referenced this pull request Jun 18, 2026
Consolidates the studio side of the GSAP keyframe/motion-path work into one
PR: runtime read layer + shared helpers, drag/commit/bridge editing infra,
motion-path geometry + commit helpers, on-canvas motion-path overlay, and the
keyframes flag with gesture recording + timeline/selection refinements.

Makes "Add keyframe at playhead" do the right thing for every GSAP animation
shape, never disabling or silently no-oping:
- Array-form keyframe tweens (keyframes: [{x,y},…]): readElementPosition now
  derives the captured props from the keyframe stops (top-level properties is
  empty for array form), and the percentage uses the tween range, not the clip
  range — so the add lands at the right spot instead of no-oping.
- Out of the tween range, the action extends the tween to reach the playhead
  and adds a hold there, rescaling existing keyframes to keep their absolute
  timing (was: disabled / destructive).
- Flat tweens (to/from/fromTo) convert to their natural keyframes, then take
  the same add/extend path.
- set() is promoted to a two-stop tween from the set's time to the playhead.
- motionPath/arc tweens add a waypoint at the on-path position (matching
  segment, so the curve is preserved) instead of being linearized; outside the
  range they extend their duration, with a merge threshold against duplicates.

Also fixes deep-link hydration. A URL with ?t=…&selId=… restored neither the
playhead nor the selection on a fresh load: useStudioUrlState requests the seek
before the player runtime mounts its requestedSeekTime subscription, so the
request never reached pendingSeekRef, and initializeAdapter (which drained only
pendingSeekRef when the adapter became ready) started at 0 — which also blocked
selection hydration (gated on the seek settling). Fixed at the source:
initializeAdapter now reconciles the store's requestedSeekTime as well, so a seek
requested any time before the adapter is ready lands deterministically.

Supersedes the separately-reviewed studio PRs #1557, #1558, #1559, #1560, #1561.
miguel-heygen added a commit that referenced this pull request Jun 19, 2026
Consolidates the studio side of the GSAP keyframe/motion-path work into one
PR: runtime read layer + shared helpers, drag/commit/bridge editing infra,
motion-path geometry + commit helpers, on-canvas motion-path overlay, and the
keyframes flag with gesture recording + timeline/selection refinements.

Makes "Add keyframe at playhead" do the right thing for every GSAP animation
shape, never disabling or silently no-oping:
- Array-form keyframe tweens (keyframes: [{x,y},…]): readElementPosition now
  derives the captured props from the keyframe stops (top-level properties is
  empty for array form), and the percentage uses the tween range, not the clip
  range — so the add lands at the right spot instead of no-oping.
- Out of the tween range, the action extends the tween to reach the playhead
  and adds a hold there, rescaling existing keyframes to keep their absolute
  timing (was: disabled / destructive).
- Flat tweens (to/from/fromTo) convert to their natural keyframes, then take
  the same add/extend path.
- set() is promoted to a two-stop tween from the set's time to the playhead.
- motionPath/arc tweens add a waypoint at the on-path position (matching
  segment, so the curve is preserved) instead of being linearized; outside the
  range they extend their duration, with a merge threshold against duplicates.

Also fixes deep-link hydration. A URL with ?t=…&selId=… restored neither the
playhead nor the selection on a fresh load: useStudioUrlState requests the seek
before the player runtime mounts its requestedSeekTime subscription, so the
request never reached pendingSeekRef, and initializeAdapter (which drained only
pendingSeekRef when the adapter became ready) started at 0 — which also blocked
selection hydration (gated on the seek settling). Fixed at the source:
initializeAdapter now reconciles the store's requestedSeekTime as well, so a seek
requested any time before the adapter is ready lands deterministically.

Supersedes the separately-reviewed studio PRs #1557, #1558, #1559, #1560, #1561.
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.

2 participants