import type { Scene } from "@/lib/studio-types"; export const TIMELINE_ZOOM_LEVELS = [30, 60, 90, 120] as const; export type TimelineZoomLevel = (typeof TIMELINE_ZOOM_LEVELS)[number]; export const DEFAULT_PX_PER_SECOND: TimelineZoomLevel = 60; /** Compact scale for scene thumbnail strip (Renderforest-style) */ export const STRIP_PX_PER_SECOND = 24; export const MIN_SCENE_DURATION = 1; export const MAX_SCENE_DURATION = 30; export const SCENE_BLOCK_COLORS = [ { base: "bg-blue-600", active: "bg-blue-500" }, { base: "bg-purple-600", active: "bg-purple-500" }, { base: "bg-green-600", active: "bg-green-500" }, { base: "bg-orange-600", active: "bg-orange-500" }, ] as const; /** Inline gradient styles for scene thumbnails — avoids Tailwind purging dynamic class names */ export const SCENE_THUMB_GRADIENTS = [ { backgroundImage: "linear-gradient(135deg,#60a5fa,#8b5cf6)" }, { backgroundImage: "linear-gradient(135deg,#a78bfa,#ec4899)" }, { backgroundImage: "linear-gradient(135deg,#22d3ee,#3b82f6)" }, { backgroundImage: "linear-gradient(135deg,#34d399,#14b8a6)" }, { backgroundImage: "linear-gradient(135deg,#fbbf24,#f97316)" }, { backgroundImage: "linear-gradient(135deg,#fb7185,#ef4444)" }, ] as const; export function formatTimelineTime(seconds: number): string { const safe = Math.max(0, seconds); const mins = Math.floor(safe / 60); const secs = Math.floor(safe % 60); return `${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`; } export function getProjectDuration(scenes: Scene[]): number { return scenes.reduce((total, scene) => total + scene.duration, 0); } export interface SceneTimelineSegment { scene: Scene; startTime: number; index: number; } export function getSceneTimelineSegments( scenes: Scene[] ): SceneTimelineSegment[] { let startTime = 0; return scenes.map((scene, index) => { const segment = { scene, startTime, index }; startTime += scene.duration; return segment; }); } export function getSceneAtTime(scenes: Scene[], time: number): Scene | undefined { const segments = getSceneTimelineSegments(scenes); if (segments.length === 0) return undefined; const total = getProjectDuration(scenes); if (time >= total) { return segments[segments.length - 1]?.scene; } return segments.find( (segment) => time >= segment.startTime && time < segment.startTime + segment.scene.duration )?.scene; } export function getSceneStartTime(scenes: Scene[], sceneId: string): number { let start = 0; for (const scene of scenes) { if (scene.id === sceneId) return start; start += scene.duration; } return 0; } export function clampSceneDuration(duration: number): number { return Math.min( MAX_SCENE_DURATION, Math.max(MIN_SCENE_DURATION, Math.round(duration * 10) / 10) ); } export function getNextZoomLevel( current: number, direction: "in" | "out" ): TimelineZoomLevel { const index = TIMELINE_ZOOM_LEVELS.findIndex((level) => level === current); const resolvedIndex = index === -1 ? 1 : index; if (direction === "in") { return TIMELINE_ZOOM_LEVELS[ Math.min(resolvedIndex + 1, TIMELINE_ZOOM_LEVELS.length - 1) ]; } return TIMELINE_ZOOM_LEVELS[Math.max(resolvedIndex - 1, 0)]; } export function snapZoomLevel(value: number): TimelineZoomLevel { const snapped = TIMELINE_ZOOM_LEVELS.reduce((prev, level) => Math.abs(level - value) < Math.abs(prev - value) ? level : prev ); return snapped; }