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

Inspector: Add window/panel boundary constraints (#32208)

sunag 2 месяцев назад
Родитель
Сommit
82834f0c48
2 измененных файлов с 410 добавлено и 97 удалено
  1. 356 90
      examples/jsm/inspector/ui/Profiler.js
  2. 54 7
      examples/jsm/inspector/ui/Style.js

+ 356 - 90
examples/jsm/inspector/ui/Profiler.js

@@ -27,6 +27,9 @@ export class Profiler {
 
 		}
 
+		// Setup window resize listener to constrain detached windows
+		this.setupWindowResizeListener();
+
 	}
 
 	detectMobile() {
@@ -69,6 +72,113 @@ export class Profiler {
 
 	}
 
+	setupWindowResizeListener() {
+
+		const constrainDetachedWindows = () => {
+
+			this.detachedWindows.forEach( detachedWindow => {
+
+				this.constrainWindowToBounds( detachedWindow.panel );
+
+			} );
+
+		};
+
+		const constrainMainPanel = () => {
+
+			// Skip if panel is maximized (it should always fill the screen)
+			if ( this.panel.classList.contains( 'maximized' ) ) return;
+
+			const windowWidth = window.innerWidth;
+			const windowHeight = window.innerHeight;
+
+			if ( this.position === 'bottom' ) {
+
+				const currentHeight = this.panel.offsetHeight;
+				const maxHeight = windowHeight - 50; // Leave 50px margin
+
+				if ( currentHeight > maxHeight ) {
+
+					this.panel.style.height = `${ maxHeight }px`;
+					this.lastHeightBottom = maxHeight;
+
+				}
+
+			} else if ( this.position === 'right' ) {
+
+				const currentWidth = this.panel.offsetWidth;
+				const maxWidth = windowWidth - 50; // Leave 50px margin
+
+				if ( currentWidth > maxWidth ) {
+
+					this.panel.style.width = `${ maxWidth }px`;
+					this.lastWidthRight = maxWidth;
+
+				}
+
+			}
+
+		};
+
+		// Listen for window resize events
+		window.addEventListener( 'resize', () => {
+
+			constrainDetachedWindows();
+			constrainMainPanel();
+
+		} );
+
+	}
+
+	constrainWindowToBounds( windowPanel ) {
+
+		const windowWidth = window.innerWidth;
+		const windowHeight = window.innerHeight;
+
+		const panelWidth = windowPanel.offsetWidth;
+		const panelHeight = windowPanel.offsetHeight;
+
+		let left = parseFloat( windowPanel.style.left ) || windowPanel.offsetLeft || 0;
+		let top = parseFloat( windowPanel.style.top ) || windowPanel.offsetTop || 0;
+
+		// Allow window to extend half its width/height outside the screen
+		const halfWidth = panelWidth / 2;
+		const halfHeight = panelHeight / 2;
+
+		// Constrain horizontal position (allow half width to extend beyond right edge)
+		if ( left + panelWidth > windowWidth + halfWidth ) {
+
+			left = windowWidth + halfWidth - panelWidth;
+
+		}
+
+		// Constrain horizontal position (allow half width to extend beyond left edge)
+		if ( left < - halfWidth ) {
+
+			left = - halfWidth;
+
+		}
+
+		// Constrain vertical position (allow half height to extend beyond bottom edge)
+		if ( top + panelHeight > windowHeight + halfHeight ) {
+
+			top = windowHeight + halfHeight - panelHeight;
+
+		}
+
+		// Constrain vertical position (allow half height to extend beyond top edge)
+		if ( top < - halfHeight ) {
+
+			top = - halfHeight;
+
+		}
+
+		// Apply constrained position
+		windowPanel.style.left = `${ left }px`;
+		windowPanel.style.top = `${ top }px`;
+
+	}
+
 	setupShell() {
 
 		this.domElement = document.createElement( 'div' );
@@ -136,6 +246,9 @@ export class Profiler {
 
 		this.domElement.append( this.toggleButton, this.panel );
 
+		// Set initial position class
+		this.panel.classList.add( `position-${this.position}` );
+
 	}
 
 	setupResizing() {
@@ -146,8 +259,9 @@ export class Profiler {
 
 			this.isResizing = true;
 			this.panel.classList.add( 'resizing' );
-			const startX = e.clientX || e.touches[ 0 ].clientX;
-			const startY = e.clientY || e.touches[ 0 ].clientY;
+			resizer.setPointerCapture( e.pointerId );
+			const startX = e.clientX;
+			const startY = e.clientY;
 			const startHeight = this.panel.offsetHeight;
 			const startWidth = this.panel.offsetWidth;
 
@@ -155,8 +269,8 @@ export class Profiler {
 
 				if ( ! this.isResizing ) return;
 				moveEvent.preventDefault();
-				const currentX = moveEvent.clientX || moveEvent.touches[ 0 ].clientX;
-				const currentY = moveEvent.clientY || moveEvent.touches[ 0 ].clientY;
+				const currentX = moveEvent.clientX;
+				const currentY = moveEvent.clientY;
 
 				if ( this.position === 'bottom' ) {
 
@@ -188,10 +302,9 @@ export class Profiler {
 
 				this.isResizing = false;
 				this.panel.classList.remove( 'resizing' );
-				document.removeEventListener( 'mousemove', onMove );
-				document.removeEventListener( 'mouseup', onEnd );
-				document.removeEventListener( 'touchmove', onMove );
-				document.removeEventListener( 'touchend', onEnd );
+				resizer.removeEventListener( 'pointermove', onMove );
+				resizer.removeEventListener( 'pointerup', onEnd );
+				resizer.removeEventListener( 'pointercancel', onEnd );
 				if ( ! this.panel.classList.contains( 'maximized' ) ) {
 
 					// Save dimensions based on current position
@@ -212,15 +325,13 @@ export class Profiler {
 
 			};
 
-			document.addEventListener( 'mousemove', onMove );
-			document.addEventListener( 'mouseup', onEnd );
-			document.addEventListener( 'touchmove', onMove, { passive: false } );
-			document.addEventListener( 'touchend', onEnd );
+			resizer.addEventListener( 'pointermove', onMove );
+			resizer.addEventListener( 'pointerup', onEnd );
+			resizer.addEventListener( 'pointercancel', onEnd );
 
 		};
 
-		resizer.addEventListener( 'mousedown', onStart );
-		resizer.addEventListener( 'touchstart', onStart );
+		resizer.addEventListener( 'pointerdown', onStart );
 
 	}
 
@@ -407,17 +518,18 @@ export class Profiler {
 
 		const onDragStart = ( e ) => {
 
-			startX = e.clientX || e.touches[ 0 ].clientX;
-			startY = e.clientY || e.touches[ 0 ].clientY;
+			startX = e.clientX;
+			startY = e.clientY;
 			isDragging = false;
 			hasMoved = false;
+			tab.button.setPointerCapture( e.pointerId );
 
 		};
 
 		const onDragMove = ( e ) => {
 
-			const currentX = e.clientX || e.touches[ 0 ].clientX;
-			const currentY = e.clientY || e.touches[ 0 ].clientY;
+			const currentX = e.clientX;
+			const currentY = e.clientY;
 
 			const deltaX = Math.abs( currentX - startX );
 			const deltaY = Math.abs( currentY - startY );
@@ -488,26 +600,18 @@ export class Profiler {
 			hasMoved = false;
 			previewWindow = null;
 
-			document.removeEventListener( 'mousemove', onDragMove );
-			document.removeEventListener( 'mouseup', onDragEnd );
-			document.removeEventListener( 'touchmove', onDragMove );
-			document.removeEventListener( 'touchend', onDragEnd );
+			tab.button.removeEventListener( 'pointermove', onDragMove );
+			tab.button.removeEventListener( 'pointerup', onDragEnd );
+			tab.button.removeEventListener( 'pointercancel', onDragEnd );
 
 		};
 
-		tab.button.addEventListener( 'mousedown', ( e ) => {
+		tab.button.addEventListener( 'pointerdown', ( e ) => {
 
 			onDragStart( e );
-			document.addEventListener( 'mousemove', onDragMove );
-			document.addEventListener( 'mouseup', onDragEnd );
-
-		} );
-
-		tab.button.addEventListener( 'touchstart', ( e ) => {
-
-			onDragStart( e );
-			document.addEventListener( 'touchmove', onDragMove, { passive: false } );
-			document.addEventListener( 'touchend', onDragEnd );
+			tab.button.addEventListener( 'pointermove', onDragMove );
+			tab.button.addEventListener( 'pointerup', onDragEnd );
+			tab.button.addEventListener( 'pointercancel', onDragEnd );
 
 		} );
 
@@ -664,10 +768,43 @@ export class Profiler {
 
 	createDetachedWindow( tab, x, y ) {
 
+		// Constrain initial position to window bounds
+		const windowWidth = window.innerWidth;
+		const windowHeight = window.innerHeight;
+		const estimatedWidth = 400; // Default detached window width
+		const estimatedHeight = 300; // Default detached window height
+
+		let constrainedX = x - 200;
+		let constrainedY = y - 20;
+
+		if ( constrainedX + estimatedWidth > windowWidth ) {
+
+			constrainedX = windowWidth - estimatedWidth;
+
+		}
+
+		if ( constrainedX < 0 ) {
+
+			constrainedX = 0;
+
+		}
+
+		if ( constrainedY + estimatedHeight > windowHeight ) {
+
+			constrainedY = windowHeight - estimatedHeight;
+
+		}
+
+		if ( constrainedY < 0 ) {
+
+			constrainedY = 0;
+
+		}
+
 		const windowPanel = document.createElement( 'div' );
 		windowPanel.className = 'detached-tab-panel';
-		windowPanel.style.left = `${ x - 200 }px`;
-		windowPanel.style.top = `${ y - 20 }px`;
+		windowPanel.style.left = `${ constrainedX }px`;
+		windowPanel.style.top = `${ constrainedY }px`;
 
 		if ( ! this.panel.classList.contains( 'visible' ) ) {
 
@@ -764,13 +901,7 @@ export class Profiler {
 		let startX, startY, startLeft, startTop;
 
 		// Bring window to front when clicking anywhere on it
-		windowPanel.addEventListener( 'mousedown', () => {
-
-			this.bringWindowToFront( windowPanel );
-
-		} );
-
-		windowPanel.addEventListener( 'touchstart', () => {
+		windowPanel.addEventListener( 'pointerdown', () => {
 
 			this.bringWindowToFront( windowPanel );
 
@@ -789,9 +920,10 @@ export class Profiler {
 
 			isDragging = true;
 			header.style.cursor = 'grabbing';
+			header.setPointerCapture( e.pointerId );
 
-			startX = e.clientX || e.touches[ 0 ].clientX;
-			startY = e.clientY || e.touches[ 0 ].clientY;
+			startX = e.clientX;
+			startY = e.clientY;
 
 			const rect = windowPanel.getBoundingClientRect();
 			startLeft = rect.left;
@@ -805,14 +937,53 @@ export class Profiler {
 
 			e.preventDefault();
 
-			const currentX = e.clientX || e.touches[ 0 ].clientX;
-			const currentY = e.clientY || e.touches[ 0 ].clientY;
+			const currentX = e.clientX;
+			const currentY = e.clientY;
 
 			const deltaX = currentX - startX;
 			const deltaY = currentY - startY;
 
-			windowPanel.style.left = `${ startLeft + deltaX }px`;
-			windowPanel.style.top = `${ startTop + deltaY }px`;
+			let newLeft = startLeft + deltaX;
+			let newTop = startTop + deltaY;
+
+			// Constrain to window bounds (allow half width/height to extend outside)
+			const windowWidth = window.innerWidth;
+			const windowHeight = window.innerHeight;
+			const panelWidth = windowPanel.offsetWidth;
+			const panelHeight = windowPanel.offsetHeight;
+			const halfWidth = panelWidth / 2;
+			const halfHeight = panelHeight / 2;
+
+			// Allow window to extend half its width beyond right edge
+			if ( newLeft + panelWidth > windowWidth + halfWidth ) {
+
+				newLeft = windowWidth + halfWidth - panelWidth;
+
+			}
+
+			// Allow window to extend half its width beyond left edge
+			if ( newLeft < - halfWidth ) {
+
+				newLeft = - halfWidth;
+
+			}
+
+			// Allow window to extend half its height beyond bottom edge
+			if ( newTop + panelHeight > windowHeight + halfHeight ) {
+
+				newTop = windowHeight + halfHeight - panelHeight;
+
+			}
+
+			// Allow window to extend half its height beyond top edge
+			if ( newTop < - halfHeight ) {
+
+				newTop = - halfHeight;
+
+			}
+
+			windowPanel.style.left = `${ newLeft }px`;
+			windowPanel.style.top = `${ newTop }px`;
 
 			// Check if cursor is over the inspector panel
 			const panelRect = this.panel.getBoundingClientRect();
@@ -843,8 +1014,8 @@ export class Profiler {
 			this.panel.style.outline = '';
 
 			// Check if dropped over the inspector panel
-			const currentX = e.clientX || ( e.changedTouches && e.changedTouches[ 0 ].clientX );
-			const currentY = e.clientY || ( e.changedTouches && e.changedTouches[ 0 ].clientY );
+			const currentX = e.clientX;
+			const currentY = e.clientY;
 
 			if ( currentX !== undefined && currentY !== undefined ) {
 
@@ -866,26 +1037,18 @@ export class Profiler {
 
 			}
 
-			document.removeEventListener( 'mousemove', onDragMove );
-			document.removeEventListener( 'mouseup', onDragEnd );
-			document.removeEventListener( 'touchmove', onDragMove );
-			document.removeEventListener( 'touchend', onDragEnd );
+			header.removeEventListener( 'pointermove', onDragMove );
+			header.removeEventListener( 'pointerup', onDragEnd );
+			header.removeEventListener( 'pointercancel', onDragEnd );
 
 		};
 
-		header.addEventListener( 'mousedown', ( e ) => {
-
-			onDragStart( e );
-			document.addEventListener( 'mousemove', onDragMove );
-			document.addEventListener( 'mouseup', onDragEnd );
-
-		} );
-
-		header.addEventListener( 'touchstart', ( e ) => {
+		header.addEventListener( 'pointerdown', ( e ) => {
 
 			onDragStart( e );
-			document.addEventListener( 'touchmove', onDragMove, { passive: false } );
-			document.addEventListener( 'touchend', onDragEnd );
+			header.addEventListener( 'pointermove', onDragMove );
+			header.addEventListener( 'pointerup', onDragEnd );
+			header.addEventListener( 'pointercancel', onDragEnd );
 
 		} );
 
@@ -912,8 +1075,10 @@ export class Profiler {
 				// Bring window to front when resizing
 				this.bringWindowToFront( windowPanel );
 
-				startX = e.clientX || e.touches[ 0 ].clientX;
-				startY = e.clientY || e.touches[ 0 ].clientY;
+				resizer.setPointerCapture( e.pointerId );
+
+				startX = e.clientX;
+				startY = e.clientY;
 				startWidth = windowPanel.offsetWidth;
 				startHeight = windowPanel.offsetHeight;
 				startLeft = windowPanel.offsetLeft;
@@ -927,16 +1092,21 @@ export class Profiler {
 
 				e.preventDefault();
 
-				const currentX = e.clientX || e.touches[ 0 ].clientX;
-				const currentY = e.clientY || e.touches[ 0 ].clientY;
+				const currentX = e.clientX;
+				const currentY = e.clientY;
 
 				const deltaX = currentX - startX;
 				const deltaY = currentY - startY;
 
+				const windowWidth = window.innerWidth;
+				const windowHeight = window.innerHeight;
+
 				if ( direction === 'right' || direction === 'corner' ) {
 
 					const newWidth = startWidth + deltaX;
-					if ( newWidth >= minWidth ) {
+					const maxWidth = windowWidth - startLeft;
+
+					if ( newWidth >= minWidth && newWidth <= maxWidth ) {
 
 						windowPanel.style.width = `${ newWidth }px`;
 
@@ -947,7 +1117,9 @@ export class Profiler {
 				if ( direction === 'bottom' || direction === 'corner' ) {
 
 					const newHeight = startHeight + deltaY;
-					if ( newHeight >= minHeight ) {
+					const maxHeight = windowHeight - startTop;
+
+					if ( newHeight >= minHeight && newHeight <= maxHeight ) {
 
 						windowPanel.style.height = `${ newHeight }px`;
 
@@ -958,10 +1130,18 @@ export class Profiler {
 				if ( direction === 'left' ) {
 
 					const newWidth = startWidth - deltaX;
+					const maxLeft = startLeft + startWidth - minWidth;
+
 					if ( newWidth >= minWidth ) {
 
-						windowPanel.style.width = `${ newWidth }px`;
-						windowPanel.style.left = `${ startLeft + deltaX }px`;
+						const newLeft = startLeft + deltaX;
+
+						if ( newLeft >= 0 && newLeft <= maxLeft ) {
+
+							windowPanel.style.width = `${ newWidth }px`;
+							windowPanel.style.left = `${ newLeft }px`;
+
+						}
 
 					}
 
@@ -970,10 +1150,18 @@ export class Profiler {
 				if ( direction === 'top' ) {
 
 					const newHeight = startHeight - deltaY;
+					const maxTop = startTop + startHeight - minHeight;
+
 					if ( newHeight >= minHeight ) {
 
-						windowPanel.style.height = `${ newHeight }px`;
-						windowPanel.style.top = `${ startTop + deltaY }px`;
+						const newTop = startTop + deltaY;
+
+						if ( newTop >= 0 && newTop <= maxTop ) {
+
+							windowPanel.style.height = `${ newHeight }px`;
+							windowPanel.style.top = `${ newTop }px`;
+
+						}
 
 					}
 
@@ -985,29 +1173,21 @@ export class Profiler {
 
 				isResizing = false;
 
-				document.removeEventListener( 'mousemove', onResizeMove );
-				document.removeEventListener( 'mouseup', onResizeEnd );
-				document.removeEventListener( 'touchmove', onResizeMove );
-				document.removeEventListener( 'touchend', onResizeEnd );
+				resizer.removeEventListener( 'pointermove', onResizeMove );
+				resizer.removeEventListener( 'pointerup', onResizeEnd );
+				resizer.removeEventListener( 'pointercancel', onResizeEnd );
 
 				// Save layout after resizing detached window
 				this.saveLayout();
 
 			};
 
-			resizer.addEventListener( 'mousedown', ( e ) => {
-
-				onResizeStart( e );
-				document.addEventListener( 'mousemove', onResizeMove );
-				document.addEventListener( 'mouseup', onResizeEnd );
-
-			} );
-
-			resizer.addEventListener( 'touchstart', ( e ) => {
+			resizer.addEventListener( 'pointerdown', ( e ) => {
 
 				onResizeStart( e );
-				document.addEventListener( 'touchmove', onResizeMove, { passive: false } );
-				document.addEventListener( 'touchend', onResizeEnd );
+				resizer.addEventListener( 'pointermove', onResizeMove );
+				resizer.addEventListener( 'pointerup', onResizeEnd );
+				resizer.addEventListener( 'pointercancel', onResizeEnd );
 
 			} );
 
@@ -1289,6 +1469,73 @@ export class Profiler {
 
 			const layout = JSON.parse( savedLayout );
 
+			// Constrain detached tabs positions to current screen bounds
+			if ( layout.detachedTabs && layout.detachedTabs.length > 0 ) {
+
+				const windowWidth = window.innerWidth;
+				const windowHeight = window.innerHeight;
+
+				layout.detachedTabs = layout.detachedTabs.map( detachedTabData => {
+
+					let { left, top, width, height } = detachedTabData;
+
+					// Ensure width and height are within bounds
+					if ( width > windowWidth ) {
+
+						width = windowWidth - 100; // Leave some margin
+
+					}
+
+					if ( height > windowHeight ) {
+
+						height = windowHeight - 100; // Leave some margin
+
+					}
+
+					// Allow window to extend half its width/height outside the screen
+					const halfWidth = width / 2;
+					const halfHeight = height / 2;
+
+					// Constrain horizontal position (allow half width to extend beyond right edge)
+					if ( left + width > windowWidth + halfWidth ) {
+
+						left = windowWidth + halfWidth - width;
+
+					}
+
+					// Constrain horizontal position (allow half width to extend beyond left edge)
+					if ( left < - halfWidth ) {
+
+						left = - halfWidth;
+
+					}
+
+					// Constrain vertical position (allow half height to extend beyond bottom edge)
+					if ( top + height > windowHeight + halfHeight ) {
+
+						top = windowHeight + halfHeight - height;
+
+					}
+
+					// Constrain vertical position (allow half height to extend beyond top edge)
+					if ( top < - halfHeight ) {
+
+						top = - halfHeight;
+
+					}
+
+					return {
+						...detachedTabData,
+						left,
+						top,
+						width,
+						height
+					};
+
+				} );
+
+			}
+
 			// Restore position and dimensions
 			if ( layout.position ) {
 
@@ -1308,6 +1555,22 @@ export class Profiler {
 
 			}
 
+			// Constrain saved dimensions to current screen bounds
+			const windowWidth = window.innerWidth;
+			const windowHeight = window.innerHeight;
+
+			if ( this.lastHeightBottom > windowHeight - 50 ) {
+
+				this.lastHeightBottom = windowHeight - 50;
+
+			}
+
+			if ( this.lastWidthRight > windowWidth - 50 ) {
+
+				this.lastWidthRight = windowWidth - 50;
+
+			}
+
 			// Apply the saved position after shell is set up
 			if ( this.position === 'right' ) {
 
@@ -1397,6 +1660,9 @@ export class Profiler {
 			detachedWindow.panel.style.width = `${ detachedTabData.width }px`;
 			detachedWindow.panel.style.height = `${ detachedTabData.height }px`;
 
+			// Constrain window to bounds after restoring position and size
+			this.constrainWindowToBounds( detachedWindow.panel );
+
 			this.detachedWindows.push( detachedWindow );
 
 			tab.isDetached = true;

+ 54 - 7
examples/jsm/inspector/ui/Style.js

@@ -220,6 +220,7 @@ export class Style {
 	height: 5px;
 	cursor: ns-resize;
 	z-index: 1001;
+	touch-action: none;
 }
 
 #profiler-panel.position-top .panel-resizer {
@@ -341,13 +342,6 @@ export class Style {
 	border-bottom: 1px solid var(--profiler-border);
 }
 
-#profiler-panel.position-right.no-tabs #maximize-btn,
-#profiler-panel.position-left.no-tabs #maximize-btn,
-#profiler-panel.position-bottom.no-tabs #maximize-btn,
-#profiler-panel.position-top.no-tabs #maximize-btn {
-	display: none;
-}
-
 #profiler-panel.position-right.no-tabs .profiler-content-wrapper,
 #profiler-panel.position-left.no-tabs .profiler-content-wrapper {
 	display: none;
@@ -464,6 +458,7 @@ export class Style {
 	font-size: 14px;
 	user-select: none;
 	transition: opacity 0.2s, transform 0.2s;
+	touch-action: none;
 }
 
 .tab-btn.active {
@@ -521,6 +516,14 @@ export class Style {
 	color: var(--text-primary);
 }
 
+/* Hide maximize button when there are no tabs */
+#profiler-panel.position-right.no-tabs #maximize-btn,
+#profiler-panel.position-left.no-tabs #maximize-btn,
+#profiler-panel.position-bottom.no-tabs #maximize-btn,
+#profiler-panel.position-top.no-tabs #maximize-btn {
+	display: none !important;
+}
+
 .profiler-content-wrapper {
 	flex-grow: 1;
 	overflow: hidden;
@@ -963,6 +966,44 @@ export class Style {
 
 }
 
+/* Touch device optimizations */
+@media (hover: none) and (pointer: coarse) {
+
+	.panel-resizer {
+		top: -10px !important;
+		height: 20px !important;
+	}
+
+	#profiler-panel.position-top .panel-resizer {
+		top: auto !important;
+		bottom: -10px !important;
+		height: 20px !important;
+	}
+
+	#profiler-panel.position-left .panel-resizer {
+		right: -10px !important;
+		width: 20px !important;
+		height: 100% !important;
+	}
+
+	#profiler-panel.position-right .panel-resizer {
+		left: -10px !important;
+		width: 20px !important;
+		height: 100% !important;
+	}
+
+	.detached-tab-resizer-top,
+	.detached-tab-resizer-bottom {
+		height: 10px !important;
+	}
+
+	.detached-tab-resizer-left,
+	.detached-tab-resizer-right {
+		width: 10px !important;
+	}
+
+}
+
 .drag-preview-indicator {
 	position: fixed;
 	background-color: rgba(0, 170, 255, 0.2);
@@ -1015,6 +1056,7 @@ body:has(#profiler-panel:not(.visible)) .detached-tab-panel {
 	flex-shrink: 0;
 	-webkit-font-smoothing: antialiased;
 	-moz-osx-font-smoothing: grayscale;
+	touch-action: none;
 }
 
 .detached-tab-header:active {
@@ -1100,6 +1142,7 @@ body:has(#profiler-panel:not(.visible)) .detached-tab-panel {
 	height: 20px;
 	cursor: nwse-resize;
 	z-index: 10;
+	touch-action: none;
 }
 
 .detached-tab-resizer::after {
@@ -1129,6 +1172,7 @@ body:has(#profiler-panel:not(.visible)) .detached-tab-panel {
 	height: 5px;
 	cursor: ns-resize;
 	z-index: 10;
+	touch-action: none;
 }
 
 .detached-tab-resizer-right {
@@ -1139,6 +1183,7 @@ body:has(#profiler-panel:not(.visible)) .detached-tab-panel {
 	width: 5px;
 	cursor: ew-resize;
 	z-index: 10;
+	touch-action: none;
 }
 
 .detached-tab-resizer-bottom {
@@ -1149,6 +1194,7 @@ body:has(#profiler-panel:not(.visible)) .detached-tab-panel {
 	height: 5px;
 	cursor: ns-resize;
 	z-index: 10;
+	touch-action: none;
 }
 
 .detached-tab-resizer-left {
@@ -1159,6 +1205,7 @@ body:has(#profiler-panel:not(.visible)) .detached-tab-panel {
 	width: 5px;
 	cursor: ew-resize;
 	z-index: 10;
+	touch-action: none;
 }
 
 /* Input number spin buttons - hide arrows */

粤ICP备19079148号