6.3 KiB
Plan
Roadmap for the next phases of the codespace web IDE.
Now: shadcn/ui dialog and dropdown migration
The frontend still uses window.prompt, window.confirm, and an absolute-positioned <div> for the file-tree context menu. Replace these with shadcn/ui components backed by Radix UI primitives. This gives us:
- Consistent visual style for all popups
- Better keyboard navigation and accessibility
- The right-click context menu in the file tree becomes a real
DropdownMenuinstead of an in-place div
Concrete steps
- Add Radix UI primitives:
@radix-ui/react-dialog,@radix-ui/react-alert-dialog,@radix-ui/react-dropdown-menu. - Add shadcn-style wrappers under
web/src/components/ui/:dialog.tsx(Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter)alert-dialog.tsx(AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel)dropdown-menu.tsx(DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator)input.tsx(used inside Dialogs for prompts)
- Migrate the existing callers:
WorkspaceSelector.tsx: replacewindow.promptfor new-workspace with aDialogcontaining anInput. Replacewindow.confirmfor delete with anAlertDialog.FileExplorerPanel.tsx: replace allwindow.prompt(new file, new folder, rename) with a genericDialog+Inputcomponent parameterized by title and callback. Replacewindow.confirm(delete) with anAlertDialog. Replace the absolute-positioned context menu withDropdownMenu.
- Do not change backend behavior.
Next: opencode ACP panel and integration
opencode acp exposes the editor as an Agent Client Protocol server. The current opencode process is started in the background but the user has no way to interact with it. The next phase adds a first-class ACP client panel in the web IDE.
Status: ✅ Completed (commits: ACP backend feat(backend): add opencode ACP ..., ACP frontend feat(web): add ACP chat panel ..., bug fix fix(acp): parse session/update content as ContentBlock, not string).
Why
The terminal currently runs bash (the shell subsystem). The opencode process is started via the toolbar and is meant to be controlled by an ACP client — the same protocol that editors like Zed use to talk to coding agents. Without an ACP panel, the opencode process is invisible: it has a PID and a status indicator, but no way to send it prompts or read responses.
Scope
-
Backend: switch the opencode process to ACP mode
- Default
opencodeCommandstaysopencode; newprocess.argsfield defaults to["acp"]so the launched command isopencode acp. Configurable viaconfigs/config.yamland theCODESPACE_OPENCODE_ARGSenv var. - The
internal/processpackage still captures stdout/stderr and exposes stdin via the WebSocket. ACP over stdio sits on top in the newinternal/acppackage — no transport changes to the existing process WS route.
- Default
-
Backend: ACP-aware HTTP API
- Implemented as four HTTP routes per ACP method. The frontend does not need to know JSON-RPC.
- Routes:
GET /api/workspaces/:id/acp/status— observational:{workspaceId, ready, sessionId, running, pid, error}. Does NOT start the process.GET /api/workspaces/:id/acp/history— accumulated{sessionId, messages: [{role, text, time}]}, oldest first. In-memory only.POST /api/workspaces/:id/acp/prompt— body{content: string}. Returns{sessionId, stopReason, text}. Starts the process + session on demand (synchronous, 5-min default timeout).POST /api/workspaces/:id/acp/cancel— best-effort interrupt.
-
Frontend: ACP panel
- Implemented as
web/src/components/acp/AcpPanel.tsxwith subcomponentsMessageBubble,MarkdownView,ThinkingIndicator. Renders markdown with code-block highlighting (react-markdown + react-syntax-highlighter Prism oneDark), with auto-scroll, error toast fade, and a "thinking…" indicator while in flight. - Hooks:
useAcpStatus(3s poll),useAcpHistory(5s poll),useAcpPrompt(mutation),useAcpCancel(mutation).
- Implemented as
-
Frontend: integration with the workspace shell
- Bottom panel converted to a tabbed area:
Terminal/Agent(decided on Tabs + Sibling tab, not replace, not split). Tab state lives inworkspaceUiStore.bottomTab. StatusBarshowsagent: ready/initializing…/<error>pill when the ACP session is initialized; hides when not.- The existing
process/*routes and raw process WebSocket remain untouched (the new acp routes are additive).
- Bottom panel converted to a tabbed area:
Concrete tasks — all done
- ✅ Switched opencode default to
opencode acpviaprocess.args. - ✅ Added
internal/acppackage: NDJSON transport + JSON-RPC 2.0 client (NDJSON framing with split-line buffering, request/response correlation, notification dispatch, per-workspaceClient+Service). - ✅ Added
internal/api/acp_handler.gowith the four routes. - ✅ Added
web/src/lib/api/acp.tswith the four hooks. - ✅ Added
web/src/components/acp/AcpPanel.tsx+ subcomponents. - ✅ Added
react-markdown+react-syntax-highlighter+remark-gfmtoweb/package.json. - ✅ Wired the ACP panel into
WorkspaceShell.tsxas a tabbed bottom panel. - ✅ Documented the ACP routes in
README.md(section + env var + example curl).
Tests
internal/acp/client_test.gocovers NDJSON split-lines, request/response correlation, notification dispatch, and agent-initiated request handling forfs/*(real dispatch) andterminal/*(MethodNotFound).- E2E smoke test against the live
opencode acpbinary: created a workspace, sent a prompt, receivedtext: "Hi there!"non-empty, history has 4 messages (1 user + 3 agent chunks), 0invalid session/updatelog entries.
Out of scope (deferred to v2)
- Streaming responses over WebSocket (v1 is single round-trip per prompt).
- Tool-call approval UI.
- Multiple agents / agent switching.
- Authentication.
- Cross-workspace agent memory.
- ACP session persistence across opencode restarts (history is in-memory and cleared on process restart — acceptable for v1).
terminal/*agent tool calls (returnMethodNotFound -32601; the agent will fail those tool calls gracefully).