Workspaces layout, automation page

This commit is contained in:
2026-02-18 17:04:32 +00:00
parent 89f23f9426
commit 485ae4c3d7
89 changed files with 2168 additions and 514 deletions
@@ -0,0 +1,104 @@
import { useEffect, useMemo, useRef } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useClient } from 'hooks/useClient';
import { useAuth } from 'hooks/useAuth';
import { usePanelChannel } from 'hooks/usePanelChannel';
import type { LayoutNode, PanelComponents } from '@/components/Workspace';
import { WorkspaceLayout } from '@/components/Workspace';
import { useUserState } from '@/state/useUserState';
import { appRegistry } from '../Workspaces/app-registry';
import { AutomationRightPanel } from './AutomationRightPanel';
import type { AutomationSelection } from './AutomationRightPanel';
import { AutomationEditChat } from './AutomationEditChat';
import { AutomationSidebar } from './AutomationSidebar';
const CHAT_PANEL_ID = 'automation-chat';
const baseLayout: LayoutNode = {
type: 'group',
id: 'automation-root',
direction: 'horizontal',
children: [
{ node: { type: 'panel', id: 'automation-left', appType: null }, size: 20 },
{ node: { type: 'panel', id: 'automation-right', appType: null }, size: 80 },
],
};
const splitLayout: LayoutNode = {
type: 'group',
id: 'automation-root',
direction: 'horizontal',
children: [
{ node: { type: 'panel', id: 'automation-left', appType: null }, size: 20 },
{
node: {
type: 'group',
id: 'automation-right-group',
direction: 'vertical',
children: [
{ node: { type: 'panel', id: 'automation-right', appType: null }, size: 50 },
{ node: { type: 'panel', id: CHAT_PANEL_ID, appType: null }, size: 50 },
],
},
size: 80,
},
],
};
export const Automation = () => {
const client = useClient();
const { isAuthenticated } = useAuth();
const { isFetched } = useQuery({
queryKey: ['USER_STATE'],
enabled: isAuthenticated,
queryFn: () => client.get('/user/state'),
staleTime: Infinity,
});
const [savedSizes, setSavedSizes] = useUserState<number[] | null>('automation:chat-sizes', null);
const [selection] = usePanelChannel<AutomationSelection>('automation:selected-capability', null);
const editing = selection?.editing ?? false;
const prevEditing = useRef(editing);
const layout = useMemo(() => {
if (!editing) return baseLayout;
const l = structuredClone(splitLayout);
if (savedSizes && l.type === 'group') {
const rightGroup = l.children[1]!.node;
if (rightGroup.type === 'group') {
rightGroup.children[0]!.size = savedSizes[0] ?? 50;
rightGroup.children[1]!.size = savedSizes[1] ?? 50;
}
}
return l;
}, [editing, savedSizes]);
const handleLayoutChange = (newLayout: LayoutNode) => {
if (!editing || newLayout.type !== 'group') return;
const rightChild = newLayout.children[1]?.node;
if (rightChild?.type === 'group') {
const sizes = rightChild.children.map((c) => c.size);
setSavedSizes(sizes);
}
};
useEffect(() => {
prevEditing.current = editing;
}, [editing]);
const panelComponents: PanelComponents = useMemo(
() => ({
'automation-left': AutomationSidebar,
'automation-right': AutomationRightPanel,
[CHAT_PANEL_ID]: AutomationEditChat,
}),
[],
);
if (!isFetched) return null;
return (
<div className="h-full w-full pt-2">
<WorkspaceLayout layout={layout} onLayoutChange={handleLayoutChange} registry={appRegistry} components={panelComponents} />
</div>
);
};