feat: add Dashboard tab with live GPU overview

Add a new Dashboard tab as the default/first tab (Dashboard - Curve -
Performance - Fans) showing a full GPU overview.

Backend:
- New hal/dashboard.py: one-shot static GPU info via NVML (VBIOS, CUDA
  cores, compute capability, bus width, BAR1/CPU-accessible VRAM,
  Resizable BAR, PCIe max, max clocks, power limits, temp thresholds,
  persistence mode, fan count, serial, board part number, UUID).
  ROP count and VRAM type are best-effort from a per-model table since
  NVML does not expose them.
- New /api/dashboard endpoint.
- MonitoringSample gains live throttle_reasons (+label), PCIe link
  width/generation (downclocks when idle, so read per poll), and VRAM
  temp (from the MEMORY thermal sensor, if exposed).

Frontend:
- New Dashboard component: critical top row (throttling, voltage,
  GPU/VRAM temps), full live-monitor grid with sparklines, and a static
  GPU-information grid. Unavailable fields are omitted.
- useDashboard hook + DashboardInfo type + api.client dashboard().

Also: restrict CORS to localhost origins (end-anchored), make
_int_key_deltas fail closed on bad keys, and clean up lint blockers in
server.py/monitoring.py.
This commit is contained in:
ARIA committed 2026-09-02 16:56:47 +02:00
1 parent 3c55263250
commit da507d55bb
11 files changed
+907 -72

No files matched your search

+20 -4
View File
@@ -1,8 +1,10 @@
import { useGpu } from "./hooks/useGpu";
import { useCurve } from "./hooks/useCurve";
import { useMonitor } from "./hooks/useMonitor";
import { useDashboard } from "./hooks/useDashboard";
import { StatusBar } from "./components/Monitor/StatusBar";
import { LiveMonitor } from "./components/Monitor/LiveMonitor";
import { Dashboard } from "./components/Dashboard/Dashboard";
import { CurveEditor } from "./components/CurveEditor/CurveEditor";
import { PointTable } from "./components/PointTable/PointTable";
import { PerformancePanel } from "./components/Limits/PerformancePanel";
@@ -89,12 +91,13 @@ function MainApp({
const gpuInfo = useGpu();
const { curve, wsStatus: curveWsStatus } = useCurve();
const { monitor, monitorHistory, wsStatus: monitorWsStatus } = useMonitor();
const { dashboard, loading: dashboardLoading } = useDashboard();
const { setCurve, activeProfile, setActiveProfile, selectedGpuIndex } =
useCurveStore();
const [activeTab, setActiveTab] = useState<"curve" | "performance" | "fans">(
"curve",
);
const [activeTab, setActiveTab] = useState<
"dashboard" | "curve" | "performance" | "fans"
>("dashboard");
const [fanState, setFanState] = useState<FanState | null>(null);
const [activeDomain, setActiveDomain] = useState<"gpu" | "memory">("gpu");
const [isProfileOpen, setIsProfileOpen] = useState(false);
@@ -168,6 +171,12 @@ function MainApp({
{/* Tab Header and Profile Selector */}
<div className="flex justify-between items-end border-b border-zinc-800 pb-2">
<div className="flex gap-6">
<button
onClick={() => setActiveTab("dashboard")}
className={`text-lg font-medium pb-2 -mb-[9px] border-b-2 transition-colors ${activeTab === "dashboard" ? "border-pink-500 text-zinc-100" : "border-transparent text-zinc-500 hover:text-zinc-300"}`}
>
Dashboard
</button>
<button
onClick={() => setActiveTab("curve")}
className={`text-lg font-medium pb-2 -mb-[9px] border-b-2 transition-colors ${activeTab === "curve" ? "border-pink-500 text-zinc-100" : "border-transparent text-zinc-500 hover:text-zinc-300"}`}
@@ -222,7 +231,14 @@ function MainApp({
{/* Main content area */}
<div className="flex flex-col gap-4 w-full">
{activeTab === "curve" ? (
{activeTab === "dashboard" ? (
<Dashboard
monitor={monitor}
history={monitorHistory}
dashboard={dashboard}
loading={dashboardLoading}
/>
) : activeTab === "curve" ? (
<>
<div className="flex gap-4 items-stretch w-full">
<div className="flex-1 min-w-0 flex flex-col">