diff --git a/frontends/nextjs/src/components/nerd-mode-ide/FileTree.tsx b/frontends/nextjs/src/components/nerd-mode-ide/FileTree.tsx
new file mode 100644
index 000000000..dd18e0f58
--- /dev/null
+++ b/frontends/nextjs/src/components/nerd-mode-ide/FileTree.tsx
@@ -0,0 +1,37 @@
+import type { FileNode } from '@/lib/nerd-mode-ide'
+import { FileTreeNode } from './FileTreeNode'
+
+interface FileTreeProps {
+ nodes: FileNode[]
+ selectedFileId: string | null
+ activeFolderId: string | null
+ onToggleFolder: (nodeId: string) => void
+ onSelectFile: (nodeId: string) => void
+ onSelectFolder: (nodeId: string) => void
+}
+
+export function FileTree({
+ nodes,
+ selectedFileId,
+ activeFolderId,
+ onToggleFolder,
+ onSelectFile,
+ onSelectFolder,
+}: FileTreeProps) {
+ return (
+
+ {nodes.map((node) => (
+
+ ))}
+
+ )
+}
diff --git a/frontends/nextjs/src/components/nerd-mode-ide/FileTreeNode.tsx b/frontends/nextjs/src/components/nerd-mode-ide/FileTreeNode.tsx
new file mode 100644
index 000000000..1369bdae1
--- /dev/null
+++ b/frontends/nextjs/src/components/nerd-mode-ide/FileTreeNode.tsx
@@ -0,0 +1,79 @@
+import ChevronRightIcon from '@mui/icons-material/ChevronRight'
+import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined'
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
+import FolderOutlinedIcon from '@mui/icons-material/FolderOutlined'
+import FolderOpenOutlinedIcon from '@mui/icons-material/FolderOpenOutlined'
+import type { FileNode } from '@/lib/nerd-mode-ide'
+
+interface FileTreeNodeProps {
+ node: FileNode
+ level: number
+ selectedFileId: string | null
+ activeFolderId: string | null
+ onToggleFolder: (nodeId: string) => void
+ onSelectFile: (nodeId: string) => void
+ onSelectFolder: (nodeId: string) => void
+}
+
+export function FileTreeNode({
+ node,
+ level,
+ selectedFileId,
+ activeFolderId,
+ onToggleFolder,
+ onSelectFile,
+ onSelectFolder,
+}: FileTreeNodeProps) {
+ const isSelected = node.type === 'file'
+ ? selectedFileId === node.id
+ : activeFolderId === node.id
+ const paddingLeft = `${level * 16}px`
+
+ return (
+
+
{
+ if (node.type === 'folder') {
+ onToggleFolder(node.id)
+ onSelectFolder(node.id)
+ } else {
+ onSelectFile(node.id)
+ }
+ }}
+ >
+ {node.type === 'folder' ? (
+ <>
+ {node.expanded ?
:
}
+ {node.expanded ?
:
}
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
{node.name}
+
+ {node.type === 'folder' && node.expanded && node.children && node.children.length > 0 && (
+
+ {node.children.map((child) => (
+
+ ))}
+
+ )}
+
+ )
+}