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.