@@ -74,14 +74,27 @@ function createTrayIconPng() {
7474 ] )
7575}
7676
77+ // Central quit — destroys GUI elements immediately then calls app.exit(0).
78+ // app.exit bypasses the window-all-closed no-op handler that otherwise keeps
79+ // the process alive as a background tray app, guaranteeing the process exits.
80+ function doQuit ( ) {
81+ if ( isQuitting ) return
82+ isQuitting = true
83+ if ( reconnectTimer ) { clearInterval ( reconnectTimer ) ; reconnectTimer = null }
84+ if ( deck ) { deck . clearPanel ( ) . catch ( ( ) => { } ) ; deck . close ( ) . catch ( ( ) => { } ) }
85+ if ( tray ) { tray . destroy ( ) ; tray = null } // remove icon immediately
86+ if ( mainWindow && ! mainWindow . isDestroyed ( ) ) mainWindow . destroy ( )
87+ app . exit ( 0 )
88+ }
89+
7790function createTray ( ) {
7891 const icon = nativeImage . createFromBuffer ( createTrayIconPng ( ) )
7992 tray = new Tray ( icon )
8093 tray . setToolTip ( 'Tech Stack Stream Deck' )
8194 const menu = Menu . buildFromTemplate ( [
8295 { label : 'Show Window' , click : ( ) => { mainWindow ?. show ( ) ; mainWindow ?. focus ( ) } } ,
8396 { type : 'separator' } ,
84- { label : 'Quit' , click : ( ) => { isQuitting = true ; if ( mainWindow && ! mainWindow . isDestroyed ( ) ) mainWindow . destroy ( ) ; app . quit ( ) } } ,
97+ { label : 'Quit' , click : doQuit } ,
8598 ] )
8699 tray . setContextMenu ( menu )
87100 // Left-click shows window (works on Windows/KDE; GNOME AppIndicator ignores it)
@@ -565,12 +578,15 @@ app.whenReady().then(async () => {
565578// Tray keeps the process alive when the window is hidden
566579app . on ( 'window-all-closed' , ( ) => { } )
567580
568- // Allow OS-level quit (shutdown, pkill) to bypass the hide intercept
569- app . on ( 'before-quit' , ( ) => { isQuitting = true } )
581+ // Ensure the tray icon disappears for any quit path that goes through app.quit()
582+ app . on ( 'before-quit' , ( ) => {
583+ isQuitting = true
584+ if ( tray ) { tray . destroy ( ) ; tray = null }
585+ } )
570586
571- // Graceful exit on OS signals (e.g. pkill, systemd stop, taskbar "Quit")
572- process . on ( 'SIGTERM' , ( ) => { isQuitting = true ; app . quit ( ) } )
573- process . on ( 'SIGINT' , ( ) => { isQuitting = true ; app . quit ( ) } )
587+ // OS signals (pkill, systemd stop) → use doQuit() for immediate, guaranteed exit
588+ process . on ( 'SIGTERM' , doQuit )
589+ process . on ( 'SIGINT' , doQuit )
574590
575591// Single-instance: second launch focuses the existing window instead
576592const gotSingleInstanceLock = app . requestSingleInstanceLock ( )
0 commit comments