Update AppImage

This commit is contained in:
MacRimi
2025-11-04 12:47:26 +01:00
parent 83dcc0c4f2
commit 55394cbf09
7 changed files with 216 additions and 38 deletions

View File

@@ -64,9 +64,12 @@ const formatMemory = (memoryKB: number | string): string => {
return `${tb.toFixed(1)} TB`
}
// Convert to GB if >= 1024 MB
if (mb >= 1024) {
const gb = mb / 1024
// If GB value is greater than 999, convert to TB
if (gb > 999) {
return `${(gb / 1024).toFixed(2)} TB`
}
return `${gb.toFixed(1)} GB`
}
@@ -1658,6 +1661,62 @@ export default function Hardware() {
const diskBadge = getDiskTypeBadge(device.name, device.rotation_rate)
const getLinkSpeedInfo = (device: StorageDevice) => {
// NVMe PCIe information
if (device.name.startsWith("nvme") && (device.pcie_gen || device.pcie_width)) {
const current = `${device.pcie_gen || ""} ${device.pcie_width || ""}`.trim()
const max =
device.pcie_max_gen && device.pcie_max_width
? `${device.pcie_max_gen} ${device.pcie_max_width}`.trim()
: null
// Check if running at lower speed than maximum
const isLowerSpeed = max && current !== max
return {
text: current || null,
maxText: max,
isWarning: isLowerSpeed,
color: isLowerSpeed ? "text-orange-500" : "text-blue-500",
}
}
// SATA information
if (device.sata_version) {
return {
text: device.sata_version,
maxText: null,
isWarning: false,
color: "text-blue-500",
}
}
// SAS information
if (device.sas_version || device.sas_speed) {
const text = [device.sas_version, device.sas_speed].filter(Boolean).join(" ")
return {
text: text || null,
maxText: null,
isWarning: false,
color: "text-blue-500",
}
}
// Generic link speed
if (device.link_speed) {
return {
text: device.link_speed,
maxText: null,
isWarning: false,
color: "text-blue-500",
}
}
return null
}
const linkSpeed = getLinkSpeedInfo(device)
return (
<div
key={index}
@@ -1672,6 +1731,14 @@ export default function Hardware() {
{device.model && (
<p className="text-xs text-muted-foreground line-clamp-2 break-words">{device.model}</p>
)}
{linkSpeed && (
<div className="mt-1 flex items-center gap-1">
<span className={`text-xs font-medium ${linkSpeed.color}`}>{linkSpeed.text}</span>
{linkSpeed.isWarning && linkSpeed.maxText && (
<span className="text-xs text-muted-foreground">(max: {linkSpeed.maxText})</span>
)}
</div>
)}
</div>
)
})}
@@ -1743,6 +1810,102 @@ export default function Hardware() {
</div>
)}
{(selectedDisk.pcie_gen ||
selectedDisk.pcie_width ||
selectedDisk.sata_version ||
selectedDisk.sas_version ||
selectedDisk.link_speed) && (
<>
<div className="pt-2">
<h3 className="text-sm font-semibold text-muted-foreground mb-2 uppercase tracking-wide">
Interface Information
</h3>
</div>
{/* NVMe PCIe Information */}
{selectedDisk.name.startsWith("nvme") && (selectedDisk.pcie_gen || selectedDisk.pcie_width) && (
<>
<div className="flex justify-between border-b border-border/50 pb-2">
<span className="text-sm font-medium text-muted-foreground">Current Link Speed</span>
<span className="text-sm font-medium text-blue-500">
{selectedDisk.pcie_gen} {selectedDisk.pcie_width}
</span>
</div>
{selectedDisk.pcie_max_gen && selectedDisk.pcie_max_width && (
<div className="flex justify-between border-b border-border/50 pb-2">
<span className="text-sm font-medium text-muted-foreground">Maximum Link Speed</span>
<span className="text-sm font-medium">
{selectedDisk.pcie_max_gen} {selectedDisk.pcie_max_width}
</span>
</div>
)}
{/* Warning if running at lower speed */}
{selectedDisk.pcie_max_gen &&
selectedDisk.pcie_max_width &&
`${selectedDisk.pcie_gen} ${selectedDisk.pcie_width}` !==
`${selectedDisk.pcie_max_gen} ${selectedDisk.pcie_max_width}` && (
<div className="rounded-lg bg-orange-500/10 p-3 border border-orange-500/20">
<div className="flex gap-2">
<svg
className="h-5 w-5 text-orange-500 flex-shrink-0"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clipRule="evenodd"
/>
</svg>
<div>
<h4 className="text-sm font-semibold text-orange-500 mb-1">Performance Notice</h4>
<p className="text-xs text-muted-foreground">
This drive is running at {selectedDisk.pcie_gen} {selectedDisk.pcie_width} but
supports up to {selectedDisk.pcie_max_gen} {selectedDisk.pcie_max_width}. Check if the
slot supports the maximum speed or if the drive is properly seated.
</p>
</div>
</div>
</div>
)}
</>
)}
{/* SATA Information */}
{selectedDisk.sata_version && (
<div className="flex justify-between border-b border-border/50 pb-2">
<span className="text-sm font-medium text-muted-foreground">SATA Version</span>
<span className="text-sm font-medium text-blue-500">{selectedDisk.sata_version}</span>
</div>
)}
{/* SAS Information */}
{selectedDisk.sas_version && (
<div className="flex justify-between border-b border-border/50 pb-2">
<span className="text-sm font-medium text-muted-foreground">SAS Version</span>
<span className="text-sm font-medium text-blue-500">{selectedDisk.sas_version}</span>
</div>
)}
{selectedDisk.sas_speed && (
<div className="flex justify-between border-b border-border/50 pb-2">
<span className="text-sm font-medium text-muted-foreground">SAS Speed</span>
<span className="text-sm font-medium text-blue-500">{selectedDisk.sas_speed}</span>
</div>
)}
{/* Generic Link Speed */}
{selectedDisk.link_speed &&
!selectedDisk.pcie_gen &&
!selectedDisk.sata_version &&
!selectedDisk.sas_version && (
<div className="flex justify-between border-b border-border/50 pb-2">
<span className="text-sm font-medium text-muted-foreground">Link Speed</span>
<span className="text-sm font-medium text-blue-500">{selectedDisk.link_speed}</span>
</div>
)}
</>
)}
{selectedDisk.model && (
<div className="flex justify-between border-b border-border/50 pb-2">
<span className="text-sm font-medium text-muted-foreground">Model</span>
@@ -1806,13 +1969,6 @@ export default function Hardware() {
<span className="text-sm">{selectedDisk.form_factor}</span>
</div>
)}
{selectedDisk.sata_version && (
<div className="flex justify-between border-b border-border/50 pb-2">
<span className="text-sm font-medium text-muted-foreground">SATA Version</span>
<span className="text-sm">{selectedDisk.sata_version}</span>
</div>
)}
</div>
)}
</DialogContent>

View File

@@ -5,6 +5,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"
import { Progress } from "./ui/progress"
import { Badge } from "./ui/badge"
import { HardDrive, Database, Archive, AlertTriangle, CheckCircle, Activity, AlertCircle } from "lucide-react"
import { formatStorage } from "@/lib/utils"
interface StorageData {
total: number
@@ -116,10 +117,10 @@ export function StorageMetrics() {
<HardDrive className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-xl lg:text-2xl font-bold text-foreground">{storageData.total.toFixed(1)} GB</div>
<div className="text-xl lg:text-2xl font-bold text-foreground">{formatStorage(storageData.total)}</div>
<Progress value={usagePercent} className="mt-2" />
<p className="text-xs text-muted-foreground mt-2">
{storageData.used.toFixed(1)} GB used {storageData.available.toFixed(1)} GB available
{formatStorage(storageData.used)} used {formatStorage(storageData.available)} available
</p>
</CardContent>
</Card>
@@ -130,7 +131,7 @@ export function StorageMetrics() {
<Database className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-xl lg:text-2xl font-bold text-foreground">{storageData.used.toFixed(1)} GB</div>
<div className="text-xl lg:text-2xl font-bold text-foreground">{formatStorage(storageData.used)}</div>
<Progress value={usagePercent} className="mt-2" />
<p className="text-xs text-muted-foreground mt-2">{usagePercent.toFixed(1)}% of total space</p>
</CardContent>
@@ -144,7 +145,7 @@ export function StorageMetrics() {
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-xl lg:text-2xl font-bold text-foreground">{storageData.available.toFixed(1)} GB</div>
<div className="text-xl lg:text-2xl font-bold text-foreground">{formatStorage(storageData.available)}</div>
<div className="flex items-center mt-2">
<Badge variant="outline" className="bg-green-500/10 text-green-500 border-green-500/20">
{((storageData.available / storageData.total) * 100).toFixed(1)}% Free
@@ -201,7 +202,7 @@ export function StorageMetrics() {
<div className="flex items-center space-x-6">
<div className="text-right">
<div className="text-sm font-medium text-foreground">
{disk.used.toFixed(1)} GB / {disk.total.toFixed(1)} GB
{formatStorage(disk.used)} / {formatStorage(disk.total)}
</div>
<Progress value={disk.usage_percent} className="w-24 mt-1" />
</div>

View File

@@ -76,12 +76,11 @@ const formatStorage = (sizeInGB: number): string => {
if (sizeInGB < 1) {
// Less than 1 GB, show in MB
return `${(sizeInGB * 1024).toFixed(1)} MB`
} else if (sizeInGB < 1024) {
// Less than 1024 GB, show in GB
return `${sizeInGB.toFixed(1)} GB`
} else if (sizeInGB > 999) {
return `${(sizeInGB / 1024).toFixed(2)} TB`
} else {
// 1024 GB or more, show in TB
return `${(sizeInGB / 1024).toFixed(1)} TB`
// Between 1 and 999 GB, show in GB
return `${sizeInGB.toFixed(2)} GB`
}
}
@@ -598,7 +597,7 @@ export function StorageOverview() {
<div className="grid grid-cols-3 gap-4 text-sm">
<div>
<p className="text-muted-foreground">Total</p>
<p className="font-medium">{storage.total.toLocaleString()} GB</p>
<p className="font-medium">{formatStorage(storage.total)}</p>
</div>
<div>
<p className="text-muted-foreground">Used</p>
@@ -611,12 +610,12 @@ export function StorageOverview() {
: "text-blue-400"
}`}
>
{storage.used.toLocaleString()} GB
{formatStorage(storage.used)}
</p>
</div>
<div>
<p className="text-muted-foreground">Available</p>
<p className="font-medium text-green-400">{storage.available.toLocaleString()} GB</p>
<p className="font-medium text-green-400">{formatStorage(storage.available)}</p>
</div>
</div>
</div>

View File

@@ -388,12 +388,11 @@ export function SystemOverview() {
if (sizeInGB < 1) {
// Less than 1 GB, show in MB
return `${(sizeInGB * 1024).toFixed(1)} MB`
} else if (sizeInGB < 1024) {
// Less than 1024 GB, show in GB
return `${sizeInGB.toFixed(1)} GB`
} else {
// 1024 GB or more, show in TB
} else if (sizeInGB > 999) {
return `${(sizeInGB / 1024).toFixed(2)} TB`
} else {
// Between 1 and 999 GB, show in GB
return `${sizeInGB.toFixed(2)} GB`
}
}

View File

@@ -25,6 +25,7 @@ import {
} from "lucide-react"
import useSWR from "swr"
import { MetricsView } from "./metrics-dialog"
import { formatStorage } from "@/lib/utils" // Import formatStorage utility
interface VMData {
vmid: number
@@ -194,18 +195,18 @@ const extractIPFromConfig = (config?: VMConfig, lxcIPInfo?: VMDetails["lxc_ip_in
return "DHCP"
}
const formatStorage = (sizeInGB: number): string => {
if (sizeInGB < 1) {
// Less than 1 GB, show in MB
return `${(sizeInGB * 1024).toFixed(1)} MB`
} else if (sizeInGB < 1024) {
// Less than 1024 GB, show in GB
return `${sizeInGB.toFixed(1)} GB`
} else {
// 1024 GB or more, show in TB
return `${(sizeInGB / 1024).toFixed(1)} TB`
}
}
// const formatStorage = (sizeInGB: number): string => {
// if (sizeInGB < 1) {
// // Less than 1 GB, show in MB
// return `${(sizeInGB * 1024).toFixed(1)} MB`
// } else if (sizeInGB < 1024) {
// // Less than 1024 GB, show in GB
// return `${sizeInGB.toFixed(1)} GB`
// } else {
// // 1024 GB or more, show in TB
// return `${(sizeInGB / 1024).toFixed(1)} TB`
// }
// }
const getUsageColor = (percent: number): string => {
if (percent >= 95) return "text-red-500"

View File

@@ -4,3 +4,18 @@ import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function formatStorage(sizeInGB: number): string {
if (sizeInGB < 1) {
// Less than 1 GB, show in MB
const mb = sizeInGB * 1024
return `${mb % 1 === 0 ? mb.toFixed(0) : mb.toFixed(1)} MB`
} else if (sizeInGB < 1024) {
// Less than 1024 GB, show in GB
return `${sizeInGB % 1 === 0 ? sizeInGB.toFixed(0) : sizeInGB.toFixed(1)} GB`
} else {
// 1024 GB or more, show in TB
const tb = sizeInGB / 1024
return `${tb % 1 === 0 ? tb.toFixed(0) : tb.toFixed(1)} TB`
}
}

View File

@@ -33,6 +33,13 @@ export interface StorageDevice {
rotation_rate?: number | string
form_factor?: string
sata_version?: string
pcie_gen?: string // e.g., "PCIe 4.0"
pcie_width?: string // e.g., "x4"
pcie_max_gen?: string // Maximum supported PCIe generation
pcie_max_width?: string // Maximum supported PCIe lanes
sas_version?: string // e.g., "SAS-3"
sas_speed?: string // e.g., "12Gb/s"
link_speed?: string // Generic link speed info
}
export interface PCIDevice {