1+ < script >
2+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
3+ // Target only shell/bash command blocks (marked with ```shell in markdown)
4+ const codeBlocks = document . querySelectorAll ( '.language-shell' ) ;
5+
6+ codeBlocks . forEach ( function ( block ) {
7+ // Skip if already has copy button
8+ if ( block . querySelector ( '.copy-btn' ) ) return ;
9+
10+ const pre = block . querySelector ( 'pre' ) ;
11+ if ( ! pre ) return ;
12+
13+ // Create copy button
14+ const copyBtn = document . createElement ( 'button' ) ;
15+ copyBtn . className = 'copy-btn' ;
16+ copyBtn . innerHTML = '📋' ;
17+ copyBtn . setAttribute ( 'data-tooltip' , 'Copy' ) ;
18+
19+ // Position button
20+ block . style . position = 'relative' ;
21+ block . appendChild ( copyBtn ) ;
22+
23+ // Copy functionality
24+ copyBtn . addEventListener ( 'click' , function ( ) {
25+ const code = ( pre . textContent || pre . innerText ) . trim ( ) ;
26+
27+ if ( navigator . clipboard ) {
28+ navigator . clipboard . writeText ( code ) . then ( function ( ) {
29+ showSuccess ( copyBtn ) ;
30+ } ) . catch ( function ( ) {
31+ fallbackCopy ( code , copyBtn ) ;
32+ } ) ;
33+ } else {
34+ fallbackCopy ( code , copyBtn ) ;
35+ }
36+ } ) ;
37+ } ) ;
38+
39+ function showSuccess ( button ) {
40+ const original = button . innerHTML ;
41+ button . innerHTML = '✅' ;
42+ button . setAttribute ( 'data-tooltip' , 'Copied!' ) ;
43+ button . style . backgroundColor = '#22c55e' ;
44+
45+ setTimeout ( function ( ) {
46+ button . innerHTML = original ;
47+ button . setAttribute ( 'data-tooltip' , 'Copy' ) ;
48+ button . style . backgroundColor = '' ;
49+ } , 2000 ) ;
50+ }
51+
52+ function fallbackCopy ( text , button ) {
53+ const textArea = document . createElement ( 'textarea' ) ;
54+ textArea . value = text ;
55+ textArea . style . position = 'fixed' ;
56+ textArea . style . left = '-9999px' ;
57+ document . body . appendChild ( textArea ) ;
58+ textArea . select ( ) ;
59+
60+ try {
61+ document . execCommand ( 'copy' ) ;
62+ showSuccess ( button ) ;
63+ } catch ( err ) {
64+ console . error ( 'Copy failed:' , err ) ;
65+ }
66+
67+ document . body . removeChild ( textArea ) ;
68+ }
69+ } ) ;
70+ </ script >
71+
72+ < style >
73+ .copy-btn {
74+ position : absolute;
75+ top : 8px ;
76+ right : 8px ;
77+ background : rgba (0 , 0 , 0 , 0.7 );
78+ border : none;
79+ border-radius : 4px ;
80+ color : white;
81+ padding : 4px 8px ;
82+ cursor : pointer;
83+ font-size : 14px ;
84+ opacity : 0.7 ;
85+ transition : all 0.2s ease;
86+ z-index : 10 ;
87+ }
88+
89+ .copy-btn : hover {
90+ opacity : 1 ;
91+ transform : scale (1.1 );
92+ }
93+
94+ .copy-btn : active {
95+ transform : scale (0.9 );
96+ }
97+
98+ /* Tooltip */
99+ .copy-btn ::before {
100+ content : attr (data-tooltip);
101+ position : absolute;
102+ bottom : 100% ;
103+ right : 0 ;
104+ background : rgba (0 , 0 , 0 , 0.9 );
105+ color : white;
106+ padding : 4px 8px ;
107+ border-radius : 4px ;
108+ font-size : 12px ;
109+ white-space : nowrap;
110+ opacity : 0 ;
111+ visibility : hidden;
112+ transition : opacity 0.2s ease, visibility 0.2s ease;
113+ transform : translateY (-4px );
114+ z-index : 11 ;
115+ }
116+
117+ .copy-btn ::after {
118+ content : '' ;
119+ position : absolute;
120+ bottom : 100% ;
121+ right : 8px ;
122+ border : 4px solid transparent;
123+ border-top-color : rgba (0 , 0 , 0 , 0.9 );
124+ opacity : 0 ;
125+ visibility : hidden;
126+ transition : opacity 0.2s ease, visibility 0.2s ease;
127+ transform : translateY (-4px );
128+ }
129+
130+ .copy-btn : hover ::before ,
131+ .copy-btn : hover ::after {
132+ opacity : 1 ;
133+ visibility : visible;
134+ transform : translateY (-8px );
135+ }
136+
137+ /* Dark theme compatibility */
138+ [data-theme = "dark" ] .copy-btn {
139+ background : rgba (255 , 255 , 255 , 0.2 );
140+ color : # e5e5e5 ;
141+ }
142+
143+ [data-theme = "dark" ] .copy-btn ::before {
144+ background : rgba (255 , 255 , 255 , 0.95 );
145+ color : # 1a1a1a ;
146+ }
147+
148+ [data-theme = "dark" ] .copy-btn ::after {
149+ border-top-color : rgba (255 , 255 , 255 , 0.95 );
150+ }
151+
152+ [data-theme = "light" ] .copy-btn {
153+ background : rgba (0 , 0 , 0 , 0.7 );
154+ color : white;
155+ }
156+ </ style >
0 commit comments