// === Qwen App Builder Logic ===
document.addEventListener('DOMContentLoaded', () => {
const chatMessages = document.getElementById('chat-messages');
const promptInput = document.getElementById('prompt-input');
const sendBtn = document.getElementById('send-btn');
const attachBtn = document.getElementById('attach-btn');
const fileInput = document.getElementById('file-input');
const imagePreview = document.getElementById('image-preview');
const previewImg = document.getElementById('preview-img');
const previewName = document.getElementById('preview-name');
const removeImageBtn = document.getElementById('remove-image');
const apiKeyInput = document.getElementById('api-key');
const saveKeyBtn = document.getElementById('save-key');
const modelSelect = document.getElementById('model-select');
const clearChatBtn = document.getElementById('clear-chat');
let attachedImage = null; // { base64, name }
let isStreaming = false;
// Load saved API key
const savedKey = localStorage.getItem('hf_token');
if (savedKey) apiKeyInput.value = savedKey;
const savedModel = localStorage.getItem('hf_model');
if (savedModel) modelSelect.value = savedModel;
// Save API key
saveKeyBtn.addEventListener('click', () => {
localStorage.setItem('hf_token', apiKeyInput.value.trim());
localStorage.setItem('hf_model', modelSelect.value);
saveKeyBtn.textContent = 'Saved!';
setTimeout(() => { saveKeyBtn.textContent = 'Save'; }, 1500);
});
// Auto-resize textarea
promptInput.addEventListener('input', () => {
promptInput.style.height = 'auto';
promptInput.style.height = Math.min(promptInput.scrollHeight, 128) + 'px';
});
// Enter to send
promptInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
sendBtn.addEventListener('click', sendMessage);
// Image attachment
attachBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
if (file.size > 4 * 1024 * 1024) {
alert('Image too large. Max 4MB for base64 encoding.');
return;
}
const reader = new FileReader();
reader.onload = (event) => {
attachedImage = { base64: event.target.result, name: file.name };
previewImg.src = event.target.result;
previewName.textContent = file.name;
imagePreview.classList.remove('hidden');
feather.replace();
};
reader.readAsDataURL(file);
});
removeImageBtn.addEventListener('click', () => {
attachedImage = null;
imagePreview.classList.add('hidden');
fileInput.value = '';
});
// Clear chat
clearChatBtn.addEventListener('click', () => {
chatMessages.innerHTML = '';
addMessage('bot', '👋 Chat cleared. What app shall we build next?');
});
// Add message to chat
function addMessage(role, content, isCode = false) {
const wrapper = document.createElement('div');
wrapper.className = `flex ${role === 'user' ? 'justify-end' : 'justify-start'}`;
const bubble = document.createElement('div');
bubble.className = role === 'user'
? 'bg-undefined-500 text-white rounded-2xl rounded-tr-sm px-4 py-3 max-w-[85%]'
: 'bg-gray-100 dark:bg-gray-700 rounded-2xl rounded-tl-sm px-4 py-3 max-w-[85%]';
if (role === 'bot') {
const header = document.createElement('div');
header.className = 'flex items-center gap-2 mb-1 text-xs text-gray-500';
header.innerHTML = ' Qwen';
bubble.appendChild(header);
}
const textDiv = document.createElement('div');
textDiv.className = 'text-sm whitespace-pre-wrap break-words';
textDiv.textContent = content;
bubble.appendChild(textDiv);
wrapper.appendChild(bubble);
chatMessages.appendChild(wrapper);
chatMessages.scrollTop = chatMessages.scrollHeight;
feather.replace();
return textDiv;
}
// Add streaming message (returns the text element to update)
function addStreamingMessage() {
const wrapper = document.createElement('div');
wrapper.className = 'flex justify-start';
const bubble = document.createElement('div');
bubble.className = 'bg-gray-100 dark:bg-gray-700 rounded-2xl rounded-tl-sm px-4 py-3 max-w-[85%]';
const header = document.createElement('div');
header.className = 'flex items-center gap-2 mb-1 text-xs text-gray-500';
header.innerHTML = ' Qwen ●';
bubble.appendChild(header);
const textDiv = document.createElement('div');
textDiv.className = 'text-sm whitespace-pre-wrap break-words';
bubble.appendChild(textDiv);
wrapper.appendChild(bubble);
chatMessages.appendChild(wrapper);
chatMessages.scrollTop = chatMessages.scrollHeight;
feather.replace();
return textDiv;
}
// Format code blocks in text
function formatContent(element, text) {
element.innerHTML = '';
const codeRegex = /