// static/js/app.js v7 function switchView(id, navItem) { document.querySelectorAll('.view-container').forEach(function(v) { v.classList.remove('active'); }); var el = document.getElementById(id); if (el) el.classList.add('active'); if (navItem) { document.querySelectorAll('.nav-item').forEach(function(n) { n.classList.remove('active'); }); navItem.classList.add('active'); if (id === 'projects-view') loadPublishedProjects(); if (id === 'ai-chat-view') loadAIChatHistory(); if (id === 'apps-view') checkGithubStatus(); if (id === 'terminal-view' && typeof initTerminal === 'function') { setTimeout(function() { initTerminal(); }, 100); } if (id === 'editor-view' && typeof initEditor === 'function') { setTimeout(function() { initEditor(); }, 100); } if (window.innerWidth <= 768) { document.getElementById('sidebar').classList.remove('open'); document.getElementById('sidebar-overlay').classList.remove('active'); } } } window.toggleTheme = function() { var r = document.documentElement; var isLight = r.classList.toggle('light'); var icon = document.getElementById('theme-icon'); localStorage.setItem('theme', isLight ? 'light' : 'dark'); if (icon) { icon.className = isLight ? 'fa-solid fa-sun' : 'fa-solid fa-moon'; } if (typeof editor !== 'undefined' && editor) editor.setTheme(isLight ? 'ace/theme/github' : 'ace/theme/tomorrow_night_eighties'); if (typeof updateTermTheme === 'function') updateTermTheme(); var sel = document.getElementById('setting-theme-mode'); if (sel) sel.value = isLight ? 'light' : 'dark'; }; function loadTheme() { if (localStorage.getItem('theme') === 'light') { document.documentElement.classList.add('light'); var icon = document.getElementById('theme-icon'); if (icon) { icon.className = 'fa-solid fa-sun'; } } } document.addEventListener('DOMContentLoaded', function() { loadTheme(); if (typeof marked !== 'undefined') { marked.setOptions({ highlight: function(code, lang) { if (typeof hljs !== 'undefined') { var l = hljs.getLanguage(lang) ? lang : 'plaintext'; return hljs.highlight(code, { language: l }).value; } return code; }, breaks: true, gfm: true }); } }); // Toast function showToast(msg, type) { type = type || 'info'; var c = document.getElementById('toast-container'); if (!c) { c = document.createElement('div'); c.id = 'toast-container'; document.body.appendChild(c); } var t = document.createElement('div'); var icon = 'fa-info-circle', col = 'var(--info)'; if (type === 'success') { icon = 'fa-check-circle'; col = 'var(--success)'; } if (type === 'error') { icon = 'fa-exclamation-circle'; col = 'var(--error)'; } if (type === 'warning') { icon = 'fa-exclamation-triangle'; col = 'var(--warning)'; } t.style.cssText = 'background:var(--bg-elevated);color:var(--text-1);border:1px solid var(--border);border-left:3px solid ' + col + ';padding:12px 16px;border-radius:var(--r-sm);box-shadow:var(--elev-2);display:flex;align-items:center;gap:10px;transform:translateY(20px);opacity:0;transition:all 0.2s ease;font-size:14px;min-height:44px;'; t.innerHTML = '' + msg + ''; c.appendChild(t); requestAnimationFrame(function() { t.style.transform = 'translateY(0)'; t.style.opacity = '1'; }); setTimeout(function() { t.style.transform = 'translateY(20px)'; t.style.opacity = '0'; setTimeout(function() { t.remove(); }, 200); }, 3500); } // AI Chat var aiChatHistory = []; async function sendChatMessage() { var inputEl = document.getElementById('ai-chat-input'); var msg = inputEl.value.trim(); if (!msg || !currentToken) return; inputEl.value = ''; appendChat('user', msg); var bid = 'ai-' + Date.now(); appendChat('system', ' Thinking...', bid); try { var sr = await fetch('/api/settings/get', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken}) }).catch(function() { return {}; }); var settings = {}; if (sr.ok) { var r = await sr.json(); if (r.settings) settings = r.settings; } var resp = await fetch('/api/chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ token: currentToken, message: msg, history: aiChatHistory, model: settings.aiModel, openrouter_key: settings.openrouterKey, nvidia_key: settings.nvidiaKey, system_prompt: settings.aiPrompt }) }); if (!resp.ok) throw new Error('HTTP ' + resp.status); var bubble = document.getElementById(bid); var reader = resp.body.getReader(), dec = new TextDecoder(), full = ''; while (true) { var d = await reader.read(); if (d.done) break; full += dec.decode(d.value, { stream: true }); bubble.innerHTML = DOMPurify.sanitize(marked.parse(full)); var h = document.getElementById('ai-chat-history'); h.scrollTop = h.scrollHeight; } aiChatHistory.push({role: 'user', content: msg}); aiChatHistory.push({role: 'assistant', content: full}); } catch (e) { var b = document.getElementById(bid); if (b) b.innerHTML = 'Error: ' + e.message + ''; } } function appendChat(role, content, bid) { var h = document.getElementById('ai-chat-history'); var w = document.createElement('div'); w.className = 'chat-msg ' + role; var av = role === 'user' ? '' : ''; w.innerHTML = av + '
' + content + '
'; h.appendChild(w); h.scrollTop = h.scrollHeight; } function handleChatInput(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendChatMessage(); } } async function loadAIChatHistory() { if (!currentToken) return; try { var res = await fetch('/api/chat/history', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken}) }); var data = await res.json(); if (data.history) { aiChatHistory = data.history; var h = document.getElementById('ai-chat-history'); h.innerHTML = ''; if (data.history.length === 0) { appendChat('system', 'Hello! How can I help you build today?'); } else { data.history.forEach(function(m) { if (m.role === 'system') return; var html = m.role === 'assistant' ? DOMPurify.sanitize(marked.parse(m.content)) : m.content; appendChat(m.role, html); }); } } } catch (e) { console.error(e); } } async function clearAIChatHistory() { if (!confirm('Clear chat history?')) return; try { var res = await fetch('/api/chat/history/clear', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken}) }); var data = await res.json(); if (data.success) { aiChatHistory = []; document.getElementById('ai-chat-history').innerHTML = ''; appendChat('system', 'Chat cleared. How can I help?'); } } catch (e) { showToast('Failed to clear', 'error'); } } // GitHub window.addEventListener('message', function(e) { if (e.data === 'github_oauth_success') { showToast('GitHub connected!', 'success'); checkGithubStatus(); } }); function connectGithub() { if (!currentToken) return; var w = 500, h = 600, l = (screen.width - w) / 2, t = (screen.height - h) / 2; window.open('/api/github/login?user_token=' + currentToken, 'gh', 'width=' + w + ',height=' + h + ',top=' + t + ',left=' + l); } async function checkGithubStatus() { if (!currentToken) return; try { var res = await fetch('/api/github/status', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken}) }); var data = await res.json(); var s = document.getElementById('github-auth-status'), r = document.getElementById('github-repos-section'); if (data.connected) { s.innerHTML = ' Connected'; r.style.display = 'block'; fetchGithubRepos(); } else { s.innerHTML = ''; r.style.display = 'none'; } } catch (e) { console.error(e); } } async function fetchGithubRepos() { var list = document.getElementById('github-repos-list'); list.innerHTML = '

Loading...

'; try { var res = await fetch('/api/github/repos', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken}) }); var data = await res.json(); if (data.error) { list.innerHTML = '

' + data.error + '

'; return; } list.innerHTML = ''; if (!data.repos.length) { list.innerHTML = '

No repos found.

'; return; } data.repos.forEach(function(repo) { var priv = repo.private ? ' ' : ''; var card = document.createElement('div'); card.style.cssText = 'background:rgba(255,255,255,0.03);border:1px solid var(--border);padding:var(--sp-3);border-radius:var(--r-sm)'; card.innerHTML = '

' + repo.name + priv + '

' + (repo.description || 'No description') + '

'; list.appendChild(card); }); } catch (e) { list.innerHTML = '

Error loading repos

'; } } async function importOauthRepo(url) { document.getElementById('import-loading-overlay').style.display = 'flex'; try { var res = await fetch('/api/github/import_oauth', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken, github_url: url}) }); var data = await res.json(); if (data.success) { showToast(data.message, 'success'); if (typeof loadFiles !== 'undefined') loadFiles(); } else showToast('Failed: ' + (data.error || 'Unknown'), 'error'); } catch (e) { showToast('Network error', 'error'); } finally { document.getElementById('import-loading-overlay').style.display = 'none'; } }