import { useRef } from 'react'; import { useClient } from 'hooks/useClient'; import { MicVocal, Pause, Play } from 'lucide-react'; import { SeekBar } from 'officerdev'; import { coverUrl, fmtClock } from './shared'; import { seekPlayer } from 'officerdev'; import { useLyricsOpen } from 'officerdev'; import { useMusicPlayer } from 'officerdev'; import { usePlayerClock } from './usePlayerClock'; /** * The player, reduced to what the /music screen does not already show. The album view has the transport * and the tracklist, so this is the scrubber — plus play/pause and the lyrics toggle, which are the two * controls you can still want while browsing an album that ISN'T the one playing. * * It sits inside the detail panel, which is why the full dock hides on /music: two bars would be one bar * too many, and the dock's own row costs the workspace its height on every screen. */ export const MusicMiniBar = () => { const { token } = useClient(); const { current, playing, toggle } = useMusicPlayer(); const [lyricsOpen, toggleLyrics] = useLyricsOpen(); const { position, duration } = usePlayerClock(); const barRef = useRef(null); if (!current) return null; const onSeekDown = (ev: React.MouseEvent) => { const seekAt = (clientX: number) => { const bar = barRef.current; if (!bar || !duration) return; const rect = bar.getBoundingClientRect(); seekPlayer(Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)) * duration); }; ev.preventDefault(); seekAt(ev.clientX); const onMove = (moveEv: MouseEvent) => seekAt(moveEv.clientX); const onUp = () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); }; window.addEventListener('mousemove', onMove); window.addEventListener('mouseup', onUp); }; return (
{ (ev.currentTarget as HTMLImageElement).style.visibility = 'hidden'; }} />

{current.title ?? current.file}

{current.artist &&

{current.artist}

}
{fmtClock(position)}
{fmtClock(duration)}
); };