diff --git a/posimai-dev/station.html b/posimai-dev/station.html
index 88107181..5f752e38 100644
--- a/posimai-dev/station.html
+++ b/posimai-dev/station.html
@@ -83,11 +83,11 @@
.metric-header-row { display:flex;justify-content:space-between;align-items:baseline; }
.metric-label { font-size:12px;color:var(--text2); }
.metric-val { font-family:'JetBrains Mono',monospace;font-size:12px;font-weight:500; }
- .metric-bin { font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:0.18em;color:rgba(34,211,238,0.35);margin-top:2px;transition:color 0.4s; }
- .bar-track { height:4px;border-radius:2px;background:rgba(255,255,255,0.05);overflow:hidden; }
- .bar-fill { height:100%;border-radius:2px;background:var(--accent);transition:width 0.8s cubic-bezier(0.4,0,0.2,1),background 0.4s; }
- .bar-fill.warn { background:var(--warn); }
- .bar-fill.crit { background:var(--crit); }
+ .bin-bar { font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:0.04em;display:flex;gap:0px;margin-top:2px; }
+ .bin-bar .b1 { color:var(--accent);transition:color 0.5s; }
+ .bin-bar .b0 { color:rgba(255,255,255,0.1); }
+ .bin-bar.warn .b1 { color:var(--warn); }
+ .bin-bar.crit .b1 { color:var(--crit); }
.load-row { display:flex;gap:6px;flex-shrink:0; }
.load-chip { flex:1;background:var(--surface2);border-radius:7px;padding:7px;text-align:center; }
@@ -144,8 +144,8 @@
.service-uptime.full { color:var(--ok); }
.service-uptime.partial { color:#FB923C; }
.service-uptime.down { color:var(--crit); }
- .latency-bar-wrap { height:3px;border-radius:2px;background:rgba(255,255,255,0.07);overflow:hidden;margin-top:4px; }
- .latency-bar { height:100%;border-radius:2px;transition:width 0.6s ease; }
+ .svc-spark-wrap { height:28px;margin:2px 0; }
+ .svc-spark { width:100%;height:100%;overflow:visible; }
/* stream */
#stream-feed { flex:1;overflow:hidden;display:flex;flex-direction:column;gap:0; }
@@ -162,7 +162,7 @@
.s-bar-fill.warn { background:var(--warn); }
.s-bar-fill.crit { background:var(--crit); }
#stream-ticker { font-family:'JetBrains Mono',monospace;font-size:9px;color:var(--text3);padding-top:7px;overflow:hidden;white-space:nowrap;border-top:1px solid var(--border2);flex-shrink:0; }
- #stream-ticker-inner { display:inline-block;animation:ticker 30s linear infinite; }
+ #stream-ticker-inner { display:inline-block;animation:ticker 60s linear infinite; }
@keyframes ticker { from{transform:translateX(0)} to{transform:translateX(-50%)} }
#bottom { display:flex;align-items:center;justify-content:space-between;padding-top:12px;border-top:1px solid var(--border); }
@@ -200,18 +200,15 @@
Ubuntu PC
Load Avg
@@ -328,7 +325,8 @@ const SERVICES = [
];
const hist = {cpu:[], load:[]};
const svcHist = {};
-SERVICES.forEach(s => svcHist[s.id] = []);
+const svcLatHist = {};
+SERVICES.forEach(s => { svcHist[s.id] = []; svcLatHist[s.id] = []; });
let streamData = null;
function p(n){ return String(n).padStart(2,'0'); }
@@ -396,15 +394,12 @@ async function fetchHealth(){
window._cpuCount=cpuCount;
document.getElementById('cpu-val').textContent=`${cpuPct}%`;
- const cb=document.getElementById('cpu-bar');cb.style.width=`${cpuPct}%`;cb.className='bar-fill'+(cpuPct>80?' crit':cpuPct>60?' warn':'');
- document.getElementById('cpu-bin').textContent=cpuPct.toString(2).padStart(8,'0');
+ renderBinBar('cpu-bar',cpuPct,60,80);
document.getElementById('mem-val').textContent=`${data.mem_used_mb}/${data.mem_total_mb}MB (${memPct}%)`;
- const mb=document.getElementById('mem-bar');mb.style.width=`${memPct}%`;mb.className='bar-fill'+(memPct>85?' crit':memPct>65?' warn':'');
- document.getElementById('mem-bin').textContent=memPct.toString(2).padStart(8,'0');
+ renderBinBar('mem-bar',memPct,65,85);
if(data.disk){
document.getElementById('disk-val').textContent=`${data.disk.used_gb}/${data.disk.total_gb}GB (${diskPct}%)`;
- const db=document.getElementById('disk-bar');db.style.width=`${diskPct}%`;db.className='bar-fill'+(diskPct>90?' crit':diskPct>75?' warn':'');
- document.getElementById('disk-bin').textContent=diskPct.toString(2).padStart(8,'0');
+ renderBinBar('disk-bar',diskPct,75,90);
}
document.getElementById('cpu-count-label').textContent=`(core:${cpuCount})`;
['load-1','load-5','load-15'].forEach((id,i)=>{
@@ -453,17 +448,50 @@ function buildServiceCards(){
SERVICES.forEach(svc=>{
const card=document.createElement('div'); card.className='service-card'; card.id=`svc-${svc.id}`;
const dots=Array(5).fill(0).map((_,i)=>`
`).join('');
- card.innerHTML=`
${svc.name}...
${svc.desc}`;
+ card.innerHTML=`
${svc.name}...
${svc.desc}`;
grid.appendChild(card);
});
}
-function updateLatencyBar(id,ms){
- const bar=document.getElementById(`lbar-${id}`); if(!bar)return;
- if(ms===null){bar.style.width='100%';bar.style.background='var(--crit)';return;}
- const w=ms<=50?100:ms<=200?80:ms<=500?55:35;
- const color=ms<=200?'var(--ok)':ms<=500?'#FB923C':'var(--crit)';
- bar.style.width=w+'%'; bar.style.background=color;
+function renderBinBar(id,pct,warnTh,critTh){
+ const el=document.getElementById(id); if(!el)return;
+ const cells=20,filled=Math.round(pct/100*cells);
+ const cls=pct>critTh?'crit':pct>warnTh?'warn':'';
+ el.className='bin-bar'+(cls?' '+cls:'');
+ el.innerHTML=Array.from({length:cells},(_,i)=>
+ `
${i`
+ ).join('');
+}
+
+function updateSparkline(id, ms, ok){
+ const h = svcLatHist[id];
+ h.push(ok ? (ms||0) : null);
+ if(h.length > 12) h.shift();
+ const svg = document.getElementById(`spark-${id}`);
+ if(!svg || h.length < 2) return;
+ const valid = h.filter(v => v !== null);
+ if(valid.length < 2){ svg.innerHTML=''; return; }
+ const maxV = Math.max(...valid, 1);
+ const W=100, H=24, pad=2;
+ const pts = h.map((v,i) => {
+ const x = (i/(h.length-1))*W;
+ const y = v===null ? H-pad : H-pad - ((v/maxV)*(H-pad*2));
+ return `${x.toFixed(1)},${y.toFixed(1)}`;
+ }).join(' ');
+ const color = !ok ? 'rgba(248,113,113,0.7)' : maxV > 500 ? 'rgba(251,146,60,0.7)' : 'rgba(34,211,238,0.6)';
+ const area = h.map((v,i) => {
+ const x=(i/(h.length-1))*W;
+ const y=v===null?H-pad:H-pad-((v/maxV)*(H-pad*2));
+ return `${x.toFixed(1)},${y.toFixed(1)}`;
+ });
+ area.push(`${W},${H}`, `0,${H}`);
+ svg.innerHTML = `
+
+
+
+
+
+ `;
}
function pushSvcHistory(id,ok){
@@ -498,7 +526,7 @@ async function checkService(svc){
badge.className='service-badge '+(ok?'ok':'crit');
badge.textContent=ok?'OK':'DOWN';
latEl.textContent=ms?`${ms}ms`:'';
- updateLatencyBar(svc.id,ok?ms:null);
+ updateSparkline(svc.id,ms,!!ok);
pushSvcHistory(svc.id,!!ok);
}else{
await fetch(svc.url,{method:'HEAD',mode:'no-cors',signal:ctrl.signal});
@@ -506,7 +534,7 @@ async function checkService(svc){
const ms=Date.now()-t0;
badge.className='service-badge ok'; badge.textContent='OK';
latEl.textContent=`${ms}ms`;
- updateLatencyBar(svc.id,ms);
+ updateSparkline(svc.id,ms,true);
pushSvcHistory(svc.id,true);
}
}catch(e){
@@ -555,10 +583,21 @@ function updateStream(data){
const r=rows[Math.floor(Date.now()/1000)%rows.length];
pushStreamRow(r.label,r.value,r.bin,r.pct,r.level);
const ip='100.77.11.43';
- const ipBin=ip.split('.').map(o=>toBin(parseInt(o),8)).join(' ');
- const base=`${ipBin} // ${data.hostname||'ubuntu-pc'} // ${data.node_version||''} // `;
+ const b8 = n => n.toString(2).padStart(8,'0');
+ const segments = [
+ `CPU:${b8(cpuPct??0)}`,
+ `MEM:${b8(memPct??0)}`,
+ `DISK:${b8(diskPct??0)}`,
+ `LOAD:${b8(Math.min(255,Math.round((loadAvg[0]||0)*64)))}`,
+ `UP:${(data.uptime_s||0).toString(2)}`,
+ `SESSION:${b8(data.active_sessions||0)}`,
+ `TIME:${(Math.floor(Date.now()/1000)).toString(2).slice(-20)}`,
+ `IP:${ip.split('.').map(o=>b8(parseInt(o))).join('.')}`,
+ `HOST:${(data.hostname||'ubuntu-pc').split('').map(c=>b8(c.charCodeAt(0))).join(' ')}`,
+ ];
+ const tape = segments.join(' // ') + ' // ';
const el=document.getElementById('stream-ticker-inner');
- if(el)el.textContent=base+base;
+ if(el){ el.textContent=tape+tape; el.style.animation='none'; void el.offsetWidth; el.style.animation=''; }
}
setInterval(()=>{if(streamData)updateStream(streamData);},4000);