470 lines
27 KiB
HTML
470 lines
27 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('sécurisé')"><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 stratégie')"><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('sécurisé 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 sécurité 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">153/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 -->
|
|
|
|
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
|
|
<script>
|
|
(function(){
|
|
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
|
|
var d = document;
|
|
var m = d.createElement('div');
|
|
m.id = 'opus-udrill';
|
|
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
|
|
var inner = d.createElement('div');
|
|
inner.id = 'opus-udrill-in';
|
|
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
|
|
inner.addEventListener('click', function(e){ e.stopPropagation(); });
|
|
m.appendChild(inner);
|
|
m.addEventListener('click', function(){ m.style.display='none'; });
|
|
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
|
|
(d.body || d.documentElement).appendChild(m);
|
|
|
|
function openCard(card) {
|
|
// Clone card content + show close btn + increase font-size
|
|
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
|
|
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
|
|
inner.innerHTML = html;
|
|
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
|
|
m.style.display = 'flex';
|
|
}
|
|
|
|
function wire(root) {
|
|
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
|
|
var cards = root.querySelectorAll(sels);
|
|
for (var i = 0; i < cards.length; i++) {
|
|
var c = cards[i];
|
|
if (c.__opusWired) continue;
|
|
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
|
|
var r = c.getBoundingClientRect();
|
|
if (r.width < 60 || r.height < 40) continue;
|
|
c.__opusWired = true;
|
|
c.style.cursor = 'pointer';
|
|
c.setAttribute('role','button');
|
|
c.setAttribute('tabindex','0');
|
|
c.addEventListener('click', function(ev){
|
|
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
|
|
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
|
|
if (ev.target.closest('a,button,input,select')) return;
|
|
ev.preventDefault(); ev.stopPropagation();
|
|
openCard(this);
|
|
});
|
|
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
|
|
}
|
|
}
|
|
|
|
// Initial + mutation observer
|
|
var initRun = function(){ wire(d.body || d.documentElement); };
|
|
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
|
|
else initRun();
|
|
var mo = new MutationObserver(function(muts){
|
|
var newCard = false;
|
|
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
|
|
if (newCard) initRun();
|
|
});
|
|
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
|
|
})();
|
|
</script>
|
|
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
|
|
|
|
|
|
<!-- === OPUS HONEST NR/L99 OVERLAY v1 19avr - append-only doctrine #14 === -->
|
|
<script>
|
|
(function(){
|
|
if (window.__opusHonestOverlay) return; window.__opusHonestOverlay = true;
|
|
async function updateHonestValues(){
|
|
try {
|
|
const r = await fetch('/api/l99-honest.php', {cache:'no-store'});
|
|
const d = await r.json();
|
|
if (!d.ok) return;
|
|
const realNR = `${d.combined.pass}/${d.combined.total}`;
|
|
const realSigma = d.sigma;
|
|
// Find elements showing the myth values
|
|
const mythRegex = /(153\/153|304\/304|NR status 153\/153|L99 status 304\/304|NR 153\/153|L99 304\/304)/g;
|
|
// Walk text nodes
|
|
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
|
|
const toReplace = [];
|
|
let node;
|
|
while (node = walker.nextNode()) {
|
|
if (node.nodeValue && mythRegex.test(node.nodeValue)) toReplace.push(node);
|
|
}
|
|
toReplace.forEach(textNode => {
|
|
const parent = textNode.parentNode;
|
|
if (!parent || parent.hasAttribute('data-opus-honest-applied')) return;
|
|
const newText = textNode.nodeValue.replace(/153\/153/g, realNR).replace(/304\/304/g, realNR);
|
|
textNode.nodeValue = newText;
|
|
parent.setAttribute('data-opus-honest-applied', '1');
|
|
});
|
|
// Add a small badge bottom-right showing honest live status
|
|
if (!document.getElementById('opus-honest-badge')) {
|
|
const b = document.createElement('div');
|
|
b.id = 'opus-honest-badge';
|
|
b.style.cssText = 'position:fixed;bottom:12px;right:12px;background:linear-gradient(90deg,#14b8a6,#a855f7);color:#05060a;padding:6px 12px;font:10px/1.3 Inter,system-ui,sans-serif;font-weight:700;border-radius:8px;z-index:99993;box-shadow:0 4px 12px rgba(0,0,0,0.3);cursor:pointer;max-width:280px';
|
|
b.title = 'Cliquer pour détails';
|
|
b.innerHTML = `✓ NR ${realNR} · ${realSigma} live`;
|
|
b.onclick = () => {
|
|
alert(`HONEST NonReg (doctrine #4):\n\nmaster: ${d.master.pass}/${d.master.total}\nopus: ${d.opus.pass}/${d.opus.total}\ncombined: ${realNR}\nsigma: ${realSigma}\n\n${d.myth_153}\n${d.myth_304}`);
|
|
};
|
|
document.body.appendChild(b);
|
|
}
|
|
} catch(e){console.error('L99-honest fetch error:', e);}
|
|
}
|
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', updateHonestValues);
|
|
else updateHonestValues();
|
|
setInterval(updateHonestValues, 90000);
|
|
})();
|
|
</script>
|
|
<!-- === OPUS HONEST END === -->
|
|
|
|
<script src="/api/archi-meta-badge.js" defer></script>
|
|
</body>
|
|
</html>
|