// src/components/Terminal.tsx import React, { useEffect, useRef } from 'react'; import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import 'xterm/css/xterm.css'; const useTerminalResize = (terminalRef: React.RefObject, fitAddon: FitAddon) => { useEffect(() => { const handleResize = () => { if (fitAddon && terminalRef.current) { fitAddon.fit(); } }; window.addEventListener('resize', handleResize); // Fit the terminal initially handleResize(); return () => { window.removeEventListener('resize', handleResize); }; }, [terminalRef, fitAddon]); }; function TerminalComponent (p: {websocket: string|null}) { const {websocket} = p const terminalRef = useRef(null); const terminal = useRef(null); const fitAddon = useRef(null); useEffect(() => { if (terminalRef.current) { terminal.current = new Terminal(); fitAddon.current = new FitAddon(); terminal.current.loadAddon(fitAddon.current); terminal.current.open(terminalRef.current); fitAddon.current.fit(); if (websocket == null){ return ()=>{ terminal.current?.dispose(); } } const socket = new WebSocket(websocket); terminal.current.onResize( ({cols, rows}, _)=>{ socket.send(JSON.stringify({CommandType: 'resize', Arguments: [cols, rows]})) } ) socket.addEventListener('open', () => { socket.send(JSON.stringify({CommandType: 'resize', Arguments: [terminal.current?.cols, terminal.current?.rows]})) }); socket.addEventListener('message', (event) => { terminal.current?.write(JSON.parse(event.data)); }); terminal.current.onData((data) => { socket.send(JSON.stringify({CommandType: 'insert', Arguments: [data]})); }); return () => { terminal.current?.dispose(); socket.close(); }; } }, []); useTerminalResize(terminalRef, fitAddon.current!) return
; }; export default TerminalComponent;