ssh: Add tweaks to the UI (#18817)

Danilo Leal created

Follow up to https://github.com/zed-industries/zed/pull/18727

---

Release Notes:

- N/A

Change summary

crates/recent_projects/src/dev_servers.rs     |  39 +++--
crates/recent_projects/src/recent_projects.rs |   6 
crates/recent_projects/src/ssh_connections.rs | 145 ++++++++++++--------
crates/title_bar/src/title_bar.rs             |  14 +-
4 files changed, 122 insertions(+), 82 deletions(-)

Detailed changes

crates/recent_projects/src/dev_servers.rs đź”—

@@ -556,23 +556,28 @@ impl DevServerProjects {
                     .w_full()
                     .border_l_1()
                     .border_color(cx.theme().colors().border_variant)
-                    .my_1()
+                    .mb_1()
                     .mx_1p5()
-                    .py_0p5()
-                    .px_3()
+                    .pl_2()
                     .child(
                         List::new()
                             .empty_message("No projects.")
                             .children(ssh_connection.projects.iter().enumerate().map(|(pix, p)| {
-                                self.render_ssh_project(ix, &ssh_connection, pix, p, cx)
+                                v_flex().gap_0p5().child(self.render_ssh_project(
+                                    ix,
+                                    &ssh_connection,
+                                    pix,
+                                    p,
+                                    cx,
+                                ))
                             }))
                             .child(
-                                h_flex().child(
+                                h_flex().mt_1().pl_1().child(
                                     Button::new("new-remote_project", "Open Folder…")
-                                        .icon(IconName::Plus)
                                         .size(ButtonSize::Default)
-                                        .style(ButtonStyle::Filled)
                                         .layer(ElevationIndex::ModalSurface)
+                                        .icon(IconName::Plus)
+                                        .icon_color(Color::Muted)
                                         .icon_position(IconPosition::Start)
                                         .on_click(cx.listener(move |this, _, cx| {
                                             this.create_ssh_project(ix, ssh_connection.clone(), cx);
@@ -593,9 +598,15 @@ impl DevServerProjects {
     ) -> impl IntoElement {
         let project = project.clone();
         let server = server.clone();
+
         ListItem::new(("remote-project", ix))
+            .inset(true)
             .spacing(ui::ListItemSpacing::Sparse)
-            .start_slot(Icon::new(IconName::Folder).color(Color::Muted))
+            .start_slot(
+                Icon::new(IconName::Folder)
+                    .color(Color::Muted)
+                    .size(IconSize::Small),
+            )
             .child(Label::new(project.paths.join(", ")))
             .on_click(cx.listener(move |this, _, cx| {
                 let Some(app_state) = this
@@ -635,7 +646,7 @@ impl DevServerProjects {
                     .on_click(
                         cx.listener(move |this, _, cx| this.delete_ssh_project(server_ix, ix, cx)),
                     )
-                    .tooltip(|cx| Tooltip::text("Delete remote project", cx))
+                    .tooltip(|cx| Tooltip::text("Delete Remote Project", cx))
                     .into_any_element(),
             ))
     }
@@ -709,6 +720,7 @@ impl DevServerProjects {
             })
         });
         let theme = cx.theme();
+
         v_flex()
             .id("create-dev-server")
             .overflow_hidden()
@@ -763,6 +775,7 @@ impl DevServerProjects {
             .child(
                 h_flex()
                     .bg(theme.colors().editor_background)
+                    .rounded_b_md()
                     .w_full()
                     .map(|this| {
                         if let Some(ssh_prompt) = ssh_prompt {
@@ -773,9 +786,8 @@ impl DevServerProjects {
                                 h_flex()
                                     .p_2()
                                     .w_full()
-                                    .content_center()
-                                    .gap_2()
-                                    .child(h_flex().w_full())
+                                    .justify_center()
+                                    .gap_1p5()
                                     .child(
                                         div().p_1().rounded_lg().bg(color).with_animation(
                                             "pulse-ssh-waiting-for-connection",
@@ -788,8 +800,7 @@ impl DevServerProjects {
                                     .child(
                                         Label::new("Waiting for connection…")
                                             .size(LabelSize::Small),
-                                    )
-                                    .child(h_flex().w_full()),
+                                    ),
                             )
                         }
                     }),

crates/recent_projects/src/recent_projects.rs đź”—

@@ -566,7 +566,7 @@ impl PickerDelegate for RecentProjectsDelegate {
                 .border_t_1()
                 .py_2()
                 .pr_2()
-                .border_color(cx.theme().colors().border)
+                .border_color(cx.theme().colors().border_variant)
                 .justify_end()
                 .gap_4()
                 .child(
@@ -574,7 +574,7 @@ impl PickerDelegate for RecentProjectsDelegate {
                         .when_some(KeyBinding::for_action(&OpenRemote, cx), |button, key| {
                             button.child(key)
                         })
-                        .child(Label::new("Open remote folder…").color(Color::Muted))
+                        .child(Label::new("Open Remote Folder…").color(Color::Muted))
                         .on_click(|_, cx| cx.dispatch_action(OpenRemote.boxed_clone())),
                 )
                 .child(
@@ -583,7 +583,7 @@ impl PickerDelegate for RecentProjectsDelegate {
                             KeyBinding::for_action(&workspace::Open, cx),
                             |button, key| button.child(key),
                         )
-                        .child(Label::new("Open local folder…").color(Color::Muted))
+                        .child(Label::new("Open Local Folder…").color(Color::Muted))
                         .on_click(|_, cx| cx.dispatch_action(workspace::Open.boxed_clone())),
                 )
                 .into_any(),

crates/recent_projects/src/ssh_connections.rs đź”—

@@ -16,9 +16,9 @@ use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
 use ui::{
-    div, h_flex, v_flex, ActiveTheme, ButtonCommon, Clickable, Color, FluentBuilder as _, Icon,
-    IconButton, IconName, IconSize, InteractiveElement, IntoElement, Label, LabelCommon, Styled,
-    StyledExt as _, Tooltip, ViewContext, VisualContext, WindowContext,
+    div, h_flex, prelude::*, v_flex, ActiveTheme, ButtonCommon, Clickable, Color, Icon, IconButton,
+    IconName, IconSize, InteractiveElement, IntoElement, Label, LabelCommon, Styled, Tooltip,
+    ViewContext, VisualContext, WindowContext,
 };
 use workspace::{AppState, ModalView, Workspace};
 
@@ -84,6 +84,7 @@ pub struct SshPrompt {
 pub struct SshConnectionModal {
     pub(crate) prompt: View<SshPrompt>,
 }
+
 impl SshPrompt {
     pub fn new(connection_options: &SshConnectionOptions, cx: &mut ViewContext<Self>) -> Self {
         let connection_string = connection_options.connection_string().into();
@@ -136,57 +137,70 @@ impl SshPrompt {
 }
 
 impl Render for SshPrompt {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        let cx = cx.window_context();
+        let theme = cx.theme();
         v_flex()
-            .w_full()
             .key_context("PasswordPrompt")
-            .justify_start()
+            .size_full()
+            .justify_center()
             .child(
-                v_flex()
-                    .p_4()
-                    .size_full()
-                    .child(
-                        h_flex()
-                            .gap_2()
-                            .justify_between()
-                            .child(h_flex().w_full())
-                            .child(if self.error_message.is_some() {
-                                Icon::new(IconName::XCircle)
-                                    .size(IconSize::Medium)
-                                    .color(Color::Error)
-                                    .into_any_element()
-                            } else {
-                                Icon::new(IconName::ArrowCircle)
-                                    .size(IconSize::Medium)
-                                    .with_animation(
-                                        "arrow-circle",
-                                        Animation::new(Duration::from_secs(2)).repeat(),
-                                        |icon, delta| {
-                                            icon.transform(Transformation::rotate(percentage(
-                                                delta,
-                                            )))
-                                        },
-                                    )
-                                    .into_any_element()
-                            })
-                            .child(Label::new(format!(
-                                "Connecting to {}…",
-                                self.connection_string
-                            )))
-                            .child(h_flex().w_full()),
-                    )
-                    .when_some(self.error_message.as_ref(), |el, error| {
-                        el.child(Label::new(error.clone()))
+                h_flex()
+                    .py_2()
+                    .px_4()
+                    .justify_center()
+                    .child(if self.error_message.is_some() {
+                        Icon::new(IconName::XCircle)
+                            .size(IconSize::Medium)
+                            .color(Color::Error)
+                            .into_any_element()
+                    } else {
+                        Icon::new(IconName::ArrowCircle)
+                            .size(IconSize::Medium)
+                            .with_animation(
+                                "arrow-circle",
+                                Animation::new(Duration::from_secs(2)).repeat(),
+                                |icon, delta| {
+                                    icon.transform(Transformation::rotate(percentage(delta)))
+                                },
+                            )
+                            .into_any_element()
                     })
-                    .when(
-                        self.error_message.is_none() && self.status_message.is_some(),
-                        |el| el.child(Label::new(self.status_message.clone().unwrap())),
+                    .child(
+                        div()
+                            .ml_1()
+                            .child(Label::new("SSH Connection").size(LabelSize::Small)),
                     )
-                    .when_some(self.prompt.as_ref(), |el, prompt| {
-                        el.child(Label::new(prompt.0.clone()))
-                            .child(self.editor.clone())
-                    }),
+                    .child(
+                        div()
+                            .when_some(self.error_message.as_ref(), |el, error| {
+                                el.child(Label::new(format!("-{}", error)).size(LabelSize::Small))
+                            })
+                            .when(
+                                self.error_message.is_none() && self.status_message.is_some(),
+                                |el| {
+                                    el.child(
+                                        Label::new(format!(
+                                            "-{}",
+                                            self.status_message.clone().unwrap()
+                                        ))
+                                        .size(LabelSize::Small),
+                                    )
+                                },
+                            ),
+                    ),
             )
+            .child(div().when_some(self.prompt.as_ref(), |el, prompt| {
+                el.child(
+                    h_flex()
+                        .p_4()
+                        .border_t_1()
+                        .border_color(theme.colors().border_variant)
+                        .font_buffer(cx)
+                        .child(Label::new(prompt.0.clone()))
+                        .child(self.editor.clone()),
+                )
+            }))
     }
 }
 
@@ -210,39 +224,54 @@ impl Render for SshConnectionModal {
     fn render(&mut self, cx: &mut ui::ViewContext<Self>) -> impl ui::IntoElement {
         let connection_string = self.prompt.read(cx).connection_string.clone();
         let theme = cx.theme();
-        let header_color = theme.colors().element_background;
-        let body_color = theme.colors().background;
+        let mut header_color = cx.theme().colors().text;
+        header_color.fade_out(0.96);
+        let body_color = theme.colors().editor_background;
+
         v_flex()
             .elevation_3(cx)
             .on_action(cx.listener(Self::dismiss))
             .on_action(cx.listener(Self::confirm))
-            .w(px(400.))
+            .w(px(500.))
+            .border_1()
+            .border_color(theme.colors().border)
             .child(
                 h_flex()
+                    .relative()
                     .p_1()
+                    .rounded_t_md()
                     .border_b_1()
                     .border_color(theme.colors().border)
                     .bg(header_color)
                     .justify_between()
                     .child(
-                        IconButton::new("ssh-connection-cancel", IconName::ArrowLeft)
-                            .icon_size(IconSize::XSmall)
-                            .on_click(|_, cx| cx.dispatch_action(menu::Cancel.boxed_clone()))
-                            .tooltip(|cx| Tooltip::for_action("Back", &menu::Cancel, cx)),
+                        div().absolute().left_0p5().top_0p5().child(
+                            IconButton::new("ssh-connection-cancel", IconName::ArrowLeft)
+                                .icon_size(IconSize::XSmall)
+                                .on_click(|_, cx| cx.dispatch_action(menu::Cancel.boxed_clone()))
+                                .tooltip(|cx| Tooltip::for_action("Back", &menu::Cancel, cx)),
+                        ),
                     )
                     .child(
                         h_flex()
+                            .w_full()
                             .gap_2()
+                            .justify_center()
                             .child(Icon::new(IconName::Server).size(IconSize::XSmall))
                             .child(
                                 Label::new(connection_string)
                                     .size(ui::LabelSize::Small)
                                     .single_line(),
                             ),
-                    )
-                    .child(div()),
+                    ),
+            )
+            .child(
+                h_flex()
+                    .rounded_b_md()
+                    .bg(body_color)
+                    .w_full()
+                    .child(self.prompt.clone()),
             )
-            .child(h_flex().bg(body_color).w_full().child(self.prompt.clone()))
     }
 }
 

crates/title_bar/src/title_bar.rs đź”—

@@ -24,8 +24,8 @@ use smallvec::SmallVec;
 use std::sync::Arc;
 use theme::ActiveTheme;
 use ui::{
-    h_flex, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon, IconName,
-    Indicator, PopoverMenu, Tooltip,
+    h_flex, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon,
+    IconButtonShape, IconName, IconSize, Indicator, PopoverMenu, Tooltip,
 };
 use util::ResultExt;
 use vcs_menu::{BranchList, OpenRecent as ToggleVcsMenu};
@@ -274,18 +274,19 @@ impl TitleBar {
         };
         let indicator = div()
             .absolute()
-            .w_1_4()
-            .h_1_4()
+            .size_1p5()
             .right_0p5()
             .bottom_0p5()
-            .p_1()
-            .rounded_2xl()
+            .rounded_full()
             .bg(indicator_color.color(cx));
 
         Some(
             div()
+                .relative()
                 .child(
                     IconButton::new("ssh-server-icon", IconName::Server)
+                        .icon_size(IconSize::Small)
+                        .shape(IconButtonShape::Square)
                         .tooltip(move |cx| {
                             Tooltip::with_meta(
                                 "Remote Project",
@@ -294,7 +295,6 @@ impl TitleBar {
                                 cx,
                             )
                         })
-                        .shape(ui::IconButtonShape::Square)
                         .on_click(|_, cx| {
                             cx.dispatch_action(OpenRemote.boxed_clone());
                         }),