Files
weval-consulting/wevia-master.html

931 lines
54 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>WEVIA Master AI</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{--bg:#0a0a0f;--s1:#101018;--s2:#16161f;--s3:#1c1c28;--bd:rgba(255,255,255,.06);--tx:#e8e6f0;--dim:#8888aa;--dim2:#555570;--acc:#00e87b;--acc2:rgba(0,232,123,.08);--red:#ff4466;--blue:#4488ff;--yellow:#ffc800;--cyan:#00d4ff;--font:'Inter',system-ui,sans-serif;--mono:'JetBrains Mono',monospace;--r:12px;--maxw:820px}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
body{background:var(--bg);color:var(--tx);font-family:var(--font);height:100dvh;display:flex;overflow:hidden}
::selection{background:var(--acc);color:#000}
.sidebar{width:260px;background:var(--s1);border-right:1px solid var(--bd);display:flex;flex-direction:column;flex-shrink:0;transition:transform .3s}
.sb-head{padding:16px 20px;border-bottom:1px solid var(--bd);display:flex;align-items:center;gap:10px}
.sb-logo{font-family:var(--mono);font-weight:600;font-size:15px;color:var(--acc);letter-spacing:.05em}
.sb-logo small{color:var(--dim2);font-weight:400;font-size:10px;display:block;margin-top:2px;letter-spacing:.02em}
.sb-new{margin-left:auto;width:32px;height:32px;border-radius:8px;border:1px solid var(--bd);background:transparent;color:var(--dim);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:18px;transition:all .2s}
.sb-new:hover{border-color:var(--acc);color:var(--acc)}
.sb-label{padding:14px 20px 6px;font-family:var(--mono);font-size:9px;text-transform:uppercase;letter-spacing:.14em;color:var(--dim2)}
.sb-nav{flex:1;overflow-y:auto;padding:4px 10px}
.sb-item{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:var(--r);cursor:pointer;font-size:13px;color:var(--dim);transition:all .15s;margin-bottom:1px}
.sb-item:hover{background:var(--s2);color:var(--tx)}
.sb-item .ico{font-size:13px;width:20px;text-align:center;flex-shrink:0;opacity:.7}
.sb-foot{padding:12px 20px;border-top:1px solid var(--bd);font-family:var(--mono);font-size:9px;color:var(--dim2);line-height:1.8}
.dot{display:inline-block;width:5px;height:5px;border-radius:50%;margin-right:4px;animation:pulse 2s ease infinite}
.dot.g{background:var(--acc)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
.main{flex:1;display:flex;flex-direction:column;min-width:0}
.top{height:44px;border-bottom:1px solid var(--bd);display:flex;align-items:center;padding:0 20px;gap:10px;flex-shrink:0;background:var(--s1)}
.top-title{font-weight:500;font-size:13px}
.pills{margin-left:auto;display:flex;gap:5px}
.pill{font-family:var(--mono);font-size:8px;padding:2px 7px;border-radius:20px;border:1px solid var(--bd);color:var(--dim2)}
.pill.live{border-color:var(--acc);color:var(--acc)}
.menu-btn{display:none;width:32px;height:32px;border:none;background:none;color:var(--dim);cursor:pointer;font-size:18px}
.chat{flex:1;overflow-y:auto;padding:24px 0;scroll-behavior:smooth}
.chat::-webkit-scrollbar{width:4px}
.chat::-webkit-scrollbar-thumb{background:var(--bd);border-radius:2px}
.ci{max-width:var(--maxw);margin:0 auto;padding:0 24px}
/* WELCOME */
.welcome{text-align:center;padding:100px 20px 40px}
.welcome h1{font-family:var(--mono);font-size:32px;color:var(--acc);font-weight:700;letter-spacing:.08em}
.welcome p{color:var(--dim);font-size:13px;margin-top:8px}
.welcome-cards{display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:500px;margin:40px auto 0}
.wcard{background:var(--s2);border:1px solid var(--bd);border-radius:var(--r);padding:16px;cursor:pointer;text-align:center;transition:all .2s}
.wcard:hover{border-color:rgba(255,255,255,.12);background:var(--s3);transform:translateY(-1px)}
.wcard h3{font-size:13px;font-weight:500;margin-bottom:4px;display:flex;align-items:center;gap:6px}
.wcard p{font-size:11px;color:var(--dim2);line-height:1.4}
/* MESSAGES */
.msg{margin-bottom:20px;animation:fadeIn .3s ease}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.msg.user{display:flex;justify-content:flex-end}
.msg.user .bubble{background:var(--s3);border:1px solid var(--bd);border-radius:var(--r) var(--r) 4px var(--r);padding:12px 16px;max-width:75%;font-size:14px;line-height:1.6}
.msg.ai{display:flex;gap:12px;align-items:flex-start}
.msg.ai .av{width:28px;height:28px;border-radius:8px;background:var(--acc);color:#000;display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:11px;font-weight:700;flex-shrink:0;margin-top:2px}
.msg.ai .body{flex:1;min-width:0}
.msg.ai .content{font-size:14px;line-height:1.7;color:var(--tx)}
.msg.ai .content p{margin-bottom:10px}
.msg.ai .content strong{color:#fff}
.msg.ai .content h3{font-size:15px;margin:16px 0 6px;color:#fff}
.msg.ai .content code{font-family:var(--mono);font-size:12px;background:var(--s3);padding:2px 5px;border-radius:4px;color:var(--cyan)}
.msg.ai .content pre{background:var(--s2);border:1px solid var(--bd);border-radius:var(--r);padding:14px;margin:10px 0;overflow-x:auto;position:relative}
.msg.ai .content pre code{background:none;padding:0;color:var(--tx);font-size:12px;line-height:1.6}
.msg.ai .content ul,.msg.ai .content ol{margin:8px 0;padding-left:20px}
.msg.ai .content li{margin-bottom:4px}
.copy-btn{position:absolute;top:8px;right:8px;font-family:var(--mono);font-size:9px;padding:3px 8px;border-radius:4px;border:1px solid var(--bd);background:var(--s3);color:var(--dim);cursor:pointer;transition:all .2s}
.copy-btn:hover{color:var(--acc);border-color:var(--acc)}
/* THINKING INDICATOR (Claude-like) */
.thinking{display:flex;gap:12px;align-items:flex-start;margin-bottom:16px;animation:fadeIn .3s ease}
.thinking .av{width:28px;height:28px;border-radius:8px;background:var(--acc);color:#000;display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:11px;font-weight:700;flex-shrink:0}
.think-box{flex:1}
.think-steps{display:flex;flex-direction:column;gap:6px}
.think-step{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dim);font-family:var(--mono);padding:6px 12px;background:var(--s2);border-radius:8px;border-left:2px solid var(--acc);animation:slideIn .3s ease}
@keyframes slideIn{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:none}}
.think-step .spinner{width:12px;height:12px;border:2px solid var(--bd);border-top-color:var(--acc);border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.think-step.done .spinner{display:none}
.think-step.done::before{content:'✓';color:var(--acc);font-size:11px;font-weight:bold}
.think-step .label{flex:1}
.think-step .val{color:var(--dim2);font-size:10px}
/* META TAGS */
.meta{display:flex;flex-wrap:wrap;gap:4px;margin-top:10px}
.tag{font-family:var(--mono);font-size:9px;padding:2px 7px;border-radius:6px;border:1px solid var(--bd);color:var(--dim2)}
.tag.t0{border-color:var(--acc);color:var(--acc)}
.tag.t1{border-color:var(--blue);color:var(--blue)}
.tag.rag{border-color:var(--yellow);color:var(--yellow)}
/* INPUT */
.input-area{border-top:1px solid var(--bd);padding:12px 24px 16px;background:var(--s1)}
.input-wrap{max-width:var(--maxw);margin:0 auto;position:relative}
.input-box{display:flex;align-items:flex-end;background:var(--s2);border:1px solid var(--bd);border-radius:var(--r);padding:10px 52px 10px 16px;transition:border-color .2s}
.input-box:focus-within{border-color:rgba(255,255,255,.15)}
textarea{width:100%;background:none;border:none;color:var(--tx);font-family:var(--font);font-size:14px;resize:none;outline:none;line-height:1.5;max-height:160px}
textarea::placeholder{color:var(--dim2)}
.send-btn{position:absolute;right:14px;bottom:14px;width:34px;height:34px;border-radius:10px;background:var(--acc);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .15s}
.send-btn:hover{background:#00ff88;transform:scale(1.05)}
.send-btn:active{transform:scale(.95)}
.send-btn:disabled{opacity:.2;cursor:default;transform:none}
.send-btn svg{width:15px;height:15px;stroke:#000;fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}
.input-hint{max-width:var(--maxw);margin:6px auto 0;font-family:var(--mono);font-size:9px;color:var(--dim2);display:flex;justify-content:space-between}
/* DROP ZONE */
.drop-overlay{display:none;position:fixed;inset:0;z-index:100;background:rgba(0,232,123,.06);backdrop-filter:blur(4px);align-items:center;justify-content:center}
.drop-overlay.show{display:flex}
.drop-box{border:2px dashed var(--acc);border-radius:20px;padding:60px 80px;text-align:center;color:var(--acc);font-size:18px;font-weight:500;animation:dropPulse 1.5s ease infinite}
@keyframes dropPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.7;transform:scale(1.02)}}
/* FILE PREVIEW */
.file-preview{display:flex;gap:8px;padding:8px 0;flex-wrap:wrap}
.file-chip{display:flex;align-items:center;gap:6px;background:var(--s3);border:1px solid var(--bd);border-radius:8px;padding:5px 10px;font-size:11px;color:var(--dim);font-family:var(--mono);animation:fadeIn .2s ease}
.file-chip img{width:32px;height:32px;border-radius:4px;object-fit:cover}
.file-chip .remove{cursor:pointer;color:var(--red);font-size:14px;margin-left:4px;opacity:.6;transition:opacity .2s}
.file-chip .remove:hover{opacity:1}
.file-chip .fsize{color:var(--dim2);font-size:9px}
/* ATTACH BUTTON */
.attach-btn{position:absolute;left:10px;bottom:14px;width:32px;height:32px;border-radius:8px;border:none;background:transparent;color:var(--dim);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}
.attach-btn:hover{color:var(--acc);background:var(--acc2)}
.attach-btn svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
/* IMAGE IN MESSAGE */
.msg-image{max-width:300px;border-radius:var(--r);margin-top:8px;border:1px solid var(--bd)}
/* CURSOR BLINK */
.cursor{display:inline-block;width:2px;height:16px;background:var(--acc);animation:blink 1s step-end infinite;vertical-align:text-bottom;margin-left:2px}
@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
/* SCROLL TO BOTTOM */
.scroll-btn{position:fixed;bottom:100px;right:40px;width:36px;height:36px;border-radius:50%;background:var(--s2);border:1px solid var(--bd);color:var(--dim);cursor:pointer;display:none;align-items:center;justify-content:center;font-size:16px;transition:all .2s;z-index:10}
.scroll-btn:hover{border-color:var(--acc);color:var(--acc)}
.scroll-btn.show{display:flex}
.voice-btn{position:absolute;right:52px;bottom:14px;width:34px;height:34px;border-radius:10px;background:transparent;border:1px solid var(--bd);color:var(--dim);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}
.voice-btn:hover{border-color:var(--acc);color:var(--acc)}
.voice-btn.recording{background:var(--red);border-color:var(--red);color:#fff;animation:pulse 1s ease infinite}
.voice-btn svg{width:15px;height:15px;fill:currentColor}
.tts-btn{font-family:var(--mono);font-size:9px;padding:2px 6px;border-radius:4px;border:1px solid var(--bd);background:transparent;color:var(--dim2);cursor:pointer;margin-left:4px}
.tts-btn:hover{color:var(--acc);border-color:var(--acc)}
/* MOBILE */
.sb-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:49}
@media(max-width:768px){
.sidebar{position:fixed;left:0;top:0;bottom:0;z-index:50;transform:translateX(-100%)}
.sidebar.open{transform:none}
.sb-overlay.show{display:block}
.menu-btn{display:flex}
.welcome-cards{grid-template-columns:1fr}
}
.voice-btn{position:absolute!important;right:88px;top:50%;transform:translateY(-50%);z-index:5;width:32px;height:32px;opacity:0.5}.voice-btn:hover{opacity:1}</style>
</head>
<body>
<div class="drop-overlay" id="dropZone"><div class="drop-box">📎 Déposez vos fichiers ici<br><small style="font-size:12px;color:var(--dim)">Images, PDF, code — WEVIA analyse tout</small></div></div>
<button class="scroll-btn" id="scrollBtn" onclick="scrollChat()"></button>
<div class="sb-overlay" id="overlay" onclick="toggleSb()"></div>
<div class="sidebar" id="sidebar">
<div class="sb-head">
<div class="sb-logo">WEVIA<small>Master AI v3.0</small></div>
<button class="sb-new" onclick="newChat()" title="Nouvelle conversation">+</button>
</div>
<div class="sb-label">Agents</div>
<div class="sb-nav">
<div class="sb-item" onclick="q('agent monitor full health check')"><span class="ico">🖥</span>Monitor Infra</div>
<div class="sb-item" onclick="q('agent devops check memory processes docker')"><span class="ico">⚙️</span>DevOps Check</div>
<div class="sb-item" onclick="q('agent ethica count HCP top specialties')"><span class="ico">💊</span>Ethica HCP</div>
<div class="sb-item" onclick="q('agent security scan SSL certificates')"><span class="ico">🔒</span>Security Audit</div>
</div>
<div class="sb-label">Raccourcis</div>
<div class="sb-nav">
<div class="sb-item" onclick="q('Statut complet infrastructure WEVAL S204 S95 S151')"><span class="ico">📊</span>Statut Infra</div>
<div class="sb-item" onclick="q('Liste les providers IA gratuits de WEVIA')"><span class="ico">🤖</span>Providers IA</div>
<div class="sb-item" onclick="q('Combien de vecteurs Qdrant et quelles collections?')"><span class="ico">🧠</span>RAG Status</div>
<div class="sb-item" onclick="q('Quels modèles Ollama sur S204?')"><span class="ico">🎯</span>Modèles Ollama</div>
<div class="sb-item" onclick="q('Comment scraper Ethica DabaDoc Casablanca?')"><span class="ico">🔬</span>Ethica Scraping</div>
<div class="sb-item" onclick="q('Plan souveraineté IA totale WEVAL')"><span class="ico">🏛</span>Souveraineté</div>
<div class="sb-item" onclick="window.open('/api/wevia-master-api.php?dashboard','_blank')"><span class="ico">📈</span>Dashboard</div>
</div>
<div class="sb-foot">
<span class="dot g"></span> 15 providers LIVE | 0€<br>
<span class="dot g"></span> Ollama 9 modèles | 24GB<br>
<span class="dot g"></span> RAG 15,128 vecteurs
</div>
</div>
<div class="main">
<div class="top">
<button class="menu-btn" onclick="toggleSb()"></button>
<span class="top-title">WEVIA Master AI</span>
<div class="pills">
<span class="pill live" id="tp-status">● LIVE</span>
<span class="pill" id="tp-provider"></span>
<span class="pill" id="tp-rag">RAG</span>
</div>
</div>
<div class="chat" id="chat">
<div class="ci" id="ci">
<div class="welcome" id="welcome">
<h1>WEVIA</h1>
<p>IA souveraine — 15 providers — RAG 14K vecteurs — 146 tools — 629 skills</p>
<div class="welcome-cards">
<div class="wcard" onclick="q('Statut infrastructure S204/S95/S151')"><h3>📊 Statut Infrastructure</h3><p>Analyse complète S204/S95/S151</p></div>
<div class="wcard" onclick="q('Scrapers Ethica DabaDoc pour HCP pharma')"><h3>💊 Ethica HCP Pharma</h3><p>Scrapers, DB, enrichissement</p></div>
<div class="wcard" onclick="q('Architecture IA WEVIA Master: router, RAG, agents')"><h3>🧠 Architecture IA</h3><p>Router, RAG, agents, cascade</p></div>
<div class="wcard" onclick="q('Script PHP monitoring Docker et alerte')"><h3>💻 Code Generation</h3><p>Scripts, APIs, monitoring</p></div>
</div>
</div>
</div>
</div>
<div class="input-area">
<div class="input-wrap">
<div class="file-preview" id="filePreview"></div>
<div class="input-box">
<button class="attach-btn" onclick="document.getElementById('fileInput').click()" title="Joindre un fichier">
<svg viewBox="0 0 24 24"><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>
</button>
<textarea id="input" rows="1" placeholder="Demandez à WEVIA..." oninput="resize(this)" onkeydown="onKey(event)" style="padding-left:36px"></textarea>
</div>
<button class="voice-btn" id="voiceBtn mic-inside" onclick="toggleVoice()" title="Parler à WEVIA"><svg viewBox="0 0 24 24"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3zM19 10v2a7 7 0 0 1-14 0v-2H3v2a9 9 0 0 0 8 8.94V23h2v-2.06A9 9 0 0 0 21 12v-2h-2z"/></svg></button>
<button class="send-btn" id="sendBtn" onclick="send()">
<svg viewBox="0 0 24 24"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
</button>
<input type="file" id="fileInput" multiple accept="image/*,.pdf,.txt,.py,.php,.js,.json,.csv,.md" style="display:none" onchange="handleFiles(this.files)">
</div>
<div class="input-hint">
<span>Enter envoyer · Shift+Enter nouvelle ligne · 📎 Glissez-déposez des fichiers</span>
</div>
</div>
</div>
<script>
const STREAM='/api/wevia-autonomous.php';
const AGENT='/api/wevia-agent-loop.php';
let hist=[];
function toggleSb(){document.getElementById('sidebar').classList.toggle('open');document.getElementById('overlay').classList.toggle('show');}
function newChat(){hist=[];document.getElementById('ci').innerHTML=document.querySelector('.welcome')?.outerHTML||'';const w=document.getElementById('welcome');if(w)w.style.display='';}
function resize(el){el.style.height='auto';el.style.height=Math.min(el.scrollHeight,160)+'px';}
function onKey(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send();}}
function q(t){document.getElementById('input').value=t;send();if(innerWidth<768)toggleSb();}
function scrollChat(){document.getElementById('chat').scrollTop=document.getElementById('chat').scrollHeight;}
function esc(s){return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
function md(text){
if(!text)return'';
let h=esc(text);
h=h.replace(/```(\w*)\n?([\s\S]*?)```/g,(_,lang,code)=>`<pre><button class="copy-btn" onclick="copyCode(this)">copier</button><code>${code.trim()}</code></pre>`);
h=h.replace(/`([^`]+)`/g,'<code>$1</code>');
h=h.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');
h=h.replace(/^### (.+)$/gm,'<h3>$1</h3>');
h=h.replace(/^- (.+)$/gm,'<li>$1</li>');
h=h.replace(/\n{2,}/g,'</p><p>');
h=h.replace(/\n/g,'<br>');
return'<p>'+h+'</p>';
}
function copyCode(btn){navigator.clipboard.writeText(btn.nextElementSibling.textContent).then(()=>{btn.textContent='copié!';setTimeout(()=>btn.textContent='copier',1500);});}
function addUser(msg){
const w=document.getElementById('welcome');if(w)w.style.display='none';
const ci=document.getElementById('ci');
const d=document.createElement('div');d.className='msg user';
d.innerHTML=`<div class="bubble">${esc(msg)}</div>`;
ci.appendChild(d);scroll();
}
function showThinking(){
const ci=document.getElementById('ci');
const d=document.createElement('div');d.className='thinking';d.id='thinkBox';
d.innerHTML=`<div class="av">W</div><div class="think-box"><div class="think-steps" id="thinkSteps"></div></div>`;
ci.appendChild(d);scroll();
}
function addThinkStep(label,val){
const steps=document.getElementById('thinkSteps');if(!steps)return;
const s=document.createElement('div');s.className='think-step';
s.innerHTML=`<div class="spinner"></div><span class="label">${label}</span><span class="val">${val||''}</span>`;
steps.appendChild(s);scroll();
return s;
}
function completeStep(step,val){
if(!step)return;
step.classList.add('done');
if(val)step.querySelector('.val').textContent=val;
}
function removeThinking(){const t=document.getElementById('thinkBox');if(t)t.remove();}
function startStream(){
const ci=document.getElementById('ci');
const d=document.createElement('div');d.className='msg ai';
d.innerHTML=`<div class="av">W</div><div class="body"><div class="content" id="streamOut"></div></div>`;
ci.appendChild(d);scroll();
return document.getElementById('streamOut');
}
function finishStream(el,meta){
el.removeAttribute('id');
if(meta){
const body=el.closest('.body');
let h='<div class="meta">';
if(meta.tier!==undefined){const c=meta.tier<=0?'t0':'t1';h+=`<span class="tag ${c}">T${meta.tier} ${meta.provider}</span>`;}
if(meta.model)h+=`<span class="tag">${meta.model.substring(0,25)}</span>`;
if(meta.latency_ms)h+=`<span class="tag">${meta.latency_ms}ms</span>`;
if(meta.rag>0)h+=`<span class="tag rag">RAG ${meta.rag}</span>`;
if(meta.mem>0)h+=`<span class="tag rag">MEM ${meta.mem}</span>`;
h+='<span class="tag">0€</span><button class="tts-btn" onclick="speakText(this.closest(\'\'.msg-body\'\').querySelector(\'\'.content\'\').textContent)">🔊</button></div>';
body.insertAdjacentHTML('beforeend',h);
document.getElementById('tp-provider').textContent=meta.provider;
}
}
function addAIMsg(content,meta){
const ci=document.getElementById('ci');
const d=document.createElement('div');d.className='msg ai';
let metaH='';
if(meta){
metaH='<div class="meta">';
if(meta.provider)metaH+=`<span class="tag t1">${meta.provider}</span>`;
if(meta.latency_ms)metaH+=`<span class="tag">${meta.latency_ms}ms</span>`;
metaH+='<span class="tag">0€</span></div>';
}
d.innerHTML=`<div class="av">W</div><div class="body"><div class="content">${md(content)}</div>${metaH}</div>`;
ci.appendChild(d);scroll();
}
async function send(){
const el=document.getElementById('input');
const msg=el.value.trim();if(!msg)return;
el.value='';el.style.height='auto';
document.getElementById('sendBtn').disabled=true;
addUser(msg);
hist.push({role:'user',content:msg});
const isAgent=/agent\s+(monitor|devops|ethica|security)/i.exec(msg);
try{
if(isAgent){
// AGENT MODE
const id=isAgent[1].toLowerCase();
const goal=msg.replace(isAgent[0],'').trim()||'full check';
showThinking();
const s1=addThinkStep(`Lancement agent ${id}...`);
const s2=addThinkStep('Exécution des étapes...');
const r=await fetch(`${AGENT}?agent=${id}&goal=${encodeURIComponent(goal)}`,{signal:AbortSignal.timeout(120000)});
const data=await r.json();
completeStep(s1,`${data.steps_count||'?'} étapes`);
completeStep(s2,`${data.total_time_ms||'?'}ms`);
setTimeout(()=>{
removeThinking();
let c=`**Agent ${id}** — ${data.steps_count||'?'} étapes | ${data.total_time_ms||'?'}ms\n\n`;
if(data.summary)c+=data.summary+'\n\n';
if(data.steps){c+='**Détail:**\n';for(const s of data.steps){const a=s.action||{};c+=`- **Step ${s.step}**: \`${a.action||'?'}\``;if(a.cmd)c+=`\`${a.cmd.substring(0,80)}\``;c+='\n';if(s.result?.output)c+=`\`\`\`\n${s.result.output.substring(0,300)}\n\`\`\`\n`;}}
addAIMsg(c,{provider:'agent-'+id,latency_ms:data.total_time_ms});
},500);
}else{
// STREAMING MODE avec thinking steps
showThinking();
const s1=addThinkStep('Recherche mémoire...');
const s2=addThinkStep('Recherche RAG...');
const resp=await fetch(STREAM,{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({message:msg,history:hist.slice(-10)}),
signal:AbortSignal.timeout(90000)
});
const reader=resp.body.getReader();
const dec=new TextDecoder();
let buf='',full='',provider='',model='',lat=0,ragN=0,memN=0,started=false,streamEl=null;
while(true){
const{done,value}=await reader.read();
if(done)break;
buf+=dec.decode(value,{stream:true});
const lines=buf.split('\n');buf=lines.pop();
for(const line of lines){
if(!line.startsWith('data: ')||line==='data: [DONE]')continue;
try{
const d=JSON.parse(line.slice(6));
if(d.type==='memory'){
memN=d.count||0;
completeStep(s1,memN>0?`${memN} souvenirs`:'aucun');
}
if(d.type==='rag'){
ragN=d.count||0;
completeStep(s2,ragN>0?`${ragN} résultats | ${d.latency||0}ms`:'aucun');
}
if(d.type==='start'){
provider=d.provider;model=d.model;
const s3=addThinkStep(`Connexion ${d.provider}...`);
completeStep(s3,d.model?.substring(0,25));
setTimeout(()=>{removeThinking();},300);
}
if(d.type==='token'){
if(!started){started=true;streamEl=startStream();}
full+=d.content;
streamEl.innerHTML=md(full);
scroll();
}
if(d.type==='done'){
lat=d.latency_ms;ragN=d.rag||ragN;
}
}catch(e){}
}
}
if(streamEl){
finishStream(streamEl,{tier:1,provider,model,latency_ms:lat,rag:ragN,mem:memN});
}else{
removeThinking();
addAIMsg('Aucune réponse reçue. Réessayez.',{provider:'error'});
}
hist.push({role:'assistant',content:full});
}
}catch(e){
removeThinking();
addAIMsg(`Erreur: ${e.message}. Réessayez avec une question plus courte.`,{provider:'error'});
}
document.getElementById('sendBtn').disabled=false;
el.focus();
}
// === VOICE: Speech-to-Text + Text-to-Speech ===
let recognition = null;
let isRecording = false;
function toggleVoice() {
if (!("webkitSpeechRecognition" in window || "SpeechRecognition" in window)) {
alert("Votre navigateur ne supporte pas la reconnaissance vocale.");
return;
}
if (isRecording) { stopVoice(); return; }
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
recognition = new SR();
recognition.lang = "fr-FR";
recognition.continuous = false;
recognition.interimResults = true;
recognition.onstart = () => {
isRecording = true;
document.getElementById("voiceBtn mic-inside").classList.add("recording");
};
recognition.onresult = (e) => {
let transcript = "";
for (let i = e.resultIndex; i < e.results.length; i++) {
transcript += e.results[i][0].transcript;
}
document.getElementById("input").value = transcript;
resize(document.getElementById("input"));
};
recognition.onend = () => {
isRecording = false;
document.getElementById("voiceBtn mic-inside").classList.remove("recording");
if (document.getElementById("input").value.trim()) send();
};
recognition.onerror = (e) => {
isRecording = false;
document.getElementById("voiceBtn mic-inside").classList.remove("recording");
};
recognition.start();
}
function stopVoice() {
if (recognition) recognition.stop();
isRecording = false;
document.getElementById("voiceBtn mic-inside").classList.remove("recording");
}
function speakText(text) {
if (!("speechSynthesis" in window)) return;
const u = new SpeechSynthesisUtterance(text.replace(/[*#`\[\]]/g, "").substring(0, 500));
u.lang = "fr-FR";
u.rate = 1.1;
speechSynthesis.speak(u);
}
document.getElementById('input').focus();
// === FILE HANDLING ===
let pendingFiles = [];
function handleFiles(fileList) {
for (const f of fileList) {
if (f.size > 10 * 1024 * 1024) { alert('Max 10MB'); continue; }
const reader = new FileReader();
reader.onload = (e) => {
pendingFiles.push({ name: f.name, size: f.size, type: f.type, data: e.target.result });
renderFilePreview();
};
if (f.type.startsWith('image/')) reader.readAsDataURL(f);
else reader.readAsDataURL(f);
}
}
function renderFilePreview() {
const el = document.getElementById('filePreview');
el.innerHTML = pendingFiles.map((f, i) => {
const sz = f.size < 1024 ? f.size + 'B' : (f.size / 1024).toFixed(0) + 'KB';
const img = f.type.startsWith('image/') ? `<img src="${f.data}" alt="">` : '';
return `<div class="file-chip">${img}<span>${f.name}</span><span class="fsize">${sz}</span><span class="remove" onclick="removeFile(${i})">✕</span></div>`;
}).join('');
}
function removeFile(i) { pendingFiles.splice(i, 1); renderFilePreview(); }
// === DRAG & DROP ===
let dragCount = 0;
document.addEventListener('dragenter', (e) => { e.preventDefault(); dragCount++; document.getElementById('dropZone').classList.add('show'); });
document.addEventListener('dragleave', (e) => { e.preventDefault(); dragCount--; if (dragCount <= 0) { dragCount = 0; document.getElementById('dropZone').classList.remove('show'); } });
document.addEventListener('dragover', (e) => e.preventDefault());
document.addEventListener('drop', (e) => {
e.preventDefault(); dragCount = 0;
document.getElementById('dropZone').classList.remove('show');
if (e.dataTransfer.files.length) handleFiles(e.dataTransfer.files);
});
// === SCROLL BUTTON ===
const chatEl = document.getElementById('chat');
chatEl.addEventListener('scroll', () => {
const atBottom = chatEl.scrollHeight - chatEl.scrollTop - chatEl.clientHeight < 100;
document.getElementById('scrollBtn').classList.toggle('show', !atBottom);
});
// === ENHANCED SEND (with files) ===
const origSend = send;
const _send = send;
send = async function() {
if (pendingFiles.length > 0) {
const el = document.getElementById('input');
const msg = el.value.trim() || 'Analyse ce fichier';
el.value = ''; el.style.height = 'auto';
document.getElementById('sendBtn').disabled = true;
// Show user message with file previews
const w = document.getElementById('welcome'); if (w) w.style.display = 'none';
const ci = document.getElementById('ci');
const d = document.createElement('div'); d.className = 'msg user';
let filesHtml = pendingFiles.map(f => {
if (f.type.startsWith('image/')) return `<img class="msg-image" src="${f.data}">`;
return `<div class="file-chip"><span>📄 ${f.name}</span></div>`;
}).join('');
d.innerHTML = `<div class="bubble">${esc(msg)}${filesHtml}</div>`;
ci.appendChild(d); scroll();
hist.push({ role: 'user', content: msg });
// If image, use vision API
const imageFile = pendingFiles.find(f => f.type.startsWith('image/'));
if (imageFile) {
showThinking();
const s1 = addThinkStep('Analyse image via Vision...');
try {
const resp = await fetch('/api/wevia-vision-api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image_base64: imageFile.data.split(',')[1], prompt: msg }),
signal: AbortSignal.timeout(30000)
});
const data = await resp.json();
completeStep(s1, `${data.provider} ${data.latency_ms}ms`);
setTimeout(() => {
removeThinking();
addAIMsg(data.content || 'Analyse terminée.', { provider: data.provider, latency_ms: data.latency_ms });
}, 300);
} catch (e) {
removeThinking();
addAIMsg(`Erreur vision: ${e.message}`, { provider: 'error' });
}
} else {
// Non-image file: include content in message
let fileContent = '';
for (const f of pendingFiles) {
if (f.data.includes('base64,')) {
try { fileContent += '\n\nFichier ' + f.name + ':\n' + atob(f.data.split(',')[1]).substring(0, 5000); } catch(e) {}
}
}
document.getElementById('input').value = msg + fileContent;
pendingFiles = []; renderFilePreview();
document.getElementById('sendBtn').disabled = false;
return _send();
}
pendingFiles = []; renderFilePreview();
document.getElementById('sendBtn').disabled = false;
return;
}
return _send();
};
// === PASTE IMAGE ===
document.getElementById('input').addEventListener('paste', (e) => {
const items = e.clipboardData?.items;
if (!items) return;
for (const item of items) {
if (item.type.startsWith('image/')) {
e.preventDefault();
const file = item.getAsFile();
handleFiles([file]);
}
}
});
</script>
<script>
// WEVIA UX Enhancement v2 — inject inside sidebar properly
document.addEventListener('DOMContentLoaded', function(){
var sb = document.querySelector('.sidebar-items, .sb-items, nav');
if (!sb) { var items = document.querySelectorAll('.sb-item'); if(items.length) sb = items[0].parentElement; }
if (!sb) return;
// Make sidebar scrollable
sb.style.overflowY = 'auto';
sb.style.maxHeight = 'calc(100vh - 180px)';
sb.style.paddingBottom = '60px';
// Add new sections
var html = '';
var tools = [
['consensus expert meilleur LLM','\ud83e\udde0','Consensus'],
['status mirofish','\ud83d\udc1f','MiroFish'],
['status deerflow research','\ud83e\udd8c','DeerFlow'],
['status paperclip CEO agent','\ud83d\udcce','Paperclip'],
['screenshot https://weval-consulting.com','\ud83d\udcf8','Screenshot'],
];
tools.forEach(function(t){ html += '<div class="sb-item" onclick="q(\''+t[0]+'\')"><span class="ico">'+t[1]+'</span>'+t[2]+'</div>'; });
html += '';
var actions = [
['docker containers status','\ud83d\udc33','Docker'],
['git log recent','\ud83d\udcc2','Git'],
['cron list taches actives','\u23f0','Crons'],
['liste boites email','\ud83d\udce7','Email'],
['execute python print(42)','\ud83d\udcbb','Code Exec'],
['nettoie les logs','\ud83e\uddf9','Cleanup'],
];
actions.forEach(function(t){ html += '<div class="sb-item" onclick="q(\''+t[0]+'\')"><span class="ico">'+t[1]+'</span>'+t[2]+'</div>'; });
html += '';
var oss = [
['collections qdrant vectors','\ud83d\udd0d','Qdrant'],
['status n8n workflows','\ud83d\udd04','n8n'],
['nginx config vhosts','\ud83c\udf10','Nginx'],
['diagnostique systeme complet','\ud83d\udd27','Debug'],
['status vault gold backup','\ud83d\udcbe','Backup'],
];
oss.forEach(function(t){ html += '<div class="sb-item" onclick="q(\''+t[0]+'\')"><span class="ico">'+t[1]+'</span>'+t[2]+'</div>'; });
sb.insertAdjacentHTML('beforeend', html);
// Enhance welcome: add 4 more cards
var grid = document.querySelector('[style*="grid-template-columns"]');
if (grid && !document.getElementById('extra-cards-added')) {
var extra = document.createElement('div');
extra.id = 'extra-cards-added';
extra.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:560px;margin:12px auto 0';
extra.innerHTML = [
['scan SSL securite headers','\ud83d\udd12','Security Audit','SSL, headers, ports'],
['consensus expert meilleur LLM 2026','\ud83e\udde0','Consensus IA','3 experts debattent'],
['docker containers status','\ud83d\udc33','Docker Services','24 containers'],
['execute python: import platform; print(platform.node())','\ud83d\udcbb','Code Execution','Python, PHP, Bash'],
].map(function(c){ return '<div onclick="q(\''+c[0]+'\')" style="background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);border-radius:12px;padding:14px;cursor:pointer;transition:border-color .2s" onmouseover="this.style.borderColor=\'rgba(255,255,255,0.2)\'" onmouseout="this.style.borderColor=\'rgba(255,255,255,0.08)\'"><span>'+c[1]+'</span> <b style="color:#eee">'+c[2]+'</b><br><small style="color:#888">'+c[3]+'</small></div>'; }).join('');
grid.parentElement.insertBefore(extra, grid.nextSibling);
}
});
</script>
<script>(function(){
if(document.getElementById('wx5')){return;}
var s=document.createElement('style');
s.textContent=`
/* SIDEBAR CLEAN */
.sb-cat{font-size:9px!important;letter-spacing:1.5px!important;color:rgba(255,255,255,0.22)!important;padding:12px 14px 3px!important;font-weight:600!important}
.sb-item{font-size:11.5px!important;padding:5px 12px!important;border-radius:6px!important;margin:1px 6px!important;transition:all .12s!important;color:rgba(255,255,255,0.5)!important}
.sb-item:hover{background:rgba(100,220,160,0.07)!important;color:#fff!important}
/* WELCOME CENTERED + COMPACT */
.welcome{display:flex!important;flex-direction:column!important;align-items:center!important;justify-content:center!important;text-align:center!important;padding:16px 20px!important;min-height:auto!important}
.welcome h1{font-size:36px!important;font-weight:700!important;letter-spacing:8px!important;background:linear-gradient(135deg,#64dca0,#4a9eff,#a78bfa)!important;-webkit-background-clip:text!important;-webkit-text-fill-color:transparent!important;margin:0 0 4px!important}
.welcome>p{color:rgba(255,255,255,0.25)!important;font-size:12px!important;margin:0 0 16px!important}
/* CARDS - COMPACT 2x4 GRID */
.wx-grid{display:grid!important;grid-template-columns:repeat(2,1fr)!important;gap:8px!important;width:100%!important;max-width:560px!important;margin:0 auto!important}
.wx-grid>div{background:rgba(255,255,255,0.02)!important;border:1px solid rgba(255,255,255,0.06)!important;border-radius:12px!important;padding:12px 14px!important;cursor:pointer!important;transition:all .2s!important;text-align:center!important;display:flex!important;flex-direction:column!important;align-items:center!important;gap:2px!important}
.wx-grid>div:hover{border-color:rgba(100,220,160,0.35)!important;background:rgba(100,220,160,0.04)!important;transform:translateY(-2px)!important;box-shadow:0 8px 20px rgba(0,0,0,0.2)!important}
.wx-grid .ci{font-size:20px!important;margin-bottom:2px!important}
.wx-grid b{color:#ddd!important;font-size:12.5px!important;font-weight:600!important}
.wx-grid small{color:rgba(255,255,255,0.25)!important;font-size:10.5px!important}
/* INPUT - CENTERED BUTTONS */
.input-wrap{display:flex!important;align-items:center!important;gap:6px!important;padding:6px 12px!important;background:rgba(30,30,30,0.95)!important;border:1px solid rgba(255,255,255,0.08)!important;border-radius:16px!important;max-width:700px!important;margin:0 auto!important;width:calc(100% - 40px)!important}
.input-box{flex:1!important;position:relative!important;display:flex!important;align-items:center!important;background:transparent!important;border:none!important;padding:0!important;margin:0!important}
.input-box textarea{background:transparent!important;border:none!important;outline:none!important;color:#eee!important;font-size:14px!important;width:100%!important;padding:6px 8px 6px 36px!important;resize:none!important}
.attach-btn{position:absolute!important;left:8px!important;top:50%!important;transform:translateY(-50%)!important;z-index:5!important;opacity:0.35!important;transition:opacity .2s!important;background:none!important;border:none!important;cursor:pointer!important}
.attach-btn:hover{opacity:0.8!important}
.voice-btn{flex-shrink:0!important;opacity:0.3!important;transition:opacity .2s!important;background:none!important;border:none!important;cursor:pointer!important;position:static!important;transform:none!important}
.voice-btn:hover{opacity:0.8!important}
.send-btn{flex-shrink:0!important;opacity:0.9!important;background:rgb(100,220,160)!important;border:none!important;border-radius:10px!important;width:36px!important;height:36px!important;cursor:pointer!important;display:flex!important;align-items:center!important;justify-content:center!important;transition:all .2s!important;position:static!important;transform:none!important}
.send-btn:hover{background:rgb(80,200,140)!important;transform:scale(1.05)!important}
.file-preview{display:none!important}
/* NO DOUBLE SCROLLBAR */
.sidebar{overflow-y:auto!important;overflow-x:hidden!important}
.sidebar::-webkit-scrollbar{width:2px}
.sidebar::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.04);border-radius:2px}
`;
document.head.appendChild(s);
/* Clean wrong items */
['AnythingLLM','CrewAI','Flowise','AIOS','Browser-Use'].forEach(function(w){
document.querySelectorAll('.sb-item').forEach(function(el){if(el.textContent.indexOf(w)>=0)el.remove();});
});
var seen={};document.querySelectorAll('.sb-cat').forEach(function(c){var t=c.textContent.trim();if(seen[t])c.remove();seen[t]=1;});
/* Sidebar sections */
console.log('WX5 SB SEARCH',document.querySelectorAll('.sb-item').length);var sb=document.querySelector('.sb-item');if(!sb)return;
var p=sb.parentElement;
p.style.overflowY='auto';p.style.maxHeight='calc(100vh - 150px)';p.style.paddingBottom='40px';
function S(cat,items){
var h='<div class="sb-cat">'+cat+'</div>';
items.forEach(function(i){h+='<div class="sb-item" data-w5="1" onclick="q(\''+i[2]+'\')"><span class="ico">'+i[0]+'</span> '+i[1]+'</div>';});
p.insertAdjacentHTML('beforeend',h);
}
if(!document.querySelector('[data-w5]')){
S('\u2728 IA AGENTS',[['\ud83e\udde0','Consensus','consensus expert meilleur LLM'],['\ud83d\udc1f','MiroFish','status mirofish'],['\ud83e\udd8c','DeerFlow','status deerflow'],['\ud83d\udcce','Paperclip','status paperclip'],['\ud83e\udd16','DeepAgent','status deepagent'],['\ud83e\uddea','Opus Deep','analyse profonde']]);
S('\ud83d\udcbb CODE',[['\ud83d\udc07','CodeRabbit','code review'],['\ud83e\udd80','Claw-Code','status claw-code'],['\ud83d\udd0d','Nuclei','scan nuclei'],['\ud83d\udee1','CrowdSec','status CrowdSec'],['\ud83d\udd12','SSL','scan SSL'],['\u2328','Execute','execute python print(42)']]);
S('\ud83d\ude80 OPS',[['\ud83d\udc33','Docker','docker containers'],['\ud83c\udf10','Nginx','nginx config'],['\ud83d\udcc2','Git','git log'],['\u23f0','Crons','cron list'],['\ud83d\udce7','Email','liste email'],['\ud83d\udcf8','Screenshot','screenshot weval-consulting.com'],['\ud83e\uddf9','Cleanup','nettoie logs'],['\ud83d\udcbe','Backup','status vault gold']]);
S('\ud83e\udde9 OSS',[['\ud83d\udd0d','Qdrant','collections qdrant'],['\ud83d\udd04','n8n','status n8n'],['\ud83c\udf0e','SearXNG','recherche WEVAL'],['\ud83e\udd86','Goose','status goose'],['\ud83c\udf10','Browser','status browser-use'],['\ud83d\udcda','AnythingLLM','status anythingllm'],['\ud83e\udde9','CrewAI','status crewai'],['\ud83d\udd17','Flowise','status flowise'],['\ud83e\uddbe','AIOS','status aios'],['\ud83d\udd2e','Dify','status dify'],['\ud83d\udcda','554 Skills','liste skills']]);
S('\ud83d\udca4 DREAM ENGINE',[['\ud83c\udf19','autoDream','dream consolider memoires Qdrant'],['\u26a1','KAIROS','kairos daemon proactif alertes'],['\ud83d\udcdd','ULTRAPLAN','ultraplan architecture microservices'],['\ud83d\udddc','Compress','compress texte contexte'],['\ud83e\ude79','Self-Heal','heal memoire contradictions']]);
S('\ud83c\udfe2 ENTERPRISE',[['\ud83d\udcca','Dashboard','dashboard entreprise WEVAL stats'],['\ud83c\udfaf','Strategie','strategie WEVAL Consulting 2026'],['\ud83d\udcc8','Pipeline','pipeline commercial deals actifs'],['\ud83d\udcb0','Financier','plan financier revenus couts'],['\ud83d\uddd3','Roadmap','roadmap produit Q2 Q3 2026'],['\u2694','Concurrents','analyse concurrentielle Maroc'],['\ud83e\udd1d','Partenaires','strategie partenaires SAP Huawei Vistex'],['\ud83d\udc65','RH','plan recrutement equipe 2026'],['\ud83d\udcca','KPIs','indicateurs performance WEVAL'],['\ud83d\ude80','Evolution','plan evolution entreprise 12 mois']]);
S('\ud83c\udfdb ECOSYSTEM WEVAL',[['\ud83d\udce7','WEVIA Life','status wevia-life emails opps'],['\ud83d\udda5','Blade IA','status blade agent razer'],['\ud83d\udee0','PowerToys','ouvre powertoys DNS SSL QR Hash'],['\ud83d\udcbc','CRM','status CRM deals'],['\u2705','L99 Tests','status L99 passed total'],['\ud83d\udccb','NonReg','status nonreg 153 tests'],['\ud83c\udfd7','Architecture','architecture WEVIA complete'],['\ud83d\ude80','Ops Center','status ops-center 36 tools'],['\ud83c\udf0e','Tech Radar','status tech-radar 101 tools'],['\ud83d\udcdd','WEVADS','status wevads 79 modules'],['\ud83d\udcc5','Booking','booking calendly WEVAL']]);
S('\ud83d\udd75 DARK & ARSENAL',[['\ud83d\udd0d','Nuclei Scan','scan nuclei vulnerabilites weval-consulting.com'],['\ud83d\udd12','Gitleaks','scan gitleaks secrets code'],['\ud83d\udee1','Trivy','scan trivy containers vulnerabilites'],['\ud83d\udd75','Sherlock','sherlock OSINT username'],['\ud83d\udce7','Holehe','holehe OSINT email'],['\ud83c\udf10','Amass','amass subdomains weval-consulting.com'],['\ud83d\udcca','ADX Stats','status ADX contacts campaigns'],['\ud83d\udc8a','Ethica DB','status ethica medecins validated'],['\ud83d\udd04','Arsenal','status arsenal sentinel S95'],['\ud83d\udd77','Scrapers','status scrapers ethica']]);
S('\u26a1 PROVIDERS',[['\ud83c\udf1f','13 Providers','liste providers IA'],['\ud83e\udde0','R1 671B','test deepseek R1'],['\ud83d\ude80','Cerebras','test cerebras'],['\ud83d\udcca','Ollama','modeles Ollama']]);
}
/* Replace welcome cards - COMPACT */
var wc=document.querySelector('.welcome-cards');
if(wc){
var g=document.createElement('div');g.className='wx-grid';
g.innerHTML=[
['\ud83d\udcca','Audit Infra','S204\u2022S95\u2022S151','audit complet RAM disk Docker'],
['\ud83d\udc8a','Ethica HCP','131K m\u00e9decins','status ethica count HCP'],
['\ud83e\udde0','Consensus','3 IA experts','consensus expert stack IA'],
['\ud83d\udd12','Security','SSL\u2022Nuclei','scan SSL nuclei securite'],
['\ud83d\udc33','Docker','24 containers','docker containers status'],
['\ud83d\udcbb','Code Exec','Python\u2022PHP','execute python: print(42)'],
['\ud83e\udd8c','Research','800 skills','deerflow recherche IA'],
['\ud83d\ude80','Architecture','182 routes','architecture WEVIA Master'],
].map(function(c){return'<div onclick="q(\''+c[3]+'\')"><span class="ci">'+c[0]+'</span><b>'+c[1]+'</b><br><small>'+c[2]+'</small></div>';}).join('');
wc.parentElement.replaceChild(g,wc);
}
var wsub=document.querySelector('.welcome>p');
if(wsub)wsub.textContent='IA souveraine \u2014 15 providers \u2014 R1 671B \u2014 146 tools \u2014 800 skills \u2014 0\u20ac';
document.createElement('div').id='wx5';document.body.appendChild(document.getElementById('wx5')||Object.assign(document.createElement('div'),{id:'wx5'}));
})();
/* WEVIA Stream Premium — Rich response rendering */
(function(){
if(document.getElementById('wsp'))return;
var s=document.createElement('style');
s.textContent=`
/* RICH MESSAGE RENDERING */
.msg-assistant{font-family:'Plus Jakarta Sans','Inter',sans-serif!important}
.msg-assistant pre{background:rgba(0,0,0,0.3)!important;border:1px solid rgba(255,255,255,0.08)!important;border-radius:10px!important;padding:14px!important;overflow-x:auto!important;font-size:12px!important;line-height:1.5!important}
.msg-assistant code{font-family:'JetBrains Mono',monospace!important;font-size:12px!important;background:rgba(255,255,255,0.06)!important;padding:1px 5px!important;border-radius:4px!important}
.msg-assistant pre code{background:none!important;padding:0!important}
.msg-assistant h1,.msg-assistant h2,.msg-assistant h3{color:#64dca0!important;font-weight:600!important;margin:12px 0 6px!important}
.msg-assistant h1{font-size:18px!important;border-bottom:1px solid rgba(255,255,255,0.08)!important;padding-bottom:6px!important}
.msg-assistant h2{font-size:15px!important}
.msg-assistant h3{font-size:13px!important;color:rgba(255,255,255,0.5)!important}
.msg-assistant strong{color:#e8e8e8!important}
.msg-assistant ul,.msg-assistant ol{padding-left:20px!important;margin:6px 0!important}
.msg-assistant li{margin:3px 0!important;line-height:1.5!important}
.msg-assistant table{border-collapse:collapse!important;width:100%!important;margin:8px 0!important;font-size:12px!important}
.msg-assistant th{background:rgba(100,220,160,0.1)!important;color:#64dca0!important;padding:6px 10px!important;text-align:left!important;font-weight:600!important;border-bottom:1px solid rgba(255,255,255,0.1)!important}
.msg-assistant td{padding:5px 10px!important;border-bottom:1px solid rgba(255,255,255,0.04)!important}
.msg-assistant tr:hover td{background:rgba(255,255,255,0.02)!important}
/* ACTION BADGES */
.action-badge{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:8px;font-size:11px;font-weight:500;margin:2px 4px 2px 0}
.action-badge.exec{background:rgba(100,220,160,0.1);color:#64dca0;border:1px solid rgba(100,220,160,0.2)}
.action-badge.error{background:rgba(255,80,80,0.1);color:#ff5050;border:1px solid rgba(255,80,80,0.2)}
.action-badge.info{background:rgba(80,160,255,0.1);color:#50a0ff;border:1px solid rgba(80,160,255,0.2)}
/* STATS CARDS */
.stat-row{display:flex;gap:8px;margin:8px 0;flex-wrap:wrap}
.stat-card{background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);border-radius:10px;padding:10px 14px;flex:1;min-width:100px;text-align:center}
.stat-card .stat-value{font-size:20px;font-weight:700;color:#64dca0}
.stat-card .stat-label{font-size:10px;color:rgba(255,255,255,0.35);text-transform:uppercase;letter-spacing:1px;margin-top:2px}
/* PROGRESS */
.stream-progress{display:flex;align-items:center;gap:8px;padding:8px 12px;background:rgba(255,255,255,0.02);border-radius:8px;margin:4px 0;font-size:12px;color:rgba(255,255,255,0.4)}
.stream-progress .dot{width:6px;height:6px;border-radius:50%;background:#64dca0;animation:blink 1.2s infinite}
@keyframes blink{0%,100%{opacity:1}50%{opacity:0.2}}
/* COPY BUTTON */
.copy-btn{position:absolute;top:6px;right:6px;background:rgba(255,255,255,0.08);border:none;border-radius:4px;color:rgba(255,255,255,0.4);padding:3px 8px;font-size:10px;cursor:pointer;opacity:0;transition:opacity .2s}
pre:hover .copy-btn{opacity:1}
.copy-btn:hover{color:#fff;background:rgba(255,255,255,0.15)}
`;
document.head.appendChild(s);
// Post-process messages: convert markdown-like to rich HTML
var observer = new MutationObserver(function(mutations){
mutations.forEach(function(m){
m.addedNodes.forEach(function(node){
if(node.nodeType===1 && (node.classList.contains('msg')||node.classList.contains('bubble')||node.querySelector)){
// Find text content that looks like markdown
var el = node.classList.contains('msg-assistant') ? node : node.querySelector('.msg-assistant,.assistant');
if(!el||el.dataset.rendered) return;
var html = el.innerHTML;
// Convert code blocks
html = html.replace(/\x60\x60\x60(\w*)\n?([\s\S]*?)\x60\x60\x60/g, function(m,lang,code){
return '<pre style="position:relative"><code class="lang-'+lang+'">'+code.trim()+'</code><button class="copy-btn" onclick="navigator.clipboard.writeText(this.previousElementSibling.textContent)">copy</button></pre>';
});
// Convert **bold**
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong></strong>');
// Convert inline code
html = html.replace(/\x60([^\x60]+)\x60/g, '<code></code>');
// Convert ## headers
html = html.replace(/^### (.+)$/gm, '<h3></h3>');
html = html.replace(/^## (.+)$/gm, '<h2></h2>');
html = html.replace(/^# (.+)$/gm, '<h1></h1>');
// Convert - lists
html = html.replace(/^- (.+)$/gm, '<li></li>');
if(html !== el.innerHTML){
el.innerHTML = html;
el.dataset.rendered = '1';
}
}
});
});
});
var chatArea = document.querySelector('.chat-messages,.messages,.chat-area');
if(chatArea){
observer.observe(chatArea, {childList:true, subtree:true});
}
var d=document.createElement('div');d.id='wsp';document.body.appendChild(d);
})();
/* ═══ WEVIA AUTO-AUDIT UX FIXES (self-recommended) ═══ */
(function(){
if(document.getElementById('wux'))return;
var s=document.createElement('style');
s.id='wux-css';
s.textContent=`
/* 1. CARDS: hover premium avec translateY + glow */
.wcard{transition:transform .22s cubic-bezier(.22,1,.36,1),box-shadow .22s ease!important;cursor:pointer!important}
.wcard:hover{transform:translateY(-3px)!important;box-shadow:0 8px 25px rgba(100,220,160,0.15),0 4px 10px rgba(0,0,0,0.2)!important;border-color:rgba(100,220,160,0.3)!important}
.wcard:active{transform:translateY(0)!important;transition:.08s!important}
/* 2. SIDEBAR: active state + hover glow */
.sb-item{transition:all .18s ease!important;border-radius:8px!important;margin:1px 6px!important;padding:7px 10px!important}
.sb-item:hover{background:rgba(100,220,160,0.08)!important;padding-left:14px!important}
.sb-item:active{background:rgba(100,220,160,0.15)!important;transform:scale(0.98)}
/* 3. MICRO-ANIMATIONS: tout element interactif */
button,a,.send-btn,.voice-btn,.attach-btn{transition:all .2s ease!important}
button:hover,.send-btn:hover{transform:scale(1.05)!important;filter:brightness(1.15)!important}
.send-btn:active,.voice-btn:active{transform:scale(0.95)!important}
/* 4. CONTRASTE: texte secondaire plus lisible */
.wcard>div:last-child{color:rgba(255,255,255,0.5)!important}
.sb-cat{color:rgba(100,220,160,0.7)!important;letter-spacing:1.5px!important;font-size:10px!important;font-weight:700!important}
/* 5. INPUT: focus glow premium */
textarea:focus{box-shadow:0 0 0 2px rgba(100,220,160,0.3),0 0 20px rgba(100,220,160,0.05)!important;border-color:rgba(100,220,160,0.4)!important}
.input-wrap{transition:box-shadow .2s ease!important}
.input-wrap:focus-within{box-shadow:0 0 0 1px rgba(100,220,160,0.2),0 4px 20px rgba(0,0,0,0.15)!important}
/* 6. TITLE: plus grand + meilleur gradient */
.welcome h1{font-size:52px!important;letter-spacing:12px!important;background:linear-gradient(135deg,#64dca0,#50b4ff,#a080ff)!important;-webkit-background-clip:text!important;-webkit-text-fill-color:transparent!important;font-weight:800!important}
.welcome p{font-size:13px!important;color:rgba(255,255,255,0.45)!important;letter-spacing:0.5px!important}
/* 7. SCROLLBAR premium */
::-webkit-scrollbar{width:5px!important}
::-webkit-scrollbar-track{background:transparent!important}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.1)!important;border-radius:10px!important}
::-webkit-scrollbar-thumb:hover{background:rgba(100,220,160,0.3)!important}
/* 8. MESSAGE BUBBLES: premium */
.msg{border-radius:14px!important;padding:14px 18px!important;line-height:1.65!important;font-size:13.5px!important}
.msg-assistant{background:rgba(255,255,255,0.03)!important;border:1px solid rgba(255,255,255,0.05)!important}
/* 9. STATUS BAR: glass effect */
.status-bar,.footer-stats{backdrop-filter:blur(10px)!important;background:rgba(20,20,30,0.8)!important}
/* 10. ANIMATIONS: smooth page load */
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.wcard{animation:fadeIn .4s ease backwards!important}
.wcard:nth-child(1){animation-delay:0s!important}
.wcard:nth-child(2){animation-delay:.05s!important}
.wcard:nth-child(3){animation-delay:.1s!important}
.wcard:nth-child(4){animation-delay:.15s!important}
.wcard:nth-child(5){animation-delay:.2s!important}
.wcard:nth-child(6){animation-delay:.25s!important}
.wcard:nth-child(7){animation-delay:.3s!important}
.wcard:nth-child(8){animation-delay:.35s!important}
.sb-item{animation:fadeIn .3s ease backwards!important}
`;
document.head.appendChild(s);
var d=document.createElement('div');d.id='wux';document.body.appendChild(d);
})();
</script><script defer src="wevia-claude.js?v=2"></script></body>
</html>