Files
html/wevia-widget.html

524 lines
25 KiB
HTML
Raw Permalink 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>
<script src="/api/ambre-confidential-shield.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>WEVIA Chat</title>
<style>
:root { --bg:#0a0f1a; --card:#141a2e; --tx:#e8e8f0; --tx2:#8888a0; --acc:#7c3aed; --acc2:#9b8afb; --border:#1e293b; }
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; background:var(--bg); color:var(--tx); height:100vh; display:flex; flex-direction:column; overflow:hidden; }
/* Header */
.header { background:linear-gradient(135deg,#667eea,#764ba2); padding:10px 16px; display:flex; align-items:center; gap:10px; flex-shrink:0; }
.header-logo { width:28px; height:28px; border-radius:8px; background:rgba(255,255,255,0.15); display:flex; align-items:center; justify-content:center; font-size:14px; }
.header-title { font-size:14px; font-weight:700; color:#fff; }
.header-sub { font-size:10px; color:rgba(255,255,255,0.7); }
.header-status { margin-left:auto; display:flex; align-items:center; gap:5px; font-size:10px; color:rgba(255,255,255,0.8); }
.header-status::before { content:''; width:6px; height:6px; border-radius:50%; background:#34d399; }
/* Messages */
.messages { flex:1; overflow-y:auto; padding:12px; display:flex; flex-direction:column; gap:8px; }
.messages::-webkit-scrollbar { width:4px; }
.messages::-webkit-scrollbar-thumb { background:rgba(255,255,255,0.1); border-radius:2px; }
.msg { max-width:88%; padding:10px 14px; border-radius:14px; font-size:13px; line-height:1.5; word-wrap:break-word; animation:fadeIn .2s ease; }
@keyframes fadeIn { from{opacity:0;transform:translateY(4px)} to{opacity:1;transform:translateY(0)} }
.msg.user { align-self:flex-end; background:var(--acc); color:#fff; border-bottom-right-radius:4px; }
.msg.ai { align-self:flex-start; background:var(--card); color:var(--tx); border:1px solid var(--border); border-bottom-left-radius:4px; }
.msg.ai .provider { font-size:9px; color:var(--acc2); margin-bottom:4px; font-weight:600; letter-spacing:0.5px; text-transform:uppercase; }
.msg.greeting { text-align:center; align-self:center; background:transparent; border:none; padding:20px; max-width:100%; }
.msg.greeting .logo { font-size:36px; margin-bottom:8px; }
.msg.greeting .title { font-size:15px; font-weight:700; background:linear-gradient(135deg,#a78bfa,#06b6d4); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
.msg.greeting .sub { font-size:12px; color:var(--tx2); margin-top:4px; }
.typing { display:flex; gap:4px; padding:6px 0; }
.typing span { width:6px; height:6px; border-radius:50%; background:var(--acc2); animation:bounce .6s infinite alternate; }
.typing span:nth-child(2) { animation-delay:.2s; }
.typing span:nth-child(3) { animation-delay:.4s; }
@keyframes bounce { to{opacity:.3;transform:translateY(-4px)} }
/* Quick actions */
.quick-actions { display:none; gap:6px; padding:4px 12px 8px; flex-wrap:wrap; flex-shrink:0; }
.quick-btn { background:var(--card); border:1px solid var(--border); color:var(--tx2); font-size:11px; padding:5px 10px; border-radius:16px; cursor:pointer; transition:all .15s; white-space:nowrap; }
.quick-btn:hover { border-color:var(--acc); color:var(--acc2); background:rgba(124,58,237,0.1); }
/* Input */
.input-area { padding:8px 12px 12px; flex-shrink:0; border-top:1px solid var(--border); }
.input-row { display:flex; gap:8px; align-items:center; background:var(--card); border:1px solid var(--border); border-radius:20px; padding:4px 6px 4px 14px; transition:border-color .2s; }
.input-row:focus-within { border-color:var(--acc); }
.input-row input { flex:1; background:none; border:none; outline:none; color:var(--tx); font-size:13px; font-family:inherit; }
.input-row input::placeholder { color:var(--tx2); }
.send-btn { width:32px; height:32px; border-radius:50%; background:var(--acc); border:none; color:#fff; font-size:14px; cursor:pointer; display:flex; align-items:center; justify-content:center; transition:all .15s; flex-shrink:0; }
.send-btn:hover { background:#6d28d9; transform:scale(1.05); }
.send-btn:disabled { opacity:.4; cursor:default; transform:none; }
/* Markdown basic */
.msg.ai p { margin:4px 0; }
.msg.ai strong, .msg.ai b { color:#a78bfa; }
.msg.ai code { background:rgba(255,255,255,0.06); padding:1px 4px; border-radius:3px; font-size:12px; font-family:monospace; }
.msg.ai ul, .msg.ai ol { padding-left:18px; margin:4px 0; }
.msg.ai a { color:var(--acc2); text-decoration:underline; }
</style>
<style>
.wevia-progress{width:100%;background:rgba(99,102,241,.15);border-radius:8px;overflow:hidden;height:6px;margin:8px 0}
.wevia-progress-bar{height:100%;background:linear-gradient(90deg,#6366f1,#a78bfa,#6366f1);background-size:200%;animation:weviaShimmer 1.5s infinite;border-radius:8px;transition:width .3s}
.wevia-progress-text{font-size:11px;color:#a0a0b0;margin-top:4px;text-align:center}
@keyframes weviaShimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
</style>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad:false,theme:'dark',themeVariables:{primaryColor:'#7c3aed',primaryTextColor:'#e8e8f0',lineColor:'#9b8afb',secondaryColor:'#1e293b',tertiaryColor:'#141a2e'}});</script>
<style>
.mermaid-container{background:#0d1117;border:1px solid var(--border);border-radius:10px;padding:16px;margin:8px 0;overflow-x:auto}
.mermaid-container svg{max-width:100%;height:auto}
.code-block-wrap{background:#0d1117;border:1px solid var(--border);border-radius:10px;padding:12px;margin:8px 0;overflow-x:auto;position:relative}
.code-block-wrap pre{margin:0;white-space:pre-wrap;word-break:break-word;font-family:monospace;font-size:12px;color:#c9d1d9;line-height:1.5}
.code-block-wrap .cb-lang{position:absolute;top:6px;right:10px;font-size:9px;color:#7c3aed;text-transform:uppercase;letter-spacing:1px}
.code-block-wrap .cb-copy{position:absolute;top:4px;right:50px;background:rgba(124,58,237,0.2);border:1px solid rgba(124,58,237,0.3);color:#a78bfa;font-size:10px;padding:2px 8px;border-radius:4px;cursor:pointer}
.svg-logo-container{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:16px;margin:8px 0;text-align:center}
.svg-logo-container svg{max-width:200px;max-height:200px}
</style>
<style>.in-widget .header{display:none!important}.in-widget #chat{height:100vh!important;padding-top:8px}</style><script>if(window!==window.parent)document.documentElement.classList.add("in-widget")</script>
<script>
if (window !== window.top) {
document.addEventListener('DOMContentLoaded', function() {
var h = document.querySelector('.header');
if (h) h.style.display = 'none';
});
}
</script>
<!-- DOCTRINE-60-UX-ENRICH direct-inject-20260424-143919 -->
<style id="doctrine60-ux-direct">
/* DOCTRINE-60-UX-ENRICH injected-direct */
body::before {
content: '';
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: radial-gradient(circle at 50% 50%, rgba(100,180,255,0.08), transparent 60%);
pointer-events: none;
z-index: -1;
}
.card, .kpi, .panel, .btn {
transition: all 0.3s cubic-bezier(0.2,0,0.1,1);
}
.card:hover, .kpi:hover, .panel:hover {
box-shadow: 0 4px 20px rgba(100,180,255,0.2);
border-color: rgba(100,180,255,0.5);
}
@keyframes pulseD60 {
0%,100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.05); }
}
.pulse, .live-indicator, .active, .online {
animation: pulseD60 3s ease-in-out infinite;
}
.modal, .chat, .speech, .overlay {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.enter-stagger {
animation: enterStagD60 0.5s cubic-bezier(0.2,0,0.1,1) forwards;
}
@keyframes enterStagD60 {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
<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>
<div class="header">
<div class="header-logo">🧠</div>
<div>
<div class="header-title">WEVIA</div>
<div class="header-sub">Assistant IA</div>
</div>
<div class="header-status">En ligne</div><a href="/wevia" target="_top" style="margin-left:6px;color:rgba(255,255,255,.7);font-size:11px;text-decoration:none">[Plein ecran]</a>
</div>
<div class="messages" id="messages">
<div class="msg greeting">
<div class="logo">🧠</div>
<div class="sub">Comment puis-je vous aider ?<br><a href="/wevia" target="_top" style="display:inline-flex;align-items:center;gap:6px;margin-top:10px;padding:8px 16px;background:linear-gradient(135deg,#667eea,#764ba2);color:white;text-decoration:none;border-radius:20px;font-size:11px;font-weight:600">Ouvrir en plein ecran</a></div>
</div>
</div>
<div class="quick-actions" id="quickActions">
<button class="quick-btn" onclick="sendQuick('Qui est WEVAL ?')">WEVAL</button>
<button class="quick-btn" onclick="sendQuick('Vos expertises ?')">Expertises</button>
<button class="quick-btn" onclick="sendQuick('Solutions IA et Cloud')">IA & Cloud</button>
<button class="quick-btn" onclick="sendQuick('Transformation digitale')">Transfo Digitale</button>
<button class="quick-btn" onclick="sendQuick('ERP SAP et Vistex')">ERP & SAP</button>
<button class="quick-btn" onclick="sendQuick('Life Sciences et Pharma')">Life Sciences</button>
<button class="quick-btn" onclick="sendQuick('Marketing et recrutement')">Marketing</button>
<button class="quick-btn" onclick="sendQuick('Contactez-nous')">Contact</button>
</div>
<div class="input-area">
<div class="input-row">
<input type="text" id="input" placeholder="Posez votre question..." autocomplete="off" autofocus>
<button class="send-btn" id="sendBtn" onclick="send()"></button>
</div>
</div>
<script>
var convId = null;
var busy = false;
var msgEl = document.getElementById('messages');
var inputEl = document.getElementById('input');
var sendBtnEl = document.getElementById('sendBtn');
var quickEl = document.getElementById('quickActions');
inputEl.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } });
function sendQuick(txt) { inputEl.value = txt; send(); }
function addMsg(text, cls, showProvider) {
var div = document.createElement('div');
div.className = 'msg ' + cls;
if (showProvider) {
var prov = document.createElement('div');
prov.className = 'provider';
prov.textContent = '';
div.appendChild(prov);
}
if (cls === 'ai') {
div.innerHTML += formatMd(text);
} else {
div.textContent = text;
}
msgEl.appendChild(div);
msgEl.scrollTop = msgEl.scrollHeight;
if (cls === 'ai') {
var mms = div.querySelectorAll('pre.mermaid:not([data-processed])');
mms.forEach(function(el) {
try {
var code = el.textContent.trim();
code = code.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
code = code.replace(/'/g, ' ');
el.textContent = code;
mermaid.run({nodes:[el]}).then(function(){
el.setAttribute('data-processed','true');
}).catch(function(e){
console.warn('Mermaid err:', e);
});
} catch(e) { console.warn('Mermaid parse:', e); }
});
}
return div;
}
function showTyping() {
var div = document.createElement('div');
div.className = 'msg ai';
div.id = 'typing';
div.innerHTML = '<div class="typing"><span></span><span></span><span></span></div>';
msgEl.appendChild(div);
msgEl.scrollTop = msgEl.scrollHeight;
}
function hideTyping() {
var el = document.getElementById('typing');
if (el) el.remove();
}
function formatMd(text) {
if (!text) return '';
var blocks = [];
text = text.replace(/```(\w*)\n([\s\S]*?)```/g, function(m, lang, code) {
var idx = blocks.length;
blocks.push({lang: lang.toLowerCase(), code: code.trim()});
return '%%BLOCK_' + idx + '%%';
});
text = text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>')
.replace(/\*(.+?)\*/g,'<em>$1</em>')
.replace(/`(.+?)`/g,'<code>$1</code>')
.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,'<img src="$2" alt="$1" style="max-width:100%;border-radius:8px;margin:8px 0">')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank">$1</a>')
.replace(/^[\-\*] (.+)/gm,'<li>$1</li>')
.replace(/(<li>.*<\/li>)/gs,'<ul>$1</ul>')
.replace(/\n{2,}/g,'</p><p>')
.replace(/\n/g,'<br>')
.replace(/^/,'<p>').replace(/$/,'</p>');
blocks.forEach(function(b, i) {
var ph = '%%BLOCK_' + i + '%%';
var rpl = '';
if (b.lang === 'mermaid') {
var mid = 'mm_' + Date.now() + '_' + i;
rpl = '<div class="mermaid-container"><pre class="mermaid" id="' + mid + '">' + b.code + '</pre></div>';
} else if (b.lang === 'svg') {
rpl = '<div class="svg-logo-container">' + b.code + '</div>';
} else {
var esc = b.code.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
rpl = '<div class="code-block-wrap"><span class="cb-lang">' + (b.lang||'code') + '</span><button class="cb-copy" onclick="navigator.clipboard.writeText(this.nextElementSibling.textContent)">Copier</button><pre>' + esc + '</pre></div>';
}
text = text.replace(ph, rpl);
});
return text;
}
function send() {
if (busy) return;
var text = inputEl.value.trim();
if (!text) return;
inputEl.value = '';
addMsg(text, 'user');
quickEl.style.display = 'none';
busy = true;
sendBtnEl.disabled = true;
showTyping();
var body = { message: text, lang: 'fr' };
if (convId) body.conversation_id = convId;
fetch('/api/wevia-json-api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
})
.then(function(r) { return r.json(); })
.then(function(d) {
hideTyping();
if (d.conversation_id) convId = d.conversation_id;
addMsg(d.response || 'Désolé, une erreur est survenue.', 'ai', true);
})
.catch(function(err) {
hideTyping();
addMsg('Erreur de connexion. Réessayez.', 'ai', false);
})
.finally(function() {
busy = false;
sendBtnEl.disabled = false;
inputEl.focus();
});
}
</script>
<script>
function weviaProgress(container,startMs){
if(!container)return;
var el=document.createElement('div');
el.className='wevia-pb-wrap';
el.innerHTML='<div class="wevia-progress"><div class="wevia-progress-bar" style="width:5%"></div></div><div class="wevia-progress-text">Analyse en cours...</div>';
container.appendChild(el);
var bar=el.querySelector('.wevia-progress-bar'),txt=el.querySelector('.wevia-progress-text');
var steps=['Connexion au moteur IA...','Analyse de votre demande...','Génération de la réponse...','Finalisation...'];
var iv=setInterval(function(){
var elapsed=(Date.now()-startMs)/1000;
var pct=Math.min(95,5+elapsed*3);
bar.style.width=pct+'%';
var si=Math.min(3,Math.floor(elapsed/3));
txt.textContent=steps[si]+' ('+Math.round(elapsed)+'s)';
if(elapsed>25)txt.textContent='Presque termine... ('+Math.round(elapsed)+'s)';
},300);
return {el:el,stop:function(){clearInterval(iv);bar.style.width='100%';txt.textContent='Termine!';}};
}
</script>
<script>
if (window !== window.top) {
document.addEventListener('DOMContentLoaded', function() {
var h = document.querySelector('.header');
if (h) h.style.display = 'none';
});
}
</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) {
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
inner.innerHTML = html;
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
m.style.display = 'flex';
}
function wire(root) {
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
var cards = root.querySelectorAll(sels);
for (var i = 0; i < cards.length; i++) {
var c = cards[i];
if (c.__opusWired) continue;
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
var r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 40) continue;
c.__opusWired = true;
c.style.cursor = 'pointer';
c.setAttribute('role','button');
c.setAttribute('tabindex','0');
c.addEventListener('click', function(ev){
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
if (ev.target.closest('a,button,input,select')) return;
ev.preventDefault(); ev.stopPropagation();
openCard(this);
});
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
}
}
var initRun = function(){ wire(d.body || d.documentElement); };
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
else initRun();
var mo = new MutationObserver(function(muts){
var newCard = false;
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
if (newCard) initRun();
});
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
<!-- V29 SECURITY: archi-meta-badge.js removed from public iframe -->
<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-widget</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-widget';
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 = sessionStorage.getItem(OPUS_SESSION_KEY);
if (!sess) {
sess = 'opus-' + BOT + '-' + Date.now().toString(36) + '-' + Math.random().toString(36).substr(2, 6);
sessionStorage.setItem(OPUS_SESSION_KEY, sess);
}
const url = '/api/claude-pattern-sse.php?message=' + encodeURIComponent(msg) + '&chatbot=' + encodeURIComponent(BOT) + '&session=' + encodeURIComponent(sess);
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-direct">
// DOCTRINE-60-UX-JS staggered entrance
(function(){
if (!('IntersectionObserver' in window)) return;
const obs = new IntersectionObserver((entries) => {
entries.forEach((e, i) => {
if (e.isIntersecting) {
setTimeout(() => e.target.classList.add('enter-stagger'), i * 80);
obs.unobserve(e.target);
}
});
});
document.querySelectorAll('.card, .kpi, .panel').forEach(el => obs.observe(el));
})();
</script>
</body>
</html>