Spaces:
Running
Running
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Répond AI - Assistant Virtuel</title> | |
| <!-- Importation des icônes RemixIcon --> | |
| <link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet"> | |
| <!-- Police Google Fonts (Inter) --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| /* Palette de couleurs (Thème Sombre / Néon) */ | |
| --bg-body: #0f172a; | |
| --bg-sidebar: #1e293b; | |
| --bg-chat: #0f172a; | |
| --primary: #6366f1; | |
| --primary-hover: #4f46e5; | |
| --accent: #ec4899; | |
| --glass-bg: rgba(30, 41, 59, 0.7); | |
| --glass-border: rgba(255, 255, 255, 0.1); | |
| --text-main: #f8fafc; | |
| --text-muted: #94a3b8; | |
| --msg-user-bg: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%); | |
| --msg-ai-bg: #1e293b; | |
| --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.3); | |
| --radius-lg: 16px; | |
| --radius-md: 12px; | |
| --header-height: 60px; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| body { | |
| background-color: var(--bg-body); | |
| color: var(--text-main); | |
| height: 100vh; | |
| overflow: hidden; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* --- Header --- */ | |
| header { | |
| height: var(--header-height); | |
| background: rgba(15, 23, 42, 0.8); | |
| backdrop-filter: blur(10px); | |
| border-bottom: 1px solid var(--glass-border); | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 20px; | |
| position: fixed; | |
| top: 0; | |
| width: 100%; | |
| z-index: 100; | |
| } | |
| .brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| font-weight: 700; | |
| font-size: 1.2rem; | |
| color: var(--text-main); | |
| } | |
| .brand i { | |
| color: var(--primary); | |
| font-size: 1.5rem; | |
| } | |
| .anycoder-link { | |
| font-size: 0.85rem; | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| transition: color 0.3s; | |
| background: rgba(255,255,255,0.05); | |
| padding: 5px 12px; | |
| border-radius: 20px; | |
| border: 1px solid var(--glass-border); | |
| } | |
| .anycoder-link:hover { | |
| color: var(--primary); | |
| border-color: var(--primary); | |
| } | |
| /* --- Main Layout --- */ | |
| .app-container { | |
| display: flex; | |
| height: 100vh; | |
| padding-top: var(--header-height); | |
| } | |
| /* --- Sidebar --- */ | |
| aside { | |
| width: 260px; | |
| background-color: var(--bg-sidebar); | |
| border-right: 1px solid var(--glass-border); | |
| display: flex; | |
| flex-direction: column; | |
| transition: transform 0.3s ease; | |
| z-index: 50; | |
| } | |
| .sidebar-header { | |
| padding: 20px; | |
| } | |
| .new-chat-btn { | |
| width: 100%; | |
| padding: 12px; | |
| background: var(--primary); | |
| color: white; | |
| border: none; | |
| border-radius: var(--radius-md); | |
| font-weight: 500; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| transition: background 0.2s; | |
| } | |
| .new-chat-btn:hover { | |
| background: var(--primary-hover); | |
| } | |
| .history-list { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 0 10px; | |
| } | |
| .history-group-title { | |
| font-size: 0.75rem; | |
| color: var(--text-muted); | |
| padding: 15px 10px 5px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| .history-item { | |
| padding: 10px 12px; | |
| border-radius: 8px; | |
| color: var(--text-main); | |
| text-decoration: none; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| font-size: 0.9rem; | |
| cursor: pointer; | |
| transition: background 0.2s; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .history-item:hover { | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .history-item i { | |
| color: var(--text-muted); | |
| } | |
| .user-profile { | |
| padding: 20px; | |
| border-top: 1px solid var(--glass-border); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .avatar { | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| object-fit: cover; | |
| border: 2px solid var(--primary); | |
| } | |
| .user-info h4 { | |
| font-size: 0.9rem; | |
| font-weight: 500; | |
| } | |
| .user-info span { | |
| font-size: 0.75rem; | |
| color: var(--text-muted); | |
| } | |
| /* --- Chat Area --- */ | |
| main { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| background-color: var(--bg-chat); | |
| position: relative; | |
| } | |
| .chat-container { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 20px 0; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| scroll-behavior: smooth; | |
| } | |
| /* Scrollbar styling */ | |
| .chat-container::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .chat-container::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| .chat-container::-webkit-scrollbar-thumb { | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 4px; | |
| } | |
| .message-wrapper { | |
| display: flex; | |
| justify-content: center; | |
| padding: 0 20px; | |
| animation: fadeIn 0.3s ease-out; | |
| } | |
| .message { | |
| max-width: 800px; | |
| width: 100%; | |
| display: flex; | |
| gap: 16px; | |
| } | |
| .message.user { | |
| flex-direction: row-reverse; | |
| } | |
| .message-avatar { | |
| width: 36px; | |
| height: 36px; | |
| border-radius: 8px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-shrink: 0; | |
| } | |
| .ai-avatar { | |
| background: linear-gradient(135deg, #10b981, #059669); | |
| color: white; | |
| } | |
| .user-avatar-img { | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 8px; | |
| object-fit: cover; | |
| } | |
| .message-content { | |
| padding: 12px 16px; | |
| border-radius: var(--radius-lg); | |
| font-size: 0.95rem; | |
| line-height: 1.5; | |
| position: relative; | |
| } | |
| .message.ai .message-content { | |
| background-color: var(--msg-ai-bg); | |
| color: var(--text-main); | |
| border-top-left-radius: 2px; | |
| } | |
| .message.user .message-content { | |
| background: var(--msg-user-bg); | |
| color: white; | |
| border-top-right-radius: 2px; | |
| } | |
| .message-time { | |
| font-size: 0.7rem; | |
| margin-top: 5px; | |
| opacity: 0.7; | |
| text-align: right; | |
| } | |
| .message.ai .message-time { | |
| text-align: left; | |
| } | |
| /* --- Input Area --- */ | |
| .input-area { | |
| padding: 20px; | |
| background: linear-gradient(to top, var(--bg-chat) 80%, transparent); | |
| display: flex; | |
| justify-content: center; | |
| } | |
| .input-wrapper { | |
| max-width: 800px; | |
| width: 100%; | |
| position: relative; | |
| background: var(--glass-bg); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 24px; | |
| padding: 8px 16px; | |
| display: flex; | |
| align-items: flex-end; | |
| gap: 10px; | |
| box-shadow: var(--shadow-lg); | |
| backdrop-filter: blur(12px); | |
| } | |
| .input-wrapper:focus-within { | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2); | |
| } | |
| textarea { | |
| flex: 1; | |
| background: transparent; | |
| border: none; | |
| color: var(--text-main); | |
| font-size: 1rem; | |
| resize: none; | |
| max-height: 150px; | |
| padding: 10px 0; | |
| outline: none; | |
| line-height: 1.4; | |
| } | |
| .send-btn { | |
| background: var(--primary); | |
| color: white; | |
| border: none; | |
| width: 36px; | |
| height: 36px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: transform 0.2s, background 0.2s; | |
| margin-bottom: 4px; | |
| } | |
| .send-btn:hover { | |
| background: var(--primary-hover); | |
| transform: scale(1.05); | |
| } | |
| .send-btn:disabled { | |
| background: var(--text-muted); | |
| cursor: not-allowed; | |
| opacity: 0.5; | |
| } | |
| /* --- Animations --- */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| @keyframes bounce { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-4px); } | |
| } | |
| .typing-indicator { | |
| display: flex; | |
| gap: 4px; | |
| padding: 4px 8px; | |
| } | |
| .dot { | |
| width: 6px; | |
| height: 6px; | |
| background: var(--text-muted); | |
| border-radius: 50%; | |
| animation: bounce 1.4s infinite ease-in-out both; | |
| } | |
| .dot:nth-child(1) { animation-delay: -0.32s; } | |
| .dot:nth-child(2) { animation-delay: -0.16s; } | |
| /* --- Toast Notification --- */ | |
| .toast { | |
| position: fixed; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%) translateY(100px); | |
| background: #ef4444; | |
| color: white; | |
| padding: 10px 20px; | |
| border-radius: 8px; | |
| font-size: 0.9rem; | |
| opacity: 0; | |
| transition: all 0.3s ease; | |
| z-index: 1000; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.2); | |
| } | |
| .toast.show { | |
| transform: translateX(-50%) translateY(0); | |
| opacity: 1; | |
| } | |
| /* --- Mobile Responsive --- */ | |
| .mobile-menu-btn { | |
| display: none; | |
| background: none; | |
| border: none; | |
| color: var(--text-main); | |
| font-size: 1.5rem; | |
| cursor: pointer; | |
| } | |
| @media (max-width: 768px) { | |
| aside { | |
| position: absolute; | |
| height: calc(100vh - var(--header-height)); | |
| transform: translateX(-100%); | |
| box-shadow: var(--shadow-lg); | |
| } | |
| aside.open { | |
| transform: translateX(0); | |
| } | |
| .mobile-menu-btn { | |
| display: block; | |
| } | |
| .message { | |
| gap: 8px; | |
| } | |
| .message-content { | |
| font-size: 0.9rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Header --> | |
| <header> | |
| <div class="brand"> | |
| <button class="mobile-menu-btn" id="menuToggle" aria-label="Menu"> | |
| <i class="ri-menu-line"></i> | |
| </button> | |
| <i class="ri-robot-2-fill"></i> | |
| <span>Répond AI</span> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link"> | |
| Built with anycoder <i class="ri-external-link-line" style="font-size: 0.8em;"></i> | |
| </a> | |
| </header> | |
| <div class="app-container"> | |
| <!-- Sidebar --> | |
| <aside id="sidebar"> | |
| <div class="sidebar-header"> | |
| <button class="new-chat-btn" id="newChatBtn"> | |
| <i class="ri-add-line"></i> Nouvelle Discussion | |
| </button> | |
| </div> | |
| <div class="history-list"> | |
| <div class="history-group-title">Aujourd'hui</div> | |
| <div class="history-item"><i class="ri-message-3-line"></i> Idées marketing</div> | |
| <div class="history-item"><i class="ri-message-3-line"></i> Recette Python</div> | |
| <div class="history-item"><i class="ri-message-3-line"></i> Voyage au Japon</div> | |
| <div class="history-group-title">Hier</div> | |
| <div class="history-item"><i class="ri-message-3-line"></i> Analyse de données</div> | |
| <div class="history-item"><i class="ri-message-3-line"></i> Email professionnel</div> | |
| </div> | |
| <div class="user-profile"> | |
| <img src="https://picsum.photos/seed/user123/64/64" alt="User" class="avatar"> | |
| <div class="user-info"> | |
| <h4>Utilisateur</h4> | |
| <span>Plan Gratuit</span> | |
| </div> | |
| </div> | |
| </aside> | |
| <!-- Main Chat Area --> | |
| <main> | |
| <div class="chat-container" id="chatContainer"> | |
| <!-- Welcome Message --> | |
| <div class="message-wrapper"> | |
| <div class="message ai"> | |
| <div class="message-avatar ai-avatar"> | |
| <i class="ri-sparkling-fill"></i> | |
| </div> | |
| <div class="message-content"> | |
| Bonjour ! Je suis <strong>Répond AI</strong>. Posez-moi une question, demandez-moi d'écrire du code ou simplement discutons. Comment puis-je vous aider ? | |
| <div class="message-time">Maintenant</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Input Area --> | |
| <div class="input-area"> | |
| <div class="input-wrapper"> | |
| <textarea id="userInput" rows="1" placeholder="Écrivez votre message ici..."></textarea> | |
| <button class="send-btn" id="sendBtn" disabled aria-label="Envoyer"> | |
| <i class="ri-send-plane-fill"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <!-- Toast Notification --> | |
| <div id="toast" class="toast">Veuillez entrer un message.</div> | |
| <script> | |
| // DOM Elements | |
| const chatContainer = document.getElementById('chatContainer'); | |
| const userInput = document.getElementById('userInput'); | |
| const sendBtn = document.getElementById('sendBtn'); | |
| const menuToggle = document.getElementById('menuToggle'); | |
| const sidebar = document.getElementById('sidebar'); | |
| const newChatBtn = document.getElementById('newChatBtn'); | |
| const toast = document.getElementById('toast'); | |
| // State | |
| let isTyping = false; | |
| // Auto-resize textarea | |
| userInput.addEventListener('input', function() { | |
| this.style.height = 'auto'; | |
| this.style.height = (this.scrollHeight) + 'px'; | |
| // Enable/Disable button | |
| if (this.value.trim().length > 0) { | |
| sendBtn.disabled = false; | |
| } else { | |
| sendBtn.disabled = true; | |
| } | |
| }); | |
| // Toggle Sidebar on Mobile | |
| menuToggle.addEventListener('click', () => { | |
| sidebar.classList.toggle('open'); | |
| }); | |
| // Close sidebar when clicking outside on mobile | |
| document.addEventListener('click', (e) => { | |
| if (window.innerWidth <= 768) { | |
| if (!sidebar.contains(e.target) && !menuToggle.contains(e.target) && sidebar.classList.contains('open')) { | |
| sidebar.classList.remove('open'); | |
| } | |
| } | |
| }); | |
| // Send Message Logic | |
| function sendMessage() { | |
| const text = userInput.value.trim(); | |
| if (!text) { | |
| showToast("Veuillez entrer un message."); | |
| return; | |
| } | |
| if (isTyping) return; | |
| // 1. Add User Message | |
| addMessage(text, 'user'); | |
| userInput.value = ''; | |
| userInput.style.height = 'auto'; | |
| sendBtn.disabled = true; | |
| // 2. Simulate AI Thinking | |
| isTyping = true; | |
| const typingId = showTypingIndicator(); | |
| // 3. Generate Response (Simulation) | |
| setTimeout(() => { | |
| removeMessage(typingId); | |
| const response = generateAIResponse(text); | |
| addMessage(response, 'ai'); | |
| isTyping = false; | |
| }, 1500 + Math.random() * 1000); // Random delay 1.5s - 2.5s | |
| } | |
| // Handle Enter key | |
| userInput.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| sendMessage(); | |
| } | |
| }); | |
| sendBtn.addEventListener('click', sendMessage); | |
| // Add Message to DOM | |
| function addMessage(text, sender) { | |
| const wrapper = document.createElement('div'); | |
| wrapper.className = 'message-wrapper'; | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message ${sender}`; | |
| const time = new Date().toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }); | |
| let avatarHtml = ''; | |
| if (sender === 'ai') { | |
| avatarHtml = `<div class="message-avatar ai-avatar"><i class="ri-sparkling-fill"></i></div>`; | |
| } else { | |
| avatarHtml = `<img src="https://picsum.photos/seed/user123/64/64" class="message-avatar user-avatar-img" alt="User">`; | |
| } | |
| messageDiv.innerHTML = ` | |
| ${avatarHtml} | |
| <div class="message-content"> | |
| ${formatText(text)} | |
| <div class="message-time">${time}</div> | |
| </div> | |
| `; | |
| wrapper.appendChild(messageDiv); | |
| chatContainer.appendChild(wrapper); | |
| scrollToBottom(); | |
| } | |
| // Show Typing Indicator | |
| function showTypingIndicator() { | |
| const id = 'typing-' + Date.now(); | |
| const wrapper = document.createElement('div'); | |
| wrapper.className = 'message-wrapper'; | |
| wrapper.id = id; | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = 'message ai'; | |
| messageDiv.innerHTML = ` | |
| <div class="message-avatar ai-avatar"><i class="ri-sparkling-fill"></i></div> | |
| <div class="message-content"> | |
| <div class="typing-indicator"> | |
| <div class="dot"></div> | |
| <div class="dot"></div> | |
| <div class="dot"></div> | |
| </div> | |
| </div> | |
| `; | |
| wrapper.appendChild(messageDiv); | |
| chatContainer.appendChild(wrapper); | |
| scrollToBottom(); | |
| return id; | |
| } | |
| // Remove Message (Typing indicator) | |
| function removeMessage(id) { | |
| const el = document.getElementById(id); | |
| if (el) el.remove(); | |
| } | |
| // Scroll to bottom | |
| function scrollToBottom() { | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } | |
| // Simple Text Formatter (Bold, Code blocks) | |
| function formatText(text) { | |
| // Replace **text** with <b>text</b> | |
| text = text.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>'); | |
| // Replace `code` with <code>code</code> (simple inline code) | |
| text = text.replace(/`(.*?)`/g, '<code style="background:rgba(255,255,255,0.1); padding:2px 4px; border-radius:4px; font-family:monospace;">$1</code>'); | |
| return text; | |
| } | |
| // Simple AI Response Simulator | |
| function generateAIResponse(input) { | |
| const lowerInput = input.toLowerCase(); | |
| if (lowerInput.includes('bonjour') || lowerInput.includes('salut') || lowerInput.includes('hello')) { | |
| return "Bonjour ! Ravi de vous revoir. Sur quel sujet souhaitez-vous échanger aujourd'hui ?"; | |
| } | |
| if (lowerInput.includes('code') || lowerInput.includes('html') || lowerInput.includes('css') || lowerInput.includes('js')) { | |
| return "Je peux vous aider avec le code ! Voici un exemple de structure HTML :\n\n`<div class='container'>Contenu</div>`\n\nVoulez-vous que je développe un composant spécifique ?"; | |
| } | |
| if (lowerInput.includes('qui es-tu') || lowerInput.includes('ton nom')) { | |
| return "Je suis **Répond AI**, une interface de démonstration conçue pour montrer une expérience utilisateur moderne et fluide."; | |
| } | |
| if (lowerInput.includes('merci')) { | |
| return "Je vous en prie ! N'hésitez pas si vous avez d'autres questions."; | |
| } | |
| if (lowerInput.includes('aide') || lowerInput.includes('help')) { | |
| return "Bien sûr ! Je peux répondre à des questions, générer du texte, simuler du code, ou simplement discuter. Essayez de me demander 'Explique-moi le CSS Flexbox'."; | |
| } | |
| // Fallback generic responses | |
| const generics = [ | |
| "C'est une perspective intéressante. Pouvez-vous développer ?", | |
| "Je comprends. Avez-vous considéré d'autres approches pour ce problème ?", | |
| "C'est noté. Je peux vous fournir plus de détails sur ce sujet si vous le souhaitez.", | |
| "Intéressant ! Dites-m'en plus.", | |
| "Je suis une IA, donc j'apprends de chaque interaction. Merci pour votre question !" | |
| ]; | |
| return generics[Math.floor(Math.random() * generics.length)]; | |
| } | |
| // New Chat Button | |
| newChatBtn.addEventListener('click', () => { | |
| chatContainer.innerHTML = ''; | |
| // Re-add welcome message | |
| const wrapper = document.createElement('div'); | |
| wrapper.className = 'message-wrapper'; | |
| wrapper.innerHTML = ` | |
| <div class="message ai"> | |
| <div class="message-avatar ai-avatar"><i class="ri-sparkling-fill"></i></div> | |
| <div class="message-content"> | |
| Nouvelle discussion commencée. Je suis prêt ! | |
| <div class="message-time">Maintenant</div> | |
| </div> | |
| </div> | |
| `; | |
| chatContainer.appendChild(wrapper); | |
| if (window.innerWidth <= 768) sidebar.classList.remove('open'); | |
| }); | |
| // Toast Helper | |
| function showToast(message) { | |
| toast.textContent = message; | |
| toast.classList.add('show'); | |
| setTimeout(() => { | |
| toast.classList.remove('show'); | |
| }, 3000); | |
| } | |
| </script> | |
| </body> | |
| </html> |