Files
html/weval-arena-v2.html

686 lines
35 KiB
HTML
Raw Permalink 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 lang="fr">
<head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WEVAL Arena v2 — Command Center IA Souveraine</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--bg:#06090f;--s1:#0c1018;--s2:#111827;--s3:#1a2744;
--brd:#1e2d4a;--brd2:#2a3f6a;
--t1:#e8ecf4;--t2:#8899b4;--t3:#506380;
--green:#00e09e;--red:#ff4d6a;--amber:#ffb547;
--blue:#4da6ff;--cyan:#22d3ee;--purple:#a78bfa;
--pink:#f472b6;--orange:#fb923c;
--font:'Outfit',sans-serif;--mono:'JetBrains Mono',monospace;
}
body{background:var(--bg);color:var(--t1);font-family:var(--font);overflow-x:hidden;min-height:100vh}
.app{display:grid;grid-template-columns:1fr 320px;grid-template-rows:auto 1fr auto;height:100vh;gap:0}
/* TOPBAR */
.topbar{grid-column:1/-1;background:linear-gradient(135deg,#0a0f1a 0%,#111b2e 100%);border-bottom:1px solid var(--brd);padding:12px 24px;display:flex;align-items:center;gap:16px}
.topbar .logo{font-size:22px;font-weight:800;letter-spacing:1px;background:linear-gradient(135deg,var(--cyan),var(--green));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.topbar .logo span{font-weight:400;font-size:14px;opacity:.6;-webkit-text-fill-color:var(--t2)}
.topbar .score{margin-left:auto;display:flex;gap:16px;font-family:var(--mono);font-size:12px}
.topbar .score .item{display:flex;align-items:center;gap:6px}
.topbar .score .dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green)}
.topbar .score .val{color:var(--green);font-weight:600}
.topbar .btn-logout{background:transparent;border:1px solid var(--brd);color:var(--t2);padding:6px 14px;border-radius:8px;cursor:pointer;font-size:12px;transition:.2s}
.topbar .btn-logout:hover{border-color:var(--cyan);color:var(--cyan)}
/* MAIN */
.main{overflow-y:auto;padding:24px;display:flex;flex-direction:column;gap:20px}
/* PROVIDER GRID */
.section-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:2px;color:var(--t3);margin-bottom:8px;display:flex;align-items:center;gap:8px}
.section-title::after{content:'';flex:1;height:1px;background:var(--brd)}
.providers-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
.prov-card{background:var(--s1);border:1px solid var(--brd);border-radius:12px;padding:14px;cursor:pointer;transition:all .25s;position:relative;overflow:hidden}
.prov-card:hover{border-color:var(--cyan);transform:translateY(-2px);box-shadow:0 8px 24px rgba(34,211,238,.08)}
.prov-card.active{border-color:var(--green);background:rgba(0,224,158,.05)}
.prov-card .prov-icon{font-size:24px;margin-bottom:8px}
.prov-card .prov-name{font-size:13px;font-weight:600;margin-bottom:2px}
.prov-card .prov-type{font-size:10px;color:var(--t3);font-family:var(--mono)}
.prov-card .prov-badge{position:absolute;top:8px;right:8px;font-size:9px;padding:2px 6px;border-radius:4px;font-weight:600;font-family:var(--mono)}
.prov-card .prov-badge.free{background:rgba(0,224,158,.15);color:var(--green)}
.prov-card .prov-badge.api{background:rgba(77,166,255,.15);color:var(--blue)}
/* CONSENSUS ROW */
.consensus-row{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.consensus-card{background:linear-gradient(135deg,rgba(167,139,250,.08),rgba(34,211,238,.05));border:1px solid rgba(167,139,250,.25);border-radius:12px;padding:16px;cursor:pointer;transition:.25s}
.consensus-card:hover{border-color:var(--purple);box-shadow:0 4px 20px rgba(167,139,250,.1)}
.consensus-card .cc-title{font-size:14px;font-weight:700;display:flex;align-items:center;gap:8px}
.consensus-card .cc-desc{font-size:11px;color:var(--t2);margin-top:4px}
/* MODES */
.modes-bar{display:flex;gap:6px;flex-wrap:wrap}
.mode-btn{background:var(--s1);border:1px solid var(--brd);border-radius:8px;padding:8px 14px;font-size:12px;color:var(--t2);cursor:pointer;transition:.2s;font-family:var(--font)}
.mode-btn:hover{border-color:var(--cyan);color:var(--t1)}
.mode-btn.active{background:rgba(34,211,238,.1);border-color:var(--cyan);color:var(--cyan)}
/* CHAT */
.chat-area{flex:1;min-height:200px;overflow-y:auto;display:flex;flex-direction:column;gap:12px;padding:8px 0}
.msg{max-width:85%;padding:12px 16px;border-radius:12px;font-size:14px;line-height:1.5;animation:fadeIn .3s}
.msg.user{background:var(--s3);align-self:flex-end;border-bottom-right-radius:4px}
.msg.ai{background:var(--s1);border:1px solid var(--brd);align-self:flex-start;border-bottom-left-radius:4px}
.msg .provider-tag{font-size:10px;color:var(--cyan);font-family:var(--mono);margin-top:6px;opacity:.7}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
/* INPUT */
.input-bar{display:flex;gap:8px;padding:16px 0 0}
.input-bar textarea{flex:1;background:var(--s1);border:1px solid var(--brd);border-radius:12px;padding:14px 16px;color:var(--t1);font-size:14px;font-family:var(--font);resize:none;height:52px;transition:.2s}
.input-bar textarea:focus{outline:none;border-color:var(--cyan);box-shadow:0 0 0 3px rgba(34,211,238,.1)}
.input-bar textarea::placeholder{color:var(--t3)}
.input-bar .send-btn{width:52px;height:52px;background:linear-gradient(135deg,var(--cyan),var(--green));border:none;border-radius:12px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.2s}
.input-bar .send-btn:hover{transform:scale(1.05);box-shadow:0 4px 16px rgba(34,211,238,.3)}
.input-bar .send-btn svg{width:20px;height:20px;fill:#06090f}
/* SIDEBAR */
.sidebar{background:var(--s1);border-left:1px solid var(--brd);padding:20px;overflow-y:auto;display:flex;flex-direction:column;gap:16px}
.sidebar .stat-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.stat-box{background:var(--s2);border:1px solid var(--brd);border-radius:10px;padding:12px;text-align:center}
.stat-box .val{font-size:22px;font-weight:700;font-family:var(--mono)}
.stat-box .val.green{color:var(--green)}
.stat-box .val.cyan{color:var(--cyan)}
.stat-box .val.purple{color:var(--purple)}
.stat-box .val.amber{color:var(--amber)}
.stat-box .label{font-size:10px;color:var(--t3);text-transform:uppercase;letter-spacing:1px;margin-top:2px}
.cascade-list{display:flex;flex-direction:column;gap:4px}
.cascade-item{display:flex;align-items:center;gap:8px;padding:6px 0;font-size:12px}
.cascade-item .c-dot{width:6px;height:6px;border-radius:50%;background:var(--green);flex-shrink:0}
.cascade-item .c-name{color:var(--t2)}
.cascade-item.active .c-name{color:var(--t1);font-weight:500}
/* ADVANCED DROPDOWN */
.advanced-toggle{background:var(--s2);border:1px solid var(--brd);border-radius:8px;padding:8px 12px;font-size:11px;color:var(--t3);cursor:pointer;display:flex;align-items:center;gap:6px;width:100%;transition:.2s}
.advanced-toggle:hover{border-color:var(--cyan);color:var(--t2)}
.advanced-select{width:100%;background:var(--s2);border:1px solid var(--brd);border-radius:8px;padding:8px;color:var(--t1);font-size:11px;font-family:var(--mono);display:none;max-height:300px;overflow-y:auto}
.advanced-select.open{display:block}
.advanced-select option{padding:4px 8px}
.advanced-select optgroup{color:var(--cyan);font-style:normal}
/* SELECTED PROVIDER INDICATOR */
.selected-provider{display:flex;align-items:center;gap:8px;padding:8px 14px;background:rgba(34,211,238,.06);border:1px solid rgba(34,211,238,.2);border-radius:8px;font-size:12px}
.selected-provider .sp-icon{font-size:18px}
.selected-provider .sp-name{font-weight:600}
.selected-provider .sp-cost{margin-left:auto;color:var(--green);font-family:var(--mono);font-size:10px}
@media(max-width:900px){
.app{grid-template-columns:1fr}
.sidebar{display:none}
.providers-grid{grid-template-columns:repeat(2,1fr)}
.consensus-row{grid-template-columns:1fr}
}
</style>
<!-- DOCTRINE-60-UX-ENRICH direct-inject-20260424-143215 -->
<style id="doctrine60-ux-direct">
/* DOCTRINE-60-UX-ENRICH injected-direct */
body::before {
content: '';
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: radial-gradient(circle at 50% 50%, rgba(100,180,255,0.08), transparent 60%);
pointer-events: none;
z-index: -1;
}
.card, .kpi, .panel, .btn {
transition: all 0.3s cubic-bezier(0.2,0,0.1,1);
}
.card:hover, .kpi:hover, .panel:hover {
box-shadow: 0 4px 20px rgba(100,180,255,0.2);
border-color: rgba(100,180,255,0.5);
}
@keyframes pulseD60 {
0%,100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.05); }
}
.pulse, .live-indicator, .active, .online {
animation: pulseD60 3s ease-in-out infinite;
}
.modal, .chat, .speech, .overlay {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.enter-stagger {
animation: enterStagD60 0.5s cubic-bezier(0.2,0,0.1,1) forwards;
}
@keyframes enterStagD60 {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="app">
<!-- TOPBAR -->
<div class="topbar">
<div class="logo">WEVAL Arena <span>v2 · Command Center</span></div>
<div class="score">
<div class="item"><span class="dot"></span>NonReg <span class="val">153/153</span></div>
<div class="item"><span class="dot"></span>L99 <span class="val">957/957</span></div>
<div class="item"><span class="dot"></span>Providers <span class="val" id="provCount">8/8</span></div>
</div>
<button class="btn-logout" onclick="location.href='/login'">Logout</button>
</div>
<!-- MAIN CONTENT -->
<div class="main">
<!-- SELECTED PROVIDER -->
<div class="selected-provider" id="selectedProvider">
<span class="sp-icon">🔥</span>
<span class="sp-name">DeepSeek Chat</span>
<span class="sp-cost">0€ ILLIMITÉ</span>
</div>
<!-- WEB CHAT PROVIDERS - FREE & UNLIMITED -->
<div>
<div class="section-title">💬 Chat IA Gratuit · Illimité · 0€</div>
<div class="providers-grid">
<div class="prov-card active" data-model="deepseek-web" onclick="selectProvider(this)">
<div class="prov-icon">🔥</div>
<div class="prov-name">DeepSeek Chat</div>
<div class="prov-type">deepseek-v3 · web</div>
<span class="prov-badge free">0€</span>
</div>
<div class="prov-card" data-model="web-copilot" onclick="selectProvider(this)">
<div class="prov-icon">🤖</div>
<div class="prov-name">Copilot GPT-4o</div>
<div class="prov-type">microsoft · web</div>
<span class="prov-badge free">0€</span>
</div>
<div class="prov-card" data-model="web-meta" onclick="selectProvider(this)">
<div class="prov-icon">Ⓜ️</div>
<div class="prov-name">Meta AI Llama</div>
<div class="prov-type">meta · web</div>
<span class="prov-badge free">0€</span>
</div>
<div class="prov-card" data-model="web-qwen" onclick="selectProvider(this)">
<div class="prov-icon">💜</div>
<div class="prov-name">Qwen Chat</div>
<div class="prov-type">alibaba · web</div>
<span class="prov-badge free">0€</span>
</div>
<div class="prov-card" data-model="web-perplexity" onclick="selectProvider(this)">
<div class="prov-icon">🔍</div>
<div class="prov-name">Perplexity</div>
<div class="prov-type">search+ai · web</div>
<span class="prov-badge free">0€</span>
</div>
<div class="prov-card" data-model="web-duckduckgo" onclick="selectProvider(this)">
<div class="prov-icon">🦆</div>
<div class="prov-name">DuckDuckGo AI</div>
<div class="prov-type">privacy-first · web</div>
<span class="prov-badge free">0€</span>
</div>
<div class="prov-card" data-model="web-lechat" onclick="selectProvider(this)">
<div class="prov-icon">🇫🇷</div>
<div class="prov-name">Le Chat Mistral</div>
<div class="prov-type">mistral.ai · web</div>
<span class="prov-badge free">0€</span>
</div>
<div class="prov-card" data-model="web-huggingchat" onclick="selectProvider(this)">
<div class="prov-icon">🤗</div>
<div class="prov-name">HuggingChat</div>
<div class="prov-type">huggingface · web</div>
<span class="prov-badge free">0€</span>
</div>
</div>
</div>
<!-- CONSENSUS + MULTI-AGENT -->
<div>
<div class="section-title">⚖️ Consensus & Multi-Agent</div>
<div class="consensus-row">
<div class="consensus-card" data-model="consensus" onclick="selectProvider(this)">
<div class="cc-title">⚖️ Consensus MoA</div>
<div class="cc-desc">4 providers votent · meilleure réponse sélectionnée</div>
</div>
<div class="consensus-card" data-model="multiagent-all" onclick="selectProvider(this)">
<div class="cc-title">🤖 Multi-Agent ALL</div>
<div class="cc-desc">Tous les providers actifs en parallèle</div>
</div>
</div>
</div>
<!-- API PROVIDERS -->
<div>
<div class="section-title">⚡ API Sovereign · 0€ Cascade</div>
<div class="providers-grid">
<div class="prov-card" data-model="cerebras-fast" onclick="selectProvider(this)">
<div class="prov-icon"></div>
<div class="prov-name">Cerebras Fast</div>
<div class="prov-type">llama-4-scout · api</div>
<span class="prov-badge api">API</span>
</div>
<div class="prov-card" data-model="groq-llama" onclick="selectProvider(this)">
<div class="prov-icon">🟢</div>
<div class="prov-name">Groq</div>
<div class="prov-type">llama-3.1 · fastest</div>
<span class="prov-badge api">API</span>
</div>
<div class="prov-card" data-model="nim-deepseek" onclick="selectProvider(this)">
<div class="prov-icon">🟩</div>
<div class="prov-name">NVIDIA NIM</div>
<div class="prov-type">deepseek-r1 · api</div>
<span class="prov-badge api">API</span>
</div>
<div class="prov-card" data-model="mistral" onclick="selectProvider(this)">
<div class="prov-icon">🔵</div>
<div class="prov-name">Mistral</div>
<div class="prov-type">mistral-small · api</div>
<span class="prov-badge api">API</span>
</div>
</div>
</div>
<!-- MODES -->
<div>
<div class="section-title">🎯 Modes</div>
<div class="modes-bar">
<button class="mode-btn active" data-mode="think-search" onclick="selectMode(this)">⚡ Think+Search</button>
<button class="mode-btn" data-mode="deepthink" onclick="selectMode(this)">🧠 DeepThink</button>
<button class="mode-btn" data-mode="search" onclick="selectMode(this)">🔍 Search</button>
<button class="mode-btn" data-mode="expert" onclick="selectMode(this)">🎓 Expert</button>
<button class="mode-btn" data-mode="code" onclick="selectMode(this)">💻 Code</button>
<button class="mode-btn" data-mode="creative" onclick="selectMode(this)">✨ Creative</button>
</div>
</div>
<!-- CHAT AREA -->
<div class="chat-area" id="chatArea"></div>
<!-- INPUT -->
<div class="input-bar">
<textarea id="msgInput" placeholder="Message WEVAL Arena... (Enter pour envoyer)" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendMsg()}"></textarea>
<button class="send-btn" onclick="sendMsg()">
<svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
</button>
</div>
<!-- ADVANCED -->
<details style="margin-top:8px">
<summary class="advanced-toggle">📂 405 options avancées (Hubs, Skills, Arsenal, OSS, GPU...)</summary>
<select class="advanced-select open" id="advancedSelect" onchange="selectAdvanced(this)" size="8">
<optgroup label="🔥 WEBCHAT 0€ ILLIMITÉ">
<option value="deepseek-web">🔥 DeepSeek Chat</option>
<option value="deepseek-web-think">🧠 DS DeepThink</option>
<option value="deepseek-web-search">🔍 DS Search</option>
</optgroup>
<optgroup label="⚡ API SOVEREIGN 0€">
<option value="auto">🏆 Auto (meilleur disponible)</option>
<option value="cerebras-fast">⚡ Cerebras Fast</option>
<option value="groq-llama">🟢 Groq 70B</option>
<option value="nim-deepseek">🟩 NVIDIA NIM DeepSeek</option>
</optgroup>
<optgroup label="⚖️ CONSENSUS">
<option value="consensus">⚖️ Consensus MoA · 4 providers</option>
<option value="multiagent-all">🤖 ALL Agents</option>
<option value="multiagent-5">🤖 5 Agents</option>
</optgroup>
</select>
</details>
</div>
<!-- SIDEBAR -->
<div class="sidebar">
<div class="section-title">📊 System Live</div>
<div class="stat-grid">
<div class="stat-box"><div class="val green">153/153</div><div class="label">NonReg</div></div>
<div class="stat-box"><div class="val cyan">957/957</div><div class="label">State</div></div>
<div class="stat-box"><div class="val purple">310</div><div class="label">Intents</div></div>
<div class="stat-box"><div class="val amber">17</div><div class="label">Docker</div></div>
</div>
<div class="section-title">🔗 Cascade Providers</div>
<div class="cascade-list" id="cascadeList">
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">Groq</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">Cerebras</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">SambaNova</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">Gemini</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">Mistral</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">DeepSeek</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">OpenRouter</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">Anthropic</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">Ollama</span></div>
<div class="cascade-item active"><span class="c-dot"></span><span class="c-name">DeepSeek Web</span></div>
</div>
<div class="section-title">⚡ Quick Actions</div>
<div style="display:flex;flex-direction:column;gap:4px">
<button class="mode-btn" style="width:100%;text-align:left" onclick="sendQuick('system status')">💚 System status</button>
<button class="mode-btn" style="width:100%;text-align:left" onclick="sendQuick('nonreg tests')">✅ NonReg tests</button>
<button class="mode-btn" style="width:100%;text-align:left" onclick="sendQuick('git push')">📦 Git push</button>
<button class="mode-btn" style="width:100%;text-align:left" onclick="sendQuick('cascade status')">🔗 Cascade status</button>
<button class="mode-btn" style="width:100%;text-align:left" onclick="sendQuick('reconcilie tout')">🔄 Réconciliation</button>
<button class="mode-btn" style="width:100%;text-align:left" onclick="sendQuick('teste tous les providers')">🧪 Test providers</button>
</div>
</div>
</div>
<script>
let currentModel = 'deepseek-web';
let currentMode = 'think-search';
let history = [];
function selectProvider(el) {
document.querySelectorAll('.prov-card,.consensus-card').forEach(c => c.classList.remove('active'));
el.classList.add('active');
currentModel = el.dataset.model;
const name = el.querySelector('.prov-name,.cc-title')?.textContent || currentModel;
const icon = el.querySelector('.prov-icon')?.textContent || '⚡';
document.getElementById('selectedProvider').innerHTML = `
<span class="sp-icon">${icon}</span>
<span class="sp-name">${name}</span>
<span class="sp-cost">0€ ILLIMITÉ</span>`;
document.getElementById('msgInput').focus();
}
function selectMode(el) {
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
el.classList.add('active');
currentMode = el.dataset.mode;
}
function selectAdvanced(sel) {
currentModel = sel.value;
document.getElementById('selectedProvider').innerHTML = `
<span class="sp-icon">⚙️</span>
<span class="sp-name">${sel.options[sel.selectedIndex].text}</span>
<span class="sp-cost">0€</span>`;
}
function addMsg(text, type, provider) {
const area = document.getElementById('chatArea');
const div = document.createElement('div');
div.className = `msg ${type}`;
div.innerHTML = text.replace(/\n/g,'<br>');
if (provider) div.innerHTML += `<div class="provider-tag">${provider}</div>`;
area.appendChild(div);
area.scrollTop = area.scrollHeight;
}
async function sendMsg() {
const input = document.getElementById('msgInput');
const msg = input.value.trim();
if (!msg) return;
input.value = '';
addMsg(msg, 'user');
history.push({role:'user',content:msg});
// Determine endpoint
const webModels = ['deepseek-web','deepseek-web-think','deepseek-web-search','web-copilot','web-meta','web-qwen','web-perplexity','web-duckduckgo','web-lechat','web-huggingchat'];
const ep = webModels.includes(currentModel) ? '/api/wevia-webchat-direct.php' : '/api/wevia-multi-provider.php';
try {
const res = await fetch(ep, {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify(webModels.includes(currentModel) ? {service:currentModel.replace('web-',''), message:msg} : {message:msg, mode:currentMode, model:currentModel, history:history.slice(-12)})
});
/* HTML_GUARD_V2_BATCH */ const _t_d=await res.text(); const d=null; {var _q=(_t_d||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d={error:"[HTTP "+(res.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:"[JSON] "+e.message}}}}
const content = d.content || d.response || 'Pas de réponse';
const prov = d.provider || currentModel;
addMsg(content, 'ai', `${prov} · ${d.cost || '0€'}`);
history.push({role:'assistant',content});
} catch(e) {
addMsg('Erreur de connexion', 'ai', 'error');
}
}
function sendQuick(text) {
document.getElementById('msgInput').value = text;
sendMsg();
}
</script>
<!-- === 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/a11y-auto-enhancer.js" defer></script>
<!-- WTP_UDOCK_V1 (Opus 21-avr t33b5) --><script src="/wtp-unified-dock.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
<!-- Opus v17 · Claude Pattern SSE (auto-injected) -->
<style id="opus-pattern-style">
#opus-pattern-badge{position:fixed;bottom:20px;right:20px;z-index:99990;
background:linear-gradient(135deg,#06b6d4,#8b5cf6);color:#fff;
padding:10px 16px;border-radius:20px;font:700 0.78rem -apple-system,sans-serif;
cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,0.35);transition:all 0.2s;
display:flex;align-items:center;gap:6px}
#opus-pattern-badge:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(6,182,212,0.4)}
#opus-pattern-modal{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.8);
z-index:99991;align-items:center;justify-content:center;padding:20px}
#opus-pattern-modal.show{display:flex}
#opus-pattern-box{background:#0b0d15;color:#e2e8f0;border:1px solid rgba(6,182,212,0.3);
border-radius:14px;padding:22px;max-width:820px;width:100%;max-height:85vh;overflow:auto;
font:-apple-system,sans-serif}
#opus-pattern-box h3{font:800 1.2rem;margin-bottom:12px;
background:linear-gradient(135deg,#06b6d4,#ec4899);
-webkit-background-clip:text;-webkit-text-fill-color:transparent}
#opus-pattern-input{width:100%;background:#1a1f3a;color:#fff;border:1px solid rgba(100,116,139,0.3);
border-radius:8px;padding:10px;margin-bottom:10px;font:0.9rem -apple-system}
#opus-pattern-run{background:linear-gradient(135deg,#10b981,#06b6d4);color:#fff;border:0;
padding:10px 20px;border-radius:8px;font:700 0.85rem;cursor:pointer;margin-bottom:14px}
.phase-card{background:rgba(15,23,42,0.8);border:1px solid rgba(100,116,139,0.2);
border-left:3px solid #06b6d4;border-radius:8px;padding:10px 14px;margin-bottom:8px;
font-size:0.82rem}
.phase-card.done{border-left-color:#22c55e}
.phase-card.active{border-left-color:#f59e0b;animation:pulse 1.2s ease infinite}
.phase-name{font-weight:800;color:#06b6d4;margin-bottom:4px;font-size:0.78rem;text-transform:uppercase;letter-spacing:1px}
.phase-data{font-size:0.72rem;color:#94a3b8;font-family:ui-monospace,monospace}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.6}}
#opus-pattern-close{position:absolute;top:14px;right:20px;background:0;border:0;color:#94a3b8;
font-size:1.6rem;cursor:pointer}
</style>
<div id="opus-pattern-badge" onclick="window.__opusPatternOpen()">
<span>🧠</span><span>Claude Pattern</span>
</div>
<div id="opus-pattern-modal" onclick="if(event.target.id==='opus-pattern-modal')window.__opusPatternClose()">
<div id="opus-pattern-box">
<button id="opus-pattern-close" onclick="window.__opusPatternClose()">×</button>
<h3>🧠 Claude Pattern · 7 phases REAL (SSE live)</h3>
<p style="font-size:0.82rem;color:#94a3b8;margin-bottom:12px">Backend: <b id="opus-pattern-bot">weval-arena-v2</b> · anti-hallucination · langue naturelle</p>
<input id="opus-pattern-input" placeholder="Posez une question (FR ou EN)..." value="bonjour quel est le statut" />
<button id="opus-pattern-run" onclick="window.__opusPatternRun()">▶ Lancer (SSE stream)</button>
<div id="opus-pattern-output"></div>
</div>
</div>
<script>
(function(){
const BOT = 'weval-arena-v2';
window.__opusPatternOpen = () => document.getElementById('opus-pattern-modal').classList.add('show');
window.__opusPatternClose = () => document.getElementById('opus-pattern-modal').classList.remove('show');
window.__opusPatternRun = () => {
const msg = document.getElementById('opus-pattern-input').value.trim();
if (!msg) return;
const out = document.getElementById('opus-pattern-output');
out.innerHTML = '';
const OPUS_SESSION_KEY = 'opus_chatbot_session_' + BOT;
let sess = localStorage.getItem(OPUS_SESSION_KEY);
if (!sess) {
sess = 'opus-' + BOT + '-' + Date.now().toString(36) + '-' + Math.random().toString(36).substr(2, 6);
localStorage.setItem(OPUS_SESSION_KEY, sess);
}
// CF_BYPASS_V23 · direct 127.0.0.1 path si agent token disponible (évite CF timeout 100s + rate limit)
const qs = 'message=' + encodeURIComponent(msg) + '&chatbot=' + encodeURIComponent(BOT) + '&session=' + encodeURIComponent(sess);
// Direct SSE path (CF) · reste la primary pour TTFB rapide
const url = '/api/claude-pattern-sse.php?' + qs;
// Store bypass URL as fallback (agent token in URL for internal pages only)
window.__opusBypassUrl = '/api/cf-bypass-helper.php?target=' + encodeURIComponent('/api/claude-pattern-sse.php?' + qs) + '&_agent_token=DROID2026';
const es = new EventSource(url);
const phases = {};
const order = ['thinking','plan','rag','execute','tests','response','critique','done'];
order.forEach(p => {
const card = document.createElement('div');
card.className = 'phase-card';
card.id = 'phase-' + p;
card.innerHTML = '<div class="phase-name">' + p.toUpperCase() + '</div><div class="phase-data">⏳ waiting...</div>';
out.appendChild(card);
});
order.forEach(evName => {
es.addEventListener(evName, (e) => {
const data = JSON.parse(e.data);
const card = document.getElementById('phase-' + evName);
if (card) {
card.classList.add('done');
card.classList.remove('active');
let txt;
if (evName === 'response' && data.text) {
txt = '<div style="background:rgba(6,182,212,0.1);padding:10px;border-radius:6px;margin-top:6px;color:#e2e8f0;font-size:0.82rem">' + (data.text.substring(0, 600)) + (data.text.length > 600 ? '...' : '') + '</div>';
} else if (evName === 'tests') {
txt = '<div>' + data.passed + '/' + data.total + ' tests ✓</div>';
} else if (evName === 'critique') {
txt = '<div>Quality: <b style="color:' + (data.quality === 'EXCELLENT' ? '#22c55e' : (data.quality === 'OK' ? '#f59e0b' : '#ef4444')) + '">' + data.quality + '</b> (' + (data.quality_score * 5).toFixed(0) + '/5)</div>';
} else {
txt = JSON.stringify(data).substring(0, 300);
}
card.querySelector('.phase-data').innerHTML = txt;
}
if (evName === 'done') es.close();
});
});
es.addEventListener('error', () => es.close());
};
})();
</script>
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-direct">
// DOCTRINE-60-UX-JS staggered entrance
(function(){
if (!('IntersectionObserver' in window)) return;
const obs = new IntersectionObserver((entries) => {
entries.forEach((e, i) => {
if (e.isIntersecting) {
setTimeout(() => e.target.classList.add('enter-stagger'), i * 80);
obs.unobserve(e.target);
}
});
});
document.querySelectorAll('.card, .kpi, .panel').forEach(el => obs.observe(el));
})();
</script>
</body>
</html>