// Theme handling: "undefined mode" defaults to system unless explicitly toggled (function initTheme() { const root = document.documentElement; const saved = localStorage.getItem('theme-mode'); // 'light' | 'dark' const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; const apply = (mode) => { root.classList.toggle('dark', mode === 'dark'); }; if (saved === 'light' || saved === 'dark') { apply(saved); } else { apply(prefersDark ? 'dark' : 'light'); } })(); const themeToggle = document.getElementById('themeToggle'); if (themeToggle) { themeToggle.addEventListener('click', () => { const root = document.documentElement; const isDark = root.classList.toggle('dark'); localStorage.setItem('theme-mode', isDark ? 'dark' : 'light'); }); } // Elements const chatBox = document.getElementById('chatBox'); const composerForm = document.getElementById('composerForm'); const messageInput = document.getElementById('messageInput'); const sendBtn = document.getElementById('sendBtn'); const imageInput = document.getElementById('imageInput'); const codeInput = document.getElementById('codeInput'); const uploadImageBtn = document.getElementById('uploadImageBtn'); const uploadCodeBtn = document.getElementById('uploadCodeBtn'); const connectGitHubBtn = document.getElementById('connectGitHubBtn'); const githubConnectHeaderBtn = document.getElementById('githubConnectBtn'); const attachmentPreview = document.getElementById('attachmentPreview'); const modeIndicator = document.getElementById('modeIndicator'); const modeButtons = document.querySelectorAll('.mode-btn'); let currentMode = 'code'; updateModeIndicator(); // Helpers function formatTime(date = new Date()) { return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } function scrollToBottom() { chatBox.scrollTop = chatBox.scrollHeight; } function createAvatar(initials, seed = 0) { const palette = ['bg-undefined-500', 'bg-blue-500', 'bg-teal-500', 'bg-violet-500', 'bg-rose-500']; const color = palette[seed % palette.length]; return `
${initials}
`; } function agentBubble({ name = 'Agent', message = '', attachments = [] }) { const idx = Math.floor(Math.random() * 5); return `
${createAvatar(name.slice(0,1).toUpperCase(), idx)}
${name}
${message ? `

${escapeHtml(message)}

` : ''} ${attachments.length ? `
${attachments.join('')}
` : ''}
${formatTime()}
`; } function userBubble({ message = '', attachments = [] }) { return `
You
${message ? `

${escapeHtml(message)}

` : ''} ${attachments.length ? `
${attachments.join('')}
` : ''}
${formatTime()}
${createAvatar('Y')}
`; } function escapeHtml(str) { return str .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>'); } // Attachments UI function showAttachmentPreview(items) { if (!items.length) { attachmentPreview.classList.add('hidden'); attachmentPreview.innerHTML = ''; return; } attachmentPreview.classList.remove('hidden'); attachmentPreview.innerHTML = items.map((item) => { if (item.type.startsWith('image/')) { return `
preview
${item.file?.name || 'image'}
${(item.file?.size / 1024).toFixed(1)} KB
`; } // code file preview return `
📄
${item.file?.name || 'code'}
${(item.file?.size / 1024).toFixed(1)} KB
`; }).join(''); // Remove handler attachmentPreview.querySelectorAll('[data-remove]').forEach(btn => { btn.addEventListener('click', () => { const card = btn.closest('[class*=rounded-lg]'); const index = Array.from(attachmentPreview.children).indexOf(card); pendingAttachments.splice(index, 1); showAttachmentPreview(pendingAttachments); }); }); } const pendingAttachments = []; // Mode handling function updateModeIndicator() { if (modeIndicator) modeIndicator.textContent = `Mode: ${currentMode}`; modeButtons.forEach(b => { const isActive = b.dataset.mode === currentMode; b.classList.toggle('bg-undefined-500', isActive); b.classList.toggle('text-white', isActive); b.classList.toggle('hover:opacity-90', isActive); }); } modeButtons.forEach(btn => { btn.addEventListener('click', () => { currentMode = btn.dataset.mode; updateModeIndicator(); // Post a small status message const statusMsg = `Mode switched to: ${currentMode}`; chatBox.insertAdjacentHTML('beforeend', agentBubble({ name: 'System', message: statusMsg })); scrollToBottom(); }); }); // Composer events composerForm.addEventListener('submit', (e) => { e.preventDefault(); const message = messageInput.value.trim(); const attachments = pendingAttachments.slice(); if (!message && !attachments.length) return; chatBox.insertAdjacentHTML('beforeend', userBubble({ message, attachments })); scrollToBottom(); // Simulate agent response setTimeout(() => { const reply = agentReply(message, currentMode, attachments); chatBox.insertAdjacentHTML('beforeend', reply); scrollToBottom(); }, 600); // Reset composer messageInput.value = ''; pendingAttachments.length = 0; showAttachmentPreview(pendingAttachments); messageInput.focus(); }); function agentReply(text, mode, attachments) { let message = ''; if (attachments.some(a => a.type.startsWith('image/'))) { message += 'Got your image(s). '; } if (attachments.some(a => !a.type.startsWith('image/'))) { message += 'I also see code/file attachments. '; } const modeHints = { design: 'I can suggest UI/UX and styling tweaks.', architect: 'I can outline components, data flow, and constraints.', plan: 'I can draft a plan with milestones and tasks.', debug: 'I can help triage issues and propose fixes.', code: 'I can produce and refine code based on your input.' }; message += `${modeHints[mode] || ''}${text ? ` Regarding "${escapeHtml(text)}"` : ''} — what would you like to do next?`; return agentBubble({ name: 'Agent', message }); } // Input attachments uploadImageBtn.addEventListener('click', () => imageInput.click()); uploadCodeBtn.addEventListener('click', () => codeInput.click()); imageInput.addEventListener('change', async (e) => { const files = Array.from(e.target.files || []); files.forEach(file => { if (!file.type.startsWith('image/')) return; const url = URL.createObjectURL(file); pendingAttachments.push({ type: file.type, file, url }); }); showAttachmentPreview(pendingAttachments); imageInput.value = ''; }); codeInput.addEventListener('change', async (e) => { const files = Array.from(e.target.files || []); files.forEach(file => { // accept text-based files const isText = /(\.|\/)(txt|md|json|js|ts|jsx|tsx|html|css|scss|py|rb|go|rs|java|c|cpp|cs|yml|yaml|ini|env)$/i.test(file.name); if (!isText) return; const url = URL.createObjectURL(file); pendingAttachments.push({ type: 'text/x-code', file, url }); }); showAttachmentPreview(pendingAttachments); codeInput.value = ''; }); // GitHub connect function connectGitHub() { // In a real app, redirect to your backend OAuth route const token = prompt('Paste a personal access token (classic or fine-grained) to simulate GitHub connection:'); if (!token) return; const shortToken = token.slice(0, 6) + '...' + token.slice(-4); const msg = `Connected to GitHub (token: ${shortToken}). You can now fetch repos, issues, and PRs.`; chatBox.insertAdjacentHTML('beforeend', agentBubble({ name: 'GitHub', message: msg })); scrollToBottom(); } connectGitHubBtn?.addEventListener('click', connectGitHub); githubConnectHeaderBtn?.addEventListener('click', connectGitHub); // Auto-resize textarea messageInput.addEventListener('input', () => { messageInput.style.height = 'auto'; messageInput.style.height = Math.min(messageInput.scrollHeight, 160) + 'px'; }); // Accessibility: focus input on load messageInput.focus();