diff --git a/internal/httpapi/server.go b/internal/httpapi/server.go index 4eac167..5741ca2 100644 --- a/internal/httpapi/server.go +++ b/internal/httpapi/server.go @@ -332,15 +332,7 @@ func (s *Server) handleUI(w http.ResponseWriter, r *http.Request) { if(!res.ok){ throw new Error('HTTP '+res.status); } return res.json(); } - async function loadInfo(){ - try{ const data = await api('/api/info'); - document.getElementById('version').textContent = data.version+ ' ('+data.commit+')'; - document.getElementById('built').textContent = data.builtAt; - document.getElementById('uptime').textContent = data.uptime; - document.getElementById('connected').textContent = data.connected? 'yes':'no'; - document.getElementById('counts').textContent = 'ingested ' + data.messagesIngested + ', notified ' + data.notificationsSent + ', pruned ' + data.messagesPruned; - }catch(e){ console.error(e); } - } + async function loadInfo(){ /* no-op for now; footer shows version */ } async function loadChannels(){ try{ const data = await api('/api/channels'); st.channels = data; renderChannels(); if(data.length>0){ selectChannel(data[0]); } } catch(e){ console.error(e); } @@ -353,11 +345,12 @@ func (s *Server) handleUI(w http.ResponseWriter, r *http.Request) { catch(e){} } } } function colorFor(nick){ let h=0; for(let i=0;i>>0 } return 'hsl('+(h%360)+',60%,'+(window.matchMedia('(prefers-color-scheme: dark)').matches? '70%':'35%')+')'; } - function lineHTML(m){ const ts = `[${m.time}]`; const nick = `${m.author}`; const body = escapeHtml(m.body); return `${ts} ${nick}: ${linkify(body)}`; } + function lineHTML(m){ const ts = '[' + m.time + ']'; const nick = '' + m.author + ''; const body = escapeHtml(m.body); return ts + ' ' + nick + ': ' + linkify(body); } function escapeHtml(s){ return s.replace(/[&<>"]/g, c=>({'&':'&','<':'<','>':'>','"':'"'}[c])); } - function linkify(t){ return t.replace(/https?:\/\/\S+/g, u=> `${u}`); } - function appendBatch(arr){ const el=document.getElementById('tail'); const frag=document.createDocumentFragment(); arr.forEach(m=>{ const div=document.createElement('div'); div.className='msg'; div.innerHTML=lineHTML(m); frag.appendChild(div); }); el.appendChild(frag); if(st.atBottom){ el.scrollTop = el.scrollHeight; } } - function prependBatch(arr){ const el=document.getElementById('tail'); const oldTop=el.firstChild; const frag=document.createDocumentFragment(); arr.forEach(m=>{ const div=document.createElement('div'); div.className='msg'; div.innerHTML=lineHTML(m); frag.appendChild(div); }); el.insertBefore(frag, el.firstChild); if(oldTop){ oldTop.scrollIntoView(); } } + function linkify(t){ return t.replace(/https?:\/\/\S+/g, function(u){ return '' + u + ''; }); } + function appendBatch(arr){ const el=document.getElementById('tail'); const frag=document.createDocumentFragment(); arr.forEach(m=>{ const div=document.createElement('div'); div.className='msg'; div.innerHTML=lineHTML(m); frag.appendChild(div); processLinks(div); }); el.appendChild(frag); if(st.atBottom){ el.scrollTop = el.scrollHeight; } } + function prependBatch(arr){ const el=document.getElementById('tail'); const oldTop=el.firstChild; const frag=document.createDocumentFragment(); arr.forEach(m=>{ const div=document.createElement('div'); div.className='msg'; div.innerHTML=lineHTML(m); frag.appendChild(div); processLinks(div); }); el.insertBefore(frag, el.firstChild); if(oldTop){ oldTop.scrollIntoView(); } } + function processLinks(scope){ const links = scope.querySelectorAll('a[href]:not([data-card])'); links.forEach(a=>{ a.setAttribute('data-card','1'); fetch('/api/linkcard?url='+encodeURIComponent(a.href)).then(r=>r.json()).then(card=>{ if(!card) return; if(card.title||card.description||card.image){ const c=document.createElement('div'); c.className='card'; var html=''; if(card.image){ html += '
'; } html += '
'; if(card.title){ html += '
'+escapeHtml(card.title)+'
'; } if(card.description){ html += '
'+escapeHtml(card.description)+'
'; } html += '
'; c.innerHTML = '
'+html+'
'; a.parentNode.insertBefore(c, a.nextSibling); } }).catch(()=>{}); }); } function startStream(){ const url=new URL('/api/stream', window.location.origin); url.searchParams.set('channel', st.current); const es=new EventSource(url); st.sse=es; es.onmessage=(ev)=>{ try{ const m=JSON.parse(ev.data); appendBatch([m]); }catch(e){} }; es.onerror=()=>{ es.close(); st.sse=null; setTimeout(startStream, 3000); } } async function doSumm(){ const ch = document.getElementById('channel').value; @@ -373,7 +366,7 @@ func (s *Server) handleUI(w http.ResponseWriter, r *http.Request) { }catch(e){ document.getElementById('summary').textContent = 'error: '+e; } btn.disabled = false; prog.style.display = 'none'; } - window.addEventListener('DOMContentLoaded', ()=>{ loadInfo(); loadChannels(); }); + window.addEventListener('DOMContentLoaded', ()=>{ loadChannels(); }); function onFollowToggle(cb){ if(cb.checked){ if(st.tailTimer) clearInterval(st.tailTimer);