Skip to content

Commit de61bbf

Browse files
authored
Merge pull request #431 from AbraxasContact/new-feature-branch
add copy-to-clipboard buttons for all code blocks with visual feedback
2 parents 2f4732c + 85fce6e commit de61bbf

File tree

1 file changed

+240
-0
lines changed

1 file changed

+240
-0
lines changed

index.html

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,155 @@
455455
color: var(--code-text);
456456
font-weight: 400;
457457
}
458+
/* Copy Button Styles - No Overlay Version */
459+
.code-block-wrapper {
460+
position: relative;
461+
margin: 2rem 0;
462+
isolation: isolate;
463+
}
464+
465+
.code-block-wrapper pre {
466+
margin: 0;
467+
}
468+
469+
.copy-button {
470+
position: absolute;
471+
top: 0.75rem;
472+
right: 0.75rem;
473+
background: rgba(91, 124, 153, 0.9);
474+
color: white;
475+
border: none;
476+
border-radius: 6px;
477+
padding: 0.5rem 0.75rem;
478+
font-size: 0.75rem;
479+
font-weight: 500;
480+
font-family: 'Inter', sans-serif;
481+
cursor: pointer;
482+
display: flex;
483+
align-items: center;
484+
gap: 0.4rem;
485+
transition: all 0.2s ease;
486+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
487+
z-index: 100;
488+
opacity: 0;
489+
transform: translateY(-2px);
490+
pointer-events: auto;
491+
-webkit-tap-highlight-color: transparent;
492+
-webkit-touch-callout: none;
493+
-webkit-user-select: none;
494+
-moz-user-select: none;
495+
user-select: none;
496+
}
497+
498+
.code-block-wrapper:hover .copy-button {
499+
opacity: 1;
500+
transform: translateY(0);
501+
}
502+
503+
.copy-button:hover {
504+
background: var(--primary-hover);
505+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
506+
transform: translateY(-1px);
507+
}
508+
509+
.copy-button:active {
510+
transform: translateY(0);
511+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
512+
}
513+
514+
.copy-button:focus {
515+
outline: none;
516+
}
517+
518+
.copy-button:focus-visible {
519+
outline: 2px solid var(--accent-color);
520+
outline-offset: 2px;
521+
}
522+
523+
.copy-button.copied {
524+
background: var(--success-color);
525+
opacity: 1;
526+
}
527+
528+
.copy-button i {
529+
font-size: 0.875rem;
530+
pointer-events: none;
531+
}
532+
533+
.copy-button .copy-text {
534+
pointer-events: none;
535+
user-select: none;
536+
}
537+
538+
.copy-button .copy-icon {
539+
display: inline-block;
540+
}
541+
542+
.copy-button .check-icon {
543+
display: none;
544+
}
545+
546+
.copy-button.copied .copy-icon {
547+
display: none;
548+
}
549+
550+
.copy-button.copied .check-icon {
551+
display: inline-block;
552+
animation: checkmark 0.3s ease;
553+
}
458554

555+
@keyframes checkmark {
556+
0% {
557+
transform: scale(0) rotate(-45deg);
558+
}
559+
50% {
560+
transform: scale(1.2) rotate(0deg);
561+
}
562+
100% {
563+
transform: scale(1) rotate(0deg);
564+
}
565+
}
566+
567+
/* Completely remove all pseudo-elements that could show overlays */
568+
.copy-button::before,
569+
.copy-button::after {
570+
display: none !important;
571+
content: none !important;
572+
opacity: 0 !important;
573+
visibility: hidden !important;
574+
}
575+
576+
.copy-button:hover::before,
577+
.copy-button:hover::after,
578+
.copy-button:focus::before,
579+
.copy-button:focus::after,
580+
.copy-button:active::before,
581+
.copy-button:active::after {
582+
display: none !important;
583+
content: none !important;
584+
}
585+
586+
/* Dark mode adjustments */
587+
body.dark-mode .copy-button {
588+
background: rgba(127, 169, 155, 0.9);
589+
}
590+
591+
body.dark-mode .copy-button:hover {
592+
background: var(--accent-color);
593+
}
594+
595+
/* Mobile optimizations */
596+
@media (max-width: 768px) {
597+
.copy-button {
598+
opacity: 1;
599+
padding: 0.45rem 0.65rem;
600+
font-size: 0.7rem;
601+
}
602+
603+
.copy-button i {
604+
font-size: 0.8rem;
605+
}
606+
}
459607
/* Blockquotes */
460608
.content-wrapper blockquote {
461609
margin: 2.5rem 0;
@@ -21677,6 +21825,7 @@ <h2>Download Other Formats</h2>
2167721825
loadPreferences();
2167821826
setupEventListeners();
2167921827
updateActiveNavigation();
21828+
initializeCopyButtons();
2168021829

2168121830
if (window.innerWidth > 1024) {
2168221831
toggleSidebar();
@@ -21937,6 +22086,97 @@ <h2>Download Other Formats</h2>
2193722086
}
2193822087
}
2193922088
});
22089+
22090+
// Copy to Clipboard Functionality - No Overlays Version
22091+
function initializeCopyButtons() {
22092+
const preElements = document.querySelectorAll('.content-wrapper pre');
22093+
22094+
preElements.forEach((pre) => {
22095+
if (pre.parentElement.classList.contains('code-block-wrapper')) {
22096+
return;
22097+
}
22098+
22099+
const wrapper = document.createElement('div');
22100+
wrapper.className = 'code-block-wrapper';
22101+
pre.parentNode.insertBefore(wrapper, pre);
22102+
wrapper.appendChild(pre);
22103+
22104+
const copyButton = document.createElement('button');
22105+
copyButton.className = 'copy-button';
22106+
copyButton.type = 'button';
22107+
copyButton.setAttribute('aria-label', 'Copy code to clipboard');
22108+
copyButton.setAttribute('title', ''); // Prevent default tooltip
22109+
copyButton.innerHTML = `
22110+
<i class="fas fa-copy copy-icon" aria-hidden="true"></i>
22111+
<i class="fas fa-check check-icon" aria-hidden="true"></i>
22112+
<span class="copy-text">Copy</span>
22113+
`;
22114+
22115+
copyButton.addEventListener('click', async function(e) {
22116+
e.preventDefault();
22117+
e.stopPropagation();
22118+
e.stopImmediatePropagation();
22119+
22120+
const code = pre.querySelector('code');
22121+
const textToCopy = code ? code.textContent : pre.textContent;
22122+
22123+
try {
22124+
await navigator.clipboard.writeText(textToCopy);
22125+
22126+
// Visual feedback
22127+
copyButton.classList.add('copied');
22128+
const copyText = copyButton.querySelector('.copy-text');
22129+
if (copyText) copyText.textContent = 'Copied!';
22130+
22131+
setTimeout(() => {
22132+
copyButton.classList.remove('copied');
22133+
if (copyText) copyText.textContent = 'Copy';
22134+
}, 2000);
22135+
22136+
} catch (err) {
22137+
// Fallback for older browsers
22138+
const textArea = document.createElement('textarea');
22139+
textArea.value = textToCopy;
22140+
textArea.style.position = 'fixed';
22141+
textArea.style.left = '-999999px';
22142+
textArea.style.top = '-999999px';
22143+
document.body.appendChild(textArea);
22144+
textArea.focus();
22145+
textArea.select();
22146+
22147+
try {
22148+
document.execCommand('copy');
22149+
textArea.remove();
22150+
22151+
copyButton.classList.add('copied');
22152+
const copyText = copyButton.querySelector('.copy-text');
22153+
if (copyText) copyText.textContent = 'Copied!';
22154+
22155+
setTimeout(() => {
22156+
copyButton.classList.remove('copied');
22157+
if (copyText) copyText.textContent = 'Copy';
22158+
}, 2000);
22159+
22160+
} catch (err) {
22161+
console.error('Failed to copy text: ', err);
22162+
textArea.remove();
22163+
}
22164+
}
22165+
22166+
return false;
22167+
}, { capture: true });
22168+
22169+
copyButton.addEventListener('touchstart', function(e) {
22170+
e.stopPropagation();
22171+
}, { passive: true });
22172+
22173+
copyButton.addEventListener('touchend', function(e) {
22174+
e.stopPropagation();
22175+
}, { passive: true });
22176+
22177+
wrapper.appendChild(copyButton);
22178+
});
22179+
}
2194022180
</script>
2194122181
</body>
2194222182
</html>

0 commit comments

Comments
 (0)