@eternalheart/react-file-preview 1.3.13 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/lib/FilePreviewContent.d.ts +3 -17
  2. package/lib/FilePreviewContent.d.ts.map +1 -1
  3. package/lib/FilePreviewEmbed.d.ts +2 -0
  4. package/lib/FilePreviewEmbed.d.ts.map +1 -1
  5. package/lib/chunks/RendererError-D5i8eSpN.mjs +15 -0
  6. package/lib/chunks/RendererError-D5i8eSpN.mjs.map +1 -0
  7. package/lib/chunks/{index-CIqtwqgy.mjs → index-0v5STX5f.mjs} +18 -18
  8. package/lib/chunks/index-0v5STX5f.mjs.map +1 -0
  9. package/lib/chunks/{index-p4mew8Hx.mjs → index-10O8tfTH.mjs} +7 -7
  10. package/lib/chunks/index-10O8tfTH.mjs.map +1 -0
  11. package/lib/chunks/{index-CRn7LdHD.mjs → index-BCyv1HM9.mjs} +6 -6
  12. package/lib/chunks/index-BCyv1HM9.mjs.map +1 -0
  13. package/lib/chunks/{index-B2IlXQPc.mjs → index-Bo90aGhy.mjs} +3 -3
  14. package/lib/chunks/{index-B2IlXQPc.mjs.map → index-Bo90aGhy.mjs.map} +1 -1
  15. package/lib/chunks/index-CEeKt7L3.mjs +2808 -0
  16. package/lib/chunks/index-CEeKt7L3.mjs.map +1 -0
  17. package/lib/chunks/{index-DtZwBUd0.mjs → index-CWKbnvW6.mjs} +3 -3
  18. package/lib/chunks/{index-DtZwBUd0.mjs.map → index-CWKbnvW6.mjs.map} +1 -1
  19. package/lib/chunks/{index-B0QA380T.mjs → index-C_BJatqr.mjs} +14 -14
  20. package/lib/chunks/index-C_BJatqr.mjs.map +1 -0
  21. package/lib/chunks/index-Cbz5Z6ZK.mjs +263 -0
  22. package/lib/chunks/index-Cbz5Z6ZK.mjs.map +1 -0
  23. package/lib/chunks/{index-DYNPnFww.mjs → index-Cp68OevR.mjs} +3 -3
  24. package/lib/chunks/{index-DYNPnFww.mjs.map → index-Cp68OevR.mjs.map} +1 -1
  25. package/lib/chunks/{index-_EpQHlUY.mjs → index-CzM2mxrD.mjs} +4 -4
  26. package/lib/chunks/{index-_EpQHlUY.mjs.map → index-CzM2mxrD.mjs.map} +1 -1
  27. package/lib/chunks/{index-DfkP0zX3.mjs → index-DTYBFuAH.mjs} +4 -4
  28. package/lib/chunks/{index-DfkP0zX3.mjs.map → index-DTYBFuAH.mjs.map} +1 -1
  29. package/lib/chunks/{index-DqXfw2eb.mjs → index-DaAXRBWL.mjs} +3 -3
  30. package/lib/chunks/{index-DqXfw2eb.mjs.map → index-DaAXRBWL.mjs.map} +1 -1
  31. package/lib/chunks/{index-BgMhDDkd.mjs → index-DoFsoBKL.mjs} +3 -3
  32. package/lib/chunks/{index-BgMhDDkd.mjs.map → index-DoFsoBKL.mjs.map} +1 -1
  33. package/lib/chunks/{index-sft0uUd7.mjs → index-DuP0Tlpo.mjs} +3 -3
  34. package/lib/chunks/{index-sft0uUd7.mjs.map → index-DuP0Tlpo.mjs.map} +1 -1
  35. package/lib/chunks/index-Dv3RQz86.mjs +270 -0
  36. package/lib/chunks/index-Dv3RQz86.mjs.map +1 -0
  37. package/lib/chunks/{index-8pqs-pW7.mjs → index-QfpHck8N.mjs} +5 -5
  38. package/lib/chunks/{index-8pqs-pW7.mjs.map → index-QfpHck8N.mjs.map} +1 -1
  39. package/lib/chunks/{index-DN4Lc1dx.mjs → index-gjSQeou7.mjs} +3 -3
  40. package/lib/chunks/{index-DN4Lc1dx.mjs.map → index-gjSQeou7.mjs.map} +1 -1
  41. package/lib/chunks/{index-DZxzCMp2.mjs → index-kALp0tqz.mjs} +3 -3
  42. package/lib/chunks/{index-DZxzCMp2.mjs.map → index-kALp0tqz.mjs.map} +1 -1
  43. package/lib/chunks/{index-C8r2-Evl.mjs → index-kCeSnFs-.mjs} +9 -9
  44. package/lib/chunks/{index-C8r2-Evl.mjs.map → index-kCeSnFs-.mjs.map} +1 -1
  45. package/lib/chunks/{useShikiHighlight-CDDi36pF.mjs → useShikiHighlight-BA9qgdGA.mjs} +2 -2
  46. package/lib/chunks/{useShikiHighlight-CDDi36pF.mjs.map → useShikiHighlight-BA9qgdGA.mjs.map} +1 -1
  47. package/lib/components/preview/FilePreviewRenderer.d.ts +18 -0
  48. package/lib/components/preview/FilePreviewRenderer.d.ts.map +1 -0
  49. package/lib/components/preview/FilePreviewToolbar.d.ts +19 -0
  50. package/lib/components/preview/FilePreviewToolbar.d.ts.map +1 -0
  51. package/lib/components/preview/NavArrows.d.ts +18 -0
  52. package/lib/components/preview/NavArrows.d.ts.map +1 -0
  53. package/lib/components/preview/RendererError.d.ts +15 -0
  54. package/lib/components/preview/RendererError.d.ts.map +1 -0
  55. package/lib/components/preview/RendererErrorBoundary.d.ts +23 -0
  56. package/lib/components/preview/RendererErrorBoundary.d.ts.map +1 -0
  57. package/lib/components/preview/ToolbarButton.d.ts +14 -0
  58. package/lib/components/preview/ToolbarButton.d.ts.map +1 -0
  59. package/lib/components/preview/index.d.ts +7 -0
  60. package/lib/components/preview/index.d.ts.map +1 -0
  61. package/lib/hooks/index.d.ts +9 -0
  62. package/lib/hooks/index.d.ts.map +1 -0
  63. package/lib/hooks/rendererReducer.d.ts +10 -0
  64. package/lib/hooks/rendererReducer.d.ts.map +1 -0
  65. package/lib/hooks/types.d.ts +152 -0
  66. package/lib/hooks/types.d.ts.map +1 -0
  67. package/lib/hooks/useBookRenderer.d.ts +14 -0
  68. package/lib/hooks/useBookRenderer.d.ts.map +1 -0
  69. package/lib/hooks/useFilePreviewState.d.ts +10 -0
  70. package/lib/hooks/useFilePreviewState.d.ts.map +1 -0
  71. package/lib/hooks/useImageAutoFit.d.ts +13 -0
  72. package/lib/hooks/useImageAutoFit.d.ts.map +1 -0
  73. package/lib/hooks/useKeyboardNavigation.d.ts +15 -0
  74. package/lib/hooks/useKeyboardNavigation.d.ts.map +1 -0
  75. package/lib/hooks/useThemeMode.d.ts +7 -0
  76. package/lib/hooks/useThemeMode.d.ts.map +1 -0
  77. package/lib/hooks/useToolbarConfig.d.ts +25 -0
  78. package/lib/hooks/useToolbarConfig.d.ts.map +1 -0
  79. package/lib/index.cjs +30 -30
  80. package/lib/index.cjs.map +1 -1
  81. package/lib/index.css +1 -1
  82. package/lib/index.mjs +1 -1
  83. package/lib/renderers/Epub/index.d.ts.map +1 -1
  84. package/lib/renderers/Markdown/index.d.ts.map +1 -1
  85. package/lib/renderers/Pdf/index.d.ts +2 -0
  86. package/lib/renderers/Pdf/index.d.ts.map +1 -1
  87. package/lib/renderers/Pdf/toolbar.d.ts +5 -0
  88. package/lib/renderers/Pdf/toolbar.d.ts.map +1 -1
  89. package/lib/renderers/toolbar.types.d.ts +1 -0
  90. package/lib/renderers/toolbar.types.d.ts.map +1 -1
  91. package/lib/toolbar/registry.d.ts +51 -0
  92. package/lib/toolbar/registry.d.ts.map +1 -0
  93. package/lib/toolbar/renderItems.d.ts +8 -0
  94. package/lib/toolbar/renderItems.d.ts.map +1 -0
  95. package/package.json +1 -1
  96. package/lib/chunks/RendererError-BH6fzLrN.mjs +0 -15
  97. package/lib/chunks/RendererError-BH6fzLrN.mjs.map +0 -1
  98. package/lib/chunks/index-B0QA380T.mjs.map +0 -1
  99. package/lib/chunks/index-BvjPzMFc.mjs +0 -161
  100. package/lib/chunks/index-BvjPzMFc.mjs.map +0 -1
  101. package/lib/chunks/index-CIqtwqgy.mjs.map +0 -1
  102. package/lib/chunks/index-CRn7LdHD.mjs.map +0 -1
  103. package/lib/chunks/index-CWCNvV2X.mjs +0 -2323
  104. package/lib/chunks/index-CWCNvV2X.mjs.map +0 -1
  105. package/lib/chunks/index-Ctf8mG_u.mjs +0 -240
  106. package/lib/chunks/index-Ctf8mG_u.mjs.map +0 -1
  107. package/lib/chunks/index-p4mew8Hx.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index-DtZwBUd0.mjs","sources":["../../src/renderers/Image/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { motion } from 'framer-motion';\nimport { Loader2 } from 'lucide-react';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { detectImageFormat, getLoaderForMimeType } from '@eternalheart/file-preview-core';\nimport type { PreviewFile } from '@eternalheart/file-preview-core';\nimport { RendererError } from '../RendererError';\n\ninterface ImageRendererProps {\n url: string;\n zoom: number;\n rotation: number;\n resetKey?: number;\n fileSize?: number;\n file?: PreviewFile | File;\n onZoomChange?: (zoom: number) => void;\n onNaturalWidthChange?: (width: number) => void;\n onNaturalHeightChange?: (height: number) => void;\n}\n\nexport const ImageRenderer: React.FC<ImageRendererProps> = ({\n url,\n zoom,\n rotation,\n resetKey,\n fileSize,\n file,\n onZoomChange,\n onNaturalWidthChange,\n onNaturalHeightChange\n}) => {\n const t = useTranslator();\n const [loaded, setLoaded] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [decoding, setDecoding] = useState(false);\n const [decodeProgress, setDecodeProgress] = useState(0);\n const [decodeError, setDecodeError] = useState<string | null>(null);\n const [imageSrc, setImageSrc] = useState<string>('');\n const [currentPage, setCurrentPage] = useState(1);\n const [totalPages, setTotalPages] = useState(1);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const [dragStart, setDragStart] = useState({ x: 0, y: 0 });\n const [internalZoom, setInternalZoom] = useState(1);\n const [naturalSize, setNaturalSize] = useState({ width: 0, height: 0 });\n const imgRef = useRef<HTMLImageElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const blobUrlRef = useRef<string | null>(null);\n const fileBlobRef = useRef<Blob | null>(null);\n const loaderRef = useRef<any>(null);\n const pageCacheRef = useRef<Map<number, string>>(new Map());\n const isTouchDevice = useRef(false);\n const touchStartDistance = useRef(0);\n const touchStartZoom = useRef(1);\n const touchStartPos = useRef({ x: 0, y: 0 });\n const lastTapTime = useRef(0);\n\n // 解码逻辑:检测格式并按需解码\n useEffect(() => {\n let cancelled = false;\n\n const decodeIfNeeded = async () => {\n // 重置状态:清空 src 以避免上一张图片的 onLoad/onError 误触发到新文件\n setImageSrc('');\n setLoaded(false);\n setError(null);\n setDecoding(false);\n setDecodeError(null);\n setDecodeProgress(0);\n setPosition({ x: 0, y: 0 });\n setInternalZoom(1);\n setCurrentPage(1);\n setTotalPages(1);\n\n // 清理旧的 blob URL 与缓存\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n blobUrlRef.current = null;\n }\n pageCacheRef.current.forEach((url) => URL.revokeObjectURL(url));\n pageCacheRef.current.clear();\n fileBlobRef.current = null;\n loaderRef.current = null;\n\n // 如果没有 file 对象,直接使用 url\n if (!file) {\n if (!cancelled) setImageSrc(url);\n return;\n }\n\n try {\n // 检测图片格式\n const mimeType = await detectImageFormat(file);\n const loader = await getLoaderForMimeType(mimeType);\n\n // 如果不需要解码,直接使用原 URL\n if (!loader || !(await loader.needsDecode(mimeType))) {\n if (!cancelled) setImageSrc(url);\n return;\n }\n\n // 需要解码\n setDecoding(true);\n\n // 获取文件 Blob\n let fileBlob: Blob;\n if (file instanceof Blob) {\n fileBlob = file;\n } else {\n const response = await fetch(url);\n if (!response.ok) throw new Error('Failed to fetch file');\n fileBlob = await response.blob();\n }\n\n if (cancelled) return;\n\n // 缓存 Blob 与 loader 以便后续翻页\n fileBlobRef.current = fileBlob;\n loaderRef.current = loader;\n\n // 缓存 Blob 与 loader 以便后续翻页\n fileBlobRef.current = fileBlob;\n loaderRef.current = loader;\n\n // 获取元数据(用于检测多页 TIFF)\n if (loader.getMetadata) {\n try {\n const metadata = await loader.getMetadata(fileBlob);\n if (!cancelled && metadata.pageCount && metadata.pageCount > 1) {\n setTotalPages(metadata.pageCount);\n }\n } catch {\n // 忽略元数据获取失败\n }\n }\n\n // 调用 loader 解码(第 1 页 / 缩略图模式)\n const decodedBlob = await loader.decode(fileBlob, {\n page: 1,\n fullQuality: false,\n onProgress: (percent: number) => {\n if (!cancelled) {\n setDecodeProgress(percent);\n }\n },\n });\n\n if (cancelled) return;\n\n // 生成 blob URL\n const blobUrl = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n blobUrlRef.current = blobUrl;\n pageCacheRef.current.set(1, blobUrl);\n setImageSrc(blobUrl);\n setDecoding(false);\n } catch (err: any) {\n if (!cancelled) {\n setDecodeError(err?.message || '解码失败');\n setDecoding(false);\n }\n }\n };\n\n decodeIfNeeded();\n\n return () => {\n cancelled = true;\n };\n }, [url, file]);\n\n // 多页 TIFF 翻页:切换页码时重新解码\n const handlePageChange = useCallback(async (page: number) => {\n if (!fileBlobRef.current || !loaderRef.current) return;\n if (page < 1 || page > totalPages) return;\n\n // 命中缓存:直接切换\n const cached = pageCacheRef.current.get(page);\n if (cached) {\n setCurrentPage(page);\n setImageSrc(cached);\n return;\n }\n\n // 解码新页面\n setDecoding(true);\n try {\n const decodedBlob = await loaderRef.current.decode(fileBlobRef.current, { page });\n const blobUrl = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n // LRU:缓存超过 10 页时删除最早的\n if (pageCacheRef.current.size >= 10) {\n const firstKey = pageCacheRef.current.keys().next().value;\n if (firstKey !== undefined) {\n const oldUrl = pageCacheRef.current.get(firstKey);\n if (oldUrl) URL.revokeObjectURL(oldUrl);\n pageCacheRef.current.delete(firstKey);\n }\n }\n\n pageCacheRef.current.set(page, blobUrl);\n setCurrentPage(page);\n setImageSrc(blobUrl);\n setDecoding(false);\n } catch (err: any) {\n setDecodeError(err?.message || '翻页解码失败');\n setDecoding(false);\n }\n }, [totalPages]);\n\n // Cleanup blob URL on unmount\n useEffect(() => {\n return () => {\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n }\n pageCacheRef.current.forEach((url) => URL.revokeObjectURL(url));\n pageCacheRef.current.clear();\n };\n }, []);\n\n // 当外部 zoom 改变时,同步内部 zoom\n useEffect(() => {\n setInternalZoom(zoom);\n }, [zoom]);\n\n // 适应窗口/原始尺寸等操作时重置位置居中\n useEffect(() => {\n if (resetKey !== undefined) {\n setPosition({ x: 0, y: 0 });\n }\n }, [resetKey]);\n\n const handleLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n setLoaded(true);\n const img = e.currentTarget;\n setNaturalSize({ width: img.naturalWidth, height: img.naturalHeight });\n onNaturalWidthChange?.(img.naturalWidth);\n onNaturalHeightChange?.(img.naturalHeight);\n };\n\n // 边界限制:确保图片至少有一部分可见\n const clampPosition = useCallback((pos: { x: number; y: number }, currentZoom: number) => {\n const container = containerRef.current;\n if (!container || naturalSize.width === 0) return pos;\n\n const containerW = container.clientWidth;\n const containerH = container.clientHeight;\n const imgW = naturalSize.width * currentZoom;\n const imgH = naturalSize.height * currentZoom;\n\n // 至少保留 margin px 的图片在视口内\n const margin = Math.min(80, containerW * 0.15, containerH * 0.15);\n const rangeX = (containerW + imgW) / 2 - margin;\n const rangeY = (containerH + imgH) / 2 - margin;\n\n return {\n x: rangeX > 0 ? Math.max(-rangeX, Math.min(rangeX, pos.x)) : 0,\n y: rangeY > 0 ? Math.max(-rangeY, Math.min(rangeY, pos.y)) : 0,\n };\n }, [naturalSize]);\n\n const handleError = () => {\n setError(t('image.load_failed'));\n setLoaded(true);\n };\n\n // 双击复原:居中 + 缩放100%\n const handleDoubleClick = () => {\n setPosition({ x: 0, y: 0 });\n setInternalZoom(1);\n onZoomChange?.(1);\n };\n\n // 触屏事件处理\n const handleTouchStart = useCallback((e: TouchEvent) => {\n isTouchDevice.current = true;\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1) {\n // 单指拖拽\n setIsDragging(true);\n setDragStart({\n x: touches[0].clientX - position.x,\n y: touches[0].clientY - position.y,\n });\n\n // 双击检测\n const now = Date.now();\n if (now - lastTapTime.current < 300) {\n // 双击复原:居中 + 缩放100%\n setPosition({ x: 0, y: 0 });\n setInternalZoom(1);\n onZoomChange?.(1);\n }\n lastTapTime.current = now;\n } else if (touches.length === 2) {\n // 双指缩放初始化\n setIsDragging(false);\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n touchStartDistance.current = distance;\n touchStartZoom.current = internalZoom;\n touchStartPos.current = { ...position };\n }\n }, [position, internalZoom, onZoomChange]);\n\n const handleTouchMove = useCallback((e: TouchEvent) => {\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1 && isDragging) {\n // 单指拖拽\n setPosition(clampPosition({\n x: touches[0].clientX - dragStart.x,\n y: touches[0].clientY - dragStart.y,\n }, internalZoom));\n } else if (touches.length === 2) {\n // 双指缩放\n const container = containerRef.current;\n if (!container) return;\n\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n\n // 最小距离变化阈值,防止抖动\n if (Math.abs(distance - touchStartDistance.current) < 5) return;\n\n const scale = distance / touchStartDistance.current;\n const newZoom = Math.max(0.01, Math.min(10, touchStartZoom.current * scale));\n\n // 双指中心点作为缩放原点\n const rect = container.getBoundingClientRect();\n const centerX = (touches[0].clientX + touches[1].clientX) / 2 - rect.left - rect.width / 2;\n const centerY = (touches[0].clientY + touches[1].clientY) / 2 - rect.top - rect.height / 2;\n\n const zoomScale = newZoom / internalZoom;\n setPosition(clampPosition({\n x: centerX - zoomScale * (centerX - touchStartPos.current.x),\n y: centerY - zoomScale * (centerY - touchStartPos.current.y),\n }, newZoom));\n\n setInternalZoom(newZoom);\n onZoomChange?.(newZoom);\n }\n }, [isDragging, dragStart, internalZoom, clampPosition, onZoomChange]);\n\n const handleTouchEnd = useCallback(() => {\n setIsDragging(false);\n touchStartDistance.current = 0;\n }, []);\n\n // 鼠标滚轮缩放 —— 以鼠标位置为缩放原点\n // 使用原生事件 + passive: false,确保 preventDefault 生效,\n // 避免滚轮事件冒泡触发外层(如嵌入模式下的页面滚动)\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleWheelNative = (e: WheelEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const rect = container.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - rect.width / 2;\n const mouseY = e.clientY - rect.top - rect.height / 2;\n\n const delta = e.deltaY > 0 ? -0.05 : 0.05;\n\n setInternalZoom(prev => {\n const newZoom = Math.max(0.01, Math.min(10, prev + delta));\n const scale = newZoom / prev;\n\n setPosition(pos => clampPosition({\n x: mouseX - scale * (mouseX - pos.x),\n y: mouseY - scale * (mouseY - pos.y),\n }, newZoom));\n\n onZoomChange?.(newZoom);\n return newZoom;\n });\n };\n\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n return () => container.removeEventListener('wheel', handleWheelNative);\n }, [onZoomChange, clampPosition]);\n\n // 触屏事件监听器\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n\n return () => {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n };\n }, [handleTouchStart, handleTouchMove, handleTouchEnd]);\n\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n if (isTouchDevice.current) return;\n if (e.button !== 0) return;\n setIsDragging(true);\n setDragStart({\n x: e.clientX - position.x,\n y: e.clientY - position.y,\n });\n }, [position]);\n\n const handleMouseMove = useCallback((e: React.MouseEvent) => {\n if (isTouchDevice.current) return;\n if (!isDragging) return;\n setPosition(clampPosition({\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n }, internalZoom));\n }, [isDragging, dragStart, internalZoom, clampPosition]);\n\n const handleMouseUp = useCallback(() => {\n if (isTouchDevice.current) return;\n setIsDragging(false);\n }, []);\n\n return (\n <div\n ref={containerRef}\n className=\"rfp-relative rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full rfp-overflow-hidden\"\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n style={{ cursor: isDragging ? 'grabbing' : 'grab', touchAction: 'none' }}\n >\n {/* 解码中 */}\n {decoding && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-flex-col rfp-items-center rfp-justify-center rfp-bg-surface-1/80 rfp-z-10\">\n <Loader2 className=\"rfp-w-12 rfp-h-12 rfp-text-fg-primary rfp-animate-spin\" />\n <p className=\"rfp-mt-4 rfp-text-fg-secondary\">\n 正在解码... {decodeProgress > 0 && `${Math.round(decodeProgress)}%`}\n </p>\n </div>\n )}\n\n {/* 解码错误 */}\n {decodeError && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-1/80 rfp-z-10\">\n <RendererError message={t('image.decode_failed')} detail={decodeError} />\n </div>\n )}\n\n {!loaded && !error && !decoding && !decodeError && (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n\n {error && (\n <RendererError message={error} />\n )}\n\n {imageSrc && (\n <motion.img\n ref={imgRef}\n src={imageSrc}\n alt=\"Preview\"\n className={`rfp-max-w-none rfp-select-none ${!loaded || error || decodeError ? 'rfp-hidden' : ''}`}\n style={{\n transform: `translate(${position.x}px, ${position.y}px) scale(${internalZoom}) rotate(${rotation}deg)`,\n transformOrigin: 'center',\n transition: isDragging ? 'none' : 'transform 0.3s ease-out',\n }}\n onLoad={handleLoad}\n onError={handleError}\n onDoubleClick={handleDoubleClick}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded && !error && !decodeError ? 1 : 0 }}\n transition={{ duration: 0.3 }}\n draggable={false}\n />\n )}\n\n {/* 右下角分辨率 */}\n {loaded && !error && naturalSize.width > 0 && (\n <div className=\"rfp-absolute rfp-bottom-2 rfp-right-3 rfp-text-[10px] rfp-text-fg-disabled hover:rfp-text-fg-secondary rfp-transition-colors rfp-pointer-events-auto rfp-select-none rfp-cursor-default\">\n {naturalSize.width} × {naturalSize.height}{fileSize != null && ` · ${fileSize < 1024 ? `${fileSize} B` : fileSize < 1024 * 1024 ? `${(fileSize / 1024).toFixed(1)} KB` : `${(fileSize / (1024 * 1024)).toFixed(1)} MB`}`}\n </div>\n )}\n\n {/* 多页 TIFF 翻页器 */}\n {totalPages > 1 && (\n <div className=\"rfp-absolute rfp-bottom-2 rfp-left-1/2 -rfp-translate-x-1/2 rfp-flex rfp-items-center rfp-gap-2 rfp-px-3 rfp-py-1.5 rfp-bg-surface-toolbar rfp-border rfp-border-line rfp-rounded-lg rfp-text-sm rfp-text-fg-primary rfp-shadow-md\">\n <button\n type=\"button\"\n onClick={() => handlePageChange(currentPage - 1)}\n disabled={currentPage <= 1 || decoding}\n className=\"rfp-px-2 rfp-py-0.5 rfp-rounded hover:rfp-bg-surface-nav-hover disabled:rfp-opacity-40 disabled:rfp-cursor-not-allowed\"\n >\n 上一页\n </button>\n <span className=\"rfp-text-fg-secondary rfp-tabular-nums\">\n {currentPage} / {totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => handlePageChange(currentPage + 1)}\n disabled={currentPage >= totalPages || decoding}\n className=\"rfp-px-2 rfp-py-0.5 rfp-rounded hover:rfp-bg-surface-nav-hover disabled:rfp-opacity-40 disabled:rfp-cursor-not-allowed\"\n >\n 下一页\n </button>\n </div>\n )}\n </div>\n );\n};\n"],"names":["ImageRenderer","url","zoom","rotation","resetKey","fileSize","file","onZoomChange","onNaturalWidthChange","onNaturalHeightChange","t","useTranslator","loaded","setLoaded","useState","error","setError","decoding","setDecoding","decodeProgress","setDecodeProgress","decodeError","setDecodeError","imageSrc","setImageSrc","currentPage","setCurrentPage","totalPages","setTotalPages","position","setPosition","isDragging","setIsDragging","dragStart","setDragStart","internalZoom","setInternalZoom","naturalSize","setNaturalSize","imgRef","useRef","containerRef","blobUrlRef","fileBlobRef","loaderRef","pageCacheRef","isTouchDevice","touchStartDistance","touchStartZoom","touchStartPos","lastTapTime","useEffect","cancelled","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","blobUrl","err","handlePageChange","useCallback","page","cached","firstKey","oldUrl","handleLoad","img","clampPosition","pos","currentZoom","container","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleError","handleDoubleClick","handleTouchStart","touches","now","distance","handleTouchMove","scale","newZoom","rect","centerX","centerY","zoomScale","handleTouchEnd","handleWheelNative","e","mouseX","mouseY","delta","prev","handleMouseDown","handleMouseMove","handleMouseUp","jsxs","jsx","Loader2","RendererError","motion"],"mappings":"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"}
1
+ {"version":3,"file":"index-CWKbnvW6.mjs","sources":["../../src/renderers/Image/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { motion } from 'framer-motion';\nimport { Loader2 } from 'lucide-react';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { detectImageFormat, getLoaderForMimeType } from '@eternalheart/file-preview-core';\nimport type { PreviewFile } from '@eternalheart/file-preview-core';\nimport { RendererError } from '../RendererError';\n\ninterface ImageRendererProps {\n url: string;\n zoom: number;\n rotation: number;\n resetKey?: number;\n fileSize?: number;\n file?: PreviewFile | File;\n onZoomChange?: (zoom: number) => void;\n onNaturalWidthChange?: (width: number) => void;\n onNaturalHeightChange?: (height: number) => void;\n}\n\nexport const ImageRenderer: React.FC<ImageRendererProps> = ({\n url,\n zoom,\n rotation,\n resetKey,\n fileSize,\n file,\n onZoomChange,\n onNaturalWidthChange,\n onNaturalHeightChange\n}) => {\n const t = useTranslator();\n const [loaded, setLoaded] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [decoding, setDecoding] = useState(false);\n const [decodeProgress, setDecodeProgress] = useState(0);\n const [decodeError, setDecodeError] = useState<string | null>(null);\n const [imageSrc, setImageSrc] = useState<string>('');\n const [currentPage, setCurrentPage] = useState(1);\n const [totalPages, setTotalPages] = useState(1);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const [dragStart, setDragStart] = useState({ x: 0, y: 0 });\n const [internalZoom, setInternalZoom] = useState(1);\n const [naturalSize, setNaturalSize] = useState({ width: 0, height: 0 });\n const imgRef = useRef<HTMLImageElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const blobUrlRef = useRef<string | null>(null);\n const fileBlobRef = useRef<Blob | null>(null);\n const loaderRef = useRef<any>(null);\n const pageCacheRef = useRef<Map<number, string>>(new Map());\n const isTouchDevice = useRef(false);\n const touchStartDistance = useRef(0);\n const touchStartZoom = useRef(1);\n const touchStartPos = useRef({ x: 0, y: 0 });\n const lastTapTime = useRef(0);\n\n // 解码逻辑:检测格式并按需解码\n useEffect(() => {\n let cancelled = false;\n\n const decodeIfNeeded = async () => {\n // 重置状态:清空 src 以避免上一张图片的 onLoad/onError 误触发到新文件\n setImageSrc('');\n setLoaded(false);\n setError(null);\n setDecoding(false);\n setDecodeError(null);\n setDecodeProgress(0);\n setPosition({ x: 0, y: 0 });\n setInternalZoom(1);\n setCurrentPage(1);\n setTotalPages(1);\n\n // 清理旧的 blob URL 与缓存\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n blobUrlRef.current = null;\n }\n pageCacheRef.current.forEach((url) => URL.revokeObjectURL(url));\n pageCacheRef.current.clear();\n fileBlobRef.current = null;\n loaderRef.current = null;\n\n // 如果没有 file 对象,直接使用 url\n if (!file) {\n if (!cancelled) setImageSrc(url);\n return;\n }\n\n try {\n // 检测图片格式\n const mimeType = await detectImageFormat(file);\n const loader = await getLoaderForMimeType(mimeType);\n\n // 如果不需要解码,直接使用原 URL\n if (!loader || !(await loader.needsDecode(mimeType))) {\n if (!cancelled) setImageSrc(url);\n return;\n }\n\n // 需要解码\n setDecoding(true);\n\n // 获取文件 Blob\n let fileBlob: Blob;\n if (file instanceof Blob) {\n fileBlob = file;\n } else {\n const response = await fetch(url);\n if (!response.ok) throw new Error('Failed to fetch file');\n fileBlob = await response.blob();\n }\n\n if (cancelled) return;\n\n // 缓存 Blob 与 loader 以便后续翻页\n fileBlobRef.current = fileBlob;\n loaderRef.current = loader;\n\n // 缓存 Blob 与 loader 以便后续翻页\n fileBlobRef.current = fileBlob;\n loaderRef.current = loader;\n\n // 获取元数据(用于检测多页 TIFF)\n if (loader.getMetadata) {\n try {\n const metadata = await loader.getMetadata(fileBlob);\n if (!cancelled && metadata.pageCount && metadata.pageCount > 1) {\n setTotalPages(metadata.pageCount);\n }\n } catch {\n // 忽略元数据获取失败\n }\n }\n\n // 调用 loader 解码(第 1 页 / 缩略图模式)\n const decodedBlob = await loader.decode(fileBlob, {\n page: 1,\n fullQuality: false,\n onProgress: (percent: number) => {\n if (!cancelled) {\n setDecodeProgress(percent);\n }\n },\n });\n\n if (cancelled) return;\n\n // 生成 blob URL\n const blobUrl = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n blobUrlRef.current = blobUrl;\n pageCacheRef.current.set(1, blobUrl);\n setImageSrc(blobUrl);\n setDecoding(false);\n } catch (err: any) {\n if (!cancelled) {\n setDecodeError(err?.message || '解码失败');\n setDecoding(false);\n }\n }\n };\n\n decodeIfNeeded();\n\n return () => {\n cancelled = true;\n };\n }, [url, file]);\n\n // 多页 TIFF 翻页:切换页码时重新解码\n const handlePageChange = useCallback(async (page: number) => {\n if (!fileBlobRef.current || !loaderRef.current) return;\n if (page < 1 || page > totalPages) return;\n\n // 命中缓存:直接切换\n const cached = pageCacheRef.current.get(page);\n if (cached) {\n setCurrentPage(page);\n setImageSrc(cached);\n return;\n }\n\n // 解码新页面\n setDecoding(true);\n try {\n const decodedBlob = await loaderRef.current.decode(fileBlobRef.current, { page });\n const blobUrl = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n // LRU:缓存超过 10 页时删除最早的\n if (pageCacheRef.current.size >= 10) {\n const firstKey = pageCacheRef.current.keys().next().value;\n if (firstKey !== undefined) {\n const oldUrl = pageCacheRef.current.get(firstKey);\n if (oldUrl) URL.revokeObjectURL(oldUrl);\n pageCacheRef.current.delete(firstKey);\n }\n }\n\n pageCacheRef.current.set(page, blobUrl);\n setCurrentPage(page);\n setImageSrc(blobUrl);\n setDecoding(false);\n } catch (err: any) {\n setDecodeError(err?.message || '翻页解码失败');\n setDecoding(false);\n }\n }, [totalPages]);\n\n // Cleanup blob URL on unmount\n useEffect(() => {\n return () => {\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n }\n pageCacheRef.current.forEach((url) => URL.revokeObjectURL(url));\n pageCacheRef.current.clear();\n };\n }, []);\n\n // 当外部 zoom 改变时,同步内部 zoom\n useEffect(() => {\n setInternalZoom(zoom);\n }, [zoom]);\n\n // 适应窗口/原始尺寸等操作时重置位置居中\n useEffect(() => {\n if (resetKey !== undefined) {\n setPosition({ x: 0, y: 0 });\n }\n }, [resetKey]);\n\n const handleLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n setLoaded(true);\n const img = e.currentTarget;\n setNaturalSize({ width: img.naturalWidth, height: img.naturalHeight });\n onNaturalWidthChange?.(img.naturalWidth);\n onNaturalHeightChange?.(img.naturalHeight);\n };\n\n // 边界限制:确保图片至少有一部分可见\n const clampPosition = useCallback((pos: { x: number; y: number }, currentZoom: number) => {\n const container = containerRef.current;\n if (!container || naturalSize.width === 0) return pos;\n\n const containerW = container.clientWidth;\n const containerH = container.clientHeight;\n const imgW = naturalSize.width * currentZoom;\n const imgH = naturalSize.height * currentZoom;\n\n // 至少保留 margin px 的图片在视口内\n const margin = Math.min(80, containerW * 0.15, containerH * 0.15);\n const rangeX = (containerW + imgW) / 2 - margin;\n const rangeY = (containerH + imgH) / 2 - margin;\n\n return {\n x: rangeX > 0 ? Math.max(-rangeX, Math.min(rangeX, pos.x)) : 0,\n y: rangeY > 0 ? Math.max(-rangeY, Math.min(rangeY, pos.y)) : 0,\n };\n }, [naturalSize]);\n\n const handleError = () => {\n setError(t('image.load_failed'));\n setLoaded(true);\n };\n\n // 双击复原:居中 + 缩放100%\n const handleDoubleClick = () => {\n setPosition({ x: 0, y: 0 });\n setInternalZoom(1);\n onZoomChange?.(1);\n };\n\n // 触屏事件处理\n const handleTouchStart = useCallback((e: TouchEvent) => {\n isTouchDevice.current = true;\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1) {\n // 单指拖拽\n setIsDragging(true);\n setDragStart({\n x: touches[0].clientX - position.x,\n y: touches[0].clientY - position.y,\n });\n\n // 双击检测\n const now = Date.now();\n if (now - lastTapTime.current < 300) {\n // 双击复原:居中 + 缩放100%\n setPosition({ x: 0, y: 0 });\n setInternalZoom(1);\n onZoomChange?.(1);\n }\n lastTapTime.current = now;\n } else if (touches.length === 2) {\n // 双指缩放初始化\n setIsDragging(false);\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n touchStartDistance.current = distance;\n touchStartZoom.current = internalZoom;\n touchStartPos.current = { ...position };\n }\n }, [position, internalZoom, onZoomChange]);\n\n const handleTouchMove = useCallback((e: TouchEvent) => {\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1 && isDragging) {\n // 单指拖拽\n setPosition(clampPosition({\n x: touches[0].clientX - dragStart.x,\n y: touches[0].clientY - dragStart.y,\n }, internalZoom));\n } else if (touches.length === 2) {\n // 双指缩放\n const container = containerRef.current;\n if (!container) return;\n\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n\n // 最小距离变化阈值,防止抖动\n if (Math.abs(distance - touchStartDistance.current) < 5) return;\n\n const scale = distance / touchStartDistance.current;\n const newZoom = Math.max(0.01, Math.min(10, touchStartZoom.current * scale));\n\n // 双指中心点作为缩放原点\n const rect = container.getBoundingClientRect();\n const centerX = (touches[0].clientX + touches[1].clientX) / 2 - rect.left - rect.width / 2;\n const centerY = (touches[0].clientY + touches[1].clientY) / 2 - rect.top - rect.height / 2;\n\n const zoomScale = newZoom / internalZoom;\n setPosition(clampPosition({\n x: centerX - zoomScale * (centerX - touchStartPos.current.x),\n y: centerY - zoomScale * (centerY - touchStartPos.current.y),\n }, newZoom));\n\n setInternalZoom(newZoom);\n onZoomChange?.(newZoom);\n }\n }, [isDragging, dragStart, internalZoom, clampPosition, onZoomChange]);\n\n const handleTouchEnd = useCallback(() => {\n setIsDragging(false);\n touchStartDistance.current = 0;\n }, []);\n\n // 鼠标滚轮缩放 —— 以鼠标位置为缩放原点\n // 使用原生事件 + passive: false,确保 preventDefault 生效,\n // 避免滚轮事件冒泡触发外层(如嵌入模式下的页面滚动)\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleWheelNative = (e: WheelEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const rect = container.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - rect.width / 2;\n const mouseY = e.clientY - rect.top - rect.height / 2;\n\n const delta = e.deltaY > 0 ? -0.05 : 0.05;\n\n setInternalZoom(prev => {\n const newZoom = Math.max(0.01, Math.min(10, prev + delta));\n const scale = newZoom / prev;\n\n setPosition(pos => clampPosition({\n x: mouseX - scale * (mouseX - pos.x),\n y: mouseY - scale * (mouseY - pos.y),\n }, newZoom));\n\n onZoomChange?.(newZoom);\n return newZoom;\n });\n };\n\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n return () => container.removeEventListener('wheel', handleWheelNative);\n }, [onZoomChange, clampPosition]);\n\n // 触屏事件监听器\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n\n return () => {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n };\n }, [handleTouchStart, handleTouchMove, handleTouchEnd]);\n\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n if (isTouchDevice.current) return;\n if (e.button !== 0) return;\n setIsDragging(true);\n setDragStart({\n x: e.clientX - position.x,\n y: e.clientY - position.y,\n });\n }, [position]);\n\n const handleMouseMove = useCallback((e: React.MouseEvent) => {\n if (isTouchDevice.current) return;\n if (!isDragging) return;\n setPosition(clampPosition({\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n }, internalZoom));\n }, [isDragging, dragStart, internalZoom, clampPosition]);\n\n const handleMouseUp = useCallback(() => {\n if (isTouchDevice.current) return;\n setIsDragging(false);\n }, []);\n\n return (\n <div\n ref={containerRef}\n className=\"rfp-relative rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full rfp-overflow-hidden\"\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n style={{ cursor: isDragging ? 'grabbing' : 'grab', touchAction: 'none' }}\n >\n {/* 解码中 */}\n {decoding && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-flex-col rfp-items-center rfp-justify-center rfp-bg-surface-1/80 rfp-z-10\">\n <Loader2 className=\"rfp-w-12 rfp-h-12 rfp-text-fg-primary rfp-animate-spin\" />\n <p className=\"rfp-mt-4 rfp-text-fg-secondary\">\n 正在解码... {decodeProgress > 0 && `${Math.round(decodeProgress)}%`}\n </p>\n </div>\n )}\n\n {/* 解码错误 */}\n {decodeError && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-1/80 rfp-z-10\">\n <RendererError message={t('image.decode_failed')} detail={decodeError} />\n </div>\n )}\n\n {!loaded && !error && !decoding && !decodeError && (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n\n {error && (\n <RendererError message={error} />\n )}\n\n {imageSrc && (\n <motion.img\n ref={imgRef}\n src={imageSrc}\n alt=\"Preview\"\n className={`rfp-max-w-none rfp-select-none ${!loaded || error || decodeError ? 'rfp-hidden' : ''}`}\n style={{\n transform: `translate(${position.x}px, ${position.y}px) scale(${internalZoom}) rotate(${rotation}deg)`,\n transformOrigin: 'center',\n transition: isDragging ? 'none' : 'transform 0.3s ease-out',\n }}\n onLoad={handleLoad}\n onError={handleError}\n onDoubleClick={handleDoubleClick}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded && !error && !decodeError ? 1 : 0 }}\n transition={{ duration: 0.3 }}\n draggable={false}\n />\n )}\n\n {/* 右下角分辨率 */}\n {loaded && !error && naturalSize.width > 0 && (\n <div className=\"rfp-absolute rfp-bottom-2 rfp-right-3 rfp-text-[10px] rfp-text-fg-disabled hover:rfp-text-fg-secondary rfp-transition-colors rfp-pointer-events-auto rfp-select-none rfp-cursor-default\">\n {naturalSize.width} × {naturalSize.height}{fileSize != null && ` · ${fileSize < 1024 ? `${fileSize} B` : fileSize < 1024 * 1024 ? `${(fileSize / 1024).toFixed(1)} KB` : `${(fileSize / (1024 * 1024)).toFixed(1)} MB`}`}\n </div>\n )}\n\n {/* 多页 TIFF 翻页器 */}\n {totalPages > 1 && (\n <div className=\"rfp-absolute rfp-bottom-2 rfp-left-1/2 -rfp-translate-x-1/2 rfp-flex rfp-items-center rfp-gap-2 rfp-px-3 rfp-py-1.5 rfp-bg-surface-toolbar rfp-border rfp-border-line rfp-rounded-lg rfp-text-sm rfp-text-fg-primary rfp-shadow-md\">\n <button\n type=\"button\"\n onClick={() => handlePageChange(currentPage - 1)}\n disabled={currentPage <= 1 || decoding}\n className=\"rfp-px-2 rfp-py-0.5 rfp-rounded hover:rfp-bg-surface-nav-hover disabled:rfp-opacity-40 disabled:rfp-cursor-not-allowed\"\n >\n 上一页\n </button>\n <span className=\"rfp-text-fg-secondary rfp-tabular-nums\">\n {currentPage} / {totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => handlePageChange(currentPage + 1)}\n disabled={currentPage >= totalPages || decoding}\n className=\"rfp-px-2 rfp-py-0.5 rfp-rounded hover:rfp-bg-surface-nav-hover disabled:rfp-opacity-40 disabled:rfp-cursor-not-allowed\"\n >\n 下一页\n </button>\n </div>\n )}\n </div>\n );\n};\n"],"names":["ImageRenderer","url","zoom","rotation","resetKey","fileSize","file","onZoomChange","onNaturalWidthChange","onNaturalHeightChange","t","useTranslator","loaded","setLoaded","useState","error","setError","decoding","setDecoding","decodeProgress","setDecodeProgress","decodeError","setDecodeError","imageSrc","setImageSrc","currentPage","setCurrentPage","totalPages","setTotalPages","position","setPosition","isDragging","setIsDragging","dragStart","setDragStart","internalZoom","setInternalZoom","naturalSize","setNaturalSize","imgRef","useRef","containerRef","blobUrlRef","fileBlobRef","loaderRef","pageCacheRef","isTouchDevice","touchStartDistance","touchStartZoom","touchStartPos","lastTapTime","useEffect","cancelled","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","blobUrl","err","handlePageChange","useCallback","page","cached","firstKey","oldUrl","handleLoad","img","clampPosition","pos","currentZoom","container","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleError","handleDoubleClick","handleTouchStart","touches","now","distance","handleTouchMove","scale","newZoom","rect","centerX","centerY","zoomScale","handleTouchEnd","handleWheelNative","e","mouseX","mouseY","delta","prev","handleMouseDown","handleMouseMove","handleMouseUp","jsxs","jsx","Loader2","RendererError","motion"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as r, jsxs as t, Fragment as y } from "react/jsx-runtime";
2
- import { useState as c, useEffect as E, useMemo as k } from "react";
3
- import { u as F, a as R, P as S, N as n, E as T } from "./index-CWCNvV2X.mjs";
4
- import { R as _ } from "./RendererError-BH6fzLrN.mjs";
2
+ import { useState as d, useEffect as E, useMemo as k } from "react";
3
+ import { u as F, a as R, T as S, P as n, E as T } from "./index-CEeKt7L3.mjs";
4
+ import { R as _ } from "./RendererError-D5i8eSpN.mjs";
5
5
  const $ = {
6
6
  srt: "srt",
7
7
  vtt: "vtt",
@@ -16,7 +16,7 @@ const $ = {
16
16
  const l = ((f = a.split(".").pop()) == null ? void 0 : f.toLowerCase()) || "";
17
17
  return $[l];
18
18
  }, P = ({ url: a, fileName: l }) => {
19
- const f = F(), b = R(), [o, h] = c(""), [v, m] = c(!0), [u, x] = c(null);
19
+ const f = F(), b = R(), [o, h] = d(""), [v, m] = d(!0), [u, x] = d(null);
20
20
  E(() => {
21
21
  const e = new AbortController();
22
22
  return (async () => {
@@ -44,13 +44,13 @@ const $ = {
44
44
  return /* @__PURE__ */ r(_, { message: u || f("subtitle.parse_failed") });
45
45
  const i = s.format === "lrc" || s.format === "elrc", g = s.metadata ?? {}, w = i ? "group-hover:rfp-bg-violet-400" : "group-hover:rfp-bg-sky-400";
46
46
  return /* @__PURE__ */ t("div", { className: "rfp-relative rfp-w-full rfp-h-full rfp-bg-[#0f0f12]", children: [
47
- /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-px-6 md:rfp-px-10 rfp-pt-6 rfp-pb-16 md:rfp-pb-20", children: /* @__PURE__ */ t("div", { className: "rfp-relative rfp-max-w-5xl rfp-mx-auto", children: [
48
- /* @__PURE__ */ r("div", { className: "rfp-absolute rfp-left-[5px] md:rfp-left-[7px] rfp-top-2 rfp-bottom-2 rfp-w-px rfp-bg-surface-1" }),
49
- /* @__PURE__ */ r("ol", { className: "rfp-space-y-5 md:rfp-space-y-6", children: s.cues.map((e, d) => /* @__PURE__ */ t("li", { className: "rfp-relative rfp-pl-6 md:rfp-pl-8 rfp-group", children: [
47
+ /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-px-4 rfp-pt-6 rfp-pb-16", children: /* @__PURE__ */ t("div", { className: "rfp-relative", children: [
48
+ /* @__PURE__ */ r("div", { className: "rfp-absolute rfp-left-[5px] rfp-top-2 rfp-bottom-2 rfp-w-px rfp-bg-surface-1" }),
49
+ /* @__PURE__ */ r("ol", { className: "rfp-space-y-5", children: s.cues.map((e, c) => /* @__PURE__ */ t("li", { className: "rfp-relative rfp-pl-6 rfp-group", children: [
50
50
  /* @__PURE__ */ r(
51
51
  "div",
52
52
  {
53
- className: `rfp-absolute rfp-left-0 rfp-top-2 rfp-w-3 rfp-h-3 rfp-rounded-full rfp-bg-surface-3 rfp-border-2 rfp-border-[#0f0f12] rfp-transition-colors ${w}`
53
+ className: `rfp-absolute rfp-left-0 rfp-top-[0.4rem] rfp-w-3 rfp-h-3 rfp-rounded-full rfp-bg-surface-3 rfp-border-2 rfp-border-[#0f0f12] rfp-transition-colors ${w}`
54
54
  }
55
55
  ),
56
56
  /* @__PURE__ */ t("div", { className: "rfp-flex rfp-flex-wrap rfp-items-baseline rfp-gap-x-3 rfp-gap-y-1 rfp-mb-1.5", children: [
@@ -59,11 +59,11 @@ const $ = {
59
59
  /* @__PURE__ */ r("span", { className: "rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums", children: n(e.end) }),
60
60
  /* @__PURE__ */ t("span", { className: "rfp-text-[10px] rfp-font-mono rfp-text-fg-disabled rfp-tabular-nums", children: [
61
61
  "#",
62
- e.id ?? d + 1
62
+ e.id ?? c + 1
63
63
  ] }),
64
64
  e.style && /* @__PURE__ */ r("span", { className: "rfp-text-[9px] rfp-uppercase rfp-tracking-widest rfp-text-fg-tertiary rfp-px-1.5 rfp-py-0.5 rfp-rounded rfp-bg-surface-1 rfp-border rfp-border-line-weak", children: e.style })
65
65
  ] }),
66
- e.words && e.words.length > 0 ? /* @__PURE__ */ r("div", { className: "rfp-flex rfp-flex-wrap rfp-gap-x-1.5 rfp-gap-y-1 rfp-text-base md:rfp-text-lg rfp-text-fg-primary rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors", children: e.words.map((p, N) => /* @__PURE__ */ t(
66
+ e.words && e.words.length > 0 ? /* @__PURE__ */ r("div", { className: "rfp-flex rfp-flex-wrap rfp-gap-x-1.5 rfp-gap-y-1 rfp-text-base rfp-text-fg-primary rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors", children: e.words.map((p, N) => /* @__PURE__ */ t(
67
67
  "span",
68
68
  {
69
69
  className: "rfp-inline-flex rfp-flex-col rfp-items-start",
@@ -77,13 +77,13 @@ const $ = {
77
77
  )) }) : /* @__PURE__ */ r(
78
78
  "p",
79
79
  {
80
- className: `rfp-whitespace-pre-wrap rfp-break-words rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors rfp-text-fg-primary ${i ? "rfp-text-base md:rfp-text-xl rfp-font-medium" : "rfp-text-sm md:rfp-text-base"}`,
80
+ className: `rfp-whitespace-pre-wrap rfp-break-words rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors rfp-text-fg-primary rfp-min-h-[1.25rem] ${i ? "rfp-text-base rfp-font-medium" : "rfp-text-sm"}`,
81
81
  children: e.text
82
82
  }
83
83
  )
84
- ] }, `cue-${d}`)) })
84
+ ] }, `cue-${c}`)) })
85
85
  ] }) }),
86
- /* @__PURE__ */ t("div", { className: "rfp-pointer-events-none rfp-absolute rfp-bottom-3 rfp-right-3 md:rfp-bottom-4 md:rfp-right-4 rfp-flex rfp-items-center rfp-gap-2 rfp-px-2.5 rfp-py-1 rfp-rounded-full rfp-bg-surface-nav rfp-backdrop-blur rfp-border rfp-border-line-weak rfp-text-[10px] rfp-text-fg-tertiary rfp-font-mono rfp-tabular-nums", children: [
86
+ /* @__PURE__ */ t("div", { className: "rfp-pointer-events-none rfp-absolute rfp-bottom-3 rfp-right-3 rfp-flex rfp-items-center rfp-gap-2 rfp-px-2.5 rfp-py-1 rfp-rounded-full rfp-bg-surface-nav rfp-backdrop-blur rfp-border rfp-border-line-weak rfp-text-[10px] rfp-text-fg-tertiary rfp-font-mono rfp-tabular-nums", children: [
87
87
  /* @__PURE__ */ t("span", { children: [
88
88
  s.cues.length,
89
89
  " ",
@@ -99,4 +99,4 @@ const $ = {
99
99
  export {
100
100
  P as SubtitleRenderer
101
101
  };
102
- //# sourceMappingURL=index-B0QA380T.mjs.map
102
+ //# sourceMappingURL=index-C_BJatqr.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-C_BJatqr.mjs","sources":["../../src/renderers/Subtitle/index.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport {\n parseSubtitle,\n formatSubtitleTime,\n fetchTextUtf8,\n type SubtitleParseResult,\n type SubtitleFormat,\n} from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface SubtitleRendererProps {\n url: string;\n fileName: string;\n}\n\nconst FORMAT_BY_EXT: Record<string, SubtitleFormat> = {\n srt: 'srt',\n vtt: 'vtt',\n lrc: 'lrc',\n elrc: 'elrc',\n ass: 'ass',\n ssa: 'ssa',\n ttml: 'ttml',\n dfxp: 'ttml',\n};\n\nconst getFormat = (fileName: string): SubtitleFormat | undefined => {\n const ext = fileName.split('.').pop()?.toLowerCase() || '';\n return FORMAT_BY_EXT[ext];\n};\n\nexport const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({ url, fileName }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [text, setText] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n setText(await fetchTextUtf8(url, { fetcher, signal: controller.signal }));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.warn('[SubtitleRenderer] Failed to load subtitle:', err instanceof Error ? err.message : String(err));\n setError(t('subtitle.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\n\n const parsed: SubtitleParseResult | null = useMemo(() => {\n if (!text) return null;\n try {\n return parseSubtitle(text, getFormat(fileName));\n } catch (err) {\n // 字幕解析失败通常是格式不支持或文件损坏,用 warn 级别记录\n console.warn('[SubtitleRenderer] Failed to parse subtitle:', err instanceof Error ? err.message : String(err));\n return null;\n }\n }, [text, fileName]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full rfp-bg-[#0f0f12]\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error || !parsed) {\n return <RendererError message={error || t('subtitle.parse_failed')} />;\n }\n\n const isLyric = parsed.format === 'lrc' || parsed.format === 'elrc';\n const meta = parsed.metadata ?? {};\n const dotHover = isLyric ? 'group-hover:rfp-bg-violet-400' : 'group-hover:rfp-bg-sky-400';\n\n return (\n <div className=\"rfp-relative rfp-w-full rfp-h-full rfp-bg-[#0f0f12]\">\n {/* 内容滚动区 */}\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-px-4 rfp-pt-6 rfp-pb-16\">\n <div className=\"rfp-relative\">\n {/* vertical line */}\n <div className=\"rfp-absolute rfp-left-[5px] rfp-top-2 rfp-bottom-2 rfp-w-px rfp-bg-surface-1\" />\n\n <ol className=\"rfp-space-y-5\">\n {parsed.cues.map((cue, i) => (\n <li key={`cue-${i}`} className=\"rfp-relative rfp-pl-6 rfp-group\">\n {/* dot */}\n <div\n className={`rfp-absolute rfp-left-0 rfp-top-[0.4rem] rfp-w-3 rfp-h-3 rfp-rounded-full rfp-bg-surface-3 rfp-border-2 rfp-border-[#0f0f12] rfp-transition-colors ${dotHover}`}\n />\n\n <div className=\"rfp-flex rfp-flex-wrap rfp-items-baseline rfp-gap-x-3 rfp-gap-y-1 rfp-mb-1.5\">\n <span className=\"rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums\">\n {formatSubtitleTime(cue.start)}\n </span>\n <span className=\"rfp-text-[11px] rfp-text-fg-disabled\">→</span>\n <span className=\"rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums\">\n {formatSubtitleTime(cue.end)}\n </span>\n <span className=\"rfp-text-[10px] rfp-font-mono rfp-text-fg-disabled rfp-tabular-nums\">\n #{cue.id ?? i + 1}\n </span>\n {cue.style && (\n <span className=\"rfp-text-[9px] rfp-uppercase rfp-tracking-widest rfp-text-fg-tertiary rfp-px-1.5 rfp-py-0.5 rfp-rounded rfp-bg-surface-1 rfp-border rfp-border-line-weak\">\n {cue.style}\n </span>\n )}\n </div>\n\n {cue.words && cue.words.length > 0 ? (\n <div className=\"rfp-flex rfp-flex-wrap rfp-gap-x-1.5 rfp-gap-y-1 rfp-text-base rfp-text-fg-primary rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors\">\n {cue.words.map((word, wi) => (\n <span\n key={`w-${wi}`}\n className=\"rfp-inline-flex rfp-flex-col rfp-items-start\"\n title={formatSubtitleTime(word.start)}\n >\n <span className=\"rfp-text-[9px] rfp-text-fg-disabled rfp-font-mono rfp-leading-none rfp-tabular-nums\">\n {formatSubtitleTime(word.start).slice(3, 8)}\n </span>\n <span className=\"rfp-leading-snug\">{word.text}</span>\n </span>\n ))}\n </div>\n ) : (\n <p\n className={`rfp-whitespace-pre-wrap rfp-break-words rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors rfp-text-fg-primary rfp-min-h-[1.25rem] ${\n isLyric ? 'rfp-text-base rfp-font-medium' : 'rfp-text-sm'\n }`}\n >\n {cue.text}\n </p>\n )}\n </li>\n ))}\n </ol>\n </div>\n </div>\n\n {/* 底部状态栏 */}\n <div className=\"rfp-pointer-events-none rfp-absolute rfp-bottom-3 rfp-right-3 rfp-flex rfp-items-center rfp-gap-2 rfp-px-2.5 rfp-py-1 rfp-rounded-full rfp-bg-surface-nav rfp-backdrop-blur rfp-border rfp-border-line-weak rfp-text-[10px] rfp-text-fg-tertiary rfp-font-mono rfp-tabular-nums\">\n <span>{parsed.cues.length} {isLyric ? t('subtitle.lines') : t('subtitle.cues')}</span>\n {meta.length && (\n <>\n <span className=\"rfp-text-fg-disabled\">·</span>\n <span>{meta.length}</span>\n </>\n )}\n </div>\n </div>\n );\n};\n"],"names":["FORMAT_BY_EXT","getFormat","fileName","ext","_a","SubtitleRenderer","url","t","useTranslator","fetcher","useFetcher","text","setText","useState","loading","setLoading","error","setError","useEffect","controller","fetchTextUtf8","err","parsed","useMemo","parseSubtitle","jsx","RendererError","isLyric","meta","dotHover","jsxs","cue","i","formatSubtitleTime","word","wi","Fragment"],"mappings":"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"}
@@ -0,0 +1,263 @@
1
+ import { jsxs as E, jsx as l } from "react/jsx-runtime";
2
+ import { forwardRef as dr, useRef as f, useState as x, useCallback as b, useImperativeHandle as mr, useEffect as q } from "react";
3
+ import hr from "@likecoin/epub-ts";
4
+ import { X as br } from "lucide-react";
5
+ import { u as wr, a as gr } from "./index-CEeKt7L3.mjs";
6
+ import { R as yr } from "./RendererError-D5i8eSpN.mjs";
7
+ if (typeof document < "u" && !document.getElementById("rfp-epub-styles")) {
8
+ const u = document.createElement("style");
9
+ u.id = "rfp-epub-styles", u.textContent = `
10
+ .epub-container { overflow-y: auto !important; scrollbar-width: thin; }
11
+ .epub-container::-webkit-scrollbar { width: 8px; }
12
+ .epub-container::-webkit-scrollbar-track { background: transparent; }
13
+ .epub-container::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 4px; }
14
+ .epub-container::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.3); }
15
+ .epub-view > iframe { background: white; }
16
+ `, document.head.appendChild(u);
17
+ }
18
+ const vr = 794, Er = dr(
19
+ ({ url: u, onChapterChange: M, onFullWidthChange: X }, sr) => {
20
+ const _ = wr(), cr = gr(), h = f(null), I = f(null), p = f(null), d = f(M), W = f(X);
21
+ d.current = M, W.current = X;
22
+ const k = f(0), w = f(null), G = f(!1), [ar, j] = x(!0), [A, O] = x(null), [U, fr] = x(!1), [B, V] = x([]), [S, T] = x(!1), [J, K] = x(""), Q = f([]);
23
+ Q.current = B, G.current = U;
24
+ const Y = b(() => {
25
+ var r;
26
+ (r = p.current) == null || r.prev();
27
+ }, []), Z = b(() => {
28
+ var r;
29
+ (r = p.current) == null || r.next();
30
+ }, []), g = f(null), z = f(0), D = f((r) => {
31
+ var i, s;
32
+ const e = g.current;
33
+ if (!e) return;
34
+ const t = e;
35
+ if (t.scrollTop + t.clientHeight >= t.scrollHeight - 200)
36
+ try {
37
+ const n = (i = p.current) == null ? void 0 : i.manager;
38
+ (s = n == null ? void 0 : n.check) == null || s.call(n, 500, 500);
39
+ } catch {
40
+ }
41
+ }), y = b(() => {
42
+ g.current && (g.current.removeEventListener("scroll", D.current), g.current = null), cancelAnimationFrame(z.current);
43
+ const r = () => {
44
+ var t;
45
+ const e = ((t = h.current) == null ? void 0 : t.querySelector(".epub-container")) ?? null;
46
+ if (!e) {
47
+ z.current = requestAnimationFrame(r);
48
+ return;
49
+ }
50
+ g.current = e, e.addEventListener("scroll", D.current, { passive: !0 });
51
+ };
52
+ z.current = requestAnimationFrame(r);
53
+ }, []), C = b(() => {
54
+ var e;
55
+ const r = !G.current;
56
+ fr(r), (e = W.current) == null || e.call(W, r), setTimeout(() => {
57
+ const t = h.current, o = p.current;
58
+ !t || !o || (o.resize(t.offsetWidth, t.offsetHeight), w.current && o.display(w.current), y());
59
+ }, 350);
60
+ }, [y]), rr = b(() => {
61
+ T((r) => !r);
62
+ }, []), lr = b((r) => {
63
+ var e;
64
+ K(r), (e = p.current) == null || e.display(r), T(!1);
65
+ }, []);
66
+ mr(sr, () => ({
67
+ prevPage: Y,
68
+ nextPage: Z,
69
+ toggleFullWidth: C,
70
+ toggleToc: rr
71
+ }), [Y, Z, C, rr]), q(() => {
72
+ const r = h.current;
73
+ if (!r || !u) return;
74
+ j(!0), O(null), V([]), T(!1), r.innerHTML = "", w.current = null, k.current = 0;
75
+ let e = !1;
76
+ const t = window.setTimeout(() => {
77
+ e || o();
78
+ }, 0), o = async () => {
79
+ var i;
80
+ try {
81
+ let s = u;
82
+ u.startsWith("blob:") && (s = await (await cr(u)).arrayBuffer());
83
+ const n = hr(s);
84
+ I.current = n;
85
+ const c = n.renderTo(r, {
86
+ manager: "continuous",
87
+ flow: "scrolled",
88
+ width: "100%",
89
+ height: "100%"
90
+ });
91
+ p.current = c, c.themes.register("default", {
92
+ body: {
93
+ background: "#ffffff !important",
94
+ color: "#1a1a1a !important",
95
+ "font-family": '"Noto Serif SC", "Source Han Serif SC", Georgia, "Times New Roman", serif !important',
96
+ "font-size": "16px !important",
97
+ "line-height": "2 !important",
98
+ padding: "40px 60px !important",
99
+ "max-width": "100% !important",
100
+ "box-sizing": "border-box !important",
101
+ "word-break": "break-word !important",
102
+ "overflow-wrap": "break-word !important"
103
+ },
104
+ p: { "text-indent": "2em !important", margin: "0.8em 0 !important" },
105
+ h1: { "text-align": "center !important", margin: "1.5em 0 1em !important" },
106
+ h2: { margin: "1.2em 0 0.8em !important" },
107
+ h3: { margin: "1em 0 0.6em !important" },
108
+ img: { "max-width": "100% !important", height: "auto !important" },
109
+ a: { color: "#2563eb !important", "text-decoration": "none !important" }
110
+ }), c.themes.select("default"), await n.ready, n.locations.generate(1024).then(() => {
111
+ var N, R, H;
112
+ if (e) return;
113
+ k.current = n.locations.length();
114
+ const m = (N = p.current) == null ? void 0 : N.currentLocation(), a = ((R = m == null ? void 0 : m.start) == null ? void 0 : R.location) ?? 0;
115
+ (H = d.current) == null || H.call(d, a + 1, k.current);
116
+ }).catch(() => {
117
+ });
118
+ const v = await n.loaded.navigation;
119
+ if (!e && Array.isArray(v == null ? void 0 : v.toc) && V(v.toc), await c.display(), e) return;
120
+ j(!1), (i = d.current) == null || i.call(d, 1, k.current || 1), c.on("relocated", (m) => {
121
+ var H, er, nr, or;
122
+ const a = m;
123
+ if ((H = a == null ? void 0 : a.start) != null && H.cfi && (w.current = a.start.cfi), (er = a == null ? void 0 : a.start) != null && er.href) {
124
+ const P = a.start.href, $ = [], ir = (pr) => {
125
+ for (const L of pr) {
126
+ const F = L.href.split("#")[0];
127
+ F && (P === F || P.endsWith("/" + F) || P.endsWith(F)) && $.push(L.href), L.subitems && ir(L.subitems);
128
+ }
129
+ };
130
+ ir(Q.current), $.length === 1 && K($[0]);
131
+ }
132
+ const N = (nr = a == null ? void 0 : a.start) == null ? void 0 : nr.location, R = k.current;
133
+ typeof N == "number" && R > 0 && ((or = d.current) == null || or.call(d, N + 1, R));
134
+ });
135
+ } catch (s) {
136
+ console.error("EPUB 加载错误:", s), e || (O(_("epub.load_failed")), j(!1));
137
+ }
138
+ };
139
+ return () => {
140
+ var i, s, n;
141
+ e = !0, window.clearTimeout(t);
142
+ try {
143
+ (s = (i = p.current) == null ? void 0 : i.destroy) == null || s.call(i);
144
+ } catch {
145
+ }
146
+ try {
147
+ (n = I.current) == null || n.destroy();
148
+ } catch {
149
+ }
150
+ p.current = null, I.current = null;
151
+ };
152
+ }, [u]), q(() => {
153
+ const r = h.current;
154
+ if (!r) return;
155
+ let e = !0, t = { width: 0, height: 0 }, o = null;
156
+ const i = () => {
157
+ const n = h.current, c = p.current;
158
+ if (!(!n || !c)) {
159
+ if (c.resize(n.offsetWidth, n.offsetHeight), w.current)
160
+ try {
161
+ c.display(w.current);
162
+ } catch {
163
+ }
164
+ y();
165
+ }
166
+ }, s = new ResizeObserver(() => {
167
+ const n = h.current;
168
+ if (!n) return;
169
+ if (e) {
170
+ e = !1, t = { width: n.offsetWidth, height: n.offsetHeight };
171
+ return;
172
+ }
173
+ const c = { width: n.offsetWidth, height: n.offsetHeight }, v = Math.abs(t.width - c.width), m = Math.abs(t.height - c.height);
174
+ v < 10 && m < 10 || (t = c, o !== null && clearTimeout(o), o = window.setTimeout(() => {
175
+ i();
176
+ }, 350));
177
+ });
178
+ return s.observe(r), () => {
179
+ s.disconnect(), o !== null && clearTimeout(o);
180
+ };
181
+ }, [y]), q(() => (y(), () => {
182
+ var r;
183
+ cancelAnimationFrame(z.current), (r = g.current) == null || r.removeEventListener("scroll", D.current);
184
+ }), [u, y]);
185
+ const ur = b((r) => r === J, [J]), tr = (r, e = 0) => /* @__PURE__ */ l("ul", { style: { marginLeft: e > 0 ? 16 : 0 }, children: r.map((t, o) => {
186
+ const i = ur(t.href);
187
+ return /* @__PURE__ */ E("li", { children: [
188
+ /* @__PURE__ */ l(
189
+ "button",
190
+ {
191
+ onClick: () => lr(t.href),
192
+ className: `rfp-w-full rfp-text-left rfp-py-2 rfp-px-3 rfp-text-sm rfp-rounded rfp-transition-all rfp-truncate ${i ? "rfp-text-fg-primary rfp-bg-surface-3 rfp-font-medium" : "rfp-text-fg-secondary hover:rfp-text-fg-primary hover:rfp-bg-surface-2"}`,
193
+ title: t.label,
194
+ children: t.label.trim()
195
+ }
196
+ ),
197
+ t.subitems && t.subitems.length > 0 && tr(t.subitems, e + 1)
198
+ ] }, `${t.href}-${o}`);
199
+ }) });
200
+ return /* @__PURE__ */ E("div", { className: "rfp-relative rfp-w-full rfp-h-full rfp-flex rfp-justify-center rfp-bg-surface-1 rfp-overflow-hidden", children: [
201
+ A && /* @__PURE__ */ l(yr, { message: A }),
202
+ ar && !A && /* @__PURE__ */ l("div", { className: "rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-z-10", children: /* @__PURE__ */ l("div", { className: "rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin" }) }),
203
+ B.length > 0 && /* @__PURE__ */ E(
204
+ "div",
205
+ {
206
+ className: "rfp-absolute rfp-inset-0 rfp-z-20 rfp-flex rfp-transition-opacity rfp-duration-300",
207
+ style: {
208
+ opacity: S ? 1 : 0,
209
+ pointerEvents: S ? "auto" : "none"
210
+ },
211
+ children: [
212
+ /* @__PURE__ */ E(
213
+ "div",
214
+ {
215
+ className: "rfp-w-72 rfp-max-w-[80%] rfp-h-full rfp-bg-surface-overlay rfp-backdrop-blur-xl rfp-border-r rfp-border-line-weak rfp-flex rfp-flex-col rfp-shadow-2xl rfp-transition-transform rfp-duration-300",
216
+ style: { transform: S ? "translateX(0)" : "translateX(-100%)" },
217
+ children: [
218
+ /* @__PURE__ */ E("div", { className: "rfp-flex rfp-items-center rfp-justify-between rfp-px-4 rfp-py-3 rfp-border-b rfp-border-line-weak rfp-flex-shrink-0", children: [
219
+ /* @__PURE__ */ l("span", { className: "rfp-text-fg-primary rfp-font-medium rfp-text-sm", children: _("toolbar.toc") }),
220
+ /* @__PURE__ */ l(
221
+ "button",
222
+ {
223
+ onClick: () => T(!1),
224
+ className: "rfp-text-fg-tertiary hover:rfp-text-fg-primary rfp-transition-colors",
225
+ children: /* @__PURE__ */ l(br, { className: "rfp-w-4 rfp-h-4" })
226
+ }
227
+ )
228
+ ] }),
229
+ /* @__PURE__ */ l("div", { className: "rfp-flex-1 rfp-overflow-y-auto rfp-py-4 rfp-px-1", children: tr(B) })
230
+ ]
231
+ }
232
+ ),
233
+ /* @__PURE__ */ l(
234
+ "div",
235
+ {
236
+ className: "rfp-flex-1 rfp-transition-opacity rfp-duration-300",
237
+ style: { background: S ? "rgba(0,0,0,0.3)" : "transparent" },
238
+ onClick: () => T(!1)
239
+ }
240
+ )
241
+ ]
242
+ }
243
+ ),
244
+ !A && /* @__PURE__ */ l(
245
+ "div",
246
+ {
247
+ ref: h,
248
+ className: "rfp-h-full rfp-bg-surface-toolbar rfp-shadow-lg",
249
+ style: {
250
+ width: U ? "100%" : `${vr}px`,
251
+ maxWidth: "100%",
252
+ transition: "width 0.3s ease",
253
+ overflow: "hidden"
254
+ }
255
+ }
256
+ )
257
+ ] });
258
+ }
259
+ );
260
+ export {
261
+ Er as EpubRenderer
262
+ };
263
+ //# sourceMappingURL=index-Cbz5Z6ZK.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-Cbz5Z6ZK.mjs","sources":["../../src/renderers/Epub/index.tsx"],"sourcesContent":["import { useEffect, useRef, useState, useCallback, useImperativeHandle, forwardRef } from 'react';\nimport ePub from '@likecoin/epub-ts';\nimport { X } from 'lucide-react';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\n// 全局注入 epubjs 容器样式(只注入一次)\nif (typeof document !== 'undefined' && !document.getElementById('rfp-epub-styles')) {\n const styleEl = document.createElement('style');\n styleEl.id = 'rfp-epub-styles';\n styleEl.textContent = `\n .epub-container { overflow-y: auto !important; scrollbar-width: thin; }\n .epub-container::-webkit-scrollbar { width: 8px; }\n .epub-container::-webkit-scrollbar-track { background: transparent; }\n .epub-container::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 4px; }\n .epub-container::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.3); }\n .epub-view > iframe { background: white; }\n `;\n document.head.appendChild(styleEl);\n}\n\nexport interface TocItem {\n label: string;\n href: string;\n subitems?: TocItem[];\n}\n\nexport interface EpubRendererHandle {\n prevPage: () => void;\n nextPage: () => void;\n toggleFullWidth: () => void;\n toggleToc: () => void;\n}\n\ninterface EpubRendererProps {\n url: string;\n onChapterChange?: (current: number, total: number) => void;\n onFullWidthChange?: (isFullWidth: boolean) => void;\n}\n\ninterface RenditionLike {\n display: (target?: string) => Promise<unknown>;\n next: () => Promise<unknown>;\n prev: () => Promise<unknown>;\n on: (event: string, cb: (...args: unknown[]) => void) => void;\n resize: (width: number, height: number) => void;\n currentLocation: () => unknown;\n destroy?: () => void;\n themes: {\n register: (name: string, styles: Record<string, unknown>) => void;\n select: (name: string) => void;\n };\n}\n\ninterface BookLike {\n ready: Promise<unknown>;\n loaded: { navigation: Promise<unknown> };\n locations: {\n generate: (chars: number) => Promise<string[]>;\n length: () => number;\n locationFromCfi: (cfi: string) => number;\n };\n renderTo: (el: HTMLElement, opts: Record<string, unknown>) => RenditionLike;\n destroy: () => void;\n}\n\nconst A4_WIDTH = 794;\n\nexport const EpubRenderer = forwardRef<EpubRendererHandle, EpubRendererProps>(\n ({ url, onChapterChange, onFullWidthChange }, ref) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const viewerRef = useRef<HTMLDivElement>(null);\n const bookRef = useRef<BookLike | null>(null);\n const renditionRef = useRef<RenditionLike | null>(null);\n const onChapterChangeRef = useRef(onChapterChange);\n const onFullWidthChangeRef = useRef(onFullWidthChange);\n onChapterChangeRef.current = onChapterChange;\n onFullWidthChangeRef.current = onFullWidthChange;\n\n const totalLocationsRef = useRef(0);\n const lastCfiRef = useRef<string | null>(null);\n const isFullWidthRef = useRef(false);\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [isFullWidth, setIsFullWidth] = useState(false);\n const [toc, setToc] = useState<TocItem[]>([]);\n const [showToc, setShowToc] = useState(false);\n const [activeTocHref, setActiveTocHref] = useState<string>('');\n const tocRef = useRef<TocItem[]>([]);\n tocRef.current = toc;\n\n isFullWidthRef.current = isFullWidth;\n\n const handlePrev = useCallback(() => {\n renditionRef.current?.prev();\n }, []);\n\n const handleNext = useCallback(() => {\n renditionRef.current?.next();\n }, []);\n\n // 滚动监听:接近底部时强制触发 check 加载后续 section\n const scrollContainerRef = useRef<Element | null>(null);\n const scrollRafRef = useRef(0);\n\n const onScrollRef = useRef((_e?: Event) => {\n const container = scrollContainerRef.current;\n if (!container) return;\n const el = container as HTMLElement;\n const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 200;\n if (nearBottom) {\n try {\n const mgr = (renditionRef.current as unknown as { manager?: { check?: (t?: number, e?: number) => Promise<unknown> } })?.manager;\n mgr?.check?.(500, 500);\n } catch { /* ignore */ }\n }\n });\n\n const reattachScrollListener = useCallback(() => {\n // 清理旧监听\n if (scrollContainerRef.current) {\n scrollContainerRef.current.removeEventListener('scroll', onScrollRef.current);\n scrollContainerRef.current = null;\n }\n cancelAnimationFrame(scrollRafRef.current);\n\n const tryAttach = () => {\n const container = viewerRef.current?.querySelector('.epub-container') ?? null;\n if (!container) {\n scrollRafRef.current = requestAnimationFrame(tryAttach);\n return;\n }\n scrollContainerRef.current = container;\n container.addEventListener('scroll', onScrollRef.current, { passive: true });\n };\n scrollRafRef.current = requestAnimationFrame(tryAttach);\n }, []);\n\n const toggleFullWidth = useCallback(() => {\n const newVal = !isFullWidthRef.current;\n setIsFullWidth(newVal);\n onFullWidthChangeRef.current?.(newVal);\n // 等 CSS transition 完成后再 resize 并恢复位置\n setTimeout(() => {\n const viewer = viewerRef.current;\n const rendition = renditionRef.current;\n if (!viewer || !rendition) return;\n rendition.resize(viewer.offsetWidth, viewer.offsetHeight);\n // 重排后恢复阅读位置\n if (lastCfiRef.current) {\n rendition.display(lastCfiRef.current);\n }\n // resize/display 可能重建 .epub-container,需要重新绑定滚动监听\n reattachScrollListener();\n }, 350);\n }, [reattachScrollListener]);\n\n const toggleToc = useCallback(() => {\n setShowToc(prev => !prev);\n }, []);\n\n const handleTocClick = useCallback((href: string) => {\n setActiveTocHref(href);\n renditionRef.current?.display(href);\n setShowToc(false);\n }, []);\n\n useImperativeHandle(ref, () => ({\n prevPage: handlePrev,\n nextPage: handleNext,\n toggleFullWidth,\n toggleToc,\n }), [handlePrev, handleNext, toggleFullWidth, toggleToc]);\n\n useEffect(() => {\n const viewer = viewerRef.current;\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!viewer || !url) return;\n\n setLoading(true);\n setError(null);\n setToc([]);\n setShowToc(false);\n viewer.innerHTML = '';\n lastCfiRef.current = null;\n totalLocationsRef.current = 0;\n\n let cancelled = false;\n // StrictMode 下 effect 会立即 mount→unmount→mount\n // 用 microtask 延迟初始化,让第一次的 cleanup 先执行,避免 epubjs 内部状态被污染\n const loadTimer = window.setTimeout(() => {\n if (cancelled) return;\n load();\n }, 0);\n\n const load = async () => {\n try {\n let bookInput: string | ArrayBuffer = url;\n if (url.startsWith('blob:')) {\n const resp = await fetcher(url);\n bookInput = await resp.arrayBuffer();\n }\n\n const book = ePub(bookInput) as unknown as BookLike;\n bookRef.current = book;\n\n const rendition = book.renderTo(viewer, {\n manager: 'continuous',\n flow: 'scrolled',\n width: '100%',\n height: '100%',\n });\n renditionRef.current = rendition;\n\n rendition.themes.register('default', {\n body: {\n background: '#ffffff !important',\n color: '#1a1a1a !important',\n 'font-family': '\"Noto Serif SC\", \"Source Han Serif SC\", Georgia, \"Times New Roman\", serif !important',\n 'font-size': '16px !important',\n 'line-height': '2 !important',\n padding: '40px 60px !important',\n 'max-width': '100% !important',\n 'box-sizing': 'border-box !important',\n 'word-break': 'break-word !important',\n 'overflow-wrap': 'break-word !important',\n },\n p: { 'text-indent': '2em !important', margin: '0.8em 0 !important' },\n h1: { 'text-align': 'center !important', margin: '1.5em 0 1em !important' },\n h2: { margin: '1.2em 0 0.8em !important' },\n h3: { margin: '1em 0 0.6em !important' },\n img: { 'max-width': '100% !important', height: 'auto !important' },\n a: { color: '#2563eb !important', 'text-decoration': 'none !important' },\n });\n rendition.themes.select('default');\n\n await book.ready;\n\n // 异步生成 locations 索引(用于实时页数)\n book.locations.generate(1024).then(() => {\n if (cancelled) return;\n totalLocationsRef.current = book.locations.length();\n // 触发一次更新让父组件拿到 total\n const loc = renditionRef.current?.currentLocation() as { start?: { location?: number; cfi?: string } } | undefined;\n const cur = loc?.start?.location ?? 0;\n onChapterChangeRef.current?.(cur + 1, totalLocationsRef.current);\n }).catch(() => { /* ignore */ });\n\n // 获取目录\n const nav = await book.loaded.navigation as { toc?: TocItem[] };\n if (!cancelled && Array.isArray(nav?.toc)) {\n setToc(nav.toc);\n }\n\n await rendition.display();\n\n if (cancelled) return;\n\n setLoading(false);\n onChapterChangeRef.current?.(1, totalLocationsRef.current || 1);\n\n rendition.on('relocated', (location: unknown) => {\n const loc = location as { start?: { cfi?: string; location?: number; href?: string } };\n if (loc?.start?.cfi) {\n lastCfiRef.current = loc.start.cfi;\n }\n if (loc?.start?.href) {\n // 根据 spine href 查找匹配的 TOC 项\n const spineHref = loc.start.href;\n const matches: string[] = [];\n const collect = (items: TocItem[]) => {\n for (const item of items) {\n const base = item.href.split('#')[0];\n if (base && (spineHref === base || spineHref.endsWith('/' + base) || spineHref.endsWith(base))) {\n matches.push(item.href);\n }\n if (item.subitems) collect(item.subitems);\n }\n };\n collect(tocRef.current);\n if (matches.length === 1) {\n // 唯一匹配,直接设置\n setActiveTocHref(matches[0]);\n }\n // 多个匹配(同一文件不同 anchor)时保持当前选中(由点击设置)\n }\n const cur = loc?.start?.location;\n const total = totalLocationsRef.current;\n if (typeof cur === 'number' && total > 0) {\n onChapterChangeRef.current?.(cur + 1, total);\n }\n });\n\n } catch (err) {\n console.error('EPUB 加载错误:', err);\n if (!cancelled) {\n setError(t('epub.load_failed'));\n setLoading(false);\n }\n }\n };\n\n return () => {\n cancelled = true;\n window.clearTimeout(loadTimer);\n try { renditionRef.current?.destroy?.(); } catch { /* ignore */ }\n try { bookRef.current?.destroy(); } catch { /* ignore */ }\n renditionRef.current = null;\n bookRef.current = null;\n };\n }, [url]);\n\n // 监听容器尺寸变化:等待 transition 完成后才 resize,避免 transition 期间频繁触发导致频闪\n useEffect(() => {\n const viewer = viewerRef.current;\n if (!viewer) return;\n\n let isInitialRender = true;\n let lastDimensions = { width: 0, height: 0 };\n let resizeTimeout: number | null = null;\n\n const doResize = () => {\n const v = viewerRef.current;\n const rendition = renditionRef.current;\n if (!v || !rendition) return;\n rendition.resize(v.offsetWidth, v.offsetHeight);\n // resize 后恢复阅读位置\n if (lastCfiRef.current) {\n try { rendition.display(lastCfiRef.current); } catch { /* ignore */ }\n }\n // resize/display 可能重建 .epub-container,需要重新绑定滚动监听\n reattachScrollListener();\n };\n\n const observer = new ResizeObserver(() => {\n const v = viewerRef.current;\n if (!v) return;\n\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensions = { width: v.offsetWidth, height: v.offsetHeight };\n return;\n }\n\n const newDimensions = { width: v.offsetWidth, height: v.offsetHeight };\n const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n // 微小变化不触发,避免抖动\n if (widthDiff < 10 && heightDiff < 10) return;\n\n lastDimensions = newDimensions;\n\n // 防抖:等待 transition 完成(350ms)后才重新渲染\n if (resizeTimeout !== null) clearTimeout(resizeTimeout);\n resizeTimeout = window.setTimeout(() => {\n doResize();\n }, 350);\n });\n\n observer.observe(viewer);\n\n return () => {\n observer.disconnect();\n if (resizeTimeout !== null) clearTimeout(resizeTimeout);\n };\n }, [reattachScrollListener]);\n\n useEffect(() => {\n reattachScrollListener();\n return () => {\n cancelAnimationFrame(scrollRafRef.current);\n scrollContainerRef.current?.removeEventListener('scroll', onScrollRef.current);\n };\n }, [url, reattachScrollListener]);\n\n const isActive = useCallback((href: string) => {\n return href === activeTocHref;\n }, [activeTocHref]);\n\n const renderTocItems = (items: TocItem[], depth = 0) => (\n <ul style={{ marginLeft: depth > 0 ? 16 : 0 }}>\n {items.map((item, i) => {\n const active = isActive(item.href);\n return (\n <li key={`${item.href}-${i}`}>\n <button\n onClick={() => handleTocClick(item.href)}\n className={`rfp-w-full rfp-text-left rfp-py-2 rfp-px-3 rfp-text-sm rfp-rounded rfp-transition-all rfp-truncate ${active\n ? 'rfp-text-fg-primary rfp-bg-surface-3 rfp-font-medium'\n : 'rfp-text-fg-secondary hover:rfp-text-fg-primary hover:rfp-bg-surface-2'\n }`}\n title={item.label}\n >\n {item.label.trim()}\n </button>\n {item.subitems && item.subitems.length > 0 && renderTocItems(item.subitems, depth + 1)}\n </li>\n );\n })}\n </ul>\n );\n\n return (\n <div className=\"rfp-relative rfp-w-full rfp-h-full rfp-flex rfp-justify-center rfp-bg-surface-1 rfp-overflow-hidden\">\n {error && <RendererError message={error} />}\n\n {loading && !error && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-z-10\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n\n {/* 目录侧栏 - 滑入动画 */}\n {toc.length > 0 && (\n <div\n className=\"rfp-absolute rfp-inset-0 rfp-z-20 rfp-flex rfp-transition-opacity rfp-duration-300\"\n style={{\n opacity: showToc ? 1 : 0,\n pointerEvents: showToc ? 'auto' : 'none',\n }}\n >\n <div\n className=\"rfp-w-72 rfp-max-w-[80%] rfp-h-full rfp-bg-surface-overlay rfp-backdrop-blur-xl rfp-border-r rfp-border-line-weak rfp-flex rfp-flex-col rfp-shadow-2xl rfp-transition-transform rfp-duration-300\"\n style={{ transform: showToc ? 'translateX(0)' : 'translateX(-100%)' }}\n >\n <div className=\"rfp-flex rfp-items-center rfp-justify-between rfp-px-4 rfp-py-3 rfp-border-b rfp-border-line-weak rfp-flex-shrink-0\">\n <span className=\"rfp-text-fg-primary rfp-font-medium rfp-text-sm\">{t('toolbar.toc')}</span>\n <button\n onClick={() => setShowToc(false)}\n className=\"rfp-text-fg-tertiary hover:rfp-text-fg-primary rfp-transition-colors\"\n >\n <X className=\"rfp-w-4 rfp-h-4\" />\n </button>\n </div>\n <div className=\"rfp-flex-1 rfp-overflow-y-auto rfp-py-4 rfp-px-1\">\n {renderTocItems(toc)}\n </div>\n </div>\n <div\n className=\"rfp-flex-1 rfp-transition-opacity rfp-duration-300\"\n style={{ background: showToc ? 'rgba(0,0,0,0.3)' : 'transparent' }}\n onClick={() => setShowToc(false)}\n />\n </div>\n )}\n\n {!error && (\n <div\n ref={viewerRef}\n className=\"rfp-h-full rfp-bg-surface-toolbar rfp-shadow-lg\"\n style={{\n width: isFullWidth ? '100%' : `${A4_WIDTH}px`,\n maxWidth: '100%',\n transition: 'width 0.3s ease',\n overflow: 'hidden',\n }}\n />\n )}\n </div>\n );\n }\n);\n"],"names":["styleEl","A4_WIDTH","EpubRenderer","forwardRef","url","onChapterChange","onFullWidthChange","ref","t","useTranslator","fetcher","useFetcher","viewerRef","useRef","bookRef","renditionRef","onChapterChangeRef","onFullWidthChangeRef","totalLocationsRef","lastCfiRef","isFullWidthRef","loading","setLoading","useState","error","setError","isFullWidth","setIsFullWidth","toc","setToc","showToc","setShowToc","activeTocHref","setActiveTocHref","tocRef","handlePrev","useCallback","_a","handleNext","scrollContainerRef","scrollRafRef","onScrollRef","_e","container","el","mgr","_b","reattachScrollListener","tryAttach","toggleFullWidth","newVal","viewer","rendition","toggleToc","prev","handleTocClick","href","useImperativeHandle","useEffect","cancelled","loadTimer","load","bookInput","book","ePub","loc","cur","_c","nav","location","spineHref","matches","collect","items","item","base","total","_d","err","isInitialRender","lastDimensions","resizeTimeout","doResize","v","observer","newDimensions","widthDiff","heightDiff","isActive","renderTocItems","depth","jsx","i","active","jsxs","RendererError","X"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as z, jsxs as $ } from "react/jsx-runtime";
2
2
  import { useState as Pe, useRef as Jt, useEffect as xr, useCallback as Rs } from "react";
3
- import { u as Is, a as Ls, b as As } from "./index-CWCNvV2X.mjs";
4
- import { R as Us } from "./RendererError-BH6fzLrN.mjs";
3
+ import { u as Is, a as Ls, b as As } from "./index-CEeKt7L3.mjs";
4
+ import { R as Us } from "./RendererError-D5i8eSpN.mjs";
5
5
  var fn = 0, Sr = -3;
6
6
  function nt() {
7
7
  this.table = new Uint16Array(16), this.trans = new Uint16Array(288);
@@ -12332,4 +12332,4 @@ abcdefghijklmnopqrstuvwxyz
12332
12332
  export {
12333
12333
  Hc as FontRenderer
12334
12334
  };
12335
- //# sourceMappingURL=index-DYNPnFww.mjs.map
12335
+ //# sourceMappingURL=index-Cp68OevR.mjs.map