返回 oh-my-ppt
GenerationLogPanel.tsx
根目录 / src / renderer / src / components / session-generating / GenerationLogPanel.tsx
1 import type React from 'react'
2 import dayjs from 'dayjs'
3 import { CircleAlert, Loader2, Sparkles } from 'lucide-react'
4 import { ScrollArea } from '@renderer/components/ui/ScrollArea'
5 import type { GenerationLogEvent, GenerationRunStatus } from './types'
6
7 export function GenerationLogPanel({
8 events,
9 status,
10 pageCountLabel,
11 growingLabel,
12 failedLabel,
13 logTitle,
14 viewportRef,
15 onViewportScroll
16 }: {
17 events: GenerationLogEvent[]
18 status: GenerationRunStatus
19 pageCountLabel: string
20 growingLabel: string
21 failedLabel: string
22 logTitle: string
23 viewportRef?: React.Ref<HTMLDivElement>
24 onViewportScroll?: React.UIEventHandler<HTMLDivElement>
25 }): React.JSX.Element {
26 return (
27 <section className="relative flex min-h-0 flex-1 flex-col rounded-lg border border-[#d8ccb5]/72 bg-[#fff9ef]/82 p-2.5 shadow-[0_14px_30px_rgba(78,91,63,0.1)]">
28 <div className="mb-2 flex min-h-8 items-center pr-14">
29 <div className="flex min-w-0 items-center gap-1.5 text-xs font-semibold text-[#495a3b]">
30 <Sparkles className="h-4 w-4 text-[#6f8159]" />
31 <span className="min-w-0 truncate">{logTitle}</span>
32 </div>
33 <span
34 className="absolute right-2.5 top-2.5 z-20 inline-flex h-6 min-w-10 items-center justify-center rounded-md border border-[#b8d3a6] bg-[#edf6e8] px-2 text-[11px] font-semibold text-[#365528] shadow-sm"
35 title={pageCountLabel}
36 >
37 {pageCountLabel}
38 </span>
39 </div>
40
41 <ScrollArea
42 className="min-h-0 flex-1 rounded-lg border border-[#e4d9c3]/55 bg-[#fffaf1]/38"
43 viewportRef={viewportRef}
44 onViewportScroll={onViewportScroll}
45 viewportClassName="px-2 py-2"
46 >
47 <div className="space-y-2">
48 {events.map((event, index) => (
49 <div
50 key={`${event.text}-${index}`}
51 className="rounded-lg border border-[#e4d9c3]/70 bg-white/46 px-2.5 py-1.5 text-xs leading-5 text-[#5a674c] shadow-[0_6px_14px_rgba(93,107,77,0.06)]"
52 >
53 {event.time && (
54 <div className="mb-0.5 text-[10px] leading-4 text-[#a09882]">
55 {dayjs(event.time).format('HH:mm:ss')}
56 </div>
57 )}
58 <div className="break-words">{event.text}</div>
59 </div>
60 ))}
61
62 {status === 'queued' || status === 'running' ? (
63 <div className="flex items-center gap-2 rounded-lg border border-[#e4d9c3]/70 bg-white/46 px-2.5 py-1.5 text-xs text-[#a09882] shadow-[0_6px_14px_rgba(93,107,77,0.06)]">
64 <Loader2 className="h-3 w-3 shrink-0 animate-spin" />
65 <span className="min-w-0 truncate">{growingLabel}</span>
66 </div>
67 ) : status === 'failed' || status === 'cancelled' ? (
68 <div className="flex items-center gap-2 rounded-lg border border-[#d7b5ae]/70 bg-[#fff8f4]/72 px-2.5 py-1.5 text-xs text-[#93564f] shadow-[0_6px_14px_rgba(93,107,77,0.06)]">
69 <CircleAlert className="h-3.5 w-3.5 shrink-0" />
70 <span className="min-w-0 truncate">{failedLabel}</span>
71 </div>
72 ) : null}
73 </div>
74 </ScrollArea>
75 </section>
76 )
77 }
78
78 lines Plain Text