steer_tui/tui/widgets/formatters/
replace.rs1use super::ToolFormatter;
2use crate::tui::theme::{Component, Theme};
3use ratatui::{
4 style::{Color, Modifier, Style},
5 text::{Line, Span},
6};
7use serde_json::Value;
8use steer_core::app::conversation::ToolResult;
9use steer_tools::tools::replace::ReplaceParams;
10
11pub struct ReplaceFormatter;
12
13impl ToolFormatter for ReplaceFormatter {
14 fn compact(
15 &self,
16 params: &Value,
17 _result: &Option<ToolResult>,
18 _wrap_width: usize,
19 theme: &Theme,
20 ) -> Vec<Line<'static>> {
21 let mut lines = Vec::new();
22
23 let Ok(params) = serde_json::from_value::<ReplaceParams>(params.clone()) else {
24 return vec![Line::from(Span::styled(
25 "Invalid replace params",
26 theme.style(Component::ErrorText),
27 ))];
28 };
29
30 let line_count = params.content.lines().count();
31
32 lines.push(Line::from(vec![
33 Span::styled(format!("{} ", params.file_path), Style::default()),
34 Span::styled(
35 format!("({line_count} lines)"),
36 theme
37 .style(Component::DimText)
38 .add_modifier(Modifier::ITALIC),
39 ),
40 ]));
41
42 lines
43 }
44
45 fn detailed(
46 &self,
47 params: &Value,
48 result: &Option<ToolResult>,
49 wrap_width: usize,
50 theme: &Theme,
51 ) -> Vec<Line<'static>> {
52 let mut lines = Vec::new();
53
54 let Ok(params) = serde_json::from_value::<ReplaceParams>(params.clone()) else {
55 return vec![Line::from(Span::styled(
56 "Invalid replace params",
57 theme.style(Component::ErrorText),
58 ))];
59 };
60
61 lines.push(Line::from(Span::styled(
62 format!("Replacing {}", params.file_path),
63 Style::default()
64 .fg(Color::Yellow)
65 .add_modifier(Modifier::BOLD),
66 )));
67
68 if result.is_none() {
69 lines.push(Line::from(Span::styled(
71 format!("+++ {} (Full New Content)", params.file_path),
72 theme.style(Component::ToolSuccess),
73 )));
74
75 const MAX_PREVIEW_LINES: usize = 15;
76 for (idx, line) in params.content.lines().enumerate() {
77 if idx >= MAX_PREVIEW_LINES {
78 lines.push(Line::from(Span::styled(
79 format!(
80 "... ({} more lines)",
81 params.content.lines().count() - MAX_PREVIEW_LINES
82 ),
83 theme
84 .style(Component::DimText)
85 .add_modifier(Modifier::ITALIC),
86 )));
87 break;
88 }
89 for wrapped_line in textwrap::wrap(line, wrap_width) {
90 lines.push(Line::from(Span::styled(
91 format!("+ {wrapped_line}"),
92 theme.style(Component::ToolSuccess),
93 )));
94 }
95 }
96 }
97
98 if let Some(ToolResult::Error(error)) = result {
100 lines.push(Line::from(Span::styled(
101 error.to_string(),
102 theme.style(Component::ErrorText),
103 )));
104 }
105
106 lines
107 }
108}