diff --git a/client/src/functions/modes.rs b/client/src/functions/modes.rs index dda082b..853187f 100644 --- a/client/src/functions/modes.rs +++ b/client/src/functions/modes.rs @@ -1,9 +1,7 @@ // src/functions/modes.rs -pub mod read_only; pub mod edit; pub mod navigation; -pub use read_only::*; pub use edit::*; pub use navigation::*; diff --git a/client/src/functions/modes/read_only.rs b/client/src/functions/modes/read_only.rs deleted file mode 100644 index 2f1d363..0000000 --- a/client/src/functions/modes/read_only.rs +++ /dev/null @@ -1,4 +0,0 @@ -// src/functions/modes/read_only.rs - -pub mod add_table_ro; -pub mod add_logic_ro; diff --git a/client/src/functions/modes/read_only/add_logic_ro.rs b/client/src/functions/modes/read_only/add_logic_ro.rs deleted file mode 100644 index 2617df4..0000000 --- a/client/src/functions/modes/read_only/add_logic_ro.rs +++ /dev/null @@ -1,235 +0,0 @@ -// src/functions/modes/read_only/add_logic_ro.rs -use crate::config::binds::key_sequences::KeySequenceTracker; -use crate::state::pages::add_logic::AddLogicState; // Changed -use crate::state::app::state::AppState; -use canvas::canvas::CanvasState; -use anyhow::Result; - -// Word navigation helpers (get_char_type, find_next_word_start, etc.) -// can be kept as they are generic. -#[derive(PartialEq)] -enum CharType { - Whitespace, - Alphanumeric, - Punctuation, -} - -fn get_char_type(c: char) -> CharType { - if c.is_whitespace() { CharType::Whitespace } - else if c.is_alphanumeric() { CharType::Alphanumeric } - else { CharType::Punctuation } -} - -fn find_next_word_start(text: &str, current_pos: usize) -> usize { - let chars: Vec = text.chars().collect(); - let len = chars.len(); - if len == 0 || current_pos >= len { return len; } - let mut pos = current_pos; - let initial_type = get_char_type(chars[pos]); - while pos < len && get_char_type(chars[pos]) == initial_type { pos += 1; } - while pos < len && get_char_type(chars[pos]) == CharType::Whitespace { pos += 1; } - pos -} - -fn find_word_end(text: &str, current_pos: usize) -> usize { - let chars: Vec = text.chars().collect(); - let len = chars.len(); - if len == 0 { return 0; } - let mut pos = current_pos.min(len - 1); - if get_char_type(chars[pos]) == CharType::Whitespace { - pos = find_next_word_start(text, pos); - } - if pos >= len { return len.saturating_sub(1); } - let word_type = get_char_type(chars[pos]); - while pos < len && get_char_type(chars[pos]) == word_type { pos += 1; } - pos.saturating_sub(1).min(len.saturating_sub(1)) -} - -fn find_prev_word_start(text: &str, current_pos: usize) -> usize { - let chars: Vec = text.chars().collect(); - if chars.is_empty() || current_pos == 0 { return 0; } - let mut pos = current_pos.saturating_sub(1); - while pos > 0 && get_char_type(chars[pos]) == CharType::Whitespace { pos -= 1; } - if pos == 0 && get_char_type(chars[pos]) == CharType::Whitespace { return 0; } - let word_type = get_char_type(chars[pos]); - while pos > 0 && get_char_type(chars[pos - 1]) == word_type { pos -= 1; } - pos -} - -fn find_prev_word_end(text: &str, current_pos: usize) -> usize { - let prev_start = find_prev_word_start(text, current_pos); - if prev_start == 0 { return 0; } - find_word_end(text, prev_start.saturating_sub(1)) -} - - -/// Executes read-only actions for the AddLogic view canvas. -pub async fn execute_action( - action: &str, - app_state: &mut AppState, - state: &mut AddLogicState, - ideal_cursor_column: &mut usize, - key_sequence_tracker: &mut KeySequenceTracker, - command_message: &mut String, -) -> Result { - match action { - "move_up" => { - key_sequence_tracker.reset(); - let num_fields = AddLogicState::INPUT_FIELD_COUNT; - if num_fields == 0 { return Ok("No fields.".to_string()); } - let current_field = state.current_field(); - - if current_field > 0 { - let new_field = current_field - 1; - state.set_current_field(new_field); - let current_input = state.get_current_input(); - let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) }; - let new_pos = (*ideal_cursor_column).min(max_cursor_pos); - state.set_current_cursor_pos(new_pos); - } else { - *command_message = "At top of form.".to_string(); - } - Ok(command_message.clone()) - } - "move_down" => { - key_sequence_tracker.reset(); - let num_fields = AddLogicState::INPUT_FIELD_COUNT; - if num_fields == 0 { return Ok("No fields.".to_string()); } - let current_field = state.current_field(); - let last_field_index = num_fields - 1; - - if current_field < last_field_index { - let new_field = current_field + 1; - state.set_current_field(new_field); - let current_input = state.get_current_input(); - let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) }; - let new_pos = (*ideal_cursor_column).min(max_cursor_pos); - state.set_current_cursor_pos(new_pos); - } else { - // Move focus outside canvas when moving down from the last field - // FIX: Go to ScriptContentPreview instead of SaveButton - app_state.ui.focus_outside_canvas = true; - state.last_canvas_field = 2; - state.current_focus = crate::state::pages::add_logic::AddLogicFocus::ScriptContentPreview; // FIXED! - *command_message = "Focus moved to script preview".to_string(); - } - Ok(command_message.clone()) - } - // ... (rest of the actions remain the same) ... - "move_first_line" => { - key_sequence_tracker.reset(); - if AddLogicState::INPUT_FIELD_COUNT > 0 { - state.set_current_field(0); - let current_input = state.get_current_input(); - let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) }; - let new_pos = (*ideal_cursor_column).min(max_cursor_pos); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - } - Ok("".to_string()) - } - "move_last_line" => { - key_sequence_tracker.reset(); - let num_fields = AddLogicState::INPUT_FIELD_COUNT; - if num_fields > 0 { - let last_field_index = num_fields - 1; - state.set_current_field(last_field_index); - let current_input = state.get_current_input(); - let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) }; - let new_pos = (*ideal_cursor_column).min(max_cursor_pos); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - } - Ok("".to_string()) - } - "move_left" => { - let current_pos = state.current_cursor_pos(); - let new_pos = current_pos.saturating_sub(1); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - Ok("".to_string()) - } - "move_right" => { - let current_input = state.get_current_input(); - let current_pos = state.current_cursor_pos(); - if !current_input.is_empty() && current_pos < current_input.len().saturating_sub(1) { - let new_pos = current_pos + 1; - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - } - Ok("".to_string()) - } - "move_word_next" => { - let current_input = state.get_current_input(); - if !current_input.is_empty() { - let new_pos = find_next_word_start(current_input, state.current_cursor_pos()); - let final_pos = new_pos.min(current_input.len().saturating_sub(1)); - state.set_current_cursor_pos(final_pos); - *ideal_cursor_column = final_pos; - } - Ok("".to_string()) - } - "move_word_end" => { - let current_input = state.get_current_input(); - if !current_input.is_empty() { - let current_pos = state.current_cursor_pos(); - let new_pos = find_word_end(current_input, current_pos); - let final_pos = if new_pos == current_pos && current_pos < current_input.len().saturating_sub(1) { - find_word_end(current_input, current_pos + 1) - } else { - new_pos - }; - let max_valid_index = current_input.len().saturating_sub(1); - let clamped_pos = final_pos.min(max_valid_index); - state.set_current_cursor_pos(clamped_pos); - *ideal_cursor_column = clamped_pos; - } - Ok("".to_string()) - } - "move_word_prev" => { - let current_input = state.get_current_input(); - if !current_input.is_empty() { - let new_pos = find_prev_word_start(current_input, state.current_cursor_pos()); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - } - Ok("".to_string()) - } - "move_word_end_prev" => { - let current_input = state.get_current_input(); - if !current_input.is_empty() { - let new_pos = find_prev_word_end(current_input, state.current_cursor_pos()); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - } - Ok("".to_string()) - } - "move_line_start" => { - state.set_current_cursor_pos(0); - *ideal_cursor_column = 0; - Ok("".to_string()) - } - "move_line_end" => { - let current_input = state.get_current_input(); - if !current_input.is_empty() { - let new_pos = current_input.len().saturating_sub(1); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - } else { - state.set_current_cursor_pos(0); - *ideal_cursor_column = 0; - } - Ok("".to_string()) - } - "enter_edit_mode_before" | "enter_edit_mode_after" | "enter_command_mode" | "exit_highlight_mode" => { - key_sequence_tracker.reset(); - Ok("Mode change handled by main loop".to_string()) - } - _ => { - key_sequence_tracker.reset(); - command_message.clear(); - Ok(format!("Unknown read-only action: {}", action)) - }, - } -} - diff --git a/client/src/functions/modes/read_only/add_table_ro.rs b/client/src/functions/modes/read_only/add_table_ro.rs deleted file mode 100644 index 3318cdb..0000000 --- a/client/src/functions/modes/read_only/add_table_ro.rs +++ /dev/null @@ -1,267 +0,0 @@ -// src/functions/modes/read_only/add_table_ro.rs -use crate::config::binds::key_sequences::KeySequenceTracker; -use crate::state::pages::add_table::AddTableState; -use crate::state::app::state::AppState; -use canvas::canvas::CanvasState; -use anyhow::Result; - -// Re-use word navigation helpers if they are public or move them to a common module -// For now, duplicating them here for simplicity. Consider refactoring later. -#[derive(PartialEq)] -enum CharType { - Whitespace, - Alphanumeric, - Punctuation, -} - -fn get_char_type(c: char) -> CharType { - if c.is_whitespace() { CharType::Whitespace } - else if c.is_alphanumeric() { CharType::Alphanumeric } - else { CharType::Punctuation } -} - -fn find_next_word_start(text: &str, current_pos: usize) -> usize { - let chars: Vec = text.chars().collect(); - let len = chars.len(); - if len == 0 || current_pos >= len { return len; } - let mut pos = current_pos; - let initial_type = get_char_type(chars[pos]); - while pos < len && get_char_type(chars[pos]) == initial_type { pos += 1; } - while pos < len && get_char_type(chars[pos]) == CharType::Whitespace { pos += 1; } - pos -} - -fn find_word_end(text: &str, current_pos: usize) -> usize { - let chars: Vec = text.chars().collect(); - let len = chars.len(); - if len == 0 { return 0; } - let mut pos = current_pos.min(len - 1); - if get_char_type(chars[pos]) == CharType::Whitespace { - pos = find_next_word_start(text, pos); - } - if pos >= len { return len.saturating_sub(1); } - let word_type = get_char_type(chars[pos]); - while pos < len && get_char_type(chars[pos]) == word_type { pos += 1; } - pos.saturating_sub(1).min(len.saturating_sub(1)) -} - -fn find_prev_word_start(text: &str, current_pos: usize) -> usize { - let chars: Vec = text.chars().collect(); - if chars.is_empty() || current_pos == 0 { return 0; } - let mut pos = current_pos.saturating_sub(1); - while pos > 0 && get_char_type(chars[pos]) == CharType::Whitespace { pos -= 1; } - if pos == 0 && get_char_type(chars[pos]) == CharType::Whitespace { return 0; } - let word_type = get_char_type(chars[pos]); - while pos > 0 && get_char_type(chars[pos - 1]) == word_type { pos -= 1; } - pos -} - -// Note: find_prev_word_end might need adjustments based on desired behavior. -// This version finds the end of the word *before* the previous word start. -fn find_prev_word_end(text: &str, current_pos: usize) -> usize { - let prev_start = find_prev_word_start(text, current_pos); - if prev_start == 0 { return 0; } - // Find the end of the word that starts at prev_start - 1 - find_word_end(text, prev_start.saturating_sub(1)) -} - - -/// Executes read-only actions for the AddTable view canvas. -pub async fn execute_action( - action: &str, - app_state: &mut AppState, // Needed for focus_outside_canvas - state: &mut AddTableState, - ideal_cursor_column: &mut usize, - key_sequence_tracker: &mut KeySequenceTracker, - command_message: &mut String, // Keep for potential messages -) -> Result { - // Use the CanvasState trait methods implemented for AddTableState - match action { - "move_up" => { - key_sequence_tracker.reset(); - let num_fields = AddTableState::INPUT_FIELD_COUNT; - if num_fields == 0 { - *command_message = "No fields.".to_string(); - return Ok(command_message.clone()); - } - let current_field = state.current_field(); // Gets the index (0, 1, or 2) - - if current_field > 0 { - // This handles moving from field 2 -> 1, or 1 -> 0 - let new_field = current_field - 1; - state.set_current_field(new_field); - let current_input = state.get_current_input(); - let max_cursor_pos = current_input.len(); // Allow cursor at end - let new_pos = (*ideal_cursor_column).min(max_cursor_pos); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; // Update ideal column as cursor moved - *command_message = "".to_string(); // Clear message for successful internal navigation - } else { - // current_field is 0 (InputTableName), and user pressed Up. - // Forbid moving up. Do not change focus or cursor. - *command_message = "At top of form.".to_string(); - } - Ok(command_message.clone()) - } - "move_down" => { - key_sequence_tracker.reset(); - let num_fields = AddTableState::INPUT_FIELD_COUNT; - if num_fields == 0 { - *command_message = "No fields.".to_string(); - return Ok(command_message.clone()); - } - let current_field = state.current_field(); - let last_field_index = num_fields - 1; - - if current_field < last_field_index { - let new_field = current_field + 1; - state.set_current_field(new_field); - let current_input = state.get_current_input(); - let max_cursor_pos = current_input.len(); // Allow cursor at end - let new_pos = (*ideal_cursor_column).min(max_cursor_pos); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; // Update ideal column - *command_message = "".to_string(); - } else { - // Move focus outside canvas when moving down from the last field - app_state.ui.focus_outside_canvas = true; - // Set focus to the first element outside canvas (AddColumnButton) - state.current_focus = - crate::state::pages::add_table::AddTableFocus::AddColumnButton; - *command_message = "Focus moved below canvas".to_string(); - } - Ok(command_message.clone()) - } - // ... (other actions like "move_first_line", "move_left", etc. remain the same) ... - "move_first_line" => { - key_sequence_tracker.reset(); - if AddTableState::INPUT_FIELD_COUNT > 0 { - state.set_current_field(0); - let current_input = state.get_current_input(); - let max_cursor_pos = current_input.len(); - let new_pos = (*ideal_cursor_column).min(max_cursor_pos); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; // Update ideal column - } - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_last_line" => { - key_sequence_tracker.reset(); - let num_fields = AddTableState::INPUT_FIELD_COUNT; - if num_fields > 0 { - let last_field_index = num_fields - 1; - state.set_current_field(last_field_index); - let current_input = state.get_current_input(); - let max_cursor_pos = current_input.len(); - let new_pos = (*ideal_cursor_column).min(max_cursor_pos); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; // Update ideal column - } - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_left" => { - let current_pos = state.current_cursor_pos(); - let new_pos = current_pos.saturating_sub(1); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_right" => { - let current_input = state.get_current_input(); - let current_pos = state.current_cursor_pos(); - // Allow moving cursor one position past the end - if current_pos < current_input.len() { - let new_pos = current_pos + 1; - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - } - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_word_next" => { - let current_input = state.get_current_input(); - let new_pos = find_next_word_start( - current_input, - state.current_cursor_pos(), - ); - let final_pos = new_pos.min(current_input.len()); // Allow cursor at end - state.set_current_cursor_pos(final_pos); - *ideal_cursor_column = final_pos; - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_word_end" => { - let current_input = state.get_current_input(); - let current_pos = state.current_cursor_pos(); - let new_pos = find_word_end(current_input, current_pos); - // If find_word_end returns current_pos, try starting search from next char - let final_pos = - if new_pos == current_pos && current_pos < current_input.len() { - find_word_end(current_input, current_pos + 1) - } else { - new_pos - }; - let max_valid_index = current_input.len(); // Allow cursor at end - let clamped_pos = final_pos.min(max_valid_index); - state.set_current_cursor_pos(clamped_pos); - *ideal_cursor_column = clamped_pos; - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_word_prev" => { - let current_input = state.get_current_input(); - let new_pos = find_prev_word_start( - current_input, - state.current_cursor_pos(), - ); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_word_end_prev" => { - let current_input = state.get_current_input(); - let new_pos = find_prev_word_end( - current_input, - state.current_cursor_pos(), - ); - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_line_start" => { - state.set_current_cursor_pos(0); - *ideal_cursor_column = 0; - *command_message = "".to_string(); - Ok(command_message.clone()) - } - "move_line_end" => { - let current_input = state.get_current_input(); - let new_pos = current_input.len(); // Allow cursor at end - state.set_current_cursor_pos(new_pos); - *ideal_cursor_column = new_pos; - *command_message = "".to_string(); - Ok(command_message.clone()) - } - // Actions handled by main event loop (mode changes) - "enter_edit_mode_before" | "enter_edit_mode_after" - | "enter_command_mode" | "exit_highlight_mode" => { - key_sequence_tracker.reset(); - // These actions are primarily mode changes handled by the main event loop. - // The message here might be overridden by the main loop's message for mode change. - *command_message = "Mode change initiated".to_string(); - Ok(command_message.clone()) - } - _ => { - key_sequence_tracker.reset(); - *command_message = - format!("Unknown read-only action: {}", action); - Ok(command_message.clone()) - } - } -} - diff --git a/client/src/modes/canvas/read_only.rs b/client/src/modes/canvas/read_only.rs index db2b460..afd0058 100644 --- a/client/src/modes/canvas/read_only.rs +++ b/client/src/modes/canvas/read_only.rs @@ -9,11 +9,50 @@ 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; +/// Helper function to dispatch canvas action for any CanvasState +async fn dispatch_canvas_action( + action: &str, + state: &mut S, + ideal_cursor_column: &mut usize, +) -> String { + let canvas_action = CanvasAction::from_string(action); + match ActionDispatcher::dispatch(canvas_action, state, ideal_cursor_column).await { + Ok(ActionResult::Success(msg)) => msg.unwrap_or_default(), + Ok(ActionResult::HandledByFeature(msg)) => msg, + Ok(ActionResult::Error(msg)) => format!("Error: {}", msg), + Ok(ActionResult::RequiresContext(msg)) => format!("Context needed: {}", msg), + Err(e) => format!("Action failed: {}", e), + } +} + +/// Helper function to dispatch canvas action to the appropriate state based on UI +async fn dispatch_to_active_state( + action: &str, + app_state: &AppState, + form_state: &mut FormState, + login_state: &mut LoginState, + register_state: &mut RegisterState, + add_table_state: &mut AddTableState, + add_logic_state: &mut AddLogicState, + ideal_cursor_column: &mut usize, +) -> String { + if app_state.ui.show_add_table { + dispatch_canvas_action(action, add_table_state, ideal_cursor_column).await + } else if app_state.ui.show_add_logic { + dispatch_canvas_action(action, add_logic_state, ideal_cursor_column).await + } else if app_state.ui.show_register { + dispatch_canvas_action(action, register_state, ideal_cursor_column).await + } else if app_state.ui.show_login { + dispatch_canvas_action(action, login_state, ideal_cursor_column).await + } else { + dispatch_canvas_action(action, form_state, ideal_cursor_column).await + } +} + pub async fn handle_form_readonly_with_canvas( key_event: KeyEvent, config: &Config, @@ -88,8 +127,7 @@ pub async fn handle_read_only_event( } if config.is_enter_edit_mode_after(key.code, key.modifiers) { - // Determine target state to adjust cursor - + // Determine target state to adjust cursor - all states now use CanvasState trait if app_state.ui.show_login { let current_input = login_state.get_current_input(); let current_pos = login_state.current_cursor_pos(); @@ -119,8 +157,7 @@ pub async fn handle_read_only_event( *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 + // Handle FormState 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() { @@ -158,45 +195,18 @@ pub async fn handle_read_only_event( .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( + } else { + // All canvas states handled uniformly + dispatch_to_active_state( action, app_state, + form_state, + login_state, + register_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() + ).await }; key_sequence_tracker.reset(); return Ok((false, result)); @@ -218,45 +228,18 @@ pub async fn handle_read_only_event( .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( + } else { + // All canvas states handled uniformly + dispatch_to_active_state( action, app_state, + form_state, + login_state, + register_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() + ).await }; key_sequence_tracker.reset(); return Ok((false, result)); @@ -277,45 +260,18 @@ pub async fn handle_read_only_event( .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( + } else { + // All canvas states handled uniformly + dispatch_to_active_state( action, app_state, + form_state, + login_state, + register_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() + ).await }; return Ok((false, result)); }