Files
wevads-platform/public/hamid-widget.php.broken
2026-02-26 04:53:11 +01:00

546 lines
26 KiB
Plaintext
Executable File
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.
<?php require_once __DIR__ . '/hamid-providers-config.php'; ?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WEVAL MIND Widget - Assistant IA</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #22d3ee;
--primary-dark: #0891b2;
--secondary: #3b82f6;
--bg-dark: #0f172a;
--bg-medium: #1e293b;
--bg-light: #334155;
--text-primary: #f1f5f9;
--text-secondary: #94a3b8;
--text-muted: #64748b;
--success: #22c55e;
--error: #ef4444;
--warning: #f59e0b;
--border: #475569;
}
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, var(--bg-dark) 0%, #1a1a2e 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 20px; }
/* Widget Container */
.widget-wrapper { position: relative; }
/* Floating Button */
.widget-btn { width: 70px; height: 70px; border-radius: 50%; background: linear-gradient(135deg, var(--primary), var(--secondary)); border: none; cursor: pointer; box-shadow: 0 8px 32px rgba(34,211,238,0.4); display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; position: fixed; bottom: 30px; right: 30px; z-index: 9998; }
.widget-btn:hover { transform: scale(1.1) rotate(10deg); box-shadow: 0 12px 40px rgba(34,211,238,0.6); }
.widget-btn img { width: 50px; height: 50px; border-radius: 50%; object-fit: cover; }
.widget-btn .pulse { position: absolute; width: 100%; height: 100%; border-radius: 50%; background: var(--primary); animation: pulse 2s infinite; opacity: 0.3; }
@keyframes pulse { 0%, 100% { transform: scale(1); opacity: 0.3; } 50% { transform: scale(1.2); opacity: 0; } }
/* Bubble */
.widget-bubble { position: fixed; bottom: 110px; right: 30px; background: white; padding: 12px 18px; border-radius: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); font-size: 14px; color: var(--bg-dark); animation: bubblePop 0.4s ease; z-index: 9997; }
.widget-bubble::after { content: ''; position: absolute; bottom: -8px; right: 30px; border: 8px solid transparent; border-top-color: white; }
@keyframes bubblePop { from { opacity: 0; transform: scale(0.8) translateY(10px); } to { opacity: 1; transform: scale(1) translateY(0); } }
/* Chat Window */
.widget-chat { position: fixed; bottom: 110px; right: 30px; width: 400px; height: 600px; background: var(--bg-dark); border-radius: 24px; box-shadow: 0 25px 80px rgba(0,0,0,0.5); display: none; flex-direction: column; overflow: hidden; z-index: 9999; animation: slideUp 0.3s ease; }
.widget-chat.open { display: flex; }
@keyframes slideUp { from { opacity: 0; transform: translateY(30px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } }
/* Header */
.widget-header { background: linear-gradient(135deg, var(--primary), var(--secondary)); padding: 20px; display: flex; align-items: center; gap: 14px; }
.widget-header .avatar { width: 50px; height: 50px; border-radius: 14px; background: rgba(255,255,255,0.2); display: flex; align-items: center; justify-content: center; font-size: 28px; }
.widget-header .info { flex: 1; color: white; }
.widget-header .title { font-size: 18px; font-weight: 600; }
.widget-header .status { font-size: 13px; opacity: 0.9; display: flex; align-items: center; gap: 6px; }
.widget-header .status .dot { width: 8px; height: 8px; background: #4ade80; border-radius: 50%; animation: blink 2s infinite; }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
.widget-header .close-btn { background: rgba(255,255,255,0.2); border: none; width: 36px; height: 36px; border-radius: 10px; color: white; font-size: 20px; cursor: pointer; transition: all 0.2s; }
.widget-header .close-btn:hover { background: rgba(255,255,255,0.3); }
/* Provider Select */
.provider-bar { background: var(--bg-medium); padding: 10px 16px; display: flex; align-items: center; gap: 10px; border-bottom: 1px solid var(--border); }
.provider-bar label { font-size: 12px; color: var(--text-muted); }
.provider-bar select { flex: 1; background: var(--bg-light); border: 1px solid var(--border); color: var(--text-primary); padding: 8px 12px; border-radius: 8px; font-size: 13px; }
/* Messages */
.widget-messages { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 16px; }
.widget-messages::-webkit-scrollbar { width: 6px; }
.widget-messages::-webkit-scrollbar-track { background: transparent; }
.widget-messages::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.message { display: flex; gap: 10px; animation: msgFade 0.3s ease; }
@keyframes msgFade { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.message.user { flex-direction: row-reverse; }
.message .avatar { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0; }
.message.bot .avatar { background: linear-gradient(135deg, var(--primary), var(--secondary)); }
.message.user .avatar { background: var(--bg-light); }
.message .bubble { max-width: 85%; padding: 14px 18px; border-radius: 18px; font-size: 14px; line-height: 1.5; }
.message.bot .bubble { background: var(--bg-medium); color: var(--text-primary); border-bottom-left-radius: 6px; }
.message.user .bubble { background: linear-gradient(135deg, var(--primary), var(--secondary)); color: white; border-bottom-right-radius: 6px; }
.message .time { font-size: 11px; color: var(--text-muted); margin-top: 6px; }
/* Thinking Box */
.thinking-box { background: linear-gradient(135deg, rgba(168,85,247,0.1), rgba(34,211,238,0.05)); border: 1px solid rgba(168,85,247,0.2); border-radius: 12px; margin: 8px 0; overflow: hidden; }
.thinking-header { padding: 10px 14px; cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 12px; color: #a855f7; }
.thinking-header .icon { animation: sparkle 2s linear infinite; }
@keyframes sparkle { 0%, 100% { opacity: 0.5; transform: rotate(0deg); } 50% { opacity: 1; transform: rotate(180deg); } }
.thinking-header .chevron { margin-left: auto; transition: transform 0.3s; font-size: 10px; }
.thinking-box.expanded .chevron { transform: rotate(180deg); }
.thinking-content { max-height: 0; overflow: hidden; transition: max-height 0.3s; }
.thinking-box.expanded .thinking-content { max-height: 150px; padding: 10px 14px; border-top: 1px solid rgba(168,85,247,0.15); font-size: 12px; color: var(--text-secondary); overflow-y: auto; }
/* Code Block */
.code-block { background: var(--bg-dark); border: 1px solid var(--border); border-radius: 10px; margin: 10px 0; overflow: hidden; }
.code-header { display: flex; justify-content: space-between; padding: 8px 12px; background: var(--bg-light); font-size: 11px; color: var(--text-muted); }
.code-header button { background: none; border: none; color: var(--primary); cursor: pointer; font-size: 11px; }
.code-content { padding: 12px; font-family: 'Fira Code', monospace; font-size: 12px; overflow-x: auto; color: var(--text-primary); }
/* Quick Actions */
.quick-actions { padding: 12px 16px; display: flex; gap: 8px; flex-wrap: wrap; border-bottom: 1px solid var(--border); }
.quick-btn { background: var(--bg-medium); border: 1px solid var(--border); color: var(--text-secondary); padding: 8px 14px; border-radius: 20px; font-size: 12px; cursor: pointer; transition: all 0.2s; }
.quick-btn:hover { border-color: var(--primary); color: var(--primary); }
/* Input Area */
.widget-input { padding: 16px; background: var(--bg-medium); border-top: 1px solid var(--border); }
.input-container { display: flex; gap: 10px; align-items: flex-end; }
.input-wrapper { flex: 1; background: var(--bg-light); border: 1px solid var(--border); border-radius: 16px; overflow: hidden; transition: border-color 0.3s; }
.input-wrapper:focus-within { border-color: var(--primary); }
.input-wrapper textarea { width: 100%; padding: 12px 16px; background: transparent; border: none; color: var(--text-primary); font-size: 14px; resize: none; outline: none; font-family: inherit; min-height: 44px; max-height: 100px; }
.input-wrapper textarea::placeholder { color: var(--text-muted); }
.input-toolbar { display: flex; padding: 8px 12px; gap: 8px; border-top: 1px solid var(--border); }
.input-toolbar button { background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 4px; font-size: 16px; transition: color 0.2s; }
.input-toolbar button:hover { color: var(--primary); }
.send-btn { width: 48px; height: 48px; border-radius: 14px; background: linear-gradient(135deg, var(--primary), var(--secondary)); border: none; color: white; font-size: 20px; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center; }
.send-btn:hover { transform: scale(1.05); box-shadow: 0 4px 20px rgba(34,211,238,0.4); }
.send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
/* Typing Indicator */
.typing { display: flex; align-items: center; gap: 10px; padding: 14px 18px; }
.typing .dots { display: flex; gap: 4px; }
.typing .dots span { width: 8px; height: 8px; background: var(--primary); border-radius: 50%; animation: typing 1.4s infinite ease-in-out both; }
.typing .dots span:nth-child(1) { animation-delay: -0.32s; }
.typing .dots span:nth-child(2) { animation-delay: -0.16s; }
@keyframes typing { 0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; } 40% { transform: scale(1); opacity: 1; } }
/* Responsive */
@media (max-width: 500px) {
.widget-chat { width: 100%; height: 100%; bottom: 0; right: 0; border-radius: 0; }
.widget-btn { bottom: 20px; right: 20px; }
}
/* Demo Mode Styles */
.demo-container { display: flex; flex-direction: column; align-items: center; gap: 30px; }
.demo-title { color: var(--text-primary); font-size: 24px; font-weight: 600; text-align: center; }
.demo-subtitle { color: var(--text-secondary); font-size: 14px; margin-top: -20px; }
.demo-widget { position: relative; }
</style>
</head>
<body>
<div class="demo-container">
<h1 class="demo-title">🔲 WEVAL MIND Widget</h1>
<p class="demo-subtitle">Widget IA intégrable sur n'importe quel site</p>
<div class="demo-widget">
<!-- Floating Button -->
<div class="widget-btn" onclick="toggleChat()" id="widgetBtn">
<div class="pulse"></div>
<span style="font-size:32px;">🤖</span>
</div>
<!-- Bubble -->
<div class="widget-bubble" id="bubble">Besoin d'aide? 💬</div>
<!-- Chat Window -->
<div class="widget-chat" id="chatWindow">
<div class="widget-header">
<div class="avatar">🤖</div>
<div class="info">
<div class="title">WEVAL MIND</div>
<div class="status"><span class="dot"></span> En ligne</div>
</div>
<button class="close-btn" onclick="toggleChat()">×</button>
</div>
<div class="provider-bar">
<label>Provider:</label>
<?php echo hamid_providers_dropdown("cerebras", "provider", ""); ?>
</div>
<div class="quick-actions">
<button class="quick-btn" onclick="quickMsg('Bonjour!')">👋 Saluer</button>
<button class="quick-btn" onclick="quickMsg('Aide moi avec mon code')">💻 Code</button>
<button class="quick-btn" onclick="quickMsg('Explique moi')">📖 Expliquer</button>
<button class="quick-btn" onclick="quickMsg('Résume')">📝 Résumer</button>
</div>
<div class="widget-messages" id="messages">
<div class="message bot">
<div class="avatar">🤖</div>
<div>
<div class="bubble">
Bonjour! 👋 Je suis <strong>WEVAL MIND</strong>, votre assistant IA.<br><br>
Comment puis-je vous aider aujourd'hui?
</div>
<div class="time">Maintenant</div>
</div>
</div>
</div>
<div class="widget-input">
<div class="input-container">
<div class="input-wrapper">
<textarea id="input" placeholder="Écrivez votre message..." rows="1"></textarea>
<div class="input-toolbar">
<button title="Pièce jointe">📎</button>
<button title="Emoji">😊</button>
<button title="Microphone">🎤</button>
</div>
</div>
<button class="send-btn" onclick="sendMessage()" id="sendBtn">➤</button>
</div>
</div>
</div>
</div>
</div>
<script>
const messagesEl = document.getElementById('messages');
const inputEl = document.getElementById('input');
const providerSelect = document.getElementById('provider');
const chatWindow = document.getElementById('chatWindow');
const bubble = document.getElementById('bubble');
const sendBtn = document.getElementById('sendBtn');
let sessionId = 'widget_' + Date.now();
let isOpen = false;
// Toggle chat
function toggleChat() {
isOpen = !isOpen;
chatWindow.classList.toggle('open', isOpen);
bubble.style.display = isOpen ? 'none' : 'block';
if (isOpen) inputEl.focus();
}
// Quick message
function quickMsg(text) {
inputEl.value = text;
sendMessage();
}
// Input handling
inputEl.addEventListener('keydown', e => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
inputEl.addEventListener('input', () => {
inputEl.style.height = 'auto';
inputEl.style.height = Math.min(inputEl.scrollHeight, 100) + 'px';
});
function addMessage(content, isUser = false) {
const div = document.createElement('div');
div.className = 'message ' + (isUser ? 'user' : 'bot');
const time = new Date().toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
div.innerHTML = `
<div class="avatar">${isUser ? '👤' : '🤖'}</div>
<div>
<div class="bubble">${formatContent(content)}</div>
<div class="time">${time}</div>
</div>
`;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function formatContent(text) {
// Code blocks
text = text.replace(/```(\w+)?\n?([\s\S]*?)```/g, (_, lang, code) => {
return `<div class="code-block">
<div class="code-header"><span>${lang || 'code'}</span><button onclick="copyCode(this)">📋 Copier</button></div>
<div class="code-content"><pre>${escapeHtml(code.trim())}</pre></div>
</div>`;
});
text = text.replace(/`([^`]+)`/g, '<code style="background:var(--bg-light);padding:2px 6px;border-radius:4px;">$1</code>');
text = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
text = text.replace(/\n/g, '<br>');
return text;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function copyCode(btn) {
const code = btn.closest('.code-block').querySelector('pre').textContent;
navigator.clipboard.writeText(code);
btn.textContent = '✅ Copié!';
setTimeout(() => btn.textContent = '📋 Copier', 2000);
}
function addThinking(text) {
const div = document.createElement('div');
div.className = 'thinking-box';
div.innerHTML = `
<div class="thinking-header" onclick="this.parentElement.classList.toggle('expanded')">
<span class="icon">✨</span> Réflexion <span class="chevron">▼</span>
</div>
<div class="thinking-content">${text}</div>
`;
messagesEl.appendChild(div);
}
function addTyping() {
const div = document.createElement('div');
div.className = 'message bot';
div.id = 'typing';
div.innerHTML = `
<div class="avatar">🤖</div>
<div class="typing">
<div class="dots"><span></span><span></span><span></span></div>
</div>
`;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function removeTyping() {
const el = document.getElementById('typing');
if (el) el.remove();
}
async function sendMessage() {
const message = inputEl.value.trim();
if (!message) return;
addMessage(message, true);
inputEl.value = '';
inputEl.style.height = 'auto';
sendBtn.disabled = true;
addTyping();
try {
const res = await fetch('/hamid-api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: message,
provider: providerSelect.value,
session_id: sessionId
})
});
const data = await res.json();
removeTyping();
if (data.error) {
addMessage('❌ ' + data.error);
} else {
if (data.thinking) addThinking(data.thinking);
addMessage(data.response);
}
} catch (e) {
removeTyping();
addMessage('❌ Erreur de connexion');
}
sendBtn.disabled = false;
}
// Auto-hide bubble after 5s
setTimeout(() => { bubble.style.display = 'none'; }, 5000);
</script>
</body>
</html>
<!-- FEATURES ADDITIONNELLES -->
<script>
// Export conversation
function exportConversation() {
const msgs = document.querySelectorAll('.message');
let content = 'WEVAL MIND - Conversation Export\n';
content += '================================\n\n';
msgs.forEach(m => {
const isUser = m.classList.contains('user');
const text = m.querySelector('.bubble').textContent;
content += (isUser ? 'USER: ' : 'BOT: ') + text + '\n\n';
});
const blob = new Blob([content], { type: 'text/plain' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'weval-conversation-' + Date.now() + '.txt';
a.click();
}
// Voice input
let recognition = null;
function toggleVoiceInput() {
if (!('webkitSpeechRecognition' in window)) {
alert('Voice input not supported in this browser');
return;
}
if (!recognition) {
recognition = new webkitSpeechRecognition();
recognition.lang = 'fr-FR';
recognition.continuous = false;
recognition.onresult = e => {
inputEl.value = e.results[0][0].transcript;
sendMessage();
};
}
recognition.start();
}
// Keyboard shortcuts
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && isOpen) toggleChat();
if (e.ctrlKey && e.key === 'k') { toggleChat(); e.preventDefault(); }
});
// Notification sound
function playNotification() {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.frequency.value = 800;
gain.gain.value = 0.1;
osc.start();
setTimeout(() => osc.stop(), 100);
}
// Auto-save conversation to localStorage
function saveConversation() {
const msgs = [];
document.querySelectorAll('.message').forEach(m => {
msgs.push({
isUser: m.classList.contains('user'),
content: m.querySelector('.bubble').innerHTML,
time: m.querySelector('.time')?.textContent || ''
});
});
localStorage.setItem('weval_widget_msgs_' + sessionId, JSON.stringify(msgs));
}
// Load saved conversation
function loadConversation() {
const saved = localStorage.getItem('weval_widget_msgs_' + sessionId);
if (saved) {
try {
const msgs = JSON.parse(saved);
msgs.forEach(m => {
const div = document.createElement('div');
div.className = 'message ' + (m.isUser ? 'user' : 'bot');
div.innerHTML = `
<div class="avatar">${m.isUser ? '👤' : '🤖'}</div>
<div>
<div class="bubble">${m.content}</div>
<div class="time">${m.time}</div>
</div>
`;
messagesEl.appendChild(div);
});
} catch(e) {}
}
}
// Typing indicator with random messages
const typingMessages = [
'Réflexion en cours...',
'Analyse de votre demande...',
'Consultation de la base de connaissances...',
'Génération de la réponse...'
];
function getRandomTypingMessage() {
return typingMessages[Math.floor(Math.random() * typingMessages.length)];
}
// Enhanced addTyping with message
function addTypingEnhanced() {
const div = document.createElement('div');
div.className = 'message bot';
div.id = 'typing';
div.innerHTML = `
<div class="avatar">🤖</div>
<div class="typing">
<span style="font-size:11px;color:var(--text-muted);margin-right:8px;">${getRandomTypingMessage()}</span>
<div class="dots"><span></span><span></span><span></span></div>
</div>
`;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
// Clear chat with confirmation
function clearChatConfirm() {
if (confirm('Effacer toute la conversation ?')) {
messagesEl.innerHTML = '';
localStorage.removeItem('weval_widget_msgs_' + sessionId);
addMessage('Conversation effacée. Comment puis-je vous aider ?', false);
}
}
// Feedback system
function sendFeedback(msgId, isPositive) {
console.log('Feedback:', msgId, isPositive ? 'positive' : 'negative');
// TODO: Send to API
}
// Theme toggle
let isDarkTheme = true;
function toggleTheme() {
isDarkTheme = !isDarkTheme;
document.body.style.setProperty('--bg-dark', isDarkTheme ? '#0f172a' : '#f8fafc');
document.body.style.setProperty('--text-primary', isDarkTheme ? '#f1f5f9' : '#1e293b');
}
// Window resize handler
window.addEventListener('resize', () => {
if (window.innerWidth < 500 && isOpen) {
chatWindow.style.width = '100%';
chatWindow.style.height = '100%';
chatWindow.style.borderRadius = '0';
}
});
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Show bubble after 2s
setTimeout(() => { bubble.style.display = 'block'; }, 2000);
// Hide bubble after 8s
setTimeout(() => { bubble.style.display = 'none'; }, 8000);
});
</script>
<style>
/* Additional styles for enhanced features */
.feedback-btns { display: flex; gap: 8px; margin-top: 8px; }
.feedback-btn { background: none; border: 1px solid var(--border); padding: 4px 8px; border-radius: 12px; cursor: pointer; font-size: 12px; color: var(--text-muted); transition: all 0.2s; }
.feedback-btn:hover { border-color: var(--primary); color: var(--primary); }
.feedback-btn.active { background: var(--primary); color: white; border-color: var(--primary); }
/* Tooltip */
.tooltip { position: relative; }
.tooltip::after { content: attr(data-tooltip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: var(--bg-light); color: var(--text-primary); padding: 6px 10px; border-radius: 6px; font-size: 11px; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 0.2s; }
.tooltip:hover::after { opacity: 1; }
/* Animations */
@keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } }
.shake { animation: shake 0.5s ease; }
/* Minimized state */
.widget-chat.minimized { height: 60px; }
.widget-chat.minimized .widget-messages,
.widget-chat.minimized .widget-input,
.widget-chat.minimized .quick-actions,
.widget-chat.minimized .provider-bar { display: none; }
</style>