localm / attempts /chat4.html
mihailik's picture
Moving to a proper app.
811916b
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>AI Chat App</title>
<style>
body {
margin: 0;
font-family: sans-serif;
display: flex;
flex-direction: column;
height: 100vh;
background: #f5f5f5;
}
header {
padding: 10px;
background: #333;
color: white;
text-align: center;
}
#model-select {
width: 100%;
padding: 10px;
font-size: 1em;
border: none;
outline: none;
}
#chat {
flex: 1;
overflow-y: auto;
padding: 10px;
background: white;
display: flex;
flex-direction: column;
}
.message {
margin: 10px 0;
padding: 10px;
border-radius: 5px;
max-width: 80%;
word-wrap: break-word;
}
.user {
background: #d1e7dd;
align-self: flex-end;
}
.bot {
background: #f8d7da;
align-self: flex-start;
}
#input-area {
display: flex;
padding: 10px;
background: #eee;
}
#user-input {
flex: 1;
padding: 10px;
font-size: 1em;
border: 1px solid #ccc;
border-radius: 5px;
}
#send-btn {
padding: 10px 20px;
margin-left: 10px;
font-size: 1em;
border: none;
background: #333;
color: white;
border-radius: 5px;
cursor: pointer;
}
#send-btn:disabled {
background: #999;
cursor: not-allowed;
}
</style>
</head>
<body>
<header>
<h1>AI Chat App</h1>
<select id="model-select">
<option value="Xenova/distilgpt2">Xenova/distilgpt2</option>
<option value="Xenova/phi-3-mini-4k-instruct">Xenova/phi-3-mini-4k-instruct</option>
<option value="Xenova/t5-small">Xenova/t5-small</option>
<option value="Xenova/gemma-2b-it">Xenova/gemma-2b-it</option>
<option value="Xenova/llama-3-8b-instruct">Xenova/llama-3-8b-instruct</option>
<option value="Xenova/Mistral-7B-Instruct-v0.2">Xenova/Mistral-7B-Instruct-v0.2</option>
</select>
</header>
<div id="chat"></div>
<div id="input-area">
<input type="text" id="user-input" placeholder="Type your message..." />
<button id="send-btn">Send</button>
</div>
<script type="module">
const chat = document.getElementById('chat');
const input = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const modelSelect = document.getElementById('model-select');
let busy = false;
let worker;
function appendMessage(text, sender) {
const div = document.createElement('div');
div.className = `message ${sender}`;
div.textContent = text;
chat.appendChild(div);
chat.scrollTop = chat.scrollHeight;
}
function setBusy(state) {
busy = state;
sendBtn.disabled = state;
input.disabled = state;
}
function createWorker(modelName) {
if (worker) worker.terminate();
const workerCode = `
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@3.7.1/dist/transformers.min.js';
let generator = null;
self.onmessage = async (e) => {
const { type, message } = e.data;
try {
if (type === 'load') {
self.postMessage({ type: 'status', message: 'Loading model: ' + message });
generator = await pipeline('text-generation', message);
self.postMessage({ type: 'ready' });
} else if (type === 'generate') {
if (!generator) throw new Error('Model not loaded');
const output = await generator(message, { max_new_tokens: 100 });
self.postMessage({ type: 'response', message: output[0].generated_text });
}
} catch (err) {
self.postMessage({ type: 'error', message: err.stack });
}
};
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
worker = new Worker(URL.createObjectURL(blob), { type: 'module' });
worker.onmessage = (e) => {
const { type, message } = e.data;
if (type === 'status') {
appendMessage(message, 'bot');
} else if (type === 'ready') {
appendMessage('Model loaded and ready!', 'bot');
setBusy(false);
} else if (type === 'response') {
appendMessage(message, 'bot');
setBusy(false);
} else if (type === 'error') {
appendMessage('Error: ' + message, 'bot');
setBusy(false);
}
};
setBusy(true);
worker.postMessage({ type: 'load', message: modelName });
}
modelSelect.addEventListener('change', () => {
const selected = modelSelect.value;
createWorker(selected);
});
sendBtn.addEventListener('click', () => {
const text = input.value.trim();
if (!text || busy) return;
appendMessage(text, 'user');
input.value = '';
setBusy(true);
worker.postMessage({ type: 'generate', message: text });
});
// Initial load
createWorker(modelSelect.value);
</script>
</body>
</html>