Liverpoolfc.tv: xtermjs.org/

npm install xterm


Requirements: Open the console, pop up a new page, page display terminal, through WebSocket and back-end connection.

1. Open a specific page.

import React from 'react'; 
import Terminal from '@/components/Terminal';
// ... 
const redirectToTerminal = () = > ( 
    <Router>
        <Route path="/terminal/:url" components={Terminal} exact />
    </Router>
); 
if (window.location.href.includes('terminal')) {
    returnredirectToTerminal(); };Copy the code

2. The terminal ADAPTS to the size of the parent

The xterm-Addon-Fit plug-in is used, as described on the website.

npm install xterm-addon-fit

And how to use it as described on the website

import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
const term = new Terminal();
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
// Open the terminal in #terminal-container 
term.open(document.getElementById('terminal-container'));
// Make the terminal's size and geometry fit the size of #terminal-container
fitAddon.fit();
Copy the code

Then I took the time to check the source code [source] and returned 4 methods, which are activate, dispose, FIT, proposeDimensions

3. The terminal connects to websocket

The official website also has a plug-in that can use xterm-addon-Attach

npm install xterm-addon-attach

And how to use it as described on the website


import { Terminal } from 'xterm';
import { AttachAddon } from 'xterm-addon-attach';
const term = new Terminal(); 
const socket = new WebSocket('wss://docker.example.com/containers/mycontainerid/attach/ws');
const attachAddon = new AttachAddon(socket);
// Attach the socket to term
term.loadAddon(attachAddon);

Copy the code

Because in their own use, there is a problem in the interface display, the data returned by the back end is not processed in the interface display once, and then processed after the display again, it is very embarrassing. And finally in the code, I didn’t use it.

Look at the source code [source], there are only two methods in total, one is activate, one is dispose in activate method directly made preliminary processing of the returned data and then terminal.write, I personally think this is the reason.


Finally terminal.tsx source code

import React, { useEffect } from 'react';
import 'xterm/css/xterm.css';
import { Terminal } from 'xterm';
import { useParams } from 'react-router';
import { AttachAddon } from 'xterm-addon-attach';
import { FitAddon } from 'xterm-addon-fit';
import cache from '@/utils/storage';
const TOKEN = 'token';
const action = (type: any, data? :any) = > {
	const action = Object.assign(
		{
			type: type
		},
		data
	);
	return JSON.stringify(action);
};
interface ParamsProps {
	url: string;
}
export default function MidTerminal() :JSX.Element {
	const params: ParamsProps = useParams();
	// Add HTTPS and HTTP support
	// wss://${window.location.hostname}:${window.location.port}
	const socketUrl =
		window.location.protocol.toLowerCase() === 'https:'
			? `wss://x.x.x.x:xxxx/ws/terminal?${params.url}`
			: `ws://x.x.x.x:xxxx/ws/terminal?${params.url}`;
	useEffect(() = > {
		const socket = new WebSocket(socketUrl, cache.getLocal(TOKEN));
		const terminal = new Terminal({
			cursorStyle: 'underline'.cursorBlink: true.theme: {
				foreground: '#dddddd'.cursor: 'gray'
			},
			windowsMode: true
		});

		const fitAddon = new FitAddon();
		terminal.loadAddon(fitAddon);
		const terminalDom = document.getElementById('terminal-container');
		terminal.open(terminalDom as HTMLElement);
		fitAddon.fit();
		socket.onopen = () = > {
			socket.send(action('TERMINAL_INIT'));
			socket.send(action('TERMINAL_READY'));
			socket.send(
				action('TERMINAL_RESIZE', {
					columns: fitAddon.proposeDimensions().cols,
					rows: fitAddon.proposeDimensions().rows
				})
			);
			terminal.write('Welcome to terminal! \r\n$');
		};
		socket.onclose = () = > {
			terminal.write('Bye Bye! \r\n$');
		};
		socket.onerror = () = > {
			terminal.write('Something errors \r\n$');
		};
		terminal.onData((e: string) = > {
			socket.send(
				action('TERMINAL_COMMAND', {
					command: e
				})
			);
		});
		socket.onmessage = (e: MessageEvent<any>) = > {
			const data = JSON.parse(e? .data);if(data? .type =='TERMINAL_PRINT') { terminal.write(data.text); }};return () = >{ socket.close(); terminal.dispose(); }; } []);return (
		<div
			id="terminal-container"
			style={{ width: '100% ',height: '100'}} % ></div>
	);
}

Copy the code

Xterm plug-in back and forth made nearly a week, read the source code and there are many plug-ins have not used, this time just use the most basic two, if there is a chance to try other plug-ins.