diff options
-rw-r--r-- | package-lock.json | 46 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/App.js | 76 |
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", @@ -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> |