forked from
quillmatiq.com/augment
Fork of Chiri for Astro for my blog
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 const preElement = button.closest('.copy-code-block')
30 if (!preElement) return
31
32 const codeElement = preElement.querySelector('code')
33 if (!codeElement) return
34
35 if (!button.querySelector('svg')) {
36 button.innerHTML = copyIcon
37 }
38
39 button.style.opacity = '0'
40 button.style.pointerEvents = 'none'
41
42 preElement.addEventListener('mouseenter', () => {
43 button.style.opacity = '1'
44 button.style.pointerEvents = 'auto'
45 })
46
47 preElement.addEventListener('mouseleave', () => {
48 if (!button.hasAttribute('data-copying')) {
49 button.style.opacity = '0'
50 button.style.pointerEvents = 'none'
51 }
52 })
53
54 button.addEventListener('click', async () => {
55 const codeText = codeElement.textContent || ''
56
57 try {
58 // Primary method: use modern clipboard API
59 await navigator.clipboard.writeText(codeText)
60 button.setAttribute('data-copying', 'true')
61 button.innerHTML = copiedIcon
62
63 setTimeout(() => {
64 if (!preElement.matches(':hover')) {
65 button.style.opacity = '0'
66 button.style.pointerEvents = 'none'
67 }
68 button.removeAttribute('data-copying')
69
70 setTimeout(() => {
71 button.innerHTML = copyIcon
72 }, 500)
73 }, 1500)
74 } catch (err) {
75 console.error('Failed to copy code:', err)
76
77 // Fallback method: create temporary textarea for older browsers
78 try {
79 const textArea = document.createElement('textarea')
80 textArea.value = codeText
81 textArea.style.position = 'fixed'
82 textArea.style.opacity = '0'
83 document.body.appendChild(textArea)
84 textArea.focus()
85 textArea.select()
86
87 navigator.clipboard
88 .writeText(codeText)
89 .then(() => {
90 document.body.removeChild(textArea)
91 })
92 .catch(() => {
93 console.error('Both clipboard methods failed')
94 document.body.removeChild(textArea)
95 })
96 } catch (fallbackErr) {
97 console.error('All clipboard methods failed:', fallbackErr)
98 }
99
100 button.setAttribute('data-copying', 'true')
101 button.innerHTML = copiedIcon
102
103 setTimeout(() => {
104 if (!preElement.matches(':hover')) {
105 button.style.opacity = '0'
106 button.style.pointerEvents = 'none'
107 }
108 button.removeAttribute('data-copying')
109
110 setTimeout(() => {
111 button.innerHTML = copyIcon
112 }, 500)
113 }, 1500)
114 }
115 })
116 })
117 }
118
119 document.addEventListener('astro:page-load', initCopyCode)
120 document.addEventListener('DOMContentLoaded', initCopyCode)
121</script>
122
123<style is:inline>
124 .copy-code-block {
125 position: relative !important;
126 }
127
128 .copy-button {
129 position: absolute;
130 top: 0.5rem;
131 right: 0.5rem;
132 width: 2rem;
133 height: 2rem;
134 z-index: 10;
135 background: var(--bg);
136 border-radius: 0.375rem;
137 font-size: 0.75rem;
138 color: var(--text-secondary);
139 cursor: pointer;
140 transition: all 0.15s ease-out;
141 display: flex;
142 align-items: center;
143 justify-content: center;
144 border: 1px solid var(--border);
145 backdrop-filter: blur(48px);
146 opacity: 0;
147 pointer-events: none;
148 }
149
150 body[data-copy-code='disabled'] .copy-button {
151 display: none !important;
152 }
153
154 .copy-button::before {
155 content: '';
156 position: absolute;
157 top: 0;
158 left: 0;
159 right: 0;
160 bottom: 0;
161 background: var(--code-bg);
162 border-radius: 0.325rem;
163 opacity: 0;
164 transition: opacity 0.15s ease-out;
165 pointer-events: none;
166 }
167
168 .copy-button:hover::before {
169 opacity: 1;
170 }
171
172 .copy-button:hover {
173 color: var(--text-primary);
174 }
175
176 .copy-button svg {
177 flex-shrink: 0;
178 position: relative;
179 z-index: 1;
180 }
181</style>