@@ -187,17 +187,24 @@ export class Profiler {
187187 this . toggleButton = document . createElement ( 'button' ) ;
188188 this . toggleButton . id = 'profiler-toggle' ;
189189 this . toggleButton . innerHTML = `
190+ <span id="builtin-tabs-container"></span>
190191<span id="toggle-text">
191192 <span id="fps-counter">-</span>
192193 <span class="fps-label">FPS</span>
193194</span>
194- <!-- <span class="toggle-separator"></span> -->
195195<span id="toggle-icon">
196196 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-device-ipad-horizontal-search"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11.5 20h-6.5a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v5.5" /><path d="M9 17h2" /><path d="M18 18m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0" /><path d="M20.2 20.2l1.8 1.8" /></svg>
197197</span>
198198` ;
199199 this . toggleButton . onclick = ( ) => this . togglePanel ( ) ;
200200
201+ this . builtinTabsContainer = this . toggleButton . querySelector ( '#builtin-tabs-container' ) ;
202+
203+ // Create mini-panel for builtin tabs (shown when panel is hidden)
204+ this . miniPanel = document . createElement ( 'div' ) ;
205+ this . miniPanel . id = 'profiler-mini-panel' ;
206+ this . miniPanel . className = 'profiler-mini-panel' ;
207+
201208 this . panel = document . createElement ( 'div' ) ;
202209 this . panel . id = 'profiler-panel' ;
203210
@@ -244,7 +251,7 @@ export class Profiler {
244251
245252 this . panel . append ( resizer , header , this . contentWrapper ) ;
246253
247- this . domElement . append ( this . toggleButton , this . panel ) ;
254+ this . domElement . append ( this . toggleButton , this . miniPanel , this . panel ) ;
248255
249256 // Set initial position class
250257 this . panel . classList . add ( `position-${ this . position } ` ) ;
@@ -412,11 +419,174 @@ export class Profiler {
412419 this . tabsContainer . appendChild ( tab . button ) ;
413420 this . contentWrapper . appendChild ( tab . content ) ;
414421
422+ // Apply the current visibility state to the DOM elements
423+ if ( ! tab . isVisible ) {
424+
425+ tab . button . style . display = 'none' ;
426+ tab . content . style . display = 'none' ;
427+
428+ }
429+
430+ // If tab is builtin, add it to the profiler-toggle button
431+ if ( tab . builtin ) {
432+
433+ this . addBuiltinTab ( tab ) ;
434+
435+ }
436+
415437 // Update panel size when tabs change
416438 this . updatePanelSize ( ) ;
417439
418440 }
419441
442+ addBuiltinTab ( tab ) {
443+
444+ // Create a button for the builtin tab in the profiler-toggle
445+ const builtinButton = document . createElement ( 'button' ) ;
446+ builtinButton . className = 'builtin-tab-btn' ;
447+
448+ // Use icon if provided, otherwise use first letter
449+ if ( tab . icon ) {
450+
451+ builtinButton . innerHTML = tab . icon ;
452+
453+ } else {
454+
455+ builtinButton . textContent = tab . button . textContent . charAt ( 0 ) . toUpperCase ( ) ;
456+
457+ }
458+
459+ builtinButton . title = tab . button . textContent ;
460+
461+ // Create mini-panel content container for this tab
462+ const miniContent = document . createElement ( 'div' ) ;
463+ miniContent . className = 'mini-panel-content' ;
464+ miniContent . style . display = 'none' ;
465+
466+ // Store references in the tab object
467+ tab . builtinButton = builtinButton ;
468+ tab . miniContent = miniContent ;
469+
470+ this . miniPanel . appendChild ( miniContent ) ;
471+
472+ builtinButton . onclick = ( e ) => {
473+
474+ e . stopPropagation ( ) ; // Prevent toggle panel from triggering
475+
476+ const isPanelVisible = this . panel . classList . contains ( 'visible' ) ;
477+
478+ if ( isPanelVisible ) {
479+
480+ // Panel is visible - navigate to tab
481+ if ( ! tab . isVisible ) {
482+
483+ tab . show ( ) ;
484+
485+ }
486+
487+ if ( tab . isDetached ) {
488+
489+ // If tab is detached, just bring its window to front
490+ if ( tab . detachedWindow ) {
491+
492+ this . bringWindowToFront ( tab . detachedWindow . panel ) ;
493+
494+ }
495+
496+ } else {
497+
498+ // Activate the tab
499+ this . setActiveTab ( tab . id ) ;
500+
501+ }
502+
503+ } else {
504+
505+ // Panel is hidden - toggle mini-panel for this tab
506+ const isCurrentlyActive = miniContent . style . display !== 'none' && miniContent . children . length > 0 ;
507+
508+ // Hide all other mini-panel contents
509+ this . miniPanel . querySelectorAll ( '.mini-panel-content' ) . forEach ( content => {
510+
511+ content . style . display = 'none' ;
512+
513+ } ) ;
514+
515+ // Remove active state from all builtin buttons
516+ this . builtinTabsContainer . querySelectorAll ( '.builtin-tab-btn' ) . forEach ( btn => {
517+
518+ btn . classList . remove ( 'active' ) ;
519+
520+ } ) ;
521+
522+ if ( isCurrentlyActive ) {
523+
524+ // Toggle off - hide mini-panel and move content back
525+ this . miniPanel . classList . remove ( 'visible' ) ;
526+ miniContent . style . display = 'none' ;
527+
528+ // Move content back to main panel
529+ if ( miniContent . firstChild ) {
530+
531+ tab . content . appendChild ( miniContent . firstChild ) ;
532+
533+ }
534+
535+ } else {
536+
537+ // Toggle on - show mini-panel with this tab's content
538+ builtinButton . classList . add ( 'active' ) ;
539+
540+ // Move actual content to mini-panel (not clone) if not already there
541+ if ( ! miniContent . firstChild ) {
542+
543+ const actualContent = tab . content . querySelector ( '.list-scroll-wrapper' ) || tab . content . firstElementChild ;
544+
545+ if ( actualContent ) {
546+
547+ miniContent . appendChild ( actualContent ) ;
548+
549+ }
550+
551+ }
552+
553+ // Show after content is moved
554+ miniContent . style . display = 'block' ;
555+ this . miniPanel . classList . add ( 'visible' ) ;
556+
557+ }
558+
559+ }
560+
561+ } ;
562+
563+ this . builtinTabsContainer . appendChild ( builtinButton ) ;
564+
565+ // Store references
566+ tab . builtinButton = builtinButton ;
567+ tab . miniContent = miniContent ;
568+ tab . profiler = this ;
569+
570+ // If the tab was hidden before being added, hide the builtin button
571+ if ( ! tab . isVisible ) {
572+
573+ builtinButton . style . display = 'none' ;
574+ miniContent . style . display = 'none' ;
575+
576+ // Hide the builtin-tabs-container if all builtin buttons are hidden
577+ const hasVisibleBuiltinButtons = Array . from ( this . builtinTabsContainer . querySelectorAll ( '.builtin-tab-btn' ) )
578+ . some ( btn => btn . style . display !== 'none' ) ;
579+
580+ if ( ! hasVisibleBuiltinButtons ) {
581+
582+ this . builtinTabsContainer . style . display = 'none' ;
583+
584+ }
585+
586+ }
587+
588+ }
589+
420590 updatePanelSize ( ) {
421591
422592 // Check if there are any visible tabs in the panel
@@ -1305,6 +1475,83 @@ export class Profiler {
13051475
13061476 const isVisible = this . panel . classList . contains ( 'visible' ) ;
13071477
1478+ if ( isVisible ) {
1479+
1480+ // Save mini-panel state before hiding
1481+ this . savedMiniPanelState = {
1482+ isVisible : this . miniPanel . classList . contains ( 'visible' ) ,
1483+ activeTabId : null ,
1484+ contentMap : { }
1485+ } ;
1486+
1487+ // Find which tab was active in mini-panel
1488+ this . miniPanel . querySelectorAll ( '.mini-panel-content' ) . forEach ( content => {
1489+
1490+ if ( content . style . display !== 'none' && content . firstChild ) {
1491+
1492+ // Find the tab that owns this content
1493+ Object . values ( this . tabs ) . forEach ( tab => {
1494+
1495+ if ( tab . miniContent === content ) {
1496+
1497+ this . savedMiniPanelState . activeTabId = tab . id ;
1498+ // Move content back to main panel
1499+ tab . content . appendChild ( content . firstChild ) ;
1500+
1501+ }
1502+
1503+ } ) ;
1504+
1505+ }
1506+
1507+ } ) ;
1508+
1509+ // Hide mini-panel temporarily
1510+ this . miniPanel . classList . remove ( 'visible' ) ;
1511+
1512+ // Hide all mini-panel contents
1513+ this . miniPanel . querySelectorAll ( '.mini-panel-content' ) . forEach ( content => {
1514+
1515+ content . style . display = 'none' ;
1516+
1517+ } ) ;
1518+
1519+ // Remove active state from builtin buttons
1520+ this . builtinTabsContainer . querySelectorAll ( '.builtin-tab-btn' ) . forEach ( btn => {
1521+
1522+ btn . classList . remove ( 'active' ) ;
1523+
1524+ } ) ;
1525+
1526+ } else {
1527+
1528+ // Restore mini-panel state when minimizing
1529+ if ( this . savedMiniPanelState && this . savedMiniPanelState . isVisible && this . savedMiniPanelState . activeTabId ) {
1530+
1531+ const tab = this . tabs [ this . savedMiniPanelState . activeTabId ] ;
1532+
1533+ if ( tab && tab . miniContent && tab . builtinButton ) {
1534+
1535+ // Restore mini-panel visibility
1536+ this . miniPanel . classList . add ( 'visible' ) ;
1537+ tab . miniContent . style . display = 'block' ;
1538+ tab . builtinButton . classList . add ( 'active' ) ;
1539+
1540+ // Move content back to mini-panel
1541+ const actualContent = tab . content . querySelector ( '.list-scroll-wrapper, .profiler-content > *' ) ;
1542+
1543+ if ( actualContent ) {
1544+
1545+ tab . miniContent . appendChild ( actualContent ) ;
1546+
1547+ }
1548+
1549+ }
1550+
1551+ }
1552+
1553+ }
1554+
13081555 this . detachedWindows . forEach ( detachedWindow => {
13091556
13101557 if ( isVisible ) {
0 commit comments