/** * 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 = `