import { useState } from 'react'; import { Code, History, MessageSquare, Plus, Terminal, Workflow } from 'lucide-react'; import { api } from '@/api/client'; import type { FlowRunRow } from '@/api/types'; import { sessionEvents } from '@/hooks/sessionEvents'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; interface Props { onAddPane: (kind: 'chat' | 'terminal' | 'coder') => void; disabled?: boolean; projectId?: string; } function statusSymbol(status: FlowRunRow['status']): string { if (status === 'completed') return '✓'; if (status === 'failed') return '✗'; if (status === 'running') return '⟳'; return '–'; } function relativeTime(iso: string): string { const diff = Date.now() - new Date(iso).getTime(); const mins = Math.floor(diff / 60_000); if (mins < 60) return `${mins}m ago`; const hrs = Math.floor(mins / 60); if (hrs < 24) return `${hrs}h ago`; return `${Math.floor(hrs / 24)}d ago`; } function humanize(slug: string): string { return slug.replace(/[-_]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); } // v1.8 row-2 right cluster: mirrors the desktop Workspace.tsx Split dropdown. // Terminal + Coder items pass through to addSplitPane which creates panes // of the appropriate kind. Phase 11: optional projectId enables "Recent Flows" // section (runs history). export function NewPaneMenu({ onAddPane, disabled, projectId }: Props) { const [runs, setRuns] = useState(null); const [loadingRuns, setLoadingRuns] = useState(false); function handleOpenChange(open: boolean) { if (!open || !projectId || runs !== null) return; setLoadingRuns(true); api.runs.list(projectId) .then(({ runs: r }) => setRuns(r.slice(0, 8))) .catch(() => setRuns([])) .finally(() => setLoadingRuns(false)); } return ( onAddPane('chat')}> New BooChat onAddPane('terminal')}> New BooTerm onAddPane('coder')}> New BooCode {projectId && ( sessionEvents.emit({ type: 'open_flow_launcher', project_id: projectId, placement: 'new', }) } > New Orchestrator )} {projectId && ( <> Recent Flows {loadingRuns && ( Loading… )} {!loadingRuns && runs !== null && runs.length === 0 && ( No recent flows )} {!loadingRuns && runs !== null && runs.map((run) => ( { sessionEvents.emit({ type: 'open_orchestrator_pane', state: { run_id: run.id, flow_name: run.flow_name, band: run.band }, }); }} className="flex flex-col items-start gap-0.5 max-w-[260px]" >
{humanize(run.flow_name)} {run.band}
{statusSymbol(run.status)} {run.input.question} {relativeTime(run.created_at)}
))} )}
); }