// — the swarm terminal tab's full page, mounted at // `/agents/:name/terminal`. The MVP `AgentTermPreview` (below `AgentsPage`'s // detail panel) is height-capped; this is that same // component's `fullHeight` mount — no 12em cap, floating header badges // shown (there's no detail-panel `
` here to duplicate them). // // 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, useRef, useState } from "preact/hooks"; import type { RouteComponentProps } from "wouter-preact"; import { Badge } from "@hive/shared/badge.js"; import { AgentDetailPopover } from "../../agent/detail/AgentDetailPopover.js"; import { AgentIcon } from "../../agent/AgentIcon.js"; import { SubagentsMenu } from "../../agent/terminal/SubagentsMenu.js"; import { AgentTermPreview, termPaths, } from "../../agent/terminal/AgentTermPreview.js"; import { useAgentFavicon } from "../../agent/terminal/useAgentFavicon.js"; import { useDynamicTabs } from "../../shell/useDynamicTabs.js"; import "./AgentTerminalPage.css"; // Also mounted at `/agents/:name/subagents/:subagent/terminal`, for one of // the agent's subagents. export function AgentTerminalPage({ params, }: RouteComponentProps<{ name: string; subagent?: string }>) { const { open } = useDynamicTabs(); const name = decodeURIComponent(params.name); const subagent = params.subagent === undefined ? undefined : decodeURIComponent(params.subagent); const { page, label } = termPaths(name, subagent); // A subagent has no icon of its own: its tab wears its agent's. useAgentFavicon(name); // Runs once per mounted terminal — 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: page, label }); // 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 terminal `page`. }, [page]); // The agent's details open in a popover under its badge, so reading or // changing them never means leaving the terminal, or dimming it. A // subagent's terminal opens its agent's details: a subagent has none of its // own. const [detailOpen, setDetailOpen] = useState(false); const badgeRef = useRef(null); return (
} value={name} title={`${detailOpen ? "close" : "open"} ${name}'s details`} expanded={detailOpen} onClick={() => setDetailOpen((o) => !o)} /> } /> setDetailOpen(false)} anchorRef={badgeRef} />
); }