aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--package-lock.json46
-rw-r--r--package.json2
-rw-r--r--src/App.js76
3 files changed, 89 insertions, 35 deletions
diff --git a/package-lock.json b/package-lock.json
index 8feebcd..d468a7a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,8 +11,10 @@
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.4",
"@testing-library/user-event": "^13.5.0",
+ "re-resizable": "^6.9.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
+ "react-resize-observer": "^1.1.1",
"react-scripts": "5.0.0",
"web-vitals": "^2.1.4",
"xterm": "^4.18.0",
@@ -7317,6 +7319,11 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
+ "node_modules/fast-memoize": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
+ "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
+ },
"node_modules/fastq": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
@@ -13098,6 +13105,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/re-resizable": {
+ "version": "6.9.5",
+ "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.5.tgz",
+ "integrity": "sha512-Q4+K8gOPbUBmbJCa0qfoVXBGnCwkAJrZ9KUca4GDn5FmxyV2HtLrBz7u43uUOb0y7xKbwcfuftweiOCIDEiCQA==",
+ "dependencies": {
+ "fast-memoize": "^2.5.1"
+ },
+ "peerDependencies": {
+ "react": "^16.13.1 || ^17.0.0",
+ "react-dom": "^16.13.1 || ^17.0.0"
+ }
+ },
"node_modules/react": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
@@ -13274,6 +13293,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-resize-observer": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/react-resize-observer/-/react-resize-observer-1.1.1.tgz",
+ "integrity": "sha512-3R+90Hou90Mr3wJYc+unsySC8Pn91V4nmjO32NKvUvjphRUbq9HisyLg7bDyGBE7xlMrrM6Fax7iNQaFdc/FYA==",
+ "peerDependencies": {
+ "react": ">=0.14"
+ }
+ },
"node_modules/react-scripts": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz",
@@ -21391,6 +21418,11 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
+ "fast-memoize": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
+ "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
+ },
"fastq": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
@@ -25403,6 +25435,14 @@
}
}
},
+ "re-resizable": {
+ "version": "6.9.5",
+ "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.5.tgz",
+ "integrity": "sha512-Q4+K8gOPbUBmbJCa0qfoVXBGnCwkAJrZ9KUca4GDn5FmxyV2HtLrBz7u43uUOb0y7xKbwcfuftweiOCIDEiCQA==",
+ "requires": {
+ "fast-memoize": "^2.5.1"
+ }
+ },
"react": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
@@ -25536,6 +25576,12 @@
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
},
+ "react-resize-observer": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/react-resize-observer/-/react-resize-observer-1.1.1.tgz",
+ "integrity": "sha512-3R+90Hou90Mr3wJYc+unsySC8Pn91V4nmjO32NKvUvjphRUbq9HisyLg7bDyGBE7xlMrrM6Fax7iNQaFdc/FYA==",
+ "requires": {}
+ },
"react-scripts": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz",
diff --git a/package.json b/package.json
index 6ebdfe0..c46b018 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,10 @@
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.4",
"@testing-library/user-event": "^13.5.0",
+ "re-resizable": "^6.9.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
+ "react-resize-observer": "^1.1.1",
"react-scripts": "5.0.0",
"web-vitals": "^2.1.4",
"xterm": "^4.18.0",
diff --git a/src/App.js b/src/App.js
index bc5db46..b671044 100644
--- a/src/App.js
+++ b/src/App.js
@@ -4,14 +4,20 @@ import { Terminal } from 'xterm';
import './xterm.css';
import { FitAddon } from 'xterm-addon-fit';
import React from 'react';
+import { Resizable } from "re-resizable";
+import ResizeObserver from "react-resize-observer";
export default class App extends React.Component {
constructor(props) {
super(props);
+ this.fitAddon = new FitAddon();
this.term = new Terminal({
+ screenKeys: true,
+ useStyle: true,
cursorBlink: true,
fontSize: 18,
fontWeight: 900,
+ fontFamily: `'Fira Mono', monospace`,
theme: {
foreground: "#fff",
background: "#000",
@@ -29,7 +35,10 @@ export default class App extends React.Component {
this.socket = new WebSocket('ws://localhost:8000/ws');
this.socket.binaryType = 'arraybuffer';
- this.fitAddon = new FitAddon();
+ function ab2str(buf) {
+ return String.fromCharCode.apply(null, new Uint8Array(buf));
+ }
+
// this.attachAddon = new AttachAddon(this.socket, false);
this.term.loadAddon(this.fitAddon);
@@ -38,49 +47,41 @@ export default class App extends React.Component {
this.term.open(document.getElementById("xterm"));
this.fitAddon.fit();
- const recvData = data => {
- var type = "text";
-
- if (typeof data !== 'string') {
- data = this.decoder.decode(data);
- var type = "binary";
- }
+ // Terminal events
+ this.term.onData((data) => {
+ this.socket.send(this.encoder.encode("\x00" + data));
+ });
- console.log(`Received ${type}: ${data}`);
- this.term.write(data);
- }
+ this.term.onResize((evt) => {
+ this.socket.send(this.encoder.encode("\x01" + JSON.stringify({ cols: evt.cols, rows: evt.rows })))
+ });
- const sendData = (data, isBinary) => {
- var type = "text";
+ this.term.onTitleChange((title) => {
+ document.title = title;
+ });
- if (isBinary) {
- data = this.encoder.encode(data);
- var type = "binary";
+ // Socket events
+ this.socket.onmessage = (evt) => {
+ if (evt.data instanceof ArrayBuffer) {
+ this.term.write(ab2str(evt.data.slice(1)));
+ } else {
+ alert(evt.data)
}
-
- console.log(`Sending ${type}: ${data}`);
- this.socket.send(data);
+ }
+ this.socket.onclose = (evt) => {
+ this.term.write("Session terminated");
+ this.term.destroy();
}
- this.socket.onmessage = e => recvData(e.data);
- this.term.onKey(Key => {
- switch (Key.domEvent.key) {
- case "Enter":
- sendData('\n');
- break;
-
- default:
- sendData(Key.key);
- break;
+ this.socket.onerror = (evt) => {
+ if (typeof console.log == "function") {
+ console.log(evt)
}
+ }
- });
+ this.fitAddon.fit();
}
- linefeed = () => {
- var shellprompt = "$ ";
- this.term.write("\r\n" + shellprompt);
- };
render() {
return (
@@ -91,7 +92,12 @@ export default class App extends React.Component {
Edit <code>src/App.js</code> and save to reload.
</p>
<div className="terminalContainer">
- <div id="xterm"></div>
+ <div id="xterm" style={{ height: "100%", width: "100%" }} />
+ <ResizeObserver
+ onResize={() => {
+ this.fitAddon.fit();
+ }}
+ />
</div>
</header>
</div>