feat(terminal): clipboard paste with bracketed-paste support
Pasting (Ctrl-V or browser menu) now sends clipboard text to the PTY.
- on:paste reads clipboard_data().get_data("text"); newlines normalized
to CR (the line-submit byte) via the pure, unit-tested prepare_paste()
- when the app enabled bracketed paste (DECSET 2004, exposed via the new
TerminalCore::bracketed_paste() proxy), the text is wrapped in
ESC[200~ / ESC[201~ so vim et al. don't auto-indent each pasted line
- pasting returns the view to live mode + scrolls to bottom, matching the
keydown handler
- Cargo.toml: add ClipboardEvent/DataTransfer (and explicit WheelEvent)
to the web-sys feature allowlist
- right-click is left to the browser's default menu (avoids requiring
clipboard-read permission); Ctrl-V is the primary path
Adds a prepare_paste unit test (14 terminal tests total) and marks paste
done in TERMINAL_ROADMAP.md. Also fixes a clippy manual_contains lint in
an existing test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
| `clear`(含 `ESC[3J` 清 scrollback) | `core.rs::contains_erase_scrollback` |
|
||||
| alt-screen 滚轮 → 方向键翻页 | `component.rs::handle_wheel` |
|
||||
| 基础按键:Ctrl-C/D/L、方向键、Enter/Tab/Esc/Backspace | `component.rs::map_key_to_terminal_input` |
|
||||
| 粘贴(Ctrl-V / 浏览器菜单,含 bracketed paste) | `component.rs::handle_paste` / `prepare_paste` |
|
||||
|
||||
---
|
||||
|
||||
@@ -36,14 +37,15 @@
|
||||
|
||||
### P0 — 复制 / 粘贴 + 文本选区 ⭐ 投入产出比最高
|
||||
|
||||
**现状**:完全没有。无法选中终端文本、无法粘贴。这是「能用 vs 好用」的分界线。
|
||||
**现状**:粘贴**已完成**(见下);选区 / 复制尚未做。
|
||||
|
||||
**计划实现**:
|
||||
1. **粘贴(先做,最简单)**
|
||||
- 监听 `on:paste`(`ClipboardEvent`),读 `clipboard_data().get_data("text")`。
|
||||
- 直接作为 `ClientTerminalMessage::Input` 发送。
|
||||
- 若应用开启 bracketed paste 模式(vt100 的 `screen().bracketed_paste()` 为真),把粘贴内容包进 `ESC[200~ … ESC[201~`,避免 vim 等把粘贴当连续输入触发自动缩进。
|
||||
2. **选区(较复杂)**
|
||||
1. ~~**粘贴(先做,最简单)**~~ ✅ **已完成**
|
||||
- `on:paste`(`ClipboardEvent`)读 `clipboard_data().get_data("text")`,归一换行为 `\r` 后作为 `Input` 发送。
|
||||
- bracketed paste:应用开启时(`core.bracketed_paste()`)包进 `ESC[200~ … ESC[201~`。
|
||||
- 实现于 `component.rs::handle_paste` + 纯函数 `prepare_paste`(有单元测试)。
|
||||
- 右键不拦截,走浏览器默认菜单(避免依赖剪贴板读权限);Ctrl-V 为主路径。
|
||||
2. **选区(较复杂,下一步)**
|
||||
- 因为渲染是 DOM(每行一个 `<div>`、每段一个 `<span>`),可优先尝试**浏览器原生选区**:给可见行允许 `user-select: text`,监听 `copy` 事件,从 `window.getSelection()` 取文本。
|
||||
- 但虚拟滚动只渲染可见窗口,跨窗口选区会断 → 需要把「逻辑行号 → 文本」的映射暴露给选区逻辑,从 `TerminalCore` 的 `history_rows + screen_rows` 直接取纯文本(已有 `row` → 文本的能力,测试里 `row_text` 即是)。
|
||||
- MVP 可只支持**可见范围内选区 + 复制**,跨滚动选区列为后续。
|
||||
|
||||
Reference in New Issue
Block a user