11"use client" ;
2+ import React , { useState } from "react" ;
23import { Check , Copy } from "lucide-react" ;
3- import { useState } from "react" ;
44import { Button } from "../ui/button" ;
55
6+ const hasChildren = ( props : unknown ) : props is { children : React . ReactNode } => {
7+ return typeof props === 'object' && props !== null && 'children' in props ;
8+ } ;
9+
10+ const extractTextFromReactNode = ( node : React . ReactNode ) : string => {
11+ if ( typeof node === "string" ) {
12+ return node ;
13+ }
14+ if ( Array . isArray ( node ) ) {
15+ return node . map ( extractTextFromReactNode ) . join ( "" ) ;
16+ }
17+ if ( React . isValidElement ( node ) && node . props && hasChildren ( node . props ) ) {
18+ return extractTextFromReactNode ( node . props . children ) ;
19+ }
20+ return String ( node || "" ) ;
21+ } ;
22+
623const CodeBlock = ( { children, className } : { children : React . ReactNode [ ] ; className : string } ) => {
724 const [ copied , setCopied ] = useState ( false ) ;
8- const codeContent = children [ 0 ] || "" ;
25+
26+ const getCodeContent = ( ) : string => {
27+ if ( ! children || children . length === 0 ) return "" ;
28+ return extractTextFromReactNode ( children [ 0 ] ) ;
29+ } ;
930
1031 const handleCopy = async ( ) => {
11- if ( typeof codeContent === "string" ) {
32+ const codeContent = getCodeContent ( ) ;
33+ if ( codeContent ) {
1234 await navigator . clipboard . writeText ( codeContent ) ;
1335 setCopied ( true ) ;
1436 setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
@@ -20,7 +42,13 @@ const CodeBlock = ({ children, className }: { children: React.ReactNode[]; class
2042 < pre className = { className } >
2143 < code className = { className } > { children } </ code >
2244 </ pre >
23- < Button variant = "link" onClick = { handleCopy } className = "absolute top-2 right-2 p-1.5 rounded-md opacity-0 group-hover:opacity-100 transition-opacity" aria-label = "Copy to clipboard" >
45+ < Button
46+ variant = "link"
47+ onClick = { handleCopy }
48+ className = "absolute top-2 right-2 p-2.5 rounded-md opacity-0 group-hover:opacity-100 transition-opacity bg-background/80 hover:bg-background/90"
49+ aria-label = "Copy to clipboard"
50+ title = { copied ? "Copied!" : "Copy to clipboard" }
51+ >
2452 { copied ? < Check size = { 16 } /> : < Copy size = { 16 } /> }
2553 </ Button >
2654 </ div >
0 commit comments