swarm-ui: one link-account menu, and no subagents section without subagents
The three link-account badges were wider than the agent detail panel and spilled out of it; they are now a single badge opening a dropdown of the three kinds. The subagents row, heading included, is not rendered for an agent that has none.
This commit is contained in:
parent
66df552f68
commit
2c1a9fa488
4 changed files with 75 additions and 55 deletions
|
|
@ -35,6 +35,7 @@ import { AgentTermPreview } from "./AgentTermPreview.js";
|
|||
import { SubagentTerms } from "./SubagentTerms.js";
|
||||
import { FRESHNESS, type AgentRow } from "./AgentTypes.js";
|
||||
import { LinkedAccounts } from "./LinkedAccounts.js";
|
||||
import { LinkAccountMenu } from "./LinkAccountMenu.js";
|
||||
import { Button } from "../../ui/button/Button.js";
|
||||
import { ConfirmDialog } from "../../ui/confirm-dialog/ConfirmDialog.js";
|
||||
import { Dialog } from "../../ui/dialog/Dialog.js";
|
||||
|
|
@ -602,53 +603,14 @@ export function AgentsPage() {
|
|||
</dd>
|
||||
</dl>
|
||||
<div class="ui-agent-detail-actions">
|
||||
<Badge
|
||||
variant="quiet"
|
||||
icon={<LinkIcon />}
|
||||
value="link matrix account"
|
||||
onClick={
|
||||
detailTarget.hive
|
||||
? () => setMatrixTarget(detailTarget)
|
||||
: undefined
|
||||
}
|
||||
disabled={!detailTarget.hive}
|
||||
title={
|
||||
detailTarget.hive
|
||||
? `link a matrix account to ${detailTarget.name}`
|
||||
: "no hive on record for this agent — nothing to link against"
|
||||
}
|
||||
/>
|
||||
<Badge
|
||||
variant="quiet"
|
||||
icon={<LinkIcon />}
|
||||
value="link forge account"
|
||||
onClick={
|
||||
detailTarget.hive
|
||||
? () => setForgeTarget(detailTarget)
|
||||
: undefined
|
||||
}
|
||||
disabled={!detailTarget.hive}
|
||||
title={
|
||||
detailTarget.hive
|
||||
? `link an external forge account to ${detailTarget.name}`
|
||||
: "no hive on record for this agent — nothing to link against"
|
||||
}
|
||||
/>
|
||||
<Badge
|
||||
variant="quiet"
|
||||
icon={<LinkIcon />}
|
||||
value="link github account"
|
||||
onClick={
|
||||
detailTarget.hive
|
||||
? () => setGithubTarget(detailTarget)
|
||||
: undefined
|
||||
}
|
||||
disabled={!detailTarget.hive}
|
||||
title={
|
||||
detailTarget.hive
|
||||
? `link a github account to ${detailTarget.name}`
|
||||
: "no hive on record for this agent — nothing to link against"
|
||||
}
|
||||
<LinkAccountMenu
|
||||
agent={detailTarget.name}
|
||||
hasHive={detailTarget.hive !== null}
|
||||
onSelect={(kind) => {
|
||||
if (kind === "matrix") setMatrixTarget(detailTarget);
|
||||
else if (kind === "forge") setForgeTarget(detailTarget);
|
||||
else setGithubTarget(detailTarget);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* The agent's live terminal and its input box. */}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
// <LinkAccountMenu> — one "link account" badge that opens a `Dropdown` with
|
||||
// the three kinds of account an agent can be linked to, in place of one
|
||||
// badge per kind: three side-by-side badges were wider than the detail
|
||||
// panel and spilled out of it. Presentational only, same split as
|
||||
// `WantedMenu`: the caller owns what each selection opens.
|
||||
import { useRef, useState } from "preact/hooks";
|
||||
import { Badge } from "@hive/shared/badge.js";
|
||||
import { Dropdown, type DropdownOption } from "@hive/shared/dropdown.js";
|
||||
import { LinkIcon } from "@hive/shared/icons.js";
|
||||
|
||||
export type LinkKind = "matrix" | "forge" | "github";
|
||||
|
||||
const OPTIONS: DropdownOption[] = [
|
||||
{ value: "matrix", label: "matrix account" },
|
||||
{ value: "forge", label: "forge account" },
|
||||
{ value: "github", label: "github account" },
|
||||
];
|
||||
|
||||
export function LinkAccountMenu({
|
||||
agent,
|
||||
hasHive,
|
||||
onSelect,
|
||||
}: {
|
||||
agent: string;
|
||||
/** Linking writes against the agent's hive, so without one there is
|
||||
* nothing to link against and the menu stays shut. */
|
||||
hasHive: boolean;
|
||||
onSelect: (kind: LinkKind) => void;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const anchorRef = useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<div class="ui-dropdown-anchor" ref={anchorRef}>
|
||||
<Badge
|
||||
variant="quiet"
|
||||
icon={<LinkIcon />}
|
||||
value="link account"
|
||||
onClick={hasHive ? () => setOpen((o) => !o) : undefined}
|
||||
expanded={open}
|
||||
disabled={!hasHive}
|
||||
title={
|
||||
hasHive
|
||||
? `link a matrix, forge or github account to ${agent}`
|
||||
: "no hive on record for this agent — nothing to link against"
|
||||
}
|
||||
/>
|
||||
<Dropdown
|
||||
open={open}
|
||||
options={OPTIONS}
|
||||
label={`link an account to ${agent}`}
|
||||
onSelect={(value) => {
|
||||
setOpen(false);
|
||||
onSelect(value as LinkKind);
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
anchorRef={anchorRef}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.ui-subagent-terms-heading,
|
||||
.ui-subagent-terms-empty {
|
||||
.ui-subagent-terms-heading {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,16 +37,15 @@ export function SubagentTerms({ agentName }: { agentName: string }) {
|
|||
refresh().catch((e: unknown) => setError({ detail: String(e) }));
|
||||
});
|
||||
|
||||
// An agent with no subagents shows nothing here — no heading, no
|
||||
// "none" line — and so does the first fetch while it is in flight. A
|
||||
// failed fetch still shows, so the error is not swallowed.
|
||||
if (!error && (names === null || names.length === 0)) return null;
|
||||
|
||||
return (
|
||||
<section class="ui-subagent-terms" aria-label={`${agentName}'s subagents`}>
|
||||
<div class="ui-subagent-terms-list">
|
||||
<span class="ui-subagent-terms-heading">subagents</span>
|
||||
{names === null && !error && <Badge value="loading…" />}
|
||||
{names?.length === 0 && (
|
||||
<span class="ui-subagent-terms-empty">
|
||||
none with output on record
|
||||
</span>
|
||||
)}
|
||||
{names?.map((name) => (
|
||||
<Badge
|
||||
key={name}
|
||||
|
|
|
|||
Loading…
Reference in a new issue