feat(maestro): render the structured view in the web client (#140)
Adds a transcript surface for a Structured-view session alongside the existing xterm attach view: a WS /api/structured/:id connection (structured.ts) mounted by ConnectionPool whenever a session's `view` is `"Structured"`, and pure per-row/rail renderers (structuredRender.ts, structuredDiff.ts) covering all eight transcript row kinds. Hierarchy follows the design reference (artifact 88ecd690): the approval is the only lifted, card-like element; a user turn is a tinted block with an accent rail; agent prose is plain text; tool calls are compact bordered rows expanding to output or a coloured, horizontally-scrolling diff computed from ACP's full old/new text. The client renders `reduced_state` and rows exactly as received, with no raw-event folding or turn tracking of its own. Extracted attach.ts's pane-header title building into paneTitle.ts and its connection interface into pane.ts (PaneConnection) so both connection kinds share one contract and one header.
This commit is contained in:
+43
-1
@@ -11,6 +11,8 @@ import type {
|
||||
Projects,
|
||||
RecentDirs,
|
||||
Session,
|
||||
StructuredApprovalDecision,
|
||||
StructuredPromptRequest,
|
||||
Uuid,
|
||||
} from "./types.js";
|
||||
|
||||
@@ -152,4 +154,44 @@ export async function fetchBranches(path: string): Promise<Branches> {
|
||||
return jsonOrError(await fetch(url, { credentials: "same-origin" }));
|
||||
}
|
||||
|
||||
export { eventsWsUrl } from "./routes.js";
|
||||
/** Send a prompt to a structured session's ACP turn (#138). `409
|
||||
* turn_in_flight` when one is already active — the caller reflects that from
|
||||
* `ReducedState.turn` instead of racing this call. */
|
||||
export async function sendStructuredPrompt(id: Uuid, prompt: string): Promise<void> {
|
||||
await jsonOrError(
|
||||
await fetch(routes.structuredPrompt(id), {
|
||||
method: "POST",
|
||||
credentials: "same-origin",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ prompt } satisfies StructuredPromptRequest),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/** Cancel a structured session's active ACP turn (#138). */
|
||||
export async function cancelStructuredTurn(id: Uuid): Promise<void> {
|
||||
await jsonOrError(
|
||||
await fetch(routes.structuredCancel(id), { method: "POST", credentials: "same-origin" }),
|
||||
);
|
||||
}
|
||||
|
||||
/** Answer a pending ACP permission request (#139). Always resolves once the
|
||||
* session itself is a live structured session — a nonce that is unknown or
|
||||
* already consumed still answers `204` (docs/STRUCTURED-VIEW.md), so two
|
||||
* clients racing the same approval never see this reject. */
|
||||
export async function resolveStructuredApproval(
|
||||
id: Uuid,
|
||||
nonce: string,
|
||||
decision: StructuredApprovalDecision,
|
||||
): Promise<void> {
|
||||
await jsonOrError(
|
||||
await fetch(routes.structuredApproval(id, nonce), {
|
||||
method: "POST",
|
||||
credentials: "same-origin",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(decision),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export { eventsWsUrl, structuredWsUrl } from "./routes.js";
|
||||
|
||||
Reference in New Issue
Block a user