Remove header-content wrapper so h1 and action buttons are direct
flex children of .header, placing them inline on the same row.
Remove orphaned CSS and margin-top from header-actions.
Strip the customizable icon from the Favorites header (now always a
Heart icon) and remove icon/background customization sections from the
edit modal. The modal now only handles tab order reordering. Removed
dead code: resolveCustomIconSrc, customIconSrc, unused lucide imports
(Star, Folder, Library, Heart), convertFileSrc import, header-icon CSS.
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.
Grey out shuffle, previous, play/pause, next, repeat, add-to-playlist,
and favorite buttons when no track is loaded and queue is empty.
Volume/mute controls remain always active.
Closes#103
- Auto-detect installed .desktop filename (com.blitzfc.qbz.desktop,
qbz.desktop, qbz-nix.desktop) instead of hardcoding
- Run update-desktop-database after register/deregister so xdg-open
and browsers pick up the MIME association immediately
- Fix v2_check_qobuzapp_handler to match all known desktop filenames
- Align StartupWMClass to com.blitzfc.qbz across all packaging formats
- Add StartupNotify=true to Arch/AUR/Snap desktop files
Fixes browser URI handler not working for .deb users (Discussion #44)
KeybindingsSettings (Customize Shortcuts) had z-index 1000, behind
NowPlayingBar (2001). Bumped to 3000 per ADR-003. Also widened to
660px and added 2-column grid layout with responsive fallback.
This was the actual broken modal — KeyboardShortcutsModal was already
fixed but KeybindingsSettings was missed.
- AlbumCard: right-click opens AlbumMenu at click position
- FavoritePlaylistCard: right-click opens menu at click position
- QobuzPlaylistCard: add right-click context menu with click positioning
- SearchPlaylistCard: add right-click context menu with click positioning
- All card menus now use floatingMenuStore for mutual exclusion
- Menu positioning uses click coordinates instead of trigger button
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.
reload_settings() was running before every v2_play_track call, which
is excessive and can overwrite correct in-memory audio settings with
stale DB values (especially in Flatpak where DB sync timing differs).
Moved to runtime_bootstrap Step 6, after session activation when
per-user stores are ready. v2_reinit_audio_device still reloads on
explicit device changes.
On first play after app start, the audio thread held stale settings from
Player::new() — if the user had configured PipeWire/ALSA backend, the
thread might not use the correct backend/device, defaulting to 48kHz.
Now v2_play_track reloads settings from the DB before every play_data()
call, matching what v2_reinit_audio_device already does. This is why
re-selecting the device fixed the issue: it called reload_settings first.
Also extracts convert_to_qbz_audio_settings() helper to deduplicate the
settings conversion between v2_play_track and v2_reinit_audio_device.
Fixes#69
Thread onCreateQbzRadio/onCreateQobuzRadio through TrackRow,
VirtualizedTrackList, PlaylistDetailView, and FavoritesView so
track context menus in playlists and favorites show the radio
submenu with QBZ Radio and Qobuz Radio options.
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 context menu support (right-click) to the inline track rows in
ArtistDetailView and LabelView popular tracks sections. Right-clicking
a track opens the TrackMenu at the cursor position with all available
actions. Also wires up TrackRow component with contextMenuPosition
prop for the same functionality in reusable track rows.
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.
The /radio/album endpoint returns partial track objects without the
performer field, causing all tracks to show as "Unknown Artist".
Extract track IDs from the response and fetch each via get_track()
to get complete data, matching the pattern used by artist/track radio.
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).
Add /radio/album Qobuz API endpoint support:
- RadioResponse struct in models.rs with lenient deserialization
- get_radio_album() client method
- v2_create_album_radio command that calls the API directly (no
RadioPoolBuilder/RadioDb), filters blacklisted artists, populates
queue, and sets Radio playback context
- Register command in lib.rs invoke handler
Two fixes for session not persisting:
1. Changed v2_get_all_queue_tracks to return a struct instead of a
tuple — Tauri IPC serialization of tuples is unreliable.
2. Added proactive session saves instead of relying on async
beforeunload (which races with window close):
- Debounced full save on track change (2s after new track starts)
- Debounced full save on pause
- Reduced periodic save from 60s to 15s during playback
- Close handler remains as best-effort backup
The save was broken in two ways:
1. Only current_track + upcoming (capped at 20) were saved, dropping
all history tracks. Queue was reconstructed incomplete on restore.
2. Track metadata (hires, bit_depth, sample_rate, source, etc.) was
not being persisted, only the basic 6 fields.
Add v2_get_all_queue_tracks command that returns the full uncapped
track list with correct current_index. Use it in saveSessionBeforeClose
instead of the capped v2_get_queue_state. Map all metadata fields.
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.
Update PersistedSession interface with last_view fields. Add lastView,
viewContextId, viewContextType params to saveSessionState(). Add
restoreView(), setRestoredPlaylistId(), setRestoredLocalAlbumId() to
navigationStore for session restore without triggering data fetches.
Add last_view, view_context_id, view_context_type to player_state
table via migration. Update PersistedSession struct, save/load/clear,
and v2_save_session_state with 3 new optional parameters.
- 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)