476 lines
27 KiB
HTML
476 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 Unified Hub v2 · tout dédupliqué</title>
|
|
<style>
|
|
:root{--bg:#0a0e1a;--panel:#141933;--panel2:#1a2140;--border:#263161;--text:#e4e8f7;--muted:#8b95b8;--accent:#00d4b4;--green:#10b981;--amber:#f59e0b;--red:#ef4444;--blue:#6ba3ff;--purple:#c084fc}
|
|
*{box-sizing:border-box;margin:0;padding:0}
|
|
body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;line-height:1.5}
|
|
.layout{display:grid;grid-template-columns:240px 1fr;min-height:100vh}
|
|
.sidebar{background:var(--panel);border-right:1px solid var(--border);padding:20px 16px;position:sticky;top:0;height:100vh;overflow-y:auto}
|
|
.sidebar h3{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin:16px 0 8px 0;font-weight:600}
|
|
.sidebar a{display:block;padding:8px 12px;color:var(--text);text-decoration:none;font-size:13px;border-radius:6px;margin-bottom:2px;cursor:pointer}
|
|
.sidebar a:hover{background:var(--panel2);color:var(--accent)}
|
|
.sidebar a.active{background:var(--accent);color:#000;font-weight:600}
|
|
.sidebar .logo{font-size:18px;font-weight:800;color:#fff;margin-bottom:18px}
|
|
.main{padding:24px 30px;max-width:1500px}
|
|
.header{display:flex;justify-content:space-between;align-items:center;padding:18px 24px;background:linear-gradient(135deg,#1e1b4b,#312e81);border-radius:12px;margin-bottom:20px;border:1px solid var(--border)}
|
|
.header h1{color:#fff;font-size:22px}
|
|
.header .sub{color:rgba(255,255,255,.7);font-size:11.5px;margin-top:2px}
|
|
.header .score{padding:6px 14px;background:rgba(16,185,129,.25);color:#6ee7b7;border:1px solid rgba(16,185,129,.5);border-radius:99px;font-size:12px;font-weight:700}
|
|
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:22px}
|
|
.kpi{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:14px 16px;position:relative;cursor:pointer;transition:all .2s}
|
|
.kpi:hover{border-color:var(--accent);transform:translateY(-2px)}
|
|
.kpi::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:var(--accent);border-radius:4px 0 0 4px}
|
|
.kpi.agents::before{background:var(--purple)}.kpi.intents::before{background:var(--blue)}.kpi.skills::before{background:var(--green)}.kpi.brains::before{background:var(--amber)}.kpi.dashboards::before{background:#ec4899}.kpi.apis::before{background:#14b8a6}
|
|
.kpi .l{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px;font-weight:600}
|
|
.kpi .v{font-size:28px;font-weight:800;color:var(--text);font-family:'SF Mono',monospace;line-height:1}
|
|
.kpi .s{font-size:10.5px;color:var(--muted);margin-top:4px}
|
|
.section{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px 22px;margin-bottom:18px}
|
|
.section h2{font-size:15px;font-weight:600;color:var(--accent);margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border);display:flex;justify-content:space-between;align-items:center}
|
|
.section h2 .count{font-family:monospace;background:rgba(0,212,180,.15);color:var(--accent);padding:3px 9px;border-radius:4px;font-size:12px}
|
|
.sources-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:8px;margin:14px 0}
|
|
.source-box{background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:10px 12px}
|
|
.source-box .n{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;font-weight:600;margin-bottom:4px}
|
|
.source-box .c{font-size:22px;font-weight:800;color:var(--accent);font-family:monospace;line-height:1}
|
|
.explanation{padding:14px;background:rgba(0,212,180,.05);border:1px solid rgba(0,212,180,.2);border-radius:8px;margin-bottom:14px;font-size:12.5px;color:var(--text);line-height:1.6}
|
|
.explanation b{color:var(--accent)}
|
|
.tabs{display:flex;gap:6px;margin-bottom:14px;border-bottom:1px solid var(--border);padding-bottom:0;flex-wrap:wrap}
|
|
.tab{padding:8px 14px;cursor:pointer;font-size:12px;color:var(--muted);border-radius:6px 6px 0 0;border:1px solid transparent;font-weight:500}
|
|
.tab:hover{color:var(--text)}
|
|
.tab.active{color:var(--accent);border-color:var(--border);border-bottom-color:var(--panel);background:var(--panel2);font-weight:700}
|
|
.tab .count{font-family:monospace;background:rgba(0,212,180,.12);color:var(--accent);padding:1px 6px;border-radius:3px;margin-left:6px;font-size:10.5px}
|
|
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:10px;max-height:600px;overflow-y:auto;padding-right:4px}
|
|
.grid::-webkit-scrollbar{width:6px}
|
|
.grid::-webkit-scrollbar-track{background:var(--bg)}
|
|
.grid::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}
|
|
.card{background:var(--panel2);border:1px solid var(--border);border-radius:8px;padding:11px 14px;transition:border-color .15s}
|
|
.card:hover{border-color:var(--accent)}
|
|
.card .name{font-size:12.5px;font-weight:700;color:var(--accent);margin-bottom:3px;display:flex;align-items:center;gap:6px}
|
|
.card .name .icon{font-size:16px}
|
|
.card .meta{font-size:10.5px;color:var(--muted);display:flex;gap:6px;flex-wrap:wrap;margin-top:3px}
|
|
.card .desc{font-size:11.5px;color:var(--text);margin-top:6px;line-height:1.35}
|
|
.badge{font-size:10px;padding:2px 6px;border-radius:3px;font-weight:600}
|
|
.badge.green{background:rgba(16,185,129,.2);color:#6ee7b7}
|
|
.badge.blue{background:rgba(108,163,255,.2);color:#93c5fd}
|
|
.badge.purple{background:rgba(192,132,252,.2);color:#d8b4fe}
|
|
.badge.amber{background:rgba(245,158,11,.2);color:#fbbf24}
|
|
.badge.red{background:rgba(239,68,68,.2);color:#fca5a5}
|
|
.sources-pills{display:flex;gap:3px;flex-wrap:wrap;margin-top:4px}
|
|
.sources-pills .pill{font-size:9.5px;padding:1px 6px;border-radius:3px;background:rgba(0,212,180,.1);color:var(--accent);border:1px solid rgba(0,212,180,.2)}
|
|
.search{width:100%;padding:8px 12px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:5px;font-size:12.5px;margin-bottom:10px}
|
|
.legend-row{display:flex;justify-content:space-between;align-items:center;padding:7px 12px;background:var(--panel2);border-radius:5px;margin-bottom:4px;font-size:12px}
|
|
.legend-row .b{font-family:monospace;color:var(--accent);font-weight:700}
|
|
.footer{text-align:center;margin-top:20px;font-size:10.5px;color:var(--muted);padding:16px}
|
|
.hidden{display:none}
|
|
a.card-link{text-decoration:none;color:inherit;display:block}
|
|
@media (max-width: 900px){.layout{grid-template-columns:1fr}.sidebar{position:relative;height:auto;padding:12px}}
|
|
|
|
/* === 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></head>
|
|
<body>
|
|
<div class="layout">
|
|
<aside class="sidebar">
|
|
<div class="logo">🧠 WEVIA Hub v2</div>
|
|
<h3>Vue consolidée</h3>
|
|
<a onclick="show('summary', event)" class="active">📊 Summary</a>
|
|
<a onclick="show('agents', event)">🤖 Agents</a>
|
|
<a onclick="show('intents', event)">🎯 Intents</a>
|
|
<a onclick="show('skills', event)">⚡ Skills</a>
|
|
<a onclick="show('brains', event)">🧠 Brains</a>
|
|
<a onclick="show('dashboards', event)">📊 Dashboards</a>
|
|
<h3>Outils</h3>
|
|
<a href="/wevia-autonomy-dashboard.html">🏆 Autonomy Dashboard</a>
|
|
<a href="/wevia-master.html">💬 WEVIA Master Chat</a>
|
|
<a href="/wevia-business-visual-studio.html">🎬 BVS Studio</a>
|
|
<a href="/skills/">📦 Skills Explorer</a>
|
|
<a href="/wevia-training.html">🎓 Training</a>
|
|
<a href="/weval-technology-platform.html">🎯 WTP</a>
|
|
<a href="/l99.html">📐 L99</a>
|
|
</aside>
|
|
<main class="main">
|
|
<div class="header">
|
|
<div><h1>🧠 WEVIA Unified Hub v2 · dédupliqué</h1>
|
|
<div class="sub">TOUTES les sources agrégées · déduplication par nom · finie la dispersion</div></div>
|
|
<div class="score">● GODMODE 100/100</div>
|
|
</div>
|
|
|
|
<div class="kpis" id="kpis"></div>
|
|
|
|
<div class="section" style="background:linear-gradient(135deg,#1e1b4b20,#31298120)">
|
|
<h2>💬 Demande n'importe quoi à WEVIA · NL Query</h2>
|
|
<p style="color:var(--muted);font-size:12px;margin-bottom:12px">Powered by /api/wevia.php?q=... · pattern matching sur truth-registry</p>
|
|
<div style="display:flex;gap:8px;margin-bottom:12px">
|
|
<input type="text" id="nl-q" placeholder="Ex: combien d'agents · status · agent cerebras · intent warmup..."
|
|
style="flex:1;padding:10px 14px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:5px;font-size:13px;font-family:monospace"
|
|
onkeypress="if(event.key==='Enter')doNLQuery()">
|
|
<button onclick="doNLQuery()"
|
|
style="padding:10px 22px;background:var(--accent);color:#000;border:none;border-radius:5px;cursor:pointer;font-size:12px;font-weight:700">
|
|
🎯 Ask WEVIA
|
|
</button>
|
|
</div>
|
|
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px" id="nl-examples">
|
|
<button onclick="document.getElementById('nl-q').value='combien d agents';doNLQuery()" class="ex-btn">combien d'agents</button>
|
|
<button onclick="document.getElementById('nl-q').value='status';doNLQuery()" class="ex-btn">status</button>
|
|
<button onclick="document.getElementById('nl-q').value='autonomy score';doNLQuery()" class="ex-btn">autonomy</button>
|
|
<button onclick="document.getElementById('nl-q').value='agent cerebras';doNLQuery()" class="ex-btn">agent cerebras</button>
|
|
<button onclick="document.getElementById('nl-q').value='intent ethica';doNLQuery()" class="ex-btn">intent ethica</button>
|
|
<button onclick="document.getElementById('nl-q').value='combien de skills';doNLQuery()" class="ex-btn">skills</button>
|
|
</div>
|
|
<pre id="nl-result" style="background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:14px;font-size:12px;font-family:'SF Mono',monospace;color:var(--text);min-height:80px;white-space:pre-wrap;overflow-x:auto">Clique un exemple ou tape ta question + Enter · Results apparaissent ici</pre>
|
|
</div>
|
|
<style>
|
|
.ex-btn{padding:5px 11px;background:var(--panel2);border:1px solid var(--border);color:var(--accent);border-radius:4px;cursor:pointer;font-size:11px;font-family:monospace}
|
|
.ex-btn:hover{background:var(--accent);color:#000}
|
|
</style>
|
|
<script>
|
|
async function doNLQuery(){
|
|
const q = document.getElementById('nl-q').value.trim();
|
|
if(!q) return;
|
|
const el = document.getElementById('nl-result');
|
|
el.textContent = '⏳ Thinking...';
|
|
try{
|
|
const r = await fetch('/api/wevia.php?q=' + encodeURIComponent(q) + '&format=text');
|
|
const t = await r.text();
|
|
el.textContent = t;
|
|
}catch(e){
|
|
el.textContent = 'Erreur: ' + e.message;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<!-- SUMMARY -->
|
|
<div id="view-summary" class="view">
|
|
<div class="section">
|
|
<h2>🤖 Agents · sources agrégées</h2>
|
|
<div class="explanation">
|
|
<b>Les KPI montrés dans les autres dashboards viennent de sources différentes :</b><br>
|
|
• <b>950</b> (wevia-training.html / WTP) = chiffre marketing <i>paperclip+ ecosystem reference</i><br>
|
|
• <b>990</b> (paperclip dashboard) = tools-registry 619 + ancien count<br>
|
|
• <b>126</b> (ancien unified hub) = 3 sources seulement<br>
|
|
<b>→ Vrai count : <span style="color:var(--accent);font-weight:700" id="dedup-count">…</span> agents uniques dédupliqués sur 7+ sources (total avec overlaps : <span id="overlap-count">…</span>)</b>
|
|
</div>
|
|
<div class="sources-grid" id="agent-sources"></div>
|
|
</div>
|
|
<div class="section">
|
|
<h2>🎯 Intents · status + domaine</h2>
|
|
<div id="intents-summary"></div>
|
|
</div>
|
|
<div class="section">
|
|
<h2>⚡ Skills · 5 sources</h2>
|
|
<div id="skills-summary"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- AGENTS -->
|
|
<div id="view-agents" class="view hidden">
|
|
<div class="section">
|
|
<h2>🤖 269 agents uniques (sources agrégées) <span class="count" id="ag-total">—</span></h2>
|
|
<div class="tabs" id="ag-tabs"></div>
|
|
<input type="text" class="search" id="search-agents" placeholder="🔎 Filter agents..." oninput="filterAgents()">
|
|
<div class="grid" id="agents-grid"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- INTENTS -->
|
|
<div id="view-intents" class="view hidden">
|
|
<div class="section">
|
|
<h2>🎯 Intents <span class="count" id="int-total">—</span></h2>
|
|
<div class="tabs" id="int-tabs"></div>
|
|
<input type="text" class="search" id="search-intents" placeholder="🔎 Filter intents..." oninput="filterIntents()">
|
|
<div class="grid" id="intents-grid"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SKILLS -->
|
|
<div id="view-skills" class="view hidden">
|
|
<div class="section">
|
|
<h2>⚡ Skills 15 509 <span class="count">5 sources</span></h2>
|
|
<div id="skills-view"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- BRAINS -->
|
|
<div id="view-brains" class="view hidden">
|
|
<div class="section">
|
|
<h2>🧠 Brains <span class="count" id="brains-total">—</span></h2>
|
|
<div class="grid" id="brains-grid"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DASHBOARDS -->
|
|
<div id="view-dashboards" class="view hidden">
|
|
<div class="section">
|
|
<h2>📊 92 dashboards</h2>
|
|
<p style="color:var(--muted);font-size:12px">Voir <a href="/dashboards-hub.html" style="color:var(--accent)">Dashboards Hub</a> pour liste complète catégorisée</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="footer"><span>WEVIA Unified Hub v2 · <span id="ts">—</span></span></div>
|
|
</main>
|
|
</div>
|
|
|
|
<script>
|
|
let DATA = null;
|
|
let agentsList = [];
|
|
let intentsList = [];
|
|
let curAgTab = 'all', curIntTab = 'all';
|
|
|
|
function fmt(n){return (n||0).toLocaleString('fr-FR');}
|
|
function esc(s){return String(s||'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));}
|
|
|
|
async function loadData(){
|
|
try{
|
|
const r1 = await fetch('/api/wevia-unified-api.php?action=summary&t='+Date.now(),{cache:'no-store'});
|
|
DATA = await r1.json();
|
|
document.getElementById('ts').textContent = (DATA.ts||'').replace('T',' ').split('+')[0];
|
|
|
|
const s = DATA.summary || {};
|
|
const ag = s.agents || {};
|
|
const int = s.intents || {};
|
|
const sk = s.skills || {};
|
|
|
|
// KPIs
|
|
document.getElementById('kpis').innerHTML =
|
|
'<div class="kpi agents" onclick="show(\'agents\')"><div class="l">Agents UNIQUES</div><div class="v">'+fmt(ag.UNIQUE_DEDUP)+'</div><div class="s">'+fmt(ag.total_with_overlaps)+' avec overlaps</div></div>'+
|
|
'<div class="kpi intents" onclick="show(\'intents\')"><div class="l">Intents</div><div class="v">'+fmt(int.php_files)+'</div><div class="s">'+fmt(int.arena_declared)+' declared arena</div></div>'+
|
|
'<div class="kpi skills" onclick="show(\'skills\')"><div class="l">Skills TOTAL</div><div class="v">'+fmt(sk.TOTAL_ALL_SOURCES)+'</div><div class="s">5 sources</div></div>'+
|
|
'<div class="kpi brains" onclick="show(\'brains\')"><div class="l">Brains</div><div class="v">'+fmt(s.brains?.count)+'</div><div class="s">orchestration</div></div>'+
|
|
'<div class="kpi dashboards" onclick="show(\'dashboards\')"><div class="l">Dashboards</div><div class="v">'+fmt(s.dashboards)+'</div><div class="s">HTML pages</div></div>'+
|
|
'<div class="kpi apis"><div class="l">APIs PHP</div><div class="v">'+fmt(s.apis)+'</div><div class="s">/api/*.php</div></div>';
|
|
|
|
// Summary view
|
|
document.getElementById('dedup-count').textContent = fmt(ag.UNIQUE_DEDUP);
|
|
document.getElementById('overlap-count').textContent = fmt(ag.total_with_overlaps);
|
|
|
|
// Agent sources grid
|
|
const srcMap = {
|
|
agent_avatars_v2: '🎨 Avatars v2',
|
|
agent_avatars_v1: '🎨 Avatars v1 (legacy)',
|
|
wevia_agents_registry: '📋 Registry categorized',
|
|
paperclip_agility_v71: '🏃 Paperclip V71 Agility',
|
|
api_agent_files: '📁 API agent files',
|
|
agent_stubs: '⚙️ Agent stubs PHP',
|
|
claude_subagents: '🤖 Claude sub-agents',
|
|
qdrant_agents_vectorized: '🧠 Qdrant vectorized',
|
|
paperclip_opt_catalog: '📚 Paperclip /opt catalog',
|
|
};
|
|
document.getElementById('agent-sources').innerHTML = Object.entries(ag.per_source||{}).map(([k,v]) =>
|
|
'<div class="source-box"><div class="n">'+esc(srcMap[k]||k)+'</div><div class="c">'+fmt(v)+'</div></div>'
|
|
).join('');
|
|
|
|
// Intents summary
|
|
const byStatus = int.by_status||{};
|
|
const byDomain = int.by_domain||{};
|
|
let intHtml = '<h3 style="font-size:12px;color:var(--purple);margin-bottom:8px">Par status</h3>';
|
|
Object.entries(byStatus).forEach(([k,v]) => {
|
|
const cls = k==='EXECUTED'?'green':k.includes('PENDING')?'amber':'blue';
|
|
intHtml += '<div class="legend-row"><span class="badge '+cls+'">'+esc(k)+'</span><span class="b">'+fmt(v)+'</span></div>';
|
|
});
|
|
intHtml += '<h3 style="font-size:12px;color:var(--purple);margin:14px 0 8px 0">Par domaine (top 8)</h3>';
|
|
Object.entries(byDomain).sort((a,b)=>b[1]-a[1]).slice(0,8).forEach(([k,v]) => {
|
|
intHtml += '<div class="legend-row"><span>'+esc(k)+'</span><span class="b">'+fmt(v)+'</span></div>';
|
|
});
|
|
document.getElementById('intents-summary').innerHTML = intHtml;
|
|
|
|
// Skills summary
|
|
document.getElementById('skills-summary').innerHTML =
|
|
'<div class="sources-grid">'+
|
|
'<div class="source-box"><div class="n">Disk dossiers</div><div class="c">'+fmt(sk.disk_dirs)+'</div></div>'+
|
|
'<div class="source-box"><div class="n">Qdrant vectorisés</div><div class="c">'+fmt(sk.qdrant_vectorized)+'</div></div>'+
|
|
'<div class="source-box"><div class="n">Tools registry</div><div class="c">'+fmt(sk.tools_registry)+'</div></div>'+
|
|
'<div class="source-box"><div class="n">Arena declared</div><div class="c">'+fmt(sk.arena_declared)+'</div></div>'+
|
|
'<div class="source-box"><div class="n">Doctrines</div><div class="c">'+fmt(sk.doctrines)+'</div></div>'+
|
|
'</div>'+
|
|
'<p style="color:var(--muted);font-size:12px;margin-top:10px">→ <a href="/skills/" style="color:var(--accent)">Skills Explorer complet</a> avec search Qdrant interactif</p>';
|
|
}catch(e){console.error(e);}
|
|
|
|
// Load agents
|
|
try{
|
|
const r2 = await fetch('/api/wevia-unified-api.php?action=agents&t='+Date.now(),{cache:'no-store'});
|
|
const d2 = await r2.json();
|
|
agentsList = d2.agents || [];
|
|
renderAgents();
|
|
}catch(e){console.error(e);}
|
|
}
|
|
|
|
function renderAgents(){
|
|
document.getElementById('ag-total').textContent = fmt(agentsList.length);
|
|
// Tabs by sources_count (1 source = unique, 2+ = multi-ref)
|
|
const bySources = {};
|
|
agentsList.forEach(a => {
|
|
const k = a.sources_count >= 3 ? '3+refs' : a.sources_count + 'ref';
|
|
bySources[k] = (bySources[k]||0)+1;
|
|
});
|
|
let tabsHtml = '<div class="tab active" onclick="tabAg(\'all\',event)">Tous<span class="count">'+agentsList.length+'</span></div>';
|
|
['3+refs','2ref','1ref'].forEach(k => {
|
|
if (bySources[k]) tabsHtml += '<div class="tab" onclick="tabAg(\''+k+'\',event)">'+esc(k)+'<span class="count">'+bySources[k]+'</span></div>';
|
|
});
|
|
document.getElementById('ag-tabs').innerHTML = tabsHtml;
|
|
filterAgents();
|
|
}
|
|
|
|
function tabAg(cat,ev){
|
|
curAgTab = cat;
|
|
document.querySelectorAll('#ag-tabs .tab').forEach(t => t.classList.remove('active'));
|
|
if (ev) ev.target.closest('.tab').classList.add('active');
|
|
filterAgents();
|
|
}
|
|
|
|
function filterAgents(){
|
|
const q = (document.getElementById('search-agents').value||'').toLowerCase();
|
|
const filtered = agentsList.filter(a => {
|
|
if (curAgTab !== 'all') {
|
|
const k = a.sources_count >= 3 ? '3+refs' : a.sources_count + 'ref';
|
|
if (k !== curAgTab) return false;
|
|
}
|
|
if (q && !a.name.toLowerCase().includes(q) && !(a.role||'').toLowerCase().includes(q)) return false;
|
|
return true;
|
|
}).slice(0, 400);
|
|
document.getElementById('agents-grid').innerHTML = filtered.map(a => {
|
|
const icon = a.icon || '🤖';
|
|
const pills = (a.sources||[]).map(s => '<span class="pill">'+esc(s.replace('agent_','').replace('_',' '))+'</span>').join('');
|
|
return '<div class="card"><div class="name"><span class="icon">'+esc(icon)+'</span>'+esc(a.name)+'</div>'+
|
|
(a.role ? '<div class="desc">'+esc(a.role)+'</div>' : '')+
|
|
'<div class="sources-pills">'+pills+'</div></div>';
|
|
}).join('') || '<div style="color:var(--muted);padding:20px;text-align:center">Aucun résultat</div>';
|
|
}
|
|
|
|
function show(view, ev){
|
|
if (ev) ev.preventDefault();
|
|
document.querySelectorAll('.view').forEach(v => v.classList.add('hidden'));
|
|
document.getElementById('view-'+view)?.classList.remove('hidden');
|
|
document.querySelectorAll('.sidebar a').forEach(a => a.classList.remove('active'));
|
|
if (ev) ev.target.classList.add('active');
|
|
}
|
|
|
|
async function loadIntents(){
|
|
if (intentsList.length) return;
|
|
try{
|
|
const r = await fetch('/api/wevia-unified-api.php?action=intents');
|
|
const d = await r.json();
|
|
intentsList = d.intents_list || [];
|
|
renderIntents();
|
|
}catch(e){console.error(e);}
|
|
}
|
|
|
|
function renderIntents(){
|
|
document.getElementById('int-total').textContent = fmt(intentsList.length);
|
|
const byStatus = {};
|
|
intentsList.forEach(x => byStatus[x.status] = (byStatus[x.status]||0)+1);
|
|
let tabsHtml = '<div class="tab active" onclick="tabInt(\'all\',event)">Tous<span class="count">'+intentsList.length+'</span></div>';
|
|
Object.entries(byStatus).forEach(([k,v]) => {
|
|
tabsHtml += '<div class="tab" onclick="tabInt(\''+k+'\',event)">'+esc(k)+'<span class="count">'+v+'</span></div>';
|
|
});
|
|
document.getElementById('int-tabs').innerHTML = tabsHtml;
|
|
filterIntents();
|
|
}
|
|
|
|
function tabInt(cat,ev){
|
|
curIntTab = cat;
|
|
document.querySelectorAll('#int-tabs .tab').forEach(t => t.classList.remove('active'));
|
|
if (ev) ev.target.closest('.tab').classList.add('active');
|
|
filterIntents();
|
|
}
|
|
|
|
function filterIntents(){
|
|
const q = (document.getElementById('search-intents').value||'').toLowerCase();
|
|
const filtered = intentsList.filter(x => {
|
|
if (curIntTab !== 'all' && x.status !== curIntTab) return false;
|
|
if (q) { const hay = (x.name+' '+(x.triggers||[]).join(' ')+' '+x.domain).toLowerCase(); if (!hay.includes(q)) return false; }
|
|
return true;
|
|
}).slice(0, 400);
|
|
document.getElementById('intents-grid').innerHTML = filtered.map(x => {
|
|
const cls = x.status==='EXECUTED'?'green':x.status.includes('PENDING')?'amber':'blue';
|
|
const trigs = (x.triggers||[]).slice(0,2).map(t => esc(t)).join(' · ');
|
|
return '<div class="card"><div class="name">🎯 '+esc(x.name)+'</div>'+
|
|
'<div class="meta"><span class="badge '+cls+'">'+esc(x.status)+'</span><span class="badge purple">'+esc(x.domain)+'</span></div>'+
|
|
(trigs ? '<div class="desc" style="font-family:monospace;font-size:10.5px">→ '+trigs+'</div>' : '')+
|
|
'</div>';
|
|
}).join('') || '<div style="color:var(--muted);padding:20px;text-align:center">Aucun résultat</div>';
|
|
}
|
|
|
|
// Hook on show() to lazy-load intents
|
|
const origShow = show;
|
|
show = function(v, e) { origShow(v, e); if (v==='intents') loadIntents(); };
|
|
|
|
loadData();
|
|
</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) {
|
|
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 (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);} });
|
|
}
|
|
}
|
|
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/v72-drilldown-universal.js" defer></script>
|
|
<script src="/api/archi-meta-badge.js" defer></script>
|
|
</body></html>
|