@l1yp/file-viewer 0.1.0 → 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 (439) hide show
  1. package/README.md +2 -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 +9 -0
  47. package/dist/components/DocxNavigationPane.js.map +1 -0
  48. package/dist/components/DocxNavigationPane.vue.d.ts +23 -0
  49. package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js +98 -0
  50. package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
  51. package/dist/components/DocxViewer.js +3 -1
  52. package/dist/components/DocxViewer.js.map +1 -0
  53. package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js +72 -37
  54. package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  55. package/dist/components/DrawioViewer.js +2 -0
  56. package/dist/components/DrawioViewer.js.map +1 -0
  57. package/dist/components/DrawioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  58. package/dist/components/DrawioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  59. package/dist/components/DxfViewer.js +2 -0
  60. package/dist/components/DxfViewer.js.map +1 -0
  61. package/dist/components/DxfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  62. package/dist/components/DxfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  63. package/dist/components/EbookViewer.js +2 -0
  64. package/dist/components/EbookViewer.js.map +1 -0
  65. package/dist/components/EbookViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  66. package/dist/components/EbookViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  67. package/dist/components/EmlViewer.js +2 -0
  68. package/dist/components/EmlViewer.js.map +1 -0
  69. package/dist/components/EmlViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  70. package/dist/components/EmlViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  71. package/dist/components/EplViewer.js +2 -0
  72. package/dist/components/EplViewer.js.map +1 -0
  73. package/dist/components/EplViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  74. package/dist/components/EplViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  75. package/dist/components/FileViewerBody.js +3 -1
  76. package/dist/components/FileViewerBody.js.map +1 -0
  77. package/dist/components/FileViewerBody.vue.d.ts +5 -0
  78. package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js +250 -247
  79. package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js.map +1 -0
  80. package/dist/components/FileViewerPanel.js +2 -0
  81. package/dist/components/FileViewerPanel.js.map +1 -0
  82. package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js +2 -0
  83. package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  84. package/dist/components/FontViewer.js +2 -0
  85. package/dist/components/FontViewer.js.map +1 -0
  86. package/dist/components/FontViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  87. package/dist/components/FontViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  88. package/dist/components/FrontmatterCard.js +2 -0
  89. package/dist/components/FrontmatterCard.js.map +1 -0
  90. package/dist/components/FrontmatterCard.vue_vue_type_script_setup_true_lang.js +2 -0
  91. package/dist/components/FrontmatterCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
  92. package/dist/components/GeoViewer.js +2 -0
  93. package/dist/components/GeoViewer.js.map +1 -0
  94. package/dist/components/GeoViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  95. package/dist/components/GeoViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  96. package/dist/components/HarViewer.js +2 -0
  97. package/dist/components/HarViewer.js.map +1 -0
  98. package/dist/components/HarViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  99. package/dist/components/HarViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  100. package/dist/components/HexViewer.js +2 -0
  101. package/dist/components/HexViewer.js.map +1 -0
  102. package/dist/components/HexViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  103. package/dist/components/HexViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  104. package/dist/components/HprofViewer.js +2 -0
  105. package/dist/components/HprofViewer.js.map +1 -0
  106. package/dist/components/HprofViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  107. package/dist/components/HprofViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  108. package/dist/components/ImageLightbox.js +2 -0
  109. package/dist/components/ImageLightbox.js.map +1 -0
  110. package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js +2 -0
  111. package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js.map +1 -0
  112. package/dist/components/InlineText.js +2 -0
  113. package/dist/components/InlineText.js.map +1 -0
  114. package/dist/components/JksViewer.js +2 -0
  115. package/dist/components/JksViewer.js.map +1 -0
  116. package/dist/components/JksViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  117. package/dist/components/JksViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  118. package/dist/components/JsonlViewer.js +2 -0
  119. package/dist/components/JsonlViewer.js.map +1 -0
  120. package/dist/components/JsonlViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  121. package/dist/components/JsonlViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  122. package/dist/components/LogViewer.js +2 -0
  123. package/dist/components/LogViewer.js.map +1 -0
  124. package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  125. package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  126. package/dist/components/LrcViewer.js +2 -0
  127. package/dist/components/LrcViewer.js.map +1 -0
  128. package/dist/components/LrcViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  129. package/dist/components/LrcViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  130. package/dist/components/MarkdownTable.js +2 -0
  131. package/dist/components/MarkdownTable.js.map +1 -0
  132. package/dist/components/MarkdownTable.vue_vue_type_script_setup_true_lang.js +2 -0
  133. package/dist/components/MarkdownTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
  134. package/dist/components/MarkdownText.js +2 -0
  135. package/dist/components/MarkdownText.js.map +1 -0
  136. package/dist/components/MhtViewer.js +2 -0
  137. package/dist/components/MhtViewer.js.map +1 -0
  138. package/dist/components/MhtViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  139. package/dist/components/MhtViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  140. package/dist/components/MindMapView.js +2 -0
  141. package/dist/components/MindMapView.js.map +1 -0
  142. package/dist/components/MindMapView.vue_vue_type_script_setup_true_lang.js +2 -0
  143. package/dist/components/MindMapView.vue_vue_type_script_setup_true_lang.js.map +1 -0
  144. package/dist/components/MissingDependency.js +2 -0
  145. package/dist/components/MissingDependency.js.map +1 -0
  146. package/dist/components/MissingDependency.vue_vue_type_script_setup_true_lang.js +2 -0
  147. package/dist/components/MissingDependency.vue_vue_type_script_setup_true_lang.js.map +1 -0
  148. package/dist/components/ModelViewer.js +2 -0
  149. package/dist/components/ModelViewer.js.map +1 -0
  150. package/dist/components/ModelViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  151. package/dist/components/ModelViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  152. package/dist/components/MsiViewer.js +2 -0
  153. package/dist/components/MsiViewer.js.map +1 -0
  154. package/dist/components/MsiViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  155. package/dist/components/MsiViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  156. package/dist/components/NotebookViewer.js +2 -0
  157. package/dist/components/NotebookViewer.js.map +1 -0
  158. package/dist/components/NotebookViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  159. package/dist/components/NotebookViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  160. package/dist/components/PdfViewer.js +2 -0
  161. package/dist/components/PdfViewer.js.map +1 -0
  162. package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  163. package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  164. package/dist/components/PptxNavigationPane.js +9 -0
  165. package/dist/components/PptxNavigationPane.js.map +1 -0
  166. package/dist/components/PptxNavigationPane.vue.d.ts +20 -0
  167. package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js +120 -0
  168. package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
  169. package/dist/components/PptxViewer.js +3 -1
  170. package/dist/components/PptxViewer.js.map +1 -0
  171. package/dist/components/PptxViewer.vue_vue_type_script_setup_true_lang.js +71 -39
  172. package/dist/components/PptxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  173. package/dist/components/PsdViewer.js +2 -0
  174. package/dist/components/PsdViewer.js.map +1 -0
  175. package/dist/components/PsdViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  176. package/dist/components/PsdViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  177. package/dist/components/QmcAudioViewer.js +2 -0
  178. package/dist/components/QmcAudioViewer.js.map +1 -0
  179. package/dist/components/QmcAudioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  180. package/dist/components/QmcAudioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  181. package/dist/components/RasterViewer.js +2 -0
  182. package/dist/components/RasterViewer.js.map +1 -0
  183. package/dist/components/RasterViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  184. package/dist/components/RasterViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  185. package/dist/components/RtfViewer.js +2 -0
  186. package/dist/components/RtfViewer.js.map +1 -0
  187. package/dist/components/RtfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  188. package/dist/components/RtfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  189. package/dist/components/SevenZipBrowser.js +2 -0
  190. package/dist/components/SevenZipBrowser.js.map +1 -0
  191. package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
  192. package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
  193. package/dist/components/SqliteViewer.js +2 -0
  194. package/dist/components/SqliteViewer.js.map +1 -0
  195. package/dist/components/SqliteViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  196. package/dist/components/SqliteViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  197. package/dist/components/SubtitleViewer.js +2 -0
  198. package/dist/components/SubtitleViewer.js.map +1 -0
  199. package/dist/components/SubtitleViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  200. package/dist/components/SubtitleViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  201. package/dist/components/XlsxChart.js +2 -0
  202. package/dist/components/XlsxChart.js.map +1 -0
  203. package/dist/components/XlsxChart.vue_vue_type_script_setup_true_lang.js +2 -0
  204. package/dist/components/XlsxChart.vue_vue_type_script_setup_true_lang.js.map +1 -0
  205. package/dist/components/XlsxViewer.js +3 -1
  206. package/dist/components/XlsxViewer.js.map +1 -0
  207. package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js +301 -304
  208. package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  209. package/dist/components/XmindViewer.js +2 -0
  210. package/dist/components/XmindViewer.js.map +1 -0
  211. package/dist/components/XmindViewer.vue_vue_type_script_setup_true_lang.js +2 -0
  212. package/dist/components/XmindViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  213. package/dist/components/ZipBrowser.js +3 -1
  214. package/dist/components/ZipBrowser.js.map +1 -0
  215. package/dist/components/ZipBrowser.vue.d.ts +3 -1
  216. package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js +154 -122
  217. package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
  218. package/dist/components/ZoomImage.js +2 -0
  219. package/dist/components/ZoomImage.js.map +1 -0
  220. package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js +2 -0
  221. package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js.map +1 -0
  222. package/dist/components/epl/EplCodeBlock.js +2 -0
  223. package/dist/components/epl/EplCodeBlock.js.map +1 -0
  224. package/dist/components/epl/EplCodeBlock.vue_vue_type_script_setup_true_lang.js +2 -0
  225. package/dist/components/epl/EplCodeBlock.vue_vue_type_script_setup_true_lang.js.map +1 -0
  226. package/dist/components/epl/EplDoc.js +2 -0
  227. package/dist/components/epl/EplDoc.js.map +1 -0
  228. package/dist/components/epl/EplDoc.vue_vue_type_script_setup_true_lang.js +2 -0
  229. package/dist/components/epl/EplDoc.vue_vue_type_script_setup_true_lang.js.map +1 -0
  230. package/dist/components/epl/EplMethod.js +2 -0
  231. package/dist/components/epl/EplMethod.js.map +1 -0
  232. package/dist/components/epl/EplMethod.vue_vue_type_script_setup_true_lang.js +2 -0
  233. package/dist/components/epl/EplMethod.vue_vue_type_script_setup_true_lang.js.map +1 -0
  234. package/dist/components/epl/EplResource.js +2 -0
  235. package/dist/components/epl/EplResource.js.map +1 -0
  236. package/dist/components/epl/EplResource.vue_vue_type_script_setup_true_lang.js +2 -0
  237. package/dist/components/epl/EplResource.vue_vue_type_script_setup_true_lang.js.map +1 -0
  238. package/dist/components/epl/EplTree.js +2 -0
  239. package/dist/components/epl/EplTree.js.map +1 -0
  240. package/dist/components/epl/EplTree.vue_vue_type_script_setup_true_lang.js +2 -0
  241. package/dist/components/epl/EplTree.vue_vue_type_script_setup_true_lang.js.map +1 -0
  242. package/dist/components/epl/EplVarTable.js +2 -0
  243. package/dist/components/epl/EplVarTable.js.map +1 -0
  244. package/dist/components/epl/EplVarTable.vue_vue_type_script_setup_true_lang.js +2 -0
  245. package/dist/components/epl/EplVarTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
  246. package/dist/components/epl/numbering.js +2 -0
  247. package/dist/components/epl/numbering.js.map +1 -0
  248. package/dist/components/icons/Icon.js +2 -0
  249. package/dist/components/icons/Icon.js.map +1 -0
  250. package/dist/components/icons/Icon.vue_vue_type_script_setup_true_lang.js +2 -0
  251. package/dist/components/icons/Icon.vue_vue_type_script_setup_true_lang.js.map +1 -0
  252. package/dist/components/icons/iconPaths.js +2 -0
  253. package/dist/components/icons/iconPaths.js.map +1 -0
  254. package/dist/components/optionalViewer.js +2 -0
  255. package/dist/components/optionalViewer.js.map +1 -0
  256. package/dist/components/ui/ClampBlock.js +2 -0
  257. package/dist/components/ui/ClampBlock.js.map +1 -0
  258. package/dist/composables/useElementNavigation.d.ts +21 -0
  259. package/dist/composables/useElementNavigation.js +63 -0
  260. package/dist/composables/useElementNavigation.js.map +1 -0
  261. package/dist/composables/useFileViewer.js +2 -0
  262. package/dist/composables/useFileViewer.js.map +1 -0
  263. package/dist/composables/useLightbox.js +2 -0
  264. package/dist/composables/useLightbox.js.map +1 -0
  265. package/dist/composables/useLogRules.js +2 -0
  266. package/dist/composables/useLogRules.js.map +1 -0
  267. package/dist/lib/aes.js +2 -0
  268. package/dist/lib/aes.js.map +1 -0
  269. package/dist/lib/appx.js +2 -0
  270. package/dist/lib/appx.js.map +1 -0
  271. package/dist/lib/archiveHtml.d.ts +49 -0
  272. package/dist/lib/archiveHtml.js +377 -0
  273. package/dist/lib/archiveHtml.js.map +1 -0
  274. package/dist/lib/asn1.js +2 -0
  275. package/dist/lib/asn1.js.map +1 -0
  276. package/dist/lib/binobj.js +2 -0
  277. package/dist/lib/binobj.js.map +1 -0
  278. package/dist/lib/brotli.js +2 -0
  279. package/dist/lib/brotli.js.map +1 -0
  280. package/dist/lib/bzip2.js +2 -0
  281. package/dist/lib/bzip2.js.map +1 -0
  282. package/dist/lib/classfile.js +2 -0
  283. package/dist/lib/classfile.js.map +1 -0
  284. package/dist/lib/codePreview.js +2 -0
  285. package/dist/lib/codePreview.js.map +1 -0
  286. package/dist/lib/codefold.js +2 -0
  287. package/dist/lib/codefold.js.map +1 -0
  288. package/dist/lib/codeformat.js +2 -0
  289. package/dist/lib/codeformat.js.map +1 -0
  290. package/dist/lib/csv.js +2 -0
  291. package/dist/lib/csv.js.map +1 -0
  292. package/dist/lib/des.js +2 -0
  293. package/dist/lib/des.js.map +1 -0
  294. package/dist/lib/diagram.js +2 -0
  295. package/dist/lib/diagram.js.map +1 -0
  296. package/dist/lib/dicom.js +2 -0
  297. package/dist/lib/dicom.js.map +1 -0
  298. package/dist/lib/dicomCodecs.js +2 -0
  299. package/dist/lib/dicomCodecs.js.map +1 -0
  300. package/dist/lib/dicomRender.js +2 -0
  301. package/dist/lib/dicomRender.js.map +1 -0
  302. package/dist/lib/docxNavigation.d.ts +31 -0
  303. package/dist/lib/docxNavigation.js +65 -0
  304. package/dist/lib/docxNavigation.js.map +1 -0
  305. package/dist/lib/dxf.js +2 -0
  306. package/dist/lib/dxf.js.map +1 -0
  307. package/dist/lib/eml.js +2 -0
  308. package/dist/lib/eml.js.map +1 -0
  309. package/dist/lib/encoding.js +2 -0
  310. package/dist/lib/encoding.js.map +1 -0
  311. package/dist/lib/epl/code/codeDataParser.js +2 -0
  312. package/dist/lib/epl/code/codeDataParser.js.map +1 -0
  313. package/dist/lib/epl/code/codeLines.js +2 -0
  314. package/dist/lib/epl/code/codeLines.js.map +1 -0
  315. package/dist/lib/epl/code/textCode.js +2 -0
  316. package/dist/lib/epl/code/textCode.js.map +1 -0
  317. package/dist/lib/epl/eplDocument.js +2 -0
  318. package/dist/lib/epl/eplDocument.js.map +1 -0
  319. package/dist/lib/epl/imageList.js +2 -0
  320. package/dist/lib/epl/imageList.js.map +1 -0
  321. package/dist/lib/epl/index.js +2 -0
  322. package/dist/lib/epl/index.js.map +1 -0
  323. package/dist/lib/epl/io/binaryReader.js +2 -0
  324. package/dist/lib/epl/io/binaryReader.js.map +1 -0
  325. package/dist/lib/epl/io/encoding.js +2 -0
  326. package/dist/lib/epl/io/encoding.js.map +1 -0
  327. package/dist/lib/epl/model/eplSystemId.js +2 -0
  328. package/dist/lib/epl/model/eplSystemId.js.map +1 -0
  329. package/dist/lib/epl/names/eplLibData.js +2 -0
  330. package/dist/lib/epl/names/eplLibData.js.map +1 -0
  331. package/dist/lib/epl/names/idToNameMap.js +2 -0
  332. package/dist/lib/epl/names/idToNameMap.js.map +1 -0
  333. package/dist/lib/epl/names/libNameProvider.js +2 -0
  334. package/dist/lib/epl/names/libNameProvider.js.map +1 -0
  335. package/dist/lib/epl/program.js +2 -0
  336. package/dist/lib/epl/program.js.map +1 -0
  337. package/dist/lib/epl/projectFileReader.js +2 -0
  338. package/dist/lib/epl/projectFileReader.js.map +1 -0
  339. package/dist/lib/fb2.js +2 -0
  340. package/dist/lib/fb2.js.map +1 -0
  341. package/dist/lib/fileType.js +2 -0
  342. package/dist/lib/fileType.js.map +1 -0
  343. package/dist/lib/fonts.js +2 -0
  344. package/dist/lib/fonts.js.map +1 -0
  345. package/dist/lib/format.js +2 -0
  346. package/dist/lib/format.js.map +1 -0
  347. package/dist/lib/frontmatter.js +2 -0
  348. package/dist/lib/frontmatter.js.map +1 -0
  349. package/dist/lib/geo.js +2 -0
  350. package/dist/lib/geo.js.map +1 -0
  351. package/dist/lib/har.js +2 -0
  352. package/dist/lib/har.js.map +1 -0
  353. package/dist/lib/highlight.js +2 -0
  354. package/dist/lib/highlight.js.map +1 -0
  355. package/dist/lib/highlightLangs.js +2 -0
  356. package/dist/lib/highlightLangs.js.map +1 -0
  357. package/dist/lib/hprof.js +2 -0
  358. package/dist/lib/hprof.js.map +1 -0
  359. package/dist/lib/ipynb.js +2 -0
  360. package/dist/lib/ipynb.js.map +1 -0
  361. package/dist/lib/jks.js +2 -0
  362. package/dist/lib/jks.js.map +1 -0
  363. package/dist/lib/jsonl.js +2 -0
  364. package/dist/lib/jsonl.js.map +1 -0
  365. package/dist/lib/lrc.js +2 -0
  366. package/dist/lib/lrc.js.map +1 -0
  367. package/dist/lib/lzma.js +2 -0
  368. package/dist/lib/lzma.js.map +1 -0
  369. package/dist/lib/markdown/blocks.js +2 -0
  370. package/dist/lib/markdown/blocks.js.map +1 -0
  371. package/dist/lib/markdown/inline.js +2 -0
  372. package/dist/lib/markdown/inline.js.map +1 -0
  373. package/dist/lib/markdown/math.js +2 -0
  374. package/dist/lib/markdown/math.js.map +1 -0
  375. package/dist/lib/markdown/segmentize.js +2 -0
  376. package/dist/lib/markdown/segmentize.js.map +1 -0
  377. package/dist/lib/markdown/styles.js +2 -0
  378. package/dist/lib/markdown/styles.js.map +1 -0
  379. package/dist/lib/mht.js +2 -0
  380. package/dist/lib/mht.js.map +1 -0
  381. package/dist/lib/midi.js +2 -0
  382. package/dist/lib/midi.js.map +1 -0
  383. package/dist/lib/mime.js +2 -0
  384. package/dist/lib/mime.js.map +1 -0
  385. package/dist/lib/mindmap.js +2 -0
  386. package/dist/lib/mindmap.js.map +1 -0
  387. package/dist/lib/mobi.js +2 -0
  388. package/dist/lib/mobi.js.map +1 -0
  389. package/dist/lib/msi.js +2 -0
  390. package/dist/lib/msi.js.map +1 -0
  391. package/dist/lib/optionalDep.js +2 -0
  392. package/dist/lib/optionalDep.js.map +1 -0
  393. package/dist/lib/pkcs12.js +2 -0
  394. package/dist/lib/pkcs12.js.map +1 -0
  395. package/dist/lib/pptxNavigation.d.ts +7 -0
  396. package/dist/lib/pptxNavigation.js +29 -0
  397. package/dist/lib/pptxNavigation.js.map +1 -0
  398. package/dist/lib/qmc.js +2 -0
  399. package/dist/lib/qmc.js.map +1 -0
  400. package/dist/lib/rar.js +2 -0
  401. package/dist/lib/rar.js.map +1 -0
  402. package/dist/lib/rawPreview.js +2 -0
  403. package/dist/lib/rawPreview.js.map +1 -0
  404. package/dist/lib/rc2.js +2 -0
  405. package/dist/lib/rc2.js.map +1 -0
  406. package/dist/lib/resolveInput.js +2 -0
  407. package/dist/lib/resolveInput.js.map +1 -0
  408. package/dist/lib/rtf.js +2 -0
  409. package/dist/lib/rtf.js.map +1 -0
  410. package/dist/lib/sevenZip.js +2 -0
  411. package/dist/lib/sevenZip.js.map +1 -0
  412. package/dist/lib/sha1.js +2 -0
  413. package/dist/lib/sha1.js.map +1 -0
  414. package/dist/lib/sha256.js +2 -0
  415. package/dist/lib/sha256.js.map +1 -0
  416. package/dist/lib/speechAudio.js +2 -0
  417. package/dist/lib/speechAudio.js.map +1 -0
  418. package/dist/lib/sqlite.js +2 -0
  419. package/dist/lib/sqlite.js.map +1 -0
  420. package/dist/lib/subtitle.js +2 -0
  421. package/dist/lib/subtitle.js.map +1 -0
  422. package/dist/lib/tarball.js +2 -0
  423. package/dist/lib/tarball.js.map +1 -0
  424. package/dist/lib/viewerFormats.js +2 -0
  425. package/dist/lib/viewerFormats.js.map +1 -0
  426. package/dist/lib/viewerKind.js +2 -0
  427. package/dist/lib/viewerKind.js.map +1 -0
  428. package/dist/lib/x509.js +2 -0
  429. package/dist/lib/x509.js.map +1 -0
  430. package/dist/lib/xlsx.js +2 -0
  431. package/dist/lib/xlsx.js.map +1 -0
  432. package/dist/lib/xlsxCharts.js +2 -0
  433. package/dist/lib/xlsxCharts.js.map +1 -0
  434. package/dist/lib/xmind.js +2 -0
  435. package/dist/lib/xmind.js.map +1 -0
  436. package/dist/lib/zip.js +2 -0
  437. package/dist/lib/zip.js.map +1 -0
  438. package/dist/style.css +1 -1
  439. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fb2.js","names":[],"sources":["../../src/lib/fb2.ts"],"sourcesContent":["/**\n * Hand-rolled, zero-dependency FictionBook 2 (.fb2) parser for the side viewer.\n *\n * FB2 is an XML e-book format. This walks the `<body>` tree, mapping the FB2 elements to a small whitelist of\n * HTML tags (everything else falls through to its inner content), resolves `<image>` references to the inline\n * `<binary>` base64 blobs as data URLs, and returns a COMPLETE, self-contained HTML document string ready to\n * drop into a sandboxed iframe — it carries a `script-src 'none'` CSP and only ever emits known tags with\n * escaped text, so the book's markup can't inject anything executable.\n */\n\nconst XLINK = 'http://www.w3.org/1999/xlink'\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\n// Collect <binary id content-type>…base64…</binary> → data URL by id.\nfunction collectBinaries(doc: Document): Map<string, string> {\n const map = new Map<string, string>()\n for (const bin of Array.from(doc.getElementsByTagName('binary'))) {\n const id = bin.getAttribute('id')\n if (!id) continue\n const ct = bin.getAttribute('content-type') || 'image/jpeg'\n const b64 = (bin.textContent || '').replace(/\\s+/g, '')\n if (b64) map.set(id, `data:${ct};base64,${b64}`)\n }\n return map\n}\n\nfunction imageHref(el: Element): string {\n return (\n el.getAttributeNS(XLINK, 'href') ||\n el.getAttribute('xlink:href') ||\n el.getAttribute('l:href') ||\n el.getAttribute('href') ||\n ''\n )\n}\n\n// Map an FB2 element to HTML. Unknown tags pass through their children, so structure is never lost.\nfunction serialize(node: Node, bins: Map<string, string>): string {\n if (node.nodeType === 3) return escapeHtml(node.textContent || '') // text\n if (node.nodeType !== 1) return ''\n const el = node as Element\n const inner = (): string =>\n Array.from(el.childNodes)\n .map((c) => serialize(c, bins))\n .join('')\n switch (el.tagName.toLowerCase()) {\n case 'p':\n return `<p>${inner()}</p>`\n case 'empty-line':\n return '<br>'\n case 'emphasis':\n return `<em>${inner()}</em>`\n case 'strong':\n return `<strong>${inner()}</strong>`\n case 'strikethrough':\n return `<s>${inner()}</s>`\n case 'sub':\n return `<sub>${inner()}</sub>`\n case 'sup':\n return `<sup>${inner()}</sup>`\n case 'code':\n return `<code>${inner()}</code>`\n case 'title':\n return `<header class=\"fb2-title\">${inner()}</header>`\n case 'subtitle':\n return `<p class=\"fb2-subtitle\">${inner()}</p>`\n case 'section':\n return `<section>${inner()}</section>`\n case 'epigraph':\n return `<blockquote class=\"fb2-epigraph\">${inner()}</blockquote>`\n case 'cite':\n return `<blockquote>${inner()}</blockquote>`\n case 'poem':\n return `<div class=\"fb2-poem\">${inner()}</div>`\n case 'stanza':\n return `<div class=\"fb2-stanza\">${inner()}</div>`\n case 'v':\n return `<p class=\"fb2-v\">${inner()}</p>`\n case 'text-author':\n return `<p class=\"fb2-author\">${inner()}</p>`\n case 'a':\n return `<span class=\"fb2-link\">${inner()}</span>` // links de-fanged to plain spans (no nav target)\n case 'image': {\n const src = bins.get(imageHref(el).replace(/^#/, ''))\n return src ? `<p class=\"fb2-img\"><img src=\"${src}\" alt=\"\"></p>` : ''\n }\n default:\n return inner()\n }\n}\n\nconst STYLE =\n `body{margin:0;padding:24px 28px 80px;max-width:46rem;margin-inline:auto;` +\n `font:16px/1.7 Georgia,'Source Serif 4',serif;color:#1a1a1a;background:#fff;` +\n `-webkit-text-size-adjust:100%}` +\n `img{max-width:100%;height:auto}` +\n `.fb2-title{margin:1.6em 0 1em;font-weight:700;font-size:1.3em;line-height:1.3}` +\n `.fb2-subtitle{font-weight:600;margin:1.2em 0 .6em}` +\n `.fb2-epigraph,blockquote{margin:1em 2em;color:#555;font-style:italic}` +\n `.fb2-poem{margin:1em 1.5em}.fb2-v{margin:0}.fb2-author{text-align:right;font-style:italic}` +\n `.fb2-img{text-align:center}p{margin:0 0 .9em;text-indent:1.4em}` +\n `.fb2-title p,.fb2-img,.fb2-v{text-indent:0}`\n\nexport interface ParsedFb2 {\n title: string\n html: string // a complete, sandbox-ready HTML document\n}\n\nexport function parseFb2(text: string): ParsedFb2 {\n const clean = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text\n const doc = new DOMParser().parseFromString(clean, 'application/xml')\n if (doc.getElementsByTagName('parsererror').length) throw new Error('FB2 XML 解析失败')\n\n const bins = collectBinaries(doc)\n const bodies = Array.from(doc.getElementsByTagName('body'))\n if (!bodies.length) throw new Error('FB2 缺少 <body>')\n const bodyHtml = bodies.map((b) => serialize(b, bins)).join('\\n')\n\n const titleEl = doc.getElementsByTagName('book-title')[0]\n const title = (titleEl?.textContent || '').trim()\n\n const csp =\n `<meta http-equiv=\"Content-Security-Policy\" ` +\n `content=\"script-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'\">`\n const html =\n `<!doctype html><html><head><meta charset=\"utf-8\">${csp}` +\n `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">` +\n `<style>${STYLE}</style></head><body>${bodyHtml}</body></html>`\n return { title, html }\n}\n"],"mappings":"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"}
@@ -798,3 +798,5 @@ function Je(n, r) {
798
798
  }
799
799
  //#endregion
800
800
  export { o as LARGE_TEXT_BYTES, j as ebookKind, e as fileExt, Je as hljsLang, J as isAppxFile, Re as isAudioFile, L as isBinaryObjFile, Te as isCertFile, te as isCsvFile, S as isDicomFile, X as isDocxFile, me as isDxfFile, ge as isEmlFile, R as isEplFile, Ue as isFontFile, je as isGeoFile, ye as isHarFile, H as isHprofFile, P as isHtmlFile, T as isImageFile, B as isJavaClassFile, Oe as isJksFile, u as isJsonlFile, s as isLogFile, qe as isLrcFile, _e as isMhtFile, de as isMindMapFile, Ie as isModelFile, G as isMsiFile, Be as isNotebookFile, le as isPdfFile, ae as isPptxFile, Ge as isQmcAudioFile, b as isRarFile, se as isRtfFile, h as isSevenZipFile, Se as isSqliteFile, Pe as isSubtitleFile, M as isSvgFile, v as isTarballFile, a as isTextFile, N as isVideoFile, re as isXlsxFile, pe as isXmindFile, p as isZipFile, A as rasterImageKind };
801
+
802
+ //# sourceMappingURL=fileType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileType.js","names":[],"sources":["../../src/lib/fileType.ts"],"sourcesContent":["/**\n * File-type detection for the conversation file UIs.\n *\n * The proxy's stored `content_type` is unreliable (often `application/octet-stream`), so we never\n * trust it. Instead we prefer the share JSON's own `mime_type` (present on delivered `present_files`),\n * and fall back to the filename extension — which is all we have for user uploads / intermediate files.\n *\n * Two questions this answers:\n * - {@link isTextFile}: can we open it in the side viewer? (non-text files are download-only)\n * - {@link hljsLang}: which highlight.js language to colorize with ('' = plain, no highlighting)\n */\n\n/** Lowercased file extension without the dot, or '' if the name has none. */\nexport function fileExt(name: string): string {\n const base = (name || '').split(/[\\\\/]/).pop() ?? ''\n const dot = base.lastIndexOf('.')\n return dot > 0 ? base.slice(dot + 1).toLowerCase() : ''\n}\n\n// Extension → highlight.js language id. Covers the common code/markup types; anything textual but\n// unlisted renders as plain escaped text (see hljsLang returning '').\nconst EXT_LANG: Record<string, string> = {\n js: 'javascript',\n mjs: 'javascript',\n cjs: 'javascript',\n jsx: 'javascript',\n ts: 'typescript',\n tsx: 'typescript',\n mts: 'typescript',\n cts: 'typescript',\n py: 'python',\n pyw: 'python',\n rb: 'ruby',\n rs: 'rust',\n kt: 'kotlin',\n kts: 'kotlin',\n java: 'java',\n c: 'c',\n h: 'c',\n cc: 'cpp',\n cpp: 'cpp',\n cxx: 'cpp',\n hpp: 'cpp',\n hh: 'cpp',\n cs: 'csharp',\n go: 'go',\n php: 'php',\n swift: 'swift',\n scala: 'scala',\n sh: 'bash',\n bash: 'bash',\n zsh: 'bash',\n fish: 'bash',\n ps1: 'powershell',\n psm1: 'powershell',\n bat: 'dos',\n cmd: 'dos',\n yml: 'yaml',\n yaml: 'yaml',\n toml: 'ini',\n ini: 'ini',\n cfg: 'ini',\n conf: 'ini',\n properties: 'properties',\n json: 'json',\n json5: 'json',\n xml: 'xml',\n html: 'xml',\n htm: 'xml',\n svg: 'xml',\n vue: 'xml',\n svelte: 'xml',\n // Outline / mind-map XML — viewable + XML-highlighted in source view (`.mm` is also Objective-C++, but the\n // mind-map render is content-sniffed, so a non-XML `.mm` just reads as XML-tinted source — see isMindMapFile).\n opml: 'xml',\n mm: 'xml',\n css: 'css',\n scss: 'scss',\n sass: 'scss',\n less: 'less',\n sql: 'sql',\n md: 'markdown',\n markdown: 'markdown',\n gradle: 'gradle',\n groovy: 'groovy',\n lua: 'lua',\n r: 'r',\n dart: 'dart',\n pl: 'perl',\n diff: 'diff',\n patch: 'diff',\n}\n\n// Textual extensions we let the viewer open but don't syntax-highlight (rendered as plain escaped text).\n// NOTE: `.rtf` is deliberately NOT here — it renders as rich text in the side viewer (see isRtfFile), not source.\nconst PLAIN_EXT = new Set([\n 'txt',\n 'text',\n 'log',\n 'csv',\n 'tsv',\n 'env',\n 'lock',\n 'gitignore',\n 'srt',\n 'vtt',\n])\n\n// Extension-less filenames that are nonetheless text.\nconst PLAIN_NAMES = new Set([\n 'dockerfile',\n 'makefile',\n 'license',\n 'readme',\n 'changelog',\n 'authors',\n '.gitignore',\n '.env',\n])\n\n// application/* MIME types that are actually text (text/* is handled generically).\nconst TEXTUAL_APP_MIME = new Set([\n 'application/json',\n 'application/ld+json',\n 'application/xml',\n 'application/xhtml+xml',\n 'application/javascript',\n 'application/ecmascript',\n 'application/x-sh',\n 'application/x-shellscript',\n 'application/x-python',\n 'application/x-python-code',\n 'application/x-yaml',\n 'application/yaml',\n 'application/toml',\n 'application/x-toml',\n 'application/sql',\n 'application/graphql',\n 'application/x-httpd-php',\n 'application/csv',\n 'application/markdown',\n 'image/svg+xml',\n])\n\n/** Can this file be opened in the side viewer? Prefer `mime`, fall back to the extension. */\nexport function isTextFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (m.startsWith('text/')) return true\n if (TEXTUAL_APP_MIME.has(m)) return true\n // Clearly-binary families are never viewable, regardless of extension.\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n // Other application/* (pdf, zip, msword, …): fall through to the extension as a tiebreak.\n }\n const ext = fileExt(name)\n if (ext) return ext in EXT_LANG || PLAIN_EXT.has(ext)\n return PLAIN_NAMES.has((name || '').toLowerCase())\n}\n\n// Any text file at or above this size is routed to the streaming log viewer (virtual-scrolled) instead of\n// the normal source view, so a huge .txt/.csv/.json doesn't lock the page tokenizing one giant block.\nexport const LARGE_TEXT_BYTES = 1024 * 1024\n\n/**\n * Should this open in the side viewer's streaming log viewer (virtual-scrolled, see LogViewer.vue) rather\n * than the normal highlighted source view? True for any `.log` (logs benefit from the line gutter + search),\n * and for any LARGE text file (>= {@link LARGE_TEXT_BYTES}) so big files don't freeze. Consulted in FileChip\n * BEFORE the generic text route. `size` is the manifest byte size when known (0/undefined ⇒ treat as small).\n */\nexport function isLogFile(name: string, mime?: string | null, size?: number | null): boolean {\n if (fileExt(name) === 'log') return true\n return (size ?? 0) >= LARGE_TEXT_BYTES && isTextFile(name, mime)\n}\n\n// CSV / TSV routed to the side viewer's table preview (parsed in-browser, see lib/csv.ts + CsvViewer.vue).\n// `.csv`/`.tsv` are ALSO plain text, so this is consulted in FileChip BEFORE the generic text route — but\n// AFTER the large-text/log route, so a huge CSV still streams through the virtual log viewer instead of\n// building a giant table. Extension-driven; text/csv + the tab-separated mimes also confirm it.\nconst CSV_EXT = new Set(['csv', 'tsv'])\nconst CSV_MIME = new Set(['text/csv', 'application/csv', 'text/tab-separated-values', 'text/tsv'])\n\n/**\n * Is this a CSV / TSV table (`.csv`/`.tsv`)? Such files open in the side viewer's table preview, which parses\n * the rows in-browser (see lib/csv.ts + CsvViewer.vue) with full RFC 4180 quote handling and a render/source\n * toggle. Prefer `mime`, fall back to the extension; a concrete non-text media mime rules it out (`.csv` is\n * often served as text/plain or octet-stream, so the extension is the reliable signal).\n */\nexport function isCsvFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (CSV_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return CSV_EXT.has(fileExt(name))\n}\n\n// JSON Lines / NDJSON (`.jsonl` / `.ndjson`) — one JSON value per line, routed to the side viewer's record-list\n// preview (parsed in-browser, see lib/jsonl.ts + JsonlViewer.vue). A `.jsonl` is often served as\n// application/json or octet-stream, so the extension is the reliable signal; the explicit jsonl mimes also\n// confirm it. We deliberately DON'T claim bare `.json` (that stays ordinary JSON source). Must be consulted\n// BEFORE the generic text route (a `.jsonl` served as application/json would otherwise read as JSON source).\nconst JSONL_EXT = new Set(['jsonl', 'ndjson'])\nconst JSONL_MIME = new Set([\n 'application/jsonl',\n 'application/x-jsonl',\n 'application/jsonlines',\n 'application/x-jsonlines',\n 'application/x-ndjson',\n 'application/ndjson',\n])\n\n/**\n * Is this a JSON Lines / NDJSON file (`.jsonl` / `.ndjson`)? Such files open in the side viewer's record list,\n * which parses each line independently (see lib/jsonl.ts + JsonlViewer.vue) — a truncated one-line preview per\n * record, click to expand the pretty-printed value, with a render/source toggle. Prefer `mime`, fall back to\n * the extension; a concrete non-jsonl media mime rules it out.\n */\nexport function isJsonlFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (JSONL_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return JSONL_EXT.has(fileExt(name))\n}\n\n// ZIP archive MIME types, including the JVM/Android packages that ARE zip under the hood (.jar/.war/.ear/\n// .apk/.aar). Office docs / epub are ALSO zips but have dedicated viewers, so they're matched by their own\n// detectors earlier and never reach here.\nconst ZIP_MIME = new Set([\n 'application/zip',\n 'application/x-zip',\n 'application/x-zip-compressed',\n 'multipart/x-zip',\n 'application/java-archive', // .jar / .war / .ear all commonly carry this\n 'application/x-java-archive',\n 'application/vnd.android.package-archive', // .apk\n])\n// Extensions we browse as a zip: the plain .zip plus the JVM/Android archive family (all real zip containers,\n// so the same in-browser decompressor + tree browser handles them). NOT docx/xlsx/pptx/epub — those are zips\n// too but route to their own viewers (matched before isZipFile in FileChip).\nconst ZIP_EXT = new Set(['zip', 'jar', 'war', 'ear', 'apk', 'aar'])\n\n/**\n * Is this a ZIP archive (incl. .jar/.war/.ear/.apk/.aar)? Such files open in the side viewer's archive\n * browser, which decompresses them in-browser and lists their contents. Prefer `mime`, fall back to the\n * extension (same precedence as {@link isTextFile}); a concrete non-zip mime settles it, blank/octet-stream\n * defers to the extension.\n */\nexport function isZipFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (ZIP_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return ZIP_EXT.has(fileExt(name))\n}\n\n// 7z archive MIME types. As with ZIP we key off *.7z (the extension is the reliable signal); a 7z is\n// never a zip-under-the-hood container, so there's no docx/jar-style overlap to worry about.\nconst SEVEN_ZIP_MIME = new Set(['application/x-7z-compressed', 'application/x-7z'])\n\n/**\n * Is this a 7z archive? Such files open in the side viewer's 7z browser, which decompresses them\n * in-browser (bundled LZMA decoder — browsers have no native one) and lists their contents. Prefer\n * `mime`, fall back to the extension (same precedence as {@link isZipFile}).\n */\nexport function isSevenZipFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (SEVEN_ZIP_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return fileExt(name) === '7z'\n}\n\n// tar-family MIME types (the compressed wrappers + bare tar). The reliable signal is usually the extension;\n// the stored mime is often octet-stream. We DON'T claim `application/gzip` etc. blindly — they're matched\n// here, but a lone `.svgz`/unknown `.gz` still keys off the extension set below.\nconst TARBALL_MIME = new Set([\n 'application/x-tar',\n 'application/gzip',\n 'application/x-gzip',\n 'application/x-gtar',\n 'application/x-bzip2',\n 'application/x-bzip',\n 'application/x-xz',\n 'application/x-brotli',\n])\n// Extensions we route to the tar-family browser: bare tar, the gzip/xz/bzip2/brotli wrappers, and a lone\n// compressed file (`foo.json.gz`). `fileExt` returns only the last segment, so `.tar.gz` arrives as `gz`.\nconst TARBALL_EXT = new Set([\n 'tar',\n 'gz',\n 'tgz',\n 'taz',\n 'xz',\n 'txz',\n 'bz2',\n 'tbz2',\n 'tbz',\n 'tb2',\n 'br',\n 'tbr',\n])\n\n/**\n * Is this a tar-family archive — `.tar`, a gzip/xz/bzip2/brotli wrapper (`.tar.gz`/`.tgz`/`.tar.xz`/`.txz`/\n * `.tar.bz2`/`.tbz2`/`.tar.br`), or a lone compressed file (`.gz`/`.xz`/`.bz2`/`.br`)? Such files open in the\n * side viewer's archive browser, which decompresses them in-browser (native gzip + the project's LZMA2/bzip2\n * decoders + a WASM Brotli decoder, see lib/tarball.ts) and lists their contents. Prefer `mime`, fall back to\n * the extension (same precedence as {@link isZipFile}); a concrete non-archive media/text mime rules it out.\n */\nexport function isTarballFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (TARBALL_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return TARBALL_EXT.has(fileExt(name))\n}\n\n// RAR archive MIME types. As with the other archives the extension (.rar) is the reliable signal.\nconst RAR_MIME = new Set([\n 'application/vnd.rar',\n 'application/x-rar-compressed',\n 'application/x-rar',\n])\n\n/**\n * Is this a RAR archive? Such files open in the side viewer's archive browser, which decompresses them\n * in-browser via the WASM unrar port (see lib/rar.ts) and lists their contents. Prefer `mime`, fall back to\n * the extension (same precedence as {@link isZipFile}).\n */\nexport function isRarFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (RAR_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return fileExt(name) === 'rar'\n}\n\n// DICOM medical-image MIME types. The reliable signal is usually the extension (.dcm/.dicom); the stored\n// mime is often octet-stream. `application/dicom` is the registered type when present.\nconst DICOM_MIME = new Set(['application/dicom', 'application/dicom+json', 'image/dicom'])\n\n/**\n * Is this a DICOM (.dcm) medical image? Such files open in the side viewer's DICOM viewer, which parses +\n * decodes them in-browser (dicom-parser + bundled WASM codecs for JPEG2000/JPEG-LS, see lib/dicom*) and\n * renders to a canvas with window/level. Prefer `mime`, fall back to the extension; a concrete non-DICOM\n * media/text mime rules it out, blank/octet-stream defers to the extension.\n */\nexport function isDicomFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (DICOM_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n (m.startsWith('image/') && m !== 'image/dicom') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n const ext = fileExt(name)\n return ext === 'dcm' || ext === 'dicom'\n}\n\n// Raster-image / video extensions for inline preview: images get a hover preview card + click-to-zoom\n// lightbox, videos a click-to-play modal. SVG is deliberately NOT here — it opens in the side viewer with a\n// render/source toggle (see {@link isSvgFile}), since reading its markup is often as useful as the picture.\nconst IMAGE_EXT = new Set([\n 'png',\n 'jpg',\n 'jpeg',\n 'gif',\n 'webp',\n 'bmp',\n 'ico',\n 'avif',\n 'apng',\n 'jfif',\n])\nconst VIDEO_EXT = new Set(['mp4', 'm4v', 'webm', 'ogv', 'mov', 'mkv', 'avi'])\n\n/**\n * Is this a raster image we can preview inline (a hover card on its file chip, or full-size in the lightbox)?\n * `<img src=download-url>` works despite the endpoint's octet-stream/attachment/nosniff (those don't bind\n * `<img>` subresources). SVG is excluded (it goes to the side viewer). Prefer `mime`, fall back to extension.\n */\nexport function isImageFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (m === 'image/svg+xml') return false // SVG → side viewer, not the lightbox\n if (m.startsWith('image/')) return true\n if (m.startsWith('text/') || m.startsWith('video/') || m.startsWith('audio/')) return false\n }\n return IMAGE_EXT.has(fileExt(name))\n}\n\n// Raster image formats a plain <img> can't decode — routed to the side viewer's decoding image viewer\n// (RasterViewer.vue): TIFF (utif2), PSD (ag-psd), HEIC/HEIF (heic2any), and camera RAW (best-effort embedded\n// JPEG, see lib/rawPreview.ts). NOTE bmp/ico/avif are NOT here — modern browsers decode those natively, so\n// they stay on the normal image/lightbox path (see {@link isImageFile}).\nexport type RasterKind = 'tiff' | 'psd' | 'heic' | 'raw'\nconst TIFF_EXT = new Set(['tif', 'tiff'])\nconst PSD_EXT = new Set(['psd', 'psb'])\nconst HEIC_EXT = new Set(['heic', 'heif', 'hif'])\n// Common camera RAW extensions (Canon / Nikon / Sony / Adobe / Fuji / Panasonic / Olympus / Samsung / Pentax / Sigma).\nconst RAW_EXT = new Set([\n 'cr2',\n 'cr3',\n 'crw',\n 'nef',\n 'nrw',\n 'arw',\n 'sr2',\n 'srf',\n 'dng',\n 'raf',\n 'rw2',\n 'orf',\n 'srw',\n 'pef',\n 'x3f',\n])\n\n/**\n * Which decoding image viewer (if any) should open this file? Returns the format kind for TIFF / PSD /\n * HEIC / camera-RAW (so RasterViewer can pick its decoder), or null. Prefer `mime`, fall back to the\n * extension. Must be consulted in FileChip BEFORE {@link isImageFile} — a `.tiff` carries `image/tiff`,\n * which would otherwise send it to the lightbox where a plain <img> can't decode it.\n */\nexport function rasterImageKind(name: string, mime?: string | null): RasterKind | null {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (m === 'image/tiff' || m === 'image/x-tiff') return 'tiff'\n if (\n m === 'image/vnd.adobe.photoshop' ||\n m === 'image/x-photoshop' ||\n m === 'application/x-photoshop' ||\n m === 'image/psd'\n ) {\n return 'psd'\n }\n if (m.startsWith('image/heic') || m.startsWith('image/heif')) return 'heic'\n }\n const ext = fileExt(name)\n if (TIFF_EXT.has(ext)) return 'tiff'\n if (PSD_EXT.has(ext)) return 'psd'\n if (HEIC_EXT.has(ext)) return 'heic'\n if (RAW_EXT.has(ext)) return 'raw'\n return null\n}\n\n// E-book formats routed to the side viewer's e-book reader (EbookViewer.vue): EPUB (epub.js), FictionBook\n// (.fb2, hand-rolled), MOBI/AZW3 (best-effort text extraction), and DjVu (.djvu — not previewable, shows a\n// hint). EPUB is a zip and FB2 is XML, but neither collides with the zip/text routes (we only claim these\n// extensions). DjVu carries an `image/*` mime, so like the raster formats this must beat {@link isImageFile}.\nexport type EbookKind = 'epub' | 'fb2' | 'mobi' | 'djvu'\n\n/**\n * Which e-book reader (if any) should open this file — 'epub' / 'fb2' / 'mobi' (also .azw3/.azw/.prc) /\n * 'djvu', or null. Prefer `mime`, fall back to the extension. Consulted in FileChip BEFORE the generic\n * text route (FB2 is XML) and BEFORE {@link isImageFile} (DjVu's mime is image/vnd.djvu).\n */\nexport function ebookKind(name: string, mime?: string | null): EbookKind | null {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (m === 'application/epub+zip') return 'epub'\n if (m === 'application/x-fictionbook+xml' || m === 'application/fb2+xml') return 'fb2'\n if (m === 'application/x-mobipocket-ebook' || m === 'application/vnd.amazon.ebook')\n return 'mobi'\n if (m === 'image/vnd.djvu' || m === 'image/x-djvu') return 'djvu'\n }\n const ext = fileExt(name)\n if (ext === 'epub') return 'epub'\n if (ext === 'fb2') return 'fb2'\n if (ext === 'mobi' || ext === 'azw' || ext === 'azw3' || ext === 'prc') return 'mobi'\n if (ext === 'djvu' || ext === 'djv') return 'djvu'\n return null\n}\n\n/**\n * Is this an SVG? It opens in the side viewer with a render/source toggle (like HTML) — the render view shows\n * the picture, the source view its XML. Prefer `mime`, fall back to the extension.\n */\nexport function isSvgFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') return m === 'image/svg+xml'\n return fileExt(name) === 'svg'\n}\n\n/**\n * Is this a video we can play in the modal preview? Unlike images, a direct `<video src=download-url>` can't\n * decode (octet-stream + nosniff), so the lightbox fetches the bytes and plays them from a typed blob. Prefer\n * `mime`, fall back to the extension.\n */\nexport function isVideoFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (m.startsWith('video/')) return true\n if (m.startsWith('text/') || m.startsWith('image/') || m.startsWith('audio/')) return false\n }\n return VIDEO_EXT.has(fileExt(name))\n}\n\n/**\n * Is this an HTML document? Such files get a render/source toggle in the side viewer — the rendered\n * view runs the markup in a SANDBOXED iframe (see FileViewerPanel). Prefer `mime`, fall back to the\n * extension (same precedence as {@link isTextFile}); never trust an octet-stream/blank mime.\n */\nexport function isHtmlFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n return m === 'text/html' || m === 'application/xhtml+xml'\n }\n const ext = fileExt(name)\n return ext === 'html' || ext === 'htm'\n}\n\n// Native binary objects the symbol browser parses in-browser: PE (Windows) and ELF (Unix). PE covers the\n// executable family (.exe/.dll/.sys/…); ELF covers shared objects, kernel modules and relocatables.\nconst PE_EXTS = ['exe', 'dll', 'sys', 'ocx', 'cpl', 'scr', 'efi', 'drv', 'mui', 'node']\nconst ELF_EXTS = ['elf', 'ko', 'o']\n\n/**\n * Is this a native binary object — a PE (`.exe`/`.dll`/…) or an ELF (`.so`, incl. versioned `libfoo.so.1.2.3`,\n * plus `.elf`/`.ko`/`.o`)? Such files open in the side viewer's symbol browser, which parses them in-browser\n * (see lib/binobj.ts) — listing function symbols and, for PE, executable metadata (version info + icon).\n * Detection is extension-driven: the stored mime is unreliable (usually `application/octet-stream`), so we only\n * rule it out for clearly-textual / media mimes.\n */\nexport function isBinaryObjFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n const ext = fileExt(name)\n if (PE_EXTS.includes(ext) || ELF_EXTS.includes(ext)) return true\n // Shared objects, including versioned names like libfoo.so.1.2.3.\n return /\\.so(\\.\\d+)*$/.test((name || '').toLowerCase())\n}\n\n// E-language (易语言) project files: `.e` source and `.ec` module — binary containers parsed + decompiled\n// entirely in-browser (see lib/epl + EplViewer.vue). The stored mime is unreliable (usually octet-stream),\n// so detection is extension-driven; a clearly textual / media mime rules it out (a `.e` is never text/*).\n/**\n * Is this an e-language `.e` / `.ec` project file? Such files open in the side viewer's e-language viewer,\n * which parses the binary section format and decompiles each method back to readable 易语言 source text\n * (hand-rolled port of OpenEpl/EProjectFile, see lib/epl). Extension-driven; a concrete text/media mime rules\n * it out. The viewer validates the file magic and shows a friendly message for encrypted / non-EPL bytes.\n */\nexport function isEplFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n const ext = fileExt(name)\n return ext === 'e' || ext === 'ec'\n}\n\n// Java bytecode (`.class`) — a big-endian structural container parsed in-browser (see lib/classfile.ts +\n// ClassViewer.vue), listing fields + methods (NOT decompiled). The stored mime is unreliable (usually\n// octet-stream); detection is extension-driven, with the registered JVM mimes also confirming it. A clearly\n// textual / media mime rules it out (a `.class` is never text/*).\nconst JAVA_CLASS_MIME = new Set([\n 'application/java-vm',\n 'application/x-java-vm',\n 'application/java',\n 'application/x-java-class',\n 'application/java-byte-code',\n])\n\n/**\n * Is this a Java `.class` bytecode file? Such files open in the side viewer's class viewer, which parses the\n * structure in-browser (see lib/classfile.ts) — the class signature plus its fields and methods (a lightweight\n * `javap`; no decompilation of method bodies). The viewer validates the 0xCAFEBABE magic and shows a friendly\n * message otherwise. Extension-driven (the stored mime is unreliable); a concrete text/media mime rules it out.\n */\nexport function isJavaClassFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (JAVA_CLASS_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return fileExt(name) === 'class'\n}\n\n// JVM heap dump (`.hprof`) — a big-endian record stream stream-parsed in-browser (see lib/hprof.ts +\n// HprofViewer.vue) into a class histogram + per-class object drill-in. The stored mime is unreliable (usually\n// octet-stream); detection is extension-driven, with the registered java hprof mime also confirming it. A\n// clearly textual / media mime rules it out (an .hprof is never text/*).\nconst HPROF_MIME = new Set(['application/vnd.java.hprof', 'application/x-hprof'])\n\n/**\n * Is this a JVM heap dump (`.hprof`)? Such files open in the side viewer's HPROF viewer, which STREAM-parses the\n * dump in-browser (see lib/hprof.ts) — a class histogram (instances + shallow size) plus drill-in to one class's\n * objects with decoded fields, paginated + filterable so a multi-GB dump stays memory-bounded. Extension-driven\n * (the stored mime is unreliable); a concrete text/media mime rules it out.\n */\nexport function isHprofFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (HPROF_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return fileExt(name) === 'hprof'\n}\n\n// Windows Installer database (`.msi`, plus the `.msp` patch / `.msm` merge module) — an OLE2 compound file\n// (NOT a PE; see isBinaryObjFile), parsed in-browser (see lib/msi.ts + MsiViewer.vue) to surface the product\n// identity + summary info. The stored mime is unreliable (usually octet-stream); detection is extension-driven,\n// with the registered MSI mime also confirming it. A clearly textual / media mime rules it out.\nconst MSI_MIME = new Set(['application/x-msi', 'application/vnd.ms-msi'])\nconst MSI_EXT = new Set(['msi', 'msp', 'msm'])\n\n/**\n * Is this a Windows Installer `.msi` (or `.msp`/`.msm`)? Such files open in the side viewer's MSI viewer,\n * which parses the OLE compound-file structure in-browser (see lib/msi.ts) — the product name/version/codes\n * from the Property table plus the SummaryInformation. Prefer `mime`, fall back to the extension; a concrete\n * text/media mime rules it out.\n */\nexport function isMsiFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (MSI_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return MSI_EXT.has(fileExt(name))\n}\n\n// Windows app package (`.appx`/`.msix`, and the `.appxbundle`/`.msixbundle` bundles) — an OPC ZIP archive\n// (NOT a PE, NOT an MSI), parsed in-browser (see lib/appx.ts + AppxViewer.vue) to surface the manifest\n// identity / dependencies / capabilities. An APPX is a zip under the hood, so this MUST be consulted BEFORE\n// the generic zip route (an APPX served as application/zip would otherwise open as a plain archive).\nconst APPX_MIME = new Set([\n 'application/appx',\n 'application/msix',\n 'application/appxbundle',\n 'application/msixbundle',\n 'application/vnd.ms-appx',\n 'application/windows.appx',\n])\nconst APPX_EXT = new Set(['appx', 'msix', 'appxbundle', 'msixbundle'])\n\n/**\n * Is this a Windows app package (`.appx`/`.msix`/`.appxbundle`/`.msixbundle`)? Such files open in the side\n * viewer's APPX viewer, which unzips the manifest in-browser (see lib/appx.ts) and shows the package identity,\n * dependencies, capabilities and applications. Prefer `mime`, fall back to the extension; a concrete text/media\n * mime rules it out. Must beat {@link isZipFile} (an APPX is a zip container).\n */\nexport function isAppxFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (APPX_MIME.has(m)) return true\n if (m.startsWith('text/') || m.startsWith('image/') || m.startsWith('audio/')) return false\n }\n return APPX_EXT.has(fileExt(name))\n}\n\n// Word document (OOXML) MIME. Only the modern `.docx` (a zip-under-the-hood OOXML package) is renderable —\n// the legacy binary `.doc` is NOT supported by the in-browser renderer (docx-preview), so it's excluded.\nconst DOCX_MIME = new Set([\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml',\n])\n\n/**\n * Is this a Word `.docx` document? Such files open in the side viewer's Word viewer, which unpacks +\n * renders the OOXML to HTML in-browser (docx-preview, see DocxViewer.vue) — a paginated, paper-like\n * preview. Prefer `mime`, fall back to the extension; a concrete non-docx media/text mime rules it out,\n * blank/octet-stream defers to the extension. Legacy `.doc` is deliberately NOT matched (unsupported).\n */\nexport function isDocxFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (DOCX_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return fileExt(name) === 'docx'\n}\n\n// Excel spreadsheet (OOXML) MIME. Only the modern zip-under-the-hood `.xlsx`/`.xlsm` is renderable (exceljs\n// parses SpreadsheetML); the legacy binary `.xls` is NOT supported, so it's excluded.\nconst XLSX_MIME = new Set([\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml',\n 'application/vnd.ms-excel.sheet.macroenabled.12',\n])\n\n/**\n * Is this an Excel `.xlsx` / `.xlsm` spreadsheet? Such files open in the side viewer's spreadsheet viewer,\n * which unpacks + renders the workbook to HTML tables in-browser (exceljs, see XlsxViewer.vue) — one table\n * per sheet with a tab bar. Prefer `mime`, fall back to the extension; a concrete non-xlsx media/text mime\n * rules it out, blank/octet-stream defers to the extension. Legacy `.xls` is deliberately NOT matched.\n */\nexport function isXlsxFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (XLSX_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n const ext = fileExt(name)\n return ext === 'xlsx' || ext === 'xlsm'\n}\n\n// PowerPoint presentation (OOXML) MIME. Only the modern zip-under-the-hood `.pptx`/`.pptm`/`.ppsx` is\n// renderable (@aiden0z/pptx-renderer parses PresentationML); the legacy binary `.ppt` is NOT supported, so\n// it's excluded. `.macroenabled.12` is the lowercased form of the registered `.macroEnabled.12` types.\nconst PPTX_MIME = new Set([\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml',\n 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',\n 'application/vnd.ms-powerpoint.presentation.macroenabled.12',\n 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',\n])\n\n/**\n * Is this a PowerPoint `.pptx` / `.pptm` / `.ppsx` presentation? Such files open in the side viewer's slide\n * viewer, which unpacks + renders the deck to HTML/SVG in-browser (@aiden0z/pptx-renderer, see PptxViewer.vue)\n * — a scrollable list of slides. Prefer `mime`, fall back to the extension; a concrete non-pptx media/text\n * mime rules it out, blank/octet-stream defers to the extension. Legacy `.ppt` is deliberately NOT matched.\n */\nexport function isPptxFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (PPTX_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n const ext = fileExt(name)\n return ext === 'pptx' || ext === 'pptm' || ext === 'ppsx'\n}\n\n// Rich Text Format MIME types. The reliable signal is the extension (.rtf); the stored mime is often octet-\n// stream or text/plain. `application/rtf` is the registered type, `text/rtf` a common variant.\nconst RTF_MIME = new Set(['application/rtf', 'text/rtf', 'application/x-rtf', 'text/richtext'])\n\n/**\n * Is this a Rich Text Format `.rtf` document? Such files open in the side viewer's rich-text viewer, which\n * interprets the RTF in-browser (hand-rolled, see lib/rtf.ts + RtfViewer.vue) — bold/italic/underline, colors,\n * fonts, alignment and unicode rendered onto a paper-like sheet. Prefer `mime`, fall back to the extension; a\n * concrete non-rtf media mime rules it out, blank/octet-stream/text defers to the extension (an .rtf is often\n * mislabeled text/plain). Must be consulted BEFORE the generic text route so it renders, not shows raw markup.\n */\nexport function isRtfFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (RTF_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return fileExt(name) === 'rtf'\n}\n\n// PDF MIME types. The reliable signal is usually the extension (.pdf); `application/pdf` is the registered\n// type, `application/x-pdf` an older variant.\nconst PDF_MIME = new Set(['application/pdf', 'application/x-pdf', 'application/acrobat'])\n\n/**\n * Is this a PDF document? Such files open in the side viewer's PDF viewer, which renders each page to a\n * canvas in-browser (pdfjs-dist, see PdfViewer.vue) — a vertically scrollable list of pages. Prefer `mime`,\n * fall back to the extension; a concrete non-pdf media/text mime rules it out, blank/octet-stream defers to\n * the extension.\n */\nexport function isPdfFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (PDF_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return fileExt(name) === 'pdf'\n}\n\n// Outline / mind-map MIME types. The reliable signal is the extension (.opml/.mm); these MIME types appear\n// occasionally. `.mm` shares its extension with Objective-C++ source, so a match here is only a *candidate* —\n// the side panel content-sniffs the loaded text (looksLikeMindMap) before drawing the diagram.\nconst MINDMAP_MIME = new Set([\n 'text/x-opml',\n 'text/x-opml+xml',\n 'application/xml+opml',\n 'application/x-freemind',\n 'application/x-freeplane',\n])\n\n/**\n * Is this an outline / mind-map file — OPML (.opml) or FreeMind / Freeplane (.mm)? Such files open in the side\n * viewer with a render/source toggle: render draws an interactive mind map (see MindMapView.vue), source shows\n * the XML. Detection is extension-driven (the stored mime is unreliable); a concrete media mime rules it out.\n * NOTE: `.mm` is also the Objective-C++ source extension — the panel additionally sniffs the loaded text\n * (looksLikeMindMap) before rendering the diagram, so a non-mind-map `.mm` just reads as highlighted source.\n */\nexport function isMindMapFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (MINDMAP_MIME.has(m)) return true\n if (\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/') ||\n m === 'text/html'\n ) {\n return false\n }\n }\n const ext = fileExt(name)\n return ext === 'opml' || ext === 'mm'\n}\n\n// XMind workbook (.xmind) — a ZIP container (content.json modern / content.xml legacy) rendered as a mind map\n// (see lib/xmind.ts + XmindViewer.vue, reusing MindMapView). It's a zip under the hood, so this MUST be consulted\n// BEFORE the generic zip route. Extension-driven (the stored mime is unreliable); the workbook mimes confirm it.\nconst XMIND_MIME = new Set([\n 'application/vnd.xmind.workbook',\n 'application/x-xmind',\n 'application/xmind',\n])\n\n/**\n * Is this an XMind workbook (`.xmind`)? Such files open in the side viewer's XMind viewer, which unzips the\n * `content.json` / `content.xml` in-browser (see lib/xmind.ts) and renders it as an interactive mind map (shared\n * MindMapView). Prefer `mime`, fall back to the extension; a concrete media/text mime rules it out. Must beat\n * {@link isZipFile} (an XMind is a zip container).\n */\nexport function isXmindFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (XMIND_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return fileExt(name) === 'xmind'\n}\n\n// DXF (AutoCAD Drawing Exchange Format) — an ASCII CAD drawing, rendered as an SVG (see lib/dxf.ts +\n// DxfViewer.vue). `.dxf` is textual, so this MUST be consulted BEFORE the generic text route so it opens as a\n// drawing, not raw source. NOTE: `.dwg` (the binary AutoCAD format) is deliberately NOT claimed — it's a\n// different, unsupported format. Extension-driven; the registered dxf mimes also confirm it.\nconst DXF_MIME = new Set(['image/vnd.dxf', 'application/dxf', 'application/x-dxf', 'drawing/x-dxf'])\n\n/**\n * Is this a DXF (`.dxf`) CAD drawing? Such files open in the side viewer's DXF viewer, which parses the ASCII\n * DXF in-browser (see lib/dxf.ts) and draws its entities as an SVG (pan/zoom + layer toggles). Prefer `mime`,\n * fall back to the extension; a concrete non-dxf media mime rules it out (a `.dxf` is often served text/plain\n * or octet-stream, so the extension is the reliable signal).\n */\nexport function isDxfFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (DXF_MIME.has(m)) return true\n if (m.startsWith('audio/') || m.startsWith('video/') || m.startsWith('image/')) {\n if (!DXF_MIME.has(m)) return false\n }\n }\n return fileExt(name) === 'dxf'\n}\n\n// Email (.eml) MIME types. `message/rfc822` is the registered type for a raw RFC 822 message; the stored\n// mime is often octet-stream or even text/plain, so the extension (.eml) is the reliable signal. We do NOT\n// rule out text/* here (some servers label .eml as text/plain) — only clearly-binary media families.\nconst EML_MIME = new Set(['message/rfc822', 'application/eml', 'text/eml', 'message/global'])\n\n/**\n * Is this an email `.eml` (RFC 822 / MIME) message? Such files open in the side viewer's email viewer, which\n * parses the MIME tree in-browser (see lib/eml.ts + EmlViewer.vue) — a header card, the best body (HTML in a\n * sandboxed iframe, plain-text fallback), and the attachments. Prefer `mime`, fall back to the extension; a\n * concrete non-eml media mime rules it out, blank/octet-stream/text defers to the extension.\n */\nexport function isEmlFile(name: string, mime?: string | null): boolean {\n // A .mht/.mhtml web archive can also be served as message/rfc822, but it's MHT, not an email (see isMhtFile).\n const ext = fileExt(name)\n if (ext === 'mht' || ext === 'mhtml') return false\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (EML_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return ext === 'eml'\n}\n\n/**\n * Is this a web archive `.mht`/`.mhtml` (RFC 2557 MIME container)? Such files open in the side viewer's web-\n * archive viewer, which parses the `multipart/related` container in-browser (see lib/mht.ts + MhtViewer.vue)\n * and renders the saved page — resources rewritten to blob URLs — in a sandboxed iframe. Extension-driven (the\n * stored mime is unreliable); the telltale container mimes (multipart/related, application/x-mimearchive)\n * also confirm it.\n */\nexport function isMhtFile(name: string, mime?: string | null): boolean {\n const ext = fileExt(name)\n if (ext === 'mht' || ext === 'mhtml') return true\n const m = (mime || '').toLowerCase().trim()\n return m === 'multipart/related' || m === 'application/x-mimearchive'\n}\n\n// HAR (HTTP Archive) MIME types. A .har is JSON under the hood, so it's often served as application/json or\n// octet-stream — the extension (.har) is the reliable signal. The registered type is application/json; some\n// tools use application/x-har.\nconst HAR_MIME = new Set(['application/har+json', 'application/x-har'])\n\n/**\n * Is this a HAR (`.har`, HTTP Archive 1.2) capture? Such files open in the side viewer's HAR viewer, which\n * parses the JSON archive in-browser (hand-rolled, see lib/har.ts + HarViewer.vue) — a filterable request\n * list with per-request headers / query / cookies / bodies. Extension-driven (a .har is JSON, so the stored\n * mime is usually application/json or octet-stream); a concrete non-har media mime rules it out. Must be\n * checked BEFORE the generic text route (a .har served as application/json would otherwise read as JSON source).\n */\nexport function isHarFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (HAR_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return fileExt(name) === 'har'\n}\n\n// SQLite database MIME types. The reliable signal is the extension; the stored mime is usually octet-stream.\nconst SQLITE_MIME = new Set([\n 'application/x-sqlite3',\n 'application/vnd.sqlite3',\n 'application/x-sqlite',\n])\n// Extensions we route to the SQLite viewer: the conventional database suffixes.\nconst SQLITE_EXT = new Set(['sqlite', 'sqlite3', 'db', 'db3', 'sqlitedb'])\n\n/**\n * Is this a SQLite database (`.sqlite`/`.db`/`.sqlite3`/…)? Such files open in the side viewer's SQLite viewer,\n * which loads them with sql.js (WASM) in-browser (see lib/sqlite.ts + SqliteViewer.vue) — a table picker, a\n * paginated data grid, and a SQL console. Prefer `mime`, fall back to the extension; a concrete textual/media\n * mime rules it out (`.db` is a generic suffix, so a clearly-text mime means it isn't a database).\n */\nexport function isSqliteFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (SQLITE_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return SQLITE_EXT.has(fileExt(name))\n}\n\n// Certificate / key formats routed to the side viewer's certificate visualizer (parsed in-browser, hand-rolled\n// ASN.1/X.509, see lib/asn1.ts + lib/x509.ts + CertViewer.vue): PEM (.pem/.crt/.cer/.csr/.key, also PKCS#7\n// bundles) and raw DER (.der/.cer). The viewer fetches the BYTES (binary-safe), so `.der` isn't mangled.\n// Detection is extension-driven (the stored mime is unreliable); a few registered cert mimes also confirm it.\n// NOTE: PKCS#12 (`.p12`/`.pfx`) is a KEYSTORE, not a bare cert — routed to the keystore viewer ({@link isJksFile}).\nconst CERT_EXT = new Set(['pem', 'crt', 'cer', 'der', 'csr', 'req', 'key', 'p7b', 'p7c'])\nconst CERT_MIME = new Set([\n 'application/x-x509-ca-cert',\n 'application/x-x509-user-cert',\n 'application/x-x509-cert',\n 'application/pkix-cert',\n 'application/pkix-crl',\n 'application/x-pem-file',\n 'application/pkcs8',\n 'application/pkcs10',\n 'application/x-pkcs7-certificates',\n])\n\n/**\n * Is this a certificate / key file (`.pem`/`.crt`/`.cer`/`.der`/`.csr`/`.key`/`.p7b`/…)? Such files open\n * in the side viewer's certificate visualizer, which parses the ASN.1 / X.509 in-browser (see lib/x509.ts +\n * CertViewer.vue) — subject/issuer, validity, key + signature algorithms, SANs, fingerprint, and a raw ASN.1\n * structure tree. Prefer `mime`, fall back to the extension; a concrete non-cert media/text mime rules it out.\n * PKCS#12 (`.p12`/`.pfx`) is handled by the keystore viewer ({@link isJksFile}), not here.\n */\nexport function isCertFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (CERT_MIME.has(m)) return true\n // Don't rule out text/* — a PEM (.pem/.crt/.key) is often served as text/plain; the extension settles it.\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return CERT_EXT.has(fileExt(name))\n}\n\n// Keystore formats routed to the side viewer's keystore viewer (parsed in-browser, hand-rolled — see lib/jks.ts,\n// lib/pkcs12.ts + JksViewer.vue): the Sun JKS/JCEKS binary format (`.jks`/`.keystore`/`.ks`/`.truststore`/`.jceks`,\n// magic 0xFEEDFEED / 0xCECECECE) AND PKCS#12 / PFX (`.p12`/`.pfx`). The `.jks` extension is claimed even though\n// modern keytool writes PKCS#12 into it — the viewer sniffs the actual bytes and picks the right parser, so a\n// mislabeled file still opens. Detection is extension-driven (the stored mime is unreliable, usually\n// octet-stream); a concrete text/media mime rules it out (a keystore is never text/*).\nconst JKS_MIME = new Set([\n 'application/x-java-keystore',\n 'application/x-java-jce-keystore',\n 'application/x-pkcs12',\n 'application/pkcs12',\n])\nconst JKS_EXT = new Set(['jks', 'keystore', 'ks', 'jceks', 'truststore', 'p12', 'pfx'])\n\n/**\n * Is this a keystore — Java JKS/JCEKS (`.jks`/`.keystore`/`.ks`/`.jceks`/`.truststore`) or PKCS#12 (`.p12`/\n * `.pfx`)? Such files open in the side viewer's keystore viewer, which sniffs the format and parses it in-browser\n * (see lib/jks.ts / lib/pkcs12.ts) — the entry inventory (aliases / kinds / dates), each entry's X.509 chain,\n * and, with a password, store-integrity verification + private-key decryption. Prefer `mime`, fall back to the\n * extension; a concrete text/media mime rules it out.\n */\nexport function isJksFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (JKS_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return JKS_EXT.has(fileExt(name))\n}\n\n// Geodata formats routed to the side viewer's offline SVG map (parsed in-browser, see lib/geo.ts +\n// GeoViewer.vue): GeoJSON (.geojson), KML (.kml), GPX (.gpx). `.geojson`/`.kml`/`.gpx` are also textual, so\n// this must be consulted BEFORE the generic text route (in FileChip) so they open as a map, not raw source.\n// Extension-driven; we deliberately DON'T claim bare `.json` (most JSON isn't geodata) — only `.geojson`.\nconst GEO_EXT = new Set(['geojson', 'kml', 'gpx'])\nconst GEO_MIME = new Set([\n 'application/geo+json',\n 'application/vnd.geo+json',\n 'application/vnd.google-earth.kml+xml',\n 'application/gpx+xml',\n 'application/x-gpx+xml',\n])\n\n/**\n * Is this a geodata file (`.geojson`/`.kml`/`.gpx`)? Such files open in the side viewer's map viewer, which\n * parses the geometry in-browser (see lib/geo.ts + GeoViewer.vue) and draws it as an OFFLINE SVG map (no tile\n * server) with pan/zoom and a render/source toggle. Prefer `mime`, fall back to the extension; a concrete\n * non-geo media mime rules it out.\n */\nexport function isGeoFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (GEO_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return GEO_EXT.has(fileExt(name))\n}\n\n// Subtitle / caption formats routed to the side viewer's subtitle transcript (parsed in-browser, see\n// lib/subtitle.ts + SubtitleViewer.vue): SubRip (.srt), WebVTT (.vtt), SubStation Alpha (.ass/.ssa).\n// `.srt`/`.vtt` are ALSO plain text, so this must be consulted BEFORE the generic text route (in FileChip)\n// so they open as a cue list, not raw source. Extension-driven (the stored mime is unreliable).\nconst SUBTITLE_EXT = new Set(['srt', 'vtt', 'ass', 'ssa'])\nconst SUBTITLE_MIME = new Set([\n 'application/x-subrip',\n 'application/x-subrip+srt',\n 'text/vtt',\n 'text/x-ssa',\n 'text/x-ass',\n])\n\n/**\n * Is this a subtitle / caption file (`.srt`/`.vtt`/`.ass`/`.ssa`)? Such files open in the side viewer's\n * subtitle viewer, which parses the cues in-browser (see lib/subtitle.ts + SubtitleViewer.vue) and shows\n * them as a timecoded transcript (with a render/source toggle). Prefer `mime`, fall back to the extension;\n * a concrete non-subtitle media mime rules it out, blank/octet-stream/text defers to the extension.\n */\nexport function isSubtitleFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (SUBTITLE_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return SUBTITLE_EXT.has(fileExt(name))\n}\n\n// 3D model formats routed to the side viewer's model viewer (rendered with three.js, see ModelViewer.vue):\n// STL (.stl), Wavefront OBJ (.obj), PLY (.ply), glTF (.gltf) and binary glTF (.glb). Extension-driven (the\n// stored mime is unreliable; `model/*` mimes also confirm it).\nconst MODEL_EXT = new Set(['stl', 'obj', 'ply', 'glb', 'gltf'])\nconst MODEL_MIME = new Set([\n 'model/gltf-binary',\n 'model/gltf+json',\n 'model/stl',\n 'application/sla',\n 'application/vnd.ms-pki.stl',\n 'model/obj',\n 'model/ply',\n])\n\n/**\n * Is this a 3D model (`.stl`/`.obj`/`.ply`/`.glb`/`.gltf`)? Such files open in the side viewer's model viewer,\n * which renders them with three.js in-browser (see ModelViewer.vue) — auto-framed, lit, with orbit controls.\n * Prefer `mime` (`model/*`), fall back to the extension; a concrete text/image/audio/video mime rules it out.\n */\nexport function isModelFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (MODEL_MIME.has(m) || m.startsWith('model/')) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return MODEL_EXT.has(fileExt(name))\n}\n\n// Audio formats routed to the side viewer's audio player (played via a typed Blob URL through a native\n// <audio> element, since the download endpoint's octet-stream/attachment defeats a direct src — see\n// AudioViewer.vue): mp3/wav/flac/ogg/m4a/aac/opus. `.mid`/`.midi` are included too but can't be PLAYED\n// in-browser (no synth) — the viewer shows them as a piano-roll visualization (best effort).\n// `.amr`/`.awb`/`.silk`/`.slk` (3GPP AMR + Tencent SILK 微信/QQ voice) are also routed here: browsers can't\n// decode them natively, so AudioViewer DECODES the bytes to WAV in-browser (lib/speechAudio.ts, WASM) and\n// plays that. Detection at decode time is by file-header magic (a WeChat `.amr` is often actually SILK).\nconst AUDIO_EXT = new Set([\n 'mp3',\n 'wav',\n 'flac',\n 'ogg',\n 'oga',\n 'm4a',\n 'aac',\n 'opus',\n 'weba',\n 'mid',\n 'midi',\n 'amr',\n 'awb',\n 'silk',\n 'slk',\n])\n\n/**\n * Is this an audio file (mp3/wav/flac/ogg/m4a/aac/opus, a .mid/.midi, or an AMR/SILK voice clip)? Such files\n * open in the side viewer's audio player, which fetches the bytes and plays them via a typed Blob URL (MIDI is\n * visualized as a piano roll, not played; AMR/SILK are decoded to WAV in-browser first). Prefer `mime`\n * (`audio/*`), fall back to the extension; a concrete text/image/video mime rules it out.\n */\nexport function isAudioFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (m.startsWith('audio/')) return true\n if (m.startsWith('text/') || m.startsWith('image/') || m.startsWith('video/')) return false\n }\n return AUDIO_EXT.has(fileExt(name))\n}\n\n// Jupyter Notebook (.ipynb) — JSON under the hood, so often served as application/json or octet-stream; the\n// extension is the reliable signal. The registered type is application/x-ipynb+json. Must be checked BEFORE\n// the generic text route (a .ipynb served as application/json would otherwise read as JSON source).\nconst NOTEBOOK_MIME = new Set(['application/x-ipynb+json', 'application/ipynb+json'])\n\n/**\n * Is this a Jupyter Notebook (`.ipynb`)? Such files open in the side viewer's notebook viewer, which parses\n * the JSON in-browser (hand-rolled, see lib/ipynb.ts + NotebookViewer.vue) — markdown cells via the app's\n * markdown engine, code cells highlighted, outputs (stream / image / rich HTML / error) inline. Extension-\n * driven (a .ipynb is JSON, so the stored mime is usually application/json or octet-stream); a concrete\n * non-notebook media mime rules it out.\n */\nexport function isNotebookFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (NOTEBOOK_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return fileExt(name) === 'ipynb'\n}\n\n// Font formats routed to the side viewer's specimen viewer (loaded with the native FontFace API, which\n// decodes ttf/otf/woff AND woff2 in-browser — no library; see FontViewer.vue): TrueType (.ttf), OpenType\n// (.otf), and the web wrappers (.woff/.woff2). Extension-driven (stored mime is unreliable).\nconst FONT_EXT = new Set(['ttf', 'otf', 'woff', 'woff2'])\nconst FONT_MIME = new Set([\n 'font/ttf',\n 'font/otf',\n 'font/woff',\n 'font/woff2',\n 'font/sfnt',\n 'application/font-woff',\n 'application/font-woff2',\n 'application/x-font-ttf',\n 'application/x-font-otf',\n 'application/x-font-truetype',\n 'application/x-font-opentype',\n 'application/font-sfnt',\n 'application/vnd.ms-opentype',\n])\n\n/**\n * Is this a font file (`.ttf`/`.otf`/`.woff`/`.woff2`)? Such files open in the side viewer's font specimen,\n * which loads them with the native FontFace API in-browser (see FontViewer.vue) — an editable preview line,\n * a size waterfall, and the glyph sets. Prefer `mime`, fall back to the extension; a concrete non-font\n * media/text mime rules it out, blank/octet-stream defers to the extension.\n */\nexport function isFontFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (FONT_MIME.has(m)) return true\n if (\n m.startsWith('text/') ||\n m.startsWith('image/') ||\n m.startsWith('audio/') ||\n m.startsWith('video/')\n ) {\n return false\n }\n }\n return FONT_EXT.has(fileExt(name))\n}\n\n// QQ 音乐加密音频(QMCv2,文件尾内嵌密钥)—— 浏览器内解密后用 <audio> 播放(见 lib/qmc.ts + QmcAudioViewer.vue)。\n// 涵盖 .mflac(FLAC)/.mgg(OGG)及其音质分级变体;这些都带内嵌 EKey,可独立解出。早期 QMCv1 静态密钥格式\n// (.qmcflac/.qmc0…)不在此列(无内嵌密钥)。存储 mime 一律不可信(多为 octet-stream),故按扩展名判定。\nconst QMC_EXT = new Set(['mflac', 'mflac0', 'mflac1', 'mflaca', 'mgg', 'mgg0', 'mgg1', 'mggl'])\n\n/**\n * Is this a QQ Music encrypted audio file (`.mflac`/`.mgg`/…)? Such files open in the side viewer's QMC\n * player, which decrypts the bytes in-browser (see lib/qmc.ts) — recovering the embedded key, undoing the\n * QMCv2 stream cipher, and playing the resulting FLAC/OGG/MP3 via a typed Blob (with a \"download decrypted\"\n * action). Extension-driven (the stored mime is unreliable, usually octet-stream); a clearly textual/media\n * mime rules it out.\n */\nexport function isQmcAudioFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (m.startsWith('text/') || m.startsWith('image/') || m.startsWith('video/')) return false\n }\n return QMC_EXT.has(fileExt(name))\n}\n\n// LRC 歌词(`.lrc`)—— 行式带时间戳歌词,在侧栏渲染成同步歌词列表(见 lib/lrc.ts + LrcViewer.vue)。\n// 这类文件常见 GBK 编码且夹带 HTML 实体,故查看器自取字节、嗅探编码(见 lib/encoding.ts),不能当 UTF-8 文本直读。\nconst LRC_MIME = new Set(['application/lrc', 'text/lrc', 'application/x-lrc'])\n\n/**\n * Is this an LRC lyrics file (`.lrc`)? Such files open in the side viewer's lyrics viewer, which parses the\n * timestamped lines in-browser (see lib/lrc.ts + LrcViewer.vue) and shows a synced lyric list. Extension-\n * driven; a concrete non-text media mime rules it out. NOTE: deliberately routed BEFORE the generic text\n * route (the viewer self-fetches so it can sniff GBK — a `.lrc` read as UTF-8 text would be mojibake).\n */\nexport function isLrcFile(name: string, mime?: string | null): boolean {\n const m = (mime || '').toLowerCase().trim()\n if (m && m !== 'application/octet-stream') {\n if (LRC_MIME.has(m)) return true\n if (m.startsWith('image/') || m.startsWith('audio/') || m.startsWith('video/')) return false\n }\n return fileExt(name) === 'lrc'\n}\n\n/** highlight.js language id for this file, or '' for plain text (no highlighting). */\nexport function hljsLang(name: string, mime?: string | null): string {\n const ext = fileExt(name)\n if (ext && ext in EXT_LANG) return EXT_LANG[ext]\n\n const m = (mime || '').toLowerCase().trim()\n if (m === 'text/markdown' || m === 'application/markdown') return 'markdown'\n if (m === 'application/json' || m === 'application/ld+json') return 'json'\n if (\n m === 'application/xml' ||\n m === 'application/xhtml+xml' ||\n m === 'image/svg+xml' ||\n m === 'text/html'\n ) {\n return 'xml'\n }\n if (m.includes('yaml')) return 'yaml'\n if (m === 'text/css') return 'css'\n if (m === 'application/javascript' || m === 'application/ecmascript') return 'javascript'\n\n const lower = (name || '').toLowerCase()\n if (lower === 'dockerfile') return 'dockerfile'\n if (lower === 'makefile') return 'makefile'\n return ''\n}\n"],"mappings":"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"}
package/dist/lib/fonts.js CHANGED
@@ -2,3 +2,5 @@
2
2
  var e = "var(--font-serif)", t = "var(--font-sans)", n = "var(--font-mono)";
3
3
  //#endregion
4
4
  export { n as MONO, t as SANS, e as SERIF };
5
+
6
+ //# sourceMappingURL=fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.js","names":[],"sources":["../../src/lib/fonts.ts"],"sourcesContent":["/**\n * Font-stack references for inline style objects (render functions).\n * They point at the CSS custom properties declared in tokens.css, so there is a\n * single source of truth and they stay theme-agnostic.\n */\nexport const SERIF = 'var(--font-serif)'\nexport const SANS = 'var(--font-sans)'\nexport const MONO = 'var(--font-mono)'\n"],"mappings":";AAKA,IAAa,IAAQ,qBACR,IAAO,oBACP,IAAO"}
@@ -36,3 +36,5 @@ function r(e) {
36
36
  }
37
37
  //#endregion
38
38
  export { n as formatBytes, t as formatDateTime, e as formatDuration, r as formatSizeCoarse };
39
+
40
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../src/lib/format.ts"],"sourcesContent":["/** Minimal span shape (start/stop as parseable date strings) — kept local so the package carries no app types. */\nexport interface TimelineSpan {\n start?: string | null\n stop?: string | null\n}\n\n/**\n * Format the wall-clock span of a reasoning run (earliest start → latest stop)\n * as \"12s\" / \"1m 5s\". Mirrors the old component's fmtDur.\n */\nexport function formatDuration(items: TimelineSpan[]): string {\n try {\n let start: number | null = null\n let end: number | null = null\n for (const it of items ?? []) {\n if (it.start) {\n const t = Date.parse(it.start)\n if (!Number.isNaN(t) && (start === null || t < start)) start = t\n }\n if (it.stop) {\n const t = Date.parse(it.stop)\n if (!Number.isNaN(t) && (end === null || t > end)) end = t\n }\n }\n if (start === null || end === null || end < start) return ''\n const sec = Math.max(1, Math.round((end - start) / 1000))\n if (sec < 60) return `${sec}s`\n const m = Math.floor(sec / 60)\n const r = sec % 60\n return r ? `${m}m ${r}s` : `${m}m`\n } catch {\n return ''\n }\n}\n\n/** Compact local date-time for listings: \"2026-06-20 14:03\". Empty when the timestamp is missing/zero. */\nexport function formatDateTime(ms: number | null | undefined): string {\n if (!ms) return ''\n const d = new Date(ms)\n if (Number.isNaN(d.getTime())) return ''\n const p = (n: number) => String(n).padStart(2, '0')\n return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`\n}\n\n/** Human-readable byte size: \"512 B\" / \"1.4 KB\" / \"2.3 MB\" (one decimal). */\nexport function formatBytes(n: number | null | undefined): string {\n if (n == null) return ''\n if (n < 1024) return `${n} B`\n if (n < 1048576) return `${(n / 1024).toFixed(1)} KB`\n return `${(n / 1048576).toFixed(1)} MB`\n}\n\n/** Coarser size used for attachment chips: KB rounded to whole numbers. */\nexport function formatSizeCoarse(n: number | null | undefined): string | null {\n if (n == null) return null\n if (n < 1024) return `${n} B`\n if (n < 1048576) return `${(n / 1024).toFixed(0)} KB`\n return `${(n / 1048576).toFixed(1)} MB`\n}\n"],"mappings":";AAUA,SAAgB,EAAe,GAA+B;CAC5D,IAAI;EACF,IAAI,IAAuB,MACvB,IAAqB;EACzB,KAAK,IAAM,KAAM,KAAS,CAAC,GAAG;GAC5B,IAAI,EAAG,OAAO;IACZ,IAAM,IAAI,KAAK,MAAM,EAAG,KAAK;IAC7B,AAAI,CAAC,OAAO,MAAM,CAAC,MAAM,MAAU,QAAQ,IAAI,OAAQ,IAAQ;GACjE;GACA,IAAI,EAAG,MAAM;IACX,IAAM,IAAI,KAAK,MAAM,EAAG,IAAI;IAC5B,AAAI,CAAC,OAAO,MAAM,CAAC,MAAM,MAAQ,QAAQ,IAAI,OAAM,IAAM;GAC3D;EACF;EACA,IAAI,MAAU,QAAQ,MAAQ,QAAQ,IAAM,GAAO,OAAO;EAC1D,IAAM,IAAM,KAAK,IAAI,GAAG,KAAK,OAAO,IAAM,KAAS,GAAI,CAAC;EACxD,IAAI,IAAM,IAAI,OAAO,GAAG,EAAI;EAC5B,IAAM,IAAI,KAAK,MAAM,IAAM,EAAE,GACvB,IAAI,IAAM;EAChB,OAAO,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,GAAG,EAAE;CAClC,QAAQ;EACN,OAAO;CACT;AACF;AAGA,SAAgB,EAAe,GAAuC;CACpE,IAAI,CAAC,GAAI,OAAO;CAChB,IAAM,IAAI,IAAI,KAAK,CAAE;CACrB,IAAI,OAAO,MAAM,EAAE,QAAQ,CAAC,GAAG,OAAO;CACtC,IAAM,KAAK,MAAc,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CAClD,OAAO,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,EAAE,SAAS,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,EAAE,GAAG,EAAE,EAAE,WAAW,CAAC;AAC3G;AAGA,SAAgB,EAAY,GAAsC;CAIhE,OAHI,KAAK,OAAa,KAClB,IAAI,OAAa,GAAG,EAAE,MACtB,IAAI,UAAgB,IAAI,IAAI,KAAA,CAAM,QAAQ,CAAC,EAAE,OAC1C,IAAI,IAAI,QAAA,CAAS,QAAQ,CAAC,EAAE;AACrC;AAGA,SAAgB,EAAiB,GAA6C;CAI5E,OAHI,KAAK,OAAa,OAClB,IAAI,OAAa,GAAG,EAAE,MACtB,IAAI,UAAgB,IAAI,IAAI,KAAA,CAAM,QAAQ,CAAC,EAAE,OAC1C,IAAI,IAAI,QAAA,CAAS,QAAQ,CAAC,EAAE;AACrC"}
@@ -21,3 +21,5 @@ function n(n) {
21
21
  }
22
22
  //#endregion
23
23
  export { n as parseFrontmatter };
24
+
25
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","names":[],"sources":["../../src/lib/frontmatter.ts"],"sourcesContent":["import { load as parseYaml } from 'js-yaml'\n\n/**\n * Split leading YAML frontmatter (a `---` … `---` block at the very top, as used by\n * SKILL.md and friends) from the markdown body.\n *\n * `data` is the parsed mapping (empty when there's no frontmatter or it's malformed /\n * not a mapping); `body` is the rest of the document with the block removed. On any\n * parse failure we fall back to `{ data: {}, body: <original text> }` so nothing is lost.\n */\nexport interface ParsedFrontmatter {\n data: Record<string, unknown>\n body: string\n}\n\n// Opening `---` must be the first line; the block ends at the next line that is exactly\n// `---`. Non-greedy so the *first* closing fence wins. (A leading BOM is stripped below.)\nconst FM_RE = /^---[ \\t]*\\r?\\n([\\s\\S]*?)\\r?\\n---[ \\t]*(?:\\r?\\n|$)/\n\nexport function parseFrontmatter(input: string): ParsedFrontmatter {\n // Tolerate a UTF-8 BOM so the opening fence still anchors to the start.\n const text = input.charCodeAt(0) === 0xfeff ? input.slice(1) : input\n const m = text.match(FM_RE)\n if (!m) return { data: {}, body: text }\n try {\n const parsed = parseYaml(m[1])\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return { data: parsed as Record<string, unknown>, body: text.slice(m[0].length) }\n }\n } catch {\n /* malformed YAML — fall through and render the whole text untouched */\n }\n return { data: {}, body: text }\n}\n"],"mappings":";;AAiBA,IAAM,IAAQ;AAEd,SAAgB,EAAiB,GAAkC;CAEjE,IAAM,IAAO,EAAM,WAAW,CAAC,MAAM,QAAS,EAAM,MAAM,CAAC,IAAI,GACzD,IAAI,EAAK,MAAM,CAAK;CAC1B,IAAI,CAAC,GAAG,OAAO;EAAE,MAAM,CAAC;EAAG,MAAM;CAAK;CACtC,IAAI;EACF,IAAM,IAAS,EAAU,EAAE,EAAE;EAC7B,IAAI,KAAU,OAAO,KAAW,YAAY,CAAC,MAAM,QAAQ,CAAM,GAC/D,OAAO;GAAE,MAAM;GAAmC,MAAM,EAAK,MAAM,EAAE,EAAE,CAAC,MAAM;EAAE;CAEpF,QAAQ,CAER;CACA,OAAO;EAAE,MAAM,CAAC;EAAG,MAAM;CAAK;AAChC"}
package/dist/lib/geo.js CHANGED
@@ -233,3 +233,5 @@ function f(e, t) {
233
233
  }
234
234
  //#endregion
235
235
  export { f as parseGeo };
236
+
237
+ //# sourceMappingURL=geo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.js","names":[],"sources":["../../src/lib/geo.ts"],"sourcesContent":["/**\n * Hand-rolled, zero-dependency parser for the three common geodata formats, normalized to a flat feature\n * list the side viewer can draw as an offline SVG map (no tile server — internal network):\n * - GeoJSON (.geojson/.json): FeatureCollection / Feature / bare geometry; all geometry types incl. Multi* +\n * GeometryCollection. Positions are [lon, lat(, alt)].\n * - KML (.kml): <Placemark> with Point / LineString / Polygon (incl. inside MultiGeometry); `lon,lat,alt`\n * whitespace-separated coordinate tuples.\n * - GPX (.gpx): <wpt> waypoints (points), <rte>/<rtept> routes and <trk>/<trkseg>/<trkpt> tracks (lines);\n * coordinates are `lat`/`lon` attributes.\n *\n * Each geometry is reduced to point / line / polygon primitives (polygon = outer ring only — holes are\n * dropped for the preview). Positions stay as [lon, lat]; the viewer owns the map projection.\n */\n\nexport type GeoKind = 'point' | 'line' | 'polygon'\n\nexport interface GeoGeom {\n kind: GeoKind\n /** [lon, lat] positions: point → 1, line → N, polygon → its outer ring. */\n coords: [number, number][]\n}\n\nexport interface GeoFeature {\n name: string\n geoms: GeoGeom[]\n props: Record<string, string>\n}\n\nexport interface ParsedGeo {\n format: 'geojson' | 'kml' | 'gpx'\n features: GeoFeature[]\n}\n\nfunction isFinitePair(v: unknown): v is [number, number] {\n return (\n Array.isArray(v) &&\n typeof v[0] === 'number' &&\n typeof v[1] === 'number' &&\n Number.isFinite(v[0]) &&\n Number.isFinite(v[1])\n )\n}\n\nfunction pos(v: unknown): [number, number] | null {\n return isFinitePair(v) ? [v[0], v[1]] : null\n}\n\nfunction ring(v: unknown): [number, number][] {\n if (!Array.isArray(v)) return []\n const out: [number, number][] = []\n for (const p of v) {\n const pp = pos(p)\n if (pp) out.push(pp)\n }\n return out\n}\n\n// ---- GeoJSON ------------------------------------------------------------------------------------\n\ntype AnyObj = Record<string, unknown>\n\nfunction geomToPrimitives(geometry: unknown, into: GeoGeom[]): void {\n if (typeof geometry !== 'object' || geometry === null) return\n const g = geometry as AnyObj\n const c = g.coordinates\n switch (g.type) {\n case 'Point': {\n const p = pos(c)\n if (p) into.push({ kind: 'point', coords: [p] })\n break\n }\n case 'MultiPoint':\n for (const p of ring(c)) into.push({ kind: 'point', coords: [p] })\n break\n case 'LineString': {\n const line = ring(c)\n if (line.length) into.push({ kind: 'line', coords: line })\n break\n }\n case 'MultiLineString':\n if (Array.isArray(c))\n for (const ln of c) {\n const line = ring(ln)\n if (line.length) into.push({ kind: 'line', coords: line })\n }\n break\n case 'Polygon': {\n const outer = Array.isArray(c) ? ring(c[0]) : []\n if (outer.length) into.push({ kind: 'polygon', coords: outer })\n break\n }\n case 'MultiPolygon':\n if (Array.isArray(c))\n for (const poly of c) {\n const outer = Array.isArray(poly) ? ring(poly[0]) : []\n if (outer.length) into.push({ kind: 'polygon', coords: outer })\n }\n break\n case 'GeometryCollection':\n if (Array.isArray(g.geometries)) for (const sub of g.geometries) geomToPrimitives(sub, into)\n break\n }\n}\n\nfunction propsToStrings(props: unknown): { name: string; props: Record<string, string> } {\n const out: Record<string, string> = {}\n let name = ''\n if (typeof props === 'object' && props !== null) {\n for (const [k, v] of Object.entries(props as AnyObj)) {\n if (v == null || typeof v === 'object') continue\n out[k] = String(v)\n }\n name = out.name || out.Name || out.NAME || out.title || out.Title || ''\n }\n return { name, props: out }\n}\n\nfunction fromGeoJson(text: string): GeoFeature[] {\n const root = JSON.parse(text) as unknown\n const features: GeoFeature[] = []\n const handle = (obj: unknown): void => {\n if (typeof obj !== 'object' || obj === null) return\n const o = obj as AnyObj\n if (o.type === 'FeatureCollection' && Array.isArray(o.features)) {\n for (const f of o.features) handle(f)\n } else if (o.type === 'Feature') {\n const geoms: GeoGeom[] = []\n geomToPrimitives(o.geometry, geoms)\n const { name, props } = propsToStrings(o.properties)\n if (geoms.length) features.push({ name, geoms, props })\n } else if (typeof o.type === 'string') {\n // A bare geometry object.\n const geoms: GeoGeom[] = []\n geomToPrimitives(o, geoms)\n if (geoms.length) features.push({ name: '', geoms, props: {} })\n }\n }\n handle(root)\n return features\n}\n\n// ---- XML helpers (KML / GPX) --------------------------------------------------------------------\n\nfunction parseXml(text: string): Document | null {\n if (typeof DOMParser === 'undefined') return null\n const doc = new DOMParser().parseFromString(text, 'application/xml')\n return doc.getElementsByTagName('parsererror').length ? null : doc\n}\n\nfunction childText(el: Element, tag: string): string {\n for (const c of Array.from(el.children)) {\n if (c.tagName.toLowerCase() === tag.toLowerCase()) return (c.textContent || '').trim()\n }\n return ''\n}\n\n// KML `lon,lat,alt lon,lat,alt …` (any whitespace between tuples) → [lon, lat] positions.\nfunction parseKmlCoords(text: string): [number, number][] {\n const out: [number, number][] = []\n for (const tuple of text.trim().split(/\\s+/)) {\n if (!tuple) continue\n const parts = tuple.split(',')\n const lon = parseFloat(parts[0])\n const lat = parseFloat(parts[1])\n if (Number.isFinite(lon) && Number.isFinite(lat)) out.push([lon, lat])\n }\n return out\n}\n\nfunction fromKml(text: string): GeoFeature[] {\n const doc = parseXml(text)\n if (!doc) return []\n const features: GeoFeature[] = []\n for (const pm of Array.from(doc.getElementsByTagName('Placemark'))) {\n const geoms: GeoGeom[] = []\n // getElementsByTagName recurses, so this also reaches geometry nested in <MultiGeometry>.\n for (const pt of Array.from(pm.getElementsByTagName('Point'))) {\n const c = parseKmlCoords(childText(pt, 'coordinates'))\n if (c[0]) geoms.push({ kind: 'point', coords: [c[0]] })\n }\n for (const ls of Array.from(pm.getElementsByTagName('LineString'))) {\n const c = parseKmlCoords(childText(ls, 'coordinates'))\n if (c.length) geoms.push({ kind: 'line', coords: c })\n }\n for (const pg of Array.from(pm.getElementsByTagName('Polygon'))) {\n // outer ring: the <coordinates> inside <outerBoundaryIs>; fall back to the first <coordinates>.\n const outer = pg.getElementsByTagName('outerBoundaryIs')[0] ?? pg\n const cEl = outer.getElementsByTagName('coordinates')[0]\n const c = parseKmlCoords(cEl?.textContent || '')\n if (c.length) geoms.push({ kind: 'polygon', coords: c })\n }\n if (geoms.length) {\n const name = childText(pm, 'name')\n const desc = childText(pm, 'description')\n const props: Record<string, string> = {}\n if (desc) props.description = desc\n features.push({ name, geoms, props })\n }\n }\n return features\n}\n\nfunction ptOf(el: Element): [number, number] | null {\n const lat = parseFloat(el.getAttribute('lat') || '')\n const lon = parseFloat(el.getAttribute('lon') || '')\n return Number.isFinite(lat) && Number.isFinite(lon) ? [lon, lat] : null\n}\n\nfunction fromGpx(text: string): GeoFeature[] {\n const doc = parseXml(text)\n if (!doc) return []\n const features: GeoFeature[] = []\n // Waypoints → points.\n for (const wpt of Array.from(doc.getElementsByTagName('wpt'))) {\n const p = ptOf(wpt)\n if (p)\n features.push({\n name: childText(wpt, 'name'),\n geoms: [{ kind: 'point', coords: [p] }],\n props: {},\n })\n }\n // Routes → a line of rtept.\n for (const rte of Array.from(doc.getElementsByTagName('rte'))) {\n const pts: [number, number][] = []\n for (const rp of Array.from(rte.getElementsByTagName('rtept'))) {\n const p = ptOf(rp)\n if (p) pts.push(p)\n }\n if (pts.length)\n features.push({\n name: childText(rte, 'name'),\n geoms: [{ kind: 'line', coords: pts }],\n props: {},\n })\n }\n // Tracks → one line per <trkseg>.\n for (const trk of Array.from(doc.getElementsByTagName('trk'))) {\n const geoms: GeoGeom[] = []\n for (const seg of Array.from(trk.getElementsByTagName('trkseg'))) {\n const pts: [number, number][] = []\n for (const tp of Array.from(seg.getElementsByTagName('trkpt'))) {\n const p = ptOf(tp)\n if (p) pts.push(p)\n }\n if (pts.length) geoms.push({ kind: 'line', coords: pts })\n }\n if (geoms.length) features.push({ name: childText(trk, 'name'), geoms, props: {} })\n }\n return features\n}\n\n/** Parse geodata source text into a normalized feature list. `ext` (when known) disambiguates the format. */\nexport function parseGeo(text: string, ext?: string): ParsedGeo {\n const e = (ext || '').toLowerCase()\n if (e === 'kml' || /<kml[\\s>]/i.test(text)) return { format: 'kml', features: fromKml(text) }\n if (e === 'gpx' || /<gpx[\\s>]/i.test(text)) return { format: 'gpx', features: fromGpx(text) }\n return { format: 'geojson', features: fromGeoJson(text) }\n}\n"],"mappings":"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"}
package/dist/lib/har.js CHANGED
@@ -395,3 +395,5 @@ function B(e) {
395
395
  }
396
396
  //#endregion
397
397
  export { M as decodeContent, N as decodePostData, z as fmtMs, R as fmtSize, b as parseHar, F as parseQuery, P as prettyBody, B as statusClass, L as toCurl };
398
+
399
+ //# sourceMappingURL=har.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"har.js","names":[],"sources":["../../src/lib/har.ts"],"sourcesContent":["/**\n * HAR (HTTP Archive 1.2) 解析与展示层 —— 纯逻辑,不依赖 Vue / DOM。\n *\n * 移植自 sqlite-viewer 项目(D:\\web-work\\sqlite-viewer\\src\\har\\har.ts),但做成**自包含**:原项目里\n * 依赖的 base64 / utf8 / hex / JSON 美化工具直接内联在本文件,避免引入第三方或跨模块依赖(与本仓库\n * lib/eml.ts、lib/mht.ts「手搓零依赖」的一致风格)。\n *\n * 职责:\n * 1) 把任意 HAR JSON 文本解析为规范化的 HarDoc / HarEntry(带预计算的展示字段);\n * 2) 解码请求/响应 body:标准 `encoding:\"base64\"` + 部分工具(Reqable 等)「body 被 Base64 编码但\n * 未声明」的情况自动识别;\n * 3) Header / Cookie / Query / 表单等格式化查看所需的工具函数;\n * 4) 导出 cURL。\n */\n\n/* ============ 内联字节工具(原 crypto/bytes.ts 子集) ============ */\n\nconst td = new TextDecoder('utf-8', { fatal: false })\n\nfunction bytesToUtf8(b: Uint8Array): string {\n return td.decode(b)\n}\n\nfunction bytesToHex(b: Uint8Array, sep = ' '): string {\n let s = ''\n for (let i = 0; i < b.length; i++) {\n s += (i && sep ? sep : '') + b[i].toString(16).padStart(2, '0')\n }\n return s.toUpperCase()\n}\n\n/** Base64 转字节,自动兼容 URL-safe(-_)与缺失的 padding。非法字符由 atob 抛错。 */\nfunction base64ToBytes(s: string): Uint8Array {\n let t = s\n .replace(/[\\r\\n\\t ]/g, '')\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n const pad = t.length % 4\n if (pad) t += '='.repeat(4 - pad)\n const bin = atob(t)\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/* ============ HAR 原始结构(1.2 子集,字段尽量宽松) ============ */\n\nexport interface HarNV {\n name: string\n value: string\n}\nexport interface HarCookie {\n name: string\n value: string\n path?: string\n domain?: string\n expires?: string | null\n httpOnly?: boolean\n secure?: boolean\n sameSite?: string\n}\nexport interface HarPostParam {\n name: string\n value?: string\n fileName?: string\n contentType?: string\n}\nexport interface HarPostData {\n mimeType?: string\n text?: string\n params?: HarPostParam[]\n encoding?: string // 非标准:部分工具用它标记 text 为 base64\n comment?: string\n}\nexport interface HarContent {\n size?: number\n compression?: number\n mimeType?: string\n text?: string\n encoding?: string // \"base64\" 时 text 为 base64\n}\nexport interface HarRequest {\n method: string\n url: string\n httpVersion?: string\n headers: HarNV[]\n queryString: HarNV[]\n cookies: HarCookie[]\n headersSize?: number\n bodySize?: number\n postData?: HarPostData\n}\nexport interface HarResponse {\n status: number\n statusText?: string\n httpVersion?: string\n headers: HarNV[]\n cookies: HarCookie[]\n content: HarContent\n redirectURL?: string\n headersSize?: number\n bodySize?: number\n}\nexport interface HarTimings {\n blocked?: number\n dns?: number\n connect?: number\n send?: number\n wait?: number\n receive?: number\n ssl?: number\n}\nexport interface HarEntryRaw {\n startedDateTime?: string\n time?: number\n request: HarRequest\n response: HarResponse\n timings?: HarTimings\n serverIPAddress?: string\n connection?: string\n resourceType?: string\n}\n\n/* ============ 规范化展示模型 ============ */\n\n/** 请求资源大类(用于颜色 / 快捷筛选) */\nexport type HarCategory = 'doc' | 'xhr' | 'js' | 'css' | 'img' | 'font' | 'media' | 'ws' | 'other'\n\nexport interface HarEntry {\n index: number // 原始顺序(稳定 id)\n method: string\n url: string\n scheme: string // http / https / ws / wss\n host: string\n path: string // pathname(含 query,用于列表展示)\n pathname: string // 仅路径\n status: number\n statusText: string\n category: HarCategory\n type: string // 短类型标签(json / html / png …)\n mime: string // 响应内容 mime(去掉 charset)\n size: number // 响应 body 字节数\n reqSize: number // 请求 body 字节数\n time: number // 总耗时 ms\n startedISO: string\n startedMs: number // epoch ms(排序用,无法解析则 0)\n serverIP: string\n raw: HarEntryRaw\n}\n\nexport interface HarDoc {\n entries: HarEntry[]\n creator: string\n version: string\n}\n\n/* ============ 小工具:安全读取 unknown ============ */\n\nfunction isObj(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === 'object'\n}\nfunction str(v: unknown, dflt = ''): string {\n return typeof v === 'string' ? v : dflt\n}\nfunction num(v: unknown, dflt = 0): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\nfunction bool(v: unknown): boolean {\n return v === true\n}\nfunction arr(v: unknown): unknown[] {\n return Array.isArray(v) ? v : []\n}\n\nfunction readNVs(v: unknown): HarNV[] {\n return arr(v)\n .filter(isObj)\n .map((o) => ({ name: str(o.name), value: str(o.value) }))\n}\nfunction readCookies(v: unknown): HarCookie[] {\n return arr(v)\n .filter(isObj)\n .map((o) => ({\n name: str(o.name),\n value: str(o.value),\n path: str(o.path) || undefined,\n domain: str(o.domain) || undefined,\n expires: typeof o.expires === 'string' ? o.expires : null,\n httpOnly: bool(o.httpOnly),\n secure: bool(o.secure),\n sameSite: str(o.sameSite) || undefined,\n }))\n}\nfunction readPostData(v: unknown): HarPostData | undefined {\n if (!isObj(v)) return undefined\n const params = arr(v.params)\n .filter(isObj)\n .map((o) => ({\n name: str(o.name),\n value: typeof o.value === 'string' ? o.value : undefined,\n fileName: str(o.fileName) || undefined,\n contentType: str(o.contentType) || undefined,\n }))\n return {\n mimeType: str(v.mimeType) || undefined,\n text: typeof v.text === 'string' ? v.text : undefined,\n params: params.length ? params : undefined,\n encoding: str(v.encoding) || undefined,\n comment: str(v.comment) || undefined,\n }\n}\n\n/* ============ URL / mime / 分类 ============ */\n\n/** 拆分 URL(容错:非法 URL 时退化为整串当 path) */\nexport function splitUrl(url: string): {\n scheme: string\n host: string\n pathname: string\n path: string\n} {\n try {\n const u = new URL(url)\n const scheme = u.protocol.replace(/:$/, '')\n return { scheme, host: u.host, pathname: u.pathname, path: u.pathname + u.search }\n } catch {\n return { scheme: '', host: '', pathname: url, path: url }\n }\n}\n\n/** 去掉 mime 的 charset/boundary 等参数,返回主类型 */\nexport function shortMime(mime: string): string {\n return (mime.split(';')[0] || '').trim().toLowerCase()\n}\n\nconst IMG_RE = /^image\\//\nconst FONT_RE = /font|woff/\nconst MEDIA_RE = /^(audio|video)\\//\n\n/** 由响应 mime + url + status 推断资源大类与短标签 */\nexport function categorize(\n mime: string,\n scheme: string,\n status: number,\n rt: string,\n): { category: HarCategory; type: string } {\n const m = shortMime(mime)\n const sub = m.includes('/') ? m.split('/')[1] : m\n const rtl = rt.toLowerCase()\n\n if (scheme === 'ws' || scheme === 'wss' || status === 101 || rtl === 'websocket') {\n return { category: 'ws', type: 'ws' }\n }\n if (IMG_RE.test(m)) return { category: 'img', type: sub === 'svg+xml' ? 'svg' : sub }\n if (FONT_RE.test(m)) return { category: 'font', type: sub.replace('x-', '') || 'font' }\n if (MEDIA_RE.test(m)) return { category: 'media', type: sub }\n if (m === 'text/css') return { category: 'css', type: 'css' }\n if (m.includes('javascript') || m.includes('ecmascript') || sub === 'json') {\n return {\n category: m.includes('json') || sub === 'json' ? 'xhr' : 'js',\n type: sub === 'json' ? 'json' : 'js',\n }\n }\n if (m === 'text/html' || m === 'application/xhtml+xml') return { category: 'doc', type: 'html' }\n if (m.includes('xml')) return { category: 'xhr', type: 'xml' }\n if (m === 'application/x-www-form-urlencoded') return { category: 'xhr', type: 'form' }\n if (m.startsWith('text/')) return { category: 'xhr', type: sub || 'text' }\n if (m) return { category: 'other', type: sub || 'bin' }\n return { category: 'other', type: '' }\n}\n\nfunction epochMs(iso: string): number {\n if (!iso) return 0\n const t = Date.parse(iso)\n return Number.isFinite(t) ? t : 0\n}\n\n/* ============ 解析入口 ============ */\n\nfunction normalizeEntry(raw: unknown, index: number): HarEntry | null {\n if (!isObj(raw)) return null\n const reqO = isObj(raw.request) ? raw.request : null\n const resO = isObj(raw.response) ? raw.response : null\n if (!reqO) return null\n\n const request: HarRequest = {\n method: str(reqO.method, 'GET').toUpperCase(),\n url: str(reqO.url),\n httpVersion: str(reqO.httpVersion) || undefined,\n headers: readNVs(reqO.headers),\n queryString: readNVs(reqO.queryString),\n cookies: readCookies(reqO.cookies),\n headersSize: num(reqO.headersSize, -1),\n bodySize: num(reqO.bodySize, -1),\n postData: readPostData(reqO.postData),\n }\n\n const contentO = resO && isObj(resO.content) ? resO.content : null\n const content: HarContent = {\n size: num(contentO?.size, -1),\n compression: num(contentO?.compression, 0),\n mimeType: str(contentO?.mimeType) || undefined,\n text: typeof contentO?.text === 'string' ? contentO.text : undefined,\n encoding: str(contentO?.encoding) || undefined,\n }\n const response: HarResponse = {\n status: num(resO?.status, 0),\n statusText: str(resO?.statusText) || undefined,\n httpVersion: str(resO?.httpVersion) || undefined,\n headers: readNVs(resO?.headers),\n cookies: readCookies(resO?.cookies),\n content,\n redirectURL: str(resO?.redirectURL) || undefined,\n headersSize: num(resO?.headersSize, -1),\n bodySize: num(resO?.bodySize, -1),\n }\n\n const timings: HarTimings | undefined = isObj(raw.timings)\n ? {\n blocked: num(raw.timings.blocked, -1),\n dns: num(raw.timings.dns, -1),\n connect: num(raw.timings.connect, -1),\n send: num(raw.timings.send, -1),\n wait: num(raw.timings.wait, -1),\n receive: num(raw.timings.receive, -1),\n ssl: num(raw.timings.ssl, -1),\n }\n : undefined\n\n const rt = str(raw.resourceType) || str((raw as Record<string, unknown>)._resourceType)\n const entryRaw: HarEntryRaw = {\n startedDateTime: str(raw.startedDateTime) || undefined,\n time: num(raw.time, -1),\n request,\n response,\n timings,\n serverIPAddress: str(raw.serverIPAddress) || undefined,\n connection: str(raw.connection) || undefined,\n resourceType: rt || undefined,\n }\n\n const { scheme, host, pathname, path } = splitUrl(request.url)\n const mime = shortMime(content.mimeType ?? '')\n const { category, type } = categorize(mime, scheme, response.status, rt)\n const size =\n content.size != null && content.size >= 0 ? content.size : Math.max(0, response.bodySize ?? 0)\n const reqSize = request.bodySize != null && request.bodySize >= 0 ? request.bodySize : 0\n const startedISO = entryRaw.startedDateTime ?? ''\n\n return {\n index,\n method: request.method,\n url: request.url,\n scheme,\n host,\n path: path || '/',\n pathname: pathname || '/',\n status: response.status,\n statusText: response.statusText ?? '',\n category,\n type,\n mime,\n size,\n reqSize,\n time: entryRaw.time != null && entryRaw.time >= 0 ? entryRaw.time : 0,\n startedISO,\n startedMs: epochMs(startedISO),\n serverIP: entryRaw.serverIPAddress ?? '',\n raw: entryRaw,\n }\n}\n\n/** 解析 HAR 文本。非法 / 无 log.entries 时抛错。 */\nexport function parseHar(text: string): HarDoc {\n let root: unknown\n try {\n root = JSON.parse(text)\n } catch (e) {\n throw new Error('不是合法的 JSON:' + (e instanceof Error ? e.message : String(e)))\n }\n const logO = isObj(root) && isObj(root.log) ? root.log : null\n if (!logO || !Array.isArray(logO.entries)) {\n throw new Error('不是有效的 HAR 文件(缺少 log.entries)')\n }\n const entries: HarEntry[] = []\n for (const raw of logO.entries) {\n const e = normalizeEntry(raw, entries.length)\n if (e) entries.push(e)\n }\n if (!entries.length) throw new Error('HAR 中没有可解析的请求条目')\n\n const creatorO = isObj(logO.creator) ? logO.creator : null\n const creator = creatorO ? `${str(creatorO.name, '?')} ${str(creatorO.version)}`.trim() : ''\n return { entries, creator, version: str(logO.version, '1.2') }\n}\n\n/* ============ body 解码(含 base64 自动识别) ============ */\n\nexport type BodyDecodeMode = 'auto' | 'raw' | 'base64'\n\nexport interface DecodedBody {\n kind: 'empty' | 'text' | 'binary' | 'form'\n text: string // 文本视图(binary 时为 hex 预览)\n mime: string\n size: number // 字节数(尽量真实)\n wasBase64: boolean // 是否做了 base64 解码\n isBase64Guess: boolean // 原文是否疑似 base64(供 UI 提示开关)\n isImage: boolean\n dataUri: string // 图片预览用(仅 image 且有字节时)\n form?: HarPostParam[] // 表单字段\n}\n\nconst EMPTY_BODY: DecodedBody = {\n kind: 'empty',\n text: '',\n mime: '',\n size: 0,\n wasBase64: false,\n isBase64Guess: false,\n isImage: false,\n dataUri: '',\n}\n\n/** 文本型 mime(可直接当字符串展示) */\nexport function isTextualMime(mime: string): boolean {\n const m = shortMime(mime)\n if (!m) return true // 未知按文本尝试\n if (m.startsWith('text/')) return true\n return (\n m.includes('json') ||\n m.includes('xml') ||\n m.includes('javascript') ||\n m.includes('ecmascript') ||\n m.includes('x-www-form-urlencoded') ||\n m.includes('graphql') ||\n m.includes('csv') ||\n m.includes('yaml')\n )\n}\nexport function isImageMime(mime: string): boolean {\n return IMG_RE.test(shortMime(mime))\n}\n\n/** 严格判断字符串是否「像」标准 base64(用于自动解码未声明编码的 body) */\nexport function looksLikeBase64(s: string): boolean {\n const t = s.trim()\n if (t.length < 8 || t.length % 4 !== 0) return false\n if (!/^[A-Za-z0-9+/]+={0,2}$/.test(t)) return false\n const eq = t.indexOf('=')\n if (eq !== -1 && eq < t.length - 2) return false\n return true\n}\n\n/** 含 U+FFFD 替换符即认为不是有效 UTF-8 文本(解码后判二进制) */\nfunction hasReplacement(s: string): boolean {\n return s.indexOf('�') !== -1\n}\n\nfunction hexPreview(bytes: Uint8Array, limit = 4096): string {\n const slice = bytes.length > limit ? bytes.subarray(0, limit) : bytes\n const hex = bytesToHex(slice)\n return bytes.length > limit ? hex + `\\n… (${bytes.length} bytes total)` : hex\n}\n\nfunction bytesToDataUri(bytes: Uint8Array, mime: string): string {\n let bin = ''\n const chunk = 0x8000\n for (let i = 0; i < bytes.length; i += chunk) {\n bin += String.fromCharCode(...bytes.subarray(i, i + chunk))\n }\n return `data:${shortMime(mime) || 'application/octet-stream'};base64,${btoa(bin)}`\n}\n\n/** 把已确定的字节按 mime 整理为 DecodedBody(文本/二进制/图片) */\nfunction bytesToBody(\n bytes: Uint8Array,\n mime: string,\n wasBase64: boolean,\n isBase64Guess: boolean,\n): DecodedBody {\n if (isImageMime(mime)) {\n return {\n kind: 'binary',\n text: hexPreview(bytes),\n mime,\n size: bytes.length,\n wasBase64,\n isBase64Guess,\n isImage: true,\n dataUri: bytesToDataUri(bytes, mime),\n }\n }\n const text = bytesToUtf8(bytes)\n const binary = hasReplacement(text) && !isTextualMime(mime)\n return {\n kind: binary ? 'binary' : 'text',\n text: binary ? hexPreview(bytes) : text,\n mime,\n size: bytes.length,\n wasBase64,\n isBase64Guess,\n isImage: false,\n dataUri: '',\n }\n}\n\nfunction utf8Size(s: string): number {\n let n = 0\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i)\n if (c < 0x80) n += 1\n else if (c < 0x800) n += 2\n else if (c >= 0xd800 && c <= 0xdbff) {\n n += 4\n i++\n } else n += 3\n }\n return n\n}\n\n/** SVG 文本 → 可供 <img> 预览的 data URI。用 utf8 + encodeURIComponent,避免 btoa 对非 latin1 报错。 */\nfunction svgDataUri(svgText: string): string {\n return 'data:image/svg+xml;utf8,' + encodeURIComponent(svgText)\n}\n\nfunction textBody(text: string, mime: string, guess: boolean): DecodedBody {\n const m = shortMime(mime)\n return {\n kind: 'text',\n text,\n mime: m,\n size: utf8Size(text),\n wasBase64: false,\n isBase64Guess: guess,\n isImage: false,\n dataUri: m === 'image/svg+xml' ? svgDataUri(text) : '',\n }\n}\n\n/** 解码响应内容 content。mode 控制是否对未声明编码的 body 做 base64 解码。 */\nexport function decodeContent(content: HarContent, mode: BodyDecodeMode = 'auto'): DecodedBody {\n const text = content.text ?? ''\n const mime = content.mimeType ?? ''\n if (!text) return { ...EMPTY_BODY, mime: shortMime(mime), size: Math.max(0, content.size ?? 0) }\n\n const declaredB64 = (content.encoding ?? '').toLowerCase() === 'base64'\n const guess = looksLikeBase64(text)\n const doDecode = mode === 'base64' || (mode !== 'raw' && (declaredB64 || guess))\n\n if (doDecode) {\n try {\n const bytes = base64ToBytes(text)\n const body = bytesToBody(bytes, mime, true, guess || declaredB64)\n // 自动模式下若解出来是带替换符的「文本」mime,说明大概率不是 base64,回退原文\n if (mode === 'auto' && !declaredB64 && body.kind === 'text' && hasReplacement(body.text)) {\n return textBody(text, mime, guess)\n }\n return body\n } catch {\n /* 解码失败 → 当作文本 */\n }\n }\n return textBody(text, mime, guess)\n}\n\n/** 解码请求体 postData。表单优先以字段表展示。 */\nexport function decodePostData(\n post: HarPostData | undefined,\n mode: BodyDecodeMode = 'auto',\n): DecodedBody {\n if (!post) return EMPTY_BODY\n const mime = post.mimeType ?? ''\n if (post.params && post.params.length) {\n return {\n ...EMPTY_BODY,\n kind: 'form',\n mime: shortMime(mime),\n form: post.params,\n size: post.params.reduce((a, p) => a + (p.value?.length ?? 0), 0),\n }\n }\n const text = post.text ?? ''\n if (!text) return { ...EMPTY_BODY, mime: shortMime(mime) }\n\n const declaredB64 = (post.encoding ?? '').toLowerCase() === 'base64'\n const guess = looksLikeBase64(text)\n const doDecode = mode === 'base64' || (mode !== 'raw' && (declaredB64 || guess))\n if (doDecode) {\n try {\n const bytes = base64ToBytes(text)\n const body = bytesToBody(bytes, mime, true, guess || declaredB64)\n if (mode === 'auto' && !declaredB64 && body.kind === 'text' && hasReplacement(body.text)) {\n return textBody(text, mime, guess)\n }\n return body\n } catch {\n /* ignore */\n }\n }\n return textBody(text, mime, guess)\n}\n\n/* ============ 格式化展示辅助 ============ */\n\n/** 若 body 是 JSON(或疑似 JSON)则美化,返回 {text, pretty} */\nexport function prettyBody(text: string, mime: string): { text: string; pretty: boolean } {\n const m = shortMime(mime)\n const trimmed = text.trim()\n const jsonish =\n m.includes('json') ||\n ((trimmed.startsWith('{') || trimmed.startsWith('[')) &&\n (trimmed.endsWith('}') || trimmed.endsWith(']')))\n if (jsonish) {\n try {\n return { text: JSON.stringify(JSON.parse(text), null, 2), pretty: true }\n } catch {\n /* 非法 JSON → 原样 */\n }\n }\n return { text, pretty: false }\n}\n\n/** body 高亮所用的语言(对齐 highlight.js / 本仓库 hljsLang)。注意 svg 含 \"xml\",须先于 xml 判定。 */\nexport function bodyLang(mime: string): string {\n const m = shortMime(mime)\n if (m.includes('javascript') || m.includes('ecmascript')) return 'javascript'\n if (m === 'text/css') return 'css'\n if (m === 'text/html' || m === 'application/xhtml+xml') return 'xml'\n if (m.includes('json')) return 'json'\n if (m.includes('svg') || m.includes('xml')) return 'xml'\n return ''\n}\n\n/** 解析 query string 为 K/V(容错,按 & 与 = 拆) */\nexport function parseQuery(search: string): HarNV[] {\n const s = search.startsWith('?') ? search.slice(1) : search\n if (!s) return []\n return s.split('&').map((pair) => {\n const i = pair.indexOf('=')\n const k = i === -1 ? pair : pair.slice(0, i)\n const v = i === -1 ? '' : pair.slice(i + 1)\n const dec = (x: string) => {\n try {\n return decodeURIComponent(x.replace(/\\+/g, ' '))\n } catch {\n return x\n }\n }\n return { name: dec(k), value: dec(v) }\n })\n}\n\n/** 取某个 header(大小写不敏感) */\nexport function header(list: HarNV[], name: string): string {\n const n = name.toLowerCase()\n const hit = list.find((h) => h.name.toLowerCase() === n)\n return hit ? hit.value : ''\n}\n\nconst SKIP_CURL_HEADERS = new Set(['content-length', 'host', 'connection'])\n\n/** 导出为 cURL 命令 */\nexport function toCurl(entry: HarEntry): string {\n const r = entry.raw.request\n const parts = [`curl '${entry.url}'`]\n if (r.method && r.method !== 'GET') parts.push(`-X ${r.method}`)\n for (const h of r.headers) {\n if (h.name.startsWith(':')) continue // http/2 伪头\n if (SKIP_CURL_HEADERS.has(h.name.toLowerCase())) continue\n parts.push(`-H '${h.name}: ${h.value.replace(/'/g, \"'\\\\''\")}'`)\n }\n const body = decodePostData(r.postData, 'auto')\n if (body.kind === 'form' && body.form) {\n for (const p of body.form) parts.push(`--data-urlencode '${p.name}=${p.value ?? ''}'`)\n } else if (body.text) {\n parts.push(`--data-raw '${body.text.replace(/'/g, \"'\\\\''\")}'`)\n }\n return parts.join(' \\\\\\n ')\n}\n\n/* ============ 通用格式化(大小 / 耗时 / 状态色) ============ */\n\nexport function fmtSize(n: number): string {\n if (n <= 0) return '0'\n if (n < 1024) return n + ' B'\n if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB'\n return (n / (1024 * 1024)).toFixed(2) + ' MB'\n}\nexport function fmtMs(n: number): string {\n if (n <= 0) return '0 ms'\n if (n < 1000) return Math.round(n) + ' ms'\n return (n / 1000).toFixed(2) + ' s'\n}\n/** 状态码颜色类:2xx 绿 / 3xx 蓝 / 4xx 橙 / 5xx 红 / 0 灰 */\nexport function statusClass(status: number): string {\n if (status === 0) return 's-pending'\n if (status < 300) return 's-2xx'\n if (status < 400) return 's-3xx'\n if (status < 500) return 's-4xx'\n return 's-5xx'\n}\n"],"mappings":"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"}
@@ -32,3 +32,5 @@ function i(t, i) {
32
32
  }
33
33
  //#endregion
34
34
  export { r as escapeHtml, n as highlightCode, i as highlightLines };
35
+
36
+ //# sourceMappingURL=highlight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlight.js","names":[],"sources":["../../src/lib/highlight.ts"],"sourcesContent":["import hljs from 'highlight.js'\n\nconst cache = new Map<string, string>()\n\n/**\n * Syntax-highlight `code` with highlight.js, returning escaped HTML.\n * Unlike the original (which polled for a CDN global and could return null),\n * highlight.js is bundled here so this always returns a string. Results are\n * memoized so re-renders / theme toggles don't re-tokenize.\n */\nexport function highlightCode(code: string, label?: string): string {\n const key = `${label ?? ''} ${code}`\n const hit = cache.get(key)\n if (hit !== undefined) return hit\n let out: string\n try {\n out =\n label && hljs.getLanguage(label)\n ? hljs.highlight(code, { language: label, ignoreIllegals: true }).value\n : hljs.highlightAuto(code).value\n } catch {\n out = escapeHtml(code)\n }\n cache.set(key, out)\n return out\n}\n\nexport function escapeHtml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n}\n\n/**\n * Highlight `code` and split the result into one balanced HTML string PER SOURCE LINE.\n *\n * highlight.js emits the whole block as HTML where a `<span>` can span several lines (a block comment, a\n * template literal). To render line-by-line — which the folding source view (CodeView.vue) needs — we walk\n * that HTML and, at every newline, close all currently-open spans, emit the line, and re-open them at the\n * start of the next, so each returned line is self-contained and valid HTML. With no / unknown language we\n * just escape each raw line. The returned array length always equals `code.split('\\n').length`.\n */\nexport function highlightLines(code: string, label?: string): string[] {\n if (!label || !hljs.getLanguage(label)) return code.split('\\n').map(escapeHtml)\n const html = highlightCode(code, label)\n const lines: string[] = []\n const open: string[] = [] // stack of currently-open `<span …>` opening tags\n let cur = ''\n let i = 0\n const n = html.length\n while (i < n) {\n const c = html[i]\n if (c === '<') {\n const gt = html.indexOf('>', i)\n const tag = gt === -1 ? html.slice(i) : html.slice(i, gt + 1)\n if (tag.startsWith('</')) open.pop()\n else if (!tag.endsWith('/>')) open.push(tag)\n cur += tag\n i += tag.length\n } else if (c === '\\n') {\n cur += '</span>'.repeat(open.length)\n lines.push(cur)\n cur = open.join('')\n i++\n } else {\n cur += c\n i++\n }\n }\n cur += '</span>'.repeat(open.length)\n lines.push(cur)\n return lines\n}\n"],"mappings":";;AAEA,IAAM,oBAAQ,IAAI,IAAoB;AAQtC,SAAgB,EAAc,GAAc,GAAwB;CAClE,IAAM,IAAM,GAAG,KAAS,GAAG,GAAG,KACxB,IAAM,EAAM,IAAI,CAAG;CACzB,IAAI,MAAQ,KAAA,GAAW,OAAO;CAC9B,IAAI;CACJ,IAAI;EACF,IACE,KAAS,EAAK,YAAY,CAAK,IAC3B,EAAK,UAAU,GAAM;GAAE,UAAU;GAAO,gBAAgB;EAAK,CAAC,CAAC,CAAC,QAChE,EAAK,cAAc,CAAI,CAAC,CAAC;CACjC,QAAQ;EACN,IAAM,EAAW,CAAI;CACvB;CAEA,OADA,EAAM,IAAI,GAAK,CAAG,GACX;AACT;AAEA,SAAgB,EAAW,GAAmB;CAC5C,OAAO,EAAE,QAAQ,MAAM,OAAO,CAAC,CAAC,QAAQ,MAAM,MAAM,CAAC,CAAC,QAAQ,MAAM,MAAM;AAC5E;AAWA,SAAgB,EAAe,GAAc,GAA0B;CACrE,IAAI,CAAC,KAAS,CAAC,EAAK,YAAY,CAAK,GAAG,OAAO,EAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAU;CAC9E,IAAM,IAAO,EAAc,GAAM,CAAK,GAChC,IAAkB,CAAC,GACnB,IAAiB,CAAC,GACpB,IAAM,IACN,IAAI,GACF,IAAI,EAAK;CACf,OAAO,IAAI,IAAG;EACZ,IAAM,IAAI,EAAK;EACf,IAAI,MAAM,KAAK;GACb,IAAM,IAAK,EAAK,QAAQ,KAAK,CAAC,GACxB,IAAM,MAAO,KAAK,EAAK,MAAM,CAAC,IAAI,EAAK,MAAM,GAAG,IAAK,CAAC;GAI5D,AAHI,EAAI,WAAW,IAAI,IAAG,EAAK,IAAI,IACzB,EAAI,SAAS,IAAI,KAAG,EAAK,KAAK,CAAG,GAC3C,KAAO,GACP,KAAK,EAAI;EACX,OAAO,AAAI,MAAM,QACf,KAAO,UAAU,OAAO,EAAK,MAAM,GACnC,EAAM,KAAK,CAAG,GACd,IAAM,EAAK,KAAK,EAAE,GAClB,QAEA,KAAO,GACP;CAEJ;CAGA,OAFA,KAAO,UAAU,OAAO,EAAK,MAAM,GACnC,EAAM,KAAK,CAAG,GACP;AACT"}
@@ -91,3 +91,5 @@ function i(t) {
91
91
  }
92
92
  //#endregion
93
93
  export { n as langLabel, i as searchLangs };
94
+
95
+ //# sourceMappingURL=highlightLangs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlightLangs.js","names":[],"sources":["../../src/lib/highlightLangs.ts"],"sourcesContent":["/**\n * The highlight-language picker's data — the list + labels behind CodeView's \"语言\" control (the manual override\n * for which highlight.js grammar colorizes the source view).\n *\n * Auto-detection ({@link hljsLang}) maps extension/mime → a highlight.js language id, but that guess is often\n * empty (a `.txt` that's really JSON, an extension-less config) or simply wrong. The source view therefore lets\n * the user FORCE a language: {@link COMMON_LANGS} backs the default quick list, {@link searchLangs} filters the\n * FULL set of registered highlight.js grammars for anything else, and {@link langLabel} gives every id a nice\n * display name (also used to label the current selection). `''` is the sentinel for plain text / no highlight.\n *\n * highlight.js is already bundled (CodeView highlights through it), so listing its grammars here adds no weight.\n */\nimport hljs from 'highlight.js'\n\nexport interface LangOption {\n /** highlight.js language id ('' = plain text, no highlighting). */\n id: string\n /** Human label shown in the menu and on the selector button. */\n label: string\n}\n\n// Nice display names for the ids we special-case; everything else falls back to a capitalized id (see langLabel).\nconst LABELS: Record<string, string> = {\n '': '纯文本',\n plaintext: '纯文本',\n json: 'JSON',\n xml: 'XML / HTML',\n yaml: 'YAML',\n ini: 'INI / TOML',\n toml: 'TOML',\n markdown: 'Markdown',\n javascript: 'JavaScript',\n typescript: 'TypeScript',\n python: 'Python',\n java: 'Java',\n kotlin: 'Kotlin',\n go: 'Go',\n rust: 'Rust',\n c: 'C',\n cpp: 'C++',\n csharp: 'C#',\n php: 'PHP',\n ruby: 'Ruby',\n swift: 'Swift',\n sql: 'SQL',\n bash: 'Shell / Bash',\n shell: 'Shell',\n powershell: 'PowerShell',\n dos: 'Batch',\n css: 'CSS',\n scss: 'SCSS',\n less: 'Less',\n dockerfile: 'Dockerfile',\n makefile: 'Makefile',\n diff: 'Diff',\n properties: 'Properties',\n perl: 'Perl',\n lua: 'Lua',\n r: 'R',\n dart: 'Dart',\n scala: 'Scala',\n groovy: 'Groovy',\n objectivec: 'Objective-C',\n graphql: 'GraphQL',\n}\n\n/** Display name for a highlight.js language id — a curated label, else the capitalized id ('' → 纯文本). */\nexport function langLabel(id: string): string {\n if (id in LABELS) return LABELS[id]\n return id.charAt(0).toUpperCase() + id.slice(1)\n}\n\n// The quick list shown when the search box is empty — the languages people actually reach for, in a sensible\n// order (data/markup first, then the popular programming languages, then styling/misc).\nconst COMMON_IDS = [\n 'json',\n 'xml',\n 'yaml',\n 'ini',\n 'markdown',\n 'javascript',\n 'typescript',\n 'python',\n 'java',\n 'kotlin',\n 'go',\n 'rust',\n 'c',\n 'cpp',\n 'csharp',\n 'php',\n 'ruby',\n 'swift',\n 'sql',\n 'bash',\n 'powershell',\n 'css',\n 'scss',\n 'dockerfile',\n 'diff',\n]\n\n/** The default quick-pick list (shown when the search box is empty). */\nexport const COMMON_LANGS: LangOption[] = COMMON_IDS.map((id) => ({ id, label: langLabel(id) }))\n\n/**\n * Filter the FULL set of registered highlight.js grammars by a query (matched against both the id and its\n * label), capped so the menu stays manageable. An empty query returns {@link COMMON_LANGS} (the quick list).\n */\nexport function searchLangs(query: string): LangOption[] {\n const q = query.trim().toLowerCase()\n if (!q) return COMMON_LANGS\n const out: LangOption[] = []\n for (const id of hljs.listLanguages()) {\n const label = langLabel(id)\n if (id.includes(q) || label.toLowerCase().includes(q)) out.push({ id, label })\n if (out.length >= 60) break\n }\n return out\n}\n"],"mappings":";;AAsBA,IAAM,IAAiC;CACrC,IAAI;CACJ,WAAW;CACX,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,IAAI;CACJ,MAAM;CACN,GAAG;CACH,KAAK;CACL,QAAQ;CACR,KAAK;CACL,MAAM;CACN,OAAO;CACP,KAAK;CACL,MAAM;CACN,OAAO;CACP,YAAY;CACZ,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CACN,YAAY;CACZ,UAAU;CACV,MAAM;CACN,YAAY;CACZ,MAAM;CACN,KAAK;CACL,GAAG;CACH,MAAM;CACN,OAAO;CACP,QAAQ;CACR,YAAY;CACZ,SAAS;AACX;AAGA,SAAgB,EAAU,GAAoB;CAE5C,OADI,KAAM,IAAe,EAAO,KACzB,EAAG,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAG,MAAM,CAAC;AAChD;AAiCA,IAAa,IAA6B;CA5BxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAIwC,CAAA,CAAW,KAAK,OAAQ;CAAE;CAAI,OAAO,EAAU,CAAE;AAAE,EAAE;AAM/F,SAAgB,EAAY,GAA6B;CACvD,IAAM,IAAI,EAAM,KAAK,CAAC,CAAC,YAAY;CACnC,IAAI,CAAC,GAAG,OAAO;CACf,IAAM,IAAoB,CAAC;CAC3B,KAAK,IAAM,KAAM,EAAK,cAAc,GAAG;EACrC,IAAM,IAAQ,EAAU,CAAE;EAE1B,KADI,EAAG,SAAS,CAAC,KAAK,EAAM,YAAY,CAAC,CAAC,SAAS,CAAC,MAAG,EAAI,KAAK;GAAE;GAAI;EAAM,CAAC,GACzE,EAAI,UAAU,IAAI;CACxB;CACA,OAAO;AACT"}
package/dist/lib/hprof.js CHANGED
@@ -707,3 +707,5 @@ function K(e, t) {
707
707
  }
708
708
  //#endregion
709
709
  export { G as Hprof, e as HprofParseError, W as decodeStringValue };
710
+
711
+ //# sourceMappingURL=hprof.js.map