/* ── localStorage cache ── */ const CACHE_PREFIX = 'nma_v1_'; const CACHE_TTL = 60 * 60 * 1000; // 1 hour function saveCache(key, prs) { try { const slim = prs.map(pr => ({ number: pr.number, title: pr.title, html_url: pr.html_url, merged_at: getMerged(pr), })); localStorage.setItem(CACHE_PREFIX + key, JSON.stringify({ ts: Date.now(), prs: slim })); } catch (e) { /* quota exceeded or unavailable */ } } function loadCache(key) { try { const raw = localStorage.getItem(CACHE_PREFIX + key); if (!raw) return null; const parsed = JSON.parse(raw); if (Date.now() - parsed.ts > CACHE_TTL) return null; return parsed; } catch { return null; } } function getCacheTs(key) { try { const raw = localStorage.getItem(CACHE_PREFIX + key); if (!raw) return null; return JSON.parse(raw).ts; } catch { return null; } } function clearAllCaches() { try { for (const k of Object.keys(localStorage)) { if (k.startsWith(CACHE_PREFIX)) localStorage.removeItem(k); } } catch {} } function fmtRelative(ts) { if (!ts) return ''; const sec = Math.round((Date.now() - ts) / 1000); if (sec < 60) return 'just now'; const min = Math.floor(sec / 60); if (min < 60) return `${min}m ago`; const hr = Math.floor(min / 60); return `${hr}h ${min % 60}m ago`; }