Meet Evala - your terminal in the browser
On my machine I have four applications always open - VSCode, Chrome, iTerm and Slack. I spend most of my time in Chrome and VSCode. My editor is full with awesome extensions and I feel pretty good there. What I am doing for the browser is making sure that I have fewer tabs open and install only extensions that I really use. One thing though I can achieve so far. I can't find the perfect new tab extension.
I tried the most famous ones - Momentum, Currently, ZenTab and a couple of others. My main goal was simplicity, seeing the time and the forecast plus maybe a TODO list or a notebook. All the options that I tried look good but some of them provide a little bit more features then I wanted. Those which were simple didn't completely match with my requirements. So, I am a programmer and I thought why not reinvent the wheel again. Because is fun and because I wanted different I created Evala - a web app that is available as new-tab extension for Firefox or Chrome.
It shows the time, it shows the weather forecast from DarkSky and opens my shell right there, in the browser. Not like there are no extensions that provide the same but what I wanted was really these three things - clock, forecast and terminal.
Of course we can't fully implement the last bit without little help from the
outside. That is why there is a
npm package called evala
. Once installed we have an evala
command available.
> npm install evala -g
> evala --shell=$SHELL
--shell=$SHELL
argument is important because otherwise we will
end up using just bash
(or cmd.exe
under windows).
The evala
command starts a web server that listens on
9788
port. In fact the whole app is available at
http://0.0.0.0:9788 and we can open it as a regular web page (no need to
install the browser extension).
When we open the app the browser connects to 9788
port and gets
an access to a spawn shell and basically provide the same experience.
Let me talk a little bit about what I used.
The front-end
The front-end part is made in
React by using
Stent for data flow and state
management. XTerm.js provides the shell and
to be honest is the best option for such kind stuff. Even VSCode uses it
internally. I had some wonderings how to actually make it work but the demo of
the project helped me a lot. The easiest way is to use the
attach
addon
which is basically saying "Please, send whatever the user types to
this socket and pipe the stdout from the actual shell in here".
socket = new WebSocket(...);
socket.onopen = () => {
term.attach(socket);
};
As long as the evala
process is running, there is an open socket
and the terminal replication works.
The back-end
On the server side we have a small Express server that serves the bundled app as a static resource at http://0.0.0.0:9788. We also have express-ws package that provides web sockets support and node-pty that forks processes with pseudoterminal file descriptors. The rest was just wiring:
// opening a spawned terminal
let term = pty.spawn(shell, [], {
name: 'xterm-color',
cwd: process.env.PWD,
env: process.env
});
// getting the stdout of the terminal and sending through the socket to the client
term.on('data', function (data) {
ws.send(data);
});
// getting the user's input and sending to the spawned terminal
ws.on('message', function (msg) {
term.write(msg);
});
It works pretty well I would say. All the key mappings and stuff are just like in my iTerm. The aliases and setup are also the same. Overall XTerm.js and node-pty play well together
Final words
I have several points in my TODO list. Like for example add a screen split so we have multiple terminals in one place. Fixing some bugs around key mappings and add some new once to switch between terminals. I'll be happy if you try the app (Firefox or Chrome) and let me know what you think. The code is of course free and published on GitHub - github.com/krasimir/evala.