"use client"; import { useEffect, useState, useCallback } from "react"; import { useAppStore } from "@/store/app-store"; import { getElectronAPI } from "@/lib/electron"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { File, Folder, FolderOpen, ChevronRight, ChevronDown, RefreshCw, Code, } from "lucide-react"; import { cn } from "@/lib/utils"; interface FileTreeNode { name: string; path: string; isDirectory: boolean; children?: FileTreeNode[]; isExpanded?: boolean; } const IGNORE_PATTERNS = [ "node_modules", ".git", ".next", "dist", "build", ".DS_Store", "*.log", ]; const shouldIgnore = (name: string) => { return IGNORE_PATTERNS.some((pattern) => { if (pattern.startsWith("*")) { return name.endsWith(pattern.slice(1)); } return name === pattern; }); }; export function CodeView() { const { currentProject } = useAppStore(); const [fileTree, setFileTree] = useState([]); const [selectedFile, setSelectedFile] = useState(null); const [fileContent, setFileContent] = useState(""); const [isLoading, setIsLoading] = useState(true); const [expandedFolders, setExpandedFolders] = useState>( new Set() ); // Load directory tree const loadTree = useCallback(async () => { if (!currentProject) return; setIsLoading(true); try { const api = getElectronAPI(); const result = await api.readdir(currentProject.path); if (result.success && result.entries) { const entries = result.entries .filter((e) => !shouldIgnore(e.name)) .sort((a, b) => { // Directories first if (a.isDirectory && !b.isDirectory) return -1; if (!a.isDirectory && b.isDirectory) return 1; return a.name.localeCompare(b.name); }) .map((e) => ({ name: e.name, path: `${currentProject.path}/${e.name}`, isDirectory: e.isDirectory, })); setFileTree(entries); } } catch (error) { console.error("Failed to load file tree:", error); } finally { setIsLoading(false); } }, [currentProject]); useEffect(() => { loadTree(); }, [loadTree]); // Load subdirectory const loadSubdirectory = async (path: string): Promise => { try { const api = getElectronAPI(); const result = await api.readdir(path); if (result.success && result.entries) { return result.entries .filter((e) => !shouldIgnore(e.name)) .sort((a, b) => { if (a.isDirectory && !b.isDirectory) return -1; if (!a.isDirectory && b.isDirectory) return 1; return a.name.localeCompare(b.name); }) .map((e) => ({ name: e.name, path: `${path}/${e.name}`, isDirectory: e.isDirectory, })); } } catch (error) { console.error("Failed to load subdirectory:", error); } return []; }; // Load file content const loadFileContent = async (path: string) => { try { const api = getElectronAPI(); const result = await api.readFile(path); if (result.success && result.content) { setFileContent(result.content); setSelectedFile(path); } } catch (error) { console.error("Failed to load file:", error); } }; // Toggle folder expansion const toggleFolder = async (node: FileTreeNode) => { const newExpanded = new Set(expandedFolders); if (expandedFolders.has(node.path)) { newExpanded.delete(node.path); } else { newExpanded.add(node.path); // Load children if not already loaded if (!node.children) { const children = await loadSubdirectory(node.path); // Update the tree with children const updateTree = (nodes: FileTreeNode[]): FileTreeNode[] => { return nodes.map((n) => { if (n.path === node.path) { return { ...n, children }; } if (n.children) { return { ...n, children: updateTree(n.children) }; } return n; }); }; setFileTree(updateTree(fileTree)); } } setExpandedFolders(newExpanded); }; // Render file tree node const renderNode = (node: FileTreeNode, depth: number = 0) => { const isExpanded = expandedFolders.has(node.path); const isSelected = selectedFile === node.path; return (
{ if (node.isDirectory) { toggleFolder(node); } else { loadFileContent(node.path); } }} data-testid={`file-tree-item-${node.name}`} > {node.isDirectory ? ( <> {isExpanded ? ( ) : ( )} {isExpanded ? ( ) : ( )} ) : ( <> )} {node.name}
{node.isDirectory && isExpanded && node.children && (
{node.children.map((child) => renderNode(child, depth + 1))}
)}
); }; if (!currentProject) { return (

No project selected

); } if (isLoading) { return (
); } return (
{/* Header */}

Code Explorer

{currentProject.name}

{/* Split View */}
{/* File Tree */}
{fileTree.map((node) => renderNode(node))}
{/* Code Preview */}
{selectedFile ? (

{selectedFile.replace(currentProject.path, "")}

                    {fileContent}
                  
) : (

Select a file to view its contents

)}
); }