mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-10 13:31:38 +00:00
The reusable base the Capabilities rework sits on:
- lib/{text,time,format,json-format}: consolidate ~30 hand-rolled string/date/
number/JSON helpers behind one set of tested utilities.
- master-detail scaffold (MasterDetail / ListColumn / DetailColumn / DetailPane /
CapRow / ListStrip / ToolChip / ICON_BUTTON) + row-hover; tabs-as-data
PageSearchShell; EmptyState + ErrorBanner; a shared LogTail terminal surface.
- framed CodeEditor + JsonDocumentEditor, wired into every in-app markdown/JSON
edit surface (profile SOUL.md, memory nodes, right-click sidebar profile).
- shared React Query cache helper (writeCache) + per-profile-switch/ debounce hooks.
74 lines
2.7 KiB
TypeScript
74 lines
2.7 KiB
TypeScript
// Canonical time/date formatting. Shared `Intl` instances (created once, not
|
|
// per-render) + relative-time helpers. Every surface that shows a timestamp or
|
|
// an age pulls from here so the rendered strings stay consistent app-wide.
|
|
|
|
export const SECOND = 1000
|
|
export const MINUTE = 60_000
|
|
export const HOUR = 3_600_000
|
|
export const DAY = 86_400_000
|
|
|
|
// ── Absolute date/time formatters ──────────────────────────────────────────
|
|
// `hh:mm` clock (thread today/yesterday lines).
|
|
export const fmtClock = new Intl.DateTimeFormat(undefined, { hour: 'numeric', minute: '2-digit' })
|
|
|
|
// Compact "day + clock", no year/seconds (artifacts, thread fallback, cron runs).
|
|
export const fmtDayTime = new Intl.DateTimeFormat(undefined, {
|
|
day: 'numeric',
|
|
hour: 'numeric',
|
|
minute: '2-digit',
|
|
month: 'short'
|
|
})
|
|
|
|
// Medium date + short time (command center session detail).
|
|
export const fmtDateTime = new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' })
|
|
|
|
// Date only, "5 Jun 2026" (starmap tooltip).
|
|
export const fmtDate = new Intl.DateTimeFormat(undefined, { day: 'numeric', month: 'short', year: 'numeric' })
|
|
|
|
// ── Relative time ──────────────────────────────────────────────────────────
|
|
const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto', style: 'short' })
|
|
|
|
// Localized bidirectional "in 5 min" / "2 hr ago" — coarsest sensible unit so a
|
|
// daily job reads "in 14 hr", not "in 840 min".
|
|
export function relativeTime(targetMs: number, nowMs = Date.now()): string {
|
|
const diff = targetMs - nowMs
|
|
const abs = Math.abs(diff)
|
|
const sign = diff < 0 ? -1 : 1
|
|
|
|
if (abs < MINUTE) {
|
|
return rtf.format(sign * Math.round(abs / SECOND), 'second')
|
|
}
|
|
|
|
if (abs < HOUR) {
|
|
return rtf.format(sign * Math.round(abs / MINUTE), 'minute')
|
|
}
|
|
|
|
if (abs < DAY) {
|
|
return rtf.format(sign * Math.round(abs / HOUR), 'hour')
|
|
}
|
|
|
|
return rtf.format(sign * Math.round(abs / DAY), 'day')
|
|
}
|
|
|
|
export type ElapsedUnit = 'day' | 'hour' | 'minute' | 'second'
|
|
|
|
// Coarsest elapsed bucket for a (clamped-nonnegative) duration, floored. The
|
|
// caller owns rendering — compact "5m", "5m ago", etc. — so no format is baked
|
|
// in here.
|
|
export function coarseElapsed(deltaMs: number): { unit: ElapsedUnit; value: number } {
|
|
const ms = Math.max(0, deltaMs)
|
|
|
|
if (ms >= DAY) {
|
|
return { unit: 'day', value: Math.floor(ms / DAY) }
|
|
}
|
|
|
|
if (ms >= HOUR) {
|
|
return { unit: 'hour', value: Math.floor(ms / HOUR) }
|
|
}
|
|
|
|
if (ms >= MINUTE) {
|
|
return { unit: 'minute', value: Math.floor(ms / MINUTE) }
|
|
}
|
|
|
|
return { unit: 'second', value: Math.floor(ms / SECOND) }
|
|
}
|