feat: add resizable workspace shell

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tao.chen
2026-07-02 18:15:52 +08:00
co-authored by Claude
parent 593d2aee5e
commit 0f634981b5
4 changed files with 253 additions and 25 deletions
@@ -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 (
<div className="flex h-full w-full flex-col bg-background text-foreground">
{/* Toolbar */}
<header className="flex h-9 shrink-0 items-center border-b border-border bg-sidebar px-3">
<h1 className="text-sm font-semibold">codespace</h1>
<Separator orientation="vertical" className="mx-3 h-4" />
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="sm"
className="h-7 px-2 text-xs"
onClick={toggleSidebar}
>
{sidebarCollapsed ? (
<PanelLeftOpen className="size-4" aria-hidden="true" />
) : (
<PanelLeftClose className="size-4" aria-hidden="true" />
)}
</Button>
<Button
variant="ghost"
size="sm"
className="h-7 px-2 text-xs"
onClick={toggleTerminal}
>
<Terminal className="size-4" aria-hidden="true" />
</Button>
</div>
</header>
{/* Main layout */}
<div className="flex min-h-0 flex-1">
<PanelGroup direction="horizontal" className="flex-1">
{/* Sidebar */}
{!sidebarCollapsed && (
<>
<Panel defaultSize={18} minSize={12} maxSize={30}>
<FileExplorerPanel files={files} />
</Panel>
<PanelResizeHandle className="w-1 bg-border transition-colors hover:bg-accent" />
</>
)}
{/* Main content area */}
<Panel>
<PanelGroup direction="vertical">
{/* Editor placeholder */}
<Panel>
<div className="flex h-full w-full items-center justify-center border-r border-border text-sm text-muted-foreground">
<div className="text-center">
<p className="mb-2">Editor placeholder</p>
<div className="space-y-1 text-xs">
<p>
Active file:{" "}
<code className="text-foreground">{activeFilePath}</code>
</p>
<p className="text-muted-foreground">
{activeFile
? `${activeFile.name} | ${activeFile.language ?? "plain text"} | ${activeFile.kind}`
: "No matching mock file"}
</p>
</div>
</div>
</div>
</Panel>
{/* Terminal panel */}
{terminalVisible && (
<>
<PanelResizeHandle className="h-1 bg-border transition-colors hover:bg-accent" />
<Panel defaultSize={30} minSize={10} maxSize={70}>
<div className="flex h-full w-full items-center justify-center border-r border-border text-sm text-muted-foreground">
Terminal placeholder
</div>
</Panel>
</>
)}
</PanelGroup>
</Panel>
</PanelGroup>
</div>
{/* Status bar */}
<StatusBar activeFilePath={activeFilePath} />
</div>
);
}