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 过渡:终端窗口应该即时缩放,不需要动画