Replacing the tty.js frontend with xterm.js

Tty.js is a web based terminal emulator, both server and client side included. The frontend is provided by term.js, which is quite dated. There was not any major development since 2013, and a few days ago the “no longer maintained” label was put on it. For me the biggest disadvantage of it is that it does not support the Hungarian keyboard layout, the special characters need be to inserted with ASCII codes. However the frontend is easily replacable with the newer xterm.js.

There is nothing wrong with the server side of tty.js, we can install it as it is:

npm install tty.js

It can be quickly setup thanks to the instructions on the Github page, the frontend part gets served from the static folder. For replacing the frontend we should replace the index.html there. For simplicity we should clone the whole xterm.js inside this static folder:

cd ~/node_modules/tty.js/static
git clone https://github.com/sourcelair/xterm.js.git xterm

Update: since the ed1a31d1 commit you need to compile xterm.js and instead of the src/xterm.js use the build/xterm.js.

After this we should replace the content of the index.html, so that is uses the libs of xterm.js, we can see a sample in the xterm.js demo:

<!doctype html>
<head>
  <title>xterm.js</title>
  <link rel="stylesheet" href="xterm/src/xterm.css">
  <link rel="stylesheet" href="xterm/style.css">
  <script src="socket.io/socket.io.js"></script>
  <script src="xterm/src/xterm.js"></script>
  <script src="xterm/addons/fit/fit.js"></script>
  <script src="main.js" defer></script>
</head>
<body>
  <h1>xterm</h1>
  <div id="terminal-container"></div>
</body>

The main.js file which comes with the xterm.js demo is pretty useless, it only misguides the developers how it handles the enter and the other key presses. Contrary to that there is a good example on the term.js Github page and fortunately xterm.js is backwards compatible. So much code is enough for a basic main.js:

var terminalContainer = document.getElementById('terminal-container'),
    term = new Terminal(),
    socket,
    termid;

term.open(terminalContainer);
term.fit();

if (document.location.pathname) {
  var parts = document.location.pathname.split('/')
    , base = parts.slice(0, parts.length - 1).join('/') + '/'
    , resource = base.substring(1) + 'socket.io';

  socket = io.connect(null, { resource: resource });
} else {
  socket = io.connect();
}

var cols=80,
    rows=24;                   

socket.emit('create', cols, rows, function(err, data) {
  if (err) return self._destroy();
  self.pty = data.pty;
  self.id = data.id;
  termid = self.id;
  term.emit('open tab', self);
});

term.writeln('Welcome to xterm.js');
term.writeln('Connecting to websocket...');

term.on('data', function(data) {
  socket.emit('data', termid, data);
});

term.on('resize', function(data) {
  socket.emit('resize', termid, term.cols, term.rows);
});

socket.on('connect', function() {
  term.writeln('Connected.');
  term.writeln('');
});

socket.on('data', function(id, data) {
  term.write(data);
});

With the above modifications we can get a pretty good web based terminal, which however will have its own bugs. Resizing is not handled and the long prompts are line wrapping. When we use a custom webfont, then it can be quite pretty.