summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/commands/timeline/element/move-elements.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-07 04:41:27 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-07 04:41:27 +0200
commitcbd1b8212370c67b34a70dccd21bd82de72fead2 (patch)
treed97cbee2aae22c22b59c59a50e9f8a6af0ae8ccf /apps/web/src/lib/commands/timeline/element/move-elements.ts
parent4f7d401a974047d37673ee8e3840dceaaddc295c (diff)
downloadopencut-cbd1b8212370c67b34a70dccd21bd82de72fead2.tar.gz
refactor: structured track layout per scene
Diffstat (limited to 'apps/web/src/lib/commands/timeline/element/move-elements.ts')
-rw-r--r--apps/web/src/lib/commands/timeline/element/move-elements.ts122
1 files changed, 87 insertions, 35 deletions
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 cf3afa6d..c1fa6897 100644
--- a/apps/web/src/lib/commands/timeline/element/move-elements.ts
+++ b/apps/web/src/lib/commands/timeline/element/move-elements.ts
@@ -1,18 +1,23 @@
import { Command, type CommandResult } from "@/lib/commands/base-command";
import { EditorCore } from "@/core";
import type {
- TimelineTrack,
+ SceneTracks,
TimelineElement,
TrackType,
+ TimelineTrack,
} from "@/lib/timeline";
import {
buildEmptyTrack,
validateElementTrackCompatibility,
enforceMainTrackStart,
} from "@/lib/timeline/placement";
+import {
+ findTrackInSceneTracks,
+ updateTrackInSceneTracks,
+} from "@/lib/timeline/track-element-update";
export class MoveElementCommand extends Command {
- private savedState: TimelineTrack[] | null = null;
+ private savedState: SceneTracks | null = null;
private readonly sourceTrackId: string;
private readonly targetTrackId: string;
private readonly elementId: string;
@@ -42,11 +47,12 @@ export class MoveElementCommand extends Command {
execute(): CommandResult | undefined {
const editor = EditorCore.getInstance();
- this.savedState = editor.timeline.getTracks();
+ this.savedState = editor.scenes.getActiveScene().tracks;
- const sourceTrack = this.savedState.find(
- (track) => track.id === this.sourceTrackId,
- );
+ const sourceTrack = findTrackInSceneTracks({
+ tracks: this.savedState,
+ trackId: this.sourceTrackId,
+ });
const element = sourceTrack?.elements.find(
(trackElement) => trackElement.id === this.elementId,
);
@@ -55,15 +61,21 @@ export class MoveElementCommand extends Command {
throw new Error("Source track or element not found");
}
- let targetTrack = this.savedState.find((track) => track.id === this.targetTrackId);
+ let targetTrack = findTrackInSceneTracks({
+ tracks: this.savedState,
+ trackId: this.targetTrackId,
+ });
let tracksToUpdate = this.savedState;
if (!targetTrack && this.createTrack) {
const newTrack = buildEmptyTrack({
id: this.targetTrackId,
type: this.createTrack.type,
});
- tracksToUpdate = [...this.savedState];
- tracksToUpdate.splice(this.createTrack.index, 0, newTrack);
+ tracksToUpdate = insertTrackAtDisplayIndex({
+ tracks: this.savedState,
+ track: newTrack,
+ insertIndex: this.createTrack.index,
+ });
targetTrack = newTrack;
}
if (!targetTrack) {
@@ -94,32 +106,34 @@ export class MoveElementCommand extends Command {
const isSameTrack = this.sourceTrackId === this.targetTrackId;
- const updatedTracks = tracksToUpdate.map((track): TimelineTrack => {
- if (isSameTrack && track.id === this.sourceTrackId) {
- return {
- ...track,
- elements: track.elements.map((trackElement) =>
- trackElement.id === this.elementId ? movedElement : trackElement,
- ),
- } as typeof track;
- }
-
- if (track.id === this.sourceTrackId) {
- const remainingElements = track.elements.filter(
- (trackElement) => trackElement.id !== this.elementId,
- );
- return { ...track, elements: remainingElements } as typeof track;
- }
-
- if (track.id === this.targetTrackId) {
- return {
- ...track,
- elements: [...track.elements, movedElement],
- } as typeof track;
- }
-
- return track;
- });
+ const updatedTracks = isSameTrack
+ ? updateTrackInSceneTracks({
+ tracks: tracksToUpdate,
+ trackId: this.sourceTrackId,
+ update: (track) => ({
+ ...track,
+ elements: track.elements.map((trackElement) =>
+ trackElement.id === this.elementId ? movedElement : trackElement,
+ ),
+ }),
+ })
+ : updateTrackInSceneTracks({
+ tracks: updateTrackInSceneTracks({
+ tracks: tracksToUpdate,
+ trackId: this.sourceTrackId,
+ update: (track) => ({
+ ...track,
+ elements: track.elements.filter(
+ (trackElement) => trackElement.id !== this.elementId,
+ ),
+ }),
+ }),
+ trackId: this.targetTrackId,
+ update: (track) => ({
+ ...track,
+ elements: [...track.elements, movedElement],
+ }),
+ });
editor.timeline.updateTracks(updatedTracks);
return undefined;
@@ -132,3 +146,41 @@ export class MoveElementCommand extends Command {
}
}
}
+
+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),
+ ],
+ };
+}