Spaces:
Runtime error
Runtime error
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <title>MaxiQueen OS v3 | IA de Voz 👑</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| body { | |
| background: #050505; | |
| background-image: radial-gradient(circle at 50% 0%, #1a1a24 0%, #050505 70%); | |
| font-family: 'Inter', system-ui, sans-serif; | |
| } | |
| ::-webkit-scrollbar { width: 6px; } | |
| ::-webkit-scrollbar-track { background: transparent; } | |
| ::-webkit-scrollbar-thumb { background: #3f3f46; border-radius: 10px; } | |
| ::-webkit-scrollbar-thumb:hover { background: #52525b; } | |
| .fade-in-up { animation: fadeInUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards; } | |
| @keyframes fadeInUp { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .typing-dot { animation: typing 1.4s infinite ease-in-out both; } | |
| .typing-dot:nth-child(1) { animation-delay: -0.32s; } | |
| .typing-dot:nth-child(2) { animation-delay: -0.16s; } | |
| @keyframes typing { | |
| 0%, 80%, 100% { transform: scale(0); } | |
| 40% { transform: scale(1); } | |
| } | |
| /* Animación para el micrófono cuando está escuchando */ | |
| .mic-listening { | |
| animation: pulse-red 1.5s infinite; | |
| background-color: #ef4444 ; /* Rojo */ | |
| color: white ; | |
| } | |
| @keyframes pulse-red { | |
| 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); } | |
| 70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); } | |
| 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen flex flex-col items-center justify-between text-zinc-100 selection:bg-amber-500 selection:text-black"> | |
| <main class="w-full max-w-2xl p-4 md:p-6 flex-1 flex flex-col"> | |
| <header class="relative text-center mb-6 mt-4"> | |
| <h1 class="text-3xl font-extrabold tracking-tight text-transparent bg-clip-text bg-gradient-to-r from-amber-400 to-amber-200 drop-shadow-[0_0_8px_rgba(251,191,36,0.3)]"> | |
| 👑 MaxiQueen OS | |
| </h1> | |
| <p class="text-sm text-zinc-400 mt-1 tracking-widest uppercase">Ecosistema de Inteligencia Estratégica</p> | |
| <!-- Controles Superiores --> | |
| <div class="absolute top-0 right-0 flex gap-2"> | |
| <button id="voiceToggleBtn" onclick="toggleVoice()" class="p-2 text-zinc-500 hover:text-amber-400 transition-colors tooltip" title="Activar/Desactivar Voz de la IA"> | |
| <svg id="icon-vol-off" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" clip-rule="evenodd"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2"></path></svg> | |
| <svg id="icon-vol-on" class="w-5 h-5 hidden text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"></path></svg> | |
| </button> | |
| <button onclick="restartChat()" class="p-2 text-zinc-500 hover:text-amber-400 transition-colors tooltip" title="Reiniciar Sistema"> | |
| <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg> | |
| </button> | |
| </div> | |
| </header> | |
| <div class="relative flex-1 bg-black/60 backdrop-blur-xl border border-white/10 rounded-2xl shadow-2xl flex flex-col overflow-hidden"> | |
| <div id="chatLog" class="flex-1 overflow-y-auto p-5 space-y-6 scroll-smooth"> | |
| <!-- Los mensajes irán aquí --> | |
| </div> | |
| <div id="typingIndicator" class="hidden px-5 pb-2"> | |
| <div class="bg-zinc-800/80 border border-white/5 w-fit rounded-2xl px-4 py-3 flex items-center gap-1 shadow-lg"> | |
| <div class="w-2 h-2 bg-amber-400 rounded-full typing-dot"></div> | |
| <div class="w-2 h-2 bg-amber-400 rounded-full typing-dot"></div> | |
| <div class="w-2 h-2 bg-amber-400 rounded-full typing-dot"></div> | |
| </div> | |
| </div> | |
| <div class="p-4 bg-zinc-900/80 backdrop-blur-md border-t border-white/5"> | |
| <div class="flex gap-2 sm:gap-3 items-center"> | |
| <!-- Botón de Micrófono --> | |
| <button id="micBtn" onclick="toggleListening()" class="bg-zinc-800 text-zinc-400 border border-white/10 p-3 rounded-xl hover:text-amber-400 transition-all flex items-center justify-center disabled:opacity-50" title="Dictar por voz"> | |
| <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"></path></svg> | |
| </button> | |
| <input id="userInput" | |
| type="text" | |
| placeholder="Escribe o dicta tu respuesta..." | |
| class="flex-1 bg-black/50 border border-white/10 focus:border-amber-400/50 focus:shadow-[0_0_10px_rgba(251,191,36,0.1)] rounded-xl px-4 py-3 text-white outline-none transition-all placeholder:text-zinc-600 disabled:opacity-50 disabled:cursor-not-allowed" | |
| autocomplete="off" /> | |
| <button id="sendBtn" onclick="handleUserSubmit()" | |
| class="bg-amber-400 text-black font-bold px-5 py-3 rounded-xl hover:bg-amber-300 hover:shadow-[0_0_15px_rgba(251,191,36,0.4)] transition-all active:scale-95 flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:shadow-none disabled:active:scale-100"> | |
| <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path></svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <script> | |
| /* ========================= 🧠 ESTADO GLOBAL ========================= */ | |
| let state = { | |
| flujoActual: 'inicio', | |
| datosRecopilados: {}, | |
| esperandoRespuesta: false | |
| }; | |
| let isVoiceEnabled = false; // Por defecto apagado para no asustar al usuario | |
| /* ========================= 🎙️ MOTOR DE VOZ (IA) ========================= */ | |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
| let recognition; | |
| let isListening = false; | |
| const micBtn = document.getElementById("micBtn"); | |
| // Inicializar reconocimiento de voz si el navegador lo soporta | |
| if (SpeechRecognition) { | |
| recognition = new SpeechRecognition(); | |
| recognition.lang = 'es-CO'; // Español Colombia | |
| recognition.interimResults = false; | |
| recognition.onstart = () => { | |
| isListening = true; | |
| micBtn.classList.add("mic-listening"); | |
| document.getElementById("userInput").placeholder = "Escuchando..."; | |
| }; | |
| recognition.onresult = (event) => { | |
| const transcript = event.results[0][0].transcript; | |
| document.getElementById("userInput").value = transcript; | |
| // Autocompletar y enviar tras dictar | |
| setTimeout(() => handleUserSubmit(), 500); | |
| }; | |
| recognition.onend = () => { | |
| isListening = false; | |
| micBtn.classList.remove("mic-listening"); | |
| document.getElementById("userInput").placeholder = "Escribe o dicta tu respuesta..."; | |
| }; | |
| recognition.onerror = (event) => { | |
| console.warn("Error de voz: ", event.error); | |
| isListening = false; | |
| micBtn.classList.remove("mic-listening"); | |
| document.getElementById("userInput").placeholder = "Escribe o dicta tu respuesta..."; | |
| }; | |
| } else { | |
| micBtn.style.display = "none"; // Ocultar si el navegador no lo soporta | |
| } | |
| function toggleListening() { | |
| if (!recognition) return alert("Tu navegador no soporta reconocimiento de voz."); | |
| if (isListening) { | |
| recognition.stop(); | |
| } else { | |
| recognition.start(); | |
| } | |
| } | |
| // Lógica de Text-to-Speech (IA Hablando) | |
| function toggleVoice() { | |
| isVoiceEnabled = !isVoiceEnabled; | |
| document.getElementById("icon-vol-off").classList.toggle("hidden"); | |
| document.getElementById("icon-vol-on").classList.toggle("hidden"); | |
| if (!isVoiceEnabled && window.speechSynthesis) { | |
| window.speechSynthesis.cancel(); // Callar inmediatamente si se apaga | |
| } | |
| } | |
| function speakText(text) { | |
| if (!isVoiceEnabled || !('speechSynthesis' in window)) return; | |
| window.speechSynthesis.cancel(); // Cortar audio anterior si hay | |
| // Limpiar emojis, asteriscos y saltos de línea para la lectura | |
| const cleanText = text | |
| .replace(/[\u{1F600}-\u{1F6FF}|\u{2600}-\u{26FF}]/gu, '') // quitar emojis | |
| .replace(/\*/g, '') // quitar negritas | |
| .replace(/\n/g, '. '); // pausas en saltos de linea | |
| const utterance = new SpeechSynthesisUtterance(cleanText); | |
| utterance.lang = 'es-CO'; // Español | |
| utterance.pitch = 1.1; // Tono ligeramente alterado para que no suene tan robot genérico | |
| utterance.rate = 1.05; // Velocidad un poquito más rápida | |
| window.speechSynthesis.speak(utterance); | |
| } | |
| /* ========================= 📦 BASE DE CONOCIMIENTO ========================= */ | |
| const knowledgeBase = { | |
| inicio: { | |
| mensaje: `Hola 👑\n\nEstoy aquí contigo. No soy un bot de respuestas rápidas; soy el núcleo conversacional con Inteligencia de Voz de MaxiQueen OS.\n\nMi objetivo es entenderte. ¿En qué punto sientes que estás ahora mismo?`, | |
| opciones: [ | |
| "1️⃣ Tengo ideas, pero me falta orden", | |
| "2️⃣ Estoy bloqueado o saturado", | |
| "3️⃣ Quiero crecer y vender más" | |
| ], | |
| procesar: (texto) => { | |
| if (texto.includes("idea") || texto.includes("1")) return 'ideas'; | |
| if (texto.includes("bloque") || texto.includes("saturado") || texto.includes("2")) return 'bloqueo'; | |
| if (texto.includes("crecer") || texto.includes("vender") || texto.includes("3")) return 'crecimiento'; | |
| return 'incomprendido'; | |
| } | |
| }, | |
| ideas: { | |
| mensaje: `Entendido. Tienes la materia prima, pero te falta la arquitectura.\n\nTener ideas sin un sistema genera ruido mental y poca ejecución. ¿Qué es lo que más te cuesta actualmente?`, | |
| opciones: ["No sé por dónde empezar", "Me cuesta mantener el enfoque", "No sé monetizar"], | |
| procesar: (t) => 'solucion_starter' | |
| }, | |
| bloqueo: { | |
| mensaje: `Gracias por la honestidad.\n\nEl bloqueo es exceso de decisiones sin jerarquía clara. Tu mente se satura. ¿Es por falta de dirección o estás haciendo todo solo?`, | |
| opciones: ["Falta dirección", "Hago todo solo", "Mezcla de ambas"], | |
| procesar: (t) => 'solucion_starter' | |
| }, | |
| crecimiento: { | |
| mensaje: `Excelente. Si piensas en crecimiento, la base existe. \n\nPero necesitas dejar el esfuerzo manual y automatizar. ¿Qué parte es la más lenta para ti hoy?`, | |
| opciones: ["Creación de contenido", "Cierre de ventas", "Entrega del servicio"], | |
| procesar: (t) => 'solucion_pro' | |
| }, | |
| solucion_starter: { | |
| mensaje: `Necesitas **estructura base**, lo que llamamos fase 'Starter'. Limpiar el ruido y trazar la ruta de acción. \n\n¿Te explico cómo estructuramos esto paso a paso?`, | |
| opciones: ["Sí, explícame", "Prefiero hablar con un humano"], | |
| procesar: (t) => t.includes("humano") ? 'contacto_humano' : 'explicacion_starter' | |
| }, | |
| solucion_pro: { | |
| mensaje: `La solución es sistematizar. Esta es la arquitectura 'Pro'. Implica integrar automatización y CRMs bajo una lógica humana 24/7.\n\n¿Quieres ver cómo diseñamos estos ecosistemas?`, | |
| opciones: ["Sí, muéstrame", "Necesito un asesor"], | |
| procesar: (t) => (t.includes("asesor") || t.includes("humano")) ? 'contacto_humano' : 'explicacion_pro' | |
| }, | |
| explicacion_starter: { | |
| mensaje: `La ruta:\n1. Diagnóstico Profundo.\n2. Arquitectura de prioridades.\n3. Ejecución Asistida.\n\nEl objetivo es trabajar con sentido. ¿Tiene sentido para ti?`, | |
| opciones: ["Tiene sentido", "Tengo dudas"], | |
| procesar: () => 'contacto_humano' | |
| }, | |
| explicacion_pro: { | |
| mensaje: `Diseñamos el ecosistema así:\n1. Auditoría de flujos.\n2. Ingeniería y centralización.\n3. Delegación de tareas repetitivas al sistema.\n\n¿Cómo avanzamos?`, | |
| opciones: ["Me interesa", "Quiero ver planes"], | |
| procesar: () => 'contacto_humano' | |
| }, | |
| contacto_humano: { | |
| mensaje: `👑 Llegamos al punto donde la tecnología da paso a la interacción real.\n\nPara adaptar el sistema a ti, hablemos directamente. ¿Qué canal prefieres?`, | |
| opciones: ["📲 Continuar por WhatsApp", "📸 Hablar por Instagram"], | |
| procesar: (t) => { | |
| if(t.includes("whatsapp")) { window.open("https://wa.me/573016625921", "_blank"); return 'despedida'; } | |
| if(t.includes("instagram")) { window.open("https://instagram.com/maxiqueenos", "_blank"); return 'despedida'; } | |
| return 'incomprendido'; | |
| } | |
| }, | |
| despedida: { | |
| mensaje: `Excelente. Te espero por allá para continuar construyendo. ¡Seguimos avanzando! 👑`, | |
| opciones: [], | |
| procesar: () => 'inicio' | |
| }, | |
| incomprendido: { | |
| mensaje: `Interesante perspectiva. Para guiarte correctamente, elige una de las opciones o usa otras palabras por favor.`, | |
| opciones: [], | |
| procesar: (t) => knowledgeBase[state.flujoActual].procesar(t) | |
| } | |
| }; | |
| /* ========================= 🧩 UI CONTROLLERS ========================= */ | |
| const chatLog = document.getElementById("chatLog"); | |
| const input = document.getElementById("userInput"); | |
| const typingIndicator = document.getElementById("typingIndicator"); | |
| const sendBtn = document.getElementById("sendBtn"); | |
| function getTime() { | |
| return new Date().toLocaleTimeString('es-CO', { hour: '2-digit', minute: '2-digit' }); | |
| } | |
| function scrollBottom() { | |
| setTimeout(() => chatLog.scrollTop = chatLog.scrollHeight, 50); | |
| } | |
| function renderOptions(options) { | |
| if (!options || options.length === 0) return ''; | |
| let buttonsHtml = options.map(opt => | |
| `<button onclick="handleQuickReply('${opt}')" | |
| class="block w-full text-left bg-zinc-800/80 hover:bg-amber-400 hover:text-black border border-white/5 hover:border-amber-400 transition-all rounded-xl px-4 py-3 text-sm font-medium shadow-sm"> | |
| ${opt} | |
| </button>` | |
| ).join(''); | |
| return `<div class="mt-4 space-y-2 fade-in-up" style="animation-delay: 0.2s">${buttonsHtml}</div>`; | |
| } | |
| function appendMessage(text, sender = "bot", options = []) { | |
| const msgDiv = document.createElement("div"); | |
| msgDiv.className = `flex flex-col max-w-[85%] fade-in-up ${sender === "user" ? "ml-auto items-end" : "mr-auto items-start"}`; | |
| const bubble = document.createElement("div"); | |
| bubble.className = sender === "user" | |
| ? "bg-amber-400 text-black font-semibold rounded-2xl rounded-tr-sm px-5 py-3 shadow-md" | |
| : "bg-zinc-800/90 text-zinc-200 rounded-2xl rounded-tl-sm px-5 py-4 border border-white/10 leading-relaxed whitespace-pre-wrap shadow-lg"; | |
| bubble.innerHTML = text + `<span class="block text-[10px] mt-1 ${sender === 'user' ? 'text-black/60 text-right' : 'text-zinc-500 text-left'}">${getTime()}</span>`; | |
| msgDiv.appendChild(bubble); | |
| if (sender === "bot" && options.length > 0) { | |
| msgDiv.innerHTML += renderOptions(options); | |
| } | |
| chatLog.appendChild(msgDiv); | |
| scrollBottom(); | |
| // Si es el bot, lanzar motor de voz | |
| if(sender === "bot") { | |
| speakText(text); | |
| } | |
| } | |
| function showTyping() { | |
| typingIndicator.classList.remove("hidden"); | |
| input.disabled = true; | |
| sendBtn.disabled = true; | |
| if(micBtn) micBtn.disabled = true; | |
| scrollBottom(); | |
| } | |
| function hideTyping() { | |
| typingIndicator.classList.add("hidden"); | |
| input.disabled = false; | |
| sendBtn.disabled = false; | |
| if(micBtn) micBtn.disabled = false; | |
| input.focus(); | |
| } | |
| function processInput(text) { | |
| const currentStateNode = knowledgeBase[state.flujoActual]; | |
| let nextStateKey = currentStateNode.procesar(text.toLowerCase()); | |
| if (nextStateKey === 'incomprendido') { | |
| const fallbackNode = knowledgeBase['incomprendido']; | |
| simulateBotResponse(fallbackNode.mensaje, currentStateNode.opciones); | |
| } else { | |
| state.flujoActual = nextStateKey; | |
| const nextNode = knowledgeBase[nextStateKey]; | |
| simulateBotResponse(nextNode.mensaje, nextNode.opciones); | |
| } | |
| } | |
| function simulateBotResponse(text, options) { | |
| showTyping(); | |
| const delay = Math.max(800, text.length * 15); | |
| setTimeout(() => { | |
| hideTyping(); | |
| appendMessage(text, "bot", options); | |
| }, delay > 2500 ? 2500 : delay); | |
| } | |
| function handleUserSubmit() { | |
| const message = input.value.trim(); | |
| if (!message) return; | |
| // Apagar micrófono automáticamente si estaba prendido al enviar por texto | |
| if(isListening && recognition) recognition.stop(); | |
| appendMessage(message, "user"); | |
| input.value = ""; | |
| processInput(message); | |
| } | |
| function handleQuickReply(text) { | |
| appendMessage(text, "user"); | |
| processInput(text); | |
| } | |
| function restartChat() { | |
| chatLog.innerHTML = ''; | |
| state.flujoActual = 'inicio'; | |
| state.datosRecopilados = {}; | |
| input.value = ""; | |
| if(window.speechSynthesis) window.speechSynthesis.cancel(); | |
| const inicioNode = knowledgeBase['inicio']; | |
| simulateBotResponse(inicioNode.mensaje, inicioNode.opciones); | |
| } | |
| input.addEventListener("keydown", e => { | |
| if (e.key === "Enter") handleUserSubmit(); | |
| }); | |
| window.onload = () => { | |
| const inicioNode = knowledgeBase['inicio']; | |
| simulateBotResponse(inicioNode.mensaje, inicioNode.opciones); | |
| }; | |
| </script> | |
| </body> | |
| </html> | |