Fork of Chiri for Astro for my blog
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at a1e6ead60fb44c152fefa3a86179bcf2b84d6249 205 lines 6.2 kB view raw
1--- 2import { themeConfig } from '@/config' 3--- 4 5<script define:vars={{ copyCode: themeConfig.post.copyCode }}> 6 function initCopyCode() { 7 const copyIcon = ` 8 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="14" height="14" fill="currentColor"> 9 <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> 10 <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> 11 </svg> 12 ` 13 14 const copiedIcon = ` 15 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor"> 16 <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> 17 </svg> 18 ` 19 20 document.body.setAttribute('data-copy-code', copyCode ? 'enabled' : 'disabled') 21 22 if (!copyCode) { 23 return 24 } 25 26 const copyButtons = document.querySelectorAll('.copy-button') 27 28 copyButtons.forEach((button) => { 29 // Avoid initializing multiple times (important for SPA navigation) 30 if (button.dataset.copyInit) return 31 button.dataset.copyInit = '1' 32 33 // Find the closest pre element with the .copy-code-block class 34 let preElement = button.closest('.copy-code-block') 35 if (!preElement) { 36 const parent = button.parentElement 37 if (parent) { 38 preElement = parent.querySelector('.copy-code-block') 39 } 40 } 41 if (!preElement) return 42 43 const codeElement = preElement.querySelector('code') 44 if (!codeElement) return 45 46 if (!button.querySelector('svg')) { 47 button.innerHTML = copyIcon 48 } 49 50 button.style.opacity = '0' 51 button.style.pointerEvents = 'none' 52 53 // Determine the container to attach hover events 54 let container = preElement 55 const parent = button.parentElement 56 if (parent && parent.classList.contains('copy-code-wrapper')) { 57 container = parent 58 } 59 60 container.addEventListener('mouseenter', () => { 61 button.style.opacity = '1' 62 button.style.pointerEvents = 'auto' 63 }) 64 65 container.addEventListener('mouseleave', () => { 66 if (!button.hasAttribute('data-copying')) { 67 button.style.opacity = '0' 68 button.style.pointerEvents = 'none' 69 } 70 }) 71 72 button.addEventListener('click', async () => { 73 const codeText = codeElement.textContent || '' 74 75 try { 76 // Primary method: use modern clipboard API 77 await navigator.clipboard.writeText(codeText) 78 button.setAttribute('data-copying', 'true') 79 button.innerHTML = copiedIcon 80 81 setTimeout(() => { 82 if (!container.matches(':hover')) { 83 button.style.opacity = '0' 84 button.style.pointerEvents = 'none' 85 } 86 button.removeAttribute('data-copying') 87 88 setTimeout(() => { 89 button.innerHTML = copyIcon 90 }, 500) 91 }, 1500) 92 } catch (err) { 93 console.error('Failed to copy code:', err) 94 95 // Fallback method: create temporary textarea for older browsers 96 try { 97 const textArea = document.createElement('textarea') 98 textArea.value = codeText 99 textArea.style.position = 'fixed' 100 textArea.style.opacity = '0' 101 document.body.appendChild(textArea) 102 textArea.focus() 103 textArea.select() 104 105 navigator.clipboard 106 .writeText(codeText) 107 .then(() => { 108 document.body.removeChild(textArea) 109 }) 110 .catch(() => { 111 console.error('Both clipboard methods failed') 112 document.body.removeChild(textArea) 113 }) 114 } catch (fallbackErr) { 115 console.error('All clipboard methods failed:', fallbackErr) 116 } 117 118 button.setAttribute('data-copying', 'true') 119 button.innerHTML = copiedIcon 120 121 setTimeout(() => { 122 if (!container.matches(':hover')) { 123 button.style.opacity = '0' 124 button.style.pointerEvents = 'none' 125 } 126 button.removeAttribute('data-copying') 127 128 setTimeout(() => { 129 button.innerHTML = copyIcon 130 }, 500) 131 }, 1500) 132 } 133 }) 134 }) 135 } 136 137 // Use only astro:page-load as it fires on initial load and navigation 138 document.addEventListener('astro:page-load', initCopyCode) 139</script> 140 141<style is:inline> 142 /* Ensure the positioned ancestor is correct */ 143 .copy-code-wrapper { 144 position: relative !important; 145 } 146 147 /* Keep for backward compatibility */ 148 .copy-code-block { 149 position: relative !important; 150 } 151 152 .copy-button { 153 position: absolute; 154 top: 0.5rem; 155 right: 0.5rem; 156 width: 2rem; 157 height: 2rem; 158 z-index: 10; 159 background: var(--bg); 160 border-radius: 0.375rem; 161 font-size: 0.75rem; 162 color: var(--text-secondary); 163 cursor: pointer; 164 transition: all 0.15s ease-out; 165 display: flex; 166 align-items: center; 167 justify-content: center; 168 border: 1px solid var(--border); 169 backdrop-filter: blur(48px); 170 opacity: 0; 171 pointer-events: none; 172 } 173 174 body[data-copy-code='disabled'] .copy-button { 175 display: none !important; 176 } 177 178 .copy-button::before { 179 content: ''; 180 position: absolute; 181 top: 0; 182 left: 0; 183 right: 0; 184 bottom: 0; 185 background: var(--code-bg); 186 border-radius: 0.325rem; 187 opacity: 0; 188 transition: opacity 0.15s ease-out; 189 pointer-events: none; 190 } 191 192 .copy-button:hover::before { 193 opacity: 1; 194 } 195 196 .copy-button:hover { 197 color: var(--text-primary); 198 } 199 200 .copy-button svg { 201 flex-shrink: 0; 202 position: relative; 203 z-index: 1; 204 } 205</style>