All animations default to 60fps. Each panel reads its own setting
from userStorage via shared fpsConfig utility. Settings UI shows
a collapsed section with per-panel dropdowns:
- Background Blur, Spectrum, X/Y (Lissajous), Oscilloscope,
Energy Bands, Transient Pulse, Album Reactive, Spectral Ribbon
Options: Disabled, 15, 30, 60, 120 FPS
i18n keys added to all 4 locales (en, es, de, fr)
Mirror the album cover context menu options: right-clicking the artist
photo now shows Open in Browser and Save As below the existing
custom image options, separated by a divider.
Search input can now live in the titlebar instead of the sidebar,
controlled by a new toggle in Settings > Appearance. When the custom
titlebar is hidden or system titlebar is active, search stays in the
sidebar regardless of the toggle.
- New searchBarLocationStore with localStorage persistence
- TitleBar accepts search props and exposes focusSearch()
- Sidebar hides its search container when titlebar search is active
- Ctrl+F focuses the active search input in both modes
- Titlebar height increased from 32px to 36px to fit the search field
- Translations added to all 4 locale files (en, es, de, fr)
When downloading a single track from the Purchases tab, if multiple
formats are available a popup lets the user choose before the folder
picker opens. Single-format tracks proceed directly as before.
- Add BulkActionBar component (sticky-bottom bar with queue split button,
add to playlist, add to favorites, clear selection)
- Add selectable/selected/onToggleSelect props to TrackRow with checkbox UI
- Add .selected highlight and .track-checkbox CSS to TrackRow
- Extend VirtualizedTrackList with selectable/selectedIds/onToggleSelect props
- Wire FavoritesView tracks tab: Select toggle button in toolbar,
selectedTrackIds state, BulkActionBar with bulk queue/favorites/playlist handlers
- Add actions.select and actions.cancelSelection i18n keys to all 4 locales
Replace the 5 static k-means palette previews with 8 labeled, editable
swatches showing the actual applied CSS variables: Base, Surface,
Elevated, Accent, Text, Muted, Border, Danger.
Each swatch opens a native color picker on click. Changes apply
instantly to the live theme and persist to localStorage.
Remove unused ThemePalette/PaletteColor types and k-means palette
extraction from the frontend store (no longer needed since swatches
are driven by the GeneratedTheme variables directly).
Source options: System Colors (accent -> wallpaper fallback), Wallpaper
Sync, Custom Image. Selecting System shows fullscreen overlay while
generating. On failure: modal recommends another theme or custom image,
falls back to Dark immediately.
Store cascade logic: tryAccentColorTheme() first (with wallpaper
backgrounds + system accent override), then generateFromWallpaper(),
then throw for caller to handle.
cc
Add a Move icon button inside the expandable window controls group
in ImmersiveHeader. On mousedown it calls window.startDragging() to
let the user reposition the window. Cursor shows grab/grabbing.
Only available when the controls are expanded (hover), so collapsed
state has no drag zone as intended.
The reload_settings() call at bootstrap is now gated behind a new
sync_audio_on_startup setting (default OFF). Most users don't need
it; it exists for edge cases where Player::new() holds stale settings
(e.g., after Flatpak updates changing DB schema).
Adds toggle in Settings > Audio with i18n for all 4 locales.
FavQ shuffles up to 50 tracks from the user's Qobuz favorites.
TopQ seeds the Qobuz suggest API from the user's most-played playlists.
Both have same-day cache, refresh button, red/blue gradient artwork,
and a "qbz" badge (vs "qobuz" on DailyQ/WeeklyQ).
Add /radio/artist and /radio/track Qobuz API endpoints alongside the
existing /radio/album. TrackMenu now shows a radio submenu with QBZ
Radio (RadioPoolBuilder) and Qobuz Radio options when both are
available. Artist header radio button opens a dropdown to choose
between the two engines.
Backend: get_radio_artist(), get_radio_track() in client.rs,
v2_create_qobuz_artist_radio and v2_create_qobuz_track_radio commands.
Frontend: TrackMenu radio submenu with mutual exclusion against
share/download submenus, ArtistDetailView dropdown, LabelView support.
i18n keys added to all four locale files.
Add Radio button to album detail header (next to favorite button)
that calls v2_create_album_radio, populates the queue with Qobuz
API recommended tracks, and starts playback of the first track.
Add i18n keys for album radio in all 4 locale files (en/es/de/fr).
Re-enable queue/track restore on startup (visual only, paused at 0:00).
Update saveSessionBeforeClose to persist current view with context IDs.
Add "Startup page" dropdown in Settings > Appearance (Home / Where you
left off) that controls whether the last view is restored on launch.
i18n keys added to all 4 locale files.
- Strip Shift modifier for symbol keys in eventToShortcut() so ? maps correctly
- Remove duplicate Space handler in ImmersivePlayer that caused double-fire
- Add bindingsVersion dependency to KeybindingsSettings for instant visual updates
- Add missing uiOpenLink i18n key and use getActionLabel() fallback in modal
- Add full keybindings translations to de.json and fr.json
- Add 4 bundled fonts: Montserrat, Noto Sans, Source Sans 3, plus
existing LINE Seed JP (default) and System fallback
- Font selector dropdown in Settings > Appearance, after Language
- Persisted in localStorage, applied on app bootstrap via data-font
attribute on <html> which overrides --font-sans CSS variable
- @font-face declarations for variable font files in static/fonts/
- i18n key "fontFamily" added to all 4 locales
- Add persistent drag region (32px strip) at top of immersive overlay
that remains active even when the header auto-hides after 4 seconds
- Add app-region: drag CSS to ImmersiveHeader's left spacer for proper
drag support when header is visible
- Add app-region: no-drag to tabs and window controls to keep them
clickable
- Double-click on drag strip toggles maximize
- Rename "Full Screen" to "Immersive Mode" in all 4 locales (en/es/de/fr)
Ctrl+L now accepts URLs from Spotify, Apple Music, Tidal, Deezer, and
song.link/album.link in addition to Qobuz. Non-Qobuz track/album URLs
are resolved to their Qobuz equivalent via the existing Odesli API
integration. Playlist URLs redirect to the Playlist Importer.
- Add MusicResource/MusicProvider enums and detect_music_resource()
- Add detect_resource() to each provider (spotify, apple, tidal, deezer)
- Add v2_resolve_music_link command with Odesli-based cross-resolution
- Upgrade LinkResolverModal with platform icon detection and playlist redirect
- Update i18n keys in all 4 locales (en, es, de, fr)
- Verbose capture toggle now persists to localStorage across sessions
- PurchasesView dropdown menus now have mutual exclusion (opening one closes others)
- Add to Library button wired to v2_library_add_folder with download destination tracking
- Added i18n keys for add-to-library success/error toasts (all 4 locales)
Add folder/flat toggle button that switches between showing playlists
organized in folders vs a flat list of all playlists. Add tree view as
a third view mode (grid -> list -> tree cycle) when folder mode is on.
- folderMode state persisted to localStorage
- Tree view groups playlists by folder with expand/collapse
- Flat mode shows all playlists regardless of folder assignment
- Auto-switch from tree to grid when folder mode turns off
- i18n keys added to all 4 locale files (en, es, de, fr)
Replace the flat album grid LabelView with a rich detail page featuring:
- Header with label image, name, description, and play button
- Popular Tracks section (5/10/all expand, same UX as ArtistDetailView)
- Releases horizontal scroll row with "See all" → LabelReleasesView
- Critics' Picks section from second releases container
- Playlists, Artists, and More Labels sections
Backend: add /label/page and /label/explore API endpoints with V2
commands (v2_get_label_page, v2_get_label_explore). Models use
serde_json::Value for flexible section parsing.
Old LabelView renamed to LabelReleasesView for the "See all" flow.
i18n keys added to all 4 locales (en, es, de, fr).
The download button in the tracks tab had no onclick handler, so
clicking it triggered the row's playback handler instead. Now it
opens the native folder picker, downloads with the best available
format, and shows download status (spinner/checkmark). Artist link
also uses stopPropagation to avoid triggering playback. Fixes#91.
- Add back navigation button to both DailyQ and WeeklyQ views
- Replace subtitle with home card descriptions
- Remove cache/live source text, add total playback duration
- Remove refresh button from WeeklyQ (regenerates weekly on Fridays)
- Fix weekly cache to use Friday-based key instead of daily
- Add algorithm info icon with tooltip on both views
- Fix playback context: align queue index with blacklist-filtered tracks
- Change header label from "Playlists" to "Your mixes"
- Add algorithmInfo i18n key to all 4 locales
Replaced redundant title/owner below cards with descriptive text
(DailyQ: customized selection, WeeklyQ: every Friday emphasis).
Moved qobuz badge and mix name to top of gradient card. Added
yourMixes/weeklyMixes i18n sections to de.json and fr.json.
Static section (no API call) with video-background cards that navigate
to DynamicSuggestView and WeeklySuggestView. Positioned after Qobuz
Playlists in default Home order, toggleable via Home Settings.
Add experimental toggle to force PipeWire clock.force-quantum alongside
clock.force-rate for true bit-perfect playback. Quantum is matched to
sample rate (1024 for <=48kHz, 2048 for <=96kHz, etc). Both values
reset to 0 on stop and pause-suspend to avoid affecting other apps.
Requires DAC Passthrough to be enabled, PipeWire backend only.
- Parse track_number (index) and disc_number (parentIndex) from Plex XML
- Add track_number, disc_number, album_key columns to plex_cache_tracks
- Fix plex_cache_get_album_tracks: add WHERE album_key clause (was
returning all tracks) and sort by disc/track number instead of title
- Update all SELECT/INSERT queries to include new columns
- Add inline track search to album detail view with search icon toggle
- Update PlexCachedTrack frontend interface and mapPlexTrack mapping
- Add i18n keys for search placeholder in all 4 locales
- Toolbar: FavoritesView pattern (control-btn, dropdown-menu, dropdown-item)
- Albums: grid/list toggle, group by (Off/A-Z/Artist), sort with direction
- Filter panel: LocalLibraryView pattern (checkbox for hide unavailable, Hi-Res)
- Tracks: group by (Off/Artist/Album/Name A-Z)
- Format selector: standard Dropdown component instead of custom pill
- Download All: global action-btn-circle instead of custom button
- Quality in track rows: plain text (12/192) instead of QualityBadge images
- QualityBadge compact only in album header and list mode
- Add filter.availability and filter.hideUnavailable i18n keys (4 locales)
- add Spectral Ribbon analyzer/render path and immersive panel
- tune ribbon density/interpolation and bars edge-bin rendering
- apply artist blacklist filtering to discover index home containers
- include supporting i18n/settings/player wiring changes
cx
The wizard step 6 was writing per-app bit-perfect rules to
pipewire-pulse.conf.d/pulse.rules, but QBZ uses pipewire-alsa (via
rodio/cpal), so those rules were never applied.
Changed to pipewire.conf.d/stream.rules which applies to ALL PipeWire
clients regardless of API (ALSA, PulseAudio, JACK). Also updated the
step indicator from "Pulse" to "Apps" in all 4 locale files.
Fixes#53 (wizard bug)
Added to all 4 locales (en, es, de, fr):
- settings.integrations.qobuzLinkHandler* keys for settings toggle
- linkResolver.* keys for the modal (title, placeholder, go, resolving, invalidLink)
Add V2 commands that implement the ADR Runtime Session Contract:
- v2_init_client: Phase 1 - init client only (bundle tokens)
- v2_auto_login: Phase 2 - auto-login with blocking V2 auth
- v2_manual_login: Manual login with blocking V2 auth
This allows frontend to control ToS check between init and auth:
1. v2_init_client() -> InitializedNoAuth
2. activate_user_session() -> restore session
3. loadTosAcceptance() -> check ToS
4. v2_auto_login() or v2_manual_login() -> Ready
V2 CoreBridge auth is now REQUIRED per ADR - login fails if V2 fails.
Migrate LoginView.svelte to use new V2 commands:
- Replace init_client with v2_init_client
- Replace auto_login with v2_auto_login
- Replace login with v2_manual_login
Add i18n keys for V2 auth errors in all 4 locales.