A `WantedMenu` badge dropdown on the agents table clips against
`.ui-table-scroll`s overflow the moment its row is the last (or
near-last) one — the popover extends past the table content the
scroll container bounds itself to. `Table.tsx` already solved the
identical clip for its own column-filter popover with a
position:fixed + portal computed from the anchor rect; `Dropdown` now
takes an optional `portal` prop that opts a caller into that same
recipe instead of a second hand-rolled copy of it. Off by default —
every other current caller (StatusChips x2, the components-page demo)
keeps its existing non-portal behavior unchanged.
mara, hyperhive#4079: "agent wanted state is multiple buttons insteaf
of a badge with dropdown ... same pattern as agent term badges with
dropdowns". The wanted column used to be a toggle badge plus a
separate quiet destroy badge, stacking under each other in the narrow
column. Replaced both with one WantedMenu badge that opens a Dropdown
with the three explicit states (up/offline/destroy) -- the exact
badge-triggers-a-dropdown shape the per-agent terminals StatusChips
already uses (and swarm-uis own ComponentsPage already demos with
sample data), built from the same shared Badge/Dropdown components.
"up" still declares straight away with no confirmation; "offline" and
"destroy" still go through the existing ConfirmDialog modals unchanged
-- only the trigger moved, the confirm behavior for the two directions
that already had one is untouched.
Explicit dropdown options also fix a real bug the old toggle had:
mara also asked "when no state is declared, i want to set it to
online" -- the old toggle inferred a target as the opposite of
snapshot.running for an undeclared row, so a click on an
undeclared-but-running agent silently declared it offline rather than
making its actual state explicit. The dropdown just lets "up" be
picked directly regardless of any inference, which is what she is
asking for -- flagging this reading explicitly in case an actual
one-time migration (auto-declaring every currently-undeclared agent
up) was intended instead, which this does not do.
Added a shared .ui-dropdown-anchor utility class to Dropdown.css --
this is the third near-identical "position: relative wrapper for a
badge that opens a Dropdown" (after agents own StatusChips.css and
swarm-uis ComponentsPage.css), so a new caller should not reinvent a
fourth copy. Left the two existing ones alone rather than migrating
them as a drive-by.
Verified with a local esbuild build + a throwaway mock /api/agents/status
server, screenshotted headlessly: the wanted column now shows exactly
one badge per row instead of stacked badges.
Pure `nix fmt` output from the commit before this one — no hand edits.
203 files: 52 md, 42 tsx, 32 js, 32 css, 21 ts, 13 html, 8 json, 3 mjs.
Reproduce with `nix develop -c nix fmt` on the parent commit; the result
should be byte-identical to this tree.
None of the 13 `.prettierignore` entries appears here — verified by
intersecting the changed-file list against the ignore file, with a
control proving the intersection finds a match when one exists.
mara, #3775: clicking a badge with its own dropdown open reopens it
instead of closing it. Root cause: the outside-click listener only
excludes the dropdown's own ref, not the sibling trigger that opened
it — a click on the trigger closes via that listener (pointerdown
fires first), then the trigger's own onClick toggle fires straight
after and reopens it, since its closure reads the pre-close state.
Dropdown now takes an optional anchorRef (the trigger's wrapper,
which callers already have for CSS positioning) and excludes it from
the outside-click check too — the same shape MetaNav's own hand-rolled
popover already uses correctly. Wired into StatusChips's Picker +
StatusMenu and swarm-ui's ComponentsPage demo (the only three Dropdown
consumers).
Verified two ways: reverted the fix, rebuilt, confirmed the bug
reproduces via a raw-CDP interaction test (two real clicks dispatched
through headless chromium, not just a static screenshot); restored
the fix, rebuilt, confirmed it passes.
mara, #3757: fold the separate alive badge into the turn-state badge,
with pause/resume + cancel-turn moved into its dropdown. Cancel-turn is
click-again-to-confirm, not a modal.
model/effort/ctx/cost stay as separate badges — not asked to fold
those in too.
--muted is color-mix()'d toward --base00/--bg — correct for dimmed text
on the page background, wrong for dimmed text sitting on the elevated
--purple-dim fill (badge/dropdown-item backgrounds). Badge's label/caret
used bare --muted there and mara measured it at ~1.3:1 contrast against
her theme's badge fill, essentially invisible.
--muted-on-dim mixes toward --purple-dim instead, same contrast-floor
technique as --muted itself, just anchored to the surface it's actually
used on. Applied to .ui-badge-label/.ui-badge-caret and to Dropdown's
active-item description (same bug: its row bg is --purple-dim too).
Ceiling note: even plain --fg only reaches ~3.4:1 against --purple-dim
in mara's theme, short of formal 4.5:1 AA — that's the theme's own
limit, not something the anchor choice can fix on its own. 90% gets
close to that ceiling (~3:1, more than double the old ~1.3:1) while
keeping a hint of the label/value visual distinction.
New shared Preact primitives for the per-agent terminal redesign:
- Badge (@hive/shared/badge.js): a labelled status pill. Plain <span>
when static (e.g. "alive"), a real <button> with a disclosure caret
when given onClick (e.g. opens a Dropdown, or toggles itself in
place). Same component either way so a status row reads as one
consistent set of badges regardless of which are interactive.
- Dropdown (@hive/shared/dropdown.js): a small option list anchored
directly under whatever opened it (no portal, no native <dialog> —
see the file comment for why). Closes on outside click or Escape.
This is the fix for the design guide's own named anti-example: the
agent page's model/effort pickers live in the overflow menu while the
current model/effort only show as a disconnected chip. Badge+Dropdown
composed together is that control moved inline, next to what it shows.
Demoed on swarm-ui's /components page: all Badge tones, a label-prefixed
badge, an interactive badge that opens a Dropdown (model-picker shape),
and an interactive badge that toggles itself (pause/resume shape).
Both packages build + tsc --noEmit clean.