diff --git a/src/app/api/workspace/route.ts b/src/app/api/workspace/route.ts index dffd934..0238996 100644 --- a/src/app/api/workspace/route.ts +++ b/src/app/api/workspace/route.ts @@ -161,7 +161,12 @@ export async function GET(request: NextRequest) { ? relPath.replace(/\/+$/, "").split("/").slice(0, -1).join("/") : null; - return NextResponse.json({ path: relPath, parent, entries }); + return NextResponse.json({ + path: relPath, + parent, + entries, + dir: workspaceDir, + }); } catch (error) { return NextResponse.json( { diff --git a/src/app/components/ChatInterface.tsx b/src/app/components/ChatInterface.tsx index a8e478f..e8f6180 100644 --- a/src/app/components/ChatInterface.tsx +++ b/src/app/components/ChatInterface.tsx @@ -80,7 +80,6 @@ import { import { lastTextOf, type SubAgentStep } from "@/lib/subAgentActivity"; import { useStickToBottom } from "use-stick-to-bottom"; import { FilesPopover } from "@/app/components/TasksFilesSidebar"; -import { WorkspacePanel } from "@/app/components/WorkspacePanel"; import { Dialog, DialogContent, @@ -119,6 +118,8 @@ interface ChatInterfaceProps { onNavigate?: (target: DashboardNavTarget) => void; // Open a pinned thread from the empty-state dashboard. onOpenThread?: (id: string) => void; + // Whether the workspace inspector is currently visible. + workspaceOpen?: boolean; // Register a "submit a message on THIS (main) thread" function up to page so // the Agents board can loop an async result back to the main agent. Returns // false if the main chat is mid-run (can't take a turn). Cleared on unmount. @@ -256,10 +257,15 @@ const getStatusIcon = (status: TodoItem["status"], className?: string) => { }; export const ChatInterface = React.memo( - ({ assistant, onShowAgents, onNotifyReady, onNavigate, onOpenThread }) => { - const [metaOpen, setMetaOpen] = useState< - "tasks" | "files" | "workspace" | null - >(null); + ({ + assistant, + onShowAgents, + onNotifyReady, + onNavigate, + onOpenThread, + workspaceOpen, + }) => { + const [metaOpen, setMetaOpen] = useState<"tasks" | "files" | null>(null); const tasksContainerRef = useRef(null); const textareaRef = useRef(null); const uploadInputRef = useRef(null); @@ -269,6 +275,20 @@ export const ChatInterface = React.memo( [] ); const [isUploadingFiles, setIsUploadingFiles] = useState(false); + const [workspaceDir, setWorkspaceDir] = useState(null); + + useEffect(() => { + let cancelled = false; + fetch("/api/workspace") + .then((r) => (r.ok ? r.json() : null)) + .then((d: { dir?: string } | null) => { + if (!cancelled && d?.dir) setWorkspaceDir(d.dir); + }) + .catch(() => {}); + return () => { + cancelled = true; + }; + }, []); // Messages typed while the agent is busy (Claude Code-style queue). They // drain one-per-idle-window into the thread once it's free. A ref mirrors the // latest queue so event handlers (key ↑, edit) read current state without @@ -1791,8 +1811,6 @@ export const ChatInterface = React.memo( "focus-within:ring-2 focus-within:ring-ring" )} > - {/* Always rendered: the Workspace tab is available even with no tasks - or state files yet. */} {
{!metaOpen && ( @@ -1901,31 +1919,12 @@ export const ChatInterface = React.memo( ); })(); - const workspaceTrigger = ( - - ); + if (!hasTasks && !hasFiles) return null; return (
{tasksTrigger}
{filesTrigger} - {workspaceTrigger}
); })()} @@ -1966,18 +1965,6 @@ export const ChatInterface = React.memo( )} -
)} - - {metaOpen === "workspace" && ( -
- -
- )} )} @@ -2214,6 +2195,29 @@ export const ChatInterface = React.memo( {autoApprove ? "Auto-approve On" : "Auto-approve"} + {onNavigate && workspaceDir && ( + + )}