diff --git a/client/config.toml b/client/config.toml index bb1bbca..121e747 100644 --- a/client/config.toml +++ b/client/config.toml @@ -83,6 +83,7 @@ quit = ["q"] force_quit = ["q!"] save_and_quit = ["wq"] revert = ["r"] +find_file_palette_toggle = ["ff"] [editor] keybinding_mode = "vim" # Options: "default", "vim", "emacs" diff --git a/client/src/components/common/command_line.rs b/client/src/components/common/command_line.rs index c1a35cc..970dc3a 100644 --- a/client/src/components/common/command_line.rs +++ b/client/src/components/common/command_line.rs @@ -6,27 +6,37 @@ use ratatui::{ Frame, }; use crate::config::colors::themes::Theme; +pub fn render_command_line( + f: &mut Frame, + area: Rect, + input: &str, // This is event_handler.command_input + active: bool, // This is event_handler.command_mode + theme: &Theme, + message: &str, // This is event_handler.command_message + // Palette-specific parameters are removed +) { + // This function now only renders the normal command line. + // The find_file_palette_active check in render_ui ensures this is called appropriately. -pub fn render_command_line(f: &mut Frame, area: Rect, input: &str, active: bool, theme: &Theme, message: &str) { - let prompt = if active { - ":" - } else { - "" - }; + if !active { // If not in normal command mode, render minimally or nothing + let paragraph = Paragraph::new("") + .block(Block::default().style(Style::default().bg(theme.bg))); + f.render_widget(paragraph, area); + return; + } - // Combine the prompt, input, and message - let display_text = if message.is_empty() { + let prompt = ":"; + let display_text = if message.is_empty() || message == ":" { format!("{}{}", prompt, input) } else { - format!("{}{} | {}", prompt, input, message) - }; - - let style = if active { - Style::default().fg(theme.accent) - } else { - Style::default().fg(theme.fg) + if input.is_empty() { // If command was just executed, input is cleared, show message + message.to_string() + } else { // Show input and message + format!("{}{} | {}", prompt, input, message) + } }; + let style = Style::default().fg(theme.accent); // Style for active command line let paragraph = Paragraph::new(display_text) .block(Block::default().style(Style::default().bg(theme.bg))) .style(style); diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index d0cd8f7..9041c3d 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -43,6 +43,7 @@ use crate::tui::functions::common::register::RegisterResult; use crate::functions::modes::navigation::add_table_nav::SaveTableResultSender; use crate::functions::modes::navigation::add_logic_nav::SaveLogicResultSender; use crate::functions::modes::navigation::add_logic_nav; +use crossterm::event::{KeyCode, KeyModifiers}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum EventOutcome { @@ -66,6 +67,10 @@ pub struct EventHandler { pub register_result_sender: mpsc::Sender, pub save_table_result_sender: SaveTableResultSender, pub save_logic_result_sender: SaveLogicResultSender, + pub find_file_palette_active: bool, + pub find_file_options: Vec, + pub find_file_selected_index: Option, + pub find_file_input: String, } impl EventHandler { @@ -83,15 +88,30 @@ impl EventHandler { highlight_state: HighlightState::Off, edit_mode_cooldown: false, ideal_cursor_column: 0, - key_sequence_tracker: KeySequenceTracker::new(800), + key_sequence_tracker: KeySequenceTracker::new(400), auth_client: AuthClient::new().await?, login_result_sender, register_result_sender, save_table_result_sender, save_logic_result_sender, + find_file_palette_active: false, + find_file_options: vec![ + "src/main.rs".to_string(), + "src/lib.rs".to_string(), + "Cargo.toml".to_string(), + "README.md".to_string(), + "config.toml".to_string(), + "src/ui/handlers/ui.rs".to_string(), + "src/modes/handlers/event.rs".to_string(), + "another_file.txt".to_string(), + "yet_another_one.md".to_string(), + ], + find_file_selected_index: None, + find_file_input: String::new(), }) } + #[allow(clippy::too_many_arguments)] pub async fn handle_event( &mut self, event: Event, @@ -110,6 +130,25 @@ impl EventHandler { total_count: u64, current_position: &mut u64, ) -> Result { + // Handle find file palette first + if self.find_file_palette_active { + if let Event::Key(key) = event { + if key.code == KeyCode::Esc { + self.find_file_palette_active = false; + self.find_file_input.clear(); + self.find_file_selected_index = None; + self.command_message = "Find File palette closed".to_string(); + return Ok(EventOutcome::Ok(self.command_message.clone())); + } + if let KeyCode::Char(c) = key.code { + self.find_file_input.push(c); + } else if key.code == KeyCode::Backspace { + self.find_file_input.pop(); + } + return Ok(EventOutcome::Ok("Palette event consumed".to_string())); + } + } + let current_mode = ModeManager::derive_mode(app_state, self, admin_state); app_state.update_mode(current_mode); @@ -121,7 +160,7 @@ impl EventHandler { else if ui.show_admin { AppView::Admin } else if ui.show_add_logic { AppView::AddLogic } else if ui.show_add_table { AppView::AddTable } - else if ui.show_form { AppView::Form } // Remove the dynamic name part + else if ui.show_form { AppView::Form } else { AppView::Scratch } }; buffer_state.update_history(current_view); @@ -158,7 +197,6 @@ impl EventHandler { return Ok(EventOutcome::Ok(message)); } - if !matches!(current_mode, AppMode::Edit | AppMode::Command) { if let Some(action) = config.get_action_for_key_in_mode( &config.keybindings.global, key_code, modifiers @@ -174,13 +212,8 @@ impl EventHandler { return Ok(EventOutcome::Ok("Switched to previous buffer".to_string())); } } - //"close_buffer" => { - // let message = buffer_state.close_buffer_with_intro_fallback(); - // return Ok(EventOutcome::Ok(message)); - //} "close_buffer" => { - // TODO: Replace with actual table name from server response - let current_table_name = Some("2025_customer"); // Your hardcoded table name + let current_table_name = Some("2025_customer"); let message = buffer_state.close_buffer_with_intro_fallback(current_table_name); return Ok(EventOutcome::Ok(message)); } @@ -191,7 +224,6 @@ impl EventHandler { match current_mode { AppMode::General => { - // Prioritize Admin Panel navigation if it's visible if app_state.ui.show_admin && auth_state.role.as_deref() == Some("admin") { if admin_nav::handle_admin_navigation( @@ -205,7 +237,7 @@ impl EventHandler { return Ok(EventOutcome::Ok(self.command_message.clone())); } } - // --- Add Logic Page Navigation --- + if app_state.ui.show_add_logic { let client_clone = grpc_client.clone(); let sender_clone = self.save_logic_result_sender.clone(); @@ -225,7 +257,6 @@ impl EventHandler { } } - // --- Add Table Page Navigation --- if app_state.ui.show_add_table { let client_clone = grpc_client.clone(); let sender_clone = self.save_table_result_sender.clone(); @@ -238,7 +269,6 @@ impl EventHandler { client_clone, sender_clone, &mut self.command_message, - ) { return Ok(EventOutcome::Ok(self.command_message.clone())); } @@ -296,7 +326,7 @@ impl EventHandler { UiContext::Dialog => { "Internal error: Unexpected dialog state".to_string() } - }; // Semicolon added here + }; return Ok(EventOutcome::Ok(message)); } other => return other, @@ -304,7 +334,6 @@ impl EventHandler { }, AppMode::ReadOnly => { - // Check for Linewise highlight first if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_highlight_mode_linewise") && ModeManager::can_enter_highlight_mode(current_mode) { @@ -315,7 +344,6 @@ impl EventHandler { self.command_message = "-- LINE HIGHLIGHT --".to_string(); return Ok(EventOutcome::Ok(self.command_message.clone())); } - // Check for Character-wise highlight else if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_highlight_mode") && ModeManager::can_enter_highlight_mode(current_mode) { @@ -330,7 +358,6 @@ impl EventHandler { self.command_message = "-- HIGHLIGHT --".to_string(); return Ok(EventOutcome::Ok(self.command_message.clone())); } - // Check for entering edit mode (before cursor) else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() == Some("enter_edit_mode_before") && ModeManager::can_enter_edit_mode(current_mode) { self.is_edit_mode = true; @@ -339,7 +366,6 @@ impl EventHandler { terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?; return Ok(EventOutcome::Ok(self.command_message.clone())); } - // Check for entering edit mode (after cursor) 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 = if app_state.ui.show_login || app_state.ui.show_register{ @@ -369,7 +395,6 @@ impl EventHandler { terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?; return Ok(EventOutcome::Ok(self.command_message.clone())); } - // Check for entering command mode else if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_command_mode") && ModeManager::can_enter_command_mode(current_mode) { self.command_mode = true; @@ -378,7 +403,6 @@ impl EventHandler { return Ok(EventOutcome::Ok(String::new())); } - // Check for common actions (save, quit, etc.) only if no mode change happened if let Some(action) = config.get_common_action(key_code, modifiers) { match action { "save" | "force_quit" | "save_and_quit" | "revert" => { @@ -400,7 +424,6 @@ impl EventHandler { } } - // If no mode change or specific common action handled, delegate to read_only handler let (_should_exit, message) = read_only::handle_read_only_event( app_state, key, @@ -418,22 +441,17 @@ impl EventHandler { &mut self.edit_mode_cooldown, &mut self.ideal_cursor_column, ).await?; - // Note: handle_read_only_event should ignore mode entry keys internally now return Ok(EventOutcome::Ok(message)); - }, // End AppMode::ReadOnly + }, AppMode::Highlight => { - // --- Handle Highlight Mode Specific Keys --- - // 1. Check for Exit first if config.get_highlight_action_for_key(key_code, modifiers) == Some("exit_highlight_mode") { self.highlight_state = HighlightState::Off; self.command_message = "Exited highlight mode".to_string(); terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?; return Ok(EventOutcome::Ok(self.command_message.clone())); } - // 2. Check for Switch to Linewise else if config.get_highlight_action_for_key(key_code, modifiers) == Some("enter_highlight_mode_linewise") { - // Only switch if currently characterwise if let HighlightState::Characterwise { anchor } = self.highlight_state { self.highlight_state = HighlightState::Linewise { anchor_line: anchor.0 }; self.command_message = "-- LINE HIGHLIGHT --".to_string(); @@ -444,14 +462,14 @@ impl EventHandler { let (_should_exit, message) = read_only::handle_read_only_event( app_state, key, config, form_state, login_state, - register_state, - &mut admin_state.add_table_state, + register_state, + &mut admin_state.add_table_state, &mut admin_state.add_logic_state, &mut self.key_sequence_tracker, - current_position, - total_count, + current_position, + total_count, grpc_client, - &mut self.command_message, + &mut self.command_message, &mut self.edit_mode_cooldown, &mut self.ideal_cursor_column, ) @@ -460,14 +478,9 @@ impl EventHandler { } AppMode::Edit => { - // First, check for common actions (save, revert, etc.) that apply in Edit mode - // These might take precedence or have different behavior than the edit handler if let Some(action) = config.get_common_action(key_code, modifiers) { - // Handle common actions like save, revert, force_quit, save_and_quit - // Ensure these actions return EventOutcome directly if they might exit the app match action { "save" | "force_quit" | "save_and_quit" | "revert" => { - // This call likely returns EventOutcome, handle it directly return common_mode::handle_core_action( action, form_state, @@ -482,16 +495,10 @@ impl EventHandler { total_count, ).await; }, - // Handle other common actions if necessary _ => {} } - // If a common action was handled but didn't return/exit, - // we might want to stop further processing for this key event. - // Depending on the action, you might return Ok(EventOutcome::Ok(...)) here. - // For now, assume common actions either exit or don't prevent further processing. } - // If no common action took precedence, delegate to the edit-specific handler let edit_result = edit::handle_edit_event( key, config, @@ -508,7 +515,6 @@ impl EventHandler { match edit_result { Ok(edit::EditEventOutcome::ExitEditMode) => { - // The edit handler signaled to exit the mode self.is_edit_mode = false; self.edit_mode_cooldown = true; let has_changes = if app_state.ui.show_login { login_state.has_unsaved_changes() } @@ -520,7 +526,6 @@ impl EventHandler { "Read-only mode".to_string() }; terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?; - // Adjust cursor position if needed let current_input = 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() }; @@ -536,43 +541,91 @@ impl EventHandler { return Ok(EventOutcome::Ok(self.command_message.clone())); } Ok(edit::EditEventOutcome::Message(msg)) => { - // Stay in edit mode, update message if not empty if !msg.is_empty() { self.command_message = msg; } - self.key_sequence_tracker.reset(); // Reset sequence tracker on successful edit action + self.key_sequence_tracker.reset(); return Ok(EventOutcome::Ok(self.command_message.clone())); } Err(e) => { - // Handle error from the edit handler return Err(e.into()); } } - }, // End AppMode::Edit + }, AppMode::Command => { - let outcome = command_mode::handle_command_event( - key, - config, - app_state, - login_state, - register_state, - form_state, - &mut self.command_input, - &mut self.command_message, - grpc_client, - command_handler, - terminal, - current_position, - total_count, - ).await?; - - if let EventOutcome::Ok(msg) = &outcome { - if msg == "Exited command mode" { - self.command_mode = false; - } + // Handle immediate command mode actions + if config.is_exit_command_mode(key_code, modifiers) { + self.command_input.clear(); + self.command_message.clear(); + self.command_mode = false; + self.key_sequence_tracker.reset(); + return Ok(EventOutcome::Ok("Exited command mode".to_string())); } - return Ok(outcome); + + if config.is_command_execute(key_code, modifiers) { + let outcome = command_mode::handle_command_event( + key, config, app_state, login_state, register_state, form_state, + &mut self.command_input, &mut self.command_message, + grpc_client, command_handler, terminal, current_position, total_count, + ).await?; + self.command_mode = false; + self.key_sequence_tracker.reset(); + return Ok(outcome); + } + + // Handle backspace + if key_code == KeyCode::Backspace { + self.command_input.pop(); + self.key_sequence_tracker.reset(); + return Ok(EventOutcome::Ok(String::new())); + } + + // Handle character input and sequences + if let KeyCode::Char(c) = key_code { + if c == 'f' { + self.key_sequence_tracker.add_key(key_code); + let sequence = self.key_sequence_tracker.get_sequence(); + + if config.matches_key_sequence_generalized(&sequence) == Some("find_file_palette_toggle") { + if app_state.ui.show_form || app_state.ui.show_intro { + self.find_file_palette_active = true; + self.find_file_input.clear(); + self.find_file_selected_index = if self.find_file_options.is_empty() { + None + } else { + Some(0) + }; + self.command_mode = false; + self.command_input.clear(); + self.command_message = "Find File:".to_string(); + self.key_sequence_tracker.reset(); + return Ok(EventOutcome::Ok("Find File palette activated".to_string())); + } else { + self.key_sequence_tracker.reset(); + self.command_input.push('f'); + self.command_input.push('f'); + self.command_message = "Find File not available in this view.".to_string(); + return Ok(EventOutcome::Ok(self.command_message.clone())); + } + } + + if config.is_key_sequence_prefix(&sequence) { + return Ok(EventOutcome::Ok(String::new())); + } + } + + if c != 'f' && !self.key_sequence_tracker.current_sequence.is_empty() { + self.key_sequence_tracker.reset(); + } + + self.command_input.push(c); + return Ok(EventOutcome::Ok(String::new())); + } + + // Reset tracker for other keys + self.key_sequence_tracker.reset(); + return Ok(EventOutcome::Ok(String::new())); } } } @@ -580,4 +633,8 @@ impl EventHandler { self.edit_mode_cooldown = false; Ok(EventOutcome::Ok(self.command_message.clone())) } + + fn is_processed_command(&self, command: &str) -> bool { + matches!(command, "w" | "q" | "q!" | "wq" | "r") + } } diff --git a/client/src/modes/handlers/mode_manager.rs b/client/src/modes/handlers/mode_manager.rs index b3714de..9819eaf 100644 --- a/client/src/modes/handlers/mode_manager.rs +++ b/client/src/modes/handlers/mode_manager.rs @@ -23,6 +23,10 @@ impl ModeManager { event_handler: &EventHandler, admin_state: &AdminState, ) -> AppMode { + if event_handler.find_file_palette_active { + return AppMode::General; + } + if event_handler.command_mode { return AppMode::Command; } diff --git a/client/src/ui/handlers/render.rs b/client/src/ui/handlers/render.rs index 4094380..fe8e291 100644 --- a/client/src/ui/handlers/render.rs +++ b/client/src/ui/handlers/render.rs @@ -3,7 +3,7 @@ use crate::components::{ render_background, render_buffer_list, - render_command_line, + render_command_line, // For the normal command line render_status_line, intro::intro::render_intro, handlers::sidebar::{self, calculate_sidebar_layout}, @@ -13,18 +13,68 @@ use crate::components::{ auth::{login::render_login, register::render_register}, }; use crate::config::colors::themes::Theme; -use ratatui::layout::{Constraint, Direction, Layout}; -use ratatui::Frame; +use ratatui::{ + layout::{Constraint, Direction, Layout, Rect}, // Added Rect + style::Style, // Added Style for render_find_file_palette + widgets::{Block, List, ListItem, Paragraph}, // Added for render_find_file_palette + Frame, +}; +use crate::state::pages::canvas_state::CanvasState; use crate::state::pages::form::FormState; use crate::state::pages::auth::AuthState; use crate::state::pages::auth::LoginState; use crate::state::pages::auth::RegisterState; use crate::state::pages::intro::IntroState; use crate::state::app::buffer::BufferState; -use crate::state::app::state::AppState; +use crate::state::app::state::AppState; // AppState is needed for app_state.ui checks use crate::state::pages::admin::AdminState; use crate::state::app::highlight::HighlightState; +// ++ New function to render the Find File Palette ++ +fn render_find_file_palette( + f: &mut Frame, + area: Rect, + theme: &Theme, + palette_input: &str, // Specific input for the palette + options: &[String], + selected_index: Option, +) { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(1), // For palette input line + Constraint::Min(0), // For options list + ]) + .split(area); + + // Draw the palette input line + let prompt_text = format!("Find File: {}", palette_input); // Using palette_input + let input_paragraph = Paragraph::new(prompt_text) + .style(Style::default().fg(theme.accent).bg(theme.bg)); + f.render_widget(input_paragraph, chunks[0]); + + // Draw the list of options + if !options.is_empty() && chunks.len() > 1 { + let list_items: Vec = options + .iter() + .enumerate() + .map(|(idx, opt_str)| { + let style = if Some(idx) == selected_index { + Style::default().fg(theme.bg).bg(theme.accent) // Highlight selected + } else { + Style::default().fg(theme.fg).bg(theme.bg) + }; + ListItem::new(opt_str.as_str()).style(style) + }) + .collect(); + + let options_list = List::new(list_items) + .block(Block::default().style(Style::default().bg(theme.bg))); + f.render_widget(options_list, chunks[1]); + } +} + +#[allow(clippy::too_many_arguments)] pub fn render_ui( f: &mut Frame, form_state: &mut FormState, @@ -35,175 +85,169 @@ pub fn render_ui( admin_state: &mut AdminState, buffer_state: &BufferState, theme: &Theme, - is_edit_mode: bool, + // These come from EventHandler + is_event_handler_edit_mode: bool, highlight_state: &HighlightState, + event_handler_command_input: &str, // For normal command line + event_handler_command_mode_active: bool, // Normal command line active? + event_handler_command_message: &str, + // ++ Find File Palette specific state from EventHandler ++ + find_file_palette_active: bool, + find_file_options: &[String], + find_file_selected_index: Option, + find_file_palette_input: &str, // Input for the palette + // General app state total_count: u64, current_position: u64, current_dir: &str, - command_input: &str, - command_mode: bool, - command_message: &str, current_fps: f64, - app_state: &AppState, + app_state: &AppState, // Contains app_state.ui for layout decisions ) { render_background(f, f.area(), theme); - // Adjust layout based on whether buffer list is shown - let constraints = if app_state.ui.show_buffer_list { - vec![ - Constraint::Length(1), // Buffer list - Constraint::Min(1), // Main content - Constraint::Length(1), // Status line - Constraint::Length(1), // Command line - ] + // Determine the height needed for the bottom bar (status + command/palette) + let mut bottom_area_constraints: Vec = vec![Constraint::Length(1)]; // Status line + + let command_palette_area_height = if find_file_palette_active { + // Height for palette: 1 for input + number of visible options + let max_visible_options = 7; // Example, can be adjusted + 1 + find_file_options.iter().take(max_visible_options).count().min(max_visible_options) as u16 + } else if event_handler_command_mode_active { + 1 // Height for normal command line } else { - vec![ - Constraint::Min(1), // Main content - Constraint::Length(1), // Status line (no buffer list) - Constraint::Length(1), // Command line - ] + 0 // No command line or palette }; - let root = Layout::default() - .direction(Direction::Vertical) - .constraints(constraints) - .split(f.area()); - - let mut buffer_list_area = None; - let main_content_area; - let status_line_area; - let command_line_area; - - // Assign areas based on layout - if app_state.ui.show_buffer_list { - buffer_list_area = Some(root[0]); - main_content_area = root[1]; - status_line_area = root[2]; - command_line_area = root[3]; - } else { - main_content_area = root[0]; - status_line_area = root[1]; - command_line_area = root[2]; + if command_palette_area_height > 0 { + bottom_area_constraints.push(Constraint::Length(command_palette_area_height)); } + // Main layout: Buffer List (optional), Main Content, Bottom Area (Status + Command/Palette) + let mut main_layout_constraints = vec![Constraint::Min(1)]; // Main Content + if app_state.ui.show_buffer_list { + main_layout_constraints.insert(0, Constraint::Length(1)); // Buffer List + } + main_layout_constraints.extend(bottom_area_constraints); // Add status and command/palette + + let root_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints(main_layout_constraints) + .split(f.area()); + + // Assign areas + let mut chunk_idx = 0; + let buffer_list_area = if app_state.ui.show_buffer_list { + chunk_idx += 1; + Some(root_chunks[0]) + } else { + None + }; + + let main_content_area = root_chunks[chunk_idx]; + chunk_idx += 1; + + let status_line_area = root_chunks[chunk_idx]; + chunk_idx += 1; + + let mut command_render_area = None; // For normal command line or palette + if command_palette_area_height > 0 { + if root_chunks.len() > chunk_idx { + command_render_area = Some(root_chunks[chunk_idx]); + } + } + + // --- Render main content views --- if app_state.ui.show_intro { render_intro(f, intro_state, main_content_area, theme); } else if app_state.ui.show_register { render_register( - f, - main_content_area, - theme, - register_state, - app_state, - register_state.current_field < 4, + f, main_content_area, theme, register_state, app_state, + register_state.current_field() < 4, // Example condition highlight_state, ); } else if app_state.ui.show_add_table { render_add_table( - f, - main_content_area, - theme, - app_state, - &mut admin_state.add_table_state, - login_state.current_field < 3, + f, main_content_area, theme, app_state, &mut admin_state.add_table_state, + is_event_handler_edit_mode, // Or specific logic for add_table highlight_state, ); } else if app_state.ui.show_add_logic { render_add_logic( - f, - main_content_area, - theme, - app_state, - &mut admin_state.add_logic_state, - is_edit_mode, // Pass the general edit mode status - highlight_state, + f, main_content_area, theme, app_state, &mut admin_state.add_logic_state, + is_event_handler_edit_mode, highlight_state, ); } else if app_state.ui.show_login { render_login( - f, - main_content_area, - theme, - login_state, - app_state, - login_state.current_field < 2, + f, main_content_area, theme, login_state, app_state, + login_state.current_field() < 2, // Example condition highlight_state, ); } else if app_state.ui.show_admin { crate::components::admin::admin_panel::render_admin_panel( - f, - app_state, - auth_state, - admin_state, - main_content_area, - theme, - &app_state.profile_tree, - &app_state.selected_profile, + f, app_state, auth_state, admin_state, main_content_area, theme, + &app_state.profile_tree, &app_state.selected_profile, ); } else if app_state.ui.show_form { - let (sidebar_area, form_area) = calculate_sidebar_layout( - app_state.ui.show_sidebar, - main_content_area + let (sidebar_area, form_actual_area) = calculate_sidebar_layout( + app_state.ui.show_sidebar, main_content_area ); - if let Some(sidebar_rect) = sidebar_area { sidebar::render_sidebar( - f, - sidebar_rect, - theme, - &app_state.profile_tree, - &app_state.selected_profile + f, sidebar_rect, theme, &app_state.profile_tree, &app_state.selected_profile ); } - - // This change makes the form stay stationary when toggling sidebar - let available_width = form_area.width; - let form_constraint = if available_width >= 80 { - // Use main_content_area for centering when enough space - Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Min(0), - Constraint::Length(80), - Constraint::Min(0), - ]) - .split(main_content_area)[1] + let available_width = form_actual_area.width; + let form_render_area = if available_width >= 80 { + Layout::default().direction(Direction::Horizontal) + .constraints([Constraint::Min(0), Constraint::Length(80), Constraint::Min(0)]) + .split(main_content_area)[1] // Center in main_content_area if sidebar is off } else { - // Use form_area (post sidebar) when limited space - Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Min(0), - Constraint::Length(80.min(available_width)), - Constraint::Min(0), - ]) - .split(form_area)[1] + Layout::default().direction(Direction::Horizontal) + .constraints([Constraint::Min(0), Constraint::Length(available_width.min(80)), Constraint::Min(0)]) + .split(form_actual_area)[1] // Use form_actual_area if sidebar is on }; - - // Convert fields to &[&str] and values to &[&String] - let fields: Vec<&str> = form_state.fields.iter().map(|s| s.as_str()).collect(); - let values: Vec<&String> = form_state.values.iter().collect(); - + let fields_vec: Vec<&str> = form_state.fields.iter().map(AsRef::as_ref).collect(); + let values_vec: Vec<&String> = form_state.values.iter().collect(); render_form( - f, - form_constraint, - form_state, - &fields, - &form_state.current_field, - &values, - theme, - is_edit_mode, - highlight_state, - total_count, - current_position, + f, form_render_area, form_state, &fields_vec, &form_state.current_field, + &values_vec, theme, is_event_handler_edit_mode, highlight_state, + total_count, current_position, ); } + // --- End Render main content views --- - // Render buffer list if enabled and area is available + // Render buffer list if enabled if let Some(area) = buffer_list_area { if app_state.ui.show_buffer_list { render_buffer_list(f, area, theme, buffer_state, app_state); } } - render_status_line(f, status_line_area, current_dir, theme, is_edit_mode, current_fps); - render_command_line(f, command_line_area, command_input, command_mode, theme, command_message); + + // Render status line + render_status_line(f, status_line_area, current_dir, theme, is_event_handler_edit_mode, current_fps); + + // Render Find File Palette OR Normal Command Line + if let Some(area) = command_render_area { + if find_file_palette_active { + render_find_file_palette( + f, + area, + theme, + find_file_palette_input, // Pass palette-specific input + find_file_options, + find_file_selected_index, + ); + } else if event_handler_command_mode_active { + // Normal command line + render_command_line( + f, + area, + event_handler_command_input, + true, // It's active + theme, + event_handler_command_message, + // No palette-specific args for normal command line + ); + } + } } diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index 01f2906..c83741e 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -209,14 +209,20 @@ pub async fn run_ui() -> Result<()> { &mut admin_state, &buffer_state, &theme, - event_handler.is_edit_mode, + event_handler.is_edit_mode, // is_event_handler_edit_mode &event_handler.highlight_state, + &event_handler.command_input, // event_handler_command_input + event_handler.command_mode, // event_handler_command_mode_active + &event_handler.command_message, // event_handler_command_message + // Find File Palette specific state + event_handler.find_file_palette_active, + &event_handler.find_file_options, + event_handler.find_file_selected_index, + &event_handler.find_file_input, + // General app state app_state.total_count, app_state.current_position, &app_state.current_dir, - &event_handler.command_input, - event_handler.command_mode, - &event_handler.command_message, current_fps, &app_state, );