From 9f79a7f580fc1362375df4268806aa7b1d2b9672 Mon Sep 17 00:00:00 2001 From: Arpandeep Khatua Date: Fri, 22 Nov 2024 17:53:43 -0800 Subject: [PATCH] filesystem refresh works (kind of) --- .../experimental/nodes/frontend/src/App.tsx | 18 +++- .../src/components/CodeEditor/FileSystem.css | 29 ++++++ .../src/components/CodeEditor/FileSystem.tsx | 23 ++++- .../frontend/src/hooks/useFileSystems.ts | 91 ++++++++++++++++++- 4 files changed, 155 insertions(+), 6 deletions(-) diff --git a/examples/experimental/nodes/frontend/src/App.tsx b/examples/experimental/nodes/frontend/src/App.tsx index e4455fb1..b80f4faf 100644 --- a/examples/experimental/nodes/frontend/src/App.tsx +++ b/examples/experimental/nodes/frontend/src/App.tsx @@ -65,7 +65,8 @@ const App: React.FC = () => { handleFileClose, handleFileChange, addFile, - setActiveFile + setActiveFile, + updateFileSystemFromList } = useFileSystem(); // State for various components and messages @@ -112,6 +113,18 @@ const App: React.FC = () => { // Log the message for debugging console.log('Parsed message:', messageData); + // Handle file structure refresh response + if (messageData.data?.data_type === "text" && + messageData.data.text.includes("CmdOutputObservation") && + messageData.data.text.includes("/workspace")) { + + const parts = messageData.data.text.split("**CmdOutputObservation (source=None, exit code=0)**"); + if (parts.length > 1) { + const fileList = parts[1].trim().split('\n').filter(Boolean); + updateFileSystemFromList(fileList); + } + } + } catch (error) { // Log any errors that occur during message parsing console.error('Error parsing message:', error); @@ -124,7 +137,7 @@ const App: React.FC = () => { // Clean up the listener on component unmount socket.off('new_message', handleNewMessage); }; - }, []); + }, [updateFileSystemFromList]); // Function to handle actions from agents const handleAgentAction = (messageData: any) => { @@ -227,6 +240,7 @@ const App: React.FC = () => { )} diff --git a/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.css b/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.css index 0cb76cac..89a56ce9 100644 --- a/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.css +++ b/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.css @@ -15,6 +15,10 @@ font-size: 13px; border-bottom: 1px solid #333; font-weight: bold; + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 12px; } .file-item { @@ -61,3 +65,28 @@ .file-icon { margin-right: 6px; } + + .refresh-button { + background: none; + border: none; + cursor: pointer; + padding: 4px; + color: #666; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + } + + .refresh-button:hover { + background-color: #2a2a2a; + color: #fff; + } + + .refresh-button svg { + transition: transform 0.3s ease; + } + + .refresh-button:active svg { + transform: rotate(180deg); + } diff --git a/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.tsx b/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.tsx index 2c991831..5329068c 100644 --- a/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.tsx +++ b/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.tsx @@ -15,22 +15,25 @@ * Props: * - fileSystem: An array of FileNode objects representing the file system structure. * - onFileSelect: A callback function to handle the selection of a file. + * - socket: A socket object to handle communication with the backend. * */ import React, { useState } from 'react'; -import { ChevronRight, ChevronDown, File } from 'lucide-react'; +import { ChevronRight, ChevronDown, File, RefreshCw } from 'lucide-react'; import { SiHtml5, SiCss3, SiJavascript, SiPython, SiTypescript, SiJson, SiMarkdown } from 'react-icons/si'; import './FileSystem.css'; // Import the CSS file import { FileNode } from '../../types/FileSystem'; // Import the FileNode type +import { Socket } from 'socket.io-client'; // Define the props for the FileSystem component interface FileSystemProps { fileSystem: FileNode[]; onFileSelect: (path: string) => void; + socket: Socket; // Add socket prop } // Function to get the appropriate file icon based on the file extension @@ -50,9 +53,14 @@ const getFileIcon = (fileName: string) => { }; // Main FileSystem component definition -export const FileSystem: React.FC = ({ fileSystem, onFileSelect }) => { +export const FileSystem: React.FC = ({ fileSystem, onFileSelect, socket }) => { const [expandedFolders, setExpandedFolders] = useState>(new Set(['/workspace'])); // Track expanded folders + const handleRefresh = () => { + // Send command to get file structure + socket.emit('terminal_command', "find /workspace -type f"); + }; + // Toggle the expansion state of a folder const toggleFolder = (folderName: string, e: React.MouseEvent) => { e.stopPropagation(); // Prevent event bubbling @@ -110,7 +118,16 @@ export const FileSystem: React.FC = ({ fileSystem, onFileSelect return ( <> -
Folders
+
+ Folders + +
{fileSystem.map(node => node.type === 'folder' ? renderFolder(node) : renderItem(node) // Render the file system diff --git a/examples/experimental/nodes/frontend/src/hooks/useFileSystems.ts b/examples/experimental/nodes/frontend/src/hooks/useFileSystems.ts index 3e0ae0e1..017daae0 100644 --- a/examples/experimental/nodes/frontend/src/hooks/useFileSystems.ts +++ b/examples/experimental/nodes/frontend/src/hooks/useFileSystems.ts @@ -20,6 +20,7 @@ * - handleFileChange: Function to update the content of an open file. * - addFile: Function to add a new file to the file system. * - setActiveFile: Function to set the currently active file. + * - updateFileSystemFromList: Function to update the file system state when receiving the file list from the server. * */ @@ -154,6 +155,93 @@ export const useFileSystem = () => { }); }; + const updateFileSystemFromList = (fileList: string[]) => { + // Initialize root workspace folder + const newTree: FileNode[] = [{ + name: 'workspace', + type: 'folder', + path: '/workspace', + children: [] + }]; + + // Process each file path + fileList.forEach(filePath => { + // Remove the leading /workspace/ and split the remaining path + const relativePath = filePath.replace(/^\/workspace\//, ''); + const segments = relativePath.split('/').filter(Boolean); + + if (segments.length === 0) return; // Skip if it's just the workspace folder + + let currentLevel = newTree[0].children!; + let currentPath = '/workspace'; + + // Process each segment of the path + segments.forEach((segment, index) => { + currentPath += '/' + segment; + + // If we're at the last segment, it's a file + if (index === segments.length - 1) { + currentLevel.push({ + name: segment, + type: 'file', + path: currentPath + }); + } else { + // It's a folder + let folder = currentLevel.find( + node => node.type === 'folder' && node.name === segment + ); + + // Create folder if it doesn't exist + if (!folder) { + folder = { + name: segment, + type: 'folder', + path: currentPath, + children: [] + }; + currentLevel.push(folder); + } + + currentLevel = folder.children!; + } + }); + }); + + // Sort folders and files + const sortNodes = (nodes: FileNode[]) => { + return nodes.sort((a, b) => { + // Folders come before files + if (a.type !== b.type) { + return a.type === 'folder' ? -1 : 1; + } + // Alphabetical sorting within same type + return a.name.localeCompare(b.name); + }); + }; + + // Recursively sort all levels + const sortRecursive = (node: FileNode) => { + if (node.children) { + node.children = sortNodes(node.children); + node.children.forEach(child => { + if (child.type === 'folder') { + sortRecursive(child); + } + }); + } + }; + + // Sort the tree + sortRecursive(newTree[0]); + + // Update the file system state while preserving existing file contents + setFileSystem(prev => ({ + ...prev, + tree: newTree + })); + }; + return { fileSystem, openFiles, @@ -162,6 +250,7 @@ export const useFileSystem = () => { handleFileClose, handleFileChange, addFile, - setActiveFile + setActiveFile, + updateFileSystemFromList }; };