434 lines
25 KiB
HTML
434 lines
25 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>WEVAL — Wiring & Comparison Dashboard</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Outfit:wght@300;500;700;900&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{--bg:#0a0e17;--bg2:#111827;--bg3:#1e293b;--bd:#1e293b;--fg:#e2e8f0;--fg2:#94a3b8;--fg3:#64748b;--go:#f59e0b;--gn:#22c55e;--rd:#ef4444;--bl:#3b82f6;--vi:#8b5cf6;--cy:#06b6d4;--pk:#ec4899}
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
body{background:var(--bg);color:var(--fg);font-family:'Outfit',sans-serif;min-height:100vh}
|
||
.top{background:linear-gradient(135deg,#0f172a 0%,#1a1040 50%,#0f2027 100%);padding:24px 32px;border-bottom:1px solid var(--bd)}
|
||
.top h1{font-size:28px;font-weight:900;background:linear-gradient(90deg,var(--go),var(--pk),var(--vi));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
||
.top p{color:var(--fg2);font-size:13px;margin-top:4px}
|
||
.tabs{display:flex;gap:2px;padding:8px 32px;background:var(--bg2);border-bottom:1px solid var(--bd)}
|
||
.tab{padding:8px 20px;cursor:pointer;border-radius:6px 6px 0 0;font-size:12px;font-weight:700;color:var(--fg3);transition:.2s}
|
||
.tab:hover{color:var(--fg);background:var(--bg3)}
|
||
.tab.on{color:var(--go);background:var(--bg);border:1px solid var(--bd);border-bottom:1px solid var(--bg)}
|
||
.main{padding:24px 32px}
|
||
.panel{display:none}
|
||
.panel.on{display:block}
|
||
.kpis{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:24px}
|
||
.kpi{background:var(--bg2);border:1px solid var(--bd);border-radius:8px;padding:14px;text-align:center}
|
||
.kpi-v{font-size:28px;font-weight:900;font-family:'JetBrains Mono'}
|
||
.kpi-l{font-size:10px;color:var(--fg3);margin-top:2px}
|
||
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;margin-top:16px}
|
||
.card{background:var(--bg2);border:1px solid var(--bd);border-radius:8px;padding:14px;transition:.2s}
|
||
.card:hover{border-color:var(--vi);transform:translateY(-2px)}
|
||
.card-h{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
|
||
.card-n{font-weight:700;font-size:14px}
|
||
.card-n a{color:inherit;text-decoration:none}
|
||
.card-n a:hover{color:var(--go)}
|
||
.tag{font-size:8px;padding:2px 6px;border-radius:4px;font-weight:700;text-transform:uppercase}
|
||
.tag-gn{background:rgba(34,197,94,.2);color:var(--gn)}
|
||
.tag-rd{background:rgba(239,68,68,.2);color:var(--rd)}
|
||
.tag-bl{background:rgba(59,130,246,.2);color:var(--bl)}
|
||
.tag-vi{background:rgba(139,92,246,.2);color:var(--vi)}
|
||
.tag-go{background:rgba(245,158,11,.2);color:var(--go)}
|
||
.card-row{display:flex;justify-content:space-between;font-size:11px;padding:3px 0;border-bottom:1px solid rgba(255,255,255,.03)}
|
||
.card-row:last-child{border:0}
|
||
.card-k{color:var(--fg3)}
|
||
.card-v{font-family:'JetBrains Mono';font-size:10px}
|
||
.bar{height:4px;background:var(--bg3);border-radius:2px;margin-top:6px;overflow:hidden}
|
||
.bar-f{height:100%;border-radius:2px;transition:width 1s ease}
|
||
table{width:100%;border-collapse:collapse;font-size:11px}
|
||
th{text-align:left;padding:8px;color:var(--fg3);border-bottom:1px solid var(--bd);font-size:9px;text-transform:uppercase;position:sticky;top:0;background:var(--bg)}
|
||
td{padding:6px 8px;border-bottom:1px solid rgba(255,255,255,.03)}
|
||
tr:hover{background:var(--bg3)}
|
||
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:4px}
|
||
.dot-gn{background:var(--gn)}.dot-rd{background:var(--rd)}.dot-go{background:var(--go)}
|
||
.sc{font-family:'JetBrains Mono';font-weight:700}
|
||
.opus-bar{display:flex;align-items:center;gap:8px;margin:16px 0;padding:12px;background:var(--bg2);border:1px solid var(--bd);border-radius:8px}
|
||
.opus-bar .bar{flex:1;height:8px}
|
||
.loading{text-align:center;padding:40px;color:var(--fg3)}
|
||
@media(max-width:768px){.kpis{grid-template-columns:repeat(3,1fr)}.grid{grid-template-columns:1fr}}
|
||
</style>
|
||
<!-- DOCTRINE-60-UX-ENRICH direct-inject-20260424-143919 -->
|
||
<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="top">
|
||
<h1>⚡ WEVAL Wiring & Comparison</h1>
|
||
<p>Dashboard dynamique — Benchmark IA × OSS Discovery × Architecture · Données temps réel</p>
|
||
</div>
|
||
<div class="tabs">
|
||
<div class="tab on" onclick="sw(0)">🔌 Wiring Map</div>
|
||
<div class="tab" onclick="sw(1)">⚔️ WEVAL vs Opus</div>
|
||
<div class="tab" onclick="sw(2)">🧩 OSS Skills</div>
|
||
<div class="tab" onclick="sw(3)">📊 Full Comparison</div>
|
||
</div>
|
||
<div class="main">
|
||
|
||
<!-- TAB 0: WIRING MAP -->
|
||
<div class="panel on" id="p0"><div class="loading">Chargement...</div></div>
|
||
|
||
<!-- TAB 1: WEVAL vs OPUS -->
|
||
<div class="panel" id="p1"><div class="loading">Chargement...</div></div>
|
||
|
||
<!-- TAB 2: OSS SKILLS -->
|
||
<div class="panel" id="p2"><div class="loading">Chargement...</div></div>
|
||
|
||
<!-- TAB 3: FULL COMPARISON TABLE -->
|
||
<div class="panel" id="p3"><div class="loading">Chargement...</div></div>
|
||
|
||
</div>
|
||
<script>
|
||
const $ = id => document.getElementById(id);
|
||
function sw(n){document.querySelectorAll('.tab').forEach((t,i)=>{t.classList.toggle('on',i===n)});document.querySelectorAll('.panel').forEach((p,i)=>{p.classList.toggle('on',i===n)})}
|
||
|
||
let BENCH={},OSS={},SOT={};
|
||
|
||
async function load(){
|
||
try{
|
||
const [b,o,s]=await Promise.all([
|
||
fetch('/api/ai-benchmark-cache.json?t='+Date.now()).then(r=>r.text().then(t=>{/* HTML_GUARD_V2_BATCH */var q=(t||"").trim();if(q.startsWith("<!DOCTYPE")||q.startsWith("<html")){return{error:"[HTTP "+r.status+"]",isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:"JSON "+e.message}}})),
|
||
fetch('/api/oss-cache.json?t='+Date.now()).then(r=>r.text().then(t=>{/* HTML_GUARD_V2_BATCH */var q=(t||"").trim();if(q.startsWith("<!DOCTYPE")||q.startsWith("<html")){return{error:"[HTTP "+r.status+"]",isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:"JSON "+e.message}}})).catch(()=>({})),
|
||
fetch('/api/source-of-truth.json?t='+Date.now()).then(r=>r.text().then(t=>{/* HTML_GUARD_V2_BATCH */var q=(t||"").trim();if(q.startsWith("<!DOCTYPE")||q.startsWith("<html")){return{error:"[HTTP "+r.status+"]",isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:"JSON "+e.message}}})).catch(()=>({}))
|
||
]);
|
||
BENCH=b;OSS=o;SOT=s;
|
||
renderWiring();renderVsOpus();renderOSS();renderComparison();
|
||
}catch(e){$('p0').innerHTML='<p style="color:var(--rd)">Erreur: '+e+'</p>'}
|
||
}
|
||
|
||
function renderWiring(){
|
||
const A=BENCH.all_ais||{};
|
||
const wired=Object.entries(A).filter(([n,a])=>a.wired);
|
||
const notWired=Object.entries(A).filter(([n,a])=>!a.wired);
|
||
const agents=SOT.agents||{};
|
||
let h=`<div class="kpis">
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--gn)">${wired.length}</div><div class="kpi-l">WIRED</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--rd)">${notWired.length}</div><div class="kpi-l">NOT WIRED</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--vi)">${Object.keys(A).length}</div><div class="kpi-l">TOTAL AIs</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--go)">${BENCH.report?.combined_avg||'?'}/90</div><div class="kpi-l">COMBINED</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--cy)">0€</div><div class="kpi-l">COÛT TOTAL</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--gn)">${Math.round(wired.length/Object.keys(A).length*100)}%</div><div class="kpi-l">COVERAGE</div></div>
|
||
</div>`;
|
||
h+=`<h3 style="color:var(--gn);margin-bottom:12px">✅ ${wired.length} IAs Wirées dans WEVAL</h3><div class="grid">`;
|
||
wired.sort((a,b)=>(b[1].avg||0)-(a[1].avg||0));
|
||
for(const [n,a] of wired){
|
||
const col=a.type==='sovereign'?'var(--vi)':a.type==='agent'?'var(--gn)':a.type==='security'?'var(--rd)':a.type==='chatbot'?'var(--bl)':a.type==='search'?'var(--cy)':a.type==='testing'?'var(--pk)':a.type==='composite'?'var(--go)':'var(--fg3)';
|
||
h+=`<div class="card">
|
||
<div class="card-h"><div class="card-n"><a href="${a.url||'#'}" target="_blank">${a.icon||''} ${n}</a></div><span class="tag tag-gn">✅ WIRED</span></div>
|
||
<div class="card-row"><span class="card-k">Type</span><span class="tag" style="background:${col}22;color:${col}">${a.type}</span></div>
|
||
<div class="card-row"><span class="card-k">Score</span><span class="card-v sc" style="color:${col}">${a.avg}/90</span></div>
|
||
<div class="card-row"><span class="card-k">Rôle</span><span class="card-v">${(a.used_in||'').slice(0,40)}</span></div>
|
||
<div class="card-row"><span class="card-k">Cas d'usage</span><span class="card-v">${(a.usecase||'').slice(0,40)}</span></div>
|
||
<div class="card-row"><span class="card-k">Coût</span><span class="card-v">${a.cost||'?'}</span></div>
|
||
<div class="bar"><div class="bar-f" style="width:${Math.round(a.avg/90*100)}%;background:${col}"></div></div>
|
||
</div>`;
|
||
}
|
||
h+=`</div>`;
|
||
if(notWired.length){
|
||
h+=`<h3 style="color:var(--rd);margin:24px 0 12px">❌ ${notWired.length} Non Wirées (Références)</h3><div class="grid">`;
|
||
for(const [n,a] of notWired){
|
||
h+=`<div class="card" style="opacity:.6">
|
||
<div class="card-h"><div class="card-n"><a href="${a.url||'#'}" target="_blank">${a.icon||''} ${n}</a></div><span class="tag tag-rd">❌</span></div>
|
||
<div class="card-row"><span class="card-k">Score</span><span class="card-v sc">${a.avg}/90</span></div>
|
||
<div class="card-row"><span class="card-k">Coût</span><span class="card-v">${a.cost||'💰'}</span></div>
|
||
</div>`;
|
||
}
|
||
h+=`</div>`;
|
||
}
|
||
$('p0').innerHTML=h;
|
||
}
|
||
|
||
function renderVsOpus(){
|
||
const A=BENCH.all_ais||{};
|
||
const weval=A.WEVAL_COMBINED||{avg:93};
|
||
const opus=A.Claude_Opus_4_6||A['Claude_Opus_4.6']||{avg:90};
|
||
const sov=Object.entries(A).filter(([n,a])=>a.wired&&a.type==='sovereign');
|
||
const agents=Object.entries(A).filter(([n,a])=>a.wired&&a.type==='agent');
|
||
const pct=Math.round(weval.avg/opus.avg*100);
|
||
|
||
let h=`<div class="opus-bar">
|
||
<span style="font-size:24px">🌟</span>
|
||
<div><div style="font-weight:900;font-size:18px">WEVAL Combined: <span style="color:var(--gn)">${weval.avg}</span> vs Opus: <span style="color:var(--go)">${opus.avg}</span></div>
|
||
<div style="font-size:11px;color:var(--fg2)">${pct}% d'Opus · ${pct>100?'SUPÉRIEUR':'Inférieur'} à Claude Opus 4.6</div></div>
|
||
<div class="bar"><div class="bar-f" style="width:${Math.min(pct,100)}%;background:${pct>=100?'var(--gn)':'var(--go)'}"></div></div>
|
||
</div>`;
|
||
|
||
h+=`<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:16px">`;
|
||
// Avantages WEVAL
|
||
h+=`<div class="card"><div class="card-h"><span class="card-n" style="color:var(--gn)">✅ Avantages WEVAL</span></div>
|
||
<div class="card-row"><span class="card-k">Coût</span><span class="card-v" style="color:var(--gn)">0€/mois</span></div>
|
||
<div class="card-row"><span class="card-k">Providers</span><span class="card-v">${sov.length} sovereign cascade</span></div>
|
||
<div class="card-row"><span class="card-k">Agents</span><span class="card-v">${agents.length} autonomes</span></div>
|
||
<div class="card-row"><span class="card-k">Failover</span><span class="card-v">Auto cascade 7 niveaux</span></div>
|
||
<div class="card-row"><span class="card-k">Souveraineté</span><span class="card-v">100% (Ollama local)</span></div>
|
||
<div class="card-row"><span class="card-k">RGPD</span><span class="card-v">EU data residency (Mistral)</span></div>
|
||
<div class="card-row"><span class="card-k">Consensus</span><span class="card-v">MoA 4 providers · 9.8/10</span></div>
|
||
</div>`;
|
||
// Avantages Opus
|
||
h+=`<div class="card"><div class="card-h"><span class="card-n" style="color:var(--go)">🏆 Avantages Opus</span></div>
|
||
<div class="card-row"><span class="card-k">Raisonnement</span><span class="card-v">Meilleur single-model</span></div>
|
||
<div class="card-row"><span class="card-k">Code</span><span class="card-v">Analyse complexe supérieure</span></div>
|
||
<div class="card-row"><span class="card-k">Coût</span><span class="card-v" style="color:var(--rd)">~$200/mois API</span></div>
|
||
<div class="card-row"><span class="card-k">Dépendance</span><span class="card-v" style="color:var(--rd)">Cloud US · Anthropic</span></div>
|
||
<div class="card-row"><span class="card-k">Rate limit</span><span class="card-v" style="color:var(--rd)">Oui</span></div>
|
||
<div class="card-row"><span class="card-k">Failover</span><span class="card-v" style="color:var(--rd)">Aucun</span></div>
|
||
<div class="card-row"><span class="card-k">Souveraineté</span><span class="card-v" style="color:var(--rd)">0%</span></div>
|
||
</div></div>`;
|
||
|
||
// Économies
|
||
h+=`<div class="kpis" style="margin-top:16px">
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--gn)">$0</div><div class="kpi-l">COÛT MENSUEL</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--rd)">$200</div><div class="kpi-l">OPUS API/MOIS</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--gn)">$2,400</div><div class="kpi-l">ÉCONOMIE/AN</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--go)">${pct}%</div><div class="kpi-l">% D'OPUS</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--vi)">7</div><div class="kpi-l">CASCADE LEVELS</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--gn)">100%</div><div class="kpi-l">SOUVERAIN</div></div>
|
||
</div>`;
|
||
$('p1').innerHTML=h;
|
||
}
|
||
|
||
function renderOSS(){
|
||
const R=OSS.report||{};const S=OSS.skills||{};
|
||
const tools=R.total||70;const skills=S.total||1935;
|
||
let h=`<div class="kpis">
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--bl)">${tools}</div><div class="kpi-l">TOOLS WIRED</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--vi)">${skills}</div><div class="kpi-l">SKILLS</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--gn)">${R.wire_pct||100}%</div><div class="kpi-l">WIRE RATE</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--cy)">${R.not_wired||0}</div><div class="kpi-l">NOT WIRED</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--go)">${R.categories||12}</div><div class="kpi-l">CATEGORIES</div></div>
|
||
<div class="kpi"><div class="kpi-v" style="color:var(--gn)">0€</div><div class="kpi-l">COÛT</div></div>
|
||
</div>`;
|
||
h+=`<p style="color:var(--fg2);font-size:12px;margin-bottom:12px">Données depuis <a href="/oss-discovery.html" target="_blank" style="color:var(--bl)">/oss-discovery.html</a></p>`;
|
||
const items=R.by_status||OSS.tools||[];
|
||
if(Array.isArray(items)&&items.length){
|
||
h+=`<div class="grid">`;
|
||
for(const t of items.slice(0,20)){
|
||
h+=`<div class="card"><div class="card-h"><div class="card-n">${t.name||t}</div><span class="tag tag-gn">wired</span></div></div>`;
|
||
}
|
||
h+=`</div>`;
|
||
}
|
||
$('p2').innerHTML=h;
|
||
}
|
||
|
||
function renderComparison(){
|
||
const A=BENCH.all_ais||{};
|
||
const lb=(BENCH.leaderboard||[]).sort((a,b)=>b.score-a.score);
|
||
let h=`<div style="overflow-x:auto"><table>
|
||
<tr><th>#</th><th>AI</th><th>Type</th><th>Score</th><th>%Opus</th><th>Wired</th><th>Coût</th><th>Utilisé dans</th><th>Cas d'usage</th><th>Lien</th></tr>`;
|
||
lb.forEach((x,i)=>{
|
||
const a=A[x.name]||{};
|
||
const pct=Math.round(x.score/90*100);
|
||
const wc=a.wired?'var(--gn)':'var(--rd)';
|
||
h+=`<tr>
|
||
<td>${i+1}</td>
|
||
<td style="font-weight:700">${a.icon||''} ${x.name}</td>
|
||
<td><span class="tag" style="background:rgba(255,255,255,.05)">${a.type||'?'}</span></td>
|
||
<td class="sc" style="color:${x.score>=85?'var(--gn)':x.score>=75?'var(--go)':'var(--fg3)'}">${x.score}</td>
|
||
<td>${pct}%</td>
|
||
<td style="color:${wc}">${a.wired?'✅':'❌'}</td>
|
||
<td style="font-size:9px">${a.cost||'?'}</td>
|
||
<td style="font-size:9px;max-width:150px;overflow:hidden;text-overflow:ellipsis">${a.used_in||'-'}</td>
|
||
<td style="font-size:9px;max-width:150px;overflow:hidden;text-overflow:ellipsis">${a.usecase||'-'}</td>
|
||
<td><a href="${a.url||'#'}" target="_blank" style="color:var(--bl);font-size:10px">Ouvrir →</a></td>
|
||
</tr>`;
|
||
});
|
||
h+=`</table></div>`;
|
||
$('p3').innerHTML=h;
|
||
}
|
||
|
||
load();
|
||
setInterval(load,60000);
|
||
</script>
|
||
<!-- WTP-GAP-FILL-V1 (doctrine 90-v2 gap-fill showcase, 18avr 2026) -->
|
||
<style>
|
||
.wtp-gapfill-banner{position:fixed;bottom:0;left:0;right:0;z-index:99999;background:linear-gradient(90deg,#05060a,#0b0d15 20%,#181d2e 50%,#0b0d15 80%,#05060a);border-top:2px solid #14b8a6;color:#e2e8f0;padding:10px 16px;font-family:Inter,system-ui,-apple-system,sans-serif;font-size:11.5px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;box-shadow:0 -10px 30px rgba(20,184,166,.28)}
|
||
.wtp-gapfill-banner a{color:#5eead4;text-decoration:none;font-weight:600;transition:color .15s}
|
||
.wtp-gapfill-banner a:hover{color:#22d3ee}
|
||
.wtp-gapfill-banner .pill{padding:2px 9px;background:rgba(99,102,241,.14);color:#a5b4fc;border-radius:10px;font-size:10.5px;font-family:JetBrains Mono,monospace;font-weight:600}
|
||
.wtp-gapfill-banner .pill.new{background:rgba(20,184,166,.22);color:#5eead4}
|
||
.wtp-gapfill-banner .pill.hot{background:rgba(236,72,153,.22);color:#f472b6}
|
||
.wtp-gapfill-banner .close{margin-left:auto;cursor:pointer;color:#64748b;padding:0 8px;font-size:16px;line-height:1;border:1px solid #334155;border-radius:4px}
|
||
.wtp-gapfill-banner .close:hover{color:#e2e8f0;border-color:#64748b}
|
||
.wtp-gapfill-banner.hidden{display:none}
|
||
@media(max-width:768px){.wtp-gapfill-banner{font-size:10px;padding:7px 10px;gap:8px}}
|
||
</style>
|
||
<div class="wtp-gapfill-banner" id="wtpGapFillBanner">
|
||
<span>🎯 <strong>WEVAL Agents Gap-Fill ERP</strong></span>
|
||
<span class="pill hot">47 gaps</span>
|
||
<span class="pill">SAP · Oracle · NetSuite · Dynamics</span>
|
||
<span class="pill new">🆕 Meeting Rooms</span>
|
||
<span class="pill new">🆕 Lean 6 Sigma</span>
|
||
<span id="wtp-gfb-metrics" class="pill">— chargement —</span>
|
||
<a href="/weval-technology-platform.html">→ WTP Portal (16 mod)</a>
|
||
<a href="/enterprise-model.html">Enterprise Model</a>
|
||
<a href="/api/weval-agents-gap-fill-manifest.json" target="_blank">📋 Manifest</a>
|
||
<span class="close" onclick="document.getElementById("wtpGapFillBanner").classList.add("hidden");localStorage.setItem("wtpGapFillHidden","1")">×</span>
|
||
</div>
|
||
<script>
|
||
(async()=>{
|
||
if(localStorage.getItem("wtpGapFillHidden")==="1"){document.getElementById("wtpGapFillBanner").classList.add("hidden");return;}
|
||
try{
|
||
const r=await fetch("/api/source-of-truth.json?t="+Date.now());
|
||
const d=await r.json();
|
||
const el=document.getElementById("wtp-gfb-metrics");
|
||
if(el)el.textContent=(d.ethica_total||"?")+" HCPs · "+(d.nonreg||"?")+" · "+(d.providers_count||"?")+" IA · "+(d.agents_count||"?")+" agents · "+(d.docker_running||"?")+" 🐳";
|
||
}catch(e){}
|
||
})();
|
||
</script>
|
||
|
||
<!-- WTP-D90V2-ENRICH-BANNER (doctrine 90-v2, 17avr 2026) -->
|
||
<style>
|
||
.wtp-enrich-banner{position:fixed;bottom:0;left:0;right:0;z-index:9999;background:linear-gradient(90deg,#0b0d15,#181d2e,#0b0d15);border-top:2px solid #14b8a6;color:#e2e8f0;padding:9px 18px;font-family:'JetBrains Mono',monospace,-apple-system,system-ui;font-size:11.5px;display:flex;align-items:center;gap:14px;flex-wrap:wrap;box-shadow:0 -8px 24px rgba(20,184,166,.25)}
|
||
.wtp-enrich-banner a{color:#14b8a6;text-decoration:none;font-weight:600}
|
||
.wtp-enrich-banner a:hover{color:#22d3ee}
|
||
.wtp-enrich-banner .pill{padding:2px 8px;background:rgba(99,102,241,.15);color:#a5b4fc;border-radius:10px;font-size:10.5px}
|
||
.wtp-enrich-banner .pill.new{background:rgba(20,184,166,.2);color:#5eead4}
|
||
.wtp-enrich-banner .close{margin-left:auto;cursor:pointer;color:#64748b;padding:0 6px;font-size:14px}
|
||
.wtp-enrich-banner .close:hover{color:#e2e8f0}
|
||
.wtp-enrich-banner.hidden{display:none}
|
||
@media(max-width:768px){.wtp-enrich-banner{font-size:10px;padding:6px 10px}}
|
||
</style>
|
||
<div class="wtp-enrich-banner" id="wtpEnrichBanner">
|
||
<span>🏛️ <strong>Enterprise Model 16 depts</strong></span>
|
||
<span class="pill new">🆕 Meeting Rooms</span>
|
||
<span class="pill new">🆕 Lean 6 Sigma</span>
|
||
<span id="wtp-eb-metrics" class="pill">— chargement —</span>
|
||
<a href="/weval-technology-platform.html">→ WEVAL Technology Platform (16 modules)</a>
|
||
<a href="/enterprise-model.html">Enterprise Model</a>
|
||
<a href="/wevia-master.html">WEVIA Master</a>
|
||
<span class="close" onclick="document.getElementById('wtpEnrichBanner').classList.add('hidden')">×</span>
|
||
</div>
|
||
<script>
|
||
(async()=>{try{const r=await fetch('/api/source-of-truth.json?t='+Date.now());const d=await r.json();const el=document.getElementById('wtp-eb-metrics');if(el)el.textContent=(d.ethica_total||'?')+' HCPs · '+(d.nonreg||'?')+' · '+(d.providers_count||'?')+' IA · '+(d.docker_running||'?')+' 🐳 · '+(d.subdomains_live||'?')+' subdomains';}catch(e){}})();
|
||
</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 === -->
|
||
|
||
|
||
<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>
|
||
<!-- 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>
|