swarm-ui: split agent status badge into technical status + free-text message
mara: 'separate technical status from agent provided status string' — the agents page's status column concatenated the freshness label with the agent's own free-text status_text into one Badge pill, meant for a short discrete label, not a full sentence. Long status strings blew the row out and made the table look messy. Now two columns: 'status' is just the freshness badge (fresh/stale/ never reported/not in swarm identity) plus relative time, unchanged in meaning; 'message' is the agent's own status string as plain prose in its own capped-width, wrapping cell (Table's new cellClass, ui-table- prose), not a badge. A stopped agent (status_text always null per the wire contract) shows an em dash there instead of a stale message. Verified against a static render of the built CSS (real dark theme, real Badge/table classes, the exact long strings from the screenshot mara attached) — wraps cleanly within the column instead of overflowing. Typecheck + build clean.
This commit is contained in:
parent
589ace3438
commit
675fc72e01
3 changed files with 32 additions and 7 deletions
|
|
@ -212,13 +212,13 @@ export function AgentsPage() {
|
||||||
{
|
{
|
||||||
key: "status",
|
key: "status",
|
||||||
header: "status",
|
header: "status",
|
||||||
|
// Technical freshness only — the agent's own free-text status
|
||||||
|
// string used to be concatenated into this same badge, which
|
||||||
|
// stuffed a full sentence into a pill meant for a short discrete
|
||||||
|
// label and blew the row out (mara: "looks messy"). That string
|
||||||
|
// now lives in its own "message" column below.
|
||||||
render: (a) => {
|
render: (a) => {
|
||||||
const { tone, label } = FRESHNESS[a.freshness];
|
const { tone, label } = FRESHNESS[a.freshness];
|
||||||
const text = a.snapshot?.status_text;
|
|
||||||
// A `running: false` snapshot always carries `status_text: null`
|
|
||||||
// (the wire contract's own rule, not something this page derives),
|
|
||||||
// so an agent that reported recently but isn't running still shows
|
|
||||||
// a bare freshness badge rather than a stale status string.
|
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge
|
||||||
tone={tone}
|
tone={tone}
|
||||||
|
|
@ -229,7 +229,6 @@ export function AgentsPage() {
|
||||||
}
|
}
|
||||||
value={
|
value={
|
||||||
<>
|
<>
|
||||||
{text ? `${text} — ` : ""}
|
|
||||||
{label}
|
{label}
|
||||||
{a.last_seen_unix !== null ? (
|
{a.last_seen_unix !== null ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -243,6 +242,17 @@ export function AgentsPage() {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "message",
|
||||||
|
header: "message",
|
||||||
|
cellClass: "ui-table-prose",
|
||||||
|
// Plain text, not a Badge — the agent's own free-form status
|
||||||
|
// sentence, as distinct from the technical freshness state in the
|
||||||
|
// "status" column. A `running: false` snapshot always carries
|
||||||
|
// `status_text: null` (the wire contract's own rule), so a stopped
|
||||||
|
// agent just shows an em dash here rather than a stale message.
|
||||||
|
render: (a) => a.snapshot?.status_text ?? "—",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "wanted",
|
key: "wanted",
|
||||||
header: "wanted",
|
header: "wanted",
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,12 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1.25em 0.75em;
|
padding: 1.25em 0.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A cell holding free-form prose (an operator- or agent-provided string,
|
||||||
|
not a short discrete label) rather than open-ended growth that drags
|
||||||
|
the whole table wider — caps the column and wraps within it instead. */
|
||||||
|
.ui-table-prose {
|
||||||
|
max-width: 32em;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ export interface TableColumn<T> {
|
||||||
// can pass real markup instead of forking a second table primitive.
|
// can pass real markup instead of forking a second table primitive.
|
||||||
header: ComponentChildren;
|
header: ComponentChildren;
|
||||||
render: (row: T) => ComponentChildren;
|
render: (row: T) => ComponentChildren;
|
||||||
|
/** Extra class on this column's `<td>` — e.g. `ui-table-prose` for a
|
||||||
|
* free-text cell that should cap its width and wrap instead of
|
||||||
|
* dragging the whole table wider. Omit for the default cell styling. */
|
||||||
|
cellClass?: string;
|
||||||
/**
|
/**
|
||||||
* ARIA sort state for this column's `<th>` — `'ascending'` /
|
* ARIA sort state for this column's `<th>` — `'ascending'` /
|
||||||
* `'descending'` while this is the active sort column, `'none'` while
|
* `'descending'` while this is the active sort column, `'none'` while
|
||||||
|
|
@ -72,7 +76,9 @@ export function Table<T>({
|
||||||
rows.map((row) => (
|
rows.map((row) => (
|
||||||
<tr key={rowKey(row)}>
|
<tr key={rowKey(row)}>
|
||||||
{columns.map((c) => (
|
{columns.map((c) => (
|
||||||
<td key={c.key}>{c.render(row)}</td>
|
<td key={c.key} class={c.cellClass}>
|
||||||
|
{c.render(row)}
|
||||||
|
</td>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue