Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
6 pages centrales enrichies via cascade Cerebras: - wevia-chat-v2 (+1328B) - sovereign-monitor (+1271B) - wevia-audit (+1266B) - wevia-console (+1263B) - wevia-autonomy-dashboard (+1292B) - wevia-business-visual-studio (+1300B) Handler /var/www/html/api/enrich-hub-cascade.sh: - Try Cerebras qwen-3-235b primary - Fallback Ollama llama3.2 LOCAL (zero rate limit) - GOLD backup + chattr handling + lint - Markers DOCTRINE-60-UX-ENRICH idempotent Intent wevia_enrich_hub_cascade_cerebras_ollama wired pour chat NL. Total: 18 pages UX doctrine 60 (12 avant + 6 aujourd hui). Cascade zero-rate-limit effective: Cerebras OK + Ollama llama3.2 ready. Disk 87% stable apres recovery phase 29 +19GB.
348 lines
22 KiB
HTML
348 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr"><head>
|
|
<meta charset="UTF-8">
|
|
<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 name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>🧠 WEVIA Autonomy Dashboard · NeuroRAG v2</title>
|
|
<style>
|
|
:root{--bg:#0a0e1a;--panel:#141933;--panel2:#1a2140;--border:#263161;--text:#e4e8f7;--muted:#8b95b8;
|
|
--green:#10b981;--amber:#f59e0b;--red:#ef4444;--blue:#6ba3ff;--purple:#c084fc;--accent:#00d4b4}
|
|
*{box-sizing:border-box;margin:0;padding:0}
|
|
body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:var(--bg);color:var(--text);padding:20px;min-height:100vh;line-height:1.5}
|
|
.header{display:flex;justify-content:space-between;align-items:center;padding:20px 26px;background:linear-gradient(135deg,#1e1b4b,#312e81);border-radius:14px;margin-bottom:22px;border:1px solid var(--border)}
|
|
.header h1{color:#fff;font-size:26px;display:flex;align-items:center;gap:12px}
|
|
.header .sub{color:rgba(255,255,255,.75);font-size:12px;margin-top:4px}
|
|
.autonomy-score-box{display:grid;grid-template-columns:1fr auto;gap:28px;padding:22px 28px;background:linear-gradient(135deg,#065f46,#064e3b);border-radius:14px;margin-bottom:22px;border:1px solid rgba(16,185,129,.3)}
|
|
.autonomy-score-box .label{font-size:12px;color:#6ee7b7;text-transform:uppercase;letter-spacing:1px}
|
|
.autonomy-score-box .score{font-size:66px;font-weight:900;font-family:'SF Mono',monospace;color:#fff;line-height:1}
|
|
.autonomy-score-box .score .max{font-size:20px;color:rgba(255,255,255,.5);margin-left:6px}
|
|
.autonomy-score-box .level{margin-top:6px;padding:6px 14px;background:rgba(255,255,255,.15);color:#fff;border-radius:99px;font-size:12px;font-weight:700;letter-spacing:1px;display:inline-block}
|
|
.bar-container{background:rgba(0,0,0,.3);padding:14px;border-radius:10px;min-width:280px}
|
|
.bar-row{display:grid;grid-template-columns:150px 1fr 60px;gap:10px;align-items:center;margin-bottom:8px;font-size:11px}
|
|
.bar-row:last-child{margin-bottom:0}
|
|
.bar-row .lbl{color:rgba(255,255,255,.75);text-transform:uppercase;font-weight:500}
|
|
.bar-bg{height:14px;background:rgba(255,255,255,.12);border-radius:7px;overflow:hidden}
|
|
.bar-fill{height:100%;background:linear-gradient(90deg,#10b981,#34d399);transition:width .4s}
|
|
.bar-row .val{text-align:right;color:#fff;font-family:'SF Mono',monospace;font-weight:600;font-size:11px}
|
|
.kpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:22px}
|
|
.kpi{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:16px 18px;position:relative}
|
|
.kpi::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:var(--accent);border-radius:4px 0 0 4px}
|
|
.kpi.green::before{background:var(--green)}.kpi.amber::before{background:var(--amber)}.kpi.blue::before{background:var(--blue)}.kpi.purple::before{background:var(--purple)}
|
|
.kpi .l{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.6px;font-weight:500;margin-bottom:6px}
|
|
.kpi .v{font-size:28px;font-weight:800;color:var(--text);font-family:'SF Mono',Monaco,monospace;line-height:1.1}
|
|
.kpi .v.good{color:#6ee7b7}
|
|
.kpi .s{font-size:11px;color:var(--muted);margin-top:4px}
|
|
.section{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:22px;margin-bottom:20px}
|
|
.section h2{color:var(--purple);font-size:16px;font-weight:600;margin-bottom:14px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
|
|
.table{width:100%;border-collapse:collapse;font-size:13px}
|
|
.table th{background:var(--panel2);color:var(--muted);padding:10px 14px;text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.6px;font-weight:600;border-bottom:1px solid var(--border)}
|
|
.table td{padding:10px 14px;border-bottom:1px solid var(--border);color:var(--text)}
|
|
.table tr:hover td{background:rgba(0,212,180,.03)}
|
|
.badge{padding:3px 9px;border-radius:4px;font-size:10.5px;font-weight:600;letter-spacing:.4px}
|
|
.badge.green{background:rgba(16,185,129,.2);color:#6ee7b7}
|
|
.badge.amber{background:rgba(245,158,11,.2);color:#fbbf24}
|
|
.badge.red{background:rgba(239,68,68,.2);color:#fca5a5}
|
|
.badge.blue{background:rgba(108,163,255,.2);color:#93c5fd}
|
|
.badge.purple{background:rgba(192,132,252,.2);color:#d8b4fe}
|
|
.n{font-family:'SF Mono',Monaco,monospace;font-weight:600;color:var(--accent)}
|
|
.domains-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:10px}
|
|
.dom-card{background:var(--panel2);border:1px solid var(--border);border-radius:8px;padding:12px 14px}
|
|
.dom-card .name{font-weight:600;text-transform:uppercase;font-size:11px;color:var(--accent);letter-spacing:.4px}
|
|
.dom-card .bar{margin-top:6px;height:10px;background:rgba(0,0,0,.3);border-radius:5px;overflow:hidden}
|
|
.dom-card .fill-green{height:100%;background:var(--green)}
|
|
.dom-card .fill-amber{height:100%;background:var(--amber)}
|
|
.dom-card .fill-red{height:100%;background:var(--red)}
|
|
.dom-card .meta{display:flex;justify-content:space-between;margin-top:6px;font-size:11px}
|
|
.dom-card .wired{color:#6ee7b7;font-family:'SF Mono',monospace;font-weight:600}
|
|
.dom-card .gap{color:#fca5a5;font-family:'SF Mono',monospace;font-weight:600}
|
|
.skills-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:12px}
|
|
.skill-card{background:var(--panel2);border:1px solid var(--border);border-radius:10px;padding:16px}
|
|
.skill-card .title{font-size:15px;font-weight:700;color:var(--accent);margin-bottom:4px}
|
|
.skill-card .src{font-size:10.5px;color:var(--muted);font-family:'SF Mono',monospace;margin-bottom:10px}
|
|
.skill-card .desc{font-size:12.5px;color:var(--text);margin-bottom:10px}
|
|
.skill-card .tags{display:flex;gap:6px;flex-wrap:wrap}
|
|
.skill-card .tag{font-size:10px;padding:2px 7px;border-radius:4px;background:rgba(0,212,180,.12);color:var(--accent)}
|
|
.skill-card button{margin-top:10px;padding:7px 12px;background:var(--accent);color:#000;border:none;border-radius:6px;cursor:pointer;font-size:11px;font-weight:600}
|
|
.gap-card{background:rgba(239,68,68,.05);border:1px solid rgba(239,68,68,.3);border-radius:8px;padding:14px;margin-bottom:8px;display:grid;grid-template-columns:70px 1fr auto;gap:14px;align-items:center}
|
|
.gap-card .prio{padding:4px 10px;background:rgba(239,68,68,.2);color:#fca5a5;border-radius:4px;font-size:11px;font-weight:700;text-align:center}
|
|
.gap-card .name{font-weight:600;color:var(--text)}
|
|
.gap-card .fix{font-size:11.5px;color:var(--muted);margin-top:2px}
|
|
.footer{text-align:center;margin-top:20px;font-size:10.5px;color:var(--muted)}
|
|
.footer a{color:#93c5fd;text-decoration:none;margin:0 8px}
|
|
.status-bar{position:sticky;bottom:0;background:linear-gradient(0deg,var(--bg),rgba(10,14,26,.95));padding:12px 18px;margin:0 -20px -20px -20px;display:flex;justify-content:space-between;font-size:11.5px;color:var(--muted);border-top:1px solid var(--border)}
|
|
.loading{text-align:center;padding:30px;color:var(--muted);font-size:13px}
|
|
|
|
/* === OPUS RESPONSIVE FIX v2 19avr — append-only, doctrine #14 === */
|
|
@media(max-width: 480px) {
|
|
html, body { overflow-x: hidden !important; max-width: 100vw; }
|
|
body, main, section, article { word-break: break-word; overflow-wrap: anywhere; }
|
|
img, video, iframe, canvas, svg, table, pre, code { max-width: 100% !important; }
|
|
pre, code { white-space: pre-wrap; word-break: break-all; }
|
|
table { display: block; overflow-x: auto; }
|
|
.container, [class*="container"], [class*="wrapper"] { max-width: 100vw !important; padding-left: 12px !important; padding-right: 12px !important; }
|
|
[class*="grid"], [class*="-grid"] { grid-template-columns: 1fr !important; gap: 10px !important; }
|
|
[class*="kpi"], [class*="stats"], [class*="-cards"] { grid-template-columns: 1fr !important; }
|
|
header, nav, footer { flex-wrap: wrap !important; }
|
|
header > *, nav > *, footer > * { max-width: 100%; }
|
|
h1 { font-size: 22px !important; word-break: break-word; }
|
|
h2 { font-size: 18px !important; }
|
|
.pitch, [class*="pitch"], [class*="hero"] { word-break: break-word; overflow-wrap: anywhere; }
|
|
}
|
|
/* === OPUS RESPONSIVE FIX v2 END === */
|
|
</style><!-- DOCTRINE-60-UX-ENRICH cerebras-qwen-235b 20260424-104142 --><style id="doctrine60-ux-wevia-autonomy-dashboard">
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: radial-gradient(circle, rgba(0,0,0,0.12), transparent 70%);
|
|
z-index: -1;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.card, .btn, .kpi, .panel {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.enter-stagger {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.6; }
|
|
}
|
|
.pulse, .active, .live-indicator, .online {
|
|
animation: pulse 3s ease-in-out infinite;
|
|
}
|
|
|
|
.card:hover {
|
|
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.modal, .chat, .speech, .overlay {
|
|
backdrop-filter: blur(12px);
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<div>
|
|
<h1>🧠 WEVIA Autonomy Dashboard <span style="padding:5px 12px;background:rgba(16,185,129,.2);color:#6ee7b7;border:1px solid rgba(16,185,129,.4);border-radius:99px;font-size:11px;font-weight:600">● LIVE v2</span></h1>
|
|
<div class="sub">NeuroRAG · Qdrant · 15K+ skills · 310 intents · 61 agents · 13 providers</div>
|
|
</div>
|
|
<div style="text-align:right"><div style="font-size:11px;color:rgba(255,255,255,.6)">→</div><a href="/weval-technology-platform.html" style="color:#93c5fd;text-decoration:none;font-size:13px">WTP Platform Portal</a></div>
|
|
</div>
|
|
|
|
<div id="autonomy-score" class="autonomy-score-box"><div class="loading" style="grid-column:1/-1">Chargement…</div></div>
|
|
<div id="kpis" class="kpi-row"><div class="loading">…</div></div>
|
|
|
|
<div class="section"><h2>📊 Arena Domains · 310 intents répartis · 187 wired · 107 gap</h2><div class="domains-grid" id="domains-grid"><div class="loading">…</div></div></div>
|
|
|
|
<div class="section"><h2>📦 Top Qdrant Collections (mémoire + skills + KB)</h2>
|
|
<table class="table"><thead><tr><th>Collection</th><th>Points</th><th>Type</th></tr></thead><tbody id="cols-tbody"><tr><td colspan="3" class="loading">…</td></tr></tbody></table></div>
|
|
|
|
<div class="section"><h2>⚡ 6 Skills stratégiques (from Yacine screenshots)</h2><div class="skills-grid" id="skills-grid"></div></div>
|
|
|
|
<div class="section"><h2>🔴 Gaps autonomie · Actions P1-P3</h2><div id="gaps-list"><div class="loading">…</div></div></div>
|
|
|
|
<div class="status-bar"><div>Autonomy v2 · <span id="ts">—</span></div><div>NonReg <span id="nr">—</span>/<span id="nr-total">153</span> · <span id="nr-pct" style="color:#6ee7b7">100%</span> · L99 invariant · WEVIA Master</div></div>
|
|
|
|
<div class="footer">
|
|
<a href="/visual-management.html">Visual Management</a>·<a href="/wevia-business-visual-studio.html">BVS</a>·<a href="/weval-technology-platform.html">WTP</a>·<a href="/l99.html">L99</a>·<a href="/wevia-master.html">WEVIA Master</a>
|
|
</div>
|
|
|
|
<script>
|
|
const SKILLS = [
|
|
{title:"Feynman Research",src:"github.com/getcompanion-ai/feynman",desc:"4 agents parallèles · research/review/draft/verify · live citations",tags:["research","parallel","citations"],trigger:"feynman research"},
|
|
{title:"Magika Scan",src:"github.com/google/magika · 15K",desc:"AI file type detection 99% accuracy · Apache 2.0",tags:["security","google"],trigger:"magika scan"},
|
|
{title:"Sous-agents Claude Code",src:"Claude Code /agents",desc:"Pattern parallel backend/frontend/payment · ~2k tokens par agent",tags:["parallel","pattern"],trigger:"sous agents"},
|
|
{title:"Gemma 4 Inside",src:"Google · Apache 2.0",desc:"80% LiveCodeBench · 256k context · 0$ coding alternative",tags:["LLM","coding","free"],trigger:"gemma 4"},
|
|
{title:"bitnet.cpp",src:"1-bit framework",desc:"100B params on CPU · 82% less energy · 100% open",tags:["CPU","offline"],trigger:"bitnet status"},
|
|
{title:"AirLLM",src:"github.com/lyogavin/airllm",desc:"405B LLMs on 8GB VRAM · layered inference",tags:["GPU","quantize"],trigger:"airllm status"}
|
|
];
|
|
|
|
function fmt(n){return (n||0).toLocaleString('fr-FR');}
|
|
function esc(s){return String(s||'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));}
|
|
|
|
async function refresh(){
|
|
try{
|
|
const r = await fetch('/api/wevia-neurorag-api.php?action=status&t='+Date.now(),{cache:'no-store'});
|
|
const d = await r.json();
|
|
const a = d.autonomy || {};
|
|
const I = a.intents || {}, S = a.skills || {}, AG = a.agents || {}, Q = a.qdrant || {};
|
|
|
|
document.getElementById('ts').textContent = (d.ts||'').replace('T',' ').split('+')[0];
|
|
document.getElementById('nr').textContent = a.nonreg_pass ?? a.nonreg_total ?? 153;
|
|
document.getElementById('nr-total').textContent = a.nonreg_total ?? 153;
|
|
document.getElementById('nr-pct').textContent = (a.nonreg_score ?? 100) + '%';
|
|
|
|
// Autonomy score
|
|
const score = a.autonomy_score || 0;
|
|
const level = a.autonomy_level || '—';
|
|
const levelColor = level==='MAX AUTONOMY' ? '#6ee7b7' : level==='HIGH' ? '#fbbf24' : '#fca5a5';
|
|
const cats = [
|
|
{l:'Skills TOTAL', v:S.TOTAL_ALL_SOURCES||0, m:15000},
|
|
{l:'Intents arena', v:I.TOTAL_MAX||0, m:375},
|
|
{l:'Agents TOTAL', v:AG.TOTAL||0, m:120},
|
|
{l:'Providers LLM', v:a.sovereign_providers||0, m:15},
|
|
{l:'Qdrant cols', v:Q.collections_count||0, m:30},
|
|
];
|
|
document.getElementById('autonomy-score').innerHTML =
|
|
'<div>'+
|
|
'<div class="label">Autonomy Score · '+esc(level)+'</div>'+
|
|
'<div class="score">'+score+'<span class="max">/100</span></div>'+
|
|
'<div class="level" style="background:'+levelColor+'33;color:'+levelColor+'">'+esc(level)+'</div>'+
|
|
'</div>'+
|
|
'<div class="bar-container">'+
|
|
cats.map(c=>{
|
|
const p = Math.min(100,(c.v/c.m)*100);
|
|
return '<div class="bar-row"><span class="lbl">'+esc(c.l)+'</span><div class="bar-bg"><div class="bar-fill" style="width:'+p+'%"></div></div><span class="val">'+fmt(c.v)+'</span></div>';
|
|
}).join('')+
|
|
'</div>';
|
|
|
|
// KPIs
|
|
document.getElementById('kpis').innerHTML =
|
|
'<div class="kpi green"><div class="l">Skills TOTAL</div><div class="v good">'+fmt(S.TOTAL_ALL_SOURCES)+'</div><div class="s">Qdrant '+fmt(S.qdrant_weval_skills_points)+' + tools '+fmt(S.tools_registry)+'</div></div>'+
|
|
'<div class="kpi blue"><div class="l">Intents Arena</div><div class="v">'+fmt(I.TOTAL_MAX)+'</div><div class="s">'+fmt(I.arena_wired)+' wired · gap '+fmt(I.arena_gap)+'</div></div>'+
|
|
'<div class="kpi purple"><div class="l">Agents TOTAL</div><div class="v">'+fmt(AG.TOTAL)+'</div><div class="s">'+fmt(AG.agent_files)+' files + '+fmt(AG.agent_stubs)+' stubs</div></div>'+
|
|
'<div class="kpi amber"><div class="l">Qdrant Points</div><div class="v">'+fmt(Q.total_points)+'</div><div class="s">'+fmt(Q.collections_count)+' collections</div></div>'+
|
|
'<div class="kpi green"><div class="l">Sovereign LLMs</div><div class="v good">'+(a.sovereign_providers||0)+'/13</div><div class="s">providers 0$</div></div>'+
|
|
'<div class="kpi green"><div class="l">NonReg</div><div class="v good">'+(a.nonreg_pass||a.nonreg_total||153)+'/'+(a.nonreg_total||153)+'</div><div class="s">'+(a.nonreg_score||100)+'% · invariant</div></div>';
|
|
|
|
// Domains
|
|
const domains = a.arena_domains || {};
|
|
const domHtml = Object.entries(domains).map(([name,info])=>{
|
|
const w = info.wired||0, t = info.count||0, g = info.gap||0;
|
|
const pct = t ? (w/t)*100 : 0;
|
|
const cls = pct===100 ? 'fill-green' : pct>=50 ? 'fill-amber' : 'fill-red';
|
|
return '<div class="dom-card"><div class="name">'+esc(name)+'</div><div class="bar"><div class="'+cls+'" style="width:'+pct+'%"></div></div><div class="meta"><span class="wired">'+w+'/'+t+' wired</span><span class="gap">gap '+g+'</span></div></div>';
|
|
}).join('');
|
|
document.getElementById('domains-grid').innerHTML = domHtml || '<div class="loading">N/A</div>';
|
|
|
|
// Collections
|
|
const cols = Q.top_collections || [];
|
|
const colHtml = cols.map(c=>{
|
|
const type = c.name.includes('memory')?'Memory':c.name.includes('skill')?'Skill':'KB';
|
|
const badge = type==='Memory'?'green':type==='Skill'?'purple':'blue';
|
|
return '<tr><td><code>'+esc(c.name)+'</code></td><td class="n">'+fmt(c.points)+'</td><td><span class="badge '+badge+'">'+type+'</span></td></tr>';
|
|
}).join('');
|
|
document.getElementById('cols-tbody').innerHTML = colHtml || '<tr><td colspan="3">N/A</td></tr>';
|
|
|
|
// Skills
|
|
document.getElementById('skills-grid').innerHTML = SKILLS.map(s=>
|
|
'<div class="skill-card"><div class="title">'+esc(s.title)+'</div><div class="src">'+esc(s.src)+'</div><div class="desc">'+esc(s.desc)+'</div><div class="tags">'+s.tags.map(t=>'<span class="tag">'+esc(t)+'</span>').join('')+'</div><button onclick="runIntent(\''+esc(s.trigger)+'\')">▶ '+esc(s.trigger)+'</button></div>'
|
|
).join('');
|
|
|
|
// Gaps
|
|
const gaps = a.gaps || [];
|
|
document.getElementById('gaps-list').innerHTML = gaps.length ?
|
|
gaps.map(g=>'<div class="gap-card"><span class="prio">'+esc(g.priority)+'</span><div><div class="name">'+esc(g.name)+'</div><div class="fix">'+esc(g.fix)+'</div></div><button onclick="runIntent(\'fix '+esc(g.priority).toLowerCase()+'\')" style="padding:7px 12px;background:var(--accent);color:#000;border:none;border-radius:6px;cursor:pointer;font-weight:600">Fix</button></div>').join('') :
|
|
'<div style="color:var(--muted);padding:12px">Aucun gap critique</div>';
|
|
}catch(e){console.error(e);}
|
|
}
|
|
|
|
async function runIntent(t){
|
|
try{
|
|
const r = await fetch('/api/wevia-master-api.php?fast=1',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:t})});
|
|
const x = await r.text();
|
|
alert('WEVIA ('+x.length+'b):\n'+x.substring(0,500));
|
|
}catch(e){alert('Error: '+e.message);}
|
|
}
|
|
|
|
refresh(); setInterval(refresh, 45000);
|
|
</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/archi-meta-badge.js" defer></script>
|
|
|
|
<script src="/api/a11y-auto-enhancer.js" defer></script>
|
|
<!-- WTP_UDOCK_V1 (Opus 21-avr t33b6) --><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-wevia-autonomy-dashboard">
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry, index) => {
|
|
if (entry.isIntersecting) {
|
|
setTimeout(() => {
|
|
entry.target.classList.add('enter-stagger');
|
|
}, index * 80);
|
|
}
|
|
});
|
|
}, { threshold: 0.1 });
|
|
|
|
document.querySelectorAll('.card, .btn, .kpi, .panel').forEach(el => {
|
|
observer.observe(el);
|
|
});
|
|
|
|
</script>
|
|
</body></html>
|