dashboard: preserve <details open> across refresh via data-restore-key
generalises the focus-preservation pattern to expanded details sections (journald viewer was collapsing on every 5s refresh; same issue for approval diff blocks). before re-render we snapshot which <details data-restore-key=...> are open; after render we re-apply. setting .open = true programmatically also fires the toggle event, so journald's lazy-fetch listener re-runs cleanly. tagged: journal:<container>, approval-diff:<id>. anything else that should survive a refresh just needs a stable data-restore-key attribute.
This commit is contained in:
parent
fd0e493bf5
commit
b374f39b0d
1 changed files with 37 additions and 2 deletions
|
|
@ -300,7 +300,10 @@
|
|||
// operator expands; refresh re-fetches; unit toggle switches
|
||||
// between the harness service and the full machine journal.
|
||||
function buildJournalDetails(containerName, defaultUnit) {
|
||||
const details = el('details', { class: 'journal' });
|
||||
const details = el('details', {
|
||||
class: 'journal',
|
||||
'data-restore-key': 'journal:' + containerName,
|
||||
});
|
||||
const summary = el('summary', {}, '↳ logs · ' + containerName);
|
||||
const body = el('div', { class: 'journal-body' });
|
||||
const controls = el('div', { class: 'journal-controls' });
|
||||
|
|
@ -576,7 +579,9 @@
|
|||
);
|
||||
li.append(row);
|
||||
if (a.diff_html) {
|
||||
const details = el('details');
|
||||
const details = el('details', {
|
||||
'data-restore-key': 'approval-diff:' + a.id,
|
||||
});
|
||||
details.append(el('summary', {}, 'diff vs applied'));
|
||||
// diff_html is pre-rendered server-side (per-line class spans inside
|
||||
// a <pre>); inject as innerHTML.
|
||||
|
|
@ -601,6 +606,34 @@
|
|||
'inbox-section',
|
||||
'approvals-section',
|
||||
];
|
||||
// <details> sections that should survive a refresh need a stable
|
||||
// `data-restore-key` attribute. snapshotOpenDetails walks managed
|
||||
// sections and records which keys are currently open; restoreOpenDetails
|
||||
// re-applies after the render. The `toggle` event fires on
|
||||
// programmatic open changes too, so any onload-fetch wired up via
|
||||
// a toggle listener (e.g. journald) re-fires cleanly.
|
||||
function snapshotOpenDetails() {
|
||||
const open = new Set();
|
||||
for (const id of MANAGED_SECTION_IDS) {
|
||||
const sect = document.getElementById(id);
|
||||
if (!sect) continue;
|
||||
for (const d of sect.querySelectorAll('details[data-restore-key]')) {
|
||||
if (d.open) open.add(d.dataset.restoreKey);
|
||||
}
|
||||
}
|
||||
return open;
|
||||
}
|
||||
function restoreOpenDetails(open) {
|
||||
if (!open.size) return;
|
||||
for (const id of MANAGED_SECTION_IDS) {
|
||||
const sect = document.getElementById(id);
|
||||
if (!sect) continue;
|
||||
for (const d of sect.querySelectorAll('details[data-restore-key]')) {
|
||||
if (open.has(d.dataset.restoreKey)) d.open = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function operatorIsTyping() {
|
||||
const el_ = document.activeElement;
|
||||
if (!el_ || el_ === document.body) return false;
|
||||
|
|
@ -624,11 +657,13 @@
|
|||
const resp = await fetch('/api/state');
|
||||
if (!resp.ok) throw new Error('http ' + resp.status);
|
||||
const s = await resp.json();
|
||||
const openDetails = snapshotOpenDetails();
|
||||
renderContainers(s);
|
||||
renderTombstones(s);
|
||||
renderQuestions(s);
|
||||
renderInbox(s);
|
||||
renderApprovals(s);
|
||||
restoreOpenDetails(openDetails);
|
||||
notifyDeltas(s);
|
||||
// Auto-refresh: fast (2s) while a spawn or a per-container
|
||||
// action is in flight, otherwise heartbeat (5s) so newly-queued
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue