"use client"; import { SETTINGS_PANEL_INSET } from "@/modules/settings/components/panel/surface"; import { useServerInfo } from "@/modules/system/hooks/useServerInfo"; import { useSystemMetrics } from "@/modules/system/hooks/useSystemMetrics"; import { cn } from "@/lib/utils"; import type { ServerHardwareInfo } from "@/lib/shared/contracts/server-info"; import type { SystemMetricsSnapshot } from "@/lib/shared/contracts/system"; import { useState } from "react"; function formatBytes(bytes: number | null): string { if (bytes === null) return "—"; if (bytes >= 1_073_741_824) return `${(bytes / 1_073_741_824).toFixed(1)} GB`; if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(0)} MB`; if (bytes >= 1_024) return `${(bytes / 1_024).toFixed(0)} KB`; return `${bytes} B`; } function Row({ label, value, mono = false }: { label: string; value: string | null | undefined; mono?: boolean }) { if (!value) return null; return (
{label} {value}
); } function Rows({ children }: { children: React.ReactNode }) { return
{children}
; } function OsTab({ info }: { info: ServerHardwareInfo }) { const { os, system } = info; return ( {system.manufacturer && system.model && ( )} ); } function CpuTab({ info }: { info: ServerHardwareInfo }) { const { cpu } = info; const name = [cpu.manufacturer, cpu.brand].filter(Boolean).join(" ") || null; const coreInfo = cpu.physicalCores && cpu.cores ? `${cpu.physicalCores} physical · ${cpu.cores} logical` : cpu.cores ? `${cpu.cores} logical` : null; const speedInfo = cpu.maxSpeedGhz ? `${cpu.speedGhz ?? "?"} GHz (max ${cpu.maxSpeedGhz} GHz)` : cpu.speedGhz ? `${cpu.speedGhz} GHz` : null; return ( {cpu.processors && cpu.processors > 1 && } ); } function MemoryTab({ info }: { info: ServerHardwareInfo }) { const { memory } = info; const filledSlots = memory.slots.filter((s) => s.sizeBytes && s.sizeBytes > 0); return (
{filledSlots.length > 0 && } {filledSlots.length > 0 && ( <>
Slots
{filledSlots.map((slot, i) => (
{slot.bank ?? `Slot ${i + 1}`}
{formatBytes(slot.sizeBytes)}
{slot.manufacturer &&
{slot.manufacturer}
}
{[slot.type, slot.formFactor, slot.clockSpeedMhz ? `${slot.clockSpeedMhz} MHz` : null].filter(Boolean).join(" · ")}
))}
)}
); } function GpuTab({ info }: { info: ServerHardwareInfo }) { const { controllers } = info.gpu; if (controllers.length === 0) { return
No GPU detected.
; } return (
{controllers.map((ctrl, i) => { const name = [ctrl.vendor, ctrl.model].filter(Boolean).join(" ") || `GPU ${i + 1}`; return (
{name}
{ctrl.bus && {ctrl.bus}} {ctrl.vramBytes && VRAM {formatBytes(ctrl.vramBytes)}} {ctrl.subVendor && {ctrl.subVendor}}
); })}
); } function ThermalTab({ metrics }: { metrics: SystemMetricsSnapshot | undefined }) { const temp = metrics?.temperature; if (!metrics) { return
Loading temperature data…
; } const main = temp?.mainCelsius; const max = temp?.maxCelsius; const cores = temp?.coresCelsius ?? []; if (main === null && cores.length === 0) { return
Temperature sensors not available on this host.
; } function tempColor(c: number) { if (c >= 85) return "text-status-red"; if (c >= 70) return "text-status-amber"; return "text-status-green"; } function tempBarColor(c: number) { if (c >= 85) return "bg-status-red"; if (c >= 70) return "bg-status-amber"; return "bg-status-green"; } return (
{/* Summary row */}
{main != null && (
CPU
{main}°C
)} {max != null && (
Max
{max}°C
)}
{/* Per-core grid */} {cores.length > 0 && (
Cores
{cores.map((c, i) => (
Core {i} {c}°C
))}
)}
); } function NetworkTab({ info }: { info: ServerHardwareInfo }) { const { interfaces } = info.network; if (interfaces.length === 0) { return
No network interfaces found.
; } return (
{interfaces.map((iface, i) => (
{/* Header row */}
{iface.iface} {iface.type && ( {iface.type} )} {iface.isDefault && ( default )}
{iface.operstate ?? "unknown"}
{/* Details */}
{iface.ip4 && (
IPv4 {iface.ip4}{iface.ip4subnet ? `/${iface.ip4subnet}` : ""}
)} {iface.ip6 && (
IPv6 {iface.ip6}
)} {iface.mac && (
MAC {iface.mac}
)}
{iface.speedMbps && ( {iface.speedMbps} Mbps )} {iface.duplex && ( {iface.duplex} duplex )} {iface.dhcp && ( DHCP )}
))}
); } function FirmwareTab({ info }: { info: ServerHardwareInfo }) { const { bios, baseboard } = info; return ( {baseboard.manufacturer && baseboard.model && ( )} ); } function BatteryTab({ info }: { info: ServerHardwareInfo }) { const { battery } = info; return ( ); } function SkeletonTab() { return (
{[80, 60, 90, 50].map((w, i) => (
))}
); } type TabId = "os" | "cpu" | "memory" | "gpu" | "thermal" | "network" | "battery" | "firmware"; export function ServerInfoSection() { const { data: info, isLoading, isError } = useServerInfo(); const { data: metrics } = useSystemMetrics(); const [activeTab, setActiveTab] = useState("os"); const tabs: { id: TabId; label: string; hidden?: boolean }[] = [ { id: "os", label: "OS" }, { id: "cpu", label: "CPU" }, { id: "memory", label: "Memory" }, { id: "gpu", label: "GPU" }, { id: "thermal", label: "Thermal" }, { id: "network", label: "Network" }, { id: "battery", label: "Battery", hidden: !info?.battery.hasBattery }, { id: "firmware", label: "Firmware", hidden: !info?.bios.vendor && !info?.baseboard.manufacturer }, ]; const visibleTabs = tabs.filter((t) => !t.hidden); const safeTab = visibleTabs.find((t) => t.id === activeTab) ? activeTab : (visibleTabs[0]?.id ?? "os"); return (
{/* Tab bar */}
{visibleTabs.map((tab) => ( ))} {info && ( {new Date(info.fetchedAt).toLocaleTimeString()} )}
{/* Tab content */}
{isLoading && } {isError && (
Failed to load hardware information.
)} {info && safeTab === "os" && } {info && safeTab === "cpu" && } {info && safeTab === "memory" && } {info && safeTab === "gpu" && } { safeTab === "thermal" && } {info && safeTab === "network" && } {info && safeTab === "battery" && } {info && safeTab === "firmware" && }
); }