From 7117f17ecec585511a076b42ff5d6af4ddbb8e96 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Sun, 18 Jan 2026 09:16:47 +0100 Subject: [PATCH] new utility, less duplicated code --- .cursor/rules/codebase-index.mdc | 9 +++++++++ .../hooks/timeline/use-timeline-interactions.ts | 8 ++++++-- .../src/hooks/timeline/use-timeline-playhead.ts | 4 ++-- apps/web/src/lib/time-utils.ts | 16 ++++++++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/.cursor/rules/codebase-index.mdc b/.cursor/rules/codebase-index.mdc index 5ac52ec1..02187b9e 100644 --- a/.cursor/rules/codebase-index.mdc +++ b/.cursor/rules/codebase-index.mdc @@ -619,6 +619,15 @@ time-utils.ts time: number; fps: number; }): number + export function getSnappedSeekTime({ + rawTime, + duration, + fps, + }: { + rawTime: number; + duration: number; + fps: number; + }): number transcription-utils.ts export function isTranscriptionConfigured() diff --git a/apps/web/src/hooks/timeline/use-timeline-interactions.ts b/apps/web/src/hooks/timeline/use-timeline-interactions.ts index d89d20a9..12d72827 100644 --- a/apps/web/src/hooks/timeline/use-timeline-interactions.ts +++ b/apps/web/src/hooks/timeline/use-timeline-interactions.ts @@ -1,7 +1,7 @@ import { useCallback, useRef } from "react"; import type { MutableRefObject, RefObject } from "react"; import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; -import { snapTimeToFrame } from "@/lib/time-utils"; +import { getSnappedSeekTime } from "@/lib/time-utils"; import { useEditor } from "../use-editor"; interface UseTimelineInteractionsProps { @@ -139,7 +139,11 @@ export function useTimelineInteractions({ ); const projectFps = activeProject?.settings.fps || 30; - const time = snapTimeToFrame({ time: rawTime, fps: projectFps }); + const time = getSnappedSeekTime({ + rawTime, + duration, + fps: projectFps, + }); seek(time); }, [ diff --git a/apps/web/src/hooks/timeline/use-timeline-playhead.ts b/apps/web/src/hooks/timeline/use-timeline-playhead.ts index 91cb012b..302f4bd0 100644 --- a/apps/web/src/hooks/timeline/use-timeline-playhead.ts +++ b/apps/web/src/hooks/timeline/use-timeline-playhead.ts @@ -1,4 +1,4 @@ -import { snapTimeToFrame } from "@/lib/time-utils"; +import { getSnappedSeekTime } from "@/lib/time-utils"; import { useState, useEffect, useCallback, useRef } from "react"; import { useEdgeAutoScroll } from "@/hooks/timeline/use-edge-auto-scroll"; import { useEditor } from "../use-editor"; @@ -89,7 +89,7 @@ export function useTimelinePlayhead({ ); // use frame snapping for playhead scrubbing const fps = activeProject.settings.fps; - const time = snapTimeToFrame({ time: rawTime, fps }); + const time = getSnappedSeekTime({ rawTime, duration, fps }); setScrubTime(time); seek(time); // update video preview in real time diff --git a/apps/web/src/lib/time-utils.ts b/apps/web/src/lib/time-utils.ts index 01f1e537..1f7ce6f5 100644 --- a/apps/web/src/lib/time-utils.ts +++ b/apps/web/src/lib/time-utils.ts @@ -182,3 +182,19 @@ export function snapTimeToFrame({ const frame = timeToFrame({ time, fps }); return frameToTime({ frame, fps }); } + +export function getSnappedSeekTime({ + rawTime, + duration, + fps, +}: { + rawTime: number; + duration: number; + fps: number; +}): number { + const snappedTime = snapTimeToFrame({ time: rawTime, fps }); + const frameOffset = fps > 0 ? 1 / fps : 0; + const maxSeekTime = + frameOffset > 0 ? Math.max(0, duration - frameOffset) : duration; + return Math.max(0, Math.min(maxSeekTime, snappedTime)); +} \ No newline at end of file