Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: show agent panel read failures as text and keep loaded data

Inference, resource caps and linked accounts showed a failed read as a
badge with the reason in a hover title, which keyboard and touch users
cannot read. They now use ApiErrorPanel, and a list that already loaded
stays on screen under a failed refetch.
This commit is contained in:
iris 2026-10-21 22:23:38 +02:00 • committed by mara
commit e727774dc8
4 changed files with 38 additions and 29 deletions

View file

@ -435,7 +435,10 @@ name that already holds an account; delete that account first to replace it.
The agent's detail panel lists its linked accounts under **accounts**, one row
per account: kind, name and host. Opening an agent makes one request,
`GET /api/hives/{hive}/agents/{agent}/linked-accounts`, which returns every
account of that agent as names and hosts and never a credential.
account of that agent as names and hosts and never a credential. When the panel
fails to read the accounts, the inference settings or the resource caps, it
shows the reason as text. A list the panel already loaded stays on screen
beneath the error.
| kind | one row per | name | host |
| ------- | ------------------------------------------- | ----------- | ------------------------ |

View file

@ -205,21 +205,23 @@ export function AgentInference({ agent }: { agent: string }) {
};
}, [agent, reload]);
if (error) {
return (
<Badge
tone="negative"
value="inference unavailable"
title={error.detail ?? "reading the inference settings failed"}
/>
);
// Shown as text, not a hover title. Settings already loaded stay under a
// failed refetch, so a save that worked is not hidden by a read that did not.
const errorPanel = error ? (
<ApiErrorPanel
context="reading the inference settings failed"
problem={error}
/>
) : null;
if (view === null) {
return errorPanel ?? <span class="ui-agent-inference-muted">…</span>;
}
if (view === null) return <span class="ui-agent-inference-muted">…</span>;
const { configured, effective } = view;
const swarmModels = configured?.subagent_models ?? [];
return (
<div class="ui-agent-inference">
{errorPanel}
<ul class="ui-agent-inference-list">
<li>
{effective ? (

View file

@ -114,19 +114,18 @@ export function AgentLimits({ agent }: { agent: string }) {
};
}, [agent, reload]);
if (error) {
return (
<Badge
tone="negative"
value="resource caps unavailable"
title={error.detail ?? "reading the resource caps failed"}
/>
);
// Shown as text, not a hover title. Caps already loaded stay under a
// failed refetch, so a save that worked is not hidden by a read that did not.
const errorPanel = error ? (
<ApiErrorPanel context="reading the resource caps failed" problem={error} />
) : null;
if (caps === null) {
return errorPanel ?? <span class="ui-agent-limits-muted">…</span>;
}
if (caps === null) return <span class="ui-agent-limits-muted">…</span>;
return (
<div class="ui-agent-limits">
{errorPanel}
<ul class="ui-agent-limits-list">
<li>
<Badge label="cpu quota" value={caps.cpu_quota ?? HIVE_DECIDES} />

View file

@ -156,21 +156,26 @@ export function LinkedAccounts({
</ConfirmDialog>
);
if (error) {
return (
<Badge
tone="negative"
value="accounts unavailable"
title={error.detail ?? "listing linked accounts failed"}
/>
);
// Shown as text, not a hover title. A list already loaded stays under a
// failed refetch, so a link or delete that worked is not hidden by a read
// that did not.
const errorPanel = error ? (
<ApiErrorPanel context="listing linked accounts failed" problem={error} />
) : null;
if (accounts === null) {
return errorPanel ?? <span class="ui-linked-accounts-muted">…</span>;
}
if (accounts === null) return <span class="ui-linked-accounts-muted">…</span>;
if (accounts.length === 0) {
return <span class="ui-linked-accounts-muted">none linked</span>;
return (
<>
{errorPanel}
<span class="ui-linked-accounts-muted">none linked</span>
</>
);
}
return (
<>
{errorPanel}
<ul class="ui-linked-accounts">
{accounts.map((a) => (
<li key={`${a.kind}/${a.name}`}>