diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-07 04:41:27 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-07 04:41:27 +0200 |
| commit | cbd1b8212370c67b34a70dccd21bd82de72fead2 (patch) | |
| tree | d97cbee2aae22c22b59c59a50e9f8a6af0ae8ccf /apps/web/src/lib/timeline/placement/apply.ts | |
| parent | 4f7d401a974047d37673ee8e3840dceaaddc295c (diff) | |
| download | opencut-cbd1b8212370c67b34a70dccd21bd82de72fead2.tar.gz | |
refactor: structured track layout per scene
Diffstat (limited to 'apps/web/src/lib/timeline/placement/apply.ts')
| -rw-r--r-- | apps/web/src/lib/timeline/placement/apply.ts | 149 |
1 files changed, 131 insertions, 18 deletions
diff --git a/apps/web/src/lib/timeline/placement/apply.ts b/apps/web/src/lib/timeline/placement/apply.ts index 4ff5d50d..639b1b42 100644 --- a/apps/web/src/lib/timeline/placement/apply.ts +++ b/apps/web/src/lib/timeline/placement/apply.ts @@ -1,7 +1,18 @@ -import type { TimelineElement, TimelineTrack } from "@/lib/timeline"; +import type { + AudioTrack, + EffectTrack, + GraphicTrack, + OverlayTrack, + SceneTracks, + TextTrack, + TimelineElement, + TimelineTrack, + VideoTrack, +} from "@/lib/timeline"; import { generateUUID } from "@/utils/id"; import { buildEmptyTrack } from "./track-factory"; import type { PlacementResult } from "./types"; +import { updateTrackInSceneTracks } from "@/lib/timeline/track-element-update"; export function applyPlacement({ tracks, @@ -9,37 +20,139 @@ export function applyPlacement({ elements, newTrackInsertIndexOverride, }: { - tracks: TimelineTrack[]; + tracks: SceneTracks; placementResult: PlacementResult; elements: TimelineElement[]; newTrackInsertIndexOverride?: number; -}): { updatedTracks: TimelineTrack[]; targetTrackId: string } | null { +}): { updatedTracks: SceneTracks; targetTrackId: string } | null { + const orderedTracks = [...tracks.overlay, tracks.main, ...tracks.audio]; if (placementResult.kind === "existingTrack") { - const targetTrack = tracks[placementResult.trackIndex]; + const targetTrack = orderedTracks[placementResult.trackIndex]; if (!targetTrack) { return null; } - const updatedTracks = tracks.map((track, trackIndex) => - trackIndex === placementResult.trackIndex - ? { - ...track, - elements: [...track.elements, ...elements], - } - : track, - ) as TimelineTrack[]; + const updatedTracks = updateTrackInSceneTracks({ + tracks, + trackId: targetTrack.id, + update: (track) => ({ + ...track, + elements: [...track.elements, ...elements], + }), + }); return { updatedTracks, targetTrackId: targetTrack.id }; } const newTrackId = generateUUID(); - const newTrack = { - ...buildEmptyTrack({ id: newTrackId, type: placementResult.trackType }), - elements, - } as TimelineTrack; const insertIndex = newTrackInsertIndexOverride ?? placementResult.insertIndex; - const updatedTracks = [...tracks]; - updatedTracks.splice(insertIndex, 0, newTrack); + const updatedTracks = + placementResult.trackType === "audio" + ? { + ...tracks, + audio: insertIntoAudioTracks({ + tracks, + insertIndex, + track: buildPlacedAudioTrack({ + id: newTrackId, + elements, + }), + }), + } + : { + ...tracks, + overlay: insertIntoOverlayTracks({ + tracks, + insertIndex, + track: buildPlacedOverlayTrack({ + id: newTrackId, + type: placementResult.trackType, + elements, + }), + }), + }; return { updatedTracks, targetTrackId: newTrackId }; } + +function insertIntoOverlayTracks({ + tracks, + insertIndex, + track, +}: { + tracks: SceneTracks; + insertIndex: number; + track: OverlayTrack; +}): OverlayTrack[] { + const normalizedInsertIndex = Math.max( + 0, + Math.min(insertIndex, tracks.overlay.length), + ); + const nextTracks = [...tracks.overlay]; + nextTracks.splice(normalizedInsertIndex, 0, track); + return nextTracks; +} + +function insertIntoAudioTracks({ + tracks, + insertIndex, + track, +}: { + tracks: SceneTracks; + insertIndex: number; + track: AudioTrack; +}): AudioTrack[] { + const audioInsertIndex = Math.max( + 0, + Math.min(insertIndex - tracks.overlay.length - 1, tracks.audio.length), + ); + const nextTracks = [...tracks.audio]; + nextTracks.splice(audioInsertIndex, 0, track); + return nextTracks; +} + +function buildPlacedAudioTrack({ + id, + elements, +}: { + id: string; + elements: TimelineElement[]; +}): AudioTrack { + return { + ...buildEmptyTrack({ id, type: "audio" }), + elements: elements as AudioTrack["elements"], + }; +} + +function buildPlacedOverlayTrack({ + id, + type, + elements, +}: { + id: string; + type: Exclude<OverlayTrack["type"], "audio">; + elements: TimelineElement[]; +}): OverlayTrack { + switch (type) { + case "video": + return { + ...buildEmptyTrack({ id, type: "video" }), + elements: elements as VideoTrack["elements"], + }; + case "text": + return { + ...buildEmptyTrack({ id, type: "text" }), + elements: elements as TextTrack["elements"], + }; + case "graphic": + return { + ...buildEmptyTrack({ id, type: "graphic" }), + elements: elements as GraphicTrack["elements"], + }; + case "effect": + return { + ...buildEmptyTrack({ id, type: "effect" }), + elements: elements as EffectTrack["elements"], + }; + } +} |
