---
url: /wave-agent/specs/ui/input-editing-keys.md
description: 输入行编辑键：Ctrl+U/K/W/A/E 行编辑、空闲 Esc 双击清空输入、SSH/tmux 退格兼容与占位符删除支持
---

# 功能规格说明：输入编辑键与清空

**创建日期**：2026-08-01

## 用户场景与测试 *（必填）*

### 用户故事：原始 DEL 字符过滤，修复 SSH/tmux 退格失效（优先级：P1）

作为通过 SSH/tmux 远程操作 Wave CLI 的用户，我希望退格键在远程环境下可靠地删除字符，以便残留字符（如 `b/`）能够被正常清除而不是越删越多。

**为什么是这个优先级**：这是本次调研发现的根本原因。标准 ink 6.7.0 将单字节 `\x7f` 映射为 `key.delete`，但在 SSH/tmux 下退格会同时产生按键事件和原始 DEL 字符（`\x7f`），长按连续退格时还会聚合成多字节 chunk（如 `\x7f\x7f`），无法匹配任何键位模式，被 Wave 的粘贴判定当成"粘贴"从而把 DEL 字符字面插入输入框。对齐 Claude Code Issue #1853 的修复（`useTextInput.ts:442-465`）：当按键不是 backspace/delete 但输入包含 `\x7f` 时，将每个 DEL 作为一个同步退格操作处理。不修复则远程场景下的输入编辑始终不可用。

**独立测试**：通过 `useInput` 注入包含 `\x7f\x7f` 的原始输入 chunk（模拟 SSH 连续退格），验证输入框文本每次减少一个字符，而不是插入 `\x7f` 字面量。

**验收场景**：

1. **假设**输入框中有文本 `b/` 且光标在末尾，**当**收到包含 `\x7f` 的原始输入 chunk（非 backspace/delete 按键事件）时，**则**输入框文本变为 `b`，DEL 字符不进入文本。
2. **假设**输入框中有文本 `abc`，**当**一次收到 `\x7f\x7f` 聚合 chunk 时，**则**文本变为 `a`（每个 DEL 顺序执行一次退格）。
3. **假设**输入框为空，**当**收到 `\x7f` 原始输入时，**则**输入框保持为空且不报错。
4. **假设**正在执行正常的 backspace 按键事件（`key.backspace === true`），**当**按下退格键时，**则**行为不变，仍走既有的字符删除路径。

***

### 用户故事：Ctrl+U 删除到行首（优先级：P1）

作为用户，我希望按 Ctrl+U 删除光标前到行首的全部内容，以便在长输入或残留字符场景下能一键清空光标前内容，而不是反复按退格。

**为什么是这个优先级**：这是用户最初反馈中明确提到的编辑键（"Ctrl+U 都无法清除"）。当前 Wave 的 reducer 仅实现了 ctrl+v/r/b，缺少 emacs 风格的行编辑快捷键。Ctrl+U 是 CC（`useTextInput.ts:224-238`，`u → killToLineStart`）与终端惯例中最常用的"清行"键，单独实现即可覆盖主要场景。

**独立测试**：在输入框中输入 `abc` 并让光标位于行尾，按 Ctrl+U，验证光标前文本被删除、光标回到行首。

**验收场景**：

1. **假设**输入框中有 `prefix rest` 且光标位于 `rest` 之后，**当**按 Ctrl+U 时，**则**光标前内容（`prefix `）被删除，光标前的文本变为 `rest`。
2. **假设**光标已在行首，**当**按 Ctrl+U 时，**则**文本无变化，不报错。

***

### 用户故事：空闲状态下 Esc 双击清空输入（优先级：P1）

作为用户，我希望在空闲状态（无 AI 响应、无命令运行）下按 Esc 两次清空整个输入框，以便在无法正常退格的环境中有一个可靠的输入清理手段。

**为什么是这个优先级**：这是除退格修复外最直接的"可靠清空"手段，对齐 CC 的 Esc 双击清空（`useTextInput.ts:126-153`）。CC 的设计为状态互斥：Esc 中止语义仅绑定在"存在可中止任务"时（`useCancelRequest.ts:150-167`：`isEscapeActive = isContextActive && (canCancelRunningTask || hasQueuedCommands)`），空闲时 Esc 落到文本层执行双击清空。**约束：Esc 双击清空只作用于空闲状态，AI 运行时的 Esc 仍保持中止响应语义，不影响运行中的中断行为。**

**独立测试**：空闲状态下输入文本后连续按两次 Esc，验证第一次 Esc 出现"再次按 Esc 清空"提示、第二次 Esc 清空输入框；再在 AI 响应进行中按一次 Esc，验证仍立即中止响应。

**验收场景**：

1. **假设**空闲状态（`isLoading`/`isCommandRunning`/`isCompacting` 均为 false）且输入框有内容，**当**按第一次 Esc 时，**则**显示"再次按 Esc 清空输入"的提示，输入内容保持不变。
2. **假设**上述状态下已在 1 秒内按了第二次 Esc，**当**再次按 Esc 时，**则**输入框被清空，且原内容保存到输入历史（对齐 CC 的 `addToHistory(originalValue)`）。
3. **假设**第一次 Esc 后超过 1 秒未按第二次，**当**再次按 Esc 时，**则**视为新一轮双击，仅重新显示提示，不清空输入。
4. **假设**空闲状态且输入框为空，**当**按 Esc 时，**则**不产生提示，无副作用。
5. **假设**AI 正在响应（`isLoading === true`）或命令正在运行（`isCommandRunning === true`），**当**按一次 Esc 时，**则**保持现有中止响应语义（`abortMessage`），不触发双击清空逻辑。
6. **假设**AI 响应结束后回到空闲状态，**当**按 Esc 两次时，**则**恢复双击清空行为。

***

### 用户故事：补充 emacs 风格行编辑键 Ctrl+K/W/A/E（优先级：P2）

作为用户，我希望输入框支持 Ctrl+K（删除到行尾）、Ctrl+W（删除光标前一个词）、Ctrl+A/E（光标移到行首/行尾），以便获得与 Claude Code 和终端惯例一致的行编辑体验。

**为什么是这个优先级**：这些键与 Ctrl+U 同属 CC 的 emacs 编辑体系（`useTextInput.ts:224-238`），但对"清空输入"这一核心诉求不是必需，属于体验完整性补充，故列为 P2。

**独立测试**：分别在输入框验证 Ctrl+K 删除光标后内容、Ctrl+W 删除光标前一个词、Ctrl+A 光标到行首、Ctrl+E 光标到行尾。

**验收场景**：

1. **假设**输入框中有 `foo bar baz` 且光标位于 `bar` 中间，**当**按 Ctrl+K 时，**则**光标后到行尾的内容被删除。
2. **假设**输入框中有 `foo bar` 且光标位于行尾，**当**按 Ctrl+W 时，**则**删除光标前一个词 `bar`，文本变为 `foo `。
3. **假设**输入框中有 `foo bar` 且光标位于行尾，**当**按 Ctrl+A 时，**则**光标移到行首；**当**再按 Ctrl+E 时，**则**光标移回行尾。

***

### 用户故事：长文本/图片占位符参与删除键（优先级：P1）

作为粘贴了长文本（被折叠为 `[LongText#N]`）或附加了图片（`[Image #N]`）的用户，我希望 Backspace 能整块删除占位符、Ctrl+U/K/W 将其作为普通字符串参与切片删除，以便粘贴的长文本能被编辑键可靠清除，而不是只能按 Enter 提交或留下残缺占位符片段。

**为什么是这个优先级**：当前 Backspace 对 `[LongText#N]` 逐字符删除，一次只删一个字符，且会留下 `[LongText#1` 这类残缺文本，随后按 Enter 会把残缺占位符当字面文本发送，原文（已存入 `longTextMap`）丢失。对齐 Claude Code：Backspace 走 `deleteTokenBefore` 正则整块删除（`Cursor.ts:937-969`，正则覆盖 `[Pasted text #N]`/`[Image #N]` 等 token，且仅当光标位于 token 末尾、后随空白或行尾时触发）；Ctrl+U/K/W 为纯文本切片/词删除（`useTextInput.ts:224-238` 的 `killToLineStart`/`killToLineEnd`/`killWordBefore`），占位符就是 input text 里的普通字符串，光标位于占位符中间时切片会留下残缺片段（与 CC 一致）；原始 DEL 字符（`\x7f`，SSH/tmux 退格）同样按 token 优先删除（`useTextInput.ts:442-465`）。不修复则长文本粘贴后编辑不可用。

**独立测试**：粘贴超长文本折叠为 `[LongText#1]` 后按 Backspace，验证一次删除整个占位符且 `longTextMap` 条目被清理；光标位于占位符中间时 Backspace 仅删除单字符；Ctrl+U/K/W 按普通文本切片删除。

**验收场景**：

1. **假设**输入框中有 `prefix [LongText#1]` 且光标位于占位符之后（行尾），**当**按 Backspace 时，**则**整个 `[LongText#1]` 占位符被删除，文本变为 `prefix `，且 `longTextMap` 中对应条目被清理。
2. **假设**输入框中有 `[LongText#1] suffix` 且光标位于占位符之后（占位符后随空白），**当**按 Backspace 时，**则**整个 `[LongText#1]` 占位符被删除，文本变为 ` suffix`。
3. **假设**光标位于 `[LongText#1]` 占位符中间，**当**按 Backspace 时，**则**仅删除光标前一个字符，不触发整块删除（对齐 CC 的 word-boundary 守卫）。
4. **假设**输入框中有 `[LongText#1]x`（占位符后紧跟非空白字符）且光标位于 `]` 之后，**当**按 Backspace 时，**则**仅删除光标前一个字符（CC 仅当光标后是空白或行尾时触发整块删除）。
5. **假设**输入框中有 `[LongText#1]` 且光标位于行尾，**当**按 Ctrl+U 时，**则**占位符作为普通字符串随整行删除，输入框清空。
6. **假设**输入框中有 `hello [LongText#1]` 且光标位于行尾，**当**按 Ctrl+W 时，**则**删除光标前一个词（含无空格的整个占位符），文本变为 `hello `。
7. **假设**光标位于 `[LongText#1]` 占位符中间，**当**按 Ctrl+K 时，**则**纯文本切片删除光标后内容，占位符可能被切成残缺片段（如 `[Long`），与 CC 行为一致。
8. **假设**输入框中有 `[LongText#1]` 且光标位于行尾，**当**收到含 `\x7f` 的原始 DEL chunk 时，**则**每个 DEL 按 token 优先整块删除占位符（对齐 CC `useTextInput.ts:442-465`）。

***

### 边界情况

* **粘贴与退格判定**：`isPasteOperation`（输入长度 > 1 且不含退格语义）与 `\x7f` 过滤需要互斥——包含 `\x7f` 的多字符 chunk 必须走退格路径而非粘贴路径。
* **Ctrl 组合键与现有快捷键冲突**：Ctrl+A/E 与终端行编辑默认行为一致；Ctrl+C 保留现有中止语义不纳入本次范围。
* **历史导航**：Esc 双击清空保存原内容到历史后，上/下方向键应能找回该内容。
* **长文本/图片占位符**：`[LongText#N]`/`[Image #N]` 是输入框中的普通字符串。Ctrl+U/K/W 走纯文本切片/词删除（光标在占位符中间时留下残缺片段，与 CC 一致）；Backspace 与原始 DEL 走 `deleteTokenBefore` 正则整块删除（对齐 CC `Cursor.ts:937-969`）——光标位于占位符末尾且后随空白或行尾时触发，并清理 `longTextMap` 条目。
* **待澄清**：空闲状态下输入框有内容但存在排队消息（`hasQueuedMessages`）时，Esc 是否应中止排队消息？\[待澄清]

## 假设

* 空闲状态定义为 `isLoading`、`isCommandRunning`、`isCompacting` 均为 false。
* "再次按 Esc 清空"提示沿用项目内现有提示机制（若无 toast 机制，可用输入框上方提示行或状态栏文本呈现）。
* 对齐 Claude Code 的互斥设计：Esc 的中止语义与双击清空语义互斥，由"是否有正在运行的任务"决定，不共存。
