Skip to content

Instantly share code, notes, and snippets.

@GitSquared
Last active September 20, 2023 16:55
Show Gist options
  • Save GitSquared/2049d7e85eaddeeeaa44e8404fe0b0e1 to your computer and use it in GitHub Desktop.
Save GitSquared/2049d7e85eaddeeeaa44e8404fe0b0e1 to your computer and use it in GitHub Desktop.

Revisions

  1. GitSquared revised this gist Sep 30, 2017. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions terminal.class.js
    Original file line number Diff line number Diff line change
    @@ -91,9 +91,7 @@ class Terminal {

    this.resize = (cols, rows) => {
    this.term.resize(cols, rows);
    setTimeout(() => {
    this.sendSizeToServer();
    }, 50);
    this.sendSizeToServer();
    }
    } else if (opts.role === "server") {

  2. GitSquared created this gist Sep 30, 2017.
    35 changes: 35 additions & 0 deletions receiver-demo.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <title>Remote terminal</title>

    <link rel="stylesheet" href="xterm.css" />

    <script src="terminal.class.js"></script>

    <style>
    div#terminal {
    width: 500px;
    height: 300px;
    }
    </style>
    </head>
    <body>
    <div id="terminal-container"></div>

    <script>
    window.terminalRemote = new Terminal({
    role: "client",
    parentId: "terminal-container",
    host: "127.0.0.1",
    port: 3000
    });

    // Wait for terminal to be initialized then resize it to fit in the container
    setTimeout(() => {
    window.term.fit();
    }, 500);
    </script>
    </body>
    </html>
    22 changes: 22 additions & 0 deletions server-demo.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    // Server demo (running node.js)

    const Terminal = require("./terminal.class.js").Terminal;
    let terminalServer = new Terminal({
    role: "server",
    shell: (process.platform === "win32") ? "cmd.exe" : "bash",
    port: 3000
    });

    terminalServer.onclosed = (code, signal) => {
    console.log("Terminal closed - "+code+", "+signal);
    app.quit();
    };
    terminalServer.onopened = () => {
    console.log("Connected to remote");
    };
    terminalServer.onresized = (cols, rows) => {
    console.log("Resized terminal to "+cols+"x"+rows);
    };
    terminalServer.ondisconnected = () => {
    console.log("Remote disconnected");
    };
    164 changes: 164 additions & 0 deletions terminal.class.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,164 @@
    /* ****************************************************************
    Create a terminal receiver or server.
    Currently the server only accepts one connection at a time and
    does not support SSL. If you wish to use to connect to a
    terminal over the internet, PLEASE do not use this class as-is.
    --SERVER-------------------------------------------------------
    Server requirements:
    - node-pty
    - ws
    Server usage:
    terminalServer = new Terminal({
    role: "server",
    port: 3000, // 3000 if empty
    shell: "bash" // Command to run, "bash" by default
    })
    Server events:
    terminalServer.onopened // Connected to a receiver
    .ondisconnected // Disconnected from receiver
    .onclosed // Terminal was exited
    // Args: code, signal
    .onresized // Terminal was resized
    // Args: cols, rows
    Server methods:
    None.
    --CLIENT--------------------------------------------------------
    Client requirements:
    - xterm.js
    - browser with WebSocket support
    Client usage:
    terminalClient = new Terminal({
    role: "client",
    parentId: "someid", // ID of the terminal container element
    port: 3000, // 3000 if empty
    host: "127.0.0.1" // Localhost by default
    })
    Client events:
    None.
    Client methods:
    terminalClient.fit() // Resizes the terminal to match the container's size
    .resize(cols, rows) // Manual resize
    ******************************************************************* */

    class Terminal {
    constructor(opts) {
    if (opts.role === "client") {
    if (!opts.parentId) throw "Missing options";

    this.xTerm = require("xterm");
    this.xTerm.loadAddon('attach');
    this.xTerm.loadAddon('fit');

    this.sendSizeToServer = () => {
    let cols = this.term.cols.toString();
    let rows = this.term.rows.toString();
    while (cols.length < 3) {
    cols = "0"+cols;
    }
    while (rows.length < 3) {
    rows = "0"+rows;
    }
    this.socket.send("ESCAPED|-- RESIZE:"+cols+";"+rows);
    };

    this.term = new this.xTerm({
    cols: 80,
    rows: 24
    });
    this.term.open(document.getElementById(opts.parentId), true);

    let sockHost = opts.host || "127.0.0.1";
    let sockPort = opts.port || 3000;

    this.socket = new WebSocket("ws://"+sockHost+":"+sockPort);
    this.socket.onopen = () => {
    this.term.attach(this.socket);
    };
    this.socket.onerror = (e) => {throw e};

    this.fit = () => {
    this.term.fit();
    setTimeout(() => {
    this.sendSizeToServer();
    }, 50);
    }

    this.resize = (cols, rows) => {
    this.term.resize(cols, rows);
    setTimeout(() => {
    this.sendSizeToServer();
    }, 50);
    }
    } else if (opts.role === "server") {

    this.Pty = require("node-pty");
    this.Websocket = require("ws").Server;

    this.onclosed = () => {};
    this.onopened = () => {};
    this.onresize = () => {};
    this.ondisconnected = () => {};

    this.tty = this.Pty.spawn(opts.shell || "bash", [], {
    name: 'xterm-color',
    cols: 80,
    rows: 24,
    cwd: process.env.PWD,
    env: process.env
    });

    this.tty.on('exit', (code, signal) => {
    this.onclosed(code, signal);
    });
    this.wss = new this.Websocket({
    port: opts.port || 3000,
    clientTracking: true,
    verifyClient: (info) => {
    if (this.wss.clients.length >= 1) {
    return false;
    } else {
    return true;
    }
    }
    });
    this.wss.on('connection', (ws) => {
    this.onopened();
    ws.on('message', (msg) => {
    if (msg.startsWith("ESCAPED|-- ")) {
    if (msg.startsWith("ESCAPED|-- RESIZE:")) {
    msg = msg.substr(18);
    let cols = msg.slice(0, -4);
    let rows = msg.substr(4);
    this.tty.resize(Number(cols), Number(rows));
    this.onresized(cols, rows);
    }
    } else {
    this.tty.write(msg);
    }
    });
    this.tty.on('data', (data) => {
    try {
    ws.send(data);
    } catch (e) {
    // Websocket closed
    }
    });
    });
    this.wss.on('close', () => {
    this.ondisconnected();
    });
    } else {
    throw "Unknow purpose";
    }
    }
    }

    module.exports = {
    Terminal
    };