Detailed changes
@@ -122,13 +122,17 @@
// Amount of indentation for nested items.
"indent_size": 20
},
- "channels_panel": {
+ "collaboration_panel": {
+ // Whether to show the collaboration panel button in the status bar.
+ "button": true,
// Where to dock channels panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the channels panel.
"default_width": 240
},
"assistant": {
+ // Whether to show the assistant panel button in the status bar.
+ "button": true,
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
"dock": "right",
// Default width when the assistant is docked to the left or right.
@@ -220,7 +224,9 @@
"copilot": {
// The set of glob patterns for which copilot should be disabled
// in any matching file.
- "disabled_globs": [".env"]
+ "disabled_globs": [
+ ".env"
+ ]
},
// Settings specific to journaling
"journal": {
@@ -192,6 +192,7 @@ impl AssistantPanel {
old_dock_position = new_dock_position;
cx.emit(AssistantPanelEvent::DockPositionChanged);
}
+ cx.notify();
})];
this
@@ -790,8 +791,10 @@ impl Panel for AssistantPanel {
}
}
- fn icon_path(&self) -> &'static str {
- "icons/robot_14.svg"
+ fn icon_path(&self, cx: &WindowContext) -> Option<&'static str> {
+ settings::get::<AssistantSettings>(cx)
+ .button
+ .then(|| "icons/robot_14.svg")
}
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
@@ -13,6 +13,7 @@ pub enum AssistantDockPosition {
#[derive(Deserialize, Debug)]
pub struct AssistantSettings {
+ pub button: bool,
pub dock: AssistantDockPosition,
pub default_width: f32,
pub default_height: f32,
@@ -20,6 +21,7 @@ pub struct AssistantSettings {
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContent {
+ pub button: Option<bool>,
pub dock: Option<AssistantDockPosition>,
pub default_width: Option<f32>,
pub default_height: Option<f32>,
@@ -27,7 +27,7 @@ use gpui::{
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use menu::{Confirm, SelectNext, SelectPrev};
-use panel_settings::{ChannelsPanelDockPosition, ChannelsPanelSettings};
+use panel_settings::{CollaborationPanelDockPosition, CollaborationPanelSettings};
use project::{Fs, Project};
use serde_derive::{Deserialize, Serialize};
use settings::SettingsStore;
@@ -65,7 +65,7 @@ impl_actions!(collab_panel, [RemoveChannel, NewChannel, AddMember]);
const CHANNELS_PANEL_KEY: &'static str = "ChannelsPanel";
pub fn init(_client: Arc<Client>, cx: &mut AppContext) {
- settings::register::<panel_settings::ChannelsPanelSettings>(cx);
+ settings::register::<panel_settings::CollaborationPanelSettings>(cx);
contact_finder::init(cx);
channel_modal::init(cx);
@@ -95,6 +95,7 @@ pub struct CollabPanel {
entries: Vec<ListEntry>,
selection: Option<usize>,
user_store: ModelHandle<UserStore>,
+ client: Arc<Client>,
channel_store: ModelHandle<ChannelStore>,
project: ModelHandle<Project>,
match_candidates: Vec<StringMatchCandidate>,
@@ -320,6 +321,7 @@ impl CollabPanel {
match_candidates: Vec::default(),
collapsed_sections: Vec::default(),
workspace: workspace.weak_handle(),
+ client: workspace.app_state().client.clone(),
list_state,
};
this.update_entries(cx);
@@ -334,6 +336,7 @@ impl CollabPanel {
old_dock_position = new_dock_position;
cx.emit(Event::DockPositionChanged);
}
+ cx.notify();
}),
);
@@ -1862,6 +1865,31 @@ impl View for CollabPanel {
fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
let theme = &theme::current(cx).collab_panel;
+ if self.user_store.read(cx).current_user().is_none() {
+ enum LogInButton {}
+
+ return Flex::column()
+ .with_child(
+ MouseEventHandler::<LogInButton, _>::new(0, cx, |state, _| {
+ let button = theme.log_in_button.style_for(state);
+ Label::new("Sign in to collaborate", button.text.clone())
+ .contained()
+ .with_style(button.container)
+ })
+ .on_click(MouseButton::Left, |_, this, cx| {
+ let client = this.client.clone();
+ cx.spawn(|_, cx| async move {
+ client.authenticate_and_connect(true, &cx).await.log_err()
+ })
+ .detach();
+ })
+ .with_cursor_style(CursorStyle::PointingHand),
+ )
+ .contained()
+ .with_style(theme.container)
+ .into_any();
+ }
+
enum PanelFocus {}
MouseEventHandler::<PanelFocus, _>::new(0, cx, |_, cx| {
Stack::new()
@@ -1901,9 +1929,9 @@ impl View for CollabPanel {
impl Panel for CollabPanel {
fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
- match settings::get::<ChannelsPanelSettings>(cx).dock {
- ChannelsPanelDockPosition::Left => DockPosition::Left,
- ChannelsPanelDockPosition::Right => DockPosition::Right,
+ match settings::get::<CollaborationPanelSettings>(cx).dock {
+ CollaborationPanelDockPosition::Left => DockPosition::Left,
+ CollaborationPanelDockPosition::Right => DockPosition::Right,
}
}
@@ -1912,13 +1940,15 @@ impl Panel for CollabPanel {
}
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
- settings::update_settings_file::<ChannelsPanelSettings>(
+ settings::update_settings_file::<CollaborationPanelSettings>(
self.fs.clone(),
cx,
move |settings| {
let dock = match position {
- DockPosition::Left | DockPosition::Bottom => ChannelsPanelDockPosition::Left,
- DockPosition::Right => ChannelsPanelDockPosition::Right,
+ DockPosition::Left | DockPosition::Bottom => {
+ CollaborationPanelDockPosition::Left
+ }
+ DockPosition::Right => CollaborationPanelDockPosition::Right,
};
settings.dock = Some(dock);
},
@@ -1927,7 +1957,7 @@ impl Panel for CollabPanel {
fn size(&self, cx: &gpui::WindowContext) -> f32 {
self.width
- .unwrap_or_else(|| settings::get::<ChannelsPanelSettings>(cx).default_width)
+ .unwrap_or_else(|| settings::get::<CollaborationPanelSettings>(cx).default_width)
}
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>) {
@@ -1936,8 +1966,10 @@ impl Panel for CollabPanel {
cx.notify();
}
- fn icon_path(&self) -> &'static str {
- "icons/radix/person.svg"
+ fn icon_path(&self, cx: &gpui::WindowContext) -> Option<&'static str> {
+ settings::get::<CollaborationPanelSettings>(cx)
+ .button
+ .then(|| "icons/radix/person.svg")
}
fn icon_tooltip(&self) -> (String, Option<Box<dyn gpui::Action>>) {
@@ -5,27 +5,29 @@ use settings::Setting;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
-pub enum ChannelsPanelDockPosition {
+pub enum CollaborationPanelDockPosition {
Left,
Right,
}
#[derive(Deserialize, Debug)]
-pub struct ChannelsPanelSettings {
- pub dock: ChannelsPanelDockPosition,
+pub struct CollaborationPanelSettings {
+ pub button: bool,
+ pub dock: CollaborationPanelDockPosition,
pub default_width: f32,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
-pub struct ChannelsPanelSettingsContent {
- pub dock: Option<ChannelsPanelDockPosition>,
+pub struct CollaborationPanelSettingsContent {
+ pub button: Option<bool>,
+ pub dock: Option<CollaborationPanelDockPosition>,
pub default_width: Option<f32>,
}
-impl Setting for ChannelsPanelSettings {
- const KEY: Option<&'static str> = Some("channels_panel");
+impl Setting for CollaborationPanelSettings {
+ const KEY: Option<&'static str> = Some("collaboration_panel");
- type FileContent = ChannelsPanelSettingsContent;
+ type FileContent = CollaborationPanelSettingsContent;
fn load(
default_value: &Self::FileContent,
@@ -1657,8 +1657,8 @@ impl workspace::dock::Panel for ProjectPanel {
cx.notify();
}
- fn icon_path(&self) -> &'static str {
- "icons/folder_tree_16.svg"
+ fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
+ Some("icons/folder_tree_16.svg")
}
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
@@ -396,8 +396,8 @@ impl Panel for TerminalPanel {
}
}
- fn icon_path(&self) -> &'static str {
- "icons/terminal_12.svg"
+ fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
+ Some("icons/terminal_12.svg")
}
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
@@ -220,6 +220,7 @@ pub struct CopilotAuthAuthorized {
pub struct CollabPanel {
#[serde(flatten)]
pub container: ContainerStyle,
+ pub log_in_button: Interactive<ContainedText>,
pub channel_hash: Icon,
pub channel_modal: ChannelModal,
pub user_query_editor: FieldEditor,
@@ -14,7 +14,7 @@ pub trait Panel: View {
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>);
fn size(&self, cx: &WindowContext) -> f32;
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>);
- fn icon_path(&self) -> &'static str;
+ fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>);
fn icon_label(&self, _: &WindowContext) -> Option<String> {
None
@@ -51,7 +51,7 @@ pub trait PanelHandle {
fn set_active(&self, active: bool, cx: &mut WindowContext);
fn size(&self, cx: &WindowContext) -> f32;
fn set_size(&self, size: f32, cx: &mut WindowContext);
- fn icon_path(&self, cx: &WindowContext) -> &'static str;
+ fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>);
fn icon_label(&self, cx: &WindowContext) -> Option<String>;
fn has_focus(&self, cx: &WindowContext) -> bool;
@@ -98,8 +98,8 @@ where
self.update(cx, |this, cx| this.set_active(active, cx))
}
- fn icon_path(&self, cx: &WindowContext) -> &'static str {
- self.read(cx).icon_path()
+ fn icon_path(&self, cx: &WindowContext) -> Option<&'static str> {
+ self.read(cx).icon_path(cx)
}
fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>) {
@@ -490,8 +490,9 @@ impl View for PanelButtons {
.map(|item| (item.panel.clone(), item.context_menu.clone()))
.collect::<Vec<_>>();
Flex::row()
- .with_children(panels.into_iter().enumerate().map(
+ .with_children(panels.into_iter().enumerate().filter_map(
|(panel_ix, (view, context_menu))| {
+ let icon_path = view.icon_path(cx)?;
let is_active = is_open && panel_ix == active_ix;
let (tooltip, tooltip_action) = if is_active {
(
@@ -505,94 +506,96 @@ impl View for PanelButtons {
} else {
view.icon_tooltip(cx)
};
- Stack::new()
- .with_child(
- MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| {
- let style = button_style.in_state(is_active);
-
- let style = style.style_for(state);
- Flex::row()
- .with_child(
- Svg::new(view.icon_path(cx))
- .with_color(style.icon_color)
- .constrained()
- .with_width(style.icon_size)
- .aligned(),
- )
- .with_children(if let Some(label) = view.icon_label(cx) {
- Some(
- Label::new(label, style.label.text.clone())
- .contained()
- .with_style(style.label.container)
+ Some(
+ Stack::new()
+ .with_child(
+ MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| {
+ let style = button_style.in_state(is_active);
+
+ let style = style.style_for(state);
+ Flex::row()
+ .with_child(
+ Svg::new(icon_path)
+ .with_color(style.icon_color)
+ .constrained()
+ .with_width(style.icon_size)
.aligned(),
)
- } else {
- None
- })
- .constrained()
- .with_height(style.icon_size)
- .contained()
- .with_style(style.container)
- })
- .with_cursor_style(CursorStyle::PointingHand)
- .on_click(MouseButton::Left, {
- let tooltip_action =
- tooltip_action.as_ref().map(|action| action.boxed_clone());
- move |_, this, cx| {
- if let Some(tooltip_action) = &tooltip_action {
- let window_id = cx.window_id();
- let view_id = this.workspace.id();
- let tooltip_action = tooltip_action.boxed_clone();
- cx.spawn(|_, mut cx| async move {
- cx.dispatch_action(
- window_id,
- view_id,
- &*tooltip_action,
+ .with_children(if let Some(label) = view.icon_label(cx) {
+ Some(
+ Label::new(label, style.label.text.clone())
+ .contained()
+ .with_style(style.label.container)
+ .aligned(),
)
- .ok();
+ } else {
+ None
})
- .detach();
- }
- }
- })
- .on_click(MouseButton::Right, {
- let view = view.clone();
- let menu = context_menu.clone();
- move |_, _, cx| {
- const POSITIONS: [DockPosition; 3] = [
- DockPosition::Left,
- DockPosition::Right,
- DockPosition::Bottom,
- ];
-
- menu.update(cx, |menu, cx| {
- let items = POSITIONS
- .into_iter()
- .filter(|position| {
- *position != dock_position
- && view.position_is_valid(*position, cx)
- })
- .map(|position| {
- let view = view.clone();
- ContextMenuItem::handler(
- format!("Dock {}", position.to_label()),
- move |cx| view.set_position(position, cx),
+ .constrained()
+ .with_height(style.icon_size)
+ .contained()
+ .with_style(style.container)
+ })
+ .with_cursor_style(CursorStyle::PointingHand)
+ .on_click(MouseButton::Left, {
+ let tooltip_action =
+ tooltip_action.as_ref().map(|action| action.boxed_clone());
+ move |_, this, cx| {
+ if let Some(tooltip_action) = &tooltip_action {
+ let window_id = cx.window_id();
+ let view_id = this.workspace.id();
+ let tooltip_action = tooltip_action.boxed_clone();
+ cx.spawn(|_, mut cx| async move {
+ cx.dispatch_action(
+ window_id,
+ view_id,
+ &*tooltip_action,
)
+ .ok();
})
- .collect();
- menu.show(Default::default(), menu_corner, items, cx);
- })
- }
- })
- .with_tooltip::<Self>(
- panel_ix,
- tooltip,
- tooltip_action,
- tooltip_style.clone(),
- cx,
- ),
- )
- .with_child(ChildView::new(&context_menu, cx))
+ .detach();
+ }
+ }
+ })
+ .on_click(MouseButton::Right, {
+ let view = view.clone();
+ let menu = context_menu.clone();
+ move |_, _, cx| {
+ const POSITIONS: [DockPosition; 3] = [
+ DockPosition::Left,
+ DockPosition::Right,
+ DockPosition::Bottom,
+ ];
+
+ menu.update(cx, |menu, cx| {
+ let items = POSITIONS
+ .into_iter()
+ .filter(|position| {
+ *position != dock_position
+ && view.position_is_valid(*position, cx)
+ })
+ .map(|position| {
+ let view = view.clone();
+ ContextMenuItem::handler(
+ format!("Dock {}", position.to_label()),
+ move |cx| view.set_position(position, cx),
+ )
+ })
+ .collect();
+ menu.show(Default::default(), menu_corner, items, cx);
+ })
+ }
+ })
+ .with_tooltip::<Self>(
+ panel_ix,
+ tooltip,
+ tooltip_action,
+ tooltip_style.clone(),
+ cx,
+ ),
+ )
+ .with_child(ChildView::new(&context_menu, cx)),
+ )
},
))
.contained()
@@ -702,8 +705,8 @@ pub mod test {
self.size = size;
}
- fn icon_path(&self) -> &'static str {
- "icons/test_panel.svg"
+ fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
+ Some("icons/test_panel.svg")
}
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
@@ -67,6 +67,37 @@ export default function contacts_panel(): any {
return {
channel_modal: channel_modal(),
+ log_in_button: interactive({
+ base: {
+ background: background(theme.middle),
+ border: border(theme.middle, "active"),
+ corner_radius: 4,
+ margin: {
+ top: 16,
+ left: 16,
+ right: 16,
+ },
+ padding: {
+ top: 3,
+ bottom: 3,
+ left: 7,
+ right: 7,
+ },
+ ...text(theme.middle, "sans", "default", { size: "sm" }),
+ },
+ state: {
+ hovered: {
+ ...text(theme.middle, "sans", "default", { size: "sm" }),
+ background: background(theme.middle, "hovered"),
+ border: border(theme.middle, "active"),
+ },
+ clicked: {
+ ...text(theme.middle, "sans", "default", { size: "sm" }),
+ background: background(theme.middle, "pressed"),
+ border: border(theme.middle, "active"),
+ },
+ },
+ }),
background: background(layer),
padding: {
top: 12,