Skip to main content

playwright_cdp/
keyboard.rs

1//! `Keyboard` — input via the CDP `Input` domain.
2
3use crate::error::Result;
4use crate::options::{PressOptions, PressSequentiallyOptions};
5use crate::page::Page;
6use serde_json::{json, Value};
7use std::time::Duration;
8
9/// Keyboard input device.
10#[derive(Clone)]
11pub struct Keyboard {
12    page: Page,
13}
14
15impl Keyboard {
16    pub(crate) fn new(page: Page) -> Self {
17        Self { page }
18    }
19
20    async fn dispatch(
21        &self,
22        r#type: &str,
23        key: Option<&str>,
24        code: Option<&str>,
25        text: Option<&str>,
26    ) -> Result<()> {
27        let mut p = json!({ "type": r#type });
28        if let Some(k) = key {
29            p["key"] = json!(k);
30        }
31        if let Some(c) = code {
32            p["code"] = json!(c);
33        }
34        if let Some(t) = text {
35            p["text"] = json!(t);
36        }
37        self.page
38            .session()
39            .send("Input.dispatchKeyEvent", p)
40            .await
41            .map(|_: Value| ())
42    }
43
44    /// Press and hold `key`.
45    pub async fn down(&self, key: &str) -> Result<()> {
46        self.dispatch("keyDown", Some(key), None, None).await
47    }
48
49    /// Release `key`.
50    pub async fn up(&self, key: &str) -> Result<()> {
51        self.dispatch("keyUp", Some(key), None, None).await
52    }
53
54    /// Press `key` (down + up), optionally delaying between the two.
55    pub async fn press(&self, key: &str, options: Option<PressOptions>) -> Result<()> {
56        let delay = options.and_then(|o| o.delay).map(|ms| Duration::from_millis(ms as u64));
57        self.dispatch("keyDown", Some(key), None, None).await?;
58        if let Some(d) = delay {
59            tokio::time::sleep(d).await;
60        }
61        self.dispatch("keyUp", Some(key), None, None).await
62    }
63
64    /// Insert `text` directly, replacing the selection (fast, no per-key delay).
65    pub async fn insert_text(&self, text: &str) -> Result<()> {
66        self.page
67            .session()
68            .send("Input.insertText", json!({ "text": text }))
69            .await
70            .map(|_: Value| ())
71    }
72
73    /// Type `text`. With no delay this uses [`Self::insert_text`]; with a delay
74    /// it types character-by-character (matching Playwright `keyboard.type`).
75    pub async fn type_text(&self, text: &str, options: Option<PressSequentiallyOptions>) -> Result<()> {
76        let delay = options.and_then(|o| o.delay).map(|ms| Duration::from_millis(ms as u64));
77        match delay {
78            None => self.insert_text(text).await,
79            Some(d) => {
80                for ch in text.chars() {
81                    let s = ch.to_string();
82                    self.dispatch("keyDown", None, None, Some(&s)).await?;
83                    tokio::time::sleep(d).await;
84                }
85                Ok(())
86            }
87        }
88    }
89
90    /// Playwright-style alias for [`Self::type_text`].
91    pub async fn type_(&self, text: &str, options: Option<PressSequentiallyOptions>) -> Result<()> {
92        self.type_text(text, options).await
93    }
94}