use std::collections::{HashMap, HashSet};
use serde_json::json;
use crate::cdp::client::CdpClient;
use crate::element_ref::ElementRef;
use crate::snapshot::Snapshot;
pub async fn run(
client: &CdpClient,
verbose: bool,
max_depth: Option<usize>,
focus_uid: Option<&str>,
role_filter: Option<&[&str]>,
) -> Result<Snapshot, crate::BoxError> {
let snapshot = crate::snapshot::take_snapshot(client, verbose, max_depth, focus_uid, role_filter).await?;
Ok(snapshot)
}
pub async fn scroll_collect(
client: &CdpClient,
verbose: bool,
focus_uid: Option<&str>,
role_filter: Option<&[&str]>,
limit: usize,
) -> Result<Snapshot, crate::BoxError> {
let mut collected: Vec<String> = Vec::new();
let mut seen = HashSet::new();
let mut uid_map: HashMap<String, ElementRef> = HashMap::new();
let max_scrolls = limit * 3;
let mut stale_count = 0;
for _ in 0..max_scrolls {
let snapshot = crate::snapshot::take_snapshot(client, verbose, None, focus_uid, role_filter).await?;
let prev_len = collected.len();
for line in snapshot.text.lines() {
let trimmed = line.trim();
if !trimmed.is_empty() && seen.insert(trimmed.to_string()) {
collected.push(trimmed.to_string());
}
}
uid_map.extend(snapshot.uid_map);
if collected.len() >= limit { break; }
if collected.len() == prev_len {
stale_count += 1;
if stale_count >= 3 { break; }
} else {
stale_count = 0;
}
let _ = client.call::<_, serde_json::Value>(
"Runtime.evaluate",
json!({
"expression": r"(async () => {
window.scrollBy(0, window.innerHeight);
await new Promise(resolve => {
let timer = setTimeout(resolve, 2000);
const obs = new MutationObserver(() => {
clearTimeout(timer);
timer = setTimeout(() => { obs.disconnect(); resolve(); }, 400);
});
obs.observe(document.body || document.documentElement, { childList: true, subtree: true });
});
})()",
"awaitPromise": true,
"returnByValue": true,
}),
).await;
}
collected.truncate(limit);
let text = format!("{}\n({} items collected)", collected.join("\n"), collected.len());
Ok(Snapshot { text, uid_map })
}