Scratch the Surface of NodeGUI

Scratch the Surface of NodeGUI

I’ve ported my little demo app from Proton Native to NodeGUI to see what the API and ecosystem are like. First impressions are this is a very powerful framework with a plethora of possibilities. It comes in two flavours vanilla js and react. I have a love hate with react right now but it makes a lot more sense and was actually pleasant to code with. The API reminds me a lot of React Native unsurprisingly it borrows some things from it.

There are some similarities with Proton Native like the layout engine (Yoga flexbox) but right now in my opinion NodeGUI is much more polished and finished. There were a few deal breakers for me with PN also like I wanted an event fired when the window closed so I can clean up any running processes. Luckily NG provides. Again I won’t detail the getting started but cloning this repo, installing the dependencies then one shell npm run dev and another npm start. One last point for NG over PN is the final app is around 40mb bundled versus 120mb+.

Below is the app I’m hacking together for fun:

I’m using some of nordvpn’s undocumented APIs and the official CLI tool to muddle this together. Things like connect and disconnect work, also the country flag emoji shows your current country connection. There is a custom icon in the dock and a statusbar icon.

Some of the non-typical React things I’ve got going on are making use of mobx to wrap stateless functional components or whatever they are called now

const Toggle = observer(() => (
  <View id="toggleContainer">
    <View id="toggle" style={backgroundToggleStyle()}>
      <Button
        id="toggleButton"
        style={toggleStyle()}
        on={buttonHandler}
      ></Button>
    </View>
  </View>
));

I love mobx because like vue it has elegant computed properties like so

 @computed
  get isOffline() {
    return (
      this.status.typeof === "string" &&
      this.status.includes("Status: Disconnected")
    );
  }

This isOffline is watching @observable status and updating automatically.

With this app I am spinning up and down and all around a lot of child processes, I found this library simplified my life execa. Here’s an example

const { stdout } = await execa("nordvpn", ["countries"]);

Glorious async/await!

I was doing some more crazy pipping stuff with the jq library cleverly invoked with npx but decided to simplify my life

function getCountries() {
  return new Promise(async (res, rej) => {
    let countriesArray;
    const jqIt = execa(
      "npx",
      ["jq", "--raw-output", "[.[].country] | sort | unique | .[]"],
      { kill: 10000 }
    );
    const subprocess = execa("curl", [
      "--silent",
      "https://api.nordvpn.com/server",
    ]);
    subprocess.stdout.pipe(jqIt.stdin);
    jqIt.stdout.on("data", function (data) {
      console.log(data);
      countriesArray = data;
    });
    jqIt.stdout.on("end", () => {
      const formattedCountries = countriesArray
        ? countriesArray.toString().trim("").split("\n")
        : [];
      res(formattedCountries);
    });
  });
}

To get that little flag at the top I check the status of the connection and if not disconnected I do some dark arts with String manipulation to take “Connecting to Japan #547 (jp547.nordvpn.com)” and get the country code “jp”

            .trim("")
            .replace(/\r?\n|\r/g, "")
            .replace(/^.*\(|\)/g, "")
            .substr(0, 2);

I also have buttons with each country listed to allow different connections. I had mapped out sweet emoji flags through more dark arts but for some reason the flag emoji’s don’t work in the body of NodeGUI.

So the only downsides right now I think are missing examples and documentation. The library is typed and that is about all the docs you get for most components. Some are shown well such as Button others are a mystery ScrollArea (it says every prop is optional)

All in all a pretty amazing framework, with the power of node.js and any plugin the sky is the limit.