import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Bot, Plus, Loader2, Play, Clock, CheckCircle, XCircle, Trash2, Import, ChevronDown, FileJson, Globe, Download } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from '@/components/ui/dialog'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Toast } from '@/components/ui/toast'; import { api, type Agent, type AgentRunWithMetrics } from '@/lib/api'; import { useTabState } from '@/hooks/useTabState'; import { useTranslation } from '@/hooks/useTranslation'; import { formatISOTimestamp } from '@/lib/date-utils'; import { open as openDialog, save } from '@tauri-apps/plugin-dialog'; import { invoke } from '@tauri-apps/api/core'; import { GitHubAgentBrowser } from '@/components/GitHubAgentBrowser'; interface AgentsModalProps { open: boolean; onOpenChange: (open: boolean) => void; } export const AgentsModal: React.FC = ({ open, onOpenChange }) => { const { t } = useTranslation(); const [activeTab, setActiveTab] = useState('agents'); const [agents, setAgents] = useState([]); const [runningAgents, setRunningAgents] = useState([]); const [loading, setLoading] = useState(true); const [agentToDelete, setAgentToDelete] = useState(null); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [toast, setToast] = useState<{ message: string; type: "success" | "error" } | null>(null); const [showGitHubBrowser, setShowGitHubBrowser] = useState(false); const { createAgentTab, createCreateAgentTab } = useTabState(); // Load agents when modal opens useEffect(() => { if (open) { loadAgents(); loadRunningAgents(); } }, [open]); // Refresh running agents periodically useEffect(() => { if (!open) return; const interval = setInterval(() => { loadRunningAgents(); }, 3000); // Refresh every 3 seconds return () => clearInterval(interval); }, [open]); const loadAgents = async () => { try { setLoading(true); const agentList = await api.listAgents(); setAgents(agentList); } catch (error) { console.error('Failed to load agents:', error); } finally { setLoading(false); } }; const loadRunningAgents = async () => { try { const runs = await api.listRunningAgentSessions(); const agentRuns = runs.map(run => ({ id: run.id, agent_id: run.agent_id, agent_name: run.agent_name, task: run.task, model: run.model, status: 'running' as const, created_at: run.created_at, project_path: run.project_path, } as AgentRunWithMetrics)); setRunningAgents(agentRuns); } catch (error) { console.error('Failed to load running agents:', error); } }; const handleRunAgent = async (agent: Agent) => { // Create a new agent execution tab const tabId = `agent-exec-${agent.id}-${Date.now()}`; // Close modal onOpenChange(false); // Dispatch event to open agent execution in the new tab window.dispatchEvent(new CustomEvent('open-agent-execution', { detail: { agent, tabId } })); }; const handleDeleteAgent = async (agent: Agent) => { setAgentToDelete(agent); setShowDeleteDialog(true); }; const confirmDelete = async () => { if (!agentToDelete?.id) return; try { await api.deleteAgent(agentToDelete.id); loadAgents(); // Refresh the list setShowDeleteDialog(false); setAgentToDelete(null); } catch (error) { console.error('Failed to delete agent:', error); } }; const handleOpenAgentRun = (run: AgentRunWithMetrics) => { // Create new tab for this agent run createAgentTab(run.id!.toString(), run.agent_name); onOpenChange(false); }; const handleCreateAgent = () => { // Close modal and create new tab onOpenChange(false); createCreateAgentTab(); }; const handleImportFromFile = async () => { try { const filePath = await openDialog({ multiple: false, filters: [{ name: 'JSON', extensions: ['json'] }] }); if (filePath) { const agent = await api.importAgentFromFile(filePath as string); loadAgents(); // Refresh list setToast({ message: `Agent "${agent.name}" imported successfully`, type: "success" }); } } catch (error) { console.error('Failed to import agent:', error); setToast({ message: "Failed to import agent", type: "error" }); } }; const handleImportFromGitHub = () => { setShowGitHubBrowser(true); }; const handleExportAgent = async (agent: Agent) => { try { const exportData = await api.exportAgent(agent.id!); const filePath = await save({ defaultPath: `${agent.name.toLowerCase().replace(/\s+/g, '-')}.json`, filters: [{ name: 'JSON', extensions: ['json'] }] }); if (filePath) { await invoke('write_file', { path: filePath, content: JSON.stringify(exportData, null, 2) }); setToast({ message: "Agent exported successfully", type: "success" }); } } catch (error) { console.error('Failed to export agent:', error); setToast({ message: "Failed to export agent", type: "error" }); } }; const getStatusIcon = (status: string) => { switch (status) { case 'running': return ; case 'completed': return ; case 'failed': return ; default: return ; } }; return ( <> {t('agents.agentManagement')} {t('agents.createNewOrManageAgents')} {t('agents.availableAgents')} {t('agents.runningAgents')} {runningAgents.length > 0 && ( {runningAgents.length} )}
{/* Action buttons at the top */}
{t('agents.importFromFile')} {t('agents.importFromGitHub')}
{loading ? (
) : agents.length === 0 ? (

{t('agents.noAgentsAvailable')}

{t('agents.createFirstAgentToGetStarted')}

) : (
{agents.map((agent) => (

{agent.name}

{agent.default_task && (

{agent.default_task}

)}
))}
)}
{runningAgents.length === 0 ? (

{t('agents.noRunningAgents')}

{t('agents.agentExecutionsWillAppear')}

) : (
{runningAgents.map((run) => ( handleOpenAgentRun(run)} >

{getStatusIcon(run.status)} {run.agent_name}

{run.task}

Started: {formatISOTimestamp(run.created_at)} {run.model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'}
))}
)}
{/* Delete Confirmation Dialog */} {t('agents.deleteAgentTitle')} {t('agents.deleteConfirmation', { name: agentToDelete?.name })}
{/* GitHub Agent Browser */} setShowGitHubBrowser(false)} onImportSuccess={() => { setShowGitHubBrowser(false); loadAgents(); // Refresh the agents list setToast({ message: "Agent imported successfully", type: "success" }); }} /> {/* Toast notifications */} {toast && ( setToast(null)} /> )} ); }; export default AgentsModal;