Download modules/interface.py from CVision-assitant/asistente-virtual-CVision: direct link, hf CLI and curl.
- Browser
- Download file 69.9 kB
-
https://huggingface.co/spaces/CVision-assitant/asistente-virtual-CVision/resolve/main/modules/interface.py
- Command line
-
hf download hf://spaces/CVision-assitant/asistente-virtual-CVision/modules/interface.py
-
curl -L -o interface.py https://huggingface.co/spaces/CVision-assitant/asistente-virtual-CVision/resolve/main/modules/interface.py
69.9 kB
| # ===================================================================== | |
| # SECCIΓN 1: IMPORTACIONES ESTΓNDAR Y DE SISTEMA | |
| # ===================================================================== | |
| import gradio as gr | |
| import re | |
| import os | |
| # ===================================================================== | |
| # SECCIΓN 2: MΓDULOS DE AUDIO Y TRANSCRIPCIΓN (CVISION) | |
| # ===================================================================== | |
| from modules.listen import transcribe_audio_file | |
| from modules.talker.talk import speak | |
| # ===================================================================== | |
| # SECCIΓN 3: ACOPLAMIENTO DEL MΓDULO DE VOZ MODULAR (DESDE 0) | |
| # ===================================================================== | |
| from modules.langchain_assistant.voice_module import create_voice_tab | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # FALLBACK CHAT β MΓ³dulos de respaldo si falla la importaciΓ³n | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _fallback_chat(history, message, *args, **kwargs): | |
| yield (history or []) + [[message, "β οΈ MΓ³dulo no disponible. Revisa los logs del servidor."]], None | |
| try: | |
| from modules.utils.chat_utils import ( | |
| productivity_chat, | |
| browser_chat, | |
| scientist_chat, | |
| regular_chat, | |
| document_chat, | |
| ) | |
| print("β MΓ³dulos de chat importados correctamente") | |
| except Exception as e: | |
| print(f"β Error importando mΓ³dulos de chat: {e}") | |
| regular_chat = _fallback_chat | |
| browser_chat = _fallback_chat | |
| productivity_chat = _fallback_chat | |
| scientist_chat = _fallback_chat | |
| document_chat = _fallback_chat | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CONSTANTES GLOBALES | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| PLACEHOLDER = "Escribe tu mensaje para CVision..." | |
| AVATAR_URL = "https://huggingface.co/spaces/CVision-assitant/asistente-virtual-CVision/resolve/main/assets/robot-bckound-black.png" | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CSS GLOBAL β Estilos de toda la interfaz | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CUSTOM_CSS = """ | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Exo+2:wght@300;400;500&family=Share+Tech+Mono&display=swap'); | |
| /* ββ VARIABLES CSS GLOBALES ββ | |
| Modifica aquΓ los colores principales de toda la app */ | |
| :root { | |
| --cyan:#00d4ff; /* Color principal cian */ | |
| --cyan2:#00a8e8; /* Cian secundario */ | |
| --cg:#00ff88; /* Color verde neΓ³n (online/activo) */ | |
| --bg-main:#020408; /* Fondo principal oscuro */ | |
| --border:rgba(0,212,255,0.16); | |
| --font-d:'Orbitron',monospace; /* Fuente display/tΓtulos */ | |
| --font-b:'Exo 2',sans-serif; /* Fuente body */ | |
| --font-m:'Share Tech Mono',monospace; /* Fuente monospace/datos */ | |
| } | |
| /* ββ RESET Y BASE ββ */ | |
| html,body { overflow-x:hidden !important; } | |
| body,.gradio-container { | |
| background:var(--bg-main) !important; | |
| color:white !important; | |
| font-family:'Exo 2','Segoe UI',sans-serif; | |
| } | |
| /* ββ LΓNEA DE ESCANEO ANIMADA (scan line global) ββ | |
| Desactivar: comentar este bloque */ | |
| .gradio-container::after { | |
| content:''; position:fixed; top:0; left:0; width:100%; height:2px; | |
| background:linear-gradient(90deg,transparent,rgba(0,212,255,0.30),transparent); | |
| animation:cvScanDown 7s ease-in-out infinite; | |
| z-index:9999; pointer-events:none; | |
| } | |
| /* ββ KEYFRAMES β Todas las animaciones CSS ββ | |
| Modifica aquΓ velocidades/efectos de animaciΓ³n */ | |
| @keyframes cvScanDown { 0%{top:-2px;opacity:0} 8%{opacity:1} 92%{opacity:.4} 100%{top:100vh;opacity:0} } | |
| @keyframes cvSpin { to{transform:rotate(360deg)} } | |
| @keyframes cvBlink { 0%,100%{opacity:.25} 50%{opacity:1} } | |
| @keyframes cvOrbit { to{transform:rotate(360deg)} } | |
| @keyframes cvFloat { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-4px)} } | |
| @keyframes pulse { 0%,100%{transform:scale(1);opacity:1} 50%{transform:scale(1.3);opacity:.7} } | |
| @keyframes bglow { 0%,100%{transform:scale(1);opacity:.5} 50%{transform:scale(1.2);opacity:1} } | |
| @keyframes radarSpin { to{transform:rotate(360deg)} } | |
| @keyframes headerShimmer { 0%,100%{opacity:0} 50%{opacity:1} } | |
| @keyframes btnGlow { 0%,100%{box-shadow:0 0 12px rgba(0,212,255,.3),0 0 24px rgba(0,212,255,.1)} 50%{box-shadow:0 0 22px rgba(0,212,255,.7),0 0 44px rgba(0,212,255,.25)} } | |
| @keyframes avatar-pulse { | |
| 0% { border-color:rgba(0,212,255,0.3); box-shadow:0 0 5px rgba(0,212,255,0.2); } | |
| 50% { border-color:rgba(0,212,255,1); box-shadow:0 0 18px rgba(0,212,255,0.6); } | |
| 100%{ border-color:rgba(0,212,255,0.3); box-shadow:0 0 5px rgba(0,212,255,0.2); } | |
| } | |
| /* ββ HEADER β Barra superior con logo y tΓtulo ββ | |
| Modificar padding/height aquΓ para cambiar tamaΓ±o del header */ | |
| #cvision-header { | |
| background:rgba(1,4,12,0.98); | |
| border-bottom:1px solid rgba(0,212,255,0.18); | |
| padding:12px 28px; display:flex; align-items:center; gap:16px; | |
| backdrop-filter:blur(24px); position:relative; z-index:10; flex-shrink:0; | |
| box-shadow:0 4px 40px rgba(0,0,0,.8),0 0 30px rgba(0,212,255,.04); | |
| } | |
| /* LΓnea inferior animada del header */ | |
| #cvision-header::after { | |
| content:''; position:absolute; bottom:0; left:0; right:0; height:1px; | |
| background:linear-gradient(90deg,transparent,rgba(0,212,255,.5) 30%,rgba(0,255,200,.7) 50%,rgba(0,212,255,.5) 70%,transparent); | |
| animation:headerShimmer 3s ease-in-out infinite; | |
| } | |
| /* ββ NEBULA SECTION β Contenedor de la secciΓ³n hero (nebula + info + panel derecho) ββ | |
| Modificar padding/gap aquΓ para espaciado de la secciΓ³n */ | |
| #cvision-nebula-section { | |
| position:relative; display:flex; align-items:center; gap:24px; | |
| padding:28px 32px; border-bottom:1px solid rgba(0,212,255,0.10); | |
| background:linear-gradient(180deg,rgba(0,8,20,0.97),rgba(1,6,16,0.99)); | |
| flex-shrink:0; overflow:hidden; | |
| } | |
| /* ββ GRID CANVAS β Canvas de fondo con cuadrΓcula y puntos ββ | |
| Es el fondo animado de la secciΓ³n nebula */ | |
| #cv-grid-canvas { | |
| position:absolute; inset:0; | |
| width:100% !important; height:100% !important; | |
| pointer-events:none; z-index:0; | |
| } | |
| /* ββ NEBULA CANVAS WRAPPER β Contenedor del canvas de la esfera nebula ββ | |
| Modificar width/height aquΓ para cambiar tamaΓ±o de la nebula */ | |
| #cv-nebula-wrap { | |
| position:relative; width:420px; height:420px; | |
| flex-shrink:0; z-index:2; | |
| } | |
| /* TamaΓ±o del canvas de la nebula (debe coincidir con el wrapper) */ | |
| #nebulaCanvas { width:420px !important; height:420px !important; display:block; position:relative; z-index:2; } | |
| /* ββ HERO INFO β Bloque de texto a la derecha de la nebula ββ | |
| TΓtulo, descripciΓ³n y chips de capacidades */ | |
| #cv-hero-info { | |
| position:relative; z-index:2; | |
| display:flex; flex-direction:column; gap:13px; | |
| flex:1; min-width:0; | |
| } | |
| /* Texto de descripciΓ³n del hero β modificar font-size para tamaΓ±o */ | |
| #cv-hero-info .cv-desc { | |
| font-family:'Exo 2',sans-serif; | |
| font-size:1.08rem !important; | |
| color:rgba(90,160,190,.85) !important; | |
| line-height:1.85 !important; | |
| font-weight:300; | |
| max-width:460px; | |
| } | |
| /* ββ RIGHT PANEL β Panel lateral derecho (NΓΊcleo Neural + Conectividad) ββ | |
| Modificar width para cambiar ancho del panel */ | |
| #cv-right-panel { | |
| position:relative; z-index:2; | |
| display:flex; flex-direction:column; gap:10px; | |
| width:210px; flex-shrink:0; | |
| } | |
| /* ββ RCARD β Tarjetas del panel derecho ββ | |
| Estilos compartidos por NΓΊcleo Neural y Conectividad */ | |
| .rcard { | |
| background:rgba(2,7,18,.92); border:1px solid rgba(0,212,255,.12); | |
| border-radius:12px; overflow:hidden; position:relative; | |
| box-shadow:0 0 30px rgba(0,0,0,.5); | |
| } | |
| .rcard::before { content:''; position:absolute; top:0; left:0; right:0; height:1px; background:linear-gradient(90deg,transparent,rgba(0,212,255,.4),rgba(0,255,200,.2),transparent); } | |
| .rcard::after { content:''; position:absolute; bottom:0; left:0; right:0; height:1px; background:linear-gradient(90deg,transparent,rgba(0,212,255,.1),transparent); } | |
| /* ββ RCARD HEADER β Cabecera de las tarjetas del panel derecho ββ */ | |
| .rh { display:flex; align-items:center; gap:6px; padding:8px 12px; border-bottom:1px solid rgba(0,212,255,.06); } | |
| .rh-dot { width:5px; height:5px; border-radius:50%; background:var(--cyan); box-shadow:0 0 6px var(--cyan); flex-shrink:0; animation:pulse 2s infinite; } | |
| .rh-t { font-family:'Orbitron',monospace; font-size:8px; font-weight:700; color:rgba(0,190,240,.6); letter-spacing:.15em; } | |
| .rh-s { margin-left:auto; font-family:'Share Tech Mono',monospace; font-size:7.5px; color:rgba(0,150,200,.35); letter-spacing:.1em; } | |
| /* ββ BRAIN BOX β Contenedor del canvas del cerebro neural ββ | |
| Modificar height para cambiar altura del Γ‘rea del cerebro */ | |
| .brain-box { padding:6px; display:flex; align-items:center; justify-content:center; height:180px; position:relative; } | |
| /* Glow del fondo del cerebro */ | |
| .bglow { position:absolute; width:150px; height:150px; border-radius:50%; background:radial-gradient(circle,rgba(0,100,255,.12),transparent 70%); animation:bglow 2s ease-in-out infinite; } | |
| /* TamaΓ±o del canvas del cerebro β modificar width/height */ | |
| #brainCvs { width:198px; height:168px; } | |
| /* Anillos de radar decorativos */ | |
| .rring { position:absolute; border-radius:50%; border:1px solid rgba(0,212,255,.08); } | |
| .rring1 { inset:10px; animation:radarSpin 8s linear infinite; } | |
| .rring2 { inset:25px; animation:radarSpin 12s linear infinite reverse; } | |
| /* ββ CONNECTIVITY CARD β Tarjeta de conectividad/mapa mundial ββ */ | |
| .conn-body { padding:9px 12px; } | |
| .cstat { display:flex; align-items:center; gap:7px; margin-bottom:5px; } | |
| .cdot { width:8px; height:8px; border-radius:50%; background:var(--cg); box-shadow:0 0 12px var(--cg); animation:pulse 1.5s infinite; } | |
| .cst { font-family:'Share Tech Mono',monospace; font-size:9px; color:var(--cg); letter-spacing:.12em; } | |
| .clat { font-family:'Share Tech Mono',monospace; font-size:8px; color:rgba(0,150,200,.4); letter-spacing:.1em; margin-bottom:7px; } | |
| /* Canvas del mapa mundial β modificar width/height */ | |
| #worldC { width:100%; height:52px; } | |
| /* ββ STATUS BAR β Barra de estado inferior fija ββ | |
| Modificar bottom para subir/bajar, font-size para tamaΓ±o */ | |
| #cvision-statusbar { | |
| position:fixed; | |
| bottom:4px; | |
| left:50%; transform:translateX(-50%); | |
| font-family:var(--font-m); font-size:0.75rem; letter-spacing:0.14em; | |
| color:rgba(0,212,255,0.75); display:flex; align-items:center; gap:14px; | |
| pointer-events:none; z-index:500; white-space:nowrap; | |
| padding:6px 20px; | |
| background:rgba(0,6,16,0.80); backdrop-filter:blur(12px); | |
| border:1px solid rgba(0,212,255,0.22); border-radius:8px; | |
| box-shadow:0 0 16px rgba(0,212,255,0.10); | |
| } | |
| /* Puntos separadores de la status bar */ | |
| .cv-sdot { width:4px; height:4px; border-radius:50%; background:rgba(0,212,255,0.5); animation:cvBlink 2s ease-in-out infinite; display:inline-block; } | |
| /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CHATBOT β Contenedor principal y burbujas de mensajes | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| /* ββ Contenedor externo del chatbot ββ */ | |
| .chatbot-container { | |
| border-radius:16px !important; | |
| border:1px solid rgba(0,210,255,0.25) !important; | |
| background:rgba(2,10,20,0.98) !important; | |
| box-shadow:0 12px 50px rgba(0,0,0,.8) !important; | |
| overflow:hidden !important; | |
| } | |
| /* ββ Wrapper interno (lista de mensajes) ββ */ | |
| .chatbot-container > div.wrapper { | |
| display:flex !important; | |
| flex-direction:column !important; | |
| gap:14px !important; | |
| padding:16px !important; | |
| } | |
| /* ββ BASE de todos los mensajes ββ | |
| NO poner display:block ni width fijos aquΓ */ | |
| .chatbot-container .message { | |
| max-width:82% !important; | |
| padding:12px 16px !important; | |
| font-size:14px !important; | |
| line-height:1.65 !important; | |
| position:relative !important; | |
| box-sizing:border-box !important; | |
| overflow-wrap:break-word !important; | |
| word-break:break-word !important; | |
| white-space:normal !important; | |
| hyphens:auto !important; | |
| } | |
| /* ββ BURBUJA USUARIO (derecha) ββ | |
| Modificar background/border para cambiar color del usuario */ | |
| .chatbot-container .message.user, | |
| .chatbot-container [data-testid="user"] { | |
| align-self:flex-end !important; | |
| margin-left:auto !important; | |
| margin-right:6px !important; | |
| width:fit-content !important; | |
| min-width:60px !important; | |
| max-width:75% !important; | |
| background:linear-gradient(145deg,rgba(0,80,160,0.82),rgba(0,40,95,0.96)) !important; | |
| border:1px solid rgba(0,180,255,0.45) !important; | |
| border-radius:18px 18px 4px 18px !important; | |
| color:#ffffff !important; | |
| box-shadow:0 4px 18px rgba(0,0,0,.55), 0 0 8px rgba(0,120,255,0.12) !important; | |
| text-align:left !important; | |
| display:block !important; | |
| } | |
| /* PΓ‘rrafos dentro de burbuja usuario */ | |
| .chatbot-container .message.user p, | |
| .chatbot-container [data-testid="user"] p { | |
| margin:0 0 6px 0 !important; | |
| white-space:normal !important; | |
| word-break:break-word !important; | |
| overflow-wrap:break-word !important; | |
| color:#ffffff !important; | |
| } | |
| .chatbot-container .message.user p:last-child, | |
| .chatbot-container [data-testid="user"] p:last-child { margin-bottom:0 !important; } | |
| /* Todos los hijos del mensaje de usuario */ | |
| .chatbot-container .message.user *, | |
| .chatbot-container [data-testid="user"] * { | |
| color:#ffffff !important; | |
| overflow-wrap:break-word !important; | |
| word-break:break-word !important; | |
| white-space:normal !important; | |
| max-width:100% !important; | |
| } | |
| /* ββ BURBUJA ASISTENTE (izquierda) ββ | |
| Modificar background/border-left para cambiar color del bot */ | |
| .chatbot-container .message.bot, | |
| .chatbot-container [data-testid="bot"] { | |
| align-self:flex-start !important; | |
| margin-right:auto !important; | |
| margin-left:6px !important; | |
| width:fit-content !important; | |
| min-width:80px !important; | |
| max-width:82% !important; | |
| background:linear-gradient(145deg,rgba(10,25,45,0.92),rgba(5,15,30,0.96)) !important; | |
| border:1px solid rgba(0,212,255,0.15) !important; | |
| border-radius:18px 18px 18px 4px !important; | |
| border-left:3px solid #00d4ff !important; /* β lΓnea izquierda cian */ | |
| color:#c8e8f8 !important; | |
| box-shadow:0 4px 18px rgba(0,0,0,.6) !important; | |
| display:block !important; | |
| } | |
| /* PΓ‘rrafos dentro de burbuja bot */ | |
| .chatbot-container .message.bot p, | |
| .chatbot-container [data-testid="bot"] p { | |
| margin-bottom:8px !important; | |
| white-space:pre-wrap !important; | |
| color:#c8e8f8 !important; | |
| } | |
| .chatbot-container .message.bot p:last-child, | |
| .chatbot-container [data-testid="bot"] p:last-child { margin-bottom:0 !important; } | |
| /* ββ BLOQUES DE CΓDIGO dentro del chatbot ββ */ | |
| .chatbot-container pre { | |
| background:rgba(0,5,15,0.85) !important; | |
| border:1px solid rgba(0,212,255,0.3) !important; | |
| border-radius:10px !important; | |
| padding:12px !important; margin:8px 0 !important; | |
| overflow-x:auto !important; white-space:pre !important; | |
| } | |
| .chatbot-container code { | |
| font-family:'Share Tech Mono',monospace !important; | |
| color:#00ffcc !important; | |
| background:rgba(0,255,200,0.1) !important; | |
| padding:2px 5px !important; border-radius:4px !important; | |
| white-space:pre-wrap !important; | |
| } | |
| /* ββ SCROLLBAR del chatbot ββ */ | |
| .chatbot-container > div::-webkit-scrollbar { width:5px !important; } | |
| .chatbot-container > div::-webkit-scrollbar-thumb { | |
| background:rgba(0,212,255,0.2) !important; border-radius:10px !important; | |
| } | |
| /* ββ AVATAR DEL ASISTENTE β Esfera con logo CVision ββ | |
| Modificar width/height para cambiar tamaΓ±o del avatar (actualmente 42x42) */ | |
| .chatbot-container .avatar-container { | |
| width:48px !important; height:48px !important; | |
| border-radius:50% !important; | |
| border:2px solid rgba(0,212,255,0.6) !important; | |
| box-shadow:0 0 10px rgba(0,212,255,0.35) !important; | |
| overflow:hidden !important; | |
| background:rgba(2,10,20,0.9) !important; | |
| display:flex !important; align-items:center !important; | |
| justify-content:center !important; flex-shrink:0 !important; | |
| } | |
| /* TamaΓ±o del logo dentro del avatar β scale(1.18) para zoom */ | |
| .chatbot-container .avatar-container img { | |
| width:100% !important; height:100% !important; | |
| object-fit:cover !important; | |
| transform:scale(1.18) !important; /* β aumentar para mΓ‘s zoom */ | |
| } | |
| /* AnimaciΓ³n de pulso en el avatar del bot */ | |
| .chatbot-container [data-testid="bot"] .avatar-container { | |
| animation:avatar-pulse 3s infinite ease-in-out; | |
| } | |
| /* ββ TEXTBOX β Campo de entrada de texto ββ | |
| Modificar border-radius/font-size para cambiar estilo del input */ | |
| .gradio-textbox input, .gradio-textbox textarea { | |
| background:rgba(0,8,22,.92) !important; | |
| border:1px solid rgba(0,180,255,.22) !important; | |
| border-radius:22px !important; | |
| color:#e0f4ff !important; | |
| font-family:'Exo 2',sans-serif !important; | |
| font-size:13.5px !important; | |
| padding:9px 18px !important; | |
| transition:all .3s !important; | |
| } | |
| .gradio-textbox input:focus, .gradio-textbox textarea:focus { | |
| border-color:var(--cyan) !important; | |
| box-shadow:0 0 0 1px rgba(0,212,255,.15), 0 0 20px rgba(0,212,255,.12) !important; | |
| outline:none !important; | |
| } | |
| .gradio-textbox input::placeholder, .gradio-textbox textarea::placeholder { color:rgba(0,140,190,.35) !important; } | |
| /* ββ BOTΓN ENVIAR β BotΓ³n principal de envΓo de mensaje ββ | |
| Modificar background gradient para cambiar color del botΓ³n */ | |
| .btn-send-cv button, .btn-send-cv { | |
| background:linear-gradient(135deg, #001a3a 0%, #003670 40%, #0068b8 75%, #00aadd 100%) !important; | |
| border:1px solid rgba(0,212,255,0.5) !important; | |
| color:#e8f8ff !important; | |
| border-radius:20px !important; | |
| font-weight:700 !important; font-size:0.72rem !important; | |
| font-family:'Orbitron',monospace !important; letter-spacing:.08em !important; | |
| padding:8px 18px !important; height:38px !important; | |
| min-width:110px !important; max-width:160px !important; | |
| box-shadow:0 0 14px rgba(0,168,232,.35), 0 0 1px rgba(0,212,255,.6) inset !important; | |
| transition:all 0.2s ease !important; | |
| animation:btnGlow 3s ease-in-out infinite !important; | |
| position:relative !important; overflow:hidden !important; | |
| white-space:nowrap !important; text-overflow:ellipsis !important; | |
| } | |
| .btn-send-cv button::after, .btn-send-cv::after { | |
| content:''; position:absolute; top:0; left:-100%; width:100%; height:100%; | |
| background:linear-gradient(90deg,transparent,rgba(255,255,255,.12),transparent); | |
| transition:.4s; | |
| } | |
| .btn-send-cv button:hover, .btn-send-cv:hover { | |
| box-shadow:0 0 28px rgba(0,212,255,.65), 0 0 55px rgba(0,212,255,.2) !important; | |
| transform:translateY(-1px) !important; | |
| border-color:rgba(0,255,220,.7) !important; | |
| } | |
| .btn-send-cv button:hover::after, .btn-send-cv:hover::after { left:100%; } | |
| /* ββ BOTΓN VOZ β BotΓ³n de activar respuesta por voz del asistente ββ */ | |
| .btn-voice-cv button, .btn-voice-cv { | |
| background:rgba(0,20,50,.6) !important; | |
| border:1px solid rgba(0,212,255,.35) !important; | |
| color:#00d4ff !important; | |
| border-radius:20px !important; font-size:0.72rem !important; | |
| font-family:'Share Tech Mono',monospace !important; letter-spacing:.1em !important; | |
| padding:8px 14px !important; height:38px !important; | |
| min-width:70px !important; max-width:110px !important; | |
| transition:all 0.2s !important; white-space:nowrap !important; | |
| } | |
| .btn-voice-cv button:hover, .btn-voice-cv:hover { | |
| background:rgba(0,80,160,.3) !important; | |
| box-shadow:0 0 16px rgba(0,212,255,.4) !important; | |
| border-color:rgba(0,212,255,.7) !important; | |
| } | |
| /* ββ BOTΓN LIMPIAR β BotΓ³n para limpiar conversaciΓ³n ββ */ | |
| .btn-clear-cv button, .btn-clear-cv { | |
| background:rgba(30,0,0,.5) !important; | |
| border:1px solid rgba(255,50,80,.25) !important; | |
| color:rgba(255,100,120,.7) !important; | |
| border-radius:20px !important; font-size:0.68rem !important; | |
| font-family:'Share Tech Mono',monospace !important; letter-spacing:.08em !important; | |
| padding:8px 14px !important; height:38px !important; | |
| min-width:90px !important; max-width:160px !important; | |
| transition:all 0.2s !important; white-space:nowrap !important; | |
| } | |
| .btn-clear-cv button:hover, .btn-clear-cv:hover { | |
| background:rgba(60,0,0,.6) !important; | |
| border-color:rgba(255,80,80,.5) !important; | |
| box-shadow:0 0 12px rgba(255,50,50,.25) !important; | |
| } | |
| /* ββ BOTΓN EJECUTAR (PDF) β BotΓ³n de envΓo en el mΓ³dulo PDF ββ */ | |
| .btn-exec-cv button, .btn-exec-cv { | |
| background:linear-gradient(135deg,#001a3a,#00306a,#0055a0) !important; | |
| border:1px solid rgba(0,200,255,.45) !important; color:#e0f8ff !important; | |
| border-radius:20px !important; font-family:'Orbitron',monospace !important; | |
| font-size:0.68rem !important; font-weight:700 !important; letter-spacing:.08em !important; | |
| padding:8px 18px !important; height:38px !important; | |
| box-shadow:0 0 12px rgba(0,150,255,.3) !important; | |
| transition:all .2s !important; white-space:nowrap !important; | |
| } | |
| .btn-exec-cv button:hover, .btn-exec-cv:hover { | |
| box-shadow:0 0 24px rgba(0,212,255,.55) !important; transform:translateY(-1px) !important; | |
| } | |
| /* ββ MIC / AUDIO COMPONENT β Componente de grabaciΓ³n de voz (vozβtexto) ββ | |
| Modificar border/background para cambiar apariencia del micrΓ³fono */ | |
| .mic-tool-cv { | |
| background:rgba(0,6,18,.9) !important; | |
| border:1px solid rgba(0,180,255,.18) !important; | |
| border-radius:12px !important; padding:4px 8px !important; | |
| } | |
| .mic-tool-cv label { | |
| font-family:'Share Tech Mono',monospace !important; | |
| font-size:0.65rem !important; color:rgba(0,180,255,.5) !important; letter-spacing:.12em !important; | |
| } | |
| .mic-tool-cv button { | |
| background:rgba(0,50,120,.4) !important; | |
| border:1px solid rgba(0,212,255,.3) !important; | |
| border-radius:8px !important; color:#00d4ff !important; | |
| font-size:0.72rem !important; padding:4px 10px !important; transition:.2s !important; | |
| } | |
| .mic-tool-cv button:hover { | |
| background:rgba(0,80,180,.5) !important; | |
| box-shadow:0 0 10px rgba(0,212,255,.3) !important; | |
| } | |
| /* ββ FILAS DE INPUT β Espaciado de las filas de controles ββ */ | |
| .input-row-cv { gap:8px !important; align-items:center !important; } | |
| .mic-row-cv { gap:8px !important; align-items:center !important; margin-top:4px !important; } | |
| /* ββ FOOTER DE GRADIO β Ocultar footer por defecto ββ */ | |
| footer { display:none !important; } | |
| """ | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # HEADER HTML β Barra superior: logo CV, tΓtulo, imagen logo, badges | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| HEADER_HTML = """ | |
| <div id="cvision-header"> | |
| <!-- Badge circular CV (izquierda) --> | |
| <div style="width:56px;height:56px;border-radius:50%; | |
| border:1.5px solid rgba(0,212,255,0.5); | |
| background:radial-gradient(circle,rgba(0,212,255,0.18) 0%,#020a18 100%); | |
| display:flex;align-items:center;justify-content:center; | |
| font-family:'Orbitron',monospace;font-size:0.55rem;font-weight:700; | |
| color:#00d4ff;letter-spacing:0.04em; | |
| box-shadow:0 0 22px rgba(0,212,255,0.45),0 0 50px rgba(0,212,255,0.12); | |
| position:relative;overflow:hidden;flex-shrink:0;"> | |
| <span style="position:relative;z-index:1;">CV</span> | |
| <div style="position:absolute;inset:-50%;width:200%;height:200%; | |
| background:conic-gradient(from 0deg,transparent 55%,rgba(0,212,255,0.28) 78%,transparent 100%); | |
| animation:cvSpin 3.5s linear infinite;"></div> | |
| </div> | |
| <!-- TΓtulo principal + subtΓtulo --> | |
| <div style="flex:1;display:flex;flex-direction:column;gap:3px;"> | |
| <div style="font-family:'Orbitron',monospace;font-size:2.8rem;font-weight:900; | |
| color:#fff;letter-spacing:0.08em;line-height:1; | |
| text-shadow:0 0 22px rgba(0,212,255,0.65),0 0 55px rgba(0,212,255,0.2);"> | |
| C<span style="color:#00d4ff;">Vision</span> Intelligence | |
| </div> | |
| <div style="font-family:'Share Tech Mono',monospace;font-size:0.75rem; | |
| letter-spacing:0.22em;color:rgba(74,122,150,.75);"> | |
| ASISTENTE TÉCNICO MULTIMODAL · CRISTIAN VELASCO | |
| </div> | |
| </div> | |
| <!-- Logo CVision animado (centro-derecha) --> | |
| <div style="display:flex;align-items:center;justify-content:center;margin:0 24px;position:relative;"> | |
| <div style="position:absolute;width:103px;height:103px;border-radius:50%; | |
| border:1px solid rgba(0,212,255,0.35); | |
| box-shadow:0 0 14px rgba(0,212,255,0.25),inset 0 0 14px rgba(0,212,255,0.1); | |
| animation:cvFloat 3s ease-in-out infinite;"></div> | |
| <div style="position:absolute;width:130px;height:130px;border-radius:50%; | |
| border:0.5px dashed rgba(0,212,255,0.15); | |
| animation:cvOrbit 12s linear infinite;"></div> | |
| <img src="https://huggingface.co/spaces/CVision-assitant/asistente-virtual-CVision/resolve/main/assets/CVision_logo-trprnte.png" | |
| onerror="this.style.display='none'" | |
| style="width:130px;height:130px;object-fit:contain;border-radius:50%; | |
| filter:drop-shadow(0 0 10px rgba(0,212,255,0.7)) drop-shadow(0 0 24px rgba(0,180,255,0.4)); | |
| animation:cvFloat 3s ease-in-out infinite;position:relative;z-index:1;"> | |
| </div> | |
| <!-- Badges de estado (derecha) --> | |
| <div style="display:flex;gap:7px;align-items:center;flex-shrink:0;"> | |
| <div style="width:7px;height:7px;border-radius:50%;background:#00ff96; | |
| box-shadow:0 0 8px #00ff96;animation:cvBlink 2s ease-in-out infinite;"></div> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.82rem; | |
| letter-spacing:0.14em;padding:4px 11px;border-radius:20px; | |
| border:1px solid rgba(0,255,150,0.3);background:rgba(0,255,150,0.07); | |
| color:#00ff96;">ONLINE</span> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.82rem; | |
| letter-spacing:0.14em;padding:4px 11px;border-radius:20px; | |
| border:1px solid rgba(0,212,255,0.28);background:rgba(0,212,255,0.07); | |
| color:#00d4ff;">v3.1.0</span> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.82rem; | |
| letter-spacing:0.14em;padding:4px 11px;border-radius:20px; | |
| border:1px solid rgba(0,212,255,0.28);background:rgba(0,212,255,0.07); | |
| color:#00d4ff;">NEURAL · ACTIVE</span> | |
| </div> | |
| </div> | |
| """ | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # NEBULA SECTION HTML β Hero con nebula, info y panel derecho | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| NEBULA_SECTION_HTML = """ | |
| <div id="cvision-nebula-section"> | |
| <!-- Canvas de cuadrΓcula de fondo --> | |
| <canvas id="cv-grid-canvas"></canvas> | |
| <!-- Canvas de la esfera nebula animada --> | |
| <div id="cv-nebula-wrap"> | |
| <canvas id="nebulaCanvas"></canvas> | |
| </div> | |
| <!-- Bloque de informaciΓ³n hero (tΓtulo + descripciΓ³n + chips) --> | |
| <div id="cv-hero-info"> | |
| <div style="font-family:'Share Tech Mono',monospace;font-size:0.6rem; | |
| letter-spacing:0.3em;color:rgba(0,180,220,.5);text-transform:uppercase; | |
| display:flex;align-items:center;gap:8px;"> | |
| <span style="display:inline-block;width:6px;height:6px;border-radius:50%; | |
| background:#00d4ff;box-shadow:0 0 8px #00d4ff;"></span> | |
| SISTEMA ACTIVADO · CORE OPERATIVO | |
| </div> | |
| <!-- TΓtulo grande del hero --> | |
| <div style="font-family:'Orbitron',monospace;font-size:2.15rem; | |
| font-weight:900;line-height:1.12;color:#dff0ff; | |
| text-shadow:0 0 30px rgba(0,212,255,0.55),0 0 70px rgba(0,180,255,0.2);"> | |
| Asistente Virtual<br> | |
| <span style="color:transparent; | |
| background:linear-gradient(135deg,#00eeff 0%,#ffffff 45%,#00aaff 100%); | |
| -webkit-background-clip:text;background-clip:text; | |
| filter:drop-shadow(0 0 10px rgba(0,220,255,0.7));">CVision</span> | |
| </div> | |
| <!-- DescripciΓ³n β clase cv-desc (ver CSS #cv-hero-info .cv-desc) --> | |
| <p class="cv-desc"> | |
| Interfaz de <strong style="color:rgba(0,220,255,.95);font-weight:600;">inteligencia artificial</strong> multimodal. | |
| Combina procesamiento de lenguaje natural, análisis de documentos | |
| y búsqueda web en un solo núcleo cognitivo. | |
| </p> | |
| <!-- Chips de capacidades --> | |
| <div style="display:flex;flex-wrap:wrap;gap:7px;margin-top:4px;"> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;letter-spacing:0.1em;padding:5px 13px;border-radius:20px;border:1px solid rgba(0,212,255,0.22);background:rgba(0,80,160,0.08);color:rgba(0,200,255,.5);">PROCESAMIENTO NLP</span> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;letter-spacing:0.1em;padding:5px 13px;border-radius:20px;border:1px solid rgba(0,212,255,0.22);background:rgba(0,80,160,0.08);color:rgba(0,200,255,.5);">VISIÓN COMPUTACIONAL</span> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;letter-spacing:0.1em;padding:5px 13px;border-radius:20px;border:1px solid rgba(0,212,255,0.22);background:rgba(0,80,160,0.08);color:rgba(0,200,255,.5);">BÚSQUEDA WEB</span> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;letter-spacing:0.1em;padding:5px 13px;border-radius:20px;border:1px solid rgba(0,212,255,0.22);background:rgba(0,80,160,0.08);color:rgba(0,200,255,.5);">ANÁLISIS PDF</span> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;letter-spacing:0.1em;padding:5px 13px;border-radius:20px;border:1px solid rgba(0,212,255,0.22);background:rgba(0,80,160,0.08);color:rgba(0,200,255,.5);">VOZ · WHISPER</span> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;letter-spacing:0.1em;padding:5px 13px;border-radius:20px;border:1px solid rgba(0,212,255,0.22);background:rgba(0,80,160,0.08);color:rgba(0,200,255,.5);">RAZONAMIENTO IA</span> | |
| </div> | |
| </div> | |
| <!-- Panel derecho: NΓΊcleo Neural + Conectividad --> | |
| <div id="cv-right-panel"> | |
| <!-- Tarjeta NΓΊcleo Neural (canvas del cerebro) --> | |
| <div class="rcard"> | |
| <div class="rh" style="padding:10px 12px 8px;"> | |
| <span style="color:#00d4ff;font-size:14px;margin-right:6px;">✦</span> | |
| <div style="display:flex;flex-direction:column;"> | |
| <span class="rh-t" style="font-size:8.5px;font-weight:900;letter-spacing:.18em;color:rgba(160,210,255,.95);">NÚCLEO NEURAL</span> | |
| <span style="font-family:'Share Tech Mono',monospace;font-size:6.5px;color:rgba(0,150,200,.4);letter-spacing:.18em;margin-top:2px;">ACTIVIDAD EN TIEMPO REAL</span> | |
| </div> | |
| </div> | |
| <div class="brain-box"> | |
| <div class="bglow"></div> | |
| <div class="rring rring1"></div> | |
| <div class="rring rring2"></div> | |
| <canvas id="brainCvs" width="198" height="168"></canvas> | |
| </div> | |
| </div> | |
| <!-- Tarjeta Conectividad (canvas mapa mundial) --> | |
| <div class="rcard"> | |
| <div class="rh"> | |
| <div class="rh-dot" style="background:#00ff88;box-shadow:0 0 8px #00ff88;"></div> | |
| <span class="rh-t">CONECTIVIDAD</span> | |
| <span class="rh-s" style="color:#00ff88;">● ONLINE</span> | |
| </div> | |
| <div class="conn-body"> | |
| <div class="cstat"><div class="cdot"></div><span class="cst">ONLINE</span></div> | |
| <div class="clat">Latencia: <span style="color:#00d4ff;">18ms</span> · Pérdida: <span style="color:#00ffcc;">0.0%</span></div> | |
| <canvas id="worldC" width="190" height="52"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # STATUS BAR + JAVASCRIPT β Barra de estado y animaciones canvas | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| STATUS_AND_JS_HTML = """ | |
| <!-- ββ STATUS BAR HTML ββ --> | |
| <div id="cvision-statusbar"> | |
| <span>CORE ACTIVE</span><span class="cv-sdot"></span> | |
| <span>ALL SYSTEMS NOMINAL</span><span class="cv-sdot" style="animation-delay:.6s"></span> | |
| <span id="cv-clock">00:00:00</span><span class="cv-sdot" style="animation-delay:1.2s"></span> | |
| <span>SCANNING</span> | |
| </div> | |
| <script> | |
| 'use strict'; | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // RELOJ β Actualiza el reloj de la status bar cada segundo | |
| // Para cambiar formato: modificar el string de textContent | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| (function clockTick(){ | |
| var el = document.getElementById('cv-clock'); | |
| if(el){ | |
| var n = new Date(); | |
| var p = function(x){ return String(x).padStart(2,'0'); }; | |
| el.textContent = p(n.getHours()) + ':' + p(n.getMinutes()) + ':' + p(n.getSeconds()); | |
| } | |
| setTimeout(clockTick, 1000); | |
| })(); | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // GRID CANVAS β CuadrΓcula + puntos flotantes de fondo | |
| // OPTIMIZACIΓN: DOT_COUNT reducido (30β18), GRID mΓ‘s amplia | |
| // Para mΓ‘s/menos puntos: cambiar DOT_COUNT | |
| // Para mΓ‘s/menos lΓneas: cambiar GRID (mayor = menos lΓneas) | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| (function initGrid(){ | |
| function setup(){ | |
| var section = document.getElementById('cvision-nebula-section'); | |
| var gc = document.getElementById('cv-grid-canvas'); | |
| if(!section || !gc){ setTimeout(setup, 200); return; } | |
| var ctx = gc.getContext('2d'); | |
| var W, H, dots = []; | |
| var GRID = 52; /* β Mayor = cuadrΓcula mΓ‘s espaciada (menos lΓneas) */ | |
| var DOT_COUNT = 18; /* β Reducir para mejorar rendimiento */ | |
| function resize(){ | |
| W = gc.width = section.offsetWidth; | |
| H = gc.height = section.offsetHeight; | |
| buildDots(); | |
| } | |
| function buildDots(){ | |
| dots = []; | |
| for(var i = 0; i < DOT_COUNT; i++){ | |
| dots.push({ x:Math.random()*W, y:Math.random()*H, | |
| vx:(Math.random()-.5)*.35, vy:(Math.random()-.5)*.35, | |
| r:.8+Math.random()*1.4, a:.10+Math.random()*.28 }); | |
| } | |
| } | |
| function drawGrid(){ | |
| ctx.strokeStyle='rgba(0,200,255,0.035)'; ctx.lineWidth=.5; | |
| for(var x=0;x<W;x+=GRID){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke();} | |
| for(var y=0;y<H;y+=GRID){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke();} | |
| ctx.fillStyle='rgba(0,200,255,0.08)'; | |
| for(var x2=0;x2<W;x2+=GRID) for(var y2=0;y2<H;y2+=GRID){ctx.beginPath();ctx.arc(x2,y2,1,0,Math.PI*2);ctx.fill();} | |
| } | |
| function drawDots(){ | |
| for(var i=0;i<dots.length;i++){ | |
| var d=dots[i]; d.x+=d.vx; d.y+=d.vy; | |
| if(d.x<0)d.x=W; if(d.x>W)d.x=0; if(d.y<0)d.y=H; if(d.y>H)d.y=0; | |
| ctx.globalAlpha=d.a; ctx.fillStyle='#00d4ff'; ctx.beginPath(); ctx.arc(d.x,d.y,d.r,0,Math.PI*2); ctx.fill(); | |
| } | |
| ctx.globalAlpha=1; | |
| } | |
| function drawVig(){ | |
| var g=ctx.createRadialGradient(W*.5,H*.5,0,W*.5,H*.5,Math.max(W,H)*.65); | |
| g.addColorStop(0,'rgba(0,8,22,0)'); g.addColorStop(1,'rgba(0,4,12,0.65)'); | |
| ctx.fillStyle=g; ctx.fillRect(0,0,W,H); | |
| } | |
| /* OPTIMIZACIΓN: throttle a ~40fps (25ms) en vez de requestAnimationFrame puro */ | |
| var lastGrid = 0; | |
| function loop(ts){ | |
| requestAnimationFrame(loop); | |
| if(ts - lastGrid < 25) return; | |
| lastGrid = ts; | |
| ctx.clearRect(0,0,W,H); drawGrid(); drawDots(); drawVig(); | |
| } | |
| window.addEventListener('resize',resize,{passive:true}); resize(); | |
| requestAnimationFrame(loop); | |
| } | |
| setup(); | |
| })(); | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // NEBULA β Esfera 3D con partΓculas, Γ³rbitas y texto CVision | |
| // OPTIMIZACIΓN: partΓculas reducidas (280β180), rings (16β10), | |
| // clouds (7β5), orbits (5β4), meridianos (10β7) | |
| // t += .018 (era .022), throttle a ~50fps | |
| // Para mΓ‘s calidad: aumentar N, rings, clouds | |
| // Para mΓ‘s rendimiento: reducirlos | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| (function(){ | |
| var cv=document.getElementById('nebulaCanvas'); if(!cv) return; | |
| var ctx=cv.getContext('2d'); | |
| var W=420,H=420,cx=210,cy=210,R=165; cv.width=W; cv.height=H; | |
| var t=0; | |
| /* PartΓculas 3D β N=180 (era 280) */ | |
| var N=180, parts=[]; | |
| for(var i=0;i<N;i++){ | |
| var theta=Math.random()*Math.PI*2, phi=Math.acos(2*Math.random()-1), shell=Math.random(); | |
| var orbR=R*(shell<.15?.25+Math.random()*.20:shell<.50?.45+Math.random()*.35:.65+Math.random()*.35); | |
| parts.push({theta:theta,phi:phi,r:orbR, | |
| dT:(Math.random()-.5)*.025,dP:(Math.random()-.5)*.016, | |
| sz:2.0+Math.random()*4.0,hue:160+Math.random()*120, | |
| bright:.5+Math.random()*.5,tw:Math.random()*Math.PI*2,twS:.05+Math.random()*.09}); | |
| } | |
| /* Rings decorativos β 10 (era 16) */ | |
| var rings=[]; | |
| for(var i=0;i<10;i++) rings.push({tiltX:Math.random()*Math.PI,spin:Math.random()*Math.PI*2, | |
| spinS:(Math.random()-.5)*.026,r:35+i*13,op:.04+Math.random()*.18, | |
| dOn:1.5+Math.random()*5,dOff:2+Math.random()*7, | |
| color:Math.random()>.5?[0,200,255]:Math.random()>.5?[0,255,200]:[180,100,255]}); | |
| /* Nubes de nebulosa β 5 (era 7) */ | |
| var clouds=[]; | |
| for(var i=0;i<5;i++) clouds.push({angle:Math.random()*Math.PI*2,dist:.22+Math.random()*.28, | |
| speed:(Math.random()-.5)*.014,size:.32+Math.random()*.20,hue:175+i*36}); | |
| /* Γrbitas de Γ‘tomo β 4 (era 5) */ | |
| var orbits=[]; | |
| for(var i=0;i<4;i++) orbits.push({tilt:i*(Math.PI/4),spin:Math.random()*Math.PI*2, | |
| spinSpeed:(Math.random()>.5?1:-1)*(.012+Math.random()*.018), | |
| r:R*.55+i*R*.07,opacity:.18+Math.random()*.16}); | |
| var lastNeb=0; | |
| function draw(ts){ | |
| requestAnimationFrame(draw); | |
| /* OPTIMIZACIΓN: throttle a ~50fps */ | |
| if(ts - lastNeb < 20) return; | |
| lastNeb = ts; | |
| ctx.clearRect(0,0,W,H); t+=.018; | |
| var atm=ctx.createRadialGradient(cx,cy,R*.5,cx,cy,R*1.55); | |
| atm.addColorStop(0,'rgba(0,100,255,.09)');atm.addColorStop(.5,'rgba(0,60,180,.04)');atm.addColorStop(1,'rgba(0,0,0,0)'); | |
| ctx.fillStyle=atm; ctx.fillRect(0,0,W,H); | |
| var sg=ctx.createRadialGradient(cx-R*.28,cy-R*.28,0,cx,cy,R); | |
| sg.addColorStop(0,'rgba(80,180,255,.22)');sg.addColorStop(.25,'rgba(0,100,220,.14)');sg.addColorStop(.65,'rgba(0,50,140,.08)');sg.addColorStop(1,'rgba(0,15,50,.02)'); | |
| ctx.fillStyle=sg; ctx.beginPath(); ctx.arc(cx,cy,R,0,Math.PI*2); ctx.fill(); | |
| var eg=ctx.createRadialGradient(cx,cy,R*.78,cx,cy,R); | |
| eg.addColorStop(0,'rgba(0,0,0,0)');eg.addColorStop(.55,'rgba(0,180,255,.12)');eg.addColorStop(1,'rgba(0,100,255,.26)'); | |
| ctx.fillStyle=eg; ctx.beginPath(); ctx.arc(cx,cy,R,0,Math.PI*2); ctx.fill(); | |
| ctx.beginPath(); ctx.arc(cx,cy,R,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(0,210,255,'+(.35+Math.sin(t*.3)*.12)+')'; ctx.lineWidth=2; ctx.stroke(); | |
| /* Γrbitas de Γ‘tomo */ | |
| for(var oi=0;oi<orbits.length;oi++){ | |
| var orb=orbits[oi]; orb.spin+=orb.spinSpeed; | |
| ctx.save(); ctx.translate(cx,cy); ctx.rotate(orb.tilt); ctx.scale(1,.40); ctx.rotate(orb.spin); | |
| ctx.beginPath(); ctx.arc(0,0,orb.r,0,Math.PI*2); | |
| var pulse=.65+Math.abs(Math.sin(t*.65+orb.tilt))*.5; | |
| ctx.strokeStyle='rgba(0,215,255,'+(orb.opacity*pulse)+')'; ctx.lineWidth=1.3; ctx.stroke(); | |
| var da=orb.spin*2.8,dx=Math.cos(da)*orb.r,dy=Math.sin(da)*orb.r*.40; | |
| ctx.fillStyle='rgba(140,238,255,'+(orb.opacity*pulse*2.1)+')'; | |
| ctx.beginPath(); ctx.arc(dx,dy,2.6,0,Math.PI*2); ctx.fill(); ctx.restore(); | |
| } | |
| /* Nubes */ | |
| for(var ci=0;ci<clouds.length;ci++){ | |
| var cl=clouds[ci]; cl.angle+=cl.speed; | |
| var nx=cx+Math.cos(cl.angle)*R*cl.dist, ny=cy+Math.sin(cl.angle)*R*cl.dist*.50; | |
| var cg=ctx.createRadialGradient(nx,ny,0,nx,ny,R*cl.size); | |
| var h=cl.hue+Math.sin(t*.22)*28; | |
| cg.addColorStop(0,'hsla('+h+',100%,58%,.10)'); cg.addColorStop(1,'rgba(0,0,0,0)'); | |
| ctx.fillStyle=cg; ctx.fillRect(cx-R,cy-R,R*2,R*2); | |
| } | |
| /* Rings */ | |
| for(var ri=0;ri<rings.length;ri++){ | |
| var rg=rings[ri]; rg.spin+=rg.spinS; | |
| ctx.save(); ctx.translate(cx,cy); ctx.rotate(rg.spin); | |
| var tiltY=Math.sin(rg.tiltX+t*.16)*.52+.12; ctx.scale(1,Math.abs(tiltY)); | |
| ctx.beginPath(); ctx.arc(0,0,rg.r,0,Math.PI*2); | |
| ctx.setLineDash([rg.dOn,rg.dOff]); | |
| var pls=.55+Math.abs(Math.sin(t*.58+rg.r*.025))*.55, rc=rg.color; | |
| ctx.strokeStyle='rgba('+rc[0]+','+rc[1]+','+rc[2]+','+(rg.op*pls)+')'; | |
| ctx.lineWidth=1; ctx.stroke(); ctx.setLineDash([]); ctx.restore(); | |
| } | |
| /* Wireframe β 7 meridianos (era 10) */ | |
| for(var li=1;li<6;li++){ | |
| var yl=cy-R+li*(R*2/6),rL=Math.sqrt(Math.max(0,R*R-(yl-cy)*(yl-cy))); | |
| if(rL>3){ctx.beginPath();ctx.ellipse(cx,yl,rL,rL*.32,0,0,Math.PI*2);ctx.strokeStyle='rgba(0,185,255,.05)';ctx.lineWidth=.5;ctx.stroke();} | |
| } | |
| for(var mi=0;mi<7;mi++){ | |
| var a2=(mi/7)*Math.PI*2+t*.022, ex=cx+R*Math.cos(a2)*Math.abs(Math.cos(t*.07+mi*.35)); | |
| ctx.beginPath(); ctx.moveTo(cx,cy-R); ctx.bezierCurveTo(ex,cy-R*.55,ex,cy+R*.55,cx,cy+R); | |
| ctx.strokeStyle='rgba(0,200,255,'+(.02+Math.abs(Math.cos(a2+t*.12))*.038)+')'; ctx.lineWidth=.5; ctx.stroke(); | |
| } | |
| /* PartΓculas (ordenadas por profundidad) */ | |
| var visible=[]; | |
| for(var pi=0;pi<parts.length;pi++){ | |
| var p=parts[pi]; p.theta+=p.dT; p.phi+=p.dP; p.tw+=p.twS; | |
| var sP=Math.sin(p.phi),cP=Math.cos(p.phi),sT=Math.sin(p.theta),cT=Math.cos(p.theta); | |
| var px2=cx+p.r*sP*cT, py2=cy+p.r*sP*sT*.56, pz=p.r*cP, depth=(pz+R)/(2*R); | |
| visible.push({px:px2,py:py2,pz:pz,depth:depth,p:p}); | |
| } | |
| visible.sort(function(a,b){return a.pz-b.pz;}); | |
| for(var vi=0;vi<visible.length;vi++){ | |
| var v=visible[vi],dep=v.depth,vp=v.p; | |
| var tw=.5+Math.abs(Math.sin(vp.tw))*.55; | |
| var alpha=Math.max(.18,dep*.96*vp.bright*tw), sz=vp.sz*(.42+dep*.82); | |
| var h2=vp.hue+Math.sin(t*.3+vp.theta)*38; | |
| if(dep>.32){ctx.beginPath();ctx.arc(v.px,v.py,sz*2.6,0,Math.PI*2);ctx.fillStyle='hsla('+h2+',100%,68%,'+Math.min(alpha*.30,.36)+')';ctx.fill();} | |
| ctx.beginPath(); ctx.arc(v.px,v.py,sz,0,Math.PI*2); | |
| if(dep>.56){ctx.shadowBlur=sz*5;ctx.shadowColor='hsla('+h2+',100%,78%,1)';} | |
| ctx.fillStyle='hsla('+h2+',100%,84%,'+Math.min(alpha,.97)+')'; ctx.fill(); ctx.shadowBlur=0; | |
| if(dep>.72&&tw>.72){ctx.beginPath();ctx.arc(v.px,v.py,sz*.42,0,Math.PI*2);ctx.fillStyle='rgba(255,255,255,'+Math.min(alpha*.88,.93)+')';ctx.fill();} | |
| } | |
| /* Anillos de datos externos */ | |
| for(var ti=0;ti<2;ti++){ | |
| var rad=R+25+ti*18; | |
| ctx.save(); ctx.translate(cx,cy); ctx.rotate(Date.now()*.00028*(ti%2===0?1:-.68)); | |
| ctx.beginPath(); ctx.arc(0,0,rad,0,Math.PI*2); | |
| ctx.strokeStyle='rgba(0,155,255,'+(.08-ti*.022)+')'; ctx.lineWidth=.9; ctx.stroke(); | |
| var tks=20+ti*10; | |
| for(var tk=0;tk<tks;tk++){ | |
| if(tk%3!==0) continue; | |
| var a3=(tk/tks)*Math.PI*2, ll=(tk%8===0)?9:(tk%4===0)?5:2.5, glow=(tk%8===0); | |
| ctx.beginPath(); ctx.moveTo(Math.cos(a3)*rad,Math.sin(a3)*rad); ctx.lineTo(Math.cos(a3)*(rad+ll),Math.sin(a3)*(rad+ll)); | |
| ctx.strokeStyle=glow?'rgba(0,238,255,.68)':'rgba(0,195,255,.26)'; ctx.lineWidth=glow?1.5:.75; ctx.stroke(); | |
| } | |
| ctx.restore(); | |
| } | |
| /* Texto CVision central */ | |
| var lg=ctx.createRadialGradient(cx,cy,0,cx,cy,80); | |
| lg.addColorStop(0,'rgba(0,235,255,.38)');lg.addColorStop(.5,'rgba(0,115,255,.14)');lg.addColorStop(1,'rgba(0,0,0,0)'); | |
| ctx.fillStyle=lg; ctx.beginPath(); ctx.arc(cx,cy,80,0,Math.PI*2); ctx.fill(); | |
| ctx.save(); | |
| var flicker=.93+Math.sin(t*.8)*.07; ctx.globalAlpha=flicker; | |
| ctx.fillStyle='rgba(0,5,22,.52)'; ctx.beginPath(); ctx.ellipse(cx,cy,78,32,0,0,Math.PI*2); ctx.fill(); | |
| ctx.font="bold 40px 'Orbitron',monospace"; ctx.textAlign='center'; ctx.textBaseline='middle'; | |
| ctx.shadowBlur=40; ctx.shadowColor='rgba(0,248,255,1)'; | |
| var tg2=ctx.createLinearGradient(cx-68,0,cx+68,0); | |
| tg2.addColorStop(0,'#ccffff');tg2.addColorStop(.35,'#ffffff');tg2.addColorStop(.65,'#00ffff');tg2.addColorStop(1,'#ccffff'); | |
| ctx.fillStyle=tg2; ctx.fillText('CVision',cx,cy-2); | |
| ctx.font="bold 12px 'Share Tech Mono',monospace"; ctx.globalAlpha=.72+Math.abs(Math.sin(t*.4))*.40; | |
| ctx.shadowBlur=14; ctx.shadowColor='rgba(0,235,255,.92)'; | |
| ctx.fillStyle='rgba(0,242,255,.97)'; ctx.fillText('NEURAL CORE',cx,cy+27); | |
| ctx.shadowBlur=0; ctx.restore(); | |
| } | |
| requestAnimationFrame(draw); | |
| })(); | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // BRAIN CANVAS β Cerebro neural animado (panel derecho) | |
| // OPTIMIZACIΓN: nodos reducidos (60β40), distancia conexiΓ³n | |
| // reducida (30β24), throttle a ~40fps | |
| // Para mΓ‘s detalle: aumentar el 40 del while(brainNodes.length<40) | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| (function initBrain(){ | |
| var cv=document.getElementById('brainCvs'); if(!cv) return; | |
| var ctx=cv.getContext('2d'); | |
| var W=198,H=168; cv.width=W; cv.height=H; | |
| var t=0, brainNodes=[]; | |
| function inBrain(nx,ny){ | |
| var bcx=W/2,bcy=H*.42,dx=(nx-bcx)/(W*.44),dy=(ny-bcy)/(H*.36); | |
| if(dx*dx+dy*dy<1) return true; | |
| var ccx=bcx+W*.1,ccy=bcy+H*.3,ddx=(nx-ccx)/(W*.2),ddy=(ny-ccy)/(H*.13); | |
| if(ddx*ddx+ddy*ddy<1) return true; | |
| return false; | |
| } | |
| /* OPTIMIZACIΓN: 40 nodos (era 60) */ | |
| var attempts=0; | |
| while(brainNodes.length<40 && attempts<1500){ | |
| attempts++; | |
| var nx=W*.1+Math.random()*(W*.8), ny=H*.06+Math.random()*(H*.82); | |
| if(inBrain(nx,ny)){ | |
| var dep=Math.random(); | |
| brainNodes.push({x:nx,y:ny,ox:nx,oy:ny, | |
| wobbleX:(Math.random()-.5)*2.5,wobbleY:(Math.random()-.5)*1.8, | |
| wobbleSpeed:.006+Math.random()*.014,wobblePhase:Math.random()*Math.PI*2, | |
| r:1.4+Math.random()*2.4,depth:dep,fire:0, | |
| fireTimer:Math.floor(Math.random()*120), | |
| hue:195+Math.random()*20+(dep>.5?0:40),alpha:.4+Math.random()*.6}); | |
| } | |
| } | |
| /* OPTIMIZACIΓN: conexiΓ³n mΓ‘x a 24px (era 30) */ | |
| var edges=[]; | |
| for(var i=0;i<brainNodes.length;i++) for(var j=i+1;j<brainNodes.length;j++){ | |
| var dx2=brainNodes[i].x-brainNodes[j].x,dy2=brainNodes[i].y-brainNodes[j].y,d=Math.sqrt(dx2*dx2+dy2*dy2); | |
| if(d<24&&Math.random()>.4) edges.push({a:i,b:j,len:d}); | |
| } | |
| var pulses=[]; | |
| function BP(a,b){this.a=a;this.b=b;this.p=0;this.speed=.015+Math.random()*.022;this.done=false;} | |
| BP.prototype.tick=function(){ | |
| this.p+=this.speed; | |
| if(this.p>=1){this.done=true;brainNodes[this.b].fire=1;brainNodes[this.b].fireTimer=60+Math.random()*100;} | |
| }; | |
| BP.prototype.draw=function(){ | |
| var na=brainNodes[this.a],nb=brainNodes[this.b]; | |
| var x=na.x+(nb.x-na.x)*this.p,y=na.y+(nb.y-na.y)*this.p; | |
| var tx=na.x+(nb.x-na.x)*Math.max(0,this.p-.1),ty=na.y+(nb.y-na.y)*Math.max(0,this.p-.1); | |
| ctx.save(); ctx.globalAlpha=.9-this.p*.35; | |
| ctx.strokeStyle='rgba(120,210,255,.85)'; ctx.lineWidth=1.5; | |
| ctx.shadowBlur=7; ctx.shadowColor='rgba(80,190,255,.9)'; | |
| ctx.beginPath(); ctx.moveTo(tx,ty); ctx.lineTo(x,y); ctx.stroke(); | |
| ctx.fillStyle='rgba(255,255,255,.95)'; ctx.shadowBlur=12; ctx.shadowColor='rgba(180,230,255,1)'; | |
| ctx.beginPath(); ctx.arc(x,y,2.2,0,Math.PI*2); ctx.fill(); | |
| ctx.shadowBlur=0; ctx.restore(); | |
| }; | |
| function drawBase(){ | |
| var bx=W*.5,by=H*.88; | |
| for(var r=0;r<3;r++){ /* OPTIMIZACIΓN: 3 anillos (era 4) */ | |
| var rad=14+r*12,al=.22-r*.04,p=.4+Math.abs(Math.sin(t*.8+r*.6))*.4; | |
| ctx.save(); ctx.globalAlpha=al*p; | |
| ctx.strokeStyle='rgba(0,180,255,1)'; ctx.lineWidth=1.1-r*.18; | |
| ctx.shadowBlur=5; ctx.shadowColor='rgba(0,180,255,.7)'; | |
| ctx.beginPath(); ctx.ellipse(bx,by,rad,rad*.2,0,0,Math.PI*2); ctx.stroke(); | |
| ctx.shadowBlur=0; ctx.restore(); | |
| } | |
| } | |
| function drawMesh(){ | |
| var cx2=W/2,cy2=H*.42; | |
| var g=ctx.createRadialGradient(cx2,cy2,H*.1,cx2,cy2,H*.42); | |
| g.addColorStop(0,'rgba(0,80,200,.1)'); g.addColorStop(1,'rgba(0,0,0,0)'); | |
| ctx.fillStyle=g; ctx.fillRect(0,0,W,H); | |
| for(var ei=0;ei<edges.length;ei++){ | |
| var e=edges[ei],na=brainNodes[e.a],nb=brainNodes[e.b]; | |
| var fs=(na.fire+nb.fire)*.5,da=(na.depth+nb.depth)*.5,ba2=.04+da*.08+fs*.22; | |
| ctx.save(); ctx.globalAlpha=Math.min(ba2,.55); | |
| if(fs>.3){ctx.strokeStyle='rgba(170,225,255,'+(.35+fs*.5)+')';ctx.lineWidth=.7+fs*1.1;ctx.shadowBlur=3;ctx.shadowColor='rgba(100,200,255,.5)';} | |
| else{ctx.strokeStyle='hsla('+(200+da*30)+',100%,62%,1)';ctx.lineWidth=.45+da*.4;} | |
| ctx.beginPath();ctx.moveTo(na.x,na.y);ctx.lineTo(nb.x,nb.y);ctx.stroke(); | |
| ctx.shadowBlur=0; ctx.restore(); | |
| } | |
| } | |
| var lastBrain=0; | |
| function draw(ts){ | |
| requestAnimationFrame(draw); | |
| /* OPTIMIZACIΓN: throttle a ~40fps */ | |
| if(ts - lastBrain < 25) return; | |
| lastBrain = ts; | |
| ctx.clearRect(0,0,W,H); t+=.016; | |
| var bg=ctx.createRadialGradient(W*.5,H*.38,0,W*.5,H*.38,H*.5); | |
| bg.addColorStop(0,'rgba(0,55,180,'+(.04+Math.sin(t*.4)*.02)+')');bg.addColorStop(1,'rgba(0,0,0,0)'); | |
| ctx.fillStyle=bg; ctx.fillRect(0,0,W,H); | |
| drawBase(); drawMesh(); | |
| for(var ni=0;ni<brainNodes.length;ni++){ | |
| var n=brainNodes[ni]; | |
| n.wobblePhase+=n.wobbleSpeed; | |
| n.x=n.ox+Math.sin(n.wobblePhase)*n.wobbleX; | |
| n.y=n.oy+Math.cos(n.wobblePhase*.7)*n.wobbleY; | |
| n.fireTimer--; | |
| if(n.fireTimer<=0){ | |
| n.fire=1; n.fireTimer=90+Math.random()*200; | |
| var conn=[]; | |
| for(var ei2=0;ei2<edges.length;ei2++){if(edges[ei2].a===ni||edges[ei2].b===ni)conn.push(edges[ei2]);} | |
| if(conn.length&&Math.random()>.45){var pk=conn[Math.floor(Math.random()*conn.length)];pulses.push(new BP(pk.a,pk.b));} | |
| } | |
| if(n.fire>0) n.fire=Math.max(0,n.fire-.014); | |
| var sz2=n.r*(.6+n.depth*.45); | |
| ctx.save(); ctx.globalAlpha=n.alpha*(.3+n.depth*.38+n.fire*.48); | |
| if(n.fire>.1){ctx.beginPath();ctx.arc(n.x,n.y,sz2*3.2,0,Math.PI*2);ctx.fillStyle='hsla('+n.hue+',100%,68%,'+n.fire*.22+')';ctx.fill();ctx.shadowBlur=sz2*4.5;ctx.shadowColor='hsla('+n.hue+',100%,78%,1)';} | |
| ctx.fillStyle=n.fire>.5?'rgba(255,255,255,'+(.7+n.fire*.3)+')':'hsla('+n.hue+',100%,74%,1)'; | |
| ctx.beginPath();ctx.arc(n.x,n.y,sz2,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;ctx.restore(); | |
| } | |
| for(var pi2=pulses.length-1;pi2>=0;pi2--){pulses[pi2].tick();pulses[pi2].draw();if(pulses[pi2].done)pulses.splice(pi2,1);} | |
| } | |
| requestAnimationFrame(draw); | |
| })(); | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // WORLD MAP β Mapa de conectividad mundial animado | |
| // OPTIMIZACIΓN: puntos reducidos (~75β~55), throttle a ~30fps | |
| // Para mΓ‘s/menos puntos del mapa: cambiar el 5to valor en specs | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| (function initWorld(){ | |
| var cv=document.getElementById('worldC'); if(!cv) return; | |
| var ctx=cv.getContext('2d'); var W=190,H=52,t=0; | |
| var cont=[]; | |
| /* OPTIMIZACIΓN: puntos reducidos ~35% */ | |
| var specs=[[10,42,4,22,10],[24,18,28,18,5],[72,26,3,44,13],[102,58,4,32,15],[148,22,32,14,5]]; | |
| for(var si=0;si<specs.length;si++){var s=specs[si];for(var sj=0;sj<s[4];sj++)cont.push([s[0]+Math.random()*s[1],s[2]+Math.random()*s[3]]);} | |
| var hubs=[[30,14],[80,11],[130,9],[160,38],[55,40]]; | |
| var lastWorld=0; | |
| function draw(ts){ | |
| requestAnimationFrame(draw); | |
| /* OPTIMIZACIΓN: throttle a ~30fps */ | |
| if(ts - lastWorld < 33) return; | |
| lastWorld = ts; | |
| ctx.clearRect(0,0,W,H); | |
| for(var ci=0;ci<cont.length;ci++){ctx.beginPath();ctx.arc(cont[ci][0],cont[ci][1],.9,0,Math.PI*2);ctx.fillStyle='rgba(0,120,200,.28)';ctx.fill();} | |
| for(var i=0;i<hubs.length;i++) for(var j=i+1;j<hubs.length;j++){ | |
| var x1=hubs[i][0],y1=hubs[i][1],x2=hubs[j][0],y2=hubs[j][1],mid=[(x1+x2)/2,(y1+y2)/2-10]; | |
| ctx.beginPath();ctx.moveTo(x1,y1);ctx.quadraticCurveTo(mid[0],mid[1],x2,y2);ctx.strokeStyle='rgba(0,180,255,.07)';ctx.lineWidth=.7;ctx.stroke(); | |
| var pr=(Math.sin(t*1.5+i*1.2+j*.7)+1)/2,px=x1+(x2-x1)*pr,py=y1+(y2-y1)*pr-Math.sin(pr*Math.PI)*10; | |
| ctx.beginPath();ctx.arc(px,py,1.8,0,Math.PI*2);ctx.fillStyle='rgba(0,230,255,.75)';ctx.fill(); | |
| } | |
| for(var hi=0;hi<hubs.length;hi++){ | |
| var a=.5+Math.sin(t*2+hubs[hi][0])*.4; | |
| ctx.beginPath();ctx.arc(hubs[hi][0],hubs[hi][1],3.5,0,Math.PI*2);ctx.fillStyle='rgba(0,255,150,'+a+')';ctx.shadowBlur=6;ctx.shadowColor='rgba(0,255,150,.8)';ctx.fill();ctx.shadowBlur=0; | |
| ctx.beginPath();ctx.arc(hubs[hi][0],hubs[hi][1],6+Math.sin(t*3+hubs[hi][1])*2,0,Math.PI*2);ctx.strokeStyle='rgba(0,255,150,'+(a*.28)+')';ctx.lineWidth=.7;ctx.stroke(); | |
| } | |
| t+=.016; | |
| } | |
| requestAnimationFrame(draw); | |
| })(); | |
| </script> | |
| """ | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CHAT HANDLER β Procesamiento de mensajes y selecciΓ³n de motor | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def chat_engine_handler(history, message, mode, voice_enabled=False, pdf_file=None): | |
| if not message or message.strip() == "": | |
| return history, "", None | |
| if history is None: | |
| history = [] | |
| # ββ Mapa de modos a motores de chat ββ | |
| engines = { | |
| "general": regular_chat, | |
| "web": browser_chat, | |
| "productividad": productivity_chat, | |
| "cientifico": scientist_chat, | |
| "pdf": document_chat, | |
| } | |
| selected_engine = engines.get(mode, regular_chat) | |
| try: | |
| if mode == "pdf" and pdf_file: | |
| gen = selected_engine(history, message, pdf_file) | |
| else: | |
| gen = selected_engine(history, message) | |
| last_history, last_audio = None, None | |
| for yielded in gen: | |
| if isinstance(yielded, tuple) and len(yielded) == 2: | |
| last_history, last_audio = yielded | |
| else: | |
| last_history = yielded | |
| if last_history is None: | |
| last_history = history + [[message, "Sin respuesta del sistema."]] | |
| # ββ Limpieza e imΓ‘genes markdown ββ | |
| clean_history = [] | |
| for entry in last_history: | |
| if isinstance(entry, (list, tuple)) and len(entry) == 2: | |
| user_msg = entry[0] if entry[0] is not None else "" | |
| bot_msg = entry[1] if entry[1] is not None else "" | |
| if bot_msg and "![" in str(bot_msg): | |
| bot_msg = re.sub( | |
| r'!\[([^\]]*)\]\(([^)]+)\)', | |
| r'<img src="\2" alt="\1" style="max-width:100%;border-radius:10px;margin-top:8px;">', | |
| str(bot_msg) | |
| ) | |
| clean_history.append((str(user_msg), str(bot_msg))) | |
| if not clean_history: | |
| clean_history = list(history) + [(message, "No se recibio respuesta.")] | |
| # ββ SΓntesis de voz opcional ββ | |
| audio_path = None | |
| if voice_enabled and clean_history: | |
| last_bot = clean_history[-1][1] | |
| voice_text = re.sub(r'<[^>]+>', '', last_bot).replace("*","").replace("#","").replace("`","") | |
| try: | |
| audio_path = speak(voice_text[:450]) | |
| except Exception: | |
| pass | |
| return clean_history, "", audio_path | |
| except Exception as e: | |
| print("CRITICAL ERROR EN CVISION: " + str(e)) | |
| safe = list(history) if history else [] | |
| safe.append((message, "Error del sistema: " + str(e))) | |
| return safe, "", None | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # TRANSCRIBE PROCESSOR β Convierte audio grabado a texto | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def transcribe_processor(audio): | |
| if audio is None: | |
| return "" | |
| text = transcribe_audio_file(audio) | |
| return text if text else "" | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CREATE CHAT INTERFACE β Helper que crea el layout de chat | |
| # Reutilizado por todos los tabs excepto PDF | |
| # Layout: | |
| # [Chatbot] | |
| # Fila 1: [Textboxββββββββββ] [Enviar] [Voz] | |
| # Fila 2: [Mic/Audioββββββββββββββββ] [Limpiar] | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def create_chat_interface(mode_id, placeholder_text=None): | |
| ph = placeholder_text or PLACEHOLDER | |
| with gr.Column(): | |
| # ββ Ventana de chat ββ | |
| chat_window = gr.Chatbot( | |
| height=490, | |
| bubble_full_width=False, | |
| elem_classes="chatbot-container", | |
| show_label=False, | |
| render_markdown=True, | |
| show_copy_button=True, | |
| avatar_images=(None, AVATAR_URL), | |
| ) | |
| out_audio = gr.Audio(visible=False, autoplay=True) | |
| # ββ Fila 1: Textbox + Enviar + Voz ββ | |
| with gr.Row(elem_classes="input-row-cv"): | |
| input_msg = gr.Textbox( | |
| placeholder=ph, | |
| scale=10, | |
| show_label=False, | |
| container=False, | |
| ) | |
| btn_send = gr.Button( | |
| "Enviar β°ββ€", | |
| variant="primary", | |
| scale=2, | |
| min_width=110, | |
| elem_classes="btn-send-cv", | |
| ) | |
| btn_voice = gr.Button( | |
| "Voz π€παα||α", | |
| variant="secondary", | |
| scale=2, | |
| min_width=80, | |
| elem_classes="btn-voice-cv", | |
| ) | |
| # ββ Fila 2: MicrΓ³fono (vozβtexto) + Limpiar ββ | |
| with gr.Row(elem_classes="mic-row-cv"): | |
| mic_tool = gr.Audio( | |
| sources=["microphone"], | |
| type="filepath", | |
| label="TranscripciΓ³n de Voz", | |
| scale=8, | |
| elem_classes="mic-tool-cv", | |
| ) | |
| btn_clear = gr.Button( | |
| "β Limpiar ConversaciΓ³n", | |
| variant="stop", | |
| scale=3, | |
| min_width=160, | |
| elem_classes="btn-clear-cv", | |
| ) | |
| # ββ Estado del modo activo ββ | |
| st_mode = gr.State(mode_id) | |
| # ββ Mic β transcripciΓ³n automΓ‘tica en el textbox ββ | |
| mic_tool.stop_recording( | |
| transcribe_processor, | |
| inputs=[mic_tool], | |
| outputs=[input_msg], | |
| ) | |
| # ββ Enviar mensaje ββ | |
| btn_send.click( | |
| chat_engine_handler, | |
| inputs=[chat_window, input_msg, st_mode, gr.State(False)], | |
| outputs=[chat_window, input_msg, out_audio], | |
| ) | |
| input_msg.submit( | |
| chat_engine_handler, | |
| inputs=[chat_window, input_msg, st_mode, gr.State(False)], | |
| outputs=[chat_window, input_msg, out_audio], | |
| ) | |
| # ββ Enviar con sΓntesis de voz ββ | |
| btn_voice.click( | |
| chat_engine_handler, | |
| inputs=[chat_window, input_msg, st_mode, gr.State(True)], | |
| outputs=[chat_window, input_msg, out_audio], | |
| ) | |
| # ββ Limpiar conversaciΓ³n ββ | |
| btn_clear.click( | |
| lambda: ([], "", None), | |
| inputs=None, | |
| outputs=[chat_window, input_msg, out_audio], | |
| queue=False, | |
| ) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # GRADIO INTERFACE β ConstrucciΓ³n de la interfaz principal | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Blocks(css=CUSTOM_CSS, title="CVision Intelligence | Neural OS") as assistant: | |
| # ββ SecciΓ³n visual superior ββ | |
| gr.HTML(HEADER_HTML) | |
| gr.HTML(NEBULA_SECTION_HTML) | |
| gr.HTML(STATUS_AND_JS_HTML) | |
| # ββ Tabs de mΓ³dulos ββ | |
| with gr.Tabs(): | |
| # ββ Tab: NΓΊcleo Central (chat general) ββ | |
| with gr.Tab("π π¬ CHAT GENERAL"): | |
| create_chat_interface("general") | |
| # ββ Tab: Red Global (navegaciΓ³n web) ββ | |
| with gr.Tab("ππ BΓSQUEDA WEB"): | |
| create_chat_interface("web") | |
| # ββ Tab: Algoritmos (productividad) ββ | |
| with gr.Tab("ππ― PRODUCTIVIDAD"): | |
| create_chat_interface("productividad") | |
| # ββ Tab: Laboratorio (anΓ‘lisis cientΓfico) ββ | |
| with gr.Tab("βοΈπ¬ CIENTIFICO"): | |
| create_chat_interface("cientifico") | |
| # ββ Tab: AnΓ‘lisis de Matriz (PDF) ββ | |
| with gr.Tab("ππ ANΓLISIS DE PDF"): | |
| with gr.Row(): | |
| # Columna de carga de archivo PDF | |
| with gr.Column(scale=2): | |
| gr.Markdown("### π₯ Ingesta de Archivos") | |
| file_input = gr.File( | |
| label="Documento PDF de Origen", | |
| file_types=[".pdf"], | |
| ) | |
| # Columna de chat PDF | |
| with gr.Column(scale=5): | |
| chat_pdf = gr.Chatbot( | |
| height=450, | |
| bubble_full_width=False, | |
| elem_classes="chatbot-container", | |
| show_label=False, | |
| render_markdown=True, | |
| avatar_images=(None, AVATAR_URL), | |
| ) | |
| pdf_audio = gr.Audio(visible=False, autoplay=True) | |
| # Fila 1: textbox + Ejecutar + Voz | |
| with gr.Row(elem_classes="input-row-cv"): | |
| txt_pdf = gr.Textbox( | |
| placeholder="Consultar parΓ‘metros del documento...", | |
| scale=10, | |
| container=False, | |
| ) | |
| btn_send_pdf = gr.Button( | |
| "Ejecutar β°ββ€", | |
| variant="primary", | |
| scale=2, | |
| min_width=110, | |
| elem_classes="btn-exec-cv", | |
| ) | |
| btn_voz_pdf = gr.Button( | |
| "Voz π€παα||α", | |
| variant="secondary", | |
| scale=2, | |
| min_width=80, | |
| elem_classes="btn-voice-cv", | |
| ) | |
| # Fila 2: mic + limpiar | |
| with gr.Row(elem_classes="mic-row-cv"): | |
| mic_pdf = gr.Audio( | |
| sources=["microphone"], | |
| type="filepath", | |
| label="TranscripciΓ³n de Voz", | |
| scale=8, | |
| elem_classes="mic-tool-cv", | |
| ) | |
| btn_clr_pdf = gr.Button( | |
| "βποΈ Limpiar", | |
| variant="stop", | |
| scale=3, | |
| min_width=100, | |
| elem_classes="btn-clear-cv", | |
| ) | |
| # Mic PDF β textbox | |
| mic_pdf.stop_recording( | |
| transcribe_processor, | |
| inputs=[mic_pdf], | |
| outputs=[txt_pdf], | |
| ) | |
| pdf_in = [chat_pdf, txt_pdf, gr.State("pdf"), gr.State(False), file_input] | |
| pdf_inv = [chat_pdf, txt_pdf, gr.State("pdf"), gr.State(True), file_input] | |
| pdf_out = [chat_pdf, txt_pdf, pdf_audio] | |
| btn_send_pdf.click(chat_engine_handler, pdf_in, pdf_out) | |
| txt_pdf.submit( chat_engine_handler, pdf_in, pdf_out) | |
| btn_voz_pdf.click( chat_engine_handler, pdf_inv, pdf_out) | |
| btn_clr_pdf.click(lambda: ([], "", None), None, pdf_out, queue=False) | |
| # ββ Tab 6: Modo Voz (JARVIS) ββ | |
| with gr.Tab("ποΈπ MODO VOZ"): | |
| create_voice_tab() | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # SECCIΓN 07: EXPOSICIΓN ADAPTATIVA DE LA INTERFAZ | |
| # Expone la interfaz pura de Gradio para que el orquestador controle el flujo. | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Simplemente asignamos el bloque puro de Gradio sin envolturas pesadas | |
| assistant_app = assistant |