HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1/**2 * Formatting helpers. API timestamps are UTC ISO strings; dates render in UTC. All helpers accept `Num`3 * (Postgres aggregates can arrive as strings) and return the em-dash for missing values — never a fake number.4 */5import type { Num } from './types';67const nf0 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 });8const nf1 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 1 });9const nf2 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 });10const nfCompact = new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 });1112export const DASH = '—';1314/** Coerce `Num`/unknown to a finite number or null. */15export function num(v: unknown): number | null {16 if (v === null || v === undefined || v === '') return null;17 const n = typeof v === 'number' ? v : Number(v);18 return Number.isFinite(n) ? n : null;19}2021export function fmtInt(v: Num | undefined | unknown): string {22 const n = num(v);23 return n === null ? DASH : nf0.format(n);24}25export function fmt1(v: Num | undefined | unknown): string {26 const n = num(v);27 return n === null ? DASH : nf1.format(n);28}29export function fmt2(v: Num | undefined | unknown): string {30 const n = num(v);31 return n === null ? DASH : nf2.format(n);32}33export function fmtCompact(v: Num | undefined | unknown): string {34 const n = num(v);35 return n === null ? DASH : nfCompact.format(n);36}3738/** Parameter counts: 7e9 → "7B", 1.8e12 → "1.8T", 350e6 → "350M". */39export function fmtParams(v: Num | undefined | unknown): string {40 const n = num(v);41 if (n === null) return DASH;42 if (n >= 1e12) return `${trim(n / 1e12)}T`;43 if (n >= 1e9) return `${trim(n / 1e9)}B`;44 if (n >= 1e6) return `${trim(n / 1e6)}M`;45 if (n >= 1e3) return `${trim(n / 1e3)}K`;46 return nf0.format(n);47}48/** Token windows: 128000 → "128K", 1000000 → "1M", 200000 → "200K". */49export function fmtTokens(v: Num | undefined | unknown): string {50 const n = num(v);51 if (n === null) return DASH;52 if (n >= 1e6) return `${trim(n / 1e6)}M`;53 if (n >= 1e3) return `${trim(n / 1e3)}K`;54 return nf0.format(n);55}56function trim(x: number): string {57 const r = Math.round(x * 100) / 100;58 return r % 1 === 0 ? String(r) : String(Number(r.toFixed(r < 10 ? 2 : 1)));59}6061/** USD per 1M tokens: 3 → "$3.00", 0.075 → "$0.075", 15 → "$15.00". */62export function fmtUsdPerM(v: Num | undefined | unknown, withUnit = false): string {63 const n = num(v);64 if (n === null) return DASH;65 let s: string;66 if (n === 0) s = '$0';67 else if (n < 0.01) s = `$${n.toFixed(4).replace(/0+$/, '')}`;68 else if (n < 1) s = `$${n.toFixed(3).replace(/0$/, '')}`;69 else s = `$${nf2.format(n)}`;70 return withUnit ? `${s} / 1M` : s;71}72export function fmtUsd(v: Num | undefined | unknown): string {73 const n = num(v);74 return n === null ? DASH : `$${nf0.format(n)}`;75}76export function fmtPct(v: Num | undefined | unknown, digits = 1): string {77 const n = num(v);78 return n === null ? DASH : `${n.toFixed(digits)}%`;79}80export function fmtGb(v: Num | undefined | unknown, digits = 0): string {81 const n = num(v);82 return n === null ? DASH : `${digits ? n.toFixed(digits) : nf0.format(n)} GB`;83}84export function fmtBytes(v: Num | undefined | unknown): string {85 const n = num(v);86 if (n === null) return DASH;87 if (n >= 1e12) return `${(n / 1e12).toFixed(2)} TB`;88 if (n >= 1e9) return `${(n / 1e9).toFixed(1)} GB`;89 if (n >= 1e6) return `${(n / 1e6).toFixed(0)} MB`;90 if (n >= 1e3) return `${(n / 1e3).toFixed(0)} kB`;91 return `${n} B`;92}93export function fmtScore(v: Num | undefined | unknown): string {94 const n = num(v);95 if (n === null) return DASH;96 return Number.isInteger(n) ? nf0.format(n) : nf2.format(n);97}9899/** ISO date (YYYY-MM-DD, YYYY-MM or full timestamp) → "11 Sept 2026" / "Sept 2026" / "2026" (UTC). */100export function fmtDate(v: string | null | undefined): string {101 if (!v) return DASH;102 if (/^\d{4}$/.test(v)) return v;103 if (/^\d{4}-\d{2}$/.test(v)) return fmtMonth(v);104 const d = new Date(v.length === 10 ? `${v}T00:00:00Z` : v);105 if (Number.isNaN(d.getTime())) return v;106 return d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' });107}108export function fmtMonth(v: string | null | undefined): string {109 if (!v) return DASH;110 const m = /^(\d{4})-(\d{2})/.exec(v);111 if (!m) return fmtDate(v);112 const d = new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, 1));113 return d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', timeZone: 'UTC' });114}115export function fmtDateTime(v: string | null | undefined): string {116 if (!v) return DASH;117 const d = new Date(v);118 if (Number.isNaN(d.getTime())) return DASH;119 return `${d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' })} ${d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', timeZone: 'UTC' })} UTC`;120}121export function fmtAgo(v: string | null | undefined, now: number = Date.now()): string {122 if (!v) return 'unavailable';123 const t = new Date(v).getTime();124 if (Number.isNaN(t)) return 'unavailable';125 const s = Math.max(0, Math.round((now - t) / 1000));126 if (s < 60) return 'just now';127 const m = Math.round(s / 60);128 if (m < 60) return `${m} min ago`;129 const h = Math.round(m / 60);130 if (h < 48) return `${h} h ago`;131 const d = Math.round(h / 24);132 if (d < 60) return `${d} d ago`;133 return fmtDate(v);134}135export function fmtYear(v: string | null | undefined): string {136 return v && /^\d{4}/.test(v) ? v.slice(0, 4) : DASH;137}138export function fmtDuration(seconds: Num | undefined | unknown): string {139 const n = num(seconds);140 if (n === null) return DASH;141 if (n < 60) return `${n}s`;142 if (n < 3600) return `${Math.round(n / 60)} min`;143 if (n < 86400) return `${Math.round(n / 3600)} h`;144 return `${Math.round(n / 86400)} d`;145}146147/** Signed delta: +3.2% / −1.8% (relative change between two values). Null when either side is missing or the base is 0. */148export function fmtDeltaPct(from: Num | undefined | unknown, to: Num | undefined | unknown, digits = 1): string | null {149 const a = num(from);150 const b = num(to);151 if (a === null || b === null || a === 0) return null;152 const d = ((b - a) / Math.abs(a)) * 100;153 const sign = d > 0 ? '+' : d < 0 ? '−' : '';154 return `${sign}${Math.abs(d).toFixed(digits)}%`;155}156/** Signed integer: +12 / −3 / 0. */157export function fmtSigned(v: Num | undefined | unknown): string {158 const n = num(v);159 if (n === null) return DASH;160 return n > 0 ? `+${nf0.format(n)}` : n < 0 ? `−${nf0.format(Math.abs(n))}` : '0';161}162/** Host of a URL without `www.` — for compact source labels. */163export function hostOf(url: string | null | undefined): string | null {164 if (!url) return null;165 try {166 return new URL(url).hostname.replace(/^www\./, '');167 } catch {168 return null;169 }170}171172export function titleCase(s: string | null | undefined): string {173 if (!s) return DASH;174 return s.replace(/[_-]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());175}176/** Event type / predicate constant → words: NEW_MODEL → "New model", fine_tuned_from → "fine-tuned from". */177export function humanize(s: string | null | undefined): string {178 if (!s) return DASH;179 const w = s.toLowerCase().replace(/_/g, ' ');180 return w.charAt(0).toUpperCase() + w.slice(1);181}182export function plural(n: number, one: string, many = `${one}s`): string {183 return n === 1 ? one : many;184}185186/** Render any attribute value for a spec table (never JSON-dumps a primitive). */187export function fmtValue(v: unknown, key?: string): string {188 if (v === null || v === undefined || v === '') return DASH;189 if (typeof v === 'boolean') return v ? 'Yes' : 'No';190 if (Array.isArray(v)) return v.length ? v.map((x) => (typeof x === 'object' && x ? JSON.stringify(x) : String(x))).join(', ') : DASH;191 if (typeof v === 'number') {192 if (key && /parameter_count/.test(key)) return fmtParams(v);193 if (key && /(context_length|max_output_tokens)/.test(key)) return `${fmtTokens(v)} tokens`;194 if (key && /memory_gb|file_size_gb/.test(key)) return fmtGb(v, v % 1 ? 1 : 0);195 if (key && /bandwidth_gbs/.test(key)) return `${fmtInt(v)} GB/s`;196 if (key && /tdp_watts/.test(key)) return `${fmtInt(v)} W`;197 if (key && /tflops/.test(key)) return `${fmt1(v)} TFLOPS`;198 if (key && /price_usd/.test(key)) return fmtUsd(v);199 return Number.isInteger(v) ? nf0.format(v) : nf2.format(v);200 }201 if (typeof v === 'string') {202 if (key && /(_date|_at|cutoff)$/.test(key) && /^\d{4}(-\d{2})?(-\d{2})?/.test(v)) return fmtDate(v);203 return v;204 }205 return JSON.stringify(v);206}207