document.addEventListener('DOMContentLoaded', () => { const chatWindow = document.getElementById('chat-window'); const chatInput = document.getElementById('chat-input'); const sendBtn = document.getElementById('send-btn'); const stopBtn = document.getElementById('stop-btn'); const modeItems = document.querySelectorAll('.mode-item'); const currentModeBadge = document.getElementById('current-mode-badge'); const newChatBtn = document.getElementById('new-chat-btn'); const chatHistoryList = document.getElementById('chat-history'); // Settings elements const creativityRange = document.getElementById('creativity-range'); const creativityVal = document.getElementById('creativity-val'); const limitsToggle = document.getElementById('limits-toggle'); const modelSelect = document.getElementById('model-select'); const themeSwatches = document.querySelectorAll('.theme-swatch'); const saveSettingsBtn = document.getElementById('save-settings-btn'); // Share elements const shareModal = new bootstrap.Modal(document.getElementById('shareModal')); const shareUrlInput = document.getElementById('share-url-input'); const copyShareBtn = document.getElementById('copy-share-btn'); const viewSharedLink = document.getElementById('view-shared-link'); let currentMode = 'regular'; let currentChatId = null; let abortController = null; // --- Sidebar & Mode Switching --- modeItems.forEach(item => { item.addEventListener('click', () => { if (item.classList.contains('active')) return; modeItems.forEach(i => i.classList.remove('active')); item.classList.add('active'); currentMode = item.dataset.mode; currentModeBadge.textContent = item.querySelector('span').textContent; startNewChat(); }); }); function startNewChat() { currentChatId = null; chatWindow.innerHTML = "`

New ${currentMode} Chat

How can I help you in this mode?

`"; document.querySelectorAll('.history-item').forEach(i => i.classList.remove('active')); } newChatBtn.addEventListener('click', startNewChat); // --- History Loading --- async function loadHistory() { try { const resp = await fetch('api/history.php?action=list'); const data = await resp.json(); if (data.success) { renderHistory(data.chats); } } catch (e) { console.error('Failed to load history:', e); } } function renderHistory(chats) { if (!chats || chats.length === 0) { chatHistoryList.innerHTML = '
No recent chats
'; return; } chatHistoryList.innerHTML = chats.map(chat => `
${escapeHtml(chat.title)}
`).join(''); chatHistoryList.querySelectorAll('.history-item').forEach(item => { item.addEventListener('click', (e) => { if (e.target.classList.contains('delete-chat')) { deleteChat(e.target.dataset.id); return; } loadChat(item.dataset.id); }); }); } function getModeIcon(mode) { switch(mode) { case 'coding': return 'code-slash'; case 'game': return 'controller'; case 'app': return 'window'; default: return 'chat-left-dots'; } } async function loadChat(chatId) { if (currentChatId == chatId) return; chatWindow.innerHTML = '
'; try { const resp = await fetch(`api/history.php?action=messages&chat_id=${chatId}`); const data = await resp.json(); if (data.success) { currentChatId = chatId; currentMode = data.mode; modeItems.forEach(i => { if (i.dataset.mode === currentMode) i.classList.add('active'); else i.classList.remove('active'); }); const activeModeItem = Array.from(modeItems).find(i => i.dataset.mode === currentMode); if (activeModeItem) { currentModeBadge.textContent = activeModeItem.querySelector('span').textContent; } chatWindow.innerHTML = ''; data.messages.forEach(msg => { appendMessage(msg.role, msg.content, false); if ((currentMode === 'game' || currentMode === 'app') && msg.role === 'assistant') { addActionButtons(msg.content); } }); document.querySelectorAll('.history-item').forEach(i => { if (i.dataset.id == chatId) i.classList.add('active'); else i.classList.remove('active'); }); chatWindow.scrollTop = chatWindow.scrollHeight; } } catch (e) { console.error(e); chatWindow.innerHTML = '
Failed to load chat
'; } } async function deleteChat(chatId) { if (!confirm('Are you sure you want to delete this chat?')) return; try { const resp = await fetch(`api/history.php?action=delete&chat_id=${chatId}`); const data = await resp.json(); if (data.success) { if (currentChatId == chatId) startNewChat(); loadHistory(); } } catch (e) { console.error(e); } } // --- Chat Logic --- async function sendMessage() { const message = chatInput.value.trim(); if (!message) return; const isNewChat = !currentChatId; chatInput.value = ''; chatInput.style.height = 'auto'; toggleLoading(true); appendMessage('user', message); abortController = new AbortController(); try { const response = await fetch('api/chat.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message, mode: currentMode, chat_id: currentChatId, model: modelSelect.value, creativity: creativityRange.value, limits_off: limitsToggle.checked ? 1 : 0 }), signal: abortController.signal }); const data = await response.json(); if (data.success) { currentChatId = data.chat_id; appendMessage('assistant', data.message); if (currentMode === 'game' || currentMode === 'app') { addActionButtons(data.message); } if (isNewChat) { loadHistory(); } } else { appendMessage('assistant', 'Error: ' + (data.error || 'Unknown error')); } } catch (error) { if (error.name === 'AbortError') { appendMessage('assistant', ' Generation stopped by user.'); } else { appendMessage('assistant', 'Error: ' + error.message); } } finally { toggleLoading(false); abortController = null; } } function appendMessage(role, text, animate = true) { if (role === 'system') return; const emptyState = chatWindow.querySelector('.my-auto'); if (emptyState) emptyState.remove(); const msgDiv = document.createElement('div'); msgDiv.className = `message message-${role} ${animate ? 'animate-fade-in' : ''}`; msgDiv.innerHTML = formatText(text); chatWindow.appendChild(msgDiv); chatWindow.scrollTop = chatWindow.scrollHeight; msgDiv.querySelectorAll('.copy-btn').forEach(btn => { btn.addEventListener('click', () => { const code = btn.closest('.code-block-wrapper').querySelector('code').textContent; navigator.clipboard.writeText(code).then(() => { const original = btn.innerHTML; btn.innerHTML = ' Copied!'; setTimeout(() => btn.innerHTML = original, 2000); }); }); }); } function formatText(text) { if (!text) return ''; const codeBlocks = []; let formatted = text.replace(/```(\w+)?\s*([\s\S]*?)```/g, (match, lang, code) => { const id = `CODE_BLOCK_${codeBlocks.length}`; codeBlocks.push({ id, lang: lang || 'code', code: code.trim() }); return id; }); formatted = escapeHtml(formatted).replace(/\n/g, '
'); codeBlocks.forEach(block => { const html = `
${block.lang} Copy
${escapeHtml(block.code)}
`; formatted = formatted.replace(block.id, html); }); return formatted; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function addActionButtons(content) { const match = content.match(/```(?:html|xml)?\s*([\s\S]*?)```/i); let codeToLaunch = match ? match[1] : content; const hasHtmlTags = / { let fullCode = codeToLaunch; if (!fullCode.toLowerCase().includes('AI Generated App${codeToLaunch}`; } const blob = new Blob([fullCode], { type: 'text/html' }); const url = URL.createObjectURL(blob); window.open(url, '_blank'); }; // Download Button const downloadBtn = document.createElement('button'); downloadBtn.className = 'btn btn-sm btn-outline-primary d-inline-flex align-items-center gap-2 shadow-sm'; downloadBtn.innerHTML = ' Download'; downloadBtn.onclick = () => { let fullCode = codeToLaunch; if (!fullCode.toLowerCase().includes('AI Generated App${codeToLaunch}`; } const blob = new Blob([fullCode], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `ai-app-${Date.now()}.html`; a.click(); URL.revokeObjectURL(url); }; // Share Button const shareBtn = document.createElement('button'); shareBtn.className = 'btn btn-sm btn-outline-accent d-inline-flex align-items-center gap-2 shadow-sm'; shareBtn.innerHTML = ' Share'; shareBtn.onclick = async () => { shareBtn.disabled = true; shareBtn.innerHTML = ' Sharing...'; try { const resp = await fetch('api/share.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: codeToLaunch }) }); const data = await resp.json(); if (data.success) { shareUrlInput.value = data.url; viewSharedLink.href = data.url; shareModal.show(); } else { showToast('Failed to share: ' + (data.error || 'Unknown error'), 'danger'); } } catch (e) { console.error(e); showToast('Error sharing application', 'danger'); } finally { shareBtn.disabled = false; shareBtn.innerHTML = ' Share'; } }; btnContainer.appendChild(launchBtn); btnContainer.appendChild(downloadBtn); btnContainer.appendChild(shareBtn); chatWindow.lastElementChild.appendChild(btnContainer); } } function toggleLoading(isLoading) { sendBtn.disabled = isLoading; chatInput.disabled = isLoading; if (isLoading) { sendBtn.innerHTML = ''; stopBtn.style.display = 'flex'; } else { sendBtn.innerHTML = ''; stopBtn.style.display = 'none'; chatInput.focus(); } } stopBtn.addEventListener('click', () => { if (abortController) { abortController.abort(); } }); sendBtn.addEventListener('click', sendMessage); chatInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); chatInput.addEventListener('input', () => { chatInput.style.height = 'auto'; chatInput.style.height = (chatInput.scrollHeight) + 'px'; }); // --- Settings & Themes --- creativityRange.addEventListener('input', () => { creativityVal.textContent = creativityRange.value; }); themeSwatches.forEach(swatch => { swatch.addEventListener('click', () => { const theme = swatch.dataset.theme; document.documentElement.setAttribute('data-theme', theme); themeSwatches.forEach(s => s.classList.remove('active')); swatch.classList.add('active'); }); }); saveSettingsBtn.addEventListener('click', async () => { const theme = document.documentElement.getAttribute('data-theme'); const settings = { theme: theme, creativity: creativityRange.value, limits_off: limitsToggle.checked ? '1' : '0', model: modelSelect.value }; saveSettingsBtn.disabled = true; saveSettingsBtn.innerHTML = ' Saving...'; try { const resp = await fetch('api/settings.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(settings) }); const data = await resp.json(); if (data.success) { const modal = bootstrap.Modal.getInstance(document.getElementById('settingsModal')); if (modal) modal.hide(); showToast('Settings saved successfully!'); } } catch (e) { console.error(e); showToast('Failed to save settings', 'danger'); } finally { saveSettingsBtn.disabled = false; saveSettingsBtn.textContent = 'Save changes'; } }); copyShareBtn.addEventListener('click', () => { shareUrlInput.select(); document.execCommand('copy'); const original = copyShareBtn.innerHTML; copyShareBtn.innerHTML = ' Copied!'; setTimeout(() => copyShareBtn.innerHTML = original, 2000); }); function showToast(message, type = 'success') { const toast = document.createElement('div'); toast.className = `position-fixed bottom-0 start-50 translate-middle-x mb-4 bg-${type} text-white px-4 py-2 rounded-pill shadow-lg animate-fade-in`; toast.style.zIndex = '2050'; toast.innerHTML = ` ${message}`; document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = '0'; toast.style.transition = 'opacity 0.5s ease'; setTimeout(() => toast.remove(), 500); }, 3000); } const currentTheme = document.documentElement.getAttribute('data-theme'); const activeSwatch = document.querySelector(`.theme-swatch[data-theme="${currentTheme}"]`); if (activeSwatch) activeSwatch.classList.add('active'); loadHistory(); });