1use maruzzella::{
2 default_product_spec, run, text_tab, BottomPanelLayout, ButtonAppearance, ButtonStyle,
3 InputAppearance, MaruzzellaConfig, SplitAxis, SurfaceAppearance, SurfaceLevel, TabGroupSpec,
4 TabStripAppearance, TabStripStyle, TextAppearance, TextRole, ThemeSpec, Tone,
5 WorkbenchNodeSpec,
6};
7
8fn main() {
9 let mut product = default_product_spec();
10 product.branding.title = "Notebook".to_string();
11 product.branding.search_placeholder = "Search notes, boards, and logs".to_string();
12 product.branding.status_text = "Example app styled with semantic appearances".to_string();
13
14 product.layout.left_panel = TabGroupSpec::new(
15 "panel-left",
16 Some("spaces"),
17 vec![
18 text_tab(
19 "spaces",
20 "panel-left",
21 "Spaces",
22 "Personal, Team, Archive",
23 false,
24 ),
25 text_tab(
26 "bookmarks",
27 "panel-left",
28 "Bookmarks",
29 "Pinned notes and saved searches.",
30 false,
31 ),
32 ],
33 )
34 .with_panel_appearance("library-panel")
35 .with_panel_header_appearance("library-header")
36 .with_tab_strip_appearance("library-strip");
37
38 product.layout.right_panel = TabGroupSpec::new(
39 "panel-right",
40 Some("details"),
41 vec![
42 text_tab(
43 "details",
44 "panel-right",
45 "Details",
46 "Metadata, tags, and collaborators.",
47 false,
48 ),
49 text_tab(
50 "history",
51 "panel-right",
52 "History",
53 "Recent edits and timeline events.",
54 false,
55 ),
56 ],
57 )
58 .with_panel_appearance("detail-panel")
59 .with_panel_header_appearance("detail-header")
60 .with_tab_strip_appearance("utility");
61
62 product.layout.bottom_panel = TabGroupSpec::new(
63 "panel-bottom",
64 Some("activity"),
65 vec![
66 text_tab(
67 "activity",
68 "panel-bottom",
69 "Activity",
70 "Automations, sync jobs, and notifications.",
71 false,
72 )
73 .with_text_appearance("meta"),
74 text_tab(
75 "diagnostics",
76 "panel-bottom",
77 "Diagnostics",
78 "Parser output and indexing warnings.",
79 false,
80 )
81 .with_text_appearance("code"),
82 ],
83 )
84 .with_panel_appearance("console")
85 .with_panel_header_appearance("detail-header")
86 .with_tab_strip_appearance("console")
87 .with_text_appearance("code");
88 product.layout.bottom_panel_layout = BottomPanelLayout::FullWidth;
89
90 product.layout.workbench = WorkbenchNodeSpec::Split {
91 axis: SplitAxis::Vertical,
92 children: vec![
93 WorkbenchNodeSpec::Group(
94 TabGroupSpec::new(
95 "workbench-main",
96 Some("today"),
97 vec![
98 text_tab(
99 "today",
100 "workbench-main",
101 "Today",
102 "Daily dashboard for notes, tasks, and drafts.",
103 false,
104 ),
105 text_tab(
106 "draft",
107 "workbench-main",
108 "Draft",
109 "Write here. This stays intentionally neutral in the example.",
110 true,
111 ),
112 ],
113 )
114 .with_panel_appearance("writing-surface")
115 .with_panel_header_appearance("detail-header")
116 .with_tab_strip_appearance("editor"),
117 ),
118 WorkbenchNodeSpec::Group(
119 TabGroupSpec::new(
120 "workbench-secondary",
121 Some("board"),
122 vec![
123 text_tab(
124 "board",
125 "workbench-secondary",
126 "Board",
127 "Secondary workbench area for planning and structure.",
128 false,
129 ),
130 text_tab(
131 "review",
132 "workbench-secondary",
133 "Review",
134 "Compare revisions, notes, or external context here.",
135 true,
136 )
137 .with_text_appearance("meta"),
138 ],
139 )
140 .with_panel_appearance("workbench")
141 .with_panel_header_appearance("detail-header")
142 .with_tab_strip_appearance("editor"),
143 ),
144 ],
145 };
146
147 let config = MaruzzellaConfig::new("com.example.notebook")
148 .with_persistence_id("notebook-example")
149 .with_theme(notebook_theme())
150 .with_product(product);
151
152 run(config);
153}
154
155fn notebook_theme() -> ThemeSpec {
156 let mut theme = ThemeSpec::default();
157 theme.typography.font_family = "\"IBM Plex Sans\", \"Cantarell\", sans-serif".to_string();
158 theme.typography.mono_font_family = "\"IBM Plex Mono\", monospace".to_string();
159 theme.typography.font_size_base = 15;
160 theme.typography.font_size_ui = 14;
161 theme.typography.font_size_small = 13;
162 theme.typography.font_size_tiny = 12;
163 theme.typography.font_size_title = 26;
164 theme.palette.bg_0 = "#f5f0e6".to_string();
165 theme.palette.bg_1 = "#ebe1d2".to_string();
166 theme.palette.workbench = "#fbf7f0".to_string();
167 theme.palette.panel_left = "#f1e7d8".to_string();
168 theme.palette.panel_right = "#f4ebde".to_string();
169 theme.palette.panel_bottom = "#e7d9c6".to_string();
170 theme.palette.border = "#ceb89a".to_string();
171 theme.palette.border_strong = "#b99669".to_string();
172 theme.palette.text_0 = "#2f2418".to_string();
173 theme.palette.text_1 = "#5d4a37".to_string();
174 theme.palette.text_2 = "#8a7258".to_string();
175 theme.palette.accent = "#9b5f2b".to_string();
176 theme.palette.accent_strong = "#bc753b".to_string();
177 theme.density.radius_small = 8;
178 theme.density.radius_medium = 14;
179 theme.density.radius_large = 18;
180 theme.density.space_sm = 6;
181 theme.density.space_md = 10;
182 theme.density.space_lg = 14;
183 theme.density.space_xl = 20;
184 theme.density.control_height_small = 26;
185 theme.density.control_height_medium = 36;
186 theme.density.control_height_large = 40;
187 theme.density.toolbar_height = 58;
188 theme.density.tab_height = 36;
189 theme.density.search_width_min = 360;
190 theme.density.search_width_max = 560;
191 theme.density.panel_header_height = 32;
192 theme.density.min_side_panel_width = 266;
193 theme.density.min_bottom_panel_height = 176;
194
195 theme
196 .with_surface_appearance(
197 "app-shell",
198 SurfaceAppearance::new(Tone::Neutral, SurfaceLevel::Sunken, TextRole::Body)
199 .borderless(),
200 )
201 .with_surface_appearance(
202 "topbar",
203 SurfaceAppearance::new(Tone::Primary, SurfaceLevel::Flat, TextRole::BodyStrong),
204 )
205 .with_surface_appearance(
206 "toolbar",
207 SurfaceAppearance::new(Tone::Primary, SurfaceLevel::Raised, TextRole::Body),
208 )
209 .with_surface_appearance(
210 "library-panel",
211 SurfaceAppearance::new(Tone::Primary, SurfaceLevel::Raised, TextRole::Body),
212 )
213 .with_surface_appearance(
214 "library-header",
215 SurfaceAppearance::new(Tone::Primary, SurfaceLevel::Flat, TextRole::SectionLabel),
216 )
217 .with_surface_appearance(
218 "detail-panel",
219 SurfaceAppearance::new(Tone::Secondary, SurfaceLevel::Raised, TextRole::Body),
220 )
221 .with_surface_appearance(
222 "detail-header",
223 SurfaceAppearance::new(Tone::Secondary, SurfaceLevel::Flat, TextRole::SectionLabel),
224 )
225 .with_surface_appearance(
226 "writing-surface",
227 SurfaceAppearance::new(Tone::Neutral, SurfaceLevel::Raised, TextRole::Body)
228 .borderless(),
229 )
230 .with_button_appearance(
231 "primary",
232 ButtonAppearance::new(Tone::Accent, ButtonStyle::Solid, TextRole::BodyStrong),
233 )
234 .with_button_appearance(
235 "secondary",
236 ButtonAppearance::new(Tone::Primary, ButtonStyle::Soft, TextRole::Body),
237 )
238 .with_button_appearance(
239 "ghost",
240 ButtonAppearance::new(Tone::Secondary, ButtonStyle::Ghost, TextRole::Body),
241 )
242 .with_input_appearance(
243 "search",
244 InputAppearance::new(Tone::Secondary, SurfaceLevel::Sunken, TextRole::Body),
245 )
246 .with_text_appearance(
247 "title",
248 TextAppearance {
249 role: TextRole::Title,
250 tone: Tone::Primary,
251 },
252 )
253 .with_text_appearance(
254 "meta",
255 TextAppearance {
256 role: TextRole::Meta,
257 tone: Tone::Secondary,
258 },
259 )
260 .with_tab_strip_appearance(
261 "library-strip",
262 TabStripAppearance::new(Tone::Primary, TabStripStyle::Utility, TextRole::TabLabel),
263 )
264 .with_tab_strip_appearance(
265 "editor",
266 TabStripAppearance::new(Tone::Neutral, TabStripStyle::Editor, TextRole::TabLabel),
267 )
268 .with_tab_strip_appearance(
269 "console",
270 TabStripAppearance::new(Tone::Tertiary, TabStripStyle::Console, TextRole::TabLabel),
271 )
272}