I am writing documentation for an API in org-mode, and exporting to HTML. I would like the exported HTML source blocks to have an associated button that copies the code to the clipboard, achieving something like the following: enter image description here

I suspect that I'll need to fiddle with the HTML export code, but I'm not sure where to start, exactly. For the required JS part, I'm thinking of doing something along the lines described here: https://stackoverflow.com/a/30810322/885262 and defining one button per source block, and associating the block's contents to its event listener, so that when the button is clicked, the code is copied to the clipboard.


I finally had some time to look at this again, and managed to implement a relatively simple solution, by extending the html export backend. The code to do it is:

   (require 'ox)
   (require 's)
   (require 'uuidgen)
   (defun mvr-html-src-block (src-block contents info)
     "Transcode a SRC-BLOCK element from Org to HTML, adding a 'copy to clipboard' button."
     (if (not (org-export-read-attribute :attr_html src-block :copy-button))
      (org-export-with-backend 'html src-block contents info)
      (let*((b-id (concat "btn_" (s-replace "-" "" (uuidgen-4))))
        (content (let ((print-escape-newlines t))(prin1-to-string (org-export-format-code-default src-block info))))
        (content- (s-chop-prefix "\"" (s-chop-suffix "\"" (s-replace "`" "\\`" content))))
        (btn- "button")
        (scr- "script")
        (bquote- "`")
        (script (concat "\n<" scr- " type='text/javascript'>\n var copyBtn" b-id "=document.querySelector('" btn- "[name=" b-id "]');\n"
                        "copyBtn" b-id ".addEventListener('click', function(event) {\n"
                        "copyTextToClipboard(" bquote- content- bquote- ");\n});\n</" scr- ">\n"))
        (button (concat "<" btn- " class='copyBtn' name=" b-id ">Copy to clipboard</" btn- ">")))
        (concat (org-export-with-backend 'html src-block contents info)  button script))))

   (org-export-define-derived-backend 'mvr-html 'html
     :translate-alist '((src-block . mvr-html-src-block)))

  (defun org-export-to-html-with-button (file)
  "Exports the current org-mode buffer to an HTML file, adding 'copy to clipboard' 
  buttons to source code blocks."
  (interactive "FFile Name: ")
  (org-export-to-file 'mvr-html file))

The copy button is then enabled by adding #+ATTR_HTML: :copy-button t to the relevant source blocks, like so

#+ATTR_HTML: :copy-button t

Remember to add the JS code referenced in the linked SO answer (the "complex example"), and you're good to go. The buttons are added with a "copyBtn" class, so they can be styled as needed.

Your Answer

