154 lines
13 KiB
HTML
154 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><title>NVIDIA GPU</title>
|
|
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate">
|
|
<link rel="stylesheet" href="../base1/cockpit.css">
|
|
<script src="../base1/cockpit.js"></script>
|
|
<style>
|
|
body{padding:0;font-family:var(--pf-t--global--font--family--body,Inter,RedHatText,Overpass,sans-serif);background:var(--pf-t--global--background--color--primary--default,#f0f2f5)}
|
|
.ct-page-fill{padding:1.5rem;max-width:1100px;margin:0 auto}
|
|
.page-header{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem;padding:1rem 1.25rem;background:var(--pf-t--global--background--color--secondary--default,linear-gradient(135deg,#0f172a,#1e293b));border-radius:14px;border:1px solid var(--pf-t--global--border--color--default,rgba(255,255,255,0.08));box-shadow:0 2px 12px rgba(0,0,0,0.06)}
|
|
.page-header h1{font-size:1.15rem;font-weight:700;margin:0;background:linear-gradient(135deg,#38bdf8,#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
|
.page-header .subtitle{font-size:0.7rem;color:var(--pf-t--global--text--color--subtle,#94a3b8);margin-left:auto;font-family:"SF Mono","JetBrains Mono","Fira Code",monospace;background:var(--pf-t--global--background--color--secondary--default,rgba(255,255,255,0.06));padding:0.2rem 0.6rem;border-radius:6px}
|
|
.status-dot{width:10px;height:10px;border-radius:50%;display:inline-block;position:relative}
|
|
.status-dot::after{content:'';position:absolute;inset:-4px;border-radius:50%;opacity:0.25}
|
|
.status-dot.ok{background:#22c55e;box-shadow:0 0 10px #22c55e;animation:pulse-dot 2s ease-in-out infinite}
|
|
.status-dot.warn{background:#f59e0b} .status-dot.err{background:#ef4444}
|
|
@keyframes pulse-dot{0%,100%{opacity:0.25}50%{opacity:0.6}}
|
|
.grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem}
|
|
@media(max-width:900px){.grid{grid-template-columns:1fr 1fr}}
|
|
@media(max-width:600px){.grid{grid-template-columns:1fr}}
|
|
.card{background:var(--pf-t--global--background--color--primary--default,#fff);border:1px solid var(--pf-t--global--border--color--default,#e2e8f0);border-radius:12px;padding:1.25rem;transition:box-shadow 0.2s}
|
|
.card:hover{box-shadow:0 4px 20px rgba(0,0,0,0.06)}
|
|
.card.full{grid-column:1/-1}
|
|
.card.accent-blue{border-top:3px solid #3b82f6}.card.accent-green{border-top:3px solid #22c55e}
|
|
.card.accent-orange{border-top:3px solid #f59e0b}.card.accent-purple{border-top:3px solid #a855f7}
|
|
.card.accent-red{border-top:3px solid #ef4444}.card.accent-cyan{border-top:3px solid #06b6d4}
|
|
.card h2{font-size:0.65rem;text-transform:uppercase;letter-spacing:0.06em;color:var(--pf-t--global--text--color--subtle,#94a3b8);margin:0 0 0.85rem 0;font-weight:700}
|
|
.stat{display:flex;justify-content:space-between;align-items:center;padding:0.45rem 0}
|
|
.stat+.stat{border-top:1px solid var(--pf-t--global--border--color--default,rgba(0,0,0,0.04))}
|
|
.stat-label{color:var(--pf-t--global--text--color--subtle,#64748b);font-size:0.82rem;font-weight:500}
|
|
.stat-value{font-weight:600;font-family:"SF Mono","JetBrains Mono","Fira Code",monospace;font-size:0.85rem;color:var(--pf-t--global--text--color--default,#1e293b);white-space:nowrap}
|
|
.temp-group{display:flex;align-items:center;gap:1.5rem;justify-content:center;flex-wrap:wrap;margin:0.3rem 0 0.5rem}
|
|
.temp-big{font-size:2.5rem;font-weight:300;font-family:"SF Mono";line-height:1;display:flex;align-items:baseline;gap:2px}
|
|
.temp-big .unit{font-size:1rem;color:#94a3b8}
|
|
.temp-cold{color:#3b82f6}.temp-mild{color:#22c55e}.temp-warm{color:#f59e0b}.temp-hot{color:#ef4444}
|
|
.temp-mini{display:flex;flex-direction:column;gap:0.2rem;min-width:70px}
|
|
.temp-mini-item{display:flex;justify-content:space-between;font-size:0.78rem;color:#64748b}
|
|
.temp-mini-item .val{font-family:"SF Mono";font-weight:600}
|
|
.bar-track{background:#e2e8f0;border-radius:6px;height:6px;overflow:hidden;margin:0.4rem 0}
|
|
.bar-fill{height:100%;border-radius:6px;transition:width 0.6s}
|
|
.bar-fill.green{background:linear-gradient(90deg,#22c55e,#16a34a)}
|
|
.bar-fill.amber{background:linear-gradient(90deg,#f59e0b,#d97706)}
|
|
.bar-fill.red{background:linear-gradient(90deg,#ef4444,#dc2626)}
|
|
.bar-fill.temp{background:linear-gradient(90deg,#3b82f6,#22c55e,#f59e0b,#ef4444)}
|
|
.bar-labels{display:flex;justify-content:space-between;font-size:0.6rem;color:#94a3b8;margin-top:2px}
|
|
.mem-row{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:0.3rem}
|
|
.mem-used{font-size:1.3rem;font-weight:600;font-family:"SF Mono";color:#1e293b}
|
|
.mem-of{font-size:0.78rem;color:#94a3b8}
|
|
.mem-pct{text-align:right;font-size:0.78rem;color:#94a3b8;font-family:"SF Mono";margin-top:2px}
|
|
.power-block{text-align:center;padding:0.4rem 0}
|
|
.power-num{font-size:1.6rem;font-weight:300;font-family:"SF Mono";color:#1e293b}
|
|
.power-label{font-size:0.68rem;color:#94a3b8;margin-top:0.1rem}
|
|
.model-card{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0.4rem;text-align:center}
|
|
.model-card .name{font-weight:700;font-size:0.9rem;color:#1e293b}
|
|
.model-card .sub{font-size:0.72rem;color:#94a3b8}
|
|
table{width:100%;border-collapse:collapse;font-size:0.82rem}
|
|
th{text-align:left;padding:0.45rem 0.5rem;color:#94a3b8;font-weight:600;font-size:0.65rem;text-transform:uppercase;border-bottom:2px solid #e2e8f0}
|
|
td{padding:0.45rem 0.5rem;border-bottom:1px solid rgba(0,0,0,0.03)}
|
|
td:last-child{text-align:right;font-family:"SF Mono";font-weight:600}
|
|
tr:hover td{background:rgba(0,0,0,0.015)}
|
|
.footer-row{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1rem;padding:0.5rem 1rem;background:rgba(0,0,0,0.015);border:1px solid rgba(0,0,0,0.04);border-radius:8px;font-size:0.72rem;color:#94a3b8}
|
|
.alert-box{display:none;background:linear-gradient(135deg,#fef2f2,#fff5f5);border:1px solid #fecaca;color:#991b1b;padding:0.9rem 1.25rem;border-radius:10px;margin-bottom:1rem;font-size:0.85rem;align-items:center;gap:0.6rem}
|
|
@keyframes fadeUp{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
|
|
.grid>*{animation:fadeUp 0.3s ease-out both}
|
|
.grid>*:nth-child(1){animation-delay:0s}
|
|
.grid>*:nth-child(2){animation-delay:0.04s}
|
|
.grid>*:nth-child(3){animation-delay:0.08s}
|
|
.grid>*:nth-child(4){animation-delay:0.12s}
|
|
.grid>*:nth-child(5){animation-delay:0.16s}
|
|
.grid>*:nth-child(6){animation-delay:0.20s}
|
|
.grid>*:nth-child(7){animation-delay:0.24s}
|
|
</style></head>
|
|
<body>
|
|
<div class="ct-page-fill">
|
|
<div id="nogpu" class="alert-box"><span class="icon">⚠</span> No NVIDIA GPU detected</div>
|
|
<div class="page-header"><span class="status-dot" id="sdot"></span><h1>🎮 <span id="gpumodel">NVIDIA GPU</span></h1><span class="subtitle" id="pstate">P?</span></div>
|
|
<div id="content"><div class="grid">
|
|
<!-- Thermal -->
|
|
<div class="card accent-blue"><h2>🌡 Thermal</h2>
|
|
<div class="temp-group"><div class="temp-big"><span class="temp-cold" id="gt">--</span><span class="unit">°C</span></div>
|
|
<div class="temp-mini"><div class="temp-mini-item"><span>Fan</span><span class="val" id="gf">—</span></div><div class="temp-mini-item"><span>Util</span><span class="val" id="gu">-</span></div></div></div>
|
|
<div class="bar-track"><div class="bar-fill temp" id="tbar" style="width:0%"></div></div>
|
|
<div class="bar-labels"><span>0°</span><span>50°</span><span>75°</span><span>100°</span></div></div>
|
|
<!-- CPU -->
|
|
<div class="card accent-cyan"><h2>🔥 CPU</h2>
|
|
<div class="temp-group"><div class="temp-big"><span class="temp-cold" id="cpu-temp">--</span><span class="unit">°C</span></div>
|
|
<div class="temp-mini"><div class="temp-mini-item"><span>PCH</span><span class="val" id="pch-temp">-</span></div><div class="temp-mini-item"><span>ACPI</span><span class="val" id="acpi-temp">-</span></div></div></div>
|
|
<div class="bar-track"><div class="bar-fill temp" id="cpu-bar" style="width:0%"></div></div>
|
|
<div class="bar-labels"><span>0°</span><span>50°</span><span>75°</span><span>100°</span></div></div>
|
|
<!-- Memory -->
|
|
<div class="card accent-orange"><h2>💾 Memory</h2>
|
|
<div class="mem-row"><span class="mem-used" id="mu">-</span><span class="mem-of">of <span id="mt">-</span></span></div>
|
|
<div class="bar-track" style="height:8px"><div class="bar-fill green" id="mbar" style="width:0%"></div></div>
|
|
<div class="mem-pct" id="mpct">-</div></div>
|
|
<!-- Details -->
|
|
<div class="card accent-purple"><h2>🔧 Details</h2>
|
|
<div class="stat"><span class="stat-label">Driver</span><span class="stat-value" id="dv">-</span></div>
|
|
<div class="stat"><span class="stat-label">CUDA Version</span><span class="stat-value" id="cv">-</span></div>
|
|
<div class="stat"><span class="stat-label">Perf State</span><span class="stat-value" id="gp">-</span></div>
|
|
<div class="stat"><span class="stat-label">Fan Speed</span><span class="stat-value" id="gf2">-</span></div></div>
|
|
<!-- Power -->
|
|
<div class="card accent-red"><h2>⚡ Power</h2>
|
|
<div class="power-block"><div class="power-num" id="pw">N/A</div><div class="power-label">Power Draw</div></div></div>
|
|
<!-- GPU Load -->
|
|
<div class="card accent-green"><h2>📊 GPU Load</h2>
|
|
<div class="stat"><span class="stat-label">Utilization</span><span class="stat-value" id="gu2">0%</span></div></div>
|
|
<!-- Model -->
|
|
<div class="card model-card"><div class="name" id="gpmodel2">-</div><div class="sub" id="driverver">-</div></div>
|
|
<!-- Processes -->
|
|
<div class="card full"><h2>🔄 Active Processes</h2>
|
|
<table><thead><tr><th>PID</th><th>Process</th><th>Memory</th></tr></thead>
|
|
<tbody id="pl"><tr><td colspan="3" style="text-align:center;color:#94a3b8;padding:1rem 0">No GPU processes running</td></tr></tbody></table></div>
|
|
<!-- Footer -->
|
|
<div class="full footer-row"><span>🕒 <span id="lu">-</span></span><span id="modelright" style="opacity:0.55">-</span></div>
|
|
</div></div></div>
|
|
<script>
|
|
(function(){
|
|
function $(i){return document.getElementById(i);}
|
|
function fmt(m){var v=parseInt(m);if(isNaN(v))return m+' MiB';return v>=1024?(v/1024).toFixed(1)+' GiB':v+' MiB';}
|
|
var PKG="/usr/share/cockpit/nvidia-gpu";
|
|
function update(){
|
|
var ts=new Date().toLocaleTimeString();
|
|
cockpit.spawn(["cat",PKG+"/gpu.txt"],{err:"message"}).done(function(data){
|
|
if(!data||!data.trim()){$('nogpu').style.display='flex';$('content').style.display='none';$('sdot').className='status-dot err';return;}
|
|
$('nogpu').style.display='none';$('content').style.display='block';var p=data.trim().split(', ');if(p.length<9)return;
|
|
var model=p[0];$('gpumodel').textContent=model;$('gpmodel2').textContent=model;$('sdot').className='status-dot ok';
|
|
$('dv').textContent=p[1];$('driverver').textContent='Driver '+p[1];$('modelright').textContent=model+' — Driver '+p[1];
|
|
var t=parseInt(p[2]);var tdisp=$('gt');
|
|
if(!isNaN(t)){tdisp.textContent=t;var tcls='temp-cold';if(t>=50)tcls='temp-mild';if(t>=65)tcls='temp-warm';if(t>=80)tcls='temp-hot';
|
|
tdisp.className=tcls;var tp=Math.min(100,Math.max(0,t));$('tbar').style.width=tp+'%';
|
|
$('sdot').className='status-dot '+(t<50?'ok':(t<75?'warn':'err'));}
|
|
var uPct=parseInt(p[3])||0;$('gu').textContent=uPct+'%';$('gu2').textContent=uPct+'%';
|
|
var uMem=parseInt(p[4])||0;var tMem=parseInt(p[5])||1;$('mu').textContent=fmt(uMem);$('mt').textContent=fmt(tMem);
|
|
var mp=Math.round(uMem/tMem*100);$('mbar').style.width=mp+'%';$('mbar').className='bar-fill '+(mp<60?'green':(mp<85?'amber':'red'));$('mpct').textContent=mp+'%';
|
|
$('pw').textContent=p[6]||'N/A';$('gp').textContent=p[7]||'-';$('pstate').textContent=(p[7]||'').trim();
|
|
var fan=p[8]?p[8].trim():'0';var fanDisp=(fan==='[N/A]'||fan==='0')?'—':fan+'%';$('gf').textContent=fanDisp;$('gf2').textContent=fanDisp;
|
|
}).fail(function(){$('nogpu').style.display='flex';$('content').style.display='none';$('sdot').className='status-dot err';});
|
|
cockpit.spawn(["cat",PKG+"/cpu.txt"],{err:"message"}).done(function(d){
|
|
if(d&&d.trim()){var s=d.trim().split(',');if(s.length>=3){var cp=parseInt(s[0]);$('cpu-temp').textContent=isNaN(cp)?'--':cp;
|
|
$('pch-temp').textContent=s[1]+'°';$('acpi-temp').textContent=s[2]+'°';
|
|
var tp=Math.min(100,Math.max(0,cp));$('cpu-bar').style.width=tp+'%';
|
|
var tc='temp-cold';if(cp>=50)tc='temp-mild';if(cp>=65)tc='temp-warm';if(cp>=80)tc='temp-hot';$('cpu-temp').className=tc;}}});
|
|
cockpit.spawn(["cat",PKG+"/cuda.txt"],{err:"message"}).done(function(d){if(d&&d.trim())$('cv').textContent=d.trim();});
|
|
cockpit.spawn(["cat",PKG+"/processes.txt"],{err:"message"}).done(function(d){
|
|
if(!d||!d.trim()){$('pl').innerHTML='<tr><td colspan="3" style="text-align:center;color:#94a3b8;padding:1rem 0">No GPU processes running</td></tr>';return;}
|
|
var ls=d.trim().split('\\n').filter(function(l){return l.trim();});
|
|
if(!ls.length){$('pl').innerHTML='<tr><td colspan="3" style="text-align:center;color:#94a3b8;padding:1rem 0">No GPU processes running</td></tr>';return;}
|
|
var h='';ls.forEach(function(l){var s=l.split(', ');if(s.length>=3)h+='<tr><td>'+s[0]+'</td><td>'+s[1]+'</td><td>'+fmt(s[2])+'</td></tr>';});$('pl').innerHTML=h;});
|
|
$('lu').textContent=ts;
|
|
}
|
|
update();setInterval(update,5000);
|
|
})();
|
|
</script></body></html>
|