diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 0ffcebe30..a4fba96c6 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -602,11 +602,27 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result = rest .iter() .copied() - .filter(|a| *a != "--key-events" && *a != "--keys") + .filter(|a| { + *a != "--key-events" + && *a != "--keys" + && *a != "--enter" + && *a != "--commit-enter" + }) .collect(); // `type --focused ` types into whatever element currently has // focus (no selector) — for custom widgets that move focus to a hidden @@ -615,14 +631,16 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result (or: type --focused ) [--key-events]", + usage: + "type (or: type --focused ) [--key-events] [--enter]", })?; Ok( - json!({ "id": id, "action": "type", "selector": sel, "text": rest[1..].join(" "), "keyEvents": key_events }), + json!({ "id": id, "action": "type", "selector": sel, "text": rest[1..].join(" "), "keyEvents": key_events, "commitEnter": commit_enter }), ) } "pick" => { @@ -4422,6 +4440,7 @@ mod tests { assert_eq!(cmd["selector"], "#input"); assert_eq!(cmd["text"], "some text"); assert_eq!(cmd["keyEvents"], false); + assert_eq!(cmd["commitEnter"], false); } #[test] @@ -4445,6 +4464,30 @@ mod tests { assert_eq!(focused["keyEvents"], true); } + #[test] + fn test_type_commit_enter() { + // --enter commits the typed value with a trailing Enter (async-autocomplete + // tag widgets, issue #50) and must imply --key-events so the dropdown the + // Enter commits has actually been triggered. + let cmd = parse_command(&args("type #tag ChatGPT --enter"), &default_flags()).unwrap(); + assert_eq!(cmd["action"], "type"); + assert_eq!(cmd["selector"], "#tag"); + assert_eq!(cmd["text"], "ChatGPT"); + assert_eq!(cmd["commitEnter"], true); + assert_eq!(cmd["keyEvents"], true); + + // Alias --commit-enter behaves identically, on the --focused path too. + let focused = parse_command( + &args("type --focused ChatGPT --commit-enter"), + &default_flags(), + ) + .unwrap(); + assert_eq!(focused["focused"], true); + assert_eq!(focused["text"], "ChatGPT"); + assert_eq!(focused["commitEnter"], true); + assert_eq!(focused["keyEvents"], true); + } + #[test] fn test_select() { let cmd = parse_command(&args("select #menu option1"), &default_flags()).unwrap(); diff --git a/cli/src/native/actions.rs b/cli/src/native/actions.rs index c63977dfb..f03143315 100644 --- a/cli/src/native/actions.rs +++ b/cli/src/native/actions.rs @@ -3576,6 +3576,15 @@ async fn handle_type(cmd: &Value, state: &mut DaemonState) -> Result`: type into the currently-focused element without a // selector (custom widgets that move focus to a hidden input on open). if cmd @@ -3595,7 +3604,10 @@ async fn handle_type(cmd: &Value, state: &mut DaemonState) -> Result Result --option ""`. Opens the control diff --git a/cli/src/native/interaction.rs b/cli/src/native/interaction.rs index c912f3eb0..827704a8a 100644 --- a/cli/src/native/interaction.rs +++ b/cli/src/native/interaction.rs @@ -859,6 +859,20 @@ pub async fn type_text_into_active_context( Ok(()) } +/// Commit the value just typed into an async-autocomplete / tag widget by pressing +/// Enter (issue #50: juejin's 「添加标签」 input). Such widgets query their suggestion +/// list off the per-character key events `type --key-events` fires, but the +/// dropdown lands a tick later — so we let the page settle (RAF + microtask) so the +/// candidate is mounted/highlighted before the Enter, which the widget reads as +/// "accept the current candidate". Used by `type --enter`, which forces key-events +/// typing on (a bulk insertText never triggers the dropdown Enter would commit). +pub async fn commit_with_enter(client: &CdpClient, session_id: &str) -> Result<(), String> { + wait_for_paint_settled(client, session_id).await; + press_key(client, session_id, "enter").await?; + wait_for_paint_settled(client, session_id).await; + Ok(()) +} + pub async fn press_key(client: &CdpClient, session_id: &str, key: &str) -> Result<(), String> { press_key_with_modifiers(client, session_id, key, None).await } diff --git a/skill-data/core/SKILL.md b/skill-data/core/SKILL.md index 73ce153ef..909e86507 100644 --- a/skill-data/core/SKILL.md +++ b/skill-data/core/SKILL.md @@ -352,6 +352,13 @@ chrome-use type @e5 "201-0001" --key-events # real keystrokes (not insertText) # use for autocomplete/combobox fields that # only react to key events (e.g. a postal box # that auto-fills city/prefecture, Google Places) +chrome-use type @e6 "ChatGPT" --enter # type (real keystrokes, implies --key-events) + # then press Enter to COMMIT the candidate in an + # async-autocomplete / tag widget. Use when typing + # alone shows no dropdown and the field needs a tag + # confirmed (e.g. juejin 「添加标签」). If you'd rather + # pick from the list, type --key-events first, then + # snapshot -i and click the candidate. chrome-use press Enter # press a key at current focus (down+up) chrome-use press Control+a # key combination chrome-use keydown d # HOLD a key down (no auto-release)