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:
parent
8117940aca
commit
2a878ee6d0
5 changed files with 161 additions and 87 deletions
|
@ -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,80 +2080,100 @@ 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 {
|
|
||||||
render: Arc::new({
|
|
||||||
let workspace = workspace.clone();
|
|
||||||
let active_thread = cx.entity();
|
|
||||||
move |kind, parsed_markdown, range, metadata, window, cx| {
|
|
||||||
render_markdown_code_block(
|
|
||||||
message_id,
|
|
||||||
range.start,
|
|
||||||
kind,
|
|
||||||
parsed_markdown,
|
|
||||||
metadata,
|
|
||||||
active_thread.clone(),
|
|
||||||
workspace.clone(),
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
transform: Some(Arc::new({
|
|
||||||
let active_thread = cx.entity();
|
|
||||||
move |el, range, metadata, _, cx| {
|
|
||||||
let is_expanded = active_thread
|
|
||||||
.read(cx)
|
|
||||||
.expanded_code_blocks
|
|
||||||
.get(&(message_id, range.start))
|
|
||||||
.copied()
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
if is_expanded
|
let markdown_element = if is_assistant {
|
||||||
|| metadata.line_count
|
markdown_element.code_block_renderer(
|
||||||
<= MAX_UNCOLLAPSED_LINES_IN_CODE_BLOCK
|
markdown::CodeBlockRenderer::Custom {
|
||||||
{
|
render: Arc::new({
|
||||||
return el;
|
let workspace = workspace.clone();
|
||||||
|
let active_thread = cx.entity();
|
||||||
|
move |kind,
|
||||||
|
parsed_markdown,
|
||||||
|
range,
|
||||||
|
metadata,
|
||||||
|
window,
|
||||||
|
cx| {
|
||||||
|
render_markdown_code_block(
|
||||||
|
message_id,
|
||||||
|
range.start,
|
||||||
|
kind,
|
||||||
|
parsed_markdown,
|
||||||
|
metadata,
|
||||||
|
active_thread.clone(),
|
||||||
|
workspace.clone(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
el.child(
|
}),
|
||||||
div()
|
transform: Some(Arc::new({
|
||||||
.absolute()
|
let active_thread = cx.entity();
|
||||||
.bottom_0()
|
move |el, range, metadata, _, cx| {
|
||||||
.left_0()
|
let is_expanded = active_thread
|
||||||
.w_full()
|
.read(cx)
|
||||||
.h_1_4()
|
.expanded_code_blocks
|
||||||
.rounded_b_lg()
|
.get(&(message_id, range.start))
|
||||||
.bg(gpui::linear_gradient(
|
.copied()
|
||||||
0.,
|
.unwrap_or(false);
|
||||||
gpui::linear_color_stop(
|
|
||||||
cx.theme().colors().editor_background,
|
if is_expanded
|
||||||
|
|| metadata.line_count
|
||||||
|
<= MAX_UNCOLLAPSED_LINES_IN_CODE_BLOCK
|
||||||
|
{
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
el.child(
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.bottom_0()
|
||||||
|
.left_0()
|
||||||
|
.w_full()
|
||||||
|
.h_1_4()
|
||||||
|
.rounded_b_lg()
|
||||||
|
.bg(gpui::linear_gradient(
|
||||||
0.,
|
0.,
|
||||||
),
|
gpui::linear_color_stop(
|
||||||
gpui::linear_color_stop(
|
cx.theme()
|
||||||
cx.theme()
|
.colors()
|
||||||
.colors()
|
.editor_background,
|
||||||
.editor_background
|
0.,
|
||||||
.opacity(0.),
|
),
|
||||||
1.,
|
gpui::linear_color_stop(
|
||||||
),
|
cx.theme()
|
||||||
)),
|
.colors()
|
||||||
)
|
.editor_background
|
||||||
}
|
.opacity(0.),
|
||||||
})),
|
1.,
|
||||||
})
|
),
|
||||||
.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))
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue