@@ -1031,6 +1031,7 @@ <h1 style="margin:0;flex:0 0 auto">SENTINEL NODE TEST</h1>
10311031 < div class ="results-head-wrap ">
10321032 < table class ="results-table ">
10331033 < colgroup >
1034+ < col style ="width:64px "> <!-- SDK -->
10341035 < col style ="width:130px "> <!-- Transport -->
10351036 < col > <!-- Moniker (flex) -->
10361037 < col style ="width:150px "> <!-- Node -->
@@ -1046,6 +1047,7 @@ <h1 style="margin:0;flex:0 0 auto">SENTINEL NODE TEST</h1>
10461047 </ colgroup >
10471048 < thead >
10481049 < tr >
1050+ < th style ="text-align:center "> SDK</ th >
10491051 < th style ="text-align:left "> Transport</ th >
10501052 < th style ="text-align:left "> Moniker</ th >
10511053 < th style ="text-align:left "> Node</ th >
@@ -1065,6 +1067,7 @@ <h1 style="margin:0;flex:0 0 auto">SENTINEL NODE TEST</h1>
10651067 < div class ="table-wrap ">
10661068 < table class ="results-table ">
10671069 < colgroup >
1070+ < col style ="width:64px "> <!-- SDK -->
10681071 < col style ="width:130px "> <!-- Transport -->
10691072 < col > <!-- Moniker (flex) -->
10701073 < col style ="width:150px "> <!-- Node -->
@@ -1997,11 +2000,12 @@ <h1 style="margin:0;flex:0 0 auto">SENTINEL NODE TEST</h1>
19972000 }
19982001 }
19992002
2000- // sdkLabel/sdkColors are retained for the "Other SDKs" cross-reference
2001- // column below. The per-row SDK badge was removed (it lived in a 54px
2002- // column that truncated and duplicated the WG/V2 transport badge).
2003+ // sdkLabel/sdkColors are used by both the per-row SDK column and the
2004+ // "Other SDKs" cross-reference column below. The SDK column renders the
2005+ // short label as plain readable text (no badge background, no truncation ).
20032006 const sdkLabel = r . sdk || state . activeSDK || 'js' ;
20042007 const sdkColors = { js : { bg : 'var(--bg-input)' , fg : 'var(--text)' , label : 'JS' } , csharp : { bg : 'var(--bg-input)' , fg : 'var(--text)' , label : 'C#' } , tkd : { bg : 'var(--bg-input)' , fg : 'var(--text)' , label : 'TKD' } } ;
2008+ const sdkShort = ( sdkColors [ sdkLabel ] || sdkColors . js ) . label ;
20052009
20062010 // ─── Other SDKs column: cross-reference this node against other run results ──
20072011 let otherSdkHtml = '<span style="color:var(--text-muted)">-</span>' ;
@@ -2040,6 +2044,7 @@ <h1 style="margin:0;flex:0 0 auto">SENTINEL NODE TEST</h1>
20402044 : '<span style="color:var(--text-muted)">—</span>' ;
20412045
20422046 tr . innerHTML = `
2047+ <td style="text-align:center;font-size:10px;color:var(--text-dim);white-space:nowrap">${ sdkShort } </td>
20432048 <td>${ proto } ${ transportHtml } </td>
20442049 <td style="font-weight:500">${ escHtml ( monikerText ) } </td>
20452050 <td style="font-family:var(--font-mono);font-size:11px;color:var(--text-secondary)">${ addr } </td>
@@ -2401,9 +2406,9 @@ <h1 style="margin:0;flex:0 0 auto">SENTINEL NODE TEST</h1>
24012406 }
24022407
24032408 async function openP2pSettingsDrawer ( ) {
2404- // Tear down any previous drawer
2409+ // Tear down any previous drawer (and its leaked keydown listener)
24052410 const existing = document . getElementById ( 'p2pSettingsBackdrop' ) ;
2406- if ( existing ) existing . remove ( ) ;
2411+ if ( existing ) { if ( existing . _onEsc ) document . removeEventListener ( 'keydown' , existing . _onEsc ) ; existing . remove ( ) ; }
24072412
24082413 let payload = { settings : { } , defaults : { } } ;
24092414 try {
@@ -2420,7 +2425,12 @@ <h1 style="margin:0;flex:0 0 auto">SENTINEL NODE TEST</h1>
24202425 const panel = document . createElement ( 'div' ) ;
24212426 panel . style . cssText = 'background:var(--bg-card-solid);border:1px solid var(--border-hover);border-radius:12px;padding:20px;width:min(520px,92vw);max-height:88vh;overflow:auto;color:var(--text);font-family:var(--font-display)' ;
24222427
2423- const close = ( ) => backdrop . remove ( ) ;
2428+ const close = ( ) => { document . removeEventListener ( 'keydown' , onEsc ) ; backdrop . remove ( ) ; } ;
2429+ // Esc closes THIS drawer (the top-most popup). stopImmediatePropagation so the
2430+ // underlying chooser's own keydown listener doesn't also fire on the same Esc.
2431+ const onEsc = ( e ) => { if ( e . key === 'Escape' ) { e . stopImmediatePropagation ( ) ; close ( ) ; } } ;
2432+ backdrop . _onEsc = onEsc ;
2433+ document . addEventListener ( 'keydown' , onEsc ) ;
24242434 const _esc = ( v ) => String ( v ) . replace ( / [ & < > " ' ] / g, c => ( { '&' :'&' , '<' :'<' , '>' :'>' , '"' :'"' , "'" :''' } [ c ] ) ) ;
24252435
24262436 panel . innerHTML = `
@@ -2851,9 +2861,15 @@ <h3 style="font-size:10px;font-weight:700;letter-spacing:1.4px;text-transform:up
28512861 const modal = document . createElement ( 'div' ) ;
28522862 modal . style . cssText = 'background:var(--bg-card-solid);border:1px solid var(--border-hover);border-radius:10px;padding:28px;max-width:920px;width:92%;max-height:90vh;overflow:auto;color:var(--text);font-family:var(--font-display);' ;
28532863
2854- const close = ( ) => backdrop . remove ( ) ;
2864+ // close() always removes onKey so the listener never leaks on ANY close path
2865+ // (×, backdrop click, tile click, Esc). A leaked onKey would otherwise linger
2866+ // on document and — via the bail below — silently swallow a later Escape.
2867+ const close = ( ) => { document . removeEventListener ( 'keydown' , onKey ) ; backdrop . remove ( ) ; } ;
28552868 backdrop . addEventListener ( 'click' , ( e ) => { if ( e . target === backdrop ) close ( ) ; } ) ;
2856- const onKey = ( e ) => { if ( e . key === 'Escape' ) { close ( ) ; document . removeEventListener ( 'keydown' , onKey ) ; } } ;
2869+ // Bail if the P2P-settings drawer is open on top of us: its own Esc handler
2870+ // closes that drawer first. This listener is registered before the drawer's,
2871+ // so it fires first — returning here leaves the top-most popup to be closed.
2872+ const onKey = ( e ) => { if ( e . key === 'Escape' ) { if ( document . getElementById ( 'p2pSettingsBackdrop' ) ) return ; close ( ) ; } } ;
28572873 document . addEventListener ( 'keydown' , onKey ) ;
28582874
28592875 modal . innerHTML = `
0 commit comments