@lamberl-lee/file-preview 0.2.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.
- package/COPYING +674 -0
- package/LICENSE +165 -0
- package/README.md +165 -0
- package/dist/AudioPreview.d.ts +9 -0
- package/dist/AudioPreview.js +29 -0
- package/dist/AudioPreview.js.map +1 -0
- package/dist/CodePreview.d.ts +10 -0
- package/dist/CodePreview.js +121 -0
- package/dist/CodePreview.js.map +1 -0
- package/dist/CsvPreview.d.ts +9 -0
- package/dist/CsvPreview.js +117 -0
- package/dist/CsvPreview.js.map +1 -0
- package/dist/DocxPreview.d.ts +11 -0
- package/dist/DocxPreview.js +89 -0
- package/dist/DocxPreview.js.map +1 -0
- package/dist/EpubPreview.d.ts +9 -0
- package/dist/EpubPreview.js +693 -0
- package/dist/EpubPreview.js.map +1 -0
- package/dist/HtmlPreview.d.ts +9 -0
- package/dist/HtmlPreview.js +60 -0
- package/dist/HtmlPreview.js.map +1 -0
- package/dist/ImagePreview.d.ts +9 -0
- package/dist/ImagePreview.js +44 -0
- package/dist/ImagePreview.js.map +1 -0
- package/dist/LargeFileGate.d.ts +12 -0
- package/dist/LargeFileGate.js +88 -0
- package/dist/LargeFileGate.js.map +1 -0
- package/dist/MarkdownPreview.d.ts +8 -0
- package/dist/MarkdownPreview.js +140 -0
- package/dist/MarkdownPreview.js.map +1 -0
- package/dist/PdfPreview.d.ts +11 -0
- package/dist/PdfPreview.js +206 -0
- package/dist/PdfPreview.js.map +1 -0
- package/dist/PlainTextLargePreview.d.ts +9 -0
- package/dist/PlainTextLargePreview.js +62 -0
- package/dist/PlainTextLargePreview.js.map +1 -0
- package/dist/PluginPreviewRenderer.d.ts +13 -0
- package/dist/PluginPreviewRenderer.js +89 -0
- package/dist/PluginPreviewRenderer.js.map +1 -0
- package/dist/PptxPreview.d.ts +16 -0
- package/dist/PptxPreview.js +376 -0
- package/dist/PptxPreview.js.map +1 -0
- package/dist/PreviewErrorBoundary.d.ts +29 -0
- package/dist/PreviewErrorBoundary.js +53 -0
- package/dist/PreviewErrorBoundary.js.map +1 -0
- package/dist/PreviewFallback.d.ts +18 -0
- package/dist/PreviewFallback.js +143 -0
- package/dist/PreviewFallback.js.map +1 -0
- package/dist/PreviewLoading.d.ts +8 -0
- package/dist/PreviewLoading.js +14 -0
- package/dist/PreviewLoading.js.map +1 -0
- package/dist/RtfPreview.d.ts +10 -0
- package/dist/RtfPreview.js +240 -0
- package/dist/RtfPreview.js.map +1 -0
- package/dist/ShikiSourceView.d.ts +11 -0
- package/dist/ShikiSourceView.js +112 -0
- package/dist/ShikiSourceView.js.map +1 -0
- package/dist/SvgPreview.d.ts +9 -0
- package/dist/SvgPreview.js +89 -0
- package/dist/SvgPreview.js.map +1 -0
- package/dist/TextPreview.d.ts +9 -0
- package/dist/TextPreview.js +9 -0
- package/dist/TextPreview.js.map +1 -0
- package/dist/VideoPreview.d.ts +9 -0
- package/dist/VideoPreview.js +18 -0
- package/dist/VideoPreview.js.map +1 -0
- package/dist/XlsxPreview.d.ts +12 -0
- package/dist/XlsxPreview.js +856 -0
- package/dist/XlsxPreview.js.map +1 -0
- package/dist/ZipPreview.d.ts +9 -0
- package/dist/ZipPreview.js +153 -0
- package/dist/ZipPreview.js.map +1 -0
- package/dist/core/binary.d.ts +10 -0
- package/dist/core/binary.js +27 -0
- package/dist/core/binary.js.map +1 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.js +19 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/download.d.ts +5 -0
- package/dist/core/download.js +20 -0
- package/dist/core/download.js.map +1 -0
- package/dist/core/i18n.d.ts +101 -0
- package/dist/core/i18n.js +200 -0
- package/dist/core/i18n.js.map +1 -0
- package/dist/core/plugin.d.ts +38 -0
- package/dist/core/plugin.js +46 -0
- package/dist/core/plugin.js.map +1 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/registry.js +33 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/source.d.ts +14 -0
- package/dist/core/source.js +131 -0
- package/dist/core/source.js.map +1 -0
- package/dist/core/types.d.ts +88 -0
- package/dist/core/types.js +27 -0
- package/dist/core/types.js.map +1 -0
- package/dist/hooks/useObjectUrlFromSource.d.ts +9 -0
- package/dist/hooks/useObjectUrlFromSource.js +50 -0
- package/dist/hooks/useObjectUrlFromSource.js.map +1 -0
- package/dist/hooks/useSourceBase64.d.ts +10 -0
- package/dist/hooks/useSourceBase64.js +32 -0
- package/dist/hooks/useSourceBase64.js.map +1 -0
- package/dist/hooks/useSourceText.d.ts +10 -0
- package/dist/hooks/useSourceText.js +32 -0
- package/dist/hooks/useSourceText.js.map +1 -0
- package/dist/icons.d.ts +44 -0
- package/dist/icons.js +248 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +147 -0
- package/dist/index.js.map +1 -0
- package/dist/limits.d.ts +26 -0
- package/dist/limits.js +45 -0
- package/dist/limits.js.map +1 -0
- package/dist/performance-limits.d.ts +27 -0
- package/dist/performance-limits.js +54 -0
- package/dist/performance-limits.js.map +1 -0
- package/dist/plugins/audio-plugin.d.ts +7 -0
- package/dist/plugins/audio-plugin.js +11 -0
- package/dist/plugins/audio-plugin.js.map +1 -0
- package/dist/plugins/builtin-plugins.d.ts +9 -0
- package/dist/plugins/builtin-plugins.js +43 -0
- package/dist/plugins/builtin-plugins.js.map +1 -0
- package/dist/plugins/csv-plugin.d.ts +7 -0
- package/dist/plugins/csv-plugin.js +11 -0
- package/dist/plugins/csv-plugin.js.map +1 -0
- package/dist/plugins/docx-plugin.d.ts +7 -0
- package/dist/plugins/docx-plugin.js +15 -0
- package/dist/plugins/docx-plugin.js.map +1 -0
- package/dist/plugins/epub-plugin.d.ts +7 -0
- package/dist/plugins/epub-plugin.js +15 -0
- package/dist/plugins/epub-plugin.js.map +1 -0
- package/dist/plugins/html-plugin.d.ts +7 -0
- package/dist/plugins/html-plugin.js +11 -0
- package/dist/plugins/html-plugin.js.map +1 -0
- package/dist/plugins/image-plugin.d.ts +7 -0
- package/dist/plugins/image-plugin.js +11 -0
- package/dist/plugins/image-plugin.js.map +1 -0
- package/dist/plugins/markdown-plugin.d.ts +7 -0
- package/dist/plugins/markdown-plugin.js +11 -0
- package/dist/plugins/markdown-plugin.js.map +1 -0
- package/dist/plugins/pdf-plugin.d.ts +7 -0
- package/dist/plugins/pdf-plugin.js +15 -0
- package/dist/plugins/pdf-plugin.js.map +1 -0
- package/dist/plugins/pptx-plugin.d.ts +7 -0
- package/dist/plugins/pptx-plugin.js +15 -0
- package/dist/plugins/pptx-plugin.js.map +1 -0
- package/dist/plugins/rtf-plugin.d.ts +7 -0
- package/dist/plugins/rtf-plugin.js +15 -0
- package/dist/plugins/rtf-plugin.js.map +1 -0
- package/dist/plugins/source-code-plugin.d.ts +7 -0
- package/dist/plugins/source-code-plugin.js +11 -0
- package/dist/plugins/source-code-plugin.js.map +1 -0
- package/dist/plugins/svg-plugin.d.ts +7 -0
- package/dist/plugins/svg-plugin.js +11 -0
- package/dist/plugins/svg-plugin.js.map +1 -0
- package/dist/plugins/text-plugin.d.ts +7 -0
- package/dist/plugins/text-plugin.js +11 -0
- package/dist/plugins/text-plugin.js.map +1 -0
- package/dist/plugins/video-plugin.d.ts +7 -0
- package/dist/plugins/video-plugin.js +11 -0
- package/dist/plugins/video-plugin.js.map +1 -0
- package/dist/plugins/xlsx-plugin.d.ts +7 -0
- package/dist/plugins/xlsx-plugin.js +15 -0
- package/dist/plugins/xlsx-plugin.js.map +1 -0
- package/dist/plugins/zip-plugin.d.ts +7 -0
- package/dist/plugins/zip-plugin.js +15 -0
- package/dist/plugins/zip-plugin.js.map +1 -0
- package/dist/preview-adapters/AudioPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/AudioPreviewAdapter.js +31 -0
- package/dist/preview-adapters/AudioPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/CsvPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/CsvPreviewAdapter.js +28 -0
- package/dist/preview-adapters/CsvPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/DocxPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/DocxPreviewAdapter.js +16 -0
- package/dist/preview-adapters/DocxPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/EpubPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/EpubPreviewAdapter.js +28 -0
- package/dist/preview-adapters/EpubPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/HtmlPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/HtmlPreviewAdapter.js +28 -0
- package/dist/preview-adapters/HtmlPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/ImagePreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/ImagePreviewAdapter.js +31 -0
- package/dist/preview-adapters/ImagePreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/MarkdownPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/MarkdownPreviewAdapter.js +28 -0
- package/dist/preview-adapters/MarkdownPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/PdfPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/PdfPreviewAdapter.js +16 -0
- package/dist/preview-adapters/PdfPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/PptxPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/PptxPreviewAdapter.js +16 -0
- package/dist/preview-adapters/PptxPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/RtfPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/RtfPreviewAdapter.js +54 -0
- package/dist/preview-adapters/RtfPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/SourceCodePreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/SourceCodePreviewAdapter.js +35 -0
- package/dist/preview-adapters/SourceCodePreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/SvgPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/SvgPreviewAdapter.js +28 -0
- package/dist/preview-adapters/SvgPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/TextPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/TextPreviewAdapter.js +28 -0
- package/dist/preview-adapters/TextPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/UnsupportedPluginPreview.d.ts +11 -0
- package/dist/preview-adapters/UnsupportedPluginPreview.js +34 -0
- package/dist/preview-adapters/UnsupportedPluginPreview.js.map +1 -0
- package/dist/preview-adapters/VideoPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/VideoPreviewAdapter.js +31 -0
- package/dist/preview-adapters/VideoPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/XlsxPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/XlsxPreviewAdapter.js +17 -0
- package/dist/preview-adapters/XlsxPreviewAdapter.js.map +1 -0
- package/dist/preview-adapters/ZipPreviewAdapter.d.ts +8 -0
- package/dist/preview-adapters/ZipPreviewAdapter.js +28 -0
- package/dist/preview-adapters/ZipPreviewAdapter.js.map +1 -0
- package/dist/remote-url.d.ts +20 -0
- package/dist/remote-url.js +478 -0
- package/dist/remote-url.js.map +1 -0
- package/dist/rtf/load-rtfjs.d.ts +42 -0
- package/dist/rtf/load-rtfjs.js +71 -0
- package/dist/rtf/load-rtfjs.js.map +1 -0
- package/dist/rtf/normalize-codepage.d.ts +33 -0
- package/dist/rtf/normalize-codepage.js +88 -0
- package/dist/rtf/normalize-codepage.js.map +1 -0
- package/dist/shiki.d.ts +35 -0
- package/dist/shiki.js +128 -0
- package/dist/shiki.js.map +1 -0
- package/dist/styles/AudioPreview.css +35 -0
- package/dist/styles/CsvPreview.css +106 -0
- package/dist/styles/DocxPreview.css +93 -0
- package/dist/styles/EpubPreview.css +509 -0
- package/dist/styles/HtmlPreview.css +15 -0
- package/dist/styles/ImagePreview.css +45 -0
- package/dist/styles/LargeFileGate.css +55 -0
- package/dist/styles/MarkdownPreview.css +291 -0
- package/dist/styles/PdfPreview.css +68 -0
- package/dist/styles/PlainTextLargePreview.css +85 -0
- package/dist/styles/PluginDebugBar.css +30 -0
- package/dist/styles/PptxPreview.css +207 -0
- package/dist/styles/PreviewFallback.css +88 -0
- package/dist/styles/PreviewLoading.css +13 -0
- package/dist/styles/RtfPreview.css +99 -0
- package/dist/styles/ShikiSourceView.css +159 -0
- package/dist/styles/SvgPreview.css +99 -0
- package/dist/styles/VideoPreview.css +19 -0
- package/dist/styles/ViewModeBar.css +38 -0
- package/dist/styles/XlsxPreview.css +361 -0
- package/dist/styles/ZipPreview.css +86 -0
- package/dist/styles/base.css +238 -0
- package/dist/styles/index.css +39 -0
- package/dist/support-status.d.ts +19 -0
- package/dist/support-status.js +174 -0
- package/dist/support-status.js.map +1 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.js +205 -0
- package/dist/utils.js.map +1 -0
- package/package.json +125 -0
- package/scripts/copy-pdf-worker.mjs +31 -0
- package/scripts/copy-rtfjs-bundles.mjs +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/XlsxPreview.tsx"],"sourcesContent":["import { useEffect, useState, useCallback, useMemo, useRef } from \"react\";\nimport {\n SearchIcon,\n Table2Icon,\n ZoomInIcon,\n ZoomOutIcon,\n ImageOffIcon,\n MessageSquareIcon,\n ExternalLinkIcon,\n AlertTriangleIcon,\n} from \"./icons\";\nimport \"./styles/XlsxPreview.css\";\nimport { XLSX_PREVIEW_LIMITS } from \"./limits\";\nimport { readBinaryPreviewAsUint8Array } from \"./core/binary\";\nimport type { PreviewSource } from \"./core/types\";\nimport { formatFileSize } from \"./utils\";\nimport { useLocale } from \"./core/i18n\";\n\n// Lazy-load ExcelJS\nlet ExcelJS: typeof import(\"exceljs\") | null = null;\nasync function getExcelJS() {\n if (!ExcelJS) {\n ExcelJS = await import(\"exceljs\");\n }\n return ExcelJS;\n}\n\ninterface XlsxPreviewProps {\n content?: string | null;\n source?: PreviewSource;\n fileName: string;\n fileSize: number;\n}\n\ntype XlsxPreviewMode = \"fast\" | \"fidelity\";\n\ninterface CellStyle {\n fontFamily?: string;\n fontSize?: number;\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n fontColor?: string;\n bgColor?: string;\n hAlign?: string;\n vAlign?: string;\n wrapText?: boolean;\n textRotation?: number;\n borderTop?: string;\n borderRight?: string;\n borderBottom?: string;\n borderLeft?: string;\n numFmt?: string;\n hyperlink?: string;\n comment?: string;\n}\n\ninterface EmbeddedImage {\n dataUrl: string | null;\n naturalWidth: number;\n naturalHeight: number;\n unsupported?: boolean;\n formatName?: string;\n}\n\ninterface CellData {\n value: string;\n style: CellStyle;\n colspan?: number;\n rowspan?: number;\n images?: EmbeddedImage[];\n}\n\ninterface SheetData {\n name: string;\n cellGrid: (CellData | null)[][];\n colWidths: number[];\n rowHeights: number[];\n totalRows: number;\n totalCols: number;\n imageCount: number;\n accRowHeights: number[];\n isLegacyXls?: boolean;\n}\n\n// ---- Extended Theme Colors ----\nconst THEME_COLORS: Record<number, string> = {\n 0: \"#000000\", 1: \"#FFFFFF\", 2: \"#44546A\", 3: \"#E7E6E6\",\n 4: \"#4472C4\", 5: \"#ED7D31\", 6: \"#A5A5A5\", 7: \"#FFC000\",\n 8: \"#5B9BD5\", 9: \"#70AD47\",\n 10: \"#F2F2F2\", 11: \"#D9D9D9\", 12: \"#BFBFBF\", 13: \"#A6A6A6\",\n 14: \"#808080\", 15: \"#595959\", 16: \"#404040\", 17: \"#262626\",\n};\n\n// ---- Indexed Colors ----\nconst INDEXED_COLORS: string[] = [\n \"#000000\", \"#FFFFFF\", \"#FF0000\", \"#00FF00\", \"#0000FF\", \"#FFFF00\", \"#FF00FF\", \"#00FFFF\",\n \"#000000\", \"#FFFFFF\", \"#FF0000\", \"#00FF00\", \"#0000FF\", \"#FFFF00\", \"#FF00FF\", \"#00FFFF\",\n \"#800000\", \"#008000\", \"#000080\", \"#808000\", \"#800080\", \"#008080\", \"#C0C0C0\", \"#808080\",\n \"#9999FF\", \"#993366\", \"#FFFFCC\", \"#CCFFFF\", \"#660066\", \"#FF8080\", \"#0066CC\", \"#CCCCFF\",\n \"#000080\", \"#FF00FF\", \"#FFFF00\", \"#00FFFF\", \"#800080\", \"#800000\", \"#008080\", \"#0000FF\",\n \"#00CCFF\", \"#CCFFFF\", \"#CCFFCC\", \"#FFFF99\", \"#99CCFF\", \"#FF99CC\", \"#CC99FF\", \"#FFCC99\",\n \"#3366FF\", \"#33CCCC\", \"#99CC00\", \"#FFCC00\", \"#FF9900\", \"#FF6600\", \"#666699\", \"#969696\",\n \"#003366\", \"#339966\", \"#003300\", \"#333300\", \"#993300\", \"#993366\", \"#333399\", \"#333333\",\n];\n\nfunction applyTint(hex: string, tint: number | undefined): string {\n if (!tint) return hex;\n const r = parseInt(hex.slice(1, 3), 16);\n const g = parseInt(hex.slice(3, 5), 16);\n const b = parseInt(hex.slice(5, 7), 16);\n const t = (c: number) => tint < 0 ? Math.round(c * (1 + tint)) : Math.round(c + (255 - c) * tint);\n return `#${t(r).toString(16).padStart(2, \"0\")}${t(g).toString(16).padStart(2, \"0\")}${t(b).toString(16).padStart(2, \"0\")}`;\n}\n\nfunction resolveColor(color: any): string | undefined {\n if (!color) return undefined;\n if (color.argb) {\n const a = color.argb;\n if (!a || a === \"00000000\" || a === \"FFFFFFFF\") return undefined;\n return a.length === 8 ? \"#\" + a.slice(2).toLowerCase() : a.toLowerCase();\n }\n if (color.theme !== undefined) return applyTint(THEME_COLORS[color.theme] || \"#000000\", color.tint);\n if (color.indexed !== undefined && INDEXED_COLORS[color.indexed]) return applyTint(INDEXED_COLORS[color.indexed], color.tint);\n if (typeof color === \"string\") return color;\n return undefined;\n}\n\nfunction borderCss(part: any): string {\n if (!part?.style || part.style === \"none\") return \"\";\n const w = part.style === \"thin\" ? \"1px\" : part.style === \"medium\" ? \"2px\" :\n part.style === \"thick\" ? \"3px\" : part.style === \"hair\" ? \"0.5px\" :\n part.style === \"double\" ? \"3px\" : part.style.startsWith(\"medium\") ? \"2px\" : \"1px\";\n const c = resolveColor(part.color) || \"#000000\";\n const s = /dashed|dotted|dashDot/.test(part.style) ? \"dashed\" : \"solid\";\n return `${w} ${s} ${c}`;\n}\n\n// ---- Image helpers ----\nfunction parseImageDimensions(buffer: any): { width: number; height: number } {\n const bytes = buffer instanceof Uint8Array ? buffer\n : buffer instanceof ArrayBuffer ? new Uint8Array(buffer)\n : new Uint8Array(Buffer.from(buffer));\n if (bytes.length < 10) return { width: 0, height: 0 };\n if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47 && bytes.length > 24) {\n return { width: (bytes[16] << 24) | (bytes[17] << 16) | (bytes[18] << 8) | bytes[19], height: (bytes[20] << 24) | (bytes[21] << 16) | (bytes[22] << 8) | bytes[23] };\n }\n if (bytes[0] === 0xFF && bytes[1] === 0xD8) {\n for (let i = 0; i < Math.min(bytes.length - 9, 65536); i++) {\n if (bytes[i] === 0xFF) {\n const m = bytes[i + 1];\n if (m >= 0xC0 && m <= 0xCF && m !== 0xC4 && m !== 0xC8 && m !== 0xCC) {\n return { height: (bytes[i + 5] << 8) | bytes[i + 6], width: (bytes[i + 7] << 8) | bytes[i + 8] };\n }\n }\n }\n }\n if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46) {\n return { width: bytes[6] | (bytes[7] << 8), height: bytes[8] | (bytes[9] << 8) };\n }\n if (bytes[0] === 0x42 && bytes[1] === 0x4D && bytes.length > 25) {\n return { width: bytes[18] | (bytes[19] << 8) | (bytes[20] << 16) | (bytes[21] << 24), height: bytes[22] | (bytes[23] << 8) | (bytes[24] << 16) | (bytes[25] << 24) };\n }\n return { width: 0, height: 0 };\n}\n\ntype ImageFormat = \"png\" | \"jpeg\" | \"gif\" | \"bmp\" | \"emf\" | \"wmf\" | \"tiff\" | \"webp\" | \"svg\" | \"unknown\";\nconst BROWSER_SUPPORTED_FORMATS = new Set([\"png\", \"jpeg\", \"gif\", \"bmp\", \"webp\", \"svg\"]);\n\nfunction detectImageFormat(buffer: any): ImageFormat {\n const bytes = buffer instanceof Uint8Array ? buffer\n : buffer instanceof ArrayBuffer ? new Uint8Array(buffer)\n : new Uint8Array(Buffer.from(buffer));\n if (bytes.length < 4) return \"unknown\";\n if (bytes[0] === 0x89 && bytes[1] === 0x50) return \"png\";\n if (bytes[0] === 0xFF && bytes[1] === 0xD8) return \"jpeg\";\n if (bytes[0] === 0x47 && bytes[1] === 0x49) return \"gif\";\n if (bytes[0] === 0x42 && bytes[1] === 0x4D) return \"bmp\";\n if ((bytes[0] === 0x49 && bytes[1] === 0x49 && bytes[2] === 0x2A) || (bytes[0] === 0x4D && bytes[1] === 0x4D && bytes[2] === 0x00)) return \"tiff\";\n if (bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46 && bytes.length > 11 && bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50) return \"webp\";\n if (bytes[0] === 0x01 && bytes[1] === 0x00 && bytes[2] === 0x00 && bytes[3] === 0x00 && bytes.length > 44) return \"emf\";\n if ((bytes[0] === 0xD7 && bytes[1] === 0xCD && bytes[2] === 0xC6 && bytes[3] === 0x9A) || (bytes[0] === 0x01 && bytes[1] === 0x00 && bytes[2] === 0x09 && bytes[3] === 0x00)) return \"wmf\";\n return \"unknown\";\n}\n\nfunction getMimeType(format: ImageFormat): string {\n const map: Record<string, string> = { png: \"image/png\", jpeg: \"image/jpeg\", gif: \"image/gif\", bmp: \"image/bmp\", webp: \"image/webp\", svg: \"image/svg+xml\", tiff: \"image/tiff\" };\n return map[format] || \"image/png\";\n}\n\nfunction bufferToBase64(buf: any): string {\n if (buf instanceof Uint8Array || buf instanceof ArrayBuffer) {\n const arr = buf instanceof Uint8Array ? buf : new Uint8Array(buf);\n let bin = \"\";\n for (let i = 0; i < arr.length; i += 8192) {\n const chunk = arr.subarray(i, Math.min(i + 8192, arr.length));\n bin += String.fromCharCode(...chunk);\n }\n return btoa(bin);\n }\n if (typeof buf === \"string\") return buf;\n return (buf as any).toString(\"base64\");\n}\n\n// ---- Cell Value Formatting ----\nfunction formatCellValue(cell: any): string {\n const v = cell.value;\n if (v === null || v === undefined) return \"\";\n if (v instanceof Error) return v.message || \"#ERROR\";\n if (typeof v === \"object\" && v !== null && \"richText\" in v) return v.richText.map((r: any) => r.text).join(\"\");\n if (typeof v === \"object\" && v !== null && \"formula\" in v) {\n const r = v.result;\n return r !== null && r !== undefined ? String(r) : \"\";\n }\n if (typeof v === \"object\" && v !== null && \"hyperlink\" in v) return v.text || v.hyperlink;\n if (v instanceof Date) return formatDateValue(v, cell.numFmt);\n if (typeof v === \"number\") return formatNumberValue(v, cell.numFmt);\n return String(v);\n}\n\nfunction formatDateValue(d: Date, fmt: string | undefined): string {\n if (!fmt || !/[yYmdhHs]/.test(fmt)) return d.toLocaleDateString(\"zh-CN\");\n try {\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n const monthsShort = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n const daysShort = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n let res = fmt;\n const is12Hour = /am\\/pm/i.test(res);\n res = res.replace(/AM\\/PM/gi, \"\").replace(/A\\/P/gi, \"\");\n res = res.replace(/mmmm/g, monthsShort[d.getMonth()]);\n res = res.replace(/mmm/g, monthsShort[d.getMonth()]);\n res = res.replace(/dddd/g, daysShort[d.getDay()]);\n res = res.replace(/ddd/g, daysShort[d.getDay()]);\n res = res.replace(/yyyy/g, d.getFullYear().toString());\n res = res.replace(/yy/g, d.getFullYear().toString().slice(-2));\n const parts = res.split(/hh/i);\n if (parts.length > 1) {\n parts[0] = parts[0].replace(/mm/g, pad(d.getMonth() + 1));\n parts[1] = parts[1].replace(/mm/g, pad(d.getMinutes()));\n res = parts.join(\"hh\");\n } else {\n res = res.replace(/mm/g, pad(d.getMonth() + 1));\n }\n res = res.replace(/dd/g, pad(d.getDate()));\n let hours = d.getHours();\n if (is12Hour) { const ampm = hours >= 12 ? \"PM\" : \"AM\"; hours = hours % 12 || 12; res += \" \" + ampm; }\n res = res.replace(/hh/gi, pad(hours));\n res = res.replace(/ss/g, pad(d.getSeconds()));\n res = res.replace(/[\\\\]/g, \"\").replace(/[\\[\\]]/g, \"\");\n return res;\n } catch { return d.toLocaleDateString(\"zh-CN\"); }\n}\n\nfunction formatNumberValue(v: number, fmt: string | undefined): string {\n if (!fmt || fmt === \"General\" || fmt === \"@\") return v.toString();\n if (fmt.includes(\"%\")) { const z = fmt.split(\"%\")[0].match(/0/g)?.length ?? 1; return (v * 100).toFixed(Math.max(0, z - 1)) + \"%\"; }\n if (fmt.includes(\"/\")) return formatFraction(v, fmt);\n if (/e\\+/i.test(fmt)) { const dec = fmt.split(/[eE]/)[0].split(\".\")[1]?.replace(/[^0]/g, \"\").length || 0; return v.toExponential(dec); }\n const hasCurrency = /[$¥€£]/.test(fmt);\n const currencySymbol = fmt.match(/[$¥€£]/)?.[0] || \"\";\n const hasThousands = fmt.includes(\"#,##0\") || fmt.includes(\"# ##0\") || fmt.includes(\",0\");\n const decPart = fmt.split(\".\")[1];\n const decPlaces = decPart ? (decPart.match(/0/g) || []).length : 0;\n const hasNegativeParens = fmt.includes(\");(\") || fmt.includes(\");-(\");\n let result: string;\n if (hasThousands) { result = Math.abs(v).toLocaleString(\"en-US\", { minimumFractionDigits: decPlaces, maximumFractionDigits: decPlaces }); }\n else if (decPlaces > 0) { result = Math.abs(v).toFixed(decPlaces); }\n else { result = Math.abs(v).toString(); }\n if (v < 0) result = hasNegativeParens ? `(${result})` : `-${result}`;\n if (hasCurrency) { if (fmt.indexOf(currencySymbol) < fmt.indexOf(\"0\") || fmt.indexOf(currencySymbol) < fmt.indexOf(\"#\")) result = currencySymbol + result; else result = result + currencySymbol; }\n return result;\n}\n\nfunction formatFraction(v: number, fmt: string): string {\n const denom = fmt.includes(\"??/??\") ? 100 : fmt.includes(\"?/?\") || fmt.includes(\"?/\") ? 10 : 10;\n const whole = Math.floor(Math.abs(v));\n const frac = Math.abs(v) - whole;\n const num = Math.round(frac * denom);\n if (num === 0) return whole.toString();\n const g = gcd(num, denom);\n const n = num / g, d = denom / g;\n if (whole > 0) return `${whole} ${n}/${d}`;\n return (v < 0 ? \"-\" : \"\") + `${n}/${d}`;\n}\n\nfunction gcd(a: number, b: number): number { while (b) { [a, b] = [b, a % b]; } return a; }\n\n// ---- Style Extraction ----\nfunction extractStyle(cell: any): CellStyle {\n const s: CellStyle = {};\n const f = cell.font;\n if (f) {\n if (f.name) s.fontFamily = f.name;\n if (f.size) s.fontSize = f.size;\n if (f.bold) s.bold = true;\n if (f.italic) s.italic = true;\n if (f.underline) s.underline = true;\n const fc = resolveColor(f.color); if (fc) s.fontColor = fc;\n }\n const fill = cell.fill;\n if (fill?.pattern && fill.pattern !== \"none\") {\n const fg = resolveColor(fill.fgColor), bg = resolveColor(fill.bgColor);\n s.bgColor = fg || bg;\n }\n const a = cell.alignment;\n if (a) {\n if (a.horizontal && a.horizontal !== \"fill\") s.hAlign = a.horizontal;\n if (a.vertical) s.vAlign = a.vertical;\n if (a.wrapText) s.wrapText = true;\n if (a.textRotation) s.textRotation = a.textRotation;\n }\n const b = cell.border;\n if (b) {\n if (b.top) s.borderTop = borderCss(b.top);\n if (b.right) s.borderRight = borderCss(b.right);\n if (b.bottom) s.borderBottom = borderCss(b.bottom);\n if (b.left) s.borderLeft = borderCss(b.left);\n }\n if (cell.numFmt) s.numFmt = cell.numFmt;\n if (cell.hyperlink) {\n s.hyperlink = typeof cell.hyperlink === \"string\" ? cell.hyperlink : cell.hyperlink.target;\n } else if (cell.value !== null && typeof cell.value === \"object\" && \"hyperlink\" in cell.value) {\n s.hyperlink = cell.value.hyperlink;\n }\n if (cell.note) {\n if (typeof cell.note === \"string\") s.comment = cell.note;\n else if (cell.note.texts) s.comment = cell.note.texts.map((t: any) => t.text || t).join(\"\");\n }\n return s;\n}\n\nfunction styleToCss(style: CellStyle): React.CSSProperties {\n const css: React.CSSProperties = {};\n if (style.fontFamily) css.fontFamily = `\"${style.fontFamily}\", sans-serif`;\n if (style.fontSize) css.fontSize = `${style.fontSize}px`;\n if (style.bold) css.fontWeight = \"bold\";\n if (style.italic) css.fontStyle = \"italic\";\n if (style.underline) css.textDecoration = \"underline\";\n if (style.fontColor) css.color = style.fontColor;\n if (style.bgColor && !/^#ffffff$/i.test(style.bgColor)) css.backgroundColor = style.bgColor;\n if (style.hAlign) css.textAlign = style.hAlign as any;\n if (style.vAlign) css.verticalAlign = style.vAlign === \"middle\" ? \"middle\" : style.vAlign as any;\n if (style.wrapText) css.whiteSpace = \"pre-wrap\";\n if (style.textRotation) {\n css[style.textRotation === 255 ? \"writingMode\" : \"transform\"] =\n style.textRotation === 255 ? \"vertical-rl\" as any : `rotate(-${style.textRotation}deg)`;\n }\n return css;\n}\n\nconst ROW_NUM_COL_WIDTH = 45;\nconst HEADER_ROW_HEIGHT = 22;\nconst DEFAULT_ROW_HEIGHT = 22;\nconst DEFAULT_COL_WIDTH = 80;\n// Fidelity mode DOM rendering protection; fast mode is already limited by FAST_MODE_ROW_LIMIT at parse time.\nconst MAX_RENDER_ROWS = 5000;\n\n// ---- Main Parser ----\nasync function parseXlsx(\n input: { source?: PreviewSource; content?: string | null },\n fileName: string,\n mode: XlsxPreviewMode\n): Promise<SheetData[]> {\n const EJS = await getExcelJS();\n const ext = fileName.toLowerCase().split(\".\").pop() || \"\";\n const isLegacyXls = ext === \"xls\";\n\n const bytes = await readBinaryPreviewAsUint8Array(input);\n\n const workbook = new EJS.Workbook();\n // exceljs's typings ask for Node's Buffer, but its browser bundle\n // accepts any ArrayBufferView at runtime. Pass the underlying ArrayBuffer\n // to satisfy both layers without a structured cast.\n await workbook.xlsx.load(bytes.buffer as ArrayBuffer);\n\n const sheets: SheetData[] = [];\n const isFast = mode === \"fast\";\n const fastRowLimit = isFast ? XLSX_PREVIEW_LIMITS.FAST_MODE_ROW_LIMIT : Infinity;\n\n workbook.eachSheet((worksheet: any) => {\n const rowCount = worksheet.rowCount;\n const colCount = worksheet.columnCount;\n\n if (rowCount === 0 || colCount === 0) {\n sheets.push({ name: worksheet.name, cellGrid: [], colWidths: [], rowHeights: [], totalRows: 0, totalCols: 0, imageCount: 0, accRowHeights: [], isLegacyXls });\n return;\n }\n\n const effectiveRowCount = Math.min(rowCount, fastRowLimit);\n\n // Column widths\n const colWidths: number[] = [];\n for (let c = 1; c <= colCount; c++) {\n const col = worksheet.getColumn(c);\n colWidths.push(col.width ? Math.round(Math.max(col.width * 7.5, 50)) : DEFAULT_COL_WIDTH);\n }\n\n // Row heights (only up to effectiveRowCount in fast mode)\n const rowHeights: number[] = [];\n for (let r = 1; r <= effectiveRowCount; r++) {\n const row = worksheet.getRow(r);\n rowHeights.push(row.height ? Math.round(row.height * 1.333) : 0);\n }\n\n // Accumulated row heights\n const accRowHeights: number[] = [];\n let accRow = 0;\n for (let r = 0; r < rowHeights.length; r++) { accRowHeights.push(accRow); accRow += rowHeights[r] || DEFAULT_ROW_HEIGHT; }\n accRowHeights.push(accRow);\n\n // ---- Merge ranges ----\n const merges: Map<string, { rs: number; cs: number }> = new Map();\n const mergedCells = new Set<string>();\n // Map from any cell in a merge to the top-left cell key\n const mergeRedirect: Map<string, string> = new Map();\n\n const _merges = (worksheet as any)._merges;\n if (_merges) {\n for (const [, merge] of Object.entries(_merges) as [string, any][]) {\n const m = merge?.model || merge;\n if (m?.top != null && m?.left != null) {\n const tlKey = `${m.top - 1}-${m.left - 1}`;\n merges.set(tlKey, { rs: m.bottom - m.top + 1, cs: m.right - m.left + 1 });\n for (let r = m.top; r <= m.bottom; r++) {\n for (let c = m.left; c <= m.right; c++) {\n if (r !== m.top || c !== m.left) {\n const mergedKey = `${r - 1}-${c - 1}`;\n mergedCells.add(mergedKey);\n mergeRedirect.set(mergedKey, tlKey);\n }\n }\n }\n }\n }\n }\n const modelMerges = (worksheet as any).model?.merges;\n if (modelMerges?.length > 0 && typeof modelMerges[0] === \"string\") {\n const colLetterToNum = (s: string) => { let n = 0; for (let i = 0; i < s.length; i++) n = n * 26 + (s.charCodeAt(i) - 64); return n; };\n for (const rangeStr of modelMerges) {\n const match = (rangeStr as string).match(/^([A-Z]+)(\\d+):([A-Z]+)(\\d+)$/);\n if (match) {\n const top = parseInt(match[2]), left = colLetterToNum(match[1]);\n const bottom = parseInt(match[4]), right = colLetterToNum(match[3]);\n const tlKey = `${top - 1}-${left - 1}`;\n if (!merges.has(tlKey)) {\n merges.set(tlKey, { rs: bottom - top + 1, cs: right - left + 1 });\n for (let r = top; r <= bottom; r++) {\n for (let c = left; c <= right; c++) {\n if (r !== top || c !== left) {\n const mergedKey = `${r - 1}-${c - 1}`;\n mergedCells.add(mergedKey);\n mergeRedirect.set(mergedKey, tlKey);\n }\n }\n }\n }\n }\n }\n }\n\n // ---- Extract images & group by anchor cell (fidelity mode only) ----\n const cellImages = new Map<string, EmbeddedImage[]>();\n let totalImages = 0;\n\n if (!isFast) {\n try {\n const wsImages = worksheet.getImages();\n for (const img of wsImages) {\n const imageId = parseInt(img.imageId, 10);\n if (isNaN(imageId)) continue;\n const imageData = workbook.getImage(imageId);\n if (!imageData?.buffer) continue;\n\n const buf = imageData.buffer;\n const format = detectImageFormat(buf);\n const isSupported = BROWSER_SUPPORTED_FORMATS.has(format);\n const mimeType = getMimeType(format);\n const base64 = bufferToBase64(buf);\n\n const range = img.range;\n if (!range?.tl) continue;\n\n const tlRow = range.tl.nativeRow ?? 0;\n const tlCol = range.tl.nativeCol ?? 0;\n const key = `${tlRow}-${tlCol}`;\n\n const effectiveKey = mergeRedirect.get(key) || key;\n\n const dims = parseImageDimensions(buf);\n\n const embedded: EmbeddedImage = {\n dataUrl: isSupported ? `data:${mimeType};base64,${base64}` : null,\n naturalWidth: dims.width,\n naturalHeight: dims.height,\n unsupported: !isSupported,\n formatName: format.toUpperCase(),\n };\n\n if (!cellImages.has(effectiveKey)) cellImages.set(effectiveKey, []);\n cellImages.get(effectiveKey)!.push(embedded);\n totalImages++;\n }\n } catch (err) {\n console.warn(\"Image extraction error:\", err);\n }\n }\n\n // ---- Build cell grid ----\n const cellGrid: (CellData | null)[][] = [];\n\n for (let r = 0; r < effectiveRowCount; r++) {\n const row: (CellData | null)[] = [];\n const excelRow = worksheet.getRow(r + 1);\n\n for (let c = 0; c < colCount; c++) {\n const key = `${r}-${c}`;\n if (mergedCells.has(key)) { row.push(null); continue; }\n\n const cell = excelRow.getCell(c + 1);\n const value = formatCellValue(cell);\n const style: CellStyle = isFast ? {} : extractStyle(cell);\n const merge = merges.get(key);\n const images = cellImages.get(key);\n\n row.push({\n value,\n style,\n colspan: merge?.cs,\n rowspan: merge?.rs,\n images: images && images.length > 0 ? images : undefined,\n });\n }\n cellGrid.push(row);\n }\n\n sheets.push({\n name: worksheet.name,\n cellGrid,\n colWidths,\n rowHeights,\n totalRows: rowCount,\n totalCols: colCount,\n imageCount: totalImages,\n accRowHeights,\n isLegacyXls,\n });\n });\n\n return sheets;\n}\n\nfunction colNumToLetter(num: number): string {\n let result = \"\";\n num++;\n while (num > 0) {\n num--;\n result = String.fromCharCode(65 + (num % 26)) + result;\n num = Math.floor(num / 26);\n }\n return result;\n}\n\nfunction useDebounce<T>(value: T, delay: number): T {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n const timer = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n return debounced;\n}\n\nexport function XlsxPreview({ content, source, fileName, fileSize }: XlsxPreviewProps) {\n const [sheets, setSheets] = useState<SheetData[]>([]);\n const [activeSheet, setActiveSheet] = useState(0);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [zoom, setZoom] = useState(100);\n const scrollRef = useRef<HTMLDivElement>(null);\n const [hoveredComment, setHoveredComment] = useState<{ row: number; col: number; text: string; x: number; y: number } | null>(null);\n const t = useLocale();\n\n const [mode, setMode] = useState<XlsxPreviewMode>(() => {\n return fileSize > XLSX_PREVIEW_LIMITS.LARGE_FILE_SIZE ? \"fast\" : \"fidelity\";\n });\n\n const isLargeFile = fileSize > XLSX_PREVIEW_LIMITS.LARGE_FILE_SIZE;\n const isTooLargeForFidelity = fileSize > XLSX_PREVIEW_LIMITS.MAX_FIDELITY_FILE_SIZE;\n\n const switchMode = useCallback(\n (nextMode: XlsxPreviewMode) => {\n if (nextMode === mode) return;\n if (nextMode === \"fidelity\" && isTooLargeForFidelity) {\n const confirmed = window.confirm(\n t.largeFileFidelityConfirm.replace(\"{fileSize}\", formatFileSize(fileSize))\n );\n if (!confirmed) return;\n }\n setMode(nextMode);\n },\n [mode, isTooLargeForFidelity, fileSize]\n );\n\n const debouncedSearch = useDebounce(searchTerm, 300);\n\n // Reset loading state during render when inputs change — derived state, not effect\n const [prevDeps, setPrevDeps] = useState({ content, source, fileName, mode });\n if (\n prevDeps.content !== content ||\n prevDeps.source !== source ||\n prevDeps.fileName !== fileName ||\n prevDeps.mode !== mode\n ) {\n setPrevDeps({ content, source, fileName, mode });\n setLoading(true);\n setError(null);\n }\n\n useEffect(() => {\n let cancelled = false;\n parseXlsx({ source, content }, fileName, mode).then(\n (result) => {\n if (cancelled) return;\n setSheets(result);\n setLoading(false);\n },\n (err) => {\n if (cancelled) return;\n console.error(\"XLSX parse error:\", err);\n const ext = fileName.toLowerCase().split(\".\").pop() || \"\";\n if (ext === \"xls\") {\n setError(t.legacyXlsError);\n } else {\n setError(err instanceof Error ? err.message : \"Failed to parse spreadsheet\");\n }\n setLoading(false);\n }\n );\n return () => {\n cancelled = true;\n };\n }, [content, source, fileName, mode]);\n\n useEffect(() => {\n if (scrollRef.current) scrollRef.current.scrollTop = 0;\n }, [activeSheet]);\n\n const currentSheet = sheets[activeSheet];\n\n const filteredRowIndices = useMemo(() => {\n if (!currentSheet || !debouncedSearch) return null;\n const indices: number[] = [];\n const term = debouncedSearch.toLowerCase();\n currentSheet.cellGrid.forEach((row, idx) => {\n const match = row.some((cell) => cell && (cell.value.toLowerCase().includes(term) || (cell.images && cell.images.length > 0)));\n if (match) indices.push(idx);\n });\n return indices;\n }, [currentSheet, debouncedSearch]);\n\n const showLegacyWarning = currentSheet?.isLegacyXls && !error;\n\n if (loading) {\n return (\n <div className=\"fv-xlsx__state\">\n <div className=\"fv-spinner fv-spinner--lg\" />\n <p className=\"fv-xlsx__state-msg\">{t.loadingSpreadsheet}</p>\n </div>\n );\n }\n if (error) {\n return (\n <div className=\"fv-xlsx__state fv-xlsx__state--error\">\n <AlertTriangleIcon size={36} />\n <p className=\"fv-xlsx__state-title\">{t.parseFailed}</p>\n <p className=\"fv-xlsx__state-msg\">{error}</p>\n </div>\n );\n }\n if (sheets.length === 0) {\n return (\n <div className=\"fv-xlsx__state fv-xlsx__state--empty\">\n <p className=\"fv-xlsx__state-title\">{t.sheetNotFound}</p>\n </div>\n );\n }\n\n const rows = currentSheet?.cellGrid || [];\n const isSearch = !!filteredRowIndices;\n const allDisplayRows = isSearch\n ? filteredRowIndices!.map((idx) => ({ row: rows[idx], originalIdx: idx }))\n : rows.map((row, idx) => ({ row, originalIdx: idx }));\n\n const isTruncated = !isSearch && allDisplayRows.length > MAX_RENDER_ROWS;\n const displayRows = isTruncated ? allDisplayRows.slice(0, MAX_RENDER_ROWS) : allDisplayRows;\n\n const totalCols = currentSheet?.totalCols || 0;\n const allColWidths = currentSheet?.colWidths || [];\n\n return (\n <div className=\"fv-xlsx\">\n {showLegacyWarning && (\n <div className=\"fv-xlsx__legacy-banner\">\n <AlertTriangleIcon size={14} />\n <span>{t.legacyXlsFallbackDesc}</span>\n </div>\n )}\n\n {/* Toolbar */}\n <div className=\"fv-xlsx__toolbar\">\n <div className=\"fv-xlsx__toolbar-left\">\n <Table2Icon size={14} />\n {sheets.length > 1 && (\n <div className=\"fv-xlsx__sheet-tabs\">\n {sheets.map((sheet, i) => (\n <button key={i} onClick={() => { setActiveSheet(i); setSearchTerm(\"\"); }}\n className={`fv-xlsx__sheet-tab ${i === activeSheet ? \"fv-xlsx__sheet-tab--active\" : \"\"}`}>\n {sheet.name}\n </button>\n ))}\n </div>\n )}\n {/* Mode switch */}\n <div className=\"fv-xlsx__mode-switch\">\n <button\n onClick={() => switchMode(\"fast\")}\n className={`fv-xlsx__mode-btn ${mode === \"fast\" ? \"fv-xlsx__mode-btn--active\" : \"\"}`}\n title={t.fastModeTitle}\n >\n {t.fastMode}\n </button>\n <button\n onClick={() => switchMode(\"fidelity\")}\n className={`fv-xlsx__mode-btn ${mode === \"fidelity\" ? \"fv-xlsx__mode-btn--active\" : \"\"}`}\n title={t.fidelityModeTitle}\n >\n {t.fidelityMode}\n </button>\n </div>\n </div>\n <div className=\"fv-xlsx__toolbar-right\">\n <div className=\"fv-xlsx__search-wrap\">\n <SearchIcon size={14} className=\"fv-xlsx__search-icon\" />\n <input type=\"text\" placeholder={t.search} value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n className=\"fv-xlsx__search-input\" />\n </div>\n <div className=\"fv-xlsx__zoom-group\">\n <button onClick={() => setZoom(Math.max(50, zoom - 10))} className=\"fv-xlsx__zoom-btn\" title={t.zoomOut}><ZoomOutIcon size={14} /></button>\n <span className=\"fv-xlsx__zoom-label\">{zoom}%</span>\n <button onClick={() => setZoom(Math.min(200, zoom + 10))} className=\"fv-xlsx__zoom-btn\" title={t.zoomIn}><ZoomInIcon size={14} /></button>\n </div>\n <span className=\"fv-xlsx__info\">\n {mode === \"fast\" && currentSheet?.totalRows > XLSX_PREVIEW_LIMITS.FAST_MODE_ROW_LIMIT ? (\n <>\n {XLSX_PREVIEW_LIMITS.FAST_MODE_ROW_LIMIT.toLocaleString()} / {currentSheet.totalRows.toLocaleString()} {t.largeFileRows} × {currentSheet.totalCols} {t.largeFileCols}\n </>\n ) : (\n <>\n {currentSheet?.totalRows?.toLocaleString() || 0} {t.largeFileRows} × {currentSheet?.totalCols || 0} {t.largeFileCols}\n {currentSheet?.imageCount ? ` · ${currentSheet.imageCount} ${t.largeFileImages}` : \"\"}\n </>\n )}\n </span>\n </div>\n </div>\n\n {/* Large file banners */}\n {isLargeFile && mode === \"fast\" && (\n <div className=\"fv-xlsx__large-banner fv-xlsx__large-banner--warning\">\n {t.largeFileFastModeBanner.replace(\"{fileSize}\", formatFileSize(fileSize)).replace(\"{rowLimit}\", XLSX_PREVIEW_LIMITS.FAST_MODE_ROW_LIMIT.toLocaleString())}\n </div>\n )}\n {isLargeFile && mode === \"fidelity\" && (\n <div className=\"fv-xlsx__large-banner fv-xlsx__large-banner--danger\">\n {t.largeFileFidelityBanner}\n </div>\n )}\n\n {/* Table */}\n <div ref={scrollRef} className=\"fv-xlsx__content\">\n <div style={{ zoom: zoom / 100 }}>\n <table className=\"fv-xlsx__table\" style={{ tableLayout: \"fixed\" }}>\n <colgroup>\n <col style={{ width: ROW_NUM_COL_WIDTH }} />\n {allColWidths.map((w, i) => (<col key={i} style={{ width: w }} />))}\n </colgroup>\n <thead>\n <tr>\n <th className=\"fv-xlsx__col-header\" />\n {allColWidths.map((w, i) => (\n <th key={i} className=\"fv-xlsx__col-header\">\n {colNumToLetter(i)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {displayRows.map(({ row, originalIdx }) => {\n if (!row) return null;\n const rh = currentSheet?.rowHeights[originalIdx] || 0;\n\n // Calculate min height for images in this row\n let imgMinHeight = 0;\n for (const cell of row) {\n if (cell?.images) {\n for (const img of cell.images) {\n const h = img.unsupported ? 40 : (img.naturalHeight || 60);\n imgMinHeight = Math.max(imgMinHeight, h + 8);\n }\n }\n }\n const effectiveHeight = Math.max(rh, imgMinHeight) || undefined;\n\n return (\n <tr key={originalIdx} style={effectiveHeight ? { height: effectiveHeight } : undefined}>\n <td className=\"fv-xlsx__row-num\">\n {originalIdx + 1}\n </td>\n {row.map((cell, colIdx) => {\n if (!cell) return null;\n const cs = styleToCss(cell.style);\n const db = \"1px solid #d1d5db\";\n const fs: React.CSSProperties = {\n ...cs, padding: \"1px 4px\", overflow: \"visible\",\n whiteSpace: cs.whiteSpace || \"nowrap\", position: \"relative\",\n borderTop: cell.style.borderTop || db, borderRight: cell.style.borderRight || db,\n borderBottom: cell.style.borderBottom || db, borderLeft: cell.style.borderLeft || db,\n };\n\n const hasImages = cell.images && cell.images.length > 0;\n const hasHyperlink = !!cell.style.hyperlink;\n const hasComment = !!cell.style.comment;\n\n return (\n <td key={colIdx} style={fs} rowSpan={cell.rowspan || undefined} colSpan={cell.colspan || undefined}>\n {hasImages ? (\n <div className=\"fv-xlsx__cell-images\" style={{ minHeight: 30 }}>\n {cell.images!.map((img, imgIdx) => (\n img.unsupported ? (\n <div key={imgIdx}\n className=\"fv-xlsx__cell-image-placeholder\"\n style={{ width: 60, height: 40 }}\n title={`${t.unsupportedImageFormat}: ${img.formatName || t.unknown}`}\n >\n <ImageOffIcon size={14} />\n <span style={{ fontSize: 8, color: \"#9ca3af\" }}>{img.formatName}</span>\n </div>\n ) : (\n <img key={imgIdx} src={img.dataUrl!} alt=\"\"\n style={{\n width: img.naturalWidth || \"auto\",\n height: img.naturalHeight || \"auto\",\n maxWidth: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n }}\n loading=\"lazy\"\n />\n )\n ))}\n {cell.value?.trim() && (\n <span className=\"fv-xlsx__cell-image-label\">{cell.value}</span>\n )}\n </div>\n ) : hasHyperlink ? (\n <a href={cell.style.hyperlink} target=\"_blank\" rel=\"noopener noreferrer\"\n className=\"fv-xlsx__cell-link\"\n style={{ fontSize: \"inherit\", fontFamily: \"inherit\" }}>\n {cell.value}\n <ExternalLinkIcon size={10} />\n </a>\n ) : (\n cell.value\n )}\n {hasComment && (\n <span className=\"fv-xlsx__comment-dot\"\n onMouseEnter={(e) => {\n const rect = e.currentTarget.getBoundingClientRect();\n const container = scrollRef.current?.getBoundingClientRect();\n setHoveredComment({\n row: originalIdx, col: colIdx, text: cell.style.comment!,\n x: rect.left - (container?.left ?? 0) + rect.width / 2,\n y: rect.top - (container?.top ?? 0),\n });\n }}\n onMouseLeave={() => setHoveredComment(null)}\n />\n )}\n </td>\n );\n })}\n </tr>\n );\n })}\n {isTruncated && (\n <tr>\n <td colSpan={totalCols + 1} className=\"fv-xlsx__truncation-row fv-xlsx__truncation-row--warning\">\n {t.truncatedRows.replace(\"{shown}\", MAX_RENDER_ROWS.toLocaleString()).replace(\"{total}\", allDisplayRows.length.toLocaleString())}\n </td>\n </tr>\n )}\n {displayRows.length === 0 && (\n <tr>\n <td colSpan={totalCols + 1} className=\"fv-xlsx__truncation-row fv-xlsx__truncation-row--empty\">\n {searchTerm ? t.noSearchResults : t.noData}\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n </div>\n\n {/* Comment tooltip */}\n {hoveredComment && (\n <div className=\"fv-xlsx__comment-tooltip\"\n style={{ left: hoveredComment.x, top: hoveredComment.y - 8, transform: \"translate(-50%, -100%)\" }}>\n <div className=\"fv-xlsx__comment-tooltip-header\">\n <MessageSquareIcon size={10} /> {t.comment}\n </div>\n <p className=\"fv-xlsx__comment-tooltip-text\">{hoveredComment.text}</p>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import JSZip from "jszip";
|
|
4
|
+
import { FileIcon, FolderOpenIcon } from "./icons";
|
|
5
|
+
import { formatFileSize, base64ToUint8Array } from "./utils";
|
|
6
|
+
import "./styles/ZipPreview.css";
|
|
7
|
+
async function parseZipFile(base64Content) {
|
|
8
|
+
const bytes = base64ToUint8Array(base64Content);
|
|
9
|
+
const zip = await JSZip.loadAsync(bytes);
|
|
10
|
+
const entries = [];
|
|
11
|
+
zip.forEach((relativePath, file) => {
|
|
12
|
+
const parts = relativePath.split("/").filter(Boolean);
|
|
13
|
+
const name = parts[parts.length - 1] || relativePath;
|
|
14
|
+
const ext = name.includes(".") ? name.split(".").pop().toLowerCase() : "";
|
|
15
|
+
const depth = parts.length - 1;
|
|
16
|
+
const internal = file._data;
|
|
17
|
+
const size = internal?.uncompressedSize ?? 0;
|
|
18
|
+
entries.push({
|
|
19
|
+
path: relativePath,
|
|
20
|
+
name,
|
|
21
|
+
size,
|
|
22
|
+
isDir: file.dir,
|
|
23
|
+
depth,
|
|
24
|
+
ext
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
entries.sort((a, b) => {
|
|
28
|
+
if (a.isDir && !b.isDir) return -1;
|
|
29
|
+
if (!a.isDir && b.isDir) return 1;
|
|
30
|
+
return a.path.localeCompare(b.path);
|
|
31
|
+
});
|
|
32
|
+
return entries;
|
|
33
|
+
}
|
|
34
|
+
function getFileIcon(entry) {
|
|
35
|
+
if (entry.isDir) return "\u{1F4C1}";
|
|
36
|
+
const iconMap = {
|
|
37
|
+
pdf: "\u{1F4C4}",
|
|
38
|
+
doc: "\u{1F4C3}",
|
|
39
|
+
docx: "\u{1F4C3}",
|
|
40
|
+
ppt: "\u{1F4CA}",
|
|
41
|
+
pptx: "\u{1F4CA}",
|
|
42
|
+
xls: "\u{1F4CA}",
|
|
43
|
+
xlsx: "\u{1F4CA}",
|
|
44
|
+
jpg: "\u{1F5BC}\uFE0F",
|
|
45
|
+
jpeg: "\u{1F5BC}\uFE0F",
|
|
46
|
+
png: "\u{1F5BC}\uFE0F",
|
|
47
|
+
gif: "\u{1F5BC}\uFE0F",
|
|
48
|
+
svg: "\u{1F5BC}\uFE0F",
|
|
49
|
+
webp: "\u{1F5BC}\uFE0F",
|
|
50
|
+
mp4: "\u{1F3AC}",
|
|
51
|
+
mp3: "\u{1F3B5}",
|
|
52
|
+
zip: "\u{1F4E6}",
|
|
53
|
+
rar: "\u{1F4E6}",
|
|
54
|
+
"7z": "\u{1F4E6}",
|
|
55
|
+
tar: "\u{1F4E6}",
|
|
56
|
+
gz: "\u{1F4E6}",
|
|
57
|
+
js: "\u{1F4BB}",
|
|
58
|
+
ts: "\u{1F4BB}",
|
|
59
|
+
py: "\u{1F4BB}",
|
|
60
|
+
html: "\u{1F310}",
|
|
61
|
+
css: "\u{1F3A8}",
|
|
62
|
+
json: "\u{1F527}",
|
|
63
|
+
xml: "\u{1F527}",
|
|
64
|
+
md: "\u{1F4DD}",
|
|
65
|
+
txt: "\u{1F4C4}",
|
|
66
|
+
csv: "\u{1F4CA}"
|
|
67
|
+
};
|
|
68
|
+
return iconMap[entry.ext] || "\u{1F4CE}";
|
|
69
|
+
}
|
|
70
|
+
function ZipPreview({ content, fileName }) {
|
|
71
|
+
const [entries, setEntries] = useState([]);
|
|
72
|
+
const [loading, setLoading] = useState(true);
|
|
73
|
+
const [error, setError] = useState(null);
|
|
74
|
+
const [prevContent, setPrevContent] = useState(content);
|
|
75
|
+
if (prevContent !== content) {
|
|
76
|
+
setPrevContent(content);
|
|
77
|
+
setLoading(true);
|
|
78
|
+
setError(null);
|
|
79
|
+
setEntries([]);
|
|
80
|
+
}
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
let cancelled = false;
|
|
83
|
+
parseZipFile(content).then(
|
|
84
|
+
(result) => {
|
|
85
|
+
if (cancelled) return;
|
|
86
|
+
setEntries(result);
|
|
87
|
+
setLoading(false);
|
|
88
|
+
},
|
|
89
|
+
(err) => {
|
|
90
|
+
if (cancelled) return;
|
|
91
|
+
console.error("Error parsing ZIP:", err);
|
|
92
|
+
setError(err instanceof Error ? err.message : "Failed to parse archive");
|
|
93
|
+
setLoading(false);
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
return () => {
|
|
97
|
+
cancelled = true;
|
|
98
|
+
};
|
|
99
|
+
}, [content]);
|
|
100
|
+
const fileCount = entries.filter((e) => !e.isDir).length;
|
|
101
|
+
const dirCount = entries.filter((e) => e.isDir).length;
|
|
102
|
+
const totalSize = entries.reduce((sum, e) => sum + e.size, 0);
|
|
103
|
+
if (loading) {
|
|
104
|
+
return /* @__PURE__ */ jsxs("div", { className: "fv-zip__loading", children: [
|
|
105
|
+
/* @__PURE__ */ jsx("div", { className: "fv-spinner fv-spinner--lg" }),
|
|
106
|
+
/* @__PURE__ */ jsx("p", { className: "fv-zip__loading-label", children: "Reading archive..." })
|
|
107
|
+
] });
|
|
108
|
+
}
|
|
109
|
+
if (error) {
|
|
110
|
+
return /* @__PURE__ */ jsxs("div", { className: "fv-zip__error", children: [
|
|
111
|
+
/* @__PURE__ */ jsx("p", { className: "fv-zip__error-title", children: "Parsing Failed" }),
|
|
112
|
+
/* @__PURE__ */ jsx("p", { className: "fv-zip__error-msg", children: error })
|
|
113
|
+
] });
|
|
114
|
+
}
|
|
115
|
+
return /* @__PURE__ */ jsxs("div", { className: "fv-zip", children: [
|
|
116
|
+
/* @__PURE__ */ jsxs("div", { className: "fv-zip__summary", children: [
|
|
117
|
+
/* @__PURE__ */ jsxs("span", { className: "fv-zip__summary-item", children: [
|
|
118
|
+
/* @__PURE__ */ jsx(FolderOpenIcon, { size: 14 }),
|
|
119
|
+
dirCount,
|
|
120
|
+
" folder",
|
|
121
|
+
dirCount !== 1 ? "s" : ""
|
|
122
|
+
] }),
|
|
123
|
+
/* @__PURE__ */ jsxs("span", { className: "fv-zip__summary-item", children: [
|
|
124
|
+
/* @__PURE__ */ jsx(FileIcon, { size: 14 }),
|
|
125
|
+
fileCount,
|
|
126
|
+
" file",
|
|
127
|
+
fileCount !== 1 ? "s" : ""
|
|
128
|
+
] }),
|
|
129
|
+
/* @__PURE__ */ jsx("span", { children: "\u2022" }),
|
|
130
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
131
|
+
"Total: ",
|
|
132
|
+
formatFileSize(totalSize)
|
|
133
|
+
] })
|
|
134
|
+
] }),
|
|
135
|
+
/* @__PURE__ */ jsx("div", { className: "fv-zip__tree", children: entries.map((entry, i) => /* @__PURE__ */ jsxs(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
className: "fv-zip__entry",
|
|
139
|
+
style: { paddingLeft: `${12 + entry.depth * 20}px` },
|
|
140
|
+
children: [
|
|
141
|
+
/* @__PURE__ */ jsx("span", { className: "fv-zip__entry-icon", children: getFileIcon(entry) }),
|
|
142
|
+
/* @__PURE__ */ jsx("span", { className: `fv-zip__entry-name ${entry.isDir ? "fv-zip__entry-name--dir" : ""}`, children: entry.name }),
|
|
143
|
+
!entry.isDir && entry.size > 0 && /* @__PURE__ */ jsx("span", { className: "fv-zip__entry-size", children: formatFileSize(entry.size) })
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
i
|
|
147
|
+
)) })
|
|
148
|
+
] });
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
ZipPreview
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=ZipPreview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ZipPreview.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport JSZip from \"jszip\";\nimport { FileIcon, FolderIcon, FolderOpenIcon } from \"./icons\";\nimport { formatFileSize, base64ToUint8Array } from \"./utils\";\nimport \"./styles/ZipPreview.css\";\n\ninterface ZipPreviewProps {\n content: string;\n fileName: string;\n}\n\ninterface ZipEntry {\n path: string;\n name: string;\n size: number;\n isDir: boolean;\n depth: number;\n ext: string;\n}\n\nasync function parseZipFile(base64Content: string): Promise<ZipEntry[]> {\n const bytes = base64ToUint8Array(base64Content);\n\n const zip = await JSZip.loadAsync(bytes);\n const entries: ZipEntry[] = [];\n\n zip.forEach((relativePath, file) => {\n const parts = relativePath.split(\"/\").filter(Boolean);\n const name = parts[parts.length - 1] || relativePath;\n const ext = name.includes(\".\") ? name.split(\".\").pop()!.toLowerCase() : \"\";\n const depth = parts.length - 1;\n\n const internal = (file as unknown as { _data?: { uncompressedSize?: number } })._data;\n const size = internal?.uncompressedSize ?? 0;\n\n entries.push({\n path: relativePath,\n name,\n size,\n isDir: file.dir,\n depth,\n ext,\n });\n });\n\n entries.sort((a, b) => {\n if (a.isDir && !b.isDir) return -1;\n if (!a.isDir && b.isDir) return 1;\n return a.path.localeCompare(b.path);\n });\n\n return entries;\n}\n\nfunction getFileIcon(entry: ZipEntry): string {\n if (entry.isDir) return \"📁\";\n const iconMap: Record<string, string> = {\n pdf: \"📄\", doc: \"📃\", docx: \"📃\", ppt: \"📊\", pptx: \"📊\",\n xls: \"📊\", xlsx: \"📊\", jpg: \"🖼️\", jpeg: \"🖼️\", png: \"🖼️\",\n gif: \"🖼️\", svg: \"🖼️\", webp: \"🖼️\", mp4: \"🎬\", mp3: \"🎵\",\n zip: \"📦\", rar: \"📦\", \"7z\": \"📦\", tar: \"📦\", gz: \"📦\",\n js: \"💻\", ts: \"💻\", py: \"💻\", html: \"🌐\", css: \"🎨\",\n json: \"🔧\", xml: \"🔧\", md: \"📝\", txt: \"📄\", csv: \"📊\",\n };\n return iconMap[entry.ext] || \"📎\";\n}\n\nexport function ZipPreview({ content, fileName }: ZipPreviewProps) {\n const [entries, setEntries] = useState<ZipEntry[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const [prevContent, setPrevContent] = useState(content);\n if (prevContent !== content) {\n setPrevContent(content);\n setLoading(true);\n setError(null);\n setEntries([]);\n }\n\n useEffect(() => {\n let cancelled = false;\n parseZipFile(content).then(\n (result) => {\n if (cancelled) return;\n setEntries(result);\n setLoading(false);\n },\n (err) => {\n if (cancelled) return;\n console.error(\"Error parsing ZIP:\", err);\n setError(err instanceof Error ? err.message : \"Failed to parse archive\");\n setLoading(false);\n }\n );\n return () => { cancelled = true; };\n }, [content]);\n\n const fileCount = entries.filter(e => !e.isDir).length;\n const dirCount = entries.filter(e => e.isDir).length;\n const totalSize = entries.reduce((sum, e) => sum + e.size, 0);\n\n if (loading) {\n return (\n <div className=\"fv-zip__loading\">\n <div className=\"fv-spinner fv-spinner--lg\" />\n <p className=\"fv-zip__loading-label\">Reading archive...</p>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"fv-zip__error\">\n <p className=\"fv-zip__error-title\">Parsing Failed</p>\n <p className=\"fv-zip__error-msg\">{error}</p>\n </div>\n );\n }\n\n return (\n <div className=\"fv-zip\">\n <div className=\"fv-zip__summary\">\n <span className=\"fv-zip__summary-item\">\n <FolderOpenIcon size={14} />\n {dirCount} folder{dirCount !== 1 ? \"s\" : \"\"}\n </span>\n <span className=\"fv-zip__summary-item\">\n <FileIcon size={14} />\n {fileCount} file{fileCount !== 1 ? \"s\" : \"\"}\n </span>\n <span>•</span>\n <span>Total: {formatFileSize(totalSize)}</span>\n </div>\n\n <div className=\"fv-zip__tree\">\n {entries.map((entry, i) => (\n <div\n key={i}\n className=\"fv-zip__entry\"\n style={{ paddingLeft: `${12 + entry.depth * 20}px` }}\n >\n <span className=\"fv-zip__entry-icon\">{getFileIcon(entry)}</span>\n <span className={`fv-zip__entry-name ${entry.isDir ? \"fv-zip__entry-name--dir\" : \"\"}`}>\n {entry.name}\n </span>\n {!entry.isDir && entry.size > 0 && (\n <span className=\"fv-zip__entry-size\">{formatFileSize(entry.size)}</span>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PreviewSource } from './types.js';
|
|
2
|
+
|
|
3
|
+
interface BinaryPreviewInput {
|
|
4
|
+
source?: PreviewSource;
|
|
5
|
+
content?: string | null;
|
|
6
|
+
}
|
|
7
|
+
declare function readBinaryPreviewAsArrayBuffer({ source, content, }: BinaryPreviewInput): Promise<ArrayBuffer>;
|
|
8
|
+
declare function readBinaryPreviewAsUint8Array(input: BinaryPreviewInput): Promise<Uint8Array>;
|
|
9
|
+
|
|
10
|
+
export { type BinaryPreviewInput, readBinaryPreviewAsArrayBuffer, readBinaryPreviewAsUint8Array };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { base64ToUint8Array } from "../utils";
|
|
2
|
+
import { readSourceAsArrayBuffer } from "./source";
|
|
3
|
+
async function readBinaryPreviewAsArrayBuffer({
|
|
4
|
+
source,
|
|
5
|
+
content
|
|
6
|
+
}) {
|
|
7
|
+
if (source) {
|
|
8
|
+
return readSourceAsArrayBuffer(source);
|
|
9
|
+
}
|
|
10
|
+
if (!content) {
|
|
11
|
+
throw new Error("No binary preview source or legacy base64 content provided");
|
|
12
|
+
}
|
|
13
|
+
const bytes = base64ToUint8Array(content);
|
|
14
|
+
return bytes.buffer.slice(
|
|
15
|
+
bytes.byteOffset,
|
|
16
|
+
bytes.byteOffset + bytes.byteLength
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
async function readBinaryPreviewAsUint8Array(input) {
|
|
20
|
+
const buffer = await readBinaryPreviewAsArrayBuffer(input);
|
|
21
|
+
return new Uint8Array(buffer);
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
readBinaryPreviewAsArrayBuffer,
|
|
25
|
+
readBinaryPreviewAsUint8Array
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=binary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/binary.ts"],"sourcesContent":["import { base64ToUint8Array } from \"../utils\";\nimport { readSourceAsArrayBuffer } from \"./source\";\nimport type { PreviewSource } from \"./types\";\n\nexport interface BinaryPreviewInput {\n source?: PreviewSource;\n content?: string | null;\n}\n\nexport async function readBinaryPreviewAsArrayBuffer({\n source,\n content,\n}: BinaryPreviewInput): Promise<ArrayBuffer> {\n if (source) {\n return readSourceAsArrayBuffer(source);\n }\n\n if (!content) {\n throw new Error(\"No binary preview source or legacy base64 content provided\");\n }\n\n const bytes = base64ToUint8Array(content);\n\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength\n ) as ArrayBuffer;\n}\n\nexport async function readBinaryPreviewAsUint8Array(\n input: BinaryPreviewInput\n): Promise<Uint8Array> {\n const buffer = await readBinaryPreviewAsArrayBuffer(input);\n return new Uint8Array(buffer);\n}\n"],"mappings":"AAAA,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AAQxC,eAAsB,+BAA+B;AAAA,EACnD;AAAA,EACA;AACF,GAA6C;AAC3C,MAAI,QAAQ;AACV,WAAO,wBAAwB,MAAM;AAAA,EACvC;AAEA,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,4DAA4D;AAAA,EAC9E;AAEA,QAAM,QAAQ,mBAAmB,OAAO;AAExC,SAAO,MAAM,OAAO;AAAA,IAClB,MAAM;AAAA,IACN,MAAM,aAAa,MAAM;AAAA,EAC3B;AACF;AAEA,eAAsB,8BACpB,OACqB;AACrB,QAAM,SAAS,MAAM,+BAA+B,KAAK;AACzD,SAAO,IAAI,WAAW,MAAM;AAC9B;","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileVista runtime configuration.
|
|
3
|
+
*
|
|
4
|
+
* Library consumers can set these before mounting components,
|
|
5
|
+
* or override them via `<FileVistaProvider>`.
|
|
6
|
+
*
|
|
7
|
+
* For now this is a simple module-level config; the Context-based
|
|
8
|
+
* provider will be added in the next phase.
|
|
9
|
+
*/
|
|
10
|
+
/** Set the base path for static assets. Call once before mounting. */
|
|
11
|
+
declare function setAssetBasePath(path: string): void;
|
|
12
|
+
/** Get the current asset base path. */
|
|
13
|
+
declare function getAssetBasePath(): string;
|
|
14
|
+
/** Resolve a public path against the configured base path. */
|
|
15
|
+
declare function resolveAssetPath(path: string): string;
|
|
16
|
+
|
|
17
|
+
export { getAssetBasePath, resolveAssetPath, setAssetBasePath };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
let assetBasePath = "";
|
|
2
|
+
function setAssetBasePath(path) {
|
|
3
|
+
assetBasePath = path;
|
|
4
|
+
}
|
|
5
|
+
function getAssetBasePath() {
|
|
6
|
+
return assetBasePath;
|
|
7
|
+
}
|
|
8
|
+
function resolveAssetPath(path) {
|
|
9
|
+
if (!assetBasePath) return path;
|
|
10
|
+
const base = assetBasePath.endsWith("/") ? assetBasePath.slice(0, -1) : assetBasePath;
|
|
11
|
+
const relative = path.startsWith("/") ? path : `/${path}`;
|
|
12
|
+
return `${base}${relative}`;
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
getAssetBasePath,
|
|
16
|
+
resolveAssetPath,
|
|
17
|
+
setAssetBasePath
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/config.ts"],"sourcesContent":["/**\n * FileVista runtime configuration.\n *\n * Library consumers can set these before mounting components,\n * or override them via `<FileVistaProvider>`.\n *\n * For now this is a simple module-level config; the Context-based\n * provider will be added in the next phase.\n */\n\n/** Base path for static assets (PDF.js worker, RTF.js bundles, demo files). */\nlet assetBasePath = \"\";\n\n/** Set the base path for static assets. Call once before mounting. */\nexport function setAssetBasePath(path: string): void {\n assetBasePath = path;\n}\n\n/** Get the current asset base path. */\nexport function getAssetBasePath(): string {\n return assetBasePath;\n}\n\n/** Resolve a public path against the configured base path. */\nexport function resolveAssetPath(path: string): string {\n if (!assetBasePath) return path;\n // Avoid double slash\n const base = assetBasePath.endsWith(\"/\") ? assetBasePath.slice(0, -1) : assetBasePath;\n const relative = path.startsWith(\"/\") ? path : `/${path}`;\n return `${base}${relative}`;\n}\n"],"mappings":"AAWA,IAAI,gBAAgB;AAGb,SAAS,iBAAiB,MAAoB;AACnD,kBAAgB;AAClB;AAGO,SAAS,mBAA2B;AACzC,SAAO;AACT;AAGO,SAAS,iBAAiB,MAAsB;AACrD,MAAI,CAAC,cAAe,QAAO;AAE3B,QAAM,OAAO,cAAc,SAAS,GAAG,IAAI,cAAc,MAAM,GAAG,EAAE,IAAI;AACxE,QAAM,WAAW,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AACvD,SAAO,GAAG,IAAI,GAAG,QAAQ;AAC3B;","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readSourceAsArrayBuffer } from "./source";
|
|
2
|
+
async function downloadSource(source, fileName, mimeType) {
|
|
3
|
+
const buffer = await readSourceAsArrayBuffer(source);
|
|
4
|
+
const blob = new Blob([buffer], {
|
|
5
|
+
type: mimeType || "application/octet-stream"
|
|
6
|
+
});
|
|
7
|
+
const url = URL.createObjectURL(blob);
|
|
8
|
+
try {
|
|
9
|
+
const anchor = document.createElement("a");
|
|
10
|
+
anchor.href = url;
|
|
11
|
+
anchor.download = fileName;
|
|
12
|
+
anchor.click();
|
|
13
|
+
} finally {
|
|
14
|
+
URL.revokeObjectURL(url);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
downloadSource
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/download.ts"],"sourcesContent":["import type { PreviewSource } from \"./types\";\nimport { readSourceAsArrayBuffer } from \"./source\";\n\nexport async function downloadSource(\n source: PreviewSource,\n fileName: string,\n mimeType?: string\n): Promise<void> {\n const buffer = await readSourceAsArrayBuffer(source);\n const blob = new Blob([buffer], {\n type: mimeType || \"application/octet-stream\",\n });\n\n const url = URL.createObjectURL(blob);\n\n try {\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = fileName;\n anchor.click();\n } finally {\n URL.revokeObjectURL(url);\n }\n}\n"],"mappings":"AACA,SAAS,+BAA+B;AAExC,eAAsB,eACpB,QACA,UACA,UACe;AACf,QAAM,SAAS,MAAM,wBAAwB,MAAM;AACnD,QAAM,OAAO,IAAI,KAAK,CAAC,MAAM,GAAG;AAAA,IAC9B,MAAM,YAAY;AAAA,EACpB,CAAC;AAED,QAAM,MAAM,IAAI,gBAAgB,IAAI;AAEpC,MAAI;AACF,UAAM,SAAS,SAAS,cAAc,GAAG;AACzC,WAAO,OAAO;AACd,WAAO,WAAW;AAClB,WAAO,MAAM;AAAA,EACf,UAAE;AACA,QAAI,gBAAgB,GAAG;AAAA,EACzB;AACF;","names":[]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FileVista i18n — locale messages for all UI strings.
|
|
5
|
+
*
|
|
6
|
+
* Library consumers can import and override any locale, or provide
|
|
7
|
+
* a fully custom translation via `<FileVistaProvider locale={...}>`.
|
|
8
|
+
*
|
|
9
|
+
* Usage in components:
|
|
10
|
+
* import { useLocale } from "./core/i18n";
|
|
11
|
+
* const t = useLocale();
|
|
12
|
+
* <button>{t.preview}</button>
|
|
13
|
+
*/
|
|
14
|
+
interface LocaleMessages {
|
|
15
|
+
preview: string;
|
|
16
|
+
source: string;
|
|
17
|
+
split: string;
|
|
18
|
+
download: string;
|
|
19
|
+
copy: string;
|
|
20
|
+
search: string;
|
|
21
|
+
zoomIn: string;
|
|
22
|
+
zoomOut: string;
|
|
23
|
+
reset: string;
|
|
24
|
+
fullscreen: string;
|
|
25
|
+
previous: string;
|
|
26
|
+
next: string;
|
|
27
|
+
page: string;
|
|
28
|
+
pages: string;
|
|
29
|
+
slideView: string;
|
|
30
|
+
gridView: string;
|
|
31
|
+
previousPage: string;
|
|
32
|
+
nextPage: string;
|
|
33
|
+
loadingPreview: string;
|
|
34
|
+
loadingRtf: string;
|
|
35
|
+
loadingSpreadsheet: string;
|
|
36
|
+
loadingEbook: string;
|
|
37
|
+
loadingPresentation: string;
|
|
38
|
+
previewFailed: string;
|
|
39
|
+
parseFailed: string;
|
|
40
|
+
formatNotSupported: string;
|
|
41
|
+
legacyDocTitle: string;
|
|
42
|
+
legacyPptTitle: string;
|
|
43
|
+
legacyXlsTitle: string;
|
|
44
|
+
legacyDocDesc: string;
|
|
45
|
+
legacyPptDesc: string;
|
|
46
|
+
legacyXlsDesc: string;
|
|
47
|
+
legacyXlsFallbackDesc: string;
|
|
48
|
+
legacyXlsError: string;
|
|
49
|
+
legacyXlsBanner: string;
|
|
50
|
+
unsupportedFileType: string;
|
|
51
|
+
largeFileHint: string;
|
|
52
|
+
largeFile: string;
|
|
53
|
+
largeFileRows: string;
|
|
54
|
+
largeFileCols: string;
|
|
55
|
+
largeFileImages: string;
|
|
56
|
+
largeFileFastModeBanner: string;
|
|
57
|
+
largeFileFidelityBanner: string;
|
|
58
|
+
largeFileFidelityConfirm: string;
|
|
59
|
+
truncatedRows: string;
|
|
60
|
+
sheetNotFound: string;
|
|
61
|
+
fastMode: string;
|
|
62
|
+
fidelityMode: string;
|
|
63
|
+
fastModeTitle: string;
|
|
64
|
+
fidelityModeTitle: string;
|
|
65
|
+
noData: string;
|
|
66
|
+
noSearchResults: string;
|
|
67
|
+
comment: string;
|
|
68
|
+
unsupportedImageFormat: string;
|
|
69
|
+
unknown: string;
|
|
70
|
+
downloadOriginal: string;
|
|
71
|
+
lines: string;
|
|
72
|
+
wordWrapOn: string;
|
|
73
|
+
wordWrapOff: string;
|
|
74
|
+
copyContent: string;
|
|
75
|
+
oversizedCodeBlock: string;
|
|
76
|
+
rtfFallback: string;
|
|
77
|
+
rtfNoText: string;
|
|
78
|
+
showErrorDetails: string;
|
|
79
|
+
noChaptersFound: string;
|
|
80
|
+
ebookLoadFailed: string;
|
|
81
|
+
unknownError: string;
|
|
82
|
+
tableOfContents: string;
|
|
83
|
+
foundInChapters: string;
|
|
84
|
+
noResultsFound: string;
|
|
85
|
+
searchPlaceholder: string;
|
|
86
|
+
previewNotAvailable: string;
|
|
87
|
+
failedToLoadPreview: string;
|
|
88
|
+
failedToReadFile: string;
|
|
89
|
+
loadingCancelled: string;
|
|
90
|
+
copyCode: string;
|
|
91
|
+
}
|
|
92
|
+
declare const zhCN: LocaleMessages;
|
|
93
|
+
declare const enUS: LocaleMessages;
|
|
94
|
+
/** Provider component — wrap your preview tree with this to override locale. */
|
|
95
|
+
declare const LocaleProvider: react.Provider<LocaleMessages>;
|
|
96
|
+
/** Hook to access the current locale messages. */
|
|
97
|
+
declare function useLocale(): LocaleMessages;
|
|
98
|
+
/** Get the default locale (zh-CN). */
|
|
99
|
+
declare function getDefaultLocale(): LocaleMessages;
|
|
100
|
+
|
|
101
|
+
export { type LocaleMessages, LocaleProvider, enUS, getDefaultLocale, useLocale, zhCN };
|