From b3ec6a27f74c1477c066c5d76b1d97f25ae63fd5 Mon Sep 17 00:00:00 2001 From: Chayan Datta Date: Thu, 1 Feb 2024 18:54:12 +0530 Subject: [PATCH 01/19] feat: add copy button --- frontend/components/CellOutput.js | 52 ++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js index b2a6244b88..470170740e 100644 --- a/frontend/components/CellOutput.js +++ b/frontend/components/CellOutput.js @@ -164,13 +164,51 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last if (body.startsWith("` } else { - return html`<${RawHTMLContainer} - cell_id=${cell_id} - body=${body} - persist_js_state=${persist_js_state} - last_run_timestamp=${last_run_timestamp} - sanitize_html=${sanitize_html} - />` + let copyCodeButton = html`` + if (body.startsWith('
([\s\S]*?)<\/code><\/pre>\n<\/div>/)
+
+                        if (contentMatch) {
+                            const content = contentMatch[2].trim()
+                            console.log(content)
+                            return content
+                        } else {
+                            return null
+                        }
+                    }
+
+                    const copyToClipboard = () => {
+                        
+                        var txt = document.createElement("textarea")
+                        txt.innerHTML = body
+                        navigator.clipboard.writeText(extractCode(txt.value))
+                        console.log(extractCode(txt.value))
+                    }
+
+                    const buttonStyle = {
+                        position: "absolute",
+                        top: 10,
+                        right: 20,
+                        cursor: "pointer",
+                    }
+
+                    copyCodeButton = html``
+                }
+
+                return html`
+ ${copyCodeButton} + <${RawHTMLContainer} + cell_id=${cell_id} + body=${body} + persist_js_state=${persist_js_state} + last_run_timestamp=${last_run_timestamp} + sanitize_html=${sanitize_html} + /> +
` } break case "application/vnd.pluto.tree+object": From afd0ee88502878b896c167ebbd022d55e5e2b372 Mon Sep 17 00:00:00 2001 From: Chayan Datta Date: Fri, 2 Feb 2024 14:17:58 +0530 Subject: [PATCH 02/19] removed consoles --- frontend/components/CellOutput.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js index 470170740e..64760fd0fa 100644 --- a/frontend/components/CellOutput.js +++ b/frontend/components/CellOutput.js @@ -167,12 +167,10 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last let copyCodeButton = html`` if (body.startsWith('
([\s\S]*?)<\/code><\/pre>\n<\/div>/)
 
                         if (contentMatch) {
                             const content = contentMatch[2].trim()
-                            console.log(content)
                             return content
                         } else {
                             return null
@@ -180,11 +178,9 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last
                     }
 
                     const copyToClipboard = () => {
-                        
                         var txt = document.createElement("textarea")
                         txt.innerHTML = body
                         navigator.clipboard.writeText(extractCode(txt.value))
-                        console.log(extractCode(txt.value))
                     }
 
                     const buttonStyle = {

From be1d90f8168d50f68f3dfbb03e90971a75064c3b Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Fri, 2 Feb 2024 15:10:23 +0530
Subject: [PATCH 03/19] chore: move css files

---
 frontend/components/CellOutput.js | 11 ++---------
 frontend/editor.css               | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 64760fd0fa..32b60dc380 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -183,15 +183,8 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last
                         navigator.clipboard.writeText(extractCode(txt.value))
                     }
 
-                    const buttonStyle = {
-                        position: "absolute",
-                        top: 10,
-                        right: 20,
-                        cursor: "pointer",
-                    }
-
-                    copyCodeButton = html``
                 }
 
diff --git a/frontend/editor.css b/frontend/editor.css
index 1b1c7271fe..9cfb2a610d 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3597,3 +3597,22 @@ pluto-cell.hooked_up pluto-output {
     justify-content: flex-end;
     gap: 0.5em;
 }
+
+/*  Styles for mark down copy Button*/
+.markdown-code-block-copy-code-button {
+    position: absolute;
+    top: 10px;
+    right: 20px;
+    cursor: pointer;
+    width: 20px;
+    height: 20px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    padding: 0;
+}
+
+.markdown-code-block-copy-code-button > img {
+    width: 14px;
+    height: 14px;
+}
\ No newline at end of file

From 4820697bacee431bb7675ad38702fdb2caa9f3ba Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Fri, 2 Feb 2024 15:12:42 +0530
Subject: [PATCH 04/19] chore: added EOL

---
 frontend/editor.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frontend/editor.css b/frontend/editor.css
index 9cfb2a610d..372b2f5c95 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3615,4 +3615,4 @@ pluto-cell.hooked_up pluto-output {
 .markdown-code-block-copy-code-button > img {
     width: 14px;
     height: 14px;
-}
\ No newline at end of file
+}

From b33b4e3d0c5cc8b367a0d01823ee996cca2ad511 Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Thu, 8 Feb 2024 12:20:31 +0530
Subject: [PATCH 05/19] chore: change variable to const

---
 frontend/components/CellOutput.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 32b60dc380..d53dac1ce9 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -178,7 +178,7 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last
                     }
 
                     const copyToClipboard = () => {
-                        var txt = document.createElement("textarea")
+                        const txt = document.createElement("textarea")
                         txt.innerHTML = body
                         navigator.clipboard.writeText(extractCode(txt.value))
                     }

From 6d63ed9b6f3911abd57a8dc9236adee20ad32d72 Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Thu, 8 Feb 2024 12:51:00 +0530
Subject: [PATCH 06/19] chore: modularize the generateCopyCodeButton

---
 frontend/components/CellOutput.js | 61 ++++++++++++++++++-------------
 1 file changed, 36 insertions(+), 25 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index d53dac1ce9..ce35705c0d 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -164,32 +164,8 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last
             if (body.startsWith("`
             } else {
-                let copyCodeButton = html``
-                if (body.startsWith('
([\s\S]*?)<\/code><\/pre>\n<\/div>/)
-
-                        if (contentMatch) {
-                            const content = contentMatch[2].trim()
-                            return content
-                        } else {
-                            return null
-                        }
-                    }
-
-                    const copyToClipboard = () => {
-                        const txt = document.createElement("textarea")
-                        txt.innerHTML = body
-                        navigator.clipboard.writeText(extractCode(txt.value))
-                    }
-
-                    copyCodeButton = html``
-                }
-
                 return html`
- ${copyCodeButton} + ${generateCopyCodeButton(body)} <${RawHTMLContainer} cell_id=${cell_id} body=${body} @@ -710,3 +686,38 @@ export let highlight = (code_element, language) => { } } } + +/** + * Generates a copy button for Markdown code blocks and copies them into the clipboard. + * @param {string} body - The Markdown content. + */ +export const generateCopyCodeButton = (body) => { + let copyCodeButton = html`` + + if (body.startsWith('
([\s\S]*?)<\/code><\/pre>\n<\/div>/)
+
+            if (contentMatch) {
+                const content = contentMatch[2].trim()
+                return content
+            } else {
+                return null
+            }
+        }
+
+        const copyToClipboard = () => {
+            const txt = document.createElement("textarea")
+            txt.innerHTML = body
+            navigator.clipboard.writeText(extractCode(txt.value))
+        }
+
+        copyCodeButton = html`
+            
+        `
+    }
+
+    return copyCodeButton
+}

From 15ca9051cef14d2708091091f8aa52c25e736501 Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Thu, 8 Feb 2024 18:50:02 +0530
Subject: [PATCH 07/19] chore: move generateCopyCodeButton inside
 RawHTMLContainer

---
 frontend/components/CellOutput.js | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index ce35705c0d..f9ac7478db 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -164,16 +164,14 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last
             if (body.startsWith("`
             } else {
-                return html`
- ${generateCopyCodeButton(body)} + return html` <${RawHTMLContainer} cell_id=${cell_id} body=${body} persist_js_state=${persist_js_state} last_run_timestamp=${last_run_timestamp} sanitize_html=${sanitize_html} - /> -
` + />` } break case "application/vnd.pluto.tree+object": @@ -631,7 +629,12 @@ export let RawHTMLContainer = ({ body, className = "", persist_js_state = false, } }, [body, last_run_timestamp, pluto_actions, sanitize_html]) - return html`
` + return html` +
+ ${generateCopyCodeButton(body)} +
+
+ ` } // https://github.com/fonsp/Pluto.jl/issues/1692 From 492c8f81f27cb42149130037c16e7703a62c3249 Mon Sep 17 00:00:00 2001 From: Chayan Datta Date: Thu, 8 Feb 2024 19:09:28 +0530 Subject: [PATCH 08/19] chore: lint --- frontend/components/CellOutput.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js index f9ac7478db..fd40597127 100644 --- a/frontend/components/CellOutput.js +++ b/frontend/components/CellOutput.js @@ -164,14 +164,13 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last if (body.startsWith("` } else { - return html` - <${RawHTMLContainer} - cell_id=${cell_id} - body=${body} - persist_js_state=${persist_js_state} - last_run_timestamp=${last_run_timestamp} - sanitize_html=${sanitize_html} - />` + return html`<${RawHTMLContainer} + cell_id=${cell_id} + body=${body} + persist_js_state=${persist_js_state} + last_run_timestamp=${last_run_timestamp} + sanitize_html=${sanitize_html} + />` } break case "application/vnd.pluto.tree+object": @@ -629,12 +628,10 @@ export let RawHTMLContainer = ({ body, className = "", persist_js_state = false, } }, [body, last_run_timestamp, pluto_actions, sanitize_html]) - return html` -
- ${generateCopyCodeButton(body)} -
-
- ` + return html`
+ ${generateCopyCodeButton(body)} +
+
` } // https://github.com/fonsp/Pluto.jl/issues/1692 From d39a198023f2616dc0d1a9df43571253e62ec981 Mon Sep 17 00:00:00 2001 From: Chayan Datta Date: Fri, 9 Feb 2024 01:03:05 +0530 Subject: [PATCH 09/19] chore: changes --- frontend/components/CellOutput.js | 73 +++++++++++++++++++------------ 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js index fd40597127..bff1922bfc 100644 --- a/frontend/components/CellOutput.js +++ b/frontend/components/CellOutput.js @@ -617,6 +617,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) } @@ -628,10 +640,7 @@ export let RawHTMLContainer = ({ body, className = "", persist_js_state = false, } }, [body, last_run_timestamp, pluto_actions, sanitize_html]) - return html`
- ${generateCopyCodeButton(body)} -
-
` + return html`
` } // https://github.com/fonsp/Pluto.jl/issues/1692 @@ -689,35 +698,41 @@ export let highlight = (code_element, language) => { /** * Generates a copy button for Markdown code blocks and copies them into the clipboard. - * @param {string} body - The Markdown content. + * @param {HTMLElement | null} pre - The
 element containing the code block.
  */
-export const generateCopyCodeButton = (body) => {
-    let copyCodeButton = html``
+export const generateCopyCodeButton = (pre) => {
+    if (!pre) {
+        console.error('Error: pre is null.');
+        return;
+    }
 
-    if (body.startsWith('
([\s\S]*?)<\/code><\/pre>\n<\/div>/)
+    const copyToClipboard = () => {
+        const range = document.createRange()
+        range.selectNode(pre)
+        const txt = range.startContainer.textContent || ''
+        navigator.clipboard
+            .writeText(txt)
+            .then(() => {
+                console.log("Code copied to clipboard:\n" + txt)
+            })
+            .catch((error) => {
+                console.error("Error copying code to clipboard:", error)
+            })
+    }
 
-            if (contentMatch) {
-                const content = contentMatch[2].trim()
-                return content
-            } else {
-                return null
-            }
-        }
+    // create copy button
+    const copyCodeButton = document.createElement("button")
+    copyCodeButton.className = "markdown-code-block-copy-code-button"
+    copyCodeButton.addEventListener("click", copyToClipboard)
 
-        const copyToClipboard = () => {
-            const txt = document.createElement("textarea")
-            txt.innerHTML = body
-            navigator.clipboard.writeText(extractCode(txt.value))
-        }
+    // Create copy button image
+    const copyImg = document.createElement("img")
+    copyImg.src = "https://unpkg.com/ionicons@7.1.0/dist/svg/copy-outline.svg"
+    copyImg.alt = "Copy to Clipboard"
 
-        copyCodeButton = html`
-            
-        `
-    }
+    // Append image to button
+    copyCodeButton.appendChild(copyImg)
 
-    return copyCodeButton
+    // Append copy button to the code block element
+    pre.appendChild(copyCodeButton)
 }

From f9dc130eff965a1ad3ee7fb44f2e63e11198bad2 Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Fri, 9 Feb 2024 15:15:14 +0530
Subject: [PATCH 10/19] chore: refactor generateCopyCodeButton

---
 frontend/components/CellOutput.js | 41 +++++++++++++++++++------------
 1 file changed, 25 insertions(+), 16 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index bff1922bfc..6cbb36453b 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -696,6 +696,29 @@ export let highlight = (code_element, language) => {
     }
 }
 
+/**
+ * Copies the contents of an HTML Element into the clipboard
+ * @param {HTMLElement | null} ele - The code block.
+ */
+export const copyToClipboard = (ele) => {
+    if (!ele) {
+        console.error('Error: pre is null.');
+        return;
+    }
+
+    const range = document.createRange()
+    range.selectNode(ele)
+    const txt = range.startContainer.textContent || ''
+    navigator.clipboard
+        .writeText(txt)
+        .then(() => {
+            console.log("Code copied to clipboard:\n" + txt)
+        })
+        .catch((error) => {
+            console.error("Error copying code to clipboard:", error)
+        })
+}
+
 /**
  * Generates a copy button for Markdown code blocks and copies them into the clipboard.
  * @param {HTMLElement | null} pre - The 
 element containing the code block.
@@ -706,28 +729,14 @@ export const generateCopyCodeButton = (pre) => {
         return;
     }
 
-    const copyToClipboard = () => {
-        const range = document.createRange()
-        range.selectNode(pre)
-        const txt = range.startContainer.textContent || ''
-        navigator.clipboard
-            .writeText(txt)
-            .then(() => {
-                console.log("Code copied to clipboard:\n" + txt)
-            })
-            .catch((error) => {
-                console.error("Error copying code to clipboard:", error)
-            })
-    }
-
     // create copy button
     const copyCodeButton = document.createElement("button")
     copyCodeButton.className = "markdown-code-block-copy-code-button"
-    copyCodeButton.addEventListener("click", copyToClipboard)
+    copyCodeButton.addEventListener("click", () => copyToClipboard(pre))
 
     // Create copy button image
     const copyImg = document.createElement("img")
-    copyImg.src = "https://unpkg.com/ionicons@7.1.0/dist/svg/copy-outline.svg"
+    copyImg.src = new URL("https://unpkg.com/ionicons@7.1.0/dist/svg/copy-outline.svg", import.meta.url).toString()
     copyImg.alt = "Copy to Clipboard"
 
     // Append image to button

From 4d09d337a2a746226b15f9e91d250ea1cda552a6 Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Fri, 9 Feb 2024 15:29:44 +0530
Subject: [PATCH 11/19] chore: changes

---
 frontend/components/CellOutput.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 6cbb36453b..e05718baa0 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -698,11 +698,11 @@ export let highlight = (code_element, language) => {
 
 /**
  * Copies the contents of an HTML Element into the clipboard
- * @param {HTMLElement | null} ele - The code block.
+ * @param {HTMLElement | null} ele - The HTML Element.
  */
 export const copyToClipboard = (ele) => {
     if (!ele) {
-        console.error('Error: pre is null.');
+        console.error('Error: The element is null.');
         return;
     }
 

From b651aa5b8f05b56783808d7a47dab3590bce1c37 Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Fri, 9 Feb 2024 17:14:19 +0530
Subject: [PATCH 12/19] chore: changes

---
 frontend/components/CellOutput.js | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index e05718baa0..6705d809aa 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -698,16 +698,11 @@ export let highlight = (code_element, language) => {
 
 /**
  * Copies the contents of an HTML Element into the clipboard
- * @param {HTMLElement | null} ele - The HTML Element.
  */
-export const copyToClipboard = (ele) => {
-    if (!ele) {
-        console.error('Error: The element is null.');
-        return;
-    }
-
+export const copyToClipboard = (e) => {
+    const el = e.target.parentNode.parentNode.closest("pre")
     const range = document.createRange()
-    range.selectNode(ele)
+    range.selectNode(el)
     const txt = range.startContainer.textContent || ''
     navigator.clipboard
         .writeText(txt)
@@ -732,7 +727,7 @@ export const generateCopyCodeButton = (pre) => {
     // create copy button
     const copyCodeButton = document.createElement("button")
     copyCodeButton.className = "markdown-code-block-copy-code-button"
-    copyCodeButton.addEventListener("click", () => copyToClipboard(pre))
+    copyCodeButton.addEventListener("click", copyToClipboard)
 
     // Create copy button image
     const copyImg = document.createElement("img")

From 7f58e893503cd8338ca377b9a557d79b570c80ad Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Wed, 13 Mar 2024 16:12:34 +0530
Subject: [PATCH 13/19] few fixes

---
 frontend/components/CellOutput.js | 14 ++------------
 frontend/editor.css               | 18 +++++++++++++-----
 2 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index fc5f25352a..714faa9a2a 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -705,10 +705,8 @@ export let highlight = (code_element, language) => {
  * Copies the contents of an HTML Element into the clipboard
  */
 export const copyToClipboard = (e) => {
-    const el = e.target.parentNode.parentNode.closest("pre")
-    const range = document.createRange()
-    range.selectNode(el)
-    const txt = range.startContainer.textContent || ''
+    const el = e.target.parentNode.closest("pre")
+    const txt = el.textContent || '';
     navigator.clipboard
         .writeText(txt)
         .then(() => {
@@ -734,14 +732,6 @@ export const generateCopyCodeButton = (pre) => {
     copyCodeButton.className = "markdown-code-block-copy-code-button"
     copyCodeButton.addEventListener("click", copyToClipboard)
 
-    // Create copy button image
-    const copyImg = document.createElement("img")
-    copyImg.src = new URL("https://unpkg.com/ionicons@7.1.0/dist/svg/copy-outline.svg", import.meta.url).toString()
-    copyImg.alt = "Copy to Clipboard"
-
-    // Append image to button
-    copyCodeButton.appendChild(copyImg)
-
     // Append copy button to the code block element
     pre.appendChild(copyCodeButton)
 }
diff --git a/frontend/editor.css b/frontend/editor.css
index 8e9ead3e45..da0ff3fe58 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3664,11 +3664,9 @@ pluto-cell.hooked_up pluto-output {
     gap: 0.5em;
 }
 
-/*  Styles for mark down copy Button*/
+/*  Styles for markdown copy Button*/
 .markdown-code-block-copy-code-button {
-    position: absolute;
-    top: 10px;
-    right: 20px;
+    position: relative; /* Change to relative */
     cursor: pointer;
     width: 20px;
     height: 20px;
@@ -3676,9 +3674,19 @@ pluto-cell.hooked_up pluto-output {
     justify-content: center;
     align-items: center;
     padding: 0;
+    float: right;
+    top: -20px;
+    right: -8px;
 }
 
-.markdown-code-block-copy-code-button > img {
+.markdown-code-block-copy-code-button::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    right: 0;
     width: 14px;
     height: 14px;
+    background-image: url('https://unpkg.com/ionicons@7.1.0/dist/svg/copy-outline.svg');
+    background-size: contain;
+    background-repeat: no-repeat;
 }

From 6bf4dc501fcf26ca3d07ea2155413b0b54230768 Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Wed, 13 Mar 2024 16:47:31 +0530
Subject: [PATCH 14/19] more changes

---
 frontend/components/CellOutput.js | 13 +++++++++++--
 frontend/editor.css               | 17 +++++++++++++----
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 714faa9a2a..5791020800 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -729,9 +729,18 @@ export const generateCopyCodeButton = (pre) => {
 
     // create copy button
     const copyCodeButton = document.createElement("button")
-    copyCodeButton.className = "markdown-code-block-copy-code-button"
-    copyCodeButton.addEventListener("click", copyToClipboard)
+    copyCodeButton.className = "markdown-code-block-button"
+    copyCodeButton.classList.add('markdown-code-block-copy-code-button')
+    copyCodeButton.addEventListener("click", function(e) {
+        copyToClipboard(e);
+        copyCodeButton.classList.add('markdown-code-block-copied-code-button');
+        setTimeout(function() {
+            copyCodeButton.classList.remove('markdown-code-block-copied-code-button');
+            copyCodeButton.classList.add('markdown-code-block-copy-code-button');
+        }, 2000);
+    });
 
     // Append copy button to the code block element
     pre.appendChild(copyCodeButton)
 }
+
diff --git a/frontend/editor.css b/frontend/editor.css
index da0ff3fe58..62400cffbf 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3665,8 +3665,8 @@ pluto-cell.hooked_up pluto-output {
 }
 
 /*  Styles for markdown copy Button*/
-.markdown-code-block-copy-code-button {
-    position: relative; /* Change to relative */
+.markdown-code-block-button {
+    position: relative;
     cursor: pointer;
     width: 20px;
     height: 20px;
@@ -3679,14 +3679,23 @@ pluto-cell.hooked_up pluto-output {
     right: -8px;
 }
 
-.markdown-code-block-copy-code-button::before {
+.markdown-code-block-copy-code-button::before,
+.markdown-code-block-copied-code-button::before {
     content: '';
     position: absolute;
     top: 0;
     right: 0;
     width: 14px;
     height: 14px;
-    background-image: url('https://unpkg.com/ionicons@7.1.0/dist/svg/copy-outline.svg');
     background-size: contain;
     background-repeat: no-repeat;
 }
+
+.markdown-code-block-copy-code-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');
+}
+

From c65336e85b1131577a433ccf3d59d0e1e634cd0b Mon Sep 17 00:00:00 2001
From: Chayan Datta 
Date: Wed, 13 Mar 2024 17:09:27 +0530
Subject: [PATCH 15/19] styling changes

---
 frontend/components/CellOutput.js | 3 ++-
 frontend/editor.css               | 8 ++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 5791020800..6b2331b209 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -729,6 +729,8 @@ export const generateCopyCodeButton = (pre) => {
 
     // create copy button
     const copyCodeButton = document.createElement("button")
+    copyCodeButton.title="Copy to Clipboard"
+    copyCodeButton.id="copy-to-clipboard-btn"
     copyCodeButton.className = "markdown-code-block-button"
     copyCodeButton.classList.add('markdown-code-block-copy-code-button')
     copyCodeButton.addEventListener("click", function(e) {
@@ -743,4 +745,3 @@ export const generateCopyCodeButton = (pre) => {
     // Append copy button to the code block element
     pre.appendChild(copyCodeButton)
 }
-
diff --git a/frontend/editor.css b/frontend/editor.css
index 62400cffbf..9a769ac03b 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3677,6 +3677,8 @@ pluto-cell.hooked_up pluto-output {
     float: right;
     top: -20px;
     right: -8px;
+    border: none;
+    background: none;
 }
 
 .markdown-code-block-copy-code-button::before,
@@ -3699,3 +3701,9 @@ pluto-cell.hooked_up pluto-output {
     background-image: url('https://unpkg.com/ionicons@7.1.0/dist/svg/checkmark-outline.svg');
 }
 
+/* Invert markdown copy button on theme toggle */
+@media (prefers-color-scheme: dark) {
+    #copy-to-clipboard-btn {
+      filter: invert(1);
+    }
+  }

From dc80390e591c813f3537d5650e1df3526536972d Mon Sep 17 00:00:00 2001
From: Fons van der Plas 
Date: Thu, 11 Apr 2024 15:08:43 +0300
Subject: [PATCH 16/19] run vs code formatter

---
 frontend/components/CellOutput.js | 28 ++++++++++++++--------------
 frontend/editor.css               | 10 +++++-----
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 6b2331b209..01fc50abde 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -706,7 +706,7 @@ export let highlight = (code_element, language) => {
  */
 export const copyToClipboard = (e) => {
     const el = e.target.parentNode.closest("pre")
-    const txt = el.textContent || '';
+    const txt = el.textContent || ""
     navigator.clipboard
         .writeText(txt)
         .then(() => {
@@ -723,24 +723,24 @@ export const copyToClipboard = (e) => {
  */
 export const generateCopyCodeButton = (pre) => {
     if (!pre) {
-        console.error('Error: pre is null.');
-        return;
+        console.error("Error: pre is null.")
+        return
     }
 
     // create copy button
     const copyCodeButton = document.createElement("button")
-    copyCodeButton.title="Copy to Clipboard"
-    copyCodeButton.id="copy-to-clipboard-btn"
+    copyCodeButton.title = "Copy to Clipboard"
+    copyCodeButton.id = "copy-to-clipboard-btn"
     copyCodeButton.className = "markdown-code-block-button"
-    copyCodeButton.classList.add('markdown-code-block-copy-code-button')
-    copyCodeButton.addEventListener("click", function(e) {
-        copyToClipboard(e);
-        copyCodeButton.classList.add('markdown-code-block-copied-code-button');
-        setTimeout(function() {
-            copyCodeButton.classList.remove('markdown-code-block-copied-code-button');
-            copyCodeButton.classList.add('markdown-code-block-copy-code-button');
-        }, 2000);
-    });
+    copyCodeButton.classList.add("markdown-code-block-copy-code-button")
+    copyCodeButton.addEventListener("click", (e) => {
+        copyToClipboard(e)
+        copyCodeButton.classList.add("markdown-code-block-copied-code-button")
+        setTimeout(() => {
+            copyCodeButton.classList.remove("markdown-code-block-copied-code-button")
+            copyCodeButton.classList.add("markdown-code-block-copy-code-button")
+        }, 2000)
+    })
 
     // Append copy button to the code block element
     pre.appendChild(copyCodeButton)
diff --git a/frontend/editor.css b/frontend/editor.css
index 9a769ac03b..8090d01428 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3683,10 +3683,10 @@ pluto-cell.hooked_up pluto-output {
 
 .markdown-code-block-copy-code-button::before,
 .markdown-code-block-copied-code-button::before {
-    content: '';
     position: absolute;
     top: 0;
     right: 0;
+    content: "";
     width: 14px;
     height: 14px;
     background-size: contain;
@@ -3694,16 +3694,16 @@ pluto-cell.hooked_up pluto-output {
 }
 
 .markdown-code-block-copy-code-button::before {
-    background-image: url('https://unpkg.com/ionicons@7.1.0/dist/svg/copy-outline.svg');
+    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');
+    background-image: url("https://unpkg.com/ionicons@7.1.0/dist/svg/checkmark-outline.svg");
 }
 
 /* Invert markdown copy button on theme toggle */
 @media (prefers-color-scheme: dark) {
     #copy-to-clipboard-btn {
-      filter: invert(1);
+        filter: invert(1);
     }
-  }
+}

From 62411a9c43ed5fec1608fab9e118f9898a255d39 Mon Sep 17 00:00:00 2001
From: Fons van der Plas 
Date: Thu, 11 Apr 2024 15:08:54 +0300
Subject: [PATCH 17/19] remove unused styles

---
 frontend/editor.css | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/frontend/editor.css b/frontend/editor.css
index 8090d01428..f4593957ed 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3668,11 +3668,9 @@ pluto-cell.hooked_up pluto-output {
 .markdown-code-block-button {
     position: relative;
     cursor: pointer;
-    width: 20px;
-    height: 20px;
-    display: flex;
     justify-content: center;
     align-items: center;
+    display: block;
     padding: 0;
     float: right;
     top: -20px;
@@ -3683,14 +3681,10 @@ pluto-cell.hooked_up pluto-output {
 
 .markdown-code-block-copy-code-button::before,
 .markdown-code-block-copied-code-button::before {
-    position: absolute;
-    top: 0;
-    right: 0;
     content: "";
+    display: block;
     width: 14px;
     height: 14px;
-    background-size: contain;
-    background-repeat: no-repeat;
 }
 
 .markdown-code-block-copy-code-button::before {

From 69fbeb881d4c8fe56d4f55be2a39f1e9b3f9e70a Mon Sep 17 00:00:00 2001
From: Fons van der Plas 
Date: Thu, 11 Apr 2024 15:09:02 +0300
Subject: [PATCH 18/19] prepend instead of append

---
 frontend/components/CellOutput.js | 2 +-
 frontend/editor.css               | 2 --
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 01fc50abde..8ab00e5ee0 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -743,5 +743,5 @@ export const generateCopyCodeButton = (pre) => {
     })
 
     // Append copy button to the code block element
-    pre.appendChild(copyCodeButton)
+    pre.prepend(copyCodeButton)
 }
diff --git a/frontend/editor.css b/frontend/editor.css
index f4593957ed..51c758754d 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3673,8 +3673,6 @@ pluto-cell.hooked_up pluto-output {
     display: block;
     padding: 0;
     float: right;
-    top: -20px;
-    right: -8px;
     border: none;
     background: none;
 }

From c9c4f61e7baebbc7842fa063cc755c4947bfc5e9 Mon Sep 17 00:00:00 2001
From: Fons van der Plas 
Date: Thu, 11 Apr 2024 15:17:39 +0300
Subject: [PATCH 19/19] make code a bit more compact

---
 frontend/components/CellOutput.js | 47 +++++++++----------------------
 frontend/editor.css               | 13 ++-------
 2 files changed, 16 insertions(+), 44 deletions(-)

diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 8ab00e5ee0..107e6db397 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -702,46 +702,25 @@ export let highlight = (code_element, language) => {
 }
 
 /**
- * Copies the contents of an HTML Element into the clipboard
+ * Generates a copy button for Markdown code blocks.
  */
-export const copyToClipboard = (e) => {
-    const el = e.target.parentNode.closest("pre")
-    const txt = el.textContent || ""
-    navigator.clipboard
-        .writeText(txt)
-        .then(() => {
-            console.log("Code copied to clipboard:\n" + txt)
-        })
-        .catch((error) => {
-            console.error("Error copying code to clipboard:", error)
-        })
-}
-
-/**
- * Generates a copy button for Markdown code blocks and copies them into the clipboard.
- * @param {HTMLElement | null} pre - The 
 element containing the code block.
- */
-export const generateCopyCodeButton = (pre) => {
-    if (!pre) {
-        console.error("Error: pre is null.")
-        return
-    }
+export const generateCopyCodeButton = (/** @type {HTMLElement?} */ pre) => {
+    if (!pre) return
 
     // create copy button
-    const copyCodeButton = document.createElement("button")
-    copyCodeButton.title = "Copy to Clipboard"
-    copyCodeButton.id = "copy-to-clipboard-btn"
-    copyCodeButton.className = "markdown-code-block-button"
-    copyCodeButton.classList.add("markdown-code-block-copy-code-button")
-    copyCodeButton.addEventListener("click", (e) => {
-        copyToClipboard(e)
-        copyCodeButton.classList.add("markdown-code-block-copied-code-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(() => {
-            copyCodeButton.classList.remove("markdown-code-block-copied-code-button")
-            copyCodeButton.classList.add("markdown-code-block-copy-code-button")
+            button.classList.remove("markdown-code-block-copied-code-button")
         }, 2000)
     })
 
     // Append copy button to the code block element
-    pre.prepend(copyCodeButton)
+    pre.prepend(button)
 }
diff --git a/frontend/editor.css b/frontend/editor.css
index 51c758754d..466479d90f 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -3677,25 +3677,18 @@ pluto-cell.hooked_up pluto-output {
     background: none;
 }
 
-.markdown-code-block-copy-code-button::before,
-.markdown-code-block-copied-code-button::before {
+.markdown-code-block-button::before {
     content: "";
     display: block;
     width: 14px;
     height: 14px;
+    filter: var(--image-filters);
 }
 
-.markdown-code-block-copy-code-button::before {
+.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");
 }
-
-/* Invert markdown copy button on theme toggle */
-@media (prefers-color-scheme: dark) {
-    #copy-to-clipboard-btn {
-        filter: invert(1);
-    }
-}