Backend (nvcurve/): - hal/fans.py, hal/limits.py, hal/gpu.py: replace conditional pynvml imports with the established 'pynvml: Any = _pynvml_import' pattern (fixes ~50 'possibly unbound' errors); type the result dicts; guard query_interface() results; explicit uuid/pci-bus parsing (int, hex convention documented); modernize Optional[T] -> T | None - cli.py: fix 'curve_state' possibly-unbound and snap_path None handling in cmd_setup; wrap unchecked int()/open()/makedirs() calls in try/except with clean CLI errors; add module logger for silent except-pass blocks; raise ... from exc; fix unused loop vars and set-comprehension - hal/snapshot.py: filepath: str | None; wrap all file ops; sorted imports; remove unused CT_POINTS import - daemon.py: extract 0o666 to _SOCKET_MODE constant (intentional for /run sockets) with nosemgrep - server.py: nosemgrep for Python 3.7-compat false positive (project requires >= 3.12); log previously-swallowed exception - profiles/native.py, profiles/apply.py: wrap file ops and int(k) profile-key parsing; sorted imports; modernize typing Frontend (frontend/src): - Add .js extensions to all relative imports (standard TS-ESM; Vite resolves .js -> .ts) - React.FormEvent (deprecated in React 19 types) -> React.SubmitEvent - catch (e: any) -> catch (e: unknown) + instanceof Error narrowing - React-hooks: move ref writes from render into effects; convert viewport reset to render-phase state adjustment; split selectPoint(index, multi) into selectPoint + togglePoint (no flag argument); remove non-null assertion - Static inline styles -> Tailwind classes (dynamic positioning/cursor styles kept) - Remove non-standard 'container' option from scrollIntoView (browsers ignore unknown options) which had orphaned a @ts-expect-error - Object.fromEntries for Map -> Record conversion Tooling: - .gitignore: ignore .codegraph/ local tool data Verified: tsc --noEmit, vite production build, python imports, and full LSP scan (0 errors/warnings in both projects).
48 lines
1.2 KiB
TypeScript
48 lines
1.2 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { api } from "../api/client.js";
|
|
import { useCurveStore } from "../store/curveStore.js";
|
|
import type { DashboardInfo } from "../types.js";
|
|
|
|
interface DashboardState {
|
|
gpuIndex: number;
|
|
data: DashboardInfo | null;
|
|
done: boolean;
|
|
}
|
|
|
|
/**
|
|
* Fetch static GPU info for the Dashboard tab.
|
|
* Re-fetches when the selected GPU changes.
|
|
*/
|
|
export function useDashboard() {
|
|
const { selectedGpuIndex } = useCurveStore();
|
|
const [state, setState] = useState<DashboardState>({
|
|
gpuIndex: -1,
|
|
data: null,
|
|
done: false,
|
|
});
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
(async () => {
|
|
try {
|
|
const data = await api.dashboard(selectedGpuIndex);
|
|
if (!cancelled)
|
|
setState({ gpuIndex: selectedGpuIndex, data, done: true });
|
|
} catch (err) {
|
|
console.error("Failed to load dashboard info:", err);
|
|
if (!cancelled)
|
|
setState({ gpuIndex: selectedGpuIndex, data: null, done: true });
|
|
}
|
|
})();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [selectedGpuIndex]);
|
|
|
|
const isCurrent = state.gpuIndex === selectedGpuIndex;
|
|
return {
|
|
dashboard: isCurrent ? state.data : null,
|
|
loading: !isCurrent || !state.done,
|
|
};
|
|
}
|