summaryrefslogtreecommitdiff
path: root/apps/web/src/timeline/controllers/playhead-controller.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/timeline/controllers/playhead-controller.ts')
-rw-r--r--apps/web/src/timeline/controllers/playhead-controller.ts30
1 files changed, 17 insertions, 13 deletions
diff --git a/apps/web/src/timeline/controllers/playhead-controller.ts b/apps/web/src/timeline/controllers/playhead-controller.ts
index d2b7d82f..e914ff21 100644
--- a/apps/web/src/timeline/controllers/playhead-controller.ts
+++ b/apps/web/src/timeline/controllers/playhead-controller.ts
@@ -1,6 +1,11 @@
import type { MouseEvent as ReactMouseEvent } from "react";
-import { snappedSeekTime, type FrameRate } from "opencut-wasm";
-import { TICKS_PER_SECOND } from "@/wasm";
+import type { FrameRate } from "opencut-wasm";
+import {
+ mediaTime,
+ snapSeekMediaTime,
+ TICKS_PER_SECOND,
+ type MediaTime,
+} from "@/wasm";
import {
buildTimelineSnapPoints,
getTimelineSnapThresholdInTicks,
@@ -26,7 +31,7 @@ interface ScrubSession {
/** True once the mouse has moved during a ruler drag. */
hasMoved: boolean;
/** Most recent frame-snapped time set by scrub(). */
- currentTime: number | null;
+ currentTime: MediaTime | null;
}
type Session = { kind: "idle" } | ScrubSession;
@@ -35,7 +40,7 @@ type Session = { kind: "idle" } | ScrubSession;
export interface PlayheadConfig {
zoomLevel: number;
- duration: number;
+ duration: MediaTime;
getActiveProjectFps: () => FrameRate | null;
isShiftHeld: () => boolean;
getIsPlaying: () => boolean;
@@ -45,12 +50,12 @@ export interface PlayheadConfig {
getPlayheadEl: () => HTMLDivElement | null;
getSceneTracks: () => SceneTracks;
getSceneBookmarks: () => Bookmark[];
- seek: (time: number) => void;
+ seek: (time: MediaTime) => void;
setScrubbing: (isScrubbing: boolean) => void;
setTimelineViewState: (viewState: {
zoomLevel: number;
scrollLeft: number;
- playheadTime: number;
+ playheadTime: MediaTime;
}) => void;
}
@@ -69,8 +74,8 @@ function pixelToTime({
clientX: number;
rulerEl: HTMLDivElement;
zoomLevel: number;
- duration: number;
-}): number {
+ duration: MediaTime;
+}): MediaTime {
const rulerRect = rulerEl.getBoundingClientRect();
const contentWidth = timelineTimeToPixels({ time: duration, zoomLevel });
const clampedX = Math.max(
@@ -84,7 +89,7 @@ function pixelToTime({
clampedX / (BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel),
),
);
- return Math.round(seconds * TICKS_PER_SECOND);
+ return mediaTime({ ticks: Math.round(seconds * TICKS_PER_SECOND) });
}
// --- Controller ---
@@ -159,7 +164,7 @@ export class PlayheadController {
* Called on scroll and playback events to avoid React re-renders
* during animation frame updates.
*/
- updatePlayheadLeft(time: number): void {
+ updatePlayheadLeft(time: MediaTime): void {
const playheadEl = this.config.getPlayheadEl();
if (!playheadEl) return;
@@ -175,7 +180,7 @@ export class PlayheadController {
* Updates the playhead position and auto-scrolls to keep the playhead
* visible during playback.
*/
- handlePlaybackUpdate(time: number): void {
+ handlePlaybackUpdate(time: MediaTime): void {
this.updatePlayheadLeft(time);
// Auto-scroll only during playback, not while scrubbing.
@@ -244,8 +249,7 @@ export class PlayheadController {
zoomLevel,
duration,
});
- const frameTime =
- snappedSeekTime({ time: rawTime, duration, rate: fps }) ?? rawTime;
+ const frameTime = snapSeekMediaTime({ time: rawTime, duration, fps });
const time = (() => {
if (!isElementSnappingEnabled || this.config.isShiftHeld())