mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-24 02:46:43 +00:00
catchup with main
This commit is contained in:
commit
dc49dec4f0
61 changed files with 1477 additions and 342 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -7374,6 +7374,7 @@ name = "storybook"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 3.2.25",
|
||||
"gpui2",
|
||||
"log",
|
||||
"rust-embed",
|
||||
|
@ -7381,6 +7382,7 @@ dependencies = [
|
|||
"settings",
|
||||
"simplelog",
|
||||
"theme",
|
||||
"ui",
|
||||
"util",
|
||||
]
|
||||
|
||||
|
@ -8577,6 +8579,17 @@ version = "1.17.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gpui2",
|
||||
"serde",
|
||||
"settings",
|
||||
"theme",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
|
|
|
@ -70,6 +70,7 @@ members = [
|
|||
"crates/text",
|
||||
"crates/theme",
|
||||
"crates/theme_selector",
|
||||
"crates/ui",
|
||||
"crates/util",
|
||||
"crates/semantic_index",
|
||||
"crates/vim",
|
||||
|
|
|
@ -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 ..
|
||||
|
|
|
@ -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::<lsp::notification::LogMessage>() {
|
||||
// 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| {
|
||||
|
|
|
@ -605,6 +605,10 @@ impl LanguageServer {
|
|||
self.notification_handlers.lock().remove(T::METHOD);
|
||||
}
|
||||
|
||||
pub fn has_notification_handler<T: notification::Notification>(&self) -> bool {
|
||||
self.notification_handlers.lock().contains_key(T::METHOD)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn on_custom_notification<Params, F>(&self, method: &'static str, mut f: F) -> Subscription
|
||||
where
|
||||
|
|
|
@ -9,14 +9,16 @@ 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
|
||||
settings = { path = "../settings" }
|
||||
simplelog = "0.9"
|
||||
theme = { path = "../theme" }
|
||||
ui = { path = "../ui" }
|
||||
util = { path = "../util" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -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<V: 'static> {
|
||||
|
|
2
crates/storybook/src/stories.rs
Normal file
2
crates/storybook/src/stories.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod components;
|
||||
pub mod elements;
|
2
crates/storybook/src/stories/components.rs
Normal file
2
crates/storybook/src/stories/components.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod facepile;
|
||||
pub mod traffic_lights;
|
69
crates/storybook/src/stories/components/facepile.rs
Normal file
69
crates/storybook/src/stories/components/facepile.rs
Normal file
|
@ -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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.pt_2()
|
||||
.px_4()
|
||||
.font("Zed Mono Extended")
|
||||
.fill(rgb::<Hsla>(0x282c34))
|
||||
.child(
|
||||
div()
|
||||
.text_2xl()
|
||||
.text_color(rgb::<Hsla>(0xffffff))
|
||||
.child(std::any::type_name::<ui::Facepile>()),
|
||||
)
|
||||
.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),
|
||||
])),
|
||||
)
|
||||
}
|
||||
}
|
29
crates/storybook/src/stories/components/traffic_lights.rs
Normal file
29
crates/storybook/src/stories/components/traffic_lights.rs
Normal file
|
@ -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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.pt_2()
|
||||
.px_4()
|
||||
.font("Zed Mono Extended")
|
||||
.fill(rgb::<Hsla>(0x282c34))
|
||||
.child(
|
||||
div()
|
||||
.text_2xl()
|
||||
.text_color(rgb::<Hsla>(0xffffff))
|
||||
.child(std::any::type_name::<ui::TrafficLights>()),
|
||||
)
|
||||
.child(traffic_lights())
|
||||
}
|
||||
}
|
1
crates/storybook/src/stories/elements.rs
Normal file
1
crates/storybook/src/stories/elements.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod avatar;
|
41
crates/storybook/src/stories/elements/avatar.rs
Normal file
41
crates/storybook/src/stories/elements/avatar.rs
Normal file
|
@ -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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.pt_2()
|
||||
.px_4()
|
||||
.font("Zed Mono Extended")
|
||||
.fill(rgb::<Hsla>(0x282c34))
|
||||
.child(
|
||||
div()
|
||||
.text_2xl()
|
||||
.text_color(rgb::<Hsla>(0xffffff))
|
||||
.child(std::any::type_name::<ui::Avatar>()),
|
||||
)
|
||||
.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),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,31 +1,69 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
use crate::theme::Theme;
|
||||
mod collab_panel;
|
||||
mod stories;
|
||||
mod workspace;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use ::theme as legacy_theme;
|
||||
use element_ext::ElementExt;
|
||||
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;
|
||||
|
||||
mod collab_panel;
|
||||
mod components;
|
||||
mod element_ext;
|
||||
mod prelude;
|
||||
mod theme;
|
||||
mod ui;
|
||||
mod workspace;
|
||||
use stories::components::facepile::FacepileStory;
|
||||
use stories::components::traffic_lights::TrafficLightsStory;
|
||||
use stories::elements::avatar::AvatarStory;
|
||||
use ui::{ElementExt, Theme};
|
||||
|
||||
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<Self, Self::Err> {
|
||||
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}'")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ElementStory {
|
||||
Avatar,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ComponentStory {
|
||||
Facepile,
|
||||
TrafficLights,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
story: Option<Story>,
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -40,19 +78,30 @@ 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()))
|
||||
}
|
||||
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()))
|
||||
}
|
||||
},
|
||||
);
|
||||
cx.platform().activate(true);
|
||||
});
|
||||
}
|
||||
|
||||
fn storybook<V: 'static>(cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
workspace().themed(current_theme(cx))
|
||||
fn render_story<V: 'static, S: IntoElement<V>>(
|
||||
cx: &mut ViewContext<V>,
|
||||
story: S,
|
||||
) -> impl Element<V> {
|
||||
story.into_element().themed(current_theme(cx))
|
||||
}
|
||||
|
||||
// Nathan: During the transition to gpui2, we will include the base theme on the legacy Theme struct.
|
||||
|
@ -75,7 +124,7 @@ fn current_theme<V: 'static>(cx: &mut ViewContext<V>) -> Theme {
|
|||
use anyhow::{anyhow, Result};
|
||||
use gpui2::AssetSource;
|
||||
use rust_embed::RustEmbed;
|
||||
use workspace::workspace;
|
||||
use workspace::WorkspaceElement;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../../assets"]
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
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 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::*;
|
|
@ -1,4 +0,0 @@
|
|||
pub(crate) mod facepile;
|
||||
pub(crate) mod follow_group;
|
||||
pub(crate) mod list_item;
|
||||
pub(crate) mod tab;
|
|
@ -1,9 +0,0 @@
|
|||
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;
|
|
@ -1,5 +0,0 @@
|
|||
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;
|
|
@ -1,97 +0,0 @@
|
|||
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;
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct ProjectPanel<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
pub fn project_panel<V: 'static>(scroll_state: ScrollState) -> ProjectPanel<V> {
|
||||
ProjectPanel {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ProjectPanel<V> {
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,24 +1,17 @@
|
|||
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 {
|
||||
pub struct WorkspaceElement {
|
||||
left_scroll_state: ScrollState,
|
||||
right_scroll_state: ScrollState,
|
||||
tab_bar_scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
pub fn workspace<V: 'static>() -> impl Element<V> {
|
||||
WorkspaceElement::default()
|
||||
}
|
||||
|
||||
impl WorkspaceElement {
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
|
12
crates/ui/Cargo.toml
Normal file
12
crates/ui/Cargo.toml
Normal file
|
@ -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" }
|
57
crates/ui/doc/elevation.md
Normal file
57
crates/ui/doc/elevation.md
Normal file
|
@ -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
|
|
@ -1,8 +1,27 @@
|
|||
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 list_section_header;
|
||||
mod palette_item;
|
||||
mod tab;
|
||||
mod traffic_lights;
|
||||
|
||||
pub use facepile::*;
|
||||
pub use follow_group::*;
|
||||
pub use list_item::*;
|
||||
pub use list_section_header::*;
|
||||
pub use palette_item::*;
|
||||
pub use tab::*;
|
||||
pub use traffic_lights::*;
|
||||
|
||||
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<V, D> {
|
||||
click: Option<Rc<dyn Fn(&mut V, &D, &mut EventContext<V>)>>,
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -1,17 +1,18 @@
|
|||
use crate::prelude::{InteractionState, ToggleState};
|
||||
use crate::prelude::{DisclosureControlVisibility, InteractionState, ToggleState};
|
||||
use crate::theme::theme;
|
||||
use crate::ui::{icon, IconAsset, Label};
|
||||
use gpui2::geometry::rems;
|
||||
use crate::tokens::token;
|
||||
use crate::{icon, IconAsset, Label};
|
||||
use gpui2::style::{StyleHelpers, Styleable};
|
||||
use gpui2::{elements::div, IntoElement};
|
||||
use gpui2::{Element, ParentElement, ViewContext};
|
||||
|
||||
#[derive(Element)]
|
||||
#[derive(Element, Clone)]
|
||||
pub struct ListItem {
|
||||
label: Label,
|
||||
left_icon: Option<IconAsset>,
|
||||
indent_level: u32,
|
||||
state: InteractionState,
|
||||
disclosure_control_style: DisclosureControlVisibility,
|
||||
toggle: Option<ToggleState>,
|
||||
}
|
||||
|
||||
|
@ -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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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()),
|
||||
)
|
88
crates/ui/src/components/list_section_header.rs
Normal file
88
crates/ui/src/components/list_section_header.rs
Normal file
|
@ -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<IconAsset>,
|
||||
state: InteractionState,
|
||||
toggle: Option<ToggleState>,
|
||||
}
|
||||
|
||||
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<IconAsset>) -> Self {
|
||||
self.left_icon = left_icon;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn state(mut self, state: InteractionState) -> Self {
|
||||
self.state = state;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
63
crates/ui/src/components/palette_item.rs
Normal file
63
crates/ui/src/components/palette_item.rs
Normal file
|
@ -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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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()),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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 {
|
30
crates/ui/src/components/traffic_lights.rs
Normal file
30
crates/ui/src/components/traffic_lights.rs
Normal file
|
@ -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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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<V: 'static, C: Into<Hsla>>(fill: C) -> div::Div<V> {
|
||||
div().w_3().h_3().rounded_full().fill(fill.into())
|
||||
}
|
|
@ -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<V: 'static>: Element<V> {
|
||||
fn themed(self, theme: Theme) -> Themed<V, Self>
|
||||
where
|
19
crates/ui/src/elements.rs
Normal file
19
crates/ui/src/elements.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
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::*;
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -4,26 +4,27 @@ use gpui2::style::StyleHelpers;
|
|||
use gpui2::IntoElement;
|
||||
use gpui2::{Element, ViewContext};
|
||||
|
||||
// Icon::Hash
|
||||
// icon(IconAsset::Hash).color(IconColor::Warning)
|
||||
// Icon::new(IconAsset::Hash).color(IconColor::Warning)
|
||||
|
||||
#[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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -8,22 +8,33 @@ use gpui2::{IntoElement, ParentElement};
|
|||
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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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())
|
||||
}
|
||||
}
|
|
@ -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 {
|
|
@ -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 {}
|
21
crates/ui/src/lib.rs
Normal file
21
crates/ui/src/lib.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
mod components;
|
||||
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::*;
|
5
crates/ui/src/modules.rs
Normal file
5
crates/ui/src/modules.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod list;
|
||||
mod palette;
|
||||
|
||||
pub use list::*;
|
||||
pub use palette::*;
|
64
crates/ui/src/modules/list.rs
Normal file
64
crates/ui/src/modules/list.rs
Normal file
|
@ -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<ListSectionHeader>,
|
||||
items: Vec<ListItem>,
|
||||
empty_message: &'static str,
|
||||
toggle: Option<ToggleState>,
|
||||
// footer: Option<ListSectionFooter>,
|
||||
}
|
||||
|
||||
pub fn list(items: Vec<ListItem>) -> 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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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())
|
||||
}
|
||||
}
|
124
crates/ui/src/modules/palette.rs
Normal file
124
crates/ui/src/modules/palette.rs
Normal file
|
@ -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<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
input_placeholder: &'static str,
|
||||
empty_string: &'static str,
|
||||
items: Vec<PaletteItem>,
|
||||
default_order: OrderMethod,
|
||||
}
|
||||
|
||||
pub fn palette<V: 'static>(scroll_state: ScrollState) -> Palette<V> {
|
||||
Palette {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
input_placeholder: "Find something...",
|
||||
empty_string: "No items found.",
|
||||
items: vec![],
|
||||
default_order: OrderMethod::default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Palette<V> {
|
||||
pub fn items(mut self, mut items: Vec<PaletteItem>) -> 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<V>) -> impl IntoElement<V> {
|
||||
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))
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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]
|
166
crates/ui/src/static_data.rs
Normal file
166
crates/ui/src/static_data.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
use crate::{
|
||||
label, list_item, palette_item, IconAsset, LabelColor, ListItem, PaletteItem, ToggleState,
|
||||
};
|
||||
|
||||
pub fn static_project_panel_project_items() -> Vec<ListItem> {
|
||||
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<ListItem> {
|
||||
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<PaletteItem> {
|
||||
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),
|
||||
]
|
||||
}
|
17
crates/ui/src/templates.rs
Normal file
17
crates/ui/src/templates.rs
Normal file
|
@ -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::*;
|
|
@ -1,7 +1,7 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::icon_button;
|
||||
use crate::theme::theme;
|
||||
use crate::ui::icon_button;
|
||||
use gpui2::elements::div::ScrollState;
|
||||
use gpui2::style::StyleHelpers;
|
||||
use gpui2::{elements::div, IntoElement};
|
177
crates/ui/src/templates/collab_panel.rs
Normal file
177
crates/ui/src/templates/collab_panel.rs
Normal file
|
@ -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<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
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<V: 'static>(scroll_state: ScrollState) -> CollabPanelElement<V> {
|
||||
CollabPanelElement {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> CollabPanelElement<V> {
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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<ArcCow<'static, str>>,
|
||||
expanded: bool,
|
||||
theme: &Theme,
|
||||
) -> impl Element<V> {
|
||||
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<ArcCow<'static, str>>,
|
||||
label: impl Into<ArcCow<'static, str>>,
|
||||
theme: &Theme,
|
||||
) -> impl Element<V> {
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
31
crates/ui/src/templates/command_palette.rs
Normal file
31
crates/ui/src/templates/command_palette.rs
Normal file
|
@ -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<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
pub fn command_palette<V: 'static>(scroll_state: ScrollState) -> CommandPalette<V> {
|
||||
CommandPalette {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> CommandPalette<V> {
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
div().child(
|
||||
palette(self.scroll_state.clone())
|
||||
.items(example_editor_actions())
|
||||
.placeholder("Execute a command...")
|
||||
.empty_string("No items found.")
|
||||
.default_order(OrderMethod::Ascending),
|
||||
)
|
||||
}
|
||||
}
|
62
crates/ui/src/templates/project_panel.rs
Normal file
62
crates/ui/src/templates/project_panel.rs
Normal file
|
@ -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<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
pub fn project_panel<V: 'static>(scroll_state: ScrollState) -> ProjectPanel<V> {
|
||||
ProjectPanel {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ProjectPanel<V> {
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::theme::{theme, Theme};
|
||||
use crate::ui::{icon_button, text_button, tool_divider};
|
||||
use crate::{icon_button, text_button, tool_divider};
|
||||
use gpui2::style::StyleHelpers;
|
||||
use gpui2::{elements::div, IntoElement};
|
||||
use gpui2::{Element, ParentElement, ViewContext};
|
|
@ -2,7 +2,7 @@ use std::marker::PhantomData;
|
|||
|
||||
use crate::prelude::InteractionState;
|
||||
use crate::theme::theme;
|
||||
use crate::ui::{icon_button, tab};
|
||||
use crate::{icon_button, tab};
|
||||
use gpui2::elements::div::ScrollState;
|
||||
use gpui2::style::StyleHelpers;
|
||||
use gpui2::{elements::div, IntoElement};
|
|
@ -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, traffic_lights};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct TitleBar<V: 'static> {
|
||||
|
@ -40,34 +40,7 @@ impl<V: 'static> TitleBar<V> {
|
|||
.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()
|
80
crates/ui/src/templates/workspace.rs
Normal file
80
crates/ui/src/templates/workspace.rs
Normal file
|
@ -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<V: 'static>() -> impl Element<V> {
|
||||
WorkspaceElement::default()
|
||||
}
|
||||
|
||||
impl WorkspaceElement {
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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())),
|
||||
// )
|
||||
}
|
||||
}
|
|
@ -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)]
|
18
crates/ui/src/tokens.rs
Normal file
18
crates/ui/src/tokens.rs
Normal file
|
@ -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()
|
||||
}
|
|
@ -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).
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue