Commit Graph
100 Commits
Author SHA1 Message Date
vicrodh 8480ad13da fix(blacklist): correct import path for artistBlacklistStore 2026-02-02 14:20:54 -06:00
vicrodh 6ff6269f74 feat(blacklist): add grayout styling for blacklisted artist tracks
- TrackRow: Add isBlacklisted prop with grayout styling and ban icon
- VirtualizedTrackList: Check blacklist status for each track
- PlaylistDetailView: Gray out and restrict actions for blacklisted tracks
- AlbumDetailView: Apply album-level blacklist check to all tracks
- HomeView: Gray out blacklisted tracks in Continue Listening section
- ArtistDetailView: Banner shown when viewing blacklisted artist page

Blacklisted tracks show reduced opacity, ban icon instead of track
number, and restricted menu actions (navigation only, no playback).
2026-02-02 14:17:01 -06:00
vicrodh bdffe6c709 feat(blacklist): add toast notifications for blacklist operations
- Show success/error toasts when adding/removing artists
- Show feedback when enabling/disabling blacklist
- Show confirmation toast when clearing all artists
2026-02-02 14:06:10 -06:00
vicrodh 6257f76484 fix(blacklist): use correct handleArtistClick function in BlacklistManagerView 2026-02-02 14:02:23 -06:00
vicrodh 958f0d7dca fix(blacklist): use callback prop for navigation to blacklist manager
- Replace direct navigateTo import with onBlacklistManagerClick callback
- Pass navigation callback from +page.svelte to SettingsView
- Follows same pattern as other view navigation (playlist-manager, etc)
2026-02-02 13:51:21 -06:00
vicrodh f95348bb1e feat(blacklist): add Blacklist Manager View
- Create BlacklistManagerView component with list of blocked artists
- Add enable/disable toggle, search filter, and clear all function
- Integrate navigation to blacklist manager from Settings
- Add Content Filtering section in Settings with artist count
- Add i18n translations for English and Spanish
- Wire up view routing in +page.svelte
2026-02-02 13:46:27 -06:00
vicrodh c18dfe641d fix(blacklist): filter albums and iterate most_popular for first valid item
- most_popular now iterates through all items to find first non-blacklisted
- Added album filtering to search_all by album.artist.id
- Added BlacklistState to search_albums with filtering
- Fixes edge case where most_popular was empty when first item was blacklisted
2026-02-02 13:26:23 -06:00
vicrodh e1849f0bb3 feat(blacklist): add frontend store and artist page UI
Phase 3 (minimal) - UI for testing:
- Add artistBlacklistStore.ts with O(1) local checks
- Initialize store on app startup
- Add blacklist toggle button to ArtistDetailView
- Button shows red when artist is blacklisted

cc
2026-02-02 13:18:38 -06:00
vicrodh bd4c9b7792 feat(blacklist): filter playlist suggestions by artist
Phase 2 (part 2) - Suggestions integration:
- Filter SuggestedTrack results by artist_id
- Log filtered count for debugging

cc
2026-02-02 13:10:36 -06:00
vicrodh bec6907dbe feat(blacklist): integrate filtering into search and radio commands
Phase 2 (part 1) - Read-only integration:
- Filter blacklisted artists from search_tracks, search_artists, search_all
- Filter blacklisted artists from get_similar_artists
- Filter most_popular item if blacklisted
- Add blacklist filtering to all radio commands (artist/track/infinite/refill)
- Use output-layer filtering for radio (engine internals unchanged)

cc
2026-02-02 13:09:35 -06:00
vicrodh 26c5a1b0a3 feat(blacklist): add artist blacklist module with O(1) lookups
Phase 1 of artist blacklist implementation:
- Add artist_blacklist module with HashSet-based service for O(1) lookups
- Add SQLite persistence with WAL mode
- Add global enable/disable feature flag
- Add Tauri commands for CRUD operations
- Register BlacklistState in app state

cc
2026-02-02 13:05:50 -06:00
vicrodh a742c22c8f fix(prefetch): limit concurrent prefetches to prevent memory corruption
Add semaphore to limit concurrent prefetch operations to 1 at a time.
This helps prevent potential race conditions in native audio libraries
(CPAL/PipeWire/ALSA) that can cause malloc corruption.

Also:
- Reduce QOBUZ_PREFETCH_COUNT from 3 to 2
- Reduce PREFETCH_LOOKAHEAD from 15 to 10
- Add 50ms delay before cache insertion

Addresses #38 - random crashes during prefetch
2026-02-02 12:20:10 -06:00
vicrodh aa04a26b6c fix(offline): use hybrid strategy to reduce false positives
Implement 9/10 check strategy:
- 9 out of 10 connectivity checks go to 1.1.1.1 (Cloudflare DNS)
- 1 out of 10 checks go to Qobuz to verify service availability

This prevents false offline detections when the app is making many
concurrent API calls (e.g., loading genre hierarchies), which could
trigger Qobuz rate limiting and cause timeout-based false positives.

Also reduced timeout from 15s to 10s and retry wait from 2s to 1s
since 1.1.1.1 responds much faster than Qobuz.
2026-02-02 11:29:26 -06:00
vicrodh e86ceb827c fix(genre-filter): hide carets for genres without children
Only show expand carets in the advanced view for genres that actually
have children loaded, or where children haven't been loaded yet.
Applies to all levels (parent, child, grandchild).
2026-02-02 11:14:03 -06:00
vicrodh 1dcf2ac29a feat(genre): eager load all 3 levels on first load for complete search 2026-02-02 11:10:12 -06:00
vicrodh e599a7b803 feat(genre): add 30-day localStorage cache for genres 2026-02-02 11:06:32 -06:00
vicrodh 586dfb5754 fix(genre): stop propagation on expand buttons to prevent dropdown close 2026-02-02 10:59:15 -06:00
vicrodh df85bc9fba fix(genre): single notification when loading descendants
- Add silent parameter to loadChildren to skip notification
- loadAllDescendants now loads children and grandchildren in parallel
- Only one notification after all loads complete (no triple reload)
2026-02-02 10:56:26 -06:00
vicrodh 023d88cce1 fix(genre): lazy loading, positioning, and eager descendant loading
- Lazy load children only when tree node is expanded (faster startup)
- Fix popup positioning to grow left when toggling advanced view
- Eagerly load all descendants when selecting a parent for filtering
- This ensures basic mode also filters by Genre > Children > Grandchildren
- Show loading spinner while fetching children
2026-02-02 10:53:29 -06:00
vicrodh 62e8ba7892 feat(genre): 3-level hierarchy with collapsible tree and search
Store changes:
- Load all 3 levels of genres (parent → child → grandchild)
- Add genreTree for hierarchical structure
- getAllDescendantIds() for cascade selection
- setGenresSelected() for batch operations

Popup changes:
- Advanced view with collapsible tree structure
- Search box filters and auto-expands matching nodes
- Click parent checkbox selects/deselects all descendants
- Partial selection shows indeterminate state
- Expanded size: 630px wide, 700px tall
- Descendant count badges on expandable nodes
2026-02-02 10:50:02 -06:00
vicrodh 3155627669 feat(genre): hierarchical view for granular genre selection
- Parent genres shown as headers with checkbox
- Children displayed in grid below each parent
- Clicking parent toggles all its children
- Partial selection shows indeterminate state on parent
- Popup expands 150px taller when in advanced view
2026-02-02 10:37:48 -06:00
vicrodh 589e6071c4 feat(genre): show parent genres by default with sub-genre toggle
- Default view shows only parent genres (less cluttered like Qobuz)
- When selecting a parent, filtering includes all its sub-genres
- Added "Show all sub-genres" toggle for granular control
- Store now tracks parent/child relationships for proper filtering
- getFilterGenreNames() returns selected + children for filtering
2026-02-02 10:26:21 -06:00
vicrodh d2544dceb9 fix(genre): load all genres including sub-genres
Fetch parent genres first, then fetch sub-genres for each parent in
parallel. Combine into a flat list sorted alphabetically. This fixes
the issue where albums with sub-genres (like Alternative & Indie,
Opera, Concertos) were filtered out even when all genres were selected.
2026-02-02 10:18:57 -06:00
vicrodh df7a7e2cb5 feat(favorites): add playlist overlay with action buttons
Add FavoritePlaylistCard component with hover overlay containing:
- Heart button (remove from favorites)
- Play button (start playlist playback)
- More menu (play next, add to queue, share, remove)

Card click navigates to playlist page. Updates FavoritesView to use
the new component with all action handlers wired through +page.svelte.
2026-02-02 10:15:02 -06:00
vicrodh befe36ce30 style(ui): genre button variants and dropdown alignment
- Add 'control' variant for Favorites style matching other dropdowns
- Add 'align' prop: 'left' (Home) or 'right' (Favorites)
- FavoritesView uses control variant with right alignment
- Control variant shows "Genre: X" with chevron icon
2026-02-02 10:03:55 -06:00
vicrodh 2c886fae7a feat(genre): separate persistence for Home and Favorites contexts
- Refactor genreFilterStore to support multiple contexts
- Each context (home, favorites) has independent selection state
- Separate localStorage keys: qbz_genre_filter_home, qbz_genre_filter_favorites
- GenreFilterButton/Popup accept context prop
- FavoritesView uses 'favorites' context
2026-02-02 09:55:08 -06:00
vicrodh 65b42295e9 feat(favorites): add genre filter to Albums tab
- Add GenreFilterButton to Favorites > Albums toolbar
- Filter albums by genre name (multi-select supported)
- Use genreFilterVersion for reactivity tracking

Note: Artists tab doesn't have genre filter since artist
objects don't contain genre info directly.
2026-02-02 09:49:06 -06:00
vicrodh 6e4f48ff2e style(ui): clear button always visible, popup 530px wide 2026-02-02 09:43:00 -06:00
vicrodh 8dc1efec5d feat(genre): support multi-select genre filter
- Allow multiple genre selection (checkbox behavior)
- Add getSelectedGenreNames() for multiple name retrieval
- Merge results from multiple API calls for featured albums
- Clear button now closes the popup
- Button shows "N genres" when multiple selected
2026-02-02 09:37:43 -06:00
vicrodh 22ba76a8d1 feat(home): filter user sections by genre
- Add getSelectedGenreName() to genre filter store
- Add filterAlbumsByGenre() helper in HomeView
- Apply genre filter to Recently Played and Favorite Albums
- Fetch 3x more items when filtering to ensure enough results
2026-02-02 09:29:55 -06:00
vicrodh eee364e29c style(ui): genre popup extends left, 480px wide, 3 columns 2026-02-02 09:26:17 -06:00
vicrodh e61891bfc2 style(ui): widen genre filter popup to 420px with 4 columns 2026-02-02 09:25:04 -06:00
vicrodh 1b68b85402 style(ui): make genre filter cards smaller and more neutral
- Reduce card height from aspect-ratio to fixed 36px
- Use neutral bg-tertiary background instead of colored gradients
- Horizontal layout with name and check circle
- Selected state uses accent-primary
- Better spacing to prevent overlap
2026-02-02 09:24:37 -06:00
vicrodh d37b4ef7ff fix(ui): prevent infinite loop in GenreFilterButton
Use onMount instead of $effect to load genres once.
Add initialized flag to prevent onFilterChange from firing
during initial subscription setup, which was causing
loadHome -> re-render -> loadGenres -> notify -> loadHome loop.
2026-02-02 09:11:18 -06:00
vicrodh 733a12d702 fix(reco): fix migration for existing users without genre_id column
Remove genre_id from base schema and let migration add it.
This ensures existing databases don't fail when trying to create
index on non-existent column during initial schema execution.
2026-02-02 09:07:47 -06:00
vicrodh 8428fafaa5 feat(home): integrate genre filter into HomeView
- Add GenreFilterButton to home header
- Pass genre_id to fetchFeaturedAlbums API calls
- Reload home content when genre filter changes
- Featured sections now filter by selected genre
2026-02-02 08:53:13 -06:00
vicrodh 0d0da7c5fc feat(ui): add genre filter store and components
- Add genreFilterStore.ts with localStorage persistence
- Add GenreFilterButton.svelte for filter trigger
- Add GenreFilterPopup.svelte with colored genre cards
- Support "Remember my selection" toggle
- Single-genre selection (API limitation)
2026-02-02 08:51:37 -06:00
vicrodh d0dd42db65 feat(reco): add genre backfill commands
- Add reco_backfill_genres command to populate genre_id for existing events
- Add reco_needs_genre_backfill command to check if backfill is needed
- Fetches album details from Qobuz API and updates events in batches
- Includes rate limiting to avoid API throttling
2026-02-02 08:49:02 -06:00
vicrodh b4d7f7f4c3 feat(reco): add genre_id to reco_store schema
- Add genre_id column to reco_events table
- Add migration to add column to existing databases
- Update RecoEventInput and RecoEventRecord structs
- Update insert_event and get_events_since to handle genre_id
- Add get_album_ids_without_genre() for backfill
- Add update_genre_for_album() for backfill
2026-02-02 08:47:43 -06:00
vicrodh c2d61b46dc feat(api): add genre_id filter to get_featured_albums
- Add optional genre_id parameter to client method
- Add optional genre_id parameter to Tauri command
- Qobuz API filters featured albums by genre when provided
2026-02-02 08:45:44 -06:00
vicrodh 8e31717908 feat(api): add genre list endpoint
- Add GENRE_LIST endpoint constant
- Add GenreInfo model with id, name, color, slug, path
- Add get_genres() method to client
- Add get_genres Tauri command
2026-02-02 08:44:42 -06:00
vicrodh b6c723d3df fix(search): use 4-quadrant collage for playlist cards 2026-02-02 08:11:48 -06:00
vicrodh de0b820230 fix(search): use standard menu styles for playlist card dropdown 2026-02-02 08:09:17 -06:00
vicrodh d27ae32aee feat(search): wire up playlist handlers in main page
- Add playPlaylistById to set queue and play first track
- Add queuePlaylistNextById to add playlist tracks next
- Add queuePlaylistLaterById to add playlist tracks later
- Add copyPlaylistToLibraryById using subscribe_playlist
- Add sharePlaylistQobuzLinkById to copy link to clipboard
- Connect all handlers to SearchView component
2026-02-02 08:06:27 -06:00
vicrodh 61dd071326 feat(search): integrate playlists into search page
- Add playlists to unified search results (SearchAllResults)
- Add dedicated Playlists tab in jump-nav
- Create SearchPlaylistCard with album-like overlay (heart/play/menu)
- Add playlist menu: Play next, Add to queue, Copy to library, Share
- Add offset parameter to search_playlists command
- Add PlaylistGenre and extra fields to Playlist model
- Update searchState with Playlist type and playlistResults

UI changes:
- Remove glyphs from search tabs
- Reduce tab spacing (gap 8px→4px, padding 16px→12px)
- Reduce filter font size (13px→12px)
2026-02-02 07:57:16 -06:00
vicrodh f6d2a6bddd feat(immersive): default to coverflow view when opening immersive mode 2026-02-01 19:43:55 -06:00
vicrodh 2431a66545 refactor(settings): move limit quality toggle below streaming quality
- Repositioned toggle to appear right after Streaming Quality dropdown
- Updated description to clarify it overrides quality setting when device
  doesn't support the selected sample rate
2026-02-01 19:40:19 -06:00
vicrodh 39e8814848 feat(settings): add limit quality to device toggle and max sample rate tracking
Frontend changes for quality limiting feature:
- Add limitQualityToDevice state and handler
- Add AudioSettings interface fields for limit_quality_to_device and device_max_sample_rate
- Update handleBackendDeviceChange to send max_sample_rate when device changes
- Update handleOutputDeviceChange to find max_sample_rate from backend devices
- Add Settings UI toggle for "Limit Quality to Device"

This completes the quality limiting feature that prevents requesting tracks
that exceed the device's sample rate capabilities, ensuring bit-perfect playback.
2026-02-01 19:29:15 -06:00
vicrodh 6d53907233 feat(audio): add quality limiting based on device capabilities
- Add limit_quality_to_device setting (enabled by default)
- Add device_max_sample_rate field to store selected device's max rate
- Implement limit_quality_for_device() to restrict quality based on device
- Apply quality limiting in play_track and prefetch_track commands
- Add set_audio_device_max_sample_rate Tauri command

This ensures bit-perfect playback by not requesting tracks that
exceed the device's sample rate capabilities. When enabled:
- Devices <= 48kHz: limited to Lossless (44.1kHz)
- Devices <= 96kHz: limited to HiRes (up to 96kHz)
- Devices > 96kHz: allow UltraHiRes (up to 192kHz)
2026-02-01 19:21:02 -06:00
vicrodh be4786e6d3 feat(settings): add limit_quality_to_device setting
Add new audio setting to limit streaming quality based on device
capabilities. When enabled (default: true), QBZ will avoid requesting
tracks that exceed the device's max supported sample rate.

This ensures bit-perfect playback by preventing the need to resample
high-res tracks on devices that don't support them.

- Add limit_quality_to_device field to AudioSettings
- Add database migration for new column
- Add Tauri command for frontend integration

cc
2026-02-01 19:15:11 -06:00
vicrodh da5ec2abcb feat(audio): implement controlled hw→plughw fallback for ALSA Direct
- Add AlsaDirectError enum for structured error classification
- Add BitPerfectMode enum to track runtime bit-perfect status
- Implement automatic fallback: hw fails with format error → try plughw
- Abort on non-recoverable errors (busy, permissions, invalid params)
- Respect ALSA plugin selection from UI settings
- Log runtime mode for debugging

This allows SMSL-class DACs that fail on hw: to work via plughw:
while maintaining bit-perfect guarantees (format conversion only,
no resampling).

cc
2026-02-01 19:13:11 -06:00
vicrodh 4670b1548a feat(audio): add S24_3LE format support for SMSL-class USB DACs
- Add S24_3LE (S243LE) to format priority list as first choice
- Implement S24_3LE write handler with 3-byte packed conversion
- Add S24LE write handler (was missing explicit implementation)
- Reorder format priority: S24_3LE > S32LE > S24LE > S16LE > FloatLE

This fixes bit-perfect playback on USB DACs that only support
the S24_3LE format (SMSL, Topping, Fosi with TAS1020B chip).

Closes #30

cc
2026-02-01 19:07:17 -06:00
vicrodh bb4fcfd6b8 Optimize visualizer: 16 bins mirrored, Material You colors
- Reduce FFT to 30fps with 16 bins (mirrored to 32 visual bars)
- Use 1024 FFT size for faster processing
- Extract dominant colors from album artwork
- Center artwork with track info and quality badge
- Bars render from edges inward with color gradient
2026-02-01 18:44:52 -06:00
vicrodh 4b74e1c490 Update visualizer track info: larger art, artist/album/track order
- Increase artwork to 108px (20% larger)
- Reorder info: Artist (bold) > Album (italic) > Track
- Add album prop to VisualizerPanel
- Adjust text styling hierarchy
2026-02-01 15:42:43 -06:00
vicrodh a46936da53 Move track info to bottom-right, text aligned left of artwork
- Position track info at bottom-right with margin
- Reduce artwork to 90x90px
- Text aligned right, to the left of artwork
- Compact layout that doesn't obstruct visualization
2026-02-01 15:40:48 -06:00
vicrodh 55c3e80bbc Switch to Canvas 2D wave visualizer for lower CPU usage
- Replace WebGL2 instanced bars with simple Canvas 2D waves
- Mirror wave from center (top + bottom)
- Quadratic bezier curves for smooth lines
- Gradient coloring: cyan -> blue -> purple
- Black #000000 background
- Much lighter than 128 WebGL instances
2026-02-01 15:38:44 -06:00
vicrodh 283d17b5b8 Use black background for visualizer view (skip ImmersiveBackground entirely) 2026-02-01 15:36:35 -06:00
vicrodh 587e204c7b Disable ambient background animation in visualizer view
Reduces CPU usage by disabling the WebGL ambient motion when
the visualizer tab is active - both are GPU intensive.
2026-02-01 15:35:31 -06:00
vicrodh 337d4866d6 Add Visualizer tab to Focus mode with mirror visualization
- Add 'visualizer' to FocusTab type
- Add Visualizer tab with Activity icon in ImmersiveHeader
- Update VisualizerPanel with mirror mode (bars up and down from center)
- Integrate VisualizerPanel in ImmersivePlayer focus view
- Three-color gradient: teal -> purple -> pink
- Track info overlay with artwork, title, artist
2026-02-01 15:33:47 -06:00
vicrodh 06d3de0d73 Add WebGL2 VisualizerPanel frontend component
- Instanced bar rendering with vertex/fragment shaders
- Receives binary frequency data via Tauri events
- R32F texture for efficient frequency data upload
- Cyan-to-magenta gradient coloring
- Auto-enables/disables backend when mounted/unmounted
- 64 frequency bars with logarithmic mapping from backend
2026-02-01 15:11:40 -06:00
vicrodh 7bc429744b Integrate visualizer with audio thread
- Add VisualizerTap to Player for sample capture
- Create wrap_source helper in audio thread
- Wrap all audio sources with TappedSource at 4 append points
- Player now captures samples for visualization when enabled
- Zero overhead when disabled (fast atomic check)

Audio samples flow: Source -> TappedSource -> RingBuffer -> FFT Thread -> Frontend
2026-02-01 15:10:51 -06:00
vicrodh b8cfcfb34a Add audio visualizer backend infrastructure
- Add spectrum-analyzer crate for FFT processing
- Create visualizer module with:
  - RingBuffer: lockless SPSC buffer for audio samples
  - FFT processor: dedicated thread for spectrum analysis
  - TappedSource: rodio Source wrapper for sample capture
  - VisualizerTap: shared state for audio thread integration
- Add Tauri commands: set_visualizer_enabled, is_visualizer_enabled
- Register visualizer in AppState and lib.rs

Backend compiles. Next: integrate with audio thread to capture samples.
2026-02-01 15:07:21 -06:00
vicrodh 5a93ac509d Fix volume spam: deduplicate set_volume calls in backend
- Skip setting volume if already at same value (0.001 tolerance)
- Skip logging when volume unchanged
- Prevents MPRIS/PipeWire feedback loop spam
2026-02-01 14:33:56 -06:00
vicrodh 43444951ba Hypersensitive sleeve parallax: ±120px H, ±35px V with power curve
- Doubled horizontal range to ±120px
- Doubled vertical range to ±35px
- Added power curve (0.7) for hypersensitivity near center
- Small mouse movements now produce dramatic response
2026-02-01 14:27:57 -06:00
vicrodh 33dc0ccafb Increase sleeve parallax range: 120px horizontal, 35px vertical 2026-02-01 14:26:16 -06:00
vicrodh 1a4fa3cc78 Remove all parallax from vinyl - only sleeve moves
- Vinyl stays perfectly centered, only rotates
- Removed X and Y parallax from vinyl position
- Removed parallax uniform from shader
- Only the cover art (sleeve) responds to mouse movement
2026-02-01 14:24:36 -06:00
vicrodh 2fe0efc3d4 Fix vinyl rotation: 33⅓ RPM, concentric, no Y-axis movement
- Set rotation speed to 33.333 RPM (0.0582 rad/frame at 60fps)
- Remove Y-axis parallax from vinyl position (stays centered)
- Remove Y parallax from shader for perfectly concentric rotation
- Add mipmaps and trilinear filtering for anti-aliasing
- Enable anisotropic filtering when available
2026-02-01 14:22:56 -06:00
vicrodh 81232457d1 Fix parallax values to prevent artifacts 2026-02-01 14:15:37 -06:00
vicrodh 68b863ea64 Extreme horizontal parallax and use PNG texture
- Use PNG instead of SVG for better WebGL compatibility
- Extreme horizontal parallax: cover 200px, vinyl 120px
- Reduced vertical parallax for subtle depth
- Shader parallax: 0.15 horizontal, 0.05 vertical
- Disc appears to slide out of sleeve with minimal mouse movement
2026-02-01 14:14:02 -06:00
vicrodh fc85d66f5c Fix VinylPanel: add padding and aggressive parallax
- Add padding around vinyl (scale UV by 1.15x)
- Discard pixels outside bounds to avoid edge artifacts
- Much more aggressive parallax: cover 80/50px, vinyl 50/35px
- Shader parallax offset increased to 0.08
- Vinyl size reduced to 85% for more breathing room
2026-02-01 14:10:55 -06:00
vicrodh 6265e685ea Update logo and regenerate all app icons
- Updated qbz-logo.svg and qbz-logo.png
- Added qbz-vinyl-v3.svg for VinylPanel
- Regenerated all Tauri icons from new logo
- Updated Android icons and favicon
2026-02-01 14:09:57 -06:00
vicrodh b068426e9f Rewrite VinylPanel with WebGL2 for performance
- Use WebGL2 canvas for vinyl rendering instead of CSS
- Load qbz-vinyl-v3.svg as texture
- Shader-based rotation when playing
- Parallax effect via uniforms (no DOM updates)
- Album cover slides left to reveal vinyl
- Much lower CPU usage than CSS animation
2026-02-01 14:08:57 -06:00
vicrodh 20b9aa2c86 Improve VinylPanel parallax sensitivity and alignment
- Double parallax sensitivity (30/20px cover, 18/12px disc)
- Cover and disc now vertically centered together
- Cover slides 15% left when playing to reveal disc
- Simplified transform calculations
2026-02-01 13:55:05 -06:00
vicrodh fcb81dad2b Fix VinylPanel layout and styling to match Figma design
- Correct z-ordering: cover in front (z-10), disc behind
- Cover slides left when playing to reveal disc
- Add mouse parallax effect for depth
- More visible grooves with alternating brightness
- Neutral gray colors instead of amber vintage
- Use system typography instead of Oswald
- Radial gradient on disc for realistic shading
2026-02-01 13:52:26 -06:00
vicrodh d79fdf58b6 Add Vinyl panel to Focus mode and fix MPRIS volume feedback loop
- Add VinylPanel.svelte with spinning vinyl disc animation
- Album cover slides left when playing to reveal disc
- CSS-based grooves (40 rings) and amber center label
- Vintage typography with "33 1/3 RPM" badge
- Add 'vinyl' as new Focus tab option in ImmersiveHeader
- Fix MPRIS/PipeWire volume feedback loop by checking if volume
  actually changed before calling setVolume
2026-02-01 13:51:19 -06:00
vicrodh 319150920b Enhance immersive ambient animation vibrancy and speed
- Increase animation speed to 1.8x for more dynamic movement
- Add per-channel histogram normalization to expand color range
- Boost saturation to 2.5x and brightness to 0.85
- Add 12% red channel boost for warmer tones
- Target color range 30-240 to lift shadows and preserve highlights
2026-02-01 13:42:41 -06:00
vicrodh d4cfd14fc6 fix(immersive): prevent black from dominating in B&W artwork
Problem: brightness(0.5) made whites gray while blacks stayed black,
causing dark areas to dominate even in mostly-white artwork.

Solution:
- Increased brightness from 0.5 to 0.7
- Added shadow lift: maps 0-255 to 25-255
- Black now becomes dark gray (~10% brightness floor)
- Whites remain visible and balanced

Result: 'Panda/cow' effect - both light and dark areas visible.
2026-02-01 13:32:52 -06:00
vicrodh edca4d0bec feat(immersive): dramatically increase ambient animation
New effects added:
- Layer rotation: each layer rotates slowly (1-2° range)
- Scale breathing: zoom levels pulse in/out per layer
- Pulsing vignette: edge darkening varies over time
- Increased drift amplitudes: far ±0.20, mid ±0.14, near ±0.08
- Stronger brightness breathing: ±12% (was ±8%)

All effects are decorrelated (different speeds/phases) for organic feel.
Default intensity now 1.0 for maximum visibility.
2026-02-01 13:28:48 -06:00
vicrodh cec7a799d5 feat(immersive): increase ambient animation visibility
Shader changes:
- Removed 0.5x time multiplier (was making motion too slow)
- Increased drift amplitudes ~3x (far: 0.15, mid: 0.10, near: 0.06)
- Faster drift speeds (~12-17 sec cycles instead of ~40-80 sec)
- Breathing effect doubled (0.08 vs 0.04)

Config changes:
- Default ambientIntensity: 0.8 (was 0.5)

Purpose: Keep pixels active (OLED), make motion perceptible
while maintaining ambient character (no sudden movements).
2026-02-01 13:25:47 -06:00
vicrodh 8bb38ccdda perf(lyrics): remove karaoke animation, plain white text only
Even GPU-accelerated animation was too expensive.
Immersive mode now uses simple bright white text with no animation.
2026-02-01 13:15:39 -06:00
vicrodh dbd706fc64 perf(lyrics): GPU-accelerated underline animation
Replace expensive properties with GPU-accelerated ones:
- transform: scaleX() instead of width (no layout recalc)
- Solid color instead of gradient
- Removed box-shadow blur (very expensive)
- Added will-change: transform hint

This runs entirely on GPU compositor thread.
2026-02-01 13:14:43 -06:00
vicrodh 06469aeaa3 fix(lyrics): use QBZ blue underline for karaoke effect
Revert to glowing underline approach with QBZ brand blue (#5281ff):
- Text stays bright white (no background-clip: text)
- Underline sweeps from left to right during line duration
- Blue glow effect with box-shadow
- Much better readability than gradient text
2026-02-01 13:13:36 -06:00
vicrodh fc382a2237 feat(lyrics): metallic shimmer effect for karaoke
Replace underline with metallic shimmer that sweeps across text:
- Mostly white (40% each side) with purple highlight band in center
- Gradient moves from right to left during line duration
- Uses ease-in-out for natural feel
- GPU-accelerated, single element, no JS polling
2026-02-01 13:11:01 -06:00
vicrodh c1a7a5977a fix(lyrics): use glowing underline instead of gradient text for karaoke
The background-clip: text technique made white appear gray.
New approach uses a pseudo-element underline that:
- Sweeps from left to right with the line duration
- Has purple glow effect (#a78bfa)
- Keeps text bright white and readable
- No impact on text color/brightness
2026-02-01 13:09:54 -06:00
vicrodh db2419fcb0 feat(lyrics): CSS-only karaoke animation for immersive mode
Replace JS-polled karaoke with pure CSS animation:
- Calculate line duration from timing data (timeMs)
- Use CSS animation with background-position sweep
- Animation runs once per line change, no polling needed
- Uses purple (#c4b5fd) to white gradient for visibility

Data flow: Pass full LyricsLine objects (with timeMs) through
component chain instead of stripping timing data.
2026-02-01 13:08:43 -06:00
vicrodh 562f73f350 perf(lyrics): reduce CPU usage in immersive mode by ~90%
Problem: Lyrics split view consumed more CPU than coverflow due to:
- Polling every 80-200ms for karaoke progress updates
- Inline styles on ALL ~50+ lines recalculated on every render
- Transitions tracked on all lines (4 properties each)

Solution:
1. lyricsStore: Add setProgressTrackingEnabled() to skip progress
   updates in immersive mode (only track line index changes)
2. LyricsLines: Use CSS classes for opacity instead of inline styles
   in immersive mode (distance-1, distance-2, distance-3, distance-far)
3. LyricsLines: Add CSS containment (contain: layout style) to isolate
   paint/layout recalculations
4. LyricsLines: Reduce transitions on non-active lines (2 vs 4 props)
5. ImmersivePlayer: Disable progress tracking on mount, re-enable on exit

Result: Immersive lyrics now updates only on line changes (~10-20/min)
instead of continuous progress updates (~300-750/min)
2026-02-01 13:04:13 -06:00
vicrodh f43ecb495f Add book-style collage and custom tooltip styles to SuggestionsPanel
- Book collage: 3 album covers with left/center/right positioning
- Rotated side covers (-12deg/+12deg) with hover animation
- Custom tooltip: opens bottom-right with high z-index (1000)
- Info trigger: 24px with dark background for visibility
- Fade-in animation for tooltip appearance
2026-02-01 12:55:52 -06:00
vicrodh c968beefa0 feat(immersive): merge History into Queue tab + add background transition
Queue/History:
- Removed History as separate tab from header
- Added History as sub-tab within QueuePanel
- Sub-tabs: Queue | History with counts
- Removed history-focus from focus mode tabs
- Removed keyboard shortcut 'H' and '5' for history

Background transition:
- Added 400ms crossfade when artwork changes
- Works for both WebGL and CSS fallback renderers
- Fades out, loads new texture, fades back in
2026-02-01 12:51:31 -06:00
vicrodh 780557efff fix(suggestions): remove book collage, fix badges and tooltip
- Removed book collage (playlist.images are same image at different sizes)
- Using medium quality image (images[1]) for playlist cards
- Fixed z-index on Qobuz/QBZ badges (z-index: 10, above overlay)
- Increased badge icons by 2pt (Qobuz: 22px, QBZ: 19px)
- Replaced native title tooltip with Tooltip component for radio info
2026-02-01 12:42:03 -06:00
vicrodh b20a2edf74 feat(suggestions): book-style collage for playlist cards
- Shows 3 covers like album spines when available
- Center cover fully visible, left/right at -15% offset
- Falls back to medium-quality single image if < 3 images
- Uses images[1] (medium) instead of images[0] (largest)
2026-02-01 12:33:04 -06:00
vicrodh 79672c4afb fix(suggestions): use highest quality playlist artwork
Changed from last (smallest) to first (largest) image in array
2026-02-01 12:30:35 -06:00
vicrodh 5c6ecc6a93 feat(immersive): implement Heavy Immersion shader
Three mandatory mechanisms for depth illusion:

1. Multi-scale texture sampling:
   - Far layer (0.7x zoom) - feels distant
   - Mid layer (0.9x zoom) - transition
   - Near layer (1.3x zoom) - feels close

2. Scale-dependent UV drift:
   - Far: slowest drift (0.03, 0.025)
   - Mid: medium drift (0.05, 0.04)
   - Near: fastest drift (0.07, 0.06)
   - Creates parallax-like depth perception

3. Spatial weighting:
   - Near layer dominant at screen center
   - Far layer dominant at screen edges
   - Mid layer fills the ring between
   - Vignette for additional depth

Result: Background feels spatially enveloping,
not like a zoomed/cropped image.
2026-02-01 12:21:03 -06:00
vicrodh dd0bd1e170 checkpoint: solid immersive baseline before heavy immersion upgrade
Working state:
- Atmospheric background (no recognizable artwork)
- Multi-sample blur in shader
- Gradient tint (cool top, warm bottom)
- Oswald font for lyrics (28px/30px)
- Lyrics panel 700px wide
- Text shadow for contrast
- Karaoke effect disabled in immersive

Ready for Heavy Immersion shader upgrade.
2026-02-01 12:19:52 -06:00
vicrodh 0f6c24f4f9 fix(lyrics): disable karaoke gradient in immersive, increase font +2pt
- Font size: 28px inactive, 30px active
- Disabled background-clip karaoke effect in immersive mode
- Active line now pure white (#ffffff)
- Past lines dimmed to 35% opacity
2026-02-01 12:06:18 -06:00
vicrodh 9d3f05025a fix(immersive): preserve blue colors and improve lyrics readability
Shader:
- Fixed luminance formula that was killing blue (30% blend instead of 100%)

Lyrics:
- Added Oswald font for immersive lyrics
- Increased font size by 2pt (26px inactive, 28px active)
- Active lines now pure white with higher weight for clarity
2026-02-01 11:58:47 -06:00
vicrodh 01adbb7ab4 feat(immersive): enhance shader and widen lyrics panel
Shader improvements:
- Multi-sample blur (4 corners averaged)
- Luminance preservation over saturation
- Gradient tint (cool top, warm bottom) for anti-flat look

Layout:
- Lyrics panel width increased from 560px to 700px (+140px)
- Responsive breakpoint adjusted (480px → 620px)
2026-02-01 11:50:16 -06:00
vicrodh 7535fdb8cc fix(lyrics): add text shadow for contrast against any background
Immersive lyrics now have dark shadow behind text for readability
on light/warm backgrounds like Powerslave's golden tones.
2026-02-01 11:43:41 -06:00
vicrodh 240ca09c6a fix(immersive): sample 90-100% of image for complete color representation
Rely on extreme blur to destroy details, not cropping.
This ensures all dominant colors (like blue sky) are captured.
2026-02-01 11:39:32 -06:00
vicrodh 8917049b00 fix(immersive): increase crop sample size for more relevant colors
Zoom factor changed from 1.5-3x to 1.1-1.6x
Now samples 62-90% of image instead of 33-66%
2026-02-01 11:34:36 -06:00
vicrodh 0bdd83dc08 fix(immersive): destroy artwork structure, preserve only mood
Implement proper atmospheric background pipeline:
- Random NON-CENTERED crop (150-300% zoom) to break composition
- Extreme blur at small size (destroys all semantic detail)
- Scale up with smoothing for additional blur
- Color adjustments (saturation 1.4x, brightness 0.5x)
- Vignette overlay

The background is now an unrecognizable color field derived from
artwork colors. Acceptance test: "Can I identify the album from
the background alone?" must be NO.

Applies to both WebGL and CSS fallback renderers.
2026-02-01 11:32:29 -06:00