From 6bbf614a37a9cc8da6518fe36f1cdd42f4af3eb7 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 21 Sep 2023 16:56:04 -0400 Subject: [PATCH 01/11] Fix some typos in `README.md` --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6c502ebc74fadd46df3546c85e0dc25fbb3ca806..b3d4987526a46be3304ca649d90166566c03029e 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea sudo xcodebuild -license ``` -* Install homebrew, node and rustup-init (rutup, rust, cargo, etc.) +* Install homebrew, node and rustup-init (rustup, rust, cargo, etc.) ``` /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew install node rustup-init @@ -36,7 +36,7 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea brew install foreman ``` -* Ensure the Zed.dev website is checked out in a sibling directory and install it's dependencies: +* Ensure the Zed.dev website is checked out in a sibling directory and install its dependencies: ``` cd .. From 92d3115f3d8337a7c04d69d93f86a8b174d164da Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 21 Sep 2023 17:21:40 -0400 Subject: [PATCH 02/11] Fix some typos in `tools.md` --- docs/tools.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tools.md b/docs/tools.md index 6e424a6f8145bc26235cc4e6eeca2db1d52df234..22810e3e07909c6d0f42bdd4c2336d8cd438bf7a 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -32,7 +32,7 @@ Have a team member add you to the [Zed Industries](https://zed-industries.slack. ### Discord -We have a discord community. You can use [this link](https://discord.gg/SSD9eJrn6s) to join. **!Don't share this link, this is specifically for team memebers!** +We have a Discord community. You can use [this link](https://discord.gg/SSD9eJrn6s) to join. **!Don't share this link, this is specifically for team members!** Once you have joined the community, let a team member know and we can add your correct role. @@ -56,7 +56,7 @@ We use Vercel for all of our web deployments and some backend things. If you sig ### Environment Variables -You can get access to many of our shared enviroment variables through 1Password and Vercel. For one password search the value you are looking for, or sort by passwords or API credentials. +You can get access to many of our shared enviroment variables through 1Password and Vercel. For 1Password search the value you are looking for, or sort by passwords or API credentials. For Vercel, go to `settings` -> `Environment Variables` (either on the entire org, or on a specific project depending on where it is shared.) For a given Vercel project if you have their CLI installed you can use `vercel pull` or `vercel env` to pull values down directly. More on those in their [CLI docs](https://vercel.com/docs/cli/env). From c252eae32e395e668a6585cf2dc35643ba970614 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 21 Sep 2023 17:46:37 -0400 Subject: [PATCH 03/11] Reorganize `ui` module exports (#3007) This PR reorganizes the exports for the `ui` module in the `storybook` crate. ### Motivation Currently we expose each of the various elements/components/modules in two places: - Through the module itself (e.g., `ui::element::Avatar`) - Through the `ui` module's re-exports (e.g., `ui::Avatar`) This means it's possible to import any given item from two spots, which can lead to inconsistencies in the consumers. Additionally, it also means we're shipping the exact module structure underneath `ui` as part of the public API. ### Explanation To avoid this, we can avoid exposing each of the individual modules underneath `ui::{element, component, module}` and instead export just the module contents themselves. This makes the `ui` module namespace flat. Release Notes: - N/A --- crates/storybook/src/ui.rs | 26 +++++--------------------- crates/storybook/src/ui/component.rs | 13 +++++++++---- crates/storybook/src/ui/element.rs | 28 +++++++++++++++++++--------- crates/storybook/src/ui/module.rs | 16 +++++++++++----- 4 files changed, 44 insertions(+), 39 deletions(-) diff --git a/crates/storybook/src/ui.rs b/crates/storybook/src/ui.rs index 056ad56a2b81a4ef15ad134391e4bca55c5aef35..f8f0e7a65f45bbfc70c4b96fea2d90f41fc0707f 100644 --- a/crates/storybook/src/ui.rs +++ b/crates/storybook/src/ui.rs @@ -1,23 +1,7 @@ -mod element; -pub use element::avatar::*; -pub use element::details::*; -pub use element::icon::*; -pub use element::icon_button::*; -pub use element::indicator::*; -pub use element::input::*; -pub use element::label::*; -pub use element::text_button::*; -pub use element::tool_divider::*; - mod component; -pub use component::facepile::*; -pub use component::follow_group::*; -pub use component::list_item::*; -pub use component::tab::*; - +mod element; mod module; -pub use module::chat_panel::*; -pub use module::project_panel::*; -pub use module::status_bar::*; -pub use module::tab_bar::*; -pub use module::title_bar::*; + +pub use component::*; +pub use element::*; +pub use module::*; diff --git a/crates/storybook/src/ui/component.rs b/crates/storybook/src/ui/component.rs index 49a126886301a01a67b65ce9b27d6a75b2514625..26fa01584761da227b4c6be6ea185ceb2b5a141a 100644 --- a/crates/storybook/src/ui/component.rs +++ b/crates/storybook/src/ui/component.rs @@ -1,4 +1,9 @@ -pub(crate) mod facepile; -pub(crate) mod follow_group; -pub(crate) mod list_item; -pub(crate) mod tab; +mod facepile; +mod follow_group; +mod list_item; +mod tab; + +pub use facepile::*; +pub use follow_group::*; +pub use list_item::*; +pub use tab::*; diff --git a/crates/storybook/src/ui/element.rs b/crates/storybook/src/ui/element.rs index e79a9c5986ad2c0e63e82acc19ad851587492cad..3f76af0d150e5aa2bd39a3c9ad5eedefbaf57019 100644 --- a/crates/storybook/src/ui/element.rs +++ b/crates/storybook/src/ui/element.rs @@ -1,9 +1,19 @@ -pub(crate) mod avatar; -pub(crate) mod details; -pub(crate) mod icon; -pub(crate) mod icon_button; -pub(crate) mod indicator; -pub(crate) mod input; -pub(crate) mod label; -pub(crate) mod text_button; -pub(crate) mod tool_divider; +mod avatar; +mod details; +mod icon; +mod icon_button; +mod indicator; +mod input; +mod label; +mod text_button; +mod tool_divider; + +pub use avatar::*; +pub use details::*; +pub use icon::*; +pub use icon_button::*; +pub use indicator::*; +pub use input::*; +pub use label::*; +pub use text_button::*; +pub use tool_divider::*; diff --git a/crates/storybook/src/ui/module.rs b/crates/storybook/src/ui/module.rs index a261fffcd61a7129ba102ddb70bbf1dcf38e51fd..a1cead7df9df397602906cdfca7750508532522a 100644 --- a/crates/storybook/src/ui/module.rs +++ b/crates/storybook/src/ui/module.rs @@ -1,5 +1,11 @@ -pub(crate) mod chat_panel; -pub(crate) mod project_panel; -pub(crate) mod status_bar; -pub(crate) mod tab_bar; -pub(crate) mod title_bar; +mod chat_panel; +mod project_panel; +mod status_bar; +mod tab_bar; +mod title_bar; + +pub use chat_panel::*; +pub use project_panel::*; +pub use status_bar::*; +pub use tab_bar::*; +pub use title_bar::*; From baa07e935ea300adcbeb36044aef278fbc5dc383 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 21 Sep 2023 19:25:35 -0400 Subject: [PATCH 04/11] Extract UI elements from `storybook` into new `ui` crate (#3008) This PR extracts the various UI elements from the `storybook` crate into a new `ui` library crate. Release Notes: - N/A --- Cargo.lock | 12 ++++++++++ Cargo.toml | 1 + crates/storybook/Cargo.toml | 1 + crates/storybook/src/collab_panel.rs | 2 +- crates/storybook/src/storybook.rs | 8 +------ crates/storybook/src/ui.rs | 7 ------ crates/storybook/src/ui/component.rs | 9 -------- crates/storybook/src/workspace.rs | 5 +--- crates/ui/Cargo.toml | 12 ++++++++++ crates/{storybook => ui}/src/components.rs | 23 +++++++++++++++---- .../src/components}/facepile.rs | 7 +++--- .../src/components}/follow_group.rs | 8 +++---- .../src/components}/list_item.rs | 10 ++++---- .../ui/component => ui/src/components}/tab.rs | 7 +++--- crates/{storybook => ui}/src/element_ext.rs | 6 +++-- .../src/ui/element.rs => ui/src/elements.rs} | 0 .../ui/element => ui/src/elements}/avatar.rs | 8 +++---- .../ui/element => ui/src/elements}/details.rs | 6 ++--- .../ui/element => ui/src/elements}/icon.rs | 6 ++--- .../src/elements}/icon_button.rs | 10 ++++---- .../element => ui/src/elements}/indicator.rs | 7 +++--- .../ui/element => ui/src/elements}/input.rs | 9 ++++---- .../ui/element => ui/src/elements}/label.rs | 6 ++--- .../src/elements}/text_button.rs | 9 ++++---- .../src/elements}/tool_divider.rs | 7 +++--- crates/ui/src/lib.rs | 14 +++++++++++ .../src/ui/module.rs => ui/src/modules.rs} | 0 .../module => ui/src/modules}/chat_panel.rs | 8 +++---- .../src/modules}/project_panel.rs | 19 +++++++-------- .../module => ui/src/modules}/status_bar.rs | 9 ++++---- .../ui/module => ui/src/modules}/tab_bar.rs | 10 ++++---- .../ui/module => ui/src/modules}/title_bar.rs | 10 ++++---- crates/{storybook => ui}/src/prelude.rs | 0 crates/{storybook => ui}/src/theme.rs | 15 +++++++----- crates/{storybook/src => }/ui/tracker.md | 0 35 files changed, 154 insertions(+), 117 deletions(-) delete mode 100644 crates/storybook/src/ui.rs delete mode 100644 crates/storybook/src/ui/component.rs create mode 100644 crates/ui/Cargo.toml rename crates/{storybook => ui}/src/components.rs (85%) rename crates/{storybook/src/ui/component => ui/src/components}/facepile.rs (84%) rename crates/{storybook/src/ui/component => ui/src/components}/follow_group.rs (88%) rename crates/{storybook/src/ui/component => ui/src/components}/list_item.rs (92%) rename crates/{storybook/src/ui/component => ui/src/components}/tab.rs (92%) rename crates/{storybook => ui}/src/element_ext.rs (99%) rename crates/{storybook/src/ui/element.rs => ui/src/elements.rs} (100%) rename crates/{storybook/src/ui/element => ui/src/elements}/avatar.rs (87%) rename crates/{storybook/src/ui/element => ui/src/elements}/details.rs (88%) rename crates/{storybook/src/ui/element => ui/src/elements}/icon.rs (95%) rename crates/{storybook/src/ui/element => ui/src/elements}/icon_button.rs (88%) rename crates/{storybook/src/ui/element => ui/src/elements}/indicator.rs (86%) rename crates/{storybook/src/ui/element => ui/src/elements}/input.rs (94%) rename crates/{storybook/src/ui/element => ui/src/elements}/label.rs (92%) rename crates/{storybook/src/ui/element => ui/src/elements}/text_button.rs (93%) rename crates/{storybook/src/ui/element => ui/src/elements}/tool_divider.rs (78%) create mode 100644 crates/ui/src/lib.rs rename crates/{storybook/src/ui/module.rs => ui/src/modules.rs} (100%) rename crates/{storybook/src/ui/module => ui/src/modules}/chat_panel.rs (91%) rename crates/{storybook/src/ui/module => ui/src/modules}/project_panel.rs (93%) rename crates/{storybook/src/ui/module => ui/src/modules}/status_bar.rs (96%) rename crates/{storybook/src/ui/module => ui/src/modules}/tab_bar.rs (95%) rename crates/{storybook/src/ui/module => ui/src/modules}/title_bar.rs (95%) rename crates/{storybook => ui}/src/prelude.rs (100%) rename crates/{storybook => ui}/src/theme.rs (94%) rename crates/{storybook/src => }/ui/tracker.md (100%) diff --git a/Cargo.lock b/Cargo.lock index 3cced78c4272e5fc9f571d8023719a1ea56d06d2..05b29aabf400e9843dc45041c956044c3bb7658c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7397,6 +7397,7 @@ dependencies = [ "settings", "simplelog", "theme", + "ui", "util", ] @@ -8599,6 +8600,17 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "ui" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui2", + "serde", + "settings", + "theme", +] + [[package]] name = "unicase" version = "2.7.0" diff --git a/Cargo.toml b/Cargo.toml index c1876434ad46fc7f8c5eddf59d46255fcde4136e..b9622a1c3e3b1970cdae4bd1cb24bdcf96430301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ members = [ "crates/text", "crates/theme", "crates/theme_selector", + "crates/ui", "crates/util", "crates/semantic_index", "crates/vim", diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index f634ea4eca1cb58056f9a14769dfadfcba83a118..c1096a87babed86dd08e513ffbd2f629d6e44c90 100644 --- a/crates/storybook/Cargo.toml +++ b/crates/storybook/Cargo.toml @@ -17,6 +17,7 @@ serde.workspace = true settings = { path = "../settings" } simplelog = "0.9" theme = { path = "../theme" } +ui = { path = "../ui" } util = { path = "../util" } [dev-dependencies] diff --git a/crates/storybook/src/collab_panel.rs b/crates/storybook/src/collab_panel.rs index 87fd536391609c91a1e277015780114d53ef94fc..30d15a5b0400c653dc41a9b65d546b05bb1eed72 100644 --- a/crates/storybook/src/collab_panel.rs +++ b/crates/storybook/src/collab_panel.rs @@ -1,10 +1,10 @@ -use crate::theme::{theme, Theme}; use gpui2::{ elements::{div, div::ScrollState, img, svg}, style::{StyleHelpers, Styleable}, ArcCow, Element, IntoElement, ParentElement, ViewContext, }; use std::marker::PhantomData; +use ui::{theme, Theme}; #[derive(Element)] pub struct CollabPanelElement { diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs index df1db7b8c21d0b6e9083ec2c974b56e7bf99cbeb..92d178fad2728cb1e5e3a5d44caaaf3a5b2347be 100644 --- a/crates/storybook/src/storybook.rs +++ b/crates/storybook/src/storybook.rs @@ -1,20 +1,14 @@ #![allow(dead_code, unused_variables)] -use crate::theme::Theme; use ::theme as legacy_theme; -use element_ext::ElementExt; use gpui2::{serde_json, vec2f, view, Element, RectF, ViewContext, WindowBounds}; use legacy_theme::ThemeSettings; use log::LevelFilter; use settings::{default_settings, SettingsStore}; use simplelog::SimpleLogger; +use ui::{ElementExt, Theme}; mod collab_panel; -mod components; -mod element_ext; -mod prelude; -mod theme; -mod ui; mod workspace; gpui2::actions! { diff --git a/crates/storybook/src/ui.rs b/crates/storybook/src/ui.rs deleted file mode 100644 index f8f0e7a65f45bbfc70c4b96fea2d90f41fc0707f..0000000000000000000000000000000000000000 --- a/crates/storybook/src/ui.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod component; -mod element; -mod module; - -pub use component::*; -pub use element::*; -pub use module::*; diff --git a/crates/storybook/src/ui/component.rs b/crates/storybook/src/ui/component.rs deleted file mode 100644 index 26fa01584761da227b4c6be6ea185ceb2b5a141a..0000000000000000000000000000000000000000 --- a/crates/storybook/src/ui/component.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod facepile; -mod follow_group; -mod list_item; -mod tab; - -pub use facepile::*; -pub use follow_group::*; -pub use list_item::*; -pub use tab::*; diff --git a/crates/storybook/src/workspace.rs b/crates/storybook/src/workspace.rs index 3127bcf837cc24694e78759fbeec8b6788634b78..95152ec8328f26e0749215a7fe55694f7247da2f 100644 --- a/crates/storybook/src/workspace.rs +++ b/crates/storybook/src/workspace.rs @@ -1,12 +1,9 @@ -use crate::{ - theme::theme, - ui::{chat_panel, project_panel, status_bar, tab_bar, title_bar}, -}; use gpui2::{ elements::{div, div::ScrollState}, style::StyleHelpers, Element, IntoElement, ParentElement, ViewContext, }; +use ui::{chat_panel, project_panel, status_bar, tab_bar, theme, title_bar}; #[derive(Element, Default)] struct WorkspaceElement { diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..5018a9173988203d61e26efc2ccbbac2db1e796f --- /dev/null +++ b/crates/ui/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ui" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +anyhow.workspace = true +gpui2 = { path = "../gpui2" } +serde.workspace = true +settings = { path = "../settings" } +theme = { path = "../theme" } diff --git a/crates/storybook/src/components.rs b/crates/ui/src/components.rs similarity index 85% rename from crates/storybook/src/components.rs rename to crates/ui/src/components.rs index 1aafefc1a6a0a89728f64b6c6299e8c68ef1cc20..a82d28eb8cb2539cef985c12a59ba712b97699ae 100644 --- a/crates/storybook/src/components.rs +++ b/crates/ui/src/components.rs @@ -1,8 +1,21 @@ -use gpui2::{ - elements::div, interactive::Interactive, platform::MouseButton, style::StyleHelpers, ArcCow, - Element, EventContext, IntoElement, ParentElement, ViewContext, -}; -use std::{marker::PhantomData, rc::Rc}; +mod facepile; +mod follow_group; +mod list_item; +mod tab; + +pub use facepile::*; +pub use follow_group::*; +pub use list_item::*; +pub use tab::*; + +use std::marker::PhantomData; +use std::rc::Rc; + +use gpui2::elements::div; +use gpui2::interactive::Interactive; +use gpui2::platform::MouseButton; +use gpui2::style::StyleHelpers; +use gpui2::{ArcCow, Element, EventContext, IntoElement, ParentElement, ViewContext}; struct ButtonHandlers { click: Option)>>, diff --git a/crates/storybook/src/ui/component/facepile.rs b/crates/ui/src/components/facepile.rs similarity index 84% rename from crates/storybook/src/ui/component/facepile.rs rename to crates/ui/src/components/facepile.rs index 73ab231c07fe6b28742831fd5120d5ae9d257020..d9566c216cfbf82fc0dce336b3da0dc2bf67e6c6 100644 --- a/crates/storybook/src/ui/component/facepile.rs +++ b/crates/ui/src/components/facepile.rs @@ -1,7 +1,8 @@ -use crate::{theme::theme, ui::Avatar}; +use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::{theme, Avatar}; #[derive(Element)] pub struct Facepile { diff --git a/crates/storybook/src/ui/component/follow_group.rs b/crates/ui/src/components/follow_group.rs similarity index 88% rename from crates/storybook/src/ui/component/follow_group.rs rename to crates/ui/src/components/follow_group.rs index 5d79b8fc06fb3bdab9ddaf83832aa00d685ae676..75e24e9135a93576b63dd65f90dd5e617c26fc37 100644 --- a/crates/storybook/src/ui/component/follow_group.rs +++ b/crates/ui/src/components/follow_group.rs @@ -1,8 +1,8 @@ -use crate::theme::theme; -use crate::ui::{facepile, indicator, Avatar}; +use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::{facepile, indicator, theme, Avatar}; #[derive(Element)] pub struct FollowGroup { diff --git a/crates/storybook/src/ui/component/list_item.rs b/crates/ui/src/components/list_item.rs similarity index 92% rename from crates/storybook/src/ui/component/list_item.rs rename to crates/ui/src/components/list_item.rs index 46df86bfa1e5985a6e28aba6d5aea79e2269a093..868b58e449f778d8356a1823c91f3312328ab4aa 100644 --- a/crates/storybook/src/ui/component/list_item.rs +++ b/crates/ui/src/components/list_item.rs @@ -1,10 +1,10 @@ -use crate::prelude::{InteractionState, ToggleState}; -use crate::theme::theme; -use crate::ui::{icon, IconAsset, Label}; +use gpui2::elements::div; use gpui2::geometry::rems; use gpui2::style::{StyleHelpers, Styleable}; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::prelude::*; +use crate::{icon, theme, IconAsset, Label}; #[derive(Element)] pub struct ListItem { diff --git a/crates/storybook/src/ui/component/tab.rs b/crates/ui/src/components/tab.rs similarity index 92% rename from crates/storybook/src/ui/component/tab.rs rename to crates/ui/src/components/tab.rs index 8237aac0041254c1754d7ba4fd7ea0037597244f..e812a26cd51a024d27036ea8fecf2f83fa796923 100644 --- a/crates/storybook/src/ui/component/tab.rs +++ b/crates/ui/src/components/tab.rs @@ -1,7 +1,8 @@ -use crate::theme::theme; +use gpui2::elements::div; use gpui2::style::{StyleHelpers, Styleable}; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::theme; #[derive(Element)] pub struct Tab { diff --git a/crates/storybook/src/element_ext.rs b/crates/ui/src/element_ext.rs similarity index 99% rename from crates/storybook/src/element_ext.rs rename to crates/ui/src/element_ext.rs index 72ec5b328af00279427795a13e12cd969c34688c..67352f0779774527af4067b37bda662b1fcfdb2c 100644 --- a/crates/storybook/src/element_ext.rs +++ b/crates/ui/src/element_ext.rs @@ -1,7 +1,9 @@ -use crate::theme::{Theme, Themed}; -use gpui2::Element; use std::marker::PhantomData; +use gpui2::Element; + +use crate::theme::{Theme, Themed}; + pub trait ElementExt: Element { fn themed(self, theme: Theme) -> Themed where diff --git a/crates/storybook/src/ui/element.rs b/crates/ui/src/elements.rs similarity index 100% rename from crates/storybook/src/ui/element.rs rename to crates/ui/src/elements.rs diff --git a/crates/storybook/src/ui/element/avatar.rs b/crates/ui/src/elements/avatar.rs similarity index 87% rename from crates/storybook/src/ui/element/avatar.rs rename to crates/ui/src/elements/avatar.rs index 83edc73deba7d6d08539c3dec3b66ad9697860c6..068ec7a28a99472775f887a71d79e83de0459114 100644 --- a/crates/storybook/src/ui/element/avatar.rs +++ b/crates/ui/src/elements/avatar.rs @@ -1,9 +1,9 @@ -use crate::prelude::Shape; -use crate::theme::theme; use gpui2::elements::img; use gpui2::style::StyleHelpers; -use gpui2::{ArcCow, IntoElement}; -use gpui2::{Element, ViewContext}; +use gpui2::{ArcCow, Element, IntoElement, ViewContext}; + +use crate::prelude::*; +use crate::theme; #[derive(Element, Clone)] pub struct Avatar { diff --git a/crates/storybook/src/ui/element/details.rs b/crates/ui/src/elements/details.rs similarity index 88% rename from crates/storybook/src/ui/element/details.rs rename to crates/ui/src/elements/details.rs index 5d06862439cd4b8c4ba177f9302b8f4d4f81e65d..f156199f9e8556a1212f8439f6c71cd20725fff9 100644 --- a/crates/storybook/src/ui/element/details.rs +++ b/crates/ui/src/elements/details.rs @@ -1,8 +1,8 @@ -use crate::theme::theme; use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{Element, ViewContext}; -use gpui2::{IntoElement, ParentElement}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::theme; #[derive(Element, Clone)] pub struct Details { diff --git a/crates/storybook/src/ui/element/icon.rs b/crates/ui/src/elements/icon.rs similarity index 95% rename from crates/storybook/src/ui/element/icon.rs rename to crates/ui/src/elements/icon.rs index 5a9e8fb6cb7769f1e951d77a216d6d15646f8dad..dbe30cb4f941c9b4cfb2bf1d3386f768c35c43da 100644 --- a/crates/storybook/src/ui/element/icon.rs +++ b/crates/ui/src/elements/icon.rs @@ -1,8 +1,8 @@ -use crate::theme::theme; use gpui2::elements::svg; use gpui2::style::StyleHelpers; -use gpui2::IntoElement; -use gpui2::{Element, ViewContext}; +use gpui2::{Element, IntoElement, ViewContext}; + +use crate::theme; // Icon::Hash // icon(IconAsset::Hash).color(IconColor::Warning) diff --git a/crates/storybook/src/ui/element/icon_button.rs b/crates/ui/src/elements/icon_button.rs similarity index 88% rename from crates/storybook/src/ui/element/icon_button.rs rename to crates/ui/src/elements/icon_button.rs index f82979ab7c6fb871e56ccbf67f23fcde06c1ebbf..4270e0ca5daa3e1dd3655dd54a9e3f48028c7d1c 100644 --- a/crates/storybook/src/ui/element/icon_button.rs +++ b/crates/ui/src/elements/icon_button.rs @@ -1,9 +1,9 @@ -use crate::prelude::{ButtonVariant, InteractionState}; -use crate::theme::theme; -use gpui2::elements::svg; +use gpui2::elements::{div, svg}; use gpui2::style::{StyleHelpers, Styleable}; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::prelude::*; +use crate::theme; #[derive(Element)] pub struct IconButton { diff --git a/crates/storybook/src/ui/element/indicator.rs b/crates/ui/src/elements/indicator.rs similarity index 86% rename from crates/storybook/src/ui/element/indicator.rs rename to crates/ui/src/elements/indicator.rs index b905892f1a592cf460a8ab16e408f7aa76e69c1f..2ee40a57ac67de5740dc3db47be9c23949f941bd 100644 --- a/crates/storybook/src/ui/element/indicator.rs +++ b/crates/ui/src/elements/indicator.rs @@ -1,7 +1,8 @@ -use crate::theme::theme; +use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ViewContext}; +use gpui2::{Element, IntoElement, ViewContext}; + +use crate::theme; #[derive(Element)] pub struct Indicator { diff --git a/crates/storybook/src/ui/element/input.rs b/crates/ui/src/elements/input.rs similarity index 94% rename from crates/storybook/src/ui/element/input.rs rename to crates/ui/src/elements/input.rs index 310287797cfff956650080115059405372c69667..5a3da16fdd5caf24b1a83a8e1f973c29754fa111 100644 --- a/crates/storybook/src/ui/element/input.rs +++ b/crates/ui/src/elements/input.rs @@ -1,8 +1,9 @@ -use crate::prelude::{InputVariant, InteractionState}; -use crate::theme::theme; +use gpui2::elements::div; use gpui2::style::{StyleHelpers, Styleable}; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::prelude::*; +use crate::theme; #[derive(Element)] pub struct Input { diff --git a/crates/storybook/src/ui/element/label.rs b/crates/ui/src/elements/label.rs similarity index 92% rename from crates/storybook/src/ui/element/label.rs rename to crates/ui/src/elements/label.rs index bbbedd0b10879a2183f67e0b0f433695870d09d0..d3e94297ad891f2670a35c76aaf28ab69f0e5de1 100644 --- a/crates/storybook/src/ui/element/label.rs +++ b/crates/ui/src/elements/label.rs @@ -1,8 +1,8 @@ -use crate::theme::theme; use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{Element, ViewContext}; -use gpui2::{IntoElement, ParentElement}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::theme; #[derive(Default, PartialEq, Copy, Clone)] pub enum LabelColor { diff --git a/crates/storybook/src/ui/element/text_button.rs b/crates/ui/src/elements/text_button.rs similarity index 93% rename from crates/storybook/src/ui/element/text_button.rs rename to crates/ui/src/elements/text_button.rs index 24fd1eb5d35898511694a90dde9f101e3f88195b..851efdd4f691db8dbd5f836cf89cb487d9d71920 100644 --- a/crates/storybook/src/ui/element/text_button.rs +++ b/crates/ui/src/elements/text_button.rs @@ -1,8 +1,9 @@ -use crate::prelude::{ButtonVariant, InteractionState}; -use crate::theme::theme; +use gpui2::elements::div; use gpui2::style::{StyleHelpers, Styleable}; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::prelude::*; +use crate::theme; #[derive(Element)] pub struct TextButton { diff --git a/crates/storybook/src/ui/element/tool_divider.rs b/crates/ui/src/elements/tool_divider.rs similarity index 78% rename from crates/storybook/src/ui/element/tool_divider.rs rename to crates/ui/src/elements/tool_divider.rs index a923628defbd3d6622e61b17b660cc8bd986a6d6..2ef29b225f235031974a6fde0728ef95360282f4 100644 --- a/crates/storybook/src/ui/element/tool_divider.rs +++ b/crates/ui/src/elements/tool_divider.rs @@ -1,7 +1,8 @@ -use crate::theme::theme; +use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ViewContext}; +use gpui2::{Element, IntoElement, ViewContext}; + +use crate::theme; #[derive(Element)] pub struct ToolDivider {} diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..415cdcbaf383c95b4986c7f54378571b1494e239 --- /dev/null +++ b/crates/ui/src/lib.rs @@ -0,0 +1,14 @@ +#![allow(dead_code, unused_variables)] + +mod components; +mod element_ext; +mod elements; +mod modules; +pub mod prelude; +mod theme; + +pub use components::*; +pub use element_ext::*; +pub use elements::*; +pub use modules::*; +pub use theme::*; diff --git a/crates/storybook/src/ui/module.rs b/crates/ui/src/modules.rs similarity index 100% rename from crates/storybook/src/ui/module.rs rename to crates/ui/src/modules.rs diff --git a/crates/storybook/src/ui/module/chat_panel.rs b/crates/ui/src/modules/chat_panel.rs similarity index 91% rename from crates/storybook/src/ui/module/chat_panel.rs rename to crates/ui/src/modules/chat_panel.rs index de4428826b9074f643326fbc0e643194338f6be0..77c5b2ef20ba8a2de7cc47fead7d715b1b055706 100644 --- a/crates/storybook/src/ui/module/chat_panel.rs +++ b/crates/ui/src/modules/chat_panel.rs @@ -1,11 +1,11 @@ use std::marker::PhantomData; -use crate::theme::theme; -use crate::ui::icon_button; +use gpui2::elements::div; use gpui2::elements::div::ScrollState; use gpui2::style::StyleHelpers; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::{icon_button, theme}; #[derive(Element)] pub struct ChatPanel { diff --git a/crates/storybook/src/ui/module/project_panel.rs b/crates/ui/src/modules/project_panel.rs similarity index 93% rename from crates/storybook/src/ui/module/project_panel.rs rename to crates/ui/src/modules/project_panel.rs index 5a9608d3e3db7bdfa923b1a35e12f2127e390a3e..e17ff4c8edf425801731a37f60d1e05d65f61186 100644 --- a/crates/storybook/src/ui/module/project_panel.rs +++ b/crates/ui/src/modules/project_panel.rs @@ -1,16 +1,13 @@ -use crate::{ - prelude::{InteractionState, ToggleState}, - theme::theme, - ui::{details, input, label, list_item, IconAsset, LabelColor}, -}; -use gpui2::{ - elements::{div, div::ScrollState}, - style::StyleHelpers, - ParentElement, ViewContext, -}; -use gpui2::{Element, IntoElement}; use std::marker::PhantomData; +use gpui2::elements::div; +use gpui2::elements::div::ScrollState; +use gpui2::style::StyleHelpers; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::prelude::*; +use crate::{details, input, label, list_item, theme, IconAsset, LabelColor}; + #[derive(Element)] pub struct ProjectPanel { view_type: PhantomData, diff --git a/crates/storybook/src/ui/module/status_bar.rs b/crates/ui/src/modules/status_bar.rs similarity index 96% rename from crates/storybook/src/ui/module/status_bar.rs rename to crates/ui/src/modules/status_bar.rs index f34cfa88efd44f63692acd9c295714ddb6ed9e73..763a585176128fa1e832bfc35fa35e670aeb5e41 100644 --- a/crates/storybook/src/ui/module/status_bar.rs +++ b/crates/ui/src/modules/status_bar.rs @@ -1,10 +1,11 @@ use std::marker::PhantomData; -use crate::theme::{theme, Theme}; -use crate::ui::{icon_button, text_button, tool_divider}; +use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::theme::{theme, Theme}; +use crate::{icon_button, text_button, tool_divider}; #[derive(Default, PartialEq)] pub enum Tool { diff --git a/crates/storybook/src/ui/module/tab_bar.rs b/crates/ui/src/modules/tab_bar.rs similarity index 95% rename from crates/storybook/src/ui/module/tab_bar.rs rename to crates/ui/src/modules/tab_bar.rs index 9b96171f2f3591997aa31c408e50849382ce08b8..08caad310729f66b1f723366994a73c660b447a3 100644 --- a/crates/storybook/src/ui/module/tab_bar.rs +++ b/crates/ui/src/modules/tab_bar.rs @@ -1,12 +1,12 @@ use std::marker::PhantomData; -use crate::prelude::InteractionState; -use crate::theme::theme; -use crate::ui::{icon_button, tab}; +use gpui2::elements::div; use gpui2::elements::div::ScrollState; use gpui2::style::StyleHelpers; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::prelude::InteractionState; +use crate::{icon_button, tab, theme}; #[derive(Element)] pub struct TabBar { diff --git a/crates/storybook/src/ui/module/title_bar.rs b/crates/ui/src/modules/title_bar.rs similarity index 95% rename from crates/storybook/src/ui/module/title_bar.rs rename to crates/ui/src/modules/title_bar.rs index a41d56476f1dae379fc93176c3d8ebcfe8377f03..705d0d8866dc757f488cf52e5d162121a2abd0be 100644 --- a/crates/storybook/src/ui/module/title_bar.rs +++ b/crates/ui/src/modules/title_bar.rs @@ -1,11 +1,11 @@ use std::marker::PhantomData; -use crate::prelude::Shape; -use crate::theme::theme; -use crate::ui::{avatar, follow_group, icon_button, text_button, tool_divider}; +use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{elements::div, IntoElement}; -use gpui2::{Element, ParentElement, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement, ViewContext}; + +use crate::prelude::Shape; +use crate::{avatar, follow_group, icon_button, text_button, theme, tool_divider}; #[derive(Element)] pub struct TitleBar { diff --git a/crates/storybook/src/prelude.rs b/crates/ui/src/prelude.rs similarity index 100% rename from crates/storybook/src/prelude.rs rename to crates/ui/src/prelude.rs diff --git a/crates/storybook/src/theme.rs b/crates/ui/src/theme.rs similarity index 94% rename from crates/storybook/src/theme.rs rename to crates/ui/src/theme.rs index 0a86a61499f6d1d642d56b25cab3caaa0bb509ed..71235625d6dfc3cc7faa9c1f143688b5cb68fa20 100644 --- a/crates/storybook/src/theme.rs +++ b/crates/ui/src/theme.rs @@ -1,9 +1,12 @@ -use gpui2::{ - color::Hsla, element::Element, serde_json, AppContext, IntoElement, Vector2F, ViewContext, - WindowContext, -}; -use serde::{de::Visitor, Deserialize, Deserializer}; -use std::{collections::HashMap, fmt, marker::PhantomData}; +use std::collections::HashMap; +use std::fmt; +use std::marker::PhantomData; + +use gpui2::color::Hsla; +use gpui2::element::Element; +use gpui2::{serde_json, AppContext, IntoElement, Vector2F, ViewContext, WindowContext}; +use serde::de::Visitor; +use serde::{Deserialize, Deserializer}; use theme::ThemeSettings; #[derive(Deserialize, Clone, Default, Debug)] diff --git a/crates/storybook/src/ui/tracker.md b/crates/ui/tracker.md similarity index 100% rename from crates/storybook/src/ui/tracker.md rename to crates/ui/tracker.md From 1e6ac8caf2f312ea71a64cbc01c23a5d8344334f Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 21 Sep 2023 20:21:56 -0400 Subject: [PATCH 05/11] `theme::*` -> `crate::theme::*;` --- crates/ui/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index 415cdcbaf383c95b4986c7f54378571b1494e239..eac16aa776cebda31ea81e578a8d63fc6946ec90 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -11,4 +11,4 @@ pub use components::*; pub use element_ext::*; pub use elements::*; pub use modules::*; -pub use theme::*; +pub use crate::theme::*; From 8440ac3a54ce9743cc797f832474d92ff4b1fe48 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 21 Sep 2023 20:25:25 -0400 Subject: [PATCH 06/11] Fix fmt complaining about order --- crates/ui/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index eac16aa776cebda31ea81e578a8d63fc6946ec90..c7bab1e0b0786a9390dcf3612cc87dd80276675f 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -7,8 +7,8 @@ mod modules; pub mod prelude; mod theme; +pub use crate::theme::*; pub use components::*; pub use element_ext::*; pub use elements::*; pub use modules::*; -pub use crate::theme::*; From 66358f2900cf3c2ec2d42b169d81b012c8642e7e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 21 Sep 2023 22:41:53 -0400 Subject: [PATCH 07/11] Update storybook to support stories for individual components (#3010) This PR updates the `storybook` with support for adding stories for individual components. ### Motivation Right now we just have one story in the storybook that renders an entire `WorkspaceElement`. While iterating on the various UI components, it will be helpful to be able to create stories of those components just by themselves. This is especially true for components that have a number of different states, as we can render the components in all of the various states in a single layout. ### Explanation We achieve this by adding a simple CLI to the storybook. The `storybook` binary now accepts an optional `[STORY]` parameter that can be used to indicate which story should be loaded. If this parameter is not provided, it will load the workspace story as it currently does. Passing a story name will load the corresponding story, if it exists. For example: ``` cargo run -- elements/avatar ``` Screenshot 2023-09-21 at 10 29 52 PM ``` cargo run -- components/facepile ``` Screenshot 2023-09-21 at 10 30 07 PM Release Notes: - N/A --- Cargo.lock | 1 + crates/storybook/Cargo.toml | 3 +- crates/storybook/src/stories.rs | 2 + crates/storybook/src/stories/components.rs | 1 + .../src/stories/components/facepile.rs | 69 +++++++++++++++++ crates/storybook/src/stories/elements.rs | 1 + .../storybook/src/stories/elements/avatar.rs | 41 ++++++++++ crates/storybook/src/storybook.rs | 75 +++++++++++++++---- crates/storybook/src/workspace.rs | 6 +- 9 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 crates/storybook/src/stories.rs create mode 100644 crates/storybook/src/stories/components.rs create mode 100644 crates/storybook/src/stories/components/facepile.rs create mode 100644 crates/storybook/src/stories/elements.rs create mode 100644 crates/storybook/src/stories/elements/avatar.rs diff --git a/Cargo.lock b/Cargo.lock index 05b29aabf400e9843dc45041c956044c3bb7658c..9d6358edb496a7b01d6b00c951aaed6dba87bf0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7390,6 +7390,7 @@ name = "storybook" version = "0.1.0" dependencies = [ "anyhow", + "clap 3.2.25", "gpui2", "log", "rust-embed", diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index c1096a87babed86dd08e513ffbd2f629d6e44c90..882a1a1bdb14b98907728543bd0552fb50667441 100644 --- a/crates/storybook/Cargo.toml +++ b/crates/storybook/Cargo.toml @@ -9,8 +9,9 @@ name = "storybook" path = "src/storybook.rs" [dependencies] -gpui2 = { path = "../gpui2" } anyhow.workspace = true +clap = { version = "3.1", features = ["derive"] } +gpui2 = { path = "../gpui2" } log.workspace = true rust-embed.workspace = true serde.workspace = true diff --git a/crates/storybook/src/stories.rs b/crates/storybook/src/stories.rs new file mode 100644 index 0000000000000000000000000000000000000000..57674c3c70b8aaeeef62f5717faecb040f87174b --- /dev/null +++ b/crates/storybook/src/stories.rs @@ -0,0 +1,2 @@ +pub mod components; +pub mod elements; diff --git a/crates/storybook/src/stories/components.rs b/crates/storybook/src/stories/components.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e3d309419beecf44de6e396e04cebcd80cba716 --- /dev/null +++ b/crates/storybook/src/stories/components.rs @@ -0,0 +1 @@ +pub mod facepile; diff --git a/crates/storybook/src/stories/components/facepile.rs b/crates/storybook/src/stories/components/facepile.rs new file mode 100644 index 0000000000000000000000000000000000000000..6bde4539ed5aa4f1187d32b0879f025e89a37b69 --- /dev/null +++ b/crates/storybook/src/stories/components/facepile.rs @@ -0,0 +1,69 @@ +use gpui2::elements::div; +use gpui2::style::StyleHelpers; +use gpui2::{rgb, Element, Hsla, IntoElement, ParentElement, ViewContext}; +use ui::{avatar, theme}; +use ui::{facepile, prelude::*}; + +#[derive(Element, Default)] +pub struct FacepileStory {} + +impl FacepileStory { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + div() + .size_full() + .flex() + .flex_col() + .pt_2() + .px_4() + .font("Zed Mono Extended") + .fill(rgb::(0x282c34)) + .child( + div() + .text_2xl() + .text_color(rgb::(0xffffff)) + .child(std::any::type_name::()), + ) + .child( + div() + .flex() + .gap_3() + .child(facepile(vec![avatar( + "https://avatars.githubusercontent.com/u/1714999?v=4", + )])) + .child(facepile(vec![ + avatar("https://avatars.githubusercontent.com/u/1714999?v=4"), + avatar("https://avatars.githubusercontent.com/u/1714999?v=4"), + ])) + .child(facepile(vec![ + avatar("https://avatars.githubusercontent.com/u/1714999?v=4"), + avatar("https://avatars.githubusercontent.com/u/1714999?v=4"), + avatar("https://avatars.githubusercontent.com/u/1714999?v=4"), + ])), + ) + .child( + div() + .flex() + .gap_3() + .child(facepile(vec![avatar( + "https://avatars.githubusercontent.com/u/1714999?v=4", + ) + .shape(Shape::RoundedRectangle)])) + .child(facepile(vec![ + avatar("https://avatars.githubusercontent.com/u/1714999?v=4") + .shape(Shape::RoundedRectangle), + avatar("https://avatars.githubusercontent.com/u/1714999?v=4") + .shape(Shape::RoundedRectangle), + ])) + .child(facepile(vec![ + avatar("https://avatars.githubusercontent.com/u/1714999?v=4") + .shape(Shape::RoundedRectangle), + avatar("https://avatars.githubusercontent.com/u/1714999?v=4") + .shape(Shape::RoundedRectangle), + avatar("https://avatars.githubusercontent.com/u/1714999?v=4") + .shape(Shape::RoundedRectangle), + ])), + ) + } +} diff --git a/crates/storybook/src/stories/elements.rs b/crates/storybook/src/stories/elements.rs new file mode 100644 index 0000000000000000000000000000000000000000..124369cf9d0b26354048b6b4515d05e9fb533a07 --- /dev/null +++ b/crates/storybook/src/stories/elements.rs @@ -0,0 +1 @@ +pub mod avatar; diff --git a/crates/storybook/src/stories/elements/avatar.rs b/crates/storybook/src/stories/elements/avatar.rs new file mode 100644 index 0000000000000000000000000000000000000000..3239dbec9c0a8141ccc2cefa13151bf85f9a22e6 --- /dev/null +++ b/crates/storybook/src/stories/elements/avatar.rs @@ -0,0 +1,41 @@ +use gpui2::elements::div; +use gpui2::style::StyleHelpers; +use gpui2::{rgb, Element, Hsla, IntoElement, ParentElement, ViewContext}; +use ui::prelude::*; +use ui::{avatar, theme}; + +#[derive(Element, Default)] +pub struct AvatarStory {} + +impl AvatarStory { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + div() + .size_full() + .flex() + .flex_col() + .pt_2() + .px_4() + .font("Zed Mono Extended") + .fill(rgb::(0x282c34)) + .child( + div() + .text_2xl() + .text_color(rgb::(0xffffff)) + .child(std::any::type_name::()), + ) + .child( + div() + .flex() + .gap_3() + .child(avatar( + "https://avatars.githubusercontent.com/u/1714999?v=4", + )) + .child( + avatar("https://avatars.githubusercontent.com/u/1714999?v=4") + .shape(Shape::RoundedRectangle), + ), + ) + } +} diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs index 92d178fad2728cb1e5e3a5d44caaaf3a5b2347be..089ff33049e3acccc91a1127e778935c86523802 100644 --- a/crates/storybook/src/storybook.rs +++ b/crates/storybook/src/storybook.rs @@ -1,25 +1,66 @@ #![allow(dead_code, unused_variables)] +mod collab_panel; +mod stories; +mod workspace; + +use std::str::FromStr; + use ::theme as legacy_theme; -use gpui2::{serde_json, vec2f, view, Element, RectF, ViewContext, WindowBounds}; +use clap::Parser; +use gpui2::{serde_json, vec2f, view, Element, IntoElement, RectF, ViewContext, WindowBounds}; use legacy_theme::ThemeSettings; use log::LevelFilter; use settings::{default_settings, SettingsStore}; use simplelog::SimpleLogger; +use stories::components::facepile::FacepileStory; +use stories::elements::avatar::AvatarStory; use ui::{ElementExt, Theme}; -mod collab_panel; -mod workspace; - gpui2::actions! { storybook, [ToggleInspector] } +#[derive(Debug, Clone, Copy)] +enum Story { + Element(ElementStory), + Component(ComponentStory), +} + +impl FromStr for Story { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + match s.to_ascii_lowercase().as_str() { + "elements/avatar" => Ok(Self::Element(ElementStory::Avatar)), + "components/facepile" => Ok(Self::Component(ComponentStory::Facepile)), + _ => Err(anyhow!("story not found for '{s}'")), + } + } +} + +#[derive(Debug, Clone, Copy)] +enum ElementStory { + Avatar, +} + +#[derive(Debug, Clone, Copy)] +enum ComponentStory { + Facepile, +} + +#[derive(Parser)] +struct Args { + story: Option, +} + fn main() { SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - gpui2::App::new(Assets).unwrap().run(|cx| { + let args = Args::parse(); + + gpui2::App::new(Assets).unwrap().run(move |cx| { let mut store = SettingsStore::default(); store .set_default_settings(default_settings().as_ref(), cx) @@ -34,19 +75,27 @@ fn main() { center: true, ..Default::default() }, - |cx| { - view(|cx| { - // cx.enable_inspector(); - storybook(&mut ViewContext::new(cx)) - }) + |cx| match args.story { + Some(Story::Element(ElementStory::Avatar)) => { + view(|cx| render_story(&mut ViewContext::new(cx), AvatarStory::default())) + } + Some(Story::Component(ComponentStory::Facepile)) => { + view(|cx| render_story(&mut ViewContext::new(cx), FacepileStory::default())) + } + None => { + view(|cx| render_story(&mut ViewContext::new(cx), WorkspaceElement::default())) + } }, ); cx.platform().activate(true); }); } -fn storybook(cx: &mut ViewContext) -> impl Element { - workspace().themed(current_theme(cx)) +fn render_story>( + cx: &mut ViewContext, + story: S, +) -> impl Element { + story.into_element().themed(current_theme(cx)) } // Nathan: During the transition to gpui2, we will include the base theme on the legacy Theme struct. @@ -69,7 +118,7 @@ fn current_theme(cx: &mut ViewContext) -> Theme { use anyhow::{anyhow, Result}; use gpui2::AssetSource; use rust_embed::RustEmbed; -use workspace::workspace; +use workspace::WorkspaceElement; #[derive(RustEmbed)] #[folder = "../../assets"] diff --git a/crates/storybook/src/workspace.rs b/crates/storybook/src/workspace.rs index 95152ec8328f26e0749215a7fe55694f7247da2f..58c5fed62dad748f98f225d0acb6d1d77b3090cd 100644 --- a/crates/storybook/src/workspace.rs +++ b/crates/storybook/src/workspace.rs @@ -6,16 +6,12 @@ use gpui2::{ use ui::{chat_panel, project_panel, status_bar, tab_bar, theme, title_bar}; #[derive(Element, Default)] -struct WorkspaceElement { +pub struct WorkspaceElement { left_scroll_state: ScrollState, right_scroll_state: ScrollState, tab_bar_scroll_state: ScrollState, } -pub fn workspace() -> impl Element { - WorkspaceElement::default() -} - impl WorkspaceElement { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); From 5083ab7694d7bc99ddd9c41f76c6c2313d9d6cac Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 21 Sep 2023 23:42:18 -0400 Subject: [PATCH 08/11] Add `TrafficLights` component (#3011) This PR adds a `TrafficLights` component for GPUI2. Screenshot 2023-09-21 at 11 32 10 PM Release Notes: - N/A --- crates/storybook/src/stories/components.rs | 1 + .../src/stories/components/traffic_lights.rs | 29 +++++++++++++++++ crates/storybook/src/storybook.rs | 6 ++++ crates/ui/src/components.rs | 2 ++ crates/ui/src/components/traffic_lights.rs | 30 ++++++++++++++++++ crates/ui/src/modules/title_bar.rs | 31 ++----------------- 6 files changed, 70 insertions(+), 29 deletions(-) create mode 100644 crates/storybook/src/stories/components/traffic_lights.rs create mode 100644 crates/ui/src/components/traffic_lights.rs diff --git a/crates/storybook/src/stories/components.rs b/crates/storybook/src/stories/components.rs index 5e3d309419beecf44de6e396e04cebcd80cba716..89ee5a92eae848dbd31721f378472cd2615f942a 100644 --- a/crates/storybook/src/stories/components.rs +++ b/crates/storybook/src/stories/components.rs @@ -1 +1,2 @@ pub mod facepile; +pub mod traffic_lights; diff --git a/crates/storybook/src/stories/components/traffic_lights.rs b/crates/storybook/src/stories/components/traffic_lights.rs new file mode 100644 index 0000000000000000000000000000000000000000..252caf99f64b5433ac33f9c2b45c551cf1473409 --- /dev/null +++ b/crates/storybook/src/stories/components/traffic_lights.rs @@ -0,0 +1,29 @@ +use gpui2::elements::div; +use gpui2::style::StyleHelpers; +use gpui2::{rgb, Element, Hsla, IntoElement, ParentElement, ViewContext}; +use ui::{theme, traffic_lights}; + +#[derive(Element, Default)] +pub struct TrafficLightsStory {} + +impl TrafficLightsStory { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + div() + .size_full() + .flex() + .flex_col() + .pt_2() + .px_4() + .font("Zed Mono Extended") + .fill(rgb::(0x282c34)) + .child( + div() + .text_2xl() + .text_color(rgb::(0xffffff)) + .child(std::any::type_name::()), + ) + .child(traffic_lights()) + } +} diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs index 089ff33049e3acccc91a1127e778935c86523802..b72c4a5681e99b48e326673464f9510e24bbe531 100644 --- a/crates/storybook/src/storybook.rs +++ b/crates/storybook/src/storybook.rs @@ -14,6 +14,7 @@ use log::LevelFilter; use settings::{default_settings, SettingsStore}; use simplelog::SimpleLogger; use stories::components::facepile::FacepileStory; +use stories::components::traffic_lights::TrafficLightsStory; use stories::elements::avatar::AvatarStory; use ui::{ElementExt, Theme}; @@ -35,6 +36,7 @@ impl FromStr for Story { match s.to_ascii_lowercase().as_str() { "elements/avatar" => Ok(Self::Element(ElementStory::Avatar)), "components/facepile" => Ok(Self::Component(ComponentStory::Facepile)), + "components/traffic_lights" => Ok(Self::Component(ComponentStory::TrafficLights)), _ => Err(anyhow!("story not found for '{s}'")), } } @@ -48,6 +50,7 @@ enum ElementStory { #[derive(Debug, Clone, Copy)] enum ComponentStory { Facepile, + TrafficLights, } #[derive(Parser)] @@ -82,6 +85,9 @@ fn main() { Some(Story::Component(ComponentStory::Facepile)) => { view(|cx| render_story(&mut ViewContext::new(cx), FacepileStory::default())) } + Some(Story::Component(ComponentStory::TrafficLights)) => view(|cx| { + render_story(&mut ViewContext::new(cx), TrafficLightsStory::default()) + }), None => { view(|cx| render_story(&mut ViewContext::new(cx), WorkspaceElement::default())) } diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs index a82d28eb8cb2539cef985c12a59ba712b97699ae..b400a491e4434883ab9d2e65c8a4dc309c9cdca3 100644 --- a/crates/ui/src/components.rs +++ b/crates/ui/src/components.rs @@ -2,11 +2,13 @@ mod facepile; mod follow_group; mod list_item; mod tab; +mod traffic_lights; pub use facepile::*; pub use follow_group::*; pub use list_item::*; pub use tab::*; +pub use traffic_lights::*; use std::marker::PhantomData; use std::rc::Rc; diff --git a/crates/ui/src/components/traffic_lights.rs b/crates/ui/src/components/traffic_lights.rs new file mode 100644 index 0000000000000000000000000000000000000000..128af9976f71c47684731838e9965b3d727ed1ee --- /dev/null +++ b/crates/ui/src/components/traffic_lights.rs @@ -0,0 +1,30 @@ +use gpui2::elements::div; +use gpui2::style::StyleHelpers; +use gpui2::{Element, Hsla, IntoElement, ParentElement, ViewContext}; + +use crate::theme; + +#[derive(Element)] +pub struct TrafficLights {} + +pub fn traffic_lights() -> TrafficLights { + TrafficLights {} +} + +impl TrafficLights { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + div() + .flex() + .items_center() + .gap_2() + .child(traffic_light(theme.lowest.negative.default.foreground)) + .child(traffic_light(theme.lowest.warning.default.foreground)) + .child(traffic_light(theme.lowest.positive.default.foreground)) + } +} + +fn traffic_light>(fill: C) -> div::Div { + div().w_3().h_3().rounded_full().fill(fill.into()) +} diff --git a/crates/ui/src/modules/title_bar.rs b/crates/ui/src/modules/title_bar.rs index 705d0d8866dc757f488cf52e5d162121a2abd0be..82c5c0234b8f3f449bed331a84f88de0fa16fcc0 100644 --- a/crates/ui/src/modules/title_bar.rs +++ b/crates/ui/src/modules/title_bar.rs @@ -5,7 +5,7 @@ use gpui2::style::StyleHelpers; use gpui2::{Element, IntoElement, ParentElement, ViewContext}; use crate::prelude::Shape; -use crate::{avatar, follow_group, icon_button, text_button, theme, tool_divider}; +use crate::{avatar, follow_group, icon_button, text_button, theme, tool_divider, traffic_lights}; #[derive(Element)] pub struct TitleBar { @@ -40,34 +40,7 @@ impl TitleBar { .h_full() .gap_4() .px_2() - // === Traffic Lights === // - .child( - div() - .flex() - .items_center() - .gap_2() - .child( - div() - .w_3() - .h_3() - .rounded_full() - .fill(theme.lowest.positive.default.foreground), - ) - .child( - div() - .w_3() - .h_3() - .rounded_full() - .fill(theme.lowest.warning.default.foreground), - ) - .child( - div() - .w_3() - .h_3() - .rounded_full() - .fill(theme.lowest.negative.default.foreground), - ), - ) + .child(traffic_lights()) // === Project Info === // .child( div() From f54634aeb234c0881f34682c04db1054b1dcb38b Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 21 Sep 2023 23:46:06 -0400 Subject: [PATCH 09/11] Bring UI crate up to date --- crates/ui/doc/elevation.md | 57 ++++++ crates/ui/src/components.rs | 4 + crates/ui/src/components/list_item.rs | 72 ++++--- .../ui/src/components/list_section_header.rs | 88 +++++++++ crates/ui/src/components/palette_item.rs | 63 +++++++ crates/ui/src/elements/icon.rs | 45 ++--- crates/ui/src/elements/label.rs | 34 +++- crates/ui/src/lib.rs | 7 + crates/ui/src/modules.rs | 14 +- crates/ui/src/modules/list.rs | 64 +++++++ crates/ui/src/modules/palette.rs | 124 ++++++++++++ crates/ui/src/modules/project_panel.rs | 94 ---------- crates/ui/src/prelude.rs | 15 ++ crates/ui/src/static_data.rs | 166 ++++++++++++++++ crates/ui/src/templates.rs | 17 ++ .../src/{modules => templates}/chat_panel.rs | 8 +- crates/ui/src/templates/collab_panel.rs | 177 ++++++++++++++++++ crates/ui/src/templates/command_palette.rs | 31 +++ crates/ui/src/templates/project_panel.rs | 62 ++++++ .../src/{modules => templates}/status_bar.rs | 7 +- .../ui/src/{modules => templates}/tab_bar.rs | 10 +- .../src/{modules => templates}/title_bar.rs | 0 crates/ui/src/templates/workspace.rs | 80 ++++++++ crates/ui/src/tokens.rs | 18 ++ 24 files changed, 1090 insertions(+), 167 deletions(-) create mode 100644 crates/ui/doc/elevation.md create mode 100644 crates/ui/src/components/list_section_header.rs create mode 100644 crates/ui/src/components/palette_item.rs create mode 100644 crates/ui/src/modules/list.rs create mode 100644 crates/ui/src/modules/palette.rs delete mode 100644 crates/ui/src/modules/project_panel.rs create mode 100644 crates/ui/src/static_data.rs create mode 100644 crates/ui/src/templates.rs rename crates/ui/src/{modules => templates}/chat_panel.rs (92%) create mode 100644 crates/ui/src/templates/collab_panel.rs create mode 100644 crates/ui/src/templates/command_palette.rs create mode 100644 crates/ui/src/templates/project_panel.rs rename crates/ui/src/{modules => templates}/status_bar.rs (97%) rename crates/ui/src/{modules => templates}/tab_bar.rs (95%) rename crates/ui/src/{modules => templates}/title_bar.rs (100%) create mode 100644 crates/ui/src/templates/workspace.rs create mode 100644 crates/ui/src/tokens.rs diff --git a/crates/ui/doc/elevation.md b/crates/ui/doc/elevation.md new file mode 100644 index 0000000000000000000000000000000000000000..bd34de3396dea1f1042bb267f8a23abe6a45441f --- /dev/null +++ b/crates/ui/doc/elevation.md @@ -0,0 +1,57 @@ +# Elevation + +Elevation in Zed applies to all surfaces and components. Elevation is categorized into levels. + +Elevation accomplishes the following: +- Allows surfaces to move in front of or behind others, such as content scrolling beneath app top bars. +- Reflects spatial relationships, for instance, how a floating action button’s shadow intimates its disconnection from a collection of cards. +- Directs attention to structures at the highest elevation, like a temporary dialog arising in front of other surfaces. + +Elevations are the initial elevation values assigned to components by default. + +Components may transition to a higher elevation in some cases, like user interations. + +On such occasions, components transition to predetermined dynamic elevation offsets. These are the typical elevations to which components move when they are not at rest. + +## Understanding Elevation + +Elevation can be thought of as the physical closeness of an element to the user. Elements with lower elevations are physically further away from the user on the z-axis and appear to be underneath elements with higher elevations. + +Material Design 3 has a some great visualizations of elevation that may be helpful to understanding the mental modal of elevation. [Material Design – Elevation](https://m3.material.io/styles/elevation/overview) + +## Elevation Levels + +Zed integrates six unique elevation levels in its design system. The elevation of a surface is expressed as a whole number ranging from 0 to 5, both numbers inclusive. A component’s elevation is ascertained by combining the component’s resting elevation with any dynamic elevation offsets. + +The levels are detailed as follows: + +0. App Background +1. UI Surface +2. Elevated Elements +3. Wash +4. Focused Element +5. Dragged Element + +### 0. App Background + +The app background constitutes the lowest elevation layer, appearing behind all other surfaces and components. It is predominantly used for the background color of the app. + +### 1. UI Surface + +The UI Surface is the standard elevation for components and is placed above the app background. It is generally used for the background color of the app bar, card, and sheet. + +### 2. Elevated Elements + +Elevated elements appear above the UI surface layer surfaces and components. Elevated elements are predominantly used for creating popovers, context menus, and tooltips. + +### 3. Wash + +Wash denotes a distinct elevation reserved to isolate app UI layers from high elevation components such as modals, notifications, and overlaid panels. The wash may not consistently be visible when these components are active. This layer is often referred to as a scrim or overlay and the background color of the wash is typically deployed in its design. + +### 4. Focused Element + +Focused elements obtain a higher elevation above surfaces and components at wash elevation. They are often used for modals, notifications, and overlaid panels and indicate that they are the sole element the user is interacting with at the moment. + +### 5. Dragged Element + +Dragged elements gain the highest elevation, thus appearing above surfaces and components at the elevation of focused elements. These are typically used for elements that are being dragged, following the cursor diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs index a82d28eb8cb2539cef985c12a59ba712b97699ae..1d513db7d29e4415923b59acb997f735b5ea91f1 100644 --- a/crates/ui/src/components.rs +++ b/crates/ui/src/components.rs @@ -1,11 +1,15 @@ mod facepile; mod follow_group; mod list_item; +mod list_section_header; +mod palette_item; mod tab; pub use facepile::*; pub use follow_group::*; pub use list_item::*; +pub use list_section_header::*; +pub use palette_item::*; pub use tab::*; use std::marker::PhantomData; diff --git a/crates/ui/src/components/list_item.rs b/crates/ui/src/components/list_item.rs index 868b58e449f778d8356a1823c91f3312328ab4aa..19e5b26abe4c4d21103f771b1b2b26403c83238f 100644 --- a/crates/ui/src/components/list_item.rs +++ b/crates/ui/src/components/list_item.rs @@ -1,17 +1,18 @@ -use gpui2::elements::div; -use gpui2::geometry::rems; +use crate::prelude::{DisclosureControlVisibility, InteractionState, ToggleState}; +use crate::theme::theme; +use crate::tokens::token; +use crate::{icon, IconAsset, Label}; use gpui2::style::{StyleHelpers, Styleable}; -use gpui2::{Element, IntoElement, ParentElement, ViewContext}; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; -use crate::prelude::*; -use crate::{icon, theme, IconAsset, Label}; - -#[derive(Element)] +#[derive(Element, Clone)] pub struct ListItem { label: Label, left_icon: Option, indent_level: u32, state: InteractionState, + disclosure_control_style: DisclosureControlVisibility, toggle: Option, } @@ -20,6 +21,7 @@ pub fn list_item(label: Label) -> ListItem { label, indent_level: 0, left_icon: None, + disclosure_control_style: DisclosureControlVisibility::default(), state: InteractionState::default(), toggle: None, } @@ -46,8 +48,30 @@ impl ListItem { self } + pub fn disclosure_control_style( + mut self, + disclosure_control_style: DisclosureControlVisibility, + ) -> Self { + self.disclosure_control_style = disclosure_control_style; + self + } + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); + let token = token(); + let mut disclosure_control = match self.toggle { + Some(ToggleState::NotToggled) => Some(div().child(icon(IconAsset::ChevronRight))), + Some(ToggleState::Toggled) => Some(div().child(icon(IconAsset::ChevronDown))), + None => Some(div()), + }; + + match self.disclosure_control_style { + DisclosureControlVisibility::OnHover => { + disclosure_control = + disclosure_control.map(|c| div().absolute().neg_left_5().child(c)); + } + DisclosureControlVisibility::Always => {} + } div() .fill(theme.middle.base.default.background) @@ -56,31 +80,31 @@ impl ListItem { .active() .fill(theme.middle.base.pressed.background) .relative() + .py_1() .child( div() - .h_7() + .h_6() .px_2() // .ml(rems(0.75 * self.indent_level as f32)) .children((0..self.indent_level).map(|_| { - div().w(rems(0.75)).h_full().flex().justify_center().child( - div() - .w_px() - .h_full() - .fill(theme.middle.base.default.border) - .hover() - .fill(theme.middle.warning.default.border) - .active() - .fill(theme.middle.negative.default.border), - ) + div() + .w(token.list_indent_depth) + .h_full() + .flex() + .justify_center() + .child( + div() + .ml_px() + .w_px() + .h_full() + .fill(theme.middle.base.default.border), + ) })) .flex() - .gap_2() + .gap_1() .items_center() - .children(match self.toggle { - Some(ToggleState::NotToggled) => Some(icon(IconAsset::ChevronRight)), - Some(ToggleState::Toggled) => Some(icon(IconAsset::ChevronDown)), - None => None, - }) + .relative() + .children(disclosure_control) .children(self.left_icon.map(|i| icon(i))) .child(self.label.clone()), ) diff --git a/crates/ui/src/components/list_section_header.rs b/crates/ui/src/components/list_section_header.rs new file mode 100644 index 0000000000000000000000000000000000000000..76a0d4cee7612fb291b325f08dd6a8600d13fbbe --- /dev/null +++ b/crates/ui/src/components/list_section_header.rs @@ -0,0 +1,88 @@ +use crate::prelude::{InteractionState, ToggleState}; +use crate::theme::theme; +use crate::tokens::token; +use crate::{icon, label, IconAsset, LabelColor, LabelSize}; +use gpui2::style::{StyleHelpers, Styleable}; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; + +#[derive(Element, Clone, Copy)] +pub struct ListSectionHeader { + label: &'static str, + left_icon: Option, + state: InteractionState, + toggle: Option, +} + +pub fn list_section_header(label: &'static str) -> ListSectionHeader { + ListSectionHeader { + label, + left_icon: None, + state: InteractionState::default(), + toggle: None, + } +} + +impl ListSectionHeader { + pub fn set_toggle(mut self, toggle: ToggleState) -> Self { + self.toggle = Some(toggle); + self + } + + pub fn left_icon(mut self, left_icon: Option) -> Self { + self.left_icon = left_icon; + self + } + + pub fn state(mut self, state: InteractionState) -> Self { + self.state = state; + self + } + + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + let token = token(); + + let disclosure_control = match self.toggle { + Some(ToggleState::NotToggled) => Some(div().child(icon(IconAsset::ChevronRight))), + Some(ToggleState::Toggled) => Some(div().child(icon(IconAsset::ChevronDown))), + None => Some(div()), + }; + + div() + .flex() + .flex_1() + .w_full() + .fill(theme.middle.base.default.background) + .hover() + .fill(theme.middle.base.hovered.background) + .active() + .fill(theme.middle.base.pressed.background) + .relative() + .py_1() + .child( + div() + .h_6() + .px_2() + .flex() + .flex_1() + .w_full() + .gap_1() + .items_center() + .justify_between() + .child( + div() + .flex() + .gap_1() + .items_center() + .children(self.left_icon.map(|i| icon(i))) + .child( + label(self.label.clone()) + .color(LabelColor::Muted) + .size(LabelSize::Small), + ), + ) + .children(disclosure_control), + ) + } +} diff --git a/crates/ui/src/components/palette_item.rs b/crates/ui/src/components/palette_item.rs new file mode 100644 index 0000000000000000000000000000000000000000..9e7883d700252e621eabf569f91261ffc58d6824 --- /dev/null +++ b/crates/ui/src/components/palette_item.rs @@ -0,0 +1,63 @@ +use crate::theme::theme; +use crate::{label, LabelColor, LabelSize}; +use gpui2::elements::div; +use gpui2::style::StyleHelpers; +use gpui2::{Element, IntoElement}; +use gpui2::{ParentElement, ViewContext}; + +#[derive(Element)] +pub struct PaletteItem { + pub label: &'static str, + pub keybinding: Option<&'static str>, +} + +pub fn palette_item(label: &'static str, keybinding: Option<&'static str>) -> PaletteItem { + PaletteItem { label, keybinding } +} + +impl PaletteItem { + pub fn label(mut self, label: &'static str) -> Self { + self.label = label; + self + } + + pub fn keybinding(mut self, keybinding: Option<&'static str>) -> Self { + self.keybinding = keybinding; + self + } + + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + let keybinding_label = match self.keybinding { + Some(keybind) => label(keybind) + .color(LabelColor::Muted) + .size(LabelSize::Small), + None => label(""), + }; + + div() + .flex() + .flex_row() + .grow() + .justify_between() + .child(label(self.label)) + .child( + self.keybinding + .map(|_| { + div() + .flex() + .items_center() + .justify_center() + .px_1() + .py_0() + .my_0p5() + .rounded_md() + .text_sm() + .fill(theme.lowest.on.default.background) + .child(keybinding_label) + }) + .unwrap_or_else(|| div()), + ) + } +} diff --git a/crates/ui/src/elements/icon.rs b/crates/ui/src/elements/icon.rs index dbe30cb4f941c9b4cfb2bf1d3386f768c35c43da..08b8e3e7c5733589537f46623c257d0801de9852 100644 --- a/crates/ui/src/elements/icon.rs +++ b/crates/ui/src/elements/icon.rs @@ -1,29 +1,30 @@ +use crate::theme::theme; use gpui2::elements::svg; use gpui2::style::StyleHelpers; -use gpui2::{Element, IntoElement, ViewContext}; - -use crate::theme; - -// Icon::Hash -// icon(IconAsset::Hash).color(IconColor::Warning) -// Icon::new(IconAsset::Hash).color(IconColor::Warning) +use gpui2::IntoElement; +use gpui2::{Element, ViewContext}; #[derive(Default, PartialEq, Copy, Clone)] pub enum IconAsset { Ai, ArrowLeft, ArrowRight, - #[default] ArrowUpRight, Bolt, - Hash, - File, - Folder, - FolderOpen, ChevronDown, - ChevronUp, ChevronLeft, ChevronRight, + ChevronUp, + #[default] + File, + FileDoc, + FileGit, + FileLock, + FileRust, + FileToml, + Folder, + FolderOpen, + Hash, } impl IconAsset { @@ -34,14 +35,19 @@ impl IconAsset { IconAsset::ArrowRight => "icons/arrow_right.svg", IconAsset::ArrowUpRight => "icons/arrow_up_right.svg", IconAsset::Bolt => "icons/bolt.svg", - IconAsset::Hash => "icons/hash.svg", IconAsset::ChevronDown => "icons/chevron_down.svg", - IconAsset::ChevronUp => "icons/chevron_up.svg", IconAsset::ChevronLeft => "icons/chevron_left.svg", IconAsset::ChevronRight => "icons/chevron_right.svg", + IconAsset::ChevronUp => "icons/chevron_up.svg", IconAsset::File => "icons/file_icons/file.svg", + IconAsset::FileDoc => "icons/file_icons/book.svg", + IconAsset::FileGit => "icons/file_icons/git.svg", + IconAsset::FileLock => "icons/file_icons/lock.svg", + IconAsset::FileRust => "icons/file_icons/rust.svg", + IconAsset::FileToml => "icons/file_icons/toml.svg", IconAsset::Folder => "icons/file_icons/folder.svg", IconAsset::FolderOpen => "icons/file_icons/folder_open.svg", + IconAsset::Hash => "icons/hash.svg", } } } @@ -55,19 +61,14 @@ pub fn icon(asset: IconAsset) -> Icon { Icon { asset } } -// impl Icon { -// pub fn new(asset: IconAsset) -> Icon { -// Icon { asset } -// } -// } - impl Icon { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); svg() + .flex_none() .path(self.asset.path()) .size_4() - .fill(theme.lowest.base.default.foreground) + .fill(theme.lowest.variant.default.foreground) } } diff --git a/crates/ui/src/elements/label.rs b/crates/ui/src/elements/label.rs index d3e94297ad891f2670a35c76aaf28ab69f0e5de1..90e6b525ebb2fc1c675a07bd73325fb6504efa08 100644 --- a/crates/ui/src/elements/label.rs +++ b/crates/ui/src/elements/label.rs @@ -1,29 +1,40 @@ +use crate::theme::theme; use gpui2::elements::div; use gpui2::style::StyleHelpers; -use gpui2::{Element, IntoElement, ParentElement, ViewContext}; - -use crate::theme; +use gpui2::{Element, ViewContext}; +use gpui2::{IntoElement, ParentElement}; #[derive(Default, PartialEq, Copy, Clone)] pub enum LabelColor { #[default] Default, + Muted, Created, Modified, Deleted, Hidden, + Placeholder, +} + +#[derive(Default, PartialEq, Copy, Clone)] +pub enum LabelSize { + #[default] + Default, + Small, } #[derive(Element, Clone)] pub struct Label { label: &'static str, color: LabelColor, + size: LabelSize, } pub fn label(label: &'static str) -> Label { Label { label, color: LabelColor::Default, + size: LabelSize::Default, } } @@ -33,17 +44,32 @@ impl Label { self } + pub fn size(mut self, size: LabelSize) -> Self { + self.size = size; + self + } + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); let color = match self.color { LabelColor::Default => theme.lowest.base.default.foreground, + LabelColor::Muted => theme.lowest.variant.default.foreground, LabelColor::Created => theme.lowest.positive.default.foreground, LabelColor::Modified => theme.lowest.warning.default.foreground, LabelColor::Deleted => theme.lowest.negative.default.foreground, LabelColor::Hidden => theme.lowest.variant.default.foreground, + LabelColor::Placeholder => theme.lowest.base.disabled.foreground, }; - div().text_sm().text_color(color).child(self.label.clone()) + let mut div = div(); + + if self.size == LabelSize::Small { + div = div.text_xs(); + } else { + div = div.text_sm(); + } + + div.text_color(color).child(self.label.clone()) } } diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index c7bab1e0b0786a9390dcf3612cc87dd80276675f..0bcd96164393716fe60cdba409a012988c630e03 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -5,10 +5,17 @@ mod element_ext; mod elements; mod modules; pub mod prelude; +mod static_data; +mod templates; mod theme; +mod tokens; pub use crate::theme::*; pub use components::*; pub use element_ext::*; pub use elements::*; pub use modules::*; +pub use prelude::*; +pub use static_data::*; +pub use templates::*; +pub use tokens::*; diff --git a/crates/ui/src/modules.rs b/crates/ui/src/modules.rs index a1cead7df9df397602906cdfca7750508532522a..d29e31072b5662ad574fb31d58f9b9bb224a4b2a 100644 --- a/crates/ui/src/modules.rs +++ b/crates/ui/src/modules.rs @@ -1,11 +1,5 @@ -mod chat_panel; -mod project_panel; -mod status_bar; -mod tab_bar; -mod title_bar; +mod list; +mod palette; -pub use chat_panel::*; -pub use project_panel::*; -pub use status_bar::*; -pub use tab_bar::*; -pub use title_bar::*; +pub use list::*; +pub use palette::*; diff --git a/crates/ui/src/modules/list.rs b/crates/ui/src/modules/list.rs new file mode 100644 index 0000000000000000000000000000000000000000..a7fb06132faf861931b4cba3c18a3ddd7d36d573 --- /dev/null +++ b/crates/ui/src/modules/list.rs @@ -0,0 +1,64 @@ +use crate::theme::theme; +use crate::tokens::token; +use crate::{icon, label, prelude::*, IconAsset, LabelColor, ListItem, ListSectionHeader}; +use gpui2::style::StyleHelpers; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; + +#[derive(Element)] +pub struct List { + header: Option, + items: Vec, + empty_message: &'static str, + toggle: Option, + // footer: Option, +} + +pub fn list(items: Vec) -> List { + List { + header: None, + items, + empty_message: "No items", + toggle: None, + } +} + +impl List { + pub fn header(mut self, header: ListSectionHeader) -> Self { + self.header = Some(header); + self + } + + pub fn empty_message(mut self, empty_message: &'static str) -> Self { + self.empty_message = empty_message; + self + } + + pub fn set_toggle(mut self, toggle: ToggleState) -> Self { + self.toggle = Some(toggle); + self + } + + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + let token = token(); + + let disclosure_control = match self.toggle { + Some(ToggleState::NotToggled) => Some(icon(IconAsset::ChevronRight)), + Some(ToggleState::Toggled) => Some(icon(IconAsset::ChevronDown)), + None => None, + }; + + div() + .py_1() + .flex() + .flex_col() + .children(self.header.map(|h| h)) + .children( + self.items + .is_empty() + .then(|| label(self.empty_message).color(LabelColor::Muted)), + ) + .children(self.items.iter().cloned()) + } +} diff --git a/crates/ui/src/modules/palette.rs b/crates/ui/src/modules/palette.rs new file mode 100644 index 0000000000000000000000000000000000000000..a540d29169b0fa65d8fe7d9e7fba2f048efb2ee3 --- /dev/null +++ b/crates/ui/src/modules/palette.rs @@ -0,0 +1,124 @@ +use std::marker::PhantomData; + +use crate::prelude::OrderMethod; +use crate::theme::theme; +use crate::{label, palette_item, LabelColor, PaletteItem}; +use gpui2::elements::div::ScrollState; +use gpui2::style::{StyleHelpers, Styleable}; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; + +#[derive(Element)] +pub struct Palette { + view_type: PhantomData, + scroll_state: ScrollState, + input_placeholder: &'static str, + empty_string: &'static str, + items: Vec, + default_order: OrderMethod, +} + +pub fn palette(scroll_state: ScrollState) -> Palette { + Palette { + view_type: PhantomData, + scroll_state, + input_placeholder: "Find something...", + empty_string: "No items found.", + items: vec![], + default_order: OrderMethod::default(), + } +} + +impl Palette { + pub fn items(mut self, mut items: Vec) -> Self { + items.sort_by_key(|item| item.label); + self.items = items; + self + } + + pub fn placeholder(mut self, input_placeholder: &'static str) -> Self { + self.input_placeholder = input_placeholder; + self + } + + pub fn empty_string(mut self, empty_string: &'static str) -> Self { + self.empty_string = empty_string; + self + } + + // TODO: Hook up sort order + pub fn default_order(mut self, default_order: OrderMethod) -> Self { + self.default_order = default_order; + self + } + + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + div() + .w_96() + .rounded_lg() + .fill(theme.lowest.base.default.background) + .border() + .border_color(theme.lowest.base.default.border) + .flex() + .flex_col() + .child( + div() + .flex() + .flex_col() + .gap_px() + .child( + div().py_0p5().px_1().flex().flex_col().child( + div().px_2().py_0p5().child( + label(self.input_placeholder).color(LabelColor::Placeholder), + ), + ), + ) + .child(div().h_px().w_full().fill(theme.lowest.base.default.border)) + .child( + div() + .py_0p5() + .px_1() + .flex() + .flex_col() + .grow() + .max_h_96() + .overflow_y_scroll(self.scroll_state.clone()) + .children( + vec![if self.items.is_empty() { + Some( + div() + .flex() + .flex_row() + .justify_between() + .px_2() + .py_1() + .child( + label(self.empty_string).color(LabelColor::Muted), + ), + ) + } else { + None + }] + .into_iter() + .flatten(), + ) + .children(self.items.iter().map(|item| { + div() + .flex() + .flex_row() + .justify_between() + .px_2() + .py_0p5() + .rounded_lg() + .hover() + .fill(theme.lowest.base.hovered.background) + .active() + .fill(theme.lowest.base.pressed.background) + .child(palette_item(item.label, item.keybinding)) + })), + ), + ) + } +} diff --git a/crates/ui/src/modules/project_panel.rs b/crates/ui/src/modules/project_panel.rs deleted file mode 100644 index e17ff4c8edf425801731a37f60d1e05d65f61186..0000000000000000000000000000000000000000 --- a/crates/ui/src/modules/project_panel.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::marker::PhantomData; - -use gpui2::elements::div; -use gpui2::elements::div::ScrollState; -use gpui2::style::StyleHelpers; -use gpui2::{Element, IntoElement, ParentElement, ViewContext}; - -use crate::prelude::*; -use crate::{details, input, label, list_item, theme, IconAsset, LabelColor}; - -#[derive(Element)] -pub struct ProjectPanel { - view_type: PhantomData, - scroll_state: ScrollState, -} - -pub fn project_panel(scroll_state: ScrollState) -> ProjectPanel { - ProjectPanel { - view_type: PhantomData, - scroll_state, - } -} - -impl ProjectPanel { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - .w_56() - .h_full() - .flex() - .flex_col() - .fill(theme.middle.base.default.background) - .child( - div() - .w_56() - .flex() - .flex_col() - .overflow_y_scroll(self.scroll_state.clone()) - .child(details("This is a long string that should wrap when it keeps going for a long time.").meta_text("6 h ago)")) - .child( - div().flex().flex_col().children( - std::iter::repeat_with(|| { - vec![ - list_item(label("sqlez").color(LabelColor::Modified)) - .left_icon(IconAsset::FolderOpen.into()) - .indent_level(0) - .set_toggle(ToggleState::NotToggled), - list_item(label("storybook").color(LabelColor::Modified)) - .left_icon(IconAsset::FolderOpen.into()) - .indent_level(0) - .set_toggle(ToggleState::Toggled), - list_item(label("docs").color(LabelColor::Default)) - .left_icon(IconAsset::Folder.into()) - .indent_level(1) - .set_toggle(ToggleState::Toggled), - list_item(label("src").color(LabelColor::Modified)) - .left_icon(IconAsset::FolderOpen.into()) - .indent_level(2) - .set_toggle(ToggleState::Toggled), - list_item(label("ui").color(LabelColor::Modified)) - .left_icon(IconAsset::FolderOpen.into()) - .indent_level(3) - .set_toggle(ToggleState::Toggled), - list_item(label("component").color(LabelColor::Created)) - .left_icon(IconAsset::FolderOpen.into()) - .indent_level(4) - .set_toggle(ToggleState::Toggled), - list_item(label("facepile.rs").color(LabelColor::Default)) - .left_icon(IconAsset::File.into()) - .indent_level(5), - list_item(label("follow_group.rs").color(LabelColor::Default)) - .left_icon(IconAsset::File.into()) - .indent_level(5), - list_item(label("list_item.rs").color(LabelColor::Created)) - .left_icon(IconAsset::File.into()) - .indent_level(5), - list_item(label("tab.rs").color(LabelColor::Default)) - .left_icon(IconAsset::File.into()) - .indent_level(5), - ] - }) - .take(10) - .flatten(), - ), - ), - ) - .child( - input("Find something...") - .value("buffe".to_string()) - .state(InteractionState::Focused), - ) - } -} diff --git a/crates/ui/src/prelude.rs b/crates/ui/src/prelude.rs index 61dbb676e2d7d01a4843f2c5a84db7f32064c30b..70b9ab4a5ed0e0dbbc6f0c13670c50fd452cf225 100644 --- a/crates/ui/src/prelude.rs +++ b/crates/ui/src/prelude.rs @@ -1,3 +1,11 @@ +#[derive(Default, PartialEq)] +pub enum OrderMethod { + #[default] + Ascending, + Descending, + MostRecent, +} + #[derive(Default, PartialEq)] pub enum ButtonVariant { #[default] @@ -19,6 +27,13 @@ pub enum Shape { RoundedRectangle, } +#[derive(Default, PartialEq, Clone, Copy)] +pub enum DisclosureControlVisibility { + #[default] + OnHover, + Always, +} + #[derive(Default, PartialEq, Clone, Copy)] pub enum InteractionState { #[default] diff --git a/crates/ui/src/static_data.rs b/crates/ui/src/static_data.rs new file mode 100644 index 0000000000000000000000000000000000000000..de946dab28d9cbabd5ba95a28ab162386d8e101c --- /dev/null +++ b/crates/ui/src/static_data.rs @@ -0,0 +1,166 @@ +use crate::{ + label, list_item, palette_item, IconAsset, LabelColor, ListItem, PaletteItem, ToggleState, +}; + +pub fn static_project_panel_project_items() -> Vec { + vec![ + list_item(label("zed")) + .left_icon(IconAsset::FolderOpen.into()) + .indent_level(0) + .set_toggle(ToggleState::Toggled), + list_item(label(".cargo")) + .left_icon(IconAsset::Folder.into()) + .indent_level(1), + list_item(label(".config")) + .left_icon(IconAsset::Folder.into()) + .indent_level(1), + list_item(label(".git").color(LabelColor::Hidden)) + .left_icon(IconAsset::Folder.into()) + .indent_level(1), + list_item(label(".cargo")) + .left_icon(IconAsset::Folder.into()) + .indent_level(1), + list_item(label(".idea").color(LabelColor::Hidden)) + .left_icon(IconAsset::Folder.into()) + .indent_level(1), + list_item(label("assets")) + .left_icon(IconAsset::Folder.into()) + .indent_level(1) + .set_toggle(ToggleState::Toggled), + list_item(label("cargo-target").color(LabelColor::Hidden)) + .left_icon(IconAsset::Folder.into()) + .indent_level(1), + list_item(label("crates")) + .left_icon(IconAsset::FolderOpen.into()) + .indent_level(1) + .set_toggle(ToggleState::Toggled), + list_item(label("activity_indicator")) + .left_icon(IconAsset::Folder.into()) + .indent_level(2), + list_item(label("ai")) + .left_icon(IconAsset::Folder.into()) + .indent_level(2), + list_item(label("audio")) + .left_icon(IconAsset::Folder.into()) + .indent_level(2), + list_item(label("auto_update")) + .left_icon(IconAsset::Folder.into()) + .indent_level(2), + list_item(label("breadcrumbs")) + .left_icon(IconAsset::Folder.into()) + .indent_level(2), + list_item(label("call")) + .left_icon(IconAsset::Folder.into()) + .indent_level(2), + list_item(label("sqlez").color(LabelColor::Modified)) + .left_icon(IconAsset::Folder.into()) + .indent_level(2) + .set_toggle(ToggleState::NotToggled), + list_item(label("gpui2")) + .left_icon(IconAsset::FolderOpen.into()) + .indent_level(2) + .set_toggle(ToggleState::Toggled), + list_item(label("src")) + .left_icon(IconAsset::FolderOpen.into()) + .indent_level(3) + .set_toggle(ToggleState::Toggled), + list_item(label("derrive_element.rs")) + .left_icon(IconAsset::FileRust.into()) + .indent_level(4), + list_item(label("storybook").color(LabelColor::Modified)) + .left_icon(IconAsset::FolderOpen.into()) + .indent_level(1) + .set_toggle(ToggleState::Toggled), + list_item(label("docs").color(LabelColor::Default)) + .left_icon(IconAsset::Folder.into()) + .indent_level(2) + .set_toggle(ToggleState::Toggled), + list_item(label("src").color(LabelColor::Modified)) + .left_icon(IconAsset::FolderOpen.into()) + .indent_level(3) + .set_toggle(ToggleState::Toggled), + list_item(label("ui").color(LabelColor::Modified)) + .left_icon(IconAsset::FolderOpen.into()) + .indent_level(4) + .set_toggle(ToggleState::Toggled), + list_item(label("component").color(LabelColor::Created)) + .left_icon(IconAsset::FolderOpen.into()) + .indent_level(5) + .set_toggle(ToggleState::Toggled), + list_item(label("facepile.rs").color(LabelColor::Default)) + .left_icon(IconAsset::FileRust.into()) + .indent_level(6), + list_item(label("follow_group.rs").color(LabelColor::Default)) + .left_icon(IconAsset::FileRust.into()) + .indent_level(6), + list_item(label("list_item.rs").color(LabelColor::Created)) + .left_icon(IconAsset::FileRust.into()) + .indent_level(6), + list_item(label("tab.rs").color(LabelColor::Default)) + .left_icon(IconAsset::FileRust.into()) + .indent_level(6), + list_item(label("target").color(LabelColor::Hidden)) + .left_icon(IconAsset::Folder.into()) + .indent_level(1), + list_item(label(".dockerignore")) + .left_icon(IconAsset::File.into()) + .indent_level(1), + list_item(label(".DS_Store").color(LabelColor::Hidden)) + .left_icon(IconAsset::File.into()) + .indent_level(1), + list_item(label("Cargo.lock")) + .left_icon(IconAsset::FileLock.into()) + .indent_level(1), + list_item(label("Cargo.toml")) + .left_icon(IconAsset::FileToml.into()) + .indent_level(1), + list_item(label("Dockerfile")) + .left_icon(IconAsset::File.into()) + .indent_level(1), + list_item(label("Procfile")) + .left_icon(IconAsset::File.into()) + .indent_level(1), + list_item(label("README.md")) + .left_icon(IconAsset::FileDoc.into()) + .indent_level(1), + ] +} + +pub fn static_project_panel_single_items() -> Vec { + vec![ + list_item(label("todo.md")) + .left_icon(IconAsset::FileDoc.into()) + .indent_level(0), + list_item(label("README.md")) + .left_icon(IconAsset::FileDoc.into()) + .indent_level(0), + list_item(label("config.json")) + .left_icon(IconAsset::File.into()) + .indent_level(0), + ] +} + +pub fn example_editor_actions() -> Vec { + vec![ + palette_item("New File", Some("Ctrl+N")), + palette_item("Open File", Some("Ctrl+O")), + palette_item("Save File", Some("Ctrl+S")), + palette_item("Cut", Some("Ctrl+X")), + palette_item("Copy", Some("Ctrl+C")), + palette_item("Paste", Some("Ctrl+V")), + palette_item("Undo", Some("Ctrl+Z")), + palette_item("Redo", Some("Ctrl+Shift+Z")), + palette_item("Find", Some("Ctrl+F")), + palette_item("Replace", Some("Ctrl+R")), + palette_item("Jump to Line", None), + palette_item("Select All", None), + palette_item("Deselect All", None), + palette_item("Switch Document", None), + palette_item("Insert Line Below", None), + palette_item("Insert Line Above", None), + palette_item("Move Line Up", None), + palette_item("Move Line Down", None), + palette_item("Toggle Comment", None), + palette_item("Delete Line", None), + ] +} diff --git a/crates/ui/src/templates.rs b/crates/ui/src/templates.rs new file mode 100644 index 0000000000000000000000000000000000000000..f09a8a7ea4e93795d4a5b669b2f2816476c6c26c --- /dev/null +++ b/crates/ui/src/templates.rs @@ -0,0 +1,17 @@ +mod chat_panel; +mod collab_panel; +mod command_palette; +mod project_panel; +mod status_bar; +mod tab_bar; +mod title_bar; +mod workspace; + +pub use chat_panel::*; +pub use collab_panel::*; +pub use command_palette::*; +pub use project_panel::*; +pub use status_bar::*; +pub use tab_bar::*; +pub use title_bar::*; +pub use workspace::*; diff --git a/crates/ui/src/modules/chat_panel.rs b/crates/ui/src/templates/chat_panel.rs similarity index 92% rename from crates/ui/src/modules/chat_panel.rs rename to crates/ui/src/templates/chat_panel.rs index 77c5b2ef20ba8a2de7cc47fead7d715b1b055706..7c3462b3fe46c31cb1e158548435f572915ff15c 100644 --- a/crates/ui/src/modules/chat_panel.rs +++ b/crates/ui/src/templates/chat_panel.rs @@ -1,11 +1,11 @@ use std::marker::PhantomData; -use gpui2::elements::div; +use crate::icon_button; +use crate::theme::theme; use gpui2::elements::div::ScrollState; use gpui2::style::StyleHelpers; -use gpui2::{Element, IntoElement, ParentElement, ViewContext}; - -use crate::{icon_button, theme}; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; #[derive(Element)] pub struct ChatPanel { diff --git a/crates/ui/src/templates/collab_panel.rs b/crates/ui/src/templates/collab_panel.rs new file mode 100644 index 0000000000000000000000000000000000000000..7e76cb68355b3c09bf45e02a40c62376d4934612 --- /dev/null +++ b/crates/ui/src/templates/collab_panel.rs @@ -0,0 +1,177 @@ +use crate::theme::{theme, Theme}; +use gpui2::{ + elements::{div, div::ScrollState, img, svg}, + style::{StyleHelpers, Styleable}, + ArcCow, Element, IntoElement, ParentElement, ViewContext, +}; +use std::marker::PhantomData; + +#[derive(Element)] +pub struct CollabPanelElement { + view_type: PhantomData, + scroll_state: ScrollState, +} + +// When I improve child view rendering, I'd like to have V implement a trait that +// provides the scroll state, among other things. +pub fn collab_panel(scroll_state: ScrollState) -> CollabPanelElement { + CollabPanelElement { + view_type: PhantomData, + scroll_state, + } +} + +impl CollabPanelElement { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + // Panel + div() + .w_64() + .h_full() + .flex() + .flex_col() + .font("Zed Sans Extended") + .text_color(theme.middle.base.default.foreground) + .border_color(theme.middle.base.default.border) + .border() + .fill(theme.middle.base.default.background) + .child( + div() + .w_full() + .flex() + .flex_col() + .overflow_y_scroll(self.scroll_state.clone()) + // List Container + .child( + div() + .fill(theme.lowest.base.default.background) + .pb_1() + .border_color(theme.lowest.base.default.border) + .border_b() + //:: https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state + // .group() + // List Section Header + .child(self.list_section_header("#CRDB", true, theme)) + // List Item Large + .child(self.list_item( + "http://github.com/maxbrunsfeld.png?s=50", + "maxbrunsfeld", + theme, + )), + ) + .child( + div() + .py_2() + .flex() + .flex_col() + .child(self.list_section_header("CHANNELS", true, theme)), + ) + .child( + div() + .py_2() + .flex() + .flex_col() + .child(self.list_section_header("CONTACTS", true, theme)) + .children( + std::iter::repeat_with(|| { + vec![ + self.list_item( + "http://github.com/as-cii.png?s=50", + "as-cii", + theme, + ), + self.list_item( + "http://github.com/nathansobo.png?s=50", + "nathansobo", + theme, + ), + self.list_item( + "http://github.com/maxbrunsfeld.png?s=50", + "maxbrunsfeld", + theme, + ), + ] + }) + .take(3) + .flatten(), + ), + ), + ) + .child( + div() + .h_7() + .px_2() + .border_t() + .border_color(theme.middle.variant.default.border) + .flex() + .items_center() + .child( + div() + .text_sm() + .text_color(theme.middle.variant.default.foreground) + .child("Find..."), + ), + ) + } + + fn list_section_header( + &self, + label: impl Into>, + expanded: bool, + theme: &Theme, + ) -> impl Element { + div() + .h_7() + .px_2() + .flex() + .justify_between() + .items_center() + .child(div().flex().gap_1().text_sm().child(label)) + .child( + div().flex().h_full().gap_1().items_center().child( + svg() + .path(if expanded { + "icons/caret_down.svg" + } else { + "icons/caret_up.svg" + }) + .w_3p5() + .h_3p5() + .fill(theme.middle.variant.default.foreground), + ), + ) + } + + fn list_item( + &self, + avatar_uri: impl Into>, + label: impl Into>, + theme: &Theme, + ) -> impl Element { + div() + .h_7() + .px_2() + .flex() + .items_center() + .hover() + .fill(theme.lowest.variant.hovered.background) + .active() + .fill(theme.lowest.variant.pressed.background) + .child( + div() + .flex() + .items_center() + .gap_1() + .text_sm() + .child( + img() + .uri(avatar_uri) + .size_3p5() + .rounded_full() + .fill(theme.middle.positive.default.foreground), + ) + .child(label), + ) + } +} diff --git a/crates/ui/src/templates/command_palette.rs b/crates/ui/src/templates/command_palette.rs new file mode 100644 index 0000000000000000000000000000000000000000..303e2d6de9f69b0dc75a53f705878ff25319f1ae --- /dev/null +++ b/crates/ui/src/templates/command_palette.rs @@ -0,0 +1,31 @@ +use gpui2::elements::div; +use gpui2::{elements::div::ScrollState, ViewContext}; +use gpui2::{Element, IntoElement, ParentElement}; +use std::marker::PhantomData; + +use crate::{example_editor_actions, palette, OrderMethod}; + +#[derive(Element)] +pub struct CommandPalette { + view_type: PhantomData, + scroll_state: ScrollState, +} + +pub fn command_palette(scroll_state: ScrollState) -> CommandPalette { + CommandPalette { + view_type: PhantomData, + scroll_state, + } +} + +impl CommandPalette { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + div().child( + palette(self.scroll_state.clone()) + .items(example_editor_actions()) + .placeholder("Execute a command...") + .empty_string("No items found.") + .default_order(OrderMethod::Ascending), + ) + } +} diff --git a/crates/ui/src/templates/project_panel.rs b/crates/ui/src/templates/project_panel.rs new file mode 100644 index 0000000000000000000000000000000000000000..8204ad26c077c2aa03e5a8a245925cd97987c1c6 --- /dev/null +++ b/crates/ui/src/templates/project_panel.rs @@ -0,0 +1,62 @@ +use crate::{ + input, list, list_section_header, prelude::*, static_project_panel_project_items, + static_project_panel_single_items, theme, +}; + +use gpui2::{ + elements::{div, div::ScrollState}, + style::StyleHelpers, + ParentElement, ViewContext, +}; +use gpui2::{Element, IntoElement}; +use std::marker::PhantomData; + +#[derive(Element)] +pub struct ProjectPanel { + view_type: PhantomData, + scroll_state: ScrollState, +} + +pub fn project_panel(scroll_state: ScrollState) -> ProjectPanel { + ProjectPanel { + view_type: PhantomData, + scroll_state, + } +} + +impl ProjectPanel { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + div() + .w_56() + .h_full() + .flex() + .flex_col() + .fill(theme.middle.base.default.background) + .child( + div() + .w_56() + .flex() + .flex_col() + .overflow_y_scroll(self.scroll_state.clone()) + .child( + list(static_project_panel_single_items()) + .header(list_section_header("FILES").set_toggle(ToggleState::Toggled)) + .empty_message("No files in directory") + .set_toggle(ToggleState::Toggled), + ) + .child( + list(static_project_panel_project_items()) + .header(list_section_header("PROJECT").set_toggle(ToggleState::Toggled)) + .empty_message("No folders in directory") + .set_toggle(ToggleState::Toggled), + ), + ) + .child( + input("Find something...") + .value("buffe".to_string()) + .state(InteractionState::Focused), + ) + } +} diff --git a/crates/ui/src/modules/status_bar.rs b/crates/ui/src/templates/status_bar.rs similarity index 97% rename from crates/ui/src/modules/status_bar.rs rename to crates/ui/src/templates/status_bar.rs index 763a585176128fa1e832bfc35fa35e670aeb5e41..79265a757296394c3603abbca02750d2a855cb23 100644 --- a/crates/ui/src/modules/status_bar.rs +++ b/crates/ui/src/templates/status_bar.rs @@ -1,11 +1,10 @@ use std::marker::PhantomData; -use gpui2::elements::div; -use gpui2::style::StyleHelpers; -use gpui2::{Element, IntoElement, ParentElement, ViewContext}; - use crate::theme::{theme, Theme}; use crate::{icon_button, text_button, tool_divider}; +use gpui2::style::StyleHelpers; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; #[derive(Default, PartialEq)] pub enum Tool { diff --git a/crates/ui/src/modules/tab_bar.rs b/crates/ui/src/templates/tab_bar.rs similarity index 95% rename from crates/ui/src/modules/tab_bar.rs rename to crates/ui/src/templates/tab_bar.rs index 08caad310729f66b1f723366994a73c660b447a3..e1ca6b1dc793ed87bcc7d278ef1e2a3c6a9cc0b3 100644 --- a/crates/ui/src/modules/tab_bar.rs +++ b/crates/ui/src/templates/tab_bar.rs @@ -1,12 +1,12 @@ use std::marker::PhantomData; -use gpui2::elements::div; +use crate::prelude::InteractionState; +use crate::theme::theme; +use crate::{icon_button, tab}; use gpui2::elements::div::ScrollState; use gpui2::style::StyleHelpers; -use gpui2::{Element, IntoElement, ParentElement, ViewContext}; - -use crate::prelude::InteractionState; -use crate::{icon_button, tab, theme}; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; #[derive(Element)] pub struct TabBar { diff --git a/crates/ui/src/modules/title_bar.rs b/crates/ui/src/templates/title_bar.rs similarity index 100% rename from crates/ui/src/modules/title_bar.rs rename to crates/ui/src/templates/title_bar.rs diff --git a/crates/ui/src/templates/workspace.rs b/crates/ui/src/templates/workspace.rs new file mode 100644 index 0000000000000000000000000000000000000000..fb785a317f71be0b476f294b2c95141c06b56566 --- /dev/null +++ b/crates/ui/src/templates/workspace.rs @@ -0,0 +1,80 @@ +use crate::{chat_panel, collab_panel, project_panel, status_bar, tab_bar, theme, title_bar}; + +use gpui2::{ + elements::{div, div::ScrollState}, + style::StyleHelpers, + Element, IntoElement, ParentElement, ViewContext, +}; + +#[derive(Element, Default)] +struct WorkspaceElement { + project_panel_scroll_state: ScrollState, + collab_panel_scroll_state: ScrollState, + right_scroll_state: ScrollState, + tab_bar_scroll_state: ScrollState, + palette_scroll_state: ScrollState, +} + +pub fn workspace() -> impl Element { + WorkspaceElement::default() +} + +impl WorkspaceElement { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + div() + // Elevation Level 0 + .size_full() + .flex() + .flex_col() + .font("Zed Sans Extended") + .gap_0() + .justify_start() + .items_start() + .text_color(theme.lowest.base.default.foreground) + .fill(theme.lowest.base.default.background) + .relative() + // Elevation Level 1 + .child(title_bar()) + .child( + div() + .flex_1() + .w_full() + .flex() + .flex_row() + .overflow_hidden() + .child(project_panel(self.project_panel_scroll_state.clone())) + .child(collab_panel(self.collab_panel_scroll_state.clone())) + .child( + div() + .h_full() + .flex_1() + .fill(theme.highest.base.default.background) + .child( + div() + .flex() + .flex_col() + .flex_1() + .child(tab_bar(self.tab_bar_scroll_state.clone())), + ), + ) + .child(chat_panel(self.right_scroll_state.clone())), + ) + .child(status_bar()) + // Elevation Level 3 + // .child( + // div() + // .absolute() + // .top_0() + // .left_0() + // .size_full() + // .flex() + // .justify_center() + // .items_center() + // // .fill(theme.lowest.base.default.background) + // // Elevation Level 4 + // .child(command_palette(self.palette_scroll_state.clone())), + // ) + } +} diff --git a/crates/ui/src/tokens.rs b/crates/ui/src/tokens.rs new file mode 100644 index 0000000000000000000000000000000000000000..79128205330c4e1424024f9af4823749f47f3a06 --- /dev/null +++ b/crates/ui/src/tokens.rs @@ -0,0 +1,18 @@ +use gpui2::geometry::AbsoluteLength; + +#[derive(Clone, Copy)] +pub struct Token { + pub list_indent_depth: AbsoluteLength, +} + +impl Default for Token { + fn default() -> Self { + Self { + list_indent_depth: AbsoluteLength::Rems(0.5), + } + } +} + +pub fn token() -> Token { + Token::default() +} From 30b105afd55a58d33246a0ffc1356823ae61a3d1 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 21 Sep 2023 23:51:03 -0400 Subject: [PATCH 10/11] Remove leftover state doc --- docs/ui/states.md | 43 ------------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 docs/ui/states.md diff --git a/docs/ui/states.md b/docs/ui/states.md deleted file mode 100644 index 7dc3110ceda07386fbede131e155aeb7f8a87c01..0000000000000000000000000000000000000000 --- a/docs/ui/states.md +++ /dev/null @@ -1,43 +0,0 @@ -## Interaction State - -**Enabled** - -An enabled state communicates an interactive component or element. - -**Disabled** - -A disabled state communicates a inoperable component or element. - -**Hover** - -A hover state communicates when a user has placed a cursor above an interactive element. - -**Focused** - -A focused state communicates when a user has highlighted an element, using an input method such as a keyboard or voice. - -**Activated** - -An activated state communicates a highlighted destination, whether initiated by the user or by default. - -**Pressed** - -A pressed state communicates a user tap. - -**Dragged** - -A dragged state communicates when a user presses and moves an element. - -## Selected State - -**Unselected** - -dfa - -**Partially Selected** - -daf - -**Selected** - -dfa From d61565d227460742b6c2b8d45b8d079506a0bb35 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 22 Sep 2023 13:40:20 +0300 Subject: [PATCH 11/11] Do not resubscribe for Copilot logs events Copilot sends multiple events about its LSP server readiness, not necessarily recreating the server from scratch (e.g. due to re-sign in action). Avoid re-adding same log subscriptions on the same LSP server, which causes panics. --- crates/language_tools/src/lsp_log.rs | 7 +++++++ crates/lsp/src/lsp.rs | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index d0e9e9383941cfe39af543a98fb3188fcb917c1a..3f29d6f79b87b1c168b8fda47d82f73511c370bd 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -181,6 +181,13 @@ impl LogStore { }); let server = project.read(cx).language_server_for_id(id); + if let Some(server) = server.as_deref() { + if server.has_notification_handler::() { + // Another event wants to re-add the server that was already added and subscribed to, avoid doing it again. + return Some(server_state.log_buffer.clone()); + } + } + let weak_project = project.downgrade(); let io_tx = self.io_tx.clone(); server_state._io_logs_subscription = server.as_ref().map(|server| { diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index dcfce4f1fbafaba606eaf11aacffb1ec967eef76..9b0d6c98b0ec48fd7207daa25f6a602abe8fd16b 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -605,6 +605,10 @@ impl LanguageServer { self.notification_handlers.lock().remove(T::METHOD); } + pub fn has_notification_handler(&self) -> bool { + self.notification_handlers.lock().contains_key(T::METHOD) + } + #[must_use] pub fn on_custom_notification(&self, method: &'static str, mut f: F) -> Subscription where