From f2a1226e18e84c9b1345318ff067a5e487c59dbb Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 30 Apr 2024 17:33:32 -0400 Subject: [PATCH] assistant2: Setup storybook (#11228) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR sets up the `assistant2` crate with the storybook so that UI elements can be iterated on in isolation. To start, we have some stories for the `ChatMessage` component: ```sh cargo run -p storybook -- components/assistant_chat_message ``` Screenshot 2024-04-30 at 5 20 03 PM Release Notes: - N/A --- Cargo.lock | 2 + crates/assistant2/Cargo.toml | 11 ++- crates/assistant2/src/assistant2.rs | 17 ++-- crates/assistant2/src/ui.rs | 6 ++ crates/assistant2/src/ui/composer.rs | 15 ++- crates/assistant2/src/ui/stories.rs | 3 + .../assistant2/src/ui/stories/chat_message.rs | 99 +++++++++++++++++++ crates/storybook/Cargo.toml | 1 + crates/storybook/src/story_selector.rs | 4 + 9 files changed, 139 insertions(+), 19 deletions(-) create mode 100644 crates/assistant2/src/ui/stories.rs create mode 100644 crates/assistant2/src/ui/stories/chat_message.rs diff --git a/Cargo.lock b/Cargo.lock index a27363a584..30de4e97ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,6 +401,7 @@ dependencies = [ "serde", "serde_json", "settings", + "story", "theme", "ui", "util", @@ -9498,6 +9499,7 @@ name = "storybook" version = "0.1.0" dependencies = [ "anyhow", + "assistant2", "clap 4.4.4", "collab_ui", "ctrlc", diff --git a/crates/assistant2/Cargo.toml b/crates/assistant2/Cargo.toml index f6384c716b..0e5b090cf5 100644 --- a/crates/assistant2/Cargo.toml +++ b/crates/assistant2/Cargo.toml @@ -5,9 +5,16 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/assistant2.rs" +[features] +default = [] +stories = ["dep:story"] + [dependencies] anyhow.workspace = true assistant_tooling.workspace = true @@ -29,6 +36,7 @@ semantic_index.workspace = true serde.workspace = true serde_json.workspace = true settings.workspace = true +story = { workspace = true, optional = true } theme.workspace = true ui.workspace = true util.workspace = true @@ -49,6 +57,3 @@ settings = { workspace = true, features = ["test-support"] } theme = { workspace = true, features = ["test-support"] } util = { workspace = true, features = ["test-support"] } workspace = { workspace = true, features = ["test-support"] } - -[lints] -workspace = true diff --git a/crates/assistant2/src/assistant2.rs b/crates/assistant2/src/assistant2.rs index 87dc0c326f..2dc1415371 100644 --- a/crates/assistant2/src/assistant2.rs +++ b/crates/assistant2/src/assistant2.rs @@ -1,7 +1,7 @@ mod assistant_settings; mod completion_provider; mod tools; -mod ui; +pub mod ui; use ::ui::{div, prelude::*, Color, ViewContext}; use anyhow::{Context, Result}; @@ -222,7 +222,7 @@ impl FocusableView for AssistantPanel { } } -struct AssistantChat { +pub struct AssistantChat { model: String, messages: Vec, list_state: ListState, @@ -574,12 +574,15 @@ impl AssistantChat { .map(|element| { if self.editing_message_id.as_ref() == Some(id) { element.child(Composer::new( - cx.view().downgrade(), - self.model.clone(), body.clone(), self.user_store.read(cx).current_user(), self.can_submit(), self.tool_registry.clone(), + crate::ui::ModelSelector::new( + cx.view().downgrade(), + self.model.clone(), + ) + .into_any_element(), )) } else { element @@ -744,18 +747,18 @@ impl Render for AssistantChat { .text_color(Color::Default.color(cx)) .child(list(self.list_state.clone()).flex_1()) .child(Composer::new( - cx.view().downgrade(), - self.model.clone(), self.composer_editor.clone(), self.user_store.read(cx).current_user(), self.can_submit(), self.tool_registry.clone(), + crate::ui::ModelSelector::new(cx.view().downgrade(), self.model.clone()) + .into_any_element(), )) } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -struct MessageId(usize); +pub struct MessageId(usize); impl MessageId { fn post_inc(&mut self) -> Self { diff --git a/crates/assistant2/src/ui.rs b/crates/assistant2/src/ui.rs index 6cdc6c6c39..cfe0e7bf24 100644 --- a/crates/assistant2/src/ui.rs +++ b/crates/assistant2/src/ui.rs @@ -1,5 +1,11 @@ mod chat_message; mod composer; +#[cfg(feature = "stories")] +mod stories; + pub use chat_message::*; pub use composer::*; + +#[cfg(feature = "stories")] +pub use stories::*; diff --git a/crates/assistant2/src/ui/composer.rs b/crates/assistant2/src/ui/composer.rs index 3b0a17c96e..81c1d4340f 100644 --- a/crates/assistant2/src/ui/composer.rs +++ b/crates/assistant2/src/ui/composer.rs @@ -1,7 +1,7 @@ use assistant_tooling::ToolRegistry; use client::User; use editor::{Editor, EditorElement, EditorStyle}; -use gpui::{FontStyle, FontWeight, TextStyle, View, WeakView, WhiteSpace}; +use gpui::{AnyElement, FontStyle, FontWeight, TextStyle, View, WeakView, WhiteSpace}; use settings::Settings; use std::sync::Arc; use theme::ThemeSettings; @@ -11,30 +11,27 @@ use crate::{AssistantChat, CompletionProvider, Submit, SubmitMode}; #[derive(IntoElement)] pub struct Composer { - assistant_chat: WeakView, - model: String, editor: View, player: Option>, can_submit: bool, tool_registry: Arc, + model_selector: AnyElement, } impl Composer { pub fn new( - assistant_chat: WeakView, - model: String, editor: View, player: Option>, can_submit: bool, tool_registry: Arc, + model_selector: AnyElement, ) -> Self { Self { - assistant_chat, - model, editor, player, can_submit, tool_registry, + model_selector, } } } @@ -150,7 +147,7 @@ impl RenderOnce for Composer { h_flex() .w_full() .justify_between() - .child(ModelSelector::new(self.assistant_chat, self.model)) + .child(self.model_selector) .children(self.tool_registry.status_views().iter().cloned()), ), ) @@ -158,7 +155,7 @@ impl RenderOnce for Composer { } #[derive(IntoElement)] -struct ModelSelector { +pub struct ModelSelector { assistant_chat: WeakView, model: String, } diff --git a/crates/assistant2/src/ui/stories.rs b/crates/assistant2/src/ui/stories.rs new file mode 100644 index 0000000000..2aca1f0676 --- /dev/null +++ b/crates/assistant2/src/ui/stories.rs @@ -0,0 +1,3 @@ +mod chat_message; + +pub use chat_message::*; diff --git a/crates/assistant2/src/ui/stories/chat_message.rs b/crates/assistant2/src/ui/stories/chat_message.rs new file mode 100644 index 0000000000..5c5954daf0 --- /dev/null +++ b/crates/assistant2/src/ui/stories/chat_message.rs @@ -0,0 +1,99 @@ +use std::sync::Arc; + +use client::User; +use story::{StoryContainer, StoryItem, StorySection}; +use ui::prelude::*; + +use crate::ui::{ChatMessage, UserOrAssistant}; +use crate::MessageId; + +pub struct ChatMessageStory; + +impl Render for ChatMessageStory { + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + let user_1 = Arc::new(User { + id: 12345, + github_login: "iamnbutler".into(), + avatar_uri: "https://avatars.githubusercontent.com/u/1714999?v=4".into(), + }); + + StoryContainer::new( + "ChatMessage Story", + "crates/assistant2/src/ui/stories/chat_message.rs", + ) + .child( + StorySection::new() + .child(StoryItem::new( + "User chat message", + ChatMessage::new( + MessageId(0), + UserOrAssistant::User(Some(user_1.clone())), + Some(div().child("What can I do here?").into_any_element()), + false, + Box::new(|_, _| {}), + ), + )) + .child(StoryItem::new( + "User chat message (collapsed)", + ChatMessage::new( + MessageId(0), + UserOrAssistant::User(Some(user_1.clone())), + Some(div().child("What can I do here?").into_any_element()), + true, + Box::new(|_, _| {}), + ), + )), + ) + .child( + StorySection::new() + .child(StoryItem::new( + "Assistant chat message", + ChatMessage::new( + MessageId(0), + UserOrAssistant::Assistant, + Some(div().child("You can talk to me!").into_any_element()), + false, + Box::new(|_, _| {}), + ), + )) + .child(StoryItem::new( + "Assistant chat message (collapsed)", + ChatMessage::new( + MessageId(0), + UserOrAssistant::Assistant, + Some(div().child("You can talk to me!").into_any_element()), + true, + Box::new(|_, _| {}), + ), + )), + ) + .child( + StorySection::new().child(StoryItem::new( + "Conversation between user and assistant", + v_flex() + .gap_2() + .child(ChatMessage::new( + MessageId(0), + UserOrAssistant::User(Some(user_1.clone())), + Some(div().child("What is Rust??").into_any_element()), + false, + Box::new(|_, _| {}), + )) + .child(ChatMessage::new( + MessageId(0), + UserOrAssistant::Assistant, + Some(div().child("Rust is a multi-paradigm programming language focused on performance and safety").into_any_element()), + false, + Box::new(|_, _| {}), + )) + .child(ChatMessage::new( + MessageId(0), + UserOrAssistant::User(Some(user_1)), + Some(div().child("Sounds pretty cool!").into_any_element()), + false, + Box::new(|_, _| {}), + )), + )), + ) + } +} diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index 1d7a828dd7..0d084ae847 100644 --- a/crates/storybook/Cargo.toml +++ b/crates/storybook/Cargo.toml @@ -14,6 +14,7 @@ path = "src/storybook.rs" [dependencies] anyhow.workspace = true +assistant2 = { workspace = true, features = ["stories"] } clap = { workspace = true, features = ["derive", "string"] } collab_ui = { workspace = true, features = ["stories"] } ctrlc = "3.4" diff --git a/crates/storybook/src/story_selector.rs b/crates/storybook/src/story_selector.rs index dcc42546fe..5087295ca9 100644 --- a/crates/storybook/src/story_selector.rs +++ b/crates/storybook/src/story_selector.rs @@ -12,6 +12,7 @@ use ui::prelude::*; #[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)] #[strum(serialize_all = "snake_case")] pub enum ComponentStory { + AssistantChatMessage, AutoHeightEditor, Avatar, Button, @@ -42,6 +43,9 @@ pub enum ComponentStory { impl ComponentStory { pub fn story(&self, cx: &mut WindowContext) -> AnyView { match self { + Self::AssistantChatMessage => { + cx.new_view(|_cx| assistant2::ui::ChatMessageStory).into() + } Self::AutoHeightEditor => AutoHeightEditorStory::new(cx).into(), Self::Avatar => cx.new_view(|_| ui::AvatarStory).into(), Self::Button => cx.new_view(|_| ui::ButtonStory).into(),