Files
html/wevia-console.html
Opus 9a5f534d8b
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
phase30 doctrine 167 cascade enrich 6 pages - 18 pages UX doctrine 60 total
6 pages centrales enrichies via cascade Cerebras:
- wevia-chat-v2 (+1328B)
- sovereign-monitor (+1271B)
- wevia-audit (+1266B)
- wevia-console (+1263B)
- wevia-autonomy-dashboard (+1292B)
- wevia-business-visual-studio (+1300B)

Handler /var/www/html/api/enrich-hub-cascade.sh:
- Try Cerebras qwen-3-235b primary
- Fallback Ollama llama3.2 LOCAL (zero rate limit)
- GOLD backup + chattr handling + lint
- Markers DOCTRINE-60-UX-ENRICH idempotent

Intent wevia_enrich_hub_cascade_cerebras_ollama wired pour chat NL.
Total: 18 pages UX doctrine 60 (12 avant + 6 aujourd hui).

Cascade zero-rate-limit effective: Cerebras OK + Ollama llama3.2 ready.
Disk 87% stable apres recovery phase 29 +19GB.
2026-04-24 10:44:16 +02:00

459 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WEVIA Console</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Outfit:wght@300;400;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.0/mermaid.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#0a0a12;--bg2:#12121e;--bg3:#1a1a2e;--tx:#e2e8f0;--tx2:#64748b;--acc:#7c3aed;--acc2:#a78bfa;--border:#1e293b;--green:#10b981;--red:#ef4444;--blue:#3b82f6}
body{font-family:'Outfit',system-ui,sans-serif;background:var(--bg);color:var(--tx);min-height:100vh;display:flex;flex-direction:column}
.header{padding:14px 24px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:16px;background:var(--bg2)}
.header .logo{font-size:20px;font-weight:800;letter-spacing:-0.5px;background:linear-gradient(135deg,var(--acc),var(--acc2));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.header .version{font-size:11px;padding:3px 10px;border-radius:20px;background:rgba(124,58,237,0.15);color:var(--acc2);font-weight:600}
.main{flex:1;display:flex;overflow:hidden}
.chat-col{flex:1;display:flex;flex-direction:column;min-width:0}
.preview-col{width:0;transition:width .4s cubic-bezier(.4,0,.2,1);border-left:0 solid var(--border);overflow:hidden;background:var(--bg2)}
.preview-col.open{width:50%;border-left-width:1px}
.preview-col .ph{padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.preview-col .ph .title{font-size:13px;font-weight:700;display:flex;align-items:center;gap:8px}
.preview-col .ph button{background:none;border:none;color:var(--tx2);cursor:pointer;font-size:16px;padding:4px 8px;border-radius:6px}
.preview-col .ph button:hover{background:rgba(255,255,255,.06);color:var(--tx)}
.preview-col iframe{width:100%;height:calc(100% - 48px);border:0}
.msgs{flex:1;overflow-y:auto;padding:24px}
.msg{margin:0 0 20px;max-width:780px}
.msg.user{margin-left:auto;text-align:right}
.msg.user .bubble{background:var(--bg3);border:1px solid var(--border);display:inline-block;padding:10px 18px;border-radius:16px 16px 4px 16px;font-size:14px}
.msg.ai .bubble{line-height:1.7;font-size:14.5px}
.msg .meta{font-size:11px;color:var(--tx2);margin-top:6px;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.badge{padding:2px 8px;border-radius:6px;font-size:10px;font-weight:600}
.badge.prov{background:rgba(124,58,237,.15);color:var(--acc2)}
.badge.time{background:rgba(16,185,129,.12);color:var(--green)}
/* Code blocks */
.code-wrap{border-radius:10px;overflow:hidden;margin:12px 0;border:1px solid rgba(124,58,237,.12)}
.code-head{display:flex;justify-content:space-between;align-items:center;padding:6px 14px;background:#161b22;font-size:11px;font-weight:600;text-transform:uppercase;color:var(--acc2)}
.code-head button{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.08);color:var(--tx2);padding:3px 10px;border-radius:6px;cursor:pointer;font-size:11px;font-family:inherit}
.code-head button:hover{background:rgba(255,255,255,.12);color:var(--tx)}
pre{margin:0!important}pre code{font-family:'JetBrains Mono',monospace!important;font-size:12.5px!important;line-height:1.7!important;padding:14px!important}
/* SVG renders */
.svg-render{text-align:center;padding:20px;background:linear-gradient(135deg,rgba(124,58,237,.04),rgba(34,211,238,.03));border-radius:12px;margin:12px 0;border:1px solid rgba(124,58,237,.1)}
.svg-render svg{max-width:180px;height:auto;display:inline-block;margin:8px 12px;filter:drop-shadow(0 4px 12px rgba(0,0,0,.15))}
.svg-render .label{font-size:11px;color:var(--acc2);font-weight:600;margin-bottom:8px}
/* Mermaid */
.mermaid-wrap{background:#fafafa;border-radius:12px;padding:16px;margin:12px 0;text-align:center;border:1px solid #e2e8f0}
.mermaid-wrap svg{max-width:100%;height:auto}
/* Artifact link */
.art-link{display:inline-flex;align-items:center;gap:8px;padding:10px 20px;background:linear-gradient(135deg,var(--acc),#6366f1);color:#fff;border-radius:10px;text-decoration:none;font-weight:600;font-size:13px;margin:8px 0;transition:all .2s;box-shadow:0 4px 16px rgba(124,58,237,.3)}
.art-link:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(124,58,237,.4)}
/* PDF link */
.pdf-link{display:inline-flex;align-items:center;gap:8px;padding:10px 20px;background:linear-gradient(135deg,#059669,var(--green));color:#fff;border-radius:10px;text-decoration:none;font-weight:600;font-size:13px;margin:8px 0;box-shadow:0 4px 16px rgba(16,185,129,.3)}
/* Progress bar */
.progress{height:3px;background:var(--bg3);overflow:hidden;position:relative}
.progress .bar{height:100%;background:linear-gradient(90deg,var(--acc),var(--acc2),var(--acc));width:0%;border-radius:2px;animation:progress 2s ease-in-out infinite}
@keyframes progress{0%{width:0;margin-left:0}50%{width:60%}100%{width:0;margin-left:100%}}
/* Input */
.input-area{padding:16px 24px;border-top:1px solid var(--border);background:var(--bg2)}
.input-row{display:flex;gap:8px;max-width:780px;margin:0 auto}
.input-row input{flex:1;padding:12px 18px;border-radius:12px;border:1px solid var(--border);background:var(--bg3);color:var(--tx);font-size:14px;font-family:inherit;outline:none;transition:border .2s}
.input-row input:focus{border-color:var(--acc)}
.input-row button{padding:12px 24px;border-radius:12px;border:none;background:var(--acc);color:#fff;cursor:pointer;font-weight:700;font-size:14px;font-family:inherit;transition:all .2s}
.input-row button:hover{background:#6d28d9;transform:translateY(-1px)}
.input-row button:disabled{opacity:.4;cursor:default;transform:none}
.hint{font-size:11px;color:var(--tx2);text-align:center;margin-top:8px}
strong{color:#c4b5fd}
</style>
<!-- DOCTRINE-60-UX-ENRICH cerebras-qwen-235b 20260424-104127 --><style id="doctrine60-ux-wevia-console">
body::before {
content: '';
position: fixed;
width: 100%;
height: 100%;
background: radial-gradient(circle, rgba(0,0,0,0.12), transparent 70%);
z-index: -1;
pointer-events: none;
}
.card, .btn, .kpi, .panel {
opacity: 0;
transform: translateY(20px);
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.enter-stagger {
opacity: 1;
transform: translateY(0);
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
.pulse, .active, .live-indicator, .online {
animation: pulse 3s ease-in-out infinite;
}
.card:hover {
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
border-color: var(--accent);
}
.modal, .chat, .speech, .overlay {
backdrop-filter: blur(12px);
}
</style>
</head>
<body>
<div class="header">
<span class="logo">WEVIA</span>
<span class="version">Console v2</span>
<span style="flex:1"></span>
<span style="font-size:12px;color:var(--tx2)">Intelligence Artificielle Cognitive</span>
</div>
<div id="prog" class="progress" style="display:none"><div class="bar"></div></div>
<div class="main">
<div class="chat-col">
<div class="msgs" id="msgs"></div>
<div class="input-area">
<div class="input-row">
<input id="inp" placeholder="Dashboard React, PDF cybersécurité, logo DataMaroc, diagramme mermaid..." autofocus>
<button id="btn" onclick="send()">Envoyer</button>
</div>
<div class="hint">WEVIA Engine — Groq · Cerebras · SambaNova · Ollama sovereign</div>
</div>
</div>
<div class="preview-col" id="preview">
<div class="ph">
<span class="title" id="prevTitle">📄 Preview</span>
<div>
<button onclick="window.open(document.querySelector('#preview iframe')?.src,'_blank')" title="Nouvel onglet"></button>
<button onclick="document.getElementById('preview').classList.remove('open')" title="Fermer"></button>
</div>
</div>
</div>
</div>
<script>
mermaid.initialize({startOnLoad:false,theme:'neutral',securityLevel:'loose'});
var inp=document.getElementById('inp'),btn=document.getElementById('btn'),conv='c_'+Date.now();
var history=[];
inp.addEventListener('keydown',function(e){if(e.key==='Enter'&&!btn.disabled)send()});
function send(){
var msg=inp.value.trim();if(!msg)return;
inp.value='';btn.disabled=true;
addMsg('user',msg);
document.getElementById('prog').style.display='';
var t0=performance.now();
fetch('/api/wevia-json-api.php',{
method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({message:msg,lang:'fr',mode:'fast',conversation_id:conv,history:history.slice(-6)})
})
.then(function(r){return r.json()})
.then(function(d){
var s=((performance.now()-t0)/1000).toFixed(1);
var resp=d.response||'Pas de réponse.';
history.push({role:'user',content:msg});
history.push({role:'assistant',content:resp.substring(0,500)});
var rendered=renderMd(resp);
// PDF button
if(d.pdf_url){
rendered+='<a class="pdf-link" href="'+d.pdf_url+'" target="_blank">📄 Télécharger le PDF</a>';
}
addMsg('ai',rendered,'<span class="badge prov">'+(d.provider||'?')+'</span><span class="badge time">⚡ '+s+'s</span>');
// Post-render: mermaid + SVG + artifact links + highlight
setTimeout(function(){
// Mermaid
document.querySelectorAll('.mermaid-src').forEach(function(el){
var code=el.textContent;
var div=document.createElement('div');
div.className='mermaid-wrap';
var id='mm_'+Date.now()+'_'+Math.random().toString(36).substr(2,4);
try{
mermaid.render(id,code).then(function(r){div.innerHTML=r.svg;el.replaceWith(div)}).catch(function(){});
}catch(e){}
});
// SVG inline render
document.querySelectorAll('.svg-src').forEach(function(el){
var code=el.textContent;
if(code.indexOf('<svg')===-1)return;
var wrap=document.createElement('div');
wrap.className='svg-render';
wrap.innerHTML='<div class="label">🎨 Logo SVG</div>'+code;
var svgs=wrap.querySelectorAll('svg');
svgs.forEach(function(s){s.style.maxWidth='180px';s.style.height='auto'});
el.replaceWith(wrap);
});
// Artifact links → auto-open preview
document.querySelectorAll('a[href*="artifact_"]').forEach(function(a){
a.className='art-link';
a.innerHTML='⚛️ '+a.textContent;
var url=a.getAttribute('href');
openPreview(url,'Dashboard React');
});
// Highlight code
document.querySelectorAll('pre code').forEach(function(b){hljs.highlightElement(b)});
},300);
})
.catch(function(e){addMsg('ai','<span style="color:var(--red)">❌ Erreur: '+e.message+'</span>')})
.finally(function(){btn.disabled=false;document.getElementById('prog').style.display='none';inp.focus()});
}
function addMsg(role,html,meta){
var d=document.createElement('div');d.className='msg '+role;
d.innerHTML='<div class="bubble">'+html+'</div>'+(meta?'<div class="meta">'+meta+'</div>':'');
document.getElementById('msgs').appendChild(d);
document.getElementById('msgs').scrollTop=99999;
}
function openPreview(url,title){
document.getElementById('prevTitle').innerHTML='📄 '+(title||'Preview');
// Remove old iframe
var old=document.querySelector('#preview iframe');if(old)old.remove();
var ifr=document.createElement('iframe');ifr.src=url;
document.getElementById('preview').appendChild(ifr);
document.getElementById('preview').classList.add('open');
}
function renderMd(text){
// Extract code blocks
var blocks=[];
text=text.replace(/```(\w*)\n([\s\S]*?)```/g,function(m,lang,code){
var i=blocks.length;
blocks.push({lang:lang.toLowerCase(),code:code});
return '\x00BLK'+i+'\x00';
});
// Escape HTML
text=text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
// Markdown
text=text.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');
text=text.replace(/\*([^*]+)\*/g,'<em>$1</em>');
text=text.replace(/`([^`]+)`/g,'<code style="background:var(--bg3);padding:2px 6px;border-radius:4px;font-family:JetBrains Mono,monospace;font-size:12px">$1</code>');
text=text.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" style="color:var(--blue);text-decoration:underline">$1</a>');
text=text.replace(/^### (.+)$/gm,'<h3 style="margin:16px 0 8px;font-size:15px;color:var(--acc2)">$1</h3>');
text=text.replace(/^## (.+)$/gm,'<h2 style="margin:20px 0 10px;font-size:17px">$1</h2>');
text=text.replace(/^# (.+)$/gm,'<h1 style="margin:24px 0 12px;font-size:20px">$1</h1>');
text=text.replace(/^[-*] (.+)$/gm,'<div style="padding-left:16px">• $1</div>');
text=text.replace(/^> (.+)$/gm,'<blockquote style="border-left:3px solid var(--acc);padding:8px 16px;margin:8px 0;color:var(--tx2);background:rgba(124,58,237,.04);border-radius:0 8px 8px 0">$1</blockquote>');
text=text.replace(/^---$/gm,'<hr style="border:none;border-top:1px solid var(--border);margin:16px 0">');
text=text.replace(/\n\n/g,'<br><br>');
text=text.replace(/\n/g,'<br>');
// Restore blocks
text=text.replace(/\x00BLK(\d+)\x00/g,function(m,idx){
var b=blocks[parseInt(idx)];
if(!b)return '';
if(b.lang==='mermaid'){
return '<div class="mermaid-src" style="display:none">'+b.code+'</div><div class="mermaid-wrap"><p style="color:#64748b;font-style:italic">Rendu du diagramme...</p></div>';
}
if(b.lang==='svg'){
return '<div class="svg-src" style="display:none">'+b.code+'</div><div class="svg-render"><p style="color:var(--acc2);font-style:italic">Rendu SVG...</p></div>';
}
var safe=b.code.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
return '<div class="code-wrap"><div class="code-head"><span>'+(b.lang||'code').toUpperCase()+'</span><button onclick="navigator.clipboard.writeText(this.closest(\'.code-wrap\').querySelector(\'code\').textContent)">Copier</button></div><pre><code class="language-'+(b.lang||'plaintext')+'">'+safe+'</code></pre></div>';
});
return text;
}
</script>
<!-- CARTO_REMOVED -->
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
<script>
(function(){
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
var d = document;
var m = d.createElement('div');
m.id = 'opus-udrill';
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
var inner = d.createElement('div');
inner.id = 'opus-udrill-in';
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
inner.addEventListener('click', function(e){ e.stopPropagation(); });
m.appendChild(inner);
m.addEventListener('click', function(){ m.style.display='none'; });
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
(d.body || d.documentElement).appendChild(m);
function openCard(card) {
// Clone card content + show close btn + increase font-size
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
inner.innerHTML = html;
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
m.style.display = 'flex';
}
function wire(root) {
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
var cards = root.querySelectorAll(sels);
for (var i = 0; i < cards.length; i++) {
var c = cards[i];
if (c.__opusWired) continue;
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
var r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 40) continue;
c.__opusWired = true;
c.style.cursor = 'pointer';
c.setAttribute('role','button');
c.setAttribute('tabindex','0');
c.addEventListener('click', function(ev){
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
if (ev.target.closest('a,button,input,select')) return;
ev.preventDefault(); ev.stopPropagation();
openCard(this);
});
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
}
}
// Initial + mutation observer
var initRun = function(){ wire(d.body || d.documentElement); };
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
else initRun();
var mo = new MutationObserver(function(muts){
var newCard = false;
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
if (newCard) initRun();
});
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
<script src="/api/archi-meta-badge.js" defer></script>
<script src="/api/a11y-auto-enhancer.js" defer></script>
<!-- WTP_UDOCK_V1 (Opus 21-avr t33b6) --><script src="/wtp-unified-dock.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
<!-- Opus v17 · Claude Pattern SSE (auto-injected) -->
<style id="opus-pattern-style">
#opus-pattern-badge{position:fixed;bottom:20px;right:20px;z-index:99990;
background:linear-gradient(135deg,#06b6d4,#8b5cf6);color:#fff;
padding:10px 16px;border-radius:20px;font:700 0.78rem -apple-system,sans-serif;
cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,0.35);transition:all 0.2s;
display:flex;align-items:center;gap:6px}
#opus-pattern-badge:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(6,182,212,0.4)}
#opus-pattern-modal{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.8);
z-index:99991;align-items:center;justify-content:center;padding:20px}
#opus-pattern-modal.show{display:flex}
#opus-pattern-box{background:#0b0d15;color:#e2e8f0;border:1px solid rgba(6,182,212,0.3);
border-radius:14px;padding:22px;max-width:820px;width:100%;max-height:85vh;overflow:auto;
font:-apple-system,sans-serif}
#opus-pattern-box h3{font:800 1.2rem;margin-bottom:12px;
background:linear-gradient(135deg,#06b6d4,#ec4899);
-webkit-background-clip:text;-webkit-text-fill-color:transparent}
#opus-pattern-input{width:100%;background:#1a1f3a;color:#fff;border:1px solid rgba(100,116,139,0.3);
border-radius:8px;padding:10px;margin-bottom:10px;font:0.9rem -apple-system}
#opus-pattern-run{background:linear-gradient(135deg,#10b981,#06b6d4);color:#fff;border:0;
padding:10px 20px;border-radius:8px;font:700 0.85rem;cursor:pointer;margin-bottom:14px}
.phase-card{background:rgba(15,23,42,0.8);border:1px solid rgba(100,116,139,0.2);
border-left:3px solid #06b6d4;border-radius:8px;padding:10px 14px;margin-bottom:8px;
font-size:0.82rem}
.phase-card.done{border-left-color:#22c55e}
.phase-card.active{border-left-color:#f59e0b;animation:pulse 1.2s ease infinite}
.phase-name{font-weight:800;color:#06b6d4;margin-bottom:4px;font-size:0.78rem;text-transform:uppercase;letter-spacing:1px}
.phase-data{font-size:0.72rem;color:#94a3b8;font-family:ui-monospace,monospace}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.6}}
#opus-pattern-close{position:absolute;top:14px;right:20px;background:0;border:0;color:#94a3b8;
font-size:1.6rem;cursor:pointer}
</style>
<div id="opus-pattern-badge" onclick="window.__opusPatternOpen()">
<span>🧠</span><span>Claude Pattern</span>
</div>
<div id="opus-pattern-modal" onclick="if(event.target.id==='opus-pattern-modal')window.__opusPatternClose()">
<div id="opus-pattern-box">
<button id="opus-pattern-close" onclick="window.__opusPatternClose()">×</button>
<h3>🧠 Claude Pattern · 7 phases REAL (SSE live)</h3>
<p style="font-size:0.82rem;color:#94a3b8;margin-bottom:12px">Backend: <b id="opus-pattern-bot">wevia-console</b> · anti-hallucination · langue naturelle</p>
<input id="opus-pattern-input" placeholder="Posez une question (FR ou EN)..." value="bonjour quel est le statut" />
<button id="opus-pattern-run" onclick="window.__opusPatternRun()">▶ Lancer (SSE stream)</button>
<div id="opus-pattern-output"></div>
</div>
</div>
<script>
(function(){
const BOT = 'wevia-console';
window.__opusPatternOpen = () => document.getElementById('opus-pattern-modal').classList.add('show');
window.__opusPatternClose = () => document.getElementById('opus-pattern-modal').classList.remove('show');
window.__opusPatternRun = () => {
const msg = document.getElementById('opus-pattern-input').value.trim();
if (!msg) return;
const out = document.getElementById('opus-pattern-output');
out.innerHTML = '';
const OPUS_SESSION_KEY = 'opus_chatbot_session_' + BOT;
let sess = localStorage.getItem(OPUS_SESSION_KEY);
if (!sess) {
sess = 'opus-' + BOT + '-' + Date.now().toString(36) + '-' + Math.random().toString(36).substr(2, 6);
localStorage.setItem(OPUS_SESSION_KEY, sess);
}
// CF_BYPASS_V23 · direct 127.0.0.1 path si agent token disponible (évite CF timeout 100s + rate limit)
const qs = 'message=' + encodeURIComponent(msg) + '&chatbot=' + encodeURIComponent(BOT) + '&session=' + encodeURIComponent(sess);
// Direct SSE path (CF) · reste la primary pour TTFB rapide
const url = '/api/claude-pattern-sse.php?' + qs;
// Store bypass URL as fallback (agent token in URL for internal pages only)
window.__opusBypassUrl = '/api/cf-bypass-helper.php?target=' + encodeURIComponent('/api/claude-pattern-sse.php?' + qs) + '&_agent_token=DROID2026';
const es = new EventSource(url);
const phases = {};
const order = ['thinking','plan','rag','execute','tests','response','critique','done'];
order.forEach(p => {
const card = document.createElement('div');
card.className = 'phase-card';
card.id = 'phase-' + p;
card.innerHTML = '<div class="phase-name">' + p.toUpperCase() + '</div><div class="phase-data">⏳ waiting...</div>';
out.appendChild(card);
});
order.forEach(evName => {
es.addEventListener(evName, (e) => {
const data = JSON.parse(e.data);
const card = document.getElementById('phase-' + evName);
if (card) {
card.classList.add('done');
card.classList.remove('active');
let txt;
if (evName === 'response' && data.text) {
txt = '<div style="background:rgba(6,182,212,0.1);padding:10px;border-radius:6px;margin-top:6px;color:#e2e8f0;font-size:0.82rem">' + (data.text.substring(0, 600)) + (data.text.length > 600 ? '...' : '') + '</div>';
} else if (evName === 'tests') {
txt = '<div>' + data.passed + '/' + data.total + ' tests ✓</div>';
} else if (evName === 'critique') {
txt = '<div>Quality: <b style="color:' + (data.quality === 'EXCELLENT' ? '#22c55e' : (data.quality === 'OK' ? '#f59e0b' : '#ef4444')) + '">' + data.quality + '</b> (' + (data.quality_score * 5).toFixed(0) + '/5)</div>';
} else {
txt = JSON.stringify(data).substring(0, 300);
}
card.querySelector('.phase-data').innerHTML = txt;
}
if (evName === 'done') es.close();
});
});
es.addEventListener('error', () => es.close());
};
})();
</script>
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-wevia-console">
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.classList.add('enter-stagger');
}, index * 80);
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.card, .btn, .kpi, .panel').forEach(el => observer.observe(el));
</script>
</body>
</html>