diff --git a/configs/webpack.config.renderer.ts b/configs/webpack.config.renderer.ts
index e4ffd64e..699ae020 100644
--- a/configs/webpack.config.renderer.ts
+++ b/configs/webpack.config.renderer.ts
@@ -24,6 +24,7 @@ export const config: webpack.Configuration = {
new HtmlWebpackPlugin({
template: join(__dirname, '../public/renderer.html'),
filename: 'renderer.html',
+ title: process.env.npm_package_build_productName,
}),
],
};
diff --git a/public/renderer.html b/public/renderer.html
index 19409160..1604859f 100644
--- a/public/renderer.html
+++ b/public/renderer.html
@@ -4,7 +4,7 @@
-
Breach Protocol Autosolver
+ <%= htmlWebpackPlugin.options.title %>
diff --git a/src/client-electron/common.ts b/src/client-electron/common.ts
index 7a46c116..4a914763 100644
--- a/src/client-electron/common.ts
+++ b/src/client-electron/common.ts
@@ -62,6 +62,7 @@ export interface AppSettings {
experimentalBufferSizeRecognition: boolean;
format: 'png' | 'jpg';
activeDisplayId: string;
+ minimizeToTray: boolean;
}
export interface AppStats {
diff --git a/src/client-electron/main/main.ts b/src/client-electron/main/main.ts
index 1e642952..7895b9b9 100644
--- a/src/client-electron/main/main.ts
+++ b/src/client-electron/main/main.ts
@@ -5,9 +5,11 @@ import {
ipcMain as ipc,
Menu,
shell,
+ Tray,
} from 'electron';
import { copyFileSync, ensureDirSync, remove, writeJSONSync } from 'fs-extra';
import { extname, join } from 'path';
+import icon from '../renderer/assets/icon.png';
import { Store } from './store/store';
import { createBrowserWindows } from './windows';
@@ -37,6 +39,24 @@ export class Main {
},
];
+ private trayMenu: Electron.MenuItemConstructorOptions[] = [
+ {
+ label: 'Show',
+ click: () => {
+ this.renderer.show();
+ },
+ },
+ { type: 'separator' },
+ {
+ label: 'Exit',
+ click: () => {
+ this.renderer.close();
+ },
+ },
+ ];
+
+ tray: Electron.Tray;
+
init() {
const { worker, renderer } = createBrowserWindows();
this.store = new Store(worker.webContents, renderer.webContents);
@@ -47,6 +67,19 @@ export class Main {
this.registerListeners();
}
+ private createTray() {
+ const tray = new Tray(join(__dirname, icon));
+ const contextMenu = Menu.buildFromTemplate(this.trayMenu);
+
+ tray.on('double-click', () => {
+ this.renderer.show();
+ });
+ tray.setToolTip(process.env.npm_package_build_productName);
+ tray.setContextMenu(contextMenu);
+
+ return tray;
+ }
+
private registerListeners() {
ipc.on('renderer:close', this.onAppClose.bind(this));
ipc.on('renderer:minimize', this.onAppMinimize.bind(this));
@@ -60,10 +93,41 @@ export class Main {
this.renderer.once('ready-to-show', () => this.renderer.show());
this.renderer.once('closed', this.onRendererClosed.bind(this));
+ this.renderer.on('minimize', this.onRendererMinimize.bind(this));
+ this.renderer.on('restore', this.onRendererRestore.bind(this));
+ }
+
+ private onRendererMinimize(event: Electron.Event) {
+ if (!this.getSettings().minimizeToTray) {
+ return;
+ }
+
+ event.preventDefault();
+
+ this.tray = this.createTray();
+
+ this.renderer.setSkipTaskbar(true);
+ this.renderer.hide();
+ }
+
+ private onRendererRestore() {
+ if (!this.getSettings().minimizeToTray) {
+ return;
+ }
+
+ this.renderer.show();
+ this.renderer.setSkipTaskbar(false);
+
+ this.tray.destroy();
}
private removeAllListeners() {
ipc.removeAllListeners();
+
+ this.renderer.removeAllListeners();
+ this.worker.removeAllListeners();
+
+ globalShortcut.unregisterAll();
}
private registerKeyBind(keyBind: Electron.Accelerator) {
@@ -71,7 +135,7 @@ export class Main {
}
private onWorkerReady() {
- const { keyBind } = this.store.getState().settings;
+ const { keyBind } = this.getSettings();
this.registerKeyBind(keyBind);
}
@@ -132,8 +196,6 @@ export class Main {
this.removeAllListeners();
- globalShortcut.unregisterAll();
-
app.quit();
}
@@ -152,4 +214,8 @@ export class Main {
this.renderer.maximize();
}
}
+
+ private getSettings() {
+ return this.store.getState().settings;
+ }
}
diff --git a/src/client-electron/main/windows.ts b/src/client-electron/main/windows.ts
index 62d342a6..77b77518 100644
--- a/src/client-electron/main/windows.ts
+++ b/src/client-electron/main/windows.ts
@@ -1,6 +1,7 @@
import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron';
import isWsl from 'is-wsl';
import { join } from 'path';
+import icon from '../renderer/assets/icon.png';
const isDev = process.env.NODE_ENV === 'development';
@@ -27,6 +28,7 @@ const rendererOptions: BrowserWindowConstructorOptions = {
minHeight: 720,
// Maximize and drag does not work on wsl2.
frame: isDev && isWsl ? true : false,
+ icon: join(__dirname, icon),
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
diff --git a/src/client-electron/options.ts b/src/client-electron/options.ts
index 2a8b6b87..15d19bbd 100644
--- a/src/client-electron/options.ts
+++ b/src/client-electron/options.ts
@@ -104,6 +104,11 @@ const options = [
'Use Windows scaling to calculate coordinates of squares.',
false
),
+ new BreachProtocolOption(
+ 'minimizeToTray',
+ 'Minimize app to system tray.',
+ true
+ ),
];
function reduceOptions(
diff --git a/src/client-electron/renderer/assets/icon.png b/src/client-electron/renderer/assets/icon.png
new file mode 100755
index 00000000..29c948ad
Binary files /dev/null and b/src/client-electron/renderer/assets/icon.png differ
diff --git a/src/client-electron/renderer/pages/Settings.tsx b/src/client-electron/renderer/pages/Settings.tsx
index 8be41d19..8311db22 100644
--- a/src/client-electron/renderer/pages/Settings.tsx
+++ b/src/client-electron/renderer/pages/Settings.tsx
@@ -80,6 +80,10 @@ const GeneralSettings = () => {
return (
+
+
+
+
diff --git a/types/fonts/index.d.ts b/types/assets/index.d.ts
similarity index 50%
rename from types/fonts/index.d.ts
rename to types/assets/index.d.ts
index 1764f41e..6d48a89c 100644
--- a/types/fonts/index.d.ts
+++ b/types/assets/index.d.ts
@@ -1 +1,2 @@
declare module '*.ttf';
+declare module '*.png';