style: format seluruh workspace dengan cargo fmt
Menyeragamkan format kode sesuai rustfmt (126 file). Sebelumnya lefthook pre-commit 'cargo fmt --check' akan gagal pada commit apa pun.
This commit is contained in:
@@ -96,12 +96,16 @@ fn render_one_message(
|
||||
|
||||
if !msg.reasoning.trim().is_empty() {
|
||||
let reasoning_str = format!("[Thinking...]\n{}", msg.reasoning.trim());
|
||||
let reasoning_spans = super::markdown::render_markdown(&reasoning_str, content_width, false);
|
||||
let reasoning_spans =
|
||||
super::markdown::render_markdown(&reasoning_str, content_width, false);
|
||||
// Dim the reasoning text
|
||||
let dimmed_spans: Vec<Span> = reasoning_spans.into_iter().map(|mut s| {
|
||||
s.style = s.style.fg(Theme::TEXT_DIM);
|
||||
s
|
||||
}).collect();
|
||||
let dimmed_spans: Vec<Span> = reasoning_spans
|
||||
.into_iter()
|
||||
.map(|mut s| {
|
||||
s.style = s.style.fg(Theme::TEXT_DIM);
|
||||
s
|
||||
})
|
||||
.collect();
|
||||
let reasoning_lines = split_spans_into_lines(dimmed_spans);
|
||||
let mut lines_iter = reasoning_lines.into_iter();
|
||||
|
||||
@@ -156,7 +160,7 @@ fn render_one_message(
|
||||
lines.push(Line::from(spans));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !first_line_handled {
|
||||
lines.push(Line::from(header_prefix));
|
||||
}
|
||||
@@ -166,134 +170,136 @@ fn render_one_message(
|
||||
|
||||
impl Component for ChatComponent {
|
||||
fn pre_render(&mut self, state: &mut crate::state::AppStateRest) {
|
||||
let content_width = state.last_render_width.saturating_sub(PREFIX_WIDTH as u16 + 2);
|
||||
let msg_count = state.transcript_cache.messages.len();
|
||||
let cached_count = state.cached_msg_count;
|
||||
let content_width = state
|
||||
.last_render_width
|
||||
.saturating_sub(PREFIX_WIDTH as u16 + 2);
|
||||
let msg_count = state.transcript_cache.messages.len();
|
||||
let cached_count = state.cached_msg_count;
|
||||
|
||||
let needs_full_rebuild = state.transcript_cache.dirty
|
||||
&& (state.last_render_width == 0
|
||||
let needs_full_rebuild = state.transcript_cache.dirty
|
||||
&& (state.last_render_width == 0
|
||||
|| msg_count < cached_count // pesan di-evict dari depan
|
||||
|| state.render_width_at_cache != state.last_render_width); // resize
|
||||
|
||||
if needs_full_rebuild {
|
||||
// Full rebuild: parse semua pesan dari nol
|
||||
let mut all_lines: Vec<Line<'static>> = Vec::new();
|
||||
for msg in &state.transcript_cache.messages {
|
||||
let msg_lines = render_one_message(msg, content_width);
|
||||
all_lines.extend(msg_lines);
|
||||
}
|
||||
state.display_lines_cache = all_lines;
|
||||
state.cached_msg_count = msg_count;
|
||||
state.render_width_at_cache = state.last_render_width;
|
||||
state.transcript_cache.dirty = false;
|
||||
} else if state.transcript_cache.dirty && msg_count > cached_count {
|
||||
// Incremental: hanya render pesan baru yang belum ada di cache
|
||||
let new_msgs: Vec<_> = state
|
||||
.transcript_cache
|
||||
.messages
|
||||
.iter()
|
||||
.skip(cached_count)
|
||||
.collect();
|
||||
for msg in new_msgs {
|
||||
let msg_lines = render_one_message(msg, content_width);
|
||||
state.display_lines_cache.extend(msg_lines);
|
||||
}
|
||||
state.cached_msg_count = msg_count;
|
||||
state.transcript_cache.dirty = false;
|
||||
}
|
||||
|
||||
// Lazily recompute token count — hanya saat ada pesan baru
|
||||
if state.token_count_dirty {
|
||||
if let Some(ref rt) = state.session_runtime {
|
||||
state.cached_token_count = rt
|
||||
if needs_full_rebuild {
|
||||
// Full rebuild: parse semua pesan dari nol
|
||||
let mut all_lines: Vec<Line<'static>> = Vec::new();
|
||||
for msg in &state.transcript_cache.messages {
|
||||
let msg_lines = render_one_message(msg, content_width);
|
||||
all_lines.extend(msg_lines);
|
||||
}
|
||||
state.display_lines_cache = all_lines;
|
||||
state.cached_msg_count = msg_count;
|
||||
state.render_width_at_cache = state.last_render_width;
|
||||
state.transcript_cache.dirty = false;
|
||||
} else if state.transcript_cache.dirty && msg_count > cached_count {
|
||||
// Incremental: hanya render pesan baru yang belum ada di cache
|
||||
let new_msgs: Vec<_> = state
|
||||
.transcript_cache
|
||||
.messages
|
||||
.iter()
|
||||
.filter_map(|m| m.content.as_deref())
|
||||
.map(crate::state::count_tokens)
|
||||
.sum();
|
||||
} else {
|
||||
state.cached_token_count = 0;
|
||||
.skip(cached_count)
|
||||
.collect();
|
||||
for msg in new_msgs {
|
||||
let msg_lines = render_one_message(msg, content_width);
|
||||
state.display_lines_cache.extend(msg_lines);
|
||||
}
|
||||
state.cached_msg_count = msg_count;
|
||||
state.transcript_cache.dirty = false;
|
||||
}
|
||||
|
||||
// Lazily recompute token count — hanya saat ada pesan baru
|
||||
if state.token_count_dirty {
|
||||
if let Some(ref rt) = state.session_runtime {
|
||||
state.cached_token_count = rt
|
||||
.messages
|
||||
.iter()
|
||||
.filter_map(|m| m.content.as_deref())
|
||||
.map(crate::state::count_tokens)
|
||||
.sum();
|
||||
} else {
|
||||
state.cached_token_count = 0;
|
||||
}
|
||||
state.token_count_dirty = false;
|
||||
}
|
||||
state.token_count_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect, state: &crate::state::AppStateRest) {
|
||||
let messages = &state.transcript_cache.messages;
|
||||
let scroll_offset = state.scroll.offset;
|
||||
let max_visible = (area.height as usize).saturating_sub(3);
|
||||
let cache = &state.display_lines_cache;
|
||||
let messages = &state.transcript_cache.messages;
|
||||
let scroll_offset = state.scroll.offset;
|
||||
let max_visible = (area.height as usize).saturating_sub(3);
|
||||
let cache = &state.display_lines_cache;
|
||||
|
||||
// Hitung window tanpa clone
|
||||
let total = cache.len();
|
||||
let spinner_extra = usize::from(state.turn_in_flight());
|
||||
let total_with_spinner = total + spinner_extra;
|
||||
let max_offset = total_with_spinner.saturating_sub(max_visible);
|
||||
let offset = scroll_offset.min(max_offset);
|
||||
// Hitung window tanpa clone
|
||||
let total = cache.len();
|
||||
let spinner_extra = usize::from(state.turn_in_flight());
|
||||
let total_with_spinner = total + spinner_extra;
|
||||
let max_offset = total_with_spinner.saturating_sub(max_visible);
|
||||
let offset = scroll_offset.min(max_offset);
|
||||
|
||||
let end_idx = total_with_spinner.saturating_sub(offset);
|
||||
let start_idx = end_idx.saturating_sub(max_visible);
|
||||
let end_idx = total_with_spinner.saturating_sub(offset);
|
||||
let start_idx = end_idx.saturating_sub(max_visible);
|
||||
|
||||
// Kumpulkan hanya baris yang visible — tidak clone semua
|
||||
let mut visible: Vec<Line> = Vec::with_capacity(max_visible);
|
||||
let cache_end = end_idx.min(total);
|
||||
let cache_start = start_idx.min(cache_end);
|
||||
if cache_start < cache_end {
|
||||
visible.extend_from_slice(&cache[cache_start..cache_end]);
|
||||
}
|
||||
// Kumpulkan hanya baris yang visible — tidak clone semua
|
||||
let mut visible: Vec<Line> = Vec::with_capacity(max_visible);
|
||||
let cache_end = end_idx.min(total);
|
||||
let cache_start = start_idx.min(cache_end);
|
||||
if cache_start < cache_end {
|
||||
visible.extend_from_slice(&cache[cache_start..cache_end]);
|
||||
}
|
||||
|
||||
// Spinner hanya ditambahkan jika visible window mencakup posisi terakhir
|
||||
if state.turn_in_flight() && end_idx > total {
|
||||
let spinner_frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||
let frame_idx = (state.misc.tick_count as usize / 2) % spinner_frames.len();
|
||||
let spinner = spinner_frames[frame_idx];
|
||||
visible.push(Line::from(vec![
|
||||
Span::styled(
|
||||
format!("{} ", format_role_label(&Role::Assistant)),
|
||||
Style::default()
|
||||
.fg(Theme::ROLE_ASSISTANT)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::styled(format!("{spinner} "), Style::default().fg(Theme::TEXT_DIM)),
|
||||
Span::styled(
|
||||
"generating...",
|
||||
// Spinner hanya ditambahkan jika visible window mencakup posisi terakhir
|
||||
if state.turn_in_flight() && end_idx > total {
|
||||
let spinner_frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||
let frame_idx = (state.misc.tick_count as usize / 2) % spinner_frames.len();
|
||||
let spinner = spinner_frames[frame_idx];
|
||||
visible.push(Line::from(vec![
|
||||
Span::styled(
|
||||
format!("{} ", format_role_label(&Role::Assistant)),
|
||||
Style::default()
|
||||
.fg(Theme::ROLE_ASSISTANT)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::styled(format!("{spinner} "), Style::default().fg(Theme::TEXT_DIM)),
|
||||
Span::styled(
|
||||
"generating...",
|
||||
Style::default()
|
||||
.fg(Theme::TEXT_MUTED)
|
||||
.add_modifier(Modifier::ITALIC),
|
||||
),
|
||||
]));
|
||||
}
|
||||
|
||||
let scroll_pct = if total_with_spinner > max_visible && max_offset > 0 {
|
||||
((offset as f64 / max_offset as f64) * 100.0) as u8
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let title = if scroll_pct > 0 {
|
||||
format!(" 💬 Chat [{} msgs] ── {}% ↑ ", messages.len(), scroll_pct)
|
||||
} else if messages.is_empty() {
|
||||
String::from(" 💬 Chat ")
|
||||
} else {
|
||||
format!(" 💬 Chat [{} msgs] ", messages.len())
|
||||
};
|
||||
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.border_style(Style::default().fg(Theme::BORDER))
|
||||
.title(Span::styled(
|
||||
title,
|
||||
Style::default()
|
||||
.fg(Theme::TEXT_MUTED)
|
||||
.add_modifier(Modifier::ITALIC),
|
||||
),
|
||||
]));
|
||||
}
|
||||
.add_modifier(Modifier::BOLD),
|
||||
));
|
||||
|
||||
let scroll_pct = if total_with_spinner > max_visible && max_offset > 0 {
|
||||
((offset as f64 / max_offset as f64) * 100.0) as u8
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let paragraph = Paragraph::new(visible)
|
||||
.block(block)
|
||||
.style(Style::default().bg(Theme::BG));
|
||||
|
||||
let title = if scroll_pct > 0 {
|
||||
format!(" 💬 Chat [{} msgs] ── {}% ↑ ", messages.len(), scroll_pct)
|
||||
} else if messages.is_empty() {
|
||||
String::from(" 💬 Chat ")
|
||||
} else {
|
||||
format!(" 💬 Chat [{} msgs] ", messages.len())
|
||||
};
|
||||
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.border_style(Style::default().fg(Theme::BORDER))
|
||||
.title(Span::styled(
|
||||
title,
|
||||
Style::default()
|
||||
.fg(Theme::TEXT_MUTED)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
));
|
||||
|
||||
let paragraph = Paragraph::new(visible)
|
||||
.block(block)
|
||||
.style(Style::default().bg(Theme::BG));
|
||||
|
||||
frame.render_widget(paragraph, area);
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user