返回 oh-my-ppt
GenerationThumbnail.tsx
根目录 / src / renderer / src / components / session-generating / GenerationThumbnail.tsx
1 import { CircleAlert, Loader2 } from 'lucide-react'
2 import { PreviewIframe } from '@renderer/components/preview/PreviewIframe'
3 import { cn } from '@renderer/lib/utils'
4 import type { GenerationPreviewPage } from './types'
5 import type { SlideSizePreset } from '@shared/slide-size'
6
7 export function GenerationThumbnail({
8 page,
9 previewEnabled = true,
10 slideSize
11 }: {
12 page: GenerationPreviewPage
13 previewEnabled?: boolean
14 slideSize: SlideSizePreset | null
15 }): React.JSX.Element {
16 if (!slideSize) {
17 return <div className="h-[195px] w-full rounded-xl bg-[#f5f1e8]/88" />
18 }
19 const thumbnailFitStyle =
20 slideSize.width >= slideSize.height
21 ? { width: '100%', aspectRatio: `${slideSize.width}/${slideSize.height}` }
22 : { height: '100%', aspectRatio: `${slideSize.width}/${slideSize.height}` }
23 const hasPreview =
24 previewEnabled && page.status === 'completed' && (page.htmlPath || page.sourceUrl)
25
26 return (
27 <div
28 className={cn(
29 'group relative flex w-full min-w-0 flex-col overflow-hidden rounded-xl border bg-[#fffaf1]/78 p-1.5 shadow-[0_16px_34px_rgba(70,82,58,0.12)] transition-all duration-500',
30 page.status === 'completed' && 'border-[#b8d3a6] translate-y-0 opacity-100',
31 page.status === 'generating' &&
32 'border-[#8fb873] bg-[#f6fbef]/88 shadow-[0_18px_40px_rgba(95,132,72,0.22)]',
33 page.status === 'failed' && 'border-[#d7b5ae] bg-[#fbf1ee]/92',
34 page.status === 'pending' && 'border-[#dfd4bf]/72 opacity-72'
35 )}
36 >
37 <div
38 className="relative flex h-[180px] w-full min-w-0 shrink-0 items-center justify-center overflow-hidden bg-[#f5f1e8]/88 shadow-[0_5px_14px_rgba(93,107,77,0.08)]"
39 style={{ contain: 'paint' }}
40 >
41 <div className="relative max-h-full max-w-full overflow-hidden" style={thumbnailFitStyle}>
42 {hasPreview ? (
43 <PreviewIframe
44 key={`generating-thumb-${page.id}-${page.previewVersion ?? 0}`}
45 src={page.sourceUrl}
46 htmlPath={page.htmlPath}
47 pageId={page.pageId}
48 title={`generating-page-${page.pageNumber}`}
49 slideSize={slideSize}
50 inspectable={false}
51 thumbnail
52 />
53 ) : (
54 <div
55 className={cn(
56 'flex h-full w-full flex-col justify-between p-3',
57 page.status === 'generating'
58 ? 'bg-[linear-gradient(135deg,#eef6e7_0%,#fff8ec_100%)]'
59 : page.status === 'failed'
60 ? 'bg-[#f7e7e2]'
61 : 'bg-[linear-gradient(135deg,#f5efe4_0%,#e9decb_100%)]'
62 )}
63 >
64 <div className="flex items-center justify-between">
65 <span className="h-2 w-16 rounded-full bg-white/72" />
66 <span className="h-5 w-5 rounded-md border border-white/80 bg-white/58" />
67 </div>
68 <div className="space-y-2">
69 <span className="block h-3 w-3/4 rounded-full bg-white/78" />
70 <span className="block h-2 w-11/12 rounded-full bg-white/56" />
71 <span className="block h-2 w-7/12 rounded-full bg-white/56" />
72 </div>
73 <div className="grid grid-cols-3 gap-2">
74 <span className="h-7 rounded-md bg-white/54" />
75 <span className="h-7 rounded-md bg-white/42" />
76 <span className="h-7 rounded-md bg-white/54" />
77 </div>
78 </div>
79 )}
80 </div>
81
82 {page.status === 'generating' && (
83 <div className="absolute inset-0 border-2 border-[#83ad67]/70">
84 <div className="absolute right-2 top-2 rounded-full bg-[#fffaf1]/90 p-1 shadow-sm">
85 <Loader2 className="h-3.5 w-3.5 animate-spin text-[#5f8a43]" />
86 </div>
87 </div>
88 )}
89
90 {page.status === 'failed' && (
91 <div className="absolute inset-0 flex items-center justify-center bg-[#fbf1ee]/76">
92 <CircleAlert className="h-6 w-6 text-[#a45f58]" />
93 </div>
94 )}
95 </div>
96
97 <div className="flex w-full min-w-0 items-center justify-between gap-2 px-0.5 pb-0.5 pt-2">
98 <span className="shrink-0 rounded-md bg-[#5d6b4d]/10 px-1.5 py-0.5 text-[10px] font-semibold text-[#4f613f]">
99 P{page.pageNumber}
100 </span>
101 {page.status !== 'failed' && (
102 <span className="min-w-0 truncate text-xs font-medium text-[#4d5b40]" title={page.title}>
103 {page.title}
104 </span>
105 )}
106 </div>
107 </div>
108 )
109 }
110
110 lines Plain Text