Skip to content

feat(studio): MotionPathPlugin arc motion + design panel redesign#1232

Open
miguel-heygen wants to merge 34 commits into
fix/keyframe-stability-border-radiusfrom
feat/motionpath-arc-motion
Open

feat(studio): MotionPathPlugin arc motion + design panel redesign#1232
miguel-heygen wants to merge 34 commits into
fix/keyframe-stability-border-radiusfrom
feat/motionpath-arc-motion

Conversation

@miguel-heygen
Copy link
Copy Markdown
Collaborator

Summary

  • Integrate GSAP MotionPathPlugin for arc/curved motion between keyframe positions
  • Full design panel visual redesign with instrument-panel aesthetic
  • Runtime auto-discovery of GSAP-animated elements for timeline
  • Export button in header + redesigned render controls

MotionPathPlugin Arc Motion

  • Lift MotionPathPlugin validation ban + conditional CDN loading
  • ArcPathConfig types + parser recognition (waypoints, control points, curviness, autoRotate)
  • AST mutation functions for setting/updating/removing arc paths
  • Studio API mutation routes (set-arc-path, update-arc-segment, remove-arc-path)
  • Design panel controls: toggle, per-segment curviness sliders, auto-rotate
  • SVG MotionPathOverlay component for visual path rendering
  • Runtime MotionPathPlugin registration + soft reload support
  • Add-to-basket arc animation demo composition

Runtime Timeline Discovery

  • Auto-stamp data-start/data-duration on GSAP-targeted elements
  • Elements start at t=0 spanning full composition duration
  • Synthesize keyframe diamonds from flat tweens (not just keyframes: {} format)

Design Panel Redesign

  • panel-* color tokens in Tailwind config
  • Teal #2DD4BF unified accent (replaces 4 inconsistent colors)
  • Title-case bold section headers (Figma UI3 pattern)
  • Borderless bg-panel-input fields with teal focus ring
  • 2px slider tracks, 28x16px toggles, 6px input radius
  • Collapsed sections with "+" icon
  • Text section collapsed by default
  • Remove card wrapper from text field sections

Layers & Renders Panel

  • Layers panel uses panel-* tokens
  • Render controls: clean 2×2 grid (Format, Resolution, Frame rate, Quality)
  • Export button in header navigates to Renders tab
  • Format help tooltip restored
  • SSR externals broadened for render route

Test plan

  • Select element with keyframes → Arc Motion toggle appears in animation card
  • Enable arc → curviness sliders show per-segment
  • Timeline shows individual GSAP-animated elements with diamonds
  • Design panel uses teal accent throughout
  • Section headers are title-case bold
  • Export button in header opens Renders tab
  • Render controls show 2×2 labeled grid
  • Layers panel uses panel-* tokens

…ache on deletion

Two stability fixes for the keyframe editing flow:

1. Code editor refreshes after GSAP script mutations via onFileContentChanged
   callback that syncs result.after to editingFile state.

2. Keyframe cache clears on deletion: commitMutation removes cache entries for
   elements without keyframes, usePopulateKeyframeCacheForFile clears stale
   entries before repopulating.

Also fixes pre-existing typecheck: handleGsapMaterializeKeyframes optional
in TimelineToolbar DomEditSessionSlice.
Add border-top-left-radius, border-top-right-radius,
border-bottom-right-radius, border-bottom-left-radius to
CURATED_STYLE_PROPERTIES. The Radius section auto-detects
uniform vs per-corner mode: single slider when all corners
match, 4 MetricFields (TL/TR/BR/BL) when they differ.
Figma-style SVG corner picker showing a rounded rectangle preview
with 4 corner handles. Link/unlink toggle switches between uniform
mode (single field) and per-corner mode (4 fields TL/TR/BL/BR).
When GSAP animates borderRadius, read the per-corner computed styles
from the iframe element and pass them to the BorderRadiusEditor. The
design panel now shows interpolated border-radius values as the
playhead moves through GSAP keyframes.
When the code editor saves changes (via refreshKey), the GSAP keyframe
cache was not invalidated because code-tab edits bypass commitMutation.
Watch refreshKey changes and bump gsapCacheVersion so timeline diamonds
clear when keyframes are manually removed in the code editor.
Closes #1219

On 8GB RAM machines, renders time out at 5% with `Runtime.callFunctionOn timed out` during the duration probe. User-set timeout env vars (`PRODUCER_PUPPETEER_PROTOCOL_TIMEOUT_MS`) are silently ignored by the calibration path, and there are no CLI flags to control timeouts directly.

1. **Calibration timeout cap overrides user settings** — `createCaptureCalibrationConfig` used `Math.min(cfg.protocolTimeout, 30_000)`, meaning even if the user set 300s, calibration still capped at 30s. On slow hardware this causes unnecessary timeouts.

2. **8GB systems get no low-memory treatment** — `getLowMemoryFlags()`, `getGpuMemBudgetMb()`, `memoryAdaptiveCacheLimit()`, and `memoryAdaptiveCacheBytesMb()` all used `< 8192` as the threshold. Systems reporting exactly 8192 MB (common for 8GB machines) fell through to the "plenty of memory" path, getting no Chrome heap reduction or cache limits.

3. **No CLI flags for key timeouts** — Users had to discover the correct env var names (`PRODUCER_PUPPETEER_PROTOCOL_TIMEOUT_MS`, `PRODUCER_PLAYER_READY_TIMEOUT_MS`) by reading source. The non-existent `PUPPETEER_PROTOCOL_TIMEOUT` and `--browser-timeout` were common guesses that did nothing.

- `captureCost.ts`: `Math.min` → `Math.max` so the 30s calibration default is a floor, not a ceiling. User-set higher timeouts are now respected.
- `browserManager.ts`: `>= 8192` → `> 8192` in `getLowMemoryFlags()` and `<= 8192` in `getGpuMemBudgetMb()` so 8GB systems get reduced Chrome heap and GPU memory budget.
- `config.ts`: `< 8192` → `<= 8192` in `memoryAdaptiveCacheLimit()` and `memoryAdaptiveCacheBytesMb()` so 8GB systems get reduced frame cache limits.
- `render.ts`: Added `--protocol-timeout <ms>` and `--player-ready-timeout <ms>` CLI flags, wired through `resolveConfig` overrides.
- Updated calibration tests to match the new floor-not-ceiling behavior.
- Added fallow suppressions for pre-existing unused exports in `captureCost.ts`.

- [x] Engine config tests pass (`vitest run src/config.test.ts`)
- [x] Browser manager tests pass (`vitest run src/services/browserManager.test.ts`)
- [x] Calibration safeguard tests pass (4/4 in `renderOrchestrator.test.ts`)
- [x] TypeScript compiles cleanly for engine and cli packages
- [ ] CI pipeline
* fix: add progress logging during silent render pipeline stages

The render pipeline only updates progress at stage boundaries (5%, 10%,
25%), leaving multi-minute gaps with zero log output on low-memory
hardware. This adds log.info calls at key sub-steps within the three
silent stages:

- Probe stage (5%): browser launch, session initialization, duration
  discovery, media asset discovery, audio volume automation, video
  visibility window detection
- Video extraction (10%): per-video extraction progress
- Calibration (25%): browser launch, session initialization,
  per-frame calibration progress, final cost estimate

Also adds 30-second heartbeat timers for the two initializeSession
calls (probe and calibration) that can individually take minutes on
constrained hardware.

Closes #1218

* fix: resolve CI failures in typecheck, runtime seek test, and timeline test

- Make handleGsapMaterializeKeyframes optional in DomEditSessionSlice
  and use optional chaining at the call site (not yet wired)
- Update GSAP adapter seek test to expect nudge+seek pattern
  (totalTime with suppressEvents:true followed by actual seek)
- Fix Timeline canvas height test to use TRACK_H constant (48)
  instead of stale hardcoded value (72)

* refactor: extract helpers to meet 600-line file size limit

- App.tsx (603→594): extract StudioToast component
- useDomEditSession.ts (688→600): extract useGsapSelectionHandlers hook
- Timeline.tsx (614→557): extract useTimelineAssetDrop hook
- PropertyPanel.tsx (647→584): extract TimingSection to propertyPanelTimingSection

* style: fix formatting in TimelineToolbar
…CDN loading

Remove MotionPathPlugin and registerPlugin from FORBIDDEN_GSAP_PATTERNS —
each dangerous plugin (ScrollTrigger, etc.) is already individually banned,
so the blanket registerPlugin ban was redundant. Add MOTIONPATH_CDN constant
and conditionally include the MotionPathPlugin script tag in generated HTML
when the GSAP script references motionPath.
Add ArcPathSegment, ArcPathConfig types to gsapSerialize.ts. Extend
GsapAnimation with optional arcPath field. In gsapParser, parse the
motionPath property from tween vars — extract waypoints into keyframe
x/y properties and control points into ArcPathSegment. Supports both
curviness-based (waypoint array) and type:"cubic" (explicit bezier
control points) formats. When motionPath coexists with keyframes,
waypoint positions are merged into existing keyframes.
Add setArcPathInScript, updateArcSegmentInScript, removeArcPathFromScript
to gsapParser.ts. These operate at the AST level via recast, following
the existing keyframe mutation pattern. setArcPath extracts x/y from
keyframes into a motionPath object with type:"cubic" and interleaved
control points, strips x/y from keyframe nodes. updateArcSegment
rebuilds the motionPath with updated control points for a specific
segment. removeArcPath strips the motionPath property from the tween.
Add set-arc-path, update-arc-segment, remove-arc-path mutation types
to GsapMutationRequest union. Wire switch cases calling the parser
functions from U3. Add setArcPath, updateArcSegment, removeArcPath
wrappers to useGsapScriptCommits with soft reload enabled.
New ArcPathControls component with toggle, per-segment curviness sliders
(0-3 range, 0.1 step), auto-rotate toggle, and per-segment reset button.
Integrated into AnimationCard below property list, visible when animation
has keyframes. Full callback wiring through GsapAnimationSection →
PropertyPanel → StudioRightPanel → DomEditContext → useDomEditSession →
useGsapScriptCommits → API routes → parser AST mutations.
…ization

Create MotionPathOverlay component that renders curved SVG paths through
keyframe waypoints with anchor circles and control point handles. Supports
both curviness-auto-generated and explicit cubic bezier control points.
The overlay renders as an absolute-positioned SVG layer with pointer-events
for future drag interaction on control point handles.
Register MotionPathPlugin alongside CustomEase in the studio motion
render script when the plugin is available on window. In soft reload,
register the plugin before injecting the replacement IIFE script so
motionPath tweens are recognized by GSAP during timeline rebuild.
…lements

After binding the root timeline, iterate its children tweens and stamp
data-start and data-duration attributes on each target element that lacks
them. This allows the Studio timeline to discover individual animated
elements from raw GSAP compositions without requiring manual HTML
attributes. The duration is computed as the span from earliest to latest
tween touching each target.
…ration

Elements in the initial DOM are always visible — their tween start times
define when animations begin, not when the element appears. Set data-start
to 0 and data-duration to the root composition duration for all GSAP-targeted
elements that lack explicit timing attributes.
The runtime keyframe scanner now creates synthetic keyframe entries from
flat tl.to() calls, not just percentage keyframes. Each tween's start
position on the timeline becomes a diamond marker. Multiple tweens
targeting the same element accumulate into a single keyframe set. This
makes all GSAP-animated elements show their tween events on the timeline.
…thetic

Extract design tokens to panelTokens.ts. Unify accent color to #3B82F6
across all controls (replaces 4 inconsistent colors: emerald, teal, purple,
orange). Restyle primitives: inputs rounded-md (6px, was 12px), slider
tracks 2px (was 8px), toggles 28x16px with 150ms transitions, section
headers 10px uppercase with 15% opacity separators. Add tabular-nums to
numeric displays. Section content padding widened to 20px. Labels drop
uppercase (only section headers are uppercase). Focus ring brightened.
…panel

Section headers change from 10px uppercase with wide tracking to 12px
title-case bold (Figma UI3 pattern). Labels, tag badges, and color
picker sub-labels all drop uppercase and tracking — cleaner, more
readable, less "spreadsheet" feel.
Replace all blue accent (#3B82F6) references with teal (#2DD4BF) across
the design panel: toggles, slider fills, ease curve, motion path overlay,
border radius corners, keyframe diamonds, fill checkbox, color picker
focus rings, animation card badges. Single teal accent throughout.
Add panel-* color namespace to tailwind.config.js (panel-bg, panel-input,
panel-surface, panel-hover, panel-border, panel-text-1 through panel-text-5,
panel-accent, panel-danger). Replace all hardcoded hex in Tailwind classes
with panel-* tokens. Keep panelTokens.ts minimal — only the values needed
for inline style={{}} in toggles. FIELD uses bg-panel-input, LABEL uses
text-panel-text-3, Section uses border-panel-border + text-panel-text-1.
Drop the rounded-xl border bg-neutral-900/60 card wrapper from text
field rows. Content sits directly in the section — flat, no visual
container boxing. Matches the Figma-style flat section approach.
LayersPanel: bg-panel-bg, border-panel-border, text-panel-text-*,
selected state uses panel-accent, tag badges use panel-hover. RenderQueue
and RenderQueueItem: all neutral-* colors replaced with panel-* tokens.
Consistent dark instrument aesthetic across all three right panel tabs.
Render controls: compact rounded-md selects in a flow row (format, res,
fps, quality), full-width teal Export button below. Section header with
"Export" title and "Clear history" link.

Header: add teal "Export" button next to Inspector that switches to
the Renders tab and opens the right panel.
…plication

Remove "Export" section header (redundant with header button). Render
settings now a clean 2×2 grid: Format, Resolution, Frame rate, Quality —
each with a label above. Full-width Export button at bottom. Render items
use panel-accent for progress bar and download hover. Clear button
moves inline with the job list count.
Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@miguel-heygen miguel-heygen force-pushed the fix/keyframe-stability-border-radius branch from 84d75b7 to 60573ad Compare June 6, 2026 02:06
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.

1 participant