Detailed changes
@@ -466,6 +466,7 @@ impl AssistantPanel {
thread_store.downgrade(),
context_store.downgrade(),
thread.clone(),
+ agent_panel_dock_position(cx),
window,
cx,
)
@@ -795,6 +796,7 @@ impl AssistantPanel {
self.thread_store.downgrade(),
self.context_store.downgrade(),
thread,
+ agent_panel_dock_position(cx),
window,
cx,
)
@@ -1003,6 +1005,7 @@ impl AssistantPanel {
self.thread_store.downgrade(),
self.context_store.downgrade(),
thread,
+ agent_panel_dock_position(cx),
window,
cx,
)
@@ -1330,6 +1333,14 @@ impl Focusable for AssistantPanel {
}
}
+fn agent_panel_dock_position(cx: &App) -> DockPosition {
+ match AssistantSettings::get_global(cx).dock {
+ AssistantDockPosition::Left => DockPosition::Left,
+ AssistantDockPosition::Bottom => DockPosition::Bottom,
+ AssistantDockPosition::Right => DockPosition::Right,
+ }
+}
+
impl EventEmitter<PanelEvent> for AssistantPanel {}
impl Panel for AssistantPanel {
@@ -1338,18 +1349,18 @@ impl Panel for AssistantPanel {
}
fn position(&self, _window: &Window, cx: &App) -> DockPosition {
- match AssistantSettings::get_global(cx).dock {
- AssistantDockPosition::Left => DockPosition::Left,
- AssistantDockPosition::Bottom => DockPosition::Bottom,
- AssistantDockPosition::Right => DockPosition::Right,
- }
+ agent_panel_dock_position(cx)
}
- fn position_is_valid(&self, _: DockPosition) -> bool {
- true
+ fn position_is_valid(&self, position: DockPosition) -> bool {
+ position != DockPosition::Bottom
}
fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
+ self.message_editor.update(cx, |message_editor, cx| {
+ message_editor.set_dock_position(position, cx);
+ });
+
settings::update_settings_file::<AssistantSettings>(
self.fs.clone(),
cx,
@@ -35,8 +35,9 @@ use proto::Plan;
use settings::Settings;
use std::time::Duration;
use theme::ThemeSettings;
-use ui::{Disclosure, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*};
+use ui::{Disclosure, DocumentationSide, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*};
use util::{ResultExt as _, maybe};
+use workspace::dock::DockPosition;
use workspace::{CollaboratorId, Workspace};
use zed_llm_client::CompletionMode;
@@ -130,6 +131,14 @@ pub(crate) fn create_editor(
editor
}
+fn documentation_side(position: DockPosition) -> DocumentationSide {
+ match position {
+ DockPosition::Left => DocumentationSide::Right,
+ DockPosition::Bottom => DocumentationSide::Left,
+ DockPosition::Right => DocumentationSide::Left,
+ }
+}
+
impl MessageEditor {
pub fn new(
fs: Arc<dyn Fs>,
@@ -140,6 +149,7 @@ impl MessageEditor {
thread_store: WeakEntity<ThreadStore>,
text_thread_store: WeakEntity<TextThreadStore>,
thread: Entity<Thread>,
+ dock_position: DockPosition,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -213,8 +223,15 @@ impl MessageEditor {
model_selector,
edits_expanded: false,
editor_is_expanded: false,
- profile_selector: cx
- .new(|cx| ProfileSelector::new(fs, thread_store, editor.focus_handle(cx), cx)),
+ profile_selector: cx.new(|cx| {
+ ProfileSelector::new(
+ fs,
+ thread_store,
+ editor.focus_handle(cx),
+ documentation_side(dock_position),
+ cx,
+ )
+ }),
last_estimated_token_count: None,
update_token_count_task: None,
_subscriptions: subscriptions,
@@ -1253,6 +1270,12 @@ impl MessageEditor {
.ok();
}));
}
+
+ pub fn set_dock_position(&mut self, position: DockPosition, cx: &mut Context<Self>) {
+ self.profile_selector.update(cx, |profile_selector, cx| {
+ profile_selector.set_documentation_side(documentation_side(position), cx)
+ });
+ }
}
pub fn extract_message_creases(
@@ -1426,6 +1449,7 @@ impl AgentPreview for MessageEditor {
thread_store.downgrade(),
text_thread_store.downgrade(),
thread,
+ DockPosition::Left,
window,
cx,
)
@@ -7,7 +7,10 @@ use fs::Fs;
use gpui::{Action, Entity, FocusHandle, Subscription, WeakEntity, prelude::*};
use language_model::LanguageModelRegistry;
use settings::{Settings as _, SettingsStore, update_settings_file};
-use ui::{ContextMenu, ContextMenuEntry, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
+use ui::{
+ ContextMenu, ContextMenuEntry, DocumentationSide, PopoverMenu, PopoverMenuHandle, Tooltip,
+ prelude::*,
+};
use util::ResultExt as _;
use crate::{ManageProfiles, ThreadStore, ToggleProfileSelector};
@@ -19,6 +22,7 @@ pub struct ProfileSelector {
menu_handle: PopoverMenuHandle<ContextMenu>,
focus_handle: FocusHandle,
_subscriptions: Vec<Subscription>,
+ documentation_side: DocumentationSide,
}
impl ProfileSelector {
@@ -26,6 +30,7 @@ impl ProfileSelector {
fs: Arc<dyn Fs>,
thread_store: WeakEntity<ThreadStore>,
focus_handle: FocusHandle,
+ documentation_side: DocumentationSide,
cx: &mut Context<Self>,
) -> Self {
let settings_subscription = cx.observe_global::<SettingsStore>(move |this, cx| {
@@ -39,9 +44,15 @@ impl ProfileSelector {
menu_handle: PopoverMenuHandle::default(),
focus_handle,
_subscriptions: vec![settings_subscription],
+ documentation_side,
}
}
+ pub fn set_documentation_side(&mut self, side: DocumentationSide, cx: &mut Context<Self>) {
+ self.documentation_side = side;
+ cx.notify();
+ }
+
pub fn menu_handle(&self) -> PopoverMenuHandle<ContextMenu> {
self.menu_handle.clone()
}
@@ -101,7 +112,9 @@ impl ProfileSelector {
.toggleable(IconPosition::End, profile_id == settings.default_profile);
let entry = if let Some(doc_text) = documentation {
- entry.documentation_aside(move |_| Label::new(doc_text).into_any_element())
+ entry.documentation_aside(self.documentation_side, move |_| {
+ Label::new(doc_text).into_any_element()
+ })
} else {
entry
};
@@ -175,7 +188,11 @@ impl Render for ProfileSelector {
)
}
})
- .anchor(gpui::Corner::BottomRight)
+ .anchor(if self.documentation_side == DocumentationSide::Left {
+ gpui::Corner::BottomRight
+ } else {
+ gpui::Corner::BottomLeft
+ })
.with_handle(self.menu_handle.clone())
.menu(move |window, cx| {
Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
@@ -27,8 +27,8 @@ use std::{
};
use supermaven::{AccountStatus, Supermaven};
use ui::{
- Clickable, ContextMenu, ContextMenuEntry, IconButton, IconButtonShape, Indicator, PopoverMenu,
- PopoverMenuHandle, ProgressBar, Tooltip, prelude::*,
+ Clickable, ContextMenu, ContextMenuEntry, DocumentationSide, IconButton, IconButtonShape,
+ Indicator, PopoverMenu, PopoverMenuHandle, ProgressBar, Tooltip, prelude::*,
};
use util::maybe;
use workspace::{
@@ -485,7 +485,7 @@ impl InlineCompletionButton {
menu = menu.item(
entry
.disabled(true)
- .documentation_aside(move |_cx| {
+ .documentation_aside(DocumentationSide::Left, move |_cx| {
Label::new(format!("Edit predictions cannot be toggled for this buffer because they are disabled for {}", language.name()))
.into_any_element()
})
@@ -529,7 +529,7 @@ impl InlineCompletionButton {
.item(
ContextMenuEntry::new("Eager")
.toggleable(IconPosition::Start, eager_mode)
- .documentation_aside(move |_| {
+ .documentation_aside(DocumentationSide::Left, move |_| {
Label::new("Display predictions inline when there are no language server completions available.").into_any_element()
})
.handler({
@@ -542,7 +542,7 @@ impl InlineCompletionButton {
.item(
ContextMenuEntry::new("Subtle")
.toggleable(IconPosition::Start, subtle_mode)
- .documentation_aside(move |_| {
+ .documentation_aside(DocumentationSide::Left, move |_| {
Label::new("Display predictions inline only when holding a modifier key (alt by default).").into_any_element()
})
.handler({
@@ -573,7 +573,7 @@ impl InlineCompletionButton {
.toggleable(IconPosition::Start, data_collection.is_enabled())
.icon(icon_name)
.icon_color(icon_color)
- .documentation_aside(move |cx| {
+ .documentation_aside(DocumentationSide::Left, move |cx| {
let (msg, label_color, icon_name, icon_color) = match (is_open_source, is_collecting) {
(true, true) => (
"Project identified as open source, and you're sharing data.",
@@ -654,7 +654,7 @@ impl InlineCompletionButton {
ContextMenuEntry::new("Configure Excluded Files")
.icon(IconName::LockOutlined)
.icon_color(Color::Muted)
- .documentation_aside(|_| {
+ .documentation_aside(DocumentationSide::Left, |_| {
Label::new(indoc!{"
Open your settings to add sensitive paths for which Zed will never predict edits."}).into_any_element()
})
@@ -50,7 +50,7 @@ pub struct ContextMenuEntry {
handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>,
action: Option<Box<dyn Action>>,
disabled: bool,
- documentation_aside: Option<Rc<dyn Fn(&mut App) -> AnyElement>>,
+ documentation_aside: Option<DocumentationAside>,
end_slot_icon: Option<IconName>,
end_slot_title: Option<SharedString>,
end_slot_handler: Option<Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>>,
@@ -124,9 +124,14 @@ impl ContextMenuEntry {
pub fn documentation_aside(
mut self,
- element: impl Fn(&mut App) -> AnyElement + 'static,
+ side: DocumentationSide,
+ render: impl Fn(&mut App) -> AnyElement + 'static,
) -> Self {
- self.documentation_aside = Some(Rc::new(element));
+ self.documentation_aside = Some(DocumentationAside {
+ side,
+ render: Rc::new(render),
+ });
+
self
}
}
@@ -150,10 +155,22 @@ pub struct ContextMenu {
_on_blur_subscription: Subscription,
keep_open_on_confirm: bool,
eager: bool,
- documentation_aside: Option<(usize, Rc<dyn Fn(&mut App) -> AnyElement>)>,
+ documentation_aside: Option<(usize, DocumentationAside)>,
fixed_width: Option<DefiniteLength>,
}
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum DocumentationSide {
+ Left,
+ Right,
+}
+
+#[derive(Clone)]
+pub struct DocumentationAside {
+ side: DocumentationSide,
+ render: Rc<dyn Fn(&mut App) -> AnyElement>,
+}
+
impl Focusable for ContextMenu {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
@@ -933,27 +950,19 @@ impl ContextMenu {
.into_any_element()
};
- let documentation_aside_callback = documentation_aside.clone();
-
div()
.id(("context-menu-child", ix))
- .when_some(
- documentation_aside_callback.clone(),
- |this, documentation_aside_callback| {
- this.occlude()
- .on_hover(cx.listener(move |menu, hovered, _, cx| {
- if *hovered {
- menu.documentation_aside =
- Some((ix, documentation_aside_callback.clone()));
- cx.notify();
- } else if matches!(menu.documentation_aside, Some((id, _)) if id == ix)
- {
- menu.documentation_aside = None;
- cx.notify();
- }
- }))
- },
- )
+ .when_some(documentation_aside.clone(), |this, documentation_aside| {
+ this.occlude()
+ .on_hover(cx.listener(move |menu, hovered, _, cx| {
+ if *hovered {
+ menu.documentation_aside = Some((ix, documentation_aside.clone()));
+ } else if matches!(menu.documentation_aside, Some((id, _)) if id == ix) {
+ menu.documentation_aside = None;
+ }
+ cx.notify();
+ }))
+ })
.child(
ListItem::new(ix)
.group_name("label_container")
@@ -992,21 +1001,18 @@ impl ContextMenu {
})
.map(|binding| {
div().ml_4().child(binding.disabled(*disabled)).when(
- *disabled && documentation_aside_callback.is_some(),
+ *disabled && documentation_aside.is_some(),
|parent| parent.invisible(),
)
})
}))
- .when(
- *disabled && documentation_aside_callback.is_some(),
- |parent| {
- parent.child(
- Icon::new(IconName::Info)
- .size(IconSize::XSmall)
- .color(Color::Muted),
- )
- },
- ),
+ .when(*disabled && documentation_aside.is_some(), |parent| {
+ parent.child(
+ Icon::new(IconName::Info)
+ .size(IconSize::XSmall)
+ .color(Color::Muted),
+ )
+ }),
)
.when_some(
end_slot_icon
@@ -1108,10 +1114,17 @@ impl Render for ContextMenu {
let rem_size = window.rem_size();
let is_wide_window = window_size.width / rem_size > rems_from_px(800.).0;
- let aside = self
- .documentation_aside
- .as_ref()
- .map(|(_, callback)| callback.clone());
+ let aside = self.documentation_aside.clone();
+ let render_aside = |aside: DocumentationAside, cx: &mut Context<Self>| {
+ WithRemSize::new(ui_font_size)
+ .occlude()
+ .elevation_2(cx)
+ .p_2()
+ .overflow_hidden()
+ .when(is_wide_window, |this| this.max_w_96())
+ .when(!is_wide_window, |this| this.max_w_48())
+ .child((aside.render)(cx))
+ };
h_flex()
.when(is_wide_window, |this| this.flex_row())
@@ -1119,15 +1132,8 @@ impl Render for ContextMenu {
.w_full()
.items_start()
.gap_1()
- .child(div().children(aside.map(|aside| {
- WithRemSize::new(ui_font_size)
- .occlude()
- .elevation_2(cx)
- .p_2()
- .overflow_hidden()
- .when(is_wide_window, |this| this.max_w_96())
- .when(!is_wide_window, |this| this.max_w_48())
- .child(aside(cx))
+ .child(div().children(aside.clone().and_then(|(_, aside)| {
+ (aside.side == DocumentationSide::Left).then(|| render_aside(aside, cx))
})))
.child(
WithRemSize::new(ui_font_size)
@@ -1185,5 +1191,8 @@ impl Render for ContextMenu {
),
),
)
+ .child(div().children(aside.and_then(|(_, aside)| {
+ (aside.side == DocumentationSide::Right).then(|| render_aside(aside, cx))
+ })))
}
}
@@ -15,8 +15,8 @@ use gpui::{
use search::{BufferSearchBar, buffer_search};
use settings::{Settings, SettingsStore};
use ui::{
- ButtonStyle, ContextMenu, ContextMenuEntry, IconButton, IconName, IconSize, PopoverMenu,
- PopoverMenuHandle, Tooltip, prelude::*,
+ ButtonStyle, ContextMenu, ContextMenuEntry, DocumentationSide, IconButton, IconName, IconSize,
+ PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*,
};
use vim_mode_setting::VimModeSetting;
use workspace::{
@@ -291,7 +291,7 @@ impl Render for QuickActionBar {
}
});
if !edit_predictions_enabled_at_cursor {
- inline_completion_entry = inline_completion_entry.documentation_aside(|_| {
+ inline_completion_entry = inline_completion_entry.documentation_aside(DocumentationSide::Left, |_| {
Label::new("You can't toggle edit predictions for this file as it is within the excluded files list.").into_any_element()
});
}