import { marked } from 'https://esm.sh/marked@14'; const recordsEl = document.getElementById('records'); const records = recordsEl ? JSON.parse(recordsEl.textContent || '[]') : []; const filterInput = document.querySelector('.filter'); const toggleArchived = document.querySelector('.toggle-archived'); const toggleGrouping = document.querySelector('.toggle-grouping'); const drawer = document.querySelector('.drawer'); const drawerContent = drawer?.querySelector('.drawer-content'); const drawerClose = drawer?.querySelector('.drawer-close'); const refreshInfo = document.querySelector('.refresh-info'); const header = document.querySelector('header'); const generatedAt = header?.dataset.generated ? new Date(header.dataset.generated) : null; const NEXT_TICK_MIN = 5; function relativeMin(ms) { return Math.max(0, Math.floor(ms / 60000)); } function updateRefreshInfo() { if (!refreshInfo || !generatedAt) return; const ageMin = relativeMin(Date.now() - generatedAt.getTime()); const nextMin = Math.max(0, NEXT_TICK_MIN - ageMin); refreshInfo.textContent = `refreshed ${ageMin}m ago · next in ${nextMin}m`; } updateRefreshInfo(); setInterval(updateRefreshInfo, 30_000); function applyFilter(query) { const q = query.trim().toLowerCase(); document.querySelectorAll('.card').forEach((card) => { const slug = card.dataset.slug || ''; const project = card.querySelector('.badge.project')?.textContent || ''; const title = card.querySelector('.card-title')?.textContent || ''; const hay = `${slug} ${project} ${title}`.toLowerCase(); card.dataset.hidden = q === '' || hay.includes(q) ? 'false' : 'true'; }); } filterInput?.addEventListener('input', (e) => { applyFilter(e.target.value); }); toggleArchived?.addEventListener('change', (e) => { document.body.classList.toggle('show-archived', e.target.checked); }); toggleGrouping?.addEventListener('change', (e) => { document.body.classList.toggle('group-by-project', e.target.checked); }); async function openDrawer(card) { if (!drawer || !drawerContent) return; const rawUrl = card.dataset.rawUrl; const slug = card.dataset.slug; if (!rawUrl) return; drawerContent.innerHTML = `

${slug}

loading…

`; drawer.hidden = false; try { const res = await fetch(rawUrl, { credentials: 'omit' }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const md = await res.text(); drawerContent.innerHTML = `

${escapeHtml(slug || '')}

` + marked.parse(md); } catch (err) { drawerContent.innerHTML = `

${escapeHtml(slug || '')}

Failed to load: ${escapeHtml(String(err))}

`; } } document.addEventListener('click', (e) => { const card = e.target.closest?.('.card'); if (card) openDrawer(card); }); drawerClose?.addEventListener('click', () => { if (drawer) drawer.hidden = true; }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && drawer) drawer.hidden = true; }); function escapeHtml(s) { return s .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } console.log(`board-viewer: ${records.length} records loaded`);