* refactor(queue): add queue track resolver (thin-state phase 2a)
Standalone resolver: QueueItemRef → Track via index batch
(library_get_tracks_batch, ≤100/call) → network getSong fallback (P8), into a
bounded LRU cache. Holds raw tracks; session star/rating overrides (F4) merged
on read via applyQueueOverrides. Sync getCachedTrack + subscribeQueueResolver
for selectors; resolveVisibleRange prefetches a [-50, +200] window; carries
queue-only flags from refs; invalidate drops entries after a sync succeeds.
Not wired into the store/UI yet — phase 2b does that.
* refactor(queue): extract toQueueItemRefs helper
Pure helper deriving thin QueueItemRefs from a Track[] queue (per-item
serverId, queue-only flags), shared by the persist partialize and the
upcoming phase-2b resolver bridge. No behaviour change.
* refactor(queue): resolver bridge + queue selectors (thin-state phase 2b)
Seed the resolver cache from the canonical queue (queueResolverBridge, a
windowed [-50, +200] seed around the current index) and add the stable queue
selectors (useQueueTrackAt / useCurrentTrack / useQueueItems).
Additive: the store stays queue: Track[]-canonical; consumers migrate onto the
selectors in phase 3, and the selector impls move to the resolver once
queue: Track[] is dropped in phase 4. No mutation or persist change — the
persisted queueItems keeps its single restore role (no dual-role clash).
* docs(changelog): on-demand queue track loading groundwork (#859)
* feat(playback): global speed with three strategies
Add Settings → Audio and player-bar controls for global playback speed
(speed with auto pitch correction as default, varispeed, manual pitch shift).
Time-stretch runs on a background worker; Orbit sessions force 1.0× passthrough.
* fix(playback): align seekbar, seek, and progress on content timeline
Unify UI timebase across varispeed and preserve strategies: full-track
duration, speed-scaled progress for DSP paths, and content-timeline seeks
without varispeed scaling. Reset the sample counter after seek so clicks
land correctly; restart playback on strategy/enable changes instead of
fragile hot-switching.
* fix(ui): anchor playback speed popover like volume controls
Replace the centered EQ-style modal with a player-bar popover (outside
click, Escape, reposition on scroll). Show compact controls in the bar
and overflow menu; keep strategy hints and labels in Settings only.
* docs(release): CHANGELOG and credits for playback speed (PR #852)
* docs(changelog): add playback speed entry for PR #852
* fix(clippy): simplify raw_counter_samples branch for CI
Collapse duplicate if branches flagged by clippy::if-same-then-else.
* fix(ui): wheel on pitch slider adjusts pitch in speed popover
In compact player-bar controls, scroll over the pitch row changes pitch;
elsewhere in the panel changes speed. Stop propagation so overflow menu
wheel does not tweak volume.
* fix(playback): address PR #852 review and drop ineffective dynamic imports
Translate playback-rate strings for de/fr/es/zh/nb/nl/ro; restamp sample
counter on live preserve-path speed changes; use neutral rate atomics for
radio progress; static-import playerStore in playListenSession (move preview
volume sync to previewPlayerVolumeSync side-effect module).
* fix(i18n): translate playback-rate strategy labels in all locales
Replace leftover English Varispeed/Pitch strings in ru and other non-en
settings blocks so popover strategy buttons and hints read natively.
* fix(i18n): refine German varispeed label to "Tonhöhe folgt dem Tempo"
---------
Co-authored-by: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com>
* feat(linux): session GDK defaults, nvidia-quirk, optional x11-legacy wrap
Ship PSYSONIC_ALLOW_NATIVE_GDK from Nix/AUR instead of pinning WEBKIT_DISABLE_*
and GDK x11. Add flake psysonic-x11-legacy for the old wrap; alias gdk-session
to psysonic. Startup uses webkit2gtk-nvidia-quirk and Wayland-aware compositing;
refresh Help (a45) and nixos-install docs.
* fix(linux): session GDK and nvidia-quirk only; drop wrapper env heuristics
Remove PSYSONIC_ALLOW_NATIVE_GDK and devShell GDK/WEBKIT exports; stop
synthesizing GDK/WebKit vars in main.rs. Update Nix/AUR wrappers, install
docs, CHANGELOG, and help FAQ with practical user-facing workarounds.
* fix(linux): X11-pinned GDK uses DMABUF quirk path, not Wayland explicit-sync
When GDK_BACKEND is forced to x11 on a wayland user session, webkit2gtk-nvidia-quirk
would still apply __NV_DISABLE_EXPLICIT_SYNC and gray out the webview. Map that case
to WEBKIT_DISABLE_DMABUF_RENDERER like native X11.
* fix(ui): stabilize WebKitGTK/Wayland hover paint for nav and media cards
Sidebar nav links avoid transition:all and promote icons with translateZ(0).
Artist rows and album/artist/song cards use compositing hints; card shadows
and borders no longer interpolate so cover zoom can stay smooth without jitter.
* fix(ui): isolate artist/album card text and cover paint on WebKitGTK
Promote cover blocks with contain/paint and text stacks with translateZ(0);
use artist-card-info on the artists grid for the same layout as other cards.
* feat(artists): in-page overlay scroll and locked main viewport
Move list/grid into an inner OverlayScrollArea, stop sticky toolbar from
owning the route scroll, align the rail with the main panel edge, and skip
the main-route overlay thumb when the viewport cannot scroll vertically.
* feat(browse): extend in-page overlay scroll to more library routes
Reuse the locked main viewport pattern from Artists for Albums, Composers,
Lossless albums, and New releases; wire VirtualCardGrid and scroll chrome
to the matching in-page viewport ids.
* fix(linux): improve Wayland GPU compositing text clarity in WebKitGTK
Use on-demand hardware acceleration on main and mini webviews when the
session is Wayland and compositing stays on; gate subpixel body AA on the
same conditions via new Tauri probes. Document PSYSONIC_SKIP_WAYLAND_FONT_TUNING
for opt-out and changelog.
* fix(rust): satisfy clippy needless_return in Linux webkit helpers
* fix(linux): tune Wayland text rendering with HW policy env and CSS
Allow PSYSONIC_WEBKIT_WAYLAND_HW_POLICY to select WebKit hardware
acceleration policy (never/always vs default on-demand). Extend Wayland
font CSS to #root with geometricPrecision and text-size-adjust on html.
* feat(linux): Wayland text presets in settings, safe WebKit apply, CPU default
Persist profile to app config; apply WebKit policy at startup/mini only to
avoid WebKitGTK hangs on live toggles. UI + CSS preview stays live; default
preset is sharp (CPU-friendly).
* fix(linux): map Wayland sharp preset to OnDemand WebKit policy
HardwareAccelerationPolicy::Never at startup broke main-viewport wheel
scrolling on WebKitGTK+Wayland; sharp vs balanced remains a CSS AA path.
Use PSYSONIC_WEBKIT_WAYLAND_HW_POLICY for a true Never policy.
* fix(rust): gate Linux-only Wayland WebKit helpers for Windows builds
Re-export startup helpers only under cfg(linux) and drop non-Linux stubs so
Windows compiles without unused-import and dead-code warnings.
* chore(release): CHANGELOG + credits for Linux session/WebKit work (PR #731)
Consolidate scattered incremental changelog notes into two [1.47.0]
entries with PR link; remove duplicate Linux blocks from [1.46.0] Fixed.
Append settings credit line for cucadmuh.
* experiment(zoom): allow setting webview zoom via core capability
* experiment(zoom): drive uiScale through Tauri's native webview zoom
Replace the CSS `zoom: uiScale` on `.main-content-zoom` (which only
scaled the main content column, leaving the sidebar, queue panel,
player bar and portaled overlays at 1.0) with a `setZoom` call on the
current webview. That scales everything inside the window the same way
Ctrl+/− does in a browser, including portals and the queue panel.
Effect runs whenever `uiScale` changes and once on mount, so the
persisted setting is reapplied on launch.
* docs: changelog + credits for interface scale (#781)
* feat(offline): show cached albums from all servers in Offline Library
List every offline album regardless of active server; load cover art per
source server and switch before play/enqueue. Sidebar, mobile nav, and
disconnect auto-nav use any cached content; multi-server cards show a label.
* docs(changelog): link offline library PR #719
* chore(pr-719): address review — CHANGELOG in Added, helper tests
Move release note to ## Added per team changelog policy; cover
offlineAlbumCoverArt and ensureServerForOfflineAlbum in unit tests.
* fix(playback): pin queue streams, cover art, and library links to queue server
When the active server changes while a queue from another server is playing,
keep streams and UI on queueServerId; switch back for artist/album links and
queue or player-bar context menus.
* fix(playback): switch to queue server when opening Now Playing
Ensure active server matches queueServerId before Subsonic fetches on the
Now Playing page, mobile player route, and queue info panel; scope caches
by server id.
* docs(credits): mention Now Playing in PR #717 contribution line
* fix(playback): route scrobble and queue sync to queue server
Address PR review: apiForServer for scrobble/now-playing/savePlayQueue,
clear queueServerId on server removal, mini-player queueServerId sync,
block cross-server enqueue with toast, and regression tests.
111 of 122 top-level src/utils/ files move into 16 topic folders (audio,
cache, cover, share, server, playback, playlist, deviceSync, waveform,
mix, format, export, changelog, ui, perf, componentHelpers). True
singletons with no cluster stay at the utils/ root.
Pure file-move: a path-aware codemod rewrote 539 relative-import
specifiers across 275 files; no logic touched. The hot-path coverage
gate list (.github/frontend-hot-path-files.txt) is updated to the new
paths for the 11 gated utils files — a mechanical consequence of the
move, not a CI change. tsc is green.
Seven domain-eng splits peel ~200 LOC of read endpoints out of
`api/subsonic.ts`:
- `subsonicStreamUrl.ts` — `buildStreamUrl`, `coverArtCacheKey`,
`buildCoverArtUrl`, `buildDownloadUrl` (token-signed URL builders
for the four /rest endpoints we hand to the browser).
- `subsonicStarRating.ts` — `getStarred`, `star`, `unstar`,
`setRating`, `probeEntityRatingSupport`. `setRating` still triggers
the lazy `navidromeBrowse` cache invalidation; the same-folder
lazy import path is preserved.
- `subsonicSearch.ts` — `search`, `searchSongsPaged`.
- `subsonicScrobble.ts` — `scrobbleSong`, `reportNowPlaying`,
`getNowPlaying`.
- `subsonicAlbumInfo.ts` — `getAlbumInfo2`.
- `subsonicLyrics.ts` — `getLyricsBySongId`.
- `subsonicGenres.ts` — `getGenres`, `getAlbumsByGenre`.
63 external call sites migrated to direct imports. Four `vi.mock`
targets in the store-level tests pointed at `../api/subsonic` and
were updated to the new module paths.
Pure code-move. subsonic.ts: 762 → 561 LOC (−201).
Three domain-eng modules peel ~316 LOC of fetch/mapping out of
`api/subsonic.ts`:
- `subsonicLibrary.ts` — browse + random + per-song fetch
(`getMusicDirectory`, `getMusicIndexes`, `getMusicFolders`,
`getRandomAlbums`, `getAlbumList`, `getRandomSongs`,
`getRandomSongsFiltered`, `getSong`, `getAlbum`,
`filterSongsToActiveLibrary`, `similarSongsRequestCount`, plus
the private `albumIdsInActiveLibraryScope` cache).
- `subsonicArtists.ts` — artist endpoints (`getArtists`, `getArtist`,
`getArtistInfo`, `getTopSongs`, `getSimilarSongs2`,
`getSimilarSongs`). Uses Library's `filterSongsToActiveLibrary` and
`similarSongsRequestCount` for the per-library scoping fallback.
- `subsonicRatings.ts` — `parseSubsonicEntityStarRating` parser plus
`prefetchArtistUserRatings` and `prefetchAlbumUserRatings` workers
with the shared 7-min cache. Calls back into Library/Artists for
the per-id fetch.
51 external call sites migrated to direct imports. No re-export
shims in `subsonic.ts`. Statistics endpoints still in `subsonic.ts`
keep their `RATING_CACHE_TTL` constant locally (same 7-min window).
subsonic.ts: 1078 → 762 LOC (−316).