diff --git a/docs/web-ui/design-guide.md b/docs/web-ui/design-guide.md index dc24facf..a3f3f3aa 100644 --- a/docs/web-ui/design-guide.md +++ b/docs/web-ui/design-guide.md @@ -147,7 +147,7 @@ that needs it, not after it's been styled inline and left for later — a page reaching for a component that's already there has ready-made blocks to build with, instead of the next contributor duplicating ad-hoc styling that someone then has to hunt down and consolidate. -`Panel`/`StatusChip`/`Table`/`TextField`/`SelectField`/`Button` are the +`Panel`/`Badge`/`Table`/`TextField`/`SelectField`/`Button` are the primitives that exist; `/components` always has the current, complete list — this doc won't try to keep a duplicate inventory in sync. diff --git a/frontend/packages/shared/src/badge/Badge.tsx b/frontend/packages/shared/src/badge/Badge.tsx index f2f4cae7..67163d4c 100644 --- a/frontend/packages/shared/src/badge/Badge.tsx +++ b/frontend/packages/shared/src/badge/Badge.tsx @@ -10,13 +10,15 @@ // "alive"). Same component either way so a page reads as one consistent // row of badges regardless of which ones happen to be interactive. // -// Distinct from swarm-ui's own `StatusChip` (`swarm-ui/src/ui/status-chip/`): -// that one is presentational-only and swarm-ui-local. This one lives here -// in `shared` because both swarm-ui *and* the per-agent page need the -// interactive shape, and `shared` is the one package both already depend -// on (see `docs/web-ui/design-guide.md`'s component-first + junk-drawer -// rules — this is the same visual language, applied where it's actually -// consumed). +// Formerly two components: swarm-ui had its own presentational-only +// `StatusChip`, a strict subset of this shape (no interactivity, no +// label/value split, no icon). Deleted in favour of this one — a +// non-interactive `Badge` renders identically, and having two components +// for the same visual pill just meant a page had to pick between them +// with no real distinction to justify it. Lives in `shared` because both +// swarm-ui *and* the per-agent page need the interactive shape, and +// `shared` is the one package both already depend on (see +// `docs/web-ui/design-guide.md`'s component-first + junk-drawer rules). import type { ComponentChildren } from 'preact'; import './Badge.css'; diff --git a/frontend/packages/shared/src/colors.css b/frontend/packages/shared/src/colors.css index bf78ee55..4dbcb5ec 100644 --- a/frontend/packages/shared/src/colors.css +++ b/frontend/packages/shared/src/colors.css @@ -49,7 +49,7 @@ (same hue/saturation, lower HSL lightness), not a literal upstream port like base00-07 above. A review round computed real WCAG contrast ratios and found the literal Latte accents fail badly as - `StatusChip` fill-text (green/amber/red/yellow on + `Badge`/chip fill-text (green/amber/red/yellow on `--purple-dim`/base03: 1.4:1-3:1, need 4.5:1) and even as plain text on `--bg`/base00 in the agent/dashboard packages that also consume this same file (2.3:1-4.8:1). Root cause: Latte's own diff --git a/frontend/packages/swarm-ui/src/pages/AgentsPage.tsx b/frontend/packages/swarm-ui/src/pages/AgentsPage.tsx index bfcb22b7..78f20bf8 100644 --- a/frontend/packages/swarm-ui/src/pages/AgentsPage.tsx +++ b/frontend/packages/swarm-ui/src/pages/AgentsPage.tsx @@ -21,6 +21,7 @@ import { useState } from 'preact/hooks'; import { ApiErrorPanel } from '@hive/shared/api-error-panel.js'; import { readApiError, type ProblemDetails } from '@hive/shared/api-error.js'; +import { Badge } from '@hive/shared/badge.js'; import { Button } from '../ui/button/Button.js'; import { Dialog } from '../ui/dialog/Dialog.js'; import { Panel } from '../ui/panel/Panel.js'; @@ -29,7 +30,6 @@ import { useRefreshInterval, type RefreshIntervalMs, } from '../ui/refresh-interval/RefreshInterval.js'; -import { StatusChip } from '../ui/status-chip/StatusChip.js'; import { Table, type TableColumn } from '../ui/table/Table.js'; import { CreateAgentForm } from './CreateAgentForm.js'; @@ -50,9 +50,9 @@ const COLUMNS: TableColumn[] = [ header: 'config PR', render: (a) => a.configPr ? ( - #{a.configPr.pr_number} diff --git a/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx b/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx index f2c1c0b5..a3a6d78d 100644 --- a/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx +++ b/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx @@ -10,7 +10,6 @@ import type { ComponentChildren } from 'preact'; import { Panel } from '../ui/panel/Panel.js'; import { RelativeTime } from '../ui/relative-time/RelativeTime.js'; import { RefreshIntervalPicker, type RefreshIntervalMs } from '../ui/refresh-interval/RefreshInterval.js'; -import { StatusChip, type ChipTone } from '../ui/status-chip/StatusChip.js'; import { Table, type TableColumn } from '../ui/table/Table.js'; import { TextField } from '../ui/text-field/TextField.js'; import { SelectField, type SelectOption } from '../ui/select-field/SelectField.js'; @@ -37,8 +36,6 @@ function Sample({ label, children }: { label: string; children: ComponentChildre ); } -const CHIP_TONES: ChipTone[] = ['neutral', 'positive', 'warning', 'negative']; - interface Row { name: string; detail: string; @@ -47,7 +44,7 @@ interface Row { const TABLE_COLUMNS: TableColumn[] = [ { key: 'name', header: 'name', render: (r) => r.name }, { key: 'detail', header: 'detail', render: (r) => r.detail }, - { key: 'status', header: 'status', render: () => }, + { key: 'status', header: 'status', render: () => }, ]; const TABLE_ROWS: Row[] = [ @@ -170,16 +167,6 @@ export function ComponentsPage() { -
-
- {CHIP_TONES.map((tone) => ( - - - - ))} -
-
-
r.name} /> diff --git a/frontend/packages/swarm-ui/src/pages/HivesPage.tsx b/frontend/packages/swarm-ui/src/pages/HivesPage.tsx index 369b1df4..118937d2 100644 --- a/frontend/packages/swarm-ui/src/pages/HivesPage.tsx +++ b/frontend/packages/swarm-ui/src/pages/HivesPage.tsx @@ -15,6 +15,7 @@ import { useState } from 'preact/hooks'; import { ApiErrorPanel } from '@hive/shared/api-error-panel.js'; import { readApiError, type ProblemDetails } from '@hive/shared/api-error.js'; +import { Badge, type BadgeTone } from '@hive/shared/badge.js'; import { Panel } from '../ui/panel/Panel.js'; import { RelativeTime } from '../ui/relative-time/RelativeTime.js'; import { @@ -22,7 +23,6 @@ import { useRefreshInterval, type RefreshIntervalMs, } from '../ui/refresh-interval/RefreshInterval.js'; -import { StatusChip, type ChipTone } from '../ui/status-chip/StatusChip.js'; import { Table, type TableColumn } from '../ui/table/Table.js'; type Freshness = 'fresh' | 'stale' | 'never_reported' | 'unknown'; @@ -41,7 +41,7 @@ interface HiveStatus { // One tone + label per freshness value. `unknown` (reporting but absent // from the roster) reads `negative` — it's the one case this endpoint // cannot vouch for at all, per `status.rs`'s doc comment. -const FRESHNESS: Record = { +const FRESHNESS: Record = { fresh: { tone: 'positive', label: 'fresh' }, stale: { tone: 'warning', label: 'stale' }, never_reported: { tone: 'neutral', label: 'never reported' }, @@ -68,9 +68,9 @@ const COLUMNS: TableColumn[] = [ render: (h) => { const { tone, label } = FRESHNESS[h.freshness]; return ( - {label} () diff --git a/frontend/packages/swarm-ui/src/ui/status-chip/StatusChip.css b/frontend/packages/swarm-ui/src/ui/status-chip/StatusChip.css deleted file mode 100644 index c6908552..00000000 --- a/frontend/packages/swarm-ui/src/ui/status-chip/StatusChip.css +++ /dev/null @@ -1,21 +0,0 @@ -.ui-chip { - display: inline-block; - padding: 0.15em 0.6em; - border-radius: 1em; - font-size: 0.85em; - line-height: 1.4; - background: var(--purple-dim); - color: var(--fg); -} -.ui-chip-neutral { - color: var(--muted); -} -.ui-chip-positive { - color: var(--green); -} -.ui-chip-warning { - color: var(--amber); -} -.ui-chip-negative { - color: var(--red); -} diff --git a/frontend/packages/swarm-ui/src/ui/status-chip/StatusChip.tsx b/frontend/packages/swarm-ui/src/ui/status-chip/StatusChip.tsx deleted file mode 100644 index 0db5ef24..00000000 --- a/frontend/packages/swarm-ui/src/ui/status-chip/StatusChip.tsx +++ /dev/null @@ -1,25 +0,0 @@ -// — a small labelled state indicator. Generic over -// `tone` rather than a fixed status vocabulary (`online`/`offline`/…) -// because the first real caller (the hive roster) starts with a -// single static "configured" tone and grows real online/stale/offline -// states once a status rollup lands server-side — same component, -// richer data later, no rebuild. -import type { ComponentChildren } from 'preact'; -import './StatusChip.css'; - -export type ChipTone = 'neutral' | 'positive' | 'warning' | 'negative'; - -// `label` takes renderable children, not just a string — a freshness -// chip embeds a live `` alongside its static text (e.g. -// "fresh (5s ago)" where the age keeps ticking), and a plain string -// couldn't carry that without the chip reaching back into a caller's -// formatting choices. -export function StatusChip({ - tone = 'neutral', - label, -}: { - tone?: ChipTone; - label: ComponentChildren; -}) { - return {label}; -}