From 584650fee17fe23bf7a3c90c7885c5c493ffefff Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Sun, 18 Jan 2026 03:47:11 -0600 Subject: [PATCH] feat(phase37): implement clickable context tooltip navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit StackIcon Component: - Now accepts onClick prop - Changed from div to button element - Added arrow (→) that appears on hover - Tooltip text changed to "Click to go to: {source}" - Visual feedback: hover background + active scale Navigation Logic (+page.svelte): - Added handleContextNavigation function - Switch based on context_type: - album → handleAlbumClick - playlist → navigate + set selectedPlaylistId - artist_top → handleArtistClick - favorites → navigate to favorites - home_list → navigate to home - search → navigate to search - Import getCurrentContext from playbackContextStore UI Integration: - ExpandedPlayer: Added onContextClick prop, passed to StackIcon - FocusMode: Added onContextClick prop, passed to StackIcon - Both receive handleContextNavigation callback Phase 3.7 COMPLETE ✅ cp --- src/lib/components/ExpandedPlayer.svelte | 4 +- src/lib/components/FocusMode.svelte | 4 +- src/lib/components/StackIcon.svelte | 46 +++++++++++++++-- src/routes/+page.svelte | 65 +++++++++++++++++++++++- 4 files changed, 112 insertions(+), 7 deletions(-) diff --git a/src/lib/components/ExpandedPlayer.svelte b/src/lib/components/ExpandedPlayer.svelte index f3b6e4045..c11184fff 100644 --- a/src/lib/components/ExpandedPlayer.svelte +++ b/src/lib/components/ExpandedPlayer.svelte @@ -50,6 +50,7 @@ onOpenFocusMode?: () => void; onCast?: () => void; isCastConnected?: boolean; + onContextClick?: () => void; // Lyrics props lyricsLines?: LyricsLine[]; lyricsActiveIndex?: number; @@ -89,6 +90,7 @@ onOpenFocusMode, onCast, isCastConnected = false, + onContextClick, lyricsLines = [], lyricsActiveIndex = -1, lyricsActiveProgress = 0, @@ -247,7 +249,7 @@

{trackTitle}

- +

{artist}

·

{album}

diff --git a/src/lib/components/FocusMode.svelte b/src/lib/components/FocusMode.svelte index d25ea069a..1d7deda8f 100644 --- a/src/lib/components/FocusMode.svelte +++ b/src/lib/components/FocusMode.svelte @@ -22,6 +22,7 @@ currentTime: number; duration: number; onSeek: (time: number) => void; + onContextClick?: () => void; // Lyrics props lyricsLines?: LyricsLine[]; lyricsActiveIndex?: number; @@ -44,6 +45,7 @@ currentTime, duration, onSeek, + onContextClick, lyricsLines = [], lyricsActiveIndex = -1, lyricsActiveProgress = 0, @@ -187,7 +189,7 @@
{trackTitle}
- +
{artist}
diff --git a/src/lib/components/StackIcon.svelte b/src/lib/components/StackIcon.svelte index 35a7c9ed3..be4c32bc4 100644 --- a/src/lib/components/StackIcon.svelte +++ b/src/lib/components/StackIcon.svelte @@ -6,14 +6,23 @@ interface Props { size?: number; class?: string; + onClick?: () => void; } - let { size = 16, class: className = '' }: Props = $props(); + let { size = 16, class: className = '', onClick }: Props = $props(); let context = $state(getCurrentContext()); let displayInfo = $state(context ? getContextDisplayInfo() : null); let showIcon = $state(getCachedPreferences().show_context_icon); + function handleClick(e: MouseEvent) { + e.stopPropagation(); + if (onClick) { + console.log('[StackIcon] Clicked - navigating to source'); + onClick(); + } + } + // Subscribe to context changes $effect(() => { const unsubscribe = subscribe(() => { @@ -43,9 +52,15 @@ {#if context && displayInfo && showIcon} -
+
+ → + {/if} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 908df7148..68d522eea 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -29,7 +29,8 @@ import { initPlaybackContextStore, setPlaybackContext, - clearPlaybackContext + clearPlaybackContext, + getCurrentContext } from '$lib/stores/playbackContextStore'; import { initPlaybackPreferences, @@ -371,6 +372,66 @@ } } + /** + * Navigate to the source of current playback context + */ + async function handleContextNavigation() { + const context = getCurrentContext(); + if (!context) { + console.log('[ContextNav] No context available'); + return; + } + + console.log('[ContextNav] Navigating to:', context); + + try { + switch (context.context_type) { + case 'album': + // Navigate to album page + await handleAlbumClick(context.context_id); + break; + + case 'playlist': + // Navigate to playlist page + const playlistId = parseInt(context.context_id); + if (!isNaN(playlistId)) { + selectedPlaylistId = playlistId; + navigateTo('playlist'); + } + break; + + case 'artist_top': + // Navigate to artist page + const artistId = parseInt(context.context_id); + if (!isNaN(artistId)) { + await handleArtistClick(artistId); + } + break; + + case 'favorites': + // Navigate to favorites page + navigateTo('favorites'); + break; + + case 'home_list': + // Navigate to home page + navigateTo('home'); + break; + + case 'search': + // Navigate to search (could restore query if needed) + navigateTo('search'); + break; + + default: + console.warn('[ContextNav] Unknown context type:', context.context_type); + } + } catch (err) { + console.error('[ContextNav] Navigation failed:', err); + showToast('Failed to navigate to source', 'error'); + } + } + interface ArtistAlbumsResponse { items: QobuzAlbum[]; total: number; @@ -2201,6 +2262,7 @@ handleAlbumClick(currentTrack.albumId); } }} + onContextClick={handleContextNavigation} /> {:else} {/if}