import React, { useState, useRef } from 'react' import { ChevronLeft, ChevronRight } from 'lucide-react' import { cn } from '@/utils/cn' import { Button } from '@/components/ui/button' import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area' import { useSize } from './hooks/useSize' import { PPTEditProvider, type SlideJson } from '@/components/html-editor/context' import { PPTEditToolBar } from '@/components/html-editor/components/toolbar-web' import { usePPTEditor } from '@/components/ppt-editor/hooks/usePPTEditor' import { FloatingToolbar } from './FloatingToolbar' import type { TaskArtifact } from '../nova-sdk' export interface SlideItem { content: string file_name: string file_type: string id: string path : string title: string [key: string]: unknown } export interface PptPreviewProps { /** conversationId */ taskId: string | null | undefined /** PPT 文件的 URL */ url: string /** TaskArtifact */ artifact: TaskArtifact /** 是否可编辑 */ editable?: boolean } /** * PPT 预览组件 */ export const PptPreview = ({ url, artifact, taskId, editable = true}: PptPreviewProps) => { const [currentIndex, setCurrentIndex] = useState(0) const [slideList, setSlideList] = useState([]) const [sliderJson,setSliderJson] = useState() const [loading, setLoading] = useState(true) const containerRef = useRef(null) React.useEffect(() => { if (!url) return setLoading(true) fetch(url) .then(res => res.json()) .then(data => { const slides = data.slide_list || [] setSlideList(slides) setSliderJson(data) }) .catch(() => setSlideList([])) .finally(() => setLoading(false)) }, [url]) if (loading) { return (
加载中...
) } if (!slideList || slideList.length === 0) { return (
📊

暂无幻灯片内容

) } return (
{editable && }
) } function PptSlideViewer({ slideList, currentIndex, setCurrentIndex, artifact, sliderJson, taskId, editable }: { slideList: SlideItem[] currentIndex: number setCurrentIndex: (index: number) => void artifact: TaskArtifact sliderJson: SlideJson | undefined taskId:string | null | undefined editable: boolean }) { const containerRef = useRef(null) const iframeRef = useRef(null) const size = useSize(containerRef) const [iframeHeight, setIframeHeight] = useState(720) const [loadState, setLoadState] = useState<'loading' | 'loaded' | 'error'>('loading') const currentSlide = slideList[currentIndex] const scale = size ? size.width / 1280 : 1 const handleIframeLoad = (event: React.SyntheticEvent) => { const iframe = event.currentTarget try { const actualHeight = iframe.contentDocument?.documentElement.scrollHeight if (actualHeight && actualHeight > 0) { setIframeHeight(actualHeight) } setLoadState('loaded') } catch (error) { console.warn('Cannot access iframe content:', error) setLoadState('loaded') } } const handleIframeError = () => { setLoadState('error') } // 切换幻灯片时重置加载状态 React.useEffect(() => { setLoadState('loading') setIframeHeight(720) }, [currentIndex]) const editState = usePPTEditor(currentSlide.id, iframeRef, scale, sliderJson, artifact, taskId, editable) return (
{/* 悬浮工具栏 */} {editable && (
editState.undo.current()} onRedo={() => editState.redo.current()} onSave={editState.manualSave} isSaving={editState.isSaving} saveType={editState.saveType} />
)} {/* 主预览区 */}
{loadState === 'loading' && (

加载中...

)}