From 70a6d58b8432e455ab1443d45bc23dd70b363bea Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Tue, 31 Mar 2026 15:47:30 +0200 Subject: refactor: centralize track placement into its own module --- apps/web/src/lib/timeline/placement/main-track.ts | 96 +++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 apps/web/src/lib/timeline/placement/main-track.ts (limited to 'apps/web/src/lib/timeline/placement/main-track.ts') diff --git a/apps/web/src/lib/timeline/placement/main-track.ts b/apps/web/src/lib/timeline/placement/main-track.ts new file mode 100644 index 00000000..1783352c --- /dev/null +++ b/apps/web/src/lib/timeline/placement/main-track.ts @@ -0,0 +1,96 @@ +import type { TimelineElement, TimelineTrack, VideoTrack } from "@/lib/timeline"; +import { generateUUID } from "@/utils/id"; + +const MAIN_TRACK_NAME = "Main Track"; + +export function isMainTrack(track: TimelineTrack): track is VideoTrack { + return track.type === "video" && track.isMain === true; +} + +export function getMainTrack({ + tracks, +}: { + tracks: TimelineTrack[]; +}): VideoTrack | null { + return tracks.find((track) => isMainTrack(track)) ?? null; +} + +export function ensureMainTrack({ + tracks, +}: { + tracks: TimelineTrack[]; +}): TimelineTrack[] { + if (tracks.some((track) => isMainTrack(track))) { + return tracks; + } + + return [ + { + id: generateUUID(), + name: MAIN_TRACK_NAME, + type: "video", + elements: [], + muted: false, + isMain: true, + hidden: false, + }, + ...tracks, + ]; +} + +export function getEarliestMainTrackElement({ + tracks, + excludeElementId, +}: { + tracks: TimelineTrack[]; + excludeElementId?: string; +}): TimelineElement | null { + const mainTrack = getMainTrack({ tracks }); + if (!mainTrack) { + return null; + } + + const elements = mainTrack.elements.filter((element) => { + return !excludeElementId || element.id !== excludeElementId; + }); + if (elements.length === 0) { + return null; + } + + return elements.reduce((earliestElement, element) => { + return element.startTime < earliestElement.startTime + ? element + : earliestElement; + }); +} + +export function enforceMainTrackStart({ + tracks, + targetTrackId, + requestedStartTime, + excludeElementId, +}: { + tracks: TimelineTrack[]; + targetTrackId: string; + requestedStartTime: number; + excludeElementId?: string; +}): number { + const mainTrack = getMainTrack({ tracks }); + if (!mainTrack || mainTrack.id !== targetTrackId) { + return requestedStartTime; + } + + const earliestElement = getEarliestMainTrackElement({ + tracks, + excludeElementId, + }); + if (!earliestElement) { + return 0; + } + + if (requestedStartTime <= earliestElement.startTime) { + return 0; + } + + return requestedStartTime; +} -- cgit v1.3.1