diff --git a/assets/sass/content.scss b/assets/sass/content.scss index 1a03fc8..8ef1144 100644 --- a/assets/sass/content.scss +++ b/assets/sass/content.scss @@ -49,6 +49,43 @@ pre { border-radius: 5px; overflow-x: auto; } +.code-block-wrapper { + position: relative; + margin-bottom: 1.5rem; +} + +.copy-code-button { + position: absolute; + top: 8px; + right: 8px; + width: 28px; + height: 28px; + + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(4px); + border-radius: 6px; + + border: 1px solid rgba(255, 255, 255, 0.25); + cursor: pointer; + + display: flex; + align-items: center; + justify-content: center; + + transition: background 0.2s ease, border-color 0.2s ease; + + svg { + width: 16px; + height: 16px; + fill: white; + } + + &:hover { + background: rgba(255, 255, 255, 0.25); + border-color: rgba(255, 255, 255, 0.4); + } +} + details { margin-bottom: 2em; @@ -58,5 +95,5 @@ details { } details[open] { - border-color: 1px solid var(--text-color); -} + border-color: var(--text-color); +} \ No newline at end of file diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 429e9a6..49306a7 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -24,6 +24,7 @@ + @@ -44,7 +45,7 @@

Helmet.js

{{ block "main" . }}{{ end }} - +
+ - diff --git a/static/img/check.svg b/static/img/check.svg new file mode 100644 index 0000000..c5150f5 --- /dev/null +++ b/static/img/check.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/img/copy.svg b/static/img/copy.svg new file mode 100644 index 0000000..bd5f033 --- /dev/null +++ b/static/img/copy.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/static/img/error.svg b/static/img/error.svg new file mode 100644 index 0000000..4c46831 --- /dev/null +++ b/static/img/error.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/static/js/copy-code.js b/static/js/copy-code.js new file mode 100644 index 0000000..5bce446 --- /dev/null +++ b/static/js/copy-code.js @@ -0,0 +1,58 @@ +document.addEventListener("DOMContentLoaded", () => { + const codeBlocks = document.querySelectorAll("pre > code"); + + const icons = { + copy: ` + + + + `, + check: ` + + + + ` + }; + + function setButtonState(btn, icon, label) { + btn.innerHTML = icon; + btn.setAttribute("aria-label", label); + } + + codeBlocks.forEach(code => { + const pre = code.parentElement; + const wrapper = document.createElement("div"); + + wrapper.className = "code-block-wrapper"; + pre.replaceWith(wrapper); + wrapper.appendChild(pre); + + const button = document.createElement("button"); + button.className = "copy-code-button"; + setButtonState(button, icons.copy, "Copy code"); + + wrapper.appendChild(button); + + let cooling = false; + + button.addEventListener("click", async () => { + if (cooling) return; + cooling = true; + + try { + await navigator.clipboard.writeText(code.textContent.trim()); + setButtonState(button, icons.check, "Copied"); + } catch { + setButtonState(button, icons.copy, "Copy code"); + } + + setTimeout(() => { + setButtonState(button, icons.copy, "Copy code"); + cooling = false; + }, 1000); + }); + }); +});