Commit Graph
1805 Commits
Author SHA1 Message Date
vicrodh 976e5226b4 feat: add miniplayer keyboard shortcuts and fix X button behavior
Add Shift+M toggle shortcut for mini player mode (main and mini window).
Add 1-5 number keys for surface switching (micro/compact/artwork/queue/lyrics)
in mini player context. Add playback shortcuts (Space, Ctrl+Arrow) to mini
player window. Fix X button to close app (or hide to tray) instead of
duplicating the Expand button behavior. Closes #134.
2026-03-01 14:04:18 -06:00
vicrodh 0f1f6b3716 fix: amplify ambient shader motion for visible animation
Previous shader was too subtle — drift, rotation, and breathing were
imperceptible with the smooth 8x8 texture. Now with much larger values:
- Drift: 0.05 -> 0.20 max (Lissajous with two frequencies)
- Scale breathing: 2% -> 12% range
- Rotation: 0.86deg -> 5deg range
- Added warm/cool color shifting over time
- Stronger vignette pulsing and brightness breathing

Safe because the 8x8-sourced texture has zero structure to reveal.
2026-03-01 13:30:18 -06:00
vicrodh db5d9182f2 fix: use 8x8 downscale instead of CSS blur to destroy artwork structure
CSS filter:blur() may not work reliably in all WebView environments,
leaving artwork structure visible as rectangular artifacts. New
approach: draw the full artwork at 8x8 resolution (64 pixels) where
no structure can physically survive, then scale up to 128x128 with
high-quality bilinear smoothing. One optional blur pass for polish.
2026-03-01 13:29:48 -06:00
vicrodh e5955a8f91 fix: replace multi-layer ambient shader with single-layer approach
The 3-layer parallax shader created visible rectangular artifacts on
dark/uniform artwork. Each layer sampled at different scale/offset,
and the spatial weighting transitions between layers showed as
geometric seams regardless of offset magnitude.

New shader uses a single texture sample with organic Lissajous drift,
gentle rotation, scale breathing, pulsing vignette, and vertical tint.
The pre-blurred texture handles the visual complexity — the shader
just adds subtle life without geometric artifacts.
2026-03-01 13:24:30 -06:00
vicrodh 10d395a89e fix: preserve dark artwork mood in ambient background
Two issues caused visible dark rectangles on simple/dark artwork:

1. Texture normalization always mapped to [30-240] regardless of source
   brightness. For dark artwork (Metallica), this amplified noise 5x and
   turned a dark album into a light gray background. Now uses
   brightness-aware blending: dark artwork gets gentle lift (preserves
   mood), colorful artwork gets full normalization.

2. Shader layer offsets were too large (up to 20% of texture), causing
   visible layer boundaries where each sampling layer showed different
   parts of the amplified texture. Reduced offsets by ~60% and tightened
   scale ranges for smoother parallax.
2026-03-01 13:22:05 -06:00
vicrodh 23aefcda80 fix: replace CLAMP_TO_EDGE with MIRRORED_REPEAT for ambient texture
CLAMP_TO_EDGE stretched edge pixels when ambient shader UVs exceeded
[0,1] range, creating visible dark rectangles on simple/dark artwork
(e.g. Metallica's black album). MIRRORED_REPEAT seamlessly mirrors
the texture instead. Also removed the hard clamp() in the fragment
shader so the mirrored wrapping actually takes effect.
2026-03-01 13:15:33 -06:00
vicrodh ef48cd5936 refactor: move immersive settings to end of Appearance section
Group immersive-related settings under a new "Immersive" subsection
at the bottom of Settings > Appearance, in order:
1. Immersive Default View
2. Composition (collapsible)
3. Disable Blur Background
4. Animation Frame Rate (collapsible)

Also fix chevron alignment in collapsible sections to be right-aligned
(justify-content: space-between) and clean up unused CSS classes.
2026-03-01 13:03:12 -06:00
vicrodh 298d669c58 fix: align settings UI elements with established patterns
- Replace native <select> with Dropdown component for GSK renderer
- Fix collapsible section layout: title+chevron on one row, description below
- Apply section-title-row wrapper to both Animation Frame Rate and Composition
- Add divider between Animation Frame Rate and Mini Player Default View
- Remove unused .composition-select CSS
2026-03-01 12:55:34 -06:00
vicrodh c518cffe30 fix: align immersive settings with standard collapsible pattern
- Replace raw checkbox with Toggle component for blur background
- Use composition-subsection class for FPS section (adds top border)
- Use composition-title class for consistent 14px font size
- Move desc to section-note inside expanded content
- Add summary key for inline collapsed text
- Shorten title from "Immersive Animation Frame Rate" to
  "Animation Frame Rate"
2026-03-01 12:46:18 -06:00
vicrodh d023af5478 fix: align immersive FPS collapse with other collapsible sections
Replace raw text caret and h4 with the standard section-title +
section-summary + ChevronDown/Up icon pattern used by the composition
section. Remove duplicate description that now shows as summary.
2026-03-01 12:40:48 -06:00
vicrodh d509c30979 feat: update composition profiles with GSK renderer and blur toggle
Add gskRenderer and disableBlurBackground fields to CompositionProfile.
Update existing profiles: Xwayland Performance now disables blur.
Add new Max Performance profile (Cairo software renderer + blur off)
for maximum compatibility on stutter-affected systems.

Profile matching and apply logic now includes all new fields.

Part of #127.
2026-03-01 12:39:47 -06:00
vicrodh 09b1ee0d72 feat: add option to disable immersive blur background
When enabled, replaces the expensive blurred album artwork background
with a solid dark color extracted from the artwork at tiny resolution.
Significantly reduces GPU load and fixes stutter on affected systems.

Setting is stored in localStorage (same as FPS settings) and applies
on next immersive mode entry.

Part of #127.
2026-03-01 12:34:46 -06:00
vicrodh 58ad252798 feat: add GSK_RENDERER control for GTK4 rendering backend
Add gsk_renderer field to graphics settings DB with UI dropdown
(Auto/GL/NGL/Vulkan/Cairo). Value is read at startup and set as
GSK_RENDERER env var. Env var override takes precedence. Also
included in --reset-graphics and reset now restores hw_accel to true.

Part of #127.
2026-03-01 12:30:41 -06:00
vicrodh 86450f629d fix(security): use cryptographically secure session id generation
cx
2026-03-01 09:20:39 -06:00
vicrodh 426d0ea3ff fix(security): replace regex HTML sanitizer with DOM-based sanitization
cx
2026-03-01 09:20:21 -06:00
vicrodh 2095e5fce3 fix: add FavQ and TopQ context types to playback context
The ContextType enum and v2_set_playback_context match were missing
fav_q and top_q variants, causing an "Invalid context type" error that
blocked all playback from FavQ and TopQ views. Also wrap
setPlaybackContext calls in try/catch so context errors don't prevent
track playback.

Fixes #140
2026-03-01 08:58:45 -06:00
vicrodh 37df510994 feat: add quality-adjusted tooltip case for hardware compatibility
When QBZ adjusts quality for device compatibility (e.g. requesting
Hi-Res instead of Ultra Hi-Res because the DAC doesn't support
176.4kHz), show a blue info tooltip instead of the yellow "Resampled
by system" warning. The new message clarifies that playback is
bit-perfect with no resampling — QBZ simply requested a compatible
quality from the source.

Two distinct tooltip cases:
- Yellow: "Resampled by system" (PipeWire/system is resampling)
- Blue: "Quality adjusted for device compatibility" (QBZ chose lower
  quality, hardware plays natively)
2026-02-28 07:48:03 -06:00
vicrodh 33e80b0836 fix: lower default visualizer FPS from 60 to 15 for better performance 2026-02-28 07:48:03 -06:00
vicrodh 6c8354f825 feat: add booklet download button, remove pdfjs-dist dependency
- Add save/download button to booklet toolbar with native file dialog
- Add v2_booklet_save command to copy PDF to user-chosen location
- Remove pdfjs-dist npm package (replaced by MuPDF backend)
- Delete static/pdfjs/ files (~1.9MB: worker, WASM decoders, fallbacks)
- Add bookletDownload i18n keys to all 4 locales
2026-02-28 07:48:03 -06:00
vicrodh bb723c7ca5 feat: switch booklet viewer from pdfjs-dist to MuPDF backend
Replace browser-side pdfjs-dist canvas rendering with server-side
MuPDF rendering for high-quality PDF booklet display. MuPDF renders
pages to PNG at configurable DPI in Rust, producing much sharper
results than the JavaScript canvas approach which had persistent
JPEG 2000 WASM decoder issues.

- Add mupdf crate with sys-lib features (avoid GTK symbol conflicts)
- Create pdf_viewer.rs with v2_booklet_open/render_page/close commands
- Download PDF to temp file, render pages on demand via MuPDF
- Display rendered PNGs as img tags instead of canvas
- DPI scales with zoom and devicePixelRatio for crisp output
- Support rotation via MuPDF transformation matrix
2026-02-28 07:48:03 -06:00
vicrodh e78dc626ff fix: serve pdfjs worker from static/ for proper WASM resolution
The worker was loaded via Vite's blob: URL transform, which
prevented it from resolving sibling WASM files (openjpeg.wasm,
jbig2.wasm). Now the worker is served from static/pdfjs/ as a
real HTTP resource alongside the WASM files, so JPEG 2000 and
JBIG2 decoders initialize correctly.

Removed disableWorker hack — worker runs properly now.
2026-02-28 07:48:03 -06:00
vicrodh 93f4b1bf68 fix: auto-fit booklet page to viewer width on load
PDF was rendering tiny because zoom=1 means 1 PDF point = 1 CSS
pixel. Now calculates zoom to fill the container width on load.
Fit-to-width button does the same recalculation.
2026-02-28 07:48:03 -06:00
vicrodh 7d981ee641 fix: restore workerSrc — pdfjs v5 requires it at init time
pdfjs v5 checks GlobalWorkerOptions.workerSrc before reading
document options like disableWorker. Set it to satisfy the
init check even though the worker is disabled per-document.
2026-02-28 07:48:03 -06:00
vicrodh c438b8896c fix: disable pdfjs worker for reliable WASM image decoding
The web worker runs in a blob: URL context where /pdfjs/ WASM
paths don't resolve, causing JPEG 2000 images to decode at
degraded quality. Disable worker so all decoding runs on the
main thread where static assets are reachable.

Also bumped render scale from 2x to 3x for near-print quality.
2026-02-28 07:48:03 -06:00
vicrodh 8757108e00 fix: first page black screen and low quality rendering
First page was black because renderPage() ran while isLoading was
still true, so the canvas wasn't in the DOM yet. Fixed by setting
isLoading=false and awaiting tick() before rendering.

Increased render quality from 1.5x to 2x DPR multiplier for
sharper booklet pages.
2026-02-28 07:48:03 -06:00
vicrodh 4bafaac1cd fix: provide pdfjs WASM files for JPEG 2000 decoding in booklets
PDF booklets use JPEG 2000 images which require OpenJPEG WASM.
Copy pdfjs-dist WASM files to static/pdfjs/ so they are served
at a known URL, and set wasmUrl parameter on getDocument().
2026-02-28 07:48:03 -06:00
vicrodh 883f3546c5 fix: fetch booklet PDF through Tauri backend to bypass CORS
PDF loading was failing because pdfjs-dist cannot fetch from
static.qobuz.com due to CORS restrictions in the WebView.

Added v2_fetch_url_bytes command that fetches URL bytes through
Rust's reqwest (no CORS), then passes the binary data to pdfjs.

Also added rotation support (R key or toolbar button) for
vertical booklets.
2026-02-28 07:48:03 -06:00
vicrodh 7e34232823 feat: add booklet button and viewer to AlbumDetailView
Show a BookOpen icon button in album header when the album has
PDF goodies from Qobuz. Opens the BookletViewer modal to display
the booklet inline.
2026-02-28 07:48:03 -06:00
vicrodh f561e12850 feat: add booklet viewer i18n keys to all 4 locales 2026-02-28 07:48:03 -06:00
vicrodh 025aba7c1a feat: add BookletViewer component with pdfjs-dist
Modal PDF viewer for album booklets with page navigation,
zoom controls, and keyboard shortcuts. Uses pdfjs-dist to
render PDF pages from URL in canvas.
2026-02-28 07:48:03 -06:00
vicrodh cb86f402e6 feat: add goodies/booklet types to frontend and adapter
Add QobuzGoody interface, goodies field to QobuzAlbum and AlbumDetail,
and pass goodies through in convertQobuzAlbum adapter.
2026-02-28 07:48:02 -06:00
vicrodh dec2278624 feat: per-panel FPS settings for all immersive animations
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)
2026-02-28 07:48:02 -06:00
vicrodh 8c179ecf00 feat: configurable ambient background FPS in settings
Add setting for immersive background animation frame rate:
- Disabled, 15 FPS (default), 30 FPS, 60 FPS, 120 FPS
- Persisted via userStorage, read at canvas init
- Disabled renders a single static frame
- Setting placed in Appearance section after immersive default view
- i18n keys added to all 4 locales (en, es, de, fr)

Visualizer panels reverted to 30fps (unrelated to ambient bg).
2026-02-28 07:48:02 -06:00
vicrodh fae4d912ab feat: bump all visualizer panels from 30fps to 60fps
Updated frame throttle in all immersive panels:
- LissajousPanel (X/Y)
- VisualizerPanel (spectrum)
- OscilloscopePanel
- EnergyBandsPanel
- TransientPulsePanel
- AlbumReactivePanel

SpectralRibbon was already at 60fps.
2026-02-28 07:48:02 -06:00
vicrodh 1ba4818228 feat: bump immersive visualizer to 60fps
Was hardcoded at 15fps (not the 30fps from config). Updated both
the canvas frame throttle and the default config to 60fps.
2026-02-28 07:48:02 -06:00
vicrodh d21a8a3a49 Merge branch 'feature/miniplayer' into pre-release 2026-02-26 02:45:28 -06:00
vicrodh 630a8c16c5 refactor(i18n): replace get(t) with direct $t() calls in 6 components
Remove unnecessary get() wrapper from svelte/store when calling the
i18n translate function. Direct $t() works correctly in regular
functions (only $derived is problematic per ADR-001). Removes unused
get import from GenreFilterButton, FavoritesView, PlaylistDetailView,
PurchaseAlbumDetailView, PurchasesView, and SettingsView.
2026-02-26 02:44:17 -06:00
vicrodh 39a35e5467 fix(player): reduce now playing song card width for compact layouts
cx
2026-02-26 02:35:35 -06:00
vicrodh 67a159e270 fix(miniplayer): center micro window controls overlay
cx
2026-02-26 02:34:42 -06:00
vicrodh d6852500bf fix(miniplayer): refine queue artwork overlays and inline controls
cx
2026-02-26 02:22:52 -06:00
vicrodh e0cfc5d92b feat(miniplayer): add default surface setting and queue row play controls
cx
2026-02-26 02:14:09 -06:00
vicrodh 339f2b8106 fix(miniplayer): restore next/prev playback and remanent shuffle
cx
2026-02-26 02:03:15 -06:00
vicrodh 9302ffe5e6 fix(miniplayer): sync window theme with global app settings
cx
2026-02-26 01:58:37 -06:00
vicrodh 1ba6fe13d5 refactor(miniplayer): tighten compact spacing and simplify play button
cx
2026-02-26 01:54:11 -06:00
vicrodh d6b0e0f5ea feat(miniplayer): refine micro surface layout and controls
cx
2026-02-26 01:45:59 -06:00
vicrodh 99c8ae1ed3 feat(miniplayer): rebuild surfaces window with compact overlay polish
cx
2026-02-26 00:54:13 -06:00
vicrodh e8979e88c0 feat(artist): add open in browser and save as to artist image context menu
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.
2026-02-25 20:27:24 -06:00
vicrodh af83cec692 feat(ui): subtle rounded corners and shadow when window is floating
Detect maximized state via Tauri window API. When the window is not
maximized (floating), apply 8px border-radius and a soft box-shadow.
Body/html backgrounds set to transparent so corners show through
the Tauri transparent window.
2026-02-25 20:20:59 -06:00
vicrodh 08c92b0bc2 fix(titlebar): match background color with sidebar across all themes
Replace hardcoded gradient with var(--bg-secondary) so the titlebar
background matches the sidebar in every theme.
2026-02-25 20:15:00 -06:00
vicrodh 6c3f7e8d40 fix(favorites): heart button works in tracks tab, animate out on unfavorite
Remove isFavoriteOverride={true} that forced all hearts filled,
preventing the toggle from having any visible effect. Subscribe to
favoritesStore so unfavorited tracks animate out (slide+fade) and
are removed from the list after 300ms without requiring a refresh.
2026-02-25 20:11:31 -06:00