The Multi-AI Intelligence Platform ✦ Number 10

or
Powered by ☀ SOLAI · Name Number 10
✦ Number 10
demo@solai.ai
D
0 tokens
System

Ask me anything.

Choose a single AI or compare all four side-by-side. Your intelligence hub is ready.

Sending to:
SOLAI ✦ Number 10 · Sun Intelligence · All responses are AI-generated
D
$1,247
Monthly Revenue
↑ 18% this month
24
Active Clients
↑ 3 new this week
4,892
Total Messages
↑ 12% this week
98.4%
Uptime
↑ All systems go
AI Usage This Week
Claude
GPT
Gemini
Grok
Claude
GPT
Grok
AI Share of Responses
4.9K
total
Claude 40%
ChatGPT 30%
Gemini 20%
Grok 10%
Revenue (Last 8 Months)
Recent Activity
New client TechCorp Ltd upgraded to Pro
2m ago
482 Claude messages today
1h ago
🏷
White-label client FlashAI launched
3h ago
$
Revenue milestone: $1,000/mo reached
1d ago
Clients
Client Plan Messages Revenue Status
TechCorp Ltd
techcorp.com
Agency 1,240 $99/mo ● Active
FlashAI
flashai.co
Pro 887 $29/mo ● Active
GreenBot
greenbot.io
Pro 542 $29/mo ◐ Trial
Marcus Agency
marcusai.com
Starter 104 Free ● Active
AI API Keys
◆ Claude (Anthropic)
Built-in — no key needed for demo
⬡ ChatGPT (OpenAI)
Get from platform.openai.com
◈ Gemini (Google)
Get from aistudio.google.com
𝕏 Grok (xAI)
Get from console.x.ai · $175/mo free credits
const API_BASE = window.SOLAI_API || ( window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' ? 'http://localhost:3001' : window.location.origin // same-origin fallback for when frontend+backend served together ); let authToken = localStorage.getItem('solai_token') || null; let backendOnline = false; async function api(method, path, body) { const headers = { 'Content-Type': 'application/json' }; if (authToken) headers['Authorization'] = 'Bearer ' + authToken; try { const r = await fetch(API_BASE + path, { method, headers, body: body ? JSON.stringify(body) : undefined, }); backendOnline = true; return r.json(); } catch { backendOnline = false; return null; } } // ── STATE ──────────────────────────────────────────────────────── let user = null; let activeAI = 'all'; let chatHistory = { claude:[], gpt:[], gemini:[], grok:[] }; let isLoading = false; let abortController = null; let lastUserMsg = ''; let totalTokens = 0; let apiKeys = { gpt:null, gemini:null, grok:null }; let brandColor = '#f5a623'; let brandColor2 = '#ff7c4a'; let isDarkMode = true; let currentSessionId = null; const AI_CFG = { claude: { name:'Claude', model:'claude-sonnet-4-20250514', tag:'Sonnet 4', av:'av-c', nm:'nm-c', td:'td-c', icon:'◆' }, gpt: { name:'ChatGPT', model:'gpt-4o', tag:'GPT-4o', av:'av-g', nm:'nm-g', td:'td-g', icon:'⬡' }, gemini: { name:'Gemini', model:'gemini-1.5-flash', tag:'1.5 Flash', av:'av-m', nm:'nm-m', td:'td-m', icon:'◈' }, grok: { name:'Grok', model:'grok-4-1-fast-non-reasoning', tag:'4.1 Fast', av:'av-x', nm:'nm-x', td:'td-x', icon:'𝕏' }, }; // ── UTILS ──────────────────────────────────────────────────────── function esc(s) { return s.replace(/&/g,'&').replace(//g,'>'); } function toast(msg, dur) { if (dur === undefined) dur = 2200; var el = document.getElementById('toastEl'); if (!el) return; el.textContent = msg; el.classList.add('show'); clearTimeout(el._t); el._t = setTimeout(function(){ el.classList.remove('show'); }, dur); } function getSYS() { var bar = document.getElementById('sysPromptInput'); var persona = document.querySelector('#sett-persona .sett-textarea'); return (bar && bar.value.trim()) || (persona && persona.value.trim()) || 'You are SOLAI, a brilliant, helpful AI assistant. Be concise yet thorough. Use markdown for clarity.'; } // ── AUTH ───────────────────────────────────────────────────────── function switchAuthTab(t) { document.getElementById('tabA').classList.toggle('active', t==='login'); document.getElementById('tabB').classList.toggle('active', t==='signup'); document.getElementById('loginF').style.display = t==='login' ? 'block' : 'none'; document.getElementById('signupF').style.display = t==='signup' ? 'block' : 'none'; } async function doLogin() { var emailEl = document.getElementById('loginEmail'); var passEl = document.getElementById('loginPass'); var email = emailEl ? emailEl.value.trim() : ''; var pass = passEl ? passEl.value : ''; if (!email) { toast('⚠️ Enter your email'); return; } var data = await api('POST', '/api/auth/login', { email: email, password: pass }); if (data && data.token) { authToken = data.token; localStorage.setItem('solai_token', authToken); await mountUser(data.user); } else if (data && data.error) { toast('✗ ' + data.error); } else { toast('⚠️ Backend offline — demo mode'); mountUser({ id:'demo', name: email.split('@')[0], email: email, plan:'pro' }); } } async function doSignup() { var name = (document.getElementById('signupName') ? document.getElementById('signupName').value : '').trim(); var email = (document.getElementById('signupEmail') ? document.getElementById('signupEmail').value : '').trim(); var pass = (document.getElementById('signupPass') ? document.getElementById('signupPass').value : ''); if (!email || !pass) { toast('⚠️ Email and password required'); return; } if (pass.length < 6) { toast('⚠️ Password must be at least 6 characters'); return; } var data = await api('POST', '/api/auth/register', { name: name, email: email, password: pass }); if (data && data.token) { authToken = data.token; localStorage.setItem('solai_token', authToken); toast('✓ Account created! Welcome to SOLAI.'); await mountUser(data.user); } else if (data && data.error) { toast('✗ ' + data.error); } else { toast('⚠️ Backend offline — demo mode'); mountUser({ id:'demo', name: name || email.split('@')[0], email: email, plan:'starter' }); } } async function mountUser(u) { user = u; document.getElementById('authScreen').style.display = 'none'; document.getElementById('mainApp').style.display = 'flex'; document.getElementById('mainApp').style.flex = '1'; var init = (u.name || 'U')[0].toUpperCase(); ['userAvatar','topbarAvatar','dashAvatar'].forEach(function(id) { var el = document.getElementById(id); if (el) el.textContent = init; }); ['topbarUser','dashUser','settUser'].forEach(function(id) { var el = document.getElementById(id); if (el) el.textContent = u.email; }); var an = document.getElementById('accName'); if (an) an.value = u.name || ''; var ae = document.getElementById('accEmail'); if (ae) ae.value = u.email || ''; updatePills(); await Promise.all([loadKeys(), loadBrand(), loadSessions(), loadUsage()]); } function signOut() { authToken = null; localStorage.removeItem('solai_token'); user = null; chatHistory = { claude:[], gpt:[], gemini:[], grok:[] }; totalTokens = 0; currentSessionId = null; updateTokenDisplay(); document.getElementById('authScreen').style.display = 'flex'; document.getElementById('mainApp').style.display = 'none'; } function goLanding() { window.location.href = 'solai-landing.html'; } // Auto-login if token exists in localStorage (async function() { if (!authToken) return; var data = await api('GET', '/api/auth/me'); if (data && data.id) { await mountUser(data); } else { authToken = null; localStorage.removeItem('solai_token'); } })(); // ── BACKEND LOADERS ────────────────────────────────────────────── async function loadKeys() { var data = await api('GET', '/api/keys'); if (!data) return; if (data.gpt) { var el = document.getElementById('gptKeyInput'); if (el) el.placeholder = data.gpt; var dot = document.getElementById('gptStatus'); if (dot) { dot.classList.add('set'); dot.classList.remove('unset'); } } if (data.gemini) { var el = document.getElementById('geminiKeyInput'); if (el) el.placeholder = data.gemini; var dot = document.getElementById('geminiStatus'); if (dot) { dot.classList.add('set'); dot.classList.remove('unset'); } } if (data.grok) { var el = document.getElementById('grokKeyInput'); if (el) el.placeholder = data.grok; var dot = document.getElementById('grokStatus'); if (dot) { dot.classList.add('set'); dot.classList.remove('unset'); } } } async function loadBrand() { var data = await api('GET', '/api/brand'); if (!data || !data.name) return; brandColor = data.color1 || '#f5a623'; brandColor2 = data.color2 || '#ff7c4a'; var nameInput = document.getElementById('brandNameInput'); var iconInput = document.getElementById('brandIconInput'); if (nameInput) nameInput.value = data.name; if (iconInput) iconInput.value = data.icon || '☀'; if (data.sysPrompt) { var sysEl = document.getElementById('sysPromptInput'); if (sysEl) sysEl.value = data.sysPrompt; } document.documentElement.style.setProperty('--brand-color', brandColor); document.documentElement.style.setProperty('--brand-color2', brandColor2); var sbn = document.getElementById('sidebarBrandName'); if (sbn) sbn.textContent = data.name; var tb = document.getElementById('topbarBrand'); if (tb) tb.textContent = (data.icon || '☀') + ' ' + data.name; } async function loadSessions() { var data = await api('GET', '/api/sessions'); if (!Array.isArray(data)) { buildRevChart(); return; } var histEl = document.getElementById('historyList'); if (!histEl) { buildRevChart(); return; } histEl.innerHTML = ''; data.slice(0, 30).forEach(function(sess) { var h = document.createElement('div'); h.className = 'hist-item'; h.textContent = sess.title || 'Untitled'; h.title = sess.title; h.dataset.sessId = sess.id; h.onclick = function() { loadSession(sess.id); }; histEl.appendChild(h); }); buildRevChart(); } async function loadSession(id) { var data = await api('GET', '/api/sessions/' + id); if (!data || !data.messages) return; currentSessionId = id; if (chatEl) chatEl.innerHTML = ''; chatHistory = { claude:[], gpt:[], gemini:[], grok:[] }; data.messages.forEach(function(m) { if (m.role === 'user') { addUserMsgUI(m.content); Object.keys(chatHistory).forEach(function(ai) { chatHistory[ai].push({ role:'user', content:m.content }); }); } else { var ai = m.ai || 'claude'; var bubs = addAIBlock([ai]); setBub(bubs[ai], m.content); chatHistory[ai].push({ role:'assistant', content:m.content }); } }); toast('✓ Session loaded: ' + (data.title || 'Chat')); } async function loadUsage() { var data = await api('GET', '/api/usage'); if (!data) { buildRevChart(); return; } var statRevEl = document.querySelector('.stat-card:nth-child(1) .stat-card-num'); if (statRevEl && data.monthlyRevenue !== undefined) statRevEl.textContent = '$' + data.monthlyRevenue.toLocaleString(); var statClientsEl = document.querySelector('.stat-card:nth-child(2) .stat-card-num'); if (statClientsEl && data.clients !== undefined) statClientsEl.textContent = data.clients; var statMsgsEl = document.querySelector('.stat-card:nth-child(3) .stat-card-num'); if (statMsgsEl) statMsgsEl.textContent = data.totalRequests ? data.totalRequests.toLocaleString() : '0'; buildRevChart(data.daily); } // ── PANEL NAV ──────────────────────────────────────────────────── function showPanel(p) { document.querySelectorAll('.panel').forEach(function(el) { el.classList.remove('active'); }); document.querySelectorAll('.lnav-btn').forEach(function(b) { b.classList.remove('active'); }); var panel = document.getElementById('panel-' + p); var nav = document.getElementById('nav-' + p); if (panel) panel.classList.add('active'); if (nav) nav.classList.add('active'); if (p === 'dashboard') loadClientsTable(); } async function loadClientsTable() { var data = await api('GET', '/api/clients'); if (!Array.isArray(data) || !data.length) return; var tbody = document.querySelector('.clients-table tbody'); if (!tbody) return; tbody.innerHTML = data.map(function(c) { var plan = c.plan === 'agency' ? 'agency' : c.plan === 'pro' ? 'pro' : 'starter'; var rev = c.revenue ? '$' + c.revenue + '/mo' : 'Free'; var st = c.status === 'active' ? 'active' : 'trial'; return '' + '' + esc(c.name) + '
' + esc(c.email) + '
' + '' + c.plan + '' + '' + ((c.messages || 0)).toLocaleString() + '' + '' + rev + '' + '● ' + (c.status || 'trial') + '' + ''; }).join(''); } // ── AI SELECT ──────────────────────────────────────────────────── function selectAI(ai) { activeAI = ai; document.querySelectorAll('.ai-opt').forEach(function(b) { b.classList.remove('sel'); }); var btn = document.querySelector('[data-ai="' + ai + '"]'); if (btn) btn.classList.add('sel'); updatePills(); } function updatePills() { var row = document.getElementById('pillsRow'); if (!row) return; row.innerHTML = ''; if (activeAI === 'all') { var p = document.createElement('div'); p.className = 'pill pill-all'; p.textContent = '✦ All 4 AIs'; row.appendChild(p); return; } var cfg = AI_CFG[activeAI]; if (!cfg) return; var clsMap = { claude:'pill-c', gpt:'pill-g', gemini:'pill-m', grok:'pill-x' }; var p = document.createElement('div'); p.className = 'pill ' + (clsMap[activeAI] || 'pill-all'); p.textContent = cfg.icon + ' ' + cfg.name; row.appendChild(p); } // ── CHAT CORE ──────────────────────────────────────────────────── var chatEl = document.getElementById('chat'); var input = document.getElementById('userInput'); if (input) { input.addEventListener('input', function() { input.style.height = 'auto'; input.style.height = Math.min(input.scrollHeight, 140) + 'px'; var cc = document.getElementById('charCounter'); if (cc) { var len = input.value.length; cc.textContent = len > 0 ? len + ' chars' : ''; cc.classList.toggle('warn', len > 800); } }); input.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); } function hideWelcome() { var w = document.getElementById('welcome'); if (w) w.remove(); } function scroll() { if (chatEl) chatEl.scrollTop = chatEl.scrollHeight; } function updateTokenDisplay() { var el = document.getElementById('tokenDisplay'); if (el) el.textContent = totalTokens.toLocaleString() + ' tokens'; } function showWelcome() { if (!chatEl || document.getElementById('welcome')) return; var w = document.createElement('div'); w.className = 'welcome'; w.id = 'welcome'; w.innerHTML = '
' + '

Ask me anything.

' + '

Choose a single AI or compare all four side-by-side.

' + '
' + '' + '' + '' + '' + '' + '' + '
'; chatEl.appendChild(w); } function addUserMsgUI(t) { var d = document.createElement('div'); d.className = 'user-msg message'; d.innerHTML = '
' + esc(t) + '
'; chatEl.appendChild(d); scroll(); } function addUserMsg(t) { hideWelcome(); addUserMsgUI(t); var histEl = document.getElementById('historyList'); if (histEl) { var h = document.createElement('div'); h.className = 'hist-item'; h.textContent = t.slice(0, 40) + (t.length > 40 ? '…' : ''); h.title = t; h.onclick = function() { if (input) { input.value = t; input.focus(); } }; histEl.prepend(h); } } function addAIBlock(ais) { var block = document.createElement('div'); block.className = 'ai-block'; if (ais.length > 1) { var lbl = document.createElement('div'); lbl.className = 'cmp-label'; lbl.innerHTML = '
Comparing ' + ais.length + ' AIs
'; block.appendChild(lbl); } var resp = document.createElement('div'); resp.className = 'ai-responses'; if (ais.length === 2) resp.classList.add('cmp-2'); else if (ais.length >= 3) resp.classList.add('cmp-many'); block.appendChild(resp); var cards = {}; var ts = Date.now(); ais.forEach(function(ai) { var cfg = AI_CFG[ai]; var card = document.createElement('div'); card.className = 'ai-card'; var bubId = 'bub-' + ai + '-' + ts; card.innerHTML = '
' + cfg.icon + '
' + '
' + '
' + '' + cfg.name + '' + '' + cfg.tag + '' + '
' + '
' + '
' + '
' + '
' + '' + '' + '
' + '
'; resp.appendChild(card); cards[ai] = card.querySelector('.ai-bub'); }); chatEl.appendChild(block); scroll(); return cards; } function setBub(bub, txt) { if (bub) bub.innerHTML = fmt(txt); } // ── COPY / SPEAK ───────────────────────────────────────────────── function copyBub(bubId) { var el = document.getElementById(bubId); if (!el) return; navigator.clipboard.writeText(el.innerText || el.textContent).then(function() { toast('✓ Copied to clipboard'); var card = el.closest('.ai-card'); if (card) { var btn = card.querySelector('.ma-btn'); if (btn) { var o = btn.textContent; btn.textContent = '✓ Copied!'; btn.classList.add('copied'); setTimeout(function() { btn.textContent = o; btn.classList.remove('copied'); }, 1800); } } }); } function speakBub(bubId) { var el = document.getElementById(bubId); if (!el || !window.speechSynthesis) return; if (speechSynthesis.speaking) { speechSynthesis.cancel(); toast('⏹ Stopped speaking'); return; } var utt = new SpeechSynthesisUtterance(el.innerText || el.textContent); utt.rate = 1.05; utt.pitch = 1; speechSynthesis.speak(utt); toast('🔊 Speaking...', 3000); } // ── SEND / STOP / REGEN ────────────────────────────────────────── async function sendSug(t) { if (input) { input.value = t; input.dispatchEvent(new Event('input')); await sendMessage(); } } async function newChat() { chatHistory = { claude:[], gpt:[], gemini:[], grok:[] }; totalTokens = 0; updateTokenDisplay(); currentSessionId = null; if (chatEl) chatEl.innerHTML = ''; showWelcome(); var rb = document.getElementById('regenBtn'); if (rb) rb.style.display = 'none'; var cc = document.getElementById('charCounter'); if (cc) cc.textContent = ''; if (backendOnline) { var sess = await api('POST', '/api/sessions', {}); if (sess && sess.id) currentSessionId = sess.id; } } function setLoading(v) { isLoading = v; var sb = document.getElementById('stopBtn'); var rb = document.getElementById('regenBtn'); var sendB = document.getElementById('sendBtn'); if (sb) sb.classList.toggle('visible', v); if (sendB) sendB.style.opacity = v ? '0.4' : '1'; if (!v && rb && lastUserMsg) rb.style.display = 'flex'; } function stopGeneration() { if (abortController) abortController.abort(); setLoading(false); toast('⏹ Generation stopped'); } async function regenerate() { if (!lastUserMsg || isLoading) return; if (input) { input.value = lastUserMsg; input.dispatchEvent(new Event('input')); } await sendMessage(); } async function sendMessage() { if (!input) return; var txt = input.value.trim(); if (!txt || isLoading) return; lastUserMsg = txt; abortController = new AbortController(); setLoading(true); input.value = ''; input.style.height = 'auto'; var cc = document.getElementById('charCounter'); if (cc) cc.textContent = ''; addUserMsg(txt); var ais = activeAI === 'all' ? ['claude','gpt','gemini','grok'] : [activeAI]; var bubs = addAIBlock(ais); ais.forEach(function(ai) { chatHistory[ai].push({ role:'user', content:txt }); }); totalTokens += Math.ceil(txt.split(' ').length * 1.3); if (backendOnline && !currentSessionId) { var sess = await api('POST', '/api/sessions', {}); if (sess && sess.id) currentSessionId = sess.id; } // Capture bubIds now before async operations var bubIdMap = {}; Object.keys(bubs).forEach(function(ai){ if(bubs[ai]&&bubs[ai].id) bubIdMap[ai]=bubs[ai].id; }); await Promise.all(ais.map(function(ai){ return callAI(ai, bubs[ai], txt, bubIdMap[ai]||''); })); setLoading(false); } async function callAI(ai, bub, userTxt) { try { var reply; if (ai === 'claude') reply = await callClaude(userTxt); else if (ai === 'gpt') reply = await callGPT(userTxt); else if (ai === 'grok') reply = await callGrok(userTxt); else reply = await callGemini(userTxt); chatHistory[ai].push({ role:'assistant', content:reply }); totalTokens += Math.ceil(reply.split(' ').length * 1.3); updateTokenDisplay(); setBub(bub, reply); if (backendOnline && currentSessionId) { api('POST', '/api/sessions/' + currentSessionId + '/messages', { messages: [ { role:'user', content:userTxt }, { role:'assistant', content:reply, ai:ai }, ], }); } } catch(e) { if (e.name === 'AbortError') setBub(bub, '⏹ Generation stopped.'); else setBub(bub, '⚠️ ' + e.message); } scroll(); } // ── AI API CALLS ───────────────────────────────────────────────── async function callClaude() { if (backendOnline) { var data = await api('POST', '/api/chat/claude', { messages:chatHistory.claude.slice(-12), system:getSYS(), sessionId:currentSessionId }); if (!data) throw new Error('Backend offline'); if (data.error) throw new Error(data.error); return data.reply; } var r = await fetch('https://api.anthropic.com/v1/messages', { signal: abortController && abortController.signal, method: 'POST', headers: { 'Content-Type':'application/json' }, body: JSON.stringify({ model:'claude-sonnet-4-20250514', max_tokens:1000, system:getSYS(), messages:chatHistory.claude.slice(-12) }), }); var d = await r.json(); if (d.error) throw new Error('Claude: ' + d.error.message); return d.content[0].text; } async function callGPT() { if (backendOnline) { var data = await api('POST', '/api/chat/gpt', { messages:chatHistory.gpt.slice(-12), system:getSYS(), sessionId:currentSessionId }); if (!data) throw new Error('Backend offline'); if (data.error) throw new Error(data.error); return data.reply; } var k = apiKeys.gpt || ((document.getElementById('gptKeyInput')||{}).value||'').trim(); if (!k) return '⚙️ Add your OpenAI API key in Settings → API Keys to enable ChatGPT.'; var r = await fetch('https://api.openai.com/v1/chat/completions', { signal: abortController && abortController.signal, method: 'POST', headers: { 'Content-Type':'application/json', 'Authorization':'Bearer '+k }, body: JSON.stringify({ model:'gpt-4o', max_tokens:1000, messages:[{role:'system',content:getSYS()},...chatHistory.gpt.slice(-12)] }), }); var d = await r.json(); if (d.error) throw new Error('ChatGPT: ' + d.error.message); return d.choices[0].message.content; } async function callGemini() { if (backendOnline) { var data = await api('POST', '/api/chat/gemini', { messages:chatHistory.gemini.slice(-12), system:getSYS(), sessionId:currentSessionId }); if (!data) throw new Error('Backend offline'); if (data.error) throw new Error(data.error); return data.reply; } var k = apiKeys.gemini || ((document.getElementById('geminiKeyInput')||{}).value||'').trim(); if (!k) return '⚙️ Add your Google Gemini API key in Settings → API Keys to enable Gemini.'; var msgs = chatHistory.gemini.slice(-12).map(function(m) { return { role:m.role==='assistant'?'model':'user', parts:[{text:m.content}] }; }); var r = await fetch('https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key='+k, { signal: abortController && abortController.signal, method: 'POST', headers: { 'Content-Type':'application/json' }, body: JSON.stringify({ contents:msgs, systemInstruction:{parts:[{text:getSYS()}]} }), }); var d = await r.json(); if (d.error) throw new Error('Gemini: ' + d.error.message); return d.candidates[0].content.parts[0].text; } async function callGrok() { if (backendOnline) { var data = await api('POST', '/api/chat/grok', { messages:chatHistory.grok.slice(-12), system:getSYS(), sessionId:currentSessionId }); if (!data) throw new Error('Backend offline'); if (data.error) throw new Error(data.error); return data.reply; } var k = apiKeys.grok || ((document.getElementById('grokKeyInput')||{}).value||'').trim(); if (!k) return '⚙️ Add your xAI Grok key in Settings → API Keys. Get one free at console.x.ai'; var r = await fetch('https://api.x.ai/v1/chat/completions', { signal: abortController && abortController.signal, method: 'POST', headers: { 'Content-Type':'application/json', 'Authorization':'Bearer '+k }, body: JSON.stringify({ model:'grok-4-1-fast-non-reasoning', max_tokens:1000, messages:[{role:'system',content:getSYS()},...chatHistory.grok.slice(-12)] }), }); var d = await r.json(); if (d.error) throw new Error('Grok: ' + d.error.message); return d.choices[0].message.content; } // ── TOOLBAR ────────────────────────────────────────────────────── function toggleSysPrompt() { var bar = document.getElementById('sysBar'); if (!bar) return; bar.classList.toggle('visible'); var btn = document.getElementById('sysBtnLabel'); if (btn) btn.textContent = bar.classList.contains('visible') ? '✕ Close Prompt' : '⚙ System Prompt'; } function clearChat() { chatHistory = { claude:[], gpt:[], gemini:[], grok:[] }; totalTokens = 0; updateTokenDisplay(); currentSessionId = null; if (chatEl) chatEl.innerHTML = ''; showWelcome(); var rb = document.getElementById('regenBtn'); if (rb) rb.style.display = 'none'; toast('✓ Chat cleared'); } function exportChat() { if (!chatEl) return; var blocks = chatEl.querySelectorAll('.user-msg,.ai-card'); if (!blocks.length) { toast('Nothing to export yet'); return; } var out = 'SOLAI Chat Export\n' + '='.repeat(40) + '\n\n'; blocks.forEach(function(b) { if (b.classList.contains('user-msg')) { out += 'You:\n' + ((b.querySelector('.user-bub')||{}).innerText||'') + '\n\n'; } else { var name = ((b.querySelector('.ai-nm')||{}).textContent) || 'AI'; var txt = ((b.querySelector('.ai-bub')||{}).innerText) || ''; out += name + ':\n' + txt + '\n\n'; } }); out += '='.repeat(40) + '\nExported: ' + new Date().toLocaleString(); var a = document.createElement('a'); a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(out); a.download = 'SOLAI-chat-' + Date.now() + '.txt'; a.click(); toast('✓ Chat exported'); } function copyFullChat() { if (!chatEl) return; var blocks = chatEl.querySelectorAll('.user-msg,.ai-card'); if (!blocks.length) { toast('Nothing to copy yet'); return; } var out = ''; blocks.forEach(function(b) { if (b.classList.contains('user-msg')) { out += 'You: ' + ((b.querySelector('.user-bub')||{}).innerText||'') + '\n\n'; } else { var name = ((b.querySelector('.ai-nm')||{}).textContent)||'AI'; var txt = ((b.querySelector('.ai-bub')||{}).innerText)||''; out += name + ': ' + txt + '\n\n'; } }); navigator.clipboard.writeText(out.trim()).then(function() { toast('✓ Chat copied to clipboard'); }); } function toggleTheme() { isDarkMode = !isDarkMode; document.body.classList.toggle('light-mode', !isDarkMode); var knob = document.querySelector('.tt-knob'); if (knob) knob.style.left = isDarkMode ? '2px' : '18px'; toast(isDarkMode ? '🌙 Dark mode' : '☀ Light mode'); } function showShortcuts() { var o = document.getElementById('shortcutsOverlay'); if (o) o.classList.add('visible'); } function hideShortcuts() { var o = document.getElementById('shortcutsOverlay'); if (o) o.classList.remove('visible'); } document.addEventListener('keydown', function(e) { var meta = e.metaKey || e.ctrlKey; if (e.key === 'Escape') { hideShortcuts(); return; } if (!meta) return; switch(e.key) { case 'k': e.preventDefault(); newChat(); break; case 'l': e.preventDefault(); clearChat(); break; case 'p': e.preventDefault(); toggleSysPrompt(); break; case 'e': e.preventDefault(); exportChat(); break; case 'd': e.preventDefault(); toggleTheme(); break; case '1': e.preventDefault(); selectAI('all'); break; case '2': e.preventDefault(); selectAI('claude'); break; case '3': e.preventDefault(); selectAI('gpt'); break; case '4': e.preventDefault(); selectAI('gemini'); break; case '5': e.preventDefault(); selectAI('grok'); break; case '/': e.preventDefault(); if (input) input.focus(); break; } }); // ── SETTINGS ───────────────────────────────────────────────────── function switchSettTab(btn, id) { document.querySelectorAll('.sett-tab').forEach(function(t) { t.classList.remove('active'); }); btn.classList.add('active'); document.querySelectorAll('.sett-section').forEach(function(s) { s.style.display = 'none'; }); var sec = document.getElementById(id); if (sec) sec.style.display = 'block'; } function updateKeyStatus(ai) { var inp = document.getElementById(ai + 'KeyInput'); var dot = document.getElementById(ai + 'Status'); if (!inp || !dot) return; dot.classList.toggle('set', inp.value.trim().length > 0); dot.classList.toggle('unset', inp.value.trim().length === 0); } async function saveKeys() { var gpt = ((document.getElementById('gptKeyInput')||{}).value||'').trim() || null; var gemini = ((document.getElementById('geminiKeyInput')||{}).value||'').trim() || null; var grok = ((document.getElementById('grokKeyInput')||{}).value||'').trim() || null; apiKeys = { gpt:gpt, gemini:gemini, grok:grok }; var data = await api('POST', '/api/keys', { gpt:gpt, gemini:gemini, grok:grok }); if (data && data.ok) toast('✓ API keys saved securely to server'); else toast('✓ API keys saved locally' + (backendOnline ? '' : ' (backend offline)')); var b = document.querySelector('#sett-api .sett-btn-primary'); if (b) { b.textContent = '✓ Saved!'; setTimeout(function(){ b.textContent = 'Save API Keys'; }, 2000); } } async function savePersona(btn) { var personaTA = document.querySelector('#sett-persona .sett-textarea'); var personaName = document.querySelector('#sett-persona .sett-input'); var sysInput = document.getElementById('sysPromptInput'); var newPrompt = personaTA ? personaTA.value.trim() : ''; var newName = personaName ? personaName.value.trim() : ''; if (newPrompt && sysInput) sysInput.value = newPrompt; var wt = document.getElementById('welcomeTitle'); if (wt && newName) wt.textContent = 'Ask ' + newName + ' anything.'; if (newPrompt) await api('POST', '/api/brand', { sysPrompt:newPrompt, name:newName || undefined }); toast('✓ Persona saved & applied'); if (btn) { btn.textContent = '✓ Saved!'; setTimeout(function(){ btn.textContent = 'Save Persona Settings'; }, 2000); } } async function saveAccount(btn) { var name = ((document.getElementById('accName')||{}).value||'').trim(); var email = ((document.getElementById('accEmail')||{}).value||'').trim(); if (name && user) { user.name = name; user.email = email || user.email; } var data = await api('PUT', '/api/auth/me', { name:name, email:email }); toast((data && !data.error) ? '✓ Account details saved' : '✓ Account details saved locally'); if (btn) { btn.textContent = '✓ Saved!'; setTimeout(function(){ btn.textContent = 'Save Changes'; }, 2000); } } function updateBrandPreview() { var n = ((document.getElementById('brandNameInput')||{}).value) || 'SOLAI'; var ic = ((document.getElementById('brandIconInput')||{}).value) || '☀'; var pn = document.getElementById('prevName'); if (pn) pn.textContent = n; var pd = document.getElementById('prevDot'); if (pd) { pd.textContent = ic; pd.style.background = 'linear-gradient(135deg,' + brandColor + ',' + brandColor2 + ')'; } } function setBrandColor(el, c1, c2) { document.querySelectorAll('.color-swatch').forEach(function(s) { s.classList.remove('active'); }); el.classList.add('active'); brandColor = c1; brandColor2 = c2; updateBrandPreview(); } async function applyBrand() { var n = ((document.getElementById('brandNameInput')||{}).value) || 'SOLAI'; var ic = ((document.getElementById('brandIconInput')||{}).value) || '☀'; document.documentElement.style.setProperty('--brand-color', brandColor); document.documentElement.style.setProperty('--brand-color2', brandColor2); var sbn = document.getElementById('sidebarBrandName'); if (sbn) sbn.textContent = n; var tb = document.getElementById('topbarBrand'); if (tb) tb.textContent = ic + ' ' + n; document.querySelectorAll('.welcome-sun').forEach(function(w) { w.textContent = ic; w.style.background = 'linear-gradient(135deg,' + brandColor + ',' + brandColor2 + ')'; }); var lnav = document.querySelector('.lnav-logo'); if (lnav) lnav.textContent = ic; await api('POST', '/api/brand', { name:n, icon:ic, color1:brandColor, color2:brandColor2 }); toast('✓ Branding applied' + (backendOnline ? ' & saved' : '')); var b = document.querySelector('#sett-brand .sett-btn-primary'); if (b) { b.textContent = '✓ Applied!'; setTimeout(function(){ b.textContent = 'Apply Branding'; }, 2000); } } function resetBrand() { brandColor = '#f5a623'; brandColor2 = '#ff7c4a'; document.documentElement.style.setProperty('--brand-color', '#f5a623'); document.documentElement.style.setProperty('--brand-color2', '#ff7c4a'); var bi = document.getElementById('brandNameInput'); if (bi) bi.value = 'SOLAI'; var bic = document.getElementById('brandIconInput'); if (bic) bic.value = '☀'; var sbn = document.getElementById('sidebarBrandName'); if (sbn) sbn.textContent = 'SOLAI'; var tb = document.getElementById('topbarBrand'); if (tb) tb.textContent = '☀ SOLAI'; updateBrandPreview(); toast('↩ Brand reset to SOLAI'); } function copyEmbed() { var code = ((document.getElementById('embedSnippet')||{}).textContent) || ''; if (!code) return; navigator.clipboard.writeText(code).then(function() { toast('✓ Embed code copied'); var b = document.querySelector('#sett-embed .sett-btn-primary'); if (b) { b.textContent = '✓ Copied!'; setTimeout(function(){ b.textContent = 'Copy Code'; }, 2000); } }); } // ── REVENUE CHART ───────────────────────────────────────────────── function buildRevChart(dailyData) { var bars = document.getElementById('revBars'); var labels = document.getElementById('revLabels'); if (!bars || !labels) return; bars.innerHTML = ''; labels.innerHTML = ''; var months, vals; if (dailyData && dailyData.length > 0) { var slice = dailyData.slice(-8); months = slice.map(function(d) { return d.date.slice(5); }); vals = slice.map(function(d) { return d.count; }); } else { months = ['Aug','Sep','Oct','Nov','Dec','Jan','Feb','Mar']; vals = [220,310,480,620,750,890,1100,1247]; } var max = Math.max.apply(null, vals.concat([1])); vals.forEach(function(v, i) { var b = document.createElement('div'); b.className = 'rev-bar'; b.style.height = (v / max * 100) + '%'; b.title = months[i] + ': ' + v; bars.appendChild(b); var l = document.createElement('div'); l.className = 'rev-label'; l.textContent = months[i]; labels.appendChild(l); }); } // ── MARKDOWN FORMATTER ──────────────────────────────────────────── function fmt(t) { var s = t.replace(/&/g,'&').replace(//g,'>'); s = s.replace(/```(\w*)\n?([\s\S]*?)```/g, function(_, lang, code) { return '
'
      + (lang ? '
' + lang + '
' : '') + code.trim() + '
'; }); s = s.replace(/`([^`\n]+)`/g, '$1'); s = s.replace(/^### (.+)$/gm, '

$1

'); s = s.replace(/^## (.+)$/gm, '

$1

'); s = s.replace(/^# (.+)$/gm, '

$1

'); s = s.replace(/\*\*\*(.+?)\*\*\*/g, '$1'); s = s.replace(/\*\*(.+?)\*\*/g, '$1'); s = s.replace(/\*([^*\n]+?)\*/g, '$1'); s = s.replace(/^---+$/gm, '
'); s = s.replace(/(\|.+\|\n)((?:\|[-:| ]+\|\n))((?:\|.+\|\n?)+)/g, function(match, head, sep, body) { var headers = head.trim().split('|').filter(function(c){return c.trim();}).map(function(c){return ''+c.trim()+'';}).join(''); var rows = body.trim().split('\n').map(function(row){var cells=row.split('|').filter(function(c){return c.trim();}).map(function(c){return ''+c.trim()+'';}).join('');return ''+cells+'';}).join(''); return ''+headers+''+rows+'
'; }); s = s.replace(/^\d+\. (.+)$/gm, '
  • $1
  • '); s = s.replace(/^[-*] (.+)$/gm, '
  • $1
  • '); s = s.replace(/(]*>[\s\S]*?<\/li>\n?)+/g, function(m){return '';}); s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); s = s.replace(/\n/g, '
    '); return s; }
    ⌨ Keyboard Shortcuts
    Send messageEnter
    New lineShiftEnter
    New chatK
    Clear chatL
    Toggle system promptP
    Export chatE
    Switch to Compare All1
    Toggle dark/light modeD
    Close this overlayEsc

    📚 Prompt Library

    🎨 AI Image Generation

    Requires your OpenAI API key (DALL-E). Add it in Settings → API Keys first.