From a8774d0c87c6a623c2e899f21ae8174ce1914211 Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Mon, 19 Jan 2026 22:25:06 -0800 Subject: [PATCH] feat: add interrupt button --- src-tauri/src/commands.rs | 6 ++ src-tauri/src/config.rs | 3 + src-tauri/src/lib.rs | 1 + src-tauri/src/wsl_bridge.rs | 24 ++++++ src/lib/components/InputBar.svelte | 131 +++++++++++++++++++++++++---- src/lib/stores/claude.ts | 32 +++++++ src/lib/stores/historyRestore.ts | 29 +++++++ src/lib/tauri.ts | 23 ++++- 8 files changed, 231 insertions(+), 18 deletions(-) create mode 100644 src/lib/stores/historyRestore.ts diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 1bafa39..6862fa8 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -25,6 +25,12 @@ pub async fn stop_claude(app: AppHandle, bridge: State<'_, SharedBridge>) -> Res Ok(()) } +#[tauri::command] +pub async fn interrupt_claude(app: AppHandle, bridge: State<'_, SharedBridge>) -> Result<(), String> { + let mut bridge = bridge.lock(); + bridge.interrupt(&app) +} + #[tauri::command] pub async fn send_prompt(bridge: State<'_, SharedBridge>, message: String) -> Result<(), String> { let mut bridge = bridge.lock(); diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 8aaf580..a801550 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -19,6 +19,9 @@ pub struct ClaudeStartOptions { #[serde(default)] pub allowed_tools: Vec, + + #[serde(default)] + pub skip_greeting: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index d7646eb..c595bbf 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -32,6 +32,7 @@ pub fn run() { .invoke_handler(tauri::generate_handler![ start_claude, stop_claude, + interrupt_claude, send_prompt, is_claude_running, get_working_directory, diff --git a/src-tauri/src/wsl_bridge.rs b/src-tauri/src/wsl_bridge.rs index b3744f2..12e78ba 100644 --- a/src-tauri/src/wsl_bridge.rs +++ b/src-tauri/src/wsl_bridge.rs @@ -332,6 +332,30 @@ impl WslBridge { Ok(()) } + pub fn interrupt(&mut self, app: &AppHandle) -> Result<(), String> { + // Due to persistent bug in Claude Code where ESC/Ctrl+C doesn't work, + // we have to kill the process. This is the only reliable way to stop it. + // See: https://github.com/anthropics/claude-code/issues/3455 + if let Some(mut process) = self.process.take() { + // Kill the process immediately + let _ = process.kill(); + let _ = process.wait(); + + // Clear stdin + self.stdin = None; + + // Keep session_id and working directory for user reference + // The user will see what session was interrupted + + // Emit disconnected status + emit_connection_status(app, ConnectionStatus::Disconnected); + + Ok(()) + } else { + Err("No active process to interrupt".to_string()) + } + } + pub fn stop(&mut self, app: &AppHandle) { if let Some(mut process) = self.process.take() { let _ = process.kill(); diff --git a/src/lib/components/InputBar.svelte b/src/lib/components/InputBar.svelte index 3517c19..c057439 100644 --- a/src/lib/components/InputBar.svelte +++ b/src/lib/components/InputBar.svelte @@ -1,17 +1,30 @@