MainRunner.ts
1 import { 2 app, 3 BrowserWindow, 4 RenderProcessGoneDetails, 5 BrowserWindowConstructorOptions 6 } from 'electron' 7 import Constants, { TrayOptions } from './utils/Constants' 8 import IPCs, { registerIpcHandlers } from './IPCs' 9 import { createTray, hideWindow, showWindow } from './tray' 10 11 import { loadConfig } from './mcp/init' 12 13 const options = { 14 width: Constants.IS_DEV_ENV ? 1500 : 1280, 15 height: 1080, 16 minWidth: 375, 17 minHeight: 480, 18 tray: { 19 // all optional values from DEFAULT_TRAY_OPTIONS can de defined here 20 enabled: true, 21 menu: true, // true, to use a tray menu ; false to toggle visibility on click on tray icon 22 trayWindow: false // true, to use a tray floating window attached to top try icon 23 } 24 } 25 26 const exitApp = (mainWindow: BrowserWindow): void => { 27 if (mainWindow && !mainWindow.isDestroyed()) { 28 mainWindow.hide() 29 } 30 mainWindow.destroy() 31 app.exit() 32 } 33 34 export const createSplashWindow = async (): Promise<BrowserWindow> => { 35 const splashWindow = new BrowserWindow({ 36 width: 400, 37 height: 300, 38 frame: false, 39 alwaysOnTop: true, 40 resizable: false, 41 show: true, 42 skipTaskbar: true, 43 transparent: true 44 }) 45 46 if (Constants.IS_DEV_ENV) { 47 await splashWindow.loadURL(Constants.APP_SPLASH_URL_DEV) 48 } else { 49 await splashWindow.loadFile(Constants.APP_SPLASH_URL_PROD) 50 } 51 52 return splashWindow 53 } 54 55 export const createMainWindow = async (): Promise<BrowserWindow> => { 56 let opt: BrowserWindowConstructorOptions = { 57 title: Constants.APP_NAME, 58 show: false, 59 width: options.width, 60 height: options.height, 61 minWidth: options.minWidth, 62 minHeight: options.minHeight, 63 useContentSize: true, 64 webPreferences: Constants.DEFAULT_WEB_PREFERENCES, 65 frame: true, 66 ...(process.platform == 'win32' || process.platform == 'linux' || process.platform == 'darwin' 67 ? { 68 titleBarStyle: 'hidden', 69 titleBarOverlay: { 70 color: '#344767', 71 symbolColor: 'white', 72 height: 36 73 } 74 } 75 : {}) 76 } 77 const trayOptions: TrayOptions = options.tray?.enabled 78 ? { 79 ...Constants.DEFAULT_TRAY_OPTIONS, 80 ...options.tray 81 } 82 : { 83 ...Constants.DEFAULT_TRAY_OPTIONS, 84 enabled: false 85 } 86 87 // trayWindow requires tray.enabled=true 88 if (trayOptions.enabled && trayOptions.trayWindow) { 89 opt = { 90 ...opt, 91 width: options.width, 92 height: options.height, 93 maxWidth: options.width, 94 maxHeight: options.height, 95 show: false, 96 frame: false, 97 fullscreenable: false, 98 hiddenInMissionControl: true, 99 resizable: false, 100 transparent: true, 101 alwaysOnTop: true, 102 webPreferences: { 103 ...Constants.DEFAULT_WEB_PREFERENCES, 104 backgroundThrottling: false 105 } 106 } 107 } 108 const mainWindow = new BrowserWindow(opt) 109 110 // This will disable dev-tool as well 111 mainWindow.setMenu(null) 112 113 mainWindow.on('close', (event: Event): void => { 114 event.preventDefault() 115 exitApp(mainWindow) 116 }) 117 118 mainWindow.webContents.on('did-frame-finish-load', (): void => { 119 if (Constants.IS_DEV_ENV && Constants.IS_DEVTOOLS) { 120 mainWindow.webContents.openDevTools() 121 } 122 }) 123 124 if (trayOptions.enabled) { 125 createTray(mainWindow, trayOptions) 126 } 127 128 if (trayOptions.enabled && trayOptions.trayWindow) { 129 hideWindow(mainWindow) 130 if (trayOptions.showAtStartup) { 131 showWindow(mainWindow) 132 } 133 } else { 134 mainWindow.once('ready-to-show', (): void => { 135 mainWindow.setAlwaysOnTop(true) 136 mainWindow.show() 137 mainWindow.focus() 138 mainWindow.setAlwaysOnTop(false) 139 }) 140 } 141 142 // Initialize IPC Communication 143 IPCs.initialize() 144 145 const configs = await loadConfig() 146 147 const features = configs.map((params) => { 148 return registerIpcHandlers(params) 149 }) 150 151 IPCs.initializeMCP(features) 152 153 if (Constants.IS_DEV_ENV) { 154 await mainWindow.loadURL(Constants.APP_INDEX_URL_DEV) 155 } else { 156 await mainWindow.loadFile(Constants.APP_INDEX_URL_PROD) 157 } 158 159 return mainWindow 160 } 161 162 export const createErrorWindow = async ( 163 errorWindow: BrowserWindow, 164 mainWindow: BrowserWindow, 165 _details?: RenderProcessGoneDetails 166 ): Promise<BrowserWindow> => { 167 if (!Constants.IS_DEV_ENV) { 168 mainWindow?.hide() 169 } 170 171 errorWindow = new BrowserWindow({ 172 title: Constants.APP_NAME, 173 show: false, 174 resizable: Constants.IS_DEV_ENV, 175 webPreferences: Constants.DEFAULT_WEB_PREFERENCES 176 }) 177 178 errorWindow.setMenu(null) 179 180 if (Constants.IS_DEV_ENV) { 181 await errorWindow.loadURL(`${Constants.APP_INDEX_URL_DEV}#/error`) 182 } else { 183 await errorWindow.loadFile(Constants.APP_INDEX_URL_PROD, { hash: 'error' }) 184 } 185 186 errorWindow.on('ready-to-show', (): void => { 187 if (!Constants.IS_DEV_ENV && mainWindow && !mainWindow.isDestroyed()) { 188 mainWindow.destroy() 189 } 190 errorWindow.show() 191 errorWindow.focus() 192 }) 193 194 errorWindow.webContents.on('did-frame-finish-load', (): void => { 195 if (Constants.IS_DEV_ENV) { 196 errorWindow.webContents.openDevTools() 197 } 198 }) 199 200 return errorWindow 201 }