Skip to content

Commit f1ceb41

Browse files
committed
Streamlined window management
1 parent ca25a18 commit f1ceb41

File tree

3 files changed

+35
-48
lines changed

3 files changed

+35
-48
lines changed

.changeset/poor-islands-film.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@bluecadet/launchpad-monitor": minor
3+
---
4+
5+
Swapped window management engine for fewer dependencies. Removed 'fake key' setting.

packages/monitor/lib/launchpad-monitor.js

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@ export class LaunchpadMonitor {
3838
*/
3939
_pm2Bus = null;
4040

41-
/**
42-
* @type {Object}
43-
*/
44-
_windowsApi = null;
45-
4641
/**
4742
* Creates a new instance, starts it with the
4843
* config and resolves with the monitor instance.
@@ -393,12 +388,6 @@ export class LaunchpadMonitor {
393388
* @returns {Promise}
394389
*/
395390
async _applyWindowSettings(appNames = null) {
396-
397-
if (!this._isWindowsOS()) {
398-
this._logger.warn(`Not applying windows settings since this is only supported on Windows OS.`);
399-
return;
400-
}
401-
402391
const currVersion = process.version;
403392
const requVersion = this._config.windowsApi.nodeVersion;
404393

@@ -410,62 +399,58 @@ export class LaunchpadMonitor {
410399
appNames = this._validateAppNames(appNames);
411400

412401
this._logger.info(`Applying window settings to ${appNames.length} ${appNames.length === 1 ? 'app' : 'apps'}`);
413-
414-
let windowsApi = null;
415-
try {
416-
windowsApi = await this._getWindowsApi();
417-
} catch (err) {
418-
this._logger.error(`Could not retrieve Windows API libraries. Make sure optional deps are installed: 'npm i robotjs ffi-napi ref-napi'`, err);
419-
}
402+
420403

421404
const fgPids = new Set();
422405
const minPids = new Set();
423406
const hidePids = new Set();
424407

425408
windowManager.requestAccessibility();
426409
const visibleWindows = windowManager.getWindows().filter(win => win.isVisible());
410+
const visiblePids = new Set(visibleWindows.map(win => win.processId));
427411

428412
for (const appName of appNames) {
429413
const appOptions = this.getAppOptions(appName);
430414
const winOptions = appOptions.windows;
431415
const appProcess = await this.getAppProcess(appName);
432416

433417
if (!appProcess || appProcess.pm2_env.status !== 'online') {
434-
this._logger.warn(`Not applying window settings to ${appName} because it's not online.`);
435-
return appProcess;
418+
this._logger.warn(`Not applying window settings to ${chalk.blue(appName)} because it's not online.`);
419+
continue;
436420
}
437421

438-
const appLabel = `"${appName}" (pid: ${appProcess.pid})`;
422+
if (!visiblePids.has(appProcess.pid)) {
423+
this._logger.warn(`No window found for ${chalk.blue(appName)} with pid ${chalk.blue(appProcess.pid)}.`);
424+
continue;
425+
}
439426

440427
if (winOptions.hide) {
441-
this._logger.debug(`Hiding ${appLabel}`);
442428
hidePids.add(appProcess.pid);
443429
}
444430
if (winOptions.minimize) {
445-
this._logger.debug(`Minimizing ${appLabel}`);
446431
minPids.add(appProcess.pid);
447432
}
448433
if (winOptions.foreground) {
449-
this._logger.debug(`Moving ${appLabel} to the foreground`);
450434
fgPids.add(appProcess.pid);
451435
}
452436
}
453437

454-
visibleWindows.filter(win => hidePids.has(win.processId)).forEach(win => win.hide());
455-
visibleWindows.filter(win => minPids.has(win.processId)).forEach(win => win.minimize());
456-
visibleWindows.filter(win => fgPids.has(win.processId)).forEach(win => win.bringToTop());
438+
visibleWindows.filter(win => hidePids.has(win.processId)).forEach(win => {
439+
this._logger.info(`Hiding ${win.getTitle()} (pid: ${win.processId})`);
440+
win.hide();
441+
});
442+
visibleWindows.filter(win => minPids.has(win.processId)).forEach(win => {
443+
this._logger.info(`Minimizing ${win.getTitle()} (pid: ${win.processId})`);
444+
win.minimize();
445+
});
446+
visibleWindows.filter(win => fgPids.has(win.processId)).forEach(win => {
447+
this._logger.info(`Foregrounding ${win.getTitle()} (pid: ${win.processId})`);
448+
win.bringToTop();
449+
});
457450

458451
this._logger.debug(`✅ Done applying window settings.`);
459452
}
460453

461-
async _getWindowsApi() {
462-
if (!this._windowsApi) {
463-
// Importing at runtime allows optional dependencies for non-Windows platforms
464-
this._windowsApi = await import('./windows-api.js');
465-
}
466-
return this._windowsApi;
467-
}
468-
469454
_isWindowsOS() {
470455
return process.platform === 'win32';
471456
}

packages/monitor/lib/monitor-options.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@ export class AppOptions {
5454
* @default null
5555
*/
5656
this.pm2 = pm2;
57+
5758
/**
5859
* Optional settings for moving this app's main windows to the foreground, minimize or hide them.
5960
* @type {WindowOptions}
6061
* @default new WindowOptions()
6162
*/
6263
this.windows = windows;
64+
6365
/**
6466
* Optional settings for how to log this app's output.
6567
* @type {AppLogOptions}
@@ -84,12 +86,14 @@ export class WindowOptions {
8486
* @default false
8587
*/
8688
this.foreground = foreground;
89+
8790
/**
8891
* Minimize this app's windows once all apps have been launched.
8992
* @type {boolean}
9093
* @default false
9194
*/
9295
this.minimize = minimize;
96+
9397
/**
9498
* Completely hide this app's windows once all apps have been launched. Helpful for headless apps, but note that this might cause issues with GUI-based apps.
9599
*
@@ -111,6 +115,7 @@ export const LogModes = {
111115
* @type {string}
112116
*/
113117
TailLogFile: 'file',
118+
114119
/**
115120
* Logs directly from the app's stdout/stderr bus. Can result in interrupted logs if the buffer isn't consistently flushed by an app.
116121
* @type {string}
@@ -134,6 +139,7 @@ export class AppLogOptions {
134139
* @default true
135140
*/
136141
this.logToLaunchpadDir = logToLaunchpadDir;
142+
137143
/**
138144
* How to grab the app's logs. Supported values:
139145
* - `'file'`: Logs by tailing the app's log files. Slight lag, but can result in better formatting than bus.
@@ -142,12 +148,14 @@ export class AppLogOptions {
142148
* @default 'file'
143149
*/
144150
this.mode = mode;
151+
145152
/**
146153
* Whether or not to include output from `stdout`
147154
* @type {boolean}
148155
* @default true
149156
*/
150157
this.showStdout = showStdout;
158+
151159
/**
152160
* Whether or not to include output from `stderr`
153161
* @type {boolean}
@@ -162,9 +170,8 @@ export class AppLogOptions {
162170
*/
163171
export class WindowsApiOptions {
164172
constructor({
165-
nodeVersion = '>=17.4.0',
166173
debounceDelay = 3000,
167-
fakeKey = 'control',
174+
nodeVersion = '>=17.4.0',
168175
...rest
169176
} = {}) {
170177
/**
@@ -176,6 +183,7 @@ export class WindowsApiOptions {
176183
* @default '>=17.4.0'
177184
*/
178185
this.nodeVersion = nodeVersion;
186+
179187
/**
180188
* The delay until windows are ordered after launch of in ms.
181189
*
@@ -186,17 +194,6 @@ export class WindowsApiOptions {
186194
* @default 3000
187195
*/
188196
this.debounceDelay = debounceDelay;
189-
/**
190-
* Windows OS is very strict with when and how apps can move windows to the foreground or backgruond. As a workaround, Launchpad emulates a keypress to make the current process active.
191-
*
192-
* This setting configures which key is used to emulate in order to gain control over window foregrounding/backgrounding. This key gets emulated after an app launches or re-launches.
193-
*
194-
* @see https://robotjs.io/docs/syntax#keys for available options
195-
* @see https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-allowsetforegroundwindow#remarks for window management requirements
196-
* @type {string}
197-
* @default 'control'
198-
*/
199-
this.fakeKey = fakeKey;
200197

201198
Object.assign(this, rest);
202199
}

0 commit comments

Comments
 (0)