@l1yp/file-viewer 0.1.1 → 0.1.2

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 (431) hide show
  1. package/README.md +1 -0
  2. package/dist/FileViewer.js +2 -0
  3. package/dist/FileViewer.js.map +1 -0
  4. package/dist/FileViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  5. package/dist/FileViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  6. package/dist/components/AppxViewer.js +2 -0
  7. package/dist/components/AppxViewer.js.map +1 -0
  8. package/dist/components/AppxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  9. package/dist/components/AppxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  10. package/dist/components/ArchiveBrowser.js +2 -0
  11. package/dist/components/ArchiveBrowser.js.map +1 -0
  12. package/dist/components/ArchiveBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
  13. package/dist/components/ArchiveBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
  14. package/dist/components/AudioViewer.js +2 -0
  15. package/dist/components/AudioViewer.js.map +1 -0
  16. package/dist/components/AudioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  17. package/dist/components/AudioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  18. package/dist/components/BinObjBrowser.js +2 -0
  19. package/dist/components/BinObjBrowser.js.map +1 -0
  20. package/dist/components/BinObjBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
  21. package/dist/components/BinObjBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
  22. package/dist/components/CertViewer.js +2 -0
  23. package/dist/components/CertViewer.js.map +1 -0
  24. package/dist/components/CertViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  25. package/dist/components/CertViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  26. package/dist/components/ClassViewer.js +2 -0
  27. package/dist/components/ClassViewer.js.map +1 -0
  28. package/dist/components/ClassViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  29. package/dist/components/ClassViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  30. package/dist/components/CodeBlock.js +2 -0
  31. package/dist/components/CodeBlock.js.map +1 -0
  32. package/dist/components/CodeBlock.vue_vue_type_script_setup_true_lang.js +2 -0
  33. package/dist/components/CodeBlock.vue_vue_type_script_setup_true_lang.js.map +1 -0
  34. package/dist/components/CodeView.js +2 -0
  35. package/dist/components/CodeView.js.map +1 -0
  36. package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js +2 -0
  37. package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js.map +1 -0
  38. package/dist/components/CsvViewer.js +2 -0
  39. package/dist/components/CsvViewer.js.map +1 -0
  40. package/dist/components/CsvViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  41. package/dist/components/CsvViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  42. package/dist/components/DicomViewer.js +2 -0
  43. package/dist/components/DicomViewer.js.map +1 -0
  44. package/dist/components/DicomViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  45. package/dist/components/DicomViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  46. package/dist/components/DocxNavigationPane.js +2 -0
  47. package/dist/components/DocxNavigationPane.js.map +1 -0
  48. package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js +2 -0
  49. package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
  50. package/dist/components/DocxViewer.js +2 -0
  51. package/dist/components/DocxViewer.js.map +1 -0
  52. package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  53. package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  54. package/dist/components/DrawioViewer.js +2 -0
  55. package/dist/components/DrawioViewer.js.map +1 -0
  56. package/dist/components/DrawioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  57. package/dist/components/DrawioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  58. package/dist/components/DxfViewer.js +2 -0
  59. package/dist/components/DxfViewer.js.map +1 -0
  60. package/dist/components/DxfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  61. package/dist/components/DxfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  62. package/dist/components/EbookViewer.js +2 -0
  63. package/dist/components/EbookViewer.js.map +1 -0
  64. package/dist/components/EbookViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  65. package/dist/components/EbookViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  66. package/dist/components/EmlViewer.js +2 -0
  67. package/dist/components/EmlViewer.js.map +1 -0
  68. package/dist/components/EmlViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  69. package/dist/components/EmlViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  70. package/dist/components/EplViewer.js +2 -0
  71. package/dist/components/EplViewer.js.map +1 -0
  72. package/dist/components/EplViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  73. package/dist/components/EplViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  74. package/dist/components/FileViewerBody.js +2 -0
  75. package/dist/components/FileViewerBody.js.map +1 -0
  76. package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js +2 -0
  77. package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js.map +1 -0
  78. package/dist/components/FileViewerPanel.js +2 -0
  79. package/dist/components/FileViewerPanel.js.map +1 -0
  80. package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js +2 -0
  81. package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  82. package/dist/components/FontViewer.js +2 -0
  83. package/dist/components/FontViewer.js.map +1 -0
  84. package/dist/components/FontViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  85. package/dist/components/FontViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  86. package/dist/components/FrontmatterCard.js +2 -0
  87. package/dist/components/FrontmatterCard.js.map +1 -0
  88. package/dist/components/FrontmatterCard.vue_vue_type_script_setup_true_lang.js +2 -0
  89. package/dist/components/FrontmatterCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
  90. package/dist/components/GeoViewer.js +2 -0
  91. package/dist/components/GeoViewer.js.map +1 -0
  92. package/dist/components/GeoViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  93. package/dist/components/GeoViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  94. package/dist/components/HarViewer.js +2 -0
  95. package/dist/components/HarViewer.js.map +1 -0
  96. package/dist/components/HarViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  97. package/dist/components/HarViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  98. package/dist/components/HexViewer.js +2 -0
  99. package/dist/components/HexViewer.js.map +1 -0
  100. package/dist/components/HexViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  101. package/dist/components/HexViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  102. package/dist/components/HprofViewer.js +2 -0
  103. package/dist/components/HprofViewer.js.map +1 -0
  104. package/dist/components/HprofViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  105. package/dist/components/HprofViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  106. package/dist/components/ImageLightbox.js +2 -0
  107. package/dist/components/ImageLightbox.js.map +1 -0
  108. package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js +2 -0
  109. package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js.map +1 -0
  110. package/dist/components/InlineText.js +2 -0
  111. package/dist/components/InlineText.js.map +1 -0
  112. package/dist/components/JksViewer.js +2 -0
  113. package/dist/components/JksViewer.js.map +1 -0
  114. package/dist/components/JksViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  115. package/dist/components/JksViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  116. package/dist/components/JsonlViewer.js +2 -0
  117. package/dist/components/JsonlViewer.js.map +1 -0
  118. package/dist/components/JsonlViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  119. package/dist/components/JsonlViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  120. package/dist/components/LogViewer.js +2 -0
  121. package/dist/components/LogViewer.js.map +1 -0
  122. package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  123. package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  124. package/dist/components/LrcViewer.js +2 -0
  125. package/dist/components/LrcViewer.js.map +1 -0
  126. package/dist/components/LrcViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  127. package/dist/components/LrcViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  128. package/dist/components/MarkdownTable.js +2 -0
  129. package/dist/components/MarkdownTable.js.map +1 -0
  130. package/dist/components/MarkdownTable.vue_vue_type_script_setup_true_lang.js +2 -0
  131. package/dist/components/MarkdownTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
  132. package/dist/components/MarkdownText.js +2 -0
  133. package/dist/components/MarkdownText.js.map +1 -0
  134. package/dist/components/MhtViewer.js +2 -0
  135. package/dist/components/MhtViewer.js.map +1 -0
  136. package/dist/components/MhtViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  137. package/dist/components/MhtViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  138. package/dist/components/MindMapView.js +2 -0
  139. package/dist/components/MindMapView.js.map +1 -0
  140. package/dist/components/MindMapView.vue_vue_type_script_setup_true_lang.js +2 -0
  141. package/dist/components/MindMapView.vue_vue_type_script_setup_true_lang.js.map +1 -0
  142. package/dist/components/MissingDependency.js +2 -0
  143. package/dist/components/MissingDependency.js.map +1 -0
  144. package/dist/components/MissingDependency.vue_vue_type_script_setup_true_lang.js +2 -0
  145. package/dist/components/MissingDependency.vue_vue_type_script_setup_true_lang.js.map +1 -0
  146. package/dist/components/ModelViewer.js +2 -0
  147. package/dist/components/ModelViewer.js.map +1 -0
  148. package/dist/components/ModelViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  149. package/dist/components/ModelViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  150. package/dist/components/MsiViewer.js +2 -0
  151. package/dist/components/MsiViewer.js.map +1 -0
  152. package/dist/components/MsiViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  153. package/dist/components/MsiViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  154. package/dist/components/NotebookViewer.js +2 -0
  155. package/dist/components/NotebookViewer.js.map +1 -0
  156. package/dist/components/NotebookViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  157. package/dist/components/NotebookViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  158. package/dist/components/PdfViewer.js +2 -0
  159. package/dist/components/PdfViewer.js.map +1 -0
  160. package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  161. package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  162. package/dist/components/PptxNavigationPane.js +2 -0
  163. package/dist/components/PptxNavigationPane.js.map +1 -0
  164. package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js +2 -0
  165. package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
  166. package/dist/components/PptxViewer.js +3 -1
  167. package/dist/components/PptxViewer.js.map +1 -0
  168. package/dist/components/PptxViewer.vue_vue_type_script_setup_true_lang.js +55 -49
  169. package/dist/components/PptxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  170. package/dist/components/PsdViewer.js +2 -0
  171. package/dist/components/PsdViewer.js.map +1 -0
  172. package/dist/components/PsdViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  173. package/dist/components/PsdViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  174. package/dist/components/QmcAudioViewer.js +2 -0
  175. package/dist/components/QmcAudioViewer.js.map +1 -0
  176. package/dist/components/QmcAudioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  177. package/dist/components/QmcAudioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  178. package/dist/components/RasterViewer.js +2 -0
  179. package/dist/components/RasterViewer.js.map +1 -0
  180. package/dist/components/RasterViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  181. package/dist/components/RasterViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  182. package/dist/components/RtfViewer.js +2 -0
  183. package/dist/components/RtfViewer.js.map +1 -0
  184. package/dist/components/RtfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  185. package/dist/components/RtfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  186. package/dist/components/SevenZipBrowser.js +2 -0
  187. package/dist/components/SevenZipBrowser.js.map +1 -0
  188. package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
  189. package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
  190. package/dist/components/SqliteViewer.js +2 -0
  191. package/dist/components/SqliteViewer.js.map +1 -0
  192. package/dist/components/SqliteViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  193. package/dist/components/SqliteViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  194. package/dist/components/SubtitleViewer.js +2 -0
  195. package/dist/components/SubtitleViewer.js.map +1 -0
  196. package/dist/components/SubtitleViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  197. package/dist/components/SubtitleViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  198. package/dist/components/XlsxChart.js +2 -0
  199. package/dist/components/XlsxChart.js.map +1 -0
  200. package/dist/components/XlsxChart.vue_vue_type_script_setup_true_lang.js +2 -0
  201. package/dist/components/XlsxChart.vue_vue_type_script_setup_true_lang.js.map +1 -0
  202. package/dist/components/XlsxViewer.js +2 -0
  203. package/dist/components/XlsxViewer.js.map +1 -0
  204. package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  205. package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  206. package/dist/components/XmindViewer.js +2 -0
  207. package/dist/components/XmindViewer.js.map +1 -0
  208. package/dist/components/XmindViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  209. package/dist/components/XmindViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  210. package/dist/components/ZipBrowser.js +2 -0
  211. package/dist/components/ZipBrowser.js.map +1 -0
  212. package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
  213. package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
  214. package/dist/components/ZoomImage.js +2 -0
  215. package/dist/components/ZoomImage.js.map +1 -0
  216. package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js +2 -0
  217. package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js.map +1 -0
  218. package/dist/components/epl/EplCodeBlock.js +2 -0
  219. package/dist/components/epl/EplCodeBlock.js.map +1 -0
  220. package/dist/components/epl/EplCodeBlock.vue_vue_type_script_setup_true_lang.js +2 -0
  221. package/dist/components/epl/EplCodeBlock.vue_vue_type_script_setup_true_lang.js.map +1 -0
  222. package/dist/components/epl/EplDoc.js +2 -0
  223. package/dist/components/epl/EplDoc.js.map +1 -0
  224. package/dist/components/epl/EplDoc.vue_vue_type_script_setup_true_lang.js +2 -0
  225. package/dist/components/epl/EplDoc.vue_vue_type_script_setup_true_lang.js.map +1 -0
  226. package/dist/components/epl/EplMethod.js +2 -0
  227. package/dist/components/epl/EplMethod.js.map +1 -0
  228. package/dist/components/epl/EplMethod.vue_vue_type_script_setup_true_lang.js +2 -0
  229. package/dist/components/epl/EplMethod.vue_vue_type_script_setup_true_lang.js.map +1 -0
  230. package/dist/components/epl/EplResource.js +2 -0
  231. package/dist/components/epl/EplResource.js.map +1 -0
  232. package/dist/components/epl/EplResource.vue_vue_type_script_setup_true_lang.js +2 -0
  233. package/dist/components/epl/EplResource.vue_vue_type_script_setup_true_lang.js.map +1 -0
  234. package/dist/components/epl/EplTree.js +2 -0
  235. package/dist/components/epl/EplTree.js.map +1 -0
  236. package/dist/components/epl/EplTree.vue_vue_type_script_setup_true_lang.js +2 -0
  237. package/dist/components/epl/EplTree.vue_vue_type_script_setup_true_lang.js.map +1 -0
  238. package/dist/components/epl/EplVarTable.js +2 -0
  239. package/dist/components/epl/EplVarTable.js.map +1 -0
  240. package/dist/components/epl/EplVarTable.vue_vue_type_script_setup_true_lang.js +2 -0
  241. package/dist/components/epl/EplVarTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
  242. package/dist/components/epl/numbering.js +2 -0
  243. package/dist/components/epl/numbering.js.map +1 -0
  244. package/dist/components/icons/Icon.js +2 -0
  245. package/dist/components/icons/Icon.js.map +1 -0
  246. package/dist/components/icons/Icon.vue_vue_type_script_setup_true_lang.js +2 -0
  247. package/dist/components/icons/Icon.vue_vue_type_script_setup_true_lang.js.map +1 -0
  248. package/dist/components/icons/iconPaths.js +2 -0
  249. package/dist/components/icons/iconPaths.js.map +1 -0
  250. package/dist/components/optionalViewer.js +2 -0
  251. package/dist/components/optionalViewer.js.map +1 -0
  252. package/dist/components/ui/ClampBlock.js +2 -0
  253. package/dist/components/ui/ClampBlock.js.map +1 -0
  254. package/dist/composables/useElementNavigation.js +2 -0
  255. package/dist/composables/useElementNavigation.js.map +1 -0
  256. package/dist/composables/useFileViewer.js +2 -0
  257. package/dist/composables/useFileViewer.js.map +1 -0
  258. package/dist/composables/useLightbox.js +2 -0
  259. package/dist/composables/useLightbox.js.map +1 -0
  260. package/dist/composables/useLogRules.js +2 -0
  261. package/dist/composables/useLogRules.js.map +1 -0
  262. package/dist/lib/aes.js +2 -0
  263. package/dist/lib/aes.js.map +1 -0
  264. package/dist/lib/appx.js +2 -0
  265. package/dist/lib/appx.js.map +1 -0
  266. package/dist/lib/archiveHtml.js +2 -0
  267. package/dist/lib/archiveHtml.js.map +1 -0
  268. package/dist/lib/asn1.js +2 -0
  269. package/dist/lib/asn1.js.map +1 -0
  270. package/dist/lib/binobj.js +2 -0
  271. package/dist/lib/binobj.js.map +1 -0
  272. package/dist/lib/brotli.js +2 -0
  273. package/dist/lib/brotli.js.map +1 -0
  274. package/dist/lib/bzip2.js +2 -0
  275. package/dist/lib/bzip2.js.map +1 -0
  276. package/dist/lib/classfile.js +2 -0
  277. package/dist/lib/classfile.js.map +1 -0
  278. package/dist/lib/codePreview.js +2 -0
  279. package/dist/lib/codePreview.js.map +1 -0
  280. package/dist/lib/codefold.js +2 -0
  281. package/dist/lib/codefold.js.map +1 -0
  282. package/dist/lib/codeformat.js +2 -0
  283. package/dist/lib/codeformat.js.map +1 -0
  284. package/dist/lib/csv.js +2 -0
  285. package/dist/lib/csv.js.map +1 -0
  286. package/dist/lib/des.js +2 -0
  287. package/dist/lib/des.js.map +1 -0
  288. package/dist/lib/diagram.js +2 -0
  289. package/dist/lib/diagram.js.map +1 -0
  290. package/dist/lib/dicom.js +2 -0
  291. package/dist/lib/dicom.js.map +1 -0
  292. package/dist/lib/dicomCodecs.js +2 -0
  293. package/dist/lib/dicomCodecs.js.map +1 -0
  294. package/dist/lib/dicomRender.js +2 -0
  295. package/dist/lib/dicomRender.js.map +1 -0
  296. package/dist/lib/docxNavigation.js +2 -0
  297. package/dist/lib/docxNavigation.js.map +1 -0
  298. package/dist/lib/dxf.js +2 -0
  299. package/dist/lib/dxf.js.map +1 -0
  300. package/dist/lib/eml.js +2 -0
  301. package/dist/lib/eml.js.map +1 -0
  302. package/dist/lib/encoding.js +2 -0
  303. package/dist/lib/encoding.js.map +1 -0
  304. package/dist/lib/epl/code/codeDataParser.js +2 -0
  305. package/dist/lib/epl/code/codeDataParser.js.map +1 -0
  306. package/dist/lib/epl/code/codeLines.js +2 -0
  307. package/dist/lib/epl/code/codeLines.js.map +1 -0
  308. package/dist/lib/epl/code/textCode.js +2 -0
  309. package/dist/lib/epl/code/textCode.js.map +1 -0
  310. package/dist/lib/epl/eplDocument.js +2 -0
  311. package/dist/lib/epl/eplDocument.js.map +1 -0
  312. package/dist/lib/epl/imageList.js +2 -0
  313. package/dist/lib/epl/imageList.js.map +1 -0
  314. package/dist/lib/epl/index.js +2 -0
  315. package/dist/lib/epl/index.js.map +1 -0
  316. package/dist/lib/epl/io/binaryReader.js +2 -0
  317. package/dist/lib/epl/io/binaryReader.js.map +1 -0
  318. package/dist/lib/epl/io/encoding.js +2 -0
  319. package/dist/lib/epl/io/encoding.js.map +1 -0
  320. package/dist/lib/epl/model/eplSystemId.js +2 -0
  321. package/dist/lib/epl/model/eplSystemId.js.map +1 -0
  322. package/dist/lib/epl/names/eplLibData.js +2 -0
  323. package/dist/lib/epl/names/eplLibData.js.map +1 -0
  324. package/dist/lib/epl/names/idToNameMap.js +2 -0
  325. package/dist/lib/epl/names/idToNameMap.js.map +1 -0
  326. package/dist/lib/epl/names/libNameProvider.js +2 -0
  327. package/dist/lib/epl/names/libNameProvider.js.map +1 -0
  328. package/dist/lib/epl/program.js +2 -0
  329. package/dist/lib/epl/program.js.map +1 -0
  330. package/dist/lib/epl/projectFileReader.js +2 -0
  331. package/dist/lib/epl/projectFileReader.js.map +1 -0
  332. package/dist/lib/fb2.js +2 -0
  333. package/dist/lib/fb2.js.map +1 -0
  334. package/dist/lib/fileType.js +2 -0
  335. package/dist/lib/fileType.js.map +1 -0
  336. package/dist/lib/fonts.js +2 -0
  337. package/dist/lib/fonts.js.map +1 -0
  338. package/dist/lib/format.js +2 -0
  339. package/dist/lib/format.js.map +1 -0
  340. package/dist/lib/frontmatter.js +2 -0
  341. package/dist/lib/frontmatter.js.map +1 -0
  342. package/dist/lib/geo.js +2 -0
  343. package/dist/lib/geo.js.map +1 -0
  344. package/dist/lib/har.js +2 -0
  345. package/dist/lib/har.js.map +1 -0
  346. package/dist/lib/highlight.js +2 -0
  347. package/dist/lib/highlight.js.map +1 -0
  348. package/dist/lib/highlightLangs.js +2 -0
  349. package/dist/lib/highlightLangs.js.map +1 -0
  350. package/dist/lib/hprof.js +2 -0
  351. package/dist/lib/hprof.js.map +1 -0
  352. package/dist/lib/ipynb.js +2 -0
  353. package/dist/lib/ipynb.js.map +1 -0
  354. package/dist/lib/jks.js +2 -0
  355. package/dist/lib/jks.js.map +1 -0
  356. package/dist/lib/jsonl.js +2 -0
  357. package/dist/lib/jsonl.js.map +1 -0
  358. package/dist/lib/lrc.js +2 -0
  359. package/dist/lib/lrc.js.map +1 -0
  360. package/dist/lib/lzma.js +2 -0
  361. package/dist/lib/lzma.js.map +1 -0
  362. package/dist/lib/markdown/blocks.js +2 -0
  363. package/dist/lib/markdown/blocks.js.map +1 -0
  364. package/dist/lib/markdown/inline.js +2 -0
  365. package/dist/lib/markdown/inline.js.map +1 -0
  366. package/dist/lib/markdown/math.js +2 -0
  367. package/dist/lib/markdown/math.js.map +1 -0
  368. package/dist/lib/markdown/segmentize.js +2 -0
  369. package/dist/lib/markdown/segmentize.js.map +1 -0
  370. package/dist/lib/markdown/styles.js +2 -0
  371. package/dist/lib/markdown/styles.js.map +1 -0
  372. package/dist/lib/mht.js +2 -0
  373. package/dist/lib/mht.js.map +1 -0
  374. package/dist/lib/midi.js +2 -0
  375. package/dist/lib/midi.js.map +1 -0
  376. package/dist/lib/mime.js +2 -0
  377. package/dist/lib/mime.js.map +1 -0
  378. package/dist/lib/mindmap.js +2 -0
  379. package/dist/lib/mindmap.js.map +1 -0
  380. package/dist/lib/mobi.js +2 -0
  381. package/dist/lib/mobi.js.map +1 -0
  382. package/dist/lib/msi.js +2 -0
  383. package/dist/lib/msi.js.map +1 -0
  384. package/dist/lib/optionalDep.js +2 -0
  385. package/dist/lib/optionalDep.js.map +1 -0
  386. package/dist/lib/pkcs12.js +2 -0
  387. package/dist/lib/pkcs12.js.map +1 -0
  388. package/dist/lib/pptxNavigation.js +2 -0
  389. package/dist/lib/pptxNavigation.js.map +1 -0
  390. package/dist/lib/qmc.js +2 -0
  391. package/dist/lib/qmc.js.map +1 -0
  392. package/dist/lib/rar.js +2 -0
  393. package/dist/lib/rar.js.map +1 -0
  394. package/dist/lib/rawPreview.js +2 -0
  395. package/dist/lib/rawPreview.js.map +1 -0
  396. package/dist/lib/rc2.js +2 -0
  397. package/dist/lib/rc2.js.map +1 -0
  398. package/dist/lib/resolveInput.js +2 -0
  399. package/dist/lib/resolveInput.js.map +1 -0
  400. package/dist/lib/rtf.js +2 -0
  401. package/dist/lib/rtf.js.map +1 -0
  402. package/dist/lib/sevenZip.js +2 -0
  403. package/dist/lib/sevenZip.js.map +1 -0
  404. package/dist/lib/sha1.js +2 -0
  405. package/dist/lib/sha1.js.map +1 -0
  406. package/dist/lib/sha256.js +2 -0
  407. package/dist/lib/sha256.js.map +1 -0
  408. package/dist/lib/speechAudio.js +2 -0
  409. package/dist/lib/speechAudio.js.map +1 -0
  410. package/dist/lib/sqlite.js +2 -0
  411. package/dist/lib/sqlite.js.map +1 -0
  412. package/dist/lib/subtitle.js +2 -0
  413. package/dist/lib/subtitle.js.map +1 -0
  414. package/dist/lib/tarball.js +2 -0
  415. package/dist/lib/tarball.js.map +1 -0
  416. package/dist/lib/viewerFormats.js +2 -0
  417. package/dist/lib/viewerFormats.js.map +1 -0
  418. package/dist/lib/viewerKind.js +2 -0
  419. package/dist/lib/viewerKind.js.map +1 -0
  420. package/dist/lib/x509.js +2 -0
  421. package/dist/lib/x509.js.map +1 -0
  422. package/dist/lib/xlsx.js +2 -0
  423. package/dist/lib/xlsx.js.map +1 -0
  424. package/dist/lib/xlsxCharts.js +2 -0
  425. package/dist/lib/xlsxCharts.js.map +1 -0
  426. package/dist/lib/xmind.js +2 -0
  427. package/dist/lib/xmind.js.map +1 -0
  428. package/dist/lib/zip.js +2 -0
  429. package/dist/lib/zip.js.map +1 -0
  430. package/dist/style.css +1 -1
  431. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sevenZip.js","names":[],"sources":["../../src/lib/sevenZip.ts"],"sourcesContent":["/**\n * Minimal in-browser 7z reader — no dependency, no WASM.\n *\n * Mirrors {@link lib/zip.ts}: hand-rolled structural parsing over a DataView/Uint8Array, decoding only\n * what conversation artifacts actually ship. The 7z container is considerably more involved than ZIP —\n * its directory (\"header\") is itself usually LZMA-compressed and describes *folders* (chains of coders)\n * rather than per-file streams — so this leans on the bundled LZMA decoder ({@link lib/lzma.ts}) both to\n * read the header and to extract files. Supported coders: Copy (00), LZMA (03 01 01), LZMA2 (21) and the\n * x86 BCJ filter (03 03 01 03); anything else (other branch filters, BCJ2, AES) is surfaced as an error.\n *\n * Used by SevenZipBrowser (the side viewer's 7z mode): the bytes come from the same download endpoint as\n * any other file, are decompressed entirely client-side, and never leave the browser.\n */\n\nimport { aesCbcDecrypt } from '@/lib/aes'\nimport { bcjX86Decode, lzma2Decode, lzmaDecode } from '@/lib/lzma'\nimport { Sha256 } from '@/lib/sha256'\n\nconst SIGNATURE = [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c] // \"7z\\xBC\\xAF\\x27\\x1C\"\nconst SIG_HEADER_SIZE = 32\n\n// Header property ids.\nconst K_END = 0x00\nconst K_HEADER = 0x01\nconst K_MAIN_STREAMS_INFO = 0x04\nconst K_FILES_INFO = 0x05\nconst K_PACK_INFO = 0x06\nconst K_UNPACK_INFO = 0x07\nconst K_SUBSTREAMS_INFO = 0x08\nconst K_SIZE = 0x09\nconst K_CRC = 0x0a\nconst K_FOLDER = 0x0b\nconst K_CODERS_UNPACK_SIZE = 0x0c\nconst K_NUM_UNPACK_STREAM = 0x0d\nconst K_EMPTY_STREAM = 0x0e\nconst K_EMPTY_FILE = 0x0f\nconst K_NAME = 0x11\nconst K_ENCODED_HEADER = 0x17\n\n// AES-256 + SHA-256 coder method id — 7z whole-header (\"encrypt file names\") or per-file content encryption.\nconst AES_CODER = '06f10701'\n\n/** Thrown when the archive (its header or an entry) is AES-encrypted and no password was supplied. */\nexport class SevenZipEncryptedError extends Error {\n constructor() {\n super('该 7z 已加密,需要密码')\n this.name = 'SevenZipEncryptedError'\n }\n}\n/** Thrown when the supplied password fails to decrypt the header/content. */\nexport class SevenZipPasswordError extends Error {\n constructor() {\n super('密码错误,无法解压')\n this.name = 'SevenZipPasswordError'\n }\n}\n\nexport interface SevenZipEntry {\n /** Full path within the archive, forward-slashed. */\n path: string\n /** Basename (last path segment). */\n name: string\n /** Uncompressed size in bytes. */\n size: number\n /** Directory entry. */\n isDir: boolean\n /** True when this file's folder is AES-encrypted (extraction / listing needs a password). */\n encrypted: boolean\n /** Index of the folder holding this file's bytes (-1 for empty files / directories). */\n folderIndex: number\n /** Byte offset of this file within its (solid) folder's decoded output. */\n offsetInFolder: number\n}\n\ninterface Coder {\n /** Method id as a hex string, e.g. \"00\", \"21\", \"030101\", \"03030103\". */\n id: string\n numIn: number\n numOut: number\n props: Uint8Array\n}\n\ninterface BindPair {\n inIndex: number\n outIndex: number\n}\n\ninterface Folder {\n coders: Coder[]\n bindPairs: BindPair[]\n /** Global in-stream indices fed by the archive's packed streams, in pack order. */\n packedIndices: number[]\n /** Unpacked size per global out-stream. */\n unpackSizes: number[]\n numPackStreams: number\n /** Byte ranges (absolute offsets into the archive) of this folder's packed streams. */\n packStreams: { offset: number; size: number }[]\n}\n\nexport interface SevenZipArchive {\n entries: SevenZipEntry[]\n /** Internal: parsed folder metadata, consumed by {@link extractSevenZipEntry}. */\n folders: Folder[]\n}\n\n/** A cursor over the (already-decoded) header byte stream. */\nclass Reader {\n pos = 0\n constructor(public readonly buf: Uint8Array) {}\n\n byte(): number {\n if (this.pos >= this.buf.length) throw new Error('7z 头部意外结束')\n return this.buf[this.pos++]\n }\n\n bytes(n: number): Uint8Array {\n const s = this.buf.subarray(this.pos, this.pos + n)\n this.pos += n\n return s\n }\n\n /** 7z variable-length number (REAL_UINT64): the first byte's high bits count the extra bytes. */\n number(): number {\n const first = this.byte()\n let mask = 0x80\n let value = 0\n for (let i = 0; i < 8; i++) {\n if ((first & mask) === 0) {\n value += (first & (mask - 1)) * 2 ** (8 * i)\n break\n }\n value += this.byte() * 2 ** (8 * i)\n mask >>= 1\n }\n return value\n }\n\n /** A bit vector of `n` bits, MSB-first within each byte. */\n bitVector(n: number): boolean[] {\n const v: boolean[] = []\n let b = 0\n let mask = 0\n for (let i = 0; i < n; i++) {\n if (mask === 0) {\n b = this.byte()\n mask = 0x80\n }\n v.push((b & mask) !== 0)\n mask >>= 1\n }\n return v\n }\n\n /** A \"defined\" vector: a leading all-defined flag, else an explicit bit vector. */\n boolVectorWithAll(n: number): boolean[] {\n if (this.byte() !== 0) return Array.from({ length: n }, () => true)\n return this.bitVector(n)\n }\n}\n\nfunction readUInt64LE(view: DataView, off: number): number {\n return view.getUint32(off + 4, true) * 0x100000000 + view.getUint32(off, true)\n}\n\n// ── Folder / streams parsing ──────────────────────────────────────────────────────────────────────\n\nfunction readFolder(r: Reader): Folder {\n const numCoders = r.number()\n const coders: Coder[] = []\n let totalIn = 0\n let totalOut = 0\n for (let i = 0; i < numCoders; i++) {\n const flags = r.byte()\n const idSize = flags & 0x0f\n const idBytes = r.bytes(idSize)\n let id = ''\n for (const b of idBytes) id += b.toString(16).padStart(2, '0')\n let numIn = 1\n let numOut = 1\n if (flags & 0x10) {\n numIn = r.number()\n numOut = r.number()\n }\n let props = new Uint8Array(0)\n if (flags & 0x20) props = r.bytes(r.number()).slice()\n coders.push({ id, numIn, numOut, props })\n totalIn += numIn\n totalOut += numOut\n }\n\n const numBindPairs = totalOut - 1\n const bindPairs: BindPair[] = []\n for (let i = 0; i < numBindPairs; i++) {\n bindPairs.push({ inIndex: r.number(), outIndex: r.number() })\n }\n\n const numPackStreams = totalIn - numBindPairs\n const packedIndices: number[] = []\n if (numPackStreams === 1) {\n // the single in-stream not used by any bind pair\n for (let i = 0; i < totalIn; i++) {\n if (!bindPairs.some((bp) => bp.inIndex === i)) {\n packedIndices.push(i)\n break\n }\n }\n } else {\n for (let i = 0; i < numPackStreams; i++) packedIndices.push(r.number())\n }\n\n return {\n coders,\n bindPairs,\n packedIndices,\n unpackSizes: [],\n numPackStreams,\n packStreams: [],\n }\n}\n\n/** The folder's final output is the one out-stream not consumed by any bind pair. */\nfunction folderFinalOutIndex(folder: Folder): number {\n let total = 0\n for (const c of folder.coders) total += c.numOut\n for (let i = 0; i < total; i++) {\n if (!folder.bindPairs.some((bp) => bp.outIndex === i)) return i\n }\n return total - 1\n}\n\nfunction folderUnpackSize(folder: Folder): number {\n return folder.unpackSizes[folderFinalOutIndex(folder)] ?? 0\n}\n\ninterface StreamsInfo {\n packPos: number\n packSizes: number[]\n folders: Folder[]\n /** Per-folder substream count (solid archives pack several files into one folder). */\n numUnpackStreamsInFolders: number[]\n /** Flat list of substream sizes, in folder order. */\n subSizes: number[]\n /** Whether each folder carried its own CRC in UnpackInfo (affects the SubStreamsInfo digest count). */\n folderHasCrc: boolean[]\n}\n\nfunction readStreamsInfo(r: Reader): StreamsInfo {\n const info: StreamsInfo = {\n packPos: 0,\n packSizes: [],\n folders: [],\n numUnpackStreamsInFolders: [],\n subSizes: [],\n folderHasCrc: [],\n }\n let id = r.byte()\n\n if (id === K_PACK_INFO) {\n info.packPos = r.number()\n const numPack = r.number()\n let sub = r.byte()\n while (sub !== K_END) {\n if (sub === K_SIZE) {\n for (let i = 0; i < numPack; i++) info.packSizes.push(r.number())\n } else if (sub === K_CRC) {\n readDigests(r, numPack)\n } else {\n throw new Error(`7z PackInfo 未知字段 0x${sub.toString(16)}`)\n }\n sub = r.byte()\n }\n id = r.byte()\n }\n\n if (id === K_UNPACK_INFO) {\n if (r.byte() !== K_FOLDER) throw new Error('7z UnpackInfo 缺少 Folder')\n const numFolders = r.number()\n const external = r.byte()\n if (external !== 0) throw new Error('7z 不支持外部 Folder 定义')\n for (let i = 0; i < numFolders; i++) info.folders.push(readFolder(r))\n if (r.byte() !== K_CODERS_UNPACK_SIZE) throw new Error('7z 缺少 CodersUnpackSize')\n for (const folder of info.folders) {\n let totalOut = 0\n for (const c of folder.coders) totalOut += c.numOut\n for (let i = 0; i < totalOut; i++) folder.unpackSizes.push(r.number())\n }\n info.folderHasCrc = info.folders.map(() => false)\n let sub = r.byte()\n while (sub !== K_END) {\n if (sub === K_CRC) {\n info.folderHasCrc = readDigests(r, info.folders.length)\n } else {\n throw new Error(`7z UnpackInfo 未知字段 0x${sub.toString(16)}`)\n }\n sub = r.byte()\n }\n id = r.byte()\n }\n\n // Default: one substream per folder. SubStreamsInfo overrides when present.\n info.numUnpackStreamsInFolders = info.folders.map(() => 1)\n if (id === K_SUBSTREAMS_INFO) {\n readSubStreamsInfo(r, info)\n id = r.byte()\n } else {\n info.subSizes = info.folders.map((f) => folderUnpackSize(f))\n }\n\n if (id !== K_END) throw new Error(`7z StreamsInfo 未以 End 结束 (0x${id.toString(16)})`)\n\n // Slice the archive's pack streams across folders in order, computing absolute offsets.\n let packIndex = 0\n let offset = SIG_HEADER_SIZE + info.packPos\n const packOffsets: number[] = []\n for (const sz of info.packSizes) {\n packOffsets.push(offset)\n offset += sz\n }\n for (const folder of info.folders) {\n for (let i = 0; i < folder.numPackStreams; i++) {\n folder.packStreams.push({ offset: packOffsets[packIndex], size: info.packSizes[packIndex] })\n packIndex++\n }\n }\n return info\n}\n\nfunction readSubStreamsInfo(r: Reader, info: StreamsInfo): void {\n let type = r.byte()\n if (type === K_NUM_UNPACK_STREAM) {\n info.numUnpackStreamsInFolders = info.folders.map(() => r.number())\n type = r.byte()\n } else {\n info.numUnpackStreamsInFolders = info.folders.map(() => 1)\n }\n\n const subSizes: number[] = []\n for (let f = 0; f < info.folders.length; f++) {\n const num = info.numUnpackStreamsInFolders[f]\n if (num === 0) continue\n let sum = 0\n if (type === K_SIZE) {\n for (let i = 1; i < num; i++) {\n const s = r.number()\n subSizes.push(s)\n sum += s\n }\n }\n subSizes.push(folderUnpackSize(info.folders[f]) - sum)\n }\n info.subSizes = subSizes\n if (type === K_SIZE) type = r.byte()\n\n // The SubStreamsInfo CRC block lists a digest only for substreams that don't already inherit a\n // folder-level CRC: a folder contributes its substream count, except a 1-substream folder that already\n // has its own CRC (which contributes none).\n let numDigests = 0\n for (let f = 0; f < info.folders.length; f++) {\n const num = info.numUnpackStreamsInFolders[f]\n if (num !== 1 || !info.folderHasCrc[f]) numDigests += num\n }\n if (type === K_CRC) {\n readDigests(r, numDigests)\n type = r.byte()\n }\n while (type !== K_END) type = r.byte() // defensively skip any unknown trailing field\n}\n\n/**\n * Read a digest block — an all-defined flag (or explicit bit vector) followed by a CRC32 per defined\n * entry — and return which entries were defined. The CRC values themselves are discarded (we don't verify).\n */\nfunction readDigests(r: Reader, count: number): boolean[] {\n const defined = r.boolVectorWithAll(count)\n for (const d of defined) if (d) r.bytes(4)\n return defined\n}\n\n// ── Files info ──────────────────────────────────────────────────────────────────────────────────\n\ninterface FilesInfo {\n names: string[]\n emptyStream: boolean[]\n emptyFile: boolean[]\n}\n\nfunction readFilesInfo(r: Reader): FilesInfo {\n const numFiles = r.number()\n const out: FilesInfo = {\n names: Array.from({ length: numFiles }, () => ''),\n emptyStream: Array.from({ length: numFiles }, () => false),\n emptyFile: [],\n }\n let numEmptyStreams = 0\n\n for (;;) {\n const propType = r.number()\n if (propType === K_END) break\n const size = r.number()\n const end = r.pos + size\n switch (propType) {\n case K_EMPTY_STREAM: {\n out.emptyStream = r.bitVector(numFiles)\n numEmptyStreams = out.emptyStream.filter(Boolean).length\n out.emptyFile = Array.from({ length: numEmptyStreams }, () => false)\n break\n }\n case K_EMPTY_FILE: {\n out.emptyFile = r.bitVector(numEmptyStreams)\n break\n }\n case K_NAME: {\n const external = r.byte()\n if (external !== 0) throw new Error('7z 不支持外部文件名')\n out.names = readNames(r, end, numFiles)\n break\n }\n default:\n break // CTime/ATime/MTime/Attributes/Dummy/… — not needed for browsing\n }\n r.pos = end // robust to partial reads\n }\n return out\n}\n\n/** Names are NUL-terminated UTF-16LE, concatenated. */\nfunction readNames(r: Reader, end: number, numFiles: number): string[] {\n const names: string[] = []\n let cur = ''\n while (r.pos + 1 < end && names.length < numFiles) {\n const lo = r.byte()\n const hi = r.byte()\n const code = lo | (hi << 8)\n if (code === 0) {\n names.push(cur)\n cur = ''\n } else {\n cur += String.fromCharCode(code)\n }\n }\n return names\n}\n\n// ── Header assembly ───────────────────────────────────────────────────────────────────────────────\n\nfunction buildEntries(streams: StreamsInfo | null, files: FilesInfo): SevenZipEntry[] {\n const entries: SevenZipEntry[] = []\n const numFiles = files.names.length\n\n // Walk the per-folder substreams in order, assigning each to the next file that has a stream.\n let folderIdx = 0\n let subInFolder = 0\n let offsetInFolder = 0\n let subSizeIdx = 0\n let emptyIdx = 0\n\n const advanceFolder = (): void => {\n // Move to the next folder that actually has substreams.\n while (\n streams &&\n folderIdx < streams.folders.length &&\n subInFolder >= streams.numUnpackStreamsInFolders[folderIdx]\n ) {\n folderIdx++\n subInFolder = 0\n offsetInFolder = 0\n }\n }\n\n for (let i = 0; i < numFiles; i++) {\n const path = files.names[i].replace(/\\\\/g, '/')\n const name = path.replace(/\\/+$/, '').split('/').pop() ?? path\n const hasStream = !files.emptyStream[i]\n if (hasStream) {\n advanceFolder()\n const size = streams ? streams.subSizes[subSizeIdx++] : 0\n entries.push({\n path,\n name,\n size,\n isDir: false,\n encrypted: streams ? folderHasAes(streams.folders[folderIdx]) : false,\n folderIndex: folderIdx,\n offsetInFolder,\n })\n offsetInFolder += size\n subInFolder++\n } else {\n // No stream: an empty file or a directory (distinguished by the emptyFile vector).\n const isEmptyFile = files.emptyFile[emptyIdx++] === true\n entries.push({\n path,\n name,\n size: 0,\n isDir: !isEmptyFile,\n encrypted: false,\n folderIndex: -1,\n offsetInFolder: 0,\n })\n }\n }\n return entries\n}\n\nfunction parseHeader(r: Reader): SevenZipArchive {\n const top = r.byte()\n if (top !== K_HEADER) throw new Error(`7z 头部类型异常 (0x${top.toString(16)})`)\n let id = r.byte()\n let streams: StreamsInfo | null = null\n let files: FilesInfo = { names: [], emptyStream: [], emptyFile: [] }\n\n // ArchiveProperties / AdditionalStreamsInfo are skipped — none of our archives use them.\n if (id === K_MAIN_STREAMS_INFO) {\n streams = readStreamsInfo(r)\n id = r.byte()\n }\n if (id === K_FILES_INFO) {\n files = readFilesInfo(r)\n id = r.byte()\n }\n\n return { entries: buildEntries(streams, files), folders: streams?.folders ?? [] }\n}\n\n// ── AES-256 decryption (encrypted 7z) ─────────────────────────────────────────────────────────────\n\ninterface DecodeCtx {\n password?: string\n}\n\n/** Does any coder in this folder apply 7z AES encryption? */\nfunction folderHasAes(folder: Folder): boolean {\n return folder.coders.some((c) => c.id === AES_CODER)\n}\n\n/** Password bytes as UTF-16LE (7z hashes the password this way). */\nfunction utf16le(s: string): Uint8Array {\n const b = new Uint8Array(s.length * 2)\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i)\n b[2 * i] = c & 0xff\n b[2 * i + 1] = (c >> 8) & 0xff\n }\n return b\n}\n\n/**\n * Derive the AES-256 key + IV for a 7z AES coder from its property bytes and the password. Property layout:\n * byte0 = numCyclesPower(bits 0-5) | saltPresent(bit 7) | ivPresent(bit 6); an optional byte1 packs extra\n * salt size (high nibble) + iv size (low nibble); then `saltSize` salt bytes, then `ivSize` iv bytes (zero-\n * padded to 16). The key is SHA-256( (salt ‖ passwordUTF16LE ‖ counterLE64) repeated 2^numCyclesPower times ),\n * the counter incrementing each round. numCyclesPower 0x3F is the degenerate \"no hashing\" form (key = salt ‖\n * password, truncated / zero-padded to 32 bytes).\n */\nfunction deriveSevenZipKey(\n props: Uint8Array,\n password: string,\n): { key: Uint8Array; iv: Uint8Array } {\n const b0 = props[0] ?? 0\n const numCyclesPower = b0 & 0x3f\n let saltSize = (b0 >> 7) & 1\n let ivSize = (b0 >> 6) & 1\n if (props.length >= 2) {\n const b1 = props[1]\n saltSize += b1 >> 4\n ivSize += b1 & 0x0f\n }\n const salt = props.subarray(2, 2 + saltSize)\n const iv = new Uint8Array(16)\n iv.set(props.subarray(2 + saltSize, 2 + saltSize + ivSize))\n\n const pass = utf16le(password)\n const key = new Uint8Array(32)\n if (numCyclesPower === 0x3f) {\n let pos = 0\n for (let i = 0; i < salt.length && pos < 32; i++) key[pos++] = salt[i]\n for (let i = 0; i < pass.length && pos < 32; i++) key[pos++] = pass[i]\n return { key, iv }\n }\n const rounds = 2 ** numCyclesPower\n const sha = new Sha256()\n const counter = new Uint8Array(8)\n for (let r = 0; r < rounds; r++) {\n if (salt.length) sha.update(salt)\n sha.update(pass)\n sha.update(counter)\n for (let i = 0; i < 8; i++) {\n counter[i] = (counter[i] + 1) & 0xff\n if (counter[i] !== 0) break\n }\n }\n key.set(sha.digest())\n return { key, iv }\n}\n\n// ── Coder / folder decoding ─────────────────────────────────────────────────────────────────────\n\nfunction decodeCoder(\n coder: Coder,\n inputs: Uint8Array[],\n outSize: number,\n ctx?: DecodeCtx,\n): Uint8Array {\n switch (coder.id) {\n case '00': // Copy\n return inputs[0].length === outSize ? inputs[0] : inputs[0].subarray(0, outSize)\n case '21': // LZMA2\n return lzma2Decode(inputs[0], coder.props, outSize)\n case '030101': // LZMA\n return lzmaDecode(inputs[0], coder.props, outSize)\n case '03030103': {\n // x86 BCJ — an in-place filter; copy so we don't mutate a shared buffer, then decode.\n const data = inputs[0].slice(0, outSize)\n bcjX86Decode(data)\n return data\n }\n case AES_CODER: {\n // AES-256-CBC: derive key+IV from the password, decrypt, then trim to the coder's real output size\n // (the ciphertext is padded up to a 16-byte boundary).\n if (!ctx?.password) throw new SevenZipEncryptedError()\n const { key, iv } = deriveSevenZipKey(coder.props, ctx.password)\n const dec = aesCbcDecrypt(key, iv, inputs[0])\n return dec.length === outSize ? dec : dec.subarray(0, outSize)\n }\n default:\n throw new Error(`不支持的压缩方式 (coder ${coder.id})`)\n }\n}\n\n/** Decode a folder's final output by resolving the coder graph (bind pairs ↔ packed streams). */\nfunction decodeFolder(buf: Uint8Array, folder: Folder, ctx?: DecodeCtx): Uint8Array {\n // Per-coder global stream base indices.\n const inBase: number[] = []\n const outBase: number[] = []\n let ti = 0\n let to = 0\n for (const c of folder.coders) {\n inBase.push(ti)\n outBase.push(to)\n ti += c.numIn\n to += c.numOut\n }\n\n const coderForOut = (outIndex: number): number => {\n for (let c = 0; c < folder.coders.length; c++) {\n if (outIndex >= outBase[c] && outIndex < outBase[c] + folder.coders[c].numOut) return c\n }\n throw new Error('7z folder 输出流索引越界')\n }\n\n const memo = new Map<number, Uint8Array>()\n const resolveOut = (outIndex: number): Uint8Array => {\n const cached = memo.get(outIndex)\n if (cached) return cached\n const c = coderForOut(outIndex)\n const coder = folder.coders[c]\n const inputs: Uint8Array[] = []\n for (let k = 0; k < coder.numIn; k++) {\n const globalIn = inBase[c] + k\n const bp = folder.bindPairs.find((b) => b.inIndex === globalIn)\n if (bp) {\n inputs.push(resolveOut(bp.outIndex))\n } else {\n const packPos = folder.packedIndices.indexOf(globalIn)\n if (packPos < 0) throw new Error('7z folder 输入流未绑定也非打包流')\n const ps = folder.packStreams[packPos]\n inputs.push(buf.subarray(ps.offset, ps.offset + ps.size))\n }\n }\n const result = decodeCoder(coder, inputs, folder.unpackSizes[outIndex], ctx)\n memo.set(outIndex, result)\n return result\n }\n\n return resolveOut(folderFinalOutIndex(folder))\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────────────────────────\n\n/** Is the buffer a 7z archive (by signature)? */\nexport function isSevenZip(buf: ArrayBuffer): boolean {\n if (buf.byteLength < SIG_HEADER_SIZE) return false\n const b = new Uint8Array(buf, 0, SIGNATURE.length)\n return SIGNATURE.every((s, i) => b[i] === s)\n}\n\n/**\n * Parse a 7z archive's header into a flat entry list (+ folder metadata for extraction). When the header\n * itself is AES-encrypted (\"encrypt file names\"), listing needs the `password`: without one this throws\n * {@link SevenZipEncryptedError} (so the UI can prompt); with a wrong one it throws {@link SevenZipPasswordError}.\n */\nexport function readSevenZip(buf: ArrayBuffer, password?: string): SevenZipArchive {\n if (!isSevenZip(buf)) throw new Error('不是有效的 7z 文件')\n const view = new DataView(buf)\n const bytes = new Uint8Array(buf)\n\n const nextHeaderOffset = readUInt64LE(view, 12)\n const nextHeaderSize = readUInt64LE(view, 20)\n if (nextHeaderSize === 0) return { entries: [], folders: [] } // empty archive\n\n const headerStart = SIG_HEADER_SIZE + nextHeaderOffset\n if (headerStart + nextHeaderSize > buf.byteLength) throw new Error('7z 头部超出文件范围')\n\n let headerBytes: Uint8Array = bytes.subarray(headerStart, headerStart + nextHeaderSize)\n let r = new Reader(headerBytes)\n let first = r.byte()\n let encryptedHeader = false\n\n // The header is often itself a compressed stream (kEncodedHeader): decode that folder to get the real\n // header bytes, then parse those. That folder can additionally be AES-encrypted (encrypted file names).\n if (first === K_ENCODED_HEADER) {\n const streams = readStreamsInfo(r)\n if (streams.folders.length !== 1) throw new Error('7z 编码头部格式异常')\n encryptedHeader = folderHasAes(streams.folders[0])\n try {\n headerBytes = decodeFolder(bytes, streams.folders[0], { password })\n } catch (e) {\n // No password → propagate SevenZipEncryptedError (UI prompts); a wrong one makes the LZMA stage choke\n // on garbage — report it as a password error.\n if (encryptedHeader && password && !(e instanceof SevenZipEncryptedError)) {\n throw new SevenZipPasswordError()\n }\n throw e\n }\n r = new Reader(headerBytes)\n first = r.byte()\n }\n if (first !== K_HEADER) {\n if (encryptedHeader && password) throw new SevenZipPasswordError() // garbage from a wrong password\n throw new Error(`7z 头部类型异常 (0x${first.toString(16)})`)\n }\n // Re-run the header parse from the top (parseHeader expects to read the kHeader id itself).\n r.pos = 0\n try {\n return parseHeader(r)\n } catch (e) {\n if (encryptedHeader && password) throw new SevenZipPasswordError()\n throw e\n }\n}\n\n// Cache the last decoded folder so extracting several files from one solid block doesn't re-decompress it.\nlet lastFolderKey: { buf: ArrayBuffer; index: number } | null = null\nlet lastFolderBytes: Uint8Array | null = null\n\n/**\n * Extract one entry's bytes. Decodes the whole folder (caching it) and slices out the file's span. For an\n * AES-encrypted folder the `password` is required — missing → {@link SevenZipEncryptedError}, wrong →\n * {@link SevenZipPasswordError}.\n */\nexport function extractSevenZipEntry(\n buf: ArrayBuffer,\n archive: SevenZipArchive,\n entry: SevenZipEntry,\n password?: string,\n): Uint8Array {\n if (entry.isDir || entry.folderIndex < 0 || entry.size === 0) return new Uint8Array(0)\n const folder = archive.folders[entry.folderIndex]\n if (!folder) throw new Error('7z 条目缺少对应 folder')\n\n let folderBytes: Uint8Array\n if (\n lastFolderBytes &&\n lastFolderKey &&\n lastFolderKey.buf === buf &&\n lastFolderKey.index === entry.folderIndex\n ) {\n folderBytes = lastFolderBytes\n } else {\n try {\n folderBytes = decodeFolder(new Uint8Array(buf), folder, { password })\n } catch (e) {\n if (folderHasAes(folder) && password && !(e instanceof SevenZipEncryptedError)) {\n throw new SevenZipPasswordError()\n }\n throw e\n }\n lastFolderKey = { buf, index: entry.folderIndex }\n lastFolderBytes = folderBytes\n }\n return folderBytes.subarray(entry.offsetInFolder, entry.offsetInFolder + entry.size).slice()\n}\n"],"mappings":"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"}
package/dist/lib/sha1.js CHANGED
@@ -51,3 +51,5 @@ function t(t) {
51
51
  }
52
52
  //#endregion
53
53
  export { e as Sha1, t as sha1 };
54
+
55
+ //# sourceMappingURL=sha1.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha1.js","names":[],"sources":["../../src/lib/sha1.ts"],"sourcesContent":["/**\n * Minimal synchronous SHA-1 (FIPS 180-4) — no dependency, no WASM.\n *\n * JKS / JCEKS keystores lean on SHA-1 in two synchronous, tight loops (see lib/jks.ts): the store-integrity\n * MAC — SHA1(passwordUtf16BE ‖ \"Mighty Aphrodite\" ‖ storeBytes) — and Sun's KeyProtector, which chains SHA-1\n * over password‖runningDigest once per 20 bytes of key material. WebCrypto's digest is async + one-shot, which\n * would turn the whole parse path async and force a throwaway buffer each round; this incremental hasher stays\n * synchronous and lets the KeyProtector feed the password + running digest directly, mirroring {@link Sha256}.\n *\n * SHA-1 is broken for collision resistance — used here ONLY because the JKS on-disk format mandates it, never\n * as a security primitive for anything new. Not constant-time (fine for reading a keystore locally).\n */\n\nexport class Sha1 {\n private readonly h = new Uint32Array([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0])\n private readonly block = new Uint8Array(64)\n private readonly w = new Uint32Array(80)\n private blen = 0\n private total = 0 // total bytes fed (for the length suffix)\n\n update(data: Uint8Array): this {\n this.total += data.length\n for (let i = 0; i < data.length; i++) {\n this.block[this.blen++] = data[i]\n if (this.blen === 64) {\n this.compress()\n this.blen = 0\n }\n }\n return this\n }\n\n digest(): Uint8Array {\n const bitLen = this.total * 8\n // Append 0x80, pad with zeros, then a 64-bit big-endian bit length.\n this.block[this.blen++] = 0x80\n if (this.blen > 56) {\n while (this.blen < 64) this.block[this.blen++] = 0\n this.compress()\n this.blen = 0\n }\n while (this.blen < 56) this.block[this.blen++] = 0\n // High 32 bits (bitLen can exceed 2^32; use division to stay exact).\n const hi = Math.floor(bitLen / 0x100000000)\n const lo = bitLen >>> 0\n this.block[56] = (hi >>> 24) & 0xff\n this.block[57] = (hi >>> 16) & 0xff\n this.block[58] = (hi >>> 8) & 0xff\n this.block[59] = hi & 0xff\n this.block[60] = (lo >>> 24) & 0xff\n this.block[61] = (lo >>> 16) & 0xff\n this.block[62] = (lo >>> 8) & 0xff\n this.block[63] = lo & 0xff\n this.compress()\n\n const out = new Uint8Array(20)\n for (let i = 0; i < 5; i++) {\n out[4 * i] = (this.h[i] >>> 24) & 0xff\n out[4 * i + 1] = (this.h[i] >>> 16) & 0xff\n out[4 * i + 2] = (this.h[i] >>> 8) & 0xff\n out[4 * i + 3] = this.h[i] & 0xff\n }\n return out\n }\n\n private compress(): void {\n const w = this.w\n const blk = this.block\n for (let i = 0; i < 16; i++) {\n w[i] = (blk[4 * i] << 24) | (blk[4 * i + 1] << 16) | (blk[4 * i + 2] << 8) | blk[4 * i + 3]\n }\n for (let i = 16; i < 80; i++) {\n const x = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]\n w[i] = (x << 1) | (x >>> 31)\n }\n let a = this.h[0]\n let b = this.h[1]\n let c = this.h[2]\n let d = this.h[3]\n let e = this.h[4]\n for (let i = 0; i < 80; i++) {\n let f: number\n let k: number\n if (i < 20) {\n f = (b & c) | (~b & d)\n k = 0x5a827999\n } else if (i < 40) {\n f = b ^ c ^ d\n k = 0x6ed9eba1\n } else if (i < 60) {\n f = (b & c) | (b & d) | (c & d)\n k = 0x8f1bbcdc\n } else {\n f = b ^ c ^ d\n k = 0xca62c1d6\n }\n const t = (((a << 5) | (a >>> 27)) + f + e + k + w[i]) | 0\n e = d\n d = c\n c = (b << 30) | (b >>> 2)\n b = a\n a = t\n }\n this.h[0] = (this.h[0] + a) | 0\n this.h[1] = (this.h[1] + b) | 0\n this.h[2] = (this.h[2] + c) | 0\n this.h[3] = (this.h[3] + d) | 0\n this.h[4] = (this.h[4] + e) | 0\n }\n}\n\n/** One-shot convenience: SHA-1 of a single buffer. */\nexport function sha1(data: Uint8Array): Uint8Array {\n return new Sha1().update(data).digest()\n}\n"],"mappings":"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"}
@@ -117,3 +117,5 @@ function n(e) {
117
117
  }
118
118
  //#endregion
119
119
  export { t as Sha256, n as sha256 };
120
+
121
+ //# sourceMappingURL=sha256.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256.js","names":[],"sources":["../../src/lib/sha256.ts"],"sourcesContent":["/**\n * Minimal synchronous SHA-256 (FIPS 180-4) — no dependency, no WASM.\n *\n * WebCrypto's digest is async and one-shot (no streaming), which is awkward for the 7z key-derivation loop\n * that hashes salt‖password‖counter hundreds of thousands of times: a one-shot API would force a ~10 MB\n * throwaway buffer AND make the whole 7z decode path async. This incremental hasher lets {@link lib/sevenZip}\n * feed each round's bytes directly and stay synchronous. Not constant-time — fine for archive key derivation,\n * not for anything that must resist timing attacks.\n */\n\nconst K = /* @__PURE__ */ new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n])\n\nexport class Sha256 {\n private readonly h = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n ])\n private readonly block = new Uint8Array(64)\n private readonly w = new Uint32Array(64)\n private blen = 0\n private total = 0 // total bytes fed (for the length suffix)\n\n update(data: Uint8Array): this {\n this.total += data.length\n for (let i = 0; i < data.length; i++) {\n this.block[this.blen++] = data[i]\n if (this.blen === 64) {\n this.compress()\n this.blen = 0\n }\n }\n return this\n }\n\n digest(): Uint8Array {\n const bitLen = this.total * 8\n // Append 0x80, pad with zeros, then a 64-bit big-endian bit length.\n this.block[this.blen++] = 0x80\n if (this.blen > 56) {\n while (this.blen < 64) this.block[this.blen++] = 0\n this.compress()\n this.blen = 0\n }\n while (this.blen < 56) this.block[this.blen++] = 0\n // High 32 bits (bitLen can exceed 2^32; use division to stay exact).\n const hi = Math.floor(bitLen / 0x100000000)\n const lo = bitLen >>> 0\n this.block[56] = (hi >>> 24) & 0xff\n this.block[57] = (hi >>> 16) & 0xff\n this.block[58] = (hi >>> 8) & 0xff\n this.block[59] = hi & 0xff\n this.block[60] = (lo >>> 24) & 0xff\n this.block[61] = (lo >>> 16) & 0xff\n this.block[62] = (lo >>> 8) & 0xff\n this.block[63] = lo & 0xff\n this.compress()\n\n const out = new Uint8Array(32)\n for (let i = 0; i < 8; i++) {\n out[4 * i] = (this.h[i] >>> 24) & 0xff\n out[4 * i + 1] = (this.h[i] >>> 16) & 0xff\n out[4 * i + 2] = (this.h[i] >>> 8) & 0xff\n out[4 * i + 3] = this.h[i] & 0xff\n }\n return out\n }\n\n private compress(): void {\n const w = this.w\n const b = this.block\n for (let i = 0; i < 16; i++) {\n w[i] = (b[4 * i] << 24) | (b[4 * i + 1] << 16) | (b[4 * i + 2] << 8) | b[4 * i + 3]\n }\n for (let i = 16; i < 64; i++) {\n const x = w[i - 15]\n const y = w[i - 2]\n const s0 = ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3)\n const s1 = ((y >>> 17) | (y << 15)) ^ ((y >>> 19) | (y << 13)) ^ (y >>> 10)\n w[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0\n }\n let a = this.h[0]\n let bb = this.h[1]\n let c = this.h[2]\n let d = this.h[3]\n let e = this.h[4]\n let f = this.h[5]\n let g = this.h[6]\n let hh = this.h[7]\n for (let i = 0; i < 64; i++) {\n const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7))\n const ch = (e & f) ^ (~e & g)\n const t1 = (hh + S1 + ch + K[i] + w[i]) | 0\n const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10))\n const maj = (a & bb) ^ (a & c) ^ (bb & c)\n const t2 = (S0 + maj) | 0\n hh = g\n g = f\n f = e\n e = (d + t1) | 0\n d = c\n c = bb\n bb = a\n a = (t1 + t2) | 0\n }\n this.h[0] = (this.h[0] + a) | 0\n this.h[1] = (this.h[1] + bb) | 0\n this.h[2] = (this.h[2] + c) | 0\n this.h[3] = (this.h[3] + d) | 0\n this.h[4] = (this.h[4] + e) | 0\n this.h[5] = (this.h[5] + f) | 0\n this.h[6] = (this.h[6] + g) | 0\n this.h[7] = (this.h[7] + hh) | 0\n }\n}\n\n/** One-shot convenience: SHA-256 of a single buffer. */\nexport function sha256(data: Uint8Array): Uint8Array {\n return new Sha256().update(data).digest()\n}\n"],"mappings":"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"}
@@ -83,3 +83,5 @@ async function d(e) {
83
83
  }
84
84
  //#endregion
85
85
  export { d as decodeSpeechToWav, i as sniffSpeechCodec };
86
+
87
+ //# sourceMappingURL=speechAudio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speechAudio.js","names":[],"sources":["../../src/lib/speechAudio.ts"],"sourcesContent":["/**\n * 语音编解码(3GPP AMR / 腾讯 SILK)→ WAV 的浏览器内解码器 —— 按需懒加载 WASM。\n *\n * 浏览器原生 <audio> 不认 3GPP AMR,也不认腾讯 SILK(微信 / QQ 语音;文件常见 `.silk` / `.slk`,也\n * 大量以 `.amr` 为扩展名而内容其实是 SILK)。两者都是 CELP 系语音编解码,无法手搓,故各引一个编译好的\n * WASM 解码器(与 lib/brotli、lib/sqlite 的 wasm 同思路:仅在真正播放这类文件时才 `import()`):\n * - AMR-NB(8kHz) / AMR-WB(16kHz):`@audio/decode-amr`(opencore-amr 编译;wasm 内联进 .cjs,无独立资源)\n * - SILK v3:`silk-wasm`(腾讯 SILK 编解码;独立 `silk.wasm`,Vite 经 `import.meta.url` 产出同源资源)\n *\n * 解出 PCM(s16le) 后用手写的 44 字节 PCM WAV 头包成 WAV,交给 <audio> 原生播放(同 QMC 解密后播放)。\n * 识别一律按**文件头魔数**而非扩展名 —— 微信 `.amr` 实为 SILK,故 `.amr` 既可能是真 AMR 也可能是 SILK。\n */\n\nexport type SpeechCodec = 'silk' | 'amr-nb' | 'amr-wb'\n\n// \"#!AMR\\n\" / \"#!AMR-WB\\n\"(RFC 4867 单声道存储头);\"#!SILK_V3\"(SILK 容器标记)。\nconst MAGIC_AMR_NB = [0x23, 0x21, 0x41, 0x4d, 0x52, 0x0a]\nconst MAGIC_AMR_WB = [0x23, 0x21, 0x41, 0x4d, 0x52, 0x2d, 0x57, 0x42, 0x0a]\nconst MAGIC_SILK = [0x23, 0x21, 0x53, 0x49, 0x4c, 0x4b, 0x5f, 0x56, 0x33]\n\nfunction matchAt(b: Uint8Array, off: number, magic: number[]): boolean {\n if (off + magic.length > b.length) return false\n for (let i = 0; i < magic.length; i++) if (b[off + i] !== magic[i]) return false\n return true\n}\n\n/**\n * 按文件头判别语音编解码;非语音编解码(普通 mp3/wav/… 或无法识别)返回 null。\n * SILK 容器有两种头:Skype 裸头 `#!SILK_V3`(偏移 0)与腾讯头 `\\x02#!SILK_V3`(偏移 1)。\n * AMR-WB 必须先于 AMR-NB 判断(`#!AMR` 是 `#!AMR-WB` 的前缀)。\n */\nexport function sniffSpeechCodec(bytes: Uint8Array): SpeechCodec | null {\n if (matchAt(bytes, 0, MAGIC_SILK) || matchAt(bytes, 1, MAGIC_SILK)) return 'silk'\n if (matchAt(bytes, 0, MAGIC_AMR_WB)) return 'amr-wb'\n if (matchAt(bytes, 0, MAGIC_AMR_NB)) return 'amr-nb'\n return null\n}\n\n// SILK 容器不在头部记采样率(SILK 本身与采样率解耦),业界解码器(silk-v3-decoder 等)一律按 24000Hz\n// 还原微信 / QQ 语音,这里沿用该约定。\nconst SILK_SAMPLE_RATE = 24000\n\nexport interface DecodedSpeech {\n /** 完整 WAV 字节(44 字节头 + PCM s16le)。 */\n wav: Uint8Array\n codec: SpeechCodec\n sampleRate: number\n channels: number\n durationSec: number\n}\n\nexport class SpeechDecodeError extends Error {}\n\n/** Float32(−1..1) → PCM s16le 字节。 */\nfunction floatToS16le(f: Float32Array): Uint8Array {\n const out = new Uint8Array(f.length * 2)\n const dv = new DataView(out.buffer)\n for (let i = 0; i < f.length; i++) {\n const s = Math.max(-1, Math.min(1, f[i]))\n dv.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true)\n }\n return out\n}\n\n/** 用 PCM s16le 数据包出一段标准 44 字节头的 WAV(单声道、16 位)。 */\nexport function wavFromPcmS16le(pcm: Uint8Array, sampleRate: number, channels = 1): Uint8Array {\n const blockAlign = channels * 2\n const out = new Uint8Array(44 + pcm.length)\n const dv = new DataView(out.buffer)\n const tag = (o: number, s: string): void => {\n for (let i = 0; i < s.length; i++) out[o + i] = s.charCodeAt(i)\n }\n tag(0, 'RIFF')\n dv.setUint32(4, 36 + pcm.length, true)\n tag(8, 'WAVE')\n tag(12, 'fmt ')\n dv.setUint32(16, 16, true) // fmt chunk 长度(PCM)\n dv.setUint16(20, 1, true) // 格式 = PCM\n dv.setUint16(22, channels, true)\n dv.setUint32(24, sampleRate, true)\n dv.setUint32(28, sampleRate * blockAlign, true) // 字节率\n dv.setUint16(32, blockAlign, true)\n dv.setUint16(34, 16, true) // 位深\n tag(36, 'data')\n dv.setUint32(40, pcm.length, true)\n out.set(pcm, 44)\n return out\n}\n\nasync function decodeSilk(bytes: Uint8Array): Promise<DecodedSpeech> {\n const { decode } = await import('silk-wasm')\n const { data, duration } = await decode(bytes, SILK_SAMPLE_RATE)\n if (!data.length) throw new SpeechDecodeError('SILK 解码结果为空')\n return {\n wav: wavFromPcmS16le(data, SILK_SAMPLE_RATE),\n codec: 'silk',\n sampleRate: SILK_SAMPLE_RATE,\n channels: 1,\n durationSec: duration / 1000,\n }\n}\n\nasync function decodeAmr(bytes: Uint8Array, codec: 'amr-nb' | 'amr-wb'): Promise<DecodedSpeech> {\n const { default: amrDecode } = await import('@audio/decode-amr')\n const { channelData, sampleRate } = await amrDecode(bytes)\n const mono = channelData[0]\n if (!mono || !mono.length) throw new SpeechDecodeError('AMR 解码结果为空')\n return {\n wav: wavFromPcmS16le(floatToS16le(mono), sampleRate),\n codec,\n sampleRate,\n channels: 1,\n durationSec: mono.length / sampleRate,\n }\n}\n\n/**\n * 把一段 AMR / SILK 语音字节解码成可播放的 WAV:先 {@link sniffSpeechCodec} 判型,再懒加载对应 WASM\n * 解码器解出 PCM 并包成 WAV。非 AMR/SILK 抛 {@link SpeechDecodeError}(调用方据此回退原生播放)。\n */\nexport async function decodeSpeechToWav(input: ArrayBuffer | Uint8Array): Promise<DecodedSpeech> {\n const bytes = input instanceof Uint8Array ? input : new Uint8Array(input)\n const codec = sniffSpeechCodec(bytes)\n if (!codec) throw new SpeechDecodeError('不是可识别的 AMR / SILK 语音文件')\n try {\n if (codec === 'silk') return await decodeSilk(bytes)\n return await decodeAmr(bytes, codec)\n } catch (e) {\n if (e instanceof SpeechDecodeError) throw e\n throw new SpeechDecodeError(e instanceof Error ? e.message : String(e))\n }\n}\n"],"mappings":"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"}
@@ -102,3 +102,5 @@ function d(e, t) {
102
102
  }
103
103
  //#endregion
104
104
  export { d as execSql, r as initSqlEngine, l as listTables, i as loadDatabase, u as readPage };
105
+
106
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","names":[],"sources":["../../src/lib/sqlite.ts"],"sourcesContent":["/**\n * SQLite 只读读取层 —— 基于 sql.js(WASM),不依赖 Vue / DOM。\n *\n * 移植自 sqlite-viewer 项目(D:\\web-work\\sqlite-viewer\\src\\db\\engine.ts),但**裁剪为只读预览**:仅\n * 保留「加载库 → 列出表/视图 → 按需分页读行 → 执行任意 SQL」这条链路,去掉建表/改表/编辑/事务等写操作\n * (文件查看器是只读的,且各组件都强制 attachment 下载、不回写)。\n *\n * sql.js 的 wasm 走 Vite `?url` 同源资源(与 lib/rar.ts 的 unrar.wasm 同款),不连任何 CDN;引擎实例\n * 全局复用一次初始化。本模块被 SqliteViewer.vue 动态 import,故 sql.js + wasm 仅在打开 .sqlite 时才下载。\n */\nimport initSqlJs from 'sql.js'\nimport type { Database, SqlJsStatic, SqlValue } from 'sql.js'\nimport wasmUrl from 'sql.js/dist/sql-wasm.wasm?url'\n\nlet sqlPromise: Promise<SqlJsStatic> | null = null\n\n/** 加载并初始化 sql.js(WASM)。多次调用复用同一实例。 */\nexport function initSqlEngine(): Promise<SqlJsStatic> {\n if (!sqlPromise) sqlPromise = initSqlJs({ locateFile: () => wasmUrl })\n return sqlPromise\n}\n\n/** 从 .sqlite / .db 文件字节加载一个库(一次性入内存,sql.js 全在内存运行)。 */\nexport function loadDatabase(SQL: SqlJsStatic, bytes: Uint8Array): Database {\n return new SQL.Database(bytes)\n}\n\nexport type CellValue = string | number | null\n\nexport interface Column {\n name: string\n type: string\n pk?: boolean\n fk?: boolean\n idx?: boolean\n notnull?: boolean\n}\n\n/** 一张表 / 视图的概要(不含行数据,行数据按需分页读取)。 */\nexport interface TableInfo {\n name: string\n kind: 'table' | 'view'\n rowCount: number\n columns: Column[]\n}\n\nexport interface QueryResult {\n columns: string[]\n rows: CellValue[][]\n rowCount: number\n elapsedMs: number\n error: string\n}\n\n/** 标识符加双引号转义,安全拼进 SQL(表名来自库自身,仍按规范转义)。 */\nfunction quoteIdent(name: string): string {\n return '\"' + name.replace(/\"/g, '\"\"') + '\"'\n}\n\n/** SqlValue → CellValue(BLOB 以占位文本展示,避免把二进制塞进表格)。 */\nfunction toCell(v: SqlValue): CellValue {\n if (v == null) return null\n if (v instanceof Uint8Array) return `[BLOB ${v.length}B]`\n return v\n}\n\n/** 读出某表/视图的列结构(含 pk / fk / idx / notnull 徽标信息)。 */\nexport function readColumns(db: Database, table: string): Column[] {\n const qi = quoteIdent(table)\n\n // 外键列\n const fkCols = new Set<string>()\n const fkRes = db.exec(`PRAGMA foreign_key_list(${qi})`)\n if (fkRes.length) {\n const fromIdx = fkRes[0].columns.indexOf('from')\n for (const r of fkRes[0].values) fkCols.add(String(r[fromIdx]))\n }\n\n // 非主键索引涉及的列(用于列徽标)\n const idxCols = new Set<string>()\n const idxList = db.exec(`PRAGMA index_list(${qi})`)\n if (idxList.length) {\n const ic = idxList[0].columns\n const nameIdx = ic.indexOf('name')\n const originIdx = ic.indexOf('origin')\n for (const r of idxList[0].values) {\n const iname = String(r[nameIdx])\n const origin = originIdx >= 0 ? String(r[originIdx]) : 'c'\n if (origin === 'pk') continue\n const infoRes = db.exec(`PRAGMA index_info(${quoteIdent(iname)})`)\n if (infoRes.length) {\n const colIdx = infoRes[0].columns.indexOf('name')\n for (const ir of infoRes[0].values) {\n if (ir[colIdx] != null) idxCols.add(String(ir[colIdx]))\n }\n }\n }\n }\n\n const columns: Column[] = []\n const info = db.exec(`PRAGMA table_info(${qi})`)\n if (info.length) {\n const c = info[0].columns\n const nameI = c.indexOf('name')\n const typeI = c.indexOf('type')\n const pkI = c.indexOf('pk')\n const nnI = c.indexOf('notnull')\n for (const r of info[0].values) {\n const col: Column = { name: String(r[nameI]), type: String(r[typeI] ?? '') }\n if (Number(r[pkI]) > 0) col.pk = true\n if (nnI >= 0 && Number(r[nnI]) === 1) col.notnull = true\n if (fkCols.has(col.name)) col.fk = true\n if (idxCols.has(col.name)) col.idx = true\n columns.push(col)\n }\n }\n return columns\n}\n\n/** 表/视图行数(视图可能为表达式,失败回退 0)。 */\nexport function rowCount(db: Database, table: string): number {\n try {\n const res = db.exec(`SELECT COUNT(*) FROM ${quoteIdent(table)}`)\n return res.length && res[0].values.length ? Number(res[0].values[0][0]) : 0\n } catch {\n return 0\n }\n}\n\n/** 列出所有用户表与视图(含列结构与行数),按名称排序;排除 sqlite_ 内部表。 */\nexport function listTables(db: Database): TableInfo[] {\n const res = db.exec(\n \"SELECT name, type FROM sqlite_master WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' ORDER BY type, name\",\n )\n if (!res.length) return []\n const out: TableInfo[] = []\n for (const r of res[0].values) {\n const name = String(r[0])\n const kind = String(r[1]) === 'view' ? 'view' : 'table'\n out.push({ name, kind, rowCount: rowCount(db, name), columns: readColumns(db, name) })\n }\n return out\n}\n\n/** 按页读取某表/视图的行(LIMIT/OFFSET)。仅取数据,不带列结构(列结构走 readColumns)。 */\nexport function readPage(\n db: Database,\n table: string,\n limit: number,\n offset: number,\n): CellValue[][] {\n const res = db.exec(`SELECT * FROM ${quoteIdent(table)} LIMIT ${limit} OFFSET ${offset}`)\n return res.length ? res[0].values.map((r) => r.map(toCell)) : []\n}\n\n/** 执行任意 SQL,返回最后一个结果集;出错时 error 非空(只读预览,不强制只读但不暴露写 UI)。 */\nexport function execSql(db: Database, sql: string): QueryResult {\n const start = performance.now()\n try {\n const res = db.exec(sql)\n const elapsed = performance.now() - start\n const last = res.length ? res[res.length - 1] : null\n if (last) {\n return {\n columns: last.columns,\n rows: last.values.map((r) => r.map(toCell)),\n rowCount: last.values.length,\n elapsedMs: elapsed,\n error: '',\n }\n }\n return { columns: [], rows: [], rowCount: 0, elapsedMs: elapsed, error: '' }\n } catch (e) {\n return {\n columns: [],\n rows: [],\n rowCount: 0,\n elapsedMs: performance.now() - start,\n error: e instanceof Error ? e.message : String(e),\n }\n }\n}\n"],"mappings":"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"}
@@ -97,3 +97,5 @@ function c(e, t) {
97
97
  }
98
98
  //#endregion
99
99
  export { c as parseSubtitle };
100
+
101
+ //# sourceMappingURL=subtitle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subtitle.js","names":[],"sources":["../../src/lib/subtitle.ts"],"sourcesContent":["/**\n * Hand-rolled, zero-dependency subtitle parser for the side viewer.\n *\n * Parses the three common caption formats into a uniform cue list:\n * - SubRip (.srt): block = optional index line, `HH:MM:SS,mmm --> HH:MM:SS,mmm`, then text lines\n * - WebVTT (.vtt): a `WEBVTT` header, optional cue ids / NOTE / STYLE / REGION blocks, and\n * `HH:MM:SS.mmm --> HH:MM:SS.mmm <settings>` timing lines; inline tags stripped\n * - SubStation Alpha (.ass/.ssa): an `[Events]` section with a `Format:` line + `Dialogue:` rows;\n * override tags `{\\…}` and the `\\N` / `\\h` escapes are handled\n *\n * The viewer only needs start/end/text (plus an optional speaker from the ASS Name field), so styling,\n * positioning, karaoke timing, etc. are intentionally discarded — this is a readable transcript, not a\n * pixel-accurate renderer. Times are normalized to milliseconds.\n */\n\nexport interface SubtitleCue {\n start: number // ms\n end: number // ms\n text: string // plain text, lines joined with '\\n', markup stripped\n speaker?: string // ASS \"Name\"/actor field, when present\n}\n\nexport interface ParsedSubtitle {\n format: 'srt' | 'vtt' | 'ass'\n cues: SubtitleCue[]\n}\n\n/** A clock anywhere in a string: optional hours, then MM:SS, then `.`/`,` and 1–3 fractional digits. */\nconst CLOCK_RE = /(?:(\\d+):)?(\\d{1,2}):(\\d{1,2})[.,](\\d{1,3})/\n\n// Extract the first clock found in `s` (so trailing VTT cue settings / SRT coords are ignored) → ms.\n// ASS centiseconds (`.cc`) round-trip correctly: `.05` → \"050\" → 50ms, `.5` → \"500\" → 500ms.\nfunction extractClock(s: string): number | null {\n const m = s.match(CLOCK_RE)\n if (!m) return null\n const h = m[1] ? parseInt(m[1], 10) : 0\n const min = parseInt(m[2], 10)\n const sec = parseInt(m[3], 10)\n const frac = parseInt(m[4].padEnd(3, '0').slice(0, 3), 10)\n return ((h * 60 + min) * 60 + sec) * 1000 + frac\n}\n\n// Strip WebVTT / SubRip inline markup: HTML-ish tags (<b>, <i>, <c.color>, <v Speaker>) and the\n// `<00:00:01.000>` karaoke timestamps. Leaves the readable text.\nfunction stripCueMarkup(s: string): string {\n return s\n .replace(/<[^>]*>/g, '')\n .replace(/\\{\\\\[^}]*\\}/g, '')\n .replace(/[ \\t]+\\n/g, '\\n')\n .trim()\n}\n\nfunction parseSrtVtt(text: string, vtt: boolean): SubtitleCue[] {\n const cues: SubtitleCue[] = []\n for (const block of text.split(/\\n{2,}/)) {\n // VTT preamble / metadata blocks never carry a cue.\n if (vtt && /^\\s*(WEBVTT|NOTE|STYLE|REGION)\\b/.test(block)) continue\n const lines = block.split('\\n')\n const tIdx = lines.findIndex((l) => l.includes('-->'))\n if (tIdx < 0) continue\n const [left, right = ''] = lines[tIdx].split('-->')\n const start = extractClock(left)\n const end = extractClock(right)\n if (start === null || end === null) continue\n // Lines before the timing line are an optional index (SRT) or cue id (VTT) — dropped.\n const body = lines\n .slice(tIdx + 1)\n .join('\\n')\n .trim()\n cues.push({ start, end, text: stripCueMarkup(body) })\n }\n return cues\n}\n\n// Standard ASS/SSA Events field order, used when a `[Events]` section omits its `Format:` line.\nconst DEFAULT_ASS_FMT = [\n 'layer',\n 'start',\n 'end',\n 'style',\n 'name',\n 'marginl',\n 'marginr',\n 'marginv',\n 'effect',\n 'text',\n]\n\n// The ASS Text field is always last and may itself contain commas, so split into exactly `count`\n// fields — the first count-1 on commas, the remainder (text) kept whole.\nfunction splitAssFields(rest: string, count: number): string[] {\n if (count <= 1) return [rest]\n const out: string[] = []\n let i = 0\n for (let f = 0; f < count - 1; f++) {\n const c = rest.indexOf(',', i)\n if (c < 0) break\n out.push(rest.slice(i, c))\n i = c + 1\n }\n out.push(rest.slice(i))\n return out\n}\n\n// Drop override blocks `{\\…}`, turn `\\N`/`\\n` (hard breaks) into newlines and `\\h` into a space.\nfunction cleanAssText(s: string): string {\n return s\n .replace(/\\{[^}]*\\}/g, '')\n .replace(/\\\\N/gi, '\\n')\n .replace(/\\\\h/gi, ' ')\n .trim()\n}\n\nfunction parseAss(text: string): SubtitleCue[] {\n const cues: SubtitleCue[] = []\n let inEvents = false\n let fmt: string[] = []\n for (const raw of text.split('\\n')) {\n const line = raw.trim()\n if (line.startsWith('[')) {\n inEvents = /^\\[events\\]/i.test(line)\n continue\n }\n if (!inEvents || !line) continue\n if (/^format\\s*:/i.test(line)) {\n fmt = line\n .slice(line.indexOf(':') + 1)\n .split(',')\n .map((s) => s.trim().toLowerCase())\n continue\n }\n if (/^dialogue\\s*:/i.test(line)) {\n const order = fmt.length ? fmt : DEFAULT_ASS_FMT\n const fields = splitAssFields(line.slice(line.indexOf(':') + 1), order.length)\n const get = (key: string): string => {\n const i = order.indexOf(key)\n return i >= 0 ? (fields[i] ?? '') : ''\n }\n const start = extractClock(get('start'))\n const end = extractClock(get('end'))\n if (start === null || end === null) continue\n const speaker = get('name').trim()\n cues.push({\n start,\n end,\n text: cleanAssText(get('text') || fields[fields.length - 1] || ''),\n speaker: speaker || undefined,\n })\n }\n }\n return cues\n}\n\n/** Parse subtitle source text into a sorted cue list. `ext` (when known) disambiguates the format. */\nexport function parseSubtitle(input: string, ext?: string): ParsedSubtitle {\n // Strip a leading UTF-8 BOM (U+FEFF) and normalize CRLF/CR line endings.\n const text = (input.charCodeAt(0) === 0xfeff ? input.slice(1) : input).replace(/\\r\\n?/g, '\\n')\n const e = (ext || '').toLowerCase()\n let result: ParsedSubtitle\n if (e === 'ass' || e === 'ssa' || /^\\s*\\[script info\\]/i.test(text)) {\n result = { format: 'ass', cues: parseAss(text) }\n } else if (e === 'vtt' || /^\\s*WEBVTT/.test(text)) {\n result = { format: 'vtt', cues: parseSrtVtt(text, true) }\n } else {\n result = { format: 'srt', cues: parseSrtVtt(text, false) }\n }\n result.cues.sort((a, b) => a.start - b.start || a.end - b.end)\n return result\n}\n"],"mappings":"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"}
@@ -246,3 +246,5 @@ async function S(e, t) {
246
246
  }
247
247
  //#endregion
248
248
  export { S as openTarball };
249
+
250
+ //# sourceMappingURL=tarball.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tarball.js","names":[],"sources":["../../src/lib/tarball.ts"],"sourcesContent":["/**\n * In-browser reader for the tar family — `.tar` and its compressed wrappers `.gz`/`.tgz`, `.xz`/`.txz`,\n * `.bz2`/`.tbz2`, `.br`/`.tar.br` — plus a lone compressed file (`foo.json.gz`). No dependency beyond the\n * browser's native gzip; xz reuses the project's hand-rolled LZMA2 decoder ({@link lib/lzma}), bzip2 its BWT\n * decoder ({@link lib/bzip2}), and Brotli a lazily-loaded WASM decoder ({@link lib/brotli}).\n *\n * Unlike ZIP/7z (random-access central directories), a tar is solid: the whole stream is decompressed to\n * memory once, then tar headers are walked to index each file as an (offset, size) slice into that buffer —\n * so extraction is a cheap, synchronous sub-array copy. The result is exposed through the shared\n * {@link OpenedArchive} interface so {@link components/ArchiveBrowser} can browse it like any other archive.\n *\n * If the decompressed bytes aren't a tar (a bare `foo.txt.gz`), the archive is presented as a single entry\n * named by stripping the compression suffix.\n */\n\nimport { bcjX86Decode, lzma2Decode } from '@/lib/lzma'\nimport { bzip2Decode } from '@/lib/bzip2'\nimport { decodeFileNameBytes } from '@/lib/encoding'\nimport type { ArchiveEntry, OpenedArchive } from '@/lib/archive'\n\nconst GZIP_MAGIC = [0x1f, 0x8b]\nconst XZ_MAGIC = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00]\nconst BZIP2_MAGIC = [0x42, 0x5a, 0x68] // \"BZh\"\n\nfunction hasMagic(b: Uint8Array, magic: number[], off = 0): boolean {\n if (b.length < off + magic.length) return false\n return magic.every((m, i) => b[off + i] === m)\n}\n\n// ── gzip (browser-native) ───────────────────────────────────────────────────────────────────────\n\nasync function gunzip(data: Uint8Array): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'undefined') {\n throw new Error('当前浏览器不支持解压 gzip(缺少 DecompressionStream)')\n }\n const stream = new Blob([data]).stream().pipeThrough(new DecompressionStream('gzip'))\n return new Uint8Array(await new Response(stream).arrayBuffer())\n}\n\n// ── xz (LZMA2 in the xz container) ────────────────────────────────────────────────────────────────\n\n// Check sizes per the stream-flags check id (0/4/8/16/32/64). None=0, CRC32=1, CRC64=4, SHA256=10.\nconst XZ_CHECK_SIZE = [0, 4, 4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 32, 64, 64, 64]\n\n/** Read an xz/LZMA2 variable-length integer (base-128 little-endian). */\nfunction readVli(b: Uint8Array, pos: number): { value: number; next: number } {\n let value = 0\n let shift = 0\n let i = pos\n for (;;) {\n const byte = b[i++]\n value += (byte & 0x7f) * 2 ** shift\n if ((byte & 0x80) === 0) break\n shift += 7\n if (shift > 63) throw new Error('xz VLI 过长')\n }\n return { value, next: i }\n}\n\n/**\n * Walk an LZMA2 chunk stream (without decoding) to find where it ends (its 0x00 terminator) and sum the\n * uncompressed size — xz block headers usually omit both, but the chunk framing carries them. Mirrors the\n * framing in {@link lzma2Decode}.\n */\nfunction scanLzma2(b: Uint8Array, start: number): { compLen: number; uncompSize: number } {\n let ip = start\n let uncomp = 0\n for (;;) {\n const control = b[ip++]\n if (control === 0) break\n if (control >= 0x80) {\n const unpackSize = ((control & 0x1f) << 16) + (b[ip] << 8) + b[ip + 1] + 1\n ip += 2\n const packSize = (b[ip] << 8) + b[ip + 1] + 1\n ip += 2\n if (((control >> 5) & 3) >= 2) ip++ // props byte\n ip += packSize\n uncomp += unpackSize\n } else {\n if (control > 2) throw new Error(`xz LZMA2 非法控制字节 0x${control.toString(16)}`)\n const size = (b[ip] << 8) + b[ip + 1] + 1\n ip += 2\n ip += size\n uncomp += size\n }\n }\n return { compLen: ip - start, uncompSize: uncomp }\n}\n\nfunction xzDecode(data: Uint8Array): Uint8Array {\n if (!hasMagic(data, XZ_MAGIC)) throw new Error('不是有效的 xz 文件')\n const checkSize = XZ_CHECK_SIZE[data[7] & 0x0f]\n const chunks: Uint8Array[] = []\n let pos = 12 // past the 12-byte stream header\n while (pos < data.length) {\n const hsByte = data[pos]\n if (hsByte === 0x00) break // index indicator — blocks are done\n const headerSize = (hsByte + 1) * 4\n const flags = data[pos + 1]\n const numFilters = (flags & 0x03) + 1\n let c = pos + 2\n if (flags & 0x40) c = readVli(data, c).next // compressed size present (unused — scan finds it)\n let declaredUncomp = -1\n if (flags & 0x80) {\n const r = readVli(data, c)\n declaredUncomp = r.value\n c = r.next\n }\n let lzma2Props = new Uint8Array(0)\n let useBcj = false\n for (let f = 0; f < numFilters; f++) {\n const id = readVli(data, c)\n c = id.next\n const ps = readVli(data, c)\n c = ps.next\n const props = data.subarray(c, c + ps.value)\n c += ps.value\n if (id.value === 0x21) lzma2Props = props.slice()\n else if (id.value === 0x04)\n useBcj = true // x86 BCJ\n else throw new Error(`xz 不支持的过滤器 0x${id.value.toString(16)}`)\n }\n const dataStart = pos + headerSize\n const { compLen, uncompSize } = scanLzma2(data, dataStart)\n const outSize = declaredUncomp >= 0 ? declaredUncomp : uncompSize\n const out = lzma2Decode(data.subarray(dataStart, dataStart + compLen), lzma2Props, outSize)\n if (useBcj) bcjX86Decode(out)\n chunks.push(out)\n let next = dataStart + compLen\n next += (4 - (compLen % 4)) % 4 // block padding aligns compressed data to 4 bytes\n next += checkSize\n pos = next\n }\n let total = 0\n for (const c of chunks) total += c.length\n const out = new Uint8Array(total)\n let off = 0\n for (const c of chunks) {\n out.set(c, off)\n off += c.length\n }\n return out\n}\n\n// ── tar ───────────────────────────────────────────────────────────────────────────────────────────\n\ninterface TarItem {\n path: string\n name: string\n size: number\n isDir: boolean\n /** Byte offset of this file's data within the decompressed tar buffer. */\n offset: number\n}\n\nconst td = new TextDecoder('utf-8')\n\n/** Read a NUL-terminated field from a 512-byte tar header block. */\nfunction field(block: Uint8Array, off: number, len: number): string {\n let end = off\n while (end < off + len && block[end] !== 0) end++\n return td.decode(block.subarray(off, end))\n}\n\n/** Like {@link field}, but for a name field — sniffs the encoding (UTF-8 → GBK → \\uXXXX) instead of forcing UTF-8. */\nfunction nameField(block: Uint8Array, off: number, len: number): string {\n let end = off\n while (end < off + len && block[end] !== 0) end++\n return decodeFileNameBytes(block.subarray(off, end))\n}\n\n/** Parse a tar numeric field — octal, or GNU base-256 when the high bit of the first byte is set. */\nfunction numField(block: Uint8Array, off: number, len: number): number {\n if (block[off] & 0x80) {\n let v = block[off] & 0x7f\n for (let i = 1; i < len; i++) v = v * 256 + block[off + i]\n return v\n }\n const s = field(block, off, len).trim()\n return s ? parseInt(s, 8) || 0 : 0\n}\n\n/** Extract `path=` / `size=` overrides from a pax extended header's \"<len> key=value\\n\" records. */\nfunction parsePax(text: string): { path?: string; size?: number } {\n const out: { path?: string; size?: number } = {}\n let i = 0\n while (i < text.length) {\n const sp = text.indexOf(' ', i)\n if (sp < 0) break\n const len = parseInt(text.slice(i, sp), 10)\n if (!len || len <= 0) break\n const record = text.slice(sp + 1, i + len - 1) // drop the trailing newline\n const eq = record.indexOf('=')\n if (eq > 0) {\n const key = record.slice(0, eq)\n const val = record.slice(eq + 1)\n if (key === 'path') out.path = val\n else if (key === 'size') out.size = parseInt(val, 10) || 0\n }\n i += len\n }\n return out\n}\n\nconst ceil512 = (n: number): number => (n + 511) & ~511\n\n/** Walk a decompressed tar buffer into a flat file list. Handles GNU long names ('L') and pax ('x') headers. */\nfunction parseTar(data: Uint8Array): TarItem[] {\n const items: TarItem[] = []\n let off = 0\n let longName = '' // pending GNU 'L' override for the next entry\n let pax: { path?: string; size?: number } = {} // pending pax override for the next entry\n while (off + 512 <= data.length) {\n const block = data.subarray(off, off + 512)\n // An all-zero block marks end-of-archive.\n let allZero = true\n for (let i = 0; i < 512; i++) {\n if (block[i] !== 0) {\n allZero = false\n break\n }\n }\n if (allZero) break\n\n const name = nameField(block, 0, 100)\n const prefix = nameField(block, 345, 155)\n const type = String.fromCharCode(block[156])\n let size = numField(block, 124, 12)\n const dataOff = off + 512\n\n if (type === 'L') {\n // GNU long name: the real name is this entry's payload.\n longName = nameField(data.subarray(dataOff, dataOff + size), 0, size)\n off = dataOff + ceil512(size)\n continue\n }\n if (type === 'K') {\n // GNU long link target — irrelevant to browsing; skip its payload.\n off = dataOff + ceil512(size)\n continue\n }\n if (type === 'x' || type === 'g') {\n // pax extended header: parse path/size for the following entry (g = global, applied likewise).\n pax = parsePax(td.decode(data.subarray(dataOff, dataOff + size)))\n off = dataOff + ceil512(size)\n continue\n }\n\n let full = longName || pax.path || (prefix ? `${prefix}/${name}` : name)\n if (pax.size !== undefined) size = pax.size\n longName = ''\n pax = {}\n\n const isDir = type === '5' || full.endsWith('/')\n full = full.replace(/\\/+$/, '')\n if (full) {\n items.push({\n path: full,\n name: full.split('/').pop() ?? full,\n size: isDir ? 0 : size,\n isDir,\n offset: dataOff,\n })\n }\n off = dataOff + (isDir ? 0 : ceil512(size))\n }\n return items\n}\n\n// ── format dispatch ─────────────────────────────────────────────────────────────────────────────\n\n/** A decompressed buffer is a tar if it carries the ustar magic at offset 257. */\nfunction isTar(data: Uint8Array): boolean {\n return data.length >= 512 && field(data.subarray(257, 263), 0, 5) === 'ustar'\n}\n\n/** Strip a compression suffix to name the single inner file of a non-tar `.gz`/`.xz`/`.bz2`. */\nfunction strippedName(name: string): string {\n const lower = name.toLowerCase()\n for (const [suf, rep] of [\n ['.tar.gz', '.tar'],\n ['.tar.xz', '.tar'],\n ['.tar.bz2', '.tar'],\n ['.tgz', '.tar'],\n ['.txz', '.tar'],\n ['.tbz2', '.tar'],\n ['.tbz', '.tar'],\n ['.tar.br', '.tar'],\n ['.tbr', '.tar'],\n ] as const) {\n if (lower.endsWith(suf)) return name.slice(0, -suf.length) + rep\n }\n for (const suf of ['.gz', '.xz', '.bz2', '.bz', '.br']) {\n if (lower.endsWith(suf)) return name.slice(0, -suf.length)\n }\n return `${name}.out`\n}\n\n/**\n * Open a tar-family archive: decompress the outer wrapper (gzip/xz/bzip2, by magic) if present, then either\n * index it as a tar or, failing that, surface the single decompressed file.\n */\nexport async function openTarball(buf: ArrayBuffer, name: string): Promise<OpenedArchive> {\n let data: Uint8Array = new Uint8Array(buf)\n if (hasMagic(data, GZIP_MAGIC)) data = await gunzip(data)\n else if (hasMagic(data, XZ_MAGIC)) data = xzDecode(data)\n else if (hasMagic(data, BZIP2_MAGIC)) data = bzip2Decode(data)\n else if (!isTar(data)) {\n // Brotli streams carry NO magic bytes. Anything routed here that isn't gzip/xz/bzip2 and isn't already a\n // bare tar (the only other no-magic tar-family member) is treated as Brotli (.br / .tar.br). The WASM\n // decoder is dynamic-imported so its ~250KB wasm loads only when a Brotli file is actually opened.\n const { brotliDecode } = await import('@/lib/brotli')\n data = await brotliDecode(data)\n }\n\n if (isTar(data)) {\n const items = parseTar(data)\n const entries: ArchiveEntry[] = items.map((it, i) => ({\n path: it.path,\n name: it.name,\n size: it.size,\n isDir: it.isDir,\n ref: i,\n }))\n const bytes = data\n return {\n entries,\n extract(e) {\n const it = items[e.ref]\n if (!it || it.isDir) return new Uint8Array(0)\n return bytes.subarray(it.offset, it.offset + it.size).slice()\n },\n }\n }\n\n // Not a tar — a lone compressed file. Present it as one entry.\n const inner = strippedName(name)\n const single = data\n return {\n entries: [{ path: inner, name: inner, size: single.length, isDir: false, ref: 0 }],\n extract() {\n return single\n },\n }\n}\n"],"mappings":"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"}
@@ -81,3 +81,5 @@ function n(e, t) {
81
81
  }
82
82
  //#endregion
83
83
  export { e as VIEWER_FORMATTERS, n as applyFormatter, t as findFormatter };
84
+
85
+ //# sourceMappingURL=viewerFormats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewerFormats.js","names":[],"sources":["../../src/lib/viewerFormats.ts"],"sourcesContent":["/**\n * The \"open with\" formatter set — the universal escape hatch for the side viewer.\n *\n * Auto-detection ({@link classifyViewerFile}) routes a file to a specialized viewer by extension/mime, but that\n * guess can be wrong (a mislabeled file, a `.voice` blob that's really SILK, bytes worth eyeballing as hex).\n * Each formatter here REINTERPRETS the same name/url/size/content as a different {@link ViewerFile}, so it\n * applies to ANY file regardless of what detection picked. The list is shared by:\n * - {@link FileViewerPanel} — renders the header \"打开方式\" menu (available for every file), and\n * - {@link FileViewerBody} — applies the choice and also offers the same chips on its unpreviewable-binary\n * card (the archive-browser context, where there is no host header).\n *\n * These are deliberately the GENERIC reinterpretations that work on raw bytes — hex / text / markdown / html /\n * svg / image / audio / video. The heavy structured viewers (docx / xlsx / zip / …) stay auto-detected only:\n * forcing arbitrary bytes through them just errors, so they'd be noise in a \"force open as\" list.\n */\nimport type { StrokeIconName } from '@/components/icons/iconPaths'\nimport type { ViewerFile } from '@/composables/useFileViewer'\n\nexport interface ViewerFormatter {\n /** Stable id (used for the active-selection highlight and the body↔header sync). */\n key: string\n label: string\n icon: StrokeIconName\n /** Merged onto the base file to force this interpretation; `lang` is always set (it drives the source view). */\n patch: Partial<ViewerFile> & { lang: string }\n}\n\nexport const VIEWER_FORMATTERS: ViewerFormatter[] = [\n { key: 'hex', label: '十六进制', icon: 'hex', patch: { lang: '', hex: true } },\n { key: 'text', label: '文本 / 源码', icon: 'code', patch: { lang: '' } },\n { key: 'markdown', label: 'Markdown', icon: 'fileOutline', patch: { lang: 'markdown' } },\n { key: 'html', label: 'HTML', icon: 'globe', patch: { lang: 'xml', html: true } },\n { key: 'svg', label: 'SVG', icon: 'image', patch: { lang: 'xml', svg: true } },\n { key: 'image', label: '图片', icon: 'image', patch: { lang: '', image: true } },\n { key: 'audio', label: '音频', icon: 'music', patch: { lang: '', audio: true } },\n { key: 'video', label: '视频', icon: 'play', patch: { lang: '', video: true } },\n]\n\n/** Look a formatter up by its {@link ViewerFormatter.key}; undefined for an unknown key. */\nexport function findFormatter(key: string): ViewerFormatter | undefined {\n return VIEWER_FORMATTERS.find((f) => f.key === key)\n}\n\n/**\n * Build the EFFECTIVE file for a chosen formatter: same name/url/size/content as the original, but only the\n * formatter's flags (no carried-over detection flags), so the body's dispatch re-runs cleanly as that format.\n */\nexport function applyFormatter(file: ViewerFile, f: ViewerFormatter): ViewerFile {\n return { name: file.name, url: file.url, size: file.size, content: file.content, ...f.patch }\n}\n"],"mappings":";AA2BA,IAAa,IAAuC;CAClD;EAAE,KAAK;EAAO,OAAO;EAAQ,MAAM;EAAO,OAAO;GAAE,MAAM;GAAI,KAAK;EAAK;CAAE;CACzE;EAAE,KAAK;EAAQ,OAAO;EAAW,MAAM;EAAQ,OAAO,EAAE,MAAM,GAAG;CAAE;CACnE;EAAE,KAAK;EAAY,OAAO;EAAY,MAAM;EAAe,OAAO,EAAE,MAAM,WAAW;CAAE;CACvF;EAAE,KAAK;EAAQ,OAAO;EAAQ,MAAM;EAAS,OAAO;GAAE,MAAM;GAAO,MAAM;EAAK;CAAE;CAChF;EAAE,KAAK;EAAO,OAAO;EAAO,MAAM;EAAS,OAAO;GAAE,MAAM;GAAO,KAAK;EAAK;CAAE;CAC7E;EAAE,KAAK;EAAS,OAAO;EAAM,MAAM;EAAS,OAAO;GAAE,MAAM;GAAI,OAAO;EAAK;CAAE;CAC7E;EAAE,KAAK;EAAS,OAAO;EAAM,MAAM;EAAS,OAAO;GAAE,MAAM;GAAI,OAAO;EAAK;CAAE;CAC7E;EAAE,KAAK;EAAS,OAAO;EAAM,MAAM;EAAQ,OAAO;GAAE,MAAM;GAAI,OAAO;EAAK;CAAE;AAC9E;AAGA,SAAgB,EAAc,GAA0C;CACtE,OAAO,EAAkB,MAAM,MAAM,EAAE,QAAQ,CAAG;AACpD;AAMA,SAAgB,EAAe,GAAkB,GAAgC;CAC/E,OAAO;EAAE,MAAM,EAAK;EAAM,KAAK,EAAK;EAAK,MAAM,EAAK;EAAM,SAAS,EAAK;EAAS,GAAG,EAAE;CAAM;AAC9F"}
@@ -231,3 +231,5 @@ function Z(e, n) {
231
231
  }
232
232
  //#endregion
233
233
  export { Z as canToggleView, q as classifyViewerFile, Y as mediaMimeForName, X as skipsTextLoad };
234
+
235
+ //# sourceMappingURL=viewerKind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewerKind.js","names":[],"sources":["../../src/lib/viewerKind.ts"],"sourcesContent":["/**\n * Maps a file (name / mime / size / URL) to a {@link ViewerFile} — the single source of truth for \"which\n * in-browser viewer renders this\", shared by:\n * - {@link FileChip} (the chip in a conversation): routes images/videos to the lightbox itself, then\n * delegates everything else here.\n * - the archive browsers (Zip / 7z / tar / rar): an entry, once extracted to a blob URL, is classified the\n * same way and handed to {@link FileViewerBody} — so a file INSIDE an archive previews with the very same\n * viewers as a top-level file (the previous archive detail view only handled text/image/office/dicom).\n *\n * Detection order mirrors FileChip's historical priority exactly (raster/ebook beat the image lightbox; the\n * textual specializations — har/subtitle/geo/csv/notebook/cert — beat the generic source route). The only\n * additions for the archive context are `image` / `video` (rendered inline in the body instead of the lightbox)\n * and a `binary` fall-through (an entry that matches no viewer and isn't text → a download card).\n */\nimport {\n ebookKind,\n fileExt,\n hljsLang,\n isAppxFile,\n isAudioFile,\n isBinaryObjFile,\n isCertFile,\n isCsvFile,\n isDicomFile,\n isDocxFile,\n isDxfFile,\n isEmlFile,\n isEplFile,\n isFontFile,\n isGeoFile,\n isHarFile,\n isHprofFile,\n isHtmlFile,\n isImageFile,\n isJavaClassFile,\n isJksFile,\n isJsonlFile,\n isLogFile,\n isMhtFile,\n isMindMapFile,\n isModelFile,\n isLrcFile,\n isMsiFile,\n isNotebookFile,\n isPdfFile,\n isQmcAudioFile,\n isPptxFile,\n isRarFile,\n isRtfFile,\n isSevenZipFile,\n isSqliteFile,\n isSubtitleFile,\n isSvgFile,\n isTarballFile,\n isTextFile,\n isVideoFile,\n isXlsxFile,\n isXmindFile,\n isZipFile,\n rasterImageKind,\n} from '@/lib/fileType'\nimport { isDiagramFile } from '@/lib/diagram'\nimport { looksLikeMindMap } from '@/lib/mindmap'\nimport type { ViewerFile } from '@/composables/useFileViewer'\n\nexport interface ClassifyOpts {\n /** Display name (becomes {@link ViewerFile.name}). */\n name: string\n /** Fetch / download URL — the real endpoint, or a `blob:` URL for an extracted archive entry. */\n url: string\n /** Provider mime when known; null for archive entries (extension-driven detection). */\n mime?: string | null\n /** Byte size when known (drives the large-file / log routing). */\n size?: number\n /** Name used for TYPE detection when it differs from the display name (e.g. a chip captioned \"16×16 PNG\"). */\n detectName?: string\n /** Fall-back hint when nothing matches and the extension is ambiguous (archive NUL-sniff says \"looks textual\"). */\n textHint?: boolean\n}\n\n/**\n * Classify a file into the {@link ViewerFile} the side viewer should render. Pure — no lightbox / fetching\n * side effects (FileChip handles the lightbox before calling this). Never returns image/video for the main\n * chip flow (FileChip routes those itself); archive callers DO get image/video/binary.\n */\nexport function classifyViewerFile(o: ClassifyOpts): ViewerFile {\n const name = o.name\n const dn = o.detectName ?? o.name\n const mime = o.mime ?? null\n const size = o.size\n const base = { name, url: o.url, size }\n\n // Decoding images (TIFF/PSD/HEIC/RAW) and e-books carry image/* mimes, so they must beat the image route.\n const raster = rasterImageKind(dn, mime)\n if (raster) return { ...base, lang: '', raster }\n const eb = ebookKind(dn, mime)\n if (eb) return { ...base, lang: '', ebook: eb }\n // Diagrams (.drawio/.excalidraw) are XML/JSON under the hood — render as SVG, with the raw text on the source\n // toggle (lang feeds that highlight: xml for drawio, json for excalidraw).\n const diagram = isDiagramFile(dn, mime)\n if (diagram) return { ...base, lang: diagram === 'excalidraw' ? 'json' : 'xml', diagram }\n // image / video render inline in the body (archive context); the main FileChip flow never reaches here for\n // those (it opens the lightbox first), so this only fires for archive entries.\n if (isImageFile(dn, mime)) return { ...base, lang: '', image: true }\n if (isVideoFile(dn, mime)) return { ...base, lang: '', video: true }\n // APPX/MSIX are zip containers, so they MUST be claimed before the generic zip route; MSI is an OLE\n // compound file (not a zip), grouped here with the other Windows installer/package formats.\n if (isAppxFile(dn, mime)) return { ...base, lang: '', appx: true }\n if (isMsiFile(dn, mime)) return { ...base, lang: '', msi: true }\n // XMind is a zip container → claim it before the generic zip route (renders as a mind map, not an archive).\n if (isXmindFile(dn, mime)) return { ...base, lang: '', xmind: true }\n if (isZipFile(dn, mime)) return { ...base, lang: '', zip: true }\n if (isSevenZipFile(dn, mime)) return { ...base, lang: '', sevenZip: true }\n if (isTarballFile(dn, mime)) return { ...base, lang: '', archive: 'tar' }\n if (isRarFile(dn, mime)) return { ...base, lang: '', archive: 'rar' }\n if (isDicomFile(dn, mime)) return { ...base, lang: '', dicom: true }\n if (isBinaryObjFile(dn, mime)) return { ...base, lang: '', binobj: true }\n if (isEplFile(dn, mime)) return { ...base, lang: '', epl: true }\n if (isJavaClassFile(dn, mime)) return { ...base, lang: '', javaClass: true }\n if (isHprofFile(dn, mime)) return { ...base, lang: '', hprof: true }\n if (isDocxFile(dn, mime)) return { ...base, lang: '', docx: true }\n if (isXlsxFile(dn, mime)) return { ...base, lang: '', xlsx: true }\n if (isPptxFile(dn, mime)) return { ...base, lang: '', pptx: true }\n if (isRtfFile(dn, mime)) return { ...base, lang: '', rtf: true }\n if (isPdfFile(dn, mime)) return { ...base, lang: '', pdf: true }\n if (isMindMapFile(dn, mime)) return { ...base, lang: hljsLang(dn, mime), mindmap: true }\n // DXF is textual (ASCII CAD) → claim it before the generic text route so it renders as a drawing.\n if (isDxfFile(dn, mime)) return { ...base, lang: '', dxf: true }\n if (isMhtFile(dn, mime)) return { ...base, lang: '', mht: true }\n if (isEmlFile(dn, mime)) return { ...base, lang: '', eml: true }\n if (isHarFile(dn, mime)) return { ...base, lang: '', har: true }\n if (isSqliteFile(dn, mime)) return { ...base, lang: '', sqlite: true }\n if (isSubtitleFile(dn, mime)) return { ...base, lang: '', subtitle: true }\n if (isGeoFile(dn, mime)) return { ...base, lang: hljsLang(dn, mime), geo: true }\n // QQ Music encrypted audio (.mflac/.mgg/…) → decrypt + play; LRC lyrics (.lrc) → synced lyric list. LRC must\n // beat the generic text route (its viewer self-fetches so it can sniff the usual GBK encoding).\n if (isQmcAudioFile(dn, mime)) return { ...base, lang: '', qmc: true }\n if (isLrcFile(dn, mime)) return { ...base, lang: '', lrc: true }\n if (isAudioFile(dn, mime)) return { ...base, lang: '', audio: true }\n // Logs / large text before the generic text + csv routes (a huge CSV streams through the log viewer).\n if (isLogFile(dn, mime, size)) return { ...base, lang: '', log: true }\n if (isCsvFile(dn, mime)) return { ...base, lang: '', csv: true }\n if (isJsonlFile(dn, mime)) return { ...base, lang: 'json', jsonl: true }\n if (isModelFile(dn, mime)) return { ...base, lang: '', model: true }\n if (isFontFile(dn, mime)) return { ...base, lang: '', font: true }\n if (isNotebookFile(dn, mime)) return { ...base, lang: '', ipynb: true }\n // Keystores (JKS/JCEKS + PKCS#12) before the cert route — the viewer sniffs the bytes to pick the parser.\n if (isJksFile(dn, mime)) return { ...base, lang: '', jks: true }\n if (isCertFile(dn, mime)) return { ...base, lang: '', cert: true }\n // SVG / HTML get a render/source toggle; plain text/code falls to the source view.\n if (isSvgFile(dn, mime)) return { ...base, lang: hljsLang(dn, mime), svg: true }\n if (isHtmlFile(dn, mime)) return { ...base, lang: hljsLang(dn, mime), html: true }\n if (isTextFile(dn, mime) || o.textHint) return { ...base, lang: hljsLang(dn, mime) }\n // Nothing matched and it isn't text → an unpreviewable binary (archive entries only; FileChip never opens these).\n return { ...base, lang: '', binary: true }\n}\n\n// Common image/video/audio MIME by extension — used when an archive browser wraps extracted entry bytes in a\n// Blob URL, so an <img>/<video>/<audio> reliably decodes it (an untyped blob makes the browser guess). Returns\n// '' for non-media (text/office/etc. sub-viewers fetch the bytes themselves, where the blob type is irrelevant).\nconst MEDIA_MIME: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n jfif: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n ico: 'image/x-icon',\n avif: 'image/avif',\n apng: 'image/apng',\n svg: 'image/svg+xml',\n mp4: 'video/mp4',\n m4v: 'video/mp4',\n webm: 'video/webm',\n ogv: 'video/ogg',\n mov: 'video/quicktime',\n mkv: 'video/x-matroska',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n flac: 'audio/flac',\n ogg: 'audio/ogg',\n oga: 'audio/ogg',\n m4a: 'audio/mp4',\n aac: 'audio/aac',\n opus: 'audio/opus',\n}\n\n/** Best-effort media MIME for an extracted archive entry's Blob (see {@link MEDIA_MIME}); '' when not media. */\nexport function mediaMimeForName(name: string): string {\n return MEDIA_MIME[fileExt(name)] ?? ''\n}\n\n/**\n * Does this file render WITHOUT the panel-loaded text — i.e. a self-fetching sub-viewer (it pulls its own\n * bytes), or an image/video/binary (rendered straight from the URL)? When true the panel skips the text\n * fetch and hides the copy/refresh actions; when false the content is built from fetched text (markdown /\n * html / svg / subtitle / geo / csv / mind-map / source) or inline `content`.\n */\nexport function skipsTextLoad(file: ViewerFile | null): boolean {\n if (!file) return false\n return !!(\n file.zip ||\n file.sevenZip ||\n file.xmind ||\n file.dxf ||\n file.archive ||\n file.binobj ||\n file.epl ||\n file.javaClass ||\n file.hprof ||\n file.msi ||\n file.appx ||\n file.dicom ||\n file.docx ||\n file.xlsx ||\n file.pptx ||\n file.rtf ||\n file.pdf ||\n file.eml ||\n file.mht ||\n file.har ||\n file.sqlite ||\n file.cert ||\n file.jks ||\n file.font ||\n file.ipynb ||\n file.audio ||\n file.qmc ||\n file.lrc ||\n file.log ||\n file.model ||\n file.raster ||\n file.ebook ||\n file.image ||\n file.video ||\n file.binary ||\n file.hex\n )\n}\n\n/**\n * Can this file be shown both rendered and as source (so the header offers an eye/code toggle)? True for\n * markdown / html / svg / subtitle / geo / csv, and for a mind map only once the loaded text actually looks\n * like one (a non-mind-map `.mm` reads as plain source, no toggle).\n */\nexport function canToggleView(file: ViewerFile | null, text: string): boolean {\n if (!file) return false\n if (\n file.lang === 'markdown' ||\n file.html === true ||\n file.svg === true ||\n file.subtitle === true ||\n file.geo === true ||\n file.csv === true ||\n file.jsonl === true ||\n file.diagram != null\n ) {\n return true\n }\n if (file.mindmap === true) return looksLikeMindMap(text, fileExt(file.name))\n return false\n}\n"],"mappings":"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"}
package/dist/lib/x509.js CHANGED
@@ -265,3 +265,5 @@ async function O(e) {
265
265
  }
266
266
  //#endregion
267
267
  export { D as describePrivateKey, u as looksLikePem, s as oidName, C as parseCertificate, w as parseCsr, l as parsePemBlocks, O as sha256Hex, T as summarizeBlock };
268
+
269
+ //# sourceMappingURL=x509.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x509.js","names":[],"sources":["../../src/lib/x509.ts"],"sourcesContent":["/**\n * Zero-dependency X.509 / PKCS#10 reading on top of the ASN.1 decoder (lib/asn1.ts), for the certificate\n * viewer (CertViewer.vue). It extracts the human-meaningful fields — subject / issuer, validity, serial,\n * signature + public-key algorithm, SANs, key usages, CA flag — from a DER certificate or CSR, and splits a\n * PEM file into its blocks. This is a READ-ONLY visualizer: it does NOT verify signatures or trust chains, so\n * a malformed or untrusted file is shown, not rejected. Anything it can't map to a friendly field still shows\n * in the raw ASN.1 structure tree the viewer renders alongside.\n */\n\nimport {\n asn1String,\n asn1Time,\n bytesToHex,\n decodeAsn1,\n oidToString,\n TAG,\n type Asn1Node,\n} from './asn1'\n\n// OID → short name. Covers the attribute types, algorithms, curves and extensions seen on real certificates;\n// unknown OIDs fall back to their dotted form (see oidName).\nconst OID_NAMES: Record<string, string> = {\n // Distinguished-name attribute types\n '2.5.4.3': 'CN',\n '2.5.4.4': 'SN',\n '2.5.4.5': 'serialNumber',\n '2.5.4.6': 'C',\n '2.5.4.7': 'L',\n '2.5.4.8': 'ST',\n '2.5.4.9': 'street',\n '2.5.4.10': 'O',\n '2.5.4.11': 'OU',\n '2.5.4.12': 'title',\n '2.5.4.42': 'GN',\n '2.5.4.43': 'initials',\n '1.2.840.113549.1.9.1': 'E',\n '0.9.2342.19200300.100.1.25': 'DC',\n '0.9.2342.19200300.100.1.1': 'UID',\n // Signature / public-key algorithms\n '1.2.840.113549.1.1.1': 'RSA',\n '1.2.840.113549.1.1.5': 'sha1WithRSA',\n '1.2.840.113549.1.1.11': 'sha256WithRSA',\n '1.2.840.113549.1.1.12': 'sha384WithRSA',\n '1.2.840.113549.1.1.13': 'sha512WithRSA',\n '1.2.840.113549.1.1.10': 'RSASSA-PSS',\n '1.2.840.10045.2.1': 'EC',\n '1.2.840.10045.4.3.2': 'ecdsa-with-SHA256',\n '1.2.840.10045.4.3.3': 'ecdsa-with-SHA384',\n '1.2.840.10045.4.3.4': 'ecdsa-with-SHA512',\n '1.3.101.112': 'Ed25519',\n '1.3.101.113': 'Ed448',\n // EC named curves\n '1.2.840.10045.3.1.7': 'P-256 (secp256r1)',\n '1.3.132.0.34': 'P-384 (secp384r1)',\n '1.3.132.0.35': 'P-521 (secp521r1)',\n '1.3.132.0.10': 'secp256k1',\n // Certificate extensions\n '2.5.29.14': 'subjectKeyIdentifier',\n '2.5.29.15': 'keyUsage',\n '2.5.29.17': 'subjectAltName',\n '2.5.29.19': 'basicConstraints',\n '2.5.29.31': 'cRLDistributionPoints',\n '2.5.29.32': 'certificatePolicies',\n '2.5.29.35': 'authorityKeyIdentifier',\n '2.5.29.37': 'extKeyUsage',\n '1.3.6.1.5.5.7.1.1': 'authorityInfoAccess',\n // Extended key usages\n '1.3.6.1.5.5.7.3.1': 'serverAuth',\n '1.3.6.1.5.5.7.3.2': 'clientAuth',\n '1.3.6.1.5.5.7.3.3': 'codeSigning',\n '1.3.6.1.5.5.7.3.4': 'emailProtection',\n '1.3.6.1.5.5.7.3.8': 'timeStamping',\n}\n\nexport function oidName(oid: string): string {\n return OID_NAMES[oid] ?? oid\n}\n\nexport interface RDN {\n oid: string\n name: string\n value: string\n}\n\nexport type CertKind = 'certificate' | 'request' | 'key' | 'crl' | 'pkcs12' | 'pkcs7' | 'unknown'\n\nexport interface CertSummary {\n ok: boolean\n kind: CertKind\n label: string\n subject: RDN[]\n issuer: RDN[]\n subjectCN?: string\n issuerCN?: string\n serialHex?: string\n version?: number\n sigAlg?: string\n pubKeyAlg?: string\n pubKeyBits?: number\n pubKeyCurve?: string\n notBefore?: string\n notAfter?: string\n notBeforeMs?: number\n notAfterMs?: number\n sans: string[]\n keyUsages: string[]\n extKeyUsages: string[]\n isCa?: boolean\n pathLen?: number\n der: Uint8Array\n fingerprintSha256?: string // hex, filled in asynchronously by the viewer\n note?: string\n}\n\nexport interface PemBlock {\n label: string\n der: Uint8Array\n}\n\nfunction b64ToBytes(b64: string): Uint8Array {\n const bin = atob(b64)\n const out = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i)\n return out\n}\n\n/** Split a PEM document into its `-----BEGIN X-----`…`-----END X-----` blocks (base64 → DER bytes). */\nexport function parsePemBlocks(text: string): PemBlock[] {\n const blocks: PemBlock[] = []\n const re = /-----BEGIN ([^-]+)-----([\\s\\S]*?)-----END \\1-----/g\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const b64 = m[2].replace(/[^A-Za-z0-9+/=]/g, '')\n if (!b64) continue\n try {\n blocks.push({ label: m[1].trim(), der: b64ToBytes(b64) })\n } catch {\n /* malformed base64 — skip this block */\n }\n }\n return blocks\n}\n\n/** Does this text look like a PEM document (has at least one BEGIN marker)? */\nexport function looksLikePem(text: string): boolean {\n return /-----BEGIN [^-]+-----/.test(text)\n}\n\nfunction emptySummary(label: string, kind: CertKind, der: Uint8Array): CertSummary {\n return {\n ok: false,\n kind,\n label,\n subject: [],\n issuer: [],\n sans: [],\n keyUsages: [],\n extKeyUsages: [],\n der,\n }\n}\n\nfunction intValue(node: Asn1Node): number {\n let v = 0\n for (const b of node.content) v = v * 256 + b\n return v\n}\n\nfunction stripLeadingZero(bytes: Uint8Array): Uint8Array {\n return bytes.length > 1 && bytes[0] === 0 ? bytes.subarray(1) : bytes\n}\n\nfunction cnOf(rdns: RDN[]): string | undefined {\n return rdns.find((r) => r.name === 'CN')?.value\n}\n\nfunction algName(algId: Asn1Node | undefined): string {\n const oidNode = algId?.children[0]\n return oidNode ? oidName(oidToString(oidNode.content)) : ''\n}\n\n// Name ::= SEQUENCE OF RelativeDistinguishedName (SET OF AttributeTypeAndValue { type OID, value ANY }).\nfunction parseName(name: Asn1Node | undefined): RDN[] {\n const out: RDN[] = []\n if (!name) return out\n for (const rdn of name.children) {\n for (const atv of rdn.children) {\n const oidNode = atv.children[0]\n const valNode = atv.children[1]\n if (!oidNode || !valNode) continue\n const oid = oidToString(oidNode.content)\n out.push({ oid, name: oidName(oid), value: asn1String(valNode) })\n }\n }\n return out\n}\n\n// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }.\nfunction applyPubKey(summary: CertSummary, spki: Asn1Node | undefined): void {\n const algId = spki?.children[0]\n const bitStr = spki?.children[1]\n const algOidNode = algId?.children[0]\n if (!algOidNode) return\n const algOid = oidToString(algOidNode.content)\n summary.pubKeyAlg = oidName(algOid)\n if (algOid === '1.2.840.113549.1.1.1' && bitStr) {\n // RSA: BIT STRING = [unusedBits byte][DER RSAPublicKey SEQUENCE { modulus INTEGER, exponent INTEGER }].\n const seq = decodeAsn1(bitStr.content.subarray(1))[0]\n const modulus = seq?.children[0]\n if (modulus) summary.pubKeyBits = stripLeadingZero(modulus.content).length * 8\n } else if (algOid === '1.2.840.10045.2.1') {\n // EC: AlgorithmIdentifier.parameters is the named-curve OID.\n const params = algId?.children[1]\n if (params) {\n summary.pubKeyCurve = oidName(oidToString(params.content))\n const m = summary.pubKeyCurve.match(/(\\d{3})/)\n if (m) summary.pubKeyBits = parseInt(m[1], 10)\n }\n } else if (algOid === '1.3.101.112') {\n summary.pubKeyBits = 256 // Ed25519\n }\n}\n\nconst KEY_USAGE = [\n 'digitalSignature',\n 'nonRepudiation',\n 'keyEncipherment',\n 'dataEncipherment',\n 'keyAgreement',\n 'keyCertSign',\n 'cRLSign',\n 'encipherOnly',\n 'decipherOnly',\n]\n\nfunction parseKeyUsage(bitStr: Asn1Node): string[] {\n const content = bitStr.content\n if (content.length < 2) return []\n const bits = content.subarray(1)\n const total = bits.length * 8 - content[0]\n const out: string[] = []\n for (let i = 0; i < total && i < KEY_USAGE.length; i++) {\n if (bits[Math.floor(i / 8)] & (0x80 >> (i % 8))) out.push(KEY_USAGE[i])\n }\n return out\n}\n\nfunction ipFromBytes(b: Uint8Array): string {\n if (b.length === 4) return Array.from(b).join('.')\n if (b.length === 16) {\n const parts: string[] = []\n for (let i = 0; i < 16; i += 2) parts.push(((b[i] << 8) | b[i + 1]).toString(16))\n return parts.join(':')\n }\n return bytesToHex(b)\n}\n\n// SubjectAltName ::= SEQUENCE OF GeneralName (context-tagged: [1]email [2]DNS [6]URI [7]IP, the common ones).\nfunction parseSan(seq: Asn1Node): string[] {\n const out: string[] = []\n for (const gn of seq.children) {\n if (gn.tagClass !== 2) continue\n const txt = new TextDecoder('latin1').decode(gn.content)\n if (gn.tagNumber === 2) out.push(`DNS:${txt}`)\n else if (gn.tagNumber === 1) out.push(`email:${txt}`)\n else if (gn.tagNumber === 6) out.push(`URI:${txt}`)\n else if (gn.tagNumber === 7) out.push(`IP:${ipFromBytes(gn.content)}`)\n }\n return out\n}\n\nfunction applyExtensions(summary: CertSummary, extSeq: Asn1Node): void {\n for (const ext of extSeq.children) {\n const oidNode = ext.children[0]\n if (!oidNode) continue\n const oid = oidToString(oidNode.content)\n const valNode = ext.children[ext.children.length - 1] // extnValue OCTET STRING (after optional critical)\n const inner = valNode ? decodeAsn1(valNode.content)[0] : undefined\n if (!inner) continue\n if (oid === '2.5.29.17') {\n summary.sans = parseSan(inner)\n } else if (oid === '2.5.29.19') {\n const ca = inner.children.find((c) => c.tagNumber === TAG.BOOLEAN)\n summary.isCa = ca ? ca.content[0] !== 0 : false\n const pl = inner.children.find((c) => c.tagNumber === TAG.INTEGER)\n if (pl) summary.pathLen = intValue(pl)\n } else if (oid === '2.5.29.37') {\n for (const eku of inner.children) summary.extKeyUsages.push(oidName(oidToString(eku.content)))\n } else if (oid === '2.5.29.15') {\n summary.keyUsages = parseKeyUsage(inner)\n }\n }\n}\n\n/** Parse a DER X.509 Certificate into its friendly fields (best effort; `ok=false` + `note` on failure). */\nexport function parseCertificate(der: Uint8Array, label = 'CERTIFICATE'): CertSummary {\n const summary = emptySummary(label, 'certificate', der)\n try {\n const top = decodeAsn1(der)[0]\n const tbs = top?.children[0]\n if (!top || top.tagNumber !== TAG.SEQUENCE || !tbs) {\n summary.note = '不是有效的 X.509 证书结构'\n return summary\n }\n const c = tbs.children\n let idx = 0\n if (c[idx] && c[idx].tagClass === 2 && c[idx].tagNumber === 0) {\n const vint = c[idx].children[0]\n summary.version = vint ? intValue(vint) + 1 : 1\n idx++\n } else {\n summary.version = 1\n }\n if (c[idx]) summary.serialHex = bytesToHex(stripLeadingZero(c[idx++].content), ':')\n if (c[idx]) summary.sigAlg = algName(c[idx++])\n if (c[idx]) summary.issuer = parseName(c[idx++])\n if (c[idx]) {\n const nb = c[idx].children[0] && asn1Time(c[idx].children[0])\n const na = c[idx].children[1] && asn1Time(c[idx].children[1])\n if (nb) {\n summary.notBefore = nb.text\n summary.notBeforeMs = nb.ms\n }\n if (na) {\n summary.notAfter = na.text\n summary.notAfterMs = na.ms\n }\n idx++\n }\n if (c[idx]) summary.subject = parseName(c[idx++])\n if (c[idx]) applyPubKey(summary, c[idx++])\n const extWrap = c.find((n) => n.tagClass === 2 && n.tagNumber === 3)\n if (extWrap && extWrap.children[0]) applyExtensions(summary, extWrap.children[0])\n summary.subjectCN = cnOf(summary.subject)\n summary.issuerCN = cnOf(summary.issuer)\n summary.ok = true\n } catch (e) {\n summary.note = e instanceof Error ? e.message : String(e)\n }\n return summary\n}\n\n/** Parse a DER PKCS#10 CertificationRequest (CSR): subject + public key (best effort). */\nexport function parseCsr(der: Uint8Array, label = 'CERTIFICATE REQUEST'): CertSummary {\n const summary = emptySummary(label, 'request', der)\n try {\n const cri = decodeAsn1(der)[0]?.children[0]\n if (!cri) {\n summary.note = '不是有效的 PKCS#10 请求结构'\n return summary\n }\n // CertificationRequestInfo ::= SEQUENCE { version, subject Name, subjectPKInfo, attributes [0] }\n summary.subject = parseName(cri.children[1])\n applyPubKey(summary, cri.children[2])\n summary.subjectCN = cnOf(summary.subject)\n summary.ok = true\n } catch (e) {\n summary.note = e instanceof Error ? e.message : String(e)\n }\n return summary\n}\n\n/** Pick a parser from a PEM block label (CERTIFICATE / CERTIFICATE REQUEST / *KEY* / *CRL* / fallback). */\nexport function summarizeBlock(label: string, der: Uint8Array): CertSummary {\n const up = label.toUpperCase()\n if (up.includes('CERTIFICATE REQUEST')) return parseCsr(der, label)\n if (up.includes('CERTIFICATE')) return parseCertificate(der, label)\n if (up.includes('CRL')) {\n const s = emptySummary(label, 'crl', der)\n s.ok = true\n s.note = 'X.509 证书吊销列表(CRL)—— 展开下方结构树查看条目。'\n return s\n }\n if (up.includes('KEY')) {\n const s = emptySummary(label, 'key', der)\n s.ok = true\n // PKCS#8 PrivateKeyInfo / SubjectPublicKeyInfo both start with an AlgorithmIdentifier we can name.\n try {\n const top = decodeAsn1(der)[0]\n const algId = top?.children.find((n) => n.tagNumber === TAG.SEQUENCE)\n summarizeKeyAlg(s, algId)\n } catch {\n /* leave the algorithm unset */\n }\n s.note = up.includes('PUBLIC')\n ? '公钥文件 —— 仅展示算法与结构,不含私密材料。'\n : '私钥文件 —— 仅展示算法与结构,密钥材料请勿外泄。'\n return s\n }\n // Unknown label — try a certificate, else just mark the structure as inspectable.\n const cert = parseCertificate(der, label)\n if (cert.ok) return cert\n const s = emptySummary(label, 'unknown', der)\n s.ok = true\n return s\n}\n\nfunction summarizeKeyAlg(s: CertSummary, algId: Asn1Node | undefined): void {\n const oidNode = algId?.children[0]\n if (oidNode && oidNode.tagNumber === TAG.OID) s.pubKeyAlg = oidName(oidToString(oidNode.content))\n}\n\n/**\n * Best-effort algorithm / size read from a DER PKCS#8 PrivateKeyInfo (never throws) — shared by the keystore\n * viewers (lib/jks.ts, lib/pkcs12.ts) to label a decrypted private key. Returns the friendly algorithm name\n * plus, for RSA, the modulus bit-length, and for EC, the named curve (+ its bit size when the name encodes it).\n */\nexport function describePrivateKey(pkcs8: Uint8Array): {\n algorithm: string\n bits?: number\n curve?: string\n} {\n try {\n // PrivateKeyInfo ::= SEQUENCE { version INTEGER, privateKeyAlgorithm AlgorithmIdentifier, privateKey OCTET STRING }\n const top = decodeAsn1(pkcs8)[0]\n const algId = top?.children[1]\n const oidNode = algId?.children[0]\n const oid = oidNode ? oidToString(oidNode.content) : ''\n const res: { algorithm: string; bits?: number; curve?: string } = {\n algorithm: oid ? oidName(oid) : '未知',\n }\n if (oid === '1.2.840.113549.1.1.1') {\n // RSA: privateKey OCTET STRING wraps an RSAPrivateKey SEQUENCE { version, modulus INTEGER, ... }.\n const pk = top?.children[2]\n const rsa = pk ? decodeAsn1(pk.content)[0] : undefined\n const modulus = rsa?.children[1]\n if (modulus) res.bits = stripLeadingZero(modulus.content).length * 8\n } else if (oid === '1.2.840.10045.2.1') {\n // EC: the named curve is the algorithm parameters OID.\n const params = algId?.children[1]\n if (params && params.tagNumber === TAG.OID) {\n res.curve = oidName(oidToString(params.content))\n const m = res.curve.match(/(\\d{3})/)\n if (m) res.bits = parseInt(m[1], 10)\n }\n }\n return res\n } catch {\n return { algorithm: '未知' }\n }\n}\n\n/** SHA-256 fingerprint of DER bytes, as colon-separated hex (e.g. `AB:CD:…`). */\nexport async function sha256Hex(der: Uint8Array): Promise<string> {\n // Copy into a fresh ArrayBuffer so a subarray view's offset doesn't confuse digest.\n const copy = der.slice()\n const digest = await crypto.subtle.digest('SHA-256', copy)\n return bytesToHex(new Uint8Array(digest), ':').toUpperCase()\n}\n"],"mappings":"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"}
package/dist/lib/xlsx.js CHANGED
@@ -319,3 +319,5 @@ async function F(t) {
319
319
  }
320
320
  //#endregion
321
321
  export { o as numToCol, F as parseXlsx };
322
+
323
+ //# sourceMappingURL=xlsx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xlsx.js","names":[],"sources":["../../src/lib/xlsx.ts"],"sourcesContent":["/**\n * In-browser `.xlsx` (and `.xlsm`) parsing for the side viewer's spreadsheet preview.\n *\n * exceljs does the OOXML heavy lifting (unzip + parse the SpreadsheetML); here we flatten one of its\n * Workbooks into a small, serializable model the {@link XlsxViewer} renders as plain HTML `<table>`s —\n * one table per worksheet, with merged cells, column widths, and the common cell styling (bold/italic/\n * color/fill/alignment/borders) carried over so the preview reads like the original sheet.\n *\n * exceljs is heavy (~1MB), so this module is only ever imported by the async-loaded XlsxViewer — it never\n * lands in the main bundle. We import the namespace (not named exports) because exceljs's browser entry is\n * a UMD bundle: `new ExcelJS.Workbook()` resolves identically under Vite's CJS interop and the .d.ts.\n */\nimport * as ExcelJS from 'exceljs'\nimport { format as nfFormat, isDateFormat as nfIsDateFormat } from 'numfmt'\n\n/** One rendered cell. A cell covered by a merge is dropped from the row entirely (see {@link XlsxRow}). */\nexport interface XlsxCell {\n text: string\n rowSpan: number\n colSpan: number\n /** 1-based column index of this cell's left edge — lets the renderer place cells after merge gaps. */\n col: number\n bold?: boolean\n italic?: boolean\n underline?: boolean\n strike?: boolean\n /** Font size in px (converted from the workbook's points), or undefined to use the table default. */\n fontSize?: number\n /** The cell's formula expression WITHOUT a leading `=` (e.g. `数据基表!B10`, `#REF!`), for the source view. */\n formula?: string\n /** CSS color for the text / the cell background, derived from the workbook's ARGB colors. */\n color?: string\n bg?: string\n align?: 'left' | 'center' | 'right'\n valign?: 'top' | 'middle' | 'bottom'\n wrap?: boolean\n /** Which borders the cell draws (true = a visible edge in the source). */\n bt?: boolean\n br?: boolean\n bb?: boolean\n bl?: boolean\n}\n\nexport interface XlsxRow {\n /** 1-based row number in the worksheet (so the viewer can label the gutter and key frozen/hidden state). */\n num: number\n /** Row height in px (converted from Excel points), or undefined to size to content. */\n height?: number\n /** Hidden in the source (\"hide rows\") — the viewer collapses it behind an expander, like Excel/WPS. */\n hidden?: boolean\n /** Only the cells that actually render — merge-covered slots are omitted; use {@link XlsxCell.col}. */\n cells: XlsxCell[]\n}\n\nexport interface XlsxSheet {\n name: string\n /** Per-column width in px (index 0 = column A); columns past this use a default width. */\n colWidths: number[]\n /** Per-column hidden flag (index 0 = column A) — collapsed behind an expander, like Excel/WPS. */\n colHidden: boolean[]\n rows: XlsxRow[]\n cols: number\n /** Frozen pane sizes (Excel \"freeze panes\"): the first N rows / M columns stay pinned while scrolling. */\n frozenRows: number\n frozenCols: number\n}\n\nexport interface XlsxModel {\n sheets: XlsxSheet[]\n}\n\n// Guardrails: a corrupt or absurdly large sheet shouldn't lock up the tab. We cap the rendered range and\n// let the viewer note the truncation. (Real attachments are tiny; these only bite pathological files.)\nconst MAX_ROWS = 2000\nconst MAX_COLS = 200\n\n// Excel column-letter (A, B, …, AA) → 1-based index.\nfunction colToNum(s: string): number {\n let n = 0\n for (let i = 0; i < s.length; i++) n = n * 26 + (s.charCodeAt(i) - 64)\n return n\n}\n\n// 1-based index → Excel column-letter (1→A, 26→Z, 27→AA). Used by the viewer's A/B/C… header row.\nexport function numToCol(n: number): string {\n let s = ''\n while (n > 0) {\n const m = (n - 1) % 26\n s = String.fromCharCode(65 + m) + s\n n = Math.floor((n - 1) / 26)\n }\n return s\n}\n\ninterface Span {\n rowSpan: number\n colSpan: number\n}\n\n// Parse exceljs's merge ranges (\"A1:B2\") into: span info keyed at each master cell, and a set of the\n// addresses (`r,c`) covered by a merge (which we then skip rendering).\nfunction parseMerges(merges: string[]): { spans: Map<string, Span>; covered: Set<string> } {\n const spans = new Map<string, Span>()\n const covered = new Set<string>()\n for (const range of merges) {\n const m = /^([A-Z]+)(\\d+):([A-Z]+)(\\d+)$/.exec(range)\n if (!m) continue\n const c1 = colToNum(m[1])\n const r1 = parseInt(m[2], 10)\n const c2 = colToNum(m[3])\n const r2 = parseInt(m[4], 10)\n const top = Math.min(r1, r2)\n const bottom = Math.max(r1, r2)\n const left = Math.min(c1, c2)\n const right = Math.max(c1, c2)\n spans.set(`${top},${left}`, { rowSpan: bottom - top + 1, colSpan: right - left + 1 })\n for (let r = top; r <= bottom; r++) {\n for (let c = left; c <= right; c++) {\n if (r === top && c === left) continue\n covered.add(`${r},${c}`)\n }\n }\n }\n return { spans, covered }\n}\n\n// Excel's legacy 56-colour indexed palette (BIFF8). Cells/fonts that don't carry an explicit ARGB reference\n// a palette slot instead (e.g. `{ indexed: 47 }`) — very common in older workbooks. 64/65 are the system\n// foreground/background (\"automatic\"), handled specially below.\n// prettier-ignore\nconst INDEXED_COLORS = [\n '000000', 'FFFFFF', 'FF0000', '00FF00', '0000FF', 'FFFF00', 'FF00FF', '00FFFF',\n '000000', 'FFFFFF', 'FF0000', '00FF00', '0000FF', 'FFFF00', 'FF00FF', '00FFFF',\n '800000', '008000', '000080', '808000', '800080', '008080', 'C0C0C0', '808080',\n '9999FF', '993366', 'FFFFCC', 'CCFFFF', '660066', 'FF8080', '0066CC', 'CCCCFF',\n '000080', 'FF00FF', 'FFFF00', '00FFFF', '800080', '800000', '008080', '0000FF',\n '00CCFF', 'CCFFFF', 'CCFFCC', 'FFFF99', '99CCFF', 'FF99CC', 'CC99FF', 'FFCC99',\n '3366FF', '33CCCC', '99CC00', 'FFCC00', 'FF9900', 'FF6600', '666699', '969696',\n '003366', '339966', '003300', '333300', '993300', '993366', '333399', '333333',\n]\n\n// Default Office theme colours, in the order the styles `theme` attribute indexes them (0=bg1/white,\n// 1=text1/black, 2=bg2, 3=text2, 4..9=accent1..6, 10=hlink, 11=folHlink). We don't parse the workbook's\n// actual theme XML (exceljs doesn't surface it), so this default scheme is a best-effort — but it gets the\n// common cases right (notably theme 1 = black text, theme 0 = white) and `tint` is applied on top.\n// prettier-ignore\nconst THEME_COLORS = [\n 'FFFFFF', '000000', 'E7E6E6', '44546A', '4472C4', 'ED7D31',\n 'A5A5A5', 'FFC000', '5B9BD5', '70AD47', '0563C1', '954F72',\n]\n\nfunction rgbToHsl(r: number, g: number, b: number): [number, number, number] {\n r /= 255\n g /= 255\n b /= 255\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n let h = 0\n let s = 0\n const l = (max + min) / 2\n if (max !== min) {\n const d = max - min\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min)\n if (max === r) h = (g - b) / d + (g < b ? 6 : 0)\n else if (max === g) h = (b - r) / d + 2\n else h = (r - g) / d + 4\n h /= 6\n }\n return [h, s, l]\n}\n\nfunction hue2rgb(p: number, q: number, t: number): number {\n if (t < 0) t += 1\n if (t > 1) t -= 1\n if (t < 1 / 6) return p + (q - p) * 6 * t\n if (t < 1 / 2) return q\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6\n return p\n}\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n if (s === 0) {\n const v = Math.round(l * 255)\n return [v, v, v]\n }\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s\n const p = 2 * l - q\n return [\n Math.round(hue2rgb(p, q, h + 1 / 3) * 255),\n Math.round(hue2rgb(p, q, h) * 255),\n Math.round(hue2rgb(p, q, h - 1 / 3) * 255),\n ]\n}\n\n// Excel applies `tint` to a theme/indexed colour by shifting its HSL luminance: negative darkens, positive\n// lightens. Mirrors the algorithm in the OOXML spec so shaded theme fills come out the right tone.\nfunction applyTint(hex: string, tint: number): string {\n const r = parseInt(hex.slice(0, 2), 16)\n const g = parseInt(hex.slice(2, 4), 16)\n const b = parseInt(hex.slice(4, 6), 16)\n const [h, s, l0] = rgbToHsl(r, g, b)\n let l = tint < 0 ? l0 * (1 + tint) : l0 * (1 - tint) + tint\n l = Math.max(0, Math.min(1, l))\n const [nr, ng, nb] = hslToRgb(h, s, l)\n const hx = (n: number): string => n.toString(16).padStart(2, '0')\n return hx(nr) + hx(ng) + hx(nb)\n}\n\n/**\n * Resolve any exceljs colour object to a CSS `#RRGGBB`, covering all three references Excel uses: explicit\n * `argb`, the legacy `indexed` palette, and `theme` index (+ optional `tint`). Returns undefined for \"no\n * colour\" — a fully-transparent argb, the system-automatic indices, or an unresolvable shape.\n */\nfunction colorToCss(color: unknown): string | undefined {\n if (!color || typeof color !== 'object') return undefined\n const c = color as { argb?: string; theme?: number; indexed?: number; tint?: number }\n let hex: string | undefined\n if (typeof c.argb === 'string') {\n if (c.argb.length === 8) {\n if (c.argb.slice(0, 2).toLowerCase() === '00') return undefined // fully transparent → no colour\n hex = c.argb.slice(2)\n } else if (c.argb.length === 6) {\n hex = c.argb\n }\n } else if (typeof c.theme === 'number') {\n hex = THEME_COLORS[c.theme]\n } else if (typeof c.indexed === 'number') {\n if (c.indexed === 64) return undefined // system foreground / automatic → let the default apply\n hex = c.indexed === 65 ? 'FFFFFF' : INDEXED_COLORS[c.indexed]\n }\n if (!hex || !/^[0-9a-fA-F]{6}$/.test(hex)) return undefined\n if (typeof c.tint === 'number' && c.tint !== 0) hex = applyTint(hex, c.tint)\n return `#${hex}`\n}\n\n// Excel font sizes are in points; CSS wants px. The conventional screen conversion is pt × 96/72 (= ×4/3),\n// which keeps relative sizing faithful (a 24pt title reads clearly larger than 10pt body text).\nfunction fontSizeToPx(size: number | undefined): number | undefined {\n if (!size || size <= 0) return undefined\n return Math.round((size * 4) / 3)\n}\n\n// Excel column width is in \"character\" units; the conventional pixel conversion to the COLUMN's total width is\n// width*7 + 5 (the +5 is Excel's own internal cell padding). But our HTML cell adds its OWN padding (~4px each\n// side) and a gridline border on top of the `<col>` width, so reusing Excel's total width would leave the\n// content area ~8px short and clip text into \"…\". We add that HTML chrome back (+9 instead of +5) so the\n// usable content width matches Excel's, keeping the cell `padding`/border in sync (see XlsxViewer .xv__cell).\nfunction colWidthToPx(w: number | undefined): number {\n if (!w || w <= 0) return 80\n return Math.round(w * 7 + 9)\n}\n\n// A two-digit zero-pad for the date formatter.\nfunction pad2(n: number): string {\n return n < 10 ? `0${n}` : String(n)\n}\n\n// Render a Date the way a spreadsheet preview should — `YYYY-MM-DD`, plus a `HH:MM[:SS]` tail only when the\n// value carries a time-of-day. (exceljs's own `cell.text` gives the raw JS `Date.toString()`, e.g.\n// \"Sat May 04 2019 08:00:00 GMT+0800 (中国标准时间)\", which is useless in a cell.)\nfunction formatDate(d: Date): string {\n const date = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`\n const h = d.getHours()\n const mi = d.getMinutes()\n const s = d.getSeconds()\n if (!h && !mi && !s) return date\n return `${date} ${pad2(h)}:${pad2(mi)}${s ? `:${pad2(s)}` : ''}`\n}\n\nfunction richTextToString(runs: unknown): string {\n if (!Array.isArray(runs)) return ''\n return runs\n .map((r) => (r && typeof r === 'object' ? ((r as { text?: string }).text ?? '') : ''))\n .join('')\n}\n\n// CJK numeral digits/units for Excel's `[DBNum1]` (中文小写) and `[DBNum2]` (中文大写/financial) — numfmt does\n// not implement these (Excel's 特殊 → 中文数字), so we convert the integer ourselves with proper 万/亿 grouping.\nconst DBNUM_DIGITS = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']\nconst DBNUM_DIGITS_UP = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']\nconst DBNUM_UNITS = ['', '十', '百', '千']\nconst DBNUM_UNITS_UP = ['', '拾', '佰', '仟']\nconst DBNUM_BIG = ['', '万', '亿', '兆']\n\n// Convert one 0..9999 group to CJK numerals, collapsing internal zeros to a single 零 (no trailing zero unit).\nfunction dbNumGroup(n: number, digits: string[], units: string[]): string {\n let s = ''\n let pendingZero = false\n for (let i = 3; i >= 0; i--) {\n const x = Math.floor(n / 10 ** i) % 10\n if (x === 0) {\n if (s !== '') pendingZero = true\n } else {\n if (pendingZero) {\n s += digits[0]\n pendingZero = false\n }\n s += digits[x] + units[i]\n }\n }\n return s\n}\n\n// Excel `[DBNum1]`/`[DBNum2]` of a non-negative integer: e.g. 1234 → 一千二百三十四 / 壹仟贰佰叁拾肆, 10001 → 一万零一,\n// 100000001 → 一亿零一. (Keeps Excel's leading 一十 for 10-19, single 零 between groups, like Excel's own output.)\nfunction toChineseNumeral(n: number, upper: boolean): string {\n const digits = upper ? DBNUM_DIGITS_UP : DBNUM_DIGITS\n const units = upper ? DBNUM_UNITS_UP : DBNUM_UNITS\n if (n === 0) return digits[0]\n const groups: number[] = []\n let x = n\n while (x > 0) {\n groups.push(x % 10000)\n x = Math.floor(x / 10000)\n }\n let s = ''\n for (let i = groups.length - 1; i >= 0; i--) {\n const g = groups[i]\n if (g === 0) continue // an all-zero group: the next non-zero group's leading 零 covers the gap\n let gs = dbNumGroup(g, digits, units)\n if (i < groups.length - 1 && g < 1000) gs = digits[0] + gs // 1,0001 → 一万零一 (zero after a higher group)\n s += gs + DBNUM_BIG[i]\n }\n return s\n}\n\n// Map ASCII digits to full-width — Excel's `[DBNum3]` (全角数字); applied over numfmt's normal output so any\n// separators / decimals survive.\nfunction toFullWidthDigits(s: string): string {\n return s.replace(/[0-9]/g, (d) => '0123456789'[Number(d)])\n}\n\n// Honor Excel's `[DBNumN]` CJK-numeral prefixes that numfmt can't render. Returns null to fall back to numfmt\n// (negatives / non-integers for DBNum1/2, where Excel's behavior is fiddly — a plain number is safer than a guess).\nfunction formatDbNum(value: number, n: number, fmt: string): string | null {\n if (n === 3) {\n try {\n const out = nfFormat(fmt, value) // numfmt strips the [DBNum3] tag and formats the rest\n if (typeof out === 'string') return toFullWidthDigits(out)\n } catch {\n /* fall through */\n }\n return null\n }\n if (Number.isInteger(value) && value >= 0) return toChineseNumeral(value, n === 2)\n return null\n}\n\n// numfmt understands the English \"General\" keyword but not Excel's LOCALIZED one (Simplified Chinese \"G/通用格式\",\n// Japanese \"G/標準\", etc.), throwing \"Invalid pattern\" — normalize those to \"General\" so a locale-tagged General\n// format (common in CJK files after a [DBNum]/[$-…] prefix) parses. (Plain \"[$-NNN]\" locale tags numfmt handles.)\nfunction cleanFmt(fmt: string): string {\n return fmt.replace(/G\\/(通用格式|標準|标准|표준)/g, 'General')\n}\n\n// Apply the cell's Excel number format (`cell.numFmt`) to a numeric value the way Excel DISPLAYS it — exceljs's\n// `cell.text` returns the raw stored number, so an integer-formatted cell (\"0\") shows the stored float\n// \"2.9999999999\" instead of \"3\", a percent format shows \"0.5\" instead of \"50%\", thousands separators go missing,\n// etc. An undefined format → \"General\", which (like Excel) also strips IEEE float noise (0.1+0.2 → \"0.3\"); this is\n// why a cell *referencing* a formatted one rendered wrong too — its cached result is the noisy float. numfmt covers\n// currency / accounting / fraction / scientific / percent / text(@) / 邮编 / custom codes; `[DBNumN]` CJK numerals\n// (which numfmt doesn't implement) are handled here. Unsupported codes / unexpected output fall back to a plain\n// string, never throwing.\nfunction formatNumber(value: number, numFmt: string | undefined): string {\n const fmt = cleanFmt(numFmt || 'General')\n const dbnum = /\\[DBNum([123])\\]/.exec(fmt)\n if (dbnum) {\n const out = formatDbNum(value, Number(dbnum[1]), fmt)\n if (out != null) return out\n }\n try {\n const out = nfFormat(fmt, value)\n return typeof out === 'string' ? out : String(value)\n } catch {\n return String(value)\n }\n}\n\n// Format a date the way Excel's number format dictates — exceljs hands us a JS `Date`, and numfmt formats it\n// directly (reading its local components) for any date/time format code: ISO, 中文 yyyy\"年\"m\"月\"d\"日\", h:mm AM/PM,\n// custom date-times, etc. Only route through numfmt when the format actually IS a date format; otherwise (no/General\n// format) keep the viewer's plain YYYY-MM-DD default from {@link formatDate}.\nfunction formatDateValue(d: Date, numFmt: string | undefined): string {\n if (numFmt && numFmt !== 'General' && nfIsDateFormat(numFmt)) {\n try {\n const out = nfFormat(numFmt, d)\n if (typeof out === 'string' && out !== '') return out\n } catch {\n /* fall through to the plain default */\n }\n }\n return formatDate(d)\n}\n\n/**\n * Resolve a cell's display text from its value SHAPE rather than exceljs's `cell.text` — which returns the\n * raw object for some value kinds (so a formula whose cached result is an error renders as \"[object Object]\")\n * and even throws on certain merged cells. We cover every {@link ExcelJS.ValueType}: numbers/strings/booleans\n * verbatim, dates formatted, rich text joined, hyperlinks as their label, formula results unwrapped (errors →\n * their code like \"#REF!\", a nested date/rich-text result resolved), and error cells as their code. Anything\n * unexpected falls back to a guarded `cell.text`, never to a stringified object.\n */\nfunction cellText(cell: ExcelJS.Cell): string {\n const v = cell.value as unknown\n if (v == null) return ''\n if (typeof v === 'number') return formatNumber(v, cell.numFmt)\n if (typeof v === 'string' || typeof v === 'boolean') return String(v)\n if (v instanceof Date) return formatDateValue(v, cell.numFmt)\n if (typeof v === 'object') {\n const o = v as Record<string, unknown>\n if (typeof o.error === 'string') return o.error // error cell: { error: '#DIV/0!' }\n if (Array.isArray(o.richText)) return richTextToString(o.richText) // rich text\n if ('hyperlink' in o) {\n // hyperlink: { text, hyperlink } — text may itself be rich text\n const t = o.text\n if (Array.isArray((t as { richText?: unknown })?.richText)) {\n return richTextToString((t as { richText?: unknown }).richText)\n }\n return t == null ? String(o.hyperlink) : String(t)\n }\n if ('result' in o || 'formula' in o || 'sharedFormula' in o) {\n // formula: { formula, result } — the result is what Excel shows in the cell\n const res = o.result as unknown\n if (res == null) return ''\n if (typeof res === 'number') return formatNumber(res, cell.numFmt)\n if (typeof res === 'string' || typeof res === 'boolean') return String(res)\n if (res instanceof Date) return formatDateValue(res, cell.numFmt)\n if (res && typeof res === 'object') {\n const ro = res as Record<string, unknown>\n if (typeof ro.error === 'string') return ro.error // formula result is an error → its code\n if (Array.isArray(ro.richText)) return richTextToString(ro.richText)\n }\n return ''\n }\n }\n // Unknown shape: try exceljs's own text, but never surface a stringified object (and never throw).\n try {\n const t = cell.text\n return t != null && t !== '[object Object]' ? String(t) : ''\n } catch {\n return ''\n }\n}\n\n// Numbers and dates align right by default in Excel (text aligns left). Mirror that when the cell has no\n// explicit horizontal alignment — including formula cells whose cached result is a number or date.\nfunction defaultRight(value: unknown): boolean {\n if (typeof value === 'number') return true\n if (value instanceof Date) return true\n if (value && typeof value === 'object' && 'result' in value) {\n const res = (value as { result?: unknown }).result\n return typeof res === 'number' || res instanceof Date\n }\n return false\n}\n\nfunction buildSheet(ws: ExcelJS.Worksheet): XlsxSheet {\n const merges = Array.isArray(ws.model?.merges) ? (ws.model.merges as string[]) : []\n const { spans, covered } = parseMerges(merges)\n\n const cols = Math.min(ws.columnCount || 0, MAX_COLS)\n const rowCount = Math.min(ws.rowCount || 0, MAX_ROWS)\n\n const colWidths: number[] = []\n const colHidden: boolean[] = []\n for (let c = 1; c <= cols; c++) {\n const col = ws.getColumn(c)\n colWidths.push(colWidthToPx(col?.width))\n colHidden.push(!!col?.hidden)\n }\n\n const rows: XlsxRow[] = []\n for (let r = 1; r <= rowCount; r++) {\n const row = ws.getRow(r)\n const cells: XlsxCell[] = []\n for (let c = 1; c <= cols; c++) {\n if (covered.has(`${r},${c}`)) continue\n const cell = row.getCell(c)\n const span = spans.get(`${r},${c}`)\n const text = cellText(cell)\n // Formula source (without leading `=`): regular cells carry `{ formula }`, shared-formula dependents\n // carry `{ sharedFormula }` (exceljs's per-cell expression). Read from the value shape to avoid the\n // `.formula` getter's side effects.\n let formula: string | undefined\n const cv = cell.value as unknown\n if (cv && typeof cv === 'object') {\n const o = cv as Record<string, unknown>\n if (typeof o.formula === 'string' && o.formula) formula = o.formula\n else if (typeof o.sharedFormula === 'string' && o.sharedFormula) formula = o.sharedFormula\n }\n // Skip truly empty, unstyled, unmerged cells but still emit a placeholder so columns line up — we do\n // that by always pushing a cell (with its `col`), letting the renderer position via colSpan gaps.\n const font = cell.font || {}\n const align = cell.alignment || {}\n const border = cell.border || {}\n const horizontal =\n align.horizontal === 'center' || align.horizontal === 'centerContinuous'\n ? 'center'\n : align.horizontal === 'right'\n ? 'right'\n : align.horizontal === 'left'\n ? 'left'\n : defaultRight(cell.value)\n ? 'right'\n : undefined\n const vertical =\n align.vertical === 'top'\n ? 'top'\n : align.vertical === 'bottom'\n ? 'bottom'\n : align.vertical === 'middle'\n ? 'middle'\n : undefined\n cells.push({\n text,\n col: c,\n rowSpan: span?.rowSpan ?? 1,\n colSpan: span?.colSpan ?? 1,\n bold: font.bold || undefined,\n italic: font.italic || undefined,\n underline: !!font.underline || undefined,\n strike: font.strike || undefined,\n fontSize: fontSizeToPx(font.size),\n formula,\n color: colorToCss(font.color),\n bg:\n cell.fill && cell.fill.type === 'pattern'\n ? colorToCss((cell.fill as { fgColor?: unknown }).fgColor)\n : undefined,\n align: horizontal,\n valign: vertical,\n wrap: align.wrapText || undefined,\n bt: !!border.top?.style || undefined,\n br: !!border.right?.style || undefined,\n bb: !!border.bottom?.style || undefined,\n bl: !!border.left?.style || undefined,\n })\n }\n rows.push({\n num: r,\n height: row.height ? Math.round((row.height * 4) / 3) : undefined,\n hidden: !!row.hidden,\n cells,\n })\n }\n\n // Freeze panes: exceljs surfaces the active view as { state:'frozen', xSplit, ySplit } where xSplit/ySplit\n // are the counts of columns/rows held fixed. Clamp to the rendered range so a stale split can't pin offscreen.\n const view = Array.isArray(ws.views) ? ws.views[0] : undefined\n const frozen = view && (view as { state?: string }).state === 'frozen' ? view : undefined\n const frozenCols = frozen\n ? Math.min(Math.max(0, (frozen as { xSplit?: number }).xSplit ?? 0), cols)\n : 0\n const frozenRows = frozen\n ? Math.min(Math.max(0, (frozen as { ySplit?: number }).ySplit ?? 0), rowCount)\n : 0\n\n return { name: ws.name || `Sheet`, colWidths, colHidden, rows, cols, frozenRows, frozenCols }\n}\n\n/** Parse `.xlsx` bytes into the render model. Throws on a non-spreadsheet / corrupt file. */\nexport async function parseXlsx(buf: ArrayBuffer): Promise<XlsxModel> {\n const wb = new ExcelJS.Workbook()\n await wb.xlsx.load(buf)\n const sheets = wb.worksheets\n // Hidden sheets (state 'hidden'/'veryHidden') aren't shown in Excel's tab bar; skip them too.\n .filter((ws) => ws && ws.state !== 'hidden' && ws.state !== 'veryHidden')\n .map(buildSheet)\n if (sheets.length === 0) throw new Error('工作簿中没有可显示的工作表')\n return { sheets }\n}\n"],"mappings":"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"}
@@ -150,3 +150,5 @@ async function v(e) {
150
150
  }
151
151
  //#endregion
152
152
  export { v as parseXlsxCharts };
153
+
154
+ //# sourceMappingURL=xlsxCharts.js.map