From c7242cc1165afd62b18827ea29b0ed8f15ad1476 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 24 Feb 2026 00:50:53 +0000 Subject: [PATCH 1/3] Add copy-to-clipboard button to code blocks in generated docs Fixes #72. Adds a 'Copy' button that appears on hover in the top-right corner of every code block (both standard Markdown fenced blocks and F# syntax-highlighted table.pre blocks). - docs/content/fsdocs-copy-button.js: new module that injects copy buttons via the Clipboard API (with execCommand fallback for non-HTTPS) - docs/content/fsdocs-default.css: styles for .copy-code-button and .code-block-wrapper (used to wrap table.pre for positioning) - docs/_template.html: load the new script - RELEASE_NOTES.md: document the addition Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- RELEASE_NOTES.md | 3 ++ docs/_template.html | 1 + docs/content/fsdocs-copy-button.js | 75 ++++++++++++++++++++++++++++++ docs/content/fsdocs-default.css | 43 ++++++++++++++++- 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 docs/content/fsdocs-copy-button.js diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 15403ad56..e70fd2c56 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Added +* Add "Copy" button to all code blocks in generated documentation, making it easy to copy code samples to the clipboard. [#72](https://github.com/fsprojects/FSharp.Formatting/issues/72) + ### Fixed * Improve error message when a named code snippet is not found (e.g. `(*** include:name ***)` with undefined name now reports the missing name clearly). [#982](https://github.com/fsprojects/FSharp.Formatting/pull/982) diff --git a/docs/_template.html b/docs/_template.html index a49523762..d8e797616 100644 --- a/docs/_template.html +++ b/docs/_template.html @@ -97,6 +97,7 @@ + {{fsdocs-body-extra}} \ No newline at end of file diff --git a/docs/content/fsdocs-copy-button.js b/docs/content/fsdocs-copy-button.js new file mode 100644 index 000000000..d1e598291 --- /dev/null +++ b/docs/content/fsdocs-copy-button.js @@ -0,0 +1,75 @@ +// Adds a "Copy" button to every code block so readers can easily copy snippets. +function createCopyButton() { + const button = document.createElement('button') + button.className = 'copy-code-button' + button.setAttribute('aria-label', 'Copy code to clipboard') + button.textContent = 'Copy' + return button +} + +function attachCopyHandler(button, getText) { + button.addEventListener('click', function () { + const text = getText() + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then( + function () { + button.textContent = 'Copied!' + setTimeout(function () { + button.textContent = 'Copy' + }, 2000) + }, + function () { + button.textContent = 'Failed' + setTimeout(function () { + button.textContent = 'Copy' + }, 2000) + } + ) + } else { + // Fallback for non-HTTPS environments + const el = document.createElement('textarea') + el.value = text + document.body.appendChild(el) + el.select() + document.execCommand('copy') + document.body.removeChild(el) + button.textContent = 'Copied!' + setTimeout(function () { + button.textContent = 'Copy' + }, 2000) + } + }) +} + +document.addEventListener('DOMContentLoaded', function () { + // table.pre blocks (F# highlighted code, sometimes with line numbers) + document.querySelectorAll('table.pre').forEach(function (table) { + const wrapper = document.createElement('div') + wrapper.className = 'code-block-wrapper' + table.parentNode.insertBefore(wrapper, table) + wrapper.appendChild(table) + + const button = createCopyButton() + wrapper.appendChild(button) + + const snippet = table.querySelector('.snippet pre') + attachCopyHandler(button, function () { + return (snippet || table).innerText + }) + }) + + // Standard pre > code blocks (Markdown fenced code, standalone fssnip, etc.) + // Skip those already handled inside table.pre above. + document.querySelectorAll('pre > code').forEach(function (code) { + if (code.closest('table.pre')) return + const pre = code.parentElement + pre.classList.add('has-copy-button') + + const button = createCopyButton() + pre.appendChild(button) + + attachCopyHandler(button, function () { + return code.innerText + }) + }) +}) diff --git a/docs/content/fsdocs-default.css b/docs/content/fsdocs-default.css index 7dea22869..6978f9aec 100644 --- a/docs/content/fsdocs-default.css +++ b/docs/content/fsdocs-default.css @@ -731,7 +731,7 @@ h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { font-size: inherit; } -table.pre, #content > pre.fssnip { +table.pre, #content > pre.fssnip, .code-block-wrapper > table.pre { border: 1px solid var(--code-fence-border-color); } @@ -740,6 +740,47 @@ table.pre, pre.fssnip.highlighted { padding: var(--spacing-200); } +/* Copy code button */ +.code-block-wrapper { + position: relative; + display: block; + margin: var(--spacing-300) 0; +} + +.code-block-wrapper > table.pre { + margin: 0; +} + +pre.has-copy-button { + position: relative; +} + +.copy-code-button { + position: absolute; + top: var(--spacing-100); + right: var(--spacing-100); + padding: var(--spacing-50) var(--spacing-100); + background-color: var(--header-background); + border: 1px solid var(--header-border); + border-radius: var(--radius); + color: var(--text-color); + cursor: pointer; + font-family: var(--system-font); + font-size: var(--font-50); + opacity: 0; + transition: opacity 0.2s ease-in-out; + z-index: 10; + + &:hover { + background-color: var(--menu-item-hover-background); + } +} + +pre.has-copy-button:hover .copy-code-button, +.code-block-wrapper:hover .copy-code-button { + opacity: 1; +} + table.pre .snippet pre.fssnip { padding: 0; margin: 0; From 63efe2048353bd880ee97ac7d5a6aae639da892c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 00:49:12 +0000 Subject: [PATCH 2/3] Use navigator.clipboard in Clipboard_CopyTo with execCommand fallback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/content/fsdocs-tips.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/content/fsdocs-tips.js b/docs/content/fsdocs-tips.js index 787e183f4..873882535 100644 --- a/docs/content/fsdocs-tips.js +++ b/docs/content/fsdocs-tips.js @@ -45,12 +45,16 @@ function showTip(evt, name, unique, owner) { } function Clipboard_CopyTo(value) { - const tempInput = document.createElement("input"); - tempInput.value = value; - document.body.appendChild(tempInput); - tempInput.select(); - document.execCommand("copy"); - document.body.removeChild(tempInput); + if (navigator.clipboard) { + navigator.clipboard.writeText(value); + } else { + const tempInput = document.createElement("input"); + tempInput.value = value; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand("copy"); + document.body.removeChild(tempInput); + } } window.showTip = showTip; From a4214b3484dc31c4fb7edc2ecc223b3d6822928f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 26 Feb 2026 00:50:35 +0000 Subject: [PATCH 3/3] ci: trigger CI checks