diff --git a/hive-c0re/assets/app.js b/hive-c0re/assets/app.js index 2da6459..0a7a52b 100644 --- a/hive-c0re/assets/app.js +++ b/hive-c0re/assets/app.js @@ -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
); inject as innerHTML.
@@ -601,6 +606,34 @@
'inbox-section',
'approvals-section',
];
+ // 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