First slice of the authStore split. Pure code-move: no behaviour change.
- `authStoreTypes.ts` — `ServerProfile`, `AuthState`, plus all union
types (`SeekbarStyle`, `LoggingMode`, `NormalizationEngine`,
`DiscordCoverSource`, `LoudnessLufsPreset`, `LyricsSourceId`,
`LyricsSourceConfig`, `TrackPreviewLocation`, `TrackPreviewLocations`).
- `authStoreDefaults.ts` — `LOUDNESS_LUFS_PRESETS`,
`DEFAULT_LOUDNESS_PRE_ANALYSIS_ATTENUATION_DB`,
`TRACK_PREVIEW_LOCATIONS`, `DEFAULT_TRACK_PREVIEW_LOCATIONS`,
`DEFAULT_LYRICS_SOURCES`, `MIX_MIN_RATING_FILTER_MAX_STARS`,
`RANDOM_MIX_SIZE_OPTIONS`.
- `authStoreHelpers.ts` — `generateId`,
`sanitizeLoudnessLufsPreset`, `sanitizeLoudnessPreAnalysisFromStorage`,
`clampMixFilterMinStars`, `clampRandomMixSize`,
`clampSkipStarThreshold`, `skipStarCountStorageKey`,
`sanitizeSkipStarCounts`.
12 external call sites migrated to direct imports from the new
modules (no re-export shims left in authStore.ts — applies the
[feedback_prevent_god_modules] rule 5: avoid re-export debt).
authStore.ts: 889 → 518 LOC (−371).
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.
MiniPlayer.test.tsx (4): mounts without throwing, renders the
always-present titlebar controls (Pin + Open main window — Close is
Linux-only and lives on the manual smoke list per pick 4a), click on
Open main window / Close does not throw, clicking the Pin button
flips the alwaysOnTop label between "Unpin" and "Pin on top". Bridge
contract (mini:ready / mini:sync, geometry persistence) deferred to
B-tier phase B5 -- jsdom does not model two webviews.
FullscreenPlayer.test.tsx (9): renders the labelled Fullscreen Player
dialog + Close Fullscreen control. Control wiring: Close calls
onClose, Stop calls stop, Previous calls previous, Next calls next,
Repeat cycles via toggleRepeat.
§4.5 of the v2 plan -- useCachedUrl(coverUrl, coverKey, false)
regression. Mocks the CachedImage module so the call args are
observable. Pins:
- the 500 px cover-art call passes opt=false (no fetchUrl fallback;
prevents double crossfade fetchUrl -> blobUrl);
- the 300 px art-box call passes opt=true (default behaviour).
A refactor that "tidies up" the useCachedUrl call sites would silently
regress the FS player cover; this test makes it loud.
Harness fix: vi.mock for @tauri-apps/api/event now returns an async
emit that resolves -- components chain .catch() on emit which crashed
on the bare vi.fn() return value during first-render useEffect.
Benefits any future component test that mounts something using emit.
Frontend suite: 399 -> 412 tests (+13). F5 (and Phases F0-F6) complete.
QueuePanel.test.tsx (8): empty-queue affordance, one row per queue track
with matching data-queue-idx, track titles render. Toolbar exposes
Shuffle queue / Save Playlist / Load Playlist / Copy queue share link /
Clear via aria-label; Shuffle is disabled when the queue has fewer than
2 tracks.
§4.4 of the v2 plan -- DnD architecture pin. Queue rows do NOT declare
draggable=true (no HTML5 native drag); the source file has no
dataTransfer.setData / dataTransfer.getData / onDragStart / onDrop JSX
usage; no application/json MIME anywhere. The project's psy-drop custom
event system sidesteps WebView2's text/plain-only restriction by
avoiding HTML5 DnD entirely -- a refactor that re-introduces native DnD
on the queue would silently break Windows.
PlayerBar.test.tsx (9): renders the labelled "Music Player" region.
Surfaces Previous Track / Play / Next Track / Repeat / Stop when a
track is loaded. The middle control flips between "Play" and "Pause"
based on isPlaying. Control wiring: clicking Play/Pause calls
togglePlay, Previous calls previous, Next calls next, Repeat cycles
off -> all -> one, Stop calls stop. The region landmark is still
rendered when no track is loaded.
Adds a generic scrollIntoView no-op stub in src/test/mocks/browser.ts --
jsdom does not implement it and QueuePanel's queueAutoScroll calls it
on mount with auto-advance enabled. Benefits any future component test
that touches scroll affordances.
Frontend suite: 382 -> 399 tests (+17).
trackShape.test.ts: songToTrack mapping (required, optional, replayGain
flattening, missing-block fallback, no invented flags); resolveReplayGainDb
precedence (disabled, track, album, auto with prev/next neighbour match,
missing albumId fallback, both gains missing); shuffleArray (non-mutating,
preserves multiset, copies, empty/single, deterministic under mocked RNG).
queue.test.ts: enqueue / enqueueAt (auto-added separator placement,
queueIndex shift, index clamping), playNext (playNextAdded tag, position,
empty input), clearQueue (state reset, audio_stop call), reorderQueue
(splice, currentTrack-id-following queueIndex), removeTrack (clamp on
shrink, keep when after cursor), undo / redo (empty stacks return false,
rollback, replay, new edit drops pending redo).
Adds a 5-line test-only export _resetQueueUndoStacksForTest in playerStore.ts
because the undo/redo arrays live at module scope outside the Zustand state
graph; storeReset.ts now calls it so resetPlayerStore is fully complete.
playerStore.ts coverage 4.76% -> 18.1% lines. PR 2b + 2c push further toward
the F1 floor of 50%.