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);
this.termsMade = 0
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;
//termId = (new Date().getMilliseconds()).toString();
termId = this.termsMade + 1;
curTerminals.push();
this.termsMade = termId;
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);
}
);
}
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();
}
);
}
goToThisContainer = (containerId) =>
{
let containers = this.state.terminals.slice();
let i = 0;
//console.log("Searching for Container " + containerId);
while (i < containers.length && containers[i].key !== containerId)
{
//console.log("Not key " + containers[i].key);
i++;
}
i = i % this.state.numberOfTerminals;
//console.log("curContainer is " + curContainer);
this.setState({
curTermID: i
}, () => {
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.deleteCurTerminal()}
goToNextTerminal={() => this.goToNextTerminal()}
goToLastTerminal={() => this.goToLastTerminal()}
goToThisContainer={this.goToThisContainer}
/>
);
}
}
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);
//console.log("this.props.conatiners");
//console.log(this.props.containers);
this.state = {
//containers: this.props.containers
}
}
createTemplate(){
//location.replace("index.html");
//console.log("create template");
}
toStudentView() {
// location.replace("index.html");
//console.log("student view");
}
logOut() {
// location.replace("index.html");
window.location.replace("/");
//console.log("log out");
}
render() {
const containers = this.props.containers;
let containerButtons = [];
let numOfContainers = containers.length;
//console.log("containers");
//console.log(this.state.containers);
if (containers === null || numOfContainers < 1)
{
containerButtons = [
this.props.goToNextTerminal()}>Next Container
,
this.props.goToLastTerminal()}>Last Container
]
}
else
{
for (let i = 0; i < numOfContainers; i++)
{
const containerKey = containers[i].key;
const containerId = 'goTo' + containerKey;
//console.log("Got key " + containerKey)
//console.log("Got id " + containerId);
containerButtons.push( this.props.goToThisContainer(containerKey)}>Container {containerKey}
)
}
}
return (
this.createTemplate()}>Create Template
this.toStudentView()}>View as Student
this.props.createNewTerminal()}>Create New Container
this.props.deleteCurTerminal()}>Delete Current Container
{ containerButtons }
this.logOut()}>Log Out
);
}
}
export default App;