{
- const target = keyframeTarget(kf, groupAware);
+ {markers.map((marker, i) => {
+ const kf = marker.keyframe;
+ const target = keyframeTarget(kf);
const kfKey = timelineKeyframeSelectionKey(elementId, target);
// While dragging this diamond, render it at the live preview clip-%.
const renderPct = preview?.kfKey === kfKey ? preview.clipPct : kf.percentage;
@@ -344,7 +356,6 @@ export const TimelineDiamondLane = memo(function TimelineDiamondLane({
// The 0% diamond's left half lands in the reserved left gutter (the
// content origin is inset past the label column, Figma-style) so it stays
// fully visible instead of being clipped by the sticky label column.
- const marker = markerMetrics[i]!;
const leftPx = (renderPct / 100) * clipWidthPx - marker.hitWidth / 2;
const isKfSelected = selectedKeyframes.has(kfKey);
const atPlayhead = isSelected && Math.abs(kf.percentage - currentPercentage) < 0.5;
@@ -565,12 +576,10 @@ export const TimelineClipDiamonds = memo(function TimelineClipDiamonds(
props.onClickKeyframe?.(target.percentage)}
- onShiftClickKeyframe={(target) =>
- props.onShiftClickKeyframe?.(props.elementId, target.percentage)
- }
+ onClickKeyframe={(target) => props.onClickKeyframe?.(props.elementId, target)}
+ onShiftClickKeyframe={(target) => props.onShiftClickKeyframe?.(props.elementId, target)}
onContextMenuKeyframe={(e, target) =>
- props.onContextMenuKeyframe?.(e, props.elementId, target.percentage)
+ props.onContextMenuKeyframe?.(e, props.elementId, target)
}
onMoveKeyframe={
props.onMoveKeyframe
diff --git a/packages/studio/src/player/components/TimelineLanes.tsx b/packages/studio/src/player/components/TimelineLanes.tsx
index 8437bb64e..4ee3afc53 100644
--- a/packages/studio/src/player/components/TimelineLanes.tsx
+++ b/packages/studio/src/player/components/TimelineLanes.tsx
@@ -72,9 +72,13 @@ export interface TimelineLaneBaseProps {
keyframeCache?: Map;
selectedKeyframes: Set;
currentTime: number;
- onClickKeyframe?: (element: TimelineElement, percentage: number) => void;
- onShiftClickKeyframe?: (elementId: string, percentage: number) => void;
- onContextMenuKeyframe?: (e: React.MouseEvent, elementId: string, percentage: number) => void;
+ onClickKeyframe?: (element: TimelineElement, keyframe: TimelineKeyframeTarget) => void;
+ onShiftClickKeyframe?: (elementId: string, keyframe: TimelineKeyframeTarget) => void;
+ onContextMenuKeyframe?: (
+ e: React.MouseEvent,
+ elementId: string,
+ keyframe: TimelineKeyframeTarget,
+ ) => void;
onMoveKeyframe?: (
elementId: string,
keyframe: TimelineKeyframeTarget,
@@ -474,7 +478,9 @@ export function TimelineLanes({
}
elementId={elementKey}
selectedKeyframes={selectedKeyframes}
- onClickKeyframe={(pct) => onClickKeyframe?.(previewElement, pct)}
+ onClickKeyframe={(_elId, keyframe) =>
+ onClickKeyframe?.(previewElement, keyframe)
+ }
onShiftClickKeyframe={onShiftClickKeyframe}
onContextMenuKeyframe={onContextMenuKeyframe}
onMoveKeyframe={onMoveKeyframe}
diff --git a/packages/studio/src/player/components/TimelinePropertyLanes.test.tsx b/packages/studio/src/player/components/TimelinePropertyLanes.test.tsx
index 200f25d8c..387d84567 100644
--- a/packages/studio/src/player/components/TimelinePropertyLanes.test.tsx
+++ b/packages/studio/src/player/components/TimelinePropertyLanes.test.tsx
@@ -424,7 +424,10 @@ describe("TimelinePropertyLanes", () => {
act(() => {
diamonds[1]?.dispatchEvent(new MouseEvent("pointerup", { bubbles: true, button: 0 }));
});
- expect(onClickKeyframe).toHaveBeenCalledWith(50);
+ expect(onClickKeyframe).toHaveBeenCalledWith(
+ "clip-1",
+ expect.objectContaining({ percentage: 50 }),
+ );
act(() => root.unmount());
});
});