// Shared helpers used by app.js, explore.js, and share.js. window.$ = (sel, root = document) => root.querySelector(sel); window.api = { async req(method, path, body) { const res = await fetch(path, { method, headers: body ? { "Content-Type": "application/json" } : undefined, body: body ? JSON.stringify(body) : undefined, }); const data = res.status === 204 ? null : await res.json().catch(() => null); if (!res.ok) { const e = new Error((data && data.error) || "Request failed"); e.status = res.status; throw e; } return data; }, }; window.escapeHtml = (s) => String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); // SQLite stores UTC datetimes as "YYYY-MM-DD HH:MM:SS"; parse as UTC. window.parseUTC = (s) => new Date(s.replace(" ", "T") + "Z"); window.relativeTime = (s) => { const then = parseUTC(s); const diff = (Date.now() - then.getTime()) / 1000; if (diff < 45) return "just now"; if (diff < 90) return "a minute ago"; if (diff < 3600) return `${Math.round(diff / 60)} min ago`; if (diff < 7200) return "an hour ago"; if (diff < 86400) return `${Math.round(diff / 3600)} hours ago`; if (diff < 172800) return "yesterday"; if (diff < 604800) return `${Math.round(diff / 86400)} days ago`; return then.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric", }); }; window.fullTime = (s) => parseUTC(s).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short", }); let toastTimer; window.toast = (msg, isError = false) => { const el = $("#toast"); if (!el) return; el.textContent = msg; el.classList.toggle("error", isError); el.classList.add("show"); clearTimeout(toastTimer); toastTimer = setTimeout(() => el.classList.remove("show"), 2600); }; // Render a list of notes into a container element (read-only unless onDelete given). window.renderNotesInto = (container, notes, onDelete) => { if (!container) return; if (!notes.length) { container.innerHTML = '
No notes yet.
'; return; } container.innerHTML = notes .map( (n) => `
📝
${escapeHtml(n.body)}
${relativeTime( n.created_at )} ${onDelete ? `delete` : ""}
` ) .join(""); if (onDelete) { container.querySelectorAll(".del").forEach((el) => el.addEventListener("click", () => onDelete(Number(el.dataset.note))) ); } }; // Provider sign-in buttons (used by login gate). window.providerButton = (p) => { const icons = { github: '', google: '', }; return ``; };