Spaces:
Running
Running
| /** | |
| * NEURAL NEXUS - Main Application Controller | |
| * Real-Time Conversational AI Agent | |
| */ | |
| class NeuralNexusApp { | |
| constructor() { | |
| // State Management | |
| this.state = { | |
| isListening: false, | |
| isSpeaking: false, | |
| isThinking: false, | |
| toolsActive: false, | |
| sessionStartTime: Date.now(), | |
| latency: 45, | |
| messages: [], | |
| currentTranscript: '', | |
| sessionTimer: null | |
| }; | |
| // DOM Elements | |
| this.elements = { | |
| // Main UI | |
| messageInput: document.getElementById('message-input'), | |
| sendBtn: document.getElementById('send-btn'), | |
| voiceBtn: document.getElementById('voice-btn'), | |
| transcriptContainer: document.getElementById('transcript-container'), | |
| latencyDisplay: document.getElementById('latency-display'), | |
| sessionTimer: document.getElementById('session-timer'), | |
| // Avatar and Visualizers | |
| avatar: document.getElementById('ai-avatar'), | |
| agentVisualizer: document.getElementById('agent-visualizer'), | |
| userVisualizer: document.getElementById('user-visualizer'), | |
| statusIndicator: document.getElementById('main-status'), | |
| // Tool Status | |
| toolStatus: document.getElementById('tool-status'), | |
| toolStatusText: document.getElementById('tool-status-text'), | |
| // Neural Network Canvas | |
| neuralCanvas: document.getElementById('neural-network-canvas'), | |
| // Settings | |
| settingsModal: document.getElementById('settings-modal'), | |
| settingsBtn: document.getElementById('settings-btn'), | |
| closeSettings: document.getElementById('close-settings'), | |
| // FABs | |
| pdfUploadBtn: document.getElementById('pdf-upload-btn'), | |
| pdfFileInput: document.getElementById('pdf-file-input'), | |
| webSearchBtn: document.getElementById('web-search-btn'), | |
| connectTwitterBtn: document.getElementById('connect-twitter-btn'), | |
| // Chat controls | |
| clearChatBtn: document.getElementById('clear-chat'), | |
| exportChatBtn: document.getElementById('export-chat'), | |
| fullscreenBtn: document.getElementById('fullscreen-btn') | |
| }; | |
| // Initialize subsystems | |
| this.initializeNeuralNetworkCanvas(); | |
| this.initializeEventListeners(); | |
| this.initializeSessionTimer(); | |
| this.simulateLatency(); | |
| } | |
| /** | |
| * Initialize neural network background animation | |
| */ | |
| initializeNeuralNetworkCanvas() { | |
| const canvas = this.elements.neuralCanvas; | |
| const ctx = canvas.getContext('2d'); | |
| const resizeCanvas = () => { | |
| canvas.width = canvas.offsetWidth * window.devicePixelRatio; | |
| canvas.height = canvas.offsetHeight * window.devicePixelRatio; | |
| ctx.scale(window.devicePixelRatio, window.devicePixelRatio); | |
| }; | |
| resizeCanvas(); | |
| window.addEventListener('resize', resizeCanvas); | |
| // Neural network nodes and connections | |
| const nodes = []; | |
| const connections = []; | |
| // Create nodes | |
| for (let i = 0; i < 50; i++) { | |
| nodes.push({ | |
| x: Math.random() * canvas.offsetWidth, | |
| y: Math.random() * canvas.offsetHeight, | |
| vx: (Math.random() - 0.5) * 0.5, | |
| vy: (Math.random() - 0.5) * 0.5, | |
| size: Math.random() * 2 + 1 | |
| }); | |
| } | |
| // Create connections | |
| for (let i = 0; i < nodes.length; i++) { | |
| for (let j = i + 1; j < nodes.length; j++) { | |
| const dist = Math.hypot(nodes[i].x - nodes[j].x, nodes[i].y - nodes[j].y); | |
| if (dist < 150) { | |
| connections.push({ from: i, to: j, alpha: Math.random() }); | |
| } | |
| } | |
| } | |
| let animationFrame; | |
| const animate = () => { | |
| ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight); | |
| // Update and draw nodes | |
| nodes.forEach(node => { | |
| node.x += node.vx; | |
| node.y += node.vy; | |
| // Bounce off edges | |
| if (node.x < 0 || node.x > canvas.offsetWidth) node.vx *= -1; | |
| if (node.y < 0 || node.y > canvas.offsetHeight) node.vy *= -1; | |
| // Draw node | |
| ctx.beginPath(); | |
| ctx.arc(node.x, node.y, node.size, 0, Math.PI * 2); | |
| ctx.fillStyle = '#8b5cf6'; | |
| ctx.fill(); | |
| }); | |
| // Draw connections | |
| connections.forEach(conn => { | |
| const from = nodes[conn.from]; | |
| const to = nodes[conn.to]; | |
| const dist = Math.hypot(from.x - to.x, from.y - to.y); | |
| if (dist < 150) { | |
| ctx.beginPath(); | |
| ctx.moveTo(from.x, from.y); | |
| ctx.lineTo(to.x, to.y); | |
| const alpha = (1 - dist / 150) * 0.3; | |
| ctx.strokeStyle = `rgba(139, 92, 246, ${alpha})`; | |
| ctx.stroke(); | |
| } | |
| }); | |
| animationFrame = requestAnimationFrame(animate); | |
| }; | |
| animate(); | |
| } | |
| /** | |
| * Initialize all event listeners | |
| */ | |
| initializeEventListeners() { | |
| // Message input | |
| this.elements.messageInput.addEventListener('input', (e) => { | |
| const hasValue = e.target.value.trim().length > 0; | |
| this.elements.sendBtn.disabled = !hasValue; | |
| this.elements.sendBtn.classList.toggle('opacity-50', !hasValue); | |
| }); | |
| this.elements.messageInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| this.sendMessage(); | |
| } | |
| }); | |
| // Send button | |
| this.elements.sendBtn.addEventListener('click', () => this.sendMessage()); | |
| // Voice button | |
| this.elements.voiceBtn.addEventListener('click', () => this.toggleVoice()); | |
| // Settings modal | |
| this.elements.settingsBtn.addEventListener('click', () => { | |
| this.elements.settingsModal.classList.remove('hidden'); | |
| this.elements.settingsModal.classList.add('flex'); | |
| }); | |
| this.elements.closeSettings.addEventListener('click', () => { | |
| this.elements.settingsModal.classList.add('hidden'); | |
| this.elements.settingsModal.classList.remove('flex'); | |
| }); | |
| // FABs | |
| this.elements.pdfUploadBtn.addEventListener('click', () => { | |
| this.elements.pdfFileInput.click(); | |
| }); | |
| this.elements.pdfFileInput.addEventListener('change', (e) => this.handlePDFUpload(e)); | |
| this.elements.webSearchBtn.addEventListener('click', () => this.toggleWebSearch()); | |
| this.elements.connectTwitterBtn.addEventListener('click', () => this.connectTwitterSpaces()); | |
| // Chat controls | |
| this.elements.clearChatBtn.addEventListener('click', () => this.clearChat()); | |
| this.elements.exportChatBtn.addEventListener('click', () => this.exportChat()); | |
| this.elements.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen()); | |
| } | |
| /** | |
| * Session timer | |
| */ | |
| initializeSessionTimer() { | |
| const updateTimer = () => { | |
| const elapsed = Date.now() - this.state.sessionStartTime; | |
| const hours = Math.floor(elapsed / 3600000).toString().padStart(2, '0'); | |
| const minutes = Math.floor((elapsed % 3600000) / 60000).toString().padStart(2, '0'); | |
| const seconds = Math.floor((elapsed % 60000) / 1000).toString().padStart(2, '0'); | |
| this.elements.sessionTimer.textContent = `${hours}:${minutes}:${seconds}`; | |
| }; | |
| updateTimer(); | |
| this.state.sessionTimer = setInterval(updateTimer, 1000); | |
| } | |
| /** | |
| * Simulate latency variation | |
| */ | |
| simulateLatency() { | |
| setInterval(() => { | |
| // Simulate network latency between 30-70ms | |
| this.state.latency = Math.floor(Math.random() * 40) + 30; | |
| this.elements.latencyDisplay.textContent = `${this.state.latency}ms`; | |
| }, 3000); | |
| } | |
| /** | |
| * Send message handler | |
| */ | |
| async sendMessage() { | |
| const message = this.elements.messageInput.value.trim(); | |
| if (!message || this.state.isThinking) return; | |
| // Add user message to transcript | |
| this.addMessageToTranscript({ | |
| speaker: 'user', | |
| text: message, | |
| timestamp: new Date() | |
| }); | |
| this.elements.messageInput.value = ''; | |
| this.elements.sendBtn.disabled = true; | |
| this.elements.sendBtn.classList.add('opacity-50'); | |
| // Show user waveform briefly | |
| this.elements.userVisualizer.style.opacity = '1'; | |
| setTimeout(() => { | |
| this.elements.userVisualizer.style.opacity = '0'; | |
| }, 1000); | |
| // Process message through AI pipeline | |
| await this.processAIMessage(message); | |
| } | |
| /** | |
| * Add message to transcript UI | |
| */ | |
| addMessageToTranscript(message) { | |
| const messageEl = document.createElement('div'); | |
| messageEl.className = 'flex items-start space-x-3 animate-slide-up'; | |
| const isUser = message.speaker === 'user'; | |
| const timeStr = message.timestamp.toLocaleTimeString('en-US', { | |
| hour12: false, | |
| hour: '2-digit', | |
| minute: '2-digit', | |
| second: '2-digit' | |
| }); | |
| messageEl.innerHTML = ` | |
| <div class="w-8 h-8 rounded-full ${isUser ? 'bg-gradient-to-br from-info to-accent' : 'bg-gradient-to-br from-primary to-secondary'} flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="${isUser ? 'user' : 'cpu'}" class="w-4 h-4 text-white"></i> | |
| </div> | |
| <div class="${isUser ? 'glass-message ml-auto' : 'glass-message'} max-w-xs"> | |
| <p class="text-sm text-slate-300">${message.text}</p> | |
| <span class="text-xs text-slate-400 block mt-2">${timeStr}</span> | |
| </div> | |
| `; | |
| this.elements.transcriptContainer.appendChild(messageEl); | |
| this.elements.transcriptContainer.scrollTop = this.elements.transcriptContainer.scrollHeight; | |
| // Re-render feather icons | |
| feather.replace(); | |
| } | |
| /** | |
| * Process AI message with simulated pipeline | |
| */ | |
| async processAIMessage(userMessage) { | |
| // Set thinking state | |
| this.state.isThinking = true; | |
| this.elements.statusIndicator.setStatus('thinking'); | |
| // Simulate LLM processing time (target: <800ms) | |
| const thinkingTime = Math.random() * 400 + 300; // 300-700ms | |
| await this.delay(thinkingTime); | |
| // Simulate tool usage for certain queries | |
| if (userMessage.toLowerCase().includes('search') || userMessage.toLowerCase().includes('what is')) { | |
| await this.executeTool('web-search', 'Searching web for information...'); | |
| } | |
| // Generate AI response (simulated) | |
| const responses = [ | |
| "I've analyzed your query. Based on real-time data processing, I can confirm that the latency metrics are well within acceptable parameters. The system is operating at optimal performance.", | |
| "Processing complete. I've integrated the information into my context window. The neural pathways are firing at peak efficiency, with sub-50ms audio processing latency maintained.", | |
| "Interesting question! Let me search my knowledge base... Ah yes, I found relevant information. The vector embeddings show high similarity scores for this topic.", | |
| "I'm detecting a knowledge gap in my training data. Initiating web search protocol... Stand by for real-time information retrieval.", | |
| "Analysis complete. The PDF document has been successfully processed and indexed. You can now query its contents naturally in our conversation." | |
| ]; | |
| const response = responses[Math.floor(Math.random() * responses.length)]; | |
| // Update status to speaking | |
| this.state.isThinking = false; | |
| this.elements.statusIndicator.setStatus('speaking'); | |
| this.elements.avatar.startSpeaking(); | |
| // Add AI message to transcript with typing effect | |
| await this.typeMessage({ | |
| speaker: 'agent', | |
| text: response, | |
| timestamp: new Date() | |
| }); | |
| // Show agent waveform | |
| this.elements.agentVisualizer.style.opacity = '1'; | |
| // Simulate speaking duration | |
| const speakingTime = response.length * 50; // ~50ms per character | |
| setTimeout(() => { | |
| this.elements.avatar.stopSpeaking(); | |
| this.elements.statusIndicator.setStatus('idle'); | |
| this.elements.agentVisualizer.style.opacity = '0'; | |
| }, speakingTime); | |
| } | |
| /** | |
| * Typewriter effect for AI messages | |
| */ | |
| async typeMessage(message) { | |
| const messageEl = document.createElement('div'); | |
| messageEl.className = 'flex items-start space-x-3 animate-slide-up'; | |
| const timeStr = message.timestamp.toLocaleTimeString('en-US', { | |
| hour12: false, | |
| hour: '2-digit', | |
| minute: '2-digit', | |
| second: '2-digit' | |
| }); | |
| messageEl.innerHTML = ` | |
| <div class="w-8 h-8 rounded-full bg-gradient-to-br from-primary to-secondary flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="cpu" class="w-4 h-4 text-white"></i> | |
| </div> | |
| <div class="glass-message max-w-xs"> | |
| <p class=" |