- Add t import and use translations for all empty state messages
- Translate loading states (favorites, playlists, albums)
- Translate section headers (Discography, EPs & Singles, Live Albums)
- Translate button titles (Play All, Shuffle, More, Search, Clear)
- Add hint translations for liking items to favorites
- Update getSortLabel to use sort.* translation keys
- Update getTabLabel to use favorites.tabLabels.* keys
cc
- Default view shows only parent genres (less cluttered like Qobuz)
- When selecting a parent, filtering includes all its sub-genres
- Added "Show all sub-genres" toggle for granular control
- Store now tracks parent/child relationships for proper filtering
- getFilterGenreNames() returns selected + children for filtering
Add FavoritePlaylistCard component with hover overlay containing:
- Heart button (remove from favorites)
- Play button (start playlist playback)
- More menu (play next, add to queue, share, remove)
Card click navigates to playlist page. Updates FavoritesView to use
the new component with all action handlers wired through +page.svelte.
- Add 'control' variant for Favorites style matching other dropdowns
- Add 'align' prop: 'left' (Home) or 'right' (Favorites)
- FavoritesView uses control variant with right alignment
- Control variant shows "Genre: X" with chevron icon
- Add GenreFilterButton to Favorites > Albums toolbar
- Filter albums by genre name (multi-select supported)
- Use genreFilterVersion for reactivity tracking
Note: Artists tab doesn't have genre filter since artist
objects don't contain genre info directly.
- Artist names in sidepanel now animate with ticker effect on hover when text overflows
- Each album section (Discography, EPs & Singles, Live Albums) now has a sort dropdown
- Sort options: Default, Newest, Oldest, A-Z, Z-A
- Use sorted arrays for rendering instead of raw grouped arrays
- Group albums by category: Discography, EPs & Singles, Live Albums
- Exclude tributes and compilations (others) from display
- Each section has its own header that scrolls with content
- Updated footer: "To view compilations and other content, go to..."
- All content scrolls together (sections + footer)
- Add no-outer-scroll class when sidepanel mode is active
- Set up proper flex container hierarchy for full height:
- .favorites-view becomes flex column container
- .content gets flex: 1 with min-height: 0
- ViewTransition wrapper gets flex: 1 via :global selector
- Fix negative margins to match parent padding (was -32px, now -8px/-18px)
- Reduce artist column width to 240px (matches LocalLibrary)
- Fix album column right padding to 8px
- Add artist count header like LocalLibrary
- Remove sticky positioning from group headers (caused overlap)
- Restructure albums area with scroll wrapper containing both grid and footer
- Footer now scrolls with album grid (visible at bottom after scrolling)
- Match LocalLibrary CSS patterns for proper independent scrolling
- Fix toggle button to use control-btn icon-only style (matches Library)
- Add proper background/hover states to the toggle button
- Fix two-column layout for independent scrolling:
- Artist list scrolls independently from albums grid
- Added scroll optimization properties (overscroll-behavior, contain)
- Adjusted padding and margins for edge-to-edge layout
- Increased album card grid size to 180px (matches LocalLibrary)
Implements a two-column sidepanel view for Favorites > Artists:
- Left column shows artists grouped A-Z with sticky headers
- Right column shows selected artist's Discography albums
- Albums are filtered to show only studio albums (not EPs, Singles, Live)
- Proper AlbumCard with artwork, favorite button, and quality badge
- Footer link to navigate to full artist page for other album types
- Alpha index in toolbar for quick navigation
- Toggle button to switch between grid and browse views
- View mode persists to localStorage
Also exports categorizeAlbum from qobuzAdapters for reuse.
FavoritesView Albums:
- Change from combined sort modes to sortBy + sortDirection
- Click option to select, click again to toggle direction
- Show arrow indicator on selected option
PlaylistDetailView:
- Remove separate Ascending/Descending options
- Integrate direction toggle into each sortable option
- Default and Custom Order remain direction-insensitive
When switching tabs, the loading spinner now animates with slide-down
transition (200ms, 12px) using {#key activeTab} to re-trigger on tab change.
This completes the animation flow: spinner slides down -> fadeout -> content slides up
Wrap entire FavoritesView in ViewTransition with direction="down" so the
initial view (header + spinner) slides down on first load.
Complete flow:
0. Initial view appears with slide-down transition (200ms, 12px)
1. Loading spinner shows while syncing
2. When sync completes, spinner fades out (200ms)
3. Content appears with slide-up transition (200ms, 12px)
- Add ViewTransition wrapper to AlbumDetailView (200ms, 12px slide up)
- Add ViewTransition to FavoritesView content sections (tracks, albums, artists, playlists)
- Add spinner fadeout animation (200ms) before content transition in FavoritesView
- Both loading spinner and playlists loading spinner now fade out smoothly
This ensures favorites sync properly across all views (album detail,
continue listening, etc.) by using local persistence as the source
of truth for UI, with API calls for actual operations.
Backend (Rust):
- Add favorites_cache.rs module with SQLite tables:
- favorite_tracks (track_id INTEGER PRIMARY KEY)
- favorite_albums (album_id TEXT PRIMARY KEY)
- favorite_artists (artist_id INTEGER PRIMARY KEY)
- Add Tauri commands for cache operations:
- get_cached_favorite_* - read from local cache
- cache_favorite_* / uncache_favorite_* - add/remove single items
- sync_cached_favorite_* - bulk sync from API data
- clear_favorites_cache - for logout
Frontend:
- Rewrite favoritesStore.ts to use persistence:
- loadFavorites(): Load from cache first, then sync from API
- toggleFavorite(): API call first → update cache on success
- No optimistic updates - UI shows loading state until confirmed
- Rewrite albumFavoritesStore.ts with same pattern
- Create artistFavoritesStore.ts for artist favorites
- Update +page.svelte to load all favorites on login
- Update FavoritesView to sync cache when loading from API
Sync strategy:
- On login: Fetch from Qobuz API → sync to local cache
- On toggle: API call → on success → update local cache → notify UI
- FavoritesView reads from API and syncs local cache
- All other views read from local cache (instant UI)
- Header now outside .track-sections.virtualized (like LocalLibraryView)
- Use calc() height for virtualized section to give explicit dimensions
- Removes complex flex/absolute positioning that was breaking layout
- Add track list header with columns: #, Title, Album, Duration,
Quality, Favorite icon, Download icon
- Album column is hidden when grouping by album (redundant)
- Add getTrackAlbum and showAlbum props to VirtualizedTrackList
- Style matches PlaylistDetailView for consistency
- Add z-index: 1000 to dropdown-container to appear above sidebar
- Change dropdown alignment from right: 0 to left: 0 for natural
left-to-right opening
- Affects all tabs: Albums, Tracks, Artists
- Fix Favorites Albums group dropdown appearing behind sidebar
(z-index 10 -> 100)
- Persist volume level to localStorage between sessions
- Allow changing volume when nothing is playing (volume
applies when playback starts)
- Add compact prop to QualityBadge for inline usage in track lists
- Compact mode shows simple text (24bit/96kHz or CD Quality)
- Use golden gradient for hires badges in compact mode
- Update ArtistDetailView to use QualityBadge compact
- Update FavoritesView to use QualityBadge compact
- Update SearchView to use QualityBadge compact
- Remove duplicate CSS and getQualityLabel functions
- LocalLibrary views keep custom format showing codec (FLAC 24/96)
- Use golden gradient (#fbbf24 to #b8860b) with dark text for hires badges
- Increase min-width to 90px for consistent sizing across all badges
- Rename track-row favorite button to avoid CSS conflict with header button
- Header favorite button now properly circular like radio and network buttons
1. Search: Re-clicking Search button in sidebar scrolls to top and focuses input
- Added triggerSearchFocus mechanism in searchState store
- SearchView subscribes to focus trigger for scroll + focus
2. ArtistDetailView: Added quality badge and favorite button to Popular Tracks
- Added getQualityLabel function with bit depth/sample rate
- Added quality-badge element with hires styling
- Added favorite button in track actions
3. FavoritesView: Fixed getAlbumQualityLabel to show detailed format
- Now shows "24bit/96kHz" instead of generic "Hi-Res"
- Use size="large" (180px) for all AlbumCard instances
- Change album grids from CSS grid with 1fr to flexbox
- Fix LocalLibrary gap issue by using consistent spacing
- Align VirtualizedAlbumList, ArtistDetailView, SearchView,
FavoritesView, HomeView, and AlbumDetailView
- Add onTrackShowInfo to HomeView (Continue Listening tracks)
- Add onShowInfo to VirtualizedTrackList (for FavoritesView)
- Add onTrackShowInfo to FavoritesView and wire to VirtualizedTrackList
- Pass showTrackInfo to HomeView and FavoritesView in +page.svelte
- Add clickable artist name to TrackInfoModal header
- Artist link navigates to artist page and closes modal
Add --alpha-* CSS variables (4-95%) to app.css that adapt to theme:
- Dark themes: white-based rgba
- Light themes: black-based rgba (auto-inverts for proper contrast)
Mass-replaced 100+ instances of rgba(255,255,255,...) across 23 files
with var(--alpha-*) for consistent theming support.
This fixes borders, overlays, and UI elements being invisible or
having poor contrast in light themes.
The jump-nav sticky header uses negative margins to extend beyond
the container padding. The right margin was -16px (or -32px in Settings)
but the container only has 8px (or 24px) of right padding, causing
8px of horizontal overflow.
Fixed by matching the negative right margin to the actual padding:
- ArtistDetailView: -16px → -8px, width calc(100%+40px) → calc(100%+32px)
- FavoritesView: -16px → -8px, width calc(100%+40px) → calc(100%+32px)
- LocalLibraryView: -16px → -8px, width calc(100%+40px) → calc(100%+32px)
- SettingsView: -32px → -24px, width calc(100%+64px) → calc(100%+56px)
- Removed padding-left: 20px from main-content
- Toggle button and jump-nav don't collide (different vertical positions)
- Updated all view nav bars with corrected margin/width calculations
- Nav bars now truly flush with sidebar edge
- ArtistDetailView: jump-nav now flush with sidebar (margin -44px left)
- FavoritesView: favorites-nav flush with sidebar
- LocalLibraryView: jump-nav flush with sidebar
- SettingsView: settings-nav flush with sidebar
- All navs compensate for both main-content and view padding
- Radio animation: faster floating messages (1.2s), infinite loop
- Add backdrop-filter blur to favorites-nav for glass effect
- Fix nav margins to match parent padding (24px left, 8px right)
- Add proper width calculation for full-width coverage
Add sort dropdown for favorite albums with options:
- Default (API order)
- Newest First (by release date)
- Oldest First (by release date)
- Title (A-Z / Z-A)
- Artist
Sort preference is persisted to localStorage.
- Create offlineCacheState.ts with compliant terminology
- Update all component imports and type references
- Add compatibility aliases for gradual migration
- Remove deprecated downloadState.ts
cc
Use encodeURIComponent instead of slugify for group IDs to preserve
case sensitivity. This prevents collisions like "Aki" and "AKI" both
becoming "aki" which caused each_key_duplicate errors.