// A compact media-player control: big play/pause button, seekable progress // bar, and transport buttons. Presentation only — playback state and handlers // come from useAudio.js so the Home (whole book) and Reader (chapter) players // can share this UI. function formatTime(seconds) { if (!Number.isFinite(seconds) || seconds < 0) return '0:00'; const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${String(secs).padStart(2, '0')}`; } function PlayIcon() { return ( ); } function PauseIcon() { return ( ); } function StopIcon() { return ( ); } function Seek10Icon({ direction }) { const flip = direction === 'back' ? 'scale(-1,1) translate(-20,0)' : undefined; return ( 10 ); } function NextTrackIcon() { return ( ); } export default function AudioPlayer({ title, subtitle, errorMessage, status, // 'idle' | 'playing' | 'paused' | 'error' currentTime = 0, duration = 0, onPlay, onTogglePause, onSeekBack, onSeekForward, onSeekTo, onNext, nextDisabled, onStop, controls, // optional extra controls (e.g. book/narrator selects), rendered top-right }) { const isActive = status === 'playing' || status === 'paused'; const progressPct = duration > 0 ? Math.min(100, (currentTime / duration) * 100) : 0; return (

{title}

{subtitle}

{status === 'error' && errorMessage && (

{errorMessage}

)}
{controls &&
{controls}
}
{isActive && (
onSeekTo?.(Number(e.target.value))} disabled={!duration} className="h-1.5 w-full cursor-pointer appearance-none rounded-full bg-slate-200 accent-sky-600 disabled:cursor-default" style={{ background: `linear-gradient(to right, rgb(2 132 199) ${progressPct}%, rgb(226 232 240) ${progressPct}%)` }} aria-label="Seek" />
{formatTime(currentTime)} {formatTime(duration)}
)}
{isActive ? ( <> {onNext && ( )} ) : ( )}
); }