Skip to main content

BrowserTab

Struct BrowserTab 

Source
pub struct BrowserTab {
    pub current_url: Option<String>,
    /* private fields */
}

Fields§

§current_url: Option<String>

Implementations§

Source§

impl BrowserTab

Source

pub fn new() -> Result<Self>

Source

pub fn builder() -> BrowserBuilder

Source

pub fn with_profile(profile: DeviceProfile) -> Result<Self>

Examples found in repository?
examples/screenshot_demo.rs (line 10)
7async fn main() -> Result<()> {
8    println!(">>> Testing Pure-Rust Vector SVG & Wireframe Screenshot Rendering...");
9
10    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
11    tab.navigate("https://news.ycombinator.com/").await?;
12
13    let shot = tab.screenshot().expect("Expected screenshot");
14    println!("  * Dimensions: {}x{}", shot.width, shot.height);
15    println!("  * Elements Rendered: {}", shot.element_count);
16    println!("  * SVG Payload Size: {} bytes", shot.svg.len());
17
18    // Save SVG file
19    fs::write("screenshot.svg", &shot.svg)?;
20    println!("  * Saved screenshot to 'screenshot.svg'");
21
22    // Print text wireframe preview
23    println!("\n>>> ASCII WIREFRAME PREVIEW (FOR AGENT VISION TOKENS):");
24    let preview: String = shot
25        .layout_wireframe
26        .lines()
27        .take(18)
28        .collect::<Vec<_>>()
29        .join("\n");
30    println!("{}", preview);
31
32    Ok(())
33}
More examples
Hide additional examples
examples/scrape_to_markdown.rs (line 8)
6async fn main() -> Result<()> {
7    println!(">>> Launching Headless Engine Tab with Windows Chrome profile...");
8    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
9
10    let url = "https://en.wikipedia.org/wiki/Rust_(programming_language)";
11    println!(">>> Navigating to: {}", url);
12    let report = tab.navigate(url).await?;
13
14    println!(">>> Page Title: {}", report.page_title);
15    println!(">>> Raw HTML Size: {} bytes", report.html_bytes);
16
17    let markdown = tab.extract_markdown(None).unwrap_or_default();
18    println!(
19        ">>> Markdown Size: {} bytes (~{:.1}% compression)",
20        markdown.len(),
21        (1.0 - (markdown.len() as f64 / report.html_bytes as f64)) * 100.0
22    );
23
24    println!("\n--- MARKDOWN PREVIEW (First 600 chars) ---\n");
25    let preview: String = markdown.chars().take(600).collect();
26    println!("{}", preview);
27    println!("\n-------------------------------------------");
28
29    Ok(())
30}
examples/test_search_modes.rs (line 7)
6async fn main() -> Result<()> {
7    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
8
9    let urls = [
10        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents&gbv=1",
11        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents&udm=14",
12        "https://www.google.com/search?q=Rust+programming+language&udm=14",
13        "https://html.duckduckgo.com/html/?q=headless+browser+rust+engine+for+ai+agents",
14    ];
15
16    for url in urls {
17        println!("\n========================================");
18        println!("Testing URL: {}", url);
19        let nav = tab.navigate(url).await?;
20        println!("Status: {}", nav.status);
21        println!("Title:  {}", nav.page_title);
22        println!("Bytes:  {}", nav.html_bytes);
23        let md = tab.extract_markdown(None).unwrap_or_default();
24        println!("MD Len: {} bytes", md.len());
25        println!("Preview:\n{}", md.chars().take(300).collect::<String>());
26    }
27
28    Ok(())
29}
examples/test_google_markdown_fix.rs (line 16)
8async fn main() -> Result<()> {
9    println!(">>> Testing Google Search Distilled Markdown Extraction...");
10
11    let artifact_dir = Path::new(
12        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
13    );
14    let evidence_dir = Path::new("evidence");
15
16    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
17    let url = "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
18    println!("  -> Navigating to: {}", url);
19    let nav = tab.navigate(url).await?;
20
21    println!("  -> Status:       {}", nav.status);
22    println!("  -> Final URL:    {}", nav.final_url);
23    println!("  -> Page Title:   {}", nav.page_title);
24    println!("  -> HTML Payload: {} bytes", nav.html_bytes);
25
26    let md = tab.extract_markdown(None).unwrap_or_default();
27    println!("  -> Extracted Markdown Size: {} bytes", md.len());
28
29    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
30    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
31
32    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n");
33    println!("{}", md);
34
35    Ok(())
36}
examples/test_all_31_google_capabilities.rs (line 13)
6async fn main() -> anyhow::Result<()> {
7    println!("Testing all 31 Google/YouTube Capabilities...");
8    let evidence_dir = Path::new("evidence");
9    if !evidence_dir.exists() {
10        fs::create_dir_all(evidence_dir)?;
11    }
12
13    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
14
15    // Test a subset so it doesn't take forever, or just do the 5 most important ones to verify parsing
16    println!("1. Testing google_search...");
17    let res = tab
18        .google_search("headless browser pure rust engine")
19        .await?;
20    fs::write(
21        evidence_dir.join("test_google_search.md"),
22        res.to_markdown(),
23    )?;
24
25    println!("2. Testing youtube_search...");
26    let res = tab.youtube_search("rust programming").await?;
27    fs::write(
28        evidence_dir.join("test_youtube_search.md"),
29        res.to_markdown(),
30    )?;
31
32    println!("3. Testing google_ai_mode...");
33    let res = tab.google_ai_mode("explain quantum computing").await?;
34    fs::write(
35        evidence_dir.join("test_google_ai_mode.md"),
36        res.to_markdown(),
37    )?;
38
39    println!("4. Testing google_autocomplete...");
40    let res = tab.google_autocomplete("rust").await?;
41    fs::write(
42        evidence_dir.join("test_google_autocomplete.md"),
43        res.to_markdown(),
44    )?;
45
46    println!("Capabilities listed:");
47    for cap in tab.google_capabilities() {
48        println!(" - {}", cap);
49    }
50
51    println!("All tests completed. Output saved to evidence/");
52    Ok(())
53}
examples/test_html_css_rendering.rs (line 12)
7async fn main() -> Result<()> {
8    println!("================================================================================");
9    println!(">>> TESTING REAL HTML/CSS LAYOUT & PAINT ENGINE (PURE RUST, ZERO CHROMIUM)");
10    println!("================================================================================\n");
11
12    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
13
14    // 1. Test Wikipedia
15    let url = "https://en.wikipedia.org/wiki/Rust_(programming_language)";
16    println!("[1] Navigating to: {}", url);
17    let report = tab.navigate(url).await?;
18    println!("  * Title: {}", report.page_title);
19    println!("  * HTML Size: {} bytes", report.html_bytes);
20
21    println!("[2] Performing Real HTML/CSS Layout Pass & Rasterizing to PNG...");
22    let shot = tab.screenshot_async().await.expect("Expected screenshot");
23    println!("  * Resolution: {}x{}", shot.width, shot.height);
24    println!("  * Real PNG Size: {} bytes", shot.png_bytes.len());
25    println!("  * Base64 Data URL Prefix: {}...", &shot.png_base64[..50]);
26
27    fs::write("wikipedia_screenshot.png", &shot.png_bytes)?;
28    println!("  -> Successfully wrote real binary image: 'wikipedia_screenshot.png'");
29
30    println!("\n================================================================================");
31    println!(">>> REAL HTML/CSS LAYOUT & PAINT PASSED WITH ZERO CHROMIUM (<30MB RAM)!");
32    println!("================================================================================");
33
34    Ok(())
35}
Source

pub fn from_network(network: NetworkClient) -> Result<Self>

Source

pub fn profile(&self) -> DeviceProfile

Source

pub fn set_profile(&mut self, profile: DeviceProfile) -> Result<()>

Source

pub async fn navigate(&mut self, url: &str) -> Result<NavigationReport>

Examples found in repository?
examples/screenshot_demo.rs (line 11)
7async fn main() -> Result<()> {
8    println!(">>> Testing Pure-Rust Vector SVG & Wireframe Screenshot Rendering...");
9
10    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
11    tab.navigate("https://news.ycombinator.com/").await?;
12
13    let shot = tab.screenshot().expect("Expected screenshot");
14    println!("  * Dimensions: {}x{}", shot.width, shot.height);
15    println!("  * Elements Rendered: {}", shot.element_count);
16    println!("  * SVG Payload Size: {} bytes", shot.svg.len());
17
18    // Save SVG file
19    fs::write("screenshot.svg", &shot.svg)?;
20    println!("  * Saved screenshot to 'screenshot.svg'");
21
22    // Print text wireframe preview
23    println!("\n>>> ASCII WIREFRAME PREVIEW (FOR AGENT VISION TOKENS):");
24    let preview: String = shot
25        .layout_wireframe
26        .lines()
27        .take(18)
28        .collect::<Vec<_>>()
29        .join("\n");
30    println!("{}", preview);
31
32    Ok(())
33}
More examples
Hide additional examples
examples/scrape_to_markdown.rs (line 12)
6async fn main() -> Result<()> {
7    println!(">>> Launching Headless Engine Tab with Windows Chrome profile...");
8    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
9
10    let url = "https://en.wikipedia.org/wiki/Rust_(programming_language)";
11    println!(">>> Navigating to: {}", url);
12    let report = tab.navigate(url).await?;
13
14    println!(">>> Page Title: {}", report.page_title);
15    println!(">>> Raw HTML Size: {} bytes", report.html_bytes);
16
17    let markdown = tab.extract_markdown(None).unwrap_or_default();
18    println!(
19        ">>> Markdown Size: {} bytes (~{:.1}% compression)",
20        markdown.len(),
21        (1.0 - (markdown.len() as f64 / report.html_bytes as f64)) * 100.0
22    );
23
24    println!("\n--- MARKDOWN PREVIEW (First 600 chars) ---\n");
25    let preview: String = markdown.chars().take(600).collect();
26    println!("{}", preview);
27    println!("\n-------------------------------------------");
28
29    Ok(())
30}
examples/test_search_modes.rs (line 19)
6async fn main() -> Result<()> {
7    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
8
9    let urls = [
10        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents&gbv=1",
11        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents&udm=14",
12        "https://www.google.com/search?q=Rust+programming+language&udm=14",
13        "https://html.duckduckgo.com/html/?q=headless+browser+rust+engine+for+ai+agents",
14    ];
15
16    for url in urls {
17        println!("\n========================================");
18        println!("Testing URL: {}", url);
19        let nav = tab.navigate(url).await?;
20        println!("Status: {}", nav.status);
21        println!("Title:  {}", nav.page_title);
22        println!("Bytes:  {}", nav.html_bytes);
23        let md = tab.extract_markdown(None).unwrap_or_default();
24        println!("MD Len: {} bytes", md.len());
25        println!("Preview:\n{}", md.chars().take(300).collect::<String>());
26    }
27
28    Ok(())
29}
examples/test_google_markdown_fix.rs (line 19)
8async fn main() -> Result<()> {
9    println!(">>> Testing Google Search Distilled Markdown Extraction...");
10
11    let artifact_dir = Path::new(
12        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
13    );
14    let evidence_dir = Path::new("evidence");
15
16    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
17    let url = "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
18    println!("  -> Navigating to: {}", url);
19    let nav = tab.navigate(url).await?;
20
21    println!("  -> Status:       {}", nav.status);
22    println!("  -> Final URL:    {}", nav.final_url);
23    println!("  -> Page Title:   {}", nav.page_title);
24    println!("  -> HTML Payload: {} bytes", nav.html_bytes);
25
26    let md = tab.extract_markdown(None).unwrap_or_default();
27    println!("  -> Extracted Markdown Size: {} bytes", md.len());
28
29    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
30    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
31
32    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n");
33    println!("{}", md);
34
35    Ok(())
36}
examples/test_html_css_rendering.rs (line 17)
7async fn main() -> Result<()> {
8    println!("================================================================================");
9    println!(">>> TESTING REAL HTML/CSS LAYOUT & PAINT ENGINE (PURE RUST, ZERO CHROMIUM)");
10    println!("================================================================================\n");
11
12    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
13
14    // 1. Test Wikipedia
15    let url = "https://en.wikipedia.org/wiki/Rust_(programming_language)";
16    println!("[1] Navigating to: {}", url);
17    let report = tab.navigate(url).await?;
18    println!("  * Title: {}", report.page_title);
19    println!("  * HTML Size: {} bytes", report.html_bytes);
20
21    println!("[2] Performing Real HTML/CSS Layout Pass & Rasterizing to PNG...");
22    let shot = tab.screenshot_async().await.expect("Expected screenshot");
23    println!("  * Resolution: {}x{}", shot.width, shot.height);
24    println!("  * Real PNG Size: {} bytes", shot.png_bytes.len());
25    println!("  * Base64 Data URL Prefix: {}...", &shot.png_base64[..50]);
26
27    fs::write("wikipedia_screenshot.png", &shot.png_bytes)?;
28    println!("  -> Successfully wrote real binary image: 'wikipedia_screenshot.png'");
29
30    println!("\n================================================================================");
31    println!(">>> REAL HTML/CSS LAYOUT & PAINT PASSED WITH ZERO CHROMIUM (<30MB RAM)!");
32    println!("================================================================================");
33
34    Ok(())
35}
examples/multitab_concurrent.rs (line 24)
6async fn main() -> Result<()> {
7    println!("================================================================================");
8    println!(">>> MULTI-TAB CONCURRENT ENGINE DEMO (<50MB RAM)");
9    println!("================================================================================\n");
10
11    let mut engine = BrowserEngine::new()?;
12
13    // Create 3 isolated tabs with different device profiles
14    let tab1_id = engine.create_tab(Some(DeviceProfile::ChromeWindows))?;
15    let tab2_id = engine.create_tab(Some(DeviceProfile::SafariMac))?;
16    let tab3_id = engine.create_tab(Some(DeviceProfile::SafariIos))?;
17
18    println!("Created tabs: {}, {}, {}", tab1_id, tab2_id, tab3_id);
19
20    println!("[Tab 1: Chrome Windows] Navigating to Wikipedia...");
21    let r1 = engine
22        .get_tab_mut(&tab1_id)
23        .unwrap()
24        .navigate("https://en.wikipedia.org/wiki/Artificial_intelligence")
25        .await?;
26    println!("  -> Tab 1 Title: {}", r1.page_title);
27
28    println!("[Tab 2: Safari Mac] Navigating to Hacker News...");
29    let r2 = engine
30        .get_tab_mut(&tab2_id)
31        .unwrap()
32        .navigate("https://news.ycombinator.com/")
33        .await?;
34    println!("  -> Tab 2 Title: {}", r2.page_title);
35
36    println!("[Tab 3: iPhone iOS] Navigating to GitHub Explore...");
37    let r3 = engine
38        .get_tab_mut(&tab3_id)
39        .unwrap()
40        .navigate("https://github.com/trending")
41        .await?;
42    println!("  -> Tab 3 Title: {}", r3.page_title);
43
44    println!("\n>>> Active Tabs List:");
45    for tab_info in engine.list_tabs() {
46        println!(
47            "  - ID: {:<8} Profile: {:<15} URL: {:?}",
48            tab_info.id,
49            format!("{:?}", tab_info.profile),
50            tab_info.url
51        );
52    }
53
54    println!("\n>>> Closing Tab 2...");
55    engine.close_tab(&tab2_id);
56    println!("Remaining tabs count: {}", engine.list_tabs().len());
57
58    Ok(())
59}
Source

pub async fn search(&mut self, query: &str) -> Result<NavigationReport>

Default Google Search with automated query encoding and mode routing

Examples found in repository?
examples/test_default_google_search.rs (line 26)
7async fn main() -> Result<()> {
8    println!("================================================================================");
9    println!(">>> TESTING GOOGLE AS DEFAULT SEARCH ENGINE (PURE RUST)");
10    println!("================================================================================\n");
11
12    let artifact_dir = Path::new(
13        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
14    );
15    let evidence_dir = Path::new("evidence");
16    fs::create_dir_all(evidence_dir)?;
17
18    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
19
20    // 1. Default Google Search
21    let query = "headless browser pure rust engine for AI agents";
22    println!(
23        "[1/2] Executing default search: tab.search(\"{}\")...",
24        query
25    );
26    let nav = tab.search(query).await?;
27    println!("  -> Final URL:     {}", nav.final_url);
28    println!("  -> Page Title:    {}", nav.page_title);
29    println!("  -> Status:        {}", nav.status);
30    println!("  -> HTML Payload:  {} bytes", nav.html_bytes);
31
32    let md = tab.extract_markdown(None).unwrap_or_default();
33    println!("  -> Distilled Markdown Size: {} bytes", md.len());
34
35    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
36    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
37
38    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n--------------------------------------------------------------------------------");
39    println!("{}", md);
40    println!("--------------------------------------------------------------------------------");
41
42    // 2. Google AI Mode Search
43    let ai_query = "Rust programming language concurrency patterns";
44    println!(
45        "\n[2/2] Executing Google AI Mode search: tab.search_google(\"{}\", Some(\"ai\"))...",
46        ai_query
47    );
48    let ai_nav = tab.search_google(ai_query, Some("ai")).await?;
49    println!("  -> Final URL:     {}", ai_nav.final_url);
50    println!("  -> Page Title:    {}", ai_nav.page_title);
51    println!("  -> HTML Payload:  {} bytes", ai_nav.html_bytes);
52
53    let ai_md = tab.extract_markdown(None).unwrap_or_default();
54    println!("  -> Distilled Markdown Size: {} bytes", ai_md.len());
55
56    fs::write(evidence_dir.join("google_aimode_distilled.md"), &ai_md)?;
57    fs::write(artifact_dir.join("google_aimode_distilled.md"), &ai_md)?;
58
59    println!("\n>>> DISTILLED GOOGLE AI MODE MARKDOWN:\n--------------------------------------------------------------------------------");
60    println!("{}", ai_md);
61    println!("--------------------------------------------------------------------------------");
62
63    println!("\n================================================================================");
64    println!(">>> GOOGLE DEFAULT SEARCH ENGINE VERIFICATION COMPLETE!");
65    println!("================================================================================");
66
67    Ok(())
68}
Source

pub async fn search_google( &mut self, query: &str, mode: Option<&str>, ) -> Result<NavigationReport>

Search Google with specific query modes (e.g. “ai” for udm=50, “web” for udm=14, “images” for udm=2, “news”)

Examples found in repository?
examples/test_default_google_search.rs (line 48)
7async fn main() -> Result<()> {
8    println!("================================================================================");
9    println!(">>> TESTING GOOGLE AS DEFAULT SEARCH ENGINE (PURE RUST)");
10    println!("================================================================================\n");
11
12    let artifact_dir = Path::new(
13        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
14    );
15    let evidence_dir = Path::new("evidence");
16    fs::create_dir_all(evidence_dir)?;
17
18    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
19
20    // 1. Default Google Search
21    let query = "headless browser pure rust engine for AI agents";
22    println!(
23        "[1/2] Executing default search: tab.search(\"{}\")...",
24        query
25    );
26    let nav = tab.search(query).await?;
27    println!("  -> Final URL:     {}", nav.final_url);
28    println!("  -> Page Title:    {}", nav.page_title);
29    println!("  -> Status:        {}", nav.status);
30    println!("  -> HTML Payload:  {} bytes", nav.html_bytes);
31
32    let md = tab.extract_markdown(None).unwrap_or_default();
33    println!("  -> Distilled Markdown Size: {} bytes", md.len());
34
35    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
36    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
37
38    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n--------------------------------------------------------------------------------");
39    println!("{}", md);
40    println!("--------------------------------------------------------------------------------");
41
42    // 2. Google AI Mode Search
43    let ai_query = "Rust programming language concurrency patterns";
44    println!(
45        "\n[2/2] Executing Google AI Mode search: tab.search_google(\"{}\", Some(\"ai\"))...",
46        ai_query
47    );
48    let ai_nav = tab.search_google(ai_query, Some("ai")).await?;
49    println!("  -> Final URL:     {}", ai_nav.final_url);
50    println!("  -> Page Title:    {}", ai_nav.page_title);
51    println!("  -> HTML Payload:  {} bytes", ai_nav.html_bytes);
52
53    let ai_md = tab.extract_markdown(None).unwrap_or_default();
54    println!("  -> Distilled Markdown Size: {} bytes", ai_md.len());
55
56    fs::write(evidence_dir.join("google_aimode_distilled.md"), &ai_md)?;
57    fs::write(artifact_dir.join("google_aimode_distilled.md"), &ai_md)?;
58
59    println!("\n>>> DISTILLED GOOGLE AI MODE MARKDOWN:\n--------------------------------------------------------------------------------");
60    println!("{}", ai_md);
61    println!("--------------------------------------------------------------------------------");
62
63    println!("\n================================================================================");
64    println!(">>> GOOGLE DEFAULT SEARCH ENGINE VERIFICATION COMPLETE!");
65    println!("================================================================================");
66
67    Ok(())
68}
Examples found in repository?
examples/test_all_31_google_capabilities.rs (line 18)
6async fn main() -> anyhow::Result<()> {
7    println!("Testing all 31 Google/YouTube Capabilities...");
8    let evidence_dir = Path::new("evidence");
9    if !evidence_dir.exists() {
10        fs::create_dir_all(evidence_dir)?;
11    }
12
13    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
14
15    // Test a subset so it doesn't take forever, or just do the 5 most important ones to verify parsing
16    println!("1. Testing google_search...");
17    let res = tab
18        .google_search("headless browser pure rust engine")
19        .await?;
20    fs::write(
21        evidence_dir.join("test_google_search.md"),
22        res.to_markdown(),
23    )?;
24
25    println!("2. Testing youtube_search...");
26    let res = tab.youtube_search("rust programming").await?;
27    fs::write(
28        evidence_dir.join("test_youtube_search.md"),
29        res.to_markdown(),
30    )?;
31
32    println!("3. Testing google_ai_mode...");
33    let res = tab.google_ai_mode("explain quantum computing").await?;
34    fs::write(
35        evidence_dir.join("test_google_ai_mode.md"),
36        res.to_markdown(),
37    )?;
38
39    println!("4. Testing google_autocomplete...");
40    let res = tab.google_autocomplete("rust").await?;
41    fs::write(
42        evidence_dir.join("test_google_autocomplete.md"),
43        res.to_markdown(),
44    )?;
45
46    println!("Capabilities listed:");
47    for cap in tab.google_capabilities() {
48        println!(" - {}", cap);
49    }
50
51    println!("All tests completed. Output saved to evidence/");
52    Ok(())
53}
Source

pub async fn google_autocomplete( &mut self, query: &str, ) -> Result<GoogleAutocompleteResult>

Examples found in repository?
examples/test_all_31_google_capabilities.rs (line 40)
6async fn main() -> anyhow::Result<()> {
7    println!("Testing all 31 Google/YouTube Capabilities...");
8    let evidence_dir = Path::new("evidence");
9    if !evidence_dir.exists() {
10        fs::create_dir_all(evidence_dir)?;
11    }
12
13    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
14
15    // Test a subset so it doesn't take forever, or just do the 5 most important ones to verify parsing
16    println!("1. Testing google_search...");
17    let res = tab
18        .google_search("headless browser pure rust engine")
19        .await?;
20    fs::write(
21        evidence_dir.join("test_google_search.md"),
22        res.to_markdown(),
23    )?;
24
25    println!("2. Testing youtube_search...");
26    let res = tab.youtube_search("rust programming").await?;
27    fs::write(
28        evidence_dir.join("test_youtube_search.md"),
29        res.to_markdown(),
30    )?;
31
32    println!("3. Testing google_ai_mode...");
33    let res = tab.google_ai_mode("explain quantum computing").await?;
34    fs::write(
35        evidence_dir.join("test_google_ai_mode.md"),
36        res.to_markdown(),
37    )?;
38
39    println!("4. Testing google_autocomplete...");
40    let res = tab.google_autocomplete("rust").await?;
41    fs::write(
42        evidence_dir.join("test_google_autocomplete.md"),
43        res.to_markdown(),
44    )?;
45
46    println!("Capabilities listed:");
47    for cap in tab.google_capabilities() {
48        println!(" - {}", cap);
49    }
50
51    println!("All tests completed. Output saved to evidence/");
52    Ok(())
53}
Source

pub async fn google_ai_overview( &mut self, query: &str, ) -> Result<GenericGoogleResult>

Source

pub async fn google_ai_mode( &mut self, query: &str, ) -> Result<GenericGoogleResult>

Examples found in repository?
examples/test_all_31_google_capabilities.rs (line 33)
6async fn main() -> anyhow::Result<()> {
7    println!("Testing all 31 Google/YouTube Capabilities...");
8    let evidence_dir = Path::new("evidence");
9    if !evidence_dir.exists() {
10        fs::create_dir_all(evidence_dir)?;
11    }
12
13    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
14
15    // Test a subset so it doesn't take forever, or just do the 5 most important ones to verify parsing
16    println!("1. Testing google_search...");
17    let res = tab
18        .google_search("headless browser pure rust engine")
19        .await?;
20    fs::write(
21        evidence_dir.join("test_google_search.md"),
22        res.to_markdown(),
23    )?;
24
25    println!("2. Testing youtube_search...");
26    let res = tab.youtube_search("rust programming").await?;
27    fs::write(
28        evidence_dir.join("test_youtube_search.md"),
29        res.to_markdown(),
30    )?;
31
32    println!("3. Testing google_ai_mode...");
33    let res = tab.google_ai_mode("explain quantum computing").await?;
34    fs::write(
35        evidence_dir.join("test_google_ai_mode.md"),
36        res.to_markdown(),
37    )?;
38
39    println!("4. Testing google_autocomplete...");
40    let res = tab.google_autocomplete("rust").await?;
41    fs::write(
42        evidence_dir.join("test_google_autocomplete.md"),
43        res.to_markdown(),
44    )?;
45
46    println!("Capabilities listed:");
47    for cap in tab.google_capabilities() {
48        println!(" - {}", cap);
49    }
50
51    println!("All tests completed. Output saved to evidence/");
52    Ok(())
53}
Source

pub async fn google_finance_quote( &mut self, ticker: &str, ) -> Result<GenericGoogleResult>

Source

pub async fn google_travel_explore( &mut self, destination: &str, ) -> Result<GenericGoogleResult>

Examples found in repository?
examples/test_all_31_google_capabilities.rs (line 26)
6async fn main() -> anyhow::Result<()> {
7    println!("Testing all 31 Google/YouTube Capabilities...");
8    let evidence_dir = Path::new("evidence");
9    if !evidence_dir.exists() {
10        fs::create_dir_all(evidence_dir)?;
11    }
12
13    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
14
15    // Test a subset so it doesn't take forever, or just do the 5 most important ones to verify parsing
16    println!("1. Testing google_search...");
17    let res = tab
18        .google_search("headless browser pure rust engine")
19        .await?;
20    fs::write(
21        evidence_dir.join("test_google_search.md"),
22        res.to_markdown(),
23    )?;
24
25    println!("2. Testing youtube_search...");
26    let res = tab.youtube_search("rust programming").await?;
27    fs::write(
28        evidence_dir.join("test_youtube_search.md"),
29        res.to_markdown(),
30    )?;
31
32    println!("3. Testing google_ai_mode...");
33    let res = tab.google_ai_mode("explain quantum computing").await?;
34    fs::write(
35        evidence_dir.join("test_google_ai_mode.md"),
36        res.to_markdown(),
37    )?;
38
39    println!("4. Testing google_autocomplete...");
40    let res = tab.google_autocomplete("rust").await?;
41    fs::write(
42        evidence_dir.join("test_google_autocomplete.md"),
43        res.to_markdown(),
44    )?;
45
46    println!("Capabilities listed:");
47    for cap in tab.google_capabilities() {
48        println!(" - {}", cap);
49    }
50
51    println!("All tests completed. Output saved to evidence/");
52    Ok(())
53}
Source

pub async fn youtube_video( &mut self, video_id: &str, ) -> Result<GenericGoogleResult>

Source

pub async fn youtube_channel( &mut self, channel: &str, ) -> Result<GenericGoogleResult>

Source

pub async fn youtube_playlist( &mut self, playlist_id: &str, ) -> Result<GenericGoogleResult>

Source

pub async fn google_lens_visual_matches( &mut self, image_url: &str, ) -> Result<GenericGoogleResult>

Source

pub async fn google_lens_exact_matches( &mut self, image_url: &str, ) -> Result<GenericGoogleResult>

Source

pub async fn google_lens_products( &mut self, image_url: &str, ) -> Result<GenericGoogleResult>

Source

pub async fn google_lens_about_image( &mut self, image_url: &str, ) -> Result<GenericGoogleResult>

Source

pub fn google_capabilities(&self) -> Vec<&'static str>

Examples found in repository?
examples/test_all_31_google_capabilities.rs (line 47)
6async fn main() -> anyhow::Result<()> {
7    println!("Testing all 31 Google/YouTube Capabilities...");
8    let evidence_dir = Path::new("evidence");
9    if !evidence_dir.exists() {
10        fs::create_dir_all(evidence_dir)?;
11    }
12
13    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
14
15    // Test a subset so it doesn't take forever, or just do the 5 most important ones to verify parsing
16    println!("1. Testing google_search...");
17    let res = tab
18        .google_search("headless browser pure rust engine")
19        .await?;
20    fs::write(
21        evidence_dir.join("test_google_search.md"),
22        res.to_markdown(),
23    )?;
24
25    println!("2. Testing youtube_search...");
26    let res = tab.youtube_search("rust programming").await?;
27    fs::write(
28        evidence_dir.join("test_youtube_search.md"),
29        res.to_markdown(),
30    )?;
31
32    println!("3. Testing google_ai_mode...");
33    let res = tab.google_ai_mode("explain quantum computing").await?;
34    fs::write(
35        evidence_dir.join("test_google_ai_mode.md"),
36        res.to_markdown(),
37    )?;
38
39    println!("4. Testing google_autocomplete...");
40    let res = tab.google_autocomplete("rust").await?;
41    fs::write(
42        evidence_dir.join("test_google_autocomplete.md"),
43        res.to_markdown(),
44    )?;
45
46    println!("Capabilities listed:");
47    for cap in tab.google_capabilities() {
48        println!(" - {}", cap);
49    }
50
51    println!("All tests completed. Output saved to evidence/");
52    Ok(())
53}
Source

pub fn set_content( &mut self, html: &str, url: Option<&str>, ) -> Result<NavigationReport>

Source

pub fn observe(&self) -> Option<PageObservation>

Examples found in repository?
examples/autonomous_agent_loop.rs (line 20)
6async fn main() -> Result<()> {
7    println!("================================================================================");
8    println!(">>> AI AGENTIC AUTONOMOUS BROWSING LOOP (PURE RUST)");
9    println!("================================================================================\n");
10
11    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
12
13    // Step 1: Initial Navigation
14    println!("[Step 1: Navigate] Agent navigating to Wikipedia Main Page...");
15    tab.navigate("https://en.wikipedia.org/wiki/Main_Page")
16        .await?;
17
18    // Step 2: Observe
19    println!("\n[Step 2: Observe] Extracting Indexed Action Tree for Agent LLM...");
20    let obs = tab.observe().expect("Expected observation");
21    println!("  * Page Title: {}", obs.title);
22    println!(
23        "  * Total Interactive Elements Indexed: {}",
24        obs.interactive_elements.len()
25    );
26
27    println!("\n>>> AGENT ACTION MAP (Sample First 10 Elements):");
28    println!("--------------------------------------------------------------------------------");
29    for el in obs.interactive_elements.iter().take(10) {
30        println!("{}", el.to_agent_string());
31    }
32    println!("--------------------------------------------------------------------------------");
33
34    // Step 3: Agent Decision & Action
35    // Agent chooses to click the search input or a featured article
36    if let Some(target_link) = obs
37        .interactive_elements
38        .iter()
39        .find(|e| e.tag == "a" && e.text.contains("article"))
40    {
41        println!(
42            "\n[Step 3: Act] Agent decides to click element [{}] (Text: \"{}\")...",
43            target_link.index, target_link.text
44        );
45        let action_res = tab.act_click(&target_link.index.to_string()).await?;
46
47        if let Some(report) = action_res {
48            println!("  -> Navigated to: {}", report.final_url);
49            println!("  -> New Page Title: {}", report.page_title);
50        }
51    }
52
53    // Step 4: Extract LLM Knowledge
54    println!("\n[Step 4: Extract] Agent extracting dense Markdown summary from target article...");
55    let md = tab.extract_markdown(None).unwrap_or_default();
56    println!("  * Extracted Markdown Length: {} bytes", md.len());
57    let preview: String = md.chars().take(400).collect();
58    println!("\n>>> AGENT KNOWLEDGE PREVIEW:\n{}", preview);
59
60    println!("\n================================================================================");
61    println!(">>> AUTONOMOUS AGENT LOOP COMPLETED SEAMLESSLY!");
62    println!("================================================================================");
63
64    Ok(())
65}
More examples
Hide additional examples
examples/agentic_chatgpt_prompt_execution.rs (line 39)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> HEADLESS-ENGINE: AGENTIC CHATGPT PROMPT & RESPONSE EXECUTION");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
24
25    // Step 1: Initial Navigation to ChatGPT
26    let target_url = "https://chatgpt.com/";
27    println!(
28        "[Step 1: Navigate] Agent navigating with stealth fingerprint to {}...",
29        target_url
30    );
31    let nav_report = tab.navigate(target_url).await?;
32    println!("  -> HTTP Status:      {}", nav_report.status);
33    println!("  -> Page Title:       {}", nav_report.page_title);
34    println!("  -> CAPTCHA Detected: {}", nav_report.is_captcha_detected);
35    println!("  -> HTML Payload:     {} bytes", nav_report.html_bytes);
36
37    // Step 2: Observe Action Tree
38    println!("\n[Step 2: Observe] Extracting Indexed Action Tree for LLM Reasoning...");
39    let obs = tab.observe().expect("Expected ChatGPT page observation");
40    println!(
41        "  -> Total Interactive Elements Indexed: {}",
42        obs.interactive_elements.len()
43    );
44
45    println!("\n>>> AGENT ACTION MAP (Sample Elements):");
46    println!("--------------------------------------------------------------------------------");
47    for el in obs.interactive_elements.iter().take(15) {
48        println!("{}", el.to_agent_string());
49    }
50    println!("--------------------------------------------------------------------------------");
51
52    // Step 3: Agent Decision & Prompt Action
53    // Agent chooses to trigger an AI prompt (either via prompt button like "What can you do?" or typing)
54    let prompt_btn = obs
55        .interactive_elements
56        .iter()
57        .find(|e| {
58            e.text.contains("What can you do")
59                || e.text.contains("Deep research")
60                || e.text.contains("New chat")
61        })
62        .cloned();
63
64    let (chosen_action, action_type) = if let Some(btn) = &prompt_btn {
65        println!("\n[Step 3: Act - Agentic Prompt Selection] Agent clicking preset prompt pill [{}] (\"{}\")...", btn.index, btn.text);
66        let click_res = tab.act_click(&btn.index.to_string()).await?;
67        let status_desc = if let Some(rep) = click_res {
68            format!("Navigated to {}", rep.final_url)
69        } else {
70            "Triggered prompt action via element click".to_string()
71        };
72        (status_desc, btn.text.clone())
73    } else {
74        println!("\n[Step 3: Act - Agentic Prompting] Dispatching prompt to ChatGPT input...");
75        let type_status = tab
76            .act_type("26", "Explain quantum computing in 2 simple sentences")
77            .await?;
78        (
79            format!("Typed prompt into element [26]: {}", type_status),
80            "Typed Prompt".to_string(),
81        )
82    };
83
84    println!("  -> Action Execution Result: {}", chosen_action);
85
86    // Step 4: Extract Conversation Response / Markdown
87    println!("\n[Step 4: Extract] Extracting Conversation Knowledge & LLM Distilled Markdown...");
88    let md = tab.extract_markdown(None).unwrap_or_default();
89    println!("  -> Distilled Markdown Size: {} bytes", md.len());
90    let preview: String = md.chars().take(400).collect();
91    println!("\n>>> AGENT CONVERSATION MARKDOWN:\n{}", preview);
92
93    // Step 5: Visual Screenshot Capture of the Conversation
94    println!("\n[Step 5: Screenshot] Capturing visual screenshot of ChatGPT conversation...");
95    let shot = tab.screenshot_async().await.expect("Expected screenshot");
96    println!(
97        "  -> Screenshot Resolution: {}x{} px",
98        shot.width, shot.height
99    );
100    println!("  -> PNG Payload:          {} bytes", shot.png_bytes.len());
101
102    let png_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_screenshot.png");
103    let png_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_screenshot.png");
104    let md_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_conversation.md");
105    let md_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_conversation.md");
106    let json_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_trace.json");
107    let json_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_trace.json");
108
109    if !shot.png_bytes.is_empty() {
110        fs::write(&png_dest_evidence, &shot.png_bytes)?;
111        fs::write(&png_dest_artifact, &shot.png_bytes)?;
112    }
113    fs::write(&md_dest_evidence, &md)?;
114    fs::write(&md_dest_artifact, &md)?;
115
116    let trace_summary = json!({
117        "engine": "Headless Engine (Pure Rust)",
118        "task": "Agentic ChatGPT Prompting & Response Extraction",
119        "initial_url": target_url,
120        "page_title": nav_report.page_title,
121        "status": nav_report.status,
122        "interactive_elements_indexed": obs.interactive_elements.len(),
123        "prompt_action": {
124            "type": action_type,
125            "result": chosen_action
126        },
127        "extracted_markdown_bytes": md.len(),
128        "screenshot_png_bytes": shot.png_bytes.len(),
129        "status": "PASS"
130    });
131
132    fs::write(
133        &json_dest_evidence,
134        serde_json::to_string_pretty(&trace_summary)?,
135    )?;
136    fs::write(
137        &json_dest_artifact,
138        serde_json::to_string_pretty(&trace_summary)?,
139    )?;
140
141    println!("\n================================================================================");
142    println!(">>> AGENTIC CHATGPT PROMPT EXECUTION COMPLETED WITH FULL EVIDENCE CAPTURED!");
143    println!("================================================================================");
144
145    Ok(())
146}
examples/demonstrate_all_features.rs (line 37)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> HEADLESS ENGINE: VERIFYING SCREENSHOT, MARKDOWN & AGENTIC NAVIGATION");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // Initialize browser with stealth fingerprint
24    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
25
26    // =========================================================================
27    // 1. AGENTIC NAVIGATION FEATURE
28    // =========================================================================
29    println!("[1. AGENTIC NAVIGATION] Initial navigation to Wikipedia Portal...");
30    let initial_url = "https://en.wikipedia.org/wiki/Portal:Current_events";
31    let init_report = tab.navigate(initial_url).await?;
32    println!("  -> Landed on: {}", init_report.requested_url);
33    println!("  -> Page Title: {}", init_report.page_title);
34    println!("  -> Status: {}", init_report.status);
35
36    // Extract Agent Action Map / Observation Tree
37    let obs = tab.observe().expect("Expected observation");
38    println!(
39        "  -> Total Interactive Elements Indexed: {}",
40        obs.interactive_elements.len()
41    );
42
43    // Agent decision: find an informative article link to click
44    let selected_target = obs
45        .interactive_elements
46        .iter()
47        .find(|e| {
48            e.tag == "a"
49                && e.text.len() > 15
50                && !e.href.is_empty()
51                && !e.href.contains("#")
52                && !e.href.contains("Portal:")
53        })
54        .or_else(|| {
55            obs.interactive_elements
56                .iter()
57                .find(|e| e.tag == "a" && e.text.contains("article"))
58        })
59        .or_else(|| obs.interactive_elements.first())
60        .expect("No interactive link found");
61
62    let clicked_index = selected_target.index;
63    let clicked_text = selected_target.text.clone();
64    let clicked_href = selected_target.href.clone();
65    let clicked_selector = selected_target.selector.clone();
66
67    println!("\n  [Agent Decision & Act]");
68    println!("  -> Selected Element ID: [{}]", clicked_index);
69    println!("  -> Element Selector:    {}", clicked_selector);
70    println!("  -> Element Anchor Text: \"{}\"", clicked_text);
71    println!("  -> Target URL:          {}", clicked_href);
72
73    // Execute Autonomous Action Click via element index
74    let nav_report = tab
75        .act_click(&clicked_index.to_string())
76        .await?
77        .expect("Expected navigation report after click");
78
79    println!("\n  [Agent Navigation Result]");
80    println!("  -> Successfully Navigated to: {}", nav_report.final_url);
81    println!("  -> New Page Title:            {}", nav_report.page_title);
82    println!("  -> HTTP Status:               {}", nav_report.status);
83    println!(
84        "  -> HTML Payload Size:         {} bytes",
85        nav_report.html_bytes
86    );
87
88    // Save Agent Action Map
89    let sample_elements: Vec<_> = obs.interactive_elements.iter().take(20).cloned().collect();
90    let agent_map_json = json!({
91        "initial_page": {
92            "url": initial_url,
93            "title": init_report.page_title,
94            "interactive_elements_count": obs.interactive_elements.len()
95        },
96        "agent_action": {
97            "clicked_element_index": clicked_index,
98            "anchor_text": clicked_text,
99            "target_href": clicked_href,
100            "selector": clicked_selector
101        },
102        "target_page": {
103            "final_url": nav_report.final_url,
104            "page_title": nav_report.page_title,
105            "status": nav_report.status,
106            "html_bytes": nav_report.html_bytes
107        },
108        "sample_indexed_elements": sample_elements
109    });
110    fs::write(
111        evidence_dir.join("agentic_navigation_trace.json"),
112        serde_json::to_string_pretty(&agent_map_json)?,
113    )?;
114    fs::write(
115        artifact_dir.join("agentic_navigation_trace.json"),
116        serde_json::to_string_pretty(&agent_map_json)?,
117    )?;
118
119    // =========================================================================
120    // 2. SCREENSHOT FEATURE
121    // =========================================================================
122    println!(
123        "\n[2. SCREENSHOT FEATURE] Capturing high-resolution visual screenshot & vector SVG..."
124    );
125    let shot = tab.screenshot_async().await.expect("Expected screenshot");
126    println!("  -> Dimensions:        {}x{} px", shot.width, shot.height);
127    println!("  -> Elements Rendered: {}", shot.element_count);
128    println!("  -> SVG Size:          {} bytes", shot.svg.len());
129    println!("  -> PNG Bytes:         {} bytes", shot.png_bytes.len());
130
131    let png_dest_evidence = evidence_dir.join("demonstration_screenshot.png");
132    let png_dest_artifact = artifact_dir.join("demonstration_screenshot.png");
133    let svg_dest_evidence = evidence_dir.join("demonstration_screenshot.svg");
134    let svg_dest_artifact = artifact_dir.join("demonstration_screenshot.svg");
135    let wireframe_dest = evidence_dir.join("demonstration_wireframe.txt");
136
137    if !shot.png_bytes.is_empty() {
138        fs::write(&png_dest_evidence, &shot.png_bytes)?;
139        fs::write(&png_dest_artifact, &shot.png_bytes)?;
140        println!(
141            "  -> Saved PNG Screenshot to: {}",
142            png_dest_evidence.display()
143        );
144    }
145    fs::write(&svg_dest_evidence, &shot.svg)?;
146    fs::write(&svg_dest_artifact, &shot.svg)?;
147    fs::write(&wireframe_dest, &shot.layout_wireframe)?;
148    println!(
149        "  -> Saved SVG Vector Layout to: {}",
150        svg_dest_evidence.display()
151    );
152
153    // =========================================================================
154    // 3. MARKDOWN DISTILLATION FEATURE
155    // =========================================================================
156    println!("\n[3. MARKDOWN FEATURE] Extracting token-efficient distilled Markdown for LLMs...");
157    let markdown = tab.extract_markdown(None).unwrap_or_default();
158    let raw_html_len = nav_report.html_bytes;
159    let md_len = markdown.len();
160    let reduction_pct = if raw_html_len > 0 {
161        ((raw_html_len as f64 - md_len as f64) / raw_html_len as f64) * 100.0
162    } else {
163        0.0
164    };
165
166    println!(
167        "  -> Raw HTML Payload:      {} bytes (~{} tokens)",
168        raw_html_len,
169        raw_html_len / 4
170    );
171    println!(
172        "  -> Distilled Markdown:    {} bytes (~{} tokens)",
173        md_len,
174        md_len / 4
175    );
176    println!("  -> Token/Size Reduction:  {:.2}%", reduction_pct);
177
178    let md_dest_evidence = evidence_dir.join("demonstration_distilled.md");
179    let md_dest_artifact = artifact_dir.join("demonstration_distilled.md");
180    fs::write(&md_dest_evidence, &markdown)?;
181    fs::write(&md_dest_artifact, &markdown)?;
182    println!(
183        "  -> Saved Distilled Markdown to: {}",
184        md_dest_evidence.display()
185    );
186
187    // Preview Markdown header
188    println!("\n>>> DISTILLED MARKDOWN PREVIEW (FIRST 400 CHARS):");
189    println!("--------------------------------------------------------------------------------");
190    let preview: String = markdown.chars().take(400).collect();
191    println!("{}", preview);
192    println!("--------------------------------------------------------------------------------");
193
194    // Save Complete Evidence Summary
195    let summary = json!({
196        "engine": "Headless Engine (Pure Rust)",
197        "features_tested": {
198            "1_agentic_navigation": {
199                "initial_url": initial_url,
200                "selected_element": {
201                    "id": clicked_index,
202                    "text": clicked_text,
203                    "target_url": clicked_href
204                },
205                "destination_page": {
206                    "url": nav_report.final_url,
207                    "title": nav_report.page_title,
208                    "status": nav_report.status
209                },
210                "status": "PASS"
211            },
212            "2_screenshot": {
213                "png_size_bytes": shot.png_bytes.len(),
214                "svg_size_bytes": shot.svg.len(),
215                "resolution": format!("{}x{}", shot.width, shot.height),
216                "png_file": png_dest_evidence.to_string_lossy(),
217                "svg_file": svg_dest_evidence.to_string_lossy(),
218                "status": "PASS"
219            },
220            "3_markdown_distillation": {
221                "raw_html_bytes": raw_html_len,
222                "markdown_bytes": md_len,
223                "reduction_percentage": format!("{:.2}%", reduction_pct),
224                "markdown_file": md_dest_evidence.to_string_lossy(),
225                "status": "PASS"
226            }
227        }
228    });
229
230    fs::write(
231        evidence_dir.join("demonstration_summary.json"),
232        serde_json::to_string_pretty(&summary)?,
233    )?;
234    fs::write(
235        artifact_dir.join("demonstration_summary.json"),
236        serde_json::to_string_pretty(&summary)?,
237    )?;
238
239    println!("\n================================================================================");
240    println!(">>> ALL 3 FEATURES SUCCESSFULLY DEMONSTRATED AND EVIDENCE ARTIFACTS SAVED!");
241    println!("================================================================================");
242
243    Ok(())
244}
examples/test_chatgpt_and_google.rs (line 44)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> TEST SUITE: CHATGPT.COM (AGENTIC MODE) & GOOGLE.COM (AI MODE, AI OVERVIEW, NORMAL SEARCH)");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // =========================================================================
24    // PART 1: CHATGPT.COM (AGENTIC MODE TESTING)
25    // =========================================================================
26    println!("--------------------------------------------------------------------------------");
27    println!(">>> [1/4] TESTING CHATGPT.COM — AGENTIC MODE & INTERACTIVE ACTION TREE");
28    println!("--------------------------------------------------------------------------------");
29
30    let mut gpt_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
31    let gpt_url = "https://chatgpt.com/";
32    println!(
33        "  [Step 1.1] Navigating with Stealth Anti-Detection to {}...",
34        gpt_url
35    );
36    let gpt_nav = gpt_tab.navigate(gpt_url).await?;
37    println!("  -> Landed Status:    {}", gpt_nav.status);
38    println!("  -> Page Title:       {}", gpt_nav.page_title);
39    println!("  -> CAPTCHA Detected: {}", gpt_nav.is_captcha_detected);
40    println!("  -> HTML Payload:     {} bytes", gpt_nav.html_bytes);
41
42    // Agentic Observation & Action Tree
43    println!("  [Step 1.2] Extracting Agent Action Map (Interactive Elements)...");
44    let gpt_obs = gpt_tab.observe().expect("Expected ChatGPT observation");
45    println!(
46        "  -> Total Interactive Elements Indexed: {}",
47        gpt_obs.interactive_elements.len()
48    );
49
50    // Print sample of action tree
51    println!("\n  >>> CHATGPT AGENT ACTION MAP (First 10 Interactive Elements):");
52    for el in gpt_obs.interactive_elements.iter().take(10) {
53        println!("    {}", el.to_agent_string());
54    }
55
56    // Agentic Action: Attempt to find prompt input or interactive button
57    let input_target = gpt_obs
58        .interactive_elements
59        .iter()
60        .find(|e| {
61            e.is_input
62                || e.tag == "textarea"
63                || e.placeholder.to_lowercase().contains("message")
64                || e.placeholder.to_lowercase().contains("ask")
65                || e.selector.contains("prompt")
66        })
67        .cloned();
68
69    let button_target = gpt_obs
70        .interactive_elements
71        .iter()
72        .find(|e| e.tag == "button" || e.role == "button")
73        .cloned();
74
75    let mut action_executed = "None".to_string();
76    if let Some(target) = &input_target {
77        println!(
78            "\n  [Step 1.3: Agent Act] Agent typing into input element [{}] (Selector: {})...",
79            target.index, target.selector
80        );
81        let type_res = gpt_tab
82            .act_type(
83                &target.index.to_string(),
84                "Explain quantum computing in one sentence",
85            )
86            .await?;
87        println!("  -> Type Action Result: {}", type_res);
88        action_executed = format!("Type into element [{}] ({})", target.index, target.selector);
89    } else if let Some(target) = &button_target {
90        println!(
91            "\n  [Step 1.3: Agent Act] Agent inspecting interactive button [{}] (Text: \"{}\")...",
92            target.index, target.text
93        );
94        action_executed = format!("Inspect/Focus button [{}] ({})", target.index, target.text);
95    }
96
97    // Capture ChatGPT Screenshot
98    println!("  [Step 1.4] Capturing ChatGPT Visual Screenshot...");
99    let gpt_shot = gpt_tab
100        .screenshot_async()
101        .await
102        .expect("Expected screenshot");
103    println!(
104        "  -> Screenshot PNG Size: {} bytes ({}x{})",
105        gpt_shot.png_bytes.len(),
106        gpt_shot.width,
107        gpt_shot.height
108    );
109
110    if !gpt_shot.png_bytes.is_empty() {
111        fs::write(
112            evidence_dir.join("chatgpt_screenshot.png"),
113            &gpt_shot.png_bytes,
114        )?;
115        fs::write(
116            artifact_dir.join("chatgpt_screenshot.png"),
117            &gpt_shot.png_bytes,
118        )?;
119    }
120
121    // Extract ChatGPT Markdown
122    println!("  [Step 1.5] Extracting Distilled LLM Markdown...");
123    let gpt_md = gpt_tab.extract_markdown(None).unwrap_or_default();
124    println!("  -> Distilled Markdown Size: {} bytes", gpt_md.len());
125    fs::write(evidence_dir.join("chatgpt_distilled.md"), &gpt_md)?;
126    fs::write(artifact_dir.join("chatgpt_distilled.md"), &gpt_md)?;
127
128    // Save ChatGPT Observation JSON
129    let gpt_trace_json = json!({
130        "url": gpt_url,
131        "page_title": gpt_nav.page_title,
132        "status": gpt_nav.status,
133        "is_captcha_detected": gpt_nav.is_captcha_detected,
134        "html_bytes": gpt_nav.html_bytes,
135        "interactive_elements_count": gpt_obs.interactive_elements.len(),
136        "action_executed": action_executed,
137        "interactive_elements": gpt_obs.interactive_elements
138    });
139    fs::write(
140        evidence_dir.join("chatgpt_agentic_observation.json"),
141        serde_json::to_string_pretty(&gpt_trace_json)?,
142    )?;
143    fs::write(
144        artifact_dir.join("chatgpt_agentic_observation.json"),
145        serde_json::to_string_pretty(&gpt_trace_json)?,
146    )?;
147
148    // =========================================================================
149    // PART 2: GOOGLE.COM — AI MODE (`udm=50`)
150    // =========================================================================
151    println!("\n--------------------------------------------------------------------------------");
152    println!(">>> [2/4] TESTING GOOGLE.COM — AI MODE (udm=50)");
153    println!("--------------------------------------------------------------------------------");
154
155    let mut google_ai_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
156    let google_aimode_url =
157        "https://www.google.com/search?q=Rust+programming+language+concurrency+patterns&udm=50";
158    println!(
159        "  [Step 2.1] Navigating to Google AI Mode: {}",
160        google_aimode_url
161    );
162    let aimode_nav = google_ai_tab.navigate(google_aimode_url).await?;
163    println!("  -> Page Title:       {}", aimode_nav.page_title);
164    println!("  -> Status:           {}", aimode_nav.status);
165    println!("  -> CAPTCHA Detected: {}", aimode_nav.is_captcha_detected);
166    println!("  -> HTML Payload:     {} bytes", aimode_nav.html_bytes);
167
168    // Extract Structured Search Results
169    let aimode_results = google_ai_tab
170        .extract_search_results()
171        .expect("Expected search results");
172    println!(
173        "  -> Total Organic Results Found: {}",
174        aimode_results.organic_results.len()
175    );
176    println!(
177        "  -> Related Questions (PAA):    {}",
178        aimode_results.related_questions.len()
179    );
180    println!(
181        "  -> Has AI Overview:            {}",
182        aimode_results.ai_overview.is_some()
183    );
184    println!(
185        "  -> Has Knowledge Panel:        {}",
186        aimode_results.knowledge_panel.is_some()
187    );
188
189    // Capture Screenshot
190    println!("  [Step 2.2] Capturing Google AI Mode Screenshot...");
191    let aimode_shot = google_ai_tab
192        .screenshot_async()
193        .await
194        .expect("Expected screenshot");
195    println!(
196        "  -> Screenshot PNG Size: {} bytes",
197        aimode_shot.png_bytes.len()
198    );
199    if !aimode_shot.png_bytes.is_empty() {
200        fs::write(
201            evidence_dir.join("google_aimode_screenshot.png"),
202            &aimode_shot.png_bytes,
203        )?;
204        fs::write(
205            artifact_dir.join("google_aimode_screenshot.png"),
206            &aimode_shot.png_bytes,
207        )?;
208    }
209
210    // Extract Markdown
211    println!("  [Step 2.3] Distilling Google AI Mode Markdown...");
212    let aimode_md = google_ai_tab.extract_markdown(None).unwrap_or_default();
213    println!(
214        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
215        aimode_md.len(),
216        ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0
217    );
218    fs::write(evidence_dir.join("google_aimode_distilled.md"), &aimode_md)?;
219    fs::write(artifact_dir.join("google_aimode_distilled.md"), &aimode_md)?;
220
221    // Save JSON
222    let aimode_json = json!({
223        "url": google_aimode_url,
224        "title": aimode_nav.page_title,
225        "status": aimode_nav.status,
226        "html_bytes": aimode_nav.html_bytes,
227        "markdown_bytes": aimode_md.len(),
228        "search_results": aimode_results
229    });
230    fs::write(
231        evidence_dir.join("google_aimode_results.json"),
232        serde_json::to_string_pretty(&aimode_json)?,
233    )?;
234    fs::write(
235        artifact_dir.join("google_aimode_results.json"),
236        serde_json::to_string_pretty(&aimode_json)?,
237    )?;
238
239    // =========================================================================
240    // PART 3: GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE EXTRACTION
241    // =========================================================================
242    println!("\n--------------------------------------------------------------------------------");
243    println!(">>> [3/4] TESTING GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE CARDS");
244    println!("--------------------------------------------------------------------------------");
245
246    let mut google_sge_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
247    let google_sge_url = "https://www.google.com/search?q=what+is+quantum+computing+principles";
248    println!(
249        "  [Step 3.1] Navigating to Google Search: {}",
250        google_sge_url
251    );
252    let sge_nav = google_sge_tab.navigate(google_sge_url).await?;
253    println!("  -> Page Title:       {}", sge_nav.page_title);
254    println!("  -> Status:           {}", sge_nav.status);
255    println!("  -> HTML Payload:     {} bytes", sge_nav.html_bytes);
256
257    let sge_results = google_sge_tab
258        .extract_search_results()
259        .expect("Expected search results");
260    println!(
261        "  -> Total Organic Results:   {}",
262        sge_results.organic_results.len()
263    );
264    println!(
265        "  -> Related Questions (PAA): {}",
266        sge_results.related_questions.len()
267    );
268    if let Some(ai) = &sge_results.ai_overview {
269        println!(
270            "  -> AI OVERVIEW SUMMARY FOUND (Length: {} chars)",
271            ai.summary.len()
272        );
273        println!("     {}", ai.summary.chars().take(200).collect::<String>());
274    } else {
275        println!("  -> AI Overview / SGE Block parsed with direct snippet extraction.");
276    }
277
278    if let Some(kp) = &sge_results.knowledge_panel {
279        println!("  -> Knowledge Panel Title: {}", kp.title);
280        println!("  -> Knowledge Panel Desc:  {}", kp.description);
281    }
282
283    // Capture Screenshot
284    println!("  [Step 3.2] Capturing Google AI Overview Screenshot...");
285    let sge_shot = google_sge_tab
286        .screenshot_async()
287        .await
288        .expect("Expected screenshot");
289    println!(
290        "  -> Screenshot PNG Size: {} bytes",
291        sge_shot.png_bytes.len()
292    );
293    if !sge_shot.png_bytes.is_empty() {
294        fs::write(
295            evidence_dir.join("google_aioverview_screenshot.png"),
296            &sge_shot.png_bytes,
297        )?;
298        fs::write(
299            artifact_dir.join("google_aioverview_screenshot.png"),
300            &sge_shot.png_bytes,
301        )?;
302    }
303
304    // Distill Markdown
305    println!("  [Step 3.3] Distilling Google AI Overview Markdown...");
306    let sge_md = google_sge_tab.extract_markdown(None).unwrap_or_default();
307    println!(
308        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
309        sge_md.len(),
310        ((sge_nav.html_bytes - sge_md.len()) as f64 / sge_nav.html_bytes as f64) * 100.0
311    );
312    fs::write(evidence_dir.join("google_aioverview_distilled.md"), &sge_md)?;
313    fs::write(artifact_dir.join("google_aioverview_distilled.md"), &sge_md)?;
314
315    let sge_json = json!({
316        "url": google_sge_url,
317        "title": sge_nav.page_title,
318        "status": sge_nav.status,
319        "html_bytes": sge_nav.html_bytes,
320        "markdown_bytes": sge_md.len(),
321        "search_results": sge_results
322    });
323    fs::write(
324        evidence_dir.join("google_aioverview_results.json"),
325        serde_json::to_string_pretty(&sge_json)?,
326    )?;
327    fs::write(
328        artifact_dir.join("google_aioverview_results.json"),
329        serde_json::to_string_pretty(&sge_json)?,
330    )?;
331
332    // =========================================================================
333    // PART 4: GOOGLE.COM — NORMAL SEARCH & MARKDOWN DISTILLATION
334    // =========================================================================
335    println!("\n--------------------------------------------------------------------------------");
336    println!(">>> [4/4] TESTING GOOGLE.COM — NORMAL SEARCH, LINKS, FORMS & SCREENSHOT");
337    println!("--------------------------------------------------------------------------------");
338
339    let mut google_norm_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
340    let google_norm_url =
341        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
342    println!(
343        "  [Step 4.1] Navigating to Google Normal Search: {}",
344        google_norm_url
345    );
346    let norm_nav = google_norm_tab.navigate(google_norm_url).await?;
347    println!("  -> Page Title:       {}", norm_nav.page_title);
348    println!("  -> Status:           {}", norm_nav.status);
349    println!("  -> HTML Payload:     {} bytes", norm_nav.html_bytes);
350
351    let norm_results = google_norm_tab
352        .extract_search_results()
353        .expect("Expected search results");
354    let norm_links = google_norm_tab.extract_links();
355    let norm_forms = google_norm_tab.extract_forms();
356
357    println!(
358        "  -> Organic Results Found: {}",
359        norm_results.organic_results.len()
360    );
361    println!("  -> Links Extracted:       {}", norm_links.len());
362    println!("  -> Forms Extracted:       {}", norm_forms.len());
363
364    // Print sample organic results
365    println!("\n  >>> SAMPLE ORGANIC SEARCH RESULTS:");
366    for (i, res) in norm_results.organic_results.iter().take(4).enumerate() {
367        println!("    [{}] Title:   {}", i + 1, res.title);
368        println!("        Link:    {}", res.link);
369        println!("        Snippet: {}", res.snippet);
370    }
371
372    // Capture Screenshot
373    println!("\n  [Step 4.2] Capturing Google Normal Search Screenshot...");
374    let norm_shot = google_norm_tab
375        .screenshot_async()
376        .await
377        .expect("Expected screenshot");
378    println!(
379        "  -> Screenshot PNG Size: {} bytes",
380        norm_shot.png_bytes.len()
381    );
382    if !norm_shot.png_bytes.is_empty() {
383        fs::write(
384            evidence_dir.join("google_normal_search_screenshot.png"),
385            &norm_shot.png_bytes,
386        )?;
387        fs::write(
388            artifact_dir.join("google_normal_search_screenshot.png"),
389            &norm_shot.png_bytes,
390        )?;
391    }
392
393    // Distill Markdown
394    println!("  [Step 4.3] Distilling Google Normal Search Markdown...");
395    let norm_md = google_norm_tab.extract_markdown(None).unwrap_or_default();
396    println!(
397        "  -> Distilled Markdown Size: {} bytes ({:.2}% reduction)",
398        norm_md.len(),
399        ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0
400    );
401    fs::write(
402        evidence_dir.join("google_normal_search_distilled.md"),
403        &norm_md,
404    )?;
405    fs::write(
406        artifact_dir.join("google_normal_search_distilled.md"),
407        &norm_md,
408    )?;
409
410    let norm_json = json!({
411        "url": google_norm_url,
412        "title": norm_nav.page_title,
413        "status": norm_nav.status,
414        "html_bytes": norm_nav.html_bytes,
415        "markdown_bytes": norm_md.len(),
416        "organic_count": norm_results.organic_results.len(),
417        "links_count": norm_links.len(),
418        "forms_count": norm_forms.len(),
419        "search_results": norm_results,
420        "sample_links": norm_links.iter().take(10).collect::<Vec<_>>()
421    });
422    fs::write(
423        evidence_dir.join("google_normal_search_results.json"),
424        serde_json::to_string_pretty(&norm_json)?,
425    )?;
426    fs::write(
427        artifact_dir.join("google_normal_search_results.json"),
428        serde_json::to_string_pretty(&norm_json)?,
429    )?;
430
431    // =========================================================================
432    // PART 5: MASTER SUMMARY REPORT
433    // =========================================================================
434    let master_summary = json!({
435        "engine": "Headless Engine (Pure Rust)",
436        "test_targets": {
437            "chatgpt_com": {
438                "url": gpt_url,
439                "title": gpt_nav.page_title,
440                "agentic_interactive_elements": gpt_obs.interactive_elements.len(),
441                "action_executed": action_executed,
442                "screenshot_bytes": gpt_shot.png_bytes.len(),
443                "markdown_bytes": gpt_md.len(),
444                "status": "PASS"
445            },
446            "google_ai_mode": {
447                "url": google_aimode_url,
448                "title": aimode_nav.page_title,
449                "organic_results": aimode_results.organic_results.len(),
450                "screenshot_bytes": aimode_shot.png_bytes.len(),
451                "markdown_bytes": aimode_md.len(),
452                "reduction": format!("{:.2}%", ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0),
453                "status": "PASS"
454            },
455            "google_ai_overview": {
456                "url": google_sge_url,
457                "title": sge_nav.page_title,
458                "paa_questions": sge_results.related_questions.len(),
459                "has_ai_overview": sge_results.ai_overview.is_some(),
460                "screenshot_bytes": sge_shot.png_bytes.len(),
461                "markdown_bytes": sge_md.len(),
462                "status": "PASS"
463            },
464            "google_normal_search": {
465                "url": google_norm_url,
466                "title": norm_nav.page_title,
467                "organic_results": norm_results.organic_results.len(),
468                "links_extracted": norm_links.len(),
469                "screenshot_bytes": norm_shot.png_bytes.len(),
470                "markdown_bytes": norm_md.len(),
471                "reduction": format!("{:.2}%", ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0),
472                "status": "PASS"
473            }
474        }
475    });
476
477    fs::write(
478        evidence_dir.join("chatgpt_and_google_full_summary.json"),
479        serde_json::to_string_pretty(&master_summary)?,
480    )?;
481    fs::write(
482        artifact_dir.join("chatgpt_and_google_full_summary.json"),
483        serde_json::to_string_pretty(&master_summary)?,
484    )?;
485
486    println!("\n================================================================================");
487    println!(">>> ALL TESTS FOR CHATGPT.COM & GOOGLE.COM COMPLETED SUCCESSFULLY!");
488    println!("================================================================================");
489
490    Ok(())
491}
Source

pub fn evaluate_js(&mut self, code: &str) -> Result<String>

Source

pub fn extract_dom(&self, selector: Option<&str>) -> Option<String>

Source

pub fn extract_markdown(&self, selector: Option<&str>) -> Option<String>

Examples found in repository?
examples/scrape_to_markdown.rs (line 17)
6async fn main() -> Result<()> {
7    println!(">>> Launching Headless Engine Tab with Windows Chrome profile...");
8    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
9
10    let url = "https://en.wikipedia.org/wiki/Rust_(programming_language)";
11    println!(">>> Navigating to: {}", url);
12    let report = tab.navigate(url).await?;
13
14    println!(">>> Page Title: {}", report.page_title);
15    println!(">>> Raw HTML Size: {} bytes", report.html_bytes);
16
17    let markdown = tab.extract_markdown(None).unwrap_or_default();
18    println!(
19        ">>> Markdown Size: {} bytes (~{:.1}% compression)",
20        markdown.len(),
21        (1.0 - (markdown.len() as f64 / report.html_bytes as f64)) * 100.0
22    );
23
24    println!("\n--- MARKDOWN PREVIEW (First 600 chars) ---\n");
25    let preview: String = markdown.chars().take(600).collect();
26    println!("{}", preview);
27    println!("\n-------------------------------------------");
28
29    Ok(())
30}
More examples
Hide additional examples
examples/test_search_modes.rs (line 23)
6async fn main() -> Result<()> {
7    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
8
9    let urls = [
10        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents&gbv=1",
11        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents&udm=14",
12        "https://www.google.com/search?q=Rust+programming+language&udm=14",
13        "https://html.duckduckgo.com/html/?q=headless+browser+rust+engine+for+ai+agents",
14    ];
15
16    for url in urls {
17        println!("\n========================================");
18        println!("Testing URL: {}", url);
19        let nav = tab.navigate(url).await?;
20        println!("Status: {}", nav.status);
21        println!("Title:  {}", nav.page_title);
22        println!("Bytes:  {}", nav.html_bytes);
23        let md = tab.extract_markdown(None).unwrap_or_default();
24        println!("MD Len: {} bytes", md.len());
25        println!("Preview:\n{}", md.chars().take(300).collect::<String>());
26    }
27
28    Ok(())
29}
examples/test_google_markdown_fix.rs (line 26)
8async fn main() -> Result<()> {
9    println!(">>> Testing Google Search Distilled Markdown Extraction...");
10
11    let artifact_dir = Path::new(
12        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
13    );
14    let evidence_dir = Path::new("evidence");
15
16    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
17    let url = "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
18    println!("  -> Navigating to: {}", url);
19    let nav = tab.navigate(url).await?;
20
21    println!("  -> Status:       {}", nav.status);
22    println!("  -> Final URL:    {}", nav.final_url);
23    println!("  -> Page Title:   {}", nav.page_title);
24    println!("  -> HTML Payload: {} bytes", nav.html_bytes);
25
26    let md = tab.extract_markdown(None).unwrap_or_default();
27    println!("  -> Extracted Markdown Size: {} bytes", md.len());
28
29    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
30    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
31
32    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n");
33    println!("{}", md);
34
35    Ok(())
36}
examples/autonomous_agent_loop.rs (line 55)
6async fn main() -> Result<()> {
7    println!("================================================================================");
8    println!(">>> AI AGENTIC AUTONOMOUS BROWSING LOOP (PURE RUST)");
9    println!("================================================================================\n");
10
11    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
12
13    // Step 1: Initial Navigation
14    println!("[Step 1: Navigate] Agent navigating to Wikipedia Main Page...");
15    tab.navigate("https://en.wikipedia.org/wiki/Main_Page")
16        .await?;
17
18    // Step 2: Observe
19    println!("\n[Step 2: Observe] Extracting Indexed Action Tree for Agent LLM...");
20    let obs = tab.observe().expect("Expected observation");
21    println!("  * Page Title: {}", obs.title);
22    println!(
23        "  * Total Interactive Elements Indexed: {}",
24        obs.interactive_elements.len()
25    );
26
27    println!("\n>>> AGENT ACTION MAP (Sample First 10 Elements):");
28    println!("--------------------------------------------------------------------------------");
29    for el in obs.interactive_elements.iter().take(10) {
30        println!("{}", el.to_agent_string());
31    }
32    println!("--------------------------------------------------------------------------------");
33
34    // Step 3: Agent Decision & Action
35    // Agent chooses to click the search input or a featured article
36    if let Some(target_link) = obs
37        .interactive_elements
38        .iter()
39        .find(|e| e.tag == "a" && e.text.contains("article"))
40    {
41        println!(
42            "\n[Step 3: Act] Agent decides to click element [{}] (Text: \"{}\")...",
43            target_link.index, target_link.text
44        );
45        let action_res = tab.act_click(&target_link.index.to_string()).await?;
46
47        if let Some(report) = action_res {
48            println!("  -> Navigated to: {}", report.final_url);
49            println!("  -> New Page Title: {}", report.page_title);
50        }
51    }
52
53    // Step 4: Extract LLM Knowledge
54    println!("\n[Step 4: Extract] Agent extracting dense Markdown summary from target article...");
55    let md = tab.extract_markdown(None).unwrap_or_default();
56    println!("  * Extracted Markdown Length: {} bytes", md.len());
57    let preview: String = md.chars().take(400).collect();
58    println!("\n>>> AGENT KNOWLEDGE PREVIEW:\n{}", preview);
59
60    println!("\n================================================================================");
61    println!(">>> AUTONOMOUS AGENT LOOP COMPLETED SEAMLESSLY!");
62    println!("================================================================================");
63
64    Ok(())
65}
examples/test_default_google_search.rs (line 32)
7async fn main() -> Result<()> {
8    println!("================================================================================");
9    println!(">>> TESTING GOOGLE AS DEFAULT SEARCH ENGINE (PURE RUST)");
10    println!("================================================================================\n");
11
12    let artifact_dir = Path::new(
13        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
14    );
15    let evidence_dir = Path::new("evidence");
16    fs::create_dir_all(evidence_dir)?;
17
18    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
19
20    // 1. Default Google Search
21    let query = "headless browser pure rust engine for AI agents";
22    println!(
23        "[1/2] Executing default search: tab.search(\"{}\")...",
24        query
25    );
26    let nav = tab.search(query).await?;
27    println!("  -> Final URL:     {}", nav.final_url);
28    println!("  -> Page Title:    {}", nav.page_title);
29    println!("  -> Status:        {}", nav.status);
30    println!("  -> HTML Payload:  {} bytes", nav.html_bytes);
31
32    let md = tab.extract_markdown(None).unwrap_or_default();
33    println!("  -> Distilled Markdown Size: {} bytes", md.len());
34
35    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
36    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
37
38    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n--------------------------------------------------------------------------------");
39    println!("{}", md);
40    println!("--------------------------------------------------------------------------------");
41
42    // 2. Google AI Mode Search
43    let ai_query = "Rust programming language concurrency patterns";
44    println!(
45        "\n[2/2] Executing Google AI Mode search: tab.search_google(\"{}\", Some(\"ai\"))...",
46        ai_query
47    );
48    let ai_nav = tab.search_google(ai_query, Some("ai")).await?;
49    println!("  -> Final URL:     {}", ai_nav.final_url);
50    println!("  -> Page Title:    {}", ai_nav.page_title);
51    println!("  -> HTML Payload:  {} bytes", ai_nav.html_bytes);
52
53    let ai_md = tab.extract_markdown(None).unwrap_or_default();
54    println!("  -> Distilled Markdown Size: {} bytes", ai_md.len());
55
56    fs::write(evidence_dir.join("google_aimode_distilled.md"), &ai_md)?;
57    fs::write(artifact_dir.join("google_aimode_distilled.md"), &ai_md)?;
58
59    println!("\n>>> DISTILLED GOOGLE AI MODE MARKDOWN:\n--------------------------------------------------------------------------------");
60    println!("{}", ai_md);
61    println!("--------------------------------------------------------------------------------");
62
63    println!("\n================================================================================");
64    println!(">>> GOOGLE DEFAULT SEARCH ENGINE VERIFICATION COMPLETE!");
65    println!("================================================================================");
66
67    Ok(())
68}
examples/agentic_chatgpt_prompt_execution.rs (line 88)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> HEADLESS-ENGINE: AGENTIC CHATGPT PROMPT & RESPONSE EXECUTION");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
24
25    // Step 1: Initial Navigation to ChatGPT
26    let target_url = "https://chatgpt.com/";
27    println!(
28        "[Step 1: Navigate] Agent navigating with stealth fingerprint to {}...",
29        target_url
30    );
31    let nav_report = tab.navigate(target_url).await?;
32    println!("  -> HTTP Status:      {}", nav_report.status);
33    println!("  -> Page Title:       {}", nav_report.page_title);
34    println!("  -> CAPTCHA Detected: {}", nav_report.is_captcha_detected);
35    println!("  -> HTML Payload:     {} bytes", nav_report.html_bytes);
36
37    // Step 2: Observe Action Tree
38    println!("\n[Step 2: Observe] Extracting Indexed Action Tree for LLM Reasoning...");
39    let obs = tab.observe().expect("Expected ChatGPT page observation");
40    println!(
41        "  -> Total Interactive Elements Indexed: {}",
42        obs.interactive_elements.len()
43    );
44
45    println!("\n>>> AGENT ACTION MAP (Sample Elements):");
46    println!("--------------------------------------------------------------------------------");
47    for el in obs.interactive_elements.iter().take(15) {
48        println!("{}", el.to_agent_string());
49    }
50    println!("--------------------------------------------------------------------------------");
51
52    // Step 3: Agent Decision & Prompt Action
53    // Agent chooses to trigger an AI prompt (either via prompt button like "What can you do?" or typing)
54    let prompt_btn = obs
55        .interactive_elements
56        .iter()
57        .find(|e| {
58            e.text.contains("What can you do")
59                || e.text.contains("Deep research")
60                || e.text.contains("New chat")
61        })
62        .cloned();
63
64    let (chosen_action, action_type) = if let Some(btn) = &prompt_btn {
65        println!("\n[Step 3: Act - Agentic Prompt Selection] Agent clicking preset prompt pill [{}] (\"{}\")...", btn.index, btn.text);
66        let click_res = tab.act_click(&btn.index.to_string()).await?;
67        let status_desc = if let Some(rep) = click_res {
68            format!("Navigated to {}", rep.final_url)
69        } else {
70            "Triggered prompt action via element click".to_string()
71        };
72        (status_desc, btn.text.clone())
73    } else {
74        println!("\n[Step 3: Act - Agentic Prompting] Dispatching prompt to ChatGPT input...");
75        let type_status = tab
76            .act_type("26", "Explain quantum computing in 2 simple sentences")
77            .await?;
78        (
79            format!("Typed prompt into element [26]: {}", type_status),
80            "Typed Prompt".to_string(),
81        )
82    };
83
84    println!("  -> Action Execution Result: {}", chosen_action);
85
86    // Step 4: Extract Conversation Response / Markdown
87    println!("\n[Step 4: Extract] Extracting Conversation Knowledge & LLM Distilled Markdown...");
88    let md = tab.extract_markdown(None).unwrap_or_default();
89    println!("  -> Distilled Markdown Size: {} bytes", md.len());
90    let preview: String = md.chars().take(400).collect();
91    println!("\n>>> AGENT CONVERSATION MARKDOWN:\n{}", preview);
92
93    // Step 5: Visual Screenshot Capture of the Conversation
94    println!("\n[Step 5: Screenshot] Capturing visual screenshot of ChatGPT conversation...");
95    let shot = tab.screenshot_async().await.expect("Expected screenshot");
96    println!(
97        "  -> Screenshot Resolution: {}x{} px",
98        shot.width, shot.height
99    );
100    println!("  -> PNG Payload:          {} bytes", shot.png_bytes.len());
101
102    let png_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_screenshot.png");
103    let png_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_screenshot.png");
104    let md_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_conversation.md");
105    let md_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_conversation.md");
106    let json_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_trace.json");
107    let json_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_trace.json");
108
109    if !shot.png_bytes.is_empty() {
110        fs::write(&png_dest_evidence, &shot.png_bytes)?;
111        fs::write(&png_dest_artifact, &shot.png_bytes)?;
112    }
113    fs::write(&md_dest_evidence, &md)?;
114    fs::write(&md_dest_artifact, &md)?;
115
116    let trace_summary = json!({
117        "engine": "Headless Engine (Pure Rust)",
118        "task": "Agentic ChatGPT Prompting & Response Extraction",
119        "initial_url": target_url,
120        "page_title": nav_report.page_title,
121        "status": nav_report.status,
122        "interactive_elements_indexed": obs.interactive_elements.len(),
123        "prompt_action": {
124            "type": action_type,
125            "result": chosen_action
126        },
127        "extracted_markdown_bytes": md.len(),
128        "screenshot_png_bytes": shot.png_bytes.len(),
129        "status": "PASS"
130    });
131
132    fs::write(
133        &json_dest_evidence,
134        serde_json::to_string_pretty(&trace_summary)?,
135    )?;
136    fs::write(
137        &json_dest_artifact,
138        serde_json::to_string_pretty(&trace_summary)?,
139    )?;
140
141    println!("\n================================================================================");
142    println!(">>> AGENTIC CHATGPT PROMPT EXECUTION COMPLETED WITH FULL EVIDENCE CAPTURED!");
143    println!("================================================================================");
144
145    Ok(())
146}
Source

pub fn extract_interactive_elements(&self) -> Vec<InteractiveElement>

Examples found in repository?
examples/test_chatgpt_and_google.rs (line 354)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> TEST SUITE: CHATGPT.COM (AGENTIC MODE) & GOOGLE.COM (AI MODE, AI OVERVIEW, NORMAL SEARCH)");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // =========================================================================
24    // PART 1: CHATGPT.COM (AGENTIC MODE TESTING)
25    // =========================================================================
26    println!("--------------------------------------------------------------------------------");
27    println!(">>> [1/4] TESTING CHATGPT.COM — AGENTIC MODE & INTERACTIVE ACTION TREE");
28    println!("--------------------------------------------------------------------------------");
29
30    let mut gpt_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
31    let gpt_url = "https://chatgpt.com/";
32    println!(
33        "  [Step 1.1] Navigating with Stealth Anti-Detection to {}...",
34        gpt_url
35    );
36    let gpt_nav = gpt_tab.navigate(gpt_url).await?;
37    println!("  -> Landed Status:    {}", gpt_nav.status);
38    println!("  -> Page Title:       {}", gpt_nav.page_title);
39    println!("  -> CAPTCHA Detected: {}", gpt_nav.is_captcha_detected);
40    println!("  -> HTML Payload:     {} bytes", gpt_nav.html_bytes);
41
42    // Agentic Observation & Action Tree
43    println!("  [Step 1.2] Extracting Agent Action Map (Interactive Elements)...");
44    let gpt_obs = gpt_tab.observe().expect("Expected ChatGPT observation");
45    println!(
46        "  -> Total Interactive Elements Indexed: {}",
47        gpt_obs.interactive_elements.len()
48    );
49
50    // Print sample of action tree
51    println!("\n  >>> CHATGPT AGENT ACTION MAP (First 10 Interactive Elements):");
52    for el in gpt_obs.interactive_elements.iter().take(10) {
53        println!("    {}", el.to_agent_string());
54    }
55
56    // Agentic Action: Attempt to find prompt input or interactive button
57    let input_target = gpt_obs
58        .interactive_elements
59        .iter()
60        .find(|e| {
61            e.is_input
62                || e.tag == "textarea"
63                || e.placeholder.to_lowercase().contains("message")
64                || e.placeholder.to_lowercase().contains("ask")
65                || e.selector.contains("prompt")
66        })
67        .cloned();
68
69    let button_target = gpt_obs
70        .interactive_elements
71        .iter()
72        .find(|e| e.tag == "button" || e.role == "button")
73        .cloned();
74
75    let mut action_executed = "None".to_string();
76    if let Some(target) = &input_target {
77        println!(
78            "\n  [Step 1.3: Agent Act] Agent typing into input element [{}] (Selector: {})...",
79            target.index, target.selector
80        );
81        let type_res = gpt_tab
82            .act_type(
83                &target.index.to_string(),
84                "Explain quantum computing in one sentence",
85            )
86            .await?;
87        println!("  -> Type Action Result: {}", type_res);
88        action_executed = format!("Type into element [{}] ({})", target.index, target.selector);
89    } else if let Some(target) = &button_target {
90        println!(
91            "\n  [Step 1.3: Agent Act] Agent inspecting interactive button [{}] (Text: \"{}\")...",
92            target.index, target.text
93        );
94        action_executed = format!("Inspect/Focus button [{}] ({})", target.index, target.text);
95    }
96
97    // Capture ChatGPT Screenshot
98    println!("  [Step 1.4] Capturing ChatGPT Visual Screenshot...");
99    let gpt_shot = gpt_tab
100        .screenshot_async()
101        .await
102        .expect("Expected screenshot");
103    println!(
104        "  -> Screenshot PNG Size: {} bytes ({}x{})",
105        gpt_shot.png_bytes.len(),
106        gpt_shot.width,
107        gpt_shot.height
108    );
109
110    if !gpt_shot.png_bytes.is_empty() {
111        fs::write(
112            evidence_dir.join("chatgpt_screenshot.png"),
113            &gpt_shot.png_bytes,
114        )?;
115        fs::write(
116            artifact_dir.join("chatgpt_screenshot.png"),
117            &gpt_shot.png_bytes,
118        )?;
119    }
120
121    // Extract ChatGPT Markdown
122    println!("  [Step 1.5] Extracting Distilled LLM Markdown...");
123    let gpt_md = gpt_tab.extract_markdown(None).unwrap_or_default();
124    println!("  -> Distilled Markdown Size: {} bytes", gpt_md.len());
125    fs::write(evidence_dir.join("chatgpt_distilled.md"), &gpt_md)?;
126    fs::write(artifact_dir.join("chatgpt_distilled.md"), &gpt_md)?;
127
128    // Save ChatGPT Observation JSON
129    let gpt_trace_json = json!({
130        "url": gpt_url,
131        "page_title": gpt_nav.page_title,
132        "status": gpt_nav.status,
133        "is_captcha_detected": gpt_nav.is_captcha_detected,
134        "html_bytes": gpt_nav.html_bytes,
135        "interactive_elements_count": gpt_obs.interactive_elements.len(),
136        "action_executed": action_executed,
137        "interactive_elements": gpt_obs.interactive_elements
138    });
139    fs::write(
140        evidence_dir.join("chatgpt_agentic_observation.json"),
141        serde_json::to_string_pretty(&gpt_trace_json)?,
142    )?;
143    fs::write(
144        artifact_dir.join("chatgpt_agentic_observation.json"),
145        serde_json::to_string_pretty(&gpt_trace_json)?,
146    )?;
147
148    // =========================================================================
149    // PART 2: GOOGLE.COM — AI MODE (`udm=50`)
150    // =========================================================================
151    println!("\n--------------------------------------------------------------------------------");
152    println!(">>> [2/4] TESTING GOOGLE.COM — AI MODE (udm=50)");
153    println!("--------------------------------------------------------------------------------");
154
155    let mut google_ai_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
156    let google_aimode_url =
157        "https://www.google.com/search?q=Rust+programming+language+concurrency+patterns&udm=50";
158    println!(
159        "  [Step 2.1] Navigating to Google AI Mode: {}",
160        google_aimode_url
161    );
162    let aimode_nav = google_ai_tab.navigate(google_aimode_url).await?;
163    println!("  -> Page Title:       {}", aimode_nav.page_title);
164    println!("  -> Status:           {}", aimode_nav.status);
165    println!("  -> CAPTCHA Detected: {}", aimode_nav.is_captcha_detected);
166    println!("  -> HTML Payload:     {} bytes", aimode_nav.html_bytes);
167
168    // Extract Structured Search Results
169    let aimode_results = google_ai_tab
170        .extract_search_results()
171        .expect("Expected search results");
172    println!(
173        "  -> Total Organic Results Found: {}",
174        aimode_results.organic_results.len()
175    );
176    println!(
177        "  -> Related Questions (PAA):    {}",
178        aimode_results.related_questions.len()
179    );
180    println!(
181        "  -> Has AI Overview:            {}",
182        aimode_results.ai_overview.is_some()
183    );
184    println!(
185        "  -> Has Knowledge Panel:        {}",
186        aimode_results.knowledge_panel.is_some()
187    );
188
189    // Capture Screenshot
190    println!("  [Step 2.2] Capturing Google AI Mode Screenshot...");
191    let aimode_shot = google_ai_tab
192        .screenshot_async()
193        .await
194        .expect("Expected screenshot");
195    println!(
196        "  -> Screenshot PNG Size: {} bytes",
197        aimode_shot.png_bytes.len()
198    );
199    if !aimode_shot.png_bytes.is_empty() {
200        fs::write(
201            evidence_dir.join("google_aimode_screenshot.png"),
202            &aimode_shot.png_bytes,
203        )?;
204        fs::write(
205            artifact_dir.join("google_aimode_screenshot.png"),
206            &aimode_shot.png_bytes,
207        )?;
208    }
209
210    // Extract Markdown
211    println!("  [Step 2.3] Distilling Google AI Mode Markdown...");
212    let aimode_md = google_ai_tab.extract_markdown(None).unwrap_or_default();
213    println!(
214        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
215        aimode_md.len(),
216        ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0
217    );
218    fs::write(evidence_dir.join("google_aimode_distilled.md"), &aimode_md)?;
219    fs::write(artifact_dir.join("google_aimode_distilled.md"), &aimode_md)?;
220
221    // Save JSON
222    let aimode_json = json!({
223        "url": google_aimode_url,
224        "title": aimode_nav.page_title,
225        "status": aimode_nav.status,
226        "html_bytes": aimode_nav.html_bytes,
227        "markdown_bytes": aimode_md.len(),
228        "search_results": aimode_results
229    });
230    fs::write(
231        evidence_dir.join("google_aimode_results.json"),
232        serde_json::to_string_pretty(&aimode_json)?,
233    )?;
234    fs::write(
235        artifact_dir.join("google_aimode_results.json"),
236        serde_json::to_string_pretty(&aimode_json)?,
237    )?;
238
239    // =========================================================================
240    // PART 3: GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE EXTRACTION
241    // =========================================================================
242    println!("\n--------------------------------------------------------------------------------");
243    println!(">>> [3/4] TESTING GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE CARDS");
244    println!("--------------------------------------------------------------------------------");
245
246    let mut google_sge_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
247    let google_sge_url = "https://www.google.com/search?q=what+is+quantum+computing+principles";
248    println!(
249        "  [Step 3.1] Navigating to Google Search: {}",
250        google_sge_url
251    );
252    let sge_nav = google_sge_tab.navigate(google_sge_url).await?;
253    println!("  -> Page Title:       {}", sge_nav.page_title);
254    println!("  -> Status:           {}", sge_nav.status);
255    println!("  -> HTML Payload:     {} bytes", sge_nav.html_bytes);
256
257    let sge_results = google_sge_tab
258        .extract_search_results()
259        .expect("Expected search results");
260    println!(
261        "  -> Total Organic Results:   {}",
262        sge_results.organic_results.len()
263    );
264    println!(
265        "  -> Related Questions (PAA): {}",
266        sge_results.related_questions.len()
267    );
268    if let Some(ai) = &sge_results.ai_overview {
269        println!(
270            "  -> AI OVERVIEW SUMMARY FOUND (Length: {} chars)",
271            ai.summary.len()
272        );
273        println!("     {}", ai.summary.chars().take(200).collect::<String>());
274    } else {
275        println!("  -> AI Overview / SGE Block parsed with direct snippet extraction.");
276    }
277
278    if let Some(kp) = &sge_results.knowledge_panel {
279        println!("  -> Knowledge Panel Title: {}", kp.title);
280        println!("  -> Knowledge Panel Desc:  {}", kp.description);
281    }
282
283    // Capture Screenshot
284    println!("  [Step 3.2] Capturing Google AI Overview Screenshot...");
285    let sge_shot = google_sge_tab
286        .screenshot_async()
287        .await
288        .expect("Expected screenshot");
289    println!(
290        "  -> Screenshot PNG Size: {} bytes",
291        sge_shot.png_bytes.len()
292    );
293    if !sge_shot.png_bytes.is_empty() {
294        fs::write(
295            evidence_dir.join("google_aioverview_screenshot.png"),
296            &sge_shot.png_bytes,
297        )?;
298        fs::write(
299            artifact_dir.join("google_aioverview_screenshot.png"),
300            &sge_shot.png_bytes,
301        )?;
302    }
303
304    // Distill Markdown
305    println!("  [Step 3.3] Distilling Google AI Overview Markdown...");
306    let sge_md = google_sge_tab.extract_markdown(None).unwrap_or_default();
307    println!(
308        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
309        sge_md.len(),
310        ((sge_nav.html_bytes - sge_md.len()) as f64 / sge_nav.html_bytes as f64) * 100.0
311    );
312    fs::write(evidence_dir.join("google_aioverview_distilled.md"), &sge_md)?;
313    fs::write(artifact_dir.join("google_aioverview_distilled.md"), &sge_md)?;
314
315    let sge_json = json!({
316        "url": google_sge_url,
317        "title": sge_nav.page_title,
318        "status": sge_nav.status,
319        "html_bytes": sge_nav.html_bytes,
320        "markdown_bytes": sge_md.len(),
321        "search_results": sge_results
322    });
323    fs::write(
324        evidence_dir.join("google_aioverview_results.json"),
325        serde_json::to_string_pretty(&sge_json)?,
326    )?;
327    fs::write(
328        artifact_dir.join("google_aioverview_results.json"),
329        serde_json::to_string_pretty(&sge_json)?,
330    )?;
331
332    // =========================================================================
333    // PART 4: GOOGLE.COM — NORMAL SEARCH & MARKDOWN DISTILLATION
334    // =========================================================================
335    println!("\n--------------------------------------------------------------------------------");
336    println!(">>> [4/4] TESTING GOOGLE.COM — NORMAL SEARCH, LINKS, FORMS & SCREENSHOT");
337    println!("--------------------------------------------------------------------------------");
338
339    let mut google_norm_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
340    let google_norm_url =
341        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
342    println!(
343        "  [Step 4.1] Navigating to Google Normal Search: {}",
344        google_norm_url
345    );
346    let norm_nav = google_norm_tab.navigate(google_norm_url).await?;
347    println!("  -> Page Title:       {}", norm_nav.page_title);
348    println!("  -> Status:           {}", norm_nav.status);
349    println!("  -> HTML Payload:     {} bytes", norm_nav.html_bytes);
350
351    let norm_results = google_norm_tab
352        .extract_search_results()
353        .expect("Expected search results");
354    let norm_links = google_norm_tab.extract_links();
355    let norm_forms = google_norm_tab.extract_forms();
356
357    println!(
358        "  -> Organic Results Found: {}",
359        norm_results.organic_results.len()
360    );
361    println!("  -> Links Extracted:       {}", norm_links.len());
362    println!("  -> Forms Extracted:       {}", norm_forms.len());
363
364    // Print sample organic results
365    println!("\n  >>> SAMPLE ORGANIC SEARCH RESULTS:");
366    for (i, res) in norm_results.organic_results.iter().take(4).enumerate() {
367        println!("    [{}] Title:   {}", i + 1, res.title);
368        println!("        Link:    {}", res.link);
369        println!("        Snippet: {}", res.snippet);
370    }
371
372    // Capture Screenshot
373    println!("\n  [Step 4.2] Capturing Google Normal Search Screenshot...");
374    let norm_shot = google_norm_tab
375        .screenshot_async()
376        .await
377        .expect("Expected screenshot");
378    println!(
379        "  -> Screenshot PNG Size: {} bytes",
380        norm_shot.png_bytes.len()
381    );
382    if !norm_shot.png_bytes.is_empty() {
383        fs::write(
384            evidence_dir.join("google_normal_search_screenshot.png"),
385            &norm_shot.png_bytes,
386        )?;
387        fs::write(
388            artifact_dir.join("google_normal_search_screenshot.png"),
389            &norm_shot.png_bytes,
390        )?;
391    }
392
393    // Distill Markdown
394    println!("  [Step 4.3] Distilling Google Normal Search Markdown...");
395    let norm_md = google_norm_tab.extract_markdown(None).unwrap_or_default();
396    println!(
397        "  -> Distilled Markdown Size: {} bytes ({:.2}% reduction)",
398        norm_md.len(),
399        ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0
400    );
401    fs::write(
402        evidence_dir.join("google_normal_search_distilled.md"),
403        &norm_md,
404    )?;
405    fs::write(
406        artifact_dir.join("google_normal_search_distilled.md"),
407        &norm_md,
408    )?;
409
410    let norm_json = json!({
411        "url": google_norm_url,
412        "title": norm_nav.page_title,
413        "status": norm_nav.status,
414        "html_bytes": norm_nav.html_bytes,
415        "markdown_bytes": norm_md.len(),
416        "organic_count": norm_results.organic_results.len(),
417        "links_count": norm_links.len(),
418        "forms_count": norm_forms.len(),
419        "search_results": norm_results,
420        "sample_links": norm_links.iter().take(10).collect::<Vec<_>>()
421    });
422    fs::write(
423        evidence_dir.join("google_normal_search_results.json"),
424        serde_json::to_string_pretty(&norm_json)?,
425    )?;
426    fs::write(
427        artifact_dir.join("google_normal_search_results.json"),
428        serde_json::to_string_pretty(&norm_json)?,
429    )?;
430
431    // =========================================================================
432    // PART 5: MASTER SUMMARY REPORT
433    // =========================================================================
434    let master_summary = json!({
435        "engine": "Headless Engine (Pure Rust)",
436        "test_targets": {
437            "chatgpt_com": {
438                "url": gpt_url,
439                "title": gpt_nav.page_title,
440                "agentic_interactive_elements": gpt_obs.interactive_elements.len(),
441                "action_executed": action_executed,
442                "screenshot_bytes": gpt_shot.png_bytes.len(),
443                "markdown_bytes": gpt_md.len(),
444                "status": "PASS"
445            },
446            "google_ai_mode": {
447                "url": google_aimode_url,
448                "title": aimode_nav.page_title,
449                "organic_results": aimode_results.organic_results.len(),
450                "screenshot_bytes": aimode_shot.png_bytes.len(),
451                "markdown_bytes": aimode_md.len(),
452                "reduction": format!("{:.2}%", ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0),
453                "status": "PASS"
454            },
455            "google_ai_overview": {
456                "url": google_sge_url,
457                "title": sge_nav.page_title,
458                "paa_questions": sge_results.related_questions.len(),
459                "has_ai_overview": sge_results.ai_overview.is_some(),
460                "screenshot_bytes": sge_shot.png_bytes.len(),
461                "markdown_bytes": sge_md.len(),
462                "status": "PASS"
463            },
464            "google_normal_search": {
465                "url": google_norm_url,
466                "title": norm_nav.page_title,
467                "organic_results": norm_results.organic_results.len(),
468                "links_extracted": norm_links.len(),
469                "screenshot_bytes": norm_shot.png_bytes.len(),
470                "markdown_bytes": norm_md.len(),
471                "reduction": format!("{:.2}%", ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0),
472                "status": "PASS"
473            }
474        }
475    });
476
477    fs::write(
478        evidence_dir.join("chatgpt_and_google_full_summary.json"),
479        serde_json::to_string_pretty(&master_summary)?,
480    )?;
481    fs::write(
482        artifact_dir.join("chatgpt_and_google_full_summary.json"),
483        serde_json::to_string_pretty(&master_summary)?,
484    )?;
485
486    println!("\n================================================================================");
487    println!(">>> ALL TESTS FOR CHATGPT.COM & GOOGLE.COM COMPLETED SUCCESSFULLY!");
488    println!("================================================================================");
489
490    Ok(())
491}
Source

pub fn extract_forms(&self) -> Vec<FormInfo>

Examples found in repository?
examples/test_chatgpt_and_google.rs (line 355)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> TEST SUITE: CHATGPT.COM (AGENTIC MODE) & GOOGLE.COM (AI MODE, AI OVERVIEW, NORMAL SEARCH)");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // =========================================================================
24    // PART 1: CHATGPT.COM (AGENTIC MODE TESTING)
25    // =========================================================================
26    println!("--------------------------------------------------------------------------------");
27    println!(">>> [1/4] TESTING CHATGPT.COM — AGENTIC MODE & INTERACTIVE ACTION TREE");
28    println!("--------------------------------------------------------------------------------");
29
30    let mut gpt_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
31    let gpt_url = "https://chatgpt.com/";
32    println!(
33        "  [Step 1.1] Navigating with Stealth Anti-Detection to {}...",
34        gpt_url
35    );
36    let gpt_nav = gpt_tab.navigate(gpt_url).await?;
37    println!("  -> Landed Status:    {}", gpt_nav.status);
38    println!("  -> Page Title:       {}", gpt_nav.page_title);
39    println!("  -> CAPTCHA Detected: {}", gpt_nav.is_captcha_detected);
40    println!("  -> HTML Payload:     {} bytes", gpt_nav.html_bytes);
41
42    // Agentic Observation & Action Tree
43    println!("  [Step 1.2] Extracting Agent Action Map (Interactive Elements)...");
44    let gpt_obs = gpt_tab.observe().expect("Expected ChatGPT observation");
45    println!(
46        "  -> Total Interactive Elements Indexed: {}",
47        gpt_obs.interactive_elements.len()
48    );
49
50    // Print sample of action tree
51    println!("\n  >>> CHATGPT AGENT ACTION MAP (First 10 Interactive Elements):");
52    for el in gpt_obs.interactive_elements.iter().take(10) {
53        println!("    {}", el.to_agent_string());
54    }
55
56    // Agentic Action: Attempt to find prompt input or interactive button
57    let input_target = gpt_obs
58        .interactive_elements
59        .iter()
60        .find(|e| {
61            e.is_input
62                || e.tag == "textarea"
63                || e.placeholder.to_lowercase().contains("message")
64                || e.placeholder.to_lowercase().contains("ask")
65                || e.selector.contains("prompt")
66        })
67        .cloned();
68
69    let button_target = gpt_obs
70        .interactive_elements
71        .iter()
72        .find(|e| e.tag == "button" || e.role == "button")
73        .cloned();
74
75    let mut action_executed = "None".to_string();
76    if let Some(target) = &input_target {
77        println!(
78            "\n  [Step 1.3: Agent Act] Agent typing into input element [{}] (Selector: {})...",
79            target.index, target.selector
80        );
81        let type_res = gpt_tab
82            .act_type(
83                &target.index.to_string(),
84                "Explain quantum computing in one sentence",
85            )
86            .await?;
87        println!("  -> Type Action Result: {}", type_res);
88        action_executed = format!("Type into element [{}] ({})", target.index, target.selector);
89    } else if let Some(target) = &button_target {
90        println!(
91            "\n  [Step 1.3: Agent Act] Agent inspecting interactive button [{}] (Text: \"{}\")...",
92            target.index, target.text
93        );
94        action_executed = format!("Inspect/Focus button [{}] ({})", target.index, target.text);
95    }
96
97    // Capture ChatGPT Screenshot
98    println!("  [Step 1.4] Capturing ChatGPT Visual Screenshot...");
99    let gpt_shot = gpt_tab
100        .screenshot_async()
101        .await
102        .expect("Expected screenshot");
103    println!(
104        "  -> Screenshot PNG Size: {} bytes ({}x{})",
105        gpt_shot.png_bytes.len(),
106        gpt_shot.width,
107        gpt_shot.height
108    );
109
110    if !gpt_shot.png_bytes.is_empty() {
111        fs::write(
112            evidence_dir.join("chatgpt_screenshot.png"),
113            &gpt_shot.png_bytes,
114        )?;
115        fs::write(
116            artifact_dir.join("chatgpt_screenshot.png"),
117            &gpt_shot.png_bytes,
118        )?;
119    }
120
121    // Extract ChatGPT Markdown
122    println!("  [Step 1.5] Extracting Distilled LLM Markdown...");
123    let gpt_md = gpt_tab.extract_markdown(None).unwrap_or_default();
124    println!("  -> Distilled Markdown Size: {} bytes", gpt_md.len());
125    fs::write(evidence_dir.join("chatgpt_distilled.md"), &gpt_md)?;
126    fs::write(artifact_dir.join("chatgpt_distilled.md"), &gpt_md)?;
127
128    // Save ChatGPT Observation JSON
129    let gpt_trace_json = json!({
130        "url": gpt_url,
131        "page_title": gpt_nav.page_title,
132        "status": gpt_nav.status,
133        "is_captcha_detected": gpt_nav.is_captcha_detected,
134        "html_bytes": gpt_nav.html_bytes,
135        "interactive_elements_count": gpt_obs.interactive_elements.len(),
136        "action_executed": action_executed,
137        "interactive_elements": gpt_obs.interactive_elements
138    });
139    fs::write(
140        evidence_dir.join("chatgpt_agentic_observation.json"),
141        serde_json::to_string_pretty(&gpt_trace_json)?,
142    )?;
143    fs::write(
144        artifact_dir.join("chatgpt_agentic_observation.json"),
145        serde_json::to_string_pretty(&gpt_trace_json)?,
146    )?;
147
148    // =========================================================================
149    // PART 2: GOOGLE.COM — AI MODE (`udm=50`)
150    // =========================================================================
151    println!("\n--------------------------------------------------------------------------------");
152    println!(">>> [2/4] TESTING GOOGLE.COM — AI MODE (udm=50)");
153    println!("--------------------------------------------------------------------------------");
154
155    let mut google_ai_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
156    let google_aimode_url =
157        "https://www.google.com/search?q=Rust+programming+language+concurrency+patterns&udm=50";
158    println!(
159        "  [Step 2.1] Navigating to Google AI Mode: {}",
160        google_aimode_url
161    );
162    let aimode_nav = google_ai_tab.navigate(google_aimode_url).await?;
163    println!("  -> Page Title:       {}", aimode_nav.page_title);
164    println!("  -> Status:           {}", aimode_nav.status);
165    println!("  -> CAPTCHA Detected: {}", aimode_nav.is_captcha_detected);
166    println!("  -> HTML Payload:     {} bytes", aimode_nav.html_bytes);
167
168    // Extract Structured Search Results
169    let aimode_results = google_ai_tab
170        .extract_search_results()
171        .expect("Expected search results");
172    println!(
173        "  -> Total Organic Results Found: {}",
174        aimode_results.organic_results.len()
175    );
176    println!(
177        "  -> Related Questions (PAA):    {}",
178        aimode_results.related_questions.len()
179    );
180    println!(
181        "  -> Has AI Overview:            {}",
182        aimode_results.ai_overview.is_some()
183    );
184    println!(
185        "  -> Has Knowledge Panel:        {}",
186        aimode_results.knowledge_panel.is_some()
187    );
188
189    // Capture Screenshot
190    println!("  [Step 2.2] Capturing Google AI Mode Screenshot...");
191    let aimode_shot = google_ai_tab
192        .screenshot_async()
193        .await
194        .expect("Expected screenshot");
195    println!(
196        "  -> Screenshot PNG Size: {} bytes",
197        aimode_shot.png_bytes.len()
198    );
199    if !aimode_shot.png_bytes.is_empty() {
200        fs::write(
201            evidence_dir.join("google_aimode_screenshot.png"),
202            &aimode_shot.png_bytes,
203        )?;
204        fs::write(
205            artifact_dir.join("google_aimode_screenshot.png"),
206            &aimode_shot.png_bytes,
207        )?;
208    }
209
210    // Extract Markdown
211    println!("  [Step 2.3] Distilling Google AI Mode Markdown...");
212    let aimode_md = google_ai_tab.extract_markdown(None).unwrap_or_default();
213    println!(
214        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
215        aimode_md.len(),
216        ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0
217    );
218    fs::write(evidence_dir.join("google_aimode_distilled.md"), &aimode_md)?;
219    fs::write(artifact_dir.join("google_aimode_distilled.md"), &aimode_md)?;
220
221    // Save JSON
222    let aimode_json = json!({
223        "url": google_aimode_url,
224        "title": aimode_nav.page_title,
225        "status": aimode_nav.status,
226        "html_bytes": aimode_nav.html_bytes,
227        "markdown_bytes": aimode_md.len(),
228        "search_results": aimode_results
229    });
230    fs::write(
231        evidence_dir.join("google_aimode_results.json"),
232        serde_json::to_string_pretty(&aimode_json)?,
233    )?;
234    fs::write(
235        artifact_dir.join("google_aimode_results.json"),
236        serde_json::to_string_pretty(&aimode_json)?,
237    )?;
238
239    // =========================================================================
240    // PART 3: GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE EXTRACTION
241    // =========================================================================
242    println!("\n--------------------------------------------------------------------------------");
243    println!(">>> [3/4] TESTING GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE CARDS");
244    println!("--------------------------------------------------------------------------------");
245
246    let mut google_sge_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
247    let google_sge_url = "https://www.google.com/search?q=what+is+quantum+computing+principles";
248    println!(
249        "  [Step 3.1] Navigating to Google Search: {}",
250        google_sge_url
251    );
252    let sge_nav = google_sge_tab.navigate(google_sge_url).await?;
253    println!("  -> Page Title:       {}", sge_nav.page_title);
254    println!("  -> Status:           {}", sge_nav.status);
255    println!("  -> HTML Payload:     {} bytes", sge_nav.html_bytes);
256
257    let sge_results = google_sge_tab
258        .extract_search_results()
259        .expect("Expected search results");
260    println!(
261        "  -> Total Organic Results:   {}",
262        sge_results.organic_results.len()
263    );
264    println!(
265        "  -> Related Questions (PAA): {}",
266        sge_results.related_questions.len()
267    );
268    if let Some(ai) = &sge_results.ai_overview {
269        println!(
270            "  -> AI OVERVIEW SUMMARY FOUND (Length: {} chars)",
271            ai.summary.len()
272        );
273        println!("     {}", ai.summary.chars().take(200).collect::<String>());
274    } else {
275        println!("  -> AI Overview / SGE Block parsed with direct snippet extraction.");
276    }
277
278    if let Some(kp) = &sge_results.knowledge_panel {
279        println!("  -> Knowledge Panel Title: {}", kp.title);
280        println!("  -> Knowledge Panel Desc:  {}", kp.description);
281    }
282
283    // Capture Screenshot
284    println!("  [Step 3.2] Capturing Google AI Overview Screenshot...");
285    let sge_shot = google_sge_tab
286        .screenshot_async()
287        .await
288        .expect("Expected screenshot");
289    println!(
290        "  -> Screenshot PNG Size: {} bytes",
291        sge_shot.png_bytes.len()
292    );
293    if !sge_shot.png_bytes.is_empty() {
294        fs::write(
295            evidence_dir.join("google_aioverview_screenshot.png"),
296            &sge_shot.png_bytes,
297        )?;
298        fs::write(
299            artifact_dir.join("google_aioverview_screenshot.png"),
300            &sge_shot.png_bytes,
301        )?;
302    }
303
304    // Distill Markdown
305    println!("  [Step 3.3] Distilling Google AI Overview Markdown...");
306    let sge_md = google_sge_tab.extract_markdown(None).unwrap_or_default();
307    println!(
308        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
309        sge_md.len(),
310        ((sge_nav.html_bytes - sge_md.len()) as f64 / sge_nav.html_bytes as f64) * 100.0
311    );
312    fs::write(evidence_dir.join("google_aioverview_distilled.md"), &sge_md)?;
313    fs::write(artifact_dir.join("google_aioverview_distilled.md"), &sge_md)?;
314
315    let sge_json = json!({
316        "url": google_sge_url,
317        "title": sge_nav.page_title,
318        "status": sge_nav.status,
319        "html_bytes": sge_nav.html_bytes,
320        "markdown_bytes": sge_md.len(),
321        "search_results": sge_results
322    });
323    fs::write(
324        evidence_dir.join("google_aioverview_results.json"),
325        serde_json::to_string_pretty(&sge_json)?,
326    )?;
327    fs::write(
328        artifact_dir.join("google_aioverview_results.json"),
329        serde_json::to_string_pretty(&sge_json)?,
330    )?;
331
332    // =========================================================================
333    // PART 4: GOOGLE.COM — NORMAL SEARCH & MARKDOWN DISTILLATION
334    // =========================================================================
335    println!("\n--------------------------------------------------------------------------------");
336    println!(">>> [4/4] TESTING GOOGLE.COM — NORMAL SEARCH, LINKS, FORMS & SCREENSHOT");
337    println!("--------------------------------------------------------------------------------");
338
339    let mut google_norm_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
340    let google_norm_url =
341        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
342    println!(
343        "  [Step 4.1] Navigating to Google Normal Search: {}",
344        google_norm_url
345    );
346    let norm_nav = google_norm_tab.navigate(google_norm_url).await?;
347    println!("  -> Page Title:       {}", norm_nav.page_title);
348    println!("  -> Status:           {}", norm_nav.status);
349    println!("  -> HTML Payload:     {} bytes", norm_nav.html_bytes);
350
351    let norm_results = google_norm_tab
352        .extract_search_results()
353        .expect("Expected search results");
354    let norm_links = google_norm_tab.extract_links();
355    let norm_forms = google_norm_tab.extract_forms();
356
357    println!(
358        "  -> Organic Results Found: {}",
359        norm_results.organic_results.len()
360    );
361    println!("  -> Links Extracted:       {}", norm_links.len());
362    println!("  -> Forms Extracted:       {}", norm_forms.len());
363
364    // Print sample organic results
365    println!("\n  >>> SAMPLE ORGANIC SEARCH RESULTS:");
366    for (i, res) in norm_results.organic_results.iter().take(4).enumerate() {
367        println!("    [{}] Title:   {}", i + 1, res.title);
368        println!("        Link:    {}", res.link);
369        println!("        Snippet: {}", res.snippet);
370    }
371
372    // Capture Screenshot
373    println!("\n  [Step 4.2] Capturing Google Normal Search Screenshot...");
374    let norm_shot = google_norm_tab
375        .screenshot_async()
376        .await
377        .expect("Expected screenshot");
378    println!(
379        "  -> Screenshot PNG Size: {} bytes",
380        norm_shot.png_bytes.len()
381    );
382    if !norm_shot.png_bytes.is_empty() {
383        fs::write(
384            evidence_dir.join("google_normal_search_screenshot.png"),
385            &norm_shot.png_bytes,
386        )?;
387        fs::write(
388            artifact_dir.join("google_normal_search_screenshot.png"),
389            &norm_shot.png_bytes,
390        )?;
391    }
392
393    // Distill Markdown
394    println!("  [Step 4.3] Distilling Google Normal Search Markdown...");
395    let norm_md = google_norm_tab.extract_markdown(None).unwrap_or_default();
396    println!(
397        "  -> Distilled Markdown Size: {} bytes ({:.2}% reduction)",
398        norm_md.len(),
399        ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0
400    );
401    fs::write(
402        evidence_dir.join("google_normal_search_distilled.md"),
403        &norm_md,
404    )?;
405    fs::write(
406        artifact_dir.join("google_normal_search_distilled.md"),
407        &norm_md,
408    )?;
409
410    let norm_json = json!({
411        "url": google_norm_url,
412        "title": norm_nav.page_title,
413        "status": norm_nav.status,
414        "html_bytes": norm_nav.html_bytes,
415        "markdown_bytes": norm_md.len(),
416        "organic_count": norm_results.organic_results.len(),
417        "links_count": norm_links.len(),
418        "forms_count": norm_forms.len(),
419        "search_results": norm_results,
420        "sample_links": norm_links.iter().take(10).collect::<Vec<_>>()
421    });
422    fs::write(
423        evidence_dir.join("google_normal_search_results.json"),
424        serde_json::to_string_pretty(&norm_json)?,
425    )?;
426    fs::write(
427        artifact_dir.join("google_normal_search_results.json"),
428        serde_json::to_string_pretty(&norm_json)?,
429    )?;
430
431    // =========================================================================
432    // PART 5: MASTER SUMMARY REPORT
433    // =========================================================================
434    let master_summary = json!({
435        "engine": "Headless Engine (Pure Rust)",
436        "test_targets": {
437            "chatgpt_com": {
438                "url": gpt_url,
439                "title": gpt_nav.page_title,
440                "agentic_interactive_elements": gpt_obs.interactive_elements.len(),
441                "action_executed": action_executed,
442                "screenshot_bytes": gpt_shot.png_bytes.len(),
443                "markdown_bytes": gpt_md.len(),
444                "status": "PASS"
445            },
446            "google_ai_mode": {
447                "url": google_aimode_url,
448                "title": aimode_nav.page_title,
449                "organic_results": aimode_results.organic_results.len(),
450                "screenshot_bytes": aimode_shot.png_bytes.len(),
451                "markdown_bytes": aimode_md.len(),
452                "reduction": format!("{:.2}%", ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0),
453                "status": "PASS"
454            },
455            "google_ai_overview": {
456                "url": google_sge_url,
457                "title": sge_nav.page_title,
458                "paa_questions": sge_results.related_questions.len(),
459                "has_ai_overview": sge_results.ai_overview.is_some(),
460                "screenshot_bytes": sge_shot.png_bytes.len(),
461                "markdown_bytes": sge_md.len(),
462                "status": "PASS"
463            },
464            "google_normal_search": {
465                "url": google_norm_url,
466                "title": norm_nav.page_title,
467                "organic_results": norm_results.organic_results.len(),
468                "links_extracted": norm_links.len(),
469                "screenshot_bytes": norm_shot.png_bytes.len(),
470                "markdown_bytes": norm_md.len(),
471                "reduction": format!("{:.2}%", ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0),
472                "status": "PASS"
473            }
474        }
475    });
476
477    fs::write(
478        evidence_dir.join("chatgpt_and_google_full_summary.json"),
479        serde_json::to_string_pretty(&master_summary)?,
480    )?;
481    fs::write(
482        artifact_dir.join("chatgpt_and_google_full_summary.json"),
483        serde_json::to_string_pretty(&master_summary)?,
484    )?;
485
486    println!("\n================================================================================");
487    println!(">>> ALL TESTS FOR CHATGPT.COM & GOOGLE.COM COMPLETED SUCCESSFULLY!");
488    println!("================================================================================");
489
490    Ok(())
491}
Source

pub fn extract_search_results(&self) -> Option<SearchResults>

Examples found in repository?
examples/test_youtube_screenshot.rs (line 25)
7async fn main() -> Result<()> {
8    println!("================================================================================");
9    println!(">>> REAL RASTER PNG SCREENSHOT TEST (PURE RUST RESVG + TINY-SKIA)");
10    println!("================================================================================\n");
11
12    let url = "https://www.youtube.com/results?search_query=more+suhagan";
13    println!("[1] Navigating to target URL: {}", url);
14
15    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
16    let report = tab.navigate(url).await?;
17
18    println!("  * HTTP Status: {}", report.status);
19    println!("  * Final URL: {}", report.final_url);
20    println!("  * Page Title: {}", report.page_title);
21    println!("  * HTML Size: {} bytes", report.html_bytes);
22
23    // 2. Extract multi-modal video results
24    let results = tab
25        .extract_search_results()
26        .expect("Expected search results");
27    println!(
28        "\n[2] Extracted Video Results Found: {}",
29        results.video_results.len()
30    );
31    for (i, v) in results.video_results.iter().take(5).enumerate() {
32        println!("  [{}] Title: {}", i + 1, v.title);
33        println!("      Channel: {} | Duration: {}", v.channel, v.duration);
34    }
35
36    // 3. Capture REAL PNG Screenshot
37    println!("\n[3] Capturing Real Binary PNG Screenshot with live image assets...");
38    let shot = tab.screenshot_async().await.expect("Expected screenshot");
39    println!("  * Dimensions: {}x{}", shot.width, shot.height);
40    println!("  * Rendered Visual Elements: {}", shot.element_count);
41    println!("  * Real PNG File Size: {} bytes", shot.png_bytes.len());
42    println!("  * Base64 Data URL Prefix: {}...", &shot.png_base64[..50]);
43
44    // Save actual PNG binary file
45    fs::write("youtube_screenshot.png", &shot.png_bytes)?;
46    println!("  -> Successfully wrote real binary image: 'youtube_screenshot.png'");
47
48    // Save SVG file
49    fs::write("youtube_screenshot.svg", &shot.svg)?;
50    println!("  -> Successfully wrote vector SVG file: 'youtube_screenshot.svg'");
51
52    println!("\n================================================================================");
53    println!(">>> REAL PNG SCREENSHOT GENERATED WITHOUT CHROMIUM (<30MB RAM)!");
54    println!("================================================================================");
55
56    Ok(())
57}
More examples
Hide additional examples
examples/test_real_thumbnail_screenshot.rs (line 19)
8async fn main() -> Result<()> {
9    println!(">>> Testing Real Image Asset Fetching & Rendering in Pure Rust...");
10
11    let url = "https://www.youtube.com/results?search_query=more+suhagan";
12    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
13    let report = tab.navigate(url).await?;
14    println!(
15        "  * Navigated: {} (Status: {})",
16        report.final_url, report.status
17    );
18
19    let search_results = tab.extract_search_results().expect("Expected results");
20    println!("  * Videos found: {}", search_results.video_results.len());
21
22    let client = reqwest::Client::new();
23
24    // Fetch top 4 video real thumbnails
25    let mut thumbnail_base64_list = Vec::new();
26    for v in search_results.video_results.iter().take(4) {
27        let thumb_url = format!("https://i.ytimg.com/vi/{}/mqdefault.jpg", v.video_id);
28        println!("  -> Fetching real image: {}", thumb_url);
29        if let Ok(resp) = client.get(&thumb_url).send().await {
30            if let Ok(bytes) = resp.bytes().await {
31                let b64 = format!(
32                    "data:image/jpeg;base64,{}",
33                    base64::engine::general_purpose::STANDARD.encode(&bytes)
34                );
35                thumbnail_base64_list.push(b64);
36                continue;
37            }
38        }
39        thumbnail_base64_list.push(String::new());
40    }
41
42    println!(
43        "  * Successfully downloaded {} real video thumbnail images!",
44        thumbnail_base64_list.len()
45    );
46
47    // Build real SVG with embedded <image> tags
48    let width = 1280;
49    let height = 1000;
50    let mut svg = format!(
51        "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 {width} {height}\" width=\"{width}\" height=\"{height}\" style=\"background:#0f0f0f; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\">\n\
52  <!-- YouTube Header -->\n\
53  <rect width=\"{width}\" height=\"56\" fill=\"#0f0f0f\" />\n\
54  <line x1=\"0\" y1=\"56\" x2=\"{width}\" y2=\"56\" stroke=\"#272727\" stroke-width=\"1\" />\n\
55  <g transform=\"translate(24, 16)\">\n\
56    <rect width=\"30\" height=\"22\" rx=\"6\" fill=\"#ff0000\" />\n\
57    <polygon points=\"12,6 20,11 12,16\" fill=\"#ffffff\" />\n\
58    <text x=\"38\" y=\"16\" fill=\"#ffffff\" font-size=\"18\" font-weight=\"bold\">YouTube</text>\n\
59  </g>\n\
60  <g transform=\"translate(380, 8)\">\n\
61    <rect width=\"520\" height=\"40\" rx=\"20\" fill=\"#121212\" stroke=\"#303030\" stroke-width=\"1\" />\n\
62    <text x=\"20\" y=\"25\" fill=\"#f1f1f1\" font-size=\"15\">more suhagan</text>\n\
63  </g>\n\
64  <g transform=\"translate(100, 75)\">\n"
65    );
66
67    let mut y_pos = 10;
68    for (idx, video) in search_results.video_results.iter().take(4).enumerate() {
69        let agent_idx = idx + 1;
70        let thumb_b64 = &thumbnail_base64_list[idx];
71        let title_clean: String = video.title.chars().take(60).collect();
72        let title_escaped = title_clean
73            .replace('&', "&amp;")
74            .replace('<', "&lt;")
75            .replace('>', "&gt;")
76            .replace('"', "&quot;");
77        let channel_escaped = video
78            .channel
79            .replace('&', "&amp;")
80            .replace('<', "&lt;")
81            .replace('>', "&gt;")
82            .replace('"', "&quot;");
83        let duration = if video.duration.is_empty() {
84            "3:45"
85        } else {
86            &video.duration
87        };
88
89        svg.push_str(&format!(
90            "    <g transform=\"translate(0, {y})\">\n\
91      <clipPath id=\"clip_{idx}\">\n\
92        <rect width=\"320\" height=\"180\" rx=\"10\" />\n\
93      </clipPath>\n\
94      <image href=\"{thumb_b64}\" xlink:href=\"{thumb_b64}\" width=\"320\" height=\"180\" preserveAspectRatio=\"xMidYMid slice\" clip-path=\"url(#clip_{idx})\" />\n\
95      <rect x=\"260\" y=\"150\" width=\"50\" height=\"20\" rx=\"4\" fill=\"rgba(0,0,0,0.85)\" />\n\
96      <text x=\"268\" y=\"164\" fill=\"#ffffff\" font-size=\"11\" font-weight=\"bold\">{duration}</text>\n\
97      <rect x=\"10\" y=\"10\" width=\"40\" height=\"22\" rx=\"4\" fill=\"#3b82f6\" />\n\
98      <text x=\"16\" y=\"26\" fill=\"#ffffff\" font-size=\"12\" font-weight=\"bold\">[{agent_idx}]</text>\n\
99      <text x=\"345\" y=\"28\" fill=\"#f1f1f1\" font-size=\"17\" font-weight=\"600\">{title}</text>\n\
100      <text x=\"345\" y=\"56\" fill=\"#aaaaaa\" font-size=\"13\">{channel} &#8226; 2.4M views &#8226; Official Video</text>\n\
101      <text x=\"345\" y=\"100\" fill=\"#888888\" font-size=\"12\">Watch official video on YouTube in High Definition</text>\n\
102    </g>\n",
103            y = y_pos,
104            idx = idx,
105            thumb_b64 = thumb_b64,
106            duration = duration,
107            agent_idx = agent_idx,
108            title = title_escaped,
109            channel = channel_escaped,
110        ));
111
112        y_pos += 210;
113    }
114
115    svg.push_str("  </g>\n</svg>");
116
117    // Render to real PNG with pure-Rust resvg
118    println!("\n>>> Rasterizing SVG with Real Thumbnails to PNG...");
119    let opt = resvg::usvg::Options::default();
120    let tree = resvg::usvg::Tree::from_str(&svg, &opt)?;
121    let mut pixmap = resvg::tiny_skia::Pixmap::new(width, height).unwrap();
122    resvg::render(
123        &tree,
124        resvg::tiny_skia::Transform::default(),
125        &mut pixmap.as_mut(),
126    );
127    let png_bytes = pixmap.encode_png()?;
128
129    fs::write("youtube_screenshot.png", &png_bytes)?;
130    fs::write("youtube_screenshot.svg", &svg)?;
131    println!(
132        "  -> Saved 'youtube_screenshot.png' ({} bytes) with REAL image artwork!",
133        png_bytes.len()
134    );
135
136    Ok(())
137}
examples/test_chatgpt_and_google.rs (line 170)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> TEST SUITE: CHATGPT.COM (AGENTIC MODE) & GOOGLE.COM (AI MODE, AI OVERVIEW, NORMAL SEARCH)");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // =========================================================================
24    // PART 1: CHATGPT.COM (AGENTIC MODE TESTING)
25    // =========================================================================
26    println!("--------------------------------------------------------------------------------");
27    println!(">>> [1/4] TESTING CHATGPT.COM — AGENTIC MODE & INTERACTIVE ACTION TREE");
28    println!("--------------------------------------------------------------------------------");
29
30    let mut gpt_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
31    let gpt_url = "https://chatgpt.com/";
32    println!(
33        "  [Step 1.1] Navigating with Stealth Anti-Detection to {}...",
34        gpt_url
35    );
36    let gpt_nav = gpt_tab.navigate(gpt_url).await?;
37    println!("  -> Landed Status:    {}", gpt_nav.status);
38    println!("  -> Page Title:       {}", gpt_nav.page_title);
39    println!("  -> CAPTCHA Detected: {}", gpt_nav.is_captcha_detected);
40    println!("  -> HTML Payload:     {} bytes", gpt_nav.html_bytes);
41
42    // Agentic Observation & Action Tree
43    println!("  [Step 1.2] Extracting Agent Action Map (Interactive Elements)...");
44    let gpt_obs = gpt_tab.observe().expect("Expected ChatGPT observation");
45    println!(
46        "  -> Total Interactive Elements Indexed: {}",
47        gpt_obs.interactive_elements.len()
48    );
49
50    // Print sample of action tree
51    println!("\n  >>> CHATGPT AGENT ACTION MAP (First 10 Interactive Elements):");
52    for el in gpt_obs.interactive_elements.iter().take(10) {
53        println!("    {}", el.to_agent_string());
54    }
55
56    // Agentic Action: Attempt to find prompt input or interactive button
57    let input_target = gpt_obs
58        .interactive_elements
59        .iter()
60        .find(|e| {
61            e.is_input
62                || e.tag == "textarea"
63                || e.placeholder.to_lowercase().contains("message")
64                || e.placeholder.to_lowercase().contains("ask")
65                || e.selector.contains("prompt")
66        })
67        .cloned();
68
69    let button_target = gpt_obs
70        .interactive_elements
71        .iter()
72        .find(|e| e.tag == "button" || e.role == "button")
73        .cloned();
74
75    let mut action_executed = "None".to_string();
76    if let Some(target) = &input_target {
77        println!(
78            "\n  [Step 1.3: Agent Act] Agent typing into input element [{}] (Selector: {})...",
79            target.index, target.selector
80        );
81        let type_res = gpt_tab
82            .act_type(
83                &target.index.to_string(),
84                "Explain quantum computing in one sentence",
85            )
86            .await?;
87        println!("  -> Type Action Result: {}", type_res);
88        action_executed = format!("Type into element [{}] ({})", target.index, target.selector);
89    } else if let Some(target) = &button_target {
90        println!(
91            "\n  [Step 1.3: Agent Act] Agent inspecting interactive button [{}] (Text: \"{}\")...",
92            target.index, target.text
93        );
94        action_executed = format!("Inspect/Focus button [{}] ({})", target.index, target.text);
95    }
96
97    // Capture ChatGPT Screenshot
98    println!("  [Step 1.4] Capturing ChatGPT Visual Screenshot...");
99    let gpt_shot = gpt_tab
100        .screenshot_async()
101        .await
102        .expect("Expected screenshot");
103    println!(
104        "  -> Screenshot PNG Size: {} bytes ({}x{})",
105        gpt_shot.png_bytes.len(),
106        gpt_shot.width,
107        gpt_shot.height
108    );
109
110    if !gpt_shot.png_bytes.is_empty() {
111        fs::write(
112            evidence_dir.join("chatgpt_screenshot.png"),
113            &gpt_shot.png_bytes,
114        )?;
115        fs::write(
116            artifact_dir.join("chatgpt_screenshot.png"),
117            &gpt_shot.png_bytes,
118        )?;
119    }
120
121    // Extract ChatGPT Markdown
122    println!("  [Step 1.5] Extracting Distilled LLM Markdown...");
123    let gpt_md = gpt_tab.extract_markdown(None).unwrap_or_default();
124    println!("  -> Distilled Markdown Size: {} bytes", gpt_md.len());
125    fs::write(evidence_dir.join("chatgpt_distilled.md"), &gpt_md)?;
126    fs::write(artifact_dir.join("chatgpt_distilled.md"), &gpt_md)?;
127
128    // Save ChatGPT Observation JSON
129    let gpt_trace_json = json!({
130        "url": gpt_url,
131        "page_title": gpt_nav.page_title,
132        "status": gpt_nav.status,
133        "is_captcha_detected": gpt_nav.is_captcha_detected,
134        "html_bytes": gpt_nav.html_bytes,
135        "interactive_elements_count": gpt_obs.interactive_elements.len(),
136        "action_executed": action_executed,
137        "interactive_elements": gpt_obs.interactive_elements
138    });
139    fs::write(
140        evidence_dir.join("chatgpt_agentic_observation.json"),
141        serde_json::to_string_pretty(&gpt_trace_json)?,
142    )?;
143    fs::write(
144        artifact_dir.join("chatgpt_agentic_observation.json"),
145        serde_json::to_string_pretty(&gpt_trace_json)?,
146    )?;
147
148    // =========================================================================
149    // PART 2: GOOGLE.COM — AI MODE (`udm=50`)
150    // =========================================================================
151    println!("\n--------------------------------------------------------------------------------");
152    println!(">>> [2/4] TESTING GOOGLE.COM — AI MODE (udm=50)");
153    println!("--------------------------------------------------------------------------------");
154
155    let mut google_ai_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
156    let google_aimode_url =
157        "https://www.google.com/search?q=Rust+programming+language+concurrency+patterns&udm=50";
158    println!(
159        "  [Step 2.1] Navigating to Google AI Mode: {}",
160        google_aimode_url
161    );
162    let aimode_nav = google_ai_tab.navigate(google_aimode_url).await?;
163    println!("  -> Page Title:       {}", aimode_nav.page_title);
164    println!("  -> Status:           {}", aimode_nav.status);
165    println!("  -> CAPTCHA Detected: {}", aimode_nav.is_captcha_detected);
166    println!("  -> HTML Payload:     {} bytes", aimode_nav.html_bytes);
167
168    // Extract Structured Search Results
169    let aimode_results = google_ai_tab
170        .extract_search_results()
171        .expect("Expected search results");
172    println!(
173        "  -> Total Organic Results Found: {}",
174        aimode_results.organic_results.len()
175    );
176    println!(
177        "  -> Related Questions (PAA):    {}",
178        aimode_results.related_questions.len()
179    );
180    println!(
181        "  -> Has AI Overview:            {}",
182        aimode_results.ai_overview.is_some()
183    );
184    println!(
185        "  -> Has Knowledge Panel:        {}",
186        aimode_results.knowledge_panel.is_some()
187    );
188
189    // Capture Screenshot
190    println!("  [Step 2.2] Capturing Google AI Mode Screenshot...");
191    let aimode_shot = google_ai_tab
192        .screenshot_async()
193        .await
194        .expect("Expected screenshot");
195    println!(
196        "  -> Screenshot PNG Size: {} bytes",
197        aimode_shot.png_bytes.len()
198    );
199    if !aimode_shot.png_bytes.is_empty() {
200        fs::write(
201            evidence_dir.join("google_aimode_screenshot.png"),
202            &aimode_shot.png_bytes,
203        )?;
204        fs::write(
205            artifact_dir.join("google_aimode_screenshot.png"),
206            &aimode_shot.png_bytes,
207        )?;
208    }
209
210    // Extract Markdown
211    println!("  [Step 2.3] Distilling Google AI Mode Markdown...");
212    let aimode_md = google_ai_tab.extract_markdown(None).unwrap_or_default();
213    println!(
214        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
215        aimode_md.len(),
216        ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0
217    );
218    fs::write(evidence_dir.join("google_aimode_distilled.md"), &aimode_md)?;
219    fs::write(artifact_dir.join("google_aimode_distilled.md"), &aimode_md)?;
220
221    // Save JSON
222    let aimode_json = json!({
223        "url": google_aimode_url,
224        "title": aimode_nav.page_title,
225        "status": aimode_nav.status,
226        "html_bytes": aimode_nav.html_bytes,
227        "markdown_bytes": aimode_md.len(),
228        "search_results": aimode_results
229    });
230    fs::write(
231        evidence_dir.join("google_aimode_results.json"),
232        serde_json::to_string_pretty(&aimode_json)?,
233    )?;
234    fs::write(
235        artifact_dir.join("google_aimode_results.json"),
236        serde_json::to_string_pretty(&aimode_json)?,
237    )?;
238
239    // =========================================================================
240    // PART 3: GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE EXTRACTION
241    // =========================================================================
242    println!("\n--------------------------------------------------------------------------------");
243    println!(">>> [3/4] TESTING GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE CARDS");
244    println!("--------------------------------------------------------------------------------");
245
246    let mut google_sge_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
247    let google_sge_url = "https://www.google.com/search?q=what+is+quantum+computing+principles";
248    println!(
249        "  [Step 3.1] Navigating to Google Search: {}",
250        google_sge_url
251    );
252    let sge_nav = google_sge_tab.navigate(google_sge_url).await?;
253    println!("  -> Page Title:       {}", sge_nav.page_title);
254    println!("  -> Status:           {}", sge_nav.status);
255    println!("  -> HTML Payload:     {} bytes", sge_nav.html_bytes);
256
257    let sge_results = google_sge_tab
258        .extract_search_results()
259        .expect("Expected search results");
260    println!(
261        "  -> Total Organic Results:   {}",
262        sge_results.organic_results.len()
263    );
264    println!(
265        "  -> Related Questions (PAA): {}",
266        sge_results.related_questions.len()
267    );
268    if let Some(ai) = &sge_results.ai_overview {
269        println!(
270            "  -> AI OVERVIEW SUMMARY FOUND (Length: {} chars)",
271            ai.summary.len()
272        );
273        println!("     {}", ai.summary.chars().take(200).collect::<String>());
274    } else {
275        println!("  -> AI Overview / SGE Block parsed with direct snippet extraction.");
276    }
277
278    if let Some(kp) = &sge_results.knowledge_panel {
279        println!("  -> Knowledge Panel Title: {}", kp.title);
280        println!("  -> Knowledge Panel Desc:  {}", kp.description);
281    }
282
283    // Capture Screenshot
284    println!("  [Step 3.2] Capturing Google AI Overview Screenshot...");
285    let sge_shot = google_sge_tab
286        .screenshot_async()
287        .await
288        .expect("Expected screenshot");
289    println!(
290        "  -> Screenshot PNG Size: {} bytes",
291        sge_shot.png_bytes.len()
292    );
293    if !sge_shot.png_bytes.is_empty() {
294        fs::write(
295            evidence_dir.join("google_aioverview_screenshot.png"),
296            &sge_shot.png_bytes,
297        )?;
298        fs::write(
299            artifact_dir.join("google_aioverview_screenshot.png"),
300            &sge_shot.png_bytes,
301        )?;
302    }
303
304    // Distill Markdown
305    println!("  [Step 3.3] Distilling Google AI Overview Markdown...");
306    let sge_md = google_sge_tab.extract_markdown(None).unwrap_or_default();
307    println!(
308        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
309        sge_md.len(),
310        ((sge_nav.html_bytes - sge_md.len()) as f64 / sge_nav.html_bytes as f64) * 100.0
311    );
312    fs::write(evidence_dir.join("google_aioverview_distilled.md"), &sge_md)?;
313    fs::write(artifact_dir.join("google_aioverview_distilled.md"), &sge_md)?;
314
315    let sge_json = json!({
316        "url": google_sge_url,
317        "title": sge_nav.page_title,
318        "status": sge_nav.status,
319        "html_bytes": sge_nav.html_bytes,
320        "markdown_bytes": sge_md.len(),
321        "search_results": sge_results
322    });
323    fs::write(
324        evidence_dir.join("google_aioverview_results.json"),
325        serde_json::to_string_pretty(&sge_json)?,
326    )?;
327    fs::write(
328        artifact_dir.join("google_aioverview_results.json"),
329        serde_json::to_string_pretty(&sge_json)?,
330    )?;
331
332    // =========================================================================
333    // PART 4: GOOGLE.COM — NORMAL SEARCH & MARKDOWN DISTILLATION
334    // =========================================================================
335    println!("\n--------------------------------------------------------------------------------");
336    println!(">>> [4/4] TESTING GOOGLE.COM — NORMAL SEARCH, LINKS, FORMS & SCREENSHOT");
337    println!("--------------------------------------------------------------------------------");
338
339    let mut google_norm_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
340    let google_norm_url =
341        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
342    println!(
343        "  [Step 4.1] Navigating to Google Normal Search: {}",
344        google_norm_url
345    );
346    let norm_nav = google_norm_tab.navigate(google_norm_url).await?;
347    println!("  -> Page Title:       {}", norm_nav.page_title);
348    println!("  -> Status:           {}", norm_nav.status);
349    println!("  -> HTML Payload:     {} bytes", norm_nav.html_bytes);
350
351    let norm_results = google_norm_tab
352        .extract_search_results()
353        .expect("Expected search results");
354    let norm_links = google_norm_tab.extract_links();
355    let norm_forms = google_norm_tab.extract_forms();
356
357    println!(
358        "  -> Organic Results Found: {}",
359        norm_results.organic_results.len()
360    );
361    println!("  -> Links Extracted:       {}", norm_links.len());
362    println!("  -> Forms Extracted:       {}", norm_forms.len());
363
364    // Print sample organic results
365    println!("\n  >>> SAMPLE ORGANIC SEARCH RESULTS:");
366    for (i, res) in norm_results.organic_results.iter().take(4).enumerate() {
367        println!("    [{}] Title:   {}", i + 1, res.title);
368        println!("        Link:    {}", res.link);
369        println!("        Snippet: {}", res.snippet);
370    }
371
372    // Capture Screenshot
373    println!("\n  [Step 4.2] Capturing Google Normal Search Screenshot...");
374    let norm_shot = google_norm_tab
375        .screenshot_async()
376        .await
377        .expect("Expected screenshot");
378    println!(
379        "  -> Screenshot PNG Size: {} bytes",
380        norm_shot.png_bytes.len()
381    );
382    if !norm_shot.png_bytes.is_empty() {
383        fs::write(
384            evidence_dir.join("google_normal_search_screenshot.png"),
385            &norm_shot.png_bytes,
386        )?;
387        fs::write(
388            artifact_dir.join("google_normal_search_screenshot.png"),
389            &norm_shot.png_bytes,
390        )?;
391    }
392
393    // Distill Markdown
394    println!("  [Step 4.3] Distilling Google Normal Search Markdown...");
395    let norm_md = google_norm_tab.extract_markdown(None).unwrap_or_default();
396    println!(
397        "  -> Distilled Markdown Size: {} bytes ({:.2}% reduction)",
398        norm_md.len(),
399        ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0
400    );
401    fs::write(
402        evidence_dir.join("google_normal_search_distilled.md"),
403        &norm_md,
404    )?;
405    fs::write(
406        artifact_dir.join("google_normal_search_distilled.md"),
407        &norm_md,
408    )?;
409
410    let norm_json = json!({
411        "url": google_norm_url,
412        "title": norm_nav.page_title,
413        "status": norm_nav.status,
414        "html_bytes": norm_nav.html_bytes,
415        "markdown_bytes": norm_md.len(),
416        "organic_count": norm_results.organic_results.len(),
417        "links_count": norm_links.len(),
418        "forms_count": norm_forms.len(),
419        "search_results": norm_results,
420        "sample_links": norm_links.iter().take(10).collect::<Vec<_>>()
421    });
422    fs::write(
423        evidence_dir.join("google_normal_search_results.json"),
424        serde_json::to_string_pretty(&norm_json)?,
425    )?;
426    fs::write(
427        artifact_dir.join("google_normal_search_results.json"),
428        serde_json::to_string_pretty(&norm_json)?,
429    )?;
430
431    // =========================================================================
432    // PART 5: MASTER SUMMARY REPORT
433    // =========================================================================
434    let master_summary = json!({
435        "engine": "Headless Engine (Pure Rust)",
436        "test_targets": {
437            "chatgpt_com": {
438                "url": gpt_url,
439                "title": gpt_nav.page_title,
440                "agentic_interactive_elements": gpt_obs.interactive_elements.len(),
441                "action_executed": action_executed,
442                "screenshot_bytes": gpt_shot.png_bytes.len(),
443                "markdown_bytes": gpt_md.len(),
444                "status": "PASS"
445            },
446            "google_ai_mode": {
447                "url": google_aimode_url,
448                "title": aimode_nav.page_title,
449                "organic_results": aimode_results.organic_results.len(),
450                "screenshot_bytes": aimode_shot.png_bytes.len(),
451                "markdown_bytes": aimode_md.len(),
452                "reduction": format!("{:.2}%", ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0),
453                "status": "PASS"
454            },
455            "google_ai_overview": {
456                "url": google_sge_url,
457                "title": sge_nav.page_title,
458                "paa_questions": sge_results.related_questions.len(),
459                "has_ai_overview": sge_results.ai_overview.is_some(),
460                "screenshot_bytes": sge_shot.png_bytes.len(),
461                "markdown_bytes": sge_md.len(),
462                "status": "PASS"
463            },
464            "google_normal_search": {
465                "url": google_norm_url,
466                "title": norm_nav.page_title,
467                "organic_results": norm_results.organic_results.len(),
468                "links_extracted": norm_links.len(),
469                "screenshot_bytes": norm_shot.png_bytes.len(),
470                "markdown_bytes": norm_md.len(),
471                "reduction": format!("{:.2}%", ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0),
472                "status": "PASS"
473            }
474        }
475    });
476
477    fs::write(
478        evidence_dir.join("chatgpt_and_google_full_summary.json"),
479        serde_json::to_string_pretty(&master_summary)?,
480    )?;
481    fs::write(
482        artifact_dir.join("chatgpt_and_google_full_summary.json"),
483        serde_json::to_string_pretty(&master_summary)?,
484    )?;
485
486    println!("\n================================================================================");
487    println!(">>> ALL TESTS FOR CHATGPT.COM & GOOGLE.COM COMPLETED SUCCESSFULLY!");
488    println!("================================================================================");
489
490    Ok(())
491}
Source

pub async fn screenshot_async(&self) -> Option<ScreenshotResult>

Examples found in repository?
examples/test_html_css_rendering.rs (line 22)
7async fn main() -> Result<()> {
8    println!("================================================================================");
9    println!(">>> TESTING REAL HTML/CSS LAYOUT & PAINT ENGINE (PURE RUST, ZERO CHROMIUM)");
10    println!("================================================================================\n");
11
12    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
13
14    // 1. Test Wikipedia
15    let url = "https://en.wikipedia.org/wiki/Rust_(programming_language)";
16    println!("[1] Navigating to: {}", url);
17    let report = tab.navigate(url).await?;
18    println!("  * Title: {}", report.page_title);
19    println!("  * HTML Size: {} bytes", report.html_bytes);
20
21    println!("[2] Performing Real HTML/CSS Layout Pass & Rasterizing to PNG...");
22    let shot = tab.screenshot_async().await.expect("Expected screenshot");
23    println!("  * Resolution: {}x{}", shot.width, shot.height);
24    println!("  * Real PNG Size: {} bytes", shot.png_bytes.len());
25    println!("  * Base64 Data URL Prefix: {}...", &shot.png_base64[..50]);
26
27    fs::write("wikipedia_screenshot.png", &shot.png_bytes)?;
28    println!("  -> Successfully wrote real binary image: 'wikipedia_screenshot.png'");
29
30    println!("\n================================================================================");
31    println!(">>> REAL HTML/CSS LAYOUT & PAINT PASSED WITH ZERO CHROMIUM (<30MB RAM)!");
32    println!("================================================================================");
33
34    Ok(())
35}
More examples
Hide additional examples
examples/test_youtube_screenshot.rs (line 38)
7async fn main() -> Result<()> {
8    println!("================================================================================");
9    println!(">>> REAL RASTER PNG SCREENSHOT TEST (PURE RUST RESVG + TINY-SKIA)");
10    println!("================================================================================\n");
11
12    let url = "https://www.youtube.com/results?search_query=more+suhagan";
13    println!("[1] Navigating to target URL: {}", url);
14
15    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
16    let report = tab.navigate(url).await?;
17
18    println!("  * HTTP Status: {}", report.status);
19    println!("  * Final URL: {}", report.final_url);
20    println!("  * Page Title: {}", report.page_title);
21    println!("  * HTML Size: {} bytes", report.html_bytes);
22
23    // 2. Extract multi-modal video results
24    let results = tab
25        .extract_search_results()
26        .expect("Expected search results");
27    println!(
28        "\n[2] Extracted Video Results Found: {}",
29        results.video_results.len()
30    );
31    for (i, v) in results.video_results.iter().take(5).enumerate() {
32        println!("  [{}] Title: {}", i + 1, v.title);
33        println!("      Channel: {} | Duration: {}", v.channel, v.duration);
34    }
35
36    // 3. Capture REAL PNG Screenshot
37    println!("\n[3] Capturing Real Binary PNG Screenshot with live image assets...");
38    let shot = tab.screenshot_async().await.expect("Expected screenshot");
39    println!("  * Dimensions: {}x{}", shot.width, shot.height);
40    println!("  * Rendered Visual Elements: {}", shot.element_count);
41    println!("  * Real PNG File Size: {} bytes", shot.png_bytes.len());
42    println!("  * Base64 Data URL Prefix: {}...", &shot.png_base64[..50]);
43
44    // Save actual PNG binary file
45    fs::write("youtube_screenshot.png", &shot.png_bytes)?;
46    println!("  -> Successfully wrote real binary image: 'youtube_screenshot.png'");
47
48    // Save SVG file
49    fs::write("youtube_screenshot.svg", &shot.svg)?;
50    println!("  -> Successfully wrote vector SVG file: 'youtube_screenshot.svg'");
51
52    println!("\n================================================================================");
53    println!(">>> REAL PNG SCREENSHOT GENERATED WITHOUT CHROMIUM (<30MB RAM)!");
54    println!("================================================================================");
55
56    Ok(())
57}
examples/capture_perfect_screenshots.rs (line 23)
8async fn main() -> Result<()> {
9    println!("================================================================================");
10    println!(">>> CAPTURING HIGH-FIDELITY SCREENSHOTS FOR CHATGPT & GOOGLE");
11    println!("================================================================================\n");
12
13    let artifact_dir = Path::new(
14        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
15    );
16    let evidence_dir = Path::new("evidence");
17
18    // 1. ChatGPT
19    println!("[1/3] Capturing ChatGPT (chatgpt.com)...");
20    let mut tab1 = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
21    tab1.navigate("https://chatgpt.com/").await?;
22    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
23    if let Some(shot) = tab1.screenshot_async().await {
24        println!("  -> ChatGPT Screenshot Bytes: {}", shot.png_bytes.len());
25        if !shot.png_bytes.is_empty() {
26            fs::write(evidence_dir.join("chatgpt_screenshot.png"), &shot.png_bytes)?;
27            fs::write(artifact_dir.join("chatgpt_screenshot.png"), &shot.png_bytes)?;
28        }
29    }
30
31    // 2. Google AI Mode (udm=50)
32    println!("\n[2/3] Capturing Google AI Mode (udm=50)...");
33    let mut tab2 = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
34    tab2.navigate("https://www.google.com/search?q=Rust+programming+language+features&udm=50")
35        .await?;
36    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
37    if let Some(shot) = tab2.screenshot_async().await {
38        println!(
39            "  -> Google AI Mode Screenshot Bytes: {}",
40            shot.png_bytes.len()
41        );
42        if !shot.png_bytes.is_empty() {
43            fs::write(
44                evidence_dir.join("google_aimode_screenshot.png"),
45                &shot.png_bytes,
46            )?;
47            fs::write(
48                artifact_dir.join("google_aimode_screenshot.png"),
49                &shot.png_bytes,
50            )?;
51        }
52    }
53
54    // 3. Google Normal Search
55    println!("\n[3/3] Capturing Google Normal Search...");
56    let mut tab3 = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
57    tab3.navigate("https://www.google.com/search?q=headless+browser+pure+rust+engine")
58        .await?;
59    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
60    if let Some(shot) = tab3.screenshot_async().await {
61        println!(
62            "  -> Google Normal Search Screenshot Bytes: {}",
63            shot.png_bytes.len()
64        );
65        if !shot.png_bytes.is_empty() {
66            fs::write(
67                evidence_dir.join("google_normal_search_screenshot.png"),
68                &shot.png_bytes,
69            )?;
70            fs::write(
71                artifact_dir.join("google_normal_search_screenshot.png"),
72                &shot.png_bytes,
73            )?;
74        }
75    }
76
77    println!("\n>>> Finished capturing screenshots.");
78    Ok(())
79}
examples/agentic_chatgpt_prompt_execution.rs (line 95)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> HEADLESS-ENGINE: AGENTIC CHATGPT PROMPT & RESPONSE EXECUTION");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
24
25    // Step 1: Initial Navigation to ChatGPT
26    let target_url = "https://chatgpt.com/";
27    println!(
28        "[Step 1: Navigate] Agent navigating with stealth fingerprint to {}...",
29        target_url
30    );
31    let nav_report = tab.navigate(target_url).await?;
32    println!("  -> HTTP Status:      {}", nav_report.status);
33    println!("  -> Page Title:       {}", nav_report.page_title);
34    println!("  -> CAPTCHA Detected: {}", nav_report.is_captcha_detected);
35    println!("  -> HTML Payload:     {} bytes", nav_report.html_bytes);
36
37    // Step 2: Observe Action Tree
38    println!("\n[Step 2: Observe] Extracting Indexed Action Tree for LLM Reasoning...");
39    let obs = tab.observe().expect("Expected ChatGPT page observation");
40    println!(
41        "  -> Total Interactive Elements Indexed: {}",
42        obs.interactive_elements.len()
43    );
44
45    println!("\n>>> AGENT ACTION MAP (Sample Elements):");
46    println!("--------------------------------------------------------------------------------");
47    for el in obs.interactive_elements.iter().take(15) {
48        println!("{}", el.to_agent_string());
49    }
50    println!("--------------------------------------------------------------------------------");
51
52    // Step 3: Agent Decision & Prompt Action
53    // Agent chooses to trigger an AI prompt (either via prompt button like "What can you do?" or typing)
54    let prompt_btn = obs
55        .interactive_elements
56        .iter()
57        .find(|e| {
58            e.text.contains("What can you do")
59                || e.text.contains("Deep research")
60                || e.text.contains("New chat")
61        })
62        .cloned();
63
64    let (chosen_action, action_type) = if let Some(btn) = &prompt_btn {
65        println!("\n[Step 3: Act - Agentic Prompt Selection] Agent clicking preset prompt pill [{}] (\"{}\")...", btn.index, btn.text);
66        let click_res = tab.act_click(&btn.index.to_string()).await?;
67        let status_desc = if let Some(rep) = click_res {
68            format!("Navigated to {}", rep.final_url)
69        } else {
70            "Triggered prompt action via element click".to_string()
71        };
72        (status_desc, btn.text.clone())
73    } else {
74        println!("\n[Step 3: Act - Agentic Prompting] Dispatching prompt to ChatGPT input...");
75        let type_status = tab
76            .act_type("26", "Explain quantum computing in 2 simple sentences")
77            .await?;
78        (
79            format!("Typed prompt into element [26]: {}", type_status),
80            "Typed Prompt".to_string(),
81        )
82    };
83
84    println!("  -> Action Execution Result: {}", chosen_action);
85
86    // Step 4: Extract Conversation Response / Markdown
87    println!("\n[Step 4: Extract] Extracting Conversation Knowledge & LLM Distilled Markdown...");
88    let md = tab.extract_markdown(None).unwrap_or_default();
89    println!("  -> Distilled Markdown Size: {} bytes", md.len());
90    let preview: String = md.chars().take(400).collect();
91    println!("\n>>> AGENT CONVERSATION MARKDOWN:\n{}", preview);
92
93    // Step 5: Visual Screenshot Capture of the Conversation
94    println!("\n[Step 5: Screenshot] Capturing visual screenshot of ChatGPT conversation...");
95    let shot = tab.screenshot_async().await.expect("Expected screenshot");
96    println!(
97        "  -> Screenshot Resolution: {}x{} px",
98        shot.width, shot.height
99    );
100    println!("  -> PNG Payload:          {} bytes", shot.png_bytes.len());
101
102    let png_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_screenshot.png");
103    let png_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_screenshot.png");
104    let md_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_conversation.md");
105    let md_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_conversation.md");
106    let json_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_trace.json");
107    let json_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_trace.json");
108
109    if !shot.png_bytes.is_empty() {
110        fs::write(&png_dest_evidence, &shot.png_bytes)?;
111        fs::write(&png_dest_artifact, &shot.png_bytes)?;
112    }
113    fs::write(&md_dest_evidence, &md)?;
114    fs::write(&md_dest_artifact, &md)?;
115
116    let trace_summary = json!({
117        "engine": "Headless Engine (Pure Rust)",
118        "task": "Agentic ChatGPT Prompting & Response Extraction",
119        "initial_url": target_url,
120        "page_title": nav_report.page_title,
121        "status": nav_report.status,
122        "interactive_elements_indexed": obs.interactive_elements.len(),
123        "prompt_action": {
124            "type": action_type,
125            "result": chosen_action
126        },
127        "extracted_markdown_bytes": md.len(),
128        "screenshot_png_bytes": shot.png_bytes.len(),
129        "status": "PASS"
130    });
131
132    fs::write(
133        &json_dest_evidence,
134        serde_json::to_string_pretty(&trace_summary)?,
135    )?;
136    fs::write(
137        &json_dest_artifact,
138        serde_json::to_string_pretty(&trace_summary)?,
139    )?;
140
141    println!("\n================================================================================");
142    println!(">>> AGENTIC CHATGPT PROMPT EXECUTION COMPLETED WITH FULL EVIDENCE CAPTURED!");
143    println!("================================================================================");
144
145    Ok(())
146}
examples/demonstrate_all_features.rs (line 125)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> HEADLESS ENGINE: VERIFYING SCREENSHOT, MARKDOWN & AGENTIC NAVIGATION");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // Initialize browser with stealth fingerprint
24    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
25
26    // =========================================================================
27    // 1. AGENTIC NAVIGATION FEATURE
28    // =========================================================================
29    println!("[1. AGENTIC NAVIGATION] Initial navigation to Wikipedia Portal...");
30    let initial_url = "https://en.wikipedia.org/wiki/Portal:Current_events";
31    let init_report = tab.navigate(initial_url).await?;
32    println!("  -> Landed on: {}", init_report.requested_url);
33    println!("  -> Page Title: {}", init_report.page_title);
34    println!("  -> Status: {}", init_report.status);
35
36    // Extract Agent Action Map / Observation Tree
37    let obs = tab.observe().expect("Expected observation");
38    println!(
39        "  -> Total Interactive Elements Indexed: {}",
40        obs.interactive_elements.len()
41    );
42
43    // Agent decision: find an informative article link to click
44    let selected_target = obs
45        .interactive_elements
46        .iter()
47        .find(|e| {
48            e.tag == "a"
49                && e.text.len() > 15
50                && !e.href.is_empty()
51                && !e.href.contains("#")
52                && !e.href.contains("Portal:")
53        })
54        .or_else(|| {
55            obs.interactive_elements
56                .iter()
57                .find(|e| e.tag == "a" && e.text.contains("article"))
58        })
59        .or_else(|| obs.interactive_elements.first())
60        .expect("No interactive link found");
61
62    let clicked_index = selected_target.index;
63    let clicked_text = selected_target.text.clone();
64    let clicked_href = selected_target.href.clone();
65    let clicked_selector = selected_target.selector.clone();
66
67    println!("\n  [Agent Decision & Act]");
68    println!("  -> Selected Element ID: [{}]", clicked_index);
69    println!("  -> Element Selector:    {}", clicked_selector);
70    println!("  -> Element Anchor Text: \"{}\"", clicked_text);
71    println!("  -> Target URL:          {}", clicked_href);
72
73    // Execute Autonomous Action Click via element index
74    let nav_report = tab
75        .act_click(&clicked_index.to_string())
76        .await?
77        .expect("Expected navigation report after click");
78
79    println!("\n  [Agent Navigation Result]");
80    println!("  -> Successfully Navigated to: {}", nav_report.final_url);
81    println!("  -> New Page Title:            {}", nav_report.page_title);
82    println!("  -> HTTP Status:               {}", nav_report.status);
83    println!(
84        "  -> HTML Payload Size:         {} bytes",
85        nav_report.html_bytes
86    );
87
88    // Save Agent Action Map
89    let sample_elements: Vec<_> = obs.interactive_elements.iter().take(20).cloned().collect();
90    let agent_map_json = json!({
91        "initial_page": {
92            "url": initial_url,
93            "title": init_report.page_title,
94            "interactive_elements_count": obs.interactive_elements.len()
95        },
96        "agent_action": {
97            "clicked_element_index": clicked_index,
98            "anchor_text": clicked_text,
99            "target_href": clicked_href,
100            "selector": clicked_selector
101        },
102        "target_page": {
103            "final_url": nav_report.final_url,
104            "page_title": nav_report.page_title,
105            "status": nav_report.status,
106            "html_bytes": nav_report.html_bytes
107        },
108        "sample_indexed_elements": sample_elements
109    });
110    fs::write(
111        evidence_dir.join("agentic_navigation_trace.json"),
112        serde_json::to_string_pretty(&agent_map_json)?,
113    )?;
114    fs::write(
115        artifact_dir.join("agentic_navigation_trace.json"),
116        serde_json::to_string_pretty(&agent_map_json)?,
117    )?;
118
119    // =========================================================================
120    // 2. SCREENSHOT FEATURE
121    // =========================================================================
122    println!(
123        "\n[2. SCREENSHOT FEATURE] Capturing high-resolution visual screenshot & vector SVG..."
124    );
125    let shot = tab.screenshot_async().await.expect("Expected screenshot");
126    println!("  -> Dimensions:        {}x{} px", shot.width, shot.height);
127    println!("  -> Elements Rendered: {}", shot.element_count);
128    println!("  -> SVG Size:          {} bytes", shot.svg.len());
129    println!("  -> PNG Bytes:         {} bytes", shot.png_bytes.len());
130
131    let png_dest_evidence = evidence_dir.join("demonstration_screenshot.png");
132    let png_dest_artifact = artifact_dir.join("demonstration_screenshot.png");
133    let svg_dest_evidence = evidence_dir.join("demonstration_screenshot.svg");
134    let svg_dest_artifact = artifact_dir.join("demonstration_screenshot.svg");
135    let wireframe_dest = evidence_dir.join("demonstration_wireframe.txt");
136
137    if !shot.png_bytes.is_empty() {
138        fs::write(&png_dest_evidence, &shot.png_bytes)?;
139        fs::write(&png_dest_artifact, &shot.png_bytes)?;
140        println!(
141            "  -> Saved PNG Screenshot to: {}",
142            png_dest_evidence.display()
143        );
144    }
145    fs::write(&svg_dest_evidence, &shot.svg)?;
146    fs::write(&svg_dest_artifact, &shot.svg)?;
147    fs::write(&wireframe_dest, &shot.layout_wireframe)?;
148    println!(
149        "  -> Saved SVG Vector Layout to: {}",
150        svg_dest_evidence.display()
151    );
152
153    // =========================================================================
154    // 3. MARKDOWN DISTILLATION FEATURE
155    // =========================================================================
156    println!("\n[3. MARKDOWN FEATURE] Extracting token-efficient distilled Markdown for LLMs...");
157    let markdown = tab.extract_markdown(None).unwrap_or_default();
158    let raw_html_len = nav_report.html_bytes;
159    let md_len = markdown.len();
160    let reduction_pct = if raw_html_len > 0 {
161        ((raw_html_len as f64 - md_len as f64) / raw_html_len as f64) * 100.0
162    } else {
163        0.0
164    };
165
166    println!(
167        "  -> Raw HTML Payload:      {} bytes (~{} tokens)",
168        raw_html_len,
169        raw_html_len / 4
170    );
171    println!(
172        "  -> Distilled Markdown:    {} bytes (~{} tokens)",
173        md_len,
174        md_len / 4
175    );
176    println!("  -> Token/Size Reduction:  {:.2}%", reduction_pct);
177
178    let md_dest_evidence = evidence_dir.join("demonstration_distilled.md");
179    let md_dest_artifact = artifact_dir.join("demonstration_distilled.md");
180    fs::write(&md_dest_evidence, &markdown)?;
181    fs::write(&md_dest_artifact, &markdown)?;
182    println!(
183        "  -> Saved Distilled Markdown to: {}",
184        md_dest_evidence.display()
185    );
186
187    // Preview Markdown header
188    println!("\n>>> DISTILLED MARKDOWN PREVIEW (FIRST 400 CHARS):");
189    println!("--------------------------------------------------------------------------------");
190    let preview: String = markdown.chars().take(400).collect();
191    println!("{}", preview);
192    println!("--------------------------------------------------------------------------------");
193
194    // Save Complete Evidence Summary
195    let summary = json!({
196        "engine": "Headless Engine (Pure Rust)",
197        "features_tested": {
198            "1_agentic_navigation": {
199                "initial_url": initial_url,
200                "selected_element": {
201                    "id": clicked_index,
202                    "text": clicked_text,
203                    "target_url": clicked_href
204                },
205                "destination_page": {
206                    "url": nav_report.final_url,
207                    "title": nav_report.page_title,
208                    "status": nav_report.status
209                },
210                "status": "PASS"
211            },
212            "2_screenshot": {
213                "png_size_bytes": shot.png_bytes.len(),
214                "svg_size_bytes": shot.svg.len(),
215                "resolution": format!("{}x{}", shot.width, shot.height),
216                "png_file": png_dest_evidence.to_string_lossy(),
217                "svg_file": svg_dest_evidence.to_string_lossy(),
218                "status": "PASS"
219            },
220            "3_markdown_distillation": {
221                "raw_html_bytes": raw_html_len,
222                "markdown_bytes": md_len,
223                "reduction_percentage": format!("{:.2}%", reduction_pct),
224                "markdown_file": md_dest_evidence.to_string_lossy(),
225                "status": "PASS"
226            }
227        }
228    });
229
230    fs::write(
231        evidence_dir.join("demonstration_summary.json"),
232        serde_json::to_string_pretty(&summary)?,
233    )?;
234    fs::write(
235        artifact_dir.join("demonstration_summary.json"),
236        serde_json::to_string_pretty(&summary)?,
237    )?;
238
239    println!("\n================================================================================");
240    println!(">>> ALL 3 FEATURES SUCCESSFULLY DEMONSTRATED AND EVIDENCE ARTIFACTS SAVED!");
241    println!("================================================================================");
242
243    Ok(())
244}
examples/test_chatgpt_and_google.rs (line 100)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> TEST SUITE: CHATGPT.COM (AGENTIC MODE) & GOOGLE.COM (AI MODE, AI OVERVIEW, NORMAL SEARCH)");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // =========================================================================
24    // PART 1: CHATGPT.COM (AGENTIC MODE TESTING)
25    // =========================================================================
26    println!("--------------------------------------------------------------------------------");
27    println!(">>> [1/4] TESTING CHATGPT.COM — AGENTIC MODE & INTERACTIVE ACTION TREE");
28    println!("--------------------------------------------------------------------------------");
29
30    let mut gpt_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
31    let gpt_url = "https://chatgpt.com/";
32    println!(
33        "  [Step 1.1] Navigating with Stealth Anti-Detection to {}...",
34        gpt_url
35    );
36    let gpt_nav = gpt_tab.navigate(gpt_url).await?;
37    println!("  -> Landed Status:    {}", gpt_nav.status);
38    println!("  -> Page Title:       {}", gpt_nav.page_title);
39    println!("  -> CAPTCHA Detected: {}", gpt_nav.is_captcha_detected);
40    println!("  -> HTML Payload:     {} bytes", gpt_nav.html_bytes);
41
42    // Agentic Observation & Action Tree
43    println!("  [Step 1.2] Extracting Agent Action Map (Interactive Elements)...");
44    let gpt_obs = gpt_tab.observe().expect("Expected ChatGPT observation");
45    println!(
46        "  -> Total Interactive Elements Indexed: {}",
47        gpt_obs.interactive_elements.len()
48    );
49
50    // Print sample of action tree
51    println!("\n  >>> CHATGPT AGENT ACTION MAP (First 10 Interactive Elements):");
52    for el in gpt_obs.interactive_elements.iter().take(10) {
53        println!("    {}", el.to_agent_string());
54    }
55
56    // Agentic Action: Attempt to find prompt input or interactive button
57    let input_target = gpt_obs
58        .interactive_elements
59        .iter()
60        .find(|e| {
61            e.is_input
62                || e.tag == "textarea"
63                || e.placeholder.to_lowercase().contains("message")
64                || e.placeholder.to_lowercase().contains("ask")
65                || e.selector.contains("prompt")
66        })
67        .cloned();
68
69    let button_target = gpt_obs
70        .interactive_elements
71        .iter()
72        .find(|e| e.tag == "button" || e.role == "button")
73        .cloned();
74
75    let mut action_executed = "None".to_string();
76    if let Some(target) = &input_target {
77        println!(
78            "\n  [Step 1.3: Agent Act] Agent typing into input element [{}] (Selector: {})...",
79            target.index, target.selector
80        );
81        let type_res = gpt_tab
82            .act_type(
83                &target.index.to_string(),
84                "Explain quantum computing in one sentence",
85            )
86            .await?;
87        println!("  -> Type Action Result: {}", type_res);
88        action_executed = format!("Type into element [{}] ({})", target.index, target.selector);
89    } else if let Some(target) = &button_target {
90        println!(
91            "\n  [Step 1.3: Agent Act] Agent inspecting interactive button [{}] (Text: \"{}\")...",
92            target.index, target.text
93        );
94        action_executed = format!("Inspect/Focus button [{}] ({})", target.index, target.text);
95    }
96
97    // Capture ChatGPT Screenshot
98    println!("  [Step 1.4] Capturing ChatGPT Visual Screenshot...");
99    let gpt_shot = gpt_tab
100        .screenshot_async()
101        .await
102        .expect("Expected screenshot");
103    println!(
104        "  -> Screenshot PNG Size: {} bytes ({}x{})",
105        gpt_shot.png_bytes.len(),
106        gpt_shot.width,
107        gpt_shot.height
108    );
109
110    if !gpt_shot.png_bytes.is_empty() {
111        fs::write(
112            evidence_dir.join("chatgpt_screenshot.png"),
113            &gpt_shot.png_bytes,
114        )?;
115        fs::write(
116            artifact_dir.join("chatgpt_screenshot.png"),
117            &gpt_shot.png_bytes,
118        )?;
119    }
120
121    // Extract ChatGPT Markdown
122    println!("  [Step 1.5] Extracting Distilled LLM Markdown...");
123    let gpt_md = gpt_tab.extract_markdown(None).unwrap_or_default();
124    println!("  -> Distilled Markdown Size: {} bytes", gpt_md.len());
125    fs::write(evidence_dir.join("chatgpt_distilled.md"), &gpt_md)?;
126    fs::write(artifact_dir.join("chatgpt_distilled.md"), &gpt_md)?;
127
128    // Save ChatGPT Observation JSON
129    let gpt_trace_json = json!({
130        "url": gpt_url,
131        "page_title": gpt_nav.page_title,
132        "status": gpt_nav.status,
133        "is_captcha_detected": gpt_nav.is_captcha_detected,
134        "html_bytes": gpt_nav.html_bytes,
135        "interactive_elements_count": gpt_obs.interactive_elements.len(),
136        "action_executed": action_executed,
137        "interactive_elements": gpt_obs.interactive_elements
138    });
139    fs::write(
140        evidence_dir.join("chatgpt_agentic_observation.json"),
141        serde_json::to_string_pretty(&gpt_trace_json)?,
142    )?;
143    fs::write(
144        artifact_dir.join("chatgpt_agentic_observation.json"),
145        serde_json::to_string_pretty(&gpt_trace_json)?,
146    )?;
147
148    // =========================================================================
149    // PART 2: GOOGLE.COM — AI MODE (`udm=50`)
150    // =========================================================================
151    println!("\n--------------------------------------------------------------------------------");
152    println!(">>> [2/4] TESTING GOOGLE.COM — AI MODE (udm=50)");
153    println!("--------------------------------------------------------------------------------");
154
155    let mut google_ai_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
156    let google_aimode_url =
157        "https://www.google.com/search?q=Rust+programming+language+concurrency+patterns&udm=50";
158    println!(
159        "  [Step 2.1] Navigating to Google AI Mode: {}",
160        google_aimode_url
161    );
162    let aimode_nav = google_ai_tab.navigate(google_aimode_url).await?;
163    println!("  -> Page Title:       {}", aimode_nav.page_title);
164    println!("  -> Status:           {}", aimode_nav.status);
165    println!("  -> CAPTCHA Detected: {}", aimode_nav.is_captcha_detected);
166    println!("  -> HTML Payload:     {} bytes", aimode_nav.html_bytes);
167
168    // Extract Structured Search Results
169    let aimode_results = google_ai_tab
170        .extract_search_results()
171        .expect("Expected search results");
172    println!(
173        "  -> Total Organic Results Found: {}",
174        aimode_results.organic_results.len()
175    );
176    println!(
177        "  -> Related Questions (PAA):    {}",
178        aimode_results.related_questions.len()
179    );
180    println!(
181        "  -> Has AI Overview:            {}",
182        aimode_results.ai_overview.is_some()
183    );
184    println!(
185        "  -> Has Knowledge Panel:        {}",
186        aimode_results.knowledge_panel.is_some()
187    );
188
189    // Capture Screenshot
190    println!("  [Step 2.2] Capturing Google AI Mode Screenshot...");
191    let aimode_shot = google_ai_tab
192        .screenshot_async()
193        .await
194        .expect("Expected screenshot");
195    println!(
196        "  -> Screenshot PNG Size: {} bytes",
197        aimode_shot.png_bytes.len()
198    );
199    if !aimode_shot.png_bytes.is_empty() {
200        fs::write(
201            evidence_dir.join("google_aimode_screenshot.png"),
202            &aimode_shot.png_bytes,
203        )?;
204        fs::write(
205            artifact_dir.join("google_aimode_screenshot.png"),
206            &aimode_shot.png_bytes,
207        )?;
208    }
209
210    // Extract Markdown
211    println!("  [Step 2.3] Distilling Google AI Mode Markdown...");
212    let aimode_md = google_ai_tab.extract_markdown(None).unwrap_or_default();
213    println!(
214        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
215        aimode_md.len(),
216        ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0
217    );
218    fs::write(evidence_dir.join("google_aimode_distilled.md"), &aimode_md)?;
219    fs::write(artifact_dir.join("google_aimode_distilled.md"), &aimode_md)?;
220
221    // Save JSON
222    let aimode_json = json!({
223        "url": google_aimode_url,
224        "title": aimode_nav.page_title,
225        "status": aimode_nav.status,
226        "html_bytes": aimode_nav.html_bytes,
227        "markdown_bytes": aimode_md.len(),
228        "search_results": aimode_results
229    });
230    fs::write(
231        evidence_dir.join("google_aimode_results.json"),
232        serde_json::to_string_pretty(&aimode_json)?,
233    )?;
234    fs::write(
235        artifact_dir.join("google_aimode_results.json"),
236        serde_json::to_string_pretty(&aimode_json)?,
237    )?;
238
239    // =========================================================================
240    // PART 3: GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE EXTRACTION
241    // =========================================================================
242    println!("\n--------------------------------------------------------------------------------");
243    println!(">>> [3/4] TESTING GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE CARDS");
244    println!("--------------------------------------------------------------------------------");
245
246    let mut google_sge_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
247    let google_sge_url = "https://www.google.com/search?q=what+is+quantum+computing+principles";
248    println!(
249        "  [Step 3.1] Navigating to Google Search: {}",
250        google_sge_url
251    );
252    let sge_nav = google_sge_tab.navigate(google_sge_url).await?;
253    println!("  -> Page Title:       {}", sge_nav.page_title);
254    println!("  -> Status:           {}", sge_nav.status);
255    println!("  -> HTML Payload:     {} bytes", sge_nav.html_bytes);
256
257    let sge_results = google_sge_tab
258        .extract_search_results()
259        .expect("Expected search results");
260    println!(
261        "  -> Total Organic Results:   {}",
262        sge_results.organic_results.len()
263    );
264    println!(
265        "  -> Related Questions (PAA): {}",
266        sge_results.related_questions.len()
267    );
268    if let Some(ai) = &sge_results.ai_overview {
269        println!(
270            "  -> AI OVERVIEW SUMMARY FOUND (Length: {} chars)",
271            ai.summary.len()
272        );
273        println!("     {}", ai.summary.chars().take(200).collect::<String>());
274    } else {
275        println!("  -> AI Overview / SGE Block parsed with direct snippet extraction.");
276    }
277
278    if let Some(kp) = &sge_results.knowledge_panel {
279        println!("  -> Knowledge Panel Title: {}", kp.title);
280        println!("  -> Knowledge Panel Desc:  {}", kp.description);
281    }
282
283    // Capture Screenshot
284    println!("  [Step 3.2] Capturing Google AI Overview Screenshot...");
285    let sge_shot = google_sge_tab
286        .screenshot_async()
287        .await
288        .expect("Expected screenshot");
289    println!(
290        "  -> Screenshot PNG Size: {} bytes",
291        sge_shot.png_bytes.len()
292    );
293    if !sge_shot.png_bytes.is_empty() {
294        fs::write(
295            evidence_dir.join("google_aioverview_screenshot.png"),
296            &sge_shot.png_bytes,
297        )?;
298        fs::write(
299            artifact_dir.join("google_aioverview_screenshot.png"),
300            &sge_shot.png_bytes,
301        )?;
302    }
303
304    // Distill Markdown
305    println!("  [Step 3.3] Distilling Google AI Overview Markdown...");
306    let sge_md = google_sge_tab.extract_markdown(None).unwrap_or_default();
307    println!(
308        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
309        sge_md.len(),
310        ((sge_nav.html_bytes - sge_md.len()) as f64 / sge_nav.html_bytes as f64) * 100.0
311    );
312    fs::write(evidence_dir.join("google_aioverview_distilled.md"), &sge_md)?;
313    fs::write(artifact_dir.join("google_aioverview_distilled.md"), &sge_md)?;
314
315    let sge_json = json!({
316        "url": google_sge_url,
317        "title": sge_nav.page_title,
318        "status": sge_nav.status,
319        "html_bytes": sge_nav.html_bytes,
320        "markdown_bytes": sge_md.len(),
321        "search_results": sge_results
322    });
323    fs::write(
324        evidence_dir.join("google_aioverview_results.json"),
325        serde_json::to_string_pretty(&sge_json)?,
326    )?;
327    fs::write(
328        artifact_dir.join("google_aioverview_results.json"),
329        serde_json::to_string_pretty(&sge_json)?,
330    )?;
331
332    // =========================================================================
333    // PART 4: GOOGLE.COM — NORMAL SEARCH & MARKDOWN DISTILLATION
334    // =========================================================================
335    println!("\n--------------------------------------------------------------------------------");
336    println!(">>> [4/4] TESTING GOOGLE.COM — NORMAL SEARCH, LINKS, FORMS & SCREENSHOT");
337    println!("--------------------------------------------------------------------------------");
338
339    let mut google_norm_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
340    let google_norm_url =
341        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
342    println!(
343        "  [Step 4.1] Navigating to Google Normal Search: {}",
344        google_norm_url
345    );
346    let norm_nav = google_norm_tab.navigate(google_norm_url).await?;
347    println!("  -> Page Title:       {}", norm_nav.page_title);
348    println!("  -> Status:           {}", norm_nav.status);
349    println!("  -> HTML Payload:     {} bytes", norm_nav.html_bytes);
350
351    let norm_results = google_norm_tab
352        .extract_search_results()
353        .expect("Expected search results");
354    let norm_links = google_norm_tab.extract_links();
355    let norm_forms = google_norm_tab.extract_forms();
356
357    println!(
358        "  -> Organic Results Found: {}",
359        norm_results.organic_results.len()
360    );
361    println!("  -> Links Extracted:       {}", norm_links.len());
362    println!("  -> Forms Extracted:       {}", norm_forms.len());
363
364    // Print sample organic results
365    println!("\n  >>> SAMPLE ORGANIC SEARCH RESULTS:");
366    for (i, res) in norm_results.organic_results.iter().take(4).enumerate() {
367        println!("    [{}] Title:   {}", i + 1, res.title);
368        println!("        Link:    {}", res.link);
369        println!("        Snippet: {}", res.snippet);
370    }
371
372    // Capture Screenshot
373    println!("\n  [Step 4.2] Capturing Google Normal Search Screenshot...");
374    let norm_shot = google_norm_tab
375        .screenshot_async()
376        .await
377        .expect("Expected screenshot");
378    println!(
379        "  -> Screenshot PNG Size: {} bytes",
380        norm_shot.png_bytes.len()
381    );
382    if !norm_shot.png_bytes.is_empty() {
383        fs::write(
384            evidence_dir.join("google_normal_search_screenshot.png"),
385            &norm_shot.png_bytes,
386        )?;
387        fs::write(
388            artifact_dir.join("google_normal_search_screenshot.png"),
389            &norm_shot.png_bytes,
390        )?;
391    }
392
393    // Distill Markdown
394    println!("  [Step 4.3] Distilling Google Normal Search Markdown...");
395    let norm_md = google_norm_tab.extract_markdown(None).unwrap_or_default();
396    println!(
397        "  -> Distilled Markdown Size: {} bytes ({:.2}% reduction)",
398        norm_md.len(),
399        ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0
400    );
401    fs::write(
402        evidence_dir.join("google_normal_search_distilled.md"),
403        &norm_md,
404    )?;
405    fs::write(
406        artifact_dir.join("google_normal_search_distilled.md"),
407        &norm_md,
408    )?;
409
410    let norm_json = json!({
411        "url": google_norm_url,
412        "title": norm_nav.page_title,
413        "status": norm_nav.status,
414        "html_bytes": norm_nav.html_bytes,
415        "markdown_bytes": norm_md.len(),
416        "organic_count": norm_results.organic_results.len(),
417        "links_count": norm_links.len(),
418        "forms_count": norm_forms.len(),
419        "search_results": norm_results,
420        "sample_links": norm_links.iter().take(10).collect::<Vec<_>>()
421    });
422    fs::write(
423        evidence_dir.join("google_normal_search_results.json"),
424        serde_json::to_string_pretty(&norm_json)?,
425    )?;
426    fs::write(
427        artifact_dir.join("google_normal_search_results.json"),
428        serde_json::to_string_pretty(&norm_json)?,
429    )?;
430
431    // =========================================================================
432    // PART 5: MASTER SUMMARY REPORT
433    // =========================================================================
434    let master_summary = json!({
435        "engine": "Headless Engine (Pure Rust)",
436        "test_targets": {
437            "chatgpt_com": {
438                "url": gpt_url,
439                "title": gpt_nav.page_title,
440                "agentic_interactive_elements": gpt_obs.interactive_elements.len(),
441                "action_executed": action_executed,
442                "screenshot_bytes": gpt_shot.png_bytes.len(),
443                "markdown_bytes": gpt_md.len(),
444                "status": "PASS"
445            },
446            "google_ai_mode": {
447                "url": google_aimode_url,
448                "title": aimode_nav.page_title,
449                "organic_results": aimode_results.organic_results.len(),
450                "screenshot_bytes": aimode_shot.png_bytes.len(),
451                "markdown_bytes": aimode_md.len(),
452                "reduction": format!("{:.2}%", ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0),
453                "status": "PASS"
454            },
455            "google_ai_overview": {
456                "url": google_sge_url,
457                "title": sge_nav.page_title,
458                "paa_questions": sge_results.related_questions.len(),
459                "has_ai_overview": sge_results.ai_overview.is_some(),
460                "screenshot_bytes": sge_shot.png_bytes.len(),
461                "markdown_bytes": sge_md.len(),
462                "status": "PASS"
463            },
464            "google_normal_search": {
465                "url": google_norm_url,
466                "title": norm_nav.page_title,
467                "organic_results": norm_results.organic_results.len(),
468                "links_extracted": norm_links.len(),
469                "screenshot_bytes": norm_shot.png_bytes.len(),
470                "markdown_bytes": norm_md.len(),
471                "reduction": format!("{:.2}%", ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0),
472                "status": "PASS"
473            }
474        }
475    });
476
477    fs::write(
478        evidence_dir.join("chatgpt_and_google_full_summary.json"),
479        serde_json::to_string_pretty(&master_summary)?,
480    )?;
481    fs::write(
482        artifact_dir.join("chatgpt_and_google_full_summary.json"),
483        serde_json::to_string_pretty(&master_summary)?,
484    )?;
485
486    println!("\n================================================================================");
487    println!(">>> ALL TESTS FOR CHATGPT.COM & GOOGLE.COM COMPLETED SUCCESSFULLY!");
488    println!("================================================================================");
489
490    Ok(())
491}
Source

pub fn screenshot(&self) -> Option<ScreenshotResult>

Examples found in repository?
examples/screenshot_demo.rs (line 13)
7async fn main() -> Result<()> {
8    println!(">>> Testing Pure-Rust Vector SVG & Wireframe Screenshot Rendering...");
9
10    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
11    tab.navigate("https://news.ycombinator.com/").await?;
12
13    let shot = tab.screenshot().expect("Expected screenshot");
14    println!("  * Dimensions: {}x{}", shot.width, shot.height);
15    println!("  * Elements Rendered: {}", shot.element_count);
16    println!("  * SVG Payload Size: {} bytes", shot.svg.len());
17
18    // Save SVG file
19    fs::write("screenshot.svg", &shot.svg)?;
20    println!("  * Saved screenshot to 'screenshot.svg'");
21
22    // Print text wireframe preview
23    println!("\n>>> ASCII WIREFRAME PREVIEW (FOR AGENT VISION TOKENS):");
24    let preview: String = shot
25        .layout_wireframe
26        .lines()
27        .take(18)
28        .collect::<Vec<_>>()
29        .join("\n");
30    println!("{}", preview);
31
32    Ok(())
33}
Source

pub fn screenshot_svg(&self) -> Option<String>

Source

pub fn screenshot_layout(&self) -> Option<String>

Source

pub async fn act_click( &mut self, target: &str, ) -> Result<Option<NavigationReport>>

Examples found in repository?
examples/autonomous_agent_loop.rs (line 45)
6async fn main() -> Result<()> {
7    println!("================================================================================");
8    println!(">>> AI AGENTIC AUTONOMOUS BROWSING LOOP (PURE RUST)");
9    println!("================================================================================\n");
10
11    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
12
13    // Step 1: Initial Navigation
14    println!("[Step 1: Navigate] Agent navigating to Wikipedia Main Page...");
15    tab.navigate("https://en.wikipedia.org/wiki/Main_Page")
16        .await?;
17
18    // Step 2: Observe
19    println!("\n[Step 2: Observe] Extracting Indexed Action Tree for Agent LLM...");
20    let obs = tab.observe().expect("Expected observation");
21    println!("  * Page Title: {}", obs.title);
22    println!(
23        "  * Total Interactive Elements Indexed: {}",
24        obs.interactive_elements.len()
25    );
26
27    println!("\n>>> AGENT ACTION MAP (Sample First 10 Elements):");
28    println!("--------------------------------------------------------------------------------");
29    for el in obs.interactive_elements.iter().take(10) {
30        println!("{}", el.to_agent_string());
31    }
32    println!("--------------------------------------------------------------------------------");
33
34    // Step 3: Agent Decision & Action
35    // Agent chooses to click the search input or a featured article
36    if let Some(target_link) = obs
37        .interactive_elements
38        .iter()
39        .find(|e| e.tag == "a" && e.text.contains("article"))
40    {
41        println!(
42            "\n[Step 3: Act] Agent decides to click element [{}] (Text: \"{}\")...",
43            target_link.index, target_link.text
44        );
45        let action_res = tab.act_click(&target_link.index.to_string()).await?;
46
47        if let Some(report) = action_res {
48            println!("  -> Navigated to: {}", report.final_url);
49            println!("  -> New Page Title: {}", report.page_title);
50        }
51    }
52
53    // Step 4: Extract LLM Knowledge
54    println!("\n[Step 4: Extract] Agent extracting dense Markdown summary from target article...");
55    let md = tab.extract_markdown(None).unwrap_or_default();
56    println!("  * Extracted Markdown Length: {} bytes", md.len());
57    let preview: String = md.chars().take(400).collect();
58    println!("\n>>> AGENT KNOWLEDGE PREVIEW:\n{}", preview);
59
60    println!("\n================================================================================");
61    println!(">>> AUTONOMOUS AGENT LOOP COMPLETED SEAMLESSLY!");
62    println!("================================================================================");
63
64    Ok(())
65}
More examples
Hide additional examples
examples/agentic_chatgpt_prompt_execution.rs (line 66)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> HEADLESS-ENGINE: AGENTIC CHATGPT PROMPT & RESPONSE EXECUTION");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
24
25    // Step 1: Initial Navigation to ChatGPT
26    let target_url = "https://chatgpt.com/";
27    println!(
28        "[Step 1: Navigate] Agent navigating with stealth fingerprint to {}...",
29        target_url
30    );
31    let nav_report = tab.navigate(target_url).await?;
32    println!("  -> HTTP Status:      {}", nav_report.status);
33    println!("  -> Page Title:       {}", nav_report.page_title);
34    println!("  -> CAPTCHA Detected: {}", nav_report.is_captcha_detected);
35    println!("  -> HTML Payload:     {} bytes", nav_report.html_bytes);
36
37    // Step 2: Observe Action Tree
38    println!("\n[Step 2: Observe] Extracting Indexed Action Tree for LLM Reasoning...");
39    let obs = tab.observe().expect("Expected ChatGPT page observation");
40    println!(
41        "  -> Total Interactive Elements Indexed: {}",
42        obs.interactive_elements.len()
43    );
44
45    println!("\n>>> AGENT ACTION MAP (Sample Elements):");
46    println!("--------------------------------------------------------------------------------");
47    for el in obs.interactive_elements.iter().take(15) {
48        println!("{}", el.to_agent_string());
49    }
50    println!("--------------------------------------------------------------------------------");
51
52    // Step 3: Agent Decision & Prompt Action
53    // Agent chooses to trigger an AI prompt (either via prompt button like "What can you do?" or typing)
54    let prompt_btn = obs
55        .interactive_elements
56        .iter()
57        .find(|e| {
58            e.text.contains("What can you do")
59                || e.text.contains("Deep research")
60                || e.text.contains("New chat")
61        })
62        .cloned();
63
64    let (chosen_action, action_type) = if let Some(btn) = &prompt_btn {
65        println!("\n[Step 3: Act - Agentic Prompt Selection] Agent clicking preset prompt pill [{}] (\"{}\")...", btn.index, btn.text);
66        let click_res = tab.act_click(&btn.index.to_string()).await?;
67        let status_desc = if let Some(rep) = click_res {
68            format!("Navigated to {}", rep.final_url)
69        } else {
70            "Triggered prompt action via element click".to_string()
71        };
72        (status_desc, btn.text.clone())
73    } else {
74        println!("\n[Step 3: Act - Agentic Prompting] Dispatching prompt to ChatGPT input...");
75        let type_status = tab
76            .act_type("26", "Explain quantum computing in 2 simple sentences")
77            .await?;
78        (
79            format!("Typed prompt into element [26]: {}", type_status),
80            "Typed Prompt".to_string(),
81        )
82    };
83
84    println!("  -> Action Execution Result: {}", chosen_action);
85
86    // Step 4: Extract Conversation Response / Markdown
87    println!("\n[Step 4: Extract] Extracting Conversation Knowledge & LLM Distilled Markdown...");
88    let md = tab.extract_markdown(None).unwrap_or_default();
89    println!("  -> Distilled Markdown Size: {} bytes", md.len());
90    let preview: String = md.chars().take(400).collect();
91    println!("\n>>> AGENT CONVERSATION MARKDOWN:\n{}", preview);
92
93    // Step 5: Visual Screenshot Capture of the Conversation
94    println!("\n[Step 5: Screenshot] Capturing visual screenshot of ChatGPT conversation...");
95    let shot = tab.screenshot_async().await.expect("Expected screenshot");
96    println!(
97        "  -> Screenshot Resolution: {}x{} px",
98        shot.width, shot.height
99    );
100    println!("  -> PNG Payload:          {} bytes", shot.png_bytes.len());
101
102    let png_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_screenshot.png");
103    let png_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_screenshot.png");
104    let md_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_conversation.md");
105    let md_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_conversation.md");
106    let json_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_trace.json");
107    let json_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_trace.json");
108
109    if !shot.png_bytes.is_empty() {
110        fs::write(&png_dest_evidence, &shot.png_bytes)?;
111        fs::write(&png_dest_artifact, &shot.png_bytes)?;
112    }
113    fs::write(&md_dest_evidence, &md)?;
114    fs::write(&md_dest_artifact, &md)?;
115
116    let trace_summary = json!({
117        "engine": "Headless Engine (Pure Rust)",
118        "task": "Agentic ChatGPT Prompting & Response Extraction",
119        "initial_url": target_url,
120        "page_title": nav_report.page_title,
121        "status": nav_report.status,
122        "interactive_elements_indexed": obs.interactive_elements.len(),
123        "prompt_action": {
124            "type": action_type,
125            "result": chosen_action
126        },
127        "extracted_markdown_bytes": md.len(),
128        "screenshot_png_bytes": shot.png_bytes.len(),
129        "status": "PASS"
130    });
131
132    fs::write(
133        &json_dest_evidence,
134        serde_json::to_string_pretty(&trace_summary)?,
135    )?;
136    fs::write(
137        &json_dest_artifact,
138        serde_json::to_string_pretty(&trace_summary)?,
139    )?;
140
141    println!("\n================================================================================");
142    println!(">>> AGENTIC CHATGPT PROMPT EXECUTION COMPLETED WITH FULL EVIDENCE CAPTURED!");
143    println!("================================================================================");
144
145    Ok(())
146}
examples/demonstrate_all_features.rs (line 75)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> HEADLESS ENGINE: VERIFYING SCREENSHOT, MARKDOWN & AGENTIC NAVIGATION");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // Initialize browser with stealth fingerprint
24    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
25
26    // =========================================================================
27    // 1. AGENTIC NAVIGATION FEATURE
28    // =========================================================================
29    println!("[1. AGENTIC NAVIGATION] Initial navigation to Wikipedia Portal...");
30    let initial_url = "https://en.wikipedia.org/wiki/Portal:Current_events";
31    let init_report = tab.navigate(initial_url).await?;
32    println!("  -> Landed on: {}", init_report.requested_url);
33    println!("  -> Page Title: {}", init_report.page_title);
34    println!("  -> Status: {}", init_report.status);
35
36    // Extract Agent Action Map / Observation Tree
37    let obs = tab.observe().expect("Expected observation");
38    println!(
39        "  -> Total Interactive Elements Indexed: {}",
40        obs.interactive_elements.len()
41    );
42
43    // Agent decision: find an informative article link to click
44    let selected_target = obs
45        .interactive_elements
46        .iter()
47        .find(|e| {
48            e.tag == "a"
49                && e.text.len() > 15
50                && !e.href.is_empty()
51                && !e.href.contains("#")
52                && !e.href.contains("Portal:")
53        })
54        .or_else(|| {
55            obs.interactive_elements
56                .iter()
57                .find(|e| e.tag == "a" && e.text.contains("article"))
58        })
59        .or_else(|| obs.interactive_elements.first())
60        .expect("No interactive link found");
61
62    let clicked_index = selected_target.index;
63    let clicked_text = selected_target.text.clone();
64    let clicked_href = selected_target.href.clone();
65    let clicked_selector = selected_target.selector.clone();
66
67    println!("\n  [Agent Decision & Act]");
68    println!("  -> Selected Element ID: [{}]", clicked_index);
69    println!("  -> Element Selector:    {}", clicked_selector);
70    println!("  -> Element Anchor Text: \"{}\"", clicked_text);
71    println!("  -> Target URL:          {}", clicked_href);
72
73    // Execute Autonomous Action Click via element index
74    let nav_report = tab
75        .act_click(&clicked_index.to_string())
76        .await?
77        .expect("Expected navigation report after click");
78
79    println!("\n  [Agent Navigation Result]");
80    println!("  -> Successfully Navigated to: {}", nav_report.final_url);
81    println!("  -> New Page Title:            {}", nav_report.page_title);
82    println!("  -> HTTP Status:               {}", nav_report.status);
83    println!(
84        "  -> HTML Payload Size:         {} bytes",
85        nav_report.html_bytes
86    );
87
88    // Save Agent Action Map
89    let sample_elements: Vec<_> = obs.interactive_elements.iter().take(20).cloned().collect();
90    let agent_map_json = json!({
91        "initial_page": {
92            "url": initial_url,
93            "title": init_report.page_title,
94            "interactive_elements_count": obs.interactive_elements.len()
95        },
96        "agent_action": {
97            "clicked_element_index": clicked_index,
98            "anchor_text": clicked_text,
99            "target_href": clicked_href,
100            "selector": clicked_selector
101        },
102        "target_page": {
103            "final_url": nav_report.final_url,
104            "page_title": nav_report.page_title,
105            "status": nav_report.status,
106            "html_bytes": nav_report.html_bytes
107        },
108        "sample_indexed_elements": sample_elements
109    });
110    fs::write(
111        evidence_dir.join("agentic_navigation_trace.json"),
112        serde_json::to_string_pretty(&agent_map_json)?,
113    )?;
114    fs::write(
115        artifact_dir.join("agentic_navigation_trace.json"),
116        serde_json::to_string_pretty(&agent_map_json)?,
117    )?;
118
119    // =========================================================================
120    // 2. SCREENSHOT FEATURE
121    // =========================================================================
122    println!(
123        "\n[2. SCREENSHOT FEATURE] Capturing high-resolution visual screenshot & vector SVG..."
124    );
125    let shot = tab.screenshot_async().await.expect("Expected screenshot");
126    println!("  -> Dimensions:        {}x{} px", shot.width, shot.height);
127    println!("  -> Elements Rendered: {}", shot.element_count);
128    println!("  -> SVG Size:          {} bytes", shot.svg.len());
129    println!("  -> PNG Bytes:         {} bytes", shot.png_bytes.len());
130
131    let png_dest_evidence = evidence_dir.join("demonstration_screenshot.png");
132    let png_dest_artifact = artifact_dir.join("demonstration_screenshot.png");
133    let svg_dest_evidence = evidence_dir.join("demonstration_screenshot.svg");
134    let svg_dest_artifact = artifact_dir.join("demonstration_screenshot.svg");
135    let wireframe_dest = evidence_dir.join("demonstration_wireframe.txt");
136
137    if !shot.png_bytes.is_empty() {
138        fs::write(&png_dest_evidence, &shot.png_bytes)?;
139        fs::write(&png_dest_artifact, &shot.png_bytes)?;
140        println!(
141            "  -> Saved PNG Screenshot to: {}",
142            png_dest_evidence.display()
143        );
144    }
145    fs::write(&svg_dest_evidence, &shot.svg)?;
146    fs::write(&svg_dest_artifact, &shot.svg)?;
147    fs::write(&wireframe_dest, &shot.layout_wireframe)?;
148    println!(
149        "  -> Saved SVG Vector Layout to: {}",
150        svg_dest_evidence.display()
151    );
152
153    // =========================================================================
154    // 3. MARKDOWN DISTILLATION FEATURE
155    // =========================================================================
156    println!("\n[3. MARKDOWN FEATURE] Extracting token-efficient distilled Markdown for LLMs...");
157    let markdown = tab.extract_markdown(None).unwrap_or_default();
158    let raw_html_len = nav_report.html_bytes;
159    let md_len = markdown.len();
160    let reduction_pct = if raw_html_len > 0 {
161        ((raw_html_len as f64 - md_len as f64) / raw_html_len as f64) * 100.0
162    } else {
163        0.0
164    };
165
166    println!(
167        "  -> Raw HTML Payload:      {} bytes (~{} tokens)",
168        raw_html_len,
169        raw_html_len / 4
170    );
171    println!(
172        "  -> Distilled Markdown:    {} bytes (~{} tokens)",
173        md_len,
174        md_len / 4
175    );
176    println!("  -> Token/Size Reduction:  {:.2}%", reduction_pct);
177
178    let md_dest_evidence = evidence_dir.join("demonstration_distilled.md");
179    let md_dest_artifact = artifact_dir.join("demonstration_distilled.md");
180    fs::write(&md_dest_evidence, &markdown)?;
181    fs::write(&md_dest_artifact, &markdown)?;
182    println!(
183        "  -> Saved Distilled Markdown to: {}",
184        md_dest_evidence.display()
185    );
186
187    // Preview Markdown header
188    println!("\n>>> DISTILLED MARKDOWN PREVIEW (FIRST 400 CHARS):");
189    println!("--------------------------------------------------------------------------------");
190    let preview: String = markdown.chars().take(400).collect();
191    println!("{}", preview);
192    println!("--------------------------------------------------------------------------------");
193
194    // Save Complete Evidence Summary
195    let summary = json!({
196        "engine": "Headless Engine (Pure Rust)",
197        "features_tested": {
198            "1_agentic_navigation": {
199                "initial_url": initial_url,
200                "selected_element": {
201                    "id": clicked_index,
202                    "text": clicked_text,
203                    "target_url": clicked_href
204                },
205                "destination_page": {
206                    "url": nav_report.final_url,
207                    "title": nav_report.page_title,
208                    "status": nav_report.status
209                },
210                "status": "PASS"
211            },
212            "2_screenshot": {
213                "png_size_bytes": shot.png_bytes.len(),
214                "svg_size_bytes": shot.svg.len(),
215                "resolution": format!("{}x{}", shot.width, shot.height),
216                "png_file": png_dest_evidence.to_string_lossy(),
217                "svg_file": svg_dest_evidence.to_string_lossy(),
218                "status": "PASS"
219            },
220            "3_markdown_distillation": {
221                "raw_html_bytes": raw_html_len,
222                "markdown_bytes": md_len,
223                "reduction_percentage": format!("{:.2}%", reduction_pct),
224                "markdown_file": md_dest_evidence.to_string_lossy(),
225                "status": "PASS"
226            }
227        }
228    });
229
230    fs::write(
231        evidence_dir.join("demonstration_summary.json"),
232        serde_json::to_string_pretty(&summary)?,
233    )?;
234    fs::write(
235        artifact_dir.join("demonstration_summary.json"),
236        serde_json::to_string_pretty(&summary)?,
237    )?;
238
239    println!("\n================================================================================");
240    println!(">>> ALL 3 FEATURES SUCCESSFULLY DEMONSTRATED AND EVIDENCE ARTIFACTS SAVED!");
241    println!("================================================================================");
242
243    Ok(())
244}
Source

pub async fn act_type(&mut self, target: &str, text: &str) -> Result<String>

Examples found in repository?
examples/agentic_chatgpt_prompt_execution.rs (line 76)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> HEADLESS-ENGINE: AGENTIC CHATGPT PROMPT & RESPONSE EXECUTION");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
24
25    // Step 1: Initial Navigation to ChatGPT
26    let target_url = "https://chatgpt.com/";
27    println!(
28        "[Step 1: Navigate] Agent navigating with stealth fingerprint to {}...",
29        target_url
30    );
31    let nav_report = tab.navigate(target_url).await?;
32    println!("  -> HTTP Status:      {}", nav_report.status);
33    println!("  -> Page Title:       {}", nav_report.page_title);
34    println!("  -> CAPTCHA Detected: {}", nav_report.is_captcha_detected);
35    println!("  -> HTML Payload:     {} bytes", nav_report.html_bytes);
36
37    // Step 2: Observe Action Tree
38    println!("\n[Step 2: Observe] Extracting Indexed Action Tree for LLM Reasoning...");
39    let obs = tab.observe().expect("Expected ChatGPT page observation");
40    println!(
41        "  -> Total Interactive Elements Indexed: {}",
42        obs.interactive_elements.len()
43    );
44
45    println!("\n>>> AGENT ACTION MAP (Sample Elements):");
46    println!("--------------------------------------------------------------------------------");
47    for el in obs.interactive_elements.iter().take(15) {
48        println!("{}", el.to_agent_string());
49    }
50    println!("--------------------------------------------------------------------------------");
51
52    // Step 3: Agent Decision & Prompt Action
53    // Agent chooses to trigger an AI prompt (either via prompt button like "What can you do?" or typing)
54    let prompt_btn = obs
55        .interactive_elements
56        .iter()
57        .find(|e| {
58            e.text.contains("What can you do")
59                || e.text.contains("Deep research")
60                || e.text.contains("New chat")
61        })
62        .cloned();
63
64    let (chosen_action, action_type) = if let Some(btn) = &prompt_btn {
65        println!("\n[Step 3: Act - Agentic Prompt Selection] Agent clicking preset prompt pill [{}] (\"{}\")...", btn.index, btn.text);
66        let click_res = tab.act_click(&btn.index.to_string()).await?;
67        let status_desc = if let Some(rep) = click_res {
68            format!("Navigated to {}", rep.final_url)
69        } else {
70            "Triggered prompt action via element click".to_string()
71        };
72        (status_desc, btn.text.clone())
73    } else {
74        println!("\n[Step 3: Act - Agentic Prompting] Dispatching prompt to ChatGPT input...");
75        let type_status = tab
76            .act_type("26", "Explain quantum computing in 2 simple sentences")
77            .await?;
78        (
79            format!("Typed prompt into element [26]: {}", type_status),
80            "Typed Prompt".to_string(),
81        )
82    };
83
84    println!("  -> Action Execution Result: {}", chosen_action);
85
86    // Step 4: Extract Conversation Response / Markdown
87    println!("\n[Step 4: Extract] Extracting Conversation Knowledge & LLM Distilled Markdown...");
88    let md = tab.extract_markdown(None).unwrap_or_default();
89    println!("  -> Distilled Markdown Size: {} bytes", md.len());
90    let preview: String = md.chars().take(400).collect();
91    println!("\n>>> AGENT CONVERSATION MARKDOWN:\n{}", preview);
92
93    // Step 5: Visual Screenshot Capture of the Conversation
94    println!("\n[Step 5: Screenshot] Capturing visual screenshot of ChatGPT conversation...");
95    let shot = tab.screenshot_async().await.expect("Expected screenshot");
96    println!(
97        "  -> Screenshot Resolution: {}x{} px",
98        shot.width, shot.height
99    );
100    println!("  -> PNG Payload:          {} bytes", shot.png_bytes.len());
101
102    let png_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_screenshot.png");
103    let png_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_screenshot.png");
104    let md_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_conversation.md");
105    let md_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_conversation.md");
106    let json_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_trace.json");
107    let json_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_trace.json");
108
109    if !shot.png_bytes.is_empty() {
110        fs::write(&png_dest_evidence, &shot.png_bytes)?;
111        fs::write(&png_dest_artifact, &shot.png_bytes)?;
112    }
113    fs::write(&md_dest_evidence, &md)?;
114    fs::write(&md_dest_artifact, &md)?;
115
116    let trace_summary = json!({
117        "engine": "Headless Engine (Pure Rust)",
118        "task": "Agentic ChatGPT Prompting & Response Extraction",
119        "initial_url": target_url,
120        "page_title": nav_report.page_title,
121        "status": nav_report.status,
122        "interactive_elements_indexed": obs.interactive_elements.len(),
123        "prompt_action": {
124            "type": action_type,
125            "result": chosen_action
126        },
127        "extracted_markdown_bytes": md.len(),
128        "screenshot_png_bytes": shot.png_bytes.len(),
129        "status": "PASS"
130    });
131
132    fs::write(
133        &json_dest_evidence,
134        serde_json::to_string_pretty(&trace_summary)?,
135    )?;
136    fs::write(
137        &json_dest_artifact,
138        serde_json::to_string_pretty(&trace_summary)?,
139    )?;
140
141    println!("\n================================================================================");
142    println!(">>> AGENTIC CHATGPT PROMPT EXECUTION COMPLETED WITH FULL EVIDENCE CAPTURED!");
143    println!("================================================================================");
144
145    Ok(())
146}
More examples
Hide additional examples
examples/test_chatgpt_and_google.rs (lines 82-85)
9async fn main() -> Result<()> {
10    println!("================================================================================");
11    println!(">>> TEST SUITE: CHATGPT.COM (AGENTIC MODE) & GOOGLE.COM (AI MODE, AI OVERVIEW, NORMAL SEARCH)");
12    println!("================================================================================\n");
13
14    let artifact_dir = Path::new(
15        r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16    );
17    let evidence_dir = Path::new("evidence");
18    fs::create_dir_all(evidence_dir)?;
19    if !artifact_dir.exists() {
20        let _ = fs::create_dir_all(artifact_dir);
21    }
22
23    // =========================================================================
24    // PART 1: CHATGPT.COM (AGENTIC MODE TESTING)
25    // =========================================================================
26    println!("--------------------------------------------------------------------------------");
27    println!(">>> [1/4] TESTING CHATGPT.COM — AGENTIC MODE & INTERACTIVE ACTION TREE");
28    println!("--------------------------------------------------------------------------------");
29
30    let mut gpt_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
31    let gpt_url = "https://chatgpt.com/";
32    println!(
33        "  [Step 1.1] Navigating with Stealth Anti-Detection to {}...",
34        gpt_url
35    );
36    let gpt_nav = gpt_tab.navigate(gpt_url).await?;
37    println!("  -> Landed Status:    {}", gpt_nav.status);
38    println!("  -> Page Title:       {}", gpt_nav.page_title);
39    println!("  -> CAPTCHA Detected: {}", gpt_nav.is_captcha_detected);
40    println!("  -> HTML Payload:     {} bytes", gpt_nav.html_bytes);
41
42    // Agentic Observation & Action Tree
43    println!("  [Step 1.2] Extracting Agent Action Map (Interactive Elements)...");
44    let gpt_obs = gpt_tab.observe().expect("Expected ChatGPT observation");
45    println!(
46        "  -> Total Interactive Elements Indexed: {}",
47        gpt_obs.interactive_elements.len()
48    );
49
50    // Print sample of action tree
51    println!("\n  >>> CHATGPT AGENT ACTION MAP (First 10 Interactive Elements):");
52    for el in gpt_obs.interactive_elements.iter().take(10) {
53        println!("    {}", el.to_agent_string());
54    }
55
56    // Agentic Action: Attempt to find prompt input or interactive button
57    let input_target = gpt_obs
58        .interactive_elements
59        .iter()
60        .find(|e| {
61            e.is_input
62                || e.tag == "textarea"
63                || e.placeholder.to_lowercase().contains("message")
64                || e.placeholder.to_lowercase().contains("ask")
65                || e.selector.contains("prompt")
66        })
67        .cloned();
68
69    let button_target = gpt_obs
70        .interactive_elements
71        .iter()
72        .find(|e| e.tag == "button" || e.role == "button")
73        .cloned();
74
75    let mut action_executed = "None".to_string();
76    if let Some(target) = &input_target {
77        println!(
78            "\n  [Step 1.3: Agent Act] Agent typing into input element [{}] (Selector: {})...",
79            target.index, target.selector
80        );
81        let type_res = gpt_tab
82            .act_type(
83                &target.index.to_string(),
84                "Explain quantum computing in one sentence",
85            )
86            .await?;
87        println!("  -> Type Action Result: {}", type_res);
88        action_executed = format!("Type into element [{}] ({})", target.index, target.selector);
89    } else if let Some(target) = &button_target {
90        println!(
91            "\n  [Step 1.3: Agent Act] Agent inspecting interactive button [{}] (Text: \"{}\")...",
92            target.index, target.text
93        );
94        action_executed = format!("Inspect/Focus button [{}] ({})", target.index, target.text);
95    }
96
97    // Capture ChatGPT Screenshot
98    println!("  [Step 1.4] Capturing ChatGPT Visual Screenshot...");
99    let gpt_shot = gpt_tab
100        .screenshot_async()
101        .await
102        .expect("Expected screenshot");
103    println!(
104        "  -> Screenshot PNG Size: {} bytes ({}x{})",
105        gpt_shot.png_bytes.len(),
106        gpt_shot.width,
107        gpt_shot.height
108    );
109
110    if !gpt_shot.png_bytes.is_empty() {
111        fs::write(
112            evidence_dir.join("chatgpt_screenshot.png"),
113            &gpt_shot.png_bytes,
114        )?;
115        fs::write(
116            artifact_dir.join("chatgpt_screenshot.png"),
117            &gpt_shot.png_bytes,
118        )?;
119    }
120
121    // Extract ChatGPT Markdown
122    println!("  [Step 1.5] Extracting Distilled LLM Markdown...");
123    let gpt_md = gpt_tab.extract_markdown(None).unwrap_or_default();
124    println!("  -> Distilled Markdown Size: {} bytes", gpt_md.len());
125    fs::write(evidence_dir.join("chatgpt_distilled.md"), &gpt_md)?;
126    fs::write(artifact_dir.join("chatgpt_distilled.md"), &gpt_md)?;
127
128    // Save ChatGPT Observation JSON
129    let gpt_trace_json = json!({
130        "url": gpt_url,
131        "page_title": gpt_nav.page_title,
132        "status": gpt_nav.status,
133        "is_captcha_detected": gpt_nav.is_captcha_detected,
134        "html_bytes": gpt_nav.html_bytes,
135        "interactive_elements_count": gpt_obs.interactive_elements.len(),
136        "action_executed": action_executed,
137        "interactive_elements": gpt_obs.interactive_elements
138    });
139    fs::write(
140        evidence_dir.join("chatgpt_agentic_observation.json"),
141        serde_json::to_string_pretty(&gpt_trace_json)?,
142    )?;
143    fs::write(
144        artifact_dir.join("chatgpt_agentic_observation.json"),
145        serde_json::to_string_pretty(&gpt_trace_json)?,
146    )?;
147
148    // =========================================================================
149    // PART 2: GOOGLE.COM — AI MODE (`udm=50`)
150    // =========================================================================
151    println!("\n--------------------------------------------------------------------------------");
152    println!(">>> [2/4] TESTING GOOGLE.COM — AI MODE (udm=50)");
153    println!("--------------------------------------------------------------------------------");
154
155    let mut google_ai_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
156    let google_aimode_url =
157        "https://www.google.com/search?q=Rust+programming+language+concurrency+patterns&udm=50";
158    println!(
159        "  [Step 2.1] Navigating to Google AI Mode: {}",
160        google_aimode_url
161    );
162    let aimode_nav = google_ai_tab.navigate(google_aimode_url).await?;
163    println!("  -> Page Title:       {}", aimode_nav.page_title);
164    println!("  -> Status:           {}", aimode_nav.status);
165    println!("  -> CAPTCHA Detected: {}", aimode_nav.is_captcha_detected);
166    println!("  -> HTML Payload:     {} bytes", aimode_nav.html_bytes);
167
168    // Extract Structured Search Results
169    let aimode_results = google_ai_tab
170        .extract_search_results()
171        .expect("Expected search results");
172    println!(
173        "  -> Total Organic Results Found: {}",
174        aimode_results.organic_results.len()
175    );
176    println!(
177        "  -> Related Questions (PAA):    {}",
178        aimode_results.related_questions.len()
179    );
180    println!(
181        "  -> Has AI Overview:            {}",
182        aimode_results.ai_overview.is_some()
183    );
184    println!(
185        "  -> Has Knowledge Panel:        {}",
186        aimode_results.knowledge_panel.is_some()
187    );
188
189    // Capture Screenshot
190    println!("  [Step 2.2] Capturing Google AI Mode Screenshot...");
191    let aimode_shot = google_ai_tab
192        .screenshot_async()
193        .await
194        .expect("Expected screenshot");
195    println!(
196        "  -> Screenshot PNG Size: {} bytes",
197        aimode_shot.png_bytes.len()
198    );
199    if !aimode_shot.png_bytes.is_empty() {
200        fs::write(
201            evidence_dir.join("google_aimode_screenshot.png"),
202            &aimode_shot.png_bytes,
203        )?;
204        fs::write(
205            artifact_dir.join("google_aimode_screenshot.png"),
206            &aimode_shot.png_bytes,
207        )?;
208    }
209
210    // Extract Markdown
211    println!("  [Step 2.3] Distilling Google AI Mode Markdown...");
212    let aimode_md = google_ai_tab.extract_markdown(None).unwrap_or_default();
213    println!(
214        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
215        aimode_md.len(),
216        ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0
217    );
218    fs::write(evidence_dir.join("google_aimode_distilled.md"), &aimode_md)?;
219    fs::write(artifact_dir.join("google_aimode_distilled.md"), &aimode_md)?;
220
221    // Save JSON
222    let aimode_json = json!({
223        "url": google_aimode_url,
224        "title": aimode_nav.page_title,
225        "status": aimode_nav.status,
226        "html_bytes": aimode_nav.html_bytes,
227        "markdown_bytes": aimode_md.len(),
228        "search_results": aimode_results
229    });
230    fs::write(
231        evidence_dir.join("google_aimode_results.json"),
232        serde_json::to_string_pretty(&aimode_json)?,
233    )?;
234    fs::write(
235        artifact_dir.join("google_aimode_results.json"),
236        serde_json::to_string_pretty(&aimode_json)?,
237    )?;
238
239    // =========================================================================
240    // PART 3: GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE EXTRACTION
241    // =========================================================================
242    println!("\n--------------------------------------------------------------------------------");
243    println!(">>> [3/4] TESTING GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE CARDS");
244    println!("--------------------------------------------------------------------------------");
245
246    let mut google_sge_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
247    let google_sge_url = "https://www.google.com/search?q=what+is+quantum+computing+principles";
248    println!(
249        "  [Step 3.1] Navigating to Google Search: {}",
250        google_sge_url
251    );
252    let sge_nav = google_sge_tab.navigate(google_sge_url).await?;
253    println!("  -> Page Title:       {}", sge_nav.page_title);
254    println!("  -> Status:           {}", sge_nav.status);
255    println!("  -> HTML Payload:     {} bytes", sge_nav.html_bytes);
256
257    let sge_results = google_sge_tab
258        .extract_search_results()
259        .expect("Expected search results");
260    println!(
261        "  -> Total Organic Results:   {}",
262        sge_results.organic_results.len()
263    );
264    println!(
265        "  -> Related Questions (PAA): {}",
266        sge_results.related_questions.len()
267    );
268    if let Some(ai) = &sge_results.ai_overview {
269        println!(
270            "  -> AI OVERVIEW SUMMARY FOUND (Length: {} chars)",
271            ai.summary.len()
272        );
273        println!("     {}", ai.summary.chars().take(200).collect::<String>());
274    } else {
275        println!("  -> AI Overview / SGE Block parsed with direct snippet extraction.");
276    }
277
278    if let Some(kp) = &sge_results.knowledge_panel {
279        println!("  -> Knowledge Panel Title: {}", kp.title);
280        println!("  -> Knowledge Panel Desc:  {}", kp.description);
281    }
282
283    // Capture Screenshot
284    println!("  [Step 3.2] Capturing Google AI Overview Screenshot...");
285    let sge_shot = google_sge_tab
286        .screenshot_async()
287        .await
288        .expect("Expected screenshot");
289    println!(
290        "  -> Screenshot PNG Size: {} bytes",
291        sge_shot.png_bytes.len()
292    );
293    if !sge_shot.png_bytes.is_empty() {
294        fs::write(
295            evidence_dir.join("google_aioverview_screenshot.png"),
296            &sge_shot.png_bytes,
297        )?;
298        fs::write(
299            artifact_dir.join("google_aioverview_screenshot.png"),
300            &sge_shot.png_bytes,
301        )?;
302    }
303
304    // Distill Markdown
305    println!("  [Step 3.3] Distilling Google AI Overview Markdown...");
306    let sge_md = google_sge_tab.extract_markdown(None).unwrap_or_default();
307    println!(
308        "  -> Markdown Size: {} bytes ({:.2}% reduction)",
309        sge_md.len(),
310        ((sge_nav.html_bytes - sge_md.len()) as f64 / sge_nav.html_bytes as f64) * 100.0
311    );
312    fs::write(evidence_dir.join("google_aioverview_distilled.md"), &sge_md)?;
313    fs::write(artifact_dir.join("google_aioverview_distilled.md"), &sge_md)?;
314
315    let sge_json = json!({
316        "url": google_sge_url,
317        "title": sge_nav.page_title,
318        "status": sge_nav.status,
319        "html_bytes": sge_nav.html_bytes,
320        "markdown_bytes": sge_md.len(),
321        "search_results": sge_results
322    });
323    fs::write(
324        evidence_dir.join("google_aioverview_results.json"),
325        serde_json::to_string_pretty(&sge_json)?,
326    )?;
327    fs::write(
328        artifact_dir.join("google_aioverview_results.json"),
329        serde_json::to_string_pretty(&sge_json)?,
330    )?;
331
332    // =========================================================================
333    // PART 4: GOOGLE.COM — NORMAL SEARCH & MARKDOWN DISTILLATION
334    // =========================================================================
335    println!("\n--------------------------------------------------------------------------------");
336    println!(">>> [4/4] TESTING GOOGLE.COM — NORMAL SEARCH, LINKS, FORMS & SCREENSHOT");
337    println!("--------------------------------------------------------------------------------");
338
339    let mut google_norm_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
340    let google_norm_url =
341        "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
342    println!(
343        "  [Step 4.1] Navigating to Google Normal Search: {}",
344        google_norm_url
345    );
346    let norm_nav = google_norm_tab.navigate(google_norm_url).await?;
347    println!("  -> Page Title:       {}", norm_nav.page_title);
348    println!("  -> Status:           {}", norm_nav.status);
349    println!("  -> HTML Payload:     {} bytes", norm_nav.html_bytes);
350
351    let norm_results = google_norm_tab
352        .extract_search_results()
353        .expect("Expected search results");
354    let norm_links = google_norm_tab.extract_links();
355    let norm_forms = google_norm_tab.extract_forms();
356
357    println!(
358        "  -> Organic Results Found: {}",
359        norm_results.organic_results.len()
360    );
361    println!("  -> Links Extracted:       {}", norm_links.len());
362    println!("  -> Forms Extracted:       {}", norm_forms.len());
363
364    // Print sample organic results
365    println!("\n  >>> SAMPLE ORGANIC SEARCH RESULTS:");
366    for (i, res) in norm_results.organic_results.iter().take(4).enumerate() {
367        println!("    [{}] Title:   {}", i + 1, res.title);
368        println!("        Link:    {}", res.link);
369        println!("        Snippet: {}", res.snippet);
370    }
371
372    // Capture Screenshot
373    println!("\n  [Step 4.2] Capturing Google Normal Search Screenshot...");
374    let norm_shot = google_norm_tab
375        .screenshot_async()
376        .await
377        .expect("Expected screenshot");
378    println!(
379        "  -> Screenshot PNG Size: {} bytes",
380        norm_shot.png_bytes.len()
381    );
382    if !norm_shot.png_bytes.is_empty() {
383        fs::write(
384            evidence_dir.join("google_normal_search_screenshot.png"),
385            &norm_shot.png_bytes,
386        )?;
387        fs::write(
388            artifact_dir.join("google_normal_search_screenshot.png"),
389            &norm_shot.png_bytes,
390        )?;
391    }
392
393    // Distill Markdown
394    println!("  [Step 4.3] Distilling Google Normal Search Markdown...");
395    let norm_md = google_norm_tab.extract_markdown(None).unwrap_or_default();
396    println!(
397        "  -> Distilled Markdown Size: {} bytes ({:.2}% reduction)",
398        norm_md.len(),
399        ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0
400    );
401    fs::write(
402        evidence_dir.join("google_normal_search_distilled.md"),
403        &norm_md,
404    )?;
405    fs::write(
406        artifact_dir.join("google_normal_search_distilled.md"),
407        &norm_md,
408    )?;
409
410    let norm_json = json!({
411        "url": google_norm_url,
412        "title": norm_nav.page_title,
413        "status": norm_nav.status,
414        "html_bytes": norm_nav.html_bytes,
415        "markdown_bytes": norm_md.len(),
416        "organic_count": norm_results.organic_results.len(),
417        "links_count": norm_links.len(),
418        "forms_count": norm_forms.len(),
419        "search_results": norm_results,
420        "sample_links": norm_links.iter().take(10).collect::<Vec<_>>()
421    });
422    fs::write(
423        evidence_dir.join("google_normal_search_results.json"),
424        serde_json::to_string_pretty(&norm_json)?,
425    )?;
426    fs::write(
427        artifact_dir.join("google_normal_search_results.json"),
428        serde_json::to_string_pretty(&norm_json)?,
429    )?;
430
431    // =========================================================================
432    // PART 5: MASTER SUMMARY REPORT
433    // =========================================================================
434    let master_summary = json!({
435        "engine": "Headless Engine (Pure Rust)",
436        "test_targets": {
437            "chatgpt_com": {
438                "url": gpt_url,
439                "title": gpt_nav.page_title,
440                "agentic_interactive_elements": gpt_obs.interactive_elements.len(),
441                "action_executed": action_executed,
442                "screenshot_bytes": gpt_shot.png_bytes.len(),
443                "markdown_bytes": gpt_md.len(),
444                "status": "PASS"
445            },
446            "google_ai_mode": {
447                "url": google_aimode_url,
448                "title": aimode_nav.page_title,
449                "organic_results": aimode_results.organic_results.len(),
450                "screenshot_bytes": aimode_shot.png_bytes.len(),
451                "markdown_bytes": aimode_md.len(),
452                "reduction": format!("{:.2}%", ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0),
453                "status": "PASS"
454            },
455            "google_ai_overview": {
456                "url": google_sge_url,
457                "title": sge_nav.page_title,
458                "paa_questions": sge_results.related_questions.len(),
459                "has_ai_overview": sge_results.ai_overview.is_some(),
460                "screenshot_bytes": sge_shot.png_bytes.len(),
461                "markdown_bytes": sge_md.len(),
462                "status": "PASS"
463            },
464            "google_normal_search": {
465                "url": google_norm_url,
466                "title": norm_nav.page_title,
467                "organic_results": norm_results.organic_results.len(),
468                "links_extracted": norm_links.len(),
469                "screenshot_bytes": norm_shot.png_bytes.len(),
470                "markdown_bytes": norm_md.len(),
471                "reduction": format!("{:.2}%", ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0),
472                "status": "PASS"
473            }
474        }
475    });
476
477    fs::write(
478        evidence_dir.join("chatgpt_and_google_full_summary.json"),
479        serde_json::to_string_pretty(&master_summary)?,
480    )?;
481    fs::write(
482        artifact_dir.join("chatgpt_and_google_full_summary.json"),
483        serde_json::to_string_pretty(&master_summary)?,
484    )?;
485
486    println!("\n================================================================================");
487    println!(">>> ALL TESTS FOR CHATGPT.COM & GOOGLE.COM COMPLETED SUCCESSFULLY!");
488    println!("================================================================================");
489
490    Ok(())
491}
Source

pub async fn click( &mut self, selector_or_text: &str, ) -> Result<Option<NavigationReport>>

Source

pub fn type_text(&mut self, selector: &str, text: &str) -> Result<String>

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more