{"version":3,"file":"index-BROjlnEN.mjs","sources":["../../src/renderers/Pptx/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback, forwardRef, useImperativeHandle } from 'react';\nimport { init } from 'pptx-preview';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\nimport type { RendererHandle } from '../base.types';\n\ninterface PptxRendererProps {\n  url: string;\n  /** 是否平铺展示所有页面，默认 true */\n  tiled?: boolean;\n}\n\nexport const PptxRenderer = forwardRef<RendererHandle, PptxRendererProps>(({ url, tiled = true }, ref) => {\n  const t = useTranslator();\n  const fetcher = useFetcher();\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n  const [slideCount, setSlideCount] = useState(0);\n  const containerRef = useRef<HTMLDivElement>(null);\n  const previewerRef = useRef<ReturnType<typeof init> | null>(null);\n  const resizeObserverRef = useRef<ResizeObserver | null>(null);\n  const arrayBufferRef = useRef<ArrayBuffer | null>(null);\n  const resizeTimeoutRef = useRef<number | null>(null);\n  const lastDimensionsRef = useRef({ width: 0, height: 0 });\n\n  // 计算容器尺寸，带回退逻辑\n  const calculateDimensions = useCallback(() => {\n    if (!containerRef.current) return { width: 960, height: 540 };\n    const rawWidth = containerRef.current.clientWidth;\n    const parentWidth = containerRef.current.parentElement?.clientWidth || 0;\n    // 如果容器宽度太小，回退到父容器宽度或默认最小值\n    const containerWidth = rawWidth > 100 ? rawWidth : (parentWidth > 100 ? parentWidth : 300);\n    // 16:9 比例\n    const height = Math.floor(containerWidth * 9 / 16);\n    return { width: containerWidth, height };\n  }, []);\n\n  // 重新初始化预览器\n  const reinitializePreviewer = useCallback(async () => {\n    if (!containerRef.current || !arrayBufferRef.current || slideCount === 0) return;\n\n    try {\n      // 销毁旧的预览器\n      if (previewerRef.current) {\n        try {\n          previewerRef.current.destroy();\n        } catch {\n          // 忽略销毁错误\n        }\n      }\n\n      // 清空容器\n      containerRef.current.innerHTML = '';\n\n      // 获取当前容器尺寸\n      const currentDimensions = calculateDimensions();\n\n      // 初始化新的预览器，平铺模式下高度按页数计算\n      const previewer = init(containerRef.current, {\n        width: currentDimensions.width,\n        height: tiled ? currentDimensions.height * slideCount : currentDimensions.height,\n        mode: tiled ? 'list' : 'slide',\n      });\n      previewerRef.current = previewer;\n\n      // 重新预览\n      await previewer.preview(arrayBufferRef.current);\n    } catch {\n      // 重新初始化失败，静默处理\n    }\n  }, [calculateDimensions, tiled, slideCount]);\n\n  // 监听容器尺寸变化\n  useEffect(() => {\n    if (!containerRef.current) return;\n\n    let isInitialRender = true;\n\n    const updateDimensions = () => {\n      // 跳过初始渲染时的尺寸检查\n      if (isInitialRender) {\n        isInitialRender = false;\n        lastDimensionsRef.current = calculateDimensions();\n        return;\n      }\n\n      const newDimensions = calculateDimensions();\n\n      // 检查尺寸是否真正变化（至少变化10px才触发）\n      const lastDimensions = lastDimensionsRef.current;\n      const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n      const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n      if (widthDiff < 10 && heightDiff < 10) {\n        return;\n      }\n\n      // 更新最后的尺寸\n      lastDimensionsRef.current = newDimensions;\n\n      // 清除之前的定时器\n      if (resizeTimeoutRef.current) {\n        clearTimeout(resizeTimeoutRef.current);\n      }\n\n      // 防抖：800ms 后重新初始化预览器\n      resizeTimeoutRef.current = window.setTimeout(() => {\n        if (previewerRef.current && arrayBufferRef.current) {\n          reinitializePreviewer();\n        }\n      }, 800);\n    };\n\n    // 创建 ResizeObserver\n    resizeObserverRef.current = new ResizeObserver(() => {\n      updateDimensions();\n    });\n\n    // 开始观察容器\n    resizeObserverRef.current.observe(containerRef.current);\n\n    return () => {\n      if (resizeObserverRef.current) {\n        resizeObserverRef.current.disconnect();\n      }\n      if (resizeTimeoutRef.current) {\n        clearTimeout(resizeTimeoutRef.current);\n      }\n    };\n  }, [calculateDimensions, reinitializePreviewer]);\n\n  useEffect(() => {\n    // 只有 URL 有效时才加载（避免空字符串或已 revoke 的 blob URL）\n    if (!url) return;\n\n    let isMounted = true;\n    let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n    const loadPptx = async () => {\n      if (!containerRef.current) return;\n\n      setLoading(true);\n      setError(null);\n\n      // 设置30秒超时\n      timeoutId = setTimeout(() => {\n        if (isMounted) {\n          setError(t('pptx.timeout'));\n          setLoading(false);\n        }\n      }, 30000);\n\n      try {\n        // 获取文件，处理 CORS 和重定向\n        const response = await fetcher(url, {\n          mode: 'cors',\n          credentials: 'omit',\n          redirect: 'follow',\n        });\n\n        if (!response.ok) {\n          if (response.status === 404) {\n            throw new Error(t('pptx.not_found'));\n          } else if (response.status === 403) {\n            throw new Error('无权限访问此文件');\n          } else if (response.status >= 500) {\n            throw new Error('服务器错误，请稍后重试');\n          } else {\n            throw new Error(`文件加载失败 (${response.status})`);\n          }\n        }\n\n        const arrayBuffer = await response.arrayBuffer();\n\n        // 验证文件大小\n        if (arrayBuffer.byteLength === 0) {\n          throw new Error('文件为空');\n        }\n\n        arrayBufferRef.current = arrayBuffer;\n\n        if (!isMounted) return;\n\n        // 步骤 1: 创建隐藏容器，预处理获取 slideCount\n        const hiddenContainer = document.createElement('div');\n        hiddenContainer.style.cssText = 'position:absolute;left:-9999px;top:-9999px;visibility:hidden';\n        document.body.appendChild(hiddenContainer);\n\n        try {\n          // 在隐藏容器中初始化临时预览器获取页数\n          const tempPreviewer = init(hiddenContainer, {\n            width: 100,\n            height: 100,\n            mode: 'slide',\n          });\n\n          try {\n            await tempPreviewer.preview(arrayBuffer);\n          } catch {\n            throw new Error(t('pptx.invalid_format'));\n          }\n\n          const count = tempPreviewer.slideCount;\n\n          if (!count || count === 0) {\n            throw new Error(t('pptx.no_pages'));\n          }\n\n          // 销毁临时预览器\n          tempPreviewer.destroy();\n\n          if (!isMounted) return;\n\n          // 保存 slideCount\n          setSlideCount(count);\n\n          // 步骤 2: 清空真实容器并初始化\n          if (containerRef.current) {\n            containerRef.current.innerHTML = '';\n          }\n\n          const currentDimensions = calculateDimensions();\n\n          // 步骤 3: 初始化真实预览器，平铺模式下使用正确的总高度\n          const previewer = init(containerRef.current, {\n            width: currentDimensions.width,\n            height: tiled ? currentDimensions.height * count : currentDimensions.height,\n            mode: tiled ? 'list' : 'slide',\n          });\n          previewerRef.current = previewer;\n\n          // 步骤 4: 预览 PPTX\n          await previewer.preview(arrayBuffer);\n\n          // 清除超时定时器\n          if (timeoutId) {\n            clearTimeout(timeoutId);\n            timeoutId = null;\n          }\n\n          if (isMounted) {\n            setLoading(false);\n          }\n        } finally {\n          // 移除隐藏容器\n          if (document.body.contains(hiddenContainer)) {\n            document.body.removeChild(hiddenContainer);\n          }\n        }\n      } catch (err) {\n        // 清除超时定时器\n        if (timeoutId) {\n          clearTimeout(timeoutId);\n          timeoutId = null;\n        }\n\n        if (isMounted) {\n          let errorMsg = t('pptx.parse_failed');\n          if (err instanceof Error) {\n            errorMsg = err.message;\n          } else if (typeof err === 'string') {\n            errorMsg = err;\n          }\n          setError(errorMsg);\n          setLoading(false);\n        }\n      }\n    };\n\n    // 延迟执行，使用 requestAnimationFrame 确保 DOM 已准备好\n    const timer = setTimeout(() => {\n      requestAnimationFrame(() => {\n        requestAnimationFrame(() => {\n          loadPptx();\n        });\n      });\n    }, 150);\n\n    // 清理函数\n    return () => {\n      isMounted = false;\n      clearTimeout(timer);\n      if (timeoutId) {\n        clearTimeout(timeoutId);\n      }\n      arrayBufferRef.current = null;\n      setSlideCount(0);\n      if (previewerRef.current) {\n        try {\n          previewerRef.current.destroy();\n        } catch {\n          // 忽略销毁错误\n        }\n      }\n      previewerRef.current = null;\n    };\n  }, [url, calculateDimensions, tiled]);\n\n  // 暴露接口给父组件\n  useImperativeHandle(ref, () => ({\n    getToolbarGroups: () => [],\n  }), []);\n\n  return (\n    <div className=\"rfp-relative rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full\">\n      {/* 加载状态 - 绝对定位覆盖 */}\n      {loading && (\n        <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-toolbar rfp-backdrop-blur-sm rfp-z-10\">\n          <div className=\"rfp-text-center\">\n            <div className=\"rfp-w-10 rfp-h-10 md:rfp-w-12 md:rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n            <p className=\"rfp-text-xs md:rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('pptx.loading')}</p>\n          </div>\n        </div>\n      )}\n\n      {/* 错误状态 - 绝对定位覆盖 */}\n      {error && !loading && (\n        <RendererError message={t('pptx.load_failed')} detail={error} />\n      )}\n\n      {/* PPT 容器 - 仅在非错误状态下渲染 */}\n      {!error && (\n        <div\n          ref={containerRef}\n          className=\"pptx-wrapper rfp-w-full rfp-max-w-full md:rfp-max-w-6xl\"\n          style={{ opacity: loading ? 0 : 1 }}\n        />\n      )}\n    </div>\n  );\n});\n"],"names":["PptxRenderer","forwardRef","url","tiled","ref","t","useTranslator","fetcher","useFetcher","loading","setLoading","useState","error","setError","slideCount","setSlideCount","containerRef","useRef","previewerRef","resizeObserverRef","arrayBufferRef","resizeTimeoutRef","lastDimensionsRef","calculateDimensions","useCallback","rawWidth","parentWidth","_a","containerWidth","height","reinitializePreviewer","currentDimensions","previewer","init","useEffect","isInitialRender","updateDimensions","newDimensions","lastDimensions","widthDiff","heightDiff","isMounted","timeoutId","loadPptx","response","arrayBuffer","hiddenContainer","tempPreviewer","count","err","errorMsg","timer","useImperativeHandle","jsxs","jsx","RendererError"],"mappings":"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"}