docs: scrub selection-bar #443/#596 cookies in tabs.js, add interaction-model preface (#712 batch 3)
selection + selection-bar in tabs.js carried six #443 cookies + two #596 cookies — most marking the bulk-bar architectural decision ('actions live in bar, not on per-card buttons; manager not special-cased') or the SW4RM-tab gate. All scrubbed; the canonical docs/web-ui.md::Selection bar section already covered the bulk actions in detail but was missing a preface for the **interaction model** itself (how selection enters/exits, why per-card buttons moved). Added that as the opening paragraph of the section so the in-code refs have something to point at. Net in this batch: - tabs.js: -8 #NNN refs (#443 ×6, #596 ×2) - docs/web-ui.md: +9 lines (interaction-model preface) - functional code unchanged; build clean refs #712
This commit is contained in:
parent
7647969d49
commit
a9422518e5
2 changed files with 34 additions and 27 deletions
|
|
@ -504,6 +504,15 @@ stops at the row's icon midline).
|
||||||
|
|
||||||
### Selection bar
|
### Selection bar
|
||||||
|
|
||||||
|
Per-card action buttons (`R3ST4RT` / `ST0P` / `ST4RT` / `R3BU1LD` /
|
||||||
|
`DESTR0Y` / `PURG3`) used to live on each container row; the
|
||||||
|
operator picked the bulk-bar model instead. Clicking an agent's
|
||||||
|
icon toggles its selection (an in-memory `Set<name>`); `Esc` or
|
||||||
|
the bar's `✕ clear` button drops everything. The selection
|
||||||
|
persists across tab switches in-memory — the bar just hides on
|
||||||
|
non-SW4RM tabs since other tabs don't show the agent cards needed
|
||||||
|
to cross-reference.
|
||||||
|
|
||||||
When one or more agents are selected (via icon click), a sticky
|
When one or more agents are selected (via icon click), a sticky
|
||||||
frosted-mauve bar slides up from the bottom of the viewport
|
frosted-mauve bar slides up from the bottom of the viewport
|
||||||
(`#selection-bar`, `position: fixed; bottom: 0`). It shows:
|
(`#selection-bar`, `position: fixed; bottom: 0`). It shows:
|
||||||
|
|
|
||||||
|
|
@ -287,11 +287,11 @@ window.marked = marked;
|
||||||
if (s) renderContainers(s);
|
if (s) renderContainers(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── selection (#443) ───────────────────────────────────────────────
|
// ─── selection ──────────────────────────────────────────────────────
|
||||||
// Set of selected agent logical names. Toggled by clicking the
|
// In-memory set of selected agent logical names backing the sticky
|
||||||
// container-row icon. When non-empty, the sticky #selection-bar
|
// #selection-bar. See docs/web-ui.md::Selection bar for the
|
||||||
// becomes visible with the bulk actions. Per-card action buttons
|
// interaction model (icon-click toggle, Esc/clear button drop,
|
||||||
// are gone — actions live in the bar.
|
// tab-gated visibility).
|
||||||
const selectionState = new Set();
|
const selectionState = new Set();
|
||||||
function toggleSelection(name) {
|
function toggleSelection(name) {
|
||||||
if (selectionState.has(name)) selectionState.delete(name);
|
if (selectionState.has(name)) selectionState.delete(name);
|
||||||
|
|
@ -534,7 +534,7 @@ window.marked = marked;
|
||||||
// hyperhive mark (`/favicon.svg`, served by the dashboard
|
// hyperhive mark (`/favicon.svg`, served by the dashboard
|
||||||
// itself, always reachable). (issues #195, #202)
|
// itself, always reachable). (issues #195, #202)
|
||||||
const iconImg = el('img', { class: 'container-icon-img', alt: '' });
|
const iconImg = el('img', { class: 'container-icon-img', alt: '' });
|
||||||
// #443: icon is the selection toggle. Click → add/remove from
|
// Icon is the selection toggle. Click → add/remove from
|
||||||
// `selectionState` → re-render. role=button + tabindex makes it
|
// `selectionState` → re-render. role=button + tabindex makes it
|
||||||
// keyboard-accessible; aria-pressed reflects the toggle state.
|
// keyboard-accessible; aria-pressed reflects the toggle state.
|
||||||
const icon = el('div', {
|
const icon = el('div', {
|
||||||
|
|
@ -698,10 +698,9 @@ window.marked = marked;
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-card action buttons used to live here (R3ST4RT / ST0P /
|
// Per-card action buttons (R3ST4RT / ST0P / ST4RT / R3BU1LD /
|
||||||
// ST4RT / R3BU1LD / DESTR0Y / PURG3). Per mara on #443: "dont
|
// DESTR0Y / PURG3) moved to the selection bar — see
|
||||||
// show all the restart buttons etc., just show state and links.
|
// docs/web-ui.md::Selection bar. Actions
|
||||||
// instead, clicking an agent icon selects that agent." Actions
|
|
||||||
// moved into the sticky #selection-bar (see renderSelectionBar)
|
// moved into the sticky #selection-bar (see renderSelectionBar)
|
||||||
// which appears when the operator has at least one agent
|
// which appears when the operator has at least one agent
|
||||||
// selected via the icon click. The contextual `needs update ↻`
|
// selected via the icon click. The contextual `needs update ↻`
|
||||||
|
|
@ -730,12 +729,13 @@ window.marked = marked;
|
||||||
renderSelectionBar(containers);
|
renderSelectionBar(containers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── selection bar (#443) ───────────────────────────────────────────
|
// ─── selection bar ──────────────────────────────────────────────────
|
||||||
// Sticky-bottom strip; visible when ≥1 agent selected. mara picked
|
// Sticky-bottom strip; visible when ≥1 agent selected on the SW4RM
|
||||||
// option B: show every action button, disable the ones that don't
|
// tab. See docs/web-ui.md::Selection bar for the interaction model
|
||||||
// apply to the full selection, hover tooltip explains why. Actions
|
// and the per-action availability rules (disabled-with-tooltip for
|
||||||
// POST per agent in a loop (no new backend wire — endpoints already
|
// actions that don't apply to the full selection). Actions POST per
|
||||||
// exist and are individually idempotent / event-covered).
|
// agent in a loop (endpoints are individually idempotent /
|
||||||
|
// event-covered, so no new bulk backend wire is needed).
|
||||||
function renderSelectionBar(containers) {
|
function renderSelectionBar(containers) {
|
||||||
const bar = $('selection-bar');
|
const bar = $('selection-bar');
|
||||||
if (!bar) return;
|
if (!bar) return;
|
||||||
|
|
@ -745,11 +745,9 @@ window.marked = marked;
|
||||||
if (!countSpan || !namesSpan || !actions) return;
|
if (!countSpan || !namesSpan || !actions) return;
|
||||||
|
|
||||||
const selected = containers.filter((c) => selectionState.has(c.name));
|
const selected = containers.filter((c) => selectionState.has(c.name));
|
||||||
// #596: the bar's actions only make sense on the SW4RM tab — that's
|
// Tab-gate: bar only renders on SW4RM (the only tab with agent
|
||||||
// where the agent cards are visible to cross-reference against the
|
// cards to cross-reference). Selection state lives in-memory and
|
||||||
// selection. On other tabs the operator just sees a floating bar
|
// the bar reappears on return to SW4RM if still non-empty.
|
||||||
// with no context, so hide it. Selection state persists in-memory
|
|
||||||
// and the bar reappears on return to SW4RM if still non-empty.
|
|
||||||
const onSwarmTab = (document.body.dataset.activeTab || 'swarm') === 'swarm';
|
const onSwarmTab = (document.body.dataset.activeTab || 'swarm') === 'swarm';
|
||||||
if (!selected.length || !onSwarmTab) {
|
if (!selected.length || !onSwarmTab) {
|
||||||
bar.hidden = true;
|
bar.hidden = true;
|
||||||
|
|
@ -785,8 +783,8 @@ window.marked = marked;
|
||||||
confirm: (names) => `restart ${names.length} agent${names.length === 1 ? '' : 's'} (${names.join(', ')})?`,
|
confirm: (names) => `restart ${names.length} agent${names.length === 1 ? '' : 's'} (${names.join(', ')})?`,
|
||||||
disabledTitle: why('↺ R3ST4RT', stoppedNames.map((n) => `\`${n}\` is stopped`)),
|
disabledTitle: why('↺ R3ST4RT', stoppedNames.map((n) => `\`${n}\` is stopped`)),
|
||||||
});
|
});
|
||||||
// #443 also lifts the manager-stop guard: when the whole selection
|
// ST0P does not special-case the manager: when the whole selection
|
||||||
// is running, ST0P applies — manager included. host-side hive-c0re
|
// is running it applies, manager included. host-side hive-c0re
|
||||||
// keeps serving the dashboard either way + per-agent approvals +
|
// keeps serving the dashboard either way + per-agent approvals +
|
||||||
// meta-input updates still work without the manager up, so we
|
// meta-input updates still work without the manager up, so we
|
||||||
// don't special-case the confirm prompt when the manager is in
|
// don't special-case the confirm prompt when the manager is in
|
||||||
|
|
@ -844,7 +842,7 @@ window.marked = marked;
|
||||||
// No client-side manager special-case (mara on #695): backend
|
// No client-side manager special-case (mara on #695): backend
|
||||||
// `topology::set_parent` refuses to move the manager and surfaces the
|
// `topology::set_parent` refuses to move the manager and surfaces the
|
||||||
// refusal as a per-agent failure in the bulk-action error roll-up. Same
|
// refusal as a per-agent failure in the bulk-action error roll-up. Same
|
||||||
// pattern as #443 ST0P (which also doesn't special-case manager).
|
// pattern as ST0P (which also doesn't special-case manager).
|
||||||
function addMoveActions(parent, selected, containers) {
|
function addMoveActions(parent, selected, containers) {
|
||||||
// M0V3 → ROOT: parent=null for every selected agent. Only meaningful
|
// M0V3 → ROOT: parent=null for every selected agent. Only meaningful
|
||||||
// when at least one selected agent currently has a non-null parent;
|
// when at least one selected agent currently has a non-null parent;
|
||||||
|
|
@ -3266,9 +3264,9 @@ window.marked = marked;
|
||||||
if (tab) tab.classList.toggle('active', t === target);
|
if (tab) tab.classList.toggle('active', t === target);
|
||||||
if (pane) pane.classList.toggle('tab-pane-active', t === target);
|
if (pane) pane.classList.toggle('tab-pane-active', t === target);
|
||||||
}
|
}
|
||||||
// #596: track active tab on the body so renderSelectionBar can
|
// Track active tab on the body so renderSelectionBar can gate
|
||||||
// gate visibility (bar only belongs on SW4RM where agent cards
|
// visibility (bar only belongs on SW4RM where agent cards live).
|
||||||
// live). Re-render the bar so the toggle takes effect immediately
|
// Re-render the bar so the toggle takes effect immediately
|
||||||
// on hashchange without waiting for the next SSE update.
|
// on hashchange without waiting for the next SSE update.
|
||||||
document.body.dataset.activeTab = target;
|
document.body.dataset.activeTab = target;
|
||||||
renderSelectionBar(Array.from(containersState.values()));
|
renderSelectionBar(Array.from(containersState.values()));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue