Split the screen into two registered panel apps that coordinate via a 'music:cwd' panel channel, like /chat: - music-browser (left): library selector, publishes the path. - music-detail (right): renders the path — album tracklist, artist discography sections, or a folder grid — and drives the app-wide player. MusicScreen is now a WorkspaceView over a horizontal 2-panel layout (persisted as screens/music), so the panels are resizable. Registered in AppRegistry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
import { useEffect, useMemo } from 'react';
|
|
import type { LayoutNode } from 'officerdev';
|
|
import { WorkspaceView } from 'officerdev';
|
|
import { useDashboardState } from 'state/useDashboardState';
|
|
import { defaultLayout } from './defaultLayout';
|
|
|
|
// /music uses the Workspace/Panel system (like /chat): two vertical panels — the library browser
|
|
// (music-browser) and the content/detail (music-detail) — coordinating via the 'music:cwd' channel.
|
|
|
|
const ALLOWED_APP_TYPES = new Set<string | null>(['music-browser', 'music-detail', null]);
|
|
|
|
function normalizeLayout(node: LayoutNode): LayoutNode {
|
|
if (node.type === 'panel') {
|
|
return ALLOWED_APP_TYPES.has(node.appType) ? node : { ...node, appType: 'music-detail' };
|
|
}
|
|
const children = node.children.map((c) => {
|
|
const fixed = normalizeLayout(c.node);
|
|
return fixed === c.node ? c : { ...c, node: fixed };
|
|
});
|
|
const changed = children.some((c, i) => c !== node.children[i]);
|
|
return changed ? { ...node, children } : node;
|
|
}
|
|
|
|
export const MusicScreen = () => {
|
|
const rawWorkspace = useDashboardState<LayoutNode>('screens/music', defaultLayout);
|
|
|
|
const workspace = useMemo(() => {
|
|
const fixed = normalizeLayout(rawWorkspace.value);
|
|
if (fixed === rawWorkspace.value) return rawWorkspace;
|
|
return { ...rawWorkspace, value: fixed };
|
|
}, [rawWorkspace]);
|
|
|
|
useEffect(() => {
|
|
if (rawWorkspace.isLoaded && workspace.value !== rawWorkspace.value) {
|
|
rawWorkspace.setValue(workspace.value);
|
|
}
|
|
}, [rawWorkspace.isLoaded, workspace.value, rawWorkspace.value]);
|
|
|
|
return (
|
|
<div className="h-full w-full pt-2">
|
|
<WorkspaceView workspace={workspace} locked />
|
|
</div>
|
|
);
|
|
};
|