Commit Graph
5638 Commits
Author SHA1 Message Date
vicrodh 45cbe24206 feat(kiosk): lightweight KioskSearch + KioskLibrary views
Replace the heavy reused SearchResultsView and FavoritesView on the kiosk
search/favorites routes with Pi-light versions built from the shared kiosk
cards: KioskAlbumGrid (wrapping grid of KioskCard), KioskArtistCard (round),
KioskTrackRow. KioskSearch tabs All/Albums/Tracks/Artists/Playlists reading
SearchState; KioskLibrary tabs Tracks/Albums/Artists/Playlists/Labels reading
FavoritesState. Both bind the shared models so the artwork pipeline pushes
covers in with no request hook (album covers windowed = first ~60 for now).
Validated via slintcheck.

cc
2026-07-10 19:21:48 -06:00
vicrodh f27d5e864c fix(kiosk): pin NavRail row to root.width so tiles distribute
The tiles still collapsed to the bottom-left even with per-tile
horizontal-stretch: a HorizontalLayout nested in the rail's VerticalLayout
was taking its preferred (content) width, not the rail width. Drop the
wrapper, make the tile row an explicit width: root.width child with the
hairline as an overlay, and the 6 tiles now spread evenly across the panel.

cc
2026-07-10 19:21:48 -06:00
vicrodh ce2db8cb54 feat(kiosk): lightweight KioskDiscover with simple cards
Replace the heavy reused HomeView on the kiosk home route with a Pi-light
Discover: KioskCard (square art + quality badge + title + artist, tap-to-
open, no hover/context-menu chrome) in horizontal shelves, reading the SAME
DiscoverState model so the artwork pipeline pushes covers in with no request
hook. Tab pill (Home/Editor's Picks/For You). First of the mockup-matching
lightweight views; also guards single-key hotkeys while typing in the kiosk
search field. Validated via slintcheck.

cc
2026-07-10 18:39:24 -06:00
vicrodh 01311db29c fix(kiosk): stretch NavRail tiles across the full width
The tiles collapsed to icon width, packed bottom-left with truncated labels
(a horizontal-stretch set only on the child component root is not honored by
the parent layout). Set horizontal-stretch at each mount site + fill the rail
height so the 6 tiles distribute evenly as full-height touch targets.

cc
2026-07-10 18:39:24 -06:00
vicrodh dc32cafbd5 fix(kiosk): resolve is_creating_mini() at window setup
The P0 kiosk block read a creating_mini local that only exists inside
select_slint_backend(); at the main-window setup site it is out of scope.
Call crate::miniplayer::is_creating_mini() directly (same source the backend
hook uses) so the qbz bin compiles. Confirmed: the kiosk shell builds
(release) and launches.

cc
2026-07-10 17:21:53 -06:00
vicrodh 93e523f51b feat(kiosk): QBZ_KIOSK_NO_FULLSCREEN dev escape hatch
Skip fullscreen-at-boot when QBZ_KIOSK_NO_FULLSCREEN is set so the kiosk
layout can be previewed in a normal window (the kiosk shell exposes no
titlebar control to leave fullscreen). reduce-motion still applies.

cc
2026-07-10 16:39:06 -06:00
vicrodh 408c945c44 feat(kiosk): persistent search field in the KioskShell back bar
The kiosk drops the HeaderBar (where the desktop search box lives), so add a
search LineEdit to the back bar: Enter submits through SearchActions.submit
(the same seam the header uses) and lands on the results view. This makes
Search functional end-to-end in kiosk (query -> SearchResultsView -> tap a
result -> the album/artist detail views mounted in P2). Validated via the
slintcheck stub.

cc
2026-07-10 15:02:38 -06:00
vicrodh af34a8e40c feat(kiosk): full ContentView route parity in KioskShell (P2)
Extend the KioskShell content mount to every ContentView route AppShell
hosts (discover-browse, recent-albums, local-album, artist-releases,
musician, label(-releases), award(-albums), location, purchases, purchase-
album, playlist(-manager), mixtapes, collections, mixtape-detail,
discography-builder, offline/blacklist managers, local-library, mix,
playlist) plus the offline placeholder + qobuz-view-blocked gate — a
verbatim copy of AppShell so navigation reaches the same real views by
construction. Discover, Search, and the album/artist detail views tapped
from them are functional. Validated through the Slint compiler (slintcheck
stub). ShellContent DRY extraction still deferred.

cc
2026-07-10 14:59:07 -06:00
vicrodh 242a02055a feat(kiosk): KioskShell touch shell + NavRail (P1, axis B)
Add AppScreen.kiosk mounting a KioskShell sibling of AppShell in the same
AppWindow: a thin back bar + persistent PlayerBarSmall + a bottom NavRail of
6 tiles replace the HeaderBar/Sidebar, sharing every *State global verbatim.
The NavRail routes through the SAME NavState routes the desktop uses
(header-menu-navigate / request-settings / ContentView.search). Content
reuses the real Home/Album/Artist/Favorites/Search/Settings views (verbatim
subset of AppShell mounts) with the arithmetic content-height pattern to
avoid the layout-collapse footgun. Rust lands the Kiosk screen post-login
when the kiosk profile is active. Validated through the Slint 1.16.1
compiler (slintcheck stub); remaining routes + touch-interaction remap are
P2/P3.

cc
2026-07-10 14:48:53 -06:00
vicrodh 7e6e7ade17 feat(kiosk): QBZ_PROFILE=kiosk runtime profile (P0, axis A)
Add a kiosk startup profile for small-panel touch appliances (Pi/SBC): the
main window boots fullscreen and forces reduce-motion regardless of renderer
tier. Resolution is env-first (QBZ_PROFILE=kiosk, so the kiosk image sets it
in the autostart) with a persisted ui_prefs.profile fallback; the miniplayer
window is never affected. No .slint touched. The kiosk image still pins
QBZ_RENDERER=gl and the XS ui_scale preset via env (separate knobs).

cc
2026-07-10 14:17:49 -06:00
vicrodh a9949b8cf2 chore: fix stale packaging and monitor references to the deleted tree
cc
2026-07-10 01:05:47 -06:00
vicrodh 15ff525d43 ci(dependabot): track the crates/ workspace, drop npm and src-tauri
cc
2026-07-10 01:04:42 -06:00
vicrodh ca1fa56a5b docs(contributing): route contributors to the crates/ Slint tree
cc
2026-07-10 01:04:31 -06:00
vicrodh 13fce17bd1 refactor(qbz-app): drop Tauri-only boot/graphics/settings modules
Remove boot_watchdog, graphics_detection, and settings::{download,legal,
library,image_cache} (only src-tauri consumed them). Split
graphics_autoconfig: keep Environment/detect_environment/detect_gpu_name
(used by the Slint diagnostics panel), drop the WebKit Recommendation half.

cc
2026-07-10 01:02:38 -06:00
vicrodh 39204a41ff chore(cleanup): remove legacy Tauri/vite dev scripts and legacy-tree-notice workflow
cc
2026-07-10 01:00:06 -06:00
vicrodh 6f2dc90ef9 chore(cleanup): drop root npm/vite/svelte build config, patches, and .env.example
cc
2026-07-10 00:59:53 -06:00
vicrodh f8a42d3f42 chore(cleanup): remove legacy Svelte src/ and Tauri src-tauri/ trees
The live app is the Slint UI under crates/. Both trees are preserved at
tag legacy-tauri-svelte for reference.

cc
2026-07-10 00:58:35 -06:00
vicrodh e8d7f28f0e chore: bump flake to 2.0.1
cc
2026-07-09 18:15:42 -06:00
vicrodh 5ba00ab4ef ci(linux): save the amd64 slint-linux cache on main (was never written)
release-linux had save-if:false deferring cache writes to build-slint.yml,
which no longer runs — so the amd64 `slint-linux` dep cache was never created
and every release/snap amd64 build ran fully cold (no dep cache; qbz_ui
recompiles regardless). release-linux-aarch64 already saves (save-if:true),
which is why only the aarch64 cache existed. Save on main so pre-flights warm
it and tag runs restore it.

cc
2026-07-09 12:09:54 -06:00
vicrodh 235bad253a ci(snap): skip the Snap Store publish when no store credentials are set
The publish step failed the whole run with "login_data is empty" when
SNAPCRAFT_STORE_CREDENTIALS is unset, even though both binaries compiled and
the amd64 snap packed fine. Move the secret to job level and gate the publish
step on it: with no secret the snap is still built and uploaded as an artifact,
publish is skipped (green run) instead of failing. Applies to both arches.

cc
2026-07-09 10:13:02 -06:00
vicrodh 207d928fd8 ci(snap): add an arm64 track to release-snap (native runner, dump repack)
Both arches now publish from CI: the existing amd64 track plus a new
build-binary-arm64 (native ubuntu-24.04-arm, free for public repos, reuses
release-linux-aarch64's cargo cache) -> snap-arm64 that dumps the prebuilt
aarch64 binary with packaging/snap-slint/snapcraft-arm64.yaml. This CI upload
is the sole publish path; Launchpad / "Build from GitHub" is not used.

cc
2026-07-09 08:13:58 -06:00
vicrodh 50d6b6d4da docs(release): carry the 2.0.x codename into the 2.0.1 TAG_DETAILS
New rule: the release codename stays fixed across a minor line and only changes
at the next minor (2.1.x). 2.0.1 therefore keeps "Rebuild 破 (You Can (Not)
Advance)"; the About-modal literal is unchanged.

cc
2026-07-08 22:56:05 -06:00
vicrodh 59754b4169 Merge pre-release into main for 2.0.1 2026-07-08 22:48:48 -06:00
vicrodh 078cce0e32 docs(release): keep 2.0.0 TAG_DETAILS, add a 2.0.1 one-liner section on top
The About modal and website render the release's TAG_DETAILS; for the 2.0.1
point release, preserve the full 2.0.0 story and prepend a terse 2.0.1 section
(one line per fix) plus the 2.0.1 compare link.

cc
2026-07-08 22:48:29 -06:00
vicrodh 7ea9e2567b chore: bump version to 2.0.1
Workspace + lockfiles, the dead-Tauri consistency version fields, the metainfo
<release> entry, and the release docs (TAG_DETAILS.md / CHANGELOG.md).

The About-modal codename and the TAG_DETAILS h1 are left as __CODENAME__ /
__SUBTITLE__ placeholders on purpose — the owner names the release before the
tag (2.0.0 was "Rebuild 破 (You Can (Not) Advance)").

cc
2026-07-08 18:43:27 -06:00
vicrodh bb01545996 Merge bugfix/queue-context-menu-actions into pre-release 2026-07-08 18:38:32 -06:00
vicrodh 222001a9eb fix(queue): wire the Remove all after and Add to playlist row actions
The per-track context menu in the Queue sidebar had two placeholder items
with no click handler: "Remove all after" and "Add to playlist". Wire both
end to end.

- QueueState gains remove-all-after(int) and add-to-playlist(int), resolved
  from the page-local row index like remove-upcoming (search-filter aware).
- Core/player gain remove_upcoming_after(upcoming_index): truncates the
  upcoming list in PLAY order (shuffle-aware, reuses remove_upcoming_track),
  clears the stop-after marker if it lands in the removed range, and emits a
  single QueueUpdated. The pre-existing remove_after (absolute-index, not
  shuffle-aware) was dead code and stays only for its unit coverage.
- Add to playlist reuses the save-as-playlist picker handoff seeded with the
  single row's track id.

cc
2026-07-08 18:38:27 -06:00
vicrodh a84519c10e Merge bugfix/viz-paused-cpu into pre-release
cc
2026-07-08 16:57:28 -06:00
vicrodh f5ed2ec1fe fix(viz): stop the visualizer pipeline while paused
The NPB Large dock idled at ~2.5% CPU paused: the visualizer gates on
visibility only, so the FFT producer kept re-transforming the stale
ring buffer at 30fps and the drain timer kept churning the 28 bar
models. Producer now parks when enabled-but-paused (same bounded
200ms park_timeout shape as the existing disabled-park; the atomic is
a lone Relaxed store, submit path untouched); the drain early-returns
when not playing and unparks the producer on the resume edge, so
fresh bars land in <100ms. The paused flag is stored at every
playing-state flip: toggle, track meta seed, the poll loop dirty-push
(catch-all for MPRIS/tray/hotkeys/QConnect within one 450ms tick),
the cast poll while casting, stop-after, queue end and the
unavailable-skip halts. Bars freeze at their last values while
paused; immersive freezes too — intended. Legacy Tauri tree's tap
literal gained the field to keep compiling.

cc
2026-07-08 16:57:28 -06:00
vicrodh fef978fc76 Merge feature/qconnect-device-name into pre-release
cc
2026-07-08 15:26:39 -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 029bb0583f Merge feature/recent-albums-view-all into pre-release
cc
2026-07-08 14:07:16 -06:00
vicrodh d3b00b78ef feat(home): Recently Played Albums gets a View-all full page
The rail's 'View all' opens a dedicated page listing the recently
played albums (the dedicated album history from the #567 fix, cap 24).
Built on the AwardAlbumsView skeleton stripped to essentials: fixed
header + windowed AlbumCollectionView wired per the new sampler
contract, scroll-restore, shared scrollbar. Works offline (local
history — the AppShell arm deliberately skips the offline gate) with
mixed-source rows routing through the existing open-album funnel
(qobuz/plex/local) and source-split artwork dispatch mirroring the
rail's. Full nav integration: NavEntry::RecentAlbums round-trips
through last_nav so view persistence restores it (a local JSON read —
cannot crash-loop), ADR-004 back button via the standard record +
navigate + flags shape. Zero new msgids — title, empty state and the
View-all link all reuse existing strings across the 8 locales.

cc
2026-07-08 14:07:16 -06:00
vicrodh addc18b219 Merge bugfix/windowing-recursion-sweep into pre-release
cc
2026-07-08 13:43:50 -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 11ed6c6dea Merge bugfix/search-artist-carousel-height into pre-release
cc
2026-07-08 11:51:51 -06:00
vicrodh 42d7375dab fix(ui): size the Search artists carousel for the taller ArtistCard
ArtistCard grew for the Recommendations subtitle line, and Search
cards fill that same slot ('N albums') plus the Follow chip — but the
two Search carousel instantiations kept the 220px default, clipping
the chip. Pass card-height 256px, the same value Recommendations
sizes the identical stack with. ArtistCard itself untouched.

cc
2026-07-08 11:51:51 -06:00
vicrodh 9a66b6dc1e Merge chore/legacy-tree-notices into pre-release
cc
2026-07-08 11:47:28 -06:00
vicrodh ec1f48b581 chore: legacy-tree notices — PR bot, sentinel READMEs, PR template
Two contributors in one week sent solid PRs against the retired
Tauri/Svelte tree (#561, #564) because nothing in the repo says it is
dead. Three zero-risk notices, no code touched: a pull_request_target
bot that leaves one sticky comment when a PR touches src//src-tauri/
(target on purpose — fork PRs cannot be commented under plain
pull_request; the PR code is never checked out), sentinel READMEs at
both tree roots (what GitHub renders when browsing), and a PR template
checkbox. The tree deletion + local reference worktree is the 2.0.2
follow-up (owner hold: no dependency risk allowed before 2.0.1).

cc
2026-07-08 11:47:28 -06:00
vicrodh e576d4d210 Merge bugfix/home-customize-566-567 into pre-release
cc
2026-07-08 11:46:57 -06:00
vicrodh 338d20df9b fix(home): render all 13 configurator sections; own recent-albums history
Two Home bugs, one dropped port. #566: four configurator sections
(favoriteAlbums 'Library Albums', qobuzMixes, releaseWatch, topArtists)
were offered and persisted but had no HomeView render arm and no data
in load_home — enabling them produced an invisible row. All four were
genuine Tauri-Home features (DiscoveryView.svelte arms :632/:653/:660/
:666, loaded unconditionally), so this is the full port, not a trim:
own HomeState fields (separate lifecycle from ForYouState), shared
foryou fetch pipelines (favorite_album_cards / fetch_release_watch /
top_artist_cards extracted pub(crate), For You behavior untouched),
Home-scoped artwork targets, and HomeView arms mirroring ForYouView.
load_home joins the three fetches concurrently with the index — zero
added latency. A HOME_RENDERABLE guard next to descriptors_for is now
the single source of truth: an enabled id without a render arm can
never emit an invisible descriptor again.

#567: 'Recently Played Albums' starved because albums derived from the
24-track history (long albums collapse to ~4 distinct). recently.rs
now keeps a separate album history (dedupe by id at record time, own
24 cap) with additive migration from the legacy bare-array format;
tracks keep their cap; load()/load_albums() signatures unchanged.

Zero new msgids (all three titles reuse For You's). qbz-app tests
10/10; slintcheck green.

cc
2026-07-08 11:46:57 -06:00
vicrodh 99a2079df1 Merge bugfix/html-entity-decode into pre-release
cc
2026-07-08 11:11:29 -06:00
vicrodh 604902521f Merge bugfix/lenient-parse-extension into pre-release
cc
2026-07-08 11:11:29 -06:00
vicrodh c21a9393a7 fix(text): decode HTML entities in API prose — including malformed bare forms
Qobuz prose fields carry HTML entities the plain-text Slint renderer
showed literally — the artist bio credit rendered '&copy John Book
/TiVo' (the API sample itself ends '&copy  Mariano Prunes /TiVo': a
malformed entity with NO semicolon, which the old decoder never
matched). New qbz_text_utils::decode_html_entities: ~90 named
entities, full numeric dec/hex with cp1252 quirk range, and a
word-boundary tolerance for the bare no-semicolon forms restricted to
copy/reg/amp/nbsp (documented tradeoff; 'AC&DC' never decodes). Wired
INTO the existing strip_html pipeline so every treated funnel (album/
label/playlist descriptions, bios) inherits it, plus four new direct
sites: bio source credit, magazine story title/excerpt, and the
track/album copyright lines in the info modals. 19 unit tests.

cc
2026-07-08 11:08:01 -06:00
vicrodh e73dcf65da fix(parse): lenient per-item parsing everywhere a bad item blanked a list
Extends the #556/#565 fix (one malformed item in a JSON array nuked
the whole list via from_value::<Vec<T>>().unwrap_or_default()) from
favorites to every remaining silent-blank site found by audit: the
search parse_page (EVERY search tab — scalars now parse separately,
items per-item), For You fav-albums/fav-artists rails, the daily-mix
favorite_tracks source, and qbz-qobuz get_dynamic_suggest. The helper
moves to qbz-models::lenient (the one crate all consumers reach) with
unit tests; favorites re-uses it via alias, local copy deleted. The
~30 endpoints whose parse errors PROPAGATE stay as-is — honest errors
stay honest. New parse_page test covers a poisoned item surviving.

cc
2026-07-08 11:00:15 -06:00
vicrodh 982716acb7 test(core): fix album_with_artists fixture — missing composer/version
qbz-core tests did not compile since the Album model gained these
fields; the test-only helper never got them. Found while adding the
poisoned-item parse_page test.

cc
2026-07-08 11:00:15 -06:00
vicrodh 2f258e1933 Merge fix/macos-titlebar-settings into pre-release
cc

# Conflicts:
#	crates/qbz-ui/ui/settings/AppearanceSettings.slint
2026-07-08 10:53:01 -06:00
Victor RH c671bcd85e Merge pull request #569 from afonsojramos/charlottetown
fix(settings): hide title bar section on macOS
2026-07-08 10:52:32 -06:00
vicrodh 0e55f6a24f fix(ui): macOS title bar — restart-to-apply toggle, hide-completely row hidden
PR #569 follow-through with the owner-corrected scope: on macOS BOTH
titlebar modes work (native bar, or traffic lights integrated in the
QBZ header) — what's broken is hot-applying the toggle: the overlay
window attributes are fixed at creation, but the .slint chrome
(no-frame, header drag+inset, Purchases placement) flipped reactively,
hiding the buttons one way and overlapping them the other.

Split pref from applied state: the toggle edits use-system-title-bar
(persisted), the chrome reads a new system-title-bar-active seeded at
startup; the handler mirrors pref->active live on Linux only (behavior
byte-identical there), macOS keeps the session value + the existing
restart toast. The hide-title-bar row is macOS-hidden (that part of
the PR was right); the system-bar toggle stays.

cc
2026-07-08 10:52:30 -06:00
Afonso Jorge Ramos 8f1f287191 fix(settings): hide title bar section on macOS 2026-07-08 17:24:38 +02:00