Browse Source

Inspector: Mini-panel for parameters (#32257)

sunag 1 month ago
parent
commit
0757803b25

+ 4 - 1
examples/jsm/inspector/Inspector.js

@@ -42,7 +42,10 @@ class Inspector extends RendererInspector {
 
 		const profiler = new Profiler();
 
-		const parameters = new Parameters();
+		const parameters = new Parameters( {
+			builtin: true,
+			icon: '<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"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M4 6l8 0" /><path d="M16 6l4 0" /><path d="M8 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M4 12l2 0" /><path d="M10 12l10 0" /><path d="M17 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M4 18l11 0" /><path d="M19 18l1 0" /></svg>'
+		} );
 		parameters.hide();
 		profiler.addTab( parameters );
 

+ 249 - 2
examples/jsm/inspector/ui/Profiler.js

@@ -187,17 +187,24 @@ export class Profiler {
 		this.toggleButton = document.createElement( 'button' );
 		this.toggleButton.id = 'profiler-toggle';
 		this.toggleButton.innerHTML = `
+<span id="builtin-tabs-container"></span>
 <span id="toggle-text">
 	<span id="fps-counter">-</span>
 	<span class="fps-label">FPS</span>
 </span>
-<!-- <span class="toggle-separator"></span> -->
 <span id="toggle-icon">
 	<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>
 </span>
 `;
 		this.toggleButton.onclick = () => this.togglePanel();
 
+		this.builtinTabsContainer = this.toggleButton.querySelector( '#builtin-tabs-container' );
+
+		// Create mini-panel for builtin tabs (shown when panel is hidden)
+		this.miniPanel = document.createElement( 'div' );
+		this.miniPanel.id = 'profiler-mini-panel';
+		this.miniPanel.className = 'profiler-mini-panel';
+
 		this.panel = document.createElement( 'div' );
 		this.panel.id = 'profiler-panel';
 
@@ -244,7 +251,7 @@ export class Profiler {
 
 		this.panel.append( resizer, header, this.contentWrapper );
 
-		this.domElement.append( this.toggleButton, this.panel );
+		this.domElement.append( this.toggleButton, this.miniPanel, this.panel );
 
 		// Set initial position class
 		this.panel.classList.add( `position-${this.position}` );
@@ -412,11 +419,174 @@ export class Profiler {
 		this.tabsContainer.appendChild( tab.button );
 		this.contentWrapper.appendChild( tab.content );
 
+		// Apply the current visibility state to the DOM elements
+		if ( ! tab.isVisible ) {
+
+			tab.button.style.display = 'none';
+			tab.content.style.display = 'none';
+
+		}
+
+		// If tab is builtin, add it to the profiler-toggle button
+		if ( tab.builtin ) {
+
+			this.addBuiltinTab( tab );
+
+		}
+
 		// Update panel size when tabs change
 		this.updatePanelSize();
 
 	}
 
+	addBuiltinTab( tab ) {
+
+		// Create a button for the builtin tab in the profiler-toggle
+		const builtinButton = document.createElement( 'button' );
+		builtinButton.className = 'builtin-tab-btn';
+		
+		// Use icon if provided, otherwise use first letter
+		if ( tab.icon ) {
+
+			builtinButton.innerHTML = tab.icon;
+
+		} else {
+
+			builtinButton.textContent = tab.button.textContent.charAt( 0 ).toUpperCase();
+
+		}
+
+		builtinButton.title = tab.button.textContent;
+
+		// Create mini-panel content container for this tab
+		const miniContent = document.createElement( 'div' );
+		miniContent.className = 'mini-panel-content';
+		miniContent.style.display = 'none';
+
+		// Store references in the tab object
+		tab.builtinButton = builtinButton;
+		tab.miniContent = miniContent;
+
+		this.miniPanel.appendChild( miniContent );
+
+		builtinButton.onclick = ( e ) => {
+
+			e.stopPropagation(); // Prevent toggle panel from triggering
+
+			const isPanelVisible = this.panel.classList.contains( 'visible' );
+
+			if ( isPanelVisible ) {
+
+				// Panel is visible - navigate to tab
+				if ( ! tab.isVisible ) {
+
+					tab.show();
+
+				}
+
+				if ( tab.isDetached ) {
+
+					// If tab is detached, just bring its window to front
+					if ( tab.detachedWindow ) {
+
+						this.bringWindowToFront( tab.detachedWindow.panel );
+
+					}
+
+				} else {
+
+					// Activate the tab
+					this.setActiveTab( tab.id );
+
+				}
+
+			} else {
+
+				// Panel is hidden - toggle mini-panel for this tab
+				const isCurrentlyActive = miniContent.style.display !== 'none' && miniContent.children.length > 0;
+
+				// Hide all other mini-panel contents
+				this.miniPanel.querySelectorAll( '.mini-panel-content' ).forEach( content => {
+
+					content.style.display = 'none';
+
+				} );
+
+				// Remove active state from all builtin buttons
+				this.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ).forEach( btn => {
+
+					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 );
+
+						}
+
+					}
+
+					// Show after content is moved
+					miniContent.style.display = 'block';
+					this.miniPanel.classList.add( 'visible' );
+
+				}
+
+			}
+
+		};
+
+		this.builtinTabsContainer.appendChild( builtinButton );
+
+		// Store references
+		tab.builtinButton = builtinButton;
+		tab.miniContent = miniContent;
+		tab.profiler = this;
+
+		// If the tab was hidden before being added, hide the builtin button
+		if ( ! tab.isVisible ) {
+
+			builtinButton.style.display = 'none';
+			miniContent.style.display = 'none';
+
+			// Hide the builtin-tabs-container if all builtin buttons are hidden
+			const hasVisibleBuiltinButtons = Array.from( this.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ) )
+				.some( btn => btn.style.display !== 'none' );
+
+			if ( ! hasVisibleBuiltinButtons ) {
+
+				this.builtinTabsContainer.style.display = 'none';
+
+			}
+
+		}
+
+	}
+
 	updatePanelSize() {
 
 		// Check if there are any visible tabs in the panel
@@ -1305,6 +1475,83 @@ export class Profiler {
 
 		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 ) {

+ 346 - 3
examples/jsm/inspector/ui/Style.js

@@ -36,12 +36,13 @@ export class Style {
 	right: 15px;
 	background-color: rgba(30, 30, 36, 0.85);
 	border: 1px solid #4a4a5a54;
-	border-radius: 6px 12px 12px 6px;
+	border-radius: 12px 6px 6px 12px;
 	color: var(--text-primary);
 	cursor: pointer;
 	z-index: 1001;
 	transition: all 0.2s ease-in-out;
-	font-size: 14px;
+	/*font-size: 14px;*/
+	font-size: 15px;
 	backdrop-filter: blur(8px);
 	box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
 	display: flex;
@@ -92,6 +93,289 @@ export class Style {
     color: #999;
 }
 
+#builtin-tabs-container {
+	display: flex;
+	align-items: stretch;
+	gap: 0;
+	border-right: 1px solid #262636;
+	order: -1;
+}
+
+.builtin-tab-btn {
+	background: transparent;
+	border: none;
+	color: var(--text-secondary);
+	cursor: pointer;
+	padding: 8px 14px;
+	font-family: var(--font-family);
+	font-size: 13px;
+	font-weight: 600;
+	transition: all 0.2s;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	min-width: 32px;
+	position: relative;
+}
+
+.builtin-tab-btn svg {
+	width: 20px;
+	height: 20px;
+	stroke: currentColor;
+}
+
+.builtin-tab-btn:hover {
+	background-color: rgba(255, 255, 255, 0.08);
+	color: var(--accent-color);
+}
+
+.builtin-tab-btn:active {
+	background-color: rgba(255, 255, 255, 0.12);
+}
+
+.builtin-tab-btn.active {
+	background-color: rgba(0, 170, 255, 0.2);
+	color: var(--accent-color);
+}
+
+.builtin-tab-btn.active:hover {
+	background-color: rgba(0, 170, 255, 0.3);
+}
+
+#profiler-mini-panel {
+	position: fixed;
+	top: 60px;
+	right: 15px;
+	background-color: rgba(30, 30, 36, 0.85);
+	border: 1px solid #4a4a5a54;
+	border-radius: 8px;
+	color: var(--text-primary);
+	z-index: 9999;
+	backdrop-filter: blur(8px);
+	box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
+	font-family: var(--font-family);
+	font-size: 11px;
+	width: 350px;
+	max-height: calc(100vh - 100px);
+	overflow-y: auto;
+	overflow-x: hidden;
+	display: none;
+	opacity: 0;
+	transform: translateY(-10px) scale(0.98);
+	transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), 
+	            transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+#profiler-mini-panel.visible {
+	display: block;
+	opacity: 1;
+	transform: translateY(0) scale(1);
+}
+
+#profiler-mini-panel::-webkit-scrollbar {
+	width: 6px;
+}
+
+#profiler-mini-panel::-webkit-scrollbar-track {
+	background: transparent;
+}
+
+#profiler-mini-panel::-webkit-scrollbar-thumb {
+	background: rgba(255, 255, 255, 0.15);
+	border-radius: 3px;
+	transition: background 0.2s;
+}
+
+#profiler-mini-panel::-webkit-scrollbar-thumb:hover {
+	background: rgba(255, 255, 255, 0.25);
+}
+
+.mini-panel-content {
+	padding: 0;
+	font-size: 11px;
+	line-height: 1.5;
+	font-family: var(--font-mono);
+	letter-spacing: 0.3px;
+	user-select: none;
+	-webkit-user-select: none;
+}
+
+.mini-panel-content .profiler-content {
+	display: block !important;
+	background: transparent;
+}
+
+.mini-panel-content .list-scroll-wrapper {
+	max-height: calc(100vh - 120px);
+	overflow-y: auto;
+	overflow-x: hidden;
+}
+
+.mini-panel-content .list-scroll-wrapper::-webkit-scrollbar {
+	width: 4px;
+}
+
+.mini-panel-content .list-scroll-wrapper::-webkit-scrollbar-track {
+	background: transparent;
+}
+
+.mini-panel-content .list-scroll-wrapper::-webkit-scrollbar-thumb {
+	background: rgba(255, 255, 255, 0.1);
+	border-radius: 2px;
+}
+
+.mini-panel-content .list-scroll-wrapper::-webkit-scrollbar-thumb:hover {
+	background: rgba(255, 255, 255, 0.2);
+}
+
+.mini-panel-content .parameters {
+	background: transparent;
+	border: none;
+	box-shadow: none;
+	padding: 4px;
+}
+
+.mini-panel-content .list-container.parameters {
+	padding: 2px 6px 0px 6px !important;
+}
+
+.mini-panel-content .list-header {
+	display: none;
+	padding: 2px 4px;
+	font-size: 11px;
+	font-weight: 600;
+	text-transform: uppercase;
+	letter-spacing: 0.5px;
+}
+
+.mini-panel-content .list-item {
+	border-bottom: 1px solid rgba(74, 74, 90, 0.2);
+	transition: background-color 0.15s;
+}
+
+.mini-panel-content .list-item:last-child {
+	border-bottom: none;
+}
+
+.mini-panel-content .list-item:hover {
+	background-color: rgba(255, 255, 255, 0.04);
+}
+
+.mini-panel-content .list-item.actionable:hover {
+	background-color: rgba(255, 255, 255, 0.06);
+	cursor: pointer;
+}
+
+/* Style adjustments for lil-gui look */
+.mini-panel-content .item-row {
+	padding: 3px 8px;
+	min-height: 24px;
+}
+
+.mini-panel-content .list-item-row {
+	padding: 1px 4px;
+	gap: 8px;
+	min-height: 21px;
+	align-items: center;
+}
+
+.mini-panel-content input[type="checkbox"] {
+	width: 12px;
+	height: 12px;
+}
+
+.mini-panel-content input[type="range"] {
+	height: 18px;
+}
+
+.mini-panel-content .value-number input,
+.mini-panel-content .value-slider input {
+	background-color: rgba(0, 0, 0, 0.3);
+	border: 1px solid rgba(74, 74, 90, 0.5);
+	font-size: 10px;
+}
+
+.mini-panel-content .value-number input:focus,
+.mini-panel-content .value-slider input:focus {
+	border-color: var(--accent-color);
+}
+
+.mini-panel-content .value-slider {
+	gap: 6px;
+}
+
+/* Compact nested items */
+.mini-panel-content .list-item .list-item {
+	margin-left: 8px;
+}
+
+.mini-panel-content .list-item .list-item .item-row,
+.mini-panel-content .list-item .list-item .list-item-row {
+	padding: 2px 6px;
+	min-height: 22px;
+}
+
+/* Compact collapsible headers */
+.mini-panel-content .collapsible .item-row,
+.mini-panel-content .list-item-row.collapsible {
+	padding: 2px 8px;
+	font-weight: 600;
+	min-height: 16px;
+	display: flex;
+	align-items: center;
+}
+
+.mini-panel-content .collapsible-icon {
+	font-size: 10px;
+	width: 14px;
+	height: 14px;
+}
+
+.mini-panel-content .param-control input[type="range"] {
+	height: 12px;
+	margin-top: 1px;
+	padding-top: 5px;
+	user-select: none;
+	-webkit-user-select: none;
+	outline: none;
+}
+
+.mini-panel-content .param-control input[type="range"]::-webkit-slider-thumb {
+	width: 14px;
+	height: 14px;
+	margin-top: -5px;
+	user-select: none;
+	-webkit-user-select: none;
+}
+
+.mini-panel-content .param-control input[type="range"]::-moz-range-thumb {
+	width: 14px;
+	height: 14px;
+	user-select: none;
+	-moz-user-select: none;
+}
+
+.mini-panel-content .list-children-container {
+	padding-left: 0;
+}
+
+.mini-panel-content .param-control input[type="number"] {
+	flex-basis: 60px !important;
+}
+
+.mini-panel-content .param-control {
+	align-items: center;
+}
+
+.mini-panel-content .param-control select {
+	font-size: 11px;
+}
+
+.mini-panel-content .list-item-wrapper {
+	margin-top: 0;
+	margin-bottom: 0;
+}
+
 #profiler-panel {
 	position: fixed;
 	z-index: 1001 !important;
@@ -544,6 +828,8 @@ export class Style {
 	box-sizing: border-box;
 	display: flex;
 	flex-direction: column;
+	user-select: none;
+	-webkit-user-select: none;
 }
 
 .profiler-content.active {
@@ -591,11 +877,15 @@ export class Style {
 	transition: background-color 0.2s;
 	gap: 10px;
 	border-bottom: none;
+	user-select: none;
+	-webkit-user-select: none;
 }
 
 .list-item-wrapper {
 	margin-top: 2px;
 	margin-bottom: 2px;
+	user-select: none;
+	-webkit-user-select: none;
 }
 
 .list-item-wrapper:first-child {
@@ -648,6 +938,8 @@ export class Style {
 	white-space: pre;
 	display: flex;
 	align-items: center;
+	user-select: none;
+	-webkit-user-select: none;
 }
 
 .list-item-cell:not(:first-child) {
@@ -665,6 +957,8 @@ export class Style {
 	border-bottom: 1px solid var(--profiler-border);
 	margin-bottom: 5px;
 	gap: 10px;
+	user-select: none;
+	-webkit-user-select: none;
 }
 
 .list-item-wrapper.section-start {
@@ -716,10 +1010,11 @@ export class Style {
 .list-scroll-wrapper {
 	overflow-x: auto;
 	width: 100%;
+	user-select: none;
+	-webkit-user-select: none;
 }
 
 .list-container.parameters .list-item-row:not(.collapsible) {
-	height: 31px;
 }
 
 .graph-container {
@@ -958,6 +1253,54 @@ export class Style {
 	border: 1px solid var(--profiler-border);
 }
 
+/* Override .param-control styles for mini-panel-content */
+.mini-panel-content input,
+.mini-panel-content select,
+.mini-panel-content button {
+	padding: 2px 4px;
+	height: 21px;
+	line-height: 1.4;
+	padding-top: 4px;
+}
+
+.mini-panel-content .param-control input,
+.mini-panel-content .param-control select,
+.mini-panel-content .param-control button {
+	background-color: #1e1e24c2;
+}
+
+.mini-panel-content .param-control select {
+	padding: 2px 2px;
+	padding-top: 3px;
+}
+
+.mini-panel-content .param-control input[type="number"]::-webkit-outer-spin-button,
+.mini-panel-content .param-control input[type="number"]::-webkit-inner-spin-button {
+	-webkit-appearance: none;
+	margin: 0;
+}
+
+.mini-panel-content .param-control input[type="number"] {
+	-moz-appearance: textfield;
+}
+
+.mini-panel-content .list-item-cell span {
+	position: relative;
+	top: 1px;
+	margin-left: 2px;
+}
+
+.mini-panel-content .custom-checkbox .checkmark {
+	width: 12px;
+	height: 12px;
+	margin-bottom: 2px;
+}
+
+.mini-panel-content .custom-checkbox .checkmark::after {
+	width: 8px;
+	height: 8px;
+}
+
 @media screen and (max-width: 450px) and (orientation: portrait) {
 
 	.console-filter-input {

+ 142 - 0
examples/jsm/inspector/ui/Tab.js

@@ -3,6 +3,8 @@
  * @param {string} title - The title of the tab
  * @param {Object} options - Options for the tab
  * @param {boolean} [options.allowDetach=true] - Whether the tab can be detached into a separate window
+ * @param {boolean} [options.builtin=false] - Whether the tab should appear in the profiler-toggle button
+ * @param {string} [options.icon] - SVG icon HTML for the builtin button
  *
  * @example
  * // Create a tab that can be detached (default behavior)
@@ -10,6 +12,16 @@
  *
  * // Create a tab that cannot be detached
  * const tab2 = new Tab('Fixed Tab', { allowDetach: false });
+ *
+ * // Create a builtin tab that appears in the profiler-toggle
+ * const tab3 = new Tab('Builtin Tab', { builtin: true });
+ *
+ * // Create a builtin tab with custom icon
+ * const tab4 = new Tab('Settings', { builtin: true, icon: '<svg>...</svg>' });
+ *
+ * // Control builtin tab visibility
+ * tab3.showBuiltin(); // Show the builtin button and mini-content
+ * tab3.hideBuiltin(); // Hide the builtin button and mini-content
  */
 export class Tab {
 
@@ -29,6 +41,11 @@ export class Tab {
 		this.isDetached = false;
 		this.detachedWindow = null;
 		this.allowDetach = options.allowDetach !== undefined ? options.allowDetach : true;
+		this.builtin = options.builtin !== undefined ? options.builtin : false;
+		this.icon = options.icon || null;
+		this.builtinButton = null; // Reference to the builtin button in profiler-toggle
+		this.miniContent = null; // Reference to the mini-panel content container
+		this.profiler = null; // Reference to the profiler instance
 		this.onVisibilityChange = null; // Callback for visibility changes
 
 	}
@@ -63,6 +80,8 @@ export class Tab {
 
 		}
 
+		this.showBuiltin();
+
 	}
 
 	hide() {
@@ -86,6 +105,129 @@ export class Tab {
 
 		}
 
+		this.hideBuiltin();
+
+	}
+
+	showBuiltin() {
+
+		if ( ! this.builtin ) return;
+
+		// Show the builtin-tabs-container
+		if ( this.profiler && this.profiler.builtinTabsContainer ) {
+
+			this.profiler.builtinTabsContainer.style.display = '';
+
+		}
+
+		// Show the button
+		if ( this.builtinButton ) {
+
+			this.builtinButton.style.display = '';
+
+		}
+
+		// Show and activate the mini-panel with content
+		if ( this.miniContent && this.profiler ) {
+
+			// Hide all other mini-panel contents
+			this.profiler.miniPanel.querySelectorAll( '.mini-panel-content' ).forEach( content => {
+
+				content.style.display = 'none';
+
+			} );
+
+			// Remove active state from all builtin buttons
+			this.profiler.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ).forEach( btn => {
+
+				btn.classList.remove( 'active' );
+
+			} );
+
+			// Activate this tab's button
+			if ( this.builtinButton ) {
+
+				this.builtinButton.classList.add( 'active' );
+
+			}
+
+			// Move content to mini-panel if not already there
+			if ( ! this.miniContent.firstChild ) {
+
+				const actualContent = this.content.querySelector( '.list-scroll-wrapper' ) || this.content.firstElementChild;
+
+				if ( actualContent ) {
+
+					this.miniContent.appendChild( actualContent );
+
+				}
+
+			}
+
+			// Show the mini-panel and content
+			this.miniContent.style.display = 'block';
+			this.profiler.miniPanel.classList.add( 'visible' );
+
+		}
+
+	}
+
+	hideBuiltin() {
+
+		if ( ! this.builtin ) return;
+
+		// Hide the button
+		if ( this.builtinButton ) {
+
+			this.builtinButton.style.display = 'none';
+
+		}
+
+		// Hide the mini-panel content
+		if ( this.miniContent ) {
+
+			this.miniContent.style.display = 'none';
+
+			// Move content back to main panel
+			if ( this.miniContent.firstChild ) {
+
+				this.content.appendChild( this.miniContent.firstChild );
+
+			}
+
+		}
+
+		// Deactivate button
+		if ( this.builtinButton ) {
+
+			this.builtinButton.classList.remove( 'active' );
+
+		}
+
+		// Hide mini-panel if no content is visible
+		if ( this.profiler ) {
+
+			const hasVisibleContent = Array.from( this.profiler.miniPanel.querySelectorAll( '.mini-panel-content' ) )
+				.some( content => content.style.display !== 'none' );
+
+			if ( ! hasVisibleContent ) {
+
+				this.profiler.miniPanel.classList.remove( 'visible' );
+
+			}
+
+			// Hide the builtin-tabs-container if all builtin buttons are hidden
+			const hasVisibleBuiltinButtons = Array.from( this.profiler.builtinTabsContainer.querySelectorAll( '.builtin-tab-btn' ) )
+				.some( btn => btn.style.display !== 'none' );
+
+			if ( ! hasVisibleBuiltinButtons ) {
+
+				this.profiler.builtinTabsContainer.style.display = 'none';
+
+			}
+
+		}
+
 	}
 
 }

粤ICP备19079148号