Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:34:37.447Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

theme_preview.rs

379 lines · 12.0 KB · rust
1#![allow(unused, dead_code)]
2use gpui::{Hsla, Length};
3use std::{
4    cell::LazyCell,
5    sync::{Arc, LazyLock, OnceLock},
6};
7use theme::{Theme, ThemeColors, ThemeRegistry};
8use ui::{
9    IntoElement, RenderOnce, component_prelude::Documented, prelude::*, utils::inner_corner_radius,
10};
11
12#[derive(Clone, PartialEq)]
13pub enum ThemePreviewStyle {
14    Bordered,
15    Borderless,
16    SideBySide(Arc<Theme>),
17}
18
19/// Shows a preview of a theme as an abstract illustration
20/// of a thumbnail-sized editor.
21#[derive(IntoElement, RegisterComponent, Documented)]
22pub struct ThemePreviewTile {
23    theme: Arc<Theme>,
24    seed: f32,
25    style: ThemePreviewStyle,
26}
27
28static CHILD_RADIUS: LazyLock<Pixels> = LazyLock::new(|| {
29    inner_corner_radius(
30        ThemePreviewTile::ROOT_RADIUS,
31        ThemePreviewTile::ROOT_BORDER,
32        ThemePreviewTile::ROOT_PADDING,
33        ThemePreviewTile::CHILD_BORDER,
34    )
35});
36
37impl ThemePreviewTile {
38    pub const SKELETON_HEIGHT_DEFAULT: Pixels = px(2.);
39    pub const SIDEBAR_SKELETON_ITEM_COUNT: usize = 8;
40    pub const SIDEBAR_WIDTH_DEFAULT: DefiniteLength = relative(0.25);
41    pub const ROOT_RADIUS: Pixels = px(8.0);
42    pub const ROOT_BORDER: Pixels = px(2.0);
43    pub const ROOT_PADDING: Pixels = px(2.0);
44    pub const CHILD_BORDER: Pixels = px(1.0);
45
46    pub fn new(theme: Arc<Theme>, seed: f32) -> Self {
47        Self {
48            theme,
49            seed,
50            style: ThemePreviewStyle::Bordered,
51        }
52    }
53
54    pub fn style(mut self, style: ThemePreviewStyle) -> Self {
55        self.style = style;
56        self
57    }
58
59    pub fn item_skeleton(w: Length, h: Length, bg: Hsla) -> impl IntoElement {
60        div().w(w).h(h).rounded_full().bg(bg)
61    }
62
63    pub fn render_sidebar_skeleton_items(
64        seed: f32,
65        colors: &ThemeColors,
66        skeleton_height: impl Into<Length> + Clone,
67    ) -> [impl IntoElement; Self::SIDEBAR_SKELETON_ITEM_COUNT] {
68        let skeleton_height = skeleton_height.into();
69        std::array::from_fn(|index| {
70            let width = {
71                let value = (seed * 1000.0 + index as f32 * 10.0).sin() * 0.5 + 0.5;
72                0.5 + value * 0.45
73            };
74            Self::item_skeleton(
75                relative(width).into(),
76                skeleton_height,
77                colors.text.alpha(0.45),
78            )
79        })
80    }
81
82    pub fn render_pseudo_code_skeleton(
83        seed: f32,
84        theme: Arc<Theme>,
85        skeleton_height: impl Into<Length>,
86    ) -> impl IntoElement {
87        let colors = theme.colors();
88        let syntax = theme.syntax();
89
90        let keyword_color = syntax.style_for_name("keyword").and_then(|s| s.color);
91        let function_color = syntax.style_for_name("function").and_then(|s| s.color);
92        let string_color = syntax.style_for_name("string").and_then(|s| s.color);
93        let comment_color = syntax.style_for_name("comment").and_then(|s| s.color);
94        let variable_color = syntax.style_for_name("variable").and_then(|s| s.color);
95        let type_color = syntax.style_for_name("type").and_then(|s| s.color);
96        let punctuation_color = syntax.style_for_name("punctuation").and_then(|s| s.color);
97
98        let syntax_colors = [
99            keyword_color,
100            function_color,
101            string_color,
102            variable_color,
103            type_color,
104            punctuation_color,
105            comment_color,
106        ];
107
108        let skeleton_height = skeleton_height.into();
109
110        let line_width = |line_idx: usize, block_idx: usize| -> f32 {
111            let val =
112                (seed * 100.0 + line_idx as f32 * 20.0 + block_idx as f32 * 5.0).sin() * 0.5 + 0.5;
113            0.05 + val * 0.2
114        };
115
116        let indentation = |line_idx: usize| -> f32 {
117            let step = line_idx % 6;
118            if step < 3 {
119                step as f32 * 0.1
120            } else {
121                (5 - step) as f32 * 0.1
122            }
123        };
124
125        let pick_color = |line_idx: usize, block_idx: usize| -> Hsla {
126            let idx = ((seed * 10.0 + line_idx as f32 * 7.0 + block_idx as f32 * 3.0).sin() * 3.5)
127                .abs() as usize
128                % syntax_colors.len();
129            syntax_colors[idx].unwrap_or(colors.text)
130        };
131
132        let line_count = 10;
133
134        let lines = (0..line_count)
135            .map(|line_idx| {
136                let block_count = (((seed * 30.0 + line_idx as f32 * 12.0).sin() * 0.5 + 0.5) * 3.0)
137                    .round() as usize
138                    + 2;
139
140                let indent = indentation(line_idx);
141
142                let blocks = (0..block_count)
143                    .map(|block_idx| {
144                        let width = line_width(line_idx, block_idx);
145                        let color = pick_color(line_idx, block_idx);
146                        Self::item_skeleton(relative(width).into(), skeleton_height, color)
147                    })
148                    .collect::<Vec<_>>();
149
150                h_flex().gap_0p5().ml(relative(indent)).children(blocks)
151            })
152            .collect::<Vec<_>>();
153
154        v_flex().size_full().p_1().gap_1p5().children(lines)
155    }
156
157    pub fn render_sidebar(
158        seed: f32,
159        colors: &ThemeColors,
160        width: impl Into<Length> + Clone,
161        skeleton_height: impl Into<Length>,
162    ) -> impl IntoElement {
163        v_flex()
164            .h_full()
165            .w(width)
166            .p_2()
167            .gap_1()
168            .bg(colors.panel_background)
169            .children(Self::render_sidebar_skeleton_items(
170                seed,
171                colors,
172                skeleton_height.into(),
173            ))
174    }
175
176    pub fn render_pane(
177        seed: f32,
178        theme: Arc<Theme>,
179        skeleton_height: impl Into<Length>,
180    ) -> impl IntoElement {
181        div()
182            .p_2()
183            .size_full()
184            .overflow_hidden()
185            .bg(theme.colors().editor_background)
186            .child(Self::render_pseudo_code_skeleton(
187                seed,
188                theme,
189                skeleton_height.into(),
190            ))
191    }
192
193    pub fn render_editor(
194        seed: f32,
195        theme: Arc<Theme>,
196        sidebar_width: impl Into<Length> + Clone,
197        skeleton_height: impl Into<Length> + Clone,
198    ) -> impl IntoElement {
199        div()
200            .flex()
201            .size_full()
202            .bg(theme.colors().background.alpha(1.00))
203            .child(Self::render_sidebar(
204                seed,
205                theme.colors(),
206                sidebar_width,
207                skeleton_height.clone(),
208            ))
209            .child(Self::render_pane(seed, theme, skeleton_height))
210    }
211
212    fn render_borderless(seed: f32, theme: Arc<Theme>) -> impl IntoElement {
213        Self::render_editor(
214            seed,
215            theme,
216            Self::SIDEBAR_WIDTH_DEFAULT,
217            Self::SKELETON_HEIGHT_DEFAULT,
218        )
219    }
220
221    fn render_border(seed: f32, theme: Arc<Theme>) -> impl IntoElement {
222        div()
223            .size_full()
224            .p(Self::ROOT_PADDING)
225            .rounded(Self::ROOT_RADIUS)
226            .child(
227                div()
228                    .size_full()
229                    .rounded(*CHILD_RADIUS)
230                    .border(Self::CHILD_BORDER)
231                    .border_color(theme.colors().border)
232                    .child(Self::render_editor(
233                        seed,
234                        theme.clone(),
235                        Self::SIDEBAR_WIDTH_DEFAULT,
236                        Self::SKELETON_HEIGHT_DEFAULT,
237                    )),
238            )
239    }
240
241    fn render_side_by_side(
242        seed: f32,
243        theme: Arc<Theme>,
244        other_theme: Arc<Theme>,
245        border_color: Hsla,
246    ) -> impl IntoElement {
247        let sidebar_width = relative(0.20);
248
249        div()
250            .size_full()
251            .p(Self::ROOT_PADDING)
252            .rounded(Self::ROOT_RADIUS)
253            .child(
254                h_flex()
255                    .size_full()
256                    .relative()
257                    .rounded(*CHILD_RADIUS)
258                    .border(Self::CHILD_BORDER)
259                    .border_color(border_color)
260                    .overflow_hidden()
261                    .child(div().size_full().child(Self::render_editor(
262                        seed,
263                        theme,
264                        sidebar_width,
265                        Self::SKELETON_HEIGHT_DEFAULT,
266                    )))
267                    .child(
268                        div()
269                            .size_full()
270                            .absolute()
271                            .left_1_2()
272                            .bg(other_theme.colors().editor_background)
273                            .child(Self::render_editor(
274                                seed,
275                                other_theme,
276                                sidebar_width,
277                                Self::SKELETON_HEIGHT_DEFAULT,
278                            )),
279                    ),
280            )
281            .into_any_element()
282    }
283}
284
285impl RenderOnce for ThemePreviewTile {
286    fn render(self, _window: &mut ui::Window, _cx: &mut ui::App) -> impl IntoElement {
287        match self.style {
288            ThemePreviewStyle::Bordered => {
289                Self::render_border(self.seed, self.theme).into_any_element()
290            }
291            ThemePreviewStyle::Borderless => {
292                Self::render_borderless(self.seed, self.theme).into_any_element()
293            }
294            ThemePreviewStyle::SideBySide(other_theme) => Self::render_side_by_side(
295                self.seed,
296                self.theme,
297                other_theme,
298                _cx.theme().colors().border,
299            )
300            .into_any_element(),
301        }
302    }
303}
304
305impl Component for ThemePreviewTile {
306    fn scope() -> ComponentScope {
307        ComponentScope::Onboarding
308    }
309
310    fn name() -> &'static str {
311        "Theme Preview Tile"
312    }
313
314    fn sort_name() -> &'static str {
315        "Theme Preview Tile"
316    }
317
318    fn description() -> &'static str {
319        Self::DOCS
320    }
321
322    fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
323        let theme_registry = ThemeRegistry::global(cx);
324
325        // Aiur is dark-only (omega#70).
326        let aiur = theme_registry.get("Aiur");
327        let gruvbox_dark = theme_registry.get("Gruvbox Dark");
328        let gruvbox_light = theme_registry.get("Gruvbox Light");
329
330        let themes_to_preview = vec![
331            aiur.clone().ok(),
332            gruvbox_dark.ok(),
333            gruvbox_light.ok(),
334        ]
335        .into_iter()
336        .flatten()
337        .collect::<Vec<_>>();
338
339        v_flex()
340            .gap_6()
341            .p_4()
342            .children({
343                if let Some(aiur_dark) = aiur.ok() {
344                    vec![example_group(vec![single_example(
345                        "Default",
346                        div()
347                            .w(px(240.))
348                            .h(px(180.))
349                            .child(ThemePreviewTile::new(aiur_dark, 0.42))
350                            .into_any_element(),
351                    )])]
352                } else {
353                    vec![]
354                }
355            })
356            .child(
357                example_group(vec![single_example(
358                    "Default Themes",
359                    h_flex()
360                        .gap_4()
361                        .children(
362                            themes_to_preview
363                                .into_iter()
364                                .map(|theme| {
365                                    div()
366                                        .w(px(200.))
367                                        .h(px(140.))
368                                        .child(ThemePreviewTile::new(theme, 0.42))
369                                })
370                                .collect::<Vec<_>>(),
371                        )
372                        .into_any_element(),
373                )])
374                .grow(),
375            )
376            .into_any_element()
377    }
378}
379
Served at tenant.openagents/omega Member data and write actions are omitted.