Skip to content

Commit 2d70eba

Browse files
committed
fix: fallback to older copy method for Safari markdown copying
1 parent 350ef73 commit 2d70eba

File tree

1 file changed

+45
-10
lines changed

1 file changed

+45
-10
lines changed

src/components/Layout/mdx/PageHeader.tsx

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,27 @@ export const PageHeader: React.FC<PageHeaderProps> = ({ title, intro }) => {
4444
[activePage.languages, product],
4545
);
4646

47+
// This sucks, but is necessary to support Safari
48+
const fallbackCopyToClipboard = (text: string): boolean => {
49+
const textArea = document.createElement('textarea');
50+
textArea.value = text;
51+
textArea.style.position = 'fixed';
52+
textArea.style.left = '-999999px';
53+
textArea.style.top = '-999999px';
54+
document.body.appendChild(textArea);
55+
textArea.focus();
56+
textArea.select();
57+
58+
try {
59+
const successful = document.execCommand('copy');
60+
document.body.removeChild(textArea);
61+
return successful;
62+
} catch (err) {
63+
document.body.removeChild(textArea);
64+
return false;
65+
}
66+
};
67+
4768
const handleCopyMarkdown = useCallback(async () => {
4869
try {
4970
const response = await fetch(`${location.pathname}.md`);
@@ -58,17 +79,31 @@ export const PageHeader: React.FC<PageHeaderProps> = ({ title, intro }) => {
5879
}
5980

6081
const content = await response.text();
61-
await navigator.clipboard.writeText(content);
62-
setCopyTooltipContent('Copied!');
63-
setCopyTooltipOpen(true);
64-
setTimeout(() => {
65-
setCopyTooltipOpen(false);
66-
setTimeout(() => setCopyTooltipContent('Copy'), 150);
67-
}, 2000);
6882

69-
track('markdown_copy_link_clicked', {
70-
location: location.pathname,
71-
});
83+
// Try modern clipboard API first, fallback to execCommand for Safari
84+
let copySuccessful = false;
85+
try {
86+
await navigator.clipboard.writeText(content);
87+
copySuccessful = true;
88+
} catch (clipboardError) {
89+
// Safari or other browsers that block clipboard access after async operations
90+
copySuccessful = fallbackCopyToClipboard(content);
91+
}
92+
93+
if (copySuccessful) {
94+
setCopyTooltipContent('Copied!');
95+
setCopyTooltipOpen(true);
96+
setTimeout(() => {
97+
setCopyTooltipOpen(false);
98+
setTimeout(() => setCopyTooltipContent('Copy'), 150);
99+
}, 2000);
100+
101+
track('markdown_copy_link_clicked', {
102+
location: location.pathname,
103+
});
104+
} else {
105+
throw new Error('Both clipboard methods failed');
106+
}
72107
} catch (error) {
73108
console.error('Failed to copy markdown:', error);
74109
setCopyTooltipContent('Error!');

0 commit comments

Comments
 (0)