Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/trueberryless/the-thinking-hub. The central knowledge hub for 8 key Computer Science thinking modes. the-thinking-hub.felixs.dev
Something went wrong. Try again.
6.1 kB · 205 lines
Astro
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206---import { themeConfig } from '@/config'---
<script define:vars={{ copyCode: themeConfig.post.copyCode }}> function initCopyCode() { const copyIcon = ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="14" height="14" fill="currentColor"> <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path> <path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> </svg> `
const copiedIcon = ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor"> <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path> </svg> `
document.body.setAttribute('data-copy-code', copyCode ? 'enabled' : 'disabled')
if (!copyCode) { return }
const copyButtons = document.querySelectorAll('.copy-button')
copyButtons.forEach((button) => { // Avoid initializing multiple times (important for SPA navigation) if (button.dataset.copyInit) return button.dataset.copyInit = '1'
// Find the closest pre element with the .copy-code-block class let preElement = button.closest('.copy-code-block') if (!preElement) { const parent = button.parentElement if (parent) { preElement = parent.querySelector('.copy-code-block') } } if (!preElement) return
const codeElement = preElement.querySelector('code') if (!codeElement) return
if (!button.querySelector('svg')) { button.innerHTML = copyIcon }
button.style.opacity = '0' button.style.pointerEvents = 'none'
// Determine the container to attach hover events let container = preElement const parent = button.parentElement if (parent && parent.classList.contains('copy-code-wrapper')) { container = parent }
container.addEventListener('mouseenter', () => { button.style.opacity = '1' button.style.pointerEvents = 'auto' })
container.addEventListener('mouseleave', () => { if (!button.hasAttribute('data-copying')) { button.style.opacity = '0' button.style.pointerEvents = 'none' } })
button.addEventListener('click', async () => { const codeText = codeElement.textContent || ''
try { // Primary method: use modern clipboard API await navigator.clipboard.writeText(codeText) button.setAttribute('data-copying', 'true') button.innerHTML = copiedIcon
setTimeout(() => { if (!container.matches(':hover')) { button.style.opacity = '0' button.style.pointerEvents = 'none' } button.removeAttribute('data-copying')
setTimeout(() => { button.innerHTML = copyIcon }, 500) }, 1500) } catch (err) { console.error('Failed to copy code:', err)
// Fallback method: create temporary textarea for older browsers try { const textArea = document.createElement('textarea') textArea.value = codeText textArea.style.position = 'fixed' textArea.style.opacity = '0' document.body.appendChild(textArea) textArea.focus() textArea.select()
navigator.clipboard .writeText(codeText) .then(() => { document.body.removeChild(textArea) }) .catch(() => { console.error('Both clipboard methods failed') document.body.removeChild(textArea) }) } catch (fallbackErr) { console.error('All clipboard methods failed:', fallbackErr) }
button.setAttribute('data-copying', 'true') button.innerHTML = copiedIcon
setTimeout(() => { if (!container.matches(':hover')) { button.style.opacity = '0' button.style.pointerEvents = 'none' } button.removeAttribute('data-copying')
setTimeout(() => { button.innerHTML = copyIcon }, 500) }, 1500) } }) }) }
document.addEventListener('astro:page-load', initCopyCode) document.addEventListener('DOMContentLoaded', initCopyCode)</script>
<style is:inline> /* Ensure the positioned ancestor is correct */ .copy-code-wrapper { position: relative !important; }
/* Keep for backward compatibility */ .copy-code-block { position: relative !important; }
.copy-button { position: absolute; top: 0.5rem; right: 0.5rem; width: 2rem; height: 2rem; z-index: 10; background: var(--bg); border-radius: 0.375rem; font-size: 0.75rem; color: var(--text-secondary); cursor: pointer; transition: all 0.15s ease-out; display: flex; align-items: center; justify-content: center; border: 1px solid var(--border); backdrop-filter: blur(48px); opacity: 0; pointer-events: none; }
body[data-copy-code='disabled'] .copy-button { display: none !important; }
.copy-button::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: var(--code-bg); border-radius: 0.325rem; opacity: 0; transition: opacity 0.15s ease-out; pointer-events: none; }
.copy-button:hover::before { opacity: 1; }
.copy-button:hover { color: var(--text-primary); }
.copy-button svg { flex-shrink: 0; position: relative; z-index: 1; }</style>