// Global state let conversationHistory = JSON.parse(localStorage.getItem('claudeChat_history')) || []; let selectedModel = localStorage.getItem('claudeChat_selectedModel') || 'claude-sonnet-4-5'; let isProcessing = false; let isStreaming = false; // DOM Elements const modelSelector = document.getElementById('model-selector'); const messageInput = document.getElementById('message-input'); const sendBtn = document.getElementById('send-btn'); const streamBtn = document.getElementById('stream-btn'); const chatContainer = document.getElementById('chat-container'); const messagesContainer = document.getElementById('messages'); const examplePrompts = document.getElementById('example-prompts'); const exampleButtons = document.querySelectorAll('.example-btn'); const statusBar = document.getElementById('status-bar'); const statusMessage = document.getElementById('status-message'); const retryBtn = document.getElementById('retry-btn'); const typingIndicator = document.getElementById('typing-indicator'); const charCount = document.getElementById('char-count'); const newChatBtn = document.getElementById('new-chat-btn'); const appTitle = document.getElementById('app-title'); // Initialize document.addEventListener('DOMContentLoaded', () => { // Set initial model modelSelector.value = selectedModel; // Load conversation history renderConversationHistory(); // Set up event listeners setupEventListeners(); // Update character count updateCharCount(); }); // Set up event listeners function setupEventListeners() { // Model selection modelSelector.addEventListener('change', handleModelChange); // Message input messageInput.addEventListener('input', updateCharCount); messageInput.addEventListener('keydown', handleKeyDown); // Send buttons sendBtn.addEventListener('click', () => sendMessage(false)); streamBtn.addEventListener('click', () => sendMessage(true)); // Example prompts exampleButtons.forEach(button => { button.addEventListener('click', () => { messageInput.value = button.textContent; messageInput.focus(); updateCharCount(); }); }); // Status bar retryBtn.addEventListener('click', retryLastMessage); // New chat newChatBtn.addEventListener('click', resetConversation); appTitle.addEventListener('click', resetConversation); // Auto-scroll chatContainer.addEventListener('scroll', handleScroll); } // Handle model change function handleModelChange() { const newModel = modelSelector.value; const previousModel = selectedModel; selectedModel = newModel; localStorage.setItem('claudeChat_selectedModel', selectedModel); // If conversation exists, add system message if (conversationHistory.length > 0) { const modelName = modelSelector.options[modelSelector.selectedIndex].text; addSystemMessage(`Model changed to ${modelName} — previous context retained`); } } // Handle keyboard shortcuts function handleKeyDown(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); if (!isProcessing && messageInput.value.trim()) { sendMessage(false); } } // Clear input shortcut if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); messageInput.value = ''; updateCharCount(); } // New chat shortcut if ((e.ctrlKey || e.metaKey) && e.key === 'n') { e.preventDefault(); resetConversation(); } } // Update character count function updateCharCount() { const count = messageInput.value.length; charCount.textContent = `${count}/10000`; if (count > 8000) { charCount.classList.add('text-red-500'); } else { charCount.classList.remove('text-red-500'); } // Enable/disable buttons const hasText = count > 0; sendBtn.disabled = !hasText || isProcessing; streamBtn.disabled = !hasText || isProcessing; } // Send message async function sendMessage(stream = false) { const message = messageInput.value.trim(); if (!message || isProcessing) return; isProcessing = true; isStreaming = stream; updateUIState(); // Add user message to UI addUserMessage(message); // Clear input messageInput.value = ''; updateCharCount(); // Show typing indicator for non-streaming if (!stream) { typingIndicator.classList.remove('hidden'); chatContainer.scrollTop = chatContainer.scrollHeight; } try { // Add to history conversationHistory.push({ role: 'user', content: message }); saveConversation(); // Call API if (stream) { await streamResponse(message); } else { await getResponse(message); } } catch (error) { handleError(error); } finally { isProcessing = false; isStreaming = false; updateUIState(); typingIndicator.classList.add('hidden'); } } // Get response from API (non-streaming) async function getResponse(userMessage) { try { const response = await puter.ai.chat(userMessage, { model: selectedModel, conversationHistory: conversationHistory }); const assistantMessage = response.message?.content?.[0]?.text || "Sorry, I couldn't process that."; // Add to history conversationHistory.push({ role: 'assistant', content: assistantMessage }); saveConversation(); // Add to UI addAssistantMessage(assistantMessage); } catch (error) { throw error; } } // Stream response from API async function streamResponse(userMessage) { try { const response = await puter.ai.chat(userMessage, { model: selectedModel, conversationHistory: conversationHistory, stream: true }); // Create assistant message element const messageElement = document.createElement('div'); messageElement.className = 'assistant-message bg-white p-4 rounded-lg shadow-sm'; messageElement.innerHTML = `