@eternalheart/react-file-preview 1.3.11 → 1.3.13

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 (226) hide show
  1. package/README.md +27 -0
  2. package/README.zh-CN.md +27 -0
  3. package/lib/chunks/{index-nVblatyi.mjs → index-8pqs-pW7.mjs} +3 -3
  4. package/lib/chunks/{index-nVblatyi.mjs.map → index-8pqs-pW7.mjs.map} +1 -1
  5. package/lib/chunks/{index-CCkHAeLh.mjs → index-B0QA380T.mjs} +2 -2
  6. package/lib/chunks/{index-CCkHAeLh.mjs.map → index-B0QA380T.mjs.map} +1 -1
  7. package/lib/chunks/{index-D660ENHx.mjs → index-B2IlXQPc.mjs} +2 -2
  8. package/lib/chunks/{index-D660ENHx.mjs.map → index-B2IlXQPc.mjs.map} +1 -1
  9. package/lib/chunks/{index-BiyghBxu.mjs → index-BgMhDDkd.mjs} +2 -2
  10. package/lib/chunks/{index-BiyghBxu.mjs.map → index-BgMhDDkd.mjs.map} +1 -1
  11. package/lib/chunks/{index-BYdqrqnR.mjs → index-BvjPzMFc.mjs} +2 -2
  12. package/lib/chunks/{index-BYdqrqnR.mjs.map → index-BvjPzMFc.mjs.map} +1 -1
  13. package/lib/chunks/{index-B57rEPin.mjs → index-C8r2-Evl.mjs} +3 -3
  14. package/lib/chunks/{index-B57rEPin.mjs.map → index-C8r2-Evl.mjs.map} +1 -1
  15. package/lib/chunks/{index-CdmkF0CO.mjs → index-CIqtwqgy.mjs} +2 -2
  16. package/lib/chunks/{index-CdmkF0CO.mjs.map → index-CIqtwqgy.mjs.map} +1 -1
  17. package/lib/chunks/{index-C8CyS_sj.mjs → index-CRn7LdHD.mjs} +3 -3
  18. package/lib/chunks/{index-C8CyS_sj.mjs.map → index-CRn7LdHD.mjs.map} +1 -1
  19. package/lib/chunks/{index-CuAALtwC.mjs → index-CWCNvV2X.mjs} +500 -476
  20. package/lib/chunks/index-CWCNvV2X.mjs.map +1 -0
  21. package/lib/chunks/{index-BJ6vTNxc.mjs → index-Ctf8mG_u.mjs} +2 -2
  22. package/lib/chunks/{index-BJ6vTNxc.mjs.map → index-Ctf8mG_u.mjs.map} +1 -1
  23. package/lib/chunks/{index-DBEG73K1.mjs → index-DN4Lc1dx.mjs} +2 -2
  24. package/lib/chunks/{index-DBEG73K1.mjs.map → index-DN4Lc1dx.mjs.map} +1 -1
  25. package/lib/chunks/{index-rUfsiVR8.mjs → index-DYNPnFww.mjs} +2 -2
  26. package/lib/chunks/{index-rUfsiVR8.mjs.map → index-DYNPnFww.mjs.map} +1 -1
  27. package/lib/chunks/{index-qOC5xQyK.mjs → index-DZxzCMp2.mjs} +2 -2
  28. package/lib/chunks/{index-qOC5xQyK.mjs.map → index-DZxzCMp2.mjs.map} +1 -1
  29. package/lib/chunks/index-DfkP0zX3.mjs +357 -0
  30. package/lib/chunks/index-DfkP0zX3.mjs.map +1 -0
  31. package/lib/chunks/{index-DaFlk9dd.mjs → index-DqXfw2eb.mjs} +2 -2
  32. package/lib/chunks/{index-DaFlk9dd.mjs.map → index-DqXfw2eb.mjs.map} +1 -1
  33. package/lib/chunks/index-DtZwBUd0.mjs +270 -0
  34. package/lib/chunks/index-DtZwBUd0.mjs.map +1 -0
  35. package/lib/chunks/{index-C8OrFLd5.mjs → index-_EpQHlUY.mjs} +2 -2
  36. package/lib/chunks/{index-C8OrFLd5.mjs.map → index-_EpQHlUY.mjs.map} +1 -1
  37. package/lib/chunks/{index-ow-nGpuu.mjs → index-p4mew8Hx.mjs} +2 -2
  38. package/lib/chunks/{index-ow-nGpuu.mjs.map → index-p4mew8Hx.mjs.map} +1 -1
  39. package/lib/chunks/{index-_mP0FkqE.mjs → index-sft0uUd7.mjs} +2 -2
  40. package/lib/chunks/{index-_mP0FkqE.mjs.map → index-sft0uUd7.mjs.map} +1 -1
  41. package/lib/chunks/{useShikiHighlight-ClbUXJ2p.mjs → useShikiHighlight-CDDi36pF.mjs} +2 -2
  42. package/lib/chunks/{useShikiHighlight-ClbUXJ2p.mjs.map → useShikiHighlight-CDDi36pF.mjs.map} +1 -1
  43. package/lib/components/ResizableSplit.d.ts +12 -2
  44. package/lib/components/ResizableSplit.d.ts.map +1 -1
  45. package/lib/index.cjs +29 -29
  46. package/lib/index.cjs.map +1 -1
  47. package/lib/index.css +1 -1
  48. package/lib/index.mjs +1 -1
  49. package/lib/renderers/Image/index.d.ts.map +1 -1
  50. package/lib/renderers/Zip/index.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/lib/chunks/index-BCuRRLYt.mjs +0 -222
  53. package/lib/chunks/index-BCuRRLYt.mjs.map +0 -1
  54. package/lib/chunks/index-CuAALtwC.mjs.map +0 -1
  55. package/lib/chunks/index-nLTQXCV7.mjs +0 -301
  56. package/lib/chunks/index-nLTQXCV7.mjs.map +0 -1
  57. package/lib/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  58. package/lib/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  59. package/lib/pdfjs/cmaps/78-H.bcmap +0 -0
  60. package/lib/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  61. package/lib/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  62. package/lib/pdfjs/cmaps/78-V.bcmap +0 -0
  63. package/lib/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  64. package/lib/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  65. package/lib/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  66. package/lib/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  67. package/lib/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  68. package/lib/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  69. package/lib/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  70. package/lib/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  71. package/lib/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  72. package/lib/pdfjs/cmaps/Add-H.bcmap +0 -0
  73. package/lib/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  74. package/lib/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  75. package/lib/pdfjs/cmaps/Add-V.bcmap +0 -0
  76. package/lib/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  77. package/lib/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  78. package/lib/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  79. package/lib/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  80. package/lib/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  81. package/lib/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  82. package/lib/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  83. package/lib/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  84. package/lib/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  85. package/lib/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  86. package/lib/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  87. package/lib/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  88. package/lib/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  89. package/lib/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  90. package/lib/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  91. package/lib/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  92. package/lib/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  93. package/lib/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  94. package/lib/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  95. package/lib/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  96. package/lib/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  97. package/lib/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  98. package/lib/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  99. package/lib/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  100. package/lib/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  101. package/lib/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  102. package/lib/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  103. package/lib/pdfjs/cmaps/B5-H.bcmap +0 -0
  104. package/lib/pdfjs/cmaps/B5-V.bcmap +0 -0
  105. package/lib/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  106. package/lib/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  107. package/lib/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  108. package/lib/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  109. package/lib/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  110. package/lib/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  111. package/lib/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  112. package/lib/pdfjs/cmaps/CNS2-V.bcmap +0 -3
  113. package/lib/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  114. package/lib/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  115. package/lib/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  116. package/lib/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  117. package/lib/pdfjs/cmaps/ETenms-B5-H.bcmap +0 -3
  118. package/lib/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  119. package/lib/pdfjs/cmaps/EUC-H.bcmap +0 -0
  120. package/lib/pdfjs/cmaps/EUC-V.bcmap +0 -0
  121. package/lib/pdfjs/cmaps/Ext-H.bcmap +0 -0
  122. package/lib/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  123. package/lib/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  124. package/lib/pdfjs/cmaps/Ext-V.bcmap +0 -0
  125. package/lib/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  126. package/lib/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  127. package/lib/pdfjs/cmaps/GB-H.bcmap +0 -4
  128. package/lib/pdfjs/cmaps/GB-V.bcmap +0 -0
  129. package/lib/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  130. package/lib/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  131. package/lib/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  132. package/lib/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  133. package/lib/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  134. package/lib/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  135. package/lib/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  136. package/lib/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  137. package/lib/pdfjs/cmaps/GBT-H.bcmap +0 -0
  138. package/lib/pdfjs/cmaps/GBT-V.bcmap +0 -0
  139. package/lib/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  140. package/lib/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  141. package/lib/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  142. package/lib/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  143. package/lib/pdfjs/cmaps/H.bcmap +0 -0
  144. package/lib/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  145. package/lib/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  146. package/lib/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  147. package/lib/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  148. package/lib/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  149. package/lib/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  150. package/lib/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  151. package/lib/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  152. package/lib/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  153. package/lib/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  154. package/lib/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  155. package/lib/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  156. package/lib/pdfjs/cmaps/Hankaku.bcmap +0 -0
  157. package/lib/pdfjs/cmaps/Hiragana.bcmap +0 -0
  158. package/lib/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  159. package/lib/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  160. package/lib/pdfjs/cmaps/KSC-H.bcmap +0 -0
  161. package/lib/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  162. package/lib/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  163. package/lib/pdfjs/cmaps/KSC-V.bcmap +0 -0
  164. package/lib/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  165. package/lib/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  166. package/lib/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  167. package/lib/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  168. package/lib/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  169. package/lib/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  170. package/lib/pdfjs/cmaps/Katakana.bcmap +0 -0
  171. package/lib/pdfjs/cmaps/LICENSE +0 -36
  172. package/lib/pdfjs/cmaps/NWP-H.bcmap +0 -0
  173. package/lib/pdfjs/cmaps/NWP-V.bcmap +0 -0
  174. package/lib/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  175. package/lib/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  176. package/lib/pdfjs/cmaps/Roman.bcmap +0 -0
  177. package/lib/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  178. package/lib/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  179. package/lib/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  180. package/lib/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  181. package/lib/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  182. package/lib/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  183. package/lib/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  184. package/lib/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  185. package/lib/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  186. package/lib/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  187. package/lib/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  188. package/lib/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  189. package/lib/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  190. package/lib/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  191. package/lib/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  192. package/lib/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  193. package/lib/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  194. package/lib/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  195. package/lib/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  196. package/lib/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  197. package/lib/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  198. package/lib/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  199. package/lib/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  200. package/lib/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  201. package/lib/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  202. package/lib/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  203. package/lib/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  204. package/lib/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  205. package/lib/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  206. package/lib/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  207. package/lib/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  208. package/lib/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  209. package/lib/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  210. package/lib/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  211. package/lib/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  212. package/lib/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  213. package/lib/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  214. package/lib/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  215. package/lib/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  216. package/lib/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  217. package/lib/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  218. package/lib/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  219. package/lib/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  220. package/lib/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  221. package/lib/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  222. package/lib/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  223. package/lib/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  224. package/lib/pdfjs/cmaps/V.bcmap +0 -0
  225. package/lib/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  226. package/lib/pdfjs/pdf.worker.min.mjs +0 -21
package/README.md CHANGED
@@ -111,6 +111,33 @@ configurePdfjs({
111
111
  });
112
112
  ```
113
113
 
114
+ ### Vite Bundler Note (AVIF Decoder)
115
+
116
+ If your project bundler is Vite and you happen to have `@jsquash/avif` installed (transitively or directly), the production build may fail with:
117
+
118
+ ```
119
+ [commonjs--resolver] Invalid value "iife" for option "worker.format"
120
+ - UMD and IIFE output formats are not supported for code-splitting builds.
121
+ file: .../@jsquash/avif/codec/enc/avif_enc_mt.js
122
+ ```
123
+
124
+ This happens because `@jsquash/avif` ships a multi-threaded worker that uses code-splitting, while Vite's default `worker.format` is `'iife'`, which does not support split chunks.
125
+
126
+ **Fix** — add this to your `vite.config.ts`:
127
+
128
+ ```ts
129
+ export default defineConfig({
130
+ // ... your existing config
131
+ worker: {
132
+ format: 'es',
133
+ },
134
+ });
135
+ ```
136
+
137
+ `'es'` produces module workers (`type: 'module'`), supported by all modern browsers and compatible with code-splitting.
138
+
139
+ > Note: `@jsquash/avif` is only used as a fallback when the browser does not natively support AVIF (Chrome 85+, Firefox 93+, Safari 16+ all support it natively). If your target browsers cover the native list, you can also remove `@jsquash/avif` from your dependencies entirely.
140
+
114
141
  ## 🚀 Quick Start
115
142
 
116
143
  📖 **New to this library?** Check out the [Quick Start Guide](./QUICK_START.md) for a 5-minute introduction!
package/README.zh-CN.md CHANGED
@@ -109,6 +109,33 @@ configurePdfjs({
109
109
  });
110
110
  ```
111
111
 
112
+ ### Vite 打包提示(AVIF 解码器)
113
+
114
+ 如果你的项目使用 Vite 打包,且项目里安装了 `@jsquash/avif`(直接安装或被其它依赖间接引入),生产构建时可能报错:
115
+
116
+ ```
117
+ [commonjs--resolver] Invalid value "iife" for option "worker.format"
118
+ - UMD and IIFE output formats are not supported for code-splitting builds.
119
+ file: .../@jsquash/avif/codec/enc/avif_enc_mt.js
120
+ ```
121
+
122
+ 原因:`@jsquash/avif` 内部包含一个使用代码分割的多线程 worker,而 Vite 默认的 `worker.format` 是 `'iife'`,不支持多 chunk 拆分。
123
+
124
+ **解决方法** —— 在你的 `vite.config.ts` 中加入:
125
+
126
+ ```ts
127
+ export default defineConfig({
128
+ // ... 原有配置
129
+ worker: {
130
+ format: 'es',
131
+ },
132
+ });
133
+ ```
134
+
135
+ `'es'` 会产生 module worker(`type: 'module'`),现代浏览器全部支持,且兼容代码分割。
136
+
137
+ > 说明:`@jsquash/avif` 仅在浏览器不原生支持 AVIF 时作为兜底使用(Chrome 85+、Firefox 93+、Safari 16+ 均已原生支持)。如果你的目标浏览器都覆盖原生支持范围,也可以直接从依赖中移除 `@jsquash/avif`。
138
+
112
139
  ## 🚀 快速开始
113
140
 
114
141
  📖 **第一次使用?** 查看 [快速开始指南](./QUICK_START.md) 获取 5 分钟入门教程!
@@ -1,7 +1,7 @@
1
1
  import { jsx as r } from "react/jsx-runtime";
2
2
  import { useState as s, useEffect as b } from "react";
3
- import { u as x, a as y, y as v, E } from "./index-CuAALtwC.mjs";
4
- import { u as N } from "./useShikiHighlight-ClbUXJ2p.mjs";
3
+ import { u as x, a as y, y as v, E } from "./index-CWCNvV2X.mjs";
4
+ import { u as N } from "./useShikiHighlight-CDDi36pF.mjs";
5
5
  import { R as T } from "./RendererError-BH6fzLrN.mjs";
6
6
  const C = ({
7
7
  url: l,
@@ -52,4 +52,4 @@ const C = ({
52
52
  export {
53
53
  C as TextRenderer
54
54
  };
55
- //# sourceMappingURL=index-nVblatyi.mjs.map
55
+ //# sourceMappingURL=index-8pqs-pW7.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-nVblatyi.mjs","sources":["../../src/renderers/Text/index.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { fetchTextUtf8, getLanguageFromFileName } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { useShikiHighlight } from '../../hooks/useShikiHighlight';\nimport { RendererError } from '../RendererError';\n\ninterface TextRendererProps {\n url: string;\n fileName: string;\n wordWrap?: boolean;\n htmlPreview?: boolean;\n}\n\nexport const TextRenderer: React.FC<TextRendererProps> = ({\n url,\n fileName,\n wordWrap = true,\n htmlPreview = false,\n}) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [content, setContent] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const language = getLanguageFromFileName(fileName);\n const { html: highlighted } = useShikiHighlight(\n language !== 'text' ? content : '',\n language,\n );\n\n useEffect(() => {\n const controller = new AbortController();\n const loadText = async () => {\n try {\n setLoading(true);\n setError(null);\n const text = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(text);\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n setError(t('text.load_failed'));\n console.error(err);\n } finally {\n setLoading(false);\n }\n };\n\n loadText();\n return () => controller.abort();\n }, [url]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error) {\n return <RendererError message={error} />;\n }\n\n // HTML 预览模式\n if (htmlPreview && (language === 'html')) {\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-bg-surface-toolbar\">\n <iframe\n srcDoc={content}\n sandbox=\"allow-same-origin\"\n className=\"rfp-w-full rfp-h-full rfp-border-0\"\n title={fileName}\n />\n </div>\n );\n }\n\n // 源码模式\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n {language === 'text' || !highlighted ? (\n <pre\n className={`rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm ${\n wordWrap ? 'rfp-whitespace-pre-wrap rfp-break-words' : 'rfp-whitespace-pre'\n }`}\n >\n {content}\n </pre>\n ) : (\n <div\n className={`rfp-shiki-wrapper with-line-numbers ${wordWrap ? '' : 'no-wrap'}`}\n dangerouslySetInnerHTML={{ __html: highlighted }}\n />\n )}\n </div>\n );\n};\n"],"names":["TextRenderer","url","fileName","wordWrap","htmlPreview","t","useTranslator","fetcher","useFetcher","content","setContent","useState","loading","setLoading","error","setError","language","getLanguageFromFileName","highlighted","useShikiHighlight","useEffect","controller","text","fetchTextUtf8","err","jsx","RendererError"],"mappings":";;;;;AAcO,MAAMA,IAA4C,CAAC;AAAA,EACxD,KAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,aAAAC,IAAc;AAChB,MAAM;AACJ,QAAMC,IAAIC,EAAA,GACJC,IAAUC,EAAA,GACV,CAACC,GAASC,CAAU,IAAIC,EAAiB,EAAE,GAC3C,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI,GACrC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAChDK,IAAWC,EAAwBf,CAAQ,GAC3C,EAAE,MAAMgB,EAAA,IAAgBC;AAAA,IAC5BH,MAAa,SAASP,IAAU;AAAA,IAChCO;AAAA,EAAA;AAwBF,SArBAI,EAAU,MAAM;AACd,UAAMC,IAAa,IAAI,gBAAA;AAgBvB,YAfiB,YAAY;AAC3B,UAAI;AACF,QAAAR,EAAW,EAAI,GACfE,EAAS,IAAI;AACb,cAAMO,IAAO,MAAMC,EAActB,GAAK,EAAE,SAAAM,GAAS,QAAQc,EAAW,QAAQ;AAC5E,QAAAX,EAAWY,CAAI;AAAA,MACjB,SAASE,GAAU;AACjB,YAAIA,EAAI,SAAS,aAAc;AAC/B,QAAAT,EAASV,EAAE,kBAAkB,CAAC,GAC9B,QAAQ,MAAMmB,CAAG;AAAA,MACnB,UAAA;AACE,QAAAX,EAAW,EAAK;AAAA,MAClB;AAAA,IACF,GAEA,GACO,MAAMQ,EAAW,MAAA;AAAA,EAC1B,GAAG,CAACpB,CAAG,CAAC,GAEJW,IAEA,gBAAAa,EAAC,SAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,qHAAoH,EAAA,CACrI,IAIAX,IACK,gBAAAW,EAACC,GAAA,EAAc,SAASZ,EAAA,CAAO,IAIpCV,KAAgBY,MAAa,SAE7B,gBAAAS,EAAC,OAAA,EAAI,WAAU,gDACb,UAAA,gBAAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,QAAQhB;AAAA,MACR,SAAQ;AAAA,MACR,WAAU;AAAA,MACV,OAAOP;AAAA,IAAA;AAAA,EAAA,GAEX,sBAMD,OAAA,EAAI,WAAU,0DACZ,UAAAc,MAAa,UAAU,CAACE,IACvB,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,yDACTtB,IAAW,4CAA4C,oBACzD;AAAA,MAEC,UAAAM;AAAA,IAAA;AAAA,EAAA,IAGH,gBAAAgB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,uCAAuCtB,IAAW,KAAK,SAAS;AAAA,MAC3E,yBAAyB,EAAE,QAAQe,EAAA;AAAA,IAAY;AAAA,EAAA,GAGrD;AAEJ;"}
1
+ {"version":3,"file":"index-8pqs-pW7.mjs","sources":["../../src/renderers/Text/index.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { fetchTextUtf8, getLanguageFromFileName } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { useShikiHighlight } from '../../hooks/useShikiHighlight';\nimport { RendererError } from '../RendererError';\n\ninterface TextRendererProps {\n url: string;\n fileName: string;\n wordWrap?: boolean;\n htmlPreview?: boolean;\n}\n\nexport const TextRenderer: React.FC<TextRendererProps> = ({\n url,\n fileName,\n wordWrap = true,\n htmlPreview = false,\n}) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [content, setContent] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const language = getLanguageFromFileName(fileName);\n const { html: highlighted } = useShikiHighlight(\n language !== 'text' ? content : '',\n language,\n );\n\n useEffect(() => {\n const controller = new AbortController();\n const loadText = async () => {\n try {\n setLoading(true);\n setError(null);\n const text = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(text);\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n setError(t('text.load_failed'));\n console.error(err);\n } finally {\n setLoading(false);\n }\n };\n\n loadText();\n return () => controller.abort();\n }, [url]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error) {\n return <RendererError message={error} />;\n }\n\n // HTML 预览模式\n if (htmlPreview && (language === 'html')) {\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-bg-surface-toolbar\">\n <iframe\n srcDoc={content}\n sandbox=\"allow-same-origin\"\n className=\"rfp-w-full rfp-h-full rfp-border-0\"\n title={fileName}\n />\n </div>\n );\n }\n\n // 源码模式\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n {language === 'text' || !highlighted ? (\n <pre\n className={`rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm ${\n wordWrap ? 'rfp-whitespace-pre-wrap rfp-break-words' : 'rfp-whitespace-pre'\n }`}\n >\n {content}\n </pre>\n ) : (\n <div\n className={`rfp-shiki-wrapper with-line-numbers ${wordWrap ? '' : 'no-wrap'}`}\n dangerouslySetInnerHTML={{ __html: highlighted }}\n />\n )}\n </div>\n );\n};\n"],"names":["TextRenderer","url","fileName","wordWrap","htmlPreview","t","useTranslator","fetcher","useFetcher","content","setContent","useState","loading","setLoading","error","setError","language","getLanguageFromFileName","highlighted","useShikiHighlight","useEffect","controller","text","fetchTextUtf8","err","jsx","RendererError"],"mappings":";;;;;AAcO,MAAMA,IAA4C,CAAC;AAAA,EACxD,KAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,aAAAC,IAAc;AAChB,MAAM;AACJ,QAAMC,IAAIC,EAAA,GACJC,IAAUC,EAAA,GACV,CAACC,GAASC,CAAU,IAAIC,EAAiB,EAAE,GAC3C,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI,GACrC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAChDK,IAAWC,EAAwBf,CAAQ,GAC3C,EAAE,MAAMgB,EAAA,IAAgBC;AAAA,IAC5BH,MAAa,SAASP,IAAU;AAAA,IAChCO;AAAA,EAAA;AAwBF,SArBAI,EAAU,MAAM;AACd,UAAMC,IAAa,IAAI,gBAAA;AAgBvB,YAfiB,YAAY;AAC3B,UAAI;AACF,QAAAR,EAAW,EAAI,GACfE,EAAS,IAAI;AACb,cAAMO,IAAO,MAAMC,EAActB,GAAK,EAAE,SAAAM,GAAS,QAAQc,EAAW,QAAQ;AAC5E,QAAAX,EAAWY,CAAI;AAAA,MACjB,SAASE,GAAU;AACjB,YAAIA,EAAI,SAAS,aAAc;AAC/B,QAAAT,EAASV,EAAE,kBAAkB,CAAC,GAC9B,QAAQ,MAAMmB,CAAG;AAAA,MACnB,UAAA;AACE,QAAAX,EAAW,EAAK;AAAA,MAClB;AAAA,IACF,GAEA,GACO,MAAMQ,EAAW,MAAA;AAAA,EAC1B,GAAG,CAACpB,CAAG,CAAC,GAEJW,IAEA,gBAAAa,EAAC,SAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,qHAAoH,EAAA,CACrI,IAIAX,IACK,gBAAAW,EAACC,GAAA,EAAc,SAASZ,EAAA,CAAO,IAIpCV,KAAgBY,MAAa,SAE7B,gBAAAS,EAAC,OAAA,EAAI,WAAU,gDACb,UAAA,gBAAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,QAAQhB;AAAA,MACR,SAAQ;AAAA,MACR,WAAU;AAAA,MACV,OAAOP;AAAA,IAAA;AAAA,EAAA,GAEX,sBAMD,OAAA,EAAI,WAAU,0DACZ,UAAAc,MAAa,UAAU,CAACE,IACvB,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,yDACTtB,IAAW,4CAA4C,oBACzD;AAAA,MAEC,UAAAM;AAAA,IAAA;AAAA,EAAA,IAGH,gBAAAgB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,uCAAuCtB,IAAW,KAAK,SAAS;AAAA,MAC3E,yBAAyB,EAAE,QAAQe,EAAA;AAAA,IAAY;AAAA,EAAA,GAGrD;AAEJ;"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as r, jsxs as t, Fragment as y } from "react/jsx-runtime";
2
2
  import { useState as c, useEffect as E, useMemo as k } from "react";
3
- import { u as F, a as R, P as S, N as n, E as T } from "./index-CuAALtwC.mjs";
3
+ import { u as F, a as R, P as S, N as n, E as T } from "./index-CWCNvV2X.mjs";
4
4
  import { R as _ } from "./RendererError-BH6fzLrN.mjs";
5
5
  const $ = {
6
6
  srt: "srt",
@@ -99,4 +99,4 @@ const $ = {
99
99
  export {
100
100
  P as SubtitleRenderer
101
101
  };
102
- //# sourceMappingURL=index-CCkHAeLh.mjs.map
102
+ //# sourceMappingURL=index-B0QA380T.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-CCkHAeLh.mjs","sources":["../../src/renderers/Subtitle/index.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport {\n parseSubtitle,\n formatSubtitleTime,\n fetchTextUtf8,\n type SubtitleParseResult,\n type SubtitleFormat,\n} from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface SubtitleRendererProps {\n url: string;\n fileName: string;\n}\n\nconst FORMAT_BY_EXT: Record<string, SubtitleFormat> = {\n srt: 'srt',\n vtt: 'vtt',\n lrc: 'lrc',\n elrc: 'elrc',\n ass: 'ass',\n ssa: 'ssa',\n ttml: 'ttml',\n dfxp: 'ttml',\n};\n\nconst getFormat = (fileName: string): SubtitleFormat | undefined => {\n const ext = fileName.split('.').pop()?.toLowerCase() || '';\n return FORMAT_BY_EXT[ext];\n};\n\nexport const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({ url, fileName }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [text, setText] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n setText(await fetchTextUtf8(url, { fetcher, signal: controller.signal }));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.warn('[SubtitleRenderer] Failed to load subtitle:', err instanceof Error ? err.message : String(err));\n setError(t('subtitle.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\n\n const parsed: SubtitleParseResult | null = useMemo(() => {\n if (!text) return null;\n try {\n return parseSubtitle(text, getFormat(fileName));\n } catch (err) {\n // 字幕解析失败通常是格式不支持或文件损坏,用 warn 级别记录\n console.warn('[SubtitleRenderer] Failed to parse subtitle:', err instanceof Error ? err.message : String(err));\n return null;\n }\n }, [text, fileName]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full rfp-bg-[#0f0f12]\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error || !parsed) {\n return <RendererError message={error || t('subtitle.parse_failed')} />;\n }\n\n const isLyric = parsed.format === 'lrc' || parsed.format === 'elrc';\n const meta = parsed.metadata ?? {};\n const dotHover = isLyric ? 'group-hover:rfp-bg-violet-400' : 'group-hover:rfp-bg-sky-400';\n\n return (\n <div className=\"rfp-relative rfp-w-full rfp-h-full rfp-bg-[#0f0f12]\">\n {/* 内容滚动区 */}\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-px-6 md:rfp-px-10 rfp-pt-6 rfp-pb-16 md:rfp-pb-20\">\n <div className=\"rfp-relative rfp-max-w-5xl rfp-mx-auto\">\n {/* vertical line */}\n <div className=\"rfp-absolute rfp-left-[5px] md:rfp-left-[7px] rfp-top-2 rfp-bottom-2 rfp-w-px rfp-bg-surface-1\" />\n\n <ol className=\"rfp-space-y-5 md:rfp-space-y-6\">\n {parsed.cues.map((cue, i) => (\n <li key={`cue-${i}`} className=\"rfp-relative rfp-pl-6 md:rfp-pl-8 rfp-group\">\n {/* dot */}\n <div\n className={`rfp-absolute rfp-left-0 rfp-top-2 rfp-w-3 rfp-h-3 rfp-rounded-full rfp-bg-surface-3 rfp-border-2 rfp-border-[#0f0f12] rfp-transition-colors ${dotHover}`}\n />\n\n <div className=\"rfp-flex rfp-flex-wrap rfp-items-baseline rfp-gap-x-3 rfp-gap-y-1 rfp-mb-1.5\">\n <span className=\"rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums\">\n {formatSubtitleTime(cue.start)}\n </span>\n <span className=\"rfp-text-[11px] rfp-text-fg-disabled\">→</span>\n <span className=\"rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums\">\n {formatSubtitleTime(cue.end)}\n </span>\n <span className=\"rfp-text-[10px] rfp-font-mono rfp-text-fg-disabled rfp-tabular-nums\">\n #{cue.id ?? i + 1}\n </span>\n {cue.style && (\n <span className=\"rfp-text-[9px] rfp-uppercase rfp-tracking-widest rfp-text-fg-tertiary rfp-px-1.5 rfp-py-0.5 rfp-rounded rfp-bg-surface-1 rfp-border rfp-border-line-weak\">\n {cue.style}\n </span>\n )}\n </div>\n\n {cue.words && cue.words.length > 0 ? (\n <div className=\"rfp-flex rfp-flex-wrap rfp-gap-x-1.5 rfp-gap-y-1 rfp-text-base md:rfp-text-lg rfp-text-fg-primary rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors\">\n {cue.words.map((word, wi) => (\n <span\n key={`w-${wi}`}\n className=\"rfp-inline-flex rfp-flex-col rfp-items-start\"\n title={formatSubtitleTime(word.start)}\n >\n <span className=\"rfp-text-[9px] rfp-text-fg-disabled rfp-font-mono rfp-leading-none rfp-tabular-nums\">\n {formatSubtitleTime(word.start).slice(3, 8)}\n </span>\n <span className=\"rfp-leading-snug\">{word.text}</span>\n </span>\n ))}\n </div>\n ) : (\n <p\n className={`rfp-whitespace-pre-wrap rfp-break-words rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors rfp-text-fg-primary ${\n isLyric ? 'rfp-text-base md:rfp-text-xl rfp-font-medium' : 'rfp-text-sm md:rfp-text-base'\n }`}\n >\n {cue.text}\n </p>\n )}\n </li>\n ))}\n </ol>\n </div>\n </div>\n\n {/* 底部状态栏 */}\n <div className=\"rfp-pointer-events-none rfp-absolute rfp-bottom-3 rfp-right-3 md:rfp-bottom-4 md:rfp-right-4 rfp-flex rfp-items-center rfp-gap-2 rfp-px-2.5 rfp-py-1 rfp-rounded-full rfp-bg-surface-nav rfp-backdrop-blur rfp-border rfp-border-line-weak rfp-text-[10px] rfp-text-fg-tertiary rfp-font-mono rfp-tabular-nums\">\n <span>{parsed.cues.length} {isLyric ? t('subtitle.lines') : t('subtitle.cues')}</span>\n {meta.length && (\n <>\n <span className=\"rfp-text-fg-disabled\">·</span>\n <span>{meta.length}</span>\n </>\n )}\n </div>\n </div>\n );\n};\n"],"names":["FORMAT_BY_EXT","getFormat","fileName","ext","_a","SubtitleRenderer","url","t","useTranslator","fetcher","useFetcher","text","setText","useState","loading","setLoading","error","setError","useEffect","controller","fetchTextUtf8","err","parsed","useMemo","parseSubtitle","jsx","RendererError","isLyric","meta","dotHover","jsxs","cue","i","formatSubtitleTime","word","wi","Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"index-B0QA380T.mjs","sources":["../../src/renderers/Subtitle/index.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport {\n parseSubtitle,\n formatSubtitleTime,\n fetchTextUtf8,\n type SubtitleParseResult,\n type SubtitleFormat,\n} from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface SubtitleRendererProps {\n url: string;\n fileName: string;\n}\n\nconst FORMAT_BY_EXT: Record<string, SubtitleFormat> = {\n srt: 'srt',\n vtt: 'vtt',\n lrc: 'lrc',\n elrc: 'elrc',\n ass: 'ass',\n ssa: 'ssa',\n ttml: 'ttml',\n dfxp: 'ttml',\n};\n\nconst getFormat = (fileName: string): SubtitleFormat | undefined => {\n const ext = fileName.split('.').pop()?.toLowerCase() || '';\n return FORMAT_BY_EXT[ext];\n};\n\nexport const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({ url, fileName }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [text, setText] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n setText(await fetchTextUtf8(url, { fetcher, signal: controller.signal }));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.warn('[SubtitleRenderer] Failed to load subtitle:', err instanceof Error ? err.message : String(err));\n setError(t('subtitle.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\n\n const parsed: SubtitleParseResult | null = useMemo(() => {\n if (!text) return null;\n try {\n return parseSubtitle(text, getFormat(fileName));\n } catch (err) {\n // 字幕解析失败通常是格式不支持或文件损坏,用 warn 级别记录\n console.warn('[SubtitleRenderer] Failed to parse subtitle:', err instanceof Error ? err.message : String(err));\n return null;\n }\n }, [text, fileName]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full rfp-bg-[#0f0f12]\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error || !parsed) {\n return <RendererError message={error || t('subtitle.parse_failed')} />;\n }\n\n const isLyric = parsed.format === 'lrc' || parsed.format === 'elrc';\n const meta = parsed.metadata ?? {};\n const dotHover = isLyric ? 'group-hover:rfp-bg-violet-400' : 'group-hover:rfp-bg-sky-400';\n\n return (\n <div className=\"rfp-relative rfp-w-full rfp-h-full rfp-bg-[#0f0f12]\">\n {/* 内容滚动区 */}\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-px-6 md:rfp-px-10 rfp-pt-6 rfp-pb-16 md:rfp-pb-20\">\n <div className=\"rfp-relative rfp-max-w-5xl rfp-mx-auto\">\n {/* vertical line */}\n <div className=\"rfp-absolute rfp-left-[5px] md:rfp-left-[7px] rfp-top-2 rfp-bottom-2 rfp-w-px rfp-bg-surface-1\" />\n\n <ol className=\"rfp-space-y-5 md:rfp-space-y-6\">\n {parsed.cues.map((cue, i) => (\n <li key={`cue-${i}`} className=\"rfp-relative rfp-pl-6 md:rfp-pl-8 rfp-group\">\n {/* dot */}\n <div\n className={`rfp-absolute rfp-left-0 rfp-top-2 rfp-w-3 rfp-h-3 rfp-rounded-full rfp-bg-surface-3 rfp-border-2 rfp-border-[#0f0f12] rfp-transition-colors ${dotHover}`}\n />\n\n <div className=\"rfp-flex rfp-flex-wrap rfp-items-baseline rfp-gap-x-3 rfp-gap-y-1 rfp-mb-1.5\">\n <span className=\"rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums\">\n {formatSubtitleTime(cue.start)}\n </span>\n <span className=\"rfp-text-[11px] rfp-text-fg-disabled\">→</span>\n <span className=\"rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums\">\n {formatSubtitleTime(cue.end)}\n </span>\n <span className=\"rfp-text-[10px] rfp-font-mono rfp-text-fg-disabled rfp-tabular-nums\">\n #{cue.id ?? i + 1}\n </span>\n {cue.style && (\n <span className=\"rfp-text-[9px] rfp-uppercase rfp-tracking-widest rfp-text-fg-tertiary rfp-px-1.5 rfp-py-0.5 rfp-rounded rfp-bg-surface-1 rfp-border rfp-border-line-weak\">\n {cue.style}\n </span>\n )}\n </div>\n\n {cue.words && cue.words.length > 0 ? (\n <div className=\"rfp-flex rfp-flex-wrap rfp-gap-x-1.5 rfp-gap-y-1 rfp-text-base md:rfp-text-lg rfp-text-fg-primary rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors\">\n {cue.words.map((word, wi) => (\n <span\n key={`w-${wi}`}\n className=\"rfp-inline-flex rfp-flex-col rfp-items-start\"\n title={formatSubtitleTime(word.start)}\n >\n <span className=\"rfp-text-[9px] rfp-text-fg-disabled rfp-font-mono rfp-leading-none rfp-tabular-nums\">\n {formatSubtitleTime(word.start).slice(3, 8)}\n </span>\n <span className=\"rfp-leading-snug\">{word.text}</span>\n </span>\n ))}\n </div>\n ) : (\n <p\n className={`rfp-whitespace-pre-wrap rfp-break-words rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors rfp-text-fg-primary ${\n isLyric ? 'rfp-text-base md:rfp-text-xl rfp-font-medium' : 'rfp-text-sm md:rfp-text-base'\n }`}\n >\n {cue.text}\n </p>\n )}\n </li>\n ))}\n </ol>\n </div>\n </div>\n\n {/* 底部状态栏 */}\n <div className=\"rfp-pointer-events-none rfp-absolute rfp-bottom-3 rfp-right-3 md:rfp-bottom-4 md:rfp-right-4 rfp-flex rfp-items-center rfp-gap-2 rfp-px-2.5 rfp-py-1 rfp-rounded-full rfp-bg-surface-nav rfp-backdrop-blur rfp-border rfp-border-line-weak rfp-text-[10px] rfp-text-fg-tertiary rfp-font-mono rfp-tabular-nums\">\n <span>{parsed.cues.length} {isLyric ? t('subtitle.lines') : t('subtitle.cues')}</span>\n {meta.length && (\n <>\n <span className=\"rfp-text-fg-disabled\">·</span>\n <span>{meta.length}</span>\n </>\n )}\n </div>\n </div>\n );\n};\n"],"names":["FORMAT_BY_EXT","getFormat","fileName","ext","_a","SubtitleRenderer","url","t","useTranslator","fetcher","useFetcher","text","setText","useState","loading","setLoading","error","setError","useEffect","controller","fetchTextUtf8","err","parsed","useMemo","parseSubtitle","jsx","RendererError","isLyric","meta","dotHover","jsxs","cue","i","formatSubtitleTime","word","wi","Fragment"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as n, jsxs as g } from "react/jsx-runtime";
2
2
  import { useState as y, useRef as x, useEffect as h } from "react";
3
3
  import w from "video.js";
4
- import { u as k } from "./index-CuAALtwC.mjs";
4
+ import { u as k } from "./index-CWCNvV2X.mjs";
5
5
  import { R as T } from "./RendererError-BH6fzLrN.mjs";
6
6
  const E = /* @__PURE__ */ new Set(["avi", "wmv", "flv"]), j = (r) => {
7
7
  var o;
@@ -111,4 +111,4 @@ const E = /* @__PURE__ */ new Set(["avi", "wmv", "flv"]), j = (r) => {
111
111
  export {
112
112
  V as VideoRenderer
113
113
  };
114
- //# sourceMappingURL=index-D660ENHx.mjs.map
114
+ //# sourceMappingURL=index-B2IlXQPc.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-D660ENHx.mjs","sources":["../../src/renderers/Video/index.tsx"],"sourcesContent":["import { useRef, useEffect, useState } from 'react';\nimport videojs from 'video.js';\nimport 'video.js/dist/video-js.css';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { RendererError } from '../RendererError';\n\ntype VideoJsPlayer = ReturnType<typeof videojs>;\n\ninterface VideoRendererProps {\n url: string;\n fileName?: string;\n}\n\n// 浏览器原生不支持的视频容器(无论编码,<video> 都无法播放)\n// — 这些扩展名直接短路 videojs 初始化,给出明确提示\nconst BROWSER_UNSUPPORTED_EXTS = new Set(['avi', 'wmv', 'flv']);\n\n// 根据 URL 获取视频 MIME 类型\nconst getVideoType = (url: string): string => {\n const ext = url.split('.').pop()?.toLowerCase().split('?')[0] || '';\n const typeMap: Record<string, string> = {\n mp4: 'video/mp4',\n webm: 'video/webm',\n ogg: 'video/ogg',\n ogv: 'video/ogg',\n mov: 'video/quicktime', // MOV 使用 QuickTime MIME 类型\n avi: 'video/x-msvideo',\n mkv: 'video/x-matroska',\n m4v: 'video/mp4',\n '3gp': 'video/3gpp',\n flv: 'video/x-flv',\n };\n return typeMap[ext] || 'video/mp4';\n};\n\n// 获取视频文件扩展名(优先用文件名,blob/HTTP URL 都拿不到真扩展名)\nconst getVideoExt = (url: string, fileName?: string): string => {\n const source = fileName || url;\n return source.split('.').pop()?.toLowerCase().split('?')[0] || '';\n};\n\ninterface ErrorState {\n title: string;\n detail: string;\n}\n\nexport const VideoRenderer: React.FC<VideoRendererProps> = ({ url, fileName }) => {\n const t = useTranslator();\n const [error, setError] = useState<ErrorState | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const videoRef = useRef<HTMLDivElement>(null);\n const playerRef = useRef<VideoJsPlayer | null>(null);\n\n useEffect(() => {\n const videoExt = getVideoExt(url, fileName);\n\n // 已知浏览器不支持的容器:跳过 videojs 初始化,直接展示友好提示\n if (BROWSER_UNSUPPORTED_EXTS.has(videoExt)) {\n setError({\n title: t('video.unsupported_title'),\n detail: t('video.unsupported_detail', { format: videoExt.toUpperCase() }),\n });\n setIsLoading(false);\n return;\n }\n\n // 确保 Video.js 播放器只初始化一次\n if (!playerRef.current && videoRef.current) {\n const videoElement = document.createElement('video-js');\n videoElement.classList.add('vjs-big-play-centered', 'vjs-theme-apple');\n videoRef.current.appendChild(videoElement);\n\n const videoType = getVideoType(url);\n\n // 为特定格式提供多个 MIME 类型作为备用\n let sources: Array<{ src: string; type: string }>;\n\n if (videoType === 'video/quicktime') {\n // MOV 格式 fallback\n sources = [\n { src: url, type: 'video/quicktime' },\n { src: url, type: 'video/mp4' }\n ];\n } else {\n sources = [{ src: url, type: videoType }];\n }\n\n const player = videojs(videoElement, {\n controls: true,\n fill: true,\n preload: 'auto',\n controlBar: {\n children: [\n 'playToggle',\n 'volumePanel',\n 'currentTimeDisplay',\n 'timeDivider',\n 'durationDisplay',\n 'progressControl',\n 'remainingTimeDisplay',\n 'fullscreenToggle'\n ],\n volumePanel: {\n inline: false\n }\n },\n html5: {\n vhs: {\n overrideNative: true\n },\n nativeVideoTracks: false,\n nativeAudioTracks: false,\n nativeTextTracks: false\n },\n sources\n });\n\n // 确保视频保持比例\n const videoEl = player.el().querySelector('video');\n if (videoEl) {\n (videoEl as HTMLVideoElement).style.objectFit = 'contain';\n }\n\n // 监听加载完成\n player.on('loadeddata', () => {\n setIsLoading(false);\n });\n\n player.on('error', () => {\n const err = player.error();\n // 视频加载错误是正常情况(格式不支持、编解码器缺失等),用 warn 级别记录\n console.warn('[VideoRenderer] Video playback error:', err?.message || 'Unknown error');\n\n // MEDIA_ERR_SRC_NOT_SUPPORTED(code=4):编码或容器层面浏览器解不了\n if (err?.code === 4) {\n setError({\n title: t('video.unsupported_title'),\n detail: t('video.unsupported_detail', {\n format: videoExt ? videoExt.toUpperCase() : t('common.unknown_error'),\n }),\n });\n } else {\n setError({\n title: t('video.load_failed'),\n detail: err?.message || t('common.unknown_error'),\n });\n }\n setIsLoading(false);\n });\n\n playerRef.current = player;\n }\n }, [url, fileName, t]);\n\n // 清理函数\n useEffect(() => {\n const player = playerRef.current;\n\n return () => {\n if (player && !player.isDisposed()) {\n player.dispose();\n playerRef.current = null;\n }\n };\n }, []);\n\n if (error) {\n return <RendererError message={error.title} detail={error.detail} />;\n }\n\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-full rfp-h-full rfp-relative\">\n {/* 加载状态 */}\n {isLoading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-3 rfp-backdrop-blur-sm rfp-z-10\">\n <div className=\"rfp-text-center\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-3 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n <p className=\"rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('video.loading')}</p>\n </div>\n </div>\n )}\n\n {/* 视频播放器容器 */}\n <div\n ref={videoRef}\n className=\"rfp-overflow-hidden rfp-w-full rfp-h-full [&_video]:rfp-object-contain\"\n style={{\n boxShadow: '0 20px 60px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05)'\n }}\n />\n </div>\n </div>\n );\n};\n"],"names":["BROWSER_UNSUPPORTED_EXTS","getVideoType","url","ext","_a","getVideoExt","fileName","VideoRenderer","t","useTranslator","error","setError","useState","isLoading","setIsLoading","videoRef","useRef","playerRef","useEffect","videoExt","videoElement","videoType","sources","player","videojs","videoEl","err","RendererError","jsxs","jsx"],"mappings":"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"}
1
+ {"version":3,"file":"index-B2IlXQPc.mjs","sources":["../../src/renderers/Video/index.tsx"],"sourcesContent":["import { useRef, useEffect, useState } from 'react';\nimport videojs from 'video.js';\nimport 'video.js/dist/video-js.css';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { RendererError } from '../RendererError';\n\ntype VideoJsPlayer = ReturnType<typeof videojs>;\n\ninterface VideoRendererProps {\n url: string;\n fileName?: string;\n}\n\n// 浏览器原生不支持的视频容器(无论编码,<video> 都无法播放)\n// — 这些扩展名直接短路 videojs 初始化,给出明确提示\nconst BROWSER_UNSUPPORTED_EXTS = new Set(['avi', 'wmv', 'flv']);\n\n// 根据 URL 获取视频 MIME 类型\nconst getVideoType = (url: string): string => {\n const ext = url.split('.').pop()?.toLowerCase().split('?')[0] || '';\n const typeMap: Record<string, string> = {\n mp4: 'video/mp4',\n webm: 'video/webm',\n ogg: 'video/ogg',\n ogv: 'video/ogg',\n mov: 'video/quicktime', // MOV 使用 QuickTime MIME 类型\n avi: 'video/x-msvideo',\n mkv: 'video/x-matroska',\n m4v: 'video/mp4',\n '3gp': 'video/3gpp',\n flv: 'video/x-flv',\n };\n return typeMap[ext] || 'video/mp4';\n};\n\n// 获取视频文件扩展名(优先用文件名,blob/HTTP URL 都拿不到真扩展名)\nconst getVideoExt = (url: string, fileName?: string): string => {\n const source = fileName || url;\n return source.split('.').pop()?.toLowerCase().split('?')[0] || '';\n};\n\ninterface ErrorState {\n title: string;\n detail: string;\n}\n\nexport const VideoRenderer: React.FC<VideoRendererProps> = ({ url, fileName }) => {\n const t = useTranslator();\n const [error, setError] = useState<ErrorState | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const videoRef = useRef<HTMLDivElement>(null);\n const playerRef = useRef<VideoJsPlayer | null>(null);\n\n useEffect(() => {\n const videoExt = getVideoExt(url, fileName);\n\n // 已知浏览器不支持的容器:跳过 videojs 初始化,直接展示友好提示\n if (BROWSER_UNSUPPORTED_EXTS.has(videoExt)) {\n setError({\n title: t('video.unsupported_title'),\n detail: t('video.unsupported_detail', { format: videoExt.toUpperCase() }),\n });\n setIsLoading(false);\n return;\n }\n\n // 确保 Video.js 播放器只初始化一次\n if (!playerRef.current && videoRef.current) {\n const videoElement = document.createElement('video-js');\n videoElement.classList.add('vjs-big-play-centered', 'vjs-theme-apple');\n videoRef.current.appendChild(videoElement);\n\n const videoType = getVideoType(url);\n\n // 为特定格式提供多个 MIME 类型作为备用\n let sources: Array<{ src: string; type: string }>;\n\n if (videoType === 'video/quicktime') {\n // MOV 格式 fallback\n sources = [\n { src: url, type: 'video/quicktime' },\n { src: url, type: 'video/mp4' }\n ];\n } else {\n sources = [{ src: url, type: videoType }];\n }\n\n const player = videojs(videoElement, {\n controls: true,\n fill: true,\n preload: 'auto',\n controlBar: {\n children: [\n 'playToggle',\n 'volumePanel',\n 'currentTimeDisplay',\n 'timeDivider',\n 'durationDisplay',\n 'progressControl',\n 'remainingTimeDisplay',\n 'fullscreenToggle'\n ],\n volumePanel: {\n inline: false\n }\n },\n html5: {\n vhs: {\n overrideNative: true\n },\n nativeVideoTracks: false,\n nativeAudioTracks: false,\n nativeTextTracks: false\n },\n sources\n });\n\n // 确保视频保持比例\n const videoEl = player.el().querySelector('video');\n if (videoEl) {\n (videoEl as HTMLVideoElement).style.objectFit = 'contain';\n }\n\n // 监听加载完成\n player.on('loadeddata', () => {\n setIsLoading(false);\n });\n\n player.on('error', () => {\n const err = player.error();\n // 视频加载错误是正常情况(格式不支持、编解码器缺失等),用 warn 级别记录\n console.warn('[VideoRenderer] Video playback error:', err?.message || 'Unknown error');\n\n // MEDIA_ERR_SRC_NOT_SUPPORTED(code=4):编码或容器层面浏览器解不了\n if (err?.code === 4) {\n setError({\n title: t('video.unsupported_title'),\n detail: t('video.unsupported_detail', {\n format: videoExt ? videoExt.toUpperCase() : t('common.unknown_error'),\n }),\n });\n } else {\n setError({\n title: t('video.load_failed'),\n detail: err?.message || t('common.unknown_error'),\n });\n }\n setIsLoading(false);\n });\n\n playerRef.current = player;\n }\n }, [url, fileName, t]);\n\n // 清理函数\n useEffect(() => {\n const player = playerRef.current;\n\n return () => {\n if (player && !player.isDisposed()) {\n player.dispose();\n playerRef.current = null;\n }\n };\n }, []);\n\n if (error) {\n return <RendererError message={error.title} detail={error.detail} />;\n }\n\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-full rfp-h-full rfp-relative\">\n {/* 加载状态 */}\n {isLoading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-3 rfp-backdrop-blur-sm rfp-z-10\">\n <div className=\"rfp-text-center\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-3 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n <p className=\"rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('video.loading')}</p>\n </div>\n </div>\n )}\n\n {/* 视频播放器容器 */}\n <div\n ref={videoRef}\n className=\"rfp-overflow-hidden rfp-w-full rfp-h-full [&_video]:rfp-object-contain\"\n style={{\n boxShadow: '0 20px 60px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05)'\n }}\n />\n </div>\n </div>\n );\n};\n"],"names":["BROWSER_UNSUPPORTED_EXTS","getVideoType","url","ext","_a","getVideoExt","fileName","VideoRenderer","t","useTranslator","error","setError","useState","isLoading","setIsLoading","videoRef","useRef","playerRef","useEffect","videoExt","videoElement","videoType","sources","player","videojs","videoEl","err","RendererError","jsxs","jsx"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsxs as L, jsx as m } from "react/jsx-runtime";
2
2
  import { useState as M, useRef as l, useCallback as P, useEffect as j } from "react";
3
3
  import { init as N } from "pptx-preview";
4
- import { u as B, a as F } from "./index-CuAALtwC.mjs";
4
+ import { u as B, a as F } from "./index-CWCNvV2X.mjs";
5
5
  import { R as q } from "./RendererError-BH6fzLrN.mjs";
6
6
  const $ = ({ url: g, tiled: f = !0 }) => {
7
7
  const c = B(), k = F(), [b, w] = M(!0), [v, T] = M(null), [E, z] = M(0), n = l(null), s = l(null), x = l(null), p = l(null), d = l(null), R = l({ width: 0, height: 0 }), u = P(() => {
@@ -133,4 +133,4 @@ const $ = ({ url: g, tiled: f = !0 }) => {
133
133
  export {
134
134
  $ as PptxRenderer
135
135
  };
136
- //# sourceMappingURL=index-BiyghBxu.mjs.map
136
+ //# sourceMappingURL=index-BgMhDDkd.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-BiyghBxu.mjs","sources":["../../src/renderers/Pptx/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { init } from 'pptx-preview';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface PptxRendererProps {\n url: string;\n /** 是否平铺展示所有页面,默认 true */\n tiled?: boolean;\n}\n\nexport const PptxRenderer: React.FC<PptxRendererProps> = ({ url, tiled = true }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [slideCount, setSlideCount] = useState(0);\n const containerRef = useRef<HTMLDivElement>(null);\n const previewerRef = useRef<ReturnType<typeof init> | null>(null);\n const resizeObserverRef = useRef<ResizeObserver | null>(null);\n const arrayBufferRef = useRef<ArrayBuffer | null>(null);\n const resizeTimeoutRef = useRef<number | null>(null);\n const lastDimensionsRef = useRef({ width: 0, height: 0 });\n\n // 计算容器尺寸,带回退逻辑\n const calculateDimensions = useCallback(() => {\n if (!containerRef.current) return { width: 960, height: 540 };\n const rawWidth = containerRef.current.clientWidth;\n const parentWidth = containerRef.current.parentElement?.clientWidth || 0;\n // 如果容器宽度太小,回退到父容器宽度或默认最小值\n const containerWidth = rawWidth > 100 ? rawWidth : (parentWidth > 100 ? parentWidth : 300);\n // 16:9 比例\n const height = Math.floor(containerWidth * 9 / 16);\n return { width: containerWidth, height };\n }, []);\n\n // 重新初始化预览器\n const reinitializePreviewer = useCallback(async () => {\n if (!containerRef.current || !arrayBufferRef.current || slideCount === 0) return;\n\n try {\n // 销毁旧的预览器\n if (previewerRef.current) {\n try {\n previewerRef.current.destroy();\n } catch {\n // 忽略销毁错误\n }\n }\n\n // 清空容器\n containerRef.current.innerHTML = '';\n\n // 获取当前容器尺寸\n const currentDimensions = calculateDimensions();\n\n // 初始化新的预览器,平铺模式下高度按页数计算\n const previewer = init(containerRef.current, {\n width: currentDimensions.width,\n height: tiled ? currentDimensions.height * slideCount : currentDimensions.height,\n mode: tiled ? 'list' : 'slide',\n });\n previewerRef.current = previewer;\n\n // 重新预览\n await previewer.preview(arrayBufferRef.current);\n } catch {\n // 重新初始化失败,静默处理\n }\n }, [calculateDimensions, tiled, slideCount]);\n\n // 监听容器尺寸变化\n useEffect(() => {\n if (!containerRef.current) return;\n\n let isInitialRender = true;\n\n const updateDimensions = () => {\n // 跳过初始渲染时的尺寸检查\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensionsRef.current = calculateDimensions();\n return;\n }\n\n const newDimensions = calculateDimensions();\n\n // 检查尺寸是否真正变化(至少变化10px才触发)\n const lastDimensions = lastDimensionsRef.current;\n const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n if (widthDiff < 10 && heightDiff < 10) {\n return;\n }\n\n // 更新最后的尺寸\n lastDimensionsRef.current = newDimensions;\n\n // 清除之前的定时器\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n\n // 防抖:800ms 后重新初始化预览器\n resizeTimeoutRef.current = window.setTimeout(() => {\n if (previewerRef.current && arrayBufferRef.current) {\n reinitializePreviewer();\n }\n }, 800);\n };\n\n // 创建 ResizeObserver\n resizeObserverRef.current = new ResizeObserver(() => {\n updateDimensions();\n });\n\n // 开始观察容器\n resizeObserverRef.current.observe(containerRef.current);\n\n return () => {\n if (resizeObserverRef.current) {\n resizeObserverRef.current.disconnect();\n }\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n };\n }, [calculateDimensions, reinitializePreviewer]);\n\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!url) return;\n\n let isMounted = true;\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const loadPptx = async () => {\n if (!containerRef.current) return;\n\n setLoading(true);\n setError(null);\n\n // 设置30秒超时\n timeoutId = setTimeout(() => {\n if (isMounted) {\n setError(t('pptx.timeout'));\n setLoading(false);\n }\n }, 30000);\n\n try {\n // 获取文件,处理 CORS 和重定向\n const response = await fetcher(url, {\n mode: 'cors',\n credentials: 'omit',\n redirect: 'follow',\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(t('pptx.not_found'));\n } else if (response.status === 403) {\n throw new Error('无权限访问此文件');\n } else if (response.status >= 500) {\n throw new Error('服务器错误,请稍后重试');\n } else {\n throw new Error(`文件加载失败 (${response.status})`);\n }\n }\n\n const arrayBuffer = await response.arrayBuffer();\n\n // 验证文件大小\n if (arrayBuffer.byteLength === 0) {\n throw new Error('文件为空');\n }\n\n arrayBufferRef.current = arrayBuffer;\n\n if (!isMounted) return;\n\n // 步骤 1: 创建隐藏容器,预处理获取 slideCount\n const hiddenContainer = document.createElement('div');\n hiddenContainer.style.cssText = 'position:absolute;left:-9999px;top:-9999px;visibility:hidden';\n document.body.appendChild(hiddenContainer);\n\n try {\n // 在隐藏容器中初始化临时预览器获取页数\n const tempPreviewer = init(hiddenContainer, {\n width: 100,\n height: 100,\n mode: 'slide',\n });\n\n try {\n await tempPreviewer.preview(arrayBuffer);\n } catch {\n throw new Error(t('pptx.invalid_format'));\n }\n\n const count = tempPreviewer.slideCount;\n\n if (!count || count === 0) {\n throw new Error(t('pptx.no_pages'));\n }\n\n // 销毁临时预览器\n tempPreviewer.destroy();\n\n if (!isMounted) return;\n\n // 保存 slideCount\n setSlideCount(count);\n\n // 步骤 2: 清空真实容器并初始化\n if (containerRef.current) {\n containerRef.current.innerHTML = '';\n }\n\n const currentDimensions = calculateDimensions();\n\n // 步骤 3: 初始化真实预览器,平铺模式下使用正确的总高度\n const previewer = init(containerRef.current, {\n width: currentDimensions.width,\n height: tiled ? currentDimensions.height * count : currentDimensions.height,\n mode: tiled ? 'list' : 'slide',\n });\n previewerRef.current = previewer;\n\n // 步骤 4: 预览 PPTX\n await previewer.preview(arrayBuffer);\n\n // 清除超时定时器\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n if (isMounted) {\n setLoading(false);\n }\n } finally {\n // 移除隐藏容器\n if (document.body.contains(hiddenContainer)) {\n document.body.removeChild(hiddenContainer);\n }\n }\n } catch (err) {\n // 清除超时定时器\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n if (isMounted) {\n let errorMsg = t('pptx.parse_failed');\n if (err instanceof Error) {\n errorMsg = err.message;\n } else if (typeof err === 'string') {\n errorMsg = err;\n }\n setError(errorMsg);\n setLoading(false);\n }\n }\n };\n\n // 延迟执行,使用 requestAnimationFrame 确保 DOM 已准备好\n const timer = setTimeout(() => {\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n loadPptx();\n });\n });\n }, 150);\n\n // 清理函数\n return () => {\n isMounted = false;\n clearTimeout(timer);\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n arrayBufferRef.current = null;\n setSlideCount(0);\n if (previewerRef.current) {\n try {\n previewerRef.current.destroy();\n } catch {\n // 忽略销毁错误\n }\n }\n previewerRef.current = null;\n };\n }, [url, calculateDimensions, tiled]);\n\n return (\n <div className=\"rfp-relative rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full\">\n {/* 加载状态 - 绝对定位覆盖 */}\n {loading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-toolbar rfp-backdrop-blur-sm rfp-z-10\">\n <div className=\"rfp-text-center\">\n <div className=\"rfp-w-10 rfp-h-10 md:rfp-w-12 md:rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n <p className=\"rfp-text-xs md:rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('pptx.loading')}</p>\n </div>\n </div>\n )}\n\n {/* 错误状态 - 绝对定位覆盖 */}\n {error && !loading && (\n <RendererError message={t('pptx.load_failed')} detail={error} />\n )}\n\n {/* PPT 容器 - 仅在非错误状态下渲染 */}\n {!error && (\n <div\n ref={containerRef}\n className=\"pptx-wrapper rfp-w-full rfp-max-w-full md:rfp-max-w-6xl\"\n style={{ opacity: loading ? 0 : 1 }}\n />\n )}\n </div>\n );\n};\n"],"names":["PptxRenderer","url","tiled","t","useTranslator","fetcher","useFetcher","loading","setLoading","useState","error","setError","slideCount","setSlideCount","containerRef","useRef","previewerRef","resizeObserverRef","arrayBufferRef","resizeTimeoutRef","lastDimensionsRef","calculateDimensions","useCallback","rawWidth","parentWidth","_a","containerWidth","height","reinitializePreviewer","currentDimensions","previewer","init","useEffect","isInitialRender","updateDimensions","newDimensions","lastDimensions","widthDiff","heightDiff","isMounted","timeoutId","loadPptx","response","arrayBuffer","hiddenContainer","tempPreviewer","count","err","errorMsg","timer","jsxs","jsx","RendererError"],"mappings":"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"}
1
+ {"version":3,"file":"index-BgMhDDkd.mjs","sources":["../../src/renderers/Pptx/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { init } from 'pptx-preview';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface PptxRendererProps {\n url: string;\n /** 是否平铺展示所有页面,默认 true */\n tiled?: boolean;\n}\n\nexport const PptxRenderer: React.FC<PptxRendererProps> = ({ url, tiled = true }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [slideCount, setSlideCount] = useState(0);\n const containerRef = useRef<HTMLDivElement>(null);\n const previewerRef = useRef<ReturnType<typeof init> | null>(null);\n const resizeObserverRef = useRef<ResizeObserver | null>(null);\n const arrayBufferRef = useRef<ArrayBuffer | null>(null);\n const resizeTimeoutRef = useRef<number | null>(null);\n const lastDimensionsRef = useRef({ width: 0, height: 0 });\n\n // 计算容器尺寸,带回退逻辑\n const calculateDimensions = useCallback(() => {\n if (!containerRef.current) return { width: 960, height: 540 };\n const rawWidth = containerRef.current.clientWidth;\n const parentWidth = containerRef.current.parentElement?.clientWidth || 0;\n // 如果容器宽度太小,回退到父容器宽度或默认最小值\n const containerWidth = rawWidth > 100 ? rawWidth : (parentWidth > 100 ? parentWidth : 300);\n // 16:9 比例\n const height = Math.floor(containerWidth * 9 / 16);\n return { width: containerWidth, height };\n }, []);\n\n // 重新初始化预览器\n const reinitializePreviewer = useCallback(async () => {\n if (!containerRef.current || !arrayBufferRef.current || slideCount === 0) return;\n\n try {\n // 销毁旧的预览器\n if (previewerRef.current) {\n try {\n previewerRef.current.destroy();\n } catch {\n // 忽略销毁错误\n }\n }\n\n // 清空容器\n containerRef.current.innerHTML = '';\n\n // 获取当前容器尺寸\n const currentDimensions = calculateDimensions();\n\n // 初始化新的预览器,平铺模式下高度按页数计算\n const previewer = init(containerRef.current, {\n width: currentDimensions.width,\n height: tiled ? currentDimensions.height * slideCount : currentDimensions.height,\n mode: tiled ? 'list' : 'slide',\n });\n previewerRef.current = previewer;\n\n // 重新预览\n await previewer.preview(arrayBufferRef.current);\n } catch {\n // 重新初始化失败,静默处理\n }\n }, [calculateDimensions, tiled, slideCount]);\n\n // 监听容器尺寸变化\n useEffect(() => {\n if (!containerRef.current) return;\n\n let isInitialRender = true;\n\n const updateDimensions = () => {\n // 跳过初始渲染时的尺寸检查\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensionsRef.current = calculateDimensions();\n return;\n }\n\n const newDimensions = calculateDimensions();\n\n // 检查尺寸是否真正变化(至少变化10px才触发)\n const lastDimensions = lastDimensionsRef.current;\n const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n if (widthDiff < 10 && heightDiff < 10) {\n return;\n }\n\n // 更新最后的尺寸\n lastDimensionsRef.current = newDimensions;\n\n // 清除之前的定时器\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n\n // 防抖:800ms 后重新初始化预览器\n resizeTimeoutRef.current = window.setTimeout(() => {\n if (previewerRef.current && arrayBufferRef.current) {\n reinitializePreviewer();\n }\n }, 800);\n };\n\n // 创建 ResizeObserver\n resizeObserverRef.current = new ResizeObserver(() => {\n updateDimensions();\n });\n\n // 开始观察容器\n resizeObserverRef.current.observe(containerRef.current);\n\n return () => {\n if (resizeObserverRef.current) {\n resizeObserverRef.current.disconnect();\n }\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n };\n }, [calculateDimensions, reinitializePreviewer]);\n\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!url) return;\n\n let isMounted = true;\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const loadPptx = async () => {\n if (!containerRef.current) return;\n\n setLoading(true);\n setError(null);\n\n // 设置30秒超时\n timeoutId = setTimeout(() => {\n if (isMounted) {\n setError(t('pptx.timeout'));\n setLoading(false);\n }\n }, 30000);\n\n try {\n // 获取文件,处理 CORS 和重定向\n const response = await fetcher(url, {\n mode: 'cors',\n credentials: 'omit',\n redirect: 'follow',\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(t('pptx.not_found'));\n } else if (response.status === 403) {\n throw new Error('无权限访问此文件');\n } else if (response.status >= 500) {\n throw new Error('服务器错误,请稍后重试');\n } else {\n throw new Error(`文件加载失败 (${response.status})`);\n }\n }\n\n const arrayBuffer = await response.arrayBuffer();\n\n // 验证文件大小\n if (arrayBuffer.byteLength === 0) {\n throw new Error('文件为空');\n }\n\n arrayBufferRef.current = arrayBuffer;\n\n if (!isMounted) return;\n\n // 步骤 1: 创建隐藏容器,预处理获取 slideCount\n const hiddenContainer = document.createElement('div');\n hiddenContainer.style.cssText = 'position:absolute;left:-9999px;top:-9999px;visibility:hidden';\n document.body.appendChild(hiddenContainer);\n\n try {\n // 在隐藏容器中初始化临时预览器获取页数\n const tempPreviewer = init(hiddenContainer, {\n width: 100,\n height: 100,\n mode: 'slide',\n });\n\n try {\n await tempPreviewer.preview(arrayBuffer);\n } catch {\n throw new Error(t('pptx.invalid_format'));\n }\n\n const count = tempPreviewer.slideCount;\n\n if (!count || count === 0) {\n throw new Error(t('pptx.no_pages'));\n }\n\n // 销毁临时预览器\n tempPreviewer.destroy();\n\n if (!isMounted) return;\n\n // 保存 slideCount\n setSlideCount(count);\n\n // 步骤 2: 清空真实容器并初始化\n if (containerRef.current) {\n containerRef.current.innerHTML = '';\n }\n\n const currentDimensions = calculateDimensions();\n\n // 步骤 3: 初始化真实预览器,平铺模式下使用正确的总高度\n const previewer = init(containerRef.current, {\n width: currentDimensions.width,\n height: tiled ? currentDimensions.height * count : currentDimensions.height,\n mode: tiled ? 'list' : 'slide',\n });\n previewerRef.current = previewer;\n\n // 步骤 4: 预览 PPTX\n await previewer.preview(arrayBuffer);\n\n // 清除超时定时器\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n if (isMounted) {\n setLoading(false);\n }\n } finally {\n // 移除隐藏容器\n if (document.body.contains(hiddenContainer)) {\n document.body.removeChild(hiddenContainer);\n }\n }\n } catch (err) {\n // 清除超时定时器\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n if (isMounted) {\n let errorMsg = t('pptx.parse_failed');\n if (err instanceof Error) {\n errorMsg = err.message;\n } else if (typeof err === 'string') {\n errorMsg = err;\n }\n setError(errorMsg);\n setLoading(false);\n }\n }\n };\n\n // 延迟执行,使用 requestAnimationFrame 确保 DOM 已准备好\n const timer = setTimeout(() => {\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n loadPptx();\n });\n });\n }, 150);\n\n // 清理函数\n return () => {\n isMounted = false;\n clearTimeout(timer);\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n arrayBufferRef.current = null;\n setSlideCount(0);\n if (previewerRef.current) {\n try {\n previewerRef.current.destroy();\n } catch {\n // 忽略销毁错误\n }\n }\n previewerRef.current = null;\n };\n }, [url, calculateDimensions, tiled]);\n\n return (\n <div className=\"rfp-relative rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full\">\n {/* 加载状态 - 绝对定位覆盖 */}\n {loading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-toolbar rfp-backdrop-blur-sm rfp-z-10\">\n <div className=\"rfp-text-center\">\n <div className=\"rfp-w-10 rfp-h-10 md:rfp-w-12 md:rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n <p className=\"rfp-text-xs md:rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('pptx.loading')}</p>\n </div>\n </div>\n )}\n\n {/* 错误状态 - 绝对定位覆盖 */}\n {error && !loading && (\n <RendererError message={t('pptx.load_failed')} detail={error} />\n )}\n\n {/* PPT 容器 - 仅在非错误状态下渲染 */}\n {!error && (\n <div\n ref={containerRef}\n className=\"pptx-wrapper rfp-w-full rfp-max-w-full md:rfp-max-w-6xl\"\n style={{ opacity: loading ? 0 : 1 }}\n />\n )}\n </div>\n );\n};\n"],"names":["PptxRenderer","url","tiled","t","useTranslator","fetcher","useFetcher","loading","setLoading","useState","error","setError","slideCount","setSlideCount","containerRef","useRef","previewerRef","resizeObserverRef","arrayBufferRef","resizeTimeoutRef","lastDimensionsRef","calculateDimensions","useCallback","rawWidth","parentWidth","_a","containerWidth","height","reinitializePreviewer","currentDimensions","previewer","init","useEffect","isInitialRender","updateDimensions","newDimensions","lastDimensions","widthDiff","heightDiff","isMounted","timeoutId","loadPptx","response","arrayBuffer","hiddenContainer","tempPreviewer","count","err","errorMsg","timer","jsxs","jsx","RendererError"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { jsxs as A, jsx as y } from "react/jsx-runtime";
2
2
  import { useState as S, useRef as w, useCallback as u, useEffect as d } from "react";
3
- import { u as F } from "./index-CuAALtwC.mjs";
3
+ import { u as F } from "./index-CWCNvV2X.mjs";
4
4
  import { R as O } from "./RendererError-BH6fzLrN.mjs";
5
5
  import * as _ from "pdfjs-dist/build/pdf.mjs";
6
6
  const Q = ({
@@ -158,4 +158,4 @@ const Q = ({
158
158
  export {
159
159
  Q as PdfRenderer
160
160
  };
161
- //# sourceMappingURL=index-BYdqrqnR.mjs.map
161
+ //# sourceMappingURL=index-BvjPzMFc.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-BYdqrqnR.mjs","sources":["../../src/renderers/Pdf/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { RendererError } from '../RendererError';\n// @ts-ignore - pdfjs-dist 类型路径\nimport * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs';\n\ninterface PdfPageProxy {\n getViewport(opts: { scale: number }): { width: number; height: number };\n render(opts: { canvasContext: CanvasRenderingContext2D; viewport: { width: number; height: number } }): {\n promise: Promise<void>;\n cancel(): void;\n };\n}\n\ninterface PdfDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<PdfPageProxy>;\n destroy(): void;\n}\n\ninterface PageState {\n element: HTMLDivElement;\n rendered: boolean;\n rendering: boolean;\n renderTask: { cancel(): void } | null;\n}\n\ninterface PdfRendererProps {\n url: string;\n zoom: number;\n currentPage: number;\n onPageChange: (page: number) => void;\n onTotalPagesChange: (total: number) => void;\n onPageWidthChange?: (width: number) => void;\n}\n\nexport const PdfRenderer: React.FC<PdfRendererProps> = ({\n url,\n zoom,\n currentPage,\n onPageChange,\n onTotalPagesChange,\n onPageWidthChange,\n}) => {\n const t = useTranslator();\n const [numPages, setNumPages] = useState<number>(0);\n const [error, setError] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const containerRef = useRef<HTMLDivElement>(null);\n const pdfDocRef = useRef<PdfDocumentProxy | null>(null);\n const pageStatesRef = useRef<Map<number, PageState>>(new Map());\n const observerRef = useRef<IntersectionObserver | null>(null);\n\n // 渲染单个页面\n const renderPage = useCallback(async (pageNumber: number, scale: number) => {\n if (!pdfDocRef.current) return;\n const state = pageStatesRef.current.get(pageNumber);\n if (!state || state.rendering) return;\n\n state.rendering = true;\n\n try {\n const page = await pdfDocRef.current.getPage(pageNumber);\n const viewport = page.getViewport({ scale });\n\n const canvas = document.createElement('canvas');\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n canvas.style.maxWidth = '100%';\n canvas.style.height = 'auto';\n canvas.style.borderRadius = '0';\n canvas.style.display = 'block';\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const renderTask = page.render({ canvasContext: ctx, viewport });\n state.renderTask = renderTask;\n await renderTask.promise;\n\n // 上报第一页原始宽度\n if (pageNumber === 1 && onPageWidthChange) {\n const baseViewport = page.getViewport({ scale: 1 });\n onPageWidthChange(baseViewport.width);\n }\n\n state.element.innerHTML = '';\n state.element.appendChild(canvas);\n\n // 页码标签\n const label = document.createElement('div');\n label.textContent = String(pageNumber);\n label.className = 'rfp-absolute rfp-top-2 rfp-right-2 rfp-bg-surface-nav-hover rfp-backdrop-blur-sm rfp-text-fg-primary rfp-text-xs rfp-px-3 rfp-py-1 rfp-rounded-full';\n state.element.appendChild(label);\n\n state.rendered = true;\n } catch (err: any) {\n if (err?.name !== 'RenderingCancelledException') {\n console.error(`渲染页面 ${pageNumber} 失败:`, err);\n }\n } finally {\n state.rendering = false;\n state.renderTask = null;\n }\n }, [onPageWidthChange]);\n\n // 清理页面 canvas\n const clearPageCanvas = useCallback((pageNumber: number) => {\n const state = pageStatesRef.current.get(pageNumber);\n if (!state) return;\n\n // 取消正在进行的渲染\n if (state.renderTask) {\n state.renderTask.cancel();\n state.renderTask = null;\n }\n\n // 清理 canvas\n const canvas = state.element.querySelector('canvas');\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (ctx) {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n }\n canvas.remove();\n }\n\n state.element.innerHTML = '';\n state.rendered = false;\n state.rendering = false;\n }, []);\n\n // 初始化页面占位符\n const initPagePlaceholders = useCallback(() => {\n if (!pdfDocRef.current || !containerRef.current) return;\n\n const wrapper = containerRef.current.querySelector('.pdf-pages') as HTMLDivElement | null;\n if (!wrapper) return;\n\n wrapper.innerHTML = '';\n pageStatesRef.current.clear();\n\n for (let i = 1; i <= numPages; i++) {\n const pageDiv = document.createElement('div');\n pageDiv.className = 'rfp-relative rfp-flex rfp-justify-center rfp-min-h-[800px]';\n pageDiv.setAttribute('data-page-number', String(i));\n wrapper.appendChild(pageDiv);\n\n pageStatesRef.current.set(i, {\n element: pageDiv,\n rendered: false,\n rendering: false,\n renderTask: null,\n });\n\n // 观察页面元素\n if (observerRef.current) {\n observerRef.current.observe(pageDiv);\n }\n }\n }, [numPages]);\n\n // 加载 PDF 文档\n const loadPdf = useCallback(async () => {\n setError(null);\n setIsLoading(true);\n setNumPages(0);\n\n if (pdfDocRef.current) {\n try {\n pdfDocRef.current.destroy();\n } catch {\n // ignore\n }\n pdfDocRef.current = null;\n }\n\n try {\n const loadingTask = pdfjsLib.getDocument({ url });\n pdfDocRef.current = (await loadingTask.promise) as PdfDocumentProxy;\n const total = pdfDocRef.current.numPages;\n\n setNumPages(total);\n onTotalPagesChange(total);\n onPageChange(1);\n setIsLoading(false);\n } catch (err) {\n console.error('PDF 加载错误:', err);\n setError(t('pdf.load_failed'));\n setIsLoading(false);\n }\n }, [url, onTotalPagesChange, onPageChange, t]);\n\n // 滚动处理\n const handleScroll = useCallback(() => {\n if (!containerRef.current || pageStatesRef.current.size === 0) return;\n\n const container = containerRef.current;\n const scrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const scrollCenter = scrollTop + containerHeight / 2;\n\n let currentVisiblePage = 1;\n let minDistance = Infinity;\n\n pageStatesRef.current.forEach((state, pageNumber) => {\n const rect = state.element.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const pageCenter = rect.top - containerRect.top + rect.height / 2 + scrollTop;\n const distance = Math.abs(pageCenter - scrollCenter);\n\n if (distance < minDistance) {\n minDistance = distance;\n currentVisiblePage = pageNumber;\n }\n });\n\n if (currentVisiblePage !== currentPage) {\n onPageChange(currentVisiblePage);\n }\n }, [currentPage, onPageChange]);\n\n // 初始化 IntersectionObserver\n useEffect(() => {\n observerRef.current = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n const pageNumber = Number(entry.target.getAttribute('data-page-number'));\n if (!pageNumber) return;\n\n if (entry.isIntersecting) {\n // 页面进入视口,渲染\n renderPage(pageNumber, zoom);\n } else {\n // 页面离开视口,清理\n const state = pageStatesRef.current.get(pageNumber);\n if (state && state.rendered) {\n clearPageCanvas(pageNumber);\n }\n }\n });\n },\n {\n root: containerRef.current,\n rootMargin: '500px 0px',\n threshold: 0,\n }\n );\n\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n };\n }, [zoom, renderPage, clearPageCanvas]);\n\n // 监听 URL 变化\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (url) {\n loadPdf();\n }\n }, [url, loadPdf]);\n\n // 监听 numPages 变化,初始化占位符\n useEffect(() => {\n if (numPages > 0) {\n // 等待 DOM 更新后初始化占位符\n setTimeout(() => {\n initPagePlaceholders();\n }, 0);\n }\n }, [numPages, initPagePlaceholders]);\n\n // 监听 zoom 变化(防抖)\n useEffect(() => {\n const timer = setTimeout(() => {\n // 清理所有已渲染页面\n pageStatesRef.current.forEach((state, pageNumber) => {\n if (state.rendered) {\n clearPageCanvas(pageNumber);\n }\n });\n\n // 触发重新渲染\n if (observerRef.current && containerRef.current) {\n pageStatesRef.current.forEach((state) => {\n observerRef.current?.unobserve(state.element);\n observerRef.current?.observe(state.element);\n });\n }\n }, 150);\n\n return () => clearTimeout(timer);\n }, [zoom, clearPageCanvas]);\n\n // 监听滚动事件\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener('scroll', handleScroll);\n return () => container.removeEventListener('scroll', handleScroll);\n }, [handleScroll]);\n\n // 清理\n useEffect(() => {\n return () => {\n // 清理所有渲染任务\n pageStatesRef.current.forEach((state) => {\n if (state.renderTask) {\n state.renderTask.cancel();\n }\n });\n pageStatesRef.current.clear();\n\n if (pdfDocRef.current) {\n try {\n pdfDocRef.current.destroy();\n } catch {\n // ignore\n }\n pdfDocRef.current = null;\n }\n };\n }, []);\n\n return (\n <div\n ref={containerRef}\n className=\"rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full rfp-overflow-auto rfp-py-4 md:rfp-py-8 rfp-px-2 md:rfp-px-4\"\n >\n {error && (\n <RendererError message={error} />\n )}\n\n {!error && isLoading && (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-min-h-screen\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n\n {!error && !isLoading && (\n <div className=\"pdf-pages rfp-flex rfp-flex-col rfp-gap-4\" />\n )}\n\n {/* 底部页码指示器 */}\n {numPages > 0 && (\n <div className=\"rfp-sticky rfp-bottom-2 md:rfp-bottom-4 rfp-mt-4 md:rfp-mt-8 rfp-bg-surface-nav-hover rfp-backdrop-blur-xl rfp-text-fg-primary rfp-px-4 rfp-py-2 md:rfp-px-6 md:rfp-py-3 rfp-rounded-full rfp-text-xs md:rfp-text-sm rfp-font-medium rfp-shadow-2xl rfp-border rfp-border-line-weak\">\n 第 {currentPage} 页 / 共 {numPages} 页\n </div>\n )}\n </div>\n );\n};\n"],"names":["PdfRenderer","url","zoom","currentPage","onPageChange","onTotalPagesChange","onPageWidthChange","t","useTranslator","numPages","setNumPages","useState","error","setError","isLoading","setIsLoading","containerRef","useRef","pdfDocRef","pageStatesRef","observerRef","renderPage","useCallback","pageNumber","scale","state","page","viewport","canvas","ctx","renderTask","baseViewport","label","err","clearPageCanvas","initPagePlaceholders","wrapper","i","pageDiv","loadPdf","loadingTask","pdfjsLib","total","handleScroll","container","scrollTop","containerHeight","scrollCenter","currentVisiblePage","minDistance","rect","containerRect","pageCenter","distance","useEffect","entries","entry","timer","_a","_b","jsxs","jsx","RendererError"],"mappings":"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"}
1
+ {"version":3,"file":"index-BvjPzMFc.mjs","sources":["../../src/renderers/Pdf/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { RendererError } from '../RendererError';\n// @ts-ignore - pdfjs-dist 类型路径\nimport * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs';\n\ninterface PdfPageProxy {\n getViewport(opts: { scale: number }): { width: number; height: number };\n render(opts: { canvasContext: CanvasRenderingContext2D; viewport: { width: number; height: number } }): {\n promise: Promise<void>;\n cancel(): void;\n };\n}\n\ninterface PdfDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<PdfPageProxy>;\n destroy(): void;\n}\n\ninterface PageState {\n element: HTMLDivElement;\n rendered: boolean;\n rendering: boolean;\n renderTask: { cancel(): void } | null;\n}\n\ninterface PdfRendererProps {\n url: string;\n zoom: number;\n currentPage: number;\n onPageChange: (page: number) => void;\n onTotalPagesChange: (total: number) => void;\n onPageWidthChange?: (width: number) => void;\n}\n\nexport const PdfRenderer: React.FC<PdfRendererProps> = ({\n url,\n zoom,\n currentPage,\n onPageChange,\n onTotalPagesChange,\n onPageWidthChange,\n}) => {\n const t = useTranslator();\n const [numPages, setNumPages] = useState<number>(0);\n const [error, setError] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const containerRef = useRef<HTMLDivElement>(null);\n const pdfDocRef = useRef<PdfDocumentProxy | null>(null);\n const pageStatesRef = useRef<Map<number, PageState>>(new Map());\n const observerRef = useRef<IntersectionObserver | null>(null);\n\n // 渲染单个页面\n const renderPage = useCallback(async (pageNumber: number, scale: number) => {\n if (!pdfDocRef.current) return;\n const state = pageStatesRef.current.get(pageNumber);\n if (!state || state.rendering) return;\n\n state.rendering = true;\n\n try {\n const page = await pdfDocRef.current.getPage(pageNumber);\n const viewport = page.getViewport({ scale });\n\n const canvas = document.createElement('canvas');\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n canvas.style.maxWidth = '100%';\n canvas.style.height = 'auto';\n canvas.style.borderRadius = '0';\n canvas.style.display = 'block';\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const renderTask = page.render({ canvasContext: ctx, viewport });\n state.renderTask = renderTask;\n await renderTask.promise;\n\n // 上报第一页原始宽度\n if (pageNumber === 1 && onPageWidthChange) {\n const baseViewport = page.getViewport({ scale: 1 });\n onPageWidthChange(baseViewport.width);\n }\n\n state.element.innerHTML = '';\n state.element.appendChild(canvas);\n\n // 页码标签\n const label = document.createElement('div');\n label.textContent = String(pageNumber);\n label.className = 'rfp-absolute rfp-top-2 rfp-right-2 rfp-bg-surface-nav-hover rfp-backdrop-blur-sm rfp-text-fg-primary rfp-text-xs rfp-px-3 rfp-py-1 rfp-rounded-full';\n state.element.appendChild(label);\n\n state.rendered = true;\n } catch (err: any) {\n if (err?.name !== 'RenderingCancelledException') {\n console.error(`渲染页面 ${pageNumber} 失败:`, err);\n }\n } finally {\n state.rendering = false;\n state.renderTask = null;\n }\n }, [onPageWidthChange]);\n\n // 清理页面 canvas\n const clearPageCanvas = useCallback((pageNumber: number) => {\n const state = pageStatesRef.current.get(pageNumber);\n if (!state) return;\n\n // 取消正在进行的渲染\n if (state.renderTask) {\n state.renderTask.cancel();\n state.renderTask = null;\n }\n\n // 清理 canvas\n const canvas = state.element.querySelector('canvas');\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (ctx) {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n }\n canvas.remove();\n }\n\n state.element.innerHTML = '';\n state.rendered = false;\n state.rendering = false;\n }, []);\n\n // 初始化页面占位符\n const initPagePlaceholders = useCallback(() => {\n if (!pdfDocRef.current || !containerRef.current) return;\n\n const wrapper = containerRef.current.querySelector('.pdf-pages') as HTMLDivElement | null;\n if (!wrapper) return;\n\n wrapper.innerHTML = '';\n pageStatesRef.current.clear();\n\n for (let i = 1; i <= numPages; i++) {\n const pageDiv = document.createElement('div');\n pageDiv.className = 'rfp-relative rfp-flex rfp-justify-center rfp-min-h-[800px]';\n pageDiv.setAttribute('data-page-number', String(i));\n wrapper.appendChild(pageDiv);\n\n pageStatesRef.current.set(i, {\n element: pageDiv,\n rendered: false,\n rendering: false,\n renderTask: null,\n });\n\n // 观察页面元素\n if (observerRef.current) {\n observerRef.current.observe(pageDiv);\n }\n }\n }, [numPages]);\n\n // 加载 PDF 文档\n const loadPdf = useCallback(async () => {\n setError(null);\n setIsLoading(true);\n setNumPages(0);\n\n if (pdfDocRef.current) {\n try {\n pdfDocRef.current.destroy();\n } catch {\n // ignore\n }\n pdfDocRef.current = null;\n }\n\n try {\n const loadingTask = pdfjsLib.getDocument({ url });\n pdfDocRef.current = (await loadingTask.promise) as PdfDocumentProxy;\n const total = pdfDocRef.current.numPages;\n\n setNumPages(total);\n onTotalPagesChange(total);\n onPageChange(1);\n setIsLoading(false);\n } catch (err) {\n console.error('PDF 加载错误:', err);\n setError(t('pdf.load_failed'));\n setIsLoading(false);\n }\n }, [url, onTotalPagesChange, onPageChange, t]);\n\n // 滚动处理\n const handleScroll = useCallback(() => {\n if (!containerRef.current || pageStatesRef.current.size === 0) return;\n\n const container = containerRef.current;\n const scrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const scrollCenter = scrollTop + containerHeight / 2;\n\n let currentVisiblePage = 1;\n let minDistance = Infinity;\n\n pageStatesRef.current.forEach((state, pageNumber) => {\n const rect = state.element.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const pageCenter = rect.top - containerRect.top + rect.height / 2 + scrollTop;\n const distance = Math.abs(pageCenter - scrollCenter);\n\n if (distance < minDistance) {\n minDistance = distance;\n currentVisiblePage = pageNumber;\n }\n });\n\n if (currentVisiblePage !== currentPage) {\n onPageChange(currentVisiblePage);\n }\n }, [currentPage, onPageChange]);\n\n // 初始化 IntersectionObserver\n useEffect(() => {\n observerRef.current = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n const pageNumber = Number(entry.target.getAttribute('data-page-number'));\n if (!pageNumber) return;\n\n if (entry.isIntersecting) {\n // 页面进入视口,渲染\n renderPage(pageNumber, zoom);\n } else {\n // 页面离开视口,清理\n const state = pageStatesRef.current.get(pageNumber);\n if (state && state.rendered) {\n clearPageCanvas(pageNumber);\n }\n }\n });\n },\n {\n root: containerRef.current,\n rootMargin: '500px 0px',\n threshold: 0,\n }\n );\n\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n };\n }, [zoom, renderPage, clearPageCanvas]);\n\n // 监听 URL 变化\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (url) {\n loadPdf();\n }\n }, [url, loadPdf]);\n\n // 监听 numPages 变化,初始化占位符\n useEffect(() => {\n if (numPages > 0) {\n // 等待 DOM 更新后初始化占位符\n setTimeout(() => {\n initPagePlaceholders();\n }, 0);\n }\n }, [numPages, initPagePlaceholders]);\n\n // 监听 zoom 变化(防抖)\n useEffect(() => {\n const timer = setTimeout(() => {\n // 清理所有已渲染页面\n pageStatesRef.current.forEach((state, pageNumber) => {\n if (state.rendered) {\n clearPageCanvas(pageNumber);\n }\n });\n\n // 触发重新渲染\n if (observerRef.current && containerRef.current) {\n pageStatesRef.current.forEach((state) => {\n observerRef.current?.unobserve(state.element);\n observerRef.current?.observe(state.element);\n });\n }\n }, 150);\n\n return () => clearTimeout(timer);\n }, [zoom, clearPageCanvas]);\n\n // 监听滚动事件\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener('scroll', handleScroll);\n return () => container.removeEventListener('scroll', handleScroll);\n }, [handleScroll]);\n\n // 清理\n useEffect(() => {\n return () => {\n // 清理所有渲染任务\n pageStatesRef.current.forEach((state) => {\n if (state.renderTask) {\n state.renderTask.cancel();\n }\n });\n pageStatesRef.current.clear();\n\n if (pdfDocRef.current) {\n try {\n pdfDocRef.current.destroy();\n } catch {\n // ignore\n }\n pdfDocRef.current = null;\n }\n };\n }, []);\n\n return (\n <div\n ref={containerRef}\n className=\"rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full rfp-overflow-auto rfp-py-4 md:rfp-py-8 rfp-px-2 md:rfp-px-4\"\n >\n {error && (\n <RendererError message={error} />\n )}\n\n {!error && isLoading && (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-min-h-screen\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n\n {!error && !isLoading && (\n <div className=\"pdf-pages rfp-flex rfp-flex-col rfp-gap-4\" />\n )}\n\n {/* 底部页码指示器 */}\n {numPages > 0 && (\n <div className=\"rfp-sticky rfp-bottom-2 md:rfp-bottom-4 rfp-mt-4 md:rfp-mt-8 rfp-bg-surface-nav-hover rfp-backdrop-blur-xl rfp-text-fg-primary rfp-px-4 rfp-py-2 md:rfp-px-6 md:rfp-py-3 rfp-rounded-full rfp-text-xs md:rfp-text-sm rfp-font-medium rfp-shadow-2xl rfp-border rfp-border-line-weak\">\n 第 {currentPage} 页 / 共 {numPages} 页\n </div>\n )}\n </div>\n );\n};\n"],"names":["PdfRenderer","url","zoom","currentPage","onPageChange","onTotalPagesChange","onPageWidthChange","t","useTranslator","numPages","setNumPages","useState","error","setError","isLoading","setIsLoading","containerRef","useRef","pdfDocRef","pageStatesRef","observerRef","renderPage","useCallback","pageNumber","scale","state","page","viewport","canvas","ctx","renderTask","baseViewport","label","err","clearPageCanvas","initPagePlaceholders","wrapper","i","pageDiv","loadPdf","loadingTask","pdfjsLib","total","handleScroll","container","scrollTop","containerHeight","scrollCenter","currentVisiblePage","minDistance","rect","containerRect","pageCenter","distance","useEffect","entries","entry","timer","_a","_b","jsxs","jsx","RendererError"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as o } from "react/jsx-runtime";
2
2
  import { useState as f, useEffect as u } from "react";
3
- import { u as h, a as g, E as w } from "./index-CuAALtwC.mjs";
4
- import { u as b } from "./useShikiHighlight-ClbUXJ2p.mjs";
3
+ import { u as h, a as g, E as w } from "./index-CWCNvV2X.mjs";
4
+ import { u as b } from "./useShikiHighlight-CDDi36pF.mjs";
5
5
  import { R as y } from "./RendererError-BH6fzLrN.mjs";
6
6
  const N = (r) => {
7
7
  try {
@@ -51,4 +51,4 @@ $2$3`), t = 0;
51
51
  export {
52
52
  X as XmlRenderer
53
53
  };
54
- //# sourceMappingURL=index-B57rEPin.mjs.map
54
+ //# sourceMappingURL=index-C8r2-Evl.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-B57rEPin.mjs","sources":["../../src/renderers/Xml/index.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { useShikiHighlight } from '../../hooks/useShikiHighlight';\nimport { RendererError } from '../RendererError';\n\ninterface XmlRendererProps {\n url: string;\n fileName: string;\n}\n\n/**\n * 用 DOMParser 美化 XML:失败则原样返回\n */\nconst prettyPrintXml = (xml: string): string => {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'application/xml');\n // 检测解析错误\n const errNode = doc.querySelector('parsererror');\n if (errNode) return xml;\n // 使用 XSLT 或手动缩进:这里手动缩进更稳\n const serializer = new XMLSerializer();\n const serialized = serializer.serializeToString(doc);\n return indentXml(serialized);\n } catch {\n return xml;\n }\n};\n\nconst indentXml = (xml: string): string => {\n const PADDING = ' ';\n const reg = /(>)(<)(\\/*)/g;\n let formatted = xml.replace(reg, '$1\\n$2$3');\n // 自闭合和 CDATA 等不处理\n let pad = 0;\n return formatted\n .split('\\n')\n .map((line) => {\n let indent = 0;\n if (/^<\\/\\w/.test(line)) {\n pad = Math.max(pad - 1, 0);\n } else if (/^<\\w[^>]*[^/]>.*$/.test(line) && !/<.+<\\/.+>$/.test(line)) {\n indent = 1;\n }\n const padded = PADDING.repeat(pad) + line;\n pad += indent;\n return padded;\n })\n .join('\\n');\n};\n\nexport const XmlRenderer: React.FC<XmlRendererProps> = ({ url }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [content, setContent] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const { html: highlighted } = useShikiHighlight(content, 'xml');\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n const raw = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(prettyPrintXml(raw));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.error(err);\n setError(t('xml.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error) {\n return <RendererError message={error} />;\n }\n\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n {highlighted ? (\n <div\n className=\"rfp-shiki-wrapper with-line-numbers\"\n dangerouslySetInnerHTML={{ __html: highlighted }}\n />\n ) : (\n <pre className=\"rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words\">\n {content}\n </pre>\n )}\n </div>\n );\n};\n"],"names":["prettyPrintXml","xml","doc","serialized","indentXml","PADDING","reg","formatted","pad","line","indent","padded","XmlRenderer","url","t","useTranslator","fetcher","useFetcher","content","setContent","useState","loading","setLoading","error","setError","highlighted","useShikiHighlight","useEffect","controller","raw","fetchTextUtf8","err","jsx","RendererError"],"mappings":";;;;;AAeA,MAAMA,IAAiB,CAACC,MAAwB;AAC9C,MAAI;AAEF,UAAMC,IADS,IAAI,UAAA,EACA,gBAAgBD,GAAK,iBAAiB;AAGzD,QADgBC,EAAI,cAAc,aAAa,EAClC,QAAOD;AAGpB,UAAME,IADa,IAAI,cAAA,EACO,kBAAkBD,CAAG;AACnD,WAAOE,EAAUD,CAAU;AAAA,EAC7B,QAAQ;AACN,WAAOF;AAAA,EACT;AACF,GAEMG,IAAY,CAACH,MAAwB;AACzC,QAAMI,IAAU,MACVC,IAAM;AACZ,MAAIC,IAAYN,EAAI,QAAQK,GAAK;AAAA,KAAU,GAEvCE,IAAM;AACV,SAAOD,EACJ,MAAM;AAAA,CAAI,EACV,IAAI,CAACE,MAAS;AACb,QAAIC,IAAS;AACb,IAAI,SAAS,KAAKD,CAAI,IACpBD,IAAM,KAAK,IAAIA,IAAM,GAAG,CAAC,IAChB,oBAAoB,KAAKC,CAAI,KAAK,CAAC,aAAa,KAAKA,CAAI,MAClEC,IAAS;AAEX,UAAMC,IAASN,EAAQ,OAAOG,CAAG,IAAIC;AACrC,WAAAD,KAAOE,GACAC;AAAA,EACT,CAAC,EACA,KAAK;AAAA,CAAI;AACd,GAEaC,IAA0C,CAAC,EAAE,KAAAC,QAAU;AAClE,QAAMC,IAAIC,EAAA,GACJC,IAAUC,EAAA,GACV,CAACC,GAASC,CAAU,IAAIC,EAAiB,EAAE,GAC3C,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI,GACrC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAChD,EAAE,MAAMK,EAAA,IAAgBC,EAAkBR,GAAS,KAAK;AAsB9D,SApBAS,EAAU,MAAM;AACd,UAAMC,IAAa,IAAI,gBAAA;AAevB,YAda,YAAY;AACvB,UAAI;AACF,QAAAN,EAAW,EAAI,GACfE,EAAS,IAAI;AACb,cAAMK,IAAM,MAAMC,EAAcjB,GAAK,EAAE,SAAAG,GAAS,QAAQY,EAAW,QAAQ;AAC3E,QAAAT,EAAWnB,EAAe6B,CAAG,CAAC;AAAA,MAChC,SAASE,GAAU;AACjB,YAAIA,EAAI,SAAS,aAAc;AAC/B,gBAAQ,MAAMA,CAAG,GACjBP,EAASV,EAAE,iBAAiB,CAAC;AAAA,MAC/B,UAAA;AACE,QAAAQ,EAAW,EAAK;AAAA,MAClB;AAAA,IACF,GACA,GACO,MAAMM,EAAW,MAAA;AAAA,EAC1B,GAAG,CAACf,CAAG,CAAC,GAEJQ,IAEA,gBAAAW,EAAC,SAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,qHAAoH,EAAA,CACrI,IAIAT,IACK,gBAAAS,EAACC,GAAA,EAAc,SAASV,EAAA,CAAO,IAItC,gBAAAS,EAAC,OAAA,EAAI,WAAU,0DACZ,UAAAP,IACC,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,yBAAyB,EAAE,QAAQP,EAAA;AAAA,IAAY;AAAA,EAAA,IAGjD,gBAAAO,EAAC,OAAA,EAAI,WAAU,iGACZ,aACH,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"index-C8r2-Evl.mjs","sources":["../../src/renderers/Xml/index.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { useShikiHighlight } from '../../hooks/useShikiHighlight';\nimport { RendererError } from '../RendererError';\n\ninterface XmlRendererProps {\n url: string;\n fileName: string;\n}\n\n/**\n * 用 DOMParser 美化 XML:失败则原样返回\n */\nconst prettyPrintXml = (xml: string): string => {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'application/xml');\n // 检测解析错误\n const errNode = doc.querySelector('parsererror');\n if (errNode) return xml;\n // 使用 XSLT 或手动缩进:这里手动缩进更稳\n const serializer = new XMLSerializer();\n const serialized = serializer.serializeToString(doc);\n return indentXml(serialized);\n } catch {\n return xml;\n }\n};\n\nconst indentXml = (xml: string): string => {\n const PADDING = ' ';\n const reg = /(>)(<)(\\/*)/g;\n let formatted = xml.replace(reg, '$1\\n$2$3');\n // 自闭合和 CDATA 等不处理\n let pad = 0;\n return formatted\n .split('\\n')\n .map((line) => {\n let indent = 0;\n if (/^<\\/\\w/.test(line)) {\n pad = Math.max(pad - 1, 0);\n } else if (/^<\\w[^>]*[^/]>.*$/.test(line) && !/<.+<\\/.+>$/.test(line)) {\n indent = 1;\n }\n const padded = PADDING.repeat(pad) + line;\n pad += indent;\n return padded;\n })\n .join('\\n');\n};\n\nexport const XmlRenderer: React.FC<XmlRendererProps> = ({ url }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [content, setContent] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const { html: highlighted } = useShikiHighlight(content, 'xml');\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n const raw = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(prettyPrintXml(raw));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.error(err);\n setError(t('xml.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error) {\n return <RendererError message={error} />;\n }\n\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n {highlighted ? (\n <div\n className=\"rfp-shiki-wrapper with-line-numbers\"\n dangerouslySetInnerHTML={{ __html: highlighted }}\n />\n ) : (\n <pre className=\"rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words\">\n {content}\n </pre>\n )}\n </div>\n );\n};\n"],"names":["prettyPrintXml","xml","doc","serialized","indentXml","PADDING","reg","formatted","pad","line","indent","padded","XmlRenderer","url","t","useTranslator","fetcher","useFetcher","content","setContent","useState","loading","setLoading","error","setError","highlighted","useShikiHighlight","useEffect","controller","raw","fetchTextUtf8","err","jsx","RendererError"],"mappings":";;;;;AAeA,MAAMA,IAAiB,CAACC,MAAwB;AAC9C,MAAI;AAEF,UAAMC,IADS,IAAI,UAAA,EACA,gBAAgBD,GAAK,iBAAiB;AAGzD,QADgBC,EAAI,cAAc,aAAa,EAClC,QAAOD;AAGpB,UAAME,IADa,IAAI,cAAA,EACO,kBAAkBD,CAAG;AACnD,WAAOE,EAAUD,CAAU;AAAA,EAC7B,QAAQ;AACN,WAAOF;AAAA,EACT;AACF,GAEMG,IAAY,CAACH,MAAwB;AACzC,QAAMI,IAAU,MACVC,IAAM;AACZ,MAAIC,IAAYN,EAAI,QAAQK,GAAK;AAAA,KAAU,GAEvCE,IAAM;AACV,SAAOD,EACJ,MAAM;AAAA,CAAI,EACV,IAAI,CAACE,MAAS;AACb,QAAIC,IAAS;AACb,IAAI,SAAS,KAAKD,CAAI,IACpBD,IAAM,KAAK,IAAIA,IAAM,GAAG,CAAC,IAChB,oBAAoB,KAAKC,CAAI,KAAK,CAAC,aAAa,KAAKA,CAAI,MAClEC,IAAS;AAEX,UAAMC,IAASN,EAAQ,OAAOG,CAAG,IAAIC;AACrC,WAAAD,KAAOE,GACAC;AAAA,EACT,CAAC,EACA,KAAK;AAAA,CAAI;AACd,GAEaC,IAA0C,CAAC,EAAE,KAAAC,QAAU;AAClE,QAAMC,IAAIC,EAAA,GACJC,IAAUC,EAAA,GACV,CAACC,GAASC,CAAU,IAAIC,EAAiB,EAAE,GAC3C,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI,GACrC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAChD,EAAE,MAAMK,EAAA,IAAgBC,EAAkBR,GAAS,KAAK;AAsB9D,SApBAS,EAAU,MAAM;AACd,UAAMC,IAAa,IAAI,gBAAA;AAevB,YAda,YAAY;AACvB,UAAI;AACF,QAAAN,EAAW,EAAI,GACfE,EAAS,IAAI;AACb,cAAMK,IAAM,MAAMC,EAAcjB,GAAK,EAAE,SAAAG,GAAS,QAAQY,EAAW,QAAQ;AAC3E,QAAAT,EAAWnB,EAAe6B,CAAG,CAAC;AAAA,MAChC,SAASE,GAAU;AACjB,YAAIA,EAAI,SAAS,aAAc;AAC/B,gBAAQ,MAAMA,CAAG,GACjBP,EAASV,EAAE,iBAAiB,CAAC;AAAA,MAC/B,UAAA;AACE,QAAAQ,EAAW,EAAK;AAAA,MAClB;AAAA,IACF,GACA,GACO,MAAMM,EAAW,MAAA;AAAA,EAC1B,GAAG,CAACf,CAAG,CAAC,GAEJQ,IAEA,gBAAAW,EAAC,SAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,qHAAoH,EAAA,CACrI,IAIAT,IACK,gBAAAS,EAACC,GAAA,EAAc,SAASV,EAAA,CAAO,IAItC,gBAAAS,EAAC,OAAA,EAAI,WAAU,0DACZ,UAAAP,IACC,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,yBAAyB,EAAE,QAAQP,EAAA;AAAA,IAAY;AAAA,EAAA,IAGjD,gBAAAO,EAAC,OAAA,EAAI,WAAU,iGACZ,aACH,GAEJ;AAEJ;"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as t, jsxs as G } from "react/jsx-runtime";
2
2
  import { useState as l, useRef as P, useEffect as E, useCallback as T } from "react";
3
3
  import D from "mammoth";
4
- import { u as N, a as I } from "./index-CuAALtwC.mjs";
4
+ import { u as N, a as I } from "./index-CWCNvV2X.mjs";
5
5
  import { R as S } from "./RendererError-BH6fzLrN.mjs";
6
6
  const H = 1123, b = 60, _ = 50, k = H - b * 2, R = 24, v = {
7
7
  fontFamily: "system-ui, -apple-system, sans-serif",
@@ -102,4 +102,4 @@ const H = 1123, b = 60, _ = 50, k = H - b * 2, R = 24, v = {
102
102
  export {
103
103
  B as DocxRenderer
104
104
  };
105
- //# sourceMappingURL=index-CdmkF0CO.mjs.map
105
+ //# sourceMappingURL=index-CIqtwqgy.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-CdmkF0CO.mjs","sources":["../../src/renderers/Docx/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport mammoth from 'mammoth';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface DocxRendererProps {\n url: string;\n}\n\n// A4 page dimensions (96dpi)\nconst PAGE_HEIGHT = 1123;\nconst PAGE_PADDING_Y = 60;\nconst PAGE_PADDING_X = 50;\nconst PAGE_CONTENT_HEIGHT = PAGE_HEIGHT - PAGE_PADDING_Y * 2;\nconst PAGE_GAP = 24;\n\nconst contentStyle: React.CSSProperties = {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n lineHeight: '1.8',\n color: '#333',\n};\n\nexport const DocxRenderer: React.FC<DocxRendererProps> = ({ url }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [html, setHtml] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [pages, setPages] = useState<string[]>([]);\n const measureRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!url) return;\n\n const loadDocx = async () => {\n setLoading(true);\n setError(null);\n setHtml('');\n\n try {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error('文件加载失败');\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const result = await mammoth.convertToHtml({ arrayBuffer });\n setHtml(result.value);\n } catch (err) {\n console.error('Docx 解析错误:', err);\n setError(t('docx.parse_failed'));\n } finally {\n setLoading(false);\n }\n };\n\n loadDocx();\n }, [url, fetcher, t]);\n\n const paginate = useCallback(() => {\n const container = measureRef.current;\n if (!container || !html) return;\n\n const children = Array.from(container.children) as HTMLElement[];\n if (children.length === 0) {\n setPages([html]);\n return;\n }\n\n const result: string[][] = [[]];\n let currentPageUsed = 0;\n\n for (const child of children) {\n const h = child.offsetHeight;\n\n // If adding this block would exceed page content area and page isn't empty,\n // start a new page\n if (currentPageUsed > 0 && currentPageUsed + h > PAGE_CONTENT_HEIGHT) {\n result.push([]);\n currentPageUsed = 0;\n }\n\n result[result.length - 1].push(child.outerHTML);\n currentPageUsed += h;\n }\n\n // At least one page\n if (result.length === 0) result.push([]);\n\n setPages(result.map(blocks => blocks.join('')));\n }, [html]);\n\n useEffect(() => {\n if (!html || !measureRef.current) return;\n requestAnimationFrame(() => {\n paginate();\n });\n }, [html, paginate]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error) {\n return <RendererError message={error} />;\n }\n\n return (\n <div\n className=\"rfp-w-full rfp-h-full rfp-overflow-auto\"\n style={{ background: 'rgba(0, 0, 0, 0.15)' }}\n >\n {/* Hidden measurement div — same width as page content area */}\n <div\n ref={measureRef}\n dangerouslySetInnerHTML={{ __html: html }}\n style={{\n ...contentStyle,\n position: 'absolute',\n visibility: 'hidden',\n width: `${794 - PAGE_PADDING_X * 2}px`,\n pointerEvents: 'none',\n }}\n />\n\n {/* Visible pages */}\n <div\n className=\"rfp-py-6 md:rfp-py-10 rfp-flex rfp-flex-col rfp-items-center\"\n style={{ gap: `${PAGE_GAP}px` }}\n >\n {(pages.length > 0 ? pages : ['']).map((pageHtml, i) => (\n <div\n key={i}\n style={{\n width: '100%',\n maxWidth: '794px',\n minHeight: `${PAGE_HEIGHT}px`,\n background: 'white',\n boxShadow: '0 2px 12px rgba(0, 0, 0, 0.15)',\n flexShrink: 0,\n padding: `${PAGE_PADDING_Y}px ${PAGE_PADDING_X}px`,\n }}\n >\n <div\n dangerouslySetInnerHTML={{ __html: pageHtml }}\n style={contentStyle}\n />\n </div>\n ))}\n </div>\n </div>\n );\n};\n"],"names":["PAGE_HEIGHT","PAGE_PADDING_Y","PAGE_PADDING_X","PAGE_CONTENT_HEIGHT","PAGE_GAP","contentStyle","DocxRenderer","url","t","useTranslator","fetcher","useFetcher","html","setHtml","useState","loading","setLoading","error","setError","pages","setPages","measureRef","useRef","useEffect","response","arrayBuffer","result","mammoth","err","paginate","useCallback","container","children","currentPageUsed","child","h","blocks","jsx","RendererError","jsxs","pageHtml","i"],"mappings":"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"}
1
+ {"version":3,"file":"index-CIqtwqgy.mjs","sources":["../../src/renderers/Docx/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport mammoth from 'mammoth';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface DocxRendererProps {\n url: string;\n}\n\n// A4 page dimensions (96dpi)\nconst PAGE_HEIGHT = 1123;\nconst PAGE_PADDING_Y = 60;\nconst PAGE_PADDING_X = 50;\nconst PAGE_CONTENT_HEIGHT = PAGE_HEIGHT - PAGE_PADDING_Y * 2;\nconst PAGE_GAP = 24;\n\nconst contentStyle: React.CSSProperties = {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n lineHeight: '1.8',\n color: '#333',\n};\n\nexport const DocxRenderer: React.FC<DocxRendererProps> = ({ url }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [html, setHtml] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [pages, setPages] = useState<string[]>([]);\n const measureRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!url) return;\n\n const loadDocx = async () => {\n setLoading(true);\n setError(null);\n setHtml('');\n\n try {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error('文件加载失败');\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const result = await mammoth.convertToHtml({ arrayBuffer });\n setHtml(result.value);\n } catch (err) {\n console.error('Docx 解析错误:', err);\n setError(t('docx.parse_failed'));\n } finally {\n setLoading(false);\n }\n };\n\n loadDocx();\n }, [url, fetcher, t]);\n\n const paginate = useCallback(() => {\n const container = measureRef.current;\n if (!container || !html) return;\n\n const children = Array.from(container.children) as HTMLElement[];\n if (children.length === 0) {\n setPages([html]);\n return;\n }\n\n const result: string[][] = [[]];\n let currentPageUsed = 0;\n\n for (const child of children) {\n const h = child.offsetHeight;\n\n // If adding this block would exceed page content area and page isn't empty,\n // start a new page\n if (currentPageUsed > 0 && currentPageUsed + h > PAGE_CONTENT_HEIGHT) {\n result.push([]);\n currentPageUsed = 0;\n }\n\n result[result.length - 1].push(child.outerHTML);\n currentPageUsed += h;\n }\n\n // At least one page\n if (result.length === 0) result.push([]);\n\n setPages(result.map(blocks => blocks.join('')));\n }, [html]);\n\n useEffect(() => {\n if (!html || !measureRef.current) return;\n requestAnimationFrame(() => {\n paginate();\n });\n }, [html, paginate]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error) {\n return <RendererError message={error} />;\n }\n\n return (\n <div\n className=\"rfp-w-full rfp-h-full rfp-overflow-auto\"\n style={{ background: 'rgba(0, 0, 0, 0.15)' }}\n >\n {/* Hidden measurement div — same width as page content area */}\n <div\n ref={measureRef}\n dangerouslySetInnerHTML={{ __html: html }}\n style={{\n ...contentStyle,\n position: 'absolute',\n visibility: 'hidden',\n width: `${794 - PAGE_PADDING_X * 2}px`,\n pointerEvents: 'none',\n }}\n />\n\n {/* Visible pages */}\n <div\n className=\"rfp-py-6 md:rfp-py-10 rfp-flex rfp-flex-col rfp-items-center\"\n style={{ gap: `${PAGE_GAP}px` }}\n >\n {(pages.length > 0 ? pages : ['']).map((pageHtml, i) => (\n <div\n key={i}\n style={{\n width: '100%',\n maxWidth: '794px',\n minHeight: `${PAGE_HEIGHT}px`,\n background: 'white',\n boxShadow: '0 2px 12px rgba(0, 0, 0, 0.15)',\n flexShrink: 0,\n padding: `${PAGE_PADDING_Y}px ${PAGE_PADDING_X}px`,\n }}\n >\n <div\n dangerouslySetInnerHTML={{ __html: pageHtml }}\n style={contentStyle}\n />\n </div>\n ))}\n </div>\n </div>\n );\n};\n"],"names":["PAGE_HEIGHT","PAGE_PADDING_Y","PAGE_PADDING_X","PAGE_CONTENT_HEIGHT","PAGE_GAP","contentStyle","DocxRenderer","url","t","useTranslator","fetcher","useFetcher","html","setHtml","useState","loading","setLoading","error","setError","pages","setPages","measureRef","useRef","useEffect","response","arrayBuffer","result","mammoth","err","paginate","useCallback","container","children","currentPageUsed","child","h","blocks","jsx","RendererError","jsxs","pageHtml","i"],"mappings":"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"}
@@ -6,8 +6,8 @@ import M from "remark-math";
6
6
  import I from "rehype-katex";
7
7
  import R from "rehype-raw";
8
8
  import { Check as y, Copy as w } from "lucide-react";
9
- import { u, a as T, E as j } from "./index-CuAALtwC.mjs";
10
- import { u as N } from "./useShikiHighlight-ClbUXJ2p.mjs";
9
+ import { u, a as T, E as j } from "./index-CWCNvV2X.mjs";
10
+ import { u as N } from "./useShikiHighlight-CDDi36pF.mjs";
11
11
  import { R as B } from "./RendererError-BH6fzLrN.mjs";
12
12
  const k = (t) => {
13
13
  const [f, p] = l(!1), a = E(async () => {
@@ -172,4 +172,4 @@ const k = (t) => {
172
172
  export {
173
173
  X as MarkdownRenderer
174
174
  };
175
- //# sourceMappingURL=index-C8CyS_sj.mjs.map
175
+ //# sourceMappingURL=index-CRn7LdHD.mjs.map