fix(terminal): robust Ctrl/paste handling + alt-screen wheel coalescing
Keyboard input fixes addressing reported regressions: - Pressing Ctrl alone no longer sends SIGINT: key_to_bytes now guards on a single-character key, so modifier keys "Control"/"Shift"/"Meta" are not misread as Ctrl-C (0x03). - Ctrl-Shift-C copy restored: the copy branch runs before the generic Ctrl+letter suppression so the clipboard user-gesture stays intact. - Ctrl+C copies when a selection exists (VS Code / Windows Terminal behaviour); without a selection it still sends SIGINT. - Ctrl+V / Ctrl-Shift+V paste via navigator.clipboard.readText() — reliable across browsers that don't fire paste on a non-contenteditable div; on:paste retained for right-click / menu paste. - Bare Ctrl+letter (no Shift/Alt/Meta) is preventDefault'd so the browser stops intercepting Ctrl-R/F/A etc. (Ctrl-W/T/N stay browser-reserved and cannot be intercepted by any web page). - Filter Ctrl-Z (0x1a) / Ctrl-\ (0x1c) so stty SIGTSTP/SIGQUIT don't surprise-kill the foreground process; Ctrl-C (0x03) is kept. Alt-screen wheel: - Coalesce multiple wheel events per animation frame into a single arrow-key batch (Rc<Cell> accumulator + requestAnimationFrame), so one trackpad swipe pages at most one viewport instead of flying past. Tests: 18/18 pass, clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
| 宽字符 / 组合字符网格对齐 | `core.rs` `TerminalSegment.cols` |
|
||||
| resize / reflow | `core.rs::resize` |
|
||||
| `clear`(含 `ESC[3J` 清 scrollback) | `core.rs::contains_erase_scrollback` |
|
||||
| alt-screen 滚轮 → 方向键翻页 | `component.rs::handle_wheel` |
|
||||
| alt-screen 滚轮 → 方向键翻页(rAF 合并多次 wheel 事件,单次滑动最多一屏) | `component.rs::handle_wheel` |
|
||||
| 基础按键:Ctrl-C/D/L、方向键、Enter/Tab/Esc/Backspace | `component.rs::map_key_to_terminal_input` |
|
||||
| 完整键表(F1–F12/Home/End/PageUp/Down/Ctrl A–Z/Alt-key) | `component.rs::key_to_bytes` |
|
||||
| application-cursor 模式(DECCKM,方向键切 SS3) | `component.rs::cursor_seq` + `TerminalCore::application_cursor` |
|
||||
@@ -52,7 +52,8 @@
|
||||
- 选区按**逻辑行列坐标**跟踪(`Selection { anchor, head }`),鼠标 `mousedown/move/up` 拖拽更新;`point_to_cell` 做像素→单元格换算(含 padding/scroll)。
|
||||
- 高亮 overlay:`selection_rects` 算每行矩形,渲染在虚拟滚动层内(共享 translateY,跟随滚动),不拆分文本段。
|
||||
- 复制取 `TerminalCore::selection_text(start, end)` —— 直接从 `history_rows + screen_cache` 数据模型按行列取文本,**跨滚动、输出流动时都正确**(不依赖 DOM)。
|
||||
- **Ctrl-Shift-C** 复制(Ctrl-C 保持 SIGINT);打字清除选区。
|
||||
- **Ctrl-Shift-C** 复制(Ctrl-C 保持 SIGINT);**当选区存在时 Ctrl+C 优先复制**(与 VS Code / Windows Terminal 一致),无选区时才发送 SIGINT。
|
||||
- 打字清除选区。
|
||||
- 实现于 `component.rs`(`Selection`/`selection_rects`/`point_to_cell`/鼠标处理)+ `core.rs::selection_text` + `core.rs::TerminalRow::text_in_cols`,均有单元测试。
|
||||
3. **快捷键**:Ctrl-Shift-C 复制 / Ctrl-V 粘贴。
|
||||
|
||||
@@ -71,14 +72,14 @@
|
||||
- Ctrl A..Z → 0x01..0x1a;以及 `@[\\]^_` 变体(→ 0x00/0x1b/0x1c/0x1d/0x1e/0x1f)。
|
||||
- Alt+char → `ESC char`(标准 meta-as-ESC 编码)。
|
||||
2. **application-cursor 模式(DECCKM)**:`cursor_seq(byte, app_cursor)` 助手;为真时方向键 / Home / End 发 SS3(`ESC O X`),否则 CSI(`ESC [ X`)。`handle_keydown` 和 `handle_wheel` 都据此切换。
|
||||
3. **测试**:`key_to_bytes_table` 单测覆盖整套键表,17 个 terminal 测试全过、clippy 干净。
|
||||
3. **浏览器快捷键仲裁**:`handle_keydown` 对 bare `Ctrl+字母`(无 Shift/Alt/Meta)调用 `preventDefault()`,阻止浏览器吃掉 `Ctrl-R` 刷新、`Ctrl-F` 查找、`Ctrl-A` 全选等。`Ctrl+Shift` 组合(如 `Ctrl-Shift-C` 复制)和 `Ctrl+Alt`/`Ctrl+Meta` 组合保留给浏览器/系统。
|
||||
|
||||
**有意保留的"看起来像 bug"行为**(与真终端/xterm 一致):
|
||||
- `Ctrl-C` (0x03) → SIGINT 中断前台进程
|
||||
- `Ctrl-Z` (0x1a) → SIGTSTP 暂停前台进程(shell 提示符立刻返回)
|
||||
- `Ctrl-\` (0x1c) → SIGQUIT 杀掉前台进程(shell 提示符返回)
|
||||
**浏览器预留键(硬限制,无法拦截)**:`Ctrl-W`(关标签)、`Ctrl-T`(新标签)、`Ctrl-N`(新窗口)从 Chrome 4 起被浏览器保留,`preventDefault()` 对它们无效,浏览器在 `keydown` 到达页面前就消费了。这是所有网页终端的共同限制(xterm.js 同样如此),只能在浏览器之外解决(系统全局快捷键 / 扩展 / Electron 包装)。readline 退格删词可用 `Alt+Backspace` 替代 `Ctrl-W`。
|
||||
4. **测试**:`key_to_bytes_table` 单测覆盖整套键表,18 个 terminal 测试全过、clippy 干净。
|
||||
|
||||
这些是 bash 等的 `stty` 默认行为(intr/quit/susp),由内核 tty 纪律触发,与 xterm/SSH 进真终端一样——不是本组件 bug,也按用户确认保留。
|
||||
**有意调整的行为**(出于浏览器终端的可用性,与原生 xterm 不同):
|
||||
- `Ctrl-C` (0x03) → 无选区时发 SIGINT 中断前台进程;**有选区时优先复制**,不发送 0x03。
|
||||
- `Ctrl-Z` (0x1a) 与 `Ctrl-\` (0x1c) → 被前端过滤为 `None`,不发送到 PTY。原因是这两个键在默认 `stty` 下会触发 SIGTSTP / SIGQUIT,在浏览器标签页里极易误触导致前台进程被挂起或杀掉;需要这两个信号的用户可用 shell 内建命令(如 `kill -STOP` / `kill -QUIT`)替代。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user