agent: Add design tweaks (#28963)

One more batch of fine-tuning the agent panel's design.

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-04-17 12:20:25 -03:00 committed by GitHub
parent 8117940aca
commit 2a878ee6d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 161 additions and 87 deletions

View file

@ -501,11 +501,13 @@ fn render_markdown_code_block(
let codeblock_header = h_flex() let codeblock_header = h_flex()
.group("codeblock_header") .group("codeblock_header")
.p_1() .py_1()
.pl_1p5()
.pr_1()
.gap_1() .gap_1()
.justify_between() .justify_between()
.border_b_1() .border_b_1()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border.opacity(0.6))
.bg(codeblock_header_bg) .bg(codeblock_header_bg)
.rounded_t_md() .rounded_t_md()
.children(label) .children(label)
@ -599,7 +601,7 @@ fn render_markdown_code_block(
.overflow_hidden() .overflow_hidden()
.rounded_lg() .rounded_lg()
.border_1() .border_1()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border.opacity(0.6))
.bg(cx.theme().colors().editor_background) .bg(cx.theme().colors().editor_background)
.child(codeblock_header) .child(codeblock_header)
.when( .when(
@ -1504,7 +1506,14 @@ impl ActiveThread {
window.dispatch_action(Box::new(OpenActiveThreadAsMarkdown), cx) window.dispatch_action(Box::new(OpenActiveThreadAsMarkdown), cx)
}); });
let feedback_container = h_flex().py_2().px_4().gap_1().justify_between(); // For all items that should be aligned with the Assistant's response.
const RESPONSE_PADDING_X: Pixels = px(18.);
let feedback_container = h_flex()
.py_2()
.px(RESPONSE_PADDING_X)
.gap_1()
.justify_between();
let feedback_items = match self.thread.read(cx).message_feedback(message_id) { let feedback_items = match self.thread.read(cx).message_feedback(message_id) {
Some(feedback) => feedback_container Some(feedback) => feedback_container
.child( .child(
@ -1705,9 +1714,9 @@ impl ActiveThread {
this.pt_4() this.pt_4()
} }
}) })
.pb_4()
.pl_2() .pl_2()
.pr_2p5() .pr_2p5()
.pb_4()
.child( .child(
v_flex() v_flex()
.bg(colors.editor_background) .bg(colors.editor_background)
@ -1814,9 +1823,8 @@ impl ActiveThread {
), ),
Role::Assistant => v_flex() Role::Assistant => v_flex()
.id(("message-container", ix)) .id(("message-container", ix))
.ml_2p5() .px(RESPONSE_PADDING_X)
.pl_2() .gap_2()
.pr_4()
.children(message_content) .children(message_content)
.when(has_tool_uses, |parent| { .when(has_tool_uses, |parent| {
parent.children( parent.children(
@ -1840,9 +1848,10 @@ impl ActiveThread {
message_id > *editing_message_id message_id > *editing_message_id
}); });
let panel_background = cx.theme().colors().panel_background;
v_flex() v_flex()
.w_full() .w_full()
.when(after_editing_message, |parent| parent.opacity(0.2))
.when_some(checkpoint, |parent, checkpoint| { .when_some(checkpoint, |parent, checkpoint| {
let mut is_pending = false; let mut is_pending = false;
let mut error = None; let mut error = None;
@ -2004,6 +2013,18 @@ impl ActiveThread {
}, },
) )
}) })
.when(after_editing_message, |parent| {
// Backdrop to dim out the whole thread below the editing user message
parent.relative().child(
div()
.occlude()
.absolute()
.inset_0()
.size_full()
.bg(panel_background)
.opacity(0.8),
)
})
.into_any() .into_any()
} }
@ -2030,6 +2051,15 @@ impl ActiveThread {
None None
}; };
let message_role = self
.thread
.read(cx)
.message(message_id)
.map(|m| m.role)
.unwrap_or(Role::User);
let is_assistant = message_role == Role::Assistant;
v_flex() v_flex()
.text_ui(cx) .text_ui(cx)
.gap_2() .gap_2()
@ -2050,17 +2080,24 @@ impl ActiveThread {
cx, cx,
) )
.into_any_element(), .into_any_element(),
RenderedMessageSegment::Text(markdown) => div() RenderedMessageSegment::Text(markdown) => {
.child( let markdown_element = MarkdownElement::new(
MarkdownElement::new(
markdown.clone(), markdown.clone(),
default_markdown_style(window, cx), default_markdown_style(window, cx),
) );
.code_block_renderer(markdown::CodeBlockRenderer::Custom {
let markdown_element = if is_assistant {
markdown_element.code_block_renderer(
markdown::CodeBlockRenderer::Custom {
render: Arc::new({ render: Arc::new({
let workspace = workspace.clone(); let workspace = workspace.clone();
let active_thread = cx.entity(); let active_thread = cx.entity();
move |kind, parsed_markdown, range, metadata, window, cx| { move |kind,
parsed_markdown,
range,
metadata,
window,
cx| {
render_markdown_code_block( render_markdown_code_block(
message_id, message_id,
range.start, range.start,
@ -2101,7 +2138,9 @@ impl ActiveThread {
.bg(gpui::linear_gradient( .bg(gpui::linear_gradient(
0., 0.,
gpui::linear_color_stop( gpui::linear_color_stop(
cx.theme().colors().editor_background, cx.theme()
.colors()
.editor_background,
0., 0.,
), ),
gpui::linear_color_stop( gpui::linear_color_stop(
@ -2115,15 +2154,26 @@ impl ActiveThread {
) )
} }
})), })),
}) },
.on_url_click({ )
} else {
markdown_element.code_block_renderer(
markdown::CodeBlockRenderer::Default {
copy_button: false,
border: true,
},
)
};
div()
.child(markdown_element.on_url_click({
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
move |text, window, cx| { move |text, window, cx| {
open_markdown_link(text, workspace.clone(), window, cx); open_markdown_link(text, workspace.clone(), window, cx);
} }
}), }))
) .into_any_element()
.into_any_element(), }
}, },
), ),
) )
@ -2392,6 +2442,7 @@ impl ActiveThread {
.get(&tool_use.id) .get(&tool_use.id)
.copied() .copied()
.unwrap_or_default(); .unwrap_or_default();
let is_status_finished = matches!(&tool_use.status, ToolUseStatus::Finished(_)); let is_status_finished = matches!(&tool_use.status, ToolUseStatus::Finished(_));
let fs = self let fs = self
@ -2452,6 +2503,7 @@ impl ActiveThread {
) )
.code_block_renderer(markdown::CodeBlockRenderer::Default { .code_block_renderer(markdown::CodeBlockRenderer::Default {
copy_button: false, copy_button: false,
border: false,
}) })
.on_url_click({ .on_url_click({
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
@ -2481,6 +2533,7 @@ impl ActiveThread {
) )
.code_block_renderer(markdown::CodeBlockRenderer::Default { .code_block_renderer(markdown::CodeBlockRenderer::Default {
copy_button: false, copy_button: false,
border: false,
}) })
.on_url_click({ .on_url_click({
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
@ -2583,11 +2636,10 @@ impl ActiveThread {
)) ))
}; };
div().map(|element| { v_flex().gap_1().mb_3().map(|element| {
if !edit_tools { if !edit_tools {
element.child( element.child(
v_flex() v_flex()
.my_2()
.child( .child(
h_flex() h_flex()
.group("disclosure-header") .group("disclosure-header")
@ -2609,7 +2661,7 @@ impl ActiveThread {
.color(Color::Muted), .color(Color::Muted),
) )
.child( .child(
h_flex().pr_8().text_ui_sm(cx).children( h_flex().pr_8().text_size(rems(0.8125)).children(
rendered_tool_use.map(|rendered| MarkdownElement::new(rendered.label, tool_use_markdown_style(window, cx)).on_url_click({let workspace = self.workspace.clone(); move |text, window, cx| { rendered_tool_use.map(|rendered| MarkdownElement::new(rendered.label, tool_use_markdown_style(window, cx)).on_url_click({let workspace = self.workspace.clone(); move |text, window, cx| {
open_markdown_link(text, workspace.clone(), window, cx); open_markdown_link(text, workspace.clone(), window, cx);
}})) }}))
@ -2659,7 +2711,7 @@ impl ActiveThread {
) )
} else { } else {
v_flex() v_flex()
.my_2() .mb_2()
.rounded_lg() .rounded_lg()
.border_1() .border_1()
.border_color(self.tool_card_border_color(cx)) .border_color(self.tool_card_border_color(cx))

View file

@ -1180,8 +1180,8 @@ impl AssistantPanel {
parent parent
.child( .child(
h_flex() h_flex()
.mr_0p5() .mr_1()
.size_2() .size_2p5()
.justify_center() .justify_center()
.rounded_full() .rounded_full()
.bg(cx.theme().colors().text.opacity(0.1)) .bg(cx.theme().colors().text.opacity(0.1))

View file

@ -632,6 +632,7 @@ impl CompletionsMenu {
MarkdownElement::new(markdown.clone(), hover_markdown_style(window, cx)) MarkdownElement::new(markdown.clone(), hover_markdown_style(window, cx))
.code_block_renderer(markdown::CodeBlockRenderer::Default { .code_block_renderer(markdown::CodeBlockRenderer::Default {
copy_button: false, copy_button: false,
border: false,
}) })
.on_url_click(open_markdown_url), .on_url_click(open_markdown_url),
) )

View file

@ -841,6 +841,7 @@ impl InfoPopover {
MarkdownElement::new(markdown, hover_markdown_style(window, cx)) MarkdownElement::new(markdown, hover_markdown_style(window, cx))
.code_block_renderer(markdown::CodeBlockRenderer::Default { .code_block_renderer(markdown::CodeBlockRenderer::Default {
copy_button: false, copy_button: false,
border: false,
}) })
.on_url_click(open_markdown_url), .on_url_click(open_markdown_url),
), ),
@ -969,6 +970,7 @@ impl DiagnosticPopover {
}) })
.code_block_renderer(markdown::CodeBlockRenderer::Default { .code_block_renderer(markdown::CodeBlockRenderer::Default {
copy_button: false, copy_button: false,
border: false,
}) })
.on_url_click(open_markdown_url), .on_url_click(open_markdown_url),
) )

View file

@ -107,6 +107,7 @@ struct Options {
pub enum CodeBlockRenderer { pub enum CodeBlockRenderer {
Default { Default {
copy_button: bool, copy_button: bool,
border: bool,
}, },
Custom { Custom {
render: CodeBlockRenderFn, render: CodeBlockRenderFn,
@ -381,7 +382,10 @@ impl MarkdownElement {
Self { Self {
markdown, markdown,
style, style,
code_block_renderer: CodeBlockRenderer::Default { copy_button: true }, code_block_renderer: CodeBlockRenderer::Default {
copy_button: true,
border: false,
},
on_url_click: None, on_url_click: None,
} }
} }
@ -748,6 +752,21 @@ impl Element for MarkdownElement {
code_block.w_full() code_block.w_full()
} }
}); });
// Apply border if required
// Usage examples:
// CodeBlockRenderer::Default { copy_button: true, border: true } - Both copy button and border
// CodeBlockRenderer::Default { copy_button: false, border: true } - Border only
// CodeBlockRenderer::Default { copy_button: true, border: false } - Copy button only (default)
if let CodeBlockRenderer::Default { border: true, .. } =
&self.code_block_renderer
{
code_block = code_block
.rounded_md()
.border_1()
.border_color(cx.theme().colors().border_variant);
}
code_block.style().refine(&self.style.code_block); code_block.style().refine(&self.style.code_block);
if let Some(code_block_text_style) = &self.style.code_block.text if let Some(code_block_text_style) = &self.style.code_block.text
{ {
@ -947,10 +966,10 @@ impl Element for MarkdownElement {
}); });
} }
if matches!( if let CodeBlockRenderer::Default {
&self.code_block_renderer, copy_button: true, ..
CodeBlockRenderer::Default { copy_button: true } } = &self.code_block_renderer
) { {
builder.flush_text(); builder.flush_text();
builder.modify_current_div(|el| { builder.modify_current_div(|el| {
let content_range = parser::extract_code_block_content_range( let content_range = parser::extract_code_block_content_range(