Add ChatPanel component

This commit is contained in:
Marshall Bowers 2023-10-07 11:50:41 -04:00
parent 0dcbc47e15
commit 9e79ad5a62
8 changed files with 199 additions and 39 deletions

1
Cargo.lock generated
View file

@ -7818,6 +7818,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"backtrace-on-stack-overflow", "backtrace-on-stack-overflow",
"chrono",
"clap 4.4.4", "clap 4.4.4",
"gpui3", "gpui3",
"itertools 0.11.0", "itertools 0.11.0",

View file

@ -13,6 +13,7 @@ anyhow.workspace = true
# TODO: Remove after diagnosing stack overflow. # TODO: Remove after diagnosing stack overflow.
backtrace-on-stack-overflow = "0.3.0" backtrace-on-stack-overflow = "0.3.0"
clap = { version = "4.4", features = ["derive", "string"] } clap = { version = "4.4", features = ["derive", "string"] }
chrono = "0.4"
gpui3 = { path = "../gpui3" } gpui3 = { path = "../gpui3" }
itertools = "0.11.0" itertools = "0.11.0"
log.workspace = true log.workspace = true

View file

@ -1,6 +1,7 @@
pub mod assistant_panel; pub mod assistant_panel;
pub mod breadcrumb; pub mod breadcrumb;
pub mod buffer; pub mod buffer;
pub mod chat_panel;
pub mod panel; pub mod panel;
pub mod project_panel; pub mod project_panel;
pub mod tab; pub mod tab;

View file

@ -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<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ChatPanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ChatPanel<S>>(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(()),
))
}
}

View file

@ -39,6 +39,7 @@ pub enum ComponentStory {
AssistantPanel, AssistantPanel,
Breadcrumb, Breadcrumb,
Buffer, Buffer,
ChatPanel,
Panel, Panel,
ProjectPanel, ProjectPanel,
Tab, Tab,
@ -58,6 +59,7 @@ impl ComponentStory {
} }
Self::Buffer => components::buffer::BufferStory::new().into_any(), Self::Buffer => components::buffer::BufferStory::new().into_any(),
Self::Breadcrumb => components::breadcrumb::BreadcrumbStory::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::Panel => components::panel::PanelStory::new().into_any(),
Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(), Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(),
Self::Tab => components::tab::TabStory::new().into_any(), Self::Tab => components::tab::TabStory::new().into_any(),

View file

@ -1,6 +1,7 @@
mod assistant_panel; mod assistant_panel;
mod breadcrumb; mod breadcrumb;
mod buffer; mod buffer;
mod chat_panel;
mod editor_pane; mod editor_pane;
mod icon_button; mod icon_button;
mod list; mod list;
@ -17,6 +18,7 @@ mod workspace;
pub use assistant_panel::*; pub use assistant_panel::*;
pub use breadcrumb::*; pub use breadcrumb::*;
pub use buffer::*; pub use buffer::*;
pub use chat_panel::*;
pub use editor_pane::*; pub use editor_pane::*;
pub use icon_button::*; pub use icon_button::*;
pub use list::*; pub use list::*;

View file

@ -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<S: 'static + Send + Sync + Clone> {
scroll_state: ScrollState,
messages: Vec<ChatMessage<S>>,
}
impl<S: 'static + Send + Sync + Clone> ChatPanel<S> {
pub fn new(scroll_state: ScrollState) -> Self {
Self {
scroll_state,
messages: Vec::new(),
}
}
pub fn with_messages(mut self, messages: Vec<ChatMessage<S>>) -> Self {
self.messages = messages;
self
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
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<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
author: String,
text: String,
sent_at: NaiveDateTime,
}
impl<S: 'static + Send + Sync + Clone> ChatMessage<S> {
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<S>) -> impl Element<State = S> {
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())))
}
}

View file

@ -6,8 +6,9 @@ use gpui3::{relative, rems, Size};
use crate::prelude::*; use crate::prelude::*;
use crate::{ use crate::{
hello_world_rust_editor_with_status_example, theme, v_stack, EditorPane, Pane, PaneGroup, hello_world_rust_editor_with_status_example, theme, v_stack, ChatMessage, ChatPanel,
Panel, PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, EditorPane, Pane, PaneGroup, Panel, PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection,
StatusBar, Terminal,
}; };
#[derive(Element)] #[derive(Element)]
@ -139,11 +140,7 @@ impl<S: 'static + Send + Sync + Clone> WorkspaceElement<S> {
.child( .child(
Panel::new( Panel::new(
self.bottom_panel_scroll_state.clone(), self.bottom_panel_scroll_state.clone(),
|_, _| { |_, _| vec![Terminal::new().into_any()],
vec![
// Terminal::new().into_any()
]
},
Box::new(()), Box::new(()),
) )
.allowed_sides(PanelAllowedSides::BottomOnly) .allowed_sides(PanelAllowedSides::BottomOnly)
@ -153,42 +150,34 @@ impl<S: 'static + Send + Sync + Clone> WorkspaceElement<S> {
.child( .child(
Panel::new( Panel::new(
self.right_panel_scroll_state.clone(), 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(()), Box::new(()),
) )
.side(PanelSide::Right), .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()) .child(StatusBar::new())
// An example of a toast is below // An example of a toast is below