import { useEffect, useRef, useCallback } from "react"; import { useFileManager } from "../../hooks/useFileManager"; interface Props { projectId: string; projectName: string; onClose: () => void; } function formatSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`; } export default function FileManagerModal({ projectId, projectName, onClose }: Props) { const { currentPath, entries, loading, error, navigate, goUp, refresh, downloadFile, uploadFile, } = useFileManager(projectId); const overlayRef = useRef(null); // Load initial directory useEffect(() => { navigate("/workspace"); }, [navigate]); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [onClose]); const handleOverlayClick = useCallback( (e: React.MouseEvent) => { if (e.target === overlayRef.current) onClose(); }, [onClose], ); // Build breadcrumbs from current path const breadcrumbs = currentPath === "/" ? [{ label: "/", path: "/" }] : currentPath.split("/").reduce<{ label: string; path: string }[]>((acc, part, i) => { if (i === 0) { acc.push({ label: "/", path: "/" }); } else if (part) { const parentPath = acc[acc.length - 1].path; const fullPath = parentPath === "/" ? `/${part}` : `${parentPath}/${part}`; acc.push({ label: part, path: fullPath }); } return acc; }, []); return (
{/* Header */}

Files — {projectName}

{/* Path bar */}
{breadcrumbs.map((crumb, i) => ( {i > 0 && /} ))}
{/* Content */}
{error && (
{error}
)} {loading && entries.length === 0 ? (
Loading...
) : ( {/* Go up entry */} {currentPath !== "/" && ( goUp()} className="cursor-pointer hover:bg-[var(--bg-tertiary)] transition-colors" > )} {entries.map((entry) => ( entry.is_directory && navigate(entry.path)} className={`${ entry.is_directory ? "cursor-pointer" : "" } hover:bg-[var(--bg-tertiary)] transition-colors`} > ))} {entries.length === 0 && !loading && ( )}
..
{entry.is_directory ? "📁 " : ""}{entry.name} {!entry.is_directory && formatSize(entry.size)} {entry.modified} {!entry.is_directory && ( )}
Empty directory
)}
{/* Footer */}
); }