From 660e6fb1b4b4fa57596471d80c4e4264c711f915 Mon Sep 17 00:00:00 2001 From: Kyren223 Date: Fri, 9 Oct 2026 19:11:34 +0300 Subject: Blog stuff initial version --- public/footnotes.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 public/footnotes.js (limited to 'public/footnotes.js') diff --git a/public/footnotes.js b/public/footnotes.js new file mode 100644 index 0000000..50f709e --- /dev/null +++ b/public/footnotes.js @@ -0,0 +1,69 @@ +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(); +}); -- cgit v1.3.1