Skip to content

Add copy-to-clipboard support to code blocks #961

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 20, 2025

Conversation

DebugSteven
Copy link
Contributor

@DebugSteven DebugSteven commented Aug 6, 2025

Summary

This PR adds support for a new copy-to-clipboard button in code blocks. When this feature is enabled through the enable-experimental-code-block flag, a copy-to-clipboard button is rendered in the top-right corner of all code blocks, allowing users to easily copy its contents. When an author does not want the contents of a code block to be copyable, a nocopy option can be used to disable the copy-to-clipboard button.

User Experience

When the enable-experimental-code-block flag is used, a copy button will appear in the top-right corner of all code blocks. Clicking the button copies the full contents of the code block to the clipboard and displays a checkmark to confirm success. If a code block includes the nocopy keyword in the language line, the copy button will not appear on that code block.

Implementation Overview

  • In swift-docc-render, this adds copyToClipboard to the properties on BlockType.codeListing.
  • Adds a copy-to-clipboard button in CodeListing.vue, similar to swift.org’s copy-to-clipboard button on its code blocks, when copyToClipboard is set to true from the feature flag enable-experimental-code-block. Includes a function, copyCodeToClipboard, to copy the contents of a code block to clipboard.
  • Adding ```nocopy disables the copy button on that code block.
  • This flag is forwarded from swift-docc. The accompanying branch/PR is here: Add copy-to-clipboard support to code blocks swift-docc#1273

Dependencies

This PR depends on associated changes in swift-docc to pass the parameter for the copy button.

Testing

Setup

  1. Use the codeblock-copy branches for swift-docc and swift-docc-render with the copy-to-clipboard changes.
  2. Rebuild documentation using swift-docc with the feature flag enable-experimental-code-block and serve it using a local build of swift-docc-render.

How to Test

  1. In all code listings with the enable-experimental-code-block flag, a copy button will appear in the top-right corner.
  2. Click the copy icon. The code should be copied to your clipboard and the icon should update to a checkmark briefly. Paste to verify the copy functionality works.

To disable the copy icon with the feature flag enabled:

  1. In any code listing using ``` or by adding a code listing, add the nocopy option like this:
```swift, nocopy
print(“Hello, world!”)
```

or like this:

```nocopy
print(“Hello, world!”)
```


2. Verify the copy button does not appear on this code block.
Screenshot 2025-08-11 at 5 47 54 PM

Checklist

  • Added tests
  • Ran npm test, and it succeeded
  • Updated documentation if necessary – On my DocC PR, I updated RenderNode.spec.json to include copyToClipboard as a property on CodeListing. Please let me know if there's any other documentation I should update here.

show on hover
always show copy button mobile

@heckj
Copy link
Member

heckj commented Aug 6, 2025

@swift-ci please test

Copy link
Contributor

@mportiz08 mportiz08 left a comment

Choose a reason for hiding this comment

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

Hi @DebugSteven :) I'm really excited to see you bringing this functionality to DocC and DocC-Render, and I especially love the minimal/additive syntax proposed to make this a progressive, opt-in enhancement for DocC code listings.

Unfortunately, I'm about to be offline for a week, so I won't have time to properly test/review this PR during that time.

However, I took a brief look at the implementation and have a suggestion for a simpler way to position the icon/button that maybe you could explore—I promise to take a more complete look at this PR when I'm back online either way—sorry again for the bad timing on my end!

@heckj
Copy link
Member

heckj commented Aug 12, 2025

@swift-ci please test

Copy link
Contributor

@mportiz08 mportiz08 left a comment

Choose a reason for hiding this comment

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

This is looking really great! I'm glad that the absolute positioning approach simplified things and is working out well.

I have some feedback after looking this over more closely and testing it out, but my comments are mostly pretty minor suggestions.

Thank you so much for looking into this!

@@ -22,6 +22,33 @@
>{{ fileName }}
</Filename>
<div class="container-general">
<button
Copy link
Contributor

Choose a reason for hiding this comment

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

Design-wise, I think the button can be a little distracting for code listings with a long first-line of code since the button always shows directly over the text (when enabled).

If we were to enable this button by default for code listings (which I personally think would be great), we may want to consider only showing it when hovering over the listing to try and eliminate some of that distraction maybe? That's just my personal opinion though. We could also consider a different layout where the button doesn't appear directly over the text as an alternative, although the positioning could get tricky in that case.

As a concrete example, 2 of the 3 code listings on this screenshot from the PR description are obscured by the button, regardless of where the user focus is:
476812913-bbc26127-bcb7-43b0-84f6-c52a95985040

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that it’s nicer to show the copy button only when hovering over code blocks. Originally I had the button only show when hovering over code blocks. However, Joe and I got some feedback on the forums that it’d be better for mobile use to have the button always visible.

I think I’ve got a decent solution here in my latest commit. I’m using @media (hover: hover) for devices that support hover and I added @media (hover: none), when hover isn’t supported, to set the button to be always visible. Let me know what you think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to share the same pictures from the forums, here's what those changes look like on desktop and mobile.
show on hover
always show button mobile

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that's a pretty reasonable compromise for now, especially since this is only enabled with an experimental flag for now.

I think we may want to consider disabling it entirely or using an alternate layout for mobile/non-hover devices in the future due to the smaller benefit there weighed against the downside of how it obscures a lot of content.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice work! Glad to see the discussion on the forums.

Copy link
Contributor

@mportiz08 mportiz08 left a comment

Choose a reason for hiding this comment

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

Awesome work! I think this is almost ready to go—just have a small suggested change for the icon imports.

data() {
return {
syntaxHighlightedLines: [],
copyState: 'idle',
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a blocker, but it might be nice to have constants for each of the distinct states to make remembering the literal string values a little less fragile.

Something like:

const CopyState = {
  idle: 'idle',
  success: 'success',
  failure: 'failure',
};

Copy link
Contributor

@mportiz08 mportiz08 left a comment

Choose a reason for hiding this comment

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

📋✅

Nice work @DebugSteven!

- Show copy-to-clipboard button on hover for devices that support it
- Persistently display button on non-hover devices
- Add copyCodeToClipboard function to write code block text to clipboard
- Add Copy/Checkmark/Cross icons with CopyState for state-specific styling
@heckj
Copy link
Member

heckj commented Aug 20, 2025

@swift-ci please test

@heckj heckj merged commit 2a44318 into swiftlang:main Aug 20, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants