agent_ui: Fix markdown block for tool call input and output content (#45454)

Danilo Leal created

This PR fixes two issues with regards to markdown codeblocks rendered in
tool call input and output content display:
- the JSON code snippets weren't properly indented
- codeblocks weren't being rendered in unique containers; e.g., if you
hovered one scrollbar, all of them would also be hovered, even though
horizontal scrolling itself worked properly

Here's the end result:


https://github.com/user-attachments/assets/3d6daf64-0f88-4a16-a5a0-94998c1ba7e2

Release Notes:

- agent: Fix scrollbar and JSON indentation for tool call input/output
content's markdown codeblocks.

Change summary

crates/acp_thread/src/acp_thread.rs    |  5 ++++-
crates/agent_ui/src/acp/thread_view.rs | 28 ++++++++++++++++------------
2 files changed, 20 insertions(+), 13 deletions(-)

Detailed changes

crates/acp_thread/src/acp_thread.rs 🔗

@@ -11,6 +11,7 @@ use language::language_settings::FormatOnSave;
 pub use mention::*;
 use project::lsp_store::{FormatTrigger, LspFormatTarget};
 use serde::{Deserialize, Serialize};
+use serde_json::to_string_pretty;
 use settings::Settings as _;
 use task::{Shell, ShellBuilder};
 pub use terminal::*;
@@ -2422,8 +2423,10 @@ fn markdown_for_raw_output(
             )
         })),
         value => Some(cx.new(|cx| {
+            let pretty_json = to_string_pretty(value).unwrap_or_else(|_| value.to_string());
+
             Markdown::new(
-                format!("```json\n{}\n```", value).into(),
+                format!("```json\n{}\n```", pretty_json).into(),
                 Some(language_registry.clone()),
                 None,
                 cx,

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -2489,9 +2489,11 @@ impl AcpThreadView {
                                     .border_color(self.tool_card_border_color(cx))
                                     .child(input_output_header("Raw Input:".into()))
                                     .children(tool_call.raw_input_markdown.clone().map(|input| {
-                                        self.render_markdown(
-                                            input,
-                                            default_markdown_style(false, false, window, cx),
+                                        div().id(("tool-call-raw-input-markdown", entry_ix)).child(
+                                            self.render_markdown(
+                                                input,
+                                                default_markdown_style(false, false, window, cx),
+                                            ),
                                         )
                                     }))
                                     .child(input_output_header("Output:".into())),
@@ -2499,15 +2501,17 @@ impl AcpThreadView {
                         })
                         .children(tool_call.content.iter().enumerate().map(
                             |(content_ix, content)| {
-                                div().child(self.render_tool_call_content(
-                                    entry_ix,
-                                    content,
-                                    content_ix,
-                                    tool_call,
-                                    use_card_layout,
-                                    window,
-                                    cx,
-                                ))
+                                div().id(("tool-call-output", entry_ix)).child(
+                                    self.render_tool_call_content(
+                                        entry_ix,
+                                        content,
+                                        content_ix,
+                                        tool_call,
+                                        use_card_layout,
+                                        window,
+                                        cx,
+                                    ),
+                                )
                             },
                         ))
                         .into_any(),