diff --git a/web/src/components/layout/WorkspaceShell.tsx b/web/src/components/layout/WorkspaceShell.tsx
new file mode 100644
index 0000000..4dc3b91
--- /dev/null
+++ b/web/src/components/layout/WorkspaceShell.tsx
@@ -0,0 +1,112 @@
+import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
+import { PanelLeftClose, PanelLeftOpen, Terminal } from "lucide-react";
+
+import { Button } from "@/components/ui/button";
+import { Separator } from "@/components/ui/separator";
+import { FileExplorerPanel } from "@/components/workspace/FileExplorerPanel";
+import { StatusBar } from "@/components/workspace/StatusBar";
+import { useWorkspaceUiStore } from "@/lib/store/workspace-ui-store";
+import {
+ findFileByPath,
+ type MockFileNode,
+} from "@/pages/workspace/mock-data";
+
+interface WorkspaceShellProps {
+ files: MockFileNode[];
+}
+
+export function WorkspaceShell({ files }: WorkspaceShellProps) {
+ const activeFilePath = useWorkspaceUiStore((s) => s.activeFilePath);
+ const sidebarCollapsed = useWorkspaceUiStore((s) => s.sidebarCollapsed);
+ const terminalVisible = useWorkspaceUiStore((s) => s.terminalVisible);
+ const toggleSidebar = useWorkspaceUiStore((s) => s.toggleSidebar);
+ const toggleTerminal = useWorkspaceUiStore((s) => s.toggleTerminal);
+ const activeFile = findFileByPath(files, activeFilePath);
+
+ return (
+
+ {/* Toolbar */}
+
+ codespace
+
+
+
+
+
+
+
+ {/* Main layout */}
+
+
+ {/* Sidebar */}
+ {!sidebarCollapsed && (
+ <>
+
+
+
+
+ >
+ )}
+
+ {/* Main content area */}
+
+
+ {/* Editor placeholder */}
+
+
+
+
Editor placeholder
+
+
+ Active file:{" "}
+ {activeFilePath}
+
+
+ {activeFile
+ ? `${activeFile.name} | ${activeFile.language ?? "plain text"} | ${activeFile.kind}`
+ : "No matching mock file"}
+
+
+
+
+
+
+ {/* Terminal panel */}
+ {terminalVisible && (
+ <>
+
+
+
+ Terminal placeholder
+
+
+ >
+ )}
+
+
+
+
+
+ {/* Status bar */}
+
+
+ );
+}
diff --git a/web/src/components/workspace/FileExplorerPanel.tsx b/web/src/components/workspace/FileExplorerPanel.tsx
new file mode 100644
index 0000000..809a0c2
--- /dev/null
+++ b/web/src/components/workspace/FileExplorerPanel.tsx
@@ -0,0 +1,99 @@
+import { ChevronRight, FileCode2, Folder } from "lucide-react";
+
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { useWorkspaceUiStore } from "@/lib/store/workspace-ui-store";
+import { cn } from "@/lib/utils";
+import type { MockFileNode } from "@/pages/workspace/mock-data";
+
+interface FileExplorerPanelProps {
+ files: MockFileNode[];
+}
+
+interface FileTreeNodeProps {
+ node: MockFileNode;
+ depth: number;
+ activeFilePath: string;
+ onSelectFile: (path: string) => void;
+}
+
+function FileTreeNode({
+ node,
+ depth,
+ activeFilePath,
+ onSelectFile,
+}: FileTreeNodeProps) {
+ const isFile = node.kind === "file";
+ const isActive = isFile && node.path === activeFilePath;
+ const paddingLeft = `${depth * 0.875 + 0.75}rem`;
+
+ return (
+
+
+ {node.children?.length ? (
+
+ {node.children.map((child) => (
+
+ ))}
+
+ ) : null}
+
+ );
+}
+
+export function FileExplorerPanel({ files }: FileExplorerPanelProps) {
+ const activeFilePath = useWorkspaceUiStore((s) => s.activeFilePath);
+ const setActiveFilePath = useWorkspaceUiStore((s) => s.setActiveFilePath);
+
+ return (
+
+ );
+}
diff --git a/web/src/components/workspace/StatusBar.tsx b/web/src/components/workspace/StatusBar.tsx
new file mode 100644
index 0000000..cd2e632
--- /dev/null
+++ b/web/src/components/workspace/StatusBar.tsx
@@ -0,0 +1,39 @@
+import { Terminal, FileCode2 } from "lucide-react";
+
+import { Separator } from "@/components/ui/separator";
+import { cn } from "@/lib/utils";
+import { useWorkspaceUiStore } from "@/lib/store/workspace-ui-store";
+
+interface StatusBarProps {
+ activeFilePath: string;
+}
+
+export function StatusBar({ activeFilePath }: StatusBarProps) {
+ const terminalVisible = useWorkspaceUiStore((s) => s.terminalVisible);
+ const toggleTerminal = useWorkspaceUiStore((s) => s.toggleTerminal);
+
+ return (
+
+ );
+}
diff --git a/web/src/pages/workspace/WorkspacePage.tsx b/web/src/pages/workspace/WorkspacePage.tsx
index 7f0316e..93e4ec9 100644
--- a/web/src/pages/workspace/WorkspacePage.tsx
+++ b/web/src/pages/workspace/WorkspacePage.tsx
@@ -1,28 +1,6 @@
-import { findFileByPath, mockWorkspaceFiles } from "@/pages/workspace/mock-data";
-import { useWorkspaceUiStore } from "@/lib/store/workspace-ui-store";
+import { mockWorkspaceFiles } from "@/pages/workspace/mock-data";
+import { WorkspaceShell } from "@/components/layout/WorkspaceShell";
export function WorkspacePage() {
- const activeFilePath = useWorkspaceUiStore((s) => s.activeFilePath);
- const activeFile = findFileByPath(mockWorkspaceFiles, activeFilePath);
-
- return (
-
-
-
-
- Active file: {activeFilePath}
-
-
- Mock file match:{" "}
-
- {activeFile
- ? `${activeFile.name} (${activeFile.language ?? activeFile.kind})`
- : "not found"}
-
-
-
-
- );
+ return ;
}