diff --git a/_includes/footer.html b/_includes/footer.html index 7d2e03d067..f4d72db177 100644 --- a/_includes/footer.html +++ b/_includes/footer.html @@ -65,4 +65,5 @@

The preCICE newsletter

+ diff --git a/css/customstyles-precice.css b/css/customstyles-precice.css index a7c72dff67..27fc166211 100644 --- a/css/customstyles-precice.css +++ b/css/customstyles-precice.css @@ -376,3 +376,39 @@ details > summary::after { details[open] > summary::after { content: "▲"; } + +/* Copy to Clipboard Button Styles */ +div.highlighter-rouge { + position: relative; +} + +.copy-code-btn { + position: absolute; + top: 5px; + right: 5px; + padding: 4px 8px; + font-size: 12px; + color: #555; + background-color: #f8f9fa; + border: 1px solid #ccc; + border-radius: 4px; + cursor: pointer; + opacity: 0; + transition: opacity 0.2s, background-color 0.2s, color 0.2s; + z-index: 10; +} + +div.highlighter-rouge:hover .copy-code-btn { + opacity: 1; +} + +.copy-code-btn:hover { + background-color: #e2e6ea; + color: #333; +} + +.copy-code-btn.copied { + background-color: #28a745; + color: white; + border-color: #28a745; +} diff --git a/js/copy-code.js b/js/copy-code.js new file mode 100644 index 0000000000..2d8a708505 --- /dev/null +++ b/js/copy-code.js @@ -0,0 +1,54 @@ +document.addEventListener('DOMContentLoaded', () => { + const codeBlocks = document.querySelectorAll('div.highlighter-rouge'); + + codeBlocks.forEach(codeBlock => { + const copyButton = document.createElement('button'); + copyButton.className = 'copy-code-btn'; + copyButton.innerHTML = ' Copy'; + copyButton.setAttribute('aria-label', 'Copy code to clipboard'); + + codeBlock.appendChild(copyButton); + + copyButton.addEventListener('click', async () => { + const code = codeBlock.querySelector('code'); + if (!code) return; + + try { + const textToCopy = code.textContent; + + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(textToCopy); + } else { + // Fallback for HTTP (non-secure) contexts + const textArea = document.createElement("textarea"); + textArea.value = textToCopy; + textArea.style.position = "fixed"; + textArea.style.left = "-999999px"; + textArea.style.top = "-999999px"; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { + document.execCommand('copy'); + } catch (err) { + console.error('Fallback copy failed', err); + } + textArea.remove(); + } + + // Visual feedback + const originalHtml = copyButton.innerHTML; + copyButton.innerHTML = ' Copied!'; + copyButton.classList.add('copied'); + + setTimeout(() => { + copyButton.innerHTML = originalHtml; + copyButton.classList.remove('copied'); + }, 2000); + } catch (err) { + console.error('Failed to copy text: ', err); + } + }); + }); +});