@@ -15,15 +15,58 @@ export interface CodeBlockProps {
1515 title ?: string ;
1616}
1717
18+ /**
19+ *
20+ * Copy `element`'s text children as long as long as they are not `.no-copy`
21+ */
22+ function getCopiableText ( element : HTMLDivElement ) {
23+ let text = '' ;
24+ const walker = document . createTreeWalker ( element , NodeFilter . SHOW_TEXT , {
25+ acceptNode : function ( node ) {
26+ // Skip if parent has .no-copy class
27+ if ( node . parentElement ?. classList . contains ( 'no-copy' ) ) {
28+ return NodeFilter . FILTER_REJECT ;
29+ }
30+ return NodeFilter . FILTER_ACCEPT ;
31+ } ,
32+ } ) ;
33+
34+ let node : Node | null ;
35+ // eslint-disable-next-line no-cond-assign
36+ while ( ( node = walker . nextNode ( ) ) ) {
37+ text += node . textContent ;
38+ }
39+
40+ return text . trim ( ) ;
41+ }
42+
1843export function CodeBlock ( { filename, language, children} : CodeBlockProps ) {
1944 const [ showCopied , setShowCopied ] = useState ( false ) ;
2045 const codeRef = useRef < HTMLDivElement > ( null ) ;
2146
2247 // Show the copy button after js has loaded
2348 // otherwise the copy button will not work
2449 const [ showCopyButton , setShowCopyButton ] = useState ( false ) ;
50+
2551 useEffect ( ( ) => {
2652 setShowCopyButton ( true ) ;
53+ // prevent .no-copy elements from being copied during selection Right click copy or / Cmd+C
54+ const noCopyElements = codeRef . current ?. querySelectorAll < HTMLSpanElement > ( '.no-copy' ) ;
55+ const handleSelectionChange = ( ) => {
56+ // hide no copy elements within the selection
57+ const selection = window . getSelection ( ) ;
58+ noCopyElements ?. forEach ( element => {
59+ if ( selection ?. containsNode ( element , true ) ) {
60+ element . style . display = 'none' ;
61+ } else {
62+ element . style . display = 'inline' ;
63+ }
64+ } ) ;
65+ } ;
66+ document . addEventListener ( 'selectionchange' , handleSelectionChange ) ;
67+ return ( ) => {
68+ document . removeEventListener ( 'selectionchange' , handleSelectionChange ) ;
69+ } ;
2770 } , [ ] ) ;
2871
2972 useCleanSnippetInClipboard ( codeRef , { language} ) ;
@@ -33,7 +76,9 @@ export function CodeBlock({filename, language, children}: CodeBlockProps) {
3376 return ;
3477 }
3578
36- const code = cleanCodeSnippet ( codeRef . current . innerText , { language} ) ;
79+ const code = cleanCodeSnippet ( getCopiableText ( codeRef . current ) , {
80+ language,
81+ } ) ;
3782
3883 try {
3984 await navigator . clipboard . writeText ( code ) ;
0 commit comments