Commit Graph

1541 Commits

Author SHA1 Message Date
Psychotoxical 13142e6ae3 refactor(lib): extract clean server + navigation infra to lib/ (decouple-knot files stay in utils/ pending seam) 2026-06-30 20:44:34 +02:00
Psychotoxical 7d98e649da refactor(hooks): relocate misplaced useTracklistColumns hook from utils/ to hooks/ (cross-cutting) 2026-06-30 20:39:22 +02:00
Psychotoxical 390fdfbcca refactor(lib): fold utils/perf into lib/perf + utils/ui (DOM utilities) into lib/dom 2026-06-30 20:36:23 +02:00
Psychotoxical 6becd57798 refactor(lib,playback): fold utils/cache+themes into lib; playbackScheduleFormat→playback (drains utils/format) 2026-06-30 20:33:25 +02:00
Psychotoxical 31d4a87401 refactor(lib): fold utils/audio + autodj-overlap into lib/audio (audio-transition infra; drains utils/playback) 2026-06-30 20:30:07 +02:00
Psychotoxical aea8b7750b refactor(lib,playback,whatsNew): home waveform→lib, timeline utils→playback, releaseNotes+changelog→whatsNew 2026-06-30 20:26:34 +02:00
Psychotoxical 695462ed87 refactor(cover,lib): fold utils/cover into cover/ + utils/media into lib/media (generic infra) 2026-06-30 20:21:15 +02:00
Psychotoxical 982499d32b refactor(orbit,playback): move feature-owned stores (helpModal→orbit; libraryPlaybackHint+playerBarLayout→playback) 2026-06-30 20:16:55 +02:00
Psychotoxical 03ce57feff refactor(app): co-locate shell-only lifecycle hooks in app/hooks; hooks/ now holds only cross-cutting hooks 2026-06-30 20:09:22 +02:00
Psychotoxical 79c02a93cf refactor(app): move Tauri IPC bridge hooks (tauriBridge/) into app/ where the shell composes them 2026-06-30 20:06:05 +02:00
Psychotoxical 7ae259cacf refactor(updater,settings): move feature-owned updater + theme-update hooks into their features 2026-06-30 20:03:25 +02:00
Psychotoxical 312fa78240 refactor(playback,playlist): move player-bar overflow/popover/delay + bulk-picker hooks into their features 2026-06-30 19:59:38 +02:00
Psychotoxical c2461c88d5 refactor(playback): co-locate floating-player-bar + fs-idle-fade hooks (and orphaned playback tests) with their features 2026-06-30 19:55:20 +02:00
Psychotoxical f03a5f13e8 refactor(cover): move cover-coupled hooks into cover/; drop deprecated usePlaybackCoverArt shim 2026-06-30 19:52:29 +02:00
Psychotoxical 28e049425e refactor(music-network): move enrichment-primary icon/label hooks into music-network/ui 2026-06-30 19:48:54 +02:00
Psychotoxical 777c72adf7 refactor(lib): move library-index hooks into lib/library/hooks 2026-06-30 19:48:49 +02:00
Psychotoxical 1f93984482 refactor(hooks): move generic scroll/pagination/dnd UI hooks into lib/hooks 2026-06-30 19:34:28 +02:00
Psychotoxical 31bc24a178 refactor(hooks): co-locate feature-coupled hooks (playback server/navigate/timeline/audio-devices->playback, smart-collage/pending-polling->playlist, contextmenu rating/keyboard->contextMenu) 2026-06-30 19:30:41 +02:00
Psychotoxical e0d6623bf1 refactor(lyrics): co-locate lyrics feature into features/lyrics (LyricsPane + hooks + cache + lrclib/lyricsplus/netease providers; lyricsStore stays global) 2026-06-30 19:21:21 +02:00
Psychotoxical d9dfdf66e9 refactor(music-network): co-locate MusicNetworkIndicator + presetIcon into music-network/ui (barrel-exported) 2026-06-30 19:15:56 +02:00
Psychotoxical 15a6590e77 refactor(updater+contextMenu): co-locate updater feature (AppUpdater+Changelog) and the context-menu subsystem into features/ 2026-06-30 19:12:26 +02:00
Psychotoxical 7d2645171b refactor(components): co-locate remaining UI/feature components (TracklistColumnPicker+LosslessModeBanner->ui, fixedThemes->utils/themes, ThemeUpdateBanner/WindowButtonPreview/AboutPsysonicLol->settings, BottomNav/MobileMoreOverlay->sidebar, MobilePlayerView->nowPlaying, WhatsNewBanner->whatsNew) 2026-06-30 19:07:46 +02:00
Psychotoxical ba9e0d6541 refactor(app): move app-shell chrome into app/ (TitleBar, ConnectionIndicator, ErrorBoundary, AppShellQueueResizerSeam, dev overlays + PerfOverlaySparkline) 2026-06-30 19:04:37 +02:00
Psychotoxical d876781fcb refactor(components): co-locate feature-specific components (HostApprovalQueue->orbit, Playback{Delay,Schedule}->playback, LicensesPanel->settings) 2026-06-30 18:55:48 +02:00
Psychotoxical 9eee4eed5b refactor(components): GenreFilterBar -> ui/ (shared), SongCard -> features/home (sole consumer) 2026-06-30 18:50:46 +02:00
Psychotoxical 0a8d82fe2a refactor(ui): move GenreFilterBar (shared genre filter) into ui/ 2026-06-30 18:50:36 +02:00
Psychotoxical afa8999d37 refactor(search): co-locate tracks song-browse cluster (SongRow/PagedSongList/SongBrowseSection/TracksPageChrome/useSongBrowseList) into features/search 2026-06-30 18:41:31 +02:00
Psychotoxical 5c3a135040 refactor(playback): co-locate player bar UI (PlayerBar + playerBar/* + PlaybackBufferingOverlay) into features/playback/components 2026-06-30 18:37:06 +02:00
Psychotoxical 5cb5cf1d24 refactor(equalizer): co-locate equalizer UI into features/equalizer (eqStore/eqCurve stay audio-core) 2026-06-30 18:34:11 +02:00
Psychotoxical 41877d10d8 refactor(ui): move generic presentational primitives into ui/ (CoverLightbox, StarRating, LongPressWaveOverlay, icons, VirtualCardGrid) 2026-06-30 18:28:54 +02:00
Psychotoxical 8cd23e86db refactor(ui): move generic filter/list primitives into ui/ (filter buttons, SortDropdown, MarqueeText, SelectionToggleButton, InpageScrollSentinel) 2026-06-30 18:25:20 +02:00
Psychotoxical b3b7af0390 refactor(help): co-locate help page into features/help; pages/ now empty 2026-06-30 18:16:44 +02:00
Psychotoxical 1e3c7f1915 refactor(whatsNew): co-locate release-notes page into features/whatsNew 2026-06-30 18:16:44 +02:00
Psychotoxical 132db4e620 refactor(auth): co-locate login page into features/auth 2026-06-30 18:16:44 +02:00
Psychotoxical 2f4303ecc8 refactor(home): co-locate Mainstage/home feature into features/home 2026-06-30 18:12:27 +02:00
Psychotoxical f05183d412 refactor(randomMix): co-locate random/lucky-mix feature into features/randomMix 2026-06-30 18:08:15 +02:00
Psychotoxical f443a60c54 refactor(composers): co-locate composers feature into features/composers 2026-06-30 18:03:43 +02:00
Psychotoxical 5fdec6cd2a refactor(genre): co-locate genre browse pages into features/genre 2026-06-30 17:59:06 +02:00
Psychotoxical 5808ef0839 refactor(folderBrowser): co-locate folder-browser feature into features/folderBrowser 2026-06-30 17:57:17 +02:00
Psychotoxical ac6974d4f1 refactor(album): co-locate NewReleases + MostPlayed browse pages into features/album 2026-06-30 17:54:25 +02:00
Psychotoxical 8690e1529a docs(src): refresh CLAUDE.md + test/README.md key-file paths after the restructure
The frontend moves left the subsystem docs pointing at pre-campaign locations.
Update the src/CLAUDE.md key-files table + prose and the test/README.md examples
to current homes: playerStore/playAlbum -> features/playback, Track model +
songToTrack -> lib/media, sidebar -> features/sidebar, QueuePanel ->
features/queue, CachedImage/TooltipPortal/CustomSelect -> ui/, toast -> utils/ui,
subsonic/i18n -> lib, App.tsx split into app/{MainApp,AppShell,RequireAuth}.
Docs only; no code touched.
2026-06-30 17:32:34 +02:00
Psychotoxical 572dce4703 refactor(lib): move songToTrack + pure server-scope helpers to lib/media; split trackServerScope
songToTrack (the canonical Subsonic-song -> Track mapper) and the pure
server-scope helpers (activeServerProfileId, stampTrackServerId/s,
isMultiServerQueue, profileIdFromQueueRef) operate only on the lib/media model +
authStore + server-key utils -- no playback-store read. Move them to lib/media so
the ~58 app-wide consumers (cover, context menus, sharing, lucky-mix, library
browse, pages, hooks) depend on lib, not on the playback feature.

trackServerScope is split: the store-reading queue helpers (queueItemRefAt,
filterQueueRefs*, activeServerQueueTrackIds) stay in
features/playback/utils/playback/trackServerScope and build on the pure lib half.

With Track/QueueItemRef (prior commit), songToTrack and shuffleArray now in lib,
the media-domain model is fully out of features/playback. The remaining
playerStoreTypes imports are PlayerState only (the store shape). tsc 0, lint 0,
full suite 2353/2353, build OK.
2026-06-30 17:24:29 +02:00
Psychotoxical 9058abd340 refactor(lib): extract Track/QueueItemRef domain model to lib/media/trackTypes
Track is the app's normalized song model and QueueItemRef its thin queue
identity -- consumed app-wide (queue, cover, context menus, sharing, lucky-mix,
library browse), not a playback-internal detail. They lived in
features/playback/store/playerStoreTypes, forcing ~115 core/feature files to
reach into the playback feature for the central media type.

Move the two interfaces to lib/media/trackTypes; playerStoreTypes keeps
PlayerState and now imports them from lib. 115 importers repointed (mixed
'PlayerState, Track' lines split so PlayerState stays). Pure type move (erased at
runtime). tsc 0, lint 0.
2026-06-30 17:16:56 +02:00
Psychotoxical 2756cb7698 refactor(lib): relocate the local-library subsystem utils/library -> lib/library
utils/library is the local-index query/browse/sync engine -- feature-free shared
infra consumed by the album, search, genre, artist and offline features. With
its last feature edges removed (queue resolver, shuffleArray, albumBrowseCatalogChunk,
genreBrowsePlayback all relocated earlier this branch), the whole directory is
pure infra and belongs under lib/.

Whole-directory move (58 files, 76 consumers rewritten) via resolver-based
rewrite: cross-cluster relatives absolutized to @/, intra-cluster siblings kept
relative. utils/library is gone; lib/library imports zero @/features. Three
stale doc comments in the album/search/artist barrels repointed.

tsc 0, lint 0, full suite 2353/2353, production build OK.
2026-06-30 17:06:03 +02:00
Psychotoxical e5705f853e refactor(playback): move genreBrowsePlayback into the feature; utils/library now feature-free
genreBrowsePlayback is a "play X" queue builder -- it turns a genre seed into a
Track[] for the player, the genre analogue of playArtistShuffled which already
lives in features/playback/utils/playback. Consumed only by the 3 genre pages,
by no utils/library sibling, so the whole-file move is clean (no split). Its
Track/songToTrack edges become intra-feature; its remaining utils/library
siblings (advancedSearchLocal, albumBrowseSort, genreCatalogCountsCache,
genreAlbumBrowse, libraryReady) are plain infra deps.

utils/library now has ZERO @/features importers (source AND tests) -- the bulk
is ready to relocate to lib/library. tsc 0, lint 0, genre suite green.
2026-06-30 17:00:57 +02:00
Psychotoxical edab32d7ee refactor(lib): relocate shuffleArray to lib/util; albumBrowseCatalogChunk to features/album
shuffleArray is a pure generic Fisher-Yates over T[], misfiled in
features/playback. Move to lib/util (next to dedupeById): fixes the existing
core->feature edges (utils/componentHelpers/*) and drops one of
genreBrowsePlayback's playback deps.

albumBrowseCatalogChunk is the feature-layer orchestrator that picks the offline
branch on top of the pure lib catalog loaders; its sole consumer lives in
features/album and its offline dep is a legal feature->feature edge there. Its
.test.ts stays in utils/library -- that test exercises fetchLocalAlbumCatalogChunk
from albumBrowseLoad (misnamed), which is not moving.

utils/library now has a single remaining feature importer: genreBrowsePlayback
(songToTrack + type Track), blocked on the Track domain model living in
features/playback. tsc 0, lint 0, targeted suites green.
2026-06-30 16:55:18 +02:00
Psychotoxical f41005682d refactor(playback): move thin-state queue resolver out of utils/library into the feature
The queue-resolver family (queueTrackResolver, queueRestore, queueItemRef,
queueTrackView) runtime-imports usePlayerStore / playerStoreTypes — it is the
playback engine's thin-state queue subsystem, not shared library infra. It sat
in utils/library only because it queries the local index. Co-locate into
features/playback/store so those edges become intra-feature; its remaining deps
(advancedSearchLocal, libraryReady, serverLookup, serverIndexKey, authStore) are
plain core/infra (feature -> infra, no inversion).

Removes 4 of the 6 store/utils -> feature runtime inversions that blocked the
utils/library -> lib move. 8 files moved, 46 consumers rewritten. tsc 0, lint 0,
targeted suites green.
2026-06-30 16:50:29 +02:00
Psychotoxical cb1a110afb refactor(playback): move the audio engine into features/playback
Relocate the playback/queue/transport/audio-output engine out of the type-first
store/ + utils/playback/ + utils/audio/ dirs into a cohesive src/features/playback/,
structure-preserving:
  store/<x>                    -> features/playback/store/<x>
  store/audioListenerSetup/<x> -> features/playback/store/audioListenerSetup/<x>
  utils/playback/<x>           -> features/playback/utils/playback/<x>
  utils/audio/<x>              -> features/playback/utils/audio/<x>

184 files moved (107 source + 77 tests), 365 consumers rewritten. Pure move — no
behavior change, no state-split (the playerStore state-split stays a separate M5
question). Enabled by this session's decouple seams (artist/offline/orbit/auth →
core registries), so the engine carries no inbound core->feature inversion: store/
now holds only the 50 cross-cutting global stores (auth family, the seams, library
index, UI/settings stores).

KEPT OUT of the move (would re-create global->engine edges): the 3 pure config
helpers utils/audio/{loudnessPreAnalysisSlider,hiResCrossfadeResample} +
utils/playback/autodjOverlapCap (authStore + settings UI read them — they stay in
utils/). Ambiguous view-state stores (eqStore, queueToolbarStore,
playerBarLayoutStore) stay global (no engine imports).

Consumers use DEEP paths (@/features/playback/...), no barrel — matches the lib/
approach and avoids barrel-mock-collapse across the 140 usePlayerStore consumers.
Two tolerated type-only core->feature edges remain (localPlaybackStore->QueueItemRef,
localPlaybackMigration->HotCacheEntry, both erased).

tsc 0, lint 0, full suite 319/2353 green, iron-rule clean (no runtime store->feature
import). Behavior-touching only via the prerequisite bridge seam (already QA-flagged);
the move itself is pure.
2026-06-30 15:00:20 +02:00
Psychotoxical 6651abbc6f refactor(decouple): playback-engine bridge — break authStore→engine edges
The last core→engine inversions blocking the playback-core move live in the
authStore settings/profile family: authServerProfileActions reads
usePlayerStore.queueServerId + calls clearQueueServerForPlayback (on server
delete), and authAudioSettingsActions calls
usePlayerStore.updateReplayGainForCurrentTrack from ~8 ReplayGain/normalization
setters. Both are re-exported through the authStore barrel, so a global store
would depend on the (soon-to-move) engine.

Add core seam store/playbackEngineBridge.ts: a registry exposing getQueueServerId
/ clearQueueServerForPlayback / updateReplayGainForCurrentTrack with no-op/null
defaults. The engine registers its impls via store/playbackEngineBridgeRegister.ts
(side-effect-imported by MainApp at boot). authStore actions now call the neutral
delegators instead of importing the engine.

Default is safe: the only callers are user-triggered settings/profile actions that
fire long after boot (engine already registered); at boot there's no current track
or queue binding, so the no-op would be correct anyway. authStore.servers.test
imports the register module so removeServer's queue-clear runs through the real
engine wiring.

Prepares the playback-core move (Engine → features/playback): with this + keeping
the 3 config-helper utils in utils/ + tolerating 2 type-only edges, the engine has
no inbound core→feature inversions left. tsc 0, lint 0, suite 319/2353 green.
Behavior-touching (server-delete queue clear, settings→gain refresh) → Frank QA.
2026-06-30 14:45:34 +02:00
Psychotoxical 651a2adba4 refactor(decouple): orbit seam — invert orbit off the audio core
Add core seam store/orbitRuntime.ts: a registry (registerOrbitRuntime) exposing a
neutral session snapshot {role,phase,state} + an async bulkGuard, plus pure
derivations mirrored from the feature (isInOrbitSession, isOrbitPlaybackSyncActive,
estimateLivePosition). Default (unregistered) = neutral snapshot + bulkGuard allow
— bit-identical to today's no-session behavior, and a session can only start via
the topbar which loads the @/features/orbit barrel (→ registers) first.

features/orbit/utils/orbitBulkGuard registers the runtime at module init
(store-backed getSnapshot + the existing confirm-modal orbitBulkGuard as the gate).
The orbit feature keeps its own copies of the pure helpers for UI (incl. the
arg-form isOrbitPlaybackSyncActive(role,phase) used by two settings/player-bar
components), so nothing UI-facing changes.

Repointed the 9 audio-core sites (playbackRateStore, previewStore,
playbackReportSession, nextAction, resumeAction, playTrackAction,
queueMutationActions, playAlbum) from @/features/orbit to @/store/orbitRuntime;
state reads (useOrbitStore.getState().role/.state) become orbitSnapshot().
Migrated 6 audio-core test mocks to @/store/orbitRuntime via importOriginal-spread
(keeps registerOrbitRuntime callable). enqueueShareSearchPayload stays on the orbit
barrel (share util, not audio core) — its test mock unchanged.

Decouple Step 3 — last seam. The audio ENGINE is now free of @/features/* runtime
imports (only type-only edges + the non-engine composerBrowseSessionStore browse
store remain). Unblocks the playback-core move + utils/library→lib.

tsc 0, lint 0, full suite 319/2353 green.
NEEDS Frank's live-session QA before relying on it (host+guest bulk-replace modal,
guest catch-up, rate/preview/scrobble suppression).
2026-06-30 14:19:22 +02:00