use crate::browser::Browser;
use crate::error::RenderError;
use url::Url;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Viewport {
pub name: &'static str,
pub width: u32,
pub height: u32,
}
pub const MOBILE: Viewport = Viewport {
name: "mobile",
width: 390,
height: 844,
};
pub const TABLET: Viewport = Viewport {
name: "tablet",
width: 768,
height: 1024,
};
pub const DESKTOP: Viewport = Viewport {
name: "desktop",
width: 1440,
height: 900,
};
#[derive(Debug, Clone)]
pub struct Screenshot {
pub viewport: Viewport,
pub png_bytes: Vec<u8>,
}
pub async fn capture_viewports(
browser: &Browser,
url: &Url,
viewports: &[Viewport],
) -> Result<Vec<Screenshot>, RenderError> {
let inner_browser = browser.inner();
let mut screenshots = Vec::new();
let page = inner_browser
.new_page(url.as_str())
.await
.map_err(|e| RenderError::Screenshot {
url: url.to_string(),
message: format!("Failed to open new page: {e}"),
})?;
tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
for vp in viewports {
use chromiumoxide::cdp::browser_protocol::emulation::SetDeviceMetricsOverrideParams;
page.execute(SetDeviceMetricsOverrideParams::new(
vp.width as i64,
vp.height as i64,
1.0,
vp.name == "mobile",
))
.await
.map_err(|e| RenderError::Screenshot {
url: url.to_string(),
message: format!("Failed to set viewport to {}x{}: {e}", vp.width, vp.height),
})?;
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
let png_bytes = page
.screenshot(
chromiumoxide::page::ScreenshotParams::builder()
.format(
chromiumoxide::cdp::browser_protocol::page::CaptureScreenshotFormat::Png,
)
.full_page(true)
.build(),
)
.await
.map_err(|e| RenderError::Screenshot {
url: url.to_string(),
message: format!("Failed to capture screenshot for viewport {}: {e}", vp.name),
})?;
screenshots.push(Screenshot {
viewport: *vp,
png_bytes,
});
}
let _ = page.close().await;
Ok(screenshots)
}