Skip to content

Commit d36386e

Browse files
author
SqlRush
committed
Accept clipboard item string paste payloads
1 parent 7380558 commit d36386e

5 files changed

Lines changed: 39 additions & 2 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
| M7 初始 TUI 层 | 已完成轻量 terminal frame renderer、PromptInput 状态机、history 导航、ctrl-p/ctrl-n history navigation、shift-enter 多行输入、多行 prompt 行内 ctrl-a/ctrl-e/ctrl-u/ctrl-k 和 wrap/render/cursor、共享 kill ring、ctrl-b/ctrl-f/ctrl-u/ctrl-k/ctrl-w 行编辑、alt-b/alt-f/alt-d/alt-backspace word 编辑、ctrl-left/ctrl-right/alt-left/alt-right word motion、ctrl-y yank 和 alt-y yank-pop 初版、reverse-search cursor/word 编辑/kill/yank/yank-pop 初版、ctrl-c interrupt/双击退出事件、ctrl-d delete-forward/空输入双击退出事件、ctrl-l 重绘事件、ctrl-o/ctrl-t 全局切换事件、ctrl-g/ctrl-s/ctrl-x chord chat 事件、reverse-search 状态/渲染/脚本断言/空结果/选择回填/cursor 断言、paste/image hint 输入和 OSC ST/base64 filename 兼容、text/image pasted-content 引用/metadata 脚本断言/提交展开/history entry restoration、SGR mouse 解析、alternate terminal navigation key sequences including modified Home/End/Delete/PageUp/PageDown、滚轮滚动、修饰键滚轮/左键、左键拖动选择、viewport 半页/顶部/底部可配置滚动、viewport 点击选择和 dialog action 点击、focus/blur 事件、resize 视口保持、keybinding resolver/config/chord pending/null-unbind/key/action camelCase alias、JSON config loader 和 focus/mouse/paste/image key name 覆盖、vim insert/normal/j/k/word/WORD/ge/gE/line-local ^/$/0/|/I/A/D/quote/bracket text-object/yank/register/paste/delete/count/replace/undo/find/till/repeat/matching-pair %/dot-repeat/G/gg/toggle/join/open-line/indent/substitute 动作、normal-mode arrow/backspace/delete 映射和 operator linewise/字符范围、REPL screen、permission/task dialog builder、dialog kind/id routing/runtime/status line、runtime 到 REPL screen 的 dialog/status 同步、runtime-aware interaction script runner、prompt text/cursor/expanded/vim mode/register/task state/dialog result/runtime mutation/task bulk-cancel/permission cancel/keybinding mutation/status negative/snapshot negative/screen size/event-sequence/event-count/no-event/dialog-result-count/no-dialog-result 脚本断言、viewport 脚本断言、named-key 脚本输入、script JSON/JSONL/wrapper loader、script file runner 和 runtime/task camel field aliases、stale dialog race guard、cancel active、permission id/all cancellation、queued permission promotion、active task dialog refresh、task lifecycle/bulk-cancel 初版、idempotent alternate screen lifecycle/reset/reassert interactive 初版、mouse/focus/bracketed-paste terminal mode lifecycle/reconciliation、ANSI snapshot 基础、snapshot corpus write/compare/script-file compare/missing-baseline/diff/batch/strict unexpected-baseline 状态、scripted interaction runner/assertions/multi-key/text/paste/image/pasted-content metadata 初版、status/dialog/message components、viewport/selection |
3333
| 全量测试 | 当前 `go test ./...` 通过 |
3434

35+
M7 补充:interaction script paste payload 现在接受 ClipboardItem 风格的 `items[].getAsString`/`get_as_string` 以及 `stringData`/`textData` 文本字段,DOM clipboard 录制脚本可直接恢复 pasted text。
36+
3537
M7 补充:scripted task runtime payload 和 task expectation 现在接受 `taskID``jobId``runId``label``displayName``phase``taskState``message``currentStep``percent`/`percentage`/`pct` 等相邻字段,并支持数字 task ID 与数字字符串 progress。
3638

3739
M7 补充:scripted permission payload、dialog expectation、event、cancel-permission 和 dialog-result expectation 现在接受 `ID`/`ToolName`/`Actions``permissionID``requestID``toolUseID``operationID``operation``commandName``resourcePath``body``reasonText``allowedActions``buttons` 等相邻字段,并支持数字 request ID。

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ test/parity/ # golden tests against TS/official behavior
266266
- 本轮补充:interaction script touch payload 现在可从 `touches`/`targetTouches`/`changedTouches` 的首个 touch point 恢复坐标,`touchmove` 映射为 SGR drag motion,`touchcancel` 映射为 release,减少 DOM touch 录制 fixture 的手工改写。
267267
- 本轮补充:REPL dialog 鼠标处理现在忽略 SGR motion/drag button,只响应实际 press/click,避免 pointer/touch move 回放关闭 permission/task dialog。
268268
- 本轮补充:interaction script paste payload 现在接受 DOM `clipboardData`/`dataTransfer` 对象,可从 `text/plain``plainText``items[].text` 恢复 pasted text,减少 ClipboardEvent 录制 fixture 的手工改写。
269+
- 本轮补充:interaction script paste payload 现在也接受 ClipboardItem 风格的 `items[].getAsString`/`get_as_string` 和相邻 `stringData`/`textData` 文本字段,录制脚本不需要先把 clipboard item 扁平化成 `text`
269270
- 本轮补充:interaction script paste payload 现在也可从 DOM `clipboardData.items``dataTransfer.files` 中恢复 `image/*` file item 为 image paste,并避免把 image file 的 `data`/`base64` 内容误当成普通 pasted text。
270271
- 本轮补充:interaction script clipboard/dataTransfer image paste 现在优先读取 `items[].file``items[].blob``items[].getAsFile` 等嵌套 file payload,保留真实 filename、media type、base64 和 source path。
271272
- 本轮补充:interaction script resize payload 现在接受 DOM/window 尺寸别名 `innerWidth`/`innerHeight``clientWidth`/`clientHeight``offsetWidth`/`offsetHeight` 和 ResizeObserver 风格 `contentRect`/`target` wrapper。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ M7 progress now includes:
302302
- `internal/tui`: interaction script touch payloads now recover coordinates from the first `touches`/`targetTouches`/`changedTouches` touch point, map `touchmove` to SGR drag motion, and map `touchcancel` to release events for DOM touch recording fixtures.
303303
- `internal/tui`: REPL dialogs now ignore SGR motion/drag mouse buttons and only act on real press/click events, preventing pointer or touch move replay from closing permission/task dialogs.
304304
- `internal/tui`: interaction script paste payloads now accept DOM `clipboardData`/`dataTransfer` objects and recover pasted text from `text/plain`, `plainText`, and `items[].text` fields for ClipboardEvent recording fixtures.
305+
- `internal/tui`: interaction script paste payloads now also recover text from ClipboardItem-style `items[].getAsString`/`get_as_string` plus adjacent `stringData`/`textData` fields, so DOM clipboard recordings do not need to flatten item text first.
305306
- `internal/tui`: interaction script paste payloads now also recover `image/*` file items from DOM `clipboardData.items` and `dataTransfer.files` as image paste events while avoiding accidental base64-as-text insertion from image `data` payloads.
306307
- `internal/tui`: interaction script clipboard/dataTransfer image paste now prefers nested file payloads such as `items[].file`, `items[].blob`, and `items[].getAsFile`, preserving filename, media type, base64 data, and source path instead of stopping at the outer MIME.
307308
- `internal/tui`: interaction script resize payloads now accept DOM/window size aliases such as `innerWidth`/`innerHeight`, `clientWidth`/`clientHeight`, `offsetWidth`/`offsetHeight`, plus ResizeObserver-style `contentRect` and `target` wrappers.

internal/tui/script_aliases.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,12 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
896896
"plainText",
897897
"clipboard_text",
898898
"clipboardText",
899+
"get_as_string",
900+
"getAsString",
901+
"string_data",
902+
"stringData",
903+
"text_data",
904+
"textData",
899905
"items",
900906
"item",
901907
"clipboard_items",
@@ -1853,7 +1859,7 @@ func applyScriptStepActionAlias(step *ScriptStep, fields map[string]json.RawMess
18531859
step.Image = scriptClipboardImageJSONField(fields, "paste", "clipboard", "clipboard_data", "clipboardData", "data_transfer", "dataTransfer", "files", "items", "payload", "data", "value")
18541860
}
18551861
if step.Paste == "" {
1856-
step.Paste = scriptActionStringField(fields, "paste", "clipboard", "clipboard_data", "clipboardData", "data_transfer", "dataTransfer", "text/plain", "textPlain", "plain_text", "plainText", "clipboard_text", "clipboardText", "items", "item", "clipboard_items", "clipboardItems", "text", "content", "body", "message", "data", "payload", "value")
1862+
step.Paste = scriptActionStringField(fields, "paste", "clipboard", "clipboard_data", "clipboardData", "data_transfer", "dataTransfer", "text/plain", "textPlain", "plain_text", "plainText", "clipboard_text", "clipboardText", "get_as_string", "getAsString", "string_data", "stringData", "text_data", "textData", "items", "item", "clipboard_items", "clipboardItems", "text", "content", "body", "message", "data", "payload", "value")
18571863
}
18581864
case "status", "setstatus", "set-status", "statusline", "status-line":
18591865
if step.Status == "" {
@@ -3752,7 +3758,7 @@ func scriptActionStringSkipsImageLikeObject(fields map[string]json.RawMessage, o
37523758
if !scriptActionStringNamesIncludeClipboardPayload(objectNames) || !scriptImageLikeFields(fields) {
37533759
return false
37543760
}
3755-
return stringJSONField(fields, "text/plain", "textPlain", "plain_text", "plainText", "clipboard_text", "clipboardText", "text") == ""
3761+
return stringJSONField(fields, "text/plain", "textPlain", "plain_text", "plainText", "clipboard_text", "clipboardText", "get_as_string", "getAsString", "string_data", "stringData", "text_data", "textData", "text") == ""
37563762
}
37573763

37583764
func scriptActionStringNamesIncludeClipboardPayload(names []string) bool {

internal/tui/tui_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7324,6 +7324,33 @@ func TestRunInteractionScriptAcceptsNestedClipboardImageFiles(t *testing.T) {
73247324
}
73257325
}
73267326

7327+
func TestRunInteractionScriptAcceptsClipboardItemGetAsString(t *testing.T) {
7328+
steps, err := ParseInteractionScript([]byte(`[
7329+
{
7330+
"action": "paste",
7331+
"payload": {
7332+
"clipboardData": {
7333+
"items": [
7334+
{"kind": "string", "type": "text/plain", "getAsString": "from clipboard item"}
7335+
]
7336+
}
7337+
},
7338+
"expectPrompt": {
7339+
"text": "[Pasted text #1]",
7340+
"expandedText": "from clipboard item",
7341+
"pastedContentCount": 1
7342+
}
7343+
}
7344+
]`))
7345+
if err != nil {
7346+
t.Fatal(err)
7347+
}
7348+
screen := NewREPLScreen(40, 8, nil)
7349+
if _, err := RunInteractionScriptChecked(&screen, steps); err != nil {
7350+
t.Fatal(err)
7351+
}
7352+
}
7353+
73277354
func TestRunInteractionScriptAcceptsJSONFieldAliases(t *testing.T) {
73287355
var steps []ScriptStep
73297356
if err := json.Unmarshal([]byte(`[

0 commit comments

Comments
 (0)