swarm-ui: a group pane's maximize and close are icon-only badges in its top right corner
The two controls sat among the pane's header badges as a labelled badge and a cross glyph. They are now icon badges in the top right corner, through a `trailing` slot of the terminal preview, so the header on the left keeps the agent, split, subagents and state badges and a narrow pane wraps less.
This commit is contained in:
parent
d733b1e2a9
commit
4876acd02a
5 changed files with 53 additions and 15 deletions
|
|
@ -169,11 +169,12 @@ a group of the two, with its own header tab at `/agents/group/<id>`. In a group,
|
||||||
the same drag onto a pane splits that pane, as many times as you like, and
|
the same drag onto a pane splits that pane, as many times as you like, and
|
||||||
dragging a pane's header badge onto another pane moves it there. The **split**
|
dragging a pane's header badge onto another pane moves it there. The **split**
|
||||||
button in a pane's header does the same for an agent whose terminal tab is open:
|
button in a pane's header does the same for an agent whose terminal tab is open:
|
||||||
**right** or **below** puts it beside the pane. **maximize** opens that
|
**right** or **below** puts it beside the pane. A pane can hold a subagent's
|
||||||
agent's own terminal in its tab, or goes to the tab when one is open already. A pane
|
terminal (its header reads `agent/subagent`), put there by the **subagents**
|
||||||
can hold a subagent's terminal (its header reads `agent/subagent`), put there by
|
badge in the pane's header. Two icon buttons sit in a pane's top right corner.
|
||||||
the **subagents** badge in the pane's header. The **×** takes the pane's
|
**Maximize** (the arrows pointing to the corners) opens that pane's own terminal
|
||||||
agent out of the group, and a group left with one agent goes, landing on that
|
in its tab, or goes to the tab when one is open already. **Close** (the cross)
|
||||||
|
takes the pane out of the group, and a group left with one pane goes, landing on that
|
||||||
agent's own terminal. Drag a divider to resize, or focus it and use the arrow
|
agent's own terminal. Drag a divider to resize, or focus it and use the arrow
|
||||||
keys. Panes have no minimum size; a divider stops at 2% so a pane can't
|
keys. Panes have no minimum size; a divider stops at 2% so a pane can't
|
||||||
vanish.
|
vanish.
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,27 @@ export function ExpandIcon() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A cross, for an icon-only close trigger (a pane's "take this out of the
|
||||||
|
// group"). Same size and stroke as `ExpandIcon`.
|
||||||
|
export function CloseIcon() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="1.3em"
|
||||||
|
height="1.3em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<line x1="5" y1="5" x2="19" y2="19" />
|
||||||
|
<line x1="19" y1="5" x2="5" y2="19" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// A funnel, the standard "filter" glyph — smaller than the other two
|
// A funnel, the standard "filter" glyph — smaller than the other two
|
||||||
// (1em not 1.3em) since its first caller sits inside a table header
|
// (1em not 1.3em) since its first caller sits inside a table header
|
||||||
// cell, not a nav trigger button; scale via the caller's own
|
// cell, not a nav trigger button; scale via the caller's own
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,12 @@
|
||||||
.ui-agent-term-preview-controls > * {
|
.ui-agent-term-preview-controls > * {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
/* The icon-only controls in the top right corner. */
|
||||||
|
.ui-agent-term-preview-trailing {
|
||||||
|
display: flex;
|
||||||
|
flex: none;
|
||||||
|
gap: 0.25em;
|
||||||
|
}
|
||||||
.ui-agent-term-preview-header-badges {
|
.ui-agent-term-preview-header-badges {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,7 @@ export function AgentTermPreview({
|
||||||
subagent,
|
subagent,
|
||||||
fullHeight = false,
|
fullHeight = false,
|
||||||
leading,
|
leading,
|
||||||
|
trailing,
|
||||||
}: {
|
}: {
|
||||||
agentName: string;
|
agentName: string;
|
||||||
// Set: this is one of `agentName`'s subagents, output only like the
|
// Set: this is one of `agentName`'s subagents, output only like the
|
||||||
|
|
@ -217,6 +218,9 @@ export function AgentTermPreview({
|
||||||
// A badge ahead of the connection badge, for the full-tab mount's
|
// A badge ahead of the connection badge, for the full-tab mount's
|
||||||
// link back to the agent's details.
|
// link back to the agent's details.
|
||||||
leading?: ComponentChildren;
|
leading?: ComponentChildren;
|
||||||
|
// Icon-only badges in the top right corner, for the full-tab mount's own
|
||||||
|
// controls (a group pane's maximize and close).
|
||||||
|
trailing?: ComponentChildren;
|
||||||
}) {
|
}) {
|
||||||
const paths = termPaths(agentName, subagent);
|
const paths = termPaths(agentName, subagent);
|
||||||
const { rows, connection, reconnect } = useSwarmTermStream(paths.stream);
|
const { rows, connection, reconnect } = useSwarmTermStream(paths.stream);
|
||||||
|
|
@ -293,6 +297,9 @@ export function AgentTermPreview({
|
||||||
onClick={() => openTab({ href: paths.page, label: paths.label })}
|
onClick={() => openTab({ href: paths.page, label: paths.label })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{trailing !== undefined && (
|
||||||
|
<div class="ui-agent-term-preview-trailing">{trailing}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="live terminal" ref={logRef} onScroll={handleScroll}>
|
<div class="live terminal" ref={logRef} onScroll={handleScroll}>
|
||||||
{rows.length === 0 ? (
|
{rows.length === 0 ? (
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,17 @@
|
||||||
// `/agents/group/:id`: each pane is one agent's full terminal, split by
|
// `/agents/group/:id`: each pane is one agent's full terminal, split by
|
||||||
// dragging another agent's terminal tab (or a pane's header) onto the edge of
|
// dragging another agent's terminal tab (or a pane's header) onto the edge of
|
||||||
// a pane, or by the split button in a pane's header. A pane can also be one of
|
// a pane, or by the split button in a pane's header. A pane can also be one of
|
||||||
// an agent's subagents, put beside it by the subagents menu. Dividers resize, and the
|
// an agent's subagents, put beside it by the subagents menu. Dividers resize,
|
||||||
// layout is saved in the browser (`useTermGroups`). A pane's × takes its agent
|
// and the layout is saved in the browser (`useTermGroups`). A pane's close icon
|
||||||
// out of the group; a group of one gives way to that agent's own terminal.
|
// takes its agent out of the group, its maximize icon opens the agent on its
|
||||||
|
// own; a group of one gives way to that agent's own terminal.
|
||||||
//
|
//
|
||||||
// Registers its header tab like an agent terminal does, so a reload or a
|
// Registers its header tab like an agent terminal does, so a reload or a
|
||||||
// bookmark reopens it. The tab shows the group's name, or its agents.
|
// bookmark reopens it. The tab shows the group's name, or its agents.
|
||||||
import { useEffect } from "preact/hooks";
|
import { useEffect } from "preact/hooks";
|
||||||
import { useLocation, type RouteComponentProps } from "wouter-preact";
|
import { useLocation, type RouteComponentProps } from "wouter-preact";
|
||||||
import { Badge } from "@hive/shared/badge.js";
|
import { Badge } from "@hive/shared/badge.js";
|
||||||
import { ExpandIcon } from "@hive/shared/icons.js";
|
import { CloseIcon, ExpandIcon } from "@hive/shared/icons.js";
|
||||||
import { AgentIcon } from "../../agent/AgentIcon.js";
|
import { AgentIcon } from "../../agent/AgentIcon.js";
|
||||||
import { SubagentsMenu } from "../../agent/terminal/SubagentsMenu.js";
|
import { SubagentsMenu } from "../../agent/terminal/SubagentsMenu.js";
|
||||||
import {
|
import {
|
||||||
|
|
@ -185,10 +186,13 @@ export function AgentGroupPage({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
trailing={
|
||||||
|
<>
|
||||||
<Badge
|
<Badge
|
||||||
icon={<ExpandIcon />}
|
value={<ExpandIcon />}
|
||||||
value="maximize"
|
title={`maximize: open ${key}'s terminal on its own`}
|
||||||
title={`open ${key}'s terminal on its own`}
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
open({
|
open({
|
||||||
href: termPaths(agent, subagent).page,
|
href: termPaths(agent, subagent).page,
|
||||||
|
|
@ -197,9 +201,8 @@ export function AgentGroupPage({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Badge
|
<Badge
|
||||||
variant="quiet"
|
value={<CloseIcon />}
|
||||||
value="×"
|
title={`close: take ${key} out of this group`}
|
||||||
title={`take ${key} out of this group`}
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
groups.update(id, (r) => removeAgent(r, key) ?? r)
|
groups.update(id, (r) => removeAgent(r, key) ?? r)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue