import { useCallback, type ReactNode } from 'react'; import { Group, Panel, Separator } from 'react-resizable-panels'; import { X, PanelRightOpen, PanelRightClose } from 'lucide-react'; /** * Resizable dual-panel layout for chat + artifact/detail panel. * * Uses react-resizable-panels v4 API: * - Left panel: Chat area (always visible) * - Right panel: Artifact/detail viewer (collapsible) * - Draggable resize handle between panels * - Persisted panel sizes via localStorage * * Side-panel toggle is injected into the chat header via `headerAction` * so it sits cleanly in the top-right corner (Trae Solo style). */ interface ResizableChatLayoutProps { chatPanel: ReactNode; rightPanel?: ReactNode; rightPanelTitle?: string; rightPanelOpen?: boolean; onRightPanelToggle?: (open: boolean) => void; } const STORAGE_KEY = 'zclaw-layout-panels'; const LEFT_PANEL_ID = 'chat-panel'; const RIGHT_PANEL_ID = 'detail-panel'; function loadPanelSizes(): { left: string; right: string } { try { const stored = localStorage.getItem(STORAGE_KEY); if (stored) { const parsed = JSON.parse(stored); if (parsed.left && parsed.right) { return { left: parsed.left, right: parsed.right }; } } } catch { /* ignore */ } return { left: '65%', right: '35%' }; } function savePanelSizes(layout: Record) { try { const left = layout[LEFT_PANEL_ID]; const right = layout[RIGHT_PANEL_ID]; if (left !== undefined && right !== undefined) { localStorage.setItem(STORAGE_KEY, JSON.stringify({ left, right })); } } catch { /* ignore */ } } export function ResizableChatLayout({ chatPanel, rightPanel, rightPanelTitle = '详情', rightPanelOpen = false, onRightPanelToggle, }: ResizableChatLayoutProps) { const sizes = loadPanelSizes(); const handleToggle = useCallback(() => { onRightPanelToggle?.(!rightPanelOpen); }, [rightPanelOpen, onRightPanelToggle]); if (!rightPanelOpen || !rightPanel) { // Panel closed: just render chat panel, no floating button return (
{chatPanel}
); } return (
savePanelSizes(layout)} > {/* Left panel: Chat */}
{chatPanel}
{/* Resize handle */}
{/* Right panel: Artifact/Detail */}
{/* Panel header */}
{rightPanelTitle}
{/* Panel content */}
{rightPanel}
); } /** * Toggle button for embedding in chat header. * Renders PanelRightOpen/PanelRightClose icon — Trae Solo style. */ export function PanelToggleButton({ panelOpen, onToggle, }: { panelOpen: boolean; onToggle: () => void; }) { return ( ); }