Skip to content

Commit 5529c53

Browse files
author
SqlRush
committed
Replay DOM input deletion events
1 parent 5833275 commit 5529c53

4 files changed

Lines changed: 65 additions & 1 deletion

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,8 @@ M7 补充:prompt history `LogEntry` 读取现在接受 `sessionID`/`session`/`
614614

615615
本轮补充:scripted interaction action replay 现在接受 DOM `beforeinput`/`input` event 的 `data` payload,`insertText` 等文本输入进入 prompt typing,`insertFromPaste`/drop variants 进入现有 pasted-content 路径。
616616

617+
本轮补充:scripted interaction DOM input replay 现在会把 `deleteContentBackward``deleteWordBackward``deleteHardLineForward``insertLineBreak``inputType` 映射到已有 prompt key action,覆盖浏览器录制的删除和换行事件。
618+
617619
本轮补充:scripted interaction step 现在接受 `expect`/`expected`/`assertions`/`checks`/`verify`/`then`/`after` 等 expectation wrapper object,可把嵌套的 prompt/event/dialog/snapshot/screen/task/vim/viewport 断言映射到已有 `expect*` 字段。
618620

619621
本轮补充:scripted interaction expectation wrapper 现在也接受 assertion/check 数组,数组元素可用 `type`/`kind`/`name`/`target` 等 discriminator 搭配 `value`/`payload` 声明 prompt/event/dialog/snapshot/screen/task/vim/viewport 断言,减少官方脚本 fixture 的结构改写成本。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ M7 progress now includes:
281281
- `internal/tui`: interaction script key event objects now treat DOM `keyup`/`keyUp`/`key-release` payloads as no-op replay events, so browser/Playwright recordings that include both keydown and keyup do not duplicate prompt input.
282282
- `internal/tui`: interaction script key event replay now also skips DOM `Dead`/`Process`/IME key names, `isComposing` payloads, and composition event types, preventing IME/dead-key recording artifacts from being inserted as literal prompt text.
283283
- `internal/tui`: interaction script action replay now accepts DOM `beforeinput`/`input` events with `data` payloads, mapping text insertion to prompt typing and `insertFromPaste`/drop variants to the existing pasted-content path.
284+
- `internal/tui`: interaction script DOM input replay now maps deletion and line-break `inputType` values such as `deleteContentBackward`, `deleteWordBackward`, `deleteHardLineForward`, and `insertLineBreak` onto existing prompt key actions.
284285
- `internal/tui`: interaction script mouse payloads now infer release state from event type aliases such as `mouseup`, `pointerUp`, and `touchend`, while explicit release booleans still take precedence.
285286
- `internal/tui`: interaction script mouse payloads now infer SGR wheel buttons from DOM/compact wheel inputs such as `wheel`, `mousewheel`, `scrollUp`/`scrollDown`, `direction`, `deltaY`, and legacy `wheelDelta`, allowing recorded wheel events to drive viewport scrolling.
286287
- `internal/tui`: interaction script mouse payloads now accept DOM `which` and `buttons`/`buttonState` bitmasks and map them to SGR left/middle/right buttons so recorded secondary or auxiliary clicks are not treated as primary clicks.

internal/tui/script_aliases.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1831,7 +1831,11 @@ func applyScriptStepActionAlias(step *ScriptStep, fields map[string]json.RawMess
18311831
step.Keys = append(step.Keys, scriptActionStringListField(fields, "keys", "sequence", "key_sequence", "keySequence", "shortcuts", "data", "payload", "value")...)
18321832
}
18331833
case "text", "type", "typetext", "type-text", "input", "beforeinput", "before-input", "inputevent", "input-event", "dominput", "dom-input", "inputtext", "textinput", "insert", "inserttext", "write", "writetext":
1834-
if scriptDOMInputEventIsPaste(fields) {
1834+
if keys := scriptDOMInputEventKeys(fields); len(keys) > 0 {
1835+
if step.Key == "" && len(step.Keys) == 0 {
1836+
step.Keys = append(step.Keys, keys...)
1837+
}
1838+
} else if scriptDOMInputEventIsPaste(fields) {
18351839
if step.Paste == "" {
18361840
step.Paste = scriptActionStringField(fields, "data", "text", "input", "content", "body", "message", "payload", "value")
18371841
}
@@ -1928,6 +1932,30 @@ func scriptDOMInputEventIsPaste(fields map[string]json.RawMessage) bool {
19281932
}
19291933
}
19301934

1935+
func scriptDOMInputEventKeys(fields map[string]json.RawMessage) []string {
1936+
inputType := canonicalScriptStepAction(stringJSONField(fields, "inputType", "input_type", "input-type", "inputKind", "input_kind"))
1937+
switch inputType {
1938+
case "insertlinebreak", "insert-line-break", "insertparagraph", "insert-paragraph":
1939+
return []string{"shift-enter"}
1940+
case "deletecontentbackward", "delete-content-backward":
1941+
return []string{"backspace"}
1942+
case "deletecontentforward", "delete-content-forward", "deletecontent", "delete-content":
1943+
return []string{"delete"}
1944+
case "deletewordbackward", "delete-word-backward":
1945+
return []string{"ctrl-w"}
1946+
case "deletewordforward", "delete-word-forward":
1947+
return []string{"alt-d"}
1948+
case "deletesoftlinebackward", "delete-soft-line-backward", "deletehardlinebackward", "delete-hard-line-backward":
1949+
return []string{"ctrl-u"}
1950+
case "deletesoftlineforward", "delete-soft-line-forward", "deletehardlineforward", "delete-hard-line-forward":
1951+
return []string{"ctrl-k"}
1952+
case "deleteentiresoftline", "delete-entire-soft-line":
1953+
return []string{"ctrl-u", "ctrl-k"}
1954+
default:
1955+
return nil
1956+
}
1957+
}
1958+
19311959
func scriptPermissionRequestActionField(fields map[string]json.RawMessage) *PermissionRequest {
19321960
for _, raw := range scriptActionRawFields(fields, "request", "permission", "permission_request", "permissionRequest") {
19331961
if request, ok := scriptPermissionRequestFromJSON(raw, 0); ok {

internal/tui/tui_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7565,6 +7565,39 @@ func TestRunInteractionScriptAcceptsDOMInputEvents(t *testing.T) {
75657565
}
75667566
}
75677567

7568+
func TestRunInteractionScriptAcceptsDOMInputDeletionEvents(t *testing.T) {
7569+
steps, err := ParseInteractionScript([]byte(`[
7570+
{"text":"abc","expectPrompt":{"text":"abc","cursor":3}},
7571+
{"type":"beforeinput","inputType":"deleteContentBackward","expectPrompt":{"text":"ab","cursor":2}},
7572+
{"key":"enter","expectEvent":{"type":"prompt_submitted","value":"ab"},"expectPrompt":{"empty":true}},
7573+
{"text":"abc","expectPrompt":{"text":"abc","cursor":3}},
7574+
{"key":"home","expectPrompt":{"text":"abc","cursor":0}},
7575+
{"type":"input","inputType":"deleteContentForward","expectPrompt":{"text":"bc","cursor":0}},
7576+
{"key":"enter","expectEvent":{"type":"prompt_submitted","value":"bc"},"expectPrompt":{"empty":true}},
7577+
{"text":"one two","expectPrompt":{"text":"one two","cursor":7}},
7578+
{"type":"beforeinput","inputType":"deleteWordBackward","expectPrompt":{"text":"one ","cursor":4}},
7579+
{"type":"input","inputType":"insertLineBreak","expectPrompt":{"text":"one \n","cursor":5}},
7580+
{"text":"tail","expectPrompt":{"text":"one \ntail","cursor":9}},
7581+
{"type":"beforeinput","inputType":"deleteHardLineBackward","expectPrompt":{"text":"one \n","cursor":5}},
7582+
{"text":"tail","expectPrompt":{"text":"one \ntail","cursor":9}},
7583+
{"key":"home","expectPrompt":{"text":"one \ntail","cursor":5}},
7584+
{"type":"beforeinput","inputType":"deleteHardLineForward","expectPrompt":{"text":"one \n","cursor":5}}
7585+
]`))
7586+
if err != nil {
7587+
t.Fatal(err)
7588+
}
7589+
screen := NewREPLScreen(40, 8, nil)
7590+
result, err := RunInteractionScriptChecked(&screen, steps)
7591+
if err != nil {
7592+
t.Fatal(err)
7593+
}
7594+
if len(result.Events) != 2 ||
7595+
result.Events[0].Type != ScreenEventPromptSubmitted || result.Events[0].Value != "ab" ||
7596+
result.Events[1].Type != ScreenEventPromptSubmitted || result.Events[1].Value != "bc" {
7597+
t.Fatalf("events = %#v", result.Events)
7598+
}
7599+
}
7600+
75687601
func TestRunInteractionScriptAcceptsPressFieldAliases(t *testing.T) {
75697602
steps, err := ParseInteractionScript([]byte(`[
75707603
{"press":"a","expectPrompt":{"text":"a"}},

0 commit comments

Comments
 (0)