- Scaling 16px base pour ultrawide 3440x1440 - Bar: CPU/RAM/GPU visible, media single player (skip playerctld), network tooltip LAN/WAN IPv4 - Volume: class module pour sizing cohérent - Battery: désactivé (PC fixe) - Clock: tooltip calendrier + uptime - BrainPower: panel enrichi (focus, session, intentions, todos, repos git, derniers commits) - App: BrainPower sur moniteur principal uniquement - Heartbeat: Layer.TOP pour compatibilité COSMIC
90 lines
2.6 KiB
TypeScript
90 lines
2.6 KiB
TypeScript
import GLib from "gi://GLib"
|
|
import { createPoll } from "ags/time"
|
|
import { exec } from "ags/process"
|
|
|
|
function readProc(path: string): string | null {
|
|
try {
|
|
const [ok, contents] = GLib.file_get_contents(path)
|
|
if (ok && contents) return new TextDecoder().decode(contents)
|
|
} catch {}
|
|
return null
|
|
}
|
|
|
|
let prevIdle = 0
|
|
let prevTotal = 0
|
|
|
|
function getCpuUsage(): string {
|
|
const content = readProc("/proc/stat")
|
|
if (!content) return "?"
|
|
|
|
const line = content.split("\n")[0]
|
|
const parts = line.split(/\s+/).slice(1).map(Number)
|
|
const idle = parts[3] + parts[4]
|
|
const total = parts.reduce((a, b) => a + b, 0)
|
|
|
|
const dIdle = idle - prevIdle
|
|
const dTotal = total - prevTotal
|
|
prevIdle = idle
|
|
prevTotal = total
|
|
|
|
if (dTotal === 0) return "0"
|
|
return `${Math.round(((dTotal - dIdle) / dTotal) * 100)}`
|
|
}
|
|
|
|
function getRamUsage(): { pct: string; used: string; total: string } {
|
|
const content = readProc("/proc/meminfo")
|
|
if (!content) return { pct: "?", used: "?", total: "?" }
|
|
|
|
const lines = content.split("\n")
|
|
let totalKb = 0, availableKb = 0
|
|
|
|
for (const line of lines) {
|
|
if (line.startsWith("MemTotal:")) totalKb = parseInt(line.split(/\s+/)[1])
|
|
if (line.startsWith("MemAvailable:")) availableKb = parseInt(line.split(/\s+/)[1])
|
|
if (totalKb && availableKb) break
|
|
}
|
|
|
|
if (!totalKb) return { pct: "?", used: "?", total: "?" }
|
|
const usedGb = ((totalKb - availableKb) / 1048576).toFixed(1)
|
|
const totalGb = (totalKb / 1048576).toFixed(0)
|
|
const pct = `${Math.round(((totalKb - availableKb) / totalKb) * 100)}`
|
|
return { pct, used: usedGb, total: totalGb }
|
|
}
|
|
|
|
function getGpuInfo(): string {
|
|
try {
|
|
const out = exec("bash -c \"nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,noheader,nounits 2>/dev/null\"")
|
|
const [usage, temp] = out.trim().split(", ")
|
|
return `GPU ${usage}% · ${temp}°C`
|
|
} catch {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
export default function SystemStats() {
|
|
const cpu = createPoll("0", 3000, () => getCpuUsage())
|
|
const ram = createPoll("", 5000, () => {
|
|
const r = getRamUsage()
|
|
return JSON.stringify(r)
|
|
})
|
|
|
|
const ramLabel = ram((v: string) => {
|
|
try {
|
|
const r = JSON.parse(v)
|
|
return `RAM ${r.used}/${r.total}G`
|
|
} catch { return "RAM ?" }
|
|
})
|
|
|
|
const gpu = createPoll("", 5000, () => getGpuInfo())
|
|
|
|
return (
|
|
<box>
|
|
<label class="cpu module" label={cpu((v: string) => `CPU ${v}%`)} />
|
|
<label class="separator" label="│" />
|
|
<label class="ram module" label={ramLabel} />
|
|
<label class="separator" label="│" />
|
|
<label class="gpu module" label={gpu((v: string) => v || "GPU ?")} />
|
|
</box>
|
|
)
|
|
}
|