@@ -213,12 +213,74 @@ const markdownComponents = {
213213 </ code >
214214 ) ;
215215 }
216+ const [ copied , setCopied ] = React . useState ( false ) ;
217+ const textToCopy = raw ;
218+
219+ const handleCopy = ( ) => {
220+ const doSet = ( ) => {
221+ setCopied ( true ) ;
222+ setTimeout ( ( ) => setCopied ( false ) , 1500 ) ;
223+ } ;
224+ try {
225+ if ( navigator && navigator . clipboard && navigator . clipboard . writeText ) {
226+ navigator . clipboard . writeText ( textToCopy ) . then ( doSet ) . catch ( ( ) => {
227+ // Fallback
228+ const ta = document . createElement ( 'textarea' ) ;
229+ ta . value = textToCopy ;
230+ ta . style . position = 'fixed' ;
231+ ta . style . opacity = '0' ;
232+ document . body . appendChild ( ta ) ;
233+ ta . select ( ) ;
234+ try { document . execCommand ( 'copy' ) ; } catch { }
235+ document . body . removeChild ( ta ) ;
236+ doSet ( ) ;
237+ } ) ;
238+ } else {
239+ const ta = document . createElement ( 'textarea' ) ;
240+ ta . value = textToCopy ;
241+ ta . style . position = 'fixed' ;
242+ ta . style . opacity = '0' ;
243+ document . body . appendChild ( ta ) ;
244+ ta . select ( ) ;
245+ try { document . execCommand ( 'copy' ) ; } catch { }
246+ document . body . removeChild ( ta ) ;
247+ doSet ( ) ;
248+ }
249+ } catch { }
250+ } ;
251+
216252 return (
217- < pre className = "bg-gray-900 dark:bg-gray-900 border border-gray-700/40 rounded-lg p-3 overflow-x-auto my-2" >
218- < code className = { `text-gray-100 dark:text-gray-100 text-sm font-mono ${ className || '' } ` } { ...props } >
219- { children }
220- </ code >
221- </ pre >
253+ < div className = "relative group my-2" >
254+ < button
255+ type = "button"
256+ onClick = { handleCopy }
257+ className = "absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 focus:opacity-100 active:opacity-100 transition-opacity text-xs px-2 py-1 rounded-md bg-gray-700/80 hover:bg-gray-700 text-white border border-gray-600"
258+ title = { copied ? 'Copied' : 'Copy code' }
259+ aria-label = { copied ? 'Copied' : 'Copy code' }
260+ >
261+ { copied ? (
262+ < span className = "flex items-center gap-1" >
263+ < svg className = "w-3.5 h-3.5" viewBox = "0 0 20 20" fill = "currentColor" >
264+ < path fillRule = "evenodd" d = "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule = "evenodd" />
265+ </ svg >
266+ Copied
267+ </ span >
268+ ) : (
269+ < span className = "flex items-center gap-1" >
270+ < svg className = "w-3.5 h-3.5" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" >
271+ < rect x = "9" y = "9" width = "13" height = "13" rx = "2" ry = "2" > </ rect >
272+ < path d = "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" > </ path >
273+ </ svg >
274+ Copy
275+ </ span >
276+ ) }
277+ </ button >
278+ < pre className = "bg-gray-900 dark:bg-gray-900 border border-gray-700/40 rounded-lg p-3 overflow-x-auto" >
279+ < code className = { `text-gray-100 dark:text-gray-100 text-sm font-mono ${ className || '' } ` } { ...props } >
280+ { children }
281+ </ code >
282+ </ pre >
283+ </ div >
222284 ) ;
223285 } ,
224286 blockquote : ( { children } ) => (
0 commit comments