Begin setting up stories
This commit is contained in:
parent
4b793f44ef
commit
a05cbf8169
17 changed files with 471 additions and 1 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -7606,6 +7606,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"settings",
|
"settings",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
|
"smallvec",
|
||||||
"strum",
|
"strum",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
|
|
|
@ -25,6 +25,18 @@ impl<V: 'static> IntoAnyElement<V> for &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Figure out how to pass `String` to `child` without this.
|
||||||
|
// This impl doesn't exist in the `gpui2` crate.
|
||||||
|
impl<S: 'static> IntoAnyElement<S> for String {
|
||||||
|
fn into_any(self) -> AnyElement<S> {
|
||||||
|
Text {
|
||||||
|
text: ArcCow::from(self),
|
||||||
|
state_type: PhantomData,
|
||||||
|
}
|
||||||
|
.into_any()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Text<S> {
|
pub struct Text<S> {
|
||||||
text: ArcCow<'static, str>,
|
text: ArcCow<'static, str>,
|
||||||
state_type: PhantomData<S>,
|
state_type: PhantomData<S>,
|
||||||
|
|
|
@ -20,6 +20,7 @@ rust-embed.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
simplelog = "0.9"
|
simplelog = "0.9"
|
||||||
|
smallvec.workspace = true
|
||||||
strum = { version = "0.25.0", features = ["derive"] }
|
strum = { version = "0.25.0", features = ["derive"] }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
3
crates/storybook2/src/stories.rs
Normal file
3
crates/storybook2/src/stories.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod components;
|
||||||
|
pub mod elements;
|
||||||
|
pub mod kitchen_sink;
|
1
crates/storybook2/src/stories/components.rs
Normal file
1
crates/storybook2/src/stories/components.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod panel;
|
35
crates/storybook2/src/stories/components/panel.rs
Normal file
35
crates/storybook2/src/stories/components/panel.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::ui::prelude::*;
|
||||||
|
use crate::ui::{Label, Panel};
|
||||||
|
|
||||||
|
use crate::story::Story;
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct PanelStory<S: 'static + Send + Sync> {
|
||||||
|
state_type: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync> PanelStory<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::<_, Panel<S>>(cx))
|
||||||
|
.child(Story::label(cx, "Default"))
|
||||||
|
.child(Panel::new(
|
||||||
|
ScrollState::default(),
|
||||||
|
|_, _| {
|
||||||
|
vec![div()
|
||||||
|
.overflow_y_scroll(ScrollState::default())
|
||||||
|
.children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1))))
|
||||||
|
.into_any()]
|
||||||
|
},
|
||||||
|
Box::new(()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
1
crates/storybook2/src/stories/elements.rs
Normal file
1
crates/storybook2/src/stories/elements.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod label;
|
28
crates/storybook2/src/stories/elements/label.rs
Normal file
28
crates/storybook2/src/stories/elements/label.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::ui::prelude::*;
|
||||||
|
use crate::ui::Label;
|
||||||
|
|
||||||
|
use crate::story::Story;
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct LabelStory<S: 'static + Send + Sync> {
|
||||||
|
state_type: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync> LabelStory<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::<_, Label<S>>(cx))
|
||||||
|
.child(Story::label(cx, "Default"))
|
||||||
|
.child(Label::new("Hello, world!"))
|
||||||
|
.child(Story::label(cx, "Highlighted"))
|
||||||
|
.child(Label::new("Hello, world!").with_highlights(vec![0, 1, 2, 7, 8, 12]))
|
||||||
|
}
|
||||||
|
}
|
36
crates/storybook2/src/stories/kitchen_sink.rs
Normal file
36
crates/storybook2/src/stories/kitchen_sink.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::story::Story;
|
||||||
|
use crate::story_selector::{ComponentStory, ElementStory};
|
||||||
|
use crate::ui::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct KitchenSinkStory<S: 'static + Send + Sync> {
|
||||||
|
state_type: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync> KitchenSinkStory<S> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state_type: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
|
||||||
|
let element_stories = ElementStory::iter().map(|selector| selector.story());
|
||||||
|
let component_stories = ComponentStory::iter().map(|selector| selector.story());
|
||||||
|
|
||||||
|
Story::container(cx)
|
||||||
|
.overflow_y_scroll(ScrollState::default())
|
||||||
|
.child(Story::title(cx, "Kitchen Sink"))
|
||||||
|
.child(Story::label(cx, "Elements"))
|
||||||
|
.child(div().flex().flex_col().children_any(element_stories))
|
||||||
|
.child(Story::label(cx, "Components"))
|
||||||
|
.child(div().flex().flex_col().children_any(component_stories))
|
||||||
|
// Add a bit of space at the bottom of the kitchen sink so elements
|
||||||
|
// don't end up squished right up against the bottom of the screen.
|
||||||
|
.child(div().p_4())
|
||||||
|
}
|
||||||
|
}
|
52
crates/storybook2/src/story.rs
Normal file
52
crates/storybook2/src/story.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use crate::theme::theme;
|
||||||
|
use crate::ui::prelude::*;
|
||||||
|
use gpui3::Div;
|
||||||
|
|
||||||
|
pub struct Story {}
|
||||||
|
|
||||||
|
impl Story {
|
||||||
|
pub fn container<S: 'static + Send + Sync>(cx: &mut ViewContext<S>) -> Div<S> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.size_full()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.pt_2()
|
||||||
|
.px_4()
|
||||||
|
.font("Zed Mono Extended")
|
||||||
|
.fill(theme.lowest.base.default.background)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title<S: 'static + Send + Sync>(
|
||||||
|
cx: &mut ViewContext<S>,
|
||||||
|
title: &str,
|
||||||
|
) -> impl Element<State = S> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.text_xl()
|
||||||
|
.text_color(theme.lowest.base.default.foreground)
|
||||||
|
.child(title.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title_for<S: 'static + Send + Sync, T>(
|
||||||
|
cx: &mut ViewContext<S>,
|
||||||
|
) -> impl Element<State = S> {
|
||||||
|
Self::title(cx, std::any::type_name::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label<S: 'static + Send + Sync>(
|
||||||
|
cx: &mut ViewContext<S>,
|
||||||
|
label: &str,
|
||||||
|
) -> impl Element<State = S> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.mt_4()
|
||||||
|
.mb_2()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(theme.lowest.base.default.foreground)
|
||||||
|
.child(label.to_owned())
|
||||||
|
}
|
||||||
|
}
|
116
crates/storybook2/src/story_selector.rs
Normal file
116
crates/storybook2/src/story_selector.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context};
|
||||||
|
use clap::builder::PossibleValue;
|
||||||
|
use clap::ValueEnum;
|
||||||
|
use gpui3::AnyElement;
|
||||||
|
use strum::{EnumIter, EnumString, IntoEnumIterator};
|
||||||
|
|
||||||
|
use crate::ui::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)]
|
||||||
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
pub enum ElementStory {
|
||||||
|
Label,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElementStory {
|
||||||
|
pub fn story<S: 'static + Send + Sync>(&self) -> AnyElement<S> {
|
||||||
|
use crate::stories::elements;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Label => elements::label::LabelStory::new().into_any(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)]
|
||||||
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
pub enum ComponentStory {
|
||||||
|
Panel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentStory {
|
||||||
|
pub fn story<S: 'static + Send + Sync>(&self) -> AnyElement<S> {
|
||||||
|
use crate::stories::components;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Panel => components::panel::PanelStory::new().into_any(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum StorySelector {
|
||||||
|
Element(ElementStory),
|
||||||
|
Component(ComponentStory),
|
||||||
|
KitchenSink,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for StorySelector {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(raw_story_name: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
|
let story = raw_story_name.to_ascii_lowercase();
|
||||||
|
|
||||||
|
if story == "kitchen_sink" {
|
||||||
|
return Ok(Self::KitchenSink);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((_, story)) = story.split_once("elements/") {
|
||||||
|
let element_story = ElementStory::from_str(story)
|
||||||
|
.with_context(|| format!("story not found for element '{story}'"))?;
|
||||||
|
|
||||||
|
return Ok(Self::Element(element_story));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((_, story)) = story.split_once("components/") {
|
||||||
|
let component_story = ComponentStory::from_str(story)
|
||||||
|
.with_context(|| format!("story not found for component '{story}'"))?;
|
||||||
|
|
||||||
|
return Ok(Self::Component(component_story));
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(anyhow!("story not found for '{raw_story_name}'"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorySelector {
|
||||||
|
pub fn story<S: 'static + Send + Sync>(&self) -> AnyElement<S> {
|
||||||
|
match self {
|
||||||
|
Self::Element(element_story) => element_story.story(),
|
||||||
|
Self::Component(component_story) => component_story.story(),
|
||||||
|
Self::KitchenSink => crate::stories::kitchen_sink::KitchenSinkStory::new().into_any(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of all stories available in the storybook.
|
||||||
|
static ALL_STORY_SELECTORS: OnceLock<Vec<StorySelector>> = OnceLock::new();
|
||||||
|
|
||||||
|
impl ValueEnum for StorySelector {
|
||||||
|
fn value_variants<'a>() -> &'a [Self] {
|
||||||
|
let stories = ALL_STORY_SELECTORS.get_or_init(|| {
|
||||||
|
let element_stories = ElementStory::iter().map(StorySelector::Element);
|
||||||
|
let component_stories = ComponentStory::iter().map(StorySelector::Component);
|
||||||
|
|
||||||
|
element_stories
|
||||||
|
.chain(component_stories)
|
||||||
|
.chain(std::iter::once(StorySelector::KitchenSink))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
});
|
||||||
|
|
||||||
|
stories
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
|
||||||
|
let value = match self {
|
||||||
|
Self::Element(story) => format!("elements/{story}"),
|
||||||
|
Self::Component(story) => format!("components/{story}"),
|
||||||
|
Self::KitchenSink => "kitchen_sink".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(PossibleValue::new(value))
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,9 @@ use workspace::workspace;
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
mod collab_panel;
|
mod collab_panel;
|
||||||
|
mod stories;
|
||||||
|
mod story;
|
||||||
|
mod story_selector;
|
||||||
mod theme;
|
mod theme;
|
||||||
mod themes;
|
mod themes;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
|
@ -2,9 +2,11 @@ mod children;
|
||||||
mod components;
|
mod components;
|
||||||
mod elements;
|
mod elements;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
mod theme;
|
||||||
mod tokens;
|
mod tokens;
|
||||||
|
|
||||||
pub use children::*;
|
pub use children::*;
|
||||||
pub use components::*;
|
pub use components::*;
|
||||||
pub use elements::*;
|
pub use elements::*;
|
||||||
|
pub use theme::*;
|
||||||
pub use tokens::*;
|
pub use tokens::*;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
mod label;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
|
||||||
|
pub use label::*;
|
||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
|
|
165
crates/storybook2/src/ui/elements/label.rs
Normal file
165
crates/storybook2/src/ui/elements/label.rs
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use gpui3::{Hsla, WindowContext};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use crate::theme::theme;
|
||||||
|
use crate::ui::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Copy, Clone)]
|
||||||
|
pub enum LabelColor {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
Muted,
|
||||||
|
Created,
|
||||||
|
Modified,
|
||||||
|
Deleted,
|
||||||
|
Disabled,
|
||||||
|
Hidden,
|
||||||
|
Placeholder,
|
||||||
|
Accent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LabelColor {
|
||||||
|
pub fn hsla(&self, cx: &WindowContext) -> Hsla {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Default => theme.middle.base.default.foreground,
|
||||||
|
Self::Muted => theme.middle.variant.default.foreground,
|
||||||
|
Self::Created => theme.middle.positive.default.foreground,
|
||||||
|
Self::Modified => theme.middle.warning.default.foreground,
|
||||||
|
Self::Deleted => theme.middle.negative.default.foreground,
|
||||||
|
Self::Disabled => theme.middle.base.disabled.foreground,
|
||||||
|
Self::Hidden => theme.middle.variant.default.foreground,
|
||||||
|
Self::Placeholder => theme.middle.base.disabled.foreground,
|
||||||
|
Self::Accent => theme.middle.accent.default.foreground,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Copy, Clone)]
|
||||||
|
pub enum LabelSize {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
Small,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Element, Clone)]
|
||||||
|
pub struct Label<S: 'static + Send + Sync> {
|
||||||
|
state_type: PhantomData<S>,
|
||||||
|
label: String,
|
||||||
|
color: LabelColor,
|
||||||
|
size: LabelSize,
|
||||||
|
highlight_indices: Vec<usize>,
|
||||||
|
strikethrough: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync> Label<S> {
|
||||||
|
pub fn new<L>(label: L) -> Self
|
||||||
|
where
|
||||||
|
L: Into<String>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
state_type: PhantomData,
|
||||||
|
label: label.into(),
|
||||||
|
color: LabelColor::Default,
|
||||||
|
size: LabelSize::Default,
|
||||||
|
highlight_indices: Vec::new(),
|
||||||
|
strikethrough: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn color(mut self, color: LabelColor) -> Self {
|
||||||
|
self.color = color;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(mut self, size: LabelSize) -> Self {
|
||||||
|
self.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
|
||||||
|
self.highlight_indices = indices;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
|
||||||
|
self.strikethrough = strikethrough;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
let highlight_color = theme.lowest.accent.default.foreground;
|
||||||
|
|
||||||
|
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
|
||||||
|
|
||||||
|
let mut runs: SmallVec<[Run; 8]> = SmallVec::new();
|
||||||
|
|
||||||
|
for (char_ix, char) in self.label.char_indices() {
|
||||||
|
let mut color = self.color.hsla(cx);
|
||||||
|
|
||||||
|
if let Some(highlight_ix) = highlight_indices.peek() {
|
||||||
|
if char_ix == *highlight_ix {
|
||||||
|
color = highlight_color;
|
||||||
|
|
||||||
|
highlight_indices.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let last_run = runs.last_mut();
|
||||||
|
|
||||||
|
let start_new_run = if let Some(last_run) = last_run {
|
||||||
|
if color == last_run.color {
|
||||||
|
last_run.text.push(char);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
if start_new_run {
|
||||||
|
runs.push(Run {
|
||||||
|
text: char.to_string(),
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
// .when(self.strikethrough, |this| {
|
||||||
|
// this.relative().child(
|
||||||
|
// div()
|
||||||
|
// .absolute()
|
||||||
|
// .top_px()
|
||||||
|
// .my_auto()
|
||||||
|
// .w_full()
|
||||||
|
// .h_px()
|
||||||
|
// .fill(LabelColor::Hidden.hsla(cx)),
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
.children(runs.into_iter().map(|run| {
|
||||||
|
let mut div = div();
|
||||||
|
|
||||||
|
if self.size == LabelSize::Small {
|
||||||
|
div = div.text_xs();
|
||||||
|
} else {
|
||||||
|
div = div.text_sm();
|
||||||
|
}
|
||||||
|
|
||||||
|
div.text_color(run.color).child(run.text)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A run of text that receives the same style.
|
||||||
|
struct Run {
|
||||||
|
pub text: String,
|
||||||
|
pub color: Hsla,
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
pub use gpui3::{Element, IntoAnyElement, ParentElement, ScrollState, StyleHelpers, ViewContext};
|
pub use gpui3::{
|
||||||
|
div, Element, IntoAnyElement, ParentElement, ScrollState, StyleHelpers, ViewContext,
|
||||||
|
};
|
||||||
|
|
||||||
pub use crate::ui::{HackyChildren, HackyChildrenPayload};
|
pub use crate::ui::{HackyChildren, HackyChildrenPayload};
|
||||||
|
|
10
crates/storybook2/src/ui/theme.rs
Normal file
10
crates/storybook2/src/ui/theme.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use gpui3::WindowContext;
|
||||||
|
|
||||||
|
use crate::theme::Theme;
|
||||||
|
use crate::themes::rose_pine_dawn;
|
||||||
|
|
||||||
|
pub fn theme(cx: &WindowContext) -> Arc<Theme> {
|
||||||
|
Arc::new(rose_pine_dawn())
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue