Commit Graph
5325 Commits
Author SHA1 Message Date
vicrodh bc44fe5e0c feat(purchases): add pagination + per-type totals service + controller load shells
Slice 3/13. New qbz-offline-cache/src/purchases_service.rs (pagination glue,
get_purchase_total two-call per-type-totals primitive, byte-for-byte
filter_purchase_response). New qbz-slint/src/purchases.rs controller load shells
(get_downloaded_track_ids -> HashSet, load_purchases_metadata with TWO separate
ids-totals calls, load_purchases_by_tab with total backfill + error mapping).
Replicates Tauri's 'Failed to fetch ... purchases' wrapper so network errors map
to purchases.loadFailed 1:1 (TDD-caught). 10 unit tests green. Lazy/cache/force
gating deferred to UI slices 8/9 by design.

cc
2026-06-24 04:16:14 -06:00
vicrodh 26b6480ff1 feat(purchases): add purchase HTTP + getFileUrl + CDN fetch
Slice 2/13. New impl QobuzClient block in qbz-qobuz/src/purchases.rs ported
1:1 from src-tauri api/client.rs: typed/ids purchase listing (page_limit 500,
two-call per-type totals quirk preserved), get_track_file_url_by_format
(RPC-signed, intent=stream, empty-url -> TrackUnavailable), and download_audio
(http1_only, NO total timeout). Adds 2 endpoint consts; bumps
http()/secret()/authenticated_headers() to pub(crate) for the sibling module.
Offline-gated via self.http()?. Drops .use_native_tls() (crates/ workspace is
rustls-only; matches cmaf.rs build_cdn_client) to keep the standalone build green.

cc
2026-06-24 02:08:08 -06:00
vicrodh aceaf9b039 feat(purchases): add purchase wire models + lenient deserializers
Slice 1/13 of the Purchases Tauri->Slint port. Adds PurchaseResponse,
PurchaseIdsResponse, PurchaseAlbum, PurchaseTrack (no version field) and
PurchaseFormatOption to qbz-models, plus the 5 lenient deserializers ported
verbatim from src-tauri api/models.rs. Derives Default on SearchResultsPage
for the empty-page fallback. 4 round-trip tests, cargo test green.

cc
2026-06-24 01:48:34 -06:00
vicrodh 502b0dae2b i18n: apply bundled translation after window init + reseed option arrays live
select_bundled_translation must run after AppWindow::new() (the component's
global translation context does not exist before then), so startup now paints
in the persisted language instead of falling back to English. Adds
reseed_i18n_labels() which seeds the 14 AppearanceState option arrays from
qbz_i18n::t() — these were @tr() property defaults, which are not reactive to
translation changes — called at startup and on live language switch so the
settings <select> options translate and update without restart.

cc
2026-06-24 00:04:32 -06:00
vicrodh 24da9f1b74 i18n: translate full catalog to es/de/fr/pt (100% coverage)
Fills all previously-untranslated msgstr (UI strings absent from the Tauri
JSON seed) via a translation pass, then hand-fills the remainder. Neutral
Latin-American Spanish; standard professional register for de/fr/pt; brand/
technical tokens (FLAC, kHz, Plex, etc.) kept literal; {} placeholders and
gettext plural forms preserved. All four catalogs pass msgfmt -c.

cc
2026-06-23 21:45:05 -06:00
vicrodh 25f9a15f90 i18n: register dynamic msgid literals via mark() and fix double-translation
Adds qbz_i18n::mark() (identity, extraction-only) and applies it at the
definition sites of literals later translated through t(variable):
release-type classifier, discover section labels, artist release-section
titles + month names, label jump tabs, and audio settings dropdown tables.
Fixes the discover_prefs double-t() (label_for now returns the marked
literal, translated once at the consumer).

cc
2026-06-23 21:31:42 -06:00
vicrodh 294f01d8bb i18n: re-extract fresh Slint pot (capture option arrays + Task-10 strings)
cc
2026-06-23 21:25:24 -06:00
vicrodh 630663fc4a i18n: extract Rust msgids and re-seed catalogs
cc
2026-06-23 15:15:04 -06:00
vicrodh e167936cc1 i18n(rust): misc strings
cc
2026-06-23 15:11:43 -06:00
vicrodh ed6bda2108 i18n(rust): integrations + dac wizard strings
cc
2026-06-23 15:11:38 -06:00
vicrodh 150ca7f2fc i18n(rust): playback/offline strings
cc
2026-06-23 14:55:18 -06:00
vicrodh f26e1a6626 i18n(rust): artist/album/label strings
Wrap user-facing Rust-built strings in qbz_i18n helpers across the
Artist/Album/Label surfaces: artist release-section titles + jump-tab
labels (routing kept on the English keys, only displayed labels
translated), MusicBrainz month-name/date formatting, album track-count
subtitle + "From the same artist"/"Listening suggestions" carousels,
label carousels + jump tabs + group-by "Unknown", and the shared
album_map release-type labels (Album/EP/Single/Live/Compilation).

cc
2026-06-23 14:47:57 -06:00
vicrodh b2facfbd24 i18n(rust): discover/home/search strings
Wrap user-facing Rust-built strings in qbz_i18n helpers across the
Discover/Home/Search surfaces: discover section titles, configurator
labels (discover_prefs::label_for), For You carousel titles, mix
subtitles + duration, suggestions track-count + Song Radio, search
section headers + count subtitles, and the Lossless quality tooltip.
Reuses shared msgids ({} track/{} tracks, {} album/{} albums, section
names) so the catalog stays deduped.

cc
2026-06-23 14:47:50 -06:00
vicrodh 66aa469aa5 i18n(rust): my-qbz strings
Wrap user-facing eyebrow labels (MIXTAPE/COLLECTION/ARTIST), album/item
counts, add/edit/builder/cover toasts, dialog titles + image filters,
play/queue toasts, and the default discography name in the My QBZ
controllers with qbz_i18n t/t_args/tf helpers. Keep the My QBZ brand
itself untranslated via positional interpolation.

cc
2026-06-23 14:32:38 -06:00
vicrodh f14c513099 i18n(rust): local library strings
Wrap user-facing toasts, dialog titles/descriptions, durations,
track/folder counts, scan/cleanup status, and grouping labels in the
Local Library controllers (local_library, local_library_settings,
local_playlist, tag_editor) with qbz_i18n t/t_args/tf helpers.

cc
2026-06-23 14:32:31 -06:00
vicrodh 937b80c4c7 i18n: translate Rust-seeded 'No folder' option
The folder-options dropdown is re-seeded from Rust at runtime in both
create-playlist builder sites, so the state.slint default is dead. Wrap
the seed-site 'No folder' literal in qbz_i18n::t so it resolves through
the shared gettext catalog.

cc
2026-06-23 14:13:48 -06:00
vicrodh 171b175b4f i18n: wrap hardcoded Slint labels and plural concatenations
Wrap remaining user-facing literals in @tr across discover (RADIO,
TOP TRACKS), DiscographyBuilder (release-type/order-by labels), the
OfflineManager hero, ExpandableSearch placeholder default, and the
appearance option arrays in state.slint (displayed as-is, no Rust
re-seed). Replace string concatenation with positional @tr plus
ngettext plurals for the DiscographyBuilder footer and OfflineManager
track counts so word order is locale-stable. Language endonyms and the
macOS/Adwaita/quality-tier brand names stay literal.

cc
2026-06-23 14:13:40 -06:00
vicrodh 8243f2a856 i18n: language selector switches translations live + persists
Wire the Appearance language selector (appearance-select "language"): map the
dropdown index to a language key via the verified order (0 Auto, 1 English,
2 Espanol, 3 Francais, 4 Deutsch, 5 Portugues), persist the raw user choice,
resolve "auto", then set_language() + select_bundled_translation() to switch
all @tr bindings live with no restart. Seed the selector index from the
persisted key at startup.

cc
2026-06-23 14:02:04 -06:00
vicrodh 69d85f1932 i18n: init language at startup + persist + locale-aware dates
Add a persisted `language` UiPrefs field (default "auto") with index<->key
helpers matching the existing appearance-prefs pattern. At startup, before
the AppWindow is created, resolve "auto" via qbz_i18n::resolve_auto(), call
qbz_i18n::set_language() and slint::select_bundled_translation() so the first
paint uses the right translations. Map dates::current_locale() from the active
runtime language so month tokens localize with the rest of the UI.

cc
2026-06-23 14:01:57 -06:00
vicrodh 86b22d7325 i18n: extract Slint msgids and seed es/de/fr/pt from Tauri JSON
cc
2026-06-23 13:52:34 -06:00
vicrodh 7142435211 i18n: honor LC_ALL precedence in resolve_auto
POSIX locale precedence is LC_ALL > LC_MESSAGES > LANG. resolve_auto
previously only checked LC_MESSAGES and LANG; add LC_ALL as the
highest-priority env var, skipping it when empty. Extend the
resolve_auto test (env-mutation serialized via the existing Mutex).

cc
2026-06-23 13:49:10 -06:00
vicrodh 57244ac0c1 feat(qbz-i18n): gettext .po catalog, plural rules, and t/tn/t_args/tf API
Implement the frontend-agnostic translation layer test-first:

- plural.rs: PluralRule parsing nplurals + the two expressions our
  locales use ((n != 1), (n > 1)); index(n) selects the form, with a
  safe English default for anything unrecognized.
- po.rs: Catalog::parse handles the header entry, msgid/msgid_plural/
  msgstr/msgstr[N], multi-line continuations, and \n \t \" \\
  escapes; empty msgstr means no translation (get returns None so
  callers fall back to English). Reads Plural-Forms from the header.
- lib.rs: 5 catalogs embedded via include_str! and lazily parsed
  (OnceLock); AtomicU8 language state; set_language/current_language/
  resolve_auto (LC_MESSAGES then LANG -> 2-letter prefix); t/tn/t_args/
  tf with left-to-right {} substitution and English fallback.

17 unit tests, all green via cargo test -p qbz-i18n. Content tests use
inline fixtures since the bundled .po files are header-only for now.

cc
2026-06-23 13:45:58 -06:00
vicrodh f92c95ee21 build(qbz-i18n): scaffold pure-Rust i18n crate
Add new no-dependency qbz-i18n crate to the workspace members.
Frontend-agnostic translation catalog per ADR-006 (reusable by
Slint/TUI/headless; no slint or tauri deps).

cc
2026-06-23 13:45:29 -06:00
vicrodh 28d7ada650 i18n: bundle Slint translations via compile_with_config (no gettext C dep)
cc
2026-06-23 13:41:46 -06:00
vicrodh 404526f118 i18n: scaffold empty po catalogs (en/es/de/fr/pt)
cc
2026-06-23 13:41:42 -06:00
vicrodh dd32f573b4 feat(slint): persist session across restarts (startup page, window geometry, playback)
Three restart-persistence features built and tested together (they share
ui_prefs + the enter_shell restore sequence, so kept as one change):

- Startup page: persist + restore the last SAFE top-level view via the
  canonical apply_entry dispatcher (loads data, not a blank view). Wired to
  Settings > Appearance > "Where you left off"; ui_prefs startup_page/last_view.
- Window geometry: persist logical size + outer position and restore them at
  launch, clamped to the current monitor so a smaller/disconnected display can
  never open an oversized or off-screen window (winit Resized/Moved hook).
- Playback session: new session_persist module wiring qbz-app's SessionStore.
  Saves queue + current track + volume + shuffle/repeat + position on
  track-change / pause / ~5s tick / exit (incl. hide-to-tray and tray Quit);
  restores the queue PAUSED at launch. Position resume on first play threads
  the player's existing start_position_secs (no protected-audio change beyond
  passing that param). Gated on the persist_session / resume_playback_position
  playback prefs; opened per-user on login + boot-restore + offline entry.

Known issue: the seek bar/timer do not visibly show the resume position until
play is pressed (the audio resume itself works); marked in code for follow-up.

cc
2026-06-23 12:09:34 -06:00
vicrodh 181b85c359 feat(slint): My QBZ rename-only with the fixed monochrome icon
Drop the custom-icon picker (owner decision): My QBZ now always uses
QBZ's monochrome (tray) glyph — in the Settings preview, the sidebar row,
and the header section nav (incl. the icons-only compact nav when the
sidebar is fully closed). Only the name stays editable; the reset control
is a square glyph button that clears the name to the default. The
icon-change backend (pick-icon / custom-icon) stays but is unsurfaced.

cc
2026-06-22 23:57:23 -06:00
vicrodh 2c9104e1e3 feat(slint): persist sidebar state, volume and nav placement
Restore across restarts: the three-state sidebar (open/mini/closed) and
the section-nav placement (sidebar vs header) seed from ui_prefs before
the shell renders and persist via new ShellState callbacks; the player
volume persists on slider release only (a new QbzSlider `released`
callback + NowPlayingState.persist-volume — no ui_prefs write per drag
tick) and is reapplied to the player on session entry.

cc
2026-06-22 23:57:08 -06:00
vicrodh be8c90bb80 feat(slint): disable the Track info trigger for non-Qobuz tracks
Track info is a Qobuz-only metadata page, but it fired for local / Plex /
ephemeral playing tracks too. Add NowPlayingState.source (populated from
the QueueTrack: None coerces to "qobuz" for catalog tracks, "local" for
local files) and gate the trigger on source == "qobuz" — in the SongCard
TouchAreas (pointer cursor + click both no-op otherwise) and the PlayerBar
menu handlers.

cc
2026-06-22 23:55:38 -06:00
vicrodh 8bc7026a01 fix(slint): right-align the Output device controls
The refresh button + device select sat in a center-aligned row, so they
floated with a right-hand gap once the window widened out of responsive
mode instead of landing flush on the same right edge as the single-control
rows. Switch the row to alignment: end.

cc
2026-06-22 23:55:24 -06:00
vicrodh fbbd54679c chore(slint): remove the Match system window chrome appearance setting
Drop the unported "Match system window chrome" row from Settings >
Appearance entirely (UI only; the AppearanceState flag + handler stay
inert for now).

cc
2026-06-22 22:56:05 -06:00
vicrodh ef38e7a04e feat(slint): port Discord Rich Presence (opt-in) from Tauri
Frontend-agnostic core in qbz-integrations::discord (DiscordRpc +
NowListening) — the Tauri backend minus the tauri::command/State
wrappers: lazy IPC connect, Listening activity (details/state/assets +
"Get QBZ" button + timestamps), and the opt-in Flatpak socket bridge.
qbz-slint discord_rpc controller: process-global client, init AFTER the
session is active (PR #477 persistence fix — both online and offline
entry, never at early boot), push on the track-change and play/pause
edges, clear on logout. Opt-in toggle in Settings > Integrations
(DiscordState, persisted in ui_prefs.discord_rpc_enabled) + a Flatpak
socket-access note. The opt-in is per-machine (ui_prefs) rather than
Tauri's per-user.

cc
2026-06-22 22:56:05 -06:00
vicrodh e7c969549c feat(slint): wire System Notifications + Mini player default view, drop obsolete Appearance rows
System Notifications now persists (ui_prefs.system_notifications) and
gates the desktop notification via a poll-thread atomic, seeded at
startup and flipped live by the toggle (scrobbling stays independent).
Mini player default view persists to mini_default_view and seeds its
select index at startup; the miniplayer already reads the key on open.
Both lose the red "unsupported" flag. Also removed 13 unsupported
Appearance rows that will not be ported: Font, the title-bar navigation
group (Search/Navigation in title bar + per-section toggles) and
navigation buttons position, window controls style/size/color preset,
and a duplicate immersive default view.

cc
2026-06-22 21:36:13 -06:00
vicrodh 907054711f fix(slint): install the rustls CryptoProvider at startup for QConnect
The binary compiles both rustls providers (aws-lc-rs via rust_cast, ring
via reqwest), so rustls cannot auto-select one and panics on the first
TLS connection. It was only installed lazily on the cast paths, so
joining a Qobuz Connect session without ever casting panicked the
WebSocket worker thread. Install it once in main() before any TLS;
idempotent, so the cast paths' calls stay no-ops.

cc
2026-06-22 21:35:56 -06:00
vicrodh ca46e3c478 feat(slint): offline playlists in folders + sidebar cover collage
Local playlists were excluded from both sidebar folders and the cover
collage. Folders: new local_playlists.folder_id column (+ migration) on
the shared playlist_folders, move_to_folder/clear_folder helpers, the
move-to-folder context menu enabled for local rows, optimistic cache
move, and grouping under folders in the rebuild + collapsed-folder
flyout. Collage: resolve up to four covers from a local playlist's own
tracks with no network (local file artwork, Plex thumbs, cached-Qobuz
covers), populated in the async load and routed through the local/Plex
art loader (file/Plex paths would miss the HTTP loader).

cc
2026-06-22 21:35:45 -06:00
vicrodh 8401258326 fix(slint): reflect now-playing and shuffle on the queue sidebar
The sidebar Queue reads the same core pointer as the now-playing bar,
but several paths refreshed the bar without the sidebar, so the panel
showed a stale NOW PLAYING track and the NPB shuffle button looked dead
(the reordered up-next was never re-pushed). Pair refresh_sidebar with
the now-playing refresh in toggle_shuffle, the local-tracks inline
shuffle, the gapless seamless-change (regardless of moved), and the Plex
quality patch. The shuffle engine itself was already correct.

cc
2026-06-22 21:35:20 -06:00
vicrodh 88f1967d8a fix(slint): single click on button primitives (FocusScope stole the first press)
Slint hit-tests sibling children in reverse declaration order, so the
keyboard FocusScope declared after the pointer TouchArea was visited
first and, with the default focus-on-click, consumed the first Pressed
just to grab focus — aborting traversal before the TouchArea, so every
button needed two clicks. Set focus-on-click: false on the FocusScope in
QbzPrimaryButton, SecondaryButton, QbzCheckbox, QbzToggle, ToggleButton
and QbzSelect; the press now reaches the TouchArea on the first click
(Tab/Enter/Space activation unaffected).

cc
2026-06-22 21:34:59 -06:00
vicrodh 21942c60aa fix(slint): show album art for every row in the immersive Queue
The immersive Queue up-next reuses the coverflow model, whose art was
windowed to ±4 around the current track, so only now-playing + the next
four rows decoded a cover. Build art jobs for the whole list and widen
the decode window when the immersive Queue panel is open (still lazy per
row); the panel fires QueueState.panel-opened on mount so the covers
repaint when it opens.

cc
2026-06-22 21:34:59 -06:00
vicrodh a515eb5529 fix(slint): wrap long credit lists in Album Info + Track Info modals
Album Info expanded-track credits: the roles line now stretches and word-wraps
instead of running off the right edge of the modal.

Track Info credits: render two independent fixed-width columns (even-index ->
left, odd-index -> right) so long name lists wrap and the right column stays
aligned on every row, with no height-coupled row gaps; names stay clickable,
one per line. Mirror the wrap in the immersive Track Info panel.

cc
2026-06-22 11:01:19 -06:00
vicrodh 091d023529 polish(slint): album sidebar external-link brand icons + wider column
Widen the album label/awards/external-links sidebar 160->200px. Replace the
generic external-link chips with inline brand SVG icons (Last.fm / Discogs /
MusicBrainz) rendered in their native colors (MusicBrainz split half purple /
half orange), no visible label + a hover tooltip. Uppercase the "EXTERNAL
LINKS" heading to match LABEL / AWARDS.

cc
2026-06-22 11:01:11 -06:00
vicrodh 74880009f6 feat(slint): AlbumView polish — booklet reader, carousels, external links
- Booklet: new frontend-agnostic qbz-booklet crate (MuPDF render to PNG;
  the Tauri Cargo stanza + device_rgb/alpha=false render workaround ported
  verbatim). In-app reader modal (paginate/zoom/rotate/download) driven by
  BookletState/BookletActions; header booklet button now opens it.
- "From the same artist" carousel (/artist/getReleasesGrid) and "Listening
  suggestions" carousel (/album/suggest), reusing the Discover Carousel,
  appended below the track list with their own artwork pipeline targets.
- External Links sidebar block (Last.fm / Discogs / MusicBrainz) built from
  artist + title, opening in the system browser.

cc
2026-06-22 01:27:12 -06:00
vicrodh 391cbb86b9 feat(slint): ArtistView overhaul — server-driven buckets, dedicated discography, sort/load-more, Magazine, image fixes
Bucketing now trusts the server release_type: drop the foreign-artist filter
that hid band/guest releases (e.g. Vicky Psarakis' Albums were credited to
Sicksense), render every non-empty bucket, hide 'Purchase Only' (download),
and move 'Other' to the end, collapsed by default. Drop the misleading
per-section item counts.

New 'Latest release' highlight (last_release) and 'Appears On' track section
(tracks_appears_on, previously mislabeled as Compilations).

Per-section persisted 5-mode sort (artist_prefs) + per-section load-more
capped at 4 pages, reusing get_releases_grid. New dedicated discography page
(ArtistReleasesView + artist_releases controller) with sort + infinite scroll,
reached via each section's 'See discography' link.

Magazine/Stories sidebar tab (artist/story) with 1:1 thumbnails and a
Network|Magazine tab switcher; hide the empty similar-artists block to remove
the 'no similar' over populated-discovery incoherence. Header Share action.
Per-release award ribbons.

Fix custom artist AND album images: decode + apply immediately on upload and
resolve the local override on navigation, so a custom image now shows for
artists/albums with no Qobuz artwork.

Data layer: type last_release as PageArtistRelease, add /artist/story endpoint
+ core/client methods + ArtistStory* models, export ArtistReleases* globals.

cc
2026-06-21 21:42:22 -06:00
vicrodh 9d6b3533bc feat(slint): add Large now-playing-bar mode (NPB mode B)
Fourth NPB layout: a cover + spectrum dock pinned flush to the bottom-left
of the open sidebar (forming an L), with the bottom bar reusing the New
layout (window-centred transport, full-width inset seekbar, right cluster)
minus the cover. Gated on the open sidebar (npb-mode 3 && sidebar-state 0);
cycling the sidebar out of the open state reverts live to New while keeping
the persisted choice.

- SidebarNowPlayingDock: AppShell root overlay, cover flush to the window
  bottom-left, compact spectrum band (Bars 28-mirrored / Waveform / Energy)
  above it, single top-right eye toggle to show/hide it. FFT reuses
  VisualizerState; when hidden the sidebar list reclaims the space.
- Extract AudioStamp into a shared exported component (consumed by the Small
  and Large bars); in Large it sits at a constant gap left of the right
  cluster, out of the left song card.
- Persist the visualizer on/off and the spectrum mode in ui_prefs; restore
  at startup and prime the FFT tap after visualizer::install().
- Reduce the horizontal volume slider width 10% across all NPB modes.

cc
2026-06-21 14:53:33 -06:00
vicrodh b1ac402cf7 feat(slint): extend cortinilla with Local Library + Plex + per-source artwork
Add local albums/artists/tracks sections to the main header cortinilla
(below the Qobuz results) and local albums to the immersive cortinilla,
derived by grouping the on-device track rows. Merge the Plex cache into
the local search so Plex-only content surfaces (db.search is
local_tracks-only). Route each cover by source via spawn_search_loads
(Qobuz http / Plex /library PlexThumb / local file). Widen the local
section caps when offline or signed out. Rename the section lore from
"on this device" to "on Local Library".

Also clear the search input after a cortinilla element is activated
(row click / View more / Enter) so lingering text can no longer
re-invoke the dropdown when focus returns to the field; replicated in
the immersive search field via a dedicated raw-text property.

cc
2026-06-21 11:19:00 -06:00
vicrodh 85b02417be feat(slint): immersive views polish — 50/50 split, no glass, sharp art, default-view
Split layout is a true 50/50 (explicit half-widths; the horizontal-stretch weights skewed it because the left's intrinsic content out-measured the right). Removed the glass backing plates from the split panel and the song-data metadata block — both sit directly on the atmosphere now.

Cover art no longer upscales blurry on large windows: the now-playing large cover decodes at 1000px (was 300, the hover-preview size) so the full Qobuz source resolution comes through, and the split / Static / Album-Reactive panels cap the art at its native size so it grows sharply instead of stretching.

New Settings > Appearance 'Immersive default view': 'Remember last' restores the last view, or pin Album Reactive / Static / Coverflow / Spectrum / Lyrics / Queue. Persisted via ui_prefs + ImmersiveActions opened/view-changed hooks.

cc
2026-06-20 19:45:16 -06:00
vicrodh d0deeb7044 merge: integrate Tauri->Slint theme migration + a11y redesign
33 themes via Rust-driven qbz-theme registry, theme persistence + OLED
default, 6 redesigned accessibility themes (WCAG AAA, High Contrast x2,
Okabe-Ito colorblind) with contrast unit tests, and the P4
redundant-encoding pass (focus rings, HC-gated borders, tokenized status
colors). Branch feature/slint-themes-a11y.

cc

# Conflicts:
#	crates/qbz-slint/src/main.rs
#	crates/qbz-slint/src/ui_prefs.rs
2026-06-20 17:47:06 -06:00
vicrodh 22fb4162f7 feat(slint): search cortinilla — main-header + immersive
Intelligent Search (phases 3-4): a frontend-agnostic SearchService in qbz-app (Capa A result cache + Capa B interaction ranking) behind a live header dropdown with skeleton loading, stale-while-revalidate, 220ms debounce, keyboard nav + scroll-into-view, per-section caps, a URL-match artwork guard, and an opt-out Settings toggle. Typing previews in the dropdown; Enter goes to Search > All; local-library results show in the dropdown only.

Immersive search: an in-immersive glass cortinilla (Albums/Artists/Playlists) that switches playback without navigating — a Settings action select (disabled/replace-queue/play-next/add-to-queue), the HUD stays up while searching and never hides while the cursor rests on the chrome, twinned top buttons, and a clear button with always-centered field text.

cc
2026-06-20 17:38:02 -06:00
vicrodh 8f90c234b6 feat(a11y): tokenize DiscoverConfigModal warning icon to Theme.warning
cc
2026-06-20 16:02:12 -06:00
vicrodh 80e4d5da4e fix(slint-a11y): destructive button hover darkens solid danger, not the tint
Theme.danger-hover is a 15-25% translucent tint sized for danger-bg
surfaces, so using it as the destructive QbzPrimaryButton fill made the
button ~80% transparent (standard themes) or pale-pink (a11y themes) on
hover. Use Theme.danger.darker(0.15) instead — an opaque, richer red in
every palette, matching the existing danger-button hover idiom in
BlacklistManagerView.

cc
2026-06-20 15:54:28 -06:00
vicrodh 269cae3db8 feat(a11y): tokenize inline disabled-warning banner to Theme.warning
The BlacklistManagerView disabled-filter banner hardcoded the Dark-theme
warning hex (#fbbf24 / #fbbf241a / #fbbf244d) for fill, border, icon tint
and label. These resolve to no other cluster (item #6's named-file list
excludes this view; item #7 owns the WarningBanner.slint primitive, a
different file), so without this swap accessible themes (wcag-dark,
high-contrast) would render the wrong amber here, defeating P4's
'accessible themes take effect everywhere' goal. The hex equal the Dark
warning family exactly, so the swap is identity-preserving on the default
theme.

cc
2026-06-20 15:52:22 -06:00