import React, { useEffect, useRef, useState } from 'react'; import { Terminal } from '@xterm/xterm'; import 'xterm/css/xterm.css'; import { FitAddon } from '@xterm/addon-fit'; import './App.css'; const App = () => { const terminalRef = useRef(null); const terminal = useRef(null); const fitAddon = useRef(null); const socket = useRef(null); const [host, setHost] = useState(''); const [port, setPort] = useState('22'); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [isConnected, setIsConnected] = useState(false); const [isSideBarHidden, setIsSideBarHidden] = useState(false); useEffect(() => { terminal.current = new Terminal({ cursorBlink: true, theme: { background: '#1e1e1e', foreground: '#ffffff', }, macOptionIsMeta: true, allowProposedApi: true, scrollback: 5000, }); fitAddon.current = new FitAddon(); terminal.current.loadAddon(fitAddon.current); terminal.current.open(terminalRef.current); fitAddon.current.fit(); // Fit the terminal and send the size when needed const fitAndNotifyResize = () => { fitAddon.current.fit(); if (socket.current && socket.current.readyState === WebSocket.OPEN) { socket.current.send(JSON.stringify({ cols: terminal.current.cols, rows: terminal.current.rows, })); } }; window.addEventListener('resize', fitAndNotifyResize); terminal.current.onResize(({ cols, rows }) => { console.log(`Terminal resized to cols:${cols}, rows:${rows}`); fitAndNotifyResize(); }); const handleConnectionEstablished = () => { fitAndNotifyResize(); }; window.addEventListener('connection-established', handleConnectionEstablished); // Monitor terminal data (activity) terminal.current.onData((data) => { if (socket.current && socket.current.readyState === WebSocket.OPEN) { socket.current.send(data); } }); return () => { terminal.current.dispose(); if (socket.current) { socket.current.close(); } window.removeEventListener('resize', fitAndNotifyResize); }; }, []); const handleConnect = () => { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws/`; // Use current host and "/ws/" endpoint if (!host || !username || !password) { terminal.current.writeln('Please fill in all fields.'); return; } socket.current = new WebSocket("ws://localhost:8081"); socket.current.onopen = () => { terminal.current.writeln(`Connected to WebSocket server at ${wsUrl}`); socket.current.send( JSON.stringify({ host, port, username, password, rows: terminal.current.rows, cols: terminal.current.cols }) ); // Dispatch a custom event when connection is open const event = new Event('connection-established'); window.dispatchEvent(event); setIsConnected(true); }; socket.current.onmessage = (event) => { terminal.current.write(event.data); }; socket.current.onerror = (error) => { terminal.current.writeln(`WebSocket error: ${error.message}`); }; socket.current.onclose = () => { terminal.current.writeln('Disconnected from WebSocket server.'); setIsConnected(false); }; }; const handleInputChange = (event, setState) => { setState(event.target.value); }; const handleSideBarHiding = () => { setIsSideBarHidden((prevState) => { const newState = !prevState; if (newState) { setTimeout(() => { // Add a delay to ensure layout settles before resize action fitAddon.current.fit(); if (socket.current && socket.current.readyState === WebSocket.OPEN) { socket.current.send(JSON.stringify({ cols: terminal.current.cols, rows: terminal.current.rows, })); } }, 100); // Delay of 100 milliseconds } else { setTimeout(() => { // Refit terminal when showing sidebar as well fitAddon.current.fit(); if (socket.current && socket.current.readyState === WebSocket.OPEN) { socket.current.send(JSON.stringify({ cols: terminal.current.cols, rows: terminal.current.rows, })); } }, 100); // Delay of 100 milliseconds } return newState; }); }; return (