Files
jellytau/src/lib/utils/formatBytes.ts
T
dtourolle f25deba824 feat(downloads): browsable downloaded library with on-disk usage
Replace the flat download list with a Downloaded browse surface that
reuses the online grids/cards/detail pages, filtered to on-device media,
plus a demoted Transfers tab. Add repository browse commands
(getDownloadedLibraries/Items, disk usage) with offline/hybrid
implementations, a downloadedCatalog service, formatBytes helper, and
per-item/device disk-usage labels on cards and grids. Regenerated
bindings.

Also carries the inseparable UR-052 offline-filter hunks in
offline.rs/hybrid.rs.

TRACES: UR-055 | DR-081, DR-082, DR-083, DR-084; UR-056 | DR-085
2026-07-23 20:02:55 +02:00

51 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 23 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 23 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 23 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]}`;
}