From eb1d913b478f77ccaf8ecf746e257c25836e29ac Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 21 Jun 2026 22:42:34 +0200 Subject: [PATCH] fix(dashboard): call.js question handlers referenced tabs.js-only renderContainersFromState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit applyQuestionAdded / applyQuestionResolved (call.js) called renderContainersFromState() to refresh the SW4RM rows' per-agent question-count badges, but that's a tabs.js closure-local not in call.js's scope — so the question_added / question_resolved SSE handlers threw 'ReferenceError: renderContainersFromState is not defined' and aborted. Inject it as an onContainersDirty callback via initCall (same pattern as onCountsChanged), wired by tabs.js to renderContainersFromState. Closes #1826. --- frontend/packages/dashboard/src/call.js | 12 ++++++++++-- frontend/packages/dashboard/src/tabs.js | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/packages/dashboard/src/call.js b/frontend/packages/dashboard/src/call.js index c0b478ff..83d205c2 100644 --- a/frontend/packages/dashboard/src/call.js +++ b/frontend/packages/dashboard/src/call.js @@ -22,9 +22,17 @@ import { questionsState, QUESTION_HISTORY_LIMIT } from './state.js'; // Registered by the dashboard entry at boot; defaults to a no-op so the // module is safe to call before wiring. let onCountsChanged = () => {}; +// Re-render the SW4RM container rows (their per-agent question-count badges +// read questionsState). `renderContainersFromState` is a tabs.js closure-local +// — not in this module's scope — so the entry injects it here via `initCall`, +// same pattern as `onCountsChanged`. Referencing it directly threw +// `ReferenceError: renderContainersFromState is not defined` and aborted the +// `question_added` / `question_resolved` SSE handlers. +let onContainersDirty = () => {}; export function initCall(opts = {}) { if (typeof opts.onCountsChanged === 'function') onCountsChanged = opts.onCountsChanged; + if (typeof opts.onContainersDirty === 'function') onContainersDirty = opts.onContainersDirty; } // ─── operator inbox — unread agent→operator messages ──────────── @@ -438,7 +446,7 @@ export function applyQuestionAdded(ev) { question_refs: ev.question_refs || [], }); renderQuestions(); - renderContainersFromState(); + onContainersDirty(); } export function applyQuestionResolved(ev) { const idx = questionsState.pending.findIndex((q) => q.id === ev.id); @@ -468,7 +476,7 @@ export function applyQuestionResolved(ev) { } } renderQuestions(); - renderContainersFromState(); + onContainersDirty(); } // Filter selection for the questions section. Persisted so the // operator's preferred view (all / operator-targeted / peer) diff --git a/frontend/packages/dashboard/src/tabs.js b/frontend/packages/dashboard/src/tabs.js index 0b3325cd..c35e036f 100644 --- a/frontend/packages/dashboard/src/tabs.js +++ b/frontend/packages/dashboard/src/tabs.js @@ -1592,7 +1592,7 @@ window.marked = marked; // Register the Y3R C4LL domain's count callback (call.js) — its live // mutations (inbox stream append, mark-read) trigger a tab-count refresh // through this instead of reaching back into the coordinator directly. - initCall({ onCountsChanged: refreshTabCounts }); + initCall({ onCountsChanged: refreshTabCounts, onContainersDirty: renderContainersFromState }); // Tab count pills — pure derived data from the existing state