From ad6306d7def3801cc80c01baa02ca39c6d6ff3d1 Mon Sep 17 00:00:00 2001 From: Chayan Datta Date: Thu, 11 Apr 2024 17:49:57 +0530 Subject: [PATCH] feat: add copy button for markdown code blocks (#2799) Co-authored-by: Fons van der Plas --- frontend/components/CellOutput.js | 36 +++++++++++++++++++++++++++++++ frontend/editor.css | 29 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js index 77af503d88..107e6db397 100644 --- a/frontend/components/CellOutput.js +++ b/frontend/components/CellOutput.js @@ -622,6 +622,18 @@ export let RawHTMLContainer = ({ body, className = "", persist_js_state = false, } catch (err) { console.warn("Highlighting failed", err) } + + // Find code blocks and add a copy button: + try { + if (container.firstElementChild?.matches("div.markdown")) { + container.querySelectorAll("pre > code").forEach((code_element) => { + const pre = code_element.parentElement + generateCopyCodeButton(pre) + }) + } + } catch (err) { + console.warn("Adding markdown code copy button failed", err) + } } finally { js_init_set?.delete(container) } @@ -688,3 +700,27 @@ export let highlight = (code_element, language) => { } } } + +/** + * Generates a copy button for Markdown code blocks. + */ +export const generateCopyCodeButton = (/** @type {HTMLElement?} */ pre) => { + if (!pre) return + + // create copy button + const button = document.createElement("button") + button.title = "Copy to Clipboard" + button.className = "markdown-code-block-button" + button.addEventListener("click", (e) => { + const txt = pre.textContent ?? "" + navigator.clipboard.writeText(txt) + + button.classList.add("markdown-code-block-copied-code-button") + setTimeout(() => { + button.classList.remove("markdown-code-block-copied-code-button") + }, 2000) + }) + + // Append copy button to the code block element + pre.prepend(button) +} diff --git a/frontend/editor.css b/frontend/editor.css index 0eeab2158f..9f4983f88b 100644 --- a/frontend/editor.css +++ b/frontend/editor.css @@ -3681,3 +3681,32 @@ pluto-cell.hooked_up pluto-output { justify-content: flex-end; gap: 0.5em; } + +/* Styles for markdown copy Button*/ +.markdown-code-block-button { + position: relative; + cursor: pointer; + justify-content: center; + align-items: center; + display: block; + padding: 0; + float: right; + border: none; + background: none; +} + +.markdown-code-block-button::before { + content: ""; + display: block; + width: 14px; + height: 14px; + filter: var(--image-filters); +} + +.markdown-code-block-button::before { + background-image: url("https://unpkg.com/ionicons@7.1.0/dist/svg/copy-outline.svg"); +} + +.markdown-code-block-copied-code-button::before { + background-image: url("https://unpkg.com/ionicons@7.1.0/dist/svg/checkmark-outline.svg"); +}