refactor: Store pasted images in memory as base64 data URLs

- Remove save_clipboard_image and cleanup_temp_images backend commands
- Update FloatingPromptInput to store pasted images as data URLs in the prompt
- Update ImagePreview component to handle both file paths and data URLs
- Update extractImagePaths to properly handle quoted data URLs
- Update handleRemoveImage to handle data URL removal

This eliminates file system operations for pasted images and stores them directly
in the prompt as base64 data URLs (e.g., @"data:image/png;base64,...").
Images are now fully self-contained within the session without creating temp files.
This commit is contained in:
Vivek R
2025-07-06 16:45:12 +05:30
parent 2009601dd9
commit 4cd104b1dd
5 changed files with 52 additions and 145 deletions

View File

@@ -922,104 +922,7 @@ pub async fn load_session_history(
Ok(messages)
}
/// Saves a clipboard image to a temporary location for a session
///
/// This command handles pasted images by saving them to a temp directory
/// within the project structure, making them accessible for Claude.
#[tauri::command]
pub async fn save_clipboard_image(
project_path: String,
session_id: String,
image_data: String, // Base64 encoded image data
mime_type: String,
) -> Result<String, String> {
log::info!(
"Saving clipboard image for session: {} in project: {}",
session_id,
project_path
);
// Parse the base64 data (remove data URL prefix if present)
let base64_data = if image_data.starts_with("data:") {
image_data
.split(',')
.nth(1)
.ok_or("Invalid data URL format")?
} else {
&image_data
};
// Decode base64 to bytes
use base64::Engine;
let image_bytes = base64::engine::general_purpose::STANDARD
.decode(base64_data)
.map_err(|e| format!("Failed to decode base64 image: {}", e))?;
// Determine file extension from MIME type
let extension = match mime_type.as_str() {
"image/png" => "png",
"image/jpeg" | "image/jpg" => "jpg",
"image/gif" => "gif",
"image/webp" => "webp",
"image/svg+xml" => "svg",
_ => "png", // Default to PNG
};
// Create temp directory for the project if it doesn't exist
let project_path_buf = PathBuf::from(&project_path);
let temp_dir = project_path_buf.join(".claude_temp").join(&session_id);
fs::create_dir_all(&temp_dir)
.map_err(|e| format!("Failed to create temp directory: {}", e))?;
// Generate unique filename with timestamp
let timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_millis();
let filename = format!("pasted_image_{}.{}", timestamp, extension);
let file_path = temp_dir.join(&filename);
// Write image to file
fs::write(&file_path, image_bytes)
.map_err(|e| format!("Failed to write image file: {}", e))?;
// Return the absolute path
let absolute_path = file_path
.canonicalize()
.map_err(|e| format!("Failed to get absolute path: {}", e))?
.to_string_lossy()
.to_string();
log::info!("Saved clipboard image to: {}", absolute_path);
Ok(absolute_path)
}
/// Cleans up temporary images for a session
///
/// This command removes the temporary directory created for pasted images
/// when a session ends to avoid accumulating temporary files.
#[tauri::command]
pub async fn cleanup_temp_images(
project_path: String,
session_id: String,
) -> Result<(), String> {
log::info!(
"Cleaning up temp images for session: {} in project: {}",
session_id,
project_path
);
let project_path_buf = PathBuf::from(&project_path);
let temp_dir = project_path_buf.join(".claude_temp").join(&session_id);
if temp_dir.exists() {
fs::remove_dir_all(&temp_dir)
.map_err(|e| format!("Failed to remove temp directory: {}", e))?;
log::info!("Cleaned up temp directory: {}", temp_dir.display());
}
Ok(())
}
/// Execute a new interactive Claude Code session with streaming output
#[tauri::command]

View File

@@ -18,13 +18,13 @@ use commands::agents::{
};
use commands::claude::{
cancel_claude_execution, check_auto_checkpoint, check_claude_version, cleanup_old_checkpoints,
cleanup_temp_images, clear_checkpoint_manager, continue_claude_code, create_checkpoint, execute_claude_code,
clear_checkpoint_manager, continue_claude_code, create_checkpoint, execute_claude_code,
find_claude_md_files, fork_from_checkpoint, get_checkpoint_diff, get_checkpoint_settings,
get_checkpoint_state_stats, get_claude_session_output, get_claude_settings, get_project_sessions,
get_recently_modified_files, get_session_timeline, get_system_prompt, list_checkpoints,
list_directory_contents, list_projects, list_running_claude_sessions, load_session_history,
open_new_session, read_claude_md_file, restore_checkpoint, resume_claude_code,
save_claude_md_file, save_claude_settings, save_clipboard_image, save_system_prompt, search_files,
save_claude_md_file, save_claude_settings, save_system_prompt, search_files,
track_checkpoint_message, track_session_messages, update_checkpoint_settings,
get_hooks_config, update_hooks_config, validate_hook_command,
ClaudeProcessState,
@@ -101,8 +101,6 @@ fn main() {
find_claude_md_files,
read_claude_md_file,
save_claude_md_file,
save_clipboard_image,
cleanup_temp_images,
load_session_history,
execute_claude_code,
continue_claude_code,