Add Badge + Dropdown Preact components, demo on swarm-ui /components

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.
This commit is contained in:
iris 2026-08-28 01:39:32 +02:00 committed by mara
commit 62cc0620c8
7 changed files with 365 additions and 1 deletions

View file

@ -0,0 +1,48 @@
/* <Dropdown> anchored option list. `position: absolute` against the
caller's `position: relative` wrapper (see Dropdown.tsx's file-top
comment) so it hangs directly under the badge that opened it.
`--bg-elev` is the same elevated-surface slot the badge uses when
open (../badge/Badge.css), so the pair reads as one continuous panel. */
.ui-dropdown {
position: absolute;
top: calc(100% + 0.25em);
left: 0;
z-index: 20;
display: flex;
flex-direction: column;
min-width: 12em;
padding: 0.25em;
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: 0.5em;
box-shadow: 0 0.4em 1em rgba(0, 0, 0, 0.35);
}
.ui-dropdown-item {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.1em;
min-height: 2.75em;
padding: 0.4em 0.7em;
border: none;
border-radius: 0.35em;
background: transparent;
color: var(--fg);
font: inherit;
text-align: left;
cursor: pointer;
}
.ui-dropdown-item:hover {
background: var(--border);
}
.ui-dropdown-item-active {
background: var(--purple-dim);
}
.ui-dropdown-item-active .ui-dropdown-item-label::before {
content: '✓ ';
color: var(--purple);
}
.ui-dropdown-item-desc {
font-size: 0.8em;
color: var(--muted);
}