diff --git a/src/components/Documentation/CopyPageButton/index.tsx b/src/components/Documentation/CopyPageButton/index.tsx index 564cc9d741..6adcb63401 100644 --- a/src/components/Documentation/CopyPageButton/index.tsx +++ b/src/components/Documentation/CopyPageButton/index.tsx @@ -26,10 +26,21 @@ const CopyPageButton: React.FC = ({ pagePath }) => { setCopyState('copying') setOpen(false) try { - const res = await fetch(markdownUrl) - if (!res.ok) throw new Error(res.statusText) - const text = await res.text() - await navigator.clipboard.writeText(text) + const textPromise = fetch(markdownUrl).then(res => { + if (!res.ok) throw new Error(res.statusText) + return res.text() + }) + + // Safari requires clipboard writes to start in the synchronous call + // stack of a user gesture. Using ClipboardItem with a Promise + // lets us begin the write synchronously while content resolves async. + await navigator.clipboard.write([ + new ClipboardItem({ + 'text/plain': textPromise.then( + t => new Blob([t], { type: 'text/plain' }) + ) + }) + ]) } catch { setCopyState('idle') return