├── .npmrc ├── .gitattributes ├── .gitignore ├── app ├── templates │ ├── gitattributes │ ├── gitignore │ ├── build │ │ ├── icon.png │ │ ├── background.png │ │ └── background@2x.png │ ├── static │ │ └── icon.png │ ├── config.js │ ├── editorconfig │ ├── index.html │ ├── github │ │ └── workflows │ │ │ └── main.yml │ ├── index.css │ ├── readme.md │ ├── license │ ├── _package.json │ ├── index.js │ └── menu.js └── index.js ├── screenshot.png ├── .editorconfig ├── .github └── workflows │ └── main.yml ├── test.js ├── license ├── package.json └── readme.md /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | temp 4 | -------------------------------------------------------------------------------- /app/templates/gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /app/templates/gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | /dist 4 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/generator-electron/HEAD/screenshot.png -------------------------------------------------------------------------------- /app/templates/build/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/generator-electron/HEAD/app/templates/build/icon.png -------------------------------------------------------------------------------- /app/templates/static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/generator-electron/HEAD/app/templates/static/icon.png -------------------------------------------------------------------------------- /app/templates/build/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/generator-electron/HEAD/app/templates/build/background.png -------------------------------------------------------------------------------- /app/templates/build/background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/generator-electron/HEAD/app/templates/build/background@2x.png -------------------------------------------------------------------------------- /app/templates/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Store = require('electron-store'); 3 | 4 | module.exports = new Store({ 5 | defaults: { 6 | favoriteAnimal: '🦄', 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /app/templates/editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= appName %> 6 | 7 | 8 | 9 |
10 |
11 |

<%= appName %>

12 |

13 |
14 |
15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /app/templates/github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | name: Node.js ${{ matrix.node-version }} 8 | runs-on: macos-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: 13 | - 16 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - run: npm install 20 | - run: npm test 21 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | name: Node.js ${{ matrix.node-version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: 13 | - 14 14 | - 12 15 | - 10 16 | - 8 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: actions/setup-node@v4 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | - run: npm install 23 | - run: npm test 24 | -------------------------------------------------------------------------------- /app/templates/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | background: transparent; 6 | } 7 | 8 | /* Use OS default fonts */ 9 | body { 10 | font-family: 11 | system-ui, 12 | -apple-system, 13 | 'Segoe UI', 14 | Roboto, 15 | Helvetica, 16 | Arial, 17 | sans-serif, 18 | 'Apple Color Emoji', 19 | 'Segoe UI Emoji'; 20 | text-rendering: optimizeLegibility; 21 | font-feature-settings: 'liga', 'clig', 'kern'; 22 | } 23 | 24 | header { 25 | position: absolute; 26 | width: 500px; 27 | height: 250px; 28 | top: 50%; 29 | left: 50%; 30 | margin-top: -125px; 31 | margin-left: -250px; 32 | text-align: center; 33 | } 34 | 35 | header h1 { 36 | font-size: 60px; 37 | font-weight: 200; 38 | margin: 0; 39 | padding: 0; 40 | opacity: 0.7; 41 | } 42 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import {promisify} from 'util'; 2 | import path from 'path'; 3 | import {serial as test} from 'ava'; 4 | import helpers from 'yeoman-test'; 5 | import assert from 'yeoman-assert'; 6 | 7 | let generator; 8 | 9 | test.beforeEach(async () => { 10 | await promisify(helpers.testDirectory)(path.join(__dirname, 'temp')); 11 | generator = helpers.createGenerator('electron:app', ['../app'], undefined, {skipInstall: true}); 12 | }); 13 | 14 | test('generates expected files', async () => { 15 | helpers.mockPrompt(generator, { 16 | appName: 'test', 17 | githubUsername: 'test', 18 | website: 'test.com' 19 | }); 20 | 21 | await generator.run(); 22 | 23 | assert.file([ 24 | '.editorconfig', 25 | '.gitattributes', 26 | '.gitignore', 27 | 'index.js', 28 | 'index.html', 29 | 'index.css', 30 | 'license', 31 | 'package.json', 32 | 'readme.md' 33 | ]); 34 | }); 35 | -------------------------------------------------------------------------------- /app/templates/readme.md: -------------------------------------------------------------------------------- 1 | # <%= appName %> 2 | 3 | > My <%= superb %> app 4 | 5 | ## Install 6 | 7 | *macOS 10.13+, Linux, and Windows 7+ are supported (64-bit only).* 8 | 9 | **macOS** 10 | 11 | [**Download**](<%= repoUrl %>/releases/latest) the `.dmg` file. 12 | 13 | **Linux** 14 | 15 | [**Download**](<%= repoUrl %>/releases/latest) the `.AppImage` or `.deb` file. 16 | 17 | *The AppImage needs to be [made executable](https://discourse.appimage.org/t/how-to-make-an-appimage-executable/80) after download.* 18 | 19 | **Windows** 20 | 21 | [**Download**](<%= repoUrl %>/releases/latest) the `.exe` file. 22 | 23 | --- 24 | 25 | ## Dev 26 | 27 | Built with [Electron](https://electronjs.org). 28 | 29 | ### Run 30 | 31 | ```sh 32 | npm install 33 | npm start 34 | ``` 35 | 36 | ### Publish 37 | 38 | ```sh 39 | npm run release 40 | ``` 41 | 42 | After the app is built, open the release draft it created and click "Publish". 43 | -------------------------------------------------------------------------------- /app/templates/license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) <%= name %> <<%= email %>> (<%= website %>) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus (https://sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-electron", 3 | "version": "2.4.0", 4 | "description": "Scaffold out an Electron app", 5 | "license": "MIT", 6 | "repository": "sindresorhus/generator-electron", 7 | "funding": "https://github.com/sponsors/sindresorhus", 8 | "author": { 9 | "name": "Sindre Sorhus", 10 | "email": "sindresorhus@gmail.com", 11 | "url": "https://sindresorhus.com" 12 | }, 13 | "sideEffects": false, 14 | "engines": { 15 | "node": ">=8" 16 | }, 17 | "scripts": { 18 | "test": "xo && ava" 19 | }, 20 | "files": [ 21 | "app" 22 | ], 23 | "keywords": [ 24 | "yeoman-generator", 25 | "boilerplate", 26 | "template", 27 | "scaffold", 28 | "node", 29 | "desktop", 30 | "app", 31 | "application", 32 | "electron" 33 | ], 34 | "dependencies": { 35 | "humanize-url": "^2.1.0", 36 | "normalize-url": "^4.3.0", 37 | "superb": "^4.0.0", 38 | "underscore.string": "^3.0.3", 39 | "yeoman-generator": "^4.0.2" 40 | }, 41 | "devDependencies": { 42 | "ava": "^2.3.0", 43 | "xo": "^0.25.3", 44 | "yeoman-assert": "^3.0.0", 45 | "yeoman-test": "^2.0.0" 46 | }, 47 | "xo": { 48 | "ignores": [ 49 | "app/templates/**" 50 | ] 51 | }, 52 | "ava": { 53 | "failWithoutAssertions": false 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # generator-electron 2 | 3 | > Scaffold out an Electron app 4 | 5 | See [awesome-electron](https://github.com/sindresorhus/awesome-electron) for more useful Electron resources. 6 | 7 | See [Caprine](https://github.com/sindresorhus/caprine) for a production app based on this boilerplate. 8 | 9 | 10 | ## Features 11 | 12 | - [`electron-builder`](https://www.electron.build) fully set up to create cross-platform builds 13 | - [Builds the app on Travis](https://www.electron.build/multi-platform-build.html) 14 | - [Silent auto-updates](https://www.electron.build/auto-update.html) 15 | - App menu that adheres to the system user interface guidelines 16 | - [Config handling](https://github.com/sindresorhus/electron-store) 17 | - [Context menu](https://github.com/sindresorhus/electron-context-menu) 18 | - [User-friendly handling of unhandled errors](https://github.com/sindresorhus/electron-unhandled) 19 | - Easily publish new versions to GitHub Releases 20 | - And much more! 21 | 22 | 23 | ## Install 24 | 25 | ```sh 26 | npm install --global yo generator-electron 27 | ``` 28 | 29 | 30 | ## Usage 31 | 32 | With [yo](https://github.com/yeoman/yo): 33 | 34 | ``` 35 | $ yo electron 36 | ``` 37 | 38 | 39 | ## Related 40 | 41 | - [electron-boilerplate](https://github.com/sindresorhus/electron-boilerplate) - Electron app boilerplate 42 | -------------------------------------------------------------------------------- /app/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= slugifiedAppName %>", 3 | "productName": "<%= appName %>", 4 | "version": "0.0.0", 5 | "description": "My <%= superb %> app", 6 | "license": "MIT", 7 | "repository": "<%= githubUsername %>/<%= slugifiedAppName %>", 8 | "author": { 9 | "name": "<%= name %>", 10 | "email": "<%= email %>", 11 | "url": "<%= humanizedWebsite %>" 12 | }, 13 | "scripts": { 14 | "postinstall": "electron-builder install-app-deps", 15 | "lint": "xo", 16 | "test": "npm run lint", 17 | "start": "electron .", 18 | "pack": "electron-builder --dir", 19 | "dist": "electron-builder --macos --linux --windows", 20 | "release": "np" 21 | }, 22 | "dependencies": { 23 | "electron-context-menu": "^3.4.0", 24 | "electron-debug": "^3.2.0", 25 | "electron-store": "^8.1.0", 26 | "electron-unhandled": "^4.0.1", 27 | "electron-updater": "^5.2.1", 28 | "electron-util": "^0.17.2" 29 | }, 30 | "devDependencies": { 31 | "electron": "^13.0.0", 32 | "electron-builder": "^23.3.3", 33 | "np": "^7.6.2", 34 | "xo": "^0.51.0" 35 | }, 36 | "xo": { 37 | "envs": [ 38 | "node", 39 | "browser" 40 | ], 41 | "rules": { 42 | "unicorn/prefer-module": "off" 43 | } 44 | }, 45 | "np": { 46 | "publish": false, 47 | "releaseDraft": false 48 | }, 49 | "build": { 50 | "appId": "com.<%= githubUsername %>.<%= slugifiedAppName %>", 51 | "mac": { 52 | "category": "public.app-category.social-networking", 53 | "darkModeSupport": true 54 | }, 55 | "dmg": { 56 | "iconSize": 160, 57 | "contents": [ 58 | { 59 | "x": 180, 60 | "y": 170 61 | }, 62 | { 63 | "x": 480, 64 | "y": 170, 65 | "type": "link", 66 | "path": "/Applications" 67 | } 68 | ] 69 | }, 70 | "linux": { 71 | "target": [ 72 | "AppImage", 73 | "deb" 74 | ], 75 | "category": "Network;Chat" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const superb = require('superb'); 3 | const normalizeUrl = require('normalize-url'); 4 | const humanizeUrl = require('humanize-url'); 5 | const Generator = require('yeoman-generator'); 6 | const _s = require('underscore.string'); 7 | 8 | module.exports = class extends Generator { 9 | async init() { 10 | const props = await this.prompt([{ 11 | name: 'appName', 12 | message: 'What do you want to name your app?', 13 | default: this.appname 14 | }, { 15 | name: 'githubUsername', 16 | message: 'What is your GitHub username?', 17 | store: true, 18 | validate: x => x.length > 0 ? true : 'You have to provide a username' 19 | }, { 20 | name: 'website', 21 | message: 'What is the URL of your website?', 22 | store: true, 23 | validate: x => x.length > 0 ? true : 'You have to provide a website URL', 24 | filter: x => normalizeUrl(x) 25 | }]); 26 | 27 | const template = { 28 | appName: props.appName, 29 | slugifiedAppName: _s.slugify(props.appName), 30 | classifiedAppName: _s.classify(props.appName), 31 | githubUsername: props.githubUsername, 32 | repoUrl: `https://github.com/${props.githubUsername}/${props.slugifiedAppName}`, 33 | name: this.user.git.name(), 34 | email: this.user.git.email(), 35 | website: props.website, 36 | humanizedWebsite: humanizeUrl(props.website), 37 | superb: superb.random() 38 | }; 39 | 40 | const moveDest = (from, to) => { 41 | this.fs.move(this.destinationPath(from), this.destinationPath(to)); 42 | }; 43 | 44 | const copy = (from, to) => { 45 | this.fs.copy(this.templatePath(from), this.destinationPath(to || from)); 46 | }; 47 | 48 | this.fs.copyTpl(`${this.templatePath()}/*`, this.destinationPath(), template); 49 | copy('build'); 50 | copy('static'); 51 | copy('github/workflows/main.yml', '.github/workflows/main.yml'); 52 | moveDest('editorconfig', '.editorconfig'); 53 | moveDest('gitattributes', '.gitattributes'); 54 | moveDest('gitignore', '.gitignore'); 55 | moveDest('_package.json', 'package.json'); 56 | } 57 | 58 | install() { 59 | this.installDependencies({bower: false}); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /app/templates/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const {app, BrowserWindow, Menu} = require('electron'); 4 | /// const {autoUpdater} = require('electron-updater'); 5 | const {is} = require('electron-util'); 6 | const unhandled = require('electron-unhandled'); 7 | const debug = require('electron-debug'); 8 | const contextMenu = require('electron-context-menu'); 9 | const config = require('./config.js'); 10 | const menu = require('./menu.js'); 11 | const packageJson = require('./package.json'); 12 | 13 | unhandled(); 14 | debug(); 15 | contextMenu(); 16 | 17 | app.setAppUserModelId(packageJson.build.appId); 18 | 19 | // Uncomment this before publishing your first version. 20 | // It's commented out as it throws an error if there are no published versions. 21 | // if (!is.development) { 22 | // const FOUR_HOURS = 1000 * 60 * 60 * 4; 23 | // setInterval(() => { 24 | // autoUpdater.checkForUpdates(); 25 | // }, FOUR_HOURS); 26 | // 27 | // autoUpdater.checkForUpdates(); 28 | // } 29 | 30 | // Prevent window from being garbage collected 31 | let mainWindow; 32 | 33 | const createMainWindow = async () => { 34 | const window_ = new BrowserWindow({ 35 | title: app.name, 36 | show: false, 37 | width: 600, 38 | height: 400, 39 | }); 40 | 41 | window_.on('ready-to-show', () => { 42 | window_.show(); 43 | }); 44 | 45 | window_.on('closed', () => { 46 | // Dereference the window 47 | // For multiple windows store them in an array 48 | mainWindow = undefined; 49 | }); 50 | 51 | await window_.loadFile(path.join(__dirname, 'index.html')); 52 | 53 | return window_; 54 | }; 55 | 56 | // Prevent multiple instances of the app 57 | if (!app.requestSingleInstanceLock()) { 58 | app.quit(); 59 | } 60 | 61 | app.on('second-instance', () => { 62 | if (mainWindow) { 63 | if (mainWindow.isMinimized()) { 64 | mainWindow.restore(); 65 | } 66 | 67 | mainWindow.show(); 68 | } 69 | }); 70 | 71 | app.on('window-all-closed', () => { 72 | if (!is.macos) { 73 | app.quit(); 74 | } 75 | }); 76 | 77 | app.on('activate', () => { 78 | if (!mainWindow) { 79 | mainWindow = createMainWindow(); 80 | } 81 | }); 82 | 83 | (async () => { 84 | await app.whenReady(); 85 | Menu.setApplicationMenu(menu); 86 | mainWindow = await createMainWindow(); 87 | 88 | const favoriteAnimal = config.get('favoriteAnimal'); 89 | mainWindow.webContents.executeJavaScript(`document.querySelector('header p').textContent = 'Your favorite animal is ${favoriteAnimal}'`); 90 | })(); 91 | -------------------------------------------------------------------------------- /app/templates/menu.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const {app, Menu, shell} = require('electron'); 4 | const { 5 | is, 6 | appMenu, 7 | aboutMenuItem, 8 | openUrlMenuItem, 9 | openNewGitHubIssue, 10 | debugInfo, 11 | } = require('electron-util'); 12 | const config = require('./config.js'); 13 | 14 | const showPreferences = () => { 15 | // Show the app's preferences here 16 | }; 17 | 18 | const helpSubmenu = [ 19 | openUrlMenuItem({ 20 | label: 'Website', 21 | url: '<%= repoUrl %>', 22 | }), 23 | openUrlMenuItem({ 24 | label: 'Source Code', 25 | url: '<%= repoUrl %>', 26 | }), 27 | { 28 | label: 'Report an Issue…', 29 | click() { 30 | const body = ` 31 | 32 | 33 | 34 | --- 35 | 36 | ${debugInfo()}`; 37 | 38 | openNewGitHubIssue({ 39 | user: '<%= githubUsername %>', 40 | repo: '<%= slugifiedAppName %>', 41 | body, 42 | }); 43 | }, 44 | }, 45 | ]; 46 | 47 | if (!is.macos) { 48 | helpSubmenu.push( 49 | { 50 | type: 'separator', 51 | }, 52 | aboutMenuItem({ 53 | icon: path.join(__dirname, 'static', 'icon.png'), 54 | text: 'Created by <%= name %>', 55 | }), 56 | ); 57 | } 58 | 59 | const debugSubmenu = [ 60 | { 61 | label: 'Show Settings', 62 | click() { 63 | config.openInEditor(); 64 | }, 65 | }, 66 | { 67 | label: 'Show App Data', 68 | click() { 69 | shell.openItem(app.getPath('userData')); 70 | }, 71 | }, 72 | { 73 | type: 'separator', 74 | }, 75 | { 76 | label: 'Delete Settings', 77 | click() { 78 | config.clear(); 79 | app.relaunch(); 80 | app.quit(); 81 | }, 82 | }, 83 | { 84 | label: 'Delete App Data', 85 | click() { 86 | shell.moveItemToTrash(app.getPath('userData')); 87 | app.relaunch(); 88 | app.quit(); 89 | }, 90 | }, 91 | ]; 92 | 93 | const macosTemplate = [ 94 | appMenu([ 95 | { 96 | label: 'Preferences…', 97 | accelerator: 'Command+,', 98 | click() { 99 | showPreferences(); 100 | }, 101 | }, 102 | ]), 103 | { 104 | role: 'fileMenu', 105 | submenu: [ 106 | { 107 | label: 'Custom', 108 | }, 109 | { 110 | type: 'separator', 111 | }, 112 | { 113 | role: 'close', 114 | }, 115 | ], 116 | }, 117 | { 118 | role: 'editMenu', 119 | }, 120 | { 121 | role: 'viewMenu', 122 | }, 123 | { 124 | role: 'windowMenu', 125 | }, 126 | { 127 | role: 'help', 128 | submenu: helpSubmenu, 129 | }, 130 | ]; 131 | 132 | // Linux and Windows 133 | const otherTemplate = [ 134 | { 135 | role: 'fileMenu', 136 | submenu: [ 137 | { 138 | label: 'Custom', 139 | }, 140 | { 141 | type: 'separator', 142 | }, 143 | { 144 | label: 'Settings', 145 | accelerator: 'Control+,', 146 | click() { 147 | showPreferences(); 148 | }, 149 | }, 150 | { 151 | type: 'separator', 152 | }, 153 | { 154 | role: 'quit', 155 | }, 156 | ], 157 | }, 158 | { 159 | role: 'editMenu', 160 | }, 161 | { 162 | role: 'viewMenu', 163 | }, 164 | { 165 | role: 'help', 166 | submenu: helpSubmenu, 167 | }, 168 | ]; 169 | 170 | const template = is.macos ? macosTemplate : otherTemplate; 171 | 172 | if (is.development) { 173 | template.push({ 174 | label: 'Debug', 175 | submenu: debugSubmenu, 176 | }); 177 | } 178 | 179 | module.exports = Menu.buildFromTemplate(template); 180 | --------------------------------------------------------------------------------