├── .github ├── ISSUE_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── lint.yml │ └── test.yml ├── .gitignore ├── .nvmrc ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── COLLABORATORS.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── WORKING_PLATFORMS.md ├── assets ├── Icon.icns ├── IconTemplate.png ├── IconTemplate@2x.png ├── screenshot-linux.png ├── screenshot-macos-dark.png └── screenshot-windows.png ├── biome.json ├── docs ├── README.md ├── classes │ └── _menubar_.menubar.md ├── globals.md ├── interfaces │ └── _types_.options.md └── modules │ ├── _index_.md │ ├── _menubar_.md │ ├── _types_.md │ └── _util_getwindowposition_.md ├── examples ├── arrow │ ├── README.md │ ├── index.css │ ├── index.html │ ├── main.js │ ├── package.json │ └── screenshot.png ├── hello-world │ ├── README.md │ ├── index.html │ ├── main.js │ ├── package.json │ └── screenshot.png ├── icon-animation │ ├── index.html │ ├── main.js │ ├── package.json │ ├── state-ok-20.png │ ├── state-sync-20-120.png │ ├── state-sync-20-60.png │ └── state-sync-20.png ├── native-menu │ ├── README.md │ ├── main.js │ ├── package.json │ └── screenshot.png ├── package.json └── yarn.lock ├── jest.config.js ├── package.json ├── src ├── Menubar.spec.ts ├── Menubar.ts ├── __mocks__ │ └── electron.ts ├── ambient.d.ts ├── index.ts ├── types.ts └── util │ ├── cleanOptions.spec.ts │ ├── cleanOptions.ts │ └── getWindowPosition.ts ├── tsconfig.json ├── typedoc.js └── yarn.lock /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | [//]: # "Hey there and thank you for using Menubar's issue tracker!" 2 | [//]: # "Please fill in these information to describe your issue." 3 | 4 | ### Description 5 | 6 | ### Steps to Reproduce the Problem 7 | 8 | 1. ... 9 | 2. ... 10 | 3. ... 11 | 12 | ### Expected Behaviour 13 | 14 | ### Actual Behaviour 15 | 16 | ### Specifications 17 | 18 | - Menubar version: 19 | - Platform: 20 | - Electron version: (Run `electron -v` or `node_modules/.bin/electron -v` to get it) 21 | 22 | ### Other information 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | time: "02:00" 8 | timezone: Europe/Berlin 9 | open-pull-requests-limit: 10 10 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Linting 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | lint: 11 | name: Lint 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-node@v4 17 | with: 18 | node-version-file: '.nvmrc' 19 | - run: yarn install 20 | - run: yarn lint 21 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | lint: 11 | name: Run Tests 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-node@v4 17 | with: 18 | node-version-file: '.nvmrc' 19 | - run: yarn install 20 | - run: yarn test 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | lib 5 | dist 6 | *.app 7 | *.log 8 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["biomejs.biome"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": "explicit", 4 | "source.organizeImports.biome": "explicit", 5 | "quickfix.biome": "explicit" 6 | }, 7 | "editor.defaultFormatter": "biomejs.biome" 8 | } 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [9.5.1](https://github.com/maxogden/menubar/compare/v9.5.0...v9.5.1) (2024-10-11) 6 | 7 | ## [9.5.0](https://github.com/maxogden/menubar/compare/v9.4.0...v9.5.0) (2024-06-25) 8 | 9 | 10 | ### Features 11 | 12 | * widen electron support ([#473](https://github.com/maxogden/menubar/issues/473)) ([bd3beb2](https://github.com/maxogden/menubar/commit/bd3beb2c2bc9700f4738a4e0f25085aa8a0f5e86)) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * Fix for Broken Native Menubar Example ([#471](https://github.com/maxogden/menubar/issues/471)) ([b000c86](https://github.com/maxogden/menubar/commit/b000c86777cb0cf6fa883fb0256410981e154ce1)) 18 | * win32 position ([#479](https://github.com/maxogden/menubar/issues/479)) ([9e5cb86](https://github.com/maxogden/menubar/commit/9e5cb86a276f29efd36d10a07e27fa820343f880)) 19 | 20 | ## [9.4.0](https://github.com/maxogden/menubar/compare/v9.3.0...v9.4.0) (2023-11-28) 21 | 22 | 23 | ### Features 24 | 25 | * Update electron to 27 ([#458](https://github.com/maxogden/menubar/issues/458)) ([f3dbac6](https://github.com/maxogden/menubar/commit/f3dbac66582baa322c2c8caa5027f1b8ef480e67)) 26 | 27 | 28 | ### Bug Fixes 29 | 30 | * Avoid setVisibleOnAllWorkspaces from destroying the original visible state of app.dock ([#442](https://github.com/maxogden/menubar/issues/442)) ([d8df2ab](https://github.com/maxogden/menubar/commit/d8df2ab4b65564c8ec58dc7156128bfd53fab2c9)) 31 | 32 | ## [9.3.0](https://github.com/maxogden/menubar/compare/v9.2.3...v9.3.0) (2023-02-13) 33 | 34 | 35 | ### Features 36 | 37 | * add electron 22 support ([#414](https://github.com/maxogden/menubar/issues/414)) ([a39382f](https://github.com/maxogden/menubar/commit/a39382fe843953395bf99267ee83102def70b06e)) 38 | 39 | ### [9.2.3](https://github.com/maxogden/menubar/compare/v9.2.2...v9.2.3) (2022-10-05) 40 | 41 | 42 | ### Features 43 | 44 | * electron 21 support ([#382](https://github.com/maxogden/menubar/issues/382)) ([c9d5b61](https://github.com/maxogden/menubar/commit/c9d5b61da2b55e20e9b2d0f4c0181c57f33a27c3)) 45 | 46 | ### [9.2.2](https://github.com/maxogden/menubar/compare/v9.2.1...v9.2.2) (2022-09-05) 47 | 48 | 49 | ### Features 50 | 51 | * Add Electron 20 as a peerDependency ([#379](https://github.com/maxogden/menubar/issues/379)) ([4c027b2](https://github.com/maxogden/menubar/commit/4c027b22499c7c83a4d87b9aa17d5e86f3172b68)) 52 | 53 | 54 | ### Bug Fixes 55 | 56 | * arrow example problem ([#342](https://github.com/maxogden/menubar/issues/342)) ([64b80d5](https://github.com/maxogden/menubar/commit/64b80d5f49ebb367b75e21e88af15fdc874cda86)) 57 | 58 | ### [9.2.1](https://github.com/maxogden/menubar/compare/v9.2.0...v9.2.1) (2022-07-11) 59 | 60 | 61 | ### Bug Fixes 62 | 63 | * Add support for electron 19 ([#376](https://github.com/maxogden/menubar/issues/376)) ([6d0feb0](https://github.com/maxogden/menubar/commit/6d0feb0e492681200966dde10a63a73cfb503138)) 64 | 65 | ## [9.2.0](https://github.com/maxogden/menubar/compare/v9.1.2...v9.2.0) (2022-04-11) 66 | 67 | 68 | ### Features 69 | 70 | * add `before-load` event ([#370](https://github.com/maxogden/menubar/issues/370)) ([3247986](https://github.com/maxogden/menubar/commit/3247986f164d8b2fb2912ab33beb25c9c6d8ece3)) 71 | 72 | ### [9.1.2](https://github.com/maxogden/menubar/compare/v9.1.1...v9.1.2) (2022-03-29) 73 | 74 | 75 | ### Bug Fixes 76 | 77 | * Add electron 17 support ([#373](https://github.com/maxogden/menubar/issues/373)) ([fafc29f](https://github.com/maxogden/menubar/commit/fafc29f168e91369ad5d60a61abc0363076c303a)) 78 | 79 | ### [9.1.1](https://github.com/maxogden/menubar/compare/v9.1.0...v9.1.1) (2021-12-09) 80 | 81 | 82 | ### Bug Fixes 83 | 84 | * Add electron 16 support ([#364](https://github.com/maxogden/menubar/issues/364)) ([325e151](https://github.com/maxogden/menubar/commit/325e1517cfc407bc0ccc98be776d8d9590d6f119)) 85 | 86 | ## [9.1.0](https://github.com/maxogden/menubar/compare/v9.0.6...v9.1.0) (2021-10-27) 87 | 88 | 89 | ### Features 90 | 91 | * add option activateWithApp to allow not activate with this event ([#361](https://github.com/maxogden/menubar/issues/361)) ([8384bf2](https://github.com/maxogden/menubar/commit/8384bf24abe6138aded26fcd7bd6d1d7a325f319)) 92 | 93 | ### [9.0.6](https://github.com/maxogden/menubar/compare/v9.0.5...v9.0.6) (2021-10-15) 94 | 95 | 96 | ### Bug Fixes 97 | 98 | * Add support for electron 14 and 15 ([#358](https://github.com/maxogden/menubar/issues/358)) ([8eaba8c](https://github.com/maxogden/menubar/commit/8eaba8cdfcd3ce9427ee3ffd65b3426c62f65048)) 99 | 100 | ### [9.0.5](https://github.com/maxogden/menubar/compare/v9.0.4...v9.0.5) (2021-06-28) 101 | 102 | 103 | ### Bug Fixes 104 | 105 | * Add support for Electron 13 ([#347](https://github.com/maxogden/menubar/issues/347)) ([fbf07bd](https://github.com/maxogden/menubar/commit/fbf07bd0bd24b2aac26cdd1db61eb55924f3ee63)) 106 | * window position on linux & windows when taskbar is on the left ([#343](https://github.com/maxogden/menubar/issues/343)) ([5d8e0c8](https://github.com/maxogden/menubar/commit/5d8e0c89996f67b58f059ec767a87db104a90292)) 107 | 108 | ### [9.0.4](https://github.com/maxogden/menubar/compare/v9.0.3...v9.0.4) (2021-05-03) 109 | 110 | 111 | ### Bug Fixes 112 | 113 | * Add support for Electron 12 ([#332](https://github.com/maxogden/menubar/issues/332)) ([c1f055d](https://github.com/maxogden/menubar/commit/c1f055daed76be2d0f408fda5d4835defcd59dcc)) 114 | 115 | ### [9.0.3](https://github.com/maxogden/menubar/compare/v9.0.2...v9.0.3) (2021-02-24) 116 | 117 | 118 | ### Bug Fixes 119 | 120 | * Add support for Electron 10 and 11 ([#321](https://github.com/maxogden/menubar/issues/321)) ([4a89656](https://github.com/maxogden/menubar/commit/4a8965628a0a1a7a14602fef3add7bef436a508f)) 121 | 122 | ### [9.0.2](https://github.com/maxogden/menubar/compare/v9.0.1...v9.0.2) (2021-01-20) 123 | 124 | 125 | ### Bug Fixes 126 | 127 | * Improve 'windows' OS detection of taskbar location ([#307](https://github.com/maxogden/menubar/issues/307)) ([4726584](https://github.com/maxogden/menubar/commit/4726584664148a57656c40872836ebba2d030980)) 128 | 129 | ### [9.0.1](https://github.com/maxogden/menubar/compare/v9.0.0...v9.0.1) (2020-05-28) 130 | 131 | 132 | ### Bug Fixes 133 | 134 | * calling showWindow() prevents menubar window from closing ([#287](https://github.com/maxogden/menubar/issues/287)) ([53d8f82](https://github.com/maxogden/menubar/commit/53d8f82b604ad5555f59108a97234ebf32e43f80)) 135 | 136 | ## [9.0.0](https://github.com/maxogden/menubar/compare/v8.0.2...v9.0.0) (2020-05-27) 137 | 138 | 139 | ### ⚠ BREAKING CHANGES 140 | 141 | * Please use Electron 9 with this menubar version. 142 | 143 | ### Features 144 | 145 | * Support Electron 9 ([#286](https://github.com/maxogden/menubar/issues/286)) ([44cf1b1](https://github.com/maxogden/menubar/commit/44cf1b1e6cce83f9e63a39f1d32fbb664396e7bc)) 146 | 147 | ### [8.0.2](https://github.com/maxogden/menubar/compare/v8.0.1...v8.0.2) (2020-04-27) 148 | 149 | 150 | ### Bug Fixes 151 | 152 | * Show window on dock icon click ([#279](https://github.com/maxogden/menubar/issues/279)) ([a8607fa](https://github.com/maxogden/menubar/commit/a8607fa708d229d9124471127482fe461198f1f3)) 153 | * update prevent flicker on Windows (fixes [#274](https://github.com/maxogden/menubar/issues/274)) ([#276](https://github.com/maxogden/menubar/issues/276)) ([9592f34](https://github.com/maxogden/menubar/commit/9592f3437ce3660b6464f5b436ed111291eb75d3)) 154 | 155 | ### [8.0.1](https://github.com/maxogden/menubar/compare/v8.0.0...v8.0.1) (2020-03-14) 156 | 157 | 158 | ### Bug Fixes 159 | 160 | * **deps:** [Security] bump acorn from 6.1.1 to 6.4.1 ([#272](https://github.com/maxogden/menubar/issues/272)) ([1332b77](https://github.com/maxogden/menubar/commit/1332b774372de69c04e2a098833ae35775c35cad)) 161 | 162 | ## [8.0.0](https://github.com/maxogden/menubar/compare/v7.2.0...v8.0.0) (2020-02-10) 163 | 164 | 165 | ### ⚠ BREAKING CHANGES 166 | 167 | * Menubar's recommended peer dependency is `electron@^8.0.0` 168 | 169 | ### Features 170 | 171 | * Support Electron 8 ([#268](https://github.com/maxogden/menubar/issues/268)) ([ad99c5a](https://github.com/maxogden/menubar/commit/ad99c5add02ab6d0d751cf6bda8a2c96c674620f)) 172 | 173 | ## [7.2.0](https://github.com/maxogden/menubar/compare/v7.1.0...v7.2.0) (2020-01-16) 174 | 175 | 176 | ### Features 177 | 178 | * Adding a loadUrlOptions option ([#263](https://github.com/maxogden/menubar/issues/263)) ([8e6bd01](https://github.com/maxogden/menubar/commit/8e6bd0154aaee02a5d601bbe37c51c55065c3923)) 179 | 180 | ## [7.1.0](https://github.com/maxogden/menubar/compare/v7.0.0...v7.1.0) (2019-11-25) 181 | 182 | 183 | ### Features 184 | 185 | * Allow skipping loadUrl ([#257](https://github.com/maxogden/menubar/issues/257)) ([095486a](https://github.com/maxogden/menubar/commit/095486ab338df26fc4d6a1e7a658cfa9fa4a69b7)) 186 | 187 | # [7.0.0](https://github.com/maxogden/menubar/compare/v6.0.8...v7.0.0) (2019-10-23) 188 | 189 | 190 | * feat!: Support Electron 7 (#250) ([b54dce5](https://github.com/maxogden/menubar/commit/b54dce5)), closes [#250](https://github.com/maxogden/menubar/issues/250) 191 | 192 | 193 | ### BREAKING CHANGES 194 | 195 | * - Drop support for Electron 4, 5, and 6. 196 | - Remove deprecated passing string argument to `menubar`, use `dir` field instead 197 | ```diff 198 | - menubar('/home/me/MY_ABSOLUTE_PATH'); 199 | + menubar({ dir: '/home/me/MY_ABSOLUTE_PATH' }); 200 | ``` 201 | - Remove deprecated passing `x`, `y`, `height`, `width`, `alwaysOnTop` fields to `menubar`, pass them instead into the `browserWindow` field 202 | ```diff 203 | - menubar({ 204 | - x: 12, 205 | - y: 34, 206 | - height: 500, 207 | - width: 320, 208 | - alwaysOnTop: true 209 | - }); 210 | + menubar({ 211 | + browserWindow: { 212 | + x: 12, 213 | + y: 34, 214 | + height: 500, 215 | + width: 320, 216 | + alwaysOnTop: true 217 | + } 218 | + }); 219 | ``` 220 | 221 | 222 | 223 | ## [6.0.8](https://github.com/maxogden/menubar/compare/v6.0.7...v6.0.8) (2019-09-16) 224 | 225 | 226 | ### Bug Fixes 227 | 228 | * Move doc tool to `devDependencies` ([#245](https://github.com/maxogden/menubar/issues/245)) ([2756c7a](https://github.com/maxogden/menubar/commit/2756c7a)) 229 | 230 | 231 | 232 | ## [6.0.7](https://github.com/maxogden/menubar/compare/v6.0.6...v6.0.7) (2019-07-31) 233 | 234 | 235 | ### Bug Fixes 236 | 237 | * Support Electron 6 ([#242](https://github.com/maxogden/menubar/issues/242)) ([1fd9bd7](https://github.com/maxogden/menubar/commit/1fd9bd7)) 238 | 239 | 240 | 241 | ## [6.0.6](https://github.com/maxogden/menubar/compare/v6.0.5...v6.0.6) (2019-07-02) 242 | 243 | 244 | ### Bug Fixes 245 | 246 | * Fix crash on windows position ([#235](https://github.com/maxogden/menubar/issues/235)) ([cbbe175](https://github.com/maxogden/menubar/commit/cbbe175)) 247 | 248 | 249 | 250 | ## [6.0.5](https://github.com/maxogden/menubar/compare/v6.0.4...v6.0.5) (2019-06-11) 251 | 252 | 253 | ### Bug Fixes 254 | 255 | * Remove postinstall, export taskbarLocation ([#226](https://github.com/maxogden/menubar/issues/226)) ([941b3be](https://github.com/maxogden/menubar/commit/941b3be)) 256 | 257 | 258 | 259 | ## [6.0.4](https://github.com/maxogden/menubar/compare/v6.0.3...v6.0.4) (2019-06-11) 260 | 261 | 262 | ### Bug Fixes 263 | 264 | * Correct position on Windows & multi-taskbar ([#217](https://github.com/maxogden/menubar/issues/217)) ([4f29fe2](https://github.com/maxogden/menubar/commit/4f29fe2)), closes [#196](https://github.com/maxogden/menubar/issues/196) 265 | * Fix double 'after-hide' event ([#216](https://github.com/maxogden/menubar/issues/216)) ([a4d900e](https://github.com/maxogden/menubar/commit/a4d900e)) 266 | 267 | 268 | 269 | ## [6.0.3](https://github.com/maxogden/menubar/compare/v6.0.2...v6.0.3) (2019-06-05) 270 | 271 | 272 | ### Bug Fixes 273 | 274 | * Fix accessing Menubar.window ([#214](https://github.com/maxogden/menubar/issues/214)) ([cd5ef73](https://github.com/maxogden/menubar/commit/cd5ef73)) 275 | 276 | 277 | 278 | ## [6.0.2](https://github.com/maxogden/menubar/compare/v6.0.1...v6.0.2) (2019-05-31) 279 | 280 | 281 | ### Bug Fixes 282 | 283 | * Use cat icon if no icon provided ([#205](https://github.com/maxogden/menubar/issues/205)) ([fc02e02](https://github.com/maxogden/menubar/commit/fc02e02)) 284 | 285 | 286 | 287 | ## [6.0.1](https://github.com/maxogden/menubar/compare/v6.0.0...v6.0.1) (2019-05-31) 288 | 289 | 290 | ### Bug Fixes 291 | 292 | * Fix changelog links ([#204](https://github.com/maxogden/menubar/issues/204)) ([de96756](https://github.com/maxogden/menubar/commit/de96756)) 293 | 294 | 295 | 296 | # [6.0.0](https://github.com/maxogden/menubar/compare/v5.2.3...v6.0.0) (2019-05-31) 297 | 298 | 299 | ### Bug Fixes 300 | 301 | * Update to Electron 5 ([#15](https://github.com/amaurym/g/menubar/issues/15)) ([ce86216](https://github.com/maxogden/menubar/commit/ce86216)) 302 | * Window does not show when already app is ready ([#8](https://github.com/amaurym/g/menubar/issues/8)) ([251fb21](https://github.com/maxogden/menubar/commit/251fb21)) 303 | 304 | 305 | ### Code Refactoring 306 | 307 | * Convert all codebase to typescript ([#2](https://github.com/amaurym/g/menubar/issues/2)) ([820d41a](https://github.com/maxogden/menubar/commit/820d41a)) 308 | 309 | 310 | ### Features 311 | 312 | * Add `browserWindow` field in options, deprecate `height`, `width`, `x`, `y`, `alwaysOnTop` in favor of `browserWindow` ([#18](https://github.com/amaurym/g/menubar/issues/18)) ([0b2d897](https://github.com/maxogden/menubar/commit/0b2d897)) 313 | * Support Electron.NativeImage icon argument ([#7](https://github.com/amaurym/g/menubar/issues/7)) ([03d67f3](https://github.com/maxogden/menubar/commit/03d67f3)) 314 | 315 | 316 | ### BREAKING CHANGES 317 | 318 | * We're using a named export in Typescript now: 319 | ```diff 320 | - var menubar = require('menubar'); 321 | + var { menubar } = require('menubar'); 322 | ``` 323 | 324 | Alternatively, using ES6/TS syntax: 325 | ```javascript 326 | import { menubar } from 'menubar'; 327 | ``` 328 | -------------------------------------------------------------------------------- /COLLABORATORS.md: -------------------------------------------------------------------------------- 1 | ## Collaborators 2 | 3 | menubar is only possible due to the excellent work of the following collaborators: 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
maxogdenGitHub/maxogden
fritzyGitHub/fritzy
pquernaGitHub/pquerna
fabien-dGitHub/fabien-d
jenslindGitHub/jenslind
amaurymGitHub/amaurym
15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Before opening issues 2 | 3 | **If you are asking a question**: 4 | 5 | Remember that `menubar` is just a lightweight wrapper around Electron. Most of the time you can probably find the answer to your question already answered in the [Electron Issue Tracker](https://github.com/electron/electron/issues) 6 | 7 | **For bug reports/technical issues**: 8 | 9 | Please provide the following information when opening issues: 10 | 11 | - Which version of menubar are you using? 12 | - What cli arguments are you passing? 13 | - What platform are you running menubar on? 14 | - Is there a stack trace in the error message you're seeing? 15 | - If possible, please provide instructions to reproduce your problem. 16 | 17 | Thanks! 18 | 19 | ## Releases 20 | 21 | - commit your changes 22 | - `npm version ` 23 | - `git push && git push --tags` (or `git push` with `git config --global push.followTags true` on latest git) 24 | - `npm publish` 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2015-2019, Max Ogden 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/max-mapper/menubar/test.yml) 2 | [![npm (scoped)](https://img.shields.io/npm/v/menubar.svg)](https://www.npmjs.com/package/menubar) 3 | ![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/max-mapper/menubar) 4 | ![npm bundle size](https://img.shields.io/bundlephobia/minzip/menubar.svg) 5 | ![npm bundle size](https://img.shields.io/bundlephobia/min/menubar.svg) 6 | 7 |


8 | 9 |

➖ Menubar

10 |

High level way to create menubar desktop applications with Electron.

11 | 12 |


13 | 14 | This module provides boilerplate for setting up a menubar application using Electron. All you have to do is point it at your `index.html` and `menubar` will handle the rest. 15 | 16 | ✅ Only one dependency, and one peer-dependency. 17 | 18 | ✅ Works on macOS, Windows and most Linuxes. See [details](./WORKING_PLATFORMS.md). 19 | 20 | ✅ 💥 [**3.6kB minified + gzipped**](https://bundlephobia.com/result?p=menubar) 💥 21 | 22 | | | | | 23 | | :-----------------------------------------------------------: | :--------------------------------------------------------: | :------------------------------------------------------: | 24 | | macOS Mojave 10.14 | Windows 10 | Ubuntu 18.04 | 25 | 26 | ## Installation 27 | 28 | ```bash 29 | yarn add menubar 30 | ``` 31 | 32 | ## Usage 33 | 34 | Starting with your own new project, run these commands: 35 | 36 | ```bash 37 | $ yarn add menubar 38 | $ touch myApp.js 39 | $ touch index.html 40 | ``` 41 | 42 | Fill `index.html` with some HTML, and `myApp.js` like this: 43 | 44 | ```javascript 45 | const { menubar } = require('menubar'); 46 | 47 | const mb = menubar(); 48 | 49 | mb.on('ready', () => { 50 | console.log('app is ready'); 51 | // your app code here 52 | }); 53 | ``` 54 | 55 | Then use `electron` to run the app: 56 | 57 | ```bash 58 | $ electron myApp.js 59 | ``` 60 | 61 | Alternatively, see [`examples/hello-world`](/examples/hello-world) folder for a simple working example. 62 | 63 | ## `Menubar` Class 64 | 65 | The return value of `menubar()` is a `Menubar` class instance, which has these properties: 66 | 67 | - `app`: the [Electron App](https://electronjs.org/docs/api/app) instance, 68 | - `window`: the [Electron Browser Window](https://electronjs.org/docs/api/browser-window) instance, 69 | - `tray`: the [Electron Tray](https://electronjs.org/docs/api/tray) instance, 70 | - `positioner`: the [Electron Positioner](https://github.com/jenslind/electron-positioner) instance, 71 | - `setOption(option, value)`: change an option after menubar is created, 72 | - `getOption(option)`: get an menubar option, 73 | - `showWindow()`: show the menubar window, 74 | - `hideWindow()`: hide the menubar window 75 | 76 | See the reference [API docs](./docs/classes/_menubar_.menubar.md). 77 | 78 | ## `menubar()` Options 79 | 80 | You can pass an optional options object into the `menubar({ ... })` function: 81 | 82 | - `dir` (default `process.cwd()`) - the app source directory 83 | - `index` (default `file:// + opts.dir + index.html`) - The URL to load the menubar's browserWindow with. The url can be a remote address (e.g. `http://`) or a path to a local HTML file using the `file://` protocol. 84 | - `browserWindow` - BrowserWindow options to be passed to the BrowserWindow constructor, see [Electron docs](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions). Some interesting fields to passed down are: 85 | - `x` (default `undefined`) - the x position of the window 86 | - `y` (default `undefined`) - the y position of the window 87 | - `width` (default 400) - window width 88 | - `height` (default 400) - window height 89 | - `alwaysOnTop` (default false) - if true, the window will not hide on blur 90 | - `icon` (default `opts.dir + IconTemplate.png`) - the png icon to use for the menubar. A good size to start with is 20x20. To support retina, supply a 2x sized image (e.g. 40x40) with `@2x` added to the end of the name, so `icon.png` and `icon@2x.png` and Electron will automatically use your `@2x` version on retina screens. 91 | - `tooltip` (default empty) - menubar tray icon tooltip text 92 | - `tray` (default created on-the-fly) - an electron `Tray` instance. if provided `opts.icon` will be ignored 93 | - `preloadWindow` (default false) - Create [BrowserWindow](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions) instance before it is used -- increasing resource usage, but making the click on the menubar load faster. 94 | - `loadUrlOptions` - (default undefined) The options passed when loading the index URL in the menubar's browserWindow. Everything browserWindow.loadURL supports is supported; this object is simply passed onto [browserWindow.loadURL](https://electronjs.org/docs/api/browser-window#winloadurlurl-options) 95 | - `showOnAllWorkspaces` (default true) - Makes the window available on all OS X workspaces. 96 | - `windowPosition` (default trayCenter and trayBottomCenter on Windows) - Sets the window position (x and y will still override this), check [positioner docs](https://github.com/jenslind/electron-positioner#docs) for valid values. 97 | - `showDockIcon` (default false) - Configure the visibility of the application dock icon. 98 | - `showOnRightClick` (default false) - Show the window on 'right-click' event instead of regular 'click' 99 | 100 | See the reference [API docs](./docs/interfaces/_types_.options.md). 101 | 102 | ## Events 103 | 104 | The `Menubar` class is an event emitter: 105 | 106 | - `ready` - when `menubar`'s tray icon has been created and initialized, i.e. when `menubar` is ready to be used. Note: this is different than Electron app's `ready` event, which happens much earlier in the process 107 | - `create-window` - the line before `new BrowserWindow()` is called 108 | - `before-load` - after create window, before loadUrl (can be used for `require("@electron/remote/main").enable(webContents)`) 109 | - `after-create-window` - the line after all window init code is done and url was loaded 110 | - `show` - the line before `window.show()` is called 111 | - `after-show` - the line after `window.show()` is called 112 | - `hide` - the line before `window.hide()` is called (on window blur) 113 | - `after-hide` - the line after `window.hide()` is called 114 | - `after-close` - after the `.window` (BrowserWindow) property has been deleted 115 | - `focus-lost` - emitted if always-on-top option is set and the user clicks away 116 | 117 | ## Compatibility with Electron 118 | 119 | | menubar | Electron | Notes | 120 | | -------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------- | 121 | | 9.x.x | >=9.x.x <33.x.x | | 122 | | 8.x.x | 8.x.xx | | 123 | | 7.x.x | 7.x.xx | | 124 | | 6.x.x | >=4.x.x <7.x.x | Not recommended for [security reasons](https://electronjs.org/docs/tutorial/security#17-use-a-current-version-of-electron) | 125 | | <= 5.x.x | <= 3.x.x | Please, _please_ don't use these old versions | 126 | 127 | ## API Docs 128 | 129 | See the reference [API docs](./docs/globals.md). 130 | 131 | ## Tips 132 | 133 | - Use `mb.on('after-create-window', callback)` to run things after your app has loaded. For example you could run `mb.window.openDevTools()` to open the developer tools for debugging, or load a different URL with `mb.window.loadURL()` 134 | - Use `mb.on('focus-lost')` if you would like to perform some operation when using the option `browserWindow.alwaysOnTop: true` 135 | - To restore focus of previous window after menubar hide, use `mb.on('after-hide', () => { mb.app.hide() } )` or similar 136 | - To create a native menu, you can use `tray.setContextMenu(contextMenu)`, and pass this custom tray to menubar: `const mb = menubar({ tray });`. See [this example](https://github.com/maxogden/menubar/tree/master/examples/native-menu) for more information. 137 | - To avoid a flash when opening your menubar app, you can disable backgrounding the app using the following: `mb.app.commandLine.appendSwitch('disable-backgrounding-occluded-windows', 'true');` 138 | -------------------------------------------------------------------------------- /WORKING_PLATFORMS.md: -------------------------------------------------------------------------------- 1 | # Platforms where `menubar` is known to work 2 | 3 | This document is still a work-in-progress. If you have tested `menubar` with a platform that is not listed under here, I would greatly appreciate a PR! 4 | 5 | ## macOS 6 | 7 | | Version | Working Status | Known Issues | 8 | | ----------------- | -------------- | -------------------------------------------------------------------------------------------------------------- | 9 | | 14.15 Sonoma | ✅ Good | | 10 | | 10.14 Mojave | ✅ Good | [#147](https://github.com/maxogden/menubar/issues/147), [#215](https://github.com/maxogden/menubar/issues/215) | 11 | | 10.13 High Sierra | ✅ Good | | 12 | 13 | ## Windows 14 | 15 | | Version | Working Status | Known Issues | 16 | | ---------- | -------------- | ------------ | 17 | | Windows 11 | ✅ Good | | 18 | | Windows 10 | ✅ Good | | 19 | | Windows 8 | ✅ Good | | 20 | 21 | ## Linux 22 | 23 | | Distribution | Desktop Environment | Working Status | Known Issues | 24 | | ------------- | ------------------- | -------------- | ------------------------------------------------------ | 25 | | openSUSE 13.1 | Xfce 4.10.1 | ❌ Bad | [#123](https://github.com/maxogden/menubar/issues/123) | 26 | | Ubuntu 18.04 | Unity | ✅ Good | | 27 | | Ubuntu 14.04 | Unity | ❌ Bad | [#68](https://github.com/maxogden/menubar/issues/68) | 28 | -------------------------------------------------------------------------------- /assets/Icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/assets/Icon.icns -------------------------------------------------------------------------------- /assets/IconTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/assets/IconTemplate.png -------------------------------------------------------------------------------- /assets/IconTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/assets/IconTemplate@2x.png -------------------------------------------------------------------------------- /assets/screenshot-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/assets/screenshot-linux.png -------------------------------------------------------------------------------- /assets/screenshot-macos-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/assets/screenshot-macos-dark.png -------------------------------------------------------------------------------- /assets/screenshot-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/assets/screenshot-windows.png -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.3/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "linter": { 7 | "enabled": true, 8 | "rules": { 9 | "recommended": true, 10 | "style": { 11 | "noParameterAssign": "warn", 12 | "noNonNullAssertion": "warn", 13 | "useNodejsImportProtocol": "warn" 14 | }, 15 | "suspicious": { 16 | "noAssignInExpressions": "warn" 17 | } 18 | } 19 | }, 20 | "vcs": { 21 | "enabled": true, 22 | "clientKind": "git", 23 | "useIgnoreFile": true 24 | }, 25 | "formatter": { 26 | "enabled": true, 27 | "indentStyle": "space", 28 | "indentWidth": 2 29 | }, 30 | "javascript": { 31 | "formatter": { 32 | "quoteStyle": "single", 33 | "jsxQuoteStyle": "double" 34 | } 35 | }, 36 | "json": { 37 | "parser": { 38 | "allowComments": true 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | > **[menubar](README.md)** 2 | 3 | [Globals](globals.md) / 4 | 5 | [![Build Status](https://travis-ci.org/maxogden/menubar.svg?branch=master)](https://travis-ci.org/maxogden/menubar) 6 | [![npm (scoped)](https://img.shields.io/npm/v/menubar.svg)](https://www.npmjs.com/package/@maxogden/menubar) 7 | [![dependencies Status](https://david-dm.org/maxogden/menubar/status.svg)](https://david-dm.org/maxogden/menubar) 8 | ![npm bundle size](https://img.shields.io/bundlephobia/minzip/menubar.svg) 9 | ![npm bundle size](https://img.shields.io/bundlephobia/min/menubar.svg) 10 | 11 |


12 | 13 |

➖ Menubar

14 |

High level way to create menubar desktop applications with Electron.

15 | 16 |


17 | 18 | This module provides boilerplate for setting up a menubar application using Electron. All you have to do is point it at your `index.html` and `menubar` will handle the rest. 19 | 20 | ✅ Only one dependency, and one peer-dependency. 21 | 22 | ✅ Works on macOS, Windows and most Linuxes. See [details](./WORKING_PLATFORMS.md). 23 | 24 | ✅ 💥 [**3.6kB minified + gzipped**](https://bundlephobia.com/result?p=menubar) 💥 25 | 26 | | | | | 27 | | :-----------------------------------------------------------: | :--------------------------------------------------------: | :------------------------------------------------------: | 28 | | macOS Mojave 10.14 | Windows 10 | Ubuntu 18.04 | 29 | 30 | ## Installation 31 | 32 | ```bash 33 | yarn add menubar 34 | ``` 35 | 36 | ## Usage 37 | 38 | Starting with your own new project, run these commands: 39 | 40 | ```bash 41 | $ yarn add menubar 42 | $ touch myApp.js 43 | $ touch index.html 44 | ``` 45 | 46 | Fill `index.html` with some HTML, and `myApp.js` like this: 47 | 48 | ```javascript 49 | const { menubar } = require('menubar'); 50 | 51 | const mb = menubar(); 52 | 53 | mb.on('ready', () => { 54 | console.log('app is ready'); 55 | // your app code here 56 | }); 57 | ``` 58 | 59 | Then use `electron` to run the app: 60 | 61 | ```bash 62 | $ electron myApp.js 63 | ``` 64 | 65 | Alternatively, see [`examples/hello-world`](/examples/hello-world) folder for a simple working example. 66 | 67 | ## `Menubar` Class 68 | 69 | The return value of `menubar()` is a `Menubar` class instance, which has these properties: 70 | 71 | - `app`: the [Electron App](https://electronjs.org/docs/api/app) instance, 72 | - `window`: the [Electron Browser Window](https://electronjs.org/docs/api/browser-window) instance, 73 | - `tray`: the [Electron Tray](https://electronjs.org/docs/api/tray) instance, 74 | - `positioner`: the [Electron Positioner](https://github.com/jenslind/electron-positioner) instance, 75 | - `setOption(option, value)`: change an option after menubar is created, 76 | - `getOption(option)`: get an menubar option, 77 | - `showWindow()`: show the menubar window, 78 | - `hideWindow()`: hide the menubar window 79 | 80 | See the reference [API docs](./docs/classes/_menubar_.menubar.md). 81 | 82 | ## `menubar()` Options 83 | 84 | You can pass an optional options object into the `menubar({ ... })` function: 85 | 86 | - `dir` (default `process.cwd()`) - the app source directory 87 | - `index` (default `file:// + opts.dir + index.html`) - The URL to load the menubar's browserWindow with. The url can be a remote address (e.g. `http://`) or a path to a local HTML file using the `file://` protocol. 88 | - `browserWindow` - BrowserWindow options to be passed to the BrowserWindow constructor, see [Electron docs](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions). Some interesting fields to passed down are: 89 | - `x` (default `undefined`) - the x position of the window 90 | - `y` (default `undefined`) - the y position of the window 91 | - `width` (default 400) - window width 92 | - `height` (default 400) - window height 93 | - `alwaysOnTop` (default false) - if true, the window will not hide on blur 94 | - `icon` (default `opts.dir + IconTemplate.png`) - the png icon to use for the menubar. A good size to start with is 20x20. To support retina, supply a 2x sized image (e.g. 40x40) with `@2x` added to the end of the name, so `icon.png` and `icon@2x.png` and Electron will automatically use your `@2x` version on retina screens. 95 | - `tooltip` (default empty) - menubar tray icon tooltip text 96 | - `tray` (default created on-the-fly) - an electron `Tray` instance. if provided `opts.icon` will be ignored 97 | - `preloadWindow` (default false) - Create [BrowserWindow](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions) instance before it is used -- increasing resource usage, but making the click on the menubar load faster. 98 | - `showOnAllWorkspaces` (default true) - Makes the window available on all OS X workspaces. 99 | - `windowPosition` (default trayCenter and trayBottomCenter on Windows) - Sets the window position (x and y will still override this), check [positioner docs](https://github.com/jenslind/electron-positioner#docs) for valid values. 100 | - `showDockIcon` (default false) - Configure the visibility of the application dock icon. 101 | - `showOnRightClick` (default false) - Show the window on 'right-click' event instead of regular 'click' 102 | 103 | See the reference [API docs](./docs/interfaces/_types_.options.md). 104 | 105 | ## Events 106 | 107 | The `Menubar` class is an event emitter: 108 | 109 | - `ready` - when `menubar`'s tray icon has been created and initialized, i.e. when `menubar` is ready to be used. Note: this is different than Electron app's `ready` event, which happens much earlier in the process 110 | - `create-window` - the line before `new BrowserWindow()` is called 111 | - `after-create-window` - the line after all window init code is done 112 | - `show` - the line before `window.show()` is called 113 | - `after-show` - the line after `window.show()` is called 114 | - `hide` - the line before `window.hide()` is called (on window blur) 115 | - `after-hide` - the line after `window.hide()` is called 116 | - `after-close` - after the `.window` (BrowserWindow) property has been deleted 117 | - `focus-lost` - emitted if always-on-top option is set and the user clicks away 118 | 119 | ## Compatibility with Electron 120 | 121 | | menubar | Electron | Notes | 122 | | -------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------- | 123 | | 7.x.x | 7.x.x | 124 | | 6.x.x | 4.x.x \| 5.x.x \| 6.x.x | Not recommended for [security reasons](https://electronjs.org/docs/tutorial/security#17-use-a-current-version-of-electron) | 125 | | <= 5.x.x | <= 3.x.x | Please, _please_ don't use these old versions | 126 | 127 | ## API Docs 128 | 129 | See the reference [API docs](./docs/globals.md). 130 | 131 | ## Tips 132 | 133 | - Use `mb.on('after-create-window', callback)` to run things after your app has loaded. For example you could run `mb.window.openDevTools()` to open the developer tools for debugging, or load a different URL with `mb.window.loadUrl()` 134 | - Use `mb.on('focus-lost')` if you would like to perform some operation when using the option `browserWindow.alwaysOnTop: true` 135 | - To restore focus of previous window after menubar hide, use `mb.on('after-hide', () => { mb.app.hide() } )` or similar 136 | - To create a native menu, you can use `tray.setContextMenu(contextMenu)`, and pass this custom tray to menubar: `const mb = menubar({ tray });`. See [this example](https://github.com/maxogden/menubar/tree/master/examples/native-menu) for more information. 137 | - To avoid a flash when opening your menubar app, you can disable backgrounding the app using the following: `mb.app.commandLine.appendSwitch('disable-backgrounding-occluded-windows', 'true');` -------------------------------------------------------------------------------- /docs/classes/_menubar_.menubar.md: -------------------------------------------------------------------------------- 1 | > **[menubar](../README.md)** 2 | 3 | [Globals](../globals.md) / ["Menubar"](../modules/_menubar_.md) / [Menubar](_menubar_.menubar.md) / 4 | 5 | # Class: Menubar 6 | 7 | ## Hierarchy 8 | 9 | * `EventEmitter` 10 | 11 | * **Menubar** 12 | 13 | ## Index 14 | 15 | ### Constructors 16 | 17 | * [constructor](_menubar_.menubar.md#constructor) 18 | 19 | ### Accessors 20 | 21 | * [app](_menubar_.menubar.md#app) 22 | * [positioner](_menubar_.menubar.md#positioner) 23 | * [tray](_menubar_.menubar.md#tray) 24 | * [window](_menubar_.menubar.md#window) 25 | 26 | ### Methods 27 | 28 | * [getOption](_menubar_.menubar.md#getoption) 29 | * [hideWindow](_menubar_.menubar.md#hidewindow) 30 | * [setOption](_menubar_.menubar.md#setoption) 31 | * [showWindow](_menubar_.menubar.md#showwindow) 32 | 33 | ## Constructors 34 | 35 | ### constructor 36 | 37 | \+ **new Menubar**(`app`: `App`, `options?`: `Partial`): *[Menubar](_menubar_.menubar.md)* 38 | 39 | *Defined in [Menubar.ts:24](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L24)* 40 | 41 | **Parameters:** 42 | 43 | Name | Type | 44 | ------ | ------ | 45 | `app` | `App` | 46 | `options?` | `Partial` | 47 | 48 | **Returns:** *[Menubar](_menubar_.menubar.md)* 49 | 50 | ## Accessors 51 | 52 | ### app 53 | 54 | • **app**: 55 | 56 | *Defined in [Menubar.ts:47](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L47)* 57 | 58 | ___ 59 | 60 | ### positioner 61 | 62 | • **positioner**: 63 | 64 | *Defined in [Menubar.ts:56](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L56)* 65 | 66 | ___ 67 | 68 | ### tray 69 | 70 | • **tray**: 71 | 72 | *Defined in [Menubar.ts:69](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L69)* 73 | 74 | ___ 75 | 76 | ### window 77 | 78 | • **window**: 79 | 80 | *Defined in [Menubar.ts:83](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L83)* 81 | 82 | ## Methods 83 | 84 | ### getOption 85 | 86 | ▸ **getOption**<**K**>(`key`: `K`): *`Options[K]`* 87 | 88 | *Defined in [Menubar.ts:92](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L92)* 89 | 90 | **Type parameters:** 91 | 92 | ▪ **K**: *keyof Options* 93 | 94 | **Parameters:** 95 | 96 | Name | Type | Description | 97 | ------ | ------ | ------ | 98 | `key` | `K` | The option key to retrieve, see [Options](../interfaces/_types_.options.md). | 99 | 100 | **Returns:** *`Options[K]`* 101 | 102 | ___ 103 | 104 | ### hideWindow 105 | 106 | ▸ **hideWindow**(): *void* 107 | 108 | *Defined in [Menubar.ts:99](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L99)* 109 | 110 | **Returns:** *void* 111 | 112 | ___ 113 | 114 | ### setOption 115 | 116 | ▸ **setOption**<**K**>(`key`: `K`, `value`: `Options[K]`): *void* 117 | 118 | *Defined in [Menubar.ts:115](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L115)* 119 | 120 | **Type parameters:** 121 | 122 | ▪ **K**: *keyof Options* 123 | 124 | **Parameters:** 125 | 126 | Name | Type | Description | 127 | ------ | ------ | ------ | 128 | `key` | `K` | The option key to modify, see [Options](../interfaces/_types_.options.md). | 129 | `value` | `Options[K]` | The value to set. | 130 | 131 | **Returns:** *void* 132 | 133 | ___ 134 | 135 | ### showWindow 136 | 137 | ▸ **showWindow**(`trayPos?`: `Electron.Rectangle`): *`Promise`* 138 | 139 | *Defined in [Menubar.ts:124](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L124)* 140 | 141 | **Parameters:** 142 | 143 | Name | Type | Description | 144 | ------ | ------ | ------ | 145 | `trayPos?` | `Electron.Rectangle` | The bounds to show the window in. | 146 | 147 | **Returns:** *`Promise`* -------------------------------------------------------------------------------- /docs/globals.md: -------------------------------------------------------------------------------- 1 | > **[menubar](README.md)** 2 | 3 | [Globals](globals.md) / 4 | 5 | # menubar 6 | 7 | ## Index 8 | 9 | ### External modules 10 | 11 | * ["Menubar"](modules/_menubar_.md) 12 | * ["index"](modules/_index_.md) 13 | * ["types"](modules/_types_.md) 14 | * ["util/getWindowPosition"](modules/_util_getwindowposition_.md) -------------------------------------------------------------------------------- /docs/interfaces/_types_.options.md: -------------------------------------------------------------------------------- 1 | > **[menubar](../README.md)** 2 | 3 | [Globals](../globals.md) / ["types"](../modules/_types_.md) / [Options](_types_.options.md) / 4 | 5 | # Interface: Options 6 | 7 | ## Hierarchy 8 | 9 | * **Options** 10 | 11 | ## Table of Contents 12 | 13 | ### Properties 14 | 15 | * [browserWindow](_types_.options.md#browserwindow) 16 | * [dir](_types_.options.md#dir) 17 | * [icon](_types_.options.md#optional-icon) 18 | * [index](_types_.options.md#index) 19 | * [loadUrlOptions](_types_.options.md#optional-loadurloptions) 20 | * [preloadWindow](_types_.options.md#optional-preloadwindow) 21 | * [showDockIcon](_types_.options.md#optional-showdockicon) 22 | * [showOnAllWorkspaces](_types_.options.md#optional-showonallworkspaces) 23 | * [showOnRightClick](_types_.options.md#optional-showonrightclick) 24 | * [tooltip](_types_.options.md#tooltip) 25 | * [tray](_types_.options.md#optional-tray) 26 | * [windowPosition](_types_.options.md#optional-windowposition) 27 | 28 | ## Properties 29 | 30 | ### browserWindow 31 | 32 | • **browserWindow**: *`BrowserWindowConstructorOptions`* 33 | 34 | *Defined in [types.ts:23](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L23)* 35 | 36 | ___ 37 | 38 | ### dir 39 | 40 | • **dir**: *string* 41 | 42 | *Defined in [types.ts:27](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L27)* 43 | 44 | ___ 45 | 46 | ### `Optional` icon 47 | 48 | • **icon**? : *string | `NativeImage`* 49 | 50 | *Defined in [types.ts:34](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L34)* 51 | 52 | ___ 53 | 54 | ### index 55 | 56 | • **index**: *string | false* 57 | 58 | *Defined in [types.ts:43](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L43)* 59 | 60 | ___ 61 | 62 | ### `Optional` loadUrlOptions 63 | 64 | • **loadUrlOptions**? : *`LoadURLOptions`* 65 | 66 | *Defined in [types.ts:51](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L51)* 67 | 68 | ___ 69 | 70 | ### `Optional` preloadWindow 71 | 72 | • **preloadWindow**? : *undefined | false | true* 73 | 74 | *Defined in [types.ts:56](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L56)* 75 | 76 | ___ 77 | 78 | ### `Optional` showDockIcon 79 | 80 | • **showDockIcon**? : *undefined | false | true* 81 | 82 | *Defined in [types.ts:61](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L61)* 83 | 84 | ___ 85 | 86 | ### `Optional` showOnAllWorkspaces 87 | 88 | • **showOnAllWorkspaces**? : *undefined | false | true* 89 | 90 | *Defined in [types.ts:66](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L66)* 91 | 92 | ___ 93 | 94 | ### `Optional` showOnRightClick 95 | 96 | • **showOnRightClick**? : *undefined | false | true* 97 | 98 | *Defined in [types.ts:70](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L70)* 99 | 100 | ___ 101 | 102 | ### tooltip 103 | 104 | • **tooltip**: *string* 105 | 106 | *Defined in [types.ts:74](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L74)* 107 | 108 | ___ 109 | 110 | ### `Optional` tray 111 | 112 | • **tray**? : *`Tray`* 113 | 114 | *Defined in [types.ts:78](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L78)* 115 | 116 | ___ 117 | 118 | ### `Optional` windowPosition 119 | 120 | • **windowPosition**? : *"trayLeft" | "trayBottomLeft" | "trayRight" | "trayBottomRight" | "trayCenter" | "trayBottomCenter" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight" | "topCenter" | "bottomCenter" | "leftCenter" | "rightCenter" | "center"* 121 | 122 | *Defined in [types.ts:83](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L83)* 123 | -------------------------------------------------------------------------------- /docs/modules/_index_.md: -------------------------------------------------------------------------------- 1 | > **[menubar](../README.md)** 2 | 3 | [Globals](../globals.md) / ["index"](_index_.md) / 4 | 5 | # External module: "index" 6 | 7 | ## Index 8 | 9 | ### Functions 10 | 11 | * [menubar](_index_.md#menubar) 12 | 13 | ## Functions 14 | 15 | ### menubar 16 | 17 | ▸ **menubar**(`options?`: `Partial`): *[Menubar](../classes/_menubar_.menubar.md)* 18 | 19 | *Defined in [index.ts:25](https://github.com/adam-lynch/menubar/blob/6b93752/src/index.ts#L25)* 20 | 21 | **Parameters:** 22 | 23 | Name | Type | Description | 24 | ------ | ------ | ------ | 25 | `options?` | `Partial` | Options for creating a menubar application, see [Options](../interfaces/_types_.options.md) | 26 | 27 | **Returns:** *[Menubar](../classes/_menubar_.menubar.md)* -------------------------------------------------------------------------------- /docs/modules/_menubar_.md: -------------------------------------------------------------------------------- 1 | > **[menubar](../README.md)** 2 | 3 | [Globals](../globals.md) / ["Menubar"](_menubar_.md) / 4 | 5 | # External module: "Menubar" 6 | 7 | ## Index 8 | 9 | ### Classes 10 | 11 | * [Menubar](../classes/_menubar_.menubar.md) -------------------------------------------------------------------------------- /docs/modules/_types_.md: -------------------------------------------------------------------------------- 1 | > **[menubar](../README.md)** 2 | 3 | [Globals](../globals.md) / ["types"](_types_.md) / 4 | 5 | # External module: "types" 6 | 7 | ## Index 8 | 9 | ### Interfaces 10 | 11 | * [Options](../interfaces/_types_.options.md) -------------------------------------------------------------------------------- /docs/modules/_util_getwindowposition_.md: -------------------------------------------------------------------------------- 1 | > **[menubar](../README.md)** 2 | 3 | [Globals](../globals.md) / ["util/getWindowPosition"](_util_getwindowposition_.md) / 4 | 5 | # External module: "util/getWindowPosition" 6 | 7 | ## Index 8 | 9 | ### Functions 10 | 11 | * [getWindowPosition](_util_getwindowposition_.md#getwindowposition) 12 | * [taskbarLocation](_util_getwindowposition_.md#taskbarlocation) 13 | 14 | ## Functions 15 | 16 | ### getWindowPosition 17 | 18 | ▸ **getWindowPosition**(`tray`: `Tray`): *`WindowPosition`* 19 | 20 | *Defined in [util/getWindowPosition.ts:52](https://github.com/adam-lynch/menubar/blob/6b93752/src/util/getWindowPosition.ts#L52)* 21 | 22 | **Parameters:** 23 | 24 | Name | Type | Description | 25 | ------ | ------ | ------ | 26 | `tray` | `Tray` | The Electron Tray instance. | 27 | 28 | **Returns:** *`WindowPosition`* 29 | 30 | ___ 31 | 32 | ### taskbarLocation 33 | 34 | ▸ **taskbarLocation**(`tray`: `Tray`): *`TaskbarLocation`* 35 | 36 | *Defined in [util/getWindowPosition.ts:18](https://github.com/adam-lynch/menubar/blob/6b93752/src/util/getWindowPosition.ts#L18)* 37 | 38 | **Parameters:** 39 | 40 | Name | Type | Description | 41 | ------ | ------ | ------ | 42 | `tray` | `Tray` | The Electron Tray instance. | 43 | 44 | **Returns:** *`TaskbarLocation`* -------------------------------------------------------------------------------- /examples/arrow/README.md: -------------------------------------------------------------------------------- 1 | # example-menubar-arrow 2 | 3 | ## Description 4 | 5 | A menubar app with an arrow on top, that gives the app a popover look. 6 | 7 | Thanks @leilarrossi for this example, see https://github.com/maxogden/menubar/issues/78. 8 | 9 | ## Screenshot 10 | 11 | ![screenshot](./screenshot.png) 12 | 13 | ## Instructions 14 | 15 | - Clone the repository. 16 | - Run `yarn install` from the root folder. 17 | - Run `yarn build` from the root folder. 18 | - `cd` into this directory. 19 | - Run `yarn install` to install this example's dependencies. 20 | - Run `yarn start` from this directory to run app. 21 | -------------------------------------------------------------------------------- /examples/arrow/index.css: -------------------------------------------------------------------------------- 1 | .myarrow { 2 | position: relative; 3 | padding: 12px 0; 4 | } 5 | 6 | .myarrow:before { 7 | content: ""; 8 | height: 0; 9 | width: 0; 10 | border-width: 0 8px 12px 8px; 11 | border-style: solid; 12 | border-color: transparent transparent #eeeeee transparent; 13 | position: absolute; 14 | top: 0px; 15 | left: 50%; 16 | transform: translateX(-50%); 17 | } 18 | 19 | .page { 20 | background: #eeeeee; 21 | width: 340px; 22 | height: 500px; 23 | margin: 0 auto; 24 | } 25 | 26 | .darwin.page { 27 | border-radius: 5px; 28 | overflow: hidden; 29 | } 30 | -------------------------------------------------------------------------------- /examples/arrow/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Example App 4 | 5 | 6 | 7 |
Hello World.
8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/arrow/main.js: -------------------------------------------------------------------------------- 1 | const { menubar } = require('../..'); 2 | 3 | const mb = menubar({ 4 | browserWindow: { 5 | transparent: true, 6 | width: 350, 7 | height: 550, 8 | }, 9 | }); 10 | 11 | mb.on('ready', () => { 12 | console.log('Menubar app is ready.'); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/arrow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-menubar-arrow", 3 | "version": "0.1.0", 4 | "description": "Example menubar electron app", 5 | "main": "main.js", 6 | "private": true, 7 | "scripts": { 8 | "start": "electron ." 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/arrow/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/examples/arrow/screenshot.png -------------------------------------------------------------------------------- /examples/hello-world/README.md: -------------------------------------------------------------------------------- 1 | # example-menubar-hello-world 2 | 3 | ## Description 4 | 5 | This is a simple Hello World menubar app that will just open a window that says "Hello World". 6 | 7 | ## Screenshot 8 | 9 | ![screenshot](./screenshot.png) 10 | 11 | ## Instructions 12 | 13 | - Clone the repository. 14 | - Run `yarn install` from the root folder. 15 | - Run `yarn build` from the root folder. 16 | - `cd` into this directory. 17 | - Run `yarn install` to install this example's dependencies. 18 | - Run `yarn start` from this directory to run app. 19 | -------------------------------------------------------------------------------- /examples/hello-world/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Example App 4 | 5 | 6 | Hello World. 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/hello-world/main.js: -------------------------------------------------------------------------------- 1 | const { menubar } = require('../..'); 2 | 3 | const mb = menubar(); 4 | 5 | mb.on('ready', () => { 6 | console.log('Menubar app is ready.'); 7 | }); 8 | -------------------------------------------------------------------------------- /examples/hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-menubar-hello-world", 3 | "version": "0.1.0", 4 | "description": "Example menubar electron app", 5 | "main": "main.js", 6 | "private": true, 7 | "scripts": { 8 | "start": "electron ." 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/hello-world/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/examples/hello-world/screenshot.png -------------------------------------------------------------------------------- /examples/icon-animation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Example App 4 | 5 | 6 | Hello World. 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/icon-animation/main.js: -------------------------------------------------------------------------------- 1 | const { menubar } = require('../..'); 2 | 3 | const mb = menubar(); 4 | 5 | mb.on('ready', () => { 6 | setOkIcon(); 7 | const trayAnimation = setInterval(frame, 1000); 8 | // simulate data fetching 9 | sleep(3000).then(() => { 10 | clearInterval(trayAnimation); 11 | setOkIcon(); 12 | }); 13 | }); 14 | 15 | function setOkIcon() { 16 | mb.tray.setImage('state-ok-20.png'); 17 | } 18 | 19 | function frame() { 20 | setTimeout(() => mb.tray.setImage('state-sync-20.png'), 300); 21 | setTimeout(() => mb.tray.setImage('state-sync-20-60.png'), 600); 22 | setTimeout(() => mb.tray.setImage('state-sync-20-120.png'), 900); 23 | } 24 | 25 | function sleep(ms) { 26 | return new Promise((resolve) => { 27 | setTimeout(resolve, ms); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /examples/icon-animation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-menubar-icon-animation", 3 | "version": "0.1.0", 4 | "description": "Example menubar electron app", 5 | "main": "main.js", 6 | "private": true, 7 | "scripts": { 8 | "start": "electron ." 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/icon-animation/state-ok-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/examples/icon-animation/state-ok-20.png -------------------------------------------------------------------------------- /examples/icon-animation/state-sync-20-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/examples/icon-animation/state-sync-20-120.png -------------------------------------------------------------------------------- /examples/icon-animation/state-sync-20-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/examples/icon-animation/state-sync-20-60.png -------------------------------------------------------------------------------- /examples/icon-animation/state-sync-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/examples/icon-animation/state-sync-20.png -------------------------------------------------------------------------------- /examples/native-menu/README.md: -------------------------------------------------------------------------------- 1 | # example-menubar-native-menu 2 | 3 | ## Description 4 | 5 | This will create a menubar app with a native menu. 6 | 7 | Courtesy of @tslater in https://github.com/maxogden/menubar/issues/178. 8 | 9 | ## Screenshot 10 | 11 | ![screenshot](./screenshot.png) 12 | 13 | ## Instructions 14 | 15 | - Clone the repository. 16 | - Run `yarn install` from the root folder. 17 | - Run `yarn build` from the root folder. 18 | - `cd` into this directory. 19 | - Run `yarn install` to install this example's dependencies. 20 | - Run `yarn start` from this directory to run app. 21 | -------------------------------------------------------------------------------- /examples/native-menu/main.js: -------------------------------------------------------------------------------- 1 | const { app, Menu, Tray } = require('electron'); 2 | const path = require('path'); 3 | 4 | const { menubar } = require('../../'); 5 | 6 | const iconPath = path.join(__dirname, '..', '..', 'assets', 'IconTemplate.png'); 7 | 8 | app.on('ready', () => { 9 | const tray = new Tray(iconPath); 10 | const contextMenu = Menu.buildFromTemplate([ 11 | { label: 'Item1', type: 'radio' }, 12 | { label: 'Item2', type: 'radio' }, 13 | { label: 'Item3', type: 'radio', checked: true }, 14 | { label: 'Item4', type: 'radio' }, 15 | ]); 16 | tray.setContextMenu(contextMenu); 17 | 18 | const mb = menubar({ 19 | tray, 20 | }); 21 | 22 | mb.on('ready', () => { 23 | // needed for macos to remove white screen 24 | // ref: https://github.com/max-mapper/menubar/issues/345 25 | tray.removeAllListeners(); 26 | 27 | console.log('Menubar app is ready.'); 28 | // your app code here 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /examples/native-menu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-menubar-native-menu", 3 | "version": "0.1.0", 4 | "description": "Example menubar electron app", 5 | "main": "main.js", 6 | "private": true, 7 | "scripts": { 8 | "start": "electron ." 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/native-menu/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/menubar/6aa6df7aa8f6e5758aa5e48a3020caad264bfa73/examples/native-menu/screenshot.png -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-menubar", 3 | "version": "0.1.0", 4 | "description": "Examples of menubar electron apps", 5 | "private": true, 6 | "workspaces": ["arrow", "hello-world", "icon-animation", "native-menu"], 7 | "devDependencies": { 8 | "electron": "^31.0.2" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@electron/get@^2.0.0": 6 | version "2.0.3" 7 | resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.3.tgz#fba552683d387aebd9f3fcadbcafc8e12ee4f960" 8 | integrity sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ== 9 | dependencies: 10 | debug "^4.1.1" 11 | env-paths "^2.2.0" 12 | fs-extra "^8.1.0" 13 | got "^11.8.5" 14 | progress "^2.0.3" 15 | semver "^6.2.0" 16 | sumchecker "^3.0.1" 17 | optionalDependencies: 18 | global-agent "^3.0.0" 19 | 20 | "@sindresorhus/is@^4.0.0": 21 | version "4.6.0" 22 | resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" 23 | integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== 24 | 25 | "@szmarczak/http-timer@^4.0.5": 26 | version "4.0.6" 27 | resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" 28 | integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== 29 | dependencies: 30 | defer-to-connect "^2.0.0" 31 | 32 | "@types/cacheable-request@^6.0.1": 33 | version "6.0.3" 34 | resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" 35 | integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== 36 | dependencies: 37 | "@types/http-cache-semantics" "*" 38 | "@types/keyv" "^3.1.4" 39 | "@types/node" "*" 40 | "@types/responselike" "^1.0.0" 41 | 42 | "@types/http-cache-semantics@*": 43 | version "4.0.4" 44 | resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" 45 | integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== 46 | 47 | "@types/keyv@^3.1.4": 48 | version "3.1.4" 49 | resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" 50 | integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== 51 | dependencies: 52 | "@types/node" "*" 53 | 54 | "@types/node@*", "@types/node@^20.9.0": 55 | version "20.14.2" 56 | resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" 57 | integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== 58 | dependencies: 59 | undici-types "~5.26.4" 60 | 61 | "@types/responselike@^1.0.0": 62 | version "1.0.3" 63 | resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" 64 | integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== 65 | dependencies: 66 | "@types/node" "*" 67 | 68 | "@types/yauzl@^2.9.1": 69 | version "2.10.3" 70 | resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" 71 | integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== 72 | dependencies: 73 | "@types/node" "*" 74 | 75 | boolean@^3.0.1: 76 | version "3.2.0" 77 | resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" 78 | integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== 79 | 80 | buffer-crc32@~0.2.3: 81 | version "0.2.13" 82 | resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" 83 | integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== 84 | 85 | cacheable-lookup@^5.0.3: 86 | version "5.0.4" 87 | resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" 88 | integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== 89 | 90 | cacheable-request@^7.0.2: 91 | version "7.0.4" 92 | resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" 93 | integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== 94 | dependencies: 95 | clone-response "^1.0.2" 96 | get-stream "^5.1.0" 97 | http-cache-semantics "^4.0.0" 98 | keyv "^4.0.0" 99 | lowercase-keys "^2.0.0" 100 | normalize-url "^6.0.1" 101 | responselike "^2.0.0" 102 | 103 | clone-response@^1.0.2: 104 | version "1.0.3" 105 | resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" 106 | integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== 107 | dependencies: 108 | mimic-response "^1.0.0" 109 | 110 | debug@^4.1.0, debug@^4.1.1: 111 | version "4.3.5" 112 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" 113 | integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== 114 | dependencies: 115 | ms "2.1.2" 116 | 117 | decompress-response@^6.0.0: 118 | version "6.0.0" 119 | resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" 120 | integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== 121 | dependencies: 122 | mimic-response "^3.1.0" 123 | 124 | defer-to-connect@^2.0.0: 125 | version "2.0.1" 126 | resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" 127 | integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== 128 | 129 | define-data-property@^1.0.1: 130 | version "1.1.4" 131 | resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" 132 | integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== 133 | dependencies: 134 | es-define-property "^1.0.0" 135 | es-errors "^1.3.0" 136 | gopd "^1.0.1" 137 | 138 | define-properties@^1.2.1: 139 | version "1.2.1" 140 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" 141 | integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== 142 | dependencies: 143 | define-data-property "^1.0.1" 144 | has-property-descriptors "^1.0.0" 145 | object-keys "^1.1.1" 146 | 147 | detect-node@^2.0.4: 148 | version "2.1.0" 149 | resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" 150 | integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== 151 | 152 | electron@^31.0.2: 153 | version "31.0.2" 154 | resolved "https://registry.yarnpkg.com/electron/-/electron-31.0.2.tgz#9b719fe6072060fe74cb609bcbb84694abce5b17" 155 | integrity sha512-55efQ5yfLN+AQHcFC00AXQqtxC3iAGaxX2GQ3EDbFJ0ca9GHNOdSXkcrdBElLleiDrR2hpXNkQxN1bDn0oxe6w== 156 | dependencies: 157 | "@electron/get" "^2.0.0" 158 | "@types/node" "^20.9.0" 159 | extract-zip "^2.0.1" 160 | 161 | end-of-stream@^1.1.0: 162 | version "1.4.4" 163 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" 164 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 165 | dependencies: 166 | once "^1.4.0" 167 | 168 | env-paths@^2.2.0: 169 | version "2.2.1" 170 | resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" 171 | integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== 172 | 173 | es-define-property@^1.0.0: 174 | version "1.0.0" 175 | resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" 176 | integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== 177 | dependencies: 178 | get-intrinsic "^1.2.4" 179 | 180 | es-errors@^1.3.0: 181 | version "1.3.0" 182 | resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" 183 | integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== 184 | 185 | es6-error@^4.1.1: 186 | version "4.1.1" 187 | resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" 188 | integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== 189 | 190 | escape-string-regexp@^4.0.0: 191 | version "4.0.0" 192 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 193 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 194 | 195 | extract-zip@^2.0.1: 196 | version "2.0.1" 197 | resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" 198 | integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== 199 | dependencies: 200 | debug "^4.1.1" 201 | get-stream "^5.1.0" 202 | yauzl "^2.10.0" 203 | optionalDependencies: 204 | "@types/yauzl" "^2.9.1" 205 | 206 | fd-slicer@~1.1.0: 207 | version "1.1.0" 208 | resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" 209 | integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== 210 | dependencies: 211 | pend "~1.2.0" 212 | 213 | fs-extra@^8.1.0: 214 | version "8.1.0" 215 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" 216 | integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== 217 | dependencies: 218 | graceful-fs "^4.2.0" 219 | jsonfile "^4.0.0" 220 | universalify "^0.1.0" 221 | 222 | function-bind@^1.1.2: 223 | version "1.1.2" 224 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" 225 | integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== 226 | 227 | get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: 228 | version "1.2.4" 229 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" 230 | integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== 231 | dependencies: 232 | es-errors "^1.3.0" 233 | function-bind "^1.1.2" 234 | has-proto "^1.0.1" 235 | has-symbols "^1.0.3" 236 | hasown "^2.0.0" 237 | 238 | get-stream@^5.1.0: 239 | version "5.2.0" 240 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" 241 | integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== 242 | dependencies: 243 | pump "^3.0.0" 244 | 245 | global-agent@^3.0.0: 246 | version "3.0.0" 247 | resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" 248 | integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== 249 | dependencies: 250 | boolean "^3.0.1" 251 | es6-error "^4.1.1" 252 | matcher "^3.0.0" 253 | roarr "^2.15.3" 254 | semver "^7.3.2" 255 | serialize-error "^7.0.1" 256 | 257 | globalthis@^1.0.1: 258 | version "1.0.4" 259 | resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" 260 | integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== 261 | dependencies: 262 | define-properties "^1.2.1" 263 | gopd "^1.0.1" 264 | 265 | gopd@^1.0.1: 266 | version "1.0.1" 267 | resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" 268 | integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== 269 | dependencies: 270 | get-intrinsic "^1.1.3" 271 | 272 | got@^11.8.5: 273 | version "11.8.6" 274 | resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" 275 | integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== 276 | dependencies: 277 | "@sindresorhus/is" "^4.0.0" 278 | "@szmarczak/http-timer" "^4.0.5" 279 | "@types/cacheable-request" "^6.0.1" 280 | "@types/responselike" "^1.0.0" 281 | cacheable-lookup "^5.0.3" 282 | cacheable-request "^7.0.2" 283 | decompress-response "^6.0.0" 284 | http2-wrapper "^1.0.0-beta.5.2" 285 | lowercase-keys "^2.0.0" 286 | p-cancelable "^2.0.0" 287 | responselike "^2.0.0" 288 | 289 | graceful-fs@^4.1.6, graceful-fs@^4.2.0: 290 | version "4.2.11" 291 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" 292 | integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== 293 | 294 | has-property-descriptors@^1.0.0: 295 | version "1.0.2" 296 | resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" 297 | integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== 298 | dependencies: 299 | es-define-property "^1.0.0" 300 | 301 | has-proto@^1.0.1: 302 | version "1.0.3" 303 | resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" 304 | integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== 305 | 306 | has-symbols@^1.0.3: 307 | version "1.0.3" 308 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" 309 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== 310 | 311 | hasown@^2.0.0: 312 | version "2.0.2" 313 | resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" 314 | integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== 315 | dependencies: 316 | function-bind "^1.1.2" 317 | 318 | http-cache-semantics@^4.0.0: 319 | version "4.1.1" 320 | resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" 321 | integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== 322 | 323 | http2-wrapper@^1.0.0-beta.5.2: 324 | version "1.0.3" 325 | resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" 326 | integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== 327 | dependencies: 328 | quick-lru "^5.1.1" 329 | resolve-alpn "^1.0.0" 330 | 331 | json-buffer@3.0.1: 332 | version "3.0.1" 333 | resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" 334 | integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== 335 | 336 | json-stringify-safe@^5.0.1: 337 | version "5.0.1" 338 | resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" 339 | integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== 340 | 341 | jsonfile@^4.0.0: 342 | version "4.0.0" 343 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" 344 | integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== 345 | optionalDependencies: 346 | graceful-fs "^4.1.6" 347 | 348 | keyv@^4.0.0: 349 | version "4.5.4" 350 | resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" 351 | integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== 352 | dependencies: 353 | json-buffer "3.0.1" 354 | 355 | lowercase-keys@^2.0.0: 356 | version "2.0.0" 357 | resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" 358 | integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== 359 | 360 | matcher@^3.0.0: 361 | version "3.0.0" 362 | resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" 363 | integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== 364 | dependencies: 365 | escape-string-regexp "^4.0.0" 366 | 367 | mimic-response@^1.0.0: 368 | version "1.0.1" 369 | resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" 370 | integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== 371 | 372 | mimic-response@^3.1.0: 373 | version "3.1.0" 374 | resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" 375 | integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== 376 | 377 | ms@2.1.2: 378 | version "2.1.2" 379 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 380 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 381 | 382 | normalize-url@^6.0.1: 383 | version "6.1.0" 384 | resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" 385 | integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== 386 | 387 | object-keys@^1.1.1: 388 | version "1.1.1" 389 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 390 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 391 | 392 | once@^1.3.1, once@^1.4.0: 393 | version "1.4.0" 394 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 395 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 396 | dependencies: 397 | wrappy "1" 398 | 399 | p-cancelable@^2.0.0: 400 | version "2.1.1" 401 | resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" 402 | integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== 403 | 404 | pend@~1.2.0: 405 | version "1.2.0" 406 | resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" 407 | integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== 408 | 409 | progress@^2.0.3: 410 | version "2.0.3" 411 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 412 | integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== 413 | 414 | pump@^3.0.0: 415 | version "3.0.0" 416 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" 417 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== 418 | dependencies: 419 | end-of-stream "^1.1.0" 420 | once "^1.3.1" 421 | 422 | quick-lru@^5.1.1: 423 | version "5.1.1" 424 | resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" 425 | integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== 426 | 427 | resolve-alpn@^1.0.0: 428 | version "1.2.1" 429 | resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" 430 | integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== 431 | 432 | responselike@^2.0.0: 433 | version "2.0.1" 434 | resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" 435 | integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== 436 | dependencies: 437 | lowercase-keys "^2.0.0" 438 | 439 | roarr@^2.15.3: 440 | version "2.15.4" 441 | resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" 442 | integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== 443 | dependencies: 444 | boolean "^3.0.1" 445 | detect-node "^2.0.4" 446 | globalthis "^1.0.1" 447 | json-stringify-safe "^5.0.1" 448 | semver-compare "^1.0.0" 449 | sprintf-js "^1.1.2" 450 | 451 | semver-compare@^1.0.0: 452 | version "1.0.0" 453 | resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" 454 | integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== 455 | 456 | semver@^6.2.0: 457 | version "6.3.1" 458 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" 459 | integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== 460 | 461 | semver@^7.3.2: 462 | version "7.6.2" 463 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" 464 | integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== 465 | 466 | serialize-error@^7.0.1: 467 | version "7.0.1" 468 | resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" 469 | integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== 470 | dependencies: 471 | type-fest "^0.13.1" 472 | 473 | sprintf-js@^1.1.2: 474 | version "1.1.3" 475 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" 476 | integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== 477 | 478 | sumchecker@^3.0.1: 479 | version "3.0.1" 480 | resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" 481 | integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== 482 | dependencies: 483 | debug "^4.1.0" 484 | 485 | type-fest@^0.13.1: 486 | version "0.13.1" 487 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" 488 | integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== 489 | 490 | undici-types@~5.26.4: 491 | version "5.26.5" 492 | resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" 493 | integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== 494 | 495 | universalify@^0.1.0: 496 | version "0.1.2" 497 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" 498 | integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== 499 | 500 | wrappy@1: 501 | version "1.0.2" 502 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 503 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 504 | 505 | yauzl@^2.10.0: 506 | version "2.10.0" 507 | resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" 508 | integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== 509 | dependencies: 510 | buffer-crc32 "~0.2.3" 511 | fd-slicer "~1.1.0" 512 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | collectCoverageFrom: ['**/*.{ts,tsx}', '!**/node_modules/**', '!**/*.d.ts'], 3 | moduleFileExtensions: ['js', 'ts', 'tsx'], 4 | rootDir: '.', 5 | transform: { 6 | '^.+\\.(ts|tsx)$': 'ts-jest', 7 | }, 8 | testRegex: 'spec\\.(ts|tsx)$', 9 | }; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "menubar", 3 | "version": "9.5.1", 4 | "author": "Max Ogden", 5 | "bugs": { 6 | "url": "https://github.com/maxogden/menubar/issues" 7 | }, 8 | "description": "high level way to create menubar desktop applications with electron", 9 | "files": [ 10 | "/assets", 11 | "/lib" 12 | ], 13 | "homepage": "https://github.com/maxogden/menubar", 14 | "keywords": [ 15 | "electron", 16 | "shell", 17 | "menubar", 18 | "menu", 19 | "taskbar", 20 | "tray", 21 | "traybar", 22 | "mac", 23 | "linux", 24 | "windows", 25 | "app" 26 | ], 27 | "license": "BSD-2-Clause", 28 | "main": "lib/index.js", 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/maxogden/menubar.git" 35 | }, 36 | "scripts": { 37 | "build": "rimraf lib/ && tsc", 38 | "deploy": "yarn build && standard-version", 39 | "docs": "typedoc", 40 | "lint:check": "biome check", 41 | "lint": "biome check --fix", 42 | "test": "jest" 43 | }, 44 | "types": "lib/index.d.ts", 45 | "dependencies": { 46 | "electron-positioner": "^4.1.0" 47 | }, 48 | "devDependencies": { 49 | "@biomejs/biome": "^1.9.3", 50 | "@types/jest": "^25.2.3", 51 | "electron": "^32.2.0", 52 | "jest": "^26.0.1", 53 | "rimraf": "^3.0.2", 54 | "standard-version": "^8.0.0", 55 | "ts-jest": "^26.0.0", 56 | "typedoc": "^0.17.7", 57 | "typedoc-plugin-markdown": "^2.2.17", 58 | "typedoc-plugin-no-inherit": "^1.1.10", 59 | "typescript": "^4.6.2" 60 | }, 61 | "peerDependencies": { 62 | "electron": ">=9.0.0 <33.0.0" 63 | }, 64 | "packageManager": "yarn@1.22.22" 65 | } 66 | -------------------------------------------------------------------------------- /src/Menubar.spec.ts: -------------------------------------------------------------------------------- 1 | import { BrowserWindow, Tray, app } from 'electron'; 2 | 3 | import { Menubar } from './Menubar'; 4 | 5 | describe('Menubar', () => { 6 | let mb: Menubar | undefined; 7 | 8 | beforeEach(() => { 9 | mb = new Menubar(app, { preloadWindow: true }); 10 | }); 11 | 12 | it('should have property `app`', () => { 13 | expect(mb!.app).toBeDefined(); 14 | }); 15 | 16 | it('should have property `positioner`', () => { 17 | expect(() => mb!.positioner as unknown).toThrow(); 18 | return new Promise((resolve) => { 19 | mb!.on('after-create-window', () => { 20 | expect(mb!.positioner).toBeDefined(); 21 | resolve(); 22 | }); 23 | }); 24 | }); 25 | 26 | it('should have property `tray`', () => { 27 | expect(() => mb!.tray).toThrow(); 28 | return new Promise((resolve) => { 29 | mb!.on('ready', () => { 30 | expect(mb!.tray).toBeInstanceOf(Tray); 31 | resolve(); 32 | }); 33 | }); 34 | }); 35 | 36 | it('should have property `window`', () => { 37 | expect(mb!.window).toBeUndefined(); 38 | return new Promise((resolve) => { 39 | mb!.on('ready', () => { 40 | expect(mb!.window).toBeInstanceOf(BrowserWindow); 41 | resolve(); 42 | }); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/Menubar.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | import { BrowserWindow, Tray } from 'electron'; 5 | import Positioner from 'electron-positioner'; 6 | 7 | import type { Options } from './types'; 8 | import { cleanOptions } from './util/cleanOptions'; 9 | import { getWindowPosition } from './util/getWindowPosition'; 10 | 11 | /** 12 | * The main Menubar class. 13 | * 14 | * @noInheritDoc 15 | */ 16 | export class Menubar extends EventEmitter { 17 | private _app: Electron.App; 18 | private _browserWindow?: BrowserWindow; 19 | private _blurTimeout: NodeJS.Timeout | null = null; // track blur events with timeout 20 | private _isVisible: boolean; // track visibility 21 | private _cachedBounds?: Electron.Rectangle; // _cachedBounds are needed for double-clicked event 22 | private _options: Options; 23 | private _positioner: Positioner | undefined; 24 | private _tray?: Tray; 25 | 26 | constructor(app: Electron.App, options?: Partial) { 27 | super(); 28 | this._app = app; 29 | this._options = cleanOptions(options); 30 | this._isVisible = false; 31 | 32 | if (app.isReady()) { 33 | // See https://github.com/maxogden/menubar/pull/151 34 | process.nextTick(() => 35 | this.appReady().catch((err) => console.error('menubar: ', err)), 36 | ); 37 | } else { 38 | app.on('ready', () => { 39 | this.appReady().catch((err) => console.error('menubar: ', err)); 40 | }); 41 | } 42 | } 43 | 44 | /** 45 | * The Electron [App](https://electronjs.org/docs/api/app) 46 | * instance. 47 | */ 48 | get app(): Electron.App { 49 | return this._app; 50 | } 51 | 52 | /** 53 | * The [electron-positioner](https://github.com/jenslind/electron-positioner) 54 | * instance. 55 | */ 56 | get positioner(): Positioner { 57 | if (!this._positioner) { 58 | throw new Error( 59 | 'Please access `this.positioner` after the `after-create-window` event has fired.', 60 | ); 61 | } 62 | 63 | return this._positioner; 64 | } 65 | 66 | /** 67 | * The Electron [Tray](https://electronjs.org/docs/api/tray) instance. 68 | */ 69 | get tray(): Tray { 70 | if (!this._tray) { 71 | throw new Error( 72 | 'Please access `this.tray` after the `ready` event has fired.', 73 | ); 74 | } 75 | 76 | return this._tray; 77 | } 78 | 79 | /** 80 | * The Electron [BrowserWindow](https://electronjs.org/docs/api/browser-window) 81 | * instance, if it's present. 82 | */ 83 | get window(): BrowserWindow | undefined { 84 | return this._browserWindow; 85 | } 86 | 87 | /** 88 | * Retrieve a menubar option. 89 | * 90 | * @param key - The option key to retrieve, see {@link Options}. 91 | */ 92 | getOption(key: K): Options[K] { 93 | return this._options[key]; 94 | } 95 | 96 | /** 97 | * Hide the menubar window. 98 | */ 99 | hideWindow(): void { 100 | if (!this._browserWindow || !this._isVisible) { 101 | return; 102 | } 103 | this.emit('hide'); 104 | this._browserWindow.hide(); 105 | this.emit('after-hide'); 106 | this._isVisible = false; 107 | if (this._blurTimeout) { 108 | clearTimeout(this._blurTimeout); 109 | this._blurTimeout = null; 110 | } 111 | } 112 | 113 | /** 114 | * Change an option after menubar is created. 115 | * 116 | * @param key - The option key to modify, see {@link Options}. 117 | * @param value - The value to set. 118 | */ 119 | setOption(key: K, value: Options[K]): void { 120 | this._options[key] = value; 121 | } 122 | 123 | /** 124 | * Show the menubar window. 125 | * 126 | * @param trayPos - The bounds to show the window in. 127 | */ 128 | async showWindow(trayPos?: Electron.Rectangle): Promise { 129 | if (!this.tray) { 130 | throw new Error('Tray should have been instantiated by now'); 131 | } 132 | 133 | if (!this._browserWindow) { 134 | await this.createWindow(); 135 | } 136 | 137 | // Use guard for TypeScript, to avoid ! everywhere 138 | if (!this._browserWindow) { 139 | throw new Error('Window has been initialized just above. qed.'); 140 | } 141 | 142 | // 'Windows' taskbar: sync windows position each time before showing 143 | // https://github.com/maxogden/menubar/issues/232 144 | if (['win32', 'linux'].includes(process.platform)) { 145 | // Fill in this._options.windowPosition when taskbar position is available 146 | this._options.windowPosition = getWindowPosition(this.tray); 147 | } 148 | 149 | this.emit('show'); 150 | 151 | if (trayPos && trayPos.x !== 0) { 152 | // Cache the bounds 153 | this._cachedBounds = trayPos; 154 | } else if (this._cachedBounds) { 155 | // Cached value will be used if showWindow is called without bounds data 156 | trayPos = this._cachedBounds; 157 | } else if (this.tray.getBounds) { 158 | // Get the current tray bounds 159 | trayPos = this.tray.getBounds(); 160 | } 161 | 162 | // Default the window to the right if `trayPos` bounds are undefined or null. 163 | let noBoundsPosition = undefined; 164 | if ( 165 | (trayPos === undefined || trayPos.x === 0) && 166 | this._options.windowPosition && 167 | this._options.windowPosition.startsWith('tray') 168 | ) { 169 | noBoundsPosition = 170 | process.platform === 'win32' ? 'bottomRight' : 'topRight'; 171 | } 172 | 173 | const position = this.positioner.calculate( 174 | this._options.windowPosition || noBoundsPosition, 175 | trayPos, 176 | ) as { x: number; y: number }; 177 | 178 | // Not using `||` because x and y can be zero. 179 | const x = 180 | this._options.browserWindow.x !== undefined 181 | ? this._options.browserWindow.x 182 | : position.x; 183 | const y = 184 | this._options.browserWindow.y !== undefined 185 | ? this._options.browserWindow.y 186 | : position.y; 187 | 188 | // `.setPosition` crashed on non-integers 189 | // https://github.com/maxogden/menubar/issues/233 190 | this._browserWindow.setPosition(Math.round(x), Math.round(y)); 191 | this._browserWindow.show(); 192 | this._isVisible = true; 193 | this.emit('after-show'); 194 | return; 195 | } 196 | 197 | private async appReady(): Promise { 198 | if (this.app.dock && !this._options.showDockIcon) { 199 | this.app.dock.hide(); 200 | } 201 | 202 | if (this._options.activateWithApp) { 203 | this.app.on('activate', (_event, hasVisibleWindows) => { 204 | if (!hasVisibleWindows) { 205 | this.showWindow().catch(console.error); 206 | } 207 | }); 208 | } 209 | 210 | let trayImage = 211 | this._options.icon || path.join(this._options.dir, 'IconTemplate.png'); 212 | if (typeof trayImage === 'string' && !fs.existsSync(trayImage)) { 213 | trayImage = path.join(__dirname, '..', 'assets', 'IconTemplate.png'); // Default cat icon 214 | } 215 | 216 | const defaultClickEvent = this._options.showOnRightClick 217 | ? 'right-click' 218 | : 'click'; 219 | 220 | this._tray = this._options.tray || new Tray(trayImage); 221 | // Type guards for TS not to complain 222 | if (!this.tray) { 223 | throw new Error('Tray has been initialized above'); 224 | } 225 | this.tray.on( 226 | defaultClickEvent as Parameters[0], 227 | this.clicked.bind(this), 228 | ); 229 | this.tray.on('double-click', this.clicked.bind(this)); 230 | this.tray.setToolTip(this._options.tooltip); 231 | 232 | if (!this._options.windowPosition) { 233 | this._options.windowPosition = getWindowPosition(this.tray); 234 | } 235 | 236 | if (this._options.preloadWindow) { 237 | await this.createWindow(); 238 | } 239 | 240 | this.emit('ready'); 241 | } 242 | 243 | /** 244 | * Callback on tray icon click or double-click. 245 | * 246 | * @param e 247 | * @param bounds 248 | */ 249 | private async clicked( 250 | event?: Electron.KeyboardEvent, 251 | bounds?: Electron.Rectangle, 252 | ): Promise { 253 | if (event && (event.shiftKey || event.ctrlKey || event.metaKey)) { 254 | return this.hideWindow(); 255 | } 256 | 257 | // if blur was invoked clear timeout 258 | if (this._blurTimeout) { 259 | clearInterval(this._blurTimeout); 260 | } 261 | 262 | if (this._browserWindow && this._isVisible) { 263 | return this.hideWindow(); 264 | } 265 | 266 | this._cachedBounds = bounds || this._cachedBounds; 267 | await this.showWindow(this._cachedBounds); 268 | } 269 | 270 | private async createWindow(): Promise { 271 | this.emit('create-window'); 272 | 273 | // We add some default behavior for menubar's browserWindow, to make it 274 | // look like a menubar 275 | const defaults = { 276 | show: false, // Don't show it at first 277 | frame: false, // Remove window frame 278 | }; 279 | 280 | this._browserWindow = new BrowserWindow({ 281 | ...defaults, 282 | ...this._options.browserWindow, 283 | }); 284 | 285 | this._positioner = new Positioner(this._browserWindow); 286 | 287 | this._browserWindow.on('blur', () => { 288 | if (!this._browserWindow) { 289 | return; 290 | } 291 | 292 | // hack to close if icon clicked when open 293 | this._browserWindow.isAlwaysOnTop() 294 | ? this.emit('focus-lost') 295 | : (this._blurTimeout = setTimeout(() => { 296 | this.hideWindow(); 297 | }, 100)); 298 | }); 299 | 300 | if (this._options.showOnAllWorkspaces !== false) { 301 | // https://github.com/electron/electron/issues/37832#issuecomment-1497882944 302 | this._browserWindow.setVisibleOnAllWorkspaces(true, { 303 | skipTransformProcessType: true, // Avoid damaging the original visible state of app.dock 304 | }); 305 | } 306 | 307 | this._browserWindow.on('close', this.windowClear.bind(this)); 308 | 309 | this.emit('before-load'); 310 | 311 | // If the user explicity set options.index to false, we don't loadURL 312 | // https://github.com/maxogden/menubar/issues/255 313 | if (this._options.index !== false) { 314 | await this._browserWindow.loadURL( 315 | this._options.index, 316 | this._options.loadUrlOptions, 317 | ); 318 | } 319 | this.emit('after-create-window'); 320 | } 321 | 322 | private windowClear(): void { 323 | this._browserWindow = undefined; 324 | this.emit('after-close'); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/__mocks__/electron.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/electron/electron/issues/3909#issuecomment-190990825 2 | 3 | export const MOCK_APP_GETAPPPATH = 'mock.app.getAppPath'; 4 | 5 | export const app = { 6 | getAppPath: jest.fn(() => MOCK_APP_GETAPPPATH), 7 | isReady: (): Promise => Promise.resolve(), 8 | on: (): void => { 9 | /* Do nothing */ 10 | }, 11 | }; 12 | 13 | export class BrowserWindow { 14 | loadURL(): void { 15 | // Do nothing 16 | } 17 | 18 | on(): void { 19 | // Do nothing 20 | } 21 | 22 | setVisibleOnAllWorkspaces(): void { 23 | // Do nothing 24 | } 25 | } 26 | 27 | export class Tray { 28 | on(): void { 29 | // Do nothing 30 | } 31 | 32 | setToolTip(): void { 33 | // Do nothing 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/ambient.d.ts: -------------------------------------------------------------------------------- 1 | // TODO https://github.com/jenslind/electron-positioner/issues/15 2 | declare module 'electron-positioner' { 3 | export default class { 4 | constructor(window: Electron.BrowserWindow); 5 | 6 | calculate( 7 | position?: string, 8 | rectangle?: Electron.Rectangle, 9 | ): { x: number; y: number }; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Entry point of menubar 3 | * @example 4 | * ```typescript 5 | * import { menubar } from 'menubar'; 6 | * ``` 7 | */ 8 | 9 | /** */ 10 | 11 | import { app } from 'electron'; 12 | 13 | import { Menubar } from './Menubar'; 14 | import type { Options } from './types'; 15 | 16 | export * from './util/getWindowPosition'; 17 | export { Menubar }; 18 | 19 | /** 20 | * Factory function to create a menubar application 21 | * 22 | * @param options - Options for creating a menubar application, see 23 | * {@link Options} 24 | */ 25 | export function menubar(options?: Partial): Menubar { 26 | return new Menubar(app, options); 27 | } 28 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | BrowserWindowConstructorOptions, 3 | LoadURLOptions, 4 | Tray, 5 | } from 'electron'; 6 | 7 | /** 8 | * Options for creating a menubar application 9 | */ 10 | export interface Options { 11 | /** 12 | * Listen on `app.on('activate')` to open menubar when app is activated. 13 | * @default `true` 14 | */ 15 | activateWithApp?: boolean; 16 | /** 17 | * An Electron BrowserWindow instance, or an options object to be passed into 18 | * the BrowserWindow constructor. 19 | * @example 20 | * ```typescript 21 | * const options = { height: 640, width: 480 }; 22 | * 23 | * const mb = new Menubar({ 24 | * browserWindow: options 25 | * }); 26 | * ``` 27 | */ 28 | browserWindow: BrowserWindowConstructorOptions; 29 | /** 30 | * The app source directory. 31 | */ 32 | dir: string; 33 | /** 34 | * The png icon to use for the menubar. A good size to start with is 20x20. 35 | * To support retina, supply a 2x sized image (e.g. 40x40) with @2x added to 36 | * the end of the name, so icon.png and icon@2x.png and Electron will 37 | * automatically use your @2x version on retina screens. 38 | */ 39 | icon?: string | Electron.NativeImage; 40 | /** 41 | * The URL to load the menubar's browserWindow with. The url can be a remote 42 | * address (e.g. `http://`) or a path to a local HTML file using the 43 | * `file://` protocol. If false, then menubar won't call `loadURL` on 44 | * start. 45 | * @default `file:// + options.dir + index.html` 46 | * @see https://electronjs.org/docs/api/browser-window#winloadurlurl-options 47 | */ 48 | index: string | false; 49 | /** 50 | * The options passed when loading the index URL in the menubar's 51 | * browserWindow. Everything browserWindow.loadURL supports is supported; 52 | * this object is simply passed onto browserWindow.loadURL 53 | * @default `{}` 54 | * @see https://electronjs.org/docs/api/browser-window#winloadurlurl-options 55 | */ 56 | loadUrlOptions?: LoadURLOptions; 57 | /** 58 | * Create BrowserWindow instance before it is used -- increasing resource 59 | * usage, but making the click on the menubar load faster. 60 | */ 61 | preloadWindow?: boolean; 62 | /** 63 | * Configure the visibility of the application dock icon, macOS only. Calls 64 | * [`app.dock.hide`](https://electronjs.org/docs/api/app#appdockhide-macos). 65 | */ 66 | showDockIcon?: boolean; 67 | /** 68 | * Makes the window available on all OS X workspaces. Calls 69 | * [`setVisibleOnAllWorkspaces`](https://electronjs.org/docs/api/browser-window#winsetvisibleonallworkspacesvisible-options). 70 | */ 71 | showOnAllWorkspaces?: boolean; 72 | /** 73 | * Show the window on 'right-click' event instead of regular 'click'. 74 | */ 75 | showOnRightClick?: boolean; 76 | /** 77 | * Menubar tray icon tooltip text. Calls [`tray.setTooltip`](https://electronjs.org/docs/api/tray#traysettooltiptooltip). 78 | */ 79 | tooltip: string; 80 | /** 81 | * An electron Tray instance. If provided, `options.icon` will be ignored. 82 | */ 83 | tray?: Tray; 84 | /** 85 | * Sets the window position (x and y will still override this), check 86 | * electron-positioner docs for valid values. 87 | */ 88 | windowPosition?: 89 | | 'trayLeft' 90 | | 'trayBottomLeft' 91 | | 'trayRight' 92 | | 'trayBottomRight' 93 | | 'trayCenter' 94 | | 'trayBottomCenter' 95 | | 'topLeft' 96 | | 'topRight' 97 | | 'bottomLeft' 98 | | 'bottomRight' 99 | | 'topCenter' 100 | | 'bottomCenter' 101 | | 'leftCenter' 102 | | 'rightCenter' 103 | | 'center'; 104 | } 105 | -------------------------------------------------------------------------------- /src/util/cleanOptions.spec.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { MOCK_APP_GETAPPPATH } from '../__mocks__/electron'; 4 | import { cleanOptions } from './cleanOptions'; 5 | 6 | const DEFAULT_OPTIONS = { 7 | activateWithApp: true, 8 | browserWindow: { 9 | height: 400, 10 | width: 400, 11 | }, 12 | dir: path.resolve(MOCK_APP_GETAPPPATH), 13 | index: `file://${path.join(path.resolve(MOCK_APP_GETAPPPATH), 'index.html')}`, 14 | loadUrlOptions: {}, 15 | tooltip: '', 16 | }; 17 | 18 | describe('cleanOptions', () => { 19 | it('should handle undefined', () => { 20 | expect(cleanOptions(undefined)).toEqual(DEFAULT_OPTIONS); 21 | }); 22 | 23 | it('should handle a dir string with relative path', () => { 24 | expect(cleanOptions({ dir: 'MY_RELATIVE_PATH' })).toEqual({ 25 | ...DEFAULT_OPTIONS, 26 | dir: path.resolve('MY_RELATIVE_PATH'), 27 | index: `file://${path.join( 28 | path.resolve('MY_RELATIVE_PATH'), 29 | 'index.html', 30 | )}`, 31 | }); 32 | }); 33 | 34 | it('should handle a dir string with absolute path', () => { 35 | expect(cleanOptions({ dir: '/home/me/MY_ABSOLUTE_PATH' })).toEqual({ 36 | ...DEFAULT_OPTIONS, 37 | dir: '/home/me/MY_ABSOLUTE_PATH', 38 | index: 'file:///home/me/MY_ABSOLUTE_PATH/index.html', 39 | }); 40 | }); 41 | 42 | it('should handle a false index', () => { 43 | expect(cleanOptions({ index: false })).toEqual({ 44 | ...DEFAULT_OPTIONS, 45 | index: false, 46 | }); 47 | }); 48 | 49 | it('should handle an object with multiple fields', () => { 50 | expect( 51 | cleanOptions({ 52 | browserWindow: { 53 | height: 100, 54 | }, 55 | index: 'file:///home/abc/index.html', 56 | showDockIcon: true, 57 | windowPosition: 'trayCenter', 58 | }), 59 | ).toEqual({ 60 | ...DEFAULT_OPTIONS, 61 | browserWindow: { 62 | ...DEFAULT_OPTIONS.browserWindow, 63 | height: 100, 64 | }, 65 | index: 'file:///home/abc/index.html', 66 | showDockIcon: true, 67 | windowPosition: 'trayCenter', 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/util/cleanOptions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @ignore 3 | */ 4 | 5 | /** */ 6 | 7 | import path from 'path'; 8 | import url from 'url'; 9 | import { app } from 'electron'; 10 | 11 | import type { Options } from '../types'; 12 | 13 | const DEFAULT_WINDOW_HEIGHT = 400; 14 | const DEFAULT_WINDOW_WIDTH = 400; 15 | 16 | /** 17 | * Take as input some options, and return a sanitized version of it. 18 | * 19 | * @param opts - The options to clean. 20 | * @ignore 21 | */ 22 | export function cleanOptions(opts?: Partial): Options { 23 | const options: Partial = { ...opts }; 24 | 25 | if (options.activateWithApp === undefined) { 26 | options.activateWithApp = true; 27 | } 28 | if (!options.dir) { 29 | options.dir = app.getAppPath(); 30 | } 31 | if (!path.isAbsolute(options.dir)) { 32 | options.dir = path.resolve(options.dir); 33 | } 34 | // Note: options.index can be `false` 35 | if (options.index === undefined) { 36 | options.index = url.format({ 37 | pathname: path.join(options.dir, 'index.html'), 38 | protocol: 'file:', 39 | slashes: true, 40 | }); 41 | } 42 | options.loadUrlOptions = options.loadUrlOptions || {}; 43 | 44 | options.tooltip = options.tooltip || ''; 45 | 46 | // `icon`, `preloadWindow`, `showDockIcon`, `showOnAllWorkspaces`, 47 | // `showOnRightClick` don't need any special treatment 48 | 49 | // Now we take care of `browserWindow` 50 | if (!options.browserWindow) { 51 | options.browserWindow = {}; 52 | } 53 | 54 | // Set width/height on options to be usable before the window is created 55 | options.browserWindow.width = 56 | // Note: not using `options.browserWindow.width || DEFAULT_WINDOW_WIDTH` so 57 | // that users can put a 0 width 58 | options.browserWindow.width !== undefined 59 | ? options.browserWindow.width 60 | : DEFAULT_WINDOW_WIDTH; 61 | options.browserWindow.height = 62 | options.browserWindow.height !== undefined 63 | ? options.browserWindow.height 64 | : DEFAULT_WINDOW_HEIGHT; 65 | 66 | return options as Options; 67 | } 68 | -------------------------------------------------------------------------------- /src/util/getWindowPosition.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utilities to get taskbar position and consequently menubar's position 3 | */ 4 | 5 | /** */ 6 | 7 | import { type Rectangle, type Tray, screen as electronScreen } from 'electron'; 8 | 9 | const isLinux = process.platform === 'linux'; 10 | 11 | const trayToScreenRects = (tray: Tray): [Rectangle, Rectangle] => { 12 | // There may be more than one screen, so we need to figure out on which screen our tray icon lives. 13 | const { workArea, bounds: screenBounds } = electronScreen.getDisplayMatching( 14 | tray.getBounds(), 15 | ); 16 | 17 | workArea.x -= screenBounds.x; 18 | workArea.y -= screenBounds.y; 19 | 20 | return [screenBounds, workArea]; 21 | }; 22 | 23 | type TaskbarLocation = 'top' | 'bottom' | 'left' | 'right'; 24 | 25 | /** 26 | * Determine taskbard location: "top", "bottom", "left" or "right". 27 | * 28 | * Only tested on Windows for now, and only used in Windows. 29 | * 30 | * @param tray - The Electron Tray instance. 31 | */ 32 | export function taskbarLocation(tray: Tray): TaskbarLocation { 33 | const [screenBounds, workArea] = trayToScreenRects(tray); 34 | 35 | // TASKBAR LEFT 36 | if (workArea.x > 0) { 37 | // Most likely Ubuntu hence assuming the window should be on top 38 | if (isLinux && workArea.y > 0) return 'top'; 39 | // The workspace starts more on the right 40 | return 'left'; 41 | } 42 | 43 | // TASKBAR TOP 44 | if (workArea.y > 0) { 45 | return 'top'; 46 | } 47 | 48 | // TASKBAR RIGHT 49 | // Here both workArea.y and workArea.x are 0 so we can no longer leverage them. 50 | // We can use the workarea and display width though. 51 | // Determine taskbar location 52 | if (workArea.width < screenBounds.width) { 53 | // The taskbar is either on the left or right, but since the LEFT case was handled above, 54 | // we can be sure we're dealing with a right taskbar 55 | return 'right'; 56 | } 57 | 58 | // TASKBAR BOTTOM 59 | // Since all the other cases were handled, we can be sure we're dealing with a bottom taskbar 60 | return 'bottom'; 61 | } 62 | 63 | type WindowPosition = 64 | | 'trayCenter' 65 | | 'topRight' 66 | | 'trayBottomCenter' 67 | | 'bottomLeft' 68 | | 'bottomRight'; 69 | 70 | /** 71 | * Depending on where the taskbar is, determine where the window should be 72 | * positioned. 73 | * 74 | * @param tray - The Electron Tray instance. 75 | */ 76 | export function getWindowPosition(tray: Tray): WindowPosition { 77 | switch (process.platform) { 78 | // macOS 79 | // Supports top taskbars 80 | case 'darwin': 81 | return 'trayCenter'; 82 | // Linux 83 | // Windows 84 | // Supports top/bottom/left/right taskbar 85 | case 'linux': 86 | case 'win32': { 87 | const traySide = taskbarLocation(tray); 88 | 89 | // Assign position for menubar 90 | if (traySide === 'top') { 91 | return isLinux ? 'topRight' : 'trayCenter'; 92 | } 93 | if (traySide === 'bottom') { 94 | return 'bottomRight'; 95 | } 96 | if (traySide === 'left') { 97 | return 'bottomLeft'; 98 | } 99 | if (traySide === 'right') { 100 | return 'bottomRight'; 101 | } 102 | } 103 | } 104 | 105 | // When we really don't know, we just show the menubar on the top-right 106 | return 'topRight'; 107 | } 108 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "strict": true, 6 | "declaration": true, 7 | "outDir": "./lib", 8 | "skipLibCheck": true, 9 | "target": "es5" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /typedoc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | exclude: ['**/*spec.ts', '**/__mocks__/**'], 3 | excludeExternals: true, 4 | excludeNotExported: true, 5 | excludePrivate: true, 6 | excludeProtected: true, 7 | hideGenerator: true, 8 | includes: './src', 9 | module: 'commonjs', 10 | out: 'docs', 11 | stripInternal: 'true', 12 | theme: 'markdown', 13 | }; 14 | --------------------------------------------------------------------------------