diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-31 15:53:23 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-31 15:53:23 +0200 |
| commit | d23be079cce6108482fc3e85401cccf949087174 (patch) | |
| tree | 7d5acd324f9274b3b823bc67154349b8693e3412 /apps/web/src/lib/timeline/track-utils.ts | |
| parent | dcf54c1250431f5a4a2848f7c07781ef5eebacaf (diff) | |
| parent | 70a6d58b8432e455ab1443d45bc23dd70b363bea (diff) | |
| download | opencut-d23be079cce6108482fc3e85401cccf949087174.tar.gz | |
Merge branch 'feature/track-placement'
Diffstat (limited to 'apps/web/src/lib/timeline/track-utils.ts')
| -rw-r--r-- | apps/web/src/lib/timeline/track-utils.ts | 235 |
1 files changed, 0 insertions, 235 deletions
diff --git a/apps/web/src/lib/timeline/track-utils.ts b/apps/web/src/lib/timeline/track-utils.ts index 0034c72e..cc9e67c4 100644 --- a/apps/web/src/lib/timeline/track-utils.ts +++ b/apps/web/src/lib/timeline/track-utils.ts @@ -1,21 +1,17 @@ import type {
TrackType,
TimelineTrack,
- ElementType,
VideoTrack,
AudioTrack,
GraphicTrack,
TextTrack,
EffectTrack,
- TimelineElement,
} from "@/lib/timeline";
import {
- ELEMENT_TRACK_MAP,
TRACK_CONFIG,
ELEMENT_TYPE_CONFIG,
TRACK_GAP,
} from "@/constants/timeline-constants";
-import { generateUUID } from "@/utils/id";
export function canTracktHaveAudio(
track: TimelineTrack,
@@ -64,234 +60,3 @@ export function getTotalTracksHeight({ const gapsHeight = Math.max(0, tracks.length - 1) * TRACK_GAP;
return tracksHeight + gapsHeight;
}
-
-export function buildEmptyTrack({
- id,
- type,
- name,
-}: {
- id: string;
- type: TrackType;
- name?: string;
-}): TimelineTrack {
- const trackName = name ?? TRACK_CONFIG[type].defaultName;
-
- switch (type) {
- case "video":
- return {
- id,
- name: trackName,
- type: "video",
- elements: [],
- hidden: false,
- muted: false,
- isMain: false,
- };
- case "text":
- return {
- id,
- name: trackName,
- type: "text",
- elements: [],
- hidden: false,
- };
- case "graphic":
- return {
- id,
- name: trackName,
- type: "graphic",
- elements: [],
- hidden: false,
- };
- case "audio":
- return {
- id,
- name: trackName,
- type: "audio",
- elements: [],
- muted: false,
- };
- case "effect":
- return {
- id,
- name: trackName,
- type: "effect",
- elements: [],
- hidden: false,
- };
- default:
- throw new Error(`Unsupported track type: ${type}`);
- }
-}
-
-export function getDefaultInsertIndexForTrack({
- tracks,
- trackType,
-}: {
- tracks: TimelineTrack[];
- trackType: TrackType;
-}): number {
- if (trackType === "audio") {
- return tracks.length;
- }
-
- if (trackType === "effect") {
- return 0;
- }
-
- const mainTrackIndex = tracks.findIndex((track) => isMainTrack(track));
- if (mainTrackIndex >= 0) {
- return mainTrackIndex;
- }
-
- const firstAudioTrackIndex = tracks.findIndex(
- (track) => track.type === "audio",
- );
- if (firstAudioTrackIndex >= 0) {
- return firstAudioTrackIndex;
- }
-
- return tracks.length;
-}
-
-export function getHighestInsertIndexForTrack({
- tracks,
- trackType,
-}: {
- tracks: TimelineTrack[];
- trackType: TrackType;
-}): number {
- const mainTrackIndex = tracks.findIndex((track) => isMainTrack(track));
-
- if (trackType === "audio") {
- return mainTrackIndex >= 0 ? mainTrackIndex + 1 : tracks.length;
- }
-
- return 0;
-}
-
-export function isMainTrack(track: TimelineTrack): track is VideoTrack {
- return track.type === "video" && track.isMain === true;
-}
-
-export function getMainTrack({
- tracks,
-}: {
- tracks: TimelineTrack[];
-}): TimelineTrack | null {
- return tracks.find((track) => isMainTrack(track)) ?? null;
-}
-
-export function ensureMainTrack({
- tracks,
-}: {
- tracks: TimelineTrack[];
-}): TimelineTrack[] {
- const hasMainTrack = tracks.some((track) => isMainTrack(track));
-
- if (!hasMainTrack) {
- const mainTrack: TimelineTrack = {
- id: generateUUID(),
- name: "Main Track",
- type: "video",
- elements: [],
- muted: false,
- isMain: true,
- hidden: false,
- };
- return [mainTrack, ...tracks];
- }
-
- return tracks;
-}
-
-export function canElementGoOnTrack({
- elementType,
- trackType,
-}: {
- elementType: ElementType;
- trackType: TrackType;
-}): boolean {
- return ELEMENT_TRACK_MAP[elementType] === trackType;
-}
-
-export function validateElementTrackCompatibility({
- element,
- track,
-}: {
- element: { type: ElementType };
- track: { type: TrackType };
-}): { isValid: boolean; errorMessage?: string } {
- const isValid = canElementGoOnTrack({
- elementType: element.type,
- trackType: track.type,
- });
-
- if (!isValid) {
- return {
- isValid: false,
- errorMessage: `${element.type} elements cannot be placed on ${track.type} tracks`,
- };
- }
-
- return { isValid: true };
-}
-
-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) => !excludeElementId || element.id !== excludeElementId,
- );
-
- if (elements.length === 0) {
- return null;
- }
-
- return elements.reduce((earliest, element) =>
- element.startTime < earliest.startTime ? element : earliest,
- );
-}
-
-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;
- }
-
- // main track must always start at time 0; if this element would
- // become the earliest, pin it to the start
- if (requestedStartTime <= earliestElement.startTime) {
- return 0;
- }
-
- return requestedStartTime;
-}
|
