Package Webpages as Apps with Nativefier

There are a couple of specific web pages I use in separate windows from my other browser tabs, so I can always find them. However this quickly leads to a too-many-browser-windows problem. Instead, I have been turning them into basic standalone applications with a javascript tool: nativefier and I thought I'd share my recipes.

First: the problem. I'm a keyboard-only user and that is less of a problem than you'd think for day-to-day things. I can drive a terminal, browser, text editor, and slack, which is basically all I need for 95% of my week. However I've recently started streaming on Twitch (you can follow lornajanetv if you'd like this sort of content in real time) and managing the stream as well as showing what I'm doing is .... basically I'm streaming a lot of alt+tab overlays!

What I needed was the ability to focus a window immediately from a command. I have a streamdeck, so I can hook the command up to a button. I had some success with wmctrl which can raise a window by title, but since I use multiple workspaces on my main monitor (workspaces do not span displays, configured with gnome-tweak-tool, so the second monitor holds all the stream-related stuff, and is not really on a workspace), raising one firefox window would switch workspaces as well. I needed a new approach.

Enter: Nativefier. It claims to package webpages as electron apps, and since I have OBS doing what I want it to with the window raising behaviour, I thought this might work well.

Package a Web Page with Nativefier

Honestly, this tool does what it says on the tin! I wanted to put my Stream Manager page into an app, so I installed the tool as a global command and then used this:

nativefier --name StreamManagerLJ --single-instance \
https://dashboard.twitch.tv/u/lornajanetv/stream-manager \
--internal-urls ".*?\.twitch\.tv\.*?" \
--inject inject.js

There's a lot going on here so I'll try to break it down.

Name: Use --name to give your application a name. This is useful as you'll want to refer to it later.

Single instance: By making your app single instance, attempting to run another one will simply focus the existing one. I added this because I was still having some workspace changes when I tried to focus the apps with wmctrl and it works brilliantly. As a side-benefit, I have one streamdeck button that either launches or finds this, regardless of what state it is in.

URL: The page to wrap in an app. Same-domain links will work inside the app, but for everything else you'll need to specify what is an internal URL.

Internal URLs: Add a domain or regex here (you can also send multiple of this switch) to tell the app which URLs should be opened inside it. Everything else will break out into your default browser. If you need to auth, you'll need to add the domains of those pages so that it happens inside the app.

Inject: You can include *.js and *.css files to add to the application. More on this later (even I am not putting a tangent this size into a bullet point!)

The command outputs information about where it put the executable - I get a StreamManagerLJ-linux-64/StreamManagerLJ that I can run. Then I can copy that executable somewhere more useful if I want to although I usually leave it where it is and then create a .desktop file to make it executable, set its icon, etc.

Injecting Custom JS for Nativefier

One cool thing is that you can also add some scripts that aren't part of the official webpage. I spent some time trying to work out how to package Vimium, which how I use my browser from the keyboard usually. However, Chrome plugins are intentionally not included, presumably because it would get out of hand really fast! Anyway hopefully I'll figure this out at some point but for now ... a little hackery gives me all I need :)

In the stream manager, I basically need to be able to reliably focus the chat window. For everything else (which is rare), I will use my touch screen or pointing device since I can't actually stream completely keyboard-only so I plug in a wacom tablet for this anyway. Here's the inject.js that I created:

document.addEventListener('keydown', doKey);

function doKey(e) {
    switch(e.code) {
        case "KeyC":
            a = document.getElementsByClassName('chat-input__textarea');
            a[0].firstChild.focus()
            break;
    }
}

It's not much, but it's just enough to give me focus where I need it, very easily. I could also expand to add more keys and more selectors or something. Probably this will evolve over time, but if what I have helps you, then I want to share! Helpfully, the apps do come with devtools enabled so that can help with identifying elements and debugging things.

I came to nativefier as a workaround for a window management problem but I see it as a productivity hack as well, the community seems friendly and has lots of people using it for their most-used apps (trello, gmail) that they wish were desktop-native. The tool was easy to use and I can also patch it to extend it if I need to, which is always reassuring. Let me know what you package and how it goes!

Leave a Reply

Please use [code] and [/code] around any source code you wish to share.

This site uses Akismet to reduce spam. Learn how your comment data is processed.