diff --git a/crates/owlen-tui/src/ui.rs b/crates/owlen-tui/src/ui.rs index c210fa0..6512651 100644 --- a/crates/owlen-tui/src/ui.rs +++ b/crates/owlen-tui/src/ui.rs @@ -186,18 +186,37 @@ pub fn render_chat(frame: &mut Frame<'_>, app: &mut ChatApp) { // Set terminal background color let theme = app.theme().clone(); let background_block = Block::default().style(Style::default().bg(theme.background)); - let full_area = frame.area(); - frame.render_widget(background_block, full_area); + let frame_area = frame.area(); + frame.render_widget(background_block, frame_area); - let (file_area, main_area) = if app.is_file_panel_collapsed() || full_area.width < 40 { - (None, full_area) + let title_line = Line::from(vec![Span::styled( + format!(" 🦉 OWLEN v{} – AI Assistant ", APP_VERSION), + Style::default() + .fg(theme.focused_panel_border) + .add_modifier(Modifier::BOLD), + )]); + let main_block = Block::default() + .borders(Borders::ALL) + .border_style(Style::default().fg(theme.unfocused_panel_border)) + .style(Style::default().bg(theme.background).fg(theme.text)) + .title(title_line); + + let content_area = main_block.inner(frame_area); + frame.render_widget(main_block, frame_area); + + if content_area.width == 0 || content_area.height == 0 { + return; + } + + let (file_area, main_area) = if app.is_file_panel_collapsed() || content_area.width < 40 { + (None, content_area) } else { - let max_sidebar = full_area.width.saturating_sub(30).max(10); + let max_sidebar = content_area.width.saturating_sub(30).max(10); let sidebar_width = app.file_panel_width().min(max_sidebar).max(10); let segments = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Length(sidebar_width), Constraint::Min(30)]) - .split(full_area); + .split(content_area); (Some(segments[0]), segments[1]) }; @@ -253,10 +272,7 @@ pub fn render_chat(frame: &mut Frame<'_>, app: &mut ChatApp) { 0 }; - let mut constraints = vec![ - Constraint::Length(3), // Header - Constraint::Min(8), // Messages - ]; + let mut constraints = vec![Constraint::Min(8)]; // Messages if thinking_height > 0 { constraints.push(Constraint::Length(thinking_height)); // Thinking @@ -276,9 +292,6 @@ pub fn render_chat(frame: &mut Frame<'_>, app: &mut ChatApp) { .split(chat_area); let mut idx = 0; - render_header(frame, layout[idx], app); - idx += 1; - render_messages(frame, layout[idx], app); idx += 1; @@ -318,13 +331,16 @@ pub fn render_chat(frame: &mut Frame<'_>, app: &mut ChatApp) { } if app.is_model_info_visible() { - let panel_width = full_area + let panel_width = content_area .width .saturating_div(3) .max(30) - .min(full_area.width.saturating_sub(20).max(30)); - let x = full_area.x + full_area.width.saturating_sub(panel_width); - let area = Rect::new(x, full_area.y, panel_width, full_area.height); + .min(content_area.width.saturating_sub(20).max(30)) + .min(content_area.width); + let x = content_area + .x + .saturating_add(content_area.width.saturating_sub(panel_width)); + let area = Rect::new(x, content_area.y, panel_width, content_area.height); frame.render_widget(Clear, area); let viewport_height = area.height.saturating_sub(2) as usize; app.set_model_info_viewport_height(viewport_height); @@ -335,7 +351,7 @@ pub fn render_chat(frame: &mut Frame<'_>, app: &mut ChatApp) { render_code_workspace(frame, area, app); } - render_toasts(frame, app, full_area); + render_toasts(frame, app, content_area); } fn toast_palette(level: ToastLevel, theme: &Theme) -> (&'static str, Style, Style) { @@ -1109,24 +1125,6 @@ fn wrap_line_segments(line: &str, width: usize) -> Vec { result } -fn render_header(frame: &mut Frame<'_>, area: Rect, app: &ChatApp) { - let theme = app.theme(); - let title_span = Span::styled( - format!(" 🦉 OWLEN v{} – AI Assistant ", APP_VERSION), - Style::default() - .fg(theme.focused_panel_border) - .add_modifier(Modifier::BOLD), - ); - - let header_block = Block::default() - .borders(Borders::ALL) - .border_style(Style::default().fg(theme.unfocused_panel_border)) - .style(Style::default().bg(theme.background).fg(theme.text)) - .title(Line::from(vec![title_span])); - - frame.render_widget(header_block, area); -} - fn apply_visual_selection<'a>( lines: Vec>, selection: Option<((usize, usize), (usize, usize))>,