Full Code of max-mapper/menubar for AI

master 54eec00351a9 cached
53 files
71.6 KB
21.9k tokens
35 symbols
1 requests
Download .txt
Repository: max-mapper/menubar
Branch: master
Commit: 54eec00351a9
Files: 53
Total size: 71.6 KB

Directory structure:
gitextract_dw9q4gt_/

├── .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
├── 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
│   ├── hello-world/
│   │   ├── README.md
│   │   ├── index.html
│   │   ├── main.js
│   │   └── package.json
│   ├── icon-animation/
│   │   ├── index.html
│   │   ├── main.js
│   │   └── package.json
│   ├── native-menu/
│   │   ├── README.md
│   │   ├── main.js
│   │   └── package.json
│   └── package.json
├── 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

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
[//]: # "Hey there and thank you for using Menubar's issue tracker!"
[//]: # "Please fill in these information to describe your issue."

### Description

### Steps to Reproduce the Problem

1. ...
2. ...
3. ...

### Expected Behaviour

### Actual Behaviour

### Specifications

- Menubar version:
- Platform:
- Electron version: (Run `electron -v` or `node_modules/.bin/electron -v` to get it)

### Other information


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: npm
  directory: "/"
  schedule:
    interval: weekly
    time: "02:00"
    timezone: Europe/Berlin
  open-pull-requests-limit: 10


================================================
FILE: .github/workflows/lint.yml
================================================
name: Linting

on:
    push:
        branches:
            - master
    pull_request:

jobs:
    lint:
        name: Lint
        runs-on: ubuntu-latest

        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-node@v4
              with:
                  node-version-file: '.nvmrc'
            - run: yarn install
            - run: yarn lint


================================================
FILE: .github/workflows/test.yml
================================================
name: Run Tests

on:
    push:
        branches:
            - master
    pull_request:

jobs:
    lint:
        name: Run Tests
        runs-on: macos-latest

        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-node@v4
              with:
                  node-version-file: '.nvmrc'
            - run: yarn install
            - run: yarn test


================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
coverage
lib
dist
*.app
*.log


================================================
FILE: .nvmrc
================================================
20


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": ["biomejs.biome"]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.organizeImports.biome": "explicit",
    "quickfix.biome": "explicit"
  },
  "editor.defaultFormatter": "biomejs.biome"
}


================================================
FILE: CHANGELOG.md
================================================
# Changelog

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.

### [9.5.2](https://github.com/maxogden/menubar/compare/v9.5.1...v9.5.2) (2025-10-10)

### [9.5.1](https://github.com/maxogden/menubar/compare/v9.5.0...v9.5.1) (2024-10-11)

## [9.5.0](https://github.com/maxogden/menubar/compare/v9.4.0...v9.5.0) (2024-06-25)


### Features

* widen electron support ([#473](https://github.com/maxogden/menubar/issues/473)) ([bd3beb2](https://github.com/maxogden/menubar/commit/bd3beb2c2bc9700f4738a4e0f25085aa8a0f5e86))


### Bug Fixes

* Fix for Broken Native Menubar Example ([#471](https://github.com/maxogden/menubar/issues/471)) ([b000c86](https://github.com/maxogden/menubar/commit/b000c86777cb0cf6fa883fb0256410981e154ce1))
* win32 position ([#479](https://github.com/maxogden/menubar/issues/479)) ([9e5cb86](https://github.com/maxogden/menubar/commit/9e5cb86a276f29efd36d10a07e27fa820343f880))

## [9.4.0](https://github.com/maxogden/menubar/compare/v9.3.0...v9.4.0) (2023-11-28)


### Features

* Update electron to 27 ([#458](https://github.com/maxogden/menubar/issues/458)) ([f3dbac6](https://github.com/maxogden/menubar/commit/f3dbac66582baa322c2c8caa5027f1b8ef480e67))


### Bug Fixes

* 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))

## [9.3.0](https://github.com/maxogden/menubar/compare/v9.2.3...v9.3.0) (2023-02-13)


### Features

* add electron 22 support ([#414](https://github.com/maxogden/menubar/issues/414)) ([a39382f](https://github.com/maxogden/menubar/commit/a39382fe843953395bf99267ee83102def70b06e))

### [9.2.3](https://github.com/maxogden/menubar/compare/v9.2.2...v9.2.3) (2022-10-05)


### Features

* electron 21 support ([#382](https://github.com/maxogden/menubar/issues/382)) ([c9d5b61](https://github.com/maxogden/menubar/commit/c9d5b61da2b55e20e9b2d0f4c0181c57f33a27c3))

### [9.2.2](https://github.com/maxogden/menubar/compare/v9.2.1...v9.2.2) (2022-09-05)


### Features

* Add Electron 20 as a peerDependency ([#379](https://github.com/maxogden/menubar/issues/379)) ([4c027b2](https://github.com/maxogden/menubar/commit/4c027b22499c7c83a4d87b9aa17d5e86f3172b68))


### Bug Fixes

* arrow example problem ([#342](https://github.com/maxogden/menubar/issues/342)) ([64b80d5](https://github.com/maxogden/menubar/commit/64b80d5f49ebb367b75e21e88af15fdc874cda86))

### [9.2.1](https://github.com/maxogden/menubar/compare/v9.2.0...v9.2.1) (2022-07-11)


### Bug Fixes

* Add support for electron 19 ([#376](https://github.com/maxogden/menubar/issues/376)) ([6d0feb0](https://github.com/maxogden/menubar/commit/6d0feb0e492681200966dde10a63a73cfb503138))

## [9.2.0](https://github.com/maxogden/menubar/compare/v9.1.2...v9.2.0) (2022-04-11)


### Features

* add `before-load` event ([#370](https://github.com/maxogden/menubar/issues/370)) ([3247986](https://github.com/maxogden/menubar/commit/3247986f164d8b2fb2912ab33beb25c9c6d8ece3))

### [9.1.2](https://github.com/maxogden/menubar/compare/v9.1.1...v9.1.2) (2022-03-29)


### Bug Fixes

* Add electron 17 support ([#373](https://github.com/maxogden/menubar/issues/373)) ([fafc29f](https://github.com/maxogden/menubar/commit/fafc29f168e91369ad5d60a61abc0363076c303a))

### [9.1.1](https://github.com/maxogden/menubar/compare/v9.1.0...v9.1.1) (2021-12-09)


### Bug Fixes

* Add electron 16 support ([#364](https://github.com/maxogden/menubar/issues/364)) ([325e151](https://github.com/maxogden/menubar/commit/325e1517cfc407bc0ccc98be776d8d9590d6f119))

## [9.1.0](https://github.com/maxogden/menubar/compare/v9.0.6...v9.1.0) (2021-10-27)


### Features

* 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))

### [9.0.6](https://github.com/maxogden/menubar/compare/v9.0.5...v9.0.6) (2021-10-15)


### Bug Fixes

* Add support for electron 14 and 15 ([#358](https://github.com/maxogden/menubar/issues/358)) ([8eaba8c](https://github.com/maxogden/menubar/commit/8eaba8cdfcd3ce9427ee3ffd65b3426c62f65048))

### [9.0.5](https://github.com/maxogden/menubar/compare/v9.0.4...v9.0.5) (2021-06-28)


### Bug Fixes

* Add support for Electron 13 ([#347](https://github.com/maxogden/menubar/issues/347)) ([fbf07bd](https://github.com/maxogden/menubar/commit/fbf07bd0bd24b2aac26cdd1db61eb55924f3ee63))
* 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))

### [9.0.4](https://github.com/maxogden/menubar/compare/v9.0.3...v9.0.4) (2021-05-03)


### Bug Fixes

* Add support for Electron 12 ([#332](https://github.com/maxogden/menubar/issues/332)) ([c1f055d](https://github.com/maxogden/menubar/commit/c1f055daed76be2d0f408fda5d4835defcd59dcc))

### [9.0.3](https://github.com/maxogden/menubar/compare/v9.0.2...v9.0.3) (2021-02-24)


### Bug Fixes

* Add support for Electron 10 and 11 ([#321](https://github.com/maxogden/menubar/issues/321)) ([4a89656](https://github.com/maxogden/menubar/commit/4a8965628a0a1a7a14602fef3add7bef436a508f))

### [9.0.2](https://github.com/maxogden/menubar/compare/v9.0.1...v9.0.2) (2021-01-20)


### Bug Fixes

* Improve 'windows' OS detection of taskbar location ([#307](https://github.com/maxogden/menubar/issues/307)) ([4726584](https://github.com/maxogden/menubar/commit/4726584664148a57656c40872836ebba2d030980))

### [9.0.1](https://github.com/maxogden/menubar/compare/v9.0.0...v9.0.1) (2020-05-28)


### Bug Fixes

* calling showWindow() prevents menubar window from closing ([#287](https://github.com/maxogden/menubar/issues/287)) ([53d8f82](https://github.com/maxogden/menubar/commit/53d8f82b604ad5555f59108a97234ebf32e43f80))

## [9.0.0](https://github.com/maxogden/menubar/compare/v8.0.2...v9.0.0) (2020-05-27)


### ⚠ BREAKING CHANGES

* Please use Electron 9 with this menubar version.

### Features

* Support Electron 9 ([#286](https://github.com/maxogden/menubar/issues/286)) ([44cf1b1](https://github.com/maxogden/menubar/commit/44cf1b1e6cce83f9e63a39f1d32fbb664396e7bc))

### [8.0.2](https://github.com/maxogden/menubar/compare/v8.0.1...v8.0.2) (2020-04-27)


### Bug Fixes

* Show window on dock icon click ([#279](https://github.com/maxogden/menubar/issues/279)) ([a8607fa](https://github.com/maxogden/menubar/commit/a8607fa708d229d9124471127482fe461198f1f3))
* 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))

### [8.0.1](https://github.com/maxogden/menubar/compare/v8.0.0...v8.0.1) (2020-03-14)


### Bug Fixes

* **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))

## [8.0.0](https://github.com/maxogden/menubar/compare/v7.2.0...v8.0.0) (2020-02-10)


### ⚠ BREAKING CHANGES

* Menubar's recommended peer dependency is `electron@^8.0.0`

### Features

* Support Electron 8 ([#268](https://github.com/maxogden/menubar/issues/268)) ([ad99c5a](https://github.com/maxogden/menubar/commit/ad99c5add02ab6d0d751cf6bda8a2c96c674620f))

## [7.2.0](https://github.com/maxogden/menubar/compare/v7.1.0...v7.2.0) (2020-01-16)


### Features

* Adding a loadUrlOptions option ([#263](https://github.com/maxogden/menubar/issues/263)) ([8e6bd01](https://github.com/maxogden/menubar/commit/8e6bd0154aaee02a5d601bbe37c51c55065c3923))

## [7.1.0](https://github.com/maxogden/menubar/compare/v7.0.0...v7.1.0) (2019-11-25)


### Features

* Allow skipping loadUrl ([#257](https://github.com/maxogden/menubar/issues/257)) ([095486a](https://github.com/maxogden/menubar/commit/095486ab338df26fc4d6a1e7a658cfa9fa4a69b7))

# [7.0.0](https://github.com/maxogden/menubar/compare/v6.0.8...v7.0.0) (2019-10-23)


* feat!: Support Electron 7 (#250) ([b54dce5](https://github.com/maxogden/menubar/commit/b54dce5)), closes [#250](https://github.com/maxogden/menubar/issues/250)


### BREAKING CHANGES

* - Drop support for Electron 4, 5, and 6.
- Remove deprecated passing string argument to `menubar`, use `dir` field instead
```diff
- menubar('/home/me/MY_ABSOLUTE_PATH');
+ menubar({ dir: '/home/me/MY_ABSOLUTE_PATH' });
```
- Remove deprecated passing `x`, `y`, `height`, `width`, `alwaysOnTop` fields to `menubar`, pass them instead into the `browserWindow` field
```diff
- menubar({
-   x: 12,
-   y: 34,
-   height: 500,
-   width: 320,
-   alwaysOnTop: true
- });
+ menubar({
+   browserWindow: {
+     x: 12,
+     y: 34,
+     height: 500,
+     width: 320,
+     alwaysOnTop: true
+  }
+ });
```



## [6.0.8](https://github.com/maxogden/menubar/compare/v6.0.7...v6.0.8) (2019-09-16)


### Bug Fixes

* Move doc tool to `devDependencies` ([#245](https://github.com/maxogden/menubar/issues/245)) ([2756c7a](https://github.com/maxogden/menubar/commit/2756c7a))



## [6.0.7](https://github.com/maxogden/menubar/compare/v6.0.6...v6.0.7) (2019-07-31)


### Bug Fixes

* Support Electron 6 ([#242](https://github.com/maxogden/menubar/issues/242)) ([1fd9bd7](https://github.com/maxogden/menubar/commit/1fd9bd7))



## [6.0.6](https://github.com/maxogden/menubar/compare/v6.0.5...v6.0.6) (2019-07-02)


### Bug Fixes

* Fix crash on windows position ([#235](https://github.com/maxogden/menubar/issues/235)) ([cbbe175](https://github.com/maxogden/menubar/commit/cbbe175))



## [6.0.5](https://github.com/maxogden/menubar/compare/v6.0.4...v6.0.5) (2019-06-11)


### Bug Fixes

* Remove postinstall, export taskbarLocation ([#226](https://github.com/maxogden/menubar/issues/226)) ([941b3be](https://github.com/maxogden/menubar/commit/941b3be))



## [6.0.4](https://github.com/maxogden/menubar/compare/v6.0.3...v6.0.4) (2019-06-11)


### Bug Fixes

* 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)
* Fix double 'after-hide' event ([#216](https://github.com/maxogden/menubar/issues/216)) ([a4d900e](https://github.com/maxogden/menubar/commit/a4d900e))



## [6.0.3](https://github.com/maxogden/menubar/compare/v6.0.2...v6.0.3) (2019-06-05)


### Bug Fixes

* Fix accessing Menubar.window ([#214](https://github.com/maxogden/menubar/issues/214)) ([cd5ef73](https://github.com/maxogden/menubar/commit/cd5ef73))



## [6.0.2](https://github.com/maxogden/menubar/compare/v6.0.1...v6.0.2) (2019-05-31)


### Bug Fixes

* Use cat icon if no icon provided ([#205](https://github.com/maxogden/menubar/issues/205)) ([fc02e02](https://github.com/maxogden/menubar/commit/fc02e02))



## [6.0.1](https://github.com/maxogden/menubar/compare/v6.0.0...v6.0.1) (2019-05-31)


### Bug Fixes

* Fix changelog links ([#204](https://github.com/maxogden/menubar/issues/204)) ([de96756](https://github.com/maxogden/menubar/commit/de96756))



# [6.0.0](https://github.com/maxogden/menubar/compare/v5.2.3...v6.0.0) (2019-05-31)


### Bug Fixes

* Update to Electron 5 ([#15](https://github.com/amaurym/g/menubar/issues/15)) ([ce86216](https://github.com/maxogden/menubar/commit/ce86216))
* 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))


### Code Refactoring

* Convert all codebase to typescript ([#2](https://github.com/amaurym/g/menubar/issues/2)) ([820d41a](https://github.com/maxogden/menubar/commit/820d41a))


### Features

* 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))
* Support Electron.NativeImage icon argument ([#7](https://github.com/amaurym/g/menubar/issues/7)) ([03d67f3](https://github.com/maxogden/menubar/commit/03d67f3))


### BREAKING CHANGES

* We're using a named export in Typescript now:
```diff
- var menubar = require('menubar');
+ var { menubar } = require('menubar');
```

Alternatively, using ES6/TS syntax:
```javascript
import { menubar } from 'menubar';
```


================================================
FILE: COLLABORATORS.md
================================================
## Collaborators

menubar is only possible due to the excellent work of the following collaborators:

<table>
<tbody>
<tr><th align="left">maxogden</th><td><a href="https://github.com/maxogden">GitHub/maxogden</a></td></tr>
<tr><th align="left">fritzy</th><td><a href="https://github.com/fritzy">GitHub/fritzy</a></td></tr>
<tr><th align="left">pquerna</th><td><a href="https://github.com/pquerna">GitHub/pquerna</a></td></tr>
<tr><th align="left">fabien-d</th><td><a href="https://github.com/fabien-d">GitHub/fabien-d</a></td></tr>
<tr><th align="left">jenslind</th><td><a href="https://github.com/jenslind">GitHub/jenslind</a></td></tr>
<tr><th align="left">amaurym</th><td><a href="https://github.com/amaurym">GitHub/amaurym</a></td></tr>
</tbody>
</table>


================================================
FILE: CONTRIBUTING.md
================================================
## Before opening issues

**If you are asking a question**:

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)

**For bug reports/technical issues**:

Please provide the following information when opening issues:

- Which version of menubar are you using?
- What cli arguments are you passing?
- What platform are you running menubar on?
- Is there a stack trace in the error message you're seeing?
- If possible, please provide instructions to reproduce your problem.

Thanks!

## Releases

- commit your changes
- `npm version <major|minor|patch>`
- `git push && git push --tags` (or `git push` with `git config --global push.followTags true` on latest git)
- `npm publish`


================================================
FILE: LICENSE
================================================
BSD 2-Clause License

Copyright (c) 2015-2019, Max Ogden
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/max-mapper/menubar/test.yml)
[![npm (scoped)](https://img.shields.io/npm/v/menubar.svg)](https://www.npmjs.com/package/menubar)
![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/max-mapper/menubar)
![npm bundle size](https://img.shields.io/bundlephobia/minzip/menubar.svg)
![npm bundle size](https://img.shields.io/bundlephobia/min/menubar.svg)

<br /><br /><br />

<h1 align="center">➖ Menubar</h1>
<h4 align="center">High level way to create menubar desktop applications with Electron.</h4>

<br /><br /><br />

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.

✅ Only one dependency, and one peer-dependency.

✅ Works on macOS, Windows and most Linuxes. See [details](./WORKING_PLATFORMS.md).

✅ 💥 [**3.6kB minified + gzipped**](https://bundlephobia.com/result?p=menubar) 💥

| <img src="assets/screenshot-macos-dark.png" height="250px" /> | <img src="assets/screenshot-windows.png" height="250px" /> | <img src="assets/screenshot-linux.png" height="250px" /> |
| :-----------------------------------------------------------: | :--------------------------------------------------------: | :------------------------------------------------------: |
|                      macOS Mojave 10.14                       |                         Windows 10                         |                       Ubuntu 18.04                       |

## Installation

```bash
yarn add menubar
```

## Usage

Starting with your own new project, run these commands:

```bash
$ yarn add menubar
$ touch myApp.js
$ touch index.html
```

Fill `index.html` with some HTML, and `myApp.js` like this:

```javascript
const { menubar } = require('menubar');

const mb = menubar();

mb.on('ready', () => {
  console.log('app is ready');
  // your app code here
});
```

Then use `electron` to run the app:

```bash
$ electron myApp.js
```

Alternatively, see [`examples/hello-world`](/examples/hello-world) folder for a simple working example.

## `Menubar` Class

The return value of `menubar()` is a `Menubar` class instance, which has these properties:

- `app`: the [Electron App](https://electronjs.org/docs/api/app) instance,
- `window`: the [Electron Browser Window](https://electronjs.org/docs/api/browser-window) instance,
- `tray`: the [Electron Tray](https://electronjs.org/docs/api/tray) instance,
- `positioner`: the [Electron Positioner](https://github.com/jenslind/electron-positioner) instance,
- `setOption(option, value)`: change an option after menubar is created,
- `getOption(option)`: get an menubar option,
- `showWindow()`: show the menubar window,
- `hideWindow()`: hide the menubar window

See the reference [API docs](./docs/classes/_menubar_.menubar.md).

## `menubar()` Options

You can pass an optional options object into the `menubar({ ... })` function:

- `dir` (default `process.cwd()`) - the app source directory
- `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.
- `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:
  - `x` (default `undefined`) - the x position of the window
  - `y` (default `undefined`) - the y position of the window
  - `width` (default 400) - window width
  - `height` (default 400) - window height
  - `alwaysOnTop` (default false) - if true, the window will not hide on blur
- `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.
- `tooltip` (default empty) - menubar tray icon tooltip text
- `tray` (default created on-the-fly) - an electron `Tray` instance. if provided `opts.icon` will be ignored
- `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.
- `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)
- `showOnAllWorkspaces` (default true) - Makes the window available on all OS X workspaces.
- `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.
- `showDockIcon` (default false) - Configure the visibility of the application dock icon.
- `showOnRightClick` (default false) - Show the window on 'right-click' event instead of regular 'click'

See the reference [API docs](./docs/interfaces/_types_.options.md).

## Events

The `Menubar` class is an event emitter:

- `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
- `create-window` - the line before `new BrowserWindow()` is called
- `before-load` - after create window, before loadUrl (can be used for `require("@electron/remote/main").enable(webContents)`)
- `after-create-window` - the line after all window init code is done and url was loaded
- `show` - the line before `window.show()` is called
- `after-show` - the line after `window.show()` is called
- `hide` - the line before `window.hide()` is called (on window blur)
- `after-hide` - the line after `window.hide()` is called
- `after-close` - after the `.window` (BrowserWindow) property has been deleted
- `focus-lost` - emitted if always-on-top option is set and the user clicks away

## Compatibility with Electron

| menubar  | Electron                   | Notes                                                                                                                      |
| -------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| 9.x.x    | >= 9.x.x <= 34.x.x |                                                                                                                            |
| 8.x.x    | 8.x.xx                      |                                                                                                                            |
| 7.x.x    | 7.x.xx                      |                                                                                                                            |
| 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) |
| <= 5.x.x | <= 3.x.x                   | Please, _please_ don't use these old versions                                                                              |

## API Docs

See the reference [API docs](./docs/globals.md).

## Tips

- 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()`
- Use `mb.on('focus-lost')` if you would like to perform some operation when using the option `browserWindow.alwaysOnTop: true`
- To restore focus of previous window after menubar hide, use `mb.on('after-hide', () => { mb.app.hide() } )` or similar
- 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.
- 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');`


================================================
FILE: WORKING_PLATFORMS.md
================================================
# Platforms where `menubar` is known to work

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!

## macOS

| Version           | Working Status | Known Issues                                                                                                   |
| ----------------- | -------------- | -------------------------------------------------------------------------------------------------------------- |
| 14.15 Sonoma      | ✅ Good        |                                                                                                                |
| 10.14 Mojave      | ✅ Good        | [#147](https://github.com/maxogden/menubar/issues/147), [#215](https://github.com/maxogden/menubar/issues/215) |
| 10.13 High Sierra | ✅ Good        |                                                                                                                |

## Windows

| Version    | Working Status | Known Issues |
| ---------- | -------------- | ------------ |
| Windows 11 | ✅ Good        |              |
| Windows 10 | ✅ Good        |              |
| Windows 8  | ✅ Good        |              |

## Linux

| Distribution  | Desktop Environment | Working Status | Known Issues                                           |
| ------------- | ------------------- | -------------- | ------------------------------------------------------ |
| openSUSE 13.1 | Xfce 4.10.1         | ❌ Bad         | [#123](https://github.com/maxogden/menubar/issues/123) |
| Ubuntu 18.04  | Unity               | ✅ Good        |                                                        |
| Ubuntu 14.04  | Unity               | ❌ Bad         | [#68](https://github.com/maxogden/menubar/issues/68)   |


================================================
FILE: biome.json
================================================
{
  "$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "style": {
        "noParameterAssign": "warn",
        "noNonNullAssertion": "warn",
        "useNodejsImportProtocol": "warn"
      },
      "suspicious": {
        "noAssignInExpressions": "warn"
      }
    }
  },
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "jsxQuoteStyle": "double"
    }
  },
  "json": {
    "parser": {
      "allowComments": true
    }
  }
}


================================================
FILE: docs/README.md
================================================
> **[menubar](README.md)**

[Globals](globals.md) /

[![Build Status](https://travis-ci.org/maxogden/menubar.svg?branch=master)](https://travis-ci.org/maxogden/menubar)
[![npm (scoped)](https://img.shields.io/npm/v/menubar.svg)](https://www.npmjs.com/package/@maxogden/menubar)
[![dependencies Status](https://david-dm.org/maxogden/menubar/status.svg)](https://david-dm.org/maxogden/menubar)
![npm bundle size](https://img.shields.io/bundlephobia/minzip/menubar.svg)
![npm bundle size](https://img.shields.io/bundlephobia/min/menubar.svg)

<br /><br /><br />

<h1 align="center">➖ Menubar</h1>
<h4 align="center">High level way to create menubar desktop applications with Electron.</h4>

<br /><br /><br />

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.

✅ Only one dependency, and one peer-dependency.

✅ Works on macOS, Windows and most Linuxes. See [details](./WORKING_PLATFORMS.md).

✅ 💥 [**3.6kB minified + gzipped**](https://bundlephobia.com/result?p=menubar) 💥

| <img src="assets/screenshot-macos-dark.png" height="250px" /> | <img src="assets/screenshot-windows.png" height="250px" /> | <img src="assets/screenshot-linux.png" height="250px" /> |
| :-----------------------------------------------------------: | :--------------------------------------------------------: | :------------------------------------------------------: |
|                      macOS Mojave 10.14                       |                         Windows 10                         |                       Ubuntu 18.04                       |

## Installation

```bash
yarn add menubar
```

## Usage

Starting with your own new project, run these commands:

```bash
$ yarn add menubar
$ touch myApp.js
$ touch index.html
```

Fill `index.html` with some HTML, and `myApp.js` like this:

```javascript
const { menubar } = require('menubar');

const mb = menubar();

mb.on('ready', () => {
  console.log('app is ready');
  // your app code here
});
```

Then use `electron` to run the app:

```bash
$ electron myApp.js
```

Alternatively, see [`examples/hello-world`](/examples/hello-world) folder for a simple working example.

## `Menubar` Class

The return value of `menubar()` is a `Menubar` class instance, which has these properties:

- `app`: the [Electron App](https://electronjs.org/docs/api/app) instance,
- `window`: the [Electron Browser Window](https://electronjs.org/docs/api/browser-window) instance,
- `tray`: the [Electron Tray](https://electronjs.org/docs/api/tray) instance,
- `positioner`: the [Electron Positioner](https://github.com/jenslind/electron-positioner) instance,
- `setOption(option, value)`: change an option after menubar is created,
- `getOption(option)`: get an menubar option,
- `showWindow()`: show the menubar window,
- `hideWindow()`: hide the menubar window

See the reference [API docs](./docs/classes/_menubar_.menubar.md).

## `menubar()` Options

You can pass an optional options object into the `menubar({ ... })` function:

- `dir` (default `process.cwd()`) - the app source directory
- `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.
- `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:
  - `x` (default `undefined`) - the x position of the window
  - `y` (default `undefined`) - the y position of the window
  - `width` (default 400) - window width
  - `height` (default 400) - window height
  - `alwaysOnTop` (default false) - if true, the window will not hide on blur
- `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.
- `tooltip` (default empty) - menubar tray icon tooltip text
- `tray` (default created on-the-fly) - an electron `Tray` instance. if provided `opts.icon` will be ignored
- `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.
- `showOnAllWorkspaces` (default true) - Makes the window available on all OS X workspaces.
- `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.
- `showDockIcon` (default false) - Configure the visibility of the application dock icon.
- `showOnRightClick` (default false) - Show the window on 'right-click' event instead of regular 'click'

See the reference [API docs](./docs/interfaces/_types_.options.md).

## Events

The `Menubar` class is an event emitter:

- `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
- `create-window` - the line before `new BrowserWindow()` is called
- `after-create-window` - the line after all window init code is done
- `show` - the line before `window.show()` is called
- `after-show` - the line after `window.show()` is called
- `hide` - the line before `window.hide()` is called (on window blur)
- `after-hide` - the line after `window.hide()` is called
- `after-close` - after the `.window` (BrowserWindow) property has been deleted
- `focus-lost` - emitted if always-on-top option is set and the user clicks away

## Compatibility with Electron

| menubar  | Electron                | Notes                                                                                                                      |
| -------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| 7.x.x    | 7.x.x                   |
| 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) |
| <= 5.x.x | <= 3.x.x                | Please, _please_ don't use these old versions                                                                              |

## API Docs

See the reference [API docs](./docs/globals.md).

## Tips

- 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()`
- Use `mb.on('focus-lost')` if you would like to perform some operation when using the option `browserWindow.alwaysOnTop: true`
- To restore focus of previous window after menubar hide, use `mb.on('after-hide', () => { mb.app.hide() } )` or similar
- 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.
- 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');`

================================================
FILE: docs/classes/_menubar_.menubar.md
================================================
> **[menubar](../README.md)**

[Globals](../globals.md) / ["Menubar"](../modules/_menubar_.md) / [Menubar](_menubar_.menubar.md) /

# Class: Menubar

## Hierarchy

* `EventEmitter`

  * **Menubar**

## Index

### Constructors

* [constructor](_menubar_.menubar.md#constructor)

### Accessors

* [app](_menubar_.menubar.md#app)
* [positioner](_menubar_.menubar.md#positioner)
* [tray](_menubar_.menubar.md#tray)
* [window](_menubar_.menubar.md#window)

### Methods

* [getOption](_menubar_.menubar.md#getoption)
* [hideWindow](_menubar_.menubar.md#hidewindow)
* [setOption](_menubar_.menubar.md#setoption)
* [showWindow](_menubar_.menubar.md#showwindow)

## Constructors

###  constructor

\+ **new Menubar**(`app`: `App`, `options?`: `Partial<Options>`): *[Menubar](_menubar_.menubar.md)*

*Defined in [Menubar.ts:24](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L24)*

**Parameters:**

Name | Type |
------ | ------ |
`app` | `App` |
`options?` | `Partial<Options>` |

**Returns:** *[Menubar](_menubar_.menubar.md)*

## Accessors

###  app

• **app**:

*Defined in [Menubar.ts:47](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L47)*

___

###  positioner

• **positioner**:

*Defined in [Menubar.ts:56](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L56)*

___

###  tray

• **tray**:

*Defined in [Menubar.ts:69](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L69)*

___

###  window

• **window**:

*Defined in [Menubar.ts:83](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L83)*

## Methods

###  getOption

▸ **getOption**<**K**>(`key`: `K`): *`Options[K]`*

*Defined in [Menubar.ts:92](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L92)*

**Type parameters:**

▪ **K**: *keyof Options*

**Parameters:**

Name | Type | Description |
------ | ------ | ------ |
`key` | `K` | The option key to retrieve, see [Options](../interfaces/_types_.options.md).  |

**Returns:** *`Options[K]`*

___

###  hideWindow

▸ **hideWindow**(): *void*

*Defined in [Menubar.ts:99](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L99)*

**Returns:** *void*

___

###  setOption

▸ **setOption**<**K**>(`key`: `K`, `value`: `Options[K]`): *void*

*Defined in [Menubar.ts:115](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L115)*

**Type parameters:**

▪ **K**: *keyof Options*

**Parameters:**

Name | Type | Description |
------ | ------ | ------ |
`key` | `K` | The option key to modify, see [Options](../interfaces/_types_.options.md). |
`value` | `Options[K]` | The value to set.  |

**Returns:** *void*

___

###  showWindow

▸ **showWindow**(`trayPos?`: `Electron.Rectangle`): *`Promise<void>`*

*Defined in [Menubar.ts:124](https://github.com/adam-lynch/menubar/blob/6b93752/src/Menubar.ts#L124)*

**Parameters:**

Name | Type | Description |
------ | ------ | ------ |
`trayPos?` | `Electron.Rectangle` | The bounds to show the window in.  |

**Returns:** *`Promise<void>`*

================================================
FILE: docs/globals.md
================================================
> **[menubar](README.md)**

[Globals](globals.md) /

# menubar

## Index

### External modules

* ["Menubar"](modules/_menubar_.md)
* ["index"](modules/_index_.md)
* ["types"](modules/_types_.md)
* ["util/getWindowPosition"](modules/_util_getwindowposition_.md)

================================================
FILE: docs/interfaces/_types_.options.md
================================================
> **[menubar](../README.md)**

[Globals](../globals.md) / ["types"](../modules/_types_.md) / [Options](_types_.options.md) /

# Interface: Options

## Hierarchy

* **Options**

## Table of Contents

### Properties

* [browserWindow](_types_.options.md#browserwindow)
* [dir](_types_.options.md#dir)
* [icon](_types_.options.md#optional-icon)
* [index](_types_.options.md#index)
* [loadUrlOptions](_types_.options.md#optional-loadurloptions)
* [preloadWindow](_types_.options.md#optional-preloadwindow)
* [showDockIcon](_types_.options.md#optional-showdockicon)
* [showOnAllWorkspaces](_types_.options.md#optional-showonallworkspaces)
* [showOnRightClick](_types_.options.md#optional-showonrightclick)
* [tooltip](_types_.options.md#tooltip)
* [tray](_types_.options.md#optional-tray)
* [windowPosition](_types_.options.md#optional-windowposition)

## Properties

###  browserWindow

• **browserWindow**: *`BrowserWindowConstructorOptions`*

*Defined in [types.ts:23](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L23)*

___

###  dir

• **dir**: *string*

*Defined in [types.ts:27](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L27)*

___

### `Optional` icon

• **icon**? : *string | `NativeImage`*

*Defined in [types.ts:34](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L34)*

___

###  index

• **index**: *string | false*

*Defined in [types.ts:43](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L43)*

___

### `Optional` loadUrlOptions

• **loadUrlOptions**? : *`LoadURLOptions`*

*Defined in [types.ts:51](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L51)*

___

### `Optional` preloadWindow

• **preloadWindow**? : *undefined | false | true*

*Defined in [types.ts:56](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L56)*

___

### `Optional` showDockIcon

• **showDockIcon**? : *undefined | false | true*

*Defined in [types.ts:61](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L61)*

___

### `Optional` showOnAllWorkspaces

• **showOnAllWorkspaces**? : *undefined | false | true*

*Defined in [types.ts:66](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L66)*

___

### `Optional` showOnRightClick

• **showOnRightClick**? : *undefined | false | true*

*Defined in [types.ts:70](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L70)*

___

###  tooltip

• **tooltip**: *string*

*Defined in [types.ts:74](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L74)*

___

### `Optional` tray

• **tray**? : *`Tray`*

*Defined in [types.ts:78](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L78)*

___

### `Optional` windowPosition

• **windowPosition**? : *"trayLeft" | "trayBottomLeft" | "trayRight" | "trayBottomRight" | "trayCenter" | "trayBottomCenter" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight" | "topCenter" | "bottomCenter" | "leftCenter" | "rightCenter" | "center"*

*Defined in [types.ts:83](https://github.com/maxogden/menubar/blob/c7d6640/src/types.ts#L83)*


================================================
FILE: docs/modules/_index_.md
================================================
> **[menubar](../README.md)**

[Globals](../globals.md) / ["index"](_index_.md) /

# External module: "index"

## Index

### Functions

* [menubar](_index_.md#menubar)

## Functions

###  menubar

▸ **menubar**(`options?`: `Partial<Options>`): *[Menubar](../classes/_menubar_.menubar.md)*

*Defined in [index.ts:25](https://github.com/adam-lynch/menubar/blob/6b93752/src/index.ts#L25)*

**Parameters:**

Name | Type | Description |
------ | ------ | ------ |
`options?` | `Partial<Options>` | Options for creating a menubar application, see [Options](../interfaces/_types_.options.md)  |

**Returns:** *[Menubar](../classes/_menubar_.menubar.md)*

================================================
FILE: docs/modules/_menubar_.md
================================================
> **[menubar](../README.md)**

[Globals](../globals.md) / ["Menubar"](_menubar_.md) /

# External module: "Menubar"

## Index

### Classes

* [Menubar](../classes/_menubar_.menubar.md)

================================================
FILE: docs/modules/_types_.md
================================================
> **[menubar](../README.md)**

[Globals](../globals.md) / ["types"](_types_.md) /

# External module: "types"

## Index

### Interfaces

* [Options](../interfaces/_types_.options.md)

================================================
FILE: docs/modules/_util_getwindowposition_.md
================================================
> **[menubar](../README.md)**

[Globals](../globals.md) / ["util/getWindowPosition"](_util_getwindowposition_.md) /

# External module: "util/getWindowPosition"

## Index

### Functions

* [getWindowPosition](_util_getwindowposition_.md#getwindowposition)
* [taskbarLocation](_util_getwindowposition_.md#taskbarlocation)

## Functions

###  getWindowPosition

▸ **getWindowPosition**(`tray`: `Tray`): *`WindowPosition`*

*Defined in [util/getWindowPosition.ts:52](https://github.com/adam-lynch/menubar/blob/6b93752/src/util/getWindowPosition.ts#L52)*

**Parameters:**

Name | Type | Description |
------ | ------ | ------ |
`tray` | `Tray` | The Electron Tray instance.  |

**Returns:** *`WindowPosition`*

___

###  taskbarLocation

▸ **taskbarLocation**(`tray`: `Tray`): *`TaskbarLocation`*

*Defined in [util/getWindowPosition.ts:18](https://github.com/adam-lynch/menubar/blob/6b93752/src/util/getWindowPosition.ts#L18)*

**Parameters:**

Name | Type | Description |
------ | ------ | ------ |
`tray` | `Tray` | The Electron Tray instance.  |

**Returns:** *`TaskbarLocation`*

================================================
FILE: examples/arrow/README.md
================================================
# example-menubar-arrow

## Description

A menubar app with an arrow on top, that gives the app a popover look.

Thanks @leilarrossi for this example, see https://github.com/maxogden/menubar/issues/78.

## Screenshot

![screenshot](./screenshot.png)

## Instructions

- Clone the repository.
- Run `yarn install` from the root folder.
- Run `yarn build` from the root folder.
- `cd` into this directory.
- Run `yarn install` to install this example's dependencies.
- Run `yarn start` from this directory to run app.


================================================
FILE: examples/arrow/index.css
================================================
.myarrow {
  position: relative;
  padding: 12px 0;
}

.myarrow:before {
  content: "";
  height: 0;
  width: 0;
  border-width: 0 8px 12px 8px;
  border-style: solid;
  border-color: transparent transparent #eeeeee transparent;
  position: absolute;
  top: 0px;
  left: 50%;
  transform: translateX(-50%);
}

.page {
  background: #eeeeee;
  width: 340px;
  height: 500px;
  margin: 0 auto;
}

.darwin.page {
  border-radius: 5px;
  overflow: hidden;
}


================================================
FILE: examples/arrow/index.html
================================================
<html>
  <head>
    <title>Example App</title>
    <link rel="stylesheet" href="index.css" />
  </head>
  <body class="myarrow">
    <div class="page darwin">Hello World.</div>
  </body>
</html>


================================================
FILE: examples/arrow/main.js
================================================
const { menubar } = require('../..');

const mb = menubar({
  browserWindow: {
    transparent: true,
    width: 350,
    height: 550,
  },
});

mb.on('ready', () => {
  console.log('Menubar app is ready.');
});


================================================
FILE: examples/arrow/package.json
================================================
{
  "name": "example-menubar-arrow",
  "version": "0.1.0",
  "description": "Example menubar electron app",
  "main": "main.js",
  "private": true,
  "scripts": {
    "start": "electron ."
  }
}


================================================
FILE: examples/hello-world/README.md
================================================
# example-menubar-hello-world

## Description

This is a simple Hello World menubar app that will just open a window that says "Hello World".

## Screenshot

![screenshot](./screenshot.png)

## Instructions

- Clone the repository.
- Run `yarn install` from the root folder.
- Run `yarn build` from the root folder.
- `cd` into this directory.
- Run `yarn install` to install this example's dependencies.
- Run `yarn start` from this directory to run app.


================================================
FILE: examples/hello-world/index.html
================================================
<html>
  <head>
    <title>Example App</title>
  </head>
  <body>
    Hello World.
  </body>
</html>


================================================
FILE: examples/hello-world/main.js
================================================
const { menubar } = require('../..');

const mb = menubar();

mb.on('ready', () => {
  console.log('Menubar app is ready.');
});


================================================
FILE: examples/hello-world/package.json
================================================
{
  "name": "example-menubar-hello-world",
  "version": "0.1.0",
  "description": "Example menubar electron app",
  "main": "main.js",
  "private": true,
  "scripts": {
    "start": "electron ."
  }
}


================================================
FILE: examples/icon-animation/index.html
================================================
<html>
  <head>
    <title>Example App</title>
  </head>
  <body>
    Hello World.
  </body>
</html>


================================================
FILE: examples/icon-animation/main.js
================================================
const { menubar } = require('../..');

const mb = menubar();

mb.on('ready', () => {
  setOkIcon();
  const trayAnimation = setInterval(frame, 1000);
  // simulate data fetching
  sleep(3000).then(() => {
    clearInterval(trayAnimation);
    setOkIcon();
  });
});

function setOkIcon() {
  mb.tray.setImage('state-ok-20.png');
}

function frame() {
  setTimeout(() => mb.tray.setImage('state-sync-20.png'), 300);
  setTimeout(() => mb.tray.setImage('state-sync-20-60.png'), 600);
  setTimeout(() => mb.tray.setImage('state-sync-20-120.png'), 900);
}

function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}


================================================
FILE: examples/icon-animation/package.json
================================================
{
  "name": "example-menubar-icon-animation",
  "version": "0.1.0",
  "description": "Example menubar electron app",
  "main": "main.js",
  "private": true,
  "scripts": {
    "start": "electron ."
  }
}


================================================
FILE: examples/native-menu/README.md
================================================
# example-menubar-native-menu

## Description

This will create a menubar app with a native menu.

Courtesy of @tslater in https://github.com/maxogden/menubar/issues/178.

## Screenshot

![screenshot](./screenshot.png)

## Instructions

- Clone the repository.
- Run `yarn install` from the root folder.
- Run `yarn build` from the root folder.
- `cd` into this directory.
- Run `yarn install` to install this example's dependencies.
- Run `yarn start` from this directory to run app.


================================================
FILE: examples/native-menu/main.js
================================================
const { app, Menu, Tray } = require('electron');
const path = require('path');

const { menubar } = require('../../');

const iconPath = path.join(__dirname, '..', '..', 'assets', 'IconTemplate.png');

app.on('ready', () => {
  const tray = new Tray(iconPath);
  const contextMenu = Menu.buildFromTemplate([
    { label: 'Item1', type: 'radio' },
    { label: 'Item2', type: 'radio' },
    { label: 'Item3', type: 'radio', checked: true },
    { label: 'Item4', type: 'radio' },
  ]);
  tray.setContextMenu(contextMenu);

  const mb = menubar({
    tray,
  });

  mb.on('ready', () => {
    // needed for macos to remove white screen
    // ref: https://github.com/max-mapper/menubar/issues/345
    tray.removeAllListeners();

    console.log('Menubar app is ready.');
    // your app code here
  });
});


================================================
FILE: examples/native-menu/package.json
================================================
{
  "name": "example-menubar-native-menu",
  "version": "0.1.0",
  "description": "Example menubar electron app",
  "main": "main.js",
  "private": true,
  "scripts": {
    "start": "electron ."
  }
}


================================================
FILE: examples/package.json
================================================
{
  "name": "example-menubar",
  "version": "0.1.0",
  "description": "Examples of menubar electron apps",
  "private": true,
  "workspaces": ["arrow", "hello-world", "icon-animation", "native-menu"],
  "devDependencies": {
    "electron": "^34.3.0"
  }
}


================================================
FILE: jest.config.js
================================================
module.exports = {
  collectCoverageFrom: ['**/*.{ts,tsx}', '!**/node_modules/**', '!**/*.d.ts'],
  moduleFileExtensions: ['js', 'ts', 'tsx'],
  rootDir: '.',
  transform: {
    '^.+\\.(ts|tsx)$': 'ts-jest',
  },
  testRegex: 'spec\\.(ts|tsx)$',
};


================================================
FILE: package.json
================================================
{
  "name": "menubar",
  "version": "9.5.2",
  "author": "Max Ogden",
  "bugs": {
    "url": "https://github.com/maxogden/menubar/issues"
  },
  "description": "high level way to create menubar desktop applications with electron",
  "files": [
    "/assets",
    "/lib"
  ],
  "homepage": "https://github.com/maxogden/menubar",
  "keywords": [
    "electron",
    "shell",
    "menubar",
    "menu",
    "taskbar",
    "tray",
    "traybar",
    "mac",
    "linux",
    "windows",
    "app"
  ],
  "license": "BSD-2-Clause",
  "main": "lib/index.js",
  "publishConfig": {
    "access": "public"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/maxogden/menubar.git"
  },
  "scripts": {
    "build": "rimraf lib/ && tsc",
    "deploy": "yarn build && standard-version",
    "docs": "typedoc",
    "lint:check": "biome check",
    "lint": "biome check --fix",
    "test": "jest"
  },
  "types": "lib/index.d.ts",
  "dependencies": {
    "electron-positioner": "^4.1.0"
  },
  "devDependencies": {
    "@biomejs/biome": "^1.9.3",
    "@types/jest": "^25.2.3",
    "electron": "^34.3.0",
    "jest": "^26.0.1",
    "rimraf": "^3.0.2",
    "standard-version": "^8.0.0",
    "ts-jest": "^26.0.0",
    "typedoc": "^0.17.7",
    "typedoc-plugin-markdown": "^2.2.17",
    "typedoc-plugin-no-inherit": "^1.1.10",
    "typescript": "^4.6.2"
  },
  "peerDependencies": {
    "electron": ">=9.0.0 <35.0.0"
  },
  "packageManager": "yarn@1.22.22"
}


================================================
FILE: src/Menubar.spec.ts
================================================
import { BrowserWindow, Tray, app } from 'electron';

import { Menubar } from './Menubar';

describe('Menubar', () => {
  let mb: Menubar | undefined;

  beforeEach(() => {
    mb = new Menubar(app, { preloadWindow: true });
  });

  it('should have property `app`', () => {
    expect(mb!.app).toBeDefined();
  });

  it('should have property `positioner`', () => {
    expect(() => mb!.positioner as unknown).toThrow();
    return new Promise<void>((resolve) => {
      mb!.on('after-create-window', () => {
        expect(mb!.positioner).toBeDefined();
        resolve();
      });
    });
  });

  it('should have property `tray`', () => {
    expect(() => mb!.tray).toThrow();
    return new Promise<void>((resolve) => {
      mb!.on('ready', () => {
        expect(mb!.tray).toBeInstanceOf(Tray);
        resolve();
      });
    });
  });

  it('should have property `window`', () => {
    expect(mb!.window).toBeUndefined();
    return new Promise<void>((resolve) => {
      mb!.on('ready', () => {
        expect(mb!.window).toBeInstanceOf(BrowserWindow);
        resolve();
      });
    });
  });
});


================================================
FILE: src/Menubar.ts
================================================
import { EventEmitter } from 'events';
import fs from 'fs';
import path from 'path';
import { BrowserWindow, Tray } from 'electron';
import Positioner from 'electron-positioner';

import type { Options } from './types';
import { cleanOptions } from './util/cleanOptions';
import { getWindowPosition } from './util/getWindowPosition';

/**
 * The main Menubar class.
 *
 * @noInheritDoc
 */
export class Menubar extends EventEmitter {
  private _app: Electron.App;
  private _browserWindow?: BrowserWindow;
  private _blurTimeout: NodeJS.Timeout | null = null; // track blur events with timeout
  private _isVisible: boolean; // track visibility
  private _cachedBounds?: Electron.Rectangle; // _cachedBounds are needed for double-clicked event
  private _options: Options;
  private _positioner: Positioner | undefined;
  private _tray?: Tray;

  constructor(app: Electron.App, options?: Partial<Options>) {
    super();
    this._app = app;
    this._options = cleanOptions(options);
    this._isVisible = false;

    if (app.isReady()) {
      // See https://github.com/maxogden/menubar/pull/151
      process.nextTick(() =>
        this.appReady().catch((err) => console.error('menubar: ', err)),
      );
    } else {
      app.on('ready', () => {
        this.appReady().catch((err) => console.error('menubar: ', err));
      });
    }
  }

  /**
   * The Electron [App](https://electronjs.org/docs/api/app)
   * instance.
   */
  get app(): Electron.App {
    return this._app;
  }

  /**
   * The [electron-positioner](https://github.com/jenslind/electron-positioner)
   * instance.
   */
  get positioner(): Positioner {
    if (!this._positioner) {
      throw new Error(
        'Please access `this.positioner` after the `after-create-window` event has fired.',
      );
    }

    return this._positioner;
  }

  /**
   * The Electron [Tray](https://electronjs.org/docs/api/tray) instance.
   */
  get tray(): Tray {
    if (!this._tray) {
      throw new Error(
        'Please access `this.tray` after the `ready` event has fired.',
      );
    }

    return this._tray;
  }

  /**
   * The Electron [BrowserWindow](https://electronjs.org/docs/api/browser-window)
   * instance, if it's present.
   */
  get window(): BrowserWindow | undefined {
    return this._browserWindow;
  }

  /**
   * Retrieve a menubar option.
   *
   * @param key - The option key to retrieve, see {@link Options}.
   */
  getOption<K extends keyof Options>(key: K): Options[K] {
    return this._options[key];
  }

  /**
   * Hide the menubar window.
   */
  hideWindow(): void {
    if (!this._browserWindow || !this._isVisible) {
      return;
    }
    this.emit('hide');
    this._browserWindow.hide();
    this.emit('after-hide');
    this._isVisible = false;
    if (this._blurTimeout) {
      clearTimeout(this._blurTimeout);
      this._blurTimeout = null;
    }
  }

  /**
   * Change an option after menubar is created.
   *
   * @param key - The option key to modify, see {@link Options}.
   * @param value - The value to set.
   */
  setOption<K extends keyof Options>(key: K, value: Options[K]): void {
    this._options[key] = value;
  }

  /**
   * Show the menubar window.
   *
   * @param trayPos - The bounds to show the window in.
   */
  async showWindow(trayPos?: Electron.Rectangle): Promise<void> {
    if (!this.tray) {
      throw new Error('Tray should have been instantiated by now');
    }

    if (!this._browserWindow) {
      await this.createWindow();
    }

    // Use guard for TypeScript, to avoid ! everywhere
    if (!this._browserWindow) {
      throw new Error('Window has been initialized just above. qed.');
    }

    // 'Windows' taskbar: sync windows position each time before showing
    // https://github.com/maxogden/menubar/issues/232
    if (['win32', 'linux'].includes(process.platform)) {
      // Fill in this._options.windowPosition when taskbar position is available
      this._options.windowPosition = getWindowPosition(this.tray);
    }

    this.emit('show');

    if (trayPos && trayPos.x !== 0) {
      // Cache the bounds
      this._cachedBounds = trayPos;
    } else if (this._cachedBounds) {
      // Cached value will be used if showWindow is called without bounds data
      trayPos = this._cachedBounds;
    } else if (this.tray.getBounds) {
      // Get the current tray bounds
      trayPos = this.tray.getBounds();
    }

    // Default the window to the right if `trayPos` bounds are undefined or null.
    let noBoundsPosition = undefined;
    if (
      (trayPos === undefined || trayPos.x === 0) &&
      this._options.windowPosition &&
      this._options.windowPosition.startsWith('tray')
    ) {
      noBoundsPosition =
        process.platform === 'win32' ? 'bottomRight' : 'topRight';
    }

    const position = this.positioner.calculate(
      this._options.windowPosition || noBoundsPosition,
      trayPos,
    ) as { x: number; y: number };

    // Not using `||` because x and y can be zero.
    const x =
      this._options.browserWindow.x !== undefined
        ? this._options.browserWindow.x
        : position.x;
    const y =
      this._options.browserWindow.y !== undefined
        ? this._options.browserWindow.y
        : position.y;

    // `.setPosition` crashed on non-integers
    // https://github.com/maxogden/menubar/issues/233
    this._browserWindow.setPosition(Math.round(x), Math.round(y));
    this._browserWindow.show();
    this._isVisible = true;
    this.emit('after-show');
    return;
  }

  private async appReady(): Promise<void> {
    if (this.app.dock && !this._options.showDockIcon) {
      this.app.dock.hide();
    }

    if (this._options.activateWithApp) {
      this.app.on('activate', (_event, hasVisibleWindows) => {
        if (!hasVisibleWindows) {
          this.showWindow().catch(console.error);
        }
      });
    }

    let trayImage =
      this._options.icon || path.join(this._options.dir, 'IconTemplate.png');
    if (typeof trayImage === 'string' && !fs.existsSync(trayImage)) {
      trayImage = path.join(__dirname, '..', 'assets', 'IconTemplate.png'); // Default cat icon
    }

    const defaultClickEvent = this._options.showOnRightClick
      ? 'right-click'
      : 'click';

    this._tray = this._options.tray || new Tray(trayImage);
    // Type guards for TS not to complain
    if (!this.tray) {
      throw new Error('Tray has been initialized above');
    }
    this.tray.on(
      defaultClickEvent as Parameters<Tray['on']>[0],
      this.clicked.bind(this),
    );
    this.tray.on('double-click', this.clicked.bind(this));
    this.tray.setToolTip(this._options.tooltip);

    if (!this._options.windowPosition) {
      this._options.windowPosition = getWindowPosition(this.tray);
    }

    if (this._options.preloadWindow) {
      await this.createWindow();
    }

    this.emit('ready');
  }

  /**
   * Callback on tray icon click or double-click.
   *
   * @param e
   * @param bounds
   */
  private async clicked(
    event?: Electron.KeyboardEvent,
    bounds?: Electron.Rectangle,
  ): Promise<void> {
    if (event && (event.shiftKey || event.ctrlKey || event.metaKey)) {
      return this.hideWindow();
    }

    // if blur was invoked clear timeout
    if (this._blurTimeout) {
      clearInterval(this._blurTimeout);
    }

    if (this._browserWindow && this._isVisible) {
      return this.hideWindow();
    }

    this._cachedBounds = bounds || this._cachedBounds;
    await this.showWindow(this._cachedBounds);
  }

  private async createWindow(): Promise<void> {
    this.emit('create-window');

    // We add some default behavior for menubar's browserWindow, to make it
    // look like a menubar
    const defaults = {
      show: false, // Don't show it at first
      frame: false, // Remove window frame
    };

    this._browserWindow = new BrowserWindow({
      ...defaults,
      ...this._options.browserWindow,
    });

    this._positioner = new Positioner(this._browserWindow);

    this._browserWindow.on('blur', () => {
      if (!this._browserWindow) {
        return;
      }

      // hack to close if icon clicked when open
      this._browserWindow.isAlwaysOnTop()
        ? this.emit('focus-lost')
        : (this._blurTimeout = setTimeout(() => {
            this.hideWindow();
          }, 100));
    });

    if (this._options.showOnAllWorkspaces !== false) {
      // https://github.com/electron/electron/issues/37832#issuecomment-1497882944
      this._browserWindow.setVisibleOnAllWorkspaces(true, {
        skipTransformProcessType: true, // Avoid damaging the original visible state of app.dock
      });
    }

    this._browserWindow.on('close', this.windowClear.bind(this));

    this.emit('before-load');

    // If the user explicity set options.index to false, we don't loadURL
    // https://github.com/maxogden/menubar/issues/255
    if (this._options.index !== false) {
      await this._browserWindow.loadURL(
        this._options.index,
        this._options.loadUrlOptions,
      );
    }
    this.emit('after-create-window');
  }

  private windowClear(): void {
    this._browserWindow = undefined;
    this.emit('after-close');
  }
}


================================================
FILE: src/__mocks__/electron.ts
================================================
// https://github.com/electron/electron/issues/3909#issuecomment-190990825

export const MOCK_APP_GETAPPPATH = 'mock.app.getAppPath';

export const app = {
  getAppPath: jest.fn(() => MOCK_APP_GETAPPPATH),
  isReady: (): Promise<void> => Promise.resolve(),
  on: (): void => {
    /* Do nothing */
  },
};

export class BrowserWindow {
  loadURL(): void {
    // Do nothing
  }

  on(): void {
    // Do nothing
  }

  setVisibleOnAllWorkspaces(): void {
    // Do nothing
  }
}

export class Tray {
  on(): void {
    // Do nothing
  }

  setToolTip(): void {
    // Do nothing
  }
}


================================================
FILE: src/ambient.d.ts
================================================
// TODO https://github.com/jenslind/electron-positioner/issues/15
declare module 'electron-positioner' {
  export default class {
    constructor(window: Electron.BrowserWindow);

    calculate(
      position?: string,
      rectangle?: Electron.Rectangle,
    ): { x: number; y: number };
  }
}


================================================
FILE: src/index.ts
================================================
/**
 * Entry point of menubar
 * @example
 * ```typescript
 * import { menubar } from 'menubar';
 * ```
 */

/** */

import { app } from 'electron';

import { Menubar } from './Menubar';
import type { Options } from './types';

export * from './util/getWindowPosition';
export { Menubar };

/**
 * Factory function to create a menubar application
 *
 * @param options - Options for creating a menubar application, see
 * {@link Options}
 */
export function menubar(options?: Partial<Options>): Menubar {
  return new Menubar(app, options);
}


================================================
FILE: src/types.ts
================================================
import type {
  BrowserWindowConstructorOptions,
  LoadURLOptions,
  Tray,
} from 'electron';

/**
 * Options for creating a menubar application
 */
export interface Options {
  /**
   * Listen on `app.on('activate')` to open menubar when app is activated.
   * @default `true`
   */
  activateWithApp?: boolean;
  /**
   * An Electron BrowserWindow instance, or an options object to be passed into
   * the BrowserWindow constructor.
   * @example
   * ```typescript
   * const options = { height: 640, width: 480 };
   *
   * const mb = new Menubar({
   *   browserWindow: options
   * });
   * ```
   */
  browserWindow: BrowserWindowConstructorOptions;
  /**
   * The app source directory.
   */
  dir: string;
  /**
   * 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.
   */
  icon?: string | Electron.NativeImage;
  /**
   * 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. If false, then menubar won't call `loadURL` on
   * start.
   * @default `file:// + options.dir + index.html`
   * @see https://electronjs.org/docs/api/browser-window#winloadurlurl-options
   */
  index: string | false;
  /**
   * 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
   * @default `{}`
   * @see https://electronjs.org/docs/api/browser-window#winloadurlurl-options
   */
  loadUrlOptions?: LoadURLOptions;
  /**
   * Create BrowserWindow instance before it is used -- increasing resource
   * usage, but making the click on the menubar load faster.
   */
  preloadWindow?: boolean;
  /**
   * Configure the visibility of the application dock icon, macOS only. Calls
   * [`app.dock.hide`](https://electronjs.org/docs/api/app#appdockhide-macos).
   */
  showDockIcon?: boolean;
  /**
   * Makes the window available on all OS X workspaces. Calls
   * [`setVisibleOnAllWorkspaces`](https://electronjs.org/docs/api/browser-window#winsetvisibleonallworkspacesvisible-options).
   */
  showOnAllWorkspaces?: boolean;
  /**
   * Show the window on 'right-click' event instead of regular 'click'.
   */
  showOnRightClick?: boolean;
  /**
   * Menubar tray icon tooltip text. Calls [`tray.setTooltip`](https://electronjs.org/docs/api/tray#traysettooltiptooltip).
   */
  tooltip: string;
  /**
   * An electron Tray instance. If provided, `options.icon` will be ignored.
   */
  tray?: Tray;
  /**
   * Sets the window position (x and y will still override this), check
   * electron-positioner docs for valid values.
   */
  windowPosition?:
    | 'trayLeft'
    | 'trayBottomLeft'
    | 'trayRight'
    | 'trayBottomRight'
    | 'trayCenter'
    | 'trayBottomCenter'
    | 'topLeft'
    | 'topRight'
    | 'bottomLeft'
    | 'bottomRight'
    | 'topCenter'
    | 'bottomCenter'
    | 'leftCenter'
    | 'rightCenter'
    | 'center';
}


================================================
FILE: src/util/cleanOptions.spec.ts
================================================
import * as path from 'path';

import { MOCK_APP_GETAPPPATH } from '../__mocks__/electron';
import { cleanOptions } from './cleanOptions';

const DEFAULT_OPTIONS = {
  activateWithApp: true,
  browserWindow: {
    height: 400,
    width: 400,
  },
  dir: path.resolve(MOCK_APP_GETAPPPATH),
  index: `file://${path.join(path.resolve(MOCK_APP_GETAPPPATH), 'index.html')}`,
  loadUrlOptions: {},
  tooltip: '',
};

describe('cleanOptions', () => {
  it('should handle undefined', () => {
    expect(cleanOptions(undefined)).toEqual(DEFAULT_OPTIONS);
  });

  it('should handle a dir string with relative path', () => {
    expect(cleanOptions({ dir: 'MY_RELATIVE_PATH' })).toEqual({
      ...DEFAULT_OPTIONS,
      dir: path.resolve('MY_RELATIVE_PATH'),
      index: `file://${path.join(
        path.resolve('MY_RELATIVE_PATH'),
        'index.html',
      )}`,
    });
  });

  it('should handle a dir string with absolute path', () => {
    expect(cleanOptions({ dir: '/home/me/MY_ABSOLUTE_PATH' })).toEqual({
      ...DEFAULT_OPTIONS,
      dir: '/home/me/MY_ABSOLUTE_PATH',
      index: 'file:///home/me/MY_ABSOLUTE_PATH/index.html',
    });
  });

  it('should handle a false index', () => {
    expect(cleanOptions({ index: false })).toEqual({
      ...DEFAULT_OPTIONS,
      index: false,
    });
  });

  it('should handle an object with multiple fields', () => {
    expect(
      cleanOptions({
        browserWindow: {
          height: 100,
        },
        index: 'file:///home/abc/index.html',
        showDockIcon: true,
        windowPosition: 'trayCenter',
      }),
    ).toEqual({
      ...DEFAULT_OPTIONS,
      browserWindow: {
        ...DEFAULT_OPTIONS.browserWindow,
        height: 100,
      },
      index: 'file:///home/abc/index.html',
      showDockIcon: true,
      windowPosition: 'trayCenter',
    });
  });
});


================================================
FILE: src/util/cleanOptions.ts
================================================
/**
 * @ignore
 */

/** */

import path from 'path';
import url from 'url';
import { app } from 'electron';

import type { Options } from '../types';

const DEFAULT_WINDOW_HEIGHT = 400;
const DEFAULT_WINDOW_WIDTH = 400;

/**
 * Take as input some options, and return a sanitized version of it.
 *
 * @param opts - The options to clean.
 * @ignore
 */
export function cleanOptions(opts?: Partial<Options>): Options {
  const options: Partial<Options> = { ...opts };

  if (options.activateWithApp === undefined) {
    options.activateWithApp = true;
  }
  if (!options.dir) {
    options.dir = app.getAppPath();
  }
  if (!path.isAbsolute(options.dir)) {
    options.dir = path.resolve(options.dir);
  }
  // Note: options.index can be `false`
  if (options.index === undefined) {
    options.index = url.format({
      pathname: path.join(options.dir, 'index.html'),
      protocol: 'file:',
      slashes: true,
    });
  }
  options.loadUrlOptions = options.loadUrlOptions || {};

  options.tooltip = options.tooltip || '';

  // `icon`, `preloadWindow`, `showDockIcon`, `showOnAllWorkspaces`,
  // `showOnRightClick` don't need any special treatment

  // Now we take care of `browserWindow`
  if (!options.browserWindow) {
    options.browserWindow = {};
  }

  // Set width/height on options to be usable before the window is created
  options.browserWindow.width =
    // Note: not using `options.browserWindow.width || DEFAULT_WINDOW_WIDTH` so
    // that users can put a 0 width
    options.browserWindow.width !== undefined
      ? options.browserWindow.width
      : DEFAULT_WINDOW_WIDTH;
  options.browserWindow.height =
    options.browserWindow.height !== undefined
      ? options.browserWindow.height
      : DEFAULT_WINDOW_HEIGHT;

  return options as Options;
}


================================================
FILE: src/util/getWindowPosition.ts
================================================
/**
 * Utilities to get taskbar position and consequently menubar's position
 */

/** */

import { type Rectangle, type Tray, screen as electronScreen } from 'electron';

const isLinux = process.platform === 'linux';

const trayToScreenRects = (tray: Tray): [Rectangle, Rectangle] => {
  // There may be more than one screen, so we need to figure out on which screen our tray icon lives.
  const { workArea, bounds: screenBounds } = electronScreen.getDisplayMatching(
    tray.getBounds(),
  );

  workArea.x -= screenBounds.x;
  workArea.y -= screenBounds.y;

  return [screenBounds, workArea];
};

type TaskbarLocation = 'top' | 'bottom' | 'left' | 'right';

/**
 * Determine taskbard location: "top", "bottom", "left" or "right".
 *
 * Only tested on Windows for now, and only used in Windows.
 *
 * @param tray - The Electron Tray instance.
 */
export function taskbarLocation(tray: Tray): TaskbarLocation {
  const [screenBounds, workArea] = trayToScreenRects(tray);

  // TASKBAR LEFT
  if (workArea.x > 0) {
    // Most likely Ubuntu hence assuming the window should be on top
    if (isLinux && workArea.y > 0) return 'top';
    // The workspace starts more on the right
    return 'left';
  }

  // TASKBAR TOP
  if (workArea.y > 0) {
    return 'top';
  }

  // TASKBAR RIGHT
  // Here both workArea.y and workArea.x are 0 so we can no longer leverage them.
  // We can use the workarea and display width though.
  // Determine taskbar location
  if (workArea.width < screenBounds.width) {
    // The taskbar is either on the left or right, but since the LEFT case was handled above,
    // we can be sure we're dealing with a right taskbar
    return 'right';
  }

  // TASKBAR BOTTOM
  // Since all the other cases were handled, we can be sure we're dealing with a bottom taskbar
  return 'bottom';
}

type WindowPosition =
  | 'trayCenter'
  | 'topRight'
  | 'trayBottomCenter'
  | 'bottomLeft'
  | 'bottomRight';

/**
 * Depending on where the taskbar is, determine where the window should be
 * positioned.
 *
 * @param tray - The Electron Tray instance.
 */
export function getWindowPosition(tray: Tray): WindowPosition {
  switch (process.platform) {
    // macOS
    // Supports top taskbars
    case 'darwin':
      return 'trayCenter';
    // Linux
    // Windows
    // Supports top/bottom/left/right taskbar
    case 'linux':
    case 'win32': {
      const traySide = taskbarLocation(tray);

      // Assign position for menubar
      if (traySide === 'top') {
        return isLinux ? 'topRight' : 'trayCenter';
      }
      if (traySide === 'bottom') {
        return 'bottomRight';
      }
      if (traySide === 'left') {
        return 'bottomLeft';
      }
      if (traySide === 'right') {
        return 'bottomRight';
      }
    }
  }

  // When we really don't know, we just show the menubar on the top-right
  return 'topRight';
}


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "declaration": true,
    "outDir": "./lib",
    "skipLibCheck": true,
    "target": "es5"
  }
}


================================================
FILE: typedoc.js
================================================
module.exports = {
  exclude: ['**/*spec.ts', '**/__mocks__/**'],
  excludeExternals: true,
  excludeNotExported: true,
  excludePrivate: true,
  excludeProtected: true,
  hideGenerator: true,
  includes: './src',
  module: 'commonjs',
  out: 'docs',
  stripInternal: 'true',
  theme: 'markdown',
};
Download .txt
gitextract_dw9q4gt_/

├── .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
├── 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
│   ├── hello-world/
│   │   ├── README.md
│   │   ├── index.html
│   │   ├── main.js
│   │   └── package.json
│   ├── icon-animation/
│   │   ├── index.html
│   │   ├── main.js
│   │   └── package.json
│   ├── native-menu/
│   │   ├── README.md
│   │   ├── main.js
│   │   └── package.json
│   └── package.json
├── 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
Download .txt
SYMBOL INDEX (35 symbols across 8 files)

FILE: examples/icon-animation/main.js
  function setOkIcon (line 15) | function setOkIcon() {
  function frame (line 19) | function frame() {
  function sleep (line 25) | function sleep(ms) {

FILE: src/Menubar.ts
  class Menubar (line 16) | class Menubar extends EventEmitter {
    method constructor (line 26) | constructor(app: Electron.App, options?: Partial<Options>) {
    method app (line 48) | get app(): Electron.App {
    method positioner (line 56) | get positioner(): Positioner {
    method tray (line 69) | get tray(): Tray {
    method window (line 83) | get window(): BrowserWindow | undefined {
    method getOption (line 92) | getOption<K extends keyof Options>(key: K): Options[K] {
    method hideWindow (line 99) | hideWindow(): void {
    method setOption (line 119) | setOption<K extends keyof Options>(key: K, value: Options[K]): void {
    method showWindow (line 128) | async showWindow(trayPos?: Electron.Rectangle): Promise<void> {
    method appReady (line 197) | private async appReady(): Promise<void> {
    method clicked (line 249) | private async clicked(
    method createWindow (line 270) | private async createWindow(): Promise<void> {
    method windowClear (line 322) | private windowClear(): void {

FILE: src/__mocks__/electron.ts
  constant MOCK_APP_GETAPPPATH (line 3) | const MOCK_APP_GETAPPPATH = 'mock.app.getAppPath';
  class BrowserWindow (line 13) | class BrowserWindow {
    method loadURL (line 14) | loadURL(): void {
    method on (line 18) | on(): void {
    method setVisibleOnAllWorkspaces (line 22) | setVisibleOnAllWorkspaces(): void {
  class Tray (line 27) | class Tray {
    method on (line 28) | on(): void {
    method setToolTip (line 32) | setToolTip(): void {

FILE: src/index.ts
  function menubar (line 25) | function menubar(options?: Partial<Options>): Menubar {

FILE: src/types.ts
  type Options (line 10) | interface Options {

FILE: src/util/cleanOptions.spec.ts
  constant DEFAULT_OPTIONS (line 6) | const DEFAULT_OPTIONS = {

FILE: src/util/cleanOptions.ts
  constant DEFAULT_WINDOW_HEIGHT (line 13) | const DEFAULT_WINDOW_HEIGHT = 400;
  constant DEFAULT_WINDOW_WIDTH (line 14) | const DEFAULT_WINDOW_WIDTH = 400;
  function cleanOptions (line 22) | function cleanOptions(opts?: Partial<Options>): Options {

FILE: src/util/getWindowPosition.ts
  type TaskbarLocation (line 23) | type TaskbarLocation = 'top' | 'bottom' | 'left' | 'right';
  function taskbarLocation (line 32) | function taskbarLocation(tray: Tray): TaskbarLocation {
  type WindowPosition (line 63) | type WindowPosition =
  function getWindowPosition (line 76) | function getWindowPosition(tray: Tray): WindowPosition {
Condensed preview — 53 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (79K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 417,
    "preview": "[//]: # \"Hey there and thank you for using Menubar's issue tracker!\"\n[//]: # \"Please fill in these information to descri"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 172,
    "preview": "version: 2\nupdates:\n- package-ecosystem: npm\n  directory: \"/\"\n  schedule:\n    interval: weekly\n    time: \"02:00\"\n    tim"
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 378,
    "preview": "name: Linting\n\non:\n    push:\n        branches:\n            - master\n    pull_request:\n\njobs:\n    lint:\n        name: Lin"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 384,
    "preview": "name: Run Tests\n\non:\n    push:\n        branches:\n            - master\n    pull_request:\n\njobs:\n    lint:\n        name: R"
  },
  {
    "path": ".gitignore",
    "chars": 53,
    "preview": ".DS_Store\nnode_modules\ncoverage\nlib\ndist\n*.app\n*.log\n"
  },
  {
    "path": ".nvmrc",
    "chars": 3,
    "preview": "20\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 43,
    "preview": "{\n  \"recommendations\": [\"biomejs.biome\"]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 207,
    "preview": "{\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll.eslint\": \"explicit\",\n    \"source.organizeImports.biome\": \"explicit\""
  },
  {
    "path": "CHANGELOG.md",
    "chars": 12667,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github."
  },
  {
    "path": "COLLABORATORS.md",
    "chars": 760,
    "preview": "## Collaborators\n\nmenubar is only possible due to the excellent work of the following collaborators:\n\n<table>\n<tbody>\n<t"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 858,
    "preview": "## Before opening issues\n\n**If you are asking a question**:\n\nRemember that `menubar` is just a lightweight wrapper aroun"
  },
  {
    "path": "LICENSE",
    "chars": 1319,
    "preview": "BSD 2-Clause License\n\nCopyright (c) 2015-2019, Max Ogden\nAll rights reserved.\n\nRedistribution and use in source and bina"
  },
  {
    "path": "README.md",
    "chars": 8579,
    "preview": "![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/max-mapper/menubar/test.yml)\n[!["
  },
  {
    "path": "WORKING_PLATFORMS.md",
    "chars": 1789,
    "preview": "# Platforms where `menubar` is known to work\n\nThis document is still a work-in-progress. If you have tested `menubar` wi"
  },
  {
    "path": "biome.json",
    "chars": 771,
    "preview": "{\n  \"$schema\": \"https://biomejs.dev/schemas/1.9.3/schema.json\",\n  \"organizeImports\": {\n    \"enabled\": true\n  },\n  \"linte"
  },
  {
    "path": "docs/README.md",
    "chars": 7733,
    "preview": "> **[menubar](README.md)**\n\n[Globals](globals.md) /\n\n[![Build Status](https://travis-ci.org/maxogden/menubar.svg?branch="
  },
  {
    "path": "docs/classes/_menubar_.menubar.md",
    "chars": 3021,
    "preview": "> **[menubar](../README.md)**\n\n[Globals](../globals.md) / [\"Menubar\"](../modules/_menubar_.md) / [Menubar](_menubar_.men"
  },
  {
    "path": "docs/globals.md",
    "chars": 261,
    "preview": "> **[menubar](README.md)**\n\n[Globals](globals.md) /\n\n# menubar\n\n## Index\n\n### External modules\n\n* [\"Menubar\"](modules/_m"
  },
  {
    "path": "docs/interfaces/_types_.options.md",
    "chars": 3056,
    "preview": "> **[menubar](../README.md)**\n\n[Globals](../globals.md) / [\"types\"](../modules/_types_.md) / [Options](_types_.options.m"
  },
  {
    "path": "docs/modules/_index_.md",
    "chars": 646,
    "preview": "> **[menubar](../README.md)**\n\n[Globals](../globals.md) / [\"index\"](_index_.md) /\n\n# External module: \"index\"\n\n## Index\n"
  },
  {
    "path": "docs/modules/_menubar_.md",
    "chars": 184,
    "preview": "> **[menubar](../README.md)**\n\n[Globals](../globals.md) / [\"Menubar\"](_menubar_.md) /\n\n# External module: \"Menubar\"\n\n## "
  },
  {
    "path": "docs/modules/_types_.md",
    "chars": 182,
    "preview": "> **[menubar](../README.md)**\n\n[Globals](../globals.md) / [\"types\"](_types_.md) /\n\n# External module: \"types\"\n\n## Index\n"
  },
  {
    "path": "docs/modules/_util_getwindowposition_.md",
    "chars": 1079,
    "preview": "> **[menubar](../README.md)**\n\n[Globals](../globals.md) / [\"util/getWindowPosition\"](_util_getwindowposition_.md) /\n\n# E"
  },
  {
    "path": "examples/arrow/README.md",
    "chars": 516,
    "preview": "# example-menubar-arrow\n\n## Description\n\nA menubar app with an arrow on top, that gives the app a popover look.\n\nThanks "
  },
  {
    "path": "examples/arrow/index.css",
    "chars": 454,
    "preview": ".myarrow {\n  position: relative;\n  padding: 12px 0;\n}\n\n.myarrow:before {\n  content: \"\";\n  height: 0;\n  width: 0;\n  borde"
  },
  {
    "path": "examples/arrow/index.html",
    "chars": 195,
    "preview": "<html>\n  <head>\n    <title>Example App</title>\n    <link rel=\"stylesheet\" href=\"index.css\" />\n  </head>\n  <body class=\"m"
  },
  {
    "path": "examples/arrow/main.js",
    "chars": 212,
    "preview": "const { menubar } = require('../..');\n\nconst mb = menubar({\n  browserWindow: {\n    transparent: true,\n    width: 350,\n  "
  },
  {
    "path": "examples/arrow/package.json",
    "chars": 195,
    "preview": "{\n  \"name\": \"example-menubar-arrow\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Example menubar electron app\",\n  \"main\": \"m"
  },
  {
    "path": "examples/hello-world/README.md",
    "chars": 456,
    "preview": "# example-menubar-hello-world\n\n## Description\n\nThis is a simple Hello World menubar app that will just open a window tha"
  },
  {
    "path": "examples/hello-world/index.html",
    "chars": 101,
    "preview": "<html>\n  <head>\n    <title>Example App</title>\n  </head>\n  <body>\n    Hello World.\n  </body>\n</html>\n"
  },
  {
    "path": "examples/hello-world/main.js",
    "chars": 129,
    "preview": "const { menubar } = require('../..');\n\nconst mb = menubar();\n\nmb.on('ready', () => {\n  console.log('Menubar app is ready"
  },
  {
    "path": "examples/hello-world/package.json",
    "chars": 201,
    "preview": "{\n  \"name\": \"example-menubar-hello-world\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Example menubar electron app\",\n  \"mai"
  },
  {
    "path": "examples/icon-animation/index.html",
    "chars": 101,
    "preview": "<html>\n  <head>\n    <title>Example App</title>\n  </head>\n  <body>\n    Hello World.\n  </body>\n</html>\n"
  },
  {
    "path": "examples/icon-animation/main.js",
    "chars": 647,
    "preview": "const { menubar } = require('../..');\n\nconst mb = menubar();\n\nmb.on('ready', () => {\n  setOkIcon();\n  const trayAnimatio"
  },
  {
    "path": "examples/icon-animation/package.json",
    "chars": 204,
    "preview": "{\n  \"name\": \"example-menubar-icon-animation\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Example menubar electron app\",\n  \""
  },
  {
    "path": "examples/native-menu/README.md",
    "chars": 485,
    "preview": "# example-menubar-native-menu\n\n## Description\n\nThis will create a menubar app with a native menu.\n\nCourtesy of @tslater "
  },
  {
    "path": "examples/native-menu/main.js",
    "chars": 805,
    "preview": "const { app, Menu, Tray } = require('electron');\nconst path = require('path');\n\nconst { menubar } = require('../../');\n\n"
  },
  {
    "path": "examples/native-menu/package.json",
    "chars": 201,
    "preview": "{\n  \"name\": \"example-menubar-native-menu\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Example menubar electron app\",\n  \"mai"
  },
  {
    "path": "examples/package.json",
    "chars": 256,
    "preview": "{\n  \"name\": \"example-menubar\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Examples of menubar electron apps\",\n  \"private\": "
  },
  {
    "path": "jest.config.js",
    "chars": 249,
    "preview": "module.exports = {\n  collectCoverageFrom: ['**/*.{ts,tsx}', '!**/node_modules/**', '!**/*.d.ts'],\n  moduleFileExtensions"
  },
  {
    "path": "package.json",
    "chars": 1459,
    "preview": "{\n  \"name\": \"menubar\",\n  \"version\": \"9.5.2\",\n  \"author\": \"Max Ogden\",\n  \"bugs\": {\n    \"url\": \"https://github.com/maxogde"
  },
  {
    "path": "src/Menubar.spec.ts",
    "chars": 1112,
    "preview": "import { BrowserWindow, Tray, app } from 'electron';\n\nimport { Menubar } from './Menubar';\n\ndescribe('Menubar', () => {\n"
  },
  {
    "path": "src/Menubar.ts",
    "chars": 9151,
    "preview": "import { EventEmitter } from 'events';\nimport fs from 'fs';\nimport path from 'path';\nimport { BrowserWindow, Tray } from"
  },
  {
    "path": "src/__mocks__/electron.ts",
    "chars": 585,
    "preview": "// https://github.com/electron/electron/issues/3909#issuecomment-190990825\n\nexport const MOCK_APP_GETAPPPATH = 'mock.app"
  },
  {
    "path": "src/ambient.d.ts",
    "chars": 297,
    "preview": "// TODO https://github.com/jenslind/electron-positioner/issues/15\ndeclare module 'electron-positioner' {\n  export defaul"
  },
  {
    "path": "src/index.ts",
    "chars": 542,
    "preview": "/**\n * Entry point of menubar\n * @example\n * ```typescript\n * import { menubar } from 'menubar';\n * ```\n */\n\n/** */\n\nimp"
  },
  {
    "path": "src/types.ts",
    "chars": 3230,
    "preview": "import type {\n  BrowserWindowConstructorOptions,\n  LoadURLOptions,\n  Tray,\n} from 'electron';\n\n/**\n * Options for creati"
  },
  {
    "path": "src/util/cleanOptions.spec.ts",
    "chars": 1845,
    "preview": "import * as path from 'path';\n\nimport { MOCK_APP_GETAPPPATH } from '../__mocks__/electron';\nimport { cleanOptions } from"
  },
  {
    "path": "src/util/cleanOptions.ts",
    "chars": 1779,
    "preview": "/**\n * @ignore\n */\n\n/** */\n\nimport path from 'path';\nimport url from 'url';\nimport { app } from 'electron';\n\nimport type"
  },
  {
    "path": "src/util/getWindowPosition.ts",
    "chars": 2867,
    "preview": "/**\n * Utilities to get taskbar position and consequently menubar's position\n */\n\n/** */\n\nimport { type Rectangle, type "
  },
  {
    "path": "tsconfig.json",
    "chars": 200,
    "preview": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"esModuleInterop\": true,\n    \"strict\": true,\n    \"declaration\": t"
  },
  {
    "path": "typedoc.js",
    "chars": 300,
    "preview": "module.exports = {\n  exclude: ['**/*spec.ts', '**/__mocks__/**'],\n  excludeExternals: true,\n  excludeNotExported: true,\n"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the max-mapper/menubar GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 53 files (71.6 KB), approximately 21.9k tokens, and a symbol index with 35 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!