@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.
Files changed (263) hide show
  1. package/COPYING +674 -0
  2. package/LICENSE +165 -0
  3. package/README.md +165 -0
  4. package/dist/AudioPreview.d.ts +9 -0
  5. package/dist/AudioPreview.js +29 -0
  6. package/dist/AudioPreview.js.map +1 -0
  7. package/dist/CodePreview.d.ts +10 -0
  8. package/dist/CodePreview.js +121 -0
  9. package/dist/CodePreview.js.map +1 -0
  10. package/dist/CsvPreview.d.ts +9 -0
  11. package/dist/CsvPreview.js +117 -0
  12. package/dist/CsvPreview.js.map +1 -0
  13. package/dist/DocxPreview.d.ts +11 -0
  14. package/dist/DocxPreview.js +89 -0
  15. package/dist/DocxPreview.js.map +1 -0
  16. package/dist/EpubPreview.d.ts +9 -0
  17. package/dist/EpubPreview.js +693 -0
  18. package/dist/EpubPreview.js.map +1 -0
  19. package/dist/HtmlPreview.d.ts +9 -0
  20. package/dist/HtmlPreview.js +60 -0
  21. package/dist/HtmlPreview.js.map +1 -0
  22. package/dist/ImagePreview.d.ts +9 -0
  23. package/dist/ImagePreview.js +44 -0
  24. package/dist/ImagePreview.js.map +1 -0
  25. package/dist/LargeFileGate.d.ts +12 -0
  26. package/dist/LargeFileGate.js +88 -0
  27. package/dist/LargeFileGate.js.map +1 -0
  28. package/dist/MarkdownPreview.d.ts +8 -0
  29. package/dist/MarkdownPreview.js +140 -0
  30. package/dist/MarkdownPreview.js.map +1 -0
  31. package/dist/PdfPreview.d.ts +11 -0
  32. package/dist/PdfPreview.js +206 -0
  33. package/dist/PdfPreview.js.map +1 -0
  34. package/dist/PlainTextLargePreview.d.ts +9 -0
  35. package/dist/PlainTextLargePreview.js +62 -0
  36. package/dist/PlainTextLargePreview.js.map +1 -0
  37. package/dist/PluginPreviewRenderer.d.ts +13 -0
  38. package/dist/PluginPreviewRenderer.js +89 -0
  39. package/dist/PluginPreviewRenderer.js.map +1 -0
  40. package/dist/PptxPreview.d.ts +16 -0
  41. package/dist/PptxPreview.js +376 -0
  42. package/dist/PptxPreview.js.map +1 -0
  43. package/dist/PreviewErrorBoundary.d.ts +29 -0
  44. package/dist/PreviewErrorBoundary.js +53 -0
  45. package/dist/PreviewErrorBoundary.js.map +1 -0
  46. package/dist/PreviewFallback.d.ts +18 -0
  47. package/dist/PreviewFallback.js +143 -0
  48. package/dist/PreviewFallback.js.map +1 -0
  49. package/dist/PreviewLoading.d.ts +8 -0
  50. package/dist/PreviewLoading.js +14 -0
  51. package/dist/PreviewLoading.js.map +1 -0
  52. package/dist/RtfPreview.d.ts +10 -0
  53. package/dist/RtfPreview.js +240 -0
  54. package/dist/RtfPreview.js.map +1 -0
  55. package/dist/ShikiSourceView.d.ts +11 -0
  56. package/dist/ShikiSourceView.js +112 -0
  57. package/dist/ShikiSourceView.js.map +1 -0
  58. package/dist/SvgPreview.d.ts +9 -0
  59. package/dist/SvgPreview.js +89 -0
  60. package/dist/SvgPreview.js.map +1 -0
  61. package/dist/TextPreview.d.ts +9 -0
  62. package/dist/TextPreview.js +9 -0
  63. package/dist/TextPreview.js.map +1 -0
  64. package/dist/VideoPreview.d.ts +9 -0
  65. package/dist/VideoPreview.js +18 -0
  66. package/dist/VideoPreview.js.map +1 -0
  67. package/dist/XlsxPreview.d.ts +12 -0
  68. package/dist/XlsxPreview.js +856 -0
  69. package/dist/XlsxPreview.js.map +1 -0
  70. package/dist/ZipPreview.d.ts +9 -0
  71. package/dist/ZipPreview.js +153 -0
  72. package/dist/ZipPreview.js.map +1 -0
  73. package/dist/core/binary.d.ts +10 -0
  74. package/dist/core/binary.js +27 -0
  75. package/dist/core/binary.js.map +1 -0
  76. package/dist/core/config.d.ts +17 -0
  77. package/dist/core/config.js +19 -0
  78. package/dist/core/config.js.map +1 -0
  79. package/dist/core/download.d.ts +5 -0
  80. package/dist/core/download.js +20 -0
  81. package/dist/core/download.js.map +1 -0
  82. package/dist/core/i18n.d.ts +101 -0
  83. package/dist/core/i18n.js +200 -0
  84. package/dist/core/i18n.js.map +1 -0
  85. package/dist/core/plugin.d.ts +38 -0
  86. package/dist/core/plugin.js +46 -0
  87. package/dist/core/plugin.js.map +1 -0
  88. package/dist/core/registry.d.ts +13 -0
  89. package/dist/core/registry.js +33 -0
  90. package/dist/core/registry.js.map +1 -0
  91. package/dist/core/source.d.ts +14 -0
  92. package/dist/core/source.js +131 -0
  93. package/dist/core/source.js.map +1 -0
  94. package/dist/core/types.d.ts +88 -0
  95. package/dist/core/types.js +27 -0
  96. package/dist/core/types.js.map +1 -0
  97. package/dist/hooks/useObjectUrlFromSource.d.ts +9 -0
  98. package/dist/hooks/useObjectUrlFromSource.js +50 -0
  99. package/dist/hooks/useObjectUrlFromSource.js.map +1 -0
  100. package/dist/hooks/useSourceBase64.d.ts +10 -0
  101. package/dist/hooks/useSourceBase64.js +32 -0
  102. package/dist/hooks/useSourceBase64.js.map +1 -0
  103. package/dist/hooks/useSourceText.d.ts +10 -0
  104. package/dist/hooks/useSourceText.js +32 -0
  105. package/dist/hooks/useSourceText.js.map +1 -0
  106. package/dist/icons.d.ts +44 -0
  107. package/dist/icons.js +248 -0
  108. package/dist/icons.js.map +1 -0
  109. package/dist/index.d.ts +36 -0
  110. package/dist/index.js +147 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/limits.d.ts +26 -0
  113. package/dist/limits.js +45 -0
  114. package/dist/limits.js.map +1 -0
  115. package/dist/performance-limits.d.ts +27 -0
  116. package/dist/performance-limits.js +54 -0
  117. package/dist/performance-limits.js.map +1 -0
  118. package/dist/plugins/audio-plugin.d.ts +7 -0
  119. package/dist/plugins/audio-plugin.js +11 -0
  120. package/dist/plugins/audio-plugin.js.map +1 -0
  121. package/dist/plugins/builtin-plugins.d.ts +9 -0
  122. package/dist/plugins/builtin-plugins.js +43 -0
  123. package/dist/plugins/builtin-plugins.js.map +1 -0
  124. package/dist/plugins/csv-plugin.d.ts +7 -0
  125. package/dist/plugins/csv-plugin.js +11 -0
  126. package/dist/plugins/csv-plugin.js.map +1 -0
  127. package/dist/plugins/docx-plugin.d.ts +7 -0
  128. package/dist/plugins/docx-plugin.js +15 -0
  129. package/dist/plugins/docx-plugin.js.map +1 -0
  130. package/dist/plugins/epub-plugin.d.ts +7 -0
  131. package/dist/plugins/epub-plugin.js +15 -0
  132. package/dist/plugins/epub-plugin.js.map +1 -0
  133. package/dist/plugins/html-plugin.d.ts +7 -0
  134. package/dist/plugins/html-plugin.js +11 -0
  135. package/dist/plugins/html-plugin.js.map +1 -0
  136. package/dist/plugins/image-plugin.d.ts +7 -0
  137. package/dist/plugins/image-plugin.js +11 -0
  138. package/dist/plugins/image-plugin.js.map +1 -0
  139. package/dist/plugins/markdown-plugin.d.ts +7 -0
  140. package/dist/plugins/markdown-plugin.js +11 -0
  141. package/dist/plugins/markdown-plugin.js.map +1 -0
  142. package/dist/plugins/pdf-plugin.d.ts +7 -0
  143. package/dist/plugins/pdf-plugin.js +15 -0
  144. package/dist/plugins/pdf-plugin.js.map +1 -0
  145. package/dist/plugins/pptx-plugin.d.ts +7 -0
  146. package/dist/plugins/pptx-plugin.js +15 -0
  147. package/dist/plugins/pptx-plugin.js.map +1 -0
  148. package/dist/plugins/rtf-plugin.d.ts +7 -0
  149. package/dist/plugins/rtf-plugin.js +15 -0
  150. package/dist/plugins/rtf-plugin.js.map +1 -0
  151. package/dist/plugins/source-code-plugin.d.ts +7 -0
  152. package/dist/plugins/source-code-plugin.js +11 -0
  153. package/dist/plugins/source-code-plugin.js.map +1 -0
  154. package/dist/plugins/svg-plugin.d.ts +7 -0
  155. package/dist/plugins/svg-plugin.js +11 -0
  156. package/dist/plugins/svg-plugin.js.map +1 -0
  157. package/dist/plugins/text-plugin.d.ts +7 -0
  158. package/dist/plugins/text-plugin.js +11 -0
  159. package/dist/plugins/text-plugin.js.map +1 -0
  160. package/dist/plugins/video-plugin.d.ts +7 -0
  161. package/dist/plugins/video-plugin.js +11 -0
  162. package/dist/plugins/video-plugin.js.map +1 -0
  163. package/dist/plugins/xlsx-plugin.d.ts +7 -0
  164. package/dist/plugins/xlsx-plugin.js +15 -0
  165. package/dist/plugins/xlsx-plugin.js.map +1 -0
  166. package/dist/plugins/zip-plugin.d.ts +7 -0
  167. package/dist/plugins/zip-plugin.js +15 -0
  168. package/dist/plugins/zip-plugin.js.map +1 -0
  169. package/dist/preview-adapters/AudioPreviewAdapter.d.ts +8 -0
  170. package/dist/preview-adapters/AudioPreviewAdapter.js +31 -0
  171. package/dist/preview-adapters/AudioPreviewAdapter.js.map +1 -0
  172. package/dist/preview-adapters/CsvPreviewAdapter.d.ts +8 -0
  173. package/dist/preview-adapters/CsvPreviewAdapter.js +28 -0
  174. package/dist/preview-adapters/CsvPreviewAdapter.js.map +1 -0
  175. package/dist/preview-adapters/DocxPreviewAdapter.d.ts +8 -0
  176. package/dist/preview-adapters/DocxPreviewAdapter.js +16 -0
  177. package/dist/preview-adapters/DocxPreviewAdapter.js.map +1 -0
  178. package/dist/preview-adapters/EpubPreviewAdapter.d.ts +8 -0
  179. package/dist/preview-adapters/EpubPreviewAdapter.js +28 -0
  180. package/dist/preview-adapters/EpubPreviewAdapter.js.map +1 -0
  181. package/dist/preview-adapters/HtmlPreviewAdapter.d.ts +8 -0
  182. package/dist/preview-adapters/HtmlPreviewAdapter.js +28 -0
  183. package/dist/preview-adapters/HtmlPreviewAdapter.js.map +1 -0
  184. package/dist/preview-adapters/ImagePreviewAdapter.d.ts +8 -0
  185. package/dist/preview-adapters/ImagePreviewAdapter.js +31 -0
  186. package/dist/preview-adapters/ImagePreviewAdapter.js.map +1 -0
  187. package/dist/preview-adapters/MarkdownPreviewAdapter.d.ts +8 -0
  188. package/dist/preview-adapters/MarkdownPreviewAdapter.js +28 -0
  189. package/dist/preview-adapters/MarkdownPreviewAdapter.js.map +1 -0
  190. package/dist/preview-adapters/PdfPreviewAdapter.d.ts +8 -0
  191. package/dist/preview-adapters/PdfPreviewAdapter.js +16 -0
  192. package/dist/preview-adapters/PdfPreviewAdapter.js.map +1 -0
  193. package/dist/preview-adapters/PptxPreviewAdapter.d.ts +8 -0
  194. package/dist/preview-adapters/PptxPreviewAdapter.js +16 -0
  195. package/dist/preview-adapters/PptxPreviewAdapter.js.map +1 -0
  196. package/dist/preview-adapters/RtfPreviewAdapter.d.ts +8 -0
  197. package/dist/preview-adapters/RtfPreviewAdapter.js +54 -0
  198. package/dist/preview-adapters/RtfPreviewAdapter.js.map +1 -0
  199. package/dist/preview-adapters/SourceCodePreviewAdapter.d.ts +8 -0
  200. package/dist/preview-adapters/SourceCodePreviewAdapter.js +35 -0
  201. package/dist/preview-adapters/SourceCodePreviewAdapter.js.map +1 -0
  202. package/dist/preview-adapters/SvgPreviewAdapter.d.ts +8 -0
  203. package/dist/preview-adapters/SvgPreviewAdapter.js +28 -0
  204. package/dist/preview-adapters/SvgPreviewAdapter.js.map +1 -0
  205. package/dist/preview-adapters/TextPreviewAdapter.d.ts +8 -0
  206. package/dist/preview-adapters/TextPreviewAdapter.js +28 -0
  207. package/dist/preview-adapters/TextPreviewAdapter.js.map +1 -0
  208. package/dist/preview-adapters/UnsupportedPluginPreview.d.ts +11 -0
  209. package/dist/preview-adapters/UnsupportedPluginPreview.js +34 -0
  210. package/dist/preview-adapters/UnsupportedPluginPreview.js.map +1 -0
  211. package/dist/preview-adapters/VideoPreviewAdapter.d.ts +8 -0
  212. package/dist/preview-adapters/VideoPreviewAdapter.js +31 -0
  213. package/dist/preview-adapters/VideoPreviewAdapter.js.map +1 -0
  214. package/dist/preview-adapters/XlsxPreviewAdapter.d.ts +8 -0
  215. package/dist/preview-adapters/XlsxPreviewAdapter.js +17 -0
  216. package/dist/preview-adapters/XlsxPreviewAdapter.js.map +1 -0
  217. package/dist/preview-adapters/ZipPreviewAdapter.d.ts +8 -0
  218. package/dist/preview-adapters/ZipPreviewAdapter.js +28 -0
  219. package/dist/preview-adapters/ZipPreviewAdapter.js.map +1 -0
  220. package/dist/remote-url.d.ts +20 -0
  221. package/dist/remote-url.js +478 -0
  222. package/dist/remote-url.js.map +1 -0
  223. package/dist/rtf/load-rtfjs.d.ts +42 -0
  224. package/dist/rtf/load-rtfjs.js +71 -0
  225. package/dist/rtf/load-rtfjs.js.map +1 -0
  226. package/dist/rtf/normalize-codepage.d.ts +33 -0
  227. package/dist/rtf/normalize-codepage.js +88 -0
  228. package/dist/rtf/normalize-codepage.js.map +1 -0
  229. package/dist/shiki.d.ts +35 -0
  230. package/dist/shiki.js +128 -0
  231. package/dist/shiki.js.map +1 -0
  232. package/dist/styles/AudioPreview.css +35 -0
  233. package/dist/styles/CsvPreview.css +106 -0
  234. package/dist/styles/DocxPreview.css +93 -0
  235. package/dist/styles/EpubPreview.css +509 -0
  236. package/dist/styles/HtmlPreview.css +15 -0
  237. package/dist/styles/ImagePreview.css +45 -0
  238. package/dist/styles/LargeFileGate.css +55 -0
  239. package/dist/styles/MarkdownPreview.css +291 -0
  240. package/dist/styles/PdfPreview.css +68 -0
  241. package/dist/styles/PlainTextLargePreview.css +85 -0
  242. package/dist/styles/PluginDebugBar.css +30 -0
  243. package/dist/styles/PptxPreview.css +207 -0
  244. package/dist/styles/PreviewFallback.css +88 -0
  245. package/dist/styles/PreviewLoading.css +13 -0
  246. package/dist/styles/RtfPreview.css +99 -0
  247. package/dist/styles/ShikiSourceView.css +159 -0
  248. package/dist/styles/SvgPreview.css +99 -0
  249. package/dist/styles/VideoPreview.css +19 -0
  250. package/dist/styles/ViewModeBar.css +38 -0
  251. package/dist/styles/XlsxPreview.css +361 -0
  252. package/dist/styles/ZipPreview.css +86 -0
  253. package/dist/styles/base.css +238 -0
  254. package/dist/styles/index.css +39 -0
  255. package/dist/support-status.d.ts +19 -0
  256. package/dist/support-status.js +174 -0
  257. package/dist/support-status.js.map +1 -0
  258. package/dist/utils.d.ts +17 -0
  259. package/dist/utils.js +205 -0
  260. package/dist/utils.js.map +1 -0
  261. package/package.json +125 -0
  262. package/scripts/copy-pdf-worker.mjs +31 -0
  263. package/scripts/copy-rtfjs-bundles.mjs +49 -0
@@ -0,0 +1,856 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState, useCallback, useMemo, useRef } from "react";
3
+ import {
4
+ SearchIcon,
5
+ Table2Icon,
6
+ ZoomInIcon,
7
+ ZoomOutIcon,
8
+ ImageOffIcon,
9
+ MessageSquareIcon,
10
+ ExternalLinkIcon,
11
+ AlertTriangleIcon
12
+ } from "./icons";
13
+ import "./styles/XlsxPreview.css";
14
+ import { XLSX_PREVIEW_LIMITS } from "./limits";
15
+ import { readBinaryPreviewAsUint8Array } from "./core/binary";
16
+ import { formatFileSize } from "./utils";
17
+ import { useLocale } from "./core/i18n";
18
+ let ExcelJS = null;
19
+ async function getExcelJS() {
20
+ if (!ExcelJS) {
21
+ ExcelJS = await import("exceljs");
22
+ }
23
+ return ExcelJS;
24
+ }
25
+ const THEME_COLORS = {
26
+ 0: "#000000",
27
+ 1: "#FFFFFF",
28
+ 2: "#44546A",
29
+ 3: "#E7E6E6",
30
+ 4: "#4472C4",
31
+ 5: "#ED7D31",
32
+ 6: "#A5A5A5",
33
+ 7: "#FFC000",
34
+ 8: "#5B9BD5",
35
+ 9: "#70AD47",
36
+ 10: "#F2F2F2",
37
+ 11: "#D9D9D9",
38
+ 12: "#BFBFBF",
39
+ 13: "#A6A6A6",
40
+ 14: "#808080",
41
+ 15: "#595959",
42
+ 16: "#404040",
43
+ 17: "#262626"
44
+ };
45
+ const INDEXED_COLORS = [
46
+ "#000000",
47
+ "#FFFFFF",
48
+ "#FF0000",
49
+ "#00FF00",
50
+ "#0000FF",
51
+ "#FFFF00",
52
+ "#FF00FF",
53
+ "#00FFFF",
54
+ "#000000",
55
+ "#FFFFFF",
56
+ "#FF0000",
57
+ "#00FF00",
58
+ "#0000FF",
59
+ "#FFFF00",
60
+ "#FF00FF",
61
+ "#00FFFF",
62
+ "#800000",
63
+ "#008000",
64
+ "#000080",
65
+ "#808000",
66
+ "#800080",
67
+ "#008080",
68
+ "#C0C0C0",
69
+ "#808080",
70
+ "#9999FF",
71
+ "#993366",
72
+ "#FFFFCC",
73
+ "#CCFFFF",
74
+ "#660066",
75
+ "#FF8080",
76
+ "#0066CC",
77
+ "#CCCCFF",
78
+ "#000080",
79
+ "#FF00FF",
80
+ "#FFFF00",
81
+ "#00FFFF",
82
+ "#800080",
83
+ "#800000",
84
+ "#008080",
85
+ "#0000FF",
86
+ "#00CCFF",
87
+ "#CCFFFF",
88
+ "#CCFFCC",
89
+ "#FFFF99",
90
+ "#99CCFF",
91
+ "#FF99CC",
92
+ "#CC99FF",
93
+ "#FFCC99",
94
+ "#3366FF",
95
+ "#33CCCC",
96
+ "#99CC00",
97
+ "#FFCC00",
98
+ "#FF9900",
99
+ "#FF6600",
100
+ "#666699",
101
+ "#969696",
102
+ "#003366",
103
+ "#339966",
104
+ "#003300",
105
+ "#333300",
106
+ "#993300",
107
+ "#993366",
108
+ "#333399",
109
+ "#333333"
110
+ ];
111
+ function applyTint(hex, tint) {
112
+ if (!tint) return hex;
113
+ const r = parseInt(hex.slice(1, 3), 16);
114
+ const g = parseInt(hex.slice(3, 5), 16);
115
+ const b = parseInt(hex.slice(5, 7), 16);
116
+ const t = (c) => tint < 0 ? Math.round(c * (1 + tint)) : Math.round(c + (255 - c) * tint);
117
+ return `#${t(r).toString(16).padStart(2, "0")}${t(g).toString(16).padStart(2, "0")}${t(b).toString(16).padStart(2, "0")}`;
118
+ }
119
+ function resolveColor(color) {
120
+ if (!color) return void 0;
121
+ if (color.argb) {
122
+ const a = color.argb;
123
+ if (!a || a === "00000000" || a === "FFFFFFFF") return void 0;
124
+ return a.length === 8 ? "#" + a.slice(2).toLowerCase() : a.toLowerCase();
125
+ }
126
+ if (color.theme !== void 0) return applyTint(THEME_COLORS[color.theme] || "#000000", color.tint);
127
+ if (color.indexed !== void 0 && INDEXED_COLORS[color.indexed]) return applyTint(INDEXED_COLORS[color.indexed], color.tint);
128
+ if (typeof color === "string") return color;
129
+ return void 0;
130
+ }
131
+ function borderCss(part) {
132
+ if (!part?.style || part.style === "none") return "";
133
+ const w = part.style === "thin" ? "1px" : part.style === "medium" ? "2px" : part.style === "thick" ? "3px" : part.style === "hair" ? "0.5px" : part.style === "double" ? "3px" : part.style.startsWith("medium") ? "2px" : "1px";
134
+ const c = resolveColor(part.color) || "#000000";
135
+ const s = /dashed|dotted|dashDot/.test(part.style) ? "dashed" : "solid";
136
+ return `${w} ${s} ${c}`;
137
+ }
138
+ function parseImageDimensions(buffer) {
139
+ const bytes = buffer instanceof Uint8Array ? buffer : buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(Buffer.from(buffer));
140
+ if (bytes.length < 10) return { width: 0, height: 0 };
141
+ if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71 && bytes.length > 24) {
142
+ 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] };
143
+ }
144
+ if (bytes[0] === 255 && bytes[1] === 216) {
145
+ for (let i = 0; i < Math.min(bytes.length - 9, 65536); i++) {
146
+ if (bytes[i] === 255) {
147
+ const m = bytes[i + 1];
148
+ if (m >= 192 && m <= 207 && m !== 196 && m !== 200 && m !== 204) {
149
+ return { height: bytes[i + 5] << 8 | bytes[i + 6], width: bytes[i + 7] << 8 | bytes[i + 8] };
150
+ }
151
+ }
152
+ }
153
+ }
154
+ if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70) {
155
+ return { width: bytes[6] | bytes[7] << 8, height: bytes[8] | bytes[9] << 8 };
156
+ }
157
+ if (bytes[0] === 66 && bytes[1] === 77 && bytes.length > 25) {
158
+ 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 };
159
+ }
160
+ return { width: 0, height: 0 };
161
+ }
162
+ const BROWSER_SUPPORTED_FORMATS = /* @__PURE__ */ new Set(["png", "jpeg", "gif", "bmp", "webp", "svg"]);
163
+ function detectImageFormat(buffer) {
164
+ const bytes = buffer instanceof Uint8Array ? buffer : buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(Buffer.from(buffer));
165
+ if (bytes.length < 4) return "unknown";
166
+ if (bytes[0] === 137 && bytes[1] === 80) return "png";
167
+ if (bytes[0] === 255 && bytes[1] === 216) return "jpeg";
168
+ if (bytes[0] === 71 && bytes[1] === 73) return "gif";
169
+ if (bytes[0] === 66 && bytes[1] === 77) return "bmp";
170
+ if (bytes[0] === 73 && bytes[1] === 73 && bytes[2] === 42 || bytes[0] === 77 && bytes[1] === 77 && bytes[2] === 0) return "tiff";
171
+ if (bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes.length > 11 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) return "webp";
172
+ if (bytes[0] === 1 && bytes[1] === 0 && bytes[2] === 0 && bytes[3] === 0 && bytes.length > 44) return "emf";
173
+ if (bytes[0] === 215 && bytes[1] === 205 && bytes[2] === 198 && bytes[3] === 154 || bytes[0] === 1 && bytes[1] === 0 && bytes[2] === 9 && bytes[3] === 0) return "wmf";
174
+ return "unknown";
175
+ }
176
+ function getMimeType(format) {
177
+ const map = { png: "image/png", jpeg: "image/jpeg", gif: "image/gif", bmp: "image/bmp", webp: "image/webp", svg: "image/svg+xml", tiff: "image/tiff" };
178
+ return map[format] || "image/png";
179
+ }
180
+ function bufferToBase64(buf) {
181
+ if (buf instanceof Uint8Array || buf instanceof ArrayBuffer) {
182
+ const arr = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
183
+ let bin = "";
184
+ for (let i = 0; i < arr.length; i += 8192) {
185
+ const chunk = arr.subarray(i, Math.min(i + 8192, arr.length));
186
+ bin += String.fromCharCode(...chunk);
187
+ }
188
+ return btoa(bin);
189
+ }
190
+ if (typeof buf === "string") return buf;
191
+ return buf.toString("base64");
192
+ }
193
+ function formatCellValue(cell) {
194
+ const v = cell.value;
195
+ if (v === null || v === void 0) return "";
196
+ if (v instanceof Error) return v.message || "#ERROR";
197
+ if (typeof v === "object" && v !== null && "richText" in v) return v.richText.map((r) => r.text).join("");
198
+ if (typeof v === "object" && v !== null && "formula" in v) {
199
+ const r = v.result;
200
+ return r !== null && r !== void 0 ? String(r) : "";
201
+ }
202
+ if (typeof v === "object" && v !== null && "hyperlink" in v) return v.text || v.hyperlink;
203
+ if (v instanceof Date) return formatDateValue(v, cell.numFmt);
204
+ if (typeof v === "number") return formatNumberValue(v, cell.numFmt);
205
+ return String(v);
206
+ }
207
+ function formatDateValue(d, fmt) {
208
+ if (!fmt || !/[yYmdhHs]/.test(fmt)) return d.toLocaleDateString("zh-CN");
209
+ try {
210
+ const pad = (n) => n.toString().padStart(2, "0");
211
+ const monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
212
+ const daysShort = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
213
+ let res = fmt;
214
+ const is12Hour = /am\/pm/i.test(res);
215
+ res = res.replace(/AM\/PM/gi, "").replace(/A\/P/gi, "");
216
+ res = res.replace(/mmmm/g, monthsShort[d.getMonth()]);
217
+ res = res.replace(/mmm/g, monthsShort[d.getMonth()]);
218
+ res = res.replace(/dddd/g, daysShort[d.getDay()]);
219
+ res = res.replace(/ddd/g, daysShort[d.getDay()]);
220
+ res = res.replace(/yyyy/g, d.getFullYear().toString());
221
+ res = res.replace(/yy/g, d.getFullYear().toString().slice(-2));
222
+ const parts = res.split(/hh/i);
223
+ if (parts.length > 1) {
224
+ parts[0] = parts[0].replace(/mm/g, pad(d.getMonth() + 1));
225
+ parts[1] = parts[1].replace(/mm/g, pad(d.getMinutes()));
226
+ res = parts.join("hh");
227
+ } else {
228
+ res = res.replace(/mm/g, pad(d.getMonth() + 1));
229
+ }
230
+ res = res.replace(/dd/g, pad(d.getDate()));
231
+ let hours = d.getHours();
232
+ if (is12Hour) {
233
+ const ampm = hours >= 12 ? "PM" : "AM";
234
+ hours = hours % 12 || 12;
235
+ res += " " + ampm;
236
+ }
237
+ res = res.replace(/hh/gi, pad(hours));
238
+ res = res.replace(/ss/g, pad(d.getSeconds()));
239
+ res = res.replace(/[\\]/g, "").replace(/[\[\]]/g, "");
240
+ return res;
241
+ } catch {
242
+ return d.toLocaleDateString("zh-CN");
243
+ }
244
+ }
245
+ function formatNumberValue(v, fmt) {
246
+ if (!fmt || fmt === "General" || fmt === "@") return v.toString();
247
+ if (fmt.includes("%")) {
248
+ const z = fmt.split("%")[0].match(/0/g)?.length ?? 1;
249
+ return (v * 100).toFixed(Math.max(0, z - 1)) + "%";
250
+ }
251
+ if (fmt.includes("/")) return formatFraction(v, fmt);
252
+ if (/e\+/i.test(fmt)) {
253
+ const dec = fmt.split(/[eE]/)[0].split(".")[1]?.replace(/[^0]/g, "").length || 0;
254
+ return v.toExponential(dec);
255
+ }
256
+ const hasCurrency = /[$¥€£]/.test(fmt);
257
+ const currencySymbol = fmt.match(/[$¥€£]/)?.[0] || "";
258
+ const hasThousands = fmt.includes("#,##0") || fmt.includes("# ##0") || fmt.includes(",0");
259
+ const decPart = fmt.split(".")[1];
260
+ const decPlaces = decPart ? (decPart.match(/0/g) || []).length : 0;
261
+ const hasNegativeParens = fmt.includes(");(") || fmt.includes(");-(");
262
+ let result;
263
+ if (hasThousands) {
264
+ result = Math.abs(v).toLocaleString("en-US", { minimumFractionDigits: decPlaces, maximumFractionDigits: decPlaces });
265
+ } else if (decPlaces > 0) {
266
+ result = Math.abs(v).toFixed(decPlaces);
267
+ } else {
268
+ result = Math.abs(v).toString();
269
+ }
270
+ if (v < 0) result = hasNegativeParens ? `(${result})` : `-${result}`;
271
+ if (hasCurrency) {
272
+ if (fmt.indexOf(currencySymbol) < fmt.indexOf("0") || fmt.indexOf(currencySymbol) < fmt.indexOf("#")) result = currencySymbol + result;
273
+ else result = result + currencySymbol;
274
+ }
275
+ return result;
276
+ }
277
+ function formatFraction(v, fmt) {
278
+ const denom = fmt.includes("??/??") ? 100 : fmt.includes("?/?") || fmt.includes("?/") ? 10 : 10;
279
+ const whole = Math.floor(Math.abs(v));
280
+ const frac = Math.abs(v) - whole;
281
+ const num = Math.round(frac * denom);
282
+ if (num === 0) return whole.toString();
283
+ const g = gcd(num, denom);
284
+ const n = num / g, d = denom / g;
285
+ if (whole > 0) return `${whole} ${n}/${d}`;
286
+ return (v < 0 ? "-" : "") + `${n}/${d}`;
287
+ }
288
+ function gcd(a, b) {
289
+ while (b) {
290
+ [a, b] = [b, a % b];
291
+ }
292
+ return a;
293
+ }
294
+ function extractStyle(cell) {
295
+ const s = {};
296
+ const f = cell.font;
297
+ if (f) {
298
+ if (f.name) s.fontFamily = f.name;
299
+ if (f.size) s.fontSize = f.size;
300
+ if (f.bold) s.bold = true;
301
+ if (f.italic) s.italic = true;
302
+ if (f.underline) s.underline = true;
303
+ const fc = resolveColor(f.color);
304
+ if (fc) s.fontColor = fc;
305
+ }
306
+ const fill = cell.fill;
307
+ if (fill?.pattern && fill.pattern !== "none") {
308
+ const fg = resolveColor(fill.fgColor), bg = resolveColor(fill.bgColor);
309
+ s.bgColor = fg || bg;
310
+ }
311
+ const a = cell.alignment;
312
+ if (a) {
313
+ if (a.horizontal && a.horizontal !== "fill") s.hAlign = a.horizontal;
314
+ if (a.vertical) s.vAlign = a.vertical;
315
+ if (a.wrapText) s.wrapText = true;
316
+ if (a.textRotation) s.textRotation = a.textRotation;
317
+ }
318
+ const b = cell.border;
319
+ if (b) {
320
+ if (b.top) s.borderTop = borderCss(b.top);
321
+ if (b.right) s.borderRight = borderCss(b.right);
322
+ if (b.bottom) s.borderBottom = borderCss(b.bottom);
323
+ if (b.left) s.borderLeft = borderCss(b.left);
324
+ }
325
+ if (cell.numFmt) s.numFmt = cell.numFmt;
326
+ if (cell.hyperlink) {
327
+ s.hyperlink = typeof cell.hyperlink === "string" ? cell.hyperlink : cell.hyperlink.target;
328
+ } else if (cell.value !== null && typeof cell.value === "object" && "hyperlink" in cell.value) {
329
+ s.hyperlink = cell.value.hyperlink;
330
+ }
331
+ if (cell.note) {
332
+ if (typeof cell.note === "string") s.comment = cell.note;
333
+ else if (cell.note.texts) s.comment = cell.note.texts.map((t) => t.text || t).join("");
334
+ }
335
+ return s;
336
+ }
337
+ function styleToCss(style) {
338
+ const css = {};
339
+ if (style.fontFamily) css.fontFamily = `"${style.fontFamily}", sans-serif`;
340
+ if (style.fontSize) css.fontSize = `${style.fontSize}px`;
341
+ if (style.bold) css.fontWeight = "bold";
342
+ if (style.italic) css.fontStyle = "italic";
343
+ if (style.underline) css.textDecoration = "underline";
344
+ if (style.fontColor) css.color = style.fontColor;
345
+ if (style.bgColor && !/^#ffffff$/i.test(style.bgColor)) css.backgroundColor = style.bgColor;
346
+ if (style.hAlign) css.textAlign = style.hAlign;
347
+ if (style.vAlign) css.verticalAlign = style.vAlign === "middle" ? "middle" : style.vAlign;
348
+ if (style.wrapText) css.whiteSpace = "pre-wrap";
349
+ if (style.textRotation) {
350
+ css[style.textRotation === 255 ? "writingMode" : "transform"] = style.textRotation === 255 ? "vertical-rl" : `rotate(-${style.textRotation}deg)`;
351
+ }
352
+ return css;
353
+ }
354
+ const ROW_NUM_COL_WIDTH = 45;
355
+ const HEADER_ROW_HEIGHT = 22;
356
+ const DEFAULT_ROW_HEIGHT = 22;
357
+ const DEFAULT_COL_WIDTH = 80;
358
+ const MAX_RENDER_ROWS = 5e3;
359
+ async function parseXlsx(input, fileName, mode) {
360
+ const EJS = await getExcelJS();
361
+ const ext = fileName.toLowerCase().split(".").pop() || "";
362
+ const isLegacyXls = ext === "xls";
363
+ const bytes = await readBinaryPreviewAsUint8Array(input);
364
+ const workbook = new EJS.Workbook();
365
+ await workbook.xlsx.load(bytes.buffer);
366
+ const sheets = [];
367
+ const isFast = mode === "fast";
368
+ const fastRowLimit = isFast ? XLSX_PREVIEW_LIMITS.FAST_MODE_ROW_LIMIT : Infinity;
369
+ workbook.eachSheet((worksheet) => {
370
+ const rowCount = worksheet.rowCount;
371
+ const colCount = worksheet.columnCount;
372
+ if (rowCount === 0 || colCount === 0) {
373
+ sheets.push({ name: worksheet.name, cellGrid: [], colWidths: [], rowHeights: [], totalRows: 0, totalCols: 0, imageCount: 0, accRowHeights: [], isLegacyXls });
374
+ return;
375
+ }
376
+ const effectiveRowCount = Math.min(rowCount, fastRowLimit);
377
+ const colWidths = [];
378
+ for (let c = 1; c <= colCount; c++) {
379
+ const col = worksheet.getColumn(c);
380
+ colWidths.push(col.width ? Math.round(Math.max(col.width * 7.5, 50)) : DEFAULT_COL_WIDTH);
381
+ }
382
+ const rowHeights = [];
383
+ for (let r = 1; r <= effectiveRowCount; r++) {
384
+ const row = worksheet.getRow(r);
385
+ rowHeights.push(row.height ? Math.round(row.height * 1.333) : 0);
386
+ }
387
+ const accRowHeights = [];
388
+ let accRow = 0;
389
+ for (let r = 0; r < rowHeights.length; r++) {
390
+ accRowHeights.push(accRow);
391
+ accRow += rowHeights[r] || DEFAULT_ROW_HEIGHT;
392
+ }
393
+ accRowHeights.push(accRow);
394
+ const merges = /* @__PURE__ */ new Map();
395
+ const mergedCells = /* @__PURE__ */ new Set();
396
+ const mergeRedirect = /* @__PURE__ */ new Map();
397
+ const _merges = worksheet._merges;
398
+ if (_merges) {
399
+ for (const [, merge] of Object.entries(_merges)) {
400
+ const m = merge?.model || merge;
401
+ if (m?.top != null && m?.left != null) {
402
+ const tlKey = `${m.top - 1}-${m.left - 1}`;
403
+ merges.set(tlKey, { rs: m.bottom - m.top + 1, cs: m.right - m.left + 1 });
404
+ for (let r = m.top; r <= m.bottom; r++) {
405
+ for (let c = m.left; c <= m.right; c++) {
406
+ if (r !== m.top || c !== m.left) {
407
+ const mergedKey = `${r - 1}-${c - 1}`;
408
+ mergedCells.add(mergedKey);
409
+ mergeRedirect.set(mergedKey, tlKey);
410
+ }
411
+ }
412
+ }
413
+ }
414
+ }
415
+ }
416
+ const modelMerges = worksheet.model?.merges;
417
+ if (modelMerges?.length > 0 && typeof modelMerges[0] === "string") {
418
+ const colLetterToNum = (s) => {
419
+ let n = 0;
420
+ for (let i = 0; i < s.length; i++) n = n * 26 + (s.charCodeAt(i) - 64);
421
+ return n;
422
+ };
423
+ for (const rangeStr of modelMerges) {
424
+ const match = rangeStr.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/);
425
+ if (match) {
426
+ const top = parseInt(match[2]), left = colLetterToNum(match[1]);
427
+ const bottom = parseInt(match[4]), right = colLetterToNum(match[3]);
428
+ const tlKey = `${top - 1}-${left - 1}`;
429
+ if (!merges.has(tlKey)) {
430
+ merges.set(tlKey, { rs: bottom - top + 1, cs: right - left + 1 });
431
+ for (let r = top; r <= bottom; r++) {
432
+ for (let c = left; c <= right; c++) {
433
+ if (r !== top || c !== left) {
434
+ const mergedKey = `${r - 1}-${c - 1}`;
435
+ mergedCells.add(mergedKey);
436
+ mergeRedirect.set(mergedKey, tlKey);
437
+ }
438
+ }
439
+ }
440
+ }
441
+ }
442
+ }
443
+ }
444
+ const cellImages = /* @__PURE__ */ new Map();
445
+ let totalImages = 0;
446
+ if (!isFast) {
447
+ try {
448
+ const wsImages = worksheet.getImages();
449
+ for (const img of wsImages) {
450
+ const imageId = parseInt(img.imageId, 10);
451
+ if (isNaN(imageId)) continue;
452
+ const imageData = workbook.getImage(imageId);
453
+ if (!imageData?.buffer) continue;
454
+ const buf = imageData.buffer;
455
+ const format = detectImageFormat(buf);
456
+ const isSupported = BROWSER_SUPPORTED_FORMATS.has(format);
457
+ const mimeType = getMimeType(format);
458
+ const base64 = bufferToBase64(buf);
459
+ const range = img.range;
460
+ if (!range?.tl) continue;
461
+ const tlRow = range.tl.nativeRow ?? 0;
462
+ const tlCol = range.tl.nativeCol ?? 0;
463
+ const key = `${tlRow}-${tlCol}`;
464
+ const effectiveKey = mergeRedirect.get(key) || key;
465
+ const dims = parseImageDimensions(buf);
466
+ const embedded = {
467
+ dataUrl: isSupported ? `data:${mimeType};base64,${base64}` : null,
468
+ naturalWidth: dims.width,
469
+ naturalHeight: dims.height,
470
+ unsupported: !isSupported,
471
+ formatName: format.toUpperCase()
472
+ };
473
+ if (!cellImages.has(effectiveKey)) cellImages.set(effectiveKey, []);
474
+ cellImages.get(effectiveKey).push(embedded);
475
+ totalImages++;
476
+ }
477
+ } catch (err) {
478
+ console.warn("Image extraction error:", err);
479
+ }
480
+ }
481
+ const cellGrid = [];
482
+ for (let r = 0; r < effectiveRowCount; r++) {
483
+ const row = [];
484
+ const excelRow = worksheet.getRow(r + 1);
485
+ for (let c = 0; c < colCount; c++) {
486
+ const key = `${r}-${c}`;
487
+ if (mergedCells.has(key)) {
488
+ row.push(null);
489
+ continue;
490
+ }
491
+ const cell = excelRow.getCell(c + 1);
492
+ const value = formatCellValue(cell);
493
+ const style = isFast ? {} : extractStyle(cell);
494
+ const merge = merges.get(key);
495
+ const images = cellImages.get(key);
496
+ row.push({
497
+ value,
498
+ style,
499
+ colspan: merge?.cs,
500
+ rowspan: merge?.rs,
501
+ images: images && images.length > 0 ? images : void 0
502
+ });
503
+ }
504
+ cellGrid.push(row);
505
+ }
506
+ sheets.push({
507
+ name: worksheet.name,
508
+ cellGrid,
509
+ colWidths,
510
+ rowHeights,
511
+ totalRows: rowCount,
512
+ totalCols: colCount,
513
+ imageCount: totalImages,
514
+ accRowHeights,
515
+ isLegacyXls
516
+ });
517
+ });
518
+ return sheets;
519
+ }
520
+ function colNumToLetter(num) {
521
+ let result = "";
522
+ num++;
523
+ while (num > 0) {
524
+ num--;
525
+ result = String.fromCharCode(65 + num % 26) + result;
526
+ num = Math.floor(num / 26);
527
+ }
528
+ return result;
529
+ }
530
+ function useDebounce(value, delay) {
531
+ const [debounced, setDebounced] = useState(value);
532
+ useEffect(() => {
533
+ const timer = setTimeout(() => setDebounced(value), delay);
534
+ return () => clearTimeout(timer);
535
+ }, [value, delay]);
536
+ return debounced;
537
+ }
538
+ function XlsxPreview({ content, source, fileName, fileSize }) {
539
+ const [sheets, setSheets] = useState([]);
540
+ const [activeSheet, setActiveSheet] = useState(0);
541
+ const [loading, setLoading] = useState(true);
542
+ const [error, setError] = useState(null);
543
+ const [searchTerm, setSearchTerm] = useState("");
544
+ const [zoom, setZoom] = useState(100);
545
+ const scrollRef = useRef(null);
546
+ const [hoveredComment, setHoveredComment] = useState(null);
547
+ const t = useLocale();
548
+ const [mode, setMode] = useState(() => {
549
+ return fileSize > XLSX_PREVIEW_LIMITS.LARGE_FILE_SIZE ? "fast" : "fidelity";
550
+ });
551
+ const isLargeFile = fileSize > XLSX_PREVIEW_LIMITS.LARGE_FILE_SIZE;
552
+ const isTooLargeForFidelity = fileSize > XLSX_PREVIEW_LIMITS.MAX_FIDELITY_FILE_SIZE;
553
+ const switchMode = useCallback(
554
+ (nextMode) => {
555
+ if (nextMode === mode) return;
556
+ if (nextMode === "fidelity" && isTooLargeForFidelity) {
557
+ const confirmed = window.confirm(
558
+ t.largeFileFidelityConfirm.replace("{fileSize}", formatFileSize(fileSize))
559
+ );
560
+ if (!confirmed) return;
561
+ }
562
+ setMode(nextMode);
563
+ },
564
+ [mode, isTooLargeForFidelity, fileSize]
565
+ );
566
+ const debouncedSearch = useDebounce(searchTerm, 300);
567
+ const [prevDeps, setPrevDeps] = useState({ content, source, fileName, mode });
568
+ if (prevDeps.content !== content || prevDeps.source !== source || prevDeps.fileName !== fileName || prevDeps.mode !== mode) {
569
+ setPrevDeps({ content, source, fileName, mode });
570
+ setLoading(true);
571
+ setError(null);
572
+ }
573
+ useEffect(() => {
574
+ let cancelled = false;
575
+ parseXlsx({ source, content }, fileName, mode).then(
576
+ (result) => {
577
+ if (cancelled) return;
578
+ setSheets(result);
579
+ setLoading(false);
580
+ },
581
+ (err) => {
582
+ if (cancelled) return;
583
+ console.error("XLSX parse error:", err);
584
+ const ext = fileName.toLowerCase().split(".").pop() || "";
585
+ if (ext === "xls") {
586
+ setError(t.legacyXlsError);
587
+ } else {
588
+ setError(err instanceof Error ? err.message : "Failed to parse spreadsheet");
589
+ }
590
+ setLoading(false);
591
+ }
592
+ );
593
+ return () => {
594
+ cancelled = true;
595
+ };
596
+ }, [content, source, fileName, mode]);
597
+ useEffect(() => {
598
+ if (scrollRef.current) scrollRef.current.scrollTop = 0;
599
+ }, [activeSheet]);
600
+ const currentSheet = sheets[activeSheet];
601
+ const filteredRowIndices = useMemo(() => {
602
+ if (!currentSheet || !debouncedSearch) return null;
603
+ const indices = [];
604
+ const term = debouncedSearch.toLowerCase();
605
+ currentSheet.cellGrid.forEach((row, idx) => {
606
+ const match = row.some((cell) => cell && (cell.value.toLowerCase().includes(term) || cell.images && cell.images.length > 0));
607
+ if (match) indices.push(idx);
608
+ });
609
+ return indices;
610
+ }, [currentSheet, debouncedSearch]);
611
+ const showLegacyWarning = currentSheet?.isLegacyXls && !error;
612
+ if (loading) {
613
+ return /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__state", children: [
614
+ /* @__PURE__ */ jsx("div", { className: "fv-spinner fv-spinner--lg" }),
615
+ /* @__PURE__ */ jsx("p", { className: "fv-xlsx__state-msg", children: t.loadingSpreadsheet })
616
+ ] });
617
+ }
618
+ if (error) {
619
+ return /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__state fv-xlsx__state--error", children: [
620
+ /* @__PURE__ */ jsx(AlertTriangleIcon, { size: 36 }),
621
+ /* @__PURE__ */ jsx("p", { className: "fv-xlsx__state-title", children: t.parseFailed }),
622
+ /* @__PURE__ */ jsx("p", { className: "fv-xlsx__state-msg", children: error })
623
+ ] });
624
+ }
625
+ if (sheets.length === 0) {
626
+ return /* @__PURE__ */ jsx("div", { className: "fv-xlsx__state fv-xlsx__state--empty", children: /* @__PURE__ */ jsx("p", { className: "fv-xlsx__state-title", children: t.sheetNotFound }) });
627
+ }
628
+ const rows = currentSheet?.cellGrid || [];
629
+ const isSearch = !!filteredRowIndices;
630
+ const allDisplayRows = isSearch ? filteredRowIndices.map((idx) => ({ row: rows[idx], originalIdx: idx })) : rows.map((row, idx) => ({ row, originalIdx: idx }));
631
+ const isTruncated = !isSearch && allDisplayRows.length > MAX_RENDER_ROWS;
632
+ const displayRows = isTruncated ? allDisplayRows.slice(0, MAX_RENDER_ROWS) : allDisplayRows;
633
+ const totalCols = currentSheet?.totalCols || 0;
634
+ const allColWidths = currentSheet?.colWidths || [];
635
+ return /* @__PURE__ */ jsxs("div", { className: "fv-xlsx", children: [
636
+ showLegacyWarning && /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__legacy-banner", children: [
637
+ /* @__PURE__ */ jsx(AlertTriangleIcon, { size: 14 }),
638
+ /* @__PURE__ */ jsx("span", { children: t.legacyXlsFallbackDesc })
639
+ ] }),
640
+ /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__toolbar", children: [
641
+ /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__toolbar-left", children: [
642
+ /* @__PURE__ */ jsx(Table2Icon, { size: 14 }),
643
+ sheets.length > 1 && /* @__PURE__ */ jsx("div", { className: "fv-xlsx__sheet-tabs", children: sheets.map((sheet, i) => /* @__PURE__ */ jsx(
644
+ "button",
645
+ {
646
+ onClick: () => {
647
+ setActiveSheet(i);
648
+ setSearchTerm("");
649
+ },
650
+ className: `fv-xlsx__sheet-tab ${i === activeSheet ? "fv-xlsx__sheet-tab--active" : ""}`,
651
+ children: sheet.name
652
+ },
653
+ i
654
+ )) }),
655
+ /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__mode-switch", children: [
656
+ /* @__PURE__ */ jsx(
657
+ "button",
658
+ {
659
+ onClick: () => switchMode("fast"),
660
+ className: `fv-xlsx__mode-btn ${mode === "fast" ? "fv-xlsx__mode-btn--active" : ""}`,
661
+ title: t.fastModeTitle,
662
+ children: t.fastMode
663
+ }
664
+ ),
665
+ /* @__PURE__ */ jsx(
666
+ "button",
667
+ {
668
+ onClick: () => switchMode("fidelity"),
669
+ className: `fv-xlsx__mode-btn ${mode === "fidelity" ? "fv-xlsx__mode-btn--active" : ""}`,
670
+ title: t.fidelityModeTitle,
671
+ children: t.fidelityMode
672
+ }
673
+ )
674
+ ] })
675
+ ] }),
676
+ /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__toolbar-right", children: [
677
+ /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__search-wrap", children: [
678
+ /* @__PURE__ */ jsx(SearchIcon, { size: 14, className: "fv-xlsx__search-icon" }),
679
+ /* @__PURE__ */ jsx(
680
+ "input",
681
+ {
682
+ type: "text",
683
+ placeholder: t.search,
684
+ value: searchTerm,
685
+ onChange: (e) => setSearchTerm(e.target.value),
686
+ className: "fv-xlsx__search-input"
687
+ }
688
+ )
689
+ ] }),
690
+ /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__zoom-group", children: [
691
+ /* @__PURE__ */ jsx("button", { onClick: () => setZoom(Math.max(50, zoom - 10)), className: "fv-xlsx__zoom-btn", title: t.zoomOut, children: /* @__PURE__ */ jsx(ZoomOutIcon, { size: 14 }) }),
692
+ /* @__PURE__ */ jsxs("span", { className: "fv-xlsx__zoom-label", children: [
693
+ zoom,
694
+ "%"
695
+ ] }),
696
+ /* @__PURE__ */ jsx("button", { onClick: () => setZoom(Math.min(200, zoom + 10)), className: "fv-xlsx__zoom-btn", title: t.zoomIn, children: /* @__PURE__ */ jsx(ZoomInIcon, { size: 14 }) })
697
+ ] }),
698
+ /* @__PURE__ */ jsx("span", { className: "fv-xlsx__info", children: mode === "fast" && currentSheet?.totalRows > XLSX_PREVIEW_LIMITS.FAST_MODE_ROW_LIMIT ? /* @__PURE__ */ jsxs(Fragment, { children: [
699
+ XLSX_PREVIEW_LIMITS.FAST_MODE_ROW_LIMIT.toLocaleString(),
700
+ " / ",
701
+ currentSheet.totalRows.toLocaleString(),
702
+ " ",
703
+ t.largeFileRows,
704
+ " \xD7 ",
705
+ currentSheet.totalCols,
706
+ " ",
707
+ t.largeFileCols
708
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
709
+ currentSheet?.totalRows?.toLocaleString() || 0,
710
+ " ",
711
+ t.largeFileRows,
712
+ " \xD7 ",
713
+ currentSheet?.totalCols || 0,
714
+ " ",
715
+ t.largeFileCols,
716
+ currentSheet?.imageCount ? ` \xB7 ${currentSheet.imageCount} ${t.largeFileImages}` : ""
717
+ ] }) })
718
+ ] })
719
+ ] }),
720
+ isLargeFile && mode === "fast" && /* @__PURE__ */ jsx("div", { className: "fv-xlsx__large-banner fv-xlsx__large-banner--warning", children: t.largeFileFastModeBanner.replace("{fileSize}", formatFileSize(fileSize)).replace("{rowLimit}", XLSX_PREVIEW_LIMITS.FAST_MODE_ROW_LIMIT.toLocaleString()) }),
721
+ isLargeFile && mode === "fidelity" && /* @__PURE__ */ jsx("div", { className: "fv-xlsx__large-banner fv-xlsx__large-banner--danger", children: t.largeFileFidelityBanner }),
722
+ /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "fv-xlsx__content", children: /* @__PURE__ */ jsx("div", { style: { zoom: zoom / 100 }, children: /* @__PURE__ */ jsxs("table", { className: "fv-xlsx__table", style: { tableLayout: "fixed" }, children: [
723
+ /* @__PURE__ */ jsxs("colgroup", { children: [
724
+ /* @__PURE__ */ jsx("col", { style: { width: ROW_NUM_COL_WIDTH } }),
725
+ allColWidths.map((w, i) => /* @__PURE__ */ jsx("col", { style: { width: w } }, i))
726
+ ] }),
727
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
728
+ /* @__PURE__ */ jsx("th", { className: "fv-xlsx__col-header" }),
729
+ allColWidths.map((w, i) => /* @__PURE__ */ jsx("th", { className: "fv-xlsx__col-header", children: colNumToLetter(i) }, i))
730
+ ] }) }),
731
+ /* @__PURE__ */ jsxs("tbody", { children: [
732
+ displayRows.map(({ row, originalIdx }) => {
733
+ if (!row) return null;
734
+ const rh = currentSheet?.rowHeights[originalIdx] || 0;
735
+ let imgMinHeight = 0;
736
+ for (const cell of row) {
737
+ if (cell?.images) {
738
+ for (const img of cell.images) {
739
+ const h = img.unsupported ? 40 : img.naturalHeight || 60;
740
+ imgMinHeight = Math.max(imgMinHeight, h + 8);
741
+ }
742
+ }
743
+ }
744
+ const effectiveHeight = Math.max(rh, imgMinHeight) || void 0;
745
+ return /* @__PURE__ */ jsxs("tr", { style: effectiveHeight ? { height: effectiveHeight } : void 0, children: [
746
+ /* @__PURE__ */ jsx("td", { className: "fv-xlsx__row-num", children: originalIdx + 1 }),
747
+ row.map((cell, colIdx) => {
748
+ if (!cell) return null;
749
+ const cs = styleToCss(cell.style);
750
+ const db = "1px solid #d1d5db";
751
+ const fs = {
752
+ ...cs,
753
+ padding: "1px 4px",
754
+ overflow: "visible",
755
+ whiteSpace: cs.whiteSpace || "nowrap",
756
+ position: "relative",
757
+ borderTop: cell.style.borderTop || db,
758
+ borderRight: cell.style.borderRight || db,
759
+ borderBottom: cell.style.borderBottom || db,
760
+ borderLeft: cell.style.borderLeft || db
761
+ };
762
+ const hasImages = cell.images && cell.images.length > 0;
763
+ const hasHyperlink = !!cell.style.hyperlink;
764
+ const hasComment = !!cell.style.comment;
765
+ return /* @__PURE__ */ jsxs("td", { style: fs, rowSpan: cell.rowspan || void 0, colSpan: cell.colspan || void 0, children: [
766
+ hasImages ? /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__cell-images", style: { minHeight: 30 }, children: [
767
+ cell.images.map((img, imgIdx) => img.unsupported ? /* @__PURE__ */ jsxs(
768
+ "div",
769
+ {
770
+ className: "fv-xlsx__cell-image-placeholder",
771
+ style: { width: 60, height: 40 },
772
+ title: `${t.unsupportedImageFormat}: ${img.formatName || t.unknown}`,
773
+ children: [
774
+ /* @__PURE__ */ jsx(ImageOffIcon, { size: 14 }),
775
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 8, color: "#9ca3af" }, children: img.formatName })
776
+ ]
777
+ },
778
+ imgIdx
779
+ ) : /* @__PURE__ */ jsx(
780
+ "img",
781
+ {
782
+ src: img.dataUrl,
783
+ alt: "",
784
+ style: {
785
+ width: img.naturalWidth || "auto",
786
+ height: img.naturalHeight || "auto",
787
+ maxWidth: "100%",
788
+ objectFit: "contain",
789
+ display: "block"
790
+ },
791
+ loading: "lazy"
792
+ },
793
+ imgIdx
794
+ )),
795
+ cell.value?.trim() && /* @__PURE__ */ jsx("span", { className: "fv-xlsx__cell-image-label", children: cell.value })
796
+ ] }) : hasHyperlink ? /* @__PURE__ */ jsxs(
797
+ "a",
798
+ {
799
+ href: cell.style.hyperlink,
800
+ target: "_blank",
801
+ rel: "noopener noreferrer",
802
+ className: "fv-xlsx__cell-link",
803
+ style: { fontSize: "inherit", fontFamily: "inherit" },
804
+ children: [
805
+ cell.value,
806
+ /* @__PURE__ */ jsx(ExternalLinkIcon, { size: 10 })
807
+ ]
808
+ }
809
+ ) : cell.value,
810
+ hasComment && /* @__PURE__ */ jsx(
811
+ "span",
812
+ {
813
+ className: "fv-xlsx__comment-dot",
814
+ onMouseEnter: (e) => {
815
+ const rect = e.currentTarget.getBoundingClientRect();
816
+ const container = scrollRef.current?.getBoundingClientRect();
817
+ setHoveredComment({
818
+ row: originalIdx,
819
+ col: colIdx,
820
+ text: cell.style.comment,
821
+ x: rect.left - (container?.left ?? 0) + rect.width / 2,
822
+ y: rect.top - (container?.top ?? 0)
823
+ });
824
+ },
825
+ onMouseLeave: () => setHoveredComment(null)
826
+ }
827
+ )
828
+ ] }, colIdx);
829
+ })
830
+ ] }, originalIdx);
831
+ }),
832
+ isTruncated && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: totalCols + 1, className: "fv-xlsx__truncation-row fv-xlsx__truncation-row--warning", children: t.truncatedRows.replace("{shown}", MAX_RENDER_ROWS.toLocaleString()).replace("{total}", allDisplayRows.length.toLocaleString()) }) }),
833
+ displayRows.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: totalCols + 1, className: "fv-xlsx__truncation-row fv-xlsx__truncation-row--empty", children: searchTerm ? t.noSearchResults : t.noData }) })
834
+ ] })
835
+ ] }) }) }),
836
+ hoveredComment && /* @__PURE__ */ jsxs(
837
+ "div",
838
+ {
839
+ className: "fv-xlsx__comment-tooltip",
840
+ style: { left: hoveredComment.x, top: hoveredComment.y - 8, transform: "translate(-50%, -100%)" },
841
+ children: [
842
+ /* @__PURE__ */ jsxs("div", { className: "fv-xlsx__comment-tooltip-header", children: [
843
+ /* @__PURE__ */ jsx(MessageSquareIcon, { size: 10 }),
844
+ " ",
845
+ t.comment
846
+ ] }),
847
+ /* @__PURE__ */ jsx("p", { className: "fv-xlsx__comment-tooltip-text", children: hoveredComment.text })
848
+ ]
849
+ }
850
+ )
851
+ ] });
852
+ }
853
+ export {
854
+ XlsxPreview
855
+ };
856
+ //# sourceMappingURL=XlsxPreview.js.map