anycoder-163b0295 / index.js
akhaliq's picture
akhaliq HF Staff
Upload index.js with huggingface_hub
54a96bb verified
// Main application logic for Apple Style Chatbot
class AppleChatbot {
constructor() {
this.generator = null;
this.isGenerating = false;
this.messages = [];
this.settings = {
maxTokens: 512,
temperature: 0.7,
streaming: true
};
this.init();
}
async init() {
// Initialize UI elements
this.loadingScreen = document.getElementById('loadingScreen');
this.chatContainer = document.getElementById('chatContainer');
this.inputArea = document.getElementById('inputArea');
this.messagesList = document.getElementById('messagesList');
this.messageInput = document.getElementById('messageInput');
this.sendBtn = document.getElementById('sendBtn');
this.clearBtn = document.getElementById('clearBtn');
this.settingsBtn = document.getElementById('settingsBtn');
this.progressFill = document.getElementById('progressFill');
this.loadingStatus = document.getElementById('loadingStatus');
this.charCount = document.getElementById('charCount');
// Setup event listeners
this.setupEventListeners();
// Load model using Web Worker
await this.loadModel();
}
setupEventListeners() {
// Send button
this.sendBtn.addEventListener('click', () => this.sendMessage());
// Enter key to send (Shift+Enter for new line)
this.messageInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.sendMessage();
}
});
// Character count
this.messageInput.addEventListener('input', (e) => {
const length = e.target.value.length;
this.charCount.textContent = `${length} / 1000`;
this.sendBtn.disabled = length === 0 || this.isGenerating;
// Auto-resize textarea
e.target.style.height = 'auto';
e.target.style.height = Math.min(e.target.scrollHeight, 120) + 'px';
});
// Clear button
this.clearBtn.addEventListener('click', () => this.clearChat());
// Settings
this.settingsBtn.addEventListener('click', () => this.openSettings());
document.getElementById('closeSettings').addEventListener('click', () => this.closeSettings());
document.getElementById('saveSettings').addEventListener('click', () => this.saveSettings());
// Settings inputs
document.getElementById('maxTokens').addEventListener('input', (e) => {
document.getElementById('maxTokensValue').textContent = e.target.value;
});
document.getElementById('temperature').addEventListener('input', (e) => {
document.getElementById('temperatureValue').textContent = e.target.value;
});
// Close modal on backdrop click
document.getElementById('settingsModal').addEventListener('click', (e) => {
if (e.target.id === 'settingsModal') {
this.closeSettings();
}
});
}
async loadModel() {
try {
// Create Web Worker for model loading
const worker = new Worker('worker.js');
worker.onmessage = async (e) => {
const { type, data } = e.data;
switch (type) {
case 'progress':
this.updateProgress(data.progress, data.status);
break;
case 'model_loaded':
// Model is loaded, initialize the generator
await this.initializeGenerator();
break;
}
};
// Start loading the model
worker.postMessage({ type: 'load_model' });
} catch (error) {
console.error('Error loading model:', error);
this.showError('Failed to load AI model. Please refresh the page and try again.');
}
}
updateProgress(progress, status) {
this.progressFill.style.width = `${progress}%`;
this.loadingStatus.textContent = status;
}
async initializeGenerator() {
try {
// Initialize the text generation pipeline
this.generator = await pipeline(
'text-generation',
'onnx-community/gemma-3-270m-it-ONNX',
{ dtype: 'fp32' }
);
// Hide loading screen and show chat
this.loadingScreen.style.display = 'none';
this.chatContainer.style.display = 'block';
this.inputArea.style.display = 'block';
// Focus on input
this.messageInput.focus();
} catch (error) {
console.error('Error initializing generator:', error);
this.showError('Failed to initialize AI model. Please refresh the page and try again.');
}
}
async sendMessage() {
const text = this.messageInput.value.trim();
if (!text || this.isGenerating || !this.generator) return;
// Add user message
this.addMessage('user', text);
this.messages.push({ role: 'user', content: text });
// Clear input
this.messageInput.value = '';
this.charCount.textContent = '0 / 1000';
this.sendBtn.disabled = true;
this.messageInput.style.height = 'auto';
// Show typing indicator
const typingMessage = this.showTypingIndicator();
try {
this.isGenerating = true;
// Generate response
const response = await this.generateResponse();
// Remove typing indicator
typingMessage.remove();
// Add assistant response
this.addMessage('assistant', response);
this.messages.push({ role: 'assistant', content: response });
} catch (error) {
console.error('Error generating response:', error);
typingMessage.remove();
this.addMessage('assistant', 'Sorry, I encountered an error while generating a response. Please try again.');
} finally {
this.isGenerating = false;
this.sendBtn.disabled = false;
this.messageInput.focus();
}
}
async generateResponse() {
if (!this.generator) throw new Error('Generator not initialized');
const generationParams = {
max_new_tokens: this.settings.maxTokens,
do_sample: this.settings.temperature > 0,
temperature: this.settings.temperature,
};
// Add streamer if streaming is enabled
if (this.settings.streaming) {
generationParams.streamer = new TextStreamer(this.generator.tokenizer, {
skip_prompt: true,
skip_special_tokens: true,
});
}
// Generate response
const output = await this.generator(this.messages, generationParams);
// Extract the assistant's response
const lastMessage = output[0].generated_text.at(-1);
return lastMessage ? lastMessage.content : 'I apologize, but I couldn\'t generate a response.';
}
addMessage(role, content) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${role}`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.innerHTML = role === 'user'
? '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>'
: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7V17L12 22L22 17V7L12 2Z" />
</svg>';
const messageContent = document.createElement('div');
messageContent.className = 'message-content';
const messageText = document.createElement('div');
messageText.className = 'message-text';
messageText.textContent = content;
const messageTime = document.createElement('div');
messageTime.className = 'message-time';
messageTime.textContent = this.getCurrentTime();
messageContent.appendChild(messageText);
messageContent.appendChild(messageTime);
messageDiv.appendChild(avatar);
messageDiv.appendChild(messageContent);
this.messagesList.appendChild(messageDiv);
// Scroll to bottom
this.chatContainer.scrollTop = this.chatContainer.scrollHeight;
}
showTypingIndicator() {
const messageDiv = document.createElement('div');
messageDiv.className = 'message assistant';
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7V17L12 22L22 17V7L12 2Z" />
</svg>';
const messageContent = document.createElement('div');
messageContent.className = 'message-content';
const typingIndicator = document.createElement('div');
typingIndicator.className = 'message-text typing-indicator';
typingIndicator.innerHTML = `
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
`;
const messageTime = document.createElement('div');
messageTime.className = 'message-time';
messageTime.textContent = 'Typing...';
messageContent.appendChild(typingIndicator);
messageContent.appendChild(messageTime);
messageDiv.appendChild(avatar);
messageDiv.appendChild(messageContent);
this.messagesList.appendChild(messageDiv);
this.chatContainer.scrollTop = this.chatContainer.scrollHeight;
return messageDiv;
}
getCurrentTime() {
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}
clearChat() {
if (confirm('Are you sure you want to clear all messages?')) {
// Keep only the welcome message
this.messagesList.innerHTML = `
<div class="message assistant">
<div class="message-avatar">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7V17L12 22L22 17V7L12 2Z" />
</svg>
</div>
<div class="message-content">
<div class="message-text">
Hello! I'm your AI assistant powered by Gemma-3. How can I help you today?
</div>
<div class="message-time">${this.getCurrentTime()}</div>
</div>
</div>
`;
this.messages = [];
this.messageInput.focus();
}
}
openSettings() {
document.getElementById('settingsModal').classList.add('active');
document.getElementById('maxTokens').value = this.settings.maxTokens;
document.getElementById('maxTokensValue').textContent = this.settings.maxTokens;
document.getElementById('temperature').value = this.settings.temperature;
document.getElementById('temperatureValue').textContent = this.settings.temperature;
document.getElementById('streaming').checked = this.settings.streaming;
}
closeSettings() {
document.getElementById('settingsModal').classList.remove('active');
}
saveSettings() {
this.settings.maxTokens = parseInt(document.getElementById('maxTokens').value);
this.settings.temperature = parseFloat(document.getElementById('temperature').value);
this.settings.streaming = document.getElementById('streaming').checked;
this.closeSettings();
}
showError(message) {
this.loadingStatus.textContent = message;
this.loadingStatus.style.color = 'var(--error)';
}
}
// Import transformers.js classes
const { pipeline, TextStreamer } = window.transformers;
// Initialize the chatbot when the page loads
document.addEventListener('DOMContentLoaded', () => {
new AppleChatbot();
});