├── icon-electron-wrap.icns ├── package.json ├── README.md ├── wrap.sh └── index-electron-wrap.js /icon-electron-wrap.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thmsbfft/electron-wrap/HEAD/icon-electron-wrap.icns -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "name-electron-wrap", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index-electron-wrap.js", 6 | "scripts": { 7 | "start": "electron .", 8 | "package": "electron-packager . --overwrite --prune --out=build --platform=darwin --arch=x64 --icon=icon-electron-wrap.icns" 9 | }, 10 | "author": "thmsbfft", 11 | "license": "MIT" 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### electron-wrap 2 | 3 | --- 4 | 5 | Package any local/static website folder (typically a `/public`) into a desktop application, electron/macOS. The process requires Node/npm installed on your machine. 6 | 7 | To do so, `cd` to the folder that contains the files, and run: 8 | 9 | ``` 10 | bash <(curl -s https://raw.githubusercontent.com/thmsbfft/electron-wrap/master/wrap.sh) 11 | ``` 12 | 13 | The script will ask for a name and a window size for the app. After that, the script will: 14 | 15 | **➀** Download `package.json` and `index.js` from this repository 16 | 17 | **➁** Install the dependencies (electron, express to serve the files) 18 | 19 | **➂** Package the folder into an Electron executable 20 | 21 | **➃** Cleanup the files it downloaded + `node_modules/` 22 | 23 | --- 24 | 25 | Useful for prototyping, presenting, or for simple offline stuff. -------------------------------------------------------------------------------- /wrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -e package.json ] 3 | then 4 | echo "/!\ package.json already exists." 5 | exit 6 | else 7 | # get parameters 8 | read -p "Project name: " name 9 | read -p "Window width: " width 10 | read -p "Window height: " height 11 | 12 | # copy files 13 | echo "" 14 | echo "Copying files..." 15 | echo "" 16 | curl -O https://raw.githubusercontent.com/thmsbfft/electron-wrap/master/package.json 17 | curl -O https://raw.githubusercontent.com/thmsbfft/electron-wrap/master/index-electron-wrap.js 18 | 19 | # replace values 20 | sed -i '' "s/name-electron-wrap/$name/" package.json 21 | sed -i '' "s/WINDOW_WIDTH\ =\ null/WINDOW_WIDTH\ =\ $width/" index-electron-wrap.js 22 | sed -i '' "s/WINDOW_HEIGHT\ =\ null/WINDOW_HEIGHT\ =\ $height/" index-electron-wrap.js 23 | 24 | # if there's an icon, use it 25 | # otherwise, download a generic icon 26 | if [ -e icon.icns ]; then 27 | sed -i '' "s/icon-electron-wrap.icns/icon.icns/" package.json 28 | else 29 | echo "Icon not found!" 30 | curl -O https://raw.githubusercontent.com/thmsbfft/electron-wrap/master/icon-electron-wrap.icns 31 | fi 32 | 33 | # install dependencies 34 | echo "" 35 | echo "Installing node modules..." 36 | echo "" 37 | npm install electron --save-dev 38 | npm install electron-packager --save-dev 39 | npm install express --save 40 | 41 | # package the folder 42 | npm run package 43 | 44 | # cleanup 45 | rm package.json 46 | rm package-lock.json 47 | rm -rf node_modules 48 | rm index-electron-wrap.js 49 | rm icon-electron-wrap.icns 50 | 51 | osascript -e 'display notification "Electron wrap is done." with title "Finished!"' 52 | fi -------------------------------------------------------------------------------- /index-electron-wrap.js: -------------------------------------------------------------------------------- 1 | const {app, Menu, BrowserWindow} = require('electron') 2 | const express = require('express') 3 | 4 | const path = require('path') 5 | const url = require('url') 6 | 7 | const WINDOW_WIDTH = null 8 | const WINDOW_HEIGHT = null 9 | 10 | var w 11 | 12 | var app_ready = false 13 | var server_ready = false 14 | 15 | const server = express() 16 | const port = 3000 17 | 18 | server.use(express.static(__dirname)) 19 | server.listen(port, function() { 20 | server_ready = true 21 | if (app_ready) open() 22 | }) 23 | 24 | function open () { 25 | let bw_options = { 26 | width: WINDOW_WIDTH ? WINDOW_WIDTH : 412, 27 | height: WINDOW_HEIGHT ? WINDOW_HEIGHT : 870, 28 | resizable: false 29 | } 30 | 31 | w = new BrowserWindow( 32 | bw_options 33 | ) 34 | 35 | w.setResizable(false) 36 | 37 | w.loadURL(url.format({ 38 | pathname: 'localhost' + ':' + port, 39 | protocol: 'http:', 40 | slashes: true 41 | })) 42 | 43 | // w.webContents.openDevTools() 44 | 45 | w.on('closed', function () { 46 | w = null 47 | }) 48 | } 49 | 50 | app.on('ready', function () { 51 | app_ready = true 52 | create_menus() 53 | if (server_ready) open() 54 | }) 55 | 56 | app.on('window-all-closed', function () { 57 | app.quit() 58 | }) 59 | 60 | function create_menus () { 61 | const template = [ 62 | { 63 | label: app.getName(), 64 | submenu: [ 65 | { 66 | role: 'quit' 67 | } 68 | ] 69 | }, 70 | { 71 | label: 'Edit', 72 | submenu: [ 73 | {role: 'undo'}, 74 | {role: 'redo'}, 75 | {type: 'separator'}, 76 | {role: 'cut'}, 77 | {role: 'copy'}, 78 | {role: 'paste'}, 79 | {role: 'pasteandmatchstyle'}, 80 | {role: 'delete'}, 81 | {role: 'selectall'} 82 | ] 83 | }, 84 | { 85 | label: 'View', 86 | submenu: [ 87 | { 88 | label: 'Open in Browser...', 89 | click () { 90 | require('electron').shell.openExternal('http://localhost:' + port) 91 | } 92 | }, 93 | { 94 | type: 'separator' 95 | }, 96 | {role: 'reload'}, 97 | {role: 'forcereload'}, 98 | {role: 'toggledevtools'}, 99 | {type: 'separator'}, 100 | {role: 'resetzoom'}, 101 | {role: 'zoomin'}, 102 | {role: 'zoomout'}, 103 | {type: 'separator'}, 104 | {role: 'togglefullscreen'} 105 | ] 106 | } 107 | ] 108 | 109 | const menu = Menu.buildFromTemplate(template) 110 | Menu.setApplicationMenu(menu) 111 | } --------------------------------------------------------------------------------