diff --git a/CHANGELOG.md b/CHANGELOG.md index 422b061..7de2119 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ This file records user-facing changes for Terminal. Unless noted otherwise, vers ## [Unreleased] +- **vim 等 alternate 屏程序下 Ctrl+F 不再弹本地搜索(#108 反馈三)**。0.7.9-beta.3 的 alternate 屏热键透传覆盖了裸 ↑ 与命令历史热键,但漏了新加的 Ctrl+F→终端搜索分流:`resolveCtrlFRoute` 未判 alternate 屏,vim/less/tmux copy-mode 里 Ctrl+F(远端向前翻页)仍被本地搜索框抢占。现 alternate 屏一律放行远端,本地搜索由可配置热键(默认 Ctrl+Shift+F)全场景兜底。 + **Ctrl+F no longer opens the local search box inside alternate-screen programs like vim (#108 feedback 3).** The 0.7.9-beta.3 alternate-screen hotkey passthrough covered bare ↑ and the command-history hotkey but missed the newly added Ctrl+F→terminal-search routing: `resolveCtrlFRoute` had no alternate-screen gate, so vim/less/tmux copy-mode kept losing Ctrl+F (remote page-forward) to the local search box. The alternate screen now always passes Ctrl+F through to the remote; local search remains available everywhere via the configurable hotkey (default Ctrl+Shift+F). + - **定时任务支持本地 Shell 目标与脚本文件**。调度任务编辑器新增两处能力:任务连接可选「本地 Shell」(新连接类型 `io.dbx.ssh.local`——表单只有连接名与可选 Shell 绝对路径,无网络端点、无凭据),让定时/常驻命令直接运行在 DBX 所在机器上(进程级应用环境变量与工作目录,流式日志、取消、超时与 SSH 同契约;取消/超时按进程组 TERM→SIGKILL 清理,本地路径忽略 PTY 选项);两个触发器均新增可选「脚本文件」字段(带本机文件选择器),与「命令」互斥、至少填一个——SSH 连接把脚本上传到远端 `mktemp` 临时文件以 bash(无则 sh)执行、run 结束必删临时文件,本地 Shell 直接执行该文件。本地化覆盖全部既有语言。 **Scheduled tasks gain a local shell target and script files.** The task editor adds two capabilities: task connections may bind a "Local shell" target (new connection type `io.dbx.ssh.local` — the form carries only a display name and an optional absolute shell path, no network endpoint and no credentials) so scheduled and resident commands run on the machine running DBX itself (environment and working directory apply at the process level; streaming logs, cancel and timeout follow the same contract as SSH, with cancel/timeout tearing down the whole process group TERM→SIGKILL; the PTY field is ignored on local targets); and both triggers gain an optional "Script file" field (with a native file picker), mutually exclusive with "Command" and at least one required — SSH targets upload the script to a remote `mktemp` temp file, run it with bash (falling back to sh) and always remove the temp file afterwards, while local-shell targets execute the file directly. Localizations cover every shipped language. diff --git a/frontend/src/App.vue b/frontend/src/App.vue index a4fe827..17fd36c 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -2536,11 +2536,17 @@ function handleTerminalKey(event: KeyboardEvent) { // Ctrl+F 终端搜索(issue #108a,iTerm2 同位):ghost 建议可见待接受时上方 // 分支已把 Ctrl+F 消费为整段接受(Warp 键位模型不变);无 ghost 才轮到 // 搜索。传输占用(trz/zmodem 协议拥有输入流)时放行——与 resolveTerminalInputRoute - // 同判,不打断传输(Ctrl+F 的 0x06 由传输 filter/远端自行解释)。 + // 同判,不打断传输(Ctrl+F 的 0x06 由传输 filter/远端自行解释);alternate + // 屏(vim 的 Ctrl+F 是向前翻页,#108 反馈三)同样放行远端,本地搜索走 + // Ctrl+Shift+F 热键兜底。 if ( event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey && event.key.toLowerCase() === "f" && - resolveCtrlFRoute({ ghostVisible: !!ghostMatch.value, transferBusy: terminalTransferBusy.value }) === "search" + resolveCtrlFRoute({ + ghostVisible: !!ghostMatch.value, + transferBusy: terminalTransferBusy.value, + alternateActive: terminal?.buffer.active.type === "alternate", + }) === "search" ) { openTerminalSearch(); return consume(); diff --git a/frontend/src/lib/ghostAcceptKey.spec.ts b/frontend/src/lib/ghostAcceptKey.spec.ts index 428ec40..82b3830 100644 --- a/frontend/src/lib/ghostAcceptKey.spec.ts +++ b/frontend/src/lib/ghostAcceptKey.spec.ts @@ -63,4 +63,14 @@ describe("resolveCtrlFRoute (issue #108a,iTerm2 同位)", () => { expect(resolveCtrlFRoute({ ghostVisible: false, transferBusy: true })).toBe("passthrough"); expect(resolveCtrlFRoute({ ghostVisible: true, transferBusy: true })).toBe("passthrough"); }); + + it("passes Ctrl+F through in the alternate buffer (vim page-forward, #108 反馈三)", () => { + expect(resolveCtrlFRoute({ ghostVisible: false, transferBusy: false, alternateActive: true })).toBe("passthrough"); + expect(resolveCtrlFRoute({ ghostVisible: true, transferBusy: false, alternateActive: true })).toBe("passthrough"); + expect(resolveCtrlFRoute({ ghostVisible: false, transferBusy: true, alternateActive: true })).toBe("passthrough"); + }); + + it("keeps routing to search in the normal buffer when alternateActive is unset", () => { + expect(resolveCtrlFRoute({ ghostVisible: false, transferBusy: false })).toBe("search"); + }); }); diff --git a/frontend/src/lib/ghostAcceptKey.ts b/frontend/src/lib/ghostAcceptKey.ts index 05e7875..d744a99 100644 --- a/frontend/src/lib/ghostAcceptKey.ts +++ b/frontend/src/lib/ghostAcceptKey.ts @@ -60,12 +60,15 @@ export function matchesGhostWordAccept(eventKey: string, modifiers: GhostKeyModi // Ctrl+F 在远端 readline 是 forward-char,同时是 ghost 三键整段接受之一; // 无 ghost 待接受时它作为纯 forward-char 的价值远低于终端搜索。归属判定 // 收敛为纯函数:ghost 可见 → 接受键;传输占用(trz/zmodem 拥有输入流, -// 与 resolveTerminalInputRoute 同判)→ 一律放行不打断协议协商;其余 → -// 打开终端搜索。裸 → 与 Ctrl+E 不参与本判定(始终是接受键,行为不变)。 +// 与 resolveTerminalInputRoute 同判)→ 一律放行不打断协议协商;alternate +// 屏(vim 的 Ctrl+F 是向前翻页、less/tmux copy-mode 同理,#108 反馈三) +// → 放行远端,本地搜索交给可配置热键(默认 Ctrl+Shift+F)全场景兜底; +// 其余 → 打开终端搜索。裸 → 与 Ctrl+E 不参与本判定(始终是接受键,行为 +// 不变)。 export type CtrlFRoute = "ghost-accept" | "search" | "passthrough"; -export function resolveCtrlFRoute(options: { ghostVisible: boolean; transferBusy: boolean }): CtrlFRoute { - if (options.transferBusy) return "passthrough"; +export function resolveCtrlFRoute(options: { ghostVisible: boolean; transferBusy: boolean; alternateActive?: boolean }): CtrlFRoute { + if (options.transferBusy || options.alternateActive) return "passthrough"; return options.ghostVisible ? "ghost-accept" : "search"; }