import React from 'react'; import { InputGroup } from 'reactstrap'; import ResizeObserver from "react-resize-observer"; import { ToggleSlider } from 'react-toggle-slider'; import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import { ThemeContext, themes } from './theme'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSun, faMoon } from '@fortawesome/free-solid-svg-icons'; import './App.css'; import './xterm.css'; function App() { const [darkMode, setDarkMode] = React.useState(true); return (
{({ changeTheme }) => (
{ setDarkMode(!darkMode); changeTheme(darkMode ? themes.light : themes.dark); }} />
)}
); } class TerminalManager extends React.Component { constructor(props) { super(props); const ogTermId = (new Date().getMilliseconds()).toString(); this.state = { curTermID: 0, terminals: [ ], numberOfTerminals: 1, }; this.maxTerminals = 5; this.minTerminals = 1; } handleTerminalCallback = (id, terminal, socket) => { const curTerminals = this.state.terminals.slice(); this.setState({ curTermID: id, terminals: [curTerminals, {tid: id, terminal, socket}], }, () => { //console.log("curTermID" + this.state.curTermID); //console.log("terminals are below"); //console.log(this.state.terminals); } ); } createNewTerminal = () => { //console.log(" createNewTerminal: Beginning new terminal creation"); const id = this.state.curTermID + 1; const curTerminals = this.state.terminals.slice(); const numTerm = this.state.numberOfTerminals + 1; //console.log(" createNewTerminal: curTermID: " + this.state.curTermID); //console.log(" createNewTerminal: terminals: "); //console.log(this.state.terminals); //console.log(" createNewTerminal: numberOfTerminals: " + this.state.numberOfTerminals); //console.log(" createNewTerminal: "); //console.log(" createNewTerminal: id: " + id); //console.log(" createNewTerminal: curTerminals: "); //console.log(curTerminals); //console.log(" createNewTerminal: numTerm: " + numTerm); if (id > this.maxTerminals || numTerm > this.maxTerminals) { alert("Maximum number of terminals created"); return; } let termId = (new Date().getMilliseconds()).toString(); curTerminals.push(); this.setState({ curTermID: id, terminals: curTerminals, numberOfTerminals: numTerm, }, () => { //console.log(" createNewTerminal: curTermID " + this.state.curTermID); //console.log(" createNewTerminal: terminals are below"); //console.log(this.state.terminals); //console.log(" createNewTerminal: numberOfTerminals: " + this.state.numberOfTerminals); this.render(); } ); } deleteCurTerminal = () => { //console.log(" deleteCurTerminal: Beginning new terminal creation"); const curId = this.state.curTermID; let id = curId - 1; const numTerm = this.state.numberOfTerminals - 1; let curTerminals1; let curTerminals2; if (numTerm < this.minTerminals) { alert("Maximum number of terminals deleted"); return; } if (id < 0 || curId === 0) { id = numTerm - 1; curTerminals1 = this.state.terminals.slice(curId + 1); } else { curTerminals1 = this.state.terminals.slice(0, curId); curTerminals2 = this.state.terminals.slice(curId + 1); } //console.log(" deleteCurTerminal: curTermID: " + this.state.curTermID); //console.log(" deleteCurTerminal: terminals: "); //console.log(this.state.terminals); //console.log(" deleteCurTerminal: numberOfTerminals: " + this.state.numberOfTerminals); //console.log(" deleteCurTerminal: "); //console.log(" deleteCurTerminal: id: " + id); //console.log(" deleteCurTerminal: curTerminals1: "); //console.log(curTerminals1); //if (curId !== 0) { // console.log(" deleteCurTerminal: curTerminals2: "); // console.log(curTerminals2); //} //console.log(" deleteCurTerminal: numTerm: " + numTerm); //console.log(" deleteCurTerminal: Begin switching other terminals to hidden"); //console.log(" deleteCurTerminal: Altering visibility of terminal"); //console.log(curTerminals1[id]); if (curId !== 0) { for(const element of curTerminals2) { curTerminals1.push(element); } } this.setState({ curTermID: id, terminals: curTerminals1, numberOfTerminals: numTerm }, () => { //console.log(" deleteCurTerminal: curTermID " + this.state.curTermID); //console.log(" deleteCurTerminal: terminals are below"); //console.log(this.state.terminals); //console.log(" deleteCurTerminal: numberOfTerminals: " + this.state.numberOfTerminals); this.render(); } ); } goToNextTerminal = () => { //console.log(" goToNextTerminal: Switching to next terminal") let id = this.state.curTermID + 1; //console.log(" goToNextTerminal: id " + id); if (id >= this.state.numberOfTerminals) { //console.log(" goToNextTerminal: Looping back to first terminal") id = 0; } this.setState({ curTermID: id }, () => { //console.log(" goToNextTerminal: curTermID " + this.state.curTermID); this.render(); }); } goToLastTerminal = () => { //console.log(" goToLastTerminal: Switching to last terminal") let id = this.state.curTermID - 1; //console.log(" goToLastTerminal: id " + id); if (id < this.minTerminals - 1) { //console.log(" goToLastTerminal: Looping back to first terminal") id = this.state.numberOfTerminals - 1; } this.setState({ curTermID: id }, () => { //console.log(" goToLastTerminal: curTermID " + this.state.curTermID); this.render(); }); } render() { let id = this.state.curTermID; //console.log(" render(): Current term"); //console.log(this.state.terminals[id]); let arr = [
{this.state.terminals[0]}
] //console.log("i " + 0 + " | (this.state.curTermID === i) = " + (this.state.curTermID === 0)); for(let i = 1; i < this.state.numberOfTerminals; i++) { //console.log("i " + i + " | (this.state.curTermID === i) = " + (this.state.curTermID === i)); arr.push(
{this.state.terminals[i]}
); } //console.log("arr"); //console.log(arr); return (
{ arr } this.createNewTerminal()} deleteCurTerminal={() => this.deleteCurTerminal()} goToNextTerminal={() => this.goToNextTerminal()} goToLastTerminal={() => this.goToLastTerminal()} />
); } } class TerminalComponent 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", cursor: "#fff", cursorAccent: "#000", selection: "#fff", }, }); this.isConnecting = false; this.terminalId = "xterm" + this.props.createdAt; } componentDidMount() { this.encoder = new TextEncoder(); this.decoder = new TextDecoder(); this.socket = new WebSocket('ws://localhost:8000/ws'); this.socket.binaryType = 'arraybuffer'; //console.log(" About to load xterm interior"); this.term.loadAddon(this.fitAddon); //console.log("this.term.loadAddon(this.fitAddon);"); this.term.open(document.getElementById(this.terminalId)); //console.log("this.term.open(document.getElementById('xterm'));"); //console.log(this.term); this.fitAddon.fit(); //console.log("this.fitAddon.fit();"); // Terminal events this.term.onData((data) => { //this.socket.send(this.encoder.encode("\x00" + data)); //console.log("xterm onData preparing to send"); this.handleSend(this.encoder.encode("\x00" + data)); }); this.term.onResize((evt) => { //this.socket.send(this.encoder.encode("\x01" + JSON.stringify({ cols: evt.cols, rows: evt.rows }))); this.handleSend(this.encoder.encode("\x01" + JSON.stringify({ cols: evt.cols, rows: evt.rows }))); }); this.term.onTitleChange((title) => { document.title = title; }); // Socket events this.socket.onmessage = (evt) => { if (evt.data instanceof ArrayBuffer) { this.term.write(this.decoder.decode(evt.data.slice(1))); } else { alert(evt.data) } } this.socket.onclose = (evt) => { this.term.write("\nSession terminated"); } this.socket.onerror = (evt) => { if (typeof console.log == "function") { console.log(evt) } } this.fitAddon.fit(); } handleSend(message) { if (this.socket.readyState === WebSocket.OPEN) { this.isConnecting = false; this.socket.send(message); } else if (this.socket.readyState === WebSocket.CONNECTING && !this.isConnecting) { this.isConnecting = true; this.socket.addEventListener('open', this.handleSend(message)); } else { } }; render() { return (
{ this.fitAddon.fit(); }} />
); } } class Sidebar extends React.Component { // a sidebar which can take you to other pages // constructor(props) { // super(props); // } createTemplate(){ // location.replace("index.html"); //console.log("create template"); } toStudentView() { // location.replace("index.html"); //console.log("student view"); } createNewTerminal() { } goToNextTerminal() { } goToLastTerminal() { } logOut() { // location.replace("index.html"); window.location.replace("/"); //console.log("log out"); } render() { return (
  • this.createTemplate()}>Create Template
  • this.toStudentView()}>View as Student
  • this.props.createNewTerminal()}>Create New Terminal
  • this.props.deleteCurTerminal()}>Delete Current Terminal
  • this.props.goToNextTerminal()}>Next Terminal
  • this.props.goToLastTerminal()}>Last Terminal
  • this.logOut()}>Log Out
); } } export default App;