// src/modes/canvas/read_only.rs use crate::config::binds::config::Config; use crate::config::binds::key_sequences::KeySequenceTracker; use crate::services::grpc_client::GrpcClient; use crate::state::pages::auth::LoginState; use crate::state::pages::auth::RegisterState; use crate::state::pages::form::FormState; use crate::state::pages::add_logic::AddLogicState; use crate::state::pages::add_table::AddTableState; use crate::state::app::state::AppState; use crate::functions::modes::read_only::{add_logic_ro, add_table_ro}; use canvas::{canvas::{CanvasAction, CanvasState, ActionResult}, dispatcher::ActionDispatcher}; use crossterm::event::KeyEvent; use anyhow::Result; pub async fn handle_form_readonly_with_canvas( key_event: KeyEvent, config: &Config, form_state: &mut FormState, ideal_cursor_column: &mut usize, ) -> Result { // Try canvas action from key first if let Some(canvas_action) = CanvasAction::from_key(key_event.code) { match ActionDispatcher::dispatch(canvas_action, form_state, ideal_cursor_column).await { Ok(ActionResult::Success(msg)) => { return Ok(msg.unwrap_or_default()); } Ok(ActionResult::HandledByFeature(msg)) => { return Ok(msg); } Ok(ActionResult::Error(msg)) => { return Ok(format!("Error: {}", msg)); } Ok(ActionResult::RequiresContext(msg)) => { return Ok(format!("Context needed: {}", msg)); } Err(_) => { // Fall through to try config mapping } } } // Try config-mapped action if let Some(action_str) = config.get_read_only_action_for_key(key_event.code, key_event.modifiers) { let canvas_action = CanvasAction::from_string(&action_str); match ActionDispatcher::dispatch(canvas_action, form_state, ideal_cursor_column).await { Ok(ActionResult::Success(msg)) => { return Ok(msg.unwrap_or_default()); } Ok(ActionResult::HandledByFeature(msg)) => { return Ok(msg); } Ok(ActionResult::Error(msg)) => { return Ok(format!("Error: {}", msg)); } Ok(ActionResult::RequiresContext(msg)) => { return Ok(format!("Context needed: {}", msg)); } Err(e) => { return Ok(format!("Action failed: {}", e)); } } } Ok(String::new()) } pub async fn handle_read_only_event( app_state: &mut AppState, key: KeyEvent, config: &Config, form_state: &mut FormState, login_state: &mut LoginState, register_state: &mut RegisterState, add_table_state: &mut AddTableState, add_logic_state: &mut AddLogicState, key_sequence_tracker: &mut KeySequenceTracker, grpc_client: &mut GrpcClient, command_message: &mut String, edit_mode_cooldown: &mut bool, ideal_cursor_column: &mut usize, ) -> Result<(bool, String)> { if config.is_enter_edit_mode_before(key.code, key.modifiers) { *edit_mode_cooldown = true; *command_message = "Entering Edit mode".to_string(); return Ok((false, command_message.clone())); } if config.is_enter_edit_mode_after(key.code, key.modifiers) { // Determine target state to adjust cursor if app_state.ui.show_login { let current_input = login_state.get_current_input(); let current_pos = login_state.current_cursor_pos(); if !current_input.is_empty() && current_pos < current_input.len() { login_state.set_current_cursor_pos(current_pos + 1); *ideal_cursor_column = login_state.current_cursor_pos(); } } else if app_state.ui.show_add_logic { let current_input = add_logic_state.get_current_input(); let current_pos = add_logic_state.current_cursor_pos(); if !current_input.is_empty() && current_pos < current_input.len() { add_logic_state.set_current_cursor_pos(current_pos + 1); *ideal_cursor_column = add_logic_state.current_cursor_pos(); } } else if app_state.ui.show_register { let current_input = register_state.get_current_input(); let current_pos = register_state.current_cursor_pos(); if !current_input.is_empty() && current_pos < current_input.len() { register_state.set_current_cursor_pos(current_pos + 1); *ideal_cursor_column = register_state.current_cursor_pos(); } } else if app_state.ui.show_add_table { let current_input = add_table_state.get_current_input(); let current_pos = add_table_state.current_cursor_pos(); if !current_input.is_empty() && current_pos < current_input.len() { add_table_state.set_current_cursor_pos(current_pos + 1); *ideal_cursor_column = add_table_state.current_cursor_pos(); } } else { // Handle FormState (uses library CanvasState) use canvas::canvas::CanvasState as LibraryCanvasState; // Import at the top of the function let current_input = form_state.get_current_input(); let current_pos = form_state.current_cursor_pos(); if !current_input.is_empty() && current_pos < current_input.len() { form_state.set_current_cursor_pos(current_pos + 1); *ideal_cursor_column = form_state.current_cursor_pos(); } } *edit_mode_cooldown = true; *command_message = "Entering Edit mode (after cursor)".to_string(); return Ok((false, command_message.clone())); } const CONTEXT_ACTIONS_FORM: &[&str] = &[ "previous_entry", "next_entry", ]; const CONTEXT_ACTIONS_LOGIN: &[&str] = &[ "previous_entry", "next_entry", ]; if key.modifiers.is_empty() { key_sequence_tracker.add_key(key.code); let sequence = key_sequence_tracker.get_sequence(); if let Some(action) = config.matches_key_sequence_generalized(&sequence).as_deref() { let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) { crate::tui::functions::form::handle_action( action, form_state, grpc_client, ideal_cursor_column, ) .await? } else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) { crate::tui::functions::login::handle_action(action).await? } else if app_state.ui.show_add_table { add_table_ro::execute_action( action, app_state, add_table_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_add_logic { add_logic_ro::execute_action( action, app_state, add_logic_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_register{ auth_ro::execute_action( action, app_state, register_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_login { auth_ro::execute_action( action, app_state, login_state, ideal_cursor_column, key_sequence_tracker, command_message, ) .await? } else { String::new() }; key_sequence_tracker.reset(); return Ok((false, result)); } if config.is_key_sequence_prefix(&sequence) { return Ok((false, command_message.clone())); } if sequence.len() == 1 && !config.is_key_sequence_prefix(&sequence) { if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers).as_deref() { let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) { crate::tui::functions::form::handle_action( action, form_state, grpc_client, ideal_cursor_column, ) .await? } else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) { crate::tui::functions::login::handle_action(action).await? } else if app_state.ui.show_add_table { add_table_ro::execute_action( action, app_state, add_table_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_add_logic { add_logic_ro::execute_action( action, app_state, add_logic_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_register { auth_ro::execute_action( action, app_state, register_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_login { auth_ro::execute_action( action, app_state, login_state, ideal_cursor_column, key_sequence_tracker, command_message, ) .await? } else { String::new() }; key_sequence_tracker.reset(); return Ok((false, result)); } } key_sequence_tracker.reset(); } else { key_sequence_tracker.reset(); if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers).as_deref() { let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) { crate::tui::functions::form::handle_action( action, form_state, grpc_client, ideal_cursor_column, ) .await? } else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) { crate::tui::functions::login::handle_action(action).await? } else if app_state.ui.show_add_table { add_table_ro::execute_action( action, app_state, add_table_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_add_logic { add_logic_ro::execute_action( action, app_state, add_logic_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_register { auth_ro::execute_action( action, app_state, register_state, ideal_cursor_column, key_sequence_tracker, command_message, ).await? } else if app_state.ui.show_login { auth_ro::execute_action( action, app_state, login_state, ideal_cursor_column, key_sequence_tracker, command_message, ) .await? } else { String::new() }; return Ok((false, result)); } } if !*edit_mode_cooldown { let default_key = "i".to_string(); let edit_key = config .keybindings .read_only .get("enter_edit_mode_before") .and_then(|keys| keys.first()) .map(|k| k.to_string()) .unwrap_or(default_key); *command_message = format!("Read-only mode - press {} to edit", edit_key); } *edit_mode_cooldown = false; Ok((false, command_message.clone())) }