document.addEventListener("DOMContentLoaded", () => { const area = document.querySelector("[data-scroll-area]"); const refs = [...document.querySelectorAll("a.footnote-ref")]; if (!area || refs.length === 0) return; const panel = document.createElement("div"); panel.className = "sidenotes"; document.body.append(panel); const items = refs.map((ref) => { ref.addEventListener("click", (e) => e.preventDefault()); const note = document.getElementById(ref.getAttribute("href").slice(1)); const num = document.createElement("span"); num.className = "sidenote-num"; num.textContent = ref.textContent; const body = document.createElement("div"); body.append(...note.cloneNode(true).childNodes); body.querySelector(".footnote-backref")?.remove(); const el = document.createElement("div"); el.className = "sidenote"; el.append(num, body); panel.append(el); return { ref, el }; }); const gap = 6; let queued = false; const layout = () => { queued = false; const rect = area.getBoundingClientRect(); const frame = document.querySelector(".terminal").getBoundingClientRect(); const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); const left = frame.right + rem; Object.assign(panel.style, { left: `${left}px`, top: `${rect.top}px`, width: `${window.innerWidth - left - rem}px`, height: `${rect.height}px`, }); let bottom = -Infinity; for (const { ref, el } of items) { const top = Math.max( ref.getBoundingClientRect().top - rect.top, bottom + gap, ); el.style.top = `${top}px`; bottom = top + el.offsetHeight; } }; const schedule = () => { if (!queued) { queued = true; requestAnimationFrame(layout); } }; area.addEventListener("scroll", schedule, { passive: true }); window.addEventListener("resize", schedule); new ResizeObserver(schedule).observe(area); document.fonts?.ready.then(schedule); layout(); });