diff --git a/internal/httpapi/static/app.js b/internal/httpapi/static/app.js index 9e396db..77a1b59 100644 --- a/internal/httpapi/static/app.js +++ b/internal/httpapi/static/app.js @@ -19,7 +19,37 @@ async function api(path, params){ 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 processLinks(scope){ const links = scope.querySelectorAll('a[href]:not([data-card])'); links.forEach(a=>{ a.setAttribute('data-card','1'); + // Fetch and render card + 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 += '
'; + const row = document.createElement('div'); row.style.display='flex'; row.style.alignItems='flex-start'; row.style.gap='.5rem'; row.innerHTML = html; + c.appendChild(row); + // Summary control row + const ctrl = document.createElement('div'); ctrl.style.marginTop='.25rem'; + const btn = document.createElement('button'); btn.type='button'; btn.title='Summarize this link'; btn.textContent='\u25B6'; btn.style.padding='0 .4rem'; btn.style.fontSize='.9rem'; + const spinner = document.createElement('span'); spinner.textContent=''; spinner.style.marginLeft='.5rem'; + const sum = document.createElement('div'); sum.className='link-summary'; sum.style.whiteSpace='pre-wrap'; sum.style.marginTop='.25rem'; + btn.onclick = async ()=>{ + btn.disabled=true; spinner.textContent='…'; sum.textContent=''; + try{ const data = await api('/api/linksummary',{query:{url:a.href}}); sum.textContent = (data && data.summary) ? data.summary : '(no summary)'; } + catch(e){ sum.textContent = 'error: '+e; } + spinner.textContent=''; btn.disabled=false; + }; + ctrl.appendChild(btn); ctrl.appendChild(spinner); + c.appendChild(ctrl); + c.appendChild(sum); + a.parentNode.insertBefore(c, a.nextSibling); + } + }).catch(()=>{}); +}); } async function loadChannels(){ try{ const data = await api('/api/channels'); st.channels = data; renderChannels(); if(data.length>0){ selectChannel(data[0]); } } catch(e){} } function renderChannels(){ const list=document.getElementById('chanlist'); if(!list) return; list.innerHTML=''; st.channels.forEach(c=>{ const a=document.createElement('a'); a.href='#'; a.textContent=c; a.onclick=(ev)=>{ev.preventDefault(); selectChannel(c)}; if(c===st.current) a.className='active'; list.appendChild(a); }); }