diff --git a/client/src/components/common/status_line.rs b/client/src/components/common/status_line.rs index 8bc67ce..39545ab 100644 --- a/client/src/components/common/status_line.rs +++ b/client/src/components/common/status_line.rs @@ -20,16 +20,18 @@ pub fn render_status_line( current_fps: f64, app_state: &AppState, ) { - // --- START FIX --- - // Ensure debug_text is always a &str, which implements UnicodeWidthStr. #[cfg(feature = "ui-debug")] - let debug_text = app_state.debug_info.as_str(); - #[cfg(not(feature = "ui-debug"))] - let debug_text = ""; - // --- END FIX --- - - let debug_width = UnicodeWidthStr::width(debug_text); - let debug_separator_width = if !debug_text.is_empty() { UnicodeWidthStr::width(" | ") } else { 0 }; + { + // If ui-debug is enabled, we only show the debug info. + let debug_text = &app_state.debug_info; + let debug_span = Span::styled( + debug_text.as_str(), + Style::default().fg(theme.accent).bg(theme.bg), + ); + let paragraph = Paragraph::new(Line::from(vec![debug_span])); + f.render_widget(paragraph, area); + return; // Early return to skip the normal status line rendering. + } let program_info = format!("multieko2 v{}", env!("CARGO_PKG_VERSION")); let mode_text = if is_edit_mode { "[EDIT]" } else { "[READ-ONLY]" }; @@ -50,16 +52,25 @@ pub fn render_status_line( let separator = " | "; let separator_width = UnicodeWidthStr::width(separator); - let fixed_width_with_fps = mode_width + separator_width + separator_width + - program_info_width + separator_width + fps_width + - debug_separator_width + debug_width; + let fixed_width_with_fps = mode_width + + separator_width + + separator_width + + program_info_width + + separator_width + + fps_width; + let show_fps = fixed_width_with_fps <= available_width; let remaining_width_for_dir = available_width.saturating_sub( - mode_width + separator_width + - separator_width + program_info_width + - (if show_fps { separator_width + fps_width } else { 0 }) + - debug_separator_width + debug_width, + mode_width + + separator_width + + separator_width + + program_info_width + + (if show_fps { + separator_width + fps_width + } else { + 0 + }), ); let dir_display_text_str = if UnicodeWidthStr::width(display_dir.as_str()) <= remaining_width_for_dir { @@ -76,10 +87,11 @@ pub fn render_status_line( } }; - let mut current_content_width = mode_width + separator_width + - UnicodeWidthStr::width(dir_display_text_str.as_str()) + - separator_width + program_info_width + - debug_separator_width + debug_width; + let mut current_content_width = mode_width + + separator_width + + UnicodeWidthStr::width(dir_display_text_str.as_str()) + + separator_width + + program_info_width; if show_fps { current_content_width += separator_width + fps_width; } @@ -92,15 +104,13 @@ pub fn render_status_line( Span::styled(program_info.as_str(), Style::default().fg(theme.secondary)), ]; - if show_fps { - line_spans.push(Span::styled(separator, Style::default().fg(theme.border))); - line_spans.push(Span::styled(fps_text.as_str(), Style::default().fg(theme.secondary))); - } - - #[cfg(feature = "ui-debug")] - { - line_spans.push(Span::styled(separator, Style::default().fg(theme.border))); - line_spans.push(Span::styled(debug_text, Style::default().fg(theme.accent))); +if show_fps { + line_spans + .push(Span::styled(separator, Style::default().fg(theme.border))); + line_spans.push(Span::styled( + fps_text.as_str(), + Style::default().fg(theme.secondary), + )); } let padding_needed = available_width.saturating_sub(current_content_width); @@ -111,8 +121,8 @@ pub fn render_status_line( )); } - let paragraph = Paragraph::new(Line::from(line_spans)) - .style(Style::default().bg(theme.bg)); + let paragraph = + Paragraph::new(Line::from(line_spans)).style(Style::default().bg(theme.bg)); f.render_widget(paragraph, area); } diff --git a/client/src/main.rs b/client/src/main.rs index 92eb525..a9b18b8 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,5 +1,7 @@ // client/src/main.rs use client::run_ui; +#[cfg(feature = "ui-debug")] +use client::utils::debug_logger::UiDebugWriter; use dotenvy::dotenv; use anyhow::Result; use tracing_subscriber; @@ -7,8 +9,17 @@ use std::env; #[tokio::main] async fn main() -> Result<()> { - if env::var("ENABLE_TRACING").is_ok() { - tracing_subscriber::fmt::init(); + #[cfg(feature = "ui-debug")] + { + // If ui-debug is on, set up our custom writer. + let writer = UiDebugWriter::new(); + tracing_subscriber::fmt().with_writer(move || writer.clone()).init(); + } + #[cfg(not(feature = "ui-debug"))] + { + if env::var("ENABLE_TRACING").is_ok() { + tracing_subscriber::fmt::init(); + } } dotenv().ok(); diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 22e1c03..172e488 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -42,10 +42,9 @@ use crate::tui::{ use crate::ui::handlers::context::UiContext; use crate::ui::handlers::rat_state::UiStateHandler; use anyhow::Result; -use common::proto::multieko2::search::search_response::Hit; // Correctly imported +use common::proto::multieko2::search::search_response::Hit; use crossterm::cursor::SetCursorStyle; -use crossterm::event::KeyCode; -use crossterm::event::{Event, KeyEvent}; +use crossterm::event::{Event, KeyCode, KeyEvent}; use tokio::sync::mpsc; use tokio::sync::mpsc::unbounded_channel; use tracing::{info, error}; @@ -186,15 +185,18 @@ impl EventHandler { let table_name = search_state.table_name.clone(); let sender = self.search_result_sender.clone(); + info!("--- 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 { Ok(response) => { - info!("Search successful. Received {} hits.", response.hits.len()); + info!("--- 3a. gRPC call successful. Found {} hits. ---", response.hits.len()); let _ = sender.send(response.hits); } Err(e) => { - error!("gRPC search call failed: {}", e); + error!("--- 3b. gRPC call failed: {} ---", e); let _ = sender.send(vec![]); } } diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index f362117..91c1fff 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -24,6 +24,8 @@ use crate::ui::handlers::render::render_ui; use crate::tui::functions::common::login::LoginResult; use crate::tui::functions::common::register::RegisterResult; use crate::ui::handlers::context::DialogPurpose; +#[cfg(feature = "ui-debug")] +use crate::utils::debug_logger::get_latest_debug_message; use crate::tui::functions::common::login; use crate::tui::functions::common::register; use crate::utils::columns::filter_user_columns; @@ -129,6 +131,7 @@ pub async fn run_ui() -> Result<()> { match event_handler.search_result_receiver.try_recv() { Ok(hits) => { + info!("--- 4. Main loop received message from channel. ---"); if let Some(search_state) = app_state.search_state.as_mut() { search_state.results = hits; search_state.is_loading = false; @@ -517,10 +520,7 @@ pub async fn run_ui() -> Result<()> { #[cfg(feature = "ui-debug")] { - app_state.debug_info = format!( - "Redraw -> event: {}, needs_redraw: {}, pos_changed: {}", - event_processed, needs_redraw, position_changed - ); + app_state.debug_info = get_latest_debug_message(); } if event_processed || needs_redraw || position_changed { diff --git a/client/src/utils/debug_logger.rs b/client/src/utils/debug_logger.rs new file mode 100644 index 0000000..b307379 --- /dev/null +++ b/client/src/utils/debug_logger.rs @@ -0,0 +1,46 @@ +// client/src/utils/debug_logger.rs +use lazy_static::lazy_static; +use std::io; +use std::sync::{Arc, Mutex}; + +lazy_static! { + static ref UI_DEBUG_BUFFER: Arc> = + Arc::new(Mutex::new(String::from("Logger initialized..."))); +} + +#[derive(Clone)] +pub struct UiDebugWriter; + +impl Default for UiDebugWriter { + fn default() -> Self { + Self::new() + } +} + +impl UiDebugWriter { + pub fn new() -> Self { + Self + } +} + +// Implement the io::Write trait for our writer. +// tracing_subscriber can use any type that implements `MakeWriter`. +// A simple way to do this is to have our writer implement `io::Write`. +impl io::Write for UiDebugWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let mut buffer = UI_DEBUG_BUFFER.lock().unwrap(); + let message = String::from_utf8_lossy(buf); + // We just want the latest message, trimmed of whitespace. + *buffer = message.trim().to_string(); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +// A public function to safely get the latest message from anywhere in the app. +pub fn get_latest_debug_message() -> String { + UI_DEBUG_BUFFER.lock().unwrap().clone() +} diff --git a/client/src/utils/mod.rs b/client/src/utils/mod.rs index fd6b6c7..efc8001 100644 --- a/client/src/utils/mod.rs +++ b/client/src/utils/mod.rs @@ -1,4 +1,6 @@ // src/utils/mod.rs pub mod columns; +pub mod debug_logger; pub use columns::*; +pub use debug_logger::*; diff --git a/search/src/lib.rs b/search/src/lib.rs index 5224233..d6dd39c 100644 --- a/search/src/lib.rs +++ b/search/src/lib.rs @@ -17,7 +17,8 @@ use common::proto::multieko2::search::{ pub use common::proto::multieko2::search::searcher_server::SearcherServer; use common::proto::multieko2::search::searcher_server::Searcher; use common::search::register_slovak_tokenizers; -use sqlx::{PgPool, Row}; // <-- Import PgPool and Row +use sqlx::{PgPool, Row}; +use tracing::info; // We need to hold the database pool in our service struct. pub struct SearcherService { @@ -263,6 +264,8 @@ impl Searcher for SearcherService { }) .collect(); + info!("--- SERVER: Successfully processed search. Returning {} hits. ---", hits.len()); + let response = SearchResponse { hits }; Ok(Response::new(response)) }