Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:37:33.543Z 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_gallery.rs

318 lines · 10.6 KB · rust
1#![cfg_attr(target_family = "wasm", no_main)]
2
3use futures::FutureExt;
4use gpui::{
5    App, AppContext, Asset as _, AssetLogger, Bounds, ClickEvent, Context, ElementId, Entity,
6    ImageAssetLoader, ImageCache, ImageCacheProvider, KeyBinding, Menu, MenuItem,
7    RetainAllImageCache, SharedString, TitlebarOptions, Window, WindowBounds, WindowOptions,
8    actions, div, hash, image_cache, img, prelude::*, px, rgb, size,
9};
10#[cfg(not(target_family = "wasm"))]
11use reqwest_client::ReqwestClient;
12use std::{collections::HashMap, sync::Arc};
13
14const IMAGES_IN_GALLERY: usize = 30;
15
16struct ImageGallery {
17    image_key: String,
18    items_count: usize,
19    total_count: usize,
20    image_cache: Entity<RetainAllImageCache>,
21}
22
23impl ImageGallery {
24    fn on_next_image(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
25        self.image_cache
26            .update(cx, |image_cache, cx| image_cache.clear(window, cx));
27
28        let t = std::time::SystemTime::now()
29            .duration_since(std::time::UNIX_EPOCH)
30            .unwrap()
31            .as_millis();
32
33        self.image_key = format!("{}", t);
34        self.total_count += self.items_count;
35        cx.notify();
36    }
37}
38
39impl Render for ImageGallery {
40    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
41        let image_url: SharedString =
42            format!("https://picsum.photos/400/200?t={}", self.image_key).into();
43
44        div()
45            .flex()
46            .flex_col()
47            .text_color(gpui::white())
48            .child("Manually managed image cache:")
49            .child(
50                div()
51                    .image_cache(self.image_cache.clone())
52                    .id("main")
53                    .text_color(gpui::black())
54                    .bg(rgb(0xE9E9E9))
55                    .overflow_y_scroll()
56                    .p_4()
57                    .size_full()
58                    .flex()
59                    .flex_col()
60                    .items_center()
61                    .gap_2()
62                    .child(
63                        div()
64                            .w_full()
65                            .flex()
66                            .flex_row()
67                            .justify_between()
68                            .child(format!(
69                                "Example to show images and test memory usage (Rendered: {} images).",
70                                self.total_count
71                            ))
72                            .child(
73                                div()
74                                    .id("btn")
75                                    .py_1()
76                                    .px_4()
77                                    .bg(gpui::black())
78                                    .hover(|this| this.opacity(0.8))
79                                    .text_color(gpui::white())
80                                    .text_center()
81                                    .w_40()
82                                    .child("Next Photos")
83                                    .on_click(cx.listener(Self::on_next_image)),
84                            ),
85                    )
86                    .child(
87                        div()
88                            .id("image-gallery")
89                            .flex()
90                            .flex_row()
91                            .flex_wrap()
92                            .gap_x_4()
93                            .gap_y_2()
94                            .justify_around()
95                            .children(
96                                (0..self.items_count)
97                                    .map(|ix| img(format!("{}-{}", image_url, ix)).size_20()),
98                            ),
99                    ),
100            )
101            .child(
102                "Automatically managed image cache:"
103            )
104            .child(image_cache(simple_lru_cache("lru-cache", IMAGES_IN_GALLERY)).child(
105                div()
106                    .id("main")
107                    .bg(rgb(0xE9E9E9))
108                    .text_color(gpui::black())
109                    .overflow_y_scroll()
110                    .p_4()
111                    .size_full()
112                    .flex()
113                    .flex_col()
114                    .items_center()
115                    .gap_2()
116                    .child(
117                        div()
118                            .id("image-gallery")
119                            .flex()
120                            .flex_row()
121                            .flex_wrap()
122                            .gap_x_4()
123                            .gap_y_2()
124                            .justify_around()
125                            .children(
126                                (0..self.items_count)
127                                    .map(|ix| img(format!("{}-{}", image_url, ix)).size_20()),
128                            ),
129                    )
130            ))
131    }
132}
133
134fn simple_lru_cache(id: impl Into<ElementId>, max_items: usize) -> SimpleLruCacheProvider {
135    SimpleLruCacheProvider {
136        id: id.into(),
137        max_items,
138    }
139}
140
141struct SimpleLruCacheProvider {
142    id: ElementId,
143    max_items: usize,
144}
145
146impl ImageCacheProvider for SimpleLruCacheProvider {
147    fn provide(&mut self, window: &mut Window, cx: &mut App) -> gpui::AnyImageCache {
148        window
149            .with_global_id(self.id.clone(), |global_id, window| {
150                window.with_element_state::<Entity<SimpleLruCache>, _>(
151                    global_id,
152                    |lru_cache, _window| {
153                        let mut lru_cache = lru_cache.unwrap_or_else(|| {
154                            cx.new(|cx| SimpleLruCache::new(self.max_items, cx))
155                        });
156                        if lru_cache.read(cx).max_items != self.max_items {
157                            lru_cache = cx.new(|cx| SimpleLruCache::new(self.max_items, cx));
158                        }
159                        (lru_cache.clone(), lru_cache)
160                    },
161                )
162            })
163            .into()
164    }
165}
166
167struct SimpleLruCache {
168    max_items: usize,
169    usages: Vec<u64>,
170    cache: HashMap<u64, gpui::ImageCacheItem>,
171}
172
173impl SimpleLruCache {
174    fn new(max_items: usize, cx: &mut Context<Self>) -> Self {
175        cx.on_release(|simple_cache, cx| {
176            for (_, mut item) in std::mem::take(&mut simple_cache.cache) {
177                if let Some(Ok(image)) = item.get() {
178                    cx.drop_image(image, None);
179                }
180            }
181        })
182        .detach();
183
184        Self {
185            max_items,
186            usages: Vec::with_capacity(max_items),
187            cache: HashMap::with_capacity(max_items),
188        }
189    }
190}
191
192impl ImageCache for SimpleLruCache {
193    fn load(
194        &mut self,
195        resource: &gpui::Resource,
196        window: &mut Window,
197        cx: &mut App,
198    ) -> Option<Result<Arc<gpui::RenderImage>, gpui::ImageCacheError>> {
199        assert_eq!(self.usages.len(), self.cache.len());
200        assert!(self.cache.len() <= self.max_items);
201
202        let hash = hash(resource);
203
204        if let Some(item) = self.cache.get_mut(&hash) {
205            let current_ix = self
206                .usages
207                .iter()
208                .position(|item| *item == hash)
209                .expect("cache and usages must stay in sync");
210            self.usages.remove(current_ix);
211            self.usages.insert(0, hash);
212
213            return item.get();
214        }
215
216        let fut = AssetLogger::<ImageAssetLoader>::load(resource.clone(), cx);
217        let task = cx.background_executor().spawn(fut).shared();
218        if self.usages.len() == self.max_items {
219            let oldest = self.usages.pop().unwrap();
220            let mut image = self
221                .cache
222                .remove(&oldest)
223                .expect("cache and usages must be in sync");
224            if let Some(Ok(image)) = image.get() {
225                cx.drop_image(image, Some(window));
226            }
227        }
228        self.cache
229            .insert(hash, gpui::ImageCacheItem::Loading(task.clone()));
230        self.usages.insert(0, hash);
231
232        let entity = window.current_view();
233        window
234            .spawn(cx, {
235                async move |cx| {
236                    _ = task.await;
237                    cx.on_next_frame(move |_, cx| {
238                        cx.notify(entity);
239                    });
240                }
241            })
242            .detach();
243
244        None
245    }
246}
247
248actions!(image, [Quit]);
249
250fn run_example() {
251    #[cfg(not(target_family = "wasm"))]
252    let app = gpui_platform::application();
253    #[cfg(target_family = "wasm")]
254    let app = gpui_platform::single_threaded_web();
255
256    app.run(move |cx: &mut App| {
257        #[cfg(not(target_family = "wasm"))]
258        {
259            let http_client = ReqwestClient::user_agent("gpui example").unwrap();
260            cx.set_http_client(Arc::new(http_client));
261        }
262        #[cfg(target_family = "wasm")]
263        {
264            // Safety: the web examples run single-threaded; the client is
265            // created and used exclusively on the main thread.
266            let http_client = unsafe {
267                gpui_web::FetchHttpClient::with_user_agent("gpui example")
268                    .expect("failed to create FetchHttpClient")
269            };
270            cx.set_http_client(Arc::new(http_client));
271        }
272
273        cx.activate(true);
274        cx.on_action(|_: &Quit, cx| cx.quit());
275        cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
276        cx.set_menus([Menu::new("Image Gallery").items([MenuItem::action("Quit", Quit)])]);
277
278        let window_options = WindowOptions {
279            titlebar: Some(TitlebarOptions {
280                title: Some(SharedString::from("Image Gallery")),
281                appears_transparent: false,
282                ..Default::default()
283            }),
284
285            window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
286                None,
287                size(px(1100.), px(860.)),
288                cx,
289            ))),
290
291            ..Default::default()
292        };
293
294        cx.open_window(window_options, |_, cx| {
295            cx.new(|ctx| ImageGallery {
296                image_key: "".into(),
297                items_count: IMAGES_IN_GALLERY,
298                total_count: 0,
299                image_cache: RetainAllImageCache::new(ctx),
300            })
301        })
302        .unwrap();
303    });
304}
305
306#[cfg(not(target_family = "wasm"))]
307fn main() {
308    env_logger::init();
309    run_example();
310}
311
312#[cfg(target_family = "wasm")]
313#[wasm_bindgen::prelude::wasm_bindgen(start)]
314pub fn start() {
315    gpui_platform::web_init();
316    run_example();
317}
318
Served at tenant.openagents/omega Member data and write actions are omitted.