The four msgids fix 1 left en-only — the streaming-quality-setting cause
line, the no-higher-quality cause line, and the offline/cached over-cap
picker suffixes — now carry real translations in es/de/fr/pt/ru/ja/nl,
inserted in the same tail-block order as en.po. Register and vocabulary
match each catalog's existing quality-cap block (es tu/neutral, de Sie,
fr vous, pt-PT, ru formal, nl je) and reuse the catalogs' established
terms (Calidad de streaming, Titel, piste, faixa, качество потока,
ストリーミング音質, nummer). Informative tone throughout — a downgrade
the user configured is stated as fact, never as an error.
With this, every user-visible string introduced by the four #638 fixes
exists in all eight bundled catalogs (verified against the full
pre-release..HEAD diff, 16 msgids x 8 locales).
cc
CastState gains the fix-4 cap-row surface (device-cap-available/-key/
-options/-index + global-quality-label) and CastActions the
set-device-quality-cap(cap-key, index) callback - property/callback
additions to existing globals only, no app.slint export change. The
connected panel gets one label-left / QbzSelect-trailing row per the
flyout alignment rule (explicit menu-width, popup wider for the long
option labels, no pills), visible only when the renderer has a stable
identity, plus the muted caption that is the literal answer to #638:
Qobuz sells four discrete tiers, there is no 48 kHz option.
The quality-line suffix property now also names the cap at work
(cause 3, 'capped for this device') when no over-cap disclosure takes
precedence. Copy discipline throughout: the cap is the user's own
quality/bandwidth choice - never an implied device incapability
(renderers accept ~anything and downsample internally).
i18n: 8 new msgids in ALL EIGHT locales (en identity-translated),
including the cause-3 tooltip line AudioStamp pre-wired in fix 1.
Unit style matched per catalog (ru Cyrillic units, nl decimal comma).
Validated with the slintcheck stub (full tree + all catalogs).
cc
Settings > Audio (#638 fix 3, spec C.6): the "Limit quality to device"
toggle's description now states what the wired setting really does — it
caps the REQUESTED tier, local playback only, never casting. The
conditional "Maximum sample rate" Hz dropdown is replaced by a read-only
"Detected device limit" line ("192 kHz · Hi-Res+") fed by the probe: a Hz
control was the documented root-cause trap (it offered rates the app
cannot request — Qobuz sells four tiers, and no 48 kHz tier exists), and
with real detection there is nothing left to pick. When the probe fell
back to the assumed common rate set, a plain informative caveat below the
row discloses that the cap still applies but may not match the hardware
(owner Decision B: no warning icon, no error styling).
Removes the dead dropdown plumbing from the desktop bridge only:
MAX_SAMPLE_RATES, the SettingsState sample-rates/sample-rate-index
properties, the snapshot fields/derivation/pushes, the "sample-rate"
select arm (and with it a dead Apply::Reinit device teardown for a value
nothing read), and the table test. Deliberately KEPT per the spec's
hazard analysis: every qbz-audio SQLite column/migration/setter/getter
(positional 23-column SELECT), the qbzd CLI/TUI surfaces, the settings-
bundle entries, and all existing .po msgids (msgmerge will mark obsolete
ones; "No limit" is reused elsewhere) — grep hits for the old identifiers
under crates/qbzd/ are expected and correct.
3 new msgids in all 8 locales. Slint tree + catalogs validated with the
slintcheck stub.
cc
Wires limit_quality_to_device to a real consumer on the LOCAL output path
(#638 fix 3, spec Phase C). New crate::device_cap module caches the DAC's
detected rate ceiling mapped to a Qobuz tier (spec C.3: >96k -> Hi-Res+ =
no effective cap; 96/88.2k -> Hi-Res; <=48k -> CD 16/44.1, bit depth
included since no 48 kHz tier exists). Detection is the proven read-only
query_dac_capabilities probe; it runs in spawn_blocking on EXPLICIT
triggers only (startup, the Settings toggle, output-device change, backend
change, reset-to-defaults) — never on the playback hot path or poll tick.
A stale cap after a hot-unplug until the next device change is the
accepted trade, same class as the HiFi wizard.
playback::local_playback_quality() returns (tier, QualityLimit): the
preference clamped by the cap, with the request-time cause for the badge
tooltip (tie reports LocalDeviceCap — the more specific constraint,
mirroring the spec's cast tie rule). All LOCAL request sites switch to it:
play_track_resolved, prefetch (fetch + throttle estimate), the gapless
pre-fetch, and the now-playing badge seed (which can now name the device
as the downgrade cause, msgid x8 locales).
PRECEDENCE (owner decision): the device actually playing governs.
playback_quality() stays the PURE preference and the cast path keeps
calling it — cast_service is untouched and the play_audible cast branch
returns before any local resolve, so the local DAC cap is structurally
unreachable from a cast. The two settings stay independent; nothing is
auto-synced. No downsampling anywhere: the cap only changes which tier is
REQUESTED; cached/offline bytes still play as-is.
Settings plumbing: SettingsState gains device-cap-summary/-detected
(property additions to an existing global — no app.slint export change);
build_snapshot fills them from the cache; refresh_device_cap re-probes and
re-pushes on the explicit triggers.
Protected-crate note: no protected file is touched — qbz-audio is only
READ through the dac_capabilities probe; qbz-player, playback_engine.rs,
alsa_direct.rs, alsa_backend.rs and the backends are untouched; no
sample-rate handling changes.
cc
Fix-1 cast half (spec Phase 1). During cast there is no PlaybackEvent —
the renderer is playing, not our engine — so the delivered quality is now
MEASURED from the bytes about to be served: register_qobuz probes the FLAC
STREAMINFO (shared qbz-models prober) before register_audio moves them,
which reports the truth regardless of which tier (network / L1/L2 cache /
offline store) produced the bytes.
- CastPicker line: measured label+detail via the shared formatter
("Hi-Res FLAC", "24-bit / 96 kHz"); quality_label_from_track demoted to
the fallback for non-FLAC/local casts (F20 closed).
- The now-playing badge stays live during cast: cast_track publishes the
85e11d28 effective properties plus the fix-1 cause/effective-tier through
the SAME shared helpers the local poll uses (stream_downgraded /
classify_limit_cause / delivered_tier_str — no forked arithmetic). The
cause is the cast request-time resolution: preference today, renderer cap
when fix 4 lands; NEVER the local DAC cap (precedence rule).
- Over-cap disclosure: origin != network AND measured tier > requested tier
-> logged at info and surfaced on the picker line as "offline copy — cap
not applied" / "cached copy — cap not applied" (Decision C: local bytes
go out as-is, never resampled, never re-fetched — disclosed, not hidden).
Three new CastState properties (quality-limit-cause, quality-over-cap,
quality-origin), reset on disconnect; properties on existing globals only,
no app.slint export change.
- remote_stream.rs now calls the shared prober, keeping its own CD-shaped
defaults at the call site — behavior byte-identical (F17/F21).
- Two new msgids land in en.po; remaining locales come with the i18n pass.
Validated with the slintcheck stub (full .slint tree + 8 .po catalogs).
cc
Phase A of the #638 fixes (owner decision, reverses the deliberate #590
catalog-max choice back to Tauri behavior): while the delivered stream is
below the track's catalog max, the AudioStamp main line shows the DELIVERED
tier/detail, the amber arrow stays on, and the tooltip gains a third line
naming WHY — Source/Output plus the cause. A downgrade caused by the user's
own setting is the app doing what it was told, so the strings are
informative, never alarming.
Mechanics:
- REQUESTED_QUALITY_ID / REQUESTED_CAUSE atomics seeded once per track
change beside TRACK_MAX_* (one ui_prefs load per change, never per 450ms
poll tick), gated to Qobuz sources — local/Plex/ephemeral are not
governed by the streaming preference and never get a cause line.
- Display-time classification (classify_limit_cause): requested Hi-Res+ or
delivered below the requested tier's promise -> catalog availability;
promise met -> the request-time cause (the user's preference today; the
device/renderer caps of fixes 3/4 reuse the same discriminants).
- The #590 downgrade arithmetic (0.9 rate-family guard + full DSD
exemption) is extracted UNCHANGED into stream_downgraded so the fix-1
cast half reuses it instead of forking it.
- Two new NowPlayingState properties (quality-limit-cause int,
quality-effective-tier string) on the existing global — no app.slint
export change — reset on track change, mirrored to the miniplayer.
- Causes 2 (local device cap) and 3 (renderer cap) are pre-wired in the
tooltip chain but unreachable until fixes 3/4 land; their .po entries
ship with those fixes. The two reachable msgids land in en.po here; the
remaining locales are populated by the i18n pass.
Validated with the slintcheck stub (full .slint tree + 8 .po catalogs).
cc
Tauri parity (the sections did not survive the Slint port): a Settings
section shown only on sandboxed installs holding the one-time permission
commands with copy-to-clipboard buttons. Flatpak: exclusive-DAC D-Bus
reservation override (the user-side fix for #641's exclusive-mode failure
— the Flathub manifest cannot carry the wildcard own-name past its linter,
so the user grants it locally) and NAS/external-drive filesystem access.
Snap: the audio interface connections (qbz-player:alsa/pulseaudio/
pipewire/mpris) plus optional removable-media. New SandboxState global
seeded from qbz_app::diagnostics install_method; 14 msgids across all 8
bundled locales.
km
The card overlay's context menu used a fixed x:26/y:56 anchor, which put
the menu's vertical MIDDLE on the cursor (menu is 196x208, the button
sits at ~(152,142) of the artwork box). Natural context-menu behavior is
top-left at the click point. The OverlayButton clicked callback carries
no pointer coordinates, so anchor at the more-button's center (the
cursor proxy), converted into the artwork box's coordinate space via
absolute positions (artwork container gets an id for that).
km
Popup windows are always inlined by the Slint compiler, so every
`track-menu := TrackContextMenu` site — TrackRow in ~15 repeaters plus
SearchTrackHero — generated its own full copy: 18 structs, ~22k lines of
the 1.9M-line generated module, for one logical menu.
Now a single TrackContextMenu lives in AppShell, fed by the new
TrackMenuState global. Openers (TrackRow right-click / "..." gutter,
SearchTrackHero more button) write every gate input and bump
`open-token`; the shell instance watches the token and shows at the
pre-computed window coordinates (same scroll-safe absolute-position math
as the drag ghost). Gate expressions and dispatch ported 1:1 from the
former per-site instances — local-album view routes play/play-track +
track-menu-action, everything else hits the central media-action handler.
Menu closes on item click and on navigation.
QueueSidebar's queue menu and TrackCard's card menu are different item
sets and stay local (single instances, no multiplication).
Spec: qbz-nix-docs/context-menu-unification/2026-07-17-single-context-menu-spec.md
km
Replace the bespoke SceneArtistCard in ArtistsByLocationView with the
app-wide ArtistGridCard (follow / play / pin / context menu, homologated
with the other artist carousels). The scene model rides the shared
SlimItem (LocationArtistItem deleted): genres land in the subtitle — the
card's second row under the name, as requested. Follow and pin states
seed from the disk-backed caches at map time; mark_artist_followed now
walks the scene model too, so toggles from any surface flip the chips
live. ArtworkTarget::LocationArtist writeback updated for the SlimItem
artwork field; media-action routed through AppShell. Cards at 200x246
(AlbumCard parity).
km
The hint belongs on static subtrees; the Sidebar root toggles by
animating its width every open/close, so its cached layer texture was
perpetually invalidated and the layer blit ended up pointing at a dead
texture — sidebar content invisible with layout/hit-testing intact
(repro: open/close sidebar; miniplayer->theme->sidebar journey).
HeaderBar/NowPlayingBar keep their hints (static-mounted, no size
animation). The Sidebar's per-frame cost is covered by the femtovg
partial-render fork (#617) instead.
km
Lands the #617 line on pre-release:
- femtovg partial rendering, stages 1-4 (efc2db6d, ed897d8c) — persistent
scene texture + dirty-region repaint, env-gated OFF by default
(SLINT_FEMTOVG_PARTIAL_RENDERING; frozen-blit/visualize/log modes).
- Adversarial-review blocker fixes (1adf4e6f): femtovg set_size target
desync root-fix, dirty-region collapse to bounding rect, visualize on
the swapchain.
- cache-rendering-hint on static shell chrome (e3257631).
- Playing-row highlight/animation for offline-cache tracks (c6da4844).
Owner-smoked: visual matrix clean with visualize, idle CPU acceptable on
the owner's box; default behavior unchanged with the gate off.
km
Local track rows bind the local-library DB row id, but local_queue_track
publishes the Qobuz catalog id for offline copies (so the shared
resolver finds them), so NowPlayingState.track-id never matched the
row: no equalizer bars, no active highlight, and double-click restarted
the track instead of toggling (album view, Tracks tab, folder detail,
kiosk).
Carry the library row id in source_item_id_hint for qobuz_download and
re-publish it as NowPlayingState.local-track-id; TrackPlayCell,
TrackRow and KioskAlbum now match on either id — one alias, no
per-view hacks.
km
cache-rendering-hint: true on HeaderBar, NowPlayingBar and Sidebar roots so
femtovg caches those subtrees to a persistent texture instead of
re-tessellating them on every repaint. Sidebar root is safe: the Large-NPB
spectrum dock is an AppShell overlay, not a Sidebar child. SMOKE-WATCH:
playlists-list scroll (Flickable-inside-cache); narrow to nav-only if it
regresses.
Authored by Claude (cc) in the repaint-cache-hint worktree; committed
as-is by km so both #617 lines share one canonical commit.
km
Issue #637: a Qobuz outage + forced re-login left a user's account
transiently 403'd, and the no-backoff prefetch retry storm escalated it
into a per-IP edge/WAF block (the "error decoding response body" lines are
that non-JSON WAF body hitting a bare .json()). The bundle bump b019->b034
is exonerated: extraction yields byte-identical app_id/secret/private_key.
- ForbiddenBreaker: after repeated 403s the hot streaming/favorites paths
short-circuit without touching the network, with an exponential cooldown
and a half-open probe; success resets it.
- get_stream_url: typed 401/403 + body preview; abort the quality fallback
loop on 403/401/breaker-open instead of firing all five qualities.
- CMAF session/start + file/url: feed the breaker, map 403.
- get_favorites + qws/createToken: check status before .json() so a 403
surfaces as a 403, not a misleading "error decoding response body".
- Prefetch: per-track failure cooldown (negative cache) so a failed track
is not re-hammered every queue tick.
- Audible path: a 403/back-off no longer counts as an "unavailable" skip;
stop cleanly with a toast.
- QBZ_FORCE_403 test hook to smoke the back-off path on a healthy account.
- Header action-button tooltips for AlbumView/ArtistView/PlaylistView,
translated across all 8 locales.
cc
PlaylistCard now reflects ownership: owned playlists show favorite only;
foreign Qobuz playlists show follow/unfollow on the overlay plus follow +
copy in the context menu. Added is-owned/is-following/is-copied to
SearchPlaylistItem, seeded from the favorites owned/followed split and
owner.id == current_user_id elsewhere. Wired the actions id-scoped across
both routing families via shared helpers (copy/follow/favorite), so a card
toggles ITS playlist instead of the open detail.
Library "All" toolbar: genre filter (own "library-all" context over the
mixed feed; albums + tracks carry a genre, non-genre kinds hide when a genre
is selected) and PlaylistView-style sort menu (re-select the active field to
flip asc/desc). Tooltips on the four source toggles.
Live favorite/follow sweep across every card surface that was missed:
album favorite now reaches the Pinned carousel, the Recently Played "view
all" page and the artist "In library" grid; artist follow reaches Home Top
Artists, label-page artists and Pinned; new set_playlist_row_following flips
the follow chip on every playlist card (incl. Pinned) after subscribe.
cc
The OSK opened on kiosk search/inputs but never closed: focus stayed on
the search field. Clear focus in the field's `accepted` handler and add a
full-bleed background TouchArea that clears focus on any tap outside the
input, so the on-screen keyboard dismisses.
cc
The std ScrollView does not intercept a touch-drag that starts over the
dense SettingRow controls, so Settings did not finger-scroll on the Pi
kiosk. Replace it with a raw Flickable (intercepts the drag after a few
px) plus a ListScrollbar overlay for the desktop bar; scroll-position
restore now keys on the Flickable's own height.
cc
- Tray restore no longer crashes (RefCell panic on close / segfault on restore):
clear ImmersiveState.shader-texture in tray::hide_window BEFORE the surface is
destroyed (UI thread, no rendering-notifier RefCell borrow), instead of a
deferred clear in the RenderingTeardown notifier that ran too late when the
drain was parked (paused). The 30 fps drain repopulates it after restore.
- All list view: right-click opens the context menu AT THE CURSOR (window
pointer minus the row's absolute position); the whole row is right-click
sensitive (per-cell handlers), while the ⋯ button keeps anchoring the menu to
itself. Hide the source column + grid source badges when show-local is off
(all-Qobuz feed → no per-row Qobuz mark).
- Remove the now-unused FavArtistCard / FavLabelCard components (the Favorites
Artists/Labels grids use ArtistGridCard / LabelCard).
cc
- Rework the "All" list view to the aligned column layout of the mixtape detail
list: artwork + hover play, title + clickable-artist subtitle, a TYPE column,
a dedicated SOURCE column (SourceGlyph), the row QualityBadgeFull in a Quality
column (was the uneven grid badge), favorite/follow indicator, and the per-kind
context menu — no longer bare and out of tune.
- Fix the NVIDIA "wallpaper join" background seam: replace the float hash2 in
ambient.wgsl + plasma.wgsl with a bit-exact integer (u32) lattice hash so the
same cell corner hashes identically from adjacent cells on NVIDIA.
- Fix the close-to-tray "RefCell already mutably borrowed" panic introduced by
the teardown texture-clear: defer the set via upgrade_in_event_loop so it runs
after the window adapter releases its borrow.
- Add "Go to album" to the TrackCard context menu and the list-row menu (adds
album-id to the feed model).
cc
- Fix the tray-restore wgpu crash (pre-existing, amplified by the app-wide
background): clear ImmersiveState.shader-texture on RenderingTeardown so the
first restored frame never hands a cross-instance stale texture to the new
femtovg canvas.
- Local/Plex feed covers now decode: route the "All" artwork jobs through
spawn_search_loads (http / local fs / Plex thumb) at all four dispatch sites;
show the source badge on the feed's album/track cards (AlbumCard maps plain
"qobuz" to the Qobuz logo).
- New card-sized LabelCard (200x246), used by the feed + the Favorites Labels
tab (replaces the round FavLabelCard).
- Artist pin badge updates live in the Favorites Artists grid: add is-pinned to
FavoriteArtistItem, stamp it, bind it in the ArtistGridCard, and flip the
favorites artist models in set_artist_row_pinned.
- Rich "All" list view: type-aware subtitle, quality badge (album/track),
source icon (local/Plex), favorite/follow indicator, and a per-kind context
menu (⋯ button + right-click).
cc
- All feed clicks were dead: the Albums/Artists/Playlists/Labels branch didn't
exclude the "all" tab, so an empty Flickable overlaid the grid and swallowed
every press. Exclude "all" from that branch.
- Local favorites never bound on the online session paths: init_for_user was
only called in the offline shell. Bind it in login + restore, teardown in
logout — so the heart flips and local items reach the "All" feed.
- Add the show-local source switch + the grid/list ViewToggle to the All
toolbar; implement the compact list view.
- ArtistPage "In library" rows rendered blank: add ArtistLibraryTrack/Album
artwork targets + dispatch cover jobs in navigate_artist.
- Move the catalog/library toggle into the artist header, floated right.
- Glass under the dynamic background: ToggleButton / ExpandableSearch /
SegmentedTabBar go translucent when app-background-active.
- Library > Artists grid now uses the ArtistGridCard (flat + A-Z grouped).
cc
- Library "All": mixed feed (tracks/albums/artists/playlists/labels) sorted by
date added, reusing the existing cards; new TrackCard; toolbar (search + source
switches + sort); "All" added to the Library nav surfaces.
- ArtistPage: restored the curated Playlists carousel (parsed but dropped in the
Slint port); added the catalog/library toggle backed by a per-session
per-artist index (favorites).
- Local-library favorites layer: new SQLite store (mirrors the pinned service),
album-level; excludes the Qobuz offline cache at write time. Feeds "All" behind
the show-local switch.
- i18n: 3 new strings across all 8 locales.
cc
The metaball iso-surface + a strong high-frequency domain-warp octave produced
hard, clashing edges between the color lobes (visible over the translucent UI).
Gentle the high-freq warp (lower freq + amplitude), widen the iso-surface
smoothstep so lobes melt into the base with a long gradient instead of a hard
rim, ease the bright/dark contrast, and add a touch more dither against banding
— the flowing motion stays, the ugly edges go.
cc
- label.rs parse_top_track: the /label/page top_tracks carry the main artist in
a track-level `artists` array (roles=main-artist), not performer/artist (null
here), and the album title in `album.title`. Read both (with legacy fallbacks)
so label Popular Tracks show the artist name + album (discussion #631).
TopTrack gains an `album` field, threaded to the row.
- Artist + Label Popular Tracks: show-album: true so the album-title column
renders (owner: album required in all PopularTracks lists).
Note: "Add to Mixtape" is already present in the artist/label Popular-Tracks
multi-select bar (cassette icon, tooltip) and the per-track "..." menu
(mixtape-action = qobuz-row) — no code change needed there.
Base: 6c41f58f.
cc
NVIDIA shader fix (root-caused via diagnostic bisection on the discrete GPU):
- ambient.wgsl + plasma.wgsl: replace the sin-based hash `fract(sin(dot(p,k))*N)`
with the sin-free Dave Hoskins hash. On NVIDIA, as noise coords grow with
time, sin's argument gets huge and range-reduction precision diverges from
Intel's, degenerating the noise into big blocks. Confirmed via a solid-color
clear (clean) then a uniform-passthrough (clean) — isolating it to the shader
hash, not the texture handoff.
- ImmersiveView: the 3 immersive-exit paths (X button, in-menu exit, ESC) kept
the drain alive only for the Large NPB dock, freezing the shader texture on
the last immersive frame when the app-wide Ambient background was on. Now they
also keep it alive for Ambient so it renders the ambient scene after exit.
UI batch:
- Auto-hide scrollbar (Discover's ListScrollbar auto-hide + pointer hit-test)
on AlbumView, ArtistView, PlaylistView, PlaylistManagerView.
- #631: artist Popular Tracks now show the album cover thumbnail (thread
artwork_url through TrackData -> track_data_to_item + a new
ArtworkTarget::ArtistTopTrack decode job, mirroring LabelTopTrack); label
Popular Tracks fall back to the album's main artist for the name+id. Quality
badge already rendered by TrackRow — reused, not reinvented.
- #446: "Add to Mixtape/Collection" bulk button in the Favorites/Artist/Label/
Mix selection bars, mirroring the existing add-to-mixtape path (track items
auto-restrict the picker to mixtapes, not collections).
slintcheck + Opus Rust review green (no compile blockers). Base: 8c2f84b7.
cc
Smoke round 5 (owner feedback). Checkpoint.
- Preferred GPU selector (Settings > Renderer, Linux/wgpu): lists each detected
GPU by name ("Auto" + e.g. "NVIDIA … (discrete)" / "Intel … (integrated)"),
persisted (ui_prefs.gpu_power by device name), applied at startup via the wgpu
adapter PowerPreference (WGPU_POWER_PREF env still wins). Fixes the iGPU running
hot under the shader background on hybrid laptops — pick the discrete GPU.
New gpu_adapters() enumerates+dedups adapters; gpu_power_from_prefs /
_options / _seed_index / _value_for_index.
- Ambient metaball added to the IMMERSIVE scene picker (mode 7, wgpu-only opt-in);
the non-shader atmosphere stays the fallback for GL/software tiers.
- Glass treatment on the surfaces that were still opaque: carousel < > nav
buttons (6 carousels), "Filter by category" pill, "Filter by genre" buttons,
and the PlaylistBrowseView (View-all) header band + category radio row.
- i18n: "Preferred GPU" + description + discrete/integrated + restart toast (8
locales).
Base: 76ea5a82. Prior checkpoint: a0f5cf2b.
cc
Smoke rounds 3-4 (owner feedback). Checkpoint during iteration.
Ambient (the big one): rewrote ambient.wgsl from translating gaussian blobs to a
METABALL field (r²/d² summed + smoothstep iso-surface) with 2-octave domain warp
— lobes fuse/stretch/split like an amoeba / lava lamp (the Cider motion that was
missing), faster clock, more contrast.
Surfaces made background-aware (translucent when the dynamic background is on):
- Search results view (root + toolbar), artist Network panel + its tab-row
header (transparent to avoid double-translucent compounding), JUMP TO bar
(JumpNavBar bar-bg passed in — can't import ShellState without a cycle).
- Discover toolbars + tab pill + BrowseHeaderTools; SearchResultsView toolbar.
- CircleAction (shared: playlist/album/artist/etc. action buttons) on-surface
secondary fill goes glass; playlist Sort dropdown; QbzSelect closed control.
- New app-background-bar-alpha knob (0.3, env QBZ_BG_BAR_ALPHA): thin bars go
MORE transparent than panels (surface-alpha 0.5).
- Content well: frosted tint (0.22) + 1px hairline border → distinct panel.
- Album/Artist header gradient auto-suppressed under the dynamic background
(they clashed); header text stays light on the dynamic backdrop.
- Discover carousel edge-fade "shadows" hidden when active (Carousel /
PlaylistCarousel / ArtistCarousel / PinnedCarousel).
Base: 76ea5a82. Prior checkpoint: 4e405338.
cc
Smoke round 2 (owner feedback: content area was an opaque black slab, motion
imperceptible, ambient≈blurred).
- Content-area sweep: 27 content-view roots go transparent when the app
background is active (was opaque Theme.surface-main covering the background) —
Home/Discover/Album/Artist/Search-adjacent/Favorites/Playlist/LocalLibrary/
MyQBZ/managers/etc. Content well (AppShell) transparent too.
- SongCard (NPB classic), search input box, cortinilla panel go translucent so
they stop reading as opaque slabs over the background.
- ambient.wgsl: much faster + bigger blob travel + stronger domain warp +
brighter/more saturated so the flow is obviously moving and clearly distinct
from the blurred-art mode.
- Defaults: dim 0.35, surface-alpha 0.5.
Checkpoint during smoke iteration (owner-requested). Base: 76ea5a82.
cc
A use-site `changed has-focus` on a std-widgets LineEdit/TextEdit never
fires — has-focus is an alias into the inner TextInput (slint#7747) — so
typing a letter that doubles as a shortcut ran the action instead of
inserting the character. Sweep every text-input surface (search, import,
create/edit playlist & folder, tag editor, colour picker, settings
fields, QConnect dev modal, kiosk shell) and drive
UiFocusState.text-input-focused from a `guard-focused` probe property
that the change tracker does observe.
cc
The 2.0 port kept only the Odesli-by-URL path, which rejects Qobuz URLs
(400 could_not_resolve_entity), so album.link/Song.link never resolved
while the raw Qobuz link still copied fine. Restore the Tauri behaviour:
look the track up on Deezer by ISRC (album by UPC, with a leading-zero
retry) and build song.link/d/{id} / album.link/d/{id} from the match.
cc
Persist the Wayland app_id (set_xdg_app_id) and window geometry/maximized across
tray-hide and miniplayer round-trips so the pinned taskbar icon keeps grouping
and the size/position survive (#618); route the single-instance raise and MPRIS
Raise through a miniplayer-aware present() plus a dedicated D-Bus Present method.
Seed the Local Library Plex refresh button after login (not before, when the
per-user store is unbound). Also the shared wiring these features need in
main.rs / state.slint / app.slint / home.rs / artwork.rs (pinned globals + store
seed + mixed-artwork target, playlist-browse nav, sub-genre reload) and the
bundled i18n strings.
cc
Homologate the large-dock cover's side margins (16px each vs the visible content
bezel) and add a 4px bottom gap by trimming the FFT band 2px top/bottom. Move the
queue count out of the "Up Next" header into a fixed footer ("Showing X of Y
tracks", dropping the redundant "left"). On macOS, inset the flush-left NPB-small
art so the native rounded window corner stops clipping it.
cc
Enable the searchable QbzSelect on the theme picker (36 themes) — the search box
the migration deferred. Rides the #616 theme-filter (icon swap + list narrowing,
already on pre-release); the search filters within the active All/Dark/Light
facet.
cc
Users pin albums/artists/playlists to a configurable "Pinned" section on Home
and For You. New per-user SQLite pinned-items store (headless in qbz-app +
lifecycle wrapper), a mixed PinnedCarousel dispatching Album/ArtistGrid/Playlist
cards by kind, a rotated pin glyph (hover-only) on those cards, and Pin/Unpin
from the playlist header and the album/artist context menus. Local/Plex albums
resolve to their Local Library page (id-format routing) and render their real
covers via the mixed-source artwork dispatcher.
cc
New 200x246 artist card (framed round portrait, follow/play/more hover overlay,
centered wrap-capable name with an optional reco subtitle) matching the album/
playlist card footprint. ArtistCarousel renders it by default across the app
(search "Most popular" hero included); the legacy 160x220 ArtistCard stays for
the label/award carousels whose artist actions don't apply.
cc
Port the Tauri playlist "View all": a new PlaylistBrowseView with the category
tag bar, search filter, genre filter and grid/list toggle, extracted into a
shared BrowseHeaderTools and a reusable PlaylistListRow. Restores the "Popular
albums" see-all and aligns PlaylistCarousel's "View all" affordance with the
standard carousels.
cc
Add the desktop "Export settings…" surface for the qbzd daemon epic (T18):
a Developer-settings row that opens a modal with the single default-off
"Include Qobuz login and integration tokens" gate, its warning line, and a
Cancel/Export footer. Confirm() is a Rust callback (wired next). New
SettingsExportState/SettingsExportActions globals, exported from app.slint
so the Rust layer binds them, and the modal mounted in AppShell. New strings
translated across all 8 locales (en es de fr pt ru ja nl).
cc
Two build breaks from the View-all work: (1) MostPlayedAlbumsState/Actions were export global in state.slint but not re-exported from the app.slint entry, so slint codegen never emitted their Rust bindings — added them to the app.slint export list (verified: the generated app.rs now defines both). (2) adding plays to AlbumCardItem forced every Rust struct literal to set it — added plays: 0 to the six construction sites (album_map/artist/external_reco/foryou/home/search); most_played_item overrides it after card_to_item. slintcheck's .slint validation can't catch the export-list gap (it only bites when the qbz crate consumes the generated types), hence the miss.
cc
Full-page listing opened from the rail's View all (both tabs now wire show-view-all -> HomeActions.open-most-played-albums). New MostPlayedAlbumsView (mirrors RecentAlbumsView) with a search box; AlbumCardItem gains a plays field shown ONLY here (a gated line in AlbumCard). Rust: navigate_most_played_albums loads all_albums(), caches the ranked rows, renders the grid + fires artwork (MostPlayedAlbumsPage target, Qobuz vs Plex/local split); the search box re-filters the cached rows by title/artist and re-renders (mirrors the Qobuz-playlists filter). NavEntry::MostPlayedAlbums + AppShell mount + ContentView variant. Count label in 8 locales. slintcheck green; qbz-crate Rust pending a full build (30GB wall).
cc
Wires the rail (top-20 by play count) into both tabs, mirroring the favoriteAlbums pattern: HomeState/ForYouState.most-played-albums DiscoverSection; foryou::most_played_album_cards maps the ranked rows onto AlbumCard; home.rs builds it inline (local, no fetch) and For You rides the favorites branch; HomeMostPlayedAlbum/ForYouMostPlayedAlbum artwork targets + apply arms; render arms in HomeView/ForYouView (self-hide while empty); title in 8 locales. slintcheck green. View-all page (search + play count) is the next increment.
cc
After #606 the offline Clear-cache purges only the transient cache dirs (tracks/tracks-cmaf/artwork), never the organized downloaded albums — those are removed per-item from the offline manager. The old copy ('Remove every downloaded track. This cannot be undone.') was misleading. Relabels the row 'Clear cache', rewrites the description to say downloads are kept and point to the manager, and softens the toasts to 'Cache cleared' / 'Couldn't clear the cache'. 8 locales.
cc
The immersive VolumeBar only called set-volume (live) and never persist-volume, so a volume set in the immersive view was lost on restart/track-change. Adds a released(float) callback emitted on pointer-up, wired to NowPlayingState.persist-volume — parity with the NPB sliders. Pure .slint; persist-volume already exists in Rust.
cc
Adds a compact dropdown in the Library Albums (favoriteAlbums) row header, left of the page chevrons: Recently added (default), First added (reverse), Random (shuffle). Reorders the cached base list in Rust with no re-fetch and re-fires artwork by the new index. Also corrects the stale HomeView comment that claimed the recently-played refresh lives in the toolbar (it is a row-header button). 8 locales updated.
cc
Home's recently-played albums/tracks only populated on a full home load, so
tracks played mid-session never appeared until restart. Re-read the LOCAL
store into the rails when a track is recorded while Home is visible, and on
Home re-mount if it went dirty — event-driven, no polling, cheap (small JSON
read + cached artwork). Also add a manual refresh button in each recently-
played row header next to the page chevrons (moved off the top toolbar, where
it crowded the discovery tab bar).
cc
Native std-widgets (LineEdit/TextEdit) render from Slint's own Palette, which
we never synced to the QBZ theme, so on a light theme their text collided with
its own background (Settings input fields, effectively invisible). Add
Theme.is-dark (computed in Rust from the surface luminance, so it is correct
for static/auto/custom themes) and drive Palette.color-scheme from it in the
AppWindow: light/dark follow the theme, System stays `unknown` (follows the
OS). Reactive on theme change.
cc
CircleAction defaults to hardcoded light-on-dark colors, so every instance
sitting on the normal page background (not over artwork) vanished on light
themes. Set on-surface: true on those clusters — Popular Tracks (artist +
label), the PlaylistView hero row, LocalAlbumView, AwardView,
PurchaseDetailView, MixtapeDetailView, FavoritesView, LocalLibraryView — so
they use theme-appropriate colors. Buttons over artwork/dark headers keep the
light-on-dark defaults.
cc