diff --git a/Cargo.lock b/Cargo.lock index 9aa17fc358..d8950b856c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5169,6 +5169,17 @@ dependencies = [ "util", ] +[[package]] +name = "git_ui" +version = "0.1.0" +dependencies = [ + "gpui", + "serde", + "ui", + "windows 0.58.0", + "workspace", +] + [[package]] name = "glob" version = "0.3.1" @@ -15994,6 +16005,7 @@ dependencies = [ "futures 0.3.31", "git", "git_hosting_providers", + "git_ui", "go_to_line", "gpui", "http_client", diff --git a/Cargo.toml b/Cargo.toml index faade8c3a9..743f6178bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,6 +142,7 @@ members = [ "crates/zed", "crates/zed_actions", "crates/zeta", + "crates/git_ui", # # Extensions @@ -227,6 +228,7 @@ fs = { path = "crates/fs" } fsevent = { path = "crates/fsevent" } fuzzy = { path = "crates/fuzzy" } git = { path = "crates/git" } +git_ui = { path = "crates/git_ui" } git_hosting_providers = { path = "crates/git_hosting_providers" } go_to_line = { path = "crates/go_to_line" } google_ai = { path = "crates/google_ai" } diff --git a/assets/icons/git_branch.svg b/assets/icons/git_branch.svg new file mode 100644 index 0000000000..db6190a9c8 --- /dev/null +++ b/assets/icons/git_branch.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/panel_left.svg b/assets/icons/panel_left.svg new file mode 100644 index 0000000000..2eed26673e --- /dev/null +++ b/assets/icons/panel_left.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/panel_right.svg b/assets/icons/panel_right.svg new file mode 100644 index 0000000000..d29a4a519e --- /dev/null +++ b/assets/icons/panel_right.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/square_dot.svg b/assets/icons/square_dot.svg new file mode 100644 index 0000000000..2c1d8afdcb --- /dev/null +++ b/assets/icons/square_dot.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/square_minus.svg b/assets/icons/square_minus.svg new file mode 100644 index 0000000000..a9ab42c408 --- /dev/null +++ b/assets/icons/square_minus.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/square_plus.svg b/assets/icons/square_plus.svg new file mode 100644 index 0000000000..8cbe3dc0e7 --- /dev/null +++ b/assets/icons/square_plus.svg @@ -0,0 +1 @@ + diff --git a/crates/feature_flags/src/feature_flags.rs b/crates/feature_flags/src/feature_flags.rs index f4bebca4d8..b32e56aa5e 100644 --- a/crates/feature_flags/src/feature_flags.rs +++ b/crates/feature_flags/src/feature_flags.rs @@ -64,6 +64,11 @@ impl FeatureFlag for ZetaFeatureFlag { const NAME: &'static str = "zeta"; } +pub struct GitUiFeatureFlag; +impl FeatureFlag for GitUiFeatureFlag { + const NAME: &'static str = "git-ui"; +} + pub struct Remoting {} impl FeatureFlag for Remoting { const NAME: &'static str = "remoting"; diff --git a/crates/git_ui/Cargo.toml b/crates/git_ui/Cargo.toml new file mode 100644 index 0000000000..4875b2afdf --- /dev/null +++ b/crates/git_ui/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "git_ui" +version = "0.1.0" +edition = "2021" +publish = false +license = "GPL-3.0-or-later" + +[lints] +workspace = true + +[lib] +name = "git_ui" +path = "src/git_ui.rs" + +[dependencies] +gpui.workspace = true +serde.workspace = true +workspace.workspace = true +ui.workspace = true + +[target.'cfg(windows)'.dependencies] +windows.workspace = true + +[features] +default = [] diff --git a/crates/git_ui/LICENSE-GPL b/crates/git_ui/LICENSE-GPL new file mode 120000 index 0000000000..89e542f750 --- /dev/null +++ b/crates/git_ui/LICENSE-GPL @@ -0,0 +1 @@ +../../LICENSE-GPL \ No newline at end of file diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs new file mode 100644 index 0000000000..b901414d8c --- /dev/null +++ b/crates/git_ui/src/git_panel.rs @@ -0,0 +1,181 @@ +use gpui::*; +use ui::{prelude::*, Checkbox, Divider, DividerColor, ElevationIndex}; +use workspace::dock::{DockPosition, Panel, PanelEvent}; +use workspace::Workspace; + +pub fn init(cx: &mut AppContext) { + cx.observe_new_views( + |workspace: &mut Workspace, _cx: &mut ViewContext| { + workspace.register_action(|workspace, _: &ToggleFocus, cx| { + workspace.toggle_panel_focus::(cx); + }); + }, + ) + .detach(); +} + +actions!(git_panel, [Deploy, ToggleFocus]); + +#[derive(Clone)] +pub struct GitPanel { + _workspace: WeakView, + focus_handle: FocusHandle, + width: Option, +} + +impl GitPanel { + pub fn load( + workspace: WeakView, + cx: AsyncWindowContext, + ) -> Task>> { + cx.spawn(|mut cx| async move { + workspace.update(&mut cx, |workspace, cx| { + let workspace_handle = workspace.weak_handle(); + + cx.new_view(|cx| Self::new(workspace_handle, cx)) + }) + }) + } + + pub fn new(workspace: WeakView, cx: &mut ViewContext) -> Self { + Self { + _workspace: workspace, + focus_handle: cx.focus_handle(), + width: Some(px(360.)), + } + } + + pub fn render_panel_header(&self, cx: &mut ViewContext) -> impl IntoElement { + h_flex() + .h(px(32.)) + .items_center() + .px_3() + .bg(ElevationIndex::Surface.bg(cx)) + .child( + h_flex() + .gap_1() + .child(Checkbox::new("all-changes", true.into()).disabled(true)) + .child(div().text_buffer(cx).text_ui_sm(cx).child("0 changes")), + ) + .child(div().flex_grow()) + .child( + h_flex() + .gap_1() + .child( + IconButton::new("discard-changes", IconName::Undo) + .icon_size(IconSize::Small) + .disabled(true), + ) + .child( + Button::new("stage-all", "Stage All") + .label_size(LabelSize::Small) + .layer(ElevationIndex::ElevatedSurface) + .size(ButtonSize::Compact) + .style(ButtonStyle::Filled) + .disabled(true), + ), + ) + } + + pub fn render_commit_editor(&self, cx: &ViewContext) -> impl IntoElement { + div().w_full().h(px(140.)).px_2().pt_1().pb_2().child( + v_flex() + .h_full() + .py_2p5() + .px_3() + .bg(cx.theme().colors().editor_background) + .font_buffer(cx) + .text_ui_sm(cx) + .text_color(cx.theme().colors().text_muted) + .child("Add a message") + .gap_1() + .child(div().flex_grow()) + .child( + h_flex().child(div().gap_1().flex_grow()).child( + Button::new("commit", "Commit") + .label_size(LabelSize::Small) + .layer(ElevationIndex::ElevatedSurface) + .size(ButtonSize::Compact) + .style(ButtonStyle::Filled) + .disabled(true), + ), + ) + .cursor(CursorStyle::OperationNotAllowed) + .opacity(0.5), + ) + } +} + +impl Render for GitPanel { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + v_flex() + .key_context("GitPanel") + .font_buffer(cx) + .py_1() + .id("git_panel") + .track_focus(&self.focus_handle) + .size_full() + .overflow_hidden() + .bg(ElevationIndex::Surface.bg(cx)) + .child(self.render_panel_header(cx)) + .child( + h_flex() + .items_center() + .h(px(8.)) + .child(Divider::horizontal_dashed().color(DividerColor::Border)), + ) + .child(div().flex_1()) + .child( + h_flex() + .items_center() + .h(px(8.)) + .child(Divider::horizontal_dashed().color(DividerColor::Border)), + ) + .child(self.render_commit_editor(cx)) + } +} + +impl FocusableView for GitPanel { + fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle { + self.focus_handle.clone() + } +} + +impl EventEmitter for GitPanel {} + +impl Panel for GitPanel { + fn persistent_name() -> &'static str { + "GitPanel" + } + + fn position(&self, _cx: &gpui::WindowContext) -> DockPosition { + DockPosition::Left + } + + fn position_is_valid(&self, position: DockPosition) -> bool { + matches!(position, DockPosition::Left | DockPosition::Right) + } + + fn set_position(&mut self, _position: DockPosition, _cx: &mut ViewContext) {} + + fn size(&self, _cx: &gpui::WindowContext) -> Pixels { + self.width.unwrap_or(px(360.)) + } + + fn set_size(&mut self, size: Option, cx: &mut ViewContext) { + self.width = size; + cx.notify(); + } + + fn icon(&self, _cx: &gpui::WindowContext) -> Option { + Some(ui::IconName::GitBranch) + } + + fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { + Some("Git Panel") + } + + fn toggle_action(&self) -> Box { + Box::new(ToggleFocus) + } +} diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs new file mode 100644 index 0000000000..b23171d722 --- /dev/null +++ b/crates/git_ui/src/git_ui.rs @@ -0,0 +1 @@ +pub mod git_panel; diff --git a/crates/ui/src/components/button/button.rs b/crates/ui/src/components/button/button.rs index fdf9b537bc..774d9e59e4 100644 --- a/crates/ui/src/components/button/button.rs +++ b/crates/ui/src/components/button/button.rs @@ -445,7 +445,7 @@ impl ComponentPreview for Button { "A button allows users to take actions, and make choices, with a single tap." } - fn examples(_: &WindowContext) -> Vec> { + fn examples(_: &mut WindowContext) -> Vec> { vec![ example_group_with_title( "Styles", diff --git a/crates/ui/src/components/checkbox.rs b/crates/ui/src/components/checkbox.rs index 0a3fc6f650..df2dbe3da2 100644 --- a/crates/ui/src/components/checkbox.rs +++ b/crates/ui/src/components/checkbox.rs @@ -118,7 +118,7 @@ impl ComponentPreview for Checkbox { "A checkbox lets people choose between a pair of opposing states, like enabled and disabled, using a different appearance to indicate each state." } - fn examples(_: &WindowContext) -> Vec> { + fn examples(_: &mut WindowContext) -> Vec> { vec![ example_group_with_title( "Default", @@ -214,7 +214,7 @@ impl ComponentPreview for CheckboxWithLabel { "A checkbox with an associated label, allowing users to select an option while providing a descriptive text." } - fn examples(_: &WindowContext) -> Vec> { + fn examples(_: &mut WindowContext) -> Vec> { vec![example_group(vec![ single_example( "Unselected", diff --git a/crates/ui/src/components/content_group.rs b/crates/ui/src/components/content_group.rs index b8ba5b8860..d9d71c3eeb 100644 --- a/crates/ui/src/components/content_group.rs +++ b/crates/ui/src/components/content_group.rs @@ -95,7 +95,7 @@ impl ComponentPreview for ContentGroup { ExampleLabelSide::Bottom } - fn examples(_: &WindowContext) -> Vec> { + fn examples(_: &mut WindowContext) -> Vec> { vec![example_group(vec![ single_example( "Default", diff --git a/crates/ui/src/components/divider.rs b/crates/ui/src/components/divider.rs index 71234057b2..aa93350da4 100644 --- a/crates/ui/src/components/divider.rs +++ b/crates/ui/src/components/divider.rs @@ -3,6 +3,13 @@ use gpui::{Hsla, IntoElement}; use crate::prelude::*; +#[derive(Clone, Copy, PartialEq)] +enum DividerStyle { + Solid, + Dashed, +} + +#[derive(Clone, Copy, PartialEq)] enum DividerDirection { Horizontal, Vertical, @@ -27,6 +34,7 @@ impl DividerColor { #[derive(IntoElement)] pub struct Divider { + style: DividerStyle, direction: DividerDirection, color: DividerColor, inset: bool, @@ -34,22 +42,17 @@ pub struct Divider { impl RenderOnce for Divider { fn render(self, cx: &mut WindowContext) -> impl IntoElement { - div() - .map(|this| match self.direction { - DividerDirection::Horizontal => { - this.h_px().w_full().when(self.inset, |this| this.mx_1p5()) - } - DividerDirection::Vertical => { - this.w_px().h_full().when(self.inset, |this| this.my_1p5()) - } - }) - .bg(self.color.hsla(cx)) + match self.style { + DividerStyle::Solid => self.render_solid(cx).into_any_element(), + DividerStyle::Dashed => self.render_dashed(cx).into_any_element(), + } } } impl Divider { pub fn horizontal() -> Self { Self { + style: DividerStyle::Solid, direction: DividerDirection::Horizontal, color: DividerColor::default(), inset: false, @@ -58,6 +61,25 @@ impl Divider { pub fn vertical() -> Self { Self { + style: DividerStyle::Solid, + direction: DividerDirection::Vertical, + color: DividerColor::default(), + inset: false, + } + } + + pub fn horizontal_dashed() -> Self { + Self { + style: DividerStyle::Dashed, + direction: DividerDirection::Horizontal, + color: DividerColor::default(), + inset: false, + } + } + + pub fn vertical_dashed() -> Self { + Self { + style: DividerStyle::Dashed, direction: DividerDirection::Vertical, color: DividerColor::default(), inset: false, @@ -73,4 +95,49 @@ impl Divider { self.color = color; self } + + pub fn render_solid(self, cx: &WindowContext) -> impl IntoElement { + div() + .map(|this| match self.direction { + DividerDirection::Horizontal => { + this.h_px().w_full().when(self.inset, |this| this.mx_1p5()) + } + DividerDirection::Vertical => { + this.w_px().h_full().when(self.inset, |this| this.my_1p5()) + } + }) + .bg(self.color.hsla(cx)) + } + + // TODO: Use canvas or a shader here + // This obviously is a short term approach + pub fn render_dashed(self, cx: &WindowContext) -> impl IntoElement { + let segment_count = 128; + let segment_count_f = segment_count as f32; + let segment_min_w = 6.; + let base = match self.direction { + DividerDirection::Horizontal => h_flex(), + DividerDirection::Vertical => v_flex(), + }; + let (w, h) = match self.direction { + DividerDirection::Horizontal => (px(segment_min_w), px(1.)), + DividerDirection::Vertical => (px(1.), px(segment_min_w)), + }; + let color = self.color.hsla(cx); + let total_min_w = segment_min_w * segment_count_f * 2.; // * 2 because of the gap + + base.min_w(px(total_min_w)) + .map(|this| { + if self.direction == DividerDirection::Horizontal { + this.w_full().h_px() + } else { + this.w_px().h_full() + } + }) + .gap(px(segment_min_w)) + .overflow_hidden() + .children( + (0..segment_count).map(|_| div().flex_grow().flex_shrink_0().w(w).h(h).bg(color)), + ) + } } diff --git a/crates/ui/src/components/facepile.rs b/crates/ui/src/components/facepile.rs index eb4dd8a98e..e3b799efe5 100644 --- a/crates/ui/src/components/facepile.rs +++ b/crates/ui/src/components/facepile.rs @@ -67,7 +67,7 @@ impl ComponentPreview for Facepile { \n\nFacepiles are used to display a group of people or things,\ such as a list of participants in a collaboration session." } - fn examples(_: &WindowContext) -> Vec> { + fn examples(_: &mut WindowContext) -> Vec> { let few_faces: [&'static str; 3] = [ "https://avatars.githubusercontent.com/u/1714999?s=60&v=4", "https://avatars.githubusercontent.com/u/67129314?s=60&v=4", diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 8aaef3a69a..e663e00708 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -200,6 +200,7 @@ pub enum IconName { GenericRestore, Github, Globe, + GitBranch, Hash, HistoryRerun, Indicator, @@ -224,6 +225,8 @@ pub enum IconName { Option, PageDown, PageUp, + PanelLeft, + PanelRight, Pencil, Person, PhoneIncoming, @@ -267,6 +270,9 @@ pub enum IconName { SparkleFilled, Spinner, Split, + SquareDot, + SquareMinus, + SquarePlus, Star, StarFilled, Stop, @@ -497,7 +503,7 @@ impl RenderOnce for IconDecoration { } impl ComponentPreview for IconDecoration { - fn examples(cx: &WindowContext) -> Vec> { + fn examples(cx: &mut WindowContext) -> Vec> { let all_kinds = IconDecorationKind::iter().collect::>(); let examples = all_kinds @@ -539,7 +545,7 @@ impl RenderOnce for DecoratedIcon { } impl ComponentPreview for DecoratedIcon { - fn examples(cx: &WindowContext) -> Vec> { + fn examples(cx: &mut WindowContext) -> Vec> { let icon_1 = Icon::new(IconName::FileDoc); let icon_2 = Icon::new(IconName::FileDoc); let icon_3 = Icon::new(IconName::FileDoc); @@ -658,7 +664,7 @@ impl RenderOnce for IconWithIndicator { } impl ComponentPreview for Icon { - fn examples(_cx: &WindowContext) -> Vec> { + fn examples(_cx: &mut WindowContext) -> Vec> { let arrow_icons = vec![ IconName::ArrowDown, IconName::ArrowLeft, diff --git a/crates/ui/src/components/indicator.rs b/crates/ui/src/components/indicator.rs index b0d5b0d2da..26eebb8568 100644 --- a/crates/ui/src/components/indicator.rs +++ b/crates/ui/src/components/indicator.rs @@ -89,7 +89,7 @@ impl ComponentPreview for Indicator { "An indicator visually represents a status or state." } - fn examples(_: &WindowContext) -> Vec> { + fn examples(_: &mut WindowContext) -> Vec> { vec![ example_group_with_title( "Types", diff --git a/crates/ui/src/components/table.rs b/crates/ui/src/components/table.rs index 0ef5eda7b7..796250947f 100644 --- a/crates/ui/src/components/table.rs +++ b/crates/ui/src/components/table.rs @@ -160,7 +160,7 @@ impl ComponentPreview for Table { ExampleLabelSide::Top } - fn examples(_: &WindowContext) -> Vec> { + fn examples(_: &mut WindowContext) -> Vec> { vec![ example_group(vec![ single_example( diff --git a/crates/ui/src/traits/component_preview.rs b/crates/ui/src/traits/component_preview.rs index eefc1e8228..aab01355a1 100644 --- a/crates/ui/src/traits/component_preview.rs +++ b/crates/ui/src/traits/component_preview.rs @@ -30,20 +30,20 @@ pub trait ComponentPreview: IntoElement { ExampleLabelSide::default() } - fn examples(_cx: &WindowContext) -> Vec>; + fn examples(_cx: &mut WindowContext) -> Vec>; fn custom_example(_cx: &WindowContext) -> impl Into> { None:: } - fn component_previews(cx: &WindowContext) -> Vec { + fn component_previews(cx: &mut WindowContext) -> Vec { Self::examples(cx) .into_iter() .map(|example| Self::render_example_group(example)) .collect() } - fn render_component_previews(cx: &WindowContext) -> AnyElement { + fn render_component_previews(cx: &mut WindowContext) -> AnyElement { let title = Self::title(); let (source, title) = title .rsplit_once("::") diff --git a/crates/workspace/src/theme_preview.rs b/crates/workspace/src/theme_preview.rs index 337e603d84..932696f560 100644 --- a/crates/workspace/src/theme_preview.rs +++ b/crates/workspace/src/theme_preview.rs @@ -108,147 +108,6 @@ impl ThemePreview { cx.theme().colors().editor_background } - fn render_avatars(&self, cx: &ViewContext) -> impl IntoElement { - v_flex() - .gap_1() - .child( - Headline::new("Avatars") - .size(HeadlineSize::Small) - .color(Color::Muted), - ) - .child( - h_flex() - .items_start() - .gap_4() - .child(Avatar::new(AVATAR_URL).size(px(24.))) - .child(Avatar::new(AVATAR_URL).size(px(24.)).grayscale(true)) - .child( - Avatar::new(AVATAR_URL) - .size(px(24.)) - .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)), - ) - .child( - Avatar::new(AVATAR_URL) - .size(px(24.)) - .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)), - ) - .child( - Avatar::new(AVATAR_URL) - .size(px(24.)) - .indicator(AvatarAvailabilityIndicator::new(Availability::Free)), - ) - .child( - Avatar::new(AVATAR_URL) - .size(px(24.)) - .indicator(AvatarAvailabilityIndicator::new(Availability::Free)), - ) - .child( - Facepile::empty() - .child( - Avatar::new(AVATAR_URL) - .border_color(Self::preview_bg(cx)) - .size(px(22.)) - .into_any_element(), - ) - .child( - Avatar::new(AVATAR_URL) - .border_color(Self::preview_bg(cx)) - .size(px(22.)) - .into_any_element(), - ) - .child( - Avatar::new(AVATAR_URL) - .border_color(Self::preview_bg(cx)) - .size(px(22.)) - .into_any_element(), - ) - .child( - Avatar::new(AVATAR_URL) - .border_color(Self::preview_bg(cx)) - .size(px(22.)) - .into_any_element(), - ), - ), - ) - } - - fn render_buttons(&self, layer: ElevationIndex, cx: &ViewContext) -> impl IntoElement { - v_flex() - .gap_1() - .child( - Headline::new("Buttons") - .size(HeadlineSize::Small) - .color(Color::Muted), - ) - .child( - h_flex() - .items_start() - .gap_px() - .child( - IconButton::new("icon_button_transparent", IconName::Check) - .style(ButtonStyle::Transparent), - ) - .child( - IconButton::new("icon_button_subtle", IconName::Check) - .style(ButtonStyle::Subtle), - ) - .child( - IconButton::new("icon_button_filled", IconName::Check) - .style(ButtonStyle::Filled), - ) - .child( - IconButton::new("icon_button_selected_accent", IconName::Check) - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) - .selected(true), - ) - .child(IconButton::new("icon_button_selected", IconName::Check).selected(true)) - .child( - IconButton::new("icon_button_positive", IconName::Check) - .style(ButtonStyle::Tinted(TintColor::Positive)), - ) - .child( - IconButton::new("icon_button_warning", IconName::Check) - .style(ButtonStyle::Tinted(TintColor::Warning)), - ) - .child( - IconButton::new("icon_button_negative", IconName::Check) - .style(ButtonStyle::Tinted(TintColor::Negative)), - ), - ) - .child( - h_flex() - .gap_px() - .child( - Button::new("button_transparent", "Transparent") - .style(ButtonStyle::Transparent), - ) - .child(Button::new("button_subtle", "Subtle").style(ButtonStyle::Subtle)) - .child(Button::new("button_filled", "Filled").style(ButtonStyle::Filled)) - .child( - Button::new("button_selected", "Selected") - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) - .selected(true), - ) - .child( - Button::new("button_selected_tinted", "Selected (Tinted)") - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) - .selected(true), - ) - .child( - Button::new("button_positive", "Tint::Positive") - .style(ButtonStyle::Tinted(TintColor::Positive)), - ) - .child( - Button::new("button_warning", "Tint::Warning") - .style(ButtonStyle::Tinted(TintColor::Warning)), - ) - .child( - Button::new("button_negative", "Tint::Negative") - .style(ButtonStyle::Tinted(TintColor::Negative)), - ), - ) - } - fn render_text(&self, layer: ElevationIndex, cx: &ViewContext) -> impl IntoElement { let bg = layer.bg(cx); @@ -502,7 +361,7 @@ impl ThemePreview { ) } - fn render_components_page(&self, cx: &ViewContext) -> impl IntoElement { + fn render_components_page(&self, cx: &mut WindowContext) -> impl IntoElement { let layer = ElevationIndex::Surface; v_flex() @@ -520,8 +379,6 @@ impl ThemePreview { .child(Indicator::render_component_previews(cx)) .child(Icon::render_component_previews(cx)) .child(Table::render_component_previews(cx)) - .child(self.render_avatars(cx)) - .child(self.render_buttons(layer, cx)) } fn render_page_nav(&self, cx: &ViewContext) -> impl IntoElement { diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 3fec040ec7..f5d0b1374d 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -52,6 +52,7 @@ file_icons.workspace = true fs.workspace = true futures.workspace = true git.workspace = true +git_ui.workspace = true git_hosting_providers.workspace = true go_to_line.workspace = true gpui = { workspace = true, features = ["wayland", "x11", "font-kit"] } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index b76e4fac9c..ab90b8ec6b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -447,6 +447,7 @@ fn main() { outline::init(cx); project_symbols::init(cx); project_panel::init(Assets, cx); + git_ui::git_panel::init(cx); outline_panel::init(Assets, cx); tasks_ui::init(cx); snippets_ui::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 5829726ada..a024a098ae 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -216,7 +216,7 @@ pub fn initialize_workspace( status_bar.add_left_item(activity_indicator, cx); status_bar.add_right_item(inline_completion_button, cx); status_bar.add_right_item(active_buffer_language, cx); - status_bar.add_right_item(active_toolchain_language, cx); + status_bar.add_right_item(active_toolchain_language, cx); status_bar.add_right_item(vim_mode_indicator, cx); status_bar.add_right_item(cursor_position, cx); }); @@ -237,8 +237,11 @@ pub fn initialize_workspace( let release_channel = ReleaseChannel::global(cx); let assistant2_feature_flag = cx.wait_for_flag::(); + let git_ui_feature_flag = cx.wait_for_flag::(); let prompt_builder = prompt_builder.clone(); + let is_staff = cx.is_staff(); + cx.spawn(|workspace_handle, mut cx| async move { let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone()); let outline_panel = OutlinePanel::load(workspace_handle.clone(), cx.clone()); @@ -276,13 +279,26 @@ pub fn initialize_workspace( workspace.add_panel(chat_panel, cx); workspace.add_panel(notification_panel, cx); })?; + let git_ui_enabled = git_ui_feature_flag.await || is_staff; + + let git_panel = if git_ui_enabled { + Some(git_ui::git_panel::GitPanel::load(workspace_handle.clone(), cx.clone()).await?) + } else { + None + }; + + workspace_handle.update(&mut cx, |workspace, cx| { + if let Some(git_panel) = git_panel { + workspace.add_panel(git_panel, cx); + } + })?; + let is_assistant2_enabled = if cfg!(test) || release_channel != ReleaseChannel::Dev { false } else { assistant2_feature_flag.await - } - ; + }; let (assistant_panel, assistant2_panel) = if is_assistant2_enabled { let assistant2_panel = @@ -303,6 +319,7 @@ pub fn initialize_workspace( if let Some(assistant2_panel) = assistant2_panel { workspace.add_panel(assistant2_panel, cx); } + }) }) .detach();