Commit Graph
102 Commits
Author SHA1 Message Date
vicrodh e70204c8e1 fix(qt): stop shadowing Item.enabled and Item.palette in four components
SettingsButton, QbzToggle and QbzSelect declared their own `enabled` over
Item's, and ReactiveRingsPanel its own `palette` over Item's QPalette: Qt
flagged all four at start (propertyCache: "overrides a member of the base
object"). The shadowed `enabled` never disabled input by itself — each
control filtered clicks and keys by hand, and any child that did not would
have kept receiving events. The inherited property now drives the visuals,
the focus policy and the guards, and disables the subtree the way Item
does; the rings' colour list is `ringPalette`.

cc
2026-09-13 18:35:09 -06:00
vicrodh 1ba578a1d8 feat(qt): move a playlist to a folder from its editor, delete one from any context menu
The editor gains a Folder row — the same folders the sidebar and the
manager file playlists into, "No folder" for the root — seeded from the
playlist's current folder and applied on Save through the manager's own
move verb, so an existing playlist no longer needs the sidebar's row menu
to be filed (#785). "Delete playlist" joins the sidebar row menu (own and
local playlists; sidebar entries now carry isOwner) and the playlist card
menu (own playlists), behind a shell-level "Delete playlist?" confirmation
that runs the same delete the editor's button runs and leaves the detail
page only when it was the deleted one (#776). Eight catalogs updated.

cc
2026-09-13 14:47:12 -06:00
vicrodh e291eb3af4 fix: stabilize playback memory, local library sync and Qt settings
Add shared playback memory profiles, bounded disk-backed playback and storage
selection for desktop and daemon. Preserve gapless during streaming-only playback
and provide explicit cache eviction with playback confirmation.

Stop watcher read events and unchanged scans from rebuilding the local catalog.
Restore playlist/sidebar updates, folder availability feedback and player layout.
Add appearance presets and a radio-based cache settings panel with translated
trade-offs, plus consistent clipboard menus across visible Qt text editors.

Validate with Rust regressions, QML interaction tests, isolated release startup
and idle-library checks; extend the shared local and CI gates.

cx
2026-09-13 02:57:21 -06:00
vicrodh dc158bd9b6 feat(shell): resizable playlist sidebar and queue/lyrics column (#771)
Both panels were fixed-width (240 / 300); playlist names elided on wide
monitors. Each panel's visible edge (the content pane's edge, 8px past the
panel — the bezel between them shares the panel colour) now carries a 5px
grab strip with a 2x28 notch, split cursor, and a one-shot accent pulse when
a drag lands on a snap point.

Rules live in `panel_resize.rs` (pure, unit-tested), the bridge applies them
live and persists on release (`ui_prefs` `sidebar_width` / `queue_panel_width`,
clamped on read):
- sidebar: 240..480 while open; below 200 it snaps to the mini rail, below 24
  it closes through the same state the header button uses (with the Large-NPB
  fallback); the mini rail reopens at 220 — a 20px hysteresis band so no
  pointer x can flip open<->mini on every move (the flapping seen in the
  2026-09-11 screencast). The stored width never drops below 240, so
  reopening restores the last open width. A state snap during a drag still
  animates: the bridge raises `sidebarSnapping` before the state change and
  Sidebar's width Behavior honours it while the drag otherwise disables it.
- column: 300..600; below 260 it closes queue and lyrics as their X would.
  With the queue in the column and the width parked at 600, the notch shows a
  steady larger glow toward the content; pushing 60px further opens the
  Listen List (queue-view) and the queue leaves the column, lyrics staying
  if open. A lyrics-only column never pushes through.

The Large-NPB dock is untouched: it is mounted with absolute x/width in
AppShell and reads nothing from the sidebar width.

Verified: panel_resize 9/9, qmllint + the six qt-run.sh audits, release
builds v1-v4, four owner smoke rounds.

cc
2026-09-11 13:09:47 -06:00
vicrodh 04f6ef44e4 feat(qt): 2.1.x UI polish round — header selects, seekbar, shortcuts, per-tab filters
- Homologate the three Library header selects to the ambient Group style; Sort
  gains an arrow-down-up glyph, a "Sort" placeholder and an explicit Default,
  and every select's border clears when it returns to its default value.
- Seekbar fluidity: a drag scrubs locally and commits a single seek on release;
  the time readout is a floating opaque tooltip reparented to the overlay so the
  content-frame shadow no longer bleeds noise over it.
- Route the NowPlayingBar info button and title to the LOCAL track-info modal
  for a local row, so a local track no longer opens an empty Qobuz modal.
- App menu shows each entry's shortcut (muted, smaller, right-aligned, resolved
  per platform — Cmd on macOS, Ctrl elsewhere) and gains a Quit entry; the
  keyboard-shortcuts modal adds Close Window and Quit under Navigation, moves
  Immersive below Interface, and tightens row heights. i18n for Quit/Close
  Window across all eight locales.
- Persist each Library tab's filter toggles to disk, independently per tab.

cc
2026-09-10 11:21:50 -06:00
vicrodh 2fc03d325d fix(qt): restore window lifecycle and improve grid artwork
Restore tray-aware close and quit confirmation, circular label covers, and missing label artist portraits. Publish artwork progressively, preload two grid rows, and skip redundant motion reports while preserving data refreshes.

Include regression tests and isolated artwork benchmarks. Verified Linux build, QML checks, and scrolling smoke tests.

cx
2026-09-09 21:58:25 -06:00
vicrodh 17e08e93de fix(library): restore offline visibility and unify filters and sorting
Recover missing offline library entries, recognize cached-only libraries, and refresh availability after cache changes. Unify per-tab filters, compact the toolbar, and add reversible sort criteria with complete available purchase metadata.

cx
2026-09-09 17:08:33 -06:00
vicrodh 55ddc2c768 fix(qt): make mouse wheel scrolling predictable
Accumulate wheel distance with short easing instead of kinetic acceleration. Preserve rapid and fractional wheel input, immediate direction changes, nested scroll chaining and scroll-memory takeover.

Validated with 38 Qt Quick tests and desktop smoke testing.

cx
2026-09-08 22:19:32 -06:00
vicrodh 0e76c1f486 fix(qt): preserve navigation shortcuts and release text input focus
Keep search, settings and link shortcuts usable while editing, dismiss focus on outside clicks and navigation, and guard deferred focus against hidden inputs. Add Qt Quick regression coverage.

Refs #753

cx
2026-09-08 21:40:28 -06:00
vicrodh 0b6b111c74 fix(qt): correct playback commands and library browsing
Handle explicit MPRIS Play/Pause without toggling paused playback, remove the 10,000-favorite cutoff, and sort albums by original release dates. Isolate Library filters by tab, include purchased release dates, and support server-ranked artist popularity with safe pagination.

Validated with 766 Qt tests, QML checks, release-sort regressions, and a release build. Refs #750, #751.

cx
2026-09-08 18:27:51 -06:00
vicrodh 0ebcab6d3e fix(library): support SACD artwork fallback and image sidecars
Refresh missing artwork for unchanged SACD images when a folder cover is added after scanning. Enable image-specific metadata and cover sidecars in the Qt editor while preserving physical track numbers and never writing the ISO.

Validated with SACD regressions, the real Heathen image, 13 Qt editor tests, QML audits and the combined release build.

cx
2026-09-08 15:26:03 -06:00
vicrodh b1dba198c9 feat(qt): kiosk profile release hardening
Integrate the Kiosk profile hardening round: 12 dedicated Kiosk views plus
22 shared opt-in hosts, shared navigation history with Back/Forward, Albums
as the default library tab, native paged Local Library (artists/tracks),
lightweight MyQBZ grid and detail, Ephemeral lifecycle, bounded lists and
artwork sized to the physical frame, static skeletons, 64px transport bar,
Settings touch reflow and a persistent Immersive exit.

Desktop keeps its prior behavior: every shared component defaults to the
desktop path and only opts into Kiosk behavior through explicit properties
(origin-relative ScrollMemory, viewport-bounded artwork requests).

Regression coverage: kiosk navigation and artwork Node tests added to the
runtime gate, scroll-origin fixtures for fixed and grouped rows, i18n
strings for all eight locales.

Verified by 750 release tests, six QML audits, Kiosk smokes at 800x480 and
1280x800 and a Desktop A/B with seven pixel-identical captures.

cc
2026-09-07 08:23:11 -06:00
vicrodh 316a0c8f46 fix(qt): replace unavailable library favorites
cx
2026-09-05 17:31:17 -06:00
vicrodh 6d07dfdf4d feat(artist): sort for the in-library albums and a split play button on every album section
The "In library" Albums section gains the release buckets' sort select
(same five keys, persisted by artist_prefs under the pseudo release type
"library"; rows sorted in QML off the document's seed). Every album
section — each catalog bucket and the library grid — gains a split
button right of its sort (controls/QbzSplitButton.qml, sized like the
sm select): the body plays the whole section in its sort order as one
queue replacing the current one (`QbzPlayer.playAlbums` ->
`playback_qt::play_albums`, each album resolved through the single-album
path, failures skipped), the chevron offers Play random (one album of
the section) and Play selected, which arms a per-section select mode:
the cards show their check bottom-right inside the art
(`AlbumCard.selectMarkBottom`), the button reads "Play selected (n)" and
plays the checked albums in sort order, and the menu swaps that row for
Cancel selection. One section is armed at a time; changing artist or
tab disarms. No queue options on purpose.

cc
2026-09-04 13:10:42 -06:00
vicrodh c85fbac4c7 fix(artist): keep the jump-nav mounted in library mode and park the filters past the magnifier
With both source switches on over an artist whose purchases are not
hearted, the library tab's section set was empty, the strip unmounted,
and the switches — which live in the strip — went with it, leaving no
way to reset the filter. "About" (the strip's back-to-top) is now always
present in library mode, landing on the page top since the library
column carries no bio anchor; the active tab on a mode switch is still
the first real section.

The trailing slot moves to the far right of the bar, past the search
toggle: the field grows LEFT over the tabs when it opens, so controls
left of the magnifier were covered while typing.

cc
2026-09-04 11:51:59 -06:00
vicrodh 010b1da8d8 feat(qt): warmLibrary invokable and a trailing slot on the jump-nav bar
`QbzArtist.warmLibrary` lets the artist page's "In library" toggle re-arm
the Library feed warm (a fetch only if the session never loaded it, a
cache repaint otherwise). `QbzJumpNavBar.trailing` is a host-filled slot
between the tab strip and the magnifier, sized by its children and
costing nothing when empty, so a page can float mode-specific controls
in the strip without reflowing the tabs.

cc
2026-09-04 10:41:51 -06:00
vicrodh 561b3c25c7 feat(purchases): gate album actions by entitlement
cx
2026-09-03 04:32:23 -06:00
vicrodh feb2c9de12 fix(qt): pin the quality stamp tooltip to its text
The now-playing stamp bubble came out wider than its lines with the text
offset, and never reproduced in isolation. The three stamp ToolTips now
bind width/height straight to the content's implicit size with zero
paddings, insets and margins, leaving nothing for the style to add.

cc
2026-09-02 20:34:58 -06:00
vicrodh 472706f84e fix(settings): harden migration and accessibility
Apply interface scaling through Qt at startup, complete keyboard navigation, move account migration into setup and progress modals, and defer Subsonic sync while the server scans.

cx
2026-09-02 14:33:08 -06:00
vicrodh a5376e9064 feat(shell): active purchase downloads live in the sidebar, as a ring when compact
The Downloads section leaves the nav flyout. With the sidebar open, the
purchases downloading now sit under the Purchases entry, one row per
album with a thin progress bar, each reopening its detail. With the
sidebar collapsed, and in the compact header nav when it is closed, they
fold into one progress ring whose hover bubble lists every album with
its count and percentage. The ring is a Canvas that repaints only when a
publish moves the value — no timer, per the repaint-pulse rule. The
active set now carries done/total/percent per album.

cc
2026-09-02 07:11:03 -06:00
vicrodh fd44d38cd3 feat(quality): a DSD tier with the DSD mark
A 1-bit master wore the Hi-Res logo and "24-bit / 352.8 kHz" — a stream
Qobuz never serves (nothing above 24/192 streams). Give DSD its own
tier: the official DSD mark, baked light and dark so the monochrome logo
follows the theme, on the mini badge, the full badge and the now-playing
stamp, with the multiple (DSD64 / DSD128) as the detail line. The
now-playing derivation and the delivered-tier evaluation report `dsd`
for a 1-bit stream instead of folding it into hires.

cc
2026-09-02 01:17:48 -06:00
vicrodh 1cb988954e fix(qt): rename on-prefixed QML properties and gate the pattern
A property named `on<Upper>` is filed as a signal handler as soon as a
sibling member `<upper>` exists: its initializer never binds, it sits at
the type default, and nothing warns. `PurchasesView.qml` declared
`onAlbums` next to `albums`, so the Albums tab bound `[]` once and never
re-evaluated, which blanked Purchases for every account with real
purchases. Reproduced standalone with the `qml` runtime: the identical
expression under another name reads `true`.

Rename it to `albumsTab` and the four other `on<Upper>` properties
(`collectionsTab`, `surfaceTint`, `accentFloor`, `litColor`) so the rule
can be exact, and add `qml_on_prefixed_property_audit.py` to the four
QML audit sites (qt-run, cargo-test, test-crates, qt-gate-windows).

cc
2026-09-01 23:40:33 -06:00
vicrodh f6e1c4e023 fix(qconnect): harden queue ownership handoffs
Preserve local playback when it is authoritative, make renderer conflicts explicit and persist the selected policy. Filter unsupported queue rows, restore portable Qobuz identities after disconnect, and fence takeover commands to avoid stale queue and playback loops.

cx
2026-09-01 17:23:14 -06:00
vicrodh d563d9aa63 fix(qt): align the playlist picker footer
cx
2026-09-01 17:22:45 -06:00
vicrodh 9493993124 feat(qt): overAmbient legibility mode for the quality badge and track info
Immersive surfaces sit directly on the ambient field, where theme tokens
have no contrast guarantee — a light cover washed the split Track Info
panel and the quality badges out completely. QualityBadgeFull,
TrackInfoBody, InfoMetaCell and InfoCreditCell gain an opt-in overAmbient
mode: fixed light text with the restrained native shadow the lyrics
surfaces already use, and the badge swaps the theme surface for its own
translucent dark scrim covering only the chip. Defaults stay off, so the
desktop modal and every themed mount are untouched. The immersive Track
Info panel and song card opt in.

cc
2026-08-31 15:03:09 -06:00
vicrodh 69e5066d07 fix(qt): wheel-lock sweep across plain-Item modal overlays
Plain-Item overlays get no modal grab, so a MouseArea that swallows
presses still lets the wheel scroll the page underneath (the rule and
its wording live in DiscoverConfigModal.qml). Every scrim and panel
swallow across the modal set now accepts the wheel; the modal Popups
(TrackInfo, CastPicker, AlbumInfo, QconnectDev, LocalMediaInfo) already
had the Overlay grab, and CoverLightbox — a non-modal Popup — gets its
own.

cc
2026-08-31 11:50:32 -06:00
vicrodh c650856c84 feat(qt): picker modal wheel lock, wider card, containment side column
The picker and its duplicate sub-modal eat the wheel on scrim and card
(the DiscoverConfigModal contract) so the page behind no longer
scrolls. The card widens to 735px, and Already in moves from an in-flow
horizontal strip to a right-hand column that exists only when there are
containments to show — the playlist dropdown overlays the main column
only, so the answer stays readable while the list is open.

cc
2026-08-30 18:58:33 -06:00
vicrodh 4984736370 feat(qt): picker modal pins last playlist and shows containment strip
The dropdown pins the session's last successful target above the
results (clock glyph, de-duplicated by Rust, retained under a search
only when it matches). The body gains the collapsible Already in strip:
one card per writable playlist already holding carried tracks, with an
N-of-M badge on partial multi-track containment; clicking a card
selects that playlist so completing a partial add is one click plus the
duplicate-confirm's only-new answer. The strip renders only when the
index can back the claim — complete with matches, or updating under an
explicit caveat — and an empty answer from an incomplete index renders
nothing. Four new strings in all eight locales.

cc
2026-08-30 17:23:58 -06:00
vicrodh 96342e23bc feat(qt): expose ALSA hardware volume settings
Add capability-aware enablement, exact control selection, diagnostics, live volume synchronization, and translated unsupported-state feedback to the Qt settings surface.

cx
2026-08-30 09:14:29 -06:00
vicrodh c7a3012955 feat(local-library): complete catalog and metadata workflows
Add bounded native library models, complete grouping rails and scan progress. Bring source-aware metadata editing, media information and collection offline preflight to Qt, and repair inherited Plex album artwork without overriding healthy tokens.

cx
2026-08-28 18:50:05 -06:00
vicrodh d555b8fde4 feat(qt): add album quick view and queue focus controls
cx
2026-08-26 16:29:16 -06:00
vicrodh aa66c58ea4 fix(playlists): restore cover editing
cx
2026-08-26 16:29:04 -06:00
vicrodh 8d1fb5593d refactor(ui): rename music link action
cx
2026-08-26 12:31:35 -06:00
vicrodh edcb12b8a1 feat(qt): close migration parity gaps
cx
2026-08-26 11:11:50 -06:00
vicrodh fc7fe4f094 fix(qbz-qt): align collection controls with theme
cx
2026-08-26 02:28:52 -06:00
vicrodh bae1523abf fix(qbz-qt): finish compact album header layout
cx
2026-08-25 23:58:45 -06:00
vicrodh d0654f77bd fix(qbz-qt): hide native scopes on software renderer
cx
2026-08-25 23:17:33 -06:00
vicrodh 2d31244a7a fix(qbz-qt): refine compact album header layout
cx
2026-08-25 22:04:35 -06:00
vicrodh 1bd99aed67 fix(ui): finish pending Qt interaction polish
Keep segmented controls within their visual well, use the active theme for playback indicators, disconnect inactive pulse receivers and improve near-fit song metadata.

cx
2026-08-25 18:25:05 -06:00
vicrodh 5501221fdc fix(visualizer): refine scopes and seekbar
Make immersive scopes responsive and derive readable colors from album artwork. Use distinct full-size bar and compact contour renderers for the seek waveform.

cx
2026-08-25 08:21:21 -06:00
vicrodh ef7ea217c8 fix(visualizer): improve stereo scope rendering
Render the oscilloscope as a filled antialiased trace and the goniometer as a persistent point cloud with robust autoscaling. Keep the animated scene graph native and the instrument guides static to avoid the previous low-impact line rendering.\n\ncx
2026-08-25 02:51:31 -06:00
vicrodh d11e946227 feat(visualizer): add stereo scopes
Add a theme-aware goniometer and pitch-locked oscilloscope to the large dock and immersive focus modes.

Gate scope DSP and scenegraph updates by actual panel visibility so hidden modes remain idle, including while shader scenes cover immersive panels.

cx
2026-08-25 00:22:40 -06:00
vicrodh 635b8b3a45 fix(metadata): restore editor mounting
Remove an unsupported shared-button property that invalidated the
workspace type lazily in both AlbumView and ephemeral surfaces.

cx
2026-08-24 22:00:22 -06:00
vicrodh 5e759fab50 refactor(metadata): split editor workspace
Give track editing a dense spreadsheet pane, move album tags to an independent sidebar, and isolate remote metadata and artwork lookup in a loading-aware popup.

cx
2026-08-24 21:37:11 -06:00
vicrodh fa649394d1 fix(metadata): scope track row selection
Bind nested editor controls to the delegate index so virtualized rows never resolve an undefined context property.

cx
2026-08-24 20:44:13 -06:00
vicrodh 75a668ed12 feat(metadata): expand the local tag editor
Promote metadata editing to a routed full-page workflow with tabular per-track fields, ordered artist credits, provider identifiers, and compilation handling.

Add bounded artwork selection and retrieval through MusicBrainz, Discogs, and Last.fm. Direct and sidecar saves preflight immutable rows, verify rich tag writes, cache covers by content, and update exact database ids transactionally.

cx
2026-08-24 19:46:59 -06:00
vicrodh 5a14eef258 fix(metadata): complete local editor sessions
Consume preflight data when loading clears so the modal cannot spin indefinitely.

Reuse the editor for ephemeral folders and bind sidecar or direct writes to the exact active session.

cx
2026-08-24 17:28:54 -06:00
vicrodh 0e53837582 feat(metadata): restore local tag editor in Qt
Restore the selected-version editor with sidecar-safe defaults, inspected tag layers, guarded MusicBrainz and Discogs lookup, and confirmed verified direct writes. Keep paths Rust-owned and reject stale or injected drafts.

cx
2026-08-24 16:41:06 -06:00
vicrodh 5e4d7ca033 fix(queue): preserve metadata and polish extended view
cx
2026-08-22 20:58:46 -06:00
vicrodh ef333cd7ec fix(login): point a never-had-an-account user at the door that is for them
Local-only mode already WORKED — verified by driving a genuinely fresh
profile (no credentials, keyring unreachable): "Start offline" enters the
guest profile, Local Library opens with its proper empty state, Settings
renders in full, zero QML complaints. The capability was never the
problem. Two pieces of the screen were.

1. THE SIGNPOST WAS OFF FOR EXACTLY THE RIGHT PERSON. The callout with
   the caret pointing at the Start-offline link was gated on
   `hasPreviousSession && connectivity === 2`. `hasPreviousSession` is
   seeded from `load_last_user_id()` — None on a fresh profile — and only
   ever set true by a successful Qobuz login. So the one piece of UI that
   says "you can use this without an account" could not be seen by
   anybody who had never signed in. The gate was written for the
   returning-user-with-no-wifi case and the guest case was bolted onto
   the link alone.

2. THE COPY WAS FALSE. Once that box is visible to a first-run user with
   working internet, "No internet connection" is simply untrue, and the
   offline placeholder said the same thing after entering: measured on a
   fresh profile, the log read `connectivity Up, induced false,
   offline_session true` while the screen claimed the network was down.
   Both now branch on which of the THREE states this is — induced
   offline, no connectivity, or no account — and the captive-portal line
   is gated on connectivity too, so it cannot appear under a "No Qobuz
   account?" heading.

Two new msgids in all seven non-English catalogues.

Verified after the change on a fresh profile: the callout appears, the
link is reachable, and the placeholder reads "You're using QBZ without a
Qobuz account."

HARNESS NOTE for whoever tests this next: XDG_CONFIG_HOME sandboxes the
token FILE but NOT the system keyring, and a rejected token there calls
`clear_oauth_token`, which deletes the keyring entry for the real user.
Point DBUS_SESSION_BUS_ADDRESS at nothing so the Secret Service is
unreachable. (The file stays authoritative, so nothing is lost — but the
run is not clean either.)

cc
2026-08-22 05:09:56 -06:00