fix(dashboard): call.js question handlers referenced tabs.js-only renderContainersFromState

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.
This commit is contained in:
iris 2026-06-21 22:42:34 +02:00
commit eb1d913b47
2 changed files with 11 additions and 3 deletions

View file

@ -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)

View file

@ -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