@@ -0,0 +1,85 @@
+use crate::prelude::*;
+
+#[derive(IntoElement, RegisterComponent)]
+pub struct DiffStat {
+ id: ElementId,
+ added: usize,
+ removed: usize,
+}
+
+impl DiffStat {
+ pub fn new(id: impl Into<ElementId>, added: usize, removed: usize) -> Self {
+ Self {
+ id: id.into(),
+ added,
+ removed,
+ }
+ }
+}
+
+impl RenderOnce for DiffStat {
+ fn render(self, _: &mut Window, _cx: &mut App) -> impl IntoElement {
+ h_flex()
+ .id(self.id)
+ .gap_1()
+ .child(
+ h_flex()
+ .gap_0p5()
+ .child(
+ Icon::new(IconName::Plus)
+ .size(IconSize::XSmall)
+ .color(Color::Success),
+ )
+ .child(
+ Label::new(self.added.to_string())
+ .color(Color::Success)
+ .size(LabelSize::Small),
+ ),
+ )
+ .child(
+ h_flex()
+ .gap_0p5()
+ .child(
+ Icon::new(IconName::Dash)
+ .size(IconSize::XSmall)
+ .color(Color::Error),
+ )
+ .child(
+ Label::new(self.removed.to_string())
+ .color(Color::Error)
+ .size(LabelSize::Small),
+ ),
+ )
+ }
+}
+
+impl Component for DiffStat {
+ fn scope() -> ComponentScope {
+ ComponentScope::VersionControl
+ }
+
+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
+ let container = || {
+ h_flex()
+ .py_4()
+ .w_72()
+ .justify_center()
+ .border_1()
+ .border_color(cx.theme().colors().border_variant)
+ .bg(cx.theme().colors().panel_background)
+ };
+
+ let diff_stat_example = vec![single_example(
+ "Default",
+ container()
+ .child(DiffStat::new("id", 1, 2))
+ .into_any_element(),
+ )];
+
+ Some(
+ example_group(diff_stat_example)
+ .vertical()
+ .into_any_element(),
+ )
+ }
+}
@@ -1,4 +1,4 @@
-use crate::{Chip, Indicator, SpinnerLabel, prelude::*};
+use crate::{Chip, DiffStat, Indicator, SpinnerLabel, prelude::*};
use gpui::{ClickEvent, SharedString};
#[derive(IntoElement, RegisterComponent)]
@@ -10,7 +10,8 @@ pub struct ThreadItem {
running: bool,
generation_done: bool,
selected: bool,
- has_changes: bool,
+ added: Option<usize>,
+ removed: Option<usize>,
worktree: Option<SharedString>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
}
@@ -25,7 +26,8 @@ impl ThreadItem {
running: false,
generation_done: false,
selected: false,
- has_changes: false,
+ added: None,
+ removed: None,
worktree: None,
on_click: None,
}
@@ -56,8 +58,13 @@ impl ThreadItem {
self
}
- pub fn has_changes(mut self, has_changes: bool) -> Self {
- self.has_changes = has_changes;
+ pub fn added(mut self, added: usize) -> Self {
+ self.added = Some(added);
+ self
+ }
+
+ pub fn removed(mut self, removed: usize) -> Self {
+ self.removed = Some(removed);
self
}
@@ -90,8 +97,10 @@ impl RenderOnce for ThreadItem {
)
};
+ let has_no_changes = self.added.is_none() && self.removed.is_none();
+
v_flex()
- .id(self.id)
+ .id(self.id.clone())
.cursor_pointer()
.p_2()
.when(self.selected, |this| {
@@ -123,12 +132,19 @@ impl RenderOnce for ThreadItem {
.color(Color::Muted)
.alpha(0.5),
)
- .when(!self.has_changes, |this| {
+ .when(has_no_changes, |this| {
this.child(
Label::new("No Changes")
.size(LabelSize::Small)
.color(Color::Muted),
)
+ })
+ .when(self.added.is_some() || self.removed.is_some(), |this| {
+ this.child(DiffStat::new(
+ self.id,
+ self.added.unwrap_or(0),
+ self.removed.unwrap_or(0),
+ ))
}),
)
.when_some(self.on_click, |this, on_click| this.on_click(on_click))
@@ -192,11 +208,23 @@ impl Component for ThreadItem {
)
.into_any_element(),
),
+ single_example(
+ "With Changes",
+ container()
+ .child(
+ ThreadItem::new("ti-5", "Managing user and project settings interactions")
+ .icon(IconName::AiClaude)
+ .timestamp("7:37 PM")
+ .added(10)
+ .removed(3),
+ )
+ .into_any_element(),
+ ),
single_example(
"Selected Item",
container()
.child(
- ThreadItem::new("ti-5", "Refine textarea interaction behavior")
+ ThreadItem::new("ti-6", "Refine textarea interaction behavior")
.icon(IconName::AiGemini)
.timestamp("3:00 PM")
.selected(true),