Files
html/wevia-chat.html
opus e30ddf5007
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
auto-sync via WEVIA git_sync_all intent 2026-04-20T13:11:38+02:00
2026-04-20 13:11:38 +02:00

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>