SPB Git

Repository Public

format.ts 3.0 KB 69 lines typescript

1/** Formatting helpers. Null/undefined always renders as an em dash: the UI never invents values. */2export const DASH = '—';34export function fmtBytes(n: number | null | undefined): string {5  if (n == null || !Number.isFinite(n)) return DASH;6  if (n < 1024) return `${n} B`;7  const units = ['KiB', 'MiB', 'GiB', 'TiB'];8  let v = n / 1024;9  let i = 0;10  while (v >= 1024 && i < units.length - 1) {11    v /= 1024;12    i++;13  }14  return `${v >= 100 ? v.toFixed(0) : v.toFixed(1)} ${units[i]}`;15}16export function fmtNum(n: number | null | undefined, digits = 0, unit = ''): string {17  if (n == null || !Number.isFinite(n)) return DASH;18  return `${n.toLocaleString('en-CA', { minimumFractionDigits: digits, maximumFractionDigits: digits })}${unit}`;19}20export function fmtMbAsGb(mb: number | null | undefined, digits = 0): string {21  return mb == null ? DASH : `${(mb / 1024).toFixed(digits)} GB`;22}23export function fmtPct(n: number | null | undefined, digits = 0): string {24  return n == null || !Number.isFinite(n) ? DASH : `${n.toFixed(digits)} %`;25}26export function fmtTime(iso: string | number | null | undefined): string {27  if (iso == null) return DASH;28  const d = new Date(iso);29  return Number.isNaN(d.getTime()) ? DASH : d.toLocaleTimeString('en-CA', { hour12: false });30}31export function fmtDateTime(iso: string | number | null | undefined): string {32  if (iso == null) return DASH;33  const d = new Date(iso);34  if (Number.isNaN(d.getTime())) return DASH;35  return `${d.toLocaleDateString('en-CA')} ${d.toLocaleTimeString('en-CA', { hour12: false })}`;36}37export function fmtRelative(iso: string | number | null | undefined, nowMs = Date.now()): string {38  if (iso == null) return DASH;39  const t = new Date(iso).getTime();40  if (Number.isNaN(t)) return DASH;41  const s = Math.round((nowMs - t) / 1000);42  if (s < 5) return 'just now';43  if (s < 60) return `${s} s ago`;44  if (s < 3600) return `${Math.floor(s / 60)} min ago`;45  if (s < 86400) return `${Math.floor(s / 3600)} h ago`;46  if (s < 86400 * 30) return `${Math.floor(s / 86400)} d ago`;47  return new Date(t).toLocaleDateString('en-CA');48}49export function fmtDuration(sec: number | null | undefined): string {50  if (sec == null || !Number.isFinite(sec) || sec < 0) return DASH;51  const s = Math.floor(sec);52  if (s < 60) return `${s} s`;53  if (s < 3600) return `${Math.floor(s / 60)} min ${String(s % 60).padStart(2, '0')} s`;54  if (s < 86400) return `${Math.floor(s / 3600)} h ${String(Math.floor((s % 3600) / 60)).padStart(2, '0')} min`;55  return `${Math.floor(s / 86400)} d ${Math.floor((s % 86400) / 3600)} h`;56}57export function basename(p: string): string {58  const i = p.lastIndexOf('/');59  return i < 0 ? p : p.slice(i + 1);60}61export function dirname(p: string): string {62  const i = p.lastIndexOf('/');63  return i < 0 ? '' : p.slice(0, i);64}65export function joinPath(dir: string, name: string): string {66  return dir ? `${dir}/${name}` : name;67}68export const isMac = (): boolean => typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);69
Files main
Menu

Theme