| 1 | /** biome-ignore-all lint/performance/noImgElement: This is a valid use case */ |
| 2 | "use client"; |
| 3 | |
| 4 | import Image from "next/image"; |
| 5 | |
| 6 | import { Spinner } from "@/components/ui/spinner"; |
| 7 | import { cn } from "@/lib/utils"; |
| 8 | import { usePresentationState } from "@/states/presentation-state"; |
| 9 | import { ChartRenderer } from "../charts/ChartRenderer"; |
| 10 | import { EmbedRenderer } from "../embeds/EmbedRenderer"; |
| 11 | import ImagePlaceholder from "../image-placeholder"; |
| 12 | import { type RootImageProps } from "../root-image"; |
| 13 | import { |
| 14 | getRootImageObjectStyles, |
| 15 | getRootImageSizeStyle, |
| 16 | } from "../root-image-layout"; |
| 17 | |
| 18 | export default function RootImageStatic({ |
| 19 | image, |
| 20 | layoutType, |
| 21 | slideId, |
| 22 | }: RootImageProps) { |
| 23 | const isSideLayout = layoutType === "left" || layoutType === "right"; |
| 24 | const rootImageGeneration = usePresentationState( |
| 25 | (s) => s.rootImageGeneration, |
| 26 | ); |
| 27 | const rawComputedGen = slideId ? rootImageGeneration[slideId] : undefined; |
| 28 | const imageQuery = image.query.trim(); |
| 29 | const computedGen = |
| 30 | rawComputedGen && |
| 31 | (!imageQuery || rawComputedGen.query.trim() === imageQuery) |
| 32 | ? rawComputedGen |
| 33 | : undefined; |
| 34 | const computedImageUrl = computedGen?.url ?? image.url; |
| 35 | const imageStyles = getRootImageObjectStyles(image); |
| 36 | const sizeStyle = getRootImageSizeStyle(image, layoutType); |
| 37 | const isRootImageGenerating = |
| 38 | image.isQueryStreaming || |
| 39 | computedGen?.status === "queued" || |
| 40 | computedGen?.status === "generating"; |
| 41 | const shouldShowGenerationPlaceholder = |
| 42 | isRootImageGenerating && |
| 43 | !computedImageUrl && |
| 44 | !image.embedType && |
| 45 | !image.chartType; |
| 46 | |
| 47 | // Root image is now generated by PresentationGenerationManager |
| 48 | |
| 49 | return ( |
| 50 | <div |
| 51 | className={cn( |
| 52 | "relative shrink-0", |
| 53 | isSideLayout && "min-h-0 self-stretch", |
| 54 | )} |
| 55 | style={sizeStyle} |
| 56 | > |
| 57 | <div |
| 58 | className={cn( |
| 59 | "overflow-hidden border bg-background/80 backdrop-blur-xs", |
| 60 | isSideLayout ? "absolute inset-0" : "h-full", |
| 61 | )} |
| 62 | data-root-image={slideId} |
| 63 | style={{ |
| 64 | borderRadius: "var(--presentation-border-radius, 0.5rem)", |
| 65 | boxShadow: |
| 66 | "var(--presentation-card-shadow, 0 1px 3px rgba(0,0,0,0.12))", |
| 67 | }} |
| 68 | > |
| 69 | {shouldShowGenerationPlaceholder ? ( |
| 70 | <div className="flex h-full flex-col items-center justify-center gap-3 bg-muted/30 p-4 text-center"> |
| 71 | <Spinner className="size-8" /> |
| 72 | <div className="space-y-1"> |
| 73 | <p className="text-sm font-medium text-foreground"> |
| 74 | Generating root image |
| 75 | </p> |
| 76 | <p className="text-xs text-muted-foreground"> |
| 77 | This can take a moment. |
| 78 | </p> |
| 79 | </div> |
| 80 | </div> |
| 81 | ) : image.chartType && image.chartData ? ( |
| 82 | <div className="relative h-full"> |
| 83 | <ChartRenderer |
| 84 | chartType={image.chartType} |
| 85 | chartData={image.chartData} |
| 86 | chartOptions={image.chartOptions} |
| 87 | className="h-full w-full" |
| 88 | /> |
| 89 | </div> |
| 90 | ) : image.embedType && image.url ? ( |
| 91 | <div className="relative h-full"> |
| 92 | <EmbedRenderer |
| 93 | embedType={image.embedType} |
| 94 | url={image.url} |
| 95 | className="h-full w-full" |
| 96 | style={imageStyles} |
| 97 | /> |
| 98 | </div> |
| 99 | ) : ( |
| 100 | <div className="relative h-full"> |
| 101 | {computedImageUrl ? ( |
| 102 | <Image |
| 103 | unoptimized |
| 104 | width={400} |
| 105 | height={300} |
| 106 | src={computedImageUrl} |
| 107 | alt={image.query} |
| 108 | style={imageStyles} |
| 109 | /> |
| 110 | ) : ( |
| 111 | <ImagePlaceholder |
| 112 | isStatic={true} |
| 113 | className="h-full" |
| 114 | slideId={slideId} |
| 115 | imageNotFound={computedGen?.status === "error"} |
| 116 | /> |
| 117 | )} |
| 118 | </div> |
| 119 | )} |
| 120 | </div> |
| 121 | </div> |
| 122 | ); |
| 123 | } |
| 124 |