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 6c502ebc74..b3d4987526 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 6e424a6f81..22810e3e07 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 056ad56a2b..f8f0e7a65f 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 49a1268863..26fa015847 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 e79a9c5986..3f76af0d15 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 a261fffcd6..a1cead7df9 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 3cced78c42..05b29aabf4 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 c1876434ad..b9622a1c3e 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 f634ea4eca..c1096a87ba 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 87fd536391..30d15a5b04 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 df1db7b8c2..92d178fad2 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 f8f0e7a65f..0000000000 --- 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 26fa015847..0000000000 --- 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 3127bcf837..95152ec832 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 0000000000..5018a91739 --- /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 1aafefc1a6..a82d28eb8c 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 73ab231c07..d9566c216c 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 5d79b8fc06..75e24e9135 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 46df86bfa1..868b58e449 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 8237aac004..e812a26cd5 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 72ec5b328a..67352f0779 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 83edc73deb..068ec7a28a 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 5d06862439..f156199f9e 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 5a9e8fb6cb..dbe30cb4f9 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 f82979ab7c..4270e0ca5d 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 b905892f1a..2ee40a57ac 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 310287797c..5a3da16fdd 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 bbbedd0b10..d3e94297ad 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 24fd1eb5d3..851efdd4f6 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 a923628def..2ef29b225f 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 0000000000..415cdcbaf3 --- /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 de4428826b..77c5b2ef20 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 5a9608d3e3..e17ff4c8ed 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 f34cfa88ef..763a585176 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 9b96171f2f..08caad3107 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 a41d56476f..705d0d8866 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 0a86a61499..71235625d6 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 415cdcbaf3..eac16aa776 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 eac16aa776..c7bab1e0b0 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 05b29aabf4..9d6358edb4 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 c1096a87ba..882a1a1bdb 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 0000000000..57674c3c70 --- /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 0000000000..5e3d309419 --- /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 0000000000..6bde4539ed --- /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 0000000000..124369cf9d --- /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 0000000000..3239dbec9c --- /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 92d178fad2..089ff33049 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 95152ec832..58c5fed62d 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 5e3d309419..89ee5a92ea 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 0000000000..252caf99f6 --- /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 089ff33049..b72c4a5681 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 a82d28eb8c..b400a491e4 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 0000000000..128af9976f --- /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 705d0d8866..82c5c0234b 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 0000000000..bd34de3396 --- /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 a82d28eb8c..1d513db7d2 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 868b58e449..19e5b26abe 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 0000000000..76a0d4cee7 --- /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 0000000000..9e7883d700 --- /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 dbe30cb4f9..08b8e3e7c5 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 d3e94297ad..90e6b525eb 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 c7bab1e0b0..0bcd961643 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 a1cead7df9..d29e31072b 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 0000000000..a7fb06132f --- /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 0000000000..a540d29169 --- /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 e17ff4c8ed..0000000000 --- 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 61dbb676e2..70b9ab4a5e 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 0000000000..de946dab28 --- /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 0000000000..f09a8a7ea4 --- /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 77c5b2ef20..7c3462b3fe 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 0000000000..7e76cb6835 --- /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 0000000000..303e2d6de9 --- /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 0000000000..8204ad26c0 --- /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 763a585176..79265a7572 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 08caad3107..e1ca6b1dc7 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 0000000000..fb785a317f --- /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 0000000000..7912820533 --- /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 7dc3110ced..0000000000 --- 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 d0e9e93839..3f29d6f79b 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 dcfce4f1fb..9b0d6c98b0 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