diff --git a/Cargo.lock b/Cargo.lock index 72e4557fe1..cf72c6e48e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7818,6 +7818,7 @@ version = "0.1.0" dependencies = [ "anyhow", "backtrace-on-stack-overflow", + "chrono", "clap 4.4.4", "gpui3", "itertools 0.11.0", diff --git a/crates/storybook2/Cargo.toml b/crates/storybook2/Cargo.toml index a8f67cf6ce..7c5bfaaaa5 100644 --- a/crates/storybook2/Cargo.toml +++ b/crates/storybook2/Cargo.toml @@ -13,6 +13,7 @@ anyhow.workspace = true # TODO: Remove after diagnosing stack overflow. backtrace-on-stack-overflow = "0.3.0" clap = { version = "4.4", features = ["derive", "string"] } +chrono = "0.4" gpui3 = { path = "../gpui3" } itertools = "0.11.0" log.workspace = true diff --git a/crates/storybook2/src/stories/components.rs b/crates/storybook2/src/stories/components.rs index 15ec29eca9..0e522a2fc1 100644 --- a/crates/storybook2/src/stories/components.rs +++ b/crates/storybook2/src/stories/components.rs @@ -1,6 +1,7 @@ pub mod assistant_panel; pub mod breadcrumb; pub mod buffer; +pub mod chat_panel; pub mod panel; pub mod project_panel; pub mod tab; diff --git a/crates/storybook2/src/stories/components/chat_panel.rs b/crates/storybook2/src/stories/components/chat_panel.rs new file mode 100644 index 0000000000..cdf3f862c3 --- /dev/null +++ b/crates/storybook2/src/stories/components/chat_panel.rs @@ -0,0 +1,56 @@ +use std::marker::PhantomData; + +use chrono::DateTime; +use ui::prelude::*; +use ui::{ChatMessage, ChatPanel, Panel}; + +use crate::story::Story; + +#[derive(Element)] +pub struct ChatPanelStory { + state_type: PhantomData, +} + +impl ChatPanelStory { + pub fn new() -> Self { + Self { + state_type: PhantomData, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + Story::container(cx) + .child(Story::title_for::<_, ChatPanel>(cx)) + .child(Story::label(cx, "Default")) + .child(Panel::new( + ScrollState::default(), + |_, _| vec![ChatPanel::new(ScrollState::default()).into_any()], + Box::new(()), + )) + .child(Story::label(cx, "With Mesages")) + .child(Panel::new( + ScrollState::default(), + |_, _| { + vec![ChatPanel::new(ScrollState::default()) + .with_messages(vec![ + ChatMessage::new( + "osiewicz".to_string(), + "is this thing on?".to_string(), + DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z") + .unwrap() + .naive_local(), + ), + ChatMessage::new( + "maxdeviant".to_string(), + "Reading you loud and clear!".to_string(), + DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z") + .unwrap() + .naive_local(), + ), + ]) + .into_any()] + }, + Box::new(()), + )) + } +} diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 86c069a2b1..6acbb97584 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -39,6 +39,7 @@ pub enum ComponentStory { AssistantPanel, Breadcrumb, Buffer, + ChatPanel, Panel, ProjectPanel, Tab, @@ -58,6 +59,7 @@ impl ComponentStory { } Self::Buffer => components::buffer::BufferStory::new().into_any(), Self::Breadcrumb => components::breadcrumb::BreadcrumbStory::new().into_any(), + Self::ChatPanel => components::chat_panel::ChatPanelStory::new().into_any(), Self::Panel => components::panel::PanelStory::new().into_any(), Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(), Self::Tab => components::tab::TabStory::new().into_any(), diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index c4eba3fb65..094dae076f 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -1,6 +1,7 @@ mod assistant_panel; mod breadcrumb; mod buffer; +mod chat_panel; mod editor_pane; mod icon_button; mod list; @@ -17,6 +18,7 @@ mod workspace; pub use assistant_panel::*; pub use breadcrumb::*; pub use buffer::*; +pub use chat_panel::*; pub use editor_pane::*; pub use icon_button::*; pub use list::*; diff --git a/crates/ui2/src/components/chat_panel.rs b/crates/ui2/src/components/chat_panel.rs new file mode 100644 index 0000000000..e0cdd3382d --- /dev/null +++ b/crates/ui2/src/components/chat_panel.rs @@ -0,0 +1,108 @@ +use std::marker::PhantomData; + +use chrono::NaiveDateTime; + +use crate::prelude::*; +use crate::theme::theme; +use crate::{Icon, IconButton, Input, Label, LabelColor}; + +#[derive(Element)] +pub struct ChatPanel { + scroll_state: ScrollState, + messages: Vec>, +} + +impl ChatPanel { + pub fn new(scroll_state: ScrollState) -> Self { + Self { + scroll_state, + messages: Vec::new(), + } + } + + pub fn with_messages(mut self, messages: Vec>) -> Self { + self.messages = messages; + self + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + let theme = theme(cx); + + div() + .flex() + .flex_col() + .justify_between() + .h_full() + .px_2() + .gap_2() + // Header + .child( + div() + .flex() + .justify_between() + .py_2() + .child(div().flex().child(Label::new("#design"))) + .child( + div() + .flex() + .items_center() + .gap_px() + .child(IconButton::new(Icon::File)) + .child(IconButton::new(Icon::AudioOn)), + ), + ) + .child( + div() + .flex() + .flex_col() + // Chat Body + .child( + div() + .w_full() + .flex() + .flex_col() + .gap_3() + .overflow_y_scroll(self.scroll_state.clone()) + .children(self.messages.clone()), + ) + // Composer + .child(div().flex().my_2().child(Input::new("Message #design"))), + ) + } +} + +#[derive(Element, Clone)] +pub struct ChatMessage { + state_type: PhantomData, + author: String, + text: String, + sent_at: NaiveDateTime, +} + +impl ChatMessage { + pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self { + Self { + state_type: PhantomData, + author, + text, + sent_at, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + div() + .flex() + .flex_col() + .child( + div() + .flex() + .gap_2() + .child(Label::new(self.author.clone())) + .child( + Label::new(self.sent_at.format("%m/%d/%Y").to_string()) + .color(LabelColor::Muted), + ), + ) + .child(div().child(Label::new(self.text.clone()))) + } +} diff --git a/crates/ui2/src/components/workspace.rs b/crates/ui2/src/components/workspace.rs index abeb3fc1aa..3f109ddf5e 100644 --- a/crates/ui2/src/components/workspace.rs +++ b/crates/ui2/src/components/workspace.rs @@ -6,8 +6,9 @@ use gpui3::{relative, rems, Size}; use crate::prelude::*; use crate::{ - hello_world_rust_editor_with_status_example, theme, v_stack, EditorPane, Pane, PaneGroup, - Panel, PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, + hello_world_rust_editor_with_status_example, theme, v_stack, ChatMessage, ChatPanel, + EditorPane, Pane, PaneGroup, Panel, PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection, + StatusBar, Terminal, }; #[derive(Element)] @@ -139,11 +140,7 @@ impl WorkspaceElement { .child( Panel::new( self.bottom_panel_scroll_state.clone(), - |_, _| { - vec![ - // Terminal::new().into_any() - ] - }, + |_, _| vec![Terminal::new().into_any()], Box::new(()), ) .allowed_sides(PanelAllowedSides::BottomOnly) @@ -153,42 +150,34 @@ impl WorkspaceElement { .child( Panel::new( self.right_panel_scroll_state.clone(), - |_, payload| vec![ProjectPanel::new(ScrollState::default()).into_any()], + |_, payload| { + vec![ChatPanel::new(ScrollState::default()) + .with_messages(vec![ + ChatMessage::new( + "osiewicz".to_string(), + "is this thing on?".to_string(), + DateTime::parse_from_rfc3339( + "2023-09-27T15:40:52.707Z", + ) + .unwrap() + .naive_local(), + ), + ChatMessage::new( + "maxdeviant".to_string(), + "Reading you loud and clear!".to_string(), + DateTime::parse_from_rfc3339( + "2023-09-28T15:40:52.707Z", + ) + .unwrap() + .naive_local(), + ), + ]) + .into_any()] + }, Box::new(()), ) .side(PanelSide::Right), ), - // .child( - // Panel::new( - // self.right_panel_scroll_state.clone(), - // |_, payload| { - // vec![ChatPanel::new(ScrollState::default()) - // .with_messages(vec![ - // ChatMessage::new( - // "osiewicz".to_string(), - // "is this thing on?".to_string(), - // DateTime::parse_from_rfc3339( - // "2023-09-27T15:40:52.707Z", - // ) - // .unwrap() - // .naive_local(), - // ), - // ChatMessage::new( - // "maxdeviant".to_string(), - // "Reading you loud and clear!".to_string(), - // DateTime::parse_from_rfc3339( - // "2023-09-28T15:40:52.707Z", - // ) - // .unwrap() - // .naive_local(), - // ), - // ]) - // .into_any()] - // }, - // Box::new(()), - // ) - // .side(PanelSide::Right), - // ), ) .child(StatusBar::new()) // An example of a toast is below