diff --git a/packages/app/src/components/terminal-emulator.native.tsx b/packages/app/src/components/terminal-emulator.native.tsx index b3e6634ee..8742c8361 100644 --- a/packages/app/src/components/terminal-emulator.native.tsx +++ b/packages/app/src/components/terminal-emulator.native.tsx @@ -8,7 +8,7 @@ import { type ComponentProps, type Ref, } from "react"; -import { StyleSheet, View, type StyleProp, type ViewStyle } from "react-native"; +import { Keyboard, StyleSheet, View, type StyleProp, type ViewStyle } from "react-native"; import { WebView, type WebViewMessageEvent } from "react-native-webview"; import type { ITheme } from "@xterm/xterm"; import type { TerminalState } from "@server/shared/messages"; @@ -28,6 +28,7 @@ export interface TerminalEmulatorHandle { restoreOutput: (data: TerminalOutputData) => void; renderSnapshot: (state: TerminalState | null) => void; clear: () => void; + blur: () => void; } interface TerminalEmulatorProps { @@ -334,6 +335,12 @@ export default function TerminalEmulator({ outputDecoderRef.current.decode(); sendToWebView({ type: "clear", streamKey }); }, + blur: () => { + webViewRef.current?.injectJavaScript( + "window.__PASEO_TERMINAL_WEBVIEW_BLUR__ && window.__PASEO_TERMINAL_WEBVIEW_BLUR__(); true;", + ); + Keyboard.dismiss(); + }, }), [sendToWebView, streamKey], ); diff --git a/packages/app/src/components/terminal-emulator.tsx b/packages/app/src/components/terminal-emulator.tsx index 957898828..4b0566b2b 100644 --- a/packages/app/src/components/terminal-emulator.tsx +++ b/packages/app/src/components/terminal-emulator.tsx @@ -47,6 +47,7 @@ export interface TerminalEmulatorHandle { restoreOutput: (data: TerminalOutputData) => void; renderSnapshot: (state: TerminalState | null) => void; clear: () => void; + blur: () => void; } const SCROLLBAR_HANDLE_WIDTH_IDLE = 6; @@ -302,6 +303,9 @@ export default function TerminalEmulator({ clear: () => { runtimeRef.current?.clear(); }, + blur: () => { + runtimeRef.current?.blur(); + }, }), [], ); @@ -320,6 +324,9 @@ export default function TerminalEmulator({ clear: () => { runtimeRef.current?.clear(); }, + blur: () => { + runtimeRef.current?.blur(); + }, }), [], ); diff --git a/packages/app/src/components/terminal-pane.tsx b/packages/app/src/components/terminal-pane.tsx index 1f1be0107..27e7649f3 100644 --- a/packages/app/src/components/terminal-pane.tsx +++ b/packages/app/src/components/terminal-pane.tsx @@ -231,6 +231,12 @@ export function TerminalPane({ const requestTerminalReflow = useCallback(() => { setResizeRequestToken((current) => current + 1); }, []); + useEffect(() => { + if (!isMobile || !isWorkspaceFocused || mobileView === "agent") { + return; + } + emulatorRef.current?.blur(); + }, [isMobile, isWorkspaceFocused, mobileView]); const handleRendererReadyChange = useCallback( (change: TerminalRendererReadyChange) => { setRendererReadyStreamKey((current) => applyTerminalRendererReadyChange(current, change)); @@ -716,11 +722,13 @@ export function TerminalPane({ const handleSwipeRight = useCallback(() => { if (!swipeGesturesEnabled) return; + emulatorRef.current?.blur(); showMobileAgentList(); }, [swipeGesturesEnabled, showMobileAgentList]); const handleSwipeLeft = useCallback(() => { if (!swipeGesturesEnabled) return; + emulatorRef.current?.blur(); onOpenFileExplorer(); }, [swipeGesturesEnabled, onOpenFileExplorer]); const showLoadingOverlay = shouldShowTerminalLoadingOverlay({ diff --git a/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts b/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts index a1de5b1d7..158baefeb 100644 --- a/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts +++ b/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts @@ -680,6 +680,10 @@ export class TerminalEmulatorRuntime { terminal.focus(); } + blur(): void { + this.terminal?.blur(); + } + private refreshVisibleRows(): void { const terminal = this.terminal; if (!terminal || terminal.rows <= 0) { diff --git a/packages/app/src/terminal/webview/terminal-emulator-webview-entry.ts b/packages/app/src/terminal/webview/terminal-emulator-webview-entry.ts index 81619d740..edc9b68b3 100644 --- a/packages/app/src/terminal/webview/terminal-emulator-webview-entry.ts +++ b/packages/app/src/terminal/webview/terminal-emulator-webview-entry.ts @@ -81,6 +81,7 @@ declare global { postMessage?: (data: string) => void; }; __PASEO_TERMINAL_WEBVIEW_RECEIVE__?: (message: InboundMessage) => void; + __PASEO_TERMINAL_WEBVIEW_BLUR__?: () => void; } } @@ -303,6 +304,10 @@ class TerminalWebViewBridge { this.host.style.backgroundColor = background; } + blur = (): void => { + this.runtime?.blur(); + }; + private unmount(streamKey: string | null): void { if (!this.runtime) { return; @@ -435,4 +440,5 @@ document.body.appendChild(root); const bridge = new TerminalWebViewBridge(root, host); window.__PASEO_TERMINAL_WEBVIEW_RECEIVE__ = bridge.receive; +window.__PASEO_TERMINAL_WEBVIEW_BLUR__ = bridge.blur; sendToNative({ type: "bridgeReady" }); diff --git a/packages/app/src/terminal/webview/terminal-emulator-webview-html.ts b/packages/app/src/terminal/webview/terminal-emulator-webview-html.ts index cd592df3a..1b61188b7 100644 --- a/packages/app/src/terminal/webview/terminal-emulator-webview-html.ts +++ b/packages/app/src/terminal/webview/terminal-emulator-webview-html.ts @@ -1,4 +1,4 @@ // Generated by packages/app/scripts/build-terminal-webview-html.mjs. // Do not edit by hand. -export const terminalEmulatorWebViewHtml = "\n\n
\n \n \n \n \n \n \n"; +export const terminalEmulatorWebViewHtml = "\n\n \n \n \n \n \n \n \n";