diff --git a/src/components/AudioWaveform.tsx b/src/components/AudioWaveform.tsx new file mode 100644 index 00000000..f1bbd8f4 --- /dev/null +++ b/src/components/AudioWaveform.tsx @@ -0,0 +1,514 @@ +"use client"; + +import { useEffect, useRef, useCallback, useState, memo } from "react"; +import { ZoomIn, ZoomOut } from "lucide-react"; + +interface Props { + samples: number[]; + duration: number; + currentTime: number; + trimStart: number; + trimEnd: number | null; + loading: boolean; + hasAudio: boolean; + onTrimStartChange: (sec: number) => void; + onTrimEndChange: (sec: number) => void; + onSeek: (sec: number) => void; +} + +const BAR_HEIGHT_RATIO = 0.85; +const MIN_ZOOM = 1; +const MAX_ZOOM = 20; +const HANDLE_WIDTH = 12; + +function getCssVar(name: string, fallback: string): string { + if (typeof window === "undefined") return fallback; + return getComputedStyle(document.documentElement).getPropertyValue(name).trim() || fallback; +} + +function formatTime(seconds: number): string { + const m = Math.floor(seconds / 60); + const s = Math.floor(seconds % 60); + return `${m}:${s.toString().padStart(2, "0")}`; +} + +const Playhead = memo(function Playhead({ position }: { position: number }) { + return ( +