import { useState, useEffect } from 'react' import { Folder, File, ChevronRight, RefreshCw, X, FileCode, FileJson, FileText, ArrowLeft } from 'lucide-react' interface FileEntry { name: string type: 'file' | 'directory' size: number | null } interface TrialFilesPanelProps { trialPath: string // e.g., "main-with-skills-pass1/task-name__xyz123" onClose: () => void } function getFileIcon(name: string) { const ext = name.split('.').pop()?.toLowerCase() switch (ext) { case 'json': return case 'py': case 'ts': case 'js': case 'tsx': case 'jsx': return case 'txt': case 'md': case 'log': return default: return } } function formatSize(bytes: number | null): string { if (bytes === null) return '' if (bytes < 1024) return `${bytes} B` if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` return `${(bytes / (1024 * 1024)).toFixed(1)} MB` } export function TrialFilesPanel({ trialPath, onClose }: TrialFilesPanelProps) { const [currentPath, setCurrentPath] = useState('') const [files, setFiles] = useState([]) const [loading, setLoading] = useState(true) const [fileContent, setFileContent] = useState(null) const [selectedFile, setSelectedFile] = useState(null) const [fileLoading, setFileLoading] = useState(false) const [error, setError] = useState(null) const fetchFiles = async (subPath: string = '') => { setLoading(true) setError(null) try { const params = new URLSearchParams({ path: trialPath }) if (subPath) params.append('subpath', subPath) const res = await fetch(`/api/files?${params}`) if (!res.ok) throw new Error('Failed to load files') const data = await res.json() setFiles(data.files) setCurrentPath(subPath) } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load files') } finally { setLoading(false) } } const fetchFileContent = async (filePath: string) => { setFileLoading(true) setSelectedFile(filePath) setError(null) try { const fullPath = currentPath ? `${currentPath}/${filePath}` : filePath const params = new URLSearchParams({ path: trialPath, file: fullPath }) const res = await fetch(`/api/file?${params}`) if (!res.ok) throw new Error('Failed to load file') const contentType = res.headers.get('content-type') if (contentType?.includes('application/json')) { const json = await res.json() setFileContent(JSON.stringify(json, null, 2)) } else { const data = await res.json() setFileContent(data.content) } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load file') setFileContent(null) } finally { setFileLoading(false) } } useEffect(() => { fetchFiles() }, [trialPath]) // Close on escape key useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose() } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [onClose]) const handleEntryClick = (entry: FileEntry) => { if (entry.type === 'directory') { const newPath = currentPath ? `${currentPath}/${entry.name}` : entry.name fetchFiles(newPath) setFileContent(null) setSelectedFile(null) } else { fetchFileContent(entry.name) } } const goUp = () => { const parentPath = currentPath.split('/').slice(0, -1).join('/') fetchFiles(parentPath) setFileContent(null) setSelectedFile(null) } const trialName = trialPath.split('/').pop() || trialPath const jobName = trialPath.split('/')[0] || '' return ( // Modal overlay
{ if (e.target === e.currentTarget) onClose() }} > {/* Modal content */}
{/* Header */}
{trialName} {jobName}
{/* Breadcrumb */}
{currentPath && currentPath.split('/').map((part, idx, arr) => ( ))}
{/* Content */}
{/* File list */}
{loading ? (
) : error ? (
{error}
) : (
{currentPath && ( )} {files.map((entry) => ( ))} {files.length === 0 && (
Empty directory
)}
)}
{/* File viewer */} {fileContent !== null && (
{fileLoading ? (
) : (
                  {fileContent}
                
)}
)}
) }