/* ── FileTree component for Files tab in ConversationPage ── * Displays workspace files with tabs for list/tree views, matching IDE mockup layout */ import { useState, useCallback } from "react"; export interface FileNode { name: string; path: string; type: "file" | "folder"; children?: FileNode[]; size?: number; } import { ChevronDown, ChevronRight, File, Folder } from "lucide-react"; interface FileTreeProps { files: FileNode[]; onOpenFile?: (path: string) => void; editable?: boolean; viewMode?: "list" | "tree"; } export function FileTree({ files, onOpenFile, editable = true, viewMode = "tree" }: FileTreeProps) { const [expandedFolders, setExpandedFolders] = useState>(new Set()); const [viewModeState, setViewModeState] = useState(viewMode); const toggleFolder = useCallback((path: string) => { setExpandedFolders(prev => { const next = new Set(prev); if (next.has(path)) next.delete(path); else next.add(path); return next; }); }, []); const formatFileSize = (bytes?: number) => { if (!bytes) return "--"; if (bytes < 1024) return bytes + " B"; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB"; return (bytes / (1024 * 1024)).toFixed(1) + " MB"; }; const renderFile = (file: FileNode, depth = 0) => { const isFolder = file.type === "folder"; const isExpanded = expandedFolders.has(file.path); const indent = depth * 20; return (
{ e.currentTarget.style.background = 'var(--surface-hover)'; }} onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }} onClick={() => { if (isFolder) { toggleFolder(file.path); } else if (onOpenFile) { onOpenFile(file.path); } }} >
{isFolder ? ( ) : ( )}
{isFolder ? ( ) : (
)} {file.name} {!isFolder && file.size && ( {formatFileSize(file.size)} )} {!isFolder && editable && (
)}
{isFolder && isExpanded && file.children && file.children.length > 0 && (
{file.children.map(child => renderFile(child, depth + 1))}
)}
); }; return (

Files

{files.filter(f => f.type === "file").length} files
{viewModeState === "tree" ? ( files.map(file => renderFile(file)) ) : (
{files.filter(f => f.type === "file").map(file => renderFile(file))}
)}
); }