diff options
Diffstat (limited to 'apps/web/src/lib/commands/timeline/element')
13 files changed, 90 insertions, 592 deletions
diff --git a/apps/web/src/lib/commands/timeline/element/delete-elements.ts b/apps/web/src/lib/commands/timeline/element/delete-elements.ts index 1e1ad661..c8cc4494 100644 --- a/apps/web/src/lib/commands/timeline/element/delete-elements.ts +++ b/apps/web/src/lib/commands/timeline/element/delete-elements.ts @@ -1,23 +1,18 @@ import { Command, type CommandResult } from "@/lib/commands/base-command"; import type { TimelineTrack } from "@/lib/timeline"; import { EditorCore } from "@/core"; -import { rippleShiftElements } from "@/lib/timeline"; export class DeleteElementsCommand extends Command { private savedState: TimelineTrack[] | null = null; private readonly elements: { trackId: string; elementId: string }[]; - private readonly rippleEnabled: boolean; constructor({ elements, - rippleEnabled = false, }: { elements: { trackId: string; elementId: string }[]; - rippleEnabled?: boolean; }) { super(); this.elements = elements; - this.rippleEnabled = rippleEnabled; } execute(): CommandResult | undefined { @@ -25,45 +20,25 @@ export class DeleteElementsCommand extends Command { this.savedState = editor.timeline.getTracks(); const updatedTracks = this.savedState.map((track) => { - const elementsToDeleteOnTrack = this.elements.filter( - (target) => target.trackId === track.id, - ); - const hasElementsToDelete = elementsToDeleteOnTrack.length > 0; + const elementsToDeleteOnTrack = this.elements.filter( + (target) => target.trackId === track.id, + ); - if (!hasElementsToDelete) { - return track; - } + if (elementsToDeleteOnTrack.length === 0) { + return track; + } - const deletedElementInfos = elementsToDeleteOnTrack - .map((target) => - track.elements.find((element) => element.id === target.elementId), - ) - .filter((element): element is NonNullable<typeof element> => element !== undefined) - .map((element) => ({ startTime: element.startTime, duration: element.duration })); + const elements = track.elements.filter( + (element) => + !this.elements.some( + (target) => + target.trackId === track.id && + target.elementId === element.id, + ), + ); - let elements = track.elements.filter( - (element) => - !this.elements.some( - (target) => - target.trackId === track.id && target.elementId === element.id, - ), - ); - - if (this.rippleEnabled && deletedElementInfos.length > 0) { - const sortedByStartDesc = [...deletedElementInfos].sort( - (a, b) => b.startTime - a.startTime, - ); - for (const { startTime, duration } of sortedByStartDesc) { - elements = rippleShiftElements({ - elements, - afterTime: startTime, - shiftAmount: duration, - }); - } - } - - return { ...track, elements } as typeof track; - }); + return { ...track, elements } as typeof track; + }); editor.timeline.updateTracks(updatedTracks); diff --git a/apps/web/src/lib/commands/timeline/element/index.ts b/apps/web/src/lib/commands/timeline/element/index.ts index 60e12ddd..eabdc385 100644 --- a/apps/web/src/lib/commands/timeline/element/index.ts +++ b/apps/web/src/lib/commands/timeline/element/index.ts @@ -1,17 +1,11 @@ export { InsertElementCommand } from "./insert-element";
export { DeleteElementsCommand } from "./delete-elements";
export { DuplicateElementsCommand } from "./duplicate-elements";
-export { UpdateElementTrimCommand } from "./update-element-trim";
-export { UpdateElementDurationCommand } from "./update-element-duration";
-export { UpdateElementStartTimeCommand } from "./update-element-start-time";
export { SplitElementsCommand } from "./split-elements";
-export { UpdateElementCommand } from "./update-element";
-export { ToggleElementsVisibilityCommand } from "./toggle-elements-visibility";
-export { ToggleElementsMutedCommand } from "./toggle-elements-muted";
+export { UpdateElementsCommand } from "./update-elements";
export { ToggleSourceAudioSeparationCommand } from "./toggle-source-audio-separation";
export { MoveElementCommand } from "./move-elements";
export * from "./keyframes";
export * from "./effects";
export * from "./masks";
-export * from "./retime";
diff --git a/apps/web/src/lib/commands/timeline/element/move-elements.ts b/apps/web/src/lib/commands/timeline/element/move-elements.ts index e944ca81..cf3afa6d 100644 --- a/apps/web/src/lib/commands/timeline/element/move-elements.ts +++ b/apps/web/src/lib/commands/timeline/element/move-elements.ts @@ -10,7 +10,6 @@ import { validateElementTrackCompatibility, enforceMainTrackStart, } from "@/lib/timeline/placement"; -import { rippleShiftElements } from "@/lib/timeline/ripple-utils"; export class MoveElementCommand extends Command { private savedState: TimelineTrack[] | null = null; @@ -19,7 +18,6 @@ export class MoveElementCommand extends Command { private readonly elementId: string; private readonly newStartTime: number; private readonly createTrack: { type: TrackType; index: number } | undefined; - private readonly rippleEnabled: boolean; constructor({ sourceTrackId, @@ -27,14 +25,12 @@ export class MoveElementCommand extends Command { elementId, newStartTime, createTrack, - rippleEnabled = false, }: { sourceTrackId: string; targetTrackId: string; elementId: string; newStartTime: number; createTrack?: { type: TrackType; index: number }; - rippleEnabled?: boolean; }) { super(); this.sourceTrackId = sourceTrackId; @@ -42,7 +38,6 @@ export class MoveElementCommand extends Command { this.elementId = elementId; this.newStartTime = newStartTime; this.createTrack = createTrack; - this.rippleEnabled = rippleEnabled; } execute(): CommandResult | undefined { @@ -113,14 +108,7 @@ export class MoveElementCommand extends Command { const remainingElements = track.elements.filter( (trackElement) => trackElement.id !== this.elementId, ); - const shiftedElements = this.rippleEnabled - ? rippleShiftElements({ - elements: remainingElements, - afterTime: element.startTime, - shiftAmount: element.duration, - }) - : remainingElements; - return { ...track, elements: shiftedElements } as typeof track; + return { ...track, elements: remainingElements } as typeof track; } if (track.id === this.targetTrackId) { diff --git a/apps/web/src/lib/commands/timeline/element/retime/index.ts b/apps/web/src/lib/commands/timeline/element/retime/index.ts deleted file mode 100644 index e9c24c44..00000000 --- a/apps/web/src/lib/commands/timeline/element/retime/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UpdateElementRetimeCommand } from "./update-element-retime";
diff --git a/apps/web/src/lib/commands/timeline/element/retime/update-element-retime.ts b/apps/web/src/lib/commands/timeline/element/retime/update-element-retime.ts deleted file mode 100644 index 474396a1..00000000 --- a/apps/web/src/lib/commands/timeline/element/retime/update-element-retime.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { EditorCore } from "@/core"; -import { clampRetimeRate } from "@/lib/retime/rate"; -import { clampAnimationsToDuration } from "@/lib/animation"; -import { Command, type CommandResult } from "@/lib/commands/base-command"; -import { getTimelineDurationForSourceSpan, getSourceSpanAtClipTime } from "@/lib/retime"; -import { isRetimableElement, updateElementInTracks } from "@/lib/timeline"; -import type { RetimeConfig, TimelineTrack } from "@/lib/timeline"; - -function getSourceDuration({ - trimStart, - trimEnd, - duration, - sourceDuration, - retime, -}: { - trimStart: number; - trimEnd: number; - duration: number; - sourceDuration?: number; - retime?: RetimeConfig; -}): number { - if (typeof sourceDuration === "number") { - return sourceDuration; - } - - return ( - trimStart + - getSourceSpanAtClipTime({ - clipTime: duration, - retime, - }) + - trimEnd - ); -} - -export class UpdateElementRetimeCommand extends Command { - private savedState: TimelineTrack[] | null = null; - private readonly trackId: string; - private readonly elementId: string; - private readonly retime: RetimeConfig | undefined; - - constructor({ - trackId, - elementId, - retime, - }: { - trackId: string; - elementId: string; - retime?: RetimeConfig; - }) { - super(); - this.trackId = trackId; - this.elementId = elementId; - this.retime = retime; - } - - execute(): CommandResult | undefined { - const editor = EditorCore.getInstance(); - this.savedState = editor.timeline.getTracks(); - - const updatedTracks = updateElementInTracks({ - tracks: this.savedState, - trackId: this.trackId, - elementId: this.elementId, - elementPredicate: isRetimableElement, - update: (element) => { - if (!isRetimableElement(element)) { - return element; - } - - const nextRetime = this.retime - ? { - ...this.retime, - rate: clampRetimeRate({ rate: this.retime.rate }), - } - : undefined; - const sourceDuration = getSourceDuration({ - trimStart: element.trimStart, - trimEnd: element.trimEnd, - duration: element.duration, - sourceDuration: element.sourceDuration, - retime: element.retime, - }); - const visibleSourceSpan = Math.max( - 0, - sourceDuration - element.trimStart - element.trimEnd, - ); - const nextDuration = getTimelineDurationForSourceSpan({ - sourceSpan: visibleSourceSpan, - retime: nextRetime, - }); - - return { - ...element, - retime: nextRetime, - duration: nextDuration, - animations: clampAnimationsToDuration({ - animations: element.animations, - duration: nextDuration, - }), - }; - }, - }); - - editor.timeline.updateTracks(updatedTracks); - return undefined; - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } -} diff --git a/apps/web/src/lib/commands/timeline/element/split-elements.ts b/apps/web/src/lib/commands/timeline/element/split-elements.ts index 250a1ce6..79722b4c 100644 --- a/apps/web/src/lib/commands/timeline/element/split-elements.ts +++ b/apps/web/src/lib/commands/timeline/element/split-elements.ts @@ -2,7 +2,7 @@ import { Command, type CommandResult } from "@/lib/commands/base-command"; import type { TimelineTrack } from "@/lib/timeline"; import { generateUUID } from "@/utils/id"; import { EditorCore } from "@/core"; -import { isRetimableElement, rippleShiftElements } from "@/lib/timeline"; +import { isRetimableElement } from "@/lib/timeline"; import { splitAnimationsAtTime } from "@/lib/animation"; import { getSourceSpanAtClipTime } from "@/lib/retime"; @@ -12,24 +12,20 @@ export class SplitElementsCommand extends Command { private readonly elements: { trackId: string; elementId: string }[]; private readonly splitTime: number; private readonly retainSide: "both" | "left" | "right"; - private readonly rippleEnabled: boolean; constructor({ elements, splitTime, retainSide = "both", - rippleEnabled = false, }: { elements: { trackId: string; elementId: string }[]; splitTime: number; retainSide?: "both" | "left" | "right"; - rippleEnabled?: boolean; }) { super(); this.elements = elements; this.splitTime = splitTime; this.retainSide = retainSide; - this.rippleEnabled = rippleEnabled; } getRightSideElements(): { trackId: string; elementId: string }[] { @@ -50,8 +46,6 @@ export class SplitElementsCommand extends Command { return track; } - let leftVisibleDurationForRipple: number | null = null; - let elements = track.elements.flatMap((element) => { const shouldSplit = elementsToSplit.some( (target) => target.elementId === element.id, @@ -106,9 +100,6 @@ export class SplitElementsCommand extends Command { } if (this.retainSide === "right") { - if (this.rippleEnabled && elementsToSplit.length === 1) { - leftVisibleDurationForRipple = leftVisibleDuration; - } const newId = generateUUID(); this.rightSideElements.push({ trackId: track.id, @@ -157,14 +148,6 @@ export class SplitElementsCommand extends Command { ]; }); - if (this.rippleEnabled && leftVisibleDurationForRipple !== null) { - elements = rippleShiftElements({ - elements, - afterTime: this.splitTime, - shiftAmount: leftVisibleDurationForRipple, - }); - } - return { ...track, elements } as typeof track; }); diff --git a/apps/web/src/lib/commands/timeline/element/toggle-elements-muted.ts b/apps/web/src/lib/commands/timeline/element/toggle-elements-muted.ts deleted file mode 100644 index 75b77562..00000000 --- a/apps/web/src/lib/commands/timeline/element/toggle-elements-muted.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Command, type CommandResult } from "@/lib/commands/base-command";
-import type { TimelineTrack } from "@/lib/timeline";
-import { canElementHaveAudio } from "@/lib/timeline/element-utils";
-import { EditorCore } from "@/core";
-
-export class ToggleElementsMutedCommand extends Command {
- private savedState: TimelineTrack[] | null = null;
-
- constructor(private elements: { trackId: string; elementId: string }[]) {
- super();
- }
-
- execute(): CommandResult | undefined {
- const editor = EditorCore.getInstance();
- this.savedState = editor.timeline.getTracks();
-
- const mutableElements = this.elements.filter(({ trackId, elementId }) => {
- const track = this.savedState?.find((t) => t.id === trackId);
- const element = track?.elements.find((e) => e.id === elementId);
- return element && canElementHaveAudio(element);
- });
-
- if (mutableElements.length === 0) {
- return;
- }
-
- const shouldMute = mutableElements.some(({ trackId, elementId }) => {
- const track = this.savedState?.find((t) => t.id === trackId);
- const element = track?.elements.find((e) => e.id === elementId);
- return element && canElementHaveAudio(element) && !element.muted;
- });
-
- const updatedTracks = this.savedState.map((track) => {
- const newElements = track.elements.map((element) => {
- const shouldUpdate = mutableElements.some(
- ({ trackId, elementId }) =>
- track.id === trackId && element.id === elementId,
- );
- return shouldUpdate &&
- canElementHaveAudio(element) &&
- element.muted !== shouldMute
- ? { ...element, muted: shouldMute }
- : element;
- });
- return { ...track, elements: newElements } as typeof track;
- });
-
- editor.timeline.updateTracks(updatedTracks);
- }
-
- undo(): void {
- if (this.savedState) {
- const editor = EditorCore.getInstance();
- editor.timeline.updateTracks(this.savedState);
- }
- }
-}
diff --git a/apps/web/src/lib/commands/timeline/element/toggle-elements-visibility.ts b/apps/web/src/lib/commands/timeline/element/toggle-elements-visibility.ts deleted file mode 100644 index 271b0a00..00000000 --- a/apps/web/src/lib/commands/timeline/element/toggle-elements-visibility.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Command, type CommandResult } from "@/lib/commands/base-command";
-import type { TimelineTrack } from "@/lib/timeline";
-import { canElementBeHidden } from "@/lib/timeline/element-utils";
-import { EditorCore } from "@/core";
-
-export class ToggleElementsVisibilityCommand extends Command {
- private savedState: TimelineTrack[] | null = null;
-
- constructor(private elements: { trackId: string; elementId: string }[]) {
- super();
- }
-
- execute(): CommandResult | undefined {
- const editor = EditorCore.getInstance();
- this.savedState = editor.timeline.getTracks();
-
- const shouldHide = this.elements.some(({ trackId, elementId }) => {
- const track = this.savedState?.find((t) => t.id === trackId);
- const element = track?.elements.find((e) => e.id === elementId);
- return element && canElementBeHidden(element) && !element.hidden;
- });
-
- const updatedTracks = this.savedState.map((track) => {
- const newElements = track.elements.map((element) => {
- const shouldUpdate = this.elements.some(
- ({ trackId, elementId }) =>
- track.id === trackId && element.id === elementId,
- );
- return shouldUpdate &&
- canElementBeHidden(element) &&
- element.hidden !== shouldHide
- ? { ...element, hidden: shouldHide }
- : element;
- });
- return { ...track, elements: newElements } as typeof track;
- });
-
- editor.timeline.updateTracks(updatedTracks);
- return undefined;
- }
-
- undo(): void {
- if (this.savedState) {
- const editor = EditorCore.getInstance();
- editor.timeline.updateTracks(this.savedState);
- }
- }
-}
diff --git a/apps/web/src/lib/commands/timeline/element/update-element-duration.ts b/apps/web/src/lib/commands/timeline/element/update-element-duration.ts deleted file mode 100644 index 7ad62000..00000000 --- a/apps/web/src/lib/commands/timeline/element/update-element-duration.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Command, type CommandResult } from "@/lib/commands/base-command"; -import type { TimelineTrack } from "@/lib/timeline"; -import { EditorCore } from "@/core"; -import { clampAnimationsToDuration } from "@/lib/animation"; - -export class UpdateElementDurationCommand extends Command { - private savedState: TimelineTrack[] | null = null; - private readonly trackId: string; - private readonly elementId: string; - private readonly duration: number; - - constructor({ - trackId, - elementId, - duration, - }: { - trackId: string; - elementId: string; - duration: number; - }) { - super(); - this.trackId = trackId; - this.elementId = elementId; - this.duration = duration; - } - - execute(): CommandResult | undefined { - const editor = EditorCore.getInstance(); - this.savedState = editor.timeline.getTracks(); - - const updatedTracks = this.savedState.map((track) => { - if (track.id !== this.trackId) return track; - const newElements = track.elements.map((element) => - element.id === this.elementId - ? { - ...element, - duration: this.duration, - animations: clampAnimationsToDuration({ - animations: element.animations, - duration: this.duration, - }), - } - : element, - ); - return { ...track, elements: newElements } as typeof track; - }); - - editor.timeline.updateTracks(updatedTracks); - return undefined; - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } -} diff --git a/apps/web/src/lib/commands/timeline/element/update-element-start-time.ts b/apps/web/src/lib/commands/timeline/element/update-element-start-time.ts deleted file mode 100644 index 6232d539..00000000 --- a/apps/web/src/lib/commands/timeline/element/update-element-start-time.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Command, type CommandResult } from "@/lib/commands/base-command"; -import type { TimelineTrack } from "@/lib/timeline"; -import { EditorCore } from "@/core"; -import { enforceMainTrackStart } from "@/lib/timeline/placement"; - -export class UpdateElementStartTimeCommand extends Command { - private savedState: TimelineTrack[] | null = null; - private readonly elements: { trackId: string; elementId: string }[]; - private readonly startTime: number; - - constructor({ - elements, - startTime, - }: { - elements: { trackId: string; elementId: string }[]; - startTime: number; - }) { - super(); - this.elements = elements; - this.startTime = startTime; - } - - execute(): CommandResult | undefined { - const editor = EditorCore.getInstance(); - this.savedState = editor.timeline.getTracks(); - - const currentTracks = this.savedState; - const updatedTracks = currentTracks.map((track) => { - const hasElementsToUpdate = this.elements.some( - (elementEntry) => elementEntry.trackId === track.id, - ); - - if (!hasElementsToUpdate) { - return track; - } - - const newElements = track.elements.map((element) => { - const shouldUpdate = this.elements.some( - (elementEntry) => - elementEntry.elementId === element.id && - elementEntry.trackId === track.id, - ); - if (!shouldUpdate) { - return element; - } - - const baseStartTime = Math.max(0, this.startTime); - const adjustedStartTime = enforceMainTrackStart({ - tracks: currentTracks, - targetTrackId: track.id, - requestedStartTime: baseStartTime, - excludeElementId: element.id, - }); - - return { ...element, startTime: adjustedStartTime }; - }); - return { ...track, elements: newElements } as typeof track; - }); - - editor.timeline.updateTracks(updatedTracks); - return undefined; - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } -} diff --git a/apps/web/src/lib/commands/timeline/element/update-element-trim.ts b/apps/web/src/lib/commands/timeline/element/update-element-trim.ts deleted file mode 100644 index e3cb596b..00000000 --- a/apps/web/src/lib/commands/timeline/element/update-element-trim.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { Command, type CommandResult } from "@/lib/commands/base-command"; -import type { TimelineTrack } from "@/lib/timeline"; -import { EditorCore } from "@/core"; -import { clampAnimationsToDuration } from "@/lib/animation"; -import { isRetimableElement, rippleShiftElements } from "@/lib/timeline"; -import { enforceMainTrackStart } from "@/lib/timeline/placement"; - -export class UpdateElementTrimCommand extends Command { - private savedState: TimelineTrack[] | null = null; - private readonly elementId: string; - private readonly trimStart: number; - private readonly trimEnd: number; - private readonly startTime: number | undefined; - private readonly duration: number | undefined; - private readonly rippleEnabled: boolean; - - constructor({ - elementId, - trimStart, - trimEnd, - startTime, - duration, - rippleEnabled = false, - }: { - elementId: string; - trimStart: number; - trimEnd: number; - startTime?: number; - duration?: number; - rippleEnabled?: boolean; - }) { - super(); - this.elementId = elementId; - this.trimStart = trimStart; - this.trimEnd = trimEnd; - this.startTime = startTime; - this.duration = duration; - this.rippleEnabled = rippleEnabled; - } - - execute(): CommandResult | undefined { - const editor = EditorCore.getInstance(); - this.savedState = editor.timeline.getTracks(); - - const updatedTracks = this.savedState.map((track) => { - const targetElement = track.elements.find( - (element) => element.id === this.elementId, - ); - if (!targetElement) return track; - - const nextDuration = this.duration ?? targetElement.duration; - const requestedStartTime = this.startTime ?? targetElement.startTime; - const nextStartTime = enforceMainTrackStart({ - tracks: this.savedState ?? [], - targetTrackId: track.id, - requestedStartTime, - excludeElementId: this.elementId, - }); - - const oldEndTime = targetElement.startTime + targetElement.duration; - const newEndTime = nextStartTime + nextDuration; - const shiftAmount = oldEndTime - newEndTime; - - const updatedElement = { - ...targetElement, - trimStart: this.trimStart, - trimEnd: this.trimEnd, - startTime: nextStartTime, - duration: nextDuration, - ...(isRetimableElement(targetElement) - ? { retime: targetElement.retime } - : {}), - animations: clampAnimationsToDuration({ - animations: targetElement.animations, - duration: nextDuration, - }), - }; - - if (this.rippleEnabled && Math.abs(shiftAmount) > 0) { - const shiftedOthers = rippleShiftElements({ - elements: track.elements.filter( - (element) => element.id !== this.elementId, - ), - afterTime: oldEndTime, - shiftAmount, - }); - return { - ...track, - elements: track.elements.map((element) => - element.id === this.elementId - ? updatedElement - : (shiftedOthers.find((shifted) => shifted.id === element.id) ?? - element), - ), - } as typeof track; - } - - return { - ...track, - elements: track.elements.map((element) => - element.id === this.elementId ? updatedElement : element, - ), - } as typeof track; - }); - - editor.timeline.updateTracks(updatedTracks); - return undefined; - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } -} diff --git a/apps/web/src/lib/commands/timeline/element/update-element.ts b/apps/web/src/lib/commands/timeline/element/update-element.ts deleted file mode 100644 index 45b2603f..00000000 --- a/apps/web/src/lib/commands/timeline/element/update-element.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Command, type CommandResult } from "@/lib/commands/base-command"; -import type { TimelineElement, TimelineTrack } from "@/lib/timeline"; -import { EditorCore } from "@/core"; -import { updateElementInTracks } from "@/lib/timeline"; - -export class UpdateElementCommand extends Command { - private savedState: TimelineTrack[] | null = null; - private readonly trackId: string; - private readonly elementId: string; - private readonly updates: Partial<TimelineElement>; - - constructor({ - trackId, - elementId, - updates, - }: { - trackId: string; - elementId: string; - updates: Partial<TimelineElement>; - }) { - super(); - this.trackId = trackId; - this.elementId = elementId; - this.updates = updates; - } - - execute(): CommandResult | undefined { - const editor = EditorCore.getInstance(); - this.savedState = editor.timeline.getTracks(); - - const updatedTracks = updateElementInTracks({ - tracks: this.savedState, - trackId: this.trackId, - elementId: this.elementId, - update: (element) => ({ ...element, ...this.updates }) as TimelineElement, - }); - - editor.timeline.updateTracks(updatedTracks); - return undefined; - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - } - } -} diff --git a/apps/web/src/lib/commands/timeline/element/update-elements.ts b/apps/web/src/lib/commands/timeline/element/update-elements.ts new file mode 100644 index 00000000..abeec842 --- /dev/null +++ b/apps/web/src/lib/commands/timeline/element/update-elements.ts @@ -0,0 +1,71 @@ +import { EditorCore } from "@/core"; +import { Command, type CommandResult } from "@/lib/commands/base-command"; +import type { TimelineElement, TimelineTrack } from "@/lib/timeline"; +import { updateElementInTracks } from "@/lib/timeline"; +import { applyElementUpdate } from "@/lib/timeline/update-pipeline"; + +export class UpdateElementsCommand extends Command { + private savedState: TimelineTrack[] | null = null; + private readonly updates: Array<{ + trackId: string; + elementId: string; + patch: Partial<TimelineElement>; + }>; + + constructor({ + updates, + }: { + updates: Array<{ + trackId: string; + elementId: string; + patch: Partial<TimelineElement>; + }>; + }) { + super(); + this.updates = updates; + } + + execute(): CommandResult | undefined { + const editor = EditorCore.getInstance(); + this.savedState = editor.timeline.getTracks(); + let updatedTracks = this.savedState; + + for (const updateEntry of this.updates) { + const currentTrack = updatedTracks.find( + (track) => track.id === updateEntry.trackId, + ); + const currentElement = currentTrack?.elements.find( + (element) => element.id === updateEntry.elementId, + ); + if (!currentTrack || !currentElement) { + continue; + } + + const nextElement = applyElementUpdate({ + element: currentElement, + patch: updateEntry.patch, + context: { + tracks: updatedTracks, + trackId: updateEntry.trackId, + }, + }); + + updatedTracks = updateElementInTracks({ + tracks: updatedTracks, + trackId: updateEntry.trackId, + elementId: updateEntry.elementId, + update: () => nextElement, + }); + } + + editor.timeline.updateTracks(updatedTracks); + return undefined; + } + + undo(): void { + if (this.savedState) { + const editor = EditorCore.getInstance(); + editor.timeline.updateTracks(this.savedState); + } + } +} |
