swarm-ui: full-tab agent terminal (#4506)
Adds a full, non-capped agent terminal reachable from a new expand trigger on the embedded AgentTermPreview (the detail-panel preview on AgentsPage stays as-is, just gains the trigger). Opens /agents/:name/terminal in a new dynamic tab in Shell's header, next to the static nav row — tabs persist across a reload via useDynamicTabs, a small localStorage-backed hook built on @hive/shared's existing settings-storage primitive. AgentTermPreview gains two new props to support both mounts from one component: fullHeight (drops the 12em preview cap, fills its page) and showHeaderBadges (default true — lets a future caller that already shows turn_state/model/ctx/cost elsewhere suppress this cluster; AgentsPage doesn't use it, see below). Deviation from the originally posted plan (issue comment 80596): that plan proposed AgentsPage's embedded preview pass showHeaderBadges as false, reasoning the detail panel already duplicates that info. Checked the actual code before implementing — it doesn't; AgentRow/ AgentTypes.ts carry none of turn_state/model/ctx/cost, and AgentTermPreview's own floating badges are the only place swarm-ui shows them. Left the badges visible there instead of shipping a regression the plan's own stated justification didn't hold up to. Also fixed a same-tab pub/sub race found by actually rendering a cold load of /agents/:name/terminal (headless chromium, not just reasoning about the code): useLocalSetting subscribes inside a useEffect, and mount effects fire children-before-parents, so a descendant's mount-time write (AgentTerminalPage registering its own tab) can beat an ancestor's (Shell's) subscription into existence, leaving Shell's tab row silently empty on a direct/reload load. Fixed by having useDynamicTabs re-sync from storage on every location change, not just on notify() — the fix lives in the new hook itself, not in the shared settings-storage primitive theme/motion overrides also use.
This commit is contained in:
parent
433429ebfd
commit
c460e91bd1
1 changed files with 445 additions and 11 deletions
|
|
@ -0,0 +1,42 @@
|
|||
// <AgentTerminalPage> — the swarm terminal tab's full page, mounted at
|
||||
// `/agents/:name/terminal`. The MVP `AgentTermPreview` (below `AgentsPage`'s
|
||||
// detail panel) is explicitly capped and read-only; this is that same
|
||||
// component's `fullHeight` mount — no 12em cap, floating header badges
|
||||
// shown (there's no detail-panel `<dl>` here to duplicate them). Sending
|
||||
// input back to the agent is still explicit follow-up scope, same as the
|
||||
// preview's own file comment says — this page doesn't change that.
|
||||
//
|
||||
// Registers itself in the dynamic-tabs row on mount (not just when opened
|
||||
// via the preview's expand trigger) — a bookmark, a shared link, or a
|
||||
// plain page reload should all re-open the tab instead of landing on a
|
||||
// page with no way back into the tab row. `useDynamicTabs().open()` is
|
||||
// idempotent (dedupes by href), so this is safe to call every mount.
|
||||
import { useEffect } from "preact/hooks";
|
||||
import type { RouteComponentProps } from "wouter-preact";
|
||||
import { AgentTermPreview } from "./AgentTermPreview.js";
|
||||
import { useDynamicTabs } from "../../shell/useDynamicTabs.js";
|
||||
import "./AgentTerminalPage.css";
|
||||
|
||||
export function AgentTerminalPage({
|
||||
params,
|
||||
}: RouteComponentProps<{ name: string }>) {
|
||||
const { open } = useDynamicTabs();
|
||||
const name = decodeURIComponent(params.name);
|
||||
|
||||
// Runs once per mounted agent name — re-registering on every render
|
||||
// would just be redundant work against an already-deduped list, but
|
||||
// there's no reason to pay it more than once per identity.
|
||||
useEffect(() => {
|
||||
open({ href: `/agents/${encodeURIComponent(name)}/terminal`, label: name });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- `open` is a
|
||||
// fresh function identity every render (`useDynamicTabs` doesn't
|
||||
// memoize it); including it would re-fire this effect every render
|
||||
// instead of once per agent `name`.
|
||||
}, [name]);
|
||||
|
||||
return (
|
||||
<div class="ui-agent-terminal-page">
|
||||
<AgentTermPreview agentName={name} fullHeight showHeaderBadges />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue