1- // components/XTermSSH.jsx
2- import { useEffect , useRef } from "react" ;
1+ import { useEffect , useRef , useState } from "react" ;
32import { Terminal } from "xterm" ;
43import { FitAddon } from "xterm-addon-fit" ;
54import "xterm/css/xterm.css" ;
@@ -9,29 +8,40 @@ export default function XTermSSH() {
98 const term = useRef ( null ) ;
109 const fitAddon = useRef ( new FitAddon ( ) ) ;
1110 const socketRef = useRef ( null ) ;
11+ const [ showToast , setShowToast ] = useState ( false ) ;
12+
13+ const getTheme = ( ) => {
14+ const isDark = document . documentElement . classList . contains ( "dark" ) ;
15+ return {
16+ background : isDark ? "#" : "#ffffff" ,
17+ foreground : isDark ? "#00ff00" : "#000000" ,
18+ cursor : isDark ? "#00ff00" : "#000000" ,
19+ selection : isDark ? "rgba(255, 255, 255, 0.3)" : "rgba(0,0,0,0.3)" ,
20+ } ;
21+ } ;
22+
23+ const applyTheme = ( ) => {
24+ if ( term . current ) {
25+ const theme = getTheme ( ) ;
26+ term . current . options . theme = theme ;
27+ term . current . refresh ( 0 , term . current . rows - 1 ) ;
28+ }
29+ } ;
1230
1331 useEffect ( ( ) => {
1432 term . current = new Terminal ( {
1533 cursorBlink : true ,
1634 fontSize : 14 ,
1735 fontFamily : "monospace" ,
18- theme : {
19- background : "#1e1e1e" ,
20- foreground : "#00ff00" ,
21- } ,
36+ theme : getTheme ( ) ,
2237 } ) ;
2338
2439 term . current . loadAddon ( fitAddon . current ) ;
2540 term . current . open ( terminalRef . current ) ;
2641 fitAddon . current . fit ( ) ;
2742
28- // Conexión WebSocket
2943 socketRef . current = new WebSocket ( "ws://localhost:3001" ) ;
3044
31- socketRef . current . onopen = ( ) => {
32- term . current . write ( "🟢 Conectado al servidor\n" ) ;
33- } ;
34-
3545 socketRef . current . onmessage = ( event ) => {
3646 const data = JSON . parse ( event . data ) ;
3747 if ( data . output ) {
@@ -41,33 +51,59 @@ export default function XTermSSH() {
4151 }
4252 } ;
4353
44- socketRef . current . onclose = ( ) => {
45- term . current . write ( "\n🔌 Conexión cerrada" ) ;
46- } ;
47-
4854 socketRef . current . onerror = ( err ) => {
4955 term . current . write ( `\n❌ Error: ${ err . message } ` ) ;
5056 } ;
5157
52- // Captura de entrada del usuario
5358 term . current . onData ( ( data ) => {
5459 socketRef . current . send ( JSON . stringify ( { input : data } ) ) ;
5560 } ) ;
5661
57- // Resize terminal si la ventana cambia de tamaño
62+ const observer = new MutationObserver ( applyTheme ) ;
63+ observer . observe ( document . documentElement , {
64+ attributes : true ,
65+ attributeFilter : [ "class" ] ,
66+ } ) ;
67+
5868 const handleResize = ( ) => fitAddon . current . fit ( ) ;
5969 window . addEventListener ( "resize" , handleResize ) ;
6070
71+ // ✅ Copiar automáticamente lo seleccionado (una sola vez)
72+ let lastCopiedSelection = "" ;
73+ term . current . onSelectionChange ( ( ) => {
74+ const selection = term . current . getSelection ( ) ;
75+ if ( selection && selection !== lastCopiedSelection ) {
76+ navigator . clipboard . writeText ( selection ) . then ( ( ) => {
77+ setShowToast ( true ) ;
78+ setTimeout ( ( ) => setShowToast ( false ) , 1500 ) ;
79+ } ) ;
80+ lastCopiedSelection = selection ;
81+ }
82+ } ) ;
83+
84+ // ✅ Pegar con clic derecho
85+ terminalRef . current . addEventListener ( "contextmenu" , async ( e ) => {
86+ e . preventDefault ( ) ;
87+ const text = await navigator . clipboard . readText ( ) ;
88+ if ( text ) term . current . paste ( text ) ;
89+ } ) ;
90+
6191 return ( ) => {
6292 term . current . dispose ( ) ;
6393 socketRef . current . close ( ) ;
94+ observer . disconnect ( ) ;
6495 window . removeEventListener ( "resize" , handleResize ) ;
6596 } ;
6697 } , [ ] ) ;
6798
6899 return (
69- < div className = "h-screen bg-black " >
100+ < div className = "relative w-full h-full " >
70101 < div ref = { terminalRef } className = "w-full h-full" />
102+ { showToast && (
103+ < div className = "absolute top-2 right-2 bg-green-600 text-white px-4 py-2 rounded shadow-lg text-sm animate-fade-in-out z-50" >
104+ Copiado al portapapeles
105+ </ div >
106+ ) }
71107 </ div >
72108 ) ;
73109}
0 commit comments