electron-menubar 1.0.2 → 10.1.1
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/LICENSE +22 -17
- package/README.md +171 -51
- package/assets/IconTemplate.png +0 -0
- package/assets/IconTemplate@2x.png +0 -0
- package/lib/index.cjs +564 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +330 -0
- package/lib/index.d.mts +330 -0
- package/lib/index.mjs +535 -0
- package/lib/index.mjs.map +1 -0
- package/package.json +66 -26
- package/.travis.yml +0 -4
- package/index.js +0 -7
- package/menubar.js +0 -179
- package/positioner.js +0 -211
package/package.json
CHANGED
|
@@ -1,37 +1,77 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-menubar",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
3
|
+
"version": "10.1.1",
|
|
4
|
+
"description": "high level way to create menubar desktop applications with electron",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"app",
|
|
7
|
+
"electron",
|
|
8
|
+
"linux",
|
|
9
|
+
"mac",
|
|
10
|
+
"menu",
|
|
11
|
+
"menubar",
|
|
12
|
+
"shell",
|
|
13
|
+
"taskbar",
|
|
14
|
+
"tray",
|
|
15
|
+
"traybar",
|
|
16
|
+
"windows"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/gitify-app/electron-menubar",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/gitify-app/electron-menubar/issues"
|
|
21
|
+
},
|
|
22
|
+
"license": "BSD-2-Clause",
|
|
23
|
+
"author": "Gitify",
|
|
24
|
+
"contributors": [
|
|
25
|
+
"Afonso Jorge Ramos (https://github.com/afonsojramos)",
|
|
26
|
+
"Adam Setch (https://github.com/setchy)"
|
|
27
|
+
],
|
|
6
28
|
"repository": {
|
|
7
29
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/
|
|
30
|
+
"url": "git+https://github.com/gitify-app/electron-menubar.git"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"assets/IconTemplate.png",
|
|
34
|
+
"assets/IconTemplate@2x.png",
|
|
35
|
+
"lib"
|
|
36
|
+
],
|
|
37
|
+
"main": "lib/index.cjs",
|
|
38
|
+
"module": "lib/index.mjs",
|
|
39
|
+
"types": "lib/index.d.cts",
|
|
40
|
+
"exports": {
|
|
41
|
+
".": {
|
|
42
|
+
"import": {
|
|
43
|
+
"types": "./lib/index.d.mts",
|
|
44
|
+
"default": "./lib/index.mjs"
|
|
45
|
+
},
|
|
46
|
+
"require": {
|
|
47
|
+
"types": "./lib/index.d.cts",
|
|
48
|
+
"default": "./lib/index.cjs"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
9
54
|
},
|
|
10
55
|
"scripts": {
|
|
11
|
-
"
|
|
12
|
-
"lint": "
|
|
56
|
+
"build": "vp pack",
|
|
57
|
+
"lint:check": "vp check",
|
|
58
|
+
"lint": "vp check --fix",
|
|
59
|
+
"test": "vp test",
|
|
60
|
+
"test:e2e": "playwright test --config tests/e2e/playwright.config.ts",
|
|
61
|
+
"test:visual": "node tests/visual/run.mts",
|
|
62
|
+
"typecheck": "tsc --noEmit"
|
|
13
63
|
},
|
|
14
|
-
"keywords": [
|
|
15
|
-
"electron",
|
|
16
|
-
"atom",
|
|
17
|
-
"shell",
|
|
18
|
-
"menubar",
|
|
19
|
-
"menu",
|
|
20
|
-
"mac",
|
|
21
|
-
"windows",
|
|
22
|
-
"linux",
|
|
23
|
-
"app"
|
|
24
|
-
],
|
|
25
|
-
"author": "Henrique Dias <hacdias@gmail.com>",
|
|
26
|
-
"license": "MIT",
|
|
27
64
|
"devDependencies": {
|
|
28
|
-
"
|
|
29
|
-
"
|
|
65
|
+
"@playwright/test": "1.61.1",
|
|
66
|
+
"@types/pngjs": "6.0.5",
|
|
67
|
+
"electron": "43.0.0",
|
|
68
|
+
"pngjs": "7.0.0",
|
|
69
|
+
"typescript": "6.0.3",
|
|
70
|
+
"vite-plus": "0.2.1",
|
|
71
|
+
"vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24"
|
|
30
72
|
},
|
|
31
|
-
"
|
|
32
|
-
"
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"electron": "43.0.0"
|
|
33
75
|
},
|
|
34
|
-
"
|
|
35
|
-
"lint"
|
|
36
|
-
]
|
|
76
|
+
"packageManager": "pnpm@11.9.0"
|
|
37
77
|
}
|
package/.travis.yml
DELETED
package/index.js
DELETED
package/menubar.js
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const Positioner = require('./positioner')
|
|
3
|
-
const merge = require('lodash.merge')
|
|
4
|
-
const { EventEmitter } = require('events')
|
|
5
|
-
const electron = require('electron')
|
|
6
|
-
const { app, Tray, BrowserWindow } = electron
|
|
7
|
-
|
|
8
|
-
const defaults = {
|
|
9
|
-
preloadWindow: false,
|
|
10
|
-
showDockIcon: false,
|
|
11
|
-
showOnRightClick: false,
|
|
12
|
-
showOnAllWorkspaces: true,
|
|
13
|
-
tooltip: '',
|
|
14
|
-
window: {
|
|
15
|
-
width: 400,
|
|
16
|
-
height: 400,
|
|
17
|
-
show: false,
|
|
18
|
-
frame: false
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Class Menubar.
|
|
24
|
-
*/
|
|
25
|
-
class Menubar extends EventEmitter {
|
|
26
|
-
constructor (opts) {
|
|
27
|
-
super()
|
|
28
|
-
|
|
29
|
-
if (typeof opts !== 'object') {
|
|
30
|
-
opts = {}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
opts = merge(defaults, opts)
|
|
34
|
-
|
|
35
|
-
if (!opts.index) {
|
|
36
|
-
opts.index = `file://${path.join(app.getAppPath(), 'index.html')}`
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (!opts.icon) {
|
|
40
|
-
opts.icon = path.join(app.getAppPath(), 'icon.png')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
this.opts = opts
|
|
44
|
-
this.ready = false
|
|
45
|
-
this.cachedBounds = null
|
|
46
|
-
this.window = null
|
|
47
|
-
|
|
48
|
-
if (app.isReady()) {
|
|
49
|
-
this._appReady()
|
|
50
|
-
} else {
|
|
51
|
-
app.on('ready', this._appReady.bind(this))
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* This sets up the tray. Must only be called once.
|
|
57
|
-
*
|
|
58
|
-
* @returns {void}
|
|
59
|
-
*/
|
|
60
|
-
_appReady () {
|
|
61
|
-
if (app.dock && !this.opts.showDockIcon) {
|
|
62
|
-
app.dock.hide()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
let clickEvent = this.opts.showOnRightClick ? 'right-click' : 'click'
|
|
66
|
-
|
|
67
|
-
this.tray = new Tray(this.opts.icon)
|
|
68
|
-
this.tray.on(clickEvent, this._clicked.bind(this))
|
|
69
|
-
this.tray.on('double-click', this._clicked.bind(this))
|
|
70
|
-
this.tray.setToolTip(this.opts.tooltip)
|
|
71
|
-
this.tray.setHighlightMode('never')
|
|
72
|
-
|
|
73
|
-
if (this.opts.preloadWindow) {
|
|
74
|
-
this._createWindow()
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
this.ready = true
|
|
78
|
-
this.emit('ready')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Creates the Window. Must only be called once.
|
|
83
|
-
*
|
|
84
|
-
* @returns {void}
|
|
85
|
-
*/
|
|
86
|
-
_createWindow () {
|
|
87
|
-
this.emit('create-window')
|
|
88
|
-
|
|
89
|
-
this.window = new BrowserWindow(this.opts.window)
|
|
90
|
-
|
|
91
|
-
if (this.opts.showOnAllWorkspaces !== false) {
|
|
92
|
-
this.window.setVisibleOnAllWorkspaces(true)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
this.window.on('close', () => {
|
|
96
|
-
this.window = null
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
this.window.on('blur', () => {
|
|
100
|
-
if (!this.window.isAlwaysOnTop()) this.hide()
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
this.window.loadURL(this.opts.index)
|
|
104
|
-
this.emit('after-create-window')
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Triggered when clicking the tray icon.
|
|
109
|
-
*
|
|
110
|
-
* @param {Event} event
|
|
111
|
-
* @param {Rectangle} bounds
|
|
112
|
-
*
|
|
113
|
-
* @returns {void}
|
|
114
|
-
*/
|
|
115
|
-
_clicked (event, bounds) {
|
|
116
|
-
// Hide the window if clicking with ALT/SHIFT/CTRL or Meta keys.
|
|
117
|
-
if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
|
|
118
|
-
return this.hide()
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// If there is a window and it is already visible, hide it.
|
|
122
|
-
if (this.window && this.window.isVisible()) {
|
|
123
|
-
return this.hide()
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
this.cachedBounds = bounds || this.cachedBounds
|
|
127
|
-
this.show()
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Informs if the Menubar is already ready.
|
|
132
|
-
*
|
|
133
|
-
* @returns {void}
|
|
134
|
-
*/
|
|
135
|
-
isReady () {
|
|
136
|
-
return this.ready
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Hides the Window.
|
|
141
|
-
*
|
|
142
|
-
* @returns {void}
|
|
143
|
-
*/
|
|
144
|
-
hide () {
|
|
145
|
-
this.tray.setHighlightMode('never')
|
|
146
|
-
if (!this.window) return
|
|
147
|
-
this.emit('hide')
|
|
148
|
-
this.window.hide()
|
|
149
|
-
this.emit('after-hide')
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Shows the Window.
|
|
154
|
-
*
|
|
155
|
-
* @returns {void}
|
|
156
|
-
*/
|
|
157
|
-
show () {
|
|
158
|
-
this.tray.setHighlightMode('always')
|
|
159
|
-
if (!this.window) {
|
|
160
|
-
this._createWindow()
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
this.emit('show')
|
|
164
|
-
|
|
165
|
-
if (!this.cachedBounds) {
|
|
166
|
-
this.cachedBounds = this.tray.getBounds()
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
let pos = Positioner.calculate(this.window.getBounds(), this.cachedBounds)
|
|
170
|
-
pos.x = (this.opts.window.x !== undefined) ? this.opts.window.x : pos.x
|
|
171
|
-
pos.y = (this.opts.window.y !== undefined) ? this.opts.window.y : pos.y
|
|
172
|
-
|
|
173
|
-
this.window.setPosition(pos.x, pos.y)
|
|
174
|
-
this.window.show()
|
|
175
|
-
this.emit('after-show')
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
module.exports = Menubar
|
package/positioner.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
const electron = require('electron')
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Get the display nearest the current cursor position
|
|
5
|
-
*
|
|
6
|
-
* @return {Display} - the display closest to the current cursor position
|
|
7
|
-
*/
|
|
8
|
-
function getDisplay () {
|
|
9
|
-
const screen = electron.screen
|
|
10
|
-
return screen.getDisplayNearestPoint(screen.getCursorScreenPoint())
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Get cursor position
|
|
15
|
-
*
|
|
16
|
-
* @return {Point} - the position of the cursor
|
|
17
|
-
*/
|
|
18
|
-
function getCursorPosition () {
|
|
19
|
-
return electron.screen.getCursorScreenPoint()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Calculates the x position of the tray window
|
|
24
|
-
*
|
|
25
|
-
* @param {Rectangle} windowBounds - electron BrowserWindow bounds of tray window to position
|
|
26
|
-
* @param {Rectangle} trayBounds - tray bounds from electron Tray.getBounds()
|
|
27
|
-
* @param {string} [align] - align left|center|right, default: center
|
|
28
|
-
*
|
|
29
|
-
* @return {integer} - calculated x position
|
|
30
|
-
*/
|
|
31
|
-
function calculateXAlign (windowBounds, trayBounds, align) {
|
|
32
|
-
const display = getDisplay()
|
|
33
|
-
let x
|
|
34
|
-
|
|
35
|
-
switch (align) {
|
|
36
|
-
case 'right':
|
|
37
|
-
x = trayBounds.x
|
|
38
|
-
break
|
|
39
|
-
case 'left':
|
|
40
|
-
x = trayBounds.x + trayBounds.width - windowBounds.width
|
|
41
|
-
break
|
|
42
|
-
case 'center':
|
|
43
|
-
default:
|
|
44
|
-
x = Math.round(trayBounds.x + (trayBounds.width / 2) - (windowBounds.width / 2))
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (x + windowBounds.width > display.bounds.width && align !== 'left') {
|
|
48
|
-
// if window would overlap on right side align it to the end of the screen
|
|
49
|
-
x = display.bounds.width - windowBounds.width
|
|
50
|
-
} else if (x < 0 && align !== 'right') {
|
|
51
|
-
// if window would overlap on the left side align it to the beginning
|
|
52
|
-
x = 0
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return x
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Calculates the y position of the tray window
|
|
60
|
-
*
|
|
61
|
-
* @param {Rectangle} windowBounds - electron BrowserWindow bounds
|
|
62
|
-
* @param {Rectangle} trayBounds - tray bounds from electron Tray.getBounds()
|
|
63
|
-
* @param {string} [align] - align up|middle|down, default: down
|
|
64
|
-
*
|
|
65
|
-
* @return {integer} - calculated y position
|
|
66
|
-
*/
|
|
67
|
-
function calculateYAlign (windowBounds, trayBounds, align) {
|
|
68
|
-
const display = getDisplay()
|
|
69
|
-
let y
|
|
70
|
-
|
|
71
|
-
switch (align) {
|
|
72
|
-
case 'up':
|
|
73
|
-
y = trayBounds.y + trayBounds.height - windowBounds.height
|
|
74
|
-
break
|
|
75
|
-
case 'down':
|
|
76
|
-
y = trayBounds.y
|
|
77
|
-
break
|
|
78
|
-
case 'center':
|
|
79
|
-
default:
|
|
80
|
-
y = Math.round((trayBounds.y + (trayBounds.height / 2)) - (windowBounds.height / 2))
|
|
81
|
-
break
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (y + windowBounds.height > display.bounds.height && align !== 'up') {
|
|
85
|
-
y = trayBounds.y + trayBounds.height - windowBounds.height
|
|
86
|
-
} else if (y < 0 && align !== 'down') {
|
|
87
|
-
y = 0
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return y
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Calculates the position of the tray window based on current cursor position
|
|
95
|
-
* This method is used on linux where trayBounds are not available
|
|
96
|
-
*
|
|
97
|
-
* @param {Rectangle} windowBounds - electron BrowserWindow bounds of tray window to position
|
|
98
|
-
* @param {Eelectron.Display} display - display on which the cursor is currently
|
|
99
|
-
* @param {Point} cursor - current cursor position
|
|
100
|
-
*
|
|
101
|
-
* @return {Point} - Calculated point {x, y} where the window should be positioned
|
|
102
|
-
*/
|
|
103
|
-
function calculateByCursorPosition (windowBounds, display, cursor) {
|
|
104
|
-
let x = cursor.x
|
|
105
|
-
let y = cursor.y
|
|
106
|
-
|
|
107
|
-
if (x + windowBounds.width > display.bounds.width) {
|
|
108
|
-
// if window would overlap on right side of screen, align it to the left of the cursor
|
|
109
|
-
x -= windowBounds.width
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (y + windowBounds.height > display.bounds.height) {
|
|
113
|
-
// if window would overlap at bottom of screen, align it up from cursor position
|
|
114
|
-
y -= windowBounds.height
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
x: x,
|
|
119
|
-
y: y
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
class Positioner {
|
|
124
|
-
/**
|
|
125
|
-
* Calculates the position of the tray window
|
|
126
|
-
*
|
|
127
|
-
* @param {Rectangle} windowBounds - electron BrowserWindow bounds of tray window to position
|
|
128
|
-
* @param {Rectangle} trayBounds - tray bounds from electron Tray
|
|
129
|
-
* @param {Object} [alignment] - alignment of window to tray
|
|
130
|
-
* @param {string} [alignment.x] - x align if tray bar is on top or bottom (left|center|right),
|
|
131
|
-
default: center
|
|
132
|
-
* @param {string} [alignment.y] - y align if tray bar is left or right (up|middle|down),
|
|
133
|
-
default: down
|
|
134
|
-
* @return {Point} - Calculated point {x, y} where the window should be positioned
|
|
135
|
-
*/
|
|
136
|
-
static calculate (windowBounds, trayBounds, alignment) {
|
|
137
|
-
if (process.platform === 'linux') {
|
|
138
|
-
const cursor = getCursorPosition()
|
|
139
|
-
return calculateByCursorPosition(windowBounds, getDisplay(), cursor)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const _alignment = alignment || {}
|
|
143
|
-
const taskbarPosition = this.getTaskbarPosition()
|
|
144
|
-
const display = getDisplay()
|
|
145
|
-
let x
|
|
146
|
-
let y
|
|
147
|
-
|
|
148
|
-
switch (taskbarPosition) {
|
|
149
|
-
case 'left':
|
|
150
|
-
x = display.workArea.x
|
|
151
|
-
y = calculateYAlign(windowBounds, trayBounds, _alignment.y)
|
|
152
|
-
break
|
|
153
|
-
case 'right':
|
|
154
|
-
x = display.workArea.width - windowBounds.width
|
|
155
|
-
y = calculateYAlign(windowBounds, trayBounds, _alignment.y)
|
|
156
|
-
break
|
|
157
|
-
case 'bottom':
|
|
158
|
-
x = calculateXAlign(windowBounds, trayBounds, _alignment.x)
|
|
159
|
-
y = display.workArea.height - windowBounds.height
|
|
160
|
-
break
|
|
161
|
-
case 'top':
|
|
162
|
-
default:
|
|
163
|
-
x = calculateXAlign(windowBounds, trayBounds, _alignment.x)
|
|
164
|
-
y = display.workArea.y
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
x,
|
|
169
|
-
y
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Calculates the position of the tray window
|
|
175
|
-
*
|
|
176
|
-
* @param {BrowserWindow} window - window to position
|
|
177
|
-
* @param {Rectangle} trayBounds - tray bounds from electron Tray
|
|
178
|
-
* @param {Object} [alignment] - alignment of window to tray
|
|
179
|
-
* @param {string} [alignment.x] - x align if tray bar is on top or bottom (left|center|right),
|
|
180
|
-
default: center
|
|
181
|
-
* @param {string} [alignment.y] - y align if tray bar is left or right (up|middle|down),
|
|
182
|
-
default: down
|
|
183
|
-
*
|
|
184
|
-
* @return {Void}
|
|
185
|
-
*/
|
|
186
|
-
static position (window, trayBounds, alignment) {
|
|
187
|
-
const position = this.calculate(window.getBounds(), trayBounds, alignment)
|
|
188
|
-
window.setPosition(position.x, position.y, false)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Calculates the position of the tray window
|
|
193
|
-
*
|
|
194
|
-
* @return {string} - the position of the taskbar (top|right|bottom|left)
|
|
195
|
-
*/
|
|
196
|
-
static getTaskbarPosition () {
|
|
197
|
-
const { bounds, workArea } = getDisplay()
|
|
198
|
-
|
|
199
|
-
if (workArea.y > bounds.y) {
|
|
200
|
-
return 'top'
|
|
201
|
-
} else if (workArea.x > bounds.x) {
|
|
202
|
-
return 'left'
|
|
203
|
-
} else if (workArea.width === bounds.width) {
|
|
204
|
-
return 'bottom'
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return 'right'
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
module.exports = Positioner
|