diff --git a/pages/_app.js b/pages/_app.js
index c233976..78de167 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -19,9 +19,139 @@ const App = ({ Component, pageProps }) => {
script2.defer = true;
document.body.appendChild(script2);
+ const COPY_SUCCESS_TIMEOUT = 2000;
+
+ const copySvg = `
+
+ `;
+
+ const tickSvg = `
+
+ `;
+
+ function createCopyButtonFor(block) {
+ try {
+ if (!block || block.dataset.copyAttached === "true") return;
+ const pre = block.querySelector("pre");
+ const code =
+ block.querySelector("pre > code") || block.querySelector("code");
+ if (!pre || !code) return;
+
+ if (!pre.style.position || pre.style.position === "")
+ pre.style.position = "relative";
+
+ if (pre.querySelector("button[data-copy-btn]")) {
+ block.dataset.copyAttached = "true";
+ return;
+ }
+
+ const btn = document.createElement("button");
+ btn.setAttribute("type", "button");
+ btn.setAttribute("aria-label", "Copy Code Snippet");
+ btn.setAttribute("title", "Copy Code Snippet");
+ btn.dataset.copyBtn = "true";
+ btn.innerHTML = copySvg;
+
+ btn.style.position = "absolute";
+ btn.style.right = "7px";
+ btn.style.top = "10px";
+ btn.style.zIndex = "1";
+ btn.style.border = "1px solid rgba(0,0,0,0.06)";
+ btn.style.padding = "8px";
+ btn.style.backdropFilter = "blur(8px)";
+ btn.style.cursor = "pointer";
+ btn.style.fontSize = "14px";
+ btn.style.borderRadius = "10px";
+ btn.style.boxShadow = "inset 0 0 0 1.2px rgba(0,0,0,0.04)";
+ btn.style.background = "rgba(255,255,255,0.6)";
+ btn.style.display = "flex";
+ btn.style.alignItems = "center";
+ btn.style.justifyContent = "center";
+
+ let timeoutId = null;
+
+ async function doCopy() {
+ const text = code.innerText || code.textContent || "";
+ if (!text) return;
+ let ok = false;
+ try {
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ await navigator.clipboard.writeText(text);
+ ok = true;
+ } else {
+ const ta = document.createElement("textarea");
+ ta.value = text;
+ document.body.appendChild(ta);
+ ta.select();
+ ok = document.execCommand("copy");
+ document.body.removeChild(ta);
+ }
+ } catch (e) {
+ console.warn("copy failed", e);
+ }
+
+ if (ok) {
+ btn.innerHTML = tickSvg;
+ btn.setAttribute("aria-label", "Copied");
+ btn.setAttribute("title", "Copied");
+ clearTimeout(timeoutId);
+ timeoutId = setTimeout(() => {
+ btn.innerHTML = copySvg;
+ btn.setAttribute("aria-label", "Copy Code Snippet");
+ btn.setAttribute("title", "Copy Code Snippet");
+ }, COPY_SUCCESS_TIMEOUT);
+ } else {
+ btn.animate(
+ [
+ { transform: "translateX(0)" },
+ { transform: "translateX(-4px)" },
+ { transform: "translateX(4px)" },
+ { transform: "translateX(0)" },
+ ],
+ { duration: 300 }
+ );
+ }
+ }
+
+ btn.addEventListener("click", doCopy);
+ btn.addEventListener("keydown", (e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ doCopy();
+ }
+ });
+
+ pre.appendChild(btn);
+ block.dataset.copyAttached = "true";
+ } catch (err) {
+ console.error("Error attaching copy button", err);
+ }
+ }
+
+ function attachToAll() {
+ document
+ .querySelectorAll(".nextra-code-block")
+ .forEach(createCopyButtonFor);
+ }
+
+ attachToAll();
+
+ const mo = new MutationObserver((mutations) => {
+ for (const m of mutations) {
+ if (m.type === "childList" && m.addedNodes.length) {
+ attachToAll();
+ break;
+ }
+ }
+ });
+ mo.observe(document.body, { childList: true, subtree: true });
+
return () => {
document.body.removeChild(script1);
document.body.removeChild(script2);
+ mo.disconnect();
};
}, []);