diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-27 00:55:15 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-29 15:56:48 +0200 |
| commit | 4d127a038bf768eded68aae3cfa5c1b12241bdac (patch) | |
| tree | 16bbb801caf6770546cff4fdb37b6408ec9ead98 /apps/web/src/lib/commands/timeline/element/duplicate-elements.ts | |
| parent | 82817cb50cb0487350573d6129130aec705f924b (diff) | |
| download | opencut-4d127a038bf768eded68aae3cfa5c1b12241bdac.tar.gz | |
chore: normalize line endings
Diffstat (limited to 'apps/web/src/lib/commands/timeline/element/duplicate-elements.ts')
| -rw-r--r-- | apps/web/src/lib/commands/timeline/element/duplicate-elements.ts | 256 |
1 files changed, 128 insertions, 128 deletions
diff --git a/apps/web/src/lib/commands/timeline/element/duplicate-elements.ts b/apps/web/src/lib/commands/timeline/element/duplicate-elements.ts index 207e2373..346f8c73 100644 --- a/apps/web/src/lib/commands/timeline/element/duplicate-elements.ts +++ b/apps/web/src/lib/commands/timeline/element/duplicate-elements.ts @@ -1,128 +1,128 @@ -import { Command } from "@/lib/commands/base-command"; -import type { TimelineElement, TimelineTrack } from "@/lib/timeline"; -import { generateUUID } from "@/utils/id"; -import { EditorCore } from "@/core"; -import { - buildEmptyTrack, - getHighestInsertIndexForTrack, -} from "@/lib/timeline/track-utils"; -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: TimelineTrack[] | null = null; - private previousSelection: { trackId: string; elementId: string }[] = []; - private elements: DuplicateElementsParams["elements"]; - - constructor({ elements }: DuplicateElementsParams) { - super(); - this.elements = elements; - } - - execute(): void { - const editor = EditorCore.getInstance(); - this.savedState = editor.timeline.getTracks(); - this.previousSelection = editor.selection.getSelectedElements(); - this.duplicatedElements = []; - - const updatedTracks = [...this.savedState]; - - for (const track of this.savedState) { - 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[] = []; - - const newTrackId = generateUUID(); - const newTrackBase = buildEmptyTrack({ - id: newTrackId, - type: track.type, - }); - - for (const element of track.elements) { - if (!elementIdsToDuplicate.has(element.id)) { - continue; - } - - const newId = generateUUID(); - this.duplicatedElements.push({ - trackId: newTrackId, - elementId: newId, - }); - newTrackElements.push( - buildDuplicateElement({ - element, - id: newId, - startTime: element.startTime, - }), - ); - } - - const newTrack = { - ...newTrackBase, - elements: newTrackElements, - } as TimelineTrack; - - const insertIndex = getHighestInsertIndexForTrack({ - tracks: updatedTracks, - trackType: track.type, - }); - updatedTracks.splice(insertIndex, 0, newTrack); - } - - editor.timeline.updateTracks(updatedTracks); - - if (this.duplicatedElements.length > 0) { - editor.selection.setSelectedElements({ - elements: this.duplicatedElements, - }); - } - } - - undo(): void { - if (this.savedState) { - const editor = EditorCore.getInstance(); - editor.timeline.updateTracks(this.savedState); - editor.selection.setSelectedElements({ - elements: this.previousSelection, - }); - } - } - - 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, - }), - }; -} +import { Command } from "@/lib/commands/base-command";
+import type { TimelineElement, TimelineTrack } from "@/lib/timeline";
+import { generateUUID } from "@/utils/id";
+import { EditorCore } from "@/core";
+import {
+ buildEmptyTrack,
+ getHighestInsertIndexForTrack,
+} from "@/lib/timeline/track-utils";
+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: TimelineTrack[] | null = null;
+ private previousSelection: { trackId: string; elementId: string }[] = [];
+ private elements: DuplicateElementsParams["elements"];
+
+ constructor({ elements }: DuplicateElementsParams) {
+ super();
+ this.elements = elements;
+ }
+
+ execute(): void {
+ const editor = EditorCore.getInstance();
+ this.savedState = editor.timeline.getTracks();
+ this.previousSelection = editor.selection.getSelectedElements();
+ this.duplicatedElements = [];
+
+ const updatedTracks = [...this.savedState];
+
+ for (const track of this.savedState) {
+ 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[] = [];
+
+ const newTrackId = generateUUID();
+ const newTrackBase = buildEmptyTrack({
+ id: newTrackId,
+ type: track.type,
+ });
+
+ for (const element of track.elements) {
+ if (!elementIdsToDuplicate.has(element.id)) {
+ continue;
+ }
+
+ const newId = generateUUID();
+ this.duplicatedElements.push({
+ trackId: newTrackId,
+ elementId: newId,
+ });
+ newTrackElements.push(
+ buildDuplicateElement({
+ element,
+ id: newId,
+ startTime: element.startTime,
+ }),
+ );
+ }
+
+ const newTrack = {
+ ...newTrackBase,
+ elements: newTrackElements,
+ } as TimelineTrack;
+
+ const insertIndex = getHighestInsertIndexForTrack({
+ tracks: updatedTracks,
+ trackType: track.type,
+ });
+ updatedTracks.splice(insertIndex, 0, newTrack);
+ }
+
+ editor.timeline.updateTracks(updatedTracks);
+
+ if (this.duplicatedElements.length > 0) {
+ editor.selection.setSelectedElements({
+ elements: this.duplicatedElements,
+ });
+ }
+ }
+
+ undo(): void {
+ if (this.savedState) {
+ const editor = EditorCore.getInstance();
+ editor.timeline.updateTracks(this.savedState);
+ editor.selection.setSelectedElements({
+ elements: this.previousSelection,
+ });
+ }
+ }
+
+ 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,
+ }),
+ };
+}
|
