feat(studio): keyframe system — parser, runtime, timeline UI, design panel, gesture recording (#1311)

* feat(studio): runtime hooks — global time compiler + keyframe runtime

Add the runtime bridge layer: global time compilation (tween % → clip %),
soft reload after mutations, runtime keyframe preview, and keyframe
commit helper.

* feat(studio): runtime hooks — global time compiler + keyframe runtime

Add the runtime bridge layer: global time compilation (tween % → clip %),
soft reload after mutations, runtime keyframe preview, and keyframe
commit helper.

* feat(studio): keyframe cache + commit hooks

Add hooks for keyframe cache population (tween → clip-relative %),
mutation dispatch, keyframe snapping, and audio beat detection.

* feat(studio): timeline UI — dopesheet diamonds + keyboard nav

Add dopesheet strip with diamond keyframe indicators, timeline property
rows, keyboard navigation (J/Shift+J/Delete/K), and feature gate
(STUDIO_KEYFRAMES_ENABLED defaults to false).

* feat(studio): design panel — arc controls + ease curve + stagger

Add arc path controls (curviness slider, auto-rotate), motion path SVG
overlay, ease curve visualization, stagger controls, and expanded
animation card. Includes border-radius editor dependency from #1217.

* feat(studio): gesture recording core

Add gesture recording engine with RAF sampling, modifier key property
mapping (Shift→rotationXY, Alt→rotation, Cmd→opacity),
Ramer-Douglas-Peucker simplification, and ghost trail SVG overlay.

* fix(studio): keyframe drag + recording bug bash

21 fixes: capture GSAP base at drag start, translate:none before
gsap.set, skip reapplyPathOffsets for GSAP elements, clamp recording
seek, _auto flag for 100% keyframes, overlay flash fix, block edits
during recording.

* feat(studio): keyframe integration wiring + docs

Wire App.tsx recording orchestration, TimelineToolbar K/R buttons,
PropertyPanel per-property diamonds, shortcuts panel, toast
notifications, and keyframes guide documentation. All gated on
STUDIO_KEYFRAMES_ENABLED (default false).
This commit is contained in:
Miguel Ángel
2026-06-09 18:30:23 -04:00
committed by GitHub
parent 96b8d617d8
commit a468550f82
72 changed files with 4421 additions and 621 deletions
@@ -67,13 +67,17 @@ export function DomEditProvider({
handleGsapAddFromProperty,
handleGsapRemoveFromProperty,
handleGsapAddKeyframe,
handleGsapAddKeyframeBatch,
handleGsapRemoveKeyframe,
handleGsapConvertToKeyframes,
handleGsapRemoveAllKeyframes,
handleResetSelectedElementKeyframes,
commitAnimatedProperty,
handleSetArcPath,
handleUpdateArcSegment,
invalidateGsapCache,
previewIframeRef,
commitMutation,
},
children,
}: {
@@ -136,13 +140,17 @@ export function DomEditProvider({
handleGsapAddFromProperty,
handleGsapRemoveFromProperty,
handleGsapAddKeyframe,
handleGsapAddKeyframeBatch,
handleGsapRemoveKeyframe,
handleGsapConvertToKeyframes,
handleGsapRemoveAllKeyframes,
handleResetSelectedElementKeyframes,
commitAnimatedProperty,
handleSetArcPath,
handleUpdateArcSegment,
invalidateGsapCache,
previewIframeRef,
commitMutation,
}),
[
domEditSelection,
@@ -199,13 +207,17 @@ export function DomEditProvider({
handleGsapAddFromProperty,
handleGsapRemoveFromProperty,
handleGsapAddKeyframe,
handleGsapAddKeyframeBatch,
handleGsapRemoveKeyframe,
handleGsapConvertToKeyframes,
handleGsapRemoveAllKeyframes,
handleResetSelectedElementKeyframes,
commitAnimatedProperty,
handleSetArcPath,
handleUpdateArcSegment,
invalidateGsapCache,
previewIframeRef,
commitMutation,
],
);
return <DomEditContext value={stable}>{children}</DomEditContext>;
@@ -26,6 +26,7 @@ export function FileManagerProvider({
readProjectFile,
writeProjectFile,
readOptionalProjectFile,
updateEditingFileContent,
revealSourceOffset,
openSourceForSelection,
handleFileSelect,
@@ -64,6 +65,7 @@ export function FileManagerProvider({
readProjectFile,
writeProjectFile,
readOptionalProjectFile,
updateEditingFileContent,
revealSourceOffset,
openSourceForSelection,
handleFileSelect,
@@ -96,6 +98,7 @@ export function FileManagerProvider({
readProjectFile,
writeProjectFile,
readOptionalProjectFile,
updateEditingFileContent,
revealSourceOffset,
openSourceForSelection,
handleFileSelect,
@@ -12,7 +12,6 @@ export interface StudioContextValue {
compositionLoading: boolean;
refreshKey: number;
setRefreshKey: React.Dispatch<React.SetStateAction<number>>;
currentTime: number;
timelineElements: TimelineElement[];
isPlaying: boolean;
editHistory: {
@@ -63,7 +62,6 @@ export function StudioProvider({
compositionLoading,
refreshKey,
setRefreshKey,
currentTime,
timelineElements,
isPlaying,
editHistory,
@@ -89,7 +87,6 @@ export function StudioProvider({
compositionLoading,
refreshKey,
setRefreshKey,
currentTime,
timelineElements,
isPlaying,
editHistory,
@@ -112,7 +109,6 @@ export function StudioProvider({
captionEditMode,
compositionLoading,
refreshKey,
currentTime,
isPlaying,
compositionDimensions,
timelineVisible,