1234function timelineTips() {5 const dots = document.querySelectorAll(".timeline .dot");6 if (dots.length === 0) return;7 const tip = document.createElement("div");8 tip.className = "timeline-tip";9 tip.hidden = true;10 document.body.append(tip);11 for (const dot of dots) {12 13 const title = dot.querySelector("title");14 const [id, message, meta] = (title?.textContent ?? "").split("\n");15 title?.remove();16 dot.setAttribute("aria-label", `${id} ${message}`);17 dot.addEventListener("pointerenter", (event) => {18 tip.replaceChildren(line("tip-id", id), line("tip-msg", message), line("tip-meta", meta));19 tip.hidden = false;20 const left = event.clientX + 14;21 tip.style.left = `${left + 340 > window.innerWidth ? Math.max(8, event.clientX - 354) : left}px`;22 tip.style.top = `${event.clientY - 10}px`;23 });24 dot.addEventListener("pointerleave", () => {25 tip.hidden = true;26 });27 }28}2930function line(className, text) {31 const div = document.createElement("div");32 div.className = className;33 div.textContent = text ?? "";34 return div;35}3637function copyButtons() {38 for (const button of document.querySelectorAll(".copy-btn[data-copy]")) {39 button.hidden = false;40 button.addEventListener("click", async () => {41 await navigator.clipboard.writeText(button.dataset.copy);42 button.textContent = "copied";43 button.classList.add("copied");44 setTimeout(() => {45 button.textContent = "copy";46 button.classList.remove("copied");47 }, 1400);48 });49 }50}515253function lineRanges() {54 const code = document.querySelector(".code");55 if (!code) return;56 let anchor = null;57 const mark = () => {58 for (const marked of code.querySelectorAll(".line.hl")) marked.classList.remove("hl");59 const match = /^#L(\d+)(?:-L(\d+))?$/.exec(location.hash);60 if (!match) return;61 const from = Number(match[1]);62 const to = Number(match[2] ?? match[1]);63 anchor = from;64 for (let n = Math.min(from, to); n <= Math.max(from, to); n++) {65 document.getElementById(`L${n}`)?.classList.add("hl");66 }67 document.getElementById(`L${Math.min(from, to)}`)?.scrollIntoView({ block: "center" });68 };69 code.addEventListener("click", (event) => {70 const link = event.target.closest(".ln");71 if (!link || !event.shiftKey || anchor === null) return;72 event.preventDefault();73 const n = Number(link.textContent);74 history.replaceState(null, "", `#L${Math.min(anchor, n)}-L${Math.max(anchor, n)}`);75 mark();76 });77 window.addEventListener("hashchange", mark);78 mark();79}8081timelineTips();82copyButtons();83lineRanges();