diff --git a/client/src/components/admin/add_table.rs b/client/src/components/admin/add_table.rs index 2002aff..47ac9d6 100644 --- a/client/src/components/admin/add_table.rs +++ b/client/src/components/admin/add_table.rs @@ -3,7 +3,7 @@ use crate::config::colors::themes::Theme; use crate::state::app::highlight::HighlightState; use crate::state::app::state::AppState; use crate::state::pages::add_table::{AddTableFocus, AddTableState}; -use canvas::canvas::{render_canvas, CanvasState, HighlightState as CanvasHighlightState}; // Use canvas library +use canvas::canvas::{render_canvas, CanvasState, HighlightState as CanvasHighlightState}; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Modifier, Style}, diff --git a/client/src/components/handlers/canvas.rs b/client/src/components/handlers/canvas.rs index ddf8e34..8afb855 100644 --- a/client/src/components/handlers/canvas.rs +++ b/client/src/components/handlers/canvas.rs @@ -9,43 +9,14 @@ use ratatui::{ }; use crate::config::colors::themes::Theme; use crate::state::app::highlight::HighlightState; -use crate::state::pages::canvas_state::CanvasState as LegacyCanvasState; -use canvas::canvas::CanvasState as LibraryCanvasState; +use canvas::canvas::CanvasState; use std::cmp::{max, min}; -/// Render canvas for legacy CanvasState (AddTableState, LoginState, RegisterState, AddLogicState) +/// Render canvas (FormState) pub fn render_canvas( f: &mut Frame, area: Rect, - form_state: &impl LegacyCanvasState, - fields: &[&str], - current_field_idx: &usize, - inputs: &[&String], - theme: &Theme, - is_edit_mode: bool, - highlight_state: &HighlightState, -) -> Option { - render_canvas_impl( - f, - area, - fields, - current_field_idx, - inputs, - theme, - is_edit_mode, - highlight_state, - form_state.current_cursor_pos(), - form_state.has_unsaved_changes(), - |i| form_state.get_display_value_for_field(i).to_string(), - |i| form_state.has_display_override(i), - ) -} - -/// Render canvas for library CanvasState (FormState) -pub fn render_canvas_library( - f: &mut Frame, - area: Rect, - form_state: &impl LibraryCanvasState, + form_state: &impl CanvasState, fields: &[&str], current_field_idx: &usize, inputs: &[&String], diff --git a/client/src/modes/canvas/read_only.rs b/client/src/modes/canvas/read_only.rs index 92b7f10..c65ec04 100644 --- a/client/src/modes/canvas/read_only.rs +++ b/client/src/modes/canvas/read_only.rs @@ -5,7 +5,6 @@ use crate::config::binds::key_sequences::KeySequenceTracker; use crate::services::grpc_client::GrpcClient; use crate::state::pages::auth::LoginState; use crate::state::pages::auth::RegisterState; -use crate::state::pages::canvas_state::CanvasState as LocalCanvasState; use crate::state::pages::form::FormState; use crate::state::pages::add_logic::AddLogicState; use crate::state::pages::add_table::AddTableState; diff --git a/client/src/modes/handlers.rs b/client/src/modes/handlers.rs index f78843c..01db6da 100644 --- a/client/src/modes/handlers.rs +++ b/client/src/modes/handlers.rs @@ -1,4 +1,3 @@ // src/modes/handlers.rs pub mod event; -pub mod event_helper; pub mod mode_manager; diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 2a329b1..ea131c2 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -15,23 +15,20 @@ use crate::modes::{ general::{dialog, navigation}, handlers::mode_manager::{AppMode, ModeManager}, }; -use crate::state::pages::canvas_state::CanvasState as LegacyCanvasState; use crate::services::auth::AuthClient; use crate::services::grpc_client::GrpcClient; use canvas::{canvas::CanvasAction, dispatcher::ActionDispatcher}; -use canvas::canvas::CanvasState as LibraryCanvasState; -use super::event_helper::*; +use canvas::canvas::CanvasState; // Only need this import now use crate::state::{ app::{ buffer::{AppView, BufferState}, highlight::HighlightState, - search::SearchState, // Correctly imported + search::SearchState, state::AppState, }, pages::{ admin::AdminState, auth::{AuthState, LoginState, RegisterState}, - canvas_state::CanvasState, form::FormState, intro::IntroState, }, @@ -89,7 +86,6 @@ pub struct EventHandler { pub navigation_state: NavigationState, pub search_result_sender: mpsc::UnboundedSender>, pub search_result_receiver: mpsc::UnboundedReceiver>, - // --- ADDED FOR LIVE AUTOCOMPLETE --- pub autocomplete_result_sender: mpsc::UnboundedSender>, pub autocomplete_result_receiver: mpsc::UnboundedReceiver>, } @@ -103,7 +99,7 @@ impl EventHandler { grpc_client: GrpcClient, ) -> Result { let (search_tx, search_rx) = unbounded_channel(); - let (autocomplete_tx, autocomplete_rx) = unbounded_channel(); // ADDED + let (autocomplete_tx, autocomplete_rx) = unbounded_channel(); Ok(EventHandler { command_mode: false, command_input: String::new(), @@ -122,7 +118,6 @@ impl EventHandler { navigation_state: NavigationState::new(), search_result_sender: search_tx, search_result_receiver: search_rx, - // --- ADDED --- autocomplete_result_sender: autocomplete_tx, autocomplete_result_receiver: autocomplete_rx, }) @@ -136,6 +131,95 @@ impl EventHandler { self.navigation_state.activate_find_file(options); } + // Helper functions - replace the removed event_helper functions + fn get_current_field_for_state( + app_state: &AppState, + login_state: &LoginState, + register_state: &RegisterState, + form_state: &FormState, + ) -> usize { + if app_state.ui.show_login { + login_state.current_field() + } else if app_state.ui.show_register { + register_state.current_field() + } else { + form_state.current_field() + } + } + + fn get_current_cursor_pos_for_state( + app_state: &AppState, + login_state: &LoginState, + register_state: &RegisterState, + form_state: &FormState, + ) -> usize { + if app_state.ui.show_login { + login_state.current_cursor_pos() + } else if app_state.ui.show_register { + register_state.current_cursor_pos() + } else { + form_state.current_cursor_pos() + } + } + + fn get_has_unsaved_changes_for_state( + app_state: &AppState, + login_state: &LoginState, + register_state: &RegisterState, + form_state: &FormState, + ) -> bool { + if app_state.ui.show_login { + login_state.has_unsaved_changes() + } else if app_state.ui.show_register { + register_state.has_unsaved_changes() + } else { + form_state.has_unsaved_changes() + } + } + + fn get_current_input_for_state<'a>( + app_state: &AppState, + login_state: &'a LoginState, + register_state: &'a RegisterState, + form_state: &'a FormState, + ) -> &'a str { + if app_state.ui.show_login { + login_state.get_current_input() + } else if app_state.ui.show_register { + register_state.get_current_input() + } else { + form_state.get_current_input() + } + } + + fn set_current_cursor_pos_for_state( + app_state: &AppState, + login_state: &mut LoginState, + register_state: &mut RegisterState, + form_state: &mut FormState, + pos: usize, + ) { + if app_state.ui.show_login { + login_state.set_current_cursor_pos(pos); + } else if app_state.ui.show_register { + register_state.set_current_cursor_pos(pos); + } else { + form_state.set_current_cursor_pos(pos); + } + } + + fn get_cursor_pos_for_mixed_state( + app_state: &AppState, + login_state: &LoginState, + form_state: &FormState, + ) -> usize { + if app_state.ui.show_login || app_state.ui.show_register { + login_state.current_cursor_pos() + } else { + form_state.current_cursor_pos() + } + } + // This function handles state changes. async fn handle_search_palette_event( &mut self, @@ -199,7 +283,6 @@ impl EventHandler { _ => {} } - // --- START CORRECTED LOGIC --- if trigger_search { search_state.is_loading = true; search_state.results.clear(); @@ -214,7 +297,6 @@ impl EventHandler { "--- 1. Spawning search task for query: '{}' ---", query ); - // We now move the grpc_client into the task, just like with login. tokio::spawn(async move { info!("--- 2. Background task started. ---"); match grpc_client.search_table(table_name, query).await { @@ -226,7 +308,6 @@ impl EventHandler { let _ = sender.send(response.hits); } Err(e) => { - // THE FIX: Use the debug formatter `{:?}` to print the full error chain. error!("--- 3b. gRPC call failed: {:?} ---", e); let _ = sender.send(vec![]); } @@ -235,8 +316,6 @@ impl EventHandler { } } - // The borrow on `app_state.search_state` ends here. - // Now we can safely modify the Option itself. if should_close { app_state.search_state = None; app_state.ui.show_search_palette = false; @@ -264,7 +343,6 @@ impl EventHandler { ) -> Result { if app_state.ui.show_search_palette { if let Event::Key(key_event) = event { - // The call no longer passes grpc_client return self .handle_search_palette_event( key_event, @@ -581,7 +659,7 @@ impl EventHandler { if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_highlight_mode_linewise") && ModeManager::can_enter_highlight_mode(current_mode) { - let current_field_index = get_current_field_for_state( + let current_field_index = Self::get_current_field_for_state( app_state, login_state, register_state, @@ -596,13 +674,13 @@ impl EventHandler { else if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_highlight_mode") && ModeManager::can_enter_highlight_mode(current_mode) { - let current_field_index = get_current_field_for_state( + let current_field_index = Self::get_current_field_for_state( app_state, login_state, register_state, form_state ); - let current_cursor_pos = get_current_cursor_pos_for_state( + let current_cursor_pos = Self::get_current_cursor_pos_for_state( app_state, login_state, register_state, @@ -627,13 +705,13 @@ impl EventHandler { else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() == Some("enter_edit_mode_after") && ModeManager::can_enter_edit_mode(current_mode) { - let current_input = get_current_input_for_state( + let current_input = Self::get_current_input_for_state( app_state, login_state, register_state, form_state ); - let current_cursor_pos = get_cursor_pos_for_mixed_state( + let current_cursor_pos = Self::get_cursor_pos_for_mixed_state( app_state, login_state, form_state @@ -642,14 +720,14 @@ impl EventHandler { // Move cursor forward if possible if !current_input.is_empty() && current_cursor_pos < current_input.len() { let new_cursor_pos = current_cursor_pos + 1; - set_current_cursor_pos_for_state( + Self::set_current_cursor_pos_for_state( app_state, login_state, register_state, form_state, new_cursor_pos ); - self.ideal_cursor_column = get_current_cursor_pos_for_state( + self.ideal_cursor_column = Self::get_current_cursor_pos_for_state( app_state, login_state, register_state, @@ -694,13 +772,13 @@ impl EventHandler { } } - // Try canvas action for form first (NEW: Canvas library integration) + // Try canvas action for form first if app_state.ui.show_form { if let Ok(Some(canvas_message)) = self.handle_form_canvas_action( key_event, config, form_state, - false, // not edit mode + false, ).await { return Ok(EventOutcome::Ok(canvas_message)); } @@ -753,7 +831,7 @@ impl EventHandler { &mut admin_state.add_table_state, &mut admin_state.add_logic_state, &mut self.key_sequence_tracker, - &mut self.grpc_client, // <-- FIX 2 + &mut self.grpc_client, &mut self.command_message, &mut self.edit_mode_cooldown, &mut self.ideal_cursor_column, @@ -784,13 +862,13 @@ impl EventHandler { } } - // Try canvas action for form first (NEW: Canvas library integration) + // Try canvas action for form first if app_state.ui.show_form { if let Ok(Some(canvas_message)) = self.handle_form_canvas_action( key_event, config, form_state, - true, // edit mode + true, ).await { if !canvas_message.is_empty() { self.command_message = canvas_message.clone(); @@ -823,7 +901,7 @@ impl EventHandler { self.edit_mode_cooldown = true; // Check for unsaved changes across all states - let has_changes = get_has_unsaved_changes_for_state( + let has_changes = Self::get_has_unsaved_changes_for_state( app_state, login_state, register_state, @@ -840,13 +918,13 @@ impl EventHandler { terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?; // Get current input and cursor position - let current_input = get_current_input_for_state( + let current_input = Self::get_current_input_for_state( app_state, login_state, register_state, form_state ); - let current_cursor_pos = get_current_cursor_pos_for_state( + let current_cursor_pos = Self::get_current_cursor_pos_for_state( app_state, login_state, register_state, @@ -856,7 +934,7 @@ impl EventHandler { // Adjust cursor if it's beyond the input length if !current_input.is_empty() && current_cursor_pos >= current_input.len() { let new_pos = current_input.len() - 1; - set_current_cursor_pos_for_state( + Self::set_current_cursor_pos_for_state( app_state, login_state, register_state, @@ -906,7 +984,7 @@ impl EventHandler { form_state, &mut self.command_input, &mut self.command_message, - &mut self.grpc_client, // <-- FIX 5 + &mut self.grpc_client, command_handler, terminal, &mut current_position, @@ -1024,11 +1102,10 @@ impl EventHandler { async fn handle_form_canvas_action( &mut self, key_event: KeyEvent, - _config: &Config, // Not used anymore - canvas has its own config + _config: &Config, form_state: &mut FormState, is_edit_mode: bool, ) -> Result> { - // Load canvas config (canvas_config.toml or vim defaults) let canvas_config = canvas::config::CanvasConfig::load(); // Handle suggestion actions first if suggestions are active @@ -1083,7 +1160,6 @@ impl EventHandler { } } - // FIXED: Use canvas config instead of client config let action_str = canvas_config.get_action_for_key( key_event.code, key_event.modifiers, @@ -1092,9 +1168,8 @@ impl EventHandler { ); if let Some(action_str) = action_str { - // Filter out mode transition actions - let legacy handlers deal with these if Self::is_mode_transition_action(action_str) { - return Ok(None); // Let legacy handler handle mode transitions + return Ok(None); } let canvas_action = CanvasAction::from_string(&action_str); @@ -1131,7 +1206,6 @@ impl EventHandler { } else { // In read-only mode, only handle non-character keys let canvas_action = match key_event.code { - // Only handle special keys that don't conflict with vim bindings KeyCode::Left => Some(CanvasAction::MoveLeft), KeyCode::Right => Some(CanvasAction::MoveRight), KeyCode::Up => Some(CanvasAction::MoveUp), @@ -1164,7 +1238,6 @@ impl EventHandler { Ok(None) } - // ADDED: Helper function to identify mode transition actions fn is_mode_transition_action(action: &str) -> bool { matches!(action, "exit" | @@ -1181,11 +1254,11 @@ impl EventHandler { "force_quit" | "save_and_quit" | "revert" | - "enter_decider" | // This is also handled specially by legacy system - "trigger_autocomplete" | // This is handled specially by legacy system - "suggestion_up" | // These are handled above in suggestion logic + "enter_decider" | + "trigger_autocomplete" | + "suggestion_up" | "suggestion_down" | - "previous_entry" | // Navigation between records + "previous_entry" | "next_entry" | "toggle_sidebar" | "toggle_buffer_list" | diff --git a/client/src/modes/handlers/event_helper.rs b/client/src/modes/handlers/event_helper.rs deleted file mode 100644 index 184ec4c..0000000 --- a/client/src/modes/handlers/event_helper.rs +++ /dev/null @@ -1,105 +0,0 @@ - -// src/modes/handlers/event_helper.rs -//! Helper functions to handle the differences between legacy and library CanvasState traits - -use crate::state::app::state::AppState; -use crate::state::pages::{ - form::FormState, - auth::{LoginState, RegisterState}, -}; -use crate::state::pages::canvas_state::CanvasState as LegacyCanvasState; -use canvas::canvas::CanvasState as LibraryCanvasState; - -/// Get the current field index from the appropriate state based on which UI is active -pub fn get_current_field_for_state( - app_state: &AppState, - login_state: &LoginState, - register_state: &RegisterState, - form_state: &FormState, -) -> usize { - if app_state.ui.show_login { - login_state.current_field() // Uses LegacyCanvasState - } else if app_state.ui.show_register { - register_state.current_field() // Uses LegacyCanvasState - } else { - form_state.current_field() // Uses LibraryCanvasState - } -} - -/// Get the current cursor position from the appropriate state based on which UI is active -pub fn get_current_cursor_pos_for_state( - app_state: &AppState, - login_state: &LoginState, - register_state: &RegisterState, - form_state: &FormState, -) -> usize { - if app_state.ui.show_login { - login_state.current_cursor_pos() // Uses LegacyCanvasState - } else if app_state.ui.show_register { - register_state.current_cursor_pos() // Uses LegacyCanvasState - } else { - form_state.current_cursor_pos() // Uses LibraryCanvasState - } -} - -/// Check if the appropriate state has unsaved changes based on which UI is active -pub fn get_has_unsaved_changes_for_state( - app_state: &AppState, - login_state: &LoginState, - register_state: &RegisterState, - form_state: &FormState, -) -> bool { - if app_state.ui.show_login { - login_state.has_unsaved_changes() // Uses LegacyCanvasState - } else if app_state.ui.show_register { - register_state.has_unsaved_changes() // Uses LegacyCanvasState - } else { - form_state.has_unsaved_changes() // Uses LibraryCanvasState - } -} - -/// Get the current input from the appropriate state based on which UI is active -pub fn get_current_input_for_state<'a>( - app_state: &AppState, - login_state: &'a LoginState, - register_state: &'a RegisterState, - form_state: &'a FormState, -) -> &'a str { - if app_state.ui.show_login { - login_state.get_current_input() // Uses LegacyCanvasState - } else if app_state.ui.show_register { - register_state.get_current_input() // Uses LegacyCanvasState - } else { - form_state.get_current_input() // Uses LibraryCanvasState - } -} - -/// Set the cursor position for the appropriate state based on which UI is active -pub fn set_current_cursor_pos_for_state( - app_state: &AppState, - login_state: &mut LoginState, - register_state: &mut RegisterState, - form_state: &mut FormState, - pos: usize, -) { - if app_state.ui.show_login { - login_state.set_current_cursor_pos(pos); // Uses LegacyCanvasState - } else if app_state.ui.show_register { - register_state.set_current_cursor_pos(pos); // Uses LegacyCanvasState - } else { - form_state.set_current_cursor_pos(pos); // Uses LibraryCanvasState - } -} - -/// Get cursor position for mixed login/register vs form logic -pub fn get_cursor_pos_for_mixed_state( - app_state: &AppState, - login_state: &LoginState, - form_state: &FormState, -) -> usize { - if app_state.ui.show_login || app_state.ui.show_register { - login_state.current_cursor_pos() // Uses LegacyCanvasState - } else { - form_state.current_cursor_pos() // Uses LibraryCanvasState - } -} diff --git a/client/src/ui/handlers/render.rs b/client/src/ui/handlers/render.rs index 19ca255..5ef0a4b 100644 --- a/client/src/ui/handlers/render.rs +++ b/client/src/ui/handlers/render.rs @@ -16,11 +16,10 @@ use crate::components::{ }; use crate::config::colors::themes::Theme; use crate::modes::general::command_navigation::NavigationState; -use crate::state::pages::canvas_state::CanvasState as LocalCanvasState; // Keep local one with alias -use canvas::canvas::CanvasState; // Import external library's CanvasState trait +use canvas::canvas::CanvasState; use crate::state::app::buffer::BufferState; -use crate::state::app::highlight::HighlightState as LocalHighlightState; // CHANGED: Alias local version -use canvas::canvas::HighlightState as CanvasHighlightState; // CHANGED: Import canvas version with alias +use crate::state::app::highlight::HighlightState as LocalHighlightState; +use canvas::canvas::HighlightState as CanvasHighlightState; use crate::state::app::state::AppState; use crate::state::pages::admin::AdminState; use crate::state::pages::auth::AuthState; diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index 7d493b0..c7390bc 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -8,9 +8,8 @@ use crate::config::storage::storage::load_auth_data; use crate::modes::common::commands::CommandHandler; use crate::modes::handlers::event::{EventHandler, EventOutcome}; use crate::modes::handlers::mode_manager::{AppMode, ModeManager}; -use crate::state::pages::canvas_state::CanvasState as LocalCanvasState; // Keep local one with alias -use canvas::canvas::CanvasState; // Import external library's CanvasState trait -use crate::state::pages::form::{FormState, FieldDefinition}; // Import FieldDefinition +use canvas::canvas::CanvasState; // Only external library import +use crate::state::pages::form::{FormState, FieldDefinition}; use crate::state::pages::auth::AuthState; use crate::state::pages::auth::LoginState; use crate::state::pages::auth::RegisterState; diff --git a/client/tests/form/gui/form_tests.rs b/client/tests/form/gui/form_tests.rs index 334ae07..03e2383 100644 --- a/client/tests/form/gui/form_tests.rs +++ b/client/tests/form/gui/form_tests.rs @@ -2,8 +2,7 @@ use rstest::{fixture, rstest}; use std::collections::HashMap; use client::state::pages::form::{FormState, FieldDefinition}; -use canvas::state::CanvasState -use client::state::pages::canvas_state::CanvasState; +use canvas::canvas::CanvasState; #[fixture] fn test_form_state() -> FormState { diff --git a/client/tests/form/requests/form_request_tests.rs b/client/tests/form/requests/form_request_tests.rs index 90f8145..8e25d2b 100644 --- a/client/tests/form/requests/form_request_tests.rs +++ b/client/tests/form/requests/form_request_tests.rs @@ -2,7 +2,7 @@ pub use rstest::{fixture, rstest}; pub use client::services::grpc_client::GrpcClient; pub use client::state::pages::form::FormState; -pub use client::state::pages::canvas_state::CanvasState; +pub use canvas::canvas::CanvasState; pub use prost_types::Value; pub use prost_types::value::Kind; pub use std::collections::HashMap;