Skip to content

Commit 7d2cd72

Browse files
authored
fix: route keyboard type through text input (#1014)
1 parent 5ac01fa commit 7d2cd72

2 files changed

Lines changed: 54 additions & 18 deletions

File tree

cli/src/native/actions.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3115,6 +3115,33 @@ async fn handle_keyboard(cmd: &Value, state: &DaemonState) -> Result<Value, Stri
31153115
let mgr = state.browser.as_ref().ok_or("Browser not launched")?;
31163116
let session_id = mgr.active_session_id()?.to_string();
31173117

3118+
match cmd.get("subaction").and_then(|v| v.as_str()) {
3119+
Some("type") => {
3120+
let text = cmd
3121+
.get("text")
3122+
.and_then(|v| v.as_str())
3123+
.ok_or("Missing 'text' parameter")?;
3124+
interaction::type_text_into_active_context(&mgr.client, &session_id, text, None)
3125+
.await?;
3126+
return Ok(json!({ "typed": text }));
3127+
}
3128+
Some("insertText") => {
3129+
let text = cmd
3130+
.get("text")
3131+
.and_then(|v| v.as_str())
3132+
.ok_or("Missing 'text' parameter")?;
3133+
mgr.client
3134+
.send_command(
3135+
"Input.insertText",
3136+
Some(json!({ "text": text })),
3137+
Some(&session_id),
3138+
)
3139+
.await?;
3140+
return Ok(json!({ "inserted": true }));
3141+
}
3142+
_ => {}
3143+
}
3144+
31183145
let event_type = cmd
31193146
.get("eventType")
31203147
.and_then(|v| v.as_str())

cli/src/native/interaction.rs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -202,34 +202,30 @@ pub async fn type_text(
202202
.await?;
203203
}
204204

205+
type_text_into_active_context(client, session_id, text, delay_ms).await
206+
}
207+
208+
pub async fn type_text_into_active_context(
209+
client: &CdpClient,
210+
session_id: &str,
211+
text: &str,
212+
delay_ms: Option<u64>,
213+
) -> Result<(), String> {
205214
let delay = delay_ms.unwrap_or(0);
206215

207216
for ch in text.chars() {
208-
let text_str = ch.to_string();
209-
let (key, code, key_code) = char_to_key_info(ch);
210-
211-
// Characters that have no US-keyboard mapping (key_code == 0 and empty
212-
// code) are inserted via `Input.insertText`, matching Playwright's
213-
// keyboard.type() fallback behaviour. This handles emoji, CJK, and
214-
// other characters that don't correspond to a physical key.
215-
if key_code == 0 && code.is_empty() {
216-
client
217-
.send_command_typed::<_, Value>(
218-
"Input.insertText",
219-
&InsertTextParams { text: text_str },
220-
Some(session_id),
221-
)
222-
.await?;
223-
} else {
217+
if matches!(ch, '\n' | '\r' | '\t') {
218+
let (key, code, key_code) = char_to_key_info(ch);
219+
let text_str = key_text(&key);
224220
client
225221
.send_command_typed::<_, Value>(
226222
"Input.dispatchKeyEvent",
227223
&DispatchKeyEventParams {
228224
event_type: "keyDown".to_string(),
229225
key: Some(key.clone()),
230226
code: Some(code.clone()),
231-
text: Some(text_str.clone()),
232-
unmodified_text: Some(text_str.clone()),
227+
text: text_str.clone(),
228+
unmodified_text: text_str,
233229
windows_virtual_key_code: Some(key_code),
234230
native_virtual_key_code: Some(key_code),
235231
modifiers: None,
@@ -254,6 +250,19 @@ pub async fn type_text(
254250
Some(session_id),
255251
)
256252
.await?;
253+
} else {
254+
// VS Code/Electron webviews reject repeated dispatchKeyEvent calls
255+
// carrying printable `text`. Insert printable characters directly
256+
// and reserve key events for controls like Enter and Tab.
257+
client
258+
.send_command_typed::<_, Value>(
259+
"Input.insertText",
260+
&InsertTextParams {
261+
text: ch.to_string(),
262+
},
263+
Some(session_id),
264+
)
265+
.await?;
257266
}
258267

259268
if delay > 0 {

0 commit comments

Comments
 (0)