frontend/src/terminal.tsx

82 lines
2.5 KiB
TypeScript

// 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<HTMLDivElement>, 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<HTMLDivElement>(null);
const terminal = useRef<Terminal | null>(null);
const fitAddon = useRef<FitAddon | null>(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 <div ref={terminalRef} style={{ width: '100%', height: '100%' }}></div>;
};
export default TerminalComponent;