import { MessageCircle, ScrollText, X, Copy, Check, Code, FlaskConical, Maximize2 } from 'lucide-react' import { useState } from 'react' import { createPortal } from 'react-dom' import { AgentAvatar } from './AgentAvatar' import type { ActiveAgent, AgentLogEntry, AgentType, BrowserScreenshot } from '../lib/types' import { AGENT_MASCOTS } from '../lib/types' import { Card, CardContent } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' interface AgentCardProps { agent: ActiveAgent onShowLogs?: (agentIndex: number) => void browserScreenshot?: BrowserScreenshot } // Get a friendly state description function getStateText(state: ActiveAgent['state']): string { switch (state) { case 'idle': return 'Standing by...' case 'thinking': return 'Pondering...' case 'working': return 'Coding away...' case 'testing': return 'Checking work...' case 'success': return 'Nailed it!' case 'error': return 'Trying plan B...' case 'struggling': return 'Being persistent...' default: return 'Busy...' } } // Get state color class function getStateColor(state: ActiveAgent['state']): string { switch (state) { case 'success': return 'text-primary' case 'error': return 'text-yellow-600' case 'struggling': return 'text-orange-500' case 'working': case 'testing': return 'text-primary' case 'thinking': return 'text-yellow-600' default: return 'text-muted-foreground' } } // Get agent type badge config function getAgentTypeBadge(agentType: AgentType): { label: string; className: string; icon: typeof Code } { if (agentType === 'testing') { return { label: 'TEST', className: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300', icon: FlaskConical, } } return { label: 'CODE', className: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300', icon: Code, } } export function AgentCard({ agent, onShowLogs, browserScreenshot }: AgentCardProps) { const isActive = ['thinking', 'working', 'testing'].includes(agent.state) const hasLogs = agent.logs && agent.logs.length > 0 const typeBadge = getAgentTypeBadge(agent.agentType || 'coding') const TypeIcon = typeBadge.icon const [screenshotExpanded, setScreenshotExpanded] = useState(false) return ( <> {/* Expanded screenshot overlay */} {screenshotExpanded && browserScreenshot && createPortal(
setScreenshotExpanded(false)} >
e.stopPropagation()} >
{AGENT_MASCOTS[agent.agentIndex % AGENT_MASCOTS.length]} {agent.agentType || 'coding'} {agent.featureName}
{`Browser
, document.body )} {/* Agent type badge */}
{typeBadge.label}
{/* Header with avatar and name */}
{agent.agentName}
{getStateText(agent.state)}
{/* Log button */} {hasLogs && onShowLogs && ( )}
{/* Feature info */}
{agent.featureIds && agent.featureIds.length > 1 ? ( <>
Batch: {agent.featureIds.map(id => `#${id}`).join(', ')}
Active: Feature #{agent.featureId}
) : ( <>
Feature #{agent.featureId}
{agent.featureName}
)}
{/* Browser screenshot thumbnail with expand */} {browserScreenshot && (
setScreenshotExpanded(true)} > Browser view
)} {/* Thought bubble */} {agent.thought && (

{agent.thought}

)}
) } // Log viewer modal component interface AgentLogModalProps { agent: ActiveAgent logs: AgentLogEntry[] onClose: () => void } export function AgentLogModal({ agent, logs, onClose }: AgentLogModalProps) { const [copied, setCopied] = useState(false) const typeBadge = getAgentTypeBadge(agent.agentType || 'coding') const TypeIcon = typeBadge.icon const handleCopy = async () => { const logText = logs .map(log => `[${log.timestamp}] ${log.line}`) .join('\n') await navigator.clipboard.writeText(logText) setCopied(true) setTimeout(() => setCopied(false), 2000) } const getLogColor = (type: AgentLogEntry['type']) => { switch (type) { case 'error': return 'text-destructive' case 'state_change': return 'text-primary' default: return 'text-foreground' } } return createPortal(
{ if (e.target === e.currentTarget) onClose() }} > {/* Header */}

{agent.agentName} Logs

{typeBadge.label}

{agent.featureIds && agent.featureIds.length > 1 ? `Batch: ${agent.featureIds.map(id => `#${id}`).join(', ')}` : `Feature #${agent.featureId}: ${agent.featureName}` }

{/* Log content */}
{logs.length === 0 ? (

No logs available

) : ( logs.map((log, idx) => (
[{new Date(log.timestamp).toLocaleTimeString()}] {' '} {log.line}
)) )}
{/* Footer */}
{logs.length} log entries
, document.body ) }