Skip to content

Commit 1316f1d

Browse files
committed
fix(docs): copy buttons failing — prefetch markdown so clipboard write stays in user gesture (+execCommand fallback)
1 parent 064381d commit 1316f1d

1 file changed

Lines changed: 46 additions & 10 deletions

File tree

docs/app/components/copy-page.jsx

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,34 +38,70 @@ const HOVER = 'light-dark(rgba(0,0,0,0.05), rgba(255,255,255,0.07))'
3838
const MENU_BG = 'light-dark(#ffffff, #1c1c1f)'
3939
const MUTED = 'light-dark(#52525b, #a1a1aa)'
4040

41+
function writeClipboard(text) {
42+
if (navigator.clipboard && window.isSecureContext) {
43+
return navigator.clipboard.writeText(text)
44+
}
45+
return new Promise((resolve, reject) => {
46+
const ta = document.createElement('textarea')
47+
ta.value = text
48+
ta.style.position = 'fixed'
49+
ta.style.opacity = '0'
50+
document.body.appendChild(ta)
51+
ta.focus()
52+
ta.select()
53+
try {
54+
document.execCommand('copy') ? resolve() : reject(new Error('execCommand failed'))
55+
} catch (e) {
56+
reject(e)
57+
} finally {
58+
document.body.removeChild(ta)
59+
}
60+
})
61+
}
62+
4163
export default function CopyPage() {
4264
const pathname = usePathname()
4365
const [open, setOpen] = useState(false)
4466
const [copied, setCopied] = useState(false)
4567
const ref = useRef(null)
46-
47-
useEffect(() => {
48-
const onDoc = (e) => ref.current && !ref.current.contains(e.target) && setOpen(false)
49-
document.addEventListener('click', onDoc)
50-
return () => document.removeEventListener('click', onDoc)
51-
}, [])
68+
const mdRef = useRef(null)
5269

5370
const slug = slugFrom(pathname)
5471
const mdUrl = `${BASE}/md/${slug}.md`
5572
const pageUrl = typeof window !== 'undefined' ? window.location.href : ''
5673
const absMd = typeof window !== 'undefined' ? `${window.location.origin}${mdUrl}` : mdUrl
5774
const prompt = `Read ${pageUrl} (raw markdown: ${absMd}) — a Zaileys documentation page — then help me with it.`
5875

76+
useEffect(() => {
77+
const onDoc = (e) => ref.current && !ref.current.contains(e.target) && setOpen(false)
78+
document.addEventListener('click', onDoc)
79+
let alive = true
80+
fetch(mdUrl)
81+
.then((r) => (r.ok ? r.text() : null))
82+
.then((t) => alive && (mdRef.current = t))
83+
.catch(() => {})
84+
return () => {
85+
alive = false
86+
document.removeEventListener('click', onDoc)
87+
}
88+
}, [mdUrl])
89+
5990
const copy = async () => {
91+
setOpen(false)
92+
let text = mdRef.current
6093
try {
61-
const text = await (await fetch(mdUrl)).text()
62-
await navigator.clipboard.writeText(text)
94+
if (!text) text = await (await fetch(mdUrl)).text()
95+
await writeClipboard(text)
6396
} catch {
64-
await navigator.clipboard.writeText(pageUrl)
97+
try {
98+
await writeClipboard(pageUrl)
99+
} catch {
100+
return
101+
}
65102
}
66103
setCopied(true)
67104
setTimeout(() => setCopied(false), 1800)
68-
setOpen(false)
69105
}
70106

71107
const item = {

0 commit comments

Comments
 (0)