electron-menubar 10.1.7 → 10.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/lib/index.cjs +27 -6
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +31 -4
- package/lib/index.d.mts +31 -4
- package/lib/index.mjs +27 -6
- package/lib/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -87,6 +87,7 @@ The return value of `menubar()` is a `Menubar` class instance, which exposes the
|
|
|
87
87
|
| `toggleWindow()` | Show the window if hidden, hide it if visible. |
|
|
88
88
|
| `recenterOnTray()` | Re-center the window over the tray icon. |
|
|
89
89
|
| `setContextMenu(menu)` | Replace the tray context menu (auto-re-publishes on Linux). |
|
|
90
|
+
| `refreshContextMenu()` | Re-publish the current context menu after mutating its items in place. Required on Linux, a no-op elsewhere. |
|
|
90
91
|
| `setGlobalShortcut(accelerator)` | Register a global accelerator that toggles the window. Returns `false` on registration failure. |
|
|
91
92
|
| `destroy()` | Tear down the menubar instance. |
|
|
92
93
|
| `isDestroyed()` | Whether the menubar is currently destroyed. |
|
|
@@ -107,11 +108,11 @@ You can pass an optional options object into the `menubar({ ... })` function:
|
|
|
107
108
|
| `loadUrlOptions` | `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][electron-docs-browserwindow-loadurl]. |
|
|
108
109
|
| `showOnAllWorkspaces` | `true` | Makes the window available on all macOS workspaces. |
|
|
109
110
|
| `windowPosition` | `trayCenter` (macOS/Linux), `trayBottomCenter` (Windows) | Sets the window position (`browserWindow.x` / `browserWindow.y` will still override this). Valid values: `trayLeft`, `trayBottomLeft`, `trayRight`, `trayBottomRight`, `trayCenter`, `trayBottomCenter`, `topLeft`, `topRight`, `bottomLeft`, `bottomRight`, `topCenter`, `bottomCenter`, `leftCenter`, `rightCenter`, `center`. |
|
|
110
|
-
| `showDockIcon` | `false` | Configure the visibility of the application dock icon. |
|
|
111
|
+
| `showDockIcon` | `false` | Configure the visibility of the application dock icon, macOS only. <details><summary>Hiding reliably</summary>When `false`, the library calls [`app.dock.hide()`](https://www.electronjs.org/docs/latest/api/app#appdockhide-macos) at startup and re-asserts it once shortly after, because macOS can silently drop the underlying process transform when it races the launch activation. The transform only runs after the process has already launched as a regular dock app, so the tile briefly exists on every launch and hiding it stays best-effort. Packaged apps that never want a dock tile should also declare [`LSUIElement`](https://developer.apple.com/documentation/bundleresources/information-property-list/lsuielement) in their `Info.plist` (with electron-builder: `mac.extendInfo.LSUIElement: true`), so the process starts as an agent app and no tile is ever created. `showDockIcon: false` then still covers development runs, where the stock Electron binary's plist is not under your control.</details> |
|
|
111
112
|
| `trigger` | `'click'` | Tray event that toggles the menubar window. One of `'click'`, `'right-click'`, or `'none'`. Use `'none'` to disable automatic toggling, useful when a single tray icon serves multiple windows. The window can still be shown by calling `mb.showWindow()` directly. |
|
|
112
113
|
| `showOnRightClick` | `false` | **Deprecated**, use `trigger: 'right-click'` instead. Show the window on `right-click` event instead of regular `click`. |
|
|
113
114
|
| `contextMenu` | | An Electron `Menu` to attach to the tray icon. <details><summary>Platform behavior</summary>On Linux it is bound via `tray.setContextMenu` (required by libappindicator / StatusNotifierItem) and re-published on every show/hide to defeat the indicator's menu cache. On macOS and Windows it pops up on right-click via `tray.popUpContextMenu`, so left-click continues to toggle the window. Combine with `trigger: 'none'` if you want right-click to be the only interaction.</details> |
|
|
114
|
-
| `hideOnClose` | `false` | Hide the window on `close` instead of destroying it, so the next tray click re-uses the same `BrowserWindow`. <details><summary>Platform notes</summary>On Linux/Wayland the hide is deferred via `setImmediate` to work around a compositor bug that leaves frameless surfaces in a half-closed state when hidden synchronously from the `close` handler. The library tracks the app's `before-quit` event internally, so real quits go through unimpeded.</details> |
|
|
115
|
+
| `hideOnClose` | `false` | Hide the window on `close` instead of destroying it, so the next tray click re-uses the same `BrowserWindow`. <details><summary>Platform notes</summary>On Linux/Wayland the hide is deferred via `setImmediate` to work around a compositor bug that leaves frameless surfaces in a half-closed state when hidden synchronously from the `close` handler. The library tracks the app's `before-quit` event and the auto updater's `before-quit-for-update` event internally, so real quits and update restarts go through unimpeded.</details> |
|
|
115
116
|
| `escapeToHide` | `false` | Hide the menubar window when the user presses `Escape` while it has focus. |
|
|
116
117
|
| `ignoreDoubleClickEvents` | `true` (macOS only) | Calls `tray.setIgnoreDoubleClickEvents(true)` so an accidental double-click doesn't race the close-on-blur handler and flicker the tray icon. Pass `false` to opt out. No-op on Linux/Windows. |
|
|
117
118
|
| `globalShortcut` | | An [Accelerator][electron-docs-accelerator] string registered as a global keyboard shortcut that toggles the menubar window. Unregistered automatically on `destroy()`. Use `mb.setGlobalShortcut(accelerator)` to change or clear it at runtime. |
|
package/lib/index.cjs
CHANGED
|
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
}
|
|
17
17
|
return to;
|
|
18
18
|
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
20
20
|
value: mod,
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
@@ -244,6 +244,7 @@ var Menubar = class extends node_events.EventEmitter {
|
|
|
244
244
|
this._isQuitting = false;
|
|
245
245
|
this._isVisible = false;
|
|
246
246
|
app.on("before-quit", this.onBeforeQuit);
|
|
247
|
+
electron.autoUpdater.on("before-quit-for-update", this.onBeforeQuit);
|
|
247
248
|
if (app.isReady()) process.nextTick(this.onAppReady);
|
|
248
249
|
else app.on("ready", this.onAppReady);
|
|
249
250
|
}
|
|
@@ -308,6 +309,7 @@ var Menubar = class extends node_events.EventEmitter {
|
|
|
308
309
|
this._app.removeListener("ready", this.onAppReady);
|
|
309
310
|
this._app.removeListener("activate", this.onAppActivate);
|
|
310
311
|
this._app.removeListener("before-quit", this.onBeforeQuit);
|
|
312
|
+
electron.autoUpdater.removeListener("before-quit-for-update", this.onBeforeQuit);
|
|
311
313
|
}
|
|
312
314
|
/**
|
|
313
315
|
* Whether {@link destroy} has been called on this menubar instance.
|
|
@@ -336,7 +338,7 @@ var Menubar = class extends node_events.EventEmitter {
|
|
|
336
338
|
clearTimeout(this._blurTimeout);
|
|
337
339
|
this._blurTimeout = null;
|
|
338
340
|
}
|
|
339
|
-
this.
|
|
341
|
+
this.refreshContextMenu();
|
|
340
342
|
}
|
|
341
343
|
/**
|
|
342
344
|
* Register a global keyboard accelerator that toggles the menubar window.
|
|
@@ -402,6 +404,19 @@ var Menubar = class extends node_events.EventEmitter {
|
|
|
402
404
|
if (menu && !this._rightClickContextMenuBound) this.bindRightClickContextMenu();
|
|
403
405
|
}
|
|
404
406
|
/**
|
|
407
|
+
* Re-publish the current context menu so in-place mutations of its items
|
|
408
|
+
* (`visible`, `enabled`, `checked`, `label`) become visible to the user.
|
|
409
|
+
*
|
|
410
|
+
* Only Linux needs this: libappindicator / StatusNotifierItem serializes the
|
|
411
|
+
* menu once and serves that copy until it is set again, so a mutated item
|
|
412
|
+
* would otherwise keep rendering its old state until the next show or hide.
|
|
413
|
+
* A no-op on macOS and Windows, where the menu is read at popup time — call
|
|
414
|
+
* it unconditionally after mutating items.
|
|
415
|
+
*/
|
|
416
|
+
refreshContextMenu() {
|
|
417
|
+
if (process.platform === "linux" && this._contextMenu && this._tray && !this._tray.isDestroyed?.()) this._tray.setContextMenu(this._contextMenu);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
405
420
|
* Change an option after menubar is created.
|
|
406
421
|
*
|
|
407
422
|
* @param key - The option key to modify, see {@link Options}.
|
|
@@ -427,7 +442,7 @@ var Menubar = class extends node_events.EventEmitter {
|
|
|
427
442
|
this._browserWindow.show();
|
|
428
443
|
this._isVisible = true;
|
|
429
444
|
this.emit("after-show");
|
|
430
|
-
this.
|
|
445
|
+
this.refreshContextMenu();
|
|
431
446
|
}
|
|
432
447
|
/**
|
|
433
448
|
* Compute and apply the tray-anchored position of the browser window. Safe
|
|
@@ -510,6 +525,15 @@ var Menubar = class extends node_events.EventEmitter {
|
|
|
510
525
|
onAppActivate = (_event, hasVisibleWindows) => {
|
|
511
526
|
if (!hasVisibleWindows) this.showWindow().catch(console.error);
|
|
512
527
|
};
|
|
528
|
+
/**
|
|
529
|
+
* Marks the app as shutting down so `hideOnClose` stops intercepting closes.
|
|
530
|
+
*
|
|
531
|
+
* Bound to both `app`'s `before-quit` and the auto updater's
|
|
532
|
+
* `before-quit-for-update`. Installing an update never emits `before-quit`:
|
|
533
|
+
* Electron closes every window first and only quits once the window list is
|
|
534
|
+
* empty, so a `hideOnClose` veto there would leave the window open and the
|
|
535
|
+
* install waiting forever.
|
|
536
|
+
*/
|
|
513
537
|
onBeforeQuit = () => {
|
|
514
538
|
this._isQuitting = true;
|
|
515
539
|
};
|
|
@@ -532,9 +556,6 @@ var Menubar = class extends node_events.EventEmitter {
|
|
|
532
556
|
});
|
|
533
557
|
this._rightClickContextMenuBound = true;
|
|
534
558
|
}
|
|
535
|
-
refreshLinuxContextMenu() {
|
|
536
|
-
if (process.platform === "linux" && this._contextMenu && this._tray && !this._tray.isDestroyed?.()) this._tray.setContextMenu(this._contextMenu);
|
|
537
|
-
}
|
|
538
559
|
onAppReady = () => {
|
|
539
560
|
if (this._isDestroyed) return;
|
|
540
561
|
this.appReady().catch((err) => console.error("menubar: ", err));
|
package/lib/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["screen","app","path","url","electronScreen","EventEmitter","globalShortcut","path","fs","Tray","BrowserWindow","app"],"sources":["../src/Positioner.ts","../src/util/cleanOptions.ts","../src/util/getWindowPosition.ts","../src/Menubar.ts","../src/index.ts"],"sourcesContent":["import { type BrowserWindow, type Rectangle, screen } from 'electron';\n\n/**\n * Named anchor points for placing the menubar window. The `tray*` values are\n * relative to the tray icon's bounds; the rest are relative to the work area\n * of the display containing the cursor (or the tray, when bounds are given).\n */\nexport type WindowPosition =\n | 'trayLeft'\n | 'trayBottomLeft'\n | 'trayRight'\n | 'trayBottomRight'\n | 'trayCenter'\n | 'trayBottomCenter'\n | 'topLeft'\n | 'topRight'\n | 'bottomLeft'\n | 'bottomRight'\n | 'topCenter'\n | 'bottomCenter'\n | 'leftCenter'\n | 'rightCenter'\n | 'center';\n\n/**\n * Computes `{x, y}` coordinates for placing a {@link BrowserWindow} at a named\n * position, optionally relative to a tray icon's bounds. Ported from\n * `electron-positioner@4.1.0` to drop the unmaintained runtime dependency.\n */\nexport class Positioner {\n private readonly browserWindow: BrowserWindow;\n\n constructor(browserWindow: BrowserWindow) {\n this.browserWindow = browserWindow;\n }\n\n calculate(\n position?: WindowPosition,\n trayBounds?: Rectangle,\n ): { x: number; y: number } {\n if (!position) {\n throw new TypeError(\n 'Positioner.calculate: a `position` argument is required.',\n );\n }\n\n const screenSize = trayBounds\n ? screen.getDisplayMatching(trayBounds).workArea\n : screen.getDisplayNearestPoint(screen.getCursorScreenPoint()).workArea;\n const [windowWidth, windowHeight] = this.browserWindow.getSize();\n const trayX = trayBounds?.x ?? Number.NaN;\n const trayWidth = trayBounds?.width ?? Number.NaN;\n\n const positions: Record<WindowPosition, { x: number; y: number }> = {\n trayLeft: {\n x: Math.floor(trayX),\n y: screenSize.y,\n },\n trayBottomLeft: {\n x: Math.floor(trayX),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n trayRight: {\n x: Math.floor(trayX - windowWidth + trayWidth),\n y: screenSize.y,\n },\n trayBottomRight: {\n x: Math.floor(trayX - windowWidth + trayWidth),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n trayCenter: {\n x: Math.floor(trayX - windowWidth / 2 + trayWidth / 2),\n y: screenSize.y,\n },\n trayBottomCenter: {\n x: Math.floor(trayX - windowWidth / 2 + trayWidth / 2),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n topLeft: {\n x: screenSize.x,\n y: screenSize.y,\n },\n topRight: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y: screenSize.y,\n },\n bottomLeft: {\n x: screenSize.x,\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n bottomRight: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n topCenter: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: screenSize.y,\n },\n bottomCenter: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n leftCenter: {\n x: screenSize.x,\n y:\n screenSize.y +\n Math.floor(screenSize.height / 2) -\n Math.floor(windowHeight / 2),\n },\n rightCenter: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y:\n screenSize.y +\n Math.floor(screenSize.height / 2) -\n Math.floor(windowHeight / 2),\n },\n center: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: Math.floor(\n (screenSize.height + screenSize.y) / 2 - windowHeight / 2,\n ),\n },\n };\n\n const coords = positions[position];\n\n // On Windows, a tray-relative position can push the window past the right\n // edge of the work area. Snap back to `topRight` x in that case so it stays\n // visible. See https://github.com/jenslind/electron-positioner.\n if (position.startsWith('tray')) {\n if (coords.x + windowWidth > screenSize.width + screenSize.x) {\n return { x: positions.topRight.x, y: coords.y };\n }\n }\n\n return coords;\n }\n}\n","/**\n * @ignore\n */\n\n/** */\n\nimport path from 'node:path';\nimport url from 'node:url';\n\nimport { app } from 'electron';\n\nimport type { Options } from '../types';\n\nconst DEFAULT_WINDOW_HEIGHT = 400;\nconst DEFAULT_WINDOW_WIDTH = 400;\n\n/**\n * Take as input some options, and return a sanitized version of it.\n *\n * @param opts - The options to clean.\n * @ignore\n */\nexport function cleanOptions(opts?: Partial<Options>): Options {\n const options: Partial<Options> = { ...opts };\n\n if (options.activateWithApp === undefined) {\n options.activateWithApp = true;\n }\n if (options.ignoreDoubleClickEvents === undefined) {\n options.ignoreDoubleClickEvents = true;\n }\n if (!options.dir) {\n options.dir = app.getAppPath();\n }\n if (!path.isAbsolute(options.dir)) {\n options.dir = path.resolve(options.dir);\n }\n // Note: options.index can be `false`\n if (options.index === undefined) {\n options.index = url.format({\n pathname: path.join(options.dir, 'index.html'),\n protocol: 'file:',\n slashes: true,\n });\n }\n options.loadUrlOptions = options.loadUrlOptions || {};\n\n options.tooltip = options.tooltip || '';\n\n // `icon`, `preloadWindow`, `showDockIcon`, `showOnAllWorkspaces`,\n // `showOnRightClick` don't need any special treatment\n\n // Now we take care of `browserWindow`\n if (!options.browserWindow) {\n options.browserWindow = {};\n }\n\n // Set width/height on options to be usable before the window is created\n options.browserWindow.width =\n // Note: not using `options.browserWindow.width || DEFAULT_WINDOW_WIDTH` so\n // that users can put a 0 width\n options.browserWindow.width !== undefined\n ? options.browserWindow.width\n : DEFAULT_WINDOW_WIDTH;\n options.browserWindow.height =\n options.browserWindow.height !== undefined\n ? options.browserWindow.height\n : DEFAULT_WINDOW_HEIGHT;\n\n return options as Options;\n}\n","/**\n * Utilities to get taskbar position and consequently menubar's position\n */\n\n/** */\n\nimport { screen as electronScreen, type Rectangle, type Tray } from 'electron';\n\nconst isLinux = process.platform === 'linux';\n\nconst trayToScreenRects = (tray: Tray): [Rectangle, Rectangle] => {\n // There may be more than one screen, so we need to figure out on which screen our tray icon lives.\n const { workArea, bounds: screenBounds } = electronScreen.getDisplayMatching(\n tray.getBounds(),\n );\n\n workArea.x -= screenBounds.x;\n workArea.y -= screenBounds.y;\n\n return [screenBounds, workArea];\n};\n\ntype TaskbarLocation = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Determine taskbard location: \"top\", \"bottom\", \"left\" or \"right\".\n *\n * Only tested on Windows for now, and only used in Windows.\n *\n * @param tray - The Electron Tray instance.\n */\nexport function taskbarLocation(tray: Tray): TaskbarLocation {\n const [screenBounds, workArea] = trayToScreenRects(tray);\n\n // TASKBAR LEFT\n if (workArea.x > 0) {\n // Most likely Ubuntu hence assuming the window should be on top\n if (isLinux && workArea.y > 0) return 'top';\n // The workspace starts more on the right\n return 'left';\n }\n\n // TASKBAR TOP\n if (workArea.y > 0) {\n return 'top';\n }\n\n // TASKBAR RIGHT\n // Here both workArea.y and workArea.x are 0 so we can no longer leverage them.\n // We can use the workarea and display width though.\n // Determine taskbar location\n if (workArea.width < screenBounds.width) {\n // The taskbar is either on the left or right, but since the LEFT case was handled above,\n // we can be sure we're dealing with a right taskbar\n return 'right';\n }\n\n // TASKBAR BOTTOM\n // Since all the other cases were handled, we can be sure we're dealing with a bottom taskbar\n return 'bottom';\n}\n\ntype WindowPosition =\n | 'trayCenter'\n | 'topRight'\n | 'trayBottomCenter'\n | 'leftCenter'\n | 'bottomRight';\n\n/**\n * Depending on where the taskbar is, determine where the window should be\n * positioned.\n *\n * @param tray - The Electron Tray instance.\n */\nexport function getWindowPosition(tray: Tray): WindowPosition {\n switch (process.platform) {\n // macOS\n // Supports top taskbars\n case 'darwin':\n return 'trayCenter';\n // Linux\n // Windows\n // Supports top/bottom/left/right taskbar\n case 'linux':\n case 'win32': {\n const traySide = taskbarLocation(tray);\n\n // Assign position for menubar\n if (traySide === 'top') {\n return isLinux ? 'topRight' : 'trayCenter';\n }\n if (traySide === 'bottom') {\n return 'bottomRight';\n }\n if (traySide === 'left') {\n // Vertically centered against the left edge of the work area.\n // `bottomLeft` would put the window in the screen's bottom-left\n // corner — visually disconnected from the tray icon, which sits\n // somewhere on the left strip. Tray-anchored y isn't available\n // through `Positioner` for side taskbars, so center is the best\n // compromise that stays close to the tray.\n return 'leftCenter';\n }\n if (traySide === 'right') {\n return 'bottomRight';\n }\n }\n }\n\n // When we really don't know, we just show the menubar on the top-right\n return 'topRight';\n}\n","import { EventEmitter } from 'node:events';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { BrowserWindow, globalShortcut, type Menu, Tray } from 'electron';\n\nimport { Positioner } from './Positioner';\nimport type { Options } from './types';\nimport { cleanOptions } from './util/cleanOptions';\nimport { getWindowPosition } from './util/getWindowPosition';\n\n/**\n * Grace period after `showWindow()` during which a `blur` on the popup is\n * treated as the spurious Windows post-show blur and ignored instead of\n * triggering the auto-hide. See the `blur` handler in {@link Menubar.createWindow}.\n */\nconst BLUR_HIDE_GRACE_MS = 400;\n\n/**\n * Delay before re-asserting the hidden dock after startup. macOS can silently\n * drop an `app.dock.hide()` that races the app's launch activation\n * transition, leaving the dock icon stuck for the whole session. See the\n * re-check in {@link Menubar.appReady}.\n */\nconst DOCK_REHIDE_DELAY_MS = 2_000;\n\n/**\n * The main Menubar class.\n */\nexport class Menubar extends EventEmitter {\n private _app: Electron.App;\n private _browserWindow?: BrowserWindow;\n private _contextMenu?: Menu;\n private _blurTimeout: NodeJS.Timeout | null = null; // track blur events with timeout\n private _isDestroyed: boolean;\n private _isQuitting: boolean; // set on app `before-quit`, used by hideOnClose\n private _isVisible: boolean; // track visibility\n private _cachedBounds?: Electron.Rectangle; // _cachedBounds are needed for double-clicked event\n private _options: Options;\n private _positioner: Positioner | undefined;\n private _shortcut?: Electron.Accelerator;\n private _rightClickContextMenuBound = false;\n private _warnedNoPositioning = false; // guards the one-time Wayland warning\n private _lastShowTime = 0; // timestamp of last show(), debounces the post-show blur on Windows\n private _dockRehideTimeout?: NodeJS.Timeout; // pending post-startup dock re-hide check\n private _repositioning = false; // guards against re-entrant positionWindow calls\n private _tray?: Tray;\n\n constructor(app: Electron.App, options?: Partial<Options>) {\n super();\n this._app = app;\n this._options = cleanOptions(options);\n this._isDestroyed = false;\n this._isQuitting = false;\n this._isVisible = false;\n\n app.on('before-quit', this.onBeforeQuit);\n\n if (app.isReady()) {\n // See https://github.com/maxogden/menubar/pull/151\n process.nextTick(this.onAppReady);\n } else {\n app.on('ready', this.onAppReady);\n }\n }\n\n /**\n * The Electron [App](https://electronjs.org/docs/api/app)\n * instance.\n */\n get app(): Electron.App {\n return this._app;\n }\n\n /**\n * The {@link Positioner} instance used to compute where the menubar window\n * should appear on screen. Available after the `after-create-window` event.\n */\n get positioner(): Positioner {\n if (!this._positioner) {\n throw new Error(\n 'Please access `this.positioner` after the `after-create-window` event has fired.',\n );\n }\n\n return this._positioner;\n }\n\n /**\n * The Electron [Tray](https://electronjs.org/docs/api/tray) instance.\n */\n get tray(): Tray {\n if (!this._tray) {\n throw new Error(\n 'Please access `this.tray` after the `ready` event has fired.',\n );\n }\n\n return this._tray;\n }\n\n /**\n * The Electron [BrowserWindow](https://electronjs.org/docs/api/browser-window)\n * instance, if it's present.\n */\n get window(): BrowserWindow | undefined {\n return this._browserWindow;\n }\n\n /**\n * Tear down the menubar instance: destroy the window, remove the tray, and\n * detach all listeners. Subsequent clicks on the tray will be no-ops until a\n * new {@link Menubar} instance is created.\n */\n destroy(): void {\n if (this.isDestroyed()) {\n return;\n }\n // Set first so `hideOnClose` lets the close go through instead of\n // intercepting it.\n this._isDestroyed = true;\n\n if (this._shortcut) {\n globalShortcut.unregister(this._shortcut);\n this._shortcut = undefined;\n }\n\n if (this._dockRehideTimeout) {\n clearTimeout(this._dockRehideTimeout);\n this._dockRehideTimeout = undefined;\n }\n\n if (this._browserWindow) {\n this._browserWindow.destroy();\n this._browserWindow = undefined;\n }\n\n if (this._tray) {\n // Ensure all potential listeners are removed.\n for (const event of ['click', 'right-click', 'double-click']) {\n this._tray.removeListener(\n event as Parameters<Tray['on']>[0],\n this.clicked,\n );\n }\n this._tray.setToolTip('');\n this._tray = undefined;\n }\n\n this._app.removeListener('ready', this.onAppReady);\n this._app.removeListener('activate', this.onAppActivate);\n this._app.removeListener('before-quit', this.onBeforeQuit);\n }\n\n /**\n * Whether {@link destroy} has been called on this menubar instance.\n */\n isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Retrieve a menubar option.\n *\n * @param key - The option key to retrieve, see {@link Options}.\n */\n getOption<K extends keyof Options>(key: K): Options[K] {\n return this._options[key];\n }\n\n /**\n * Hide the menubar window.\n */\n hideWindow(): void {\n if (!this._browserWindow || !this._isVisible) {\n return;\n }\n this.emit('hide');\n this._browserWindow.hide();\n this.emit('after-hide');\n this._isVisible = false;\n if (this._blurTimeout) {\n clearTimeout(this._blurTimeout);\n this._blurTimeout = null;\n }\n this.refreshLinuxContextMenu();\n }\n\n /**\n * Register a global keyboard accelerator that toggles the menubar window.\n * Replaces any previously registered shortcut owned by this Menubar.\n * Pass `undefined` to clear the current shortcut without registering a new\n * one. Returns whether the registration succeeded.\n *\n * @param accelerator - An Electron\n * [Accelerator](https://electronjs.org/docs/api/accelerator) string, or\n * `undefined` to clear.\n */\n setGlobalShortcut(accelerator: Electron.Accelerator | undefined): boolean {\n if (this._shortcut) {\n globalShortcut.unregister(this._shortcut);\n this._shortcut = undefined;\n }\n this._options.globalShortcut = accelerator;\n if (!accelerator) {\n return true;\n }\n const ok = globalShortcut.register(accelerator, () => this.toggleWindow());\n if (ok) {\n this._shortcut = accelerator;\n }\n return ok;\n }\n\n /**\n * Toggle the menubar window: hide it if visible, show it otherwise.\n * Resolves once the window finishes showing or hiding.\n */\n async toggleWindow(): Promise<void> {\n if (this._browserWindow && this._isVisible) {\n this.hideWindow();\n return;\n }\n await this.showWindow();\n }\n\n /**\n * Re-center the menubar window over the tray icon. Convenience wrapper for\n * `positioner.move('trayCenter', tray.getBounds())` that's safe to call\n * after the `after-create-window` event. No-op if the window doesn't\n * exist yet.\n */\n recenterOnTray(): void {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n const bounds = this._tray.getBounds();\n const { x, y } = this.positioner.calculate('trayCenter', bounds);\n this._browserWindow.setPosition(Math.round(x), Math.round(y));\n }\n\n /**\n * Replace the tray context menu after construction. On Linux this also\n * re-publishes the menu to the SNI host, which is required after mutating\n * items in-place since libappindicator caches the previous serialization.\n * On macOS/Windows the right-click popup handler reads the current menu\n * reference, so swapping or clearing here takes effect immediately.\n *\n * @param menu - The new menu, or `null` to clear it.\n */\n setContextMenu(menu: Menu | null): void {\n this._contextMenu = menu ?? undefined;\n this._options.contextMenu = menu ?? undefined;\n if (!this._tray) {\n return;\n }\n if (process.platform === 'linux') {\n // `setContextMenu(null)` clears the menu on Linux.\n this._tray.setContextMenu(menu);\n return;\n }\n // macOS / Windows: bind the right-click popup once on first non-empty\n // assignment. The handler reads `this._contextMenu` at invoke time, so\n // later swaps and clears take effect without rebinding (and never leak\n // a stale closure reference).\n if (menu && !this._rightClickContextMenuBound) {\n this.bindRightClickContextMenu();\n }\n }\n\n /**\n * Change an option after menubar is created.\n *\n * @param key - The option key to modify, see {@link Options}.\n * @param value - The value to set.\n */\n setOption<K extends keyof Options>(key: K, value: Options[K]): void {\n this._options[key] = value;\n }\n\n /**\n * Show the menubar window.\n *\n * @param trayPos - The bounds to show the window in.\n */\n async showWindow(trayPos?: Electron.Rectangle): Promise<void> {\n if (!this.tray) {\n throw new Error('Tray should have been instantiated by now');\n }\n\n if (!this._browserWindow) {\n await this.createWindow();\n }\n\n // Use guard for TypeScript, to avoid ! everywhere\n if (!this._browserWindow) {\n throw new Error('Window has been initialized just above. qed.');\n }\n\n this.emit('show');\n\n // Cache fresh tray bounds (or fall back to existing cache / tray bounds)\n // so `positionWindow` can reposition without an event payload — for\n // example when the window resizes via `setSize`.\n if (trayPos && trayPos.x !== 0) {\n this._cachedBounds = trayPos;\n } else if (!this._cachedBounds && this.tray.getBounds) {\n this._cachedBounds = this.tray.getBounds();\n }\n\n this.positionWindow();\n // Record the show time before `show()` so the blur handler can recognise\n // and ignore the transient blur Windows fires right after (see below).\n this._lastShowTime = Date.now();\n this._browserWindow.show();\n this._isVisible = true;\n this.emit('after-show');\n this.refreshLinuxContextMenu();\n }\n\n /**\n * Compute and apply the tray-anchored position of the browser window. Safe\n * to call any time after `createWindow` has run — invoked from\n * {@link showWindow} on every show, and from the window's `resize` event so\n * `setSize` calls reposition the window correctly.\n */\n private positionWindow = (): void => {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n\n // `setPosition` below can make Windows emit `resize` synchronously, which\n // re-enters this method through the window's `resize` listener. Each pass\n // nudges the window again, so the recursion never unwinds: the main\n // process wedges with the event loop blocked and the window grows past\n // screen size (gitify-app/gitify#3064). Ignore re-entrant calls; the\n // outermost one already applies the final position.\n if (this._repositioning) {\n return;\n }\n this._repositioning = true;\n try {\n this.applyWindowPosition();\n } finally {\n this._repositioning = false;\n }\n };\n\n /**\n * Compute and apply the tray-anchored position. Always call through\n * {@link positionWindow}, which guards against re-entrancy.\n */\n private applyWindowPosition(): void {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n\n // 'Windows' taskbar: sync window position each time before positioning.\n // https://github.com/maxogden/menubar/issues/232\n if (['win32', 'linux'].includes(process.platform)) {\n this._options.windowPosition = getWindowPosition(this._tray);\n }\n\n const trayPos = this._cachedBounds ?? this._tray.getBounds?.();\n\n // Default the window to the right if `trayPos` bounds are undefined or null.\n let noBoundsPosition: Options['windowPosition'];\n if (\n (trayPos === undefined || trayPos.x === 0) &&\n this._options.windowPosition?.startsWith('tray')\n ) {\n noBoundsPosition =\n process.platform === 'win32' ? 'bottomRight' : 'topRight';\n }\n\n const position = this.positioner.calculate(\n this._options.windowPosition || noBoundsPosition,\n trayPos,\n ) as { x: number; y: number };\n\n // Not using `||` because x and y can be zero.\n const x =\n this._options.browserWindow.x !== undefined\n ? this._options.browserWindow.x\n : position.x;\n const y =\n this._options.browserWindow.y !== undefined\n ? this._options.browserWindow.y\n : position.y;\n\n // `.setPosition` crashed on non-integers\n // https://github.com/maxogden/menubar/issues/233\n const targetX = Math.round(x);\n const targetY = Math.round(y);\n this._browserWindow.setPosition(targetX, targetY);\n\n // Native Wayland gives an application no way to position its own window:\n // `setPosition` is a no-op and `getPosition` reads back [0, 0]\n // (https://github.com/electron/electron/issues/40886). The compositor\n // decides where the window lands, usually centered, so the popover can't\n // be anchored to the tray. Detect that the move didn't take and warn once,\n // pointing at the X11 fallback. Gated on a Wayland session (`WAYLAND_DISPLAY`)\n // so pure X11 never trips it; the tolerance absorbs the few-pixel offsets\n // X11 window managers add for decorations, so XWayland (where positioning\n // works) doesn't trip it either.\n if (\n process.platform === 'linux' &&\n !!process.env.WAYLAND_DISPLAY &&\n !this._warnedNoPositioning\n ) {\n const [actualX, actualY] = this._browserWindow.getPosition();\n const ignored =\n Math.abs(actualX - targetX) > 24 || Math.abs(actualY - targetY) > 24;\n if (ignored) {\n this._warnedNoPositioning = true;\n console.warn(\n '[menubar] The window could not be positioned programmatically, ' +\n 'which is expected on native Wayland where the compositor controls ' +\n 'placement. The popover will not be anchored to the tray icon. Run ' +\n 'with --ozone-platform=x11 to restore tray-relative positioning.',\n );\n }\n }\n }\n\n private async appReady(): Promise<void> {\n if (this.app.dock && !this._options.showDockIcon) {\n this.app.dock.hide();\n\n // The hide above can be silently dropped when it races the launch\n // activation transition. Re-check once startup has settled; guarded on\n // visibility because `dock.hide()` also deactivates the app, so it must\n // not run when the dock is already hidden.\n this._dockRehideTimeout = setTimeout(() => {\n if (!this._isDestroyed && this.app.dock?.isVisible()) {\n this.app.dock.hide();\n }\n }, DOCK_REHIDE_DELAY_MS);\n }\n\n if (this._options.activateWithApp) {\n this.app.on('activate', this.onAppActivate);\n }\n\n let trayImage =\n this._options.icon || path.join(this._options.dir, 'IconTemplate.png');\n if (typeof trayImage === 'string' && !fs.existsSync(trayImage)) {\n trayImage = path.join(__dirname, '..', 'assets', 'IconTemplate.png'); // Default cat icon\n }\n\n const trigger =\n this._options.trigger ??\n (this._options.showOnRightClick ? 'right-click' : 'click');\n\n this._tray = this._options.tray || new Tray(trayImage);\n // Type guards for TS not to complain\n if (!this.tray) {\n throw new Error('Tray has been initialized above');\n }\n if (trigger !== 'none') {\n this.tray.on(trigger as Parameters<Tray['on']>[0], this.clicked);\n this.tray.on('double-click', this.clicked);\n }\n // macOS-only: ignore double-click so an accidental second click doesn't\n // race the blur handler and cause a tray-icon flicker.\n if (\n process.platform === 'darwin' &&\n this._options.ignoreDoubleClickEvents\n ) {\n this.tray.setIgnoreDoubleClickEvents(true);\n }\n this.tray.setToolTip(this._options.tooltip);\n\n if (this._options.contextMenu) {\n this.bindContextMenu(this._options.contextMenu);\n }\n\n if (this._options.globalShortcut) {\n this.setGlobalShortcut(this._options.globalShortcut);\n }\n\n if (!this._options.windowPosition) {\n this._options.windowPosition = getWindowPosition(this.tray);\n }\n\n if (this._options.preloadWindow) {\n await this.createWindow();\n }\n\n this.emit('ready');\n }\n\n /**\n * Callback on tray icon click or double-click.\n *\n * @param e\n * @param bounds\n */\n private clicked = async (\n event?: Electron.KeyboardEvent,\n bounds?: Electron.Rectangle,\n ): Promise<void> => {\n if (event && (event.shiftKey || event.ctrlKey || event.metaKey)) {\n return this.hideWindow();\n }\n\n // if blur was invoked clear timeout\n if (this._blurTimeout) {\n clearInterval(this._blurTimeout);\n }\n\n if (this._browserWindow && this._isVisible) {\n return this.hideWindow();\n }\n\n this._cachedBounds = bounds || this._cachedBounds;\n await this.showWindow(this._cachedBounds);\n };\n\n private onAppActivate = (\n _event: Electron.Event,\n hasVisibleWindows: boolean,\n ): void => {\n if (!hasVisibleWindows) {\n this.showWindow().catch(console.error);\n }\n };\n\n private onBeforeQuit = (): void => {\n this._isQuitting = true;\n };\n\n private bindContextMenu(menu: Menu): void {\n this._contextMenu = menu;\n if (process.platform === 'linux') {\n // libappindicator / StatusNotifierItem requires the menu to live on the\n // tray itself; right-click is handled by the desktop environment.\n this.tray.setContextMenu(menu);\n return;\n }\n this.bindRightClickContextMenu();\n }\n\n private bindRightClickContextMenu(): void {\n // macOS / Windows: pop up the current menu on right-click so left-click\n // stays bound to toggling the menubar window. Read `this._contextMenu`\n // at invoke time so `setContextMenu()` swaps and clears take effect\n // without rebinding (and without leaking a stale closure reference).\n this.tray.on('right-click', (_event, bounds) => {\n const current = this._contextMenu;\n if (!current) {\n return;\n }\n this.tray.popUpContextMenu(current, { x: bounds.x, y: bounds.y });\n });\n this._rightClickContextMenuBound = true;\n }\n\n private refreshLinuxContextMenu(): void {\n // libappindicator caches the menu's serialized state, so consumers that\n // mutate items in-place need the menu re-published. Cheap to do; safe to\n // call unconditionally on every show/hide.\n if (\n process.platform === 'linux' &&\n this._contextMenu &&\n this._tray &&\n !this._tray.isDestroyed?.()\n ) {\n this._tray.setContextMenu(this._contextMenu);\n }\n }\n\n private onAppReady = (): void => {\n // Guard against `destroy()` being called between construction and the\n // scheduled `process.nextTick`/`'ready'` firing.\n if (this._isDestroyed) {\n return;\n }\n this.appReady().catch((err) => console.error('menubar: ', err));\n };\n\n private async createWindow(): Promise<void> {\n this.emit('create-window');\n\n // We add some default behavior for menubar's browserWindow, to make it\n // look like a menubar\n const defaults = {\n show: false, // Don't show it at first\n frame: false, // Remove window frame\n };\n\n this._browserWindow = new BrowserWindow({\n ...defaults,\n ...this._options.browserWindow,\n });\n\n this._positioner = new Positioner(this._browserWindow);\n\n this._browserWindow.on('blur', () => {\n if (!this._browserWindow) {\n return;\n }\n\n // The window was pinned (e.g. a host \"keep open on blur\" preference);\n // don't auto-hide, just surface the event for the host app to react to.\n if (this._browserWindow.isAlwaysOnTop()) {\n this.emit('focus-lost');\n return;\n }\n\n // Windows foreground-activation race: clicking the tray icon keeps the\n // shell (explorer.exe) as the foreground process, so `show()` cannot pull\n // focus to the popup and Windows immediately fires `blur`. Left unguarded,\n // the hide timer below fires before the first paint and the window never\n // visibly appears (gitify-app/gitify#3064). Ignore blur events that land\n // within the grace window right after a show; a genuine click-away\n // arrives well after it.\n if (\n process.platform === 'win32' &&\n Date.now() - this._lastShowTime < BLUR_HIDE_GRACE_MS\n ) {\n return;\n }\n\n this._blurTimeout = setTimeout(() => {\n this.hideWindow();\n }, 100);\n });\n\n if (this._options.showOnAllWorkspaces !== false) {\n // https://github.com/electron/electron/issues/37832#issuecomment-1497882944\n this._browserWindow.setVisibleOnAllWorkspaces(true, {\n skipTransformProcessType: true, // Avoid damaging the original visible state of app.dock\n });\n }\n\n if (this._options.hideOnClose) {\n this._browserWindow.on('close', (event) => {\n if (this._isDestroyed || this._isQuitting) {\n return;\n }\n event.preventDefault();\n // Defer the hide for Wayland: hiding synchronously from the `close`\n // handler can leave frameless surfaces in a half-closed state.\n setImmediate(() => this.hideWindow());\n });\n }\n\n if (this._options.escapeToHide) {\n this._browserWindow.webContents.on(\n 'before-input-event',\n (_event, input) => {\n if (input.type === 'keyDown' && input.key === 'Escape') {\n this.hideWindow();\n }\n },\n );\n }\n\n // Use `closed` (not `close`) so consumer `close` listeners can still read\n // `mb.window` and call `event.preventDefault()` without racing our cleanup.\n this._browserWindow.on('closed', this.windowClear.bind(this));\n\n // Re-anchor the window to the tray when its size changes (e.g. via\n // `mb.window.setSize(...)`), so the window doesn't end up clipped under\n // the taskbar. https://github.com/maxogden/menubar/issues/349\n this._browserWindow.on('resize', this.positionWindow);\n\n this.emit('before-load');\n\n // If the user explicity set options.index to false, we don't loadURL\n // https://github.com/maxogden/menubar/issues/255\n if (this._options.index !== false) {\n await this._browserWindow.loadURL(\n this._options.index,\n this._options.loadUrlOptions,\n );\n }\n this.emit('after-create-window');\n }\n\n private windowClear(): void {\n this._browserWindow = undefined;\n this.emit('after-close');\n }\n}\n","/**\n * Entry point of menubar\n * @example\n * ```typescript\n * import { menubar } from 'menubar';\n * ```\n */\n\n/** */\n\nimport { app } from 'electron';\n\nimport { Menubar } from './Menubar';\nimport type { Options } from './types';\n\nexport * from './util/getWindowPosition';\nexport { Menubar };\n\n/**\n * Factory function to create a menubar application\n *\n * @param options - Options for creating a menubar application, see\n * {@link Options}\n */\nexport function menubar(options?: Partial<Options>): Menubar {\n return new Menubar(app, options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["screen","app","path","url","electronScreen","EventEmitter","globalShortcut","path","fs","Tray","BrowserWindow","app"],"sources":["../src/Positioner.ts","../src/util/cleanOptions.ts","../src/util/getWindowPosition.ts","../src/Menubar.ts","../src/index.ts"],"sourcesContent":["import { type BrowserWindow, type Rectangle, screen } from 'electron';\n\n/**\n * Named anchor points for placing the menubar window. The `tray*` values are\n * relative to the tray icon's bounds; the rest are relative to the work area\n * of the display containing the cursor (or the tray, when bounds are given).\n */\nexport type WindowPosition =\n | 'trayLeft'\n | 'trayBottomLeft'\n | 'trayRight'\n | 'trayBottomRight'\n | 'trayCenter'\n | 'trayBottomCenter'\n | 'topLeft'\n | 'topRight'\n | 'bottomLeft'\n | 'bottomRight'\n | 'topCenter'\n | 'bottomCenter'\n | 'leftCenter'\n | 'rightCenter'\n | 'center';\n\n/**\n * Computes `{x, y}` coordinates for placing a {@link BrowserWindow} at a named\n * position, optionally relative to a tray icon's bounds. Ported from\n * `electron-positioner@4.1.0` to drop the unmaintained runtime dependency.\n */\nexport class Positioner {\n private readonly browserWindow: BrowserWindow;\n\n constructor(browserWindow: BrowserWindow) {\n this.browserWindow = browserWindow;\n }\n\n calculate(\n position?: WindowPosition,\n trayBounds?: Rectangle,\n ): { x: number; y: number } {\n if (!position) {\n throw new TypeError(\n 'Positioner.calculate: a `position` argument is required.',\n );\n }\n\n const screenSize = trayBounds\n ? screen.getDisplayMatching(trayBounds).workArea\n : screen.getDisplayNearestPoint(screen.getCursorScreenPoint()).workArea;\n const [windowWidth, windowHeight] = this.browserWindow.getSize();\n const trayX = trayBounds?.x ?? Number.NaN;\n const trayWidth = trayBounds?.width ?? Number.NaN;\n\n const positions: Record<WindowPosition, { x: number; y: number }> = {\n trayLeft: {\n x: Math.floor(trayX),\n y: screenSize.y,\n },\n trayBottomLeft: {\n x: Math.floor(trayX),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n trayRight: {\n x: Math.floor(trayX - windowWidth + trayWidth),\n y: screenSize.y,\n },\n trayBottomRight: {\n x: Math.floor(trayX - windowWidth + trayWidth),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n trayCenter: {\n x: Math.floor(trayX - windowWidth / 2 + trayWidth / 2),\n y: screenSize.y,\n },\n trayBottomCenter: {\n x: Math.floor(trayX - windowWidth / 2 + trayWidth / 2),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n topLeft: {\n x: screenSize.x,\n y: screenSize.y,\n },\n topRight: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y: screenSize.y,\n },\n bottomLeft: {\n x: screenSize.x,\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n bottomRight: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n topCenter: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: screenSize.y,\n },\n bottomCenter: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n leftCenter: {\n x: screenSize.x,\n y:\n screenSize.y +\n Math.floor(screenSize.height / 2) -\n Math.floor(windowHeight / 2),\n },\n rightCenter: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y:\n screenSize.y +\n Math.floor(screenSize.height / 2) -\n Math.floor(windowHeight / 2),\n },\n center: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: Math.floor(\n (screenSize.height + screenSize.y) / 2 - windowHeight / 2,\n ),\n },\n };\n\n const coords = positions[position];\n\n // On Windows, a tray-relative position can push the window past the right\n // edge of the work area. Snap back to `topRight` x in that case so it stays\n // visible. See https://github.com/jenslind/electron-positioner.\n if (position.startsWith('tray')) {\n if (coords.x + windowWidth > screenSize.width + screenSize.x) {\n return { x: positions.topRight.x, y: coords.y };\n }\n }\n\n return coords;\n }\n}\n","/**\n * @ignore\n */\n\n/** */\n\nimport path from 'node:path';\nimport url from 'node:url';\n\nimport { app } from 'electron';\n\nimport type { Options } from '../types';\n\nconst DEFAULT_WINDOW_HEIGHT = 400;\nconst DEFAULT_WINDOW_WIDTH = 400;\n\n/**\n * Take as input some options, and return a sanitized version of it.\n *\n * @param opts - The options to clean.\n * @ignore\n */\nexport function cleanOptions(opts?: Partial<Options>): Options {\n const options: Partial<Options> = { ...opts };\n\n if (options.activateWithApp === undefined) {\n options.activateWithApp = true;\n }\n if (options.ignoreDoubleClickEvents === undefined) {\n options.ignoreDoubleClickEvents = true;\n }\n if (!options.dir) {\n options.dir = app.getAppPath();\n }\n if (!path.isAbsolute(options.dir)) {\n options.dir = path.resolve(options.dir);\n }\n // Note: options.index can be `false`\n if (options.index === undefined) {\n options.index = url.format({\n pathname: path.join(options.dir, 'index.html'),\n protocol: 'file:',\n slashes: true,\n });\n }\n options.loadUrlOptions = options.loadUrlOptions || {};\n\n options.tooltip = options.tooltip || '';\n\n // `icon`, `preloadWindow`, `showDockIcon`, `showOnAllWorkspaces`,\n // `showOnRightClick` don't need any special treatment\n\n // Now we take care of `browserWindow`\n if (!options.browserWindow) {\n options.browserWindow = {};\n }\n\n // Set width/height on options to be usable before the window is created\n options.browserWindow.width =\n // Note: not using `options.browserWindow.width || DEFAULT_WINDOW_WIDTH` so\n // that users can put a 0 width\n options.browserWindow.width !== undefined\n ? options.browserWindow.width\n : DEFAULT_WINDOW_WIDTH;\n options.browserWindow.height =\n options.browserWindow.height !== undefined\n ? options.browserWindow.height\n : DEFAULT_WINDOW_HEIGHT;\n\n return options as Options;\n}\n","/**\n * Utilities to get taskbar position and consequently menubar's position\n */\n\n/** */\n\nimport { screen as electronScreen, type Rectangle, type Tray } from 'electron';\n\nconst isLinux = process.platform === 'linux';\n\nconst trayToScreenRects = (tray: Tray): [Rectangle, Rectangle] => {\n // There may be more than one screen, so we need to figure out on which screen our tray icon lives.\n const { workArea, bounds: screenBounds } = electronScreen.getDisplayMatching(\n tray.getBounds(),\n );\n\n workArea.x -= screenBounds.x;\n workArea.y -= screenBounds.y;\n\n return [screenBounds, workArea];\n};\n\ntype TaskbarLocation = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Determine taskbard location: \"top\", \"bottom\", \"left\" or \"right\".\n *\n * Only tested on Windows for now, and only used in Windows.\n *\n * @param tray - The Electron Tray instance.\n */\nexport function taskbarLocation(tray: Tray): TaskbarLocation {\n const [screenBounds, workArea] = trayToScreenRects(tray);\n\n // TASKBAR LEFT\n if (workArea.x > 0) {\n // Most likely Ubuntu hence assuming the window should be on top\n if (isLinux && workArea.y > 0) return 'top';\n // The workspace starts more on the right\n return 'left';\n }\n\n // TASKBAR TOP\n if (workArea.y > 0) {\n return 'top';\n }\n\n // TASKBAR RIGHT\n // Here both workArea.y and workArea.x are 0 so we can no longer leverage them.\n // We can use the workarea and display width though.\n // Determine taskbar location\n if (workArea.width < screenBounds.width) {\n // The taskbar is either on the left or right, but since the LEFT case was handled above,\n // we can be sure we're dealing with a right taskbar\n return 'right';\n }\n\n // TASKBAR BOTTOM\n // Since all the other cases were handled, we can be sure we're dealing with a bottom taskbar\n return 'bottom';\n}\n\ntype WindowPosition =\n | 'trayCenter'\n | 'topRight'\n | 'trayBottomCenter'\n | 'leftCenter'\n | 'bottomRight';\n\n/**\n * Depending on where the taskbar is, determine where the window should be\n * positioned.\n *\n * @param tray - The Electron Tray instance.\n */\nexport function getWindowPosition(tray: Tray): WindowPosition {\n switch (process.platform) {\n // macOS\n // Supports top taskbars\n case 'darwin':\n return 'trayCenter';\n // Linux\n // Windows\n // Supports top/bottom/left/right taskbar\n case 'linux':\n case 'win32': {\n const traySide = taskbarLocation(tray);\n\n // Assign position for menubar\n if (traySide === 'top') {\n return isLinux ? 'topRight' : 'trayCenter';\n }\n if (traySide === 'bottom') {\n return 'bottomRight';\n }\n if (traySide === 'left') {\n // Vertically centered against the left edge of the work area.\n // `bottomLeft` would put the window in the screen's bottom-left\n // corner — visually disconnected from the tray icon, which sits\n // somewhere on the left strip. Tray-anchored y isn't available\n // through `Positioner` for side taskbars, so center is the best\n // compromise that stays close to the tray.\n return 'leftCenter';\n }\n if (traySide === 'right') {\n return 'bottomRight';\n }\n }\n }\n\n // When we really don't know, we just show the menubar on the top-right\n return 'topRight';\n}\n","import { EventEmitter } from 'node:events';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n autoUpdater,\n BrowserWindow,\n globalShortcut,\n type Menu,\n Tray,\n} from 'electron';\n\nimport { Positioner } from './Positioner';\nimport type { Options } from './types';\nimport { cleanOptions } from './util/cleanOptions';\nimport { getWindowPosition } from './util/getWindowPosition';\n\n/**\n * Grace period after `showWindow()` during which a `blur` on the popup is\n * treated as the spurious Windows post-show blur and ignored instead of\n * triggering the auto-hide. See the `blur` handler in {@link Menubar.createWindow}.\n */\nconst BLUR_HIDE_GRACE_MS = 400;\n\n/**\n * Delay before re-asserting the hidden dock after startup. macOS can silently\n * drop an `app.dock.hide()` that races the app's launch activation\n * transition, leaving the dock icon stuck for the whole session. See the\n * re-check in {@link Menubar.appReady}.\n */\nconst DOCK_REHIDE_DELAY_MS = 2_000;\n\n/**\n * The main Menubar class.\n */\nexport class Menubar extends EventEmitter {\n private _app: Electron.App;\n private _browserWindow?: BrowserWindow;\n private _contextMenu?: Menu;\n private _blurTimeout: NodeJS.Timeout | null = null; // track blur events with timeout\n private _isDestroyed: boolean;\n private _isQuitting: boolean; // set when the app is shutting down, used by hideOnClose\n private _isVisible: boolean; // track visibility\n private _cachedBounds?: Electron.Rectangle; // _cachedBounds are needed for double-clicked event\n private _options: Options;\n private _positioner: Positioner | undefined;\n private _shortcut?: Electron.Accelerator;\n private _rightClickContextMenuBound = false;\n private _warnedNoPositioning = false; // guards the one-time Wayland warning\n private _lastShowTime = 0; // timestamp of last show(), debounces the post-show blur on Windows\n private _dockRehideTimeout?: NodeJS.Timeout; // pending post-startup dock re-hide check\n private _repositioning = false; // guards against re-entrant positionWindow calls\n private _tray?: Tray;\n\n constructor(app: Electron.App, options?: Partial<Options>) {\n super();\n this._app = app;\n this._options = cleanOptions(options);\n this._isDestroyed = false;\n this._isQuitting = false;\n this._isVisible = false;\n\n app.on('before-quit', this.onBeforeQuit);\n autoUpdater.on('before-quit-for-update', this.onBeforeQuit);\n\n if (app.isReady()) {\n // See https://github.com/maxogden/menubar/pull/151\n process.nextTick(this.onAppReady);\n } else {\n app.on('ready', this.onAppReady);\n }\n }\n\n /**\n * The Electron [App](https://electronjs.org/docs/api/app)\n * instance.\n */\n get app(): Electron.App {\n return this._app;\n }\n\n /**\n * The {@link Positioner} instance used to compute where the menubar window\n * should appear on screen. Available after the `after-create-window` event.\n */\n get positioner(): Positioner {\n if (!this._positioner) {\n throw new Error(\n 'Please access `this.positioner` after the `after-create-window` event has fired.',\n );\n }\n\n return this._positioner;\n }\n\n /**\n * The Electron [Tray](https://electronjs.org/docs/api/tray) instance.\n */\n get tray(): Tray {\n if (!this._tray) {\n throw new Error(\n 'Please access `this.tray` after the `ready` event has fired.',\n );\n }\n\n return this._tray;\n }\n\n /**\n * The Electron [BrowserWindow](https://electronjs.org/docs/api/browser-window)\n * instance, if it's present.\n */\n get window(): BrowserWindow | undefined {\n return this._browserWindow;\n }\n\n /**\n * Tear down the menubar instance: destroy the window, remove the tray, and\n * detach all listeners. Subsequent clicks on the tray will be no-ops until a\n * new {@link Menubar} instance is created.\n */\n destroy(): void {\n if (this.isDestroyed()) {\n return;\n }\n // Set first so `hideOnClose` lets the close go through instead of\n // intercepting it.\n this._isDestroyed = true;\n\n if (this._shortcut) {\n globalShortcut.unregister(this._shortcut);\n this._shortcut = undefined;\n }\n\n if (this._dockRehideTimeout) {\n clearTimeout(this._dockRehideTimeout);\n this._dockRehideTimeout = undefined;\n }\n\n if (this._browserWindow) {\n this._browserWindow.destroy();\n this._browserWindow = undefined;\n }\n\n if (this._tray) {\n // Ensure all potential listeners are removed.\n for (const event of ['click', 'right-click', 'double-click']) {\n this._tray.removeListener(\n event as Parameters<Tray['on']>[0],\n this.clicked,\n );\n }\n this._tray.setToolTip('');\n this._tray = undefined;\n }\n\n this._app.removeListener('ready', this.onAppReady);\n this._app.removeListener('activate', this.onAppActivate);\n this._app.removeListener('before-quit', this.onBeforeQuit);\n autoUpdater.removeListener('before-quit-for-update', this.onBeforeQuit);\n }\n\n /**\n * Whether {@link destroy} has been called on this menubar instance.\n */\n isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Retrieve a menubar option.\n *\n * @param key - The option key to retrieve, see {@link Options}.\n */\n getOption<K extends keyof Options>(key: K): Options[K] {\n return this._options[key];\n }\n\n /**\n * Hide the menubar window.\n */\n hideWindow(): void {\n if (!this._browserWindow || !this._isVisible) {\n return;\n }\n this.emit('hide');\n this._browserWindow.hide();\n this.emit('after-hide');\n this._isVisible = false;\n if (this._blurTimeout) {\n clearTimeout(this._blurTimeout);\n this._blurTimeout = null;\n }\n this.refreshContextMenu();\n }\n\n /**\n * Register a global keyboard accelerator that toggles the menubar window.\n * Replaces any previously registered shortcut owned by this Menubar.\n * Pass `undefined` to clear the current shortcut without registering a new\n * one. Returns whether the registration succeeded.\n *\n * @param accelerator - An Electron\n * [Accelerator](https://electronjs.org/docs/api/accelerator) string, or\n * `undefined` to clear.\n */\n setGlobalShortcut(accelerator: Electron.Accelerator | undefined): boolean {\n if (this._shortcut) {\n globalShortcut.unregister(this._shortcut);\n this._shortcut = undefined;\n }\n this._options.globalShortcut = accelerator;\n if (!accelerator) {\n return true;\n }\n const ok = globalShortcut.register(accelerator, () => this.toggleWindow());\n if (ok) {\n this._shortcut = accelerator;\n }\n return ok;\n }\n\n /**\n * Toggle the menubar window: hide it if visible, show it otherwise.\n * Resolves once the window finishes showing or hiding.\n */\n async toggleWindow(): Promise<void> {\n if (this._browserWindow && this._isVisible) {\n this.hideWindow();\n return;\n }\n await this.showWindow();\n }\n\n /**\n * Re-center the menubar window over the tray icon. Convenience wrapper for\n * `positioner.move('trayCenter', tray.getBounds())` that's safe to call\n * after the `after-create-window` event. No-op if the window doesn't\n * exist yet.\n */\n recenterOnTray(): void {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n const bounds = this._tray.getBounds();\n const { x, y } = this.positioner.calculate('trayCenter', bounds);\n this._browserWindow.setPosition(Math.round(x), Math.round(y));\n }\n\n /**\n * Replace the tray context menu after construction. On Linux this also\n * re-publishes the menu to the SNI host, which is required after mutating\n * items in-place since libappindicator caches the previous serialization.\n * On macOS/Windows the right-click popup handler reads the current menu\n * reference, so swapping or clearing here takes effect immediately.\n *\n * @param menu - The new menu, or `null` to clear it.\n */\n setContextMenu(menu: Menu | null): void {\n this._contextMenu = menu ?? undefined;\n this._options.contextMenu = menu ?? undefined;\n if (!this._tray) {\n return;\n }\n if (process.platform === 'linux') {\n // `setContextMenu(null)` clears the menu on Linux.\n this._tray.setContextMenu(menu);\n return;\n }\n // macOS / Windows: bind the right-click popup once on first non-empty\n // assignment. The handler reads `this._contextMenu` at invoke time, so\n // later swaps and clears take effect without rebinding (and never leak\n // a stale closure reference).\n if (menu && !this._rightClickContextMenuBound) {\n this.bindRightClickContextMenu();\n }\n }\n\n /**\n * Re-publish the current context menu so in-place mutations of its items\n * (`visible`, `enabled`, `checked`, `label`) become visible to the user.\n *\n * Only Linux needs this: libappindicator / StatusNotifierItem serializes the\n * menu once and serves that copy until it is set again, so a mutated item\n * would otherwise keep rendering its old state until the next show or hide.\n * A no-op on macOS and Windows, where the menu is read at popup time — call\n * it unconditionally after mutating items.\n */\n refreshContextMenu(): void {\n if (\n process.platform === 'linux' &&\n this._contextMenu &&\n this._tray &&\n !this._tray.isDestroyed?.()\n ) {\n this._tray.setContextMenu(this._contextMenu);\n }\n }\n\n /**\n * Change an option after menubar is created.\n *\n * @param key - The option key to modify, see {@link Options}.\n * @param value - The value to set.\n */\n setOption<K extends keyof Options>(key: K, value: Options[K]): void {\n this._options[key] = value;\n }\n\n /**\n * Show the menubar window.\n *\n * @param trayPos - The bounds to show the window in.\n */\n async showWindow(trayPos?: Electron.Rectangle): Promise<void> {\n if (!this.tray) {\n throw new Error('Tray should have been instantiated by now');\n }\n\n if (!this._browserWindow) {\n await this.createWindow();\n }\n\n // Use guard for TypeScript, to avoid ! everywhere\n if (!this._browserWindow) {\n throw new Error('Window has been initialized just above. qed.');\n }\n\n this.emit('show');\n\n // Cache fresh tray bounds (or fall back to existing cache / tray bounds)\n // so `positionWindow` can reposition without an event payload — for\n // example when the window resizes via `setSize`.\n if (trayPos && trayPos.x !== 0) {\n this._cachedBounds = trayPos;\n } else if (!this._cachedBounds && this.tray.getBounds) {\n this._cachedBounds = this.tray.getBounds();\n }\n\n this.positionWindow();\n // Record the show time before `show()` so the blur handler can recognise\n // and ignore the transient blur Windows fires right after (see below).\n this._lastShowTime = Date.now();\n this._browserWindow.show();\n this._isVisible = true;\n this.emit('after-show');\n this.refreshContextMenu();\n }\n\n /**\n * Compute and apply the tray-anchored position of the browser window. Safe\n * to call any time after `createWindow` has run — invoked from\n * {@link showWindow} on every show, and from the window's `resize` event so\n * `setSize` calls reposition the window correctly.\n */\n private positionWindow = (): void => {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n\n // `setPosition` below can make Windows emit `resize` synchronously, which\n // re-enters this method through the window's `resize` listener. Each pass\n // nudges the window again, so the recursion never unwinds: the main\n // process wedges with the event loop blocked and the window grows past\n // screen size (gitify-app/gitify#3064). Ignore re-entrant calls; the\n // outermost one already applies the final position.\n if (this._repositioning) {\n return;\n }\n this._repositioning = true;\n try {\n this.applyWindowPosition();\n } finally {\n this._repositioning = false;\n }\n };\n\n /**\n * Compute and apply the tray-anchored position. Always call through\n * {@link positionWindow}, which guards against re-entrancy.\n */\n private applyWindowPosition(): void {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n\n // 'Windows' taskbar: sync window position each time before positioning.\n // https://github.com/maxogden/menubar/issues/232\n if (['win32', 'linux'].includes(process.platform)) {\n this._options.windowPosition = getWindowPosition(this._tray);\n }\n\n const trayPos = this._cachedBounds ?? this._tray.getBounds?.();\n\n // Default the window to the right if `trayPos` bounds are undefined or null.\n let noBoundsPosition: Options['windowPosition'];\n if (\n (trayPos === undefined || trayPos.x === 0) &&\n this._options.windowPosition?.startsWith('tray')\n ) {\n noBoundsPosition =\n process.platform === 'win32' ? 'bottomRight' : 'topRight';\n }\n\n const position = this.positioner.calculate(\n this._options.windowPosition || noBoundsPosition,\n trayPos,\n ) as { x: number; y: number };\n\n // Not using `||` because x and y can be zero.\n const x =\n this._options.browserWindow.x !== undefined\n ? this._options.browserWindow.x\n : position.x;\n const y =\n this._options.browserWindow.y !== undefined\n ? this._options.browserWindow.y\n : position.y;\n\n // `.setPosition` crashed on non-integers\n // https://github.com/maxogden/menubar/issues/233\n const targetX = Math.round(x);\n const targetY = Math.round(y);\n this._browserWindow.setPosition(targetX, targetY);\n\n // Native Wayland gives an application no way to position its own window:\n // `setPosition` is a no-op and `getPosition` reads back [0, 0]\n // (https://github.com/electron/electron/issues/40886). The compositor\n // decides where the window lands, usually centered, so the popover can't\n // be anchored to the tray. Detect that the move didn't take and warn once,\n // pointing at the X11 fallback. Gated on a Wayland session (`WAYLAND_DISPLAY`)\n // so pure X11 never trips it; the tolerance absorbs the few-pixel offsets\n // X11 window managers add for decorations, so XWayland (where positioning\n // works) doesn't trip it either.\n if (\n process.platform === 'linux' &&\n !!process.env.WAYLAND_DISPLAY &&\n !this._warnedNoPositioning\n ) {\n const [actualX, actualY] = this._browserWindow.getPosition();\n const ignored =\n Math.abs(actualX - targetX) > 24 || Math.abs(actualY - targetY) > 24;\n if (ignored) {\n this._warnedNoPositioning = true;\n console.warn(\n '[menubar] The window could not be positioned programmatically, ' +\n 'which is expected on native Wayland where the compositor controls ' +\n 'placement. The popover will not be anchored to the tray icon. Run ' +\n 'with --ozone-platform=x11 to restore tray-relative positioning.',\n );\n }\n }\n }\n\n private async appReady(): Promise<void> {\n if (this.app.dock && !this._options.showDockIcon) {\n this.app.dock.hide();\n\n // The hide above can be silently dropped when it races the launch\n // activation transition. Re-check once startup has settled; guarded on\n // visibility because `dock.hide()` also deactivates the app, so it must\n // not run when the dock is already hidden.\n this._dockRehideTimeout = setTimeout(() => {\n if (!this._isDestroyed && this.app.dock?.isVisible()) {\n this.app.dock.hide();\n }\n }, DOCK_REHIDE_DELAY_MS);\n }\n\n if (this._options.activateWithApp) {\n this.app.on('activate', this.onAppActivate);\n }\n\n let trayImage =\n this._options.icon || path.join(this._options.dir, 'IconTemplate.png');\n if (typeof trayImage === 'string' && !fs.existsSync(trayImage)) {\n trayImage = path.join(__dirname, '..', 'assets', 'IconTemplate.png'); // Default cat icon\n }\n\n const trigger =\n this._options.trigger ??\n (this._options.showOnRightClick ? 'right-click' : 'click');\n\n this._tray = this._options.tray || new Tray(trayImage);\n // Type guards for TS not to complain\n if (!this.tray) {\n throw new Error('Tray has been initialized above');\n }\n if (trigger !== 'none') {\n this.tray.on(trigger as Parameters<Tray['on']>[0], this.clicked);\n this.tray.on('double-click', this.clicked);\n }\n // macOS-only: ignore double-click so an accidental second click doesn't\n // race the blur handler and cause a tray-icon flicker.\n if (\n process.platform === 'darwin' &&\n this._options.ignoreDoubleClickEvents\n ) {\n this.tray.setIgnoreDoubleClickEvents(true);\n }\n this.tray.setToolTip(this._options.tooltip);\n\n if (this._options.contextMenu) {\n this.bindContextMenu(this._options.contextMenu);\n }\n\n if (this._options.globalShortcut) {\n this.setGlobalShortcut(this._options.globalShortcut);\n }\n\n if (!this._options.windowPosition) {\n this._options.windowPosition = getWindowPosition(this.tray);\n }\n\n if (this._options.preloadWindow) {\n await this.createWindow();\n }\n\n this.emit('ready');\n }\n\n /**\n * Callback on tray icon click or double-click.\n *\n * @param e\n * @param bounds\n */\n private clicked = async (\n event?: Electron.KeyboardEvent,\n bounds?: Electron.Rectangle,\n ): Promise<void> => {\n if (event && (event.shiftKey || event.ctrlKey || event.metaKey)) {\n return this.hideWindow();\n }\n\n // if blur was invoked clear timeout\n if (this._blurTimeout) {\n clearInterval(this._blurTimeout);\n }\n\n if (this._browserWindow && this._isVisible) {\n return this.hideWindow();\n }\n\n this._cachedBounds = bounds || this._cachedBounds;\n await this.showWindow(this._cachedBounds);\n };\n\n private onAppActivate = (\n _event: Electron.Event,\n hasVisibleWindows: boolean,\n ): void => {\n if (!hasVisibleWindows) {\n this.showWindow().catch(console.error);\n }\n };\n\n /**\n * Marks the app as shutting down so `hideOnClose` stops intercepting closes.\n *\n * Bound to both `app`'s `before-quit` and the auto updater's\n * `before-quit-for-update`. Installing an update never emits `before-quit`:\n * Electron closes every window first and only quits once the window list is\n * empty, so a `hideOnClose` veto there would leave the window open and the\n * install waiting forever.\n */\n private onBeforeQuit = (): void => {\n this._isQuitting = true;\n };\n\n private bindContextMenu(menu: Menu): void {\n this._contextMenu = menu;\n if (process.platform === 'linux') {\n // libappindicator / StatusNotifierItem requires the menu to live on the\n // tray itself; right-click is handled by the desktop environment.\n this.tray.setContextMenu(menu);\n return;\n }\n this.bindRightClickContextMenu();\n }\n\n private bindRightClickContextMenu(): void {\n // macOS / Windows: pop up the current menu on right-click so left-click\n // stays bound to toggling the menubar window. Read `this._contextMenu`\n // at invoke time so `setContextMenu()` swaps and clears take effect\n // without rebinding (and without leaking a stale closure reference).\n this.tray.on('right-click', (_event, bounds) => {\n const current = this._contextMenu;\n if (!current) {\n return;\n }\n this.tray.popUpContextMenu(current, { x: bounds.x, y: bounds.y });\n });\n this._rightClickContextMenuBound = true;\n }\n\n private onAppReady = (): void => {\n // Guard against `destroy()` being called between construction and the\n // scheduled `process.nextTick`/`'ready'` firing.\n if (this._isDestroyed) {\n return;\n }\n this.appReady().catch((err) => console.error('menubar: ', err));\n };\n\n private async createWindow(): Promise<void> {\n this.emit('create-window');\n\n // We add some default behavior for menubar's browserWindow, to make it\n // look like a menubar\n const defaults = {\n show: false, // Don't show it at first\n frame: false, // Remove window frame\n };\n\n this._browserWindow = new BrowserWindow({\n ...defaults,\n ...this._options.browserWindow,\n });\n\n this._positioner = new Positioner(this._browserWindow);\n\n this._browserWindow.on('blur', () => {\n if (!this._browserWindow) {\n return;\n }\n\n // The window was pinned (e.g. a host \"keep open on blur\" preference);\n // don't auto-hide, just surface the event for the host app to react to.\n if (this._browserWindow.isAlwaysOnTop()) {\n this.emit('focus-lost');\n return;\n }\n\n // Windows foreground-activation race: clicking the tray icon keeps the\n // shell (explorer.exe) as the foreground process, so `show()` cannot pull\n // focus to the popup and Windows immediately fires `blur`. Left unguarded,\n // the hide timer below fires before the first paint and the window never\n // visibly appears (gitify-app/gitify#3064). Ignore blur events that land\n // within the grace window right after a show; a genuine click-away\n // arrives well after it.\n if (\n process.platform === 'win32' &&\n Date.now() - this._lastShowTime < BLUR_HIDE_GRACE_MS\n ) {\n return;\n }\n\n this._blurTimeout = setTimeout(() => {\n this.hideWindow();\n }, 100);\n });\n\n if (this._options.showOnAllWorkspaces !== false) {\n // https://github.com/electron/electron/issues/37832#issuecomment-1497882944\n this._browserWindow.setVisibleOnAllWorkspaces(true, {\n skipTransformProcessType: true, // Avoid damaging the original visible state of app.dock\n });\n }\n\n if (this._options.hideOnClose) {\n this._browserWindow.on('close', (event) => {\n if (this._isDestroyed || this._isQuitting) {\n return;\n }\n event.preventDefault();\n // Defer the hide for Wayland: hiding synchronously from the `close`\n // handler can leave frameless surfaces in a half-closed state.\n setImmediate(() => this.hideWindow());\n });\n }\n\n if (this._options.escapeToHide) {\n this._browserWindow.webContents.on(\n 'before-input-event',\n (_event, input) => {\n if (input.type === 'keyDown' && input.key === 'Escape') {\n this.hideWindow();\n }\n },\n );\n }\n\n // Use `closed` (not `close`) so consumer `close` listeners can still read\n // `mb.window` and call `event.preventDefault()` without racing our cleanup.\n this._browserWindow.on('closed', this.windowClear.bind(this));\n\n // Re-anchor the window to the tray when its size changes (e.g. via\n // `mb.window.setSize(...)`), so the window doesn't end up clipped under\n // the taskbar. https://github.com/maxogden/menubar/issues/349\n this._browserWindow.on('resize', this.positionWindow);\n\n this.emit('before-load');\n\n // If the user explicity set options.index to false, we don't loadURL\n // https://github.com/maxogden/menubar/issues/255\n if (this._options.index !== false) {\n await this._browserWindow.loadURL(\n this._options.index,\n this._options.loadUrlOptions,\n );\n }\n this.emit('after-create-window');\n }\n\n private windowClear(): void {\n this._browserWindow = undefined;\n this.emit('after-close');\n }\n}\n","/**\n * Entry point of menubar\n * @example\n * ```typescript\n * import { menubar } from 'menubar';\n * ```\n */\n\n/** */\n\nimport { app } from 'electron';\n\nimport { Menubar } from './Menubar';\nimport type { Options } from './types';\n\nexport * from './util/getWindowPosition';\nexport { Menubar };\n\n/**\n * Factory function to create a menubar application\n *\n * @param options - Options for creating a menubar application, see\n * {@link Options}\n */\nexport function menubar(options?: Partial<Options>): Menubar {\n return new Menubar(app, options);\n}\n"],"mappings":"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"}
|
package/lib/index.d.cts
CHANGED
|
@@ -78,9 +78,10 @@ interface Options {
|
|
|
78
78
|
* compositor bug that leaves frameless surfaces in a half-closed state
|
|
79
79
|
* when hidden synchronously from the `close` handler.
|
|
80
80
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* when the close event was
|
|
81
|
+
* Closes go through unimpeded once the app is shutting down, which the
|
|
82
|
+
* library tracks via `app`'s `before-quit` and the auto updater's
|
|
83
|
+
* `before-quit-for-update`. Has no effect when the close event was
|
|
84
|
+
* triggered by {@link Menubar.destroy}.
|
|
84
85
|
* @default `false`
|
|
85
86
|
*/
|
|
86
87
|
hideOnClose?: boolean;
|
|
@@ -130,6 +131,13 @@ interface Options {
|
|
|
130
131
|
/**
|
|
131
132
|
* Configure the visibility of the application dock icon, macOS only. Calls
|
|
132
133
|
* [`app.dock.hide`](https://electronjs.org/docs/api/app#appdockhide-macos).
|
|
134
|
+
*
|
|
135
|
+
* Hiding at runtime is a process transform that macOS can silently drop, so
|
|
136
|
+
* packaged apps that never want a dock tile should also declare
|
|
137
|
+
* `LSUIElement` in their `Info.plist` (with electron-builder:
|
|
138
|
+
* `mac.extendInfo.LSUIElement: true`). This option then still covers
|
|
139
|
+
* development runs, where the stock Electron binary's plist is not under
|
|
140
|
+
* your control.
|
|
133
141
|
*/
|
|
134
142
|
showDockIcon?: boolean;
|
|
135
143
|
/**
|
|
@@ -262,6 +270,17 @@ declare class Menubar extends EventEmitter {
|
|
|
262
270
|
* @param menu - The new menu, or `null` to clear it.
|
|
263
271
|
*/
|
|
264
272
|
setContextMenu(menu: Menu | null): void;
|
|
273
|
+
/**
|
|
274
|
+
* Re-publish the current context menu so in-place mutations of its items
|
|
275
|
+
* (`visible`, `enabled`, `checked`, `label`) become visible to the user.
|
|
276
|
+
*
|
|
277
|
+
* Only Linux needs this: libappindicator / StatusNotifierItem serializes the
|
|
278
|
+
* menu once and serves that copy until it is set again, so a mutated item
|
|
279
|
+
* would otherwise keep rendering its old state until the next show or hide.
|
|
280
|
+
* A no-op on macOS and Windows, where the menu is read at popup time — call
|
|
281
|
+
* it unconditionally after mutating items.
|
|
282
|
+
*/
|
|
283
|
+
refreshContextMenu(): void;
|
|
265
284
|
/**
|
|
266
285
|
* Change an option after menubar is created.
|
|
267
286
|
*
|
|
@@ -296,10 +315,18 @@ declare class Menubar extends EventEmitter {
|
|
|
296
315
|
*/
|
|
297
316
|
private clicked;
|
|
298
317
|
private onAppActivate;
|
|
318
|
+
/**
|
|
319
|
+
* Marks the app as shutting down so `hideOnClose` stops intercepting closes.
|
|
320
|
+
*
|
|
321
|
+
* Bound to both `app`'s `before-quit` and the auto updater's
|
|
322
|
+
* `before-quit-for-update`. Installing an update never emits `before-quit`:
|
|
323
|
+
* Electron closes every window first and only quits once the window list is
|
|
324
|
+
* empty, so a `hideOnClose` veto there would leave the window open and the
|
|
325
|
+
* install waiting forever.
|
|
326
|
+
*/
|
|
299
327
|
private onBeforeQuit;
|
|
300
328
|
private bindContextMenu;
|
|
301
329
|
private bindRightClickContextMenu;
|
|
302
|
-
private refreshLinuxContextMenu;
|
|
303
330
|
private onAppReady;
|
|
304
331
|
private createWindow;
|
|
305
332
|
private windowClear;
|
package/lib/index.d.mts
CHANGED
|
@@ -78,9 +78,10 @@ interface Options {
|
|
|
78
78
|
* compositor bug that leaves frameless surfaces in a half-closed state
|
|
79
79
|
* when hidden synchronously from the `close` handler.
|
|
80
80
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* when the close event was
|
|
81
|
+
* Closes go through unimpeded once the app is shutting down, which the
|
|
82
|
+
* library tracks via `app`'s `before-quit` and the auto updater's
|
|
83
|
+
* `before-quit-for-update`. Has no effect when the close event was
|
|
84
|
+
* triggered by {@link Menubar.destroy}.
|
|
84
85
|
* @default `false`
|
|
85
86
|
*/
|
|
86
87
|
hideOnClose?: boolean;
|
|
@@ -130,6 +131,13 @@ interface Options {
|
|
|
130
131
|
/**
|
|
131
132
|
* Configure the visibility of the application dock icon, macOS only. Calls
|
|
132
133
|
* [`app.dock.hide`](https://electronjs.org/docs/api/app#appdockhide-macos).
|
|
134
|
+
*
|
|
135
|
+
* Hiding at runtime is a process transform that macOS can silently drop, so
|
|
136
|
+
* packaged apps that never want a dock tile should also declare
|
|
137
|
+
* `LSUIElement` in their `Info.plist` (with electron-builder:
|
|
138
|
+
* `mac.extendInfo.LSUIElement: true`). This option then still covers
|
|
139
|
+
* development runs, where the stock Electron binary's plist is not under
|
|
140
|
+
* your control.
|
|
133
141
|
*/
|
|
134
142
|
showDockIcon?: boolean;
|
|
135
143
|
/**
|
|
@@ -262,6 +270,17 @@ declare class Menubar extends EventEmitter {
|
|
|
262
270
|
* @param menu - The new menu, or `null` to clear it.
|
|
263
271
|
*/
|
|
264
272
|
setContextMenu(menu: Menu | null): void;
|
|
273
|
+
/**
|
|
274
|
+
* Re-publish the current context menu so in-place mutations of its items
|
|
275
|
+
* (`visible`, `enabled`, `checked`, `label`) become visible to the user.
|
|
276
|
+
*
|
|
277
|
+
* Only Linux needs this: libappindicator / StatusNotifierItem serializes the
|
|
278
|
+
* menu once and serves that copy until it is set again, so a mutated item
|
|
279
|
+
* would otherwise keep rendering its old state until the next show or hide.
|
|
280
|
+
* A no-op on macOS and Windows, where the menu is read at popup time — call
|
|
281
|
+
* it unconditionally after mutating items.
|
|
282
|
+
*/
|
|
283
|
+
refreshContextMenu(): void;
|
|
265
284
|
/**
|
|
266
285
|
* Change an option after menubar is created.
|
|
267
286
|
*
|
|
@@ -296,10 +315,18 @@ declare class Menubar extends EventEmitter {
|
|
|
296
315
|
*/
|
|
297
316
|
private clicked;
|
|
298
317
|
private onAppActivate;
|
|
318
|
+
/**
|
|
319
|
+
* Marks the app as shutting down so `hideOnClose` stops intercepting closes.
|
|
320
|
+
*
|
|
321
|
+
* Bound to both `app`'s `before-quit` and the auto updater's
|
|
322
|
+
* `before-quit-for-update`. Installing an update never emits `before-quit`:
|
|
323
|
+
* Electron closes every window first and only quits once the window list is
|
|
324
|
+
* empty, so a `hideOnClose` veto there would leave the window open and the
|
|
325
|
+
* install waiting forever.
|
|
326
|
+
*/
|
|
299
327
|
private onBeforeQuit;
|
|
300
328
|
private bindContextMenu;
|
|
301
329
|
private bindRightClickContextMenu;
|
|
302
|
-
private refreshLinuxContextMenu;
|
|
303
330
|
private onAppReady;
|
|
304
331
|
private createWindow;
|
|
305
332
|
private windowClear;
|
package/lib/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BrowserWindow, Tray, app, globalShortcut, screen } from "electron";
|
|
1
|
+
import { BrowserWindow, Tray, app, autoUpdater, globalShortcut, screen } from "electron";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
@@ -218,6 +218,7 @@ var Menubar = class extends EventEmitter {
|
|
|
218
218
|
this._isQuitting = false;
|
|
219
219
|
this._isVisible = false;
|
|
220
220
|
app.on("before-quit", this.onBeforeQuit);
|
|
221
|
+
autoUpdater.on("before-quit-for-update", this.onBeforeQuit);
|
|
221
222
|
if (app.isReady()) process.nextTick(this.onAppReady);
|
|
222
223
|
else app.on("ready", this.onAppReady);
|
|
223
224
|
}
|
|
@@ -282,6 +283,7 @@ var Menubar = class extends EventEmitter {
|
|
|
282
283
|
this._app.removeListener("ready", this.onAppReady);
|
|
283
284
|
this._app.removeListener("activate", this.onAppActivate);
|
|
284
285
|
this._app.removeListener("before-quit", this.onBeforeQuit);
|
|
286
|
+
autoUpdater.removeListener("before-quit-for-update", this.onBeforeQuit);
|
|
285
287
|
}
|
|
286
288
|
/**
|
|
287
289
|
* Whether {@link destroy} has been called on this menubar instance.
|
|
@@ -310,7 +312,7 @@ var Menubar = class extends EventEmitter {
|
|
|
310
312
|
clearTimeout(this._blurTimeout);
|
|
311
313
|
this._blurTimeout = null;
|
|
312
314
|
}
|
|
313
|
-
this.
|
|
315
|
+
this.refreshContextMenu();
|
|
314
316
|
}
|
|
315
317
|
/**
|
|
316
318
|
* Register a global keyboard accelerator that toggles the menubar window.
|
|
@@ -376,6 +378,19 @@ var Menubar = class extends EventEmitter {
|
|
|
376
378
|
if (menu && !this._rightClickContextMenuBound) this.bindRightClickContextMenu();
|
|
377
379
|
}
|
|
378
380
|
/**
|
|
381
|
+
* Re-publish the current context menu so in-place mutations of its items
|
|
382
|
+
* (`visible`, `enabled`, `checked`, `label`) become visible to the user.
|
|
383
|
+
*
|
|
384
|
+
* Only Linux needs this: libappindicator / StatusNotifierItem serializes the
|
|
385
|
+
* menu once and serves that copy until it is set again, so a mutated item
|
|
386
|
+
* would otherwise keep rendering its old state until the next show or hide.
|
|
387
|
+
* A no-op on macOS and Windows, where the menu is read at popup time — call
|
|
388
|
+
* it unconditionally after mutating items.
|
|
389
|
+
*/
|
|
390
|
+
refreshContextMenu() {
|
|
391
|
+
if (process.platform === "linux" && this._contextMenu && this._tray && !this._tray.isDestroyed?.()) this._tray.setContextMenu(this._contextMenu);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
379
394
|
* Change an option after menubar is created.
|
|
380
395
|
*
|
|
381
396
|
* @param key - The option key to modify, see {@link Options}.
|
|
@@ -401,7 +416,7 @@ var Menubar = class extends EventEmitter {
|
|
|
401
416
|
this._browserWindow.show();
|
|
402
417
|
this._isVisible = true;
|
|
403
418
|
this.emit("after-show");
|
|
404
|
-
this.
|
|
419
|
+
this.refreshContextMenu();
|
|
405
420
|
}
|
|
406
421
|
/**
|
|
407
422
|
* Compute and apply the tray-anchored position of the browser window. Safe
|
|
@@ -484,6 +499,15 @@ var Menubar = class extends EventEmitter {
|
|
|
484
499
|
onAppActivate = (_event, hasVisibleWindows) => {
|
|
485
500
|
if (!hasVisibleWindows) this.showWindow().catch(console.error);
|
|
486
501
|
};
|
|
502
|
+
/**
|
|
503
|
+
* Marks the app as shutting down so `hideOnClose` stops intercepting closes.
|
|
504
|
+
*
|
|
505
|
+
* Bound to both `app`'s `before-quit` and the auto updater's
|
|
506
|
+
* `before-quit-for-update`. Installing an update never emits `before-quit`:
|
|
507
|
+
* Electron closes every window first and only quits once the window list is
|
|
508
|
+
* empty, so a `hideOnClose` veto there would leave the window open and the
|
|
509
|
+
* install waiting forever.
|
|
510
|
+
*/
|
|
487
511
|
onBeforeQuit = () => {
|
|
488
512
|
this._isQuitting = true;
|
|
489
513
|
};
|
|
@@ -506,9 +530,6 @@ var Menubar = class extends EventEmitter {
|
|
|
506
530
|
});
|
|
507
531
|
this._rightClickContextMenuBound = true;
|
|
508
532
|
}
|
|
509
|
-
refreshLinuxContextMenu() {
|
|
510
|
-
if (process.platform === "linux" && this._contextMenu && this._tray && !this._tray.isDestroyed?.()) this._tray.setContextMenu(this._contextMenu);
|
|
511
|
-
}
|
|
512
533
|
onAppReady = () => {
|
|
513
534
|
if (this._isDestroyed) return;
|
|
514
535
|
this.appReady().catch((err) => console.error("menubar: ", err));
|
package/lib/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["electronScreen"],"sources":["../src/Positioner.ts","../src/util/cleanOptions.ts","../src/util/getWindowPosition.ts","../src/Menubar.ts","../src/index.ts"],"sourcesContent":["import { type BrowserWindow, type Rectangle, screen } from 'electron';\n\n/**\n * Named anchor points for placing the menubar window. The `tray*` values are\n * relative to the tray icon's bounds; the rest are relative to the work area\n * of the display containing the cursor (or the tray, when bounds are given).\n */\nexport type WindowPosition =\n | 'trayLeft'\n | 'trayBottomLeft'\n | 'trayRight'\n | 'trayBottomRight'\n | 'trayCenter'\n | 'trayBottomCenter'\n | 'topLeft'\n | 'topRight'\n | 'bottomLeft'\n | 'bottomRight'\n | 'topCenter'\n | 'bottomCenter'\n | 'leftCenter'\n | 'rightCenter'\n | 'center';\n\n/**\n * Computes `{x, y}` coordinates for placing a {@link BrowserWindow} at a named\n * position, optionally relative to a tray icon's bounds. Ported from\n * `electron-positioner@4.1.0` to drop the unmaintained runtime dependency.\n */\nexport class Positioner {\n private readonly browserWindow: BrowserWindow;\n\n constructor(browserWindow: BrowserWindow) {\n this.browserWindow = browserWindow;\n }\n\n calculate(\n position?: WindowPosition,\n trayBounds?: Rectangle,\n ): { x: number; y: number } {\n if (!position) {\n throw new TypeError(\n 'Positioner.calculate: a `position` argument is required.',\n );\n }\n\n const screenSize = trayBounds\n ? screen.getDisplayMatching(trayBounds).workArea\n : screen.getDisplayNearestPoint(screen.getCursorScreenPoint()).workArea;\n const [windowWidth, windowHeight] = this.browserWindow.getSize();\n const trayX = trayBounds?.x ?? Number.NaN;\n const trayWidth = trayBounds?.width ?? Number.NaN;\n\n const positions: Record<WindowPosition, { x: number; y: number }> = {\n trayLeft: {\n x: Math.floor(trayX),\n y: screenSize.y,\n },\n trayBottomLeft: {\n x: Math.floor(trayX),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n trayRight: {\n x: Math.floor(trayX - windowWidth + trayWidth),\n y: screenSize.y,\n },\n trayBottomRight: {\n x: Math.floor(trayX - windowWidth + trayWidth),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n trayCenter: {\n x: Math.floor(trayX - windowWidth / 2 + trayWidth / 2),\n y: screenSize.y,\n },\n trayBottomCenter: {\n x: Math.floor(trayX - windowWidth / 2 + trayWidth / 2),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n topLeft: {\n x: screenSize.x,\n y: screenSize.y,\n },\n topRight: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y: screenSize.y,\n },\n bottomLeft: {\n x: screenSize.x,\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n bottomRight: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n topCenter: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: screenSize.y,\n },\n bottomCenter: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n leftCenter: {\n x: screenSize.x,\n y:\n screenSize.y +\n Math.floor(screenSize.height / 2) -\n Math.floor(windowHeight / 2),\n },\n rightCenter: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y:\n screenSize.y +\n Math.floor(screenSize.height / 2) -\n Math.floor(windowHeight / 2),\n },\n center: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: Math.floor(\n (screenSize.height + screenSize.y) / 2 - windowHeight / 2,\n ),\n },\n };\n\n const coords = positions[position];\n\n // On Windows, a tray-relative position can push the window past the right\n // edge of the work area. Snap back to `topRight` x in that case so it stays\n // visible. See https://github.com/jenslind/electron-positioner.\n if (position.startsWith('tray')) {\n if (coords.x + windowWidth > screenSize.width + screenSize.x) {\n return { x: positions.topRight.x, y: coords.y };\n }\n }\n\n return coords;\n }\n}\n","/**\n * @ignore\n */\n\n/** */\n\nimport path from 'node:path';\nimport url from 'node:url';\n\nimport { app } from 'electron';\n\nimport type { Options } from '../types';\n\nconst DEFAULT_WINDOW_HEIGHT = 400;\nconst DEFAULT_WINDOW_WIDTH = 400;\n\n/**\n * Take as input some options, and return a sanitized version of it.\n *\n * @param opts - The options to clean.\n * @ignore\n */\nexport function cleanOptions(opts?: Partial<Options>): Options {\n const options: Partial<Options> = { ...opts };\n\n if (options.activateWithApp === undefined) {\n options.activateWithApp = true;\n }\n if (options.ignoreDoubleClickEvents === undefined) {\n options.ignoreDoubleClickEvents = true;\n }\n if (!options.dir) {\n options.dir = app.getAppPath();\n }\n if (!path.isAbsolute(options.dir)) {\n options.dir = path.resolve(options.dir);\n }\n // Note: options.index can be `false`\n if (options.index === undefined) {\n options.index = url.format({\n pathname: path.join(options.dir, 'index.html'),\n protocol: 'file:',\n slashes: true,\n });\n }\n options.loadUrlOptions = options.loadUrlOptions || {};\n\n options.tooltip = options.tooltip || '';\n\n // `icon`, `preloadWindow`, `showDockIcon`, `showOnAllWorkspaces`,\n // `showOnRightClick` don't need any special treatment\n\n // Now we take care of `browserWindow`\n if (!options.browserWindow) {\n options.browserWindow = {};\n }\n\n // Set width/height on options to be usable before the window is created\n options.browserWindow.width =\n // Note: not using `options.browserWindow.width || DEFAULT_WINDOW_WIDTH` so\n // that users can put a 0 width\n options.browserWindow.width !== undefined\n ? options.browserWindow.width\n : DEFAULT_WINDOW_WIDTH;\n options.browserWindow.height =\n options.browserWindow.height !== undefined\n ? options.browserWindow.height\n : DEFAULT_WINDOW_HEIGHT;\n\n return options as Options;\n}\n","/**\n * Utilities to get taskbar position and consequently menubar's position\n */\n\n/** */\n\nimport { screen as electronScreen, type Rectangle, type Tray } from 'electron';\n\nconst isLinux = process.platform === 'linux';\n\nconst trayToScreenRects = (tray: Tray): [Rectangle, Rectangle] => {\n // There may be more than one screen, so we need to figure out on which screen our tray icon lives.\n const { workArea, bounds: screenBounds } = electronScreen.getDisplayMatching(\n tray.getBounds(),\n );\n\n workArea.x -= screenBounds.x;\n workArea.y -= screenBounds.y;\n\n return [screenBounds, workArea];\n};\n\ntype TaskbarLocation = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Determine taskbard location: \"top\", \"bottom\", \"left\" or \"right\".\n *\n * Only tested on Windows for now, and only used in Windows.\n *\n * @param tray - The Electron Tray instance.\n */\nexport function taskbarLocation(tray: Tray): TaskbarLocation {\n const [screenBounds, workArea] = trayToScreenRects(tray);\n\n // TASKBAR LEFT\n if (workArea.x > 0) {\n // Most likely Ubuntu hence assuming the window should be on top\n if (isLinux && workArea.y > 0) return 'top';\n // The workspace starts more on the right\n return 'left';\n }\n\n // TASKBAR TOP\n if (workArea.y > 0) {\n return 'top';\n }\n\n // TASKBAR RIGHT\n // Here both workArea.y and workArea.x are 0 so we can no longer leverage them.\n // We can use the workarea and display width though.\n // Determine taskbar location\n if (workArea.width < screenBounds.width) {\n // The taskbar is either on the left or right, but since the LEFT case was handled above,\n // we can be sure we're dealing with a right taskbar\n return 'right';\n }\n\n // TASKBAR BOTTOM\n // Since all the other cases were handled, we can be sure we're dealing with a bottom taskbar\n return 'bottom';\n}\n\ntype WindowPosition =\n | 'trayCenter'\n | 'topRight'\n | 'trayBottomCenter'\n | 'leftCenter'\n | 'bottomRight';\n\n/**\n * Depending on where the taskbar is, determine where the window should be\n * positioned.\n *\n * @param tray - The Electron Tray instance.\n */\nexport function getWindowPosition(tray: Tray): WindowPosition {\n switch (process.platform) {\n // macOS\n // Supports top taskbars\n case 'darwin':\n return 'trayCenter';\n // Linux\n // Windows\n // Supports top/bottom/left/right taskbar\n case 'linux':\n case 'win32': {\n const traySide = taskbarLocation(tray);\n\n // Assign position for menubar\n if (traySide === 'top') {\n return isLinux ? 'topRight' : 'trayCenter';\n }\n if (traySide === 'bottom') {\n return 'bottomRight';\n }\n if (traySide === 'left') {\n // Vertically centered against the left edge of the work area.\n // `bottomLeft` would put the window in the screen's bottom-left\n // corner — visually disconnected from the tray icon, which sits\n // somewhere on the left strip. Tray-anchored y isn't available\n // through `Positioner` for side taskbars, so center is the best\n // compromise that stays close to the tray.\n return 'leftCenter';\n }\n if (traySide === 'right') {\n return 'bottomRight';\n }\n }\n }\n\n // When we really don't know, we just show the menubar on the top-right\n return 'topRight';\n}\n","import { EventEmitter } from 'node:events';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { BrowserWindow, globalShortcut, type Menu, Tray } from 'electron';\n\nimport { Positioner } from './Positioner';\nimport type { Options } from './types';\nimport { cleanOptions } from './util/cleanOptions';\nimport { getWindowPosition } from './util/getWindowPosition';\n\n/**\n * Grace period after `showWindow()` during which a `blur` on the popup is\n * treated as the spurious Windows post-show blur and ignored instead of\n * triggering the auto-hide. See the `blur` handler in {@link Menubar.createWindow}.\n */\nconst BLUR_HIDE_GRACE_MS = 400;\n\n/**\n * Delay before re-asserting the hidden dock after startup. macOS can silently\n * drop an `app.dock.hide()` that races the app's launch activation\n * transition, leaving the dock icon stuck for the whole session. See the\n * re-check in {@link Menubar.appReady}.\n */\nconst DOCK_REHIDE_DELAY_MS = 2_000;\n\n/**\n * The main Menubar class.\n */\nexport class Menubar extends EventEmitter {\n private _app: Electron.App;\n private _browserWindow?: BrowserWindow;\n private _contextMenu?: Menu;\n private _blurTimeout: NodeJS.Timeout | null = null; // track blur events with timeout\n private _isDestroyed: boolean;\n private _isQuitting: boolean; // set on app `before-quit`, used by hideOnClose\n private _isVisible: boolean; // track visibility\n private _cachedBounds?: Electron.Rectangle; // _cachedBounds are needed for double-clicked event\n private _options: Options;\n private _positioner: Positioner | undefined;\n private _shortcut?: Electron.Accelerator;\n private _rightClickContextMenuBound = false;\n private _warnedNoPositioning = false; // guards the one-time Wayland warning\n private _lastShowTime = 0; // timestamp of last show(), debounces the post-show blur on Windows\n private _dockRehideTimeout?: NodeJS.Timeout; // pending post-startup dock re-hide check\n private _repositioning = false; // guards against re-entrant positionWindow calls\n private _tray?: Tray;\n\n constructor(app: Electron.App, options?: Partial<Options>) {\n super();\n this._app = app;\n this._options = cleanOptions(options);\n this._isDestroyed = false;\n this._isQuitting = false;\n this._isVisible = false;\n\n app.on('before-quit', this.onBeforeQuit);\n\n if (app.isReady()) {\n // See https://github.com/maxogden/menubar/pull/151\n process.nextTick(this.onAppReady);\n } else {\n app.on('ready', this.onAppReady);\n }\n }\n\n /**\n * The Electron [App](https://electronjs.org/docs/api/app)\n * instance.\n */\n get app(): Electron.App {\n return this._app;\n }\n\n /**\n * The {@link Positioner} instance used to compute where the menubar window\n * should appear on screen. Available after the `after-create-window` event.\n */\n get positioner(): Positioner {\n if (!this._positioner) {\n throw new Error(\n 'Please access `this.positioner` after the `after-create-window` event has fired.',\n );\n }\n\n return this._positioner;\n }\n\n /**\n * The Electron [Tray](https://electronjs.org/docs/api/tray) instance.\n */\n get tray(): Tray {\n if (!this._tray) {\n throw new Error(\n 'Please access `this.tray` after the `ready` event has fired.',\n );\n }\n\n return this._tray;\n }\n\n /**\n * The Electron [BrowserWindow](https://electronjs.org/docs/api/browser-window)\n * instance, if it's present.\n */\n get window(): BrowserWindow | undefined {\n return this._browserWindow;\n }\n\n /**\n * Tear down the menubar instance: destroy the window, remove the tray, and\n * detach all listeners. Subsequent clicks on the tray will be no-ops until a\n * new {@link Menubar} instance is created.\n */\n destroy(): void {\n if (this.isDestroyed()) {\n return;\n }\n // Set first so `hideOnClose` lets the close go through instead of\n // intercepting it.\n this._isDestroyed = true;\n\n if (this._shortcut) {\n globalShortcut.unregister(this._shortcut);\n this._shortcut = undefined;\n }\n\n if (this._dockRehideTimeout) {\n clearTimeout(this._dockRehideTimeout);\n this._dockRehideTimeout = undefined;\n }\n\n if (this._browserWindow) {\n this._browserWindow.destroy();\n this._browserWindow = undefined;\n }\n\n if (this._tray) {\n // Ensure all potential listeners are removed.\n for (const event of ['click', 'right-click', 'double-click']) {\n this._tray.removeListener(\n event as Parameters<Tray['on']>[0],\n this.clicked,\n );\n }\n this._tray.setToolTip('');\n this._tray = undefined;\n }\n\n this._app.removeListener('ready', this.onAppReady);\n this._app.removeListener('activate', this.onAppActivate);\n this._app.removeListener('before-quit', this.onBeforeQuit);\n }\n\n /**\n * Whether {@link destroy} has been called on this menubar instance.\n */\n isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Retrieve a menubar option.\n *\n * @param key - The option key to retrieve, see {@link Options}.\n */\n getOption<K extends keyof Options>(key: K): Options[K] {\n return this._options[key];\n }\n\n /**\n * Hide the menubar window.\n */\n hideWindow(): void {\n if (!this._browserWindow || !this._isVisible) {\n return;\n }\n this.emit('hide');\n this._browserWindow.hide();\n this.emit('after-hide');\n this._isVisible = false;\n if (this._blurTimeout) {\n clearTimeout(this._blurTimeout);\n this._blurTimeout = null;\n }\n this.refreshLinuxContextMenu();\n }\n\n /**\n * Register a global keyboard accelerator that toggles the menubar window.\n * Replaces any previously registered shortcut owned by this Menubar.\n * Pass `undefined` to clear the current shortcut without registering a new\n * one. Returns whether the registration succeeded.\n *\n * @param accelerator - An Electron\n * [Accelerator](https://electronjs.org/docs/api/accelerator) string, or\n * `undefined` to clear.\n */\n setGlobalShortcut(accelerator: Electron.Accelerator | undefined): boolean {\n if (this._shortcut) {\n globalShortcut.unregister(this._shortcut);\n this._shortcut = undefined;\n }\n this._options.globalShortcut = accelerator;\n if (!accelerator) {\n return true;\n }\n const ok = globalShortcut.register(accelerator, () => this.toggleWindow());\n if (ok) {\n this._shortcut = accelerator;\n }\n return ok;\n }\n\n /**\n * Toggle the menubar window: hide it if visible, show it otherwise.\n * Resolves once the window finishes showing or hiding.\n */\n async toggleWindow(): Promise<void> {\n if (this._browserWindow && this._isVisible) {\n this.hideWindow();\n return;\n }\n await this.showWindow();\n }\n\n /**\n * Re-center the menubar window over the tray icon. Convenience wrapper for\n * `positioner.move('trayCenter', tray.getBounds())` that's safe to call\n * after the `after-create-window` event. No-op if the window doesn't\n * exist yet.\n */\n recenterOnTray(): void {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n const bounds = this._tray.getBounds();\n const { x, y } = this.positioner.calculate('trayCenter', bounds);\n this._browserWindow.setPosition(Math.round(x), Math.round(y));\n }\n\n /**\n * Replace the tray context menu after construction. On Linux this also\n * re-publishes the menu to the SNI host, which is required after mutating\n * items in-place since libappindicator caches the previous serialization.\n * On macOS/Windows the right-click popup handler reads the current menu\n * reference, so swapping or clearing here takes effect immediately.\n *\n * @param menu - The new menu, or `null` to clear it.\n */\n setContextMenu(menu: Menu | null): void {\n this._contextMenu = menu ?? undefined;\n this._options.contextMenu = menu ?? undefined;\n if (!this._tray) {\n return;\n }\n if (process.platform === 'linux') {\n // `setContextMenu(null)` clears the menu on Linux.\n this._tray.setContextMenu(menu);\n return;\n }\n // macOS / Windows: bind the right-click popup once on first non-empty\n // assignment. The handler reads `this._contextMenu` at invoke time, so\n // later swaps and clears take effect without rebinding (and never leak\n // a stale closure reference).\n if (menu && !this._rightClickContextMenuBound) {\n this.bindRightClickContextMenu();\n }\n }\n\n /**\n * Change an option after menubar is created.\n *\n * @param key - The option key to modify, see {@link Options}.\n * @param value - The value to set.\n */\n setOption<K extends keyof Options>(key: K, value: Options[K]): void {\n this._options[key] = value;\n }\n\n /**\n * Show the menubar window.\n *\n * @param trayPos - The bounds to show the window in.\n */\n async showWindow(trayPos?: Electron.Rectangle): Promise<void> {\n if (!this.tray) {\n throw new Error('Tray should have been instantiated by now');\n }\n\n if (!this._browserWindow) {\n await this.createWindow();\n }\n\n // Use guard for TypeScript, to avoid ! everywhere\n if (!this._browserWindow) {\n throw new Error('Window has been initialized just above. qed.');\n }\n\n this.emit('show');\n\n // Cache fresh tray bounds (or fall back to existing cache / tray bounds)\n // so `positionWindow` can reposition without an event payload — for\n // example when the window resizes via `setSize`.\n if (trayPos && trayPos.x !== 0) {\n this._cachedBounds = trayPos;\n } else if (!this._cachedBounds && this.tray.getBounds) {\n this._cachedBounds = this.tray.getBounds();\n }\n\n this.positionWindow();\n // Record the show time before `show()` so the blur handler can recognise\n // and ignore the transient blur Windows fires right after (see below).\n this._lastShowTime = Date.now();\n this._browserWindow.show();\n this._isVisible = true;\n this.emit('after-show');\n this.refreshLinuxContextMenu();\n }\n\n /**\n * Compute and apply the tray-anchored position of the browser window. Safe\n * to call any time after `createWindow` has run — invoked from\n * {@link showWindow} on every show, and from the window's `resize` event so\n * `setSize` calls reposition the window correctly.\n */\n private positionWindow = (): void => {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n\n // `setPosition` below can make Windows emit `resize` synchronously, which\n // re-enters this method through the window's `resize` listener. Each pass\n // nudges the window again, so the recursion never unwinds: the main\n // process wedges with the event loop blocked and the window grows past\n // screen size (gitify-app/gitify#3064). Ignore re-entrant calls; the\n // outermost one already applies the final position.\n if (this._repositioning) {\n return;\n }\n this._repositioning = true;\n try {\n this.applyWindowPosition();\n } finally {\n this._repositioning = false;\n }\n };\n\n /**\n * Compute and apply the tray-anchored position. Always call through\n * {@link positionWindow}, which guards against re-entrancy.\n */\n private applyWindowPosition(): void {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n\n // 'Windows' taskbar: sync window position each time before positioning.\n // https://github.com/maxogden/menubar/issues/232\n if (['win32', 'linux'].includes(process.platform)) {\n this._options.windowPosition = getWindowPosition(this._tray);\n }\n\n const trayPos = this._cachedBounds ?? this._tray.getBounds?.();\n\n // Default the window to the right if `trayPos` bounds are undefined or null.\n let noBoundsPosition: Options['windowPosition'];\n if (\n (trayPos === undefined || trayPos.x === 0) &&\n this._options.windowPosition?.startsWith('tray')\n ) {\n noBoundsPosition =\n process.platform === 'win32' ? 'bottomRight' : 'topRight';\n }\n\n const position = this.positioner.calculate(\n this._options.windowPosition || noBoundsPosition,\n trayPos,\n ) as { x: number; y: number };\n\n // Not using `||` because x and y can be zero.\n const x =\n this._options.browserWindow.x !== undefined\n ? this._options.browserWindow.x\n : position.x;\n const y =\n this._options.browserWindow.y !== undefined\n ? this._options.browserWindow.y\n : position.y;\n\n // `.setPosition` crashed on non-integers\n // https://github.com/maxogden/menubar/issues/233\n const targetX = Math.round(x);\n const targetY = Math.round(y);\n this._browserWindow.setPosition(targetX, targetY);\n\n // Native Wayland gives an application no way to position its own window:\n // `setPosition` is a no-op and `getPosition` reads back [0, 0]\n // (https://github.com/electron/electron/issues/40886). The compositor\n // decides where the window lands, usually centered, so the popover can't\n // be anchored to the tray. Detect that the move didn't take and warn once,\n // pointing at the X11 fallback. Gated on a Wayland session (`WAYLAND_DISPLAY`)\n // so pure X11 never trips it; the tolerance absorbs the few-pixel offsets\n // X11 window managers add for decorations, so XWayland (where positioning\n // works) doesn't trip it either.\n if (\n process.platform === 'linux' &&\n !!process.env.WAYLAND_DISPLAY &&\n !this._warnedNoPositioning\n ) {\n const [actualX, actualY] = this._browserWindow.getPosition();\n const ignored =\n Math.abs(actualX - targetX) > 24 || Math.abs(actualY - targetY) > 24;\n if (ignored) {\n this._warnedNoPositioning = true;\n console.warn(\n '[menubar] The window could not be positioned programmatically, ' +\n 'which is expected on native Wayland where the compositor controls ' +\n 'placement. The popover will not be anchored to the tray icon. Run ' +\n 'with --ozone-platform=x11 to restore tray-relative positioning.',\n );\n }\n }\n }\n\n private async appReady(): Promise<void> {\n if (this.app.dock && !this._options.showDockIcon) {\n this.app.dock.hide();\n\n // The hide above can be silently dropped when it races the launch\n // activation transition. Re-check once startup has settled; guarded on\n // visibility because `dock.hide()` also deactivates the app, so it must\n // not run when the dock is already hidden.\n this._dockRehideTimeout = setTimeout(() => {\n if (!this._isDestroyed && this.app.dock?.isVisible()) {\n this.app.dock.hide();\n }\n }, DOCK_REHIDE_DELAY_MS);\n }\n\n if (this._options.activateWithApp) {\n this.app.on('activate', this.onAppActivate);\n }\n\n let trayImage =\n this._options.icon || path.join(this._options.dir, 'IconTemplate.png');\n if (typeof trayImage === 'string' && !fs.existsSync(trayImage)) {\n trayImage = path.join(__dirname, '..', 'assets', 'IconTemplate.png'); // Default cat icon\n }\n\n const trigger =\n this._options.trigger ??\n (this._options.showOnRightClick ? 'right-click' : 'click');\n\n this._tray = this._options.tray || new Tray(trayImage);\n // Type guards for TS not to complain\n if (!this.tray) {\n throw new Error('Tray has been initialized above');\n }\n if (trigger !== 'none') {\n this.tray.on(trigger as Parameters<Tray['on']>[0], this.clicked);\n this.tray.on('double-click', this.clicked);\n }\n // macOS-only: ignore double-click so an accidental second click doesn't\n // race the blur handler and cause a tray-icon flicker.\n if (\n process.platform === 'darwin' &&\n this._options.ignoreDoubleClickEvents\n ) {\n this.tray.setIgnoreDoubleClickEvents(true);\n }\n this.tray.setToolTip(this._options.tooltip);\n\n if (this._options.contextMenu) {\n this.bindContextMenu(this._options.contextMenu);\n }\n\n if (this._options.globalShortcut) {\n this.setGlobalShortcut(this._options.globalShortcut);\n }\n\n if (!this._options.windowPosition) {\n this._options.windowPosition = getWindowPosition(this.tray);\n }\n\n if (this._options.preloadWindow) {\n await this.createWindow();\n }\n\n this.emit('ready');\n }\n\n /**\n * Callback on tray icon click or double-click.\n *\n * @param e\n * @param bounds\n */\n private clicked = async (\n event?: Electron.KeyboardEvent,\n bounds?: Electron.Rectangle,\n ): Promise<void> => {\n if (event && (event.shiftKey || event.ctrlKey || event.metaKey)) {\n return this.hideWindow();\n }\n\n // if blur was invoked clear timeout\n if (this._blurTimeout) {\n clearInterval(this._blurTimeout);\n }\n\n if (this._browserWindow && this._isVisible) {\n return this.hideWindow();\n }\n\n this._cachedBounds = bounds || this._cachedBounds;\n await this.showWindow(this._cachedBounds);\n };\n\n private onAppActivate = (\n _event: Electron.Event,\n hasVisibleWindows: boolean,\n ): void => {\n if (!hasVisibleWindows) {\n this.showWindow().catch(console.error);\n }\n };\n\n private onBeforeQuit = (): void => {\n this._isQuitting = true;\n };\n\n private bindContextMenu(menu: Menu): void {\n this._contextMenu = menu;\n if (process.platform === 'linux') {\n // libappindicator / StatusNotifierItem requires the menu to live on the\n // tray itself; right-click is handled by the desktop environment.\n this.tray.setContextMenu(menu);\n return;\n }\n this.bindRightClickContextMenu();\n }\n\n private bindRightClickContextMenu(): void {\n // macOS / Windows: pop up the current menu on right-click so left-click\n // stays bound to toggling the menubar window. Read `this._contextMenu`\n // at invoke time so `setContextMenu()` swaps and clears take effect\n // without rebinding (and without leaking a stale closure reference).\n this.tray.on('right-click', (_event, bounds) => {\n const current = this._contextMenu;\n if (!current) {\n return;\n }\n this.tray.popUpContextMenu(current, { x: bounds.x, y: bounds.y });\n });\n this._rightClickContextMenuBound = true;\n }\n\n private refreshLinuxContextMenu(): void {\n // libappindicator caches the menu's serialized state, so consumers that\n // mutate items in-place need the menu re-published. Cheap to do; safe to\n // call unconditionally on every show/hide.\n if (\n process.platform === 'linux' &&\n this._contextMenu &&\n this._tray &&\n !this._tray.isDestroyed?.()\n ) {\n this._tray.setContextMenu(this._contextMenu);\n }\n }\n\n private onAppReady = (): void => {\n // Guard against `destroy()` being called between construction and the\n // scheduled `process.nextTick`/`'ready'` firing.\n if (this._isDestroyed) {\n return;\n }\n this.appReady().catch((err) => console.error('menubar: ', err));\n };\n\n private async createWindow(): Promise<void> {\n this.emit('create-window');\n\n // We add some default behavior for menubar's browserWindow, to make it\n // look like a menubar\n const defaults = {\n show: false, // Don't show it at first\n frame: false, // Remove window frame\n };\n\n this._browserWindow = new BrowserWindow({\n ...defaults,\n ...this._options.browserWindow,\n });\n\n this._positioner = new Positioner(this._browserWindow);\n\n this._browserWindow.on('blur', () => {\n if (!this._browserWindow) {\n return;\n }\n\n // The window was pinned (e.g. a host \"keep open on blur\" preference);\n // don't auto-hide, just surface the event for the host app to react to.\n if (this._browserWindow.isAlwaysOnTop()) {\n this.emit('focus-lost');\n return;\n }\n\n // Windows foreground-activation race: clicking the tray icon keeps the\n // shell (explorer.exe) as the foreground process, so `show()` cannot pull\n // focus to the popup and Windows immediately fires `blur`. Left unguarded,\n // the hide timer below fires before the first paint and the window never\n // visibly appears (gitify-app/gitify#3064). Ignore blur events that land\n // within the grace window right after a show; a genuine click-away\n // arrives well after it.\n if (\n process.platform === 'win32' &&\n Date.now() - this._lastShowTime < BLUR_HIDE_GRACE_MS\n ) {\n return;\n }\n\n this._blurTimeout = setTimeout(() => {\n this.hideWindow();\n }, 100);\n });\n\n if (this._options.showOnAllWorkspaces !== false) {\n // https://github.com/electron/electron/issues/37832#issuecomment-1497882944\n this._browserWindow.setVisibleOnAllWorkspaces(true, {\n skipTransformProcessType: true, // Avoid damaging the original visible state of app.dock\n });\n }\n\n if (this._options.hideOnClose) {\n this._browserWindow.on('close', (event) => {\n if (this._isDestroyed || this._isQuitting) {\n return;\n }\n event.preventDefault();\n // Defer the hide for Wayland: hiding synchronously from the `close`\n // handler can leave frameless surfaces in a half-closed state.\n setImmediate(() => this.hideWindow());\n });\n }\n\n if (this._options.escapeToHide) {\n this._browserWindow.webContents.on(\n 'before-input-event',\n (_event, input) => {\n if (input.type === 'keyDown' && input.key === 'Escape') {\n this.hideWindow();\n }\n },\n );\n }\n\n // Use `closed` (not `close`) so consumer `close` listeners can still read\n // `mb.window` and call `event.preventDefault()` without racing our cleanup.\n this._browserWindow.on('closed', this.windowClear.bind(this));\n\n // Re-anchor the window to the tray when its size changes (e.g. via\n // `mb.window.setSize(...)`), so the window doesn't end up clipped under\n // the taskbar. https://github.com/maxogden/menubar/issues/349\n this._browserWindow.on('resize', this.positionWindow);\n\n this.emit('before-load');\n\n // If the user explicity set options.index to false, we don't loadURL\n // https://github.com/maxogden/menubar/issues/255\n if (this._options.index !== false) {\n await this._browserWindow.loadURL(\n this._options.index,\n this._options.loadUrlOptions,\n );\n }\n this.emit('after-create-window');\n }\n\n private windowClear(): void {\n this._browserWindow = undefined;\n this.emit('after-close');\n }\n}\n","/**\n * Entry point of menubar\n * @example\n * ```typescript\n * import { menubar } from 'menubar';\n * ```\n */\n\n/** */\n\nimport { app } from 'electron';\n\nimport { Menubar } from './Menubar';\nimport type { Options } from './types';\n\nexport * from './util/getWindowPosition';\nexport { Menubar };\n\n/**\n * Factory function to create a menubar application\n *\n * @param options - Options for creating a menubar application, see\n * {@link Options}\n */\nexport function menubar(options?: Partial<Options>): Menubar {\n return new Menubar(app, options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["electronScreen"],"sources":["../src/Positioner.ts","../src/util/cleanOptions.ts","../src/util/getWindowPosition.ts","../src/Menubar.ts","../src/index.ts"],"sourcesContent":["import { type BrowserWindow, type Rectangle, screen } from 'electron';\n\n/**\n * Named anchor points for placing the menubar window. The `tray*` values are\n * relative to the tray icon's bounds; the rest are relative to the work area\n * of the display containing the cursor (or the tray, when bounds are given).\n */\nexport type WindowPosition =\n | 'trayLeft'\n | 'trayBottomLeft'\n | 'trayRight'\n | 'trayBottomRight'\n | 'trayCenter'\n | 'trayBottomCenter'\n | 'topLeft'\n | 'topRight'\n | 'bottomLeft'\n | 'bottomRight'\n | 'topCenter'\n | 'bottomCenter'\n | 'leftCenter'\n | 'rightCenter'\n | 'center';\n\n/**\n * Computes `{x, y}` coordinates for placing a {@link BrowserWindow} at a named\n * position, optionally relative to a tray icon's bounds. Ported from\n * `electron-positioner@4.1.0` to drop the unmaintained runtime dependency.\n */\nexport class Positioner {\n private readonly browserWindow: BrowserWindow;\n\n constructor(browserWindow: BrowserWindow) {\n this.browserWindow = browserWindow;\n }\n\n calculate(\n position?: WindowPosition,\n trayBounds?: Rectangle,\n ): { x: number; y: number } {\n if (!position) {\n throw new TypeError(\n 'Positioner.calculate: a `position` argument is required.',\n );\n }\n\n const screenSize = trayBounds\n ? screen.getDisplayMatching(trayBounds).workArea\n : screen.getDisplayNearestPoint(screen.getCursorScreenPoint()).workArea;\n const [windowWidth, windowHeight] = this.browserWindow.getSize();\n const trayX = trayBounds?.x ?? Number.NaN;\n const trayWidth = trayBounds?.width ?? Number.NaN;\n\n const positions: Record<WindowPosition, { x: number; y: number }> = {\n trayLeft: {\n x: Math.floor(trayX),\n y: screenSize.y,\n },\n trayBottomLeft: {\n x: Math.floor(trayX),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n trayRight: {\n x: Math.floor(trayX - windowWidth + trayWidth),\n y: screenSize.y,\n },\n trayBottomRight: {\n x: Math.floor(trayX - windowWidth + trayWidth),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n trayCenter: {\n x: Math.floor(trayX - windowWidth / 2 + trayWidth / 2),\n y: screenSize.y,\n },\n trayBottomCenter: {\n x: Math.floor(trayX - windowWidth / 2 + trayWidth / 2),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n topLeft: {\n x: screenSize.x,\n y: screenSize.y,\n },\n topRight: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y: screenSize.y,\n },\n bottomLeft: {\n x: screenSize.x,\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n bottomRight: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n topCenter: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: screenSize.y,\n },\n bottomCenter: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: Math.floor(screenSize.height - (windowHeight - screenSize.y)),\n },\n leftCenter: {\n x: screenSize.x,\n y:\n screenSize.y +\n Math.floor(screenSize.height / 2) -\n Math.floor(windowHeight / 2),\n },\n rightCenter: {\n x: Math.floor(screenSize.x + (screenSize.width - windowWidth)),\n y:\n screenSize.y +\n Math.floor(screenSize.height / 2) -\n Math.floor(windowHeight / 2),\n },\n center: {\n x: Math.floor(screenSize.x + (screenSize.width / 2 - windowWidth / 2)),\n y: Math.floor(\n (screenSize.height + screenSize.y) / 2 - windowHeight / 2,\n ),\n },\n };\n\n const coords = positions[position];\n\n // On Windows, a tray-relative position can push the window past the right\n // edge of the work area. Snap back to `topRight` x in that case so it stays\n // visible. See https://github.com/jenslind/electron-positioner.\n if (position.startsWith('tray')) {\n if (coords.x + windowWidth > screenSize.width + screenSize.x) {\n return { x: positions.topRight.x, y: coords.y };\n }\n }\n\n return coords;\n }\n}\n","/**\n * @ignore\n */\n\n/** */\n\nimport path from 'node:path';\nimport url from 'node:url';\n\nimport { app } from 'electron';\n\nimport type { Options } from '../types';\n\nconst DEFAULT_WINDOW_HEIGHT = 400;\nconst DEFAULT_WINDOW_WIDTH = 400;\n\n/**\n * Take as input some options, and return a sanitized version of it.\n *\n * @param opts - The options to clean.\n * @ignore\n */\nexport function cleanOptions(opts?: Partial<Options>): Options {\n const options: Partial<Options> = { ...opts };\n\n if (options.activateWithApp === undefined) {\n options.activateWithApp = true;\n }\n if (options.ignoreDoubleClickEvents === undefined) {\n options.ignoreDoubleClickEvents = true;\n }\n if (!options.dir) {\n options.dir = app.getAppPath();\n }\n if (!path.isAbsolute(options.dir)) {\n options.dir = path.resolve(options.dir);\n }\n // Note: options.index can be `false`\n if (options.index === undefined) {\n options.index = url.format({\n pathname: path.join(options.dir, 'index.html'),\n protocol: 'file:',\n slashes: true,\n });\n }\n options.loadUrlOptions = options.loadUrlOptions || {};\n\n options.tooltip = options.tooltip || '';\n\n // `icon`, `preloadWindow`, `showDockIcon`, `showOnAllWorkspaces`,\n // `showOnRightClick` don't need any special treatment\n\n // Now we take care of `browserWindow`\n if (!options.browserWindow) {\n options.browserWindow = {};\n }\n\n // Set width/height on options to be usable before the window is created\n options.browserWindow.width =\n // Note: not using `options.browserWindow.width || DEFAULT_WINDOW_WIDTH` so\n // that users can put a 0 width\n options.browserWindow.width !== undefined\n ? options.browserWindow.width\n : DEFAULT_WINDOW_WIDTH;\n options.browserWindow.height =\n options.browserWindow.height !== undefined\n ? options.browserWindow.height\n : DEFAULT_WINDOW_HEIGHT;\n\n return options as Options;\n}\n","/**\n * Utilities to get taskbar position and consequently menubar's position\n */\n\n/** */\n\nimport { screen as electronScreen, type Rectangle, type Tray } from 'electron';\n\nconst isLinux = process.platform === 'linux';\n\nconst trayToScreenRects = (tray: Tray): [Rectangle, Rectangle] => {\n // There may be more than one screen, so we need to figure out on which screen our tray icon lives.\n const { workArea, bounds: screenBounds } = electronScreen.getDisplayMatching(\n tray.getBounds(),\n );\n\n workArea.x -= screenBounds.x;\n workArea.y -= screenBounds.y;\n\n return [screenBounds, workArea];\n};\n\ntype TaskbarLocation = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Determine taskbard location: \"top\", \"bottom\", \"left\" or \"right\".\n *\n * Only tested on Windows for now, and only used in Windows.\n *\n * @param tray - The Electron Tray instance.\n */\nexport function taskbarLocation(tray: Tray): TaskbarLocation {\n const [screenBounds, workArea] = trayToScreenRects(tray);\n\n // TASKBAR LEFT\n if (workArea.x > 0) {\n // Most likely Ubuntu hence assuming the window should be on top\n if (isLinux && workArea.y > 0) return 'top';\n // The workspace starts more on the right\n return 'left';\n }\n\n // TASKBAR TOP\n if (workArea.y > 0) {\n return 'top';\n }\n\n // TASKBAR RIGHT\n // Here both workArea.y and workArea.x are 0 so we can no longer leverage them.\n // We can use the workarea and display width though.\n // Determine taskbar location\n if (workArea.width < screenBounds.width) {\n // The taskbar is either on the left or right, but since the LEFT case was handled above,\n // we can be sure we're dealing with a right taskbar\n return 'right';\n }\n\n // TASKBAR BOTTOM\n // Since all the other cases were handled, we can be sure we're dealing with a bottom taskbar\n return 'bottom';\n}\n\ntype WindowPosition =\n | 'trayCenter'\n | 'topRight'\n | 'trayBottomCenter'\n | 'leftCenter'\n | 'bottomRight';\n\n/**\n * Depending on where the taskbar is, determine where the window should be\n * positioned.\n *\n * @param tray - The Electron Tray instance.\n */\nexport function getWindowPosition(tray: Tray): WindowPosition {\n switch (process.platform) {\n // macOS\n // Supports top taskbars\n case 'darwin':\n return 'trayCenter';\n // Linux\n // Windows\n // Supports top/bottom/left/right taskbar\n case 'linux':\n case 'win32': {\n const traySide = taskbarLocation(tray);\n\n // Assign position for menubar\n if (traySide === 'top') {\n return isLinux ? 'topRight' : 'trayCenter';\n }\n if (traySide === 'bottom') {\n return 'bottomRight';\n }\n if (traySide === 'left') {\n // Vertically centered against the left edge of the work area.\n // `bottomLeft` would put the window in the screen's bottom-left\n // corner — visually disconnected from the tray icon, which sits\n // somewhere on the left strip. Tray-anchored y isn't available\n // through `Positioner` for side taskbars, so center is the best\n // compromise that stays close to the tray.\n return 'leftCenter';\n }\n if (traySide === 'right') {\n return 'bottomRight';\n }\n }\n }\n\n // When we really don't know, we just show the menubar on the top-right\n return 'topRight';\n}\n","import { EventEmitter } from 'node:events';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n autoUpdater,\n BrowserWindow,\n globalShortcut,\n type Menu,\n Tray,\n} from 'electron';\n\nimport { Positioner } from './Positioner';\nimport type { Options } from './types';\nimport { cleanOptions } from './util/cleanOptions';\nimport { getWindowPosition } from './util/getWindowPosition';\n\n/**\n * Grace period after `showWindow()` during which a `blur` on the popup is\n * treated as the spurious Windows post-show blur and ignored instead of\n * triggering the auto-hide. See the `blur` handler in {@link Menubar.createWindow}.\n */\nconst BLUR_HIDE_GRACE_MS = 400;\n\n/**\n * Delay before re-asserting the hidden dock after startup. macOS can silently\n * drop an `app.dock.hide()` that races the app's launch activation\n * transition, leaving the dock icon stuck for the whole session. See the\n * re-check in {@link Menubar.appReady}.\n */\nconst DOCK_REHIDE_DELAY_MS = 2_000;\n\n/**\n * The main Menubar class.\n */\nexport class Menubar extends EventEmitter {\n private _app: Electron.App;\n private _browserWindow?: BrowserWindow;\n private _contextMenu?: Menu;\n private _blurTimeout: NodeJS.Timeout | null = null; // track blur events with timeout\n private _isDestroyed: boolean;\n private _isQuitting: boolean; // set when the app is shutting down, used by hideOnClose\n private _isVisible: boolean; // track visibility\n private _cachedBounds?: Electron.Rectangle; // _cachedBounds are needed for double-clicked event\n private _options: Options;\n private _positioner: Positioner | undefined;\n private _shortcut?: Electron.Accelerator;\n private _rightClickContextMenuBound = false;\n private _warnedNoPositioning = false; // guards the one-time Wayland warning\n private _lastShowTime = 0; // timestamp of last show(), debounces the post-show blur on Windows\n private _dockRehideTimeout?: NodeJS.Timeout; // pending post-startup dock re-hide check\n private _repositioning = false; // guards against re-entrant positionWindow calls\n private _tray?: Tray;\n\n constructor(app: Electron.App, options?: Partial<Options>) {\n super();\n this._app = app;\n this._options = cleanOptions(options);\n this._isDestroyed = false;\n this._isQuitting = false;\n this._isVisible = false;\n\n app.on('before-quit', this.onBeforeQuit);\n autoUpdater.on('before-quit-for-update', this.onBeforeQuit);\n\n if (app.isReady()) {\n // See https://github.com/maxogden/menubar/pull/151\n process.nextTick(this.onAppReady);\n } else {\n app.on('ready', this.onAppReady);\n }\n }\n\n /**\n * The Electron [App](https://electronjs.org/docs/api/app)\n * instance.\n */\n get app(): Electron.App {\n return this._app;\n }\n\n /**\n * The {@link Positioner} instance used to compute where the menubar window\n * should appear on screen. Available after the `after-create-window` event.\n */\n get positioner(): Positioner {\n if (!this._positioner) {\n throw new Error(\n 'Please access `this.positioner` after the `after-create-window` event has fired.',\n );\n }\n\n return this._positioner;\n }\n\n /**\n * The Electron [Tray](https://electronjs.org/docs/api/tray) instance.\n */\n get tray(): Tray {\n if (!this._tray) {\n throw new Error(\n 'Please access `this.tray` after the `ready` event has fired.',\n );\n }\n\n return this._tray;\n }\n\n /**\n * The Electron [BrowserWindow](https://electronjs.org/docs/api/browser-window)\n * instance, if it's present.\n */\n get window(): BrowserWindow | undefined {\n return this._browserWindow;\n }\n\n /**\n * Tear down the menubar instance: destroy the window, remove the tray, and\n * detach all listeners. Subsequent clicks on the tray will be no-ops until a\n * new {@link Menubar} instance is created.\n */\n destroy(): void {\n if (this.isDestroyed()) {\n return;\n }\n // Set first so `hideOnClose` lets the close go through instead of\n // intercepting it.\n this._isDestroyed = true;\n\n if (this._shortcut) {\n globalShortcut.unregister(this._shortcut);\n this._shortcut = undefined;\n }\n\n if (this._dockRehideTimeout) {\n clearTimeout(this._dockRehideTimeout);\n this._dockRehideTimeout = undefined;\n }\n\n if (this._browserWindow) {\n this._browserWindow.destroy();\n this._browserWindow = undefined;\n }\n\n if (this._tray) {\n // Ensure all potential listeners are removed.\n for (const event of ['click', 'right-click', 'double-click']) {\n this._tray.removeListener(\n event as Parameters<Tray['on']>[0],\n this.clicked,\n );\n }\n this._tray.setToolTip('');\n this._tray = undefined;\n }\n\n this._app.removeListener('ready', this.onAppReady);\n this._app.removeListener('activate', this.onAppActivate);\n this._app.removeListener('before-quit', this.onBeforeQuit);\n autoUpdater.removeListener('before-quit-for-update', this.onBeforeQuit);\n }\n\n /**\n * Whether {@link destroy} has been called on this menubar instance.\n */\n isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Retrieve a menubar option.\n *\n * @param key - The option key to retrieve, see {@link Options}.\n */\n getOption<K extends keyof Options>(key: K): Options[K] {\n return this._options[key];\n }\n\n /**\n * Hide the menubar window.\n */\n hideWindow(): void {\n if (!this._browserWindow || !this._isVisible) {\n return;\n }\n this.emit('hide');\n this._browserWindow.hide();\n this.emit('after-hide');\n this._isVisible = false;\n if (this._blurTimeout) {\n clearTimeout(this._blurTimeout);\n this._blurTimeout = null;\n }\n this.refreshContextMenu();\n }\n\n /**\n * Register a global keyboard accelerator that toggles the menubar window.\n * Replaces any previously registered shortcut owned by this Menubar.\n * Pass `undefined` to clear the current shortcut without registering a new\n * one. Returns whether the registration succeeded.\n *\n * @param accelerator - An Electron\n * [Accelerator](https://electronjs.org/docs/api/accelerator) string, or\n * `undefined` to clear.\n */\n setGlobalShortcut(accelerator: Electron.Accelerator | undefined): boolean {\n if (this._shortcut) {\n globalShortcut.unregister(this._shortcut);\n this._shortcut = undefined;\n }\n this._options.globalShortcut = accelerator;\n if (!accelerator) {\n return true;\n }\n const ok = globalShortcut.register(accelerator, () => this.toggleWindow());\n if (ok) {\n this._shortcut = accelerator;\n }\n return ok;\n }\n\n /**\n * Toggle the menubar window: hide it if visible, show it otherwise.\n * Resolves once the window finishes showing or hiding.\n */\n async toggleWindow(): Promise<void> {\n if (this._browserWindow && this._isVisible) {\n this.hideWindow();\n return;\n }\n await this.showWindow();\n }\n\n /**\n * Re-center the menubar window over the tray icon. Convenience wrapper for\n * `positioner.move('trayCenter', tray.getBounds())` that's safe to call\n * after the `after-create-window` event. No-op if the window doesn't\n * exist yet.\n */\n recenterOnTray(): void {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n const bounds = this._tray.getBounds();\n const { x, y } = this.positioner.calculate('trayCenter', bounds);\n this._browserWindow.setPosition(Math.round(x), Math.round(y));\n }\n\n /**\n * Replace the tray context menu after construction. On Linux this also\n * re-publishes the menu to the SNI host, which is required after mutating\n * items in-place since libappindicator caches the previous serialization.\n * On macOS/Windows the right-click popup handler reads the current menu\n * reference, so swapping or clearing here takes effect immediately.\n *\n * @param menu - The new menu, or `null` to clear it.\n */\n setContextMenu(menu: Menu | null): void {\n this._contextMenu = menu ?? undefined;\n this._options.contextMenu = menu ?? undefined;\n if (!this._tray) {\n return;\n }\n if (process.platform === 'linux') {\n // `setContextMenu(null)` clears the menu on Linux.\n this._tray.setContextMenu(menu);\n return;\n }\n // macOS / Windows: bind the right-click popup once on first non-empty\n // assignment. The handler reads `this._contextMenu` at invoke time, so\n // later swaps and clears take effect without rebinding (and never leak\n // a stale closure reference).\n if (menu && !this._rightClickContextMenuBound) {\n this.bindRightClickContextMenu();\n }\n }\n\n /**\n * Re-publish the current context menu so in-place mutations of its items\n * (`visible`, `enabled`, `checked`, `label`) become visible to the user.\n *\n * Only Linux needs this: libappindicator / StatusNotifierItem serializes the\n * menu once and serves that copy until it is set again, so a mutated item\n * would otherwise keep rendering its old state until the next show or hide.\n * A no-op on macOS and Windows, where the menu is read at popup time — call\n * it unconditionally after mutating items.\n */\n refreshContextMenu(): void {\n if (\n process.platform === 'linux' &&\n this._contextMenu &&\n this._tray &&\n !this._tray.isDestroyed?.()\n ) {\n this._tray.setContextMenu(this._contextMenu);\n }\n }\n\n /**\n * Change an option after menubar is created.\n *\n * @param key - The option key to modify, see {@link Options}.\n * @param value - The value to set.\n */\n setOption<K extends keyof Options>(key: K, value: Options[K]): void {\n this._options[key] = value;\n }\n\n /**\n * Show the menubar window.\n *\n * @param trayPos - The bounds to show the window in.\n */\n async showWindow(trayPos?: Electron.Rectangle): Promise<void> {\n if (!this.tray) {\n throw new Error('Tray should have been instantiated by now');\n }\n\n if (!this._browserWindow) {\n await this.createWindow();\n }\n\n // Use guard for TypeScript, to avoid ! everywhere\n if (!this._browserWindow) {\n throw new Error('Window has been initialized just above. qed.');\n }\n\n this.emit('show');\n\n // Cache fresh tray bounds (or fall back to existing cache / tray bounds)\n // so `positionWindow` can reposition without an event payload — for\n // example when the window resizes via `setSize`.\n if (trayPos && trayPos.x !== 0) {\n this._cachedBounds = trayPos;\n } else if (!this._cachedBounds && this.tray.getBounds) {\n this._cachedBounds = this.tray.getBounds();\n }\n\n this.positionWindow();\n // Record the show time before `show()` so the blur handler can recognise\n // and ignore the transient blur Windows fires right after (see below).\n this._lastShowTime = Date.now();\n this._browserWindow.show();\n this._isVisible = true;\n this.emit('after-show');\n this.refreshContextMenu();\n }\n\n /**\n * Compute and apply the tray-anchored position of the browser window. Safe\n * to call any time after `createWindow` has run — invoked from\n * {@link showWindow} on every show, and from the window's `resize` event so\n * `setSize` calls reposition the window correctly.\n */\n private positionWindow = (): void => {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n\n // `setPosition` below can make Windows emit `resize` synchronously, which\n // re-enters this method through the window's `resize` listener. Each pass\n // nudges the window again, so the recursion never unwinds: the main\n // process wedges with the event loop blocked and the window grows past\n // screen size (gitify-app/gitify#3064). Ignore re-entrant calls; the\n // outermost one already applies the final position.\n if (this._repositioning) {\n return;\n }\n this._repositioning = true;\n try {\n this.applyWindowPosition();\n } finally {\n this._repositioning = false;\n }\n };\n\n /**\n * Compute and apply the tray-anchored position. Always call through\n * {@link positionWindow}, which guards against re-entrancy.\n */\n private applyWindowPosition(): void {\n if (!this._browserWindow || !this._tray) {\n return;\n }\n\n // 'Windows' taskbar: sync window position each time before positioning.\n // https://github.com/maxogden/menubar/issues/232\n if (['win32', 'linux'].includes(process.platform)) {\n this._options.windowPosition = getWindowPosition(this._tray);\n }\n\n const trayPos = this._cachedBounds ?? this._tray.getBounds?.();\n\n // Default the window to the right if `trayPos` bounds are undefined or null.\n let noBoundsPosition: Options['windowPosition'];\n if (\n (trayPos === undefined || trayPos.x === 0) &&\n this._options.windowPosition?.startsWith('tray')\n ) {\n noBoundsPosition =\n process.platform === 'win32' ? 'bottomRight' : 'topRight';\n }\n\n const position = this.positioner.calculate(\n this._options.windowPosition || noBoundsPosition,\n trayPos,\n ) as { x: number; y: number };\n\n // Not using `||` because x and y can be zero.\n const x =\n this._options.browserWindow.x !== undefined\n ? this._options.browserWindow.x\n : position.x;\n const y =\n this._options.browserWindow.y !== undefined\n ? this._options.browserWindow.y\n : position.y;\n\n // `.setPosition` crashed on non-integers\n // https://github.com/maxogden/menubar/issues/233\n const targetX = Math.round(x);\n const targetY = Math.round(y);\n this._browserWindow.setPosition(targetX, targetY);\n\n // Native Wayland gives an application no way to position its own window:\n // `setPosition` is a no-op and `getPosition` reads back [0, 0]\n // (https://github.com/electron/electron/issues/40886). The compositor\n // decides where the window lands, usually centered, so the popover can't\n // be anchored to the tray. Detect that the move didn't take and warn once,\n // pointing at the X11 fallback. Gated on a Wayland session (`WAYLAND_DISPLAY`)\n // so pure X11 never trips it; the tolerance absorbs the few-pixel offsets\n // X11 window managers add for decorations, so XWayland (where positioning\n // works) doesn't trip it either.\n if (\n process.platform === 'linux' &&\n !!process.env.WAYLAND_DISPLAY &&\n !this._warnedNoPositioning\n ) {\n const [actualX, actualY] = this._browserWindow.getPosition();\n const ignored =\n Math.abs(actualX - targetX) > 24 || Math.abs(actualY - targetY) > 24;\n if (ignored) {\n this._warnedNoPositioning = true;\n console.warn(\n '[menubar] The window could not be positioned programmatically, ' +\n 'which is expected on native Wayland where the compositor controls ' +\n 'placement. The popover will not be anchored to the tray icon. Run ' +\n 'with --ozone-platform=x11 to restore tray-relative positioning.',\n );\n }\n }\n }\n\n private async appReady(): Promise<void> {\n if (this.app.dock && !this._options.showDockIcon) {\n this.app.dock.hide();\n\n // The hide above can be silently dropped when it races the launch\n // activation transition. Re-check once startup has settled; guarded on\n // visibility because `dock.hide()` also deactivates the app, so it must\n // not run when the dock is already hidden.\n this._dockRehideTimeout = setTimeout(() => {\n if (!this._isDestroyed && this.app.dock?.isVisible()) {\n this.app.dock.hide();\n }\n }, DOCK_REHIDE_DELAY_MS);\n }\n\n if (this._options.activateWithApp) {\n this.app.on('activate', this.onAppActivate);\n }\n\n let trayImage =\n this._options.icon || path.join(this._options.dir, 'IconTemplate.png');\n if (typeof trayImage === 'string' && !fs.existsSync(trayImage)) {\n trayImage = path.join(__dirname, '..', 'assets', 'IconTemplate.png'); // Default cat icon\n }\n\n const trigger =\n this._options.trigger ??\n (this._options.showOnRightClick ? 'right-click' : 'click');\n\n this._tray = this._options.tray || new Tray(trayImage);\n // Type guards for TS not to complain\n if (!this.tray) {\n throw new Error('Tray has been initialized above');\n }\n if (trigger !== 'none') {\n this.tray.on(trigger as Parameters<Tray['on']>[0], this.clicked);\n this.tray.on('double-click', this.clicked);\n }\n // macOS-only: ignore double-click so an accidental second click doesn't\n // race the blur handler and cause a tray-icon flicker.\n if (\n process.platform === 'darwin' &&\n this._options.ignoreDoubleClickEvents\n ) {\n this.tray.setIgnoreDoubleClickEvents(true);\n }\n this.tray.setToolTip(this._options.tooltip);\n\n if (this._options.contextMenu) {\n this.bindContextMenu(this._options.contextMenu);\n }\n\n if (this._options.globalShortcut) {\n this.setGlobalShortcut(this._options.globalShortcut);\n }\n\n if (!this._options.windowPosition) {\n this._options.windowPosition = getWindowPosition(this.tray);\n }\n\n if (this._options.preloadWindow) {\n await this.createWindow();\n }\n\n this.emit('ready');\n }\n\n /**\n * Callback on tray icon click or double-click.\n *\n * @param e\n * @param bounds\n */\n private clicked = async (\n event?: Electron.KeyboardEvent,\n bounds?: Electron.Rectangle,\n ): Promise<void> => {\n if (event && (event.shiftKey || event.ctrlKey || event.metaKey)) {\n return this.hideWindow();\n }\n\n // if blur was invoked clear timeout\n if (this._blurTimeout) {\n clearInterval(this._blurTimeout);\n }\n\n if (this._browserWindow && this._isVisible) {\n return this.hideWindow();\n }\n\n this._cachedBounds = bounds || this._cachedBounds;\n await this.showWindow(this._cachedBounds);\n };\n\n private onAppActivate = (\n _event: Electron.Event,\n hasVisibleWindows: boolean,\n ): void => {\n if (!hasVisibleWindows) {\n this.showWindow().catch(console.error);\n }\n };\n\n /**\n * Marks the app as shutting down so `hideOnClose` stops intercepting closes.\n *\n * Bound to both `app`'s `before-quit` and the auto updater's\n * `before-quit-for-update`. Installing an update never emits `before-quit`:\n * Electron closes every window first and only quits once the window list is\n * empty, so a `hideOnClose` veto there would leave the window open and the\n * install waiting forever.\n */\n private onBeforeQuit = (): void => {\n this._isQuitting = true;\n };\n\n private bindContextMenu(menu: Menu): void {\n this._contextMenu = menu;\n if (process.platform === 'linux') {\n // libappindicator / StatusNotifierItem requires the menu to live on the\n // tray itself; right-click is handled by the desktop environment.\n this.tray.setContextMenu(menu);\n return;\n }\n this.bindRightClickContextMenu();\n }\n\n private bindRightClickContextMenu(): void {\n // macOS / Windows: pop up the current menu on right-click so left-click\n // stays bound to toggling the menubar window. Read `this._contextMenu`\n // at invoke time so `setContextMenu()` swaps and clears take effect\n // without rebinding (and without leaking a stale closure reference).\n this.tray.on('right-click', (_event, bounds) => {\n const current = this._contextMenu;\n if (!current) {\n return;\n }\n this.tray.popUpContextMenu(current, { x: bounds.x, y: bounds.y });\n });\n this._rightClickContextMenuBound = true;\n }\n\n private onAppReady = (): void => {\n // Guard against `destroy()` being called between construction and the\n // scheduled `process.nextTick`/`'ready'` firing.\n if (this._isDestroyed) {\n return;\n }\n this.appReady().catch((err) => console.error('menubar: ', err));\n };\n\n private async createWindow(): Promise<void> {\n this.emit('create-window');\n\n // We add some default behavior for menubar's browserWindow, to make it\n // look like a menubar\n const defaults = {\n show: false, // Don't show it at first\n frame: false, // Remove window frame\n };\n\n this._browserWindow = new BrowserWindow({\n ...defaults,\n ...this._options.browserWindow,\n });\n\n this._positioner = new Positioner(this._browserWindow);\n\n this._browserWindow.on('blur', () => {\n if (!this._browserWindow) {\n return;\n }\n\n // The window was pinned (e.g. a host \"keep open on blur\" preference);\n // don't auto-hide, just surface the event for the host app to react to.\n if (this._browserWindow.isAlwaysOnTop()) {\n this.emit('focus-lost');\n return;\n }\n\n // Windows foreground-activation race: clicking the tray icon keeps the\n // shell (explorer.exe) as the foreground process, so `show()` cannot pull\n // focus to the popup and Windows immediately fires `blur`. Left unguarded,\n // the hide timer below fires before the first paint and the window never\n // visibly appears (gitify-app/gitify#3064). Ignore blur events that land\n // within the grace window right after a show; a genuine click-away\n // arrives well after it.\n if (\n process.platform === 'win32' &&\n Date.now() - this._lastShowTime < BLUR_HIDE_GRACE_MS\n ) {\n return;\n }\n\n this._blurTimeout = setTimeout(() => {\n this.hideWindow();\n }, 100);\n });\n\n if (this._options.showOnAllWorkspaces !== false) {\n // https://github.com/electron/electron/issues/37832#issuecomment-1497882944\n this._browserWindow.setVisibleOnAllWorkspaces(true, {\n skipTransformProcessType: true, // Avoid damaging the original visible state of app.dock\n });\n }\n\n if (this._options.hideOnClose) {\n this._browserWindow.on('close', (event) => {\n if (this._isDestroyed || this._isQuitting) {\n return;\n }\n event.preventDefault();\n // Defer the hide for Wayland: hiding synchronously from the `close`\n // handler can leave frameless surfaces in a half-closed state.\n setImmediate(() => this.hideWindow());\n });\n }\n\n if (this._options.escapeToHide) {\n this._browserWindow.webContents.on(\n 'before-input-event',\n (_event, input) => {\n if (input.type === 'keyDown' && input.key === 'Escape') {\n this.hideWindow();\n }\n },\n );\n }\n\n // Use `closed` (not `close`) so consumer `close` listeners can still read\n // `mb.window` and call `event.preventDefault()` without racing our cleanup.\n this._browserWindow.on('closed', this.windowClear.bind(this));\n\n // Re-anchor the window to the tray when its size changes (e.g. via\n // `mb.window.setSize(...)`), so the window doesn't end up clipped under\n // the taskbar. https://github.com/maxogden/menubar/issues/349\n this._browserWindow.on('resize', this.positionWindow);\n\n this.emit('before-load');\n\n // If the user explicity set options.index to false, we don't loadURL\n // https://github.com/maxogden/menubar/issues/255\n if (this._options.index !== false) {\n await this._browserWindow.loadURL(\n this._options.index,\n this._options.loadUrlOptions,\n );\n }\n this.emit('after-create-window');\n }\n\n private windowClear(): void {\n this._browserWindow = undefined;\n this.emit('after-close');\n }\n}\n","/**\n * Entry point of menubar\n * @example\n * ```typescript\n * import { menubar } from 'menubar';\n * ```\n */\n\n/** */\n\nimport { app } from 'electron';\n\nimport { Menubar } from './Menubar';\nimport type { Options } from './types';\n\nexport * from './util/getWindowPosition';\nexport { Menubar };\n\n/**\n * Factory function to create a menubar application\n *\n * @param options - Options for creating a menubar application, see\n * {@link Options}\n */\nexport function menubar(options?: Partial<Options>): Menubar {\n return new Menubar(app, options);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-menubar",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.2.0",
|
|
4
4
|
"description": "high level way to create menubar desktop applications with electron",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"oxfmt": "0.60.0",
|
|
69
69
|
"oxlint": "1.75.0",
|
|
70
70
|
"pngjs": "7.0.0",
|
|
71
|
-
"rolldown": "1.2.
|
|
71
|
+
"rolldown": "1.2.1",
|
|
72
72
|
"rolldown-plugin-dts": "0.27.14",
|
|
73
73
|
"typescript": "7.0.2",
|
|
74
74
|
"vitest": "4.1.10"
|