1mod assets;
2mod stories;
3mod story_selector;
4
5use clap::Parser;
6use dialoguer::FuzzySelect;
7use gpui::{
8 div, px, size, AnyView, AppContext, Bounds, Render, ViewContext, VisualContext, WindowBounds,
9 WindowOptions,
10};
11use log::LevelFilter;
12use settings::{default_settings, Settings, SettingsStore};
13use simplelog::SimpleLogger;
14use strum::IntoEnumIterator;
15use theme::{ThemeRegistry, ThemeSettings};
16use ui::prelude::*;
17
18use crate::assets::Assets;
19use crate::story_selector::{ComponentStory, StorySelector};
20pub use indoc::indoc;
21
22#[derive(Parser)]
23#[command(author, version, about, long_about = None)]
24struct Args {
25 #[arg(value_enum)]
26 story: Option<StorySelector>,
27
28 /// The name of the theme to use in the storybook.
29 ///
30 /// If not provided, the default theme will be used.
31 #[arg(long)]
32 theme: Option<String>,
33}
34
35fn main() {
36 // unsafe { backtrace_on_stack_overflow::enable() };
37
38 SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
39
40 let args = Args::parse();
41
42 let story_selector = args.story.clone().unwrap_or_else(|| {
43 let stories = ComponentStory::iter().collect::<Vec<_>>();
44
45 ctrlc::set_handler(move || {}).unwrap();
46
47 let result = FuzzySelect::new()
48 .with_prompt("Choose a story to run:")
49 .items(&stories)
50 .interact();
51
52 let Ok(selection) = result else {
53 dialoguer::console::Term::stderr().show_cursor().unwrap();
54 std::process::exit(0);
55 };
56
57 StorySelector::Component(stories[selection])
58 });
59 let theme_name = args.theme.unwrap_or("One Dark".to_string());
60
61 gpui::App::new().with_assets(Assets).run(move |cx| {
62 load_embedded_fonts(cx).unwrap();
63
64 let mut store = SettingsStore::default();
65 store
66 .set_default_settings(default_settings().as_ref(), cx)
67 .unwrap();
68 cx.set_global(store);
69
70 theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
71
72 let selector = story_selector;
73
74 let theme_registry = ThemeRegistry::global(cx);
75 let mut theme_settings = ThemeSettings::get_global(cx).clone();
76 theme_settings.active_theme = theme_registry.get(&theme_name).unwrap();
77 ThemeSettings::override_global(theme_settings, cx);
78
79 language::init(cx);
80 editor::init(cx);
81
82 let _window = cx.open_window(
83 WindowOptions {
84 bounds: WindowBounds::Fixed(Bounds {
85 origin: Default::default(),
86 size: size(px(1500.), px(780.)).into(),
87 }),
88 ..Default::default()
89 },
90 move |cx| {
91 let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
92 cx.set_rem_size(ui_font_size);
93
94 cx.new_view(|cx| StoryWrapper::new(selector.story(cx)))
95 },
96 );
97
98 cx.activate(true);
99 });
100}
101
102#[derive(Clone)]
103pub struct StoryWrapper {
104 story: AnyView,
105}
106
107impl StoryWrapper {
108 pub(crate) fn new(story: AnyView) -> Self {
109 Self { story }
110 }
111}
112
113impl Render for StoryWrapper {
114 fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
115 div()
116 .flex()
117 .flex_col()
118 .size_full()
119 .font("Zed Mono")
120 .child(self.story.clone())
121 }
122}
123
124fn load_embedded_fonts(cx: &AppContext) -> gpui::Result<()> {
125 let font_paths = cx.asset_source().list("fonts")?;
126 let mut embedded_fonts = Vec::new();
127 for font_path in font_paths {
128 if font_path.ends_with(".ttf") {
129 let font_bytes = cx.asset_source().load(&font_path)?;
130 embedded_fonts.push(font_bytes);
131 }
132 }
133
134 cx.text_system().add_fonts(embedded_fonts)
135}