Describe the bug
When Minimize to tray is enabled, Kudu cannot be quit through any normal means. On macOS this goes further: the app blocks system shutdown/restart — macOS waits for Kudu to quit, and the shutdown hangs until the user force-quits Kudu.
To reproduce
- Enable Minimize to tray in Settings (macOS, Kudu 1.44.1, also reproducible from current
main).
- Press Cmd+Q (or Kudu menu → Quit Kudu, or right-click the Dock icon → Quit). The window disappears, but the app keeps running (menu bar icon still there, processes alive).
- Shut down or restart the Mac → shutdown hangs on Kudu until it is force-quit.
Root cause
In src/main/index.ts, the main window's close handler calls e.preventDefault() unconditionally whenever minimizeToTray is enabled:
mainWindow.on('close', (e) => {
const currentSettings = getSettings()
if (currentSettings.minimizeToTray && mainWindow && !mainWindow.isDestroyed()) {
e.preventDefault()
mainWindow.hide()
}
})
In Electron, app.quit() (which is what Cmd+Q, the menu Quit, and the OS shutdown Apple Event go through) first tries to close all windows — and a preventDefault() in any window's close handler aborts the entire quit. The before-quit handler never sets any "we are quitting" flag, so there is no path for quit to get past the interceptor.
The tray menu's Quit item works around this with mainWindow.removeAllListeners('close'), which is why quitting from the tray is currently the only way out.
Expected behavior
Closing the window with Minimize to tray enabled should hide it to the tray (as today), but an explicit quit (Cmd+Q / menu / Dock / OS shutdown) should actually quit the app.
Also affected: update restarts
The same interceptor breaks the auto-updater on macOS: autoUpdater.quitAndInstall() closes all windows before app emits before-quit, so with minimize to tray enabled the prevented close silently blocks the "restart to install update" flow too (autoRestart/daemon updates never complete).
Suggested fix
The standard Electron pattern: set an isQuitting flag on before-quit (and before-quit-for-update for the updater path) and let close proceed when it's set. Fix proposed in #240.
Environment
- Kudu 1.44.1 (also present in current
main)
- macOS 15 (Darwin 25.5.0), Apple Silicon
Describe the bug
When Minimize to tray is enabled, Kudu cannot be quit through any normal means. On macOS this goes further: the app blocks system shutdown/restart — macOS waits for Kudu to quit, and the shutdown hangs until the user force-quits Kudu.
To reproduce
main).Root cause
In
src/main/index.ts, the main window'sclosehandler callse.preventDefault()unconditionally wheneverminimizeToTrayis enabled:In Electron,
app.quit()(which is what Cmd+Q, the menu Quit, and the OS shutdown Apple Event go through) first tries to close all windows — and apreventDefault()in any window'sclosehandler aborts the entire quit. Thebefore-quithandler never sets any "we are quitting" flag, so there is no path for quit to get past the interceptor.The tray menu's Quit item works around this with
mainWindow.removeAllListeners('close'), which is why quitting from the tray is currently the only way out.Expected behavior
Closing the window with Minimize to tray enabled should hide it to the tray (as today), but an explicit quit (Cmd+Q / menu / Dock / OS shutdown) should actually quit the app.
Also affected: update restarts
The same interceptor breaks the auto-updater on macOS:
autoUpdater.quitAndInstall()closes all windows beforeappemitsbefore-quit, so with minimize to tray enabled the prevented close silently blocks the "restart to install update" flow too (autoRestart/daemon updates never complete).Suggested fix
The standard Electron pattern: set an
isQuittingflag onbefore-quit(andbefore-quit-for-updatefor the updater path) and letcloseproceed when it's set. Fix proposed in #240.Environment
main)