swarm-ui: the schedule form picks agents from the same card grid as the broadcast
The grid becomes a shared AgentPicker; the broadcast composer uses it too.
This commit is contained in:
parent
175019607c
commit
79b1fc35c5
2 changed files with 128 additions and 102 deletions
51
frontend/packages/swarm-ui/src/agent/picker/AgentPicker.css
Normal file
51
frontend/packages/swarm-ui/src/agent/picker/AgentPicker.css
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* <AgentPicker>: each agent a labelled checkbox card, icon over name; the grid
|
||||
wraps to however many cards fit. */
|
||||
.ui-agent-picker {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(8em, 1fr));
|
||||
gap: 0.6em;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ui-agent-picker-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.35em;
|
||||
padding: 0.7em 0.5em 0.6em;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.5em;
|
||||
background: var(--bg);
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui-agent-picker-card:hover {
|
||||
border-color: var(--muted);
|
||||
}
|
||||
.ui-agent-picker-card-on {
|
||||
border-color: var(--purple);
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
.ui-agent-picker-card:focus-within {
|
||||
outline: 2px solid var(--purple);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.ui-agent-picker-check {
|
||||
position: absolute;
|
||||
top: 0.5em;
|
||||
left: 0.5em;
|
||||
margin: 0;
|
||||
}
|
||||
.ui-agent-picker-name {
|
||||
max-width: 100%;
|
||||
overflow-wrap: anywhere;
|
||||
text-align: center;
|
||||
}
|
||||
.ui-agent-picker-state {
|
||||
color: var(--muted);
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.ui-agent-picker-state-up {
|
||||
color: var(--green);
|
||||
}
|
||||
63
frontend/packages/swarm-ui/src/agent/picker/AgentPicker.tsx
Normal file
63
frontend/packages/swarm-ui/src/agent/picker/AgentPicker.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// <AgentPicker> — pick agents from a grid of cards, each a labelled checkbox
|
||||
// with the agent's icon over its name and, when the caller knows it, a state
|
||||
// line under that. The broadcast composer and the schedule form share it, so
|
||||
// choosing agents looks the same wherever the operator does it. It holds no
|
||||
// state: the caller owns the selection and what a toggle does to it.
|
||||
import { AgentIcon } from "../AgentIcon.js";
|
||||
import "./AgentPicker.css";
|
||||
|
||||
export interface AgentPickerOption {
|
||||
name: string;
|
||||
/** A line under the name, such as "running". Omitted: no line. */
|
||||
state?: string;
|
||||
/** Colours the state line as up. */
|
||||
up?: boolean;
|
||||
}
|
||||
|
||||
export function AgentPicker({
|
||||
options,
|
||||
selected,
|
||||
onToggle,
|
||||
label = "agents",
|
||||
}: {
|
||||
options: AgentPickerOption[];
|
||||
selected: string[];
|
||||
onToggle: (name: string) => void;
|
||||
label?: string;
|
||||
}) {
|
||||
return (
|
||||
<ul class="ui-agent-picker" aria-label={label}>
|
||||
{options.map((agent) => {
|
||||
const on = selected.includes(agent.name);
|
||||
return (
|
||||
<li key={agent.name}>
|
||||
<label
|
||||
class={
|
||||
"ui-agent-picker-card" + (on ? " ui-agent-picker-card-on" : "")
|
||||
}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="ui-agent-picker-check"
|
||||
checked={on}
|
||||
onChange={() => onToggle(agent.name)}
|
||||
/>
|
||||
<AgentIcon name={agent.name} />
|
||||
<span class="ui-agent-picker-name">{agent.name}</span>
|
||||
{agent.state !== undefined ? (
|
||||
<span
|
||||
class={
|
||||
"ui-agent-picker-state" +
|
||||
(agent.up ? " ui-agent-picker-state-up" : "")
|
||||
}
|
||||
>
|
||||
{agent.state}
|
||||
</span>
|
||||
) : null}
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue