355 lines
21 KiB
HTML
355 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>WEVIA Master AI</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root{--bg:#080c14;--bg2:#0f1520;--bg3:#182030;--bg4:#1e293b;--bd:#1e293b;--fg:#e8ecf4;--fg2:#8899b0;--fg3:#506078;--ac:#10b981;--ac2:#059669;--w:#f59e0b;--rd:#ef4444;--bl:#3b82f6;--vi:#8b5cf6}
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
html,body{height:100%;overflow:hidden}
|
|
body{background:var(--bg);color:var(--fg);font-family:'DM Sans',sans-serif;display:flex}
|
|
|
|
/* SIDEBAR */
|
|
.side{width:220px;background:var(--bg2);border-right:1px solid var(--bd);display:flex;flex-direction:column;flex-shrink:0}
|
|
.side-h{padding:16px;border-bottom:1px solid var(--bd)}
|
|
.side-h h2{font-size:18px;font-weight:700;color:var(--ac)}
|
|
.side-h p{font-size:10px;color:var(--fg3);margin-top:2px}
|
|
.side-sec{padding:12px 0;border-bottom:1px solid var(--bd)}
|
|
.side-sec h4{font-size:9px;text-transform:uppercase;letter-spacing:1.5px;color:var(--fg3);padding:0 16px 6px}
|
|
.side-btn{display:flex;align-items:center;gap:8px;padding:6px 16px;cursor:pointer;font-size:12px;color:var(--fg2);transition:.15s;border:none;background:none;width:100%;text-align:left}
|
|
.side-btn:hover{background:var(--bg3);color:var(--fg)}
|
|
.side-btn .ic{font-size:14px;width:20px;text-align:center}
|
|
.side-f{margin-top:auto;padding:12px 16px;border-top:1px solid var(--bd);font-size:9px;color:var(--fg3);line-height:1.5}
|
|
.live-dot{width:6px;height:6px;border-radius:50%;background:var(--ac);display:inline-block;animation:pulse 2s infinite}
|
|
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
|
|
|
|
/* MAIN */
|
|
.main{flex:1;display:flex;flex-direction:column;min-width:0}
|
|
.top-bar{height:48px;background:var(--bg2);border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:space-between;padding:0 20px;flex-shrink:0}
|
|
.top-bar h3{font-size:14px;font-weight:700}
|
|
.status{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--fg2)}
|
|
|
|
/* MESSAGES */
|
|
.msgs{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:12px}
|
|
.msgs::-webkit-scrollbar{width:4px}
|
|
.msgs::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:2px}
|
|
.msg{max-width:75%;padding:12px 16px;border-radius:12px;font-size:13px;line-height:1.6;animation:fadeIn .3s ease}
|
|
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
|
|
.msg-u{align-self:flex-end;background:var(--ac2);color:#fff;border-bottom-right-radius:4px}
|
|
.msg-a{align-self:flex-start;background:var(--bg3);border:1px solid var(--bd);border-bottom-left-radius:4px}
|
|
.msg-a pre{background:var(--bg);border:1px solid var(--bd);border-radius:6px;padding:10px;margin:8px 0;overflow-x:auto;font-family:'JetBrains Mono',monospace;font-size:11px;line-height:1.5}
|
|
.msg-a code{font-family:'JetBrains Mono',monospace;font-size:11px;background:var(--bg);padding:1px 4px;border-radius:3px}
|
|
.msg-meta{display:flex;align-items:center;gap:8px;margin-top:6px;font-size:9px;color:var(--fg3)}
|
|
.msg-meta .engine{color:var(--ac);font-weight:700}
|
|
.msg-meta .timing{color:var(--fg3)}
|
|
.typing{display:flex;gap:4px;padding:12px 16px}
|
|
.typing span{width:6px;height:6px;background:var(--fg3);border-radius:50%;animation:bounce .6s infinite alternate}
|
|
.typing span:nth-child(2){animation-delay:.15s}
|
|
.typing span:nth-child(3){animation-delay:.3s}
|
|
@keyframes bounce{to{transform:translateY(-6px);background:var(--ac)}}
|
|
|
|
/* WELCOME */
|
|
.welcome{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;padding:40px}
|
|
.welcome h1{font-size:36px;font-weight:700;background:linear-gradient(135deg,var(--ac),var(--bl));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
|
.welcome p{color:var(--fg2);font-size:13px;max-width:400px;text-align:center}
|
|
.cards{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;width:100%;max-width:700px}
|
|
.card{background:var(--bg3);border:1px solid var(--bd);border-radius:10px;padding:14px;cursor:pointer;transition:.2s;text-align:center}
|
|
.card:hover{border-color:var(--ac);transform:translateY(-3px);box-shadow:0 6px 20px rgba(16,185,129,.1)}
|
|
.card .emoji{font-size:22px;margin-bottom:6px}
|
|
.card .label{font-size:11px;font-weight:700;color:var(--fg)}
|
|
.cat-tab{padding:4px 10px;border-radius:6px;font-size:10px;font-weight:700;border:1px solid var(--bd);background:var(--bg3);color:var(--fg2);cursor:pointer;transition:.2s}
|
|
.cat-tab:hover{border-color:var(--ac)}
|
|
.cat-tab.on{background:var(--ac);color:#fff;border-color:var(--ac)}
|
|
.card .desc{font-size:9px;color:var(--fg3);margin-top:2px}
|
|
|
|
/* INPUT */
|
|
.input-wrap{padding:12px 20px;border-top:1px solid var(--bd);background:var(--bg2);flex-shrink:0}
|
|
.input-row{display:flex;gap:8px;max-width:800px;margin:0 auto}
|
|
.input-row input{flex:1;background:var(--bg3);border:1px solid var(--bd);border-radius:10px;padding:12px 16px;color:var(--fg);font-size:13px;font-family:'DM Sans',sans-serif;outline:none;transition:.2s}
|
|
.input-row input:focus{border-color:var(--ac);box-shadow:0 0 0 2px rgba(16,185,129,.15)}
|
|
.input-row input::placeholder{color:var(--fg3)}
|
|
.send-btn{width:42px;height:42px;border-radius:10px;background:var(--ac);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.2s;flex-shrink:0}
|
|
.send-btn:hover{background:var(--ac2);transform:scale(1.05)}
|
|
.send-btn:disabled{opacity:.4;cursor:default;transform:none}
|
|
.send-btn svg{width:18px;height:18px;fill:#fff}
|
|
|
|
@media(max-width:768px){.side{display:none}.cards{grid-template-columns:repeat(2,1fr)}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="side">
|
|
<div class="side-h">
|
|
<h2>WEVIA</h2>
|
|
<p>Master AI v4.0</p>
|
|
</div>
|
|
<div class="side-sec">
|
|
<h4>Agents</h4>
|
|
<button class="side-btn" onclick="ask('ethica')"><span class="ic">💊</span>Ethica HCP</button>
|
|
<button class="side-btn" onclick="ask('deerflow')"><span class="ic">🦌</span>DeerFlow</button>
|
|
<button class="side-btn" onclick="ask('paperclip')"><span class="ic">📎</span>Paperclip</button>
|
|
<button class="side-btn" onclick="ask('blade')"><span class="ic">⚡</span>Blade IA</button>
|
|
<button class="side-btn" onclick="ask('consensus')"><span class="ic">⚖️</span>Consensus</button>
|
|
<button class="side-btn" onclick="ask('wedroid')"><span class="ic">🔧</span>WEDROID</button>
|
|
<button class="side-btn" onclick="ask('director')"><span class="ic">👁️</span>Director</button>
|
|
</div>
|
|
<div class="side-sec">
|
|
<h4>Actions</h4>
|
|
<button class="side-btn" onclick="ask('audit complet')"><span class="ic">🔍</span>Audit Infra</button>
|
|
<button class="side-btn" onclick="ask('auto-fix')"><span class="ic">🔧</span>Auto-Fix</button>
|
|
<button class="side-btn" onclick="ask('lance nonreg')"><span class="ic">🧪</span>NonReg</button>
|
|
<button class="side-btn" onclick="ask('benchmark')"><span class="ic">📊</span>Benchmark</button>
|
|
<button class="side-btn" onclick="ask('securise')"><span class="ic">🛡️</span>Security</button>
|
|
<button class="side-btn" onclick="ask('nettoie le disque')"><span class="ic">🧹</span>Disk Clean</button>
|
|
</div>
|
|
<div class="side-f">
|
|
<span class="live-dot"></span> <span id="prov-count">7</span> providers LIVE | 0€<br>
|
|
<span id="footer-stats">34 patterns | Sovereign</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="main">
|
|
<div class="top-bar">
|
|
<h3>WEVIA Master AI</h3>
|
|
<div class="status"><span class="live-dot"></span> <span id="status-text">Connecté</span></div>
|
|
</div>
|
|
|
|
<div class="msgs" id="msgs">
|
|
<div class="welcome" id="welcome">
|
|
<h1>WEVIA</h1>
|
|
<p>IA souveraine — 39 IAs, 34 patterns, 7 cascade providers, 0€</p>
|
|
<div id="cat-tabs" style="display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-bottom:12px">
|
|
<button class="cat-tab on" onclick="showCat('all')">Tout</button>
|
|
<button class="cat-tab" onclick="showCat('agent')">🤖 Agents</button>
|
|
<button class="cat-tab" onclick="showCat('code')">⚙️ Code</button>
|
|
<button class="cat-tab" onclick="showCat('infra')">🏗️ Infra</button>
|
|
<button class="cat-tab" onclick="showCat('data')">📊 Data</button>
|
|
<button class="cat-tab" onclick="showCat('security')">🛡️ Sécurité</button>
|
|
<button class="cat-tab" onclick="showCat('ai')">🧠 IA</button>
|
|
<button class="cat-tab" onclick="showCat('devops')">🔧 DevOps</button>
|
|
</div>
|
|
<div class="cards" id="intent-grid" style="grid-template-columns:repeat(5,1fr)">
|
|
<div class="card c-agent" onclick="ask('ethica stats')"><div class="emoji">💊</div><div class="label">Ethica</div><div class="desc">128K HCPs</div></div>
|
|
<div class="card c-agent" onclick="ask('deerflow research')"><div class="emoji">🦌</div><div class="label">DeerFlow</div><div class="desc">42 skills</div></div>
|
|
<div class="card c-agent" onclick="ask('paperclip goals')"><div class="emoji">📎</div><div class="label">Paperclip</div><div class="desc">Project mgmt</div></div>
|
|
<div class="card c-agent" onclick="ask('blade status')"><div class="emoji">⚡</div><div class="label">Blade IA</div><div class="desc">34 caps</div></div>
|
|
<div class="card c-agent" onclick="ask('director supervision')"><div class="emoji">👁️</div><div class="label">Director</div><div class="desc">Auto-supervision</div></div>
|
|
<div class="card c-agent" onclick="ask('wedroid diagnostic')"><div class="emoji">🔧</div><div class="label">WEDROID</div><div class="desc">Backend v5</div></div>
|
|
<div class="card c-agent" onclick="ask('consensus strategie')"><div class="emoji">⚖️</div><div class="label">Consensus</div><div class="desc">MoA 9.8/10</div></div>
|
|
<div class="card c-agent" onclick="ask('openclaw ollama')"><div class="emoji">🐙</div><div class="label">OpenClaw</div><div class="desc">5 models local</div></div>
|
|
<div class="card c-agent" onclick="ask('wevcode modes')"><div class="emoji">💻</div><div class="label">WEVCODE</div><div class="desc">4 modes code</div></div>
|
|
<div class="card c-agent" onclick="ask('nuclei pentest')"><div class="emoji">🔬</div><div class="label">Nuclei</div><div class="desc">Vuln scanner</div></div>
|
|
<div class="card c-code" onclick="ask('cree une API REST Flask Python')"><div class="emoji">🐍</div><div class="label">API Python</div><div class="desc">Flask REST</div></div>
|
|
<div class="card c-code" onclick="ask('cree composant React dashboard')"><div class="emoji">⚛️</div><div class="label">React</div><div class="desc">Component</div></div>
|
|
<div class="card c-code" onclick="ask('debug ce code PHP')"><div class="emoji">🐛</div><div class="label">Debug</div><div class="desc">Fix bugs</div></div>
|
|
<div class="card c-code" onclick="ask('refactor cette fonction')"><div class="emoji">✂️</div><div class="label">Refactor</div><div class="desc">Clean code</div></div>
|
|
<div class="card c-code" onclick="ask('cree script bash monitoring')"><div class="emoji">🖥️</div><div class="label">Bash</div><div class="desc">Scripts</div></div>
|
|
<div class="card c-code" onclick="ask('diagramme mermaid architecture')"><div class="emoji">📐</div><div class="label">Mermaid</div><div class="desc">Diagrams</div></div>
|
|
<div class="card c-infra" onclick="ask('audit complet RAM disk Docker')"><div class="emoji">🔍</div><div class="label">Audit</div><div class="desc">RAM·Disk·Docker</div></div>
|
|
<div class="card c-infra" onclick="ask('status serveurs health')"><div class="emoji">💚</div><div class="label">Health</div><div class="desc">Servers status</div></div>
|
|
<div class="card c-infra" onclick="ask('docker containers')"><div class="emoji">🐳</div><div class="label">Docker</div><div class="desc">17 containers</div></div>
|
|
<div class="card c-infra" onclick="ask('port scan ouverts')"><div class="emoji">🔌</div><div class="label">Ports</div><div class="desc">Open ports</div></div>
|
|
<div class="card c-infra" onclick="ask('consolide les crons')"><div class="emoji">⏰</div><div class="label">Crons</div><div class="desc">45 actifs</div></div>
|
|
<div class="card c-infra" onclick="ask('provider status cascade')"><div class="emoji">🔗</div><div class="label">Providers</div><div class="desc">7 cascade</div></div>
|
|
<div class="card c-data" onclick="ask('ethica combien de HCP')"><div class="emoji">💊</div><div class="label">HCPs</div><div class="desc">128K live</div></div>
|
|
<div class="card c-data" onclick="ask('rag qdrant vectors')"><div class="emoji">🧠</div><div class="label">RAG</div><div class="desc">Qdrant vectors</div></div>
|
|
<div class="card c-data" onclick="ask('scan oss skills')"><div class="emoji">🧩</div><div class="label">OSS</div><div class="desc">1935 skills</div></div>
|
|
<div class="card c-data" onclick="ask('vacuum ethica')"><div class="emoji">🗄️</div><div class="label">Vacuum</div><div class="desc">DB optimize</div></div>
|
|
<div class="card c-data" onclick="ask('benchmark classement')"><div class="emoji">📊</div><div class="label">Benchmark</div><div class="desc">39 AIs ranked</div></div>
|
|
<div class="card c-security" onclick="ask('securise firewall')"><div class="emoji">🛡️</div><div class="label">Securise</div><div class="desc">HMAC·CORS</div></div>
|
|
<div class="card c-security" onclick="ask('scan securite nuclei')"><div class="emoji">🔬</div><div class="label">Pentest</div><div class="desc">Nuclei scan</div></div>
|
|
<div class="card c-security" onclick="ask('lance guardian sso')"><div class="emoji">🔒</div><div class="label">Guardian</div><div class="desc">SSO watch</div></div>
|
|
<div class="card c-security" onclick="ask('ssl certificat check')"><div class="emoji">📜</div><div class="label">SSL</div><div class="desc">Cert check</div></div>
|
|
<div class="card c-ai" onclick="ask('cherche tendances IA 2026')"><div class="emoji">🔍</div><div class="label">Search</div><div class="desc">SearXNG web</div></div>
|
|
<div class="card c-ai" onclick="ask('traduis en anglais bonjour')"><div class="emoji">🌐</div><div class="label">Translate</div><div class="desc">Multi-langue</div></div>
|
|
<div class="card c-ai" onclick="ask('redige email professionnel')"><div class="emoji">✉️</div><div class="label">Email</div><div class="desc">Rédaction pro</div></div>
|
|
<div class="card c-ai" onclick="ask('wiring connexion agents')"><div class="emoji">🔌</div><div class="label">Wiring</div><div class="desc">32/39 wired</div></div>
|
|
<div class="card c-devops" onclick="ask('auto-fix')"><div class="emoji">🔧</div><div class="label">Auto-Fix</div><div class="desc">Detect+Correct</div></div>
|
|
<div class="card c-devops" onclick="ask('lance nonreg')"><div class="emoji">🧪</div><div class="label">NonReg</div><div class="desc">152/153</div></div>
|
|
<div class="card c-devops" onclick="ask('test fonctionnel')"><div class="emoji">✅</div><div class="label">Func Test</div><div class="desc">12/15</div></div>
|
|
<div class="card c-devops" onclick="ask('git push status')"><div class="emoji">📦</div><div class="label">Git</div><div class="desc">Push/Status</div></div>
|
|
<div class="card c-devops" onclick="ask('nettoie le disque')"><div class="emoji">🧹</div><div class="label">Cleanup</div><div class="desc">Disk prune</div></div>
|
|
<div class="card c-devops" onclick="ask('monte coverage 100')"><div class="emoji">📈</div><div class="label">Coverage</div><div class="desc">L99 boost</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="input-wrap">
|
|
<div class="input-row">
|
|
<input type="text" id="input" placeholder="Demandez à WEVIA..." autocomplete="off">
|
|
<button class="send-btn" id="send-btn" onclick="send()">
|
|
<svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const msgsEl = document.getElementById('msgs');
|
|
const inputEl = document.getElementById('input');
|
|
const sendBtn = document.getElementById('send-btn');
|
|
const welcomeEl = document.getElementById('welcome');
|
|
const statusEl = document.getElementById('status-text');
|
|
let busy = false;
|
|
|
|
inputEl.addEventListener('keydown', e => { if(e.key==='Enter'&&!e.shiftKey&&!busy) send(); });
|
|
|
|
function ask(text) { inputEl.value = text; send(); }
|
|
|
|
function addMsg(text, type, meta) {
|
|
if(welcomeEl) welcomeEl.style.display='none';
|
|
const div = document.createElement('div');
|
|
div.className = 'msg msg-' + type;
|
|
if(type === 'a') {
|
|
div.innerHTML = formatText(text);
|
|
if(meta) {
|
|
const m = document.createElement('div');
|
|
m.className = 'msg-meta';
|
|
m.innerHTML = `<span class="engine">${meta.engine||''}</span><span class="timing">${meta.time||''}</span>`;
|
|
div.appendChild(m);
|
|
}
|
|
} else {
|
|
div.textContent = text;
|
|
}
|
|
msgsEl.appendChild(div);
|
|
msgsEl.scrollTop = msgsEl.scrollHeight;
|
|
return div;
|
|
}
|
|
|
|
function formatText(text) {
|
|
// Code blocks
|
|
text = text.replace(/```(\w*)\n?([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
|
|
// Inline code
|
|
text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
// Bold
|
|
text = text.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
// Line breaks
|
|
text = text.replace(/\n/g, '<br>');
|
|
return text;
|
|
}
|
|
|
|
function showTyping() {
|
|
const div = document.createElement('div');
|
|
div.className = 'typing';
|
|
div.id = 'typing';
|
|
div.innerHTML = '<span></span><span></span><span></span>';
|
|
msgsEl.appendChild(div);
|
|
msgsEl.scrollTop = msgsEl.scrollHeight;
|
|
}
|
|
|
|
function hideTyping() {
|
|
const t = document.getElementById('typing');
|
|
if(t) t.remove();
|
|
}
|
|
|
|
async function send() {
|
|
const text = inputEl.value.trim();
|
|
if(!text || busy) return;
|
|
|
|
busy = true;
|
|
sendBtn.disabled = true;
|
|
inputEl.value = '';
|
|
statusEl.textContent = 'Réflexion...';
|
|
|
|
addMsg(text, 'u');
|
|
showTyping();
|
|
|
|
const t0 = Date.now();
|
|
|
|
try {
|
|
const res = await fetch('/api/wevia-autonomous.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ message: text }),
|
|
signal: AbortSignal.timeout(30000)
|
|
});
|
|
|
|
hideTyping();
|
|
|
|
if(!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
|
|
const responseText = await res.text();
|
|
const elapsed = ((Date.now() - t0) / 1000).toFixed(1) + 's';
|
|
|
|
// Parse SSE data lines
|
|
let fullText = '';
|
|
let engine = '';
|
|
let intent = '';
|
|
|
|
const lines = responseText.split('\n');
|
|
for(const line of lines) {
|
|
if(line.startsWith('data: ')) {
|
|
try {
|
|
const data = JSON.parse(line.slice(6));
|
|
if(data.type === 'answer') {
|
|
fullText = data.text || '';
|
|
engine = data.engine || '';
|
|
intent = data.intent || '';
|
|
} else if(data.type === 'chunk') {
|
|
fullText += data.text || '';
|
|
engine = data.engine || engine;
|
|
} else if(data.type === 'thinking') {
|
|
statusEl.textContent = data.step || 'Analyse...';
|
|
} else if(data.type === 'exec_result') {
|
|
fullText += (data.desc || '') + ': ' + (data.output || '') + '\n';
|
|
engine = 'Exec';
|
|
} else if(data.type === 'done') {
|
|
engine = data.provider || engine;
|
|
} else if(data.type === 'llm_chunk') {
|
|
fullText += data.text || '';
|
|
engine = data.provider || engine;
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
}
|
|
|
|
if(!fullText) fullText = responseText.replace(/^data: /gm, '').trim() || 'Pas de réponse';
|
|
|
|
addMsg(fullText, 'a', { engine: engine || 'WEVIA', time: elapsed });
|
|
statusEl.textContent = 'Connecté';
|
|
|
|
} catch(err) {
|
|
hideTyping();
|
|
|
|
// Fallback: try wevia-full-exec.php (JSON, no SSE)
|
|
try {
|
|
const res2 = await fetch('/api/wevia-full-exec.php?m=' + encodeURIComponent(text), {
|
|
signal: AbortSignal.timeout(20000)
|
|
});
|
|
/* HTML_GUARD_V2_BATCH */ const _t_data2=await res2.text(); const data2=null; {var _q=(_t_data2||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){data2={error:"[HTTP "+(res2.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{data2=JSON.parse(_q)}catch(e){data2={error:"[JSON] "+e.message}}}}
|
|
const elapsed = ((Date.now() - t0) / 1000).toFixed(1) + 's';
|
|
addMsg(data2.response || 'Pas de réponse', 'a', { engine: data2.provider || 'Exec', time: elapsed });
|
|
} catch(err2) {
|
|
addMsg('Erreur: ' + err.message + '. Réessayez.', 'a', { engine: 'Error', time: '' });
|
|
}
|
|
statusEl.textContent = 'Connecté';
|
|
}
|
|
|
|
busy = false;
|
|
sendBtn.disabled = false;
|
|
inputEl.focus();
|
|
}
|
|
|
|
// Load live stats
|
|
async function loadStats() {
|
|
try {
|
|
const r = await fetch('/api/source-of-truth.json?t=' + Date.now());
|
|
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); let d=null; {var _q=(_t_d||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d={error:"[HTTP "+(r.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:"[JSON] "+e.message}}}}
|
|
document.getElementById('prov-count').textContent = Object.keys(d.providers || {}).length;
|
|
document.getElementById('footer-stats').textContent = Object.keys(d.agents || {}).length + ' agents | Sovereign';
|
|
} catch(e) {}
|
|
}
|
|
function showCat(cat){
|
|
document.querySelectorAll('.cat-tab').forEach(t=>t.classList.toggle('on',t.textContent.includes(cat==='all'?'Tout':cat)));
|
|
document.querySelectorAll('#intent-grid .card').forEach(c=>{
|
|
c.style.display=(cat==='all'||c.className.includes('c-'+cat))?'':'none';
|
|
});
|
|
}
|
|
loadStats();
|
|
</script>
|
|
<!-- CARTO_REMOVED -->
|
|
</body>
|
|
</html>
|