import { useRef, useCallback, useState, useEffect } from "react"; interface TimelineProps { duration: number; currentTime: number; trimStart: number; trimEnd: number; onTrimChange: (start: number, end: number) => void; onSeek: (time: number) => void; } function formatTime(seconds: number): string { const m = Math.floor(seconds / 60); const s = Math.floor(seconds % 60); const ms = Math.floor((seconds % 1) * 10); return `${m}:${s.toString().padStart(2, "0")}.${ms}`; } export default function Timeline({ duration, currentTime, trimStart, trimEnd, onTrimChange, onSeek, }: TimelineProps) { const trackRef = useRef(null); const [dragging, setDragging] = useState<"in" | "out" | null>(null); const timeToPercent = (t: number) => (duration > 0 ? (t / duration) * 100 : 0); const positionToTime = useCallback( (clientX: number) => { const track = trackRef.current; if (!track || duration <= 0) return 0; const rect = track.getBoundingClientRect(); const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)); return ratio * duration; }, [duration], ); const handleTrackClick = (e: React.MouseEvent) => { if (dragging) return; onSeek(positionToTime(e.clientX)); }; const handleMouseDown = (handle: "in" | "out") => (e: React.MouseEvent) => { e.stopPropagation(); setDragging(handle); }; useEffect(() => { if (!dragging) return; const minGap = 0.1; const handleMove = (e: MouseEvent) => { const time = positionToTime(e.clientX); if (dragging === "in") { onTrimChange(Math.min(time, trimEnd - minGap), trimEnd); } else { onTrimChange(trimStart, Math.max(time, trimStart + minGap)); } }; const handleUp = () => setDragging(null); document.addEventListener("mousemove", handleMove); document.addEventListener("mouseup", handleUp); return () => { document.removeEventListener("mousemove", handleMove); document.removeEventListener("mouseup", handleUp); }; }, [dragging, trimStart, trimEnd, positionToTime, onTrimChange]); const inPct = timeToPercent(trimStart); const outPct = timeToPercent(trimEnd); const playheadPct = timeToPercent(currentTime); const selectionDuration = trimEnd - trimStart; return (
In: {formatTime(trimStart)} Selection: {formatTime(selectionDuration)} Out: {formatTime(trimEnd)}
{/* Dimmed regions */}
{/* Selection highlight */}
{/* Playhead */}
{/* Handles */}
0:00 {formatTime(duration)}
); }