@@ -9,6 +9,7 @@ use tauri::{AppHandle, Emitter, Manager};
99use tokio:: process:: { Command , Child } ;
1010use tokio:: sync:: Mutex ;
1111use std:: sync:: Arc ;
12+ use uuid;
1213
1314/// Global state to track current Claude process
1415pub struct ClaudeProcessState {
@@ -857,8 +858,8 @@ pub async fn resume_claude_code(
857858
858859/// Cancel the currently running Claude Code execution
859860#[ tauri:: command]
860- pub async fn cancel_claude_execution ( app : AppHandle ) -> Result < ( ) , String > {
861- log:: info!( "Cancelling Claude Code execution" ) ;
861+ pub async fn cancel_claude_execution ( app : AppHandle , session_id : Option < String > ) -> Result < ( ) , String > {
862+ log:: info!( "Cancelling Claude Code execution for session: {:?}" , session_id ) ;
862863
863864 let claude_state = app. state :: < ClaudeProcessState > ( ) ;
864865 let mut current_process = claude_state. current_process . lock ( ) . await ;
@@ -872,9 +873,16 @@ pub async fn cancel_claude_execution(app: AppHandle) -> Result<(), String> {
872873 match child. kill ( ) . await {
873874 Ok ( _) => {
874875 log:: info!( "Successfully killed Claude process" ) ;
875- // Emit cancellation event
876+
877+ // If we have a session ID, emit session-specific events
878+ if let Some ( sid) = session_id {
879+ let _ = app. emit ( & format ! ( "claude-cancelled:{}" , sid) , true ) ;
880+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 100 ) ) . await ;
881+ let _ = app. emit ( & format ! ( "claude-complete:{}" , sid) , false ) ;
882+ }
883+
884+ // Also emit generic events for backward compatibility
876885 let _ = app. emit ( "claude-cancelled" , true ) ;
877- // Also emit complete with false to indicate failure
878886 tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 100 ) ) . await ;
879887 let _ = app. emit ( "claude-complete" , false ) ;
880888 Ok ( ( ) )
@@ -1055,6 +1063,15 @@ fn get_claude_settings_sync(_app: &AppHandle) -> Result<ClaudeSettings, String>
10551063async fn spawn_claude_process ( app : AppHandle , mut cmd : Command ) -> Result < ( ) , String > {
10561064 use tokio:: io:: { AsyncBufReadExt , BufReader } ;
10571065
1066+ // Generate a unique session ID for this Claude Code session
1067+ let session_id = format ! ( "claude-{}-{}" ,
1068+ std:: time:: SystemTime :: now( )
1069+ . duration_since( std:: time:: UNIX_EPOCH )
1070+ . unwrap_or_default( )
1071+ . as_millis( ) ,
1072+ uuid:: Uuid :: new_v4( ) . to_string( )
1073+ ) ;
1074+
10581075 // Spawn the process
10591076 let mut child = cmd. spawn ( ) . map_err ( |e| format ! ( "Failed to spawn Claude: {}" , e) ) ?;
10601077
@@ -1064,43 +1081,55 @@ async fn spawn_claude_process(app: AppHandle, mut cmd: Command) -> Result<(), St
10641081
10651082 // Get the child PID for logging
10661083 let pid = child. id ( ) ;
1067- log:: info!( "Spawned Claude process with PID: {:?}" , pid) ;
1084+ log:: info!( "Spawned Claude process with PID: {:?} and session ID: {} " , pid, session_id ) ;
10681085
10691086 // Create readers
10701087 let stdout_reader = BufReader :: new ( stdout) ;
10711088 let stderr_reader = BufReader :: new ( stderr) ;
10721089
1073- // Store the child process in the global state
1090+ // Store the child process in the global state (for backward compatibility)
10741091 let claude_state = app. state :: < ClaudeProcessState > ( ) ;
10751092 {
10761093 let mut current_process = claude_state. current_process . lock ( ) . await ;
1094+ // If there's already a process running, kill it first
1095+ if let Some ( mut existing_child) = current_process. take ( ) {
1096+ log:: warn!( "Killing existing Claude process before starting new one" ) ;
1097+ let _ = existing_child. kill ( ) . await ;
1098+ }
10771099 * current_process = Some ( child) ;
10781100 }
10791101
10801102 // Spawn tasks to read stdout and stderr
10811103 let app_handle = app. clone ( ) ;
1104+ let session_id_clone = session_id. clone ( ) ;
10821105 let stdout_task = tokio:: spawn ( async move {
10831106 let mut lines = stdout_reader. lines ( ) ;
10841107 while let Ok ( Some ( line) ) = lines. next_line ( ) . await {
10851108 log:: debug!( "Claude stdout: {}" , line) ;
1086- // Emit the line to the frontend
1109+ // Emit the line to the frontend with session isolation
1110+ let _ = app_handle. emit ( & format ! ( "claude-output:{}" , session_id_clone) , & line) ;
1111+ // Also emit to the generic event for backward compatibility
10871112 let _ = app_handle. emit ( "claude-output" , & line) ;
10881113 }
10891114 } ) ;
10901115
10911116 let app_handle_stderr = app. clone ( ) ;
1117+ let session_id_clone2 = session_id. clone ( ) ;
10921118 let stderr_task = tokio:: spawn ( async move {
10931119 let mut lines = stderr_reader. lines ( ) ;
10941120 while let Ok ( Some ( line) ) = lines. next_line ( ) . await {
10951121 log:: error!( "Claude stderr: {}" , line) ;
1096- // Emit error lines to the frontend
1122+ // Emit error lines to the frontend with session isolation
1123+ let _ = app_handle_stderr. emit ( & format ! ( "claude-error:{}" , session_id_clone2) , & line) ;
1124+ // Also emit to the generic event for backward compatibility
10971125 let _ = app_handle_stderr. emit ( "claude-error" , & line) ;
10981126 }
10991127 } ) ;
11001128
11011129 // Wait for the process to complete
11021130 let app_handle_wait = app. clone ( ) ;
11031131 let claude_state_wait = claude_state. current_process . clone ( ) ;
1132+ let session_id_clone3 = session_id. clone ( ) ;
11041133 tokio:: spawn ( async move {
11051134 let _ = stdout_task. await ;
11061135 let _ = stderr_task. await ;
@@ -1113,12 +1142,16 @@ async fn spawn_claude_process(app: AppHandle, mut cmd: Command) -> Result<(), St
11131142 log:: info!( "Claude process exited with status: {}" , status) ;
11141143 // Add a small delay to ensure all messages are processed
11151144 tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 100 ) ) . await ;
1145+ let _ = app_handle_wait. emit ( & format ! ( "claude-complete:{}" , session_id_clone3) , status. success ( ) ) ;
1146+ // Also emit to the generic event for backward compatibility
11161147 let _ = app_handle_wait. emit ( "claude-complete" , status. success ( ) ) ;
11171148 }
11181149 Err ( e) => {
11191150 log:: error!( "Failed to wait for Claude process: {}" , e) ;
11201151 // Add a small delay to ensure all messages are processed
11211152 tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 100 ) ) . await ;
1153+ let _ = app_handle_wait. emit ( & format ! ( "claude-complete:{}" , session_id_clone3) , false ) ;
1154+ // Also emit to the generic event for backward compatibility
11221155 let _ = app_handle_wait. emit ( "claude-complete" , false ) ;
11231156 }
11241157 }
@@ -1128,6 +1161,9 @@ async fn spawn_claude_process(app: AppHandle, mut cmd: Command) -> Result<(), St
11281161 * current_process = None ;
11291162 } ) ;
11301163
1164+ // Return the session ID to the frontend
1165+ let _ = app. emit ( & format ! ( "claude-session-started:{}" , session_id) , session_id. clone ( ) ) ;
1166+
11311167 Ok ( ( ) )
11321168}
11331169
0 commit comments