Slide 1

Slide 1 text

I can't work on my phone @stefanjudis Desktop all the things!

Slide 2

Slide 2 text

Stefan Judis Frontend Developer, Occasional Teacher, Meetup Organizer ❤ Open Source, Performance and Accessibility ❤ @stefanjudis

Slide 3

Slide 3 text

cssclass.es

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

I had a passion for music Photo by Allen Holt

Slide 6

Slide 6 text

I became a sound engineer Photo by Josep Tomàs

Slide 7

Slide 7 text

I lost my passion Photo by Guian Bolisay

Slide 8

Slide 8 text

I got a Bachelors degree in 'Media and Computer Science' Photo by Aaron Hawkins

Slide 9

Slide 9 text

JS I found another passion

Slide 10

Slide 10 text

10 Web technology is everywhere SERVER WEB IOT FOUNDER

Slide 11

Slide 11 text

Everybody agrees that mobile is a thing Photo by jeanbaptisteparis

Slide 12

Slide 12 text

OPENED JS PULL REQUESTS ON GITHUB IN 2016 1,604,219 The state of the Octoverse 2016

Slide 13

Slide 13 text

ACTIVE USERS IN 2016 +5.8M The state of the Octoverse 2016

Slide 14

Slide 14 text

90% are self taught Stack Overflow Developer Survey 2017

Slide 15

Slide 15 text

Learning out of curiosity

Slide 16

Slide 16 text

Stefan, you're working too much!

Slide 17

Slide 17 text

I spend a lot of time in front of a computer because I can't work on my phone!

Slide 18

Slide 18 text

Electron cross platform desktop apps with JavaScript, HTML, and CSS

Slide 19

Slide 19 text

Let’s build our dream editor! With Web Technology!

Slide 20

Slide 20 text

REQUIREMENTS Write to disk Native functionality Separation of concerns

Slide 21

Slide 21 text

ATOM

Slide 22

Slide 22 text

Atom-shell is a Node.js programmable Chromium browser Photo by Peter Roome

Slide 23

Slide 23 text

CHROMIUM BROWSER - architecture - Browser / Main https://speakerdeck.com/zcbenz/practice-on-embedding-node-dot-js-into-atom-editor Controlled by C++ Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Renderer HTML JavaScript (DOM) Renderer HTML JavaScript (DOM) Window Window

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Browser process

Slide 26

Slide 26 text

Renderer process

Slide 27

Slide 27 text

CHROMIUM BROWSER - architecture - https://speakerdeck.com/zcbenz/practice-on-embedding-node-dot-js-into-atom-editor Controlled by C++ Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Renderer HTML JavaScript (DOM) Renderer HTML JavaScript (DOM) Window Window Browser / Main

Slide 28

Slide 28 text

https://speakerdeck.com/zcbenz/practice-on-embedding-node-dot-js-into-atom-editor Controlled by Node.js Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Window Window ATOM-SHELL - architecture - Browser / Main

Slide 29

Slide 29 text

ELECTRON

Slide 30

Slide 30 text

Let's get started!

Slide 31

Slide 31 text

{ "name": "electron-quick-start", "version": "1.0.0", "description": "A minimal Electron application", "main": "main.js", "scripts": { "start": "electron ." }, "repository": "https://github.com/electron/electron-quick-start", "keywords": [], "devDependencies": { "electron": "^1.4.1" } } package.json

Slide 32

Slide 32 text

{ "name": "electron-quick-start", "version": "1.0.0", "description": "A minimal Electron application", "main": "main.js", "scripts": { "start": "electron ." }, "repository": "https://github.com/electron/electron-quick-start", "keywords": [], "devDependencies": { "electron": "^1.4.1" } } package.json

Slide 33

Slide 33 text

{ "name": "electron-quick-start", "version": "1.0.0", "description": "A minimal Electron application", "main": "main.js", "scripts": { "start": "electron ." }, "repository": "https://github.com/electron/electron-quick-start", "keywords": [], "devDependencies": { "electron": "^1.4.1" } } package.json

Slide 34

Slide 34 text

const electron = require( 'electron' ); const app = electron.app; const BrowserWindow = electron.BrowserWindow; const path = require( 'path' ); const url = require( 'url' ); let mainWindow; function createWindow () { mainWindow = new BrowserWindow({ width: 800, height: 600 }); mainWindow.loadURL( url.format({ pathname : path.join( __dirname, 'index.html' ), protocol : 'file:', slashes : true }) ); mainWindow.on( 'closed', function () { mainWindow = null } ); } app.on( 'ready', createWindow ); main.js Browser process

Slide 35

Slide 35 text

const electron = require( 'electron' ); const app = electron.app; const BrowserWindow = electron.BrowserWindow; const path = require( 'path' ); const url = require( 'url' ); let mainWindow; function createWindow () { mainWindow = new BrowserWindow({ width: 800, height: 600 }); mainWindow.loadURL( url.format({ pathname : path.join( __dirname, 'index.html' ), protocol : 'file:', slashes : true }) ); mainWindow.on( 'closed', function () { mainWindow = null } ); } app.on( 'ready', createWindow ); main.js Browser process

Slide 36

Slide 36 text

const electron = require( 'electron' ); const app = electron.app; const BrowserWindow = electron.BrowserWindow; const path = require( 'path' ); const url = require( 'url' ); let mainWindow; function createWindow () { mainWindow = new BrowserWindow({ width: 800, height: 600 }); mainWindow.loadURL( url.format({ pathname : path.join( __dirname, 'index.html' ), protocol : 'file:', slashes : true }) ); mainWindow.on( 'closed', function () { mainWindow = null } ); } app.on( 'ready', createWindow ); main.js Browser process

Slide 37

Slide 37 text

const electron = require( 'electron' ); const app = electron.app; const BrowserWindow = electron.BrowserWindow; const path = require( 'path' ); const url = require( 'url' ); let mainWindow; function createWindow () { mainWindow = new BrowserWindow({ width: 800, height: 600 }); mainWindow.loadURL( url.format({ pathname : path.join( __dirname, 'index.html' ), protocol : 'file:', slashes : true }) ); mainWindow.on( 'closed', function () { mainWindow = null } ); } app.on( 'ready', createWindow ); main.js Browser process

Slide 38

Slide 38 text

main.js const electron = require( 'electron' ); const app = electron.app; const BrowserWindow = electron.BrowserWindow; const path = require( 'path' ); const url = require( 'url' ); let mainWindow; function createWindow () { mainWindow = new BrowserWindow({ width: 800, height: 600 }); mainWindow.loadURL( url.format({ pathname : path.join( __dirname, 'index.html' ), protocol : 'file:', slashes : true }) ); mainWindow.on( 'closed', function () { mainWindow = null } ); } app.on( 'ready', createWindow ); Browser process

Slide 39

Slide 39 text

index.html Hello World!

Hello World!

We are using Node.js document.write(process.versions.node), Chromium document.write(process.versions.chrome), and Electron document.write(process.versions.electron). require( './renderer.js' ); Renderer process

Slide 40

Slide 40 text

index.html Hello World!

Hello World!

We are using Node.js document.write(process.versions.node), Chromium document.write(process.versions.chrome), and Electron document.write(process.versions.electron). require( './renderer.js' ); Renderer process

Slide 41

Slide 41 text

index.html Hello World!

Hello World!

We are using Node.js document.write(process.versions.node), Chromium document.write(process.versions.chrome), and Electron document.write(process.versions.electron). require( './renderer.js' ); Renderer process

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Browser (main.js) Controlled by Node.js Window Renderer (index.html) HTML JavaScript (DOM) Node.js ATOM-SHELL - architecture - // main.js mainWindow = new BrowserWindow();

Slide 45

Slide 45 text

Facts about the renderer process

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

It's a controlled environment!

Slide 49

Slide 49 text

VAR, LET, CONST GRID LAYOUT CSS CUSTOM PROPERTIES ... ... ... Renderer process USE LATEST TECHNOLOGY (without babel, auto-prefixer, ...)

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

Renderer process Renderer process Take a picture!!! ...
... // 50 lines of JS

Slide 53

Slide 53 text

Renderer process Take a picture!!! ...
... // 50 lines of inlined JS Renderer process

Slide 54

Slide 54 text

Renderer process Renderer process navigator.getUserMedia( { video: true }, ( stream ) => { videoElem.src = URL.createObjectURL( stream ); }, () => { alert('could not connect stream'); } );

Slide 55

Slide 55 text

Renderer process Renderer process videoElem.addEventListener('play', () => { setInterval(() => { context.fillRect(0, 0, width, height); context.drawImage(videoElem, 0, 0, width, height); }, 33); });

Slide 56

Slide 56 text

Renderer process Renderer process button.addEventListener('click', () => { const fileName = 'some/folder/some/file.png'; writeFile( fileName, canvasContent, () => { // success } ) });

Slide 57

Slide 57 text

Renderer process const userHome = require( 'user-home' ); const { join } = require( 'path' ); const { writeFile } = require( 'fs' ); const canvasElem = document.getElementById( 'canvas' ); button.addEventListener( 'click', () => { const fileName = join( userHome, 'desktop', 'test-images', + (new Date()) + '.png' ); writeFile( fileName, new Buffer( canvasElem.toDataURL( 'image/png' ).replace( /^data:image\/png;base64,/, '' ), 'base64' ), ( error ) => { if ( error ) { return console.log(error); } successElem.innerHTML = `${fileName} written!` setTimeout( _ => successElem.innerHTML = '', 3000 ); } ) } ); Renderer process

Slide 58

Slide 58 text

Renderer process const userHome = require( 'user-home' ); const { join } = require( 'path' ); const { writeFile } = require( 'fs' ); const canvasElem = document.getElementById( 'canvas' ); button.addEventListener( 'click', () => { const fileName = join( userHome, 'desktop', 'test-images', + (new Date()) + '.png' ); writeFile( fileName, new Buffer( canvasElem.toDataURL( 'image/png' ).replace( /^data:image\/png;base64,/, '' ), 'base64' ), ( error ) => { if ( error ) { return console.log(error); } successElem.innerHTML = `${fileName} written!` setTimeout( _ => successElem.innerHTML = '', 3000 ); } ) } ); Renderer process

Slide 59

Slide 59 text

Renderer process const userHome = require( 'user-home' ); const { join } = require( 'path' ); const { writeFile } = require( 'fs' ); const canvasElem = document.getElementById( 'canvas' ); button.addEventListener( 'click', () => { const fileName = join( userHome, 'desktop', 'test-images', + (new Date()) + '.png' ); writeFile( fileName, new Buffer( canvasElem.toDataURL( 'image/png' ).replace( /^data:image\/png;base64,/, '' ), 'base64' ), ( error ) => { if ( error ) { return console.log(error); } successElem.innerHTML = `${fileName} written!` setTimeout( _ => successElem.innerHTML = '', 3000 ); } ) } ); Renderer process

Slide 60

Slide 60 text

Renderer process const userHome = require( 'user-home' ); const { join } = require( 'path' ); const { writeFile } = require( 'fs' ); const canvasElem = document.getElementById( 'canvas' ); button.addEventListener( 'click', () => { const fileName = join( userHome, 'desktop', 'test-images', + (new Date()) + '.png' ); writeFile( fileName, new Buffer( canvasElem.toDataURL( 'image/png' ).replace( /^data:image\/png;base64,/, '' ), 'base64' ), ( error ) => { if ( error ) { return console.log(error); } successElem.innerHTML = `${fileName} written!` setTimeout( _ => successElem.innerHTML = '', 3000 ); } ) } ); Renderer process Node.js lives in the DOM!

Slide 61

Slide 61 text

or require('./webcam'); Renderer process WORKS IN AN EXTERNAL FILE, TOO!

Slide 62

Slide 62 text

or require('./webcam'); Renderer process WORKS IN AN EXTERNAL FILE, TOO! Build your apps as usual!

Slide 63

Slide 63 text

Let's have a look at real stuff

Slide 64

Slide 64 text

BLOCKY - real world examples - I NEED A TOOL TO EASILY DISABLE THE SCREENSAVER " "

Slide 65

Slide 65 text

main.js Browser process const { app, Menu, Tray, powerSaveBlocker } = require( 'electron' ); const path = require( 'path' ); const icons = { active : path.join( __dirname, 'media', 'icon_active.png' ) , notActive : path.join( __dirname, 'media', 'icon.png' ) }; const menus = { active : Menu.buildFromTemplate( [ { label: 'Time for sleeping again', click : toggleSleepPermission }, { type: 'separator' }, { label: 'Quit', click : app.quit } ] ), notActive : Menu.buildFromTemplate( [ { label: 'Don\'t go to sleep', click : toggleSleepPermission }, { type: 'separator' }, { label: 'Quit', click : app.quit } ] ) };

Slide 66

Slide 66 text

main.js Browser process const { app, Menu, Tray, powerSaveBlocker } = require( 'electron' ); const path = require( 'path' ); const icons = { active : path.join( __dirname, 'media', 'icon_active.png' ) , notActive : path.join( __dirname, 'media', 'icon.png' ) }; const menus = { active : Menu.buildFromTemplate( [ { label: 'Time for sleeping again', click : toggleSleepPermission }, { type: 'separator' }, { label: 'Quit', click : app.quit } ] ), notActive : Menu.buildFromTemplate( [ { label: 'Don\'t go to sleep', click : toggleSleepPermission }, { type: 'separator' }, { label: 'Quit', click : app.quit } ] ) };

Slide 67

Slide 67 text

main.js const { app, Menu, Tray, powerSaveBlocker } = require( 'electron' ); const path = require( 'path' ); const icons = { active : path.join( __dirname, 'media', 'icon_active.png' ) , notActive : path.join( __dirname, 'media', 'icon.png' ) }; const menus = { active : Menu.buildFromTemplate( [ { label: 'Time for sleeping again', click : toggleSleepPermission }, { type: 'separator' }, { label: 'Quit', click : app.quit } ] ), notActive : Menu.buildFromTemplate( [ { label: 'Don\'t go to sleep', click : toggleSleepPermission }, { type: 'separator' }, { label: 'Quit', click : app.quit } ] ) }; Browser process

Slide 68

Slide 68 text

main.js const { app, Menu, Tray, powerSaveBlocker } = require( 'electron' ); const path = require( 'path' ); const icons = { active : path.join( __dirname, 'media', 'icon_active.png' ) , notActive : path.join( __dirname, 'media', 'icon.png' ) }; const menus = { active : Menu.buildFromTemplate( [ { label: 'Time for sleeping again', click : toggleSleepPermission }, { type: 'separator' }, { label: 'Quit', click : app.quit } ] ), notActive : Menu.buildFromTemplate( [ { label: 'Don\'t go to sleep', click : toggleSleepPermission }, { type: 'separator' }, { label: 'Quit', click : app.quit } ] ) }; Browser process

Slide 69

Slide 69 text

main.js Browser process let appTray = null; app.on( 'ready', initTray ); function initTray() { if ( app.dock ) { app.dock.hide(); } appTray = new Tray( path.resolve( __dirname, 'media', 'icon.png' ) ); appTray.setToolTip( 'Blocky' ); appTray.setContextMenu( menus.notActive ); }

Slide 70

Slide 70 text

main.js Browser process let currentBlocker = null; function toggleSleepPermission() { let state; if ( currentBlocker === null ) { currentBlocker = powerSaveBlocker.start( 'prevent-display-sleep' ); state = 'active'; } else { powerSaveBlocker.stop( currentBlocker ); currentBlocker = null; state = 'notActive'; } appTray.setContextMenu( menus[ state ] ); appTray.setImage( icons[ state ] ); }

Slide 71

Slide 71 text

main.js Browser process let currentBlocker = null; function toggleSleepPermission() { let state; if ( currentBlocker === null ) { currentBlocker = powerSaveBlocker.start( 'prevent-display-sleep' ); state = 'active'; } else { powerSaveBlocker.stop( currentBlocker ); currentBlocker = null; state = 'notActive'; } appTray.setContextMenu( menus[ state ] ); appTray.setImage( icons[ state ] ); }

Slide 72

Slide 72 text

main.js Browser process let currentBlocker = null; function toggleSleepPermission() { let state; if ( currentBlocker === null ) { currentBlocker = powerSaveBlocker.start( 'prevent-display-sleep' ); state = 'active'; } else { powerSaveBlocker.stop( currentBlocker ); currentBlocker = null; state = 'notActive'; } appTray.setContextMenu( menus[ state ] ); appTray.setImage( icons[ state ] ); }

Slide 73

Slide 73 text

main.js Browser process let currentBlocker = null; function toggleSleepPermission() { let state; if ( currentBlocker === null ) { currentBlocker = powerSaveBlocker.start( 'prevent-display-sleep' ); state = 'active'; } else { powerSaveBlocker.stop( currentBlocker ); currentBlocker = null; state = 'notActive'; } appTray.setContextMenu( menus[ state ] ); appTray.setImage( icons[ state ] ); }

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

Application Menus Browser process github.com/electron/electron/blob/master/docs/api/menu.md const { Menu } = require( 'electron' ); const template = [ ... { label : 'KCDC', submenu : [ { label : 'Say "Barbecue"', accelerator : 'CmdOrCtrl+B', click() { console.log('Barbecue...'); } }, ... ] } ]; const menu = Menu.buildFromTemplate( template ); Menu.setApplicationMenu( menu );

Slide 77

Slide 77 text

Browser process github.com/electron/electron/blob/master/docs/api/menu.md const { Menu } = require( 'electron' ); const template = [ ... { label : 'KCDC', submenu : [ { label : 'Say "Barbecue"', accelerator : 'CmdOrCtrl+B', click() { console.log('Barbecue...'); } }, ... ] } ]; const menu = Menu.buildFromTemplate( template ); Menu.setApplicationMenu( menu ); Application Menus

Slide 78

Slide 78 text

const { Menu } = require( 'electron' ); const template = [ ... { label : 'KCDC', submenu : [ { label : 'Say "Barbecue"', accelerator : 'CmdOrCtrl+B', click() { console.log('Barbecue...'); } }, ... ] } ]; const menu = Menu.buildFromTemplate( template ); Menu.setApplicationMenu( menu ); Browser process github.com/electron/electron/blob/master/docs/api/menu.md Application Menus

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

But: Follow OS conventions!

Slide 82

Slide 82 text

github.com/electron/electron/blob/master/docs/api/menu.md Application Menus if ( process.platform === 'darwin' ) { ... } if ( process.platform === 'win32' ) { ... } if ( process.platform === 'linux' ) { ... }

Slide 83

Slide 83 text

Global Shortcuts Browser process github.com/electron/electron/blob/master/docs/api/global-shortcut.md function initTray() { if ( app.dock ) { app.dock.hide(); } globalShortcut.register( 'CmdOrCtrl+B', toggleSleepPermission ); appTray = new Tray( path.resolve( __dirname, 'media', 'icon.png' ) ); appTray.setToolTip( 'Block screensaver' ); appTray.setContextMenu( menus.notActive ); }

Slide 84

Slide 84 text

Global Shortcuts Browser process github.com/electron/electron/blob/master/docs/api/global-shortcut.md function initTray() { if ( app.dock ) { app.dock.hide(); } globalShortcut.register( 'CmdOrCtrl+B', toggleSleepPermission ); appTray = new Tray( path.resolve( __dirname, 'media', 'icon.png' ) ); appTray.setToolTip( 'Block screensaver' ); appTray.setContextMenu( menus.notActive ); }

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

blocky - 72 LOC - native functionality is extremely easy to implement github.com/stefanjudis/blocky

Slide 88

Slide 88 text

slack://T082W2AN6/magic-login/2...

Slide 89

Slide 89 text

slack://T082W2AN6/magic-login/2...

Slide 90

Slide 90 text

Remember There is always new stuff to learn

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

menubar High level way to create menubar desktop applications with electron github.com/maxogden/menubar

Slide 93

Slide 93 text

menubar iconTemplate.png [email protected] index.html main.js package.json

Slide 94

Slide 94 text

main.js Browser process const menubar = require( 'menubar' ); const mb = menubar(); mb.on( 'ready', () => { console.log( 'app is ready' ); } );

Slide 95

Slide 95 text

index.html Renderer process ...

Today I learned

    const contentful = require( 'contentful' ); const client = contentful.createClient( {...} ) .getEntries( {...} ) .then( entries => { document.getElementById( 'posts' ).innerHTML = entries.items.map( entry => { return `<li><a href="...">${entry.fields.title}</a></li>`; } ).join( '' ); } );

    Slide 96

    Slide 96 text

    index.html ...

    Today I learned

      const contentful = require( 'contentful' ); const client = contentful.createClient( {...} ) .getEntries( {...} ) .then( entries => { document.getElementById( 'posts' ).innerHTML = entries.items.map( entry => { return `<li><a href="...">${entry.fields.title}</a></li>`; } ).join( '' ); } ); Renderer process

      Slide 97

      Slide 97 text

      index.html ...

      Today I learned

        const contentful = require( 'contentful' ); const client = contentful.createClient( {...} ) .getEntries( {...} ) .then( entries => { document.getElementById( 'posts' ).innerHTML = entries.items.map( entry => { return `<li><a href="...">${entry.fields.title}</a></li>`; } ).join( '' ); } ); Renderer process

        Slide 98

        Slide 98 text

        index.html ...

        Today I learned

          const contentful = require( 'contentful' ); const client = contentful.createClient( {...} ) .getEntries( {...} ) .then( entries => { document.getElementById( 'posts' ).innerHTML = entries.items.map( entry => { return `<li><a href="...">${entry.fields.title}</a></li>`; } ).join( '' ); } ); Renderer process

          Slide 99

          Slide 99 text

          No content

          Slide 100

          Slide 100 text

          No content

          Slide 101

          Slide 101 text

          No content

          Slide 102

          Slide 102 text

          No content

          Slide 103

          Slide 103 text

          PODCAST APP - real world examples - I NEED A TOOL TO TO LISTEN TO PODCASTS. " "

          Slide 104

          Slide 104 text

          No content

          Slide 105

          Slide 105 text

          index.html Renderer process Pocketcast App body, html { height: 100%; margin: 0; padding: 0; } webview { width: 100%; height: 100%; }

          Slide 106

          Slide 106 text

          index.html Pocketcast App body, html { height: 100%; margin: 0; padding: 0; } webview { width: 100%; height: 100%; } Renderer process

          Slide 107

          Slide 107 text

          index.html Pocketcast App body, html { height: 100%; margin: 0; padding: 0; } webview { width: 100%; height: 100%; } Renderer process

          Slide 108

          Slide 108 text

          No content

          Slide 109

          Slide 109 text

          No content

          Slide 110

          Slide 110 text

          index.html Pocketcast App body, html { height: 100%; margin: 0; padding: 0; } webview { width: 100%; height: 100%; } Renderer process

          Slide 111

          Slide 111 text

          webview.js Renderer process ( function( window ) { const openExternal = require( 'electron' ).shell.openExternal; window.addEventListener( 'click', ( event ) => { const baseRegex = new RegExp( `${ window.location.hostname}` ); if ( event.target.tagName === 'A' && ! baseRegex.test( event.target.href ) ) { openExternal( event.target.href ); event.preventDefault(); } } ); } )( window );

          Slide 112

          Slide 112 text

          ( function( window ) { const openExternal = require( 'electron' ).shell.openExternal; window.addEventListener( 'click', ( event ) => { const baseRegex = new RegExp( `${ window.location.hostname}` ); if ( event.target.tagName === 'A' && ! baseRegex.test( event.target.href ) ) { openExternal( event.target.href ); event.preventDefault(); } } ); } )( window ); webview.js Renderer process

          Slide 113

          Slide 113 text

          No content

          Slide 114

          Slide 114 text

          No content

          Slide 115

          Slide 115 text

          showItemInFolder openItem openExternalUrl moveItemToTrash require( 'electron' ).shell beep

          Slide 116

          Slide 116 text

          webview example - productivity boost - default system functionality is extremely easy to implement

          Slide 117

          Slide 117 text

          nativefier Desktop all the things! github.com/jiahaog/nativefier/

          Slide 118

          Slide 118 text

          No content

          Slide 119

          Slide 119 text

          No content

          Slide 120

          Slide 120 text

          FORREST - real world examples - I NEED A TOOL TO GET LONG RUNNING PROCESSES OUT OF MY TERMINAL. " "

          Slide 121

          Slide 121 text

          No content

          Slide 122

          Slide 122 text

          No content

          Slide 123

          Slide 123 text

          IPC Renderer process const { ipcRenderer } = require( 'electron' ); ipcRenderer.send( 'delete-repo', repo.id ); - one renderer process -

          Slide 124

          Slide 124 text

          Renderer process const { ipcRenderer } = require( 'electron' ); ipcRenderer.send( 'delete-repo', repo.id ); const { ipcMain } = require( 'electron' ); ipcMain.on( 'delete-repo', ( event, repoId ) => { // delete repo and save settings deleteRepoWithId( repoId ); event.sender.send( 'update-repos', getRepos() ); } ); Browser process IPC - one renderer process -

          Slide 125

          Slide 125 text

          Renderer process const { ipcRenderer } = require( 'electron' ); ipcRenderer.send( 'delete-repo', repo.id ); const { ipcMain } = require( 'electron' ); ipcMain.on( 'delete-repo', ( event, repoId ) => { // delete repo and save settings deleteRepoWithId( repoId ); event.sender.send( 'update-repos', getRepos() ); } ); const { ipcRenderer } = require( 'electron' ); ipcRenderer.on( 'update-repos', ( event, repos ) => { // update application state } ); Browser process Renderer process IPC - one renderer process -

          Slide 126

          Slide 126 text

          Browser Controlled by Node.js Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Window Window IPC - one renderer process -

          Slide 127

          Slide 127 text

          Browser Controlled by Node.js Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Window Window delete-repo IPC - one renderer process -

          Slide 128

          Slide 128 text

          Browser Controlled by Node.js Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Window Window delete-repo update-repos IPC - one renderer process -

          Slide 129

          Slide 129 text

          IPC - several renderer processes - const { ipcMain } = require( 'electron' ); const windows = [ new BrowserWindow( { /* ... */ }, new BrowserWindow( { /* ... */ }, new BrowserWindow( { /* ... */ } ]; ipcMain.on( 'delete-repo', ( event, repoId ) => { // delete repo and save settings deleteRepoWithId( repoId ); windows.forEach( window => window.webContents.emit( 'update-repos', getRepos() ) ); } ); Browser process

          Slide 130

          Slide 130 text

          IPC - several renderer processes - const { ipcMain } = require( 'electron' ); const windows = [ new BrowserWindow( { /* ... */ }, new BrowserWindow( { /* ... */ }, new BrowserWindow( { /* ... */ } ]; ipcMain.on( 'delete-repo', ( event, repoId ) => { // delete repo and save settings deleteRepoWithId( repoId ); windows.forEach( window => window.webContents.emit( 'update-repos', getRepos() ) ); } ); Browser process

          Slide 131

          Slide 131 text

          Browser Controlled by Node.js Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Window Window IPC - one renderer process -

          Slide 132

          Slide 132 text

          Browser Controlled by Node.js Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Window Window delete-repo IPC - one renderer process -

          Slide 133

          Slide 133 text

          Browser Controlled by Node.js Window … Tray Menu Dialog IPC Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Renderer HTML JavaScript (DOM) Node.js Window Window delete-repo IPC - one renderer process - update-repos

          Slide 134

          Slide 134 text

          How hard can it be to build proper terminal output? Photo by Craig Sunter

          Slide 135

          Slide 135 text

          runScript() { this.processCmd = options.isCustom ? `npm run ${ script.name }` : script.command; this.$set( 'process', this.exec( this.processCmd, { cwd : options.cwd } ) ); this.process.stdout.on( 'data', this.handleData ); this.process.stderr.on( 'data', this.handleData ); this.process.on( 'error', this.handleScriptExit ); this.process.on( 'close', this.handleScriptExit ); this.process.on( 'exit', this.handleScriptExit ); } BUILDING PROPER TERMINAL OUTPUT - execute a command -

          Slide 136

          Slide 136 text

          runScript() { this.processCmd = options.isCustom ? `npm run ${ script.name }` : script.command; this.$set( 'process', this.exec( this.processCmd, { cwd : options.cwd } ) ); this.process.stdout.on( 'data', this.handleData ); this.process.stderr.on( 'data', this.handleData ); this.process.on( 'error', this.handleScriptExit ); this.process.on( 'close', this.handleScriptExit ); this.process.on( 'exit', this.handleScriptExit ); } BUILDING PROPER TERMINAL OUTPUT - execute a command -

          Slide 137

          Slide 137 text

          runScript() { this.processCmd = options.isCustom ? `npm run ${ script.name }` : script.command; this.$set( 'process', this.exec( this.processCmd, { cwd : options.cwd } ) ); this.process.stdout.on( 'data', this.handleData ); this.process.stderr.on( 'data', this.handleData ); this.process.on( 'error', this.handleScriptExit ); this.process.on( 'close', this.handleScriptExit ); this.process.on( 'exit', this.handleScriptExit ); } BUILDING PROPER TERMINAL OUTPUT - execute a command -

          Slide 138

          Slide 138 text

          runScript() { this.processCmd = options.isCustom ? `npm run ${ script.name }` : script.command; this.$set( 'process', this.exec( this.processCmd, { cwd : options.cwd } ) ); this.process.stdout.on( 'data', this.handleData ); this.process.stderr.on( 'data', this.handleData ); this.process.on( 'error', this.handleScriptExit ); this.process.on( 'close', this.handleScriptExit ); this.process.on( 'exit', this.handleScriptExit ); } BUILDING PROPER TERMINAL OUTPUT There is no guarantee that these events are emitted in order. - execute a command -

          Slide 139

          Slide 139 text

          I was stuck for several days! Photo by madstreetz

          Slide 140

          Slide 140 text

          hyper github.com/zeit/hyper

          Slide 141

          Slide 141 text

          module.exports = class Session extends EventEmitter { constructor () { super(); const baseEnv = Object.assign( {}, process.env, { LANG : app.getLocale().replace( '-', '_' ) + '.UTF-8', TERM : 'xterm-256color' } ); this.pty = spawn( defaultShell, [ '--login' ], { env : baseEnv } ); this.pty.stdout.on( 'data', ( data ) => { this.emit( 'data', data.toString( 'utf8' ) ); } ); } write ( data ) { this.pty.stdin.write( data ); } }; BUILDING PROPER TERMINAL OUTPUT - let a shell execute the command -

          Slide 142

          Slide 142 text

          module.exports = class Session extends EventEmitter { constructor () { super(); const baseEnv = Object.assign( {}, process.env, { LANG : app.getLocale().replace( '-', '_' ) + '.UTF-8', TERM : 'xterm-256color' } ); this.pty = spawn( defaultShell, [ '--login' ], { env : baseEnv } ); this.pty.stdout.on( 'data', ( data ) => { this.emit( 'data', data.toString( 'utf8' ) ); } ); } write ( data ) { this.pty.stdin.write( data ); } }; BUILDING PROPER TERMINAL OUTPUT - let a shell execute the command -

          Slide 143

          Slide 143 text

          module.exports = class Session extends EventEmitter { constructor () { super(); const baseEnv = Object.assign( {}, process.env, { LANG : app.getLocale().replace( '-', '_' ) + '.UTF-8', TERM : 'xterm-256color' } ); this.pty = spawn( defaultShell, [ '--login' ], { env : baseEnv } ); this.pty.stdout.on( 'data', ( data ) => { this.emit( 'data', data.toString( 'utf8' ) ); } ); } write ( data ) { this.pty.stdin.write( data ); } }; BUILDING PROPER TERMINAL OUTPUT - let a shell execute the command -

          Slide 144

          Slide 144 text

          module.exports = class Session extends EventEmitter { constructor () { super(); const baseEnv = Object.assign( {}, process.env, { LANG : app.getLocale().replace( '-', '_' ) + '.UTF-8', TERM : 'xterm-256color' } ); this.pty = spawn( defaultShell, [ '--login' ], { env : baseEnv } ); this.pty.stdout.on( 'data', ( data ) => { this.emit( 'data', data.toString( 'utf8' ) ); } ); } write ( data ) { this.pty.stdin.write( data ); } }; BUILDING PROPER TERMINAL OUTPUT - let a shell execute the command -

          Slide 145

          Slide 145 text

          hterm www.npmjs.com/package/hterm-umdjs

          Slide 146

          Slide 146 text

          No content

          Slide 147

          Slide 147 text

          Community Photo by rotesnichts

          Slide 148

          Slide 148 text

          CONTRIBUTORS 626

          Slide 149

          Slide 149 text

          ELECTRON RELATED PACKAGES ON NPM 1600+

          Slide 150

          Slide 150 text

          COMMUNITY www.facebook.com/groups/electronjs/ atom-slack.herokuapp.com/

          Slide 151

          Slide 151 text

          github.com/sindresorhus/awesome-electron AWESOME-ELECTRON

          Slide 152

          Slide 152 text

          github.com/electron-userland ELECTRON USERLAND

          Slide 153

          Slide 153 text

          Distribution tooling (from the community) Photo by GrahamAndDairne

          Slide 154

          Slide 154 text

          github.com/electron-userland/electron-packager ELECTRON-PACKAGER

          Slide 155

          Slide 155 text

          No content

          Slide 156

          Slide 156 text

          No content

          Slide 157

          Slide 157 text

          blocky.app > 200MB

          Slide 158

          Slide 158 text

          github.com/electron-userland/electron-builder ELECTRON-BUILDER

          Slide 159

          Slide 159 text

          DISTRIBUTION - electron-builder - { "scripts": { "start": "electron .", "clean": "rm -rf ./dist", "clean:osx": "rm -rf ./dist/osx", "clean:win": "rm -rf ./dist/win", "pack": "npm run clean && npm run pack:osx && npm run pack:win", "pack:osx": "npm run clean:osx && electron-packager . \"Blocky\" --out=dist/osx --platform=darwin \ --arch=x64 --version=0.36.1 --icon=media/icon.icns --ignore=\"(node_modules|dist)\"", "pack:win": "npm run clean:win && electron-packager . \"Blocky\" --out=dist/win --platform=win32 \ --arch=ia32 --version=0.36.1 --icon=media/icon.ico --ignore=\"(node_modules|dist)\"", "build": "npm run build:osx && npm run build:win", "build:osx": "npm run pack:osx && electron-builder \"dist/osx/Blocky-darwin-x64/Blocky.app\" \ --platform=osx --out=\"dist/osx\" --config=builder.json", "build:win": "npm run pack:win && electron-builder \"dist/win/Blocky-win32-ia32\" \ --platform=win --out=\"dist/win\" --config=builder.json" } }

          Slide 160

          Slide 160 text

          DISTRIBUTION - electron-builder - { "scripts": { "start": "electron .", "clean": "rm -rf ./dist", "clean:osx": "rm -rf ./dist/osx", "clean:win": "rm -rf ./dist/win", "pack": "npm run clean && npm run pack:osx && npm run pack:win", "pack:osx": "npm run clean:osx && electron-packager . \"Blocky\" --out=dist/osx --platform=darwin \ --arch=x64 --version=0.36.1 --icon=media/icon.icns --ignore=\"(node_modules|dist)\"", "pack:win": "npm run clean:win && electron-packager . \"Blocky\" --out=dist/win --platform=win32 \ --arch=ia32 --version=0.36.1 --icon=media/icon.ico --ignore=\"(node_modules|dist)\"", "build": "npm run build:osx && npm run build:win", "build:osx": "npm run pack:osx && electron-builder \"dist/osx/Blocky-darwin-x64/Blocky.app\" \ --platform=osx --out=\"dist/osx\" --config=builder.json", "build:win": "npm run pack:win && electron-builder \"dist/win/Blocky-win32-ia32\" \ --platform=win --out=\"dist/win\" --config=builder.json" } }

          Slide 161

          Slide 161 text

          DISTRIBUTION - electron-builder - { "scripts": { "start": "electron .", "clean": "rm -rf ./dist", "clean:osx": "rm -rf ./dist/osx", "clean:win": "rm -rf ./dist/win", "pack": "npm run clean && npm run pack:osx && npm run pack:win", "pack:osx": "npm run clean:osx && electron-packager . \"Blocky\" --out=dist/osx --platform=darwin \ --arch=x64 --version=0.36.1 --icon=media/icon.icns --ignore=\"(node_modules|dist)\"", "pack:win": "npm run clean:win && electron-packager . \"Blocky\" --out=dist/win --platform=win32 \ --arch=ia32 --version=0.36.1 --icon=media/icon.ico --ignore=\"(node_modules|dist)\"", "build": "npm run build:osx && npm run build:win", "build:osx": "npm run pack:osx && electron-builder \"dist/osx/Blocky-darwin-x64/Blocky.app\" \ --platform=osx --out=\"dist/osx\" --config=builder.json", "build:win": "npm run pack:win && electron-builder \"dist/win/Blocky-win32-ia32\" \ --platform=win --out=\"dist/win\" --config=builder.json" } }

          Slide 162

          Slide 162 text

          DX (developer experience) matters Photo by Thomas Leuthard Vladimir Krivosheev @develar

          Slide 163

          Slide 163 text

          It should just work! Photo by Thomas Leuthard

          Slide 164

          Slide 164 text

          DISTRIBUTION - electron-builder - { "name": "blocky", "build": { "appId": "com.electron.blocky" }, "version": "1.0.0", "description": "A tray app to block the screensaver", "main": "index.js", "scripts": { "start": "electron .", "pack": "build --dir", "dist": "build" }, "author": "stefanjudis ", "license": "MIT", "devDependencies": { "electron": "^1.4.15", "electron-builder": "^13.5.0" } }

          Slide 165

          Slide 165 text

          DISTRIBUTION - electron-builder - { "name": "blocky", "build": { "appId": "com.electron.blocky" }, "version": "1.0.0", "description": "A tray app to block the screensaver", "main": "index.js", "scripts": { "start": "electron .", "pack": "build --dir", "dist": "build" }, "author": "stefanjudis ", "license": "MIT", "devDependencies": { "electron": "^1.4.15", "electron-builder": "^13.5.0" } }

          Slide 166

          Slide 166 text

          DISTRIBUTION - electron-builder -

          Slide 167

          Slide 167 text

          DISTRIBUTION - electron-builder - { "name": "blocky", "build": { "appId": "com.electron.blocky" }, "version": "1.0.0", "description": "A tray app to block the screensaver", "main": "index.js", "scripts": { "start": "electron .", "pack": "build --dir", "dist": "build" }, "author": "stefanjudis ", "license": "MIT", "devDependencies": { "electron": "^1.4.15", "electron-builder": "^13.5.0" } }

          Slide 168

          Slide 168 text

          No content

          Slide 169

          Slide 169 text

          No content

          Slide 170

          Slide 170 text

          Where are we today?

          Slide 171

          Slide 171 text

          No content

          Slide 172

          Slide 172 text

          No content

          Slide 173

          Slide 173 text

          No content

          Slide 174

          Slide 174 text

          No content

          Slide 175

          Slide 175 text

          No content

          Slide 176

          Slide 176 text

          No content

          Slide 177

          Slide 177 text

          No content

          Slide 178

          Slide 178 text

          No content

          Slide 179

          Slide 179 text

          No content

          Slide 180

          Slide 180 text

          No content

          Slide 181

          Slide 181 text

          No content

          Slide 182

          Slide 182 text

          No content

          Slide 183

          Slide 183 text

          No content

          Slide 184

          Slide 184 text

          “Web Dev is still delightfully fascinating and surprising and interesting and wonderful!” Laurie Voss Photo by eltpics

          Slide 185

          Slide 185 text

          160 My missing piece SERVER WEB IOT DESKTOP

          Slide 186

          Slide 186 text

          Passion is everything!

          Slide 187

          Slide 187 text

          Thanks! @stefanjudis Slides Feedback bit.ly/desktop-all-the-things bit.ly/desktop-all-the-things-feedback

          Slide 188

          Slide 188 text

          AWESOME APPS HYPER.IS ATOM.IO CODE.VISUALSTUDIO.COM THOMAS101.GITHUB.IO/WMAIL BRAVE.COM GETKAP.CO MEETFRANZ.COM SLACK.COM GITHUB.COM/STEFANJUDIS/FORREST

          Slide 189

          Slide 189 text

          Be careful

          Slide 190

          Slide 190 text

          Renderer process memory management