Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: the agents page shows placeholder cards in the shape of the real ones while the roster loads

A new Skeleton (a shimmering shape, still where motion is off) and a region
that tells assistive tech once that something is loading. The agent card's
header is split into AgentCardLayout, a layout with a slot for each part: the
card fills the slots with the real icon, badges and controls, its new
AgentCardSkeleton twin fills the same slots with placeholder shapes, so the
card that replaces it lands in the same place. The agents page uses the twin in
place of its "loading…" text.
This commit is contained in:
iris 2026-11-05 23:18:33 +01:00 • committed by mara
commit 27c4a593bb
2 changed files with 277 additions and 50 deletions

View file

@ -7,7 +7,9 @@
// swarm-controller's API is up or not.
import { useRef, useState } from "preact/hooks";
import type { ComponentChildren } from "preact";
import { AgentListSkeleton } from "../agent/roster/AgentCardSkeleton.js";
import { Card } from "../ui/card/Card.js";
import { SkeletonRegion } from "../ui/skeleton/Skeleton.js";
import { FilterableView } from "../ui/filterable-view/FilterableView.js";
import {
MultiselectFilter,
@ -483,6 +485,14 @@ export function ComponentsPage() {
</div>
</Section>
<Section title="Skeleton">
<Sample label="agent card placeholders">
<SkeletonRegion label="the agents">
<AgentListSkeleton count={2} />
</SkeletonRegion>
</Sample>
</Section>
<Section title="MultiselectFilter">
<Sample label="trigger + checkbox popover">
<MultiselectFilterSample />

View file

@ -32,9 +32,11 @@ import { useState } from "preact/hooks";
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
import { useAgentActions } from "../../agent/roster/AgentActions.js";
import { AgentCard } from "../../agent/roster/AgentCard.js";
import { AgentListSkeleton } from "../../agent/roster/AgentCardSkeleton.js";
import { FRESHNESS, type AgentRow } from "../../agent/AgentTypes.js";
import { Button } from "../../ui/button/Button.js";
import { Dialog } from "../../ui/dialog/Dialog.js";
import { SkeletonRegion } from "../../ui/skeleton/Skeleton.js";
import { FilterableView } from "../../ui/filterable-view/FilterableView.js";
import {
RefreshIntervalPicker,
@ -154,7 +156,11 @@ export function AgentsPage() {
problem={error}
/>
) : null}
{!error && rows === null ? <p>loading…</p> : null}
{!error && rows === null ? (
<SkeletonRegion label="the agents">
<AgentListSkeleton />
</SkeletonRegion>
) : null}
{rows ? (
<FilterableView
view="cards"