Skip to content

Commit a59c329

Browse files
author
SqlRush
committed
Accept wrapped resize alias fields
1 parent b4fed79 commit a59c329

5 files changed

Lines changed: 198 additions & 18 deletions

File tree

docs/cc-100-roadmap.md

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

599599
本轮补充:interaction script 的 resize/terminalSize/screenSize action payload 现在递归解包 `value`/`payload`/`data`/`resource`/`attributes`/`properties`/`attrs`/`edge.node` 等 wrapper,JSON:API/GraphQL fixture 中的 columns/rows 可直接驱动 screen resize。
600600

601+
本轮补充:interaction script 的 direct resize 数字 alias 字段现在也递归解包 wrapper;`resizeWidth`/`resizeHeight``screenWidth`/`screenHeight` 和 terminal width/height 相邻别名可从 wrapped `value``columns``rows` 中恢复尺寸,避免 direct field fixture 在 int decode 阶段失败。
602+
601603
本轮补充:interaction script action/type/kind/name/operation 动作判别字段现在也接受 compact/camel event/media aliases,包括 `focusIn``focusOut``mouseEvent``pasteImage``imagePaste`
602604

603605
本轮补充:interaction script 的 mouseEvent/pasteImage action payload 现在递归解包 JSON:API/GraphQL-style `resource`/`attributes`/`properties`/`attrs`/`edge.node` wrapper,wrapped mouse 坐标/按钮和 image filename/media/content 可直接驱动 dialog click 与 image paste。

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ test/parity/ # golden tests against TS/official behavior
310310
- 本轮补充:interaction script action/type/kind/name/operation 动作判别字段接受 compact/camel fixture aliases,包括 `typeText``inputText``insertText``keyPress``pressKey``keySequence``pasteText``pastedText``clipboardText``setStatus``statusLine``terminalSize``screenSize`
311311
- 本轮补充:interaction script 的 typeText/pasteText/setStatus/snapshot 字符串 action payload 现在递归解包 JSON:API/GraphQL-style wrapper,`payload.resource.attributes.text``edge.node.attrs.content``resource.attributes.message` 和 wrapped snapshot `name` 可直接驱动 prompt、paste、status 与 snapshot capture。
312312
- 本轮补充:interaction script resize/terminalSize/screenSize action payload 现在递归解包 `value`/`payload`/`data`/`resource`/`attributes`/`properties`/`attrs`/`edge.node` 等 wrapper,官方/API fixture 可把 columns/rows 放在 JSON:API 或 GraphQL envelope 内。
313+
- 本轮补充:interaction script 的 direct resize 数字 alias 字段现在也递归解包 wrapper;`resizeWidth`/`resizeHeight``screenWidth`/`screenHeight` 和 terminal width/height 相邻别名可从 wrapped `value``columns``rows` 中恢复尺寸。
313314
- 本轮补充:interaction script action/type/kind/name/operation 动作判别字段继续接受 compact/camel event/media aliases,包括 `focusIn``focusOut``mouseEvent``pasteImage``imagePaste`
314315
- 本轮补充:interaction script mouseEvent/pasteImage action payload 现在递归解包 JSON:API/GraphQL-style `resource`/`attributes`/`properties`/`attrs`/`edge.node` wrapper,wrapped mouse 坐标/按钮和 image filename/media/content 可直接驱动 dialog click 与 image paste。
315316
- 本轮补充:interaction script action/type/kind/name/operation 动作判别字段也可驱动 runtime/dialog mutation,支持 `requestPermission``taskStatus``showTasks``cancelTasks``removeTask``showDialog` 等动作,并从 `value`/`payload`/`data`/`body` 载荷解析对象、ID 或取消原因。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ M7 progress now includes:
290290
- `internal/tui`: scripted key and key-sequence action payloads now recursively unwrap JSON:API/GraphQL-style wrappers, preserving wrapped single keys and key sequence arrays from API-shaped interaction fixtures.
291291
- `internal/tui`: direct scripted key alias fields such as `key`, `keyPress`, and `keyPresses` now accept JSON:API/GraphQL-style wrapper objects without failing strong string/list decoding.
292292
- `internal/tui`: direct scripted string alias fields such as `text`, `pasteText`, `setStatus`, and `snapshotName` now accept JSON:API/GraphQL-style wrapper objects without failing strong scalar decoding.
293+
- `internal/tui`: direct scripted resize aliases such as `resizeWidth`/`resizeHeight` and `screenWidth`/`screenHeight` now recursively unwrap numeric JSON:API/GraphQL-style wrapper objects.
293294
- `internal/tui`: terminal key parsing now accepts CSI-u/kitty keyboard protocol sequences for existing ctrl/alt editing keys, shift-enter, shift-tab, and printable shift-only runes.
294295
- `internal/tui`: terminal CSI-u/kitty keyboard parsing now also accepts colon-suffixed alternate codepoint and modifier event-type fields such as `CSI 97:65;5:1u`.
295296
- `internal/tui`: terminal CSI-u/kitty keyboard parsing now also accepts base/unmodified sequences such as `CSI 97u` and `CSI 13;1u`, mapping printable runes plus Enter, Tab, Esc, and Backspace instead of treating ordinary extended-key reports as unknown.

internal/tui/script_aliases.go

Lines changed: 144 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,10 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
439439
OpenTasksCamel *json.RawMessage `json:"openTasks"`
440440
ShowTasks *json.RawMessage `json:"show_tasks"`
441441
ShowTasksCamel *json.RawMessage `json:"showTasks"`
442-
ResizeWidth *int `json:"resize_width"`
443-
ResizeWidthCamel *int `json:"resizeWidth"`
444-
ResizeHeight *int `json:"resize_height"`
445-
ResizeHeightCamel *int `json:"resizeHeight"`
442+
ResizeWidth *json.RawMessage `json:"resize_width"`
443+
ResizeWidthCamel *json.RawMessage `json:"resizeWidth"`
444+
ResizeHeight *json.RawMessage `json:"resize_height"`
445+
ResizeHeightCamel *json.RawMessage `json:"resizeHeight"`
446446
SnapshotName *json.RawMessage `json:"snapshot_name"`
447447
SnapshotNameCamel *json.RawMessage `json:"snapshotName"`
448448
Focus *bool `json:"focus"`
@@ -716,17 +716,40 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
716716
if value, ok := scriptRuntimeMutationBoolField(fieldMap, "open_tasks_dialog", "openTasksDialog", "open_tasks", "openTasks", "show_tasks", "showTasks"); ok {
717717
step.OpenTasksDialog = value
718718
}
719-
if fields.ResizeWidth != nil {
720-
step.ResizeWidth = *fields.ResizeWidth
721-
}
722-
if fields.ResizeWidthCamel != nil {
723-
step.ResizeWidth = *fields.ResizeWidthCamel
724-
}
725-
if fields.ResizeHeight != nil {
726-
step.ResizeHeight = *fields.ResizeHeight
719+
if step.ResizeWidth <= 0 {
720+
if width, ok := scriptNamedIntField(rawFieldMap,
721+
[]string{"ResizeWidth", "resize_width", "resizeWidth"},
722+
"width",
723+
"w",
724+
"columns",
725+
"cols",
726+
"screen_width",
727+
"screenWidth",
728+
"terminal_width",
729+
"terminalWidth",
730+
"resize_width",
731+
"resizeWidth",
732+
"value",
733+
); ok {
734+
step.ResizeWidth = width
735+
}
727736
}
728-
if fields.ResizeHeightCamel != nil {
729-
step.ResizeHeight = *fields.ResizeHeightCamel
737+
if step.ResizeHeight <= 0 {
738+
if height, ok := scriptNamedIntField(rawFieldMap,
739+
[]string{"ResizeHeight", "resize_height", "resizeHeight"},
740+
"height",
741+
"h",
742+
"rows",
743+
"screen_height",
744+
"screenHeight",
745+
"terminal_height",
746+
"terminalHeight",
747+
"resize_height",
748+
"resizeHeight",
749+
"value",
750+
); ok {
751+
step.ResizeHeight = height
752+
}
730753
}
731754
if size := scriptSizeJSONField(fieldMap, "resize", "resize_to", "resizeTo", "screen_size", "screenSize", "terminal_size", "terminalSize", "size"); size != nil {
732755
if size.Width > 0 {
@@ -737,13 +760,38 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
737760
}
738761
}
739762
if step.ResizeWidth <= 0 {
740-
if width := intPtrJSONField(fieldMap, "width", "columns", "cols", "screen_width", "screenWidth", "terminal_width", "terminalWidth"); width != nil {
741-
step.ResizeWidth = *width
763+
if width, ok := scriptNamedIntField(rawFieldMap,
764+
[]string{"width", "columns", "cols", "screen_width", "screenWidth", "terminal_width", "terminalWidth"},
765+
"width",
766+
"w",
767+
"columns",
768+
"cols",
769+
"screen_width",
770+
"screenWidth",
771+
"terminal_width",
772+
"terminalWidth",
773+
"resize_width",
774+
"resizeWidth",
775+
"value",
776+
); ok {
777+
step.ResizeWidth = width
742778
}
743779
}
744780
if step.ResizeHeight <= 0 {
745-
if height := intPtrJSONField(fieldMap, "height", "rows", "screen_height", "screenHeight", "terminal_height", "terminalHeight"); height != nil {
746-
step.ResizeHeight = *height
781+
if height, ok := scriptNamedIntField(rawFieldMap,
782+
[]string{"height", "rows", "screen_height", "screenHeight", "terminal_height", "terminalHeight"},
783+
"height",
784+
"h",
785+
"rows",
786+
"screen_height",
787+
"screenHeight",
788+
"terminal_height",
789+
"terminalHeight",
790+
"resize_height",
791+
"resizeHeight",
792+
"value",
793+
); ok {
794+
step.ResizeHeight = height
747795
}
748796
}
749797
if step.SnapshotName == "" {
@@ -1719,6 +1767,78 @@ func scriptNamedStringField(fields map[string]json.RawMessage, directNames []str
17191767
return ""
17201768
}
17211769

1770+
func scriptNamedIntField(fields map[string]json.RawMessage, directNames []string, nestedNames ...string) (int, bool) {
1771+
for _, name := range directNames {
1772+
raw, ok := fields[name]
1773+
if !ok {
1774+
continue
1775+
}
1776+
if value, ok := scriptActionIntFromJSON(raw, nestedNames, 0); ok {
1777+
return value, true
1778+
}
1779+
}
1780+
return 0, false
1781+
}
1782+
1783+
func scriptActionIntFromJSON(raw json.RawMessage, names []string, depth int) (int, bool) {
1784+
raw = bytes.TrimSpace(raw)
1785+
if len(raw) == 0 || bytes.Equal(raw, []byte("null")) {
1786+
return 0, false
1787+
}
1788+
if value, ok := scriptIntFromJSON(raw); ok {
1789+
return value, true
1790+
}
1791+
if depth >= 8 {
1792+
return 0, false
1793+
}
1794+
if raw[0] == '[' {
1795+
var items []json.RawMessage
1796+
if err := json.Unmarshal(raw, &items); err != nil {
1797+
return 0, false
1798+
}
1799+
for _, item := range items {
1800+
if value, ok := scriptActionIntFromJSON(item, names, depth+1); ok {
1801+
return value, true
1802+
}
1803+
}
1804+
return 0, false
1805+
}
1806+
if raw[0] != '{' {
1807+
return 0, false
1808+
}
1809+
fields := map[string]json.RawMessage{}
1810+
if err := json.Unmarshal(raw, &fields); err != nil {
1811+
return 0, false
1812+
}
1813+
if value := intPtrJSONField(fields, names...); value != nil {
1814+
return *value, true
1815+
}
1816+
for _, name := range scriptRuntimePayloadWrapperNames(names...) {
1817+
nested, ok := fields[name]
1818+
if !ok {
1819+
continue
1820+
}
1821+
if value, ok := scriptActionIntFromJSON(nested, names, depth+1); ok {
1822+
return value, true
1823+
}
1824+
}
1825+
return 0, false
1826+
}
1827+
1828+
func scriptIntFromJSON(raw json.RawMessage) (int, bool) {
1829+
var value int
1830+
if err := json.Unmarshal(raw, &value); err == nil {
1831+
return value, true
1832+
}
1833+
var stringValue string
1834+
if err := json.Unmarshal(raw, &stringValue); err == nil {
1835+
if value, err := strconv.Atoi(strings.TrimSpace(stringValue)); err == nil {
1836+
return value, true
1837+
}
1838+
}
1839+
return 0, false
1840+
}
1841+
17221842
func scriptActionStringListField(fields map[string]json.RawMessage, names ...string) []string {
17231843
for _, name := range append([]string{"value", "payload", "data", "body"}, names...) {
17241844
raw, ok := fields[name]
@@ -2215,6 +2335,12 @@ func stripScriptStepRawScalarAliasFields(data []byte) []byte {
22152335
"status",
22162336
"SnapshotName",
22172337
"snapshotName",
2338+
"ResizeWidth",
2339+
"resizeWidth",
2340+
"resize_width",
2341+
"ResizeHeight",
2342+
"resizeHeight",
2343+
"resize_height",
22182344
} {
22192345
raw, ok := fields[name]
22202346
if !ok {

internal/tui/tui_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5535,6 +5535,56 @@ func TestRunInteractionScriptAcceptsWrappedResizeActionPayloads(t *testing.T) {
55355535
}
55365536
}
55375537

5538+
func TestRunInteractionScriptAcceptsWrappedResizeAliasFields(t *testing.T) {
5539+
steps, err := ParseInteractionScript([]byte(`[
5540+
{
5541+
"resizeWidth": {
5542+
"resource": {
5543+
"attributes": {
5544+
"value": "58"
5545+
}
5546+
}
5547+
},
5548+
"resizeHeight": {
5549+
"edge": {
5550+
"node": {
5551+
"attrs": {
5552+
"value": "12"
5553+
}
5554+
}
5555+
}
5556+
},
5557+
"expectScreen": {"columns": 58, "rows": 12}
5558+
},
5559+
{
5560+
"screenWidth": {
5561+
"resource": {
5562+
"attributes": {
5563+
"columns": "42"
5564+
}
5565+
}
5566+
},
5567+
"screenHeight": {
5568+
"edge": {
5569+
"node": {
5570+
"attrs": {
5571+
"rows": "9"
5572+
}
5573+
}
5574+
}
5575+
},
5576+
"expectScreen": {"columns": 42, "rows": 9}
5577+
}
5578+
]`))
5579+
if err != nil {
5580+
t.Fatal(err)
5581+
}
5582+
screen := NewREPLScreen(40, 8, nil)
5583+
if _, err := RunInteractionScriptChecked(&screen, steps); err != nil {
5584+
t.Fatal(err)
5585+
}
5586+
}
5587+
55385588
func TestRunInteractionScriptAcceptsWrappedStringActionPayloads(t *testing.T) {
55395589
steps, err := ParseInteractionScript([]byte(`[
55405590
{

0 commit comments

Comments
 (0)