From 59d44933cfc7d3e65305e7a64955093475478a07 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Mon, 8 Jun 2026 15:51:05 +0200 Subject: [PATCH] refactor(beszel): centralize shared constants and formatters Move alertColorMap and containerColors to _shared/colors.ts, extract statusOptions to _shared/options.ts, and rename formatBytes to formatByteRate for clarity since it formats byte rates not raw bytes. --- packages/widgets/src/beszel/_shared/colors.ts | 11 ++++ packages/widgets/src/beszel/_shared/format.ts | 53 ++++++++++++++++--- .../widgets/src/beszel/_shared/options.ts | 6 +++ 3 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 packages/widgets/src/beszel/_shared/options.ts diff --git a/packages/widgets/src/beszel/_shared/colors.ts b/packages/widgets/src/beszel/_shared/colors.ts index ab2ed79ff..b2394602d 100644 --- a/packages/widgets/src/beszel/_shared/colors.ts +++ b/packages/widgets/src/beszel/_shared/colors.ts @@ -19,3 +19,14 @@ export const statusColorMap: Record = { paused: "gray", pending: "yellow", }; + +export const alertColorMap: Record = { + triggered: "red", + ok: "green", +}; + +export const containerColors = [ + "blue.6", "teal.6", "orange.6", "grape.6", "cyan.6", + "pink.6", "lime.6", "violet.6", "indigo.6", "yellow.6", + "red.6", "green.6", "blue.4", "teal.4", "orange.4", +]; diff --git a/packages/widgets/src/beszel/_shared/format.ts b/packages/widgets/src/beszel/_shared/format.ts index 61c09cb8f..04df347de 100644 --- a/packages/widgets/src/beszel/_shared/format.ts +++ b/packages/widgets/src/beszel/_shared/format.ts @@ -1,11 +1,52 @@ -export const formatBytes = (bytes: number): string => { - if (bytes === 0) return "0 B/s"; - const units = ["B/s", "KB/s", "MB/s", "GB/s"]; - const i = Math.floor(Math.log(bytes) / Math.log(1024)); - const value = bytes / Math.pow(1024, i); - return `${value.toFixed(value < 10 ? 2 : 1)} ${units[i]}`; +const byteUnits = ["B", "KB", "MB", "GB", "TB"] as const; +const rateUnits = ["B/s", "KB/s", "MB/s", "GB/s"] as const; + +const formatScaled = (value: number, units: readonly string[], zeroLabel: string): string => { + if (!value || !Number.isFinite(value)) return zeroLabel; + const i = Math.max(0, Math.min(Math.floor(Math.log(Math.abs(value)) / Math.log(1024)), units.length - 1)); + const scaled = value / 1024 ** i; + const decimals = scaled < 10 ? 2 : 1; + return `${scaled.toFixed(decimals)} ${units[i]!}`; }; +const formatScaledCompact = (value: number, units: readonly string[], zeroLabel: string): string => { + if (!value || !Number.isFinite(value)) return zeroLabel; + const i = Math.max(0, Math.min(Math.floor(Math.log(Math.abs(value)) / Math.log(1024)), units.length - 1)); + const scaled = value / 1024 ** i; + return `${scaled < 10 ? scaled.toFixed(1) : Math.round(scaled)}${units[i]!}`; +}; + +export const formatByteRate = (value: number): string => formatScaled(value, rateUnits, "0 B/s"); + +export const formatStorageBytes = (bytes: number): string => formatScaled(bytes, byteUnits, "0 B"); + +export const formatGB = (value: number): string => { + if (!value || !Number.isFinite(value)) return "0 GB"; + return `${value.toFixed(2)} GB`; +}; + +export const formatMB = (value: number): string => { + if (!value || !Number.isFinite(value)) return "0 MB"; + return `${value.toFixed(1)} MB`; +}; + +export const chartAxisFormatters = { + percent: (value: number) => `${Number(value).toFixed(1)}%`, + percentPrecise: (value: number) => `${Number(value).toFixed(1)}%`, + gb: (value: number) => (!value ? "0" : `${Number(value).toFixed(0)}G`), + mb: (value: number) => (!value ? "0" : `${Number(value).toFixed(0)}M`), + bytes: (value: number) => formatScaledCompact(Number(value), byteUnits, "0"), + rate: (value: number) => formatScaledCompact(Number(value), rateUnits, "0"), +} as const; + +export const getBandwidthRates = (stats: { b?: [number, number] }): { sent: number; recv: number } => ({ + sent: stats.b?.[0] ?? 0, + recv: stats.b?.[1] ?? 0, +}); + +export const getContainerNetworkRate = (container: { b?: [number, number] }): number => + (container.b?.[0] ?? 0) + (container.b?.[1] ?? 0); + export const formatUptime = (seconds: number): string => { const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); diff --git a/packages/widgets/src/beszel/_shared/options.ts b/packages/widgets/src/beszel/_shared/options.ts new file mode 100644 index 000000000..3bb2c9ca1 --- /dev/null +++ b/packages/widgets/src/beszel/_shared/options.ts @@ -0,0 +1,6 @@ +export const statusOptions = [ + { value: "all", label: "All Systems" }, + { value: "up", label: "Up" }, + { value: "down", label: "Down" }, + { value: "paused", label: "Paused" }, +];