website/
main.rs

1#![cfg_attr(
2    all(not(debug_assertions), target_os = "windows"),
3    windows_subsystem = "windows"
4)]
5
6use freya::{
7    prelude::*,
8    text_edit::Rope,
9};
10use tree_sitter_highlight::{
11    HighlightEvent,
12    Highlighter,
13};
14
15fn main() {
16    launch(
17        LaunchConfig::new().with_window(
18            WindowConfig::new(app)
19                .with_title("like freyaui.dev but in freya")
20                .with_size(1500.0, 900.0),
21        ),
22    )
23}
24
25fn app() -> Element {
26    use_init_theme(|| DARK_THEME);
27
28    rect()
29        .background((24, 24, 27))
30        .color((255, 255, 255))
31        .child(
32            ScrollView::new().child(
33                rect()
34                    .cross_align(Alignment::Center)
35                    .width(Size::fill())
36                    .child(
37                        rect()
38                            .width(Size::percent(60.))
39                            .spacing(40.0)
40                            .padding((40.0, 0.0))
41                            .child(Navigation)
42                            .child(Home),
43                    ),
44            ),
45        )
46        .into()
47}
48
49#[derive(PartialEq)]
50struct Home;
51impl Render for Home {
52    fn render(&self) -> impl IntoElement {
53        rect()
54            .cross_align(Alignment::Center)
55            .width(Size::fill())
56            .spacing(40.0)
57            .child(
58                rect()
59                    .direction(Direction::Horizontal)
60                    .cross_align(Alignment::Center)
61                    .spacing(12.0)
62                    .child(svg(Bytes::from_static(include_bytes!("./freya_icon.svg"))).width(Size::px(150.)).height(Size::px(150.)))
63                    .child(
64                        rect()
65                            .spacing(16.0)
66                            .child(
67                                paragraph()
68                                    .width(Size::px(500.0))
69                                    .span(Span::new("Build native & cross-platform GUI applications using 🦀 Rust. Powered by 🧬 "))
70                                    .span(Span::new("Dioxus "))
71                                    .span(Span::new(" and 🎨 "))
72                                    .span(Span::new("Skia.")),
73                            )
74                            .child(
75                                rect()
76                                    .direction(Direction::Horizontal)
77                                    .spacing(10.0)
78                                    .child(Link::new("https://book.freyaui.dev/getting_started.html")
79                                        .child(Button::new()
80                                            .padding((10., 24.))
81                                            .background((14, 165, 233))
82                                            .hover_background((2, 132, 199))
83                                            .border_fill(Color::TRANSPARENT)
84                                            .color(Color::BLACK)
85                                            .child("Get Started")
86                                        )
87                                    )
88                                    .child(Link::new("https://github.com/marc2332/freya")
89                                        .child(Button::new()
90                                            .padding((10., 24.))
91                                            .background((253, 186, 116))
92                                            .hover_background((251, 146, 60))
93                                            .border_fill(Color::TRANSPARENT)
94                                            .color(Color::BLACK)
95                                            .child("Source Code")
96                                        )
97                                    )
98                                    .child(Link::new("https://github.com/sponsors/marc2332")
99                                        .child(Button::new()
100                                            .padding((10., 24.))
101                                            .background((249, 168, 212))
102                                            .hover_background((244, 114, 182))
103                                            .border_fill(Color::TRANSPARENT)
104                                            .color(Color::BLACK)
105                                            .child("Sponsor")
106                                        )
107                                    )
108                            ),
109                    ),
110            )
111            .child(
112                rect()
113                    .width(Size::fill())
114                    .cross_align(Alignment::Center)
115                    .child(
116                        rect()
117                            .background((19, 19, 21))
118                            .border(Border::new().alignment(BorderAlignment::Inner).fill((41, 37, 36)).width(1.0))
119                            .corner_radius(16.0)
120                            .width(Size::px(960.0))
121                            .height(Size::px(700.0))
122                            .spacing(20.0)
123                            .padding((32.0, 24.0))
124                            .direction(Direction::Horizontal)
125                            .child(
126                                rect()
127                                    .width(Size::func(|c| Some(c.parent / 2. - 20.)))
128                                    .height(Size::fill())
129                                    .cross_align(Alignment::Center)
130                                    .child(Code),
131                            )
132                            .child(
133                                rect()
134                                    .width(Size::func(|c| Some(c.parent / 2. - 20.)))
135                                    .height(Size::fill())
136                                    .padding((20.0, 20.0))
137                                    .spacing(20.0)
138                                    .child(rect().height(Size::percent(90.)).child(Counter))
139                                    .child(
140                                        rect()
141                                            .height(Size::fill())
142                                            .width(Size::fill())
143                                            .cross_align(Alignment::Center)
144                                            .child(
145                                                Link::new("https://github.com/marc2332/freya#want-to-try-it-")
146                                                    .child(Button::new()
147                                                        .padding((10., 24.))
148                                                        .background((109, 78, 233))
149                                                        .hover_background((87, 62, 186))
150                                                        .border_fill(Color::TRANSPARENT)
151                                                        .color(Color::WHITE)
152                                                        .child("Run Locally")
153                                                    )
154                                            ),
155                                    ),
156                            ),
157                    ),
158            )
159    }
160}
161
162#[derive(PartialEq)]
163struct Navigation;
164impl Render for Navigation {
165    fn render(&self) -> impl IntoElement {
166        rect()
167            .direction(Direction::Horizontal)
168            .spacing(24.0)
169            .cross_align(Alignment::Center)
170            .color((214, 211, 209))
171            .child(
172                svg(Bytes::from_static(include_bytes!("./freya_icon.svg")))
173                    .width(Size::px(50.))
174                    .height(Size::px(50.)),
175            )
176            .child(
177                svg(Bytes::from_static(include_bytes!("./freya_logo.svg")))
178                    .width(Size::px(50.))
179                    .height(Size::px(50.)),
180            )
181            .child(Link::new("https://freyaui.dev/blog").child("Blog"))
182            .child(Link::new("https://book.freyaui.dev/").child("Book"))
183            .child(Link::new("https://docs.rs/freya/latest/freya/").child("Docs"))
184            .child(Link::new("https://discord.gg/sYejxCdewG").child("Discord"))
185    }
186}
187
188#[derive(PartialEq)]
189struct Counter;
190impl Render for Counter {
191    fn render(&self) -> impl IntoElement {
192        use_init_theme(|| LIGHT_THEME);
193        let mut count = use_state(|| 4);
194
195        rect()
196            .corner_radius(16.)
197            .overflow(Overflow::Clip)
198            .shadow(Shadow::new().blur(10.).color((0, 0, 0, 0.3)))
199            .child(
200                rect()
201                    .width(Size::fill())
202                    .height(Size::percent(50.))
203                    .center()
204                    .color((255, 255, 255))
205                    .background((15, 163, 242))
206                    .font_size(75.)
207                    .shadow((0., 4., 20., 4., (0, 0, 0, 80)))
208                    .child(count.read().to_string()),
209            )
210            .child(
211                rect()
212                    .horizontal()
213                    .background((255, 255, 255))
214                    .width(Size::fill())
215                    .height(Size::percent(50.))
216                    .center()
217                    .spacing(8.0)
218                    .child(
219                        Button::new()
220                            .on_press(move |_| {
221                                *count.write() += 1;
222                            })
223                            .child("Increase"),
224                    )
225                    .child(
226                        Button::new()
227                            .on_press(move |_| {
228                                *count.write() -= 1;
229                            })
230                            .child("Decrease"),
231                    ),
232            )
233    }
234}
235
236#[derive(PartialEq)]
237struct Code;
238impl Render for Code {
239    fn render(&self) -> impl IntoElement {
240        let code = use_hook(move || {
241            use tree_sitter_highlight::HighlightConfiguration;
242
243            let mut rust_config = HighlightConfiguration::new(
244                tree_sitter_rust::LANGUAGE.into(),
245                "rust",
246                tree_sitter_rust::HIGHLIGHTS_QUERY,
247                tree_sitter_rust::INJECTIONS_QUERY,
248                tree_sitter_rust::TAGS_QUERY,
249            )
250            .unwrap();
251
252            rust_config.configure(&HIGHLIGH_TAGS);
253
254            let mut highlighter = Highlighter::new();
255
256            let highlights = highlighter
257                .highlight(&rust_config, CODE.as_bytes(), None, |_| None)
258                .unwrap();
259
260            let rope = Rope::from_str(CODE);
261
262            let mut syntax_blocks = SyntaxBlocks::default();
263
264            let mut prepared_block: (SyntaxType, Vec<(usize, String)>) =
265                (SyntaxType::Unknown, Vec::new());
266
267            for event in highlights {
268                match event.unwrap() {
269                    HighlightEvent::Source { start, end } => {
270                        // Prepare the whole block even if it's splitted across multiple lines.
271                        let data_begining = rope.byte_slice(start..end);
272                        let starting_line = rope.char_to_line(start);
273
274                        let mut back = String::new();
275                        let mut line = starting_line;
276
277                        for (i, d) in data_begining.chars().enumerate() {
278                            if d != '\n' {
279                                back.push(d);
280                            }
281
282                            if start + i == end - 1 || d == '\n' {
283                                prepared_block.1.push((line, back.clone()));
284                                line += 1;
285                                back.clear();
286                            }
287                        }
288                    }
289                    HighlightEvent::HighlightStart(s) => {
290                        // Specify the type of the block
291                        prepared_block.0 = SyntaxType::from(HIGHLIGH_TAGS[s.0]);
292                    }
293                    HighlightEvent::HighlightEnd => {
294                        // Push all the block chunks to their specified line
295                        for (i, d) in prepared_block.1 {
296                            if syntax_blocks.get(i).is_none() {
297                                syntax_blocks.push(Vec::new());
298                            }
299                            let line = syntax_blocks.last_mut().unwrap();
300                            line.push((prepared_block.0.clone(), d));
301                        }
302                        // Clear the prepared block
303                        prepared_block = (SyntaxType::Unknown, Vec::new());
304                    }
305                }
306            }
307
308            // Mark all the remaining text as not readable
309            if !prepared_block.1.is_empty() {
310                for (i, d) in prepared_block.1 {
311                    if syntax_blocks.get(i).is_none() {
312                        syntax_blocks.push(Vec::new());
313                    }
314                    let line = syntax_blocks.last_mut().unwrap();
315                    line.push((SyntaxType::Unknown, d));
316                }
317            }
318
319            syntax_blocks
320        });
321
322        let mut container = rect();
323
324        for (line_n, line) in code.iter().enumerate() {
325            let mut p = paragraph()
326                .key(line_n)
327                .font_size(12.0)
328                .font_family("Jetbrains Mono");
329            // .line_height(1.3);
330
331            for (syntax_type, text) in line.iter() {
332                p = p.span(Span::new(text.clone()).color(syntax_type.color()));
333            }
334
335            container = container.child(p);
336        }
337
338        container
339    }
340}
341
342const CODE: &str = r#"fn app() -> impl IntoElement {
343    let mut count = use_state(|| 4);
344
345    let counter = rect()
346        .width(Size::fill())
347        .height(Size::percent(50.))
348        .center()
349        .color((255, 255, 255))
350        .background((15, 163, 242))
351        .font_size(75.)
352        .shadow((0., 4., 20., 4., (0, 0, 0, 80)))
353        .child(count.read().to_string());
354
355    let actions = rect()
356        .horizontal()
357        .width(Size::fill())
358        .height(Size::percent(50.))
359        .center()
360        .spacing(8.0)
361        .child(
362            Button::new()
363                .on_press(move |_| {
364                    *count.write() += 1;
365                })
366                .child("Increase"),
367        )
368        .child(
369            Button::new()
370                .on_press(move |_| {
371                    *count.write() -= 1;
372                })
373                .child("Decrease"),
374        );
375
376    rect().child(counter).child(actions)
377}"#;
378
379const HIGHLIGH_TAGS: [&str; 23] = [
380    "constructor",
381    "attribute",
382    "constant",
383    "constant.builtin",
384    "function.builtin",
385    "function",
386    "function.method",
387    "keyword",
388    "operator",
389    "property",
390    "punctuation",
391    "punctuation.bracket",
392    "punctuation.delimiter",
393    "string",
394    "string.special",
395    "tag",
396    "type",
397    "type.builtin",
398    "variable",
399    "variable.builtin",
400    "variable.parameter",
401    "number",
402    "comment",
403];
404
405#[derive(Clone, Debug)]
406pub enum SyntaxType {
407    Number,
408    String,
409    Keyword,
410    Operator,
411    Variable,
412    Function,
413    Comment,
414    Punctuation,
415    Unknown,
416}
417
418impl SyntaxType {
419    pub fn color(&self) -> Color {
420        match self {
421            SyntaxType::Number => Color::from_hex("#9ECBFF").unwrap(),
422            SyntaxType::String => Color::from_hex("#9ECBFF").unwrap(),
423            SyntaxType::Keyword => Color::from_hex("#F97583").unwrap(),
424            SyntaxType::Operator => Color::from_hex("#F97583").unwrap(),
425            SyntaxType::Variable => Color::WHITE,
426            SyntaxType::Function => Color::from_hex("#B392F0").unwrap(),
427            SyntaxType::Comment => Color::GREEN,
428            SyntaxType::Punctuation => Color::WHITE,
429            SyntaxType::Unknown => Color::WHITE,
430        }
431    }
432}
433
434impl From<&str> for SyntaxType {
435    fn from(s: &str) -> Self {
436        match s {
437            "keyword" => SyntaxType::Keyword,
438            "variable" => SyntaxType::Variable,
439            "operator" => SyntaxType::Operator,
440            "string" => SyntaxType::String,
441            "number" => SyntaxType::Number,
442            "function" => SyntaxType::Function,
443            "constructor" => SyntaxType::Function,
444            "comment" => SyntaxType::Comment,
445            "punctuation.bracket" => SyntaxType::Punctuation,
446            _ => SyntaxType::Unknown,
447        }
448    }
449}
450
451pub type SyntaxBlocks = Vec<Vec<(SyntaxType, String)>>;