From d666840ed0e1d3276ccc23e6f3b47abe8c57fa4c Mon Sep 17 00:00:00 2001 From: "Logan.Li" Date: Sun, 26 Jul 2026 18:21:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DSSH=E7=BB=88=E7=AB=AFE?= =?UTF-8?q?scape=E6=8C=89=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将弹框捕获的Escape控制字符直接转发到远端 - 阻止xterm对同一次Escape事件重复发送 --- .../src/components/server/TerminalDialog.tsx | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/server/TerminalDialog.tsx b/frontend/src/components/server/TerminalDialog.tsx index e1f3d59..87ef4bb 100644 --- a/frontend/src/components/server/TerminalDialog.tsx +++ b/frontend/src/components/server/TerminalDialog.tsx @@ -61,6 +61,15 @@ export function TerminalDialog({ const [assigning, setAssigning] = useState(false); const [authReady, setAuthReady] = useState(hasAuth); // 认证就绪标志 + const sendTerminalInput = (data: string) => { + const ws = wsRef.current; + if (ws?.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ status: 0, data })); + return true; + } + return false; + }; + // 加载凭据列表 useEffect(() => { if (!open || hasAuth) return; @@ -123,11 +132,9 @@ export function TerminalDialog({ const ws = new WebSocket(wsUrl); wsRef.current = ws; - term.onData((data) => { - if (ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify({ status: 0, data })); - } - }); + // Escape 由 Dialog 捕获后直接转发,xterm 跳过同一次事件以免重复发送。 // Logan.Li + term.attachCustomKeyEventHandler((event) => event.key !== "Escape"); + term.onData(sendTerminalInput); ws.onmessage = (event) => { try { @@ -214,8 +221,11 @@ export function TerminalDialog({ e.preventDefault()} + // Radix 在捕获阶段收到 Escape,阻止关窗并把控制字符送给远端 Vim。 // Logan.Li + onEscapeKeyDown={(event) => { + event.preventDefault(); + sendTerminalInput("\x1b"); + }} className={cn( "min-w-0 overflow-hidden bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] p-0 gap-0", // 禁用 CSS 过渡:终端窗口应该即时缩放,不需要动画