Commit Graph
4725 Commits
Author SHA1 Message Date
vicrodh 4430d7fb2f feat(core): add MusicBrainz orchestration methods to QbzCore
Adds qbz-integrations as a dependency of qbz-core and embeds a
MusicBrainzClient on the QbzCore struct (initialized in new()). The
client's own enabled flag governs whether requests fire; calls return
the integration-layer ServiceUnavailable error when disabled.

New methods on QbzCore<A>:

  musicbrainz_is_enabled()
  musicbrainz_set_enabled(enabled)
  musicbrainz_resolve_artist(name) -> Option<ResolvedArtist>
  musicbrainz_get_artist_metadata(mbid) -> ArtistMetadata
  musicbrainz_get_artist_relationships(mbid) -> ArtistRelationships

The metadata method calls location::extract_metadata on the full artist
response and then resolves the real country from the begin_area
hierarchy when a city-level location was found (MB's "country" field is
where the artist is currently active, not where they were born/formed).
The relationships method splits relations into members / past_members /
groups / collaborators by relation_type and direction, mirroring
v2_musicbrainz_get_artist_relationships exactly.

No cache wiring yet — every call hits the network. A cache wrapper can
be plugged in later via a setter without changing the method
signatures.

cc
2026-05-20 08:57:50 -06:00
vicrodh 508609a5d8 refactor(integrations): move MB location and genre helpers to qbz-integrations
Pure-logic move of two helper modules out of src-tauri/src/musicbrainz/
into the qbz-integrations crate, where the MusicBrainzClient and DTOs
already live:

  src-tauri/src/musicbrainz/location_discovery.rs
    -> crates/qbz-integrations/src/musicbrainz/location.rs
  src-tauri/src/musicbrainz/genre_normalization.rs
    -> crates/qbz-integrations/src/musicbrainz/genre.rs

The functions exposed are unchanged: extract_metadata,
compute_affinity_score, build_scene_cache_key, format_life_span_date
(from location); normalize_genre, is_broad_genre, extract_affinity_seeds,
genre_summary (from genre). Imports in src-tauri/src/commands_v2/
integrations.rs are repointed to the new home; the legacy file
declarations are removed from src-tauri/src/musicbrainz/mod.rs. No
behavior change.

Unblocks the Slint side from reaching the same helpers via qbz-core
without depending on src-tauri.

cc
2026-05-20 08:50:50 -06:00
vicrodh b3acb21c37 feat(slint): bucket artist releases by type and surface bio source
Groups releases into five sections (Discography, EPs and Singles, Live
Albums, Compilations, Others). Releases attributed to a different
main artist inside a group are filtered out, and the release id is
deduped across groups so the same album does not appear twice when
Qobuz lists it in multiple buckets. Compilations come from
tracks_appears_on (albums by other artists that include this artist),
deduped by album id.

ArtistData and the ArtistState global gain a bio_source field; the
ArtistPageView renders it as a small "Source: <name>" line under the
Read-more toggle when present (TiVo etc).

cc
2026-05-20 08:21:11 -06:00
vicrodh 8c353ad126 chore(player): sync src-tauri Cargo.lock with qbz-cache/qbz-cmaf deps
The qbz-cache and qbz-cmaf entries on qbz-player landed with the CMAF
playback work yesterday but the lockfile sync was missed. No code
change, lockfile only.

cc
2026-05-20 08:21:03 -06:00
vicrodh d01b1c10d8 feat(slint): artist popular tracks - select button + 5+load more
The Popular Tracks list now caps at 5 rows with a Load more button
instead of dumping 30. Each row gains a Select button (foundation for
multi-select — first surface), and the more-button is always visible
(no longer hover-revealed) so all three controls (play / select /
context) read together. AlbumTrackItem gained a selected: bool and
media-action(\"track\", id, \"toggle-select\") flips the row state via
the handler on ArtistState.top-tracks.

cc
2026-05-19 21:48:45 -06:00
vicrodh e116438023 fix(slint): artist view album covers + bio Read-more toggle
Artist release cards were rendered with empty artwork because no
artwork-pipeline target existed for ArtistState. Added
ArtworkTarget::ArtistRelease and an artist::artwork_jobs builder;
navigate_artist now spawns the cover-download jobs after applying the
page data, so every section grid fills in progressively.

Biography truncation: artist.rs precomputes a word-boundary bio_short
(360 chars), ArtistState exposes bio-short and bio-truncated, and
ArtistPageView shows the short variant with an inline Read more /
Read less toggle.

cc
2026-05-19 21:12:01 -06:00
vicrodh 6d8a2e1fff fix(slint): spinner on home/album/artist + dedupe only in All-tab carousel
Home, Album and Artist views now show the rotating LoadingSpinner (same
as search) above a label while the initial load runs, replacing the
bare Loading… text.

The top-result-artist dedupe moved from map_search_all into apply_search
and replace_category: the full artists list is kept in SearchState.artists
(used by the Artists tab grid), and a separate artists-carousel drops the
duplicate next to the Most-popular hero on the All tab.

cc
2026-05-19 21:00:40 -06:00
vicrodh e2c0d812fe fix(slint): search refinements — artist card, history, spinner, hover
- ArtistCard rebuilt 1:1 with Tauri SearchView.artist-card: 160x220
  inside a surface-card container, 12px corners, 120px circular avatar
  with a gradient placeholder, hover swaps to surface-elevated. The
  Follow chip (rectangular per ADR-008) sits below the name when the
  user does not follow the artist.
- Search loading state uses a rotating spinner (Lucide LoaderCircle on
  Slint transform-rotation) matching the Tauri spinner — 1s linear
  infinite. The search header (title + tabs + filter radios) collapses
  while the initial load runs so the chrome does not show through.
- Search history: live debounce + submit now use
  nav::push_or_replace_search, so the latest query is the single Search
  entry in history. Back from an opened album lands on that search.
- Track row backgrounds use the OR-d row-hovered (cell / fav / dl /
  more), so hovering the more button no longer drops the row tint.
- replace_category artists arm re-applies the top-result dedupe, so
  filtering does not bring the duplicate back.

cc
2026-05-19 20:37:52 -06:00
vicrodh b874661bbf feat(slint): search loading fade + 1s idle-close on header menus
New LoadingDots primitive (three pulsing dots over an optional label)
shown while a search is running. The result sections fade in once the
results arrive instead of showing empty placeholder colors. The
LoadingDots component is reusable for other heavy loads (large grids,
playlist manager) when they get the same treatment.

The header nav-tab menu now closes after 1s of pointer idle (was 1.5s)
per the project rule that no menu stays open more than 1s idle.
PopupWindow context menus already close on click-outside, which is the
idle behaviour for click-triggered popups.

cc
2026-05-19 20:13:14 -06:00
vicrodh e1aa0a71dc feat(slint): full track context menu (13 items, ported from Tauri)
New shared TrackContextMenu primitive with every option the Tauri
TrackMenu shows (flat — no submenus): Play now / Play next / Add to
queue / Create radio / Add to library / Add to mixtape / Add to
playlist / Share Qobuz link / Share Song.link / Make available offline
/ Go to album / Go to artist / Track info. Items already wired in the
MVP (play / play-next / queue / favorite) fire as before; the rest emit
their media-action and stay no-op until the backend lands. Used by
SearchTrackRow, AlbumPageView TrackRow, and ArtistPageView TrackRow.

cc
2026-05-19 20:04:53 -06:00
vicrodh c7129de096 fix(slint): follow chip, playlist card size, track row hover, dedupe
- Follow button on the artist card is no longer a pill — Radius.sm
  rounded rectangle, matching the rest of the app (new ADR-008).
- PlaylistCard sized identically to AlbumCard (220x266) with the same
  220px artwork area; collage and the grid/carousel updated to match.
- Track row play overlay now appears anywhere over the row, not only
  when the pointer is directly over the cell. Slint does not propagate
  has-hover to ancestor TouchAreas, so each row OR-s its own ta with
  every nested TouchArea (play-cell, fav/dl/more) into row-hovered.
- EqualizerBars sit on a fixed 16px baseline so the animation stays
  perfectly centered while the bar heights oscillate.
- TrackPlayCell exposes out hovered so rows can OR it cleanly.
- Search dedupes a top-result artist that is also the first carousel
  entry, removing the redundant duplicate.

cc
2026-05-19 19:53:15 -06:00
vicrodh 7efb83c075 feat(slint): functional play button with playing state on track rows
New shared TrackPlayCell for the track-row leading slot, ported 1:1 from
the Tauri track rows: at rest it shows the cover thumbnail or track
number; on row hover a play triangle; for the current track an animated
three-bar equalizer, swapping to a pause glyph on hover. Clicking a
non-current cell plays the track, clicking the current one toggles
play/pause. Wired into the search, album-page and artist-page track
rows.

cc
2026-05-19 18:25:46 -06:00
vicrodh 95fb45650e fix(slint): widen artists carousel and make the filter apply
- The most-popular row used alignment:start, which packed the artists
  carousel to its preferred width (one card). Dropped it so the
  carousel stretches and shows as many cards as fit.
- The searchType filter only re-queried on per-type tabs (none on All,
  so it did nothing). It now re-queries albums/tracks/artists with the
  search_type and replaces all three lists, so it applies on every tab.

cc
2026-05-19 14:39:28 -06:00
vicrodh 5707601174 fix(slint): search results 1:1 with Tauri
- Most popular: a crowned hero inline with the artists carousel; its
  cover now resolves (a dedicated artwork target was missing).
- Artists: a real carousel on the All tab, ArtistCard restyled to the
  Tauri For-You tile with a Follow chip (favorites the artist) shown
  when the user does not follow it.
- Playlists: a PlaylistCarousel of PlaylistCard, with a count-based
  PlaylistCollage (1/2/3/4-up) instead of a flat row list.
- Carousels gained a View all link; result counts removed from titles.
- searchType filter is a styled radio row (ring + accent dot) at the
  top right with a clear button, not a dropdown.
- Title fixed to Search Results.
- QbzCore::favorite_artist_ids backs the follow state.

cc
2026-05-19 13:59:56 -06:00
vicrodh 61232c05ce fix(slint): rebuild search results view to match Tauri
The results view was bare text rows — nothing like the Tauri search.
Rebuilt it from the components the successful views already use:

- Albums: a Carousel of AlbumCard cover cards (All tab) and an AlbumCard
  grid (Albums tab).
- Artists: new ArtistCard (circular portrait) + ArtistCarousel; an
  ArtistCard grid on the Artists tab.
- Tracks: SearchTrackRow with a position number.
- The history nav pair (NavButtons) added to the view toolbar.

cc
2026-05-19 12:53:46 -06:00
vicrodh fa5f22e609 feat(slint): searchType filter dropdown for per-type tabs
The Albums / Tracks / Artists tabs get a QbzSelect filter (No filter /
Main artist / Performer / Composer / Label / Release name). Changing it
re-queries that category from offset 0 with the search_type and replaces
the list; load-more carries the active filter forward. The filter
resets on every new search. search_type_for_filter is unit-tested.

cc
2026-05-19 12:35:20 -06:00
vicrodh 6a584803f1 fix(slint): trim header nav-tab width by 6px
Drop 2px of left padding and 4px of right padding (after the caret) on
the Discover / Library / Local Library / My QBZ tabs.

cc
2026-05-19 12:30:04 -06:00
vicrodh 0ae66ee57c feat(slint): progressive artwork loading for search results
The artwork module gains SearchAlbum/Track/Artist/Playlist targets;
navigate_search and load-more build cover-download jobs and hand them to
spawn_loads. Covers resolve through the shared qbz_cache image cache
(disk-backed, never re-downloaded) and apply per row, so a late arrival
never resets a list.

cc
2026-05-19 12:27:57 -06:00
vicrodh 573c479d4b feat(slint): load-more pagination for per-type search tabs
The Load more button now fetches the next 20-item page for the active
category via the per-type QbzCore search methods and appends onto the
existing VecModel, so already-loaded rows and their artwork are
untouched. category_for_tab maps the tab index to a category and is
unit-tested.

cc
2026-05-19 12:17:20 -06:00
vicrodh 80f861779c fix(slint): symmetric spacing on the header sidebar toggle
The collapse toggle is a bare icon; the window-edge margin (Spacing.md)
was wider than its ~10px gap to the Discover tab. Drop the tab cluster
x to Spacing.xs so the toggle glyph sits symmetrically.

cc
2026-05-19 12:13:36 -06:00
vicrodh df2ffd2190 feat(slint): tabbed search results view
SearchResultsView is now a real results page: a five-tab strip (All /
Albums / Tracks / Artists / Playlists), an All-tab dashboard (top-result
hero plus a capped preview of each category with View more), and
per-type tabs showing the full loaded list. Album and playlist result
rows are file-local components; tracks reuse SearchTrackRow, artists
reuse SlimCard. The load-more button is in place; its callback is wired
in the next task.

cc
2026-05-19 12:01:34 -06:00
vicrodh 7a40fd660c feat(slint): SearchTrackRow with configurable artwork leading slot
A search-owned track row modelled on the album-page track row, but the
leading slot is a cover thumbnail (search tracks span many albums).
show-artwork keeps the slot configurable for a future shared component.
Compiled once SearchResultsView imports it (next task).

cc
2026-05-19 11:54:36 -06:00
vicrodh bb6fd1e0f3 feat(slint): minimal search results view, functional end to end
SearchResultsView renders the four SearchState categories; mounted in
AppShell on ContentView.search. Phase 1 is now functional end to end:
header input -> debounced search -> QbzCore::search_all -> results.
Phase 2 builds this into the tabbed dashboard.

cc
2026-05-19 11:46:16 -06:00
vicrodh 7073b6553f feat(slint): wire header search input with debounced live search
The header TextInput now submits on Enter (records history, opens the
results page) and live-searches on edit. Live search debounces 300 ms
via a Slint Timer and requires 2+ characters. A monotonic version
counter guards navigate_search so a stale load cannot overwrite newer
results.

cc
2026-05-19 11:42:12 -06:00
vicrodh 37ba47434a feat(slint): wire navigate_search into navigation
navigate_search mirrors navigate_album: reset SearchState, switch to the
search view, run the search off-thread, apply on the event loop. The
apply_entry stub arm is replaced with the real call so history
back/forward re-runs searches.

cc
2026-05-19 11:36:16 -06:00
vicrodh 99d45a9e55 feat(slint): search controller with load/map/apply stages
search.rs follows the album.rs three-stage pattern: load_search fetches
a combined search via QbzCore on a worker thread, the map_* layer turns
domain types into plain Send rows, and apply_search writes SearchState
on the event loop. The map_* functions are unit-tested. Wired into
navigation in the next task.

cc
2026-05-19 11:34:32 -06:00
vicrodh acfb6a05cb feat(slint): add NavEntry::Search history variant
The Search variant carries the query. apply_entry gets a stub arm that
switches the view; Task 6 upgrades it to a full navigate_search call.

cc
2026-05-19 11:26:48 -06:00
vicrodh b040a1d359 feat(slint): add SearchState globals and search ContentView
SearchState/SearchActions globals, SearchTrackItem/SearchPlaylistItem
structs, and a search variant on ContentView. Re-exported from app.slint
so the Rust layer can populate them. Nothing consumes them yet.

cc
2026-05-19 11:18:16 -06:00
vicrodh 9365eb450d feat(core): add QbzCore::search_all with parameterized blacklist
search_all wraps catalog_search, parses the four category pages and the
most_popular hero, and drops blacklisted artists. The blacklist is a
parameter (BlacklistFilter) so Search does not depend on the un-migrated
artist_blacklist module.

cc
2026-05-19 11:03:42 -06:00
vicrodh 1e17032d03 feat(models): move combined-search result types to qbz-models
SearchAllResults and MostPopularItem now live in qbz-models; the Tauri
helpers re-export them under the legacy V2 aliases so command code and
its response serialization are unchanged.

cc
2026-05-19 10:51:14 -06:00
vicrodh 862d408789 fix(slint): crisp button icons, tighter tabs, homologate nav buttons
Icon buttons (header lyrics/queue/hamburger, queue footer) centered
their glyph at a fractional offset (e.g. (36-19)/2 = 8.5px), which
renders blurry — the offset is now rounded to whole pixels. The
header tab spacing dropped from 4px to 2px. The Discover toolbar's
back/forward pair drifted 3px from where the album/artist/settings
views place it; both toolbar clusters now anchor their vertical
center to y=25 so the [<] [>] pair holds one position across views.

cc
2026-05-19 05:05:17 -06:00
vicrodh 9a964477e5 fix(slint): unblock header menu hover-switch, tighten panel + tabs
The click-outside scrim was a full-window TouchArea — it covered the
tabs and intercepted their hover events, so hover-switching between
menus still did not work (same failure mode as the old PopupWindow
grab). The scrim now starts below the header, leaving the tabs
hoverable. The panel also dropped from header-height + 6px to 45px,
removing the unjustified gap between the tab and its dropdown. Header
tab labels lost another 2px of right padding.

cc
2026-05-19 04:53:03 -06:00
vicrodh 5972e8052b feat(slint): in-window overlay for nav-tab dropdowns with hover-switch
Add HeaderMenuOverlay, a plain in-window Rectangle rendered as the last
child of the AppShell root so it draws on top of the header and content
region. Unlike a PopupWindow it does not grab the pointer, so the header
tabs stay hoverable while a menu is open — hovering from one tab to
another overwrites HeaderMenuState and switches the menu instantly.

Behaviour preserved from the old PopupWindow menus:
  - hover-to-open and click-to-open
  - 1.5s idle auto-close, restarted on pointer activity over the panel
    or its rows and on every tab (re)publish via a changed handler
  - click-outside-close via a full-window transparent scrim
  - Discover home-item row still switches to ContentView.home
  - glyphs on all menus except Discover
Menu panel visuals are unchanged: surface-main bg, 1px border-muted,
Radius.sm, 24px drop shadow, 188px wide.

cc
2026-05-19 04:41:11 -06:00
vicrodh b6f5631f51 refactor(slint): publish nav-tab menus into HeaderMenuState global
The four header nav tabs each owned a PopupWindow for their dropdown.
A PopupWindow grabs the pointer while open, so hovering a sibling tab
delivered no hover event and the menus could not be hover-switched.

Add a HeaderMenuState global (open-index, anchor-x, items, home-item)
and the NavMenuEntry struct to state.slint. Tabs now write their
identity into the global on hover/click instead of showing a popup;
the menu panel itself will be rendered by a separate in-window overlay.
Move NavMenuItem into its own file so the overlay can reuse it.

cc
2026-05-19 04:37:26 -06:00
vicrodh 3aa902292e fix(slint): toolbar vertical align, header toggle, queue menu clicks
The Discover toolbar's side layouts had no explicit height, so they
filled the 56px bar and y resolved to 0 — the tab pill, genre filter
and gear stuck to the top instead of centering. Both now carry
height = preferred-height.

The header sidebar toggle dropped from a 19px to a 14px icon: it now
matches the nav-tab icon size and tint, and the 30px box centers it
on whole pixels (was 5.5px → blurry).

The queue row's click TouchArea was declared after the content, so it
sat on top and ate the context-menu button's clicks — the [...] just
skipped to the track. It is now declared before the content, so the
menu button keeps its own clicks.

cc
2026-05-19 04:32:37 -06:00
vicrodh c73e7ac767 feat(slint): add Play next to track and album context menus
Add a "Play next" ContextMenuItem above "Add to queue" in every menu
that offers queueing: the album page track menu, the artist page track
menu, and the discover album card menu. Play next uses the list-plus
icon and Add to queue switches to list-end, matching Tauri's ListPlus /
ListEnd icon mapping. The queue sidebar track menu is left untouched.

cc
2026-05-19 04:18:47 -06:00
vicrodh 6203a2db26 feat(slint): wire play-next media action for tracks and albums
Add play_track_next and enqueue_album_next to playback.rs, calling the
core's add_track_next to insert after the current track. Album play-next
inserts tracks in reverse so they land in order, mirroring Tauri's
v2_add_tracks_to_queue_next. Route ("track"/"album", "play-next")
through the on_media_action handler. Add the lucide list-end icon used
for the existing "Add to queue" item.

cc
2026-05-19 04:18:42 -06:00
vicrodh fe815318db fix(slint): center search placeholders, fix queue menu trigger
Search placeholders rode at the top of the input because the text
had no height to center within — they now span the box like the
TextInput. The queue row context-menu trigger is always visible with
a proper 32px hit area (matching the album track rows) instead of a
hover-only 24px dot cluster, so a near-miss no longer falls through
to the row and skips the queue.

cc
2026-05-19 04:13:12 -06:00
vicrodh 3134b83d72 feat(slint): move the sidebar toggle into the header
The sidebar collapse toggle is now an icon-only button at the far
left of the header, before the Discover tab. Removed from NavButtons,
which is now just the back/forward history pair.

cc
2026-05-19 04:13:12 -06:00
vicrodh a65b5de535 fix(slint): group Discover history + tabs left, fix toolbar overlap
The right controls (genre filter + gear) used x = parent.width -
self.width - 32px on a width-less HorizontalLayout, which fills the
toolbar so self.width = full width and the cluster landed far left,
on top of the history arrows. Side layouts now carry an explicit
width = preferred-width. The three-tab bar is no longer centered: it
sits next to the history buttons with a 16px gap. The home-gear icon
grew 2px.

cc
2026-05-19 04:04:33 -06:00
vicrodh e822bf6601 chore(slint): add home-gear icon asset
Add the home-gear.svg icon used by the Discover toolbar sort gear
button.

cc
2026-05-19 03:20:18 -06:00
vicrodh 787973dfc3 feat(slint): make header search a real input, narrow to 80%
Replace the header search field's static placeholder text with a real
TextInput plus an empty-state placeholder, matching the album
track-search pattern so the field clearly reads as an input. Reduce the
search field width to 80% of its prior size (224/320px to 179/256px).

cc
2026-05-19 03:20:12 -06:00
vicrodh b6355dc202 feat(slint): auto-close nav-tab dropdowns on idle
Add a 1.5s idle-close timer to each header nav-tab dropdown. The timer
is armed when the menu opens and reset on any pointer hover or movement
over a menu row; on timeout the popup closes itself. Single-open is
already enforced by Slint, since only one PopupWindow can be open per
window, so sweeping across hover-to-open tabs closes the previous menu
automatically. Hover-to-open and click-to-open both remain working.

cc
2026-05-19 03:20:06 -06:00
vicrodh e48c9331e2 style(slint): center Discover tab bar and swap sort gear icon
Anchor the Discover toolbar's history buttons and genre/gear controls
to the toolbar edges and absolutely center the three-tab nav pill so it
sits at the true horizontal center. Swap the sort gear icon to the
home-gear asset.

cc
2026-05-19 03:13:36 -06:00
vicrodh 6b31abcea4 style(slint): restyle sidebar toggle and history nav buttons
Make the sidebar collapse toggle a bare icon button with no background
pill, brightest text-primary tint, and a 2px larger icon. Restyle the
back/forward history buttons to match the Discover carousel page
navigators (circular elevated buttons) instead of the tab pill style.

cc
2026-05-19 03:10:07 -06:00
vicrodh 4fb9270513 feat(slint): wire gapless playback and prefetch into controller
Port Tauri's prefetch + gapless design into the Slint playback
controller. Gapless only works when the upcoming track is a cache hit
(plays via play_data), so prefetch is a prerequisite.

Prefetch: after_track_change now calls kick_prefetch, which peeks the
next 2 upcoming queue tracks and background-downloads each uncached,
non-local one into the player cache via Player::prefetch_into_cache.
Concurrency is bounded to 2 by a shared semaphore. Mirrors Tauri's
spawn_v2_prefetch (PREFETCH_LOOKAHEAD / MAX_CONCURRENT_PREFETCH).

Gapless trigger: the poll loop watches the player event; when
gapless_ready is set and gapless_next_track_id is 0, it resolves the
next upcoming track, fetches its bytes (L1 -> L2 -> CMAF) via
fetch_for_gapless, and calls Player::play_next. A gapless_requested_for
guard stops the 450ms ticker from re-firing while the download runs;
the current track is never queued as its own next.

Seamless-transition detection: when the engine performs the gapless
handoff the track changes while is_playing stays true (no stop). The
loop detects the track-id change and advances the core queue pointer,
refreshes now-playing metadata and the sidebar, and kicks prefetch for
the new track's successors -- without calling the audible play path.
The existing stop-then-play auto-advance still handles the non-gapless
(first/streamed track) case; gapless_requested_for is reset there too.

cc
2026-05-19 03:03:41 -06:00
vicrodh 3a6aae19a3 feat(player): add prefetch and gapless cache helpers
Add three public Player methods that drive gapless playback without
touching the audio thread:

- prefetch_into_cache: download an upcoming track fully into L1/L2
  cache so it later plays via play_data (cache hit) rather than the
  streaming path. CMAF download_full first, legacy full download as
  fallback. No-ops on streaming_only / already-cached / in-flight.
- is_track_cached: cheap L1/L2 membership check for the controller.
- fetch_for_gapless: resolve a track's bytes for a gapless handoff
  via L1 memory then L2 disk then CMAF download_full. Ports the
  L1/L2/CMAF tiers of Tauri's v2_play_next_gapless.

All three reuse existing primitives (audio_cache, download_audio);
the protected audio-thread command handlers are untouched.

cc
2026-05-19 03:01:22 -06:00
vicrodh ff1ad3c619 test(slint): switch the font test to bundled Inter
Replaces the LINE Seed JP probe with Inter 18pt (Regular/Medium/
SemiBold/Bold static faces, compile-time embedded). Inter is a clean,
screen-tuned UI face; with the femtovg #5177/#11335 fixes it is the
candidate for the final look. FONT_TEST_INTER toggles back to the KDE
system font.

cc
2026-05-19 02:50:21 -06:00
vicrodh 0606826df4 feat(slint): match queue footer to Tauri and add the track menu
Footer now carries the five Tauri controls with Tauri's icons — clear
(trash-list), save (add-to-list), infinite, sleep timer (clock), and
search; the timer button is visual-only for now. Queue rows gain a
context menu 1:1 with the Tauri queue track menu (Remove from queue,
Stop after this, Remove all after, Add to playlist, Track info); only
Remove is wired, the rest are placeholders.

cc
2026-05-19 02:50:21 -06:00
vicrodh 21b0ade357 fix(slint): tighten the header right-side controls by 2px
cc
2026-05-19 02:50:05 -06:00