306 lines
13 KiB
JavaScript
306 lines
13 KiB
JavaScript
/**
|
||
* WEVIA Enhancement v2 — Radical Fix
|
||
* 1. Force emojis on ALL bot responses (client-side)
|
||
* 2. Kill horizontal split (CSS override)
|
||
* 3. Arabic voice TTS
|
||
*/
|
||
(function(){
|
||
'use strict';
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// 1. KILL SPLIT — Force single-column layout
|
||
// ═══════════════════════════════════════════════
|
||
var killSplitCSS = document.createElement('style');
|
||
killSplitCSS.textContent = [
|
||
// Ensure only active panel shows, full width
|
||
'.panel{display:none!important;width:100%!important;max-width:100%!important;flex:1!important}',
|
||
'.panel.a{display:flex!important;flex-direction:column!important;width:100%!important}',
|
||
// Main area: single column, no side-by-side
|
||
'.mn{display:flex!important;flex-direction:column!important;width:100%!important;max-width:100%!important}',
|
||
// Chat takes full width
|
||
'.chat{width:100%!important;max-width:100%!important}',
|
||
'.msg{max-width:90%!important}',
|
||
// Artifacts grid full width when shown
|
||
'#artGrid{width:100%!important}',
|
||
// Input area full width
|
||
'.inp-area{width:100%!important}',
|
||
'.inp-wrap{max-width:100%!important}',
|
||
// No unexpected horizontal scrolling
|
||
'body,html,.app,.mn{overflow-x:hidden!important}',
|
||
// Hide horizontal rules in chat
|
||
'.chat hr,.bub hr{display:none!important}'
|
||
].join('\n');
|
||
document.head.appendChild(killSplitCSS);
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// 2. EMOJI INJECTION — Client-side post-processing
|
||
// ═══════════════════════════════════════════════
|
||
var emojiMap = {
|
||
greet: ['👋','🤗','😊','🙌'],
|
||
explain: ['💡','🔍','📝','🎯'],
|
||
success: ['✅','🎉','🚀','💪'],
|
||
warn: ['⚠️','🔔','❗','👀'],
|
||
error: ['❌','🚫','💥','😬'],
|
||
code: ['💻','🔧','⚙️','🛠️'],
|
||
list: ['🔹','📌','▶️','💎'],
|
||
data: ['📊','📈','📉','🗂️'],
|
||
idea: ['💡','✨','🌟','💎'],
|
||
doc: ['📄','📋','📝','📑']
|
||
};
|
||
|
||
function pickEmoji(category) {
|
||
var arr = emojiMap[category] || emojiMap.explain;
|
||
return arr[Math.floor(Math.random() * arr.length)];
|
||
}
|
||
|
||
function detectCategory(text) {
|
||
var t = text.toLowerCase();
|
||
if (/^(bonjour|salut|hello|hi|مرحب|اهلا)/.test(t)) return 'greet';
|
||
if (/erreur|error|échoué|fail|bug/.test(t)) return 'error';
|
||
if (/attention|warning|⚠|prudence/.test(t)) return 'warn';
|
||
if (/terminé|succès|fait|success|réussi|done|✓/.test(t)) return 'success';
|
||
if (/code|script|function|var |const |let |def |class /.test(t)) return 'code';
|
||
if (/document|pdf|rapport|fichier/.test(t)) return 'doc';
|
||
if (/données|data|stat|chiffr|nombre/.test(t)) return 'data';
|
||
return 'explain';
|
||
}
|
||
|
||
function hasEmoji(text) {
|
||
return /[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2700}-\u{27BF}]/u.test(text.substring(0, 20));
|
||
}
|
||
|
||
function injectEmojis(text) {
|
||
if (!text || text.length < 3) return text;
|
||
// Don't double-inject if already has emoji at start
|
||
if (hasEmoji(text)) return text;
|
||
|
||
var cat = detectCategory(text);
|
||
var startEmoji = pickEmoji(cat);
|
||
|
||
// Add emoji at start
|
||
text = startEmoji + ' ' + text;
|
||
|
||
// Replace bullet points with emoji bullets
|
||
text = text.replace(/^[\s]*[-•]\s/gm, '🔹 ');
|
||
text = text.replace(/^[\s]*(\d+)\.\s/gm, function(m, n) {
|
||
var nums = ['0️⃣','1️⃣','2️⃣','3️⃣','4️⃣','5️⃣','6️⃣','7️⃣','8️⃣','9️⃣'];
|
||
return (nums[parseInt(n)] || n + '.') + ' ';
|
||
});
|
||
|
||
// Add closing emoji if none
|
||
var trimmed = text.trim();
|
||
if (!hasEmoji(trimmed.substring(trimmed.length - 5))) {
|
||
var closers = ['✨','🚀','💡','👍','😊'];
|
||
text = text.trimEnd() + ' ' + closers[Math.floor(Math.random() * closers.length)];
|
||
}
|
||
|
||
return text;
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// 3. ARABIC VOICE TTS
|
||
// ═══════════════════════════════════════════════
|
||
var voiceEnabled = false;
|
||
var arabicVoice = null;
|
||
|
||
function initVoice() {
|
||
if (!('speechSynthesis' in window)) return;
|
||
function loadV() {
|
||
var voices = speechSynthesis.getVoices();
|
||
arabicVoice = voices.find(function(v){return v.lang.startsWith('ar')})
|
||
|| voices.find(function(v){return v.lang.indexOf('ar') >= 0})
|
||
|| (voices.length > 0 ? voices[0] : null);
|
||
}
|
||
loadV();
|
||
speechSynthesis.onvoiceschanged = loadV;
|
||
}
|
||
initVoice();
|
||
|
||
window.toggleWeviaVoice = function() {
|
||
voiceEnabled = !voiceEnabled;
|
||
var btns = document.querySelectorAll('.wevia-voice-btn,[id="ttsBtn"],[id="voiceBtn2"]');
|
||
btns.forEach(function(b) {
|
||
b.textContent = voiceEnabled ? '\u{1F50A}' : '\u{1F507}';
|
||
b.style.color = voiceEnabled ? '#22d3ee' : '';
|
||
b.title = voiceEnabled ? 'Voix Arabe ON' : 'Voix OFF';
|
||
});
|
||
if (!voiceEnabled) speechSynthesis.cancel();
|
||
};
|
||
|
||
function speakArabic(text) {
|
||
if (!voiceEnabled || !('speechSynthesis' in window)) return;
|
||
speechSynthesis.cancel();
|
||
// Clean text for speech
|
||
var clean = text
|
||
.replace(/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}]/gu, '')
|
||
.replace(/<[^>]*>/g, '')
|
||
.replace(/```[\s\S]*?```/g, '')
|
||
.replace(/\*\*/g, '').replace(/\*/g, '')
|
||
.replace(/#{1,6}\s/g, '')
|
||
.replace(/\n+/g, '. ')
|
||
.replace(/\s+/g, ' ').trim();
|
||
if (!clean || clean.length < 2) return;
|
||
|
||
var chunks = clean.match(/.{1,200}[.!?\s]|.{1,200}$/g) || [clean];
|
||
var i = 0;
|
||
(function next() {
|
||
if (i >= chunks.length || !voiceEnabled) return;
|
||
var utt = new SpeechSynthesisUtterance(chunks[i]);
|
||
if (arabicVoice) utt.voice = arabicVoice;
|
||
utt.lang = 'ar-SA';
|
||
utt.rate = 0.95;
|
||
utt.onend = function(){ i++; next(); };
|
||
utt.onerror = function(){ i++; next(); };
|
||
speechSynthesis.speak(utt);
|
||
})();
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// 4. MONKEY-PATCH message rendering functions
|
||
// ═══════════════════════════════════════════════
|
||
|
||
// For FULLSCREEN (addMsg)
|
||
if (typeof window.addMsg === 'function') {
|
||
var origAddMsg = window.addMsg;
|
||
window.addMsg = function(role, content, thinking) {
|
||
if (role !== 'user' && content) {
|
||
content = injectEmojis(content);
|
||
}
|
||
origAddMsg(role, content, thinking);
|
||
if (role !== 'user' && voiceEnabled && content) speakArabic(content);
|
||
};
|
||
}
|
||
|
||
// For WIDGET (addMessage)
|
||
if (typeof window.addMessage === 'function') {
|
||
var origAddMessage = window.addMessage;
|
||
window.addMessage = function(content, isUser) {
|
||
if (!isUser && content) {
|
||
content = injectEmojis(content);
|
||
}
|
||
origAddMessage(content, isUser);
|
||
if (!isUser && voiceEnabled && content) speakArabic(content);
|
||
};
|
||
}
|
||
|
||
// Retry patching after DOM load (functions may be defined later)
|
||
function retryPatch() {
|
||
if (typeof window.addMsg === 'function' && !window._addMsgPatched) {
|
||
var orig = window.addMsg;
|
||
window.addMsg = function(role, content, thinking) {
|
||
if (role !== 'user' && content) content = injectEmojis(content);
|
||
orig(role, content, thinking);
|
||
if (role !== 'user' && voiceEnabled && content) speakArabic(content);
|
||
};
|
||
window._addMsgPatched = true;
|
||
}
|
||
if (typeof window.addMessage === 'function' && !window._addMessagePatched) {
|
||
var orig2 = window.addMessage;
|
||
window.addMessage = function(content, isUser) {
|
||
if (!isUser && content) content = injectEmojis(content);
|
||
orig2(content, isUser);
|
||
if (!isUser && voiceEnabled && content) speakArabic(content);
|
||
};
|
||
window._addMessagePatched = true;
|
||
}
|
||
}
|
||
setTimeout(retryPatch, 500);
|
||
setTimeout(retryPatch, 1500);
|
||
setTimeout(retryPatch, 3000);
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// 5. FORCE ARTIFACTS TO OPEN IN NEW TAB
|
||
// ═══════════════════════════════════════════════
|
||
if (typeof window.addArtifact === 'function') {
|
||
window.addArtifact = function(art) {
|
||
if (!art) return;
|
||
// Open directly in new window
|
||
if (art.url) { window.open(art.url, '_blank'); return; }
|
||
if (!art.content) return;
|
||
var w = window.open('', '_blank', 'width=900,height=700');
|
||
if (!w) { alert('Autorisez les popups pour voir le document'); return; }
|
||
var css = 'body{font-family:system-ui,sans-serif;padding:24px;margin:0;background:#0a0f1a;color:#e2e8f0;line-height:1.6}pre{background:#111827;padding:16px;border-radius:8px;overflow-x:auto;font-size:13px;white-space:pre-wrap}h1,h2,h3{color:#22d3ee}';
|
||
if (art.type === 'html') {
|
||
w.document.write(art.content);
|
||
} else if (art.type === 'mermaid') {
|
||
w.document.write('<html><head><script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"><\/script><style>' + css + '</style></head><body><div class="mermaid">' + art.content + '</div><script>mermaid.initialize({startOnLoad:true,theme:"dark"})<\/script></body></html>');
|
||
} else {
|
||
w.document.write('<html><head><title>' + (art.title||'Document') + '</title><style>' + css + '</style></head><body><h2>' + (art.title||'Document') + '</h2><pre>' + art.content.replace(/</g,'<') + '</pre></body></html>');
|
||
}
|
||
w.document.close();
|
||
};
|
||
}
|
||
|
||
// Same retry for addArtifact
|
||
function retryArtifact() {
|
||
if (typeof window.addArtifact === 'function' && !window._artPatched) {
|
||
var origArt = window.addArtifact;
|
||
window.addArtifact = function(art) {
|
||
if (!art) return;
|
||
if (art.url) { window.open(art.url, '_blank'); return; }
|
||
if (!art.content) return;
|
||
var w = window.open('', '_blank', 'width=900,height=700');
|
||
if (!w) return;
|
||
var css = 'body{font-family:system-ui,sans-serif;padding:24px;margin:0;background:#0a0f1a;color:#e2e8f0;line-height:1.6}pre{background:#111827;padding:16px;border-radius:8px;overflow-x:auto;white-space:pre-wrap}h1,h2,h3{color:#22d3ee}';
|
||
if (art.type === 'html') w.document.write(art.content);
|
||
else if (art.type === 'mermaid') w.document.write('<html><head><script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"><\/script><style>'+css+'</style></head><body><div class="mermaid">'+art.content+'</div><script>mermaid.initialize({startOnLoad:true,theme:"dark"})<\/script></body></html>');
|
||
else w.document.write('<html><head><title>'+(art.title||'Doc')+'</title><style>'+css+'</style></head><body><h2>'+(art.title||'Doc')+'</h2><pre>'+art.content.replace(/</g,'<')+'</pre></body></html>');
|
||
w.document.close();
|
||
};
|
||
window._artPatched = true;
|
||
}
|
||
}
|
||
setTimeout(retryArtifact, 500);
|
||
setTimeout(retryArtifact, 1500);
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// 6. MutationObserver — catch ALL new bot messages
|
||
// ═══════════════════════════════════════════════
|
||
var observer = new MutationObserver(function(mutations) {
|
||
mutations.forEach(function(m) {
|
||
m.addedNodes.forEach(function(node) {
|
||
if (node.nodeType !== 1) return;
|
||
// Fullscreen: .msg.ai .bub
|
||
var bubs = node.querySelectorAll ? node.querySelectorAll('.msg.ai .bub, .msg:not(.u) .bub') : [];
|
||
if (node.classList && (node.classList.contains('msg') && !node.classList.contains('u'))) {
|
||
var bub = node.querySelector('.bub');
|
||
if (bub && bub.textContent && !hasEmoji(bub.textContent)) {
|
||
bub.innerHTML = injectEmojis(bub.innerHTML);
|
||
}
|
||
}
|
||
bubs.forEach(function(bub) {
|
||
if (bub.textContent && !hasEmoji(bub.textContent)) {
|
||
bub.innerHTML = injectEmojis(bub.innerHTML);
|
||
}
|
||
});
|
||
// Widget: bot messages (not user)
|
||
if (node.classList && node.classList.contains('message') && !node.classList.contains('user')) {
|
||
var content = node.querySelector('.content, .text, p');
|
||
if (content && content.textContent && !hasEmoji(content.textContent)) {
|
||
content.innerHTML = injectEmojis(content.innerHTML);
|
||
}
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
// Observe both fullscreen chat and widget chat areas
|
||
function startObserving() {
|
||
var targets = document.querySelectorAll('#chatArea, .chat, .chat-window, .messages, .chat-messages');
|
||
targets.forEach(function(t) {
|
||
observer.observe(t, { childList: true, subtree: true });
|
||
});
|
||
// Fallback: observe body
|
||
if (targets.length === 0) {
|
||
observer.observe(document.body, { childList: true, subtree: true });
|
||
}
|
||
}
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', startObserving);
|
||
} else {
|
||
startObserving();
|
||
}
|
||
|
||
console.log('✅ WEVIA Enhancement v2 loaded — Emojis + No-Split + Arabic Voice');
|
||
})();
|