import React, { useCallback, useMemo, useRef, useState } from 'react'; import { SkipBack, SkipForward, Square, Repeat, Repeat1, Heart, Shuffle, ListMusic, ChevronDown, Star, MicVocal, } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { usePlayerStore } from '@/features/playback/store/playerStore'; import { queueSongStar, queueSongRating } from '@/features/playback/store/pendingStarSync'; import { useAlbumCoverRef, useArtistCoverRef } from '@/cover/useLibraryCoverRef'; import { usePlaybackCoverArt } from '@/cover/usePlaybackCoverArt'; import { useCachedUrl } from '@/ui/CachedImage'; import { useArtistFanart } from '@/cover/useArtistFanart'; import { backdropFromConfig } from '@/cover/artistBackdrop'; import { useThemeStore } from '@/store/themeStore'; import { useFsIdleFade } from '@/features/fullscreenPlayer/hooks/useFsIdleFade'; import { useQueueTrackAt } from '@/features/queue'; import { WaveformSeek } from '@/features/waveform'; import { FsQueueModal } from '@/features/fullscreenPlayer/components/FsQueueModal'; import { FsLyricsApple } from '@/features/fullscreenPlayer/components/FsLyricsApple'; import { FsPlayBtn } from '@/features/fullscreenPlayer/components/FsPlayBtn'; import { FsClock } from '@/features/fullscreenPlayer/components/FsClock'; import { FsTimeReadout } from '@/features/fullscreenPlayer/components/FsTimeReadout'; interface Props { onClose: () => void; } /** * Fullscreen background image that eases in once its pixels are loaded, so a * new background fades up from the empty backdrop instead of hard-cutting. * * Mount it with `key={url}` so every source gets a fresh element (and a fresh * `loaded=false`). Both load paths are covered: `onLoad` for a network/disk * fetch, and the `ref`'s `complete` check for an already-cached image whose * `load` event can fire before React attaches the handler (e.g. skipping back * to a recently shown artist) — without it the background would stay black. */ function FsBackground({ url }: { url: string }) { const [loaded, setLoaded] = useState(false); if (!url) return