mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-09 20:07:39 +00:00
fix(studio): keyframe cache propertyGroup tagging + timeline UI (#1357)
* fix(core): per-property-group keyframe foundations
Add PropertyGroupName type system (position/scale/size/rotation/visual/other),
PROPERTY_GROUPS constant, classifyPropertyGroup/classifyTweenPropertyGroup
functions. Parser generates group-aware animation IDs, resolves position strings
(+=, -=, <, >), uses numeric matching with 2% tolerance, and preserves IDs
across all mutations.
* fix(core): add split-into-property-groups and replace-with-keyframes mutations
Server-side mutations for atomic property-group splitting and keyframe
replacement. Client commitMutation returns early on changed:false instead
of throwing.
* fix(studio): per-property-group intercept routing + drag/resize fixes
Rewire GSAP runtime bridge for property-group routing: drag sends only {x,y}
to position group, resize routes to scale group via data-hf-studio-original-width,
rotation routes to rotation group. Add resolveGroupTween helper, from-extend
with split-first-then-position-only pattern, autoKeyframeEnabled guards,
GSAP base + delta fix in drag draft, cancel-restores-GSAP-x/y from data attrs.
* fix(studio): keyframe cache propertyGroup tagging + timeline UI fixes
Tag cached keyframes with propertyGroup for group-aware operations.
Add tweenPercentage for accurate keyframe matching, activeKeyframePct
for diamond-click targeting, context menu offset, selected diamond z-index,
clearProps after kill in soft reload.
This commit is contained in:
@@ -7,6 +7,7 @@ export interface KeyframeDiamondContextMenuState {
|
||||
y: number;
|
||||
elementId: string;
|
||||
percentage: number;
|
||||
tweenPercentage?: number;
|
||||
currentEase?: string;
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ export const KeyframeDiamondContextMenu = memo(function KeyframeDiamondContextMe
|
||||
type="button"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-red-400 hover:bg-neutral-800 cursor-pointer text-left"
|
||||
onClick={() => {
|
||||
onDelete(state.elementId, state.percentage);
|
||||
onDelete(state.elementId, state.tweenPercentage ?? state.percentage);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -448,6 +448,9 @@ export const Timeline = memo(function Timeline({
|
||||
onSelectElement?.(el);
|
||||
const absTime = el.start + (pct / 100) * el.duration;
|
||||
onSeek?.(absTime);
|
||||
const kfData = keyframeCache?.get(elKey);
|
||||
const kf = kfData?.keyframes.find((k) => Math.abs(k.percentage - pct) < 0.5);
|
||||
usePlayerStore.getState().setActiveKeyframePct(kf?.tweenPercentage ?? null);
|
||||
}}
|
||||
onShiftClickKeyframe={(elId, pct) => {
|
||||
toggleSelectedKeyframe(`${elId}:${pct}`);
|
||||
@@ -464,12 +467,13 @@ export const Timeline = memo(function Timeline({
|
||||
onSeek?.(absTime);
|
||||
}
|
||||
const kfData = keyframeCache.get(elId);
|
||||
const kf = kfData?.keyframes.find((k) => k.percentage === pct);
|
||||
const kf = kfData?.keyframes.find((k) => Math.abs(k.percentage - pct) < 0.2);
|
||||
setKfContextMenu({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
x: e.clientX + 4,
|
||||
y: e.clientY + 2,
|
||||
elementId: elId,
|
||||
percentage: pct,
|
||||
tweenPercentage: kf?.tweenPercentage,
|
||||
currentEase: kf?.ease ?? kfData?.ease,
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -123,7 +123,8 @@ export const TimelineClipDiamonds = memo(function TimelineClipDiamonds({
|
||||
const kfKey = `${elementId}:${kf.percentage}`;
|
||||
const isKfSelected = selectedKeyframes.has(kfKey);
|
||||
const atPlayhead = isSelected && Math.abs(kf.percentage - currentPercentage) < 0.5;
|
||||
const color = isKfSelected || atPlayhead ? accentColor : "#a3a3a3";
|
||||
const isHighlighted = isKfSelected || atPlayhead;
|
||||
const color = isHighlighted ? accentColor : "#a3a3a3";
|
||||
return (
|
||||
<button
|
||||
key={`${i}-${kf.percentage}`}
|
||||
@@ -135,6 +136,7 @@ export const TimelineClipDiamonds = memo(function TimelineClipDiamonds({
|
||||
transform: "translateY(-50%)",
|
||||
width: diamondSize,
|
||||
height: diamondSize,
|
||||
zIndex: isHighlighted ? 2 : 1,
|
||||
pointerEvents: "auto",
|
||||
background: "none",
|
||||
border: "none",
|
||||
|
||||
Reference in New Issue
Block a user