返回 presentation-ai
root-image-static.tsx
根目录 / src / components / notebook / presentation / editor / custom-elements / static / root-image-static.tsx
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
124 lines Plain Text