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

@ -16,7 +16,7 @@
/* full-screen layout overrides
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
override the in-page defaults from @hive/shared. */
@ -175,7 +175,7 @@ h3 {
}
.agent-icon {
/* 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
rationale. */
width: 5em;
@ -702,7 +702,7 @@ pre.diff {
the composer (z-30) so the pill sits on the top layer instead of
being clipped by the floating chrome. (Pill is anchored in
`.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.) */
.agent-main .tail-pill {
bottom: calc(var(--agent-composer-h) + 0.6em);

View file

@ -61,7 +61,7 @@
});
// --- 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 +
// pointer-rescale model.
let fitMode = localStorage.getItem("screen-fit") !== "off";
@ -105,7 +105,7 @@
// Sends an RFB SetDesktopSize request so the VNC server (weston)
// changes its actual output resolution to match the browser
// 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).
let extDesktopSupported = false;
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));
// When the approval was requested — relative time, right-aligned.
// 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) {
const requestedSec = epochSec(a.requested_at);
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
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 {
color: var(--purple);
border-color: var(--purple);

View file

@ -68,7 +68,7 @@ export const form = (
// ─── shared-worker SSE pipe ─────────────────────────────────────────────
// Returns an EventSource-shaped facade backed by a SharedWorker that
// 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
// direct EventSource on environments without SharedWorker.
//
@ -105,7 +105,7 @@ function getSharedPort() {
// SharedWorker death detection: pings from the worker bump the
// activity clock; a visibility-gated watchdog polls and re-subscribes
// 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.
const WORKER_DEAD_THRESHOLD_MS = 90_000;
const WORKER_WATCHDOG_INTERVAL_MS = 15_000;

View file

@ -12,7 +12,7 @@
/* tabbed dashboard chrome
Top-of-page sticky header with banner + tab strip. SSE stays
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. */
body.dashboard-shell {
@ -200,7 +200,7 @@ body.dashboard-shell {
opacity 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
rationale + CSS-attr()-not-yet-portable caveat. */
.container-row[data-depth] {

View file

@ -294,7 +294,7 @@
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. -->
See docs/web-ui/dashboard.md::SCH3DUL3S tab. -->
<section
class="tab-pane"
id="tab-pane-schedules"
@ -354,7 +354,7 @@
<!-- 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
to toggle). See docs/web-ui/dashboard.md::Selection bar for the bulk
action gating + clear semantics. -->
<div
id="selection-bar"

View file

@ -1,5 +1,5 @@
/* /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 +
composer, full-viewport terminal). */
@ -68,7 +68,7 @@ body.flow-shell .flow-main-slim {
/* Tail pill ( N new): bottom offset clears the floating composer.
Pill is anchored on .flow-main (not .terminal-wrap) so the
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. */
.flow-main .tail-pill {
bottom: calc(var(--flow-composer-h) + 0.6em);

View file

@ -218,7 +218,7 @@ import { epochSec } from "./util.js";
return true;
}
// 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
// gotcha (same shape on the flow page).
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.
streamUrl:
"/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.
// query string, so this filtered subscribe is its own upstream
// 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
// — 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));
table.append(tbody);
const wrap = el("div", { class: "schedules-table-wrap" });

View file

@ -44,7 +44,7 @@ const containerRowCache = new Map();
const transientsState = new Map();
// 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,
// tab-gated visibility).
const selectionState = new Set();
@ -227,7 +227,7 @@ function derivePortConflicts(containers) {
}
// ─── 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).
function buildAgentTree(containers) {
@ -277,7 +277,7 @@ function buildAgentTree(containers) {
return out;
}
// 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.
function treePrefixDom({ depth, ancestorIsLast, isLast }) {
if (depth === 0) return null;
@ -650,7 +650,7 @@ function buildContainerLi(c, node, opts) {
// Per-card action buttons (R3ST4RT / ST0P / ST4RT / R3BU1LD /
// 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
// action button.
@ -830,7 +830,7 @@ export function renderContainers(s) {
// ─── selection bar ───────────────────────────────────────────────────────────
// 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
// actions that don't apply to the full selection). Actions POST per
// 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
// async-form submit; live updates run through `applyXxx` mutation
// 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.
//
// 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 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
// 320px / max 96vw and out-of-range stored values are dropped silently.
// 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
// never has to click the pill; the pill stays as a visual indicator.
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-
// 500ms-browser-default + 24px short-circuit rationale.
const SCROLL_ANIM_MS = 140;
@ -128,7 +128,7 @@ export function create(opts) {
// immediately so the MutationObserver stops re-firing snapToBottom()
// and the animation-guard window can expire naturally. Without this,
// 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).
const inAnim = Date.now() < smoothScrollingUntil;
const nearBottom = isNearBottom();
@ -571,7 +571,7 @@ export function create(opts) {
}
// 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
// XSS-safety + trailing-punctuation strip.
const LINKIFY_URL_RE = /https?:\/\/[^\s<>"']+/g;