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 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, per account: kind, name and host. Opening an agent makes one request,
`GET /api/hives/{hive}/agents/{agent}/linked-accounts`, which returns every `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 | | kind | one row per | name | host |
| ------- | ------------------------------------------- | ----------- | ------------------------ | | ------- | ------------------------------------------- | ----------- | ------------------------ |

View file

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

View file

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

View file

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