@eternalheart/react-file-preview 1.3.14 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/lib/FilePreviewContent.d.ts +3 -17
  2. package/lib/FilePreviewContent.d.ts.map +1 -1
  3. package/lib/chunks/RendererError-D5i8eSpN.mjs +15 -0
  4. package/lib/chunks/RendererError-D5i8eSpN.mjs.map +1 -0
  5. package/lib/chunks/{index-B_7NPlPG.mjs → index-0v5STX5f.mjs} +18 -18
  6. package/lib/chunks/index-0v5STX5f.mjs.map +1 -0
  7. package/lib/chunks/{index-d8Bt4gIX.mjs → index-10O8tfTH.mjs} +7 -7
  8. package/lib/chunks/index-10O8tfTH.mjs.map +1 -0
  9. package/lib/chunks/{index-CaobN7Im.mjs → index-BCyv1HM9.mjs} +6 -6
  10. package/lib/chunks/index-BCyv1HM9.mjs.map +1 -0
  11. package/lib/chunks/{index-Go2oJfny.mjs → index-Bo90aGhy.mjs} +3 -3
  12. package/lib/chunks/{index-Go2oJfny.mjs.map → index-Bo90aGhy.mjs.map} +1 -1
  13. package/lib/chunks/index-CEeKt7L3.mjs +2808 -0
  14. package/lib/chunks/index-CEeKt7L3.mjs.map +1 -0
  15. package/lib/chunks/{index-B05UpMZC.mjs → index-CWKbnvW6.mjs} +3 -3
  16. package/lib/chunks/{index-B05UpMZC.mjs.map → index-CWKbnvW6.mjs.map} +1 -1
  17. package/lib/chunks/{index-DCGk-moA.mjs → index-C_BJatqr.mjs} +14 -14
  18. package/lib/chunks/index-C_BJatqr.mjs.map +1 -0
  19. package/lib/chunks/index-Cbz5Z6ZK.mjs +263 -0
  20. package/lib/chunks/index-Cbz5Z6ZK.mjs.map +1 -0
  21. package/lib/chunks/{index-D_cBflBv.mjs → index-Cp68OevR.mjs} +3 -3
  22. package/lib/chunks/{index-D_cBflBv.mjs.map → index-Cp68OevR.mjs.map} +1 -1
  23. package/lib/chunks/{index-BfzV7KIz.mjs → index-CzM2mxrD.mjs} +4 -4
  24. package/lib/chunks/{index-BfzV7KIz.mjs.map → index-CzM2mxrD.mjs.map} +1 -1
  25. package/lib/chunks/{index-DMmb2rBE.mjs → index-DTYBFuAH.mjs} +4 -4
  26. package/lib/chunks/{index-DMmb2rBE.mjs.map → index-DTYBFuAH.mjs.map} +1 -1
  27. package/lib/chunks/{index-BTLV1YqJ.mjs → index-DaAXRBWL.mjs} +3 -3
  28. package/lib/chunks/{index-BTLV1YqJ.mjs.map → index-DaAXRBWL.mjs.map} +1 -1
  29. package/lib/chunks/{index-BG3Idu38.mjs → index-DoFsoBKL.mjs} +3 -3
  30. package/lib/chunks/{index-BG3Idu38.mjs.map → index-DoFsoBKL.mjs.map} +1 -1
  31. package/lib/chunks/{index-DEzF8C7L.mjs → index-DuP0Tlpo.mjs} +3 -3
  32. package/lib/chunks/{index-DEzF8C7L.mjs.map → index-DuP0Tlpo.mjs.map} +1 -1
  33. package/lib/chunks/index-Dv3RQz86.mjs +270 -0
  34. package/lib/chunks/index-Dv3RQz86.mjs.map +1 -0
  35. package/lib/chunks/{index-Ch7DqyC4.mjs → index-QfpHck8N.mjs} +5 -5
  36. package/lib/chunks/{index-Ch7DqyC4.mjs.map → index-QfpHck8N.mjs.map} +1 -1
  37. package/lib/chunks/{index-BaU-yih3.mjs → index-gjSQeou7.mjs} +3 -3
  38. package/lib/chunks/{index-BaU-yih3.mjs.map → index-gjSQeou7.mjs.map} +1 -1
  39. package/lib/chunks/{index-Br8WHz8e.mjs → index-kALp0tqz.mjs} +3 -3
  40. package/lib/chunks/{index-Br8WHz8e.mjs.map → index-kALp0tqz.mjs.map} +1 -1
  41. package/lib/chunks/{index-DKwN-YU-.mjs → index-kCeSnFs-.mjs} +9 -9
  42. package/lib/chunks/{index-DKwN-YU-.mjs.map → index-kCeSnFs-.mjs.map} +1 -1
  43. package/lib/chunks/{useShikiHighlight-DoY3TBPT.mjs → useShikiHighlight-BA9qgdGA.mjs} +2 -2
  44. package/lib/chunks/{useShikiHighlight-DoY3TBPT.mjs.map → useShikiHighlight-BA9qgdGA.mjs.map} +1 -1
  45. package/lib/components/preview/FilePreviewRenderer.d.ts +18 -0
  46. package/lib/components/preview/FilePreviewRenderer.d.ts.map +1 -0
  47. package/lib/components/preview/FilePreviewToolbar.d.ts +19 -0
  48. package/lib/components/preview/FilePreviewToolbar.d.ts.map +1 -0
  49. package/lib/components/preview/NavArrows.d.ts +18 -0
  50. package/lib/components/preview/NavArrows.d.ts.map +1 -0
  51. package/lib/components/preview/RendererError.d.ts +15 -0
  52. package/lib/components/preview/RendererError.d.ts.map +1 -0
  53. package/lib/components/preview/RendererErrorBoundary.d.ts +23 -0
  54. package/lib/components/preview/RendererErrorBoundary.d.ts.map +1 -0
  55. package/lib/components/preview/ToolbarButton.d.ts +14 -0
  56. package/lib/components/preview/ToolbarButton.d.ts.map +1 -0
  57. package/lib/components/preview/index.d.ts +7 -0
  58. package/lib/components/preview/index.d.ts.map +1 -0
  59. package/lib/hooks/index.d.ts +9 -0
  60. package/lib/hooks/index.d.ts.map +1 -0
  61. package/lib/hooks/rendererReducer.d.ts +10 -0
  62. package/lib/hooks/rendererReducer.d.ts.map +1 -0
  63. package/lib/hooks/types.d.ts +152 -0
  64. package/lib/hooks/types.d.ts.map +1 -0
  65. package/lib/hooks/useBookRenderer.d.ts +14 -0
  66. package/lib/hooks/useBookRenderer.d.ts.map +1 -0
  67. package/lib/hooks/useFilePreviewState.d.ts +10 -0
  68. package/lib/hooks/useFilePreviewState.d.ts.map +1 -0
  69. package/lib/hooks/useImageAutoFit.d.ts +13 -0
  70. package/lib/hooks/useImageAutoFit.d.ts.map +1 -0
  71. package/lib/hooks/useKeyboardNavigation.d.ts +15 -0
  72. package/lib/hooks/useKeyboardNavigation.d.ts.map +1 -0
  73. package/lib/hooks/useThemeMode.d.ts +7 -0
  74. package/lib/hooks/useThemeMode.d.ts.map +1 -0
  75. package/lib/hooks/useToolbarConfig.d.ts +25 -0
  76. package/lib/hooks/useToolbarConfig.d.ts.map +1 -0
  77. package/lib/index.cjs +30 -30
  78. package/lib/index.cjs.map +1 -1
  79. package/lib/index.css +1 -1
  80. package/lib/index.mjs +1 -1
  81. package/lib/renderers/Epub/index.d.ts.map +1 -1
  82. package/lib/renderers/Markdown/index.d.ts.map +1 -1
  83. package/lib/renderers/Pdf/index.d.ts +2 -0
  84. package/lib/renderers/Pdf/index.d.ts.map +1 -1
  85. package/lib/renderers/Pdf/toolbar.d.ts +5 -0
  86. package/lib/renderers/Pdf/toolbar.d.ts.map +1 -1
  87. package/lib/renderers/toolbar.types.d.ts +1 -0
  88. package/lib/renderers/toolbar.types.d.ts.map +1 -1
  89. package/lib/toolbar/registry.d.ts +51 -0
  90. package/lib/toolbar/registry.d.ts.map +1 -0
  91. package/lib/toolbar/renderItems.d.ts +8 -0
  92. package/lib/toolbar/renderItems.d.ts.map +1 -0
  93. package/package.json +1 -1
  94. package/lib/chunks/RendererError-BH6fzLrN.mjs +0 -15
  95. package/lib/chunks/RendererError-BH6fzLrN.mjs.map +0 -1
  96. package/lib/chunks/index--lXiT1Y_.mjs +0 -2325
  97. package/lib/chunks/index--lXiT1Y_.mjs.map +0 -1
  98. package/lib/chunks/index-B_7NPlPG.mjs.map +0 -1
  99. package/lib/chunks/index-CaobN7Im.mjs.map +0 -1
  100. package/lib/chunks/index-DCGk-moA.mjs.map +0 -1
  101. package/lib/chunks/index-Dq-90KbM.mjs +0 -240
  102. package/lib/chunks/index-Dq-90KbM.mjs.map +0 -1
  103. package/lib/chunks/index-d8Bt4gIX.mjs.map +0 -1
  104. package/lib/chunks/index-jHf5E4be.mjs +0 -161
  105. package/lib/chunks/index-jHf5E4be.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index-BaU-yih3.mjs","sources":["../../src/renderers/Mobi/index.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n useCallback,\n useImperativeHandle,\n forwardRef,\n} from 'react';\nimport { X } from 'lucide-react';\nimport 'foliate-js/view.js';\nimport type { FoliateView, TocItem } from 'foliate-js/view.js';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\nconst READER_CSS = `\n @namespace epub \"http://www.idpf.org/2007/ops\";\n html { color-scheme: light; }\n body {\n background: #ffffff !important;\n color: #1a1a1a !important;\n font-family: \"Noto Serif SC\", \"Source Han Serif SC\", Georgia, \"Times New Roman\", serif !important;\n font-size: 16px !important;\n line-height: 2 !important;\n max-width: 100% !important;\n box-sizing: border-box !important;\n word-break: break-word !important;\n overflow-wrap: break-word !important;\n }\n p, li, blockquote, dd { line-height: 2; text-align: justify; }\n p { text-indent: 2em; margin: 0.8em 0; }\n h1 { text-align: center; margin: 1.5em 0 1em; }\n h2 { margin: 1.2em 0 0.8em; }\n h3 { margin: 1em 0 0.6em; }\n img { max-width: 100% !important; height: auto !important; }\n a { color: #2563eb; text-decoration: none; }\n pre { white-space: pre-wrap !important; }\n`;\n\nconst A4_WIDTH = 794;\n\nexport interface MobiRendererHandle {\n prevPage: () => void;\n nextPage: () => void;\n toggleFullWidth: () => void;\n toggleToc: () => void;\n}\n\ninterface MobiRendererProps {\n url: string;\n onChapterChange?: (current: number, total: number) => void;\n onFullWidthChange?: (isFullWidth: boolean) => void;\n}\n\nexport const MobiRenderer = forwardRef<MobiRendererHandle, MobiRendererProps>(\n ({ url, onChapterChange, onFullWidthChange }, ref) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const hostRef = useRef<HTMLDivElement>(null);\n const viewRef = useRef<FoliateView | null>(null);\n const onChapterChangeRef = useRef(onChapterChange);\n const onFullWidthChangeRef = useRef(onFullWidthChange);\n onChapterChangeRef.current = onChapterChange;\n onFullWidthChangeRef.current = onFullWidthChange;\n\n // 模拟 epub.js 的 locations 显示\n const totalLocationsRef = useRef(1);\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [toc, setToc] = useState<TocItem[]>([]);\n const [showToc, setShowToc] = useState(false);\n const [activeTocHref, setActiveTocHref] = useState<string>('');\n const [isFullWidth, setIsFullWidth] = useState(false);\n const isFullWidthRef = useRef(false);\n isFullWidthRef.current = isFullWidth;\n\n const reportProgress = useCallback((current: number, total: number) => {\n if (total > 0) totalLocationsRef.current = total;\n onChapterChangeRef.current?.(Math.max(1, current + 1), totalLocationsRef.current);\n }, []);\n\n const handlePrev = useCallback(() => {\n const view = viewRef.current;\n if (!view) return;\n view.prev().catch(() => {});\n }, []);\n\n const handleNext = useCallback(() => {\n const view = viewRef.current;\n if (!view) return;\n view.next().catch(() => {});\n }, []);\n\n const toggleToc = useCallback(() => setShowToc((prev) => !prev), []);\n\n const toggleFullWidth = useCallback(() => {\n const newVal = !isFullWidthRef.current;\n setIsFullWidth(newVal);\n onFullWidthChangeRef.current?.(newVal);\n // 宽度改变后 paginator 需要重新分页\n const view = viewRef.current;\n if (!view) return;\n const renderer = (view as unknown as { renderer?: HTMLElement }).renderer;\n if (renderer) {\n renderer.setAttribute('max-inline-size', newVal ? '9999' : '720');\n }\n }, []);\n\n const handleTocClick = useCallback((href: string) => {\n setActiveTocHref(href);\n setShowToc(false);\n viewRef.current?.goTo(href).catch(() => {});\n }, []);\n\n useImperativeHandle(ref, () => ({\n prevPage: handlePrev,\n nextPage: handleNext,\n toggleFullWidth,\n toggleToc,\n }), [handlePrev, handleNext, toggleFullWidth, toggleToc]);\n\n useEffect(() => {\n const host = hostRef.current;\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!host || !url) return;\n\n setLoading(true);\n setError(null);\n setToc([]);\n setShowToc(false);\n setActiveTocHref('');\n host.replaceChildren();\n\n let cancelled = false;\n let view: FoliateView | null = null;\n\n const load = async () => {\n try {\n if (cancelled) return;\n\n view = document.createElement('foliate-view') as FoliateView;\n host.appendChild(view);\n viewRef.current = view;\n\n // 先注册事件\n view.addEventListener('relocate', (e: Event) => {\n const detail = (e as CustomEvent).detail;\n if (!detail) return;\n // SectionProgress 返回的 location 对象: { current, next, total }\n const loc = detail.location as { current?: number; total?: number } | undefined;\n if (loc && typeof loc.current === 'number' && typeof loc.total === 'number') {\n reportProgress(loc.current, loc.total);\n } else {\n // fallback:用 section 级别估算\n const sections = viewRef.current?.book?.sections ?? [];\n const idx = detail.index ?? 0;\n const frac = detail.fraction ?? 0;\n const total = Math.max(sections.length, 1);\n const current = Math.round((idx + frac) / total * total);\n reportProgress(current, total);\n }\n const tocItem = detail.tocItem as { href?: string } | undefined;\n if (tocItem?.href) {\n setActiveTocHref(tocItem.href);\n }\n });\n\n const res = await fetcher(url);\n if (!res.ok) throw new Error(`请求失败: ${res.status}`);\n const blob = await res.blob();\n let name = 'book.mobi';\n try {\n const u = new URL(url, window.location.href);\n const base = u.pathname.split('/').pop();\n if (base) name = decodeURIComponent(base);\n } catch { /* blob: URL */ }\n const file = new File([blob], name);\n\n await view.open(file);\n if (cancelled) { view.book?.destroy?.(); return; }\n\n // 配置 paginator:paginated 模式(默认),带动画\n const renderer = (view as unknown as { renderer: HTMLElement & {\n setStyles?: (css: string) => void;\n next?: () => Promise<void>;\n } }).renderer;\n\n if (renderer) {\n // flow=\"paginated\" 是默认值,不需要显式设置\n renderer.setAttribute('animated', '');\n renderer.setAttribute('max-inline-size', '720');\n renderer.setAttribute('margin', '48');\n renderer.setAttribute('gap', '5%');\n renderer.setStyles?.(READER_CSS);\n // 必须调 next() 渲染首页\n await renderer.next?.();\n }\n\n setToc(view.book?.toc ?? []);\n setLoading(false);\n reportProgress(0, view.book?.sections.length ?? 1);\n } catch (err) {\n // MOBI/EPUB 加载错误通常是文件损坏或 DRM 保护,用 warn 级别记录\n console.warn('[MobiRenderer] Failed to load ebook:', err instanceof Error ? err.message : String(err));\n if (!cancelled) {\n setError(t('mobi.load_failed'));\n setLoading(false);\n }\n }\n };\n\n load();\n\n return () => {\n cancelled = true;\n try { viewRef.current?.book?.destroy?.(); } catch { /* ignore */ }\n viewRef.current = null;\n host.replaceChildren();\n };\n }, [url, reportProgress]);\n\n const isActive = useCallback(\n (href: string | undefined) => !!href && href === activeTocHref,\n [activeTocHref]\n );\n\n const renderTocItems = (items: TocItem[], depth = 0) => (\n <ul style={{ listStyle: 'none', padding: 0, margin: depth > 0 ? '0 0 0 16px' : 0 }}>\n {items.map((item, i) => (\n <li key={`${item.href ?? item.label}-${i}`}>\n {item.href ? (\n <button\n onClick={() => handleTocClick(item.href!)}\n className={`rfp-w-full rfp-text-left rfp-py-2 rfp-px-3 rfp-text-sm rfp-rounded rfp-transition-all rfp-truncate ${\n isActive(item.href)\n ? 'rfp-text-fg-primary rfp-bg-surface-3 rfp-font-medium'\n : 'rfp-text-fg-secondary hover:rfp-text-fg-primary hover:rfp-bg-surface-2'\n }`}\n title={item.label}\n >\n {item.label?.trim()}\n </button>\n ) : (\n <div className=\"rfp-w-full rfp-py-2 rfp-px-3 rfp-text-sm rfp-text-fg-tertiary rfp-truncate\">\n {item.label?.trim()}\n </div>\n )}\n {item.subitems && item.subitems.length > 0 && renderTocItems(item.subitems, depth + 1)}\n </li>\n ))}\n </ul>\n );\n\n return (\n <div className=\"rfp-relative rfp-w-full rfp-h-full rfp-flex rfp-justify-center rfp-bg-surface-1 rfp-overflow-hidden\">\n {error && <RendererError message={error} />}\n\n {loading && !error && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-z-10\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n\n {/* 目录侧栏 */}\n {toc.length > 0 && (\n <div\n className=\"rfp-absolute rfp-inset-0 rfp-z-20 rfp-flex rfp-transition-opacity rfp-duration-300\"\n style={{ opacity: showToc ? 1 : 0, pointerEvents: showToc ? 'auto' : 'none' }}\n >\n <div\n className=\"rfp-w-72 rfp-max-w-[80%] rfp-h-full rfp-bg-surface-overlay rfp-backdrop-blur-xl rfp-border-r rfp-border-line-weak rfp-flex rfp-flex-col rfp-shadow-2xl rfp-transition-transform rfp-duration-300\"\n style={{ transform: showToc ? 'translateX(0)' : 'translateX(-100%)' }}\n >\n <div className=\"rfp-flex rfp-items-center rfp-justify-between rfp-px-4 rfp-py-3 rfp-border-b rfp-border-line-weak rfp-flex-shrink-0\">\n <span className=\"rfp-text-fg-primary rfp-font-medium rfp-text-sm\">{t('toolbar.toc')}</span>\n <button\n onClick={() => setShowToc(false)}\n className=\"rfp-text-fg-tertiary hover:rfp-text-fg-primary rfp-transition-colors\"\n >\n <X className=\"rfp-w-4 rfp-h-4\" />\n </button>\n </div>\n <div className=\"rfp-flex-1 rfp-overflow-y-auto rfp-py-4 rfp-px-1\">\n {renderTocItems(toc)}\n </div>\n </div>\n <div\n className=\"rfp-flex-1 rfp-transition-opacity rfp-duration-300\"\n style={{ background: showToc ? 'rgba(0,0,0,0.3)' : 'transparent' }}\n onClick={() => setShowToc(false)}\n />\n </div>\n )}\n\n {!error && (\n <div\n ref={hostRef}\n className=\"rfp-h-full rfp-bg-surface-toolbar rfp-shadow-lg\"\n style={{\n width: isFullWidth ? '100%' : `${A4_WIDTH}px`,\n maxWidth: '100%',\n transition: 'width 0.3s ease',\n }}\n />\n )}\n </div>\n );\n }\n);\n\nMobiRenderer.displayName = 'MobiRenderer';\n"],"names":["READER_CSS","A4_WIDTH","MobiRenderer","forwardRef","url","onChapterChange","onFullWidthChange","ref","t","useTranslator","fetcher","useFetcher","hostRef","useRef","viewRef","onChapterChangeRef","onFullWidthChangeRef","totalLocationsRef","loading","setLoading","useState","error","setError","toc","setToc","showToc","setShowToc","activeTocHref","setActiveTocHref","isFullWidth","setIsFullWidth","isFullWidthRef","reportProgress","useCallback","current","total","_a","handlePrev","view","handleNext","toggleToc","prev","toggleFullWidth","newVal","renderer","handleTocClick","href","useImperativeHandle","useEffect","host","cancelled","e","detail","loc","sections","_b","idx","frac","tocItem","res","blob","name","base","file","_c","_d","_e","_f","err","isActive","renderTocItems","items","depth","item","i","jsxs","jsx","RendererError","X"],"mappings":"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"}
1
+ {"version":3,"file":"index-gjSQeou7.mjs","sources":["../../src/renderers/Mobi/index.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n useCallback,\n useImperativeHandle,\n forwardRef,\n} from 'react';\nimport { X } from 'lucide-react';\nimport 'foliate-js/view.js';\nimport type { FoliateView, TocItem } from 'foliate-js/view.js';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\nconst READER_CSS = `\n @namespace epub \"http://www.idpf.org/2007/ops\";\n html { color-scheme: light; }\n body {\n background: #ffffff !important;\n color: #1a1a1a !important;\n font-family: \"Noto Serif SC\", \"Source Han Serif SC\", Georgia, \"Times New Roman\", serif !important;\n font-size: 16px !important;\n line-height: 2 !important;\n max-width: 100% !important;\n box-sizing: border-box !important;\n word-break: break-word !important;\n overflow-wrap: break-word !important;\n }\n p, li, blockquote, dd { line-height: 2; text-align: justify; }\n p { text-indent: 2em; margin: 0.8em 0; }\n h1 { text-align: center; margin: 1.5em 0 1em; }\n h2 { margin: 1.2em 0 0.8em; }\n h3 { margin: 1em 0 0.6em; }\n img { max-width: 100% !important; height: auto !important; }\n a { color: #2563eb; text-decoration: none; }\n pre { white-space: pre-wrap !important; }\n`;\n\nconst A4_WIDTH = 794;\n\nexport interface MobiRendererHandle {\n prevPage: () => void;\n nextPage: () => void;\n toggleFullWidth: () => void;\n toggleToc: () => void;\n}\n\ninterface MobiRendererProps {\n url: string;\n onChapterChange?: (current: number, total: number) => void;\n onFullWidthChange?: (isFullWidth: boolean) => void;\n}\n\nexport const MobiRenderer = forwardRef<MobiRendererHandle, MobiRendererProps>(\n ({ url, onChapterChange, onFullWidthChange }, ref) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const hostRef = useRef<HTMLDivElement>(null);\n const viewRef = useRef<FoliateView | null>(null);\n const onChapterChangeRef = useRef(onChapterChange);\n const onFullWidthChangeRef = useRef(onFullWidthChange);\n onChapterChangeRef.current = onChapterChange;\n onFullWidthChangeRef.current = onFullWidthChange;\n\n // 模拟 epub.js 的 locations 显示\n const totalLocationsRef = useRef(1);\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [toc, setToc] = useState<TocItem[]>([]);\n const [showToc, setShowToc] = useState(false);\n const [activeTocHref, setActiveTocHref] = useState<string>('');\n const [isFullWidth, setIsFullWidth] = useState(false);\n const isFullWidthRef = useRef(false);\n isFullWidthRef.current = isFullWidth;\n\n const reportProgress = useCallback((current: number, total: number) => {\n if (total > 0) totalLocationsRef.current = total;\n onChapterChangeRef.current?.(Math.max(1, current + 1), totalLocationsRef.current);\n }, []);\n\n const handlePrev = useCallback(() => {\n const view = viewRef.current;\n if (!view) return;\n view.prev().catch(() => {});\n }, []);\n\n const handleNext = useCallback(() => {\n const view = viewRef.current;\n if (!view) return;\n view.next().catch(() => {});\n }, []);\n\n const toggleToc = useCallback(() => setShowToc((prev) => !prev), []);\n\n const toggleFullWidth = useCallback(() => {\n const newVal = !isFullWidthRef.current;\n setIsFullWidth(newVal);\n onFullWidthChangeRef.current?.(newVal);\n // 宽度改变后 paginator 需要重新分页\n const view = viewRef.current;\n if (!view) return;\n const renderer = (view as unknown as { renderer?: HTMLElement }).renderer;\n if (renderer) {\n renderer.setAttribute('max-inline-size', newVal ? '9999' : '720');\n }\n }, []);\n\n const handleTocClick = useCallback((href: string) => {\n setActiveTocHref(href);\n setShowToc(false);\n viewRef.current?.goTo(href).catch(() => {});\n }, []);\n\n useImperativeHandle(ref, () => ({\n prevPage: handlePrev,\n nextPage: handleNext,\n toggleFullWidth,\n toggleToc,\n }), [handlePrev, handleNext, toggleFullWidth, toggleToc]);\n\n useEffect(() => {\n const host = hostRef.current;\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!host || !url) return;\n\n setLoading(true);\n setError(null);\n setToc([]);\n setShowToc(false);\n setActiveTocHref('');\n host.replaceChildren();\n\n let cancelled = false;\n let view: FoliateView | null = null;\n\n const load = async () => {\n try {\n if (cancelled) return;\n\n view = document.createElement('foliate-view') as FoliateView;\n host.appendChild(view);\n viewRef.current = view;\n\n // 先注册事件\n view.addEventListener('relocate', (e: Event) => {\n const detail = (e as CustomEvent).detail;\n if (!detail) return;\n // SectionProgress 返回的 location 对象: { current, next, total }\n const loc = detail.location as { current?: number; total?: number } | undefined;\n if (loc && typeof loc.current === 'number' && typeof loc.total === 'number') {\n reportProgress(loc.current, loc.total);\n } else {\n // fallback:用 section 级别估算\n const sections = viewRef.current?.book?.sections ?? [];\n const idx = detail.index ?? 0;\n const frac = detail.fraction ?? 0;\n const total = Math.max(sections.length, 1);\n const current = Math.round((idx + frac) / total * total);\n reportProgress(current, total);\n }\n const tocItem = detail.tocItem as { href?: string } | undefined;\n if (tocItem?.href) {\n setActiveTocHref(tocItem.href);\n }\n });\n\n const res = await fetcher(url);\n if (!res.ok) throw new Error(`请求失败: ${res.status}`);\n const blob = await res.blob();\n let name = 'book.mobi';\n try {\n const u = new URL(url, window.location.href);\n const base = u.pathname.split('/').pop();\n if (base) name = decodeURIComponent(base);\n } catch { /* blob: URL */ }\n const file = new File([blob], name);\n\n await view.open(file);\n if (cancelled) { view.book?.destroy?.(); return; }\n\n // 配置 paginator:paginated 模式(默认),带动画\n const renderer = (view as unknown as { renderer: HTMLElement & {\n setStyles?: (css: string) => void;\n next?: () => Promise<void>;\n } }).renderer;\n\n if (renderer) {\n // flow=\"paginated\" 是默认值,不需要显式设置\n renderer.setAttribute('animated', '');\n renderer.setAttribute('max-inline-size', '720');\n renderer.setAttribute('margin', '48');\n renderer.setAttribute('gap', '5%');\n renderer.setStyles?.(READER_CSS);\n // 必须调 next() 渲染首页\n await renderer.next?.();\n }\n\n setToc(view.book?.toc ?? []);\n setLoading(false);\n reportProgress(0, view.book?.sections.length ?? 1);\n } catch (err) {\n // MOBI/EPUB 加载错误通常是文件损坏或 DRM 保护,用 warn 级别记录\n console.warn('[MobiRenderer] Failed to load ebook:', err instanceof Error ? err.message : String(err));\n if (!cancelled) {\n setError(t('mobi.load_failed'));\n setLoading(false);\n }\n }\n };\n\n load();\n\n return () => {\n cancelled = true;\n try { viewRef.current?.book?.destroy?.(); } catch { /* ignore */ }\n viewRef.current = null;\n host.replaceChildren();\n };\n }, [url, reportProgress]);\n\n const isActive = useCallback(\n (href: string | undefined) => !!href && href === activeTocHref,\n [activeTocHref]\n );\n\n const renderTocItems = (items: TocItem[], depth = 0) => (\n <ul style={{ listStyle: 'none', padding: 0, margin: depth > 0 ? '0 0 0 16px' : 0 }}>\n {items.map((item, i) => (\n <li key={`${item.href ?? item.label}-${i}`}>\n {item.href ? (\n <button\n onClick={() => handleTocClick(item.href!)}\n className={`rfp-w-full rfp-text-left rfp-py-2 rfp-px-3 rfp-text-sm rfp-rounded rfp-transition-all rfp-truncate ${\n isActive(item.href)\n ? 'rfp-text-fg-primary rfp-bg-surface-3 rfp-font-medium'\n : 'rfp-text-fg-secondary hover:rfp-text-fg-primary hover:rfp-bg-surface-2'\n }`}\n title={item.label}\n >\n {item.label?.trim()}\n </button>\n ) : (\n <div className=\"rfp-w-full rfp-py-2 rfp-px-3 rfp-text-sm rfp-text-fg-tertiary rfp-truncate\">\n {item.label?.trim()}\n </div>\n )}\n {item.subitems && item.subitems.length > 0 && renderTocItems(item.subitems, depth + 1)}\n </li>\n ))}\n </ul>\n );\n\n return (\n <div className=\"rfp-relative rfp-w-full rfp-h-full rfp-flex rfp-justify-center rfp-bg-surface-1 rfp-overflow-hidden\">\n {error && <RendererError message={error} />}\n\n {loading && !error && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-z-10\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n\n {/* 目录侧栏 */}\n {toc.length > 0 && (\n <div\n className=\"rfp-absolute rfp-inset-0 rfp-z-20 rfp-flex rfp-transition-opacity rfp-duration-300\"\n style={{ opacity: showToc ? 1 : 0, pointerEvents: showToc ? 'auto' : 'none' }}\n >\n <div\n className=\"rfp-w-72 rfp-max-w-[80%] rfp-h-full rfp-bg-surface-overlay rfp-backdrop-blur-xl rfp-border-r rfp-border-line-weak rfp-flex rfp-flex-col rfp-shadow-2xl rfp-transition-transform rfp-duration-300\"\n style={{ transform: showToc ? 'translateX(0)' : 'translateX(-100%)' }}\n >\n <div className=\"rfp-flex rfp-items-center rfp-justify-between rfp-px-4 rfp-py-3 rfp-border-b rfp-border-line-weak rfp-flex-shrink-0\">\n <span className=\"rfp-text-fg-primary rfp-font-medium rfp-text-sm\">{t('toolbar.toc')}</span>\n <button\n onClick={() => setShowToc(false)}\n className=\"rfp-text-fg-tertiary hover:rfp-text-fg-primary rfp-transition-colors\"\n >\n <X className=\"rfp-w-4 rfp-h-4\" />\n </button>\n </div>\n <div className=\"rfp-flex-1 rfp-overflow-y-auto rfp-py-4 rfp-px-1\">\n {renderTocItems(toc)}\n </div>\n </div>\n <div\n className=\"rfp-flex-1 rfp-transition-opacity rfp-duration-300\"\n style={{ background: showToc ? 'rgba(0,0,0,0.3)' : 'transparent' }}\n onClick={() => setShowToc(false)}\n />\n </div>\n )}\n\n {!error && (\n <div\n ref={hostRef}\n className=\"rfp-h-full rfp-bg-surface-toolbar rfp-shadow-lg\"\n style={{\n width: isFullWidth ? '100%' : `${A4_WIDTH}px`,\n maxWidth: '100%',\n transition: 'width 0.3s ease',\n }}\n />\n )}\n </div>\n );\n }\n);\n\nMobiRenderer.displayName = 'MobiRenderer';\n"],"names":["READER_CSS","A4_WIDTH","MobiRenderer","forwardRef","url","onChapterChange","onFullWidthChange","ref","t","useTranslator","fetcher","useFetcher","hostRef","useRef","viewRef","onChapterChangeRef","onFullWidthChangeRef","totalLocationsRef","loading","setLoading","useState","error","setError","toc","setToc","showToc","setShowToc","activeTocHref","setActiveTocHref","isFullWidth","setIsFullWidth","isFullWidthRef","reportProgress","useCallback","current","total","_a","handlePrev","view","handleNext","toggleToc","prev","toggleFullWidth","newVal","renderer","handleTocClick","href","useImperativeHandle","useEffect","host","cancelled","e","detail","loc","sections","_b","idx","frac","tocItem","res","blob","name","base","file","_c","_d","_e","_f","err","isActive","renderTocItems","items","depth","item","i","jsxs","jsx","RendererError","X"],"mappings":"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"}
@@ -2,8 +2,8 @@ import { jsxs as T, jsx as o } from "react/jsx-runtime";
2
2
  import { useState as M, useRef as c, useCallback as y, useEffect as C } from "react";
3
3
  import z from "x-data-spreadsheet";
4
4
  /* empty css */
5
- import { u as N, a as j, E as H, S as W, M as k, U as A } from "./index--lXiT1Y_.mjs";
6
- import { R as L } from "./RendererError-BH6fzLrN.mjs";
5
+ import { u as N, a as j, E as H, S as W, M as k, U as A } from "./index-CEeKt7L3.mjs";
6
+ import { R as L } from "./RendererError-D5i8eSpN.mjs";
7
7
  const V = ({ url: v, fileName: h }) => {
8
8
  const R = N(), E = j(), [m, w] = M(!0), [b, D] = 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
9
  if (!r.current) return { width: 800, height: 600 };
@@ -93,4 +93,4 @@ const V = ({ url: v, fileName: h }) => {
93
93
  export {
94
94
  V as CsvRenderer
95
95
  };
96
- //# sourceMappingURL=index-Br8WHz8e.mjs.map
96
+ //# sourceMappingURL=index-kALp0tqz.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-Br8WHz8e.mjs","sources":["../../src/renderers/Csv/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport Spreadsheet from 'x-data-spreadsheet';\nimport 'x-data-spreadsheet/dist/xspreadsheet.css';\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';\n\ninterface CsvRendererProps {\n url: string;\n fileName: string;\n}\n\nexport const CsvRenderer: React.FC<CsvRendererProps> = ({ url, fileName }) => {\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 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","url","fileName","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","jsxs","jsx","RendererError"],"mappings":";;;;;;AAkBO,MAAMA,IAA0C,CAAC,EAAE,KAAAC,GAAK,UAAAC,QAAe;AAC5E,QAAMC,IAAIC,EAAA,GACJC,IAAUC,EAAA,GACV,CAACC,GAASC,CAAU,IAAIC,EAAS,EAAI,GACrC,CAACC,GAAOC,CAAQ,IAAIF,EAAwB,IAAI,GAChDG,IAAeC,EAAuB,IAAI,GAC1CC,IAAiBD,EAA2B,IAAI,GAChDE,IAAeF,EAAyC,IAAI,GAC5DG,IAAoBH,EAA8B,IAAI,GACtDI,IAAmBJ,EAAsB,IAAI,GAC7CK,IAAoBL,EAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,GAElDM,IAAsBC,EAAY,MAAM;AAC5C,QAAI,CAACR,EAAa,QAAS,QAAO,EAAE,OAAO,KAAK,QAAQ,IAAA;AACxD,UAAMS,IAAWT,EAAa,QAAQ,aAChCU,IAAYV,EAAa,QAAQ,cACjCW,IAAQF,IAAW,MAAMA,IAAW,KACpCG,IAASF,IAAY,MAAMA,IAAY;AAC7C,WAAO,EAAE,OAAAC,GAAO,QAAAC,EAAA;AAAA,EAClB,GAAG,CAAA,CAAE,GAECC,IAAmBL,EAAY,MAAM;AACzC,QAAI,CAACR,EAAa,WAAW,CAACG,EAAa,QAAS;AAEpD,IAAAH,EAAa,QAAQ,YAAY,IACjCE,EAAe,UAAU;AAEzB,UAAM,EAAE,OAAAS,GAAO,QAAAC,EAAA,IAAWL,EAAA,GACpBO,IAAWH,IAAQ,KAEnB,IAAI,IAAII,EAAYf,EAAa,SAAS;AAAA,MAC9C,MAAM;AAAA,MACN,aAAa;AAAA,MACb,iBAAiB;AAAA,MACjB,UAAU;AAAA,MACV,KAAK;AAAA,QACH,KAAK;AAAA,QACL,QAAQ;AAAA,MAAA;AAAA,MAEV,KAAK;AAAA,QACH,KAAK;AAAA,QACL,OAAOc,IAAW,KAAK;AAAA,QACvB,YAAYA,IAAW,KAAK;AAAA,QAC5B,UAAUA,IAAW,KAAK;AAAA,MAAA;AAAA,MAE5B,MAAM;AAAA,QACJ,QAAQ,MAAMF;AAAA,QACd,OAAO,MAAMD;AAAA,MAAA;AAAA,IACf,CACD;AAED,MAAE,SAASR,EAAa,OAA6C,GACrED,EAAe,UAAU;AAAA,EAC3B,GAAG,CAACK,CAAmB,CAAC;AAExB,SAAAS,EAAU,MAAM;AACd,QAAI,CAAChB,EAAa,QAAS;AAE3B,QAAIiB,IAAkB;AAEtB,UAAMC,IAAmB,MAAM;AAC7B,UAAID,GAAiB;AACnB,QAAAA,IAAkB,IAClBX,EAAkB,UAAUC,EAAA;AAC5B;AAAA,MACF;AAEA,YAAMY,IAAgBZ,EAAA,GAChBa,IAAiBd,EAAkB,SACnCe,IAAY,KAAK,IAAID,EAAe,QAAQD,EAAc,KAAK,GAC/DG,IAAa,KAAK,IAAIF,EAAe,SAASD,EAAc,MAAM;AAExE,MAAIE,IAAY,MAAMC,IAAa,OAEnChB,EAAkB,UAAUa,GAExBd,EAAiB,WACnB,aAAaA,EAAiB,OAAO,GAGvCA,EAAiB,UAAU,OAAO,WAAW,MAAM;AACjD,QAAIF,EAAa,WACfU,EAAA;AAAA,MAEJ,GAAG,GAAG;AAAA,IACR;AAEA,WAAAT,EAAkB,UAAU,IAAI,eAAe,MAAM;AACnD,MAAAc,EAAA;AAAA,IACF,CAAC,GAEDd,EAAkB,QAAQ,QAAQJ,EAAa,OAAO,GAE/C,MAAM;AACX,MAAII,EAAkB,WACpBA,EAAkB,QAAQ,WAAA,GAExBC,EAAiB,WACnB,aAAaA,EAAiB,OAAO;AAAA,IAEzC;AAAA,EACF,GAAG,CAACE,GAAqBM,CAAgB,CAAC,GAE1CG,EAAU,MAAM;AACd,QAAIO,IAAY;AAChB,UAAMC,IAAa,IAAI,gBAAA,GAEjBC,IAAU,YAAY;AAC1B,UAAKzB,EAAa,SAElB;AAAA,QAAAJ,EAAW,EAAI,GACfG,EAAS,IAAI;AAEb,YAAI;AACF,gBAAM2B,IAAO,MAAMC,EAActC,GAAK,EAAE,SAAAI,GAAS,QAAQ+B,EAAW,QAAQ,GACtEI,IAASC,EAASH,GAAM,EAAE,WAAWI,EAAkBxC,CAAQ,GAAG,GAClEyC,IAAYC,EAA4BJ,EAAO,QAAQA,EAAO,MAAMtC,CAAQ;AAElF,cAAI,CAACiC,EAAW;AAEhB,UAAApB,EAAa,UAAU4B,GACvBlB,EAAA,GACAjB,EAAW,EAAK;AAAA,QAClB,SAASqC,GAAU;AACjB,cAAIA,EAAI,SAAS,aAAc;AAC/B,UAAIV,MACF,QAAQ,MAAM,aAAaU,CAAG,GAC9BlC,EAASR,EAAE,iBAAiB,CAAC,GAC7BK,EAAW,EAAK;AAAA,QAEpB;AAAA;AAAA,IACF,GAEMsC,IAAQ,WAAW,MAAM;AAC7B,4BAAsB,MAAM;AAC1B,QAAAT,EAAA;AAAA,MACF,CAAC;AAAA,IACH,GAAG,GAAG;AAEN,WAAO,MAAM;AACX,MAAAF,IAAY,IACZC,EAAW,MAAA,GACX,aAAaU,CAAK,GAClB/B,EAAa,UAAU,MACnBH,EAAa,YACfA,EAAa,QAAQ,YAAY,KAEnCE,EAAe,UAAU;AAAA,IAC3B;AAAA,EACF,GAAG,CAACb,GAAKC,GAAUuB,CAAgB,CAAC,GAGlC,gBAAAsB,EAAC,OAAA,EAAI,WAAU,6EACZ,UAAA;AAAA,IAAAxC,uBACE,OAAA,EAAI,WAAU,8HACb,UAAA,gBAAAwC,EAAC,OAAA,EAAI,WAAU,mBACb,UAAA;AAAA,MAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,iKAAA,CAAiK;AAAA,wBAC/K,KAAA,EAAE,WAAU,oEAAoE,UAAA7C,EAAE,aAAa,EAAA,CAAE;AAAA,IAAA,EAAA,CACpG,EAAA,CACF;AAAA,IAGDO,KAAS,CAACH,KACT,gBAAAyC,EAAC,OAAA,EAAI,WAAU,8HACb,UAAA,gBAAAA,EAACC,GAAA,EAAc,SAASvC,EAAA,CAAO,EAAA,CACjC;AAAA,IAGD,CAACA,KACA,gBAAAsC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAKpC;AAAA,QACL,WAAU;AAAA,QACV,OAAO,EAAE,SAASL,IAAU,IAAI,EAAA;AAAA,MAAE;AAAA,IAAA;AAAA,EACpC,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"index-kALp0tqz.mjs","sources":["../../src/renderers/Csv/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport Spreadsheet from 'x-data-spreadsheet';\nimport 'x-data-spreadsheet/dist/xspreadsheet.css';\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';\n\ninterface CsvRendererProps {\n url: string;\n fileName: string;\n}\n\nexport const CsvRenderer: React.FC<CsvRendererProps> = ({ url, fileName }) => {\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 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","url","fileName","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","jsxs","jsx","RendererError"],"mappings":"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"}
@@ -1,18 +1,18 @@
1
1
  import { jsx as o } from "react/jsx-runtime";
2
2
  import { useState as f, useEffect as u } from "react";
3
- import { u as h, a as g, E as w } from "./index--lXiT1Y_.mjs";
4
- import { u as b } from "./useShikiHighlight-DoY3TBPT.mjs";
5
- import { R as y } from "./RendererError-BH6fzLrN.mjs";
6
- const N = (r) => {
3
+ import { u as h, a as g, E as w } from "./index-CEeKt7L3.mjs";
4
+ import { u as b } from "./useShikiHighlight-BA9qgdGA.mjs";
5
+ import { R as y } from "./RendererError-D5i8eSpN.mjs";
6
+ const x = (r) => {
7
7
  try {
8
8
  const n = new DOMParser().parseFromString(r, "application/xml");
9
9
  if (n.querySelector("parsererror")) return r;
10
10
  const e = new XMLSerializer().serializeToString(n);
11
- return x(e);
11
+ return N(e);
12
12
  } catch {
13
13
  return r;
14
14
  }
15
- }, x = (r) => {
15
+ }, N = (r) => {
16
16
  const l = " ", n = /(>)(<)(\/*)/g;
17
17
  let s = r.replace(n, `$1
18
18
  $2$3`), t = 0;
@@ -32,7 +32,7 @@ $2$3`), t = 0;
32
32
  try {
33
33
  a(!0), c(null);
34
34
  const p = await w(r, { fetcher: n, signal: m.signal });
35
- t(N(p));
35
+ t(x(p));
36
36
  } catch (p) {
37
37
  if (p.name === "AbortError") return;
38
38
  console.error(p), c(l("xml.load_failed"));
@@ -46,9 +46,9 @@ $2$3`), t = 0;
46
46
  className: "rfp-shiki-wrapper with-line-numbers",
47
47
  dangerouslySetInnerHTML: { __html: d }
48
48
  }
49
- ) : /* @__PURE__ */ o("pre", { className: "rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words", children: s }) });
49
+ ) : /* @__PURE__ */ o("pre", { className: "rfp-py-6 rfp-px-4 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words", children: s }) });
50
50
  };
51
51
  export {
52
52
  X as XmlRenderer
53
53
  };
54
- //# sourceMappingURL=index-DKwN-YU-.mjs.map
54
+ //# sourceMappingURL=index-kCeSnFs-.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-DKwN-YU-.mjs","sources":["../../src/renderers/Xml/index.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { useShikiHighlight } from '../../hooks/useShikiHighlight';\nimport { RendererError } from '../RendererError';\n\ninterface XmlRendererProps {\n url: string;\n fileName: string;\n}\n\n/**\n * 用 DOMParser 美化 XML:失败则原样返回\n */\nconst prettyPrintXml = (xml: string): string => {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'application/xml');\n // 检测解析错误\n const errNode = doc.querySelector('parsererror');\n if (errNode) return xml;\n // 使用 XSLT 或手动缩进:这里手动缩进更稳\n const serializer = new XMLSerializer();\n const serialized = serializer.serializeToString(doc);\n return indentXml(serialized);\n } catch {\n return xml;\n }\n};\n\nconst indentXml = (xml: string): string => {\n const PADDING = ' ';\n const reg = /(>)(<)(\\/*)/g;\n let formatted = xml.replace(reg, '$1\\n$2$3');\n // 自闭合和 CDATA 等不处理\n let pad = 0;\n return formatted\n .split('\\n')\n .map((line) => {\n let indent = 0;\n if (/^<\\/\\w/.test(line)) {\n pad = Math.max(pad - 1, 0);\n } else if (/^<\\w[^>]*[^/]>.*$/.test(line) && !/<.+<\\/.+>$/.test(line)) {\n indent = 1;\n }\n const padded = PADDING.repeat(pad) + line;\n pad += indent;\n return padded;\n })\n .join('\\n');\n};\n\nexport const XmlRenderer: React.FC<XmlRendererProps> = ({ url }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [content, setContent] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const { html: highlighted } = useShikiHighlight(content, 'xml');\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n const raw = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(prettyPrintXml(raw));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.error(err);\n setError(t('xml.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\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) {\n return <RendererError message={error} />;\n }\n\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n {highlighted ? (\n <div\n className=\"rfp-shiki-wrapper with-line-numbers\"\n dangerouslySetInnerHTML={{ __html: highlighted }}\n />\n ) : (\n <pre className=\"rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words\">\n {content}\n </pre>\n )}\n </div>\n );\n};\n"],"names":["prettyPrintXml","xml","doc","serialized","indentXml","PADDING","reg","formatted","pad","line","indent","padded","XmlRenderer","url","t","useTranslator","fetcher","useFetcher","content","setContent","useState","loading","setLoading","error","setError","highlighted","useShikiHighlight","useEffect","controller","raw","fetchTextUtf8","err","jsx","RendererError"],"mappings":";;;;;AAeA,MAAMA,IAAiB,CAACC,MAAwB;AAC9C,MAAI;AAEF,UAAMC,IADS,IAAI,UAAA,EACA,gBAAgBD,GAAK,iBAAiB;AAGzD,QADgBC,EAAI,cAAc,aAAa,EAClC,QAAOD;AAGpB,UAAME,IADa,IAAI,cAAA,EACO,kBAAkBD,CAAG;AACnD,WAAOE,EAAUD,CAAU;AAAA,EAC7B,QAAQ;AACN,WAAOF;AAAA,EACT;AACF,GAEMG,IAAY,CAACH,MAAwB;AACzC,QAAMI,IAAU,MACVC,IAAM;AACZ,MAAIC,IAAYN,EAAI,QAAQK,GAAK;AAAA,KAAU,GAEvCE,IAAM;AACV,SAAOD,EACJ,MAAM;AAAA,CAAI,EACV,IAAI,CAACE,MAAS;AACb,QAAIC,IAAS;AACb,IAAI,SAAS,KAAKD,CAAI,IACpBD,IAAM,KAAK,IAAIA,IAAM,GAAG,CAAC,IAChB,oBAAoB,KAAKC,CAAI,KAAK,CAAC,aAAa,KAAKA,CAAI,MAClEC,IAAS;AAEX,UAAMC,IAASN,EAAQ,OAAOG,CAAG,IAAIC;AACrC,WAAAD,KAAOE,GACAC;AAAA,EACT,CAAC,EACA,KAAK;AAAA,CAAI;AACd,GAEaC,IAA0C,CAAC,EAAE,KAAAC,QAAU;AAClE,QAAMC,IAAIC,EAAA,GACJC,IAAUC,EAAA,GACV,CAACC,GAASC,CAAU,IAAIC,EAAiB,EAAE,GAC3C,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI,GACrC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAChD,EAAE,MAAMK,EAAA,IAAgBC,EAAkBR,GAAS,KAAK;AAsB9D,SApBAS,EAAU,MAAM;AACd,UAAMC,IAAa,IAAI,gBAAA;AAevB,YAda,YAAY;AACvB,UAAI;AACF,QAAAN,EAAW,EAAI,GACfE,EAAS,IAAI;AACb,cAAMK,IAAM,MAAMC,EAAcjB,GAAK,EAAE,SAAAG,GAAS,QAAQY,EAAW,QAAQ;AAC3E,QAAAT,EAAWnB,EAAe6B,CAAG,CAAC;AAAA,MAChC,SAASE,GAAU;AACjB,YAAIA,EAAI,SAAS,aAAc;AAC/B,gBAAQ,MAAMA,CAAG,GACjBP,EAASV,EAAE,iBAAiB,CAAC;AAAA,MAC/B,UAAA;AACE,QAAAQ,EAAW,EAAK;AAAA,MAClB;AAAA,IACF,GACA,GACO,MAAMM,EAAW,MAAA;AAAA,EAC1B,GAAG,CAACf,CAAG,CAAC,GAEJQ,IAEA,gBAAAW,EAAC,SAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,qHAAoH,EAAA,CACrI,IAIAT,IACK,gBAAAS,EAACC,GAAA,EAAc,SAASV,EAAA,CAAO,IAItC,gBAAAS,EAAC,OAAA,EAAI,WAAU,0DACZ,UAAAP,IACC,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,yBAAyB,EAAE,QAAQP,EAAA;AAAA,IAAY;AAAA,EAAA,IAGjD,gBAAAO,EAAC,OAAA,EAAI,WAAU,iGACZ,aACH,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"index-kCeSnFs-.mjs","sources":["../../src/renderers/Xml/index.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { useShikiHighlight } from '../../hooks/useShikiHighlight';\nimport { RendererError } from '../RendererError';\n\ninterface XmlRendererProps {\n url: string;\n fileName: string;\n}\n\n/**\n * 用 DOMParser 美化 XML:失败则原样返回\n */\nconst prettyPrintXml = (xml: string): string => {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'application/xml');\n // 检测解析错误\n const errNode = doc.querySelector('parsererror');\n if (errNode) return xml;\n // 使用 XSLT 或手动缩进:这里手动缩进更稳\n const serializer = new XMLSerializer();\n const serialized = serializer.serializeToString(doc);\n return indentXml(serialized);\n } catch {\n return xml;\n }\n};\n\nconst indentXml = (xml: string): string => {\n const PADDING = ' ';\n const reg = /(>)(<)(\\/*)/g;\n let formatted = xml.replace(reg, '$1\\n$2$3');\n // 自闭合和 CDATA 等不处理\n let pad = 0;\n return formatted\n .split('\\n')\n .map((line) => {\n let indent = 0;\n if (/^<\\/\\w/.test(line)) {\n pad = Math.max(pad - 1, 0);\n } else if (/^<\\w[^>]*[^/]>.*$/.test(line) && !/<.+<\\/.+>$/.test(line)) {\n indent = 1;\n }\n const padded = PADDING.repeat(pad) + line;\n pad += indent;\n return padded;\n })\n .join('\\n');\n};\n\nexport const XmlRenderer: React.FC<XmlRendererProps> = ({ url }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [content, setContent] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const { html: highlighted } = useShikiHighlight(content, 'xml');\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n const raw = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(prettyPrintXml(raw));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.error(err);\n setError(t('xml.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\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) {\n return <RendererError message={error} />;\n }\n\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n {highlighted ? (\n <div\n className=\"rfp-shiki-wrapper with-line-numbers\"\n dangerouslySetInnerHTML={{ __html: highlighted }}\n />\n ) : (\n <pre className=\"rfp-py-6 rfp-px-4 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words\">\n {content}\n </pre>\n )}\n </div>\n );\n};\n"],"names":["prettyPrintXml","xml","doc","serialized","indentXml","PADDING","reg","formatted","pad","line","indent","padded","XmlRenderer","url","t","useTranslator","fetcher","useFetcher","content","setContent","useState","loading","setLoading","error","setError","highlighted","useShikiHighlight","useEffect","controller","raw","fetchTextUtf8","err","jsx","RendererError"],"mappings":";;;;;AAeA,MAAMA,IAAiB,CAACC,MAAwB;AAC9C,MAAI;AAEF,UAAMC,IADS,IAAI,UAAA,EACA,gBAAgBD,GAAK,iBAAiB;AAGzD,QADgBC,EAAI,cAAc,aAAa,EAClC,QAAOD;AAGpB,UAAME,IADa,IAAI,cAAA,EACO,kBAAkBD,CAAG;AACnD,WAAOE,EAAUD,CAAU;AAAA,EAC7B,QAAQ;AACN,WAAOF;AAAA,EACT;AACF,GAEMG,IAAY,CAACH,MAAwB;AACzC,QAAMI,IAAU,MACVC,IAAM;AACZ,MAAIC,IAAYN,EAAI,QAAQK,GAAK;AAAA,KAAU,GAEvCE,IAAM;AACV,SAAOD,EACJ,MAAM;AAAA,CAAI,EACV,IAAI,CAACE,MAAS;AACb,QAAIC,IAAS;AACb,IAAI,SAAS,KAAKD,CAAI,IACpBD,IAAM,KAAK,IAAIA,IAAM,GAAG,CAAC,IAChB,oBAAoB,KAAKC,CAAI,KAAK,CAAC,aAAa,KAAKA,CAAI,MAClEC,IAAS;AAEX,UAAMC,IAASN,EAAQ,OAAOG,CAAG,IAAIC;AACrC,WAAAD,KAAOE,GACAC;AAAA,EACT,CAAC,EACA,KAAK;AAAA,CAAI;AACd,GAEaC,IAA0C,CAAC,EAAE,KAAAC,QAAU;AAClE,QAAMC,IAAIC,EAAA,GACJC,IAAUC,EAAA,GACV,CAACC,GAASC,CAAU,IAAIC,EAAiB,EAAE,GAC3C,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI,GACrC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAChD,EAAE,MAAMK,EAAA,IAAgBC,EAAkBR,GAAS,KAAK;AAsB9D,SApBAS,EAAU,MAAM;AACd,UAAMC,IAAa,IAAI,gBAAA;AAevB,YAda,YAAY;AACvB,UAAI;AACF,QAAAN,EAAW,EAAI,GACfE,EAAS,IAAI;AACb,cAAMK,IAAM,MAAMC,EAAcjB,GAAK,EAAE,SAAAG,GAAS,QAAQY,EAAW,QAAQ;AAC3E,QAAAT,EAAWnB,EAAe6B,CAAG,CAAC;AAAA,MAChC,SAASE,GAAU;AACjB,YAAIA,EAAI,SAAS,aAAc;AAC/B,gBAAQ,MAAMA,CAAG,GACjBP,EAASV,EAAE,iBAAiB,CAAC;AAAA,MAC/B,UAAA;AACE,QAAAQ,EAAW,EAAK;AAAA,MAClB;AAAA,IACF,GACA,GACO,MAAMM,EAAW,MAAA;AAAA,EAC1B,GAAG,CAACf,CAAG,CAAC,GAEJQ,IAEA,gBAAAW,EAAC,SAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,qHAAoH,EAAA,CACrI,IAIAT,IACK,gBAAAS,EAACC,GAAA,EAAc,SAASV,EAAA,CAAO,IAItC,gBAAAS,EAAC,OAAA,EAAI,WAAU,0DACZ,UAAAP,IACC,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,yBAAyB,EAAE,QAAQP,EAAA;AAAA,IAAY;AAAA,EAAA,IAGjD,gBAAAO,EAAC,OAAA,EAAI,WAAU,2GACZ,aACH,GAEJ;AAEJ;"}
@@ -1,6 +1,6 @@
1
1
  import { useState as l, useEffect as f } from "react";
2
2
  import { codeToHtml as a } from "shiki";
3
- import { b as n } from "./index--lXiT1Y_.mjs";
3
+ import { b as n } from "./index-CEeKt7L3.mjs";
4
4
  function p(s, o) {
5
5
  const r = n(), [u, i] = l(""), [h, e] = l(!0);
6
6
  return f(() => {
@@ -20,4 +20,4 @@ function p(s, o) {
20
20
  export {
21
21
  p as u
22
22
  };
23
- //# sourceMappingURL=useShikiHighlight-DoY3TBPT.mjs.map
23
+ //# sourceMappingURL=useShikiHighlight-BA9qgdGA.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useShikiHighlight-DoY3TBPT.mjs","sources":["../../src/hooks/useShikiHighlight.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { codeToHtml } from 'shiki';\nimport { useResolvedTheme } from '../ThemeContext';\n\n/**\n * 用 shiki 把代码高亮成 HTML(与 vue-file-preview 同引擎、同主题,保证两端视觉一致)。\n *\n * - dark 主题用 `dark-plus`(VSCode Dark Plus)\n * - light 主题用 `github-light`(GitHub Light)\n *\n * shiki 输出的 <pre> 自带 inline 背景/前景色,主题切换时必须重新高亮,\n * 因此 resolvedTheme 进入依赖数组。\n *\n * @returns\n * - `html`: 高亮后的 HTML 字符串(失败或加载中为 '')\n * - `loading`: 是否正在高亮\n */\nexport function useShikiHighlight(code: string, lang: string): { html: string; loading: boolean } {\n const resolvedTheme = useResolvedTheme();\n const [html, setHtml] = useState('');\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n codeToHtml(code, {\n lang,\n theme: resolvedTheme === 'light' ? 'github-light' : 'dark-plus',\n })\n .then((out) => {\n if (!cancelled) {\n setHtml(out);\n setLoading(false);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setHtml('');\n setLoading(false);\n }\n });\n return () => {\n cancelled = true;\n };\n }, [code, lang, resolvedTheme]);\n\n return { html, loading };\n}\n"],"names":["useShikiHighlight","code","lang","resolvedTheme","useResolvedTheme","html","setHtml","useState","loading","setLoading","useEffect","cancelled","codeToHtml","out"],"mappings":";;;AAiBO,SAASA,EAAkBC,GAAcC,GAAkD;AAChG,QAAMC,IAAgBC,EAAA,GAChB,CAACC,GAAMC,CAAO,IAAIC,EAAS,EAAE,GAC7B,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI;AAE3C,SAAAG,EAAU,MAAM;AACd,QAAIC,IAAY;AAChB,WAAAF,EAAW,EAAI,GACfG,EAAWX,GAAM;AAAA,MACf,MAAAC;AAAA,MACA,OAAOC,MAAkB,UAAU,iBAAiB;AAAA,IAAA,CACrD,EACE,KAAK,CAACU,MAAQ;AACb,MAAKF,MACHL,EAAQO,CAAG,GACXJ,EAAW,EAAK;AAAA,IAEpB,CAAC,EACA,MAAM,MAAM;AACX,MAAKE,MACHL,EAAQ,EAAE,GACVG,EAAW,EAAK;AAAA,IAEpB,CAAC,GACI,MAAM;AACX,MAAAE,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACV,GAAMC,GAAMC,CAAa,CAAC,GAEvB,EAAE,MAAAE,GAAM,SAAAG,EAAA;AACjB;"}
1
+ {"version":3,"file":"useShikiHighlight-BA9qgdGA.mjs","sources":["../../src/hooks/useShikiHighlight.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { codeToHtml } from 'shiki';\nimport { useResolvedTheme } from '../ThemeContext';\n\n/**\n * 用 shiki 把代码高亮成 HTML(与 vue-file-preview 同引擎、同主题,保证两端视觉一致)。\n *\n * - dark 主题用 `dark-plus`(VSCode Dark Plus)\n * - light 主题用 `github-light`(GitHub Light)\n *\n * shiki 输出的 <pre> 自带 inline 背景/前景色,主题切换时必须重新高亮,\n * 因此 resolvedTheme 进入依赖数组。\n *\n * @returns\n * - `html`: 高亮后的 HTML 字符串(失败或加载中为 '')\n * - `loading`: 是否正在高亮\n */\nexport function useShikiHighlight(code: string, lang: string): { html: string; loading: boolean } {\n const resolvedTheme = useResolvedTheme();\n const [html, setHtml] = useState('');\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n codeToHtml(code, {\n lang,\n theme: resolvedTheme === 'light' ? 'github-light' : 'dark-plus',\n })\n .then((out) => {\n if (!cancelled) {\n setHtml(out);\n setLoading(false);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setHtml('');\n setLoading(false);\n }\n });\n return () => {\n cancelled = true;\n };\n }, [code, lang, resolvedTheme]);\n\n return { html, loading };\n}\n"],"names":["useShikiHighlight","code","lang","resolvedTheme","useResolvedTheme","html","setHtml","useState","loading","setLoading","useEffect","cancelled","codeToHtml","out"],"mappings":";;;AAiBO,SAASA,EAAkBC,GAAcC,GAAkD;AAChG,QAAMC,IAAgBC,EAAA,GAChB,CAACC,GAAMC,CAAO,IAAIC,EAAS,EAAE,GAC7B,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI;AAE3C,SAAAG,EAAU,MAAM;AACd,QAAIC,IAAY;AAChB,WAAAF,EAAW,EAAI,GACfG,EAAWX,GAAM;AAAA,MACf,MAAAC;AAAA,MACA,OAAOC,MAAkB,UAAU,iBAAiB;AAAA,IAAA,CACrD,EACE,KAAK,CAACU,MAAQ;AACb,MAAKF,MACHL,EAAQO,CAAG,GACXJ,EAAW,EAAK;AAAA,IAEpB,CAAC,EACA,MAAM,MAAM;AACX,MAAKE,MACHL,EAAQ,EAAE,GACVG,EAAW,EAAK;AAAA,IAEpB,CAAC,GACI,MAAM;AACX,MAAAE,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACV,GAAMC,GAAMC,CAAa,CAAC,GAEvB,EAAE,MAAAE,GAAM,SAAAG,EAAA;AACjB;"}
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { Translator } from '@eternalheart/file-preview-core';
3
+ import type { PreviewFile, CustomRenderer, CustomRendererContext } from '../../types';
4
+ export interface FilePreviewRendererProps {
5
+ currentFile: PreviewFile;
6
+ customRenderer?: CustomRenderer | null;
7
+ customRendererContext: CustomRendererContext;
8
+ t: Translator;
9
+ onDownload: () => void;
10
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
11
+ children: React.ReactNode;
12
+ }
13
+ /**
14
+ * 渲染器容器组件
15
+ * 负责 Suspense + 错误边界包装,不负责具体渲染器选择
16
+ */
17
+ export declare const FilePreviewRenderer: React.FC<FilePreviewRendererProps>;
18
+ //# sourceMappingURL=FilePreviewRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilePreviewRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/preview/FilePreviewRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAKtF,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,qBAAqB,EAAE,qBAAqB,CAAC;IAC7C,CAAC,EAAE,UAAU,CAAC;IACd,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC;IAC7D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAgDlE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import type { ToolbarGroup } from '../../renderers/toolbar.types';
3
+ import type { Translator } from '@eternalheart/file-preview-core';
4
+ export interface FilePreviewToolbarProps {
5
+ fileName: string;
6
+ currentIndex: number;
7
+ totalFiles: number;
8
+ toolGroups: ToolbarGroup[];
9
+ t: Translator;
10
+ onDownload: () => void;
11
+ onClose?: () => void;
12
+ }
13
+ /**
14
+ * 文件预览顶部工具栏组件
15
+ * - 桌面端:单行显示所有按钮
16
+ * - 移动端:第一行显示文件名 + 下载/关闭,第二行显示工具按钮
17
+ */
18
+ export declare const FilePreviewToolbar: React.FC<FilePreviewToolbarProps>;
19
+ //# sourceMappingURL=FilePreviewToolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilePreviewToolbar.d.ts","sourceRoot":"","sources":["../../../src/components/preview/FilePreviewToolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAGlE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,CAAC,EAAE,UAAU,CAAC;IACd,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAuFhE,CAAC"}
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { Translator } from '@eternalheart/file-preview-core';
3
+ export interface NavArrowsProps {
4
+ containerRef: React.RefObject<HTMLDivElement | null>;
5
+ hasPrev: boolean;
6
+ hasNext: boolean;
7
+ onPrev: () => void;
8
+ onNext: () => void;
9
+ resetKey: number;
10
+ t: Translator;
11
+ }
12
+ /**
13
+ * 导航箭头组件
14
+ * 自带 mousemove 监听 + 2s 自动隐藏定时器
15
+ * state 隔离在本组件,避免父组件因 navVisible 变化而 re-render
16
+ */
17
+ export declare const NavArrows: React.FC<NavArrowsProps>;
18
+ //# sourceMappingURL=NavArrows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavArrows.d.ts","sourceRoot":"","sources":["../../../src/components/preview/NavArrows.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAGxE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,UAAU,CAAC;CACf;AAID;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA4E9C,CAAC"}
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import type { Translator } from '@eternalheart/file-preview-core';
3
+ export interface RendererErrorProps {
4
+ error: Error;
5
+ fileName: string;
6
+ t: Translator;
7
+ onRetry: () => void;
8
+ onDownload: () => void;
9
+ }
10
+ /**
11
+ * 渲染器错误 UI 组件
12
+ * 显示错误信息、文件名、重试和下载按钮
13
+ */
14
+ export declare const RendererError: React.FC<RendererErrorProps>;
15
+ //# sourceMappingURL=RendererError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RendererError.d.ts","sourceRoot":"","sources":["../../../src/components/preview/RendererError.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,UAAU,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAuCtD,CAAC"}
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ export interface RendererErrorBoundaryProps {
3
+ children: React.ReactNode;
4
+ fallback: (error: Error, reset: () => void) => React.ReactNode;
5
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
6
+ }
7
+ interface RendererErrorBoundaryState {
8
+ hasError: boolean;
9
+ error: Error | null;
10
+ }
11
+ /**
12
+ * 渲染器错误边界类组件
13
+ * 捕获渲染器加载失败和运行时错误
14
+ */
15
+ export declare class RendererErrorBoundary extends React.Component<RendererErrorBoundaryProps, RendererErrorBoundaryState> {
16
+ constructor(props: RendererErrorBoundaryProps);
17
+ static getDerivedStateFromError(error: Error): RendererErrorBoundaryState;
18
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
19
+ reset: () => void;
20
+ render(): React.ReactNode;
21
+ }
22
+ export {};
23
+ //# sourceMappingURL=RendererErrorBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RendererErrorBoundary.d.ts","sourceRoot":"","sources":["../../../src/components/preview/RendererErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,KAAK,CAAC,SAAS,CAAC;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC;CAC9D;AAED,UAAU,0BAA0B;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,KAAK,CAAC,SAAS,CACxD,0BAA0B,EAC1B,0BAA0B,CAC3B;gBACa,KAAK,EAAE,0BAA0B;IAK7C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,0BAA0B;IAIzE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI;IAKjE,KAAK,QAAO,IAAI,CAEd;IAEF,MAAM,IAAI,KAAK,CAAC,SAAS;CAO1B"}
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ export interface ToolbarButtonProps {
3
+ icon: React.ReactNode;
4
+ label: string;
5
+ onClick: () => void;
6
+ disabled?: boolean;
7
+ ariaKeyshortcuts?: string;
8
+ }
9
+ /**
10
+ * 工具栏按钮组件
11
+ * 支持 tooltip、disabled 状态和 aria 属性
12
+ */
13
+ export declare const ToolbarButton: React.FC<ToolbarButtonProps>;
14
+ //# sourceMappingURL=ToolbarButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolbarButton.d.ts","sourceRoot":"","sources":["../../../src/components/preview/ToolbarButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA2BtD,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from './ToolbarButton';
2
+ export * from './FilePreviewToolbar';
3
+ export * from './RendererError';
4
+ export * from './RendererErrorBoundary';
5
+ export * from './FilePreviewRenderer';
6
+ export * from './NavArrows';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/preview/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,9 @@
1
+ export * from './types';
2
+ export * from './rendererReducer';
3
+ export * from './useFilePreviewState';
4
+ export * from './useKeyboardNavigation';
5
+ export * from './useBookRenderer';
6
+ export * from './useThemeMode';
7
+ export * from './useImageAutoFit';
8
+ export * from './useToolbarConfig';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { RendererState, RendererAction } from './types';
2
+ /**
3
+ * 渲染器状态初始值
4
+ */
5
+ export declare const initialRendererState: RendererState;
6
+ /**
7
+ * 渲染器状态 reducer
8
+ */
9
+ export declare function rendererReducer(state: RendererState, action: RendererAction): RendererState;
10
+ //# sourceMappingURL=rendererReducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendererReducer.d.ts","sourceRoot":"","sources":["../../src/hooks/rendererReducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,aAmClC,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,GAAG,aAAa,CA+G3F"}
@@ -0,0 +1,152 @@
1
+ import type { ZipToolbarStats } from '../renderers/Zip/toolbar';
2
+ /**
3
+ * 渲染器通用状态
4
+ */
5
+ export interface RendererCommonState {
6
+ zoom: number;
7
+ rotation: number;
8
+ }
9
+ /**
10
+ * 图片渲染器状态
11
+ */
12
+ export interface RendererImageState {
13
+ naturalWidth: number;
14
+ naturalHeight: number;
15
+ resetKey: number;
16
+ }
17
+ /**
18
+ * PDF 渲染器状态
19
+ */
20
+ export interface RendererPdfState {
21
+ currentPage: number;
22
+ totalPages: number;
23
+ showOutline: boolean;
24
+ }
25
+ /**
26
+ * EPUB 渲染器状态
27
+ */
28
+ export interface RendererEpubState {
29
+ current: number;
30
+ total: number;
31
+ fullWidth: boolean;
32
+ }
33
+ /**
34
+ * Mobi 渲染器状态
35
+ */
36
+ export interface RendererMobiState {
37
+ current: number;
38
+ total: number;
39
+ fullWidth: boolean;
40
+ }
41
+ /**
42
+ * ZIP 渲染器状态
43
+ */
44
+ export interface RendererZipState {
45
+ stats: ZipToolbarStats | null;
46
+ }
47
+ /**
48
+ * Text 渲染器状态
49
+ */
50
+ export interface RendererTextState {
51
+ wordWrap: boolean;
52
+ htmlPreview: boolean;
53
+ }
54
+ /**
55
+ * Markdown 渲染器状态
56
+ */
57
+ export interface RendererMarkdownState {
58
+ viewMode: 'preview' | 'source';
59
+ }
60
+ /**
61
+ * 所有渲染器状态的聚合
62
+ */
63
+ export interface RendererState {
64
+ common: RendererCommonState;
65
+ image: RendererImageState;
66
+ pdf: RendererPdfState;
67
+ epub: RendererEpubState;
68
+ mobi: RendererMobiState;
69
+ zip: RendererZipState;
70
+ text: RendererTextState;
71
+ markdown: RendererMarkdownState;
72
+ }
73
+ /**
74
+ * 渲染器 Action 类型
75
+ */
76
+ export type RendererAction = {
77
+ type: 'RESET';
78
+ } | {
79
+ type: 'SET_ZOOM';
80
+ payload: number;
81
+ } | {
82
+ type: 'SET_ROTATION';
83
+ payload: number;
84
+ } | {
85
+ type: 'SET_IMAGE_NATURAL_SIZE';
86
+ payload: {
87
+ width: number;
88
+ height: number;
89
+ };
90
+ } | {
91
+ type: 'RESET_IMAGE';
92
+ } | {
93
+ type: 'SET_PDF_PAGE';
94
+ payload: number;
95
+ } | {
96
+ type: 'SET_PDF_TOTAL_PAGES';
97
+ payload: number;
98
+ } | {
99
+ type: 'SET_PDF_OUTLINE';
100
+ payload: boolean;
101
+ } | {
102
+ type: 'SET_EPUB_CHAPTER';
103
+ payload: {
104
+ current: number;
105
+ total: number;
106
+ };
107
+ } | {
108
+ type: 'SET_EPUB_FULL_WIDTH';
109
+ payload: boolean;
110
+ } | {
111
+ type: 'SET_MOBI_CHAPTER';
112
+ payload: {
113
+ current: number;
114
+ total: number;
115
+ };
116
+ } | {
117
+ type: 'SET_MOBI_FULL_WIDTH';
118
+ payload: boolean;
119
+ } | {
120
+ type: 'SET_ZIP_STATS';
121
+ payload: ZipToolbarStats | null;
122
+ } | {
123
+ type: 'SET_TEXT_WORD_WRAP';
124
+ payload: boolean;
125
+ } | {
126
+ type: 'SET_TEXT_HTML_PREVIEW';
127
+ payload: boolean;
128
+ } | {
129
+ type: 'SET_MARKDOWN_VIEW_MODE';
130
+ payload: 'preview' | 'source';
131
+ };
132
+ /**
133
+ * 渲染器事件处理器集合
134
+ */
135
+ export interface RendererHandlers {
136
+ onZoomIn: () => void;
137
+ onZoomOut: () => void;
138
+ onZoomChange: (zoom: number) => void;
139
+ onRotateLeft: () => void;
140
+ onRotateRight: () => void;
141
+ onReset: () => void;
142
+ onFitToWidth: () => void;
143
+ onOriginalSize: () => void;
144
+ onPrevPage: () => void;
145
+ onNextPage: () => void;
146
+ onToggleOutline: () => void;
147
+ onToggleWrap: () => void;
148
+ onToggleHtmlPreview: () => void;
149
+ onToggleViewMode: () => void;
150
+ [key: string]: (() => void) | ((value: any) => void);
151
+ }
152
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/hooks/types.ts"],"names":[],"mappings":"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"}