Skip to content
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

Adding copy to clipboard support for highlight code blocks #616

Open
djsamperi opened this issue May 6, 2024 · 4 comments
Open

Adding copy to clipboard support for highlight code blocks #616

djsamperi opened this issue May 6, 2024 · 4 comments

Comments

@djsamperi
Copy link

It would be helpful to have support for copy-to-clipboard button for fenced code blocks in
the hugo-book theme. There is a discussion on how to do this here:
https://discourse.gohugo.io/t/copy-to-clipboard-from-code-highlight-context/49633.
I managed to get the simpler (older) strategy working with hugo-book , but
not the one recommended by jmooring that employs Node.js.

@alex-shpak
Copy link
Owner

Hi!
I looked into adding copy button before, but as I try to keep everything simple I decided against it, it was too much of support for icons, code and light-dark theme,
But I can reconsider is simple (code wise) solution with arise.

@eskopp
Copy link

eskopp commented May 30, 2024

Hello @alex-shpak and @djsamperi,
I had the same problem. I made the following solution myself. The implementation is quite simple and so is the layout. Maybe you can add it to the original repo.
Greetings
@eskopp

Solution

Dark

image

Light

image

Source Code

JavaScript Code

<script>
  document.addEventListener('DOMContentLoaded', (event) => {
    document.querySelectorAll('pre code').forEach((block) => {
      const button = document.createElement('button');
      button.className = 'copy-button';
      button.textContent = 'Copy';
      button.addEventListener('click', () => {
        const range = document.createRange();
        range.selectNodeContents(block);
        const selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
        document.execCommand('copy');
        button.textContent = 'Copied!';
        setTimeout(() => { button.textContent = 'Copy'; }, 2000);
      });
      block.parentNode.insertBefore(button, block);
    });
  });
  </script>

CSS Code

.copy-button {
  position: absolute;
  right: 10px;
  top: 10px;
  padding: 5px 10px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  background-color: #6c757d;
  color: white;
  font-family: Arial, sans-serif;
  font-size: 0.875rem;
  box-shadow: 0 2px 4px rgba(0,0,0,0.15);
  transition: background-color 0.3s, box-shadow 0.3s;
}

.copy-button:hover {
  background-color: #5a6268;
  box-shadow: 0 2px 5px rgba(0,0,0,0.25);
}

pre {
  position: relative;
  background-color: #f8f9fa;
  border: 1px solid #e1e1e1;
  padding: 16px;
  border-radius: 4px;
  overflow: auto;
}

@djsamperi
Copy link
Author

djsamperi commented Aug 7, 2024

Thanks for the tip. Perhaps it can be used to implement the kind of simple solution that @alex-shpak is looking for.

One reason I had difficulty incorporating the solution proposed by jmooring (uses NPM) is that there is already partial support for a copy function in themes/hugo-book/assets/clipboard.js (instead of a copy button just click on the code to select for copy). To incorporate the code above, simply replace the function document.querySelectorAll('pre code')... in clipboard.js with the version defined above, and place the CSS above into assets/_copybutton.scss, and add to assets/_custom.scss the line @import "copybutton";.

This adds a functional copy button that works for light and dark mode.

There are two issues. 1. The button is sometimes not well separated from the code, and 2. When line numbers are turned on two copy buttons appear, one for the code, and one for the line numbers cell! Also, when line numbers are turned on they are not properly aligned with the code lines. For this to work pygmentsUseClasses should be false,
or should not appear in hugo.toml.

Another possible strategy is to remove clipboard.js from hugo-book theme, and use clipboard.js from NPM, as in jmooring's example. But this requires adding NPM support to hugo-book.

@djsamperi
Copy link
Author

djsamperi commented Aug 8, 2024

A work-around for the problem where a copy button is inserted in the line numbers block is appended below.
A work-around for the problem where line numbers and code are not aligned is to use inline line numbers (thanks to @jmooring). In markdown use {linenos=inline} , and in hugo.toml in [markup.highlight] set lineNumbersInTable=false.

JavaScript Code

   document.querySelectorAll('pre code').forEach((block) => {
	/* Without a class or data attribute assigned to the blocks, we cannot
           distinguish between line number and code blocks, so we use the following
           brute force test (assumes there are no code blocks containing only numbers!). */
	const content = block.textContent.trim();
	const isLineNumbers = content.split('\n').every(line => /^\d+$/.test(line.trim()));
	if(!isLineNumbers) {
	    const button = document.createElement('button');
	    button.className = 'copy-button';
	    button.textContent = 'Copy';
	    button.addEventListener('click', () => {
		const range = document.createRange();
		range.selectNodeContents(block);
		const selection = window.getSelection();
		selection.removeAllRanges();
		selection.addRange(range);
		document.execCommand('copy');
		button.textContent = 'Copied!';
		setTimeout(() => { button.textContent = 'Copy'; }, 2000);
	    });
	    block.parentNode.insertBefore(button, block);
	}
    });

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

No branches or pull requests

3 participants