@eternalheart/react-file-preview 1.5.1 → 1.5.3
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.
- package/lib/chunks/{index-Cn4ZyhGM.mjs → index--xe_vUUO.mjs} +2 -2
- package/lib/chunks/{index-Cn4ZyhGM.mjs.map → index--xe_vUUO.mjs.map} +1 -1
- package/lib/chunks/{index-DreA69iU.mjs → index-B3jtj_7-.mjs} +20 -20
- package/lib/chunks/{index-DreA69iU.mjs.map → index-B3jtj_7-.mjs.map} +1 -1
- package/lib/chunks/{index-CU1Lc3lV.mjs → index-BJZ6GlPC.mjs} +3 -3
- package/lib/chunks/{index-CU1Lc3lV.mjs.map → index-BJZ6GlPC.mjs.map} +1 -1
- package/lib/chunks/{index-DVtPyN-s.mjs → index-B__mDGsc.mjs} +3 -3
- package/lib/chunks/{index-DVtPyN-s.mjs.map → index-B__mDGsc.mjs.map} +1 -1
- package/lib/chunks/{index-Dta7iGov.mjs → index-BczRYnSp.mjs} +2 -2
- package/lib/chunks/{index-Dta7iGov.mjs.map → index-BczRYnSp.mjs.map} +1 -1
- package/lib/chunks/{index-Bw3Fh4b5.mjs → index-Bj6cvzuN.mjs} +2 -2
- package/lib/chunks/{index-Bw3Fh4b5.mjs.map → index-Bj6cvzuN.mjs.map} +1 -1
- package/lib/chunks/{index-BBYKNNLb.mjs → index-BjL2sXlA.mjs} +2 -2
- package/lib/chunks/{index-BBYKNNLb.mjs.map → index-BjL2sXlA.mjs.map} +1 -1
- package/lib/chunks/{index-CEC_DHgr.mjs → index-BrTFkhJZ.mjs} +8 -9
- package/lib/chunks/index-BrTFkhJZ.mjs.map +1 -0
- package/lib/chunks/{index-CgFv7B_G.mjs → index-BvBldLaU.mjs} +3 -3
- package/lib/chunks/{index-CgFv7B_G.mjs.map → index-BvBldLaU.mjs.map} +1 -1
- package/lib/chunks/{index-DGNNEnWE.mjs → index-C6eeg9nN.mjs} +2 -2
- package/lib/chunks/{index-DGNNEnWE.mjs.map → index-C6eeg9nN.mjs.map} +1 -1
- package/lib/chunks/{index-oVJyD-FV.mjs → index-CZPztWhN.mjs} +2 -2
- package/lib/chunks/{index-oVJyD-FV.mjs.map → index-CZPztWhN.mjs.map} +1 -1
- package/lib/chunks/{index-DLk08ylq.mjs → index-Chj36kjS.mjs} +2 -2
- package/lib/chunks/{index-DLk08ylq.mjs.map → index-Chj36kjS.mjs.map} +1 -1
- package/lib/chunks/{index-fSw6Hl5e.mjs → index-CwCdx6Io.mjs} +2 -2
- package/lib/chunks/{index-fSw6Hl5e.mjs.map → index-CwCdx6Io.mjs.map} +1 -1
- package/lib/chunks/index-CyBXARuf.mjs +5215 -0
- package/lib/chunks/index-CyBXARuf.mjs.map +1 -0
- package/lib/chunks/{index-BFh22D_W.mjs → index-D3Wso9Iz.mjs} +3 -3
- package/lib/chunks/{index-BFh22D_W.mjs.map → index-D3Wso9Iz.mjs.map} +1 -1
- package/lib/chunks/{index-BKXvtJh5.mjs → index-DXZFI5Vp.mjs} +2 -2
- package/lib/chunks/{index-BKXvtJh5.mjs.map → index-DXZFI5Vp.mjs.map} +1 -1
- package/lib/chunks/{index-COOUxB5e.mjs → index-DZC5bucC.mjs} +2 -2
- package/lib/chunks/{index-COOUxB5e.mjs.map → index-DZC5bucC.mjs.map} +1 -1
- package/lib/chunks/{index-fQGAUFAX.mjs → index-VkrsxHnU.mjs} +2 -2
- package/lib/chunks/{index-fQGAUFAX.mjs.map → index-VkrsxHnU.mjs.map} +1 -1
- package/lib/chunks/{index-jvNrkVkp.mjs → index-XdWDqRwq.mjs} +2 -2
- package/lib/chunks/{index-jvNrkVkp.mjs.map → index-XdWDqRwq.mjs.map} +1 -1
- package/lib/chunks/{index-vRLKumL8.mjs → index-wlMDzgVW.mjs} +18 -19
- package/lib/chunks/index-wlMDzgVW.mjs.map +1 -0
- package/lib/chunks/{useShikiHighlight-C6nJcETW.mjs → useShikiHighlight-DHhZ6Oy9.mjs} +2 -2
- package/lib/chunks/{useShikiHighlight-C6nJcETW.mjs.map → useShikiHighlight-DHhZ6Oy9.mjs.map} +1 -1
- package/lib/index.cjs +45 -32
- package/lib/index.cjs.map +1 -1
- package/lib/index.css +1 -1
- package/lib/index.mjs +1 -1
- package/lib/renderers/Csv/index.d.ts +0 -1
- package/lib/renderers/Csv/index.d.ts.map +1 -1
- package/lib/renderers/Xlsx/index.d.ts +0 -1
- package/lib/renderers/Xlsx/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/lib/chunks/index-CEC_DHgr.mjs.map +0 -1
- package/lib/chunks/index-vRLKumL8.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as s, jsxs as y } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef as T, useState as x, useRef as b, useEffect as h, useImperativeHandle as k } from "react";
|
|
3
3
|
import E from "video.js";
|
|
4
|
-
import { u as R } from "./index-
|
|
4
|
+
import { u as R } from "./index-B3jtj_7-.mjs";
|
|
5
5
|
import { R as j } from "./RendererError-D5i8eSpN.mjs";
|
|
6
6
|
const _ = /* @__PURE__ */ new Set(["avi", "wmv", "flv"]), N = (r) => {
|
|
7
7
|
var e;
|
|
@@ -113,4 +113,4 @@ const _ = /* @__PURE__ */ new Set(["avi", "wmv", "flv"]), N = (r) => {
|
|
|
113
113
|
export {
|
|
114
114
|
q as VideoRenderer
|
|
115
115
|
};
|
|
116
|
-
//# sourceMappingURL=index-
|
|
116
|
+
//# sourceMappingURL=index-Bj6cvzuN.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-Bj6cvzuN.mjs","sources":["../../src/renderers/Video/index.tsx"],"sourcesContent":["import { useRef, useEffect, useState, forwardRef, useImperativeHandle } from 'react';\nimport videojs from 'video.js';\nimport 'video.js/dist/video-js.css';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { RendererError } from '../RendererError';\nimport type { RendererHandle } from '../base.types';\n\ntype VideoJsPlayer = ReturnType<typeof videojs>;\n\ninterface VideoRendererProps {\n url: string;\n fileName?: string;\n}\n\n// 浏览器原生不支持的视频容器(无论编码,<video> 都无法播放)\n// — 这些扩展名直接短路 videojs 初始化,给出明确提示\nconst BROWSER_UNSUPPORTED_EXTS = new Set(['avi', 'wmv', 'flv']);\n\n// 根据 URL 获取视频 MIME 类型\nconst getVideoType = (url: string): string => {\n const ext = url.split('.').pop()?.toLowerCase().split('?')[0] || '';\n const typeMap: Record<string, string> = {\n mp4: 'video/mp4',\n webm: 'video/webm',\n ogg: 'video/ogg',\n ogv: 'video/ogg',\n mov: 'video/quicktime', // MOV 使用 QuickTime MIME 类型\n avi: 'video/x-msvideo',\n mkv: 'video/x-matroska',\n m4v: 'video/mp4',\n '3gp': 'video/3gpp',\n flv: 'video/x-flv',\n };\n return typeMap[ext] || 'video/mp4';\n};\n\n// 获取视频文件扩展名(优先用文件名,blob/HTTP URL 都拿不到真扩展名)\nconst getVideoExt = (url: string, fileName?: string): string => {\n const source = fileName || url;\n return source.split('.').pop()?.toLowerCase().split('?')[0] || '';\n};\n\ninterface ErrorState {\n title: string;\n detail: string;\n}\n\nexport const VideoRenderer = forwardRef<RendererHandle, VideoRendererProps>(({ url, fileName }, ref) => {\n const t = useTranslator();\n const [error, setError] = useState<ErrorState | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const videoRef = useRef<HTMLDivElement>(null);\n const playerRef = useRef<VideoJsPlayer | null>(null);\n\n useEffect(() => {\n const videoExt = getVideoExt(url, fileName);\n\n // 已知浏览器不支持的容器:跳过 videojs 初始化,直接展示友好提示\n if (BROWSER_UNSUPPORTED_EXTS.has(videoExt)) {\n setError({\n title: t('video.unsupported_title'),\n detail: t('video.unsupported_detail', { format: videoExt.toUpperCase() }),\n });\n setIsLoading(false);\n return;\n }\n\n // 确保 Video.js 播放器只初始化一次\n if (!playerRef.current && videoRef.current) {\n const videoElement = document.createElement('video-js');\n videoElement.classList.add('vjs-big-play-centered', 'vjs-theme-apple');\n videoRef.current.appendChild(videoElement);\n\n const videoType = getVideoType(url);\n\n // 为特定格式提供多个 MIME 类型作为备用\n let sources: Array<{ src: string; type: string }>;\n\n if (videoType === 'video/quicktime') {\n // MOV 格式 fallback\n sources = [\n { src: url, type: 'video/quicktime' },\n { src: url, type: 'video/mp4' }\n ];\n } else {\n sources = [{ src: url, type: videoType }];\n }\n\n const player = videojs(videoElement, {\n controls: true,\n fill: true,\n preload: 'auto',\n controlBar: {\n children: [\n 'playToggle',\n 'volumePanel',\n 'currentTimeDisplay',\n 'timeDivider',\n 'durationDisplay',\n 'progressControl',\n 'remainingTimeDisplay',\n 'fullscreenToggle'\n ],\n volumePanel: {\n inline: false\n }\n },\n html5: {\n vhs: {\n overrideNative: true\n },\n nativeVideoTracks: false,\n nativeAudioTracks: false,\n nativeTextTracks: false\n },\n sources\n });\n\n // 确保视频保持比例\n const videoEl = player.el().querySelector('video');\n if (videoEl) {\n (videoEl as HTMLVideoElement).style.objectFit = 'contain';\n }\n\n // 监听加载完成\n player.on('loadeddata', () => {\n setIsLoading(false);\n });\n\n player.on('error', () => {\n const err = player.error();\n // 视频加载错误是正常情况(格式不支持、编解码器缺失等),用 warn 级别记录\n console.warn('[VideoRenderer] Video playback error:', err?.message || 'Unknown error');\n\n // MEDIA_ERR_SRC_NOT_SUPPORTED(code=4):编码或容器层面浏览器解不了\n if (err?.code === 4) {\n setError({\n title: t('video.unsupported_title'),\n detail: t('video.unsupported_detail', {\n format: videoExt ? videoExt.toUpperCase() : t('common.unknown_error'),\n }),\n });\n } else {\n setError({\n title: t('video.load_failed'),\n detail: err?.message || t('common.unknown_error'),\n });\n }\n setIsLoading(false);\n });\n\n playerRef.current = player;\n }\n }, [url, fileName, t]);\n\n // 清理函数\n useEffect(() => {\n const player = playerRef.current;\n\n return () => {\n if (player && !player.isDisposed()) {\n player.dispose();\n playerRef.current = null;\n }\n };\n }, []);\n\n // 暴露接口给父组件(必须在所有提前 return 之前)\n useImperativeHandle(ref, () => ({\n getToolbarGroups: () => [],\n }), []);\n\n if (error) {\n return <RendererError message={error.title} detail={error.detail} />;\n }\n\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-full rfp-h-full rfp-relative\">\n {/* 加载状态 */}\n {isLoading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-3 rfp-backdrop-blur-sm rfp-z-10\">\n <div className=\"rfp-text-center\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-3 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n <p className=\"rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('video.loading')}</p>\n </div>\n </div>\n )}\n\n {/* 视频播放器容器 */}\n <div\n ref={videoRef}\n className=\"rfp-overflow-hidden rfp-w-full rfp-h-full [&_video]:rfp-object-contain\"\n style={{\n boxShadow: '0 20px 60px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05)'\n }}\n />\n </div>\n </div>\n );\n});\n"],"names":["BROWSER_UNSUPPORTED_EXTS","getVideoType","url","ext","_a","getVideoExt","fileName","VideoRenderer","forwardRef","ref","t","useTranslator","error","setError","useState","isLoading","setIsLoading","videoRef","useRef","playerRef","useEffect","videoExt","videoElement","videoType","sources","player","videojs","videoEl","err","useImperativeHandle","RendererError","jsxs","jsx"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { jsx as a, jsxs as X } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef as kt, useState as f, useRef as h, useCallback as i, useEffect as T, useImperativeHandle as Dt } from "react";
|
|
3
3
|
import { motion as Yt } from "framer-motion";
|
|
4
4
|
import { ZoomOut as Xt, ZoomIn as Tt, Scan as jt, RotateCcw as Wt, RotateCw as Pt, RefreshCw as St, Loader2 as Ot } from "lucide-react";
|
|
5
|
-
import { u as Bt, G as $t, q as zt } from "./index-
|
|
5
|
+
import { u as Bt, G as $t, q as zt } from "./index-B3jtj_7-.mjs";
|
|
6
6
|
import { R as wt } from "./RendererError-D5i8eSpN.mjs";
|
|
7
7
|
const Ht = ({ className: L }) => /* @__PURE__ */ a(
|
|
8
8
|
"svg",
|
|
@@ -326,4 +326,4 @@ const Ht = ({ className: L }) => /* @__PURE__ */ a(
|
|
|
326
326
|
export {
|
|
327
327
|
At as ImageRenderer
|
|
328
328
|
};
|
|
329
|
-
//# sourceMappingURL=index-
|
|
329
|
+
//# sourceMappingURL=index-BjL2sXlA.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-BBYKNNLb.mjs","sources":["../../src/renderers/Image/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback, useImperativeHandle, forwardRef } from 'react';\nimport { motion } from 'framer-motion';\nimport { Loader2, ZoomIn, ZoomOut, RotateCw, RotateCcw, Scan, RefreshCw } 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';\nimport type { RendererHandle } from '../base.types';\nimport type { ToolbarGroup } from '../toolbar.types';\n\nconst OriginalSizeIcon: React.FC<{ className?: string }> = ({ className }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <text x=\"12\" y=\"17.5\" textAnchor=\"middle\" fontSize=\"20\" fontWeight=\"bold\" fill=\"currentColor\" stroke=\"none\">\n 1:1\n </text>\n </svg>\n);\n\nexport interface ImageRendererHandle extends RendererHandle {\n fitToWidth: () => void;\n resetView: () => void;\n}\n\ninterface ImageRendererProps {\n url: string;\n fileSize?: number;\n file?: PreviewFile | File;\n}\n\nexport const ImageRenderer = forwardRef<ImageRendererHandle, ImageRendererProps>(({\n url,\n fileSize,\n file,\n}, ref) => {\n const t = useTranslator();\n\n // 内部状态管理\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 [zoom, setZoom] = useState(1);\n const [rotation, setRotation] = useState(0);\n const [naturalSize, setNaturalSize] = useState({ width: 0, height: 0 });\n\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 const listenersRef = useRef<Set<() => void>>(new Set());\n const notifyToolbarChange = useCallback(() => {\n listenersRef.current.forEach(listener => listener());\n }, []);\n\n // 监听影响工具栏的状态变化\n useEffect(() => {\n notifyToolbarChange();\n }, [zoom, notifyToolbarChange]);\n\n useEffect(() => {\n notifyToolbarChange();\n }, [rotation, notifyToolbarChange]);\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 setZoom(1);\n setRotation(0);\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 const handleLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n setLoaded(true);\n const img = e.currentTarget;\n const newNaturalSize = { width: img.naturalWidth, height: img.naturalHeight };\n setNaturalSize(newNaturalSize);\n\n // 图片加载完成后,自动适应窗口大小\n if (containerRef.current && newNaturalSize.width > 0 && newNaturalSize.height > 0) {\n const containerWidth = containerRef.current.clientWidth;\n const containerHeight = containerRef.current.clientHeight;\n const scaleX = containerWidth / newNaturalSize.width;\n const scaleY = containerHeight / newNaturalSize.height;\n const newZoom = Math.min(scaleX, scaleY);\n setZoom(Math.max(0.01, Math.min(10, newZoom)));\n setPosition({ x: 0, y: 0 });\n }\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 setZoom(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 setZoom(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 = zoom;\n touchStartPos.current = { ...position };\n }\n }, [position, zoom]);\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 }, zoom));\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 / zoom;\n setPosition(clampPosition({\n x: centerX - zoomScale * (centerX - touchStartPos.current.x),\n y: centerY - zoomScale * (centerY - touchStartPos.current.y),\n }, newZoom));\n\n setZoom(newZoom);\n }\n }, [isDragging, dragStart, zoom, clampPosition]);\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 setZoom(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 return newZoom;\n });\n };\n\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n return () => container.removeEventListener('wheel', handleWheelNative);\n }, [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 }, zoom));\n }, [isDragging, dragStart, zoom, clampPosition]);\n\n const handleMouseUp = useCallback(() => {\n if (isTouchDevice.current) return;\n setIsDragging(false);\n }, []);\n\n // 工具栏事件处理\n const handleZoomIn = useCallback(() => {\n setZoom(z => Math.min(z + 0.1, 10));\n }, []);\n\n const handleZoomOut = useCallback(() => {\n setZoom(z => Math.max(z - 0.1, 0.01));\n }, []);\n\n const handleRotateLeft = useCallback(() => {\n setRotation(r => r - 90);\n }, []);\n\n const handleRotateRight = useCallback(() => {\n setRotation(r => r + 90);\n }, []);\n\n const handleFitToWidth = useCallback(() => {\n if (containerRef.current && naturalSize.width > 0 && naturalSize.height > 0) {\n const containerWidth = containerRef.current.clientWidth;\n const containerHeight = containerRef.current.clientHeight;\n const scaleX = containerWidth / naturalSize.width;\n const scaleY = containerHeight / naturalSize.height;\n const newZoom = Math.min(scaleX, scaleY);\n setZoom(Math.max(0.01, Math.min(10, newZoom)));\n setRotation(0);\n setPosition({ x: 0, y: 0 });\n }\n }, [naturalSize]);\n\n const handleOriginalSize = useCallback(() => {\n setZoom(1);\n setRotation(0);\n setPosition({ x: 0, y: 0 });\n }, []);\n\n const handleReset = useCallback(() => {\n // 重置为自适应窗口,而不是原始尺寸\n handleFitToWidth();\n }, [handleFitToWidth]);\n\n // 工具栏配置\n const getToolbarGroups = useCallback((): ToolbarGroup[] => [\n {\n items: [\n { type: 'button', icon: <ZoomOut className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.zoom_out'), action: handleZoomOut, disabled: zoom <= 0.01 },\n { type: 'text', content: `${Math.round(zoom * 100)}%`, minWidth: '3rem' },\n { type: 'button', icon: <ZoomIn className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.zoom_in'), action: handleZoomIn, disabled: zoom >= 10 },\n ],\n },\n {\n items: [\n { type: 'button', icon: <Scan className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.fit_to_window'), action: handleFitToWidth },\n { type: 'button', icon: <OriginalSizeIcon className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.original_size'), action: handleOriginalSize },\n ],\n },\n {\n items: [\n { type: 'button', icon: <RotateCcw className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.rotate_left'), action: handleRotateLeft },\n { type: 'button', icon: <RotateCw className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.rotate_right'), action: handleRotateRight },\n ],\n },\n {\n items: [\n { type: 'button', icon: <RefreshCw className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.reset'), action: handleReset },\n ],\n },\n ], [zoom, t, handleZoomIn, handleZoomOut, handleFitToWidth, handleOriginalSize, handleRotateLeft, handleRotateRight, handleReset]);\n\n // 暴露接口给父组件\n useImperativeHandle(ref, () => ({\n getToolbarGroups,\n onToolbarChange: (listener: () => void) => {\n listenersRef.current.add(listener);\n return () => listenersRef.current.delete(listener);\n },\n fitToWidth: handleFitToWidth,\n resetView: handleReset,\n }), [getToolbarGroups, handleFitToWidth, handleReset]);\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(${zoom}) 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":["OriginalSizeIcon","className","jsx","ImageRenderer","forwardRef","url","fileSize","file","ref","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","zoom","setZoom","rotation","setRotation","naturalSize","setNaturalSize","imgRef","useRef","containerRef","blobUrlRef","fileBlobRef","loaderRef","pageCacheRef","isTouchDevice","touchStartDistance","touchStartZoom","touchStartPos","lastTapTime","listenersRef","notifyToolbarChange","useCallback","listener","useEffect","cancelled","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","blobUrl","err","handlePageChange","page","cached","firstKey","oldUrl","handleLoad","e","img","newNaturalSize","containerWidth","containerHeight","scaleX","scaleY","newZoom","clampPosition","pos","currentZoom","container","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleError","handleDoubleClick","handleTouchStart","touches","now","distance","handleTouchMove","scale","rect","centerX","centerY","zoomScale","handleTouchEnd","handleWheelNative","mouseX","mouseY","delta","prev","handleMouseDown","handleMouseMove","handleMouseUp","handleZoomIn","z","handleZoomOut","handleRotateLeft","r","handleRotateRight","handleFitToWidth","handleOriginalSize","handleReset","getToolbarGroups","ZoomOut","ZoomIn","Scan","RotateCcw","RotateCw","RefreshCw","useImperativeHandle","jsxs","Loader2","RendererError","motion"],"mappings":";;;;;;AAUA,MAAMA,KAAqD,CAAC,EAAE,WAAAC,EAAA,MAC5D,gBAAAC;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,OAAM;AAAA,IACN,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,QAAO;AAAA,IACP,aAAY;AAAA,IACZ,eAAc;AAAA,IACd,gBAAe;AAAA,IACf,WAAAD;AAAA,IAEA,4BAAC,QAAA,EAAK,GAAE,MAAK,GAAE,QAAO,YAAW,UAAS,UAAS,MAAK,YAAW,QAAO,MAAK,gBAAe,QAAO,QAAO,UAAA,MAAA,CAE5G;AAAA,EAAA;AACF,GAcWE,KAAgBC,GAAoD,CAAC;AAAA,EAChF,KAAAC;AAAA,EACA,UAAAC;AAAA,EACA,MAAAC;AACF,GAAGC,OAAQ;AACT,QAAMC,IAAIC,GAAA,GAGJ,CAACC,GAAQC,CAAS,IAAIC,EAAS,EAAK,GACpC,CAACC,GAAOC,EAAQ,IAAIF,EAAwB,IAAI,GAChD,CAACG,GAAUC,CAAW,IAAIJ,EAAS,EAAK,GACxC,CAACK,IAAgBC,EAAiB,IAAIN,EAAS,CAAC,GAChD,CAACO,GAAaC,CAAc,IAAIR,EAAwB,IAAI,GAC5D,CAACS,IAAUC,CAAW,IAAIV,EAAiB,EAAE,GAC7C,CAACW,GAAaC,CAAc,IAAIZ,EAAS,CAAC,GAC1C,CAACa,GAAYC,EAAa,IAAId,EAAS,CAAC,GACxC,CAACe,GAAUC,CAAW,IAAIhB,EAAS,EAAE,GAAG,GAAG,GAAG,GAAG,GACjD,CAACiB,GAAYC,CAAa,IAAIlB,EAAS,EAAK,GAC5C,CAACmB,GAAWC,EAAY,IAAIpB,EAAS,EAAE,GAAG,GAAG,GAAG,GAAG,GACnD,CAACqB,GAAMC,CAAO,IAAItB,EAAS,CAAC,GAC5B,CAACuB,IAAUC,CAAW,IAAIxB,EAAS,CAAC,GACpC,CAACyB,GAAaC,EAAc,IAAI1B,EAAS,EAAE,OAAO,GAAG,QAAQ,GAAG,GAEhE2B,KAASC,EAAyB,IAAI,GACtCC,IAAeD,EAAuB,IAAI,GAC1CE,IAAaF,EAAsB,IAAI,GACvCG,IAAcH,EAAoB,IAAI,GACtCI,IAAYJ,EAAY,IAAI,GAC5BK,IAAeL,EAA4B,oBAAI,KAAK,GACpDM,IAAgBN,EAAO,EAAK,GAC5BO,IAAqBP,EAAO,CAAC,GAC7BQ,KAAiBR,EAAO,CAAC,GACzBS,IAAgBT,EAAO,EAAE,GAAG,GAAG,GAAG,GAAG,GACrCU,KAAcV,EAAO,CAAC,GAGtBW,IAAeX,EAAwB,oBAAI,KAAK,GAChDY,IAAsBC,EAAY,MAAM;AAC5C,IAAAF,EAAa,QAAQ,QAAQ,CAAAG,MAAYA,EAAA,CAAU;AAAA,EACrD,GAAG,CAAA,CAAE;AAGL,EAAAC,EAAU,MAAM;AACd,IAAAH,EAAA;AAAA,EACF,GAAG,CAACnB,GAAMmB,CAAmB,CAAC,GAE9BG,EAAU,MAAM;AACd,IAAAH,EAAA;AAAA,EACF,GAAG,CAACjB,IAAUiB,CAAmB,CAAC,GAGlCG,EAAU,MAAM;AACd,QAAIC,IAAY;AA4GhB,YA1GuB,YAAY;AAyBjC,UAvBAlC,EAAY,EAAE,GACdX,EAAU,EAAK,GACfG,GAAS,IAAI,GACbE,EAAY,EAAK,GACjBI,EAAe,IAAI,GACnBF,GAAkB,CAAC,GACnBU,EAAY,EAAE,GAAG,GAAG,GAAG,GAAG,GAC1BM,EAAQ,CAAC,GACTE,EAAY,CAAC,GACbZ,EAAe,CAAC,GAChBE,GAAc,CAAC,GAGXgB,EAAW,YACb,IAAI,gBAAgBA,EAAW,OAAO,GACtCA,EAAW,UAAU,OAEvBG,EAAa,QAAQ,QAAQ,CAACzC,MAAQ,IAAI,gBAAgBA,CAAG,CAAC,GAC9DyC,EAAa,QAAQ,MAAA,GACrBF,EAAY,UAAU,MACtBC,EAAU,UAAU,MAGhB,CAACtC,GAAM;AACT,QAAKkD,KAAWlC,EAAYlB,CAAG;AAC/B;AAAA,MACF;AAEA,UAAI;AAEF,cAAMqD,IAAW,MAAMC,GAAkBpD,CAAI,GACvCqD,IAAS,MAAMC,GAAqBH,CAAQ;AAGlD,YAAI,CAACE,KAAU,CAAE,MAAMA,EAAO,YAAYF,CAAQ,GAAI;AACpD,UAAKD,KAAWlC,EAAYlB,CAAG;AAC/B;AAAA,QACF;AAGA,QAAAY,EAAY,EAAI;AAGhB,YAAI6C;AACJ,YAAIvD,aAAgB;AAClB,UAAAuD,IAAWvD;AAAA,aACN;AACL,gBAAMwD,IAAW,MAAM,MAAM1D,CAAG;AAChC,cAAI,CAAC0D,EAAS,GAAI,OAAM,IAAI,MAAM,sBAAsB;AACxD,UAAAD,IAAW,MAAMC,EAAS,KAAA;AAAA,QAC5B;AAEA,YAAIN,EAAW;AAWf,YARAb,EAAY,UAAUkB,GACtBjB,EAAU,UAAUe,GAGpBhB,EAAY,UAAUkB,GACtBjB,EAAU,UAAUe,GAGhBA,EAAO;AACT,cAAI;AACF,kBAAMI,IAAW,MAAMJ,EAAO,YAAYE,CAAQ;AAClD,YAAI,CAACL,KAAaO,EAAS,aAAaA,EAAS,YAAY,KAC3DrC,GAAcqC,EAAS,SAAS;AAAA,UAEpC,QAAQ;AAAA,UAER;AAIF,cAAMC,IAAc,MAAML,EAAO,OAAOE,GAAU;AAAA,UAChD,MAAM;AAAA,UACN,aAAa;AAAA,UACb,YAAY,CAACI,MAAoB;AAC/B,YAAKT,KACHtC,GAAkB+C,CAAO;AAAA,UAE7B;AAAA,QAAA,CACD;AAED,YAAIT,EAAW;AAGf,cAAMU,IAAU,OAAOF,KAAgB,WACnCA,IACA,IAAI,gBAAgBA,CAAW;AAEnC,QAAAtB,EAAW,UAAUwB,GACrBrB,EAAa,QAAQ,IAAI,GAAGqB,CAAO,GACnC5C,EAAY4C,CAAO,GACnBlD,EAAY,EAAK;AAAA,MACnB,SAASmD,GAAU;AACjB,QAAKX,MACHpC,GAAe+C,KAAA,gBAAAA,EAAK,YAAW,MAAM,GACrCnD,EAAY,EAAK;AAAA,MAErB;AAAA,IACF,GAEA,GAEO,MAAM;AACX,MAAAwC,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACpD,GAAKE,CAAI,CAAC;AAGd,QAAM8D,KAAmBf,EAAY,OAAOgB,MAAiB;AAE3D,QADI,CAAC1B,EAAY,WAAW,CAACC,EAAU,WACnCyB,IAAO,KAAKA,IAAO5C,EAAY;AAGnC,UAAM6C,IAASzB,EAAa,QAAQ,IAAIwB,CAAI;AAC5C,QAAIC,GAAQ;AACV,MAAA9C,EAAe6C,CAAI,GACnB/C,EAAYgD,CAAM;AAClB;AAAA,IACF;AAGA,IAAAtD,EAAY,EAAI;AAChB,QAAI;AACF,YAAMgD,IAAc,MAAMpB,EAAU,QAAQ,OAAOD,EAAY,SAAS,EAAE,MAAA0B,GAAM,GAC1EH,IAAU,OAAOF,KAAgB,WACnCA,IACA,IAAI,gBAAgBA,CAAW;AAGnC,UAAInB,EAAa,QAAQ,QAAQ,IAAI;AACnC,cAAM0B,IAAW1B,EAAa,QAAQ,KAAA,EAAO,OAAO;AACpD,YAAI0B,MAAa,QAAW;AAC1B,gBAAMC,IAAS3B,EAAa,QAAQ,IAAI0B,CAAQ;AAChD,UAAIC,KAAQ,IAAI,gBAAgBA,CAAM,GACtC3B,EAAa,QAAQ,OAAO0B,CAAQ;AAAA,QACtC;AAAA,MACF;AAEA,MAAA1B,EAAa,QAAQ,IAAIwB,GAAMH,CAAO,GACtC1C,EAAe6C,CAAI,GACnB/C,EAAY4C,CAAO,GACnBlD,EAAY,EAAK;AAAA,IACnB,SAASmD,GAAU;AACjB,MAAA/C,GAAe+C,KAAA,gBAAAA,EAAK,YAAW,QAAQ,GACvCnD,EAAY,EAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAACS,CAAU,CAAC;AAGf,EAAA8B,EAAU,MACD,MAAM;AACX,IAAIb,EAAW,WACb,IAAI,gBAAgBA,EAAW,OAAO,GAExCG,EAAa,QAAQ,QAAQ,CAACzC,MAAQ,IAAI,gBAAgBA,CAAG,CAAC,GAC9DyC,EAAa,QAAQ,MAAA;AAAA,EACvB,GACC,CAAA,CAAE;AAEL,QAAM4B,KAAa,CAACC,MAA8C;AAChE,IAAA/D,EAAU,EAAI;AACd,UAAMgE,IAAMD,EAAE,eACRE,IAAiB,EAAE,OAAOD,EAAI,cAAc,QAAQA,EAAI,cAAA;AAI9D,QAHArC,GAAesC,CAAc,GAGzBnC,EAAa,WAAWmC,EAAe,QAAQ,KAAKA,EAAe,SAAS,GAAG;AACjF,YAAMC,IAAiBpC,EAAa,QAAQ,aACtCqC,IAAkBrC,EAAa,QAAQ,cACvCsC,IAASF,IAAiBD,EAAe,OACzCI,IAASF,IAAkBF,EAAe,QAC1CK,IAAU,KAAK,IAAIF,GAAQC,CAAM;AACvC,MAAA9C,EAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI+C,CAAO,CAAC,CAAC,GAC7CrD,EAAY,EAAE,GAAG,GAAG,GAAG,GAAG;AAAA,IAC5B;AAAA,EACF,GAGMsD,IAAgB7B,EAAY,CAAC8B,GAA+BC,MAAwB;AACxF,UAAMC,IAAY5C,EAAa;AAC/B,QAAI,CAAC4C,KAAahD,EAAY,UAAU,EAAG,QAAO8C;AAElD,UAAMG,IAAaD,EAAU,aACvBE,IAAaF,EAAU,cACvBG,IAAOnD,EAAY,QAAQ+C,GAC3BK,IAAOpD,EAAY,SAAS+C,GAG5BM,IAAS,KAAK,IAAI,IAAIJ,IAAa,MAAMC,IAAa,IAAI,GAC1DI,KAAUL,IAAaE,KAAQ,IAAIE,GACnCE,KAAUL,IAAaE,KAAQ,IAAIC;AAEzC,WAAO;AAAA,MACL,GAAGC,IAAS,IAAI,KAAK,IAAI,CAACA,GAAQ,KAAK,IAAIA,GAAQR,EAAI,CAAC,CAAC,IAAI;AAAA,MAC7D,GAAGS,IAAS,IAAI,KAAK,IAAI,CAACA,GAAQ,KAAK,IAAIA,GAAQT,EAAI,CAAC,CAAC,IAAI;AAAA,IAAA;AAAA,EAEjE,GAAG,CAAC9C,CAAW,CAAC,GAEVwD,KAAc,MAAM;AACxB,IAAA/E,GAASN,EAAE,mBAAmB,CAAC,GAC/BG,EAAU,EAAI;AAAA,EAChB,GAGMmF,KAAoB,MAAM;AAC9B,IAAAlE,EAAY,EAAE,GAAG,GAAG,GAAG,GAAG,GAC1BM,EAAQ,CAAC;AAAA,EACX,GAGM6D,KAAmB1C,EAAY,CAACqB,MAAkB;AACtD,IAAA5B,EAAc,UAAU,IACxB4B,EAAE,eAAA;AAEF,UAAMsB,IAAUtB,EAAE;AAClB,QAAIsB,EAAQ,WAAW,GAAG;AAExB,MAAAlE,EAAc,EAAI,GAClBE,GAAa;AAAA,QACX,GAAGgE,EAAQ,CAAC,EAAE,UAAUrE,EAAS;AAAA,QACjC,GAAGqE,EAAQ,CAAC,EAAE,UAAUrE,EAAS;AAAA,MAAA,CAClC;AAGD,YAAMsE,IAAM,KAAK,IAAA;AACjB,MAAIA,IAAM/C,GAAY,UAAU,QAE9BtB,EAAY,EAAE,GAAG,GAAG,GAAG,GAAG,GAC1BM,EAAQ,CAAC,IAEXgB,GAAY,UAAU+C;AAAA,IACxB,WAAWD,EAAQ,WAAW,GAAG;AAE/B,MAAAlE,EAAc,EAAK;AACnB,YAAMoE,IAAW,KAAK;AAAA,QACpBF,EAAQ,CAAC,EAAE,UAAUA,EAAQ,CAAC,EAAE;AAAA,QAChCA,EAAQ,CAAC,EAAE,UAAUA,EAAQ,CAAC,EAAE;AAAA,MAAA;AAElC,MAAAjD,EAAmB,UAAUmD,GAC7BlD,GAAe,UAAUf,GACzBgB,EAAc,UAAU,EAAE,GAAGtB,EAAA;AAAA,IAC/B;AAAA,EACF,GAAG,CAACA,GAAUM,CAAI,CAAC,GAEbkE,KAAkB9C,EAAY,CAACqB,MAAkB;AACrD,IAAAA,EAAE,eAAA;AAEF,UAAMsB,IAAUtB,EAAE;AAClB,QAAIsB,EAAQ,WAAW,KAAKnE;AAE1B,MAAAD,EAAYsD,EAAc;AAAA,QACxB,GAAGc,EAAQ,CAAC,EAAE,UAAUjE,EAAU;AAAA,QAClC,GAAGiE,EAAQ,CAAC,EAAE,UAAUjE,EAAU;AAAA,MAAA,GACjCE,CAAI,CAAC;AAAA,aACC+D,EAAQ,WAAW,GAAG;AAE/B,YAAMX,IAAY5C,EAAa;AAC/B,UAAI,CAAC4C,EAAW;AAEhB,YAAMa,IAAW,KAAK;AAAA,QACpBF,EAAQ,CAAC,EAAE,UAAUA,EAAQ,CAAC,EAAE;AAAA,QAChCA,EAAQ,CAAC,EAAE,UAAUA,EAAQ,CAAC,EAAE;AAAA,MAAA;AAIlC,UAAI,KAAK,IAAIE,IAAWnD,EAAmB,OAAO,IAAI,EAAG;AAEzD,YAAMqD,IAAQF,IAAWnD,EAAmB,SACtCkC,IAAU,KAAK,IAAI,MAAM,KAAK,IAAI,IAAIjC,GAAe,UAAUoD,CAAK,CAAC,GAGrEC,IAAOhB,EAAU,sBAAA,GACjBiB,KAAWN,EAAQ,CAAC,EAAE,UAAUA,EAAQ,CAAC,EAAE,WAAW,IAAIK,EAAK,OAAOA,EAAK,QAAQ,GACnFE,KAAWP,EAAQ,CAAC,EAAE,UAAUA,EAAQ,CAAC,EAAE,WAAW,IAAIK,EAAK,MAAMA,EAAK,SAAS,GAEnFG,IAAYvB,IAAUhD;AAC5B,MAAAL,EAAYsD,EAAc;AAAA,QACxB,GAAGoB,IAAUE,KAAaF,IAAUrD,EAAc,QAAQ;AAAA,QAC1D,GAAGsD,IAAUC,KAAaD,IAAUtD,EAAc,QAAQ;AAAA,MAAA,GACzDgC,CAAO,CAAC,GAEX/C,EAAQ+C,CAAO;AAAA,IACjB;AAAA,EACF,GAAG,CAACpD,GAAYE,GAAWE,GAAMiD,CAAa,CAAC,GAEzCuB,IAAiBpD,EAAY,MAAM;AACvC,IAAAvB,EAAc,EAAK,GACnBiB,EAAmB,UAAU;AAAA,EAC/B,GAAG,CAAA,CAAE;AAKL,EAAAQ,EAAU,MAAM;AACd,UAAM8B,IAAY5C,EAAa;AAC/B,QAAI,CAAC4C,EAAW;AAEhB,UAAMqB,IAAoB,CAAC,MAAkB;AAC3C,QAAE,eAAA,GACF,EAAE,gBAAA;AAEF,YAAML,IAAOhB,EAAU,sBAAA,GACjBsB,IAAS,EAAE,UAAUN,EAAK,OAAOA,EAAK,QAAQ,GAC9CO,IAAS,EAAE,UAAUP,EAAK,MAAMA,EAAK,SAAS,GAE9CQ,IAAQ,EAAE,SAAS,IAAI,QAAQ;AAErC,MAAA3E,EAAQ,CAAA4E,MAAQ;AACd,cAAM7B,IAAU,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI6B,IAAOD,CAAK,CAAC,GACnDT,IAAQnB,IAAU6B;AAExB,eAAAlF,EAAY,QAAOsD,EAAc;AAAA,UAC/B,GAAGyB,IAASP,KAASO,IAASxB,GAAI;AAAA,UAClC,GAAGyB,IAASR,KAASQ,IAASzB,GAAI;AAAA,QAAA,GACjCF,CAAO,CAAC,GAEJA;AAAA,MACT,CAAC;AAAA,IACH;AAEA,WAAAI,EAAU,iBAAiB,SAASqB,GAAmB,EAAE,SAAS,IAAO,GAClE,MAAMrB,EAAU,oBAAoB,SAASqB,CAAiB;AAAA,EACvE,GAAG,CAACxB,CAAa,CAAC,GAGlB3B,EAAU,MAAM;AACd,UAAM8B,IAAY5C,EAAa;AAC/B,QAAK4C;AAEL,aAAAA,EAAU,iBAAiB,cAAcU,IAAkB,EAAE,SAAS,IAAO,GAC7EV,EAAU,iBAAiB,aAAac,IAAiB,EAAE,SAAS,IAAO,GAC3Ed,EAAU,iBAAiB,YAAYoB,CAAc,GACrDpB,EAAU,iBAAiB,eAAeoB,CAAc,GAEjD,MAAM;AACX,QAAApB,EAAU,oBAAoB,cAAcU,EAAgB,GAC5DV,EAAU,oBAAoB,aAAac,EAAe,GAC1Dd,EAAU,oBAAoB,YAAYoB,CAAc,GACxDpB,EAAU,oBAAoB,eAAeoB,CAAc;AAAA,MAC7D;AAAA,EACF,GAAG,CAACV,IAAkBI,IAAiBM,CAAc,CAAC;AAEtD,QAAMM,KAAkB1D,EAAY,CAACqB,MAAwB;AAC3D,IAAI5B,EAAc,WACd4B,EAAE,WAAW,MACjB5C,EAAc,EAAI,GAClBE,GAAa;AAAA,MACX,GAAG0C,EAAE,UAAU/C,EAAS;AAAA,MACxB,GAAG+C,EAAE,UAAU/C,EAAS;AAAA,IAAA,CACzB;AAAA,EACH,GAAG,CAACA,CAAQ,CAAC,GAEPqF,KAAkB3D,EAAY,CAACqB,MAAwB;AAC3D,IAAI5B,EAAc,WACbjB,KACLD,EAAYsD,EAAc;AAAA,MACxB,GAAGR,EAAE,UAAU3C,EAAU;AAAA,MACzB,GAAG2C,EAAE,UAAU3C,EAAU;AAAA,IAAA,GACxBE,CAAI,CAAC;AAAA,EACV,GAAG,CAACJ,GAAYE,GAAWE,GAAMiD,CAAa,CAAC,GAEzC+B,KAAgB5D,EAAY,MAAM;AACtC,IAAIP,EAAc,WAClBhB,EAAc,EAAK;AAAA,EACrB,GAAG,CAAA,CAAE,GAGCoF,KAAe7D,EAAY,MAAM;AACrC,IAAAnB,EAAQ,OAAK,KAAK,IAAIiF,IAAI,KAAK,EAAE,CAAC;AAAA,EACpC,GAAG,CAAA,CAAE,GAECC,KAAgB/D,EAAY,MAAM;AACtC,IAAAnB,EAAQ,OAAK,KAAK,IAAIiF,IAAI,KAAK,IAAI,CAAC;AAAA,EACtC,GAAG,CAAA,CAAE,GAECE,KAAmBhE,EAAY,MAAM;AACzC,IAAAjB,EAAY,CAAAkF,MAAKA,IAAI,EAAE;AAAA,EACzB,GAAG,CAAA,CAAE,GAECC,KAAoBlE,EAAY,MAAM;AAC1C,IAAAjB,EAAY,CAAAkF,MAAKA,IAAI,EAAE;AAAA,EACzB,GAAG,CAAA,CAAE,GAECE,IAAmBnE,EAAY,MAAM;AACzC,QAAIZ,EAAa,WAAWJ,EAAY,QAAQ,KAAKA,EAAY,SAAS,GAAG;AAC3E,YAAMwC,IAAiBpC,EAAa,QAAQ,aACtCqC,IAAkBrC,EAAa,QAAQ,cACvCsC,IAASF,IAAiBxC,EAAY,OACtC2C,IAASF,IAAkBzC,EAAY,QACvC4C,IAAU,KAAK,IAAIF,GAAQC,CAAM;AACvC,MAAA9C,EAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI+C,CAAO,CAAC,CAAC,GAC7C7C,EAAY,CAAC,GACbR,EAAY,EAAE,GAAG,GAAG,GAAG,GAAG;AAAA,IAC5B;AAAA,EACF,GAAG,CAACS,CAAW,CAAC,GAEVoF,KAAqBpE,EAAY,MAAM;AAC3C,IAAAnB,EAAQ,CAAC,GACTE,EAAY,CAAC,GACbR,EAAY,EAAE,GAAG,GAAG,GAAG,GAAG;AAAA,EAC5B,GAAG,CAAA,CAAE,GAEC8F,IAAcrE,EAAY,MAAM;AAEpC,IAAAmE,EAAA;AAAA,EACF,GAAG,CAACA,CAAgB,CAAC,GAGfG,KAAmBtE,EAAY,MAAsB;AAAA,IACzD;AAAA,MACE,OAAO;AAAA,QACL,EAAE,MAAM,UAAU,MAAM,gBAAApD,EAAC2H,MAAQ,WAAU,kBAAA,CAAkB,GAAI,SAASpH,EAAE,kBAAkB,GAAG,QAAQ4G,IAAe,UAAUnF,KAAQ,KAAA;AAAA,QAC1I,EAAE,MAAM,QAAQ,SAAS,GAAG,KAAK,MAAMA,IAAO,GAAG,CAAC,KAAK,UAAU,OAAA;AAAA,QACjE,EAAE,MAAM,UAAU,MAAM,gBAAAhC,EAAC4H,MAAO,WAAU,kBAAA,CAAkB,GAAI,SAASrH,EAAE,iBAAiB,GAAG,QAAQ0G,IAAc,UAAUjF,KAAQ,GAAA;AAAA,MAAG;AAAA,IAC5I;AAAA,IAEF;AAAA,MACE,OAAO;AAAA,QACL,EAAE,MAAM,UAAU,wBAAO6F,IAAA,EAAK,WAAU,kBAAA,CAAkB,GAAI,SAAStH,EAAE,uBAAuB,GAAG,QAAQgH,EAAA;AAAA,QAC3G,EAAE,MAAM,UAAU,wBAAOzH,IAAA,EAAiB,WAAU,kBAAA,CAAkB,GAAI,SAASS,EAAE,uBAAuB,GAAG,QAAQiH,GAAA;AAAA,MAAmB;AAAA,IAC5I;AAAA,IAEF;AAAA,MACE,OAAO;AAAA,QACL,EAAE,MAAM,UAAU,wBAAOM,IAAA,EAAU,WAAU,kBAAA,CAAkB,GAAI,SAASvH,EAAE,qBAAqB,GAAG,QAAQ6G,GAAA;AAAA,QAC9G,EAAE,MAAM,UAAU,wBAAOW,IAAA,EAAS,WAAU,kBAAA,CAAkB,GAAI,SAASxH,EAAE,sBAAsB,GAAG,QAAQ+G,GAAA;AAAA,MAAkB;AAAA,IAClI;AAAA,IAEF;AAAA,MACE,OAAO;AAAA,QACL,EAAE,MAAM,UAAU,wBAAOU,IAAA,EAAU,WAAU,kBAAA,CAAkB,GAAI,SAASzH,EAAE,eAAe,GAAG,QAAQkH,EAAA;AAAA,MAAY;AAAA,IACtH;AAAA,EACF,GACC,CAACzF,GAAMzB,GAAG0G,IAAcE,IAAeI,GAAkBC,IAAoBJ,IAAkBE,IAAmBG,CAAW,CAAC;AAGjI,SAAAQ,GAAoB3H,IAAK,OAAO;AAAA,IAC9B,kBAAAoH;AAAA,IACA,iBAAiB,CAACrE,OAChBH,EAAa,QAAQ,IAAIG,CAAQ,GAC1B,MAAMH,EAAa,QAAQ,OAAOG,CAAQ;AAAA,IAEnD,YAAYkE;AAAA,IACZ,WAAWE;AAAA,EAAA,IACT,CAACC,IAAkBH,GAAkBE,CAAW,CAAC,GAGnD,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAK1F;AAAA,MACL,WAAU;AAAA,MACV,aAAasE;AAAA,MACb,aAAaC;AAAA,MACb,WAAWC;AAAA,MACX,cAAcA;AAAA,MACd,OAAO,EAAE,QAAQpF,IAAa,aAAa,QAAQ,aAAa,OAAA;AAAA,MAG/D,UAAA;AAAA,QAAAd,KACC,gBAAAoH,EAAC,OAAA,EAAI,WAAU,mHACb,UAAA;AAAA,UAAA,gBAAAlI,EAACmI,IAAA,EAAQ,WAAU,yDAAA,CAAyD;AAAA,UAC5E,gBAAAD,EAAC,KAAA,EAAE,WAAU,kCAAiC,UAAA;AAAA,YAAA;AAAA,YACnClH,KAAiB,KAAK,GAAG,KAAK,MAAMA,EAAc,CAAC;AAAA,UAAA,EAAA,CAC9D;AAAA,QAAA,GACF;AAAA,QAIDE,KACC,gBAAAlB,EAAC,OAAA,EAAI,WAAU,sGACb,UAAA,gBAAAA,EAACoI,IAAA,EAAc,SAAS7H,EAAE,qBAAqB,GAAG,QAAQW,GAAa,GACzE;AAAA,QAGD,CAACT,KAAU,CAACG,KAAS,CAACE,KAAY,CAACI,KAClC,gBAAAlB,EAAC,OAAA,EAAI,WAAU,gDACb,UAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,qHAAoH,GACrI;AAAA,QAGDY,KACC,gBAAAZ,EAACoI,IAAA,EAAc,SAASxH,EAAA,CAAO;AAAA,QAGhCQ,MACC,gBAAApB;AAAA,UAACqI,GAAO;AAAA,UAAP;AAAA,YACC,KAAK/F;AAAA,YACL,KAAKlB;AAAA,YACL,KAAI;AAAA,YACJ,WAAW,kCAAkC,CAACX,KAAUG,KAASM,IAAc,eAAe,EAAE;AAAA,YAChG,OAAO;AAAA,cACL,WAAW,aAAaQ,EAAS,CAAC,OAAOA,EAAS,CAAC,aAAaM,CAAI,YAAYE,EAAQ;AAAA,cACxF,iBAAiB;AAAA,cACjB,YAAYN,IAAa,SAAS;AAAA,YAAA;AAAA,YAEpC,QAAQ4C;AAAA,YACR,SAASoB;AAAA,YACT,eAAeC;AAAA,YACf,SAAS,EAAE,SAAS,EAAA;AAAA,YACpB,SAAS,EAAE,SAASpF,KAAU,CAACG,KAAS,CAACM,IAAc,IAAI,EAAA;AAAA,YAC3D,YAAY,EAAE,UAAU,IAAA;AAAA,YACxB,WAAW;AAAA,UAAA;AAAA,QAAA;AAAA,QAKdT,KAAU,CAACG,KAASwB,EAAY,QAAQ,KACvC,gBAAA8F,EAAC,OAAA,EAAI,WAAU,2LACZ,UAAA;AAAA,UAAA9F,EAAY;AAAA,UAAM;AAAA,UAAIA,EAAY;AAAA,UAAQhC,KAAY,QAAQ,MAAMA,IAAW,OAAO,GAAGA,CAAQ,OAAOA,IAAW,OAAO,OAAO,IAAIA,IAAW,MAAM,QAAQ,CAAC,CAAC,QAAQ,IAAIA,KAAY,OAAO,OAAO,QAAQ,CAAC,CAAC,KAAK;AAAA,QAAA,GACxN;AAAA,QAIDoB,IAAa,KACZ,gBAAA0G,EAAC,OAAA,EAAI,WAAU,sOACb,UAAA;AAAA,UAAA,gBAAAlI;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAMmE,GAAiB7C,IAAc,CAAC;AAAA,cAC/C,UAAUA,KAAe,KAAKR;AAAA,cAC9B,WAAU;AAAA,cACX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGD,gBAAAoH,EAAC,QAAA,EAAK,WAAU,0CACb,UAAA;AAAA,YAAA5G;AAAA,YAAY;AAAA,YAAIE;AAAA,UAAA,GACnB;AAAA,UACA,gBAAAxB;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAMmE,GAAiB7C,IAAc,CAAC;AAAA,cAC/C,UAAUA,KAAeE,KAAcV;AAAA,cACvC,WAAU;AAAA,cACX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QAED,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;"}
|
|
1
|
+
{"version":3,"file":"index-BjL2sXlA.mjs","sources":["../../src/renderers/Image/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback, useImperativeHandle, forwardRef } from 'react';\nimport { motion } from 'framer-motion';\nimport { Loader2, ZoomIn, ZoomOut, RotateCw, RotateCcw, Scan, RefreshCw } 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';\nimport type { RendererHandle } from '../base.types';\nimport type { ToolbarGroup } from '../toolbar.types';\n\nconst OriginalSizeIcon: React.FC<{ className?: string }> = ({ className }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <text x=\"12\" y=\"17.5\" textAnchor=\"middle\" fontSize=\"20\" fontWeight=\"bold\" fill=\"currentColor\" stroke=\"none\">\n 1:1\n </text>\n </svg>\n);\n\nexport interface ImageRendererHandle extends RendererHandle {\n fitToWidth: () => void;\n resetView: () => void;\n}\n\ninterface ImageRendererProps {\n url: string;\n fileSize?: number;\n file?: PreviewFile | File;\n}\n\nexport const ImageRenderer = forwardRef<ImageRendererHandle, ImageRendererProps>(({\n url,\n fileSize,\n file,\n}, ref) => {\n const t = useTranslator();\n\n // 内部状态管理\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 [zoom, setZoom] = useState(1);\n const [rotation, setRotation] = useState(0);\n const [naturalSize, setNaturalSize] = useState({ width: 0, height: 0 });\n\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 const listenersRef = useRef<Set<() => void>>(new Set());\n const notifyToolbarChange = useCallback(() => {\n listenersRef.current.forEach(listener => listener());\n }, []);\n\n // 监听影响工具栏的状态变化\n useEffect(() => {\n notifyToolbarChange();\n }, [zoom, notifyToolbarChange]);\n\n useEffect(() => {\n notifyToolbarChange();\n }, [rotation, notifyToolbarChange]);\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 setZoom(1);\n setRotation(0);\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 const handleLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n setLoaded(true);\n const img = e.currentTarget;\n const newNaturalSize = { width: img.naturalWidth, height: img.naturalHeight };\n setNaturalSize(newNaturalSize);\n\n // 图片加载完成后,自动适应窗口大小\n if (containerRef.current && newNaturalSize.width > 0 && newNaturalSize.height > 0) {\n const containerWidth = containerRef.current.clientWidth;\n const containerHeight = containerRef.current.clientHeight;\n const scaleX = containerWidth / newNaturalSize.width;\n const scaleY = containerHeight / newNaturalSize.height;\n const newZoom = Math.min(scaleX, scaleY);\n setZoom(Math.max(0.01, Math.min(10, newZoom)));\n setPosition({ x: 0, y: 0 });\n }\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 setZoom(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 setZoom(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 = zoom;\n touchStartPos.current = { ...position };\n }\n }, [position, zoom]);\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 }, zoom));\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 / zoom;\n setPosition(clampPosition({\n x: centerX - zoomScale * (centerX - touchStartPos.current.x),\n y: centerY - zoomScale * (centerY - touchStartPos.current.y),\n }, newZoom));\n\n setZoom(newZoom);\n }\n }, [isDragging, dragStart, zoom, clampPosition]);\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 setZoom(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 return newZoom;\n });\n };\n\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n return () => container.removeEventListener('wheel', handleWheelNative);\n }, [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 }, zoom));\n }, [isDragging, dragStart, zoom, clampPosition]);\n\n const handleMouseUp = useCallback(() => {\n if (isTouchDevice.current) return;\n setIsDragging(false);\n }, []);\n\n // 工具栏事件处理\n const handleZoomIn = useCallback(() => {\n setZoom(z => Math.min(z + 0.1, 10));\n }, []);\n\n const handleZoomOut = useCallback(() => {\n setZoom(z => Math.max(z - 0.1, 0.01));\n }, []);\n\n const handleRotateLeft = useCallback(() => {\n setRotation(r => r - 90);\n }, []);\n\n const handleRotateRight = useCallback(() => {\n setRotation(r => r + 90);\n }, []);\n\n const handleFitToWidth = useCallback(() => {\n if (containerRef.current && naturalSize.width > 0 && naturalSize.height > 0) {\n const containerWidth = containerRef.current.clientWidth;\n const containerHeight = containerRef.current.clientHeight;\n const scaleX = containerWidth / naturalSize.width;\n const scaleY = containerHeight / naturalSize.height;\n const newZoom = Math.min(scaleX, scaleY);\n setZoom(Math.max(0.01, Math.min(10, newZoom)));\n setRotation(0);\n setPosition({ x: 0, y: 0 });\n }\n }, [naturalSize]);\n\n const handleOriginalSize = useCallback(() => {\n setZoom(1);\n setRotation(0);\n setPosition({ x: 0, y: 0 });\n }, []);\n\n const handleReset = useCallback(() => {\n // 重置为自适应窗口,而不是原始尺寸\n handleFitToWidth();\n }, [handleFitToWidth]);\n\n // 工具栏配置\n const getToolbarGroups = useCallback((): ToolbarGroup[] => [\n {\n items: [\n { type: 'button', icon: <ZoomOut className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.zoom_out'), action: handleZoomOut, disabled: zoom <= 0.01 },\n { type: 'text', content: `${Math.round(zoom * 100)}%`, minWidth: '3rem' },\n { type: 'button', icon: <ZoomIn className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.zoom_in'), action: handleZoomIn, disabled: zoom >= 10 },\n ],\n },\n {\n items: [\n { type: 'button', icon: <Scan className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.fit_to_window'), action: handleFitToWidth },\n { type: 'button', icon: <OriginalSizeIcon className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.original_size'), action: handleOriginalSize },\n ],\n },\n {\n items: [\n { type: 'button', icon: <RotateCcw className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.rotate_left'), action: handleRotateLeft },\n { type: 'button', icon: <RotateCw className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.rotate_right'), action: handleRotateRight },\n ],\n },\n {\n items: [\n { type: 'button', icon: <RefreshCw className=\"rfp-w-4 rfp-h-4\" />, tooltip: t('toolbar.reset'), action: handleReset },\n ],\n },\n ], [zoom, t, handleZoomIn, handleZoomOut, handleFitToWidth, handleOriginalSize, handleRotateLeft, handleRotateRight, handleReset]);\n\n // 暴露接口给父组件\n useImperativeHandle(ref, () => ({\n getToolbarGroups,\n onToolbarChange: (listener: () => void) => {\n listenersRef.current.add(listener);\n return () => listenersRef.current.delete(listener);\n },\n fitToWidth: handleFitToWidth,\n resetView: handleReset,\n }), [getToolbarGroups, handleFitToWidth, handleReset]);\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(${zoom}) 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":["OriginalSizeIcon","className","jsx","ImageRenderer","forwardRef","url","fileSize","file","ref","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","zoom","setZoom","rotation","setRotation","naturalSize","setNaturalSize","imgRef","useRef","containerRef","blobUrlRef","fileBlobRef","loaderRef","pageCacheRef","isTouchDevice","touchStartDistance","touchStartZoom","touchStartPos","lastTapTime","listenersRef","notifyToolbarChange","useCallback","listener","useEffect","cancelled","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","blobUrl","err","handlePageChange","page","cached","firstKey","oldUrl","handleLoad","e","img","newNaturalSize","containerWidth","containerHeight","scaleX","scaleY","newZoom","clampPosition","pos","currentZoom","container","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleError","handleDoubleClick","handleTouchStart","touches","now","distance","handleTouchMove","scale","rect","centerX","centerY","zoomScale","handleTouchEnd","handleWheelNative","mouseX","mouseY","delta","prev","handleMouseDown","handleMouseMove","handleMouseUp","handleZoomIn","z","handleZoomOut","handleRotateLeft","r","handleRotateRight","handleFitToWidth","handleOriginalSize","handleReset","getToolbarGroups","ZoomOut","ZoomIn","Scan","RotateCcw","RotateCw","RefreshCw","useImperativeHandle","jsxs","Loader2","RendererError","motion"],"mappings":"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"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsxs as D, jsx as o } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef as N, useState as M, useRef as c, useCallback as y, useEffect as z, useImperativeHandle as j } from "react";
|
|
3
|
-
import E from "
|
|
4
|
-
|
|
5
|
-
import { u as W, a as k, z as A, S as L, M as F, U as G } from "./index-DreA69iU.mjs";
|
|
3
|
+
import { S as E } from "./index-CyBXARuf.mjs";
|
|
4
|
+
import { u as W, a as k, z as A, S as L, M as F, U as G } from "./index-B3jtj_7-.mjs";
|
|
6
5
|
import { R as I } from "./RendererError-D5i8eSpN.mjs";
|
|
7
|
-
const
|
|
8
|
-
const R = W(),
|
|
6
|
+
const B = N(({ url: v, fileName: h }, S) => {
|
|
7
|
+
const R = W(), C = k(), [m, w] = M(!0), [b, T] = M(null), r = c(null), g = c(null), a = c(null), u = c(null), f = c(null), x = c({ width: 0, height: 0 }), l = y(() => {
|
|
9
8
|
if (!r.current) return { width: 800, height: 600 };
|
|
10
9
|
const e = r.current.clientWidth, n = r.current.clientHeight, t = e > 100 ? e : 800, s = n > 100 ? n : 600;
|
|
11
10
|
return { width: t, height: s };
|
|
@@ -58,7 +57,7 @@ const J = N(({ url: v, fileName: h }, C) => {
|
|
|
58
57
|
if (r.current) {
|
|
59
58
|
w(!0), T(null);
|
|
60
59
|
try {
|
|
61
|
-
const i = await A(v, { fetcher:
|
|
60
|
+
const i = await A(v, { fetcher: C, signal: n.signal }), p = L(i, { delimiter: F(h) }), H = G(p.header, p.rows, h);
|
|
62
61
|
if (!e) return;
|
|
63
62
|
a.current = H, d(), w(!1);
|
|
64
63
|
} catch (i) {
|
|
@@ -74,7 +73,7 @@ const J = N(({ url: v, fileName: h }, C) => {
|
|
|
74
73
|
return () => {
|
|
75
74
|
e = !1, n.abort(), clearTimeout(s), a.current = null, r.current && (r.current.innerHTML = ""), g.current = null;
|
|
76
75
|
};
|
|
77
|
-
}, [v, h, d]), j(
|
|
76
|
+
}, [v, h, d]), j(S, () => ({
|
|
78
77
|
getToolbarGroups: () => []
|
|
79
78
|
}), []), /* @__PURE__ */ D("div", { className: "rfp-relative rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full", children: [
|
|
80
79
|
m && /* @__PURE__ */ o("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", children: /* @__PURE__ */ D("div", { className: "rfp-text-center", children: [
|
|
@@ -93,6 +92,6 @@ const J = N(({ url: v, fileName: h }, C) => {
|
|
|
93
92
|
] });
|
|
94
93
|
});
|
|
95
94
|
export {
|
|
96
|
-
|
|
95
|
+
B as CsvRenderer
|
|
97
96
|
};
|
|
98
|
-
//# sourceMappingURL=index-
|
|
97
|
+
//# sourceMappingURL=index-BrTFkhJZ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-BrTFkhJZ.mjs","sources":["../../src/renderers/Csv/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback, forwardRef, useImperativeHandle } from 'react';\nimport Spreadsheet from 'x-data-spreadsheet';\nimport {\n parseCsv,\n guessCsvDelimiter,\n fetchTextUtf8,\n convertCsvToSpreadsheetData,\n} from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\nimport type { RendererHandle } from '../base.types';\n\ninterface CsvRendererProps {\n url: string;\n fileName: string;\n}\n\nexport const CsvRenderer = forwardRef<RendererHandle, CsvRendererProps>(({ url, fileName }, 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 containerRef = useRef<HTMLDivElement>(null);\n const spreadsheetRef = useRef<Spreadsheet | null>(null);\n const sheetDataRef = useRef<Record<string, unknown>[] | null>(null);\n const resizeObserverRef = useRef<ResizeObserver | null>(null);\n const resizeTimeoutRef = useRef<number | null>(null);\n const lastDimensionsRef = useRef({ width: 0, height: 0 });\n\n const calculateDimensions = useCallback(() => {\n if (!containerRef.current) return { width: 800, height: 600 };\n const rawWidth = containerRef.current.clientWidth;\n const rawHeight = containerRef.current.clientHeight;\n const width = rawWidth > 100 ? rawWidth : 800;\n const height = rawHeight > 100 ? rawHeight : 600;\n return { width, height };\n }, []);\n\n const mountSpreadsheet = useCallback(() => {\n if (!containerRef.current || !sheetDataRef.current) return;\n\n containerRef.current.innerHTML = '';\n spreadsheetRef.current = null;\n\n const { width, height } = calculateDimensions();\n const isMobile = width < 640;\n\n const s = new Spreadsheet(containerRef.current, {\n mode: 'read',\n showToolbar: false,\n showContextmenu: false,\n showGrid: true,\n row: {\n len: 100,\n height: 25,\n },\n col: {\n len: 26,\n width: isMobile ? 80 : 100,\n indexWidth: isMobile ? 40 : 60,\n minWidth: isMobile ? 40 : 60,\n },\n view: {\n height: () => height,\n width: () => width,\n },\n });\n\n s.loadData(sheetDataRef.current as unknown as Record<string, unknown>);\n spreadsheetRef.current = s;\n }, [calculateDimensions]);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n let isInitialRender = true;\n\n const updateDimensions = () => {\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensionsRef.current = calculateDimensions();\n return;\n }\n\n const newDimensions = calculateDimensions();\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) return;\n\n lastDimensionsRef.current = newDimensions;\n\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n\n resizeTimeoutRef.current = window.setTimeout(() => {\n if (sheetDataRef.current) {\n mountSpreadsheet();\n }\n }, 500);\n };\n\n resizeObserverRef.current = new ResizeObserver(() => {\n updateDimensions();\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, mountSpreadsheet]);\n\n useEffect(() => {\n let isMounted = true;\n const controller = new AbortController();\n\n const loadCsv = async () => {\n if (!containerRef.current) return;\n\n setLoading(true);\n setError(null);\n\n try {\n const text = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n const parsed = parseCsv(text, { delimiter: guessCsvDelimiter(fileName) });\n const sheetData = convertCsvToSpreadsheetData(parsed.header, parsed.rows, fileName);\n\n if (!isMounted) return;\n\n sheetDataRef.current = sheetData as unknown as Record<string, unknown>[];\n mountSpreadsheet();\n setLoading(false);\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n if (isMounted) {\n console.error('CSV 解析错误:', err);\n setError(t('csv.load_failed'));\n setLoading(false);\n }\n }\n };\n\n const timer = setTimeout(() => {\n requestAnimationFrame(() => {\n loadCsv();\n });\n }, 100);\n\n return () => {\n isMounted = false;\n controller.abort();\n clearTimeout(timer);\n sheetDataRef.current = null;\n if (containerRef.current) {\n containerRef.current.innerHTML = '';\n }\n spreadsheetRef.current = null;\n };\n }, [url, fileName, mountSpreadsheet]);\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 {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('csv.loading')}</p>\n </div>\n </div>\n )}\n\n {error && !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 <RendererError message={error} />\n </div>\n )}\n\n {!error && (\n <div\n ref={containerRef}\n className=\"xlsx-spreadsheet-container rfp-w-full rfp-h-full\"\n style={{ opacity: loading ? 0 : 1 }}\n />\n )}\n </div>\n );\n});\n"],"names":["CsvRenderer","forwardRef","url","fileName","ref","t","useTranslator","fetcher","useFetcher","loading","setLoading","useState","error","setError","containerRef","useRef","spreadsheetRef","sheetDataRef","resizeObserverRef","resizeTimeoutRef","lastDimensionsRef","calculateDimensions","useCallback","rawWidth","rawHeight","width","height","mountSpreadsheet","isMobile","Spreadsheet","useEffect","isInitialRender","updateDimensions","newDimensions","lastDimensions","widthDiff","heightDiff","isMounted","controller","loadCsv","text","fetchTextUtf8","parsed","parseCsv","guessCsvDelimiter","sheetData","convertCsvToSpreadsheetData","err","timer","useImperativeHandle","jsxs","jsx","RendererError"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { forwardRef as V, useRef as q, useState as o, useImperativeHandle as K,
|
|
|
3
3
|
import { createPortal as le } from "react-dom";
|
|
4
4
|
import { ChevronRight as se, FolderOpen as ie, Folder as pe, FileImage as oe, FileText as ae, FileCode as ce, File as de } from "lucide-react";
|
|
5
5
|
import { R as ue } from "./RendererError-D5i8eSpN.mjs";
|
|
6
|
-
import { u as me, a as he, c as Q, R as xe, N as be, D as we, A as ve, C as ye, F as ge } from "./index-
|
|
6
|
+
import { u as me, a as he, c as Q, R as xe, N as be, D as we, A as ve, C as ye, F as ge } from "./index-B3jtj_7-.mjs";
|
|
7
7
|
const ee = V(({
|
|
8
8
|
left: t,
|
|
9
9
|
right: s,
|
|
@@ -128,7 +128,7 @@ const ee = V(({
|
|
|
128
128
|
});
|
|
129
129
|
ee.displayName = "ResizableSplit";
|
|
130
130
|
const Ne = ne(
|
|
131
|
-
() => import("./index-
|
|
131
|
+
() => import("./index-B3jtj_7-.mjs").then((t) => t.i).then((t) => ({ default: t.FilePreviewContent }))
|
|
132
132
|
), ke = (t) => {
|
|
133
133
|
const s = ge({ name: t, type: "" });
|
|
134
134
|
return s === "image" ? oe : s === "text" || s === "markdown" || s === "json" || s === "csv" || s === "xml" || s === "subtitle" ? t.endsWith(".md") || t.endsWith(".markdown") ? ae : ce : de;
|
|
@@ -356,4 +356,4 @@ const Ne = ne(
|
|
|
356
356
|
export {
|
|
357
357
|
Fe as ZipRenderer
|
|
358
358
|
};
|
|
359
|
-
//# sourceMappingURL=index-
|
|
359
|
+
//# sourceMappingURL=index-BvBldLaU.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-CgFv7B_G.mjs","sources":["../../src/components/ResizableSplit.tsx","../../src/renderers/Zip/index.tsx"],"sourcesContent":["import React, { useEffect, useImperativeHandle, useRef, useState, useCallback, forwardRef } from 'react';\n\nexport interface ResizableSplitProps {\n /** 左侧内容 */\n left: React.ReactNode;\n /** 右侧内容 */\n right: React.ReactNode;\n /** 左侧初始宽度(px);传入 storageKey 时会从 localStorage 读取 */\n initialLeftWidth?: number;\n /** 左侧最小宽度(px) */\n minLeftWidth?: number;\n /** 左侧最大宽度(px),同时不超过 `容器宽 - minRightWidth - 分隔线宽` */\n maxLeftWidth?: number;\n /** 右侧至少保留的宽度(px) */\n minRightWidth?: number;\n /** localStorage 持久化 key;不传则不持久化 */\n storageKey?: string;\n /** 启用横向拖动的媒体查询,默认 `(min-width: 768px)` */\n desktopMedia?: string;\n /** 容器额外类名 */\n className?: string;\n /** 移动端使用 Tab 切换而非上下堆叠 */\n mobileTabMode?: boolean;\n /** Tab 模式下左侧标题 */\n leftTabLabel?: string;\n /** Tab 模式下右侧标题 */\n rightTabLabel?: string;\n}\n\nexport interface ResizableSplitHandle {\n switchTab: (tab: 'left' | 'right') => void;\n}\n\n/**\n * 通用可拖动分隔布局:\n * - 桌面端(由 `desktopMedia` 判定)横向分两栏,中间分隔线可左右拖动调整左栏宽度\n * - 移动端默认退化为上下堆叠(不显示分隔线)\n * - 设置 `mobileTabMode` 时移动端使用 Tab 切换显示\n * - 可选 `storageKey` 将宽度持久化到 localStorage\n */\nexport const ResizableSplit = forwardRef<ResizableSplitHandle, ResizableSplitProps>(({\n left,\n right,\n initialLeftWidth = 280,\n minLeftWidth = 160,\n maxLeftWidth = 640,\n minRightWidth = 200,\n storageKey,\n desktopMedia = '(min-width: 768px)',\n className = '',\n mobileTabMode = false,\n leftTabLabel = '文件树',\n rightTabLabel = '预览',\n}, ref) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [leftWidth, setLeftWidth] = useState<number>(() => {\n if (storageKey && typeof window !== 'undefined') {\n const saved = Number(window.localStorage.getItem(storageKey));\n if (!isNaN(saved) && saved > 0) return saved;\n }\n return initialLeftWidth;\n });\n const [dragging, setDragging] = useState(false);\n const [isDesktop, setIsDesktop] = useState(false);\n const [activeTab, setActiveTab] = useState<'left' | 'right'>('left');\n\n useImperativeHandle(ref, () => ({\n switchTab: (tab) => setActiveTab(tab),\n }), []);\n\n // 响应式:监听媒体查询\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mq = window.matchMedia(desktopMedia);\n const handler = () => setIsDesktop(mq.matches);\n handler();\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, [desktopMedia]);\n\n // 拖动\n useEffect(() => {\n if (!dragging) return;\n const handleMove = (e: MouseEvent) => {\n if (!containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const cap = rect.width - minRightWidth - 6;\n const effectiveMax = Math.min(maxLeftWidth, cap);\n const newW = Math.max(minLeftWidth, Math.min(effectiveMax, x));\n setLeftWidth(newW);\n };\n const handleUp = () => setDragging(false);\n window.addEventListener('mousemove', handleMove);\n window.addEventListener('mouseup', handleUp);\n const prevCursor = document.body.style.cursor;\n const prevSelect = document.body.style.userSelect;\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n return () => {\n window.removeEventListener('mousemove', handleMove);\n window.removeEventListener('mouseup', handleUp);\n document.body.style.cursor = prevCursor;\n document.body.style.userSelect = prevSelect;\n };\n }, [dragging, minLeftWidth, maxLeftWidth, minRightWidth]);\n\n // 持久化\n useEffect(() => {\n if (!storageKey || dragging) return;\n try {\n window.localStorage.setItem(storageKey, String(leftWidth));\n } catch {\n // ignore\n }\n }, [leftWidth, storageKey, dragging]);\n\n const handleDividerDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n setDragging(true);\n }, []);\n\n // 移动端 Tab 模式\n if (mobileTabMode && !isDesktop) {\n return (\n <div\n ref={containerRef}\n className={`rfp-w-full rfp-h-full rfp-flex rfp-flex-col rfp-min-h-0 rfp-min-w-0 ${className}`}\n >\n <div className=\"rfp-flex rfp-flex-shrink-0 rfp-border-b rfp-border-line-weak rfp-bg-surface-toolbar\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('left')}\n className={`rfp-flex-1 rfp-py-2.5 rfp-text-sm rfp-transition-colors ${\n activeTab === 'left'\n ? 'rfp-text-fg-primary rfp-border-b-2 rfp-border-fg-primary -rfp-mb-px'\n : 'rfp-text-fg-secondary'\n }`}\n >\n {leftTabLabel}\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('right')}\n className={`rfp-flex-1 rfp-py-2.5 rfp-text-sm rfp-transition-colors ${\n activeTab === 'right'\n ? 'rfp-text-fg-primary rfp-border-b-2 rfp-border-fg-primary -rfp-mb-px'\n : 'rfp-text-fg-secondary'\n }`}\n >\n {rightTabLabel}\n </button>\n </div>\n <div\n className=\"rfp-flex-1 rfp-min-h-0 rfp-min-w-0 rfp-w-full rfp-overflow-hidden\"\n style={{ display: activeTab === 'left' ? undefined : 'none' }}\n >\n {left}\n </div>\n <div\n className=\"rfp-flex-1 rfp-min-h-0 rfp-min-w-0 rfp-w-full rfp-overflow-hidden\"\n style={{ display: activeTab === 'right' ? undefined : 'none' }}\n >\n {right}\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={`rfp-w-full rfp-h-full rfp-flex rfp-flex-col md:rfp-flex-row rfp-min-h-0 rfp-min-w-0 ${className}`}\n >\n <div\n className=\"rfp-min-h-0 rfp-min-w-0 rfp-flex-shrink-0 rfp-w-full rfp-max-h-60 md:rfp-h-full md:rfp-max-h-none\"\n style={isDesktop ? { width: `${leftWidth}px` } : undefined}\n >\n {left}\n </div>\n {/* 分隔线:仅桌面显示 */}\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n onMouseDown={handleDividerDown}\n className={`rfp-hidden md:rfp-block rfp-relative rfp-w-1.5 rfp-flex-shrink-0 rfp-cursor-col-resize rfp-transition-colors ${\n dragging ? 'rfp-bg-surface-toolbar' : 'rfp-bg-surface-2 hover:rfp-bg-surface-3'\n }`}\n >\n {/* 加宽命中区,改善拖动体验 */}\n <span className=\"rfp-absolute rfp-inset-y-0 -rfp-left-1 -rfp-right-1\" />\n </div>\n <div className=\"rfp-flex-1 rfp-min-w-0 rfp-min-h-0 rfp-overflow-hidden\">{right}</div>\n </div>\n );\n});\n\nResizableSplit.displayName = 'ResizableSplit';\n","import { useState, useEffect, useMemo, useCallback, useRef, lazy, Suspense, forwardRef, useImperativeHandle } from 'react';\nimport React from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n Folder,\n FolderOpen,\n FileText,\n FileImage,\n FileCode,\n File as FileIcon,\n ChevronRight,\n} from 'lucide-react';\nimport { RendererError } from '../RendererError';\nimport type { RendererHandle } from '../base.types';\nimport type JSZip from 'jszip';\nimport {\n loadZip,\n listZipEntries,\n buildZipTree,\n readZipEntryBlob,\n formatFileSize,\n getFileType,\n inferMimeType,\n type ZipTreeNode,\n} from '@eternalheart/file-preview-core';\nimport { ResizableSplit, type ResizableSplitHandle } from '../../components/ResizableSplit';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\n\nexport interface ZipToolbarStats {\n files: number;\n dirs: number;\n size: number;\n}\n\n// 懒加载 FilePreviewContent 以打破循环依赖\nconst LazyFilePreviewContent = lazy(() =>\n import('../../FilePreviewContent').then(m => ({ default: m.FilePreviewContent }))\n);\n\ninterface ZipRendererProps {\n url: string;\n /** ZIP 嵌套深度(由 FilePreviewContent 传入) */\n nestingDepth?: number;\n /** 解析完成后向外回报统计信息(files / dirs / size),供工具栏展示 */\n onStatsChange?: (stats: ZipToolbarStats | null) => void;\n}\n\ninterface SelectedPreview {\n path: string;\n name: string;\n size: number;\n blobUrl: string;\n}\n\n/** 根据文件类型返回树节点图标 */\nconst resolveIcon = (name: string) => {\n const ft = getFileType({ id: '', name, url: '', type: '' });\n if (ft === 'image') return FileImage;\n if (ft === 'text' || ft === 'markdown' || ft === 'json' || ft === 'csv' || ft === 'xml' || ft === 'subtitle') {\n return name.endsWith('.md') || name.endsWith('.markdown') ? FileText : FileCode;\n }\n return FileIcon;\n};\n\n// ---------- Tooltip via portal ----------\n\ninterface HoverTipState {\n text: string;\n x: number;\n y: number;\n}\n\n// ---------- Tree item ----------\n\ninterface TreeItemProps {\n node: ZipTreeNode;\n depth: number;\n selectedPath: string | null;\n expanded: Set<string>;\n onToggle: (path: string) => void;\n onSelect: (node: ZipTreeNode) => void;\n onHover: (text: string, rect: DOMRect) => void;\n onLeave: () => void;\n}\n\nconst TreeItem: React.FC<TreeItemProps> = ({\n node,\n depth,\n selectedPath,\n expanded,\n onToggle,\n onSelect,\n onHover,\n onLeave,\n}) => {\n const isOpen = expanded.has(node.path);\n const isSelected = selectedPath === node.path;\n const pad = { paddingLeft: `${depth * 14 + 10}px` };\n const handleEnter = (e: React.MouseEvent<HTMLElement>) => {\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n onHover(node.name || '/', rect);\n };\n\n if (node.isDir) {\n return (\n <>\n <button\n type=\"button\"\n onClick={() => onToggle(node.path)}\n onMouseEnter={handleEnter}\n onMouseLeave={onLeave}\n className=\"rfp-w-full rfp-flex rfp-items-center rfp-gap-1.5 rfp-py-1.5 rfp-pr-2 rfp-text-left rfp-text-fg-secondary hover:rfp-bg-surface-1 rfp-text-sm\"\n style={pad}\n >\n <ChevronRight\n className={`rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0 rfp-transition-transform ${\n isOpen ? 'rfp-rotate-90' : ''\n }`}\n />\n {isOpen ? (\n <FolderOpen className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80\" />\n ) : (\n <Folder className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80\" />\n )}\n <span className=\"rfp-truncate rfp-flex-1 rfp-min-w-0\">{node.name || '/'}</span>\n </button>\n {isOpen &&\n node.children?.map((child) => (\n <TreeItem\n key={child.path}\n node={child}\n depth={depth + 1}\n selectedPath={selectedPath}\n expanded={expanded}\n onToggle={onToggle}\n onSelect={onSelect}\n onHover={onHover}\n onLeave={onLeave}\n />\n ))}\n </>\n );\n }\n\n const Icon = resolveIcon(node.name);\n\n return (\n <button\n type=\"button\"\n onClick={() => onSelect(node)}\n onMouseEnter={handleEnter}\n onMouseLeave={onLeave}\n className={`rfp-w-full rfp-flex rfp-items-center rfp-gap-1.5 rfp-py-1.5 rfp-pr-2 rfp-text-left rfp-text-sm ${\n isSelected ? 'rfp-bg-surface-2 rfp-text-fg-primary' : 'rfp-text-fg-secondary hover:rfp-bg-surface-1'\n }`}\n style={pad}\n >\n <span className=\"rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0\" />\n <Icon className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-fg-tertiary\" />\n <span className=\"rfp-flex-1 rfp-truncate rfp-min-w-0\">{node.name}</span>\n <span className=\"rfp-text-xs rfp-text-fg-disabled rfp-flex-shrink-0 rfp-ml-2\">\n {formatFileSize(node.size)}\n </span>\n </button>\n );\n};\n\n// ---------- Main Zip Renderer ----------\n\nexport const ZipRenderer = forwardRef<RendererHandle, ZipRendererProps>(({ url, nestingDepth = 0, onStatsChange }, ref) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [zip, setZip] = useState<JSZip | null>(null);\n const [tree, setTree] = useState<ZipTreeNode | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [expanded, setExpanded] = useState<Set<string>>(new Set(['']));\n const [selected, setSelected] = useState<SelectedPreview | null>(null);\n const [previewLoading, setPreviewLoading] = useState(false);\n const [previewError, setPreviewError] = useState<string | null>(null);\n const [hoverTip, setHoverTip] = useState<HoverTipState | null>(null);\n const onStatsChangeRef = useRef(onStatsChange);\n const splitRef = useRef<ResizableSplitHandle>(null);\n\n useEffect(() => {\n onStatsChangeRef.current = onStatsChange;\n }, [onStatsChange]);\n\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!url) return;\n\n let cancelled = false;\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n const res = await fetcher(url);\n if (!res.ok) throw new Error('加载失败');\n const buf = await res.arrayBuffer();\n const z = await loadZip(buf);\n if (cancelled) return;\n const entries = listZipEntries(z);\n const root = buildZipTree(entries);\n setZip(z);\n setTree(root);\n const init = new Set<string>(['']);\n if (root.children) {\n for (const c of root.children) if (c.isDir) init.add(c.path);\n }\n setExpanded(init);\n } catch (err) {\n console.error(err);\n if (!cancelled) setError(t('zip.load_failed'));\n } finally {\n if (!cancelled) setLoading(false);\n }\n };\n load();\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n // 切换文件时回收 blob URL\n useEffect(() => {\n return () => {\n if (selected?.blobUrl) URL.revokeObjectURL(selected.blobUrl);\n };\n }, [selected]);\n\n const totalStats = useMemo<ZipToolbarStats | null>(() => {\n if (!tree) return null;\n let files = 0;\n let dirs = 0;\n let size = 0;\n const walk = (n: ZipTreeNode) => {\n if (n.isDir) {\n if (n.path) dirs++;\n n.children?.forEach(walk);\n } else {\n files++;\n size += n.size;\n }\n };\n walk(tree);\n return { files, dirs, size };\n }, [tree]);\n\n // 向外回报 stats\n useEffect(() => {\n onStatsChangeRef.current?.(totalStats);\n return () => {\n onStatsChangeRef.current?.(null);\n };\n }, [totalStats]);\n\n const handleToggle = useCallback((path: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n }, []);\n\n const handleHover = useCallback((text: string, rect: DOMRect) => {\n setHoverTip({\n text,\n x: rect.right + 8,\n y: rect.top + rect.height / 2,\n });\n }, []);\n\n const handleLeave = useCallback(() => {\n setHoverTip(null);\n }, []);\n\n const handleSelect = useCallback(\n async (node: ZipTreeNode) => {\n if (!zip || node.isDir) return;\n if (selected?.blobUrl) URL.revokeObjectURL(selected.blobUrl);\n setPreviewLoading(true);\n setPreviewError(null);\n\n try {\n const mime = inferMimeType(node.name);\n const blob = await readZipEntryBlob(zip, node.path, mime !== 'application/octet-stream' ? mime : undefined);\n const blobUrl = URL.createObjectURL(blob);\n setSelected({ path: node.path, name: node.name, size: node.size, blobUrl });\n // 移动端切换到预览 tab\n splitRef.current?.switchTab('right');\n } catch (err) {\n console.error(err);\n setPreviewError('条目读取失败');\n } finally {\n setPreviewLoading(false);\n }\n },\n [zip, selected]\n );\n\n // Memoize files 数组以避免无限重新渲染\n const previewFiles = useMemo(() => {\n if (!selected) return [];\n return [{ name: selected.name, url: selected.blobUrl, type: inferMimeType(selected.name) }];\n }, [selected]);\n\n // 暴露接口给父组件(必须在 early return 之前调用)\n useImperativeHandle(ref, () => ({\n getToolbarGroups: () => [],\n }), []);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\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 || !tree) {\n return <RendererError message={error || t('zip.parse_failed')} />;\n }\n\n // 左侧:文件树\n const leftPane = (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto\">\n {tree.children?.map((child) => (\n <TreeItem\n key={child.path}\n node={child}\n depth={0}\n selectedPath={selected?.path ?? null}\n expanded={expanded}\n onToggle={handleToggle}\n onSelect={handleSelect}\n onHover={handleHover}\n onLeave={handleLeave}\n />\n ))}\n </div>\n );\n\n // 右侧:预览区\n const rightPane = (\n <div className=\"rfp-w-full rfp-h-full rfp-flex rfp-flex-col\">\n {!selected && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-muted rfp-text-sm rfp-p-6\">\n 从左侧选择一个文件以预览\n </div>\n )}\n {selected && previewLoading && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center\">\n <div className=\"rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n {selected && !previewLoading && previewError && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-secondary\">\n {previewError}\n </div>\n )}\n {selected && !previewLoading && !previewError && (\n <>\n <div className=\"rfp-flex-1 rfp-min-h-0 rfp-overflow-hidden rfp-flex rfp-relative rfp-z-0\">\n <Suspense\n fallback={\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center\">\n <div className=\"rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n }\n >\n <LazyFilePreviewContent\n mode=\"embed\"\n files={previewFiles}\n currentIndex={0}\n zipNestingDepth={nestingDepth + 1}\n />\n </Suspense>\n </div>\n </>\n )}\n </div>\n );\n\n return (\n <>\n <ResizableSplit\n ref={splitRef}\n left={leftPane}\n right={rightPane}\n initialLeftWidth={280}\n minLeftWidth={180}\n maxLeftWidth={560}\n storageKey=\"rfp-zip-split-left\"\n mobileTabMode\n leftTabLabel=\"文件树\"\n rightTabLabel=\"预览\"\n />\n {/* 文件名 hover tooltip(portal 到 body,避免被滚动区裁剪) */}\n {hoverTip &&\n typeof document !== 'undefined' &&\n createPortal(\n <div\n className=\"rfp-fixed rfp-z-[9999] rfp-pointer-events-none rfp-px-2 rfp-py-1 rfp-bg-[rgba(0,0,0,0.85)] rfp-text-fg-primary rfp-text-xs rfp-rounded rfp-whitespace-nowrap rfp-shadow-lg\"\n style={{\n left: `${hoverTip.x}px`,\n top: `${hoverTip.y}px`,\n transform: 'translateY(-50%)',\n }}\n >\n {hoverTip.text}\n </div>,\n document.body\n )}\n </>\n );\n});\n"],"names":["ResizableSplit","forwardRef","left","right","initialLeftWidth","minLeftWidth","maxLeftWidth","minRightWidth","storageKey","desktopMedia","className","mobileTabMode","leftTabLabel","rightTabLabel","ref","containerRef","useRef","leftWidth","setLeftWidth","useState","saved","dragging","setDragging","isDesktop","setIsDesktop","activeTab","setActiveTab","useImperativeHandle","tab","useEffect","mq","handler","handleMove","e","rect","x","cap","effectiveMax","newW","handleUp","prevCursor","prevSelect","handleDividerDown","useCallback","jsxs","jsx","LazyFilePreviewContent","lazy","n","m","resolveIcon","name","ft","getFileType","FileImage","FileText","FileCode","FileIcon","TreeItem","node","depth","selectedPath","expanded","onToggle","onSelect","onHover","onLeave","isOpen","isSelected","pad","handleEnter","Fragment","ChevronRight","FolderOpen","Folder","_a","child","Icon","formatFileSize","ZipRenderer","url","nestingDepth","onStatsChange","t","useTranslator","fetcher","useFetcher","zip","setZip","tree","setTree","loading","setLoading","error","setError","setExpanded","selected","setSelected","previewLoading","setPreviewLoading","previewError","setPreviewError","hoverTip","setHoverTip","onStatsChangeRef","splitRef","cancelled","res","buf","z","loadZip","entries","listZipEntries","root","buildZipTree","init","c","err","totalStats","useMemo","files","dirs","size","walk","handleToggle","path","prev","next","handleHover","text","handleLeave","handleSelect","mime","inferMimeType","blob","readZipEntryBlob","blobUrl","previewFiles","RendererError","leftPane","rightPane","Suspense","createPortal"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-BvBldLaU.mjs","sources":["../../src/components/ResizableSplit.tsx","../../src/renderers/Zip/index.tsx"],"sourcesContent":["import React, { useEffect, useImperativeHandle, useRef, useState, useCallback, forwardRef } from 'react';\n\nexport interface ResizableSplitProps {\n /** 左侧内容 */\n left: React.ReactNode;\n /** 右侧内容 */\n right: React.ReactNode;\n /** 左侧初始宽度(px);传入 storageKey 时会从 localStorage 读取 */\n initialLeftWidth?: number;\n /** 左侧最小宽度(px) */\n minLeftWidth?: number;\n /** 左侧最大宽度(px),同时不超过 `容器宽 - minRightWidth - 分隔线宽` */\n maxLeftWidth?: number;\n /** 右侧至少保留的宽度(px) */\n minRightWidth?: number;\n /** localStorage 持久化 key;不传则不持久化 */\n storageKey?: string;\n /** 启用横向拖动的媒体查询,默认 `(min-width: 768px)` */\n desktopMedia?: string;\n /** 容器额外类名 */\n className?: string;\n /** 移动端使用 Tab 切换而非上下堆叠 */\n mobileTabMode?: boolean;\n /** Tab 模式下左侧标题 */\n leftTabLabel?: string;\n /** Tab 模式下右侧标题 */\n rightTabLabel?: string;\n}\n\nexport interface ResizableSplitHandle {\n switchTab: (tab: 'left' | 'right') => void;\n}\n\n/**\n * 通用可拖动分隔布局:\n * - 桌面端(由 `desktopMedia` 判定)横向分两栏,中间分隔线可左右拖动调整左栏宽度\n * - 移动端默认退化为上下堆叠(不显示分隔线)\n * - 设置 `mobileTabMode` 时移动端使用 Tab 切换显示\n * - 可选 `storageKey` 将宽度持久化到 localStorage\n */\nexport const ResizableSplit = forwardRef<ResizableSplitHandle, ResizableSplitProps>(({\n left,\n right,\n initialLeftWidth = 280,\n minLeftWidth = 160,\n maxLeftWidth = 640,\n minRightWidth = 200,\n storageKey,\n desktopMedia = '(min-width: 768px)',\n className = '',\n mobileTabMode = false,\n leftTabLabel = '文件树',\n rightTabLabel = '预览',\n}, ref) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [leftWidth, setLeftWidth] = useState<number>(() => {\n if (storageKey && typeof window !== 'undefined') {\n const saved = Number(window.localStorage.getItem(storageKey));\n if (!isNaN(saved) && saved > 0) return saved;\n }\n return initialLeftWidth;\n });\n const [dragging, setDragging] = useState(false);\n const [isDesktop, setIsDesktop] = useState(false);\n const [activeTab, setActiveTab] = useState<'left' | 'right'>('left');\n\n useImperativeHandle(ref, () => ({\n switchTab: (tab) => setActiveTab(tab),\n }), []);\n\n // 响应式:监听媒体查询\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mq = window.matchMedia(desktopMedia);\n const handler = () => setIsDesktop(mq.matches);\n handler();\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, [desktopMedia]);\n\n // 拖动\n useEffect(() => {\n if (!dragging) return;\n const handleMove = (e: MouseEvent) => {\n if (!containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const cap = rect.width - minRightWidth - 6;\n const effectiveMax = Math.min(maxLeftWidth, cap);\n const newW = Math.max(minLeftWidth, Math.min(effectiveMax, x));\n setLeftWidth(newW);\n };\n const handleUp = () => setDragging(false);\n window.addEventListener('mousemove', handleMove);\n window.addEventListener('mouseup', handleUp);\n const prevCursor = document.body.style.cursor;\n const prevSelect = document.body.style.userSelect;\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n return () => {\n window.removeEventListener('mousemove', handleMove);\n window.removeEventListener('mouseup', handleUp);\n document.body.style.cursor = prevCursor;\n document.body.style.userSelect = prevSelect;\n };\n }, [dragging, minLeftWidth, maxLeftWidth, minRightWidth]);\n\n // 持久化\n useEffect(() => {\n if (!storageKey || dragging) return;\n try {\n window.localStorage.setItem(storageKey, String(leftWidth));\n } catch {\n // ignore\n }\n }, [leftWidth, storageKey, dragging]);\n\n const handleDividerDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n setDragging(true);\n }, []);\n\n // 移动端 Tab 模式\n if (mobileTabMode && !isDesktop) {\n return (\n <div\n ref={containerRef}\n className={`rfp-w-full rfp-h-full rfp-flex rfp-flex-col rfp-min-h-0 rfp-min-w-0 ${className}`}\n >\n <div className=\"rfp-flex rfp-flex-shrink-0 rfp-border-b rfp-border-line-weak rfp-bg-surface-toolbar\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('left')}\n className={`rfp-flex-1 rfp-py-2.5 rfp-text-sm rfp-transition-colors ${\n activeTab === 'left'\n ? 'rfp-text-fg-primary rfp-border-b-2 rfp-border-fg-primary -rfp-mb-px'\n : 'rfp-text-fg-secondary'\n }`}\n >\n {leftTabLabel}\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('right')}\n className={`rfp-flex-1 rfp-py-2.5 rfp-text-sm rfp-transition-colors ${\n activeTab === 'right'\n ? 'rfp-text-fg-primary rfp-border-b-2 rfp-border-fg-primary -rfp-mb-px'\n : 'rfp-text-fg-secondary'\n }`}\n >\n {rightTabLabel}\n </button>\n </div>\n <div\n className=\"rfp-flex-1 rfp-min-h-0 rfp-min-w-0 rfp-w-full rfp-overflow-hidden\"\n style={{ display: activeTab === 'left' ? undefined : 'none' }}\n >\n {left}\n </div>\n <div\n className=\"rfp-flex-1 rfp-min-h-0 rfp-min-w-0 rfp-w-full rfp-overflow-hidden\"\n style={{ display: activeTab === 'right' ? undefined : 'none' }}\n >\n {right}\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={`rfp-w-full rfp-h-full rfp-flex rfp-flex-col md:rfp-flex-row rfp-min-h-0 rfp-min-w-0 ${className}`}\n >\n <div\n className=\"rfp-min-h-0 rfp-min-w-0 rfp-flex-shrink-0 rfp-w-full rfp-max-h-60 md:rfp-h-full md:rfp-max-h-none\"\n style={isDesktop ? { width: `${leftWidth}px` } : undefined}\n >\n {left}\n </div>\n {/* 分隔线:仅桌面显示 */}\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n onMouseDown={handleDividerDown}\n className={`rfp-hidden md:rfp-block rfp-relative rfp-w-1.5 rfp-flex-shrink-0 rfp-cursor-col-resize rfp-transition-colors ${\n dragging ? 'rfp-bg-surface-toolbar' : 'rfp-bg-surface-2 hover:rfp-bg-surface-3'\n }`}\n >\n {/* 加宽命中区,改善拖动体验 */}\n <span className=\"rfp-absolute rfp-inset-y-0 -rfp-left-1 -rfp-right-1\" />\n </div>\n <div className=\"rfp-flex-1 rfp-min-w-0 rfp-min-h-0 rfp-overflow-hidden\">{right}</div>\n </div>\n );\n});\n\nResizableSplit.displayName = 'ResizableSplit';\n","import { useState, useEffect, useMemo, useCallback, useRef, lazy, Suspense, forwardRef, useImperativeHandle } from 'react';\nimport React from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n Folder,\n FolderOpen,\n FileText,\n FileImage,\n FileCode,\n File as FileIcon,\n ChevronRight,\n} from 'lucide-react';\nimport { RendererError } from '../RendererError';\nimport type { RendererHandle } from '../base.types';\nimport type JSZip from 'jszip';\nimport {\n loadZip,\n listZipEntries,\n buildZipTree,\n readZipEntryBlob,\n formatFileSize,\n getFileType,\n inferMimeType,\n type ZipTreeNode,\n} from '@eternalheart/file-preview-core';\nimport { ResizableSplit, type ResizableSplitHandle } from '../../components/ResizableSplit';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\n\nexport interface ZipToolbarStats {\n files: number;\n dirs: number;\n size: number;\n}\n\n// 懒加载 FilePreviewContent 以打破循环依赖\nconst LazyFilePreviewContent = lazy(() =>\n import('../../FilePreviewContent').then(m => ({ default: m.FilePreviewContent }))\n);\n\ninterface ZipRendererProps {\n url: string;\n /** ZIP 嵌套深度(由 FilePreviewContent 传入) */\n nestingDepth?: number;\n /** 解析完成后向外回报统计信息(files / dirs / size),供工具栏展示 */\n onStatsChange?: (stats: ZipToolbarStats | null) => void;\n}\n\ninterface SelectedPreview {\n path: string;\n name: string;\n size: number;\n blobUrl: string;\n}\n\n/** 根据文件类型返回树节点图标 */\nconst resolveIcon = (name: string) => {\n const ft = getFileType({ id: '', name, url: '', type: '' });\n if (ft === 'image') return FileImage;\n if (ft === 'text' || ft === 'markdown' || ft === 'json' || ft === 'csv' || ft === 'xml' || ft === 'subtitle') {\n return name.endsWith('.md') || name.endsWith('.markdown') ? FileText : FileCode;\n }\n return FileIcon;\n};\n\n// ---------- Tooltip via portal ----------\n\ninterface HoverTipState {\n text: string;\n x: number;\n y: number;\n}\n\n// ---------- Tree item ----------\n\ninterface TreeItemProps {\n node: ZipTreeNode;\n depth: number;\n selectedPath: string | null;\n expanded: Set<string>;\n onToggle: (path: string) => void;\n onSelect: (node: ZipTreeNode) => void;\n onHover: (text: string, rect: DOMRect) => void;\n onLeave: () => void;\n}\n\nconst TreeItem: React.FC<TreeItemProps> = ({\n node,\n depth,\n selectedPath,\n expanded,\n onToggle,\n onSelect,\n onHover,\n onLeave,\n}) => {\n const isOpen = expanded.has(node.path);\n const isSelected = selectedPath === node.path;\n const pad = { paddingLeft: `${depth * 14 + 10}px` };\n const handleEnter = (e: React.MouseEvent<HTMLElement>) => {\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n onHover(node.name || '/', rect);\n };\n\n if (node.isDir) {\n return (\n <>\n <button\n type=\"button\"\n onClick={() => onToggle(node.path)}\n onMouseEnter={handleEnter}\n onMouseLeave={onLeave}\n className=\"rfp-w-full rfp-flex rfp-items-center rfp-gap-1.5 rfp-py-1.5 rfp-pr-2 rfp-text-left rfp-text-fg-secondary hover:rfp-bg-surface-1 rfp-text-sm\"\n style={pad}\n >\n <ChevronRight\n className={`rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0 rfp-transition-transform ${\n isOpen ? 'rfp-rotate-90' : ''\n }`}\n />\n {isOpen ? (\n <FolderOpen className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80\" />\n ) : (\n <Folder className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80\" />\n )}\n <span className=\"rfp-truncate rfp-flex-1 rfp-min-w-0\">{node.name || '/'}</span>\n </button>\n {isOpen &&\n node.children?.map((child) => (\n <TreeItem\n key={child.path}\n node={child}\n depth={depth + 1}\n selectedPath={selectedPath}\n expanded={expanded}\n onToggle={onToggle}\n onSelect={onSelect}\n onHover={onHover}\n onLeave={onLeave}\n />\n ))}\n </>\n );\n }\n\n const Icon = resolveIcon(node.name);\n\n return (\n <button\n type=\"button\"\n onClick={() => onSelect(node)}\n onMouseEnter={handleEnter}\n onMouseLeave={onLeave}\n className={`rfp-w-full rfp-flex rfp-items-center rfp-gap-1.5 rfp-py-1.5 rfp-pr-2 rfp-text-left rfp-text-sm ${\n isSelected ? 'rfp-bg-surface-2 rfp-text-fg-primary' : 'rfp-text-fg-secondary hover:rfp-bg-surface-1'\n }`}\n style={pad}\n >\n <span className=\"rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0\" />\n <Icon className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-fg-tertiary\" />\n <span className=\"rfp-flex-1 rfp-truncate rfp-min-w-0\">{node.name}</span>\n <span className=\"rfp-text-xs rfp-text-fg-disabled rfp-flex-shrink-0 rfp-ml-2\">\n {formatFileSize(node.size)}\n </span>\n </button>\n );\n};\n\n// ---------- Main Zip Renderer ----------\n\nexport const ZipRenderer = forwardRef<RendererHandle, ZipRendererProps>(({ url, nestingDepth = 0, onStatsChange }, ref) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [zip, setZip] = useState<JSZip | null>(null);\n const [tree, setTree] = useState<ZipTreeNode | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [expanded, setExpanded] = useState<Set<string>>(new Set(['']));\n const [selected, setSelected] = useState<SelectedPreview | null>(null);\n const [previewLoading, setPreviewLoading] = useState(false);\n const [previewError, setPreviewError] = useState<string | null>(null);\n const [hoverTip, setHoverTip] = useState<HoverTipState | null>(null);\n const onStatsChangeRef = useRef(onStatsChange);\n const splitRef = useRef<ResizableSplitHandle>(null);\n\n useEffect(() => {\n onStatsChangeRef.current = onStatsChange;\n }, [onStatsChange]);\n\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!url) return;\n\n let cancelled = false;\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n const res = await fetcher(url);\n if (!res.ok) throw new Error('加载失败');\n const buf = await res.arrayBuffer();\n const z = await loadZip(buf);\n if (cancelled) return;\n const entries = listZipEntries(z);\n const root = buildZipTree(entries);\n setZip(z);\n setTree(root);\n const init = new Set<string>(['']);\n if (root.children) {\n for (const c of root.children) if (c.isDir) init.add(c.path);\n }\n setExpanded(init);\n } catch (err) {\n console.error(err);\n if (!cancelled) setError(t('zip.load_failed'));\n } finally {\n if (!cancelled) setLoading(false);\n }\n };\n load();\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n // 切换文件时回收 blob URL\n useEffect(() => {\n return () => {\n if (selected?.blobUrl) URL.revokeObjectURL(selected.blobUrl);\n };\n }, [selected]);\n\n const totalStats = useMemo<ZipToolbarStats | null>(() => {\n if (!tree) return null;\n let files = 0;\n let dirs = 0;\n let size = 0;\n const walk = (n: ZipTreeNode) => {\n if (n.isDir) {\n if (n.path) dirs++;\n n.children?.forEach(walk);\n } else {\n files++;\n size += n.size;\n }\n };\n walk(tree);\n return { files, dirs, size };\n }, [tree]);\n\n // 向外回报 stats\n useEffect(() => {\n onStatsChangeRef.current?.(totalStats);\n return () => {\n onStatsChangeRef.current?.(null);\n };\n }, [totalStats]);\n\n const handleToggle = useCallback((path: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n }, []);\n\n const handleHover = useCallback((text: string, rect: DOMRect) => {\n setHoverTip({\n text,\n x: rect.right + 8,\n y: rect.top + rect.height / 2,\n });\n }, []);\n\n const handleLeave = useCallback(() => {\n setHoverTip(null);\n }, []);\n\n const handleSelect = useCallback(\n async (node: ZipTreeNode) => {\n if (!zip || node.isDir) return;\n if (selected?.blobUrl) URL.revokeObjectURL(selected.blobUrl);\n setPreviewLoading(true);\n setPreviewError(null);\n\n try {\n const mime = inferMimeType(node.name);\n const blob = await readZipEntryBlob(zip, node.path, mime !== 'application/octet-stream' ? mime : undefined);\n const blobUrl = URL.createObjectURL(blob);\n setSelected({ path: node.path, name: node.name, size: node.size, blobUrl });\n // 移动端切换到预览 tab\n splitRef.current?.switchTab('right');\n } catch (err) {\n console.error(err);\n setPreviewError('条目读取失败');\n } finally {\n setPreviewLoading(false);\n }\n },\n [zip, selected]\n );\n\n // Memoize files 数组以避免无限重新渲染\n const previewFiles = useMemo(() => {\n if (!selected) return [];\n return [{ name: selected.name, url: selected.blobUrl, type: inferMimeType(selected.name) }];\n }, [selected]);\n\n // 暴露接口给父组件(必须在 early return 之前调用)\n useImperativeHandle(ref, () => ({\n getToolbarGroups: () => [],\n }), []);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\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 || !tree) {\n return <RendererError message={error || t('zip.parse_failed')} />;\n }\n\n // 左侧:文件树\n const leftPane = (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto\">\n {tree.children?.map((child) => (\n <TreeItem\n key={child.path}\n node={child}\n depth={0}\n selectedPath={selected?.path ?? null}\n expanded={expanded}\n onToggle={handleToggle}\n onSelect={handleSelect}\n onHover={handleHover}\n onLeave={handleLeave}\n />\n ))}\n </div>\n );\n\n // 右侧:预览区\n const rightPane = (\n <div className=\"rfp-w-full rfp-h-full rfp-flex rfp-flex-col\">\n {!selected && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-muted rfp-text-sm rfp-p-6\">\n 从左侧选择一个文件以预览\n </div>\n )}\n {selected && previewLoading && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center\">\n <div className=\"rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n {selected && !previewLoading && previewError && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-secondary\">\n {previewError}\n </div>\n )}\n {selected && !previewLoading && !previewError && (\n <>\n <div className=\"rfp-flex-1 rfp-min-h-0 rfp-overflow-hidden rfp-flex rfp-relative rfp-z-0\">\n <Suspense\n fallback={\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center\">\n <div className=\"rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n }\n >\n <LazyFilePreviewContent\n mode=\"embed\"\n files={previewFiles}\n currentIndex={0}\n zipNestingDepth={nestingDepth + 1}\n />\n </Suspense>\n </div>\n </>\n )}\n </div>\n );\n\n return (\n <>\n <ResizableSplit\n ref={splitRef}\n left={leftPane}\n right={rightPane}\n initialLeftWidth={280}\n minLeftWidth={180}\n maxLeftWidth={560}\n storageKey=\"rfp-zip-split-left\"\n mobileTabMode\n leftTabLabel=\"文件树\"\n rightTabLabel=\"预览\"\n />\n {/* 文件名 hover tooltip(portal 到 body,避免被滚动区裁剪) */}\n {hoverTip &&\n typeof document !== 'undefined' &&\n createPortal(\n <div\n className=\"rfp-fixed rfp-z-[9999] rfp-pointer-events-none rfp-px-2 rfp-py-1 rfp-bg-[rgba(0,0,0,0.85)] rfp-text-fg-primary rfp-text-xs rfp-rounded rfp-whitespace-nowrap rfp-shadow-lg\"\n style={{\n left: `${hoverTip.x}px`,\n top: `${hoverTip.y}px`,\n transform: 'translateY(-50%)',\n }}\n >\n {hoverTip.text}\n </div>,\n document.body\n )}\n </>\n );\n});\n"],"names":["ResizableSplit","forwardRef","left","right","initialLeftWidth","minLeftWidth","maxLeftWidth","minRightWidth","storageKey","desktopMedia","className","mobileTabMode","leftTabLabel","rightTabLabel","ref","containerRef","useRef","leftWidth","setLeftWidth","useState","saved","dragging","setDragging","isDesktop","setIsDesktop","activeTab","setActiveTab","useImperativeHandle","tab","useEffect","mq","handler","handleMove","e","rect","x","cap","effectiveMax","newW","handleUp","prevCursor","prevSelect","handleDividerDown","useCallback","jsxs","jsx","LazyFilePreviewContent","lazy","n","m","resolveIcon","name","ft","getFileType","FileImage","FileText","FileCode","FileIcon","TreeItem","node","depth","selectedPath","expanded","onToggle","onSelect","onHover","onLeave","isOpen","isSelected","pad","handleEnter","Fragment","ChevronRight","FolderOpen","Folder","_a","child","Icon","formatFileSize","ZipRenderer","url","nestingDepth","onStatsChange","t","useTranslator","fetcher","useFetcher","zip","setZip","tree","setTree","loading","setLoading","error","setError","setExpanded","selected","setSelected","previewLoading","setPreviewLoading","previewError","setPreviewError","hoverTip","setHoverTip","onStatsChangeRef","splitRef","cancelled","res","buf","z","loadZip","entries","listZipEntries","root","buildZipTree","init","c","err","totalStats","useMemo","files","dirs","size","walk","handleToggle","path","prev","next","handleHover","text","handleLeave","handleSelect","mime","inferMimeType","blob","readZipEntryBlob","blobUrl","previewFiles","RendererError","leftPane","rightPane","Suspense","createPortal"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as L, jsxs as H } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef as tr, useState as qe, useEffect as rr, useImperativeHandle as ir } from "react";
|
|
3
3
|
import { User as nr, Users as ze, Calendar as mt, Clock as gt, Tag as wt, Mail as ar, Paperclip as sr, Hash as or } from "lucide-react";
|
|
4
|
-
import { u as cr, a as fr } from "./index-
|
|
4
|
+
import { u as cr, a as fr } from "./index-B3jtj_7-.mjs";
|
|
5
5
|
import { R as ur } from "./RendererError-D5i8eSpN.mjs";
|
|
6
6
|
var ne = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
|
|
7
7
|
function hr(e) {
|
|
@@ -13096,4 +13096,4 @@ const se = {
|
|
|
13096
13096
|
export {
|
|
13097
13097
|
Li as MsgRenderer
|
|
13098
13098
|
};
|
|
13099
|
-
//# sourceMappingURL=index-
|
|
13099
|
+
//# sourceMappingURL=index-C6eeg9nN.mjs.map
|