Detailed changes
@@ -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",
@@ -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" }
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-git-branch"><line x1="6" x2="6" y1="3" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panel-left"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/></svg>
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panel-right"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/></svg>
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-dot"><rect width="18" height="18" x="3" y="3" rx="2"/><circle cx="12" cy="12" r="1"/></svg>
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-minus"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M8 12h8"/></svg>
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-plus"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M8 12h8"/><path d="M12 8v8"/></svg>
@@ -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";
@@ -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 = []
@@ -0,0 +1 @@
+../../LICENSE-GPL
@@ -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>| {
+ workspace.register_action(|workspace, _: &ToggleFocus, cx| {
+ workspace.toggle_panel_focus::<GitPanel>(cx);
+ });
+ },
+ )
+ .detach();
+}
+
+actions!(git_panel, [Deploy, ToggleFocus]);
+
+#[derive(Clone)]
+pub struct GitPanel {
+ _workspace: WeakView<Workspace>,
+ focus_handle: FocusHandle,
+ width: Option<Pixels>,
+}
+
+impl GitPanel {
+ pub fn load(
+ workspace: WeakView<Workspace>,
+ cx: AsyncWindowContext,
+ ) -> Task<Result<View<Self>>> {
+ 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<Workspace>, cx: &mut ViewContext<Self>) -> Self {
+ Self {
+ _workspace: workspace,
+ focus_handle: cx.focus_handle(),
+ width: Some(px(360.)),
+ }
+ }
+
+ pub fn render_panel_header(&self, cx: &mut ViewContext<Self>) -> 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<Self>) -> 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<Self>) -> 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<PanelEvent> 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<Self>) {}
+
+ fn size(&self, _cx: &gpui::WindowContext) -> Pixels {
+ self.width.unwrap_or(px(360.))
+ }
+
+ fn set_size(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>) {
+ self.width = size;
+ cx.notify();
+ }
+
+ fn icon(&self, _cx: &gpui::WindowContext) -> Option<ui::IconName> {
+ Some(ui::IconName::GitBranch)
+ }
+
+ fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
+ Some("Git Panel")
+ }
+
+ fn toggle_action(&self) -> Box<dyn Action> {
+ Box::new(ToggleFocus)
+ }
+}
@@ -0,0 +1 @@
+pub mod git_panel;
@@ -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<ComponentExampleGroup<Self>> {
+ fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![
example_group_with_title(
"Styles",
@@ -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<ComponentExampleGroup<Self>> {
+ fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
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<ComponentExampleGroup<Self>> {
+ fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![example_group(vec![
single_example(
"Unselected",
@@ -95,7 +95,7 @@ impl ComponentPreview for ContentGroup {
ExampleLabelSide::Bottom
}
- fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+ fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![example_group(vec![
single_example(
"Default",
@@ -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)),
+ )
+ }
}
@@ -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<ComponentExampleGroup<Self>> {
+ fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
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",
@@ -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<ComponentExampleGroup<Self>> {
+ fn examples(cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
let all_kinds = IconDecorationKind::iter().collect::<Vec<_>>();
let examples = all_kinds
@@ -539,7 +545,7 @@ impl RenderOnce for DecoratedIcon {
}
impl ComponentPreview for DecoratedIcon {
- fn examples(cx: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+ fn examples(cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
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<ComponentExampleGroup<Icon>> {
+ fn examples(_cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Icon>> {
let arrow_icons = vec![
IconName::ArrowDown,
IconName::ArrowLeft,
@@ -89,7 +89,7 @@ impl ComponentPreview for Indicator {
"An indicator visually represents a status or state."
}
- fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+ fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![
example_group_with_title(
"Types",
@@ -160,7 +160,7 @@ impl ComponentPreview for Table {
ExampleLabelSide::Top
}
- fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
+ fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![
example_group(vec![
single_example(
@@ -30,20 +30,20 @@ pub trait ComponentPreview: IntoElement {
ExampleLabelSide::default()
}
- fn examples(_cx: &WindowContext) -> Vec<ComponentExampleGroup<Self>>;
+ fn examples(_cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>>;
fn custom_example(_cx: &WindowContext) -> impl Into<Option<AnyElement>> {
None::<AnyElement>
}
- fn component_previews(cx: &WindowContext) -> Vec<AnyElement> {
+ fn component_previews(cx: &mut WindowContext) -> Vec<AnyElement> {
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("::")
@@ -108,147 +108,6 @@ impl ThemePreview {
cx.theme().colors().editor_background
}
- fn render_avatars(&self, cx: &ViewContext<Self>) -> 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<Self>) -> 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<Self>) -> impl IntoElement {
let bg = layer.bg(cx);
@@ -502,7 +361,7 @@ impl ThemePreview {
)
}
- fn render_components_page(&self, cx: &ViewContext<Self>) -> 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<Self>) -> impl IntoElement {
@@ -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"] }
@@ -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);
@@ -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::<feature_flags::Assistant2FeatureFlag>();
+ let git_ui_feature_flag = cx.wait_for_flag::<feature_flags::GitUiFeatureFlag>();
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();