diff --git a/src/components/shell/utils/mobileTerminalSelection.ts b/src/components/shell/utils/mobileTerminalSelection.ts index f6be81ae..ef20e066 100644 --- a/src/components/shell/utils/mobileTerminalSelection.ts +++ b/src/components/shell/utils/mobileTerminalSelection.ts @@ -378,7 +378,7 @@ class ShellMobileSelectionCore implements MobileTerminalSelectionManager { this.recordScrollSample(touch); }; - private onTerminalTouchEnd = (): void => { + private onTerminalTouchEnd = (event: TouchEvent): void => { if (this.isPinching) { this.endPinchZoom(); return; @@ -387,6 +387,15 @@ class ShellMobileSelectionCore implements MobileTerminalSelectionManager { this.clearTapHoldTimeout(); this.touchStart = null; + // A long-press selection (or a tap dismissing one) must not let the browser + // synthesize the mouse click that refocuses xterm's hidden textarea — that + // is what pops up the mobile keyboard. A plain tap leaves isSelecting false + // and falls through, so it still focuses the terminal and shows the keyboard. + if (this.isSelecting || this.isHandleDragging) { + event.preventDefault(); + this.blurTerminalInput(); + } + if (!this.pendingClearTouch) { this.maybeStartInertia(); return; @@ -508,6 +517,9 @@ class ShellMobileSelectionCore implements MobileTerminalSelectionManager { this.selectionEnd = wordBounds?.end ?? coords; this.isSelecting = true; + // Dismiss the mobile keyboard if it was open: selecting text is not typing. + this.blurTerminalInput(); + this.updateSelection(); this.showHandles(); this.showContextMenu(); @@ -976,6 +988,13 @@ class ShellMobileSelectionCore implements MobileTerminalSelectionManager { }; } + private blurTerminalInput(): void { + const textarea = this.terminal.element?.querySelector( + '.xterm-helper-textarea', + ); + textarea?.blur(); + } + private getTerminalScreenElement(): HTMLElement | null { return ( this.terminal.element?.querySelector('.xterm-screen') ??