* chore(eslint): add eslint toolchain and configs
* fix(eslint): resolve gradual-config errors (rules-of-hooks, no-empty, prefer-const, …)
* chore(eslint): clear unused vars in config, contexts, app and test
* chore(eslint): clear unused vars in api and music-network
* chore(eslint): clear unused vars in utils
* chore(eslint): clear unused vars in store
* chore(eslint): clear unused vars in cover and hooks
* chore(eslint): clear unused vars in components
* chore(eslint): clear unused vars in pages
* chore(eslint): remove explicit any in src
* chore(eslint): align react-hooks exhaustive-deps
* chore(eslint): zero gradual config on src
* chore(eslint): strict hook rules in store and utils
* chore(eslint): strict hook rules in hooks and cover
* chore(eslint): strict hook rules in components
* chore(eslint): strict hook rules in pages and contexts
* chore(eslint): document scripts ignore in eslint config
* chore(eslint): add lint script and zero strict findings
* chore(eslint): address review round 1 (gradual 0/0, per-site disable reasons)
* chore(eslint): tighten two set-state disable comments (review round 2 LOW)
* chore(nix): sync npmDepsHash with package-lock.json
* docs(changelog): add Under the Hood entry for ESLint setup (PR #1165)
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(albums): restore scroll position when returning from album detail
Save in-page scroll and grid depth when opening an album from All Albums,
then on browser back restore filters, preload enough rows, and apply scroll
before revealing the grid to avoid a visible jump from the top.
* feat(albums): smart back navigation and restore browse session on return
Remember the originating route when opening album detail, restore All Albums
filters/scroll on back (including explicit returnTo navigation), hide the grid
until scroll is applied, and fix filters being cleared after albumBrowseRestore
state is stripped from the location.
* feat(search): restore Advanced Search session when returning from album
Stash filters and results when leaving /search/advanced for album detail,
then restore them on back navigation (POP or returnTo with advancedSearchRestore).
* feat(search): restore Advanced Search album row scroll on return from album
Save horizontal scrollLeft when opening an album from Advanced Search and
reapply it via AlbumRow on return; keep main viewport at top. Add snapshot
helpers and session stash fields; extend AlbumRow with restoreScrollLeft.
* feat(search): restore Advanced Search session scroll and artist return path
Save filters, main scroll, and album-row scroll when leaving to album or
artist; restore without flash via hidden-until-ready. Add useNavigateToArtist,
restoreMainViewportScroll helper, and AppShell scroll reset only on pathname change.
* feat(search): speed up Advanced Search back restore and year-only queries
Reveal the page right after sync scroll instead of blocking on full viewport
and album-row restore. Retry local index without the ready gate during sync;
use open-ended byYear params on network fallback, matching All Albums browse.
* feat(search): restore Advanced Search artist row scroll on back
Save leave snapshot when opening artist from ArtistCardLocal, persist
artistRowScrollLeft in session stash, and keep row restore targets in refs
so horizontal scroll survives finishLeaveRestoreUi like vertical scrollTop.
* feat(nav): route mouse back on album/artist detail like UI back
Trap history popstate when returnTo is set and call navigateAlbumDetailBack
so browser/mouse back restores browse/search session the same way as the header button.
* feat(artists): restore browse filters and scroll on back from artist detail
Persist Artists page filters, view settings, and vertical scroll when opening
an artist and returning via UI or mouse back, matching All Albums behavior.
* feat(search): unify quick and advanced search; fix LiveSearch dismiss on Enter
Serve /search and /search/advanced from one page with shared session restore
and scroll snapshot. Reset live search overlay state when navigating to full
search so the dropdown does not linger or reopen.
* feat(tracks): unify with search session and restore scroll on back
Route /tracks through AdvancedSearch with shared leave snapshot, song
browse stash, and main-viewport scroll restore when returning from album
or artist detail. Wait for hero/rails layout before applying scroll.
* refactor(search): rename AdvancedSearch page to SearchBrowsePage
The shared route shell serves /search, /search/advanced, and /tracks;
rename the page component and refresh stale file references in comments.
* feat(albums): restore New Releases and Random Albums on back from detail
Unify album grid leave-restore with surface-scoped session stash, live scroll
snapshot sync, and in-page scroll for Random Albums. Keep the same random
batch when returning from album detail; Refresh fetches anew and scrolls up.
* docs: add CHANGELOG and credits for PR #936
Global queue paste opens the preview modal before play; overlay scrollbar,
context-menu suppression, changelog/credits for PR #716 and DanielWTE (#551).
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.
Guest suggestions now auto-merge into the host's play queue at random
positions inside the upcoming range, so they actually surface alongside
host-picked tracks instead of piling up at the end. Per-item dedupe via
addedBy:addedAt:trackId keys survives reshuffles.
Guests get a read-only mirror of the session in place of their queue
panel — live-card for the host's current track, suggestion list with
submitter attribution, and a footer reminding them the host owns
playback. Track metadata is resolved lazily and cached locally.
New host-only settings popover (gear in the bar) with auto-approve and
auto-shuffle toggles plus a "Shuffle now" action. Settings live in the
OrbitState blob and push immediately to Navidrome; missing fields on
older blobs default to "both on".
15-min shuffle now touches the real playerStore queue via a new
shuffleUpcomingQueue action — previous behaviour only reshuffled the
guest-facing suggestion history. A manual shuffle is available from the
settings popover so the interval can be verified without waiting.
Start-modal reworked into a single step: share-link is visible and
copy-able from the start, auto-copied on Start if the host hasn't
already hit Copy. Link carries a live name-derived slug (parser strips
it, SID is authoritative). LAN/remote-server hint above the facts.
Random session-name suggester pulls from three pattern pools for ~10k
unique combinations.
All user-visible Orbit strings moved to a dedicated orbit i18n
namespace (en + de); other locales fall back to en via i18next.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(share): library deep links (psysonic2) with paste and toolbar actions
Add base64url-encoded payloads for track, album, artist, and ordered queue.
Handle paste outside inputs to switch server, validate entities, navigate or
play. Reuse clipboard helper for context menu, queue panel, album and artist
headers. Tests distinguish server invite strings from library shares.
* fix(share): play partial queue when some shared tracks are missing
Skip unavailable song IDs when pasting a queue share while preserving order.
Toast when some tracks are missing; error only if none resolve. Add
openedQueuePartial and queueAllUnavailable i18n; remove queueTracksMissing.
* feat(settings): paste server invites globally and scroll to add form
Handle psysonic1- magic strings outside inputs: navigate to settings or login
with prefilled add-server fields. Decode invites embedded in surrounding text.
Scroll the add-server block into view when opening from a paste so long server
lists stay oriented.
---------
Co-authored-by: Maxim Isaev <im@friclub.ru>