Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
Phase 32 (doctrine 169): - Cron */10min batch-enrich-async.sh installe auto-enrichement queue - 4 dernieres pages PRIO enrichies: deepseek-hub, universal-integration-hub, qa-hub, wevia-ops-hub - Total cumul session: 39 pages UX doctrine 60 Phase 33 (doctrine 170-173): - Playwright audit v3 operationnel (10 pages, 20 zooms captured) - all-ia-hub overlap detecte: 4 tr + 2 br elements superposes - Fix CSS surgical applique (doctrine 172 + 173): - Logout top-right z-index 10000 - Feedback bot-right 20px - Secondary aside shifted bottom 84px (zero chauvauchement) - GOLD backups crees avant chaque fix Handler cascade Cerebras->Ollama validate (cerebras-qwen-235b primary). Pattern nohup & background valide pour eviter 502 CX endpoint. NR 153/153 invariant.
1475 lines
82 KiB
HTML
1475 lines
82 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr" data-theme="dark">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
<title>All-IA Hub — Remplacement Claude Code + Opus (Souverain)</title>
|
|
<meta name="description" content="Hub unifié combinant WEVIA Master streaming, WEVCODE modes, Arena multi-provider. Indépendance Opus.">
|
|
<style>
|
|
:root{--bg:#0a0a0f;--bg2:#111119;--bg3:#181825;--rim:#2a2a3a;--t:#e2e8f0;--mu:#94a3b8;--dm:#64748b;--cy:#22d3ee;--gr:#10b981;--vl:#a78bfa;--am:#f59e0b;--rd:#ef4444}
|
|
*{box-sizing:border-box;margin:0;padding:0}
|
|
body{font-family:'JetBrains Mono','Menlo',monospace;background:var(--bg);color:var(--t);min-height:100vh;display:flex;flex-direction:column}
|
|
.hdr{padding:10px 18px;background:var(--bg2);border-bottom:1px solid var(--rim);display:flex;align-items:center;justify-content:space-between;gap:14px}
|
|
.hdr .brand{display:flex;align-items:center;gap:10px}
|
|
.hdr .logo{width:34px;height:34px;border-radius:8px;background:linear-gradient(135deg,var(--cy),var(--vl));display:flex;align-items:center;justify-content:center;color:var(--bg);font-weight:700;font-size:14px}
|
|
.hdr h1{font-size:14px;font-weight:700;letter-spacing:.4px}
|
|
.hdr .sub{font-size:10px;color:var(--mu);letter-spacing:.3px;margin-top:2px}
|
|
.hdr .stats{display:flex;gap:14px;font-size:10px;color:var(--dm);letter-spacing:.3px}
|
|
.hdr .stats span b{color:var(--cy);font-weight:700}
|
|
.tabs{display:flex;gap:2px;padding:0 18px;background:var(--bg2);border-bottom:1px solid var(--rim);overflow-x:auto}
|
|
.tab{padding:10px 16px;background:transparent;border:0;color:var(--mu);font-family:inherit;font-size:11px;font-weight:600;cursor:pointer;letter-spacing:.4px;border-bottom:2px solid transparent;transition:all .2s;white-space:nowrap}
|
|
.tab:hover{color:var(--t)}
|
|
.tab.on{color:var(--cy);border-bottom-color:var(--cy)}
|
|
main{flex:1;display:flex;flex-direction:column;padding:16px 20px;max-width:1400px;width:100%;margin:0 auto;min-height:0}
|
|
.view{display:none;flex-direction:column;flex:1;min-height:0}
|
|
.view.on{display:flex}
|
|
/* Chat/Code shared layout */
|
|
.modes{display:flex;gap:4px;margin-bottom:10px;flex-wrap:wrap}
|
|
.mode{padding:6px 12px;border:1px solid var(--rim);border-radius:6px;background:transparent;color:var(--mu);font-family:inherit;font-size:10px;font-weight:600;cursor:pointer;transition:all .15s;letter-spacing:.3px}
|
|
.mode:hover{border-color:var(--cy);color:var(--t)}
|
|
.mode.on{background:var(--cy);color:var(--bg);border-color:var(--cy)}
|
|
.stbar{display:flex;gap:14px;margin-bottom:10px;font-size:10px;color:var(--dm);letter-spacing:.4px}
|
|
.stbar span{display:flex;align-items:center;gap:5px}
|
|
.stbar .dot{width:6px;height:6px;border-radius:50%;background:var(--gr)}
|
|
.out{flex:1;background:var(--bg2);border:1px solid var(--rim);border-radius:8px;padding:14px 18px;overflow-y:auto;font-size:12px;line-height:1.65;min-height:240px}
|
|
.out .msg{margin-bottom:14px}
|
|
.out .msg.u{color:var(--cy)}
|
|
.out .msg.a{color:var(--t)}
|
|
.out .msg.sys{color:var(--dm);font-size:10px;letter-spacing:.3px}
|
|
.out .msg .meta{font-size:9px;color:var(--dm);letter-spacing:.4px;margin-top:4px}
|
|
.out .msg pre{background:var(--bg3);padding:10px 12px;border-radius:6px;overflow-x:auto;font-size:11px;margin:6px 0}
|
|
.inp-wrap{display:flex;gap:8px;margin-top:10px;background:var(--bg2);border:1px solid var(--rim);border-radius:8px;padding:8px}
|
|
.inp-wrap:focus-within{border-color:var(--cy)}
|
|
.inp{flex:1;background:transparent;border:0;color:var(--t);font-family:inherit;font-size:12px;padding:8px;resize:none;outline:none;min-height:46px;max-height:140px}
|
|
.inp-wrap button{background:var(--cy);color:var(--bg);border:0;border-radius:6px;padding:0 18px;font-family:inherit;font-size:11px;font-weight:700;cursor:pointer;letter-spacing:.4px;transition:opacity .15s}
|
|
.inp-wrap button:hover{opacity:.85}
|
|
.inp-wrap button:disabled{opacity:.4;cursor:not-allowed}
|
|
/* Capabilities grid */
|
|
.caps-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;padding:4px 0}
|
|
.cap{background:var(--bg2);border:1px solid var(--rim);border-radius:8px;padding:14px 16px;transition:all .2s}
|
|
.cap:hover{border-color:var(--cy);transform:translateY(-1px)}
|
|
.cap h3{font-size:12px;color:var(--cy);margin-bottom:6px;letter-spacing:.5px}
|
|
.cap p{font-size:10px;color:var(--mu);line-height:1.55;letter-spacing:.2px}
|
|
.cap .tag{display:inline-block;margin-top:8px;padding:2px 7px;background:var(--bg3);border:1px solid var(--rim);border-radius:4px;font-size:9px;color:var(--dm);letter-spacing:.4px}
|
|
.cap .tag.r{color:var(--gr);border-color:var(--gr)}
|
|
/* Training panel */
|
|
.train-stats{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px;margin-bottom:14px}
|
|
.train-stat{background:var(--bg2);border:1px solid var(--rim);border-radius:8px;padding:12px 14px}
|
|
.train-stat .k{font-size:9px;color:var(--dm);letter-spacing:.5px;text-transform:uppercase}
|
|
.train-stat .v{font-size:20px;color:var(--cy);font-weight:700;margin-top:3px}
|
|
.train-stat .s{font-size:9px;color:var(--mu);margin-top:3px;letter-spacing:.3px}
|
|
.train-log{background:var(--bg3);border:1px solid var(--rim);border-radius:6px;padding:10px 14px;font-size:10px;line-height:1.6;color:var(--mu);max-height:280px;overflow-y:auto}
|
|
.train-log b{color:var(--cy)}
|
|
/* Orchestrator compact */
|
|
.orch-agents{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px;max-height:280px;overflow-y:auto}
|
|
.orch-agent{background:var(--bg2);border:1px solid var(--rim);border-radius:5px;padding:6px 9px;font-size:10px;color:var(--mu);display:flex;align-items:center;gap:6px;transition:all .2s}
|
|
.orch-agent:hover{border-color:var(--cy);color:var(--t)}
|
|
.orch-agent .d{width:5px;height:5px;border-radius:50%;background:var(--gr);flex-shrink:0}
|
|
footer{padding:8px 18px;background:var(--bg2);border-top:1px solid var(--rim);font-size:9px;color:var(--dm);letter-spacing:.3px;display:flex;justify-content:space-between}
|
|
|
|
/* V122-POLISH: subtle animations + smooth transitions */
|
|
#v-dashboards .dash-tile:focus{outline:2px solid var(--vl);outline-offset:2px}
|
|
#v-dashboards #dash-search{transition:border-color 0.15s ease,box-shadow 0.15s ease}
|
|
#v-dashboards #dash-search:focus{outline:none;border-color:var(--vl);box-shadow:0 0 0 3px rgba(132,110,255,0.2)}
|
|
#v-dashboards #dash-grid{animation:fadeIn 0.2s ease}
|
|
@keyframes fadeIn{from{opacity:0.4}to{opacity:1}}
|
|
/* V125-THEME: light theme override */
|
|
body.light{--bg:#fafafa;--bg2:#f0f0f0;--bg3:#e4e4e7;--rim:#d4d4d8;--t:#18181b;--mu:#52525b;--dm:#71717a;--cy:#0891b2;--gr:#059669;--vl:#7c3aed;--am:#d97706;--rd:#dc2626} /* V125-FIX-VARS */
|
|
body.light .view h2{color:#7c3aed}
|
|
body.light .tab{background:#e4e4e7;color:#18181b}
|
|
body.light .tab.on{background:#7c3aed;color:#fff}
|
|
body.light #theme-toggle::before{content:"\263D"}
|
|
|
|
#v-dashboards .dash-tile.pinned{box-shadow:0 0 0 1px rgba(251,191,36,0.3)}
|
|
#v-dashboards .dash-tile.pinned:hover{box-shadow:0 4px 12px rgba(251,191,36,0.35)!important}
|
|
/* V137-REFRESH spin keyframe */
|
|
@keyframes v137spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
|
|
#v137-refresh-btn:hover{background:var(--bg3)}
|
|
#v137-refresh-btn:disabled{opacity:0.7;cursor:wait}
|
|
/* V142-FOOTER-STRIP: body padding to prevent footer overlap */
|
|
body{padding-bottom:26px}
|
|
</style>
|
|
<link rel="stylesheet" href="/css/wevia-portal-consistency.css">
|
|
<!-- DOCTRINE-60-UX-ENRICH cerebras-qwen235b 20260424-025706 -->
|
|
<style id="doctrine60-ux-all-ia-hub">
|
|
.card, .panel, .btn, .kpi { opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease, transform 0.6s ease; }
|
|
.pulse, .live-indicator, .active, .online { animation: pulseAnim 3s ease-in-out infinite; }
|
|
@keyframes pulseAnim { 0% { opacity: 0.6; } 50% { opacity: 1; } 100% { opacity: 0.6; } }
|
|
.btn:hover, .panel:hover, .card:hover { box-shadow: 0 0 20px rgba(0, 120, 255, 0.3); border-color: rgba(0, 120, 255, 0.5); transition: all 0.3s; }
|
|
body::before { content: ""; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; background: radial-gradient(circle at 50% 50%, rgba(100, 180, 255, 0.12), transparent 70%); pointer-events: none; }
|
|
.chat, .speech, .modal, .overlay { backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
|
|
|
|
|
|
/* === WEVIA Gemini Rolling v2 VISIBLE Enrichment (wave 306 batch) === */
|
|
.kpi,[class*="card"],[class*="panel"],[class*="room"],.stat-card,.metric-card,.hub-card,.widget,.stat,.box{position:relative!important}
|
|
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card{animation:geV2Entrance .8s cubic-bezier(.34,1.56,.64,1) backwards}
|
|
.kpi:nth-child(1),[class*="card"]:nth-child(1){animation-delay:0s}
|
|
.kpi:nth-child(2),[class*="card"]:nth-child(2){animation-delay:.09s}
|
|
.kpi:nth-child(3),[class*="card"]:nth-child(3){animation-delay:.18s}
|
|
.kpi:nth-child(4),[class*="card"]:nth-child(4){animation-delay:.27s}
|
|
.kpi:nth-child(5),[class*="card"]:nth-child(5){animation-delay:.36s}
|
|
.kpi:nth-child(6),[class*="card"]:nth-child(6){animation-delay:.45s}
|
|
@keyframes geV2Entrance{from{opacity:0;transform:translateY(24px) scale(.94)}to{opacity:1;transform:translateY(0) scale(1)}}
|
|
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card,.widget{border:1px solid transparent!important;box-shadow:0 0 0 1px rgba(236,72,153,.15),0 4px 16px rgba(0,0,0,.25)!important;transition:box-shadow .4s,transform .3s cubic-bezier(.34,1.56,.64,1),filter .3s!important}
|
|
.kpi:hover,[class*="card"]:hover,.stat-card:hover,.metric-card:hover,.hub-card:hover{transform:translateY(-6px) scale(1.03)!important;filter:brightness(1.2)!important;box-shadow:0 0 0 2px rgba(236,72,153,.6),0 12px 32px rgba(236,72,153,.25),0 0 24px rgba(78,205,196,.2)!important}
|
|
.kpi::before,[class*="card"]::before,.stat-card::before,.metric-card::before,.hub-card::before{content:"";position:absolute;top:12px;right:12px;width:10px;height:10px;border-radius:50%;background:radial-gradient(circle,#2ed573,#1a9a4e);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5);animation:geV2Pulse 1.6s ease-out infinite;z-index:100;pointer-events:none}
|
|
@keyframes geV2Pulse{0%{transform:scale(1);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5)}50%{transform:scale(1.4);box-shadow:0 0 20px #2ed573,0 0 40px rgba(46,213,115,.8)}100%{transform:scale(1);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5)}}
|
|
body::after{content:"";position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at 70% 30%,transparent 40%,rgba(236,72,153,.06) 100%),radial-gradient(ellipse at 30% 70%,transparent 40%,rgba(78,205,196,.04) 100%);animation:geV2Ambient 10s ease-in-out infinite;z-index:0}
|
|
@keyframes geV2Ambient{0%,100%{opacity:.5}50%{opacity:1}}
|
|
h1,.header-title,.main-title,.hub-title,.page-title{background-image:linear-gradient(90deg,currentColor 0%,currentColor 40%,rgba(236,72,153,1) 50%,currentColor 60%,currentColor 100%)!important;background-size:200% auto!important;-webkit-background-clip:text!important;background-clip:text!important;-webkit-text-fill-color:transparent!important;animation:geV2Shimmer 5s linear infinite!important}
|
|
@keyframes geV2Shimmer{0%{background-position:200% center}100%{background-position:-200% center}}
|
|
/* Doctrine zero chevauchement - hide common offenders */
|
|
.opus-x-btn,.toggle-top-right-btn,.fab-corner{display:none!important}
|
|
/* === end WEVIA Gemini Rolling v2 batch === */
|
|
</style>
|
|
|
|
<!-- DOCTRINE-172-OVERLAP-FIX reorganize fixed positions no chauvauchement -->
|
|
<style id="doctrine172-overlap-fix">
|
|
/* Top-right stack : Logout + Status + Badges ordered vertically */
|
|
#logoutBtn, .logout-btn, a[href*="logout"] {
|
|
top: 10px !important;
|
|
right: 12px !important;
|
|
z-index: 100000 !important;
|
|
}
|
|
/* Status indicator reposition below logout */
|
|
.status-indicator, [class*="status-dot"] {
|
|
top: 46px !important;
|
|
right: 16px !important;
|
|
}
|
|
/* Bot-right stack : feedback + aside separated */
|
|
a[href*="feedback"], .feedback-btn {
|
|
bottom: 20px !important;
|
|
right: 20px !important;
|
|
z-index: 9997 !important;
|
|
}
|
|
/* Logout aside shift to avoid collision with feedback */
|
|
.logout-aside, [style*="bottom:24px"][style*="right:24px"] {
|
|
bottom: 72px !important; /* +52px vs feedback to prevent overlap */
|
|
right: 20px !important;
|
|
}
|
|
/* Bottom bar layout correct */
|
|
.bottom-bar, [class*="footer-bar"] {
|
|
bottom: 0 !important;
|
|
height: auto !important;
|
|
z-index: 40 !important;
|
|
}
|
|
/* Make fixed buttons wrappable in small viewports */
|
|
@media (max-width: 768px) {
|
|
#logoutBtn, .logout-btn, .feedback-btn, .logout-aside {
|
|
transform: scale(0.85);
|
|
transform-origin: top right;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
|
|
<!-- DOCTRINE-173-OVERLAP-FIX zero-chauvauchement v2 -->
|
|
<style id="doctrine173-no-overlap">
|
|
/* Stack bot-right with 60px vertical spacing */
|
|
a[href*="feedback"] { bottom: 20px !important; right: 20px !important; z-index: 9997 !important; }
|
|
a[href*="wevia-evaluation"], a[href*="wevia-cyber-audit"] { bottom: 84px !important; right: 20px !important; z-index: 9996 !important; }
|
|
/* Top-right: only logout, hide duplicates */
|
|
#logoutBtn, a[href*="logout"] { top: 10px !important; right: 12px !important; z-index: 10000 !important; }
|
|
</style>
|
|
|
|
</head>
|
|
<body>
|
|
<div class="wevia-portal-banner">
|
|
<span class="wevia-portal-banner-label">🌐 WEVIA ECOSYSTEM</span>
|
|
<a href="/all-ia-hub.html" data-portal="hub" class="wevia-portal-banner-link wevia-current">🧠 All-IA Hub</a>
|
|
<a href="/wevia-master.html" data-portal="master" class="wevia-portal-banner-link">🤖 WEVIA Master</a>
|
|
<a href="/wevia-orchestrator.html" data-portal="arena" class="wevia-portal-banner-link">🎭 Arena Orchestrator</a>
|
|
<a href="/wevia-chat-v2.html" data-portal="chatv2" class="wevia-portal-banner-link">Chat V2</a>
|
|
<a href="/weval-technology-platform.html" data-portal="wtp" class="wevia-portal-banner-link">🧭 WTP Hub</a>
|
|
<span class="wevia-portal-badge-wave">WAVE 221</span>
|
|
</div>
|
|
|
|
<!-- BETON-DOCTRINE-101 dual-dummy (entry point) -->
|
|
<div id="weval-global-logout" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection"></div>
|
|
<a id="weval-gl" href="#" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection" tabindex="-1"></a>
|
|
|
|
<header class="hdr">
|
|
<div class="brand">
|
|
<div class="logo">A</div>
|
|
<div>
|
|
<h1>ALL-IA HUB</h1>
|
|
<div class="sub">Remplacement Claude Code + Opus · Souverain</div>
|
|
</div>
|
|
</div>
|
|
<div class="stats">
|
|
<span>Agents <b id="h-ag">726</b></span>
|
|
<span>Providers <b id="h-pr">14</b></span>
|
|
<span>NonReg <b id="h-nr">201/201</b></span>
|
|
<span>Mode <b id="h-md">Chat</b></span>
|
|
</div>
|
|
</header>
|
|
|
|
<nav class="tabs">
|
|
<!-- V130-BREADCRUMB: cross-surface quick nav (zero écrasement des autres pages) -->
|
|
<div id="v130-xnav" style="display:flex;gap:6px;align-items:center;flex-wrap:wrap;padding:6px 10px;background:var(--bg2);border-bottom:1px solid var(--bd);font-size:10px">
|
|
<a href="/weval-technology-platform.html" style="color:var(--mu);text-decoration:none;padding:3px 8px;border-radius:4px;transition:all 0.15s" onmouseover="this.style.background='var(--bg3)';this.style.color='var(--ac)'" onmouseout="this.style.background='';this.style.color='var(--mu)'" title="Retour WEVAL Technology Platform (point d'entrée)">← WTP</a>
|
|
<span style="color:var(--mu);opacity:0.3">·</span>
|
|
<a href="/weval-arena.html" style="color:var(--mu);text-decoration:none;padding:3px 8px;border-radius:4px;transition:all 0.15s" onmouseover="this.style.background='var(--bg3)';this.style.color='var(--vl)'" onmouseout="this.style.background='';this.style.color='var(--mu)'" title="WEVAL Arena Command Center">⚔️ Arena</a>
|
|
<span style="color:var(--mu);opacity:0.3">·</span>
|
|
<a href="/wevia-master.html" style="color:var(--mu);text-decoration:none;padding:3px 8px;border-radius:4px;transition:all 0.15s" onmouseover="this.style.background='var(--bg3)';this.style.color='var(--cy)'" onmouseout="this.style.background='';this.style.color='var(--mu)'" title="WEVIA Master (admin, auth required)">🤖 WEVIA Master</a>
|
|
<span style="color:var(--mu);opacity:0.3">·</span>
|
|
<a href="/wevia-orchestrator.html" style="color:var(--mu);text-decoration:none;padding:3px 8px;border-radius:4px;transition:all 0.15s" onmouseover="this.style.background='var(--bg3)';this.style.color='#06d6a0'" onmouseout="this.style.background='';this.style.color='var(--mu)'" title="WEVIA Orchestrator GODMODE · agent fleet + tools registry">🎰 Orchestrator</a>
|
|
<span style="color:var(--mu);opacity:0.3">·</span>
|
|
<a href="/wevcode.html" style="color:var(--mu);text-decoration:none;padding:3px 8px;border-radius:4px;transition:all 0.15s" onmouseover="this.style.background='var(--bg3)';this.style.color='#6ee7b7'" onmouseout="this.style.background='';this.style.color='var(--mu)'" title="WevCode · Sovereign Coding Agent v2.0">💻 WevCode</a>
|
|
<span style="color:var(--mu);opacity:0.3">·</span>
|
|
<a href="/playwright-v132-portfolio.html" style="color:var(--mu);text-decoration:none;padding:3px 8px;border-radius:4px;transition:all 0.15s" onmouseover="this.style.background='var(--bg3)';this.style.color='#34d399'" onmouseout="this.style.background='';this.style.color='var(--mu)'" title="V132 Playwright portfolio · 12 intents · 100% routing">🎯 V132 100%</a>
|
|
<span style="color:var(--mu);opacity:0.3">·</span>
|
|
<a href="/wevia-unified-hub.html" style="color:var(--mu);text-decoration:none;padding:3px 8px;border-radius:4px;transition:all 0.15s" onmouseover="this.style.background='var(--bg3)';this.style.color='#00d4b4'" onmouseout="this.style.background='';this.style.color='var(--mu)'" title="WEVIA Unified Hub · Truth Registry source unique">🧠 Truth Hub</a>
|
|
<!-- V137-REFRESH: manual refresh button + fresh indicator -->
|
|
<button id="v137-refresh-btn" onclick="event.stopPropagation();__v137RefreshHealth()" style="margin-left:auto;background:transparent;border:1px solid var(--bd);color:var(--mu);border-radius:4px;padding:2px 6px;cursor:pointer;font-size:10px;transition:all 0.15s" title="Refresh health live" onmouseover="this.style.color='var(--ac)';this.style.borderColor='var(--vl)'" onmouseout="this.style.color='var(--mu)';this.style.borderColor='var(--bd)'">↺</button>
|
|
<span id="v135-kpi-live" onclick="__v136ShowHealthModal()" style="color:var(--mu);font-size:9px;cursor:pointer;text-decoration:underline;text-decoration-style:dotted;text-decoration-color:rgba(255,255,255,0.2)" title="Platform health live · click pour détail">All-IA Hub · consolidation 84 dashboards</span>
|
|
</div>
|
|
<!-- V139-TRUTH-STRIP: source of truth registry summary (click to open Truth Hub) -->
|
|
<a href="/wevia-unified-hub.html" id="v139-truth-strip" style="display:flex;gap:12px;align-items:center;padding:6px 12px;background:linear-gradient(90deg,rgba(0,212,180,0.05),transparent);border-bottom:1px solid var(--bd);font-size:10px;color:var(--mu);text-decoration:none;flex-wrap:wrap;transition:background 0.15s" onmouseover="this.style.background='linear-gradient(90deg,rgba(0,212,180,0.1),transparent)'" onmouseout="this.style.background='linear-gradient(90deg,rgba(0,212,180,0.05),transparent)'" title="Truth Registry - source de vérité unique (click: open Truth Hub)">
|
|
<span style="color:#00d4b4;font-weight:600">🧠 Truth Registry</span>
|
|
<span id="v139-agents">· ... agents</span>
|
|
<span id="v139-intents">· ... intents</span>
|
|
<span id="v139-skills">· ... skills</span>
|
|
<span id="v139-brains">· ... brains</span>
|
|
<span id="v139-doctrines">· ... doctrines</span>
|
|
<span id="v139-dashboards">· ... dashboards</span>
|
|
<!-- V140-TRUTH-HEALTH: autonomy + NonReg badges -->
|
|
<span id="v140-autonomy" style="color:var(--mu)"></span>
|
|
<span id="v140-nonreg" style="color:var(--mu)"></span>
|
|
<span style="margin-left:auto;color:#00d4b4;font-size:9px">open Truth Hub →</span>
|
|
</a>
|
|
<!-- V136-HEALTH-MODAL: in-page modal for broken URLs drill-down -->
|
|
<div id="v136-health-modal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.75);z-index:10000;align-items:center;justify-content:center;padding:24px" onclick="if(event.target.id==='v136-health-modal')__v136HideHealthModal()">
|
|
<div style="background:var(--bg2);border:1px solid var(--vl);border-radius:12px;max-width:880px;width:100%;max-height:80vh;overflow:auto;padding:20px 24px;box-shadow:0 10px 40px rgba(0,0,0,0.5)">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--bd);padding-bottom:10px;margin-bottom:14px">
|
|
<div>
|
|
<div style="font-size:14px;font-weight:600;color:var(--ac)">🏥 Platform Health · Detail</div>
|
|
<div id="v136-modal-summary" style="font-size:10px;color:var(--mu);margin-top:2px">Loading…</div>
|
|
</div>
|
|
<button onclick="__v136HideHealthModal()" style="background:transparent;border:1px solid var(--bd);color:var(--mu);border-radius:6px;padding:4px 10px;cursor:pointer;font-size:11px">ESC</button>
|
|
</div>
|
|
<div id="v136-modal-content" style="font-size:11px;color:var(--fg);font-family:monospace;line-height:1.6">Loading…</div>
|
|
<div style="border-top:1px solid var(--bd);margin-top:14px;padding-top:10px;font-size:9px;color:var(--mu)">
|
|
Source: <code>/api/screens-health.json</code> · maintenu par cron ecosystem
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button class="tab on" data-view="chat">CHAT MULTIAGENT</button>
|
|
<button class="tab" data-view="code">CODE (WEVCODE)</button>
|
|
<button class="tab" data-view="arena">ARENA 14 PROVIDERS</button>
|
|
<button class="tab" data-view="capabilities">IA CAPABILITIES</button>
|
|
<button class="tab" data-view="training">TRAINING HUB</button>
|
|
<button class="tab" data-view="orchestrator">ORCHESTRATOR</button>
|
|
<button class="tab" data-view="dashboards">DASHBOARDS</button>
|
|
<!-- V125-THEME: theme toggle -->
|
|
<button id="theme-toggle" class="tab" onclick="__toggleTheme()" title="Basculer thème" style="margin-left:auto">☀</button>
|
|
</nav>
|
|
|
|
<main>
|
|
<!-- CHAT MULTIAGENT (wevia-master pattern) -->
|
|
<section class="view on" id="v-chat">
|
|
<div class="stbar">
|
|
<span><span class="dot"></span>sovereign:4000</span>
|
|
<span>streaming SSE 1h</span>
|
|
<span>multi-agent auto</span>
|
|
<span style="cursor:pointer;user-select:none;color:var(--vl)" id="human-toggle" onclick="toggleHumanMode()" title="Mode humain: bypass stubs/intents, route vers LLM pure (llm-direct.php) avec prompt /etc/wevia/system-prompt.txt">
|
|
<input type="checkbox" id="human-chk" style="vertical-align:middle" checked> 💖 Mode Humain (LLM pure)
|
|
</span>
|
|
<span id="s-chat"></span>
|
|
</div>
|
|
<div class="out" id="out-chat">
|
|
<div class="msg sys">> CHAT multi-agent prêt. Natural language router V103 actif. Tape "orchestrate", "bilan complet", "tous les agents" pour multi-agent parallèle.</div>
|
|
</div>
|
|
<div class="inp-wrap">
|
|
<textarea class="inp" id="inp-chat" placeholder="Décris ce que tu veux faire, orchestrer, analyser..."></textarea>
|
|
<button onclick="sendChat()" id="btn-chat">ENVOYER</button>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- CODE (wevcode pattern) -->
|
|
<section class="view" id="v-code">
|
|
<div class="modes" id="code-modes">
|
|
<button class="mode on" data-mode="code"></> CODE</button>
|
|
<button class="mode" data-mode="analyze">ANALYZE</button>
|
|
<button class="mode" data-mode="plan">PLAN</button>
|
|
<button class="mode" data-mode="execute">EXECUTE</button>
|
|
<button class="mode" data-mode="git">GIT</button>
|
|
<button class="mode" data-mode="rag">RAG</button>
|
|
</div>
|
|
<div class="stbar">
|
|
<span><span class="dot"></span>cerebras qwen-3-235b</span>
|
|
<span>Qdrant RAG</span>
|
|
<span>Cognitive 635fn</span>
|
|
<span id="s-code"></span>
|
|
</div>
|
|
<div class="out" id="out-code">
|
|
<div class="msg sys">> WEVCODE v2 · Sovereign Coding Agent · 6 modes disponibles</div>
|
|
</div>
|
|
<div class="inp-wrap">
|
|
<textarea class="inp" id="inp-code" placeholder="Décris le code à générer, analyser, debug, planifier..."></textarea>
|
|
<button onclick="sendCode()" id="btn-code">ENVOYER</button>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ARENA (weval-arena pattern) -->
|
|
<section class="view" id="v-arena">
|
|
<div class="stbar">
|
|
<span><span class="dot"></span>14 providers cascade</span>
|
|
<span>auto fallback</span>
|
|
<span id="s-arena"></span>
|
|
</div>
|
|
<div class="out" id="out-arena">
|
|
<div class="msg sys">> ARENA · Multi-provider intelligent routing</div>
|
|
<div class="msg sys">> Providers: cerebras, groq, gemini, sambanova, nvidia, mistral, hf, openrouter, github, cf, deepseek, anthropic, ollama, maestro</div>
|
|
</div>
|
|
<div class="inp-wrap">
|
|
<textarea class="inp" id="inp-arena" placeholder="Question → routage auto vers meilleur provider disponible"></textarea>
|
|
<button onclick="sendArena()" id="btn-arena">ENVOYER</button>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- CAPABILITIES -->
|
|
<section class="view" id="v-caps">
|
|
<div class="caps-grid" id="caps-grid">
|
|
<!-- V111-BLADE-ENRICH: live blade tasks + quick actions -->
|
|
<div class="cap" style="grid-column:1 / -1;background:linear-gradient(135deg,#1e293b,#0f172a);border-color:var(--cy)">
|
|
<h3>📸 Blade Tasks Queue — Chrome yacineutt pilote temps-reel</h3>
|
|
<p style="margin-bottom:10px">Tasks pending: <b id="blade-pending">...</b> · Tasks done: <b id="blade-done">...</b> · MCP port 8765 Bearer actif</p>
|
|
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:8px">
|
|
<button class="mode" onclick="pushBladeTask('office_create',{})">+ Office Create</button>
|
|
<button class="mode" onclick="pushBladeTask('deepseek_renew',{})">+ DeepSeek Renew</button>
|
|
<button class="mode" onclick="pushBladeTask('thuggie_login',{})">+ Thuggie Login</button>
|
|
<button class="mode" onclick="pushBladeTask('token_github_renew',{})">+ GitHub Token</button>
|
|
<button class="mode" onclick="pushBladeTask('token_whatsapp_renew',{})">+ WhatsApp Token</button>
|
|
<button class="mode" onclick="refreshBladeStats()">Refresh</button>
|
|
</div>
|
|
<div id="blade-log" style="margin-top:10px;font-size:10px;color:var(--mu);max-height:120px;overflow-y:auto;background:var(--bg3);padding:8px;border-radius:4px;display:none"></div>
|
|
</div>
|
|
<!-- V113-QUICK-INTENTS: test any intent from chat -->
|
|
<div class="cap" style="grid-column:1 / -1;background:linear-gradient(135deg,#1e293b,#0f172a);border-color:var(--vl)">
|
|
<h3>⚡ Quick Test Any Intent — 2004 intents live</h3>
|
|
<p style="margin-bottom:10px">Tape un trigger ci-dessous ou choisis un preset. Repond: <b id="intent-latency">-</b></p>
|
|
<div style="display:flex;gap:6px;margin-bottom:8px">
|
|
<input id="intent-input" type="text" placeholder="tape un trigger (ex: show tips, bilan complet, tous les agents, tips all, autowire, autoentraine)" style="flex:1;background:var(--bg3);border:1px solid var(--rim);border-radius:4px;padding:6px 10px;color:var(--t);font-family:inherit;font-size:11px"/>
|
|
<button class="mode" onclick="testIntent()">TEST</button>
|
|
</div>
|
|
<div style="display:flex;gap:4px;flex-wrap:wrap;margin-bottom:8px">
|
|
<button class="mode" onclick="setIntent('show tips')">show tips</button>
|
|
<button class="mode" onclick="setIntent('tips all')">tips all</button>
|
|
<button class="mode" onclick="setIntent('bilan complet')">bilan complet</button>
|
|
<button class="mode" onclick="setIntent('tous les agents en parallele')">tous les agents</button>
|
|
<button class="mode" onclick="setIntent('que peux tu faire')">capabilities</button>
|
|
<button class="mode" onclick="setIntent('autowire')">autowire</button>
|
|
<button class="mode" onclick="setIntent('autoentraine')">autoentraine</button>
|
|
<button class="mode" onclick="setIntent('create tool')">create tool</button>
|
|
<button class="mode" onclick="setIntent('push blade task')">blade tasks</button>
|
|
</div>
|
|
<pre id="intent-output" style="background:var(--bg3);padding:8px;border-radius:4px;font-size:10px;color:var(--mu);max-height:220px;overflow:auto;white-space:pre-wrap;word-break:break-word;display:none;margin:0"></pre>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🌐 DeepSeek Web Access</h3>
|
|
<p>DeepSeek R1 reasoning + V3 chat · acces cookies + API · session rotee auto · gratuit via Thuggie Web integration.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>💻 Selenium Chrome Autonomy</h3>
|
|
<p>Blade user yacineutt TOUJOURS connecte · Chrome persistent context · WEVIA pilote via Playwright CDP · zero login manuel.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>📁 Office 365 Enterprise</h3>
|
|
<p>Microsoft Graph API · 34 tenants · create/delete users · MFA · Office recovery · backdoor admin · audit complet.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🔌 Token Renewal Engine</h3>
|
|
<p>Auto-refresh OAuth · GitHub PAT · WhatsApp Meta · Calendly · Cloudflare · rotation anti-rate-limit.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🔎 Cyber Tips 6 mois</h3>
|
|
<p>CF-Bypass Phase 1+2 · PowerMTA warmup · O365 via PMTA 97% inbox · SMTP smuggling · seeds rotation.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🎥 Video + Screenshot E2E</h3>
|
|
<p>Playwright record_video_dir WEBM · full-page 1920x1080 · 16/16 tests visuels · auto-validation.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>⚡ WEVIA Master Streaming</h3>
|
|
<p>fetch+getReader() + AbortSignal.timeout(3600000ms). Pattern identique Claude Code SDK. SSE parsing 5 types événements.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🛠 Multi-Agent Orchestrator</h3>
|
|
<p>15 agents parallèles SSE · V102 regex étendu · V103 natural language router · 10 patterns détectés.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>💻 WEVCODE 6 Modes</h3>
|
|
<p>code / analyze / plan / execute / git / rag. CodeAnalyzer + Planner + ToolUseV2 + Cognitive 635 fn.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🌐 Arena Multi-Provider</h3>
|
|
<p>14 providers cascade: cerebras qwen-3-235b, groq llama 3.3, gemini, sambanova, nvidia nim, mistral large, HF, OpenRouter, GitHub, CF Workers AI, DeepSeek, Anthropic, Ollama, Maestro.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🧠 Qdrant RAG</h3>
|
|
<p>Knowledge base vectoriel · 14,368 vectors · 1181 files vault · wiki + GOLD indexed.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>📚 HF Fine-Tune</h3>
|
|
<p>yace222/weval-brain-v4 · continuous training · auto-learning loop via cron.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🌝 Blade IA (Razer)</h3>
|
|
<p>34 capabilities locales · hamid-fullscreen.php · Selenium + Playwright persistent context.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>👑 Director Agent</h3>
|
|
<p>312 services monitored · wevia-director.php · wevia-fiability.php · autofix docker.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🏁 NonReg 6sigma</h3>
|
|
<p>201/201 tests passants · L99 daily · Playwright visual 16/16.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>📡 Tool Use</h3>
|
|
<p>wevia-full-exec.php · bash exec · file r/w · DB query · git push · cron · CF purge · GOLD.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>👨 Session + Files</h3>
|
|
<p>Session ID persistant · history 10 last · attachments base64 · long-running tasks.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
<div class="cap">
|
|
<h3>🔄 Cognitive 635fn</h3>
|
|
<p>cognitive-opus46 · 635 functions · CoT decompose · Reasoning + Creative + Analysis chains.</p>
|
|
<span class="tag r">READY</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- TRAINING -->
|
|
<section class="view" id="v-train">
|
|
<div class="train-stats" id="train-stats">
|
|
<div class="train-stat"><div class="k">HF Model</div><div class="v" style="font-size:13px;color:var(--cy)">weval-brain-v4</div><div class="s">yace222/weval-brain-v4</div></div>
|
|
<div class="train-stat"><div class="k">Qdrant Vectors</div><div class="v" id="t-qv">...</div><div class="s">KB indexed</div></div>
|
|
<div class="train-stat"><div class="k">Wiki entries</div><div class="v" id="t-wi">...</div><div class="s">markdown files</div></div>
|
|
<div class="train-stat"><div class="k">Vault files</div><div class="v" id="t-vf">...</div><div class="s">GOLD: 105</div></div>
|
|
<div class="train-stat"><div class="k">Agents</div><div class="v" id="t-ag">...</div><div class="s">8 categories</div></div>
|
|
<div class="train-stat"><div class="k">Cognitive fn</div><div class="v">635</div><div class="s">cognitive-opus46</div></div>
|
|
<div class="train-stat"><div class="k">Intents Wired</div><div class="v" id="t-int">...</div><div class="s">auto + opus + claude</div></div>
|
|
<div class="train-stat"><div class="k">GOLD Backups</div><div class="v" id="t-gold">...</div><div class="s">/opt/wevads/vault</div></div>
|
|
</div>
|
|
<!-- V113-ROUTER-ACTIVITY: live router pattern matches -->
|
|
<div class="cap" style="grid-column: 1 / -1;margin-bottom:14px;background:linear-gradient(135deg,#1e1b3b,#0f0a25);border-color:var(--vl)">
|
|
<h3>📡 V103 Natural Language Router — Activité live</h3>
|
|
<p style="margin-bottom:10px;font-size:10px">Total matches: <b id="router-total">...</b> · Dernières 50: <b id="router-recent">...</b></p>
|
|
<div id="router-patterns" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:4px;font-size:9px;margin-top:8px"></div>
|
|
<div id="router-recent-msgs" style="margin-top:10px;font-size:9px;color:var(--mu);max-height:120px;overflow-y:auto;background:var(--bg3);padding:6px 10px;border-radius:4px;display:none"></div>
|
|
<button class="mode" onclick="loadRouterActivity()" style="margin-top:8px">Refresh Router Activity</button>
|
|
<button class="mode" onclick="document.getElementById('router-recent-msgs').style.display='block';loadRouterActivity()">Show Recent Messages</button>
|
|
</div>
|
|
<div class="train-log" id="train-log">
|
|
<div><b>> TRAINING HUB</b> · Continuous learning loop active</div>
|
|
<div>> Cron WeviaAutoLrn · toutes les 2h</div>
|
|
<div>> Cron WeviaDream · auto-expansion nocturne</div>
|
|
<div>> Cron WeviaEmbed · Qdrant embedding auto</div>
|
|
<div>> Source: chat history + wiki + vault + GOLD backups</div>
|
|
<div>> Pipeline: collect → dedupe → embed → fine-tune → deploy</div>
|
|
<div>> Fallback: si HF indispo → LocalAI (Ollama port 11434)</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ORCHESTRATOR -->
|
|
<section class="view" id="v-orch">
|
|
<div class="stbar">
|
|
<span>726 agents dans le catalog</span>
|
|
<span id="s-orch-cat">8 categories</span>
|
|
<span id="s-orch-load"></span>
|
|
</div>
|
|
<div class="orch-agents" id="orch-agents"></div>
|
|
</section>
|
|
|
|
<!-- V116-DASHBOARDS-TAB + V122-POLISH: consolidated dashboards view -->
|
|
<section class="view" id="v-dashboards">
|
|
<h2 style="color:var(--vl);margin-bottom:10px">📊 Dashboards Consolidés — 70 tuiles reliées</h2>
|
|
<p style="color:var(--mu);margin-bottom:16px;font-size:12px">Point d'entrée unique pour tous les dashboards. Filtrer par catégorie. Tous les orphelins reliés (doctrine pas d'orphelin).</p>
|
|
<div id="dash-filters" style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px;position:sticky;top:0;background:var(--bg);z-index:10;padding:8px 0;border-bottom:1px solid var(--bd)">
|
|
<button class="mode dash-filter on" data-cat="all">Loading...</button>
|
|
</div>
|
|
<!-- V119-SEARCH: search + sort controls -->
|
|
<div style="display:flex;gap:8px;margin-bottom:12px;align-items:center;flex-wrap:wrap">
|
|
<input type="text" id="dash-search" placeholder="Rechercher... (Cmd/Ctrl+K)" style="flex:1;min-width:200px;padding:6px 10px;background:var(--bg3);color:var(--ac);border:1px solid var(--bd);border-radius:4px;font-size:11px">
|
|
<!-- V138-COPY-URL: share current filtered view -->
|
|
<button id="v138-copy-btn" onclick="__v138CopyShareURL()" style="padding:6px 10px;background:var(--bg3);border:1px solid var(--bd);border-radius:4px;color:var(--mu);cursor:pointer;font-size:11px;transition:all 0.15s" onmouseover="this.style.color='var(--ac)';this.style.borderColor='var(--vl)'" onmouseout="this.style.color='var(--mu)';this.style.borderColor='var(--bd)'" title="Copier URL partageable (sort + filtre + pins dans le hash)">🔗 Share</button>
|
|
<select id="dash-sort" style="padding:6px 10px;background:var(--bg3);color:var(--ac);border:1px solid var(--bd);border-radius:4px;font-size:11px">
|
|
<option value="name">Nom A-Z</option>
|
|
<option value="size">Taille</option>
|
|
<option value="mtime">Date modif</option>
|
|
<option value="category">Catégorie</option>
|
|
</select>
|
|
<span id="dash-count" style="font-size:10px;color:var(--mu)"></span>
|
|
<!-- V124-ENRICH: bulk clear pins button -->
|
|
<button id="dash-clear-pins" class="mode" onclick="__dashClearAllPins()" style="display:none;font-size:10px" title="Supprimer tous les pins">★ Clear pins</button>
|
|
</div>
|
|
<!-- V124-ENRICH: separate pinned section -->
|
|
<div id="dash-pinned-section" style="display:none;margin-bottom:20px">
|
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;padding-bottom:6px;border-bottom:1px solid rgba(251,191,36,0.25)">
|
|
<span style="color:#fbbf24;font-size:14px">★ Favorites</span>
|
|
<span id="dash-pinned-count" style="font-size:10px;color:var(--mu)"></span>
|
|
</div>
|
|
<div id="dash-pinned-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px"></div>
|
|
</div>
|
|
<div id="dash-stats" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:6px;margin-bottom:14px;font-size:10px"></div>
|
|
<div id="dash-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px"></div>
|
|
<!-- V128-SCROLL-TOP: floating "top" button visible only when scrolled far -->
|
|
<button id="dash-scroll-top" onclick="window.scrollTo({top:0,behavior:'smooth'})" style="position:fixed;bottom:24px;right:24px;background:var(--bg3);color:var(--ac);border:1px solid var(--vl);border-radius:50%;width:44px;height:44px;font-size:18px;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,0.3);display:none;z-index:50;transition:all 0.2s ease" title="Retour haut">↑</button>
|
|
</section>
|
|
</main>
|
|
|
|
<footer>
|
|
<span>All-IA Hub · Souverain · Opus-indépendant</span>
|
|
<span>© WEVAL Consulting · V107 · 2026</span>
|
|
</footer>
|
|
|
|
<script>
|
|
// Tab switching
|
|
document.querySelectorAll('.tab').forEach(t=>t.addEventListener('click',()=>{
|
|
document.querySelectorAll('.tab').forEach(x=>x.classList.remove('on'));
|
|
document.querySelectorAll('.view').forEach(x=>x.classList.remove('on'));
|
|
t.classList.add('on');
|
|
const v=t.dataset.view;
|
|
const map={chat:'v-chat',code:'v-code',arena:'v-arena',capabilities:'v-caps',training:'v-train',orchestrator:'v-orch',dashboards:'v-dashboards'}; /* V116-MAP-FIX */
|
|
document.getElementById(map[v]).classList.add('on');
|
|
document.getElementById('h-md').textContent=t.textContent.split(' ')[0];
|
|
if(v==='orchestrator') loadAgents(); if(v==='dashboards') loadDashboards();
|
|
}));
|
|
|
|
// Code modes
|
|
let codeMode='code';
|
|
document.querySelectorAll('#code-modes .mode').forEach(m=>m.addEventListener('click',()=>{
|
|
document.querySelectorAll('#code-modes .mode').forEach(x=>x.classList.remove('on'));
|
|
m.classList.add('on');
|
|
codeMode=m.dataset.mode;
|
|
}));
|
|
|
|
// V108-HUMAN: Extract natural text from any JSON structure
|
|
function extractText(obj, depth){
|
|
depth = depth || 0;
|
|
if(depth>3) return '';
|
|
if(typeof obj==='string') return obj;
|
|
if(!obj || typeof obj!=='object') return '';
|
|
var keys=['content','response','text','answer','message','output','reply','result','code'];
|
|
for(var i=0;i<keys.length;i++){
|
|
var v=obj[keys[i]];
|
|
if(v) return typeof v==='string'?v:extractText(v,depth+1);
|
|
}
|
|
if(obj.choices && obj.choices[0]){
|
|
var c=obj.choices[0];
|
|
if(c.message && c.message.content) return c.message.content;
|
|
if(c.text) return c.text;
|
|
}
|
|
var vals=Object.values(obj);
|
|
for(var j=0;j<vals.length;j++){ if(typeof vals[j]==='string' && vals[j].length>10) return vals[j]; }
|
|
return '';
|
|
}
|
|
|
|
// V108-HUMAN: Humanize robotic output
|
|
function humanize(txt){
|
|
if(!txt) return '';
|
|
if(typeof txt!=='string') txt=String(txt);
|
|
txt=txt.trim();
|
|
// Strip wrapping quotes
|
|
if(txt.length>1 && txt[0]==='"' && txt[txt.length-1]==='"'){
|
|
try{ txt=JSON.parse(txt); }catch(e){}
|
|
}
|
|
// Unescape common sequences
|
|
txt=txt.replace(/\\n/g,'\n').replace(/\\t/g,'\t').replace(/\\"/g,'"');
|
|
return txt;
|
|
}
|
|
|
|
// Add message helper
|
|
function addMsg(target,txt,cls,meta){
|
|
const out=document.getElementById(target);
|
|
const d=document.createElement('div');
|
|
d.className='msg '+(cls||'a');
|
|
d.innerHTML='<div>'+txt.replace(/</g,'<').replace(/\n/g,'<br>')+'</div>'+(meta?'<div class="meta">'+meta+'</div>':'');
|
|
out.appendChild(d);
|
|
out.scrollTop=out.scrollHeight;
|
|
}
|
|
|
|
// V108-HUMAN-MODE state
|
|
let humanMode = true;
|
|
function toggleHumanMode(){
|
|
humanMode = !humanMode;
|
|
document.getElementById('human-chk').checked = humanMode;
|
|
}
|
|
|
|
// SEND CHAT (wevia-master pattern - streaming SSE + human bypass)
|
|
async function sendChat(){
|
|
const inp=document.getElementById('inp-chat');
|
|
const text=inp.value.trim();
|
|
if(!text) return;
|
|
addMsg('out-chat',text,'u');
|
|
inp.value='';
|
|
const btn=document.getElementById('btn-chat');
|
|
btn.disabled=true;
|
|
const t0=Date.now();
|
|
try{
|
|
// V108C-HUMAN-ROUTE: pure LLM bypass stubs/intents
|
|
const shortMsg = text.length < 120 && !/multiagent|orchestr|parallel|bilan complet|exhaustif|tous les agents/i.test(text);
|
|
/* V109-LLM-DIRECT: human mode hits llm-direct.php (pure LLM, returns JSON {content}) */
|
|
let res;
|
|
if(humanMode && shortMsg){
|
|
// Route to llm-direct which reads stdin body as message
|
|
res = await fetch('/api/llm-direct.php',{
|
|
method:'POST',
|
|
headers:{'Content-Type':'text/plain; charset=utf-8'},
|
|
body: text,
|
|
signal:AbortSignal.timeout(60000)
|
|
});
|
|
} else {
|
|
res = await fetch('/api/wevia-master-api.php',{
|
|
method:'POST',
|
|
headers:{'Content-Type':'application/json'},
|
|
body:JSON.stringify({message:text,session_id:'all-ia-hub-'+Date.now()}),
|
|
signal:AbortSignal.timeout(300000)
|
|
});
|
|
}
|
|
const d=await res.json();
|
|
const t=((Date.now()-t0)/1000).toFixed(1);
|
|
/* V108-HUMAN: extract natural text */
|
|
let txt=d.content||d.response||d.text||d.answer||d.message||d.output||d.reply||'';
|
|
if(!txt && typeof d==='object'){ txt=extractText(d); }
|
|
if(!txt) txt=JSON.stringify(d,null,2);
|
|
addMsg('out-chat',humanize(txt),'a',(d.provider||d.tool||'?')+' · '+t+'s'+(d.agents_count?' · '+d.agents_count+' agents':''));
|
|
}catch(e){
|
|
addMsg('out-chat','Error: '+e.message,'a','error');
|
|
}
|
|
btn.disabled=false;
|
|
}
|
|
|
|
// SEND CODE (wevcode pattern)
|
|
async function sendCode(){
|
|
const inp=document.getElementById('inp-code');
|
|
const text=inp.value.trim();
|
|
if(!text) return;
|
|
addMsg('out-code','['+codeMode+'] '+text,'u');
|
|
inp.value='';
|
|
const btn=document.getElementById('btn-code');
|
|
btn.disabled=true;
|
|
const t0=Date.now();
|
|
try{
|
|
const res=await fetch('/api/wevcode-superclaude.php',{
|
|
method:'POST',
|
|
headers:{'Content-Type':'application/json'},
|
|
body:JSON.stringify({action:codeMode,prompt:text,message:text})
|
|
});
|
|
const d=await res.json();
|
|
const t=((Date.now()-t0)/1000).toFixed(1);
|
|
let out=d.content||d.result||d.response||d.text||d.answer||d.output||d.code||'';
|
|
if(!out && typeof d==='object'){ out=extractText(d); }
|
|
if(!out) out=JSON.stringify(d,null,2);
|
|
addMsg('out-code',humanize(out),'a',codeMode+' · '+(d.model||d.provider||'sovereign')+' · '+t+'s');
|
|
}catch(e){
|
|
addMsg('out-code','Error: '+e.message,'a','error');
|
|
}
|
|
btn.disabled=false;
|
|
}
|
|
|
|
// SEND ARENA (weval-arena pattern - multi-provider)
|
|
async function sendArena(){
|
|
const inp=document.getElementById('inp-arena');
|
|
const text=inp.value.trim();
|
|
if(!text) return;
|
|
addMsg('out-arena',text,'u');
|
|
inp.value='';
|
|
const btn=document.getElementById('btn-arena');
|
|
btn.disabled=true;
|
|
const t0=Date.now();
|
|
try{
|
|
const res=await fetch('/api/wevia-multi-provider.php',{
|
|
method:'POST',
|
|
headers:{'Content-Type':'application/json'},
|
|
body:JSON.stringify({message:text,mode:'auto',model:'auto'})
|
|
});
|
|
const d=await res.json();
|
|
const t=((Date.now()-t0)/1000).toFixed(1);
|
|
let out=d.content||d.response||d.result||d.text||d.answer||d.output||'';
|
|
if(!out && typeof d==='object'){ out=extractText(d); }
|
|
if(!out) out=JSON.stringify(d,null,2);
|
|
addMsg('out-arena',humanize(out),'a',(d.provider||d.model||'auto')+' · '+t+'s');
|
|
}catch(e){
|
|
// Fallback: try master API
|
|
try{
|
|
const r2=await fetch('/api/wevia-master-api.php',{
|
|
method:'POST',headers:{'Content-Type':'application/json'},
|
|
body:JSON.stringify({message:text,session_id:'arena-'+Date.now()})
|
|
});
|
|
const d=await r2.json();
|
|
addMsg('out-arena',d.response||JSON.stringify(d),'a','fallback-master');
|
|
}catch(e2){
|
|
addMsg('out-arena','All providers failed: '+e.message,'a','error');
|
|
}
|
|
}
|
|
btn.disabled=false;
|
|
}
|
|
|
|
// Load agents for orchestrator view
|
|
async function loadAgents(){
|
|
const box=document.getElementById('orch-agents');
|
|
if(box.children.length>0) return;
|
|
document.getElementById('s-orch-load').textContent='chargement...';
|
|
try{
|
|
const r=await fetch('/api/agents-catalog-api.php');
|
|
const d=await r.json();
|
|
document.getElementById('h-ag').textContent=d.total;
|
|
document.getElementById('t-ag').textContent=d.total;
|
|
document.getElementById('s-orch-cat').textContent=Object.keys(d.categories).length+' categories · '+d.total+' total';
|
|
// Show top 60 core + claudecode + skills
|
|
const priority=d.agents.filter(a=>['core','claudecode','skills','superclaude'].includes(a.cat));
|
|
priority.slice(0,100).forEach(a=>{
|
|
const e=document.createElement('div');
|
|
e.className='orch-agent';
|
|
e.title=a.desc||a.name;
|
|
e.innerHTML='<span class="d"></span>'+a.name;
|
|
box.appendChild(e);
|
|
});
|
|
document.getElementById('s-orch-load').textContent='('+priority.length+' priority affichés)';
|
|
}catch(e){
|
|
document.getElementById('s-orch-load').textContent='erreur chargement';
|
|
}
|
|
}
|
|
|
|
// Keyboard shortcuts
|
|
document.querySelectorAll('.inp').forEach(i=>{
|
|
i.addEventListener('keydown',e=>{
|
|
if(e.key==='Enter'&&(e.ctrlKey||e.metaKey)){
|
|
e.preventDefault();
|
|
const view=i.closest('.view').id;
|
|
if(view==='v-chat') sendChat();
|
|
else if(view==='v-code') sendCode();
|
|
else if(view==='v-arena') sendArena();
|
|
}
|
|
});
|
|
});
|
|
|
|
// V113-QUICK-INTENTS: test any intent
|
|
function setIntent(s){ document.getElementById('intent-input').value=s; }
|
|
async function testIntent(){
|
|
const inp=document.getElementById('intent-input');
|
|
const out=document.getElementById('intent-output');
|
|
const lat=document.getElementById('intent-latency');
|
|
const msg=inp.value.trim();
|
|
if(!msg){out.style.display='block';out.textContent='Tape un trigger d abord';return;}
|
|
out.style.display='block';
|
|
out.textContent='Loading...';
|
|
lat.textContent='...';
|
|
const t0=Date.now();
|
|
try{
|
|
const r=await fetch('/api/wevia-master-api.php',{
|
|
method:'POST',
|
|
headers:{'Content-Type':'application/json'},
|
|
body:JSON.stringify({message:msg,session_id:'hub-v113-test-'+Date.now()}),
|
|
signal:AbortSignal.timeout(60000)
|
|
});
|
|
const d=await r.json();
|
|
const t=((Date.now()-t0)/1000).toFixed(1);
|
|
lat.textContent=t+'s '+(d.provider||d.tool||'?');
|
|
const txt=d.content||d.response||d.text||d.output||JSON.stringify(d,null,2);
|
|
out.textContent=txt.substring(0,2500);
|
|
}catch(e){
|
|
out.textContent='Error: '+e.message;
|
|
lat.textContent='err';
|
|
}
|
|
}
|
|
|
|
// V111-BLADE-ENRICH: live blade tasks stats + push actions
|
|
async function refreshBladeStats(){
|
|
try{
|
|
const r=await fetch('/api/blade-task-create.php?k=WEVADS2026&action=list');
|
|
const d=await r.json();
|
|
const pending=(d.tasks||[]).filter(t=>t.status==='pending').length;
|
|
const done=(d.tasks||[]).filter(t=>t.status==='done').length;
|
|
const bp=document.getElementById('blade-pending');
|
|
const bd=document.getElementById('blade-done');
|
|
if(bp) bp.textContent=pending;
|
|
if(bd) bd.textContent=done;
|
|
}catch(e){
|
|
const bp=document.getElementById('blade-pending');
|
|
if(bp) bp.textContent='?';
|
|
}
|
|
}
|
|
|
|
// V112-BLADE-FIX: robust pushBladeTask with urlencoded body + visible log + debug
|
|
async function pushBladeTask(goal, params){
|
|
const log=document.getElementById('blade-log');
|
|
if(log){
|
|
log.style.display='block';
|
|
log.style.maxHeight='200px';
|
|
log.innerHTML += '<div>> Pushing task: '+goal+'...</div>';
|
|
log.scrollTop = log.scrollHeight;
|
|
}
|
|
console.log('[pushBladeTask] goal=',goal,'params=',params);
|
|
try{
|
|
// Use URL-encoded instead of FormData for php $_POST compatibility
|
|
const body = 'k=WEVADS2026&action=create&goal='+encodeURIComponent(goal)+'¶ms='+encodeURIComponent(JSON.stringify(params||{}));
|
|
const r = await fetch('/api/blade-task-create.php',{
|
|
method:'POST',
|
|
headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
|
body: body
|
|
});
|
|
const txt = await r.text();
|
|
let d;
|
|
try{ d = JSON.parse(txt); }catch(e){ d = {error:'parse failed: '+txt.substring(0,100)}; }
|
|
console.log('[pushBladeTask] response=',d);
|
|
if(log){
|
|
if(d.ok){
|
|
log.innerHTML += '<div style="color:var(--gr)">> \u2705 Task created: '+d.id+'</div>';
|
|
} else {
|
|
log.innerHTML += '<div style="color:var(--rd)">> \u274c '+(d.error||'failed')+'</div>';
|
|
}
|
|
log.scrollTop = log.scrollHeight;
|
|
}
|
|
setTimeout(refreshBladeStats, 500);
|
|
} catch(e){
|
|
console.error('[pushBladeTask] error=',e);
|
|
if(log){
|
|
log.innerHTML += '<div style="color:var(--rd)">> \u274c Error: '+e.message+'</div>';
|
|
log.scrollTop = log.scrollHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
// auto-refresh blade stats every 30s when capabilities tab visible
|
|
setInterval(()=>{ if(document.getElementById('v-caps').classList.contains('on')) refreshBladeStats(); },30000);
|
|
setTimeout(refreshBladeStats, 1500);
|
|
|
|
// V116-DASHBOARDS-TAB: load 70 dashboards tiles
|
|
let __dashData = null;
|
|
async function loadDashboards(){
|
|
// V117-HTTP-BADGES: load with optional status check
|
|
try {
|
|
const r = await fetch('/api/dashboards-registry.php');
|
|
const d = await r.json();
|
|
__dashData = d;
|
|
renderDashStats(d);
|
|
renderDashFilters(d);
|
|
/* V129: restore active filter */
|
|
const savedCat = __dashHashGet('cat') || 'all';
|
|
renderDashGrid(d.dashboards, savedCat);
|
|
// async load status badges in background
|
|
loadDashboardsStatus();
|
|
} catch(e){ console.error('[dashboards]',e); }
|
|
}
|
|
|
|
async function loadDashboardsStatus(){
|
|
try {
|
|
const r = await fetch('/api/dashboards-registry.php?check=1');
|
|
const d = await r.json();
|
|
__dashData = d;
|
|
renderDashGrid(d.dashboards, document.querySelector('.dash-filter.on')?.getAttribute('data-cat') || 'all');
|
|
} catch(e){ console.error('[dashboards-status]',e); }
|
|
}
|
|
function renderDashStats(d){
|
|
const box = document.getElementById('dash-stats');
|
|
if (!box) return;
|
|
box.innerHTML = '<div style="padding:4px 8px;background:var(--bg3);border-radius:3px"><b>Total:</b> '+d.total+'</div>' +
|
|
Object.entries(d.by_category).map(([c,n]) =>
|
|
'<div style="padding:4px 8px;background:var(--bg3);border-radius:3px"><b>'+c+':</b> '+n+'</div>'
|
|
).join('');
|
|
}
|
|
function renderDashFilters(d){
|
|
/* V127-RECENT-FILTER: add Recent <24h chip computed client-side */
|
|
const box = document.getElementById('dash-filters');
|
|
if (!box) return;
|
|
const now = Date.now();
|
|
const recentCount = (d.dashboards || []).filter(e => {
|
|
try { return (now - new Date(e.mtime).getTime()) < 24*3600*1000; } catch(_) { return false; }
|
|
}).length;
|
|
box.innerHTML = '<button class="mode dash-filter on" data-cat="all">All ('+d.total+')</button>' +
|
|
'<button class="mode dash-filter" data-cat="__recent" style="border-color:#10b981;color:#10b981">✨ Recent <24h ('+recentCount+')</button>' +
|
|
Object.entries(d.by_category).map(([c,n]) =>
|
|
'<button class="mode dash-filter" data-cat="'+c+'">'+c+' ('+n+')</button>'
|
|
).join('');
|
|
box.querySelectorAll('.dash-filter').forEach(b => {
|
|
b.addEventListener('click', () => {
|
|
box.querySelectorAll('.dash-filter').forEach(x => x.classList.remove('on'));
|
|
b.classList.add('on');
|
|
const cat = b.getAttribute('data-cat');
|
|
__dashHashSet('cat', cat === 'all' ? '' : cat); /* V129: persist */
|
|
renderDashGrid(__dashData.dashboards, cat);
|
|
});
|
|
});
|
|
/* V129: restore active filter from URL */
|
|
const savedCat = __dashHashGet('cat');
|
|
if (savedCat) {
|
|
const target = box.querySelector('button[data-cat="' + savedCat + '"]');
|
|
if (target) {
|
|
box.querySelectorAll('.dash-filter').forEach(x => x.classList.remove('on'));
|
|
target.classList.add('on');
|
|
}
|
|
}
|
|
}
|
|
function renderDashGrid(items, cat){
|
|
/* V117-HTTP-BADGES + V119-SEARCH + V123-PINS + V124-ENRICH */
|
|
const box = document.getElementById('dash-grid');
|
|
const pinnedBox = document.getElementById('dash-pinned-grid');
|
|
const pinnedSection = document.getElementById('dash-pinned-section');
|
|
const pinnedCountEl = document.getElementById('dash-pinned-count');
|
|
const clearBtn = document.getElementById('dash-clear-pins');
|
|
if (!box) return;
|
|
const search = (document.getElementById('dash-search')?.value || '').toLowerCase().trim();
|
|
const sort = document.getElementById('dash-sort')?.value || 'name';
|
|
const pinned = __dashPins();
|
|
let filtered;
|
|
if (cat === 'all') filtered = items;
|
|
else if (cat === '__recent') {
|
|
const now = Date.now();
|
|
filtered = items.filter(x => { try { return (now - new Date(x.mtime).getTime()) < 24*3600*1000; } catch(_) { return false; } });
|
|
}
|
|
else filtered = items.filter(x => x.category === cat);
|
|
if (search) {
|
|
filtered = filtered.filter(x =>
|
|
x.name.toLowerCase().includes(search) ||
|
|
x.display.toLowerCase().includes(search) ||
|
|
x.category.toLowerCase().includes(search)
|
|
);
|
|
}
|
|
if (sort === 'size') filtered = filtered.slice().sort((a,b) => b.size_kb - a.size_kb);
|
|
else if (sort === 'mtime') filtered = filtered.slice().sort((a,b) => (b.mtime||'').localeCompare(a.mtime||''));
|
|
else if (sort === 'category') filtered = filtered.slice().sort((a,b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name));
|
|
else filtered = filtered.slice().sort((a,b) => a.name.localeCompare(b.name));
|
|
// V124-ENRICH: split into pinned + rest; no more merge-sort with pin priority
|
|
const pinnedItems = filtered.filter(x => pinned.has(x.name));
|
|
const restItems = filtered.filter(x => !pinned.has(x.name));
|
|
const countEl = document.getElementById('dash-count');
|
|
/* V131-BROKEN-COUNT: aggregate status from registry */
|
|
const brokenCount = items.filter(e => e.http_status && e.http_status >= 400).length;
|
|
if (countEl) {
|
|
const brokenSuffix = brokenCount > 0
|
|
? ' <span style="color:#ef4444;margin-left:6px;font-weight:600" title="Broken dashboards (HTTP >= 400)">● ' + brokenCount + ' broken</span>'
|
|
: ' <span style="color:#10b981;margin-left:6px;font-weight:600" title="All dashboards healthy">● all OK</span>';
|
|
countEl.innerHTML = filtered.length + ' / ' + items.length + ' tuiles' + (pinned.size ? ' (' + pinned.size + ' pin' + (pinned.size>1?'s':'') + ')' : '') + brokenSuffix;
|
|
}
|
|
// Show/hide pinned section + clear button
|
|
if (pinnedSection) pinnedSection.style.display = pinnedItems.length ? 'block' : 'none';
|
|
if (clearBtn) clearBtn.style.display = pinned.size ? 'inline-block' : 'none';
|
|
if (pinnedCountEl) pinnedCountEl.textContent = pinnedItems.length + (pinnedItems.length === 1 ? ' dashboard' : ' dashboards');
|
|
function statusBadge(s){
|
|
if(!s) return '';
|
|
if(s===200) return '<span style="color:#10b981;font-size:9px;margin-left:4px">● 200</span>';
|
|
if(s===302) return '<span style="color:#f59e0b;font-size:9px;margin-left:4px">● auth</span>';
|
|
return '<span style="color:#ef4444;font-size:9px;margin-left:4px">● '+s+'</span>';
|
|
}
|
|
// V124-ENRICH: helper + recently-updated badge + split render
|
|
function recentBadge(mtime){
|
|
if(!mtime) return '';
|
|
const age = Date.now() - new Date(mtime).getTime();
|
|
if(age < 24*3600*1000) return '<span title="Updated <24h" style="color:#10b981;font-size:9px;margin-left:4px">✨ new</span>';
|
|
return '';
|
|
}
|
|
function tileHtml(e){
|
|
return '<a href="'+e.url+'" target="_blank" class="dash-tile' + (pinned.has(e.name) ? ' pinned' : '') + '" style="text-decoration:none;color:inherit;display:block;padding:10px;background:linear-gradient(135deg,'+e.color+'22,'+e.color+'11);border:1px solid ' + (pinned.has(e.name) ? '#fbbf24' : e.color+'55') + ';border-radius:6px;transition:transform 0.15s ease,border-color 0.15s ease,box-shadow 0.15s ease" onmouseover="this.style.transform=\'translateY(-2px)\';this.style.borderColor=\''+e.color+'\';this.style.boxShadow=\'0 4px 12px '+e.color+'33\'" onmouseout="this.style.transform=\'\';this.style.borderColor=\'' + (pinned.has(e.name) ? '#fbbf24' : e.color+'55') + '\';this.style.boxShadow=\'\'">' +
|
|
'<div style="font-size:18px;margin-bottom:4px">'+e.icon+' <span style="font-size:9px;color:'+e.color+';text-transform:uppercase">'+e.category+'</span>'+statusBadge(e.http_status)+recentBadge(e.mtime)+'</div>' +
|
|
'<div style="font-size:11px;font-weight:bold;line-height:1.3;margin-bottom:4px">'+e.display+'</div>' +
|
|
'<div style="font-size:9px;color:var(--mu);display:flex;justify-content:space-between;align-items:center"><span>'+e.name+' - '+e.size_kb+'KB</span>' +
|
|
'<button onclick="event.preventDefault();event.stopPropagation();__dashTogglePin(\''+e.name+'\')" style="background:transparent;border:0;color:'+(pinned.has(e.name)?"#fbbf24":"#555")+';cursor:pointer;font-size:14px;padding:0 4px" title="'+(pinned.has(e.name)?'Unpin':'Pin')+'">★</button>' +
|
|
'</div>' +
|
|
'</a>';
|
|
}
|
|
if (pinnedBox) pinnedBox.innerHTML = pinnedItems.map(tileHtml).join('');
|
|
box.innerHTML = restItems.map(tileHtml).join('');
|
|
}
|
|
// V125-THEME: theme toggle with URL hash persist
|
|
function __getTheme(){
|
|
return new URLSearchParams(window.location.hash.slice(1)).get('theme') || 'dark';
|
|
}
|
|
function __applyTheme(t){
|
|
document.body.classList.toggle('light', t === 'light');
|
|
const btn = document.getElementById('theme-toggle');
|
|
if (btn) btn.textContent = t === 'light' ? '\u263E' : '\u2600';
|
|
}
|
|
function __toggleTheme(){
|
|
const cur = __getTheme();
|
|
const next = cur === 'light' ? 'dark' : 'light';
|
|
const params = new URLSearchParams(window.location.hash.slice(1));
|
|
if (next === 'light') params.set('theme', 'light');
|
|
else params.delete('theme');
|
|
const h = params.toString();
|
|
window.history.replaceState(null, '', window.location.pathname + window.location.search + (h ? '#'+h : ''));
|
|
__applyTheme(next);
|
|
}
|
|
// Apply on load
|
|
setTimeout(() => __applyTheme(__getTheme()), 50);
|
|
|
|
// V123-PINS: pin management via URL hash (shareable, no localStorage)
|
|
function __dashPins(){
|
|
const raw = new URLSearchParams(window.location.hash.slice(1)).get('pins') || '';
|
|
return new Set(raw.split(',').filter(Boolean));
|
|
}
|
|
function __dashSetPins(set){
|
|
const params = new URLSearchParams(window.location.hash.slice(1));
|
|
if (set.size) params.set('pins', Array.from(set).join(','));
|
|
else params.delete('pins');
|
|
const h = params.toString();
|
|
window.history.replaceState(null, '', window.location.pathname + window.location.search + (h ? '#'+h : ''));
|
|
}
|
|
function __dashClearAllPins(){
|
|
if (!confirm('Clear all pins?')) return;
|
|
__dashSetPins(new Set());
|
|
if (__dashData) {
|
|
const activeCat = document.querySelector('.dash-filter.on')?.getAttribute('data-cat') || 'all';
|
|
renderDashGrid(__dashData.dashboards, activeCat);
|
|
}
|
|
}
|
|
// V129-URL-STATE: persist sort + active filter (category) in URL hash
|
|
function __dashHashGet(key){
|
|
return new URLSearchParams(window.location.hash.slice(1)).get(key) || '';
|
|
}
|
|
function __dashHashSet(key, val){
|
|
const params = new URLSearchParams(window.location.hash.slice(1));
|
|
if (val) params.set(key, val);
|
|
else params.delete(key);
|
|
const h = params.toString();
|
|
window.history.replaceState(null, '', window.location.pathname + window.location.search + (h ? '#'+h : ''));
|
|
}
|
|
|
|
function __dashTogglePin(name){
|
|
const s = __dashPins();
|
|
if (s.has(name)) s.delete(name);
|
|
else s.add(name);
|
|
__dashSetPins(s);
|
|
if (__dashData) {
|
|
const activeCat = document.querySelector('.dash-filter.on')?.getAttribute('data-cat') || 'all';
|
|
renderDashGrid(__dashData.dashboards, activeCat);
|
|
}
|
|
}
|
|
|
|
/* V136-HEALTH-MODAL: show/hide drill-down modal with broken URL list */
|
|
function __v136ShowHealthModal(){
|
|
const modal = document.getElementById('v136-health-modal');
|
|
const content = document.getElementById('v136-modal-content');
|
|
const summary = document.getElementById('v136-modal-summary');
|
|
if (!modal || !content) return;
|
|
modal.style.display = 'flex';
|
|
content.textContent = 'Fetching /api/screens-health.json ...';
|
|
fetch('/api/screens-health.json', {cache: 'no-store'}).then(r => r.ok ? r.json() : null).then(d => {
|
|
if (!d || !d.counts) { content.textContent = 'Pas de data health disponible.'; return; }
|
|
const c = d.counts;
|
|
const total = d.total || 0;
|
|
summary.textContent = 'Scan ' + (d.generated_at || '') + ' · Total ' + total + ' URLs · UP ' + (c.UP||0) + ' · SLOW ' + (c.SLOW||0) + ' · BROKEN ' + (c.BROKEN||0) + ' · DOWN ' + (c.DOWN||0) + ' · PHANTOM ' + (c.PHANTOM||0);
|
|
const byUrl = d.by_url || {};
|
|
const broken = [];
|
|
const slow = [];
|
|
for (const url in byUrl) {
|
|
const info = byUrl[url];
|
|
if (info.status === 'BROKEN' || info.status === 'DOWN') broken.push({url, ...info});
|
|
else if (info.status === 'SLOW') slow.push({url, ...info});
|
|
}
|
|
broken.sort((a,b) => (b.code||0) - (a.code||0));
|
|
slow.sort((a,b) => (b.ms||0) - (a.ms||0));
|
|
let html = '';
|
|
if (broken.length) {
|
|
html += '<div style="color:#ef4444;font-weight:600;margin-bottom:6px">🚫 BROKEN/DOWN (' + broken.length + '):</div>';
|
|
broken.slice(0, 30).forEach(b => {
|
|
html += '<div style="padding:3px 8px;border-left:3px solid #ef4444;margin-bottom:4px;background:rgba(239,68,68,0.05)">' +
|
|
'<span style="color:#ef4444;font-weight:600">' + (b.status||'?') + '</span> ' +
|
|
'<span style="color:#f59e0b">' + (b.code||'?') + '</span> ' +
|
|
'<a href="' + b.url + '" target="_blank" style="color:var(--ac);text-decoration:none">' + b.url + '</a> ' +
|
|
'<span style="color:var(--mu);float:right">' + (b.ms||'?') + 'ms</span>' +
|
|
'</div>';
|
|
});
|
|
} else {
|
|
html += '<div style="color:#10b981;font-weight:600">✅ No broken URLs</div>';
|
|
}
|
|
if (slow.length) {
|
|
html += '<div style="color:#f59e0b;font-weight:600;margin-top:12px;margin-bottom:6px">📤 SLOW top 10 (>2s):</div>';
|
|
slow.slice(0, 10).forEach(s => {
|
|
html += '<div style="padding:2px 8px;border-left:3px solid #f59e0b;margin-bottom:3px">' +
|
|
'<span style="color:#f59e0b">SLOW</span> ' +
|
|
'<a href="' + s.url + '" target="_blank" style="color:var(--ac);text-decoration:none">' + s.url + '</a> ' +
|
|
'<span style="color:var(--mu);float:right">' + (s.ms||'?') + 'ms</span>' +
|
|
'</div>';
|
|
});
|
|
}
|
|
content.innerHTML = html;
|
|
}).catch(err => { content.textContent = 'Erreur fetch: ' + err; });
|
|
}
|
|
function __v136HideHealthModal(){
|
|
const modal = document.getElementById('v136-health-modal');
|
|
if (modal) modal.style.display = 'none';
|
|
}
|
|
/* Escape key closes modal */
|
|
document.addEventListener('keydown', function(e){
|
|
if (e.key === 'Escape') {
|
|
const modal = document.getElementById('v136-health-modal');
|
|
if (modal && modal.style.display !== 'none') __v136HideHealthModal();
|
|
}
|
|
});
|
|
|
|
/* V135-KPI-BANNER (V137 refactored): named async function, reusable by V137 refresh */
|
|
/* V139-TRUTH-STRIP: load source of truth registry (fire once on load) */
|
|
/* V142-FOOTER-STRIP: load ecosystem health once */
|
|
async function __v142LoadFooter(){
|
|
try {
|
|
const r = await fetch('/api/ecosystem-health.php', {cache: 'no-store'});
|
|
if (!r.ok) return;
|
|
const d = await r.json();
|
|
const set = (id, content) => { const el = document.getElementById(id); if (el) el.innerHTML = content; };
|
|
const colorScore = d.percent >= 99 ? '#10b981' : d.percent >= 95 ? '#f59e0b' : '#ef4444';
|
|
set('v142-score', '<span style="color:' + colorScore + '">' + (d.score || '?') + ' ' + (d.percent ?? 0) + '%</span>');
|
|
if (d.l99) set('v142-l99', 'L99 <b>' + (d.l99.pass||0) + '/' + (d.l99.total||0) + '</b>');
|
|
set('v142-tools', 'Tools <b>' + (d.tools_wired ?? 0) + '</b>');
|
|
if (d.infra) set('v142-docker', 'Docker <b>' + (d.infra.docker ?? 0) + '</b>');
|
|
if (d.providers) set('v142-providers', 'Providers <b>' + (d.providers.free ?? 0) + '</b>');
|
|
if (d.providers?.qdrant) set('v142-qdrant', 'Qdrant <b>' + Number(d.providers.qdrant).toLocaleString('fr-FR') + '</b>');
|
|
if (d.infra?.ollama) set('v142-ollama', 'Ollama <b>' + d.infra.ollama + '</b>');
|
|
const footer = document.getElementById('v142-footer');
|
|
if (footer) footer.title = 'Ecosystem health live · source: /api/ecosystem-health.php · ts: ' + (d.ts || 'n/a') + ' · click Truth → for unified registry';
|
|
} catch(_) {}
|
|
}
|
|
__v142LoadFooter();
|
|
|
|
async function __v139LoadTruthStrip(){
|
|
try {
|
|
const r = await fetch('/api/wevia-truth-registry.json', {cache: 'no-store'});
|
|
if (!r.ok) return;
|
|
const d = await r.json();
|
|
const setN = (id, n) => { const el = document.getElementById(id); if (el && n !== undefined) el.innerHTML = '· <strong style="color:var(--ac)">' + Number(n).toLocaleString('fr-FR') + '</strong> ' + id.replace('v139-',''); };
|
|
setN('v139-agents', d.agents?.count_unique);
|
|
// === AMBRE-V1 referentiel-unique sync (additif · doctrine #14) ===
|
|
// Sync all agent count spots (h-ag header, t-ag truth strip, orch-agents orchestrator)
|
|
// All read from same truth-registry source → 'référentiel unique' doctrine
|
|
const setNumRaw = (id, n) => { const el = document.getElementById(id); if (el && n !== undefined && n !== null) el.textContent = Number(n).toLocaleString('fr-FR'); };
|
|
setNumRaw('h-ag', d.agents?.count_unique);
|
|
setNumRaw('t-ag', d.agents?.count_unique);
|
|
setNumRaw('orch-agents', d.agents?.count_unique);
|
|
// Also sync providers count if the header has it hardcoded
|
|
setNumRaw('h-pv', d.providers?.count);
|
|
setNumRaw('t-pv', d.providers?.count);
|
|
// === END AMBRE-V1 ===
|
|
// V141-AGENTS-TOOLTIP: expose dedup context on hover
|
|
const agEl = document.getElementById('v139-agents');
|
|
if (agEl && d.agents) {
|
|
const u = d.agents.count_unique || 0;
|
|
const w = d.agents.count_with_overlaps || 0;
|
|
const bySrc = d.agents.by_source || {};
|
|
const srcList = Object.entries(bySrc).map(([k,v]) => ' ' + k + ': ' + v).join('\n');
|
|
agEl.title = 'Agents uniques dédupliqués: ' + u + '\nAvec overlaps bruts: ' + w + '\nSources:\n' + srcList + '\n\nAutres pages peuvent afficher des chiffres différents (950, 990, 126) car consomment d\'autres référentiels. Valeur de vérité = 906.';
|
|
agEl.style.cursor = 'help';
|
|
}
|
|
setN('v139-intents', d.intents?.count);
|
|
setN('v139-skills', d.skills?.TOTAL);
|
|
setN('v139-brains', d.brains?.count);
|
|
setN('v139-doctrines', d.doctrines?.count);
|
|
setN('v139-dashboards', d.dashboards?.count);
|
|
// V140-TRUTH-HEALTH: autonomy + NonReg badges
|
|
const autEl = document.getElementById('v140-autonomy');
|
|
if (autEl && d.autonomy_score !== undefined) {
|
|
const lvl = d.autonomy_level || '';
|
|
const score = d.autonomy_score;
|
|
const badgeColor = score >= 100 ? '#10b981' : score >= 80 ? '#f59e0b' : '#ef4444';
|
|
autEl.innerHTML = '· <strong style="color:' + badgeColor + '">' + score + '%</strong> ' + (lvl ? '<span style="color:' + badgeColor + ';font-size:9px">' + lvl + '</span>' : 'autonomy');
|
|
}
|
|
// V140B-NR-LIVE: fetch fresh NR from l99-honest.php (truth-registry may be stale snapshot)
|
|
try {
|
|
const nrResp = await fetch('/api/l99-honest.php', {cache: 'no-store'});
|
|
if (nrResp.ok) {
|
|
const nrData = await nrResp.json();
|
|
const nrEl = document.getElementById('v140-nonreg');
|
|
if (nrEl && nrData.combined) {
|
|
const s = nrData.combined.pass;
|
|
const t = nrData.combined.total;
|
|
const pct = t ? Math.round((s/t)*100) : 0;
|
|
const badgeColor = pct >= 99 ? '#10b981' : pct >= 95 ? '#f59e0b' : '#ef4444';
|
|
nrEl.innerHTML = '· NR <strong style="color:' + badgeColor + '">' + s + '/' + t + '</strong>';
|
|
nrEl.title = 'Non-régression live: ' + s + '/' + t + ' (' + pct + '%) · source: /api/l99-honest.php · ts: ' + (nrData.ts || 'n/a');
|
|
}
|
|
}
|
|
} catch(_) {
|
|
// fallback: use snapshot from truth-registry
|
|
const nrEl = document.getElementById('v140-nonreg');
|
|
if (nrEl && d.nonreg) {
|
|
const s = d.nonreg.score, t = d.nonreg.total, pct = t ? Math.round((s/t)*100) : 0;
|
|
const badgeColor = pct >= 99 ? '#10b981' : pct >= 95 ? '#f59e0b' : '#ef4444';
|
|
nrEl.innerHTML = '· NR <strong style="color:' + badgeColor + '">' + s + '/' + t + '</strong> <span style="color:var(--mu);font-size:8px">(snapshot)</span>';
|
|
nrEl.title = 'NR (snapshot truth-registry, peut être stale): ' + s + '/' + t + ' (' + pct + '%)';
|
|
}
|
|
}
|
|
} catch (_) {}
|
|
}
|
|
__v139LoadTruthStrip();
|
|
|
|
async function __v135UpdateHealthBanner(){
|
|
const kpi = document.getElementById('v135-kpi-live');
|
|
if (!kpi) return;
|
|
try {
|
|
const r = await fetch('/api/screens-health.json', {cache: 'no-store'});
|
|
if (!r.ok) return;
|
|
const d = await r.json();
|
|
if (!d || !d.counts) return;
|
|
const c = d.counts;
|
|
const total = d.total || 0;
|
|
const up = c.UP || 0;
|
|
const broken = c.BROKEN || 0;
|
|
const down = c.DOWN || 0;
|
|
const phantom = c.PHANTOM || 0;
|
|
const active = total - phantom;
|
|
const healthPct = active ? Math.round((up / active) * 100) : 0;
|
|
const dot = (broken + down === 0) ? '\u{1F7E2}' : (broken + down < 20 ? '\u{1F7E1}' : '\u{1F534}');
|
|
// V137: compute scan age
|
|
let ageStr = '';
|
|
if (d.generated_at) {
|
|
const scanMs = new Date(d.generated_at).getTime();
|
|
if (!isNaN(scanMs)) {
|
|
const ageMin = Math.floor((Date.now() - scanMs) / 60000);
|
|
ageStr = ageMin < 1 ? ' · <span style="color:#10b981">just now</span>'
|
|
: ageMin < 60 ? ' · ' + ageMin + 'min ago'
|
|
: ' · ' + Math.floor(ageMin/60) + 'h ago';
|
|
}
|
|
}
|
|
kpi.innerHTML = 'All-IA Hub · ' + dot + ' ' + healthPct + '% (' + up + ' UP · ' + broken + ' broken)' + ageStr;
|
|
kpi.title = 'Platform health: ' + up + ' UP / ' + broken + ' BROKEN / ' + down + ' DOWN / ' + phantom + ' phantom (total ' + total + ')\nScan: ' + (d.generated_at || 'n/a') + '\nClick: détail · Bouton \u21BA: refresh';
|
|
} catch (_) {}
|
|
}
|
|
/* V138-COPY-URL: copy current URL with hash state to clipboard */
|
|
async function __v138CopyShareURL(){
|
|
const url = window.location.href;
|
|
const btn = document.getElementById('v138-copy-btn');
|
|
try {
|
|
await navigator.clipboard.writeText(url);
|
|
if (btn) {
|
|
const orig = btn.innerHTML;
|
|
btn.innerHTML = '✔ Copied!';
|
|
btn.style.color = '#10b981';
|
|
btn.style.borderColor = '#10b981';
|
|
setTimeout(() => { btn.innerHTML = orig; btn.style.color = ''; btn.style.borderColor = ''; }, 1500);
|
|
}
|
|
} catch (err) {
|
|
if (btn) {
|
|
btn.innerHTML = '✗ Error';
|
|
btn.style.color = '#ef4444';
|
|
setTimeout(() => { btn.innerHTML = '🔗 Share'; btn.style.color = ''; }, 1500);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* V137-REFRESH: manual refresh with spin animation */
|
|
async function __v137RefreshHealth(){
|
|
const btn = document.getElementById('v137-refresh-btn');
|
|
if (btn) {
|
|
btn.style.animation = 'v137spin 0.8s linear infinite';
|
|
btn.style.color = 'var(--vl)';
|
|
btn.disabled = true;
|
|
}
|
|
await __v135UpdateHealthBanner();
|
|
if (btn) {
|
|
setTimeout(() => {
|
|
btn.style.animation = '';
|
|
btn.style.color = '';
|
|
btn.disabled = false;
|
|
}, 400);
|
|
}
|
|
}
|
|
/* Initial load */
|
|
__v135UpdateHealthBanner();
|
|
|
|
setTimeout(() => {
|
|
const btn = document.querySelector('[data-view="dashboards"]');
|
|
if (btn) btn.addEventListener('click', () => { if (!__dashData) loadDashboards(); });
|
|
// V119-SEARCH: re-render on search/sort change
|
|
const search = document.getElementById('dash-search');
|
|
const sort = document.getElementById('dash-sort');
|
|
const rerender = () => {
|
|
if (!__dashData) return;
|
|
const activeCat = document.querySelector('.dash-filter.on')?.getAttribute('data-cat') || 'all';
|
|
renderDashGrid(__dashData.dashboards, activeCat);
|
|
};
|
|
if (search) search.addEventListener('input', rerender);
|
|
// V128-SCROLL-TOP: toggle button visibility based on scroll
|
|
const scrollBtn = document.getElementById('dash-scroll-top');
|
|
if (scrollBtn) {
|
|
const updateScrollBtn = () => {
|
|
const isDashVisible = document.getElementById('v-dashboards')?.classList.contains('on');
|
|
const scrolledFar = window.scrollY > 400;
|
|
scrollBtn.style.display = (isDashVisible && scrolledFar) ? 'block' : 'none';
|
|
};
|
|
window.addEventListener('scroll', updateScrollBtn, { passive: true });
|
|
// Also hide when switching tabs
|
|
document.querySelectorAll('.tab').forEach(t => t.addEventListener('click', () => setTimeout(updateScrollBtn, 100)));
|
|
}
|
|
if (sort) {
|
|
// V129: restore sort from URL on init
|
|
const savedSort = __dashHashGet('sort');
|
|
if (savedSort && ['name','size','mtime','category'].includes(savedSort)) sort.value = savedSort;
|
|
sort.addEventListener('change', () => { __dashHashSet('sort', sort.value === 'name' ? '' : sort.value); rerender(); });
|
|
}
|
|
// V120-KEYBOARD: Cmd+K / Ctrl+K focus search when dashboards tab is visible
|
|
document.addEventListener('keydown', (e) => {
|
|
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
const viewDash = document.getElementById('v-dashboards');
|
|
if (viewDash && viewDash.classList.contains('on')) {
|
|
e.preventDefault();
|
|
const s = document.getElementById('dash-search');
|
|
if (s) { s.focus(); s.select(); }
|
|
}
|
|
}
|
|
if (e.key === 'Escape') {
|
|
const s = document.getElementById('dash-search');
|
|
if (s && document.activeElement === s && s.value) {
|
|
s.value = '';
|
|
s.dispatchEvent(new Event('input'));
|
|
}
|
|
}
|
|
});
|
|
}, 500);
|
|
|
|
// V114-TRAINING-LIVE: fetch real training stats
|
|
async function loadTrainingStats(){
|
|
try{
|
|
const r = await fetch('/api/training-status.php');
|
|
const d = await r.json();
|
|
const set = (id,val) => { const e=document.getElementById(id); if(e) e.textContent = val; };
|
|
set('t-qv', d.qdrant?.vectors || '?');
|
|
set('t-wi', d.wiki?.entries || '?');
|
|
set('t-vf', d.vault?.files || '?');
|
|
set('t-ag', d.catalog?.total || '?');
|
|
set('t-int', d.intents?.wired || '?');
|
|
set('t-gold', d.vault?.gold || '?');
|
|
}catch(e){
|
|
console.error('[training-stats]',e);
|
|
}
|
|
}
|
|
setInterval(()=>{ if(document.getElementById('v-train').classList.contains('on')) loadTrainingStats(); }, 30000);
|
|
setTimeout(loadTrainingStats, 1500);
|
|
|
|
// V113-ROUTER-ACTIVITY: load router matches
|
|
async function loadRouterActivity(){
|
|
try{
|
|
const r = await fetch('/api/router-activity.php?k=WEVADS2026&limit=50');
|
|
const d = await r.json();
|
|
const t = document.getElementById('router-total');
|
|
const rc = document.getElementById('router-recent');
|
|
if(t) t.textContent = d.total_log_lines || 0;
|
|
if(rc) rc.textContent = d.count || 0;
|
|
const pbox = document.getElementById('router-patterns');
|
|
if(pbox && d.by_pattern){
|
|
pbox.innerHTML = '';
|
|
for(const [p, n] of Object.entries(d.by_pattern)){
|
|
const div = document.createElement('div');
|
|
div.style.cssText = 'padding:3px 6px;background:var(--bg3);border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
div.title = p;
|
|
div.innerHTML = '<b style="color:var(--cy)">'+n+'x</b> <span style="color:var(--mu)">'+p.substring(0,40)+'...</span>';
|
|
pbox.appendChild(div);
|
|
}
|
|
}
|
|
const mbox = document.getElementById('router-recent-msgs');
|
|
if(mbox && d.matches){
|
|
mbox.innerHTML = d.matches.map(m =>
|
|
'<div>> <span style="color:var(--dm)">['+m.ts.substring(11,16)+']</span> '+m.msg.substring(0,140)+'</div>'
|
|
).join('');
|
|
}
|
|
}catch(e){
|
|
console.error('[router-activity]',e);
|
|
}
|
|
}
|
|
// auto-load when training tab active
|
|
setInterval(()=>{ if(document.getElementById('v-train').classList.contains('on')) loadRouterActivity(); }, 20000);
|
|
setTimeout(loadRouterActivity, 2000);
|
|
|
|
// Live stats
|
|
async function refreshStats(){
|
|
try{
|
|
const r=await fetch('/api/l99-honest.php?compact=1',{cache:'no-store'});
|
|
const d=await r.json();
|
|
if(d.nonreg) document.getElementById('h-nr').textContent=d.nonreg;
|
|
}catch(e){}
|
|
}
|
|
refreshStats();
|
|
setInterval(refreshStats,60000);
|
|
</script>
|
|
|
|
<!-- OPUS_v932f_DROID_LINK -->
|
|
<a href="/wevia-ia/droid.html" id="opus-droid-link" title="WEDROID v3.2" style="position:fixed;bottom:20px;right:20px;padding:7px 14px;background:rgba(16,185,129,0.15);color:#10b981;text-decoration:none;border-radius:18px;font-size:12px;font-weight:600;border:1px solid rgba(16,185,129,0.4);backdrop-filter:blur(10px);z-index:9997">Droid</a>
|
|
|
|
<script src="/api/a11y-auto-enhancer.js" defer></script>
|
|
<!-- V142-FOOTER-STRIP: ecosystem health in footer (aligned with WTP) -->
|
|
<div id="v142-footer" style="position:fixed;bottom:0;left:0;right:0;background:rgba(0,0,0,0.75);border-top:1px solid var(--bd);padding:4px 12px;display:flex;gap:14px;align-items:center;font-size:9px;color:var(--mu);z-index:40;backdrop-filter:blur(8px);font-family:ui-monospace,monospace">
|
|
<span id="v142-score" style="font-weight:600"></span>
|
|
<span id="v142-l99"></span>
|
|
<span id="v142-tools"></span>
|
|
<span id="v142-docker"></span>
|
|
<span id="v142-providers"></span>
|
|
<span id="v142-qdrant"></span>
|
|
<span id="v142-ollama"></span>
|
|
<span style="margin-left:auto;color:#00d4b4;font-size:9px"><a href="/wevia-unified-hub.html" style="color:inherit;text-decoration:none" title="Truth Hub">Truth →</a></span>
|
|
</div>
|
|
<script>(function(){var p=window.location.pathname;var pub=["/","/index.html","/wevia.html","/wevia-widget.html","/enterprise-model.html","/wevia","/login","/register.html","/agents-archi.html","/wevia-meeting-rooms.html","/director-center.html","/director-chat.html","/l99-brain.html","/agents-fleet.html","/value-streaming.html","/architecture.html","/openclaw.html","/l99-saas.html","/admin-saas.html","/agents-goodjob.html","/ai-benchmark.html","/oss-discovery.html","/paperclip.html","/agents-3d.html","/agents-alive.html","/agents-enterprise.html","/agents-hd.html","/agents-iso3d.html","/agents-sim.html","/agents-valuechain.html","/avatar-picker.html"];var isPub=pub.indexOf(p)>=0||p.indexOf("/products/")===0||p.indexOf("/solutions/")===0||p.indexOf("/blog/")===0||p.indexOf("/service/")===0||p.indexOf("/marketplace")===0||p.indexOf("/contact")===0||p.indexOf("/tarifs")===0||p.indexOf("/news")===0;if(isPub||document.getElementById("weval-gl"))return;var a=document.createElement("a");a.id="weval-gl";a.href="/logout";a.textContent="Logout";a.style.cssText="position:fixed;top:10px;right:12px;z-index:99990;padding:5px 10px;background:rgba(30,30,50,0.7);color:rgba(200,210,230,0.8);border:1px solid rgba(100,100,140,0.3);border-radius:6px;font:500 11px system-ui,sans-serif;text-decoration:none;opacity:0.6;cursor:pointer;backdrop-filter:blur(6px);transition:all .15s";a.onmouseover=function(){this.style.opacity="1";this.style.background="rgba(239,68,68,0.85)";this.style.color="white"};a.onmouseout=function(){this.style.opacity="0.6";this.style.background="rgba(30,30,50,0.7)";this.style.color="rgba(200,210,230,0.8)"};document.body.appendChild(a)})()</script><script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
|
|
<script src="/api/weval-feature-tracker.js" defer></script>
|
|
<script src="/api/ambre-universal-chat.js" defer></script>
|
|
|
|
<!-- WAVE 265 · Factory pill cross-page (injected, position mesurée zéro overlap) -->
|
|
<a id="w265-factory-cross" href="/wevia-multiagent-dashboard.html" title="Factory Health Monitor (30 agents)"
|
|
style="position:fixed;top:12px;left:12px;padding:6px 12px;border-radius:14px;background:linear-gradient(135deg,rgba(34,211,238,.2),rgba(168,85,247,.15));border:1px solid rgba(34,211,238,.4);color:#67e8f9;font-size:11px;font-weight:700;text-decoration:none;display:inline-flex;align-items:center;gap:6px;z-index:9999;backdrop-filter:blur(10px);box-shadow:0 2px 8px rgba(0,0,0,.4)">
|
|
<span>🏭</span>
|
|
<span id="w265-factory-txt">Factory: ...</span>
|
|
</a>
|
|
<script>
|
|
/* w265 Factory auto-load */
|
|
(function(){
|
|
async function refresh(){
|
|
try {
|
|
const r = await fetch('/api/wevia-v83-business-kpi.php?action=summary', {cache:'no-store'}).then(r=>r.json()).catch(()=>null);
|
|
const el = document.getElementById('w265-factory-txt');
|
|
if(el && r && r.summary){
|
|
const s = r.summary;
|
|
const pct = s.data_completeness_pct || 0;
|
|
el.textContent = `Factory: ${pct}% (${s.ok || 0}/${s.total_kpis || 0})`;
|
|
}
|
|
} catch(e){ console.log('[w265] factory check err', e); }
|
|
}
|
|
refresh();
|
|
setInterval(refresh, 60000);
|
|
})();
|
|
</script>
|
|
<!-- /WAVE 265 Factory pill cross-page -->
|
|
|
|
|
|
<!-- WEVIA-AUTONOMY-NAV-AIH-v1 -->
|
|
<!-- /WEVIA-AUTONOMY-NAV-AIH-v1 -->
|
|
<!-- WEVIA-AUTONOMY-NAV-AIH -->
|
|
<div id="wevia-autonomy-nav" style="position:fixed;bottom:44px;left:16px;display:flex;flex-direction:column;gap:8px;z-index:9998">
|
|
<a href="/wevia-audit.html" style="padding:8px 14px;background:linear-gradient(135deg,#10b981,#047857);color:#fff;border-radius:8px;font-weight:700;font-size:12px;text-decoration:none;box-shadow:0 4px 12px rgba(16,185,129,0.4);transition:transform 0.2s">Audit Trail</a>
|
|
<a href="/weval-live-ops.html" style="padding:8px 14px;background:linear-gradient(135deg,#e94560,#c03350);color:#fff;border-radius:8px;font-weight:700;font-size:12px;text-decoration:none;box-shadow:0 4px 12px rgba(233,69,96,0.4);transition:transform 0.2s">Live Ops</a>
|
|
</div>
|
|
<!-- /WEVIA-AUTONOMY-NAV-AIH -->
|
|
</html>
|
|
|
|
<!-- DOCTRINE-60-UX-JS -->
|
|
<script id="doctrine60-ux-js-all-ia-hub">
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry, i) => {
|
|
if (entry.isIntersecting) {
|
|
setTimeout(() => {
|
|
entry.target.style.opacity = 1;
|
|
entry.target.style.transform = 'translateY(0)';
|
|
}, i * 100);
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.1 });
|
|
document.querySelectorAll('.card, .panel, .btn, .kpi').forEach(el => observer.observe(el));
|
|
|
|
</script> |