@@ -268,6 +268,7 @@ class BookshelfRenderer3D {
268268 this . isDestroyed = false ;
269269 this . _modalBackdropHandler = null ;
270270 this . _escHandler = null ;
271+ this . _escListenerAttached = false ;
271272
272273 // Create live region for screen reader announcements
273274 this . liveRegion = document . createElement ( 'div' ) ;
@@ -1379,6 +1380,7 @@ class BookshelfRenderer3D {
13791380 if ( this . modal ) {
13801381 this . modal . classList . remove ( 'active' ) ;
13811382 document . body . style . overflow = '' ;
1383+ this . removeEscListener ( ) ;
13821384
13831385 // Reset flip after transition
13841386 setTimeout ( ( ) => {
@@ -1393,6 +1395,14 @@ class BookshelfRenderer3D {
13931395 }
13941396 }
13951397
1398+ removeEscListener ( ) {
1399+ if ( this . _escHandler && this . _escListenerAttached ) {
1400+ document . removeEventListener ( 'keydown' , this . _escHandler ) ;
1401+ this . _escListenerAttached = false ;
1402+ this . _escHandler = null ;
1403+ }
1404+ }
1405+
13961406 setupModalHandlers ( ) {
13971407 // Book flip interaction
13981408 const bookObject = document . getElementById ( 'book-3d-object' ) ;
@@ -1443,14 +1453,15 @@ class BookshelfRenderer3D {
14431453 this . addManagedListener ( this . modal , 'click' , this . _modalBackdropHandler ) ;
14441454 }
14451455
1446- // ESC key to close (bind once to avoid listener leaks).
1447- if ( ! this . _escHandler ) {
1456+ // ESC key to close - attach only once, remove on close
1457+ if ( ! this . _escListenerAttached ) {
14481458 this . _escHandler = ( e ) => {
14491459 if ( e . key === 'Escape' && this . modal && this . modal . classList . contains ( 'active' ) ) {
14501460 this . closeModal ( ) ;
14511461 }
14521462 } ;
1453- this . addManagedListener ( document , 'keydown' , this . _escHandler ) ;
1463+ document . addEventListener ( 'keydown' , this . _escHandler ) ;
1464+ this . _escListenerAttached = true ;
14541465 }
14551466
14561467 // Add to library button logic
0 commit comments