forked from bots-garden/ori
| ✨ Introduce new feature(s): ACP web client — Go backend (agent, bridge, httpserver, mockagent) + React SPA (Zed-like agent panel), tests, quality gate PASS | 1 | /** |
| ✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme | 2 | * Root component: header with connection/session status and the workspace |
| 3 | * toggle, the chat column (thread, plan, permissions, composer) and the | |
| 4 | * workspace panel. The WebSocket is injected so tests (and future | |
| ✨ Introduce new feature(s): ACP web client — Go backend (agent, bridge, httpserver, mockagent) + React SPA (Zed-like agent panel), tests, quality gate PASS | 5 | * embeddings) can supply a fake. |
| 6 | * | |
| 7 | * @example | |
| 8 | * <App socket={connectSocket({ url: wsUrl(), dispatch })} /> | |
| 9 | */ | |
| 10 | ||
| 11 | import { ChatThread } from "./components/ChatThread"; | |
| 12 | import { PermissionPrompt } from "./components/PermissionPrompt"; | |
| 13 | import { PlanView } from "./components/PlanView"; | |
| 14 | import { PromptInput } from "./components/PromptInput"; | |
| ✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme | 15 | import { Workspace } from "./components/Workspace"; |
| 16 | import { useChatState } from "./store"; | |
| 17 | import { toggleTheme, useTheme } from "./theme"; | |
| 18 | import { toggleWorkspace, useWorkspace } from "./workspace"; | |
| 19 | import type { OriSocket } from "./ws"; | |
| ✨ Introduce new feature(s): ACP web client — Go backend (agent, bridge, httpserver, mockagent) + React SPA (Zed-like agent panel), tests, quality gate PASS | 20 | |
| 21 | export function App({ socket }: { socket: OriSocket }) { | |
| 22 | const state = useChatState(); | |
| ✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme | 23 | const workspaceOpen = useWorkspace((workspace) => workspace.open); |
| 24 | const theme = useTheme((state) => state.theme); | |
| 25 | const nextTheme = theme === "dark" ? "light" : "dark"; | |
| ✨ Introduce new feature(s): ACP web client — Go backend (agent, bridge, httpserver, mockagent) + React SPA (Zed-like agent panel), tests, quality gate PASS | 26 | |
| 27 | return ( | |
| 28 | <div className="app"> | |
| 29 | <header className="topbar"> | |
| 30 | <h1 className="topbar-title">Ori</h1> | |
| 31 | <span className={`status status-${state.connection}`}> | |
| 32 | {state.connection} | |
| 33 | {state.sessionId ? ` · ${state.sessionId}` : ""} | |
| 34 | </span> | |
| ✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme | 35 | <button |
| 36 | type="button" | |
| 37 | className="topbar-theme" | |
| 38 | onClick={toggleTheme} | |
| 39 | aria-label={`Switch to ${nextTheme} theme`} | |
| 40 | title={`Switch to ${nextTheme} theme`} | |
| 41 | > | |
| 42 | {theme === "dark" ? "☀" : "☾"} | |
| 43 | </button> | |
| 44 | <button | |
| 45 | type="button" | |
| 46 | className={`topbar-workspace${workspaceOpen ? " topbar-workspace-active" : ""}`} | |
| 47 | onClick={toggleWorkspace} | |
| 48 | > | |
| 49 | ⊞ Workspace | |
| 50 | </button> | |
| ✨ Introduce new feature(s): ACP web client — Go backend (agent, bridge, httpserver, mockagent) + React SPA (Zed-like agent panel), tests, quality gate PASS | 51 | </header> |
| ✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme | 52 | <div className={`app-body${workspaceOpen ? " with-workspace" : ""}`}> |
| 53 | <div className="chat-column"> | |
| 54 | <ChatThread thread={state.thread} turnActive={state.turnActive} /> | |
| 55 | <PlanView entries={state.plan} /> | |
| 56 | {state.permissions.map((request) => ( | |
| 57 | <PermissionPrompt | |
| 58 | key={request.requestId} | |
| 59 | request={request} | |
| 60 | onRespond={(requestId, optionId) => | |
| 61 | socket.send({ | |
| 62 | type: "permission_response", | |
| 63 | requestId, | |
| 64 | optionId, | |
| 65 | }) | |
| 66 | } | |
| 67 | /> | |
| 68 | ))} | |
| 69 | <PromptInput | |
| 70 | turnActive={state.turnActive} | |
| 71 | commands={state.commands} | |
| 72 | onSend={(text, attachments) => | |
| 73 | socket.send( | |
| 74 | attachments.length > 0 | |
| 75 | ? { type: "prompt", text, attachments } | |
| 76 | : { type: "prompt", text }, | |
| 77 | ) | |
| 78 | } | |
| 79 | onCancel={() => socket.send({ type: "cancel" })} | |
| 80 | /> | |
| 81 | </div> | |
| 82 | <Workspace /> | |
| 83 | </div> | |
| ✨ Introduce new feature(s): ACP web client — Go backend (agent, bridge, httpserver, mockagent) + React SPA (Zed-like agent panel), tests, quality gate PASS | 84 | </div> |
| 85 | ); | |
| 86 | } |