// Shared byte-size formatter for the Downloads surface. // // One formatter, used everywhere disk usage is shown (cards, detail pages, the // device total, and the remove-reclaim prompt) so units are consistent. We use // DECIMAL units (1 GB = 1000 MB), matching how phone storage screens and file // browsers present sizes, and show 2–3 significant figures. // // TRACES: UR-056 | DR-085 const UNITS = ["B", "KB", "MB", "GB", "TB", "PB"] as const; /** * Format a byte count as a human-readable size string (decimal units). * * Examples: 0 → "0 B", 340_000_000 → "340 MB", 1_200_000_000 → "1.2 GB". * * - Bytes render as whole numbers (no "0.5 B"). * - KB and above show enough decimals for 2–3 significant figures: values * ≥ 100 render with no decimals, ≥ 10 with one, otherwise two. * - Negative / non-finite inputs are treated as 0 (sizes are never negative). */ export function formatBytes(bytes: number): string { if (!Number.isFinite(bytes) || bytes <= 0) return "0 B"; let value = bytes; let unitIndex = 0; while (value >= 1000 && unitIndex < UNITS.length - 1) { value /= 1000; unitIndex += 1; } // Bytes are always whole; larger units get 2–3 significant figures. let formatted: string; if (unitIndex === 0) { formatted = Math.round(value).toString(); } else if (value >= 100) { formatted = Math.round(value).toString(); } else if (value >= 10) { formatted = value.toFixed(1); } else { formatted = value.toFixed(2); } // Trim trailing zeros ("1.20" → "1.2", "1.00" → "1") for a cleaner label. if (formatted.includes(".")) { formatted = formatted.replace(/\.?0+$/, ""); } return `${formatted} ${UNITS[unitIndex]}`; }