Commit Graph

1306 Commits

Author SHA1 Message Date
Maxim Isaev 0feb54ac33 fix(browse): album text search union, scope, and local-first index
Merge album/FTS/track sources for title search (any-word LIKE + prefix FTS),
dedupe cluster text hits by album id instead of album_key, route scoped
cluster browse through per-server advanced search, and prefer the local
index over network race when the library index is enabled.
2026-06-05 22:20:41 +03:00
Maxim Isaev e640d14a0e fix(cluster): dedup merged album lists by album_key at album grain
Roll up track candidates to one row per (server_id, album_id) before
partitioning by album_key so the same album on multiple servers (or mixed
keyed/unkeyed tracks) appears once with priority winner per spec §4.
2026-06-05 21:31:25 +03:00
Maxim Isaev 38ec62a639 fix(browse): cluster multi-library album search without network race
When two or more sidebar libraries are selected in cluster mode, All Albums
text search uses the merged local index only so an empty search3 response from
one member cannot win the local-vs-network race.
2026-06-05 21:26:14 +03:00
Maxim Isaev 6c64c5aa41 perf(browse): album-table search and single-library scoped list
Route plain album title search through the synced album table (LIKE)
instead of FTS over all tracks. Use album+EXISTS for single-library
scope; keep track GROUP BY only for multi-library union.
2026-06-05 21:06:47 +03:00
Maxim Isaev 478c049d0a perf(browse): fast local All Albums path and scoped multi-library union
Add library_list_albums (album table when unscoped, track GROUP BY when
scoped), cluster single-member bypass, ready-member caching, and UI load
deferrals. Remove REST scope allowlists from local hot paths; fix multi-
library scope union and invalidate cluster scope cache on picker toggles.
2026-06-05 21:04:43 +03:00
Maxim Isaev 26716e2fb0 fix(browse): cluster and lossless library scope allowlists
Apply cached getAlbumList2 allowlists per server in SQL and post-filter
so narrowed sidebar scope does not leak albums from other libraries or
cluster members. Wire restrictAlbumScopes into cluster advanced search
and dedicated lossless browse; detect cluster scope in All Albums paging.
2026-06-05 20:29:57 +03:00
Maxim Isaev 496b39a4da fix(browse): layered album filters and multi-library scope
Introduce albumBrowseExecution with explicit filter layers (library scope,
AND attributes, OR genre union, finalize) so multi-genre and cluster paths
cannot leak albums from other libraries. Wire multi-select sidebar picker
scope through local index, cluster merge reads, and REST allowlist fallback.
2026-06-05 19:54:23 +03:00
Maxim Isaev 558f42dba6 feat(cluster): per-member library scope in sidebar picker and merge reads
Cluster mode lists music folders from every member (cached for instant
picker), applies per-server musicLibraryFilter scopes in merged browse,
and passes libraryScopes through Rust list/advanced-search SQL filters.
Dropdown width sizes to content; single-server picker unchanged.
2026-06-05 19:11:40 +03:00
Maxim Isaev 0a09d29b06 fix(preview): Symphonia format sniff and cluster member stream URLs
Resolve preview container hints from HTTP headers, Subsonic suffix, and
magic-byte sniff after Symphonia 0.6. Route preview streams through
clusterBrowseServerId like main playback; guard CoverArtImage when the
preview cover ref is still loading.
2026-06-05 18:48:13 +03:00
Maxim Isaev 1d65a8d339 fix(cluster): artist Play All/top tracks and member-scoped browse UX
Load artist album tracks via getAlbumForServer when clusterSeedServerId is set
so Play All, shuffle, and top-track clicks resolve on the correct member server.
Also improve connection indicator tooltips, Song Info server labels, artist browse
covers/detail fallbacks, and faster merged artist list SQL.
2026-06-05 18:38:10 +03:00
Maxim Isaev 884951fbc2 feat(cluster): playback, browse, discovery, Tier 2/3, and infra
Enqueue-time cluster resolve with mixed-server queue refs; play_session
and scrobble target resolved server; mid-session cascade monitor.
Queue share filters to representative with export count. Excluded-member
banner and cluster settings diagnostics. Cluster advanced search, random
tracks, native favorite albums/artists, extended player stats, sync
rebuild hook. Discovery merge helper, filtered Tier 1, Tier 3 by-source
UI, i18n, and tauri test mocks.
2026-06-05 17:36:53 +03:00
Maxim Isaev 3447cf9fdd feat(cluster): virtual aggregate album/artist detail pages
Add Rust library_cluster_album_detail and library_cluster_artist_detail
with cluster_key dedup, owner metadata fallback, and merged tracklists.
Wire cluster-mode hooks and navigation seedServerId so detail routes
never fall back to single-server getAlbum/getArtist.
2026-06-05 17:02:25 +03:00
Maxim Isaev e601b5888a feat(cluster): playback resolve, writes fan-out, Orbit gating, random browse
Resolve tracks to cluster candidates at play time with cascade fallback;
fan-out scrobble/star/rating to all members; block Orbit host in cluster
mode; warn on mixed-server queue share; serve random picks from merged index.
2026-06-05 16:40:37 +03:00
Maxim Isaev 2ee601683c feat(cluster): Tier 2 favorites and player statistics aggregation
Add merged favorites listing (starred on any member) and cluster-scoped
play_session stats with cluster_key dedup; wire Favorites page and player
statistics panel to cluster APIs when a cluster is active.
2026-06-05 16:36:18 +03:00
Maxim Isaev 4f37fb8752 feat(cluster): Tier 1 browse — merged albums, artists, tracks, search
Add Rust list_merged_albums/artists commands and wire cluster-mode reads
into tracks, albums, artists catalog, and text search browse paths via
clusterBrowse helpers.
2026-06-05 16:32:30 +03:00
Maxim Isaev a7effbc271 feat(cluster): server cluster builder UI and scope switcher
Add cluster management in Settings (create, reorder, rename, sync toggle),
connection indicator dropdown for servers vs clusters, delete guard when a
server is in a cluster, and i18n across all locales.
2026-06-05 16:27:52 +03:00
Maxim Isaev 9813c79c91 feat(cluster): auth store cluster model and library API wrappers (step 3)
Add persisted clusters/activeClusterId, cluster CRUD actions, scope helpers,
representative recompute hook, library_cluster_* API wrappers, and vitest
coverage for cluster store rules and delete guard.
2026-06-05 16:23:38 +03:00
Maxim Isaev f3d4a06726 feat(library): cluster merge, resolve, and search commands (server cluster step 2)
Add library_cluster_list_tracks, library_cluster_resolve_candidates, and
library_search_cluster with priority-ordered dedup by cluster_key and
duration guard. Register Tauri commands; no frontend changes in this step.
2026-06-05 16:22:10 +03:00
Maxim Isaev dbccbea6cc feat(library): attached cluster identity DB for server cluster v1 (PR-1)
Add library-cluster.db (separate from library.sqlite) with track_cluster_key
normalization, batch rebuild, and ATTACH on both library store connections.
No Tauri commands or frontend changes in this PR.
2026-06-05 16:11:59 +03:00
cucadmuh c674e4515b feat(audio): migrate Symphonia 0.5 -> 0.6 (#999)
* feat(audio): migrate Symphonia 0.5 -> 0.6

Port the audio + analysis pipeline to the Symphonia 0.6 API:
- bump symphonia to 0.6 and symphonia-adapter-libopus to 0.3; drop rodio's
  symphonia-all feature to avoid a duplicate symphonia-core 0.5
- remove the local symphonia-format-isomp4 0.5 patch and rely on upstream 0.6
- switch codec registries to register_enabled_codecs / make_audio_decoder with
  AudioCodecParameters + AudioDecoderOptions
- rework decode.rs SizedDecoder and analysis decode loops for the new
  AudioDecoder trait, GenericAudioBufferRef, Time/Timestamp newtypes, and
  next_packet() -> Result<Option<Packet>>

Streaming regression fixes:
- ProbeSeekGate hides seekability during probe for non-MP4 progressive streams
  so Symphonia 0.6's trailing-metadata scan no longer forces a full download
  before ranged FLAC/MP3/OGG playback can start
- guard the streaming probe() with a 20s timeout on a worker thread so a stalled
  ranged source (e.g. right after a server switch) can no longer hang playback
  start until a player restart; add probe start/done diagnostics

* docs(changelog): note Symphonia 0.6 migration and streaming fixes (#999)

Add CHANGELOG entries (Changed + Fixed) and a CONTRIBUTORS line for the
Symphonia 0.6 migration, ranged-stream start-latency fix, and probe timeout.

* test(audio): cover streaming probe path and ProbeSeekGate

Add unit tests for SizedDecoder::new_streaming (success + garbage) and
ProbeSeekGate (seekability toggle, byte_len, read/seek passthrough) to
restore decode.rs above the 70% hot-path coverage gate (67.3% -> 79.4%).
2026-06-05 14:12:57 +03:00
Frank Stellmacher c39465dfad feat(sidebar): toggle to pin Now Playing to the top (#1000)
* feat(sidebar): add toggle to pin Now Playing to the top

The fixed Now Playing entry can now be pinned to the very top of the
sidebar (above the Library label) instead of sitting above the bottom
spacer. Adds a persisted `nowPlayingAtTop` setting (default off, so
existing layouts are unchanged) and a toggle in the sidebar customizer
next to the Mix-navigation split. The entry stays non-hideable and
keeps its playing indicator in both positions.

* i18n(settings): Now Playing-at-top toggle strings (9 locales)

* docs(changelog): note Now Playing top toggle (#1000)
2026-06-05 12:35:39 +02:00
Frank Stellmacher 5f345dc7aa fix(settings): restore full border on active server card (#998)
* fix(settings): restore full border on active server card

The active server card mixed the `border` shorthand with borderTop/
borderBottom longhands in one inline style object. React clears the
unset longhand sides on mount, so the top and bottom borders fell back
to the subtle base border while only the left/right accent border
showed. Drive the drag drop-target indicator via an inset box-shadow
instead, leaving the border shorthand to apply on all four sides.

* docs(changelog): note active server card border fix (#998)
2026-06-05 11:46:35 +02:00
cucadmuh d1320ea2c8 chore(deps): refresh npm and Rust dependencies (#997)
* chore(deps): bump npm patch/minor dependencies

Refresh frontend lockfile for React, Vite, Vitest, i18next, axios, Tauri
plugins, and related type packages within existing semver ranges.

* chore(deps): bump Rust patch dependencies

Update id3 to 1.17, reqwest to 0.13.4, and align root zbus with psysonic-audio 5.16.

* chore(deps): upgrade jsdom to v29

Major test-environment bump; all Vitest suites still pass and npm audit is clean.

* chore(deps): upgrade sysinfo 0.39 and zip 8

Align root sysinfo with psysonic-syncfs and migrate backup archives to zip 8.
mach2 0.6 deferred — cpal/rodio still require ^0.5.

* chore(nix): sync npmDepsHash with package-lock.json

* docs(changelog): note dependency refresh (PR #997)

* docs(changelog): move deps note to [1.48.0] section

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-05 12:17:42 +03:00
Frank Stellmacher 8ca16c1971 Update CHANGELOG.md (#994) 2026-06-05 01:40:23 +02:00
Frank Stellmacher 7a2f937984 docs(changelog): thank the Discord community in 1.47.0 notes (#993)
Add a thank-you note at the top of the 1.47.0 release notes for the
Discord community's support, quality checks, bug reports and general
collaboration, with the Discord invite link.
2026-06-05 01:38:04 +02:00
github-actions[bot] 76dd5c2087 chore(release): bump main to 1.48.0-dev (#992)
* chore(release): bump main to 1.48.0-dev

* chore(nix): sync npmDepsHash with package-lock.json

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-05 01:25:52 +02:00
Frank Stellmacher 2f50a1bade fix(tray): stable tray icon id (no KDE duplicate pile-up on toggle) (#991)
* fix(tray): stable tray icon id so KDE toggle stops piling up duplicates

TrayIconBuilder::new() assigns a fresh id on every rebuild. On KDE
(StatusNotifierItem) each new id registers a new item and the stale ones
linger in the hidden-icons list, so toggling the tray icon off/on stacked
up duplicate entries. Build with a constant id so every rebuild reuses the
same item.

* docs: changelog for tray icon duplicate fix (#991)
2026-06-04 22:17:02 +02:00
Frank Stellmacher ec2bee1400 fix(audio): cap pipewire-alsa client latency on Linux (#862) (#990)
* fix(audio): cap pipewire-alsa client latency on Linux (#862)

On some Linux setups the pipewire-alsa bridge negotiates a multi-second
output buffer, so play/pause/seek/volume only take effect once it drains
(~10-20 s). cpal's buffer-size clamp is ignored by those bridges. Set
PIPEWIRE_LATENCY=256/48000 before the audio stream opens to cap the client
node latency — the reporter confirmed this makes the controls instant.
Linux-only, no-op without PipeWire, and a user-set value is left untouched.

* docs: changelog for pipewire latency fix (#990)
2026-06-04 21:56:15 +02:00
cucadmuh ca502ad833 fix(player): stable playbar clocks when showing remaining time (#987)
* chore: restore PR order in CHANGELOG [1.47.0] Fixed section

Re-sort ### blocks ascending by PR number per release-note placement rules after out-of-order Discord-fix batch inserts.

* fix(player): stable playbar clocks when showing remaining time

Pad seekbar time strings to a fixed width so ticking remaining time does not resize WaveformSeek via ResizeObserver; tighten clock-to-waveform spacing and keep the toggle icon inline.

* docs: CHANGELOG and credits for playbar remaining-time fix (PR #987)

* docs: CHANGELOG entry for playbar remaining-time waveform fix (#987)

* chore: drop settingsCredits entry for minor playbar fix (#987)

* docs: move #987 CHANGELOG entry to end of [1.47.0] Fixed section
2026-06-04 17:53:36 +03:00
cucadmuh 6da98e476b fix(ui): New badge flicker on mainstage album rail hover (#986)
* fix(ui): stop New badge flicker on mainstage album rail first hover

Dim cover via ::before under badges; keep play overlay transparent and
avoid visibility toggles on rail action buttons.

* docs: CHANGELOG for mainstage New badge hover fix (PR #986)

* fix(ui): keep New badge above rail cover zoom (match grid stacking)

Use contain:paint + translateZ layering like New Releases grid so scaled
cover art does not paint over the badge during hover animation.

* fix(ui): global album cover badge stacking for all rails and grids

Move img/badge/overlay z-index into album-card.css; stop album-grid from
resetting card transform or duplicating cover rules that hid New badges.

* docs: CHANGELOG — badge stacking applies to all album rails

* fix(ui): restore rail play buttons above scaled cover layer

Raise play overlay and badge z-index on horizontal rails so WebKit does not paint zoomed cover art over hover controls after the New badge stacking fix.
2026-06-04 16:09:54 +03:00
Frank Stellmacher 631330ebfa fix(search): load album cover for song results (#984)
* fix(search): load album cover for song results

Live Search song rows carried the per-track `mf-…` coverArt id from
search3, which the cover pipeline does not resolve, so the thumbnails went
blank. Album rows worked because they use the album-scoped `al-…` id. A
song's search thumbnail is its album cover anyway — derive it from the
albumId so it loads, and show a music glyph when there is no album.

* docs: changelog for search song cover fix (#984)
2026-06-04 13:05:42 +02:00
cucadmuh 19fdba006a fix(search): local index rejects FTS syntax in live search queries (#983)
* fix(search): reject FTS syntax chars in local index live search queries

FTS5 treats `=` and similar characters as query syntax, so tokens like
1=2 produced unrelated prefix hits. Skip FTS for unsafe tokens instead.

* docs: CHANGELOG for local index FTS syntax query fix (PR #983)

* fix(search): reject syntax junk in server search3 and live search race

Share FTS-safe token guard with search3; skip network invoke for ** and
similar queries; show empty dropdown without misleading source badge.

* fix(search): allow censorship stars in queries, reject wildcard-only tokens

Block ** and **** but keep ***Flawless-style title searches working
in local FTS and search3.
2026-06-04 13:37:11 +03:00
Frank Stellmacher d43a8c6691 fix(ui): square song-rail nav buttons (#982)
* fix(ui): square song-rail nav buttons to match other rails

The song rail's prev/next (and reroll) buttons were circular while every
other rail uses the square rounded-rect nav button. Align them.

* docs: changelog for square song-rail nav buttons (#982)
2026-06-04 11:26:57 +02:00
Frank Stellmacher b0f35eabc9 fix: usable layout when the window is small (#981)
* fix(layout): collapse browse toolbar to icons on compact width

On a narrow window the labelled filter buttons wrapped into several rows
and, being a fixed-height sticky header, starved the album grid below them
down to a clipped strip. Wrap each toolbar label in a hideable span and drop
to icon-only in compact (mobile) mode; icons keep their tooltip and
aria-label so the action stays discoverable.

* fix(window): raise minimum window size to 520x640

The old 360x480 floor let the window shrink far past the point the layout
holds together. Floor it at a laptop-friendly size where the compact layout
(icon toolbar + scrollable lists) still works.

* fix(player): scale mobile cover to fit short windows

The cover width was viewport-derived with no height bound, so on a short
window the square grew past its slot and overlapped the title. Bind it to
the shrinkable wrap via max-height and keep it square with auto sizing.

* docs: changelog for small-window layout fixes (#981)
2026-06-04 11:16:48 +02:00
Frank Stellmacher f3eb58c707 fix(playlist): suggestion rows match the normal track row (#980)
* fix(playlist): suggestion rows match the normal track row

The Suggested Songs table left the Favorite and Rating columns empty and
rendered only a single artist, so multi-artist tracks lost their split and
the blank columns read as a gap between Genre and Duration.

- Extract a shared PlaylistArtistCell that splits the OpenSubsonic artists
  array into individually navigable links; use it for both the playlist
  rows and the suggestions so a track reads the same before and after it is
  added.
- Show the real favorite heart and star rating in suggestion rows (global
  song operations), seeded from the song's own starred/userRating, removing
  the empty-column gap.

* docs: changelog for suggestion row parity (#980)
2026-06-04 10:34:25 +02:00
Frank Stellmacher a7c79ee210 fix: show album artist on featured compilation cards (#979)
* fix: show album artist on featured compilation cards

The 'Also featured on' cards are synthesised from search3 child songs,
which only read the flat `albumArtist` field. Compilation children leave
that empty — the album-artist credit lives in OpenSubsonic's structured
`albumArtists` / `displayAlbumArtist` — so the card fell back to the '—'
placeholder. Carry the structured credit (and the display string) onto
the synthesised album so it resolves a name (and stays navigable when the
server supplies an id).

* docs: changelog for featured-compilation artist credit (#979)
2026-06-04 09:54:10 +02:00
Frank Stellmacher 47e16ebfef fix: RC3 queue link underline, genre album artist split, Artists "Other" bucket (#977)
* fix: RC3 queue link underline, genre album artist split, Artists "Other" bucket

Three zunoz reports, one change:

- Queue now-playing card: the artist and album links now underline on hover
  (not just recolour), matching clickable names everywhere else. The album
  link sits on .queue-current-sub itself; artist links are nested .is-link
  spans — both selectors covered.

- Genre album cards split multi-artist credits into individual links like the
  rest of the app. Root cause was the local-index genre query hardcoding NULL
  for the album raw_json column, so OpenSubsonic artists[] never reached the
  card and it fell back to the flat "A • B" single link. Select a.raw_json
  instead (parity with the All Albums / advanced-search album queries).

- Artists page alphabet index: # is now digits-only, and a new "Other" bucket
  collects accented Latin (Æ/Ø/Å…) and non-Latin scripts (CJK, Cyrillic, …)
  that previously fell into the # catch-all. Adds artistBucketKey/compareBuckets
  helpers (unit-tested), an OTHER bucket sorted last, and the artists.other
  label across 9 locales.

* docs(changelog): queue link hover, genre card artist split, Artists Other bucket (#977)
2026-06-04 03:13:45 +02:00
Frank Stellmacher 88df194808 fix(tracks): RC3 layout regressions + multi-artist links (#976)
* fix(tracks): RC3 layout regressions + multi-artist links

- Restore vertical rhythm on the Tracks hub: the header/hero/rails/browse
  sections sat two wrapper divs below .tracks-page so its flex gap never
  reached them and they collapsed together. New .tracks-hub-stack re-applies
  the gap, fixing the tagline-touching-hero and Random-Pick-overlapping-hero
  spacing (the rail's nav buttons no longer ride into the box above).
- Stop the sticky browse-table header going transparent on hover: its base
  background became opaque but the :hover rule still forced transparent, so
  rows bled through. Header now keeps its card background on hover.
- Widen the Duration column (56px -> 72px; 56 -> 64 on mobile) so the
  "DURATION" header no longer clips/overruns.
- Split multi-artist tracks into individually clickable artist links in both
  the "Track of the moment" hero and the browse list rows (OpenSubsonic
  artists[] with single-artist fallback), matching the album tracklist.

* docs(changelog): Tracks RC3 spacing, Duration, header hover, multi-artist (#976)
2026-06-04 02:28:13 +02:00
Frank Stellmacher 908c349cfd fix(mainstage): rename Home Page setting, start-page fallback + empty state (#975)
* fix(mainstage): rename Home Page setting, add start-page fallback + empty state

- Rename "Home Page" personalisation section to "Mainstage" across 9 locales
  so the heading matches the sidebar entry; add "mainstage" search keyword.
- Index route "/" now redirects to the first visible library item when the
  Mainstage sidebar entry is hidden, instead of stranding the app on a blank
  page. New pure resolveStartRoute() mirrors sidebar order + nav-mode gating.
- Show a guided empty state on Mainstage when every section is toggled off,
  with a CTA into Settings -> Personalisation.
- Unit tests for resolveStartRoute.

* docs(changelog): Mainstage rename, start-page fallback + empty state (#975)
2026-06-04 02:12:52 +02:00
Frank Stellmacher 3be8c367dd Fix queue handle cursor and Favorites column sorting (#974)
* fix(ui): pointer cursor on the queue collapse handle

The round handle is a click-to-collapse button but showed the col-resize
cursor like the seam strip. Use a pointer on the handle; the seam keeps
col-resize and a real drag still switches the body cursor to col-resize.

* fix(favorites): make Plays, Last Played and BPM columns sortable

The header marked these columns sortable (pointer cursor) but handleSortClick
gated on a separate, narrower column set, so clicks did nothing. Both the
comparator and the header now share one SORTABLE_COLUMNS source, so the
affordance and the behaviour can't drift apart again.

* docs(changelog): queue handle cursor + favorites column sorting (#974)
2026-06-04 01:36:24 +02:00
Frank Stellmacher 0d479f3bfa Fix Random Mix audiobook exclusion: click area and false matches (#973)
* fix(random-mix): limit exclusion toggle click area to checkbox and title

The audiobook exclusion was a full-width label wrapping the checkbox,
title and description, so clicking empty space or the description text
toggled it. Make only the checkbox and its title clickable; the
description and surrounding space are no longer hit targets.

* fix(random-mix): stop excluding Thriller and Fantasy as audiobook genres

These keywords match regular music (e.g. Trance/Metal genre tags, a track
titled "Thriller") because the exclusion checks genre, title, album and
artist by substring, dropping a few legit songs per mix. Remove both from
the audiobook keyword list.

* docs(changelog): random mix audiobook exclusion fixes (#973)
2026-06-04 01:18:52 +02:00
Frank Stellmacher c119a32277 Unify button tooltips across the app (#972)
* feat(tooltip): 2s open delay and shared tooltipAttrs helper

Add a 2s hover open delay in TooltipPortal (single behaviour source) so
tooltips no longer flash on quick pointer passes; hiding stays immediate.
Add tooltipAttrs() to pair data-tooltip with a matching aria-label for
buttons touched in the unification work. Covered by Vitest.

* feat(tooltip): lower open delay to 1s

2s felt too long in testing; 1s gives the same anti-flash behaviour
without making intentional hovers wait.

* feat(tooltip): action tooltips on the artist overview

Add tooltips describing the action to Last.fm, Wikipedia, Play All,
Shuffle and Radio. Shuffle/Radio now show a tooltip on desktop too,
not just mobile. Strings added to all 9 locales.

* feat(tooltip): action tooltips on the album overview

Add tooltips describing the action to the desktop Play, Artist Bio and
Download (ZIP) buttons, matching the mobile layout. Strings added to all
9 locales.

* feat(tooltip): action tooltips on the All Albums toolbar

Add tooltips describing the action to the sort, year and genre filter
buttons. SortDropdown gains an optional tooltip prop; the year and genre
filter components carry their own, so the tooltips also appear on the
other browse pages that reuse them. Strings added to all 9 locales.

* feat(tooltip): action tooltips on song-list rows

Add Play and Add-to-queue tooltips to the per-row icons in SongRow, used
by the Tracks browse list, Search and Advanced Search. Also localizes the
aria-labels, which were hardcoded English. New common.addToQueue in all 9
locales.

* fix(tooltip): uniform tooltip placement on the Artists toolbar

The favourite and multi-select buttons forced tooltips below while the
view-mode buttons auto-flipped above, so the row looked inconsistent.
Pin the view-mode buttons below too, matching the rest of the row and the
Albums toolbar.

* feat(tooltip): clarify and align the Advanced Search scope row

Add a leading "Search in:" label and per-chip tooltips so the
All/Artists/Albums/Songs row reads as a scope limiter. Drop the forced
below-placement on the small star filter (used only here) so the
favourites chip flips with the others instead of sitting alone below.
Strings added to all 9 locales.

* docs(changelog): tooltip unification (#972)
2026-06-04 00:53:22 +02:00
cucadmuh 82c414d7bc fix(playlists): Smart Playlist editor theme, toggles, and exclude-all-genres (#970)
* fix(playlists): Smart Playlist editor theme, toggles, and exclude-all-genres

Replace native sort select with CustomSelect, fix mode-button layout shift,
color-code included vs excluded genres, collapse exclude-all to untagged rule,
and handle empty smart playlists without false "not found".

* docs: CHANGELOG and credits for Smart Playlist editor fix (PR #970)

* chore(credits): drop minor fix entries from PR #958 onward
2026-06-04 00:22:29 +03:00
cucadmuh c9b2d140d9 fix(ui): shrink-wrap floating player bar instead of full-width strip (#969)
* fix(ui): shrink-wrap floating player bar instead of full-width strip

The bar used fixed left and right insets, which stretched its background
across the whole main column. Center it with max-content width so only
the pill-shaped controls are painted, and soften the drop shadow.

* docs: note PR #969 in changelog and settings credits
2026-06-04 00:03:24 +03:00
cucadmuh e8962c21ab fix(settings): improve in-page search matching and coverage (#968)
* fix(settings): improve in-page search matching and coverage

Index AudioMuse and individual shortcut rows, tighten fuzzy matching so
junk queries return no hits, and scroll to the parent subsection when a
shortcut result is selected.

* docs: note PR #968 in changelog and settings credits
2026-06-03 23:56:54 +03:00
cucadmuh cd47a4b0fa fix(player): clamp custom delay input and align preview with armed timer (#967)
* fix(player): clamp custom delay input and align preview with armed timer

Reject absurd custom minute values that overflow setTimeout, share one
delay helper between the modal preview and schedule actions, and refresh
countdown ticks when a new deadline is armed.

* docs: note PR #967 in changelog and settings credits
2026-06-03 23:37:17 +03:00
cucadmuh f3a0b3f7af fix(artist-detail): Last.fm/Wikipedia/Favorite hover keeps button border (#966)
* fix(artist-detail): keep ext-link border visible on hover

Hover used --border-subtle, which on Catppuccin matches --bg-card and
visually erased the rim while only the fill changed. Match btn-surface:
--ctp-surface1 border, --ctp-overlay0 on hover.

* docs(changelog): credit zunoz on Psysonic Discord for PR #966

* fix(playlists): tooltips on Play/Add Songs and song count pluralization

Add data-tooltip to Play and Add Songs in playlist hero; switch
playlists.songs to count-based _one/_other forms (was {{n}} without
i18next plural suffix, breaking spacing and singular).

* fix(playlists): render BPM and optional cols in Suggested Songs rows

PlaylistSuggestions shared column headers with the main tracklist but
its row switch omitted bpm, genre, playCount, and lastPlayed.
2026-06-03 23:19:51 +03:00
cucadmuh 5990d84f5a fix(random-mix): keyword blocks and scoped genre list on Build a Mix (#965)
* fix(random-mix): honor keyword blocks and scope genre list to library

Keyword filter was gated behind the audiobook exclusion checkbox, so
blocked artists still appeared after Remix. Genre Mix now loads genres
via fetchGenreCatalog (scoped index / library filter) instead of raw
getGenres; show empty state when all tracks are filtered out.

* docs(changelog): credit zunoz on Psysonic Discord for PR #965
2026-06-03 23:06:50 +03:00
cucadmuh e76dac87ae fix(home): scope Because you listened rail to sidebar library (#964)
* fix(home): scope Because you listened rail to sidebar library

Similar-artist album picks used getArtist without library filtering;
session cache also ignored musicLibraryFilterVersion. Filter picks to
the scoped album set and key cache/reserve by filter version.

* docs(changelog): credit zunoz on Psysonic Discord for PR #964

* test: satisfy SubsonicAlbum required fields in new unit tests

Fix tsc --noEmit CI: songCount and duration are required on SubsonicAlbum.
2026-06-03 22:57:35 +03:00
cucadmuh be21f7834f fix(composers): hide performer-only artists with zero composer credits (#963)
* fix(composers): drop Navidrome role rows with zero composer albums

Navidrome can list performer-only artists under role=composer with
stats.composer.albumCount 0; filter them out of the Composers catalog
so search no longer surfaces ghost entries like Apollo 440.

* docs(changelog): credit zunoz on Psysonic Discord for PR #963
2026-06-03 22:45:52 +03:00