diff --git a/src/components/messageStream/BubbleImage.tsx b/src/components/messageStream/BubbleImage.tsx
index 027136e..799be0a 100644
--- a/src/components/messageStream/BubbleImage.tsx
+++ b/src/components/messageStream/BubbleImage.tsx
@@ -1,27 +1,49 @@
import { ImageOff } from "lucide-react";
-import { useEffect, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
type BubbleImageProps = {
src: string | undefined;
+ /**
+ * Optional absolute URL(s) to try if `src` fails to load. Useful when `src`
+ * is an optimized-but-fragile thumbnail (e.g. a root-relative thumbnailUrl)
+ * and we want to fall back to the reliable full-size asset before giving up.
+ */
+ fallbackSrc?: string | (string | undefined)[];
className?: string;
loading?: "lazy" | "eager";
};
/**
* Thumbnail for message bubbles. Renders with an empty alt (decorative)
- * and, if the asset fails to load, falls back to a neutral placeholder instead
- * of the browser's broken-image box — which would otherwise expose the raw
- * file name via alt text.
+ * and, if every candidate source fails to load, falls back to a neutral
+ * placeholder instead of the browser's broken-image box — which would otherwise
+ * expose the raw file name via alt text.
*/
-export function BubbleImage({ src, className, loading }: BubbleImageProps) {
- const [failed, setFailed] = useState(false);
+export function BubbleImage({
+ src,
+ fallbackSrc,
+ className,
+ loading,
+}: BubbleImageProps) {
+ // Ordered, de-duplicated list of sources to attempt in turn.
+ const candidates = useMemo(() => {
+ const extra = Array.isArray(fallbackSrc) ? fallbackSrc : [fallbackSrc];
+ return [src, ...extra].filter(
+ (value, index, all): value is string =>
+ !!value && all.indexOf(value) === index,
+ );
+ }, [src, fallbackSrc]);
- // Reset when the source changes so a reused element re-attempts the new src.
+ const [attempt, setAttempt] = useState(0);
+
+ // Reset when the candidate set changes so a reused element re-attempts.
useEffect(() => {
- setFailed(false);
- }, [src]);
+ setAttempt(0);
+ }, [candidates]);
- if (!src || failed) {
+ const current = candidates[attempt];
+
+ if (!current) {
return (