From f274d6ad5eb7594a563884ca05239e8f5d644ce4 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 26 Feb 2026 12:20:59 -0300 Subject: [PATCH] Inspector: Use fixed mini-panel (#33070) --- examples/jsm/inspector/Inspector.js | 6 - examples/jsm/inspector/ui/Profiler.js | 210 ++++++++------------------ examples/jsm/inspector/ui/Style.js | 27 +++- 3 files changed, 88 insertions(+), 155 deletions(-) diff --git a/examples/jsm/inspector/Inspector.js b/examples/jsm/inspector/Inspector.js index e31a7c1c97a5ee..7ee58798dbb74e 100644 --- a/examples/jsm/inspector/Inspector.js +++ b/examples/jsm/inspector/Inspector.js @@ -224,12 +224,6 @@ class Inspector extends RendererInspector { this.parameters.show(); - if ( this.parameters.isDetached === false ) { - - this.profiler.setActiveTab( this.parameters.id ); - - } - } return this.parameters.createGroup( name ); diff --git a/examples/jsm/inspector/ui/Profiler.js b/examples/jsm/inspector/ui/Profiler.js index 7df6fb45f4dc1b..bdcc80e47cb810 100644 --- a/examples/jsm/inspector/ui/Profiler.js +++ b/examples/jsm/inspector/ui/Profiler.js @@ -256,6 +256,13 @@ export class Profiler { // Set initial position class this.panel.classList.add( `position-${this.position}` ); + if ( this.position === 'right' ) { + + this.toggleButton.classList.add( 'position-right' ); + this.miniPanel.classList.add( 'position-right' ); + + } + } setupResizing() { @@ -416,7 +423,12 @@ export class Profiler { this.setupTabDragAndDrop( tab ); - this.tabsContainer.appendChild( tab.button ); + if ( ! tab.builtin ) { + + this.tabsContainer.appendChild( tab.button ); + + } + this.contentWrapper.appendChild( tab.content ); // Apply the current visibility state to the DOM elements @@ -473,89 +485,51 @@ export class Profiler { e.stopPropagation(); // Prevent toggle panel from triggering - const isPanelVisible = this.panel.classList.contains( 'visible' ); - - if ( isPanelVisible ) { + // Toggle mini-panel for this tab + const isCurrentlyActive = miniContent.style.display !== 'none' && miniContent.children.length > 0; - // Panel is visible - navigate to tab - if ( ! tab.isVisible ) { - - tab.show(); - - } + // Hide all other mini-panel contents + this.miniPanel.querySelectorAll( '.mini-panel-content' ).forEach( content => { - if ( tab.isDetached ) { + content.style.display = 'none'; - // If tab is detached, just bring its window to front - if ( tab.detachedWindow ) { + } ); - this.bringWindowToFront( tab.detachedWindow.panel ); + // Remove active state from all builtin buttons + this.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ).forEach( btn => { - } + btn.classList.remove( 'active' ); - } else { + } ); - // Activate the tab - this.setActiveTab( tab.id ); + if ( isCurrentlyActive ) { - } + // Toggle off - hide mini-panel + this.miniPanel.classList.remove( 'visible' ); + miniContent.style.display = 'none'; } else { - // Panel is hidden - toggle mini-panel for this tab - const isCurrentlyActive = miniContent.style.display !== 'none' && miniContent.children.length > 0; + // Toggle on - show mini-panel with this tab's content + builtinButton.classList.add( 'active' ); - // Hide all other mini-panel contents - this.miniPanel.querySelectorAll( '.mini-panel-content' ).forEach( content => { - - content.style.display = 'none'; - - } ); + // Move actual content to mini-panel (not clone) if not already there + if ( ! miniContent.firstChild ) { - // Remove active state from all builtin buttons - this.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ).forEach( btn => { + const actualContent = tab.content.querySelector( '.list-scroll-wrapper' ) || tab.content.firstElementChild; - btn.classList.remove( 'active' ); - - } ); - - if ( isCurrentlyActive ) { - - // Toggle off - hide mini-panel and move content back - this.miniPanel.classList.remove( 'visible' ); - miniContent.style.display = 'none'; - - // Move content back to main panel - if ( miniContent.firstChild ) { - - tab.content.appendChild( miniContent.firstChild ); - - } - - } else { - - // Toggle on - show mini-panel with this tab's content - builtinButton.classList.add( 'active' ); - - // Move actual content to mini-panel (not clone) if not already there - if ( ! miniContent.firstChild ) { - - const actualContent = tab.content.querySelector( '.list-scroll-wrapper' ) || tab.content.firstElementChild; - - if ( actualContent ) { - - miniContent.appendChild( actualContent ); + if ( actualContent ) { - } + miniContent.appendChild( actualContent ); } - // Show after content is moved - miniContent.style.display = 'block'; - this.miniPanel.classList.add( 'visible' ); - } + // Show after content is moved + miniContent.style.display = 'block'; + this.miniPanel.classList.add( 'visible' ); + } }; @@ -1471,87 +1445,11 @@ export class Profiler { togglePanel() { this.panel.classList.toggle( 'visible' ); - this.toggleButton.classList.toggle( 'hidden' ); + this.toggleButton.classList.toggle( 'panel-open' ); + this.miniPanel.classList.toggle( 'panel-open' ); const isVisible = this.panel.classList.contains( 'visible' ); - if ( isVisible ) { - - // Save mini-panel state before hiding - this.savedMiniPanelState = { - isVisible: this.miniPanel.classList.contains( 'visible' ), - activeTabId: null, - contentMap: {} - }; - - // Find which tab was active in mini-panel - this.miniPanel.querySelectorAll( '.mini-panel-content' ).forEach( content => { - - if ( content.style.display !== 'none' && content.firstChild ) { - - // Find the tab that owns this content - Object.values( this.tabs ).forEach( tab => { - - if ( tab.miniContent === content ) { - - this.savedMiniPanelState.activeTabId = tab.id; - // Move content back to main panel - tab.content.appendChild( content.firstChild ); - - } - - } ); - - } - - } ); - - // Hide mini-panel temporarily - this.miniPanel.classList.remove( 'visible' ); - - // Hide all mini-panel contents - this.miniPanel.querySelectorAll( '.mini-panel-content' ).forEach( content => { - - content.style.display = 'none'; - - } ); - - // Remove active state from builtin buttons - this.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ).forEach( btn => { - - btn.classList.remove( 'active' ); - - } ); - - } else { - - // Restore mini-panel state when minimizing - if ( this.savedMiniPanelState && this.savedMiniPanelState.isVisible && this.savedMiniPanelState.activeTabId ) { - - const tab = this.tabs[ this.savedMiniPanelState.activeTabId ]; - - if ( tab && tab.miniContent && tab.builtinButton ) { - - // Restore mini-panel visibility - this.miniPanel.classList.add( 'visible' ); - tab.miniContent.style.display = 'block'; - tab.builtinButton.classList.add( 'active' ); - - // Move content back to mini-panel - const actualContent = tab.content.querySelector( '.list-scroll-wrapper, .profiler-content > *' ); - - if ( actualContent ) { - - tab.miniContent.appendChild( actualContent ); - - } - - } - - } - - } - this.detachedWindows.forEach( detachedWindow => { if ( isVisible ) { @@ -1598,6 +1496,8 @@ export class Profiler { // Apply right position styles this.panel.classList.remove( 'position-bottom' ); this.panel.classList.add( 'position-right' ); + this.toggleButton.classList.add( 'position-right' ); + this.miniPanel.classList.add( 'position-right' ); this.panel.style.bottom = ''; this.panel.style.top = '0'; this.panel.style.right = '0'; @@ -1626,6 +1526,8 @@ export class Profiler { // Apply bottom position styles this.panel.classList.remove( 'position-right' ); this.panel.classList.add( 'position-bottom' ); + this.toggleButton.classList.remove( 'position-right' ); + this.miniPanel.classList.remove( 'position-right' ); this.panel.style.top = ''; this.panel.style.right = ''; this.panel.style.bottom = '0'; @@ -1696,7 +1598,11 @@ export class Profiler { try { - localStorage.setItem( 'profiler-layout', JSON.stringify( layout ) ); + const savedData = localStorage.getItem( 'threejs-inspector' ); + const data = JSON.parse( savedData || '{}' ); + + data.layout = layout; + localStorage.setItem( 'threejs-inspector', JSON.stringify( data ) ); } catch ( e ) { @@ -1710,11 +1616,14 @@ export class Profiler { try { - const savedLayout = localStorage.getItem( 'profiler-layout' ); + const savedData = localStorage.getItem( 'threejs-inspector' ); + + if ( ! savedData ) return; - if ( ! savedLayout ) return; + const parsedData = JSON.parse( savedData ); + const layout = parsedData.layout; - const layout = JSON.parse( savedLayout ); + if ( ! layout ) return; // Constrain detached tabs positions to current screen bounds if ( layout.detachedTabs && layout.detachedTabs.length > 0 ) { @@ -1827,6 +1736,8 @@ export class Profiler { this.panel.classList.remove( 'position-bottom' ); this.panel.classList.add( 'position-right' ); + this.toggleButton.classList.add( 'position-right' ); + this.miniPanel.classList.add( 'position-right' ); this.panel.style.bottom = ''; this.panel.style.top = '0'; this.panel.style.right = '0'; @@ -1863,6 +1774,13 @@ export class Profiler { // Update panel size after loading layout this.updatePanelSize(); + // Ensure initial open state applies to mini panel as well + if ( this.panel.classList.contains( 'visible' ) ) { + + this.miniPanel.classList.add( 'panel-open' ); + + } + } catch ( e ) { console.warn( 'Failed to load profiler layout:', e ); diff --git a/examples/jsm/inspector/ui/Style.js b/examples/jsm/inspector/ui/Style.js index ebb66efcf724a4..bd256ab7c3dbbd 100644 --- a/examples/jsm/inspector/ui/Style.js +++ b/examples/jsm/inspector/ui/Style.js @@ -52,13 +52,25 @@ export class Style { font-family: var(--font-family); } +#profiler-toggle.position-right.panel-open { + right: auto; + left: 15px; + border-radius: 6px 12px 12px 6px; + flex-direction: row-reverse; +} + +#profiler-toggle.position-right.panel-open #builtin-tabs-container { + border-right: none; + border-left: 1px solid #262636; +} + #profiler-toggle:hover { border-color: var(--accent-color); } -#profiler-toggle.hidden { - opacity: 0; - pointer-events: none; +#profiler-toggle.panel-open #toggle-icon { + background-color: rgba(0, 170, 255, 0.2); + color: var(--accent-color); } #toggle-icon { @@ -74,6 +86,10 @@ export class Style { background-color: rgba(255, 255, 255, 0.05); } +#profiler-toggle.panel-open:hover #toggle-icon { + background-color: rgba(0, 170, 255, 0.3); +} + .toggle-separator { width: 1px; background-color: var(--profiler-border); @@ -166,6 +182,11 @@ export class Style { transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); } +#profiler-mini-panel.position-right.panel-open { + right: auto; + left: 15px; +} + #profiler-mini-panel.visible { display: block; opacity: 1;