Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: show an agent's details in a popover, not a modal dialog

The details open under the agent badge without dimming or blocking the page; a click outside or Escape closes it. New ui/popover primitive (anchored, portal, no backdrop) with a sample on the components page. A confirm or link dialog opened from inside it stays modal, and the popover ignores clicks and Escape that belong to that dialog. AgentDetailDialog becomes AgentDetailPopover.
This commit is contained in:
iris 2026-10-12 00:16:25 +02:00
commit f957e19d0d
7 changed files with 221 additions and 42 deletions

View file

@ -9,10 +9,10 @@
// 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, useState } from "preact/hooks";
import { useEffect, useRef, useState } from "preact/hooks";
import type { RouteComponentProps } from "wouter-preact";
import { Badge } from "@hive/shared/badge.js";
import { AgentDetailDialog } from "../../agent/detail/AgentDetailDialog.js";
import { AgentDetailPopover } from "../../agent/detail/AgentDetailPopover.js";
import { AgentIcon } from "../../agent/AgentIcon.js";
import {
AgentTermPreview,
@ -48,10 +48,12 @@ export function AgentTerminalPage({
// instead of once per terminal `page`.
}, [page]);
// The agent's details open in a dialog over the terminal, so reading or
// changing them never means leaving it. A subagent's terminal opens its
// agent's details: a subagent has none of its own.
// 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<HTMLSpanElement>(null);
return (
<div class="ui-agent-terminal-page">
@ -61,18 +63,22 @@ export function AgentTerminalPage({
fullHeight
showHeaderBadges
leading={
<Badge
icon={<AgentIcon name={name} small />}
value={name}
title={`open ${name}'s details`}
onClick={() => setDetailOpen(true)}
/>
<span ref={badgeRef}>
<Badge
icon={<AgentIcon name={name} small />}
value={name}
title={`${detailOpen ? "close" : "open"} ${name}'s details`}
expanded={detailOpen}
onClick={() => setDetailOpen((o) => !o)}
/>
</span>
}
/>
<AgentDetailDialog
<AgentDetailPopover
agent={name}
open={detailOpen}
onClose={() => setDetailOpen(false)}
anchorRef={badgeRef}
/>
</div>
);