// 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 + '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 = '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'; } }