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);