A very opinionated kiosk UI application based on electron.
Most of the electron
project if focused around desktop application development, which is great! But when you are dealing with public computing (ATM machines, airline ticketing, movie theater ticket vendors, etc), you don't really need all the features a traditional desktop application requires. This includes things like drag-n-drop, system menus, desktop icons, etc.
The job of the oak
module is to give a really easy way to make a kiosk application with modern web technology, so that it's repeatable, scalable, and easy to rapidly prototype for production. It also takes care of a few common Electron work flow issues that aren't immediately apparent, but effect kiosks in a big way. A good example of this would be errors displaying in a dialog window.
We recommend that you install Nodejs via Node Version Manager: https://github.com/creationix/nvm#install--update-script
After you install and follow the instructions at the end (adding nvm
to your path),
$ nvm install 14.16.1
$ nvm use 14.16.1
The recommended way is to install globally, so it's in your $PATH
$ npm install -g oak
If you aren't installing globally, you will have the oak
entrypoint in your local node_modules
directory
$ npm install --save-dev oak # make sure this is saved in your devDependencies
$ node_modules/.bin/oak
If you are using native node modules, you will generally need to rebuild them to function against the version of node running in oak
.
$ cd myApp/
$ npm install
# if you are using oak globally
$ oak-rebuild .
if you are using oak in devDependencies
$ cd myApp/
$ npm install
$ node_modules/.bin/oak-rebuild .
You will need to have the XCode Commandline Tools installed. If you don't, make sure to install XCode and then run:
$ xcode-select --install
The most minimal example, this will launch a fullscreen app. This will also inject the oak
object into the client side window.oak
// index.js
const oak = require('oak')
// when oak is ready, we can tell it to load something
oak.on('ready', () => {
// loading takes an options object with a `url`, second parameter is an optional callback
oak.load({
url: 'http://www.mywebapp.com'
}) // or callback)
})
$ oak index.js
When you start your app, the
oak
module is automatically resolved in modules, meaning you don't need to include it in yourpackage.json
file. This is similar to the way electron exposes it's own modules automatically.
You can use any fully qualified URL, to simply launch a fullscreen webpage.
$ oak http://www.zivelo.com/
You can load a single .html
file as well, but just have a fully qualified path.
$ oak file://${pwd}/path/to/index.html
You can also load a .json
file, which contains the same configuration you would pass to oak.load()
.
Example: myOptions.json
{
"url": "http://www.zivelo.com",
"fullscreen": false,
"ontop": false
}
$ oak myOptions.json
$ oak --help
Usage: oak [options] [command] <uri>
If you load oak with a script path, no commandline options will apply automatically.
Options:
-V, --version output the version number
-b, --background [String] Hex background color for initial window. Example: #f0f0f0 (default: "#000000")
-f, --fullscreen [Boolean] Set the window to full width and height. This overrides the --size option (default: true)
-k, --kiosk [Boolean] Kiosk mode, which is fullscreen by default. On OSX this will cause the workspace to shift to a whole new one (default: false)
-s, --size [String] Window size in WIDTHxHEIGHT format. Example: 1024x768. This will over ride both --kiosk and --fullscreen
-x, --x [Number] Window X position (default: 0)
-y, --y [Number] Window Y position (default: 0)
-t, --title [String] Window title (default: "Oak")
-t, --ontop [Boolean] Start window ontop of others (default: true)
-D, --display [Number] Display to use (default: 0)
-S, --shortcut [List] Register shortcuts, comma separated. reload,quit (default: [])
-u, --useragent [String] User-Agent string
-F, --frame [Boolean] Show window frame (default: false)
--show [Boolean] Show window on start (default: true)
-n, --node [Boolean] Enable node integration (default: false)
-i, --insecure [Boolean] Allow insecure connections (not recommended) (default: false)
-c, --cache [Boolean] Use standard caching, setting this to false has the same effect as the --disable-http-cache chrome flag (default: true)
-d, --debugger [Boolean] Open chrome dev tools on load (default: false)
-h, --help output usage information
Commands:
version [options] [type] Prints version, options are are `all`, `oak`, `electron`, `node`
oak.load(options[, callback])
Most of these options are wrapping electron.js BrowserWindow
options, but some are specific to our kiosk use-case. This method returns the Window
object
options
: Object
url
: - Not optionalString
- The url
option is the only one required, and will load any valid URI// load a local HTML file
url: 'file://' + require('path').join(__dirname, 'index.html')
// load your own webserver
url: 'http://localhost:8080'
title
: String OAK
- The window titledisplay
: Number 0
- Your display number, and defaults to your main displayfullscreen
: Boolean true
- Set the window to max height and widthkiosk
: Boolean false
- Sets kiosk modeontop
: Boolean true
- Set the window to be always on top of othersshow
: Boolean true
- Start the window shown, this will also show the window whenever it is reloadedsize
: String - Window size in WIDTHxHEIGHT format. Example: 1024x768. This will over ride both kiosk
and fullscreen
x
: Number 0
- X positiony
: Number 0
- Y positionshortcut
Objectreload
Boolean false
- enable CommandOrControl+Shift+R to reload the windowquit
Boolean false
- enable CommandOrControl+Shift+X to close the appbackground
: String #000000
- Hex color of the window backgroundframe
: Boolean false
- Show window framescripts
: Array path
- Local node scripts or modules to load into the window
during pre-dom phase. This can be a object with name
and path
if you want the window.whatever
script to be named flags
: Array - Chrome launch flags to set while starting the windowinsecure
Boolean false
- allow running and displaying insecure content (not recommended at all)sslExceptions
Array - Bypass SSL security for specific hosts. This uses a host pattern. Example: *.mysite.com
cache
Boolean true
- Enable HTTP cache flag for chromeuserAgent
: String - Defaults to 'Oak/' + oak.version
callback
: [Function] - Executed when the ready
function has firedoak.getDisplays()
Returns the current displays, and their metadata. You can use the id
property to specify a window in oak.load
properties. An example response:
[
{
"id": 0,
"bounds": {
"x": 0,
"y": 0,
"width": 1920,
"height": 1080
},
"workArea": {
"x": 0,
"y": 0,
"width": 1920,
"height": 1080
},
"size": {
"width": 1920,
"height": 1080
},
"workAreaSize": {
"width": 1920,
"height": 1080
},
"scaleFactor": 1,
"rotation": 0,
"touchSupport": "unknown"
}
]
oak.sslExceptions
Bypass SSL security for specific hostnames. This is an array of host patterns, which follow the glob pattern of minimatch.
oak.sslExceptions = [
'*.example.com',
'subdomain.example.com'
]
oak.log
Returns a pino
instance for logging. By default the DEBUG
environment variable is set to false
, and will only log messages with the level of error
or greater.
If you run DEBUG=true
, you will get anything with a debug
level or higher, including verbose window information.
oak.load()
returns a Window
object with methods and events. Each instance of oak.load()
returns a unique object for that window, and the methods are mirrored for both the node side and client (renderer) side.
.send(event[, payload])
Send events to the window
event
: String - the event namespace, delimited by .
payload
: Any - whatever data you want to send along.
Example: window.send('myEvent', { foo: 'bar' })
.on(event, callback)
This is an instance of EventEmitter2
ready
- Will emit the ready event, and also execute the optional callbackreload
- The window has reloaded
oldUrl
- previous URLnewUrl
- new resolved URLlocation
- A window location change has happened (will not fire if window.location = X
is called in the rendered)
oldUrl
- previous URLnewUrl
- new resolved URLoldSession
- previous session IDnewSession
- new session IDloadFailed
- The window load failed
opts
: Object - original options usederr
: Errorunresponsive
- The window has hung and become unresponsive.location(url)
Set the URL location of the window. This will fire a location
event.
url
: String - URL to load.reload(cache)
Reload the window.
cache
: Boolean false
- Reload the window without cache. This will fire a reload
event..debug()
Toggle the chrome debugger
.show()
Show the window
.hide()
Hide the window
.focus()
Set the desktop focus to this window
.disableZoom()
Disables pinch zoom or any window zoom in the browser window
id
Unique id
of that window.
The window fires events from electrons BrowserWindow
and webContents
. The only event fired from that set into the renderer is dom-ready
.
note: If you do a send
of the same event from the renderer side, it will look like the same event coming from electron events. So be careful and watch your namespaces for conflicts!
If you would like to use
Check out the examples folder!
To get started running oak
in Docker... you will need to have Docker installed. You can install from here, or on Linux systems, run this script:
curl -sSL https://get.docker.com/ | sh
# add your user to the docker group
sudo usermod -aG docker $(whoami)
You will also need an X server running (xorg
). If you are on OSX, go ahead and follow the steps below to get setup.
This example is for debian based systems, you can accomplish the same by getting docker
and docker-compose
running yourself.
Install python-setuptools
sudo apt-get install -y python-setuptools
Install pip
sudo easy_install pip
Install docker-compose
pip install docker-compose>=1.8.0
Allow your X
server to allow outside connections. Make sure to disable this after you are finished!
xhost +
docker-compose up
You should turn off your open xhost after you are finished developing.
docker-compose down
xhost -
I'm not going to lie... this is a pain in the ass.
OSX doesn't have xorg
, or any build in X server by default. You are going to be using socat
to proxy Xquartz via TCP so that you can use your IP address the docker container. It may be easier to start up a VM running ubuntu or debian.
Install homebrew
Homebrew is a easy way to install linux packages on OSX. In your Terminal
app:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Install socat
and python
socat
will be needed to forward your X server socket, in order to display a window on your desktop.
brew install socat
python
is useful for a number of reasons, but in our case, a means to get docker-compose
. When you install python
, you get the pip
program along with it.
brew install python
Install docker-compose
Rather than using straight docker
commands, we use docker-compose
to simplfy orchestrating multiple containers. docker-compose
uses a .yml
file to describe docker commands and run them.
pip install docker-compose
Install XQuartz, which is a X server for OSX.
Open XQuartz, go to Preferences > Security > Allow connections from network clients.
In Terminal
, run socat
to proxy your X server connection via TCP:
socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"
After you run this, it will be waiting for connections, so don't close this Terminal
window.
Edit docker-compose.osx.yml
Replace the X's with your IP address. This will resolve your socat
connection to the container, which is proxying XQuartz.
environment:
- DISPLAY=XXX.XXX.XXX.XXX:0
If your IP address was 192.168.0.5
, the line would be DISPLAY=192.168.0.5:0
. Don't forget the :0
and the end, that specifys that it's the first display, not a port.
In your oak
directory, run docker-compose -f docker-compse.osx.yml up
Sorry but you are a little on your own as far as an X server goes! In the future we may update this readme to provide info for developing on Windows. In the mean time... Cygwin?