Commit Graph

25 Commits

Author SHA1 Message Date
cucadmuh 45e0e1206f fix(playback): pin queue playback to source server when browsing another library (#717)
* 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.
2026-05-15 16:08:41 +03:00
Frank Stellmacher 7a7a9f5e6b refactor(utils): group utils/ files into topic folders (Phase L, part 1) (#689)
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.
2026-05-14 14:27:44 +02:00
Frank Stellmacher 6f8dd73448 refactor(now-playing): G.73 — extract TourCard + DiscographyCard + useNowPlayingFetchers + useNowPlayingStarLove (cluster) (#640)
Four-cut cluster closing out the NowPlaying decomposition. 715 → 391
LOC (−324).

TourCard — Bandsintown tour-dates card with the privacy-prompt
gate (shown before the user opts in), loading state, empty state,
5-item show-more list with date / venue / place, and the
"Tour data via Bandsintown" credit footer.

DiscographyCard — chronological album grid (10×2 = 20 tiles
initial, show-more for the rest), each tile a cached cover-art
thumbnail with a tooltip and click-through to the album page.

useNowPlayingFetchers — the eight cached fetch effects that drove
the page: song meta / artist info / album / top songs / Bandsintown
tour events (+ loading state) / discography / Last.fm track stats /
Last.fm artist stats. Each effect follows the same pattern
(cache hit → seed state, cache miss → fetch + setCache + setState,
cancellation flag on cleanup). All eight makeCache<…> instances
move into the hook module too.

useNowPlayingStarLove — local starred + lfmLoved booleans seeded
from songMeta.starred and lfmTrack.userLoved respectively, plus
toggleStar (star/unstar Subsonic API) and toggleLfmLove
(lastfmLove/Unlove with the session key).

NowPlaying drops the now-unused imports: star/unstar (Subsonic),
getArtist/getArtistInfo/getTopSongs/getSong/getAlbum, the entire
'../api/lastfm' line, fetchBandsintownEvents + BandsintownEvent,
makeCache (was passing through to the page only for cache
instantiation), plus SubsonicAlbum (only the type is still used
inside the hook). The radio + dashboard JSX in the body still
references everything via the hook returns. Pure code move
otherwise.
2026-05-13 14:46:31 +02:00
Frank Stellmacher 2ddc2a2345 refactor(now-playing): G.72 — extract Hero + ArtistCard + AlbumCard + TopSongsCard + CreditsCard (cluster) (#639)
Five-cut cluster pulling the dashboard subcards out of NowPlaying.tsx.
1119 → 711 LOC (−408). Each card was already a top-level
React.memo'd block with a clean prop interface, so this is straight
file-per-card extraction with no behaviour change.

Hero — full hero strip: cover image, title, artist/album/year/age
sub, format/codec/sample-rate/bit-depth/duration badges with the
Hi-Res chip, the favourite + Last.fm-love + lyrics action buttons,
play-count line, and the Last.fm track + artist stats rows (with
their per-row `you played N×` highlight). renderStars (5-star
inline indicator) moves inline as a private helper since only Hero
calls it.

ArtistCard — about-this-artist card: optional hero image (filtered
through isRealArtistImage so we don't render the well-known
"2a96…" placeholder), name, sanitized + clamp-with-Read-more bio,
similar-artist chip row. Owns its bioExpanded / bioOverflows state
and the useLayoutEffect that measures overflow.

AlbumCard — from-this-album card: meta line (year / track position /
total duration / play count), sliding-window tracklist anchored on
the current track (top 10 by default, or the 10 ending at the
running track if it's beyond position 10), and the show-all toggle.

TopSongsCard — top-songs-by-artist card: rank-ordered list (max 8),
each row clicks into playerStore via the onPlay callback.

CreditsCard — contributor / song-info card: role-grouped list with
i18n role label lookup (falls back to the raw role string).

NowPlaying drops the now-unused imports (useLayoutEffect, LastfmIcon,
Star, MicVocal, Heart, Headphones, TrendingUp from lucide,
sanitizeHtml + isRealArtistImage + formatTotalDuration from
nowPlayingHelpers) plus the inline renderStars helper. Pure code
move otherwise.
2026-05-13 14:21:31 +02:00
Frank Stellmacher 1c34cc04c7 refactor(now-playing): G.71 — extract helpers + cache + NpCardWrap + NpColumnEl + RadioView (cluster) (#638)
Five-cut cluster opening the NowPlaying refactor. 1384 → 1119 LOC
(−265).

nowPlayingHelpers — eight pure helpers + ContributorRow type:
formatTime, formatCompact, formatTotalDuration, sanitizeHtml
(strip dangerous attributes + trailing Last.fm "Read more" link),
isoToParts (date formatting for Bandsintown), buildContributorRows
(dedupes contributor list, hides redundant "Artist = main artist"
row), isRealArtistImage (filter the Last.fm "2a96…" placeholder MD5
that aggregating Subsonic backends still emit).

nowPlayingCache — module-level TTL cache used by all subcomponents:
CACHE_TTL_MS (5 min), CacheEntry type, makeCache() factory.
NowPlaying still instantiates eight `makeCache<…>()` typed caches
inline at module scope; only the factory + TTL constant move.

NpCardWrap — drag-source wrapper around each dashboard card,
participates in the psyDnD drag stream via useDragSource.

NpColumnEl — drop-target column. Owns the document mousemove
listener that determines which wrapper the dragged card would
land before (x-axis decides column, y-axis bisects wrapper rects
to compute insert index). No-op when no card is being dragged.

RadioView — full radio-playing layout: hero card with stream
name + current artist/title/album + AzuraCast progress bar +
listeners badge, "Up Next" card, recently-played list. Subscribes
to nothing on its own; takes the radioMeta tuple + currentRadio +
resolvedCover from the parent. NonNullStoreField type alias moves
with it.

NowPlaying drops the inline definitions; renderStars + the eight
typed cache instances stay in the page for now (renderStars is used
by Hero + TopSongsCard, both of which still live inline; the
typed caches are consumed by NowPlaying's load effects). Pure code
move otherwise.
2026-05-13 14:09:37 +02:00
Frank Stellmacher 9606a99efb refactor(api): F.50 — extract 7 small domain modules (#615)
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).
2026-05-13 00:46:13 +02:00
Frank Stellmacher 006635de4b refactor(api): F.49 — extract library + artists + ratings (#614)
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).
2026-05-13 00:23:53 +02:00
Frank Stellmacher 72030f17fd refactor(api): F.48 — extract subsonic types + client primitives (#613)
First Phase F slice. Splits the 1333-LOC `api/subsonic.ts` along its
two most obvious axes:

- `subsonicTypes.ts` — all ~24 exported interfaces + type aliases
  (album/song/artist/playlist/directory/genre/now-playing/radio,
  random-songs filters, three statistics shapes, search + starred
  results, AlbumInfo, structured-lyrics types, etc.) plus the
  `RADIO_PAGE_SIZE` constant.
- `subsonicClient.ts` — token-auth + `getClient` + `api<T>()` +
  `libraryFilterParams` + `secureRandomSalt` / `getAuthParams` /
  `SUBSONIC_CLIENT`. The credential-bearing API helpers
  (`pingWithCredentials`, `apiWithCredentials`, `restBaseFromUrl`,
  `probeInstantMixWithCredentials`) stay in `subsonic.ts` for now —
  they could move into the client module in a follow-up.

66 external call sites migrated to direct imports from the new
modules (no re-export shims in `subsonic.ts`). Pure code-move;
contract test stays green.

subsonic.ts: 1333 → 1078 LOC (−255).
2026-05-13 00:05:58 +02:00
Frank Stellmacher 9fac6eb490 refactor(player): E.42 — migrate playerStore re-exports to direct imports (#607)
Migrates ~74 call sites away from the playerStore re-export shims that
were kept during M0–E.41 to avoid touching 30+ imports per PR. Now
that the bigger refactor work is done, each helper goes back to its
real home:

- `initAudioListeners`, `installQueueUndoHotkey`, `flushPlayQueuePosition`
  → from their own store modules
- `getPlaybackProgressSnapshot`, `subscribePlaybackProgress`,
  `PlaybackProgressSnapshot` → from `playbackProgress`
- `resolveReplayGainDb`, `shuffleArray`, `songToTrack`
  → from `utils/*`
- `_resetQueueUndoStacksForTest`, `consumePendingQueueListScrollTop`,
  `registerQueueListScrollTopReader` → from `queueUndo`
- `PlayerState`, `Track` types → from `playerStoreTypes`

Drops the corresponding 13 re-export stubs from `playerStore.ts` and
the now-unused imports. Also drops dead section banners + per-wrapper
comments above one-line action delegates. Trims one stale "(separate
PR)" note in `transportLightActions.ts` since that follow-up landed
in E.39.

`playerStore.ts`: 180 → 112 LOC (−68). Down from Phase E's starting
3732 LOC.

`bootstrap.test.ts` mock target updated from `../store/playerStore`
to `../store/queueUndoHotkey` to keep the spy reachable after the
import change.
2026-05-12 22:46:13 +02:00
cucadmuh 9ad0f8af6d feat(ui): UI refinements — sidebar indicators, adaptive header, and interaction polish (#397)
* feat(ui): unify queue toggle handle behavior

Show the queue toggle in the header when the queue is collapsed and use a seam-aligned drag handle when it is open. Hide the seam handle while the main content is actively scrolling to reduce accidental interactions.

* feat(ui): add adaptive header search collapse behavior

Collapse header search to a magnifier when top controls get crowded and expand it as an overlay only while active. Use measured header space with hysteresis to avoid flicker and keep neighboring controls stable.

* chore(ui): remove leftover search prototype artifacts

Drop an unused icon import from live search and remove an unused header container-type rule left from an earlier layout experiment.

* feat(ui): persist sidebar and queue visibility state

Save left sidebar collapse and right queue open/closed visibility in local storage helpers so both panel modes are restored after app restart.

* feat(ui): unify player overflow menu behavior

Use a single overflow menu for click and wheel interactions, with a volume-only mode that keeps the same layout and volume controls as the full menu.

* feat(ui): add wheel seek controls to waveform

Apply 10-second wheel seek steps with trailing 1-second debounce and keep the waveform preview stable so the playhead moves smoothly during rapid scroll input.

* fix(now-playing): stabilize narrow dashboard layout

Switch now-playing responsiveness to container-based breakpoints and prevent stacked widgets from overlapping when width is constrained.

* fix(search): reduce collapse jitter and avoid header overlap

Add a short collapse-state cooldown to prevent threshold flicker and hide conflicting header controls while collapsed search expands as an overlay.

* fix(i18n): localize player overflow controls across locales

Replace hardcoded player overflow labels with translation keys and add the missing keys for all shipped locale files.

* fix(search): keep advanced control clickable in collapsed mode

Prevent focus loss on the advanced search button in collapsed overlay mode so its click handler consistently runs.

* fix(i18n): restore queue translation in offline library

Use the existing queue.appendToQueue key for the offline enqueue button tooltip and label instead of a missing key and hardcoded English text.

* fix(ui): apply overlay scrollbar to right-panel text tabs

Switch now-playing content, lyrics, and info panes to OverlayScrollArea and harden tour-item layout so long concert metadata stays within panel bounds.

* fix(ui): add unread indicator for new releases and guard sidebar drag clicks

Track unread new-release IDs per server/library scope and clear the badge when opening the New Releases page. Also prevent click-through navigation after sidebar drag release and keep related i18n/responsive sidebar-adjacent refinements in this snapshot.

* fix(ui): stabilize live dropdown layering and unread reset flow

Render the topbar Live dropdown via a portal so it consistently overlays sidebar layers. Rework new-releases unread tracking to handle library scope baselines, ignore stale refresh races, and mark items as seen after a 5-second stay on the New Releases page.

* feat(ui): add localized New badges for recently added albums

Show a theme-consistent New badge on album cards and album detail for albums created within the last 48 hours. Localize the badge label across all supported locales and centralize recency logic in a shared utility to avoid duplication.

* fix(album): prevent tracklist jump when entering multiselect

Move bulk selection actions from the tracklist body into the album toolbar next to the track filter. Keep selection controls stable in the header area so enabling multiselect no longer shifts the tracklist content downward.

* fix(tray): add playback-state badge and finalize queue handle tooltip

Show play/pause/stop icons in the Linux tray now-playing entry and persist state safely in Tauri managed state.
Also switch the queue-resize handle tooltip to the dedicated localized key across all locales.

* fix(header): prioritize search collapse before Live/Orbit labels

Make topbar compression deterministic by collapsing search first and compacting Live/Orbit labels only in sustained low-space mode.
Add sticky hysteresis-based header compact state to prevent oscillation while resizing.

* fix(ui): stabilize header compaction and show tray state icons

Prevent topbar flicker in the narrow-width range by tightening compact-mode thresholds, gating on real overflow, and removing width transitions from live search.
Also include playback state icons in tray tooltip text across platforms while preserving tooltip length limits.

* fix(tray): keep tooltip iconization Windows-only

Revert Linux tray tooltip/title fallback attempts and keep state icons only in Windows tray tooltips, while Linux continues to show playback state in the now-playing menu entry.

* fix(ui): restore queue resize response after overlay scroll interactions

Hide the queue handle while scrolling on both the main route viewport and the now-playing viewport, and clear stale thumb-drag state before starting queue resize.
Also ignore inactive/faded overlay thumbs in resizer suppression so horizontal pointer transitions no longer leave the queue seam unresponsive.

* docs(changelog): summarize ui-refinements branch features

Document the branch-level feature additions in 1.45.0 as separate changelog sections and group remaining branch-local fixes under a single polish entry.

* docs(changelog): add PR #397 references for ui-refinements

Attach PR metadata to the new 1.45.0 ui-refinement sections and the polish entry so release notes map directly to the merged branch discussion.
2026-05-01 15:42:40 +03:00
Frank Stellmacher db72ed9e5a feat(now-playing): draggable widget cards with per-user layout (#267)
Each dashboard card (Album, Top Songs, Credits, Artist, Discography,
On Tour) is now a widget the user grabs and drops anywhere — reorder
within a column or move across columns. Layout persists per-install
via the new nowPlayingLayoutStore (Zustand + localStorage, same shape
as sidebarStore with an onRehydrateStorage guard for unknown IDs).

Drag uses psyDnD (useDragSource / psy-drop event, the mouse-event
based system from DragDropContext) — HTML5 native DnD is a no-go on
WebKitGTK Linux where it always shows a forbidden cursor. The whole
card is the drag handle; the column listens via a document-level
mousemove for the drop indicator and via a global psy-drop listener
that reads the latest hovered column from a ref, so drops below the
last card still land correctly regardless of column height.

Visibility is toggled from a layout menu in the top-right of the
dashboard — two sections (visible / hidden) plus a reset-to-defaults
button. No hide buttons on the cards themselves (keeps the card UI
uncluttered). An equal-height grid (align-items: stretch + min-height
on columns) keeps the drop zone active for the full vertical span of
either column.

Co-authored-by: Psychotoxical <dev@psysonic.app>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:14:13 +02:00
Frank Stellmacher b4f31e0954 feat(now-playing): redesign page as info dashboard (#266)
Replace the flat Now Playing page with a two-column dashboard: hero
(cover, metadata, badges, Last.fm stats + love button, release-age
tagline), and a grid of themed cards (Album tracklist with sliding
window, most-played-by-artist, credits, about-the-artist with Last.fm
bio fallback, compact discography contact-sheet with expand, upcoming
tours via Bandsintown). Adds lastfmGetTrackInfo + lastfmGetArtistStats
for global listener counts + userplaycount, plus Last.fm love/unlove
wired to the new hero button. Module-level TTL caches per entity keep
same-artist track switches instant. Artist-image guard filters the
well-known Last.fm no-image placeholder so the card collapses cleanly
when no real image exists.

Co-authored-by: Psychotoxical <dev@psysonic.app>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:30:09 +02:00
Psychotoxical ccdb899833 fix(theme): improve Latte and GTA theme readability
Latte: fix server dropdown dark background (--bg-card instead of hardcoded #1e1e2e), add back-button white text over album cover overlay, fix NowPlaying white-on-white text (artist/album, badges, bio, tracklist, star/heart icons use semantic vars).

GTA: bump --text-muted from #484848 to #707070 and --text-primary from #e8e8e8 to #d8d8d8 — sidebar section labels, Settings descriptions and album detail info were nearly invisible on the near-black background.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 17:44:18 +02:00
Maxim Isaev 69c0c5a907 merge: integrate origin/main into feat/audiomuse-navidrome 2026-04-10 13:04:34 +03:00
Maxim Isaev 36f3d42dbe feat(discovery): Navidrome AudioMuse-AI client integration and library scoping
Add per-server toggle for AudioMuse-style discovery: Instant Mix via
getSimilarSongs, server similar artists instead of Last.fm on artist pages,
and higher similar-artist count in Now Playing when enabled.

When browsing a single music folder, filter similar/top song results by
album ids from that scope (Navidrome does not apply musicFolderId to those
endpoints). Request larger similar batches under a narrow scope.

Keep radio-from-track as two blocks (shuffled top songs, then shuffled
similar-by-artist) so it differs from Instant Mix. Show Alpha badge beside
the setting. Add research/ to .gitignore.
2026-04-10 12:07:32 +03:00
Nils Israel 46cefb5712 feat: add ICY metadata and AzuraCast radio streaming support
Agent-Logs-Url: https://github.com/nisrael/psysonic/sessions/88faada5-28bb-446f-b53b-46a0efef387e

Co-authored-by: GitHub Copilot <198982749+copilot@users.noreply.github.com>
Signed-off-by: Nils Israel <nils@sxda.io>
2026-04-10 10:38:59 +02:00
Maxim Isaev 1624880c2a feat(ratings): mix cutoff filter for random flows and home, i18n
Apply the same per-axis star cutoff to random mix (multi-batch fetch until
full), random albums, Hero, and Home hero/discover rows. Prefetch artist and
album ratings via Subsonic when list payloads omit them. Exclude items when
0 < rating ≤ threshold; 0 or missing counts as unrated.

Settings: rating filter description interpolates sidebar menu labels; Russian
strings for custom title bar; short axis labels aligned across locales.
2026-04-08 04:57:26 +03:00
Psychotoxical 7263d93d42 feat: v1.29.0 — Radio fast-start, seek fix, OGG, error toasts, contributors
Incorporates PRs #38 (nisarg-78), #42 #43 #44 (JulianNymark) with fixes:

Audio / Playback
- Artist Radio starts immediately from fast getTopSongs (local library);
  getSimilarSongs2 (Last.fm) enriches queue in background — no more wait
- Fix seek audio glitch: EqualPowerFadeIn only resets to zero-gain on
  seeks to track start (<100 ms); all other seeks resume at unity gain
- OGG/Vorbis container support via symphonia-format-ogg (PR #42)
- Human-readable audio error messages in SizedDecoder (PR #44)
- Audio playback errors shown as themed toast notifications (PR #43)

Queue / Radio
- Infinite Queue: proactive load of 5 tracks (was 25) when ≤2 remain
- Radio: proactive reload at ≤2 remaining tracks, independent of
  Infinite Queue setting — radio no longer stops at last track
- Fix: clicking Start Radio multiple times no longer stacks duplicates
- Fix: Start Radio on artist keeps current song playing
- Manual tracks always appear before Radio, Radio before Auto-added
- Queue separators: "— Radio —" and "— Auto —" dividers
- Fix: radio proactive load now works even when songs lack artistId
  (uses currentRadioArtistId module var as fallback)

UI / UX
- Click synced lyrics lines to seek (PR #38)
- Volume scroll wheel on volume slider (PR #38)
- Lyrics: active / completed / upcoming visual states (PR #38)
- Shared toast utility (src/utils/toast.ts) replaces inline toast fn
- Auto-updater: relaunch_after_update Rust command exits first to
  release single-instance lock before spawning new process

About / Credits
- nisarg-78 and JulianNymark added to contributors (since v1.29.0)
- netherguy4 added as Special Thanks for feature ideas and feedback
- i18n: aboutSpecialThanksLabel in all 5 languages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 22:17:55 +02:00
Psychotoxical 361e9cfdb3 feat: v1.14.0 — Critical Buffer Fix, Gapless/Crossfade stable, UX polish
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:00:55 +01:00
Psychotoxical d927ef2082 feat: v1.12.0 — Lyrics, 15 new themes, Last.fm stable, Crossfade stable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 13:10:06 +01:00
Psychotoxical 867c5fbd3e feat: v1.11.0 — Movies themes, Settings polish, Gapless stable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 02:37:49 +01:00
Psychotoxical e550340565 feat: v1.10.0 — new streaming themes, favourite toggle fix, home page improvements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 22:58:05 +01:00
Psychotoxical 57b70e6154 feat: v1.9.0 — new themes, keybindings, font picker, home play behavior
Themes:
- Add Neon Drift (midnight blue / electric cyan synthwave)
- Add Cupertino Light + Cupertino Dark (macOS Ventura-inspired, frosted glass)
- Add Betriebssysteme group (Cupertino, Aero Glass, Luna Teal)
- Rename all trademarked theme IDs to original names (WnAmp, Navy Jukebox,
  Cobalt Media, Onyx Cinema, Aero Glass, Luna Teal)
- Reorder ThemePicker: Psysonic Themes first, then Mediaplayer, Betriebssysteme

Keybindings:
- New keybindingsStore with 10 bindable actions (play/pause, next, prev,
  volume, seek ±10s, queue, fullscreen, native fullscreen)
- Settings UI for rebinding; defaults: Space=play-pause, F11=native-fullscreen

Font picker:
- New fontStore; 10 UI fonts selectable in Settings → Appearance
- Applied via data-font attribute on <html>

Home page:
- AlbumCard: Details button → Play button via playAlbum() utility
- Hero: Play Album starts playback with 700ms fade-out instead of navigating
- playAlbum.ts: fade, store-only volume restore, playTrack handoff

Now Playing:
- 3-column hero layout; EQ bars truly centred (flex:1 on both outer columns)
- Background brightness 0.25→0.55, overlay 0.55→0.38 (art now visible)
- Better text contrast for track times, card links, section titles

Linux audio:
- Set PIPEWIRE_LATENCY + PULSE_LATENCY_MSEC before stream creation
  to reduce ALSA snd_pcm_recover underrun frequency on PipeWire

Remove butterchurn visualizer (VisualizerCanvas, butterchurn.d.ts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 14:22:02 +01:00
Psychotoxical 2ba7845c79 feat: Last.fm beta, Similar Artists, Statistics Last.fm stats, TooltipPortal, CustomSelect, Psychowave theme (v1.7.0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 18:35:26 +01:00
Psychotoxical 59115a09d2 feat: 10-band EQ, connection indicator, new icon (v1.5.0)
### 10-Band Graphic Equalizer
- Full EQ in Rust audio engine via EqSource<S> biquad peak filters
- 10 built-in presets + custom preset save/delete
- EqSource::try_seek() implemented — also fixes waveform seek which broke
  silently when EQ was introduced (rodio returned SeekError::NotSupported)
- EQ state persisted in localStorage, synced to Rust on startup

### Connection Indicator
- LED in header (green/red/pulsing) with server name and LAN/WAN label
- Offline overlay with retry button when server is unreachable
- useConnectionStatus hook

### New App Icon
- New logo (logo-psysonic.png) applied across Login, Sidebar, Settings,
  README and all Tauri platform icons (Windows, macOS, Linux, Android, iOS)

### Now Playing Page
- New /now-playing route added

### Fixes
- WaveformSeek: mousemove/mouseup on window to fix drag outside canvas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 22:25:32 +01:00