import { useRef, useEffect, useMemo, useState, useCallback } from 'react'; import { Box, Typography, IconButton, Button, Tooltip } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'; import CodeIcon from '@mui/icons-material/Code'; import TerminalIcon from '@mui/icons-material/Terminal'; import ArticleIcon from '@mui/icons-material/Article'; import EditIcon from '@mui/icons-material/Edit'; import UndoIcon from '@mui/icons-material/Undo'; import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import CheckIcon from '@mui/icons-material/Check'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { useAgentStore } from '@/store/agentStore'; import { useLayoutStore } from '@/store/layoutStore'; import { processLogs } from '@/utils/logProcessor'; import JobStatusHeader from './JobStatusHeader'; export default function CodePanel() { const { panelContent, panelTabs, activePanelTab, setActivePanelTab, removePanelTab, plan, updatePanelTabContent, setEditedScript, activeJob } = useAgentStore(); const { setRightPanelOpen } = useLayoutStore(); const scrollRef = useRef(null); const textareaRef = useRef(null); const [isEditing, setIsEditing] = useState(false); const [editedContent, setEditedContent] = useState(''); const [originalContent, setOriginalContent] = useState(''); const [copied, setCopied] = useState(false); // Get the active tab content, or fall back to panelContent for backwards compatibility const activeTab = panelTabs.find(t => t.id === activePanelTab); const currentContent = activeTab || panelContent; // Check if this is an editable script tab const isEditableScript = activeTab?.id === 'script' && activeTab?.language === 'python'; const hasUnsavedChanges = isEditing && editedContent !== originalContent; // Sync edited content when switching tabs or content changes useEffect(() => { if (currentContent?.content && isEditableScript) { setOriginalContent(currentContent.content); if (!isEditing) { setEditedContent(currentContent.content); } } }, [currentContent?.content, isEditableScript, isEditing]); const handleStartEdit = useCallback(() => { if (currentContent?.content) { setEditedContent(currentContent.content); setOriginalContent(currentContent.content); setIsEditing(true); // Focus textarea after render setTimeout(() => textareaRef.current?.focus(), 0); } }, [currentContent?.content]); const handleCancelEdit = useCallback(() => { setEditedContent(originalContent); setIsEditing(false); }, [originalContent]); const handleSaveEdit = useCallback(() => { if (activeTab && editedContent !== originalContent) { // Update the panel tab content updatePanelTabContent(activeTab.id, editedContent); // Store the edited script for approval - use tool_call_id from parameters const toolCallId = activeTab.parameters?.tool_call_id; if (toolCallId) { setEditedScript(toolCallId, editedContent); } setOriginalContent(editedContent); } setIsEditing(false); }, [activeTab, editedContent, originalContent, updatePanelTabContent, setEditedScript]); const handleCopy = useCallback(async () => { const contentToCopy = isEditing ? editedContent : (currentContent?.content || ''); if (contentToCopy) { try { await navigator.clipboard.writeText(contentToCopy); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { console.error('Failed to copy:', err); } } }, [isEditing, editedContent, currentContent?.content]); const displayContent = useMemo(() => { if (!currentContent?.content) return ''; // Apply log processing only for text/logs, not for code/json if (!currentContent.language || currentContent.language === 'text') { return processLogs(currentContent.content); } return currentContent.content; }, [currentContent?.content, currentContent?.language]); useEffect(() => { // Auto-scroll only for logs tab if (scrollRef.current && activePanelTab === 'logs') { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [displayContent, activePanelTab]); const hasTabs = panelTabs.length > 0; return ( {/* Header - Fixed 60px to align */} {hasTabs ? ( {panelTabs.map((tab) => { const isActive = activePanelTab === tab.id; // Choose icon based on tab type let icon = ; if (tab.id === 'script' || tab.language === 'python') { icon = ; } else if (tab.id === 'tool_output' || tab.language === 'markdown' || tab.language === 'json') { icon = ; } return ( setActivePanelTab(tab.id)} sx={{ display: 'flex', alignItems: 'center', gap: 0.5, px: 1.5, py: 0.75, borderRadius: 1, cursor: 'pointer', fontSize: '0.7rem', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em', color: isActive ? 'var(--text)' : 'var(--muted-text)', bgcolor: isActive ? 'rgba(255,255,255,0.08)' : 'transparent', border: '1px solid', borderColor: isActive ? 'rgba(255,255,255,0.1)' : 'transparent', transition: 'all 0.15s ease', '&:hover': { bgcolor: 'rgba(255,255,255,0.05)', }, }} > {icon} {tab.title} { e.stopPropagation(); removePanelTab(tab.id); }} sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', ml: 0.5, width: 16, height: 16, borderRadius: '50%', fontSize: '0.65rem', opacity: 0.5, '&:hover': { opacity: 1, bgcolor: 'rgba(255,255,255,0.1)', }, }} > ✕ ); })} ) : ( {currentContent?.title || 'Code Panel'} )} {/* Copy button */} {currentContent?.content && ( {copied ? : } )} {/* Edit controls for script tab */} {isEditableScript && !isEditing && ( )} {isEditing && ( <> )} setRightPanelOpen(false)} sx={{ color: 'var(--muted-text)' }}> {/* Job Status Header - shown when there's an active job */} {activeJob && (activePanelTab === 'logs' || activePanelTab === 'script') && ( )} {/* Main Content Area */} {!currentContent ? ( NO DATA LOADED ) : ( {activeTab?.id === 'tool_output' && ( Tool output )} {currentContent.content ? ( isEditing && isEditableScript ? (