├── .gitignore ├── .npmignore ├── .prettierrc.json ├── .stylelintrc.json ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── about.html ├── example ├── icon.png ├── index.html ├── main.js └── package.json ├── package-lock.json ├── package.json ├── src ├── index.ts ├── renderer.ts └── tsconfig.json ├── styles └── ui.css └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | /src/*.js 4 | /src/*.js.map 5 | /src/*.d.ts 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /typings 2 | /.git 3 | /example 4 | npm-debug.log 5 | node_modules 6 | /src/*.ts 7 | !/src/*.d.ts 8 | /tsconfig.json 9 | /tslint.json 10 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "semi": true, 4 | "singleQuote": true, 5 | "trailingComma": "es5", 6 | "printWidth": 120, 7 | "arrowParens": "avoid", 8 | "overrides": [ 9 | { 10 | "files": [ 11 | "*.ts", 12 | "*.tsx" 13 | ], 14 | "options": { 15 | "trailingComma": "all" 16 | } 17 | }, 18 | { 19 | "files": "*.css", 20 | "options": { 21 | "tabWidth": 2, 22 | "printWidth": -1 23 | } 24 | }, 25 | { 26 | "files": "*.md", 27 | "options": { 28 | "tabWidth": 2 29 | } 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # [v1.15.2](https://github.com/rhysd/electron-about-window/releases/tag/v1.15.2) - 02 Nov 2021 3 | 4 | - Fix `AboutWindowInfo` type is not found (#71) 5 | 6 | [Changes][v1.15.2] 7 | 8 | 9 | 10 | # [v1.15.1](https://github.com/rhysd/electron-about-window/releases/tag/v1.15.1) - 25 Oct 2021 11 | 12 | - Fix the type definitions file for TypeScript is outdated (thanks @h3poteto, #70) 13 | 14 | [Changes][v1.15.1] 15 | 16 | 17 | 18 | # [v1.15.0](https://github.com/rhysd/electron-about-window/releases/tag/v1.15.0) - 02 Oct 2021 19 | 20 | - Support Electron 14+ by removing dependency of `remote` module 21 | - Update all dependencies to the latest 22 | 23 | [Changes][v1.15.0] 24 | 25 | 26 | 27 | # [v1.14.0](https://github.com/rhysd/electron-about-window/releases/tag/v1.14.0) - 09 Mar 2021 28 | 29 | - Allow to specify custom version entries for version information. See [the example code](https://github.com/rhysd/electron-about-window/blob/a0a81c918fb02f8cf9772df7df5ee55ee5de6ae9/example/main.js#L38-L46) for the usage. (#53, thanks to @noob9527) 30 | - Disable context isolation on creating a `BrowserWindow` instance. It fixes this library does not work with Electron v12. (#64, thanks to @kondoumh) 31 | 32 | [Changes][v1.14.0] 33 | 34 | 35 | 36 | # [v1.13.4](https://github.com/rhysd/electron-about-window/releases/tag/v1.13.4) - 05 Jun 2020 37 | 38 | - Allow to import this package without Electron runtime (#58) 39 | 40 | [Changes][v1.13.4] 41 | 42 | 43 | 44 | # [v1.13.3](https://github.com/rhysd/electron-about-window/releases/tag/v1.13.3) - 03 Jun 2020 45 | 46 | - **Fix:** Broken at Electron v10 beta because `enableRemoteModule` is set to `false` by default from v10 47 | - **Fix:** Loading this module in renderer process did not work 48 | 49 | [Changes][v1.13.3] 50 | 51 | 52 | 53 | # [v1.13.2](https://github.com/rhysd/electron-about-window/releases/tag/v1.13.2) - 13 Nov 2019 54 | 55 | - Supported Electron v7 (#50). Still should work with electron v6 or earlier. 56 | - Fixed given options object was modified while initialization (#49) 57 | - Improved CSS rules to fit to frameless window (#41) 58 | - Fixed CSS rule was not correct 59 | - Update dependencies 60 | 61 | [Changes][v1.13.2] 62 | 63 | 64 | 65 | # [v1.13.0](https://github.com/rhysd/electron-about-window/releases/tag/v1.13.0) - 16 May 2019 66 | 67 | - Fixed this package did not work with Electron v5 #38 68 | - Added `about_page_dir` option for changing directory of HTML file #29 69 | - Added `win_title` option for changing title of window #31 70 | - Added `show_close_button` option for adding close button #34 71 | 72 | [Changes][v1.13.0] 73 | 74 | 75 | 76 | # [v1.12.0](https://github.com/rhysd/electron-about-window/releases/tag/v1.12.0) - 04 Jul 2018 77 | 78 | - Added `use_version_info` flag to hide version information #25 79 | 80 | [Changes][v1.12.0] 81 | 82 | 83 | [v1.15.2]: https://github.com/rhysd/electron-about-window/compare/v1.15.1...v1.15.2 84 | [v1.15.1]: https://github.com/rhysd/electron-about-window/compare/v1.15.0...v1.15.1 85 | [v1.15.0]: https://github.com/rhysd/electron-about-window/compare/v1.14.0...v1.15.0 86 | [v1.14.0]: https://github.com/rhysd/electron-about-window/compare/v1.13.4...v1.14.0 87 | [v1.13.4]: https://github.com/rhysd/electron-about-window/compare/v1.13.3...v1.13.4 88 | [v1.13.3]: https://github.com/rhysd/electron-about-window/compare/v1.13.2...v1.13.3 89 | [v1.13.2]: https://github.com/rhysd/electron-about-window/compare/v1.13.0...v1.13.2 90 | [v1.13.0]: https://github.com/rhysd/electron-about-window/compare/v1.12.0...v1.13.0 91 | [v1.12.0]: https://github.com/rhysd/electron-about-window/tree/v1.12.0 92 | 93 | 94 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 rhysd 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 14 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 15 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 16 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 17 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 18 | THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 'About This App' Window for [Electron](https://github.com/atom/electron) Apps 2 | ============================================================================= 3 | [![npm version](https://badge.fury.io/js/about-window.svg)](https://www.npmjs.com/package/about-window) 4 | 5 | [This package](https://www.npmjs.com/package/about-window) provides 'About This App' window for [Electron](https://github.com/atom/electron) applications. 6 | 7 | - [x] Create 'About This App' window from given parameters 8 | - [x] Icon path 9 | - [x] Copy right 10 | - [x] App name and Versions 11 | - [x] Description 12 | - [x] Gather package information from package.json 13 | - [x] Automatically detect package.json 14 | - [x] Adjust window size to its contents automatically 15 | - [x] Optional close button 16 | - [x] CSS customizability 17 | 18 | You can install this module via [npm](https://www.npmjs.com/). 19 | 20 | ```sh 21 | $ npm install about-window 22 | ``` 23 | 24 | ## Usage 25 | 26 | Only one function is exported as default. Please see [TypeScript type definition](index.d.ts). 27 | The function can be called from both main process and renderer process. 28 | 29 | ```typescript 30 | export default function openAboutWindow(info: { 31 | icon_path: string; 32 | product_name?: string; 33 | package_json_dir?: string; 34 | about_page_dir?: string; 35 | bug_report_url?: string; 36 | bug_link_text?: string; 37 | copyright?: string; 38 | homepage?: string; 39 | description?: string; 40 | license?: string; 41 | css_path?: string | string[]; 42 | adjust_window_size?: boolean; 43 | win_options?: BrowserWindowOptions; 44 | use_version_info?: boolean | [string, string][]; 45 | show_close_button?: string; 46 | app?: Electron.App; 47 | BrowserWindow?: typeof Electron.BrowserWindow; 48 | ipcMain?: Electron.IpcMain; 49 | }): BrowserWindow 50 | ``` 51 | 52 | Only `icon_path` property is required, others are optional. 53 | I recommend to specify as below to extract information from package.json as much as possible. 54 | Path to package.json is also automatically detected if possible. 55 | 56 | ```typescript 57 | openAboutWindow({ 58 | icon_path: 'path/to/icon.png' 59 | }); 60 | ``` 61 | 62 | If `string` value is passed to the first parameter, it is passed to `icon_path`. So following is a shorthand of above code: 63 | 64 | ```typescript 65 | openAboutWindow('path/to/icon.png'); 66 | ``` 67 | 68 | You can check [an example app](example) to know how to use this package. 69 | 70 | ```sh 71 | $ git clone https://github.com/rhysd/about-window.git 72 | $ cd about-window/example 73 | $ npm install 74 | $ npm start 75 | 76 | # Or for debug 77 | $ npm run debug 78 | ``` 79 | 80 | ### Parameter's properties of `openAboutWindow()` 81 | 82 | | Name | Description | Type | 83 | |------|-------------|------| 84 | | `icon_path` | Path to icon file of the application. The path is passed to `src` property of `` element. **Required** | string | 85 | | `package_json_dir` | Path to directory which contains package.json. If not specified, it will try to detect a path to package.json. If also failed, it gives up and show less information in 'About This App' window. **Optional** | string | 86 | | `bug_report_url` | URL to bug report page. If not specified, 'bugs' entry in package.json is used. **Optional** | string | 87 | | `copyright` | Copyright notice shown in window. If not specified, it is replaced with license description generated by 'license' entry of package.json. **Optional** | string | 88 | | `homepage` | URL of application's web page. If not specified, 'homepage' entry of package.json is used instead. **Optional** | string | 89 | | `description` | Description of the application. If not specified, 'description' entry of package.json is used instead. **Optional** | string | 90 | | `license` | License of the application. If not specified, 'license' entry of package.json is used instead. This property is used when `copyright` is not specified. **Optional** | string | 91 | | `win_options` | Options of 'About This App' window. It is merged into default options. **Optional** | [BrowserWindow options object](https://github.com/atom/electron/blob/master/docs/api/browser-window.md#new-browserwindowoptions) | 92 | | `css_path` | Path(s) to user-defined CSS file. It will be inserted to DOM of the window. **Optional** | (array of) string | 93 | | `adjust_window_size` | Adjust the window size to its content not to show scroll bar. **Optional** | boolean | 94 | | `open_devtools` | For debug purpose, Chrome DevTools will start when the window is opened. **Optional** | boolean | 95 | | `use_inner_html` | If `true`, set the value with `.innerHTML` on copyright, license and description Default is `false`. **Optional** | boolean | 96 | | `bug_link_text` | Text for a bug report link. **Optional** | string | 97 | | `product_name` | Name of the application **Optional** | string | 98 | | `use_version_info` | If is `false`, nothing will be displayed, if is `true`, the versions of electron, chrome, node, and v8 will be displayed, if is an array of string tuple, its entries will be displayed. Default is `true`. **Optional** | boolean | 99 | | `show_close_button` | If this is a valid string, a close button with this string be displayed. **Optional** | string | 100 | | `about_page_dir` | Directory path which contains `about.html` which is rendered in 'About this app' window. **Optional** | string | 101 | | `app` | [app](https://www.electronjs.org/docs/latest/api/app) instance to use. This property is necessary only when using on renderer processes. **Optional** | Electron.App | 102 | | `BrowserWindow` | Constructor of [BrowserWindow](https://www.electronjs.org/docs/latest/api/browser-window) to use. This property is necessary only when using on renderer processes. **Optional** | Electron.BrowserWindow | 103 | | `ipcMain` | [ipcMain](https://www.electronjs.org/docs/latest/api/ipc-main) instance to use. This property is necessary only when using on renderer processes. **Optional** | Electron.IpcMain | 104 | 105 | **Note:** If you set `use_inner_html` to `true`, please ensure that contents don't contain any untrusted external input 106 | in order to avoid XSS. Be careful. 107 | 108 | ### Open the window from non-main process 109 | 110 | Since `openAboutWindow()` depends on several APIs only available on main process, they must be provided by caller when it is called on non-main process. 111 | 112 | To mimic the APIs, use [`@electron/remote`](https://www.npmjs.com/package/@electron/remote) module. 113 | 114 | ```typescript 115 | import {app, BrowserWindow, ipcMain} from '@electron/remote'; 116 | 117 | openAboutWindow({ 118 | icon_path: 'path/to/icon.png' 119 | app, 120 | BrowserWindow, 121 | ipcMain, 122 | }); 123 | ``` 124 | 125 | ## Screen Shots 126 | 127 | ### Linux 128 | 129 | ![Linux screenshot](https://raw.githubusercontent.com/rhysd/ss/master/about-window/about-window-linux.png) 130 | 131 | ### OS X 132 | 133 | ![OS X screenshot](https://raw.githubusercontent.com/rhysd/ss/master/about-window/about-window-os-x.png) 134 | 135 | ### Windows 136 | 137 | ![Windows screenshot](https://raw.githubusercontent.com/rhysd/ss/master/about-window/about-window-windows.jpg) 138 | 139 | ## License 140 | 141 | [MIT License](/LICENSE.txt). 142 | 143 | -------------------------------------------------------------------------------- /about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | About This App 7 | 8 | 9 | 10 | 13 |

14 |

15 | 16 |
17 |
18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/electron-about-window/9dc88da999d64e9a614d33adf649d566c0a35fcb/example/icon.png -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 6 | 7 | 8 |
Find 'About This App' item in menu bar.
9 | 10 | 11 | -------------------------------------------------------------------------------- /example/main.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | const app = electron.app; 3 | const Menu = electron.Menu; 4 | const BrowserWindow = electron.BrowserWindow; 5 | const join = require('path').join; 6 | 7 | // Replace '..' with 'about-window' 8 | const openAboutWindow = require('..').default; 9 | 10 | app.once('window-all-closed', function () { 11 | app.quit(); 12 | }); 13 | 14 | app.once('ready', function () { 15 | let w = new BrowserWindow(); 16 | w.once('closed', function () { 17 | w = null; 18 | }); 19 | w.loadURL('file://' + join(__dirname, 'index.html')); 20 | 21 | const menu = Menu.buildFromTemplate([ 22 | { 23 | label: 'Example', 24 | submenu: [ 25 | { 26 | label: 'About This App', 27 | click: () => 28 | openAboutWindow({ 29 | icon_path: join(__dirname, 'icon.png'), 30 | copyright: 'Copyright (c) 2015 rhysd', 31 | package_json_dir: __dirname, 32 | open_devtools: process.env.NODE_ENV !== 'production', 33 | }), 34 | }, 35 | { 36 | label: 'About This App (custom version entry)', 37 | click: () => 38 | openAboutWindow({ 39 | icon_path: join(__dirname, 'icon.png'), 40 | copyright: 'Copyright (c) 2015 rhysd', 41 | package_json_dir: __dirname, 42 | use_version_info: [ 43 | ['my version entry 1', 'a.b.c'], 44 | ['my version entry 2', 'x.y.z'], 45 | ], 46 | }), 47 | }, 48 | { 49 | label: 'About This App (modal with close)', 50 | click: () => 51 | openAboutWindow({ 52 | icon_path: join(__dirname, 'icon.png'), 53 | copyright: 'Copyright (c) 2015 rhysd', 54 | package_json_dir: __dirname, 55 | win_options: { 56 | parent: w, 57 | modal: true, 58 | }, 59 | show_close_button: 'Close', 60 | }), 61 | }, 62 | { 63 | role: 'quit', 64 | }, 65 | ], 66 | }, 67 | ]); 68 | app.applicationMenu = menu; 69 | }); 70 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-about-window-example", 3 | "productName": "Example for about-window", 4 | "version": "1.0.0", 5 | "description": "Example for about-window npm package", 6 | "homepage": "https://github.com/rhysd/electron-about-window#readme", 7 | "bugs": { 8 | "url": "https://github.com/rhysd/electron-about-window/issues" 9 | }, 10 | "main": "main.js", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "author": "rhysd ", 15 | "license": "MIT" 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "about-window", 3 | "version": "1.15.2", 4 | "description": "'About App' window for Electron application", 5 | "main": "src/index.js", 6 | "typings": "src/index.d.ts", 7 | "scripts": { 8 | "dep": "npm install", 9 | "build": "tsc -p src/", 10 | "watch": "tsc --watch -p src/", 11 | "tslint": "tslint -p ./src", 12 | "stylelint": "stylelint styles/*.css", 13 | "check-formatting": "prettier --check '**/*.ts'", 14 | "lint": "npm run tslint && npm run stylelint && npm run check-formatting", 15 | "prettier": "prettier --write '**/*.ts'", 16 | "example": "NODE_ENV=production electron ./example", 17 | "debug": "electron ./example", 18 | "start": "npm run dep && npm run build && npm run example", 19 | "preversion": "npm run lint && npm run build" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/rhysd/electron-about-window.git" 24 | }, 25 | "keywords": [ 26 | "Electron", 27 | "electron-component", 28 | "about", 29 | "window" 30 | ], 31 | "author": "rhysd ", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/rhysd/electron-about-window/issues" 35 | }, 36 | "homepage": "https://github.com/rhysd/electron-about-window#readme", 37 | "devDependencies": { 38 | "@types/node": "^16.x", 39 | "electron": "^15.1.0", 40 | "prettier": "^2.4.1", 41 | "stylelint": "^13.13.1", 42 | "stylelint-config-standard": "^22.0.0", 43 | "tslint": "^6.1.2", 44 | "typescript": "^4.4.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { app as appMain, BrowserWindow as BrowserWindowMain, shell, ipcMain } from 'electron'; 2 | import { statSync } from 'fs'; 3 | import * as path from 'path'; 4 | 5 | export interface LicenseEntry { 6 | type: string; 7 | url: string; 8 | } 9 | 10 | export interface PackageJson { 11 | productName?: string; 12 | description?: string; 13 | homepage?: string; 14 | license?: string | LicenseEntry; 15 | bugs?: { 16 | url: string; 17 | }; 18 | } 19 | 20 | export interface AboutWindowInfo { 21 | icon_path: string; 22 | product_name?: string; 23 | copyright?: string; 24 | homepage?: string; 25 | description?: string; 26 | package_json_dir?: string; 27 | about_page_dir?: string; 28 | license?: string; 29 | bug_report_url?: string; 30 | css_path?: string | string[]; 31 | adjust_window_size?: boolean; 32 | win_options?: Electron.BrowserWindowConstructorOptions; 33 | open_devtools?: boolean; 34 | use_inner_html?: boolean; 35 | bug_link_text?: string; 36 | use_version_info?: boolean | [string, string][]; 37 | show_close_button?: string; 38 | app?: Electron.App; 39 | BrowserWindow?: typeof Electron.BrowserWindow; 40 | ipcMain?: Electron.IpcMain; 41 | } 42 | 43 | declare namespace NodeJS { 44 | interface ProcessVersions { 45 | [name: string]: string; 46 | } 47 | } 48 | 49 | function loadPackageJson(pkg_path: string): PackageJson { 50 | try { 51 | return require(pkg_path); 52 | } catch (e) { 53 | return null; 54 | } 55 | } 56 | 57 | function detectPackageJson(specified_dir: string, app: Electron.App) { 58 | if (specified_dir) { 59 | const pkg = loadPackageJson(path.join(specified_dir, 'package.json')); 60 | if (pkg !== null) { 61 | return pkg; 62 | } else { 63 | console.warn('about-window: package.json is not found in specified directory path: ' + specified_dir); 64 | } 65 | } 66 | 67 | // Note: app.getName() was replaced with app.name at Electron v7 68 | const app_name = app.name || app.getName(); 69 | 70 | for (const mod_path of (module as any).paths) { 71 | if (!path.isAbsolute(mod_path)) { 72 | continue; 73 | } 74 | 75 | const p = path.join(mod_path, '..', 'package.json'); 76 | try { 77 | const stats = statSync(p); 78 | if (stats.isFile()) { 79 | const pkg = loadPackageJson(p); 80 | if (pkg !== null && pkg.productName === app_name) { 81 | return pkg; 82 | } 83 | } 84 | } catch (e) { 85 | // File not found. Ignored. 86 | } 87 | } 88 | 89 | // Note: Not found. 90 | return null; 91 | } 92 | 93 | function injectInfoFromPackageJson(info: AboutWindowInfo, app: Electron.App) { 94 | const pkg = detectPackageJson(info.package_json_dir, app); 95 | if (pkg === null) { 96 | // Note: Give up. 97 | return info; 98 | } 99 | 100 | if (!info.product_name) { 101 | info.product_name = pkg.productName; 102 | } 103 | if (!info.description) { 104 | info.description = pkg.description; 105 | } 106 | if (!info.license && pkg.license) { 107 | const l = pkg.license; 108 | info.license = typeof l === 'string' ? l : l.type; 109 | } 110 | if (!info.homepage) { 111 | info.homepage = pkg.homepage; 112 | } 113 | if (!info.bug_report_url && typeof pkg.bugs === 'object') { 114 | info.bug_report_url = pkg.bugs.url; 115 | } 116 | if (info.use_inner_html === undefined) { 117 | info.use_inner_html = false; 118 | } 119 | if (info.use_version_info === undefined) { 120 | info.use_version_info = true; 121 | } 122 | 123 | return info; 124 | } 125 | 126 | function normalizeParam(info_or_img_path: AboutWindowInfo | string | undefined | null): AboutWindowInfo { 127 | if (!info_or_img_path) { 128 | throw new Error( 129 | 'First parameter of openAboutWindow() must not be empty. Please see the document: https://github.com/rhysd/electron-about-window/blob/master/README.md', 130 | ); 131 | } 132 | 133 | if (typeof info_or_img_path === 'string') { 134 | return { icon_path: info_or_img_path }; 135 | } else { 136 | const info = info_or_img_path; 137 | if (!info.icon_path) { 138 | throw new Error( 139 | "First parameter of openAboutWindow() must have key 'icon_path'. Please see the document: https://github.com/rhysd/electron-about-window/blob/master/README.md", 140 | ); 141 | } 142 | return { ...info }; 143 | } 144 | } 145 | 146 | export default function openAboutWindow(info_or_img_path: AboutWindowInfo | string) { 147 | let window: Electron.BrowserWindow = null; 148 | let info = normalizeParam(info_or_img_path); 149 | 150 | const ipc = ipcMain ?? info.ipcMain; 151 | const app = appMain ?? info.app; 152 | const BrowserWindow = BrowserWindowMain ?? info.BrowserWindow; 153 | if (!app || !BrowserWindow || !ipc) { 154 | throw new Error( 155 | "openAboutWindow() is called on non-main process. Set 'app', 'BrowserWindow' and 'ipcMain' properties in the 'info' argument of the function call", 156 | ); 157 | } 158 | 159 | if (window !== null) { 160 | window.focus(); 161 | return window; 162 | } 163 | 164 | let base_path = info.about_page_dir; 165 | 166 | if (base_path === undefined || base_path === null || !base_path.length) { 167 | base_path = path.join(__dirname, '..'); 168 | } 169 | 170 | const index_html = 'file://' + path.join(base_path, 'about.html'); 171 | 172 | const options = Object.assign( 173 | { 174 | width: 400, 175 | height: 400, 176 | useContentSize: true, 177 | titleBarStyle: 'hidden-inset', 178 | show: !info.adjust_window_size, 179 | icon: info.icon_path, 180 | webPreferences: { 181 | // For security reasons, nodeIntegration is no longer true by default when using Electron v5 or later 182 | // nodeIntegration can be safely enabled as long as the window source is not remote 183 | nodeIntegration: true, 184 | // From Electron v12, this option is set to true by default 185 | contextIsolation: false, 186 | }, 187 | }, 188 | info.win_options || {}, 189 | ); 190 | 191 | window = new BrowserWindow(options); 192 | 193 | const on_win_adjust_req = (_: unknown, width: number, height: number, show_close_button: boolean) => { 194 | if (height > 0 && width > 0) { 195 | // Note: 196 | // Add 30px(= about 2em) to add padding in window, if there is a close button, bit more 197 | if (show_close_button) { 198 | window.setContentSize(width, height + 40); 199 | } else { 200 | window.setContentSize(width, height + 52); 201 | } 202 | } 203 | }; 204 | const on_win_close_req = () => { 205 | window.close(); 206 | }; 207 | ipc.on('about-window:adjust-window-size', on_win_adjust_req); 208 | ipc.on('about-window:close-window', on_win_close_req); 209 | 210 | window.once('closed', () => { 211 | window = null; 212 | ipc.removeListener('about-window:adjust-window-size', on_win_adjust_req); 213 | ipc.removeListener('about-window:close-window', on_win_close_req); 214 | }); 215 | window.loadURL(index_html); 216 | 217 | window.webContents.on('will-navigate', (e, url) => { 218 | e.preventDefault(); 219 | shell.openExternal(url); 220 | }); 221 | window.webContents.on('new-window', (e, url) => { 222 | e.preventDefault(); 223 | shell.openExternal(url); 224 | }); 225 | 226 | window.webContents.once('dom-ready', () => { 227 | const win_title = info.win_options ? info.win_options.title : null; 228 | delete info.win_options; 229 | info.win_options = { title: win_title }; 230 | const app_name = info.product_name || app.name || app.getName(); 231 | const version = app.getVersion(); 232 | window.webContents.send('about-window:info', info, app_name, version); 233 | if (info.open_devtools) { 234 | if (process.versions.electron >= '1.4') { 235 | window.webContents.openDevTools({ mode: 'detach' }); 236 | } else { 237 | window.webContents.openDevTools(); 238 | } 239 | } 240 | }); 241 | 242 | window.once('ready-to-show', () => { 243 | window.show(); 244 | }); 245 | 246 | window.setMenu(null); 247 | 248 | info = injectInfoFromPackageJson(info, app); 249 | 250 | return window; 251 | } 252 | -------------------------------------------------------------------------------- /src/renderer.ts: -------------------------------------------------------------------------------- 1 | import { ipcRenderer, shell } from 'electron'; 2 | import type { AboutWindowInfo } from './index'; 3 | 4 | ipcRenderer.on('about-window:info', (_: any, info: AboutWindowInfo, app_name: string, version: string) => { 5 | // Note: app.getName() was replaced with app.name at Electron v7 6 | const open_home = () => shell.openExternal(info.homepage); 7 | const content = info.use_inner_html ? 'innerHTML' : 'innerText'; 8 | document.title = info.win_options.title || `About ${app_name}`; 9 | 10 | const title_elem = document.querySelector('.title') as HTMLHeadingElement; 11 | title_elem.innerText = `${app_name} ${version}`; 12 | 13 | if (info.homepage) { 14 | title_elem.addEventListener('click', open_home); 15 | title_elem.classList.add('clickable'); 16 | const logo_elem = document.querySelector('.logo'); 17 | logo_elem.addEventListener('click', open_home); 18 | logo_elem.classList.add('clickable'); 19 | } 20 | 21 | const copyright_elem = document.querySelector('.copyright') as any; 22 | if (info.copyright) { 23 | copyright_elem[content] = info.copyright; 24 | } else if (info.license) { 25 | copyright_elem[content] = `Distributed under ${info.license} license.`; 26 | } 27 | 28 | const icon_elem = document.getElementById('app-icon') as HTMLImageElement; 29 | icon_elem.src = info.icon_path; 30 | 31 | if (info.description) { 32 | const desc_elem = document.querySelector('.description') as any; 33 | desc_elem[content] = info.description; 34 | } 35 | 36 | if (info.bug_report_url) { 37 | const bug_report = document.querySelector('.bug-report-link') as HTMLDivElement; 38 | bug_report.innerText = info.bug_link_text || 'Report an issue'; 39 | bug_report.addEventListener('click', e => { 40 | e.preventDefault(); 41 | shell.openExternal(info.bug_report_url); 42 | }); 43 | } 44 | 45 | if (info.css_path) { 46 | const css_paths = !Array.isArray(info.css_path) ? [info.css_path] : info.css_path; 47 | for (const css_path of css_paths) { 48 | const link = document.createElement('link'); 49 | link.rel = 'stylesheet'; 50 | link.href = css_path; 51 | document.head.appendChild(link); 52 | } 53 | } 54 | 55 | if (info.adjust_window_size) { 56 | const height = document.body.scrollHeight; 57 | const width = document.body.scrollWidth; 58 | ipcRenderer.send('about-window:adjust-window-size', height, width, !!info.show_close_button); 59 | } 60 | 61 | if (!!info.use_version_info) { 62 | const versions = document.querySelector('.versions'); 63 | const version_info: [string, string][] = Array.isArray(info.use_version_info) 64 | ? info.use_version_info 65 | : ['electron', 'chrome', 'node', 'v8'].map(e => [e, process.versions[e]]); 66 | for (const [name, value] of version_info) { 67 | const tr = document.createElement('tr'); 68 | const name_td = document.createElement('td'); 69 | name_td.innerText = name; 70 | tr.appendChild(name_td); 71 | const version_td = document.createElement('td'); 72 | version_td.innerText = ' : ' + value; 73 | tr.appendChild(version_td); 74 | versions.appendChild(tr); 75 | } 76 | } 77 | 78 | if (info.show_close_button) { 79 | const buttons = document.querySelector('.buttons'); 80 | const close_button = document.createElement('button'); 81 | close_button.innerText = info.show_close_button; 82 | close_button.addEventListener('click', e => { 83 | e.preventDefault(); 84 | ipcRenderer.send('about-window:close-window'); 85 | }); 86 | buttons.appendChild(close_button); 87 | close_button.focus(); 88 | } 89 | }); 90 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "removeComments": true, 6 | "preserveConstEnums": true, 7 | "noImplicitAny": true, 8 | "noImplicitReturns": true, 9 | "noImplicitThis": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "noEmitOnError": true, 13 | "strictNullChecks": false, 14 | "target": "es2015", 15 | "sourceMap": true, 16 | "declaration": true 17 | }, 18 | "include": [ 19 | "**/*.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /styles/ui.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | width: 100%; 4 | height: 100%; 5 | -webkit-user-select: none; 6 | user-select: none; 7 | -webkit-app-region: drag; 8 | } 9 | 10 | body { 11 | margin: 0; 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | align-items: center; 16 | color: #333; 17 | background-color: #eee; 18 | font-size: 12px; 19 | font-family: 'Helvetica', 'Arial', 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', 'メイリオ', Meiryo, 'MS Pゴシック', 'MS PGothic', sans-serif; 20 | } 21 | 22 | .logo { 23 | width: 200px; 24 | -webkit-user-select: none; 25 | user-select: none; 26 | } 27 | 28 | .title, 29 | .copyright, 30 | .description { 31 | margin: 0.2em; 32 | } 33 | 34 | .clickable { 35 | cursor: pointer; 36 | } 37 | 38 | .description { 39 | margin-bottom: 1em; 40 | text-align: center; 41 | } 42 | 43 | .versions { 44 | border-collapse: collapse; 45 | margin-top: 1em; 46 | } 47 | 48 | .copyright, 49 | .versions { 50 | color: #999; 51 | } 52 | 53 | .buttons { 54 | margin-bottom: 1em; 55 | text-align: center; 56 | } 57 | 58 | .buttons button { 59 | margin-top: 1em; 60 | width: 100px; 61 | height: 24px; 62 | } 63 | 64 | .link { 65 | cursor: pointer; 66 | color: #80a0c2; 67 | } 68 | 69 | .bug-report-link { 70 | position: absolute; 71 | right: 0.5em; 72 | bottom: 0.5em; 73 | } 74 | 75 | .clickable, 76 | .bug-report-link, 77 | .buttons button { 78 | -webkit-app-region: no-drag; 79 | } 80 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended" 4 | ], 5 | "rules": { 6 | "align": [ 7 | true, 8 | "parameters", 9 | "statements" 10 | ], 11 | "ban": false, 12 | "class-name": true, 13 | "comment-format": [ 14 | true, 15 | "check-space" 16 | ], 17 | "curly": true, 18 | "eofline": true, 19 | "forin": false, 20 | "indent": [ 21 | true, 22 | "spaces" 23 | ], 24 | "interface-name": false, 25 | "jsdoc-format": true, 26 | "label-position": true, 27 | "max-line-length": false, 28 | "member-access": false, 29 | "no-any": false, 30 | "no-arg": true, 31 | "no-bitwise": false, 32 | "no-conditional-assignment": true, 33 | "no-consecutive-blank-lines": false, 34 | "no-console": [ 35 | true, 36 | "debug", 37 | "info", 38 | "time", 39 | "timeEnd", 40 | "trace" 41 | ], 42 | "no-construct": true, 43 | "no-constructor-vars": false, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": true, 47 | "no-eval": true, 48 | "no-inferrable-types": false, 49 | "no-internal-module": true, 50 | "no-require-imports": false, 51 | "no-shadowed-variable": true, 52 | "no-string-literal": true, 53 | "no-switch-case-fall-through": false, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-unsafe-finally": true, 57 | "no-var-keyword": true, 58 | "no-var-requires": true, 59 | "object-literal-sort-keys": false, 60 | "object-literal-key-quotes": [ 61 | true, 62 | "as-needed" 63 | ], 64 | "one-line": [ 65 | true, 66 | "check-open-brace", 67 | "check-catch", 68 | "check-else", 69 | "check-whitespace" 70 | ], 71 | "quotemark": [ 72 | true, 73 | "single", 74 | "avoid-escape", 75 | "jsx-double" 76 | ], 77 | "radix": true, 78 | "semicolon": true, 79 | "switch-default": true, 80 | "trailing-comma": [ 81 | true, 82 | { 83 | "multiline": "always", 84 | "singleline": "never" 85 | } 86 | ], 87 | "triple-equals": [ 88 | true, 89 | "allow-null-check" 90 | ], 91 | "typedef": [ 92 | false 93 | ], 94 | "typedef-whitespace": [ 95 | true, 96 | { 97 | "call-signature": "nospace", 98 | "index-signature": "nospace", 99 | "parameter": "nospace", 100 | "property-declaration": "nospace", 101 | "variable-declaration": "nospace" 102 | } 103 | ], 104 | "use-strict": false, 105 | "variable-name": [ 106 | false, 107 | "check-format", 108 | "allow-leading-underscore", 109 | "ban-keywords" 110 | ], 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-namespace": false, 120 | "arrow-parens": false 121 | } 122 | } 123 | --------------------------------------------------------------------------------