diff --git a/assets/icons/blocks.svg b/assets/icons/blocks.svg new file mode 100644 index 0000000000..42d44c3f95 --- /dev/null +++ b/assets/icons/blocks.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/keyboard.svg b/assets/icons/keyboard.svg new file mode 100644 index 0000000000..8bdc054a65 --- /dev/null +++ b/assets/icons/keyboard.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/phone_incoming.svg b/assets/icons/phone_incoming.svg new file mode 100644 index 0000000000..4577df47ad --- /dev/null +++ b/assets/icons/phone_incoming.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/swatch_book.svg b/assets/icons/swatch_book.svg new file mode 100644 index 0000000000..985994ffcf --- /dev/null +++ b/assets/icons/swatch_book.svg @@ -0,0 +1 @@ + diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index ba1a106413..2ea9ddafd7 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -30,6 +30,7 @@ use ui::{ use util::ResultExt; use vcs_menu::{BranchList, OpenRecent as ToggleVcsMenu}; use workspace::{notifications::NotifyResultExt, Workspace}; +use zed_actions::OpenBrowser; #[cfg(feature = "stories")] pub use stories::*; @@ -37,6 +38,8 @@ pub use stories::*; const MAX_PROJECT_NAME_LENGTH: usize = 40; const MAX_BRANCH_NAME_LENGTH: usize = 40; +const BOOK_ONBOARDING: &str = "https://dub.sh/zed-onboarding"; + actions!( collab, [ @@ -580,6 +583,13 @@ impl TitleBar { .action("Themes…", theme_selector::Toggle::default().boxed_clone()) .action("Extensions", extensions_ui::Extensions.boxed_clone()) .separator() + .link( + "Book Onboarding", + OpenBrowser { + url: BOOK_ONBOARDING.to_string(), + } + .boxed_clone(), + ) .action("Sign Out", client::SignOut.boxed_clone()) }) .into() @@ -608,6 +618,14 @@ impl TitleBar { .action("Key Bindings", Box::new(zed_actions::OpenKeymap)) .action("Themes…", theme_selector::Toggle::default().boxed_clone()) .action("Extensions", extensions_ui::Extensions.boxed_clone()) + .separator() + .link( + "Book Onboarding", + OpenBrowser { + url: BOOK_ONBOARDING.to_string(), + } + .boxed_clone(), + ) }) .into() }) diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index 702dd6a092..4829ee1e37 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -1,7 +1,7 @@ #![allow(missing_docs)] use crate::{ - h_flex, prelude::*, utils::WithRemSize, v_flex, Icon, IconName, KeyBinding, Label, List, - ListItem, ListSeparator, ListSubHeader, + h_flex, prelude::*, utils::WithRemSize, v_flex, Icon, IconName, IconSize, KeyBinding, Label, + List, ListItem, ListSeparator, ListSubHeader, }; use gpui::{ px, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, @@ -20,6 +20,7 @@ enum ContextMenuItem { toggle: Option<(IconPosition, bool)>, label: SharedString, icon: Option, + icon_size: IconSize, handler: Rc, &mut WindowContext)>, action: Option>, disabled: bool, @@ -103,6 +104,7 @@ impl ContextMenu { label: label.into(), handler: Rc::new(move |_, cx| handler(cx)), icon: None, + icon_size: IconSize::Small, action, disabled: false, }); @@ -122,6 +124,7 @@ impl ContextMenu { label: label.into(), handler: Rc::new(move |_, cx| handler(cx)), icon: None, + icon_size: IconSize::Small, action, disabled: false, }); @@ -171,6 +174,7 @@ impl ContextMenu { cx.dispatch_action(action.boxed_clone()); }), icon: None, + icon_size: IconSize::Small, disabled: false, }); self @@ -193,6 +197,7 @@ impl ContextMenu { cx.dispatch_action(action.boxed_clone()); }), icon: None, + icon_size: IconSize::Small, disabled: true, }); self @@ -206,6 +211,7 @@ impl ContextMenu { action: Some(action.boxed_clone()), handler: Rc::new(move |_, cx| cx.dispatch_action(action.boxed_clone())), icon: Some(IconName::ArrowUpRight), + icon_size: IconSize::XSmall, disabled: false, }); self @@ -393,6 +399,7 @@ impl Render for ContextMenu { label, handler, icon, + icon_size, action, disabled, } => { @@ -403,12 +410,12 @@ impl Render for ContextMenu { } else { Color::Default }; - let label_element = if let Some(icon) = icon { + let label_element = if let Some(icon_name) = icon { h_flex() .gap_1() .child(Label::new(label.clone()).color(color)) .child( - Icon::new(*icon).size(IconSize::Small).color(color), + Icon::new(*icon_name).size(*icon_size).color(color), ) .into_any_element() } else { diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 89763c3a42..4f9317203d 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -135,6 +135,7 @@ pub enum IconName { BellDot, BellOff, BellRing, + Blocks, Bolt, Book, BookCopy, @@ -202,6 +203,7 @@ pub enum IconName { Indicator, IndicatorX, InlayHint, + Keyboard, Library, LineHeight, Link, @@ -227,6 +229,7 @@ pub enum IconName { PocketKnife, Public, PullRequest, + PhoneIncoming, Quote, RefreshTitle, Regex, @@ -269,6 +272,7 @@ pub enum IconName { SupermavenDisabled, SupermavenError, SupermavenInit, + SwatchBook, Tab, Terminal, Trash, diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 0be48bd82e..a644fc22dc 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -26,6 +26,7 @@ actions!(welcome, [ResetHints]); pub const FIRST_OPEN: &str = "first_open"; pub const DOCS_URL: &str = "https://zed.dev/docs/"; +const BOOK_ONBOARDING: &str = "https://dub.sh/zed-onboarding"; pub fn init(cx: &mut AppContext) { BaseKeymap::register(cx); @@ -75,125 +76,207 @@ impl Render for WelcomePage { .track_focus(&self.focus_handle(cx)) .child( v_flex() - .w_80() - .gap_6() + .gap_8() .mx_auto() .child( - svg() - .path("icons/logo_96.svg") - .text_color(cx.theme().colors().icon_disabled) - .w(px(80.)) - .h(px(80.)) - .mx_auto(), + v_flex() + .w_full() + .child( + svg() + .path("icons/logo_96.svg") + .text_color(cx.theme().colors().icon_disabled) + .w(px(40.)) + .h(px(40.)) + .mx_auto() + .mb_4(), + ) + .child( + h_flex() + .w_full() + .justify_center() + .child(Headline::new("Welcome to Zed")), + ) + .child( + h_flex().w_full().justify_center().child( + Label::new("The editor for what's next") + .color(Color::Muted) + .italic(true), + ), + ), ) .child( - v_flex() - .gap_2() + h_flex() + .items_start() + .gap_8() .child( - Button::new("choose-theme", "Choose Theme") - .full_width() - .on_click(cx.listener(|this, _, cx| { - this.telemetry.report_app_event( - "welcome page: change theme".to_string(), - ); - this.workspace - .update(cx, |workspace, cx| { - theme_selector::toggle( - workspace, - &Default::default(), - cx, - ) - }) - .ok(); - })), + v_flex() + .gap_2() + .pr_8() + .border_r_1() + .border_color(cx.theme().colors().border_variant) + .child( + self.section_label(cx).child( + Label::new("Get Started") + .size(LabelSize::XSmall) + .color(Color::Muted), + ), + ) + .child( + Button::new("choose-theme", "Choose a Theme") + .icon(IconName::SwatchBook) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.telemetry.report_app_event( + "welcome page: change theme".to_string(), + ); + this.workspace + .update(cx, |workspace, cx| { + theme_selector::toggle( + workspace, + &Default::default(), + cx, + ) + }) + .ok(); + })), + ) + .child( + Button::new("choose-keymap", "Choose a Keymap") + .icon(IconName::Keyboard) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.telemetry.report_app_event( + "welcome page: change keymap".to_string(), + ); + this.workspace + .update(cx, |workspace, cx| { + base_keymap_picker::toggle( + workspace, + &Default::default(), + cx, + ) + }) + .ok(); + })), + ) + .child( + Button::new( + "sign-in-to-copilot", + "Sign in to GitHub Copilot", + ) + .icon(IconName::Copilot) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click( + cx.listener(|this, _, cx| { + this.telemetry.report_app_event( + "welcome page: sign in to copilot".to_string(), + ); + inline_completion_button::initiate_sign_in(cx); + }), + ), + ) + .child( + Button::new("edit settings", "Edit Settings") + .icon(IconName::Settings) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.telemetry.report_app_event( + "welcome page: edit settings".to_string(), + ); + cx.dispatch_action(Box::new( + zed_actions::OpenSettings, + )); + })), + ), ) .child( - Button::new("choose-keymap", "Choose Keymap") - .full_width() - .on_click(cx.listener(|this, _, cx| { - this.telemetry.report_app_event( - "welcome page: change keymap".to_string(), - ); - this.workspace - .update(cx, |workspace, cx| { - base_keymap_picker::toggle( - workspace, - &Default::default(), - cx, - ) - }) - .ok(); - })), - ) - .child( - Button::new("edit settings", "Edit Settings") - .full_width() - .on_click(cx.listener(|this, _, cx| { - this.telemetry.report_app_event( - "welcome page: edit settings".to_string(), - ); - cx.dispatch_action(Box::new(zed_actions::OpenSettings)); - })), - ) - .child(Button::new("view docs", "View Docs").full_width().on_click( - cx.listener(|this, _, cx| { - this.telemetry - .report_app_event("welcome page: view docs".to_string()); - cx.open_url(DOCS_URL); - }), - )), - ) - .child( - v_flex() - .gap_2() - .when(cfg!(target_os = "macos"), |el| { - el.child( - Button::new("install-cli", "Install the CLI") - .full_width() - .on_click(cx.listener(|this, _, cx| { - this.telemetry.report_app_event( - "welcome page: install cli".to_string(), - ); - cx.app_mut() - .spawn(|cx| async move { - install_cli::install_cli(&cx).await - }) - .detach_and_log_err(cx); - })), - ) - }) - .child( - Button::new("sign-in-to-copilot", "Sign in to GitHub Copilot") - .full_width() - .on_click(cx.listener(|this, _, cx| { - this.telemetry.report_app_event( - "welcome page: sign in to copilot".to_string(), - ); - inline_completion_button::initiate_sign_in(cx); - })), - ) - .child( - Button::new("explore extensions", "Explore extensions") - .full_width() - .on_click(cx.listener(|this, _, cx| { - this.telemetry.report_app_event( - "welcome page: open extensions".to_string(), - ); - cx.dispatch_action(Box::new(extensions_ui::Extensions)); - })), + v_flex() + .gap_2() + .child( + self.section_label(cx).child( + Label::new("Resources") + .size(LabelSize::XSmall) + .color(Color::Muted), + ), + ) + .when(cfg!(target_os = "macos"), |el| { + el.child( + Button::new("install-cli", "Install the CLI") + .icon(IconName::Terminal) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.telemetry.report_app_event( + "welcome page: install cli".to_string(), + ); + cx.app_mut() + .spawn(|cx| async move { + install_cli::install_cli(&cx).await + }) + .detach_and_log_err(cx); + })), + ) + }) + .child( + Button::new("view-docs", "View Documentation") + .icon(IconName::FileCode) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.telemetry.report_app_event( + "welcome page: view docs".to_string(), + ); + cx.open_url(DOCS_URL); + })), + ) + .child( + Button::new("explore-extensions", "Explore Extensions") + .icon(IconName::Blocks) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.telemetry.report_app_event( + "welcome page: open extensions".to_string(), + ); + cx.dispatch_action(Box::new( + extensions_ui::Extensions, + )); + })), + ) + .child( + Button::new("book-onboarding", "Book Onboarding") + .icon(IconName::PhoneIncoming) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|_, _, cx| { + cx.open_url(BOOK_ONBOARDING); + })), + ), ), ) .child( v_flex() .p_3() .gap_2() - .bg(cx.theme().colors().elevated_surface_background) + .bg(cx.theme().colors().element_background) .border_1() - .border_color(cx.theme().colors().border) + .border_color(cx.theme().colors().border_variant) .rounded_md() .child(CheckboxWithLabel::new( "enable-vim", - Label::new("Enable vim mode"), + Label::new("Enable Vim Mode"), if VimModeSetting::get_global(cx).0 { ui::Selection::Selected } else { @@ -209,35 +292,9 @@ impl Render for WelcomePage { ); }), )) - .child(CheckboxWithLabel::new( - "enable-telemetry", - Label::new("Send anonymous usage data"), - if TelemetrySettings::get_global(cx).metrics { - ui::Selection::Selected - } else { - ui::Selection::Unselected - }, - cx.listener(move |this, selection, cx| { - this.telemetry.report_app_event( - "welcome page: toggle metric telemetry".to_string(), - ); - this.update_settings::(selection, cx, { - let telemetry = this.telemetry.clone(); - - move |settings, value| { - settings.metrics = Some(value); - - telemetry.report_setting_event( - "metric telemetry", - value.to_string(), - ); - } - }); - }), - )) .child(CheckboxWithLabel::new( "enable-crash", - Label::new("Send crash reports"), + Label::new("Send Crash Reports"), if TelemetrySettings::get_global(cx).diagnostics { ui::Selection::Selected } else { @@ -260,6 +317,32 @@ impl Render for WelcomePage { } }); }), + )) + .child(CheckboxWithLabel::new( + "enable-telemetry", + Label::new("Send Telemetry"), + if TelemetrySettings::get_global(cx).metrics { + ui::Selection::Selected + } else { + ui::Selection::Unselected + }, + cx.listener(move |this, selection, cx| { + this.telemetry.report_app_event( + "welcome page: toggle metric telemetry".to_string(), + ); + this.update_settings::(selection, cx, { + let telemetry = this.telemetry.clone(); + + move |settings, value| { + settings.metrics = Some(value); + + telemetry.report_setting_event( + "metric telemetry", + value.to_string(), + ); + } + }); + }), )), ), ) @@ -287,6 +370,13 @@ impl WelcomePage { this } + fn section_label(&self, cx: &WindowContext) -> Div { + div() + .pl_1() + .font_buffer(cx) + .text_color(Color::Muted.color(cx)) + } + fn update_settings( &mut self, selection: &Selection,