- 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
The window-level hotkey handler only suppressed single-key shortcuts
(Space/q/?/1-5) when UiFocusState.text-input-focused was set. The ~24
std-widget LineEdit fields across 14 modals/settings never set it, so those
keys fired (and were swallowed) mid-typing. Each now mirrors its focus via
`changed has-focus`; import added where missing.
cc
The read-only shortcuts modal listed every category in one tall column that
scrolled. Split the groups round-robin into three column models (Rust) and
render them side by side; the panel widened to fit. The editable customize
editor still uses the full single-column list.
cc
Second album-page row sourced from Last.fm: the album's primary artist ->
artist.getSimilar -> one top album per similar artist, resolved to Qobuz and
deduped against the Qobuz /album/suggest row (by artist|title and by id).
Gated on Last.fm being connected; resolved row cached per album for 30 days
(re-opens make zero API calls; only non-empty results cached so a transient
failure re-fetches). New build_similar_albums_seeded in qbz-external-reco.
reset_album now also clears this row so the previous album's recommendations
don't linger while the next loads.
cc
The album header (⋯) and cover-art PopupWindows used the default
close-on-click policy, which races the row's TouchArea — the popup can
dismiss on the press before the item's clicked fires, so some rows (e.g.
Share Qobuz link) needed a second open+click. Switch both to
close-on-click-outside and close explicitly per row, matching the existing
Sidebar/QueueSidebar fix.
cc
JACK routes through the JACK graph (resamples to the graph rate), so it
can't be bit-perfect — but nothing told the user. Add a `backend-is-jack`
flag (settings.rs computes it beside alsa/pipewire at both set-sites;
state.slint property) and a WarningBanner in AudioSettings, gated on it,
pointing at ALSA-direct / PipeWire-passthrough for bit-perfect. UI-only;
backend behavior unchanged.
Blind change (no build this session) — validates on the next qbz_ui build.
cc
- About: contributors 4->5 per row so the 10 handles fill the widened
840px modal in 2 even rows instead of 3 sparse ones; modal height
720->770px.
- Splash: move the legal disclaimer out of the card to the bottom of the
screen (the main container, matching Tauri) and shrink it to 10px.
cc
Phase 2 (Slint UI + wiring) for album-level blocking, plus the album-view
enhancements that share these files.
Album blacklist UI:
- "Block this album" in all three album menus: the page-header menu as a
Block/Unblock toggle (gated to Qobuz via AlbumState), and the AlbumCard +
AlbumListRow grid menus (one-way, drop-on-reload; local/Plex excluded).
Menu heights bumped for the extra item.
- Blacklist Manager gains an Artists | Albums tab bar (segmented, ADR-008);
an AlbumRow with a cover thumbnail (blind-eye fallback) + title + artist +
"Added {date}"; tab-aware controls (shared enable toggle, per-tab search /
Clear-All / count) and confirm modal. Settings row retitled "Blacklist",
status shows both counts.
- state.slint: BlacklistedAlbumItem + album-items/album-count/active-tab on
BlacklistState; set-tab/album-select/remove-album/clear-all-albums/
block-album on BlacklistActions; AlbumState.is-album-blocked.
- main.rs: ("album","block"/"unblock") arm (header, resolves display fields
from AlbumState); 5 album BlacklistActions callbacks; seed_blacklist_status
pushes album_count.
- blacklist_manager.rs: album build/push + set_tab/block_album/remove_album/
clear_all_albums; covers resolve async via a new ArtworkTarget::BlacklistAlbum
through spawn_loads.
Album-view enhancements (same files):
- E1: full credited-artist header line with localized roles, each name a
clickable artist link (Album.artists drove this; was interpreter-only).
- E3: classical work-header composer name is now a clickable artist link
(work title + composer id/name split through TrackData/TrackItem).
i18n: es (Mexican) for all new strings; de/fr/pt/ru/ja fall back to English
pending a translator pass.
cargo check -p qbz green (Slint codegen + bin).
cc
- 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
The sleep-timer PopupWindow used the default close-policy (close-on-click),
so any click inside it — including on the preset picker — closed it before
the value could change. Set close-policy: close-on-click-outside. Also
replaced the QbzSelect (itself a PopupWindow, which Slint cannot open nested
inside another PopupWindow) with inline preset rows, so the presets are
actually pickable.
cc
In multi-select mode the entire TrackRow body is a selection target (Tauri
whole-row selectable), not just the 13px checkbox. The body click routes the
same media-action("track", id, "toggle-select") the checkbox does, so the
central toggle arm reads the modifier — Shift+Click range and Ctrl+Click
work on the body too. The "..." context-menu button + the checkbox keep
their own handlers (the menu stays reachable); the other in-row controls
(artist/album link, favorite heart, offline button) are disabled in select
mode so the click passes through to the row. Double-click no longer plays in
select mode. Inert (blacklisted) rows never select.
cc
Extend the keyboard multi-select feature to the surfaces that had no
working selection in the Slint port.
- Mix (DailyQ/WeeklyQ/FavQ/TopQ all render through one MixView): mode
toggle + MultiSelectBar, per-row checkbox via the central toggle arm + a
ContentView::Mix branch (model + recount), bulk play-next/queue/
add-to-playlist. Shift-range, Ctrl+A and Escape inherited automatically.
- Label Popular Tracks: was a dead stub (checkboxes with no bar/recount/
router arms). Converted the local mode flag to LabelState.multi-select,
added a real MultiSelectBar + label.rs recount/select_all/clear/selected,
and the missing select-toggle/bulk router arms.
- LocalLibrary Albums grid (a previously-missing surface): AlbumCardItem
gains a `selected` field; a `select-mode` prop is threaded through the
shared AlbumCard / AlbumListRow / AlbumGrid / AlbumCollectionView (default
false, so Discover/Label/Favorites are untouched). Albums toolbar toggle
+ MultiSelectBar; local_library.rs album select fns (flat + grouped,
id-resilient Shift-range over AlbumCardItem); album->tracks bulk resolve
via fetch_album_tracks_blocking on spawn_blocking for queue/play-next/
add-to-playlist/add-to-mixtape; Ctrl+A and Escape routed by active-tab.
cargo check -p qbz green (qbz_ui compiles clean).
cc
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
Circular 'liquid' audio-spectrum shader scene (Motion-Array style): FFT bands wrapped around a ring, smoothed into an organic blob glowing in the album palette, with a rotating radial-rays backdrop and a beat-triggered burst.
Wired as shader-mode 6 (liquid_spectrum.wgsl + SHADER_SOURCES[4] + render_frame), but the first cut did not land visually, so the picker row is commented out and the g-cycle is left at 0..5 — the scene is unreachable but kept intact for a future pass.
cc
Wave Bed (focus mode 6): centered vertical-mirror waveform. Spectral Ribbon (shader-mode 4): paint-as-you-play GPU spectrogram with green axes + header overlay, bass-at-bottom orientation aligned to the axes, and a real-time ceiling line tracking the highest active frequency. Line Bed (shader-mode 5): wgpu line-strip waterfall with a leveled camera, B-spline-smoothed continuous curves, a primary->accent height gradient, a bottom cutoff above the player, 2x supersampling, and a sampled-texture heights feed (no vertex storage buffer, so it runs on Slint's downlevel wgpu device).
Also: Plasma reworked to be far more reactive (4 ink emitters, beat splats, faster turbulence, quicker decay); immersive song card no longer truncates the album/artist line; player heart reflects favorite state; player bar narrowed; sample-rate and bit-depth wired through for the spectral header.
cc
Expand the shader Uniforms to 144 bytes (phase/beat/level/level_smooth + 8 log
bands + 5 energy bands + the album-art palette triad), feed them per frame from
the 30 fps visualizer drain (new FrameAudio + a forward-motion phase accumulator)
and push the palette on track change. Move plasma and aurora onto the new
uniforms and add a bottom-right ImmersiveSongCard shown over any shader scene.
Pairs with the faster tunnel from the previous commit.
cc
Amplify the forward-flight clock (phase * 6, seam-safe at the 4096 wrap), add
radial speed-lines fanning out of the vanishing point (shimmer with treble and
the beat), and bend the corridor CENTERLINE as a function of depth so the path
winds while the rectangular cross-section stays undeformed. Stronger beat punch.
tunnel.wgsl only; plasma and aurora untouched.
cc
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
When the sidebar is fully collapsed, the header playlists flyout now has
a search box (sharing the sidebar's Rust-side filter, cleared on every
close so it never leaks into the expanded list) and an Import entry
(offline-gated, mirroring the sidebar "..." menu).
cc
Classic NPB's "+" fired a one-click add-to-playlist; now it opens the same
grouped add menu (library / playlist / queue / play-next / mixtape / collection)
as New and Large. Classic keeps its inline favorite toggle next to it so the
favorite state stays visible.
cc
The Large now-playing cover sat flush against the window bottom. Lift it 2px and
take those 2px from the cover<->spectrum gap (10 -> 8) so the dock height is
unchanged; the bottom pad applies only while the spectrum is shown so the
off-state height still matches large-dock-height.
cc
- Appearance: drop the red 'unsupported' tint on Language; hide UI Scale and
the Title Bar / Window Controls / Immersive blocks (commented, not deleted).
- Window title: "Show track in window title" now works -- the OS window title
becomes "{track} - {artist} | qbz" via a reactive binding (editable template
row hidden; format is fixed).
- Plex and Scrobblers collapse carets enlarged (28/18) and moved to row level so
they are vertically centered with the master toggle.
- Hamburger: "Open Qobuz Link" -> "Open Music Link".
- Link modal: a long pasted URL no longer grows the input/modal (min-width 0 on
the clip container caps content-width propagation).
cc
The floating back-to-top button overlapped the last track rows / the docked
Suggested Songs. Reserve the FAB footprint as a bottom footer so content
scrolls clear of it. Constant (not scroll-gated) to avoid a layout binding
loop (footer height -> track-list height -> visible-height -> FAB condition).
cc
- Radio Stations seed only Qobuz-sourced recent albums; local/Plex albums
carry ids the Qobuz /radio/album endpoint can't resolve, so they no longer
generate radio tiles (they still appear in Recently Played Albums).
- Recently Played albums/tracks route artwork by scheme (spawn_search_loads):
http -> Qobuz CDN, /library/ or /photo/ -> Plex thumb, else local file. The
plain HTTP loader left Plex/local covers blank.
- Radio Stations carousel extracted to a RadioCarousel component with prev/next
nav buttons (drag preserved), mirroring SlimCarousel.
cc
Discover > Recommendations Weekly Exploration/Jams kept vanishing and loaded
slowly. Three root causes:
- Weekly track resolution ran every track through MusicBrainz's serial
1.1s/req rate limiter (~110s for 100 tracks), so the rows never painted in
practice (Fresh Releases, album-based with no MusicBrainz, returned fine).
Resolve weekly tracks via fuzzy Qobuz search instead (skip_mb) -- fast and
reliable for these mainstream playlists.
- The external-reco cache was only initialized on the offline entry path;
the two online login paths (auth.rs) never bound it, so nothing cached
online (0-byte db). Initialize it there too.
- Weeklies were bundled in the shared 48h results blob keyed only on
auth-presence. Give them their own per-week cache (keyed by the
ListenBrainz playlist mbid, 9d TTL, written only when non-empty, with a
stale-fallback to the last good week) and rebuild them independently of the
blob, so a transient empty build cannot hide them. Skip the 7d negative
cache for weekly tracks so a throttle does not lock a row empty.
Per-row skeletons: the progressive paint now marks each expected row pending
at spawn and clears it as its builder resolves, so a slow row shows its own
skeleton instead of silently missing. Adds [reco] diagnostics.
cc
The header heart binding + the Rust set_is_following calls reference an
ArtistState.is-following that did not exist (that property lived on other
header globals). Add it so the .slint compiles and the heart reflects the
follow state. Caught by cargo check -p qbz.
cc
The ArtistView header heart dispatched a dead ("artist","","favorite")
with an empty id. Wire it to the artist follow (Qobuz favorite = follow)
as a real toggle that reflects state: bind the heart to
ArtistState.is-following, seed it on artist open, and toggle add/remove
via a new in-memory artist fav cache (fav_cache::is_artist_favorite /
set_all_artists / set_artist) seeded by search + the artist page. The
("artist","follow") arm becomes a toggle (was add-only), so the
search/discovery artist-card Follow chips can unfollow too. Optimistic
flip on the cache + every visible surface, revert on failure.
cc
Add two title-adjacent icon buttons to the external-reco Weekly
Exploration and Weekly Jams rows: add the whole backing track list to the
queue, or create a playlist from it (reusing the add-to-playlist picker
from the queue save-as-playlist work, P6). SlimCarousel gains an opt-in
show-list-actions + list-action callback (default off, so the shared
recent/popular rows are unchanged); external_reco::list_track_ids reads
the row's backing ids; playback::enqueue_track_ids resolves + enqueues
them; a new ("ext-reco-list", _) media-action routes both.
cc
MusicBrainz was hardcoded ON in the core client with no toggle, no
persistence and no startup wiring, so it could never be turned off. Add a
persisted opt-out (default ON: ui_prefs.musicbrainz_enabled +
Settings>Integrations toggle) that seeds the core client at startup and
flips it live via handle_bool.
Gate the MB-dependent surfaces when off: the artist Network/Scene sidebar
marks itself unavailable and opens on the MB-independent Magazine tab;
the playlist "Suggest songs" CTA hides and activate() early-outs; and the
musician page tolerates a disabled/failed MB resolve, falling through to
the Qobuz fallback instead of rendering blank.
cc
The heart on a Qobuz playlist detail was wired to Qobuz /favorite/create,
which does not accept playlist_ids, so it persisted nowhere and never
reflected in the library. Rewire it to the INTERNAL library favorite
(db.set_playlist_favorite, mirroring the Favorites card + Tauri's
v2_playlist_set_favorite) and seed it on open.
Add the two missing actions on a non-owned Qobuz playlist:
- Copy to library: clone it (create + add all tracks, carrying the source
artwork + attribution) as an internal owned copy.
- Follow on Qobuz: subscribe/unsubscribe via new qbz-core passthroughs to
the existing client subscribe_playlist/unsubscribe_playlist.
Surface hearted-but-not-owned playlists in Library>Playlists by merging
the internal favorite ids into the manager list (they don't come back
from get_user_playlists). Copies/follows already appear there (owned /
subscribed). New PlaylistState.is-following / is-copied.
cc
When the queue runs out with InfiniteRadio autoplay on, build a smart
artist radio seeded by the just-finished track and start it, so playback
continues with related music and chains radio -> radio. The toggle, the
AutoplayMode preference and the radio primitives already existed; only
the trigger and the immersive button were missing.
Wire the trigger at the auto-advance queue-end (naturally peer-gated) and
enable the immersive infinity button (binds QueueState.infinite-play +
toggle-infinite-play). Add QueueController::is_infinite_play().
Tauri appends the radio and retries next(); that would replay the old
queue here because QueueManager::next() nulls current_index at the edge,
so this starts the radio fresh via play_tracks to reach the same intended
behavior. Manual-next refill is deferred (needs an explicit peer gate).
cc
Qobuz /radio/album and /radio/track return a tracks page that omits
`total`; the strict SearchResultsPage<Track> derive rejected it with
"missing field `total`", breaking AlbumView and Discover For You radio.
Port Tauri's lenient_page_flexible into qbz-models and apply it to
RadioResponse.tracks (now non-optional, matching Tauri); derive Default
on Track so the absent-tracks branch resolves to an empty page. Two new
qbz-models tests cover the missing-total and absent-tracks cases.
Wire ArtistView's previously-dead radio dropdown: both items now carry
ArtistState.id and have handler arms -- "QBZ Radio" -> smart pool
builder, "Qobuz Radio" -> the plain /radio/artist endpoint
(play_artist_radio, no longer dead_code). Add a "QBZ track radio"
track-menu item backed by a new play_smart_track_radio that fetches the
track to seed its performer.
cc
Surface the new Recommendations tab everywhere Discover sub-sections are listed,
and make it opt-out.
- Flyouts: the sidebar section-nav flyout and both header nav flyouts (full text
+ compact icon) now list "Recommendations" alongside Home / Editor's Picks /
For You. The Rust route handler already maps "discover-recommendations"
(strip_prefix + select_tab + ensure_loaded), so no routing change was needed.
- Opt-out: a show_recommendations flag (default on) persisted in the per-user
discover-prefs store (qbz_app), seeded into SettingsState on session start.
Settings > Integrations gains a "Show Recommendations in Discover" toggle whose
description nudges connecting Last.fm + ListenBrainz (MusicBrainz is used
automatically to match releases). Off => the tab pill and every flyout entry
disappear.
- Gating uses two mounted instances per flyout (one with the entry, one without)
because Slint can't type-infer [NavMenuEntry] from a ternary of array literals;
the HomeView pill uses a plain if.
cargo check -p qbz green.
cc
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
Owner-directed polish on the Recommendations tab to make it richer + faster +
non-repetitive.
- Common vs Recent split: the single "Recommended Artists" row over-represented a
recent soundtrack binge (a live preview showed "The Newton Brothers" flooding
the row with 8 film composers while the core metal was buried). Split into two
rows seeded from DIFFERENT periods: "More like the artists you love" (overall
top) and "Based on what you've been into lately" (1-month top) — so recent
one-offs never pollute the core taste. = 7 rows now.
- Round-robin per seed inside each artist row (re-added from the v1 engine; the
rework had dropped it) + per-seed cap, so no single seed floods the carousel.
- Results cache (48h TTL): caches the FULL built rows (serialized
ExternalCarousels) per-user, keyed by connected sources. Tab open within 48h =
INSTANT paint from cache, zero fetch; stale/missing = build + store. Rebuilds
every 48h (fresh rotation) so it's never "eternally the same" and opens stop
being slow. Distinct from the rec->Qobuz resolution cache (30d/7d).
- Toast on a real fetch (cache miss/stale): "Generating recommendations…" so the
wait is communicated; the skeleton stays; no toast on a cache hit.
cargo check -p qbz green (6m44s).
cc
Reworks the 4th Discover tab per owner feedback (the track-heavy v1 surfaced
already-heard artists). Verified against three sources that Last.fm has NO
recommendations API (getRecommendedArtists removed; web is PJAX HTML behind the
website cookie, which the API session key cannot reach) — so recommendations are
replicated from the documented similarity recipe, and ListenBrainz provides the
genuinely clean personalized feeds.
New lineup (each row paints progressively as its builder resolves):
- Recommended Artists (Last.fm artist.getSimilar seeded by last-month top, minus
the heard set; "Similar to X, Y" subtitle on the artist card).
- Recommended Albums (Last.fm artist.getTopAlbums minus your scrobbled albums).
- Fresh Releases (ListenBrainz, from artists you follow).
- Weekly Exploration + Weekly Jams (ListenBrainz curated playlists, by source_patch).
- Deep-cut albums from artists you know (Qobuz catalog, not heard in-app).
- Cold-start fallback: Qobuz editorial top albums + artists.
The "not heard" filter is now ARTIST-level (the v1 bug derived artists from
unheard TRACKS) and compares against the LOCAL reco history + a light Last.fm/LB
top-set, not a full history sweep. Album validation (UPC -> fuzzy text) added.
qbz-integrations gains Last.fm get_artist_top_albums/get_user_top_albums and
ListenBrainz get_created_for_playlists/get_playlist_tracks/get_fresh_releases.
NOTE: the Cloudflare proxy was switched to allow ALL Last.fm read methods
(anything with ".get"), so future reads need NO proxy redeploy — but the live
proxy must be redeployed ONCE with this change or all Last.fm reads keep 400ing.
cargo check -p qbz green.
cc
Adds "Recommendations" as a 4th Discover tab (alongside Home / Editor's Picks /
For You), rendering the qbz-external-reco engine output. NOT part of For You.
- ui: new ExternalRecoState global + ExternalRecoView (4 personalized carousels
C1-C4 with length>0 self-hide gates + the editorial cold-start rows), 4th tab
pill button in HomeView, mount on active-tab == "recommendations", skeleton
gate. Export the global from app.slint.
- bin: external_reco controller — RecoCatalog over QbzCore, per-user resolution
cache lifecycle, LocalHistory from reco_store (known artists + recent plays),
scrobbler-username gate (is_authed), build + apply into ExternalRecoState
(models built on the UI thread; slint::Image is !Send) + artwork jobs.
- bin: 6 ArtworkTarget variants + resolution arms; reco::recent_track_ids
wrapper; home::select_tab + discover_prefs::push_descriptors treat the new tab
like For You (own view, Home/Editor pushed empty); lazy-load hooks at the tab
click + both Discover nav-restore sites.
cargo check -p qbz green. Last.fm reads still need the proxy allowedMethods
deploy (separate qbz-api-proxy repo) before they return data.
cc
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
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