1mod canvas;
16mod chords;
17mod chrome;
18mod control;
19mod diagrams;
20mod editor;
21mod exchange;
22mod frame_rate;
23mod icons;
24mod library;
25mod log;
26mod meter;
27mod mode;
28mod notices;
29mod overlay;
30mod pacing;
31mod palette;
32mod prefs;
33mod settings;
34mod sheet;
35mod shell;
36mod sidebar;
37mod status_line;
38mod tool_cluster;
39mod top_bar;
40
41#[cfg(test)]
42mod snapshots;
43
44use std::cell::RefCell;
45
46use blockworx_store::doc::Viewing;
47use dioxus::prelude::*;
48
49use crate::canvas::Canvas;
50use crate::editor::InPlaceEditor;
51use crate::exchange::FilePick;
52use crate::frame_rate::FrameRateBadge;
53use crate::mode::Prefers;
54use crate::notices::{Notices, Toast};
55use crate::overlay::SelectionOverlay;
56use crate::palette::Palette;
57use crate::prefs::Store;
58use crate::shell::Shell;
59use crate::sidebar::{ActivityBar, SidebarPanel};
60use crate::status_line::StatusLine;
61use crate::tool_cluster::ToolCluster;
62use crate::top_bar::TopBar;
63
64const TAILWIND: Asset = asset!("/assets/tailwind.css");
67
68thread_local! {
74 static OPENED: RefCell<Option<library::Opened>> = const { RefCell::new(None) };
75}
76
77fn main() {
78 log::install();
79 shell::spawn(async {
80 let opened = library::Library::opening(Store::of(shell::window().as_ref())).await;
81 OPENED.with(|cell| cell.replace(Some(opened)));
82 dioxus::launch(App);
83 });
84}
85
86#[component]
87fn App() -> Element {
88 let (shell, chrome, store, prefs, prefers) = use_hook(|| {
93 let window = shell::window();
94 let store = Store::of(window.as_ref());
95 let prefs = store.read();
96 let prefers = window.as_ref().map_or_else(Prefers::default, Prefers::of);
97 editor::register_faces();
98 let opened = OPENED
99 .with(|cell| cell.take())
100 .unwrap_or_else(library::Opened::detached);
101 let (shell, chrome) = Shell::opening(&prefs, prefers, opened);
102 (
103 shell,
104 chrome,
105 store,
106 Signal::new(prefs),
107 Signal::new(prefers),
108 )
109 });
110
111 use_hook({
112 let shell = shell.clone();
113 move || canvas::listens(&shell)
114 });
115
116 use_hook(|| {
119 if let Some(window) = shell::window() {
120 let mut prefers = prefers;
121 mode::watch(&window, move |told| prefers.set(told));
122 }
123 });
124
125 use_effect({
128 let shell = shell.clone();
129 move || {
130 let prefs = prefs.read();
131 store.write(&prefs);
132 shell.wears(&prefs, prefers());
133 }
134 });
135
136 let at = prefs.read().luminance(prefers());
137 let field = chrome.read().edit_text.clone();
138 let vantage = chrome.read().vantage;
139 let viewing = matches!(chrome.read().top_bar.lens.viewing, Viewing::Past(_));
142 rsx! {
143 document::Stylesheet { href: TAILWIND }
144 div {
145 class: "{mode::root_class(at)} group flex h-screen w-screen flex-col \
146 overflow-hidden bg-white text-zinc-900 dark:bg-zinc-950 dark:text-zinc-100",
147 "data-viewing": if viewing { "true" } else { "false" },
148 TopBar { shell: shell.clone() }
149 div { class: "flex min-h-0 flex-1",
150 ActivityBar { shell: shell.clone() }
151 div { class: "relative min-h-0 min-w-0 flex-1",
152 Canvas { shell: shell.clone() }
153 ToolCluster { shell: shell.clone() }
154 SelectionOverlay { shell: shell.clone() }
155 if let Some(field) = field {
156 InPlaceEditor {
157 key: "{field.id:?}",
158 shell: shell.clone(),
159 field,
160 vantage,
161 typeface: shell.typeface(),
162 }
163 }
164 Notices { shell: shell.clone() }
165 Toast { shell: shell.clone() }
166 StatusLine { shell: shell.clone() }
167 FrameRateBadge { shell: shell.clone() }
168 SidebarPanel { shell: shell.clone(), prefs, prefers }
169 }
170 }
171 Palette { shell: shell.clone(), open: shell.palette() }
172 FilePick { shell }
173 }
174 }
175}