From 25537f27434fbb0af57663e12e54e41778b4da96 Mon Sep 17 00:00:00 2001 From: Psychotoxical Date: Sat, 18 Apr 2026 22:48:21 +0200 Subject: [PATCH] fix(fullscreen): lyrics menu toggle + readable panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clicking the mic button now toggles the lyrics settings panel instead of outside-handler closing it and click re-opening it — trigger ref is excluded from the outside-click check. Panel is now a solid surface (no backdrop-blur, near-opaque background, higher-contrast button text) so settings stay readable over the busy fullscreen background. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/FullscreenPlayer.tsx | 18 ++++++++++++------ src/styles/components.css | 20 +++++++++----------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/components/FullscreenPlayer.tsx b/src/components/FullscreenPlayer.tsx index 5c794a06..950e70ad 100644 --- a/src/components/FullscreenPlayer.tsx +++ b/src/components/FullscreenPlayer.tsx @@ -502,8 +502,9 @@ interface FsLyricsMenuProps { open: boolean; onClose: () => void; accentColor: string | null; + triggerRef?: React.RefObject; } -const FsLyricsMenu = memo(function FsLyricsMenu({ open, onClose, accentColor }: FsLyricsMenuProps) { +const FsLyricsMenu = memo(function FsLyricsMenu({ open, onClose, accentColor, triggerRef }: FsLyricsMenuProps) { const { t } = useTranslation(); const showLyrics = useAuthStore(s => s.showFullscreenLyrics); const lyricsStyle = useAuthStore(s => s.fsLyricsStyle); @@ -512,13 +513,16 @@ const FsLyricsMenu = memo(function FsLyricsMenu({ open, onClose, accentColor }: const panelRef = useRef(null); // Close on click outside the panel or on Escape. - // setTimeout(0) defers listener registration past the current click cycle - // so the button click that opens the panel doesn't immediately close it. + // Ignore clicks on the trigger button so re-clicking it toggles normally + // instead of outside-handler closing + click re-opening. useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; const onMouse = (e: MouseEvent) => { - if (panelRef.current && !panelRef.current.contains(e.target as Node)) onClose(); + const target = e.target as Node; + if (panelRef.current?.contains(target)) return; + if (triggerRef?.current?.contains(target)) return; + onClose(); }; window.addEventListener('keydown', onKey); const t = setTimeout(() => window.addEventListener('mousedown', onMouse), 0); @@ -527,7 +531,7 @@ const FsLyricsMenu = memo(function FsLyricsMenu({ open, onClose, accentColor }: window.removeEventListener('keydown', onKey); window.removeEventListener('mousedown', onMouse); }; - }, [open, onClose]); + }, [open, onClose, triggerRef]); if (!open) return null; @@ -702,6 +706,7 @@ export default function FullscreenPlayer({ onClose }: FullscreenPlayerProps) { // Lyrics settings popover state const [lyricsMenuOpen, setLyricsMenuOpen] = useState(false); const closeLyricsMenu = useCallback(() => setLyricsMenuOpen(false), []); + const lyricsMenuTriggerRef = useRef(null); // Idle-fade system — hides controls after 3 s of inactivity const [isIdle, setIsIdle] = useState(false); @@ -838,8 +843,9 @@ export default function FullscreenPlayer({ onClose }: FullscreenPlayerProps) { )}
- +