durationLabel moves to util.ts, termPaths to agent/terminal/term-paths.ts, StateBadges and tokenTotal to agent/state, and TryOutResult to pages/review-common, so the builds, runs and agent pages no longer import across page folders. SectionHeading is the body-size section heading the create form and the egress card each styled; Preformatted is the scrolling text block of the builds tail and the AGENTS.md view, which now share one chrome and are both keyboard-scrollable. The components page shows both.
43 lines
2 KiB
TypeScript
43 lines
2 KiB
TypeScript
// Small pure render helpers shared across swarm-ui pages. Not folded
|
|
// into `@hive/shared` — swarm-ui's formatting choices aren't a
|
|
// cross-package contract, and there's nothing here worth sharing.
|
|
|
|
// Relative age, coarsened to one unit ("5m ago"). Built on the native
|
|
// `Intl.RelativeTimeFormat` (no dependency needed — mara asked whether
|
|
// a lightweight package was warranted; the runtime already ships this)
|
|
// with `style: 'narrow'`, which produces the same "5s/5m/5h/5d ago"
|
|
// shape the dashboard package's hand-rolled equivalent does, verified
|
|
// against a real `Intl` call rather than assumed from the spec.
|
|
const RTF = new Intl.RelativeTimeFormat("en", {
|
|
numeric: "always",
|
|
style: "narrow",
|
|
});
|
|
|
|
// The same coarsening for an instant still ahead ("in 5m"). A separate
|
|
// function rather than a negative `fmtAgo`: that one clamps to zero so a
|
|
// clock a little ahead of the server never reads as "-3s ago".
|
|
export function fmtUntil(secondsAhead: number): string {
|
|
const ahead = Math.max(0, Math.floor(secondsAhead));
|
|
if (ahead < 60) return RTF.format(ahead, "second");
|
|
if (ahead < 3600) return RTF.format(Math.floor(ahead / 60), "minute");
|
|
if (ahead < 86400) return RTF.format(Math.floor(ahead / 3600), "hour");
|
|
return RTF.format(Math.floor(ahead / 86400), "day");
|
|
}
|
|
|
|
export function fmtAgo(ageSeconds: number): string {
|
|
const age = Math.max(0, Math.floor(ageSeconds));
|
|
if (age < 60) return RTF.format(-age, "second");
|
|
if (age < 3600) return RTF.format(-Math.floor(age / 60), "minute");
|
|
if (age < 86400) return RTF.format(-Math.floor(age / 3600), "hour");
|
|
return RTF.format(-Math.floor(age / 86400), "day");
|
|
}
|
|
|
|
/** How long a run took, as `45s`, `3m 12s` or `1h 4m`. A clock that ran
|
|
* backwards reads as `0s`. */
|
|
export function durationLabel(startedUnix: number, finishedUnix: number) {
|
|
const total = Math.max(0, Math.round(finishedUnix - startedUnix));
|
|
if (total < 60) return `${total}s`;
|
|
const minutes = Math.floor(total / 60);
|
|
if (minutes < 60) return `${minutes}m ${total % 60}s`;
|
|
return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
|
|
}
|