So you want a fancy “copy code” button on your Anki card.
Paste the snippet below into both the Front and Back templates.
Purely vibe-coded, but don't worry, I'm a professional software developer. It looks kinda fine to me.
<style>
.prettify-flashcard .highlight { position: relative; }
.anki-copy-btn {
position: absolute;
top: -8px;
right: 5px;
width: 30px;
height: 30px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 7px;
border: 1px solid rgba(255,255,255,0.12);
background: rgba(0,0,0,0.18);
color: rgba(255,255,255,0.85);
cursor: pointer;
user-select: none;
z-index: 9999;
opacity: 0;
transform: translateY(-2px);
transition: opacity 120ms ease, transform 120ms ease, background 120ms ease;
backdrop-filter: blur(6px);
}
.highlight:hover .anki-copy-btn,
.highlight:focus-within .anki-copy-btn {
opacity: 1;
transform: translateY(0);
}
.anki-copy-btn:hover { background: rgba(0,0,0,0.28); }
.anki-copy-btn:active { transform: translateY(0) scale(0.98); }
.anki-copy-btn svg {
width: 14px;
height: 14px;
display: block;
}
.anki-copy-btn[data-state="success"] {
background: rgba(0,0,0,0.28);
border-color: rgba(255,255,255,0.18);
}
</style>
<script>
(() => {
const ICON_COPY = `
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path fill="currentColor" d="M16 1H6c-1.1 0-2 .9-2 2v12h2V3h10V1zm3 4H10c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16h-9V7h9v14z"/>
</svg>
`;
const ICON_CHECK = `
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path fill="currentColor" d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"/>
</svg>
`;
const SUCCESS_MS = 850;
function setState(btn, state) {
btn.dataset.state = state;
btn.innerHTML = (state === "success") ? ICON_CHECK : ICON_COPY;
btn.title = (state === "success") ? "Copied" : "Copy";
btn.setAttribute("aria-label", (state === "success") ? "Copied" : "Copy code");
}
function tryExecCommandCopy(text) {
try {
const ta = document.createElement("textarea");
ta.value = text;
ta.setAttribute("readonly", "");
ta.style.position = "fixed";
ta.style.left = "-9999px";
ta.style.top = "0";
ta.style.opacity = "0";
document.body.appendChild(ta);
ta.focus();
ta.select();
ta.setSelectionRange(0, ta.value.length);
const ok = document.execCommand("copy");
document.body.removeChild(ta);
return ok;
} catch (e) {
return false;
}
}
function withTimeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error("clipboard timeout")), ms)),
]);
}
async function copyText(text) {
const okExec = tryExecCommandCopy(text);
if (okExec) return true;
const write = navigator?.clipboard?.writeText;
if (!write) return false;
try {
await withTimeout(navigator.clipboard.writeText(text), 200);
return true;
} catch (e) {
return false;
}
}
function attachToHighlight(highlightEl) {
if (!highlightEl) return;
if (highlightEl.querySelector(".anki-copy-btn")) return;
const pre = highlightEl.querySelector("pre");
if (!pre) return;
const btn = document.createElement("button");
btn.type = "button";
btn.className = "anki-copy-btn";
btn._resetTimer = null;
btn._busy = false;
setState(btn, "idle");
btn.addEventListener("click", async (e) => {
e.preventDefault();
e.stopPropagation();
if (btn._busy) return;
btn._busy = true;
try {
await copyText(pre.innerText);const ok = await copyText(pre.innerText);
if (ok) {
setState(btn, "success");
if (btn._resetTimer) clearTimeout(btn._resetTimer);
btn._resetTimer = setTimeout(() => {
setState(btn, "idle");
btn._resetTimer = null;
}, SUCCESS_MS);
} else {
setState(btn, "idle");
}
} catch (err) {
setState(btn, "idle");
} finally {
btn._busy = false;
}
});
highlightEl.appendChild(btn);
}
function enhance(root = document) {
const highlights = root.querySelectorAll(".prettify-flashcard .highlight");
highlights.forEach(attachToHighlight);
}
enhance();
const obs = new MutationObserver(() => enhance());
obs.observe(document.documentElement, { childList: true, subtree: true });
setTimeout(enhance, 50);
setTimeout(enhance, 200);
})();
</script>