Land the "New" now-playing bar as the first of the four switchable
layouts. A dedicated SongCard component holds the cover (or an idle
sleeping cat), the elidable title, the artist·album links, and the
vertical audio stamp (compact quality badge over the backend/mode LEDs).
A full-width seek bar sits on top with a buffer-driven stream lock.
The controls row uses responsive symmetric column zones (39/30/25% by
window width); the side columns share one fraction so the transport stays
absolutely centred and PLAY lands on the window centre. The song-card /
controls divider is a hard wall: the text column stretches to push the
audio stamp to its right edge, the album elides first (and never collapses
to zero) so the stamp is always visible and never crosses the wall.
Adds QualityBadgeFull compact mode, backend/mode output LED labels from
settings, a global TooltipOverlay so clipped columns keep their tooltips,
and the supporting icons.
cc
After bit-perfect ALSA Direct grabs a DAC exclusively (hw: + D-Bus
reservation), the device stays sequestered and is invisible to PipeWire,
so switching back to the PipeWire backend can't see it without restarting
the app. Tauri had a refresh affordance for hot-plugged DACs; this is the
refresh plus an explicit release, in one button.
Backend: Player::release_device() / AudioCommand::ReleaseDevice tears the
stream down immediately (cancelling the deferred drop), freeing the hw:
grab + its reservation, then un-suspends the parked sink and resets any
forced graph clock (both self-gating). It does NOT reopen, unlike
reinit_device which would re-grab the device.
UI: a refresh-cw icon button beside the Output device select, propagated
through the settings callback chain to handle_release_device, which
releases, waits for PipeWire to reclaim, then re-enumerates so a freed or
hot-plugged DAC reappears without an app restart.
cc
Backend-side foundation for a native JACK output. The hard, RT-sensitive part
is done and compiles: jack_backend.rs holds a JackStream that opens a JACK client
'qbz' with stable ports qbz:out_FL/FR, a lock-free SPSC ring buffer, and an
allocation-free process callback (de-interleaves stereo f32 to the ports). The
feeder pushes interleaved f32 via write_f32().
Also: jack 0.13 dep (linux), AudioBackendType::Jack + its factory arm + the
qbz-slint label/bit-perfect match arms. NOT yet offered in the selector
(available_backends push commented) because the player wiring is still pending:
StreamType::Jack + the dispatch branch (backend==Jack -> JackStream) + a
PlaybackEngine::Jack feeder thread that resamples each track to the JACK graph
rate. JACK mode will be opt-in and NOT bit-perfect (one fixed graph rate); the
ALSA-exclusive / DAC-passthrough bit-perfect paths are untouched.
TODO(distribution): feature-gate jack for Flatpak/distro builds without libjack.
cc
Port #470's UI behavior: add an "Auto" entry at the top of the backend
dropdown that, on select, resolves to the best available backend (PipeWire
if present, else System) and persists that concrete value. The rebuilt
snapshot then moves the dropdown onto the resolved backend, so backend_type
is never left null/Auto. Concrete backends keep mapping by their shifted
index; System wiring comes from available_backends() (merged backend half).
cc
The Output Device dropdown, on the ALSA backend, listed devices flat,
unordered, with the BP badge on too many rows. Port the Tauri
DeviceDropdown.svelte ALSA behaviour:
- Regroup devices into Defaults / Bit-perfect (Hardware / Digital) /
Plugin Hardware / Other Outputs, in that order, with a section header
above the first row of each non-empty section.
- Align device_is_bit_perfect (the BP badge rule) with the Tauri ALSA
bit-perfect group: hw: / front:CARD= / iec958: only. hdmi: and
sysdefault: route through plugins and no longer get the badge.
- The regrouped ids stay index-aligned with SettingsMaps.devices, so the
settings-select("device", i) callback still resolves to the right id
and selected-device persistence is unaffected.
cc
QueueController owns the sidebar-local view state — active tab, search
query, paginator page — and the cached favorite-track IDs. It pulls a
full queue snapshot via QbzCore::get_queue_state_full, applies the search
filter, slices out the current 40-track page, and pushes the result onto
QueueState. Every queue mutation refreshes it.
The four footer actions: clear-queue calls core clear_queue (wiping the
now-playing slot only when nothing is audible, matching Tauri);
save-as-playlist resolves the queue track set (name-input modal pending);
infinite-play toggles the persisted AutoplayMode; search filters the
upcoming list client-side. play paths route through after_track_change,
extracted from the per-call duplication in playback.rs.
cc
The BP badge marked every ALSA device; it now marks only the direct
hardware PCMs (hw:/front:/iec958:/hdmi:), matching Tauri — sysdefault
and plug devices are not bit-perfect. The device list also opens at
480px (right-anchored) so the full card + PCM id name is readable
instead of being elided in a 300px control.
cc
The output-device select now shows an accent BP marker on devices
able to deliver bit-perfect audio: every ALSA hw card, hardware nodes
on PipeWire, none under PulseAudio. QbzSelect gained an optional
parallel badges array so other selects are unaffected.
cc
NowPlayingBar (start of the playback-bar work):
- height raised to 110px
- SeekBar component: three-line track (total / cached / progress), a
hover thumb, elapsed/remaining times, and a hover time bubble with a
downward caret marking the seek position
- song card: background and border dropped, gains the extra height,
album/artist links and the context icon kept (context icon now
always shown)
- transport + secondary buttons copied from Tauri (shuffle, prev,
play, next, repeat, add, favorite; cast, picture-in-picture,
fullscreen, normalization; volume cluster). Queue/Lyrics stay in the
header — and the header now orders them lyrics-then-queue.
Settings constraints (1:1 with Tauri):
- Exclusive Mode enabled only on ALSA; DAC Passthrough only on
PipeWire; Lock Output Device disabled while DAC Passthrough is on;
Gapless disabled while Streaming Only is on
- backend change cascades: forces off DAC passthrough / force-bit-
perfect / exclusive / gapless as appropriate; DAC-passthrough and
streaming-only cascades; initial-buffer slider right-aligned
Discover fixes:
- navigation [<] [>] pair homologated to the same top/left margin
across every view
- album/artist description moved back into the metadata column (it now
fills the width there) with uniform secondary text color
- genre filter button hugs its label instead of stretching
- Recently Played Albums and Tracks modules always render, with a
placeholder until the play history has data
- wider spacing between Discover rows; the Discover gear is no longer
a Settings shortcut (it is the per-tab sort/show-hide control)
cc
Rebuild both Settings panels to match the Tauri app — every option
present, on the correct tab, and fully functional (persisted and
applied to the live player).
Audio: streaming quality, limit-quality-to-device, maximum sample rate,
backend, output device, ALSA plugin, hardware volume, exclusive mode,
reserve DAC, DAC passthrough, force bit-perfect, allow quality
fallback, sync-on-startup, lock output device, and a reset button.
Conditional rows (ALSA plugin, hardware volume, max sample rate, force
bit-perfect, lock output device) are gated on Rust-computed flags.
Playback: continue-after-track, show context icon, gapless, restore
session, resume position, stream uncached, initial buffer size (slider),
streaming only, quality-retry behavior. Note gapless and the streaming
options live on the Playback tab though the qbz-audio struct stores them.
- ui_prefs.rs: JSON store for the frontend-only streaming-quality pref
- QbzSlider primitive for the integer buffer-size control
- volume normalization row removed (Tauri hides it)
Caveat: toggling reserve-DAC persists and is read live, but re-arming
the device reservation guard mid-session needs a player-side API that
does not exist yet; it takes effect on next launch.
cc
Introduce the Settings page, reachable from the Discover gear and the
header menu. Two sub-sections backed by the existing UI-agnostic stores.
- Audio panel: backend, output device and sample-rate dropdowns;
toggles for exclusive mode, DAC passthrough, PipeWire force
bit-perfect, gapless, normalization and quality fallback
- Playback panel: autoplay mode, show context icon, remember session
and resume position
- settings.rs controller owns AudioSettingsStore + PlaybackPreferencesStore
at the shared global path; persists changes and applies audio ones to
the live Player (reload_settings, plus reinit_device for routing-
critical changes); a backend switch re-enumerates output devices
- QbzToggle (switch) and QbzSelect (PopupWindow dropdown) primitives,
SettingRow layout helper, chevron-down icon
- SettingsState global, ContentView::settings, settings-bool /
settings-select callbacks wired through the shell
cc