refactor: remove screenshot functionality and headless_chrome dependency

Remove the screenshot capture system and associated UI components to simplify the codebase.

- Remove screenshot.rs command module and related functionality
- Remove headless_chrome dependency from Cargo.toml
- Update Cargo.lock to reflect dependency changes
- Remove screenshot handlers and UI from WebviewPreview component
- Clean up screenshot-related API calls and imports
- Remove camera icon and capture controls from preview interface

This reduces bundle size and eliminates an underutilized feature.
This commit is contained in:
Mufeed VH
2025-07-02 20:29:38 +05:30
parent c3f39b966f
commit 2d73a38222
9 changed files with 6 additions and 779 deletions

381
src-tauri/Cargo.lock generated
View File

@@ -17,17 +17,6 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.8.12"
@@ -345,20 +334,6 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "auto_generate_cdp"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6e1961a0d5d77969057eba90d448e610d3c439024d135d9dbd98e33ec973520"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"serde",
"serde_json",
"ureq 2.12.1",
]
[[package]]
name = "autocfg"
version = "1.4.0"
@@ -507,25 +482,6 @@ dependencies = [
"serde",
]
[[package]]
name = "bzip2"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
dependencies = [
"bzip2-sys",
]
[[package]]
name = "bzip2-sys"
version = "0.1.13+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "cairo-rs"
version = "0.18.5"
@@ -658,16 +614,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "claudia"
version = "0.1.0"
@@ -681,7 +627,6 @@ dependencies = [
"env_logger",
"futures",
"glob",
"headless_chrome",
"libc",
"log",
"objc",
@@ -773,12 +718,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "constant_time_eq"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -873,21 +812,6 @@ dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crc32fast"
version = "1.4.2"
@@ -994,24 +918,12 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "data-encoding"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]]
name = "deflate64"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
[[package]]
name = "deranged"
version = "0.4.0"
@@ -1033,37 +945,6 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.101",
]
[[package]]
name = "derive_more"
version = "0.99.20"
@@ -1085,16 +966,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "directories"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
dependencies = [
"dirs-sys 0.5.0",
]
[[package]]
@@ -2017,33 +1888,6 @@ dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "headless_chrome"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c268ea01c2902b2acb382c1fae26818113dd661e0dba036a893f0ba40f00cdd8"
dependencies = [
"anyhow",
"auto_generate_cdp",
"base64 0.22.1",
"derive_builder",
"directories",
"log",
"rand 0.9.1",
"regex",
"serde",
"serde_json",
"tempfile",
"thiserror 2.0.12",
"tungstenite",
"ureq 3.0.12",
"url",
"walkdir",
"which",
"winreg",
"zip 2.4.2",
]
[[package]]
name = "heck"
version = "0.4.1"
@@ -2068,15 +1912,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "html5ever"
version = "0.26.0"
@@ -2171,7 +2006,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots 1.0.1",
"webpki-roots",
]
[[package]]
@@ -2407,15 +2242,6 @@ dependencies = [
"cfb",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -2724,27 +2550,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "lzma-rs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
dependencies = [
"byteorder",
"crc",
]
[[package]]
name = "lzma-sys"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "mac"
version = "0.1.1"
@@ -3411,16 +3216,6 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
"hmac",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -4079,7 +3874,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots 1.0.1",
"webpki-roots",
]
[[package]]
@@ -4188,7 +3983,6 @@ version = "0.23.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
@@ -4197,15 +3991,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.12.0"
@@ -4490,17 +4275,6 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.9"
@@ -4580,17 +4354,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "socks"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
dependencies = [
"byteorder",
"libc",
"winapi",
]
[[package]]
name = "softbuffer"
version = "0.4.6"
@@ -5148,7 +4911,7 @@ dependencies = [
"tokio",
"url",
"windows-sys 0.60.2",
"zip 4.2.0",
"zip",
]
[[package]]
@@ -5641,23 +5404,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tungstenite"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
dependencies = [
"bytes",
"data-encoding",
"http",
"httparse",
"log",
"rand 0.9.1",
"sha1",
"thiserror 2.0.12",
"utf-8",
]
[[package]]
name = "typeid"
version = "1.0.3"
@@ -5740,53 +5486,6 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
dependencies = [
"base64 0.22.1",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"socks",
"url",
"webpki-roots 0.26.11",
]
[[package]]
name = "ureq"
version = "3.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f0fde9bc91026e381155f8c67cb354bcd35260b2f4a29bcc84639f762760c39"
dependencies = [
"base64 0.22.1",
"flate2",
"log",
"percent-encoding",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"ureq-proto",
"utf-8",
"webpki-roots 0.26.11",
]
[[package]]
name = "ureq-proto"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7"
dependencies = [
"base64 0.22.1",
"http",
"httparse",
"log",
]
[[package]]
name = "url"
version = "2.5.4"
@@ -6137,15 +5836,6 @@ dependencies = [
"system-deps",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.1",
]
[[package]]
name = "webpki-roots"
version = "1.0.1"
@@ -6839,15 +6529,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "xz2"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
dependencies = [
"lzma-sys",
]
[[package]]
name = "yoke"
version = "0.8.0"
@@ -6979,20 +6660,6 @@ name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "zerotrie"
@@ -7027,36 +6694,6 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "zip"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
dependencies = [
"aes",
"arbitrary",
"bzip2",
"constant_time_eq",
"crc32fast",
"crossbeam-utils",
"deflate64",
"displaydoc",
"flate2",
"getrandom 0.3.3",
"hmac",
"indexmap 2.9.0",
"lzma-rs",
"memchr",
"pbkdf2",
"sha1",
"thiserror 2.0.12",
"time",
"xz2",
"zeroize",
"zopfli",
"zstd",
]
[[package]]
name = "zip"
version = "4.2.0"
@@ -7069,18 +6706,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "zopfli"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7"
dependencies = [
"bumpalo",
"crc32fast",
"log",
"simd-adler32",
]
[[package]]
name = "zstd"
version = "0.13.3"

View File

@@ -43,7 +43,6 @@ futures = "0.3"
async-trait = "0.1"
tempfile = "3"
which = "7"
headless_chrome = { version = "1.0", features = ["fetch"] }
sha2 = "0.10"
zstd = "0.13"
uuid = { version = "1.6", features = ["v4", "serde"] }

View File

@@ -5,7 +5,6 @@ use reqwest;
use rusqlite::{params, Connection, Result as SqliteResult};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::path::PathBuf;
use std::process::Stdio;
use std::sync::Mutex;
use tauri::{AppHandle, Emitter, Manager, State};

View File

@@ -1,5 +1,4 @@
pub mod agents;
pub mod claude;
pub mod mcp;
pub mod screenshot;
pub mod usage;

View File

@@ -1,269 +0,0 @@
use headless_chrome::protocol::cdp::Page;
use headless_chrome::{Browser, LaunchOptions};
use std::fs;
use std::time::Duration;
use tauri::AppHandle;
/// Captures a screenshot of a URL using headless Chrome
///
/// This function launches a headless Chrome browser, navigates to the specified URL,
/// and captures a screenshot of either the entire page or a specific element.
///
/// # Arguments
/// * `app` - The Tauri application handle
/// * `url` - The URL to capture
/// * `selector` - Optional CSS selector for a specific element to capture
/// * `full_page` - Whether to capture the entire page or just the viewport
///
/// # Returns
/// * `Result<String, String>` - The path to the saved screenshot file, or an error message
#[tauri::command]
pub async fn capture_url_screenshot(
_app: AppHandle,
url: String,
selector: Option<String>,
full_page: bool,
) -> Result<String, String> {
log::info!(
"Capturing screenshot of URL: {}, selector: {:?}, full_page: {}",
url,
selector,
full_page
);
// Run the browser operations in a blocking task since headless_chrome is not async
let result =
tokio::task::spawn_blocking(move || capture_screenshot_sync(url, selector, full_page))
.await
.map_err(|e| format!("Failed to spawn blocking task: {}", e))?;
// Log the result of the headless Chrome capture before returning
match &result {
Ok(path) => log::info!("capture_url_screenshot returning path: {}", path),
Err(err) => log::error!("capture_url_screenshot encountered error: {}", err),
}
result
}
/// Synchronous helper function to capture screenshots using headless Chrome
fn capture_screenshot_sync(
url: String,
selector: Option<String>,
full_page: bool,
) -> Result<String, String> {
// Configure browser launch options
let launch_options = LaunchOptions {
headless: true,
window_size: Some((1920, 1080)),
..Default::default()
};
// Launch the browser
let browser =
Browser::new(launch_options).map_err(|e| format!("Failed to launch browser: {}", e))?;
// Create a new tab
let tab = browser
.new_tab()
.map_err(|e| format!("Failed to create new tab: {}", e))?;
// Set a reasonable timeout for page navigation
tab.set_default_timeout(Duration::from_secs(30));
// Navigate to the URL
tab.navigate_to(&url)
.map_err(|e| format!("Failed to navigate to URL: {}", e))?;
// Wait for the page to load
// Try to wait for network idle, but don't fail if it times out
let _ = tab.wait_until_navigated();
// Additional wait to ensure dynamic content loads
std::thread::sleep(Duration::from_millis(500));
// Wait explicitly for the <body> element to exist this often prevents
// "Unable to capture screenshot" CDP errors on some pages
if let Err(e) = tab.wait_for_element("body") {
log::warn!(
"Timed out waiting for <body> element: {} continuing anyway",
e
);
}
// Capture the screenshot
let screenshot_data = if let Some(selector) = selector {
// Wait for the element and capture it
log::info!("Waiting for element with selector: {}", selector);
let element = tab
.wait_for_element(&selector)
.map_err(|e| format!("Failed to find element '{}': {}", selector, e))?;
element
.capture_screenshot(Page::CaptureScreenshotFormatOption::Png)
.map_err(|e| format!("Failed to capture element screenshot: {}", e))?
} else {
// Capture the entire page or viewport
log::info!(
"Capturing {} screenshot",
if full_page { "full page" } else { "viewport" }
);
// Get the page dimensions for full page screenshot
let clip = if full_page {
// Execute JavaScript to get the full page dimensions
let dimensions = tab
.evaluate(
r#"
({
width: Math.max(
document.body.scrollWidth,
document.documentElement.scrollWidth,
document.body.offsetWidth,
document.documentElement.offsetWidth,
document.documentElement.clientWidth
),
height: Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight,
document.documentElement.clientHeight
)
})
"#,
false,
)
.map_err(|e| format!("Failed to get page dimensions: {}", e))?;
// Extract dimensions from the result
let width = dimensions
.value
.as_ref()
.and_then(|v| v.as_object())
.and_then(|obj| obj.get("width"))
.and_then(|v| v.as_f64())
.unwrap_or(1920.0);
let height = dimensions
.value
.as_ref()
.and_then(|v| v.as_object())
.and_then(|obj| obj.get("height"))
.and_then(|v| v.as_f64())
.unwrap_or(1080.0);
Some(Page::Viewport {
x: 0.0,
y: 0.0,
width,
height,
scale: 1.0,
})
} else {
None
};
let capture_result = tab.capture_screenshot(
Page::CaptureScreenshotFormatOption::Png,
None,
clip.clone(),
full_page, // capture_beyond_viewport only makes sense for full page
);
match capture_result {
Ok(data) => data,
Err(err) => {
// Retry once with capture_beyond_viewport=true which works around some Chromium bugs
log::warn!(
"Initial screenshot attempt failed: {}. Retrying with capture_beyond_viewport=true",
err
);
tab.capture_screenshot(Page::CaptureScreenshotFormatOption::Png, None, clip, true)
.map_err(|e| format!("Failed to capture screenshot after retry: {}", e))?
}
}
};
// Save to temporary file
let temp_dir = std::env::temp_dir();
let timestamp = chrono::Utc::now().timestamp_millis();
let filename = format!("claudia_screenshot_{}.png", timestamp);
let file_path = temp_dir.join(filename);
fs::write(&file_path, screenshot_data)
.map_err(|e| format!("Failed to save screenshot: {}", e))?;
// Log the screenshot path prominently
println!("═══════════════════════════════════════════════════════════════");
println!("📸 SCREENSHOT SAVED SUCCESSFULLY!");
println!("📁 Location: {}", file_path.display());
println!("═══════════════════════════════════════════════════════════════");
log::info!("Screenshot saved to: {:?}", file_path);
Ok(file_path.to_string_lossy().to_string())
}
/// Cleans up old screenshot files from the temporary directory
///
/// This function removes screenshot files older than the specified number of minutes
/// to prevent accumulation of temporary files.
///
/// # Arguments
/// * `older_than_minutes` - Remove files older than this many minutes (default: 60)
///
/// # Returns
/// * `Result<usize, String>` - The number of files deleted, or an error message
#[tauri::command]
pub async fn cleanup_screenshot_temp_files(
older_than_minutes: Option<u64>,
) -> Result<usize, String> {
let minutes = older_than_minutes.unwrap_or(60);
log::info!(
"Cleaning up screenshot files older than {} minutes",
minutes
);
let temp_dir = std::env::temp_dir();
let cutoff_time = chrono::Utc::now() - chrono::Duration::minutes(minutes as i64);
let mut deleted_count = 0;
// Read directory entries
let entries =
fs::read_dir(&temp_dir).map_err(|e| format!("Failed to read temp directory: {}", e))?;
for entry in entries {
if let Ok(entry) = entry {
let path = entry.path();
// Check if it's a claudia screenshot file
if let Some(filename) = path.file_name() {
if let Some(filename_str) = filename.to_str() {
if filename_str.starts_with("claudia_screenshot_")
&& filename_str.ends_with(".png")
{
// Check file age
if let Ok(metadata) = fs::metadata(&path) {
if let Ok(modified) = metadata.modified() {
let modified_time = chrono::DateTime::<chrono::Utc>::from(modified);
if modified_time < cutoff_time {
// Delete the file
if fs::remove_file(&path).is_ok() {
deleted_count += 1;
log::debug!("Deleted old screenshot: {:?}", path);
}
}
}
}
}
}
}
}
}
log::info!("Cleaned up {} old screenshot files", deleted_count);
Ok(deleted_count)
}

View File

@@ -33,7 +33,7 @@ use commands::mcp::{
mcp_read_project_config, mcp_remove, mcp_reset_project_choices, mcp_save_project_config,
mcp_serve, mcp_test_connection,
};
use commands::screenshot::{capture_url_screenshot, cleanup_screenshot_temp_files};
use commands::usage::{
get_session_stats, get_usage_by_date_range, get_usage_details, get_usage_stats,
};
@@ -160,9 +160,7 @@ fn main() {
mcp_reset_project_choices,
mcp_get_server_status,
mcp_read_project_config,
mcp_save_project_config,
capture_url_screenshot,
cleanup_screenshot_temp_files
mcp_save_project_config
])
.run(tauri::generate_context!())
.expect("error while running tauri application");

View File

@@ -792,17 +792,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
// Keep the previewUrl so it can be restored when reopening
};
const handlePreviewScreenshot = async (imagePath: string) => {
console.log("Screenshot captured:", imagePath);
// Add the screenshot to the floating prompt input
if (floatingPromptRef.current) {
floatingPromptRef.current.addImage(imagePath);
// Show a subtle animation/feedback that the image was added
// You could add a toast notification here if desired
}
};
const handlePreviewUrlChange = (url: string) => {
console.log('[ClaudeCodeSession] Preview URL changed to:', url);
@@ -951,7 +941,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
<WebviewPreview
initialUrl={previewUrl}
onClose={handleClosePreview}
onScreenshot={handlePreviewScreenshot}
isMaximized={isPreviewMaximized}
onToggleMaximize={handleTogglePreviewMaximize}
onUrlChange={handlePreviewUrlChange}
@@ -1101,7 +1090,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
<WebviewPreview
initialUrl={previewUrl}
onClose={handleClosePreview}
onScreenshot={handlePreviewScreenshot}
isMaximized={isPreviewMaximized}
onToggleMaximize={handleTogglePreviewMaximize}
onUrlChange={handlePreviewUrlChange}

View File

@@ -7,7 +7,6 @@ import {
X,
Minimize2,
Maximize2,
Camera,
Loader2,
AlertCircle,
Globe,
@@ -17,7 +16,6 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { api } from "@/lib/api";
// TODO: These imports will be used when implementing actual Tauri webview
// import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
// import { WebviewWindow } from "@tauri-apps/api/webviewWindow";
@@ -31,10 +29,6 @@ interface WebviewPreviewProps {
* Callback when close is clicked
*/
onClose: () => void;
/**
* Callback when screenshot is requested
*/
onScreenshot?: (imagePath: string) => void;
/**
* Whether the webview is maximized
*/
@@ -60,13 +54,11 @@ interface WebviewPreviewProps {
* <WebviewPreview
* initialUrl="http://localhost:3000"
* onClose={() => setShowPreview(false)}
* onScreenshot={(path) => attachImage(path)}
* />
*/
const WebviewPreviewComponent: React.FC<WebviewPreviewProps> = ({
initialUrl,
onClose,
onScreenshot,
isMaximized = false,
onToggleMaximize,
onUrlChange,
@@ -80,8 +72,6 @@ const WebviewPreviewComponent: React.FC<WebviewPreviewProps> = ({
// TODO: These will be implemented with actual webview navigation
// const [canGoBack, setCanGoBack] = useState(false);
// const [canGoForward, setCanGoForward] = useState(false);
const [isCapturing, setIsCapturing] = useState(false);
const [showShutterAnimation, setShowShutterAnimation] = useState(false);
// TODO: These will be used for actual Tauri webview implementation
// const webviewRef = useRef<WebviewWindow | null>(null);
@@ -181,39 +171,6 @@ const WebviewPreviewComponent: React.FC<WebviewPreviewProps> = ({
navigate(initialUrl);
};
const handleScreenshot = async () => {
if (isCapturing || !currentUrl) return;
try {
setIsCapturing(true);
setShowShutterAnimation(true);
// Wait for shutter animation to start
await new Promise(resolve => setTimeout(resolve, 100));
// Capture the current URL using headless Chrome
const filePath = await api.captureUrlScreenshot(
currentUrl,
null, // No specific selector - capture the whole viewport
false // Not full page, just viewport
);
console.log("Screenshot captured and saved to:", filePath);
// Continue shutter animation
await new Promise(resolve => setTimeout(resolve, 200));
setShowShutterAnimation(false);
// Trigger callback with animation
onScreenshot?.(filePath);
} catch (err) {
console.error("Failed to capture screenshot:", err);
setShowShutterAnimation(false);
} finally {
setIsCapturing(false);
}
};
return (
<div
ref={containerRef}
@@ -330,49 +287,11 @@ const WebviewPreviewComponent: React.FC<WebviewPreviewProps> = ({
</Button>
)}
</div>
{/* Screenshot Button */}
<Button
variant="outline"
size="sm"
onClick={handleScreenshot}
disabled={isCapturing || hasError}
className="gap-2"
>
{isCapturing ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Camera className="h-4 w-4" />
)}
Send to Claude
</Button>
</div>
</div>
{/* Webview Content */}
<div className="flex-1 relative bg-background" ref={contentRef}>
{/* Shutter Animation */}
<AnimatePresence>
{showShutterAnimation && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
className="absolute inset-0 z-20 pointer-events-none"
>
<motion.div
initial={{ borderWidth: 0 }}
animate={{ borderWidth: 8 }}
exit={{ borderWidth: 0 }}
transition={{ duration: 0.3 }}
className="absolute inset-0 border-white shadow-lg"
style={{ boxShadow: 'inset 0 0 20px rgba(255, 255, 255, 0.8)' }}
/>
</motion.div>
)}
</AnimatePresence>
{/* Loading Overlay */}
<AnimatePresence>
{isLoading && (

View File

@@ -1481,38 +1481,7 @@ export const api = {
}
},
/**
* Captures a screenshot of a specific region in the window
* @param url - The URL to capture
* @param selector - Optional selector to capture
* @param fullPage - Whether to capture the full page
* @returns Promise resolving to the path of the saved screenshot
*/
async captureUrlScreenshot(
url: string,
selector?: string | null,
fullPage: boolean = false
): Promise<string> {
return await invoke<string>("capture_url_screenshot", {
url,
selector,
fullPage,
});
},
/**
* Cleans up old screenshot files from the temporary directory
* @param olderThanMinutes - Remove files older than this many minutes (default: 60)
* @returns Promise resolving to the number of files deleted
*/
async cleanupScreenshotTempFiles(olderThanMinutes?: number): Promise<number> {
try {
return await invoke<number>("cleanup_screenshot_temp_files", { olderThanMinutes });
} catch (error) {
console.error("Failed to cleanup screenshot files:", error);
throw error;
}
},
/**
* List all available Claude installations on the system