diff --git a/crates/chat_ui/src/lib.rs b/crates/chat_ui/src/lib.rs index afd44aec..ae37f311 100644 --- a/crates/chat_ui/src/lib.rs +++ b/crates/chat_ui/src/lib.rs @@ -33,6 +33,7 @@ use ui::input::{Input, InputEvent, InputState}; use ui::menu::DropdownMenu; use ui::notification::Notification; use ui::scroll::Scrollbar; +use ui::tooltip::Tooltip; use ui::{ Disableable, Icon, IconName, InteractiveElementExt, Sizable, StyledExt, WindowExtension, h_flex, v_flex, @@ -1651,7 +1652,7 @@ impl ChatPanel { .size_16() .when(cx.theme().shadow, |this| this.shadow_lg()) .rounded(cx.theme().radius) - .object_fit(ObjectFit::ScaleDown), + .object_fit(ObjectFit::Cover), ) .child( div() @@ -1734,33 +1735,23 @@ impl ChatPanel { label: SharedString, cx: &Context, ) -> AnyElement { - div() + h_flex() .id(SharedString::from(format!("file-{id}"))) - .flex() - .items_center() + .self_start() + .items_start() + .min_w_0() .gap_2() - .py_1() - .px_2() + .p_2() .border_1() .border_color(cx.theme().border_variant) .rounded(cx.theme().radius) - .hover(|this| this.bg(cx.theme().surface_background)) - .child( - Icon::new(IconName::Lock) - .small() - .text_color(cx.theme().text_placeholder), - ) + .child(Icon::new(IconName::Lock).text_color(cx.theme().icon_accent)) .child( v_flex() - .flex_1() + .min_w_0() .overflow_hidden() .text_sm() - .child( - div() - .text_ellipsis() - .line_clamp(1) - .child(file.display_name()), - ) + .child(div().line_height(relative(1.2)).child(file.display_name())) .child( div() .text_xs() @@ -1782,58 +1773,59 @@ impl ChatPanel { /// Render an uploaded, encrypted file which is not sent yet fn render_pending_file(&self, pending: &PendingFile, cx: &Context) -> impl IntoElement { let file = &pending.file; + let label = file.display_name(); div() .id(SharedString::from(file.url.to_string())) .relative() - .flex() - .items_center() - .gap_2() - .p_1() - .pr_2() - .border_1() - .border_color(cx.theme().border_variant) - .rounded(cx.theme().radius) - .when(file.is_image(), |this| { - this.child( - img(pending.path.clone()) - .size_8() - .rounded(cx.theme().radius) - .object_fit(ObjectFit::Cover), - ) + .w_16() + .tooltip(move |window, cx| Tooltip::new(label.clone(), window, cx).into()) + .map(|this| { + if file.is_image() { + this.child( + img(pending.path.clone()) + .size_16() + .when(cx.theme().shadow, |this| this.shadow_sm()) + .rounded(cx.theme().radius) + .object_fit(ObjectFit::Cover), + ) + } else { + this.child( + div() + .size_16() + .flex() + .items_center() + .justify_center() + .rounded(cx.theme().radius) + .border_1() + .border_color(cx.theme().border_variant) + .bg(cx.theme().surface_background) + .text_xs() + .text_center() + .child("Preview not available"), + ) + } }) .child( v_flex() - .text_sm() - .child( - div() - .max_w(px(160.)) - .text_ellipsis() - .line_clamp(1) - .child(file.display_name()), - ) - .child( - h_flex() - .gap_1() - .items_center() - .text_xs() - .text_color(cx.theme().text_placeholder) - .child(Icon::new(IconName::Lock).size_2()) - .child("End-to-end encrypted"), - ), - ) - .child( - Button::new(SharedString::from(format!("remove-{}", file.url))) - .icon(IconName::Close) - .xsmall() - .ghost() - .on_click({ - let url = file.url.clone(); - cx.listener(move |this, _, _, cx| { - this.remove_pending_file(&url, cx); - }) - }), + .absolute() + .top_neg_1() + .right_neg_1() + .size_4() + .items_center() + .justify_center() + .rounded_full() + .border_1() + .border_color(cx.theme().border_variant) + .bg(gpui::green()) + .child(Icon::new(IconName::Lock).size_2().text_color(gpui::white())), ) + .on_click({ + let url = file.url.clone(); + cx.listener(move |this, _, _, cx| { + this.remove_pending_file(&url, cx); + }) + }) } fn render_pending_file_list( @@ -2026,6 +2018,10 @@ impl Focusable for ChatPanel { impl Render for ChatPanel { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + const WARNING: &str = "Attachments added while typing are uploaded without encryption"; + + let is_typing = !self.input.read(cx).value().trim().is_empty(); + v_flex() .image_cache(coop_cache(self.id.clone(), 100)) .on_action(cx.listener(Self::on_command)) @@ -2059,10 +2055,8 @@ impl Render for ChatPanel { .map(|this| { if self.messages.is_empty() { this.child( - div() + h_flex() .size_full() - .flex() - .items_center() .justify_end() .child(self.render_announcement(cx)), ) @@ -2089,20 +2083,15 @@ impl Render for ChatPanel { .children(self.render_attachment_list(window, cx)) .children(self.render_pending_file_list(window, cx)) .children(self.render_reply_list(window, cx)) - .when( - !self.input.read(cx).value().trim().is_empty(), - |this| { - this.child( - div() - .px_1() - .text_xs() - .text_color(cx.theme().text_warning) - .child( - "Attachments added while typing are uploaded without encryption", - ), - ) - }, - ) + .when(is_typing, |this| { + this.child( + div() + .px_1() + .text_xs() + .text_color(cx.theme().text_warning) + .child(WARNING), + ) + }) .child( h_flex() .items_end()