docs: retire the stray top-level web-ui.md, fold it into web-ui/README.md

docs/web-ui.md duplicated the web-ui/ directory name at the top level --
the only such collision in docs/ (every other subsystem has just a
directory, no sibling <dir>.md file). That's exactly why it rendered
outside the directory structure in the docs site nav (mara's report,
hyperhive#4054): the site build walks docs/ generically with no
special-casing, so a loose top-level file next to a same-named
directory shows up as its own flat top-level entry instead of nesting
under that directory's section.

web-ui.md's own first paragraph already said as much -- 'This doc has
been split for readability... start at web-ui/README.md instead.' It
was a leftover pointer from before the split, not a page carrying
unique content on its own merit.

Folded its two sections web-ui/README.md didn't already have (the
swarm-ui design-guide link, and the task-oriented 'reading paths'
quick-lookup list) into web-ui/README.md's existing 'More depth'
section, then deleted the stray file and repointed every real
reference at it: 3 in-tree doc cross-links, 3 doc prose mentions
(retargeted to the more specific dashboard.md/shape.md sub-page each
one was actually about), and ~28 frontend source comments
(dashboard/agent/shared packages) that cited it as
'docs/web-ui.md::<heading>' for implementation context -- retargeted
each to whichever of dashboard.md/shape.md/agent.md actually carries
that heading now, verified against each file's real heading list
rather than guessed.

Verified via scripts/check-doc-refs.sh (the same lint CI runs): 0 dead
pointers, both before write (confirming the tree was clean beforehand)
and after (confirming nothing broke).
This commit is contained in:
iris 2026-09-07 15:38:05 +02:00 committed by mara
commit 77296aff35
21 changed files with 74 additions and 96 deletions

View file

@ -448,7 +448,7 @@ Two visible consequences:
on every `rebuild_queue_changed` tick), not on the approvals panel on every `rebuild_queue_changed` tick), not on the approvals panel
(which already moved the row to "approved"). A long meta-update (which already moved the row to "approved"). A long meta-update
cascade renders as a parent DAG with one child rebuild per affected cascade renders as a parent DAG with one child rebuild per affected
agent — see `docs/web-ui.md` for the layout. agent — see `docs/web-ui/dashboard.md` for the layout.
- **Cancellation**: the dashboard's *× cancel* button on a still-queued - **Cancellation**: the dashboard's *× cancel* button on a still-queued
DAG calls `POST /api/rebuild-queue/{id}/cancel`, which flips it to DAG calls `POST /api/rebuild-queue/{id}/cancel`, which flips it to
`Cancelled` before any node runs (and fails the approval row instead `Cancelled` before any node runs (and fails the approval row instead
@ -485,7 +485,7 @@ agent.
The dashboard deep-links into this org — a `config repo` link The dashboard deep-links into this org — a `config repo` link
per container row and a `review PR on forge` link per config-PR per container row and a `review PR on forge` link per config-PR
approval card. See `docs/web-ui.md`. approval card. See `docs/web-ui/dashboard.md`.
### Submitting agent's view of config repos ### Submitting agent's view of config repos

View file

@ -111,7 +111,7 @@ JSON line-delimited over unix sockets in both directions (host admin
/ manager / agent). SSE streams (`/dashboard/stream` on hive-c0re, / manager / agent). SSE streams (`/dashboard/stream` on hive-c0re,
`/events/stream` on the per-agent web UIs) are `text/event-stream`; `/events/stream` on the per-agent web UIs) are `text/event-stream`;
each frame carries a `seq` field for the snapshot-dedupe dance each frame carries a `seq` field for the snapshot-dedupe dance
(see `docs/web-ui.md`). Request/response types live in `hive-sh4re` (see `docs/web-ui/shape.md`). Request/response types live in `hive-sh4re`
— change them in one place. The dashboard event vocabulary lives — change them in one place. The dashboard event vocabulary lives
in `hive-c0re::dashboard_events::DashboardEvent`. in `hive-c0re::dashboard_events::DashboardEvent`.

View file

@ -1,59 +0,0 @@
# Web UI
Two web surfaces share the same skeleton: the dashboard (port 7000)
and the per-agent UIs (each container's port is a deterministic hash
in :8100-8999 — see
[`gotchas.md#web-ui-ports-collide-on-hash`](process/gotchas.md#web-ui-ports-collide-on-hash)
for the mechanics and the collision caveat).
Both are SPAs — `GET /` returns a static shell, `/api/state`
returns JSON, JS renders. No full-page reloads.
This doc has been split for readability. Pick the section you need —
or, if you're the operator looking for how to actually _use_ the
dashboard rather than how it's built, start at
**[web-ui/README.md](web-ui/README.md)** instead.
- **[Shape (shared by both)](web-ui/shape.md)** — shared SPA
skeleton, SSE multiplexing, terminal pane, listener bind,
per-agent relative paths, `data-async` form pattern, side panel,
atomic repaint.
- **[Dashboard layout](web-ui/dashboard.md)** — tab contents
(SW4RM, Y3R C4LL, P3RM1SS10NS, SCH3DUL3S) + standalone pages
(C0R3 `/core.html`, BU1LDS `/builds.html`, L0GS `/logs.html`,
CR3D3NTIALS `/credentials.html`), container row,
topology tree, selection bar, approval card, browser
notifications, dashboard endpoints + event channel.
- **[Per-agent page](web-ui/agent.md)** — header, main terminal,
composer, side panel + inbox, live view, slash commands,
per-agent endpoints, stats page.
- **[CSS theme variables](web-ui/css-vars.md)** — the Catppuccin
Mocha custom properties declared once in `base.css` and the rule
that per-page stylesheets reference (never redeclare) them.
- **[swarm-ui design guide](web-ui/design-guide.md)** — visual
language, motion, theming, error-UX, and component-first principles
for the swarm-level Preact app specifically (not this doc's
dashboard/agent UIs). `/components` on a running swarm-ui is the
companion living demo of every primitive it references.
## Reading paths
- **"How does the dashboard SPA stay live without polling?"** →
[`web-ui/shape.md`](web-ui/shape.md) (SSE multiplexing,
Worker-death self-heal, atomic repaint).
- **"What does a container row contain?"** →
[`web-ui/dashboard.md`](web-ui/dashboard.md) (Container row,
Topology tree, Selection bar).
- **"What endpoints does the dashboard expose?"** →
[`web-ui/dashboard.md`](web-ui/dashboard.md) (Dashboard
endpoints, Dashboard event channel).
- **"How does the per-agent terminal render tool calls?"** →
[`terminal-rendering.md`](web-ui/terminal-rendering.md) (full row
taxonomy + dispatch walkthrough); for a high-level summary see
[`web-ui/agent.md`](web-ui/agent.md) (Per-stream rendering).
- **"What slash commands does the agent accept?"** →
[`web-ui/agent.md`](web-ui/agent.md) (Terminal-embedded prompt).
- **"What are the per-agent HTTP endpoints?"** →
[`web-ui/agent.md`](web-ui/agent.md) (Per-agent endpoints).
- **"Which CSS variable do I use / where are colours defined?"** →
[`web-ui/css-vars.md`](web-ui/css-vars.md) (Palette, single-source
`base.css` rule).

View file

@ -85,12 +85,49 @@ that agent's config repo.
## More depth ## More depth
Both the dashboard and the per-agent pages are SPAs sharing one
skeleton: `GET /` returns a static shell, `/api/state` returns JSON,
JS renders — no full-page reloads.
- **[Dashboard layout](dashboard.md)** — every tab and standalone page, - **[Dashboard layout](dashboard.md)** — every tab and standalone page,
in full implementation detail: endpoint shapes, event wiring, exact in full implementation detail: endpoint shapes, event wiring, exact
badge-derivation rules. badge-derivation rules.
- **[Per-agent page](agent.md)** — the per-agent terminal, composer, - **[Per-agent page](agent.md)** — the per-agent terminal, composer,
side panel, slash commands, and per-agent endpoints. side panel, slash commands, and per-agent endpoints.
- **[Shape (shared by both)](shape.md)** — the SPA skeleton, SSE - **[Shape (shared by both)](shape.md)** — the SPA skeleton, SSE
multiplexing, and other plumbing shared across every page. multiplexing, terminal pane, listener bind, per-agent relative
- **[CSS theme variables](css-vars.md)** — the colour system, for paths, `data-async` form pattern, side panel, atomic repaint.
anyone touching the frontend's CSS. - **[CSS theme variables](css-vars.md)** — the Catppuccin Mocha custom
properties declared once in `base.css` and the rule that per-page
stylesheets reference (never redeclare) them.
- **[swarm-ui design guide](design-guide.md)** — visual language,
motion, theming, error-UX, and component-first principles for the
swarm-level Preact app specifically (not this page's dashboard/agent
UIs). `/components` on a running swarm-ui is the companion living
demo of every primitive it references.
### Implementation reading paths
Task-oriented jumps straight to the relevant section, for when you're
touching the code rather than using the UI:
- **"How does the dashboard SPA stay live without polling?"** →
[`shape.md`](shape.md) (SSE multiplexing, Worker-death self-heal,
atomic repaint).
- **"What does a container row contain?"** →
[`dashboard.md`](dashboard.md) (Container row, Topology tree,
Selection bar).
- **"What endpoints does the dashboard expose?"** →
[`dashboard.md`](dashboard.md) (Dashboard endpoints, Dashboard event
channel).
- **"How does the per-agent terminal render tool calls?"** →
[`terminal-rendering.md`](terminal-rendering.md) (full row taxonomy
and dispatch walkthrough); for a high-level summary see
[`agent.md`](agent.md) (Per-stream rendering).
- **"What slash commands does the agent accept?"** →
[`agent.md`](agent.md) (Terminal-embedded prompt).
- **"What are the per-agent HTTP endpoints?"** →
[`agent.md`](agent.md) (Per-agent endpoints).
- **"Which CSS variable do I use / where are colours defined?"** →
[`css-vars.md`](css-vars.md) (Palette, single-source `base.css`
rule).

View file

@ -1,6 +1,6 @@
# Per-agent page # Per-agent page
> Part of [Web UI](../web-ui.md). See also: > Part of [Web UI](README.md). See also:
> [Shape (shared)](shape.md) · [Dashboard layout](dashboard.md) > [Shape (shared)](shape.md) · [Dashboard layout](dashboard.md)

View file

@ -1,6 +1,6 @@
# Dashboard layout # Dashboard layout
> Part of [Web UI](../web-ui.md). See also: > Part of [Web UI](README.md). See also:
> [Shape (shared)](shape.md) · [Per-agent page](agent.md) > [Shape (shared)](shape.md) · [Per-agent page](agent.md)

View file

@ -1,6 +1,6 @@
# Shape (shared by both) # Shape (shared by both)
> Part of [Web UI](../web-ui.md). See also: > Part of [Web UI](README.md). See also:
> [Dashboard layout](dashboard.md) · [Per-agent page](agent.md) > [Dashboard layout](dashboard.md) · [Per-agent page](agent.md)
## Shared routes ## Shared routes

View file

@ -16,7 +16,7 @@
/* full-screen layout overrides /* full-screen layout overrides
The agent page mounts a full-viewport terminal under a fixed The agent page mounts a full-viewport terminal under a fixed
frosted-glass header + composer pair. See docs/web-ui.md::Per-agent frosted-glass header + composer pair. See docs/web-ui/agent.md::Per-agent
page for the layer / pill / side-panel structure; rules below page for the layer / pill / side-panel structure; rules below
override the in-page defaults from @hive/shared. */ override the in-page defaults from @hive/shared. */
@ -175,7 +175,7 @@ h3 {
} }
.agent-icon { .agent-icon {
/* Square identity anchor explicit 5em sizing + align-self. /* Square identity anchor explicit 5em sizing + align-self.
See docs/web-ui.md::Per-agent page (Agent icon) for the See docs/web-ui/agent.md::Per-agent page (Agent icon) for the
intrinsic-dim-pushes-parent-flex-open + sticks-to-top intrinsic-dim-pushes-parent-flex-open + sticks-to-top
rationale. */ rationale. */
width: 5em; width: 5em;
@ -702,7 +702,7 @@ pre.diff {
the composer (z-30) so the pill sits on the top layer instead of the composer (z-30) so the pill sits on the top layer instead of
being clipped by the floating chrome. (Pill is anchored in being clipped by the floating chrome. (Pill is anchored in
`.agent-main` rather than `.terminal-wrap` see `.agent-main` rather than `.terminal-wrap` see
docs/web-ui.md::Per-agent page Terminal-wrap paragraph for the docs/web-ui/agent.md::Per-agent page Terminal-wrap paragraph for the
backdrop-filter stacking-context rationale.) */ backdrop-filter stacking-context rationale.) */
.agent-main .tail-pill { .agent-main .tail-pill {
bottom: calc(var(--agent-composer-h) + 0.6em); bottom: calc(var(--agent-composer-h) + 0.6em);

View file

@ -61,7 +61,7 @@
}); });
// --- Fit-to-window toggle --- // --- Fit-to-window toggle ---
// See docs/web-ui.md::Per-agent endpoints (GET /screen) for the // See docs/web-ui/agent.md::Per-agent endpoints (GET /screen) for the
// localStorage persistence + canvas-intrinsic-resolution + // localStorage persistence + canvas-intrinsic-resolution +
// pointer-rescale model. // pointer-rescale model.
let fitMode = localStorage.getItem("screen-fit") !== "off"; let fitMode = localStorage.getItem("screen-fit") !== "off";
@ -105,7 +105,7 @@
// Sends an RFB SetDesktopSize request so the VNC server (weston) // Sends an RFB SetDesktopSize request so the VNC server (weston)
// changes its actual output resolution to match the browser // changes its actual output resolution to match the browser
// viewport — sharper than fit-mode's CSS downscale. Gated on the // viewport — sharper than fit-mode's CSS downscale. Gated on the
// server's ExtendedDesktopSize advert; see docs/web-ui.md::Per-agent // server's ExtendedDesktopSize advert; see docs/web-ui/agent.md::Per-agent
// endpoints (GET /screen). // endpoints (GET /screen).
let extDesktopSupported = false; let extDesktopSupported = false;
let screenId = 1; // captured from the server's ExtendedDesktopSize advert let screenId = 1; // captured from the server's ExtendedDesktopSize advert

View file

@ -270,7 +270,7 @@ export function renderApprovals() {
if (isMergePr && a.sha_short) head.append(el("code", {}, a.sha_short)); if (isMergePr && a.sha_short) head.append(el("code", {}, a.sha_short));
// When the approval was requested — relative time, right-aligned. // When the approval was requested — relative time, right-aligned.
// Goes amber once it's been pending an hour so a stale request is // Goes amber once it's been pending an hour so a stale request is
// obvious at a glance (see docs/web-ui.md::Approval card). // obvious at a glance (see docs/web-ui/dashboard.md::Approval card).
if (a.requested_at != null) { if (a.requested_at != null) {
const requestedSec = epochSec(a.requested_at); const requestedSec = epochSec(a.requested_at);
const ageSec = Math.max(0, Math.floor(Date.now() / 1000 - requestedSec)); const ageSec = Math.max(0, Math.floor(Date.now() / 1000 - requestedSec));

View file

@ -253,7 +253,7 @@ code {
} }
/* M0V3 affordance (selection bar) mauve reads as "structural /* M0V3 affordance (selection bar) mauve reads as "structural
change" rather than the destructive red / amber chrome of change" rather than the destructive red / amber chrome of
destroy / rebuild. See docs/web-ui.md::Selection bar. */ destroy / rebuild. See docs/web-ui/dashboard.md::Selection bar. */
.btn-move { .btn-move {
color: var(--purple); color: var(--purple);
border-color: var(--purple); border-color: var(--purple);

View file

@ -68,7 +68,7 @@ export const form = (
// ─── shared-worker SSE pipe ───────────────────────────────────────────── // ─── shared-worker SSE pipe ─────────────────────────────────────────────
// Returns an EventSource-shaped facade backed by a SharedWorker that // Returns an EventSource-shaped facade backed by a SharedWorker that
// holds one upstream `new EventSource(url)` and fans events out to // holds one upstream `new EventSource(url)` and fans events out to
// every connected tab. See docs/web-ui.md (SSE multiplexing paragraph) // every connected tab. See docs/web-ui/shape.md (SSE multiplexing paragraph)
// for the design + Firefox throttling motivation; graceful fallback to // for the design + Firefox throttling motivation; graceful fallback to
// direct EventSource on environments without SharedWorker. // direct EventSource on environments without SharedWorker.
// //
@ -105,7 +105,7 @@ function getSharedPort() {
// SharedWorker death detection: pings from the worker bump the // SharedWorker death detection: pings from the worker bump the
// activity clock; a visibility-gated watchdog polls and re-subscribes // activity clock; a visibility-gated watchdog polls and re-subscribes
// on a fresh port if the page has been silent past the threshold. // on a fresh port if the page has been silent past the threshold.
// See docs/web-ui.md (Worker-death self-heal paragraph) for the // See docs/web-ui/shape.md (Worker-death self-heal paragraph) for the
// timing rationale + Firefox reclaim symptom. // timing rationale + Firefox reclaim symptom.
const WORKER_DEAD_THRESHOLD_MS = 90_000; const WORKER_DEAD_THRESHOLD_MS = 90_000;
const WORKER_WATCHDOG_INTERVAL_MS = 15_000; const WORKER_WATCHDOG_INTERVAL_MS = 15_000;

View file

@ -12,7 +12,7 @@
/* tabbed dashboard chrome /* tabbed dashboard chrome
Top-of-page sticky header with banner + tab strip. SSE stays Top-of-page sticky header with banner + tab strip. SSE stays
alive across tab switches so count pills update live on inactive alive across tab switches so count pills update live on inactive
tabs. See docs/web-ui.md::Chrome header + Tab strip for the tabs. See docs/web-ui/dashboard.md::Chrome header + Tab strip for the
routing model. */ routing model. */
body.dashboard-shell { body.dashboard-shell {
@ -200,7 +200,7 @@ body.dashboard-shell {
opacity 200ms ease, opacity 200ms ease,
border-color 200ms ease; border-color 200ms ease;
} }
/* Topology indent ladder. See docs/web-ui.md::Topology tree (Indent /* Topology indent ladder. See docs/web-ui/dashboard.md::Topology tree (Indent
+ lane geometry paragraph) for the 1.8em-per-depth-level + lane geometry paragraph) for the 1.8em-per-depth-level
rationale + CSS-attr()-not-yet-portable caveat. */ rationale + CSS-attr()-not-yet-portable caveat. */
.container-row[data-depth] { .container-row[data-depth] {

View file

@ -294,7 +294,7 @@
for per-target / whole-row cancel. Live updates via for per-target / whole-row cancel. Live updates via
`schedules_changed` SSE; tab activation re-fetches as a `schedules_changed` SSE; tab activation re-fetches as a
safety net for disconnect windows. safety net for disconnect windows.
See docs/web-ui.md::SCH3DUL3S tab. --> See docs/web-ui/dashboard.md::SCH3DUL3S tab. -->
<section <section
class="tab-pane" class="tab-pane"
id="tab-pane-schedules" id="tab-pane-schedules"
@ -354,7 +354,7 @@
<!-- Selection action bar. Sticky-bottom strip that slides into <!-- Selection action bar. Sticky-bottom strip that slides into
view when one or more agent cards is selected (click the icon view when one or more agent cards is selected (click the icon
to toggle). See docs/web-ui.md::Selection bar for the bulk to toggle). See docs/web-ui/dashboard.md::Selection bar for the bulk
action gating + clear semantics. --> action gating + clear semantics. -->
<div <div
id="selection-bar" id="selection-bar"

View file

@ -1,5 +1,5 @@
/* /flow.html full-page chat /* /flow.html full-page chat
See docs/web-ui.md::FL0W page for the page-vs-pane rationale. See docs/web-ui/dashboard.md::FL0W page for the page-vs-pane rationale.
Shape mirrors the per-agent live page (frosted-glass header + Shape mirrors the per-agent live page (frosted-glass header +
composer, full-viewport terminal). */ composer, full-viewport terminal). */
@ -68,7 +68,7 @@ body.flow-shell .flow-main-slim {
/* Tail pill ( N new): bottom offset clears the floating composer. /* Tail pill ( N new): bottom offset clears the floating composer.
Pill is anchored on .flow-main (not .terminal-wrap) so the Pill is anchored on .flow-main (not .terminal-wrap) so the
backdrop-filter stacking context doesn't trap its z-index see backdrop-filter stacking context doesn't trap its z-index see
docs/web-ui.md::Per-agent page (Terminal-wrap) for the same docs/web-ui/agent.md::Per-agent page (Terminal-wrap) for the same
gotcha on the agent page. */ gotcha on the agent page. */
.flow-main .tail-pill { .flow-main .tail-pill {
bottom: calc(var(--flow-composer-h) + 0.6em); bottom: calc(var(--flow-composer-h) + 0.6em);

View file

@ -218,7 +218,7 @@ import { epochSec } from "./util.js";
return true; return true;
} }
// Anchor the `↓ N new` pill in `.flow-main` rather than the // Anchor the `↓ N new` pill in `.flow-main` rather than the
// default `.terminal-wrap` parent — see docs/web-ui.md::Per-agent // default `.terminal-wrap` parent — see docs/web-ui/agent.md::Per-agent
// page (Terminal-wrap) for the backdrop-filter stacking-context // page (Terminal-wrap) for the backdrop-filter stacking-context
// gotcha (same shape on the flow page). // gotcha (same shape on the flow page).
const flowMain = document.querySelector(".flow-main"); const flowMain = document.querySelector(".flow-main");
@ -236,7 +236,7 @@ import { epochSec } from "./util.js";
// since it routes every mutation kind into its derived stores. // since it routes every mutation kind into its derived stores.
streamUrl: streamUrl:
"/api/dashboard/stream?kinds=sent,delivered,container_state_changed,container_removed", "/api/dashboard/stream?kinds=sent,delivered,container_state_changed,container_removed",
// Route through the SharedWorker — see docs/web-ui.md (SSE // Route through the SharedWorker — see docs/web-ui/shape.md (SSE
// multiplexing paragraph). Worker keys on the full URL incl. // multiplexing paragraph). Worker keys on the full URL incl.
// query string, so this filtered subscribe is its own upstream // query string, so this filtered subscribe is its own upstream
// and won't accidentally share with tabs.js's wider subscribe. // and won't accidentally share with tabs.js's wider subscribe.

View file

@ -370,7 +370,7 @@ function renderSchedulesList() {
} }
// Always-visible inline create row at the bottom of the table // Always-visible inline create row at the bottom of the table
// — fill cells + click to POST. See // — fill cells + click to POST. See
// docs/web-ui.md::SCH3DUL3S tab for the layout rationale. // docs/web-ui/dashboard.md::SCH3DUL3S tab for the layout rationale.
tbody.append(renderInlineCreateRow(agents)); tbody.append(renderInlineCreateRow(agents));
table.append(tbody); table.append(tbody);
const wrap = el("div", { class: "schedules-table-wrap" }); const wrap = el("div", { class: "schedules-table-wrap" });

View file

@ -44,7 +44,7 @@ const containerRowCache = new Map();
const transientsState = new Map(); const transientsState = new Map();
// In-memory set of selected agent logical names backing the sticky // In-memory set of selected agent logical names backing the sticky
// #selection-bar. See docs/web-ui.md::Selection bar for the // #selection-bar. See docs/web-ui/dashboard.md::Selection bar for the
// interaction model (icon-click toggle, Esc/clear button drop, // interaction model (icon-click toggle, Esc/clear button drop,
// tab-gated visibility). // tab-gated visibility).
const selectionState = new Set(); const selectionState = new Set();
@ -227,7 +227,7 @@ function derivePortConflicts(containers) {
} }
// ─── topology tree ─────────────────────────────────────────────────────────── // ─── topology tree ───────────────────────────────────────────────────────────
// See docs/web-ui.md::Topology tree for the rendering contract // See docs/web-ui/dashboard.md::Topology tree for the rendering contract
// (forest walk, alphabetical sort, orphan + cycle handling). // (forest walk, alphabetical sort, orphan + cycle handling).
function buildAgentTree(containers) { function buildAgentTree(containers) {
@ -277,7 +277,7 @@ function buildAgentTree(containers) {
return out; return out;
} }
// Builds the .tree-prefix DOM for a row at the given depth. // Builds the .tree-prefix DOM for a row at the given depth.
// See docs/web-ui.md::Topology tree for why this is DOM-painted // See docs/web-ui/dashboard.md::Topology tree for why this is DOM-painted
// (one positioned <span> per lane) rather than text-glyph-painted. // (one positioned <span> per lane) rather than text-glyph-painted.
function treePrefixDom({ depth, ancestorIsLast, isLast }) { function treePrefixDom({ depth, ancestorIsLast, isLast }) {
if (depth === 0) return null; if (depth === 0) return null;
@ -650,7 +650,7 @@ function buildContainerLi(c, node, opts) {
// Per-card action buttons (R3ST4RT / ST0P / ST4RT / R3BU1LD / // Per-card action buttons (R3ST4RT / ST0P / ST4RT / R3BU1LD /
// DESTR0Y / PURG3) live on the selection bar, not here — see // DESTR0Y / PURG3) live on the selection bar, not here — see
// docs/web-ui.md::Selection bar. The contextual `needs update ↻` // docs/web-ui/dashboard.md::Selection bar. The contextual `needs update ↻`
// chip in the head row stays — it's a state-hint, not an // chip in the head row stays — it's a state-hint, not an
// action button. // action button.
@ -830,7 +830,7 @@ export function renderContainers(s) {
// ─── selection bar ─────────────────────────────────────────────────────────── // ─── selection bar ───────────────────────────────────────────────────────────
// Sticky-bottom strip; visible when >=1 agent selected on the SW4RM // Sticky-bottom strip; visible when >=1 agent selected on the SW4RM
// tab. See docs/web-ui.md::Selection bar for the interaction model // tab. See docs/web-ui/dashboard.md::Selection bar for the interaction model
// and the per-action availability rules (disabled-with-tooltip for // and the per-action availability rules (disabled-with-tooltip for
// actions that don't apply to the full selection). Actions POST per // actions that don't apply to the full selection). Actions POST per
// agent in a loop (endpoints are individually idempotent / // agent in a loop (endpoints are individually idempotent /

View file

@ -2,7 +2,7 @@
// + 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
// `openStream` from common.js). See docs/web-ui.md::Shape (shared by // `openStream` from common.js). See docs/web-ui/shape.md::Shape (shared by
// both) for the broader contract. // both) for the broader contract.
// //
// Pure helpers (DOM, side panel, OS notifications, path linkification) // Pure helpers (DOM, side panel, OS notifications, path linkification)

View file

@ -31,7 +31,7 @@ import { el } from "../dom.js";
import { attachShadowCss } from "../shadow-css.js"; import { attachShadowCss } from "../shadow-css.js";
import sidePanelCss from "./hive-side-panel.css"; import sidePanelCss from "./hive-side-panel.css";
// See docs/web-ui.md::Side panel for the hit-strip + pointer-capture + // See docs/web-ui/agent.md::Side panel for the hit-strip + pointer-capture +
// localStorage persistence model; CSS clamps the stored value to min // localStorage persistence model; CSS clamps the stored value to min
// 320px / max 96vw and out-of-range stored values are dropped silently. // 320px / max 96vw and out-of-range stored values are dropped silently.
// Shared by both packages deliberately — see module header. // Shared by both packages deliberately — see module header.

View file

@ -12,7 +12,7 @@ const NEAR_BOTTOM_PX = 48;
// "load older" fetch — fires via the scroll event handler so the operator // "load older" fetch — fires via the scroll event handler so the operator
// never has to click the pill; the pill stays as a visual indicator. // never has to click the pill; the pill stays as a visual indicator.
const LOAD_MORE_SCROLL_PX = 80; const LOAD_MORE_SCROLL_PX = 80;
// Snap-to-bottom animation duration. See docs/web-ui.md::Shared // Snap-to-bottom animation duration. See docs/web-ui/shape.md::Shared
// terminal pane (Sticky-bottom + snap animation) for the 140ms-vs- // terminal pane (Sticky-bottom + snap animation) for the 140ms-vs-
// 500ms-browser-default + 24px short-circuit rationale. // 500ms-browser-default + 24px short-circuit rationale.
const SCROLL_ANIM_MS = 140; const SCROLL_ANIM_MS = 140;
@ -128,7 +128,7 @@ export function create(opts) {
// immediately so the MutationObserver stops re-firing snapToBottom() // immediately so the MutationObserver stops re-firing snapToBottom()
// and the animation-guard window can expire naturally. Without this, // and the animation-guard window can expire naturally. Without this,
// live events arriving < 220ms apart permanently block scroll-to-top // live events arriving < 220ms apart permanently block scroll-to-top
// and loadMore() never fires. See docs/web-ui.md::Shared terminal // and loadMore() never fires. See docs/web-ui/shape.md::Shared terminal
// pane (Mid-animation scroll-event guard). // pane (Mid-animation scroll-event guard).
const inAnim = Date.now() < smoothScrollingUntil; const inAnim = Date.now() < smoothScrollingUntil;
const nearBottom = isNearBottom(); const nearBottom = isNearBottom();
@ -571,7 +571,7 @@ export function create(opts) {
} }
// Build a DocumentFragment from `text`, turning bare http(s) URLs into // Build a DocumentFragment from `text`, turning bare http(s) URLs into
// clickable links that open in a new tab. See docs/web-ui.md::Shared // clickable links that open in a new tab. See docs/web-ui/shape.md::Shared
// terminal pane (linkify) for the text-node-only / no-innerHTML // terminal pane (linkify) for the text-node-only / no-innerHTML
// XSS-safety + trailing-punctuation strip. // XSS-safety + trailing-punctuation strip.
const LINKIFY_URL_RE = /https?:\/\/[^\s<>"']+/g; const LINKIFY_URL_RE = /https?:\/\/[^\s<>"']+/g;