Update hardware.tsx

This commit is contained in:
MacRimi
2025-10-24 22:34:29 +02:00
parent 84a10afea1
commit ffd317aff0

View File

@@ -130,6 +130,29 @@ const getMonitoringToolRecommendation = (vendor: string): string => {
return "To get extended GPU monitoring information, please install the appropriate GPU monitoring tools for your hardware." return "To get extended GPU monitoring information, please install the appropriate GPU monitoring tools for your hardware."
} }
const improveSensorLabel = (sensorName: string, adapter: string): string => {
const adapterLower = adapter?.toLowerCase() || ""
// Detect NVIDIA sensors (nouveau or nvidia driver)
if (adapterLower.includes("nouveau") || adapterLower.includes("nvidia")) {
// Improve temperature labels
if (sensorName.toLowerCase().includes("temp")) {
return "NVIDIA GPU Temperature"
}
// Improve fan labels
if (sensorName.toLowerCase().includes("fan")) {
return "NVIDIA GPU Fan"
}
// Improve PWM labels
if (sensorName.toLowerCase().includes("pwm")) {
return "NVIDIA GPU PWM"
}
}
// Return original name if no improvement needed
return sensorName
}
const groupAndSortTemperatures = (temperatures: any[]) => { const groupAndSortTemperatures = (temperatures: any[]) => {
const groups = { const groups = {
CPU: [] as any[], CPU: [] as any[],
@@ -143,7 +166,9 @@ const groupAndSortTemperatures = (temperatures: any[]) => {
const nameLower = temp.name.toLowerCase() const nameLower = temp.name.toLowerCase()
const adapterLower = temp.adapter?.toLowerCase() || "" const adapterLower = temp.adapter?.toLowerCase() || ""
if (nameLower.includes("cpu") || nameLower.includes("core") || nameLower.includes("package")) { if (adapterLower.includes("nouveau") || adapterLower.includes("nvidia")) {
groups.GPU.push(temp)
} else if (nameLower.includes("cpu") || nameLower.includes("core") || nameLower.includes("package")) {
groups.CPU.push(temp) groups.CPU.push(temp)
} else if (nameLower.includes("gpu") || adapterLower.includes("gpu")) { } else if (nameLower.includes("gpu") || adapterLower.includes("gpu")) {
groups.GPU.push(temp) groups.GPU.push(temp)
@@ -429,23 +454,19 @@ export default function Hardware() {
</Badge> </Badge>
</div> </div>
{(() => {
const groupedTemps = groupAndSortTemperatures(hardwareData.temperatures)
return (
<div className="grid gap-6 md:grid-cols-2"> <div className="grid gap-6 md:grid-cols-2">
{/* CPU Sensors */} {/* CPU Sensors */}
{groupedTemps.CPU.length > 0 && ( {groupAndSortTemperatures(hardwareData.temperatures).CPU.length > 0 && (
<div className="md:col-span-2"> <div className="md:col-span-2">
<div className="mb-3 flex items-center gap-2"> <div className="mb-3 flex items-center gap-2">
<CpuIcon className="h-4 w-4 text-muted-foreground" /> <CpuIcon className="h-4 w-4 text-muted-foreground" />
<h3 className="text-sm font-semibold">CPU</h3> <h3 className="text-sm font-semibold">CPU</h3>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{groupedTemps.CPU.length} {groupAndSortTemperatures(hardwareData.temperatures).CPU.length}
</Badge> </Badge>
</div> </div>
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
{groupedTemps.CPU.map((temp, index) => { {groupAndSortTemperatures(hardwareData.temperatures).CPU.map((temp, index) => {
const percentage = const percentage =
temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100
const isHot = temp.current > (temp.high || 80) const isHot = temp.current > (temp.high || 80)
@@ -476,26 +497,32 @@ export default function Hardware() {
)} )}
{/* GPU Sensors */} {/* GPU Sensors */}
{groupedTemps.GPU.length > 0 && ( {groupAndSortTemperatures(hardwareData.temperatures).GPU.length > 0 && (
<div className={groupedTemps.GPU.length > 1 ? "md:col-span-2" : ""}> <div
className={groupAndSortTemperatures(hardwareData.temperatures).GPU.length > 1 ? "md:col-span-2" : ""}
>
<div className="mb-3 flex items-center gap-2"> <div className="mb-3 flex items-center gap-2">
<Gpu className="h-4 w-4 text-muted-foreground" /> <Gpu className="h-4 w-4 text-muted-foreground" />
<h3 className="text-sm font-semibold">GPU</h3> <h3 className="text-sm font-semibold">GPU</h3>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{groupedTemps.GPU.length} {groupAndSortTemperatures(hardwareData.temperatures).GPU.length}
</Badge> </Badge>
</div> </div>
<div className={`grid gap-4 ${groupedTemps.GPU.length > 1 ? "md:grid-cols-2" : ""}`}> <div
{groupedTemps.GPU.map((temp, index) => { className={`grid gap-4 ${groupAndSortTemperatures(hardwareData.temperatures).GPU.length > 1 ? "md:grid-cols-2" : ""}`}
>
{groupAndSortTemperatures(hardwareData.temperatures).GPU.map((temp, index) => {
const percentage = const percentage =
temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100
const isHot = temp.current > (temp.high || 80) const isHot = temp.current > (temp.high || 80)
const isCritical = temp.current > (temp.critical || 90) const isCritical = temp.current > (temp.critical || 90)
const displayName = improveSensorLabel(temp.name, temp.adapter)
return ( return (
<div key={index} className="space-y-2"> <div key={index} className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm font-medium">{temp.name}</span> <span className="text-sm font-medium">{displayName}</span>
<span <span
className={`text-sm font-semibold ${isCritical ? "text-red-500" : isHot ? "text-orange-500" : "text-green-500"}`} className={`text-sm font-semibold ${isCritical ? "text-red-500" : isHot ? "text-orange-500" : "text-green-500"}`}
> >
@@ -517,17 +544,21 @@ export default function Hardware() {
)} )}
{/* NVME Sensors */} {/* NVME Sensors */}
{groupedTemps.NVME.length > 0 && ( {groupAndSortTemperatures(hardwareData.temperatures).NVME.length > 0 && (
<div className={groupedTemps.NVME.length > 1 ? "md:col-span-2" : ""}> <div
className={groupAndSortTemperatures(hardwareData.temperatures).NVME.length > 1 ? "md:col-span-2" : ""}
>
<div className="mb-3 flex items-center gap-2"> <div className="mb-3 flex items-center gap-2">
<HardDrive className="h-4 w-4 text-muted-foreground" /> <HardDrive className="h-4 w-4 text-muted-foreground" />
<h3 className="text-sm font-semibold">NVME</h3> <h3 className="text-sm font-semibold">NVME</h3>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{groupedTemps.NVME.length} {groupAndSortTemperatures(hardwareData.temperatures).NVME.length}
</Badge> </Badge>
</div> </div>
<div className={`grid gap-4 ${groupedTemps.NVME.length > 1 ? "md:grid-cols-2" : ""}`}> <div
{groupedTemps.NVME.map((temp, index) => { className={`grid gap-4 ${groupAndSortTemperatures(hardwareData.temperatures).NVME.length > 1 ? "md:grid-cols-2" : ""}`}
>
{groupAndSortTemperatures(hardwareData.temperatures).NVME.map((temp, index) => {
const percentage = const percentage =
temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100
const isHot = temp.current > (temp.high || 80) const isHot = temp.current > (temp.high || 80)
@@ -558,17 +589,21 @@ export default function Hardware() {
)} )}
{/* PCI Sensors */} {/* PCI Sensors */}
{groupedTemps.PCI.length > 0 && ( {groupAndSortTemperatures(hardwareData.temperatures).PCI.length > 0 && (
<div className={groupedTemps.PCI.length > 1 ? "md:col-span-2" : ""}> <div
className={groupAndSortTemperatures(hardwareData.temperatures).PCI.length > 1 ? "md:col-span-2" : ""}
>
<div className="mb-3 flex items-center gap-2"> <div className="mb-3 flex items-center gap-2">
<CpuIcon className="h-4 w-4 text-muted-foreground" /> <CpuIcon className="h-4 w-4 text-muted-foreground" />
<h3 className="text-sm font-semibold">PCI</h3> <h3 className="text-sm font-semibold">PCI</h3>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{groupedTemps.PCI.length} {groupAndSortTemperatures(hardwareData.temperatures).PCI.length}
</Badge> </Badge>
</div> </div>
<div className={`grid gap-4 ${groupedTemps.PCI.length > 1 ? "md:grid-cols-2" : ""}`}> <div
{groupedTemps.PCI.map((temp, index) => { className={`grid gap-4 ${groupAndSortTemperatures(hardwareData.temperatures).PCI.length > 1 ? "md:grid-cols-2" : ""}`}
>
{groupAndSortTemperatures(hardwareData.temperatures).PCI.map((temp, index) => {
const percentage = const percentage =
temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100
const isHot = temp.current > (temp.high || 80) const isHot = temp.current > (temp.high || 80)
@@ -599,17 +634,21 @@ export default function Hardware() {
)} )}
{/* OTHER Sensors */} {/* OTHER Sensors */}
{groupedTemps.OTHER.length > 0 && ( {groupAndSortTemperatures(hardwareData.temperatures).OTHER.length > 0 && (
<div className={groupedTemps.OTHER.length > 1 ? "md:col-span-2" : ""}> <div
className={groupAndSortTemperatures(hardwareData.temperatures).OTHER.length > 1 ? "md:col-span-2" : ""}
>
<div className="mb-3 flex items-center gap-2"> <div className="mb-3 flex items-center gap-2">
<Thermometer className="h-4 w-4 text-muted-foreground" /> <Thermometer className="h-4 w-4 text-muted-foreground" />
<h3 className="text-sm font-semibold">OTHER</h3> <h3 className="text-sm font-semibold">OTHER</h3>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{groupedTemps.OTHER.length} {groupAndSortTemperatures(hardwareData.temperatures).OTHER.length}
</Badge> </Badge>
</div> </div>
<div className={`grid gap-4 ${groupedTemps.OTHER.length > 1 ? "md:grid-cols-2" : ""}`}> <div
{groupedTemps.OTHER.map((temp, index) => { className={`grid gap-4 ${groupAndSortTemperatures(hardwareData.temperatures).OTHER.length > 1 ? "md:grid-cols-2" : ""}`}
>
{groupAndSortTemperatures(hardwareData.temperatures).OTHER.map((temp, index) => {
const percentage = const percentage =
temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100
const isHot = temp.current > (temp.high || 80) const isHot = temp.current > (temp.high || 80)
@@ -639,8 +678,6 @@ export default function Hardware() {
</div> </div>
)} )}
</div> </div>
)
})()}
</Card> </Card>
)} )}
@@ -1188,10 +1225,12 @@ export default function Hardware() {
const isPercentage = fan.unit === "percent" || fan.unit === "%" const isPercentage = fan.unit === "percent" || fan.unit === "%"
const percentage = isPercentage ? fan.speed : Math.min((fan.speed / 5000) * 100, 100) const percentage = isPercentage ? fan.speed : Math.min((fan.speed / 5000) * 100, 100)
const displayName = improveSensorLabel(fan.name, fan.adapter)
return ( return (
<div key={index} className="space-y-2"> <div key={index} className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm font-medium">{fan.name}</span> <span className="text-sm font-medium">{displayName}</span>
<span className="text-sm font-semibold text-blue-500"> <span className="text-sm font-semibold text-blue-500">
{isPercentage ? `${fan.speed.toFixed(0)} percent` : `${fan.speed.toFixed(0)} ${fan.unit}`} {isPercentage ? `${fan.speed.toFixed(0)} percent` : `${fan.speed.toFixed(0)} ${fan.unit}`}
</span> </span>
@@ -1597,7 +1636,35 @@ export default function Hardware() {
.filter( .filter(
(device) => device.type === "disk" && !device.name.startsWith("zd") && !device.name.startsWith("loop"), (device) => device.type === "disk" && !device.name.startsWith("zd") && !device.name.startsWith("loop"),
) )
.map((device, index) => ( .map((device, index) => {
const getDiskTypeBadge = (diskName: string, rotationRate: number | undefined) => {
let diskType = "HDD"
if (diskName.startsWith("nvme")) {
diskType = "NVMe"
} else if (!rotationRate || rotationRate === 0) {
diskType = "SSD"
}
const badgeStyles: Record<string, { className: string; label: string }> = {
NVMe: {
className: "bg-purple-500/10 text-purple-500 border-purple-500/20",
label: "NVMe SSD",
},
SSD: {
className: "bg-cyan-500/10 text-cyan-500 border-cyan-500/20",
label: "SSD",
},
HDD: {
className: "bg-blue-500/10 text-blue-500 border-blue-500/20",
label: "HDD",
},
}
return badgeStyles[diskType]
}
const diskBadge = getDiskTypeBadge(device.name, device.rotation_rate)
return (
<div <div
key={index} key={index}
onClick={() => setSelectedDisk(device)} onClick={() => setSelectedDisk(device)}
@@ -1605,9 +1672,7 @@ export default function Hardware() {
> >
<div className="flex items-center justify-between gap-2 mb-2"> <div className="flex items-center justify-between gap-2 mb-2">
<span className="text-sm font-medium truncate flex-1">{device.name}</span> <span className="text-sm font-medium truncate flex-1">{device.name}</span>
<Badge className="bg-blue-500/10 text-blue-500 border-blue-500/20 px-2.5 py-0.5 shrink-0"> <Badge className={`${diskBadge.className} px-2.5 py-0.5 shrink-0`}>{diskBadge.label}</Badge>
{device.type}
</Badge>
</div> </div>
{device.size && <p className="text-sm font-medium">{formatMemory(parseLsblkSize(device.size))}</p>} {device.size && <p className="text-sm font-medium">{formatMemory(parseLsblkSize(device.size))}</p>}
{device.model && ( {device.model && (
@@ -1617,7 +1682,8 @@ export default function Hardware() {
<p className="mt-1 font-mono text-xs text-green-500 truncate">Driver: {device.driver}</p> <p className="mt-1 font-mono text-xs text-green-500 truncate">Driver: {device.driver}</p>
)} )}
</div> </div>
))} )
})}
</div> </div>
<p className="mt-4 text-xs text-muted-foreground">Click on a device for detailed hardware information</p> <p className="mt-4 text-xs text-muted-foreground">Click on a device for detailed hardware information</p>
</Card> </Card>