From f1b45b4328cff6a016720ce7b38b87ac2992dc2d Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 22 Apr 2026 14:36:51 +0200 Subject: [PATCH] chore: remove stale files --- .../lib/commands/timeline/clipboard/paste.ts | 193 ------------------ .../timeline/element/duplicate-elements.ts | 137 ------------- .../timeline/element/move-elements.ts | 187 ----------------- .../timeline/element/split-elements.ts | 182 ----------------- 4 files changed, 699 deletions(-) delete mode 100644 apps/web/src/lib/commands/timeline/clipboard/paste.ts delete mode 100644 apps/web/src/lib/commands/timeline/element/duplicate-elements.ts delete mode 100644 apps/web/src/lib/commands/timeline/element/move-elements.ts delete mode 100644 apps/web/src/lib/commands/timeline/element/split-elements.ts diff --git a/apps/web/src/lib/commands/timeline/clipboard/paste.ts b/apps/web/src/lib/commands/timeline/clipboard/paste.ts deleted file mode 100644 index 58199a94..00000000 --- a/apps/web/src/lib/commands/timeline/clipboard/paste.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { - Command, - createElementSelectionResult, - type CommandResult, -} from "@/lib/commands/base-command"; -import { EditorCore } from "@/core"; -import type { SceneTracks, TimelineElement } from "@/lib/timeline"; -import type { ElementClipboardItem } from "@/lib/clipboard"; -import { generateUUID } from "@/utils/id"; -import { - applyPlacement, - resolveTrackPlacement, - enforceMainTrackStart, -} from "@/lib/timeline/placement"; -import { cloneAnimations } from "@/lib/animation"; - -export class PasteCommand extends Command { - private savedState: SceneTracks | null = null; - private pastedElements: { trackId: string; elementId: string }[] = []; - private readonly time: number; - private readonly clipboardItems: ElementClipboardItem[]; - - constructor({ - time, - clipboardItems, - }: { - time: number; - clipboardItems: ElementClipboardItem[]; - }) { - super(); - this.time = time; - this.clipboardItems = clipboardItems; - } - - execute(): CommandResult | undefined { - if (this.clipboardItems.length === 0) return undefined; - - const editor = EditorCore.getInstance(); - this.savedState = editor.scenes.getActiveScene().tracks; - this.pastedElements = []; - - const minStart = Math.min( - ...this.clipboardItems.map((item) => item.element.startTime), - ); - - let updatedTracks = this.savedState; - const itemsByTrackId = groupClipboardItemsByTrackId({ - clipboardItems: this.clipboardItems, - }); - - for (const [trackId, items] of itemsByTrackId) { - const elementsToAdd = buildPastedElements({ - items, - minStart, - time: this.time, - }); - - if (elementsToAdd.length === 0) { - continue; - } - - const trackType = items[0].trackType; - const sourceTrackIndex = [ - ...updatedTracks.overlay, - updatedTracks.main, - ...updatedTracks.audio, - ].findIndex((track) => track.id === trackId); - const placementResult = resolveTrackPlacement({ - tracks: updatedTracks, - trackType, - timeSpans: elementsToAdd.map((element) => ({ - startTime: element.startTime, - duration: element.duration, - })), - strategy: { type: "aboveSource", sourceTrackIndex }, - }); - if (!placementResult) { - continue; - } - - let elementsForPlacement = elementsToAdd; - if (placementResult.kind === "existingTrack") { - const targetTrack = - placementResult.trackIndex < updatedTracks.overlay.length - ? updatedTracks.overlay[placementResult.trackIndex] - : placementResult.trackIndex === updatedTracks.overlay.length - ? updatedTracks.main - : updatedTracks.audio[ - placementResult.trackIndex - updatedTracks.overlay.length - 1 - ]; - if (targetTrack?.id === updatedTracks.main.id) { - const earliestElement = elementsToAdd.reduce((earliest, element) => - element.startTime < earliest.startTime ? element : earliest, - ); - const adjustedEarliestStartTime = enforceMainTrackStart({ - tracks: updatedTracks, - targetTrackId: targetTrack.id, - requestedStartTime: earliestElement.startTime, - }); - const delta = adjustedEarliestStartTime - earliestElement.startTime; - - if (delta !== 0) { - elementsForPlacement = elementsToAdd.map((element) => ({ - ...element, - startTime: Math.max(0, element.startTime + delta), - })); - } - } - } - - const applied = applyPlacement({ - tracks: updatedTracks, - placementResult, - elements: elementsForPlacement, - }); - if (!applied) { - continue; - } - - updatedTracks = applied.updatedTracks; - - for (const element of elementsForPlacement) { - this.pastedElements.push({ - trackId: applied.targetTrackId, - elementId: element.id, - }); - } - } - - editor.timeline.updateTracks(updatedTracks); - - if (this.pastedElements.length > 0) { - return createElementSelectionResult(this.pastedElements); - } - return undefined; - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } - - getPastedElements(): { trackId: string; elementId: string }[] { - return this.pastedElements; - } -} - -function groupClipboardItemsByTrackId({ - clipboardItems, -}: { - clipboardItems: ElementClipboardItem[]; -}): Map { - const groupedItems = new Map(); - - for (const item of clipboardItems) { - const existingItems = groupedItems.get(item.trackId) ?? []; - groupedItems.set(item.trackId, [...existingItems, item]); - } - - return groupedItems; -} - -function buildPastedElements({ - items, - minStart, - time, -}: { - items: ElementClipboardItem[]; - minStart: number; - time: number; -}): TimelineElement[] { - const elementsToAdd: TimelineElement[] = []; - - for (const item of items) { - const relativeOffset = item.element.startTime - minStart; - const startTime = Math.max(0, time + relativeOffset); - const newElementId = generateUUID(); - - elementsToAdd.push({ - ...item.element, - id: newElementId, - startTime, - animations: cloneAnimations({ - animations: item.element.animations, - shouldRegenerateKeyframeIds: true, - }), - } as TimelineElement); - } - - return elementsToAdd; -} diff --git a/apps/web/src/lib/commands/timeline/element/duplicate-elements.ts b/apps/web/src/lib/commands/timeline/element/duplicate-elements.ts deleted file mode 100644 index c044a4e3..00000000 --- a/apps/web/src/lib/commands/timeline/element/duplicate-elements.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { - Command, - createElementSelectionResult, - type CommandResult, -} from "@/lib/commands/base-command"; -import type { SceneTracks, TimelineElement } from "@/lib/timeline"; -import { generateUUID } from "@/utils/id"; -import { EditorCore } from "@/core"; -import { - applyPlacement, - resolveTrackPlacement, -} from "@/lib/timeline/placement"; -import { cloneAnimations } from "@/lib/animation"; - -interface DuplicateElementsParams { - elements: { trackId: string; elementId: string }[]; -} - -export class DuplicateElementsCommand extends Command { - private duplicatedElements: { trackId: string; elementId: string }[] = []; - private savedState: SceneTracks | null = null; - private elements: DuplicateElementsParams["elements"]; - - constructor({ elements }: DuplicateElementsParams) { - super(); - this.elements = elements; - } - - execute(): CommandResult | undefined { - const editor = EditorCore.getInstance(); - this.savedState = editor.scenes.getActiveScene().tracks; - this.duplicatedElements = []; - - let updatedTracks = this.savedState; - - for (const track of [ - ...this.savedState.overlay, - this.savedState.main, - ...this.savedState.audio, - ]) { - const elementsToDuplicate = this.elements.filter( - (elementEntry) => elementEntry.trackId === track.id, - ); - - if (elementsToDuplicate.length === 0) { - continue; - } - - const elementIdsToDuplicate = new Set( - elementsToDuplicate.map((element) => element.elementId), - ); - const newTrackElements: TimelineElement[] = []; - - for (const element of track.elements) { - if (!elementIdsToDuplicate.has(element.id)) { - continue; - } - - const newId = generateUUID(); - newTrackElements.push( - buildDuplicateElement({ - element, - id: newId, - startTime: element.startTime, - }), - ); - } - - const placementResult = resolveTrackPlacement({ - tracks: updatedTracks, - trackType: track.type, - timeSpans: [], - strategy: { type: "alwaysNew", position: "highest" }, - }); - if (!placementResult || placementResult.kind !== "newTrack") { - continue; - } - - const applied = applyPlacement({ - tracks: updatedTracks, - placementResult, - elements: newTrackElements, - }); - if (!applied) { - continue; - } - - updatedTracks = applied.updatedTracks; - - for (const element of newTrackElements) { - this.duplicatedElements.push({ - trackId: applied.targetTrackId, - elementId: element.id, - }); - } - } - - editor.timeline.updateTracks(updatedTracks); - - if (this.duplicatedElements.length > 0) { - return createElementSelectionResult(this.duplicatedElements); - } - return undefined; - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } - - getDuplicatedElements(): { trackId: string; elementId: string }[] { - return this.duplicatedElements; - } -} - -function buildDuplicateElement({ - element, - id, - startTime, -}: { - element: TimelineElement; - id: string; - startTime: number; -}): TimelineElement { - return { - ...element, - id, - name: `${element.name} (copy)`, - startTime, - animations: cloneAnimations({ - animations: element.animations, - shouldRegenerateKeyframeIds: true, - }), - }; -} diff --git a/apps/web/src/lib/commands/timeline/element/move-elements.ts b/apps/web/src/lib/commands/timeline/element/move-elements.ts deleted file mode 100644 index 782374d5..00000000 --- a/apps/web/src/lib/commands/timeline/element/move-elements.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { - Command, - createElementSelectionResult, - type CommandResult, -} from "@/lib/commands/base-command"; -import { EditorCore } from "@/core"; -import type { - SceneTracks, - TimelineElement, - TimelineTrack, -} from "@/lib/timeline"; -import { - buildEmptyTrack, - validateElementTrackCompatibility, -} from "@/lib/timeline/placement"; -import type { - PlannedElementMove, - PlannedTrackCreation, -} from "@/lib/timeline/group-move"; -import { findTrackInSceneTracks } from "@/lib/timeline/track-element-update"; - -export class MoveElementCommand extends Command { - private savedState: SceneTracks | null = null; - - constructor({ - moves, - createTracks = [], - }: { - moves: PlannedElementMove[]; - createTracks?: PlannedTrackCreation[]; - }) { - super(); - this.moves = moves; - this.createTracks = createTracks; - } - - private readonly moves: PlannedElementMove[]; - private readonly createTracks: PlannedTrackCreation[]; - - execute(): CommandResult | undefined { - const editor = EditorCore.getInstance(); - this.savedState = editor.scenes.getActiveScene().tracks; - - let tracksToUpdate = this.savedState; - for (const createTrack of [...this.createTracks].sort( - (firstTrack, secondTrack) => firstTrack.index - secondTrack.index, - )) { - tracksToUpdate = insertTrackAtDisplayIndex({ - tracks: tracksToUpdate, - track: buildEmptyTrack({ - id: createTrack.id, - type: createTrack.type, - }), - insertIndex: createTrack.index, - }); - } - - const movedElementsById = new Map(); - for (const move of this.moves) { - const sourceTrack = findTrackInSceneTracks({ - tracks: this.savedState, - trackId: move.sourceTrackId, - }); - const sourceElement = sourceTrack?.elements.find( - (trackElement) => trackElement.id === move.elementId, - ); - if (!sourceTrack || !sourceElement) { - throw new Error("Source track or element not found"); - } - - const targetTrack = findTrackInSceneTracks({ - tracks: tracksToUpdate, - trackId: move.targetTrackId, - }); - if (!targetTrack) { - throw new Error("Target track not found"); - } - - const validation = validateElementTrackCompatibility({ - element: sourceElement, - track: targetTrack, - }); - if (!validation.isValid) { - throw new Error(validation.errorMessage); - } - - movedElementsById.set(move.elementId, { - ...sourceElement, - startTime: move.newStartTime, - }); - } - - const movedElementIds = new Set(this.moves.map((move) => move.elementId)); - const movedElementsByTargetTrackId = new Map(); - for (const move of this.moves) { - const movedElement = movedElementsById.get(move.elementId); - if (!movedElement) { - continue; - } - - const nextTargetElements = - movedElementsByTargetTrackId.get(move.targetTrackId) ?? []; - nextTargetElements.push(movedElement); - movedElementsByTargetTrackId.set(move.targetTrackId, nextTargetElements); - } - - const updatedTracks = mapSceneTracks({ - tracks: tracksToUpdate, - update: (track) => ({ - ...track, - elements: [ - ...track.elements.filter( - (element) => !movedElementIds.has(element.id), - ), - ...(movedElementsByTargetTrackId.get(track.id) ?? []), - ], - }), - }); - - editor.timeline.updateTracks(updatedTracks); - return createElementSelectionResult( - this.moves.map(({ elementId, targetTrackId }) => ({ - trackId: targetTrackId, - elementId, - })), - ); - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } -} - -function mapSceneTracks({ - tracks, - update, -}: { - tracks: SceneTracks; - update: (track: TTrack) => TTrack; -}): SceneTracks { - return { - overlay: tracks.overlay.map((track) => update(track)), - main: update(tracks.main), - audio: tracks.audio.map((track) => update(track)), - }; -} - -function insertTrackAtDisplayIndex({ - tracks, - track, - insertIndex, -}: { - tracks: SceneTracks; - track: TimelineTrack; - insertIndex: number; -}): SceneTracks { - if (track.type === "audio") { - const audioInsertIndex = Math.max( - 0, - Math.min(insertIndex - tracks.overlay.length - 1, tracks.audio.length), - ); - return { - ...tracks, - audio: [ - ...tracks.audio.slice(0, audioInsertIndex), - track, - ...tracks.audio.slice(audioInsertIndex), - ], - }; - } - - const overlayInsertIndex = Math.max( - 0, - Math.min(insertIndex, tracks.overlay.length), - ); - return { - ...tracks, - overlay: [ - ...tracks.overlay.slice(0, overlayInsertIndex), - track, - ...tracks.overlay.slice(overlayInsertIndex), - ], - }; -} diff --git a/apps/web/src/lib/commands/timeline/element/split-elements.ts b/apps/web/src/lib/commands/timeline/element/split-elements.ts deleted file mode 100644 index f172d9c4..00000000 --- a/apps/web/src/lib/commands/timeline/element/split-elements.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { - Command, - createElementSelectionResult, - type CommandResult, -} from "@/lib/commands/base-command"; -import type { SceneTracks, TimelineElement } from "@/lib/timeline"; -import { generateUUID } from "@/utils/id"; -import { EditorCore } from "@/core"; -import { isRetimableElement } from "@/lib/timeline"; -import { splitAnimationsAtTime } from "@/lib/animation"; -import { getSourceSpanAtClipTime } from "@/lib/retime"; - -export class SplitElementsCommand extends Command { - private savedState: SceneTracks | null = null; - private rightSideElements: { trackId: string; elementId: string }[] = []; - private readonly elements: { trackId: string; elementId: string }[]; - private readonly splitTime: number; - private readonly retainSide: "both" | "left" | "right"; - - constructor({ - elements, - splitTime, - retainSide = "both", - }: { - elements: { trackId: string; elementId: string }[]; - splitTime: number; - retainSide?: "both" | "left" | "right"; - }) { - super(); - this.elements = elements; - this.splitTime = splitTime; - this.retainSide = retainSide; - } - - getRightSideElements(): { trackId: string; elementId: string }[] { - return this.rightSideElements; - } - - execute(): CommandResult | undefined { - const editor = EditorCore.getInstance(); - this.savedState = editor.scenes.getActiveScene().tracks; - this.rightSideElements = []; - - const splitTrack = < - TTrack extends { id: string; elements: TimelineElement[] }, - >( - track: TTrack, - ): TTrack => { - const elementsToSplit = this.elements.filter( - (target) => target.trackId === track.id, - ); - - if (elementsToSplit.length === 0) { - return track; - } - - const elements = track.elements.flatMap((element) => { - const shouldSplit = elementsToSplit.some( - (target) => target.elementId === element.id, - ); - - if (!shouldSplit) { - return [element]; - } - - const effectiveStart = element.startTime; - const effectiveEnd = element.startTime + element.duration; - - if ( - this.splitTime <= effectiveStart || - this.splitTime >= effectiveEnd - ) { - return [element]; - } - - const relativeTime = this.splitTime - element.startTime; - const leftVisibleDuration = relativeTime; - const rightVisibleDuration = element.duration - relativeTime; - const retimeRef = isRetimableElement(element) - ? element.retime - : undefined; - const leftSourceSpan = getSourceSpanAtClipTime({ - clipTime: leftVisibleDuration, - retime: retimeRef, - }); - const totalSourceSpan = getSourceSpanAtClipTime({ - clipTime: element.duration, - retime: retimeRef, - }); - const rightSourceSpan = totalSourceSpan - leftSourceSpan; - const { leftAnimations, rightAnimations } = splitAnimationsAtTime({ - animations: element.animations, - splitTime: relativeTime, - shouldIncludeSplitBoundary: true, - }); - let splitResult: TimelineElement[]; - - if (this.retainSide === "left") { - splitResult = [ - { - ...element, - duration: leftVisibleDuration, - trimEnd: element.trimEnd + rightSourceSpan, - name: `${element.name} (left)`, - animations: leftAnimations, - ...(retimeRef !== undefined ? { retime: retimeRef } : {}), - }, - ]; - } else if (this.retainSide === "right") { - const newId = generateUUID(); - this.rightSideElements.push({ - trackId: track.id, - elementId: newId, - }); - splitResult = [ - { - ...element, - id: newId, - startTime: this.splitTime, - duration: rightVisibleDuration, - trimStart: element.trimStart + leftSourceSpan, - name: `${element.name} (right)`, - animations: rightAnimations, - ...(retimeRef !== undefined ? { retime: retimeRef } : {}), - }, - ]; - } else { - // "both" - split into two pieces - const secondElementId = generateUUID(); - this.rightSideElements.push({ - trackId: track.id, - elementId: secondElementId, - }); - splitResult = [ - { - ...element, - duration: leftVisibleDuration, - trimEnd: element.trimEnd + rightSourceSpan, - name: `${element.name} (left)`, - animations: leftAnimations, - ...(retimeRef !== undefined ? { retime: retimeRef } : {}), - }, - { - ...element, - id: secondElementId, - startTime: this.splitTime, - duration: rightVisibleDuration, - trimStart: element.trimStart + leftSourceSpan, - name: `${element.name} (right)`, - animations: rightAnimations, - ...(retimeRef !== undefined ? { retime: retimeRef } : {}), - }, - ]; - } - - return splitResult; - }); - - return { ...track, elements } as TTrack; - }; - - const updatedTracks: SceneTracks = { - overlay: this.savedState.overlay.map((track) => splitTrack(track)), - main: splitTrack(this.savedState.main), - audio: this.savedState.audio.map((track) => splitTrack(track)), - }; - - editor.timeline.updateTracks(updatedTracks); - - if (this.rightSideElements.length > 0) { - return createElementSelectionResult(this.rightSideElements); - } - return undefined; - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } -}