|
|
@@ -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 );
|