// Main application logic for the chatbot class ChatbotApp { constructor() { this.generator = null; this.messages = [ { role: "system", content: "You are a helpful assistant." } ]; this.isGenerating = false; this.init(); } async init() { // Initialize UI elements this.loadingScreen = document.getElementById('loadingScreen'); this.chatContainer = document.getElementById('chatContainer'); this.messagesArea = document.getElementById('messagesArea'); this.messageInput = document.getElementById('messageInput'); this.sendButton = document.getElementById('sendButton'); this.progressBar = document.getElementById('progressBar'); this.progressText = document.getElementById('progressText'); // Set up event listeners this.sendButton.addEventListener('click', () => this.sendMessage()); this.messageInput.addEventListener('keydown', (e) => this.handleKeyPress(e)); this.messageInput.addEventListener('input', () => this.adjustTextareaHeight()); // Load the model await this.loadModel(); } async loadModel() { try { // Import transformers.js const { pipeline, TextStreamer, env } = await import('https://cdn.jsdelivr.net/npm/@huggingface/transformers'); // Configure environment env.allowLocalModels = false; env.allowRemoteModels = true; // Create a text generation pipeline this.generator = await pipeline( "text-generation", "onnx-community/gemma-3-270m-it-ONNX", { dtype: "fp32", progress_callback: (info) => { if (info.status === 'downloading') { const progress = Math.round((info.loaded / info.total) * 100); this.updateProgress(progress); } } } ); // Model loaded successfully this.loadingScreen.classList.add('hidden'); this.chatContainer.classList.remove('hidden'); this.messageInput.focus(); } catch (error) { console.error('Error loading model:', error); this.showError('Failed to load model. Please refresh the page and try again.'); } } updateProgress(progress) { this.progressBar.style.width = `${progress}%`; this.progressText.textContent = `${progress}%`; } showError(message) { this.loadingScreen.innerHTML = `

Error

${message}

`; } handleKeyPress(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } } adjustTextareaHeight() { this.messageInput.style.height = 'auto'; this.messageInput.style.height = Math.min(this.messageInput.scrollHeight, 120) + 'px'; } async sendMessage() { const message = this.messageInput.value.trim(); if (!message || this.isGenerating) return; // Add user message to UI this.addMessageToUI('user', message); this.messages.push({ role: "user", content: message }); // Clear input this.messageInput.value = ''; this.adjustTextareaHeight(); // Disable send button this.isGenerating = true; this.sendButton.disabled = true; this.sendButton.innerHTML = ` `; // Add assistant message placeholder const assistantMessageId = this.addMessageToUI('assistant', '', true); try { // Generate response with streaming let fullResponse = ''; const streamer = new this.generator.tokenizer.TextStreamer(this.generator.tokenizer, { skip_prompt: true, skip_special_tokens: true, callback_function: (text) => { fullResponse += text; this.updateMessage(assistantMessageId, fullResponse); } }); const output = await this.generator(this.messages, { max_new_tokens: 512, do_sample: false, streamer: streamer, }); const response = output[0].generated_text.at(-1).content; this.messages.push({ role: "assistant", content: response }); } catch (error) { console.error('Error generating response:', error); this.updateMessage(assistantMessageId, 'Sorry, I encountered an error. Please try again.'); } finally { // Re-enable send button this.isGenerating = false; this.sendButton.disabled = false; this.sendButton.innerHTML = ` `; this.messageInput.focus(); } } addMessageToUI(role, content, isStreaming = false) { const messageId = Date.now(); const messageDiv = document.createElement('div'); messageDiv.className = 'flex items-start space-x-3'; messageDiv.id = `message-${messageId}`; if (role === 'user') { messageDiv.innerHTML = `

${content}

`; } else { messageDiv.innerHTML = `

${isStreaming ? '' : content}

`; } this.messagesArea.appendChild(messageDiv); this.messagesArea.scrollTop = this.messagesArea.scrollHeight; return messageId; } updateMessage(messageId, content) { const messageElement = document.getElementById(`message-${messageId}`); if (messageElement) { const contentElement = messageElement.querySelector('.bg-white p, .bg-gray-100 p'); contentElement.innerHTML = content + ''; this.messagesArea.scrollTop = this.messagesArea.scrollHeight; } } } // Initialize the app when DOM is loaded document.addEventListener('DOMContentLoaded', () => { new ChatbotApp(); });