Skip to content

feat(core): route motion-path mutations through studio-api + fix clip stamping#1555

Open
miguel-heygen wants to merge 1 commit into
feat/core-gsap-motion-path-mutationsfrom
feat/core-motion-path-route
Open

feat(core): route motion-path mutations through studio-api + fix clip stamping#1555
miguel-heygen wants to merge 1 commit into
feat/core-gsap-motion-path-mutationsfrom
feat/core-motion-path-route

Conversation

@miguel-heygen

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

Copy link
Copy Markdown
Collaborator

Stack: GSAP keyframe + motion-path editing — studio-api wiring + runtime clip fixes (#1553#1561).

What

Two things:

  1. Route the new keyframe / motion-path mutations through the studio-api source-mutation layer and file route, so Studio edits persist through the single mutation entry point.
  2. Fix runtime clip stamping and in-flow clip hiding in runtime/init.ts.

Why

  • Studio must persist GSAP edits via studio-api (consistent validation + undo), not ad-hoc writers.
  • Clip stamping: an auto-stamped animated scene container was suppressing its own animated children from becoming timeline clips, so that scene couldn't inline-expand.
  • In-flow timed clips hidden with visibility:hidden still reserve their layout box, so a split clip's two halves stacked instead of overlapping.

How

  • sourceMutation.ts + routes/files.ts: dispatch motion-path/keyframe ops through studio-api.
  • init.ts: ancestor-suppression now counts authored data-start only (hasAuthoredTimedAncestor); in-flow timed clips hide with display:none while positioned clips keep visibility:hidden.

Test plan

  • Unit tests added (sourceMutation)
  • bun run test (core) green
  • ⚠️ Known issue under investigation: hdr-hlg-regression shard fails on the final ~4 frames (PSNR ~9.7 dB). Tracking whether the in-flow-clip hiding change interacts with HDR layered capture.

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

Holding per two independent verifications (Rames D Jusso + Via) of the same blocker. Calling out one specifically because it's a regression of work that landed earlier today.

🛑 Silent removal of the 5 readiness adapters from runtime/init.ts

packages/core/src/runtime/init.ts:9-13 + :1918-1924 — the five adapter registrations from https://github.com/heygen-com/hyperframes/pull/1548|#1548 (createMapboxAdapter / createLeafletAdapter / createGoogleMapsAdapter / createMaplibreAdapter / createD3Adapter) are removed in this diff. The factories themselves (and their .test.ts siblings) survive in runtime/adapters/, but the wiring at the registry — the thing that actually makes them participate in getReadyPromise() polling — is gone.

#1548 merged at 2026-06-18T05:21:28Z (~10 hours ago) specifically to gate __renderReady on map/viz async asset loading. If #1555 lands as-is, any composition that uses mapbox / leaflet / google-maps / maplibre / d3 will silently never reach render-readiness — the exact bug class #1548 was built to prevent. This is a real production regression, not just a code-style concern.

Most likely cause is a Graphite restack mishap when #1548 merged mid-stack and the conflict resolution dropped the wiring rather than preserved it. The benign path is one rebase + re-add the import + the 5 factory calls; the malignant path would require intentional rationale I can't find anywhere in the PR description.

Ask: confirm intentional (with rationale + regression-window acknowledgement) OR re-restack to preserve #1548's wiring.

🛑 HOLD_SYNC_MUTATION_TYPES set is incomplete

Echoing Rames D Jusso (packages/core/src/studio-api/routes/files.ts:534-548) — the set covers keyframe / motion-path-point mutations + delete but misses 5 mutation types that also change tween start or create a tween at position > 0:

  • add-motion-pathaddMotionPathToScript authors a 2-anchor motionPath at caller's position; if position > 0, fresh tween snaps from CSS-base → (0,0) on start.
  • add (generic) — can create position tweens via properties: {x, y}.
  • update-metabody.updates.position changes start; existing hold becomes stale.
  • shift-positions / scale-positions — bulk-shift; pre-existing holds end up at wrong t.
  • split-animations — produces new tweens at new positions.

syncPositionHoldsBeforeKeyframes is idempotent + script-scoped, so an over-broad set just costs one extra parse. Cheap expansion.

Non-blocking notes

Echoing Rames D Jusso:

  • init.ts:1440-1448timedClipInFlow WeakMap caches getComputedStyle(el).position lazily, never invalidates. Studio-edit-then-replay round-trip → stale cache picks wrong display branch. Either invalidate on style-position mutation or document the invariant.
  • init.ts:1555-1559 — visible branch uses raw timedClipInFlow.get(rawNode), hidden branch uses isTimedClipInFlow(rawNode) (force-populates). Works by happy accident; align both or comment why.

Plus: PR body is the unfilled template across this whole stack. Worth filling in even a one-paragraph description before merge.

Once the adapter regression is addressed (re-add the wiring OR explicit rationale + downstream owner ack), happy to re-review.

… stamping

Wire the new mutations into the file save route. Only authored clips suppress
descendant stamping, so auto-stamped animated scenes can inline-expand.
@miguel-heygen miguel-heygen force-pushed the feat/core-motion-path-route branch from 070799e to 227aebe 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-reviewed at 227aebe4. Top blocker closed — adapter wiring restored at init.ts:9-13, :1939-1943, so the 5 readiness-adapter registrations from #1548 are back. Thanks for the fast restack.

Reduced-scope REQUEST_CHANGES on the remaining items, per Via's R2 verification:

1. HOLD_SYNC_MUTATION_TYPES set still incomplete at routes/files.ts:534-548.
7 keyframe-adjacent types added in the restack (good) — remove-all-keyframes, add-with-keyframes, replace-with-keyframes, convert-to-keyframes, materialize-keyframes, delete, delete-all-for-selector. The 5 position-changing-non-keyframe types still need to be added:

  • add-motion-pathaddMotionPathToScript authors a 2-anchor motionPath at caller's position; if position > 0, fresh tween snaps from CSS-base → (0,0) on start.
  • add (generic) — can create position tweens via properties: {x, y}.
  • update-metabody.updates.position changes start; existing hold becomes stale.
  • shift-positions / scale-positions — bulk-shift; pre-existing holds end up at wrong t.
  • split-animations — produces new tweens at new positions.

Cheap: syncPositionHoldsBeforeKeyframes is idempotent + script-scoped, so an over-broad set just costs one parse.

2. timedClipInFlow WeakMap stale-cache at init.ts:1445-1452.
Still no invalidation hook. Studio-edit-then-replay round-trip → stale cache picks wrong display branch. Either timedClipInFlow.delete(el) on style-position mutation, OR explicit invariant comment naming the round-trip case as out-of-scope.

3. init.ts:1560-1563 — visible/hidden branch asymmetry.
Visible branch uses raw timedClipInFlow.get(rawNode) (returns undefined for new nodes); hidden branch uses isTimedClipInFlow(rawNode) (force-populates). Works by happy accident — align both or comment why.

Once these three land, happy to re-approve. Also: PR body is still the unfilled template; please fill in before merge.

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