Skip to main content

notebook/
notebook.rs

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}