hive ui: remove the counters on the tabs
The pills on 4G3NTS, R3BU1LD QU3U3 and K3PT ST4T3, the poll that fed them, the two count helpers, their styles, and the tab strip's unused badge option.
This commit is contained in:
parent
46001be283
commit
a781addcfe
7 changed files with 7 additions and 93 deletions
|
|
@ -106,8 +106,7 @@ build log history — is two tabs of the dashboard: **R3BU1LD QU3U3**
|
||||||
dashboard's one tab strip, `/api/state` snapshot and
|
dashboard's one tab strip, `/api/state` snapshot and
|
||||||
`/api/dashboard/stream` subscription serve them, with
|
`/api/dashboard/stream` subscription serve them, with
|
||||||
`rebuild_queue_changed` on the stream alongside the dashboard's other
|
`rebuild_queue_changed` on the stream alongside the dashboard's other
|
||||||
kinds. The count pill on
|
kinds.
|
||||||
R3BU1LD QU3U3 is the number of rebuilds in flight.
|
|
||||||
The 4G3NTS tab does **not** read the graph endpoint, by design — the swarm
|
The 4G3NTS tab does **not** read the graph endpoint, by design — the swarm
|
||||||
view stays independent of job-queue internals. Its per-agent pending
|
view stays independent of job-queue internals. Its per-agent pending
|
||||||
badges are transient-only, and its queue-summary banner reads the much
|
badges are transient-only, and its queue-summary banner reads the much
|
||||||
|
|
@ -157,7 +156,7 @@ the tree — one per lifecycle state, matching the row glyphs — lets the
|
||||||
operator narrow which root groups render; unchecking a state re-fetches
|
operator narrow which root groups render; unchecking a state re-fetches
|
||||||
`GET /api/jobq/graph?states=<checked, comma-joined>` rather than
|
`GET /api/jobq/graph?states=<checked, comma-joined>` rather than
|
||||||
hiding rows client-side, so the `onUpdate` callback's node list (and
|
hiding rows client-side, so the `onUpdate` callback's node list (and
|
||||||
everything downstream of it — the count pill, the live-log panel) only
|
everything downstream of it — the live-log panel) only
|
||||||
ever sees what's actually shown. Filtering is by a **root's own**
|
ever sees what's actually shown. Filtering is by a **root's own**
|
||||||
state, which is already its subtree's rolled-up answer, so the filter
|
state, which is already its subtree's rolled-up answer, so the filter
|
||||||
keeps or drops a whole group, never splitting mid-tree. Default selection is
|
keeps or drops a whole group, never splitting mid-tree. Default selection is
|
||||||
|
|
@ -175,7 +174,7 @@ Below the queue, a **live build-log panel** (`#rebuild-live-log`,
|
||||||
inline — collapsible, with a live/ok/fail badge and a `↓ raw`
|
inline — collapsible, with a live/ok/fail badge and a `↓ raw`
|
||||||
download. It's keyed to the first `Running` node (in wire order)
|
download. It's keyed to the first `Running` node (in wire order)
|
||||||
whose `payload.data.build_log_id` has a value — read from the `onUpdate`
|
whose `payload.data.build_log_id` has a value — read from the `onUpdate`
|
||||||
callback's node list, same source as the count pill, no separate
|
callback's node list, no separate
|
||||||
fetch — and **polls** `GET /api/build-log/{id}` every 2s
|
fetch — and **polls** `GET /api/build-log/{id}` every 2s
|
||||||
(`fetchAndRenderLiveLog` / `liveLogPollTimer`); not an `EventSource`
|
(`fetchAndRenderLiveLog` / `liveLogPollTimer`); not an `EventSource`
|
||||||
(that's the BUILD L0GS tab's own per-row expand view below — `GET
|
(that's the BUILD L0GS tab's own per-row expand view below — `GET
|
||||||
|
|
|
||||||
|
|
@ -247,21 +247,6 @@ function renderRebuildLiveLog() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── rebuild-queue count pill ─────────────────────────────────────────────────
|
|
||||||
// The queue's in-flight count, for the R3BU1LD QU3U3 tab's pill (tabs.js
|
|
||||||
// reads it on its refresh tick). Pending/Running/Finishing = in flight
|
|
||||||
// (Finishing = own work done, a sub-node still running — still counts).
|
|
||||||
// Root nodes only: each is one queue entry.
|
|
||||||
export function rebuildCount() {
|
|
||||||
return jobqNodes.filter(
|
|
||||||
(n) =>
|
|
||||||
n.parent == null &&
|
|
||||||
(n.state === "Pending" ||
|
|
||||||
n.state === "Running" ||
|
|
||||||
n.state === "Finishing"),
|
|
||||||
).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── BUILD L0GS tab ───────────────────────────────────────────────────────────
|
// ─── BUILD L0GS tab ───────────────────────────────────────────────────────────
|
||||||
// All-agents build log history with expand-to-detail. Live builds stream in
|
// All-agents build log history with expand-to-detail. Live builds stream in
|
||||||
// real time. Lazy-loaded on first tab activation; auto-refreshes (debounced
|
// real time. Lazy-loaded on first tab activation; auto-refreshes (debounced
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@
|
||||||
|
|
||||||
/* ─── tabbed dashboard chrome ──────────────────────────────────────
|
/* ─── tabbed dashboard chrome ──────────────────────────────────────
|
||||||
Top-of-page sticky header with banner + tab strip. SSE stays
|
Top-of-page sticky header with banner + tab strip. SSE stays
|
||||||
alive across tab switches so count pills update live on inactive
|
alive across tab switches. See docs/web-ui/dashboard.md::Chrome header + Tab strip for the
|
||||||
tabs. See docs/web-ui/dashboard.md::Chrome header + Tab strip for the
|
|
||||||
routing model. */
|
routing model. */
|
||||||
|
|
||||||
body.dashboard-shell {
|
body.dashboard-shell {
|
||||||
|
|
@ -112,23 +111,6 @@ body.dashboard-shell {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.tab-count {
|
|
||||||
display: inline-block;
|
|
||||||
background: var(--purple-dim);
|
|
||||||
color: var(--purple);
|
|
||||||
border-radius: 999px;
|
|
||||||
padding: 0 0.55em;
|
|
||||||
min-width: 1.6em;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.82em;
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
/* `display` above beats the UA's `[hidden]` rule, so an empty count (the
|
|
||||||
4G3NTS and R3BU1LD QU3U3 pills at zero) would otherwise draw a "0". */
|
|
||||||
.tab-count[hidden] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tab pane visibility — createTabStrip (@hive/shared/tabs.js) sets the
|
/* Tab pane visibility — createTabStrip (@hive/shared/tabs.js) sets the
|
||||||
`hidden` attribute on every inactive pane (resolved from the URL hash;
|
`hidden` attribute on every inactive pane (resolved from the URL hash;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
data-tab="agents"
|
data-tab="agents"
|
||||||
>
|
>
|
||||||
<span class="tab-label">◆ 4G3NTS ◆</span>
|
<span class="tab-label">◆ 4G3NTS ◆</span>
|
||||||
<span class="tab-count" id="tab-count-agents" hidden></span>
|
|
||||||
</a>
|
</a>
|
||||||
<!-- The build lifecycle is two tabs here (builds.js): the rebuild
|
<!-- The build lifecycle is two tabs here (builds.js): the rebuild
|
||||||
queue with its live log, and the build log history. Kept state
|
queue with its live log, and the build log history. Kept state
|
||||||
|
|
@ -41,7 +40,6 @@
|
||||||
data-tab="rebuild"
|
data-tab="rebuild"
|
||||||
>
|
>
|
||||||
<span class="tab-label">◆ R3BU1LD QU3U3 ◆</span>
|
<span class="tab-label">◆ R3BU1LD QU3U3 ◆</span>
|
||||||
<span class="tab-count" id="tab-count-rebuild" hidden></span>
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
|
|
@ -63,7 +61,6 @@
|
||||||
data-tab="kept"
|
data-tab="kept"
|
||||||
>
|
>
|
||||||
<span class="tab-label">◆ K3PT ST4T3 ◆</span>
|
<span class="tab-label">◆ K3PT ST4T3 ◆</span>
|
||||||
<span class="tab-count" id="tab-count-kept" hidden></span>
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- (Peer hives are no longer a tab — they render as a headline
|
<!-- (Peer hives are no longer a tab — they render as a headline
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,6 @@ const fmtAgeDays = (ts) => {
|
||||||
return d + " days ago";
|
return d + " days ago";
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Number of kept-state entries, for the tab's count pill. */
|
|
||||||
export function keptCount() {
|
|
||||||
return tombstonesState.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function syncKeptFromSnapshot(s) {
|
export function syncKeptFromSnapshot(s) {
|
||||||
tombstonesState = (s.tombstones || []).slice();
|
tombstonesState = (s.tombstones || []).slice();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,9 @@ import {
|
||||||
initBuilds,
|
initBuilds,
|
||||||
onBuildsTabShown,
|
onBuildsTabShown,
|
||||||
onRebuildQueueChanged,
|
onRebuildQueueChanged,
|
||||||
rebuildCount,
|
|
||||||
} from "./builds.js";
|
} from "./builds.js";
|
||||||
import {
|
import {
|
||||||
applyTombstonesChanged,
|
applyTombstonesChanged,
|
||||||
keptCount,
|
|
||||||
renderKept,
|
renderKept,
|
||||||
syncKeptFromSnapshot,
|
syncKeptFromSnapshot,
|
||||||
} from "./kept.js";
|
} from "./kept.js";
|
||||||
|
|
@ -286,41 +284,6 @@ import { renderUserMenu } from "./user-menu.js";
|
||||||
// (default 4G3NTS), toggles the active tab/pane + aria-selected, and
|
// (default 4G3NTS), toggles the active tab/pane + aria-selected, and
|
||||||
// fires activateTab for the per-tab side-effects on every change.
|
// fires activateTab for the per-tab side-effects on every change.
|
||||||
createTabStrip($("tabbar"), { defaultId: "agents", onShow: activateTab });
|
createTabStrip($("tabbar"), { defaultId: "agents", onShow: activateTab });
|
||||||
// The rebuild queue mounts now, not on first show, so its tab's count pill
|
// The rebuild queue mounts now, not on first show.
|
||||||
// is live from the start.
|
|
||||||
initBuilds();
|
initBuilds();
|
||||||
|
|
||||||
// Tab count pills — pure derived data from the existing state
|
|
||||||
// stores so SSE-driven updates flow through without extra plumbing.
|
|
||||||
// Set `hidden` when the count is zero so the pill doesn't draw
|
|
||||||
// attention to an empty room.
|
|
||||||
|
|
||||||
function setTabCount(tab, n) {
|
|
||||||
const el_ = $("tab-count-" + tab);
|
|
||||||
if (!el_) return;
|
|
||||||
el_.textContent = String(n);
|
|
||||||
el_.hidden = n <= 0;
|
|
||||||
}
|
|
||||||
/** Recompute every tab's count from the current state. Called on
|
|
||||||
* every renderXxx that's tab-relevant. */
|
|
||||||
function refreshTabCounts() {
|
|
||||||
// 4G3NTS — flag any container that's stale (needs_update). Empty
|
|
||||||
// when everyone's current. Container-row pulse signals state
|
|
||||||
// transitions; the pill catches "deploy-pending" specifically.
|
|
||||||
let stale = 0;
|
|
||||||
for (const c of containersState.values()) {
|
|
||||||
if (c.needs_update) stale++;
|
|
||||||
}
|
|
||||||
setTabCount("agents", stale);
|
|
||||||
// R3BU1LD QU3U3 — rebuilds in flight.
|
|
||||||
setTabCount("rebuild", rebuildCount());
|
|
||||||
// K3PT ST4T3 — entries of kept state.
|
|
||||||
setTabCount("kept", keptCount());
|
|
||||||
}
|
|
||||||
// Poll the state stores on a 1s tick to keep the pill counts in
|
|
||||||
// sync. The state stores are mutated synchronously by every SSE
|
|
||||||
// event + refreshState call, so polling them is correct and cheap
|
|
||||||
// — no per-renderer hookup needed.
|
|
||||||
refreshTabCounts();
|
|
||||||
setInterval(refreshTabCounts, 1000);
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
// duplication, this is markup only.
|
// duplication, this is markup only.
|
||||||
//
|
//
|
||||||
// Light-DOM (no shadow root): the existing .hive-tabbar/.hive-tab/
|
// Light-DOM (no shadow root): the existing .hive-tabbar/.hive-tab/
|
||||||
// .tab-count CSS (tabs.css + dashboard.css) already targets plain classes
|
// CSS (tabs.css) already targets plain classes
|
||||||
// on the tabbar/tab elements directly, and tab *panes* stay page-owned
|
// on the tabbar/tab elements directly, and tab *panes* stay page-owned
|
||||||
// content this element never touches — no slotting/scoping need the way
|
// content this element never touches — no slotting/scoping need the way
|
||||||
// <hive-menu>/<hive-side-panel> have.
|
// <hive-menu>/<hive-side-panel> have.
|
||||||
|
|
@ -20,9 +20,7 @@
|
||||||
// aria-controls="${prefix}-pane-${id}" data-tab="${id}">${label}</a>
|
// aria-controls="${prefix}-pane-${id}" data-tab="${id}">${label}</a>
|
||||||
// — same id/aria-controls scheme the hand-written markup used, so each
|
// — same id/aria-controls scheme the hand-written markup used, so each
|
||||||
// page's panes (aria-labelledby="${prefix}-tab-${id}") don't need to
|
// page's panes (aria-labelledby="${prefix}-tab-${id}") don't need to
|
||||||
// change. A `badgeId` tab also gets a nested `<span class="tab-count"
|
// change. It then wires up createTabStrip() and returns exactly what it
|
||||||
// id="${badgeId}" hidden>` (e.g. a rebuild-queue count
|
|
||||||
// pill), then wires up createTabStrip() and returns exactly what it
|
|
||||||
// returns ({ show, active }) — every existing `.active()`/`.show(id)`
|
// returns ({ show, active }) — every existing `.active()`/`.show(id)`
|
||||||
// call site keeps working unchanged. Call configure() exactly once per
|
// call site keeps working unchanged. Call configure() exactly once per
|
||||||
// element (throws if called twice), mirroring the old call sites.
|
// element (throws if called twice), mirroring the old call sites.
|
||||||
|
|
@ -54,11 +52,6 @@ class HiveTabStrip extends HTMLElement {
|
||||||
},
|
},
|
||||||
tab.label,
|
tab.label,
|
||||||
);
|
);
|
||||||
if (tab.badgeId) {
|
|
||||||
a.append(
|
|
||||||
el("span", { class: "tab-count", id: tab.badgeId, hidden: "" }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.append(a);
|
this.append(a);
|
||||||
}
|
}
|
||||||
this._api = createTabStrip(this, { defaultId, onShow });
|
this._api = createTabStrip(this, { defaultId, onShow });
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue