Add placeholder git panel (#21894)

Adds a simple git placeholder panel for us to iterate from. Also
includes a number of assets from the git prototyping branch that we will
use.

Note: This panel is staff flagged for now.

Release Notes:

- N/A
This commit is contained in:
Nate Butler 2024-12-11 22:13:52 -05:00 committed by GitHub
parent 611abcadc0
commit 59afc27f03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 352 additions and 170 deletions

12
Cargo.lock generated
View file

@ -5169,6 +5169,17 @@ dependencies = [
"util", "util",
] ]
[[package]]
name = "git_ui"
version = "0.1.0"
dependencies = [
"gpui",
"serde",
"ui",
"windows 0.58.0",
"workspace",
]
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.1" version = "0.3.1"
@ -15994,6 +16005,7 @@ dependencies = [
"futures 0.3.31", "futures 0.3.31",
"git", "git",
"git_hosting_providers", "git_hosting_providers",
"git_ui",
"go_to_line", "go_to_line",
"gpui", "gpui",
"http_client", "http_client",

View file

@ -142,6 +142,7 @@ members = [
"crates/zed", "crates/zed",
"crates/zed_actions", "crates/zed_actions",
"crates/zeta", "crates/zeta",
"crates/git_ui",
# #
# Extensions # Extensions
@ -227,6 +228,7 @@ fs = { path = "crates/fs" }
fsevent = { path = "crates/fsevent" } fsevent = { path = "crates/fsevent" }
fuzzy = { path = "crates/fuzzy" } fuzzy = { path = "crates/fuzzy" }
git = { path = "crates/git" } git = { path = "crates/git" }
git_ui = { path = "crates/git_ui" }
git_hosting_providers = { path = "crates/git_hosting_providers" } git_hosting_providers = { path = "crates/git_hosting_providers" }
go_to_line = { path = "crates/go_to_line" } go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" } google_ai = { path = "crates/google_ai" }

View file

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

After

Width:  |  Height:  |  Size: 348 B

View file

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

After

Width:  |  Height:  |  Size: 289 B

View file

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

After

Width:  |  Height:  |  Size: 291 B

View file

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

After

Width:  |  Height:  |  Size: 301 B

View file

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

After

Width:  |  Height:  |  Size: 291 B

View file

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

After

Width:  |  Height:  |  Size: 309 B

View file

@ -64,6 +64,11 @@ impl FeatureFlag for ZetaFeatureFlag {
const NAME: &'static str = "zeta"; const NAME: &'static str = "zeta";
} }
pub struct GitUiFeatureFlag;
impl FeatureFlag for GitUiFeatureFlag {
const NAME: &'static str = "git-ui";
}
pub struct Remoting {} pub struct Remoting {}
impl FeatureFlag for Remoting { impl FeatureFlag for Remoting {
const NAME: &'static str = "remoting"; const NAME: &'static str = "remoting";

25
crates/git_ui/Cargo.toml Normal file
View file

@ -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 = []

1
crates/git_ui/LICENSE-GPL Symbolic link
View file

@ -0,0 +1 @@
../../LICENSE-GPL

View file

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

View file

@ -0,0 +1 @@
pub mod git_panel;

View file

@ -445,7 +445,7 @@ impl ComponentPreview for Button {
"A button allows users to take actions, and make choices, with a single tap." "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![ vec![
example_group_with_title( example_group_with_title(
"Styles", "Styles",

View file

@ -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." "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![ vec![
example_group_with_title( example_group_with_title(
"Default", "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." "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![ vec![example_group(vec![
single_example( single_example(
"Unselected", "Unselected",

View file

@ -95,7 +95,7 @@ impl ComponentPreview for ContentGroup {
ExampleLabelSide::Bottom ExampleLabelSide::Bottom
} }
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> { fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![example_group(vec![ vec![example_group(vec![
single_example( single_example(
"Default", "Default",

View file

@ -3,6 +3,13 @@ use gpui::{Hsla, IntoElement};
use crate::prelude::*; use crate::prelude::*;
#[derive(Clone, Copy, PartialEq)]
enum DividerStyle {
Solid,
Dashed,
}
#[derive(Clone, Copy, PartialEq)]
enum DividerDirection { enum DividerDirection {
Horizontal, Horizontal,
Vertical, Vertical,
@ -27,6 +34,7 @@ impl DividerColor {
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct Divider { pub struct Divider {
style: DividerStyle,
direction: DividerDirection, direction: DividerDirection,
color: DividerColor, color: DividerColor,
inset: bool, inset: bool,
@ -34,22 +42,17 @@ pub struct Divider {
impl RenderOnce for Divider { impl RenderOnce for Divider {
fn render(self, cx: &mut WindowContext) -> impl IntoElement { fn render(self, cx: &mut WindowContext) -> impl IntoElement {
div() match self.style {
.map(|this| match self.direction { DividerStyle::Solid => self.render_solid(cx).into_any_element(),
DividerDirection::Horizontal => { DividerStyle::Dashed => self.render_dashed(cx).into_any_element(),
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))
} }
} }
impl Divider { impl Divider {
pub fn horizontal() -> Self { pub fn horizontal() -> Self {
Self { Self {
style: DividerStyle::Solid,
direction: DividerDirection::Horizontal, direction: DividerDirection::Horizontal,
color: DividerColor::default(), color: DividerColor::default(),
inset: false, inset: false,
@ -58,6 +61,25 @@ impl Divider {
pub fn vertical() -> Self { pub fn vertical() -> Self {
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, direction: DividerDirection::Vertical,
color: DividerColor::default(), color: DividerColor::default(),
inset: false, inset: false,
@ -73,4 +95,49 @@ impl Divider {
self.color = color; self.color = color;
self 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)),
)
}
} }

View file

@ -67,7 +67,7 @@ impl ComponentPreview for Facepile {
\n\nFacepiles are used to display a group of people or things,\ \n\nFacepiles are used to display a group of people or things,\
such as a list of participants in a collaboration session." 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] = [ let few_faces: [&'static str; 3] = [
"https://avatars.githubusercontent.com/u/1714999?s=60&v=4", "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
"https://avatars.githubusercontent.com/u/67129314?s=60&v=4", "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",

View file

@ -200,6 +200,7 @@ pub enum IconName {
GenericRestore, GenericRestore,
Github, Github,
Globe, Globe,
GitBranch,
Hash, Hash,
HistoryRerun, HistoryRerun,
Indicator, Indicator,
@ -224,6 +225,8 @@ pub enum IconName {
Option, Option,
PageDown, PageDown,
PageUp, PageUp,
PanelLeft,
PanelRight,
Pencil, Pencil,
Person, Person,
PhoneIncoming, PhoneIncoming,
@ -267,6 +270,9 @@ pub enum IconName {
SparkleFilled, SparkleFilled,
Spinner, Spinner,
Split, Split,
SquareDot,
SquareMinus,
SquarePlus,
Star, Star,
StarFilled, StarFilled,
Stop, Stop,
@ -497,7 +503,7 @@ impl RenderOnce for IconDecoration {
} }
impl ComponentPreview 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 all_kinds = IconDecorationKind::iter().collect::<Vec<_>>();
let examples = all_kinds let examples = all_kinds
@ -539,7 +545,7 @@ impl RenderOnce for DecoratedIcon {
} }
impl ComponentPreview 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_1 = Icon::new(IconName::FileDoc);
let icon_2 = Icon::new(IconName::FileDoc); let icon_2 = Icon::new(IconName::FileDoc);
let icon_3 = Icon::new(IconName::FileDoc); let icon_3 = Icon::new(IconName::FileDoc);
@ -658,7 +664,7 @@ impl RenderOnce for IconWithIndicator {
} }
impl ComponentPreview for Icon { impl ComponentPreview for Icon {
fn examples(_cx: &WindowContext) -> Vec<ComponentExampleGroup<Icon>> { fn examples(_cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Icon>> {
let arrow_icons = vec![ let arrow_icons = vec![
IconName::ArrowDown, IconName::ArrowDown,
IconName::ArrowLeft, IconName::ArrowLeft,

View file

@ -89,7 +89,7 @@ impl ComponentPreview for Indicator {
"An indicator visually represents a status or state." "An indicator visually represents a status or state."
} }
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> { fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![ vec![
example_group_with_title( example_group_with_title(
"Types", "Types",

View file

@ -160,7 +160,7 @@ impl ComponentPreview for Table {
ExampleLabelSide::Top ExampleLabelSide::Top
} }
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> { fn examples(_: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![ vec![
example_group(vec![ example_group(vec![
single_example( single_example(

View file

@ -30,20 +30,20 @@ pub trait ComponentPreview: IntoElement {
ExampleLabelSide::default() 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>> { fn custom_example(_cx: &WindowContext) -> impl Into<Option<AnyElement>> {
None::<AnyElement> None::<AnyElement>
} }
fn component_previews(cx: &WindowContext) -> Vec<AnyElement> { fn component_previews(cx: &mut WindowContext) -> Vec<AnyElement> {
Self::examples(cx) Self::examples(cx)
.into_iter() .into_iter()
.map(|example| Self::render_example_group(example)) .map(|example| Self::render_example_group(example))
.collect() .collect()
} }
fn render_component_previews(cx: &WindowContext) -> AnyElement { fn render_component_previews(cx: &mut WindowContext) -> AnyElement {
let title = Self::title(); let title = Self::title();
let (source, title) = title let (source, title) = title
.rsplit_once("::") .rsplit_once("::")

View file

@ -108,147 +108,6 @@ impl ThemePreview {
cx.theme().colors().editor_background 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 { fn render_text(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
let bg = layer.bg(cx); 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; let layer = ElevationIndex::Surface;
v_flex() v_flex()
@ -520,8 +379,6 @@ impl ThemePreview {
.child(Indicator::render_component_previews(cx)) .child(Indicator::render_component_previews(cx))
.child(Icon::render_component_previews(cx)) .child(Icon::render_component_previews(cx))
.child(Table::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 { fn render_page_nav(&self, cx: &ViewContext<Self>) -> impl IntoElement {

View file

@ -52,6 +52,7 @@ file_icons.workspace = true
fs.workspace = true fs.workspace = true
futures.workspace = true futures.workspace = true
git.workspace = true git.workspace = true
git_ui.workspace = true
git_hosting_providers.workspace = true git_hosting_providers.workspace = true
go_to_line.workspace = true go_to_line.workspace = true
gpui = { workspace = true, features = ["wayland", "x11", "font-kit"] } gpui = { workspace = true, features = ["wayland", "x11", "font-kit"] }

View file

@ -447,6 +447,7 @@ fn main() {
outline::init(cx); outline::init(cx);
project_symbols::init(cx); project_symbols::init(cx);
project_panel::init(Assets, cx); project_panel::init(Assets, cx);
git_ui::git_panel::init(cx);
outline_panel::init(Assets, cx); outline_panel::init(Assets, cx);
tasks_ui::init(cx); tasks_ui::init(cx);
snippets_ui::init(cx); snippets_ui::init(cx);

View file

@ -216,7 +216,7 @@ pub fn initialize_workspace(
status_bar.add_left_item(activity_indicator, cx); status_bar.add_left_item(activity_indicator, cx);
status_bar.add_right_item(inline_completion_button, 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_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(vim_mode_indicator, cx);
status_bar.add_right_item(cursor_position, cx); status_bar.add_right_item(cursor_position, cx);
}); });
@ -237,8 +237,11 @@ pub fn initialize_workspace(
let release_channel = ReleaseChannel::global(cx); let release_channel = ReleaseChannel::global(cx);
let assistant2_feature_flag = cx.wait_for_flag::<feature_flags::Assistant2FeatureFlag>(); 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 prompt_builder = prompt_builder.clone();
let is_staff = cx.is_staff();
cx.spawn(|workspace_handle, mut cx| async move { cx.spawn(|workspace_handle, mut cx| async move {
let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone()); let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
let outline_panel = OutlinePanel::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(chat_panel, cx);
workspace.add_panel(notification_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 = let is_assistant2_enabled =
if cfg!(test) || release_channel != ReleaseChannel::Dev { if cfg!(test) || release_channel != ReleaseChannel::Dev {
false false
} else { } else {
assistant2_feature_flag.await assistant2_feature_flag.await
} };
;
let (assistant_panel, assistant2_panel) = if is_assistant2_enabled { let (assistant_panel, assistant2_panel) = if is_assistant2_enabled {
let assistant2_panel = let assistant2_panel =
@ -303,6 +319,7 @@ pub fn initialize_workspace(
if let Some(assistant2_panel) = assistant2_panel { if let Some(assistant2_panel) = assistant2_panel {
workspace.add_panel(assistant2_panel, cx); workspace.add_panel(assistant2_panel, cx);
} }
}) })
}) })
.detach(); .detach();