2026-01-07 09:31:02 +00:00
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|
|
|
|
import { useAudio } from '../../context/AudioContext';
|
|
|
|
|
import { Mic2, AlignLeft } from 'lucide-react';
|
|
|
|
|
|
|
|
|
|
export const LyricsPanel: React.FC = () => {
|
|
|
|
|
const { currentTrack, currentTime, seek, duration } = useAudio();
|
|
|
|
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
const [autoScroll, setAutoScroll] = useState(true);
|
|
|
|
|
|
|
|
|
|
// Auto-scroll logic
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (autoScroll && scrollRef.current && currentTrack?.lyrics) {
|
2026-01-13 18:47:57 +00:00
|
|
|
const activeIndex = currentTrack.lyrics.findIndex(
|
|
|
|
|
(line: { time: number; text: string }, i: number) => {
|
|
|
|
|
return (
|
|
|
|
|
currentTime >= line.time &&
|
|
|
|
|
(i === currentTrack.lyrics!.length - 1 ||
|
|
|
|
|
currentTime < currentTrack.lyrics![i + 1].time)
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (activeIndex !== -1) {
|
|
|
|
|
const element = scrollRef.current.children[activeIndex] as HTMLElement;
|
|
|
|
|
if (element) {
|
|
|
|
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-07 09:31:02 +00:00
|
|
|
}
|
|
|
|
|
}, [currentTime, currentTrack, autoScroll]);
|
|
|
|
|
|
|
|
|
|
if (!currentTrack?.lyrics) {
|
2026-01-13 18:47:57 +00:00
|
|
|
return (
|
2026-02-07 14:33:31 +00:00
|
|
|
<div className="flex flex-col items-center justify-center h-full text-muted-foreground opacity-50">
|
2026-01-13 18:47:57 +00:00
|
|
|
<Mic2 className="w-16 h-16 mb-4" />
|
|
|
|
|
<p>No lyrics available</p>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2026-01-07 09:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2026-01-13 18:47:57 +00:00
|
|
|
<div
|
|
|
|
|
className="h-full flex flex-col relative group"
|
|
|
|
|
onMouseEnter={() => setAutoScroll(false)}
|
|
|
|
|
onMouseLeave={() => setAutoScroll(true)}
|
|
|
|
|
>
|
|
|
|
|
<div className="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setAutoScroll(!autoScroll)}
|
2026-02-07 14:33:31 +00:00
|
|
|
className={`p-2 rounded-full backdrop-blur-md ${autoScroll ? 'bg-primary/20 text-primary' : 'bg-black/30 text-muted-foreground'}`}
|
2026-01-13 18:47:57 +00:00
|
|
|
title="Auto-scroll"
|
|
|
|
|
>
|
|
|
|
|
<AlignLeft className="w-4 h-4" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
ref={scrollRef}
|
aesthetic-improvements: align spacing to 8px grid (Action 11.2.1.3)
- Created automated script (scripts/align-8px-grid.py) to align all spacing to 8px grid
- Replaced non-8px-aligned spacing: gap-3/p-3/m-3 (12px) → gap-4/p-4/m-4 (16px), gap-5/p-5/m-5 (20px) → gap-6/p-6/m-6 (24px), gap-10/p-10/m-10 (40px) → gap-12/p-12/m-12 (48px), gap-20/p-20/m-20 (80px) → gap-24/p-24/m-24 (96px)
- Preserved: 4px values (gap-1, p-1, m-1) as they may be intentional fine-tuning, responsive breakpoints (sm:, md:, lg:), test files, documentation
- Modified files across all components to ensure consistent 8px grid alignment
- Action 11.2.1.3: Align all elements to 8px grid - COMPLETE
2026-01-16 10:50:46 +00:00
|
|
|
className="flex-1 overflow-y-auto custom-scrollbar px-4 space-y-6 text-center mask-image-linear-to-b py-24"
|
2026-01-13 18:47:57 +00:00
|
|
|
>
|
|
|
|
|
{currentTrack.lyrics.map(
|
|
|
|
|
(line: { time: number; text: string }, i: number) => {
|
|
|
|
|
const isActive =
|
|
|
|
|
currentTime >= line.time &&
|
|
|
|
|
(i === currentTrack.lyrics!.length - 1 ||
|
|
|
|
|
currentTime < currentTrack.lyrics![i + 1].time);
|
|
|
|
|
return (
|
|
|
|
|
<p
|
|
|
|
|
key={i}
|
2026-02-12 01:09:29 +00:00
|
|
|
className={`text-2xl md:text-3xl font-bold transition-all duration-[var(--sumi-duration-slow)] cursor-pointer hover:text-foreground ${isActive ? 'text-foreground scale-105 origin-center' : 'text-white/20 blur-[1px]'}`}
|
2026-01-13 18:47:57 +00:00
|
|
|
onClick={() => seek((line.time / duration) * 100)}
|
|
|
|
|
>
|
|
|
|
|
{line.text}
|
|
|
|
|
</p>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|