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

365 lines
20 KiB
PHP
Executable File
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.
<?php require_once __DIR__ . '/hamid-providers-config.php'; ?>
<!DOCTYPE html>
<html data-theme="dark" 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>