=== index.html ===
AI Chat Interface
=== index.js ===
// index.js content here
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.7.2';
class Chatbot {
constructor() {
this.messagesContainer = document.getElementById('messages');
this.userInput = document.getElementById('user-input');
this.chatForm = document.getElementById('chat-form');
this.sendBtn = document.getElementById('send-btn');
this.loading = document.getElementById('loading');
this.deviceSelect = document.getElementById('device-select');
this.webgpuOption = document.getElementById('webgpu-option');
this.pipe = null;
this.currentDevice = 'cpu';
this.conversationHistory = ''; // Simple history for prompt
this.init();
}
async init() {
const supportsWebGPU = !!navigator.gpu;
if (!supportsWebGPU) {
this.webgpuOption.disabled = true;
this.webgpuOption.title = 'WebGPU not supported in this browser';
}
this.deviceSelect.addEventListener('change', (e) => {
this.currentDevice = e.target.value;
this.initializePipeline();
});
this.chatForm.addEventListener('submit', (e) => this.handleSubmit(e));
this.userInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.handleSubmit(e);
}
});
// Auto-resize textarea
this.userInput.addEventListener('input', () => {
this.userInput.style.height = 'auto';
this.userInput.style.height = Math.min(this.userInput.scrollHeight, 120) + 'px';
});
await this.initializePipeline();
this.addMessage('bot', 'Hello! I am an AI chatbot powered by transformers.js. How can I help you today?');
}
async initializePipeline() {
try {
this.pipe = await pipeline('text-generation', 'distilgpt2', {
device: this.currentDevice === 'webgpu' ? 'webgpu' : undefined
});
console.log(`Pipeline initialized on ${this.currentDevice}`);
} catch (error) {
console.error('Failed to initialize pipeline:', error);
this.addMessage('bot', 'Error: Failed to load the AI model. Please try refreshing the page.');
if (this.currentDevice === 'webgpu') {
this.currentDevice = 'cpu';
this.deviceSelect.value = 'cpu';
await this.initializePipeline();
}
}
}
async handleSubmit(e) {
e.preventDefault();
const message = this.userInput.value.trim();
if (!message || !this.pipe) return;
this.userInput.value = '';
this.userInput.style.height = 'auto';
this.addMessage('user', message);
this.showLoading(true);
try {
// Build prompt with simple history
const prompt = this.conversationHistory ? `${this.conversationHistory}\nUser: ${message}\nAssistant:` : `User: ${message}\nAssistant:`;
const response = await this.pipe(prompt, {
max_new_tokens: 50,
temperature: 0.7,
do_sample: true,
pad_token_id: 50256 // For distilgpt2
});
const botMessage = response[0].generated_text.split('Assistant:').pop().trim();
this.conversationHistory = `${prompt} ${botMessage}`;
this.addMessage('bot', botMessage);
} catch (error) {
console.error('Error generating response:', error);
this.addMessage('bot', 'Sorry, I encountered an error while generating a response.');
} finally {
this.showLoading(false);
}
}
addMessage(sender, text) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
messageDiv.setAttribute('role', 'log');
messageDiv.setAttribute('aria-label', `${sender === 'user' ? 'You' : 'Assistant'}: ${text}`);
messageDiv.textContent = text;
this.messagesContainer.appendChild(messageDiv);
this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
}
showLoading(show) {
this.loading.style.display = show ? 'block' : 'none';
if (show) {
this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
}
}
}
// Initialize the chatbot when the DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new Chatbot();
});
=== style.css ===
/* style.css content here */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #f5f5f5;
color: #333;
line-height: 1.6;
}
.app-container {
display: flex;
flex-direction: column;
height: 100vh;
max-width: 800px;
margin: 0 auto;
background: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background-color: #007bff;
color: white;
}
.header h1 {
font-size: 1.5rem;
}
.device-selector {
display: flex;
align-items: center;
gap: 0.5rem;
}
.device-selector label {
font-size: 0.9rem;
}
.device-selector select {
padding: 0.25rem;
border: none;
border-radius: 4px;
background: white;
color: #333;
}
.chat-container {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
overflow: hidden;
}
.messages {
flex: 1;
overflow-y: auto;
padding: 1rem 0;
display: flex;
flex-direction: column;
gap: 1rem;
}
.message {
max-width: 70%;
padding: 0.75rem 1rem;
border-radius: 18px;
word-wrap: break-word;
animation: fadeIn 0.3s ease-in;
}
.message.user {
align-self: flex-end;
background-color: #007bff;
color: white;
}
.message.bot {
align-self: flex-start;
background-color: #e9ecef;
color: #333;
}
.loading-indicator {
display: none;
align-self: flex-start;
padding: 0.75rem 1rem;
background-color: #e9ecef;
border-radius: 18px;
font-style: italic;
color: #666;
}
.input-container {
padding: 1rem;
border-top: 1px solid #ddd;
background: white;
}
#chat-form {
display: flex;
gap: 0.5rem;
}
#user-input {
flex: 1;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 20px;
resize: none;
font-size: 1rem;
outline: none;
transition: border-color 0.3s;
}
#user-input:focus {
border-color: #007bff;
}
#send-btn {
padding: 0.75rem 1.5rem;
background-color: #007bff;
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s;
}
#send-btn:hover:not(:disabled) {
background-color: #0056b3;
}
#send-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Responsive design */
@media (max-width: 600px) {
.header {
flex-direction: column;
gap: 0.5rem;
text-align: center;
}
.device-selector {
justify-content: center;
}
.message {
max-width: 85%;
}
.input-container {
padding: 0.5rem;
}
#chat-form {
flex-direction: column;
}
#send-btn {
align-self: stretch;
}
}
/* Accessibility improvements */
@media (prefers-reduced-motion: reduce) {
.message {
animation: none;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: #121212;
color: #e0e0e0;
}
.app-container {
background: #1e1e1e;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.1);
}
.header {
background-color: #0d6efd;
}
.message.bot {
background-color: #333;
color: #e0e0e0;
}
.loading-indicator {
background-color: #333;
color: #b0b0b0;
}
#user-input {
background-color: #333;
color: #e0e0e0;
border-color: #555;
}
#user-input:focus {
border-color: #0d6efd;
}
}