SlideShare a Scribd company logo
1 of 69
Spin Up Desktop Apps
with Electron.js
whilestevego stevegodin
About Me
About Electron.js
Disclaimer
part1: "Hello, world!"
$ mkdir project && cd project
$ npm init
$ npm install electron prebuilt --save-dev
{
"name": "animal-advisor",
"version": "0.0.0",
"description": "A desktop application for generating Advice Animals.",
"main": "entry.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron-prebuilt": "^0.35.1"
}
}
package.json
.
├── app
│ ├── assets
│ │ └── images
│ │ └── hey-hey-hey.gif
│ ├── main
│ │ └── main.js
│ └── renderer
│ └── main
│ └── index.html
├── entry.js
└── package.json
.../animal-advisor/
require("./app/main/main.js");
const App = require("app");
const BrowserWindow = require("browser-window");
...
App.on("ready", function () {
const mainWindow = new BrowserWindow({
frame: true,
resizable: true,
});
mainWindow.loadURL(`file://${pathTo.renderer}/main/index.html`);
});
entry.js
.../main/main.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Animal Advisor</title>
</head>
<body>
<h1>Hey, World!<h1>
<img src="../../assets/images/hey-hey-hey.gif"></img>
</body>
</html>
.../main/index.html
$ npm start
part2: "Tying it together"
Main Process
Main file in
package.json
Renderer Process
Renderer Process
Renderer Process Web pages
created with
BrowserWindow
const ipcMain = require('electron').ipcMain;
ipcMain.on('asynchronous-message',
function(event, arg) {
console.log(arg); // prints "ping"
event.sender.send('asynchronous-reply', 'pong');
});
Main Process (.../main.js)
const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('asynchronous-reply',
function(event, arg) {
console.log(arg); // prints "pong"
});
ipcRenderer.send('asynchronous-message', 'ping');
Renderer Process (.../index.js)
const ipcMain = require('electron').ipcMain;
ipcMain.on('asynchronous-message',
function(event, arg) {
console.log(arg); // prints "ping"
event.sender.send('asynchronous-reply', 'pong');
});
Main Process (.../main.js)
const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('asynchronous-reply',
function(event, arg) {
console.log(arg); // prints "pong"
});
ipcRenderer.send('asynchronous-message', 'ping');
Renderer Process (.../index.js)
const ipcMain = require('electron').ipcMain;
ipcMain.on('asynchronous-message',
function(event, arg) {
console.log(arg); // prints "ping"
event.sender.send('asynchronous-reply', 'pong');
});
Main Process (.../main.js)
const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('asynchronous-reply',
function(event, arg) {
console.log(arg); // prints "pong"
});
ipcRenderer.send('asynchronous-message', 'ping');
Renderer Process (.../index.js)
const Remote = require('electron').remote;
const BrowserWindow = remote.BrowserWindow;
const Dialog = remote.dialog;
const GlobalShortcut = remote.globalShortcut;
const Menu = remote.menu;
const MenuItem = remote.menuItem;
const Tray = remote.tray;
Renderer Process (.../index.js)
part3: "Animal Advisor"
animal-advisor
├── app
│ ├── assets
│ │ ├── fonts
│ │ ├── images
│ │ └── stylesheets
│ │ ├── base.css
│ │ └── photon.css
│ ├── main
│ │ └── main.js
│ └── renderer
│ └── main
│ ├── index.html
│ └── index.js
├── entry.js
├── lib
│ └── meme.js
└── package.json
.../animal-advisor/
System Notifications
function sendNotification (path) {
const title = "Animal Advisor says..."
const options = {
body: "Image download complete!",
icon: path
}
new Notification(title, options);
}
.../renderer/main/index.js
Tray Icon
const Tray = require("tray");
const Menu = require("menu");
...
App.on("ready", function () {
...
const menuConfig = [ {
label: "Toggle DevTools",
accelerator: "Alt+Command+I",
click: function() {
mainWindow.show();
mainWindow.toggleDevTools();
}
}, {
label: "Quit",
accelerator: "Command+Q",
selector: "terminate:"
} ];
const contextMenu = Menu.buildFromTemplate(menuConfig);
...
});
.../main/main.js
const Tray = require("tray");
const Menu = require("menu");
...
App.on("ready", function () {
...
const menuConfig = [ {
label: "Toggle DevTools",
accelerator: "Alt+Command+I",
click: function() {
mainWindow.show();
mainWindow.toggleDevTools();
}
}, {
label: "Quit",
accelerator: "Command+Q",
selector: "terminate:"
} ];
const contextMenu = Menu.buildFromTemplate(menuConfig);
...
});
.../main/main.js
const Tray = require("tray");
const Menu = require("menu");
...
App.on("ready", function () {
...
const menuConfig = [ {
label: "Toggle DevTools",
accelerator: "Alt+Command+I",
click: function() {
mainWindow.show();
mainWindow.toggleDevTools();
}
}, {
label: "Quit",
accelerator: "Command+Q",
selector: "terminate:"
} ];
const contextMenu = Menu.buildFromTemplate(menuConfig);
...
});
.../main/main.js
const Tray = require("tray");
const Menu = require("menu");
...
App.on("ready", function () {
...
const trayIcon = new Tray(trayIconPath);
trayIcon.setToolTip("Animal Advisor");
trayIcon.setContextMenu(contextMenu);
});
.../main/main.js
Application Menu
...
const applicationMenuConfig = [
{
label: appName,
submenu: [
{
label: `About ${appName}`,
role: "about"
}, {
type: "separator"
}, {
label: "Services",
role: "services",
submenu: []
}, {
type: "separator"
}, {
label: `Hide ${appName}`,
accelerator: "Command+H",
role: "hide"
} ... ] ...
.../main/main.js
...
const applicationMenuConfig = [
{
label: appName,
submenu: [
{
label: `About ${appName}`,
role: "about"
}, {
type: "separator"
}, {
label: "Services",
role: "services",
submenu: []
}, {
type: "separator"
}, {
label: `Hide ${appName}`,
accelerator: "Command+H",
role: "hide"
} ... ] ...
.../main/main.js
const appName = _.startCase(App.getName());
const applicationMenuConfig = [
{
label: appName,
submenu: [
{
label: `About ${appName}`,
role: "about"
}, {
type: "separator"
}, {
label: "Services",
role: "services",
submenu: []
}, {
type: "separator"
}, {
label: `Hide ${appName}`,
accelerator: "Command+H",
role: "hide"
} ...
.../main/main.js
const applicationMenuConfig = [
{ ... }, {
label: 'Window',
role: 'window',
submenu: [
{
label: 'Minimize',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
}, {
label: 'Close',
accelerator: 'CmdOrCtrl+W',
role: 'close'
}, {
type: 'separator'
}, {
label: 'Bring All to Front',
role: 'front'
}
]
}
];
.../main/main.js
const applicationMenuConfig = [
{ ... }, {
label: 'Window',
role: 'window',
submenu: [
{
label: 'Minimize',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
}, {
label: 'Close',
accelerator: 'CmdOrCtrl+W',
role: 'close'
}, {
type: 'separator'
}, {
label: 'Bring All to Front',
role: 'front'
}
]
}
];
.../main/main.js
...
App.on("ready", function () {
...
const applicationMenuConfig = [ ... ];
const applicationMenu =
Menu.buildFromTemplate(applicationMenuConfig);
Menu.setApplicationMenu(applicationMenu);
...
});
.../main/main.js
Context Menu
const Remote = require("remote");
const pathTo = Remote.getGlobal("pathTo");
const Menu = Remote.require('menu');
const MenuItem = Remote.require('menu-item');
...
const adviceAnimalMenu = new Menu();
adviceAnimalMenu.append(new MenuItem({
label: 'Reset',
click: resetAdviceAnimal
}));
adviceAnimalMenu.append(new MenuItem({
label: 'Click me!',
click: function (menuItem) { console.log('¯_(ツ)_/¯'); }
}));
...
adviceAnimalImg.addEventListener('contextmenu',
function (event) {
event.preventDefault();
adviceAnimalMenu.popup(Remote.getCurrentWindow());
}, false);
...
.../renderer/main/index.js
const Remote = require("remote");
const pathTo = Remote.getGlobal("pathTo");
const Menu = Remote.require('menu');
const MenuItem = Remote.require('menu-item');
...
const adviceAnimalMenu = new Menu();
adviceAnimalMenu.append(new MenuItem({
label: 'Reset',
click: resetAdviceAnimal
}));
adviceAnimalMenu.append(new MenuItem({
label: 'Click me!',
click: function (menuItem) { console.log('¯_(ツ)_/¯'); }
}));
...
adviceAnimalImg.addEventListener('contextmenu',
function (event) {
event.preventDefault();
adviceAnimalMenu.popup(Remote.getCurrentWindow());
}, false);
...
.../renderer/main/index.js
const Remote = require("remote");
const pathTo = Remote.getGlobal("pathTo");
const Menu = Remote.require('menu');
const MenuItem = Remote.require('menu-item');
...
const adviceAnimalMenu = new Menu();
adviceAnimalMenu.append(new MenuItem({
label: 'Reset',
click: resetAdviceAnimal
}));
adviceAnimalMenu.append(new MenuItem({
label: 'Click me!',
click: function (menuItem) { console.log('¯_(ツ)_/¯'); }
}));
...
adviceAnimalImg.addEventListener('contextmenu',
function (event) {
event.preventDefault();
adviceAnimalMenu.popup(Remote.getCurrentWindow());
}, false);
...
.../renderer/main/index.js
Native Dialogs
.../renderer/main/index.js
...
const Shell = require("shell");
const Remote = require("remote");
const Menu = Remote.require("menu");
const MenuItem = Remote.require("menu-item");
const Dialog = Remote.require("dialog");
...
.../renderer/main/index.js
...
adviceAnimalMenu.append(new MenuItem({
label: "Save Image as...",
click: showSaveImageAsDialog,
accelerator: "Command+Shift+S"
}));
...
.../renderer/main/index.js
...
function showSaveImageAsDialog() {
const options = { title: "Save Image as..." };
Dialog.showSaveDialog(
Remote.getCurrentWindow(),
options,
handleSaveDialog
);
}
function handleSaveDialog (path) {
const sourcePath =
event.target.src.replace(/file:///,"");
const extension = Path.extname(sourcePath);
const destinationPath = path + extension;
copyFile(
sourcePath,
destinationPath,
handleCopy(destinationPath)
);
}
...
.../renderer/main/index.js
...
function showSaveImageAsDialog() {
const options = { title: "Save Image as..." };
Dialog.showSaveDialog(
Remote.getCurrentWindow(),
options,
handleSaveDialog
);
}
function handleSaveDialog (path) {
const sourcePath =
event.target.src.replace(/file:///,"");
const extension = Path.extname(sourcePath);
const destinationPath = path + extension;
copyFile(
sourcePath,
destinationPath,
handleCopy(destinationPath)
);
}
...
.../renderer/main/index.js
...
function handleCopy (path) {
return function (error) {
if (error) {
Dialog.showErrorBox("Save Image as... failed", error)
} else if (path) {
Shell.showItemInFolder(path);
};
}
};
...
.../renderer/main/index.js
...
function handleCopy (path) {
return function (error) {
if (error) {
Dialog.showErrorBox("Save Image as... failed", error)
} else if (path) {
Shell.showItemInFolder(path);
};
}
};
...
Clipboard
.../renderer/main/index.js
...
const Clipboard = require("clipboard");
...
function setImage (path) {
console.log("Setting image...");
adviceAnimalImg.src = path;
adviceAnimalImg.className = "";
Clipboard.writeImage(path);
sendNotification(path);
}
...
adviceAnimalMenu.append(new MenuItem({
label: "Copy",
click: copyToClipboard,
accelerator: "Command+C"
}));
...
function copyToClipboard () {
const filePath = event.target.src.replace(/file:///,"");
Clipboard.writeImage(filePath);
} ...
.../renderer/main/index.js
...
const Clipboard = require("clipboard");
...
function setImage (path) {
console.log("Setting image...");
adviceAnimalImg.src = path;
adviceAnimalImg.className = "";
Clipboard.writeImage(path);
sendNotification(path);
}
...
adviceAnimalMenu.append(new MenuItem({
label: "Copy",
click: copyToClipboard,
accelerator: "Command+C"
}));
...
function copyToClipboard () {
const filePath = event.target.src.replace(/file:///,"");
Clipboard.writeImage(filePath);
} ...
.../renderer/main/index.js
...
const Clipboard = require("clipboard");
...
function setImage (path) {
console.log("Setting image...");
adviceAnimalImg.src = path;
adviceAnimalImg.className = "";
Clipboard.writeImage(path);
sendNotification(path);
}
...
adviceAnimalMenu.append(new MenuItem({
label: "Copy",
click: copyToClipboard,
accelerator: "Command+C"
}));
...
function copyToClipboard () {
const filePath = event.target.src.replace(/file:///,"");
Clipboard.writeImage(filePath);
} ...
Modules Not Covered
autoUpdater Module
globalShortcut Module
powerMonitor and
powerSaveBlocker Modules
crashReporter Module
part4: "Packaging"
$ npm install electron-packager --save-dev
{
"name": "animal-advisor",
"version": "0.0.0",
"description": "A desktop application for generating Advice Animals.",
"main": "entry.js",
"scripts": {
"start": "electron .",
"package": "electron-packager . "Animal Advisor" --platform=darwin --
arch=x64 --out ./dist --version 0.35.1 --overwrite --
icon=./app/assets/images/icon.icns",
...
package.json
Closing Thoughts
Demo & Questions
whilestevego stevegodin

More Related Content

What's hot

CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest
 
JavaScript Libraries (@Media)
JavaScript Libraries (@Media)JavaScript Libraries (@Media)
JavaScript Libraries (@Media)jeresig
 
WebGL For Game Development Spring 2013
WebGL For Game Development Spring 2013WebGL For Game Development Spring 2013
WebGL For Game Development Spring 2013Tony Parisi
 
HTML5 Dev Conf 2013 Presentation
HTML5 Dev Conf 2013 PresentationHTML5 Dev Conf 2013 Presentation
HTML5 Dev Conf 2013 PresentationIker Jamardo
 
SenchaCon 2016: Improve Workflow Driven Applications with Ext JS Draw Package...
SenchaCon 2016: Improve Workflow Driven Applications with Ext JS Draw Package...SenchaCon 2016: Improve Workflow Driven Applications with Ext JS Draw Package...
SenchaCon 2016: Improve Workflow Driven Applications with Ext JS Draw Package...Sencha
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.jsTechExeter
 
HTML5 APIs - native multimedia support and beyond - University of Leeds 05.05...
HTML5 APIs - native multimedia support and beyond - University of Leeds 05.05...HTML5 APIs - native multimedia support and beyond - University of Leeds 05.05...
HTML5 APIs - native multimedia support and beyond - University of Leeds 05.05...Patrick Lauke
 
Drupal image gallery_workshop
Drupal image gallery_workshopDrupal image gallery_workshop
Drupal image gallery_workshopHeather Bohn
 
Web versus Native: round 1!
Web versus Native: round 1!Web versus Native: round 1!
Web versus Native: round 1!Chris Mills
 
Mobile HTML, CSS, and JavaScript
Mobile HTML, CSS, and JavaScriptMobile HTML, CSS, and JavaScript
Mobile HTML, CSS, and JavaScriptfranksvalli
 
Making your Angular.js Application accessible
Making your Angular.js Application accessibleMaking your Angular.js Application accessible
Making your Angular.js Application accessibleDirk Ginader
 
OL4JSF - Latinoware 2010
OL4JSF - Latinoware 2010OL4JSF - Latinoware 2010
OL4JSF - Latinoware 2010Robert Anderson
 
Empowering the "mobile web"
Empowering the "mobile web"Empowering the "mobile web"
Empowering the "mobile web"Chris Mills
 
Backbone/Marionette introduction
Backbone/Marionette introductionBackbone/Marionette introduction
Backbone/Marionette introductionmatt-briggs
 
An Introduction to Vuejs
An Introduction to VuejsAn Introduction to Vuejs
An Introduction to VuejsPaddy Lock
 
Scalable Front-end Development with Vue.JS
Scalable Front-end Development with Vue.JSScalable Front-end Development with Vue.JS
Scalable Front-end Development with Vue.JSGalih Pratama
 
Drush - use full power - DrupalCamp Donetsk 2014
Drush - use full power - DrupalCamp Donetsk 2014Drush - use full power - DrupalCamp Donetsk 2014
Drush - use full power - DrupalCamp Donetsk 2014Alex S
 
APIs, now and in the future
APIs, now and in the futureAPIs, now and in the future
APIs, now and in the futureChris Mills
 
Responsive Design with WordPress (WCPHX)
Responsive Design with WordPress (WCPHX)Responsive Design with WordPress (WCPHX)
Responsive Design with WordPress (WCPHX)Joe Casabona
 
Jazz up your JavaScript: Unobtrusive scripting with JavaScript libraries
Jazz up your JavaScript: Unobtrusive scripting with JavaScript librariesJazz up your JavaScript: Unobtrusive scripting with JavaScript libraries
Jazz up your JavaScript: Unobtrusive scripting with JavaScript librariesSimon Willison
 

What's hot (20)

CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
 
JavaScript Libraries (@Media)
JavaScript Libraries (@Media)JavaScript Libraries (@Media)
JavaScript Libraries (@Media)
 
WebGL For Game Development Spring 2013
WebGL For Game Development Spring 2013WebGL For Game Development Spring 2013
WebGL For Game Development Spring 2013
 
HTML5 Dev Conf 2013 Presentation
HTML5 Dev Conf 2013 PresentationHTML5 Dev Conf 2013 Presentation
HTML5 Dev Conf 2013 Presentation
 
SenchaCon 2016: Improve Workflow Driven Applications with Ext JS Draw Package...
SenchaCon 2016: Improve Workflow Driven Applications with Ext JS Draw Package...SenchaCon 2016: Improve Workflow Driven Applications with Ext JS Draw Package...
SenchaCon 2016: Improve Workflow Driven Applications with Ext JS Draw Package...
 
Enjoy the vue.js
Enjoy the vue.jsEnjoy the vue.js
Enjoy the vue.js
 
HTML5 APIs - native multimedia support and beyond - University of Leeds 05.05...
HTML5 APIs - native multimedia support and beyond - University of Leeds 05.05...HTML5 APIs - native multimedia support and beyond - University of Leeds 05.05...
HTML5 APIs - native multimedia support and beyond - University of Leeds 05.05...
 
Drupal image gallery_workshop
Drupal image gallery_workshopDrupal image gallery_workshop
Drupal image gallery_workshop
 
Web versus Native: round 1!
Web versus Native: round 1!Web versus Native: round 1!
Web versus Native: round 1!
 
Mobile HTML, CSS, and JavaScript
Mobile HTML, CSS, and JavaScriptMobile HTML, CSS, and JavaScript
Mobile HTML, CSS, and JavaScript
 
Making your Angular.js Application accessible
Making your Angular.js Application accessibleMaking your Angular.js Application accessible
Making your Angular.js Application accessible
 
OL4JSF - Latinoware 2010
OL4JSF - Latinoware 2010OL4JSF - Latinoware 2010
OL4JSF - Latinoware 2010
 
Empowering the "mobile web"
Empowering the "mobile web"Empowering the "mobile web"
Empowering the "mobile web"
 
Backbone/Marionette introduction
Backbone/Marionette introductionBackbone/Marionette introduction
Backbone/Marionette introduction
 
An Introduction to Vuejs
An Introduction to VuejsAn Introduction to Vuejs
An Introduction to Vuejs
 
Scalable Front-end Development with Vue.JS
Scalable Front-end Development with Vue.JSScalable Front-end Development with Vue.JS
Scalable Front-end Development with Vue.JS
 
Drush - use full power - DrupalCamp Donetsk 2014
Drush - use full power - DrupalCamp Donetsk 2014Drush - use full power - DrupalCamp Donetsk 2014
Drush - use full power - DrupalCamp Donetsk 2014
 
APIs, now and in the future
APIs, now and in the futureAPIs, now and in the future
APIs, now and in the future
 
Responsive Design with WordPress (WCPHX)
Responsive Design with WordPress (WCPHX)Responsive Design with WordPress (WCPHX)
Responsive Design with WordPress (WCPHX)
 
Jazz up your JavaScript: Unobtrusive scripting with JavaScript libraries
Jazz up your JavaScript: Unobtrusive scripting with JavaScript librariesJazz up your JavaScript: Unobtrusive scripting with JavaScript libraries
Jazz up your JavaScript: Unobtrusive scripting with JavaScript libraries
 

Similar to Spin Up Desktop Apps with Electron.js

Javascript is your (Auto)mate
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mateCodemotion
 
Quick Intro to Android Development
Quick Intro to Android DevelopmentQuick Intro to Android Development
Quick Intro to Android DevelopmentJussi Pohjolainen
 
Vlad Nedomovniy "Navigation with less pain"
Vlad Nedomovniy "Navigation with less pain"Vlad Nedomovniy "Navigation with less pain"
Vlad Nedomovniy "Navigation with less pain"Provectus
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScriptAndrew Dupont
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of usOSCON Byrum
 
A re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbaiA re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbaiPraveen Puglia
 
Leaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGLLeaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGLgerbille
 
EP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EP2016 - Moving Away From Nodejs To A Pure Python Solution For AssetsEP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EP2016 - Moving Away From Nodejs To A Pure Python Solution For AssetsAlessandro Molina
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best PracticesYekmer Simsek
 
Strategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux ApplicaitonsStrategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux Applicaitonsgarbles
 
Construire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleConstruire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleThierry Wasylczenko
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your WillVincenzo Barone
 
SenchaCon 2016: Advanced Techniques for Buidling Ext JS Apps with Electron - ...
SenchaCon 2016: Advanced Techniques for Buidling Ext JS Apps with Electron - ...SenchaCon 2016: Advanced Techniques for Buidling Ext JS Apps with Electron - ...
SenchaCon 2016: Advanced Techniques for Buidling Ext JS Apps with Electron - ...Sencha
 
Rntb20200805
Rntb20200805Rntb20200805
Rntb20200805t k
 
Reliable Javascript
Reliable Javascript Reliable Javascript
Reliable Javascript Glenn Stovall
 
Python-GTK
Python-GTKPython-GTK
Python-GTKYuren Ju
 
Workflow para desenvolvimento Web & Mobile usando grunt.js
Workflow para desenvolvimento Web & Mobile usando grunt.jsWorkflow para desenvolvimento Web & Mobile usando grunt.js
Workflow para desenvolvimento Web & Mobile usando grunt.jsDavidson Fellipe
 
2011 py con
2011 py con2011 py con
2011 py conEing Ong
 

Similar to Spin Up Desktop Apps with Electron.js (20)

Javascript is your (Auto)mate
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mate
 
Quick Intro to Android Development
Quick Intro to Android DevelopmentQuick Intro to Android Development
Quick Intro to Android Development
 
Vlad Nedomovniy "Navigation with less pain"
Vlad Nedomovniy "Navigation with less pain"Vlad Nedomovniy "Navigation with less pain"
Vlad Nedomovniy "Navigation with less pain"
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
DrupalCon jQuery
DrupalCon jQueryDrupalCon jQuery
DrupalCon jQuery
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of us
 
Android 3
Android 3Android 3
Android 3
 
A re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbaiA re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbai
 
Leaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGLLeaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGL
 
EP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EP2016 - Moving Away From Nodejs To A Pure Python Solution For AssetsEP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
Strategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux ApplicaitonsStrategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux Applicaitons
 
Construire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleConstruire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradle
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your Will
 
SenchaCon 2016: Advanced Techniques for Buidling Ext JS Apps with Electron - ...
SenchaCon 2016: Advanced Techniques for Buidling Ext JS Apps with Electron - ...SenchaCon 2016: Advanced Techniques for Buidling Ext JS Apps with Electron - ...
SenchaCon 2016: Advanced Techniques for Buidling Ext JS Apps with Electron - ...
 
Rntb20200805
Rntb20200805Rntb20200805
Rntb20200805
 
Reliable Javascript
Reliable Javascript Reliable Javascript
Reliable Javascript
 
Python-GTK
Python-GTKPython-GTK
Python-GTK
 
Workflow para desenvolvimento Web & Mobile usando grunt.js
Workflow para desenvolvimento Web & Mobile usando grunt.jsWorkflow para desenvolvimento Web & Mobile usando grunt.js
Workflow para desenvolvimento Web & Mobile usando grunt.js
 
2011 py con
2011 py con2011 py con
2011 py con
 

Recently uploaded

What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....kzayra69
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfLivetecs LLC
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 

Recently uploaded (20)

What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdf
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 

Spin Up Desktop Apps with Electron.js

  • 1. Spin Up Desktop Apps with Electron.js whilestevego stevegodin
  • 4.
  • 7. $ mkdir project && cd project $ npm init $ npm install electron prebuilt --save-dev
  • 8. { "name": "animal-advisor", "version": "0.0.0", "description": "A desktop application for generating Advice Animals.", "main": "entry.js", "scripts": { "start": "electron ." }, "devDependencies": { "electron-prebuilt": "^0.35.1" } } package.json
  • 9. . ├── app │ ├── assets │ │ └── images │ │ └── hey-hey-hey.gif │ ├── main │ │ └── main.js │ └── renderer │ └── main │ └── index.html ├── entry.js └── package.json .../animal-advisor/
  • 10. require("./app/main/main.js"); const App = require("app"); const BrowserWindow = require("browser-window"); ... App.on("ready", function () { const mainWindow = new BrowserWindow({ frame: true, resizable: true, }); mainWindow.loadURL(`file://${pathTo.renderer}/main/index.html`); }); entry.js .../main/main.js
  • 11. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Animal Advisor</title> </head> <body> <h1>Hey, World!<h1> <img src="../../assets/images/hey-hey-hey.gif"></img> </body> </html> .../main/index.html
  • 13.
  • 14. part2: "Tying it together"
  • 15. Main Process Main file in package.json Renderer Process Renderer Process Renderer Process Web pages created with BrowserWindow
  • 16. const ipcMain = require('electron').ipcMain; ipcMain.on('asynchronous-message', function(event, arg) { console.log(arg); // prints "ping" event.sender.send('asynchronous-reply', 'pong'); }); Main Process (.../main.js) const ipcRenderer = require('electron').ipcRenderer; ipcRenderer.on('asynchronous-reply', function(event, arg) { console.log(arg); // prints "pong" }); ipcRenderer.send('asynchronous-message', 'ping'); Renderer Process (.../index.js)
  • 17. const ipcMain = require('electron').ipcMain; ipcMain.on('asynchronous-message', function(event, arg) { console.log(arg); // prints "ping" event.sender.send('asynchronous-reply', 'pong'); }); Main Process (.../main.js) const ipcRenderer = require('electron').ipcRenderer; ipcRenderer.on('asynchronous-reply', function(event, arg) { console.log(arg); // prints "pong" }); ipcRenderer.send('asynchronous-message', 'ping'); Renderer Process (.../index.js)
  • 18. const ipcMain = require('electron').ipcMain; ipcMain.on('asynchronous-message', function(event, arg) { console.log(arg); // prints "ping" event.sender.send('asynchronous-reply', 'pong'); }); Main Process (.../main.js) const ipcRenderer = require('electron').ipcRenderer; ipcRenderer.on('asynchronous-reply', function(event, arg) { console.log(arg); // prints "pong" }); ipcRenderer.send('asynchronous-message', 'ping'); Renderer Process (.../index.js)
  • 19. const Remote = require('electron').remote; const BrowserWindow = remote.BrowserWindow; const Dialog = remote.dialog; const GlobalShortcut = remote.globalShortcut; const Menu = remote.menu; const MenuItem = remote.menuItem; const Tray = remote.tray; Renderer Process (.../index.js)
  • 21.
  • 22. animal-advisor ├── app │ ├── assets │ │ ├── fonts │ │ ├── images │ │ └── stylesheets │ │ ├── base.css │ │ └── photon.css │ ├── main │ │ └── main.js │ └── renderer │ └── main │ ├── index.html │ └── index.js ├── entry.js ├── lib │ └── meme.js └── package.json .../animal-advisor/
  • 23.
  • 25. function sendNotification (path) { const title = "Animal Advisor says..." const options = { body: "Image download complete!", icon: path } new Notification(title, options); } .../renderer/main/index.js
  • 26.
  • 28. const Tray = require("tray"); const Menu = require("menu"); ... App.on("ready", function () { ... const menuConfig = [ { label: "Toggle DevTools", accelerator: "Alt+Command+I", click: function() { mainWindow.show(); mainWindow.toggleDevTools(); } }, { label: "Quit", accelerator: "Command+Q", selector: "terminate:" } ]; const contextMenu = Menu.buildFromTemplate(menuConfig); ... }); .../main/main.js
  • 29. const Tray = require("tray"); const Menu = require("menu"); ... App.on("ready", function () { ... const menuConfig = [ { label: "Toggle DevTools", accelerator: "Alt+Command+I", click: function() { mainWindow.show(); mainWindow.toggleDevTools(); } }, { label: "Quit", accelerator: "Command+Q", selector: "terminate:" } ]; const contextMenu = Menu.buildFromTemplate(menuConfig); ... }); .../main/main.js
  • 30. const Tray = require("tray"); const Menu = require("menu"); ... App.on("ready", function () { ... const menuConfig = [ { label: "Toggle DevTools", accelerator: "Alt+Command+I", click: function() { mainWindow.show(); mainWindow.toggleDevTools(); } }, { label: "Quit", accelerator: "Command+Q", selector: "terminate:" } ]; const contextMenu = Menu.buildFromTemplate(menuConfig); ... }); .../main/main.js
  • 31. const Tray = require("tray"); const Menu = require("menu"); ... App.on("ready", function () { ... const trayIcon = new Tray(trayIconPath); trayIcon.setToolTip("Animal Advisor"); trayIcon.setContextMenu(contextMenu); }); .../main/main.js
  • 32.
  • 34. ... const applicationMenuConfig = [ { label: appName, submenu: [ { label: `About ${appName}`, role: "about" }, { type: "separator" }, { label: "Services", role: "services", submenu: [] }, { type: "separator" }, { label: `Hide ${appName}`, accelerator: "Command+H", role: "hide" } ... ] ... .../main/main.js
  • 35. ... const applicationMenuConfig = [ { label: appName, submenu: [ { label: `About ${appName}`, role: "about" }, { type: "separator" }, { label: "Services", role: "services", submenu: [] }, { type: "separator" }, { label: `Hide ${appName}`, accelerator: "Command+H", role: "hide" } ... ] ... .../main/main.js
  • 36. const appName = _.startCase(App.getName()); const applicationMenuConfig = [ { label: appName, submenu: [ { label: `About ${appName}`, role: "about" }, { type: "separator" }, { label: "Services", role: "services", submenu: [] }, { type: "separator" }, { label: `Hide ${appName}`, accelerator: "Command+H", role: "hide" } ... .../main/main.js
  • 37. const applicationMenuConfig = [ { ... }, { label: 'Window', role: 'window', submenu: [ { label: 'Minimize', accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { label: 'Close', accelerator: 'CmdOrCtrl+W', role: 'close' }, { type: 'separator' }, { label: 'Bring All to Front', role: 'front' } ] } ]; .../main/main.js
  • 38. const applicationMenuConfig = [ { ... }, { label: 'Window', role: 'window', submenu: [ { label: 'Minimize', accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { label: 'Close', accelerator: 'CmdOrCtrl+W', role: 'close' }, { type: 'separator' }, { label: 'Bring All to Front', role: 'front' } ] } ]; .../main/main.js
  • 39. ... App.on("ready", function () { ... const applicationMenuConfig = [ ... ]; const applicationMenu = Menu.buildFromTemplate(applicationMenuConfig); Menu.setApplicationMenu(applicationMenu); ... }); .../main/main.js
  • 40.
  • 42. const Remote = require("remote"); const pathTo = Remote.getGlobal("pathTo"); const Menu = Remote.require('menu'); const MenuItem = Remote.require('menu-item'); ... const adviceAnimalMenu = new Menu(); adviceAnimalMenu.append(new MenuItem({ label: 'Reset', click: resetAdviceAnimal })); adviceAnimalMenu.append(new MenuItem({ label: 'Click me!', click: function (menuItem) { console.log('¯_(ツ)_/¯'); } })); ... adviceAnimalImg.addEventListener('contextmenu', function (event) { event.preventDefault(); adviceAnimalMenu.popup(Remote.getCurrentWindow()); }, false); ... .../renderer/main/index.js
  • 43. const Remote = require("remote"); const pathTo = Remote.getGlobal("pathTo"); const Menu = Remote.require('menu'); const MenuItem = Remote.require('menu-item'); ... const adviceAnimalMenu = new Menu(); adviceAnimalMenu.append(new MenuItem({ label: 'Reset', click: resetAdviceAnimal })); adviceAnimalMenu.append(new MenuItem({ label: 'Click me!', click: function (menuItem) { console.log('¯_(ツ)_/¯'); } })); ... adviceAnimalImg.addEventListener('contextmenu', function (event) { event.preventDefault(); adviceAnimalMenu.popup(Remote.getCurrentWindow()); }, false); ... .../renderer/main/index.js
  • 44. const Remote = require("remote"); const pathTo = Remote.getGlobal("pathTo"); const Menu = Remote.require('menu'); const MenuItem = Remote.require('menu-item'); ... const adviceAnimalMenu = new Menu(); adviceAnimalMenu.append(new MenuItem({ label: 'Reset', click: resetAdviceAnimal })); adviceAnimalMenu.append(new MenuItem({ label: 'Click me!', click: function (menuItem) { console.log('¯_(ツ)_/¯'); } })); ... adviceAnimalImg.addEventListener('contextmenu', function (event) { event.preventDefault(); adviceAnimalMenu.popup(Remote.getCurrentWindow()); }, false); ... .../renderer/main/index.js
  • 45.
  • 47. .../renderer/main/index.js ... const Shell = require("shell"); const Remote = require("remote"); const Menu = Remote.require("menu"); const MenuItem = Remote.require("menu-item"); const Dialog = Remote.require("dialog"); ...
  • 48. .../renderer/main/index.js ... adviceAnimalMenu.append(new MenuItem({ label: "Save Image as...", click: showSaveImageAsDialog, accelerator: "Command+Shift+S" })); ...
  • 49. .../renderer/main/index.js ... function showSaveImageAsDialog() { const options = { title: "Save Image as..." }; Dialog.showSaveDialog( Remote.getCurrentWindow(), options, handleSaveDialog ); } function handleSaveDialog (path) { const sourcePath = event.target.src.replace(/file:///,""); const extension = Path.extname(sourcePath); const destinationPath = path + extension; copyFile( sourcePath, destinationPath, handleCopy(destinationPath) ); } ...
  • 50. .../renderer/main/index.js ... function showSaveImageAsDialog() { const options = { title: "Save Image as..." }; Dialog.showSaveDialog( Remote.getCurrentWindow(), options, handleSaveDialog ); } function handleSaveDialog (path) { const sourcePath = event.target.src.replace(/file:///,""); const extension = Path.extname(sourcePath); const destinationPath = path + extension; copyFile( sourcePath, destinationPath, handleCopy(destinationPath) ); } ...
  • 51. .../renderer/main/index.js ... function handleCopy (path) { return function (error) { if (error) { Dialog.showErrorBox("Save Image as... failed", error) } else if (path) { Shell.showItemInFolder(path); }; } }; ...
  • 52. .../renderer/main/index.js ... function handleCopy (path) { return function (error) { if (error) { Dialog.showErrorBox("Save Image as... failed", error) } else if (path) { Shell.showItemInFolder(path); }; } }; ...
  • 53.
  • 55. .../renderer/main/index.js ... const Clipboard = require("clipboard"); ... function setImage (path) { console.log("Setting image..."); adviceAnimalImg.src = path; adviceAnimalImg.className = ""; Clipboard.writeImage(path); sendNotification(path); } ... adviceAnimalMenu.append(new MenuItem({ label: "Copy", click: copyToClipboard, accelerator: "Command+C" })); ... function copyToClipboard () { const filePath = event.target.src.replace(/file:///,""); Clipboard.writeImage(filePath); } ...
  • 56. .../renderer/main/index.js ... const Clipboard = require("clipboard"); ... function setImage (path) { console.log("Setting image..."); adviceAnimalImg.src = path; adviceAnimalImg.className = ""; Clipboard.writeImage(path); sendNotification(path); } ... adviceAnimalMenu.append(new MenuItem({ label: "Copy", click: copyToClipboard, accelerator: "Command+C" })); ... function copyToClipboard () { const filePath = event.target.src.replace(/file:///,""); Clipboard.writeImage(filePath); } ...
  • 57. .../renderer/main/index.js ... const Clipboard = require("clipboard"); ... function setImage (path) { console.log("Setting image..."); adviceAnimalImg.src = path; adviceAnimalImg.className = ""; Clipboard.writeImage(path); sendNotification(path); } ... adviceAnimalMenu.append(new MenuItem({ label: "Copy", click: copyToClipboard, accelerator: "Command+C" })); ... function copyToClipboard () { const filePath = event.target.src.replace(/file:///,""); Clipboard.writeImage(filePath); } ...
  • 58.
  • 65. $ npm install electron-packager --save-dev
  • 66. { "name": "animal-advisor", "version": "0.0.0", "description": "A desktop application for generating Advice Animals.", "main": "entry.js", "scripts": { "start": "electron .", "package": "electron-packager . "Animal Advisor" --platform=darwin -- arch=x64 --out ./dist --version 0.35.1 --overwrite -- icon=./app/assets/images/icon.icns", ... package.json
  • 67.

Editor's Notes

  1. My name is Steve. I have a love ~ hate relantionship with Javascript. And, I just spent way too much time playing with Electron.js this past month.
  2. Raise your hand if you know what it is? Raise your hand if you've built an application with it before? It's a desktop application framework built on top of Chromium and Node.js. It abstracts standard native GUI with a simple API. Node.js is used for all other low level interactions (e.g. file system manipulation)
  3. What apps are built with it? Visual Studio Code, Slack, Atom. Git Book: Uses git to collaborate writing documentation, books and research papers; Avocode: Helps prepare mocks for a web application; Zoommy: Helps you find good free stock photos; Mapbox: Tool to help style and design maps.
  4. Because you have web development skills, you can do mobile apps already, but you also want to desktop apps. This is an easy framework to work with that gives you a decent native API.
  5. The talk assumes that you know some basic Javascript. You will see some ES6 JS, but only syntax supported in Node 4.1. I'm not using any build tools for this project nor am I using any frontend framework. I'm keeping it simple.
  6. Building an obligatory "Hello, world!" app. Before that, I want to give overview of how Electron works.
  7. Assuming you have Node.js installed. This is how you get started.
  8. Emphasize lines are important. You could install Electron.js globally and just run `electron .`, but that would be less portable. `npm start` runs the electron binary downloaded in the node_modules folder.
  9. I have app folder. Inside, I keep assets, the main process and renderer processes in their own folder.
  10. My entry file for the main process just requires a main js. We wait for the application to be loaded and ready, then we create the main window with the BrowserWindow module. The module is part of Electron's Native GUI API that only the main process is allowed to use.
  11. The BrowserWindow is created with an html file. You can load stylesheets, scripts or anything else that might load in a web page.
  12. That's it for "Hello, world!". We just run the start script.
  13. Building an obligatory "Hello, world!" app. Before that, I want to give overview of how Electron works.
  14. Electron boots > Runs entry file in package.json as Main process (runs like any other Node.js script) > BrowserWindow module create new renderer processes (behaves just like a web page).
  15. Communicating between processes is fairly simple. The inter-process communication (IPC) can create channels that both, main and renderer, processes can listen on. Their interface is nearly the same as events.
  16. To set it up, we create an listener on a channel and give it a handler. In the other process, we simply send messages to the channel.
  17. In this scenario, the handler for the "asynchronous-message" channel sends off a reply to the "asynchronous-reply" channel. The renderer process is ready for it and prints out a reply.
  18. A very common use case for communicating to Main is to call Native GUI modules that only the main process can do. The Remote module abstracts that communication. Instead of IPC, I'll be using the Remote module throughout this talk.
  19. To demo Electron's Native GUI interface, I'll build an app named Animal Advisor.
  20. It's a silly app that generates advice animals like these. I won't get into the details of how I built the generator. Suffice it to say that it uses the Meme Captain API & I spent too much time on it!
  21. What's changed? I've added CSS & fonts. There is javascript file executed by main/index.html. And, we've added an internal library. That's the bit that fetches the generated Advice Animals. It makes use of the Meme Captain API.
  22. Here it is in action. Type a sentence. It's compared to some matchers, then the relevant advice animal is generated.
  23. We're going start by implementing a system notification that triggers once the image is ready.
  24. Electron doesn't implement it's own notification module. It makes use of the HTML5 Notification API.
  25. And, this is what it looks like!
  26. We're going start by implementing a system notification that triggers once the image is ready.
  27. Before creating the tray icon, we'll create a menu for it. Otherwise, we'll just have a tray that does nothing. To create a menu, we get the Electron module, menu. Create a template. The first item in the menu will toggle the chromium dev tools. We pass the menu item a click handler and which toggles it on the mainWindow.
  28. The second menu item implements a quit command. It's a standard action that is implemented by Electron. selector: "terminate:" will do.
  29. Finally, we create the Menu object with .buildFromTemplate. This gives us the menu that we'll bind to the Tray icon.
  30. Before creating the tray icon, we'll create a menu for it. Otherwise, we'll just have a tray that does nothing. To create a menu, we provide the `buildFromTemplate` method array of objects. The accelerator property is Electron's API for creating application keyboard shorcuts. We'll see more of it later. **What is the selector?**
  31. We create the Tray icon, but create a new instances of it. It takes a path to an icon which can be a png. We can then set a tool tip and a context menu which is the menu we created in the prior slides.
  32. Here's a demo.
  33. The menu at the top of your application. It commonly has File, Edit, Window, Help and more as menu items.
  34. All menus make use of the same module. The tray icon menu, application menu and context menu. For OS X (only), the first menu item (or object) in the array will always be named after the application. This is the menu item we see next to the Apple icon.
  35. Each application menu items have their own submenu. Or, the menu that appears when you click on File or Window.
  36. The role property on the submenu are for standard actions provided by the OS. In this case, Electron gives us the ability to implement about, services and hide simply by defining a role on the sub menu item.
  37. Still in the config array, we create another menu labeled "Window". It's given the role of window, a standard menu. This is the window menu you see in just about every OS X applicaton. Like a standard action, we can just give it the role of window.
  38. Window has standard submenu items. You're probably all familiar with minimize, close and front. Electron has several other standard menus and standard sub menus that you can check out in its docs.
  39. Finally, we pass our configuration array to buildFromTemplates. Using the same Menu module, we set the newly created menu as our app's application menu.
  40. Voila!
  41. We haven't done a whole lot of interesting things with our menus so far. What if I want a context menu that I can use to save our Advice Animal to disk or send to the clipboard? Let's start with the Context Menu first.
  42. We'll be working in renderer process for our main window. Because menu creation is a native operation, only main process can create them. We could use the IPC module to send events over a channel and handle them in main process. Instead, we'll just use Electron's Remote module which abstracts that for us.
  43. This time around. We're working with Menu and MenuItem methods to create it. We'll handle menu clicks with callback functions. This gives us a lot of freedom.
  44. Finally, we add an event listener for the contextmenu event on the advice animal image element. We'll prevent the regular menu from showing, then we'll popup the menu we just created on the click location (by default).
  45. This is what it looks like.
  46. It's a common feature to be able to save an image to file by accessing its context menu. Let's add that ours for the advice animal image. We'll have the user pick the save location with a native save dialog.
  47. To begin, let's require the dialog module. We'll have to use it through the Remote module, because it's a native command. It can only be called from within the main process. We're also requiring the shell module which we'll need to complete the effect.
  48. Then, let's add a "Save Image as..." item to the context menu and give it a handler to create the save dialog.
  49. We pass the showSaveDialog the current window, options and a callback. All arguments are optional. The callback will... (next slide)
  50. ...will be called with a path if the user saved and didn't cancel. In the handler, we just copy the cached picture to the location the user chooses with the save dialog. The handleCopy handler is called when the file copy is finished.
  51. In case the copy fails, we'll make use of the error dialog to alert the user. It only takes a title and text content as arguments.
  52. If there is a path (that is to say the user didn't cancel and there were no errors), we'll use the Shell module to open a finder (file explorer) window to save destination. Shell can be used in any process and it can be used to open folders, items and urls with system default applications. You can also use it send system beeps and send items to the trash.
  53. If there is a path (that is to say the user didn't cancel and there were no errors), we'll use the Shell module to open a finder (file explorer) window to save destination. Shell can be used in any process and it can be used to open folders, items and urls with system default applications. You can also use it send system beeps and send items to the trash.
  54. As a consumate shorcuter, I can't stand applications without good clipboard integration. Let's use Electron's API to integrate it.
  55. Writing to the clipboard is trivial. It's writable from both processes, main and renderer. First, we require the module.
  56. We create a new menu item for the copy command.
  57. Finally, I write the image to the clipboard when it is set and when the *copy* menu item is clicked. Passing image path to writeImage will do the job.
  58. There several other methods to write and read html, text and images from the clipboard.
  59. **This should take more than one slide** We’re going to go back an make global shortcuts, menu shortcuts and notifications configurable. We’re probably going to use `nsconf` for this. *See sound box tutorial*
  60. I didn't talk about all the interesting things supported by Electron. Here's a few I missed.
  61. An interface for the Squirrel auto-updater framework.
  62. A module to register/unregister global keyboard shortcuts with the operating system. I really wanted to show how this is done, but I ran out of time. However, you can bet that the release of Animal Advisor will support it.
  63. One is used to add event listeners to power states. The other is used to prevent the desktop from entering power saving modes.
  64. This modules enables sending your apps crash reports.
  65. It's time to package a release! Thankfully, there's a npm package that makes trivially easy for us.
  66. Install electron packager as a development dependency.
  67. We'll configure a packaging script for ease of use. The first purple text is your application name; the yellow, desktop platform; the pink, destination folder; the green, electron version; and, the last purple is the location of the app icon.
  68. A small application like this builds pretty fast. You don't need to see this in action, but I felt I was on a roll with these video demos.
  69. I encourage you to use all the build tools you're familiar with. Electron works especially well with your favorite frontend framework. I will likely rewrite this application in React and build it with Webpack. Also, I just wanted to remind you that you have access