diff --git a/frontend/packages/shared/src/jobq-graph/hive-jobq-graph.js b/frontend/packages/shared/src/jobq-graph/hive-jobq-graph.js
index 46fd3dc9..c593587d 100644
--- a/frontend/packages/shared/src/jobq-graph/hive-jobq-graph.js
+++ b/frontend/packages/shared/src/jobq-graph/hive-jobq-graph.js
@@ -6,24 +6,23 @@
// `payload.label` verbatim, and `payload.data` (if present) as a generic
// key/value list — this element never branches on what a label or a data
// key means, matching the "opaque payload" contract the wire type
-// documents. A consumer that wants domain-specific rendering (an agent
-// chip, a build-log link, ...) does its own thing on top; this is the
-// generic floor every jobq gets for free.
+// documents. A consumer wanting domain-specific rendering (an agent chip,
+// a build-log link, ...) does its own thing on top; this is the generic
+// floor every jobq gets for free.
//
-// Usage:
-// Self-fetches on connect. Call `.refresh()` whenever the host knows the
-// graph changed (e.g. on an SSE tick) — the element does not poll or
-// subscribe itself, since the right refresh trigger varies per page.
-// `.render(nodes)` is also public, for a host that already has a fresh
-// `Vec` (e.g. riding its own SSE payload) and wants to skip
-// the redundant fetch.
+// Usage: —
+// self-fetches on connect. `.refresh()` (public) re-fetches + re-renders;
+// `.render(nodes)` (public) renders host-pushed data directly, no fetch.
+// Fetching lives here, not the host page (per the issue this element was
+// built for) — every render dispatches a bubbling/composed `hive-jobq-graph-update`
+// event (`detail: { nodes }`) so a host needing the raw list for
+// something the tree doesn't show (a count badge, a live-log panel)
+// listens instead of running its own parallel fetch.
//
-// Shadow DOM + own styles (not light-DOM like ): unlike a
-// tabbar, this renders a whole subtree of markup nothing else on the page
-// needs to select into, so scoping is a win here rather than a cost.
-// Theme custom properties (--fg, --red, ...) still apply — they pierce
-// the shadow boundary by inheritance, only plain class rules need to be
-// local, which is exactly what the own stylesheet is for.
+// Shadow DOM + own styles, per instruction — unlike light-DOM
+// , this renders a whole subtree nothing else needs to
+// select into. Theme custom properties (--fg, --red, ...) still pierce
+// the shadow boundary by inheritance; only plain class rules are local.
import { el } from '../dom.js';
import { attachShadowCss } from '../shadow-css.js';
@@ -130,10 +129,15 @@ class HiveJobqGraph extends HTMLElement {
this._body.replaceChildren();
if (!nodes || !nodes.length) {
this._body.append(el('p', { class: 'jg-empty' }, 'empty'));
- return;
+ } else {
+ const roots = buildTree(nodes);
+ for (const root of roots) this._body.append(renderNode(root));
}
- const roots = buildTree(nodes);
- for (const root of roots) this._body.append(renderNode(root));
+ this.dispatchEvent(new CustomEvent('hive-jobq-graph-update', {
+ detail: { nodes: nodes || [] },
+ bubbles: true,
+ composed: true,
+ }));
}
}
customElements.define('hive-jobq-graph', HiveJobqGraph);