| 1 | import { Controller } from "@hotwired/stimulus" |
| 2 | |
| 3 | // Injects a "Copy" button into every fenced code block inside rendered |
| 4 | // Markdown (README / .md). Attach to the prose container: |
| 5 | // <div class="prose-readme" data-controller="code-copy">…</div> |
| 6 | export default class extends Controller { |
| 7 | connect() { |
| 8 | this.element.querySelectorAll("pre.highlight").forEach((pre) => { |
| 9 | if (pre.querySelector(".code-copy-btn")) return |
| 10 | pre.classList.add("code-copy-wrap") |
| 11 | |
| 12 | const btn = document.createElement("button") |
| 13 | btn.type = "button" |
| 14 | btn.className = "code-copy-btn" |
| 15 | btn.textContent = "Copy" |
| 16 | btn.addEventListener("click", () => this.#copy(pre, btn)) |
| 17 | pre.appendChild(btn) |
| 18 | }) |
| 19 | } |
| 20 | |
| 21 | async #copy(pre, btn) { |
| 22 | const code = pre.querySelector("code")?.textContent ?? pre.textContent |
| 23 | try { |
| 24 | await navigator.clipboard.writeText(code) |
| 25 | } catch { |
| 26 | return |
| 27 | } |
| 28 | const original = btn.textContent |
| 29 | btn.textContent = "Copied" |
| 30 | btn.classList.add("copied") |
| 31 | setTimeout(() => { |
| 32 | btn.textContent = original |
| 33 | btn.classList.remove("copied") |
| 34 | }, 1500) |
| 35 | } |
| 36 | } |