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
|
@ -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