From 046b97b5fb46246dc84c77f5a60e316881f516a9 Mon Sep 17 00:00:00 2001 From: Akshat Singhai Date: Fri, 10 Jul 2026 13:38:29 +0530 Subject: [PATCH 1/8] [UI] Fix copy button shift on click and add checkmark confirmation Signed-off-by: Akshat Singhai --- assets/js/click-to-copy.js | 95 ++++++++++++++++++++++++++++++++ assets/scss/_styles_project.scss | 8 +++ 2 files changed, 103 insertions(+) create mode 100644 assets/js/click-to-copy.js diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js new file mode 100644 index 00000000000..56714c96adb --- /dev/null +++ b/assets/js/click-to-copy.js @@ -0,0 +1,95 @@ +let codeListings = document.querySelectorAll('.highlight > pre'); + +for (let index = 0; index < codeListings.length; index++) { + const codeSample = codeListings[index].querySelector('code'); + const copyButton = document.createElement('button'); + const buttonAttributes = { + type: 'button', + title: 'Copy to clipboard', + 'data-bs-toggle': 'tooltip', + 'data-bs-placement': 'top', + 'data-bs-container': 'body', + }; + + Object.keys(buttonAttributes).forEach((key) => { + copyButton.setAttribute(key, buttonAttributes[key]); + }); + + copyButton.classList.add( + 'fas', + 'fa-copy', + 'btn', + 'btn-sm', + 'td-click-to-copy' + ); + const tooltip = new bootstrap.Tooltip(copyButton); + + let revertTimeout; + + copyButton.onclick = () => { + copyCode(codeSample); + copyButton.setAttribute('data-bs-original-title', 'Copied!'); + tooltip.show(); + + copyButton.classList.remove('fa-copy'); + copyButton.classList.add('fa-check', 'td-click-to-copy--copied'); + + clearTimeout(revertTimeout); + revertTimeout = setTimeout(() => { + copyButton.classList.remove('fa-check', 'td-click-to-copy--copied'); + copyButton.classList.add('fa-copy'); + copyButton.setAttribute('data-bs-original-title', 'Copy to clipboard'); + tooltip.hide(); + }, 2500); + }; + + copyButton.onmouseout = () => { + if (!copyButton.classList.contains('td-click-to-copy--copied')) { + copyButton.setAttribute('data-bs-original-title', 'Copy to clipboard'); + tooltip.hide(); + } + }; + + const buttonDiv = document.createElement('div'); + buttonDiv.classList.add('click-to-copy'); + buttonDiv.append(copyButton); + codeListings[index].insertBefore(buttonDiv, codeSample); +} + +const copyCode = (codeSample) => { + const isConsoleBlock = codeSample.matches( + "code[data-lang='console'], code.language-console" + ); + let text; + + if (isConsoleBlock) { + const clone = codeSample.cloneNode(true); + pruneUnselectableElements(codeSample, clone); + text = clone.textContent; + text = text.replace(/^ /gm, ''); + } else { + text = codeSample.textContent; + } + text = text ? text.trim() : ''; + navigator.clipboard.writeText(text + '\n'); +}; + +const pruneUnselectableElements = (sourceNode, cloneNode) => { + const sourceChildren = sourceNode.children; + const cloneChildren = cloneNode.children; + + for (let i = sourceChildren.length - 1; i >= 0; i--) { + const sourceChild = sourceChildren[i]; + const cloneChild = cloneChildren[i]; + const style = window.getComputedStyle(sourceChild); + const unselectable = + style.userSelect === 'none' || style.webkitUserSelect === 'none'; + + if (unselectable) { + cloneChild.remove(); + continue; + } + + pruneUnselectableElements(sourceChild, cloneChild); + } +}; \ No newline at end of file diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 5bb83cc425a..d4462321c33 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -705,6 +705,14 @@ a:not([href]):not([class]):hover { &:hover { color: $body-color !important; } + + &:active { + transform: none !important; + } + + &.td-click-to-copy--copied { + color: $success !important; + } } .tooltip .tooltip-inner { From 444b0c187f081995522bed6da9f89ca30fa655b1 Mon Sep 17 00:00:00 2001 From: Akshat Singhai Date: Fri, 10 Jul 2026 13:57:28 +0530 Subject: [PATCH 2/8] Update click-to-copy.js Signed-off-by: Akshat Singhai --- assets/js/click-to-copy.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js index 56714c96adb..7881d5eae93 100644 --- a/assets/js/click-to-copy.js +++ b/assets/js/click-to-copy.js @@ -2,6 +2,11 @@ let codeListings = document.querySelectorAll('.highlight > pre'); for (let index = 0; index < codeListings.length; index++) { const codeSample = codeListings[index].querySelector('code'); + + if (!codeSample) { + continue; + } + const copyButton = document.createElement('button'); const buttonAttributes = { type: 'button', @@ -71,7 +76,11 @@ const copyCode = (codeSample) => { text = codeSample.textContent; } text = text ? text.trim() : ''; - navigator.clipboard.writeText(text + '\n'); + if (navigator.clipboard) { + navigator.clipboard.writeText(text + '\n'); + } else { + console.warn('Clipboard API is not supported in this environment.'); + } }; const pruneUnselectableElements = (sourceNode, cloneNode) => { @@ -81,10 +90,9 @@ const pruneUnselectableElements = (sourceNode, cloneNode) => { for (let i = sourceChildren.length - 1; i >= 0; i--) { const sourceChild = sourceChildren[i]; const cloneChild = cloneChildren[i]; - const style = window.getComputedStyle(sourceChild); + const style = window.getComputedStyle(sourceChild); const unselectable = - style.userSelect === 'none' || style.webkitUserSelect === 'none'; - + style && (style.userSelect === 'none' || style.webkitUserSelect === 'none'); if (unselectable) { cloneChild.remove(); continue; @@ -92,4 +100,4 @@ const pruneUnselectableElements = (sourceNode, cloneNode) => { pruneUnselectableElements(sourceChild, cloneChild); } -}; \ No newline at end of file +}; From fc14ad0c44dc4b2cd21ddb4cb16b01af154cda22 Mon Sep 17 00:00:00 2001 From: Akshat Singhai Date: Fri, 10 Jul 2026 16:01:52 +0530 Subject: [PATCH 3/8] Update click-to-copy.js Signed-off-by: Akshat Singhai --- assets/js/click-to-copy.js | 46 +++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js index 7881d5eae93..3d4b4a37847 100644 --- a/assets/js/click-to-copy.js +++ b/assets/js/click-to-copy.js @@ -32,21 +32,31 @@ for (let index = 0; index < codeListings.length; index++) { let revertTimeout; copyButton.onclick = () => { - copyCode(codeSample); - copyButton.setAttribute('data-bs-original-title', 'Copied!'); - tooltip.show(); - - copyButton.classList.remove('fa-copy'); - copyButton.classList.add('fa-check', 'td-click-to-copy--copied'); - - clearTimeout(revertTimeout); - revertTimeout = setTimeout(() => { - copyButton.classList.remove('fa-check', 'td-click-to-copy--copied'); - copyButton.classList.add('fa-copy'); - copyButton.setAttribute('data-bs-original-title', 'Copy to clipboard'); - tooltip.hide(); - }, 2500); - }; + copyCode(codeSample) + .then(() => { + copyButton.setAttribute('data-bs-original-title', 'Copied!'); + tooltip.show(); + copyButton.classList.remove('fa-copy'); + copyButton.classList.add('fa-check', 'td-click-to-copy--copied'); + clearTimeout(revertTimeout); + revertTimeout = setTimeout(() => { + copyButton.classList.remove('fa-check', 'td-click-to-copy--copied'); + copyButton.classList.add('fa-copy'); + copyButton.setAttribute('data-bs-original-title', 'Copy to clipboard'); + tooltip.hide(); + }, 2500); + }) + .catch((err) => { + console.warn('Failed to copy code to clipboard:', err); + copyButton.setAttribute('data-bs-original-title', 'Failed to copy'); + tooltip.show(); + clearTimeout(revertTimeout); + revertTimeout = setTimeout(() => { + copyButton.setAttribute('data-bs-original-title', 'Copy to clipboard'); + tooltip.hide(); + }, 2500); + }); +}; copyButton.onmouseout = () => { if (!copyButton.classList.contains('td-click-to-copy--copied')) { @@ -77,10 +87,10 @@ const copyCode = (codeSample) => { } text = text ? text.trim() : ''; if (navigator.clipboard) { - navigator.clipboard.writeText(text + '\n'); - } else { - console.warn('Clipboard API is not supported in this environment.'); + return navigator.clipboard.writeText(text + '\n'); } + console.warn('Clipboard API is not supported in this environment.'); + return Promise.reject(new Error('Clipboard API is not supported in this environment.')); }; const pruneUnselectableElements = (sourceNode, cloneNode) => { From b110093a175a7f1875fee02a5642db596f474b7c Mon Sep 17 00:00:00 2001 From: Akshat Singhai Date: Fri, 10 Jul 2026 20:32:37 +0530 Subject: [PATCH 4/8] Update click-to-copy.js Signed-off-by: Akshat Singhai --- assets/js/click-to-copy.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js index 3d4b4a37847..786cfdcf43f 100644 --- a/assets/js/click-to-copy.js +++ b/assets/js/click-to-copy.js @@ -3,10 +3,6 @@ let codeListings = document.querySelectorAll('.highlight > pre'); for (let index = 0; index < codeListings.length; index++) { const codeSample = codeListings[index].querySelector('code'); - if (!codeSample) { - continue; - } - const copyButton = document.createElement('button'); const buttonAttributes = { type: 'button', From e7ed8904f44c5071d6f86921b018f51549bf5986 Mon Sep 17 00:00:00 2001 From: Akshat Singhai Date: Fri, 10 Jul 2026 20:36:05 +0530 Subject: [PATCH 5/8] Update click-to-copy.js Signed-off-by: Akshat Singhai --- assets/js/click-to-copy.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js index 786cfdcf43f..04d44534589 100644 --- a/assets/js/click-to-copy.js +++ b/assets/js/click-to-copy.js @@ -82,12 +82,8 @@ const copyCode = (codeSample) => { text = codeSample.textContent; } text = text ? text.trim() : ''; - if (navigator.clipboard) { - return navigator.clipboard.writeText(text + '\n'); - } - console.warn('Clipboard API is not supported in this environment.'); - return Promise.reject(new Error('Clipboard API is not supported in this environment.')); -}; + return navigator.clipboard.writeText(text + '\n'); + }; const pruneUnselectableElements = (sourceNode, cloneNode) => { const sourceChildren = sourceNode.children; From d49368c9eb6266a93dd1b0d15ccc37fefbbf01d6 Mon Sep 17 00:00:00 2001 From: Akshat Singhai Date: Fri, 10 Jul 2026 20:39:09 +0530 Subject: [PATCH 6/8] Update assets/js/click-to-copy.js Co-authored-by: Bharath Valaboju <69413757+Bharath314@users.noreply.github.com> Signed-off-by: Akshat Singhai --- assets/js/click-to-copy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js index 04d44534589..8c4992cbffe 100644 --- a/assets/js/click-to-copy.js +++ b/assets/js/click-to-copy.js @@ -92,7 +92,7 @@ const pruneUnselectableElements = (sourceNode, cloneNode) => { for (let i = sourceChildren.length - 1; i >= 0; i--) { const sourceChild = sourceChildren[i]; const cloneChild = cloneChildren[i]; - const style = window.getComputedStyle(sourceChild); + const style = window.getComputedStyle(sourceChild); const unselectable = style && (style.userSelect === 'none' || style.webkitUserSelect === 'none'); if (unselectable) { From 886cb3c24612d0100d871eff0134f35e1712d46f Mon Sep 17 00:00:00 2001 From: Akshat Singhai Date: Fri, 10 Jul 2026 20:42:33 +0530 Subject: [PATCH 7/8] Update click-to-copy.js Signed-off-by: Akshat Singhai --- assets/js/click-to-copy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js index 8c4992cbffe..50666027136 100644 --- a/assets/js/click-to-copy.js +++ b/assets/js/click-to-copy.js @@ -94,7 +94,7 @@ const pruneUnselectableElements = (sourceNode, cloneNode) => { const cloneChild = cloneChildren[i]; const style = window.getComputedStyle(sourceChild); const unselectable = - style && (style.userSelect === 'none' || style.webkitUserSelect === 'none'); + style.userSelect === 'none' || style.webkitUserSelect === 'none'; if (unselectable) { cloneChild.remove(); continue; From 7253432794b53187d6b012ccd3b2c0862d7013ec Mon Sep 17 00:00:00 2001 From: Akshat Singhai Date: Fri, 10 Jul 2026 20:44:41 +0530 Subject: [PATCH 8/8] Update click-to-copy.js Signed-off-by: Akshat Singhai --- assets/js/click-to-copy.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js index 50666027136..22e1dfc2f04 100644 --- a/assets/js/click-to-copy.js +++ b/assets/js/click-to-copy.js @@ -53,14 +53,6 @@ for (let index = 0; index < codeListings.length; index++) { }, 2500); }); }; - - copyButton.onmouseout = () => { - if (!copyButton.classList.contains('td-click-to-copy--copied')) { - copyButton.setAttribute('data-bs-original-title', 'Copy to clipboard'); - tooltip.hide(); - } - }; - const buttonDiv = document.createElement('div'); buttonDiv.classList.add('click-to-copy'); buttonDiv.append(copyButton);