// src/search/event.rs use crate::state::app::state::AppState; use crate::services::grpc_client::GrpcClient; use common::proto::komp_ac::search::search_response::Hit; use crossterm::event::KeyCode; use tokio::sync::mpsc; use tracing::{error, info}; use std::collections::HashMap; use anyhow::Result; pub async fn handle_search_palette_event( key_event: crossterm::event::KeyEvent, app_state: &mut AppState, grpc_client: &mut GrpcClient, search_result_sender: mpsc::UnboundedSender>, ) -> Result> { let mut should_close = false; let mut outcome_message = None; let mut trigger_search = false; if let Some(search_state) = app_state.search_state.as_mut() { match key_event.code { KeyCode::Esc => { should_close = true; outcome_message = Some("Search cancelled".to_string()); } KeyCode::Enter => { // Step 1: Extract the data we need while holding the borrow let maybe_data = search_state .results .get(search_state.selected_index) .map(|hit| (hit.id, hit.content_json.clone())); // Step 2: Process outside the borrow if let Some((id, content_json)) = maybe_data { if let Ok(data) = serde_json::from_str::>(&content_json) { // Use current view path to access the active form if let (Some(profile), Some(table)) = ( app_state.current_view_profile_name.clone(), app_state.current_view_table_name.clone(), ) { let path = format!("{}/{}", profile, table); if let Some(fs) = app_state.form_state_for_path(&path) { let detached_pos = fs.total_count + 2; fs.update_from_response(&data, detached_pos); } } should_close = true; outcome_message = Some(format!("Loaded record ID {}", id)); } } } KeyCode::Up => search_state.previous_result(), KeyCode::Down => search_state.next_result(), KeyCode::Char(c) => { search_state.input.insert(search_state.cursor_position, c); search_state.cursor_position += 1; trigger_search = true; } KeyCode::Backspace => { if search_state.cursor_position > 0 { search_state.cursor_position -= 1; search_state.input.remove(search_state.cursor_position); trigger_search = true; } } KeyCode::Left => { search_state.cursor_position = search_state.cursor_position.saturating_sub(1); } KeyCode::Right => { if search_state.cursor_position < search_state.input.len() { search_state.cursor_position += 1; } } _ => {} } } if trigger_search { if let Some(search_state) = app_state.search_state.as_mut() { search_state.is_loading = true; search_state.results.clear(); search_state.selected_index = 0; let query = search_state.input.clone(); let table_name = search_state.table_name.clone(); let sender = search_result_sender.clone(); let mut grpc_client = grpc_client.clone(); info!("Spawning search task for query: '{}'", query); tokio::spawn(async move { match grpc_client.search_table(table_name, query).await { Ok(response) => { let _ = sender.send(response.hits); } Err(e) => { error!("Search failed: {:?}", e); let _ = sender.send(vec![]); } } }); } } if should_close { app_state.search_state = None; app_state.ui.show_search_palette = false; } Ok(outcome_message) }