catchup with main

This commit is contained in:
KCaverly 2023-09-22 09:43:39 -04:00
commit dc49dec4f0
61 changed files with 1477 additions and 342 deletions

13
Cargo.lock generated
View file

@ -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"

View file

@ -70,6 +70,7 @@ members = [
"crates/text",
"crates/theme",
"crates/theme_selector",
"crates/ui",
"crates/util",
"crates/semantic_index",
"crates/vim",

View file

@ -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 ..

View file

@ -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| {

View file

@ -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

View file

@ -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]

View file

@ -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> {

View file

@ -0,0 +1,2 @@
pub mod components;
pub mod elements;

View file

@ -0,0 +1,2 @@
pub mod facepile;
pub mod traffic_lights;

View 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),
])),
)
}
}

View 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())
}
}

View file

@ -0,0 +1 @@
pub mod avatar;

View 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),
),
)
}
}

View file

@ -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"]

View file

@ -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::*;

View file

@ -1,4 +0,0 @@
pub(crate) mod facepile;
pub(crate) mod follow_group;
pub(crate) mod list_item;
pub(crate) mod tab;

View file

@ -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;

View file

@ -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;

View file

@ -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),
)
}
}

View file

@ -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
View 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" }

View 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 buttons 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 components elevation is ascertained by combining the components 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

View file

@ -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>)>>,

View file

@ -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 {

View file

@ -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 {

View file

@ -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()),
)

View 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),
)
}
}

View 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()),
)
}
}

View file

@ -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 {

View 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())
}

View file

@ -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
View 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::*;

View file

@ -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 {

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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())
}
}

View file

@ -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 {

View file

@ -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
View 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
View file

@ -0,0 +1,5 @@
mod list;
mod palette;
pub use list::*;
pub use palette::*;

View 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())
}
}

View 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))
})),
),
)
}
}

View file

@ -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]

View 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),
]
}

View 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::*;

View file

@ -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};

View 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),
)
}
}

View 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),
)
}
}

View 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),
)
}
}

View file

@ -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};

View file

@ -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};

View file

@ -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()

View 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())),
// )
}
}

View file

@ -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
View 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()
}

View file

@ -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).

View file

@ -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