Files
nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v9.html
T
Gilles Soulier a0f47bf966 feat: add plans, design system, CONSIGNE and brainstorm assets
Ajoute les trois plans d'implémentation (agent Rust, serveur Go, dashboard),
les consignes de design, les fichiers de brainstorming et le .gitignore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 08:13:53 +02:00

674 lines
56 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Nanometrics — v9</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script src="/helper.js"></script>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root[data-theme="dark"]{
--accent:#fe8019;--accent-soft:#d65d0e;--accent-glow:rgba(254,128,25,.28);
--bg-0:#1d1813;--bg-1:#2a231d;--bg-2:#32291f;--bg-3:#3c332a;--bg-4:#4a3f33;--bg-5:#57493c;
--ink-1:#f2e5c7;--ink-2:#d5c4a1;--ink-3:#a89984;--ink-4:#7c6f64;
--ok:#4dbb26;--warn:#fabd2f;--err:#fb4934;--blue:#3db0d1;--purple:#c882c8;
--border-1:rgba(255,255,255,.06);--border-2:rgba(255,255,255,.12);--border-3:rgba(255,255,255,.26);
--tile-3d:0 1px 0 rgba(255,255,255,.08) inset,0 -1px 0 rgba(0,0,0,.3) inset,0 6px 20px rgba(0,0,0,.5);
--tile-press:inset 0 2px 8px rgba(0,0,0,.5),inset 0 1px 3px rgba(0,0,0,.4);
--hover-glow:0 0 0 1px var(--accent-soft),0 0 24px var(--accent-glow),0 6px 20px rgba(0,0,0,.5);
--font-ui:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace;--font-terminal:'Share Tech Mono',monospace;
}
:root[data-theme="light"]{
--accent:#af3a03;--accent-soft:#d65d0e;--accent-glow:rgba(175,58,3,.18);
--bg-0:#d5c4a1;--bg-1:#ebdbb2;--bg-2:#d5c4a1;--bg-3:#bdae93;--bg-4:#a89984;--bg-5:#928374;
--ink-1:#3c3836;--ink-2:#504945;--ink-3:#665c54;--ink-4:#7c6f64;
--ok:#3c911c;--warn:#b57614;--err:#9d0006;--blue:#2d82a3;--purple:#8c468c;
--border-1:rgba(0,0,0,.08);--border-2:rgba(0,0,0,.15);--border-3:rgba(0,0,0,.3);
--tile-3d:0 1px 0 rgba(255,255,255,.55) inset,0 -1px 0 rgba(0,0,0,.08) inset,0 4px 14px rgba(0,0,0,.13);
--tile-press:inset 0 2px 6px rgba(0,0,0,.2);
--hover-glow:0 0 0 1px var(--accent-soft),0 0 18px var(--accent-glow),0 4px 14px rgba(0,0,0,.13);
--font-ui:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace;--font-terminal:'Share Tech Mono',monospace;
}
body{background:var(--bg-1);color:var(--ink-1);font-family:var(--font-ui);font-size:13px;height:100vh;display:flex;flex-direction:column;overflow:hidden;transition:background .2s,color .2s}
#tooltip{position:fixed;z-index:9999;pointer-events:none;background:var(--bg-0);color:var(--ink-1);border:1px solid var(--border-3);border-radius:5px;padding:4px 9px;font-size:11px;font-family:var(--font-ui);white-space:nowrap;opacity:0;transition:opacity .12s;box-shadow:0 4px 12px rgba(0,0,0,.4)}
#tooltip.show{opacity:1}
/* HEADER */
.header{background:var(--bg-2);border-bottom:1px solid var(--border-2);padding:0 20px;height:48px;display:flex;align-items:center;gap:12px;flex-shrink:0}
.logo{display:flex;align-items:center;gap:8px}
.logo-led{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 8px var(--accent-glow);animation:blink 2s infinite}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}
.logo-name{font-weight:700;font-size:14px;letter-spacing:.05em;font-family:var(--font-terminal)}
.logo-ver{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
.h-sep{width:1px;height:24px;background:var(--border-2)}
.h-spacer{flex:1}
.h-stats{display:flex;gap:14px}
.h-stat{display:flex;align-items:center;gap:5px}
.h-stat .lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
.h-stat .val{font-family:var(--font-mono);font-weight:700;font-size:13px}
.c-ok{color:var(--ok)}.c-warn{color:var(--warn)}.c-err{color:var(--err)}.c-n{color:var(--ink-2)}
/* user-select:none uniquement sur les éléments interactifs qui ne doivent pas être sélectionnés */
.hbtn{width:34px;height:34px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-3);color:var(--ink-2);font-size:14px;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;transition:background .12s,color .12s,transform .08s,box-shadow .08s}
.hbtn:hover{background:var(--bg-4);color:var(--accent)}
.hbtn:active{transform:translateY(1px) scale(.96);box-shadow:var(--tile-press)}
.hbtn.active-btn{background:var(--accent);color:var(--bg-0);border-color:var(--accent-soft)}
/* GRID */
.main{flex:1;padding:14px 16px;overflow-y:auto}
.agents-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}
/* user-select retiré du .tile — le texte (hostname, IP, valeurs) doit rester sélectionnable */
.tile{background:var(--bg-3);border-radius:10px;padding:12px 14px;border:1px solid var(--border-1);box-shadow:var(--tile-3d);cursor:pointer;display:flex;flex-direction:column;gap:9px;transition:box-shadow .15s,transform .08s,border-color .15s}
.tile:hover{box-shadow:var(--hover-glow);border-color:var(--accent-soft)}
.tile:active{transform:translateY(2px) scale(.99);box-shadow:var(--tile-press)}
.tile.t-warn{border-color:rgba(250,189,47,.3)}.tile.t-warn:hover{border-color:var(--warn);box-shadow:0 0 0 1px var(--warn),0 0 22px rgba(250,189,47,.22),0 6px 20px rgba(0,0,0,.5)}
.tile.t-err{border-color:rgba(251,73,52,.35)}.tile.t-err:hover{border-color:var(--err);box-shadow:0 0 0 1px var(--err),0 0 22px rgba(251,73,52,.25),0 6px 20px rgba(0,0,0,.5)}
.tile.t-off{opacity:.5;cursor:default}.tile.t-off:hover,.tile.t-off:active{box-shadow:var(--tile-3d);border-color:var(--border-1);transform:none}
.tile-head{display:flex;align-items:center;gap:8px;user-select:none}
.t-icon{width:28px;height:28px;border-radius:7px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:13px;flex-shrink:0;overflow:hidden}
.t-names{flex:1;min-width:0}
.t-host{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.t-ip{font-family:var(--font-mono);font-size:10px;color:var(--ink-4)}
.t-led{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.s-ok{background:var(--ok);box-shadow:0 0 6px var(--ok)}.s-warn{background:var(--warn);box-shadow:0 0 6px var(--warn);animation:blink 1.5s infinite}.s-err{background:var(--err);box-shadow:0 0 8px var(--err);animation:blink 1s infinite}.s-off{background:var(--ink-4)}
.tile-gauges{display:flex;flex-direction:column;gap:5px}
.g-row{display:flex;align-items:center;gap:7px}
.g-ico{width:18px;height:18px;display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--ink-3);flex-shrink:0;cursor:help}
.g-bar{flex:1;height:5px;border-radius:3px;background:var(--bg-1);overflow:hidden}
.g-fill{height:100%;border-radius:3px;background:var(--ok)}
.g-fill.w{background:var(--warn)}.g-fill.e{background:var(--err)}
.g-val{font-family:var(--font-mono);font-size:11px;color:var(--ink-2);width:34px;text-align:right}
.tile-foot{font-family:var(--font-terminal);font-size:10px;color:var(--ink-4);display:flex;align-items:center;gap:5px;user-select:none}
.tile-foot i{font-size:9px}
/* FOOTER */
.footer{background:var(--bg-0);border-top:1px solid var(--border-2);height:26px;display:flex;align-items:center;font-family:var(--font-terminal);font-size:11px;color:var(--ink-4);flex-shrink:0}
.f-mode{background:var(--accent);color:var(--bg-0);padding:0 12px;height:100%;display:flex;align-items:center;font-weight:700;letter-spacing:.04em;user-select:none}
.f-cell{padding:0 12px;border-right:1px solid var(--border-1);display:flex;align-items:center;gap:5px;height:100%}
.f-val{font-family:var(--font-mono);color:var(--ink-2)}.f-val.w{color:var(--warn)}
.f-minibar{width:36px;height:4px;border-radius:2px;background:var(--bg-3);overflow:hidden}
.f-minifill{height:100%;border-radius:2px;background:var(--ok)}.f-minifill.w{background:var(--warn)}
.f-spacer{flex:1}.f-right{padding:0 12px;display:flex;align-items:center;gap:6px;color:var(--ink-3)}
.f-time{font-family:var(--font-mono);color:var(--ink-2)}
/* OVERLAY */
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.65);z-index:100;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(2px)}
/* POPUP BASE */
.popup{background:var(--bg-2);border:1px solid var(--border-3);border-radius:12px;box-shadow:0 24px 64px rgba(0,0,0,.7);display:flex;flex-direction:column;overflow:hidden}
.pop-close{width:28px;height:28px;border-radius:6px;background:var(--bg-5);color:var(--ink-3);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;border:1px solid var(--border-1);user-select:none;transition:background .12s,color .12s,transform .08s}
.pop-close:hover{background:var(--err);color:#fff}
.pop-close:active{transform:translateY(1px) scale(.93);box-shadow:var(--tile-press)}
.sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;margin-bottom:8px}
.btn{padding:6px 14px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-4);color:var(--ink-2);font-size:12px;font-family:var(--font-ui);cursor:pointer;display:flex;align-items:center;gap:6px;user-select:none;transition:background .1s,transform .08s,box-shadow .08s}
.btn:hover{background:var(--bg-5)}.btn:active{transform:translateY(1px) scale(.97);box-shadow:var(--tile-press)}
.btn.primary{background:var(--accent);color:var(--bg-0);border-color:var(--accent-soft);font-weight:600}.btn.primary:hover{background:var(--accent-soft)}
/* ══ CONFIG SERVEUR (header) ══ */
#overlay-srvcfg{z-index:150}
#popup-srvcfg{width:400px;max-width:96vw;max-height:88vh}
.scfg-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
.scfg-icon{width:32px;height:32px;border-radius:8px;background:var(--accent);display:flex;align-items:center;justify-content:center;color:var(--bg-0);font-size:15px}
.scfg-title{flex:1;font-weight:700;font-size:14px}
.scfg-body{padding:18px;display:flex;flex-direction:column;gap:14px;overflow-y:auto}
.scfg-section{display:flex;flex-direction:column;gap:8px}
.scfg-sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;padding-bottom:6px;border-bottom:1px solid var(--border-1)}
.scfg-row{display:flex;align-items:center;gap:10px}
.scfg-row > label{font-size:12px;color:var(--ink-3);width:110px;flex-shrink:0;font-family:var(--font-terminal)}
.scfg-slider{flex:1;accent-color:var(--accent)}
.scfg-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);width:48px;text-align:right}
.scfg-select{flex:1;background:var(--bg-3);border:1px solid var(--border-2);border-radius:6px;color:var(--ink-1);padding:6px 10px;font-size:12px;font-family:var(--font-ui)}
.scfg-toggle-row{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;background:var(--bg-3);border-radius:7px;border:1px solid var(--border-1)}
.scfg-toggle-row label{font-size:12px;color:var(--ink-2);display:flex;align-items:center;gap:7px;cursor:pointer}
.scfg-toggle-row label i{color:var(--accent);font-size:12px}
.scfg-foot{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;gap:8px;justify-content:flex-end}
.toggle{position:relative;width:34px;height:18px;flex-shrink:0}
.toggle input{opacity:0;width:0;height:0}
.toggle-slider{position:absolute;inset:0;border-radius:9px;background:var(--bg-4);border:1px solid var(--border-2);cursor:pointer;transition:background .2s}
.toggle-slider::before{content:'';position:absolute;width:12px;height:12px;border-radius:50%;background:var(--ink-4);top:2px;left:2px;transition:transform .2s,background .2s}
.toggle input:checked+.toggle-slider{background:rgba(254,128,25,.3);border-color:var(--accent)}
.toggle input:checked+.toggle-slider::before{transform:translateX(16px);background:var(--accent)}
.tog-ok input:checked+.toggle-slider{background:rgba(77,187,38,.3);border-color:var(--ok)}
.tog-ok input:checked+.toggle-slider::before{background:var(--ok)}
/* ══ POPUP DÉTAIL AGENT — redimensionnable ══ */
#popup-detail{width:560px;max-width:96vw;max-height:92vh;resize:both;overflow:hidden;min-width:400px;min-height:320px}
#popup-detail .pop-body{overflow-y:auto;flex:1}
.pop-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:12px;flex-shrink:0}
.agent-icon-wrap{position:relative;width:44px;height:44px;border-radius:10px;flex-shrink:0;cursor:pointer;overflow:hidden;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:18px;border:2px solid var(--border-2);transition:border-color .15s}
.agent-icon-wrap:hover{border-color:var(--accent)}
.agent-icon-overlay{position:absolute;inset:0;background:rgba(0,0,0,.6);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;opacity:0;transition:opacity .15s;font-size:10px;color:#fff}
.agent-icon-overlay i{font-size:14px}
.agent-icon-wrap:hover .agent-icon-overlay{opacity:1}
#icon-upload{display:none}
.pop-head-info{flex:1}
.pop-host{font-weight:700;font-size:15px}
.pop-ip{font-family:var(--font-mono);font-size:11px;color:var(--ink-4)}
.upload-hint{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal);margin-top:2px}
.pop-led{width:10px;height:10px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok);flex-shrink:0}
.pop-body{padding:16px 18px;display:flex;flex-direction:column;gap:14px}
.kpi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}
.kpi{background:var(--bg-3);border-radius:8px;padding:10px 12px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
.kpi-lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
.kpi-val{font-family:var(--font-mono);font-size:20px;font-weight:700;line-height:1.1;margin-top:2px}
.kpi-val .u{font-size:10px;color:var(--ink-3);font-weight:400}
.kpi-sub{font-size:10px;color:var(--ink-4);font-family:var(--font-mono);margin-top:2px}
.charts-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.chart-card{background:var(--bg-3);border-radius:8px;padding:10px 12px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
.chart-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
.chart-label{display:flex;align-items:center;gap:6px;font-size:10px;font-family:var(--font-terminal);letter-spacing:.06em;color:var(--ink-3)}
.chart-cur{font-family:var(--font-mono);font-size:16px;font-weight:700}
.chart-svg{width:100%;height:52px;display:block}
.chart-axis{display:flex;justify-content:space-between;margin-top:2px;font-family:var(--font-terminal);font-size:9px;color:var(--ink-4)}
.storage-block{display:flex;flex-direction:column;gap:8px}
.det-gauges{display:flex;flex-direction:column;gap:7px}
.dg-row{display:flex;align-items:center;gap:10px}
.dg-ico{width:22px;text-align:center;font-size:13px;cursor:help}
.dg-bar{flex:1;height:7px;border-radius:4px;background:var(--bg-1);overflow:hidden}
.dg-fill{height:100%;border-radius:4px;background:var(--ok)}.dg-fill.b{background:var(--blue)}
.dg-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);width:90px;text-align:right}
/* user-select retiré du smart-btn — "SMART PASSED 34°C" doit être sélectionnable */
.smart-btn{display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-3);cursor:pointer;transition:background .12s,border-color .12s,transform .08s;font-family:var(--font-terminal);font-size:11px}
.smart-btn:hover{background:var(--bg-4);border-color:var(--border-3)}.smart-btn:active{transform:translateY(1px);box-shadow:var(--tile-press)}
.smart-btn.ok{border-color:rgba(77,187,38,.3);color:var(--ok)}.smart-dot{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 5px var(--ok)}
.meta-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}
.meta{background:var(--bg-3);border-radius:6px;padding:8px 10px;border:1px solid var(--border-1)}
.meta-lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
.meta-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);margin-top:2px}
.proto-badges{display:flex;gap:5px;margin-top:4px}
.proto-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 7px;border-radius:999px;font-size:10px;font-family:var(--font-terminal);font-weight:600}
.proto-badge.udp{background:rgba(61,176,209,.15);color:var(--blue);border:1px solid rgba(61,176,209,.3)}
.proto-badge.mqtt{background:rgba(200,130,200,.15);color:var(--purple);border:1px solid rgba(200,130,200,.3)}
.pop-foot{padding:10px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px;flex-shrink:0}
.pop-uptime{font-family:var(--font-terminal);font-size:11px;color:var(--ink-4);flex:1}
.resize-hint{font-family:var(--font-terminal);font-size:9px;color:var(--ink-4);display:flex;align-items:center;gap:4px}
.btn-agent-cfg{width:34px;height:34px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-4);color:var(--ink-3);font-size:15px;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;transition:background .12s,color .12s,transform .08s,box-shadow .08s}
.btn-agent-cfg:hover{background:var(--bg-5);color:var(--accent)}.btn-agent-cfg:active{transform:translateY(1px) scale(.93);box-shadow:var(--tile-press)}
/* ══ CONFIG AGENT ══ */
#overlay-agentcfg{z-index:200}
#popup-agentcfg{width:520px;max-width:96vw;max-height:90vh}
.cfg-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
.cfg-head-icon{width:32px;height:32px;border-radius:8px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:15px}
.cfg-head-info{flex:1}.cfg-head-title{font-weight:700;font-size:14px}.cfg-head-sub{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal)}
.cfg-body{padding:18px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;max-height:62vh}
.cfg-section{display:flex;flex-direction:column;gap:8px}
.cfg-sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;padding-bottom:6px;border-bottom:1px solid var(--border-1)}
/* paramètres MQTT */
.mqtt-opts{background:var(--bg-3);border-radius:8px;border:1px solid rgba(200,130,200,.2);padding:12px 14px;display:flex;flex-direction:column;gap:10px}
.mqtt-field{display:flex;align-items:center;gap:10px}
.mqtt-field label{font-size:11px;color:var(--ink-3);font-family:var(--font-terminal);width:90px;flex-shrink:0}
.mqtt-input{flex:1;background:var(--bg-1);border:1px solid var(--border-2);border-radius:6px;color:var(--ink-1);padding:6px 10px;font-size:12px;font-family:var(--font-mono)}
.mqtt-input:focus{outline:none;border-color:var(--purple)}
.mqtt-check-row{display:flex;align-items:center;justify-content:space-between;padding:3px 0}
.mqtt-check-row label{font-size:12px;color:var(--ink-2);display:flex;align-items:center;gap:7px;cursor:pointer}
.mqtt-check-row label i{color:var(--purple);font-size:11px}
.tog-mqtt input:checked+.toggle-slider{background:rgba(200,130,200,.3);border-color:var(--purple)}
.tog-mqtt input:checked+.toggle-slider::before{background:var(--purple)}
/* ══ MÉTRIQUES — tableau 3 colonnes : métrique / UDP / MQTT ══ */
.metrics-table{display:flex;flex-direction:column;gap:0;border:1px solid var(--border-2);border-radius:8px;overflow:hidden}
.metrics-header{display:grid;grid-template-columns:1fr 56px 56px;background:var(--bg-4);padding:8px 12px;gap:4px;align-items:center}
.mh-label{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
.mh-proto{font-size:9px;font-family:var(--font-terminal);font-weight:700;letter-spacing:.04em;text-align:center;display:flex;flex-direction:column;align-items:center;gap:2px}
.mh-proto.udp{color:var(--blue)}.mh-proto.mqtt{color:var(--purple)}
.mh-proto .proto-dot{width:5px;height:5px;border-radius:50%;margin-top:1px}
.mh-proto.udp .proto-dot{background:var(--blue)}.mh-proto.mqtt .proto-dot{background:var(--purple)}
.metric-row{display:grid;grid-template-columns:1fr 56px 56px;padding:8px 12px;gap:4px;align-items:center;background:var(--bg-3);border-top:1px solid var(--border-1);transition:background .1s}
.metric-row:hover{background:var(--bg-4)}
.metric-cell{display:flex;align-items:center;gap:8px}
.metric-ico{font-size:13px;color:var(--ink-3);width:16px;text-align:center}
.metric-name{font-size:12px;color:var(--ink-2);font-family:var(--font-terminal)}
.metric-chk{display:flex;justify-content:center}
/* checkbox compact — user-select:none pour éviter la sélection au clic rapide */
.cbox{width:20px;height:20px;border-radius:5px;border:2px solid var(--border-2);background:var(--bg-1);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:11px;color:transparent;transition:background .12s,border-color .12s,color .12s,transform .08s;user-select:none;flex-shrink:0}
.cbox:hover{border-color:var(--border-3);transform:scale(1.08)}
.cbox:active{transform:scale(.93)}
.cbox.udp-on{background:rgba(61,176,209,.18);border-color:var(--blue);color:var(--blue)}
.cbox.mqtt-on{background:rgba(200,130,200,.18);border-color:var(--purple);color:var(--purple)}
/* commandes */
.cmds-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}
.cmd-btn{display:flex;flex-direction:column;align-items:center;gap:4px;padding:10px 8px;border-radius:8px;background:var(--bg-3);border:1px solid var(--border-1);cursor:not-allowed;opacity:.4}
.cmd-btn i{font-size:16px;color:var(--ink-3)}.cmd-btn span{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
.soon-tag{font-size:8px;color:var(--ink-4);font-family:var(--font-terminal)}
.cfg-foot{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px}
.cfg-status{flex:1;display:flex;align-items:center;gap:6px;font-family:var(--font-terminal);font-size:11px;color:var(--ink-4)}
.cfg-status .dot{width:6px;height:6px;border-radius:50%;background:var(--ok)}
/* SMART */
#overlay-smart{z-index:300}
#popup-smart{width:500px;max-width:96vw;max-height:88vh}
.smart-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
.smart-head-icon{width:32px;height:32px;border-radius:8px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--ok);font-size:15px}
.smart-head-info{flex:1}.smart-head-title{font-weight:700;font-size:14px}.smart-head-sub{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal)}
.smart-body{padding:18px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;max-height:70vh}
.smart-verdict{display:flex;align-items:center;gap:14px;background:rgba(77,187,38,.1);border:1px solid rgba(77,187,38,.3);border-radius:10px;padding:14px 18px}
.verdict-icon{font-size:28px;color:var(--ok)}
.verdict-text .v-title{font-size:16px;font-weight:700;color:var(--ok)}
.verdict-text .v-sub{font-size:12px;color:var(--ink-3);margin-top:3px}
.smart-indicators{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.smart-ind{background:var(--bg-3);border-radius:8px;padding:12px 14px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
.si-header{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.si-icon{font-size:14px;width:22px;text-align:center}
.si-title{font-weight:600;font-size:12px;flex:1}
.si-status{font-size:10px;font-family:var(--font-terminal);font-weight:700;padding:1px 7px;border-radius:999px}
.si-status.ok{background:rgba(77,187,38,.15);color:var(--ok)}
.si-val{font-family:var(--font-mono);font-size:18px;font-weight:700;color:var(--ink-1)}
.si-val .u{font-size:11px;color:var(--ink-3);font-weight:400}
.si-desc{font-size:11px;color:var(--ink-3);margin-top:4px;line-height:1.4}
.smart-attrs{display:flex;flex-direction:column;gap:5px}
.attr-row{display:flex;align-items:center;gap:10px;padding:6px 10px;border-radius:6px;background:var(--bg-3);border:1px solid var(--border-1)}
.attr-id{font-family:var(--font-mono);font-size:10px;color:var(--ink-4);width:28px;flex-shrink:0}
.attr-name{flex:1;font-size:11px;color:var(--ink-2)}
.attr-val{font-family:var(--font-mono);font-size:11px;font-weight:600;width:40px;text-align:right}
.attr-explain{font-size:10px;color:var(--ink-4);width:130px;text-align:right;font-family:var(--font-terminal)}
.attr-ok{color:var(--ok)}
.smart-footer{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px}
.smart-footer-note{flex:1;font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--bg-1)}::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
</style>
</head>
<body>
<div id="tooltip"></div>
<!-- HEADER -->
<div class="header">
<div class="logo"><div class="logo-led"></div><span class="logo-name">NANOMETRICS</span><span class="logo-ver">v1.0</span></div>
<div class="h-sep"></div>
<div class="h-stats">
<div class="h-stat"><span class="lbl">AGENTS</span><span class="val c-n">8</span></div>
<div class="h-stat"><span class="lbl">OK</span><span class="val c-ok">5</span></div>
<div class="h-stat"><span class="lbl">WARN</span><span class="val c-warn">2</span></div>
<div class="h-stat"><span class="lbl">ERR</span><span class="val c-err">1</span></div>
</div>
<div class="h-spacer"></div>
<div class="hbtn" onclick="toggleTheme()" data-tip="Thème clair / sombre"><i class="fa-solid fa-moon" id="theme-icon"></i></div>
<div class="hbtn" id="btn-srvcfg" onclick="showSrvCfg()" data-tip="Configuration serveur / interface"><i class="fa-solid fa-sliders"></i></div>
</div>
<!-- GRID -->
<div class="main">
<div class="agents-grid">
<div class="tile" onclick="showDetail()">
<div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-prod-01</div><div class="t-ip">10.0.0.11</div></div><div class="t-led s-ok"></div></div>
<div class="tile-gauges">
<div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
<div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:58%"></div></div><span class="g-val">58%</span></div>
<div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
</div>
<div class="tile-foot"><i class="fa-solid fa-clock"></i>14j 6h</div>
</div>
<div class="tile t-warn"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-backup-02</div><div class="t-ip">10.0.0.12</div></div><div class="t-led s-warn"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU élevé" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill w" style="width:78%"></div></div><span class="g-val" style="color:var(--warn)">78%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM élevée" style="color:var(--warn)"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill w" style="width:72%"></div></div><span class="g-val" style="color:var(--warn)">72%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>3j 14h</div></div>
<div class="tile t-err"><div class="tile-head"><div class="t-icon" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div><div class="t-names"><div class="t-host">rpi-sensor-03</div><div class="t-ip">10.0.0.23</div></div><div class="t-led s-err"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque critique !" style="color:var(--err)"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill e" style="width:94%"></div></div><span class="g-val" style="color:var(--err)">94%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>62j 1h</div></div>
<div class="tile"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-hard-drive"></i></div><div class="t-names"><div class="t-host">nas-storage-04</div><div class="t-ip">10.0.0.30</div></div><div class="t-led s-ok"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:45%"></div></div><span class="g-val">45%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:66%"></div></div><span class="g-val">66%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>128j 3h</div></div>
<div class="tile t-off"><div class="tile-head"><div class="t-icon" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-dev-07</div><div class="t-ip">10.0.0.60</div></div><div class="t-led s-off"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)"></span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)"></span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)"></span></div></div><div class="tile-foot" style="color:var(--err)"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</div></div>
</div>
</div>
<!-- FOOTER -->
<div class="footer">
<div class="f-mode">LIVE</div>
<div class="f-cell"><i class="fa-solid fa-server" style="font-size:10px"></i><span>SERVEUR</span></div>
<div class="f-cell"><i class="fa-solid fa-microchip" style="font-size:10px"></i><span class="f-val">18%</span><div class="f-minibar"><div class="f-minifill" style="width:18%"></div></div></div>
<div class="f-cell"><i class="fa-solid fa-memory" style="font-size:10px"></i><span class="f-val w">71%</span><div class="f-minibar"><div class="f-minifill w" style="width:71%"></div></div></div>
<div class="f-spacer"></div>
<div class="f-right"><i class="fa-solid fa-rotate"></i><span>Actualisation : <span class="f-time">22:14:07</span></span></div>
</div>
<!-- ══ CONFIG SERVEUR ══ -->
<div class="overlay" id="overlay-srvcfg" style="display:none" onclick="if(event.target===this)hideSrvCfg()">
<div class="popup" id="popup-srvcfg" onclick="event.stopPropagation()">
<div class="scfg-head">
<div class="scfg-icon"><i class="fa-solid fa-sliders"></i></div>
<span class="scfg-title">Configuration interface</span>
<div class="pop-close" onclick="hideSrvCfg()" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
</div>
<div class="scfg-body">
<div class="scfg-section">
<div class="scfg-sec-title">AFFICHAGE DES TUILES</div>
<div class="scfg-row">
<label>Largeur min.</label>
<input type="range" class="scfg-slider" min="160" max="420" value="220" oninput="this.nextElementSibling.textContent=this.value+'px'">
<span class="scfg-val">220px</span>
</div>
<div class="scfg-row">
<label>Taille du texte</label>
<input type="range" class="scfg-slider" min="10" max="18" value="13" oninput="this.nextElementSibling.textContent=this.value+'px'">
<span class="scfg-val">13px</span>
</div>
</div>
<div class="scfg-section">
<div class="scfg-sec-title">SEUILS D'ALERTE PAR DÉFAUT</div>
<div class="scfg-row">
<label>Warning CPU/RAM</label>
<input type="range" class="scfg-slider" min="50" max="95" value="70" oninput="this.nextElementSibling.textContent=this.value+'%'">
<span class="scfg-val">70%</span>
</div>
<div class="scfg-row">
<label>Erreur CPU/RAM</label>
<input type="range" class="scfg-slider" min="60" max="100" value="85" oninput="this.nextElementSibling.textContent=this.value+'%'">
<span class="scfg-val">85%</span>
</div>
<div class="scfg-row">
<label>Warning Disque</label>
<input type="range" class="scfg-slider" min="50" max="95" value="75" oninput="this.nextElementSibling.textContent=this.value+'%'">
<span class="scfg-val">75%</span>
</div>
</div>
<div class="scfg-section">
<div class="scfg-sec-title">DONNÉES & RÉTENTION</div>
<div class="scfg-row">
<label>Historique</label>
<select class="scfg-select"><option>7 jours</option><option selected>30 jours</option><option>90 jours</option><option>1 an</option></select>
</div>
<div class="scfg-row">
<label>Courbes (durée)</label>
<select class="scfg-select"><option>15 min</option><option selected>30 min</option><option>1 heure</option><option>6 heures</option></select>
</div>
</div>
<div class="scfg-section">
<div class="scfg-sec-title">COMPORTEMENT</div>
<div class="scfg-toggle-row">
<label><i class="fa-solid fa-eye-slash"></i> Masquer les agents hors ligne</label>
<label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
</div>
<div class="scfg-toggle-row">
<label><i class="fa-solid fa-bell"></i> Notifications navigateur</label>
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
</div>
<div class="scfg-toggle-row">
<label><i class="fa-solid fa-up-right-and-down-left-from-center"></i> Mémoriser taille des popups</label>
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
</div>
</div>
</div>
<div class="scfg-foot">
<button class="btn" onclick="hideSrvCfg()">Annuler</button>
<button class="btn primary" onclick="hideSrvCfg()"><i class="fa-solid fa-floppy-disk"></i> Sauvegarder</button>
</div>
</div>
</div>
<!-- ══ POPUP DÉTAIL AGENT ══ -->
<div class="overlay" id="overlay-detail" style="display:flex" onclick="if(event.target===this)this.style.display='none'">
<div class="popup" id="popup-detail" onclick="event.stopPropagation()">
<div class="pop-head">
<div class="agent-icon-wrap" onclick="document.getElementById('icon-upload').click()" data-tip="Changer l'icône">
<i class="fa-solid fa-server"></i>
<div class="agent-icon-overlay"><i class="fa-solid fa-camera"></i><span>Changer</span></div>
</div>
<input type="file" id="icon-upload" accept=".svg,.jpg,.jpeg,.png,.webp">
<div class="pop-head-info">
<div class="pop-host">srv-prod-01</div>
<div class="pop-ip">10.0.0.11</div>
<div class="upload-hint">Cliquer sur l'icône pour personnaliser · SVG JPG PNG WEBP · max 128×128 px</div>
</div>
<div class="pop-led"></div>
<div class="pop-close" onclick="document.getElementById('overlay-detail').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
</div>
<div class="pop-body">
<div>
<div class="sec-title">MÉTRIQUES ACTUELLES</div>
<div class="kpi-grid">
<div class="kpi"><div class="kpi-lbl">CPU</div><div class="kpi-val c-ok">42<span class="u">%</span></div><div class="kpi-sub">4 cœurs</div></div>
<div class="kpi"><div class="kpi-lbl">MÉMOIRE</div><div class="kpi-val">3.7<span class="u">Go</span></div><div class="kpi-sub">/ 8 Go · 46%</div></div>
<div class="kpi"><div class="kpi-lbl">DISQUE</div><div class="kpi-val">62<span class="u">Go</span></div><div class="kpi-sub">/ 200 Go · 31%</div></div>
<div class="kpi"><div class="kpi-lbl">UPTIME</div><div class="kpi-val" style="font-size:15px;color:var(--ink-1)">14j 6h</div><div class="kpi-sub">depuis boot</div></div>
</div>
</div>
<div>
<div class="sec-title">HISTORIQUE — 30 MIN</div>
<div class="charts-grid">
<div class="chart-card">
<div class="chart-header"><div class="chart-label" style="color:var(--accent)"><i class="fa-solid fa-microchip"></i>CPU</div><span class="chart-cur c-ok">42%</span></div>
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="cpu-chart"></svg>
<div class="chart-axis"><span>30min</span><span>15min</span><span>now</span></div>
</div>
<div class="chart-card">
<div class="chart-header"><div class="chart-label" style="color:var(--blue)"><i class="fa-solid fa-memory"></i>RAM</div><span class="chart-cur" style="color:var(--blue)">46%</span></div>
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="ram-chart"></svg>
<div class="chart-axis"><span>30min</span><span>15min</span><span>now</span></div>
</div>
</div>
</div>
<div>
<div class="sec-title">STOCKAGE</div>
<div class="storage-block">
<div class="det-gauges">
<div class="dg-row"><div class="dg-ico" data-tip="Utilisé"><i class="fa-solid fa-hard-drive"></i></div><div class="dg-bar"><div class="dg-fill" style="width:31%"></div></div><span class="dg-val">62 / 200 Go</span></div>
<div class="dg-row"><div class="dg-ico" data-tip="Libre" style="color:var(--blue)"><i class="fa-solid fa-floppy-disk"></i></div><div class="dg-bar"><div class="dg-fill b" style="width:69%"></div></div><span class="dg-val">138 Go libre</span></div>
</div>
<div class="smart-btn ok" onclick="showSmart();event.stopPropagation()" data-tip="Voir la santé complète du disque">
<div class="smart-dot"></div>
<span style="font-weight:600;letter-spacing:.04em">SMART</span>
<span style="font-size:10px;color:var(--ink-3)">·</span>
<span style="font-size:11px;font-family:var(--font-terminal)">PASSED</span>
<span style="font-family:var(--font-mono);font-size:10px;color:var(--ink-3);margin-left:4px"><i class="fa-solid fa-temperature-half"></i> 34°C</span>
<i class="fa-solid fa-chevron-right" style="font-size:10px;color:var(--ink-4);margin-left:auto"></i>
</div>
</div>
</div>
<div>
<div class="sec-title">INFORMATIONS</div>
<div class="meta-grid">
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">srv-prod-01</div></div>
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">10.0.0.11</div></div>
<div class="meta"><div class="meta-lbl">PROTOCOLES ACTIFS</div><div class="proto-badges"><span class="proto-badge udp"><i class="fa-solid fa-arrow-up"></i>UDP</span><span class="proto-badge mqtt">M MQTT</span></div></div>
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">22:14:07</div></div>
</div>
</div>
</div>
<div class="pop-foot">
<span class="pop-uptime"><i class="fa-solid fa-clock" style="margin-right:4px"></i>En ligne depuis 14 jours 6 heures</span>
<span class="resize-hint" data-tip="Taille sauvegardée sur le serveur"><i class="fa-solid fa-up-right-and-down-left-from-center"></i>Redimensionnable</span>
<div class="btn-agent-cfg" onclick="showAgentCfg()" data-tip="Configurer l'agent"><i class="fa-solid fa-gears"></i></div>
</div>
</div>
</div>
<!-- ══ CONFIG AGENT ══ -->
<div class="overlay" id="overlay-agentcfg" style="display:none;z-index:200" onclick="if(event.target===this)this.style.display='none'">
<div class="popup" id="popup-agentcfg" onclick="event.stopPropagation()">
<div class="cfg-head">
<div class="cfg-head-icon"><i class="fa-solid fa-gears"></i></div>
<div class="cfg-head-info"><div class="cfg-head-title">Configuration de l'agent</div><div class="cfg-head-sub">srv-prod-01 · 10.0.0.11 · config récupérée à 22:14:05</div></div>
<div class="pop-close" onclick="this.closest('.overlay').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
</div>
<div class="cfg-body">
<!-- MÉTRIQUES PAR PROTOCOLE — tableau 3 colonnes sans activation générale -->
<div class="cfg-section">
<div class="cfg-sec-title">MÉTRIQUES PAR PROTOCOLE</div>
<div class="metrics-table">
<div class="metrics-header">
<span class="mh-label">MÉTRIQUE</span>
<span class="mh-proto udp">
<i class="fa-solid fa-arrow-up"></i> UDP
<span class="proto-dot"></span>
</span>
<span class="mh-proto mqtt">
<i class="fa-solid fa-tower-broadcast" style="font-size:8px"></i> MQTT
<span class="proto-dot"></span>
</span>
</div>
<div class="metric-row">
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-microchip"></i></div><span class="metric-name">cpu</span></div>
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="CPU via UDP"><i class="fa-solid fa-check"></i></div></div>
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="CPU via MQTT"><i class="fa-solid fa-check"></i></div></div>
</div>
<div class="metric-row">
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-memory"></i></div><span class="metric-name">memory</span></div>
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="RAM via UDP"><i class="fa-solid fa-check"></i></div></div>
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="RAM via MQTT"><i class="fa-solid fa-check"></i></div></div>
</div>
<div class="metric-row">
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-hard-drive"></i></div><span class="metric-name">disk</span></div>
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="Disque via UDP"><i class="fa-solid fa-check"></i></div></div>
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Disque via MQTT"><i class="fa-solid fa-check"></i></div></div>
</div>
<div class="metric-row">
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-shield-heart"></i></div><span class="metric-name">smart</span></div>
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="SMART via UDP"><i class="fa-solid fa-check"></i></div></div>
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'mqtt')" data-tip="SMART via MQTT"><i class="fa-solid fa-check"></i></div></div>
</div>
<div class="metric-row">
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-clock"></i></div><span class="metric-name">uptime</span></div>
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="Uptime via UDP"><i class="fa-solid fa-check"></i></div></div>
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Uptime via MQTT"><i class="fa-solid fa-check"></i></div></div>
</div>
<div class="metric-row">
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-network-wired"></i></div><span class="metric-name">network</span></div>
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'udp')" data-tip="Réseau via UDP"><i class="fa-solid fa-check"></i></div></div>
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'mqtt')" data-tip="Réseau via MQTT"><i class="fa-solid fa-check"></i></div></div>
</div>
<div class="metric-row">
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-thermometer-half"></i></div><span class="metric-name">temperature</span></div>
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'udp')" data-tip="Température via UDP"><i class="fa-solid fa-check"></i></div></div>
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Température via MQTT"><i class="fa-solid fa-check"></i></div></div>
</div>
</div>
</div>
<!-- PARAMÈTRES MQTT -->
<div class="cfg-section">
<div class="cfg-sec-title">PARAMÈTRES MQTT</div>
<div class="mqtt-opts">
<div class="mqtt-field"><label>Broker</label><input class="mqtt-input" type="text" value="10.0.0.3"></div>
<div class="mqtt-field"><label>Port</label><input class="mqtt-input" type="number" value="1883" style="width:90px;flex:none"></div>
<div class="mqtt-field"><label>Topic base</label><input class="mqtt-input" type="text" value="nanometrics/agents"></div>
<div style="border-top:1px solid var(--border-1);padding-top:8px;display:flex;flex-direction:column;gap:5px">
<div class="mqtt-check-row"><label><i class="fa-solid fa-satellite-dish"></i> Auto-discovery (Home Assistant)</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
<div class="mqtt-check-row"><label><i class="fa-solid fa-arrow-right-to-bracket"></i> Birth message</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
<div class="mqtt-check-row"><label><i class="fa-solid fa-skull"></i> Last Will message</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
</div>
</div>
</div>
<!-- COMMANDES -->
<div class="cfg-section">
<div class="cfg-sec-title">COMMANDES DISTANTES <span style="color:var(--ink-4);font-size:8px;margin-left:6px">— BIENTÔT</span></div>
<div class="cmds-grid">
<div class="cmd-btn"><i class="fa-solid fa-rotate-right"></i><span>reboot</span><span class="soon-tag">bientôt</span></div>
<div class="cmd-btn"><i class="fa-solid fa-power-off"></i><span>shutdown</span><span class="soon-tag">bientôt</span></div>
<div class="cmd-btn"><i class="fa-solid fa-display"></i><span>screen off</span><span class="soon-tag">bientôt</span></div>
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-from-bracket"></i><span>update</span><span class="soon-tag">bientôt</span></div>
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-right-dots"></i><span>upgrade</span><span class="soon-tag">bientôt</span></div>
<div class="cmd-btn"><i class="fa-solid fa-terminal"></i><span>shell cmd</span><span class="soon-tag">bientôt</span></div>
</div>
</div>
</div>
<div class="cfg-foot">
<div class="cfg-status"><div class="dot"></div><span>Config synchronisée avec l'agent</span></div>
<button class="btn" onclick="this.closest('.overlay').style.display='none'">Annuler</button>
<button class="btn primary"><i class="fa-solid fa-paper-plane"></i> Envoyer à l'agent</button>
</div>
</div>
</div>
<!-- SMART -->
<div class="overlay" id="overlay-smart" style="display:none;z-index:300" onclick="if(event.target===this)this.style.display='none'">
<div class="popup" id="popup-smart" onclick="event.stopPropagation()">
<div class="smart-head">
<div class="smart-head-icon"><i class="fa-solid fa-shield-heart"></i></div>
<div class="smart-head-info"><div class="smart-head-title">Santé du disque dur</div><div class="smart-head-sub">srv-prod-01 · /dev/sda · Samsung SSD 870 EVO 250GB</div></div>
<div class="pop-close" onclick="this.closest('.overlay').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
</div>
<div class="smart-body">
<div class="smart-verdict">
<div class="verdict-icon"><i class="fa-solid fa-circle-check"></i></div>
<div class="verdict-text"><div class="v-title">Disque en bonne santé</div><div class="v-sub">Aucun problème détecté. Le disque fonctionne normalement et peut être utilisé en toute confiance.</div></div>
</div>
<div>
<div class="sec-title">POINTS DE CONTRÔLE</div>
<div class="smart-indicators">
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--warn)"><i class="fa-solid fa-temperature-half"></i></div><span class="si-title">Température</span><span class="si-status ok">Normale</span></div><div class="si-val">34<span class="u">°C</span></div><div class="si-desc">Idéal : 20-50°C. Au-delà de 60°C le disque risque de s'abîmer prématurément.</div></div>
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--ok)"><i class="fa-solid fa-circle-check"></i></div><span class="si-title">Secteurs défectueux</span><span class="si-status ok">Aucun</span></div><div class="si-val">0<span class="u"> sect.</span></div><div class="si-desc">S'ils apparaissent en grand nombre, une panne est imminente.</div></div>
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--blue)"><i class="fa-solid fa-clock-rotate-left"></i></div><span class="si-title">Heures de fonctionnement</span><span class="si-status ok">Jeune</span></div><div class="si-val">4<span class="u">213h</span></div><div class="si-desc">≈175 jours. Un disque dure en moyenne 3 à 5 ans.</div></div>
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--ok)"><i class="fa-solid fa-battery-full"></i></div><span class="si-title">Durée de vie SSD</span><span class="si-status ok">Excellente</span></div><div class="si-val">98<span class="u">%</span></div><div class="si-desc">100% = neuf · 0% = fin de vie recommandée.</div></div>
</div>
</div>
<div>
<div class="sec-title">ATTRIBUTS DÉTAILLÉS</div>
<div class="smart-attrs">
<div class="attr-row"><span class="attr-id">001</span><span class="attr-name">Taux d'erreurs de lecture</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Parfait</span></div>
<div class="attr-row"><span class="attr-id">005</span><span class="attr-name">Secteurs réalloués</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun dommage</span></div>
<div class="attr-row"><span class="attr-id">009</span><span class="attr-name">Heures de fonctionnement</span><span class="attr-val attr-ok">4213</span><span class="attr-explain">175 jours</span></div>
<div class="attr-row"><span class="attr-id">177</span><span class="attr-name">Usure cellules SSD</span><span class="attr-val attr-ok">2</span><span class="attr-explain attr-ok">✓ Très faible</span></div>
<div class="attr-row"><span class="attr-id">190</span><span class="attr-name">Température</span><span class="attr-val attr-ok">34°C</span><span class="attr-explain attr-ok">✓ Normale</span></div>
<div class="attr-row"><span class="attr-id">197</span><span class="attr-name">Secteurs instables</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun</span></div>
<div class="attr-row"><span class="attr-id">198</span><span class="attr-name">Secteurs non corrigeables</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun</span></div>
</div>
</div>
</div>
<div class="smart-footer">
<span class="smart-footer-note"><i class="fa-solid fa-circle-info" style="margin-right:4px"></i>Données via smartctl · actualisées à chaque cycle de l'agent</span>
<button class="btn primary" onclick="this.closest('.overlay').style.display='none'"><i class="fa-solid fa-xmark"></i> Fermer</button>
</div>
</div>
</div>
<script>
/* tooltip */
const tip=document.getElementById('tooltip');let tt;
document.addEventListener('mouseover',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tt=setTimeout(()=>{tip.textContent=el.dataset.tip;tip.classList.add('show');mv(e);},120);});
document.addEventListener('mousemove',e=>{if(tip.classList.contains('show'))mv(e);});
document.addEventListener('mouseout',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tip.classList.remove('show');});
function mv(e){const w=tip.offsetWidth,h=tip.offsetHeight;let x=e.clientX-w/2,y=e.clientY-h-10;x=Math.max(6,Math.min(x,window.innerWidth-w-6));if(y<6)y=e.clientY+18;tip.style.left=x+'px';tip.style.top=y+'px';}
/* thème */
function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark';document.getElementById('theme-icon').className=h.dataset.theme==='dark'?'fa-solid fa-moon':'fa-solid fa-sun';drawCharts();}
/* config serveur */
function showSrvCfg(){document.getElementById('overlay-srvcfg').style.display='flex';document.getElementById('btn-srvcfg').classList.add('active-btn');}
function hideSrvCfg(){document.getElementById('overlay-srvcfg').style.display='none';document.getElementById('btn-srvcfg').classList.remove('active-btn');}
/* popups */
function showDetail(){document.getElementById('overlay-detail').style.display='flex';drawCharts();}
function showAgentCfg(){document.getElementById('overlay-agentcfg').style.display='flex';}
function showSmart(){document.getElementById('overlay-smart').style.display='flex';}
/* checkbox métrique par protocole */
function toggleCbox(el,proto){
const isOn=el.classList.contains(proto+'-on');
el.classList.toggle(proto+'-on',!isOn);
el.style.color=isOn?'transparent':'';
}
/* courbes */
function makeCurve(pts,stroke,fill,w,h){
const xs=pts.map((_,i)=>(i/(pts.length-1))*w);
const ys=pts.map(v=>h-(v/100)*(h-6)-3);
const wy=h-(70/100)*(h-6)-3;
let d=`M${xs[0]} ${ys[0]}`;
for(let i=1;i<pts.length;i++){const cx=(xs[i-1]+xs[i])/2;d+=` C${cx} ${ys[i-1]},${cx} ${ys[i]},${xs[i]} ${ys[i]}`;}
const uid=Math.random().toString(36).slice(2);
return `<defs><linearGradient id="g${uid}" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="${fill}" stop-opacity=".4"/><stop offset="100%" stop-color="${fill}" stop-opacity=".02"/></linearGradient></defs>
<line x1="0" y1="${wy}" x2="${w}" y2="${wy}" stroke="var(--warn)" stroke-width=".8" stroke-dasharray="3,3" opacity=".5"/>
<path d="${d} L${xs.at(-1)} ${h} L${xs[0]} ${h}Z" fill="url(#g${uid})"/>
<path d="${d}" fill="none" stroke="${stroke}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="${xs.at(-1)}" cy="${ys.at(-1)}" r="2.5" fill="${stroke}"/>`;
}
function drawCharts(){
const cpu=[38,41,35,40,44,42,39,45,50,48,43,42,44,41,40,43,47,45,42,44,41,43,42,40,43,41,44,43,41,42];
const ram=[44,44,45,45,46,46,47,47,46,46,45,46,46,47,47,46,46,45,46,46,46,46,46,46,46,46,46,46,46,46];
const cs=getComputedStyle(document.documentElement);
const ac=cs.getPropertyValue('--accent').trim(),bl=cs.getPropertyValue('--blue').trim();
document.getElementById('cpu-chart').innerHTML=makeCurve(cpu,ac,ac,200,52);
document.getElementById('ram-chart').innerHTML=makeCurve(ram,bl,bl,200,52);
}
/* resize popup → sauvegarde serveur */
const pd=document.getElementById('popup-detail');
new ResizeObserver(()=>{
/* en prod : fetch('/api/config', {method:'PUT', body: JSON.stringify({popup_detail_size:{w:pd.offsetWidth,h:pd.offsetHeight}})}) */
console.log('Taille sauvegardée sur serveur:',pd.offsetWidth,'x',pd.offsetHeight);
}).observe(pd);
window.addEventListener('load',drawCharts);
</script>
</body>
</html>