Commit Graph
39 Commits
Author SHA1 Message Date
vicrodh 76ea5a82bb feat(shell): app-wide dynamic album-art background ("modo Cider")
Opt-in album-art background composited behind the whole shell, wgpu-tier
only. Appearance mode picker: Off | Ambient (GPU shader) | Blurred art.

- ambient.wgsl: new calm, low-energy album-colored scene (mode 7), drifting
  mesh-gradient of the primary/secondary/accent triad, no beat/transient.
  Registered in shader_underlay SHADER_SOURCES[5] + render_frame idx math.
- state.slint: AppearanceState app-background {modes,mode-index,available,
  dim,surface-alpha}; ImmersiveState.app-shader-mode (derived 1->7);
  ShellState.app-background-active (mode>0 && wgpu && has-track).
- visualizer.rs: drain renders shader-mode when immersive OPEN, else
  app-shader-mode (7=ambient) into the same shader-texture sink.
- AppShell: background layer at root z-bottom (Ambient=shader texture,
  Blurred=ImmersiveAtmosphere) + tunable dark scrim; shell chrome surfaces
  go translucent when active; viz-should-run extended to drive the drain.
- Whole-shell translucency: Sidebar/HeaderBar/PlayerBar(Small)/QueueSidebar/
  LyricsSidebar/SidebarNowPlayingDock + AppShell content well/frame/column.
- ui_prefs: persisted `app_background` (off|ambient|blurred), String-key
  pattern; appearance-select("app-background") write-back.
- Live-tunable knobs QBZ_BG_DIM / QBZ_BG_SURFACE_ALPHA (seeded at startup)
  so the look is dialed in one smoke session without a rebuild.
- i18n: 8 locales for the picker + warning; reduce-motion / D4 (no-track ->
  opaque theme) handled via derived active flag.

Design + signed decisions: qbz-nix-docs/specs/2026-07-16-app-wide-dynamic-background.md

cc
2026-07-16 23:50:40 -06:00
vicrodh 134eddc1ae fix(share): resolve Song.link/Album.link via Deezer ISRC/UPC (#514)
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
2026-07-16 20:37:51 -06:00
vicrodh 8e57864bee fix(shell): #618 window identity/geometry + single-instance present; Plex refresh seed; wire discover/pinned state
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
2026-07-16 16:31:51 -06:00
vicrodh 897ba7128c feat(qbz-ui): settings export modal + Developer row (04 §4.2)
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
2026-07-15 07:50:27 -06:00
vicrodh 8c50c16faa fix(offline): clarify that Clear cache keeps downloaded albums
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
2026-07-13 21:01:48 -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 c3523722f4 feat(local-library): add a Plex sync button to refresh media without restarting
Fetching new Plex music required restarting QBZ. Add a sync button in the Local Library header (gated on plex-available, with a busy spinner) that triggers the existing Plex re-sync (new plex_auth::sync_now over the persisted selected libraries) and refreshes the view. Strings in all 8 locales. Resolves #573.

cc
2026-07-13 04:44:08 -06:00
vicrodh 85e11d284a feat(quality): downgrade arrow + true-quality tooltip on the Now Playing badge
Follow-up to #590: playback now downgrades to the streaming-quality setting but the badge still showed the catalog max with no indicator. The poll loop already received the delivered stream's sample-rate/bit-depth in PlaybackEvent but dropped them; publish them plus a quality-downgraded flag (effective < catalog max, DSD-exempt on depth) and the true effective detail string. AudioStamp shows an amber down-arrow glyph when downgraded and a Source/Output hover tooltip; mirrored to the miniplayer. Strings translated in all 8 locales.

cc
2026-07-13 01:49:18 -06:00
vicrodh 5d50158edb feat(qconnect): restore the device-name setting from Tauri
Tauri let the user rename how QBZ announces itself to Qobuz Connect;
the Slint port lost the UI entirely while keeping the persistence and
the announce path (same device_name key in qconnect_settings.db, read
by connect() -> bootstrap_remote_presence). Restored 1:1: a text row
in Settings > Playback next to the QConnect startup select (LineEdit
mirroring the ListenBrainz token row, placeholder = the resolved
default 'Qbz - {hostname}'), a new settings-string callback channel
through the settings chain, and a live-service setter so a rename
takes effect on the next connection without an app restart — exactly
Tauri's semantics (no live re-announce; the row description says so).
Trim-only validation like Tauri; empty clears the override. Two new
msgids across the 8 locales.

cc
2026-07-08 15:26:39 -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 860f49ca3e feat(favorites): album favorite action is a real toggle everywhere
Follow-through on 6a0c3d5e (state-visible hearts): the card-level
album 'favorite' arm was still add-only — a filled heart re-added and
toasted 'Added to favorites'. It now toggles, mirroring the header
arm: fav_cache-keyed, optimistic set_album_row_favorite + rollback on
error, cache written on success only, reco logging on ADD only (the
Capa B search-interaction record is now gated to adds, like tracks).
AlbumCard's menu item goes state-conditional (Remove from Library +
filled heart), and the now-playing '+' flyout album entry becomes
state-honest through a new NowPlayingState.album-favorite (seeded per
track in refresh_now_playing_meta, flipped live by
set_album_row_favorite — which now also syncs the open album page's
header heart, closing the stale-header undo loop). Favorites' own
intercepted unfavorite (fade + row removal) is untouched, both view
modes. One msgid x8 locales.

cc
2026-07-08 09:05:02 -06:00
vicrodh 00a44e123f feat(qconnect): startup behavior setting — remember state / on / off
Restores the Tauri-era Qobuz Connect startup setting, now in
Settings > Playback per owner request. Reuses the shared
qconnect-app::startup module (QconnectStartupMode +
compute_effective_startup, unit-tested) and Tauri's exact
qconnect_settings.db keys (startup_mode / last_known_state), so an
upgrading user's choice survives. last_known_state is written at the
manual toggle's success points only — internal teardowns (D5 offline
force-disconnect, bootstrap failure cleanup) never clobber the
remembered state, mirroring Tauri's command layer. Startup auto-connect
runs post-enter_shell (online sessions only, once per process) through
the same connect() path as the toggle, on Tauri's bounded retry
schedule, bailing silently under offline mode instead of toasting per
retry. Select row mirrors the quality-retries row; 5 msgids x8 locales.

cc
2026-07-07 17:37:30 -06:00
vicrodh 6a920f6f2a Merge feature/renderer-autodetect-hardening into pre-release
cc

# Conflicts:
#	crates/qbz-ui/translations/de/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/en/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/es/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/fr/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/ja/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/pt/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/ru/LC_MESSAGES/qbz-ui.po
#	crates/qbz/src/main.rs
2026-07-07 09:14:48 -06:00
vicrodh 7bf79378d2 Merge feature/guest-local-mode into pre-release
cc

# Conflicts:
#	crates/qbz-ui/translations/de/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/en/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/es/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/fr/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/ja/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/pt/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/ru/LC_MESSAGES/qbz-ui.po
2026-07-07 09:14:23 -06:00
vicrodh 7575b81623 Merge feature/log-uploader-visible into pre-release
cc

# Conflicts:
#	crates/qbz-ui/translations/de/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/es/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/fr/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/ja/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/pt/LC_MESSAGES/qbz-ui.po
#	crates/qbz-ui/translations/ru/LC_MESSAGES/qbz-ui.po
2026-07-07 09:14:09 -06:00
vicrodh 7d3fc2a3aa feat(renderer): autonomous degradation ladder + UI loop watchdog
Hardens the renderer auto-detect so users never need the manual
options (the anti-videogame-settings goal).

Ladder (phase 1): a failed start now walks one rung down instead of
jumping to GL — wgpu (default adapter) -> wgpu (opposite
PowerPreference) -> GL -> software. The alt rung exists because the
#542 family is an ADAPTER mixup, not a wgpu failure; its survival is
version-stamped at disarm time (renderer_wgpu_alt) so later starts arm
it directly. GL-rung failures continue to software through the
non-auto branch (the persisted 'gl' carries the 'auto-gl' sentinel
value; a user-picked gl arms the literal 'gl' — unambiguous).
Auto-degradations are version-keyed (renderer_auto_degraded): a new
build re-probes auto once, the ladder re-degrades within a start if
the stack is still broken; manual picks clear both markers.

Sentinel disarm reworked: was a fixed 5s timer that lost the race
against late startup crashes (#558's swapchain error loops forever);
now first real input / close request / clean exit, with a 30s
no-touch fallback. The 5s timer keeps only its DPR-persist duty.
User-picked 'software' no longer arms (it cannot fail to start).

Watchdog (phase 2, #555 diagnosis aid): background thread probes the
event loop's cross-thread dispatch latency every 2s (30s startup
grace, clamped against suspend, 10 consecutive >250ms probes flag
once). Detection only — never switches tiers; surfaces as the 'UI
Loop Latency' Diagnostics row + bundle line so field reports carry a
number. The #555 lag is renderer-independent, so this measures the
layer where it actually lives.

One msgid x7 (the degradation toast is now rung-generic).

cc
2026-07-07 08:00:31 -06:00
vicrodh a4dce34b28 feat(ui): login disclaimer line for the guest offline mode
One sentence under the subscription notice so the always-visible
'Start offline' link is self-explanatory: QBZ works as an offline
player without a Qobuz account, with no access to the Qobuz library.
One msgid x7 locales.

cc
2026-07-06 21:59:39 -06:00
vicrodh 763b5ae81f feat(ui): always-visible Share logs entry in the settings sub-nav
The log uploader was reachable only through Settings > Developer >
Open log viewer, so users filing issues never found it and reports
arrived without logs. A pinned entry at the bottom of the settings
sub-nav (visible from every section) now opens the same log viewer
(copy + upload). One new msgid x6 locales.

cc
2026-07-06 20:57:00 -06:00
vicrodh 41d93e5e5f fix(playback): auto-skip unavailable tracks instead of parking on them
Tauri regression: the Svelte playbackService caught TrackUnavailable play
errors and called autoSkipToNext (bounded at 5 consecutive skips, issue
Tauri#467); the Slint port only logged and cleared the spinner, so the
queue cursor parked on the dead track and playback stopped.

play_audible's error arm now detects TERMINAL unavailability (substring
contract over the flattened ApiError Display: TrackUnavailable /
NonStreamable / NoQualityAvailable — transient errors keep NOT skipping,
the client already retries those), toasts, honors stop-after-this-song
on the failed track, and advances through the real machinery
(advance_to_playable + after_track_change, async recursion boxed).
Superseded fetches (user already picked another track) never skip.
Counter resets when audio actually starts. One new msgid x6 locales.

cc
2026-07-06 19:55:49 -06:00
vicrodh 1a7d33d9e0 feat(ui): custom theme editor panel + controller + i18n
Wire the custom-theme editor end to end. In AppearanceSettings, when the
"Custom" theme is selected, show a "Start from current theme" button, a "Dark
theme" polarity toggle, and a grouped token grid (SURFACES / TEXT / ACCENT /
STATUS / OTHER) of 11 base tokens. Each token is a swatch that toggles an
inline ColorPicker; edits route through AppearanceState.custom-set-token
(drag) and custom-set-token-hex (hex commit).

The crate::custom_theme controller reads the base straight from the editor
swatch properties (the in-memory source of truth, no per-drag disk reads),
sets the touched field, re-derives via qbz_theme::theme_from_base, pushes the
palette live and persists. seed_from_current snapshots the live Theme global
into the base; toggle_dark flips polarity; set_token_hex parses in Rust (Slint
cannot). main.rs registers the four callbacks.

Also harden the ColorPicker reseed guard: skip decompose when the fed-back
value equals our own emitted color, so a drag ending at v=0/s=0 no longer
resets the hue. New strings added to all 7 gettext catalogs (es Mexican/
neutral); msgfmt --check clean; full .slint tree validated via the slintcheck
stub.

cc
2026-07-06 10:19:59 -06:00
vicrodh cd62853743 fix(library): tooltips on the Library header action buttons
The random-album / random-artist / random-playlist / random-label
buttons all exist (1:1 with 1.x) but rendered as bare shuffle icons —
1.x had title tooltips, so users couldn't tell them apart from a
tracks-shuffle and reported the feature as removed (#554). CircleAction
already supports the shared tooltip overlay; set it on all seven
Library header actions. 7 msgids translated in all 7 locales.

Fixes #554

cc
2026-07-06 08:05:57 -06:00
vicrodh 1bbc98cc8e feat(ui): auto-theme settings UI + i18n
Repoint the existing auto-theme Settings scaffold from the "System" theme to
the new dynamic "Auto (dynamic)" option:

- state.slint: add theme-is-auto (distinct from theme-is-system, which stays
  the native OS-palette follow).
- AppearanceSettings.slint: gate the Source dropdown, image picker, detected-DE
  hint and Regenerate button on theme-is-auto, and drop the placeholder
  `unsupported` flag now that the rows are functional.
- translations: add "Auto theme generation failed" to all 7 catalogs and give
  the es "Wallpaper Sync" string a proper Mexican-neutral translation.

cc
2026-07-05 22:34:09 -06:00
vicrodh cf9d28b9e1 fix(i18n): say "Library" wherever the copy still said favorites
The nav tab already reads Library, but the surrounding communication
kept the old Qobuz term: the Home/Discovery section "Favorite Albums",
the Add/Remove-favorites menu entries on every card/row surface, the
playlist context-menu "Favorite" verb, and the Favorites view's
empty/error states. Align all of it with the Library terminology
(9 msgids renamed, translated in all 7 locales). Routes, ids and the
genre-filter context string are untouched — this is copy only.

cc
2026-07-05 15:03:47 -06:00
vicrodh f83eadb398 fix(playback): surface stream errors as toasts, sandbox-aware for ALSA
The player has recorded a user-readable message on stream-open failure
since the Tauri era, but the drain lived in Tauri's polling loop and
was never ported to the Slint poll loop — record_stream_error had ZERO
consumers, so ALSA Direct/exclusive failures showed as a bar frozen at
0:00 with no explanation (#508, #534, #500). Drain the message each
tick and toast it. When running inside Flatpak/Snap and the failure is
ALSA-shaped, explain that direct hw: access is blocked by the sandbox
and point at PipeWire/System Default instead of showing the raw error.
2 new msgids translated in all 7 locales.

cc
2026-07-05 12:12:10 -06:00
vicrodh a273088205 fix(renderer): auto-revert sentinel now covers the auto-detected wgpu tier
The startup sentinel only guarded explicit Settings overrides: an
auto-detected wgpu start that crashed pre-paint re-detected wgpu on the
next launch — an unrecoverable crash loop (#542 hit exactly this). Arm
the sentinel when auto-detect picks wgpu; if it survives to the next
start, persist the GL (compatibility) renderer instead of re-detecting,
and explain with a toast. Linux-only by design (macOS has no GL tier).
Also add the .po entries for the existing revert toast, which was
missing from all catalogs, plus the new degrade toast (7 locales).

cc
2026-07-05 08:11:04 -06:00
vicrodh de5c50a5b3 feat(playlist): restore the "Date added" sort option
2.0 dropped the v1.x "added" sort. Restore it with the same semantics:
a positional proxy over the playlist's natural insertion order (the API
returns tracks oldest-first; no added-at timestamp is deserialized),
starting newest-first on first selection, re-click toggles direction.
New msgid translated in all 7 locales.

Fixes #546

cc
2026-07-05 08:04:07 -06:00
vicrodh e288e4aaee feat(slint): DSD playback mode setting, DSD badges and local gapless
Settings > Audio gains a 'DSD playback' dropdown (Convert to PCM /
DoP / Native DSD, ALSA-direct only, permanent warning copy since DoP
and native support cannot be detected). Local .dsf/.dff files route to
the player's DSD path with toasts on unsupported files. Quality badges
read DSD64/128/256 from the stored bit rate on every surface including
the now-playing stamp. The gapless prefetch now pre-queues LOCAL
tracks too: DSD via the DoP/native append or an in-memory converted
WAV, other local formats via their raw bytes (fixes local FLAC
gapless); CUE virtual tracks are excluded. Six new locale strings.

cc
2026-07-03 17:56:48 -06:00
vicrodh a7f538b3de feat(slint): custom title bar and compact header navigation option
Custom title bar (Linux opt-in, macOS overlay default): frameless window
with drawn window controls (close/maximize/minimize, left or right
placement), header drag with once-per-press latch (Wayland move-grab
swallows the release; re-arming on stale pressed state re-grabbed the
window onto the cursor), double-click maximize, edge resize, 1px
hairline. Title bar minimize is always a WM minimize now - the old
minimize-to-tray branch made it visually identical to close-to-tray.

Compact header navigation (opt-in, Appearance): with nav out of the
sidebar, the header can always use the icon-only compact nav even while
the sidebar is open. Full text tabs and compact nav stay mutually
exclusive; the playlists flyout button keeps its closed-sidebar-only
behavior. Persisted as ui_prefs.nav_header_compact.

i18n: new strings in all six locales (includes two DSD toast strings
for the in-progress DSD support, riding along in the catalogs).

cc
2026-07-03 15:52:30 -06:00
vicrodh c504059c08 i18n: sync all locale catalogs against a fresh extractor POT
Zero missing msgids in all six locales (was 27 es / 36 de-fr-pt / 50
ru-ja): renderer tiers, shortcut modals, link resolver, Purchases, the
album-blacklist batch (de/fr/pt/ru/ja) and the recommendations + JACK
batch (ru/ja); plus es Collections leak fixes. msgfmt-validated.

cc
2026-07-03 10:01:05 -06:00
vicrodh 8a734f6427 feat(login): narrate the browser OAuth flow on the login screen
The screen sat inert while the system-browser OAuth ran: no waiting or
authenticating feedback, and failures were log-only. New LoginState
global (phase + error) driven by an on_phase callback in
auth::login_via_system_browser (both call sites: login + recovery).
The blue button now reads "Sign in with your browser", disables while
an attempt is in flight, and the screen shows continue-in-browser text
(with a no-focus hint), a Cancel link that aborts the pending flow, an
authenticating state, and a sign-in-failed box. The redundant
"Use your system browser instead" link and its AppCommand are removed.
Five new msgids translated in all seven locale catalogs.

cc
2026-07-02 10:10:59 -06:00
vicrodh b7549790a3 feat: interface-size (UI scale) presets
Webview-zoom-style presets: Extra small 0.8 / Small 0.9 / Default 1.0 /
Large 1.2 / Extra large 1.5, applied through SLINT_SCALE_FACTOR set at
the top of main() before any thread or the backend exists. The env var
overrides the compositor DPR, so the value is last observed DPR x
preset; the Default preset leaves the var unset (stock DPR handling,
including live monitor changes, stays intact). last_dpr is persisted
from the winit window (real compositor DPR) on the 5s renderer-sentinel
timer.

- const-bake guard: strip SLINT_SCALE_FACTOR in qbz-ui build.rs so the
  slint compiler never bakes the factor into the generated code
- new UiScale global + scale-divided window minimums keep the physical
  minimum constant across presets (XL fits a 1366x768 screen); the
  three window-geometry gates in main.rs use the process-active factor
- artwork decode targets scale with the preset: sharp covers at >=1.2,
  smaller decoded-cache footprint below 1.0
- Settings > Appearance "Interface size" row below Language (replaces
  the commented-out percentage placeholder and its orphan properties),
  restart-to-apply toast
- Typography.boost = 1.1 on Extra small only, so body text stays
  readable at the 0.8 factor; Small (0.9) needs no compensation
- i18n: new strings added to all 7 locale catalogs

Known accepted limitation: slight text softness at fractional scale
factors (owner-accepted; diagnostic notes live in
qbz-nix-docs/ui-scale-presets/).

cc
2026-07-02 08:36:40 -06:00
vicrodh 7002e7773e fix(about): credit Rust, drop Kawarp, crisp logo, tighter disclaimer
- Acknowledgments now lead with Rust (the app's core, previously unmentioned)
  and drop Kawarp, which is no longer used (es/de/fr/pt updated).
- Logo renders from a 128px source instead of the 512px original scaled to
  56px, which aliased badly (femtovg has no mipmaps).
- Qobuz trademark notice dropped to 11px so it wraps to two lines, not three.

cc
2026-06-30 14:51:00 -06:00
vicrodh a67dd2c2df i18n: translate Recommendations/external-reco strings + JACK note (es/de/fr/pt)
The 11 Recommendations msgids (IntegrationsSettings RECOMMENDATIONS/toggle/
description, HomeView tab, ExternalRecoView row titles + empty state) had
zero catalog entries and fell back to English everywhere. Translate them in
es/de/fr/pt, plus the 2 new JACK-note strings. es is Mexican (register-
checked: no voseo/Castilian). ru/ja stay English-fallback pending a reviewed
pass.

cc
2026-06-30 09:27:07 -06:00
vicrodh be487dca4c feat(slint): work grouping, ru/ja locales, About + What's New + Discover reco modal
- Album view: classical "work" section grouping (PR #536 port) — new
  Track.work model field + run-length per-work headers in AlbumView.
- i18n: add Russian + Japanese locales (1365 msgids each); extend plural.rs
  (ru 3-form one/few/many, ja single-form) + tests; fix the language-selector
  reseed (main.rs set_languages + ui_prefs index maps). Seven languages total.
- About modal: native port of the Tauri About (version, links, build info,
  one-paragraph acknowledgments, inline contributors with GitHub avatars,
  signature). Contributors credit hoyon (PR #536) and mxnix (PR #517, ru).
- What's New modal: renders the GitHub release-tag description (markdown
  sections + TOC); release fetch + build.rs emits build date/commit.
- Discover Recommendations: move Cache window + Refresh now into the Customize
  modal, gated to the reco tab; hide the section-config UI there; add an
  explainer paragraph. New strings translated to all six non-English locales.
- About modal width +120px; splash screen gains the legal disclaimer footer.

cc
2026-06-29 22:55:17 -06:00
vicrodh 99da4c381b feat(slint): pre-launch dev panel, queue controls, appearance persistence, reco cache settings
Sleep timer (queue footer): tokio monotonic deadline + 1Hz countdown that
fires core().pause() at expiry; presets + custom minutes via a popover.

Stop after this song: 4 QbzCore passthroughs + playback consume-before-advance
seam and the load-bearing gapless-prefetch gate, plus the queue marker/menu so
the Queue persists when playback halts.

qbz-log crate: TeeLogger wrapping env_logger (stderr + 5000-line ring +
~/.local/share/qbz/logs/qbz.log) with write-time secret redaction (regex +
live-secret registry) and a diagnostics bundle builder.

Dev panel logs: in-app viewer that shows content the moment it opens, advanced
controls behind a discreet [+], "Copy all" as the sole footer button, a copy
button on the upload URL, and a redacted public paste upload.

Diagnostics: qbz-app::diagnostics (extracted, src-tauri untouched) feeding a
7-section panel with the live active output device + sample rate/format (pactl)
and a complete redacted report for the upload.

Report an Issue: wires the header menu item to a 1:1 Tauri modal that opens the
GitHub bug-report template.

Appearance persistence: 6 toggles that were live-only now persist via ui_prefs
+ handler arm + startup seed (window-title-show, show-volume-steppers,
sidebar-playlist-collage, local-library-track-artwork, in-app-toasts,
theme-filter).

Recommendations cache (per-tab only): TTL select 24/36/48/72h + Refresh now in
ExternalRecoView; no change to the other Discover tabs.

Bump workspace version 0.1.0 -> 1.2.15 so app_version is correct in the
diagnostics report and the QConnect About string. i18n es/de/fr/pt for all new
strings.

cc
2026-06-29 13:19:06 -06:00
vicrodh 98d606f913 fix(playlist): follow/unfollow button reflects the true follow state
The detail toggle used sidebar::contains to decide "following", which is
true for owned playlists shown in the sidebar and false for followed ones
opened from Favorites > Followed (a followed playlist may live in a folder
or not be rendered in the sidebar at all). Being in the sidebar is a
consequence of following, not the determinant, so derive it authoritatively
from get_user_playlists (present but owner != me). Reflect the state in the
button: a check icon + "Unfollow on Qobuz" tooltip when following.

cc
2026-06-28 12:59:06 -06:00
vicrodh c1b189363a i18n: translate the slint-polish batch UI strings
Add the 10 new @tr labels (radio menu items, playlist Follow/Copy,
MusicBrainz setting) plus the new toasts to all five locale catalogs
(en/es/de/fr/pt); es is canonical Mexican, de/fr/pt machine-drafted and
flagged for review. Convert the new user-facing toasts in
enqueue_track_ids + the playlist-copy arm from raw strings to
qbz_i18n::t / qbz_i18n::tf so they localize like the rest.

cc
2026-06-27 15:34:22 -06:00
vicrodh 2e1d8e431d feat(reco): playlist Suggested Songs UI (T8-T10)
The Slint UI for the artist_vectors suggestions, 1:1 with the Svelte
PlaylistSuggestions component (backend T1-T7 already landed):
- T8: PlaylistSuggestionsState/Actions globals (distinct from the immersive
  SuggestionsState) + a controller (crates/qbz/src/playlist_suggestions.rs)
  that holds the pool/pagination/dedup and calls core.generate_playlist_suggestions
  on a spawned task; wired into main.rs; per-row cover artwork target; suggestions
  reset on playlist navigation.
- T9: the Suggested Songs section in PlaylistView.slint (docked after the
  virtualized track list), manual activation via a header wand-sparkles button,
  per-row Add/Play/info/dismiss + loading/empty states; a Close (x) button hides
  the panel (activated is in-out).
- T10: per-playlist dismissed-track store (playlist_suggestions_dismiss.rs),
  filtered out of the pool.
- i18n keys added to es/de/fr/pt.

Owner-built and verified working.

cc
2026-06-27 01:57:30 -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