// 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 = `
`;
}
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 = `
`;
} 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();
});