New OfflinePlaceholder (mode-aware copy + Open Settings shortcut for induced
mode) mounted at the AppShell routing seam over the 12 Qobuz-only content
views; the matching nav rows (sidebar, header tabs, compact rail) render
disabled while offline instead of hiding (layout stability). Playback gains
the offline playable rule (local always, plex only under induced, qobuz only
when cached and within the subscription grace): explicit plays fast-fail
with an honest toast (distinct copy when the grace expired), manual and
auto advance skip unavailable tracks bounded by 5 consecutive (gapless
target structurally exempt), gapless prefetch skips unplayable successors,
and the network prefetch warmer pauses offline.
cc
Wire the per-history-entry scroll memory (added for Local Library) into
every top-level view: Home/Discover, Album, Local Album, Artist, Musician,
Label, Label Releases, Location, Favorites, Mix, Playlist, Playlist Manager,
Offline Manager, Search, Settings.
Each view's primary scroll container reports its viewport-y and restores it
on back/forward via NavState.restore-scope (scope_for in main.rs maps each
entry to a stable id; tab views carry the tab, e.g. "fav:albums"). The
container restores once per arm, only after content lays out, then clears
the scope. A `changed sr-armed` trigger covers views that persist across
sub-tab navigation (Home's Discover tabs, Favorites' shared list, Playlist
A->B->A) where no fresh mount occurs.
ScrollView-based views (Offline Manager, Settings) use visible-height for
the overflow check; ListView/Flickable views use height. Favorites' shared
albums/artists/playlists/labels Flickable matches the active tab's scope
dynamically.
cc
Ephemeral folders: open / browse / play a folder without indexing it —
album-grouped pane, CUE seek fast-path (no full re-decode per virtual track),
folder-path persistence + rehydrate, "already playing" add-vs-replace choice
dialog (ephemeral-only), and now-playing / queue / menu gating so an ephemeral
track exposes no persistence actions.
Folders tree brought 1:1 with Tauri:
- Rail header: select-mode, collapse-all, open-ephemeral, tree search.
- Resizable rail (default ~20% narrower; drag handle, min 50% default /
max 50% of the content area).
- Subfolder cover grid + grid/list toggle, recursive track count, name search;
compact icon bulk bar (folder-check / play-next / queue / playlist / clear).
- Tree is the default mode; circular checkboxes homologated with TrackRow;
compact rows (smaller type + icon, no ambiguous trailing count).
- Flat/Tree icon toggle (disc-album / folder-tree), floated right.
Per-source TrackRow context menu (logic, not a blanket trim): Qobuz/offline =
full; local = play/next/queue + add-to-playlist; plex/ephemeral = play/next/
queue only.
App-wide standard: Math.round pixel-snap on icon centering across the UI to
kill sub-pixel blur (CircleAction, header back/forward/collapse, and the rest).
NowPlayingBar context icon -> turntable (frees disc-album for the Folders
toggle). New assets: folder-check, folder-tree, turntable.
cc
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
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