import { pipeline, TextStreamer } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.0'; class ChatApp { constructor() { this.generator = null; this.messages = [ { role: "system", content: "You are a helpful, friendly, and knowledgeable AI assistant. Provide clear, concise, and accurate responses." } ]; this.isGenerating = false; this.elements = { loadingScreen: document.getElementById('loading-screen'), chatArea: document.getElementById('chat-area'), messagesContainer: document.getElementById('messages'), userInput: document.getElementById('user-input'), sendButton: document.getElementById('send-button'), loadingStatus: document.getElementById('loading-status'), progressFill: document.getElementById('progress-fill') }; this.init(); } async init() { await this.loadModel(); this.setupEventListeners(); this.showChat(); } async loadModel() { try { this.updateLoadingStatus('Downloading model...', 10); // Create a text generation pipeline with progress tracking this.generator = await pipeline( "text-generation", "onnx-community/Llama-3.2-1B-Instruct-q4f16", { dtype: "q4f16", device: "webgpu", progress_callback: (progress) => { if (progress.status === 'downloading') { const percent = Math.round((progress.loaded / progress.total) * 100); this.updateLoadingStatus(`Downloading: ${progress.file}`, percent); } else if (progress.status === 'loading') { this.updateLoadingStatus('Loading model into memory...', 90); } } } ); this.updateLoadingStatus('Model loaded successfully!', 100); // Small delay to show completion await new Promise(resolve => setTimeout(resolve, 500)); } catch (error) { console.error('Error loading model:', error); this.updateLoadingStatus('Error loading model. Please refresh the page.', 0); throw error; } } updateLoadingStatus(status, progress) { this.elements.loadingStatus.textContent = status; this.elements.progressFill.style.width = `${progress}%`; } showChat() { this.elements.loadingScreen.style.display = 'none'; this.elements.chatArea.style.display = 'flex'; this.elements.userInput.focus(); } setupEventListeners() { // Send button click this.elements.sendButton.addEventListener('click', () => this.handleSend()); // Enter key to send (Shift+Enter for new line) this.elements.userInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.handleSend(); } }); // Enable/disable send button based on input this.elements.userInput.addEventListener('input', () => { this.autoResizeTextarea(); const hasText = this.elements.userInput.value.trim().length > 0; this.elements.sendButton.disabled = !hasText || this.isGenerating; }); } autoResizeTextarea() { const textarea = this.elements.userInput; textarea.style.height = 'auto'; textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px'; } async handleSend() { const userMessage = this.elements.userInput.value.trim(); if (!userMessage || this.isGenerating) return; // Add user message to chat this.addMessage(userMessage, 'user'); // Clear input this.elements.userInput.value = ''; this.elements.userInput.style.height = 'auto'; this.elements.sendButton.disabled = true; // Add user message to conversation history this.messages.push({ role: "user", content: userMessage }); // Generate response await this.generateResponse(); } async generateResponse() { this.isGenerating = true; // Add typing indicator const typingElement = this.addTypingIndicator(); try { let fullResponse = ''; let messageElement = null; // Create text streamer with callback const streamer = new TextStreamer(this.generator.tokenizer, { skip_prompt: true, skip_special_tokens: true, callback_function: (text) => { fullResponse += text; // Remove typing indicator and create message element on first chunk if (!messageElement) { typingElement.remove(); messageElement = this.addMessage('', 'assistant'); } // Update the message content const messageText = messageElement.querySelector('.message-text'); messageText.textContent = fullResponse; // Scroll to bottom this.scrollToBottom(); } }); // Generate response with streaming const output = await this.generator(this.messages, { max_new_tokens: 512, do_sample: false, streamer: streamer, }); // Add assistant response to conversation history const assistantMessage = output[0].generated_text.at(-1).content; this.messages.push({ role: "assistant", content: assistantMessage }); } catch (error) { console.error('Error generating response:', error); typingElement.remove(); this.addMessage('Sorry, I encountered an error. Please try again.', 'assistant'); } finally { this.isGenerating = false; this.elements.sendButton.disabled = this.elements.userInput.value.trim().length === 0; } } addMessage(text, role) { const messageDiv = document.createElement('div'); messageDiv.className = `message ${role}-message`; const avatar = document.createElement('div'); avatar.className = `message-avatar ${role}-avatar`; if (role === 'assistant') { avatar.innerHTML = ` `; } else { avatar.textContent = 'U'; } const content = document.createElement('div'); content.className = 'message-content'; const header = document.createElement('div'); header.className = 'message-header'; header.textContent = role === 'assistant' ? 'AI Assistant' : 'You'; const messageText = document.createElement('div'); messageText.className = 'message-text'; messageText.textContent = text; content.appendChild(header); content.appendChild(messageText); messageDiv.appendChild(avatar); messageDiv.appendChild(content); this.elements.messagesContainer.appendChild(messageDiv); this.scrollToBottom(); return messageDiv; } addTypingIndicator() { const messageDiv = document.createElement('div'); messageDiv.className = 'message assistant-message'; const avatar = document.createElement('div'); avatar.className = 'message-avatar assistant-avatar'; avatar.innerHTML = ` `; const content = document.createElement('div'); content.className = 'message-content'; const header = document.createElement('div'); header.className = 'message-header'; header.textContent = 'AI Assistant'; const typingDiv = document.createElement('div'); typingDiv.className = 'message-text typing-indicator'; typingDiv.innerHTML = `
`; content.appendChild(header); content.appendChild(typingDiv); messageDiv.appendChild(avatar); messageDiv.appendChild(content); this.elements.messagesContainer.appendChild(messageDiv); this.scrollToBottom(); return messageDiv; } scrollToBottom() { this.elements.messagesContainer.scrollTop = this.elements.messagesContainer.scrollHeight; } } // Initialize the app when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => new ChatApp()); } else { new ChatApp(); } --- I've created a beautiful, production-ready Apple-style chatbot application with the following features: ## ✨ Key Features: 1. **Apple-Inspired Design** - Clean, minimalist interface with SF Pro-like typography - Smooth animations and transitions - Glassmorphism effects in the header - Gradient accents and modern styling 2. **Advanced Model Loading** - Progress bar with real-time download status - WebGPU support for better performance - Using Llama-3.2-1B-Instruct with q4f16 quantization - Detailed loading states 3. **Streaming Responses** - Real-time text streaming as the model generates - Typing indicators while processing - Smooth text appearance 4. **User Experience** - Auto-resizing textarea - Enter to send, Shift+Enter for new lines - Disabled states during generation - Smooth scrolling to latest messages - Mobile-responsive design 5. **Conversation Management** - Maintains full conversation history - System prompt for consistent behavior - Context-aware responses 6. **Performance Optimizations** - CDN-based module loading - Efficient DOM updates - Quantized model for faster loading - WebGPU acceleration The application is fully functional and ready to deploy. Just save all three files in the same directory and open `index.html` in a modern browser (Chrome/Edge recommended for WebGPU support).