/** * Web agent cell — ticket #13, lot F4. Thin wiring (no new terminal logic) that * reuses the existing, transport-neutral {@link TerminalView} to render a CLI * agent over the WebSocket in web mode. * * The agent's CLI runs server-side (B6); the browser is display-only. This * component just supplies `TerminalView` with the DI **agent gateway** as the * opener/reattacher: * - `open` → `agent.launchAgent(projectId, agentId, …)` (frame `agent.launch`, * unified `terminal.attached` ack; the minted conversation id is carried on the * returned handle), * - `reattach` → `agent.reattach(sessionId, …)` (frame `terminal.attach`, no * relaunch, bounded scrollback repainted) — so a browser reload/reconnect * resumes the surviving server-side PTY. * * The session id is persisted in component state so a re-mount re-attaches rather * than relaunching, matching the desktop cell's lifecycle. A structured agent is * refused server-side (`UNSUPPORTED`) and surfaced by `TerminalView`'s own error * banner — no crash. It touches only gateways via DI (no `@tauri-apps/api`). */ import { useCallback, useState } from "react"; import type { OpenTerminalOptions, ReattachResult, TerminalHandle, } from "@/ports"; import { useGateways } from "@/app/di"; import { TerminalView, type TerminalInputApi } from "@/features/terminals"; import { TerminalKeyBar } from "./TerminalKeyBar"; interface WebAgentCellProps { /** Owning project id (resolved server-side by `agent.launch`). */ projectId: string; /** Agent to launch/attach. */ agentId: string; /** Working directory for the cell (typically the project root). */ cwd: string; /** Layout leaf id, when the caller tracks one (drives the singleton guard). */ nodeId?: string; } export function WebAgentCell({ projectId, agentId, cwd, nodeId }: WebAgentCellProps) { const { agent } = useGateways(); const [sessionId, setSessionId] = useState(null); // Input API of the mounted xterm (#69), used by the mobile key bar. Stays // `null` in headless renders where xterm cannot mount — the bar then renders // disabled rather than throwing. const [inputApi, setInputApi] = useState(null); const open = useCallback( (options: OpenTerminalOptions, onData: (bytes: Uint8Array) => void): Promise => agent.launchAgent( projectId, agentId, nodeId ? { ...options, nodeId } : options, onData, ), [agent, projectId, agentId, nodeId], ); const reattach = useCallback( (sid: string, onData: (bytes: Uint8Array) => void): Promise => agent.reattach(sid, onData), [agent], ); return ( // #69 — the cell was a flat `h-64` (256px), which on a phone left the agent // a letterbox roughly 20 lines tall. It now takes 60% of the *dynamic* // viewport on phones (so the collapsing URL bar can't clip it) and keeps a // fixed, desktop-like height from `sm` up. `min-h-0` on the terminal row // lets it shrink inside the flex column instead of pushing the key bar off.
); }