Summary
The keyframe context menu's "Move to Playhead" action (added in #1784, which restored gesture retiming for #1782 — thanks!) is unusable by gesture. Right-clicking a keyframe diamond first moves the playhead onto that keyframe; by the time the user clicks "Move to Playhead", from% == to%, so the move hits the no-op epsilon guard and nothing happens.
The companion gesture, drag-to-retime, works correctly and preserves value + ease — only the menu path is broken.
Repro
- Composition with a non-grouped element and a keyframed tween that has an intermediate eased keyframe, e.g.
tl.to("#box", { keyframes: { "0%": { scale: 1 }, "60%": { scale: 1.18, ease: "power2.out" }, "100%": { scale: 1 } }, duration: 4 });
- Move the playhead to an arbitrary position (e.g. ~25%).
- Right-click the 60% diamond → context menu appears with "Move to Playhead".
- Click "Move to Playhead".
Expected: the 60% keyframe moves to the playhead position (~25%), keeping its value + ease.
Actual: nothing happens. The playhead has already jumped to 60% on right-click, so the move is 60% → 60% = no-op.
Mechanism
In the timeline keyframe-diamond component (TimelineClipDiamonds), onPointerDown guards drag with if (e.button !== 0) return;, but onPointerUp calls onClickKeyframe(kf.percentage) (select + seek) for any non-drag pointerup, with no button guard. A right-click therefore seeks the playhead to the keyframe before onContextMenu opens the menu. The subsequent move-keyframe mutation then short-circuits on the no-op epsilon guard (MOVE_NOOP_EPSILON_PCT).
(Confirmed in the served Studio bundle dist/studio/assets/index-jbPe1Dih.js and the readable build.)
Suggested fix
Guard the seek in onPointerUp to the primary button only (if (e.button !== 0) return; before calling onClickKeyframe), so a right-click opens the menu without re-seeking. onContextMenu already handles right-click separately. There is currently no keyboard shortcut or alternative entry point for "Move to Playhead", so this menu path is the only way to reach it.
Environment
Summary
The keyframe context menu's "Move to Playhead" action (added in #1784, which restored gesture retiming for #1782 — thanks!) is unusable by gesture. Right-clicking a keyframe diamond first moves the playhead onto that keyframe; by the time the user clicks "Move to Playhead",
from% == to%, so the move hits the no-op epsilon guard and nothing happens.The companion gesture, drag-to-retime, works correctly and preserves value + ease — only the menu path is broken.
Repro
Expected: the 60% keyframe moves to the playhead position (~25%), keeping its value + ease.
Actual: nothing happens. The playhead has already jumped to 60% on right-click, so the move is 60% → 60% = no-op.
Mechanism
In the timeline keyframe-diamond component (
TimelineClipDiamonds),onPointerDownguards drag withif (e.button !== 0) return;, butonPointerUpcallsonClickKeyframe(kf.percentage)(select + seek) for any non-drag pointerup, with no button guard. A right-click therefore seeks the playhead to the keyframe beforeonContextMenuopens the menu. The subsequentmove-keyframemutation then short-circuits on the no-op epsilon guard (MOVE_NOOP_EPSILON_PCT).(Confirmed in the served Studio bundle
dist/studio/assets/index-jbPe1Dih.jsand the readable build.)Suggested fix
Guard the seek in
onPointerUpto the primary button only (if (e.button !== 0) return;before callingonClickKeyframe), so a right-click opens the menu without re-seeking.onContextMenualready handles right-click separately. There is currently no keyboard shortcut or alternative entry point for "Move to Playhead", so this menu path is the only way to reach it.Environment