@@ -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