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 14)
8async fn main() -> Result<()> {
9    println!(">>> Testing Google Search Distilled Markdown Extraction...");
10
11    let artifact_dir = Path::new(r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f");
12    let evidence_dir = Path::new("evidence");
13
14    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
15    let url = "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
16    println!("  -> Navigating to: {}", url);
17    let nav = tab.navigate(url).await?;
18
19    println!("  -> Status:       {}", nav.status);
20    println!("  -> Final URL:    {}", nav.final_url);
21    println!("  -> Page Title:   {}", nav.page_title);
22    println!("  -> HTML Payload: {} bytes", nav.html_bytes);
23
24    let md = tab.extract_markdown(None).unwrap_or_default();
25    println!("  -> Extracted Markdown Size: {} bytes", md.len());
26
27    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
28    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
29
30    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n");
31    println!("{}", md);
32
33    Ok(())
34}
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.google_search("headless browser pure rust engine").await?;
18    fs::write(evidence_dir.join("test_google_search.md"), res.to_markdown())?;
19
20    println!("2. Testing youtube_search...");
21    let res = tab.youtube_search("rust programming").await?;
22    fs::write(evidence_dir.join("test_youtube_search.md"), res.to_markdown())?;
23    
24    println!("3. Testing google_ai_mode...");
25    let res = tab.google_ai_mode("explain quantum computing").await?;
26    fs::write(evidence_dir.join("test_google_ai_mode.md"), res.to_markdown())?;
27
28    println!("4. Testing google_autocomplete...");
29    let res = tab.google_autocomplete("rust").await?;
30    fs::write(evidence_dir.join("test_google_autocomplete.md"), res.to_markdown())?;
31
32    println!("Capabilities listed:");
33    for cap in tab.google_capabilities() {
34        println!(" - {}", cap);
35    }
36
37    println!("All tests completed. Output saved to evidence/");
38    Ok(())
39}
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 17)
8async fn main() -> Result<()> {
9    println!(">>> Testing Google Search Distilled Markdown Extraction...");
10
11    let artifact_dir = Path::new(r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f");
12    let evidence_dir = Path::new("evidence");
13
14    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
15    let url = "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
16    println!("  -> Navigating to: {}", url);
17    let nav = tab.navigate(url).await?;
18
19    println!("  -> Status:       {}", nav.status);
20    println!("  -> Final URL:    {}", nav.final_url);
21    println!("  -> Page Title:   {}", nav.page_title);
22    println!("  -> HTML Payload: {} bytes", nav.html_bytes);
23
24    let md = tab.extract_markdown(None).unwrap_or_default();
25    println!("  -> Extracted Markdown Size: {} bytes", md.len());
26
27    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
28    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
29
30    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n");
31    println!("{}", md);
32
33    Ok(())
34}
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 21)
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(r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f");
13    let evidence_dir = Path::new("evidence");
14    fs::create_dir_all(evidence_dir)?;
15
16    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
17
18    // 1. Default Google Search
19    let query = "headless browser pure rust engine for AI agents";
20    println!("[1/2] Executing default search: tab.search(\"{}\")...", query);
21    let nav = tab.search(query).await?;
22    println!("  -> Final URL:     {}", nav.final_url);
23    println!("  -> Page Title:    {}", nav.page_title);
24    println!("  -> Status:        {}", nav.status);
25    println!("  -> HTML Payload:  {} bytes", nav.html_bytes);
26
27    let md = tab.extract_markdown(None).unwrap_or_default();
28    println!("  -> Distilled Markdown Size: {} bytes", md.len());
29
30    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
31    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
32
33    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n--------------------------------------------------------------------------------");
34    println!("{}", md);
35    println!("--------------------------------------------------------------------------------");
36
37    // 2. Google AI Mode Search
38    let ai_query = "Rust programming language concurrency patterns";
39    println!("\n[2/2] Executing Google AI Mode search: tab.search_google(\"{}\", Some(\"ai\"))...", ai_query);
40    let ai_nav = tab.search_google(ai_query, Some("ai")).await?;
41    println!("  -> Final URL:     {}", ai_nav.final_url);
42    println!("  -> Page Title:    {}", ai_nav.page_title);
43    println!("  -> HTML Payload:  {} bytes", ai_nav.html_bytes);
44
45    let ai_md = tab.extract_markdown(None).unwrap_or_default();
46    println!("  -> Distilled Markdown Size: {} bytes", ai_md.len());
47
48    fs::write(evidence_dir.join("google_aimode_distilled.md"), &ai_md)?;
49    fs::write(artifact_dir.join("google_aimode_distilled.md"), &ai_md)?;
50
51    println!("\n>>> DISTILLED GOOGLE AI MODE MARKDOWN:\n--------------------------------------------------------------------------------");
52    println!("{}", ai_md);
53    println!("--------------------------------------------------------------------------------");
54
55    println!("\n================================================================================");
56    println!(">>> GOOGLE DEFAULT SEARCH ENGINE VERIFICATION COMPLETE!");
57    println!("================================================================================");
58
59    Ok(())
60}
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 40)
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(r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f");
13    let evidence_dir = Path::new("evidence");
14    fs::create_dir_all(evidence_dir)?;
15
16    let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
17
18    // 1. Default Google Search
19    let query = "headless browser pure rust engine for AI agents";
20    println!("[1/2] Executing default search: tab.search(\"{}\")...", query);
21    let nav = tab.search(query).await?;
22    println!("  -> Final URL:     {}", nav.final_url);
23    println!("  -> Page Title:    {}", nav.page_title);
24    println!("  -> Status:        {}", nav.status);
25    println!("  -> HTML Payload:  {} bytes", nav.html_bytes);
26
27    let md = tab.extract_markdown(None).unwrap_or_default();
28    println!("  -> Distilled Markdown Size: {} bytes", md.len());
29
30    fs::write(evidence_dir.join("google_normal_search_distilled.md"), &md)?;
31    fs::write(artifact_dir.join("google_normal_search_distilled.md"), &md)?;
32
33    println!("\n>>> DISTILLED GOOGLE SEARCH MARKDOWN:\n--------------------------------------------------------------------------------");
34    println!("{}", md);
35    println!("--------------------------------------------------------------------------------");
36
37    // 2. Google AI Mode Search
38    let ai_query = "Rust programming language concurrency patterns";
39    println!("\n[2/2] Executing Google AI Mode search: tab.search_google(\"{}\", Some(\"ai\"))...", ai_query);
40    let ai_nav = tab.search_google(ai_query, Some("ai")).await?;
41    println!("  -> Final URL:     {}", ai_nav.final_url);
42    println!("  -> Page Title:    {}", ai_nav.page_title);
43    println!("  -> HTML Payload:  {} bytes", ai_nav.html_bytes);
44
45    let ai_md = tab.extract_markdown(None).unwrap_or_default();
46    println!("  -> Distilled Markdown Size: {} bytes", ai_md.len());
47
48    fs::write(evidence_dir.join("google_aimode_distilled.md"), &ai_md)?;
49    fs::write(artifact_dir.join("google_aimode_distilled.md"), &ai_md)?;
50
51    println!("\n>>> DISTILLED GOOGLE AI MODE MARKDOWN:\n--------------------------------------------------------------------------------");
52    println!("{}", ai_md);
53    println!("--------------------------------------------------------------------------------");
54
55    println!("\n================================================================================");
56    println!(">>> GOOGLE DEFAULT SEARCH ENGINE VERIFICATION COMPLETE!");
57    println!("================================================================================");
58
59    Ok(())
60}
Examples found in repository?
examples/test_all_31_google_capabilities.rs (line 17)
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.google_search("headless browser pure rust engine").await?;
18    fs::write(evidence_dir.join("test_google_search.md"), res.to_markdown())?;
19
20    println!("2. Testing youtube_search...");
21    let res = tab.youtube_search("rust programming").await?;
22    fs::write(evidence_dir.join("test_youtube_search.md"), res.to_markdown())?;
23    
24    println!("3. Testing google_ai_mode...");
25    let res = tab.google_ai_mode("explain quantum computing").await?;
26    fs::write(evidence_dir.join("test_google_ai_mode.md"), res.to_markdown())?;
27
28    println!("4. Testing google_autocomplete...");
29    let res = tab.google_autocomplete("rust").await?;
30    fs::write(evidence_dir.join("test_google_autocomplete.md"), res.to_markdown())?;
31
32    println!("Capabilities listed:");
33    for cap in tab.google_capabilities() {
34        println!(" - {}", cap);
35    }
36
37    println!("All tests completed. Output saved to evidence/");
38    Ok(())
39}
Source

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

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

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

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

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

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