change todo (#1925)
<img width="746" height="135" alt="image" src="https://github.com/user-attachments/assets/1605b2fb-aa3a-4337-b9e9-93f6ff1361c5" /> <img width="747" height="126" alt="image" src="https://github.com/user-attachments/assets/6b4366bd-8548-4d29-8cfa-cd484d9a2359" />
This commit is contained in:
@@ -513,48 +513,48 @@ impl HistoryCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a user‑friendly plan update with colourful status icons and a
|
/// Render a user‑friendly plan update styled like a checkbox todo list.
|
||||||
/// simple progress indicator so users can follow along.
|
|
||||||
pub(crate) fn new_plan_update(update: UpdatePlanArgs) -> Self {
|
pub(crate) fn new_plan_update(update: UpdatePlanArgs) -> Self {
|
||||||
let UpdatePlanArgs { explanation, plan } = update;
|
let UpdatePlanArgs { explanation, plan } = update;
|
||||||
|
|
||||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||||
|
// Header with progress summary
|
||||||
|
let total = plan.len();
|
||||||
|
let completed = plan
|
||||||
|
.iter()
|
||||||
|
.filter(|p| matches!(p.status, StepStatus::Completed))
|
||||||
|
.count();
|
||||||
|
|
||||||
// Title
|
let width: usize = 10;
|
||||||
lines.push(Line::from("plan".magenta().bold()));
|
let filled = if total > 0 {
|
||||||
|
(completed * width + total / 2) / total
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let empty = width.saturating_sub(filled);
|
||||||
|
|
||||||
if !plan.is_empty() {
|
let mut header: Vec<Span> = Vec::new();
|
||||||
// Progress bar – show completed/total with a visual bar
|
header.push(Span::raw("📋"));
|
||||||
let total = plan.len();
|
header.push(Span::styled(
|
||||||
let completed = plan
|
"Updated",
|
||||||
.iter()
|
Style::default().add_modifier(Modifier::BOLD).magenta(),
|
||||||
.filter(|p| matches!(p.status, StepStatus::Completed))
|
));
|
||||||
.count();
|
header.push(Span::raw(" to do list ["));
|
||||||
let width: usize = 20;
|
if filled > 0 {
|
||||||
let filled = (completed * width + total / 2) / total;
|
header.push(Span::styled(
|
||||||
let empty = width.saturating_sub(filled);
|
"█".repeat(filled),
|
||||||
let mut bar_spans: Vec<Span> = Vec::new();
|
Style::default().fg(Color::Green),
|
||||||
if filled > 0 {
|
));
|
||||||
bar_spans.push(Span::styled(
|
|
||||||
"█".repeat(filled),
|
|
||||||
Style::default().fg(Color::Green),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if empty > 0 {
|
|
||||||
bar_spans.push(Span::styled(
|
|
||||||
"░".repeat(empty),
|
|
||||||
Style::default().fg(Color::Gray),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let progress_prefix = Span::raw("progress [");
|
|
||||||
let progress_suffix = Span::raw("] ");
|
|
||||||
let fraction = Span::raw(format!("{completed}/{total}"));
|
|
||||||
let mut progress_line_spans = vec![progress_prefix];
|
|
||||||
progress_line_spans.extend(bar_spans);
|
|
||||||
progress_line_spans.push(progress_suffix);
|
|
||||||
progress_line_spans.push(fraction);
|
|
||||||
lines.push(Line::from(progress_line_spans));
|
|
||||||
}
|
}
|
||||||
|
if empty > 0 {
|
||||||
|
header.push(Span::styled(
|
||||||
|
"░".repeat(empty),
|
||||||
|
Style::default().fg(Color::Gray),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
header.push(Span::raw("] "));
|
||||||
|
header.push(Span::raw(format!("{completed}/{total}")));
|
||||||
|
lines.push(Line::from(header));
|
||||||
|
|
||||||
// Optional explanation/note from the model
|
// Optional explanation/note from the model
|
||||||
if let Some(expl) = explanation.and_then(|s| {
|
if let Some(expl) = explanation.and_then(|s| {
|
||||||
@@ -567,22 +567,48 @@ impl HistoryCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Steps (1‑based numbering) with fun, readable status icons
|
// Steps styled as checkbox items
|
||||||
if plan.is_empty() {
|
if plan.is_empty() {
|
||||||
lines.push(Line::from("(no steps provided)".gray().italic()));
|
lines.push(Line::from("(no steps provided)".gray().italic()));
|
||||||
} else {
|
} else {
|
||||||
for (idx, PlanItemArg { step, status }) in plan.into_iter().enumerate() {
|
for (idx, PlanItemArg { step, status }) in plan.into_iter().enumerate() {
|
||||||
let num = idx + 1;
|
let (box_span, text_span) = match status {
|
||||||
let icon_span: Span = match status {
|
StepStatus::Completed => (
|
||||||
StepStatus::Completed => Span::from("✓").fg(Color::Green),
|
Span::styled("✔", Style::default().fg(Color::Green)),
|
||||||
StepStatus::InProgress => Span::from("▶").fg(Color::Yellow).bold(),
|
Span::styled(
|
||||||
StepStatus::Pending => Span::from("○").fg(Color::Gray),
|
step,
|
||||||
|
Style::default()
|
||||||
|
.fg(Color::Gray)
|
||||||
|
.add_modifier(Modifier::CROSSED_OUT | Modifier::DIM),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
StepStatus::InProgress => (
|
||||||
|
Span::raw("□"),
|
||||||
|
Span::styled(
|
||||||
|
step,
|
||||||
|
Style::default()
|
||||||
|
.fg(Color::Blue)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
StepStatus::Pending => (
|
||||||
|
Span::raw("□"),
|
||||||
|
Span::styled(
|
||||||
|
step,
|
||||||
|
Style::default().fg(Color::Gray).add_modifier(Modifier::DIM),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let prefix = if idx == 0 {
|
||||||
|
Span::raw(" ⎿ ")
|
||||||
|
} else {
|
||||||
|
Span::raw(" ")
|
||||||
};
|
};
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
format!("{num:>2}. [").into(),
|
prefix,
|
||||||
icon_span,
|
box_span,
|
||||||
"] ".into(),
|
Span::raw(" "),
|
||||||
step.into(),
|
text_span,
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user