feat(dashboard): serve H0M3 at /, relocate dashboard to /dashboard.html (#1464)
Step 2 of the nav restructure: make the H0M3 menu hub the landing page at / and move the dashboard SPA to /dashboard.html, with every surface linking back to the hub. Mechanism (frontend-only, no host-side change — confirmed against the ServeDir route table): the dashboard's ServeDir fallback serves index.html at / via append_index_html_on_directories, and there is no SPA path-routing catch-all to break. The dashboard SPA is served as the plain file dashboard.html so it never shadows the exact-match /dashboard/stream + /dashboard/history SSE routes registered before the fallback. - Swap the HTML entry files: the H0M3 page becomes index.html (loads home.js → served at /), and the dashboard SPA becomes dashboard.html (loads tabs.js → served at /dashboard.html). build.mjs copies the new set; JS bundle names are unchanged (referenced by absolute /static/ paths, independent of the HTML filename). - H0M3 Dashboard tile now points at /dashboard.html. - The dashboard gains a "← home" back-link in its chrome; flow.html and logs.html relabel their back-link from "← dashboard" to "← home" (href stays / — which is the hub now). Pages link to the hub, not to each other. - Agent page (app.js + stats.js): the "↑ dashboard" link now targets /dashboard.html. The API base (rebuild / answer-question / mark-all-read POSTs) stays the origin root, unchanged. - Comment-only: tabs.js / flow.js / common.js references to the dashboard's old index.html filename updated to dashboard.html. Note for review: git renders the file swap as a deleted home.html + an added dashboard.html + a heavily-modified index.html, because index.html exists on both sides with swapped content. It's a content swap, not a rewrite — the built dist/ is verified (index.html→home.js, dashboard.html →tabs.js). Deferred to a follow-up: removing the FL0W / L0GS / M4TR1X "→" page-links from the dashboard tab strip (touches tabs.js gating/overflow), and the shared reusable chrome component (a later step).
This commit is contained in:
parent
d7e4028a16
commit
8ddab401f9
12 changed files with 479 additions and 449 deletions
|
|
@ -186,10 +186,13 @@ window.marked = marked;
|
||||||
if (!menu) return;
|
if (!menu) return;
|
||||||
menu.replaceChildren();
|
menu.replaceChildren();
|
||||||
|
|
||||||
// ↑ dashboard — host dashboard back-link.
|
// ↑ dashboard — host dashboard back-link. The dashboard SPA lives at
|
||||||
|
// `dashboard.html` (the `/` root now serves the H0M3 menu hub), so the
|
||||||
|
// link targets that file; `dashUrl` itself stays the API origin used
|
||||||
|
// for the rebuild / answer-question / mark-all-read POSTs below.
|
||||||
menu.append(el('a', {
|
menu.append(el('a', {
|
||||||
class: 'overflow-item overflow-item-dashboard',
|
class: 'overflow-item overflow-item-dashboard',
|
||||||
href: dashUrl,
|
href: dashUrl + 'dashboard.html',
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
rel: 'noopener',
|
rel: 'noopener',
|
||||||
role: 'menuitem',
|
role: 'menuitem',
|
||||||
|
|
|
||||||
|
|
@ -403,12 +403,13 @@ window.Chart = Chart;
|
||||||
document.getElementById('title').textContent = '◆ ' + s.label + ' ◆';
|
document.getElementById('title').textContent = '◆ ' + s.label + ' ◆';
|
||||||
const dl = document.getElementById('dashboard-link');
|
const dl = document.getElementById('dashboard-link');
|
||||||
// When accessed via hive-gateway the page lives at `/agent/<name>/`
|
// When accessed via hive-gateway the page lives at `/agent/<name>/`
|
||||||
// on the same origin as the dashboard (`/`). Detect via path prefix
|
// on the same origin as the dashboard. Detect via path prefix
|
||||||
// rather than the direct port (which is unreachable or wrong scheme
|
// rather than the direct port (which is unreachable or wrong scheme
|
||||||
// behind HTTPS TLS termination).
|
// behind HTTPS TLS termination). The dashboard SPA lives at
|
||||||
|
// `dashboard.html` — the `/` root now serves the H0M3 menu hub.
|
||||||
dl.href = window.location.pathname.startsWith('/agent/')
|
dl.href = window.location.pathname.startsWith('/agent/')
|
||||||
? window.location.origin + '/'
|
? window.location.origin + '/dashboard.html'
|
||||||
: 'http://' + window.location.hostname + ':' + s.dashboard_port + '/';
|
: 'http://' + window.location.hostname + ':' + s.dashboard_port + '/dashboard.html';
|
||||||
} catch (_) { /* non-fatal */ }
|
} catch (_) { /* non-fatal */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,38 @@
|
||||||
// esbuild build for @hive/dashboard. Output layout (`dist/`):
|
// esbuild build for @hive/dashboard. Output layout (`dist/`):
|
||||||
//
|
//
|
||||||
// dist/index.html served by the Rust router at GET /
|
// dist/index.html H0M3 menu hub — served by the Rust
|
||||||
|
// router at GET / (the landing page)
|
||||||
|
// dist/dashboard.html the operator dashboard SPA — served
|
||||||
|
// at GET /dashboard.html
|
||||||
// dist/flow.html served at GET /flow.html
|
// dist/flow.html served at GET /flow.html
|
||||||
// dist/logs.html served at GET /logs.html
|
// dist/logs.html served at GET /logs.html
|
||||||
// dist/static/tabs.js /index.html entry — tab renderers +
|
// dist/static/home.js index.html (H0M3) entry — menu tiles +
|
||||||
|
// matrix-tile gating + identity line
|
||||||
|
// dist/static/tabs.js dashboard.html entry — tab renderers +
|
||||||
// tab routing + refreshState
|
// tab routing + refreshState
|
||||||
// dist/static/flow.js /flow.html entry — broker terminal +
|
// dist/static/flow.js /flow.html entry — broker terminal +
|
||||||
// operator inbox + @-mention composer
|
// @-mention composer
|
||||||
// dist/static/logs.js /logs.html entry — build/agent/system
|
// dist/static/logs.js /logs.html entry — build/agent/system
|
||||||
// log viewer sub-tabs
|
// log viewer sub-tabs
|
||||||
// dist/static/{tabs,flow,logs}.js.map source map siblings
|
// dist/static/{home,tabs,flow,logs}.js.map source map siblings
|
||||||
// dist/static/common.css loaded by every page (@hive/shared
|
// dist/static/common.css loaded by every page (@hive/shared
|
||||||
// imports + shared typography/badges/
|
// imports + shared typography/badges/
|
||||||
// buttons/inbox/side-panel)
|
// buttons/inbox/side-panel)
|
||||||
// dist/static/dashboard.css /index.html only (dashboard-specific)
|
// dist/static/dashboard.css dashboard.html only (dashboard-specific)
|
||||||
// dist/static/flow.css /flow.html only (flow chrome + composer)
|
// dist/static/flow.css /flow.html only (flow chrome + composer)
|
||||||
// dist/static/logs.css /logs.html only (log viewer sub-tabs)
|
// dist/static/logs.css /logs.html only (log viewer sub-tabs)
|
||||||
|
// dist/static/home.css index.html (H0M3) only (tile grid)
|
||||||
//
|
//
|
||||||
// All three JS entries bundle `./common.js` (DOM helpers, Panel singleton,
|
// Each JS entry bundles `./common.js` (DOM helpers, Panel singleton,
|
||||||
// NOTIF, path linkification) independently — esbuild inlines the shared
|
// NOTIF, path linkification) independently — esbuild inlines the shared
|
||||||
// module into each bundle rather than emitting a shared chunk (no
|
// module into each bundle rather than emitting a shared chunk (no
|
||||||
// `splitting: true`). The Rust binary mounts `dist/` as a
|
// `splitting: true`). The Rust binary mounts `dist/` as a
|
||||||
// `tower_http::ServeDir` fallback; the layout above keeps every URL
|
// `tower_http::ServeDir` fallback (`append_index_html_on_directories`
|
||||||
// the HTML files reference reachable without rewriting paths in the
|
// serves index.html at /); the layout above keeps every URL the HTML
|
||||||
// HTML.
|
// files reference reachable without rewriting paths in the HTML. The
|
||||||
|
// dashboard SPA lives at /dashboard.html (a plain file, not a /dashboard/
|
||||||
|
// prefix) so it never shadows the exact-match /dashboard/stream +
|
||||||
|
// /dashboard/history SSE routes registered before the ServeDir fallback.
|
||||||
|
|
||||||
import { build } from 'esbuild';
|
import { build } from 'esbuild';
|
||||||
import { mkdirSync, copyFileSync, rmSync } from 'node:fs';
|
import { mkdirSync, copyFileSync, rmSync } from 'node:fs';
|
||||||
|
|
@ -91,7 +100,7 @@ for (const entry of ['theme.css', 'common.css', 'dashboard.css', 'flow.css', 'lo
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const html of ['index.html', 'flow.html', 'logs.html', 'home.html']) {
|
for (const html of ['index.html', 'dashboard.html', 'flow.html', 'logs.html']) {
|
||||||
copyFileSync(src(html), dist(html));
|
copyFileSync(src(html), dist(html));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Shared dashboard helpers used by both index.html (./tabs.js) and
|
// Shared dashboard helpers used by both dashboard.html (./tabs.js) and
|
||||||
// flow.html (./flow.js): pure DOM helpers, the side-panel singleton,
|
// flow.html (./flow.js): pure DOM helpers, the side-panel singleton,
|
||||||
// the OS-notification module, and the path-link / file-preview
|
// the OS-notification module, and the path-link / file-preview
|
||||||
// infrastructure for the side panel.
|
// infrastructure for the side panel.
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,19 @@ body.dashboard-shell {
|
||||||
padding: 0.4em 0 0;
|
padding: 0.4em 0 0;
|
||||||
margin: 0 -1.5em 1em;
|
margin: 0 -1.5em 1em;
|
||||||
}
|
}
|
||||||
|
/* ← home back-link to the H0M3 hub. Small purple link top-left of the
|
||||||
|
chrome, mirroring the .logs-back treatment the sub-pages use so the
|
||||||
|
navigation reads consistently. Full chrome unification (a shared,
|
||||||
|
reusable tab/back-link component) is a later step. */
|
||||||
|
.dash-home-back {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--purple);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.82em;
|
||||||
|
padding: 0.1em 1em 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.dash-home-back:hover { text-decoration: underline; }
|
||||||
.banner-thin {
|
.banner-thin {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
|
||||||
390
frontend/packages/dashboard/src/dashboard.html
Normal file
390
frontend/packages/dashboard/src/dashboard.html
Normal file
|
|
@ -0,0 +1,390 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>hyperhive // h1ve-c0re</title>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
|
<link rel="stylesheet" href="/static/theme.css">
|
||||||
|
<link rel="stylesheet" href="/static/common.css">
|
||||||
|
<link rel="stylesheet" href="/static/dashboard.css">
|
||||||
|
</head>
|
||||||
|
<body class="dashboard-shell">
|
||||||
|
|
||||||
|
<!-- Sticky chrome — just the tab strip. The "WE ARE THE WIRED"
|
||||||
|
slug lives at the page footer below `<main>`; chrome is
|
||||||
|
navigation only. Tabs route via the URL hash so F5 / back-
|
||||||
|
button / shared links keep you on the same view. JS owns
|
||||||
|
the show/hide. -->
|
||||||
|
<header class="dashboard-chrome">
|
||||||
|
<!-- Back to the H0M3 menu hub (served at /). Sub-pages and the
|
||||||
|
dashboard all link back to the landing page rather than to each
|
||||||
|
other — H0M3 is the single navigation hub. -->
|
||||||
|
<a class="dash-home-back" href="/" title="back to the H0M3 menu">← home</a>
|
||||||
|
<!-- Hive identity line: swarm/hive label. Hidden until JS populates
|
||||||
|
it from /api/state (hive_name + swarm_name). Falls back to
|
||||||
|
hostname when only the domain is configured. -->
|
||||||
|
<p class="banner-thin" id="hive-identity" hidden></p>
|
||||||
|
<nav class="tabbar" id="tabbar" role="tablist">
|
||||||
|
<a class="tab" id="tab-swarm" href="#swarm" role="tab"
|
||||||
|
aria-controls="tab-pane-swarm"
|
||||||
|
data-tab="swarm">
|
||||||
|
<span class="tab-label">◆ SW4RM ◆</span>
|
||||||
|
<span class="tab-count" id="tab-count-swarm" hidden></span>
|
||||||
|
</a>
|
||||||
|
<a class="tab" id="tab-call" href="#call" role="tab"
|
||||||
|
aria-controls="tab-pane-call"
|
||||||
|
data-tab="call">
|
||||||
|
<span class="tab-label">◆ Y3R C4LL ◆</span>
|
||||||
|
<span class="tab-count tab-count-attn" id="tab-count-call" hidden></span>
|
||||||
|
</a>
|
||||||
|
<a class="tab" id="tab-system" href="#system" role="tab"
|
||||||
|
aria-controls="tab-pane-system"
|
||||||
|
data-tab="system">
|
||||||
|
<span class="tab-label">◆ SYST3M ◆</span>
|
||||||
|
<span class="tab-count" id="tab-count-system" hidden></span>
|
||||||
|
</a>
|
||||||
|
<!-- P3RM1SS10NS: per-agent capability grants and tool-group
|
||||||
|
assignments. Both tables are fetched on tab activation. -->
|
||||||
|
<a class="tab" id="tab-permissions" href="#permissions" role="tab"
|
||||||
|
aria-controls="tab-pane-permissions"
|
||||||
|
data-tab="permissions">
|
||||||
|
<span class="tab-label">◆ P3RM1SS10NS ◆</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- SCH3DUL3S: scheduled-prompts surface. List of queued
|
||||||
|
schedules + an operator-direct creation form. Count pill
|
||||||
|
mirrors the active (non-cancelled) schedule count; hidden
|
||||||
|
when zero. -->
|
||||||
|
<a class="tab" id="tab-schedules" href="#schedules" role="tab"
|
||||||
|
aria-controls="tab-pane-schedules"
|
||||||
|
data-tab="schedules">
|
||||||
|
<span class="tab-label">◆ SCH3DUL3S ◆</span>
|
||||||
|
<span class="tab-count" id="tab-count-schedules" hidden></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- ST4TS: hive-wide turn-stats rollup (swarm totals, busiest
|
||||||
|
agents, model mix, est cost). Fetched on tab activation +
|
||||||
|
window change from GET /api/stats-hive. -->
|
||||||
|
<a class="tab" id="tab-stats" href="#stats" role="tab"
|
||||||
|
aria-controls="tab-pane-stats"
|
||||||
|
data-tab="stats">
|
||||||
|
<span class="tab-label">◆ ST4TS ◆</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- P33RS: peer hive navigation links. Hidden until at least one
|
||||||
|
peer is configured in `services.hyperhive.peers`. -->
|
||||||
|
<a class="tab" id="tab-peers" href="#peers" role="tab"
|
||||||
|
aria-controls="tab-pane-peers"
|
||||||
|
data-tab="peers" hidden>
|
||||||
|
<span class="tab-label">◆ P33RS ◆</span>
|
||||||
|
<span class="tab-count" id="tab-count-peers" hidden></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- M4TR1X: optional matrix web client (fluffychat-web by
|
||||||
|
default) mounted at /matrix/ by the gateway when
|
||||||
|
`hyperhive.matrix.gui.enable = true`. Same-origin
|
||||||
|
navigation (the static dist is its own SPA). Hidden in
|
||||||
|
JS when `state.matrix_gui_enabled === false` so operators
|
||||||
|
without matrix-gui on don't see a dead link. See
|
||||||
|
docs/web-ui.md::Tab strip for the gating model and
|
||||||
|
docs/gateway.md for the matrix vhost + .well-known
|
||||||
|
auto-discovery. -->
|
||||||
|
<a class="tab tab-link" id="tab-matrix" href="/matrix/" hidden
|
||||||
|
title="open the matrix chat client (fluffychat-web)">
|
||||||
|
<span class="tab-label">◆ M4TR1X ◆ →</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- FL0W is its own page (`/flow.html`), not a tab. The link
|
||||||
|
lives in the tab strip so it reads as a peer surface;
|
||||||
|
clicking navigates rather than swapping panes in place.
|
||||||
|
Count pill mirrors the dashboard's operator-inbox length
|
||||||
|
and is hidden when zero. See docs/web-ui.md::FL0W page. -->
|
||||||
|
<a class="tab tab-link" id="tab-flow" href="/flow.html"
|
||||||
|
title="open the all-agents chat in a dedicated full-page terminal">
|
||||||
|
<span class="tab-label">◆ FL0W ◆ →</span>
|
||||||
|
<span class="tab-count" id="tab-count-flow" hidden></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- L0GS and S3TT1NGS: default-overflow — always live in the ⋮
|
||||||
|
dropdown, never in the main strip. `data-overflow="default"`
|
||||||
|
marks them so JS keeps them out of the bar regardless of
|
||||||
|
available width. -->
|
||||||
|
<a class="tab tab-link" id="tab-logs" href="/logs.html"
|
||||||
|
data-overflow="default"
|
||||||
|
title="open the log viewer">
|
||||||
|
<span class="tab-label">◆ L0GS ◆ →</span>
|
||||||
|
</a>
|
||||||
|
<a class="tab" id="tab-settings" href="#settings" role="tab"
|
||||||
|
aria-controls="tab-pane-settings"
|
||||||
|
data-tab="settings"
|
||||||
|
data-overflow="default">
|
||||||
|
<span class="tab-label">◆ S3TT1NGS ◆</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Overflow ⋮ button + dropdown. JS populates the dropdown with
|
||||||
|
tab clones and keeps the button hidden when the dropdown would
|
||||||
|
be empty. Gets `.tabbar-overflow-active` when the active tab
|
||||||
|
is inside. -->
|
||||||
|
<div class="tabbar-overflow" id="tabbar-overflow">
|
||||||
|
<button type="button" class="tabbar-overflow-btn" id="tabbar-overflow-btn"
|
||||||
|
aria-haspopup="menu" aria-expanded="false"
|
||||||
|
title="more tabs" hidden>⋮</button>
|
||||||
|
<ul class="tabbar-overflow-dropdown" id="tabbar-overflow-dropdown"
|
||||||
|
role="menu" hidden></ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Tab panes. Exactly one is `.tab-pane-active` at a time;
|
||||||
|
JS toggles based on the URL hash + `hashchange` events. -->
|
||||||
|
<main class="dashboard-main">
|
||||||
|
|
||||||
|
<!-- SW4RM: the swarm itself. Container cards (the central thing
|
||||||
|
the operator looks at) and rebuild queue / cascade visualisation
|
||||||
|
that drives them. No inline `C0NTAINERS` h2 heading + divider
|
||||||
|
— the tab label SW4RM already says it. -->
|
||||||
|
<section class="tab-pane" id="tab-pane-swarm"
|
||||||
|
role="tabpanel" aria-labelledby="tab-swarm">
|
||||||
|
<div id="containers-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Y3R C4LL: things blocked on operator decision. Approvals +
|
||||||
|
questions read as the same concept ("something is waiting on
|
||||||
|
you"); both surface their full bodies inline so the operator
|
||||||
|
can decide without leaving the pane. -->
|
||||||
|
<section class="tab-pane" id="tab-pane-call"
|
||||||
|
role="tabpanel" aria-labelledby="tab-call">
|
||||||
|
<!-- 1NB0X: unread agent→operator messages (#1469). Fetched on
|
||||||
|
tab activation + cold load, appended live from the broker
|
||||||
|
stream, cleared via "mark all read". Count folds into the
|
||||||
|
Y3R C4LL pill so messages aren't missed. -->
|
||||||
|
<h2>◆ 1NB0X ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<div id="operator-inbox-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>◆ P3NDING APPR0VALS ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<div id="approvals-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>◆ M1ND H4S QU3STI0NS ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<div id="questions-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- SYST3M: passive / rare-interaction state. Meta inputs (lock
|
||||||
|
bumps), rebuild queue (watch only), kept state from previous
|
||||||
|
tombstoned agents. Per-section content auto-compresses to a
|
||||||
|
one-line summary when empty (JS toggle). -->
|
||||||
|
<section class="tab-pane" id="tab-pane-system"
|
||||||
|
role="tabpanel" aria-labelledby="tab-system">
|
||||||
|
<h2>◆ M3T4 1NPUTS ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">select inputs to <code>nix flake update</code> in <code>/meta/</code>. selected agents rebuild in sequence after the lock bump; manager learns each outcome via the usual <code>rebuilt</code> system event.</p>
|
||||||
|
<div id="meta-inputs-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>◆ R3BU1LD QU3U3 ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">pending + running rebuilds, meta-updates, and first-spawns. one runs at a time; meta-update cascades nest under their parent. dedup: re-enqueueing a still-queued op collapses into the existing entry.</p>
|
||||||
|
<div id="rebuild-queue-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>◆ K3PT ST4T3 ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<div id="tombstones-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- C0NT41N3R L04D: live cpu + memory per agent container, read
|
||||||
|
from cgroup v2 on the host. Polled only while this tab is
|
||||||
|
active (cpu needs a short two-sample read each refresh). -->
|
||||||
|
<h2>◆ C0NT41N3R L04D ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">live cpu + memory per agent container, from cgroup v2 on the host. cpu is % of total host capacity (all cores), sampled over ~200ms each refresh; polled every 5s while this tab is open. network is omitted on purpose — agents share the host netns, so there is no per-container counter (see <code>docs/network.md</code>).</p>
|
||||||
|
<div id="container-load-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- P3RM1SS10NS: per-agent capability grants + tool-group
|
||||||
|
assignments. Both tables are column-driven from the backend
|
||||||
|
(GET /api/capabilities, GET /api/tool-groups) so new entries
|
||||||
|
require no UI change. Saving POSTs to the respective
|
||||||
|
/{agent} endpoint and queues a rebuild. -->
|
||||||
|
<section class="tab-pane" id="tab-pane-permissions"
|
||||||
|
role="tabpanel" aria-labelledby="tab-permissions">
|
||||||
|
<h2>◆ C4P4B1L1T13S ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">per-agent capability grants. capabilities unlock gated MCP tools and system access. saving queues a rebuild for the affected agent.</p>
|
||||||
|
<div id="capabilities-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- T00L GR0UPS: per-agent tool-group permission matrix. Rows = agents,
|
||||||
|
cols = tool groups fetched from GET /api/tool-groups.
|
||||||
|
Checking / unchecking and saving POSTs to
|
||||||
|
/api/tool-groups/{agent}; a rebuild is queued automatically.
|
||||||
|
Absent agents default to the role default (shown in parens). -->
|
||||||
|
<h2>◆ T00L GR0UPS ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">per-agent tool-group permissions. columns are filled from the backend — adding a new group requires no UI change. agents without an explicit entry use the role default (agents: messaging, meta, inbox, execution; manager: all). saving queues a rebuild.</p>
|
||||||
|
<div id="tool-groups-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- P33RS: peer hive link cards. Rendered from state.peer_hives;
|
||||||
|
hidden when the list is empty. -->
|
||||||
|
<section class="tab-pane" id="tab-pane-peers"
|
||||||
|
role="tabpanel" aria-labelledby="tab-peers">
|
||||||
|
<h2>◆ P33R H1V3S ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">peer hives in this swarm. click a card to open that hive's dashboard.</p>
|
||||||
|
<div id="peers-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- SCH3DUL3S: scheduled prompts. Creation + edit are folded
|
||||||
|
into the same table — empty bottom row is the create form
|
||||||
|
(fill cells, click +), inline-edit-row expands on the `✎`
|
||||||
|
toggle for existing schedules. Schedules list driven by
|
||||||
|
GET /api/schedules; POST /api/schedules to create, PATCH
|
||||||
|
/api/schedules/{id} to edit, POST /api/schedules/{id}/cancel
|
||||||
|
for per-target / whole-row cancel. Live updates via
|
||||||
|
`schedules_changed` SSE; tab activation re-fetches as a
|
||||||
|
safety net for disconnect windows.
|
||||||
|
See docs/web-ui.md::SCH3DUL3S tab. -->
|
||||||
|
<section class="tab-pane" id="tab-pane-schedules"
|
||||||
|
role="tabpanel" aria-labelledby="tab-schedules">
|
||||||
|
<h2>◆ SCH3DUL3S ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">all schedules currently in the table. fill the bottom row to queue a new schedule (operator-direct, no approval gate; recurring when an interval is set). cancel a single target with the row button or the whole schedule with <code>✕ cancel all</code>.</p>
|
||||||
|
<div id="schedules-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- QU3U3D R3M1ND3RS: self-scheduled agent reminders. Lives
|
||||||
|
on this tab so the operator has one place for everything
|
||||||
|
that fires at a future time — operator-set schedules
|
||||||
|
above, agent-self reminders here. Backed by GET
|
||||||
|
/api/reminders; live updates via `reminders_changed` SSE;
|
||||||
|
refreshReminders() called from refreshState + tab
|
||||||
|
activation as safety net for disconnect windows. -->
|
||||||
|
<h2>◆ QU3U3D R3M1ND3RS ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">reminders agents have queued for themselves but not yet delivered. cancel to drop a stuck or unwanted entry.</p>
|
||||||
|
<div id="reminders-section">
|
||||||
|
<p class="meta">loading…</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ST4TS: hive-wide turn-stats aggregate. Plain tables/bars (the
|
||||||
|
dashboard bundle has no chart lib); data from GET
|
||||||
|
/api/stats-hive?window=, fetched on tab activation + on
|
||||||
|
window change. Per-agent trend charts stay on each agent's
|
||||||
|
own /stats page. -->
|
||||||
|
<section class="tab-pane" id="tab-pane-stats"
|
||||||
|
role="tabpanel" aria-labelledby="tab-stats">
|
||||||
|
<h2>◆ ST4TS ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">hive-wide turn statistics, aggregated across every agent over the selected window. <strong>cost is a rough estimate</strong> from approximate per-model list prices — it drifts and is a ballpark, not a bill.</p>
|
||||||
|
<div class="hive-stats-windows" id="hive-stats-windows">
|
||||||
|
<button type="button" class="btn" data-w="1h">1h</button>
|
||||||
|
<button type="button" class="btn" data-w="4h">4h</button>
|
||||||
|
<button type="button" class="btn active" data-w="24h">24h</button>
|
||||||
|
<button type="button" class="btn" data-w="3d">3d</button>
|
||||||
|
<button type="button" class="btn" data-w="7d">7d</button>
|
||||||
|
<button type="button" class="btn" data-w="30d">30d</button>
|
||||||
|
</div>
|
||||||
|
<div class="hive-stats-chips" id="hive-stats-summary"></div>
|
||||||
|
<h3>◇ busiest agents</h3>
|
||||||
|
<div id="hive-stats-agents"><p class="meta">loading…</p></div>
|
||||||
|
<h3>◇ model mix (turns across the swarm)</h3>
|
||||||
|
<div id="hive-stats-models"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- FL0W: lives on its own page now (`/flow.html`). The
|
||||||
|
message-flow + inbox + compose DOM only exists there — when
|
||||||
|
tabs.js boots on this page the corresponding renderers
|
||||||
|
no-op silently (each guard is `if (!el) return`). -->
|
||||||
|
|
||||||
|
<!-- S3TT1NGS: operator-local preferences. Today this is just the
|
||||||
|
browser-notification toggle (per-browser localStorage state).
|
||||||
|
The same IDs that used to live in the tab strip migrated
|
||||||
|
here unchanged so `NOTIF.bind()` in common.js wires them
|
||||||
|
without any JS change. -->
|
||||||
|
<section class="tab-pane" id="tab-pane-settings"
|
||||||
|
role="tabpanel" aria-labelledby="tab-settings">
|
||||||
|
<h2>◆ S3TT1NGS ◆</h2>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p class="meta">operator-local preferences. these live in the browser's localStorage — they do not sync between devices and do not survive a profile wipe.</p>
|
||||||
|
|
||||||
|
<h3>◇ browser notifications</h3>
|
||||||
|
<p class="meta">desktop notifications for new approvals, new operator questions, and broker messages addressed to you. requires a secure context (https or localhost). mute silences the notifications without revoking the OS-level permission.</p>
|
||||||
|
<div id="notif-row" class="notif-row">
|
||||||
|
<button type="button" id="notif-enable" class="btn btn-notif" hidden>🔔 enable notifications</button>
|
||||||
|
<button type="button" id="notif-mute" class="btn btn-notif" hidden>🔕 mute</button>
|
||||||
|
<button type="button" id="notif-unmute" class="btn btn-notif" hidden>🔔 unmute</button>
|
||||||
|
<span id="notif-status" class="meta" hidden></span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<pre class="banner banner-thin">░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░</pre>
|
||||||
|
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
||||||
|
<p>▲△▲ <a href="https://forge.darkest.space/hyperhive/hyperhive">hyperhive</a> ▲△▲ hive-c0re on this host ▲△▲</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Slide-in detail panel. Long content (clicked file previews,
|
||||||
|
approval diffs, journald logs, applied config) opens here
|
||||||
|
instead of expanding inline. Singleton — JS swaps the title +
|
||||||
|
body and toggles `.open`. Lives outside the tab panes so it
|
||||||
|
overlays any active tab. -->
|
||||||
|
<div id="side-panel" class="side-panel" aria-hidden="true">
|
||||||
|
<div class="side-panel-backdrop" id="side-panel-backdrop"></div>
|
||||||
|
<aside class="side-panel-drawer" role="dialog" aria-modal="true"
|
||||||
|
aria-labelledby="side-panel-title">
|
||||||
|
<header class="side-panel-head">
|
||||||
|
<span class="side-panel-title" id="side-panel-title"></span>
|
||||||
|
<button type="button" class="side-panel-close" id="side-panel-close"
|
||||||
|
title="close (esc)">✕</button>
|
||||||
|
</header>
|
||||||
|
<div class="side-panel-body" id="side-panel-body"></div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Selection action bar. Sticky-bottom strip that slides into
|
||||||
|
view when one or more agent cards is selected (click the icon
|
||||||
|
to toggle). See docs/web-ui.md::Selection bar for the bulk
|
||||||
|
action gating + clear semantics. -->
|
||||||
|
<div id="selection-bar" class="selection-bar" hidden role="toolbar"
|
||||||
|
aria-label="bulk agent actions">
|
||||||
|
<span class="selection-count" id="selection-count"></span>
|
||||||
|
<span class="selection-names" id="selection-names"></span>
|
||||||
|
<span class="selection-actions" id="selection-actions"></span>
|
||||||
|
<button type="button" class="btn selection-clear" id="selection-clear"
|
||||||
|
title="clear selection (esc)">✕ clear</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Single bundled entry — tabs.js is the dashboard tabs surface;
|
||||||
|
flow.html has its own flow.js bundle. esbuild folds
|
||||||
|
@hive/shared/terminal.js and the marked npm package into
|
||||||
|
tabs.js. -->
|
||||||
|
<script type="module" src="/static/tabs.js" defer></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -11,12 +11,12 @@
|
||||||
</head>
|
</head>
|
||||||
<body class="flow-shell">
|
<body class="flow-shell">
|
||||||
|
|
||||||
<!-- Minimal chrome: just a back link. No full tabbar — the main
|
<!-- Minimal chrome: just a back link to the H0M3 hub (served at /).
|
||||||
dashboard already has the tab strip. The flow page is a
|
No full tabbar — the flow page is a dedicated full-viewport
|
||||||
dedicated full-viewport terminal surface; navigating back
|
terminal surface; navigating back to the menu is the only chrome
|
||||||
is the only chrome needed. -->
|
needed. Pages link back to H0M3, not the dashboard. -->
|
||||||
<header class="logs-header">
|
<header class="logs-header">
|
||||||
<a class="logs-back" href="/">← dashboard</a>
|
<a class="logs-back" href="/">← home</a>
|
||||||
<span class="logs-title">FL0W</span>
|
<span class="logs-title">FL0W</span>
|
||||||
<select id="flow-agent-filter" class="flow-filter" title="filter timeline by agent" aria-label="filter timeline by agent">
|
<select id="flow-agent-filter" class="flow-filter" title="filter timeline by agent" aria-label="filter timeline by agent">
|
||||||
<option value="">all agents</option>
|
<option value="">all agents</option>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
//
|
//
|
||||||
// Does NOT contain the dashboard's tab renderers, mutation-event
|
// Does NOT contain the dashboard's tab renderers, mutation-event
|
||||||
// dispatchers, or refreshState — that's `./tabs.js`, loaded only by
|
// dispatchers, or refreshState — that's `./tabs.js`, loaded only by
|
||||||
// /index.html. The flow page runs purely on the broker stream + an
|
// /dashboard.html. The flow page runs purely on the broker stream + an
|
||||||
// initial /api/state fetch (compose autocomplete needs the live
|
// initial /api/state fetch (compose autocomplete needs the live
|
||||||
// container list).
|
// container list).
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ import {
|
||||||
|
|
||||||
// ─── local containers cache (for compose autocomplete) ──────────────────
|
// ─── local containers cache (for compose autocomplete) ──────────────────
|
||||||
// The compose box's @-mention completion suggests known agent names.
|
// The compose box's @-mention completion suggests known agent names.
|
||||||
// /index.html (tabs.js) maintains the canonical `containersState`
|
// /dashboard.html (tabs.js) maintains the canonical `containersState`
|
||||||
// from /api/state + SSE; here we keep a small local mirror updated
|
// from /api/state + SSE; here we keep a small local mirror updated
|
||||||
// by the same `container_state_changed` / `container_removed` events
|
// by the same `container_state_changed` / `container_removed` events
|
||||||
// the dashboard would handle.
|
// the dashboard would handle.
|
||||||
|
|
@ -248,7 +248,7 @@ import {
|
||||||
// Drop every other mutation kind silently — without this
|
// Drop every other mutation kind silently — without this
|
||||||
// they'd fall through to the terminal module's default
|
// they'd fall through to the terminal module's default
|
||||||
// renderer and clutter the log with JSON dumps. The dashboard
|
// renderer and clutter the log with JSON dumps. The dashboard
|
||||||
// tabs handle these on /index.html via tabs.js.
|
// tabs handle these on /dashboard.html via tabs.js.
|
||||||
_default: () => {},
|
_default: () => {},
|
||||||
},
|
},
|
||||||
// Re-sync the local containers cache on every SSE (re)connect.
|
// Re-sync the local containers cache on every SSE (re)connect.
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>hyperhive // h0m3</title>
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
||||||
<link rel="stylesheet" href="/static/theme.css">
|
|
||||||
<link rel="stylesheet" href="/static/common.css">
|
|
||||||
<link rel="stylesheet" href="/static/home.css">
|
|
||||||
</head>
|
|
||||||
<body class="home-shell">
|
|
||||||
|
|
||||||
<!-- H0M3: the menu hub. A plain grid of links to every top-level
|
|
||||||
surface. Per #1464 this becomes the page served at `/` once the
|
|
||||||
route swap lands (dashboard relocates to /dashboard); for now it
|
|
||||||
lives at /home.html and links to the surfaces at their current
|
|
||||||
routes. No tabbar / SSE — it's a static portal. -->
|
|
||||||
<header class="home-header">
|
|
||||||
<p class="banner-thin" id="hive-identity" hidden></p>
|
|
||||||
<pre class="banner">░▒▓█▓▒░ ░▒▓█▓▒░ H Y P E R H I V E · H0M3 ░▒▓█▓▒░ ░▒▓█▓▒░</pre>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="home-main">
|
|
||||||
<nav class="home-menu" aria-label="hyperhive surfaces">
|
|
||||||
|
|
||||||
<a class="home-tile" href="/">
|
|
||||||
<span class="home-tile-label">Dashboard</span>
|
|
||||||
<span class="home-tile-desc">containers · approvals · system · stats</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="home-tile" href="/flow.html">
|
|
||||||
<span class="home-tile-label">Flow</span>
|
|
||||||
<span class="home-tile-desc">live all-agents message firehose</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="home-tile" href="/logs.html">
|
|
||||||
<span class="home-tile-label">Logs</span>
|
|
||||||
<span class="home-tile-desc">build · agent · system logs</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- Matrix tile: hidden until home.js confirms the matrix GUI is
|
|
||||||
enabled (state.matrix_gui_enabled), mirroring the dashboard
|
|
||||||
tab gating so operators without it don't see a dead link. -->
|
|
||||||
<a class="home-tile" id="home-tile-matrix" href="/matrix/" hidden>
|
|
||||||
<span class="home-tile-label">Matrix</span>
|
|
||||||
<span class="home-tile-desc">matrix chat client</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</nav>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<script type="module" src="/static/home.js" defer></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -3,384 +3,52 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>hyperhive // h1ve-c0re</title>
|
<title>hyperhive // h0m3</title>
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
<link rel="stylesheet" href="/static/theme.css">
|
<link rel="stylesheet" href="/static/theme.css">
|
||||||
<link rel="stylesheet" href="/static/common.css">
|
<link rel="stylesheet" href="/static/common.css">
|
||||||
<link rel="stylesheet" href="/static/dashboard.css">
|
<link rel="stylesheet" href="/static/home.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="dashboard-shell">
|
<body class="home-shell">
|
||||||
|
|
||||||
<!-- Sticky chrome — just the tab strip. The "WE ARE THE WIRED"
|
<!-- H0M3: the menu hub. A plain grid of links to every top-level
|
||||||
slug lives at the page footer below `<main>`; chrome is
|
surface. This is the page served at `/` — the landing page — with
|
||||||
navigation only. Tabs route via the URL hash so F5 / back-
|
the dashboard relocated to /dashboard.html. No tabbar / SSE —
|
||||||
button / shared links keep you on the same view. JS owns
|
it's a static portal. -->
|
||||||
the show/hide. -->
|
<header class="home-header">
|
||||||
<header class="dashboard-chrome">
|
|
||||||
<!-- Hive identity line: swarm/hive label. Hidden until JS populates
|
|
||||||
it from /api/state (hive_name + swarm_name). Falls back to
|
|
||||||
hostname when only the domain is configured. -->
|
|
||||||
<p class="banner-thin" id="hive-identity" hidden></p>
|
<p class="banner-thin" id="hive-identity" hidden></p>
|
||||||
<nav class="tabbar" id="tabbar" role="tablist">
|
<pre class="banner">░▒▓█▓▒░ ░▒▓█▓▒░ H Y P E R H I V E · H0M3 ░▒▓█▓▒░ ░▒▓█▓▒░</pre>
|
||||||
<a class="tab" id="tab-swarm" href="#swarm" role="tab"
|
|
||||||
aria-controls="tab-pane-swarm"
|
|
||||||
data-tab="swarm">
|
|
||||||
<span class="tab-label">◆ SW4RM ◆</span>
|
|
||||||
<span class="tab-count" id="tab-count-swarm" hidden></span>
|
|
||||||
</a>
|
|
||||||
<a class="tab" id="tab-call" href="#call" role="tab"
|
|
||||||
aria-controls="tab-pane-call"
|
|
||||||
data-tab="call">
|
|
||||||
<span class="tab-label">◆ Y3R C4LL ◆</span>
|
|
||||||
<span class="tab-count tab-count-attn" id="tab-count-call" hidden></span>
|
|
||||||
</a>
|
|
||||||
<a class="tab" id="tab-system" href="#system" role="tab"
|
|
||||||
aria-controls="tab-pane-system"
|
|
||||||
data-tab="system">
|
|
||||||
<span class="tab-label">◆ SYST3M ◆</span>
|
|
||||||
<span class="tab-count" id="tab-count-system" hidden></span>
|
|
||||||
</a>
|
|
||||||
<!-- P3RM1SS10NS: per-agent capability grants and tool-group
|
|
||||||
assignments. Both tables are fetched on tab activation. -->
|
|
||||||
<a class="tab" id="tab-permissions" href="#permissions" role="tab"
|
|
||||||
aria-controls="tab-pane-permissions"
|
|
||||||
data-tab="permissions">
|
|
||||||
<span class="tab-label">◆ P3RM1SS10NS ◆</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- SCH3DUL3S: scheduled-prompts surface. List of queued
|
|
||||||
schedules + an operator-direct creation form. Count pill
|
|
||||||
mirrors the active (non-cancelled) schedule count; hidden
|
|
||||||
when zero. -->
|
|
||||||
<a class="tab" id="tab-schedules" href="#schedules" role="tab"
|
|
||||||
aria-controls="tab-pane-schedules"
|
|
||||||
data-tab="schedules">
|
|
||||||
<span class="tab-label">◆ SCH3DUL3S ◆</span>
|
|
||||||
<span class="tab-count" id="tab-count-schedules" hidden></span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- ST4TS: hive-wide turn-stats rollup (swarm totals, busiest
|
|
||||||
agents, model mix, est cost). Fetched on tab activation +
|
|
||||||
window change from GET /api/stats-hive. -->
|
|
||||||
<a class="tab" id="tab-stats" href="#stats" role="tab"
|
|
||||||
aria-controls="tab-pane-stats"
|
|
||||||
data-tab="stats">
|
|
||||||
<span class="tab-label">◆ ST4TS ◆</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- P33RS: peer hive navigation links. Hidden until at least one
|
|
||||||
peer is configured in `services.hyperhive.peers`. -->
|
|
||||||
<a class="tab" id="tab-peers" href="#peers" role="tab"
|
|
||||||
aria-controls="tab-pane-peers"
|
|
||||||
data-tab="peers" hidden>
|
|
||||||
<span class="tab-label">◆ P33RS ◆</span>
|
|
||||||
<span class="tab-count" id="tab-count-peers" hidden></span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- M4TR1X: optional matrix web client (fluffychat-web by
|
|
||||||
default) mounted at /matrix/ by the gateway when
|
|
||||||
`hyperhive.matrix.gui.enable = true`. Same-origin
|
|
||||||
navigation (the static dist is its own SPA). Hidden in
|
|
||||||
JS when `state.matrix_gui_enabled === false` so operators
|
|
||||||
without matrix-gui on don't see a dead link. See
|
|
||||||
docs/web-ui.md::Tab strip for the gating model and
|
|
||||||
docs/gateway.md for the matrix vhost + .well-known
|
|
||||||
auto-discovery. -->
|
|
||||||
<a class="tab tab-link" id="tab-matrix" href="/matrix/" hidden
|
|
||||||
title="open the matrix chat client (fluffychat-web)">
|
|
||||||
<span class="tab-label">◆ M4TR1X ◆ →</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- FL0W is its own page (`/flow.html`), not a tab. The link
|
|
||||||
lives in the tab strip so it reads as a peer surface;
|
|
||||||
clicking navigates rather than swapping panes in place.
|
|
||||||
Count pill mirrors the dashboard's operator-inbox length
|
|
||||||
and is hidden when zero. See docs/web-ui.md::FL0W page. -->
|
|
||||||
<a class="tab tab-link" id="tab-flow" href="/flow.html"
|
|
||||||
title="open the all-agents chat in a dedicated full-page terminal">
|
|
||||||
<span class="tab-label">◆ FL0W ◆ →</span>
|
|
||||||
<span class="tab-count" id="tab-count-flow" hidden></span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- L0GS and S3TT1NGS: default-overflow — always live in the ⋮
|
|
||||||
dropdown, never in the main strip. `data-overflow="default"`
|
|
||||||
marks them so JS keeps them out of the bar regardless of
|
|
||||||
available width. -->
|
|
||||||
<a class="tab tab-link" id="tab-logs" href="/logs.html"
|
|
||||||
data-overflow="default"
|
|
||||||
title="open the log viewer">
|
|
||||||
<span class="tab-label">◆ L0GS ◆ →</span>
|
|
||||||
</a>
|
|
||||||
<a class="tab" id="tab-settings" href="#settings" role="tab"
|
|
||||||
aria-controls="tab-pane-settings"
|
|
||||||
data-tab="settings"
|
|
||||||
data-overflow="default">
|
|
||||||
<span class="tab-label">◆ S3TT1NGS ◆</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- Overflow ⋮ button + dropdown. JS populates the dropdown with
|
|
||||||
tab clones and keeps the button hidden when the dropdown would
|
|
||||||
be empty. Gets `.tabbar-overflow-active` when the active tab
|
|
||||||
is inside. -->
|
|
||||||
<div class="tabbar-overflow" id="tabbar-overflow">
|
|
||||||
<button type="button" class="tabbar-overflow-btn" id="tabbar-overflow-btn"
|
|
||||||
aria-haspopup="menu" aria-expanded="false"
|
|
||||||
title="more tabs" hidden>⋮</button>
|
|
||||||
<ul class="tabbar-overflow-dropdown" id="tabbar-overflow-dropdown"
|
|
||||||
role="menu" hidden></ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Tab panes. Exactly one is `.tab-pane-active` at a time;
|
<main class="home-main">
|
||||||
JS toggles based on the URL hash + `hashchange` events. -->
|
<nav class="home-menu" aria-label="hyperhive surfaces">
|
||||||
<main class="dashboard-main">
|
|
||||||
|
|
||||||
<!-- SW4RM: the swarm itself. Container cards (the central thing
|
<a class="home-tile" href="/dashboard.html">
|
||||||
the operator looks at) and rebuild queue / cascade visualisation
|
<span class="home-tile-label">Dashboard</span>
|
||||||
that drives them. No inline `C0NTAINERS` h2 heading + divider
|
<span class="home-tile-desc">containers · approvals · system · stats</span>
|
||||||
— the tab label SW4RM already says it. -->
|
</a>
|
||||||
<section class="tab-pane" id="tab-pane-swarm"
|
|
||||||
role="tabpanel" aria-labelledby="tab-swarm">
|
|
||||||
<div id="containers-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Y3R C4LL: things blocked on operator decision. Approvals +
|
<a class="home-tile" href="/flow.html">
|
||||||
questions read as the same concept ("something is waiting on
|
<span class="home-tile-label">Flow</span>
|
||||||
you"); both surface their full bodies inline so the operator
|
<span class="home-tile-desc">live all-agents message firehose</span>
|
||||||
can decide without leaving the pane. -->
|
</a>
|
||||||
<section class="tab-pane" id="tab-pane-call"
|
|
||||||
role="tabpanel" aria-labelledby="tab-call">
|
|
||||||
<!-- 1NB0X: unread agent→operator messages (#1469). Fetched on
|
|
||||||
tab activation + cold load, appended live from the broker
|
|
||||||
stream, cleared via "mark all read". Count folds into the
|
|
||||||
Y3R C4LL pill so messages aren't missed. -->
|
|
||||||
<h2>◆ 1NB0X ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<div id="operator-inbox-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>◆ P3NDING APPR0VALS ◆</h2>
|
<a class="home-tile" href="/logs.html">
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
<span class="home-tile-label">Logs</span>
|
||||||
<div id="approvals-section">
|
<span class="home-tile-desc">build · agent · system logs</span>
|
||||||
<p class="meta">loading…</p>
|
</a>
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>◆ M1ND H4S QU3STI0NS ◆</h2>
|
<!-- Matrix tile: hidden until home.js confirms the matrix GUI is
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
enabled (state.matrix_gui_enabled), mirroring the dashboard
|
||||||
<div id="questions-section">
|
tab gating so operators without it don't see a dead link. -->
|
||||||
<p class="meta">loading…</p>
|
<a class="home-tile" id="home-tile-matrix" href="/matrix/" hidden>
|
||||||
</div>
|
<span class="home-tile-label">Matrix</span>
|
||||||
</section>
|
<span class="home-tile-desc">matrix chat client</span>
|
||||||
|
</a>
|
||||||
<!-- SYST3M: passive / rare-interaction state. Meta inputs (lock
|
|
||||||
bumps), rebuild queue (watch only), kept state from previous
|
|
||||||
tombstoned agents. Per-section content auto-compresses to a
|
|
||||||
one-line summary when empty (JS toggle). -->
|
|
||||||
<section class="tab-pane" id="tab-pane-system"
|
|
||||||
role="tabpanel" aria-labelledby="tab-system">
|
|
||||||
<h2>◆ M3T4 1NPUTS ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">select inputs to <code>nix flake update</code> in <code>/meta/</code>. selected agents rebuild in sequence after the lock bump; manager learns each outcome via the usual <code>rebuilt</code> system event.</p>
|
|
||||||
<div id="meta-inputs-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>◆ R3BU1LD QU3U3 ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">pending + running rebuilds, meta-updates, and first-spawns. one runs at a time; meta-update cascades nest under their parent. dedup: re-enqueueing a still-queued op collapses into the existing entry.</p>
|
|
||||||
<div id="rebuild-queue-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>◆ K3PT ST4T3 ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<div id="tombstones-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- C0NT41N3R L04D: live cpu + memory per agent container, read
|
|
||||||
from cgroup v2 on the host. Polled only while this tab is
|
|
||||||
active (cpu needs a short two-sample read each refresh). -->
|
|
||||||
<h2>◆ C0NT41N3R L04D ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">live cpu + memory per agent container, from cgroup v2 on the host. cpu is % of total host capacity (all cores), sampled over ~200ms each refresh; polled every 5s while this tab is open. network is omitted on purpose — agents share the host netns, so there is no per-container counter (see <code>docs/network.md</code>).</p>
|
|
||||||
<div id="container-load-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- P3RM1SS10NS: per-agent capability grants + tool-group
|
|
||||||
assignments. Both tables are column-driven from the backend
|
|
||||||
(GET /api/capabilities, GET /api/tool-groups) so new entries
|
|
||||||
require no UI change. Saving POSTs to the respective
|
|
||||||
/{agent} endpoint and queues a rebuild. -->
|
|
||||||
<section class="tab-pane" id="tab-pane-permissions"
|
|
||||||
role="tabpanel" aria-labelledby="tab-permissions">
|
|
||||||
<h2>◆ C4P4B1L1T13S ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">per-agent capability grants. capabilities unlock gated MCP tools and system access. saving queues a rebuild for the affected agent.</p>
|
|
||||||
<div id="capabilities-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- T00L GR0UPS: per-agent tool-group permission matrix. Rows = agents,
|
|
||||||
cols = tool groups fetched from GET /api/tool-groups.
|
|
||||||
Checking / unchecking and saving POSTs to
|
|
||||||
/api/tool-groups/{agent}; a rebuild is queued automatically.
|
|
||||||
Absent agents default to the role default (shown in parens). -->
|
|
||||||
<h2>◆ T00L GR0UPS ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">per-agent tool-group permissions. columns are filled from the backend — adding a new group requires no UI change. agents without an explicit entry use the role default (agents: messaging, meta, inbox, execution; manager: all). saving queues a rebuild.</p>
|
|
||||||
<div id="tool-groups-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- P33RS: peer hive link cards. Rendered from state.peer_hives;
|
|
||||||
hidden when the list is empty. -->
|
|
||||||
<section class="tab-pane" id="tab-pane-peers"
|
|
||||||
role="tabpanel" aria-labelledby="tab-peers">
|
|
||||||
<h2>◆ P33R H1V3S ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">peer hives in this swarm. click a card to open that hive's dashboard.</p>
|
|
||||||
<div id="peers-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- SCH3DUL3S: scheduled prompts. Creation + edit are folded
|
|
||||||
into the same table — empty bottom row is the create form
|
|
||||||
(fill cells, click +), inline-edit-row expands on the `✎`
|
|
||||||
toggle for existing schedules. Schedules list driven by
|
|
||||||
GET /api/schedules; POST /api/schedules to create, PATCH
|
|
||||||
/api/schedules/{id} to edit, POST /api/schedules/{id}/cancel
|
|
||||||
for per-target / whole-row cancel. Live updates via
|
|
||||||
`schedules_changed` SSE; tab activation re-fetches as a
|
|
||||||
safety net for disconnect windows.
|
|
||||||
See docs/web-ui.md::SCH3DUL3S tab. -->
|
|
||||||
<section class="tab-pane" id="tab-pane-schedules"
|
|
||||||
role="tabpanel" aria-labelledby="tab-schedules">
|
|
||||||
<h2>◆ SCH3DUL3S ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">all schedules currently in the table. fill the bottom row to queue a new schedule (operator-direct, no approval gate; recurring when an interval is set). cancel a single target with the row button or the whole schedule with <code>✕ cancel all</code>.</p>
|
|
||||||
<div id="schedules-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- QU3U3D R3M1ND3RS: self-scheduled agent reminders. Lives
|
|
||||||
on this tab so the operator has one place for everything
|
|
||||||
that fires at a future time — operator-set schedules
|
|
||||||
above, agent-self reminders here. Backed by GET
|
|
||||||
/api/reminders; live updates via `reminders_changed` SSE;
|
|
||||||
refreshReminders() called from refreshState + tab
|
|
||||||
activation as safety net for disconnect windows. -->
|
|
||||||
<h2>◆ QU3U3D R3M1ND3RS ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">reminders agents have queued for themselves but not yet delivered. cancel to drop a stuck or unwanted entry.</p>
|
|
||||||
<div id="reminders-section">
|
|
||||||
<p class="meta">loading…</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- ST4TS: hive-wide turn-stats aggregate. Plain tables/bars (the
|
|
||||||
dashboard bundle has no chart lib); data from GET
|
|
||||||
/api/stats-hive?window=, fetched on tab activation + on
|
|
||||||
window change. Per-agent trend charts stay on each agent's
|
|
||||||
own /stats page. -->
|
|
||||||
<section class="tab-pane" id="tab-pane-stats"
|
|
||||||
role="tabpanel" aria-labelledby="tab-stats">
|
|
||||||
<h2>◆ ST4TS ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">hive-wide turn statistics, aggregated across every agent over the selected window. <strong>cost is a rough estimate</strong> from approximate per-model list prices — it drifts and is a ballpark, not a bill.</p>
|
|
||||||
<div class="hive-stats-windows" id="hive-stats-windows">
|
|
||||||
<button type="button" class="btn" data-w="1h">1h</button>
|
|
||||||
<button type="button" class="btn" data-w="4h">4h</button>
|
|
||||||
<button type="button" class="btn active" data-w="24h">24h</button>
|
|
||||||
<button type="button" class="btn" data-w="3d">3d</button>
|
|
||||||
<button type="button" class="btn" data-w="7d">7d</button>
|
|
||||||
<button type="button" class="btn" data-w="30d">30d</button>
|
|
||||||
</div>
|
|
||||||
<div class="hive-stats-chips" id="hive-stats-summary"></div>
|
|
||||||
<h3>◇ busiest agents</h3>
|
|
||||||
<div id="hive-stats-agents"><p class="meta">loading…</p></div>
|
|
||||||
<h3>◇ model mix (turns across the swarm)</h3>
|
|
||||||
<div id="hive-stats-models"></div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- FL0W: lives on its own page now (`/flow.html`). The
|
|
||||||
message-flow + inbox + compose DOM only exists there — when
|
|
||||||
tabs.js boots on this page the corresponding renderers
|
|
||||||
no-op silently (each guard is `if (!el) return`). -->
|
|
||||||
|
|
||||||
<!-- S3TT1NGS: operator-local preferences. Today this is just the
|
|
||||||
browser-notification toggle (per-browser localStorage state).
|
|
||||||
The same IDs that used to live in the tab strip migrated
|
|
||||||
here unchanged so `NOTIF.bind()` in common.js wires them
|
|
||||||
without any JS change. -->
|
|
||||||
<section class="tab-pane" id="tab-pane-settings"
|
|
||||||
role="tabpanel" aria-labelledby="tab-settings">
|
|
||||||
<h2>◆ S3TT1NGS ◆</h2>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p class="meta">operator-local preferences. these live in the browser's localStorage — they do not sync between devices and do not survive a profile wipe.</p>
|
|
||||||
|
|
||||||
<h3>◇ browser notifications</h3>
|
|
||||||
<p class="meta">desktop notifications for new approvals, new operator questions, and broker messages addressed to you. requires a secure context (https or localhost). mute silences the notifications without revoking the OS-level permission.</p>
|
|
||||||
<div id="notif-row" class="notif-row">
|
|
||||||
<button type="button" id="notif-enable" class="btn btn-notif" hidden>🔔 enable notifications</button>
|
|
||||||
<button type="button" id="notif-mute" class="btn btn-notif" hidden>🔕 mute</button>
|
|
||||||
<button type="button" id="notif-unmute" class="btn btn-notif" hidden>🔔 unmute</button>
|
|
||||||
<span id="notif-status" class="meta" hidden></span>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
</nav>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<script type="module" src="/static/home.js" defer></script>
|
||||||
<pre class="banner banner-thin">░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░</pre>
|
|
||||||
<div class="divider">══════════════════════════════════════════════════════════════</div>
|
|
||||||
<p>▲△▲ <a href="https://forge.darkest.space/hyperhive/hyperhive">hyperhive</a> ▲△▲ hive-c0re on this host ▲△▲</p>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<!-- Slide-in detail panel. Long content (clicked file previews,
|
|
||||||
approval diffs, journald logs, applied config) opens here
|
|
||||||
instead of expanding inline. Singleton — JS swaps the title +
|
|
||||||
body and toggles `.open`. Lives outside the tab panes so it
|
|
||||||
overlays any active tab. -->
|
|
||||||
<div id="side-panel" class="side-panel" aria-hidden="true">
|
|
||||||
<div class="side-panel-backdrop" id="side-panel-backdrop"></div>
|
|
||||||
<aside class="side-panel-drawer" role="dialog" aria-modal="true"
|
|
||||||
aria-labelledby="side-panel-title">
|
|
||||||
<header class="side-panel-head">
|
|
||||||
<span class="side-panel-title" id="side-panel-title"></span>
|
|
||||||
<button type="button" class="side-panel-close" id="side-panel-close"
|
|
||||||
title="close (esc)">✕</button>
|
|
||||||
</header>
|
|
||||||
<div class="side-panel-body" id="side-panel-body"></div>
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Selection action bar. Sticky-bottom strip that slides into
|
|
||||||
view when one or more agent cards is selected (click the icon
|
|
||||||
to toggle). See docs/web-ui.md::Selection bar for the bulk
|
|
||||||
action gating + clear semantics. -->
|
|
||||||
<div id="selection-bar" class="selection-bar" hidden role="toolbar"
|
|
||||||
aria-label="bulk agent actions">
|
|
||||||
<span class="selection-count" id="selection-count"></span>
|
|
||||||
<span class="selection-names" id="selection-names"></span>
|
|
||||||
<span class="selection-actions" id="selection-actions"></span>
|
|
||||||
<button type="button" class="btn selection-clear" id="selection-clear"
|
|
||||||
title="clear selection (esc)">✕ clear</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Single bundled entry — tabs.js is the dashboard tabs surface;
|
|
||||||
flow.html has its own flow.js bundle. esbuild folds
|
|
||||||
@hive/shared/terminal.js and the marked npm package into
|
|
||||||
tabs.js. -->
|
|
||||||
<script type="module" src="/static/tabs.js" defer></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@
|
||||||
<body class="logs-shell">
|
<body class="logs-shell">
|
||||||
|
|
||||||
<!-- Minimal chrome: back link + sub-tab strip.
|
<!-- Minimal chrome: back link + sub-tab strip.
|
||||||
Same pattern as flow.html — no full dashboard tabbar. -->
|
Same pattern as flow.html — no full dashboard tabbar. Back link
|
||||||
|
points to the H0M3 hub (served at /), not the dashboard. -->
|
||||||
<header class="logs-header">
|
<header class="logs-header">
|
||||||
<a class="logs-back" href="/">← dashboard</a>
|
<a class="logs-back" href="/">← home</a>
|
||||||
<nav class="logs-tabbar" id="logs-tabbar" role="tablist">
|
<nav class="logs-tabbar" id="logs-tabbar" role="tablist">
|
||||||
<a class="logs-tab" id="logs-tab-build" href="#build" role="tab"
|
<a class="logs-tab" id="logs-tab-build" href="#build" role="tab"
|
||||||
aria-controls="logs-pane-build" data-logs-tab="build">
|
aria-controls="logs-pane-build" data-logs-tab="build">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// /index.html entry point: tab renderers + tab routing + refreshState
|
// /dashboard.html entry point: tab renderers + tab routing + refreshState
|
||||||
// + notification deltas. Reads /api/state on cold load and after every
|
// + notification deltas. Reads /api/state on cold load and after every
|
||||||
// async-form submit; live updates run through `applyXxx` mutation
|
// async-form submit; live updates run through `applyXxx` mutation
|
||||||
// handlers triggered by the dashboard event stream (subscribed via
|
// handlers triggered by the dashboard event stream (subscribed via
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
// Pure helpers (DOM, side panel, OS notifications, path linkification)
|
// Pure helpers (DOM, side panel, OS notifications, path linkification)
|
||||||
// live in `./common.js`; the flow-page surface (operator inbox, broker
|
// live in `./common.js`; the flow-page surface (operator inbox, broker
|
||||||
// terminal, @-mention composer) lives in `./flow.js` — `flow.html` and
|
// terminal, @-mention composer) lives in `./flow.js` — `flow.html` and
|
||||||
// `index.html` each load their own bundle.
|
// `dashboard.html` each load their own bundle.
|
||||||
|
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import {
|
import {
|
||||||
|
|
@ -838,7 +838,7 @@ window.marked = marked;
|
||||||
|
|
||||||
function renderContainers(s) {
|
function renderContainers(s) {
|
||||||
const root = $('containers-section');
|
const root = $('containers-section');
|
||||||
// #containers-section only exists on /index.html. tabs.js is the
|
// #containers-section only exists on /dashboard.html. tabs.js is the
|
||||||
// bundle for that page only (flow.html loads flow.js instead).
|
// bundle for that page only (flow.html loads flow.js instead).
|
||||||
// Belt-and-suspenders for any future page that adds tabs.js
|
// Belt-and-suspenders for any future page that adds tabs.js
|
||||||
// without a #containers-section — matches the
|
// without a #containers-section — matches the
|
||||||
|
|
@ -1287,7 +1287,7 @@ window.marked = marked;
|
||||||
|
|
||||||
function renderTombstones(s) {
|
function renderTombstones(s) {
|
||||||
const root = $('tombstones-section');
|
const root = $('tombstones-section');
|
||||||
// #tombstones-section only lives on /index.html (SYST3M tab);
|
// #tombstones-section only lives on /dashboard.html (SYST3M tab);
|
||||||
// no-op on /flow.html and any other page that loads the shared
|
// no-op on /flow.html and any other page that loads the shared
|
||||||
// bundle without the dashboard's tab panes.
|
// bundle without the dashboard's tab panes.
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
|
|
@ -1814,7 +1814,7 @@ window.marked = marked;
|
||||||
|
|
||||||
function renderQuestions() {
|
function renderQuestions() {
|
||||||
const root = $('questions-section');
|
const root = $('questions-section');
|
||||||
// #questions-section only lives on /index.html (Y3R C4LL tab);
|
// #questions-section only lives on /dashboard.html (Y3R C4LL tab);
|
||||||
// no-op when the section is missing. `question_added` /
|
// no-op when the section is missing. `question_added` /
|
||||||
// `question_resolved` SSE events route through here.
|
// `question_resolved` SSE events route through here.
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
|
|
@ -2173,7 +2173,7 @@ window.marked = marked;
|
||||||
|
|
||||||
function renderApprovals() {
|
function renderApprovals() {
|
||||||
const root = $('approvals-section');
|
const root = $('approvals-section');
|
||||||
// #approvals-section only lives on /index.html (Y3R C4LL tab);
|
// #approvals-section only lives on /dashboard.html (Y3R C4LL tab);
|
||||||
// no-op elsewhere — `approval_added` / `approval_resolved` SSE
|
// no-op elsewhere — `approval_added` / `approval_resolved` SSE
|
||||||
// events route through here on every page that loads the bundle.
|
// events route through here on every page that loads the bundle.
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
|
|
@ -3990,7 +3990,7 @@ window.marked = marked;
|
||||||
// lost during the disconnect window (same pattern as flow.js's
|
// lost during the disconnect window (same pattern as flow.js's
|
||||||
// onStreamOpen).
|
// onStreamOpen).
|
||||||
//
|
//
|
||||||
// Both /index.html and /flow.html subscribe to `/dashboard/stream`
|
// Both /dashboard.html and /flow.html subscribe to `/dashboard/stream`
|
||||||
// and filter client-side — the dashboard ignores broker traffic
|
// and filter client-side — the dashboard ignores broker traffic
|
||||||
// and the inbox ignores mutation events.
|
// and the inbox ignores mutation events.
|
||||||
const MUTATION_HANDLERS = {
|
const MUTATION_HANDLERS = {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue