diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 304d10f..9cd467a 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -20,6 +20,7 @@ import { type GifSizePreset, VideoExporter, } from "@/lib/exporter"; +import { computeFrameStepTime } from "@/lib/frameStep"; import type { ProjectMedia } from "@/lib/recordingSession"; import { matchesShortcut } from "@/lib/shortcuts"; import { @@ -98,6 +99,10 @@ export default function VideoEditor() { const [isPlaying, setIsPlaying] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); + const currentTimeRef = useRef(currentTime); + currentTimeRef.current = currentTime; + const durationRef = useRef(duration); + durationRef.current = duration; const [cursorTelemetry, setCursorTelemetry] = useState([]); const [selectedZoomId, setSelectedZoomId] = useState(null); const [selectedTrimId, setSelectedTrimId] = useState(null); @@ -926,6 +931,29 @@ export default function VideoEditor() { return; } + // Frame-step navigation (arrow keys, no modifiers) + if ( + (e.key === "ArrowLeft" || e.key === "ArrowRight") && + !e.ctrlKey && + !e.metaKey && + !e.shiftKey && + !e.altKey + ) { + const target = e.target; + if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { + return; + } + e.preventDefault(); + const direction = e.key === "ArrowLeft" ? "backward" : "forward"; + const newTime = computeFrameStepTime( + currentTimeRef.current, + durationRef.current, + direction, + ); + handleSeek(newTime); + return; + } + const isInput = e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement; diff --git a/src/lib/__tests__/frameStepNavigation.test.ts b/src/lib/__tests__/frameStepNavigation.test.ts index 99d62fe..ab0393b 100644 --- a/src/lib/__tests__/frameStepNavigation.test.ts +++ b/src/lib/__tests__/frameStepNavigation.test.ts @@ -1,15 +1,6 @@ import { describe, expect, it } from "vitest"; -const FRAME_DURATION_SEC = 1 / 60; - -function computeFrameStepTime( - currentTime: number, - duration: number, - direction: "forward" | "backward", -): number { - const delta = direction === "forward" ? FRAME_DURATION_SEC : -FRAME_DURATION_SEC; - return Math.min(duration, Math.max(0, currentTime + delta)); -} +import { computeFrameStepTime, FRAME_DURATION_SEC } from "@/lib/frameStep"; describe("computeFrameStepTime", () => { const duration = 10; diff --git a/src/lib/frameStep.ts b/src/lib/frameStep.ts new file mode 100644 index 0000000..7eaaf6b --- /dev/null +++ b/src/lib/frameStep.ts @@ -0,0 +1,10 @@ +export const FRAME_DURATION_SEC = 1 / 60; + +export function computeFrameStepTime( + currentTime: number, + duration: number, + direction: "forward" | "backward", +): number { + const delta = direction === "forward" ? FRAME_DURATION_SEC : -FRAME_DURATION_SEC; + return Math.min(duration, Math.max(0, currentTime + delta)); +}