@@ -151,6 +151,10 @@ function createBridgeAndStore() {
151151    supportsClickToInspect : true , 
152152  } ) ; 
153153
154+   store . addListener ( 'enableSuspenseTab' ,  ( )  =>  { 
155+     createSuspensePanel ( ) ; 
156+   } ) ; 
157+ 
154158  store . addListener ( 'settingsUpdated' ,  settings  =>  { 
155159    chrome . storage . local . set ( settings ) ; 
156160  } ) ; 
@@ -209,6 +213,7 @@ function createBridgeAndStore() {
209213        overrideTab, 
210214        showTabBar : false , 
211215        store, 
216+         suspensePortalContainer, 
212217        warnIfUnsupportedVersionDetected : true , 
213218        viewAttributeSourceFunction, 
214219        // Firefox doesn't support chrome.devtools.panels.openResource yet 
@@ -354,6 +359,42 @@ function createSourcesEditorPanel() {
354359  } ) ; 
355360} 
356361
362+ function  createSuspensePanel ( )  { 
363+   if  ( suspensePortalContainer )  { 
364+     // Panel is created and user opened it at least once 
365+     ensureInitialHTMLIsCleared ( suspensePortalContainer ) ; 
366+     render ( 'suspense' ) ; 
367+ 
368+     return ; 
369+   } 
370+ 
371+   if  ( suspensePanel )  { 
372+     // Panel is created, but wasn't opened yet, so no document is present for it 
373+     return ; 
374+   } 
375+ 
376+   chrome . devtools . panels . create ( 
377+     __IS_CHROME__  ||  __IS_EDGE__  ? 'Suspense ⚛'  : 'Suspense' , 
378+     __IS_EDGE__  ? 'icons/production.svg'  : '' , 
379+     'panel.html' , 
380+     createdPanel  =>  { 
381+       suspensePanel  =  createdPanel ; 
382+ 
383+       createdPanel . onShown . addListener ( portal  =>  { 
384+         suspensePortalContainer  =  portal . container ; 
385+         if  ( suspensePortalContainer  !=  null  &&  render )  { 
386+           ensureInitialHTMLIsCleared ( suspensePortalContainer ) ; 
387+ 
388+           render ( 'suspense' ) ; 
389+           portal . injectStyles ( cloneStyleTags ) ; 
390+ 
391+           logEvent ( { event_name : 'selected-suspense-tab' } ) ; 
392+         } 
393+       } ) ; 
394+     } , 
395+   ) ; 
396+ } 
397+ 
357398function  performInTabNavigationCleanup ( )  { 
358399  // Potentially, if react hasn't loaded yet and user performs in-tab navigation 
359400  clearReactPollingInstance ( ) ; 
@@ -365,7 +406,12 @@ function performInTabNavigationCleanup() {
365406
366407  // If panels were already created, and we have already mounted React root to display 
367408  // tabs (Components or Profiler), we should unmount root first and render them again 
368-   if  ( ( componentsPortalContainer  ||  profilerPortalContainer )  &&  root )  { 
409+   if  ( 
410+     ( componentsPortalContainer  || 
411+       profilerPortalContainer  || 
412+       suspensePortalContainer )  && 
413+     root 
414+   )  { 
369415    // It's easiest to recreate the DevTools panel (to clean up potential stale state). 
370416    // We can revisit this in the future as a small optimization. 
371417    // This should also emit bridge.shutdown, but only if this root was mounted 
@@ -395,7 +441,12 @@ function performFullCleanup() {
395441  // Potentially, if react hasn't loaded yet and user closed the browser DevTools 
396442  clearReactPollingInstance ( ) ; 
397443
398-   if  ( ( componentsPortalContainer  ||  profilerPortalContainer )  &&  root )  { 
444+   if  ( 
445+     ( componentsPortalContainer  || 
446+       profilerPortalContainer  || 
447+       suspensePortalContainer )  && 
448+     root 
449+   )  { 
399450    // This should also emit bridge.shutdown, but only if this root was mounted 
400451    flushSync ( ( )  =>  root . unmount ( ) ) ; 
401452  }  else  { 
@@ -404,6 +455,7 @@ function performFullCleanup() {
404455
405456  componentsPortalContainer  =  null ; 
406457  profilerPortalContainer  =  null ; 
458+   suspensePortalContainer  =  null ; 
407459  root  =  null ; 
408460
409461  mostRecentOverrideTab  =  null ; 
@@ -454,6 +506,8 @@ function mountReactDevTools() {
454506  createComponentsPanel ( ) ; 
455507  createProfilerPanel ( ) ; 
456508  createSourcesEditorPanel ( ) ; 
509+   // Suspense Tab is created via the hook 
510+   // TODO(enableSuspenseTab): Create eagerly once Suspense tab is stable 
457511} 
458512
459513let  reactPollingInstance  =  null ; 
@@ -474,6 +528,12 @@ function showNoReactDisclaimer() {
474528      '<h1 class="no-react-disclaimer">Looks like this page doesn\'t have React, or it hasn\'t been loaded yet.</h1>' ; 
475529    delete  profilerPortalContainer . _hasInitialHTMLBeenCleared ; 
476530  } 
531+ 
532+   if  ( suspensePortalContainer )  { 
533+     suspensePortalContainer . innerHTML  = 
534+       '<h1 class="no-react-disclaimer">Looks like this page doesn\'t have React, or it hasn\'t been loaded yet.</h1>' ; 
535+     delete  suspensePortalContainer . _hasInitialHTMLBeenCleared ; 
536+   } 
477537} 
478538
479539function  mountReactDevToolsWhenReactHasLoaded ( )  { 
@@ -492,9 +552,11 @@ let profilingData = null;
492552
493553let  componentsPanel  =  null ; 
494554let  profilerPanel  =  null ; 
555+ let  suspensePanel  =  null ; 
495556let  editorPane  =  null ; 
496557let  componentsPortalContainer  =  null ; 
497558let  profilerPortalContainer  =  null ; 
559+ let  suspensePortalContainer  =  null ; 
498560let  editorPortalContainer  =  null ; 
499561
500562let  mostRecentOverrideTab  =  null ; 
0 commit comments