// src/state/app/state.rs use anyhow::Result; use common::proto::multieko2::table_definition::ProfileTreeResponse; // NEW: Import the types we need for the cache use common::proto::multieko2::table_structure::TableStructureResponse; use crate::modes::handlers::mode_manager::AppMode; use crate::state::app::search::SearchState; use crate::ui::handlers::context::DialogPurpose; use std::collections::HashMap; use std::env; use std::sync::Arc; #[cfg(feature = "ui-debug")] use std::time::Instant; // --- DialogState and UiState are unchanged --- pub struct DialogState { pub dialog_show: bool, pub dialog_title: String, pub dialog_message: String, pub dialog_buttons: Vec, pub dialog_active_button_index: usize, pub purpose: Option, pub is_loading: bool, } pub struct UiState { pub show_sidebar: bool, pub show_buffer_list: bool, pub show_intro: bool, pub show_admin: bool, pub show_add_table: bool, pub show_add_logic: bool, pub show_form: bool, pub show_login: bool, pub show_register: bool, pub show_search_palette: bool, pub focus_outside_canvas: bool, pub dialog: DialogState, } #[cfg(feature = "ui-debug")] #[derive(Debug, Clone)] pub struct DebugState { pub displayed_message: String, pub is_error: bool, pub display_start_time: Instant, } pub struct AppState { // Core editor state pub current_dir: String, pub profile_tree: ProfileTreeResponse, pub selected_profile: Option, pub current_mode: AppMode, pub current_view_profile_name: Option, pub current_view_table_name: Option, // NEW: The "Rulebook" cache. We use Arc for efficient sharing. pub schema_cache: HashMap>, pub focused_button_index: usize, pub pending_table_structure_fetch: Option<(String, String)>, pub search_state: Option, // UI preferences pub ui: UiState, #[cfg(feature = "ui-debug")] pub debug_state: Option, } impl AppState { pub fn new() -> Result { let current_dir = env::current_dir()?.to_string_lossy().to_string(); Ok(AppState { current_dir, profile_tree: ProfileTreeResponse::default(), selected_profile: None, current_view_profile_name: None, current_view_table_name: None, current_mode: AppMode::General, schema_cache: HashMap::new(), // NEW: Initialize the cache focused_button_index: 0, pending_table_structure_fetch: None, search_state: None, ui: UiState::default(), #[cfg(feature = "ui-debug")] debug_state: None, }) } // --- ALL YOUR EXISTING METHODS ARE UNTOUCHED --- pub fn update_mode(&mut self, mode: AppMode) { self.current_mode = mode; } pub fn set_current_view_table(&mut self, profile_name: String, table_name: String) { self.current_view_profile_name = Some(profile_name); self.current_view_table_name = Some(table_name); } pub fn show_dialog( &mut self, title: &str, message: &str, buttons: Vec, purpose: DialogPurpose, ) { self.ui.dialog.dialog_title = title.to_string(); self.ui.dialog.dialog_message = message.to_string(); self.ui.dialog.dialog_buttons = buttons; self.ui.dialog.dialog_active_button_index = 0; self.ui.dialog.purpose = Some(purpose); self.ui.dialog.is_loading = false; self.ui.dialog.dialog_show = true; self.ui.focus_outside_canvas = true; } pub fn show_loading_dialog(&mut self, title: &str, message: &str) { self.ui.dialog.dialog_title = title.to_string(); self.ui.dialog.dialog_message = message.to_string(); self.ui.dialog.dialog_buttons.clear(); self.ui.dialog.dialog_active_button_index = 0; self.ui.dialog.purpose = None; self.ui.dialog.is_loading = true; self.ui.dialog.dialog_show = true; self.ui.focus_outside_canvas = true; } pub fn update_dialog_content( &mut self, message: &str, buttons: Vec, purpose: DialogPurpose, ) { if self.ui.dialog.dialog_show { self.ui.dialog.dialog_message = message.to_string(); self.ui.dialog.dialog_buttons = buttons; self.ui.dialog.dialog_active_button_index = 0; self.ui.dialog.purpose = Some(purpose); self.ui.dialog.is_loading = false; } } pub fn hide_dialog(&mut self) { self.ui.dialog.dialog_show = false; self.ui.dialog.dialog_title.clear(); self.ui.dialog.dialog_message.clear(); self.ui.dialog.dialog_buttons.clear(); self.ui.dialog.dialog_active_button_index = 0; self.ui.dialog.purpose = None; self.ui.focus_outside_canvas = false; self.ui.dialog.is_loading = false; } pub fn next_dialog_button(&mut self) { if !self.ui.dialog.dialog_buttons.is_empty() { let next_index = (self.ui.dialog.dialog_active_button_index + 1) % self.ui.dialog.dialog_buttons.len(); self.ui.dialog.dialog_active_button_index = next_index; } } pub fn previous_dialog_button(&mut self) { if !self.ui.dialog.dialog_buttons.is_empty() { let len = self.ui.dialog.dialog_buttons.len(); let prev_index = (self.ui.dialog.dialog_active_button_index + len - 1) % len; self.ui.dialog.dialog_active_button_index = prev_index; } } pub fn get_active_dialog_button_label(&self) -> Option<&str> { self.ui.dialog .dialog_buttons .get(self.ui.dialog.dialog_active_button_index) .map(|s| s.as_str()) } } impl Default for UiState { fn default() -> Self { Self { show_sidebar: false, show_intro: true, show_admin: false, show_add_table: false, show_add_logic: false, show_form: false, show_login: false, show_register: false, show_buffer_list: true, show_search_palette: false, // ADDED focus_outside_canvas: false, dialog: DialogState::default(), } } } impl Default for DialogState { fn default() -> Self { Self { dialog_show: false, dialog_title: String::new(), dialog_message: String::new(), dialog_buttons: Vec::new(), dialog_active_button_index: 0, purpose: None, is_loading: false, } } }