use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use std::sync::mpsc::Sender;
use crate::app::{App, SubmitAction};
use crate::tui::events::common::{
copy_to_clipboard, pasted_images_dir, read_from_clipboard, read_image_from_clipboard,
uuid_or_timestamp,
};
use crate::tui::{
WorkerEvent, handle_function_action, spawn_generation_worker, spawn_models_worker,
};
pub(crate) fn handle_chat_key(
app: &mut App,
sender: &Sender<WorkerEvent>,
key: KeyEvent,
) -> Result<()> {
if app.ui.show_trust_modal {
return handle_trust_modal_key(app, key);
}
if matches!(
app.proc.pending,
Some(crate::app::PendingTask::ConfirmFunction { .. })
) {
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Quit, key)
{
app.should_quit = true;
return Ok(());
}
match key.code {
KeyCode::Char('y') | KeyCode::Char('Y') => {
if let Some(action) = app.answer_function_confirmation(true) {
handle_function_action(action, sender);
}
}
KeyCode::Char('n') | KeyCode::Char('N') => {
if let Some(action) = app.answer_function_confirmation(false) {
handle_function_action(action, sender);
}
}
KeyCode::Char('j') | KeyCode::Down => {
app.ui
.confirm_scroll
.set(app.ui.confirm_scroll.get().saturating_add(1));
}
KeyCode::Char('k') | KeyCode::Up => {
app.ui
.confirm_scroll
.set(app.ui.confirm_scroll.get().saturating_sub(1));
}
KeyCode::PageDown => {
app.ui
.confirm_scroll
.set(app.ui.confirm_scroll.get().saturating_add(10));
}
KeyCode::PageUp => {
app.ui
.confirm_scroll
.set(app.ui.confirm_scroll.get().saturating_sub(10));
}
_ => {
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Cancel, key)
&& let Some(action) = app.answer_function_confirmation(false)
{
handle_function_action(action, sender);
}
}
}
return Ok(());
}
if app.ui.model_picker_open {
return handle_model_picker_key(app, key);
}
if app.ui.theme_picker_open {
return handle_theme_picker_key(app, key);
}
if app.ui.agent_picker_open {
return handle_agent_picker_key(app, key);
}
if app.chat.shell_focused {
let is_ctrl_c = (key.code == KeyCode::Char('c')
&& key.modifiers.contains(KeyModifiers::CONTROL))
|| app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Quit, key);
let is_esc = app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Cancel, key);
if is_ctrl_c || is_esc {
handle_interrupt_signal(app);
return Ok(());
}
if key.code == KeyCode::Tab {
app.chat.shell_focused = false;
for m in &mut app.chat.messages {
if m.is_shell {
*m.cached_wrapped.borrow_mut() = None;
}
}
app.status = "Ready".to_owned();
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::ScrollUp, key)
{
app.chat.scroll.set(app.chat.scroll.get().saturating_add(1));
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::ScrollDown, key)
{
app.chat.scroll.set(app.chat.scroll.get().saturating_sub(1));
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::PageUp, key)
{
app.chat
.scroll
.set(app.chat.scroll.get().saturating_add(15));
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::PageDown, key)
{
app.chat
.scroll
.set(app.chat.scroll.get().saturating_sub(15));
return Ok(());
}
let mut written = false;
let mut written_pid = None;
let active_session_id = if app.chat.shell_focused {
app.chat.focused_shell_session_id.clone()
} else {
crate::tui::ACTIVE_PERSISTENT_SESSION_ID.lock().clone()
};
if let Some(ref session_id) = active_session_id
&& let Some(registry_mutex) = crate::tui::PERSISTENT_SESSIONS.get()
&& let Some(session) = registry_mutex.lock().get_mut(session_id)
{
use std::io::Write;
let data = match key.code {
KeyCode::Char(c) => Some(c.to_string()),
KeyCode::Enter => Some("\n".to_owned()),
KeyCode::Backspace => Some("\x08".to_owned()),
_ => None,
};
if let Some(s) = data {
let _ = session.stdin.write_all(s.as_bytes());
let _ = session.stdin.flush();
written = true;
written_pid = Some(session.pid);
}
}
if !written {
let mut guard = crate::tui::RUNNING_PROCESS_STDIN.lock();
if let Some(ref mut stdin) = guard.as_mut() {
use std::io::Write;
let data = match key.code {
KeyCode::Char(c) => Some(c.to_string()),
KeyCode::Enter => Some("\n".to_owned()),
KeyCode::Backspace => Some("\x08".to_owned()),
_ => None,
};
if let Some(s) = data {
let _ = stdin.write_all(s.as_bytes());
let _ = stdin.flush();
written = true;
{
let pid_guard = crate::tui::RUNNING_PROCESS_PID.lock();
written_pid = *pid_guard;
}
}
}
}
if !written
&& app.chat.shell_focused
&& let Some(pid) = app.chat.focused_shell_pid
{
let bg_registry = crate::tui::BACKGROUND_PROCESSES.get();
if let Some(registry_mutex) = bg_registry
&& let Some(proc) = registry_mutex.lock().get_mut(&pid)
&& let Some(ref mut stdin) = proc.stdin
{
use std::io::Write;
let data = match key.code {
KeyCode::Char(c) => Some(c.to_string()),
KeyCode::Enter => Some("\n".to_owned()),
KeyCode::Backspace => Some("\x08".to_owned()),
_ => None,
};
if let Some(s) = data {
let _ = stdin.write_all(s.as_bytes());
let _ = stdin.flush();
written = true;
written_pid = Some(pid);
}
}
}
if written {
let mut found_msg = None;
if let Some(ref session_id) = active_session_id {
found_msg = app
.chat
.messages
.iter_mut()
.rev()
.find(|m| m.is_shell && m.shell_session_id.as_ref() == Some(session_id));
}
if found_msg.is_none()
&& let Some(wp) = written_pid
{
found_msg = app
.chat
.messages
.iter_mut()
.rev()
.find(|m| m.is_shell && m.shell_pid == Some(wp));
}
if found_msg.is_none() {
found_msg = app.chat.messages.iter_mut().rev().find(|m| m.is_shell);
}
if let Some(msg) = found_msg {
if msg.text.ends_with("\nRunning...\n") {
msg.text.truncate(msg.text.len() - 11);
}
match key.code {
KeyCode::Char(c) => {
msg.text.push(c);
}
KeyCode::Enter => {
msg.text.push('\n');
}
KeyCode::Backspace => {
msg.text.pop();
}
_ => {}
}
*msg.cached_wrapped.borrow_mut() = None;
}
}
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Quit, key)
{
let is_running_process = crate::tui::RUNNING_PROCESS_PID.lock().is_some();
let is_active_persistent = crate::tui::ACTIVE_PERSISTENT_SESSION_ID.lock().is_some();
let mut has_running_command_in_history = false;
for msg in &app.chat.history {
if msg.role == "function" {
for part in &msg.parts {
if let Some(resp) = part.get("functionResponse")
&& resp.get("name").and_then(|v| v.as_str()) == Some("sh")
&& let Some(response_val) = resp.get("response")
{
let status = response_val.get("status");
let error_val = response_val
.get("error")
.and_then(|v| v.as_str())
.unwrap_or("");
let is_aborted = error_val.contains("terminated by user")
|| error_val.contains("Ctrl+C");
let is_running = status.and_then(|s| s.as_str()) == Some("running")
|| error_val == "Command timed out / is still running"
|| (status.is_none()
&& !is_aborted
&& response_val.get("exit_code").is_none());
if is_running {
has_running_command_in_history = true;
break;
}
}
}
}
}
if app.proc.pending.is_some()
|| app.chat.focused_shell_pid.is_some()
|| is_running_process
|| is_active_persistent
|| app.proc.cancel_token.is_some()
|| has_running_command_in_history
{
handle_interrupt_signal(app);
return Ok(());
} else {
app.should_quit = true;
return Ok(());
}
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Cancel, key)
{
handle_interrupt_signal(app);
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::ToggleSetup, key)
{
app.open_setup();
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::ToggleModels, key)
{
if let Some(config) = app.begin_load_chat_models() {
spawn_models_worker(config, sender.clone());
}
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::ToggleSessions, key)
{
app.open_sessions();
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Submit, key)
{
if let Some(action) = app.submit_chat_input() {
match action {
SubmitAction::Generate(request) => {
spawn_generation_worker(
request.config,
request.history,
request.cancel_token,
request.generation_id,
request.dev_mode,
sender.clone(),
);
}
SubmitAction::LoadModels(config) => {
spawn_models_worker(config, sender.clone());
}
SubmitAction::ExecuteFunction { name, args, config } => {
handle_function_action(
crate::app::FunctionAction::Execute { name, args, config },
sender,
);
}
}
}
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::ScrollUp, key)
{
let suggestions = app.command_suggestions();
if !suggestions.is_empty() {
app.chat.suggestion_idx = if app.chat.suggestion_idx == 0 {
suggestions.len().saturating_sub(1)
} else {
app.chat.suggestion_idx - 1
};
return Ok(());
}
if app.chat.input.contains('\n') {
let old_cursor = app.chat.cursor;
app.chat.move_cursor_up();
if app.chat.cursor == old_cursor {
app.chat.scroll.set(app.chat.scroll.get().saturating_add(1));
}
} else {
app.chat.scroll.set(app.chat.scroll.get().saturating_add(1));
}
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::ScrollDown, key)
{
let suggestions = app.command_suggestions();
if !suggestions.is_empty() {
app.chat.suggestion_idx = (app.chat.suggestion_idx + 1) % suggestions.len();
return Ok(());
}
if app.chat.input.contains('\n') {
let old_cursor = app.chat.cursor;
app.chat.move_cursor_down();
if app.chat.cursor == old_cursor {
app.chat.scroll.set(app.chat.scroll.get().saturating_sub(1));
}
} else {
app.chat.scroll.set(app.chat.scroll.get().saturating_sub(1));
}
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::PageUp, key)
{
app.chat
.scroll
.set(app.chat.scroll.get().saturating_add(15));
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::PageDown, key)
{
app.chat
.scroll
.set(app.chat.scroll.get().saturating_sub(15));
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::HistoryUp, key)
{
app.chat.navigate_history_up();
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::HistoryDown, key)
{
app.chat.navigate_history_down();
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Paste, key)
{
if let Ok(Some(image_bytes)) = read_image_from_clipboard()
&& let Ok(dir) = pasted_images_dir()
{
let filename = format!("image_{}.png", uuid_or_timestamp());
let path = dir.join(&filename);
if std::fs::write(&path, &image_bytes).is_ok() {
let ref_str = format!(" @{} ", filename);
app.chat.insert_text(&ref_str);
app.status = format!("Pasted image saved to {}", filename);
return Ok(());
}
}
if let Ok(text) = read_from_clipboard() {
app.chat.insert_text(&text);
}
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::TodoScrollUp, key)
{
app.chat
.todo_scroll
.set(app.chat.todo_scroll.get().saturating_sub(1));
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::TodoScrollDown, key)
{
app.chat
.todo_scroll
.set(app.chat.todo_scroll.get().saturating_add(1));
return Ok(());
}
match (key.code, key.modifiers) {
(KeyCode::Char('z'), KeyModifiers::CONTROL) => {
app.chat.undo();
}
(KeyCode::Char('r'), KeyModifiers::CONTROL) => {
app.chat.redo();
}
(KeyCode::Tab, KeyModifiers::CONTROL) | (KeyCode::Char('t'), KeyModifiers::CONTROL) => {
app.toggle_dev_mode();
}
(KeyCode::Char('k'), KeyModifiers::CONTROL) => {
let text = app.chat.input.clone();
if !text.is_empty() {
if copy_to_clipboard(&text).is_ok() {
app.status = "Copied input to clipboard".to_owned();
}
app.chat.save_history();
app.chat.input.clear();
app.chat.cursor = 0;
app.chat.input_scroll = 0;
}
}
(KeyCode::Char('l'), KeyModifiers::CONTROL) => {
let mut last_response = None;
for msg in app.chat.messages.iter().rev() {
if msg.author == "Darwin" && !msg.is_tool && !msg.is_shell && !msg.pending {
let mut text = msg.text.trim();
if text.starts_with("(empty)") {
text = text["(empty)".len()..].trim();
}
let mut clean_text = text.to_owned();
if clean_text.starts_with("Thinking...") {
clean_text = clean_text["Thinking...".len()..].to_owned();
} else if clean_text.starts_with("Thinking:") {
if let Some(first_newline_idx) = clean_text.find('\n') {
clean_text = clean_text[first_newline_idx + 1..].to_owned();
} else {
clean_text = clean_text["Thinking:".len()..].to_owned();
}
} else if clean_text.starts_with("â–‘ Thinking...") {
clean_text = clean_text["â–‘ Thinking...".len()..].to_owned();
} else if clean_text.starts_with("â–‘ Thinking:") {
if let Some(first_newline_idx) = clean_text.find('\n') {
clean_text = clean_text[first_newline_idx + 1..].to_owned();
} else {
clean_text = clean_text["â–‘ Thinking:".len()..].to_owned();
}
}
let final_text = clean_text.trim().to_owned();
if !final_text.is_empty() {
last_response = Some(final_text);
break;
}
}
}
if let Some(text) = last_response {
if copy_to_clipboard(&text).is_ok() {
app.status = "Copied last response to clipboard".to_owned();
}
} else {
app.status = "No assistant response to copy".to_owned();
}
}
(KeyCode::Tab, _) => {
let suggestions = app.command_suggestions();
if !suggestions.is_empty() {
app.accept_command_suggestion();
} else {
app.chat.shell_focused = !app.chat.shell_focused;
for m in &mut app.chat.messages {
if m.is_shell {
*m.cached_wrapped.borrow_mut() = None;
}
}
if app.chat.shell_focused {
let last_shell = app.chat.messages.iter().rev().find(|m| m.is_shell);
if let Some(msg) = last_shell {
app.chat.focused_shell_session_id = msg.shell_session_id.clone();
app.chat.focused_shell_pid = msg.shell_pid;
} else {
app.chat.focused_shell_session_id = None;
app.chat.focused_shell_pid = None;
}
app.chat.scroll.set(0); app.status = "Shell/Messages focused. Press Tab to return, or Ctrl+C to abort running command.".to_owned();
} else {
app.chat.focused_shell_session_id = None;
app.chat.focused_shell_pid = None;
app.status = "Ready".to_owned();
}
}
}
(KeyCode::Enter, modifiers) if !modifiers.is_empty() => {
app.chat.insert_char('\n');
}
(KeyCode::Backspace, _) => app.chat.remove_char(),
(KeyCode::Delete, _) => app.chat.delete_char(),
(KeyCode::Left, _) => app.chat.move_cursor_left(),
(KeyCode::Right, _) => app.chat.move_cursor_right(),
(KeyCode::Home, _) => app.chat.move_cursor_start(),
(KeyCode::End, _) => app.chat.move_cursor_end(),
(KeyCode::Char(value), modifiers)
if !modifiers.contains(KeyModifiers::CONTROL)
&& !modifiers.contains(KeyModifiers::ALT) =>
{
app.chat.insert_char(value);
}
_ => {}
}
Ok(())
}
fn handle_model_picker_key(app: &mut App, key: KeyEvent) -> Result<()> {
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Cancel, key)
{
app.cancel_models();
return Ok(());
}
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::ToggleModels, key)
{
app.cancel_models();
return Ok(());
}
match key.code {
KeyCode::Esc => {
app.cancel_models();
}
KeyCode::Enter => {
app.apply_selected_model();
}
KeyCode::Up => {
app.select_previous_model();
}
KeyCode::Down => {
app.select_next_model();
}
KeyCode::PageUp => {
let len = app.ui.models.filtered_indices().len();
for _ in 0..5 {
if len > 0 {
app.ui.models.selected =
app.ui.models.selected.checked_sub(1).unwrap_or(len - 1);
}
}
}
KeyCode::PageDown => {
let len = app.ui.models.filtered_indices().len();
for _ in 0..5 {
if len > 0 {
app.ui.models.selected = (app.ui.models.selected + 1) % len;
}
}
}
KeyCode::Backspace => {
app.ui.models.pop_query();
}
KeyCode::Char(c)
if !key.modifiers.contains(KeyModifiers::CONTROL)
&& !key.modifiers.contains(KeyModifiers::ALT) =>
{
app.ui.models.push_query(c);
}
_ => {}
}
Ok(())
}
fn handle_theme_picker_key(app: &mut App, key: KeyEvent) -> Result<()> {
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Cancel, key)
{
app.cancel_themes();
return Ok(());
}
match key.code {
KeyCode::Esc => {
app.cancel_themes();
}
KeyCode::Enter => {
app.apply_selected_theme();
}
KeyCode::Up => {
app.select_previous_theme();
}
KeyCode::Down => {
app.select_next_theme();
}
KeyCode::PageUp => {
let len = app.ui.theme_picker.filtered_indices().len();
for _ in 0..5 {
if len > 0 {
app.ui.theme_picker.selected = app
.ui
.theme_picker
.selected
.checked_sub(1)
.unwrap_or(len - 1);
}
}
}
KeyCode::PageDown => {
let len = app.ui.theme_picker.filtered_indices().len();
for _ in 0..5 {
if len > 0 {
app.ui.theme_picker.selected = (app.ui.theme_picker.selected + 1) % len;
}
}
}
KeyCode::Backspace => {
app.ui.theme_picker.pop_query();
}
KeyCode::Char(c)
if !key.modifiers.contains(KeyModifiers::CONTROL)
&& !key.modifiers.contains(KeyModifiers::ALT) =>
{
app.ui.theme_picker.push_query(c);
}
_ => {}
}
Ok(())
}
fn handle_agent_picker_key(app: &mut App, key: KeyEvent) -> Result<()> {
if app
.core
.keybindings
.matches(crate::tui::keybindings::TuiAction::Cancel, key)
{
app.cancel_agents();
return Ok(());
}
match key.code {
KeyCode::Esc => {
app.cancel_agents();
}
KeyCode::Enter => {
app.apply_selected_agent();
}
KeyCode::Up => {
app.select_previous_agent();
}
KeyCode::Down => {
app.select_next_agent();
}
KeyCode::PageUp => {
let len = app.ui.agent_picker.filtered_indices().len();
for _ in 0..5 {
if len > 0 {
app.ui.agent_picker.selected = app
.ui
.agent_picker
.selected
.checked_sub(1)
.unwrap_or(len - 1);
}
}
}
KeyCode::PageDown => {
let len = app.ui.agent_picker.filtered_indices().len();
for _ in 0..5 {
if len > 0 {
app.ui.agent_picker.selected = (app.ui.agent_picker.selected + 1) % len;
}
}
}
KeyCode::Backspace => {
app.ui.agent_picker.pop_query();
}
KeyCode::Char(c)
if !key.modifiers.contains(KeyModifiers::CONTROL)
&& !key.modifiers.contains(KeyModifiers::ALT) =>
{
app.ui.agent_picker.push_query(c);
}
_ => {}
}
Ok(())
}
fn handle_interrupt_signal(app: &mut App) {
app.cancel_generation();
let mut target_pid = {
let mut guard = crate::tui::RUNNING_PROCESS_PID.lock();
guard.take()
};
if target_pid.is_none() {
target_pid = app.chat.focused_shell_pid;
}
let mut target_session_id = None;
if let Some(ref session_id) = app.chat.focused_shell_session_id {
target_session_id = Some(session_id.clone());
} else if let Some(session_id) = crate::tui::ACTIVE_PERSISTENT_SESSION_ID.lock().as_ref() {
target_session_id = Some(session_id.clone());
}
let mut history_target = None;
for i in (0..app.chat.history.len()).rev() {
if app.chat.history[i].role == "function" {
let mut found = false;
for j in 0..app.chat.history[i].parts.len() {
let part = &app.chat.history[i].parts[j];
if let Some(resp) = part.get("functionResponse")
&& resp.get("name").and_then(|v| v.as_str()) == Some("sh")
&& let Some(response_val) = resp.get("response")
{
let status = response_val.get("status");
let error_val = response_val
.get("error")
.and_then(|v| v.as_str())
.unwrap_or("");
let is_aborted =
error_val.contains("terminated by user") || error_val.contains("Ctrl+C");
let is_running = status.and_then(|s| s.as_str()) == Some("running")
|| error_val == "Command timed out / is still running"
|| (status.is_none()
&& !is_aborted
&& response_val.get("exit_code").is_none());
if is_running {
let resp_pid = response_val
.get("pid")
.and_then(|v| v.as_u64())
.map(|v| v as u32);
let mut persistent_session_id = None;
for k in (0..i).rev() {
if app.chat.history[k].role == "model" {
for call_part in &app.chat.history[k].parts {
if let Some(call) = call_part.get("functionCall")
&& call.get("name").and_then(|v| v.as_str()) == Some("sh")
{
if let Some(args) = call.get("args") {
persistent_session_id = args
.get("persistent_session_id")
.and_then(|v| v.as_str())
.map(|s| s.to_owned());
}
break;
}
}
break;
}
}
history_target = Some((i, j, persistent_session_id, resp_pid));
found = true;
break;
}
}
}
if found {
break;
}
}
}
if let Some((_, _, ref p_sess_id, resp_pid)) = history_target {
if target_session_id.is_none() && p_sess_id.is_some() {
target_session_id = p_sess_id.clone();
}
if target_pid.is_none() && resp_pid.is_some() {
target_pid = resp_pid;
}
}
let mut killed = false;
if let Some(ref session_id) = target_session_id {
let registry = crate::tui::PERSISTENT_SESSIONS.get();
if let Some(registry_mutex) = registry
&& let Some(session) = registry_mutex.lock().get(session_id)
{
let p_pid = session.pid;
#[cfg(unix)]
{
let _ = std::process::Command::new("kill")
.arg("-2")
.arg(format!("-{}", p_pid))
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status();
}
#[cfg(not(unix))]
{
let _ = std::process::Command::new("taskkill")
.arg("/F")
.arg("/PID")
.arg(p_pid.to_string())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status();
}
killed = true;
}
}
if !killed && let Some(pid) = target_pid {
#[cfg(unix)]
{
let _ = std::process::Command::new("kill")
.arg("-9")
.arg(format!("-{}", pid))
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status();
}
#[cfg(not(unix))]
{
let _ = std::process::Command::new("taskkill")
.arg("/F")
.arg("/PID")
.arg(pid.to_string())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status();
}
}
if let Some((i, j, _, _)) = history_target {
if let Some(resp) = app.chat.history[i].parts[j].get_mut("functionResponse")
&& let Some(response_val) = resp.get_mut("response")
&& let Some(obj) = response_val.as_object_mut()
{
obj.insert(
"error".to_owned(),
serde_json::json!("Process terminated by user via Ctrl+C"),
);
obj.insert("status".to_owned(), serde_json::Value::Null);
}
} else if let Some(pid) = target_pid {
for msg in &mut app.chat.history {
if msg.role == "function" {
for part in &mut msg.parts {
if let Some(resp) = part.get_mut("functionResponse")
&& resp.get("name").and_then(|v| v.as_str()) == Some("sh")
&& let Some(response_val) = resp.get_mut("response")
{
let resp_pid = response_val
.get("pid")
.and_then(|v| v.as_u64())
.map(|v| v as u32);
if resp_pid == Some(pid)
&& let Some(obj) = response_val.as_object_mut()
{
obj.insert(
"error".to_owned(),
serde_json::json!("Process terminated by user via Ctrl+C"),
);
obj.insert("status".to_owned(), serde_json::Value::Null);
}
}
}
}
}
}
app.chat.shell_focused = false;
app.chat.focused_shell_session_id = None;
app.chat.focused_shell_pid = None;
for m in &mut app.chat.messages {
if m.is_shell {
*m.cached_wrapped.borrow_mut() = None;
}
}
app.chat.messages = crate::app::session::rebuild_messages_from_history(
&app.chat.history,
app.chat.config.show_thoughts,
);
app.save_session();
app.status = "Aborted by user".to_owned();
}
fn handle_trust_modal_key(app: &mut App, key: KeyEvent) -> Result<()> {
match key.code {
KeyCode::Left | KeyCode::Char('h') | KeyCode::BackTab => {
app.ui.trust_modal_selected_yes = true;
}
KeyCode::Right | KeyCode::Char('l') | KeyCode::Tab => {
app.ui.trust_modal_selected_yes = false;
}
KeyCode::Char('y') | KeyCode::Char('Y') => {
app.answer_trust_modal(true);
}
KeyCode::Char('n') | KeyCode::Char('N') => {
app.answer_trust_modal(false);
}
KeyCode::Enter => {
app.answer_trust_modal(app.ui.trust_modal_selected_yes);
}
KeyCode::Esc => {
app.answer_trust_modal(false);
}
_ => {}
}
Ok(())
}