import React, { useMemo, useRef, useEffect, useState } from 'react' import { transposeChord } from './ChordDisplay' const VISIBLE_SIDES = 3 // number of chords to show on each side function LiveChordView({ chordsData, currentTime, isPlaying, semitones = 0, onSeek }) { const containerRef = useRef(null) const [prevIndex, setPrevIndex] = useState(-1) const chords = chordsData?.chords || [] const hasChords = chords.length > 0 // Binary search for active chord const activeIndex = useMemo(() => { if (!hasChords) return -1 let lo = 0, hi = chords.length - 1 while (lo <= hi) { const mid = (lo + hi) >> 1 if (currentTime < chords[mid].start_time) hi = mid - 1 else if (currentTime >= chords[mid].end_time) lo = mid + 1 else return mid } return -1 }, [chords, currentTime, hasChords]) // Track previous index for transition direction useEffect(() => { if (activeIndex !== -1 && activeIndex !== prevIndex) { setPrevIndex(activeIndex) } }, [activeIndex, prevIndex]) if (!chordsData || !hasChords) return null // Progress within the current chord (0-1) const chordProgress = activeIndex >= 0 ? (() => { const c = chords[activeIndex] const span = c.end_time - c.start_time return span > 0 ? Math.max(0, Math.min(1, (currentTime - c.start_time) / span)) : 0 })() : 0 // Build the visible window of chords const windowStart = Math.max(0, (activeIndex >= 0 ? activeIndex : 0) - VISIBLE_SIDES) const windowEnd = Math.min(chords.length - 1, (activeIndex >= 0 ? activeIndex : 0) + VISIBLE_SIDES) const visibleChords = [] for (let i = windowStart; i <= windowEnd; i++) { visibleChords.push({ ...chords[i], index: i }) } return (
{/* Teleprompter row */}
{visibleChords.map((entry) => { const i = entry.index const isActive = i === activeIndex const isPast = activeIndex >= 0 && i < activeIndex const isFuture = activeIndex >= 0 && i > activeIndex const displayChord = transposeChord(entry.chord, semitones) const isNC = displayChord === 'N.C.' // Distance from active chord (0 = active, 1 = adjacent, etc.) const distance = activeIndex >= 0 ? Math.abs(i - activeIndex) : 0 // Opacity and scale based on distance let opacity, scale, textSize if (isActive) { opacity = 1 scale = 1 textSize = 'text-5xl' } else if (distance === 1) { opacity = isPast ? 0.35 : 0.5 scale = 0.7 textSize = 'text-2xl' } else if (distance === 2) { opacity = isPast ? 0.18 : 0.25 scale = 0.55 textSize = 'text-xl' } else { opacity = isPast ? 0.08 : 0.12 scale = 0.45 textSize = 'text-lg' } if (isNC && !isActive) { opacity *= 0.4 } return ( ) })}
{/* Subtle label */} {activeIndex >= 0 && (

Now Playing

)}
) } export default React.memo(LiveChordView)