From eea6d43c82e1fd09d94d73001090dad205b1f1d2 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Sun, 26 Apr 2026 02:50:04 +0200 Subject: fix: model editor time as wasm MediaTime ticks end-to-end --- apps/web/src/components/editable-timecode.tsx | 16 ++++++++++------ .../components/editor/panels/assets/draggable-item.tsx | 3 ++- .../components/editor/panels/assets/views/assets.tsx | 6 +++--- .../panels/properties/hooks/use-element-playhead.ts | 17 ++++++++++------- .../properties/hooks/use-keyframed-color-property.ts | 3 ++- .../properties/hooks/use-keyframed-number-property.ts | 3 ++- .../properties/hooks/use-keyframed-param-property.ts | 3 ++- 7 files changed, 31 insertions(+), 20 deletions(-) (limited to 'apps/web/src/components') diff --git a/apps/web/src/components/editable-timecode.tsx b/apps/web/src/components/editable-timecode.tsx index 276cb490..8450cec2 100644 --- a/apps/web/src/components/editable-timecode.tsx +++ b/apps/web/src/components/editable-timecode.tsx @@ -3,13 +3,14 @@ import { useEffect, useRef, useState } from "react"; import { formatTimecode, parseTimecode, snappedSeekTime, type FrameRate, type TimeCodeFormat } from "opencut-wasm"; import { cn } from "@/utils/ui"; +import type { MediaTime } from "@/wasm"; interface EditableTimecodeProps { - time: number; - duration: number; + time: MediaTime; + duration: MediaTime; format?: TimeCodeFormat; fps: FrameRate; - onTimeChange?: ({ time }: { time: number }) => void; + onTimeChange?: ({ time }: { time: MediaTime }) => void; className?: string; disabled?: boolean; } @@ -53,9 +54,12 @@ export function EditableTimecode({ return; } - const clampedTime = duration - ? (snappedSeekTime({ time: parsedTime, duration, rate: fps }) ?? parsedTime) - : parsedTime; + const clampedTime = ( + duration + ? (snappedSeekTime({ time: parsedTime, duration, rate: fps }) ?? + parsedTime) + : parsedTime + ) as MediaTime; onTimeChange?.({ time: clampedTime }); setIsEditing(false); diff --git a/apps/web/src/components/editor/panels/assets/draggable-item.tsx b/apps/web/src/components/editor/panels/assets/draggable-item.tsx index 2f8bc64a..fdbf5e76 100644 --- a/apps/web/src/components/editor/panels/assets/draggable-item.tsx +++ b/apps/web/src/components/editor/panels/assets/draggable-item.tsx @@ -14,13 +14,14 @@ import { useEditor } from "@/editor/use-editor"; import { clearDragData, setDragData } from "@/timeline/drag-data"; import type { TimelineDragData } from "@/timeline/drag"; import { cn } from "@/utils/ui"; +import type { MediaTime } from "@/wasm"; export interface DraggableItemProps { name: string; preview: ReactNode; dragData: TimelineDragData; onDragStart?: ({ e }: { e: React.DragEvent }) => void; - onAddToTimeline?: ({ currentTime }: { currentTime: number }) => void; + onAddToTimeline?: ({ currentTime }: { currentTime: MediaTime }) => void; aspectRatio?: number; className?: string; containerClassName?: string; diff --git a/apps/web/src/components/editor/panels/assets/views/assets.tsx b/apps/web/src/components/editor/panels/assets/views/assets.tsx index 4afd22b7..c071fdc7 100644 --- a/apps/web/src/components/editor/panels/assets/views/assets.tsx +++ b/apps/web/src/components/editor/panels/assets/views/assets.tsx @@ -26,7 +26,7 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { DEFAULT_NEW_ELEMENT_DURATION } from "@/timeline/creation"; -import { TICKS_PER_SECOND } from "@/wasm"; +import { mediaTimeFromSeconds, type MediaTime } from "@/wasm"; import { useEditor } from "@/editor/use-editor"; import { useFileUpload } from "@/media/use-file-upload"; import { invokeAction } from "@/actions"; @@ -255,11 +255,11 @@ function MediaAssetDraggable({ startTime, }: { asset: MediaAsset; - startTime: number; + startTime: MediaTime; }) => { const duration = asset.duration != null - ? Math.round(asset.duration * TICKS_PER_SECOND) + ? mediaTimeFromSeconds({ seconds: asset.duration }) : DEFAULT_NEW_ELEMENT_DURATION; const element = buildElementFromMedia({ mediaId: asset.id, diff --git a/apps/web/src/components/editor/panels/properties/hooks/use-element-playhead.ts b/apps/web/src/components/editor/panels/properties/hooks/use-element-playhead.ts index 967156b3..83e31aec 100644 --- a/apps/web/src/components/editor/panels/properties/hooks/use-element-playhead.ts +++ b/apps/web/src/components/editor/panels/properties/hooks/use-element-playhead.ts @@ -1,22 +1,25 @@ import { useEditor } from "@/editor/use-editor"; import { getElementLocalTime } from "@/animation"; +import { addMediaTime, mediaTime, type MediaTime } from "@/wasm"; export function useElementPlayhead({ startTime, duration, }: { - startTime: number; - duration: number; + startTime: MediaTime; + duration: MediaTime; }) { const playheadTime = useEditor((editor) => editor.playback.getCurrentTime()); - const localTime = getElementLocalTime({ - timelineTime: playheadTime, - elementStartTime: startTime, - elementDuration: duration, + const localTime = mediaTime({ + ticks: getElementLocalTime({ + timelineTime: playheadTime, + elementStartTime: startTime, + elementDuration: duration, + }), }); const isPlayheadWithinElementRange = playheadTime >= startTime && - playheadTime <= startTime + duration; + playheadTime <= addMediaTime({ a: startTime, b: duration }); return { localTime, isPlayheadWithinElementRange }; } diff --git a/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-color-property.ts b/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-color-property.ts index 734b514f..f6216208 100644 --- a/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-color-property.ts +++ b/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-color-property.ts @@ -6,6 +6,7 @@ import { } from "@/animation"; import type { AnimationPropertyPath, ElementAnimations } from "@/animation/types"; import type { TimelineElement } from "@/timeline"; +import type { MediaTime } from "@/wasm"; export function useKeyframedColorProperty({ trackId, @@ -21,7 +22,7 @@ export function useKeyframedColorProperty({ elementId: string; animations: ElementAnimations | undefined; propertyPath: AnimationPropertyPath; - localTime: number; + localTime: MediaTime; isPlayheadWithinElementRange: boolean; resolvedColor: string; buildBaseUpdates: ({ value }: { value: string }) => Partial; diff --git a/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-number-property.ts b/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-number-property.ts index 9dd39768..d52f7d97 100644 --- a/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-number-property.ts +++ b/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-number-property.ts @@ -8,6 +8,7 @@ import type { AnimationPropertyPath, ElementAnimations } from "@/animation/types import type { TimelineElement } from "@/timeline"; import { snapToStep } from "@/utils/math"; import { usePropertyDraft } from "./use-property-draft"; +import type { MediaTime } from "@/wasm"; export function useKeyframedNumberProperty({ trackId, @@ -27,7 +28,7 @@ export function useKeyframedNumberProperty({ elementId: string; animations: ElementAnimations | undefined; propertyPath: AnimationPropertyPath; - localTime: number; + localTime: MediaTime; isPlayheadWithinElementRange: boolean; displayValue: string; parse: (input: string) => number | null; diff --git a/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-param-property.ts b/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-param-property.ts index b0b6f01a..e679f5c3 100644 --- a/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-param-property.ts +++ b/apps/web/src/components/editor/panels/properties/hooks/use-keyframed-param-property.ts @@ -15,6 +15,7 @@ import type { } from "@/animation/types"; import type { ParamDefinition } from "@/params"; import type { TimelineElement } from "@/timeline"; +import type { MediaTime } from "@/wasm"; export interface KeyframedParamPropertyResult { hasAnimatedKeyframes: boolean; @@ -39,7 +40,7 @@ export function useKeyframedParamProperty({ trackId: string; elementId: string; animations: ElementAnimations | undefined; - localTime: number; + localTime: MediaTime; isPlayheadWithinElementRange: boolean; resolvedValue: number | string | boolean; buildBaseUpdates: ({ -- cgit v1.3.1