diff --git a/docs/web-ui/dashboard.md b/docs/web-ui/dashboard.md index 5aad6263..683f3528 100644 --- a/docs/web-ui/dashboard.md +++ b/docs/web-ui/dashboard.md @@ -986,21 +986,17 @@ agent is stale. Banner pulses on each broker SSE event **Build-queue summary banner** — when the job queue has any active work, a compact amber banner sits above the container list: `◐ build queue — N running · M queued — view queue →` (the link goes to the -BU1LDS page's R3BU1LD QU3U3). The shared `JobqRollup` Preact component -(`@hive/shared/jobq-rollup.js` — the same one swarm-ui's `/jobs` page -mounts, pointed at swarm-controller's own rollup endpoint instead), -mounted once into `#jobq-rollup-section` by `swarm.js::initJobqRollup` -and refreshed via its own handle rather than being re-rendered by -`renderContainers`. Reads `GET /api/jobq/rollup` -(`hive-jobq-wire::state_rollup`) — `Vec<{ state, nodes, roots }>`, -every lifecycle state present in a fixed order, zero counts included — -rather than the full `/api/jobq/graph` tree: `running` sums the -`Running` and `Finishing` entries' `roots` (`Finishing` = own work -done, subtree still going, still in flight), `queued` reads the -`Pending` entry's `roots`. `roots` specifically, not `nodes` — the -banner means *N whole operations*, not raw steps (one rebuild is ~7 -nodes but 1 root); `nodes` exists on the same endpoint for a consumer -that wants step-level counts instead, unused here. +BU1LDS page's R3BU1LD QU3U3). Reads `GET /api/jobq/rollup` +(`hive-jobq-wire::state_rollup`, `jobqRollupState` in `swarm.js`) — +`Vec<{ state, nodes, roots }>`, every lifecycle state present in a +fixed order, zero counts included — rather than the full +`/api/jobq/graph` tree: `running` sums the `Running` and `Finishing` +entries' `roots` (`Finishing` = own work done, subtree still going, +still in flight), `queued` reads the `Pending` entry's `roots`. +`roots` specifically, not `nodes` — the banner means *N whole +operations*, not raw steps (one rebuild is ~7 nodes but 1 root); +`nodes` exists on the same endpoint for a consumer that wants +step-level counts instead, unused here. ### Themed dialogs diff --git a/frontend/packages/dashboard/src/builds.js b/frontend/packages/dashboard/src/builds.js index 7e8f9358..16b37662 100644 --- a/frontend/packages/dashboard/src/builds.js +++ b/frontend/packages/dashboard/src/builds.js @@ -14,10 +14,9 @@ import { $, fmtAgeSecs, openStream, openBuildLogStream, initServerWarnings } fro import { el } from '@hive/shared/dom.js'; import { bindAsyncForms } from '@hive/shared/forms.js'; import { themedConfirm } from '@hive/shared/modal.js'; -import { h, render } from 'preact'; import { fmtAgo, fmtDuration, truncate } from './util.js'; import '@hive/shared/hive-tab-strip.js'; -import { JobqGraph } from '@hive/shared/jobq-graph.js'; +import { mountJobqGraph } from '@hive/shared/jobq-graph.js'; // ─── derived state ─────────────────────────────────────────────────────────── let metaInputsState = []; @@ -27,7 +26,7 @@ let metaUpdateRunning = false; // the tree itself; this flat array exists only for the two things it // doesn't render: the count-pill and the live-log panel. let jobqNodes = []; -let jobqGraphToken = 0; +let jobqGraphHandle = null; function syncFromSnapshot(s) { metaInputsState = (s.meta_inputs || []).slice(); @@ -128,40 +127,27 @@ function renderMetaInputs(s) { } // ─── rebuild queue ──────────────────────────────────────────────────────────── -// R3BU1LD QU3U3 is `JobqGraph` (Preact, rendered via plain `render(h(...))` -// — no JSX pragma needed for either call, no mount wrapper: `render` is -// already the re-render/diff entry point, per mara on review) directly — -// no hand-rolled tree/roll-up rendering here anymore. The component owns -// fetching GET /api/jobq/graph and its own refetch (bump `jobqGraphToken` -// and render again); this page just reads its `onUpdate` callback to keep -// `jobqNodes` (the flat array) in sync for the two things the generic -// view doesn't render itself: the count-pill and the live-log panel below. +// R3BU1LD QU3U3 is `JobqGraph` (Preact, mounted imperatively — see +// mountJobqGraph's own doc comment in @hive/shared/jobq-graph.js for why +// this file has no JSX pipeline) directly — no hand-rolled tree/roll-up +// rendering here anymore. The component owns fetching GET /api/jobq/graph +// and its own refetch (`.refresh()` on the mount handle); this page just +// reads its `onUpdate` callback to keep `jobqNodes` (the flat array) in +// sync for the two things the generic view doesn't render itself: the +// count-pill and the live-log panel below. // // Cancel is the one action this page *does* wire up: the `cancellable` // prop turns on the component's own per-node cancel button, which calls // `onCancel(id)` rather than posting anything — the endpoint // (`/api/rebuild-queue/{id}/cancel`) is this page's domain concept, not // the generic component's. -// Split from `renderRebuildQueue` below: `replaceChildren()` only -// belongs on the *first* render, to clear the static "loading…" -// placeholder `#rebuild-queue-section` starts with in `builds.html` — -// Preact doesn't know that placeholder is there, so a later render -// call must NOT repeat `replaceChildren()` (it would wipe Preact's own -// tracked children out from under its diffing rather than let it -// update them minimally). function mountRebuildQueue() { const root = $('rebuild-queue-section'); if (!root) return; root.replaceChildren(); - renderRebuildQueue(); -} -function renderRebuildQueue() { - const root = $('rebuild-queue-section'); - if (!root) return; - render(h(JobqGraph, { + jobqGraphHandle = mountJobqGraph(root, { endpoint: '/api/jobq/graph', cancellable: true, - refreshToken: jobqGraphToken, onUpdate: (nodes) => { jobqNodes = nodes || []; renderRebuildLiveLog(); @@ -179,12 +165,12 @@ function renderRebuildQueue() { if (!r.ok) throw new Error('http ' + r.status); // No manual refresh: cancel flips node state, which fires // rebuild_queue_changed over SSE — the existing handler below - // already bumps jobqGraphToken and re-renders on that tick. + // already calls jobqGraphHandle.refresh() on that tick. } catch (err) { console.error('cancel failed', err); } }, - }), root); + }); } // ─── running-rebuild live log ───────────────────────────────────────────────── @@ -485,8 +471,7 @@ const SSE_HANDLERS = { // queue snapshot on the wire (tabs.js/SW4RM still consumes it for the // swarm badges, untouched by this), this page just treats it as a // refetch trigger. `JobqGraph` owns the actual fetch. - jobqGraphToken += 1; - renderRebuildQueue(); + if (jobqGraphHandle) jobqGraphHandle.refresh(); // Auto-refresh build log list when the queue changes and BUILD L0GS is active. if (buildTabs && buildTabs.active() === 'buildlogs') { if (buildRefreshTimer) clearTimeout(buildRefreshTimer); diff --git a/frontend/packages/dashboard/src/dashboard.css b/frontend/packages/dashboard/src/dashboard.css index 44899b9e..251652fe 100644 --- a/frontend/packages/dashboard/src/dashboard.css +++ b/frontend/packages/dashboard/src/dashboard.css @@ -7,7 +7,6 @@ referenced from dashboard JS (the schedules view uses `.rqe-source*`), so the dashboard pulls them in here. */ @import "./system-sections.css"; -@import "@hive/shared/jobq-rollup.css"; /* ─── tabbed dashboard chrome ────────────────────────────────────── Top-of-page sticky header with banner + tab strip. SSE stays @@ -457,9 +456,30 @@ hive-agent-menu { Notification controls below sit between the banner and the containers. */ -/* Build-queue summary banner on the SW4RM tab moved to the shared - `JobqRollup` Preact component (`.jqr-*` classes, imported above) — - mounted by swarm.js::initJobqRollup into #jobq-rollup-section. */ +/* Build-queue summary banner on the SW4RM tab: one compact line + when the rebuild queue has active work, with a link to the full queue on + the C0R3 page. Amber to match the in-progress / "rebuilding" card tint. */ +.queue-summary { + display: flex; + align-items: center; + gap: 0.4em; + flex-wrap: wrap; + background: color-mix(in srgb, var(--amber) 8%, transparent); + border: 1px solid color-mix(in srgb, var(--amber) 55%, transparent); + color: var(--amber); + padding: 0.45em 0.8em; + margin-bottom: 0.6em; + border-radius: 4px; +} +.queue-summary strong { color: var(--amber); } +.queue-summary-link { + margin-left: auto; + color: var(--amber); + text-decoration: none; + font-weight: bold; + white-space: nowrap; +} +.queue-summary-link:hover { text-decoration: underline; } /* .notif-row / .btn-notif moved to settings.css with the S3TT1NGS page. */ diff --git a/frontend/packages/dashboard/src/dashboard.html b/frontend/packages/dashboard/src/dashboard.html index b1baf418..d9bd8b53 100644 --- a/frontend/packages/dashboard/src/dashboard.html +++ b/frontend/packages/dashboard/src/dashboard.html @@ -97,13 +97,6 @@ - -

loading…

diff --git a/frontend/packages/dashboard/src/swarm.js b/frontend/packages/dashboard/src/swarm.js index 6c4d2e3a..b2966320 100644 --- a/frontend/packages/dashboard/src/swarm.js +++ b/frontend/packages/dashboard/src/swarm.js @@ -9,8 +9,6 @@ import { } from './common.js'; import { el } from '@hive/shared/dom.js'; import { themedConfirm, themedToast } from '@hive/shared/modal.js'; -import { h, render } from 'preact'; -import { JobqRollup } from '@hive/shared/jobq-rollup.js'; import { containersState, questionsState, } from './state.js'; @@ -64,39 +62,35 @@ const selectionState = new Set(); // a card's pending badges are transients-only now, which already means // "what is running") and this banner, which was pulled entirely per "dont // replace one legacy thing with another" (a client-side tally over the -// generic graph was itself judged a stopgap). Rendering itself later -// moved out to the shared `JobqRollup` Preact component (same one -// swarm-ui's /jobs page mounts), which owns its own fetch of -// GET /api/jobq/rollup — this file just calls Preact's own -// `render(h(...))` directly (no `mountX()` wrapper: `render` is -// already the re-render/diff entry point, per mara on review) and -// bumps a refresh token to force a refetch. -// -// Rendered into #jobq-rollup-section, a sibling of #containers-section -// kept OUTSIDE that section's per-render `replaceChildren()` wipe (see -// dashboard.html's comment on the mount div) — rendering into a -// section that gets wiped on every container-state tick would defeat -// the component owning its own fetch lifecycle. -let jobqRollupToken = 0; +// generic graph was itself judged a stopgap). Now that the dedicated +// rollup endpoint exists (hive-jobq-wire::state_rollup, served at +// GET /api/jobq/rollup), the banner reads *that* instead — a handful of +// pre-tallied counts, not the graph. +let jobqRollupState = []; -export function initJobqRollup() { - const root = $('jobq-rollup-section'); - if (!root) return; - render(h(JobqRollup, { - endpoint: '/api/jobq/rollup', - queueHref: '/builds.html', - refreshToken: jobqRollupToken, - }), root); -} -// Refetches on cold load (tabs.js's refreshState) and whenever -// `rebuild_queue_changed` fires — a payload-less push trigger by +// Fetches the rollup fresh and re-renders. Called on cold load (see +// tabs.js's refreshState) and whenever `rebuild_queue_changed` fires +// (applyRebuildQueueChanged below) — a payload-less push trigger by // design, confirmed with atlas on the jobq-deletion tracker: the event // carries no `queue` field this page reads, same "something changed, -// go refetch" treatment builds.js already gives its own JobqGraph mount +// go refetch" treatment builds.js already gives its JobqGraph mount // handle's .refresh(). +// Best-effort: a failed fetch leaves the previous snapshot in place +// rather than wiping the banner on a network blip. +export async function refreshJobqRollup() { + let counts; + try { + const r = await fetch('/api/jobq/rollup'); + if (!r.ok) return; + counts = await r.json(); + } catch { + return; + } + jobqRollupState = counts; + renderContainersFromState(); +} export function applyRebuildQueueChanged() { - jobqRollupToken += 1; - initJobqRollup(); + refreshJobqRollup(); } // ─── transients ───────────────────────────────────────────────────────────── @@ -633,9 +627,31 @@ export function renderContainers(s) { )); } - // Queue-summary banner lives outside this section now — see - // #jobq-rollup-section / initJobqRollup, mounted once rather than - // rebuilt on every render this function does. + // Queue-summary banner: one compact line above the container list when + // the job queue has active work, linking to the full queue on the + // BU1LDS page. Reads GET /api/jobq/rollup's pre-tallied `roots` counts + // (see jobqRollupState above) rather than the full graph — `roots` + // because "N running / M queued" has always meant *operations*, not + // raw steps (one rebuild is ~7 nodes but 1 root); `nodes` exists on + // the same endpoint for a consumer that wants steps instead, unused + // here. `Finishing` counts as running (own work done, subtree still + // going, still in flight) — same treatment `roots` gets nowhere else, + // since the rollup endpoint doesn't collapse the two itself. + const byState = new Map(jobqRollupState.map((c) => [c.state, c])); + const running = (byState.get('Running')?.roots ?? 0) + + (byState.get('Finishing')?.roots ?? 0); + const queued = byState.get('Pending')?.roots ?? 0; + if (running || queued) { + const parts = []; + if (running) parts.push(`${running} running`); + if (queued) parts.push(`${queued} queued`); + root.append(el('div', { class: 'queue-summary' }, + el('span', { class: 'glyph spinner' }, '◐'), ' ', + el('strong', {}, 'build queue'), ' — ', + parts.join(' · '), ' ', + el('a', { class: 'queue-summary-link', href: '/builds.html' }, 'view queue →'), + )); + } if (!containers.length && !transientsState.size) { root.append(el('p', { class: 'empty' }, 'no managed containers')); diff --git a/frontend/packages/dashboard/src/tabs.js b/frontend/packages/dashboard/src/tabs.js index 46d7502c..9e0fe748 100644 --- a/frontend/packages/dashboard/src/tabs.js +++ b/frontend/packages/dashboard/src/tabs.js @@ -42,7 +42,7 @@ import { renderQuestions, activeQuestionCount, } from './call.js'; import { - initJobqRollup, syncTransientsFromSnapshot, + refreshJobqRollup, syncTransientsFromSnapshot, applyRebuildQueueChanged, applyContainerStateChanged, applyContainerRemoved, applyTransientSet, applyTransientCleared, renderContainers, renderContainersFromState, @@ -232,12 +232,13 @@ window.marked = marked; syncTransientsFromSnapshot(s); syncContainersFromSnapshot(s); // Job-queue rollup feeds only the SW4RM queue-summary banner - // (per-agent card badges are transient-only — see swarm.js). Its - // own `JobqRollup` mount self-fetches GET /api/jobq/rollup — not - // read off `s` (this page's snapshot carries no jobq field). - // initJobqRollup mounts once and is a no-op on later calls; the - // mount's own effect handles the actual fetch. - initJobqRollup(); + // (per-agent card badges are transient-only — see swarm.js). + // Self-fetches GET /api/jobq/rollup — not read off `s` (this + // page's snapshot carries no jobq field) — fire-and-forget: + // renderContainers below runs off whatever jobqRollupState + // already holds, and refreshJobqRollup's own re-render catches + // up once the fetch resolves. + refreshJobqRollup(); renderContainers(s); // Sync the derived approvals + questions stores from the // snapshot, then render. Live `*_added` / `*_resolved` events diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index ba195859..05d490e9 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -29,9 +29,7 @@ "./hive-warn.js": "./src/hive-warn/hive-warn.js", "./side-panel.js": "./src/side-panel/hive-side-panel.js", "./jobq-graph.js": "./src/jobq-graph/JobqGraph.tsx", - "./jobq-graph.css": "./src/jobq-graph/jobq-graph.css", - "./jobq-rollup.js": "./src/jobq-rollup/JobqRollup.tsx", - "./jobq-rollup.css": "./src/jobq-rollup/jobq-rollup.css" + "./jobq-graph.css": "./src/jobq-graph/jobq-graph.css" }, "files": [ "src/" diff --git a/frontend/packages/shared/src/jobq-graph/JobqGraph.tsx b/frontend/packages/shared/src/jobq-graph/JobqGraph.tsx index 8c335ba4..3c549f83 100644 --- a/frontend/packages/shared/src/jobq-graph/JobqGraph.tsx +++ b/frontend/packages/shared/src/jobq-graph/JobqGraph.tsx @@ -17,10 +17,11 @@ // needing the raw list (a count badge, a live-log panel) without its // own parallel fetch. // -// JSX (swarm-ui) — ``. Or plain `render(h(JobqGraph, -// props), container)` (dashboard/src/builds.js, no JSX pragma needed) -// — call again with a bumped `refreshToken` to refetch. +// Two ways to use this: JSX (swarm-ui, or any dashboard page that +// renders it directly) — ``. Or imperative mount +// (dashboard/src/builds.js, plain `.js` — mounting needs no JSX pragma) +// — `mountJobqGraph(container, props)` returns `{ refresh(), update(props) }`. // // Styles live in `@hive/shared/jobq-graph.css`, `@import`ed from a // page/component CSS file rather than imported here — dashboard bundles @@ -28,6 +29,7 @@ // loader is `text` (for unrelated shadow-DOM components' CSS-as-string // needs), and that loader is global per call, not per-module. +import { h, render } from 'preact'; import { useState, useEffect, useCallback } from 'preact/hooks'; // Mirrors `hive_jobq_wire::StateSchema` verbatim (variant names, no @@ -231,8 +233,8 @@ export interface JobqGraphProps { } // `refreshToken` is not read anywhere in the body — its only job is to -// change identity so the effect below re-runs, giving a host an -// explicit "refetch now" lever (bump it and re-render) without an +// change identity so the effect below re-runs, giving a host (or +// `mountJobqGraph`) an explicit "refetch now" lever without an // imperative ref into this component. export function JobqGraph({ endpoint, cancellable = false, onUpdate, onCancel, refreshToken = 0 }: JobqGraphProps) { const [selectedStates, setSelectedStates] = useState>( @@ -292,3 +294,19 @@ export function JobqGraph({ endpoint, cancellable = false, onUpdate, onCancel, r ); } + +// Imperative mount helper for a call site that isn't itself a JSX file +// (dashboard/src/builds.js — plain `.js`, no per-file JSX pragma to +// write `` inline). Returns a handle: `.refresh()` +// (re-fetch with the current props) and `.update(props)` (merge new +// props — e.g. a different `endpoint` — and re-render). +export function mountJobqGraph(container: Element, initialProps: JobqGraphProps) { + let props = initialProps; + let token = 0; + const draw = () => render(h(JobqGraph, { ...props, refreshToken: token }), container); + draw(); + return { + refresh() { token += 1; draw(); }, + update(next: Partial) { props = { ...props, ...next }; draw(); }, + }; +} diff --git a/frontend/packages/shared/src/jobq-rollup/JobqRollup.tsx b/frontend/packages/shared/src/jobq-rollup/JobqRollup.tsx deleted file mode 100644 index 85c6dea6..00000000 --- a/frontend/packages/shared/src/jobq-rollup/JobqRollup.tsx +++ /dev/null @@ -1,90 +0,0 @@ -// JobqRollup.tsx — , a compact one-line banner over -// GET .../api/jobq/rollup's pre-tallied `Vec` -// (any endpoint serving that shape works — hive-c0re and swarm-controller -// both do). Extracted out of dashboard/src/swarm.js's hand-rolled -// queue-summary banner, which this component now replaces there too — -// same "one shared component, not two parallel implementations" pattern -// `JobqGraph` already established for the rebuild-queue tree view. -// -// Reads `roots` (not `nodes`) — "N running / M queued" has always meant -// *operations*, not raw steps (one rebuild is ~7 nodes but 1 root). -// `Finishing` counts as running (own work done, subtree still going, -// still in flight) — the rollup endpoint doesn't collapse the two -// itself, so this component does. Renders nothing at all when there's -// no active work, matching the banner it replaces (an idle queue is not -// worth a line of chrome). -// -// `queueHref`, when given, adds a "view queue →" link (dashboard links to -// /builds.html; swarm-ui's /jobs page omits it — a link to the page -// you're already on is noise). -// -// The glyph carries `.spinner` (`@hive/shared/base.css`, imported by -// both consumers) for the "actively happening" spin — a static -// screenshot can't tell a frozen spinner from a missing one. -// -// JSX (swarm-ui) — ``. Or -// plain `render(h(JobqRollup, props), container)` (dashboard/src/ -// swarm.js, no JSX pragma needed) — call again with a bumped -// `refreshToken` to force a refetch. No mount wrapper: `render` is -// already the re-render/diff entry point. - -import { useState, useEffect } from 'preact/hooks'; - -// Mirrors `hive_jobq_wire::StateSchema` — only the subset this banner -// cares about, not the full union `JobqGraph.tsx` mirrors, since a -// rollup row's `state` is read by exact string match, not rendered. -type NodeState = 'Pending' | 'Running' | 'Finishing' | 'Done' | 'Failed' | 'Cancelled' | 'Skipped'; - -interface StateCount { - state: NodeState; - nodes: number; - roots: number; -} - -export interface JobqRollupProps { - endpoint?: string; - queueHref?: string; - refreshToken?: number; -} - -export function JobqRollup({ endpoint, queueHref, refreshToken = 0 }: JobqRollupProps) { - const [counts, setCounts] = useState([]); - - useEffect(() => { - if (!endpoint) return undefined; - let cancelled = false; - // Best-effort: a failed fetch leaves the previous snapshot in - // place rather than wiping the banner on a network blip — same - // rule the hand-rolled version followed. - (async () => { - try { - const r = await fetch(endpoint); - if (!r.ok) return; - const data = (await r.json()) as StateCount[]; - if (!cancelled) setCounts(data); - } catch { - // ignore — keep the previous snapshot - } - })(); - return () => { cancelled = true; }; - }, [endpoint, refreshToken]); - - const byState = new Map(counts.map((c) => [c.state, c])); - const running = (byState.get('Running')?.roots ?? 0) + (byState.get('Finishing')?.roots ?? 0); - const queued = byState.get('Pending')?.roots ?? 0; - if (!running && !queued) return null; - - const parts: string[] = []; - if (running) parts.push(`${running} running`); - if (queued) parts.push(`${queued} queued`); - - return ( -
- {' '} - build queue — {parts.join(' · ')}{' '} - {queueHref && ( - view queue → - )} -
- ); -} diff --git a/frontend/packages/shared/src/jobq-rollup/jobq-rollup.css b/frontend/packages/shared/src/jobq-rollup/jobq-rollup.css deleted file mode 100644 index 530c8f5a..00000000 --- a/frontend/packages/shared/src/jobq-rollup/jobq-rollup.css +++ /dev/null @@ -1,25 +0,0 @@ -/* JobqRollup.tsx styles — light DOM, `.jqr-*` prefixed to avoid - colliding with a host page's own classes (same convention - jobq-graph.css uses with `.jg-*`). Amber to match the in-progress / - "rebuilding" card tint dashboard.css already uses elsewhere. */ -.jqr-summary { - display: flex; - align-items: center; - gap: 0.4em; - flex-wrap: wrap; - background: color-mix(in srgb, var(--amber) 8%, transparent); - border: 1px solid color-mix(in srgb, var(--amber) 55%, transparent); - color: var(--amber); - padding: 0.45em 0.8em; - margin-bottom: 0.6em; - border-radius: 4px; -} -.jqr-summary strong { color: var(--amber); } -.jqr-link { - margin-left: auto; - color: var(--amber); - text-decoration: none; - font-weight: bold; - white-space: nowrap; -} -.jqr-link:hover { text-decoration: underline; } diff --git a/frontend/packages/swarm-ui/src/pages/JobsPage.css b/frontend/packages/swarm-ui/src/pages/JobsPage.css index 579a8824..beb79c0a 100644 --- a/frontend/packages/swarm-ui/src/pages/JobsPage.css +++ b/frontend/packages/swarm-ui/src/pages/JobsPage.css @@ -1,9 +1,7 @@ -/* JobsPage — wraps the shared JobqGraph + JobqRollup components pointed - at the swarm-controller's own /api/jobq/{graph,rollup} endpoints (same - wire shapes hive-c0re's dashboard consumes, see hive-jobq-wire's - README). Styles are @hive/shared's own CSS files, @import'ed here - rather than from the component files themselves — see JobqGraph.tsx's - own comment for why that split exists (esbuild's loader map is global - per bundle call). */ +/* JobsPage — wraps the shared JobqGraph component pointed at the + swarm-controller's own /api/jobq/graph endpoint (same wire shape + hive-c0re's dashboard consumes, see hive-jobq-wire's README). Styles + are @hive/shared's jobq-graph.css, @import'ed here rather than from + the component file itself — see JobqGraph.tsx's own comment for why + that split exists (esbuild's loader map is global per bundle call). */ @import "@hive/shared/jobq-graph.css"; -@import "@hive/shared/jobq-rollup.css"; diff --git a/frontend/packages/swarm-ui/src/pages/JobsPage.tsx b/frontend/packages/swarm-ui/src/pages/JobsPage.tsx index 3e95b00b..0645a566 100644 --- a/frontend/packages/swarm-ui/src/pages/JobsPage.tsx +++ b/frontend/packages/swarm-ui/src/pages/JobsPage.tsx @@ -7,19 +7,13 @@ // SwarmNodeKind/SwarmResourceKind), so this renders an empty tree today // — the page exists so the wiring is in place before the first real // swarm-level job (e.g. CreateAgent) lands. -// -// JobqRollup sits above the graph, same "N running / M queued" banner -// the dashboard's SW4RM tab shows — no `queueHref`, since a "view -// queue →" link back to this same page would be noise. import { JobqGraph } from '@hive/shared/jobq-graph.js'; -import { JobqRollup } from '@hive/shared/jobq-rollup.js'; import { Panel } from '../ui/panel/Panel.js'; import './JobsPage.css'; export function JobsPage() { return ( - );