document.addEventListener('DOMContentLoaded', () => { const chatWindow = document.getElementById('chat-window'); const chatInput = document.getElementById('chat-input'); const sendBtn = document.getElementById('send-btn'); const modeItems = document.querySelectorAll('.mode-item'); const currentModeBadge = document.getElementById('current-mode-badge'); const newChatBtn = document.getElementById('new-chat-btn'); // Settings elements const creativityRange = document.getElementById('creativity-range'); const creativityVal = document.getElementById('creativity-val'); const limitsToggle = document.getElementById('limits-toggle'); const themeSwatches = document.querySelectorAll('.theme-swatch'); const saveSettingsBtn = document.getElementById('save-settings-btn'); let currentMode = 'regular'; let currentChatId = 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?

`; } newChatBtn.addEventListener('click', startNewChat); // --- Chat Logic --- async function sendMessage() { const message = chatInput.value.trim(); if (!message) return; // Clear input and disable chatInput.value = ''; chatInput.style.height = 'auto'; toggleLoading(true); // Append User Message appendMessage('user', message); 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, creativity: creativityRange.value, limits_off: limitsToggle.checked ? 1 : 0 }) }); const data = await response.json(); if (data.success) { currentChatId = data.chat_id; appendMessage('assistant', data.message); // Special handling for game/app mode if (currentMode === 'game' || currentMode === 'app') { addLaunchButton(data.message); } } else { appendMessage('assistant', 'Error: ' + (data.error || 'Unknown error')); } } catch (error) { appendMessage('assistant', 'Error: ' + error.message); } finally { toggleLoading(false); } } function appendMessage(role, text) { // Remove empty state if present const emptyState = chatWindow.querySelector('.my-auto'); if (emptyState) emptyState.remove(); const msgDiv = document.createElement('div'); msgDiv.className = `message message-${role} animate-fade-in`; // Use marked.js or simple formatting msgDiv.innerHTML = formatText(text); chatWindow.appendChild(msgDiv); chatWindow.scrollTop = chatWindow.scrollHeight; } function formatText(text) { // Handle code blocks with more flexibility let formatted = text; // Escape HTML for non-code parts // This is tricky without a library, let's just do code blocks first // Code blocks: ```[lang]\n[code]``` formatted = formatted.replace(/```(\w+)?\s*([\s\S]*?)```/g, (match, lang, code) => { return `
${lang || 'code'} Copy
${escapeHtml(code.trim())}
`; }); // Simple line breaks for non-code parts // (Only for parts outside of the generated HTML above) // This is a bit naive but works for simple chat if (!formatted.includes('
'); } return formatted; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function addLaunchButton(content) { // Find code block content const match = content.match(/```(?:html|xml)?\s*([\s\S]*?)```/i); let codeToLaunch = match ? match[1] : content; // Only add button if it looks like a full HTML document or contains significant HTML tags const hasHtmlTags = / { // If it's not a full HTML, wrap it if (!codeToLaunch.toLowerCase().includes('AI Generated App${codeToLaunch}`; } const blob = new Blob([codeToLaunch], { type: 'text/html' }); const url = URL.createObjectURL(blob); window.open(url, '_blank'); }; // Append to the last message div chatWindow.lastElementChild.appendChild(btn); } } function toggleLoading(isLoading) { sendBtn.disabled = isLoading; chatInput.disabled = isLoading; if (isLoading) { sendBtn.innerHTML = ''; } else { sendBtn.innerHTML = ''; chatInput.focus(); } } sendBtn.addEventListener('click', sendMessage); chatInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); // Auto-resize textarea 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' }; 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'; } }); 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); } // Set active theme swatch on load const currentTheme = document.documentElement.getAttribute('data-theme'); const activeSwatch = document.querySelector(`.theme-swatch[data-theme="${currentTheme}"]`); if (activeSwatch) activeSwatch.classList.add('active'); });