From 2bc50f07c411a1df5c5874fd905a116c8d50dffd Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Thu, 9 Apr 2026 18:08:19 +0700 Subject: [PATCH] render media list --- crates/chat_ui/src/lib.rs | 58 ++++++++++++++++++++++++++-- crates/common/src/media_extractor.rs | 2 +- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/crates/chat_ui/src/lib.rs b/crates/chat_ui/src/lib.rs index 09dd12c..0c1a601 100644 --- a/crates/chat_ui/src/lib.rs +++ b/crates/chat_ui/src/lib.rs @@ -9,9 +9,9 @@ use gpui::prelude::FluentBuilder; use gpui::{ AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, MouseButton, - ObjectFit, ParentElement, PathPromptOptions, Render, SharedString, StatefulInteractiveElement, - Styled, StyledImage, Subscription, Task, WeakEntity, Window, deferred, div, img, list, px, red, - relative, svg, white, + ObjectFit, ParentElement, PathPromptOptions, Render, SharedString, SharedUri, + StatefulInteractiveElement, Styled, StyledImage, Subscription, Task, WeakEntity, Window, + deferred, div, img, list, px, red, relative, svg, white, }; use itertools::Itertools; use nostr_sdk::prelude::*; @@ -914,7 +914,8 @@ impl ChatPanel { .when(has_replies, |this| { this.children(self.render_message_replies(replies, cx)) }) - .child(rendered_text), + .child(rendered_text) + .child(self.render_media(&message.media, cx)), ), ) .child( @@ -941,6 +942,55 @@ impl ChatPanel { .into_any_element() } + fn render_media(&self, media: &[SharedUri], cx: &Context) -> impl IntoElement { + // No media: return empty div + if media.is_empty() { + return div(); + }; + + // Single media item: render full-width image + if media.len() == 1 { + return div().child( + img(media[0].clone()) + .border_1() + .border_color(cx.theme().border_variant) + .h(px(250.)) + .object_fit(ObjectFit::Cover) + .rounded(cx.theme().radius), + ); + } + + // Multiple media items: render in a row + div() + .w_full() + .flex_1() + .flex() + .flex_row() + .flex_wrap() + .gap_2() + .children({ + let mut items = vec![]; + + for (ix, item) in media.iter().enumerate() { + items.push( + div() + .id(format!("media-{ix}")) + .flex_grow_0() + .flex_shrink_0() + .child( + img(item.clone()) + .h_32() + .border_1() + .border_color(cx.theme().border_variant) + .rounded(cx.theme().radius), + ), + ); + } + + items + }) + } + fn render_message_replies( &self, replies: &[EventId], diff --git a/crates/common/src/media_extractor.rs b/crates/common/src/media_extractor.rs index ffe7f55..832586e 100644 --- a/crates/common/src/media_extractor.rs +++ b/crates/common/src/media_extractor.rs @@ -67,7 +67,7 @@ impl MediaExtractor { // Remove multiple consecutive spaces let re = Regex::new(r"\s+").unwrap(); - re.replace_all(text, " ").to_string() + re.replace_all(text, " ").trim().to_string() } /// Validates if a URL is a valid media URL