import React, { useState, useEffect, useRef } from 'react'; import { X, Copy, Check, Terminal, HardHat, Search, Wand2, CheckCircle2, ArrowLeft, Play, Send, MessageSquare, Sparkles, Loader2, History, FileCode, Plus, Minus } from 'lucide-react'; import { generateCodeWithAgents, AgentStatus, generateCodePatches, applyPatches, DEFAULT_MODEL } from '../services/geminiService'; import { Node, Edge } from 'reactflow'; interface Message { role: 'user' | 'assistant'; content: string; summary?: string; files_changed?: string[]; } interface PatchHistory { id: string; timestamp: Date; summary: string; files_changed: string[]; patches: { search: string, replace: string }[]; } interface CodeViewerProps { nodes: Node[]; edges: Edge[]; promptText: string; isOpen: boolean; onClose: () => void; isLoading: boolean; } const CodeViewer: React.FC = ({ nodes, edges, promptText, isOpen, onClose, isLoading }) => { const [copied, setCopied] = useState(false); const [viewMode, setViewMode] = useState<'prompt' | 'building' | 'code'>('prompt'); const [finalCode, setFinalCode] = useState(''); const [agentStatus, setAgentStatus] = useState('idle'); const [agentMessage, setAgentMessage] = useState(''); // Chat State const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(''); const [isChatting, setIsChatting] = useState(false); const chatEndRef = useRef(null); // History & Diff State const [patchHistory, setPatchHistory] = useState([]); const [isHistoryOpen, setIsHistoryOpen] = useState(false); const [selectedHistoryId, setSelectedHistoryId] = useState(null); const scrollToBottom = () => { chatEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(() => { scrollToBottom(); }, [messages]); // Reset state when opening/closing or changing input useEffect(() => { if (isOpen) { setViewMode('prompt'); setFinalCode(''); setAgentStatus('idle'); setMessages([]); setIsChatting(false); setPatchHistory([]); setIsHistoryOpen(false); setSelectedHistoryId(null); } }, [isOpen, promptText]); const handleSendMessage = async () => { if (!inputValue.trim() || isChatting) return; const userMsg = inputValue.trim(); setInputValue(''); setMessages(prev => [...prev, { role: 'user', content: userMsg }]); setIsChatting(true); try { const patchResult = await generateCodePatches(finalCode, userMsg, DEFAULT_MODEL); const updatedCode = applyPatches(finalCode, patchResult.patches); const newPatch: PatchHistory = { id: Date.now().toString(), timestamp: new Date(), summary: patchResult.summary, files_changed: patchResult.files_changed, patches: patchResult.patches }; setPatchHistory(prev => [newPatch, ...prev]); setFinalCode(updatedCode); setMessages(prev => [...prev, { role: 'assistant', content: patchResult.reasoning || "I've applied the requested changes to the code.", summary: patchResult.summary, files_changed: patchResult.files_changed }]); } catch (error) { setMessages(prev => [...prev, { role: 'assistant', content: "Sorry, I encountered an error while trying to apply those changes." }]); } finally { setIsChatting(false); } }; const handleCopy = (text: string) => { navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 2000); }; const handleBuildWithAgents = async () => { setViewMode('building'); setAgentStatus('architect'); setAgentMessage('Initializing agents...'); try { const result = await generateCodeWithAgents(nodes, edges, promptText, (status, msg) => { setAgentStatus(status); setAgentMessage(msg); }); setFinalCode(result.code); // Add initial evaluation/patches to chat if (result.evaluation) { const initialMsgs: Message[] = [ { role: 'assistant', content: `I've completed the initial build. Here's my self-evaluation: - Score: ${result.evaluation.score}/100 - Missing Components: ${result.evaluation.missing_components.length > 0 ? result.evaluation.missing_components.join(', ') : 'None'} - Potential Bugs: ${result.evaluation.bugs.length > 0 ? result.evaluation.bugs.join(', ') : 'None'}` } ]; if (result.initialPatches) { const patchHistoryItem: PatchHistory = { id: 'initial-auto-fix', timestamp: new Date(), summary: result.initialPatches.summary, files_changed: result.initialPatches.files_changed, patches: result.initialPatches.patches }; setPatchHistory([patchHistoryItem]); initialMsgs.push({ role: 'assistant', content: `I've automatically applied some improvements to address the evaluation findings.`, summary: result.initialPatches.summary, files_changed: result.initialPatches.files_changed }); } else if (result.autoFixError) { initialMsgs.push({ role: 'assistant', content: `I attempted to auto-fix the issues, but the auto-fixer failed: ${result.autoFixError}` }); } else if (result.evaluation.score < 90 && result.evaluation.improvement_tasks.length > 0) { initialMsgs.push({ role: 'assistant', content: `I skipped auto-fixing because patching was not successful or no patches were applied.` }); } setMessages(initialMsgs); } // Small delay to show completion state setTimeout(() => { setViewMode('code'); }, 1000); } catch (error) { setAgentStatus('error'); setAgentMessage('Generation failed. Please try again.'); setTimeout(() => setViewMode('prompt'), 2000); } }; const getStepStatus = (step: AgentStatus, current: AgentStatus) => { const order = ['idle', 'architect', 'critic', 'refiner', 'evaluator', 'patcher', 'complete']; const stepIdx = order.indexOf(step); const currentIdx = order.indexOf(current); if (current === 'error') return 'text-slate-500'; if (currentIdx > stepIdx) return 'text-emerald-400'; if (currentIdx === stepIdx) return 'text-blue-400 animate-pulse'; return 'text-slate-600'; }; const renderAgentStep = (step: AgentStatus, icon: React.ReactNode, label: string) => { const statusColor = getStepStatus(step, agentStatus); const isCurrent = agentStatus === step; const order = ['architect', 'critic', 'refiner', 'evaluator', 'patcher', 'complete']; const isDone = order.indexOf(agentStatus) > order.indexOf(step); return (
{isDone ? : icon}
{label}
{isCurrent && (
{agentMessage}
)}
{isCurrent &&
}
); }; if (!isOpen) return null; return (
{/* Header */}

{viewMode === 'prompt' && Architecture Prompt} {viewMode === 'building' && Agents Building...} {viewMode === 'code' && Final Code} {isLoading && Building...}

{viewMode === 'code' && ( )} {viewMode !== 'building' && (
{viewMode === 'code' && ( )}
)} {viewMode === 'prompt' && !isLoading && ( )}
{/* Content */}
{/* View: Loading Prompt */} {isLoading && viewMode === 'prompt' && (

Refining architecture prompt...

)} {/* View: Prompt Text */} {!isLoading && viewMode === 'prompt' && (
{promptText}
)} {/* View: Agents Building */} {viewMode === 'building' && (
{renderAgentStep('architect', , "Coder Agent: Writing Implementation")} {renderAgentStep('critic', , "Reviewer Agent: Analyzing Logic")} {renderAgentStep('refiner', , "Polisher Agent: Finalizing Code")} {renderAgentStep('evaluator', , "Evaluator Agent: Verifying Requirements")} {renderAgentStep('patcher', , "Patcher Agent: Auto-fixing bugs")}
{agentStatus === 'complete' && (
Done! Loading code...
)}
)} {/* View: Final Code */} {viewMode === 'code' && (
{finalCode}
{/* History/Diff Overlay */} {isHistoryOpen && (

Change Logs

{/* History List */}
{patchHistory.length === 0 ? (
No changes yet.
) : ( patchHistory.map((h) => ( )) )}
{/* Diff Viewer */}
{selectedHistoryId ? (
{patchHistory.find(h => h.id === selectedHistoryId)?.patches.map((p, i) => (
Patch {i + 1}
{p.search}
{p.replace}
))}
) : (

Select a change log to view the diff.

)}
)} {/* Chat Sidebar */}
AI Assistant
{messages.length === 0 && (

Ask me to modify the code or add features.

)} {messages.map((msg, i) => (
{msg.content} {msg.role === 'assistant' && msg.summary && (
Update Complete
{msg.summary}
{msg.files_changed && msg.files_changed.length > 0 && (
{msg.files_changed.length} file{msg.files_changed.length > 1 ? 's' : ''} changed
)}
)}
))} {isChatting && (
Applying patches...
)}
setInputValue(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSendMessage()} placeholder="Request changes..." className="w-full bg-slate-950 border border-slate-700 rounded-lg pl-3 pr-10 py-2 text-xs text-slate-200 focus:outline-none focus:border-blue-500 transition-colors" />
)}
); }; export default CodeViewer;