Pisces AI Messages Redirecting to /messages… return !!(auth && !auth.guest); }catch{ return false; } } function currentAuthor(){ try{ const u = window.PiscesAI?.state?.user || JSON.parse(localStorage.getItem('pisces_auth')||'null'); return (u?.display_name || u?.email || 'Me'); }catch{ return 'Me'; } } function applyAuthGate(){ const authed = isAuthed(); const newBtn = document.getElementById('newConv'); const composer = document.getElementById('composer'); const composerGate = document.getElementById('composerGate'); const saveCloudBtn = document.getElementById('saveCloud'); if(!authed){ newBtn.classList.add('composer-disabled'); composer.classList.add('composer-disabled'); composerGate.style.display='block'; if(saveCloudBtn) { saveCloudBtn.classList.add('composer-disabled'); saveCloudBtn.title='Login required to propose changes'; } const cta = document.createElement('div'); cta.className='cta'; cta.innerHTML = 'Sign in to start a DM or send messages. Login or Create Account'; document.querySelector('.grid .card .p16').appendChild(cta); }else{ newBtn.classList.remove('composer-disabled'); composer.classList.remove('composer-disabled'); composerGate.style.display='none'; if(saveCloudBtn) { saveCloudBtn.classList.remove('composer-disabled'); saveCloudBtn.removeAttribute('title'); } } } function load(){ try{ state.conversations = JSON.parse(localStorage.getItem(LS_KEY)||'[]'); }catch{ state.conversations=[] } } function save(){ localStorage.setItem(LS_KEY, JSON.stringify(state.conversations)); } function id(){ return Math.random().toString(36).slice(2) + Date.now().toString(36); } function renderList(filter=''){ const list = document.getElementById('convList'); list.innerHTML=''; const items = state.conversations.filter(c=> c.title.toLowerCase().includes(filter.toLowerCase())); if(items.length===0){ list.innerHTML = '
No conversations. Create one.
'; return; } items.forEach(c=>{ const div = document.createElement('div'); div.className='thread'; div.innerHTML = `
${escapeHtml(c.title)}
${c.messages.length} messages • ${new Date(c.updated_at).toLocaleString()}
`; div.onclick=(e)=>{ if(e.target.dataset.del){ delConv(e.target.dataset.del); e.stopPropagation(); return;} select(c.id); }; list.appendChild(div); }); } function select(cid){ state.selectedId = cid; const conv = state.conversations.find(c=>c.id===cid); const title = document.getElementById('convTitle'); const meta = document.getElementById('convMeta'); const msgs = document.getElementById('messages'); if(!conv){ title.textContent='Select a conversation'; meta.textContent='Local-only DMs stored on this device.'; msgs.innerHTML=''; return; } title.textContent = conv.title; meta.textContent = `${conv.messages.length} messages • Created ${new Date(conv.created_at).toLocaleString()}`; msgs.innerHTML=''; conv.messages.forEach(m=>{ const d = document.createElement('div'); const mine = (m.author===currentAuthor()); d.className = 'msg ' + (mine?'me':'them'); d.textContent = `${m.author}: ${m.body}`; msgs.appendChild(d); }); msgs.scrollTop = msgs.scrollHeight; } function newConv(){ const title = prompt('Conversation title (e.g., @alice)'); if(!title) return; const c = { id:id(), title, created_at: Date.now(), updated_at: Date.now(), messages: [] }; state.conversations.unshift(c); save(); renderList(); select(c.id); } function delConv(cid){ state.conversations = state.conversations.filter(c=>c.id!==cid); save(); renderList(); if(state.selectedId===cid){ select(null);} } function send(){ if(!isAuthed()) return; const input = document.getElementById('msgInput'); const body = input.value.trim(); if(!body) return; const conv = state.conversations.find(c=>c.id===state.selectedId); if(!conv) return; conv.messages.push({ id:id(), body, author: currentAuthor(), created_at: Date.now() }); conv.updated_at = Date.now(); save(); input.value=''; select(conv.id); renderList(); } function exportData(){ const blob = new Blob([JSON.stringify(state.conversations,null,2)], {type:'application/json'}); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'pisces-messages.json'; a.click(); } async function importData(file){ const text = await file.text(); const arr = JSON.parse(text); if(Array.isArray(arr)){ state.conversations = arr; save(); renderList(); select(null); } } function escapeHtml(s){ return s.replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"','\'':'''}[c])); } // init load(); renderList(); document.getElementById('newConv').onclick = newConv; document.getElementById('sendBtn').onclick = send; document.getElementById('search').oninput = (e)=> renderList(e.target.value); document.getElementById('exportConv').onclick = exportData; document.getElementById('importConv').onchange = (e)=>{ if(e.target.files[0]) importData(e.target.files[0]); }; // Cloud helpers async function loadFromCloud(){ try{ const { fetchCloudJson } = window.PiscesAICloud || {}; if(!fetchCloudJson) throw new Error('Cloud helpers not loaded'); const convs = await fetchCloudJson('data/messages/conversations.json', []); const msgs = await fetchCloudJson('data/messages/messages.json', []); const grouped = {}; msgs.forEach(m=>{ (grouped[m.conversation_id]=grouped[m.conversation_id]||[]).push(m); }); state.conversations = convs.map(c=>({ id:c.id, title: (c.title||c.participants?.join(', ')||'Conversation'), created_at: Date.parse(c.created_at)||Date.now(), updated_at: Date.now(), messages: (grouped[c.id]||[]).map(m=>({ id:m.id, body:m.content||'', author: (m.author||'them'), created_at: Date.parse(m.created_at)||Date.now() })) })); save(); renderList(); select(null); }catch(e){ alert('Failed to load cloud data'); console.warn(e); } } function saveToCloud(){ if(!isAuthed()) return; const { openCloudSyncIssue } = window.PiscesAICloud || {}; if(!openCloudSyncIssue){ alert('Cloud helpers not loaded'); return; } const convs = state.conversations.map(c=>({ id:c.id, participants: [], title:c.title, created_at: new Date(c.created_at).toISOString() })); const msgs = state.conversations.flatMap(c=> c.messages.map(m=>({ id:m.id, conversation_id:c.id, author:m.author||'web', content:m.body, created_at:new Date(m.created_at).toISOString() })) ); const updates = [ { path:'data/messages/conversations.json', op:'merge', data: convs }, { path:'data/messages/messages.json', op:'merge', data: msgs } ]; openCloudSyncIssue('Messages update', updates, 'Proposed by website user'); } document.getElementById('loadCloud').onclick = loadFromCloud; document.getElementById('saveCloud').onclick = saveToCloud; document.addEventListener('DOMContentLoaded', applyAuthGate); setTimeout(applyAuthGate, 400);