fix(tui): perbaiki isi overlay Todo dan Usage jadi tampilan detail nyata
Overlay Todo sebelumnya menampilkan jumlah pesan yang tidak relevan, bukan isi task list. Overlay Usage sekarang pakai compute_usage_summary yang sama dengan widget sidebar (DRY, angka konsisten). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
7fd55fa86d
commit
aaea300699
+67
-91
@@ -360,25 +360,16 @@ fn render_overlay(
|
||||
// ── Todo ──────────────────────────────────────────────────────
|
||||
crate::app::state::types::Overlay::Todo => {
|
||||
let block = block
|
||||
.title(Span::styled(" 📋 Tasks ", Style::default().fg(Theme::ACCENT_PURPLE).add_modifier(Modifier::BOLD)))
|
||||
.title(Span::styled(" Tasks ", Style::default().fg(Theme::ACCENT_PURPLE).add_modifier(Modifier::BOLD)))
|
||||
.border_style(Style::default().fg(Theme::ACCENT_PURPLE));
|
||||
let msg_count = state.transcript_cache.messages.len();
|
||||
let lines = vec![
|
||||
Line::from(Span::styled(
|
||||
" Session Activity",
|
||||
Style::default().fg(Theme::TEXT).add_modifier(Modifier::BOLD),
|
||||
)),
|
||||
Line::from(Span::raw("")),
|
||||
Line::from(Span::styled(
|
||||
format!(" Messages: {msg_count}"),
|
||||
Style::default().fg(Theme::INFO),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" Overlay: {:?}", state.misc.overlay),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)),
|
||||
];
|
||||
let paragraph = Paragraph::new(lines).block(block);
|
||||
let content = if state.misc.todo_content.is_empty() {
|
||||
" No tasks yet."
|
||||
} else {
|
||||
&state.misc.todo_content
|
||||
};
|
||||
let paragraph = Paragraph::new(content)
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: false });
|
||||
frame.render_widget(paragraph, overlay_area);
|
||||
}
|
||||
|
||||
@@ -615,90 +606,75 @@ fn render_overlay(
|
||||
// ── Usage ────────────────────────────────────────────────────
|
||||
crate::app::state::types::Overlay::Usage => {
|
||||
let block = block
|
||||
.title(Span::styled(" 📊 Usage ", Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD)))
|
||||
.title(Span::styled(" Usage ", Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD)))
|
||||
.border_style(Style::default().fg(Theme::INFO));
|
||||
let runtime = state.session_runtime.as_ref();
|
||||
let (tokens_in, tokens_out, api_calls, review_tokens, session_start) = runtime
|
||||
.map_or((0, 0, 0, 0, 0), |r| {
|
||||
(
|
||||
r.usage.tokens_in,
|
||||
r.usage.tokens_out,
|
||||
r.usage.api_calls,
|
||||
r.usage.review_tokens,
|
||||
r.session_start,
|
||||
)
|
||||
});
|
||||
let now_ms = chrono::Utc::now().timestamp_millis();
|
||||
let summary = runtime.map(|r| sidebar::compute_usage_summary(&r.usage, r.session_start, now_ms));
|
||||
let (edit_count, lesson_count, review_count, consec_empty) = runtime
|
||||
.map_or((0, 0, 0, 0), |r| {
|
||||
(
|
||||
r.edit_count,
|
||||
r.lesson_count,
|
||||
r.review_count,
|
||||
r.consecutive_empty_reviews,
|
||||
)
|
||||
(r.edit_count, r.lesson_count, r.review_count, r.consecutive_empty_reviews)
|
||||
});
|
||||
let elapsed_ms = chrono::Utc::now().timestamp_millis().saturating_sub(session_start);
|
||||
let hours = elapsed_ms / 3_600_000;
|
||||
let minutes = (elapsed_ms % 3_600_000) / 60_000;
|
||||
let seconds = (elapsed_ms % 60000) / 1000;
|
||||
let total_tokens = tokens_in.saturating_add(tokens_out);
|
||||
let self_learning_total = review_tokens;
|
||||
let main_tokens = total_tokens.saturating_sub(self_learning_total);
|
||||
let lines = vec![
|
||||
let mut lines = vec![
|
||||
Line::from(Span::styled(
|
||||
" Token Usage",
|
||||
Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD),
|
||||
)),
|
||||
Line::from(Span::raw("")),
|
||||
Line::from(Span::styled(
|
||||
format!(" Main agent: {main_tokens} tokens"),
|
||||
Style::default().fg(Theme::TEXT),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" Self-learning: {self_learning_total} tokens"),
|
||||
Style::default().fg(Theme::TEXT_MUTED),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" Total: {total_tokens} tokens"),
|
||||
Style::default().fg(Theme::TEXT).add_modifier(Modifier::BOLD),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" API calls: {api_calls}"),
|
||||
Style::default().fg(Theme::TEXT),
|
||||
)),
|
||||
Line::from(Span::raw("")),
|
||||
Line::from(Span::styled(
|
||||
" Activity",
|
||||
Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" Edits: {edit_count}"),
|
||||
Style::default().fg(Theme::TEXT),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" Reviews: {review_count}"),
|
||||
Style::default().fg(Theme::TEXT),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" Lessons: {lesson_count}"),
|
||||
Style::default().fg(Theme::TEXT_MUTED),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" Empty reviews: {}",
|
||||
if consec_empty > 3 {
|
||||
format!("{consec_empty} ⚠")
|
||||
} else {
|
||||
consec_empty.to_string()
|
||||
},
|
||||
),
|
||||
Style::default().fg(if consec_empty > 3 { Theme::WARNING } else { Theme::TEXT_DIM }),
|
||||
)),
|
||||
Line::from(Span::raw("")),
|
||||
Line::from(Span::styled(
|
||||
format!(" Session: {hours}h {minutes}m {seconds}s"),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)),
|
||||
];
|
||||
if let Some(s) = &summary {
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" Main agent: {} tokens", s.main_tokens),
|
||||
Style::default().fg(Theme::TEXT),
|
||||
)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" Self-learning: {} tokens", s.self_learning_tokens),
|
||||
Style::default().fg(Theme::TEXT_MUTED),
|
||||
)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" Total: {} tokens", s.total_tokens),
|
||||
Style::default().fg(Theme::TEXT).add_modifier(Modifier::BOLD),
|
||||
)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" API calls: {}", s.api_calls),
|
||||
Style::default().fg(Theme::TEXT),
|
||||
)));
|
||||
} else {
|
||||
lines.push(Line::from(Span::styled(
|
||||
" No active session.",
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)));
|
||||
}
|
||||
lines.push(Line::from(Span::raw("")));
|
||||
lines.push(Line::from(Span::styled(
|
||||
" Activity",
|
||||
Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD),
|
||||
)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" Edits: {edit_count}"),
|
||||
Style::default().fg(Theme::TEXT),
|
||||
)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" Reviews: {review_count}"),
|
||||
Style::default().fg(Theme::TEXT),
|
||||
)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" Lessons: {lesson_count}"),
|
||||
Style::default().fg(Theme::TEXT_MUTED),
|
||||
)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" Empty reviews: {}",
|
||||
if consec_empty > 3 { format!("{consec_empty} ⚠") } else { consec_empty.to_string() },
|
||||
),
|
||||
Style::default().fg(if consec_empty > 3 { Theme::WARNING } else { Theme::TEXT_DIM }),
|
||||
)));
|
||||
if let Some(s) = &summary {
|
||||
lines.push(Line::from(Span::raw("")));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(" Session: {}h {}m {}s", s.elapsed_hours, s.elapsed_minutes, s.elapsed_seconds),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)));
|
||||
}
|
||||
let paragraph = Paragraph::new(lines).block(block);
|
||||
frame.render_widget(paragraph, overlay_area);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user