"use client"; import { useState, useMemo } from "react"; import { FolderOpen, FileCode2, Folder, ChevronRight, ChevronDown, FileJson, } from "lucide-react"; import classNames from "classnames"; import { Page } from "@/types"; import { useEditor } from "@/hooks/useEditor"; import { Button } from "@/components/ui/button"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; interface FileNode { name: string; path: string; type: "file" | "folder"; children?: FileNode[]; page?: Page; } export function FileBrowser() { const { pages, currentPage, setCurrentPage, setPreviewPage, globalEditorLoading, project, } = useEditor(); const [open, setOpen] = useState(false); const [expandedFolders, setExpandedFolders] = useState>( new Set(["/"]) ); const toggleFolder = (path: string) => { setExpandedFolders((prev) => { const next = new Set(prev); if (next.has(path)) { next.delete(path); } else { next.add(path); } return next; }); }; const fileTree = useMemo(() => { const root: FileNode = { name: "root", path: "/", type: "folder", children: [], }; pages.forEach((page) => { const parts = page.path.split("/").filter(Boolean); let currentNode = root; parts.forEach((part, index) => { const isFile = index === parts.length - 1; const currentPath = "/" + parts.slice(0, index + 1).join("/"); if (!currentNode.children) { currentNode.children = []; } let existingNode = currentNode.children.find((n) => n.name === part); if (!existingNode) { existingNode = { name: part, path: currentPath, type: isFile ? "file" : "folder", children: isFile ? undefined : [], page: isFile ? page : undefined, }; currentNode.children.push(existingNode); } if (!isFile) { currentNode = existingNode; } }); }); // Sort: folders first, then files, both alphabetically const sortNodes = (nodes: FileNode[] = []): FileNode[] => { return nodes .sort((a, b) => { if (a.type !== b.type) { return a.type === "folder" ? -1 : 1; } return a.name.localeCompare(b.name); }) .map((node) => ({ ...node, children: node.children ? sortNodes(node.children) : undefined, })); }; root.children = sortNodes(root.children); return root; }, [pages]); const getFileIcon = (path: string) => { const extension = path.split(".").pop()?.toLowerCase(); switch (extension) { case "html": return ( ); case "css": return ( ); case "js": case "jsx": return ( ); case "json": return ; default: return ; } }; const getFileExtension = (path: string) => { return path.split(".").pop()?.toLowerCase() || ""; }; const getLanguageTag = (path: string) => { const extension = path.split(".").pop()?.toLowerCase(); switch (extension) { case "html": return { name: "HTML", color: "bg-orange-500/20 border-orange-500/30 text-orange-400", }; case "css": return { name: "CSS", color: "bg-blue-500/20 border-blue-500/30 text-blue-400", }; case "js": return { name: "JS", color: "bg-yellow-500/20 border-yellow-500/30 text-yellow-400", }; case "json": return { name: "JSON", color: "bg-amber-500/20 border-amber-500/30 text-amber-400", }; default: return { name: extension?.toUpperCase() || "FILE", color: "bg-neutral-500/20 border-neutral-500/30 text-neutral-400", }; } }; const currentPageData = pages.find((p) => p.path === currentPage); const renderFileTree = (nodes: FileNode[], depth = 0) => { return nodes.map((node) => { if (node.type === "folder") { const isExpanded = expandedFolders.has(node.path); return (
toggleFolder(node.path)} > {isExpanded ? ( ) : ( )} {node.name} {node.children?.length || 0}
{isExpanded && node.children && renderFileTree(node.children, depth + 1)}
); } else { const isActive = currentPage === node.page?.path; return (
{ if (node.page) { setCurrentPage(node.page.path); if ( node.page.path.endsWith(".html") && !node.page.path.includes("components") ) { setPreviewPage(node.page.path); } setOpen(false); } }} >
{getFileIcon(node.name)}
{node.name} {getFileExtension(node.name)} {isActive && (
)}
); } }); }; return (
{/* VS Code-style Tab Bar */}
{currentPageData && (
{getFileIcon(currentPageData.path)} {currentPageData.path} {getLanguageTag(currentPageData.path).name}
)}
{/* Open Explorer Button */}

Open File Explorer ({pages.length}{" "} {pages.length === 1 ? "file" : "files"})

Explorer
{project?.space_id || "No space selected"} {pages.length || 0}
{fileTree.children && renderFileTree(fileTree.children)}
HTML:{" "} {pages.filter((p) => p.path.endsWith(".html")).length}
CSS:{" "} {pages.filter((p) => p.path.endsWith(".css")).length}
JS: {pages.filter((p) => p.path.endsWith(".js")).length}
JSON:{" "} {pages.filter((p) => p.path.endsWith(".json")).length}
); }