router4 compiled

This commit is contained in:
Priec
2025-08-22 23:27:32 +02:00
parent b9072e4d7c
commit 78bc9fc432
3 changed files with 392 additions and 407 deletions

View File

@@ -12,6 +12,7 @@ use anyhow::Result;
use crate::components::common::text_editor::TextEditor; use crate::components::common::text_editor::TextEditor;
use crate::services::ui_service::UiService; use crate::services::ui_service::UiService;
use tui_textarea::CursorMove; use tui_textarea::CursorMove;
use crate::state::pages::admin::AdminState;
use crate::pages::routing::{Router, Page}; use crate::pages::routing::{Router, Page};
pub type SaveLogicResultSender = mpsc::Sender<Result<String>>; pub type SaveLogicResultSender = mpsc::Sender<Result<String>>;
@@ -20,38 +21,23 @@ pub fn handle_add_logic_navigation(
key_event: KeyEvent, key_event: KeyEvent,
config: &Config, config: &Config,
app_state: &mut AppState, app_state: &mut AppState,
add_logic_state: &mut AddLogicState,
is_edit_mode: &mut bool, is_edit_mode: &mut bool,
buffer_state: &mut BufferState, buffer_state: &mut BufferState,
grpc_client: GrpcClient, grpc_client: GrpcClient,
_save_logic_sender: SaveLogicResultSender, // Marked as unused save_logic_sender: SaveLogicResultSender,
command_message: &mut String, command_message: &mut String,
router: &mut Router, router: &mut Router,
) -> bool { ) -> bool {
// === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION === if let Page::AddLogic(add_logic_state) = &mut router.current {
if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent {
// === AUTOCOMPLETE HANDLING === // === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION ===
if add_logic_state.script_editor_autocomplete_active { if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent {
match key_event.code { // === AUTOCOMPLETE HANDLING ===
// ... (Char, Backspace, Tab, Down, Up cases remain the same) ... if add_logic_state.script_editor_autocomplete_active {
KeyCode::Char(c) if c.is_alphanumeric() || c == '_' => { match key_event.code {
add_logic_state.script_editor_filter_text.push(c); // ... (Char, Backspace, Tab, Down, Up cases remain the same) ...
add_logic_state.update_script_editor_suggestions(); KeyCode::Char(c) if c.is_alphanumeric() || c == '_' => {
{ add_logic_state.script_editor_filter_text.push(c);
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();
add_logic_state.update_script_editor_suggestions(); add_logic_state.update_script_editor_suggestions();
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); 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, &mut add_logic_state.vim_state,
); );
} }
*command_message = if add_logic_state.script_editor_filter_text.is_empty() { *command_message = format!("Filtering: @{}", add_logic_state.script_editor_filter_text);
"Autocomplete: @".to_string() return true;
} 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::Backspace => {
} if !add_logic_state.script_editor_filter_text.is_empty() {
KeyCode::Tab | KeyCode::Down => { add_logic_state.script_editor_filter_text.pop();
if !add_logic_state.script_editor_suggestions.is_empty() { add_logic_state.update_script_editor_suggestions();
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 {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); 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" { add_logic_state.deactivate_script_editor_autocomplete();
replace_autocomplete_text(&mut editor_borrow, pos, filter_len, "sql"); add_logic_state.has_unsaved_changes = true;
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);
if is_table_selection { if let Some(pos) = trigger_pos {
editor_borrow.insert_str("."); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
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); if suggestion == "sql" {
add_logic_state.script_editor_autocomplete_active = true; replace_autocomplete_text(&mut editor_borrow, pos, filter_len, "sql");
add_logic_state.script_editor_filter_text.clear(); editor_borrow.insert_str("('')");
add_logic_state.trigger_column_autocomplete_for_table(suggestion.clone()); // Move cursor back twice to be between the single quotes
editor_borrow.move_cursor(CursorMove::Back); // Before ')'
let profile_name = add_logic_state.profile_name.clone(); editor_borrow.move_cursor(CursorMove::Back); // Before ''' (inside '')
let table_name_for_fetch = suggestion.clone(); *command_message = "Inserted: @sql('')".to_string();
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 { } 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();
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 {
{ {
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut();
TextEditor::handle_input( TextEditor::handle_input(
@@ -245,184 +182,252 @@ pub fn handle_add_logic_navigation(
&mut add_logic_state.vim_state, &mut add_logic_state.vim_state,
); );
} }
if TextEditor::is_vim_normal_mode(&add_logic_state.vim_state) { return true;
*is_edit_mode = false; }
*command_message = "VIM: Normal Mode. Esc again to exit script.".to_string(); 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 { return true;
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; if key_event.code == KeyCode::Char('@') && key_event.modifiers == KeyModifiers::NONE {
*command_message = "Exited script edit. Esc again to exit script.".to_string(); let should_trigger = match add_logic_state.editor_keybinding_mode {
} else { EditorKeybindingMode::Vim => *is_edit_mode,
add_logic_state.current_focus = AddLogicFocus::ScriptContentPreview; _ => true,
app_state.ui.focus_outside_canvas = true; };
*is_edit_mode = false; if should_trigger {
*command_message = "Exited script editing.".to_string(); 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; return true;
} }
let changed = { let action = config.get_general_action(key_event.code, key_event.modifiers);
let mut editor_borrow = add_logic_state.script_content_editor.borrow_mut(); let current_focus = add_logic_state.current_focus;
TextEditor::handle_input( let mut handled = true;
&mut editor_borrow, let mut new_focus = current_focus;
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); match action.as_deref() {
let current_focus = add_logic_state.current_focus; Some("exit_table_scroll") => {
let mut handled = true; handled = false;
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,
} }
} Some("move_up") => {
Some("move_down") => { match current_focus {
match current_focus { AddLogicFocus::InputLogicName => {}
AddLogicFocus::InputLogicName => new_focus = AddLogicFocus::InputTargetColumn, AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputLogicName,
AddLogicFocus::InputTargetColumn => new_focus = AddLogicFocus::InputDescription, AddLogicFocus::InputDescription => new_focus = AddLogicFocus::InputTargetColumn,
AddLogicFocus::InputDescription => { AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription,
add_logic_state.last_canvas_field = 2; AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview,
new_focus = AddLogicFocus::ScriptContentPreview; AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton,
}, _ => handled = false,
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton, }
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton,
AddLogicFocus::CancelButton => {}
_ => handled = false,
} }
} Some("move_down") => {
Some("next_option") => { match current_focus {
match current_focus { AddLogicFocus::InputLogicName => new_focus = AddLogicFocus::InputTargetColumn,
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => 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; } { new_focus = AddLogicFocus::ScriptContentPreview; }
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton, AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::SaveButton,
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton, AddLogicFocus::SaveButton => new_focus = AddLogicFocus::CancelButton,
AddLogicFocus::CancelButton => { } AddLogicFocus::CancelButton => { }
_ => handled = false, _ => handled = false,
}
} }
} Some("previous_option") => {
Some("previous_option") => { match current_focus {
match current_focus { AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription =>
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription =>
{ } { }
AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription, AddLogicFocus::ScriptContentPreview => new_focus = AddLogicFocus::InputDescription,
AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview, AddLogicFocus::SaveButton => new_focus = AddLogicFocus::ScriptContentPreview,
AddLogicFocus::CancelButton => new_focus = AddLogicFocus::SaveButton, 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);
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();
} }
} }
} Some("next_field") => {
_ => handled = false, 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; AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => {
let new_is_canvas_input_focus = matches!(new_focus, *is_edit_mode = !*is_edit_mode;
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription *command_message = format!("Field edit mode: {}", if *is_edit_mode { "ON" } else { "OFF" });
); }
if new_is_canvas_input_focus { _ => handled = false,
*is_edit_mode = false; }
app_state.ui.focus_outside_canvas = false; }
} else { Some("toggle_edit_mode") => {
app_state.ui.focus_outside_canvas = true; match current_focus {
if matches!(new_focus, AddLogicFocus::ScriptContentPreview) { 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; *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( fn replace_autocomplete_text(

View File

@@ -3,20 +3,20 @@
use crossterm::event::{KeyEvent, KeyCode, KeyModifiers}; use crossterm::event::{KeyEvent, KeyCode, KeyModifiers};
use crate::config::binds::config::Config; use crate::config::binds::config::Config;
use crate::services::grpc_client::GrpcClient; 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::modes::common::commands::CommandHandler;
use crate::tui::terminal::core::TerminalCore; use crate::tui::terminal::core::TerminalCore;
use crate::tui::functions::common::form::{save, revert}; use crate::tui::functions::common::form::{save, revert};
use crate::modes::handlers::event::EventOutcome; use crate::modes::handlers::event::EventOutcome;
use crate::tui::functions::common::form::SaveOutcome; use crate::tui::functions::common::form::SaveOutcome;
use crate::pages::routing::{Router, Page};
use anyhow::Result; use anyhow::Result;
pub async fn handle_command_event( pub async fn handle_command_event(
key: KeyEvent, key: KeyEvent,
config: &Config, config: &Config,
app_state: &mut AppState, app_state: &mut AppState,
login_state: &LoginState, router: &mut Router,
register_state: &RegisterState,
command_input: &mut String, command_input: &mut String,
command_message: &mut String, command_message: &mut String,
grpc_client: &mut GrpcClient, grpc_client: &mut GrpcClient,
@@ -25,20 +25,19 @@ pub async fn handle_command_event(
current_position: &mut u64, current_position: &mut u64,
total_count: u64, total_count: u64,
) -> Result<EventOutcome> { ) -> Result<EventOutcome> {
// Exit command mode (via configurable keybinding) // Exit command mode
if config.is_exit_command_mode(key.code, key.modifiers) { if config.is_exit_command_mode(key.code, key.modifiers) {
command_input.clear(); command_input.clear();
*command_message = "".to_string(); *command_message = "".to_string();
return Ok(EventOutcome::Ok("Exited command mode".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) { if config.is_command_execute(key.code, key.modifiers) {
return process_command( return process_command(
config, config,
app_state, app_state,
login_state, router,
register_state,
command_input, command_input,
command_message, command_message,
grpc_client, grpc_client,
@@ -46,33 +45,31 @@ pub async fn handle_command_event(
terminal, terminal,
current_position, current_position,
total_count, total_count,
).await; )
.await;
} }
// Backspace (via configurable keybinding, defaults to Backspace) // Backspace
if config.is_command_backspace(key.code, key.modifiers) { if config.is_command_backspace(key.code, key.modifiers) {
command_input.pop(); command_input.pop();
return Ok(EventOutcome::Ok("".to_string())); 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 { 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 { if key.modifiers.is_empty() || key.modifiers == KeyModifiers::SHIFT {
command_input.push(c); command_input.push(c);
return Ok(EventOutcome::Ok("".to_string())); return Ok(EventOutcome::Ok("".to_string()));
} }
} }
// Ignore all other keys
Ok(EventOutcome::Ok("".to_string())) Ok(EventOutcome::Ok("".to_string()))
} }
async fn process_command( async fn process_command(
config: &Config, config: &Config,
app_state: &mut AppState, app_state: &mut AppState,
login_state: &LoginState, router: &mut Router,
register_state: &RegisterState,
command_input: &mut String, command_input: &mut String,
command_message: &mut String, command_message: &mut String,
grpc_client: &mut GrpcClient, grpc_client: &mut GrpcClient,
@@ -81,27 +78,18 @@ async fn process_command(
current_position: &mut u64, current_position: &mut u64,
total_count: u64, total_count: u64,
) -> Result<EventOutcome> { ) -> Result<EventOutcome> {
// Clone the trimmed command to avoid borrow issues
let command = command_input.trim().to_string(); let command = command_input.trim().to_string();
if command.is_empty() { if command.is_empty() {
*command_message = "Empty command".to_string(); *command_message = "Empty command".to_string();
return Ok(EventOutcome::Ok(command_message.clone())); 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 { match action {
"force_quit" | "save_and_quit" | "quit" => { "force_quit" | "save_and_quit" | "quit" => {
let (should_exit, message) = command_handler let (should_exit, message) = command_handler
.handle_command( .handle_command(action, terminal, app_state, router)
action,
terminal,
app_state,
login_state,
register_state,
)
.await?; .await?;
command_input.clear(); command_input.clear();
if should_exit { if should_exit {
@@ -109,12 +97,9 @@ async fn process_command(
} else { } else {
Ok(EventOutcome::Ok(message)) Ok(EventOutcome::Ok(message))
} }
}, }
"save" => { "save" => {
let outcome = save( let outcome = save(app_state, grpc_client).await?;
app_state,
grpc_client,
).await?;
let message = match outcome { let message = match outcome {
SaveOutcome::CreatedNew(_) => "New entry created".to_string(), SaveOutcome::CreatedNew(_) => "New entry created".to_string(),
SaveOutcome::UpdatedExisting => "Entry updated".to_string(), SaveOutcome::UpdatedExisting => "Entry updated".to_string(),
@@ -122,15 +107,12 @@ async fn process_command(
}; };
command_input.clear(); command_input.clear();
Ok(EventOutcome::DataSaved(outcome, message)) Ok(EventOutcome::DataSaved(outcome, message))
}, }
"revert" => { "revert" => {
let message = revert( let message = revert(app_state, grpc_client).await?;
app_state,
grpc_client,
).await?;
command_input.clear(); command_input.clear();
Ok(EventOutcome::Ok(message)) Ok(EventOutcome::Ok(message))
}, }
_ => { _ => {
let message = format!("Unhandled action: {}", action); let message = format!("Unhandled action: {}", action);
command_input.clear(); command_input.clear();

View File

@@ -369,7 +369,7 @@ impl EventHandler {
match current_mode { match current_mode {
AppMode::General => { 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 auth_state.role.as_deref() == Some("admin") {
if admin_nav::handle_admin_navigation( if admin_nav::handle_admin_navigation(
key_event, 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 client_clone = self.grpc_client.clone(); let sender_clone = self.save_logic_result_sender.clone();
let sender_clone = self.save_logic_result_sender.clone(); if add_logic_nav::handle_add_logic_navigation(
if add_logic_nav::handle_add_logic_navigation( key_event,
key_event, config,
config, app_state,
app_state, &mut self.is_edit_mode,
add_logic_state, buffer_state,
&mut self.is_edit_mode, client_clone,
buffer_state, sender_clone,
client_clone, &mut self.command_message,
sender_clone, router,
&mut self.command_message, ) {
) { return Ok(EventOutcome::Ok(
return Ok(EventOutcome::Ok(
self.command_message.clone(), self.command_message.clone(),
)); ));
}
} }
if let Page::AddTable(add_table_state) = &mut router.current { if let Page::AddTable(add_table_state) = &mut router.current {
@@ -443,7 +441,7 @@ impl EventHandler {
buffer_state, buffer_state,
index, index,
); );
if let Page::Admin(admin_state) = &router.current { if let Page::Admin(admin_state) = &mut router.current {
if !app_state if !app_state
.profile_tree .profile_tree
.profiles .profiles
@@ -457,7 +455,7 @@ impl EventHandler {
format!("Intro Option {} selected", index) format!("Intro Option {} selected", index)
} }
UiContext::Login => { UiContext::Login => {
if let Page::Login(login_state) = &router.current { if let Page::Login(login_state) = &mut router.current {
match index { match index {
0 => login::initiate_login( 0 => login::initiate_login(
login_state, login_state,
@@ -478,7 +476,7 @@ impl EventHandler {
} }
} }
UiContext::Register => { UiContext::Register => {
if let Page::Register(register_state) = &router.current { if let Page::Register(register_state) = &mut router.current {
match index { match index {
0 => register::initiate_registration( 0 => register::initiate_registration(
register_state, register_state,
@@ -807,7 +805,7 @@ impl EventHandler {
) -> Result<EventOutcome> { ) -> Result<EventOutcome> {
match action { match action {
"save" => { "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( let message = crate::tui::functions::common::login::save(
auth_state, auth_state,
login_state, login_state,
@@ -844,7 +842,7 @@ impl EventHandler {
)) ))
} }
"save_and_quit" => { "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( crate::tui::functions::common::login::save(
auth_state, auth_state,
login_state, login_state,
@@ -873,10 +871,10 @@ impl EventHandler {
))) )))
} }
"revert" => { "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) crate::tui::functions::common::login::revert(login_state, app_state)
.await .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( crate::tui::functions::common::register::revert(
register_state, register_state,
app_state, app_state,