'use client' import { useMemo, useRef, useEffect } from 'react' interface LyricLine { time: number // seconds text: string } function parseLRC(lrc: string): LyricLine[] { const lines: LyricLine[] = [] for (const raw of lrc.split('\n')) { const match = raw.match(/^\[(\d{2}):(\d{2})\.(\d{2,3})\]\s?(.*)$/) if (!match) continue const mins = parseInt(match[1], 10) const secs = parseInt(match[2], 10) const ms = parseInt(match[3].padEnd(3, '0'), 10) const text = match[4].trim() if (!text) continue lines.push({ time: mins * 60 + secs + ms / 1000, text }) } return lines.sort((a, b) => a.time - b.time) } export function SyncedLyrics({ syncedLyrics, currentTime, onSeek, }: { syncedLyrics: string currentTime: number onSeek?: (time: number) => void }) { const lines = useMemo(() => parseLRC(syncedLyrics), [syncedLyrics]) const containerRef = useRef(null) const activeRef = useRef(null) // Find active line index let activeIndex = -1 for (let i = lines.length - 1; i >= 0; i--) { if (currentTime >= lines[i].time) { activeIndex = i break } } // Auto-scroll to active line useEffect(() => { if (activeRef.current && containerRef.current) { const container = containerRef.current const el = activeRef.current const containerRect = container.getBoundingClientRect() const elRect = el.getBoundingClientRect() // Center the active line in the visible area const targetScroll = el.offsetTop - container.offsetTop - containerRect.height / 2 + elRect.height / 2 container.scrollTo({ top: targetScroll, behavior: 'smooth' }) } }, [activeIndex]) if (lines.length === 0) return null return (

Lyrics

{/* Top padding so first line can center */}
{lines.map((line, i) => { const isActive = i === activeIndex const isPast = i < activeIndex return ( ) })} {/* Bottom padding so last line can center */}
) }