95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
'use client'
|
|
|
|
import { useMusicPlayer } from './music-provider'
|
|
import { FullScreenPlayer } from './full-screen-player'
|
|
import { Slider } from '@/components/ui/slider'
|
|
import { Play, Pause, SkipBack, SkipForward } from 'lucide-react'
|
|
|
|
function formatTime(secs: number) {
|
|
if (!secs || !isFinite(secs)) return '0:00'
|
|
const m = Math.floor(secs / 60)
|
|
const s = Math.floor(secs % 60)
|
|
return `${m}:${s.toString().padStart(2, '0')}`
|
|
}
|
|
|
|
export function MiniPlayer() {
|
|
const { state, togglePlay, seek, nextTrack, prevTrack, setFullScreen } = useMusicPlayer()
|
|
|
|
if (!state.currentTrack) return null
|
|
|
|
const track = state.currentTrack
|
|
|
|
return (
|
|
<>
|
|
<div className="fixed bottom-0 inset-x-0 z-50 bg-card border-t border-border shadow-lg">
|
|
{/* Progress bar (thin, above the player) */}
|
|
<div className="px-2">
|
|
<Slider
|
|
value={[state.progress]}
|
|
max={state.duration || 1}
|
|
step={1}
|
|
onValueChange={([v]) => seek(v)}
|
|
className="h-1 [&_[data-slot=slider]]:h-1 [&_span[data-slot=scroll-bar]]:hidden"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3 px-4 py-2 h-14">
|
|
{/* Cover art - clickable to open fullscreen */}
|
|
<button
|
|
onClick={() => setFullScreen(true)}
|
|
className="flex-shrink-0 w-10 h-10 rounded-md overflow-hidden bg-muted"
|
|
>
|
|
{track.coverArt ? (
|
|
<img
|
|
src={`/api/music/cover/${track.coverArt}?size=80`}
|
|
alt={track.album}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
) : (
|
|
<div className="w-full h-full bg-muted" />
|
|
)}
|
|
</button>
|
|
|
|
{/* Track info - clickable to open fullscreen */}
|
|
<button
|
|
onClick={() => setFullScreen(true)}
|
|
className="flex-1 min-w-0 text-left"
|
|
>
|
|
<div className="text-sm font-medium truncate">{track.title}</div>
|
|
<div className="text-xs text-muted-foreground truncate">{track.artist}</div>
|
|
</button>
|
|
|
|
{/* Time */}
|
|
<span className="text-xs text-muted-foreground tabular-nums hidden sm:block">
|
|
{formatTime(state.progress)} / {formatTime(state.duration)}
|
|
</span>
|
|
|
|
{/* Controls */}
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
onClick={prevTrack}
|
|
className="p-1.5 hover:bg-muted/50 rounded-full transition-colors"
|
|
>
|
|
<SkipBack className="h-4 w-4" />
|
|
</button>
|
|
<button
|
|
onClick={togglePlay}
|
|
className="p-2 bg-primary text-primary-foreground rounded-full hover:bg-primary/90 transition-colors"
|
|
>
|
|
{state.isPlaying ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4 ml-0.5" />}
|
|
</button>
|
|
<button
|
|
onClick={nextTrack}
|
|
className="p-1.5 hover:bg-muted/50 rounded-full transition-colors"
|
|
>
|
|
<SkipForward className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<FullScreenPlayer />
|
|
</>
|
|
)
|
|
}
|