import { useState, useEffect } from 'react'; import { ChevronRight, Play, Loader2, ShieldCheck } from 'lucide-react'; import { toast } from 'sonner'; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { api } from '@/api/client'; import { sessionEvents } from '@/hooks/sessionEvents'; import { cn } from '@/lib/utils'; import { FLOW_INFO } from './flowBlurbs'; type Band = 'small' | 'medium' | 'large'; type Category = 'Analysis' | 'Discovery' | 'Planning' | 'Authoring' | 'Review'; interface FlowMeta { name: string; label: string; } const CATEGORIES: Category[] = ['Analysis', 'Discovery', 'Planning', 'Authoring', 'Review']; const FLOWS_BY_CATEGORY: Record = { Analysis: [ { name: 'research', label: 'Research' }, { name: 'investigate', label: 'Investigate' }, { name: 'architectural-analysis', label: 'Architectural Analysis' }, { name: 'security-review', label: 'Security Review' }, { name: 'gap-analysis', label: 'Gap Analysis' }, { name: 'data-review', label: 'Data Review' }, { name: 'devops-review', label: 'DevOps Review' }, { name: 'issue-triage', label: 'Issue Triage' }, ], Discovery: [ { name: 'project-discovery', label: 'Project Discovery' }, { name: 'project-documentation', label: 'Project Documentation' }, { name: 'test-planning', label: 'Test Planning' }, ], Planning: [ { name: 'plan-a-feature', label: 'Plan a Feature' }, { name: 'plan-implementation', label: 'Plan Implementation' }, { name: 'plan-a-phased-build', label: 'Plan a Phased Build' }, { name: 'plan-work-items', label: 'Plan Work Items' }, { name: 'iterative-plan-review', label: 'Iterative Plan Review' }, ], Authoring: [ { name: 'adr', label: 'ADR' }, { name: 'coding-standard', label: 'Coding Standard' }, { name: 'runbook', label: 'Runbook' }, { name: 'tdd', label: 'TDD' }, { name: 'stakeholder-summary', label: 'Stakeholder Summary' }, ], Review: [ { name: 'code-review', label: 'Code Review' }, ], }; const BAND_LABELS: { value: Band; label: string }[] = [ { value: 'small', label: 'Small' }, { value: 'medium', label: 'Medium' }, { value: 'large', label: 'Large' }, ]; export function FlowLauncherDialog() { const [open, setOpen] = useState(false); const [projectId, setProjectId] = useState(''); const [placement, setPlacement] = useState<'new' | 'split'>('new'); const [category, setCategory] = useState('Analysis'); const [band, setBand] = useState('small'); const [focus, setFocus] = useState(''); const [fast, setFast] = useState(false); // Which flow row is expanded (one at a time) vs. which flow is mid-launch. // Expand (chevron) and launch (Run) are deliberately separate actions. const [expandedFlow, setExpandedFlow] = useState(null); const [launchingFlow, setLaunchingFlow] = useState(null); useEffect(() => { return sessionEvents.subscribe((ev) => { if (ev.type !== 'open_flow_launcher') return; setProjectId(ev.project_id); setPlacement(ev.placement ?? 'new'); // Reset to defaults each time the dialog is opened. setCategory('Analysis'); setBand('small'); setFocus(''); setFast(false); setExpandedFlow(null); setLaunchingFlow(null); setOpen(true); }); }, []); function handleCategoryChange(cat: Category) { setCategory(cat); setExpandedFlow(null); } // Per-row launch. Behaviour is identical to the old bottom Launch button: // POST the run, then emit open_orchestrator_pane carrying the placement. async function handleRun(name: string) { if (!name || !projectId || launchingFlow) return; setLaunchingFlow(name); try { const { run_id } = await api.runs.launch({ project_id: projectId, flow_name: name, band, input: { question: focus, ...(fast ? { concise: true } : {}) }, }); sessionEvents.emit({ type: 'open_orchestrator_pane', state: { run_id, flow_name: name, band }, placement, }); setOpen(false); } catch (err) { toast.error(err instanceof Error ? err.message : 'Failed to launch flow'); } finally { setLaunchingFlow(null); } } const flows = FLOWS_BY_CATEGORY[category]; return ( Launch a flow {/* Read-only assurance — flows never touch your code. */}
Flows are read-only — they analyze and report, never modify your code.
{/* Scrollable body */}
{/* Run options — apply to whichever flow you Run below. */}
{/* Focus/question field */}
setFocus(e.target.value)} />
{/* Size selector */}
{BAND_LABELS.map(({ value, label }) => ( ))}
{/* Fast mode toggle */}
Fast mode Fewer agents, quicker results
{/* Flow picker */}
Choose a flow {/* Category tabs — horizontal-scroll strip on mobile */}
{CATEGORIES.map((cat) => ( ))}
{/* Flow list */}
{flows.map((flow) => { const info = FLOW_INFO[flow.name]; const isExpanded = expandedFlow === flow.name; const isLaunching = launchingFlow === flow.name; const panelId = `flow-blurb-${flow.name}`; return (
{/* Expand toggle — reveals the blurb; never launches. */} {/* Run — the only launch action; clearly separate from expand. */}
{/* In-place blurb — grid-rows trick for a smooth, reduced-motion-safe expand. */}

{info?.blurb ?? ''}

); })}
); }