Fix border wrapping issue

This commit is contained in:
Ian Keane 2025-11-25 20:21:08 -05:00
parent 9ef6143dbd
commit 89d1c02690
2 changed files with 30 additions and 27 deletions

View file

@ -86,8 +86,8 @@ impl App {
} }
match self.log_view_grouping { match self.log_view_grouping {
LogViewGrouping::ByProject => line.starts_with("\t\t"), LogViewGrouping::ByProject => line.starts_with(" "),
LogViewGrouping::ByDate => line.starts_with('\t') && !line.starts_with("\t\t"), LogViewGrouping::ByDate => line.starts_with(" ") && !line.starts_with(" "),
} }
} }
@ -637,7 +637,7 @@ impl App {
if let Some(prev) = prev_stop { if let Some(prev) = prev_stop {
let gap = start_local.signed_duration_since(prev); let gap = start_local.signed_duration_since(prev);
if gap.num_minutes() >= 5 { if gap.num_minutes() >= 5 {
lines.push("\t ---".to_string()); lines.push(" ---".to_string());
// Note: don't add to frame_indices - separator is not an entry // Note: don't add to frame_indices - separator is not an entry
} }
} }
@ -663,7 +663,7 @@ impl App {
}; };
let line_text = format!( let line_text = format!(
"\t{} to {} {:>9} {}", " {} to {} {:>9} {}",
start_time, stop_time, duration_str, display_text start_time, stop_time, duration_str, display_text
); );
@ -763,7 +763,7 @@ impl App {
}; };
lines.push(format!( lines.push(format!(
"\t\t{} to {} {:>9}{}", " {} to {} {:>9}{}",
start_time, stop_time, duration_str, tags_str start_time, stop_time, duration_str, tags_str
)); ));
frame_indices.push(*idx); frame_indices.push(*idx);

View file

@ -2,7 +2,7 @@ use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect}, layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style}, style::{Color, Modifier, Style},
text::{Line, Span}, text::{Line, Span},
widgets::{Block, Borders, Clear, List, ListItem, Paragraph, Row, Table, TableState}, widgets::{Block, Borders, Clear, Paragraph, Row, Table, TableState},
Frame, Frame,
}; };
@ -698,8 +698,8 @@ fn render_log_view(frame: &mut Frame, app: &App) {
.borders(Borders::ALL) .borders(Borders::ALL)
.style(Style::default().fg(ACTIVE_COLOR)); .style(Style::default().fg(ACTIVE_COLOR));
// Build list items with selection highlighting based on selection level // Build text lines with selection highlighting based on selection level
let items: Vec<ListItem> = { let text_lines: Vec<Line> = {
// Pre-calculate which line indices should be highlighted // Pre-calculate which line indices should be highlighted
let mut selected_date = String::new(); let mut selected_date = String::new();
let mut selected_project = String::new(); let mut selected_project = String::new();
@ -715,17 +715,17 @@ fn render_log_view(frame: &mut Frame, app: &App) {
let is_by_project = matches!(app.log_view_grouping, LogViewGrouping::ByProject); let is_by_project = matches!(app.log_view_grouping, LogViewGrouping::ByProject);
for (_idx, line) in app.log_view_content.iter().enumerate() { for (_idx, line) in app.log_view_content.iter().enumerate() {
if !line.starts_with('\t') && !line.is_empty() && !line.starts_with(" ") { if !line.starts_with(" ") && !line.is_empty() && !line.starts_with(" ") {
current_date = line.clone(); current_date = line.clone();
} else if line.starts_with(" ") && !line.starts_with("\t") { } else if line.starts_with(" ") && !line.starts_with(" ") {
current_project = line.clone(); current_project = line.clone();
} }
// Only count actual entries (not all tab-indented lines) // Only count actual entries (not all tab-indented lines)
let is_entry = if is_by_project { let is_entry = if is_by_project {
line.starts_with("\t\t") // Double tab for ByProject line.starts_with(" ") // 8 spaces for ByProject
} else { } else {
line.starts_with('\t') && !line.starts_with("\t\t") // Single tab for ByDate line.starts_with(" ") && !line.starts_with(" ") // 4 spaces for ByDate
}; };
if is_entry { if is_entry {
@ -746,9 +746,9 @@ fn render_log_view(frame: &mut Frame, app: &App) {
for (idx, line) in app.log_view_content.iter().enumerate() { for (idx, line) in app.log_view_content.iter().enumerate() {
// Use is_entry_line logic which excludes separator lines // Use is_entry_line logic which excludes separator lines
let is_entry = if is_by_project { let is_entry = if is_by_project {
line.starts_with("\t\t") line.starts_with(" ")
} else { } else {
line.starts_with('\t') && !line.starts_with("\t\t") && line.trim() != "---" line.starts_with(" ") && !line.starts_with(" ") && line.trim() != "---"
}; };
if is_entry { if is_entry {
@ -767,19 +767,19 @@ fn render_log_view(frame: &mut Frame, app: &App) {
current_date = String::new(); current_date = String::new();
for (idx, line) in app.log_view_content.iter().enumerate() { for (idx, line) in app.log_view_content.iter().enumerate() {
if !line.starts_with('\t') && !line.is_empty() && !line.starts_with(" ") { if !line.starts_with(" ") && !line.is_empty() && !line.starts_with(" ") {
current_date = line.clone(); current_date = line.clone();
if in_target_project { if in_target_project {
break; // End of project group break; // End of project group
} }
} else if line.starts_with(" ") && !line.starts_with("\t") { } else if line.starts_with(" ") && !line.starts_with(" ") {
if current_date == selected_date && line == &selected_project { if current_date == selected_date && line == &selected_project {
selected_line_start = idx; selected_line_start = idx;
in_target_project = true; in_target_project = true;
} else if in_target_project { } else if in_target_project {
break; // Different project break; // Different project
} }
} else if in_target_project && line.starts_with('\t') { } else if in_target_project && line.starts_with(" ") {
selected_line_end = idx; selected_line_end = idx;
} }
} }
@ -789,7 +789,7 @@ fn render_log_view(frame: &mut Frame, app: &App) {
let mut in_target_day = false; let mut in_target_day = false;
for (idx, line) in app.log_view_content.iter().enumerate() { for (idx, line) in app.log_view_content.iter().enumerate() {
if !line.starts_with('\t') && !line.is_empty() && !line.starts_with(" ") { if !line.starts_with(" ") && !line.is_empty() && !line.starts_with(" ") {
if line == &selected_date { if line == &selected_date {
selected_line_start = idx; selected_line_start = idx;
in_target_day = true; in_target_day = true;
@ -820,17 +820,17 @@ fn render_log_view(frame: &mut Frame, app: &App) {
for (line_idx, line) in app.log_view_content.iter().enumerate() { for (line_idx, line) in app.log_view_content.iter().enumerate() {
// New day header // New day header
if !line.starts_with('\t') && !line.is_empty() && !line.starts_with(" ") { if !line.starts_with(" ") && !line.is_empty() && !line.starts_with(" ") {
current_day_start_entry = app.log_view_content[..line_idx] current_day_start_entry = app.log_view_content[..line_idx]
.iter() .iter()
.filter(|l| l.starts_with('\t') && !l.is_empty() && l.trim() != "---") .filter(|l| l.starts_with(" ") && !l.is_empty() && l.trim() != "---")
.count(); .count();
in_day = true; in_day = true;
} else if in_day && line.starts_with('\t') && !line.is_empty() && line.trim() != "---" { } else if in_day && line.starts_with(" ") && !line.is_empty() && line.trim() != "---" {
// This is an entry line // This is an entry line
let entry_idx = app.log_view_content[..=line_idx] let entry_idx = app.log_view_content[..=line_idx]
.iter() .iter()
.filter(|l| l.starts_with('\t') && !l.is_empty() && l.trim() != "---") .filter(|l| l.starts_with(" ") && !l.is_empty() && l.trim() != "---")
.count() .count()
.saturating_sub(1); .saturating_sub(1);
@ -868,12 +868,12 @@ fn render_log_view(frame: &mut Frame, app: &App) {
let is_separator = line.trim() == "---"; let is_separator = line.trim() == "---";
// Check if this line corresponds to an overlapping entry // Check if this line corresponds to an overlapping entry
let has_overlap = if !is_separator && is_by_date && line.starts_with('\t') && !line.is_empty() { let has_overlap = if !is_separator && is_by_date && line.starts_with(" ") && !line.is_empty() {
// Count which entry this is (0-based in display order) // Count which entry this is (0-based in display order)
// Only count actual entry lines, not separator lines // Only count actual entry lines, not separator lines
let entry_idx = app.log_view_content[..=idx] let entry_idx = app.log_view_content[..=idx]
.iter() .iter()
.filter(|l| l.starts_with('\t') && !l.is_empty() && l.trim() != "---") .filter(|l| l.starts_with(" ") && !l.is_empty() && l.trim() != "---")
.count() .count()
.saturating_sub(1); .saturating_sub(1);
@ -894,13 +894,16 @@ fn render_log_view(frame: &mut Frame, app: &App) {
Style::default().fg(Color::White) Style::default().fg(Color::White)
}; };
ListItem::new(Line::from(vec![Span::styled(line.clone(), style)])) Line::from(vec![Span::styled(line.clone(), style)])
}) })
.collect() .collect()
}; };
let list = List::new(items).block(block); let paragraph = Paragraph::new(text_lines)
frame.render_widget(list, chunks[0]); .block(block)
.wrap(ratatui::widgets::Wrap { trim: false });
frame.render_widget(paragraph, chunks[0]);
// Render help hint at bottom if enabled // Render help hint at bottom if enabled
if app.config.show_help_hint { if app.config.show_help_hint {