1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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();
});
|