fix(fill/tabs): dispatch real input/change/blur (#25); close <tab> wording + chrome-use current (#26)
#25 — fill() didn't fire the events framework inputs / site autocomplete need: it set value directly (bypassing React's value-tracker) and typed via Input.insertText, so controlled components and input/change/blur listeners (e.g. Mercari's postal-code → 都道府県 lookup) never ran though the value showed. fill now emulates a real edit: focus, set through the element's prototype value setter (React _valueTracker registers), then dispatch input → input → change → blur/ focusout. SELECT and contenteditable handled too. type <sel> <text> remains for per-keystroke sites. Verified live: an input wired with input/change/blur fired 'IICB' from one fill. #26 (ergonomics): - 'close <tab>' now closes just that tab and prints 'Tab [tN] closed'; bare 'close' still closes the browser. Previously 'close t12' ran a browser close and alarmingly printed 'Browser closed'. - new 'chrome-use current': prints the active tab's stable handle (tabId + CDP targetId + url/title), refreshed live — so an agent holds the targetId (which survives cross-process nav) instead of re-deriving 'which tab is live' from 'tabs' every step. The deeper tab-id churn is the #21/#23 stable-targetId story. Tests cover fill events (live), close tab-vs-browser parse, and current.
This commit is contained in:
+38
-1
@@ -1026,7 +1026,22 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result<Value, ParseErr
|
||||
}
|
||||
|
||||
// === Close ===
|
||||
"close" | "quit" | "exit" => Ok(json!({ "id": id, "action": "close" })),
|
||||
"close" | "quit" | "exit" => {
|
||||
// `close <tab>` closes only that tab (and the output says "Tab
|
||||
// closed"); bare `close` closes the browser/session. `close --all` is
|
||||
// intercepted earlier in the dispatcher. Previously `close t12` still
|
||||
// ran a browser close and alarmingly printed "Browser closed" (#26).
|
||||
if let Some(tab_ref) = rest.iter().find(|a| !a.starts_with("--")) {
|
||||
Ok(json!({ "id": id, "action": "tab_close", "tabId": tab_ref }))
|
||||
} else {
|
||||
Ok(json!({ "id": id, "action": "close" }))
|
||||
}
|
||||
}
|
||||
|
||||
// The active tab's stable handle — `targetId` survives cross-process
|
||||
// navigation and is reusable across sessions, so an agent can hold it
|
||||
// instead of re-deriving "which tab is live" from `tabs` each step (#26).
|
||||
"current" => Ok(json!({ "id": id, "action": "current" })),
|
||||
|
||||
// === Inspect ===
|
||||
"inspect" => Ok(json!({ "id": id, "action": "inspect" })),
|
||||
@@ -4014,6 +4029,28 @@ mod tests {
|
||||
assert_eq!(cmd["tabId"], "docs");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_close_tab_vs_browser() {
|
||||
// `close <tab>` closes that tab (says "Tab closed"); bare `close` closes
|
||||
// the browser (#26).
|
||||
let tab = parse_command(&args("close t12"), &default_flags()).unwrap();
|
||||
assert_eq!(tab["action"], "tab_close");
|
||||
assert_eq!(tab["tabId"], "t12");
|
||||
let browser = parse_command(&args("close"), &default_flags()).unwrap();
|
||||
assert_eq!(browser["action"], "close");
|
||||
// `quit`/`exit` aliases still browser-close.
|
||||
assert_eq!(
|
||||
parse_command(&args("quit"), &default_flags()).unwrap()["action"],
|
||||
"close"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_current_command() {
|
||||
let cmd = parse_command(&args("current"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "current");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tab_sends_string_tab_id() {
|
||||
let cmd = parse_command(&args("tab t2"), &default_flags()).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user