diff --git a/studio/frontend/src/features/settings/tabs/resources-tab.tsx b/studio/frontend/src/features/settings/tabs/resources-tab.tsx index 7725aaa0ad..6348d2066d 100644 --- a/studio/frontend/src/features/settings/tabs/resources-tab.tsx +++ b/studio/frontend/src/features/settings/tabs/resources-tab.tsx @@ -456,97 +456,91 @@ export function ResourcesTab() { )} -
- {hasGpu ? ( - metrics.devices.map((device, index) => { - const ordinal = deviceOrdinal(device); - // Preserve null (unknown, e.g. Windows ROCm perf counter); coercing - // to 0 would render a fabricated 0 used / full free. - const total = device.memory_total_gb ?? null; - const used = device.vram_used_gb ?? null; - const free = - device.vram_free_gb ?? - (isFiniteNumber(total) && isFiniteNumber(used) - ? Math.max(0, total - used) - : null); - const percent = - device.vram_utilization_pct ?? - (isFiniteNumber(total) && total > 0 && isFiniteNumber(used) - ? (used / total) * 100 - : null); - const safePercent = clampPercent(percent); - const usedText = isFiniteNumber(used) - ? formatGiB(used) - : unknownLabel; - const freeText = isFiniteNumber(free) - ? formatGiB(free) - : unknownLabel; - const totalText = isFiniteNumber(total) - ? formatGiB(total) - : unknownLabel; - const percentText = isFiniteNumber(percent) - ? formatPercent(safePercent) - : unknownLabel; - return ( - // Two compact lines per device. The settings pane is about - // 664px at any window size, which cannot hold the name, three - // figures, meter and percentage on one row, so no breakpoint - // would ever fire. Splitting them keeps the name readable and - // the meter narrow instead of running the width of the pane. -
-
-
-
- {device.name ?? - t("settings.resources.gpu.unknownDevice")} -
-
- {ordinal === undefined - ? backendLabel - : `${t("settings.resources.gpu.deviceWithIndex", { - index: ordinal, - })}, ${backendLabel}`} -
-
-
+ {hasGpu ? ( + metrics.devices.map((device, index) => { + const ordinal = deviceOrdinal(device); + // Preserve null (unknown, e.g. Windows ROCm perf counter); coercing + // to 0 would render a fabricated 0 used / full free. + const total = device.memory_total_gb ?? null; + const used = device.vram_used_gb ?? null; + const free = + device.vram_free_gb ?? + (isFiniteNumber(total) && isFiniteNumber(used) + ? Math.max(0, total - used) + : null); + const percent = + device.vram_utilization_pct ?? + (isFiniteNumber(total) && total > 0 && isFiniteNumber(used) + ? (used / total) * 100 + : null); + const safePercent = clampPercent(percent); + const usedText = isFiniteNumber(used) + ? formatGiB(used) + : unknownLabel; + const freeText = isFiniteNumber(free) + ? formatGiB(free) + : unknownLabel; + const totalText = isFiniteNumber(total) + ? formatGiB(total) + : unknownLabel; + const percentText = isFiniteNumber(percent) + ? formatPercent(safePercent) + : unknownLabel; + return ( + // Name over backend on the left, figures and meter beside them. + // The meter is deliberately short: at full width it read as a + // rule across the pane rather than a reading for one device. +
+
+
+ {device.name ?? t("settings.resources.gpu.unknownDevice")} +
+
+ + {ordinal === undefined + ? backendLabel + : `${t("settings.resources.gpu.deviceWithIndex", { + index: ordinal, + })}, ${backendLabel}`} + + {percentText}{" "} {t("settings.resources.gpu.vramUtilization")} -
-
- {/* Wraps rather than overflows once the pane gets narrow. */} -
-
- - {t("settings.resources.gpu.used", { value: usedText })} - - - {t("settings.resources.gpu.free", { value: freeText })} - - - {t("settings.resources.gpu.total", { - value: totalText, - })} - -
- +
- ); - }) - ) : ( -
- {t("settings.resources.gpu.noGpu")} -
- )} -
+
+
+ + {t("settings.resources.gpu.used", { value: usedText })} + + + {t("settings.resources.gpu.free", { value: freeText })} + + + {t("settings.resources.gpu.total", { + value: totalText, + })} + +
+ +
+
+ ); + }) + ) : ( +
+ {t("settings.resources.gpu.noGpu")} +
+ )}