// client/src/components/common/status_line.rs use crate::config::colors::themes::Theme; use crate::state::app::state::AppState; use ratatui::{ layout::Rect, style::Style, text::{Line, Span, Text}, widgets::Paragraph, Frame, }; use ratatui::widgets::Wrap; use std::path::Path; use unicode_width::UnicodeWidthStr; pub fn render_status_line( f: &mut Frame, area: Rect, current_dir: &str, theme: &Theme, is_edit_mode: bool, current_fps: f64, app_state: &AppState, ) { #[cfg(feature = "ui-debug")] { if let Some(debug_state) = &app_state.debug_state { let paragraph = if debug_state.is_error { // --- THIS IS THE CRITICAL LOGIC FOR ERRORS --- // 1. Create a `Text` object, which can contain multiple lines. let error_text = Text::from(debug_state.displayed_message.clone()); // 2. Create a Paragraph from the Text and TELL IT TO WRAP. Paragraph::new(error_text) .wrap(Wrap { trim: true }) // This line makes the text break into new rows. .style(Style::default().bg(theme.highlight).fg(theme.bg)) } else { // --- This is for normal, single-line info messages --- Paragraph::new(debug_state.displayed_message.as_str()) .style(Style::default().fg(theme.accent).bg(theme.bg)) }; f.render_widget(paragraph, area); } else { // Fallback for when debug state is None let paragraph = Paragraph::new("").style(Style::default().bg(theme.bg)); f.render_widget(paragraph, area); } return; // Stop here and don't render the normal status line. } // --- The normal status line rendering logic (unchanged) --- let program_info = format!("komp_ac v{}", env!("CARGO_PKG_VERSION")); let mode_text = if is_edit_mode { "[EDIT]" } else { "[READ-ONLY]" }; let home_dir = dirs::home_dir() .map(|p| p.to_string_lossy().into_owned()) .unwrap_or_default(); let display_dir = if current_dir.starts_with(&home_dir) { current_dir.replacen(&home_dir, "~", 1) } else { current_dir.to_string() }; let available_width = area.width as usize; let mode_width = UnicodeWidthStr::width(mode_text); let program_info_width = UnicodeWidthStr::width(program_info.as_str()); let fps_text = format!("{:.0} FPS", current_fps); let fps_width = UnicodeWidthStr::width(fps_text.as_str()); 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; 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 }), ); let dir_display_text_str = if UnicodeWidthStr::width(display_dir.as_str()) <= remaining_width_for_dir { display_dir } else { let dir_name = Path::new(current_dir) .file_name() .and_then(|n| n.to_str()) .unwrap_or(current_dir); if UnicodeWidthStr::width(dir_name) <= remaining_width_for_dir { dir_name.to_string() } else { dir_name .chars() .take(remaining_width_for_dir) .collect::() } }; 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; } let mut line_spans = vec![ Span::styled(mode_text, Style::default().fg(theme.accent)), Span::styled(separator, Style::default().fg(theme.border)), Span::styled( dir_display_text_str.as_str(), Style::default().fg(theme.fg), ), Span::styled(separator, Style::default().fg(theme.border)), 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), )); } let padding_needed = available_width.saturating_sub(current_content_width); if padding_needed > 0 { line_spans.push(Span::styled( " ".repeat(padding_needed), Style::default().bg(theme.bg), )); } let paragraph = Paragraph::new(Line::from(line_spans)).style(Style::default().bg(theme.bg)); f.render_widget(paragraph, area); }