chore: frontend cleanup — replaceChildren, missing source badge, http case

- Replace innerHTML = '' with replaceChildren() throughout tabs.js and
  app.js (12 + 7 sites). paintAtomic already used replaceChildren; now
  the direct-clear sites are consistent with it.
- Add missing .rqe-source-startup_sweep CSS rule (startup_sweep is a
  valid QueueSource variant but had no badge style, falling through to
  the base muted appearance with no explicit intent).
- Lowercase the one uppercase 'HTTP ' in the fetchAndRenderToolGroups
  error path to match every other fetch error in the file.
This commit is contained in:
iris 2026-06-01 22:48:21 +02:00
commit 2a62a561c4
3 changed files with 21 additions and 20 deletions

View file

@ -1096,6 +1096,7 @@ code {
.rqe-source-manual { color: var(--cyan); border-color: var(--cyan); }
.rqe-source-meta_update { color: var(--purple); border-color: var(--purple); }
.rqe-source-auto_update { color: var(--muted); }
.rqe-source-startup_sweep { color: var(--muted); }
.rqe-source-crash_recover { color: var(--amber); border-color: var(--amber); }
.rqe-source-approval { color: var(--green); border-color: var(--green); }
.rqe-when { color: var(--muted); font-size: 0.85em; }

View file

@ -551,7 +551,7 @@ window.marked = marked;
// no-op-when-target-absent convention the other renderers
// (renderTombstones, etc.) follow.
if (!root) return;
root.innerHTML = '';
root.replaceChildren();
// Containers come from the derived map (event-driven) rather than
// `s.containers`; `s` still supplies hostname (for the web-ui
@ -883,7 +883,7 @@ window.marked = marked;
// Recompute action availability + tooltips per render. Each
// action declares which agents it CAN'T run on; the bar disables
// the button and surfaces the offending names in the tooltip.
actions.innerHTML = '';
actions.replaceChildren();
const allRunning = selected.every((c) => c.running);
const allStopped = selected.every((c) => !c.running);
const noManagers = selected.every((c) => !c.is_manager);
@ -1114,7 +1114,7 @@ window.marked = marked;
// no-op on /flow.html and any other page that loads the shared
// bundle without the dashboard's tab panes.
if (!root) return;
root.innerHTML = '';
root.replaceChildren();
if (!s.tombstones || !s.tombstones.length) {
root.append(el('p', { class: 'empty' }, 'no kept state — clean'));
return;
@ -1181,21 +1181,21 @@ window.marked = marked;
async function fetchAndRenderToolGroups() {
const root = $('tool-groups-section');
if (!root) return;
root.innerHTML = '';
root.replaceChildren();
root.append(el('p', { class: 'meta' }, 'loading…'));
try {
const resp = await fetch('/api/tool-groups');
if (!resp.ok) throw new Error('HTTP ' + resp.status);
if (!resp.ok) throw new Error('http ' + resp.status);
const data = await resp.json();
renderToolGroups(root, data);
} catch (err) {
root.innerHTML = '';
root.replaceChildren();
root.append(el('p', { class: 'meta' }, 'fetch failed: ' + err));
}
}
function renderToolGroups(root, data) {
root.innerHTML = '';
root.replaceChildren();
const { groups, assignments } = data;
if (!groups || !groups.length) {
root.append(el('p', { class: 'meta' }, '(no tool groups defined)'));
@ -1380,7 +1380,7 @@ window.marked = marked;
// no-op when the section is missing. `question_added` /
// `question_resolved` SSE events route through here.
if (!root) return;
root.innerHTML = '';
root.replaceChildren();
const fmt = (n) => new Date(n * 1000).toISOString().replace('T', ' ').slice(0, 19);
const allPending = questionsState.pending;
const activeFilter = getQuestionsFilter();
@ -1712,7 +1712,7 @@ window.marked = marked;
function renderPeerHives(peers) {
const root = $('peers-section');
if (!root) return;
root.innerHTML = '';
root.replaceChildren();
if (!peers || !peers.length) {
root.append(el('p', { class: 'empty' }, 'no peer hives configured'));
return;
@ -1743,7 +1743,7 @@ window.marked = marked;
// no-op elsewhere — `approval_added` / `approval_resolved` SSE
// events route through here on every page that loads the bundle.
if (!root) return;
root.innerHTML = '';
root.replaceChildren();
// Spawn request form: submitting it queues a Spawn approval that
// lands in this same list, so the form belongs here rather than on
@ -1933,7 +1933,7 @@ window.marked = marked;
function renderMetaInputs(s) {
const root = $('meta-inputs-section');
if (!root) return;
root.innerHTML = '';
root.replaceChildren();
const inputs = s.meta_inputs || [];
if (!inputs.length) {
root.append(el('p', { class: 'empty' }, 'meta repo not seeded yet'));
@ -2054,7 +2054,7 @@ window.marked = marked;
function renderRebuildQueue(s) {
const root = $('rebuild-queue-section');
if (!root) return;
root.innerHTML = '';
root.replaceChildren();
const queue = s.rebuild_queue || [];
if (!queue.length) {
root.append(el('p', { class: 'empty' }, 'queue is empty — nothing pending or in flight.'));
@ -3578,7 +3578,7 @@ window.marked = marked;
// Step 4: rebuild the dropdown from the two overflow sets.
const overflowedTabs = [...dynamicOverflow, ...defaultOverflow];
overflowDrop.innerHTML = '';
overflowDrop.replaceChildren();
for (const tab of overflowedTabs) {
const li = document.createElement('li');
li.setAttribute('role', 'presentation');