Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
3.5 kB · 100 lines
Astro
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101---import { DID } from '../../lib/atproto';---
<div id="annotationsPanel" class="tab-panel"> <div class="loading"> <span class="loading-dot"></span> <span class="loading-dot"></span> <span class="loading-dot"></span> </div></div>
<script is:inline define:vars={{ DID }}>(function() { var panel = document.getElementById('annotationsPanel');
function esc(s) { return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
function formatDate(d) { return new Date(d).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); }
async function resolvePDS() { var res = await fetch('https://plc.directory/' + DID); var doc = await res.json(); var svc = doc.service.find(function(s) { return s.type === 'AtprotoPersonalDataServer'; }); return svc.serviceEndpoint; }
async function listAllRecords(pds, collection) { var records = [], cursor = null; do { var url = new URL(pds + '/xrpc/com.atproto.repo.listRecords'); url.searchParams.set('repo', DID); url.searchParams.set('collection', collection); url.searchParams.set('limit', '100'); if (cursor) url.searchParams.set('cursor', cursor); var res = await fetch(url); if (!res.ok) throw new Error('PDS error ' + res.status); var data = await res.json(); records = records.concat(data.records); cursor = data.cursor || null; } while (cursor); return records; }
async function load() { try { var pds = await resolvePDS(); var records = await listAllRecords(pds, 'at.margin.annotation'); var annotations = records.map(function(r) { return r.value; }); annotations.sort(function(a, b) { return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); });
if (!annotations.length) { panel.innerHTML = '<div class="empty-state">No annotations yet</div>'; return; }
panel.innerHTML = ''; annotations.forEach(function(a) { var domain = ''; try { domain = new URL(a.target.source).hostname.replace('www.',''); } catch(e) {}
var html = ''; if (a.target.selector && a.target.selector.exact) { html += '<blockquote class="annotation-quote">“' + esc(a.target.selector.exact) + '”</blockquote>'; } html += '<div class="annotation-body">' + esc(a.body.value) + '</div>'; html += '<div class="card-meta">' + esc(a.target.title || domain) + ' · ' + formatDate(a.createdAt) + '</div>';
var el = document.createElement('a'); el.className = 'card annotation-card'; el.href = a.target.source; el.target = '_blank'; el.rel = 'noopener'; el.innerHTML = html; panel.appendChild(el); }); } catch(e) { panel.innerHTML = '<div class="error-state">Could not load annotations</div>'; } }
window.addEventListener('tab-switch', function(e) { if (e.detail === 'annotations' && !panel.dataset.loaded) { panel.dataset.loaded = '1'; load(); } });})();</script>
<style is:global> .annotation-card { display: block; text-decoration: none; color: var(--text); } .annotation-quote { font-style: normal; color: var(--text-light); border-left: 6px solid var(--green); padding-left: 12px; margin-bottom: 8px; font-size: 0.88rem; font-weight: 700; } .annotation-body { font-size: 0.9rem; margin-bottom: 8px; }</style>