mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
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:
@@ -4,7 +4,11 @@ type IframeWindow = Window & {
|
||||
__hfForceTimelineRebind?: () => void;
|
||||
__hfSuppressSceneMutations?: <T>(fn: () => T) => T;
|
||||
__hfStudioManualEditsApply?: () => void;
|
||||
gsap?: { timeline?: (...args: unknown[]) => unknown };
|
||||
gsap?: {
|
||||
timeline?: (...args: unknown[]) => unknown;
|
||||
registerPlugin?: (...plugins: unknown[]) => unknown;
|
||||
};
|
||||
MotionPathPlugin?: unknown;
|
||||
};
|
||||
|
||||
function isGsapScript(text: string): boolean {
|
||||
@@ -64,16 +68,32 @@ export function applySoftReload(iframe: HTMLIFrameElement | null, scriptText: st
|
||||
}
|
||||
|
||||
oldScriptEl.remove();
|
||||
const newScript = doc.createElement("script");
|
||||
// IIFE prevents const/let redeclaration errors across consecutive edits.
|
||||
// Top-level declarations are scoped to the IIFE; window.* assignments
|
||||
// (e.g. window.__timelines["root"] = tl) still reach the global scope.
|
||||
newScript.textContent = `(function(){${scriptText}\n})();`;
|
||||
doc.body.appendChild(newScript);
|
||||
|
||||
win.__hfForceTimelineRebind?.();
|
||||
win.__player?.seek?.(currentTime);
|
||||
win.__hfStudioManualEditsApply?.();
|
||||
const executeScript = () => {
|
||||
if (win.MotionPathPlugin && win.gsap?.registerPlugin) {
|
||||
win.gsap.registerPlugin(win.MotionPathPlugin);
|
||||
}
|
||||
const s = doc.createElement("script");
|
||||
s.textContent = `(function(){${scriptText}\n})();`;
|
||||
doc.body.appendChild(s);
|
||||
win.__hfForceTimelineRebind?.();
|
||||
win.__player?.seek?.(currentTime);
|
||||
win.__hfStudioManualEditsApply?.();
|
||||
};
|
||||
|
||||
// Load MotionPathPlugin on demand if the script uses motionPath.
|
||||
// Uses the same CDN as composition templates (GSAP_CDN in constants.ts).
|
||||
const needsMotionPath = /motionPath\s*[:{]/.test(scriptText);
|
||||
if (needsMotionPath && !win.MotionPathPlugin && win.gsap) {
|
||||
const pluginScript = doc.createElement("script");
|
||||
pluginScript.src = "https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/MotionPathPlugin.min.js";
|
||||
pluginScript.onload = () => executeScript();
|
||||
pluginScript.onerror = () => executeScript();
|
||||
doc.head.appendChild(pluginScript);
|
||||
return;
|
||||
}
|
||||
|
||||
executeScript();
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user