use crate::client::tui::app::{App, MessageRole}; use crate::protocol::serialize_inbound; use crate::protocol::WsInbound; use crossterm::event::{KeyCode, KeyEvent}; use futures_util::SinkExt; pub async fn handle_key_event(app: &mut App, key: KeyEvent) { if app.show_help { match key.code { KeyCode::Esc | KeyCode::Char('q') => { app.toggle_help(); } _ => {} } return; } if app.show_command_menu { match key.code { KeyCode::Esc => { app.show_command_menu = false; app.selected_command_idx = 0; } KeyCode::Up => { app.select_prev_command(); } KeyCode::Down => { app.select_next_command(); } KeyCode::Enter => { app.insert_command(); app.show_command_menu = false; app.selected_command_idx = 0; } KeyCode::Tab => { app.insert_command(); } _ => { // Handle normal input and check if menu should stay open handle_normal_input(app, key).await; } } return; } handle_normal_input(app, key).await; } async fn handle_normal_input(app: &mut App, key: KeyEvent) { match key.code { KeyCode::Esc | KeyCode::Char('q') => { app.quit(); } KeyCode::Char('?') => { app.toggle_help(); } KeyCode::Char(c) => { app.input_insert_char(c); // Show command menu when input starts with / if !app.show_command_menu && (app.input == "/" || (app.input.len() > 1 && app.input.starts_with('/'))) { app.show_command_menu = true; app.selected_command_idx = 0; } else if app.show_command_menu && !app.input.starts_with('/') { app.show_command_menu = false; } } KeyCode::Backspace => { app.input_delete_char(); // Hide menu if input no longer starts with / if app.show_command_menu && !app.input.starts_with('/') { app.show_command_menu = false; app.selected_command_idx = 0; } } KeyCode::Left => { app.input_move_cursor_left(); } KeyCode::Right => { app.input_move_cursor_right(); } KeyCode::Home => { app.input_move_cursor_to_start(); } KeyCode::End => { app.input_move_cursor_to_end(); } KeyCode::Up => { app.scroll_chat_up(); } KeyCode::Down => { app.scroll_chat_down(); } KeyCode::Enter => { let input = app.take_input(); app.show_command_menu = false; app.selected_command_idx = 0; if !input.is_empty() { process_input(app, input).await; } } _ => {} } } async fn process_input(app: &mut App, input: String) { app.add_message(MessageRole::User, input.clone()); if let Some(sender) = &mut app.ws_sender { let inbound = WsInbound::UserInput { content: input, chat_id: app.current_session_id.clone(), channel: None, sender_id: None, }; if let Ok(text) = serialize_inbound(&inbound) { let _ = sender.send(tokio_tungstenite::tungstenite::Message::Text(text.into())).await; } } }