diff --git a/llm_team_ui.py b/llm_team_ui.py
index 5cc3c5d..117bf93 100644
--- a/llm_team_ui.py
+++ b/llm_team_ui.py
@@ -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 = '';
- 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 = '';
+ 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 = ''+(ss.scans||0)+' scans'+(ss.bans||0)+' bans';
+ 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() {
+ ''+esc(v.ip||'?')+''
+ ''+esc(v.attack_type||'?')+''
+ ''+esc(v.reason||'')+'';
- sentinelCard.appendChild(vLine);
+ vList.appendChild(vLine);
});
+ sentinelCard.appendChild(vList);
}
view.appendChild(sentinelCard);