diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-13 04:41:33 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-13 05:08:29 +0200 |
| commit | 7d4e166678c35d48b22b8d1d05639f981a956a8b (patch) | |
| tree | eb596d4f4609053524224169c247db24052db353 /apps/web/src/core/managers/timeline-manager.ts | |
| parent | cad88ee4d1c9e06e7529b88ef5e5d3fdf3f89149 (diff) | |
| download | opencut-7d4e166678c35d48b22b8d1d05639f981a956a8b.tar.gz | |
fix: text measurement, rotation handle, video cache races, playback sync
Made-with: Cursor
Diffstat (limited to 'apps/web/src/core/managers/timeline-manager.ts')
| -rw-r--r-- | apps/web/src/core/managers/timeline-manager.ts | 60 |
1 files changed, 46 insertions, 14 deletions
diff --git a/apps/web/src/core/managers/timeline-manager.ts b/apps/web/src/core/managers/timeline-manager.ts index 74870a95..70075b98 100644 --- a/apps/web/src/core/managers/timeline-manager.ts +++ b/apps/web/src/core/managers/timeline-manager.ts @@ -5,7 +5,6 @@ import type { TrackType, TimelineTrack, TimelineElement, - ClipboardItem, RetimeConfig, } from "@/lib/timeline"; import { calculateTotalDuration } from "@/lib/timeline"; @@ -23,6 +22,11 @@ import type { AnimationValue, ScalarCurveKeyframePatch, } from "@/lib/animation/types"; +import { + getElementLocalTime, + resolveAnimationTarget, + resolveAnimationPathValueAtTime, +} from "@/lib/animation"; import { lastFrameTime } from "opencut-wasm"; import { BatchCommand } from "@/lib/commands"; import { @@ -35,7 +39,6 @@ import { DuplicateElementsCommand, UpdateElementsCommand, SplitElementsCommand, - PasteCommand, MoveElementCommand, TracksSnapshotCommand, UpsertKeyframeCommand, @@ -244,18 +247,6 @@ export class TimelineManager { return result; } - pasteAtTime({ - time, - clipboardItems, - }: { - time: number; - clipboardItems: ClipboardItem[]; - }): { trackId: string; elementId: string }[] { - const command = new PasteCommand({ time, clipboardItems }); - this.editor.command.execute({ command }); - return command.getPastedElements(); - } - deleteElements({ elements, }: { @@ -492,6 +483,45 @@ export class TimelineManager { return; } + // Pre-sample values at playhead for each (element, property) pair. + // This preserves "what you see is what you get" when all keyframes are deleted. + const playheadTime = this.editor.playback.getCurrentTime(); + const valueAtPlayheadMap = new Map<string, AnimationValue | null>(); + + for (const { trackId, elementId, propertyPath } of keyframes) { + const key = `${elementId}:${propertyPath}`; + if (valueAtPlayheadMap.has(key)) { + continue; + } + + const element = this.getElementByRef({ trackId, elementId }); + if (!element) { + valueAtPlayheadMap.set(key, null); + continue; + } + + const localTime = getElementLocalTime({ + timelineTime: playheadTime, + elementStartTime: element.startTime, + elementDuration: element.duration, + }); + + const target = resolveAnimationTarget({ element, path: propertyPath }); + const baseValue = target?.getBaseValue() ?? null; + if (baseValue === null) { + valueAtPlayheadMap.set(key, null); + continue; + } + + const value = resolveAnimationPathValueAtTime({ + animations: element.animations, + propertyPath, + localTime, + fallbackValue: baseValue, + }); + valueAtPlayheadMap.set(key, value); + } + const commands = keyframes.map( ({ trackId, elementId, propertyPath, keyframeId }) => new RemoveKeyframeCommand({ @@ -499,6 +529,8 @@ export class TimelineManager { elementId, propertyPath, keyframeId, + valueAtPlayhead: + valueAtPlayheadMap.get(`${elementId}:${propertyPath}`) ?? null, }), ); const command = |
