@@ -55,12 +55,12 @@ function registerHandlers(): void {
5555 MessageType . EXECUTE_QUERY ,
5656 ( msg , port ) => executionHandler . handleExecuteQuery ( msg , port )
5757 )
58-
58+
5959 messageRouter . registerHandler (
6060 MessageType . CANCEL_TASK ,
6161 ( msg , port ) => executionHandler . handleCancelTask ( msg , port )
6262 )
63-
63+
6464 messageRouter . registerHandler (
6565 MessageType . RESET_CONVERSATION ,
6666 ( msg , port ) => executionHandler . handleResetConversation ( msg , port )
@@ -87,49 +87,49 @@ function registerHandlers(): void {
8787 MessageType . GET_LLM_PROVIDERS ,
8888 ( msg , port ) => providersHandler . handleGetProviders ( msg , port )
8989 )
90-
90+
9191 messageRouter . registerHandler (
9292 MessageType . SAVE_LLM_PROVIDERS ,
9393 ( msg , port ) => providersHandler . handleSaveProviders ( msg , port )
9494 )
95-
95+
9696 // MCP handlers
9797 messageRouter . registerHandler (
9898 MessageType . GET_MCP_SERVERS ,
9999 ( msg , port ) => mcpHandler . handleGetMCPServers ( msg , port )
100100 )
101-
101+
102102 messageRouter . registerHandler (
103103 MessageType . CONNECT_MCP_SERVER ,
104104 ( msg , port ) => mcpHandler . handleConnectMCPServer ( msg , port )
105105 )
106-
106+
107107 messageRouter . registerHandler (
108108 MessageType . DISCONNECT_MCP_SERVER ,
109109 ( msg , port ) => mcpHandler . handleDisconnectMCPServer ( msg , port )
110110 )
111-
111+
112112 messageRouter . registerHandler (
113113 MessageType . CALL_MCP_TOOL ,
114114 ( msg , port ) => mcpHandler . handleCallMCPTool ( msg , port )
115115 )
116-
116+
117117 messageRouter . registerHandler (
118118 MessageType . MCP_INSTALL_SERVER ,
119119 ( msg , port ) => mcpHandler . handleInstallServer ( msg , port )
120120 )
121-
121+
122122 messageRouter . registerHandler (
123123 MessageType . MCP_DELETE_SERVER ,
124124 ( msg , port ) => mcpHandler . handleDeleteServer ( msg , port )
125125 )
126-
126+
127127 messageRouter . registerHandler (
128128 MessageType . MCP_GET_INSTALLED_SERVERS ,
129129 ( msg , port ) => mcpHandler . handleGetInstalledServers ( msg , port )
130130 )
131-
132-
131+
132+
133133 // Plan generation handlers (for AI plan generation in newtab)
134134 messageRouter . registerHandler (
135135 MessageType . GENERATE_PLAN ,
@@ -215,7 +215,7 @@ function registerHandlers(): void {
215215 Logging . log ( logMsg . source || 'Unknown' , logMsg . message , logMsg . level || 'info' )
216216 }
217217 )
218-
218+
219219 // Metrics handler
220220 messageRouter . registerHandler (
221221 MessageType . LOG_METRIC ,
@@ -224,7 +224,7 @@ function registerHandlers(): void {
224224 Logging . logMetric ( event , properties )
225225 }
226226 )
227-
227+
228228 // Heartbeat handler - acknowledge heartbeats to keep connection alive
229229 messageRouter . registerHandler (
230230 MessageType . HEARTBEAT ,
@@ -237,7 +237,7 @@ function registerHandlers(): void {
237237 } )
238238 }
239239 )
240-
240+
241241 // Panel close handler
242242 messageRouter . registerHandler (
243243 MessageType . CLOSE_PANEL ,
@@ -290,33 +290,33 @@ function registerHandlers(): void {
290290 */
291291function handlePortConnection ( port : chrome . runtime . Port ) : void {
292292 const portId = portManager . registerPort ( port )
293-
293+
294294 // Handle sidepanel connections
295295 if ( port . name === 'sidepanel' ) {
296296 isPanelOpen = true
297297 Logging . log ( 'Background' , `Side panel connected` )
298298 Logging . logMetric ( 'side_panel_opened' , { source : 'port_connection' } )
299299 }
300-
300+
301301 // Register with logging system
302302 Logging . registerPort ( port . name , port )
303-
303+
304304 // Set up message listener
305305 port . onMessage . addListener ( ( message : PortMessage ) => {
306306 messageRouter . routeMessage ( message , port )
307307 } )
308-
308+
309309 // Set up disconnect listener
310310 port . onDisconnect . addListener ( ( ) => {
311311 portManager . unregisterPort ( port )
312-
312+
313313 // Update panel state if this was the sidepanel
314314 if ( port . name === 'sidepanel' ) {
315315 isPanelOpen = false
316316 Logging . log ( 'Background' , `Side panel disconnected` )
317317 Logging . logMetric ( 'side_panel_closed' , { source : 'port_disconnection' } )
318318 }
319-
319+
320320 // Unregister from logging
321321 Logging . unregisterPort ( port . name )
322322 } )
@@ -327,9 +327,9 @@ function handlePortConnection(port: chrome.runtime.Port): void {
327327 */
328328async function toggleSidePanel ( tabId : number ) : Promise < void > {
329329 if ( isPanelToggling ) return
330-
330+
331331 isPanelToggling = true
332-
332+
333333 try {
334334 if ( isPanelOpen ) {
335335 // Signal sidepanel to close itself
@@ -345,7 +345,7 @@ async function toggleSidePanel(tabId: number): Promise<void> {
345345 }
346346 } catch ( error ) {
347347 Logging . log ( 'Background' , `Error toggling side panel: ${ error } ` , 'error' )
348-
348+
349349 // Try fallback with windowId
350350 if ( ! isPanelOpen ) {
351351 try {
@@ -371,36 +371,84 @@ async function toggleSidePanel(tabId: number): Promise<void> {
371371 */
372372function registerNotificationListeners ( ) {
373373
374- // event listener to listen for detecting when browser is opened/resumed
375- chrome . windows . onFocusChanged . addListener ( ( windowId ) => {
374+ // key: notificationId , value: windowId
375+ const windowIds = new Map < string , number > ( ) ;
376376
377- // windowId is not none that means window is focues
378- if ( windowId !== chrome . windows . WINDOW_ID_NONE ) {
377+ // key: windowId , value: notificationId[]
378+ const notificationIds = new Map < number , Array < string > > ( ) ;
379379
380- //clear all notifications because browser is in focus now
381- chrome . notifications . getAll ( ( notifications ) => {
380+ const getNotificationIds = ( windowId : number ) : Array < string > => {
382381
383- Object . keys ( notifications ) . forEach ( id => {
384- chrome . notifications . clear ( id ) ;
385- } )
382+ if ( ! notificationIds . has ( windowId ) ) {
383+ return [ ] ;
384+ }
386385
387- } )
386+ return notificationIds . get ( windowId ) ! ;
387+
388+ }
389+
390+ // event listener to listen for detecting when browser is opened/resumed
391+ chrome . windows . onFocusChanged . addListener ( async ( windowId ) => {
388392
393+ // windowId is not none when all chrome windows
394+ // are out of focus that means no notification needs to be cleared
395+ if ( windowId == chrome . windows . WINDOW_ID_NONE ) {
396+ return ;
389397 }
390-
398+
399+ const data = getNotificationIds ( windowId ) ;
400+
401+ data . forEach ( notificationId => {
402+ chrome . notifications . clear ( notificationId ) ;
403+ } )
404+
405+ notificationIds . delete ( windowId ) ;
406+
391407 } ) ;
392408
393409 //handle click of notification
394- chrome . notifications . onClicked . addListener ( ( noticationId ) => {
410+ chrome . notifications . onClicked . addListener ( ( notificationId ) => {
395411
396412 // clear notification
397- chrome . notifications . clear ( noticationId ) ;
398413
399- chrome . windows . getCurrent ( ( window ) => {
414+ const windowId = windowIds . get ( notificationId ) ;
415+
416+ if ( windowId ) {
417+
400418 //open browser window
401- chrome . windows . update ( window . id ! , { focused : true } ) ;
402- } ) ;
419+ chrome . windows . update ( windowId , { focused : true } ) ;
420+ windowIds . delete ( notificationId ) ;
403421
422+ // Not clearing `notificationId` from `notficationIds` map here becaue
423+ // the above code will open browser window and it will be cleared in the
424+ // `onFocusChange` handler
425+ } else {
426+ console . info ( "window if not found for notification" , notificationId ) ;
427+ }
428+
429+ chrome . notifications . clear ( notificationId ) ;
430+
431+ } ) ;
432+
433+ //listener for sending notification
434+ chrome . runtime . onMessage . addListener ( function ( request , sender , sendResponse ) {
435+ if ( request . action === "send-notification" ) {
436+ const windowId = request . windowId ;
437+
438+ //setting window if against notification so that it can be used to clear notification when the window opens
439+ chrome . notifications . create ( request . options , async ( notificationId ) => {
440+ if ( windowId ) {
441+ let existingNotificationIds = getNotificationIds ( windowId ! ) ;
442+ existingNotificationIds . push ( notificationId ) ;
443+
444+ notificationIds . set ( windowId ! , existingNotificationIds ) ;
445+ windowIds . set ( notificationId , windowId ! ) ;
446+ console . log ( "setting window id" , notificationId , windowId , windowIds ) ;
447+ }
448+ sendResponse ( notificationId ) ;
449+ } ) ;
450+ }
451+ return true ;
404452 } ) ;
405453
406454}
@@ -463,15 +511,15 @@ function initialize(): void {
463511
464512 // Set up port connection listener
465513 chrome . runtime . onConnect . addListener ( handlePortConnection )
466-
514+
467515 // Set up extension icon click handler
468516 chrome . action . onClicked . addListener ( async ( tab ) => {
469517 Logging . log ( 'Background' , 'Extension icon clicked' )
470518 if ( tab . id ) {
471519 await toggleSidePanel ( tab . id )
472520 }
473521 } )
474-
522+
475523 // Set up keyboard shortcut handler
476524 chrome . commands . onCommand . addListener ( async ( command ) => {
477525 if ( command === 'toggle-panel' ) {
@@ -482,24 +530,23 @@ function initialize(): void {
482530 }
483531 }
484532 } )
485-
533+
486534 // Clean up on tab removal
487535 chrome . tabs . onRemoved . addListener ( async ( tabId ) => {
488536 // With singleton execution, just log the tab removal
489537 Logging . log ( 'Background' , `Tab ${ tabId } removed` )
490538 } )
491-
539+
492540 // Handle messages from newtab only
493541 chrome . runtime . onMessage . addListener ( ( message , sender , sendResponse ) => {
494542 if ( message . type === 'NEWTAB_EXECUTE_QUERY' ) {
495543 executionHandler . handleNewtabQuery ( message , sendResponse )
496544 return true // Keep message channel open for async response
497545 }
498546 } )
499-
547+
500548 Logging . log ( 'Background' , 'Nxtscape extension initialized successfully' )
501549}
502550
503551// Initialize the extension
504- initialize ( )
505-
552+ initialize ( )
0 commit comments