From 4a33024b7fe9501354608e915a08aa7b6522ba33 Mon Sep 17 00:00:00 2001 From: Karmaa <88517757+LukeGus@users.noreply.github.com> Date: Thu, 31 Jul 2025 15:24:42 -0500 Subject: [PATCH] Update SSHTerminal.tsx --- src/apps/SSH/Terminal/SSHTerminal.tsx | 92 ++++++++++----------------- 1 file changed, 33 insertions(+), 59 deletions(-) diff --git a/src/apps/SSH/Terminal/SSHTerminal.tsx b/src/apps/SSH/Terminal/SSHTerminal.tsx index 7ca20724..9d5f226f 100644 --- a/src/apps/SSH/Terminal/SSHTerminal.tsx +++ b/src/apps/SSH/Terminal/SSHTerminal.tsx @@ -1,7 +1,7 @@ -import {useEffect, useRef, useState, useImperativeHandle, forwardRef} from 'react'; -import {useXTerm} from 'react-xtermjs'; -import {FitAddon} from '@xterm/addon-fit'; -import {ClipboardAddon} from '@xterm/addon-clipboard'; +import { useEffect, useRef, useState, useImperativeHandle, forwardRef } from 'react'; +import { useXTerm } from 'react-xtermjs'; +import { FitAddon } from '@xterm/addon-fit'; +import { ClipboardAddon } from '@xterm/addon-clipboard'; interface SSHTerminalProps { hostConfig: any; @@ -12,42 +12,39 @@ interface SSHTerminalProps { } export const SSHTerminal = forwardRef(function SSHTerminal( - {hostConfig, isVisible, splitScreen = false}, + { hostConfig, isVisible, splitScreen = false }, ref ) { - const {instance: terminal, ref: xtermRef} = useXTerm(); + const { instance: terminal, ref: xtermRef } = useXTerm(); const fitAddonRef = useRef(null); const webSocketRef = useRef(null); const resizeTimeout = useRef(null); + const wasDisconnectedBySSH = useRef(false); const [visible, setVisible] = useState(false); useImperativeHandle(ref, () => ({ disconnect: () => { - if (webSocketRef.current) { - webSocketRef.current.close(); - } + webSocketRef.current?.close(); }, fit: () => { - if (fitAddonRef.current) { - fitAddonRef.current.fit(); - } + fitAddonRef.current?.fit(); }, sendInput: (data: string) => { - if (webSocketRef.current && webSocketRef.current.readyState === 1) { - webSocketRef.current.send(JSON.stringify({type: 'input', data})); + if (webSocketRef.current?.readyState === 1) { + webSocketRef.current.send(JSON.stringify({ type: 'input', data })); } } }), []); useEffect(() => { - function handleWindowResize() { - fitAddonRef.current?.fit(); - } - window.addEventListener('resize', handleWindowResize); return () => window.removeEventListener('resize', handleWindowResize); }, []); + function handleWindowResize() { + fitAddonRef.current?.fit(); + } + useEffect(() => { if (!terminal || !xtermRef.current || !hostConfig) return; @@ -70,78 +67,55 @@ export const SSHTerminal = forwardRef(function SSHTermina }, }; - const onResize = () => { - if (!xtermRef.current) return; - const {width, height} = xtermRef.current.getBoundingClientRect(); - - if (width < 100 || height < 50) return; - + const resizeObserver = new ResizeObserver(() => { if (resizeTimeout.current) clearTimeout(resizeTimeout.current); resizeTimeout.current = setTimeout(() => { fitAddonRef.current?.fit(); - const cols = terminal.cols + 1; const rows = terminal.rows; - - webSocketRef.current?.send(JSON.stringify({ - type: 'resize', - data: {cols, rows} - })); + webSocketRef.current?.send(JSON.stringify({ type: 'resize', data: { cols, rows } })); }, 100); - }; + }); - const resizeObserver = new ResizeObserver(onResize); resizeObserver.observe(xtermRef.current); - setTimeout(() => { fitAddon.fit(); setVisible(true); const cols = terminal.cols + 1; const rows = terminal.rows; - const wsUrl = window.location.hostname === 'localhost' ? 'ws://localhost:8082' : `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}/ssh/websocket/`; const ws = new WebSocket(wsUrl); - webSocketRef.current = ws; + wasDisconnectedBySSH.current = false; ws.addEventListener('open', () => { - ws.send(JSON.stringify({ - type: 'connectToHost', - data: { - cols, - rows, - hostConfig: hostConfig - } - })); - + ws.send(JSON.stringify({ type: 'connectToHost', data: { cols, rows, hostConfig } })); terminal.onData((data) => { - ws.send(JSON.stringify({ - type: 'input', - data - })); + ws.send(JSON.stringify({ type: 'input', data })); }); }); ws.addEventListener('message', (event) => { try { const msg = JSON.parse(event.data); - - if (msg.type === 'data') { - terminal.write(msg.data); - } else if (msg.type === 'error') { - terminal.writeln(`\r\n[ERROR] ${msg.message}`); - } else if (msg.type === 'connected') { + if (msg.type === 'data') terminal.write(msg.data); + else if (msg.type === 'error') terminal.writeln(`\r\n[ERROR] ${msg.message}`); + else if (msg.type === 'connected') {} + else if (msg.type === 'disconnected') { + wasDisconnectedBySSH.current = true; + terminal.writeln(`\r\n[${msg.message || 'Disconnected'}]`); } - } catch (err) { - } + } catch (_) {} }); ws.addEventListener('close', () => { - terminal.writeln('\r\n[Connection closed]'); + if (!wasDisconnectedBySSH.current) { + terminal.writeln('\r\n[Connection closed]'); + } }); ws.addEventListener('error', () => { @@ -167,7 +141,7 @@ export const SSHTerminal = forwardRef(function SSHTermina ref={xtermRef} style={{ position: 'absolute', - top: splitScreen ? 0 : 0, + top: 0, left: 0, right: 0, bottom: 0, @@ -197,4 +171,4 @@ style.innerHTML = ` scrollbar-color: rgba(180,180,180,0.7) transparent; } `; -document.head.appendChild(style); \ No newline at end of file +document.head.appendChild(style);