swarm-ui: fold the agent terminal into AgentsPage's detail panel, not a route
mara's steer on the issue, right as the previous commit's plan was posted: the swarm-level term isn't a separate page, it's part of the same pwa -- selecting an agent should show a small preview (no header, no input) below the existing detail fields. That's the whole MVP for this issue; the full terminal (+ sending input back to the agent) is explicit follow-up scope. AgentTermPage -> AgentTermPreview: drops the Panel wrapper, the "terminal" nav badge + wouter route, and the back-link -- just the inline preview, height-capped at 12em (@hive/shared/terminal.css's own default is a much taller 72vh/60em, sized for a dedicated page rather than a peek).
This commit is contained in:
parent
593923375c
commit
727bfb775b
5 changed files with 42 additions and 64 deletions
|
|
@ -1,88 +0,0 @@
|
|||
// <AgentTermPage> — read-only live terminal for one agent, consuming
|
||||
// swarm-controller's `GET /api/agents/{name}/term/stream`. First
|
||||
// per-agent detail route in swarm-ui (linked from `AgentsPage`'s detail
|
||||
// panel). Live-only, no backfill/history — see `useSwarmTermStream`'s
|
||||
// doc for why a fresh visit starts empty rather than replaying anything.
|
||||
//
|
||||
// Auto-scroll is the simple "stick to bottom unless the operator
|
||||
// scrolled up" rule `@hive/agent`'s `LiveStream` uses, minus its
|
||||
// load-more/history plumbing (nothing to load older here).
|
||||
import { useLayoutEffect, useRef, useState } from "preact/hooks";
|
||||
import { Link, useParams } from "wouter-preact";
|
||||
import { Badge, type BadgeTone } from "@hive/shared/badge.js";
|
||||
import { Row } from "@hive/shared/term-row.js";
|
||||
import { Panel } from "../../ui/panel/Panel.js";
|
||||
import { useSwarmTermStream, type ConnectionState } from "./useSwarmTermStream.js";
|
||||
import "./AgentTermPage.css";
|
||||
|
||||
const NEAR_BOTTOM_PX = 48;
|
||||
|
||||
const CONNECTION_LABEL: Record<ConnectionState, string> = {
|
||||
connecting: "connecting…",
|
||||
open: "live",
|
||||
reconnecting: "reconnecting…",
|
||||
closed: "disconnected",
|
||||
};
|
||||
|
||||
const CONNECTION_TONE: Record<ConnectionState, BadgeTone> = {
|
||||
connecting: "neutral",
|
||||
open: "positive",
|
||||
reconnecting: "warning",
|
||||
closed: "negative",
|
||||
};
|
||||
|
||||
export function AgentTermPage() {
|
||||
const { name } = useParams<{ name: string }>();
|
||||
const { rows, connection } = useSwarmTermStream(
|
||||
`/api/agents/${encodeURIComponent(name ?? "")}/term/stream`,
|
||||
);
|
||||
const logRef = useRef<HTMLDivElement>(null);
|
||||
const [stickToBottom, setStickToBottom] = useState(true);
|
||||
|
||||
function handleScroll() {
|
||||
const el = logRef.current;
|
||||
if (!el) return;
|
||||
setStickToBottom(
|
||||
el.scrollHeight - el.scrollTop - el.clientHeight <= NEAR_BOTTOM_PX,
|
||||
);
|
||||
}
|
||||
|
||||
// Runs after Preact has committed new rows, so `scrollHeight` is
|
||||
// already current — same ordering `LiveStream`'s own layout effect
|
||||
// relies on.
|
||||
useLayoutEffect(() => {
|
||||
const el = logRef.current;
|
||||
if (el && stickToBottom) el.scrollTop = el.scrollHeight - el.clientHeight;
|
||||
}, [rows, stickToBottom]);
|
||||
|
||||
return (
|
||||
<Panel
|
||||
title={`terminal — ${name}`}
|
||||
icon="🖥️"
|
||||
actions={
|
||||
<Link to="/agents" class="ui-agent-term-back">
|
||||
← agents
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
<Badge
|
||||
class="ui-agent-term-status"
|
||||
tone={CONNECTION_TONE[connection]}
|
||||
value={CONNECTION_LABEL[connection]}
|
||||
/>
|
||||
<div class="terminal-wrap">
|
||||
<div class="live terminal" ref={logRef} onScroll={handleScroll}>
|
||||
{rows.length === 0 ? (
|
||||
<div class="row note">
|
||||
{connection === "open"
|
||||
? "(connected — waiting for events)"
|
||||
: CONNECTION_LABEL[connection]}
|
||||
</div>
|
||||
) : (
|
||||
rows.map((row) => <Row key={row.key} row={row} />)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue