"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 && (
)}
{max != null && (
)}
{/* Per-core grid */}
{cores.length > 0 && (
Cores
{cores.map((c, i) => (
))}
)}
);
}
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" &&
}
);
}