diff --git a/client/src/functions/modes/navigation/add_logic_nav.rs b/client/src/functions/modes/navigation/add_logic_nav.rs index adc105f..a8d8f44 100644 --- a/client/src/functions/modes/navigation/add_logic_nav.rs +++ b/client/src/functions/modes/navigation/add_logic_nav.rs @@ -12,6 +12,7 @@ use anyhow::Result; use crate::components::common::text_editor::TextEditor; use crate::services::ui_service::UiService; use tui_textarea::CursorMove; +use crate::state::pages::admin::AdminState; use crate::pages::routing::{Router, Page}; pub type SaveLogicResultSender = mpsc::Sender>; @@ -20,38 +21,23 @@ pub fn handle_add_logic_navigation( key_event: KeyEvent, config: &Config, app_state: &mut AppState, - add_logic_state: &mut AddLogicState, is_edit_mode: &mut bool, buffer_state: &mut BufferState, grpc_client: GrpcClient, - _save_logic_sender: SaveLogicResultSender, // Marked as unused + save_logic_sender: SaveLogicResultSender, command_message: &mut String, router: &mut Router, ) -> bool { - // === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION === - if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent { - // === AUTOCOMPLETE HANDLING === - if add_logic_state.script_editor_autocomplete_active { - match key_event.code { - // ... (Char, Backspace, Tab, Down, Up cases remain the same) ... - KeyCode::Char(c) if c.is_alphanumeric() || c == '_' => { - add_logic_state.script_editor_filter_text.push(c); - add_logic_state.update_script_editor_suggestions(); - { - let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); - TextEditor::handle_input( - &mut editor_borrow, - key_event, - &add_logic_state.editor_keybinding_mode, - &mut add_logic_state.vim_state, - ); - } - *command_message = format!("Filtering: @{}", add_logic_state.script_editor_filter_text); - return true; - } - KeyCode::Backspace => { - if !add_logic_state.script_editor_filter_text.is_empty() { - add_logic_state.script_editor_filter_text.pop(); + if let Page::AddLogic(add_logic_state) = &mut router.current { + + // === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION === + if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent { + // === AUTOCOMPLETE HANDLING === + if add_logic_state.script_editor_autocomplete_active { + match key_event.code { + // ... (Char, Backspace, Tab, Down, Up cases remain the same) ... + KeyCode::Char(c) if c.is_alphanumeric() || c == '_' => { + add_logic_state.script_editor_filter_text.push(c); add_logic_state.update_script_editor_suggestions(); { let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); @@ -62,180 +48,131 @@ pub fn handle_add_logic_navigation( &mut add_logic_state.vim_state, ); } - *command_message = if add_logic_state.script_editor_filter_text.is_empty() { - "Autocomplete: @".to_string() - } else { - format!("Filtering: @{}", add_logic_state.script_editor_filter_text) - }; - } else { - let should_deactivate = if let Some((trigger_line, trigger_col)) = add_logic_state.script_editor_trigger_position { - let current_cursor = { - let editor_borrow = add_logic_state.script_content_editor.borrow(); - editor_borrow.cursor() - }; - current_cursor.0 == trigger_line && current_cursor.1 == trigger_col + 1 - } else { - false - }; - if should_deactivate { - add_logic_state.deactivate_script_editor_autocomplete(); - *command_message = "Autocomplete cancelled".to_string(); - } - { - let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); - TextEditor::handle_input( - &mut editor_borrow, - key_event, - &add_logic_state.editor_keybinding_mode, - &mut add_logic_state.vim_state, - ); - } + *command_message = format!("Filtering: @{}", add_logic_state.script_editor_filter_text); + return true; } - return true; - } - KeyCode::Tab | KeyCode::Down => { - if !add_logic_state.script_editor_suggestions.is_empty() { - let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0); - let next = (current + 1) % add_logic_state.script_editor_suggestions.len(); - add_logic_state.script_editor_selected_suggestion_index = Some(next); - *command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[next]); - } - return true; - } - KeyCode::Up => { - if !add_logic_state.script_editor_suggestions.is_empty() { - let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0); - let prev = if current == 0 { - add_logic_state.script_editor_suggestions.len() - 1 - } else { - current - 1 - }; - add_logic_state.script_editor_selected_suggestion_index = Some(prev); - *command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[prev]); - } - return true; - } - KeyCode::Enter => { - if let Some(selected_idx) = add_logic_state.script_editor_selected_suggestion_index { - if let Some(suggestion) = add_logic_state.script_editor_suggestions.get(selected_idx).cloned() { - let trigger_pos = add_logic_state.script_editor_trigger_position; - let filter_len = add_logic_state.script_editor_filter_text.len(); - - add_logic_state.deactivate_script_editor_autocomplete(); - add_logic_state.has_unsaved_changes = true; - - if let Some(pos) = trigger_pos { + KeyCode::Backspace => { + if !add_logic_state.script_editor_filter_text.is_empty() { + add_logic_state.script_editor_filter_text.pop(); + add_logic_state.update_script_editor_suggestions(); + { let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); + TextEditor::handle_input( + &mut editor_borrow, + key_event, + &add_logic_state.editor_keybinding_mode, + &mut add_logic_state.vim_state, + ); + } + *command_message = if add_logic_state.script_editor_filter_text.is_empty() { + "Autocomplete: @".to_string() + } else { + format!("Filtering: @{}", add_logic_state.script_editor_filter_text) + }; + } else { + let should_deactivate = if let Some((trigger_line, trigger_col)) = add_logic_state.script_editor_trigger_position { + let current_cursor = { + let editor_borrow = add_logic_state.script_content_editor.borrow(); + editor_borrow.cursor() + }; + current_cursor.0 == trigger_line && current_cursor.1 == trigger_col + 1 + } else { + false + }; + if should_deactivate { + add_logic_state.deactivate_script_editor_autocomplete(); + *command_message = "Autocomplete cancelled".to_string(); + } + { + let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); + TextEditor::handle_input( + &mut editor_borrow, + key_event, + &add_logic_state.editor_keybinding_mode, + &mut add_logic_state.vim_state, + ); + } + } + return true; + } + KeyCode::Tab | KeyCode::Down => { + if !add_logic_state.script_editor_suggestions.is_empty() { + let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0); + let next = (current + 1) % add_logic_state.script_editor_suggestions.len(); + add_logic_state.script_editor_selected_suggestion_index = Some(next); + *command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[next]); + } + return true; + } + KeyCode::Up => { + if !add_logic_state.script_editor_suggestions.is_empty() { + let current = add_logic_state.script_editor_selected_suggestion_index.unwrap_or(0); + let prev = if current == 0 { + add_logic_state.script_editor_suggestions.len() - 1 + } else { + current - 1 + }; + add_logic_state.script_editor_selected_suggestion_index = Some(prev); + *command_message = format!("Selected: {}", add_logic_state.script_editor_suggestions[prev]); + } + return true; + } + KeyCode::Enter => { + if let Some(selected_idx) = add_logic_state.script_editor_selected_suggestion_index { + if let Some(suggestion) = add_logic_state.script_editor_suggestions.get(selected_idx).cloned() { + let trigger_pos = add_logic_state.script_editor_trigger_position; + let filter_len = add_logic_state.script_editor_filter_text.len(); - if suggestion == "sql" { - replace_autocomplete_text(&mut editor_borrow, pos, filter_len, "sql"); - editor_borrow.insert_str("('')"); - // Move cursor back twice to be between the single quotes - editor_borrow.move_cursor(CursorMove::Back); // Before ')' - editor_borrow.move_cursor(CursorMove::Back); // Before ''' (inside '') - *command_message = "Inserted: @sql('')".to_string(); - } else { - let is_table_selection = add_logic_state.is_table_name_suggestion(&suggestion); - replace_autocomplete_text(&mut editor_borrow, pos, filter_len, &suggestion); + add_logic_state.deactivate_script_editor_autocomplete(); + add_logic_state.has_unsaved_changes = true; - if is_table_selection { - editor_borrow.insert_str("."); - let new_cursor = editor_borrow.cursor(); - drop(editor_borrow); // Release borrow before calling add_logic_state methods + if let Some(pos) = trigger_pos { + let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); - add_logic_state.script_editor_trigger_position = Some(new_cursor); - add_logic_state.script_editor_autocomplete_active = true; - add_logic_state.script_editor_filter_text.clear(); - add_logic_state.trigger_column_autocomplete_for_table(suggestion.clone()); - - let profile_name = add_logic_state.profile_name.clone(); - let table_name_for_fetch = suggestion.clone(); - let mut client_clone = grpc_client.clone(); - tokio::spawn(async move { - match UiService::fetch_columns_for_table(&mut client_clone, &profile_name, &table_name_for_fetch).await { - Ok(_columns) => { - // Result handled by main UI loop - } - Err(e) => { - tracing::error!("Failed to fetch columns for {}.{}: {}", profile_name, table_name_for_fetch, e); - } - } - }); - *command_message = format!("Selected table '{}', fetching columns...", suggestion); + if suggestion == "sql" { + replace_autocomplete_text(&mut editor_borrow, pos, filter_len, "sql"); + editor_borrow.insert_str("('')"); + // Move cursor back twice to be between the single quotes + editor_borrow.move_cursor(CursorMove::Back); // Before ')' + editor_borrow.move_cursor(CursorMove::Back); // Before ''' (inside '') + *command_message = "Inserted: @sql('')".to_string(); } else { - *command_message = format!("Inserted: {}", suggestion); + let is_table_selection = add_logic_state.is_table_name_suggestion(&suggestion); + replace_autocomplete_text(&mut editor_borrow, pos, filter_len, &suggestion); + + if is_table_selection { + editor_borrow.insert_str("."); + let new_cursor = editor_borrow.cursor(); + drop(editor_borrow); // Release borrow before calling add_logic_state methods + + add_logic_state.script_editor_trigger_position = Some(new_cursor); + add_logic_state.script_editor_autocomplete_active = true; + add_logic_state.script_editor_filter_text.clear(); + add_logic_state.trigger_column_autocomplete_for_table(suggestion.clone()); + + let profile_name = add_logic_state.profile_name.clone(); + let table_name_for_fetch = suggestion.clone(); + let mut client_clone = grpc_client.clone(); + tokio::spawn(async move { + match UiService::fetch_columns_for_table(&mut client_clone, &profile_name, &table_name_for_fetch).await { + Ok(_columns) => { + // Result handled by main UI loop + } + Err(e) => { + tracing::error!("Failed to fetch columns for {}.{}: {}", profile_name, table_name_for_fetch, e); + } + } + }); + *command_message = format!("Selected table '{}', fetching columns...", suggestion); + } else { + *command_message = format!("Inserted: {}", suggestion); + } } } + return true; } - return true; } - } - add_logic_state.deactivate_script_editor_autocomplete(); - { - let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); - TextEditor::handle_input( - &mut editor_borrow, - key_event, - &add_logic_state.editor_keybinding_mode, - &mut add_logic_state.vim_state, - ); - } - return true; - } - KeyCode::Esc => { - add_logic_state.deactivate_script_editor_autocomplete(); - *command_message = "Autocomplete cancelled".to_string(); - } - _ => { - add_logic_state.deactivate_script_editor_autocomplete(); - *command_message = "Autocomplete cancelled".to_string(); - { - let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); - TextEditor::handle_input( - &mut editor_borrow, - key_event, - &add_logic_state.editor_keybinding_mode, - &mut add_logic_state.vim_state, - ); - } - return true; - } - } - } - - if key_event.code == KeyCode::Char('@') && key_event.modifiers == KeyModifiers::NONE { - let should_trigger = match add_logic_state.editor_keybinding_mode { - EditorKeybindingMode::Vim => *is_edit_mode, - _ => true, - }; - if should_trigger { - let cursor_before = { - let editor_borrow = add_logic_state.script_content_editor.borrow(); - editor_borrow.cursor() - }; - { - let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); - TextEditor::handle_input( - &mut editor_borrow, - key_event, - &add_logic_state.editor_keybinding_mode, - &mut add_logic_state.vim_state, - ); - } - add_logic_state.script_editor_trigger_position = Some(cursor_before); - add_logic_state.script_editor_autocomplete_active = true; - add_logic_state.script_editor_filter_text.clear(); - add_logic_state.update_script_editor_suggestions(); - add_logic_state.has_unsaved_changes = true; - *command_message = "Autocomplete: @ (Tab/↑↓ to navigate, Enter to select, Esc to cancel)".to_string(); - return true; - } - } - - if key_event.code == KeyCode::Esc && key_event.modifiers == KeyModifiers::NONE { - match add_logic_state.editor_keybinding_mode { - EditorKeybindingMode::Vim => { - if *is_edit_mode { + add_logic_state.deactivate_script_editor_autocomplete(); { let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); TextEditor::handle_input( @@ -245,184 +182,252 @@ pub fn handle_add_logic_navigation( &mut add_logic_state.vim_state, ); } - if TextEditor::is_vim_normal_mode(&add_logic_state.vim_state) { - *is_edit_mode = false; - *command_message = "VIM: Normal Mode. Esc again to exit script.".to_string(); + return true; + } + KeyCode::Esc => { + add_logic_state.deactivate_script_editor_autocomplete(); + *command_message = "Autocomplete cancelled".to_string(); + } + _ => { + add_logic_state.deactivate_script_editor_autocomplete(); + *command_message = "Autocomplete cancelled".to_string(); + { + let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); + TextEditor::handle_input( + &mut editor_borrow, + key_event, + &add_logic_state.editor_keybinding_mode, + &mut add_logic_state.vim_state, + ); } - } else { - add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview; - app_state.ui.focus_outside_canvas = true; - *is_edit_mode = false; - *command_message = "Exited script editing.".to_string(); + return true; } } - _ => { - if *is_edit_mode { - *is_edit_mode = false; - *command_message = "Exited script edit. Esc again to exit script.".to_string(); - } else { - add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview; - app_state.ui.focus_outside_canvas = true; - *is_edit_mode = false; - *command_message = "Exited script editing.".to_string(); + } + + if key_event.code == KeyCode::Char('@') && key_event.modifiers == KeyModifiers::NONE { + let should_trigger = match add_logic_state.editor_keybinding_mode { + EditorKeybindingMode::Vim => *is_edit_mode, + _ => true, + }; + if should_trigger { + let cursor_before = { + let editor_borrow = add_logic_state.script_content_editor.borrow(); + editor_borrow.cursor() + }; + { + let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); + TextEditor::handle_input( + &mut editor_borrow, + key_event, + &add_logic_state.editor_keybinding_mode, + &mut add_logic_state.vim_state, + ); + } + add_logic_state.script_editor_trigger_position = Some(cursor_before); + add_logic_state.script_editor_autocomplete_active = true; + add_logic_state.script_editor_filter_text.clear(); + add_logic_state.update_script_editor_suggestions(); + add_logic_state.has_unsaved_changes = true; + *command_message = "Autocomplete: @ (Tab/↑↓ to navigate, Enter to select, Esc to cancel)".to_string(); + return true; + } + } + + if key_event.code == KeyCode::Esc && key_event.modifiers == KeyModifiers::NONE { + match add_logic_state.editor_keybinding_mode { + EditorKeybindingMode::Vim => { + if *is_edit_mode { + { + let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); + TextEditor::handle_input( + &mut editor_borrow, + key_event, + &add_logic_state.editor_keybinding_mode, + &mut add_logic_state.vim_state, + ); + } + if TextEditor::is_vim_normal_mode(&add_logic_state.vim_state) { + *is_edit_mode = false; + *command_message = "VIM: Normal Mode. Esc again to exit script.".to_string(); + } + } else { + add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview; + app_state.ui.focus_outside_canvas = true; + *is_edit_mode = false; + *command_message = "Exited script editing.".to_string(); + } + } + _ => { + if *is_edit_mode { + *is_edit_mode = false; + *command_message = "Exited script edit. Esc again to exit script.".to_string(); + } else { + add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview; + app_state.ui.focus_outside_canvas = true; + *is_edit_mode = false; + *command_message = "Exited script editing.".to_string(); + } } } + return true; + } + + let changed = { + let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); + TextEditor::handle_input( + &mut editor_borrow, + key_event, + &add_logic_state.editor_keybinding_mode, + &mut add_logic_state.vim_state, + ) + }; + if changed { + add_logic_state.has_unsaved_changes = true; + } + if add_logic_state.editor_keybinding_mode == EditorKeybindingMode::Vim { + *is_edit_mode = !TextEditor::is_vim_normal_mode(&add_logic_state.vim_state); } return true; } - let changed = { - let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); - TextEditor::handle_input( - &mut editor_borrow, - key_event, - &add_logic_state.editor_keybinding_mode, - &mut add_logic_state.vim_state, - ) - }; - if changed { - add_logic_state.has_unsaved_changes = true; - } - if add_logic_state.editor_keybinding_mode == EditorKeybindingMode::Vim { - *is_edit_mode = !TextEditor::is_vim_normal_mode(&add_logic_state.vim_state); - } - return true; - } + let action = config.get_general_action(key_event.code, key_event.modifiers); + let current_focus = add_logic_state.current_focus; + let mut handled = true; + let mut new_focus = current_focus; - let action = config.get_general_action(key_event.code, key_event.modifiers); - let current_focus = add_logic_state.current_focus; - let mut handled = true; - let mut new_focus = current_focus; - - match action.as_deref() { - Some("exit_table_scroll") => { - handled = false; - } - Some("move_up") => { - match current_focus { - AddLogicFocus::InputLogicName => {} - AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputLogicName, - AddLogicFocus::InputDescription => new_focus = AddLogicFocus::InputTargetColumn, - AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription, - AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview, - AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton, - _ => handled = false, + match action.as_deref() { + Some("exit_table_scroll") => { + handled = false; } - } - Some("move_down") => { - match current_focus { - AddLogicFocus::InputLogicName => new_focus = AddLogicFocus::InputTargetColumn, - AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputDescription, - AddLogicFocus::InputDescription => { - add_logic_state.last_canvas_field = 2; - new_focus = AddLogicFocus::ScriptContentPreview; - }, - AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton, - AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton, - AddLogicFocus::CancelButton => {} - _ => handled = false, + Some("move_up") => { + match current_focus { + AddLogicFocus::InputLogicName => {} + AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputLogicName, + AddLogicFocus::InputDescription => new_focus = AddLogicFocus::InputTargetColumn, + AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription, + AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview, + AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton, + _ => handled = false, + } } - } - Some("next_option") => { - match current_focus { - AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => + Some("move_down") => { + match current_focus { + AddLogicFocus::InputLogicName => new_focus = AddLogicFocus::InputTargetColumn, + AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputDescription, + AddLogicFocus::InputDescription => { + add_logic_state.last_canvas_field = 2; + new_focus = AddLogicFocus::ScriptContentPreview; + }, + AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton, + AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton, + AddLogicFocus::CancelButton => {} + _ => handled = false, + } + } + Some("next_option") => { + match current_focus { + AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => { new_focus = AddLogicFocus::ScriptContentPreview; } - AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton, - AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton, - AddLogicFocus::CancelButton => { } - _ => handled = false, + AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton, + AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton, + AddLogicFocus::CancelButton => { } + _ => handled = false, + } } - } - Some("previous_option") => { - match current_focus { - AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => + Some("previous_option") => { + match current_focus { + AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => { } - AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription, - AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview, - AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton, - _ => handled = false, - } - } - Some("next_field") => { - new_focus = match current_focus { - AddLogicFocus::InputLogicName => AddLogicFocus::InputTargetColumn, - AddLogicFocus::InputTargetColumn => AddLogicFocus::InputDescription, - AddLogicFocus::InputDescription => AddLogicFocus::ScriptContentPreview, - AddLogicFocus::ScriptContentPreview => AddLogicFocus::SaveButton, - AddLogicFocus::SaveButton => AddLogicFocus::CancelButton, - AddLogicFocus::CancelButton => AddLogicFocus::InputLogicName, - _ => current_focus, - }; - } - Some("prev_field") => { - new_focus = match current_focus { - AddLogicFocus::InputLogicName => AddLogicFocus::CancelButton, - AddLogicFocus::InputTargetColumn => AddLogicFocus::InputLogicName, - AddLogicFocus::InputDescription => AddLogicFocus::InputTargetColumn, - AddLogicFocus::ScriptContentPreview => AddLogicFocus::InputDescription, - AddLogicFocus::SaveButton => AddLogicFocus::ScriptContentPreview, - AddLogicFocus::CancelButton => AddLogicFocus::SaveButton, - _ => current_focus, - }; - } - Some("select") => { - match current_focus { - AddLogicFocus::ScriptContentPreview => { - new_focus = AddLogicFocus::InsideScriptContent; - *is_edit_mode = false; - app_state.ui.focus_outside_canvas = false; - let mode_hint = match add_logic_state.editor_keybinding_mode { - EditorKeybindingMode::Vim => "VIM mode - 'i'/'a'/'o' to edit", - _ => "Enter/Ctrl+E to edit", - }; - *command_message = format!("Fullscreen script editing. {} or Esc to exit.", mode_hint); - } - AddLogicFocus::SaveButton => { - *command_message = "Save logic action".to_string(); - } - AddLogicFocus::CancelButton => { - buffer_state.update_history(AppView::Admin); - router.navigate(Page::Admin(app_state.admin_state().clone())); - *command_message = "Cancelled Add Logic".to_string(); - *is_edit_mode = false; - } - AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => { - *is_edit_mode = !*is_edit_mode; - *command_message = format!("Field edit mode: {}", if *is_edit_mode { "ON" } else { "OFF" }); - } - _ => handled = false, - } - } - Some("toggle_edit_mode") => { - match current_focus { - AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => { - *is_edit_mode = !*is_edit_mode; - *command_message = format!("Canvas field edit mode: {}", if *is_edit_mode { "ON" } else { "OFF" }); - } - _ => { - *command_message = "Cannot toggle edit mode here.".to_string(); + AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription, + AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview, + AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton, + _ => handled = false, } } - } - _ => handled = false, - } + Some("next_field") => { + new_focus = match current_focus { + AddLogicFocus::InputLogicName => AddLogicFocus::InputTargetColumn, + AddLogicFocus::InputTargetColumn => AddLogicFocus::InputDescription, + AddLogicFocus::InputDescription => AddLogicFocus::ScriptContentPreview, + AddLogicFocus::ScriptContentPreview => AddLogicFocus::SaveButton, + AddLogicFocus::SaveButton => AddLogicFocus::CancelButton, + AddLogicFocus::CancelButton => AddLogicFocus::InputLogicName, + _ => current_focus, + }; + } + Some("prev_field") => { + new_focus = match current_focus { + AddLogicFocus::InputLogicName => AddLogicFocus::CancelButton, + AddLogicFocus::InputTargetColumn => AddLogicFocus::InputLogicName, + AddLogicFocus::InputDescription => AddLogicFocus::InputTargetColumn, + AddLogicFocus::ScriptContentPreview => AddLogicFocus::InputDescription, + AddLogicFocus::SaveButton => AddLogicFocus::ScriptContentPreview, + AddLogicFocus::CancelButton => AddLogicFocus::SaveButton, + _ => current_focus, + }; + } + Some("select") => { + match current_focus { + AddLogicFocus::ScriptContentPreview => { + new_focus = AddLogicFocus::InsideScriptContent; + *is_edit_mode = false; + app_state.ui.focus_outside_canvas = false; + let mode_hint = match add_logic_state.editor_keybinding_mode { + EditorKeybindingMode::Vim => "VIM mode - 'i'/'a'/'o' to edit", + _ => "Enter/Ctrl+E to edit", + }; + *command_message = format!("Fullscreen script editing. {} or Esc to exit.", mode_hint); + } + AddLogicFocus::SaveButton => { + *command_message = "Save logic action".to_string(); + } + AddLogicFocus::CancelButton => { + buffer_state.update_history(AppView::Admin); + *command_message = "Cancelled Add Logic".to_string(); + *is_edit_mode = false; - if handled && current_focus != new_focus { - add_logic_state.current_focus = new_focus; - let new_is_canvas_input_focus = matches!(new_focus, - AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription - ); - if new_is_canvas_input_focus { - *is_edit_mode = false; - app_state.ui.focus_outside_canvas = false; - } else { - app_state.ui.focus_outside_canvas = true; - if matches!(new_focus, AddLogicFocus::ScriptContentPreview) { + } + AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => { + *is_edit_mode = !*is_edit_mode; + *command_message = format!("Field edit mode: {}", if *is_edit_mode { "ON" } else { "OFF" }); + } + _ => handled = false, + } + } + Some("toggle_edit_mode") => { + match current_focus { + AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => { + *is_edit_mode = !*is_edit_mode; + *command_message = format!("Canvas field edit mode: {}", if *is_edit_mode { "ON" } else { "OFF" }); + } + _ => { + *command_message = "Cannot toggle edit mode here.".to_string(); + } + } + } + _ => handled = false, + } + + if handled && current_focus != new_focus { + add_logic_state.current_focus = new_focus; + let new_is_canvas_input_focus = matches!(new_focus, + AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription + ); + if new_is_canvas_input_focus { *is_edit_mode = false; + app_state.ui.focus_outside_canvas = false; + } else { + app_state.ui.focus_outside_canvas = true; + if matches!(new_focus, AddLogicFocus::ScriptContentPreview) { + *is_edit_mode = false; + } } } + handled + } else { + return false; // not on AddLogic page } - handled } fn replace_autocomplete_text( diff --git a/client/src/modes/common/command_mode.rs b/client/src/modes/common/command_mode.rs index a0a85c3..9eb7c74 100644 --- a/client/src/modes/common/command_mode.rs +++ b/client/src/modes/common/command_mode.rs @@ -3,20 +3,20 @@ use crossterm::event::{KeyEvent, KeyCode, KeyModifiers}; use crate::config::binds::config::Config; use crate::services::grpc_client::GrpcClient; -use crate::state::{app::state::AppState, pages::auth::LoginState, pages::auth::RegisterState}; +use crate::state::app::state::AppState; use crate::modes::common::commands::CommandHandler; use crate::tui::terminal::core::TerminalCore; use crate::tui::functions::common::form::{save, revert}; use crate::modes::handlers::event::EventOutcome; use crate::tui::functions::common::form::SaveOutcome; +use crate::pages::routing::{Router, Page}; use anyhow::Result; pub async fn handle_command_event( key: KeyEvent, config: &Config, app_state: &mut AppState, - login_state: &LoginState, - register_state: &RegisterState, + router: &mut Router, command_input: &mut String, command_message: &mut String, grpc_client: &mut GrpcClient, @@ -25,20 +25,19 @@ pub async fn handle_command_event( current_position: &mut u64, total_count: u64, ) -> Result { - // Exit command mode (via configurable keybinding) + // Exit command mode if config.is_exit_command_mode(key.code, key.modifiers) { command_input.clear(); *command_message = "".to_string(); return Ok(EventOutcome::Ok("Exited command mode".to_string())); } - // Execute command (via configurable keybinding, defaults to Enter) + // Execute command if config.is_command_execute(key.code, key.modifiers) { return process_command( config, app_state, - login_state, - register_state, + router, command_input, command_message, grpc_client, @@ -46,33 +45,31 @@ pub async fn handle_command_event( terminal, current_position, total_count, - ).await; + ) + .await; } - // Backspace (via configurable keybinding, defaults to Backspace) + // Backspace if config.is_command_backspace(key.code, key.modifiers) { command_input.pop(); return Ok(EventOutcome::Ok("".to_string())); } - // Regular character input - accept any character in command mode + // Regular character input if let KeyCode::Char(c) = key.code { - // Accept regular or shifted characters (e.g., 'a' or 'A') if key.modifiers.is_empty() || key.modifiers == KeyModifiers::SHIFT { command_input.push(c); return Ok(EventOutcome::Ok("".to_string())); } } - // Ignore all other keys Ok(EventOutcome::Ok("".to_string())) } async fn process_command( config: &Config, app_state: &mut AppState, - login_state: &LoginState, - register_state: &RegisterState, + router: &mut Router, command_input: &mut String, command_message: &mut String, grpc_client: &mut GrpcClient, @@ -81,27 +78,18 @@ async fn process_command( current_position: &mut u64, total_count: u64, ) -> Result { - // Clone the trimmed command to avoid borrow issues let command = command_input.trim().to_string(); if command.is_empty() { *command_message = "Empty command".to_string(); return Ok(EventOutcome::Ok(command_message.clone())); } - // Get the action for the command (now checks global and common bindings too) - let action = config.get_action_for_command(&command) - .unwrap_or("unknown"); + let action = config.get_action_for_command(&command).unwrap_or("unknown"); match action { "force_quit" | "save_and_quit" | "quit" => { let (should_exit, message) = command_handler - .handle_command( - action, - terminal, - app_state, - login_state, - register_state, - ) + .handle_command(action, terminal, app_state, router) .await?; command_input.clear(); if should_exit { @@ -109,12 +97,9 @@ async fn process_command( } else { Ok(EventOutcome::Ok(message)) } - }, + } "save" => { - let outcome = save( - app_state, - grpc_client, - ).await?; + let outcome = save(app_state, grpc_client).await?; let message = match outcome { SaveOutcome::CreatedNew(_) => "New entry created".to_string(), SaveOutcome::UpdatedExisting => "Entry updated".to_string(), @@ -122,15 +107,12 @@ async fn process_command( }; command_input.clear(); Ok(EventOutcome::DataSaved(outcome, message)) - }, + } "revert" => { - let message = revert( - app_state, - grpc_client, - ).await?; + let message = revert(app_state, grpc_client).await?; command_input.clear(); Ok(EventOutcome::Ok(message)) - }, + } _ => { let message = format!("Unhandled action: {}", action); command_input.clear(); diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 86e106d..a29a30d 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -369,7 +369,7 @@ impl EventHandler { match current_mode { AppMode::General => { - if let Page::Admin(admin_state) = &router.current { + if let Page::Admin(admin_state) = &mut router.current { if auth_state.role.as_deref() == Some("admin") { if admin_nav::handle_admin_navigation( key_event, @@ -386,24 +386,22 @@ impl EventHandler { } } - if let Page::AddLogic(add_logic_state) = &mut router.current { - let client_clone = self.grpc_client.clone(); - let sender_clone = self.save_logic_result_sender.clone(); - if add_logic_nav::handle_add_logic_navigation( - key_event, - config, - app_state, - add_logic_state, - &mut self.is_edit_mode, - buffer_state, - client_clone, - sender_clone, - &mut self.command_message, - ) { - return Ok(EventOutcome::Ok( + let client_clone = self.grpc_client.clone(); + let sender_clone = self.save_logic_result_sender.clone(); + if add_logic_nav::handle_add_logic_navigation( + key_event, + config, + app_state, + &mut self.is_edit_mode, + buffer_state, + client_clone, + sender_clone, + &mut self.command_message, + router, + ) { + return Ok(EventOutcome::Ok( self.command_message.clone(), - )); - } + )); } if let Page::AddTable(add_table_state) = &mut router.current { @@ -443,7 +441,7 @@ impl EventHandler { buffer_state, index, ); - if let Page::Admin(admin_state) = &router.current { + if let Page::Admin(admin_state) = &mut router.current { if !app_state .profile_tree .profiles @@ -457,7 +455,7 @@ impl EventHandler { format!("Intro Option {} selected", index) } UiContext::Login => { - if let Page::Login(login_state) = &router.current { + if let Page::Login(login_state) = &mut router.current { match index { 0 => login::initiate_login( login_state, @@ -478,7 +476,7 @@ impl EventHandler { } } UiContext::Register => { - if let Page::Register(register_state) = &router.current { + if let Page::Register(register_state) = &mut router.current { match index { 0 => register::initiate_registration( register_state, @@ -807,7 +805,7 @@ impl EventHandler { ) -> Result { match action { "save" => { - if let Page::Login(login_state) = &router.current { + if let Page::Login(login_state) = &mut router.current { let message = crate::tui::functions::common::login::save( auth_state, login_state, @@ -844,7 +842,7 @@ impl EventHandler { )) } "save_and_quit" => { - let message = if let Page::Login(login_state) = &router.current { + let message = if let Page::Login(login_state) = &mut router.current { crate::tui::functions::common::login::save( auth_state, login_state, @@ -873,10 +871,10 @@ impl EventHandler { ))) } "revert" => { - let message = if let Page::Login(login_state) = &router.current { + let message = if let Page::Login(login_state) = &mut router.current { crate::tui::functions::common::login::revert(login_state, app_state) .await - } else if let Page::Register(register_state) = &router.current { + } else if let Page::Register(register_state) = &mut router.current { crate::tui::functions::common::register::revert( register_state, app_state,