import { useState, useMemo, useCallback } from 'react'; import { ChevronRight, ChevronDown, Folder } from 'lucide-react'; import { getIcon } from 'material-file-icons'; import { useFiles, type DirEntry } from 'widgets/FileBrowser'; type FileTreeProps = { root: string; basePath: string; onOpenFile: (path: string, name: string) => void; }; type TreeNodeProps = { entry: DirEntry; parentPath: string; root: string; onOpenFile: (path: string, name: string) => void; depth: number; }; const FileIcon = ({ name }: { name: string }) => { const svg = useMemo(() => getIcon(name).svg, [name]); return ; }; const sortEntries = (entries: DirEntry[]) => { const dirs = entries.filter((e) => e.type === 'directory').sort((a, b) => a.name.localeCompare(b.name)); const files = entries.filter((e) => e.type === 'file').sort((a, b) => a.name.localeCompare(b.name)); return [...dirs, ...files]; }; const TreeNode = ({ entry, parentPath, root, onOpenFile, depth }: TreeNodeProps) => { const [expanded, setExpanded] = useState(false); const [children, setChildren] = useState(null); const [loading, setLoading] = useState(false); const { listDir } = useFiles(root); const isDir = entry.type === 'directory'; const fullPath = parentPath === '/' ? `/${entry.name}` : `${parentPath}/${entry.name}`; const handleClick = useCallback(async () => { if (!isDir) { onOpenFile(fullPath, entry.name); return; } if (!expanded && children === null) { setLoading(true); try { const res = await listDir(fullPath); setChildren(sortEntries(res.entries)); } catch { setChildren([]); } setLoading(false); } setExpanded((prev) => !prev); }, [isDir, expanded, children, fullPath, entry.name, listDir, onOpenFile]); return ( {isDir ? ( <> {expanded ? ( ) : ( )} > ) : ( <> > )} {entry.name} {loading && ...} {isDir && expanded && children && ( {children.map((child) => ( ))} )} ); }; export const FileTree = ({ root, basePath, onOpenFile }: FileTreeProps) => { const { listDir } = useFiles(root); const [entries, setEntries] = useState(null); const [loading, setLoading] = useState(false); const loadRoot = useCallback(async () => { setLoading(true); try { const res = await listDir(basePath); setEntries(sortEntries(res.entries)); } catch { setEntries([]); } setLoading(false); }, [listDir, basePath]); if (entries === null && !loading) { loadRoot(); } if (loading && entries === null) { return Loading...; } if (!entries || entries.length === 0) { return No files; } return ( {entries.map((entry) => ( ))} ); };