← back to Cncp Failures Mockups
public/v6-brutalist.html
121 lines
<!doctype html><html><head>
<meta charset="utf-8"><title>v6 — Brutalist data-dense</title>
<style>
body { margin:0; background:#fff; color:#000; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight:500; padding:0; min-height:100vh; }
* { box-sizing: border-box; }
.top { display:grid; grid-template-columns: 1fr auto; padding:16px 24px; border-bottom:3px solid #000; }
.top h1 { margin:0; font-size:28px; font-weight:900; letter-spacing:-0.02em; text-transform:uppercase; }
.top .stamp { font-size:11px; text-transform:uppercase; letter-spacing:2px; align-self:center; padding:4px 10px; border:2px solid #000; }
.stats { display:grid; grid-template-columns: repeat(8, 1fr); border-bottom:3px solid #000; }
.stat { padding:14px 12px; border-right:1px solid #000; }
.stat:last-child { border-right:none; }
.stat .v { font-size:30px; font-weight:900; letter-spacing:-0.02em; line-height:1; }
.stat .l { font-size:9px; text-transform:uppercase; letter-spacing:1.5px; margin-top:4px; color:#000; }
.stat.h .v { color:#d00; }
.stat.m .v { color:#b56300; }
.stat.l-low .v { color:#005bbb; }
table.dense { width:100%; border-collapse:collapse; font-size:11.5px; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; }
table.dense th { text-align:left; background:#000; color:#fff; padding:6px 8px; font-weight:700; text-transform:uppercase; letter-spacing:1px; font-size:10px; border-right:1px solid #555; }
table.dense td { padding:6px 8px; border-right:1px solid #ccc; border-bottom:1px solid #ddd; vertical-align:top; }
table.dense tr:nth-child(even) td { background:#f4f4f4; }
table.dense .mono { font-family:ui-monospace,Menlo,monospace; }
table.dense .sev-h { background:#d00; color:#fff; padding:2px 6px; font-weight:700; text-transform:uppercase; font-size:9px; letter-spacing:1px; }
table.dense .sev-m { background:#b56300; color:#fff; padding:2px 6px; font-weight:700; text-transform:uppercase; font-size:9px; letter-spacing:1px; }
table.dense .sev-l { background:#005bbb; color:#fff; padding:2px 6px; font-weight:700; text-transform:uppercase; font-size:9px; letter-spacing:1px; }
table.dense .src { font-weight:700; text-transform:uppercase; font-size:10px; letter-spacing:1px; }
table.dense .status-triage { background:#fff3cd; }
table.dense .status-resolved { background:#d4edda; }
table.dense .status-investigating { background:#cce5ff; }
table.dense .status-dismissed { background:#e2e3e5; color:#666; }
table.dense .lines { font-family:ui-monospace,Menlo,monospace; color:#555; font-size:10.5px; line-height:1.4; max-width:300px; white-space:pre-wrap; word-break:break-word; }
table.dense .action { color:#005bbb; font-style:italic; font-size:11px; }
.hermes-block { padding:14px 24px; border-top:3px solid #000; }
.hermes-block h2 { margin:0 0 8px; font-size:14px; font-weight:900; text-transform:uppercase; letter-spacing:2px; }
.hermes-block table { width:100%; border-collapse:collapse; font-size:11px; }
.hermes-block table td { padding:4px 8px; border-bottom:1px solid #ddd; }
.hermes-block table td:first-child { text-transform:uppercase; letter-spacing:1px; font-size:10px; color:#666; width:120px; }
.recent-grid { display:grid; grid-template-columns: repeat(3, 1fr); gap:0; border:1px solid #000; margin-top:8px; }
.recent-grid > div { padding:8px 10px; border-right:1px solid #ddd; border-bottom:1px solid #ddd; font-size:10.5px; }
.recent-grid > div:nth-child(3n) { border-right:none; }
</style>
</head><body>
<div class="top">
<h1>Failures · Brutalist Index</h1>
<div class="stamp" id="stamp"></div>
</div>
<div class="stats" id="stats"></div>
<table class="dense">
<thead>
<tr>
<th>#</th>
<th>SEV</th>
<th>SRC</th>
<th>STATUS</th>
<th>PROCESS</th>
<th>EV</th>
<th>EXIT</th>
<th>CATEGORY</th>
<th>SUGGESTED ACTION</th>
<th>LAST OUTPUT (first line)</th>
<th>SEEN</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
<div class="hermes-block">
<h2>Hermes 3 · 8B · in mem · 4.3GB · 127.0.0.1:11434 · online</h2>
<table id="h-stats"></table>
<h2 style="margin-top:14px;">Recent Calls</h2>
<div class="recent-grid" id="h-recent"></div>
</div>
<script>
fetch('/data' + location.search).then(r=>r.json()).then(d => {
document.getElementById('stamp').textContent = new Date().toUpperCase ? new Date().toISOString().slice(0,16).replace('T',' ') : '';
document.getElementById('stamp').textContent = new Date().toISOString().slice(0,16).replace('T',' ') + ' UTC';
const sev = d.hermes.last24h.bySev;
const open = d.failures.filter(f=>f.status==='triage'||f.status==='investigating').length;
const eventsTotal = d.failures.reduce((s,f)=>s+(f.eventCount||0),0);
document.getElementById('stats').innerHTML = `
<div class="stat h"><div class="v">${sev.high}</div><div class="l">High</div></div>
<div class="stat m"><div class="v">${sev.medium}</div><div class="l">Medium</div></div>
<div class="stat l-low"><div class="v">${sev.low}</div><div class="l">Low</div></div>
<div class="stat"><div class="v">${open}</div><div class="l">Open</div></div>
<div class="stat"><div class="v">${eventsTotal}</div><div class="l">Events 24h</div></div>
<div class="stat"><div class="v">${d.hermes.last24h.total}</div><div class="l">Hermes Calls</div></div>
<div class="stat"><div class="v">${(d.hermes.last24h.avgLatencyMs/1000).toFixed(1)}<span style="font-size:13px;color:#666">s</span></div><div class="l">Avg Latency</div></div>
<div class="stat"><div class="v">${d.hermes.last24h.fail}</div><div class="l">Failed Calls</div></div>`;
const sevCls = s => s==='high'?'sev-h':s==='medium'?'sev-m':s==='low'?'sev-l':'';
document.getElementById('tbody').innerHTML = d.failures.map((f,i) => `
<tr class="status-${f.status}">
<td class="mono">${i+1}</td>
<td><span class="${sevCls(f.severity)}">${f.severity || '—'}</span></td>
<td class="src">${f.source}</td>
<td>${f.status}</td>
<td class="mono"><b>${f.processName}</b></td>
<td class="mono">${f.eventCount}</td>
<td class="mono">${f.exitCode != null ? f.exitCode : '—'}</td>
<td>${f.category || '—'}</td>
<td class="action">${f.suggestedAction || '—'}</td>
<td class="lines">${(f.lastLines||'').split('\\n').filter(Boolean)[0] || '—'}</td>
<td class="mono" style="white-space:nowrap;">${new Date(f.lastSeen).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}</td>
</tr>`).join('');
const h = d.hermes.last24h;
document.getElementById('h-stats').innerHTML = `
<tr><td>Total 24h</td><td><b>${h.total}</b></td><td>p50 latency</td><td><b>${(h.avgLatencyMs/1000).toFixed(1)}s</b></td></tr>
<tr><td>Classify</td><td><b>${h.byType.classify}</b></td><td>Refinement chat</td><td><b>${h.byType.chat}</b></td></tr>
<tr><td>Idea-chat</td><td><b>${h.byType['idea-chat']}</b></td><td>Failed</td><td><b>${h.fail}</b></td></tr>
<tr><td>Severity Dist</td><td colspan="3"><span class="${sevCls('high')}">${sev.high}H</span> <span class="${sevCls('medium')}">${sev.medium}M</span> <span class="${sevCls('low')}">${sev.low}L</span></td></tr>`;
document.getElementById('h-recent').innerHTML = d.hermes.recent.slice(0,9).map(c => `
<div>
<div style="font-family:ui-monospace,Menlo,monospace; color:#666; font-size:9px;">${new Date(c.ts).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})} · ${c.type}</div>
<div style="font-weight:600; margin-top:2px; word-break:break-word;">${c.target}</div>
<div style="color:${c.ok?'#005bbb':'#d00'}; margin-top:2px; font-size:10px;">${c.ok?'✓':'✗'} ${(c.latencyMs/1000).toFixed(1)}s${c.severity?(' · '+c.severity):''}</div>
</div>`).join('');
});
</script>
</body></html>