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).
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.
Final piece of the App.tsx slim-down. The `TauriEventBridge` component
— ZIP download progress, track-preview lifecycle, audio device
changed/reset, the full `cli:*` listener surface (audio-device-set,
instant-mix, library list/set, server list/set, search, player-command),
tray-icon visibility sync, in-app keybindings keydown handler, media
keys + tray actions, `shortcut:global-action` / `shortcut:run-action`,
seek-relative / seek-absolute / set-volume, the window:close-requested
+ app:force-quit flow (with the shared `performExit` Orbit teardown),
and the `psysonic --info` snapshot publisher — moves into
`src/app/TauriEventBridge.tsx`.
`MainApp` imports it from the new file. `App.tsx` is now 57 LOC: just
the `App()` default-export that branches between `MiniPlayerApp` and
`MainApp` after wiring the shared theme / font / track-preview
document attributes.
A.K.A. App.tsx 1453 → 57 LOC over Phase 2 (M0 + B.1 + B.2 + C.1 + C.2
+ D). Pure code move at every step — no behaviour change.
Pre-PR check: PASS (frontend tests, tsc, coverage gates, prod build,
backend tests, clippy, backend coverage gates).
Continues the App.tsx slim-down. Two pieces move out of the monolith:
- `src/app/AppRoutes.tsx` — the route table and its 32 lazy page imports.
AppShell now renders `<AppRoutes />` inside the existing `<Suspense>`;
the `perfFlags.disableMainRouteContentMount` placeholder stays in
AppShell because that branch is a layout concern, not a routing one.
`useIsMobile()` moves inside AppRoutes so the `/now-playing` mobile
swap stays self-contained.
- `src/app/RequireAuth.tsx` — the 4-line auth guard, with a focused test
that covers all three reject paths (no login, no active server id,
empty server list) plus the happy path. MainApp imports it from the
new file instead of routing through the App.tsx re-export.
Side-cleanup of imports that B.2 had already orphaned in App.tsx
(`version`, `initAudioListeners`, `lazy`, `Routes`, `Route`, `Navigate`,
`MobilePlayerView`).
`App.tsx` 1308 → 1232 LOC. `AppShell` stays in App.tsx for Phase C.2.
Pre-PR check: PASS (frontend tests, tsc, coverage gates, prod build,
backend tests, clippy, backend coverage gates).
The 186-LOC default export shrinks to a thin window-kind switch with
shared document-attribute hooks. The mini-player tree and the main-app
tree each move into their own module under src/app/.
- src/app/MiniPlayerApp.tsx (48 LOC):
DragDropProvider + MiniPlayer + cross-window storage sync
- src/app/MainApp.tsx (129 LOC):
BrowserRouter + Routes + main-only lifecycle hooks
(audio listeners, hot cache, global shortcuts, mini-player
bridge, easter egg, scrollbar auto-hide)
AppShell + RequireAuth + TauriEventBridge are now named exports from
App.tsx so MainApp can compose them; Phase C/D will extract those into
their own modules.
App.tsx: 1453 -> 1308 LOC. Behaviour-preserving.