diff --git a/src/components/nostr/MediaEmbed.tsx b/src/components/nostr/MediaEmbed.tsx index 9e1a08a..ff77a32 100644 --- a/src/components/nostr/MediaEmbed.tsx +++ b/src/components/nostr/MediaEmbed.tsx @@ -13,7 +13,7 @@ interface MediaEmbedProps { url: string; type?: "image" | "video" | "audio" | "auto"; alt?: string; - preset?: "inline" | "thumbnail" | "preview" | "banner"; + preset?: "inline" | "thumbnail" | "grid" | "preview" | "banner"; className?: string; // Image-specific @@ -42,6 +42,12 @@ const PRESETS = { maxWidthClass: "max-w-[120px]", roundedClass: "rounded-md", }, + grid: { + maxHeightClass: "", + maxWidthClass: "w-full", + roundedClass: "rounded-md", + objectFit: "cover" as const, + }, preview: { maxHeightClass: "max-h-[500px]", maxWidthClass: "max-w-full", @@ -61,7 +67,7 @@ const getDefaultAspectRatio = ( mediaType: string, preset: string, ): string | undefined => { - if (preset === "thumbnail") return "1/1"; + if (preset === "thumbnail" || preset === "grid") return "1/1"; if (mediaType === "video") return "16/9"; return undefined; // auto for images }; @@ -256,7 +262,8 @@ export function MediaEmbed({ alt={alt || "Image"} loading="lazy" className={cn( - "w-full h-full object-contain", + "w-full h-full", + preset === "grid" ? "object-cover" : "object-contain", presetStyles.roundedClass, enableZoom && "cursor-zoom-in", fadeIn && "transition-opacity duration-300", diff --git a/src/components/nostr/RichText/Gallery.tsx b/src/components/nostr/RichText/Gallery.tsx index efbe6e6..7a90734 100644 --- a/src/components/nostr/RichText/Gallery.tsx +++ b/src/components/nostr/RichText/Gallery.tsx @@ -36,13 +36,13 @@ export function Gallery({ node }: GalleryNodeProps) { if (isImageURL(url)) { if (shouldShowMedia && options.showImages) { - return ; + return ; } return ; } if (isVideoURL(url)) { if (shouldShowMedia && options.showVideos) { - return ; + return ; } return ; } @@ -65,13 +65,28 @@ export function Gallery({ node }: GalleryNodeProps) { // Only show dialog for audio files const audioLinks = links.filter((url) => isAudioURL(url)); + // Separate media types for layout + const imageLinks = links.filter((url) => isImageURL(url) || isVideoURL(url)); + const audioOnlyLinks = links.filter((url) => isAudioURL(url)); + return ( <> -
- {links.map((url: string, i: number) => ( -
{renderLink(url, i)}
- ))} -
+ {/* Grid layout for images/videos */} + {imageLinks.length > 0 && ( +
+ {imageLinks.map((url: string, i: number) => ( +
{renderLink(url, links.indexOf(url))}
+ ))} +
+ )} + {/* Stack layout for audio */} + {audioOnlyLinks.length > 0 && ( +
+ {audioOnlyLinks.map((url: string, i: number) => ( +
{renderLink(url, links.indexOf(url))}
+ ))} +
+ )} {audioLinks.length > 0 && (