Files
nvcurve/frontend/src/components/common/ConfirmDialog.tsx
T
ARIA c11ea73ead feat: GPU process list with per-process VRAM/utilization and kill
Add a GPU process table to the Performance tab showing which processes
use the selected GPU, with per-process VRAM, GPU utilization, CPU, host
memory, and command. Includes a kill endpoint (SIGTERM/SIGKILL, with
optional parent kill) behind a confirm dialog.

- nvcurve/hal/processes.py: NVML + /proc process enumeration
- server.py: GET /api/processes, POST /api/processes/kill (refuses to
  signal init/kthreadd)
- frontend: ProcessList component, API client, types
- tests: test_processes.py
- docs: API reference for the new endpoints
2026-10-03 20:25:33 +02:00

80 lines
2.4 KiB
TypeScript

export interface DialogAction {
label: string;
onClick: () => void;
className?: string;
disabled?: boolean;
}
interface Props {
message: string;
detail?: string;
confirmLabel?: string;
isDestructive?: boolean;
/** Required unless `actions` is provided (which replaces the single confirm button). */
onConfirm?: () => void;
onCancel: () => void;
/** When provided, rendered after Cancel instead of the single confirm button. */
actions?: DialogAction[];
}
export function ConfirmDialog({
message,
detail,
confirmLabel = "Confirm",
isDestructive = false,
onConfirm,
onCancel,
actions,
}: Props) {
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70"
onMouseDown={(e) => {
if (e.target === e.currentTarget) onCancel();
}}
>
<div className="bg-zinc-900 border border-zinc-700 rounded-xl shadow-2xl p-6 w-96 max-w-[90vw]">
<h2 className="text-zinc-100 font-semibold text-sm mb-1">{message}</h2>
{detail && <p className="text-zinc-400 text-xs mb-4">{detail}</p>}
{!detail && <div className="mb-4" />}
<div className="flex justify-end gap-2">
<button
onClick={onCancel}
className="px-3 py-1.5 rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs transition-colors"
>
Cancel
</button>
{actions ? (
actions.map((a) => (
<button
key={a.label}
onClick={a.onClick}
disabled={a.disabled}
className={[
a.className ??
"px-3 py-1.5 rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-200 text-xs font-semibold transition-colors",
a.disabled ? "opacity-40 cursor-not-allowed" : "",
].join(" ")}
>
{a.label}
</button>
))
) : (
<button
onClick={() => onConfirm?.()}
className={[
"px-3 py-1.5 rounded text-xs font-semibold transition-colors",
isDestructive
? "bg-red-600 hover:bg-red-500 text-white"
: "bg-emerald-600 hover:bg-emerald-500 text-white",
].join(" ")}
>
{confirmLabel}
</button>
)}
</div>
</div>
</div>
);
}