diff --git a/assets/icons/file_diff.svg b/assets/icons/file_diff.svg new file mode 100644 index 0000000000000..ff20f16c60907 --- /dev/null +++ b/assets/icons/file_diff.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/thumbs_down.svg b/assets/icons/thumbs_down.svg new file mode 100644 index 0000000000000..816273e9700a7 --- /dev/null +++ b/assets/icons/thumbs_down.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/thumbs_up.svg b/assets/icons/thumbs_up.svg new file mode 100644 index 0000000000000..01fae76fe750d --- /dev/null +++ b/assets/icons/thumbs_up.svg @@ -0,0 +1 @@ + diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 1885c927361b4..8aaef3a69a292 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -178,6 +178,7 @@ pub enum IconName { File, FileCode, FileDoc, + FileDiff, FileGeneric, FileGit, FileLock, @@ -278,6 +279,8 @@ pub enum IconName { Tab, Terminal, TextSnippet, + ThumbsUp, + ThumbsDown, Trash, TrashAlt, Triangle, diff --git a/crates/zeta/src/rate_completion_modal.rs b/crates/zeta/src/rate_completion_modal.rs index 2d5650ba16e57..c32959b459bfa 100644 --- a/crates/zeta/src/rate_completion_modal.rs +++ b/crates/zeta/src/rate_completion_modal.rs @@ -7,7 +7,7 @@ use gpui::{ use language::{language_settings, OffsetRangeExt}; use settings::Settings; use theme::ThemeSettings; -use ui::{prelude::*, ListItem, ListItemSpacing}; +use ui::{prelude::*, List, ListItem, ListItemSpacing, TintColor}; use workspace::{ModalView, Workspace}; pub struct RateCompletionModal { @@ -69,7 +69,7 @@ impl RateCompletionModal { editor.set_show_wrap_guides(false, cx); editor.set_show_indent_guides(false, cx); editor.set_show_inline_completions(Some(false), cx); - editor.set_placeholder_text("Your feedback about this completion...", cx); + editor.set_placeholder_text("Add your feedback about this completion…", cx); editor }), }); @@ -134,94 +134,116 @@ impl RateCompletionModal { }; let rated = self.zeta.read(cx).is_completion_rated(completion_id); + + let border_color = cx.theme().colors().border; + let bg_color = cx.theme().colors().editor_background; + + let label_container = || h_flex().pl_1().gap_1p5(); + Some( v_flex() - .flex_1() .size_full() - .gap_2() - .child(h_flex().justify_center().children(if rated { - Some( - Label::new("This completion was already rated") - .color(Color::Muted) - .size(LabelSize::Large), - ) - } else if active_completion.completion.edits.is_empty() { - Some( - Label::new("This completion didn't produce any edits") - .color(Color::Warning) - .size(LabelSize::Large), - ) - } else { - None - })) + .overflow_hidden() .child( - v_flex() + div() .id("diff") - .flex_1() - .flex_basis(relative(0.75)) - .bg(cx.theme().colors().editor_background) - .overflow_y_scroll() - .p_2() - .border_color(cx.theme().colors().border) - .border_1() - .rounded_lg() + .py_4() + .px_6() + .size_full() + .bg(bg_color) + .overflow_scroll() .child(StyledText::new(diff).with_highlights(&text_style, diff_highlights)), ) .child( div() - .flex_1() - .flex_basis(relative(0.25)) - .bg(cx.theme().colors().editor_background) - .border_color(cx.theme().colors().border) - .border_1() - .rounded_lg() + .h_40() + .bg(bg_color) + .border_t_1() + .border_color(border_color) .child(active_completion.feedback_editor.clone()), ) .child( h_flex() - .gap_2() - .justify_end() - .child( - Button::new("bad", "👎 Bad Completion") - .size(ButtonSize::Large) - .disabled(rated) - .label_size(LabelSize::Large) - .color(Color::Error) - .on_click({ - let completion = active_completion.completion.clone(); - let feedback_editor = active_completion.feedback_editor.clone(); - cx.listener(move |this, _, cx| { - this.zeta.update(cx, |zeta, cx| { - zeta.rate_completion( - &completion, - InlineCompletionRating::Negative, - feedback_editor.read(cx).text(cx), - cx, - ) - }) - }) - }), - ) + .p_1() + .h_8() + .border_t_1() + .border_color(border_color) + .max_w_full() + .justify_between() + .children(if rated { + Some( + label_container() + .child( + Icon::new(IconName::Check) + .size(IconSize::Small) + .color(Color::Success), + ) + .child(Label::new("Rated completion").color(Color::Muted)), + ) + } else if active_completion.completion.edits.is_empty() { + Some( + label_container() + .child( + Icon::new(IconName::Warning) + .size(IconSize::Small) + .color(Color::Warning), + ) + .child(Label::new("No edits produced").color(Color::Muted)), + ) + } else { + Some(label_container()) + }) .child( - Button::new("good", "👍 Good Completion") - .size(ButtonSize::Large) - .disabled(rated) - .label_size(LabelSize::Large) - .color(Color::Success) - .on_click({ - let completion = active_completion.completion.clone(); - let feedback_editor = active_completion.feedback_editor.clone(); - cx.listener(move |this, _, cx| { - this.zeta.update(cx, |zeta, cx| { - zeta.rate_completion( - &completion, - InlineCompletionRating::Positive, - feedback_editor.read(cx).text(cx), - cx, - ) - }) - }) - }), + h_flex() + .gap_1() + .child( + Button::new("bad", "Bad Completion") + .style(ButtonStyle::Tinted(TintColor::Negative)) + .icon(IconName::ThumbsDown) + .icon_size(IconSize::Small) + .icon_position(IconPosition::Start) + .icon_color(Color::Error) + .disabled(rated) + .on_click({ + let completion = active_completion.completion.clone(); + let feedback_editor = + active_completion.feedback_editor.clone(); + cx.listener(move |this, _, cx| { + this.zeta.update(cx, |zeta, cx| { + zeta.rate_completion( + &completion, + InlineCompletionRating::Negative, + feedback_editor.read(cx).text(cx), + cx, + ) + }) + }) + }), + ) + .child( + Button::new("good", "Good Completion") + .style(ButtonStyle::Tinted(TintColor::Positive)) + .icon(IconName::ThumbsUp) + .icon_size(IconSize::Small) + .icon_position(IconPosition::Start) + .icon_color(Color::Success) + .disabled(rated) + .on_click({ + let completion = active_completion.completion.clone(); + let feedback_editor = + active_completion.feedback_editor.clone(); + cx.listener(move |this, _, cx| { + this.zeta.update(cx, |zeta, cx| { + zeta.rate_completion( + &completion, + InlineCompletionRating::Positive, + feedback_editor.read(cx).text(cx), + cx, + ) + }) + }) + }), + ), ), ), ) @@ -230,27 +252,38 @@ impl RateCompletionModal { impl Render for RateCompletionModal { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + let border_color = cx.theme().colors().border; + h_flex() - .gap_2() - .bg(cx.theme().colors().elevated_surface_background) - .w(cx.viewport_size().width - px(256.)) - .h(cx.viewport_size().height - px(256.)) - .rounded_lg() - .shadow_lg() - .p_2() .key_context("RateCompletionModal") .track_focus(&self.focus_handle) .on_action(cx.listener(Self::dismiss)) + .bg(cx.theme().colors().elevated_surface_background) + .border_1() + .border_color(border_color) + .w(cx.viewport_size().width - px(320.)) + .h(cx.viewport_size().height - px(300.)) + .rounded_lg() + .shadow_lg() .child( div() .id("completion_list") + .border_r_1() + .border_color(border_color) .w_96() .h_full() + .p_0p5() .overflow_y_scroll() .child( - ui::List::new() + List::new() .empty_message( - "No completions, use the editor to generate some and rate them!", + div() + .p_2() + .child( + Label::new("No completions yet. Use the editor to generate some and rate them!") + .color(Color::Muted), + ) + .into_any_element(), ) .children(self.zeta.read(cx).recent_completions().cloned().map( |completion| { @@ -261,22 +294,26 @@ impl Render for RateCompletionModal { let rated = self.zeta.read(cx).is_completion_rated(completion.id); ListItem::new(completion.id) + .inset(true) .spacing(ListItemSpacing::Sparse) .selected(selected) - .end_slot(if rated { + .start_slot(if rated { Icon::new(IconName::Check).color(Color::Success) } else if completion.edits.is_empty() { - Icon::new(IconName::Ellipsis).color(Color::Muted) + Icon::new(IconName::File).color(Color::Muted).size(IconSize::Small) } else { - Icon::new(IconName::Diff).color(Color::Muted) + Icon::new(IconName::FileDiff).color(Color::Accent).size(IconSize::Small) }) .child(Label::new( completion.path.to_string_lossy().to_string(), - )) + ).size(LabelSize::Small)) .child( - Label::new(format!("({})", completion.id)) - .color(Color::Muted) - .size(LabelSize::XSmall), + div() + .overflow_hidden() + .text_ellipsis() + .child(Label::new(format!("({})", completion.id)) + .color(Color::Muted) + .size(LabelSize::XSmall)), ) .on_click(cx.listener(move |this, _, cx| { this.select_completion(Some(completion.clone()), cx);