// Shared dashboard render + format helpers. // // Small pure utilities used across most dashboard tabs (the container // tree, the rebuild queue, questions / approvals, schedules + reminders): // an atomic-swap render helper and a handful of relative-time / duration // formatters. They live here — a dashboard-internal module — rather than // in the cross-page `common.js`, because they're specific to the // dashboard's render style and not needed by the stand-alone pages. // // All pure: no DOM/module state captured, so any tab module can import // them freely without ordering concerns. // Atomic-swap render: build into an off-DOM DocumentFragment, then commit // with a single `replaceChildren`. Avoids an intermediate empty-state // flash on poll cycles even when the builder allocates a lot of nodes. export function paintAtomic(liveRoot, build) { const buf = document.createDocumentFragment(); build(buf); liveRoot.replaceChildren(buf); } // Relative age of a unix timestamp, coarsened to one unit ("5m ago"). export function fmtAgo(unixSecs) { const ageSec = Math.max(0, Math.floor(Date.now() / 1000 - unixSecs)); if (ageSec < 60) return ageSec + 's ago'; if (ageSec < 3600) return Math.floor(ageSec / 60) + 'm ago'; if (ageSec < 86400) return Math.floor(ageSec / 3600) + 'h ago'; return Math.floor(ageSec / 86400) + 'd ago'; } // Truncate a string to `n` chars, appending an ellipsis when clipped. export function truncate(s, n) { return s.length <= n ? s : s.slice(0, n - 1) + '…'; } // Running-duration label for in-flight items ("3m 12s running"). export function fmtElapsed(secs) { if (secs < 60) return secs + 's running'; if (secs < 3600) return Math.floor(secs / 60) + 'm ' + (secs % 60) + 's running'; return Math.floor(secs / 3600) + 'h ' + Math.floor((secs % 3600) / 60) + 'm running'; } // Compact duration label, two units deep ("1h 5m", "2d 3h"). export function fmtDuration(secs) { if (secs < 60) return secs + 's'; if (secs < 3600) return Math.floor(secs / 60) + 'm ' + (secs % 60) + 's'; if (secs < 86400) return Math.floor(secs / 3600) + 'h ' + Math.floor((secs % 3600) / 60) + 'm'; return Math.floor(secs / 86400) + 'd ' + Math.floor((secs % 86400) / 3600) + 'h'; }