summaryrefslogtreecommitdiff
path: root/public/footnotes.js
diff options
context:
space:
mode:
authorKyren223 <contact@kyren.codes>2026-10-09 19:11:34 +0300
committerKyren223 <contact@kyren.codes>2026-10-09 19:11:34 +0300
commit660e6fb1b4b4fa57596471d80c4e4264c711f915 (patch)
tree6dc9f7329d9c4e724e681d8550105d794e6e90ff /public/footnotes.js
parent66a5c4ea1793886aad80a67ad0e40aca6066bcde (diff)
Blog stuff initial version
Diffstat (limited to 'public/footnotes.js')
-rw-r--r--public/footnotes.js69
1 files changed, 69 insertions, 0 deletions
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();
+});