diff --git a/studio/frontend/src/features/settings/tabs/agents-tab.tsx b/studio/frontend/src/features/settings/tabs/agents-tab.tsx index 63dc31faf8..d810048a86 100644 --- a/studio/frontend/src/features/settings/tabs/agents-tab.tsx +++ b/studio/frontend/src/features/settings/tabs/agents-tab.tsx @@ -1094,12 +1094,13 @@ export function AgentsTab() { data-settings-label={t("settings.agents.intro")} className="text-sm text-muted-foreground leading-relaxed" > - {/* The chip is the docs entry point, so no separate link is needed. */} + {/* The chip is the docs entry point, so no separate link is needed. + No aria-label: it would replace the visible "unsloth start" as the + accessible name, leaving voice control unable to target it. */} diff --git a/studio/frontend/src/features/settings/tabs/resources-tab.tsx b/studio/frontend/src/features/settings/tabs/resources-tab.tsx index 496432c7a4..8ac51f3e38 100644 --- a/studio/frontend/src/features/settings/tabs/resources-tab.tsx +++ b/studio/frontend/src/features/settings/tabs/resources-tab.tsx @@ -456,92 +456,99 @@ 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 ( - // One row per device: a full-width bar under a single GPU read - // as a page-wide rule, so the bar is sized to the row instead. -
-
-
- {device.name ?? t("settings.resources.gpu.unknownDevice")} + {/* Sized against this pane, not the viewport: the Settings dialog caps + the tab at roughly 650px however wide the window is. */} +
+ {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 ( + // One row per device: a full-width bar under a single GPU read + // as a page-wide rule, so the bar is sized to the row instead. +
+
+
+ {device.name ?? t("settings.resources.gpu.unknownDevice")} +
+
+ {ordinal === undefined + ? backendLabel + : `${t("settings.resources.gpu.deviceWithIndex", { + index: ordinal, + })}, ${backendLabel}`} +
-
- {ordinal === undefined - ? backendLabel - : `${t("settings.resources.gpu.deviceWithIndex", { - index: ordinal, - })}, ${backendLabel}`} -
-
- {/* Narrow screens stack instead of hiding: the used, free and - total figures stay reachable on tablets and small laptops. */} -
-
- - {t("settings.resources.gpu.used", { value: usedText })} - - - {t("settings.resources.gpu.free", { value: freeText })} - - - {t("settings.resources.gpu.total", { value: totalText })} - -
-
- {/* Fills the space left by the percentage rather than + {/* Stacked until the pane is genuinely wide enough: the + figures, meter and percentage need about 50rem before the + name has room left to read. */} +
+
+ + {t("settings.resources.gpu.used", { value: usedText })} + + + {t("settings.resources.gpu.free", { value: freeText })} + + + {t("settings.resources.gpu.total", { + value: totalText, + })} + +
+
+ {/* Fills the space left by the percentage rather than claiming the full row, which overflowed once stacked. */} - -
- {percentText}{" "} - {t("settings.resources.gpu.vramUtilization")} + +
+ {percentText}{" "} + {t("settings.resources.gpu.vramUtilization")} +
-
- ); - }) - ) : ( -
- {t("settings.resources.gpu.noGpu")} -
- )} + ); + }) + ) : ( +
+ {t("settings.resources.gpu.noGpu")} +
+ )} +
diff --git a/studio/frontend/src/i18n/locales/en.ts b/studio/frontend/src/i18n/locales/en.ts index 2b5bf7f508..642b224d2a 100644 --- a/studio/frontend/src/i18n/locales/en.ts +++ b/studio/frontend/src/i18n/locales/en.ts @@ -843,8 +843,7 @@ export const en = { codingAgents: "Coding agents", codingAgentsHint: "Launch a coding agent against this server. It uses the loaded model; a local server mints an API key automatically, a remote one includes it in the command.", - codingAgentsSwap: - "Swap claude for codex, openclaw, opencode, hermes, or pi.", + codingAgentsSwap: "Swap claude for codex, openclaw, opencode, or hermes.", codingAgentDetected: "Installed on this machine", codingAgentsDetectedHint: "Detected on this machine: {agents}.", relativeNever: "never",