Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:42:55.684Z 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

image_loading.rs

227 lines · 8.4 KB · rust
1#![cfg_attr(target_family = "wasm", no_main)]
2
3use std::{path::Path, sync::Arc, time::Duration};
4
5use gpui::{
6    Animation, AnimationExt, App, Asset, AssetLogger, AssetSource, Bounds, Context, Hsla,
7    ImageAssetLoader, ImageCacheError, ImgResourceLoader, LOADING_DELAY, Length, RenderImage,
8    Resource, SharedString, Window, WindowBounds, WindowOptions, black, div, img, prelude::*,
9    pulsating_between, px, red, size,
10};
11use gpui_platform::application;
12
13struct Assets {}
14
15impl AssetSource for Assets {
16    fn load(&self, path: &str) -> anyhow::Result<Option<std::borrow::Cow<'static, [u8]>>> {
17        std::fs::read(path)
18            .map(Into::into)
19            .map_err(Into::into)
20            .map(Some)
21    }
22
23    fn list(&self, path: &str) -> anyhow::Result<Vec<SharedString>> {
24        Ok(std::fs::read_dir(path)?
25            .filter_map(|entry| {
26                Some(SharedString::from(
27                    entry.ok()?.path().to_string_lossy().into_owned(),
28                ))
29            })
30            .collect::<Vec<_>>())
31    }
32}
33
34const IMAGE: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/image/app-icon.png");
35
36#[derive(Copy, Clone, Hash)]
37struct LoadImageParameters {
38    timeout: Duration,
39    fail: bool,
40}
41
42struct LoadImageWithParameters {}
43
44impl Asset for LoadImageWithParameters {
45    type Source = LoadImageParameters;
46
47    type Output = Result<Arc<RenderImage>, ImageCacheError>;
48
49    fn load(
50        parameters: Self::Source,
51        cx: &mut App,
52    ) -> impl std::future::Future<Output = Self::Output> + Send + 'static {
53        let timer = cx.background_executor().timer(parameters.timeout);
54        let data = AssetLogger::<ImageAssetLoader>::load(
55            Resource::Path(Path::new(IMAGE).to_path_buf().into()),
56            cx,
57        );
58        async move {
59            timer.await;
60            if parameters.fail {
61                log::error!("Intentionally failed to load image");
62                Err(anyhow::anyhow!("Failed to load image").into())
63            } else {
64                data.await
65            }
66        }
67    }
68}
69
70struct ImageLoadingExample {}
71
72impl ImageLoadingExample {
73    fn loading_element() -> impl IntoElement {
74        div().size_full().flex_none().p_0p5().rounded_xs().child(
75            div().size_full().with_animation(
76                "loading-bg",
77                Animation::new(Duration::from_secs(3))
78                    .repeat()
79                    .with_easing(pulsating_between(0.04, 0.24)),
80                move |this, delta| this.bg(black().opacity(delta)),
81            ),
82        )
83    }
84
85    fn fallback_element() -> impl IntoElement {
86        let fallback_color: Hsla = black().opacity(0.5);
87
88        div().size_full().flex_none().p_0p5().child(
89            div()
90                .size_full()
91                .flex()
92                .items_center()
93                .justify_center()
94                .rounded_xs()
95                .text_sm()
96                .text_color(fallback_color)
97                .border_1()
98                .border_color(fallback_color)
99                .child("?"),
100        )
101    }
102}
103
104impl Render for ImageLoadingExample {
105    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
106        div().flex().flex_col().size_full().justify_around().child(
107            div().flex().flex_row().w_full().justify_around().child(
108                div()
109                    .flex()
110                    .bg(gpui::white())
111                    .size(Length::Definite(px(300.0).into()))
112                    .justify_center()
113                    .items_center()
114                    .child({
115                        let image_source = LoadImageParameters {
116                            timeout: LOADING_DELAY.saturating_sub(Duration::from_millis(25)),
117                            fail: false,
118                        };
119
120                        // Load within the 'loading delay', should not show loading fallback
121                        img(move |window: &mut Window, cx: &mut App| {
122                            window.use_asset::<LoadImageWithParameters>(&image_source, cx)
123                        })
124                        .id("image-1")
125                        .border_1()
126                        .size_12()
127                        .with_fallback(|| Self::fallback_element().into_any_element())
128                        .border_color(red())
129                        .with_loading(|| Self::loading_element().into_any_element())
130                        .on_click(move |_, _, cx| {
131                            cx.remove_asset::<LoadImageWithParameters>(&image_source);
132                        })
133                    })
134                    .child({
135                        // Load after a long delay
136                        let image_source = LoadImageParameters {
137                            timeout: Duration::from_secs(5),
138                            fail: false,
139                        };
140
141                        img(move |window: &mut Window, cx: &mut App| {
142                            window.use_asset::<LoadImageWithParameters>(&image_source, cx)
143                        })
144                        .id("image-2")
145                        .with_fallback(|| Self::fallback_element().into_any_element())
146                        .with_loading(|| Self::loading_element().into_any_element())
147                        .size_12()
148                        .border_1()
149                        .border_color(red())
150                        .on_click(move |_, _, cx| {
151                            cx.remove_asset::<LoadImageWithParameters>(&image_source);
152                        })
153                    })
154                    .child({
155                        // Fail to load image after a long delay
156                        let image_source = LoadImageParameters {
157                            timeout: Duration::from_secs(5),
158                            fail: true,
159                        };
160
161                        // Fail to load after a long delay
162                        img(move |window: &mut Window, cx: &mut App| {
163                            window.use_asset::<LoadImageWithParameters>(&image_source, cx)
164                        })
165                        .id("image-3")
166                        .with_fallback(|| Self::fallback_element().into_any_element())
167                        .with_loading(|| Self::loading_element().into_any_element())
168                        .size_12()
169                        .border_1()
170                        .border_color(red())
171                        .on_click(move |_, _, cx| {
172                            cx.remove_asset::<LoadImageWithParameters>(&image_source);
173                        })
174                    })
175                    .child({
176                        // Ensure that the normal image loader doesn't spam logs
177                        let image_source = Path::new(
178                            "this/file/really/shouldn't/exist/or/won't/be/an/image/I/hope",
179                        )
180                        .to_path_buf();
181                        img(image_source.clone())
182                            .id("image-4")
183                            .border_1()
184                            .size_12()
185                            .with_fallback(|| Self::fallback_element().into_any_element())
186                            .border_color(red())
187                            .with_loading(|| Self::loading_element().into_any_element())
188                            .on_click(move |_, _, cx| {
189                                cx.remove_asset::<ImgResourceLoader>(&image_source.clone().into());
190                            })
191                    }),
192            ),
193        )
194    }
195}
196
197fn run_example() {
198    application().with_assets(Assets {}).run(|cx: &mut App| {
199        let options = WindowOptions {
200            window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
201                None,
202                size(px(300.), px(300.)),
203                cx,
204            ))),
205            ..Default::default()
206        };
207        cx.open_window(options, |_, cx| {
208            cx.activate(false);
209            cx.new(|_| ImageLoadingExample {})
210        })
211        .unwrap();
212    });
213}
214
215#[cfg(not(target_family = "wasm"))]
216fn main() {
217    env_logger::init();
218    run_example();
219}
220
221#[cfg(target_family = "wasm")]
222#[wasm_bindgen::prelude::wasm_bindgen(start)]
223pub fn start() {
224    gpui_platform::web_init();
225    run_example();
226}
227
Served at tenant.openagents/omega Member data and write actions are omitted.