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 ──────────────────────────────────────────────────────
|
// ── Todo ──────────────────────────────────────────────────────
|
||||||
crate::app::state::types::Overlay::Todo => {
|
crate::app::state::types::Overlay::Todo => {
|
||||||
let block = block
|
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));
|
.border_style(Style::default().fg(Theme::ACCENT_PURPLE));
|
||||||
let msg_count = state.transcript_cache.messages.len();
|
let content = if state.misc.todo_content.is_empty() {
|
||||||
let lines = vec![
|
" No tasks yet."
|
||||||
Line::from(Span::styled(
|
} else {
|
||||||
" Session Activity",
|
&state.misc.todo_content
|
||||||
Style::default().fg(Theme::TEXT).add_modifier(Modifier::BOLD),
|
};
|
||||||
)),
|
let paragraph = Paragraph::new(content)
|
||||||
Line::from(Span::raw("")),
|
.block(block)
|
||||||
Line::from(Span::styled(
|
.wrap(Wrap { trim: false });
|
||||||
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);
|
|
||||||
frame.render_widget(paragraph, overlay_area);
|
frame.render_widget(paragraph, overlay_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,90 +606,75 @@ fn render_overlay(
|
|||||||
// ── Usage ────────────────────────────────────────────────────
|
// ── Usage ────────────────────────────────────────────────────
|
||||||
crate::app::state::types::Overlay::Usage => {
|
crate::app::state::types::Overlay::Usage => {
|
||||||
let block = block
|
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));
|
.border_style(Style::default().fg(Theme::INFO));
|
||||||
let runtime = state.session_runtime.as_ref();
|
let runtime = state.session_runtime.as_ref();
|
||||||
let (tokens_in, tokens_out, api_calls, review_tokens, session_start) = runtime
|
let now_ms = chrono::Utc::now().timestamp_millis();
|
||||||
.map_or((0, 0, 0, 0, 0), |r| {
|
let summary = runtime.map(|r| sidebar::compute_usage_summary(&r.usage, r.session_start, now_ms));
|
||||||
(
|
|
||||||
r.usage.tokens_in,
|
|
||||||
r.usage.tokens_out,
|
|
||||||
r.usage.api_calls,
|
|
||||||
r.usage.review_tokens,
|
|
||||||
r.session_start,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let (edit_count, lesson_count, review_count, consec_empty) = runtime
|
let (edit_count, lesson_count, review_count, consec_empty) = runtime
|
||||||
.map_or((0, 0, 0, 0), |r| {
|
.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 mut lines = vec![
|
||||||
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![
|
|
||||||
Line::from(Span::styled(
|
Line::from(Span::styled(
|
||||||
" Token Usage",
|
" Token Usage",
|
||||||
Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD),
|
Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD),
|
||||||
)),
|
)),
|
||||||
Line::from(Span::raw("")),
|
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);
|
let paragraph = Paragraph::new(lines).block(block);
|
||||||
frame.render_widget(paragraph, overlay_area);
|
frame.render_widget(paragraph, overlay_area);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user