CodeCopy.svelte
1 <script lang="ts"> 2 import { onMount } from "svelte"; 3 import CheckIcon from "~icons/ph/check"; 4 import CopyIcon from "~icons/ph/copy"; 5 6 onMount(() => { 7 const codeBlocks = document.querySelectorAll("pre"); 8 9 codeBlocks.forEach((pre) => { 10 const wrapper = document.createElement("div"); 11 wrapper.className = "relative group"; 12 13 pre.parentNode?.insertBefore(wrapper, pre); 14 wrapper.appendChild(pre); 15 16 const button = document.createElement("button"); 17 button.className = 18 "absolute top-2 right-2 p-2 rounded bg-pink-950/80 text-pink-50 opacity-0 group-hover:opacity-100 transition-opacity duration-200 focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-pink-400 hover:bg-pink-900 z-10"; 19 button.setAttribute("aria-label", "Copy code to clipboard"); 20 button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path fill="currentColor" d="M216 32H88a8 8 0 0 0-8 8v16H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-16h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8m-8 144H48V64h16v96a8 8 0 0 0 8 8h136Zm32-32h-24V48H96v-8h144Z"/></svg>`; 21 22 button.addEventListener("click", async () => { 23 const code = pre.textContent || ""; 24 try { 25 await navigator.clipboard.writeText(code); 26 button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path fill="currentColor" d="m232.5 71.5l-128 128a12.1 12.1 0 0 1-17 0l-56-56a12 12 0 0 1 17-17L96 175L215.5 55.5a12 12 0 0 1 17 17Z"/></svg>`; 27 button.classList.add("bg-green-700/80"); 28 button.classList.remove("bg-pink-950/80"); 29 30 setTimeout(() => { 31 button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path fill="currentColor" d="M216 32H88a8 8 0 0 0-8 8v16H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-16h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8m-8 144H48V64h16v96a8 8 0 0 0 8 8h136Zm32-32h-24V48H96v-8h144Z"/></svg>`; 32 button.classList.remove("bg-green-700/80"); 33 button.classList.add("bg-pink-950/80"); 34 }, 2000); 35 } catch (err) { 36 console.error("Failed to copy:", err); 37 } 38 }); 39 40 wrapper.appendChild(button); 41 }); 42 43 return () => { 44 codeBlocks.forEach((pre) => { 45 const wrapper = pre.parentElement; 46 if (wrapper?.classList.contains("group")) { 47 const button = wrapper.querySelector("button"); 48 if (button) button.remove(); 49 wrapper.parentNode?.insertBefore(pre, wrapper); 50 wrapper.remove(); 51 } 52 }); 53 }; 54 }); 55 </script>