Commit Graph
8 Commits
Author SHA1 Message Date
vicrodh 8c2f84b798 feat(shell): per-GPU selector, ambient scene in immersive, glass carousels/viewall
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
2026-07-17 10:54:45 -06:00
vicrodh a0f5cf2bf5 feat(shell): metaball ambient + translucent bars/panels/controls, drop carousel fades
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
2026-07-17 08:08:33 -06:00
vicrodh 41f8578b78 feat(home): sort control for the Library Albums rail
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
2026-07-13 21:01:48 -06:00
vicrodh a174e06119 feat(home): auto-refresh recently-played rails + a row refresh button
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
2026-07-13 13:11:43 -06:00
vicrodh 671e2c4cb7 fix(ui): kill the windowing recursion class — snapshot-sampled bands + crash-chain watchdog
Third and final hit of the 'Recursion detected' panic class
(i-slint-core properties.rs:583), this time crashing DiscoverBrowse on
startup view-restore (and crash-looping via persistence). Root cause
symbolized from the field backtrace: every .slint 'changed x =>'
compiles to a ChangeTracker whose watched expression evaluates
SYNCHRONOUSLY at component instantiation, and conditional/repeated
components instantiate from ensure_updated INSIDE whatever property
evaluation triggered them — so a windowing band that reads layout
results (viewport-y, flick.height, self.width, absolute-position) can
re-enter a property mid-evaluation. Timing-sensitive: needs the model
filling in the same pass that instantiates the grid, which startup
view-restore hits deterministically.

Class-kill, uniform on every windowed surface (owner mandate — no
survivor exceptions): band state is now plain properties written ONLY
by an 80ms sampler Timer (event-loop context, nothing on the property
stack) in the shared AlbumGrid, the horizontal Carousel and the search
grid. No changed handlers, no init-time reads; the ±1-viewport margin
absorbs the sampling cadence; consumers' declarative wiring unchanged
(inputs are polled, never observed — contract documented in-file).

Startup crash-chain watchdog ('perro policía'): a counter file armed
before risky init and cleared by the same liveness signals as the
renderer sentinel. One pre-liveness death -> next boot resets exactly
ui_prefs last_nav/last_view (the field-verified surgical bypass) with
a recovery toast; two -> additionally bypasses the session-persist
queue restore for that boot, and the exit save keeps the preserved
snapshot instead of clobbering it with the empty queue. Recovers this
incident and the #558 loop automatically. One msgid x8.

cc
2026-07-08 13:43:50 -06:00
vicrodh 19ac438097 perf(ui): horizontal element windowing in the shared Carousel
Column band from viewport-x (visible plus one viewport of margin each
side) with the same contract hysteresis as AlbumGrid; off-band cards
are same-footprint placeholder tiles. Element-count only, always on —
section loaders dispatch artwork for the whole strip up front, so a
card mounting later picks its cover from the model instantly. Covers
every Home/ForYou/Discover/Search strip through one component.

cc
2026-07-06 19:55:07 -06:00
vicrodh 05df99b713 fix(reco): full-albums-only + slop filter + taller Recommendation rows
Owner-directed polish after testing the Recommendations tab.

- Full albums only: drop singles/EPs/boxsets/compilations from every album row
  (Recommended Albums, Fresh Releases, Deep cuts). Qobuz's `release_type` is the
  source of truth ("album" | "single" | "boxset" | "compilation"); when absent,
  fall back to tracks_count >= 5. Kills the karaoke single that showed up under
  Fresh Releases.
- Slop second layer: reject karaoke / tribute / "made famous by" / "in the style
  of" / instrumental-version releases by artist-or-title keyword, applied both
  pre-resolution (candidates) and post-resolution (the Qobuz album), so AI-slop
  that still wears a full-album shape gets filtered.
- Taller rows in this view only: AlbumCard/ArtistCard/Carousel/ArtistCarousel
  grow an overridable `card-height` (defaults unchanged, so Home/Search are
  untouched); ExternalRecoView passes 266 to album rows and 256 to artist rows,
  whose cards carry the "Similar to…" subtitle (~250px) and were clipping the
  Follow chip at the old fixed 220.

cargo check -p qbz green.

cc
2026-06-27 11:20:06 -06:00
vicrodh c8ef2a1b1d refactor(slint): split qbz-slint into qbz-ui lib + qbz bin; reclaim qbz binary
Slint cannot split the .slint compilation across crates, but a Slint LIBRARY
crate exposes its generated Rust types to dependent crates (verified with a
minimal proof). So:

- NEW crate `qbz-ui` (lib): owns the whole .slint tree + build.rs + bundled
  translations; `slint::include_modules!()` + the generated types (AppWindow,
  MiniPlayerWindow, all globals/structs) are re-exported. Translation domain is
  now `qbz-ui` (the .po files renamed accordingly).
- `qbz-slint` (bin) -> renamed to `qbz` (package + `[[bin]] name = "qbz"`),
  reclaiming the binary name from the retired Tauri app. It depends on `qbz-ui`
  and does `pub use qbz_ui::*;` so existing `crate::AppWindow` / `crate::*State`
  references resolve unchanged.

This splits the single ~20 GB rustc into two crates (UI/codegen vs the 75k LOC
of logic) — the foundation for lower build peak, incremental caching, and the
upcoming per-feature crate extraction.

Also fixed for the move: qbz-i18n bundled-.po include paths, the font/WGSL
`include_bytes!`/`include_str!` paths (-> ../../qbz-ui/ui/), workspace members,
dev scripts (`-p qbz`), and the CI workflows. `cargo check -p qbz` is green.

cc
2026-06-25 17:51:09 -06:00