- Extended navigationStore with FavoritesTab state tracking
- Added setFavoritesTab() to push tab changes to history
- Modified navigateTo() to support favorites sub-navigation
- Wired handleTabChange() to call setFavoritesTab()
- Synced activeTab with navigation store via currentTab prop
- Back button now returns to correct Favorites tab
cp
- Implemented custom tooltip for StackIcon (similar to AudioOutputBadges)
- Shows 'Playing from', context name, and 'Click to navigate' hint
- Added onContextClick={handleContextNavigation} to ExpandedPlayer
- Fixed onClick prop not being passed, enabling navigation
cp
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
Added createContextAndPlayDisplayTrack helper that:
- Creates playback context with provided type/id/label/tracks
- Calls handleDisplayTrackPlay to start playback
- Will be used by Playlist, Artist, Favorites, Home views
Next: Implement context creation in each view component internally
(following AlbumDetailView pattern but keeping logic in views)
cp
CRITICAL LOGIC FIX:
The autoplay setting was misunderstood. Corrected behavior:
BEFORE (wrong):
- Setting controls whether context is created
- 'continue' = create context
- 'track_only' = clear context
AFTER (correct):
- Context is ALWAYS created when playing from a source (album/playlist)
- Stack icon ALWAYS appears
- Setting only affects what happens at END of track:
- 'continue' = play next track in context
- 'track_only' = stop after current track
Why this makes sense:
- You're playing from an album = that's the context
- Stack icon shows the origin (informative)
- Setting controls continuation behavior (not context existence)
Also:
- Added logging to handleAutoplayModeChange
- Will investigate why setting doesn't persist
cp
UI Fix:
- Add :has(.radio-group) rule to allow flexible height
- Set align-items: flex-start for radio group rows
- Prevents radio buttons from being squashed
Logging Enhancement:
- Log autoplay_mode preference value
- Log track index and total tracks
- Log when no album tracks found
- Will help diagnose why context isn't being created
cp
CRITICAL FIX: Context was being cleared instead of created.
Root cause:
- AlbumDetailView had internal handlers but used parent's onTrackPlay
- Parent's handleAlbumTrackPlay had no context logic
- Result: context always cleared (default behavior)
Solution:
- Moved context creation logic to handleAlbumTrackPlay in +page.svelte
- Checks autoplay_mode preference
- Creates context if 'continue', clears if 'track_only'
- Removed duplicate logic from AlbumDetailView (was never executed)
Now when clicking a track:
1. handleAlbumTrackPlay checks preference
2. Creates/clears context accordingly
3. Stack icon should appear/disappear correctly
Test with default setting ('continue'):
- Click any track → should see context creation logs
- Stack icon should appear immediately
cp
- Call initPlaybackContextStore() on app mount
- Call initPlaybackPreferences() on app mount
- Load backend state into frontend stores
- Phase 3 backend + UI complete
- Wiring for track actions pending (will be done per-page)
cp
- Stop playback immediately when starting a Qobuz track to prevent
previous track from continuing during download
- Show buffering toast while downloading Qobuz audio
- Skip buffering UI for local tracks since they load instantly
- Add is_local field to QueueTrack for prefetch optimization
- Update prefetch to skip local tracks (no network needed)
- Update all queue track builders to include is_local flag
Wire onPlaylistCountUpdate from PlaylistDetailView to Sidebar's
updatePlaylistCounts method in +page.svelte. This ensures the
sidebar displays accurate track counts from the database instead
of calculated values that could drift out of sync.
- Update handlePlayAll to include both Qobuz and local tracks
- Update handlePlayAllNext and handlePlayAllLater similarly
- Add onSetLocalQueue prop to mark local tracks in queue store
- Connect prop to handleSetLocalQueue in +page.svelte
- Add 60-second timeout to login initialization in LoginView
- Show retry/offline options when login times out
- Move loadFavorites() to handleLoginSuccess to prevent API calls
before authentication is complete
- Prevents errors when auto-login is slow or stuck
- Update onPlaylistUpdated callback to also refresh local track counts
- Change all Song/Songs references to Track/Tracks in Sidebar tooltips
- Ensure tooltip cache is properly invalidated when tracks are modified
- PlaylistModal now uses playlist_add_local_track command for local tracks
instead of the Qobuz-only add_tracks_to_playlist command
- Added isLocalTracks prop to PlaylistModal and wired it through uiStore
- Updated LocalLibraryView to pass isLocal=true when adding tracks to playlists
- Added HardDrive icon indicator next to quality badge for local tracks in TrackRow
cc
- Add batch check command for track local status in Rust
- Show unavailable tracks with grayed-out visual state in PlaylistDetailView
- Disable interactions for unavailable tracks (click, menu, play)
- Add WifiOff indicator for unavailable tracks
- Update QueuePanel to show unavailable track styling
- Implement auto-skip for unavailable tracks during playback
- Add offline mode sync to queue store
- Add offline-specific filter states to PlaylistManager:
- Available Offline
- Partially Available
- Not Available Offline (view-only)
- Disable edit/drag for unavailable playlists in PlaylistManager
- Add i18n strings for offline states (en/es)
Refresh offline status after successful login to prevent false offline
detection. Previously, the offline status was checked during bootstrap
before auto_login completed, causing 'NotLoggedIn' reason even though
the user would be logged in moments later.
- Add "Start in offline mode" link below login form
- Enable offline mode without authentication
- Navigate directly to local library when starting offline
- Add i18n strings for offline start link (en/es)
- Create OfflinePlaceholder component with reason-specific messaging
- Add i18n strings for offline mode (en/es)
- Subscribe to offlineStore in +page.svelte
- Show OfflinePlaceholder for home, search, favorites views when offline
- Provide quick navigation to local library from placeholder
cc
- Queue button in NowPlayingBar now acts as toggle (open/close)
- Queue sidebar uses theme-aware background instead of glassmorphic effect
- Dropdown hover and selected states now use CSS variables for theme support
- Fixes contrast issues in Light and Warm themes
cp
- Remove broken AddToPlaylistModal component import
- Use existing PlaylistModal infrastructure from parent component
- Pass onTrackAddToPlaylist callback from +page.svelte to LocalLibraryView
- Follow existing pattern used in AlbumDetailView and other views
cp
- Increase initial artist albums fetch from 500 to 1000
- Increase load more albums batch from 50 to 200
- Increase top tracks display from 5 to 20
- Prefer full biography content over summary
- Increase bio truncate threshold from 500 to 800 characters
cp
- Move item limits inline with section toggles in home settings modal
- Remove separate limits section for cleaner, more compact layout
- Add add-to-playlist functionality to now playing bar Plus button
- Wire up AddToPlaylistModal to current playing track
cp
- Add backend command to check if album is fully downloaded
- Add frontend functions to check album download status
- Pass download handlers to HomeView
- Update HomeView AlbumCards with download status props
cp
- Add submenu to album download option when album is fully downloaded
- New options: Open containing folder and Re-download album
- Backend command to open album folder in file manager
- Helper function to check if album is fully downloaded
- Works in both AlbumCard overlay and AlbumDetailView menus
cp
Mouse back/forward buttons now work in local library view.
Changes:
- Add 'library-album' ViewType to navigation store
- Add selectLocalAlbum(), clearLocalAlbum(), getSelectedLocalAlbumId()
- LocalLibraryView subscribes to navigation state changes
- Album clicks use selectLocalAlbum() for proper history tracking
- Back button uses navGoBack() to navigate properly
- Tab changes navigate back to 'library' when viewing album
- +page.svelte shows LocalLibraryView for both 'library' and 'library-album'
cc
- Playback state polling: 1000ms when paused, 250ms when playing
- Audio thread timeout: 500ms when idle, 100ms when playing
- Lyrics interval: self-check to auto-stop when conditions not met
- Add cleanup return to lyrics in +page.svelte
- Add build-and-run.sh and monitor-resources.sh scripts
- Add OPTIMIZATION_PLAN.md documentation
- Add handleSaveQueueAsPlaylist() to collect queue track IDs
- Wire QueuePanel "Save as Playlist" button to open playlist modal
- Comment out AirPlay commands in lib.rs (RAOP not implemented)
- Comment out AirPlay state initialization
- Move documentation files to qbz-nix-docs/ directory
AirPlay research concluded no viable Rust RAOP library exists.
Feature deferred until custom implementation or library emerges.
See qbz-nix-docs/AIRPLAY_IMPLEMENTATION_STATUS.md for details.
Frontend:
- Add castStore for managing cast state (connect, disconnect, play, pause, etc)
- Update CastPicker to use castStore, show connected device banner with disconnect
- Wire isCastConnected to NowPlayingBar and ExpandedPlayer for visual indicator
- Modify playbackService to cast when connected instead of local playback
DLNA Backend:
- Implement full AVTransport SOAP client with:
- SetAVTransportURI with DIDL-Lite metadata
- Play, Pause, Stop, Seek actions
- RenderingControl SetVolume
- Add dlna_play_track command that fetches stream, downloads audio, and serves via media server
- Share media server between Chromecast and DLNA
- Fetch full track info from Qobuz API when restoring session
- Populate albumId and artistId for working album/artist links
- Show correct quality badge (bitDepth/sampleRate) instead of placeholder
- Fallback to cached data if API call fails
- Add 'buffering' toast type that doesn't auto-hide
- Add shimmer animation to "Buffering" text
- Add spinning loader icon for buffering state
- Toast shows "Buffering... [track name]" format
- Dismiss buffering toast when track loads or on error
- Remove callback pattern, use toastStore directly in services
fix(artwork): show empty placeholder when no artwork available
- NowPlayingBar shows empty placeholder instead of Play icon
- Miniplayer shows empty placeholder instead of Play icon
- Prevents showing incorrect CD placeholder on session restore
- MiniPlayer now resizes the main window instead of creating a separate floating window
- Cider-inspired UI with large album art, track info, full playback controls
- Progress bar with current/remaining time display
- Shuffle, repeat, volume controls integrated
- Pin/restore buttons to control always-on-top and return to main view
- Fixed tray icon to use transparent PNG from static/icons
- Create miniplayerService.ts for window management
- Add /miniplayer route with compact player UI
- Sync playback state from main window via events
- MiniPlayer features:
- Draggable window (decorations disabled)
- Always-on-top toggle (pin button)
- Album art, track title, artist
- Play/Pause, Next, Previous controls
- Progress bar
- Close button (hides window)
- Add PictureInPicture2 button to NowPlayingBar
- State synced via emitTo('miniplayer', ...) events
- Enable debouncedSavePosition() during playback state updates
(saves position every 5 seconds while playing)
- Flush position save immediately when pausing
- Add periodic full session save every 30 seconds during playback
- Persist volume changes to session immediately
- Persist shuffle/repeat mode changes to session immediately
This fixes the race condition where async saves in beforeunload
wouldn't complete before app close. Now we save frequently enough
that the worst case is losing only 5-30 seconds of progress.