import QtQuick import Quickshell import Quickshell.Io QtObject { id: stats property var _prevCpu: null property int cpuUsage: 0 property int _memTotal: 0 property int _memAvail: 0 property int memUsage: 0 property string memInfo: "0/0" property int temperature: 0 property int updatesCount: 0 property string updatesList: "" property string cpuTemps: "" property var _cpuProc: Process { command: ["cat", "/proc/stat"] running: true stdout: SplitParser { splitMarker: "\n" onRead: data => { if (!data.startsWith("cpu ")) return let parts = data.trim().split(/\s+/).slice(1).map(Number) let idle = parts[3] + (parts[4] || 0) let total = parts.reduce((a, b) => a + b, 0) if (stats._prevCpu) { let di = idle - stats._prevCpu.idle let dt = total - stats._prevCpu.total stats.cpuUsage = dt > 0 ? Math.round(100 * (1 - di / dt)) : 0 } stats._prevCpu = { idle: idle, total: total } } } } property var _memProc: Process { command: ["cat", "/proc/meminfo"] running: true stdout: SplitParser { splitMarker: "\n" onRead: data => { let line = data.trim() if (line.startsWith("MemTotal:")) stats._memTotal = parseInt(line.split(/\s+/)[1]) || 0 else if (line.startsWith("MemAvailable:")) stats._memAvail = parseInt(line.split(/\s+/)[1]) || 0 } } onExited: (code, status) => { if (stats._memTotal > 0) { stats.memUsage = Math.round(100 * (1 - stats._memAvail / stats._memTotal)) let used = ((stats._memTotal - stats._memAvail) / 1048576).toFixed(1) let total = (stats._memTotal / 1048576).toFixed(1) stats.memInfo = used + "G/" + total + "G" } } } property var _tempProc: Process { command: [ "bash", "-c", "for f in /sys/class/hwmon/hwmon*/temp*_input; do " + " d=$(dirname \"$f\"); b=$(basename \"$f\"); " + " name=$(cat \"$d/name\" 2>/dev/null || echo unknown); " + " label=$(cat \"$d/${b%_input}_label\" 2>/dev/null || echo ''); " + " temp=$(($(cat \"$f\" 2>/dev/null)/1000)); " + " printf '%s|%s|%s\\n' \"$name\" \"$label\" \"$temp\"; " + "done 2>/dev/null" ] running: true stdout: StdioCollector { onDataChanged: { let lines = text.trim().split("\n").filter(l => l.length > 0) let tempLines = [] let maxTemp = 0 for (let i = 0; i < lines.length; i++) { let parts = lines[i].split("|") let name = parts[0] || "unknown" let label = parts[1] || "" let temp = parseInt(parts[2]) || 0 if (temp > 0) { if (temp <= 200) { let display = label ? label : name tempLines.push(display + ": " + temp + "°C") if (temp > maxTemp) maxTemp = temp } } } stats.temperature = maxTemp stats.cpuTemps = tempLines.join("\n") } } } property var _pollTimer: Timer { interval: 3000 running: true repeat: true onTriggered: { if (!stats._cpuProc.running) stats._cpuProc.running = true if (!stats._memProc.running) stats._memProc.running = true if (!stats._tempProc.running) stats._tempProc.running = true } } function checkUpdates() { if (!_updatesProc.running) _updatesProc.running = true } property var _updatesProc: Process { command: [ "bash", "-c", "(checkupdates 2>/dev/null; yay -Qua 2>/dev/null)" ] running: true stdout: StdioCollector { onDataChanged: { let lines = text.trim() let arr = lines ? lines.split("\n") : [] stats.updatesCount = arr.length stats.updatesList = arr.slice(0, 25).join("\n") + (arr.length > 25 ? "\n..." : "") } } } property var _updatesTimer: Timer { interval: 300000 running: true repeat: true onTriggered: { if (!stats._updatesProc.running) stats._updatesProc.running = true } } }