fix(content): center letterboxed images and enlarge the AI-disclosure badge

BlurImage's wrapper defaults to w-full/h-full for fill-container usages
(feed thumbnails with object-cover) — but in object-contain/letterboxed
contexts (story viewer, post detail single-image and carousel) that same
wrapper stretched to the full viewing area, defeating the parent's
flex-centering and leaving the actual rendered image pinned to the
top-left. Fixed by wrapping those three usages in an inline-flex
container sized to the actual image (wrapperClassName override), which
also gives the AI-disclosure badge a real image-relative anchor instead
of floating in the surrounding letterbox space. Bumped the badge's base
size (h-5 → h-7, h-8/h-10 in the larger detail/story contexts) per
live-test feedback that it was hard to notice.
This commit is contained in:
admin committed 2026-08-13 14:01:17 +02:00
1 parent 50e2848312
commit 092268b8e7
3 files changed
+41 -26

No files matched your search

+1 -1
View File
@@ -30,7 +30,7 @@ export function AiDisclosureBadge({ value, className }: AiDisclosureBadgeProps)
<img
src={src}
alt={value === "AI_GENERATED" ? t("generatedBadgeAlt") : t("modifiedBadgeAlt")}
className={cn("absolute top-2 left-2 h-5 w-auto select-none pointer-events-none", className)}
className={cn("absolute top-2 left-2 h-7 w-auto select-none pointer-events-none drop-shadow-md", className)}
/>
);
}
+21 -13
View File
@@ -244,14 +244,18 @@ export function PostDetailDialog({ post, open, onClose, onDeleted }: PostDetailD
);
const mediaContent = post.type === "VIDEO" && post.videoPost ? (
<VideoCard
postId={post.id}
initialStatus={post.videoPost.status}
initialPlaybackId={post.videoPost.muxPlaybackId}
/>
<div className="relative h-full w-full">
<VideoCard
postId={post.id}
initialStatus={post.videoPost.status}
initialPlaybackId={post.videoPost.muxPlaybackId}
/>
<AiDisclosureBadge value={post.aiDisclosure} className="h-8" />
</div>
) : images.length > 0 && (
isCarousel ? (
<div className="relative w-full h-full">
<AiDisclosureBadge value={post.aiDisclosure} className="h-8" />
<Carousel setApi={setCarouselApi} className="h-full">
<CarouselContent className="h-full">
{images.map((img) => (
@@ -260,7 +264,8 @@ export function PostDetailDialog({ post, open, onClose, onDeleted }: PostDetailD
src={getMediaUrl(img.storageKey) ?? ""}
blurhash={img.blurhash}
alt={currentCaption ?? `${pet.name}'s post`}
className="max-h-full max-w-full object-contain"
className="block max-h-full max-w-full object-contain"
wrapperClassName="relative block h-auto w-auto"
/>
</CarouselItem>
))}
@@ -283,12 +288,16 @@ export function PostDetailDialog({ post, open, onClose, onDeleted }: PostDetailD
</div>
</div>
) : (
<BlurImage
src={getMediaUrl(images[0].storageKey) ?? ""}
blurhash={images[0].blurhash}
alt={currentCaption ?? `${pet.name}'s post`}
className="max-h-full max-w-full object-contain"
/>
<div className="relative inline-flex max-h-full max-w-full">
<BlurImage
src={getMediaUrl(images[0].storageKey) ?? ""}
blurhash={images[0].blurhash}
alt={currentCaption ?? `${pet.name}'s post`}
className="block max-h-full max-w-full object-contain"
wrapperClassName="relative block h-auto w-auto"
/>
<AiDisclosureBadge value={post.aiDisclosure} className="h-8" />
</div>
)
);
@@ -312,7 +321,6 @@ export function PostDetailDialog({ post, open, onClose, onDeleted }: PostDetailD
{/* Image column — fixed 45dvh strip on mobile, flex-1 full-height on desktop */}
<div className="relative shrink-0 h-[45dvh] md:h-full md:flex-1 md:min-w-0 bg-black flex items-center justify-center">
{mediaContent}
<AiDisclosureBadge value={post.aiDisclosure} />
</div>
{/* Right panel: desktop header + scrollable body + sticky input */}
+19 -12
View File
@@ -155,21 +155,28 @@ export function StoryViewer({
))}
</div>
{/* Story image */}
{/* Story image — inline-flex wrapper shrinks to the actual rendered
image box (not the full-screen overlay) so both the centering
and the badge overlay track the real image bounds. BlurImage's
own wrapper defaults to w-full/h-full (fine for fill-container
usages like feed thumbnails), so it's overridden here via
wrapperClassName. */}
{mediaUrl && (
<BlurImage
src={mediaUrl}
blurhash={currentStory.blurhash}
alt={t("storyAriaLabel", { petName })}
className="max-h-screen max-w-full object-contain"
/>
<div className="relative inline-flex max-h-screen max-w-full">
<BlurImage
src={mediaUrl}
blurhash={currentStory.blurhash}
alt={t("storyAriaLabel", { petName })}
className="block max-h-screen max-w-full object-contain"
wrapperClassName="relative block h-auto w-auto"
/>
<AiDisclosureBadge
value={currentStory.aiDisclosure}
className="top-auto left-auto bottom-3 right-3 h-10"
/>
</div>
)}
<AiDisclosureBadge
value={currentStory.aiDisclosure}
className="top-auto left-auto bottom-6 right-4"
/>
{/* Pet info — bottom left */}
<div className="absolute bottom-6 left-4 flex items-center gap-2">
<Avatar className="w-8 h-8 border border-white/50">