dashboard: guard renderers against missing-root on /flow.html (#399)
Mara hit:
TypeError: can't access property "innerHTML", root is null
renderContainers app.js:666
applyContainerStateChanged app.js:484
container_state_changed app.js:2306
…on /flow.html. The same bundled `app.js` runs on both /index.html
(dashboard, has the tab panes) and /flow.html (flow page, has only
the broker terminal). SSE events arrive on every page —
`container_state_changed` / `tombstones_changed` / `approval_*` /
`question_*` route through their corresponding renderers, which
then `root.innerHTML = ''` on `$('section-id')` and crash when the
section isn't in the DOM.
The convention is already "no-op when the target DOM doesn't
exist" — `renderMetaInputs`, `renderRebuildQueue`, `renderReminders`
all guard with `if (!root) return;` at the top. Bring the rest in
line:
- `renderContainers`
- `renderTombstones`
- `renderQuestions`
- `renderApprovals`
`renderInbox` already handles the absent case via its
`if (root && !root.hidden)` branch — no change.
No behaviour change on /index.html. On /flow.html the failing
events silently no-op as intended (terminal renderers still
re-render the broker tail normally).
This commit is contained in:
parent
92becdd951
commit
4559a56e2e
1 changed files with 23 additions and 0 deletions
|
|
@ -704,6 +704,15 @@ window.marked = marked;
|
||||||
|
|
||||||
function renderContainers(s) {
|
function renderContainers(s) {
|
||||||
const root = $('containers-section');
|
const root = $('containers-section');
|
||||||
|
// #containers-section only exists on /index.html. The same
|
||||||
|
// bundled app.js runs on /flow.html (which has the broker
|
||||||
|
// terminal but no container list) and on any future pages —
|
||||||
|
// `container_state_changed` SSE events arrive on every page and
|
||||||
|
// route through `applyContainerStateChanged → renderContainersFromState`,
|
||||||
|
// so without this guard the renderer throws `root is null` on
|
||||||
|
// every event (#399). Matches the no-op-when-target-absent
|
||||||
|
// convention the other renderers (renderTombstones, etc.) follow.
|
||||||
|
if (!root) return;
|
||||||
root.innerHTML = '';
|
root.innerHTML = '';
|
||||||
|
|
||||||
// Containers come from the derived map (event-driven) rather than
|
// Containers come from the derived map (event-driven) rather than
|
||||||
|
|
@ -1042,6 +1051,10 @@ window.marked = marked;
|
||||||
|
|
||||||
function renderTombstones(s) {
|
function renderTombstones(s) {
|
||||||
const root = $('tombstones-section');
|
const root = $('tombstones-section');
|
||||||
|
// #tombstones-section only lives on /index.html (SYST3M tab);
|
||||||
|
// no-op on /flow.html and any other page that loads the shared
|
||||||
|
// bundle without the dashboard's tab panes (#399).
|
||||||
|
if (!root) return;
|
||||||
root.innerHTML = '';
|
root.innerHTML = '';
|
||||||
if (!s.tombstones || !s.tombstones.length) {
|
if (!s.tombstones || !s.tombstones.length) {
|
||||||
root.append(el('p', { class: 'empty' }, 'no kept state — clean'));
|
root.append(el('p', { class: 'empty' }, 'no kept state — clean'));
|
||||||
|
|
@ -1176,6 +1189,11 @@ window.marked = marked;
|
||||||
}
|
}
|
||||||
function renderQuestions() {
|
function renderQuestions() {
|
||||||
const root = $('questions-section');
|
const root = $('questions-section');
|
||||||
|
// #questions-section only lives on /index.html (Y3R C4LL tab);
|
||||||
|
// no-op on /flow.html etc. — the bundled app.js runs everywhere
|
||||||
|
// and `question_added` / `question_resolved` SSE events route
|
||||||
|
// through here (#399).
|
||||||
|
if (!root) return;
|
||||||
root.innerHTML = '';
|
root.innerHTML = '';
|
||||||
const fmt = (n) => new Date(n * 1000).toISOString().replace('T', ' ').slice(0, 19);
|
const fmt = (n) => new Date(n * 1000).toISOString().replace('T', ' ').slice(0, 19);
|
||||||
const allPending = questionsState.pending;
|
const allPending = questionsState.pending;
|
||||||
|
|
@ -1563,6 +1581,11 @@ window.marked = marked;
|
||||||
|
|
||||||
function renderApprovals() {
|
function renderApprovals() {
|
||||||
const root = $('approvals-section');
|
const root = $('approvals-section');
|
||||||
|
// #approvals-section only lives on /index.html (Y3R C4LL tab);
|
||||||
|
// no-op elsewhere — `approval_added` / `approval_resolved` SSE
|
||||||
|
// events route through here on every page that loads the bundle
|
||||||
|
// (#399).
|
||||||
|
if (!root) return;
|
||||||
root.innerHTML = '';
|
root.innerHTML = '';
|
||||||
|
|
||||||
// Spawn request form: submitting it queues a Spawn approval that
|
// Spawn request form: submitting it queues a Spawn approval that
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue