Select grouped by project or day
This commit is contained in:
parent
56f9322e36
commit
5aaa57a6a2
2 changed files with 480 additions and 43 deletions
168
src/ui.rs
168
src/ui.rs
|
|
@ -7,7 +7,7 @@ use ratatui::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
app::{App, LogViewGrouping, LogViewPeriod, NewEntryMode, Screen},
|
||||
app::{App, LogViewGrouping, LogViewPeriod, LogViewSelection, NewEntryMode, Screen},
|
||||
state::{AppState, TimeItem},
|
||||
};
|
||||
|
||||
|
|
@ -461,36 +461,129 @@ fn render_log_view(frame: &mut Frame, app: &App) {
|
|||
.borders(Borders::ALL)
|
||||
.style(Style::default().fg(ACTIVE_COLOR));
|
||||
|
||||
// Build list items with selection highlighting
|
||||
let items: Vec<ListItem> = app
|
||||
.log_view_content
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, line)| {
|
||||
// Check if this is a frame line (starts with tab) and matches selected frame
|
||||
let is_selected = if line.starts_with('\t') {
|
||||
// Count how many frame lines we've seen so far
|
||||
let frame_idx = app.log_view_content[..=idx]
|
||||
.iter()
|
||||
.filter(|l| l.starts_with('\t'))
|
||||
.count()
|
||||
.saturating_sub(1);
|
||||
frame_idx == app.log_view_selected
|
||||
// Build list items with selection highlighting based on selection level
|
||||
let items: Vec<ListItem> = {
|
||||
// Pre-calculate which line indices should be highlighted
|
||||
let mut selected_date = String::new();
|
||||
let mut selected_project = String::new();
|
||||
let mut frame_count = 0;
|
||||
let mut selected_line_start = 0;
|
||||
let mut selected_line_end = 0;
|
||||
|
||||
// First pass: find the date/project containing the selected frame
|
||||
let mut current_date = String::new();
|
||||
let mut current_project = String::new();
|
||||
|
||||
// Determine what counts as an entry based on grouping mode
|
||||
let is_by_project = matches!(app.log_view_grouping, LogViewGrouping::ByProject);
|
||||
|
||||
for (idx, line) in app.log_view_content.iter().enumerate() {
|
||||
if !line.starts_with('\t') && !line.is_empty() && !line.starts_with(" ") {
|
||||
current_date = line.clone();
|
||||
} else if line.starts_with(" ") && !line.starts_with("\t") {
|
||||
current_project = line.clone();
|
||||
}
|
||||
|
||||
// Only count actual entries (not all tab-indented lines)
|
||||
let is_entry = if is_by_project {
|
||||
line.starts_with("\t\t") // Double tab for ByProject
|
||||
} else {
|
||||
false
|
||||
line.starts_with('\t') && !line.starts_with("\t\t") // Single tab for ByDate
|
||||
};
|
||||
|
||||
if is_entry {
|
||||
if frame_count == app.log_view_selected {
|
||||
selected_date = current_date.clone();
|
||||
selected_project = current_project.clone();
|
||||
break;
|
||||
}
|
||||
frame_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: determine the range of lines to highlight
|
||||
match app.log_view_selection_level {
|
||||
LogViewSelection::Entry => {
|
||||
// Just find the specific entry line
|
||||
frame_count = 0;
|
||||
for (idx, line) in app.log_view_content.iter().enumerate() {
|
||||
let is_entry = if is_by_project {
|
||||
line.starts_with("\t\t")
|
||||
} else {
|
||||
line.starts_with('\t') && !line.starts_with("\t\t")
|
||||
};
|
||||
|
||||
if is_entry {
|
||||
if frame_count == app.log_view_selected {
|
||||
selected_line_start = idx;
|
||||
selected_line_end = idx;
|
||||
break;
|
||||
}
|
||||
frame_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
LogViewSelection::Project => {
|
||||
// Find the range of the selected project (within the same day)
|
||||
let mut in_target_project = false;
|
||||
current_date = String::new();
|
||||
|
||||
for (idx, line) in app.log_view_content.iter().enumerate() {
|
||||
if !line.starts_with('\t') && !line.is_empty() && !line.starts_with(" ") {
|
||||
current_date = line.clone();
|
||||
if in_target_project {
|
||||
break; // End of project group
|
||||
}
|
||||
} else if line.starts_with(" ") && !line.starts_with("\t") {
|
||||
if current_date == selected_date && line == &selected_project {
|
||||
selected_line_start = idx;
|
||||
in_target_project = true;
|
||||
} else if in_target_project {
|
||||
break; // Different project
|
||||
}
|
||||
} else if in_target_project && line.starts_with('\t') {
|
||||
selected_line_end = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
LogViewSelection::Day => {
|
||||
// Find the range of the selected day
|
||||
let mut in_target_day = false;
|
||||
|
||||
for (idx, line) in app.log_view_content.iter().enumerate() {
|
||||
if !line.starts_with('\t') && !line.is_empty() && !line.starts_with(" ") {
|
||||
if line == &selected_date {
|
||||
selected_line_start = idx;
|
||||
in_target_day = true;
|
||||
} else if in_target_day {
|
||||
break; // End of day
|
||||
}
|
||||
} else if in_target_day && !line.is_empty() {
|
||||
selected_line_end = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Third pass: render with highlighting
|
||||
app.log_view_content
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, line)| {
|
||||
let is_selected = idx >= selected_line_start && idx <= selected_line_end;
|
||||
|
||||
let style = if is_selected {
|
||||
Style::default()
|
||||
.fg(ACTIVE_COLOR)
|
||||
.add_modifier(Modifier::REVERSED)
|
||||
} else {
|
||||
Style::default().fg(Color::White)
|
||||
};
|
||||
|
||||
let style = if is_selected {
|
||||
Style::default()
|
||||
.fg(ACTIVE_COLOR)
|
||||
.add_modifier(Modifier::REVERSED)
|
||||
} else {
|
||||
Style::default().fg(Color::White)
|
||||
};
|
||||
|
||||
ListItem::new(Line::from(vec![Span::styled(line.clone(), style)]))
|
||||
})
|
||||
.collect();
|
||||
ListItem::new(Line::from(vec![Span::styled(line.clone(), style)]))
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let list = List::new(items).block(block);
|
||||
frame.render_widget(list, chunks[0]);
|
||||
|
|
@ -524,13 +617,22 @@ fn render_log_view_help(frame: &mut Frame, _app: &App) {
|
|||
" - By Project: Groups entries by project within each date",
|
||||
"",
|
||||
"Navigation:",
|
||||
"- j/k or ↑/↓: Select entries",
|
||||
"- PageUp/PageDown: Jump 10 entries",
|
||||
"- j/k or ↑/↓: Navigate selection",
|
||||
" - At Entry level: Move to next/previous entry",
|
||||
" - At Project level: Jump to next/previous project group",
|
||||
" - At Day level: Jump to next/previous day",
|
||||
"- h/l or ←/→: Change selection level (Entry ↔ Project ↔ Day)",
|
||||
"- PageUp/PageDown: Jump 10 entries (Entry level only)",
|
||||
"",
|
||||
"Selection Levels:",
|
||||
"- Entry: Select individual entry (can edit/delete/copy)",
|
||||
"- Project: Select whole project group (can copy only)",
|
||||
"- Day: Select entire day (can copy only)",
|
||||
"",
|
||||
"Actions:",
|
||||
"- e: Edit the selected entry (opens Watson's editor)",
|
||||
"- x: Delete the selected entry (no confirmation)",
|
||||
"- c: Copy all visible log entries to clipboard",
|
||||
"- e: Edit the selected entry (Entry level only)",
|
||||
"- x: Delete the selected entry (Entry level only)",
|
||||
"- c: Copy selection to clipboard (works at all levels)",
|
||||
"",
|
||||
"Other:",
|
||||
"- ?: Show this help",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue