diff --git a/package.json b/package.json
index 8f691c9..9703ad7 100644
--- a/package.json
+++ b/package.json
@@ -39,14 +39,14 @@
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@eslint/js": "^9.35.0",
- "@types/node": "^24.5.0",
+ "@types/node": "^24.5.1",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9",
"@typescript-eslint/eslint-plugin": "^8.44.0",
"@typescript-eslint/parser": "^8.44.0",
"@vitejs/plugin-react": "^5.0.2",
"cross-env": "^10.0.0",
- "electron": "^38.1.0",
+ "electron": "^38.1.1",
"electron-builder": "^26.0.12",
"electron-vite": "^4.0.0",
"eslint": "^9.35.0",
@@ -74,7 +74,7 @@
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-error-boundary": "^6.0.0",
- "systeminformation": "^5.27.9",
+ "systeminformation": "^5.27.10",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0",
"yauzl": "^3.2.0",
diff --git a/src/components/StatusBar.tsx b/src/components/StatusBar.tsx
index 9c72d3e..5bbfac5 100644
--- a/src/components/StatusBar.tsx
+++ b/src/components/StatusBar.tsx
@@ -65,14 +65,14 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
<>
>
)}
@@ -81,14 +81,14 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
1 ? ` ${index + 1}` : ''}`}
- value={`${gpu.usage.toFixed(1)}%`}
- tooltipLabel={`${gpu.usage.toFixed(1)}%`}
+ value={`${gpu.usage}%`}
+ tooltipLabel={`${gpu.usage}%`}
/>
1 ? ` ${index + 1}` : ''}`}
- value={`${gpu.memoryUsage.toFixed(1)}%`}
- tooltipLabel={`${gpu.memoryUsed.toFixed(1)} GB / ${gpu.memoryTotal.toFixed(1)} GB (${gpu.memoryUsage.toFixed(1)}%)`}
+ value={`${gpu.memoryUsage}%`}
+ tooltipLabel={`${gpu.memoryUsed.toFixed(2)} GB / ${gpu.memoryTotal.toFixed(2)} GB (${gpu.memoryUsage}%)`}
/>
))}
diff --git a/src/main/modules/monitoring.ts b/src/main/modules/monitoring.ts
index 94a3d20..ba3cdce 100644
--- a/src/main/modules/monitoring.ts
+++ b/src/main/modules/monitoring.ts
@@ -1,4 +1,3 @@
-import { platform } from 'process';
import si from 'systeminformation';
import { BrowserWindow } from 'electron';
import { logError } from '@/main/modules/logging';
@@ -46,7 +45,7 @@ let cpuInterval: ReturnType | null = null;
let memoryInterval: ReturnType | null = null;
let gpuInterval: ReturnType | null = null;
let isRunning = false;
-const updateFrequency = platform === 'win32' ? 2000 : 1000;
+const updateFrequency = 1000;
let mainWindow: BrowserWindow | null = null;
export function startMonitoring(window: BrowserWindow) {
@@ -91,7 +90,7 @@ async function collectAndSendCpuMetrics() {
try {
const cpuData = await si.currentLoad();
const metrics: CpuMetrics = {
- usage: cpuData.currentLoad,
+ usage: Math.round(cpuData.currentLoad),
};
if (mainWindow && !mainWindow.isDestroyed()) {
@@ -108,9 +107,9 @@ async function collectAndSendMemoryMetrics() {
const usedBytes = memData.active || memData.used;
const totalBytes = memData.total;
const metrics: MemoryMetrics = {
- used: Math.round((usedBytes / (1024 * 1024 * 1024)) * 10) / 10,
- total: Math.round((totalBytes / (1024 * 1024 * 1024)) * 10) / 10,
- usage: (usedBytes / totalBytes) * 100,
+ used: usedBytes / (1024 * 1024 * 1024),
+ total: totalBytes / (1024 * 1024 * 1024),
+ usage: Math.round((usedBytes / totalBytes) * 100),
};
if (mainWindow && !mainWindow.isDestroyed()) {
@@ -127,12 +126,12 @@ async function collectAndSendGpuMetrics() {
const metrics: GpuMetrics = {
gpus: gpuData.map((gpuInfo) => ({
name: gpuInfo.deviceName,
- usage: gpuInfo.usage,
+ usage: Math.round(gpuInfo.usage),
memoryUsed: gpuInfo.memoryUsed,
memoryTotal: gpuInfo.memoryTotal,
memoryUsage:
gpuInfo.memoryTotal > 0
- ? (gpuInfo.memoryUsed / gpuInfo.memoryTotal) * 100
+ ? Math.round((gpuInfo.memoryUsed / gpuInfo.memoryTotal) * 100)
: 0,
})),
};
diff --git a/src/utils/node/gpu.ts b/src/utils/node/gpu.ts
index 3adb4c1..94447fd 100644
--- a/src/utils/node/gpu.ts
+++ b/src/utils/node/gpu.ts
@@ -3,6 +3,31 @@ import { join } from 'path';
import { spawn } from 'child_process';
import { platform } from 'process';
+interface CachedGPUInfo {
+ deviceName: string;
+ devicePath: string;
+ memoryTotal: number;
+}
+
+interface WindowsCachedGPUInfo {
+ deviceName: string;
+ memoryTotal: number;
+ luid: string;
+}
+
+interface GPUData {
+ deviceName: string;
+ usage: number;
+ memoryUsed: number;
+ memoryTotal: number;
+}
+
+let linuxGpuCache: CachedGPUInfo[] | null = null;
+let windowsGpuCache: WindowsCachedGPUInfo[] | null = null;
+
+let linuxCachePromise: Promise | null = null;
+let windowsCachePromise: Promise | null = null;
+
export async function getGPUData() {
if (platform === 'win32') {
return getWindowsGPUData();
@@ -13,19 +38,20 @@ export async function getGPUData() {
}
}
-async function getWindowsGPUData() {
- return new Promise<
- {
- deviceName: string;
- usage: number;
- memoryUsed: number;
- memoryTotal: number;
- }[]
- >((resolve) => {
+async function initializeWindowsGPUCache() {
+ if (windowsGpuCache !== null) {
+ return windowsGpuCache;
+ }
+
+ if (windowsCachePromise !== null) {
+ return windowsCachePromise;
+ }
+
+ windowsCachePromise = new Promise((resolve) => {
const script = `
-# Get GPU basic info and total VRAM from registry
$gpus = Get-WmiObject -Class Win32_VideoController | Where-Object {$_.Name -notlike "*Microsoft*"}
$correctVRAM = @{}
+$gpuToLuid = @{}
try {
$regPath = "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}"
@@ -42,196 +68,342 @@ try {
}
} catch {}
-# Get VRAM usage from GPU Adapter Memory counters (what Task Manager uses)
-$vramUsageByLuid = @{}
try {
$adapterMemory = Get-Counter "\\GPU Adapter Memory(*)\\Dedicated Usage" -ErrorAction SilentlyContinue
if ($adapterMemory) {
foreach ($sample in $adapterMemory.CounterSamples) {
$instanceName = $sample.InstanceName
- $usageBytes = $sample.CookedValue
- $vramUsageByLuid[$instanceName] = $usageBytes
- }
- }
-} catch {}
-
-# Get GPU utilization from performance counters
-$gpuUtilization = @{}
-try {
- $engineCounters = Get-Counter "\\GPU Engine(*)\\Utilization Percentage" -ErrorAction SilentlyContinue
- if ($engineCounters) {
- $utilizationByLuid = @{}
- foreach ($sample in $engineCounters.CounterSamples) {
- $instanceName = $sample.InstanceName
- $utilization = $sample.CookedValue
-
- if ($instanceName -match "luid_(0x[0-9a-fA-F]+_0x[0-9a-fA-F]+)") {
+ if ($instanceName -match "luid_(0x[0-9a-fA-F]+_0x[0-9a-fA-F]+)_([^_]+)") {
$luid = $matches[1]
- if (-not $utilizationByLuid[$luid]) {
- $utilizationByLuid[$luid] = 0
- }
- $utilizationByLuid[$luid] = [Math]::Max($utilizationByLuid[$luid], $utilization)
- }
- }
-
- # Find the main GPU LUID (the one with actual VRAM usage)
- $mainLuid = ""
- $maxVramUsage = 0
- foreach ($luid in $vramUsageByLuid.Keys) {
- if ($vramUsageByLuid[$luid] -gt $maxVramUsage) {
- $maxVramUsage = $vramUsageByLuid[$luid]
- $mainLuid = $luid
- }
- }
-
- # Extract LUID from main adapter
- if ($mainLuid -match "luid_(0x[0-9a-fA-F]+_0x[0-9a-fA-F]+)") {
- $mainLuidKey = $matches[1]
- foreach ($gpu in $gpus) {
- if ($utilizationByLuid[$mainLuidKey]) {
- $gpuUtilization[$gpu.Name] = $utilizationByLuid[$mainLuidKey]
+ $gpuNameFromCounter = $matches[2]
+
+ foreach ($gpu in $gpus) {
+ $cleanGpuName = $gpu.Name -replace '[^a-zA-Z0-9]', ''
+ $cleanCounterName = $gpuNameFromCounter -replace '[^a-zA-Z0-9]', ''
+
+ if ($cleanGpuName -eq $cleanCounterName -or $gpu.Name -like "*$gpuNameFromCounter*") {
+ $gpuToLuid[$gpu.Name] = $luid
+ break
+ }
}
}
}
}
} catch {}
-# Output results
foreach ($gpu in $gpus) {
$totalVRAM = $correctVRAM[$gpu.Name]
if (-not $totalVRAM -or $totalVRAM -le 0) {
$totalVRAM = $gpu.AdapterRAM
}
- # Get VRAM usage for the main GPU (highest usage LUID)
- $usedVRAM = 0
- $maxUsage = 0
- foreach ($luid in $vramUsageByLuid.Keys) {
- $usage = $vramUsageByLuid[$luid]
- if ($usage -gt $maxUsage) {
- $maxUsage = $usage
- $usedVRAM = $usage
- }
+ $luid = $gpuToLuid[$gpu.Name]
+ if ($luid) {
+ Write-Output "$($gpu.Name)|$totalVRAM|$luid"
}
-
- $utilization = 0
- if ($gpuUtilization[$gpu.Name]) {
- $utilization = $gpuUtilization[$gpu.Name]
- }
-
- Write-Output "$($gpu.Name)|$totalVRAM|$usedVRAM|$utilization"
}
`;
- const powershell = spawn(
- 'powershell.exe',
- [
- '-NoProfile',
- '-NonInteractive',
- '-ExecutionPolicy',
- 'Bypass',
- '-Command',
- script,
- ],
- {
- stdio: ['pipe', 'pipe', 'pipe'],
- windowsHide: true,
- shell: false,
+ let timeoutHandle: ReturnType | null = null;
+ let powershellProcess: ReturnType | null = null;
+
+ const cleanup = () => {
+ if (timeoutHandle) {
+ clearTimeout(timeoutHandle);
+ timeoutHandle = null;
}
- );
+ if (powershellProcess && !powershellProcess.killed) {
+ powershellProcess.kill('SIGTERM');
+ }
+ };
- let output = '';
+ try {
+ powershellProcess = spawn(
+ 'powershell.exe',
+ [
+ '-NoProfile',
+ '-NonInteractive',
+ '-ExecutionPolicy',
+ 'Bypass',
+ '-Command',
+ script,
+ ],
+ {
+ stdio: ['pipe', 'pipe', 'pipe'],
+ windowsHide: true,
+ shell: false,
+ }
+ );
- powershell.stdout.on('data', (data) => {
- output += data.toString();
- });
+ let output = '';
- powershell.on('close', (code) => {
- const gpus: {
- deviceName: string;
- usage: number;
- memoryUsed: number;
- memoryTotal: number;
- }[] = [];
+ if (powershellProcess.stdout) {
+ powershellProcess.stdout.on('data', (data) => {
+ output += data.toString();
+ });
+ }
- if (code === 0 && output.trim()) {
- const lines = output.trim().split('\n');
- for (const line of lines) {
- const parts = line.split('|');
- if (parts.length === 4 && parts[0]) {
- const name = parts[0].trim();
- const totalRAM = parseInt(parts[1]) || 0;
- const usedRAM = parseInt(parts[2]) || 0;
- const utilization = parseFloat(parts[3]) || 0;
+ powershellProcess.on('close', (code) => {
+ cleanup();
+ const gpus = [];
- gpus.push({
- deviceName: name,
- usage: Math.round(utilization * 100) / 100,
- memoryUsed: usedRAM > 0 ? usedRAM / (1024 * 1024 * 1024) : 0,
- memoryTotal: totalRAM > 0 ? totalRAM / (1024 * 1024 * 1024) : 0,
- });
+ if (code === 0 && output.trim()) {
+ const lines = output.trim().split('\n');
+ for (const line of lines) {
+ const parts = line.split('|');
+ if (parts.length === 3 && parts[0]) {
+ const name = parts[0].trim();
+ const totalRAM = parseInt(parts[1]) || 0;
+ const luid = parts[2].trim();
+
+ if (name && luid && totalRAM >= 0) {
+ gpus.push({
+ deviceName: name,
+ memoryTotal:
+ totalRAM > 0 ? totalRAM / (1024 * 1024 * 1024) : 0,
+ luid,
+ });
+ }
+ }
}
}
- }
- resolve(gpus);
- });
+ windowsGpuCache = gpus;
+ windowsCachePromise = null;
+ resolve(gpus);
+ });
- powershell.on('error', () => resolve([]));
+ powershellProcess.on('error', () => {
+ cleanup();
+ windowsGpuCache = [];
+ windowsCachePromise = null;
+ resolve([]);
+ });
- setTimeout(() => {
- powershell.kill('SIGTERM');
+ timeoutHandle = setTimeout(() => {
+ cleanup();
+ windowsGpuCache = [];
+ windowsCachePromise = null;
+ resolve([]);
+ }, 8000);
+ } catch {
+ cleanup();
+ windowsGpuCache = [];
+ windowsCachePromise = null;
resolve([]);
- }, 5000);
+ }
});
+
+ return windowsCachePromise;
}
-async function getLinuxGPUData() {
+async function getWindowsGPUData() {
try {
- const drmPath = '/sys/class/drm';
- const entries = await readdir(drmPath);
- const cardEntries = entries.filter(
- (entry) => entry.startsWith('card') && !entry.includes('-')
- );
+ const cachedGPUs = await initializeWindowsGPUCache();
- const gpus = [];
- for (const card of cardEntries) {
- const devicePath = join(drmPath, card, 'device');
+ return new Promise((resolve) => {
+ const script = `
+$vramUsageByLuid = @{}
+$utilizationByLuid = @{}
+$job1 = $null
+$job2 = $null
- let deviceName = 'Unknown GPU';
+try {
+ $job1 = Start-Job -ScriptBlock {
+ try {
+ $adapterMemory = Get-Counter "\\GPU Adapter Memory(*)\\Dedicated Usage" -ErrorAction SilentlyContinue
+ if ($adapterMemory) {
+ $result = @{}
+ foreach ($sample in $adapterMemory.CounterSamples) {
+ $instanceName = $sample.InstanceName
+ $usageBytes = $sample.CookedValue
+ if ($instanceName -match "luid_(0x[0-9a-fA-F]+_0x[0-9a-fA-F]+)") {
+ $luid = $matches[1]
+ $result[$luid] = $usageBytes
+ }
+ }
+ return $result
+ }
+ } catch {}
+ return @{}
+ }
+
+ $job2 = Start-Job -ScriptBlock {
+ try {
+ $engineCounters = Get-Counter "\\GPU Engine(*)\\Utilization Percentage" -ErrorAction SilentlyContinue
+ if ($engineCounters) {
+ $result = @{}
+ foreach ($sample in $engineCounters.CounterSamples) {
+ $instanceName = $sample.InstanceName
+ $utilization = $sample.CookedValue
+
+ if ($instanceName -match "luid_(0x[0-9a-fA-F]+_0x[0-9a-fA-F]+)") {
+ $luid = $matches[1]
+ if (-not $result[$luid]) {
+ $result[$luid] = 0
+ }
+ $result[$luid] = [Math]::Max($result[$luid], $utilization)
+ }
+ }
+ return $result
+ }
+ } catch {}
+ return @{}
+ }
+
+ if ($job1) { $vramUsageByLuid = Receive-Job $job1 -Wait }
+ if ($job2) { $utilizationByLuid = Receive-Job $job2 -Wait }
+
+} catch {}
+finally {
+ if ($job1) { Remove-Job $job1 -Force -ErrorAction SilentlyContinue }
+ if ($job2) { Remove-Job $job2 -Force -ErrorAction SilentlyContinue }
+}
+
+foreach ($luid in $vramUsageByLuid.Keys) {
+ $vramUsed = $vramUsageByLuid[$luid]
+ $utilization = 0
+ if ($utilizationByLuid[$luid]) {
+ $utilization = $utilizationByLuid[$luid]
+ }
+ Write-Output "$luid|$vramUsed|$utilization"
+}
+`;
+
+ let timeoutHandle: ReturnType | null = null;
+ let powershellProcess: ReturnType | null = null;
+
+ const cleanup = () => {
+ if (timeoutHandle) {
+ clearTimeout(timeoutHandle);
+ timeoutHandle = null;
+ }
+ if (powershellProcess && !powershellProcess.killed) {
+ powershellProcess.kill('SIGTERM');
+ }
+ };
try {
- const deviceNameData = await readFile(`${devicePath}/device`, 'utf8');
- const deviceId = deviceNameData.trim();
+ powershellProcess = spawn(
+ 'powershell.exe',
+ [
+ '-NoProfile',
+ '-NonInteractive',
+ '-ExecutionPolicy',
+ 'Bypass',
+ '-Command',
+ script,
+ ],
+ {
+ stdio: ['pipe', 'pipe', 'pipe'],
+ windowsHide: true,
+ shell: false,
+ }
+ );
- const vendorData = await readFile(`${devicePath}/vendor`, 'utf8');
- const vendorId = vendorData.trim();
+ let output = '';
- const modalias = await readFile(`${devicePath}/modalias`, 'utf8');
-
- if (vendorId === '0x1002') {
- deviceName = 'AMD GPU';
- } else if (vendorId === '0x10de') {
- deviceName = 'NVIDIA GPU';
- } else if (vendorId === '0x8086') {
- deviceName = 'Intel GPU';
- } else {
- deviceName = `GPU (${vendorId}:${deviceId})`;
+ if (powershellProcess.stdout) {
+ powershellProcess.stdout.on('data', (data) => {
+ output += data.toString();
+ });
}
- if (modalias.includes('i915')) {
- deviceName = 'Intel GPU';
- } else if (modalias.includes('amdgpu')) {
- deviceName = 'AMD GPU';
- } else if (
- modalias.includes('nouveau') ||
- modalias.includes('nvidia')
- ) {
- deviceName = 'NVIDIA GPU';
- }
+ powershellProcess.on('close', (code) => {
+ cleanup();
+ const gpus: GPUData[] = [];
+
+ if (code === 0 && output.trim() && cachedGPUs.length > 0) {
+ const luidToData = new Map<
+ string,
+ { vramUsed: number; utilization: number }
+ >();
+
+ const lines = output.trim().split('\n');
+ for (const line of lines) {
+ const parts = line.split('|');
+ if (parts.length === 3) {
+ const luid = parts[0].trim();
+ const vramUsed = Math.max(0, parseInt(parts[1]) || 0);
+ const utilization = Math.max(
+ 0,
+ Math.min(100, parseFloat(parts[2]) || 0)
+ );
+
+ luidToData.set(luid, { vramUsed, utilization });
+ }
+ }
+
+ for (const cachedGPU of cachedGPUs) {
+ const gpuData = luidToData.get(cachedGPU.luid);
+ if (gpuData) {
+ gpus.push({
+ deviceName: cachedGPU.deviceName,
+ usage: gpuData.utilization,
+ memoryUsed:
+ gpuData.vramUsed > 0
+ ? parseFloat(
+ (gpuData.vramUsed / (1024 * 1024 * 1024)).toFixed(2)
+ )
+ : 0,
+ memoryTotal: parseFloat(
+ Math.max(0, cachedGPU.memoryTotal).toFixed(2)
+ ),
+ });
+ }
+ }
+ }
+
+ resolve(gpus);
+ });
+
+ powershellProcess.on('error', () => {
+ cleanup();
+ resolve([]);
+ });
+
+ timeoutHandle = setTimeout(() => {
+ cleanup();
+ resolve([]);
+ }, 8000);
} catch {
+ cleanup();
+ resolve([]);
+ }
+ });
+ } catch {
+ return [];
+ }
+}
+
+async function initializeLinuxGPUCache() {
+ if (linuxGpuCache !== null) {
+ return linuxGpuCache;
+ }
+
+ if (linuxCachePromise !== null) {
+ return linuxCachePromise;
+ }
+
+ linuxCachePromise = (async () => {
+ try {
+ const drmPath = '/sys/class/drm';
+ const entries = await readdir(drmPath);
+ const cardEntries = entries.filter(
+ (entry) => entry.startsWith('card') && !entry.includes('-')
+ );
+
+ const gpus = [];
+ for (const card of cardEntries) {
+ const devicePath = join(drmPath, card, 'device');
+
+ let deviceName = 'Unknown GPU';
+
try {
- const modalias = await readFile(`${devicePath}/modalias`, 'utf8');
+ const [modalias] = await Promise.all([
+ readFile(`${devicePath}/modalias`, 'utf8').catch(() => ''),
+ ]);
+
if (modalias.includes('i915')) {
deviceName = 'Intel GPU';
} else if (modalias.includes('amdgpu')) {
@@ -241,37 +413,96 @@ async function getLinuxGPUData() {
modalias.includes('nvidia')
) {
deviceName = 'NVIDIA GPU';
+ } else {
+ try {
+ const [vendorData, deviceData] = await Promise.all([
+ readFile(`${devicePath}/vendor`, 'utf8').catch(() => ''),
+ readFile(`${devicePath}/device`, 'utf8').catch(() => ''),
+ ]);
+
+ const vendorId = vendorData.trim();
+ const deviceId = deviceData.trim();
+
+ if (vendorId === '0x1002') {
+ deviceName = 'AMD GPU';
+ } else if (vendorId === '0x10de') {
+ deviceName = 'NVIDIA GPU';
+ } else if (vendorId === '0x8086') {
+ deviceName = 'Intel GPU';
+ } else {
+ deviceName = `GPU (${vendorId}:${deviceId})`;
+ }
+ } catch {
+ void 0;
+ }
}
} catch {
void 0;
}
+
+ try {
+ const memTotalData = await readFile(
+ `${devicePath}/mem_info_vram_total`,
+ 'utf8'
+ );
+ const memoryTotal = Math.max(
+ 0,
+ (parseInt(memTotalData.trim(), 10) || 0) / (1024 * 1024 * 1024)
+ );
+
+ if (memoryTotal > 0) {
+ gpus.push({
+ deviceName,
+ devicePath,
+ memoryTotal,
+ });
+ }
+ } catch {
+ continue;
+ }
}
- try {
- const usageData = await readFile(
- `${devicePath}/gpu_busy_percent`,
- 'utf8'
- );
- const memUsedData = await readFile(
- `${devicePath}/mem_info_vram_used`,
- 'utf8'
- );
- const memTotalData = await readFile(
- `${devicePath}/mem_info_vram_total`,
- 'utf8'
- );
+ linuxGpuCache = gpus;
+ linuxCachePromise = null;
+ return gpus;
+ } catch {
+ linuxGpuCache = [];
+ linuxCachePromise = null;
+ return [];
+ }
+ })();
- const usage = parseInt(usageData.trim(), 10) || 0;
- const memoryUsed =
- (parseInt(memUsedData.trim(), 10) || 0) / (1024 * 1024 * 1024);
- const memoryTotal =
- (parseInt(memTotalData.trim(), 10) || 0) / (1024 * 1024 * 1024);
+ return linuxCachePromise;
+}
+
+async function getLinuxGPUData() {
+ try {
+ const cachedGPUs = await initializeLinuxGPUCache();
+
+ const gpus: GPUData[] = [];
+ for (const cachedGPU of cachedGPUs) {
+ try {
+ const [usageData, memUsedData] = await Promise.all([
+ readFile(`${cachedGPU.devicePath}/gpu_busy_percent`, 'utf8'),
+ readFile(`${cachedGPU.devicePath}/mem_info_vram_used`, 'utf8'),
+ ]);
+
+ const usage = Math.max(
+ 0,
+ Math.min(100, parseInt(usageData.trim(), 10) || 0)
+ );
+ const memoryUsed = Math.max(
+ 0,
+ (parseInt(memUsedData.trim(), 10) || 0) / (1024 * 1024 * 1024)
+ );
gpus.push({
- deviceName,
+ deviceName: cachedGPU.deviceName,
usage,
- memoryUsed,
- memoryTotal,
+ memoryUsed: parseFloat(memoryUsed.toFixed(2)),
+ memoryTotal: parseFloat(
+ Math.max(0, cachedGPU.memoryTotal).toFixed(2)
+ ),
});
} catch {
continue;
diff --git a/yarn.lock b/yarn.lock
index c898897..9bcc3c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1287,12 +1287,12 @@ __metadata:
languageName: node
linkType: hard
-"@types/node@npm:*, @types/node@npm:^24.5.0":
- version: 24.5.0
- resolution: "@types/node@npm:24.5.0"
+"@types/node@npm:*, @types/node@npm:^24.5.1":
+ version: 24.5.1
+ resolution: "@types/node@npm:24.5.1"
dependencies:
undici-types: "npm:~7.12.0"
- checksum: 10c0/c5beff68481e2cc667279a1478b34a1cfd048dbff914219cb5888967938d134907836b6c4d6d141dc862489cb09ef28f7d446c7a3b475181fd126c0fcd2916fa
+ checksum: 10c0/5f0cb038be789b58170e616452ba1f8ebb85bf2fbce58a7e32b1eb08391f64f5e31a9cdbccefbfcd9e6d73b66b564b5e037a1d678ab20213559a32e1d7b6ce17
languageName: node
linkType: hard
@@ -2705,16 +2705,16 @@ __metadata:
languageName: node
linkType: hard
-"electron@npm:^38.1.0":
- version: 38.1.0
- resolution: "electron@npm:38.1.0"
+"electron@npm:^38.1.1":
+ version: 38.1.1
+ resolution: "electron@npm:38.1.1"
dependencies:
"@electron/get": "npm:^2.0.0"
"@types/node": "npm:^22.7.7"
extract-zip: "npm:^2.0.1"
bin:
electron: cli.js
- checksum: 10c0/186082b15862b0e9617828ca395a79311ebe2a713bb2eb15a67cf6bea208e8cda892395a143e72915e189088ac71e91b29c1a46a0c4d343022cbc33004d4d302
+ checksum: 10c0/0b1e5955679de6b9a9a12fa7a51beaaa5dcf15655888670c39e3a49914f8dba580a1f94b46d83e7f8e5dc7fd22c16b130dd5040208d7a86431cf06e16933d9e8
languageName: node
linkType: hard
@@ -3648,7 +3648,7 @@ __metadata:
"@fontsource/inter": "npm:^5.2.7"
"@mantine/core": "npm:^8.3.1"
"@mantine/hooks": "npm:^8.3.1"
- "@types/node": "npm:^24.5.0"
+ "@types/node": "npm:^24.5.1"
"@types/react": "npm:^19.1.13"
"@types/react-dom": "npm:^19.1.9"
"@types/yauzl": "npm:^2.10.3"
@@ -3657,7 +3657,7 @@ __metadata:
"@vitejs/plugin-react": "npm:^5.0.2"
axios: "npm:^1.12.2"
cross-env: "npm:^10.0.0"
- electron: "npm:^38.1.0"
+ electron: "npm:^38.1.1"
electron-builder: "npm:^26.0.12"
electron-vite: "npm:^4.0.0"
eslint: "npm:^9.35.0"
@@ -3676,7 +3676,7 @@ __metadata:
react-dom: "npm:^19.1.1"
react-error-boundary: "npm:^6.0.0"
rollup-plugin-visualizer: "npm:^6.0.3"
- systeminformation: "npm:^5.27.9"
+ systeminformation: "npm:^5.27.10"
typescript: "npm:^5.9.2"
vite: "npm:^7.1.5"
winston: "npm:^3.17.0"
@@ -6694,12 +6694,12 @@ __metadata:
languageName: node
linkType: hard
-"systeminformation@npm:^5.27.9":
- version: 5.27.9
- resolution: "systeminformation@npm:5.27.9"
+"systeminformation@npm:^5.27.10":
+ version: 5.27.10
+ resolution: "systeminformation@npm:5.27.10"
bin:
systeminformation: lib/cli.js
- checksum: 10c0/d65aeb68e40d432fb1ec6e1723fa147959cbeb1b43a25c2014b768f0ee099266da6370b3f1593a6a7ca77643f58c9b754d470d225a7afd466d1108c846a67a3c
+ checksum: 10c0/aaaafb3d5738150e4d63908b4516f712810ed9047dc69ba061eacb1c2aa2824bfc352a1dff596e9d81db8a75c2f27da1a3e5daaa88fffb658a3f5a68288a1d02
conditions: (os=darwin | os=linux | os=win32 | os=freebsd | os=openbsd | os=netbsd | os=sunos | os=android)
languageName: node
linkType: hard