2026-02-08 21:48:08 +00:00
|
|
|
import React, { useState, useRef, useEffect } from 'react';
|
2026-01-26 18:18:52 +00:00
|
|
|
import { usePlayerStore } from '../store/playerStore';
|
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
|
import { Slider } from '@/components/ui/slider';
|
feat(ui): tooltip adoption + search highlighting & skeleton loading
Tooltip adoption (18 conversions across 11 files):
- Player controls: shuffle, repeat, mute, expand, close, lyrics, auto-scroll
- Navbar: theme toggle
- File browser: download, add tag, AI auto-tag, watermark, process with AI
- Notifications: mark as read
- Share links: open link, revoke link
- Chat: scroll to bottom
Search polish:
- New highlightMatch utility — wraps matching text in <mark> with primary color
- Applied to track titles, artist names, playlist names in SearchPageResults
- Applied to suggestion dropdown titles and subtitles
- Replaced spinner loading state with content-aware SearchPageSkeleton
- Skeleton matches actual results layout (tab bar, track cards, artist circles)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:14:00 +00:00
|
|
|
import { Tooltip } from '@/components/ui/tooltip';
|
2026-01-26 18:18:52 +00:00
|
|
|
import {
|
|
|
|
|
ChevronDown, Heart, MoreHorizontal, Share2,
|
2026-02-08 21:48:08 +00:00
|
|
|
Mic2, AlignLeft
|
2026-01-26 18:18:52 +00:00
|
|
|
} from 'lucide-react';
|
|
|
|
|
import { PlayPauseButton } from './PlayPauseButton'; // We might reuse or inline for consistent style
|
|
|
|
|
import { NextPreviousButtons } from './NextPreviousButtons';
|
|
|
|
|
import { RepeatShuffleButtons } from './RepeatShuffleButtons';
|
|
|
|
|
|
|
|
|
|
interface PlayerExpandedProps {
|
|
|
|
|
isOpen: boolean;
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
currentTime: number;
|
|
|
|
|
duration: number;
|
|
|
|
|
onSeek: (time: number) => void;
|
|
|
|
|
player: any; // Using the player hook object
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function PlayerExpanded({ isOpen, onClose, currentTime, duration, onSeek, player }: PlayerExpandedProps) {
|
|
|
|
|
const { currentTrack } = usePlayerStore();
|
2026-02-08 21:48:08 +00:00
|
|
|
const [showLyrics, setShowLyrics] = useState(false);
|
|
|
|
|
const [autoScrollLyrics, setAutoScrollLyrics] = useState(true);
|
|
|
|
|
const lyricsScrollRef = useRef<HTMLDivElement>(null);
|
2026-01-26 18:18:52 +00:00
|
|
|
|
|
|
|
|
if (!isOpen || !currentTrack) return null;
|
|
|
|
|
|
2026-02-08 21:48:08 +00:00
|
|
|
const lyrics = currentTrack.lyrics;
|
2026-01-26 18:18:52 +00:00
|
|
|
const formatTime = (seconds: number) => {
|
|
|
|
|
if (!seconds && seconds !== 0) return '0:00';
|
|
|
|
|
const m = Math.floor(seconds / 60);
|
|
|
|
|
const s = Math.floor(seconds % 60);
|
|
|
|
|
return `${m}:${s.toString().padStart(2, '0')}`;
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 21:48:08 +00:00
|
|
|
// Auto-scroll lyrics to active line
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!autoScrollLyrics || !lyrics?.length || !lyricsScrollRef.current) return;
|
|
|
|
|
const activeIndex = lyrics.findIndex(
|
|
|
|
|
(line, i) =>
|
|
|
|
|
currentTime >= line.time &&
|
2026-02-12 22:12:35 +00:00
|
|
|
(i === lyrics.length - 1 || currentTime < (lyrics[i + 1]?.time ?? Infinity))
|
2026-02-08 21:48:08 +00:00
|
|
|
);
|
|
|
|
|
if (activeIndex >= 0) {
|
|
|
|
|
const el = lyricsScrollRef.current.children[activeIndex] as HTMLElement;
|
|
|
|
|
el?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
|
|
}
|
|
|
|
|
}, [currentTime, lyrics, autoScrollLyrics]);
|
|
|
|
|
|
2026-01-26 18:18:52 +00:00
|
|
|
return (
|
|
|
|
|
<div className={cn(
|
fix: UI remediation Phase 1 (S0-S5) + Phase 2 Sprint 6 shadow system
Phase 1:
- S0: Fix open redirect (safeNavigate), delete AuthContext/legacy auth, encrypt API keys, gitignore .env files
- S1: Split client.ts god object into 5 modules, unify toast system, delete unused Sidebar
- S2: Add glass button variant, migrate 32 z-index to SUMI tokens, fix card dark mode
- S3: Skip nav link, aria-hidden on icons, focus-visible ring fixes, alt attrs, aria-live regions
- S4: React.memo on list items, fix key={index}, loading=lazy on images
- S5: Branded loading screen, page transitions respect reduced-motion, LikeButton micro-interaction, i18n sidebar/header
Phase 2 Sprint 6:
- Wire Tailwind shadow utilities to SUMI tokens in @theme block (fixes 50+ files)
- Define shadow-card/shadow-card-hover tokens
- Remove dark:shadow-none workarounds from card.tsx (SUMI handles per-theme shadows)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 09:13:44 +00:00
|
|
|
"fixed inset-0 z-[var(--sumi-z-popover)] bg-black/95 backdrop-blur-3xl overflow-hidden flex flex-col transition-all duration-[var(--sumi-duration-slow)]",
|
2026-01-26 18:18:52 +00:00
|
|
|
isOpen ? "opacity-100 translate-y-0" : "opacity-0 translate-y-full pointer-events-none"
|
|
|
|
|
)}>
|
|
|
|
|
{/* Dynamic Background */}
|
|
|
|
|
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
|
|
|
|
<div
|
|
|
|
|
className="absolute inset-0 bg-cover bg-center opacity-30 blur-[100px] scale-110 transition-all duration-1000"
|
|
|
|
|
style={{ backgroundImage: `url(${currentTrack.cover || '/placeholder.svg'})` }}
|
|
|
|
|
/>
|
|
|
|
|
<div className="absolute inset-0 bg-gradient-to-b from-black/20 via-black/60 to-black/90" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Header */}
|
|
|
|
|
<div className="relative z-10 flex items-center justify-between p-6">
|
refactor: Phase 6 — Migrate feature modules to SUMI tokens
- auth: Replace gray-* with muted/border tokens, text-foreground
- settings: TwoFactorSettings + NotificationSettings text-foreground
- chat: ChatInput, ConversationItem, ChatPage — text-foreground,
remove kodo references
- player: PlayerExpanded, PlayerQueue, PlayerControls — text-foreground,
remove cyan/magenta gradients
- playlists: All components — text-foreground for badges/headings
- tracks: TrackCard, TrackListRow — text-foreground, remove glow effects
- studio: FileGridCard — text-foreground
- library: LibraryPageGrid — remove hover-glow-cyan, shadow-card-glow-cyan
- profile: UserProfilePageHeader — text-foreground
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:06:28 +00:00
|
|
|
<Button variant="ghost" className="text-foreground hover:bg-white/10 rounded-full" onClick={onClose}>
|
2026-01-26 18:18:52 +00:00
|
|
|
<ChevronDown className="w-6 h-6" />
|
|
|
|
|
</Button>
|
|
|
|
|
<span className="text-xs font-bold tracking-widest uppercase text-white/50">Following the Signal</span>
|
refactor: Phase 6 — Migrate feature modules to SUMI tokens
- auth: Replace gray-* with muted/border tokens, text-foreground
- settings: TwoFactorSettings + NotificationSettings text-foreground
- chat: ChatInput, ConversationItem, ChatPage — text-foreground,
remove kodo references
- player: PlayerExpanded, PlayerQueue, PlayerControls — text-foreground,
remove cyan/magenta gradients
- playlists: All components — text-foreground for badges/headings
- tracks: TrackCard, TrackListRow — text-foreground, remove glow effects
- studio: FileGridCard — text-foreground
- library: LibraryPageGrid — remove hover-glow-cyan, shadow-card-glow-cyan
- profile: UserProfilePageHeader — text-foreground
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:06:28 +00:00
|
|
|
<Button variant="ghost" className="text-foreground hover:bg-white/10 rounded-full">
|
2026-01-26 18:18:52 +00:00
|
|
|
<MoreHorizontal className="w-6 h-6" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Main Content */}
|
2026-02-08 21:48:08 +00:00
|
|
|
<div className={cn(
|
2026-02-12 01:09:29 +00:00
|
|
|
"flex-1 flex flex-col md:flex-row items-center justify-center gap-12 px-8 pb-12 relative z-10 max-w-7xl mx-auto w-full transition-all duration-[var(--sumi-duration-slow)]",
|
2026-02-08 21:48:08 +00:00
|
|
|
showLyrics && "md:gap-8"
|
|
|
|
|
)}>
|
2026-01-26 18:18:52 +00:00
|
|
|
{/* Left: Album Art */}
|
2026-02-08 21:48:08 +00:00
|
|
|
<div className={cn(
|
2026-02-12 01:09:29 +00:00
|
|
|
"relative group transition-all duration-[var(--sumi-duration-slow)]",
|
2026-02-08 21:48:08 +00:00
|
|
|
showLyrics ? "w-full max-w-md md:max-w-sm aspect-square" : "w-full max-w-md md:max-w-xl aspect-square"
|
|
|
|
|
)}>
|
refactor: Phase 6 — Migrate feature modules to SUMI tokens
- auth: Replace gray-* with muted/border tokens, text-foreground
- settings: TwoFactorSettings + NotificationSettings text-foreground
- chat: ChatInput, ConversationItem, ChatPage — text-foreground,
remove kodo references
- player: PlayerExpanded, PlayerQueue, PlayerControls — text-foreground,
remove cyan/magenta gradients
- playlists: All components — text-foreground for badges/headings
- tracks: TrackCard, TrackListRow — text-foreground, remove glow effects
- studio: FileGridCard — text-foreground
- library: LibraryPageGrid — remove hover-glow-cyan, shadow-card-glow-cyan
- profile: UserProfilePageHeader — text-foreground
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:06:28 +00:00
|
|
|
<div className="absolute inset-0 bg-gradient-to-br from-primary/20 to-secondary/20 rounded-xl blur-2xl transform group-hover:scale-105 transition-transform duration-700" />
|
2026-01-26 18:18:52 +00:00
|
|
|
<img
|
|
|
|
|
src={currentTrack.cover || '/placeholder.svg'}
|
|
|
|
|
alt={currentTrack.title}
|
feat(web): UI premium Discord/Spotify-like — tokens, shadows, focus, layout
Plan UI premium 6–8 semaines (design system, shell, Storybook, a11y):
- Design system: DESIGN_TOKENS.md, APP_SHELL.md, FULL_LAYOUT_PAGE.md. Single source
for layout/shell (index.css), shadows (design-system.css), durations/easing.
- Tokens: shadow-cover-depth, shadow-gold-glow, shadow-fab-glow; layout max-height
(max-h-layout-drawer, max-h-layout-panel, max-h-layout-list). All duration-200/300/500
replaced by --duration-fast/normal/slow. Arbitrary shadows replaced by token classes.
- Shell & player: Sidebar, Header, GlobalPlayer, MiniPlayer, PlayerQueue, PlayerControls,
AudioPlayer use tokens; focus-visible on Sidebar, PlayerQueue, DropdownMenuTrigger/Item,
TabsTrigger. Typography: text-[10px]/[9px] → text-xs where applicable.
- ESLint: no-restricted-syntax (warn) for w-/h-/rounded-/shadow-/text-/spacing arbitrary.
- Scripts: report-arbitrary-values.mjs, capture/compare/generate visual; visual-complete.spec.ts.
- Stories full layout: Dashboard, Playlists, Library, Settings, Profile in DashboardLayout.stories.
- .cursorrules + README: DESIGN_TOKENS, APP_SHELL, visual commands, no arbitrary without justification.
- apps/web/.gitignore: e2e test artifacts (test-results-visual, playwright-report-visual).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 16:15:58 +00:00
|
|
|
className="w-full h-full object-cover rounded-xl shadow-cover-depth relative z-10 border border-white/10"
|
2026-01-26 18:18:52 +00:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Right: Info & Controls */}
|
|
|
|
|
<div className="w-full max-w-xl flex flex-col justify-end space-y-8">
|
|
|
|
|
|
|
|
|
|
<div className="flex items-end justify-between">
|
|
|
|
|
<div className="space-y-2">
|
refactor: Phase 6 — Migrate feature modules to SUMI tokens
- auth: Replace gray-* with muted/border tokens, text-foreground
- settings: TwoFactorSettings + NotificationSettings text-foreground
- chat: ChatInput, ConversationItem, ChatPage — text-foreground,
remove kodo references
- player: PlayerExpanded, PlayerQueue, PlayerControls — text-foreground,
remove cyan/magenta gradients
- playlists: All components — text-foreground for badges/headings
- tracks: TrackCard, TrackListRow — text-foreground, remove glow effects
- studio: FileGridCard — text-foreground
- library: LibraryPageGrid — remove hover-glow-cyan, shadow-card-glow-cyan
- profile: UserProfilePageHeader — text-foreground
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:06:28 +00:00
|
|
|
<h2 className="text-4xl md:text-5xl font-heading font-bold text-foreground leading-tight">
|
2026-01-26 18:18:52 +00:00
|
|
|
{currentTrack.title}
|
|
|
|
|
</h2>
|
|
|
|
|
<p className="text-xl md:text-2xl text-muted-foreground font-medium">
|
|
|
|
|
{currentTrack.artist}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2026-02-08 22:26:34 +00:00
|
|
|
<Button size="icon" variant="ghost" className="text-muted-foreground hover:text-destructive hover:bg-destructive/10 rounded-full h-12 w-12 transition-all">
|
2026-01-26 18:18:52 +00:00
|
|
|
<Heart className="w-6 h-6" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Progress */}
|
|
|
|
|
<div className="space-y-4 group/progress">
|
|
|
|
|
<Slider
|
|
|
|
|
value={[currentTime]}
|
|
|
|
|
onValueChange={(val) => onSeek(val[0])}
|
|
|
|
|
max={duration || 100}
|
|
|
|
|
step={0.1}
|
|
|
|
|
className="py-2"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex items-center justify-between text-xs font-mono text-muted-foreground">
|
|
|
|
|
<span>{formatTime(currentTime)}</span>
|
|
|
|
|
<span>{formatTime(duration)}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Controls */}
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
{/* Todo: Reuse or reimplement buttons with larger sizes */}
|
|
|
|
|
<RepeatShuffleButtons
|
|
|
|
|
repeat={player.repeat}
|
|
|
|
|
shuffle={player.shuffle}
|
|
|
|
|
onRepeatChange={player.setRepeat}
|
|
|
|
|
onShuffleToggle={player.toggleShuffle}
|
|
|
|
|
size="lg"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-6 md:gap-8">
|
|
|
|
|
<NextPreviousButtons
|
|
|
|
|
onNext={player.next}
|
|
|
|
|
onPrevious={player.previous}
|
|
|
|
|
canGoNext={true}
|
|
|
|
|
canGoPrevious={true}
|
|
|
|
|
size="lg"
|
|
|
|
|
/>
|
|
|
|
|
<PlayPauseButton
|
|
|
|
|
isPlaying={player.isPlaying}
|
|
|
|
|
onClick={() => player.isPlaying ? player.pause() : player.resume()}
|
|
|
|
|
size="xl" // We need to support 'xl' maybe or modify the component
|
|
|
|
|
className="scale-125"
|
|
|
|
|
/>
|
|
|
|
|
<NextPreviousButtons
|
|
|
|
|
onNext={player.next}
|
|
|
|
|
onPrevious={player.previous}
|
|
|
|
|
canGoNext={true}
|
|
|
|
|
canGoPrevious={true}
|
|
|
|
|
size="lg"
|
|
|
|
|
className="hidden" // HACK: reusing comp just for previous button structure if needed
|
|
|
|
|
/>
|
|
|
|
|
{/* Wait, NextPrevious contains both buttons. I was using it wrong above. */}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-4">
|
refactor: Phase 6 — Migrate feature modules to SUMI tokens
- auth: Replace gray-* with muted/border tokens, text-foreground
- settings: TwoFactorSettings + NotificationSettings text-foreground
- chat: ChatInput, ConversationItem, ChatPage — text-foreground,
remove kodo references
- player: PlayerExpanded, PlayerQueue, PlayerControls — text-foreground,
remove cyan/magenta gradients
- playlists: All components — text-foreground for badges/headings
- tracks: TrackCard, TrackListRow — text-foreground, remove glow effects
- studio: FileGridCard — text-foreground
- library: LibraryPageGrid — remove hover-glow-cyan, shadow-card-glow-cyan
- profile: UserProfilePageHeader — text-foreground
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:06:28 +00:00
|
|
|
<Button size="icon" variant="ghost" className="text-muted-foreground hover:text-foreground">
|
2026-01-26 18:18:52 +00:00
|
|
|
<Share2 className="w-5 h-5" />
|
|
|
|
|
</Button>
|
feat(ui): tooltip adoption + search highlighting & skeleton loading
Tooltip adoption (18 conversions across 11 files):
- Player controls: shuffle, repeat, mute, expand, close, lyrics, auto-scroll
- Navbar: theme toggle
- File browser: download, add tag, AI auto-tag, watermark, process with AI
- Notifications: mark as read
- Share links: open link, revoke link
- Chat: scroll to bottom
Search polish:
- New highlightMatch utility — wraps matching text in <mark> with primary color
- Applied to track titles, artist names, playlist names in SearchPageResults
- Applied to suggestion dropdown titles and subtitles
- Replaced spinner loading state with content-aware SearchPageSkeleton
- Skeleton matches actual results layout (tab bar, track cards, artist circles)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:14:00 +00:00
|
|
|
<Tooltip content={showLyrics ? "Hide lyrics" : "Show lyrics"}>
|
|
|
|
|
<Button
|
|
|
|
|
size="icon"
|
|
|
|
|
variant="ghost"
|
refactor: Phase 6 — Migrate feature modules to SUMI tokens
- auth: Replace gray-* with muted/border tokens, text-foreground
- settings: TwoFactorSettings + NotificationSettings text-foreground
- chat: ChatInput, ConversationItem, ChatPage — text-foreground,
remove kodo references
- player: PlayerExpanded, PlayerQueue, PlayerControls — text-foreground,
remove cyan/magenta gradients
- playlists: All components — text-foreground for badges/headings
- tracks: TrackCard, TrackListRow — text-foreground, remove glow effects
- studio: FileGridCard — text-foreground
- library: LibraryPageGrid — remove hover-glow-cyan, shadow-card-glow-cyan
- profile: UserProfilePageHeader — text-foreground
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:06:28 +00:00
|
|
|
className={cn("transition-colors", showLyrics ? "text-primary" : "text-muted-foreground hover:text-foreground")}
|
feat(ui): tooltip adoption + search highlighting & skeleton loading
Tooltip adoption (18 conversions across 11 files):
- Player controls: shuffle, repeat, mute, expand, close, lyrics, auto-scroll
- Navbar: theme toggle
- File browser: download, add tag, AI auto-tag, watermark, process with AI
- Notifications: mark as read
- Share links: open link, revoke link
- Chat: scroll to bottom
Search polish:
- New highlightMatch utility — wraps matching text in <mark> with primary color
- Applied to track titles, artist names, playlist names in SearchPageResults
- Applied to suggestion dropdown titles and subtitles
- Replaced spinner loading state with content-aware SearchPageSkeleton
- Skeleton matches actual results layout (tab bar, track cards, artist circles)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:14:00 +00:00
|
|
|
onClick={() => setShowLyrics(!showLyrics)}
|
|
|
|
|
>
|
|
|
|
|
<Mic2 className="w-5 h-5" />
|
|
|
|
|
</Button>
|
|
|
|
|
</Tooltip>
|
2026-01-26 18:18:52 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-02-08 21:48:08 +00:00
|
|
|
|
|
|
|
|
{/* Lyrics Panel (when toggled and track has lyrics) */}
|
|
|
|
|
{showLyrics && (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"group/lyrics w-full md:flex-1 h-layout-lyrics-sm md:h-layout-lyrics flex flex-col relative rounded-xl overflow-hidden border border-white/10 bg-black/30 backdrop-blur-md",
|
|
|
|
|
"animate-in slide-in-from-right-4 duration-300"
|
|
|
|
|
)}
|
|
|
|
|
onMouseEnter={() => setAutoScrollLyrics(false)}
|
|
|
|
|
onMouseLeave={() => setAutoScrollLyrics(true)}
|
|
|
|
|
>
|
|
|
|
|
<div className="absolute top-2 right-2 z-10 opacity-0 group-hover/lyrics:opacity-100 transition-opacity">
|
feat(ui): tooltip adoption + search highlighting & skeleton loading
Tooltip adoption (18 conversions across 11 files):
- Player controls: shuffle, repeat, mute, expand, close, lyrics, auto-scroll
- Navbar: theme toggle
- File browser: download, add tag, AI auto-tag, watermark, process with AI
- Notifications: mark as read
- Share links: open link, revoke link
- Chat: scroll to bottom
Search polish:
- New highlightMatch utility — wraps matching text in <mark> with primary color
- Applied to track titles, artist names, playlist names in SearchPageResults
- Applied to suggestion dropdown titles and subtitles
- Replaced spinner loading state with content-aware SearchPageSkeleton
- Skeleton matches actual results layout (tab bar, track cards, artist circles)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:14:00 +00:00
|
|
|
<Tooltip content="Auto-scroll">
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="icon"
|
|
|
|
|
className={autoScrollLyrics ? "bg-primary/20 text-primary" : "text-muted-foreground"}
|
|
|
|
|
onClick={() => setAutoScrollLyrics(!autoScrollLyrics)}
|
|
|
|
|
>
|
|
|
|
|
<AlignLeft className="w-4 h-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</Tooltip>
|
2026-02-08 21:48:08 +00:00
|
|
|
</div>
|
|
|
|
|
{lyrics?.length ? (
|
|
|
|
|
<div
|
|
|
|
|
ref={lyricsScrollRef}
|
|
|
|
|
className="flex-1 overflow-y-auto custom-scrollbar px-6 py-8 space-y-6 text-center"
|
|
|
|
|
>
|
|
|
|
|
{lyrics.map((line, i) => {
|
|
|
|
|
const isActive =
|
|
|
|
|
currentTime >= line.time &&
|
2026-02-12 22:12:35 +00:00
|
|
|
(i === lyrics.length - 1 || currentTime < (lyrics[i + 1]?.time ?? Infinity));
|
2026-02-08 21:48:08 +00:00
|
|
|
return (
|
|
|
|
|
<p
|
|
|
|
|
key={i}
|
|
|
|
|
className={cn(
|
2026-02-12 01:09:29 +00:00
|
|
|
"text-xl md:text-2xl font-bold transition-all duration-[var(--sumi-duration-slow)] cursor-pointer hover:text-foreground",
|
refactor: Phase 6 — Migrate feature modules to SUMI tokens
- auth: Replace gray-* with muted/border tokens, text-foreground
- settings: TwoFactorSettings + NotificationSettings text-foreground
- chat: ChatInput, ConversationItem, ChatPage — text-foreground,
remove kodo references
- player: PlayerExpanded, PlayerQueue, PlayerControls — text-foreground,
remove cyan/magenta gradients
- playlists: All components — text-foreground for badges/headings
- tracks: TrackCard, TrackListRow — text-foreground, remove glow effects
- studio: FileGridCard — text-foreground
- library: LibraryPageGrid — remove hover-glow-cyan, shadow-card-glow-cyan
- profile: UserProfilePageHeader — text-foreground
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:06:28 +00:00
|
|
|
isActive ? "text-foreground scale-105" : "text-white/20"
|
2026-02-08 21:48:08 +00:00
|
|
|
)}
|
|
|
|
|
onClick={() => onSeek(line.time)}
|
|
|
|
|
>
|
|
|
|
|
{line.text}
|
|
|
|
|
</p>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="flex-1 flex flex-col items-center justify-center text-muted-foreground">
|
|
|
|
|
<Mic2 className="w-12 h-12 mb-3 opacity-50" />
|
|
|
|
|
<p>No lyrics available for this track.</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-01-26 18:18:52 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|