export function formatDate(iso: string | null | undefined, opts?: { relative?: boolean }): string { if (!iso) return "—"; const d = new Date(iso); if (Number.isNaN(d.getTime())) return iso; if (opts?.relative) { const diffMs = d.getTime() - Date.now(); const absMs = Math.abs(diffMs); const sec = Math.round(absMs / 1000); const min = Math.round(sec / 60); const hr = Math.round(min / 60); const day = Math.round(hr / 24); const past = diffMs < 0; if (sec < 45) return past ? "just now" : "in a moment"; if (min < 60) return past ? `${min}m ago` : `in ${min}m`; if (hr < 24) return past ? `${hr}h ago` : `in ${hr}h`; if (day < 30) return past ? `${day}d ago` : `in ${day}d`; } return d.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" }); } export function formatBytes(bytes: number | null | undefined): string { if (bytes == null) return "—"; if (bytes < 1024) return `${bytes} B`; const units = ["KB", "MB", "GB", "TB"]; let value = bytes / 1024; let unitIdx = 0; while (value >= 1024 && unitIdx < units.length - 1) { value /= 1024; unitIdx++; } return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unitIdx]}`; } export function truncate(str: string, max: number): string { if (str.length <= max) return str; return str.slice(0, Math.max(0, max - 1)) + "…"; }