From a05ff89d994d107bb9651b61be9e645812212f97 Mon Sep 17 00:00:00 2001 From: Arpandeep Khatua Date: Thu, 21 Nov 2024 22:04:35 -0800 Subject: [PATCH] Fix logic in file system The exist file in the workspace are just dummy folders that don't exist in the runtime. --- .../experimental/nodes/frontend/src/App.tsx | 107 ++++++++++++++++-- .../src/components/CodeEditor/CodeEditor.tsx | 6 +- .../src/components/CodeEditor/FileSystem.tsx | 49 ++++---- .../nodes/frontend/src/types/FileSystem.ts | 14 +++ 4 files changed, 134 insertions(+), 42 deletions(-) create mode 100644 examples/experimental/nodes/frontend/src/types/FileSystem.ts diff --git a/examples/experimental/nodes/frontend/src/App.tsx b/examples/experimental/nodes/frontend/src/App.tsx index 574b3012..b1e91242 100644 --- a/examples/experimental/nodes/frontend/src/App.tsx +++ b/examples/experimental/nodes/frontend/src/App.tsx @@ -8,6 +8,7 @@ import { ChatInterface } from './components/ChatInterface/ChatInterface'; import { Browser } from './components/Browser/Browser'; import { Sidebar } from './components/Sidebar/Sidebar'; import { SceneContext } from './components/Sidebar/SceneContext'; +import { FileSystemState, FileNode } from './types/FileSystem'; const socket = io('http://localhost:8000', { transports: ['websocket'], @@ -29,14 +30,45 @@ type FilesType = { type PanelOption = 'fileSystem' | 'sceneContext'; const App: React.FC = () => { - const [files, setFiles] = useState({ - "/workspace/index.html": "\n\n\n \n Document\n\n\n

Hello World

\n\n", - "/workspace/style.css": "body {\n background-color: #f0f0f0;\n font-family: Arial, sans-serif;\n}", - "/workspace/script.js": "console.log('Hello, World!');", - "/workspace/interview.py": "# Python code here" + const [fileSystem, setFileSystem] = useState({ + tree: [ + { + name: 'workspace', + type: 'folder', + path: '/workspace', + children: [ + { + name: 'index.html', + type: 'file', + path: '/workspace/index.html', + }, + { + name: 'style.css', + type: 'file', + path: '/workspace/style.css', + }, + { + name: 'script.js', + type: 'file', + path: '/workspace/script.js', + }, + { + name: 'main.py', + type: 'file', + path: '/workspace/main.py', + } + ] + } + ], + files: { + '/workspace/index.html': '\n...', + '/workspace/style.css': 'body {\n background-color: #f0f0f0;...', + '/workspace/script.js': 'console.log("Hello, World!");', + '/workspace/main.py': '# Python code here' + } }); - const [currentFile, setCurrentFile] = useState("/workspace/interview.py"); + const [currentFile, setCurrentFile] = useState("/workspace/main.py"); const [messages, setMessages] = useState>([]); const [terminalMessages, setTerminalMessages] = useState([]); const [activeTab, setActiveTab] = useState<'editor' | 'browser'>('editor'); @@ -75,6 +107,51 @@ const App: React.FC = () => { }; }, []); + const addFile = (path: string, content: string) => { + setFileSystem(prev => { + // Split path into parts + const parts = path.split('/').filter(Boolean); + const fileName = parts[parts.length - 1]; + + // Create new file node + const newFile: FileNode = { + name: fileName, + type: 'file', + path: path + }; + + // Update tree structure + const newTree = [...prev.tree]; + let currentLevel = newTree; + for (let i = 0; i < parts.length - 1; i++) { + const folder = currentLevel.find(node => + node.type === 'folder' && node.name === parts[i] + ); + if (folder && folder.children) { + currentLevel = folder.children; + } + } + + // Add new file to appropriate level if it doesn't exist + if (!currentLevel.find(node => node.path === path)) { + currentLevel.push(newFile); + } + + // Update files content + return { + tree: newTree, + files: { + ...prev.files, + [path]: content + } + }; + }); + + // Automatically switch to the new/updated file + setCurrentFile(path); + setActiveTab('editor'); + }; + const handleAgentAction = (messageData: any) => { const actionType = messageData.data.action_type; const agentName = messageData.data.agent_name; @@ -94,8 +171,7 @@ const App: React.FC = () => { case "write": const filePath = messageData.data.path; const fileContent = messageData.data.argument; - setFiles(prev => ({ ...prev, [filePath]: fileContent })); - setActiveTab('editor'); + addFile(filePath, fileContent); setMessages(prev => [...prev, { text: `${agentName} is writing code...`, type: 'status' as const @@ -142,7 +218,10 @@ const App: React.FC = () => {
{activePanel === 'fileSystem' && (
- + setCurrentFile(path)} + />
)} {activePanel === 'sceneContext' && ( @@ -168,9 +247,15 @@ const App: React.FC = () => { {activeTab === 'editor' ? (
- setFiles(prevFiles => ({ ...prevFiles, [currentFile]: newCode })) + setFileSystem(prevFiles => ({ + ...prevFiles, + files: { + ...prevFiles.files, + [currentFile]: newCode + } + })) } filename={currentFile} /> diff --git a/examples/experimental/nodes/frontend/src/components/CodeEditor/CodeEditor.tsx b/examples/experimental/nodes/frontend/src/components/CodeEditor/CodeEditor.tsx index 21d96ff9..47299e8c 100644 --- a/examples/experimental/nodes/frontend/src/components/CodeEditor/CodeEditor.tsx +++ b/examples/experimental/nodes/frontend/src/components/CodeEditor/CodeEditor.tsx @@ -7,14 +7,16 @@ import { githubDark } from '@uiw/codemirror-theme-github'; import './CodeEditor.css'; // Import the CSS file interface CodeEditorProps { - code: string; + code: string | undefined; onChange: (value: string) => void; filename: string; } const CodeEditor: React.FC = ({ code, onChange, filename }) => { // Add empty lines to fill the editor - const fillEmptyLines = (content: string) => { + const fillEmptyLines = (content: string | undefined) => { + if (!content) return '\n'.repeat(50); // Return 50 empty lines if no content + const lines = content.split('\n'); const currentLines = lines.length; if (currentLines < 50) { diff --git a/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.tsx b/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.tsx index 627659d7..e4b731e0 100644 --- a/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.tsx +++ b/examples/experimental/nodes/frontend/src/components/CodeEditor/FileSystem.tsx @@ -5,21 +5,13 @@ import { SiTypescript, SiJson, SiMarkdown } from 'react-icons/si'; import './FileSystem.css'; // Import the CSS file - -const files = [ - { name: 'workspace', type: 'folder', children: [ - { name: 'index.html', type: 'file' }, - { name: 'style.css', type: 'file' }, - { name: 'script.js', type: 'file' }, - { name: 'interview.py', type: 'file' }, - ]}, -]; +import { FileNode } from '../../types/FileSystem'; interface FileSystemProps { - onFileSelect: (fileName: string) => void; + fileSystem: FileNode[]; + onFileSelect: (path: string) => void; } - const getFileIcon = (fileName: string) => { const ext = fileName.split('.').pop()?.toLowerCase(); switch (ext) { @@ -35,8 +27,8 @@ const getFileIcon = (fileName: string) => { } }; -export const FileSystem: React.FC = ({ onFileSelect }) => { - const [expandedFolders, setExpandedFolders] = useState>(new Set(['workspace'])); +export const FileSystem: React.FC = ({ fileSystem, onFileSelect }) => { + const [expandedFolders, setExpandedFolders] = useState>(new Set(['/workspace'])); const toggleFolder = (folderName: string, e: React.MouseEvent) => { e.stopPropagation(); @@ -51,19 +43,19 @@ export const FileSystem: React.FC = ({ onFileSelect }) => { }); }; - const renderItem = (item: any, depth: number = 0) => { - const isExpanded = expandedFolders.has(item.name); + const renderItem = (item: FileNode, depth: number = 0) => { + const isExpanded = expandedFolders.has(item.path); return (
item.type === 'file' && onFileSelect(`/workspace/${item.name}`)} + onClick={() => item.type === 'file' && onFileSelect(item.path)} > {item.type === 'folder' ? ( <> - toggleFolder(item.name, e)}> + toggleFolder(item.path, e)}> {isExpanded ? : } @@ -75,12 +67,12 @@ export const FileSystem: React.FC = ({ onFileSelect }) => { ); }; - const renderFolder = (folder: any, depth: number = 0) => ( -
+ const renderFolder = (folder: FileNode, depth: number = 0) => ( +
{renderItem(folder, depth)} - {expandedFolders.has(folder.name) && folder.children && ( + {expandedFolders.has(folder.path) && folder.children && (
- {folder.children.map((child: any) => + {folder.children.map((child: FileNode) => child.type === 'folder' ? renderFolder(child, depth + 1) : renderItem(child, depth + 1) @@ -92,13 +84,12 @@ export const FileSystem: React.FC = ({ onFileSelect }) => { return ( <> -
Folders
-
- {files.map(file => file.type === 'folder' ? - renderFolder(file) : - renderItem(file) - )} -
+
Folders
+
+ {fileSystem.map(node => + node.type === 'folder' ? renderFolder(node) : renderItem(node) + )} +
); }; diff --git a/examples/experimental/nodes/frontend/src/types/FileSystem.ts b/examples/experimental/nodes/frontend/src/types/FileSystem.ts new file mode 100644 index 00000000..f3c40c3d --- /dev/null +++ b/examples/experimental/nodes/frontend/src/types/FileSystem.ts @@ -0,0 +1,14 @@ +export interface FileNode { + name: string; + type: 'file' | 'folder'; + path: string; + content?: string; + children?: FileNode[]; +} + +export interface FileSystemState { + tree: FileNode[]; + files: { + [path: string]: string; // path -> content mapping + }; +} \ No newline at end of file