refactor: consolidate nvim crates, extract server library, and update workspace dependencies
This commit is contained in:
1 parent
a083719cf1
commit
533adfd41b
53 files changed
+5967
-1230
No files matched your search
@@ -23,11 +23,11 @@ impl McpTool for WriteClipboardHandler {
|
||||
)
|
||||
}
|
||||
|
||||
async fn execute(&self, args: Value, _state: Arc<MemoryState>) -> crate::error::Result<String> {
|
||||
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
|
||||
let tool_args: WriteClipboardTool = serde_json::from_value(args)
|
||||
.map_err(|e| crate::error::AppError::Internal(format!("Invalid args: {}", e)))?;
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let res = tokio::task::spawn_blocking(move || {
|
||||
let mut msgs = Vec::new();
|
||||
|
||||
if let Ok(mut clipboard) = Clipboard::new()
|
||||
@@ -77,7 +77,10 @@ impl McpTool for WriteClipboardHandler {
|
||||
}
|
||||
})
|
||||
.await
|
||||
.map_err(|e| crate::error::AppError::Internal(format!("Task panic: {}", e)))?
|
||||
.map_err(|e| crate::error::AppError::Internal(format!("Task panic: {}", e)))??;
|
||||
|
||||
state.record_activity("clipboard", &res, None);
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,51 +104,51 @@ impl McpTool for ReadClipboardHandler {
|
||||
tokio::task::spawn_blocking(move || -> crate::error::Result<serde_json::Value> {
|
||||
let mut out = serde_json::Map::new();
|
||||
|
||||
if let Ok(mut clipboard) = arboard::Clipboard::new()
|
||||
&& let Ok(text) = clipboard.get_text()
|
||||
&& !text.trim().is_empty()
|
||||
{
|
||||
out.insert("text".into(), json!(text));
|
||||
}
|
||||
|
||||
if let Ok(mut clipboard) = arboard::Clipboard::new()
|
||||
&& let Ok(image_data) = clipboard.get_image()
|
||||
&& let Some(img) = ImageBuffer::<image::Rgba<u8>, _>::from_raw(
|
||||
image_data.width as u32,
|
||||
image_data.height as u32,
|
||||
image_data.bytes.into_owned(),
|
||||
)
|
||||
{
|
||||
let mut dynamic_img = image::DynamicImage::ImageRgba8(img);
|
||||
let max_dim = 1024;
|
||||
if dynamic_img.width() > max_dim || dynamic_img.height() > max_dim {
|
||||
dynamic_img = dynamic_img.resize(max_dim, max_dim, FilterType::Lanczos3);
|
||||
if let Ok(mut clipboard) = arboard::Clipboard::new() {
|
||||
if let Ok(text) = clipboard.get_text() {
|
||||
if !text.trim().is_empty() {
|
||||
out.insert("text".into(), json!(text));
|
||||
}
|
||||
}
|
||||
let rgb_img = dynamic_img.into_rgb8();
|
||||
|
||||
let cache_dir = dirs::home_dir()
|
||||
.unwrap_or_default()
|
||||
.join(".gemini/mcp_memory/clipboard");
|
||||
let _ = std::fs::create_dir_all(&cache_dir);
|
||||
if let Ok(image_data) = clipboard.get_image() {
|
||||
if let Some(img) = ImageBuffer::<image::Rgba<u8>, _>::from_raw(
|
||||
image_data.width as u32,
|
||||
image_data.height as u32,
|
||||
image_data.bytes.into_owned(),
|
||||
) {
|
||||
let mut dynamic_img = image::DynamicImage::ImageRgba8(img);
|
||||
let max_dim = 1024;
|
||||
if dynamic_img.width() > max_dim || dynamic_img.height() > max_dim {
|
||||
dynamic_img = dynamic_img.resize(max_dim, max_dim, FilterType::Lanczos3);
|
||||
}
|
||||
let rgb_img = dynamic_img.into_rgb8();
|
||||
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
let file_path = cache_dir.join(format!("clipboard_{}.jpg", timestamp));
|
||||
let cache_dir = dirs::home_dir()
|
||||
.unwrap_or_default()
|
||||
.join(".gemini/mcp_memory/clipboard");
|
||||
let _ = std::fs::create_dir_all(&cache_dir);
|
||||
|
||||
if rgb_img
|
||||
.save_with_format(&file_path, image::ImageFormat::Jpeg)
|
||||
.is_ok()
|
||||
{
|
||||
let path_str = file_path.to_string_lossy().to_string();
|
||||
out.insert("image_path".into(), json!(path_str));
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
let file_path = cache_dir.join(format!("clipboard_{}.jpg", timestamp));
|
||||
|
||||
// Read image bytes for base64 encoding if needed by vision
|
||||
if let Ok(bytes) = std::fs::read(&file_path) {
|
||||
use base64::Engine;
|
||||
let b64 = base64::engine::general_purpose::STANDARD.encode(&bytes);
|
||||
out.insert("image_base64".into(), json!(b64));
|
||||
if rgb_img
|
||||
.save_with_format(&file_path, image::ImageFormat::Jpeg)
|
||||
.is_ok()
|
||||
{
|
||||
let path_str = file_path.to_string_lossy().to_string();
|
||||
out.insert("image_path".into(), json!(path_str));
|
||||
|
||||
// Read image bytes for base64 encoding if needed by vision
|
||||
if let Ok(bytes) = std::fs::read(&file_path) {
|
||||
use base64::Engine;
|
||||
let b64 = base64::engine::general_purpose::STANDARD.encode(&bytes);
|
||||
out.insert("image_base64".into(), json!(b64));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,6 +179,7 @@ impl McpTool for ReadClipboardHandler {
|
||||
}
|
||||
}
|
||||
|
||||
state.record_activity("clipboard", "Read contents from OS clipboard", None);
|
||||
Ok::<String, crate::error::AppError>(serde_json::to_string_pretty(&final_obj)?)
|
||||
}
|
||||
}
|
||||
@@ -203,14 +207,14 @@ impl McpTool for ToggleClipboardWatchModeHandler {
|
||||
let mut watch_mode = state.clipboard_watch_mode.write().await;
|
||||
*watch_mode = tool_args.enable;
|
||||
|
||||
if tool_args.enable {
|
||||
Ok(
|
||||
"Clipboard watch mode enabled. Changes will be ingested as StickyNotes."
|
||||
.to_string(),
|
||||
)
|
||||
let status_msg = if tool_args.enable {
|
||||
"Clipboard watch mode enabled. Changes will be ingested as StickyNotes."
|
||||
} else {
|
||||
Ok("Clipboard watch mode disabled.".to_string())
|
||||
}
|
||||
"Clipboard watch mode disabled."
|
||||
};
|
||||
|
||||
state.record_activity("clipboard", status_msg, None);
|
||||
Ok(status_msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,4 +284,21 @@ mod tests {
|
||||
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
|
||||
assert!(parsed.is_object());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_read_clipboard_empty() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
|
||||
let handler = ReadClipboardHandler;
|
||||
|
||||
let result = handler
|
||||
.execute(serde_json::json!({}), state)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read clipboard: {}", e))
|
||||
.unwrap();
|
||||
|
||||
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
|
||||
assert!(parsed.is_object());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user