thread view: Refine the UI a bit (#36504)
Release Notes: - N/A --------- Co-authored-by: Agus Zubiaga <agus@zed.dev>
This commit is contained in:
parent
fbba6addfd
commit
60960409f7
11 changed files with 262 additions and 145 deletions
|
@ -1 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M2.667 8h8M2.667 4h10.666M2.667 12H8"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.333 10H8M13.333 6H2.66701" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 227 B |
3
assets/icons/menu_alt_temp.svg
Normal file
3
assets/icons/menu_alt_temp.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.333 10H8M13.333 6H2.66701" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 227 B |
3
assets/icons/x_circle_filled.svg
Normal file
3
assets/icons/x_circle_filled.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 2C11.3137 2 14 4.68629 14 8C14 11.3137 11.3137 14 8 14C4.68629 14 2 11.3137 2 8C2 4.68629 4.68629 2 8 2ZM10.4238 5.57617C10.1895 5.34187 9.81049 5.3419 9.57617 5.57617L8 7.15234L6.42383 5.57617C6.18953 5.34187 5.81049 5.3419 5.57617 5.57617C5.34186 5.81049 5.34186 6.18951 5.57617 6.42383L7.15234 8L5.57617 9.57617C5.34186 9.81049 5.34186 10.1895 5.57617 10.4238C5.81049 10.6581 6.18954 10.6581 6.42383 10.4238L8 8.84766L9.57617 10.4238C9.81049 10.6581 10.1895 10.6581 10.4238 10.4238C10.6581 10.1895 10.658 9.81048 10.4238 9.57617L8.84766 8L10.4238 6.42383C10.6581 6.18954 10.658 5.81048 10.4238 5.57617Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 737 B |
27
assets/icons/zed_agent.svg
Normal file
27
assets/icons/zed_agent.svg
Normal file
|
@ -0,0 +1,27 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 8.75V10.5C8.93097 10.5 8.06903 10.5 6 10.5V10L11 6V5.5H6V7.25" stroke="black" stroke-width="1.2"/>
|
||||
<path d="M2 8.5C2.27614 8.5 2.5 8.27614 2.5 8C2.5 7.72386 2.27614 7.5 2 7.5C1.72386 7.5 1.5 7.72386 1.5 8C1.5 8.27614 1.72386 8.5 2 8.5Z" fill="black"/>
|
||||
<path d="M2.99976 6.33002C3.2759 6.33002 3.49976 6.10616 3.49976 5.83002C3.49976 5.55387 3.2759 5.33002 2.99976 5.33002C2.72361 5.33002 2.49976 5.55387 2.49976 5.83002C2.49976 6.10616 2.72361 6.33002 2.99976 6.33002Z" fill="black"/>
|
||||
<path d="M2.99976 10.66C3.2759 10.66 3.49976 10.4361 3.49976 10.16C3.49976 9.88383 3.2759 9.65997 2.99976 9.65997C2.72361 9.65997 2.49976 9.88383 2.49976 10.16C2.49976 10.4361 2.72361 10.66 2.99976 10.66Z" fill="black"/>
|
||||
<path d="M15 8.5C15.2761 8.5 15.5 8.27614 15.5 8C15.5 7.72386 15.2761 7.5 15 7.5C14.7239 7.5 14.5 7.72386 14.5 8C14.5 8.27614 14.7239 8.5 15 8.5Z" fill="black"/>
|
||||
<path d="M14 6.33002C14.2761 6.33002 14.5 6.10616 14.5 5.83002C14.5 5.55387 14.2761 5.33002 14 5.33002C13.7239 5.33002 13.5 5.55387 13.5 5.83002C13.5 6.10616 13.7239 6.33002 14 6.33002Z" fill="black"/>
|
||||
<path d="M14 10.66C14.2761 10.66 14.5 10.4361 14.5 10.16C14.5 9.88383 14.2761 9.65997 14 9.65997C13.7239 9.65997 13.5 9.88383 13.5 10.16C13.5 10.4361 13.7239 10.66 14 10.66Z" fill="black"/>
|
||||
<path d="M8.49219 2C8.76833 2 8.99219 1.77614 8.99219 1.5C8.99219 1.22386 8.76833 1 8.49219 1C8.21605 1 7.99219 1.22386 7.99219 1.5C7.99219 1.77614 8.21605 2 8.49219 2Z" fill="black"/>
|
||||
<path d="M6 3C6.27614 3 6.5 2.77614 6.5 2.5C6.5 2.22386 6.27614 2 6 2C5.72386 2 5.5 2.22386 5.5 2.5C5.5 2.77614 5.72386 3 6 3Z" fill="black"/>
|
||||
<path d="M4 4C4.27614 4 4.5 3.77614 4.5 3.5C4.5 3.22386 4.27614 3 4 3C3.72386 3 3.5 3.22386 3.5 3.5C3.5 3.77614 3.72386 4 4 4Z" fill="black"/>
|
||||
<path d="M3.99976 13C4.2759 13 4.49976 12.7761 4.49976 12.5C4.49976 12.2239 4.2759 12 3.99976 12C3.72361 12 3.49976 12.2239 3.49976 12.5C3.49976 12.7761 3.72361 13 3.99976 13Z" fill="black"/>
|
||||
<path d="M2 12.5C2.27614 12.5 2.5 12.2761 2.5 12C2.5 11.7239 2.27614 11.5 2 11.5C1.72386 11.5 1.5 11.7239 1.5 12C1.5 12.2761 1.72386 12.5 2 12.5Z" fill="black"/>
|
||||
<path d="M2 4.5C2.27614 4.5 2.5 4.27614 2.5 4C2.5 3.72386 2.27614 3.5 2 3.5C1.72386 3.5 1.5 3.72386 1.5 4C1.5 4.27614 1.72386 4.5 2 4.5Z" fill="black"/>
|
||||
<path d="M15 12.5C15.2761 12.5 15.5 12.2761 15.5 12C15.5 11.7239 15.2761 11.5 15 11.5C14.7239 11.5 14.5 11.7239 14.5 12C14.5 12.2761 14.7239 12.5 15 12.5Z" fill="black"/>
|
||||
<path d="M15 4.5C15.2761 4.5 15.5 4.27614 15.5 4C15.5 3.72386 15.2761 3.5 15 3.5C14.7239 3.5 14.5 3.72386 14.5 4C14.5 4.27614 14.7239 4.5 15 4.5Z" fill="black"/>
|
||||
<path d="M3.99976 15C4.2759 15 4.49976 14.7761 4.49976 14.5C4.49976 14.2239 4.2759 14 3.99976 14C3.72361 14 3.49976 14.2239 3.49976 14.5C3.49976 14.7761 3.72361 15 3.99976 15Z" fill="black"/>
|
||||
<path d="M4 2C4.27614 2 4.5 1.77614 4.5 1.5C4.5 1.22386 4.27614 1 4 1C3.72386 1 3.5 1.22386 3.5 1.5C3.5 1.77614 3.72386 2 4 2Z" fill="black"/>
|
||||
<path d="M13 15C13.2761 15 13.5 14.7761 13.5 14.5C13.5 14.2239 13.2761 14 13 14C12.7239 14 12.5 14.2239 12.5 14.5C12.5 14.7761 12.7239 15 13 15Z" fill="black"/>
|
||||
<path d="M13 2C13.2761 2 13.5 1.77614 13.5 1.5C13.5 1.22386 13.2761 1 13 1C12.7239 1 12.5 1.22386 12.5 1.5C12.5 1.77614 12.7239 2 13 2Z" fill="black"/>
|
||||
<path d="M13 4C13.2761 4 13.5 3.77614 13.5 3.5C13.5 3.22386 13.2761 3 13 3C12.7239 3 12.5 3.22386 12.5 3.5C12.5 3.77614 12.7239 4 13 4Z" fill="black"/>
|
||||
<path d="M13 13C13.2761 13 13.5 12.7761 13.5 12.5C13.5 12.2239 13.2761 12 13 12C12.7239 12 12.5 12.2239 12.5 12.5C12.5 12.7761 12.7239 13 13 13Z" fill="black"/>
|
||||
<path d="M11 3C11.2761 3 11.5 2.77614 11.5 2.5C11.5 2.22386 11.2761 2 11 2C10.7239 2 10.5 2.22386 10.5 2.5C10.5 2.77614 10.7239 3 11 3Z" fill="black"/>
|
||||
<path d="M8.5 15C8.77614 15 9 14.7761 9 14.5C9 14.2239 8.77614 14 8.5 14C8.22386 14 8 14.2239 8 14.5C8 14.7761 8.22386 15 8.5 15Z" fill="black"/>
|
||||
<path d="M6 14C6.27614 14 6.5 13.7761 6.5 13.5C6.5 13.2239 6.27614 13 6 13C5.72386 13 5.5 13.2239 5.5 13.5C5.5 13.7761 5.72386 14 6 14Z" fill="black"/>
|
||||
<path d="M11 14C11.2761 14 11.5 13.7761 11.5 13.5C11.5 13.2239 11.2761 13 11 13C10.7239 13 10.5 13.2239 10.5 13.5C10.5 13.7761 10.7239 14 11 14Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
|
@ -27,16 +27,15 @@ impl AgentServer for NativeAgentServer {
|
|||
}
|
||||
|
||||
fn empty_state_headline(&self) -> &'static str {
|
||||
"Native Agent"
|
||||
""
|
||||
}
|
||||
|
||||
fn empty_state_message(&self) -> &'static str {
|
||||
"How can I help you today?"
|
||||
""
|
||||
}
|
||||
|
||||
fn logo(&self) -> ui::IconName {
|
||||
// Using the ZedAssistant icon as it's the native built-in agent
|
||||
ui::IconName::ZedAssistant
|
||||
ui::IconName::ZedAgent
|
||||
}
|
||||
|
||||
fn connect(
|
||||
|
|
|
@ -26,7 +26,7 @@ impl AgentServer for Gemini {
|
|||
}
|
||||
|
||||
fn empty_state_message(&self) -> &'static str {
|
||||
"Ask questions, edit files, run commands.\nBe specific for the best results."
|
||||
"Ask questions, edit files, run commands"
|
||||
}
|
||||
|
||||
fn logo(&self) -> ui::IconName {
|
||||
|
|
|
@ -189,6 +189,7 @@ pub enum ViewEvent {
|
|||
MessageEditorEvent(Entity<MessageEditor>, MessageEditorEvent),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Entry {
|
||||
UserMessage(Entity<MessageEditor>),
|
||||
Content(HashMap<EntityId, AnyEntity>),
|
||||
|
|
|
@ -21,11 +21,11 @@ use file_icons::FileIcons;
|
|||
use fs::Fs;
|
||||
use gpui::{
|
||||
Action, Animation, AnimationExt, AnyView, App, BorderStyle, ClickEvent, ClipboardItem,
|
||||
EdgesRefinement, Empty, Entity, FocusHandle, Focusable, Hsla, Length, ListOffset, ListState,
|
||||
MouseButton, PlatformDisplay, SharedString, Stateful, StyleRefinement, Subscription, Task,
|
||||
TextStyle, TextStyleRefinement, Transformation, UnderlineStyle, WeakEntity, Window,
|
||||
WindowHandle, div, linear_color_stop, linear_gradient, list, percentage, point, prelude::*,
|
||||
pulsating_between,
|
||||
EdgesRefinement, ElementId, Empty, Entity, FocusHandle, Focusable, Hsla, Length, ListOffset,
|
||||
ListState, MouseButton, PlatformDisplay, SharedString, Stateful, StyleRefinement, Subscription,
|
||||
Task, TextStyle, TextStyleRefinement, Transformation, UnderlineStyle, WeakEntity, Window,
|
||||
WindowHandle, div, ease_in_out, linear_color_stop, linear_gradient, list, percentage, point,
|
||||
prelude::*, pulsating_between,
|
||||
};
|
||||
use language::Buffer;
|
||||
|
||||
|
@ -170,7 +170,7 @@ impl AcpThreadView {
|
|||
project.clone(),
|
||||
thread_store.clone(),
|
||||
text_thread_store.clone(),
|
||||
"Message the agent - @ to include context",
|
||||
"Message the agent — @ to include context",
|
||||
prevent_slash_commands,
|
||||
editor::EditorMode::AutoHeight {
|
||||
min_lines: MIN_EDITOR_LINES,
|
||||
|
@ -928,29 +928,41 @@ impl AcpThreadView {
|
|||
None
|
||||
};
|
||||
|
||||
div()
|
||||
v_flex()
|
||||
.id(("user_message", entry_ix))
|
||||
.py_4()
|
||||
.pt_2()
|
||||
.pb_4()
|
||||
.px_2()
|
||||
.gap_1p5()
|
||||
.w_full()
|
||||
.children(rules_item)
|
||||
.children(message.id.clone().and_then(|message_id| {
|
||||
message.checkpoint.as_ref()?.show.then(|| {
|
||||
Button::new("restore-checkpoint", "Restore Checkpoint")
|
||||
.icon(IconName::Undo)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_position(IconPosition::Start)
|
||||
.label_size(LabelSize::XSmall)
|
||||
.on_click(cx.listener(move |this, _, _window, cx| {
|
||||
this.rewind(&message_id, cx);
|
||||
}))
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(Divider::horizontal())
|
||||
.child(
|
||||
Button::new("restore-checkpoint", "Restore Checkpoint")
|
||||
.icon(IconName::Undo)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_position(IconPosition::Start)
|
||||
.label_size(LabelSize::XSmall)
|
||||
.icon_color(Color::Muted)
|
||||
.color(Color::Muted)
|
||||
.on_click(cx.listener(move |this, _, _window, cx| {
|
||||
this.rewind(&message_id, cx);
|
||||
}))
|
||||
)
|
||||
.child(Divider::horizontal())
|
||||
})
|
||||
}))
|
||||
.children(rules_item)
|
||||
.child(
|
||||
div()
|
||||
.relative()
|
||||
.child(
|
||||
div()
|
||||
.p_3()
|
||||
.py_3()
|
||||
.px_2()
|
||||
.rounded_lg()
|
||||
.shadow_md()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
|
@ -1080,12 +1092,20 @@ impl AcpThreadView {
|
|||
if let Some(editing_index) = self.editing_message.as_ref()
|
||||
&& *editing_index < entry_ix
|
||||
{
|
||||
div()
|
||||
.child(primary)
|
||||
.opacity(0.2)
|
||||
let backdrop = div()
|
||||
.id(("backdrop", entry_ix))
|
||||
.size_full()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.bg(cx.theme().colors().panel_background)
|
||||
.opacity(0.8)
|
||||
.block_mouse_except_scroll()
|
||||
.id("overlay")
|
||||
.on_click(cx.listener(Self::cancel_editing))
|
||||
.on_click(cx.listener(Self::cancel_editing));
|
||||
|
||||
div()
|
||||
.relative()
|
||||
.child(primary)
|
||||
.child(backdrop)
|
||||
.into_any_element()
|
||||
} else {
|
||||
primary
|
||||
|
@ -1100,7 +1120,7 @@ impl AcpThreadView {
|
|||
}
|
||||
|
||||
fn tool_card_border_color(&self, cx: &Context<Self>) -> Hsla {
|
||||
cx.theme().colors().border.opacity(0.6)
|
||||
cx.theme().colors().border.opacity(0.8)
|
||||
}
|
||||
|
||||
fn tool_name_font_size(&self) -> Rems {
|
||||
|
@ -1299,23 +1319,14 @@ impl AcpThreadView {
|
|||
tool_call.status,
|
||||
ToolCallStatus::WaitingForConfirmation { .. }
|
||||
);
|
||||
let is_edit = matches!(tool_call.kind, acp::ToolKind::Edit);
|
||||
let has_diff = tool_call
|
||||
.content
|
||||
.iter()
|
||||
.any(|content| matches!(content, ToolCallContent::Diff { .. }));
|
||||
let has_nonempty_diff = tool_call.content.iter().any(|content| match content {
|
||||
ToolCallContent::Diff(diff) => diff.read(cx).has_revealed_range(cx),
|
||||
_ => false,
|
||||
});
|
||||
let use_card_layout = needs_confirmation || is_edit || has_diff;
|
||||
let is_edit =
|
||||
matches!(tool_call.kind, acp::ToolKind::Edit) || tool_call.diffs().next().is_some();
|
||||
let use_card_layout = needs_confirmation || is_edit;
|
||||
|
||||
let is_collapsible = !tool_call.content.is_empty() && !use_card_layout;
|
||||
|
||||
let is_open = tool_call.content.is_empty()
|
||||
|| needs_confirmation
|
||||
|| has_nonempty_diff
|
||||
|| self.expanded_tool_calls.contains(&tool_call.id);
|
||||
let is_open =
|
||||
needs_confirmation || is_edit || self.expanded_tool_calls.contains(&tool_call.id);
|
||||
|
||||
let gradient_overlay = |color: Hsla| {
|
||||
div()
|
||||
|
@ -1336,41 +1347,49 @@ impl AcpThreadView {
|
|||
cx.theme().colors().panel_background
|
||||
};
|
||||
|
||||
let tool_output_display = match &tool_call.status {
|
||||
ToolCallStatus::WaitingForConfirmation { options, .. } => v_flex()
|
||||
.w_full()
|
||||
.children(tool_call.content.iter().map(|content| {
|
||||
div()
|
||||
.child(
|
||||
self.render_tool_call_content(entry_ix, content, tool_call, window, cx),
|
||||
)
|
||||
.into_any_element()
|
||||
}))
|
||||
.child(self.render_permission_buttons(
|
||||
options,
|
||||
entry_ix,
|
||||
tool_call.id.clone(),
|
||||
tool_call.content.is_empty(),
|
||||
cx,
|
||||
)),
|
||||
ToolCallStatus::Pending
|
||||
| ToolCallStatus::InProgress
|
||||
| ToolCallStatus::Completed
|
||||
| ToolCallStatus::Failed
|
||||
| ToolCallStatus::Canceled => {
|
||||
v_flex()
|
||||
let tool_output_display = if is_open {
|
||||
match &tool_call.status {
|
||||
ToolCallStatus::WaitingForConfirmation { options, .. } => {
|
||||
v_flex()
|
||||
.w_full()
|
||||
.children(tool_call.content.iter().map(|content| {
|
||||
div()
|
||||
.child(self.render_tool_call_content(
|
||||
entry_ix, content, tool_call, window, cx,
|
||||
))
|
||||
.into_any_element()
|
||||
}))
|
||||
.child(self.render_permission_buttons(
|
||||
options,
|
||||
entry_ix,
|
||||
tool_call.id.clone(),
|
||||
tool_call.content.is_empty(),
|
||||
cx,
|
||||
))
|
||||
.into_any()
|
||||
}
|
||||
ToolCallStatus::Pending | ToolCallStatus::InProgress
|
||||
if is_edit && tool_call.content.is_empty() =>
|
||||
{
|
||||
self.render_diff_loading(cx).into_any()
|
||||
}
|
||||
ToolCallStatus::Pending
|
||||
| ToolCallStatus::InProgress
|
||||
| ToolCallStatus::Completed
|
||||
| ToolCallStatus::Failed
|
||||
| ToolCallStatus::Canceled => v_flex()
|
||||
.w_full()
|
||||
.children(tool_call.content.iter().map(|content| {
|
||||
div()
|
||||
.child(
|
||||
self.render_tool_call_content(
|
||||
entry_ix, content, tool_call, window, cx,
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
div().child(
|
||||
self.render_tool_call_content(entry_ix, content, tool_call, window, cx),
|
||||
)
|
||||
}))
|
||||
.into_any(),
|
||||
ToolCallStatus::Rejected => Empty.into_any(),
|
||||
}
|
||||
ToolCallStatus::Rejected => v_flex().size_0(),
|
||||
.into()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
v_flex()
|
||||
|
@ -1390,9 +1409,13 @@ impl AcpThreadView {
|
|||
.map(|this| {
|
||||
if use_card_layout {
|
||||
this.pl_2()
|
||||
.pr_1()
|
||||
.pr_1p5()
|
||||
.py_1()
|
||||
.rounded_t_md()
|
||||
.when(is_open, |this| {
|
||||
this.border_b_1()
|
||||
.border_color(self.tool_card_border_color(cx))
|
||||
})
|
||||
.bg(self.tool_card_header_bg(cx))
|
||||
} else {
|
||||
this.opacity(0.8).hover(|style| style.opacity(1.))
|
||||
|
@ -1403,6 +1426,7 @@ impl AcpThreadView {
|
|||
.group(&card_header_id)
|
||||
.relative()
|
||||
.w_full()
|
||||
.min_h_6()
|
||||
.text_size(self.tool_name_font_size())
|
||||
.child(self.render_tool_call_icon(
|
||||
card_header_id,
|
||||
|
@ -1456,11 +1480,7 @@ impl AcpThreadView {
|
|||
.overflow_x_scroll()
|
||||
.child(self.render_markdown(
|
||||
tool_call.label.clone(),
|
||||
default_markdown_style(
|
||||
needs_confirmation || is_edit || has_diff,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
default_markdown_style(false, window, cx),
|
||||
)),
|
||||
)
|
||||
.child(gradient_overlay(gradient_color))
|
||||
|
@ -1480,7 +1500,7 @@ impl AcpThreadView {
|
|||
)
|
||||
.children(status_icon),
|
||||
)
|
||||
.when(is_open, |this| this.child(tool_output_display))
|
||||
.children(tool_output_display)
|
||||
}
|
||||
|
||||
fn render_tool_call_content(
|
||||
|
@ -1501,7 +1521,7 @@ impl AcpThreadView {
|
|||
Empty.into_any_element()
|
||||
}
|
||||
}
|
||||
ToolCallContent::Diff(diff) => self.render_diff_editor(entry_ix, diff, cx),
|
||||
ToolCallContent::Diff(diff) => self.render_diff_editor(entry_ix, diff, tool_call, cx),
|
||||
ToolCallContent::Terminal(terminal) => {
|
||||
self.render_terminal_tool_call(entry_ix, terminal, tool_call, window, cx)
|
||||
}
|
||||
|
@ -1645,21 +1665,69 @@ impl AcpThreadView {
|
|||
})))
|
||||
}
|
||||
|
||||
fn render_diff_loading(&self, cx: &Context<Self>) -> AnyElement {
|
||||
let bar = |n: u64, width_class: &str| {
|
||||
let bg_color = cx.theme().colors().element_active;
|
||||
let base = h_flex().h_1().rounded_full();
|
||||
|
||||
let modified = match width_class {
|
||||
"w_4_5" => base.w_3_4(),
|
||||
"w_1_4" => base.w_1_4(),
|
||||
"w_2_4" => base.w_2_4(),
|
||||
"w_3_5" => base.w_3_5(),
|
||||
"w_2_5" => base.w_2_5(),
|
||||
_ => base.w_1_2(),
|
||||
};
|
||||
|
||||
modified.with_animation(
|
||||
ElementId::Integer(n),
|
||||
Animation::new(Duration::from_secs(2)).repeat(),
|
||||
move |tab, delta| {
|
||||
let delta = (delta - 0.15 * n as f32) / 0.7;
|
||||
let delta = 1.0 - (0.5 - delta).abs() * 2.;
|
||||
let delta = ease_in_out(delta.clamp(0., 1.));
|
||||
let delta = 0.1 + 0.9 * delta;
|
||||
|
||||
tab.bg(bg_color.opacity(delta))
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
v_flex()
|
||||
.p_3()
|
||||
.gap_1()
|
||||
.rounded_b_md()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(bar(0, "w_4_5"))
|
||||
.child(bar(1, "w_1_4"))
|
||||
.child(bar(2, "w_2_4"))
|
||||
.child(bar(3, "w_3_5"))
|
||||
.child(bar(4, "w_2_5"))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_diff_editor(
|
||||
&self,
|
||||
entry_ix: usize,
|
||||
diff: &Entity<acp_thread::Diff>,
|
||||
tool_call: &ToolCall,
|
||||
cx: &Context<Self>,
|
||||
) -> AnyElement {
|
||||
let tool_progress = matches!(
|
||||
&tool_call.status,
|
||||
ToolCallStatus::InProgress | ToolCallStatus::Pending
|
||||
);
|
||||
|
||||
v_flex()
|
||||
.h_full()
|
||||
.border_t_1()
|
||||
.border_color(self.tool_card_border_color(cx))
|
||||
.child(
|
||||
if let Some(entry) = self.entry_view_state.read(cx).entry(entry_ix)
|
||||
&& let Some(editor) = entry.editor_for_diff(diff)
|
||||
&& diff.read(cx).has_revealed_range(cx)
|
||||
{
|
||||
editor.clone().into_any_element()
|
||||
} else if tool_progress {
|
||||
self.render_diff_loading(cx)
|
||||
} else {
|
||||
Empty.into_any()
|
||||
},
|
||||
|
@ -1924,11 +1992,11 @@ impl AcpThreadView {
|
|||
.justify_center()
|
||||
.child(div().opacity(0.3).child(logo))
|
||||
.child(
|
||||
h_flex().absolute().right_1().bottom_0().child(
|
||||
Icon::new(IconName::XCircle)
|
||||
.color(Color::Error)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
h_flex()
|
||||
.absolute()
|
||||
.right_1()
|
||||
.bottom_0()
|
||||
.child(Icon::new(IconName::XCircleFilled).color(Color::Error)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
@ -1982,12 +2050,12 @@ impl AcpThreadView {
|
|||
|
||||
Some(
|
||||
v_flex()
|
||||
.pt_2()
|
||||
.px_2p5()
|
||||
.gap_1()
|
||||
.when_some(user_rules_text, |parent, user_rules_text| {
|
||||
parent.child(
|
||||
h_flex()
|
||||
.group("user-rules")
|
||||
.w_full()
|
||||
.child(
|
||||
Icon::new(IconName::Reader)
|
||||
|
@ -2008,6 +2076,7 @@ impl AcpThreadView {
|
|||
.shape(ui::IconButtonShape::Square)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_color(Color::Ignored)
|
||||
.visible_on_hover("user-rules")
|
||||
// TODO: Figure out a way to pass focus handle here so we can display the `OpenRulesLibrary` keybinding
|
||||
.tooltip(Tooltip::text("View User Rules"))
|
||||
.on_click(move |_event, window, cx| {
|
||||
|
@ -2024,6 +2093,7 @@ impl AcpThreadView {
|
|||
.when_some(rules_file_text, |parent, rules_file_text| {
|
||||
parent.child(
|
||||
h_flex()
|
||||
.group("project-rules")
|
||||
.w_full()
|
||||
.child(
|
||||
Icon::new(IconName::File)
|
||||
|
@ -2044,7 +2114,8 @@ impl AcpThreadView {
|
|||
.icon_size(IconSize::XSmall)
|
||||
.icon_color(Color::Ignored)
|
||||
.on_click(cx.listener(Self::handle_open_rules))
|
||||
.tooltip(Tooltip::text("View Rules")),
|
||||
.visible_on_hover("project-rules")
|
||||
.tooltip(Tooltip::text("View Project Rules")),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
@ -2119,11 +2190,9 @@ impl AcpThreadView {
|
|||
.items_center()
|
||||
.justify_center()
|
||||
.child(self.render_error_agent_logo())
|
||||
.child(
|
||||
h_flex().mt_4().mb_1().justify_center().child(
|
||||
Headline::new("Authentication Required").size(HeadlineSize::Medium),
|
||||
),
|
||||
)
|
||||
.child(h_flex().mt_4().mb_1().justify_center().child(
|
||||
Headline::new(self.agent.empty_state_headline()).size(HeadlineSize::Medium),
|
||||
))
|
||||
.into_any(),
|
||||
)
|
||||
.children(description.map(|desc| {
|
||||
|
@ -2838,10 +2907,10 @@ impl AcpThreadView {
|
|||
.child(
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.flex_wrap()
|
||||
.justify_between()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(self.render_follow_toggle(cx))
|
||||
.children(self.render_burn_mode_toggle(cx)),
|
||||
)
|
||||
|
@ -2883,7 +2952,7 @@ impl AcpThreadView {
|
|||
h_flex()
|
||||
.flex_shrink_0()
|
||||
.gap_0p5()
|
||||
.mr_1()
|
||||
.mr_1p5()
|
||||
.child(
|
||||
Label::new(used)
|
||||
.size(LabelSize::Small)
|
||||
|
@ -2904,7 +2973,11 @@ impl AcpThreadView {
|
|||
}
|
||||
}),
|
||||
)
|
||||
.child(Label::new("/").size(LabelSize::Small).color(Color::Muted))
|
||||
.child(
|
||||
Label::new("/")
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Custom(cx.theme().colors().text_muted.opacity(0.5))),
|
||||
)
|
||||
.child(Label::new(max).size(LabelSize::Small).color(Color::Muted)),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -65,8 +65,8 @@ use theme::ThemeSettings;
|
|||
use time::UtcOffset;
|
||||
use ui::utils::WithRemSize;
|
||||
use ui::{
|
||||
Banner, Callout, ContextMenu, ContextMenuEntry, Divider, ElevationIndex, KeyBinding,
|
||||
PopoverMenu, PopoverMenuHandle, ProgressBar, Tab, Tooltip, prelude::*,
|
||||
Banner, Callout, ContextMenu, ContextMenuEntry, ElevationIndex, KeyBinding, PopoverMenu,
|
||||
PopoverMenuHandle, ProgressBar, Tab, Tooltip, prelude::*,
|
||||
};
|
||||
use util::ResultExt as _;
|
||||
use workspace::{
|
||||
|
@ -245,17 +245,16 @@ impl AgentType {
|
|||
match self {
|
||||
Self::Zed | Self::TextThread => "Zed Agent",
|
||||
Self::NativeAgent => "Agent 2",
|
||||
Self::Gemini => "Google Gemini",
|
||||
Self::Gemini => "Gemini CLI",
|
||||
Self::ClaudeCode => "Claude Code",
|
||||
}
|
||||
}
|
||||
|
||||
fn icon(self) -> IconName {
|
||||
fn icon(self) -> Option<IconName> {
|
||||
match self {
|
||||
Self::Zed | Self::TextThread => IconName::AiZed,
|
||||
Self::NativeAgent => IconName::ZedAssistant,
|
||||
Self::Gemini => IconName::AiGemini,
|
||||
Self::ClaudeCode => IconName::AiClaude,
|
||||
Self::Zed | Self::NativeAgent | Self::TextThread => None,
|
||||
Self::Gemini => Some(IconName::AiGemini),
|
||||
Self::ClaudeCode => Some(IconName::AiClaude),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2158,12 +2157,17 @@ impl AgentPanel {
|
|||
})
|
||||
}
|
||||
|
||||
fn render_recent_entries_menu(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render_recent_entries_menu(
|
||||
&self,
|
||||
icon: IconName,
|
||||
corner: Corner,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let focus_handle = self.focus_handle(cx);
|
||||
|
||||
PopoverMenu::new("agent-nav-menu")
|
||||
.trigger_with_tooltip(
|
||||
IconButton::new("agent-nav-menu", IconName::MenuAlt).icon_size(IconSize::Small),
|
||||
IconButton::new("agent-nav-menu", icon).icon_size(IconSize::Small),
|
||||
{
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
|
@ -2177,7 +2181,7 @@ impl AgentPanel {
|
|||
}
|
||||
},
|
||||
)
|
||||
.anchor(Corner::TopLeft)
|
||||
.anchor(corner)
|
||||
.with_handle(self.assistant_navigation_menu_handle.clone())
|
||||
.menu({
|
||||
let menu = self.assistant_navigation_menu.clone();
|
||||
|
@ -2304,7 +2308,9 @@ impl AgentPanel {
|
|||
.pl(DynamicSpacing::Base04.rems(cx))
|
||||
.child(self.render_toolbar_back_button(cx))
|
||||
.into_any_element(),
|
||||
_ => self.render_recent_entries_menu(cx).into_any_element(),
|
||||
_ => self
|
||||
.render_recent_entries_menu(IconName::MenuAlt, Corner::TopLeft, cx)
|
||||
.into_any_element(),
|
||||
})
|
||||
.child(self.render_title_view(window, cx)),
|
||||
)
|
||||
|
@ -2390,7 +2396,7 @@ impl AgentPanel {
|
|||
.item(
|
||||
ContextMenuEntry::new("New Thread")
|
||||
.action(NewThread::default().boxed_clone())
|
||||
.icon(IconName::ZedAssistant)
|
||||
.icon(IconName::Thread)
|
||||
.icon_color(Color::Muted)
|
||||
.handler({
|
||||
let workspace = workspace.clone();
|
||||
|
@ -2443,7 +2449,7 @@ impl AgentPanel {
|
|||
.header("External Agents")
|
||||
.when(cx.has_flag::<GeminiAndNativeFeatureFlag>(), |menu| {
|
||||
menu.item(
|
||||
ContextMenuEntry::new("New Gemini Thread")
|
||||
ContextMenuEntry::new("New Gemini CLI Thread")
|
||||
.icon(IconName::AiGemini)
|
||||
.icon_color(Color::Muted)
|
||||
.handler({
|
||||
|
@ -2503,16 +2509,18 @@ impl AgentPanel {
|
|||
let selected_agent_label = self.selected_agent.label().into();
|
||||
let selected_agent = div()
|
||||
.id("selected_agent_icon")
|
||||
.px(DynamicSpacing::Base02.rems(cx))
|
||||
.child(Icon::new(self.selected_agent.icon()).color(Color::Muted))
|
||||
.tooltip(move |window, cx| {
|
||||
Tooltip::with_meta(
|
||||
selected_agent_label.clone(),
|
||||
None,
|
||||
"Selected Agent",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.when_some(self.selected_agent.icon(), |this, icon| {
|
||||
this.px(DynamicSpacing::Base02.rems(cx))
|
||||
.child(Icon::new(icon).color(Color::Muted))
|
||||
.tooltip(move |window, cx| {
|
||||
Tooltip::with_meta(
|
||||
selected_agent_label.clone(),
|
||||
None,
|
||||
"Selected Agent",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})
|
||||
.into_any_element();
|
||||
|
||||
|
@ -2535,31 +2543,23 @@ impl AgentPanel {
|
|||
ActiveView::History | ActiveView::Configuration => {
|
||||
self.render_toolbar_back_button(cx).into_any_element()
|
||||
}
|
||||
_ => h_flex()
|
||||
.gap_1()
|
||||
.child(self.render_recent_entries_menu(cx))
|
||||
.child(Divider::vertical())
|
||||
.child(selected_agent)
|
||||
.into_any_element(),
|
||||
_ => selected_agent.into_any_element(),
|
||||
})
|
||||
.child(self.render_title_view(window, cx)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.h_full()
|
||||
.gap_2()
|
||||
.children(self.render_token_count(cx))
|
||||
.child(
|
||||
h_flex()
|
||||
.h_full()
|
||||
.gap(DynamicSpacing::Base02.rems(cx))
|
||||
.pl(DynamicSpacing::Base04.rems(cx))
|
||||
.pr(DynamicSpacing::Base06.rems(cx))
|
||||
.border_l_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(new_thread_menu)
|
||||
.child(self.render_panel_options_menu(window, cx)),
|
||||
),
|
||||
.flex_none()
|
||||
.gap(DynamicSpacing::Base02.rems(cx))
|
||||
.pl(DynamicSpacing::Base04.rems(cx))
|
||||
.pr(DynamicSpacing::Base06.rems(cx))
|
||||
.child(new_thread_menu)
|
||||
.child(self.render_recent_entries_menu(
|
||||
IconName::MenuAltTemp,
|
||||
Corner::TopRight,
|
||||
cx,
|
||||
))
|
||||
.child(self.render_panel_options_menu(window, cx)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ pub enum IconName {
|
|||
Maximize,
|
||||
Menu,
|
||||
MenuAlt,
|
||||
MenuAltTemp,
|
||||
Mic,
|
||||
MicMute,
|
||||
Minimize,
|
||||
|
@ -245,6 +246,8 @@ pub enum IconName {
|
|||
Warning,
|
||||
WholeWord,
|
||||
XCircle,
|
||||
XCircleFilled,
|
||||
ZedAgent,
|
||||
ZedAssistant,
|
||||
ZedBurnMode,
|
||||
ZedBurnModeOn,
|
||||
|
|
|
@ -1084,7 +1084,13 @@ impl Element for MarkdownElement {
|
|||
cx,
|
||||
);
|
||||
el.child(
|
||||
div().absolute().top_1().right_0p5().w_5().child(codeblock),
|
||||
h_flex()
|
||||
.w_5()
|
||||
.absolute()
|
||||
.top_1()
|
||||
.right_1()
|
||||
.justify_center()
|
||||
.child(codeblock),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue