From 33c230665b3c705414df6cafc3b6da2d0cbf63a0 Mon Sep 17 00:00:00 2001 From: iris Date: Fri, 6 Nov 2026 00:22:14 +0100 Subject: [PATCH] swarm-ui: the permissions box lists every permission instead of a narrow scroll area The checkbox list was capped at 12em and scrolled, and the box shrinks the list to its widest label, so the scrollbar sat in the middle of the popover with the lower permissions hidden. The permissions are a short fixed list: MultiselectFilterOptions takes scrollable={false} and the permissions box uses it. Filter lists that can grow long keep the scroll area. --- docs/swarm/ui.md | 3 ++- .../swarm-ui/src/agent/detail/AgentPermissions.tsx | 1 + .../src/ui/multiselect-filter/MultiselectFilter.css | 4 ++++ .../src/ui/multiselect-filter/MultiselectFilter.tsx | 11 ++++++++++- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/swarm/ui.md b/docs/swarm/ui.md index cf17723c..f6a7fdcd 100644 --- a/docs/swarm/ui.md +++ b/docs/swarm/ui.md @@ -690,7 +690,8 @@ a badge reading the default set, none, or a count. A swarm grant is the agent's whole set, so it follows the agent to whichever hive runs it. Selecting the badge opens a box under it with one checkbox per permission the controller knows (`GET /api/permissions`), ticked as the grant stands, or on the default -set when the swarm sets none. +set when the swarm sets none. The box lists every permission at once, without a +scroll area. Each tick saves at once, as a `PUT /api/agents/{agent}/permissions` of the ticked boxes. A `PUT` replaces the stored list whole, so an unticked box revokes that diff --git a/frontend/packages/swarm-ui/src/agent/detail/AgentPermissions.tsx b/frontend/packages/swarm-ui/src/agent/detail/AgentPermissions.tsx index 6c0519c8..cad4c6e7 100644 --- a/frontend/packages/swarm-ui/src/agent/detail/AgentPermissions.tsx +++ b/frontend/packages/swarm-ui/src/agent/detail/AgentPermissions.tsx @@ -224,6 +224,7 @@ export function AgentPermissions({ filter={{ values: ticked, negate: false }} onToggle={(v) => void toggle(v)} disabled={busy} + scrollable={false} /> ) : ( diff --git a/frontend/packages/swarm-ui/src/ui/multiselect-filter/MultiselectFilter.css b/frontend/packages/swarm-ui/src/ui/multiselect-filter/MultiselectFilter.css index 698c77e0..26e97b65 100644 --- a/frontend/packages/swarm-ui/src/ui/multiselect-filter/MultiselectFilter.css +++ b/frontend/packages/swarm-ui/src/ui/multiselect-filter/MultiselectFilter.css @@ -10,6 +10,10 @@ max-height: 12em; overflow-y: auto; } +.ui-multiselect-filter-options-all { + max-height: none; + overflow-y: visible; +} .ui-multiselect-filter-checkbox { display: flex; align-items: center; diff --git a/frontend/packages/swarm-ui/src/ui/multiselect-filter/MultiselectFilter.tsx b/frontend/packages/swarm-ui/src/ui/multiselect-filter/MultiselectFilter.tsx index cf0066e3..a1e20a6b 100644 --- a/frontend/packages/swarm-ui/src/ui/multiselect-filter/MultiselectFilter.tsx +++ b/frontend/packages/swarm-ui/src/ui/multiselect-filter/MultiselectFilter.tsx @@ -31,15 +31,24 @@ export function MultiselectFilterOptions({ filter, onToggle, disabled = false, + scrollable = true, }: { options: string[]; filter: MultiselectFilterState; onToggle: (value: string) => void; /** Greys the boxes out, e.g. while a change is being saved. */ disabled?: boolean; + /** A long list scrolls inside its box. Off: every option shows, for a short + * fixed list that a scroll area would only hide part of. */ + scrollable?: boolean; }) { return ( -
+
{options.map((v) => (