Y3R C4LL is the operator inbox alone: no P3NDING APPR0VALS section, no approval cards or history, no approval_resolved subscription, no approval browser notifications, and the tab count and title prefix count unread operator messages only. The H0M3 tile text drops "approvals". The deny form was the only data-prompt form, so bindAsyncForms' prompt branch, themedPrompt and its dialog styles go too, along with the approval-only CSS (.approval*, .approvals-history, .btn-deny, the status/glyph colours and .history-note).
69 lines
2.6 KiB
JavaScript
69 lines
2.6 KiB
JavaScript
// Shared dashboard render + format helpers.
|
|
//
|
|
// Small pure utilities used across most dashboard tabs (the container
|
|
// tree, the rebuild queue, 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);
|
|
}
|
|
|
|
// Epoch seconds from an API timestamp. The hive-c0re dashboard API
|
|
// ships timestamps as RFC 3339 strings; a few feeds (rebuild queue,
|
|
// meta inputs, turn stats) still carry unix-second numbers, so
|
|
// numbers pass through unchanged.
|
|
export function epochSec(ts) {
|
|
return typeof ts === "number" ? ts : Math.floor(Date.parse(ts) / 1000);
|
|
}
|
|
|
|
// Relative age of a timestamp (RFC 3339 string or unix seconds),
|
|
// coarsened to one unit ("5m ago").
|
|
export function fmtAgo(ts) {
|
|
const ageSec = Math.max(0, Math.floor(Date.now() / 1000 - epochSec(ts)));
|
|
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"
|
|
);
|
|
}
|