From 2f1a4d85a1fdc9cbfb81912a3206c057ad9d1ed5 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Tue, 14 Jul 2026 22:34:55 +0700 Subject: [PATCH] fix(tui): perbaiki rendering multi-baris pada pesan Tool Konten Tool sebelumnya didorong sebagai satu Span tanpa split newline, sehingga output tool multi-baris (stdout bash, hasil grep, diff) tampil sebagai karakter \n literal alih-alih baris terpisah. Sekarang mengikuti pola render_markdown + split_spans_into_lines yang sama dengan role lain. Co-Authored-By: Claude Sonnet 5 --- src/view/chat.rs | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/view/chat.rs b/src/view/chat.rs index 6876577..ef89fe9 100644 --- a/src/view/chat.rs +++ b/src/view/chat.rs @@ -117,18 +117,40 @@ pub fn draw_chat(frame: &mut Frame, area: Rect, state: &crate::app::state::rest: let is_last = std::ptr::eq(msg, messages.last().unwrap()); // Tool messages render as a dim sub-line attached to whatever came - // before — no header, no speaker-change bookkeeping. + // before — no header, no speaker-change bookkeeping. Content is run + // through the same render_markdown + split_spans_into_lines pipeline + // as every other role so multi-line tool output (bash stdout, grep + // matches, diffs) becomes real wrapped `Line`s instead of a literal + // `\n` inside one Span; every rendered span is then re-styled dim + // italic to preserve the original single-line look. if msg.role == Role::Tool { let content = if msg.content.trim().is_empty() { "(tool execution)".to_string() } else { msg.content.clone() }; - display_lines.push(Line::from(vec![ + let dim = Style::default().fg(Theme::TEXT_DIM); + let dim_italic = dim.add_modifier(Modifier::ITALIC); + + let content_spans = super::markdown::render_markdown(&content, content_width); + let content_lines = split_spans_into_lines(content_spans); + let mut lines_iter = content_lines.into_iter(); + + let first_spans = lines_iter.next().map_or_else(Vec::new, |line| { + line.spans.into_iter().map(|s| Span::styled(s.content, dim_italic)).collect() + }); + let mut spans = vec![ Span::raw(" ".repeat(PREFIX_WIDTH)), - Span::styled("↳ ", Style::default().fg(Theme::TEXT_DIM)), - Span::styled(content, Style::default().fg(Theme::TEXT_DIM).add_modifier(Modifier::ITALIC)), - ])); + Span::styled("↳ ", dim), + ]; + spans.extend(first_spans); + display_lines.push(Line::from(spans)); + + for line in lines_iter { + let mut spans = vec![Span::raw(" ".repeat(PREFIX_WIDTH))]; + spans.extend(line.spans.into_iter().map(|s| Span::styled(s.content, dim_italic))); + display_lines.push(Line::from(spans)); + } continue; }