Compact sentinel card: single-line with mini ring + collapsible verdicts
- Entire sentinel status fits in one header row now - Mini 28px countdown ring (was 64px) inline with title - Scans/bans counts inline as text, not grid boxes - Verdicts collapsed by default — click to expand - Card padding reduced (8px vs 14px) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3cdfc01835
commit
357918013d
@ -740,7 +740,7 @@ async function loadThreats() {
|
||||
|
||||
// Sentinel status card
|
||||
var sentinelCard = document.createElement('div');
|
||||
sentinelCard.style.cssText = 'background:rgba(0,0,0,0.3);border:2px solid rgba(217,70,239,0.3);border-radius:2px;padding:14px;margin-bottom:16px;backdrop-filter:blur(16px)';
|
||||
sentinelCard.style.cssText = 'background:rgba(0,0,0,0.3);border:2px solid rgba(217,70,239,0.3);border-radius:2px;padding:8px 12px;margin-bottom:12px;backdrop-filter:blur(16px)';
|
||||
var sHeader = document.createElement('div');
|
||||
sHeader.style.cssText = 'display:flex;align-items:center;gap:8px;margin-bottom:8px';
|
||||
var sDot = document.createElement('div');
|
||||
@ -748,85 +748,61 @@ async function loadThreats() {
|
||||
var sTitle = document.createElement('span');
|
||||
sTitle.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:11px;text-transform:uppercase;letter-spacing:1.5px;color:#d946ef;font-weight:700';
|
||||
sTitle.textContent = 'AI Sentinel — ' + (sentinel.model || '?');
|
||||
sHeader.appendChild(sDot);sHeader.appendChild(sTitle);sentinelCard.appendChild(sHeader);
|
||||
sHeader.appendChild(sDot);sHeader.appendChild(sTitle);
|
||||
|
||||
// Countdown + metrics row
|
||||
// Inline stats + countdown — all in one row
|
||||
var ss = sentinel.stats || {};
|
||||
var nextIn = sentinel.next_scan_in || 0;
|
||||
var interval = sentinel.interval || 300;
|
||||
|
||||
var metricsRow = document.createElement('div');
|
||||
metricsRow.style.cssText = 'display:grid;grid-template-columns:auto 1fr;gap:14px;align-items:center;margin-bottom:10px';
|
||||
|
||||
// Countdown ring
|
||||
var ringWrap = document.createElement('div');
|
||||
ringWrap.style.cssText = 'position:relative;width:64px;height:64px;flex-shrink:0';
|
||||
var pct = interval > 0 ? ((interval - nextIn) / interval) : 0;
|
||||
var deg = Math.round(pct * 360);
|
||||
ringWrap.innerHTML = '<svg width="64" height="64" viewBox="0 0 64 64">'
|
||||
+ '<circle cx="32" cy="32" r="28" fill="none" stroke="#2a2d35" stroke-width="4"/>'
|
||||
+ '<circle cx="32" cy="32" r="28" fill="none" stroke="#d946ef" stroke-width="4" stroke-linecap="round"'
|
||||
+ ' stroke-dasharray="' + (2 * Math.PI * 28).toFixed(1) + '"'
|
||||
+ ' stroke-dashoffset="' + ((1 - pct) * 2 * Math.PI * 28).toFixed(1) + '"'
|
||||
+ ' transform="rotate(-90 32 32)" style="transition:stroke-dashoffset 1s"/>'
|
||||
+ '</svg>';
|
||||
var countText = document.createElement('div');
|
||||
countText.id = 'sentinel-countdown';
|
||||
countText.style.cssText = 'position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;font-family:JetBrains Mono,monospace';
|
||||
var countNum = document.createElement('div');
|
||||
countNum.style.cssText = 'font-size:16px;font-weight:700;color:#d946ef;line-height:1';
|
||||
countNum.textContent = Math.ceil(nextIn) + 's';
|
||||
var countLabel = document.createElement('div');
|
||||
countLabel.style.cssText = 'font-size:7px;color:#7a7872;text-transform:uppercase;letter-spacing:1px;margin-top:2px';
|
||||
countLabel.textContent = 'next scan';
|
||||
countText.appendChild(countNum); countText.appendChild(countLabel);
|
||||
ringWrap.appendChild(countText);
|
||||
metricsRow.appendChild(ringWrap);
|
||||
|
||||
// Stats grid
|
||||
var statsGrid = document.createElement('div');
|
||||
statsGrid.style.cssText = 'display:grid;grid-template-columns:repeat(4,1fr);gap:6px';
|
||||
[{v:ss.scans||0,l:'Scans',c:'#d946ef'},{v:ss.bans||0,l:'AI Bans',c:'#e05252'},{v:ss.last_run||'—',l:'Last Run',c:'#e8e6e3',small:true},{v:(sentinel.interval||300)+'s',l:'Interval',c:'#7a7872'}].forEach(function(m){
|
||||
var box = document.createElement('div');
|
||||
box.style.cssText = 'text-align:center';
|
||||
var val = document.createElement('div');
|
||||
val.style.cssText = 'font-family:JetBrains Mono,monospace;font-weight:700;color:'+m.c+';font-size:'+(m.small?'10px':'14px');
|
||||
val.textContent = m.v;
|
||||
var lab = document.createElement('div');
|
||||
lab.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:7px;text-transform:uppercase;letter-spacing:1px;color:#7a7872;margin-top:2px';
|
||||
lab.textContent = m.l;
|
||||
box.appendChild(val);box.appendChild(lab);statsGrid.appendChild(box);
|
||||
});
|
||||
metricsRow.appendChild(statsGrid);
|
||||
sentinelCard.appendChild(metricsRow);
|
||||
// Mini ring
|
||||
var ring = document.createElement('span');
|
||||
ring.style.cssText = 'position:relative;width:28px;height:28px;flex-shrink:0;display:inline-block;vertical-align:middle;margin-left:auto';
|
||||
ring.innerHTML = '<svg width="28" height="28" viewBox="0 0 28 28"><circle cx="14" cy="14" r="11" fill="none" stroke="#2a2d35" stroke-width="2.5"/><circle cx="14" cy="14" r="11" fill="none" stroke="#d946ef" stroke-width="2.5" stroke-linecap="round" stroke-dasharray="'+(2*Math.PI*11).toFixed(1)+'" stroke-dashoffset="'+((1-pct)*2*Math.PI*11).toFixed(1)+'" transform="rotate(-90 14 14)" style="transition:stroke-dashoffset 1s"/></svg>';
|
||||
var countTxt = document.createElement('span');
|
||||
countTxt.id = 'sentinel-countdown';
|
||||
countTxt.style.cssText = 'position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-family:JetBrains Mono,monospace;font-size:8px;font-weight:700;color:#d946ef';
|
||||
countTxt.textContent = Math.ceil(nextIn) + '';
|
||||
ring.appendChild(countTxt);
|
||||
sHeader.appendChild(ring);
|
||||
|
||||
// Start countdown timer
|
||||
// Compact stats inline
|
||||
var inlineStats = document.createElement('span');
|
||||
inlineStats.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:9px;color:#7a7872;display:flex;gap:10px;margin-left:8px';
|
||||
inlineStats.innerHTML = '<span><b style="color:#d946ef">'+(ss.scans||0)+'</b> scans</span><span><b style="color:#e05252">'+(ss.bans||0)+'</b> bans</span>';
|
||||
sHeader.appendChild(inlineStats);
|
||||
|
||||
sentinelCard.appendChild(sHeader);
|
||||
|
||||
// Start countdown
|
||||
if (window._sentinelTimer) clearInterval(window._sentinelTimer);
|
||||
window._sentinelCountdown = nextIn;
|
||||
window._sentinelTimer = setInterval(function(){
|
||||
window._sentinelCountdown = Math.max(0, window._sentinelCountdown - 1);
|
||||
var el = document.getElementById('sentinel-countdown');
|
||||
if (el) el.querySelector('div').textContent = Math.ceil(window._sentinelCountdown) + 's';
|
||||
if (window._sentinelCountdown <= 0) {
|
||||
clearInterval(window._sentinelTimer);
|
||||
var el2 = document.getElementById('sentinel-countdown');
|
||||
if (el2) { el2.querySelector('div').textContent = 'scanning...'; el2.querySelector('div').style.color = '#4ade80'; }
|
||||
}
|
||||
if (el) { el.textContent = Math.ceil(window._sentinelCountdown) || '...'; if (window._sentinelCountdown <= 0) { el.textContent = '✓'; el.style.color = '#4ade80'; clearInterval(window._sentinelTimer); } }
|
||||
}, 1000);
|
||||
|
||||
if (ss.last_error) {
|
||||
var sErr = document.createElement('div');
|
||||
sErr.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:9px;color:#e05252;border-left:2px solid #e05252;padding-left:8px;margin-bottom:8px';
|
||||
sErr.textContent = 'Last error: ' + ss.last_error;
|
||||
sErr.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:9px;color:#e05252;border-left:2px solid #e05252;padding-left:8px;margin-bottom:4px';
|
||||
sErr.textContent = 'Error: ' + ss.last_error;
|
||||
sentinelCard.appendChild(sErr);
|
||||
}
|
||||
// Recent AI verdicts
|
||||
// Recent AI verdicts — collapsible
|
||||
var verdicts = sentinel.recent_verdicts || [];
|
||||
if (verdicts.length) {
|
||||
var vTitle = document.createElement('div');
|
||||
vTitle.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:8px;text-transform:uppercase;letter-spacing:2px;color:#d946ef;margin:10px 0 6px;opacity:0.6';
|
||||
vTitle.textContent = 'Recent AI Verdicts';
|
||||
sentinelCard.appendChild(vTitle);
|
||||
var vToggle = document.createElement('div');
|
||||
vToggle.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:8px;text-transform:uppercase;letter-spacing:1.5px;color:#d946ef;margin:4px 0 0;opacity:0.5;cursor:pointer';
|
||||
vToggle.textContent = '▶ ' + verdicts.length + ' recent verdicts';
|
||||
var vList = document.createElement('div');
|
||||
vList.style.display = 'none';
|
||||
vToggle.onclick = function(){
|
||||
if (vList.style.display === 'none') { vList.style.display = 'block'; vToggle.textContent = '▼ ' + verdicts.length + ' recent verdicts'; vToggle.style.opacity = '1'; }
|
||||
else { vList.style.display = 'none'; vToggle.textContent = '▶ ' + verdicts.length + ' recent verdicts'; vToggle.style.opacity = '0.5'; }
|
||||
};
|
||||
sentinelCard.appendChild(vToggle);
|
||||
verdicts.slice(0,8).forEach(function(v){
|
||||
var vLine = document.createElement('div');
|
||||
vLine.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10px;color:#7a7872;padding:3px 0;border-bottom:1px solid rgba(42,45,53,0.3);display:flex;gap:8px';
|
||||
@ -835,8 +811,9 @@ async function loadThreats() {
|
||||
+ '<span style="min-width:120px">'+esc(v.ip||'?')+'</span>'
|
||||
+ '<span style="color:#c084fc">'+esc(v.attack_type||'?')+'</span>'
|
||||
+ '<span style="flex:1;opacity:0.6">'+esc(v.reason||'')+'</span>';
|
||||
sentinelCard.appendChild(vLine);
|
||||
vList.appendChild(vLine);
|
||||
});
|
||||
sentinelCard.appendChild(vList);
|
||||
}
|
||||
view.appendChild(sentinelCard);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user