Commit Graph
218 Commits
Author SHA1 Message Date
vicrodh 6974721b7a feat(albumcard): add show-favorite flag to hide the heart in non-favoritable contexts
The shared AlbumCard always rendered the favorite heart (overlay button +
"Add to favorites" menu item). Local Library albums are not Qobuz-favoritable
(Tauri's AlbumCardLibraryLite hides favorite/genre), so the upcoming local
grid needs to suppress it.

Add an additive `show-favorite: bool` (default true) on AlbumCard, threaded
through AlbumCollectionView -> AlbumGrid. The fav overlay button uses
`visible:` (not an `if`) so it stays referenceable by `overlay-on` (keeping
the Qobuz hover-no-flicker behavior intact); the menu item is gated with an
`if`. All existing callers (Discover / Favorites / Label) default to true,
so behavior is unchanged.

cc
2026-05-28 22:44:04 -06:00
vicrodh 1dfc22dd37 fix(locallibrary): rebuild view chrome on the mandatory fixed-chrome nav header
The nav-scaffolding view (c50ae171) invented its own chrome: a title+stats
header with no NavButtons (so no back/forward history nav), plus a tab bar
of border-radius:18px pill chips — an ADR-008 violation. Rebuild it 1:1 on
the app's mandatory fixed-chrome header (the same FavoritesView/Discover
use):

- Row 1: NavButtons (back/forward) + "Local Library" title (left), the tab
  menu floated right. Tabs are underline tabs (LibTab/LibTabMenu mirroring
  FavTab/FavTabMenu) — no pills.
- The settings gear becomes a standalone icon-only circle matching
  NavButtons (the one circular shape ADR-008 permits), routing to
  Settings > Local Library.
- Row 2 (per-tab actions + toolbar) is established with vertical-stretch:0
  so it doesn't inflate while empty; filled per slice (Albums next).
- 1px divider, then a single scrolling content area with a placeholder.

Wiring (select-tab / open-settings, nav, mount) is unchanged — only the
.slint chrome was rebuilt.

cc
2026-05-28 21:48:06 -06:00
vicrodh c50ae17102 feat(locallibrary): nav scaffolding for the Local Library Slint view
Greenfield Local Library port, slice 1 — the view now mounts and navigates
end to end (empty placeholder body; per-tab data lands next):

- state.slint: `ContentView::local-library` + `LocalLibraryState`
  (active-tab / loading / load-error / per-category counts) +
  `LocalLibraryActions` (select-tab, open-settings). Re-exported from
  app.slint so the globals reach the Rust API.
- ui/locallibrary/LocalLibraryView.slint: header (title + stats + gear) +
  4-tab bar (Albums / Artists / Folders / Tracks) + placeholder body.
- shell: AppShell mounts the view on `local-library`; HeaderBar's
  Local Library menu items now carry routes (local-albums / -artists /
  -folders / -tracks) instead of being inert.
- nav.rs: `NavEntry::LocalLibrary { tab }` — per-tab history entry,
  mirroring Favorites / Discover (back/forward moves between tabs).
- src/local_library.rs: `LibTab` controller stub (route/tab-id parsing,
  round-trip tested).
- main.rs: apply_entry arm + `navigate_local_library` + header-route
  branch + in-view tab-bar (select-tab) and gear (open-settings ->
  Settings; management lives under Settings > Local Library) wiring.

qbz-slint cargo check green. Built blind — needs a runtime smoke test
(./scripts/slint-dev.sh): click the Local Library header menu, confirm the
view shows and the 4 tabs switch.

cc
2026-05-28 17:51:52 -06:00
vicrodh 78b1a9c508 refactor(slint): Settings "Offline" panel matches the other sections' layout
The panel improvised its own title + free-form paragraph + ghost buttons,
breaking the established Settings structure. Rebuilt it like Audio/Playback/
Appearance: a GroupHeader ("OFFLINE CACHE") + SettingRow rows (label +
description + a SecondaryButton control on the right) — Manage offline cache /
Cache folder / Clear offline cache. No panel-level title (the left sub-nav
names the section). Keeps the layout consistent for the offline settings we'll
add here later.

cc
2026-05-28 13:18:03 -06:00
vicrodh 2b6c71000f feat(slint): offline manager — checkboxes + bulk, status to the end, track numbers
Per user feedback:
- Multi-select: a checkbox on every track row + "Select all"/"Clear" toggle +
  a bulk bar ("N selected" / Re-download / Remove). Selection is an in-place
  model edit (offline_manager::toggle_select / set_all_selected) with a live
  selected-count; bulk reads the checked ids and runs redownload_track /
  remove_cached per track.
- Moved the status glyph to the END of the row (albums and tracks) — it read
  as "missing art" at the leading edge under the album-grouping model.
- Track rows now show their position number in the leading slot (filling what
  looked empty). (Position within the album; the catalog track number isn't in
  the cache index.db — a follow-up if the real number is wanted.)

qbz-slint compiles clean.

cc
2026-05-28 12:23:46 -06:00
vicrodh db3d8491f5 feat(slint): offline manager 1:1 — covers, artist rail, sort, failed-only, persisted limit
Closes the v1 gaps in the offline manager toward Tauri parity:

- Album COVERS: rows load the on-disk cover.jpg (per album). Built Send-safely
  — the worker passes the cover PATH (string), the image is decoded on the UI
  thread (slint::Image is not Send).
- Two-pane layout with an A-Z ARTIST RAIL (left, "All artists" + per-artist
  "N albums · M tracks", selectable) + the discography list (right).
- SORT dropdown (A-Z / Recent / Largest / Smallest; Recent uses the DB's
  last_accessed_at DESC order) and a "Failed only" toggle. Filter state lives
  Rust-side (offline_manager) so rebuild can read it off-thread; mirrored back
  onto OfflineManagerState for the UI.
- Limit now PERSISTS to disk (<audio>/offline_limit), restored on activate via
  apply_persisted_limit; the manager's edit-limit writes it.
- Manager mutations (remove/redownload/clear) now rebuild on completion
  (offline_manager::rebuild made pub, called at the end of each cache op) —
  the 500ms timed reload hack is gone.

Deferred (user-accepted): the "track no longer available on Qobuz" case —
offline copy still plays, but warn it can't be re-downloaded / won't work on
Qobuz Connect.

qbz-slint compiles clean.

cc
2026-05-28 11:52:55 -06:00
vicrodh 3e0a718e0c feat(slint): offline cache manager + remove/redownload + Settings Offline page
Slices 3 + 4 of the offline-cache port.

Slice 3 (controller, offline_cache.rs):
- remove_album (maintenance::remove_album_cached_tracks + library rows + live
  row update), redownload_track / redownload_album(failed_only) (via
  select_redownload_targets + reset_track_for_redownload + spawn), clear_all
  (purge), open_folder (xdg-open / open the cache dir).

Slice 4 (manager + Settings):
- OfflineManagerView.slint: a standalone route (NavEntry::OfflineManager ->
  ContentView.offline-manager, mounted in AppShell, mirrors PlaylistManager).
  Stats bar (tracks count / size / limit / usage bar + edit-limit GB input +
  open-folder + clear-all) over a scrolling album-header + track-row list with
  a spinning/check/alert status glyph and per-row re-download / remove.
- offline_manager.rs: builds the album->track rollup + stats from index.db
  (get_all_tracks), pushes [OfflineRow] + stats to OfflineManagerState; set_limit
  (in-memory, session-scoped) + reload_soon (refresh after a mutation).
- OfflineManagerActions wired in main.rs (open/refresh/remove/redownload/
  set-limit/clear-all/open-folder/play).
- Settings gains an "Offline" section (OfflineSettings.slint): Manage offline
  cache (-> the manager), Open cache folder, Clear all. The Local-Library
  rows (show-in-library, repair) arrive with the Local Library port.

qbz-slint compiles clean.

cc
2026-05-28 11:23:55 -06:00
vicrodh a7f2283c92 feat(slint): offline button on every Qobuz track row + cache-whole-playlist
- TrackRow show-download now defaults to true: every Qobuz track row (album,
  artist, label, playlist, mix, search, favorites) carries the offline
  cloud/check button. Non-Qobuz surfaces (future Local Library / Plex) opt out
  by setting it false.
- Playlist header gains a "make offline" CircleAction -> ("playlist","cache")
  -> offline_cache::cache_playlist (get_playlist -> batch its tracks), mirroring
  the album-header action.

qbz-slint compiles clean.

cc
2026-05-28 10:29:43 -06:00
vicrodh 6572d6f33f feat(slint): album-header "Make album offline" + direct button on favorites rows
- offline_cache::cache_album fetches an album's tracks and batches them; wired
  to a new ("album","cache") media-action arm + a cloud-download CircleAction in
  the AlbumPageView header action row.
- Favorites track rows now enable show-download, so the direct per-row offline
  button shows there too (not just album rows / cached tracks).

Rounds out Slice 2: trigger surfaces (per-row button on album+favorites, context
menu everywhere, bulk multi-select, album header), live status (spinner/check/
alert) + seeding, and the unlock padlock. qbz-slint compiles clean.

cc
2026-05-28 09:29:20 -06:00
vicrodh 43c1599400 fix(slint): spin the downloading loader (animation-tick rotation)
The queued/downloading state showed a static loader-circle. Split it into its
own `if` arm that continuously rotates via animation-tick (same technique as
LoadingSpinner), isolated so the per-frame tick only drives a row while it is
actually downloading. The idle/ready/failed glyph stays static. Communicates
that the backend is working.

cc
2026-05-28 08:36:40 -06:00
vicrodh 3ffdca729d feat(slint): bulk "Make available offline" in the Library multi-select bar
Adds the 6th MultiSelectBar action -> offline_cache::cache_tracks(selected),
mirroring Tauri's bulk caching. Clears the selection after queuing. (Also
retires the cache_tracks dead-code warning.)

cc
2026-05-28 08:28:47 -06:00
vicrodh d5f23c38d8 feat(slint): per-row offline affordance + the unlock padlock (Slice 2 UI)
TrackRow now renders the offline state the controller maintains:

- The offline button is stateful: cloud-download (none, click -> cache) ->
  loader-circle accent (queued/downloading) -> circle-check accent (ready,
  click -> uncache) -> triangle-alert red (failed, click -> retry). Shown on
  surfaces that enable it AND on any row with active cache state.
- The unlock PADLOCK (user-requested, back from Tauri): a pulsing lock glyph
  overlays the play cell while the track's CMAF bundle decrypts, driven by the
  `unlocking` flag the row_sink toggles on UnlockStart/UnlockEnd. New lucide
  lock.svg / lock-open.svg assets.

The context menu's "Make available offline" already routes to caching. Testable
loop: cache a track (album button or context menu) -> watch it download -> play
it offline -> padlock pulses during decrypt. qbz-slint compiles clean.

cc
2026-05-27 22:05:37 -06:00
vicrodh b65b402ff1 feat(slint): offline-cache controller + per-row status/unlock plumbing (Slice 2)
The Rust half of the trigger/status UI:

- offline_cache.rs: cache_track / cache_tracks / remove_cached + row_sink — a
  CacheEventSink that reflects download status (queued/downloading%/ready/failed)
  and the unlock padlock onto every visible row, plus terminal toasts. cache_*
  pre-flight the limit, insert the queued row, and spawn the shared
  spawn_track_cache_download with the core client.
- TrackItem gains cache-status / cache-progress / unlocking; set_row_cache_status
  + set_row_unlocking mirror set_row_favorite (walk the 6 track models + search
  hero). All 7 exhaustive initializers updated.
- media-action arms ("track","cache"|"download"|"uncache") dispatch to the
  controller; the play path now passes row_sink so UnlockStart/End fire the
  padlock state during CMAF decrypt.

Compiles clean. Next: the TrackRow.slint affordance (make-offline button + ring
/ check / warn) and the padlock glyph that render these fields.

cc
2026-05-27 21:45:26 -06:00
vicrodh 81e7d21868 feat(slint): A-Z alpha index for the Artists sidepanel left list
The sidepanel left artist list is now always A-Z grouped (derive_artists
groups when group-enabled OR view-mode==sidepanel; the view toggle re-derives)
with letter headers, and an AlphaStrip on its right edge jumps the list
Flickable proportionally (same approach as the album grouping — not exact,
by design). Replaces the flat ListView.

cc
2026-05-27 16:19:12 -06:00
vicrodh 0b12d102ce feat(slint): favorites Artists sidepanel UI (two-column browse)
Add the grid<->sidepanel view toggle to the Artists toolbar (group select is
now grid-only, matching Tauri) and the sidepanel two-column layout: a
self-scrolling left list of SidepanelArtistRows (round thumb + name, selected
highlight) that calls select-artist, and a right column with the 5-state
machine (select-hint / loading / error / no-albums / sections). Each release
section renders its title + a flat AlbumCollectionView grid; a footer links to
the full artist page. The other-flick branch now excludes the sidepanel so the
two columns scroll independently.

cc
2026-05-27 16:08:45 -06:00
vicrodh 02b63d2cd2 refactor(slint): rename the Favorites nav-toolbar setting label to Library
Qobuz parity with the renamed favorites header; avoids leaving the old
"Favorites" wording behind (will be revisited during the settings wiring).

cc
2026-05-27 15:32:15 -06:00
vicrodh 41692012ee feat(slint): favorites Artists A-Z grouping + alpha strip
Add the optional A-Z group toggle to the Artists grid (Group: Off/A-Z
QbzSelect, default Off, persisted via favorites_prefs artists_group).
derive_artists now buckets the filtered set into FavArtistSection letter
groups (# first, then A-Z) and builds the alpha-jump model; the grouped grid
renders per-letter headers + sub-grids and an AlphaStrip on the right that
jumps the Flickable proportionally (mirrors the Albums alpha grouping).
Also renames the favorites view header title "Favorites" -> "Library" to
match Qobuz.

cc
2026-05-27 15:32:15 -06:00
vicrodh da683cd5d0 fix(slint): favorites sub-header (row 2) inflating on artists/playlists/labels
The per-view action+toolbar row is a HorizontalLayout whose sized children
are conditional per tab. When they are absent its auto vertical-stretch
becomes 1, so it split the leftover height with the scrolling content. The
Flickable-based tabs (artists/playlists/labels) have a small content
preferred height, leaving slack the row grabbed -> a giant empty sub-header.
Tracks/Albums looked fine only because their content fills. Pin the row to
vertical-stretch: 0 so the content always absorbs the slack.

cc
2026-05-27 15:10:01 -06:00
vicrodh bc95b30db3 feat(slint): favorites Artists header Shuffle (random artist)
Add a Shuffle CircleAction to the Artists row-2 action cluster that opens a
random artist from the visible set (random ARTIST, not a random album —
matches Tauri). Adds favorites::random_visible_artist + the artists-shuffle
handler routing through invoke_open_artist.

cc
2026-05-27 14:58:55 -06:00
vicrodh b87489206a feat(slint): favorites Artists search + render the filtered set
Point the artists grid at artists-visible (the search-filtered model) and
add the "No artists match your search" empty state. Add the Artists row-2
toolbar branch with the shared ExpandableSearch wired to artists-search +
search-artists -> derive_artists.

cc
2026-05-27 14:41:25 -06:00
vicrodh 70083cf33d feat(slint): favorites Artists grid card fidelity + derive_artists
Drop the albums_count line from the artist card (Tauri #169: Qobuz's count
includes compilations/tributes and is misleadingly high) — removed from
ArtistCard, map_artist, FavoriteArtistItem and FavArtistCard. Shrink the card
image to a 120px circle to match Tauri. Add derive_artists (name-substring
search filter → artists-visible/artists-shown), seeded on load; grouping +
the alpha strip layer on next.

cc
2026-05-27 14:35:49 -06:00
vicrodh 210808fd39 feat(slint): scaffold favorites Artists state + actions
Add the FavoritesState fields for the Artists port: the grid toolbar
(artists-search / artists-view-mode grid|sidepanel / artists-group-enabled),
the search-filtered + A-Z-grouped models (artists-visible / artists-grouped
via the new FavArtistSection struct / artists-alpha / artists-shown), and
the sidepanel selection state (selected-artist-id/name, loading/error,
selected-artist-sections as DiscoverSections + total). Add the FavoritesActions
callbacks (search-artists, artists-set-view, artists-set-group, artists-shuffle,
select-artist). Controller + view follow.

cc
2026-05-27 14:28:32 -06:00
vicrodh a75911b09b feat(slint): favorites Playlists grid/list toggle + List view (persisted)
Add the user-requested grid/list switch to the Playlists toolbar
(parameterized the shared ViewToggle so albums + playlists both drive it)
and a new PlaylistListRow (collage thumb + name + owner·tracks + play +
overflow menu: play/play-next/queue/favorite), bound to playlists-view-mode.
The view mode persists via favorites_prefs (playlists_view). Tauri ships
grid only — the list view is a labeled Slint-better addition.

cc
2026-05-27 14:21:27 -06:00
vicrodh 116bc13660 feat(slint): favorites Playlists view — sub-tabs + rich card grid
Swap the passive single-cover FavPlaylistCard tile for the shared
discover/PlaylistCard (collage + hover overlay play/fav/more + context
menu) over playlists-visible, and add the Library/Following underline
sub-tab selector (no pills, ADR-008) above the grid with per-sub-tab and
search-empty states. Extend the shared PlaylistCard with a body-opens
prop so a favorites card-body click opens the playlist while the play
button plays (discover/search keep body=play, unchanged). Add the
Playlists search box to the row-2 toolbar. Remove the dead
FavoritePlaylistItem struct + playlists field + FavPlaylistCard.

cc
2026-05-27 14:11:31 -06:00
vicrodh bfb3c9b9b5 feat(slint): scaffold favorites Playlists two-sub-tab state + actions
Add the FavoritesState fields for the Playlists port: two SearchPlaylistItem
sources (playlists-favorites = local Library, playlists-following = Qobuz
followed) + playlists-visible (current sub-tab after search), the transient
sub-tab/search/view-mode strings, and the following-count badge. Add the
FavoritesActions callbacks (playlists-set-sub-tab, search-playlists,
playlists-set-view, playlist-action). Wiring + controller follow.

cc
2026-05-27 13:32:53 -06:00
vicrodh 5240cf6e41 feat(slint): search box for the favorites Labels tab
Add an ExpandableSearch to the Labels toolbar (mirrors tracks/albums),
wired to labels-search + the new derive_labels controller fn, which
forks a name-filtered clone of the loaded labels and shares the full
model when the query is empty (artwork stays live). apply_favorites now
derives the visible set on load.

cc
2026-05-27 13:19:33 -06:00
vicrodh d731c17f31 feat(slint): favorites Labels render a circular-image grid card
Replace the generic disc-icon row list (FavLabelRow) with FavLabelCard:
a 140px circular cover in an auto-fill grid, mirroring FavArtistCard and
the artists-grid layout. No-image labels show a disc-3 placeholder on an
indigo->violet gradient (1:1 with Tauri's label card). The grid renders
labels-visible and adds the "No labels match your search" state.

cc
2026-05-27 13:14:11 -06:00
vicrodh 095be4ff57 feat(slint): carry the dropped favorites-label image through the data path
The favorites-labels wire item (LegacyLabelDto) carries an `image`
field that FavLabel silently dropped, so label cards had no artwork.
Parse it (bare string or the object form, via label::extract_label_image),
plumb it onto LabelCard + FavoriteLabelItem, and issue a FavoriteLabel
artwork job + apply arm mirroring favorite artists. Align the count line
to "{n} albums" (matches Tauri and the sibling artist card) and seed
labels-visible for the upcoming search filter.

cc
2026-05-27 13:09:27 -06:00
vicrodh 4cdd015209 style(slint): match QbzSelect dropdown option font to the closed control
In sm mode the open list options rendered at the standard body size while
the collapsed control shows 12px, so the dropdown looked oddly larger.
Use the same 12px for the option rows when sm (standard size unchanged).

cc
2026-05-27 11:59:49 -06:00
vicrodh 1d4b5c7868 style(slint): match toolbar selects to genre button + sharp toggle + grouped list rows
- QbzSelect sm now matches the favorites genre button: no outline, 6px
  corners (was Radius.sm 8px), 12px label (was body). Standard size
  (Settings) unchanged.
- ToggleButton snaps its icon to whole pixels (the HeaderBar/QueueSidebar
  treatment) so the grid/list glyphs render crisp instead of blurry.
- AlbumCollectionView grouped sections now follow the view mode: in list
  mode each group renders AlbumListRow rows (column header once on top)
  instead of always a card grid, so group + list no longer conflict.

cc
2026-05-27 11:45:52 -06:00
vicrodh 159310bbf0 style(slint): sm toolbar variant + center bulk-bar actions
Add a Bootstrap-style `sm` flag to the shared toolbar primitives
(QbzSelect, ExpandableSearch, ToggleButton) — default stays the standard
size (Settings keeps it), and the favorites toolbar opts in (genre /
group / sort / search / grid-list now ~30px). FavGenreButton + the
favorites grid/list ViewToggle shrink to match. Also vertically center
the MultiSelectBar action buttons (only the count was centered before).

cc
2026-05-27 11:16:38 -06:00
vicrodh dbd6d2e4b5 feat(slint): A-Z jump strip for favorites tracks + albums grouping
Add an AlphaStrip overlaid on the right edge when grouping
alphabetically: tracks "Group: Name" (jumps the virtualized ListView to
the first row of a letter — accurate, uniform 50px rows) and albums
"Group: A-Z" (jumps the grid Flickable proportionally by section ordinal
— approximate, always lands the section in view, per the agreed
ArtistView-style behavior). derive_tracks/derive_albums build the
per-letter jump model (tracks-alpha / albums-alpha).

cc
2026-05-27 10:38:34 -06:00
vicrodh 4465a36716 feat(slint): virtualize the favorites tracks list
Drop the redundant "N tracks" / "N albums" toolbar count (the tab-menu
badge is now the permanent counter) and split the content area: the
Tracks tab renders in a virtualized, self-scrolling ListView +
ListScrollbar (the PlaylistView pattern, now possible thanks to the
fixed-chrome restructure), while Albums/Artists/Playlists/Labels keep a
Flickable. Loading/error states center in the content area. Group headers
+ all per-row behavior are preserved inside the ListView items.

cc
2026-05-27 10:29:16 -06:00
vicrodh f8c8808ba7 refactor(slint): favorites fixed-chrome header restructure
Rework all five favorites views into a fixed chrome block over a single
scrolling content area (prerequisite for list virtualization):
- Row 1: back/forward + "Favorites" title inline (left), the tab menu as
  one floating group (right) — new FavTabMenu.
- Row 2: per-view action buttons (Play/Shuffle/select) float left, the
  per-view toolbar (count + search + genre + group + sort + grid/list)
  floats right. Lifted out of each tab's content into the fixed row.
- The lists/grids now scroll in a self-contained area below the divider
  (own Flickable + ListScrollbar), instead of the whole page scrolling.
Behavior (search/sort/group/genre/multi-select/headers) unchanged.

cc
2026-05-27 09:39:07 -06:00
vicrodh 3580c04ca6 feat(slint): visible group headers for favorites tracks grouping
When grouping the favorites tracks by album or artist, render a header
row each time the group key changes (computed inline from the previous
visible row, no extra model field), and hide the redundant album column
in album-group mode (mirrors Tauri). Name grouping keeps its alphabetical
reorder. Completes the group-by UX without a structural list rewrite.

cc
2026-05-27 09:05:16 -06:00
vicrodh 07c71adb65 feat(slint): favorites bulk Play next / Add to queue / Add to playlist
Extend the favorites tracks bulk bar beyond Remove: Play next and Add to
queue build the queue from the already-loaded Track objects (new
playback::enqueue_tracks, no re-fetch; play-next inserts in order), and
Add to playlist opens the picker for the whole selection. The picker
gains track-ids for bulk add (open_multi); on_pick adds every id via the
existing add_tracks_to_playlist. Make-offline stays out (no Slint
offline-cache path yet).

cc
2026-05-27 08:41:12 -06:00
vicrodh 8355f18e0a feat(slint): persist favorites toolbar choices across restarts
New favorites_prefs module stores albums view/sort/group + tracks group
in <data-dir>/qbz/favorites_ui.json (search stays transient). Loaded into
FavoritesState on each favorites navigation (before the data applies),
saved on every toolbar change; apply_favorites now re-derives on load so
the restored sort/group take effect (also fixes a latent case where a set
group mode wasn't applied after a tab reload). Album view toggle routes
through an albums-set-view action to persist it.

cc
2026-05-27 08:27:34 -06:00
vicrodh 4dddd36073 feat(slint): per-context genre filter + favorites genre filtering
Make genre_filter.rs selection per-context (HashMap keyed by "discover" /
"favorites" + a current context the popup edits), so favorites filters
independently from Discover. Discover keeps its server-side path
unchanged (reads filter_ids("discover") explicitly; its genre buttons set
the "discover" context on open). Favorites get a genre button in both
toolbars that opens the shared popup in the "favorites" context, plus the
popup overlay; derive_tracks / derive_albums filter client-side by the
selected genre NAMES (selected_names expands parents to descendants).
on_toggle / on_clear branch on the current context to re-derive favorites
instead of re-fetching Discover. AlbumSummary gains an optional `genre`
(favorites track album objects carry it; TrackItem can't, so a track
id->genre map backs the tracks filter). Persisted per-context (legacy
flat selection migrates to "discover").

cc
2026-05-27 08:03:56 -06:00
vicrodh 9c273119a6 feat(slint): favorites load error + retry state
navigate_favorites stores the load error in FavoritesState.load-error
(cleared on each load attempt); the view shows a "Couldn't load
favorites" + detail + Retry block when set, and the tracks/albums tabs
are gated so the error replaces them instead of showing alongside an
empty state. Retry re-navigates the current tab.

cc
2026-05-27 07:40:22 -06:00
vicrodh 2fce47bd06 feat(slint): favorites tracks group-by (Off / Album / Artist / Name)
Add a group QbzSelect to the Tracks toolbar. derive_tracks reorders the
visible rows by the group key (album+title, artist+album+title, or
title) so a group's tracks sit together. Visible group-header rows are
deferred until the list is virtualized (the list is a plain for today);
this is the group-ordering half, matching how Tauri's tracks tab uses
grouping as the ordering mechanism.

cc
2026-05-27 07:13:07 -06:00
vicrodh 39b7b6d2f1 feat(slint): favorites albums group-by (Off / A-Z / Artist)
Add a group QbzSelect to the Albums toolbar. derive_albums buckets the
filtered set by artist name or by the title's first letter (# for
non-alphabetic), sections ordered alphabetically, rendered through the
shared AlbumCollectionView grouped path. albums-shown carries the
post-filter count so the count line + no-match state stay correct in
grouped mode.

cc
2026-05-27 07:05:17 -06:00
vicrodh 66a9f9da22 fix(slint): favorite state on album cards + un-favorite in place
AlbumCardItem gains is-favorite (filled accent heart on the card +
"Remove from favorites" in the list-row menu) — favorites albums set it
true so the heart shows its state, which it never did before.

Un-favoriting from the favorites list (track heart or album card heart)
now removes the item: the row/card fades out (new `removing` field drives
a 250ms opacity animation on TrackRow / AlbumCard / AlbumListRow), the
favorite is removed on the server (+ fav_cache for tracks), and the row
is dropped from both the full and visible models after the fade. All
TrackItem / AlbumCardItem initializers carry the new fields.

cc
2026-05-27 06:56:26 -06:00
vicrodh 57bace3e1d feat(slint): favorites albums search + sort + shuffle
Add an expandable search (title / artist), a sort QbzSelect
(Default / Title A-Z / Title Z-A / Artist A-Z) and a Shuffle header
action (play a random visible album) to the Albums tab. albums-visible
derives from the full albums set via derive_albums (shared model on the
no-filter/default fast path, filtered+sorted clone otherwise — the
label.rs::derive_releases pattern), and the count reflects the filter.

cc
2026-05-26 23:09:17 -06:00
vicrodh b4b11a59e5 feat(slint): favorites albums list view + grid/list toggle
Replace the bare favorites albums grid with the shared
AlbumCollectionView plus a grid/list segmented toggle and a count line —
adding the list view that was missing (the AlbumListRow extras now fill
via the shared album_map). Album card actions (play / queue / favorite /
go-to) route through a new album-action callback to the album
media-action arms; the artist link still opens the artist.

cc
2026-05-26 22:46:52 -06:00
vicrodh e9836184e7 feat(slint): favorites tracks multi-select + bulk remove
Add a select toggle to the Tracks header that enters multi-select mode
(per-row checkboxes via the shared TrackRow) and shows the shared
MultiSelectBar with Select all / Remove from favorites / Clear. Selection
lives on the rendered tracks-visible rows (mirrors playlist.rs); the
toggle-select router arm and recount gain a Favorites case. Bulk remove
loops remove_favorite, syncs fav_cache, then reloads the tab. Play next /
queue / add-to-playlist / make-offline are deferred (each needs a backend
path: batch enqueue / multi-id picker / offline cache).

cc
2026-05-26 22:40:06 -06:00
vicrodh 6fc337a472 style(slint): shrink track row selection checkbox to ~60%
The multi-select checkbox circle was 22px and read as oversized. Drop it
to 13px (border-radius 6.5px) with an 8px check; the existing
(parent - self)/2 centering keeps it centered in the play/number cell.

cc
2026-05-26 22:31:34 -06:00
vicrodh 43b0c28aee feat(slint): favorites tracks Play all + Shuffle header actions
Retain the loaded favorite tracks as a play-ready Vec<Track> and add
Play all + Shuffle CircleAction buttons by the title (Tracks tab only),
wired to playback::play_tracks. Mirrors the Mix/Playlist transport
(those expose exactly these two; no batch play-next path exists yet).

cc
2026-05-26 22:28:19 -06:00
vicrodh 708fd50c87 feat(slint): favorites tracks search + results count
Add an expandable search over the loaded favorite tracks (title /
artist / album) plus a results-count line. tracks-visible (what the list
renders) derives from the full tracks set: an empty query shares the
model so artwork stays live, a query forks a filtered clone carrying the
already-decoded artwork (the LabelState albums/visible pattern). Adds a
no-match empty state.

cc
2026-05-26 22:18:02 -06:00
vicrodh fd5246a7ce fix(slint): favorites track row album link did not navigate
The Tracks row routed every media-action through track-action, which
forces kind="track"; an album-name click arrived as ("track", album-id,
"open") and was handled as an artist open, so only the artist link
worked. Route album/artist "open" through the wired open-album /
open-artist actions; everything else still goes to track-action.

cc
2026-05-26 22:10:09 -06:00
vicrodh 8dedc2a958 feat(slint): favorites tracks show album column + favorite heart
map_track now carries the album title (was dropped, blanked as ""), and
the Favorites tracks rows enable show-album + show-favorite so the album
column and the un-favorite heart render — matching the Tauri favorites
tracks row. Offline-cache download button stays off until that path is
wired.

cc
2026-05-26 21:35:35 -06:00