68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
import cn from "@bureaudouble/outils/cn.ts";
|
|
import type { ImageGenerationPayload } from "../../actions/generate-image.ts";
|
|
import type { NarrativeMessage } from "../../game-engine/narrative-provider.tsx";
|
|
import {
|
|
gameStateToVariantState,
|
|
personaImageMessageVariants,
|
|
} from "../../game-engine/persona-styles.ts";
|
|
import { useTurn } from "../../game-engine/turn-provider.tsx";
|
|
import { Rationale } from "./chat-message.tsx";
|
|
import { useClientLocale } from "../../game-engine/locale-provider.tsx";
|
|
|
|
export const getImageDataFromMessage = (
|
|
message?: NarrativeMessage | null,
|
|
): ImageGenerationPayload | null => {
|
|
const imageGeneration = message?.parts
|
|
.filter((v) => v.type === "tool-invocation")
|
|
.flatMap((v) => v.toolInvocation)
|
|
.filter((v) => v.state === "result")
|
|
.map((v) => v.result as ImageGenerationPayload | undefined)
|
|
.filter((v) => v?.imageUrl)
|
|
.at(0);
|
|
|
|
return imageGeneration ?? null;
|
|
};
|
|
|
|
export const ImageDisplay: React.FC<{ className?: string }> = (
|
|
{ className },
|
|
) => {
|
|
const { t } = useClientLocale();
|
|
const { message, turnData } = useTurn();
|
|
|
|
if (!turnData) return;
|
|
|
|
const imageResponse = getImageDataFromMessage(message);
|
|
const visualPromptInstructionText =
|
|
"visualPromptInstruction" in turnData && turnData?.visualPromptInstruction
|
|
? turnData.visualPromptInstruction
|
|
: undefined;
|
|
|
|
if (!imageResponse) return null;
|
|
const { isLoadingImage, imageUrl } = imageResponse;
|
|
|
|
if (!isLoadingImage && !imageUrl && !visualPromptInstructionText) return null;
|
|
const variantState = gameStateToVariantState(turnData?.fullGameState);
|
|
|
|
return (
|
|
<div
|
|
data-id={imageUrl?.slice(-10)}
|
|
className={cn(personaImageMessageVariants(variantState), className)}
|
|
>
|
|
{(visualPromptInstructionText || (!imageUrl && !isLoadingImage)) && (
|
|
<Rationale
|
|
title={t("visualRationaleTitle")}
|
|
content={visualPromptInstructionText}
|
|
/>
|
|
)}
|
|
{isLoadingImage && <div data-image-loading />}
|
|
{imageUrl && !isLoadingImage && (
|
|
<img
|
|
data-image
|
|
src={imageUrl}
|
|
alt={visualPromptInstructionText}
|
|
loading="lazy"
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|