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 --- .../commands/timeline/clipboard/paste-keyframes.ts | 24 ++++++++---- apps/web/src/commands/timeline/clipboard/paste.ts | 45 ++++++++++++++++------ 2 files changed, 51 insertions(+), 18 deletions(-) (limited to 'apps/web/src/commands/timeline/clipboard') diff --git a/apps/web/src/commands/timeline/clipboard/paste-keyframes.ts b/apps/web/src/commands/timeline/clipboard/paste-keyframes.ts index e05b49fa..c1127921 100644 --- a/apps/web/src/commands/timeline/clipboard/paste-keyframes.ts +++ b/apps/web/src/commands/timeline/clipboard/paste-keyframes.ts @@ -10,6 +10,13 @@ import type { KeyframeClipboardItem } from "@/clipboard"; import type { SceneTracks, TimelineElement } from "@/timeline"; import { updateElementInSceneTracks } from "@/timeline"; import { generateUUID } from "@/utils/id"; +import { + addMediaTime, + type MediaTime, + maxMediaTime, + minMediaTime, + ZERO_MEDIA_TIME, +} from "@/wasm"; function pasteKeyframesIntoElement({ element, @@ -17,7 +24,7 @@ function pasteKeyframesIntoElement({ clipboardItems, }: { element: TimelineElement; - time: number; + time: MediaTime; clipboardItems: KeyframeClipboardItem[]; }): TimelineElement { let nextElement = element; @@ -31,10 +38,13 @@ function pasteKeyframesIntoElement({ continue; } - const keyframeTime = Math.max( - 0, - Math.min(time + item.timeOffset, nextElement.duration), - ); + const keyframeTime = maxMediaTime({ + a: ZERO_MEDIA_TIME, + b: minMediaTime({ + a: addMediaTime({ a: time, b: item.timeOffset }), + b: nextElement.duration, + }), + }); const nextAnimations = upsertPathKeyframe({ animations: nextElement.animations, propertyPath: item.propertyPath, @@ -79,7 +89,7 @@ export class PasteKeyframesCommand extends Command { private savedState: SceneTracks | null = null; private readonly trackId: string; private readonly elementId: string; - private readonly time: number; + private readonly time: MediaTime; private readonly clipboardItems: KeyframeClipboardItem[]; constructor({ @@ -90,7 +100,7 @@ export class PasteKeyframesCommand extends Command { }: { trackId: string; elementId: string; - time: number; + time: MediaTime; clipboardItems: KeyframeClipboardItem[]; }) { super(); diff --git a/apps/web/src/commands/timeline/clipboard/paste.ts b/apps/web/src/commands/timeline/clipboard/paste.ts index 4523566e..be0c2b91 100644 --- a/apps/web/src/commands/timeline/clipboard/paste.ts +++ b/apps/web/src/commands/timeline/clipboard/paste.ts @@ -13,18 +13,25 @@ import { enforceMainTrackStart, } from "@/timeline/placement"; import { cloneAnimations } from "@/animation"; +import { + addMediaTime, + type MediaTime, + maxMediaTime, + subMediaTime, + ZERO_MEDIA_TIME, +} from "@/wasm"; export class PasteCommand extends Command { private savedState: SceneTracks | null = null; private pastedElements: { trackId: string; elementId: string }[] = []; - private readonly time: number; + private readonly time: MediaTime; private readonly clipboardItems: ElementClipboardItem[]; constructor({ time, clipboardItems, }: { - time: number; + time: MediaTime; clipboardItems: ElementClipboardItem[]; }) { super(); @@ -39,8 +46,12 @@ export class PasteCommand extends Command { this.savedState = editor.scenes.getActiveScene().tracks; this.pastedElements = []; - const minStart = Math.min( - ...this.clipboardItems.map((item) => item.element.startTime), + const minStart = this.clipboardItems.reduce( + (earliestStartTime, item) => + item.element.startTime < earliestStartTime + ? item.element.startTime + : earliestStartTime, + this.clipboardItems[0].element.startTime, ); let updatedTracks = this.savedState; @@ -97,12 +108,18 @@ export class PasteCommand extends Command { targetTrackId: targetTrack.id, requestedStartTime: earliestElement.startTime, }); - const delta = adjustedEarliestStartTime - earliestElement.startTime; + const delta = subMediaTime({ + a: adjustedEarliestStartTime, + b: earliestElement.startTime, + }); - if (delta !== 0) { + if (delta !== ZERO_MEDIA_TIME) { elementsForPlacement = elementsToAdd.map((element) => ({ ...element, - startTime: Math.max(0, element.startTime + delta), + startTime: maxMediaTime({ + a: ZERO_MEDIA_TIME, + b: addMediaTime({ a: element.startTime, b: delta }), + }), })); } } @@ -168,14 +185,20 @@ function buildPastedElements({ time, }: { items: ElementClipboardItem[]; - minStart: number; - time: number; + minStart: MediaTime; + time: MediaTime; }): TimelineElement[] { const elementsToAdd: TimelineElement[] = []; for (const item of items) { - const relativeOffset = item.element.startTime - minStart; - const startTime = Math.max(0, time + relativeOffset); + const relativeOffset = subMediaTime({ + a: item.element.startTime, + b: minStart, + }); + const startTime = maxMediaTime({ + a: ZERO_MEDIA_TIME, + b: addMediaTime({ a: time, b: relativeOffset }), + }); const newElementId = generateUUID(); elementsToAdd.push({ -- cgit v1.3.1