Constant DASHBOARD_HTML
Source const DASHBOARD_HTML: &str = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>Strat9 Web Admin</title>\n<style>\n:root{--bg:#0f1117;--card:#1a1d27;--border:#2a2d3a;--accent:#7eb8ff;--green:#22c55e;--red:#ef4444;--orange:#f59e0b;--text:#e0e0e0;--muted:#888;--dim:#555}\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);line-height:1.5}\na{color:var(--accent);text-decoration:none}\n.hdr{background:var(--card);padding:14px 24px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-wrap:wrap}\n.hdr h1{font-size:1.25rem;font-weight:600;color:var(--accent)}\n.badge{font-size:.7rem;padding:2px 8px;border-radius:9999px;font-weight:600}\n.badge-ok{background:var(--green);color:#000}\n.badge-err{background:var(--red);color:#fff}\n.badge-warn{background:var(--orange);color:#000}\n.hdr .meta{margin-left:auto;font-size:.72rem;color:var(--muted)}\n.tabs{display:flex;gap:0;background:var(--card);border-bottom:1px solid var(--border);padding:0 24px}\n.tab{padding:10px 18px;font-size:.8rem;cursor:pointer;border-bottom:2px solid transparent;color:var(--muted);transition:all .15s}\n.tab:hover{color:var(--text)}\n.tab.active{color:var(--accent);border-bottom-color:var(--accent)}\n.page{display:none;padding:20px 24px}\n.page.active{display:block}\n.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:14px}\n.card{background:var(--card);border:1px solid var(--border);border-radius:8px;padding:14px}\n.card h2{font-size:.8rem;text-transform:uppercase;letter-spacing:.05em;color:var(--accent);margin-bottom:8px}\n.card pre{font-size:.75rem;white-space:pre-wrap;word-break:break-all;color:#b0b8c8;max-height:280px;overflow-y:auto}\n.val{font-size:1.5rem;font-weight:700;color:#fff}\n.label{font-size:.72rem;color:var(--muted);margin-top:2px}\n.stat-row{display:flex;gap:14px;flex-wrap:wrap}\n.stat-item{flex:1;min-width:90px}\ntable{width:100%;border-collapse:collapse;font-size:.75rem}\nth{text-align:left;color:var(--accent);padding:6px 8px;border-bottom:1px solid var(--border);position:sticky;top:0;background:var(--card)}\ntd{padding:5px 8px;border-bottom:1px solid #1e2130}\ntr:hover td{background:#1e2233}\n.btn{display:inline-block;padding:3px 10px;font-size:.7rem;border:1px solid var(--border);border-radius:4px;cursor:pointer;background:transparent;color:var(--text);transition:all .15s}\n.btn:hover{background:var(--border)}\n.btn-danger{border-color:var(--red);color:var(--red)}\n.btn-danger:hover{background:var(--red);color:#fff}\n.spinner{display:inline-block;width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite}\n@keyframes spin{to{transform:rotate(360deg)}}\n.toast{position:fixed;bottom:20px;right:20px;background:var(--card);border:1px solid var(--border);padding:10px 16px;border-radius:6px;font-size:.78rem;opacity:0;transition:opacity .3s;pointer-events:none;z-index:99}\n.toast.show{opacity:1;pointer-events:auto}\n.footer{padding:8px 24px;font-size:.7rem;color:var(--dim);text-align:right;border-top:1px solid var(--border)}\n</style>\n</head>\n<body>\n<div class=\"hdr\">\n <h1>Strat9 Web Admin</h1>\n <span class=\"badge badge-warn\" id=\"badge\">Connecting...</span>\n <span class=\"meta\">PID <span id=\"srv-pid\">-</span> | Uptime <span id=\"srv-up\">-</span></span>\n</div>\n<div class=\"tabs\">\n <div class=\"tab active\" data-page=\"overview\">Overview</div>\n <div class=\"tab\" data-page=\"procs\">Processes</div>\n <div class=\"tab\" data-page=\"net\">Network</div>\n <div class=\"tab\" data-page=\"system\">System</div>\n</div>\n\n<!-- OVERVIEW -->\n<div class=\"page active\" id=\"page-overview\">\n<div class=\"grid\">\n <div class=\"card\">\n <h2>Uptime</h2>\n <div class=\"val\" id=\"up-val\">--</div>\n <div class=\"label\" id=\"up-detail\"></div>\n </div>\n <div class=\"card\">\n <h2>Network</h2>\n <div class=\"stat-row\">\n <div class=\"stat-item\"><div class=\"label\">IP</div><div id=\"o-ip\">--</div></div>\n <div class=\"stat-item\"><div class=\"label\">Gateway</div><div id=\"o-gw\">--</div></div>\n <div class=\"stat-item\"><div class=\"label\">DNS</div><div id=\"o-dns\">--</div></div>\n </div>\n </div>\n <div class=\"card\">\n <h2>Processes</h2>\n <div class=\"val\" id=\"proc-count\">--</div>\n <div class=\"label\">running tasks</div>\n </div>\n <div class=\"card\">\n <h2>Memory</h2>\n <pre id=\"o-mem\">--</pre>\n </div>\n <div class=\"card\">\n <h2>Silos</h2>\n <pre id=\"o-silos\">--</pre>\n </div>\n <div class=\"card\">\n <h2>Kernel</h2>\n <pre id=\"o-ver\">--</pre>\n </div>\n</div>\n</div>\n\n<!-- PROCESSES -->\n<div class=\"page\" id=\"page-procs\">\n<div class=\"card\" style=\"overflow-x:auto;max-height:70vh;overflow-y:auto\">\n <h2>Process Table <span class=\"spinner\" id=\"proc-spin\" style=\"display:none\"></span></h2>\n <table>\n <thead><tr><th>PID</th><th>PPID</th><th>Name</th><th>State</th><th>Silo</th><th>Mem</th><th></th></tr></thead>\n <tbody id=\"proc-body\"><tr><td colspan=\"7\">Loading...</td></tr></tbody>\n </table>\n</div>\n</div>\n\n<!-- NETWORK -->\n<div class=\"page\" id=\"page-net\">\n<div class=\"grid\">\n <div class=\"card\">\n <h2>Addresses</h2>\n <div class=\"stat-row\">\n <div class=\"stat-item\"><div class=\"label\">IP / CIDR</div><div id=\"n-ip\">--</div></div>\n <div class=\"stat-item\"><div class=\"label\">Netmask</div><div id=\"n-mask\">--</div></div>\n <div class=\"stat-item\"><div class=\"label\">Gateway</div><div id=\"n-gw\">--</div></div>\n <div class=\"stat-item\"><div class=\"label\">DNS</div><div id=\"n-dns\">--</div></div>\n </div>\n </div>\n <div class=\"card\" style=\"grid-column:1/-1\">\n <h2>Routing Table</h2>\n <pre id=\"n-routes\">--</pre>\n </div>\n</div>\n</div>\n\n<!-- SYSTEM -->\n<div class=\"page\" id=\"page-system\">\n<div class=\"grid\">\n <div class=\"card\">\n <h2>Kernel Version</h2>\n <pre id=\"s-ver\">--</pre>\n </div>\n <div class=\"card\">\n <h2>CPU Info</h2>\n <pre id=\"s-cpu\">--</pre>\n </div>\n <div class=\"card\">\n <h2>Memory Info</h2>\n <pre id=\"s-mem\">--</pre>\n </div>\n <div class=\"card\" style=\"grid-column:1/-1\">\n <h2>Silos</h2>\n <pre id=\"s-silos\">--</pre>\n </div>\n</div>\n</div>\n\n<div class=\"toast\" id=\"toast\"></div>\n<div class=\"footer\">Auto-refresh 5s | <span id=\"ts\"></span></div>\n\n<script>\nconst $=id=>document.getElementById(id);\nfunction toast(msg,ms){const t=$(\'toast\');t.textContent=msg;t.classList.add(\'show\');setTimeout(()=>t.classList.remove(\'show\'),ms||2500)}\nasync function api(p){try{const r=await fetch(p);if(!r.ok)throw r;return await r.json()}catch(e){return null}}\n\n// tabs\ndocument.querySelectorAll(\'.tab\').forEach(t=>t.onclick=()=>{\n document.querySelectorAll(\'.tab\').forEach(x=>x.classList.remove(\'active\'));\n document.querySelectorAll(\'.page\').forEach(x=>x.classList.remove(\'active\'));\n t.classList.add(\'active\');$(\'page-\'+t.dataset.page).classList.add(\'active\');\n});\n\nfunction esc(s){const d=document.createElement(\'div\');d.textContent=s;return d.innerHTML}\n\nasync function killProc(pid){\n if(!confirm(\'Kill process \'+pid+\'?\'))return;\n const token=prompt(\'Admin token required\')||\'\';\n if(!token){toast(\'Kill cancelled\');return;}\n let r=null;\n try{\n const resp=await fetch(\'/api/kill/\'+pid,{method:\'POST\',headers:{\'Authorization\':\'Bearer \'+token}});\n if(resp.ok)r=await resp.json();\n }catch(e){}\n if(r&&r.killed)toast(\'Process \'+pid+\' killed\');\n else toast(\'Kill failed: \'+(r?r.error:\'no response\'));\n setTimeout(refresh,500);\n}\n\nasync function refresh(){\n const d=await api(\'/api/all\');\n if(!d){$(\'badge\').className=\'badge badge-err\';$(\'badge\').textContent=\'Offline\';return}\n const h=d.health,u=d.uptime,v=d.version,c=d.cpuinfo,m=d.meminfo,s=d.silos,n=d.network,rt=d.routes,p=d.processes;\n\n // badge\n if(h&&h.status===\'ok\'){$(\'badge\').className=\'badge badge-ok\';$(\'badge\').textContent=\'Online\';$(\'srv-pid\').textContent=h.pid;$(\'srv-up\').textContent=u?u.human:\'?\'}\n else{$(\'badge\').className=\'badge badge-err\';$(\'badge\').textContent=\'Error\'}\n\n // overview\n if(u){$(\'up-val\').textContent=u.human;$(\'up-detail\').textContent=u.secs+\' seconds\'}\n if(n){$(\'o-ip\').textContent=n.address||n.ip||\'n/a\';$(\'o-gw\').textContent=n.gateway||\'n/a\';$(\'o-dns\').textContent=n.dns||\'n/a\'}\n if(m)$(\'o-mem\').textContent=m.meminfo||\'n/a\';\n if(s)$(\'o-silos\').textContent=s.silos||\'n/a\';\n if(v)$(\'o-ver\').textContent=v.version||\'n/a\';\n if(p)$(\'proc-count\').textContent=p.count||p.processes.length;\n\n // process table\n if(p&&p.processes){\n let h=\'\';\n for(const pr of p.processes){\n h+=\'<tr><td>\'+pr.pid+\'</td><td>\'+pr.ppid+\'</td><td>\'+esc(pr.name)+\'</td><td>\'+esc(pr.state)+\'</td><td>\'+esc(pr.silo||\'-\')+\'</td><td>\'+esc(pr.mem||\'-\')+\'</td>\';\n h+=\'<td><button class=\"btn btn-danger\" onclick=\"killProc(\'+pr.pid+\')\">Kill</button></td></tr>\';\n }\n $(\'proc-body\').innerHTML=h||\'<tr><td colspan=\"7\">No processes</td></tr>\';\n }\n\n // network\n if(n){$(\'n-ip\').textContent=(n.ip||n.address||\'n/a\');$(\'n-mask\').textContent=n.netmask||\'n/a\';$(\'n-gw\').textContent=n.gateway||\'n/a\';$(\'n-dns\').textContent=n.dns||\'n/a\'}\n if(rt)$(\'n-routes\').textContent=rt.routes||\'n/a\';\n\n // system\n if(v)$(\'s-ver\').textContent=v.version||\'n/a\';\n if(c)$(\'s-cpu\').textContent=c.cpuinfo||\'n/a\';\n if(m)$(\'s-mem\').textContent=m.meminfo||\'n/a\';\n if(s)$(\'s-silos\').textContent=s.silos||\'n/a\';\n\n $(\'ts\').textContent=new Date().toLocaleTimeString();\n}\nrefresh();setInterval(refresh,5000);\n</script>\n</body>\n</html>\n";