Просмотр исходного кода

Inspector: Group duplicate console messages and allow detached tab panels to remain visible (#33864)

sunag 2 недель назад
Родитель
Сommit
e98d15037e

+ 74 - 18
examples/jsm/inspector/tabs/Console.js

@@ -33,6 +33,8 @@ class Console extends Tab {
 		this.logContainer.classList.add( 'console-log' );
 		this.logContainer.classList.add( 'console-log' );
 		this.content.appendChild( this.logContainer );
 		this.content.appendChild( this.logContainer );
 
 
+		this.lastMessage = null;
+
 	}
 	}
 
 
 	buildHeader() {
 	buildHeader() {
@@ -74,9 +76,13 @@ class Console extends Tab {
 			const checkmark = document.createElement( 'span' );
 			const checkmark = document.createElement( 'span' );
 			checkmark.className = 'checkmark';
 			checkmark.className = 'checkmark';
 
 
+			const labelText = document.createElement( 'span' );
+			labelText.className = 'checkbox-text';
+			labelText.textContent = type.charAt( 0 ).toUpperCase() + type.slice( 1 );
+
 			label.appendChild( checkbox );
 			label.appendChild( checkbox );
 			label.appendChild( checkmark );
 			label.appendChild( checkmark );
-			label.append( type.charAt( 0 ).toUpperCase() + type.slice( 1 ) );
+			label.appendChild( labelText );
 			buttonsGroup.appendChild( label );
 			buttonsGroup.appendChild( label );
 
 
 		} );
 		} );
@@ -190,10 +196,6 @@ class Console extends Tab {
 			const parts = fullPrefix.slice( 0, - 2 ).split( '.' );
 			const parts = fullPrefix.slice( 0, - 2 ).split( '.' );
 			const shortPrefix = ( parts.length > 1 ? parts[ parts.length - 1 ] : parts[ 0 ] ) + ':';
 			const shortPrefix = ( parts.length > 1 ? parts[ parts.length - 1 ] : parts[ 0 ] ) + ':';
 
 
-			const icon = this._getIcon( type, shortPrefix.split( ':' )[ 0 ].toLowerCase() );
-
-			fragment.appendChild( document.createTextNode( icon + ' ' ) );
-
 			const prefixSpan = document.createElement( 'span' );
 			const prefixSpan = document.createElement( 'span' );
 			prefixSpan.className = 'log-prefix';
 			prefixSpan.className = 'log-prefix';
 			prefixSpan.textContent = shortPrefix;
 			prefixSpan.textContent = shortPrefix;
@@ -232,7 +234,7 @@ class Console extends Tab {
 
 
 		super.setActive( isActive );
 		super.setActive( isActive );
 
 
-		if ( isActive && this.profiler && this.profiler.panel.classList.contains( 'visible' ) ) {
+		if ( isActive ) {
 
 
 			this.clearUnread();
 			this.clearUnread();
 
 
@@ -319,25 +321,79 @@ class Console extends Tab {
 
 
 	addMessage( type, text ) {
 	addMessage( type, text ) {
 
 
-		const msg = document.createElement( 'div' );
-		msg.className = `log-message ${type}`;
-		msg.dataset.type = type;
-		msg.dataset.rawText = text;
+		if ( this.lastMessage && this.lastMessage.type === type && this.lastMessage.text === text ) {
 
 
-		msg.appendChild( this._formatMessage( type, text ) );
+			this.lastMessage.count ++;
+			this.lastMessage.countBadge.textContent = this.lastMessage.count;
+			this.lastMessage.countBadge.style.display = '';
 
 
-		const showByType = this.filters[ type ];
-		const showByText = text.toLowerCase().includes( this.filterText );
-		msg.classList.toggle( 'hidden', ! ( showByType && showByText ) );
+		} else {
 
 
-		this.logContainer.appendChild( msg );
-		this.logContainer.scrollTop = this.logContainer.scrollHeight;
-		if ( this.logContainer.children.length > 200 ) {
+			const msg = document.createElement( 'div' );
+			msg.className = `log-message ${type}`;
+			msg.dataset.type = type;
+			msg.dataset.rawText = text;
+
+			const countBadge = document.createElement( 'span' );
+			countBadge.className = 'log-count-badge';
+			countBadge.style.display = 'none';
+			msg.appendChild( countBadge );
+
+			let icon = null;
+			const prefixMatch = text.match( /^([\w\.]+:\s)/ );
+			if ( prefixMatch ) {
+
+				const fullPrefix = prefixMatch[ 0 ];
+				const parts = fullPrefix.slice( 0, - 2 ).split( '.' );
+				const shortPrefix = ( parts.length > 1 ? parts[ parts.length - 1 ] : parts[ 0 ] ) + ':';
+				icon = this._getIcon( type, shortPrefix.split( ':' )[ 0 ].toLowerCase() );
+
+			}
+
+			if ( icon ) {
+
+				const iconSpan = document.createElement( 'span' );
+				iconSpan.className = 'log-icon';
+				iconSpan.textContent = icon;
+				msg.appendChild( iconSpan );
+
+			}
+
+			const body = document.createElement( 'span' );
+			body.className = 'log-body';
+			body.appendChild( this._formatMessage( type, text ) );
+			msg.appendChild( body );
+
+			const showByType = this.filters[ type ];
+			const showByText = text.toLowerCase().includes( this.filterText );
+			msg.classList.toggle( 'hidden', ! ( showByType && showByText ) );
+
+			this.logContainer.appendChild( msg );
+
+			if ( this.logContainer.children.length > 200 ) {
 
 
-			this.logContainer.removeChild( this.logContainer.firstChild );
+				const firstChild = this.logContainer.firstChild;
+				this.logContainer.removeChild( firstChild );
+				if ( this.lastMessage && this.lastMessage.element === firstChild ) {
+
+					this.lastMessage = null;
+
+				}
+
+			}
+
+			this.lastMessage = {
+				type,
+				text,
+				count: 1,
+				element: msg,
+				countBadge
+			};
 
 
 		}
 		}
 
 
+		this.logContainer.scrollTop = this.logContainer.scrollHeight;
+
 		// Update unread counts if the console is not active/visible
 		// Update unread counts if the console is not active/visible
 		const isUnread = ! this.isActive;
 		const isUnread = ! this.isActive;
 
 

+ 1 - 1
examples/jsm/inspector/ui/List.js

@@ -7,7 +7,7 @@ export class List {
 		this.children = [];
 		this.children = [];
 		this.domElement = document.createElement( 'div' );
 		this.domElement = document.createElement( 'div' );
 		this.domElement.className = 'list-container';
 		this.domElement.className = 'list-container';
-		this.domElement.style.padding = '10px';
+		this.domElement.style.padding = '5px 10px 10px 10px';
 		this.id = `list-${Math.random().toString( 36 ).slice( 2, 11 )}`;
 		this.id = `list-${Math.random().toString( 36 ).slice( 2, 11 )}`;
 		this.domElement.dataset.listId = this.id;
 		this.domElement.dataset.listId = this.id;
 
 

+ 0 - 22
examples/jsm/inspector/ui/Profiler.js

@@ -1175,13 +1175,7 @@ export class Profiler extends EventDispatcher {
 		windowPanel.style.left = `${ constrainedX }px`;
 		windowPanel.style.left = `${ constrainedX }px`;
 		windowPanel.style.top = `${ constrainedY }px`;
 		windowPanel.style.top = `${ constrainedY }px`;
 
 
-		if ( ! this.panel.classList.contains( 'visible' ) ) {
 
 
-			windowPanel.style.opacity = '0';
-			windowPanel.style.visibility = 'hidden';
-			windowPanel.style.pointerEvents = 'none';
-
-		}
 
 
 		// Hide detached window if tab is not visible
 		// Hide detached window if tab is not visible
 		if ( ! tab.isVisible ) {
 		if ( ! tab.isVisible ) {
@@ -1692,23 +1686,7 @@ export class Profiler extends EventDispatcher {
 
 
 		}
 		}
 
 
-		this.detachedWindows.forEach( detachedWindow => {
-
-			if ( isVisible ) {
-
-				detachedWindow.panel.style.opacity = '';
-				detachedWindow.panel.style.visibility = '';
-				detachedWindow.panel.style.pointerEvents = '';
 
 
-			} else {
-
-				detachedWindow.panel.style.opacity = '0';
-				detachedWindow.panel.style.visibility = 'hidden';
-				detachedWindow.panel.style.pointerEvents = 'none';
-
-			}
-
-		} );
 
 
 		this.dispatchEvent( { type: 'resize' } );
 		this.dispatchEvent( { type: 'resize' } );
 
 

+ 86 - 14
examples/jsm/inspector/ui/Style.js

@@ -128,7 +128,7 @@ export class Style {
 
 
 	.tab-badge-container {
 	.tab-badge-container {
 		position: absolute;
 		position: absolute;
-		top: 2px;
+		top: 1px;
 		right: 3px;
 		right: 3px;
 		display: flex;
 		display: flex;
 		gap: 2px;
 		gap: 2px;
@@ -1081,7 +1081,7 @@ export class Style {
 	}
 	}
 
 
 	.parameters .list-item-row {
 	.parameters .list-item-row {
-		min-height: 31px;
+		min-height: 23px;
 	}
 	}
 
 
 	.mini-panel-content .parameters .list-item-row {
 	.mini-panel-content .parameters .list-item-row {
@@ -1095,6 +1095,10 @@ export class Style {
 		-webkit-user-select: none;
 		-webkit-user-select: none;
 	}
 	}
 
 
+	.list-item-wrapper:has(> .list-item-row .graph-container) {
+		margin-left: -1.5em;
+	}
+
 	.list-item-wrapper:first-child {
 	.list-item-wrapper:first-child {
 		/*margin-top: 0;*/
 		/*margin-top: 0;*/
 	}
 	}
@@ -1258,7 +1262,7 @@ export class Style {
 		border: 1px solid var(--profiler-border);
 		border: 1px solid var(--profiler-border);
 		color: var(--text-primary);
 		color: var(--text-primary);
 		border-radius: 4px;
 		border-radius: 4px;
-		padding: 4px 8px;
+		padding: 4px 10px 2px 10px;
 		font-family: var(--font-mono);
 		font-family: var(--font-mono);
 		flex-grow: 1;
 		flex-grow: 1;
 		max-width: 300px;
 		max-width: 300px;
@@ -1304,13 +1308,64 @@ export class Style {
 	}
 	}
 
 
 	.log-message {
 	.log-message {
-		padding: 2px 5px;
-		white-space: pre-wrap;
-		word-break: break-all;
+		display: flex;
+		align-items: flex-start;
+		gap: 6px;
+		padding: 3px 5px;
 		border-radius: 3px;
 		border-radius: 3px;
 		line-height: 1.5 !important;
 		line-height: 1.5 !important;
 	}
 	}
 
 
+	.log-count-badge {
+		display: inline-block;
+		text-align: center;
+		min-width: 14px;
+		height: 14px;
+		border-radius: 7px;
+		padding: 0 3px;
+		font-size: 9px;
+		font-weight: bold;
+		line-height: 14px;
+		box-sizing: border-box;
+		margin-top: 0;
+		flex-shrink: 0;
+	}
+
+	.log-icon {
+		display: inline-block;
+		text-align: center;
+		width: 14px;
+		height: 14px;
+		font-size: 11px;
+		line-height: 14px;
+		margin-top: 0;
+		flex-shrink: 0;
+	}
+
+	.log-body {
+		flex-grow: 1;
+		white-space: pre-wrap;
+		word-break: break-all;
+	}
+
+	.log-message.info .log-count-badge {
+		background-color: rgba(255, 255, 255, 0.12);
+		border: 1px solid rgba(255, 255, 255, 0.2);
+		color: var(--text-secondary);
+	}
+
+	.log-message.warn .log-count-badge {
+		background-color: rgba(255, 193, 7, 0.18);
+		border: 1px solid rgba(255, 193, 7, 0.35);
+		color: var(--color-yellow);
+	}
+
+	.log-message.error .log-count-badge {
+		background-color: rgba(244, 67, 54, 0.18);
+		border: 1px solid rgba(244, 67, 54, 0.35);
+		color: #ff8a80;
+	}
+
 	.log-message.hidden {
 	.log-message.hidden {
 		display: none;
 		display: none;
 	}
 	}
@@ -1411,6 +1466,7 @@ export class Style {
 		cursor: pointer;
 		cursor: pointer;
 		gap: 8px;
 		gap: 8px;
 		will-change: transform;
 		will-change: transform;
+		font-size: 12px;
 	}
 	}
 
 
 	.custom-checkbox input {
 	.custom-checkbox input {
@@ -1428,6 +1484,12 @@ export class Style {
 		transition: background-color 0.2s, border-color 0.2s;
 		transition: background-color 0.2s, border-color 0.2s;
 	}
 	}
 
 
+	.custom-checkbox .checkbox-text {
+		font-size: 12px;
+		margin-top: 1px;
+		color: inherit;
+	}
+
 	.custom-checkbox .checkmark::after {
 	.custom-checkbox .checkmark::after {
 		content: '';
 		content: '';
 		width: 6px;
 		width: 6px;
@@ -1439,6 +1501,16 @@ export class Style {
 		transition: transform 0.2s;
 		transition: transform 0.2s;
 	}
 	}
 
 
+	.list-container .custom-checkbox .checkmark {
+		width: 13px;
+		height: 13px;
+	}
+
+	.list-container .custom-checkbox .checkmark::after {
+		width: 7px;
+		height: 7px;
+	}
+
 	.custom-checkbox input:checked+.checkmark {
 	.custom-checkbox input:checked+.checkmark {
 		border-color: var(--color-accent);
 		border-color: var(--color-accent);
 	}
 	}
@@ -1646,12 +1718,6 @@ export class Style {
 		font-size: 13px;
 		font-size: 13px;
 	}
 	}
 
 
-	.profiler-panel:not(.visible) ~ * .detached-tab-panel,
-	body:has(.profiler-panel:not(.visible)) .detached-tab-panel {
-		opacity: 0;
-		visibility: hidden;
-		pointer-events: none;
-	}
 
 
 	.detached-tab-header {
 	.detached-tab-header {
 		background: var(--profiler-header-background);
 		background: var(--profiler-header-background);
@@ -1896,7 +1962,8 @@ export class Style {
 		display: flex;
 		display: flex;
 		align-items: center;
 		align-items: center;
 		justify-content: space-between;
 		justify-content: space-between;
-		padding: 6px 8px;
+		height: 32px;
+		padding: 4px 6px;
 		border-bottom: 1px solid var(--profiler-border);
 		border-bottom: 1px solid var(--profiler-border);
 		background: var(--profiler-header-background);
 		background: var(--profiler-header-background);
 		flex-shrink: 0;
 		flex-shrink: 0;
@@ -1905,12 +1972,17 @@ export class Style {
 	}
 	}
 
 
 	.toolbar span {
 	.toolbar span {
-		margin-right: 8px;
 		color: var(--text-secondary);
 		color: var(--text-secondary);
 		font-size: 12px;
 		font-size: 12px;
 		font-weight: 600;
 		font-weight: 600;
 	}
 	}
 
 
+	.toolbar .custom-checkbox .checkmark {
+		width: 12px;
+		height: 12px;
+		border-radius: 4px;
+	}
+
 	.viewer-content .toolbar {
 	.viewer-content .toolbar {
 		justify-content: flex-end;
 		justify-content: flex-end;
 	}
 	}

+ 3 - 1
examples/jsm/inspector/ui/Tab.js

@@ -62,11 +62,13 @@ export class Tab extends EventDispatcher {
 
 
 	get isActive() {
 	get isActive() {
 
 
+		if ( this.isDetached && this.isVisible ) return true;
+
 		const isProfilerVisible = this.profiler && this.profiler.panel.classList.contains( 'visible' );
 		const isProfilerVisible = this.profiler && this.profiler.panel.classList.contains( 'visible' );
 
 
 		if ( ! isProfilerVisible ) return false;
 		if ( ! isProfilerVisible ) return false;
 
 
-		return this.isDetached || this._isActive;
+		return this._isActive;
 
 
 	}
 	}
 
 

粤ICP备19079148号