File size: 5,503 Bytes
07ea14f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// === 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 = '<i data-feather="cpu" class="w-3 h-3"></i> 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 = '<i data-feather="cpu" class="w-3 h-3"></i> Qwen <span class="animate-pulse">●</span>';
        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 = /