From 83e9793dce469cba064561bca38b66f4807ab21b Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 14 Oct 2025 18:03:34 -0700 Subject: [PATCH] feat: add chat history import and export functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented full chat history import/export with JSON format: - Export button saves current conversation to JSON file - Import button loads conversation from JSON file - File dialog integration using tauri-plugin-dialog - Message count feedback on successful import - Automatic history reload after import - Preserves all message data including swipes and timestamps - Smart error handling (ignores cancelled dialogs) Backend (Rust): - export_chat_history: Opens save dialog, writes JSON to selected path - import_chat_history: Opens file picker, parses JSON, saves to current character - Message migration for backward compatibility - Returns helpful feedback (file path on export, message count on import) Frontend (JavaScript): - Export/import buttons in header with up/down arrow icons - Status updates during operations - Auto-reload chat view after import - Error handling with user-friendly messages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src-tauri/src/lib.rs | 85 +++++++++++++++++++++++++++++++++++++++++++- src/index.html | 12 +++++++ src/main.js | 43 ++++++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5a90eb8..b35e747 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1431,6 +1431,87 @@ async fn import_character_card(app_handle: tauri::AppHandle) -> Result Result { + use tauri_plugin_dialog::DialogExt; + + let character = get_active_character(); + let history = load_history(&character.id); + + // Open save dialog + let save_path = app_handle + .dialog() + .file() + .add_filter("Chat History", &["json"]) + .set_file_name(&format!("chat_{}.json", character.name)) + .blocking_save_file(); + + let output_path = if let Some(path) = save_path { + PathBuf::from( + path.as_path() + .ok_or_else(|| "Could not get file path".to_string())? + .to_string_lossy() + .to_string(), + ) + } else { + return Err("Save cancelled".to_string()); + }; + + // Write history to JSON file + let contents = serde_json::to_string_pretty(&history) + .map_err(|e| format!("Failed to serialize history: {}", e))?; + + fs::write(&output_path, contents) + .map_err(|e| format!("Failed to write file: {}", e))?; + + Ok(output_path.to_string_lossy().to_string()) +} + +// Import chat history from JSON +#[tauri::command] +async fn import_chat_history(app_handle: tauri::AppHandle) -> Result { + use tauri_plugin_dialog::DialogExt; + + // Open file picker for JSON files + let file_path = app_handle + .dialog() + .file() + .add_filter("Chat History", &["json"]) + .blocking_pick_file(); + + let json_path = if let Some(path) = file_path { + PathBuf::from( + path.as_path() + .ok_or_else(|| "Could not get file path".to_string())? + .to_string_lossy() + .to_string(), + ) + } else { + return Err("No file selected".to_string()); + }; + + // Read and parse history file + let contents = fs::read_to_string(&json_path) + .map_err(|e| format!("Failed to read file: {}", e))?; + + let mut history: ChatHistory = serde_json::from_str(&contents) + .map_err(|e| format!("Failed to parse history: {}", e))?; + + // Migrate messages to ensure compatibility + for msg in &mut history.messages { + msg.migrate(); + } + + let message_count = history.messages.len(); + + // Save history for current character + let character = get_active_character(); + save_history(&character.id, &history)?; + + Ok(message_count) +} + // Export character card to PNG #[tauri::command] async fn export_character_card(app_handle: tauri::AppHandle, character_id: String) -> Result { @@ -1514,7 +1595,9 @@ pub fn run() { delete_character, set_active_character, import_character_card, - export_character_card + export_character_card, + export_chat_history, + import_chat_history ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/index.html b/src/index.html index a2c62ea..83e0f1c 100644 --- a/src/index.html +++ b/src/index.html @@ -30,6 +30,18 @@
+ +