Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [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)
* Add `<FsDocsAllowExecutableProject>true</FsDocsAllowExecutableProject>` project file setting to include executable projects (OutputType=Exe/WinExe) in API documentation generation. [#918](https://github.com/fsprojects/FSharp.Formatting/issues/918)
* Add `{{fsdocs-logo-alt}}` substitution (configurable via `<FsDocsLogoAlt>` MSBuild property, defaults to `Logo`) for accessible alt text on the header logo image. [#626](https://github.com/fsprojects/FSharp.Formatting/issues/626)

Expand Down
1 change: 1 addition & 0 deletions docs/_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
<script type="module" src="{{root}}content/fsdocs-details-toggle.js"></script>
<script type="module" src="{{root}}content/fsdocs-theme.js"></script>
<script type="module" src="{{root}}content/fsdocs-search.js"></script>
<script type="module" src="{{root}}content/fsdocs-copy-button.js"></script>
{{fsdocs-body-extra}}
</body>
</html>
75 changes: 75 additions & 0 deletions docs/content/fsdocs-copy-button.js
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/repo-assist while on topic, please update docs/content/fsdocs-tips.js as well to use navigator.clipboard.

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
})
})
})
43 changes: 42 additions & 1 deletion docs/content/fsdocs-default.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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;
Expand Down
16 changes: 10 additions & 6 deletions docs/content/fsdocs-tips.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down