"use client"; import type { ComponentPropsWithoutRef } from "react"; import { useEffect, useState } from "react"; import { MessagePartPrimitive } from "@assistant-ui/react"; import { Box, Button, Center, Loader, Stack, Text } from "@mantine/core"; import { IconPhotoOff, IconRefresh } from "@tabler/icons-react"; import classes from "./assistant-image.module.css"; interface AssistantImageProps extends Omit, "src"> { source: string; messagePart?: boolean; caption?: string; loadingLabel: string; failedLabel: string; retryLabel: string; } /** * A bounded image preview shared by Markdown and assistant-ui image message parts. Message parts * deliberately render through MessagePartPrimitive.Image so their source remains connected to the * assistant-ui part context; the sanitized source prop overrides the raw part value. */ export const AssistantImage = ({ source, messagePart = false, caption, loadingLabel, failedLabel, retryLabel, alt, className, ...imageProps }: AssistantImageProps) => { const [attempt, setAttempt] = useState(0); const [state, setState] = useState<"loading" | "loaded" | "failed">("loading"); useEffect(() => { setState("loading"); setAttempt(0); }, [source]); const sharedProps = { ...imageProps, src: source, className, loading: "lazy" as const, decoding: "async" as const, referrerPolicy: "no-referrer" as const, onLoad: () => setState("loaded"), onError: () => setState("failed"), }; return ( {messagePart ? ( ) : ( {alt} )} {state === "loading" && (
)} {state === "failed" && (
{failedLabel}
)}
{caption && state === "loaded" && ( {caption} )}
); };