document.addEventListener('DOMContentLoaded', () => { // Chat Interface Logic const chatMessages = document.getElementById('chat-messages'); const chatInput = document.getElementById('chat-input'); const sendBtn = document.getElementById('send-btn'); const modelSelector = document.getElementById('model-selector'); const reasoningToggle = document.getElementById('reasoning-toggle'); const apiKeyInput = document.getElementById('api-key'); const tokenCount = document.getElementById('token-count'); const latencyDisplay = document.getElementById('latency-display'); let conversationHistory = []; let totalTokens = 0; // Default API Key (user can override) const DEFAULT_API_KEY = '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'; function getApiKey() { return apiKeyInput.value.trim() || DEFAULT_API_KEY; } function addMessage(role, content, reasoning = null) { const messageDiv = document.createElement('div'); messageDiv.className = 'flex gap-3' + (role === 'user' ? ' flex-row-reverse' : ''); const avatar = role === 'user' ? `
` : `
`; let contentHtml = `

${escapeHtml(content)}

`; if (reasoning) { contentHtml = `
Reasoning Process
${escapeHtml(reasoning)}
${contentHtml} `; } messageDiv.innerHTML = ` ${avatar}
${role === 'user' ? 'YOU' : modelSelector.options[modelSelector.selectedIndex].text.split('(')[0].trim()}
${contentHtml}
`; chatMessages.appendChild(messageDiv); chatMessages.scrollTop = chatMessages.scrollHeight; if (window.feather) { window.feather.replace(); } } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async function sendMessage() { const message = chatInput.value.trim(); if (!message) return; const model = modelSelector.value; const useReasoning = reasoningToggle.checked && ( model.includes('nemotron') || model.includes('gpt-oss') ); const apiKey = getApiKey(); // Add user message addMessage('user', message); chatInput.value = ''; // Add to conversation history conversationHistory.push({ role: 'user', content: message }); // Show loading const loadingDiv = document.createElement('div'); loadingDiv.className = 'flex gap-3'; loadingDiv.id = 'loading-indicator'; loadingDiv.innerHTML = `
`; chatMessages.appendChild(loadingDiv); chatMessages.scrollTop = chatMessages.scrollHeight; const startTime = performance.now(); try { const requestBody = { model: model, messages: conversationHistory }; if (useReasoning) { requestBody.extra_body = { reasoning: { enabled: true } }; } const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`, 'HTTP-Referer': window.location.href, 'X-Title': 'NexusNarrative Core' }, body: JSON.stringify(requestBody) }); if (!response.ok) { throw new Error(`API Error: ${response.status} ${response.statusText}`); } const data = await response.json(); const endTime = performance.now(); const latency = Math.round(endTime - startTime); // Remove loading document.getElementById('loading-indicator')?.remove(); // Extract response const assistantMessage = data.choices[0].message; const reasoningDetails = assistantMessage.reasoning_details || null; // Update stats totalTokens += data.usage?.total_tokens || 0; tokenCount.textContent = `Tokens: ${totalTokens.toLocaleString()}`; latencyDisplay.textContent = `Latency: ${latency}ms`; // Add assistant response addMessage('assistant', assistantMessage.content, reasoningDetails); // Update conversation history if (reasoningDetails) { conversationHistory.push({ role: 'assistant', content: assistantMessage.content, reasoning_details: reasoningDetails }); } else { conversationHistory.push({ role: 'assistant', content: assistantMessage.content }); } } catch (error) { document.getElementById('loading-indicator')?.remove(); addMessage('assistant', `Erreur: ${error.message}`); } } // Event listeners sendBtn.addEventListener('click', sendMessage); chatInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') sendMessage(); }); // Simulate live logs in the terminal const logsContainer = document.getElementById('terminal-logs'); const messages = [ { type: 'DIRECTOR', text: 'Optimizing task queue...', color: 'text-cyber-400' }, { type: 'SYSTEM', text: 'Garbage collection cleared 24MB.', color: 'text-slate-500' }, { type: 'WRITER', text: 'Drafting chapter 4 scene 2...', color: 'text-ai-400' }, { type: 'LORE', text: 'Updating character vector: "Kael"', color: 'text-amber-400' }, { type: 'SYSTEM', text: 'Health check passed. Latency: 14ms', color: 'text-slate-500' } ]; let msgIndex = 0; function addLog() { if(!logsContainer) return; const msg = messages[msgIndex % messages.length]; const div = document.createElement('div'); div.className = 'mb-1 opacity-0 transition-opacity duration-500'; div.innerHTML = `[${msg.type}] ${msg.text}`; // Insert before the last prompt line const lastLine = logsContainer.lastElementChild; logsContainer.insertBefore(div, lastLine); // Trigger reflow for animation setTimeout(() => div.classList.remove('opacity-0'), 50); // Auto scroll logsContainer.scrollTop = logsContainer.scrollHeight; msgIndex++; // Random interval between 2s and 5s const nextTime = Math.random() * 3000 + 2000; setTimeout(addLog, nextTime); } // Start simulation after 1s setTimeout(addLog, 1000); });