Skip to main content

steer_tui/tui/widgets/formatters/
external.rs

1use super::{
2    ToolFormatter,
3    helpers::{json_preview, separator_line, tool_error_user_message, truncate_middle, wrap_lines},
4};
5use crate::tui::theme::Theme;
6use ratatui::{
7    style::Style,
8    text::{Line, Span},
9};
10use serde_json::Value;
11use steer_grpc::client_api::ToolResult;
12use textwrap;
13
14/// Fallback formatter for MCP/external tools (names starting with "mcp__")
15/// Displays parameters and payload/error in a generic way.
16pub struct ExternalFormatter;
17
18impl ToolFormatter for ExternalFormatter {
19    fn compact(
20        &self,
21        params: &Value,
22        result: &Option<ToolResult>,
23        _wrap_width: usize,
24        theme: &Theme,
25    ) -> Vec<Line<'static>> {
26        // Show a one-liner with param preview and short payload/error summary.
27        let mut spans = Vec::new();
28
29        // Param preview
30        let preview = json_preview(params, 30);
31        spans.push(Span::styled(preview.clone(), theme.dim_text()));
32
33        if let Some(result) = result {
34            match result {
35                ToolResult::External(ext) => {
36                    let payload_preview = truncate_middle(&ext.payload, 40);
37                    spans.push(Span::raw(" → "));
38                    spans.push(Span::styled(payload_preview, Style::default()));
39                }
40                ToolResult::Error(err) => {
41                    spans.push(Span::raw(" ✗ "));
42                    spans.push(Span::styled(
43                        tool_error_user_message(err).into_owned(),
44                        theme.error_text(),
45                    ));
46                }
47                _ => {}
48            }
49        }
50
51        vec![Line::from(spans)]
52    }
53
54    fn detailed(
55        &self,
56        params: &Value,
57        result: &Option<ToolResult>,
58        wrap_width: usize,
59        theme: &Theme,
60    ) -> Vec<Line<'static>> {
61        let mut lines = Vec::new();
62
63        // Parameters block
64        lines.push(Line::from(Span::styled("Parameters:", theme.text())));
65
66        let pretty_params = serde_json::to_string_pretty(params).unwrap_or_default();
67        for line in wrap_lines(pretty_params.lines(), wrap_width) {
68            lines.push(Line::from(Span::styled(line, theme.dim_text())));
69        }
70
71        // Result block
72        if let Some(result) = result {
73            lines.push(separator_line(wrap_width, theme.dim_text()));
74            match result {
75                ToolResult::External(ext) => {
76                    // Attempt to pretty-print JSON payload with 2-space indent
77                    if let Ok(json_val) = serde_json::from_str::<serde_json::Value>(&ext.payload) {
78                        if let Ok(pretty) = serde_json::to_string_pretty(&json_val) {
79                            for ln in wrap_lines(pretty.lines(), wrap_width) {
80                                lines.push(Line::from(Span::raw(ln)));
81                            }
82                        } else {
83                            // Fallback to raw text if serialization fails
84                            for wrapped in textwrap::wrap(&ext.payload, wrap_width) {
85                                lines.push(Line::from(Span::raw(wrapped.to_string())));
86                            }
87                        }
88                    } else {
89                        // Non-JSON payload – render raw text
90                        for wrapped in textwrap::wrap(&ext.payload, wrap_width) {
91                            lines.push(Line::from(Span::raw(wrapped.to_string())));
92                        }
93                    }
94                }
95                ToolResult::Error(err) => {
96                    for wrapped in textwrap::wrap(&tool_error_user_message(err), wrap_width) {
97                        lines.push(Line::from(Span::styled(
98                            wrapped.to_string(),
99                            theme.error_text(),
100                        )));
101                    }
102                }
103                _ => {}
104            }
105        }
106
107        lines
108    }
109}