Files
mcma-webui/src/components/layout/AppShell.tsx
T
Цвылев Александр Вадимович ac6b76bf98 feat(radio): wire radio + similarity into the queue (A11/B6, §6.5)
"Start radio" in the track menu seeds the queue from a track; RadioController
(headless, in AppShell) tops it up via /radio/next as it drains. QueuePanel's
radio scaffolding is now live: the exploration slider drives familiar<->new and
each entry shows its reason ("why this is playing"). Adds the radio endpoints
(start/next/similar), radio context + actions on the queue slice, and the pick->
entry mapper. Degrades to metadata picks while ML is offline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 02:40:13 +03:00

47 lines
1.3 KiB
TypeScript

import { Outlet } from 'react-router';
import { Suspense } from 'react';
import { Sidebar } from './Sidebar';
import { PersistentPlayer } from '../player/PersistentPlayer';
import { NowPlaying } from '../player/NowPlaying';
import { QueuePanel } from '../player/QueuePanel';
import { RadioController } from '../player/RadioController';
import { TrackInfoDrawer } from '../track/TrackInfoDrawer';
import { AddToPlaylistDialog } from '../track/AddToPlaylistDialog';
import { LoadingSkeleton } from '../common/LoadingSkeleton';
export function AppShell() {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
height: '100vh',
overflow: 'hidden',
}}
>
<div className="app-body">
<Sidebar />
<main className="app-main">
<div className="app-screen">
<Suspense
fallback={
<div style={{ padding: '2rem' }}>
<LoadingSkeleton />
</div>
}
>
<Outlet />
</Suspense>
</div>
</main>
<QueuePanel />
<TrackInfoDrawer />
</div>
<AddToPlaylistDialog />
<PersistentPlayer />
<NowPlaying />
<RadioController />
</div>
);
}