@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":"diagram.js","names":[],"sources":["../../src/lib/diagram.ts"],"sourcesContent":["/**\n * Best-effort, zero-dependency diagram rendering for the file viewer — draws .drawio (mxGraph) and .excalidraw\n * scenes to a self-contained SVG string. In the codebase's hand-rolled house style (like lib/geo.ts's offline\n * map): no mxGraph / excalidraw runtime, no CDN. It covers the COMMON cases — flowchart shapes (rect / rounded /\n * ellipse / diamond / text label), edges with the right arrowheads (classic vs none), exit/entry connection\n * points, orthogonal routing + waypoints, and edge labels — and degrades gracefully on exotic shape libraries\n * (a shape it doesn't special-case falls back to a plain rectangle). For anything it can't render faithfully,\n * the viewer's source toggle still shows the raw XML/JSON, and the file remains downloadable.\n *\n * - .drawio: an <mxfile> whose <diagram> body is either inline <mxGraphModel> XML or, more often, a\n * base64 + raw-DEFLATE + URI-encoded blob (decoded here via DecompressionStream — hence the async API).\n * - .excalidraw: a JSON scene { elements: [...] } — parsed and mapped element-by-element.\n *\n * The produced SVG is OUR markup (built from the parsed geometry, never raw upstream HTML), so the viewer can\n * inline it with v-html safely; element text is escaped.\n */\nimport { fileExt } from '@/lib/fileType'\n\nexport type DiagramKind = 'drawio' | 'excalidraw'\n\n/** Which diagram renderer (if any) handles this file — extension-driven, with the registered mimes as a hint. */\nexport function isDiagramFile(name: string, mime?: string | null): DiagramKind | null {\n const ext = fileExt(name)\n if (ext === 'drawio' || ext === 'dio') return 'drawio'\n if (ext === 'excalidraw') return 'excalidraw'\n const m = (mime || '').toLowerCase().trim()\n if (m === 'application/vnd.jgraph.mxfile') return 'drawio'\n if (m === 'application/vnd.excalidraw+json') return 'excalidraw'\n return null\n}\n\n/** Render a diagram's text to a standalone SVG string. Async because drawio diagrams are usually DEFLATE'd. */\nexport async function renderDiagram(text: string, kind: DiagramKind): Promise<string> {\n return kind === 'excalidraw' ? excalidrawToSvg(text) : drawioToSvg(text)\n}\n\n// ── shared helpers ────────────────────────────────────────────────────────────────────────────────\nconst FONT = 'Helvetica, Arial, \"PingFang SC\", \"Microsoft YaHei\", sans-serif'\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\nfunction num(v: string | null | undefined, fallback = 0): number {\n const n = Number(v)\n return Number.isFinite(n) ? n : fallback\n}\n// Trim coordinate noise (perimeter / midpoint math produces long decimals) without losing visible precision.\nconst rnd = (n: number): number => Math.round(n * 100) / 100\nconst clamp = (v: number, lo: number, hi: number): number => Math.max(lo, Math.min(hi, v))\n\nfunction svgWrap(viewBox: string, w: number, h: number, bg: string, body: string): string {\n // `auto-start-reverse` lets the one marker serve both marker-start and marker-end (start gets flipped).\n const marker =\n `<defs><marker id=\"dg-arrow\" markerWidth=\"10\" markerHeight=\"8\" refX=\"8\" refY=\"3\" ` +\n `orient=\"auto-start-reverse\" markerUnits=\"strokeWidth\"><path d=\"M0,0 L9,3 L0,6 z\" fill=\"context-stroke\"/></marker></defs>`\n // The background must cover the ENTIRE viewBox. Percentage sizing (width/height=\"100%\") gives the rect the\n // viewport's size but keeps it anchored at user-space (0,0), so on any diagram whose content is offset from\n // the origin (the common case — minX/minY ≠ 0) the white rect drifts off and leaves shapes near the far\n // edges uncovered (a tall flowchart's bottom node fell BELOW the white background). Anchor the rect to the\n // viewBox's own x/y/w/h so it always lines up exactly.\n const [vx, vy, vw, vh] = viewBox.split(/\\s+/).map(Number)\n const back =\n bg && bg !== 'transparent'\n ? `<rect x=\"${rnd(vx)}\" y=\"${rnd(vy)}\" width=\"${rnd(vw)}\" height=\"${rnd(vh)}\" fill=\"${bg}\"/>`\n : ''\n return (\n `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${viewBox}\" width=\"${w}\" height=\"${h}\" ` +\n `font-family='${FONT}'>${marker}${back}${body}</svg>`\n )\n}\n\n// ── drawio (mxGraph) ────────────────────────────────────────────────────────────────────────────\nasync function drawioToSvg(text: string): Promise<string> {\n const model = await extractMxGraphModel(text)\n if (!model) throw new Error('无法解析该 drawio 文件(未找到 mxGraphModel)')\n return mxModelToSvg(model)\n}\n\nconst firstTag = (el: Document | Element, tag: string): Element | null =>\n el.getElementsByTagName(tag)[0] ?? null\n\n// Resolve to the <mxGraphModel> element, inflating a compressed first <diagram> page if needed. Uses only\n// getElementsByTagName (portable across DOM implementations — no querySelector/.children).\nasync function extractMxGraphModel(text: string): Promise<Element | null> {\n const parser = new DOMParser()\n const doc = parser.parseFromString(text, 'text/xml')\n if (firstTag(doc, 'parsererror')) return firstTag(doc, 'mxGraphModel')\n const root = doc.documentElement\n if (!root) return null\n if (root.nodeName === 'mxGraphModel') return root\n if (root.nodeName === 'mxfile') {\n const diagram = firstTag(root, 'diagram')\n if (!diagram) return firstTag(root, 'mxGraphModel')\n const childModel = firstTag(diagram, 'mxGraphModel')\n if (childModel) return childModel // uncompressed, stored as a child element\n const inner = (diagram.textContent || '').trim()\n if (!inner) return null\n const xml = inner.startsWith('<') ? inner : await inflateRaw(inner)\n return firstTag(parser.parseFromString(xml, 'text/xml'), 'mxGraphModel')\n }\n return firstTag(doc, 'mxGraphModel')\n}\n\n// base64 → raw-DEFLATE inflate → URI-decode (drawio's diagram compression).\nasync function inflateRaw(b64: string): Promise<string> {\n const bin = atob(b64.replace(/\\s+/g, ''))\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream('deflate-raw'))\n const out = await new Response(stream).arrayBuffer()\n return decodeURIComponent(new TextDecoder().decode(out))\n}\n\ninterface Pt {\n x: number\n y: number\n}\ninterface Box {\n x: number\n y: number\n w: number\n h: number\n}\ntype Side = 'L' | 'R' | 'T' | 'B'\n\ninterface MxCell {\n id: string\n value: string\n style: Record<string, string>\n vertex: boolean\n edge: boolean\n source: string\n target: string\n parent: string\n geom?: { x: number; y: number; w: number; h: number; relative: boolean }\n points?: Pt[]\n srcPoint?: Pt\n tgtPoint?: Pt\n offset?: Pt\n}\n\n// \"rounded=1;fillColor=#dae8fc;ellipse\" → { rounded:'1', fillColor:'#dae8fc', ellipse:'' }\nfunction parseStyle(s: string): Record<string, string> {\n const out: Record<string, string> = {}\n for (const part of s.split(';')) {\n if (!part) continue\n const eq = part.indexOf('=')\n if (eq < 0) out[part.trim()] = ''\n else out[part.slice(0, eq).trim()] = part.slice(eq + 1).trim()\n }\n return out\n}\n\n// Decode the handful of HTML entities drawio labels actually use (&amp; resolved last to avoid double-decoding).\nfunction decodeEntities(s: string): string {\n return s\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => String.fromCodePoint(Number.parseInt(h, 16)))\n .replace(/&#(\\d+);/g, (_, d) => String.fromCodePoint(Number(d)))\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&apos;|&#39;/g, \"'\")\n .replace(/&nbsp;/g, ' ')\n .replace(/&amp;/g, '&')\n}\n// drawio labels are HTML when html=1 — turn block/breaks into newlines, strip tags, decode entities.\nfunction stripHtml(s: string): string {\n if (!s) return ''\n const t = s\n .replace(/<br\\s*\\/?>/gi, '\\n')\n .replace(/<\\/(div|p|li|tr|h[1-6])>/gi, '\\n')\n .replace(/<[^>]+>/g, '')\n return decodeEntities(t)\n .replace(/ /g, ' ')\n .replace(/\\n{2,}/g, '\\n')\n .trim()\n}\n\nfunction parseCells(model: Element): { cells: MxCell[]; byId: Map<string, MxCell> } {\n const byId = new Map<string, MxCell>()\n const cells: MxCell[] = []\n for (const el of Array.from(model.getElementsByTagName('mxCell'))) {\n const c: MxCell = {\n id: el.getAttribute('id') || '',\n value: stripHtml(el.getAttribute('value') || ''),\n style: parseStyle(el.getAttribute('style') || ''),\n vertex: el.getAttribute('vertex') === '1',\n edge: el.getAttribute('edge') === '1',\n source: el.getAttribute('source') || '',\n target: el.getAttribute('target') || '',\n parent: el.getAttribute('parent') || '',\n }\n const g = el.getElementsByTagName('mxGeometry')[0]\n if (g) {\n c.geom = {\n x: num(g.getAttribute('x')),\n y: num(g.getAttribute('y')),\n w: num(g.getAttribute('width')),\n h: num(g.getAttribute('height')),\n relative: g.getAttribute('relative') === '1',\n }\n for (const pe of Array.from(g.getElementsByTagName('mxPoint'))) {\n const p = { x: num(pe.getAttribute('x')), y: num(pe.getAttribute('y')) }\n const as = pe.getAttribute('as')\n if (as === 'sourcePoint') c.srcPoint = p\n else if (as === 'targetPoint') c.tgtPoint = p\n else if (as === 'offset') c.offset = p\n }\n const arr = Array.from(g.getElementsByTagName('Array')).find(\n (a) => a.getAttribute('as') === 'points',\n )\n if (arr) {\n c.points = Array.from(arr.getElementsByTagName('mxPoint')).map((pe) => ({\n x: num(pe.getAttribute('x')),\n y: num(pe.getAttribute('y')),\n }))\n }\n }\n byId.set(c.id, c)\n cells.push(c)\n }\n return { cells, byId }\n}\n\n// A cell's geometry is relative to its parent vertex (a container/group). Walk up summing ancestor offsets to\n// get the absolute box. Stops at the root layer (parent \"0\"/\"1\", which has no geometry) or an edge parent.\nfunction absBox(c: MxCell, byId: Map<string, MxCell>): Box {\n const g = c.geom as { x: number; y: number; w: number; h: number }\n let x = g.x\n let y = g.y\n let p = byId.get(c.parent)\n while (p && p.geom && p.vertex && !p.edge) {\n x += p.geom.x\n y += p.geom.y\n p = byId.get(p.parent)\n }\n return { x, y, w: g.w, h: g.h }\n}\n\nconst boxCenter = (b: Box): Pt => ({ x: b.x + b.w / 2, y: b.y + b.h / 2 })\n\n// Fixed connection point from drawio's exitX/exitY (or entryX/entryY): a fraction of the node's box.\nfunction fixedPoint(b: Box, fx?: string, fy?: string): Pt | null {\n if (fx == null || fy == null) return null\n return { x: b.x + num(fx) * b.w, y: b.y + num(fy) * b.h }\n}\nfunction sideOfFixed(fx?: string, fy?: string): Side | null {\n if (fx == null || fy == null) return null\n const x = num(fx)\n const y = num(fy)\n if (x === 0) return 'L'\n if (x === 1) return 'R'\n if (y === 0) return 'T'\n if (y === 1) return 'B'\n return null\n}\n// Where a line from the box centre toward `toward` crosses the box's rectangular boundary.\nfunction rectPerim(b: Box, toward: Pt): Pt {\n const c = boxCenter(b)\n const dx = toward.x - c.x\n const dy = toward.y - c.y\n if (!dx && !dy) return c\n const sx = dx ? b.w / 2 / Math.abs(dx) : Infinity\n const sy = dy ? b.h / 2 / Math.abs(dy) : Infinity\n const s = Math.min(sx, sy)\n return { x: c.x + dx * s, y: c.y + dy * s }\n}\n// Same, for an ellipse node.\nfunction ellipsePerim(b: Box, toward: Pt): Pt {\n const c = boxCenter(b)\n const dx = toward.x - c.x\n const dy = toward.y - c.y\n if (!dx && !dy) return c\n const rx = b.w / 2 || 1\n const ry = b.h / 2 || 1\n const t = 1 / Math.sqrt((dx / rx) ** 2 + (dy / ry) ** 2)\n return { x: c.x + dx * t, y: c.y + dy * t }\n}\n\n// One end of an edge: a fixed exit/entry point if the style names one, else the node's perimeter toward `toward`.\nfunction endAt(\n node: MxCell,\n box: Box,\n fx: string | undefined,\n fy: string | undefined,\n toward: Pt,\n): { pt: Pt; side: Side | null } {\n const fixed = fixedPoint(box, fx, fy)\n if (fixed) return { pt: fixed, side: sideOfFixed(fx, fy) }\n const pt = 'ellipse' in node.style ? ellipsePerim(box, toward) : rectPerim(box, toward)\n return { pt, side: null }\n}\n\n// A simple orthogonal connector between two endpoints, using their box sides when known (else inferred from the\n// dominant axis). Good enough for drawio's orthogonalEdgeStyle when no explicit waypoints are given.\nfunction orthRoute(a: Pt, sa: Side | null, b: Pt, sb: Side | null): Pt[] {\n const dom = Math.abs(b.x - a.x) >= Math.abs(b.y - a.y)\n const ha = sa ? sa === 'L' || sa === 'R' : dom\n const hb = sb ? sb === 'L' || sb === 'R' : dom\n if (ha && hb) {\n const mx = (a.x + b.x) / 2\n return [a, { x: mx, y: a.y }, { x: mx, y: b.y }, b]\n }\n if (!ha && !hb) {\n const my = (a.y + b.y) / 2\n return [a, { x: a.x, y: my }, { x: b.x, y: my }, b]\n }\n if (ha && !hb) return [a, { x: b.x, y: a.y }, b]\n return [a, { x: a.x, y: b.y }, b]\n}\n\nconst hasBox = (c: MxCell | undefined): c is MxCell => !!c && !!c.geom && c.geom.w > 0\n\n// The full point list an edge is drawn through: resolved source end → waypoints → resolved target end.\nfunction edgePolyline(c: MxCell, byId: Map<string, MxCell>): Pt[] {\n const src = c.source ? byId.get(c.source) : undefined\n const tgt = c.target ? byId.get(c.target) : undefined\n const srcBox = hasBox(src) ? absBox(src, byId) : null\n const tgtBox = hasBox(tgt) ? absBox(tgt, byId) : null\n const wpts = c.points ?? []\n\n const exitFixed = srcBox ? fixedPoint(srcBox, c.style.exitX, c.style.exitY) : null\n const entryFixed = tgtBox ? fixedPoint(tgtBox, c.style.entryX, c.style.entryY) : null\n const srcRef = exitFixed ?? (srcBox ? boxCenter(srcBox) : c.srcPoint) ?? { x: 0, y: 0 }\n const tgtRef = entryFixed ?? (tgtBox ? boxCenter(tgtBox) : c.tgtPoint) ?? { x: 0, y: 0 }\n\n let a: Pt | undefined\n let sa: Side | null = null\n if (src && srcBox) {\n const r = endAt(src, srcBox, c.style.exitX, c.style.exitY, wpts[0] ?? tgtRef)\n a = r.pt\n sa = r.side\n } else a = c.srcPoint\n\n let b: Pt | undefined\n let sb: Side | null = null\n if (tgt && tgtBox) {\n const r = endAt(tgt, tgtBox, c.style.entryX, c.style.entryY, wpts[wpts.length - 1] ?? srcRef)\n b = r.pt\n sb = r.side\n } else b = c.tgtPoint\n\n if (!a || !b) return []\n if (wpts.length) return [a, ...wpts, b]\n if ((c.style.edgeStyle || '').toLowerCase().includes('orthogonal')) return orthRoute(a, sa, b, sb)\n return [a, b]\n}\n\nfunction colorOf(v: string | undefined, fallback: string): string {\n // Distinguish \"not set\" (use the fallback) from an explicit \"none\" (no paint).\n if (v === 'none') return 'none'\n if (!v) return fallback\n return v\n}\n\n// ── line jumps (jumpStyle) ──────────────────────────────────────────────────────────────────────\n// drawio draws a little hop where one connector crosses another (style `jumpStyle=arc`). Without it, crossing\n// lines look connected. We compute pairwise segment intersections between jump-enabled edges and give each\n// crossing to ONE edge (the more-horizontal segment hops over the more-vertical one — drawio's usual look).\nconst JUMP_R = 6 // hop radius, in diagram units\n\ninterface Jump {\n seg: number // segment index within the edge polyline\n t: number // param along that segment (0..1)\n}\n\nfunction jumpEnabled(c: MxCell): boolean {\n const js = (c.style.jumpStyle || '').toLowerCase()\n return js !== '' && js !== 'none' && c.style.noJump !== '1'\n}\n\n// Intersection of segments a→b and c→d, as {t,u} params, or null (parallel / not within both segments).\nfunction segIntersect(a: Pt, b: Pt, c: Pt, d: Pt): { t: number; u: number } | null {\n const rx = b.x - a.x\n const ry = b.y - a.y\n const sx = d.x - c.x\n const sy = d.y - c.y\n const denom = rx * sy - ry * sx\n if (Math.abs(denom) < 1e-9) return null\n const t = ((c.x - a.x) * sy - (c.y - a.y) * sx) / denom\n const u = ((c.x - a.x) * ry - (c.y - a.y) * rx) / denom\n if (t <= 0 || t >= 1 || u <= 0 || u >= 1) return null\n return { t, u }\n}\n\nfunction addJump(m: Map<string, Jump[]>, id: string, seg: number, t: number): void {\n const arr = m.get(id)\n if (arr) arr.push({ seg, t })\n else m.set(id, [{ seg, t }])\n}\n\nfunction computeJumps(edges: MxCell[], polys: Map<string, Pt[]>): Map<string, Jump[]> {\n const list = edges.filter((c) => jumpEnabled(c) && (polys.get(c.id)?.length ?? 0) >= 2)\n const out = new Map<string, Jump[]>()\n const near = (p: Pt, q: Pt, t: number): boolean => {\n const len = Math.hypot(q.x - p.x, q.y - p.y)\n return t * len < JUMP_R * 1.5 || (1 - t) * len < JUMP_R * 1.5\n }\n for (let i = 0; i < list.length; i++) {\n const A = polys.get(list[i].id) as Pt[]\n for (let j = i + 1; j < list.length; j++) {\n const B = polys.get(list[j].id) as Pt[]\n for (let ai = 0; ai + 1 < A.length; ai++) {\n for (let bi = 0; bi + 1 < B.length; bi++) {\n const hit = segIntersect(A[ai], A[ai + 1], B[bi], B[bi + 1])\n if (!hit) continue\n // skip crossings that are really a shared endpoint (edges meeting at the same node)\n if (near(A[ai], A[ai + 1], hit.t) || near(B[bi], B[bi + 1], hit.u)) continue\n const aHoriz = Math.abs(A[ai + 1].x - A[ai].x) >= Math.abs(A[ai + 1].y - A[ai].y)\n const bHoriz = Math.abs(B[bi + 1].x - B[bi].x) >= Math.abs(B[bi + 1].y - B[bi].y)\n // the more-horizontal segment hops; ties → the later-listed edge\n if (aHoriz && !bHoriz) addJump(out, list[i].id, ai, hit.t)\n else addJump(out, list[j].id, bi, hit.u)\n }\n }\n }\n }\n return out\n}\n\n// Build the edge path `d`, inserting a semicircular hop at each jump point on its way along the polyline.\nfunction edgePathD(poly: Pt[], jumps: Jump[] | undefined): string {\n const bySeg = new Map<number, number[]>()\n if (jumps) {\n for (const j of jumps) {\n const arr = bySeg.get(j.seg)\n if (arr) arr.push(j.t)\n else bySeg.set(j.seg, [j.t])\n }\n }\n let d = `M ${rnd(poly[0].x)} ${rnd(poly[0].y)}`\n for (let i = 0; i + 1 < poly.length; i++) {\n const p = poly[i]\n const q = poly[i + 1]\n const ts = bySeg.get(i)\n if (ts && ts.length) {\n ts.sort((m, n) => m - n)\n const len = Math.hypot(q.x - p.x, q.y - p.y) || 1\n const ux = (q.x - p.x) / len\n const uy = (q.y - p.y) / len\n for (const t of ts) {\n const jx = p.x + (q.x - p.x) * t\n const jy = p.y + (q.y - p.y) * t\n d += ` L ${rnd(jx - ux * JUMP_R)} ${rnd(jy - uy * JUMP_R)}`\n d += ` A ${JUMP_R} ${JUMP_R} 0 0 1 ${rnd(jx + ux * JUMP_R)} ${rnd(jy + uy * JUMP_R)}`\n }\n }\n d += ` L ${rnd(q.x)} ${rnd(q.y)}`\n }\n return d\n}\n\nfunction edgeSvg(c: MxCell, poly: Pt[], jumps?: Jump[]): string {\n if (poly.length < 2) return ''\n const stroke = colorOf(c.style.strokeColor, '#333333')\n const d = edgePathD(poly, jumps)\n const sw = num(c.style.strokeWidth, 1.4)\n const dash = c.style.dashed === '1' ? ' stroke-dasharray=\"6 4\"' : ''\n // drawio defaults: end arrow = classic, start arrow = none.\n const me = (c.style.endArrow ?? 'classic') !== 'none' ? ' marker-end=\"url(#dg-arrow)\"' : ''\n const ms = (c.style.startArrow ?? 'none') !== 'none' ? ' marker-start=\"url(#dg-arrow)\"' : ''\n return `<path d=\"${d}\" fill=\"none\" stroke=\"${stroke === 'none' ? '#333333' : stroke}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\"${dash}${me}${ms}/>`\n}\n\ntype HAlign = 'left' | 'center' | 'right'\ntype VAlign = 'top' | 'middle' | 'bottom'\n\n// Rough per-character advance (no font metrics here): CJK / full-width glyphs ≈ 1em, latin ≈ 0.58em. Only used\n// to WRAP labels to their box width and to size the diagram's bounding box — approximate is fine.\nfunction charW(ch: string, fs: number): number {\n const c = ch.codePointAt(0) ?? 0\n const wide =\n c >= 0x1100 &&\n (c <= 0x115f || // Hangul Jamo\n (c >= 0x2e80 && c <= 0xa4cf) || // CJK radicals … Yi\n (c >= 0xac00 && c <= 0xd7a3) || // Hangul syllables\n (c >= 0xf900 && c <= 0xfaff) || // CJK compatibility ideographs\n (c >= 0xfe30 && c <= 0xfe4f) || // CJK compatibility forms\n (c >= 0xff00 && c <= 0xff60) || // full-width forms\n (c >= 0xffe0 && c <= 0xffe6) ||\n (c >= 0x20000 && c <= 0x3fffd)) // CJK extension B+\n return (wide ? 1 : 0.58) * fs\n}\nfunction textW(s: string, fs: number): number {\n let w = 0\n for (const ch of s) w += charW(ch, fs)\n return w\n}\n// Word-wrap a logical line to fit `maxW` (latin by word; long tokens / CJK runs hard-break by character).\nfunction wrapLine(line: string, fs: number, maxW: number): string[] {\n if (maxW <= 4 || textW(line, fs) <= maxW) return [line]\n const out: string[] = []\n let cur = ''\n for (const word of line.split(/\\s+/).filter((w) => w !== '')) {\n if (!cur) cur = word\n else if (textW(`${cur} ${word}`, fs) <= maxW) cur = `${cur} ${word}`\n else {\n out.push(cur)\n cur = word\n }\n // hard-break the current run when a single token still overflows (covers space-less CJK text)\n while (textW(cur, fs) > maxW) {\n let piece = ''\n let rest = ''\n let broke = false\n for (const ch of cur) {\n if (!broke && piece && textW(piece + ch, fs) > maxW) broke = true\n if (broke) rest += ch\n else piece += ch\n }\n if (!piece) break\n out.push(piece)\n cur = rest\n }\n }\n if (cur) out.push(cur)\n return out.length ? out : [line]\n}\n\ninterface LabelLayout {\n lines: string[]\n anchor: 'start' | 'middle' | 'end'\n tx: number\n firstCenter: number // y-centre of the first line\n lh: number\n fontSize: number\n bits: number\n color: string\n bbox: Box // rendered extent, so the diagram bounds can include out-of-box labels\n}\n\nconst LBL_GAP = 3 // gap between an out-of-box label and the shape edge\n\n// Lay out a cell label. Unlike a naive \"centre in the box\", this honours drawio's labelPosition /\n// verticalLabelPosition (which push the label OUTSIDE the shape — the DFD data-store labels sit above / below /\n// to the right of their tiny box), and wraps `whiteSpace=wrap` text to the box width so long labels don't spill\n// out of their shape. align / verticalAlign then place the text within its region.\nfunction labelLayout(value: string, box: Box, style: Record<string, string>): LabelLayout {\n const fontSize = num(style.fontSize, 12)\n const color = colorOf(style.fontColor, '#1a1a1a')\n const bits = num(style.fontStyle, 0)\n const align = (style.align as HAlign) || 'center'\n const valign = (style.verticalAlign as VAlign) || 'middle'\n const labelPos = style.labelPosition || 'center' // horizontal position of the label vs the cell\n const vLabelPos = style.verticalLabelPosition || 'middle'\n const wrap = style.whiteSpace === 'wrap'\n const lh = fontSize * 1.25\n\n // Horizontal placement + the width text may wrap to (only labels INSIDE the box wrap).\n let anchor: 'start' | 'middle' | 'end'\n let tx: number\n let maxW = Infinity\n if (labelPos === 'left') {\n anchor = 'end'\n tx = box.x - LBL_GAP\n } else if (labelPos === 'right') {\n anchor = 'start'\n tx = box.x + box.w + LBL_GAP\n } else if (align === 'left') {\n anchor = 'start'\n tx = box.x + 6\n if (wrap && vLabelPos === 'middle') maxW = box.w - 8\n } else if (align === 'right') {\n anchor = 'end'\n tx = box.x + box.w - 6\n if (wrap && vLabelPos === 'middle') maxW = box.w - 8\n } else {\n anchor = 'middle'\n tx = box.x + box.w / 2\n if (wrap && vLabelPos === 'middle') maxW = box.w - 8\n }\n\n const lines: string[] = []\n for (const ln of value.split('\\n')) {\n if (Number.isFinite(maxW)) lines.push(...wrapLine(ln, fontSize, maxW))\n else lines.push(ln)\n }\n const n = lines.length || 1\n\n // Vertical placement: firstCenter = y-centre of the first line.\n let firstCenter: number\n if (vLabelPos === 'top') firstCenter = box.y - LBL_GAP - (n - 1) * lh - lh / 2\n else if (vLabelPos === 'bottom') firstCenter = box.y + box.h + LBL_GAP + lh / 2\n else if (valign === 'top') firstCenter = box.y + lh / 2 + 2\n else if (valign === 'bottom') firstCenter = box.y + box.h - (n - 1) * lh - lh / 2 - 2\n else firstCenter = box.y + box.h / 2 - ((n - 1) * lh) / 2\n\n let tw = 0\n for (const ln of lines) tw = Math.max(tw, textW(ln, fontSize))\n const bx0 = anchor === 'start' ? tx : anchor === 'end' ? tx - tw : tx - tw / 2\n const by0 = firstCenter - lh / 2\n const bbox: Box = { x: bx0, y: by0, w: tw, h: n * lh }\n\n return { lines, anchor, tx, firstCenter, lh, fontSize, bits, color, bbox }\n}\n\n// Render a laid-out label to an SVG <text> (honours fontStyle bold/italic/underline).\nfunction labelSvg(value: string, box: Box, style: Record<string, string>): string {\n if (!value) return ''\n const l = labelLayout(value, box, style)\n const weight = l.bits & 1 ? ' font-weight=\"700\"' : ''\n const fstyle = l.bits & 2 ? ' font-style=\"italic\"' : ''\n const deco = l.bits & 4 ? ' text-decoration=\"underline\"' : ''\n const tspans = l.lines\n .map(\n (ln, i) => `<tspan x=\"${rnd(l.tx)}\" y=\"${rnd(l.firstCenter + i * l.lh)}\">${esc(ln)}</tspan>`,\n )\n .join('')\n return `<text text-anchor=\"${l.anchor}\" dominant-baseline=\"central\" font-size=\"${l.fontSize}\" fill=\"${l.color === 'none' ? '#1a1a1a' : l.color}\"${weight}${fstyle}${deco}>${tspans}</text>`\n}\n\nfunction vertexSvg(c: MxCell, box: Box): string {\n const style = c.style\n const fill = colorOf(style.fillColor, '#ffffff')\n const stroke = colorOf(style.strokeColor, '#333333')\n\n // A bare text label (style \"text\" with no fill/stroke): render the text only, no box — matches the many\n // borderless captions in drawio diagrams (group titles like 弱电箱 / 电视柜, free labels like 入户光纤).\n if ('text' in style && fill === 'none' && stroke === 'none') {\n return c.value ? labelSvg(c.value, box, style) : ''\n }\n\n const { x, y, w, h } = box\n const sw = num(style.strokeWidth, 1.2)\n const dash = style.dashed === '1' ? ' stroke-dasharray=\"4 3\"' : ''\n const common = `fill=\"${fill}\" stroke=\"${stroke}\" stroke-width=\"${sw}\"${dash}`\n const shapeName = (style.shape || '').toLowerCase()\n let shape: string\n if ('ellipse' in style) {\n shape = `<ellipse cx=\"${rnd(x + w / 2)}\" cy=\"${rnd(y + h / 2)}\" rx=\"${rnd(w / 2)}\" ry=\"${rnd(h / 2)}\" ${common}/>`\n } else if ('rhombus' in style) {\n const pts = `${rnd(x + w / 2)},${rnd(y)} ${rnd(x + w)},${rnd(y + h / 2)} ${rnd(x + w / 2)},${rnd(y + h)} ${rnd(x)},${rnd(y + h / 2)}`\n shape = `<polygon points=\"${pts}\" ${common}/>`\n } else if (shapeName.includes('datastore')) {\n // DFD data store (Gane–Sarson): a rectangle open on the right, with a left \"ID\" divider. Fill first, then\n // the open outline + divider on top.\n const d = Math.min(w * 0.2, 20)\n shape =\n `<rect x=\"${rnd(x)}\" y=\"${rnd(y)}\" width=\"${rnd(w)}\" height=\"${rnd(h)}\" fill=\"${fill}\" stroke=\"none\"/>` +\n `<path d=\"M ${rnd(x + w)} ${rnd(y)} L ${rnd(x)} ${rnd(y)} L ${rnd(x)} ${rnd(y + h)} L ${rnd(x + w)} ${rnd(y + h)}\" fill=\"none\" stroke=\"${stroke}\" stroke-width=\"${sw}\"${dash}/>` +\n `<line x1=\"${rnd(x + d)}\" y1=\"${rnd(y)}\" x2=\"${rnd(x + d)}\" y2=\"${rnd(y + h)}\" stroke=\"${stroke}\" stroke-width=\"${sw}\"/>`\n } else {\n const rad = style.rounded === '1' ? Math.min(12, w / 4, h / 4) : 0\n shape = `<rect x=\"${rnd(x)}\" y=\"${rnd(y)}\" width=\"${rnd(w)}\" height=\"${rnd(h)}\" rx=\"${rad}\" ry=\"${rad}\" ${common}/>`\n }\n return shape + (c.value ? labelSvg(c.value, box, style) : '')\n}\n\n// Position along a polyline by arc-length fraction (0 = start, 1 = end).\nfunction pointAtFraction(poly: Pt[], t: number): Pt {\n if (poly.length === 1) return poly[0]\n const segs: number[] = []\n let total = 0\n for (let i = 1; i < poly.length; i++) {\n const d = Math.hypot(poly[i].x - poly[i - 1].x, poly[i].y - poly[i - 1].y)\n segs.push(d)\n total += d\n }\n if (total === 0) return poly[0]\n let want = t * total\n for (let i = 0; i < segs.length; i++) {\n if (want <= segs[i] || i === segs.length - 1) {\n const f = segs[i] ? want / segs[i] : 0\n return {\n x: poly[i].x + (poly[i + 1].x - poly[i].x) * f,\n y: poly[i].y + (poly[i + 1].y - poly[i].y) * f,\n }\n }\n want -= segs[i]\n }\n return poly[poly.length - 1]\n}\n\n// An edge label (a child cell of an edge): drawio stores its position as a fraction along the edge (geometry x,\n// -1..1) plus a pixel offset. White-backed so it stays legible over the line.\n// The white-backed box an edge label occupies (positioned by its fraction along the edge + pixel offset).\nfunction edgeLabelBox(c: MxCell, poly: Pt[]): Box | null {\n if (!c.value || poly.length < 2) return null\n const t = clamp(((c.geom?.x ?? 0) + 1) / 2, 0, 1)\n const base = pointAtFraction(poly, t)\n const off = c.offset ?? { x: 0, y: 0 }\n const cx = base.x + off.x\n const cy = base.y + off.y\n const fontSize = num(c.style.fontSize, 11)\n const lines = c.value.split('\\n')\n let tw = 0\n for (const l of lines) tw = Math.max(tw, textW(l, fontSize))\n tw += 8\n const th = lines.length * fontSize * 1.25 + 4\n return { x: cx - tw / 2, y: cy - th / 2, w: tw, h: th }\n}\n\nfunction edgeLabelSvg(c: MxCell, poly: Pt[]): string {\n const box = edgeLabelBox(c, poly)\n if (!box) return ''\n return (\n `<rect x=\"${rnd(box.x)}\" y=\"${rnd(box.y)}\" width=\"${rnd(box.w)}\" height=\"${rnd(box.h)}\" fill=\"#ffffff\" opacity=\"0.82\" rx=\"3\"/>` +\n labelSvg(c.value, box, c.style)\n )\n}\n\nfunction mxModelToSvg(model: Element): string {\n const { cells, byId } = parseCells(model)\n\n // Precompute every edge's polyline once (edges draw it; their labels position along it).\n const edgePaths = new Map<string, Pt[]>()\n for (const c of cells) if (c.edge) edgePaths.set(c.id, edgePolyline(c, byId))\n\n const isEdgeLabel = (c: MxCell): boolean => c.vertex && (byId.get(c.parent)?.edge ?? false)\n const drawableVertex = (c: MxCell): boolean =>\n c.vertex && !isEdgeLabel(c) && !!c.geom && c.geom.w > 0 && c.geom.h > 0\n\n // Bounding box over node boxes + every edge point.\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n const grow = (x: number, y: number): void => {\n minX = Math.min(minX, x)\n minY = Math.min(minY, y)\n maxX = Math.max(maxX, x)\n maxY = Math.max(maxY, y)\n }\n for (const c of cells) {\n if (drawableVertex(c)) {\n const b = absBox(c, byId)\n grow(b.x, b.y)\n grow(b.x + b.w, b.y + b.h)\n // Include the label's rendered extent — drawio labels can sit OUTSIDE the shape (e.g. the DFD data-store\n // labels above / below / right of their box), which would otherwise be clipped by the viewBox.\n if (c.value) {\n const lb = labelLayout(c.value, b, c.style).bbox\n grow(lb.x, lb.y)\n grow(lb.x + lb.w, lb.y + lb.h)\n }\n } else if (isEdgeLabel(c)) {\n const lb = edgeLabelBox(c, edgePaths.get(c.parent) ?? [])\n if (lb) {\n grow(lb.x, lb.y)\n grow(lb.x + lb.w, lb.y + lb.h)\n }\n }\n }\n for (const poly of edgePaths.values()) for (const p of poly) grow(p.x, p.y)\n if (!Number.isFinite(minX)) throw new Error('该 drawio 文件没有可绘制的图形')\n\n // Precompute the crossing hops (jumpStyle) once, keyed by edge id.\n const jumps = computeJumps(\n cells.filter((c) => c.edge),\n edgePaths,\n )\n\n // Render in DOCUMENT ORDER so a container box defined before an edge sits BEHIND it (and inner nodes defined\n // after the edge sit on top) — this matches drawio's z-order and is what makes an edge cross INTO a group box\n // to reach an inner node, instead of the white container painting over the line.\n const out: string[] = []\n for (const c of cells) {\n if (c.edge) out.push(edgeSvg(c, edgePaths.get(c.id) ?? [], jumps.get(c.id)))\n else if (isEdgeLabel(c)) out.push(edgeLabelSvg(c, edgePaths.get(c.parent) ?? []))\n else if (drawableVertex(c)) out.push(vertexSvg(c, absBox(c, byId)))\n }\n\n const pad = 24\n const w = maxX - minX + pad * 2\n const h = maxY - minY + pad * 2\n const vb = `${rnd(minX - pad)} ${rnd(minY - pad)} ${rnd(w)} ${rnd(h)}`\n return svgWrap(vb, rnd(w), rnd(h), '#ffffff', out.join(''))\n}\n\n// ── excalidraw ────────────────────────────────────────────────────────────────────────────────\ninterface ExEl {\n type: string\n x: number\n y: number\n width: number\n height: number\n angle?: number\n strokeColor?: string\n backgroundColor?: string\n strokeWidth?: number\n opacity?: number\n roundness?: unknown\n points?: [number, number][]\n text?: string\n fontSize?: number\n isDeleted?: boolean\n}\n\nfunction excalidrawToSvg(text: string): string {\n const data = JSON.parse(text) as {\n elements?: ExEl[]\n appState?: { viewBackgroundColor?: string }\n }\n const els = (data.elements ?? []).filter((e) => e && !e.isDeleted)\n if (!els.length) throw new Error('该 excalidraw 场景没有元素')\n\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const e of els) {\n const xs = [e.x, e.x + (e.width || 0)]\n const ys = [e.y, e.y + (e.height || 0)]\n for (const p of e.points ?? []) {\n xs.push(e.x + p[0])\n ys.push(e.y + p[1])\n }\n minX = Math.min(minX, ...xs)\n maxX = Math.max(maxX, ...xs)\n minY = Math.min(minY, ...ys)\n maxY = Math.max(maxY, ...ys)\n }\n if (!Number.isFinite(minX)) throw new Error('该 excalidraw 场景没有可绘制的元素')\n\n const body = els.map(excalidrawEl).join('')\n const pad = 16\n const w = maxX - minX + pad * 2\n const h = maxY - minY + pad * 2\n const vb = `${minX - pad} ${minY - pad} ${w} ${h}`\n return svgWrap(vb, w, h, data.appState?.viewBackgroundColor || '#ffffff', body)\n}\n\nfunction excalidrawEl(e: ExEl): string {\n const stroke = e.strokeColor && e.strokeColor !== 'transparent' ? e.strokeColor : '#1e1e1e'\n const fill = e.backgroundColor && e.backgroundColor !== 'transparent' ? e.backgroundColor : 'none'\n const sw = e.strokeWidth ?? 1.5\n const op = e.opacity != null ? e.opacity / 100 : 1\n const cx = e.x + (e.width || 0) / 2\n const cy = e.y + (e.height || 0) / 2\n const rot = e.angle ? ` transform=\"rotate(${(e.angle * 180) / Math.PI} ${cx} ${cy})\"` : ''\n const g = (inner: string): string => `<g opacity=\"${op}\"${rot}>${inner}</g>`\n\n switch (e.type) {\n case 'rectangle': {\n const r = e.roundness ? Math.min(32, (e.width || 0) / 4, (e.height || 0) / 4) : 0\n return g(\n `<rect x=\"${e.x}\" y=\"${e.y}\" width=\"${e.width}\" height=\"${e.height}\" rx=\"${r}\" ry=\"${r}\" fill=\"${fill}\" stroke=\"${stroke}\" stroke-width=\"${sw}\"/>`,\n )\n }\n case 'ellipse':\n return g(\n `<ellipse cx=\"${cx}\" cy=\"${cy}\" rx=\"${(e.width || 0) / 2}\" ry=\"${(e.height || 0) / 2}\" fill=\"${fill}\" stroke=\"${stroke}\" stroke-width=\"${sw}\"/>`,\n )\n case 'diamond': {\n const pts = `${cx},${e.y} ${e.x + (e.width || 0)},${cy} ${cx},${e.y + (e.height || 0)} ${e.x},${cy}`\n return g(`<polygon points=\"${pts}\" fill=\"${fill}\" stroke=\"${stroke}\" stroke-width=\"${sw}\"/>`)\n }\n case 'text':\n return g(\n textLeft(\n e.text || '',\n e.x,\n e.y,\n e.fontSize ?? 16,\n e.strokeColor && e.strokeColor !== 'transparent' ? e.strokeColor : '#1e1e1e',\n ),\n )\n case 'arrow':\n case 'line':\n case 'draw':\n case 'freedraw': {\n const pts = (e.points ?? []).map((p) => `${e.x + p[0]},${e.y + p[1]}`).join(' ')\n if (!pts) return ''\n const arrow = e.type === 'arrow' ? ' marker-end=\"url(#dg-arrow)\"' : ''\n return g(\n `<polyline points=\"${pts}\" fill=\"none\" stroke=\"${stroke}\" stroke-width=\"${sw}\"${arrow}/>`,\n )\n }\n default:\n // Unknown element (image / frame / embeddable …): a faint placeholder box keeps the layout honest.\n if (e.width && e.height) {\n return g(\n `<rect x=\"${e.x}\" y=\"${e.y}\" width=\"${e.width}\" height=\"${e.height}\" fill=\"none\" stroke=\"#bbbbbb\" stroke-width=\"1\" stroke-dasharray=\"4 4\"/>`,\n )\n }\n return ''\n }\n}\n\n// Left-anchored, top-based multi-line text (excalidraw text origin is the top-left).\nfunction textLeft(text: string, x: number, y: number, fontSize: number, color: string): string {\n const lh = fontSize * 1.25\n const tspans = text\n .split('\\n')\n .map((ln, i) => `<tspan x=\"${x}\" y=\"${y + fontSize + i * lh}\">${esc(ln)}</tspan>`)\n .join('')\n return `<text font-size=\"${fontSize}\" fill=\"${color}\">${tspans}</text>`\n}\n"],"mappings":"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"}
package/dist/lib/dicom.js CHANGED
@@ -440,3 +440,5 @@ function h(l) {
440
440
  }
441
441
  //#endregion
442
442
  export { n as DicomError, h as readDicom };
443
+
444
+ //# sourceMappingURL=dicom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dicom.js","names":[],"sources":["../../src/lib/dicom.ts"],"sourcesContent":["/**\n * DICOM (.dcm) container parsing for the side-viewer image preview.\n *\n * We use `dicom-parser` (zero-dependency, MIT) ONLY to walk the DICOM Part-10 byte stream and pull out the\n * header attributes + the Pixel Data element. Everything visual — decoding compressed pixel data, the\n * modality/VOI LUTs, windowing, photometric interpretation — is done by us (see lib/dicomCodecs.ts and\n * lib/dicomRender.ts), the same hand-rolled philosophy as the zip/7z/binobj viewers.\n *\n * A DICOM image is a grid of stored pixel samples plus the metadata needed to turn them into something a\n * screen can show: how many bits, signed or not, grayscale vs colour, how to rescale to real units, and a\n * default window (brightness/contrast). This module extracts that metadata and hands raw per-frame bytes to\n * the codec/render layers; it deliberately does no rendering itself.\n */\nimport dicomParser, { type DataSet, type Element } from 'dicom-parser'\n\n/** Tags we read, as dicom-parser's lowercase `xggggeeee` keys. */\nconst TAG = {\n transferSyntax: 'x00020010',\n samplesPerPixel: 'x00280002',\n photometric: 'x00280004',\n planarConfiguration: 'x00280006',\n numberOfFrames: 'x00280008',\n rows: 'x00280010',\n columns: 'x00280011',\n bitsAllocated: 'x00280100',\n bitsStored: 'x00280101',\n highBit: 'x00280102',\n pixelRepresentation: 'x00280103',\n windowCenter: 'x00281050',\n windowWidth: 'x00281051',\n rescaleIntercept: 'x00281052',\n rescaleSlope: 'x00281053',\n // Palette colour LUTs\n redDescriptor: 'x00281101',\n greenDescriptor: 'x00281102',\n blueDescriptor: 'x00281103',\n redData: 'x00281201',\n greenData: 'x00281202',\n blueData: 'x00281203',\n // Descriptive (for the info strip; no patient identifiers are surfaced)\n modality: 'x00080060',\n studyDescription: 'x00081030',\n seriesDescription: 'x0008103e',\n bodyPart: 'x00180015',\n pixelData: 'x7fe00010',\n} as const\n\nexport interface DicomPalette {\n r: Uint8ClampedArray\n g: Uint8ClampedArray\n b: Uint8ClampedArray\n}\n\nexport interface DicomMeta {\n transferSyntaxUid: string\n rows: number\n columns: number\n samplesPerPixel: number\n photometric: string\n bitsAllocated: number\n bitsStored: number\n highBit: number\n /** 0 = unsigned, 1 = two's-complement signed stored values. */\n pixelRepresentation: number\n /** 0 = colour-by-pixel (interleaved RGB), 1 = colour-by-plane (RRR…GGG…BBB). */\n planarConfiguration: number\n numberOfFrames: number\n /** Default VOI window from the file, or null when absent (then we auto-compute one from the pixels). */\n windowCenter: number | null\n windowWidth: number | null\n rescaleSlope: number\n rescaleIntercept: number\n palette: DicomPalette | null\n modality: string\n studyDescription: string\n seriesDescription: string\n bodyPart: string\n}\n\n/** One label→value metadata row (a single DICOM attribute, formatted for display). */\nexport interface DicomTagRow {\n label: string\n value: string\n}\n\n/** A titled group of metadata rows (Patient / Study / Series / …), for the viewer's info panel. */\nexport interface DicomTagGroup {\n title: string\n rows: DicomTagRow[]\n}\n\n/**\n * One slice of a series-stack: an independent DICOM *file* (its own Part-10 header), lazily fetched. The\n * DICOM viewer can browse a list of these as a single scrollable series — the classic hospital export shape\n * where one folder = one series and every file is a single-frame slice. `load()` returns the file's bytes\n * on demand (decoded/cached by the viewer), so hundreds of slices cost nothing until scrolled to.\n */\nexport interface DicomSlice {\n name: string\n size?: number\n load: () => Promise<ArrayBuffer>\n}\n\nexport interface ParsedDicom {\n meta: DicomMeta\n /** Whether the Pixel Data is encapsulated (any compressed transfer syntax). */\n encapsulated: boolean\n /** Curated, human-labelled metadata (present attributes only), grouped for the on-demand info panel. */\n tags: DicomTagGroup[]\n /** Pull the raw bytes for frame `i` — encoded bytes when compressed, raw samples when uncompressed. */\n frameBytes(i: number): Uint8Array\n}\n\nexport class DicomError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'DicomError'\n }\n}\n\nfunction num(ds: DataSet, tag: string, fallback: number): number {\n const v = ds.uint16(tag)\n return typeof v === 'number' && Number.isFinite(v) ? v : fallback\n}\n\nfunction float(ds: DataSet, tag: string, fallback: number): number {\n const v = ds.floatString(tag, 0)\n return typeof v === 'number' && Number.isFinite(v) ? v : fallback\n}\n\nfunction floatOrNull(ds: DataSet, tag: string): number | null {\n const v = ds.floatString(tag, 0)\n return typeof v === 'number' && Number.isFinite(v) ? v : null\n}\n\nfunction str(ds: DataSet, tag: string): string {\n return (ds.string(tag) ?? '').trim()\n}\n\n// A palette-colour LUT element: a 3-value descriptor (entry count, first mapped value, bits/entry) plus the\n// LUT data. 16-bit LUTs are common; we down-sample them to 8-bit per channel for canvas output.\nfunction readPalette(ds: DataSet): DicomPalette | null {\n const rd = ds.elements[TAG.redData]\n const gd = ds.elements[TAG.greenData]\n const bd = ds.elements[TAG.blueData]\n const desc = ds.elements[TAG.redDescriptor]\n if (!rd || !gd || !bd || !desc) return null\n // Descriptor: [numberOfEntries, firstValueMapped, bitsPerEntry]; numberOfEntries 0 means 65536.\n let entries = ds.uint16(TAG.redDescriptor, 0) ?? 0\n if (entries === 0) entries = 65536\n const bits = ds.uint16(TAG.redDescriptor, 2) ?? 8\n const channel = (el: Element): Uint8ClampedArray => {\n const out = new Uint8ClampedArray(entries)\n const bytes = ds.byteArray\n if (bits === 16) {\n // 16-bit LUT, little-endian; take the high byte as the 8-bit value.\n for (let i = 0; i < entries; i++) {\n const off = el.dataOffset + i * 2\n out[i] = off + 1 < bytes.length ? bytes[off + 1] : 0\n }\n } else {\n for (let i = 0; i < entries; i++) {\n const off = el.dataOffset + i\n out[i] = off < bytes.length ? bytes[off] : 0\n }\n }\n return out\n }\n return { r: channel(rd), g: channel(gd), b: channel(bd) }\n}\n\nconst UNCOMPRESSED = new Set([\n '1.2.840.10008.1.2', // Implicit VR Little Endian\n '1.2.840.10008.1.2.1', // Explicit VR Little Endian\n '1.2.840.10008.1.2.2', // Explicit VR Big Endian (handled via byte-swap in the render layer)\n])\n\n// --- Metadata table ---------------------------------------------------------------------------------------\n//\n// A curated, labelled subset of DICOM attributes for the viewer's on-demand info panel. We don't ship a full\n// data dictionary (dicom-parser has none), so we read a hand-picked set of common, useful tags by their known\n// keys + VR kind. `kind` picks the reader/formatter: 'us' is a binary unsigned-short; everything else is a\n// string VR read via dataSet.string(), with date/time/name reformatted for humans.\n\ntype TagKind = 'text' | 'name' | 'date' | 'time' | 'us'\ninterface TagSpec {\n tag: string\n label: string\n kind?: TagKind\n}\ninterface TagGroupSpec {\n title: string\n specs: TagSpec[]\n}\n\nconst TAG_GROUPS: TagGroupSpec[] = [\n {\n title: '患者',\n specs: [\n { tag: 'x00100010', label: '姓名', kind: 'name' },\n { tag: 'x00100020', label: 'ID' },\n { tag: 'x00100030', label: '出生日期', kind: 'date' },\n { tag: 'x00100040', label: '性别' },\n { tag: 'x00101010', label: '年龄' },\n { tag: 'x00101030', label: '体重 (kg)' },\n { tag: 'x00101020', label: '身高 (m)' },\n ],\n },\n {\n title: '检查 (Study)',\n specs: [\n { tag: 'x00080020', label: '检查日期', kind: 'date' },\n { tag: 'x00080030', label: '检查时间', kind: 'time' },\n { tag: 'x00081030', label: '检查描述' },\n { tag: 'x00080050', label: '登记号' },\n { tag: 'x00200010', label: 'Study ID' },\n { tag: 'x00080090', label: '申请医师', kind: 'name' },\n { tag: 'x0020000d', label: 'Study UID' },\n ],\n },\n {\n title: '序列 (Series)',\n specs: [\n { tag: 'x00080060', label: '设备类型' },\n { tag: 'x0008103e', label: '序列描述' },\n { tag: 'x00200011', label: '序列号' },\n { tag: 'x00180015', label: '检查部位' },\n { tag: 'x00185100', label: '患者体位' },\n { tag: 'x00080021', label: '序列日期', kind: 'date' },\n { tag: 'x00080031', label: '序列时间', kind: 'time' },\n { tag: 'x0020000e', label: 'Series UID' },\n ],\n },\n {\n title: '设备',\n specs: [\n { tag: 'x00080070', label: '厂商' },\n { tag: 'x00081090', label: '型号' },\n { tag: 'x00080080', label: '机构' },\n { tag: 'x00081010', label: '工作站' },\n { tag: 'x00181020', label: '软件版本' },\n { tag: 'x00080008', label: '图像类型' },\n ],\n },\n {\n title: '采集参数',\n specs: [\n { tag: 'x00181030', label: '协议名' },\n { tag: 'x00180050', label: '层厚 (mm)' },\n { tag: 'x00180088', label: '层间距 (mm)' },\n { tag: 'x00201041', label: '层位置' },\n { tag: 'x00180060', label: 'KVP' },\n { tag: 'x00181151', label: '管电流 (mA)' },\n { tag: 'x00181150', label: '曝光时间 (ms)' },\n { tag: 'x00181152', label: '曝光量 (mAs)' },\n { tag: 'x00181210', label: '卷积核' },\n { tag: 'x00180080', label: '重复时间 TR (ms)' },\n { tag: 'x00180081', label: '回波时间 TE (ms)' },\n { tag: 'x00180087', label: '磁场强度 (T)' },\n { tag: 'x00181314', label: '翻转角 (°)' },\n ],\n },\n {\n title: '图像',\n specs: [\n { tag: 'x00200013', label: '实例号' },\n { tag: 'x00280008', label: '帧数' },\n { tag: 'x00280010', label: '行 (Rows)', kind: 'us' },\n { tag: 'x00280011', label: '列 (Columns)', kind: 'us' },\n { tag: 'x00280100', label: 'Bits Allocated', kind: 'us' },\n { tag: 'x00280101', label: 'Bits Stored', kind: 'us' },\n { tag: 'x00280002', label: 'Samples/Pixel', kind: 'us' },\n { tag: 'x00280004', label: '光度' },\n { tag: 'x00280030', label: '像素间距' },\n { tag: 'x00281050', label: '窗位' },\n { tag: 'x00281051', label: '窗宽' },\n { tag: 'x00281052', label: '截距' },\n { tag: 'x00281053', label: '斜率' },\n ],\n },\n]\n\nfunction fmtDate(s: string): string {\n const m = /^(\\d{4})(\\d{2})(\\d{2})/.exec(s)\n return m ? `${m[1]}-${m[2]}-${m[3]}` : s\n}\nfunction fmtTime(s: string): string {\n const m = /^(\\d{2})(\\d{2})(\\d{2})/.exec(s)\n return m ? `${m[1]}:${m[2]}:${m[3]}` : s\n}\nfunction fmtPersonName(s: string): string {\n // DICOM PN is caret-delimited (Family^Given^Middle^Prefix^Suffix); show as spaced words.\n return s.replace(/\\^+/g, ' ').replace(/\\s+/g, ' ').trim()\n}\n\nfunction readTagValue(ds: DataSet, spec: TagSpec): string {\n if (spec.kind === 'us') {\n const v = ds.uint16(spec.tag)\n return typeof v === 'number' && Number.isFinite(v) ? String(v) : ''\n }\n const raw = (ds.string(spec.tag) ?? '').trim()\n if (!raw) return ''\n if (spec.kind === 'date') return fmtDate(raw)\n if (spec.kind === 'time') return fmtTime(raw)\n if (spec.kind === 'name') return fmtPersonName(raw)\n // Multi-valued attributes are backslash-delimited; show them comfortably.\n return raw.replace(/\\\\/g, ' / ')\n}\n\n/** Build the curated metadata table, keeping only present (non-empty) attributes and non-empty groups. */\nfunction readTagGroups(ds: DataSet): DicomTagGroup[] {\n const groups: DicomTagGroup[] = []\n for (const g of TAG_GROUPS) {\n const rows: DicomTagRow[] = []\n for (const spec of g.specs) {\n const value = readTagValue(ds, spec)\n if (value) rows.push({ label: spec.label, value })\n }\n if (rows.length > 0) groups.push({ title: g.title, rows })\n }\n return groups\n}\n\n/**\n * Parse a DICOM file. Throws {@link DicomError} when it isn't DICOM, has no image, or uses an unsupported\n * image layout (e.g. 1-bit bitmaps). Compressed pixel data is left encoded — the codec layer decodes it\n * lazily per frame; this just locates the bytes.\n */\nexport function readDicom(buf: ArrayBuffer): ParsedDicom {\n const byteArray = new Uint8Array(buf)\n // DICOM Part-10: 128-byte preamble + \"DICM\" magic. dicom-parser also accepts headerless streams, but we\n // require the magic so we don't misread an arbitrary binary as DICOM.\n if (\n byteArray.length < 132 ||\n byteArray[128] !== 0x44 ||\n byteArray[129] !== 0x49 ||\n byteArray[130] !== 0x43 ||\n byteArray[131] !== 0x4d\n ) {\n throw new DicomError('不是有效的 DICOM 文件(缺少 DICM 标识)')\n }\n\n let ds: DataSet\n try {\n ds = dicomParser.parseDicom(byteArray)\n } catch (e) {\n throw new DicomError(`DICOM 解析失败:${e instanceof Error ? e.message : String(e)}`)\n }\n\n const pixelData = ds.elements[TAG.pixelData]\n if (!pixelData) throw new DicomError('该 DICOM 不含像素数据(可能仅为结构化报告/元数据)')\n\n const rows = num(ds, TAG.rows, 0)\n const columns = num(ds, TAG.columns, 0)\n if (rows < 1 || columns < 1) throw new DicomError('像素尺寸无效')\n\n const bitsAllocated = num(ds, TAG.bitsAllocated, 8)\n if (bitsAllocated !== 8 && bitsAllocated !== 16) {\n throw new DicomError(`暂不支持 ${bitsAllocated}-bit 像素(仅支持 8/16-bit)`)\n }\n\n const meta: DicomMeta = {\n transferSyntaxUid: str(ds, TAG.transferSyntax) || '1.2.840.10008.1.2',\n rows,\n columns,\n samplesPerPixel: num(ds, TAG.samplesPerPixel, 1),\n photometric: str(ds, TAG.photometric) || 'MONOCHROME2',\n bitsAllocated,\n bitsStored: num(ds, TAG.bitsStored, bitsAllocated),\n highBit: num(ds, TAG.highBit, bitsAllocated - 1),\n pixelRepresentation: num(ds, TAG.pixelRepresentation, 0),\n planarConfiguration: num(ds, TAG.planarConfiguration, 0),\n numberOfFrames: Math.max(1, ds.intString(TAG.numberOfFrames) ?? 1),\n windowCenter: floatOrNull(ds, TAG.windowCenter),\n windowWidth: floatOrNull(ds, TAG.windowWidth),\n rescaleSlope: float(ds, TAG.rescaleSlope, 1),\n rescaleIntercept: float(ds, TAG.rescaleIntercept, 0),\n palette: null,\n modality: str(ds, TAG.modality),\n studyDescription: str(ds, TAG.studyDescription),\n seriesDescription: str(ds, TAG.seriesDescription),\n bodyPart: str(ds, TAG.bodyPart),\n }\n if (meta.photometric === 'PALETTE COLOR') meta.palette = readPalette(ds)\n\n const tags = readTagGroups(ds)\n const encapsulated = !UNCOMPRESSED.has(meta.transferSyntaxUid)\n const isRle = meta.transferSyntaxUid === '1.2.840.10008.1.2.5'\n\n // Uncompressed: frames are contiguous, fixed stride. Compressed: pull encoded bytes per frame via the\n // basic offset table / fragments.\n const frameSize = rows * columns * meta.samplesPerPixel * (bitsAllocated / 8)\n const totalFrames = meta.numberOfFrames\n\n function frameBytes(i: number): Uint8Array {\n const frame = Math.min(Math.max(i, 0), totalFrames - 1)\n if (!encapsulated) {\n const start = pixelData.dataOffset + frame * frameSize\n return byteArray.subarray(start, start + frameSize)\n }\n try {\n // RLE: the spec mandates exactly one fragment per frame, so fragment index == frame index.\n if (isRle) {\n return dicomParser.readEncapsulatedPixelDataFromFragments(ds, pixelData, frame)\n }\n // JPEG / JPEG-LS / JPEG2000: a frame may span multiple fragments. Prefer the file's basic offset\n // table; when absent, rebuild one by scanning codestream markers (handles multi-fragment frames like\n // some J2K ultrasound) so readEncapsulatedImageFrame concatenates each frame's fragments correctly.\n let bot = pixelData.basicOffsetTable\n if (!bot || bot.length === 0) {\n try {\n bot = dicomParser.createJPEGBasicOffsetTable(ds, pixelData)\n } catch {\n bot = []\n }\n }\n if (bot && bot.length > 0) {\n return dicomParser.readEncapsulatedImageFrame(ds, pixelData, frame, bot)\n }\n // Still no offset table: a lone frame uses all fragments; otherwise assume one fragment per frame.\n const fragmentCount = pixelData.fragments?.length ?? 1\n if (totalFrames <= 1) {\n return dicomParser.readEncapsulatedPixelDataFromFragments(ds, pixelData, 0, fragmentCount)\n }\n return dicomParser.readEncapsulatedPixelDataFromFragments(ds, pixelData, frame)\n } catch (e) {\n throw new DicomError(\n `读取第 ${frame + 1} 帧像素失败:${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }\n\n return { meta, encapsulated, tags, frameBytes }\n}\n"],"mappings":"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"}
@@ -154,3 +154,5 @@ function g(e, t) {
154
154
  }
155
155
  //#endregion
156
156
  export { i as UnsupportedTransferSyntaxError, m as decodeDicomFrame, r as transferSyntaxName };
157
+
158
+ //# sourceMappingURL=dicomCodecs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dicomCodecs.js","names":[],"sources":["../../src/lib/dicomCodecs.ts"],"sourcesContent":["/**\n * DICOM pixel-data decoding: turn one frame's transfer-syntax-encoded bytes into raw pixel samples.\n *\n * Browsers have no native DICOM/JPEG2000/JPEG-LS decoder, so compressed transfer syntaxes are decoded by the\n * cornerstone WASM codecs (openjpeg / charls / libjpeg-turbo), lazily `import()`-ed only when a compressed\n * frame is actually opened — so the main bundle and non-DICOM users pay nothing. Uncompressed and RLE are\n * handled here directly (RLE is a hand-rolled PackBits unpack — no dependency needed). The *predictive\n * lossless* JPEGs (Process 14 / SV1) are a different beast — libjpeg-turbo is DCT-only and cannot decode\n * them — so they go through `jpeg-lossless-decoder-js` (pure JS), also lazily imported.\n *\n * Output contract: {@link decodeDicomFrame} returns interleaved pixel-sample BYTES laid out exactly like an\n * uncompressed frame would be (little-endian for 16-bit), so the render layer (lib/dicomRender.ts) has a\n * single code path regardless of how the pixels were stored.\n */\nimport type { DicomMeta } from './dicom'\n\n/** Transfer Syntax UIDs we recognise. */\nconst TS = {\n IMPLICIT_LE: '1.2.840.10008.1.2',\n EXPLICIT_LE: '1.2.840.10008.1.2.1',\n EXPLICIT_BE: '1.2.840.10008.1.2.2',\n DEFLATED: '1.2.840.10008.1.2.1.99',\n RLE: '1.2.840.10008.1.2.5',\n JPEG_BASELINE: '1.2.840.10008.1.2.4.50',\n JPEG_EXTENDED: '1.2.840.10008.1.2.4.51',\n JPEG_LOSSLESS_57: '1.2.840.10008.1.2.4.57',\n JPEG_LOSSLESS_70: '1.2.840.10008.1.2.4.70',\n JPEGLS_LOSSLESS: '1.2.840.10008.1.2.4.80',\n JPEGLS_NEAR: '1.2.840.10008.1.2.4.81',\n J2K_LOSSLESS: '1.2.840.10008.1.2.4.90',\n J2K: '1.2.840.10008.1.2.4.91',\n} as const\n\nconst UNCOMPRESSED = new Set<string>([TS.IMPLICIT_LE, TS.EXPLICIT_LE, TS.EXPLICIT_BE])\n\n/** Human-readable transfer-syntax names for the info strip. */\nconst TS_NAMES: Record<string, string> = {\n [TS.IMPLICIT_LE]: '隐式 VR 小端(未压缩)',\n [TS.EXPLICIT_LE]: '显式 VR 小端(未压缩)',\n [TS.EXPLICIT_BE]: '显式 VR 大端(未压缩)',\n [TS.DEFLATED]: 'Deflated 显式 VR',\n [TS.RLE]: 'RLE 无损',\n [TS.JPEG_BASELINE]: 'JPEG Baseline',\n [TS.JPEG_EXTENDED]: 'JPEG Extended (12-bit)',\n [TS.JPEG_LOSSLESS_57]: 'JPEG Lossless (P14)',\n [TS.JPEG_LOSSLESS_70]: 'JPEG Lossless (P14 SV1)',\n [TS.JPEGLS_LOSSLESS]: 'JPEG-LS 无损',\n [TS.JPEGLS_NEAR]: 'JPEG-LS 近无损',\n [TS.J2K_LOSSLESS]: 'JPEG 2000 无损',\n [TS.J2K]: 'JPEG 2000',\n}\n\nexport function transferSyntaxName(uid: string): string {\n return TS_NAMES[uid] ?? uid\n}\n\nexport class UnsupportedTransferSyntaxError extends Error {\n constructor(uid: string) {\n super(`暂不支持的传输语法:${transferSyntaxName(uid)}(${uid})`)\n this.name = 'UnsupportedTransferSyntaxError'\n }\n}\n\nexport interface DecodedFrame {\n /** Interleaved pixel-sample bytes (little-endian for 16-bit), laid out like an uncompressed frame. */\n bytes: Uint8Array\n littleEndian: boolean\n bitsPerSample: number\n componentCount: number\n}\n\n// Lazy, memoised codec module promises — each WASM module instantiates once and is reused across frames.\nlet openjpegMod: Promise<DicomCodecModule> | null = null\nlet charlsMod: Promise<DicomCodecModule> | null = null\nlet libjpegMod: Promise<DicomCodecModule> | null = null\n\nfunction loadOpenJpeg(): Promise<DicomCodecModule> {\n openjpegMod ??= import('@cornerstonejs/codec-openjpeg/decode').then((m) => m.default())\n return openjpegMod\n}\nfunction loadCharls(): Promise<DicomCodecModule> {\n charlsMod ??= import('@cornerstonejs/codec-charls/decode').then((m) => m.default())\n return charlsMod\n}\nfunction loadLibjpeg(): Promise<DicomCodecModule> {\n libjpegMod ??= import('@cornerstonejs/codec-libjpeg-turbo-8bit/decode').then((m) => m.default())\n return libjpegMod\n}\n\n// Predictive-lossless JPEG (Process 14 / SV1) — a pure-JS decoder, since no WASM codec here handles it.\nlet jpegLosslessMod: Promise<typeof import('jpeg-lossless-decoder-js')> | null = null\nfunction loadJpegLossless(): Promise<typeof import('jpeg-lossless-decoder-js')> {\n jpegLosslessMod ??= import('jpeg-lossless-decoder-js')\n return jpegLosslessMod\n}\n\n// Run one decode through a WASM codec: copy encoded bytes in, decode, copy decoded bytes out (before the\n// instance — and its heap views — are freed).\nfunction runCodec(\n mod: DicomCodecModule,\n ctor: (new () => DicomCodecDecoder) | undefined,\n encoded: Uint8Array,\n): { bytes: Uint8Array; info: DicomCodecFrameInfo } {\n if (!ctor) throw new Error('codec 模块缺少解码器')\n const decoder = new ctor()\n try {\n const inBuf = decoder.getEncodedBuffer(encoded.length)\n inBuf.set(encoded)\n decoder.decode()\n const info = decoder.getFrameInfo()\n // Copy out of WASM memory — the view is invalidated by delete().\n const bytes = decoder.getDecodedBuffer().slice()\n return { bytes, info }\n } finally {\n decoder.delete()\n }\n}\n\n/** Decode one frame. Async because compressed transfer syntaxes lazy-load a WASM codec. */\nexport async function decodeDicomFrame(\n meta: DicomMeta,\n encoded: Uint8Array,\n): Promise<DecodedFrame> {\n const ts = meta.transferSyntaxUid\n\n if (UNCOMPRESSED.has(ts)) {\n return {\n bytes: encoded,\n littleEndian: ts !== TS.EXPLICIT_BE,\n bitsPerSample: meta.bitsAllocated,\n componentCount: meta.samplesPerPixel,\n }\n }\n\n if (ts === TS.RLE) {\n return {\n bytes: decodeRle(encoded, meta),\n littleEndian: true,\n bitsPerSample: meta.bitsAllocated,\n componentCount: meta.samplesPerPixel,\n }\n }\n\n // Predictive-lossless JPEG (Process 14, incl. SV1 first-order prediction). libjpeg-turbo/openjpeg can't\n // touch this codec family; the rii-mango decoder returns the samples as a Uint8/Uint16Array in native\n // (little-endian) byte order — exactly our interleaved-LE output contract.\n if (ts === TS.JPEG_LOSSLESS_57 || ts === TS.JPEG_LOSSLESS_70) {\n try {\n const { Decoder } = await loadJpegLossless()\n const decoder = new Decoder()\n const out = decoder.decode(encoded.buffer, encoded.byteOffset, encoded.byteLength)\n return {\n bytes: new Uint8Array(out.buffer, out.byteOffset, out.byteLength),\n littleEndian: true,\n bitsPerSample: (decoder.numBytes || meta.bitsAllocated / 8) * 8,\n componentCount: decoder.numComp || meta.samplesPerPixel,\n }\n } catch (e) {\n throw new Error(\n `${transferSyntaxName(ts)} 解码失败:${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }\n\n let mod: DicomCodecModule\n let ctor: (new () => DicomCodecDecoder) | undefined\n switch (ts) {\n case TS.JPEG_BASELINE:\n case TS.JPEG_EXTENDED:\n mod = await loadLibjpeg()\n ctor = mod.JPEGDecoder\n break\n case TS.JPEGLS_LOSSLESS:\n case TS.JPEGLS_NEAR:\n mod = await loadCharls()\n ctor = mod.JpegLSDecoder\n break\n case TS.J2K_LOSSLESS:\n case TS.J2K:\n mod = await loadOpenJpeg()\n ctor = mod.J2KDecoder\n break\n default:\n throw new UnsupportedTransferSyntaxError(ts)\n }\n\n let info: DicomCodecFrameInfo\n let bytes: Uint8Array\n try {\n ;({ bytes, info } = runCodec(mod, ctor, encoded))\n } catch (e) {\n throw new Error(\n `${transferSyntaxName(ts)} 解码失败:${e instanceof Error ? e.message : String(e)}`,\n )\n }\n\n return {\n bytes,\n littleEndian: true,\n bitsPerSample: info.bitsPerSample || meta.bitsAllocated,\n componentCount: info.componentCount || meta.samplesPerPixel,\n }\n}\n\n// --- DICOM RLE (PackBits) ---------------------------------------------------------------------------------\n//\n// An RLE frame opens with a 64-byte header: a uint32 segment count followed by up to 15 uint32 segment\n// offsets (all little-endian). Each segment is PackBits-compressed and unpacks to rows*columns bytes. For\n// multi-byte samples the segments are ordered most-significant byte first; for colour, all byte-planes of\n// sample 0, then sample 1, … We reassemble them into the same interleaved little-endian layout an\n// uncompressed frame would have.\n\nfunction unpackBits(src: Uint8Array, expected: number): Uint8Array {\n const out = new Uint8Array(expected)\n let o = 0\n let i = 0\n while (i < src.length && o < expected) {\n const n = (src[i++] << 24) >> 24 // sign-extend the control byte to int8\n if (n >= 0) {\n const count = n + 1\n for (let k = 0; k < count && i < src.length && o < expected; k++) out[o++] = src[i++]\n } else if (n !== -128) {\n const count = 1 - n\n const val = i < src.length ? src[i++] : 0\n for (let k = 0; k < count && o < expected; k++) out[o++] = val\n }\n // n === -128 is a no-op per the PackBits spec.\n }\n return out\n}\n\nfunction decodeRle(encoded: Uint8Array, meta: DicomMeta): Uint8Array {\n const { rows, columns, samplesPerPixel, bitsAllocated } = meta\n const pixels = rows * columns\n const bytesPerSample = bitsAllocated / 8\n const dv = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength)\n const segCount = dv.getUint32(0, true)\n const offsets: number[] = []\n for (let i = 0; i < segCount; i++) offsets.push(dv.getUint32(4 + i * 4, true))\n\n const segs: Uint8Array[] = []\n for (let s = 0; s < segCount; s++) {\n const start = offsets[s]\n const end = s + 1 < segCount ? offsets[s + 1] : encoded.length\n segs.push(unpackBits(encoded.subarray(start, end), pixels))\n }\n\n const out = new Uint8Array(pixels * samplesPerPixel * bytesPerSample)\n for (let sp = 0; sp < samplesPerPixel; sp++) {\n for (let b = 0; b < bytesPerSample; b++) {\n const seg = segs[sp * bytesPerSample + b]\n if (!seg) continue\n // Segment byte-plane b is most-significant first → little-endian byte index within the sample.\n const leByte = bytesPerSample - 1 - b\n for (let p = 0; p < pixels; p++) {\n out[(p * samplesPerPixel + sp) * bytesPerSample + leByte] = seg[p]\n }\n }\n }\n return out\n}\n"],"mappings":"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"}
@@ -112,3 +112,5 @@ function s(e, t) {
112
112
  }
113
113
  //#endregion
114
114
  export { o as autoWindow, a as buildFramePixels, s as renderToImageData };
115
+
116
+ //# sourceMappingURL=dicomRender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dicomRender.js","names":[],"sources":["../../src/lib/dicomRender.ts"],"sourcesContent":["/**\n * DICOM pixel → screen rendering: stored samples to displayable RGBA.\n *\n * Splits into two steps so the interactive window/level (brightness/contrast) drag stays cheap:\n * 1. {@link buildFramePixels} — decode the raw bytes into per-pixel values ONCE per frame (apply the\n * modality rescale LUT, honour bit depth / signedness / endianness, separate grayscale vs RGB vs\n * palette). The expensive parse happens here.\n * 2. {@link renderToImageData} — apply the VOI window + invert and write RGBA. Cheap enough to re-run on\n * every drag frame, since it only maps already-decoded values.\n *\n * The VOI window mapping follows the DICOM PS3.3 C.11.2.1.2 LINEAR formula. MONOCHROME1 (0 = white) is\n * handled by inverting the grayscale ramp.\n */\nimport type { DecodedFrame } from './dicomCodecs'\nimport type { DicomMeta, DicomPalette } from './dicom'\n\nexport interface FramePixels {\n width: number\n height: number\n mode: 'gray' | 'rgb' | 'palette'\n /** Grayscale: per-pixel modality value (rescale slope/intercept already applied). */\n gray?: Float32Array\n /** Palette: per-pixel LUT index. */\n indices?: Int32Array\n palette?: DicomPalette\n /** RGB: interleaved R,G,B bytes (3 per pixel). */\n rgb?: Uint8Array\n /** Grayscale modality-value range, for auto-windowing. */\n minValue: number\n maxValue: number\n /** MONOCHROME1: low values display bright — the grayscale ramp is inverted. */\n monochrome1: boolean\n}\n\nexport interface WindowParams {\n windowCenter: number\n windowWidth: number\n invert: boolean\n}\n\nfunction isColor(meta: DicomMeta): boolean {\n return meta.samplesPerPixel >= 3\n}\n\n// Build per-pixel grayscale modality values from the raw sample bytes (honouring bit depth, the bitsStored\n// mask, two's-complement signedness, and byte order), then apply the modality rescale (slope/intercept).\nfunction buildGray(\n decoded: DecodedFrame,\n meta: DicomMeta,\n): {\n gray: Float32Array\n min: number\n max: number\n} {\n const n = meta.rows * meta.columns\n const gray = new Float32Array(n)\n const bytes = decoded.bytes\n const le = decoded.littleEndian\n const slope = meta.rescaleSlope\n const intercept = meta.rescaleIntercept\n const stored = meta.bitsStored\n const signed = meta.pixelRepresentation === 1\n const signBit = 1 << (stored - 1)\n const signSpan = 1 << stored\n let min = Infinity\n let max = -Infinity\n\n if (meta.bitsAllocated === 16) {\n const mask = stored >= 16 ? 0xffff : signSpan - 1\n for (let i = 0; i < n; i++) {\n const o = i * 2\n let raw = le ? bytes[o] | (bytes[o + 1] << 8) : bytes[o + 1] | (bytes[o] << 8)\n raw &= mask\n if (signed && raw & signBit) raw -= signSpan\n const v = raw * slope + intercept\n gray[i] = v\n if (v < min) min = v\n if (v > max) max = v\n }\n } else {\n const mask = stored >= 8 ? 0xff : signSpan - 1\n for (let i = 0; i < n; i++) {\n let raw = bytes[i] & mask\n if (signed && raw & signBit) raw -= signSpan\n const v = raw * slope + intercept\n gray[i] = v\n if (v < min) min = v\n if (v > max) max = v\n }\n }\n if (!Number.isFinite(min)) {\n min = 0\n max = 0\n }\n return { gray, min, max }\n}\n\nfunction buildPaletteIndices(decoded: DecodedFrame, meta: DicomMeta): Int32Array {\n const n = meta.rows * meta.columns\n const out = new Int32Array(n)\n const bytes = decoded.bytes\n const le = decoded.littleEndian\n if (meta.bitsAllocated === 16) {\n for (let i = 0; i < n; i++) {\n const o = i * 2\n out[i] = le ? bytes[o] | (bytes[o + 1] << 8) : bytes[o + 1] | (bytes[o] << 8)\n }\n } else {\n for (let i = 0; i < n; i++) out[i] = bytes[i]\n }\n return out\n}\n\n// Build interleaved RGB bytes, undoing planar configuration and converting YBR_FULL to RGB when needed.\n// (Codec output for JPEG/JPEG-LS is already interleaved RGB; this mainly covers uncompressed colour.)\nfunction buildRgb(decoded: DecodedFrame, meta: DicomMeta): Uint8Array {\n const n = meta.rows * meta.columns\n const src = decoded.bytes\n const out = new Uint8Array(n * 3)\n const planar = meta.planarConfiguration === 1\n if (planar) {\n // R plane, then G plane, then B plane.\n for (let i = 0; i < n; i++) {\n out[i * 3] = src[i]\n out[i * 3 + 1] = src[n + i]\n out[i * 3 + 2] = src[2 * n + i]\n }\n } else {\n out.set(src.subarray(0, n * 3))\n }\n const photo = meta.photometric\n if (photo === 'YBR_FULL' || photo === 'YBR_FULL_422') {\n for (let i = 0; i < n; i++) {\n const o = i * 3\n const y = out[o]\n const cb = out[o + 1] - 128\n const cr = out[o + 2] - 128\n out[o] = clamp8(y + 1.402 * cr)\n out[o + 1] = clamp8(y - 0.344136 * cb - 0.714136 * cr)\n out[o + 2] = clamp8(y + 1.772 * cb)\n }\n }\n return out\n}\n\nfunction clamp8(v: number): number {\n return v < 0 ? 0 : v > 255 ? 255 : v | 0\n}\n\n/** Decode one frame's bytes into per-pixel values (the expensive step; do this once per frame). */\nexport function buildFramePixels(decoded: DecodedFrame, meta: DicomMeta): FramePixels {\n const base = {\n width: meta.columns,\n height: meta.rows,\n minValue: 0,\n maxValue: 0,\n monochrome1: meta.photometric === 'MONOCHROME1',\n }\n if (meta.photometric === 'PALETTE COLOR' && meta.palette) {\n return {\n ...base,\n mode: 'palette',\n indices: buildPaletteIndices(decoded, meta),\n palette: meta.palette,\n }\n }\n if (isColor(meta)) {\n return { ...base, mode: 'rgb', rgb: buildRgb(decoded, meta) }\n }\n const { gray, min, max } = buildGray(decoded, meta)\n return { ...base, mode: 'gray', gray, minValue: min, maxValue: max }\n}\n\n/** A sensible default window: the file's own VOI when present, else the full modality-value range. */\nexport function autoWindow(fp: FramePixels): { center: number; width: number } {\n const width = Math.max(1, fp.maxValue - fp.minValue)\n const center = (fp.maxValue + fp.minValue) / 2\n return { center, width }\n}\n\n/** Apply the VOI window + invert and produce RGBA pixels for the canvas. Cheap — safe to call on each drag. */\nexport function renderToImageData(fp: FramePixels, params: WindowParams): ImageData {\n const { width, height } = fp\n const out = new Uint8ClampedArray(width * height * 4)\n\n if (fp.mode === 'gray' && fp.gray) {\n const c = params.windowCenter\n const w = params.windowWidth > 1 ? params.windowWidth : 1\n const lo = c - 0.5 - (w - 1) / 2\n const hi = c - 0.5 + (w - 1) / 2\n const denom = w - 1 || 1\n const center = c - 0.5\n // MONOCHROME1 XOR the user's invert toggle.\n const inv = params.invert !== fp.monochrome1\n const g = fp.gray\n for (let i = 0; i < g.length; i++) {\n const v = g[i]\n let y: number\n if (v <= lo) y = 0\n else if (v > hi) y = 255\n else y = ((v - center) / denom + 0.5) * 255\n if (inv) y = 255 - y\n const o = i * 4\n out[o] = y\n out[o + 1] = y\n out[o + 2] = y\n out[o + 3] = 255\n }\n } else if (fp.mode === 'palette' && fp.indices && fp.palette) {\n const { r, g, b } = fp.palette\n const last = r.length - 1\n const idx = fp.indices\n for (let i = 0; i < idx.length; i++) {\n const k = idx[i] < 0 ? 0 : idx[i] > last ? last : idx[i]\n const o = i * 4\n out[o] = r[k]\n out[o + 1] = g[k]\n out[o + 2] = b[k]\n out[o + 3] = 255\n }\n } else if (fp.mode === 'rgb' && fp.rgb) {\n const rgb = fp.rgb\n const n = width * height\n for (let i = 0; i < n; i++) {\n const o = i * 4\n const s = i * 3\n out[o] = rgb[s]\n out[o + 1] = rgb[s + 1]\n out[o + 2] = rgb[s + 2]\n out[o + 3] = 255\n }\n }\n\n return new ImageData(out, width, height)\n}\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ElementNavigationItem } from '@/composables/useElementNavigation';
2
+ interface DocxNode {
3
+ type?: string;
4
+ styleName?: string;
5
+ outlineLevel?: number;
6
+ children?: DocxNode[];
7
+ }
8
+ interface DocxStyle {
9
+ id?: string | null;
10
+ name?: string | null;
11
+ basedOn?: string | null;
12
+ paragraphProps?: {
13
+ outlineLevel?: number;
14
+ };
15
+ }
16
+ export interface ParsedDocxModel {
17
+ documentPart?: {
18
+ body?: DocxNode;
19
+ };
20
+ stylesPart?: {
21
+ styles?: DocxStyle[];
22
+ };
23
+ }
24
+ export interface DocxNavigation {
25
+ outline: ElementNavigationItem[];
26
+ pages: ElementNavigationItem[];
27
+ }
28
+ export declare function buildDocxPageNavigation(renderRoot: HTMLElement): ElementNavigationItem[];
29
+ /** Build Word-style outline/page navigation from docx-preview's parsed model and rendered DOM. */
30
+ export declare function buildDocxNavigation(document: ParsedDocxModel, renderRoot: HTMLElement): DocxNavigation;
31
+ export {};
@@ -0,0 +1,65 @@
1
+ //#region src/lib/docxNavigation.ts
2
+ var e = /(?:heading|标题|標題|titre|überschrift|titolo|encabezado|nagłówek)\s*[-_. ]*([1-6])/i;
3
+ function t(e) {
4
+ let t = (e ?? "").replace(/\s+/g, " ").trim();
5
+ return t.length > 120 ? `${t.slice(0, 117)}…` : t;
6
+ }
7
+ function n(e) {
8
+ return typeof e == "number" && Number.isInteger(e) && e >= 0 && e <= 5 ? e : void 0;
9
+ }
10
+ function r(...t) {
11
+ for (let n of t) {
12
+ let t = n?.match(e);
13
+ if (t) return Number(t[1]) - 1;
14
+ }
15
+ }
16
+ function i(e, t) {
17
+ for (let n of e) {
18
+ if (n.type === "paragraph") {
19
+ t.push(n);
20
+ continue;
21
+ }
22
+ n.children?.length && i(n.children, t);
23
+ }
24
+ }
25
+ function a(e) {
26
+ return Array.from(e.querySelectorAll(".docx-wrapper > section.docx"), (e, t) => ({
27
+ id: `docx-page-${t}`,
28
+ title: `第 ${t + 1} 页`,
29
+ level: 0,
30
+ target: e
31
+ }));
32
+ }
33
+ function o(e, o) {
34
+ let s = e.stylesPart?.styles, c = (Array.isArray(s) ? s : []).filter((e) => !!e.id), l = new Map(c.map((e) => [e.id, e])), u = /* @__PURE__ */ new Map();
35
+ function d(e, t = /* @__PURE__ */ new Set()) {
36
+ if (!e) return;
37
+ if (u.has(e)) return u.get(e);
38
+ if (t.has(e)) return;
39
+ t.add(e);
40
+ let i = l.get(e), a = n(i?.paragraphProps?.outlineLevel) ?? r(e, i?.id, i?.name) ?? d(i?.basedOn ?? void 0, t);
41
+ return u.set(e, a), a;
42
+ }
43
+ let f = [];
44
+ i(e.documentPart?.body?.children ?? [], f);
45
+ let p = Array.from(o.querySelectorAll(".docx-wrapper > section.docx > article p")), m = [];
46
+ for (let e = 0; e < Math.min(f.length, p.length); e += 1) {
47
+ let r = f[e], i = p[e], a = n(r.outlineLevel) ?? d(r.styleName), o = t(i.textContent);
48
+ if (a === void 0 || !o) continue;
49
+ let s = `docx-outline-${m.length}`;
50
+ i.dataset.documentNavId = s, m.push({
51
+ id: s,
52
+ title: o,
53
+ level: a,
54
+ target: i
55
+ });
56
+ }
57
+ return {
58
+ outline: m,
59
+ pages: a(o)
60
+ };
61
+ }
62
+ //#endregion
63
+ export { o as buildDocxNavigation, a as buildDocxPageNavigation };
64
+
65
+ //# sourceMappingURL=docxNavigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docxNavigation.js","names":[],"sources":["../../src/lib/docxNavigation.ts"],"sourcesContent":["import type { ElementNavigationItem } from '@/composables/useElementNavigation'\n\ninterface DocxNode {\n type?: string\n styleName?: string\n outlineLevel?: number\n children?: DocxNode[]\n}\n\ninterface DocxStyle {\n id?: string | null\n name?: string | null\n basedOn?: string | null\n paragraphProps?: { outlineLevel?: number }\n}\n\nexport interface ParsedDocxModel {\n documentPart?: { body?: DocxNode }\n stylesPart?: { styles?: DocxStyle[] }\n}\n\nexport interface DocxNavigation {\n outline: ElementNavigationItem[]\n pages: ElementNavigationItem[]\n}\n\nconst HEADING_NAME =\n /(?:heading|标题|標題|titre|überschrift|titolo|encabezado|nagłówek)\\s*[-_. ]*([1-6])/i\n\nfunction cleanTitle(value: string | null | undefined): string {\n const text = (value ?? '').replace(/\\s+/g, ' ').trim()\n return text.length > 120 ? `${text.slice(0, 117)}…` : text\n}\n\nfunction normalizedOutlineLevel(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 5\n ? value\n : undefined\n}\n\nfunction headingLevelFromName(...values: Array<string | null | undefined>): number | undefined {\n for (const value of values) {\n const match = value?.match(HEADING_NAME)\n if (match) return Number(match[1]) - 1\n }\n return undefined\n}\n\nfunction collectParagraphs(nodes: readonly DocxNode[], output: DocxNode[]): void {\n for (const node of nodes) {\n if (node.type === 'paragraph') {\n output.push(node)\n continue\n }\n if (node.children?.length) collectParagraphs(node.children, output)\n }\n}\n\nexport function buildDocxPageNavigation(renderRoot: HTMLElement): ElementNavigationItem[] {\n return Array.from(\n renderRoot.querySelectorAll<HTMLElement>('.docx-wrapper > section.docx'),\n (target, index): ElementNavigationItem => ({\n id: `docx-page-${index}`,\n title: `第 ${index + 1} 页`,\n level: 0,\n target,\n }),\n )\n}\n\n/** Build Word-style outline/page navigation from docx-preview's parsed model and rendered DOM. */\nexport function buildDocxNavigation(\n document: ParsedDocxModel,\n renderRoot: HTMLElement,\n): DocxNavigation {\n const rawStyles = document.stylesPart?.styles\n const styles = (Array.isArray(rawStyles) ? rawStyles : []).filter(\n (style): style is DocxStyle & { id: string } => !!style.id,\n )\n const stylesById = new Map(styles.map((style) => [style.id, style]))\n const resolvedLevels = new Map<string, number | undefined>()\n\n function styleLevel(styleId: string | undefined, seen = new Set<string>()): number | undefined {\n if (!styleId) return undefined\n if (resolvedLevels.has(styleId)) return resolvedLevels.get(styleId)\n if (seen.has(styleId)) return undefined\n seen.add(styleId)\n const style = stylesById.get(styleId)\n const level =\n normalizedOutlineLevel(style?.paragraphProps?.outlineLevel) ??\n headingLevelFromName(styleId, style?.id, style?.name) ??\n styleLevel(style?.basedOn ?? undefined, seen)\n resolvedLevels.set(styleId, level)\n return level\n }\n\n const paragraphs: DocxNode[] = []\n collectParagraphs(document.documentPart?.body?.children ?? [], paragraphs)\n const renderedParagraphs = Array.from(\n renderRoot.querySelectorAll<HTMLElement>('.docx-wrapper > section.docx > article p'),\n )\n const outline: ElementNavigationItem[] = []\n\n for (let index = 0; index < Math.min(paragraphs.length, renderedParagraphs.length); index += 1) {\n const paragraph = paragraphs[index]\n const target = renderedParagraphs[index]\n const level = normalizedOutlineLevel(paragraph.outlineLevel) ?? styleLevel(paragraph.styleName)\n const title = cleanTitle(target.textContent)\n if (level === undefined || !title) continue\n const id = `docx-outline-${outline.length}`\n target.dataset.documentNavId = id\n outline.push({ id, title, level, target })\n }\n\n const pages = buildDocxPageNavigation(renderRoot)\n\n return { outline, pages }\n}\n"],"mappings":";AA0BA,IAAM,IACJ;AAEF,SAAS,EAAW,GAA0C;CAC5D,IAAM,KAAQ,KAAS,GAAA,CAAI,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CACrD,OAAO,EAAK,SAAS,MAAM,GAAG,EAAK,MAAM,GAAG,GAAG,EAAE,KAAK;AACxD;AAEA,SAAS,EAAuB,GAAoC;CAClE,OAAO,OAAO,KAAU,YAAY,OAAO,UAAU,CAAK,KAAK,KAAS,KAAK,KAAS,IAClF,IACA,KAAA;AACN;AAEA,SAAS,EAAqB,GAAG,GAA8D;CAC7F,KAAK,IAAM,KAAS,GAAQ;EAC1B,IAAM,IAAQ,GAAO,MAAM,CAAY;EACvC,IAAI,GAAO,OAAO,OAAO,EAAM,EAAE,IAAI;CACvC;AAEF;AAEA,SAAS,EAAkB,GAA4B,GAA0B;CAC/E,KAAK,IAAM,KAAQ,GAAO;EACxB,IAAI,EAAK,SAAS,aAAa;GAC7B,EAAO,KAAK,CAAI;GAChB;EACF;EACA,AAAI,EAAK,UAAU,UAAQ,EAAkB,EAAK,UAAU,CAAM;CACpE;AACF;AAEA,SAAgB,EAAwB,GAAkD;CACxF,OAAO,MAAM,KACX,EAAW,iBAA8B,8BAA8B,IACtE,GAAQ,OAAkC;EACzC,IAAI,aAAa;EACjB,OAAO,KAAK,IAAQ,EAAE;EACtB,OAAO;EACP;CACF,EACF;AACF;AAGA,SAAgB,EACd,GACA,GACgB;CAChB,IAAM,IAAY,EAAS,YAAY,QACjC,KAAU,MAAM,QAAQ,CAAS,IAAI,IAAY,CAAC,EAAA,CAAG,QACxD,MAA+C,CAAC,CAAC,EAAM,EAC1D,GACM,IAAa,IAAI,IAAI,EAAO,KAAK,MAAU,CAAC,EAAM,IAAI,CAAK,CAAC,CAAC,GAC7D,oBAAiB,IAAI,IAAgC;CAE3D,SAAS,EAAW,GAA6B,oBAAO,IAAI,IAAY,GAAuB;EAC7F,IAAI,CAAC,GAAS;EACd,IAAI,EAAe,IAAI,CAAO,GAAG,OAAO,EAAe,IAAI,CAAO;EAClE,IAAI,EAAK,IAAI,CAAO,GAAG;EACvB,EAAK,IAAI,CAAO;EAChB,IAAM,IAAQ,EAAW,IAAI,CAAO,GAC9B,IACJ,EAAuB,GAAO,gBAAgB,YAAY,KAC1D,EAAqB,GAAS,GAAO,IAAI,GAAO,IAAI,KACpD,EAAW,GAAO,WAAW,KAAA,GAAW,CAAI;EAE9C,OADA,EAAe,IAAI,GAAS,CAAK,GAC1B;CACT;CAEA,IAAM,IAAyB,CAAC;CAChC,EAAkB,EAAS,cAAc,MAAM,YAAY,CAAC,GAAG,CAAU;CACzE,IAAM,IAAqB,MAAM,KAC/B,EAAW,iBAA8B,0CAA0C,CACrF,GACM,IAAmC,CAAC;CAE1C,KAAK,IAAI,IAAQ,GAAG,IAAQ,KAAK,IAAI,EAAW,QAAQ,EAAmB,MAAM,GAAG,KAAS,GAAG;EAC9F,IAAM,IAAY,EAAW,IACvB,IAAS,EAAmB,IAC5B,IAAQ,EAAuB,EAAU,YAAY,KAAK,EAAW,EAAU,SAAS,GACxF,IAAQ,EAAW,EAAO,WAAW;EAC3C,IAAI,MAAU,KAAA,KAAa,CAAC,GAAO;EACnC,IAAM,IAAK,gBAAgB,EAAQ;EAEnC,AADA,EAAO,QAAQ,gBAAgB,GAC/B,EAAQ,KAAK;GAAE;GAAI;GAAO;GAAO;EAAO,CAAC;CAC3C;CAIA,OAAO;EAAE;EAAS,OAFJ,EAAwB,CAEpB;CAAM;AAC1B"}
package/dist/lib/dxf.js CHANGED
@@ -397,3 +397,5 @@ function x(e, t) {
397
397
  }
398
398
  //#endregion
399
399
  export { _ as parseDxf };
400
+
401
+ //# sourceMappingURL=dxf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dxf.js","names":[],"sources":["../../src/lib/dxf.ts"],"sourcesContent":["/**\n * Hand-rolled, zero-dependency DXF (AutoCAD Drawing Exchange Format) parser for the file viewer — turns an\n * ASCII DXF into a flat list of drawable primitives the viewer draws as an SVG (in the codebase's hand-rolled\n * house style, like lib/geo.ts / lib/diagram.ts — no CAD runtime, no CDN).\n *\n * DXF is a stream of (group-code, value) line PAIRS grouped into SECTIONS. We read:\n * - TABLES → the LAYER table (name → ACI colour + on/off), so entities colour \"ByLayer\".\n * - BLOCKS → reusable geometry, keyed by name, expanded wherever an INSERT references them (with the\n * insert's translation / scale / rotation; nested INSERTs are expanded recursively, depth-capped).\n * - ENTITIES → the drawing itself.\n * Supported entities: LINE, LWPOLYLINE (incl. bulge arcs), POLYLINE/VERTEX, CIRCLE, ARC, ELLIPSE (tessellated),\n * POINT, TEXT / MTEXT, SOLID / 3DFACE / TRACE (filled), SPLINE (fit/control points as a polyline), INSERT.\n * Anything else (HATCH / DIMENSION / WIPEOUT / …) is skipped — the drawing still renders, minus that entity.\n *\n * Coordinates are kept in DXF world space (Y up); the viewer flips Y for SVG. Binary DXF is not supported.\n */\n\nexport interface DxfLayer {\n name: string\n color: string\n on: boolean\n}\n\nexport type DxfPrim =\n | { kind: 'line'; layer: string; color: string; x1: number; y1: number; x2: number; y2: number }\n | { kind: 'polyline'; layer: string; color: string; pts: [number, number][]; closed: boolean }\n | { kind: 'circle'; layer: string; color: string; cx: number; cy: number; r: number }\n | {\n kind: 'arc'\n layer: string\n color: string\n cx: number\n cy: number\n r: number\n a0: number\n a1: number\n }\n | { kind: 'polygon'; layer: string; color: string; pts: [number, number][] }\n | {\n kind: 'text'\n layer: string\n color: string\n x: number\n y: number\n h: number\n text: string\n rot: number\n align: 'l' | 'c' | 'r'\n }\n | { kind: 'point'; layer: string; color: string; x: number; y: number }\n\nexport interface DxfBounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport interface ParsedDxf {\n prims: DxfPrim[]\n layers: DxfLayer[]\n bounds: DxfBounds | null\n counts: Record<string, number>\n}\n\n// AutoCAD Color Index → a legible hex on a light background (1-9 named colours + a grey ramp fallback).\nconst ACI: Record<number, string> = {\n 1: '#e03131',\n 2: '#f08c00',\n 3: '#2f9e44',\n 4: '#0c8599',\n 5: '#1971c2',\n 6: '#ae3ec9',\n 7: '#1a1a1a',\n 8: '#868e96',\n 9: '#adb5bd',\n}\nfunction aciColor(i: number): string {\n if (ACI[i]) return ACI[i]\n if (i >= 250 && i <= 255) {\n const g = Math.round(51 + ((i - 250) / 5) * 200)\n return `rgb(${g},${g},${g})`\n }\n return '#1a1a1a'\n}\n\n// ── tokenize ────────────────────────────────────────────────────────────────────────────────────\ninterface Pair {\n code: number\n value: string\n}\n\nfunction tokenize(text: string): Pair[] {\n const lines = text.split(/\\r\\n|\\r|\\n/)\n const pairs: Pair[] = []\n // Pairs are (code line, value line). Tolerate a trailing dangling code line.\n for (let i = 0; i + 1 < lines.length; i += 2) {\n const code = Number.parseInt(lines[i].trim(), 10)\n if (Number.isNaN(code)) {\n // Resync: skip one line (some exporters emit stray blank lines). Step back to keep parity.\n i -= 1\n continue\n }\n pairs.push({ code, value: lines[i + 1] })\n }\n return pairs\n}\n\ninterface Ent {\n type: string\n pairs: Pair[]\n}\n\n/** Split a [from,to) pair range into entities delimited by group-code-0 markers. */\nfunction splitEntities(pairs: Pair[], from: number, to: number): Ent[] {\n const ents: Ent[] = []\n let cur: Ent | null = null\n for (let i = from; i < to; i++) {\n const p = pairs[i]\n if (p.code === 0) {\n if (cur) ents.push(cur)\n cur = { type: p.value.trim(), pairs: [] }\n } else if (cur) {\n cur.pairs.push(p)\n }\n }\n if (cur) ents.push(cur)\n return ents\n}\n\nfunction first(ent: Ent, code: number): string | undefined {\n for (const p of ent.pairs) if (p.code === code) return p.value\n return undefined\n}\nfunction fnum(ent: Ent, code: number, def = 0): number {\n const v = first(ent, code)\n if (v === undefined) return def\n const n = Number.parseFloat(v.trim())\n return Number.isFinite(n) ? n : def\n}\nfunction inum(ent: Ent, code: number, def = 0): number {\n const v = first(ent, code)\n if (v === undefined) return def\n const n = Number.parseInt(v.trim(), 10)\n return Number.isFinite(n) ? n : def\n}\n\n// ── section ranges ────────────────────────────────────────────────────────────────────────────\nfunction sectionRanges(pairs: Pair[]): Map<string, [number, number]> {\n const out = new Map<string, [number, number]>()\n for (let i = 0; i + 1 < pairs.length; i++) {\n if (pairs[i].code === 0 && pairs[i].value.trim() === 'SECTION' && pairs[i + 1].code === 2) {\n const name = pairs[i + 1].value.trim()\n let j = i + 2\n while (j < pairs.length && !(pairs[j].code === 0 && pairs[j].value.trim() === 'ENDSEC')) j++\n out.set(name, [i + 2, j])\n }\n }\n return out\n}\n\n// ── layers ──────────────────────────────────────────────────────────────────────────────────\nfunction parseLayers(pairs: Pair[], range: [number, number] | undefined): Map<string, DxfLayer> {\n const layers = new Map<string, DxfLayer>()\n if (!range) return layers\n for (const ent of splitEntities(pairs, range[0], range[1])) {\n if (ent.type !== 'LAYER') continue\n const name = (first(ent, 2) ?? '').trim()\n if (!name) continue\n const ci = inum(ent, 62, 7)\n layers.set(name, { name, color: aciColor(Math.abs(ci)), on: ci >= 0 })\n }\n return layers\n}\n\n// ── affine transform (for INSERT expansion) ──────────────────────────────────────────────────\ninterface Mat {\n a: number\n b: number\n c: number\n d: number\n e: number\n f: number\n} // x' = a*x + c*y + e ; y' = b*x + d*y + f\nconst IDENT: Mat = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }\nfunction apply(m: Mat, x: number, y: number): [number, number] {\n return [m.a * x + m.c * y + m.e, m.b * x + m.d * y + m.f]\n}\nfunction mul(m: Mat, n: Mat): Mat {\n // m ∘ n (apply n first, then m)\n return {\n a: m.a * n.a + m.c * n.b,\n b: m.b * n.a + m.d * n.b,\n c: m.a * n.c + m.c * n.d,\n d: m.b * n.c + m.d * n.d,\n e: m.a * n.e + m.c * n.f + m.e,\n f: m.b * n.e + m.d * n.f + m.f,\n }\n}\nfunction insertMat(ins: Ent): Mat {\n const px = fnum(ins, 10)\n const py = fnum(ins, 20)\n const sx = fnum(ins, 41, 1)\n const sy = fnum(ins, 42, 1)\n const rot = (fnum(ins, 50, 0) * Math.PI) / 180\n const cos = Math.cos(rot)\n const sin = Math.sin(rot)\n // translate(px,py) · rotate(rot) · scale(sx,sy)\n return {\n a: cos * sx,\n b: sin * sx,\n c: -sin * sy,\n d: cos * sy,\n e: px,\n f: py,\n }\n}\n// Average scale of a matrix (for radii under scaling — approximate for non-uniform scale).\nfunction scaleOf(m: Mat): number {\n const s1 = Math.hypot(m.a, m.b)\n const s2 = Math.hypot(m.c, m.d)\n return (s1 + s2) / 2 || 1\n}\nfunction rotOf(m: Mat): number {\n return (Math.atan2(m.b, m.a) * 180) / Math.PI\n}\n\n// ── bulge tessellation (LWPOLYLINE / POLYLINE arc segments) ──────────────────────────────────\nfunction bulgeArc(p1: [number, number], p2: [number, number], bulge: number): [number, number][] {\n if (!bulge) return [p2]\n const theta = 4 * Math.atan(bulge) // included angle\n const chord = Math.hypot(p2[0] - p1[0], p2[1] - p1[1])\n if (chord < 1e-9) return [p2]\n const radius = chord / 2 / Math.sin(Math.abs(theta) / 2)\n const mid = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2]\n // perpendicular offset from chord midpoint to arc centre\n const apothem = radius * Math.cos(Math.abs(theta) / 2)\n const dir = [(p2[0] - p1[0]) / chord, (p2[1] - p1[1]) / chord]\n const nrm = bulge > 0 ? [dir[1], -dir[0]] : [-dir[1], dir[0]]\n const cx = mid[0] + nrm[0] * apothem\n const cy = mid[1] + nrm[1] * apothem\n const a0 = Math.atan2(p1[1] - cy, p1[0] - cx)\n const steps = Math.max(2, Math.min(64, Math.ceil((Math.abs(theta) / (Math.PI * 2)) * 48)))\n const out: [number, number][] = []\n for (let i = 1; i <= steps; i++) {\n const a = a0 + (theta * i) / steps\n out.push([cx + radius * Math.cos(a), cy + radius * Math.sin(a)])\n }\n return out\n}\n\n// ── MTEXT inline formatting strip ─────────────────────────────────────────────────────────────\nfunction stripMtext(s: string): string {\n return s\n .replace(/\\\\P/g, '\\n') // paragraph\n .replace(/\\\\[A-Za-z]+;?/g, '') // \\fArial; \\H2.5; \\C1; …\n .replace(/[{}]/g, '')\n .replace(/\\\\([{}\\\\])/g, '$1')\n .trim()\n}\n\n// ── entity → primitives ──────────────────────────────────────────────────────────────────────\nexport function parseDxf(text: string): ParsedDxf {\n const pairs = tokenize(text)\n const sections = sectionRanges(pairs)\n const layerMap = parseLayers(pairs, sections.get('TABLES'))\n\n // Blocks: name → its entity list (between BLOCK and ENDBLK), plus base point.\n const blocks = new Map<string, { base: [number, number]; ents: Ent[] }>()\n const blocksRange = sections.get('BLOCKS')\n if (blocksRange) {\n const ents = splitEntities(pairs, blocksRange[0], blocksRange[1])\n let cur: { name: string; base: [number, number]; ents: Ent[] } | null = null\n for (const ent of ents) {\n if (ent.type === 'BLOCK') {\n cur = { name: (first(ent, 2) ?? '').trim(), base: [fnum(ent, 10), fnum(ent, 20)], ents: [] }\n } else if (ent.type === 'ENDBLK') {\n if (cur && cur.name) blocks.set(cur.name, { base: cur.base, ents: cur.ents })\n cur = null\n } else if (cur) {\n cur.ents.push(ent)\n }\n }\n }\n\n const prims: DxfPrim[] = []\n const counts: Record<string, number> = {}\n\n const layerColor = (name: string): string => layerMap.get(name)?.color ?? '#1a1a1a'\n // Resolve an entity's colour: explicit ACI (62), else ByLayer, else the INSERT-inherited colour.\n const colorOf = (ent: Ent, layer: string, inherited: string): string => {\n const ci = inum(ent, 62, 256)\n if (ci === 0 || ci === 256) return ci === 0 ? inherited : layerColor(layer)\n return aciColor(Math.abs(ci))\n }\n\n function emit(ents: Ent[], m: Mat, inheritedColor: string, depth: number): void {\n for (let i = 0; i < ents.length; i++) {\n const ent = ents[i]\n const type = ent.type\n const layer = (first(ent, 8) ?? '0').trim()\n const color = colorOf(ent, layer, inheritedColor)\n const P = (x: number, y: number): [number, number] => apply(m, x, y)\n\n switch (type) {\n case 'LINE': {\n const [x1, y1] = P(fnum(ent, 10), fnum(ent, 20))\n const [x2, y2] = P(fnum(ent, 11), fnum(ent, 21))\n prims.push({ kind: 'line', layer, color, x1, y1, x2, y2 })\n break\n }\n case 'LWPOLYLINE': {\n const raw = lwVertices(ent)\n const pts = expandBulges(raw)\n const closed = (inum(ent, 70, 0) & 1) === 1\n prims.push({\n kind: 'polyline',\n layer,\n color,\n pts: pts.map(([x, y]) => P(x, y)),\n closed,\n })\n break\n }\n case 'POLYLINE': {\n // old-style: following VERTEX entities until SEQEND\n const verts: { pt: [number, number]; bulge: number }[] = []\n let j = i + 1\n while (j < ents.length && ents[j].type === 'VERTEX') {\n verts.push({ pt: [fnum(ents[j], 10), fnum(ents[j], 20)], bulge: fnum(ents[j], 42, 0) })\n j++\n }\n if (j < ents.length && ents[j].type === 'SEQEND') j++\n i = j - 1\n const raw = verts.map((v) => ({ x: v.pt[0], y: v.pt[1], bulge: v.bulge }))\n const pts = expandBulges(raw)\n const closed = (inum(ent, 70, 0) & 1) === 1\n if (pts.length)\n prims.push({\n kind: 'polyline',\n layer,\n color,\n pts: pts.map(([x, y]) => P(x, y)),\n closed,\n })\n break\n }\n case 'CIRCLE': {\n const [cx, cy] = P(fnum(ent, 10), fnum(ent, 20))\n prims.push({ kind: 'circle', layer, color, cx, cy, r: fnum(ent, 40) * scaleOf(m) })\n break\n }\n case 'ARC': {\n const [cx, cy] = P(fnum(ent, 10), fnum(ent, 20))\n const rot = rotOf(m)\n prims.push({\n kind: 'arc',\n layer,\n color,\n cx,\n cy,\n r: fnum(ent, 40) * scaleOf(m),\n a0: fnum(ent, 50) + rot,\n a1: fnum(ent, 51) + rot,\n })\n break\n }\n case 'ELLIPSE': {\n prims.push({ kind: 'polyline', layer, color, pts: ellipsePts(ent, P), closed: false })\n break\n }\n case 'POINT': {\n const [x, y] = P(fnum(ent, 10), fnum(ent, 20))\n prims.push({ kind: 'point', layer, color, x, y })\n break\n }\n case 'TEXT': {\n const s = (first(ent, 1) ?? '').trim()\n if (s) {\n const [x, y] = P(fnum(ent, 10), fnum(ent, 20))\n const ha = inum(ent, 72, 0)\n prims.push({\n kind: 'text',\n layer,\n color,\n x,\n y,\n h: fnum(ent, 40, 2.5) * scaleOf(m),\n text: s,\n rot: fnum(ent, 50, 0) + rotOf(m),\n align: ha === 1 ? 'c' : ha === 2 ? 'r' : 'l',\n })\n }\n break\n }\n case 'MTEXT': {\n const s = stripMtext(\n ent.pairs\n .filter((p) => p.code === 1 || p.code === 3)\n .map((p) => p.value)\n .join(''),\n )\n if (s) {\n const [x, y] = P(fnum(ent, 10), fnum(ent, 20))\n const at = inum(ent, 71, 1) // attachment: 1-3 top, 4-6 mid, 7-9 bottom; col%3 → l/c/r\n prims.push({\n kind: 'text',\n layer,\n color,\n x,\n y,\n h: fnum(ent, 40, 2.5) * scaleOf(m),\n text: s,\n rot: fnum(ent, 50, 0) + rotOf(m),\n align: at % 3 === 2 ? 'c' : at % 3 === 0 ? 'r' : 'l',\n })\n }\n break\n }\n case 'SOLID':\n case 'TRACE':\n case '3DFACE': {\n // corner order for SOLID/TRACE is 0,1,3,2 (bow-tie in file order); 3DFACE is 0,1,2,3\n const c0 = P(fnum(ent, 10), fnum(ent, 20))\n const c1 = P(fnum(ent, 11), fnum(ent, 21))\n const c2 = P(fnum(ent, 12), fnum(ent, 22))\n const c3 = P(fnum(ent, 13), fnum(ent, 23))\n const pts = type === '3DFACE' ? [c0, c1, c2, c3] : [c0, c1, c3, c2]\n prims.push({ kind: 'polygon', layer, color, pts })\n break\n }\n case 'SPLINE': {\n const pts = splinePts(ent, P)\n if (pts.length >= 2) prims.push({ kind: 'polyline', layer, color, pts, closed: false })\n break\n }\n case 'INSERT': {\n if (depth > 12) break\n const name = (first(ent, 2) ?? '').trim()\n const blk = blocks.get(name)\n if (blk) {\n const base: Mat = { ...IDENT, e: -blk.base[0], f: -blk.base[1] }\n const local = mul(insertMat(ent), base)\n emit(blk.ents, mul(m, local), color, depth + 1)\n }\n break\n }\n default:\n break\n }\n if (type && type !== 'VERTEX' && type !== 'SEQEND') counts[type] = (counts[type] ?? 0) + 1\n }\n }\n\n const entRange = sections.get('ENTITIES')\n if (entRange) emit(splitEntities(pairs, entRange[0], entRange[1]), IDENT, '#1a1a1a', 0)\n\n // Bounds over every primitive point.\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n const grow = (x: number, y: number): void => {\n if (x < minX) minX = x\n if (x > maxX) maxX = x\n if (y < minY) minY = y\n if (y > maxY) maxY = y\n }\n for (const p of prims) {\n if (p.kind === 'line') {\n grow(p.x1, p.y1)\n grow(p.x2, p.y2)\n } else if (p.kind === 'polyline' || p.kind === 'polygon') {\n for (const [x, y] of p.pts) grow(x, y)\n } else if (p.kind === 'circle' || p.kind === 'arc') {\n grow(p.cx - p.r, p.cy - p.r)\n grow(p.cx + p.r, p.cy + p.r)\n } else {\n grow(p.x, p.y)\n }\n }\n const bounds = Number.isFinite(minX) ? { minX, minY, maxX, maxY } : null\n\n // Only surface layers that actually carry geometry (plus keep their table colour/on state).\n const used = new Set(prims.map((p) => p.layer))\n const layers: DxfLayer[] = []\n for (const name of used) {\n const l = layerMap.get(name)\n layers.push(l ?? { name, color: '#1a1a1a', on: true })\n }\n layers.sort((a, b) => a.name.localeCompare(b.name))\n\n return { prims, layers, bounds, counts }\n}\n\n// LWPOLYLINE vertices in order. Each vertex is `10 (x), 20 (y)` with an optional trailing `42 (bulge)`; a new\n// `10` starts the next vertex (flushing the current one).\nfunction lwVertices(ent: Ent): { x: number; y: number; bulge: number }[] {\n const out: { x: number; y: number; bulge: number }[] = []\n let cur: { x: number; y: number; bulge: number } | null = null\n for (const p of ent.pairs) {\n if (p.code === 10) {\n if (cur) out.push(cur)\n cur = { x: Number.parseFloat(p.value), y: 0, bulge: 0 }\n } else if (p.code === 20 && cur) {\n cur.y = Number.parseFloat(p.value)\n } else if (p.code === 42 && cur) {\n cur.bulge = Number.parseFloat(p.value)\n }\n }\n if (cur) out.push(cur)\n return out\n}\n\n// Turn a vertex list with per-vertex bulges into a flat point list (bulge arcs tessellated).\nfunction expandBulges(verts: { x: number; y: number; bulge: number }[]): [number, number][] {\n if (!verts.length) return []\n const pts: [number, number][] = [[verts[0].x, verts[0].y]]\n for (let i = 0; i + 1 < verts.length; i++) {\n const a: [number, number] = [verts[i].x, verts[i].y]\n const b: [number, number] = [verts[i + 1].x, verts[i + 1].y]\n for (const p of bulgeArc(a, b, verts[i].bulge)) pts.push(p)\n }\n return pts\n}\n\nfunction ellipsePts(ent: Ent, P: (x: number, y: number) => [number, number]): [number, number][] {\n const cx = fnum(ent, 10)\n const cy = fnum(ent, 20)\n const mx = fnum(ent, 11) // major axis endpoint (relative to centre)\n const my = fnum(ent, 21)\n const ratio = fnum(ent, 40, 1)\n const start = fnum(ent, 41, 0)\n const end = fnum(ent, 42, Math.PI * 2)\n const major = Math.hypot(mx, my)\n const minor = major * ratio\n const rot = Math.atan2(my, mx)\n const cos = Math.cos(rot)\n const sin = Math.sin(rot)\n const span = end - start <= 0 ? end - start + Math.PI * 2 : end - start\n const steps = Math.max(8, Math.min(96, Math.ceil((span / (Math.PI * 2)) * 72)))\n const out: [number, number][] = []\n for (let i = 0; i <= steps; i++) {\n const a = start + (span * i) / steps\n const ex = Math.cos(a) * major\n const ey = Math.sin(a) * minor\n out.push(P(cx + ex * cos - ey * sin, cy + ex * sin + ey * cos))\n }\n return out\n}\n\nfunction splinePts(ent: Ent, P: (x: number, y: number) => [number, number]): [number, number][] {\n // Best effort: prefer fit points (11/21); else control points (10/20). No NURBS evaluation.\n const fit: [number, number][] = []\n const ctrl: [number, number][] = []\n let x: number | null = null\n for (const p of ent.pairs) {\n if (p.code === 11) x = Number.parseFloat(p.value)\n else if (p.code === 21 && x !== null) {\n fit.push(P(x, Number.parseFloat(p.value)))\n x = null\n } else if (p.code === 10) x = Number.parseFloat(p.value)\n else if (p.code === 20 && x !== null) {\n ctrl.push(P(x, Number.parseFloat(p.value)))\n x = null\n }\n }\n return fit.length ? fit : ctrl\n}\n"],"mappings":"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"}
package/dist/lib/eml.js CHANGED
@@ -44,3 +44,5 @@ function l(l) {
44
44
  }
45
45
  //#endregion
46
46
  export { l as parseEml };
47
+
48
+ //# sourceMappingURL=eml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eml.js","names":[],"sources":["../../src/lib/eml.ts"],"sourcesContent":["/**\n * Hand-rolled EML email parser — the email-specific flattening on top of the shared MIME core (lib/mime.ts).\n *\n * An `.eml` file is a MIME message; lib/mime.ts parses the container into an {@link Entity} tree and decodes\n * each leaf. Here we flatten that tree into a render-ready shape for EmlViewer.vue: the header fields, the\n * best body (HTML preferred, plain-text fallback), and the attachment + inline-image leaves. Email inline\n * images are referenced from the HTML body by `cid:` (Content-ID), so we also index them by content-id.\n */\n\nimport {\n bytesToBinary,\n collectLeaves,\n decodeCharset,\n decodeWords,\n getHeader,\n parseEntity,\n parseHeaders,\n splitHeadersBody,\n type Entity,\n} from '@/lib/mime'\n\n/** A non-body leaf: an attachment or an inline (cid-referenced) resource. */\nexport interface EmlAttachment {\n filename: string\n contentType: string\n size: number\n bytes: Uint8Array\n /** disposition:inline, or an image carrying a Content-ID (referenced from the HTML body via `cid:`). */\n isInline: boolean\n /** Content-ID without the angle brackets, '' if none — the key the HTML body's `cid:` refs resolve to. */\n contentId: string\n}\n\n/** Flattened, render-ready view of a parsed email. */\nexport interface ParsedEmail {\n subject: string\n from: string\n to: string\n cc: string\n bcc: string\n replyTo: string\n /** Raw `Date:` header value (the viewer formats it for display). */\n date: string\n /** Decoded text/html body, or null when the message has no HTML alternative. */\n htmlBody: string | null\n /** Decoded text/plain body, or null when the message has no plain alternative. */\n textBody: string | null\n /** Attachments + inline resources (inline ones are also indexed in {@link inlineById}). */\n attachments: EmlAttachment[]\n /** contentId → resource, for resolving `cid:` references in the HTML body to data URLs. */\n inlineById: Map<string, EmlAttachment>\n}\n\n/** A type/subtype with no filename → a synthetic one, so every attachment has a downloadable name. */\nfunction defaultName(type: string, i: number): string {\n const ext = type.split('/')[1]?.replace(/[^a-z0-9.+-]/gi, '') || 'bin'\n return `attachment-${i + 1}.${ext}`\n}\n\n/** Parse raw `.eml` bytes into a flattened {@link ParsedEmail}. */\nexport function parseEml(bytes: Uint8Array): ParsedEmail {\n const bin = bytesToBinary(bytes)\n const root = parseEntity(bin)\n const headers = parseHeaders(splitHeadersBody(bin)[0])\n\n const leaves: Entity[] = []\n collectLeaves(root, leaves)\n\n let htmlBody: string | null = null\n let textBody: string | null = null\n const attachments: EmlAttachment[] = []\n const inlineById = new Map<string, EmlAttachment>()\n\n for (const lf of leaves) {\n const bodyBytes = lf.bodyBytes ?? new Uint8Array()\n const isAttachment = lf.disposition === 'attachment' || !!lf.filename\n // The first non-attachment text/html and text/plain leaves are the body alternatives.\n if (!isAttachment && lf.contentType === 'text/html' && htmlBody === null) {\n htmlBody = decodeCharset(bodyBytes, lf.charset)\n continue\n }\n if (!isAttachment && lf.contentType === 'text/plain' && textBody === null) {\n textBody = decodeCharset(bodyBytes, lf.charset)\n continue\n }\n // Everything else is an attachment or an inline resource.\n const att: EmlAttachment = {\n filename: lf.filename || defaultName(lf.contentType, attachments.length),\n contentType: lf.contentType,\n size: bodyBytes.length,\n bytes: bodyBytes,\n isInline:\n lf.disposition === 'inline' || (!!lf.contentId && lf.contentType.startsWith('image/')),\n contentId: lf.contentId,\n }\n attachments.push(att)\n if (lf.contentId) inlineById.set(lf.contentId, att)\n }\n\n return {\n subject: decodeWords(getHeader(headers, 'subject')),\n from: decodeWords(getHeader(headers, 'from')),\n to: decodeWords(getHeader(headers, 'to')),\n cc: decodeWords(getHeader(headers, 'cc')),\n bcc: decodeWords(getHeader(headers, 'bcc')),\n replyTo: decodeWords(getHeader(headers, 'reply-to')),\n date: getHeader(headers, 'date'),\n htmlBody,\n textBody,\n attachments,\n inlineById,\n }\n}\n"],"mappings":";;AAsDA,SAAS,EAAY,GAAc,GAAmB;CACpD,IAAM,IAAM,EAAK,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,QAAQ,kBAAkB,EAAE,KAAK;CACjE,OAAO,cAAc,IAAI,EAAE,GAAG;AAChC;AAGA,SAAgB,EAAS,GAAgC;CACvD,IAAM,IAAM,EAAc,CAAK,GACzB,IAAO,EAAY,CAAG,GACtB,IAAU,EAAa,EAAiB,CAAG,CAAC,CAAC,EAAE,GAE/C,IAAmB,CAAC;CAC1B,EAAc,GAAM,CAAM;CAE1B,IAAI,IAA0B,MAC1B,IAA0B,MACxB,IAA+B,CAAC,GAChC,oBAAa,IAAI,IAA2B;CAElD,KAAK,IAAM,KAAM,GAAQ;EACvB,IAAM,IAAY,EAAG,aAAa,IAAI,WAAW,GAC3C,IAAe,EAAG,gBAAgB,gBAAgB,CAAC,CAAC,EAAG;EAE7D,IAAI,CAAC,KAAgB,EAAG,gBAAgB,eAAe,MAAa,MAAM;GACxE,IAAW,EAAc,GAAW,EAAG,OAAO;GAC9C;EACF;EACA,IAAI,CAAC,KAAgB,EAAG,gBAAgB,gBAAgB,MAAa,MAAM;GACzE,IAAW,EAAc,GAAW,EAAG,OAAO;GAC9C;EACF;EAEA,IAAM,IAAqB;GACzB,UAAU,EAAG,YAAY,EAAY,EAAG,aAAa,EAAY,MAAM;GACvE,aAAa,EAAG;GAChB,MAAM,EAAU;GAChB,OAAO;GACP,UACE,EAAG,gBAAgB,YAAa,CAAC,CAAC,EAAG,aAAa,EAAG,YAAY,WAAW,QAAQ;GACtF,WAAW,EAAG;EAChB;EAEA,AADA,EAAY,KAAK,CAAG,GAChB,EAAG,aAAW,EAAW,IAAI,EAAG,WAAW,CAAG;CACpD;CAEA,OAAO;EACL,SAAS,EAAY,EAAU,GAAS,SAAS,CAAC;EAClD,MAAM,EAAY,EAAU,GAAS,MAAM,CAAC;EAC5C,IAAI,EAAY,EAAU,GAAS,IAAI,CAAC;EACxC,IAAI,EAAY,EAAU,GAAS,IAAI,CAAC;EACxC,KAAK,EAAY,EAAU,GAAS,KAAK,CAAC;EAC1C,SAAS,EAAY,EAAU,GAAS,UAAU,CAAC;EACnD,MAAM,EAAU,GAAS,MAAM;EAC/B;EACA;EACA;EACA;CACF;AACF"}
@@ -42,3 +42,5 @@ function o(t, n = !1) {
42
42
  }
43
43
  //#endregion
44
44
  export { o as decodeFileNameBytes, a as decodeTextSniff };
45
+
46
+ //# sourceMappingURL=encoding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.js","names":[],"sources":["../../src/lib/encoding.ts"],"sourcesContent":["/**\n * Decode an archive entry's filename bytes, sniffing the encoding.\n *\n * Archive formats store names in whatever byte encoding the producer's OS used: ZIP without its UTF-8 flag and\n * legacy tar both carry raw bytes, and on Chinese Windows that's usually **GBK** — which turns into mojibake if\n * force-decoded as UTF-8. So we probe: try strict UTF-8 first (the modern default, and a superset of ASCII); if\n * the bytes aren't valid UTF-8, try GBK; if neither decodes cleanly, fall back to a lossless `\\uXXXX` escape of\n * the raw bytes so the name is at least unambiguous instead of garbled.\n *\n * \"Strict\" = a `fatal` TextDecoder that throws on an invalid sequence, which is exactly our \"is it this\n * encoding?\" probe. Non-streaming `decode()` calls are independent, so the decoders are safely reused.\n */\n\nconst utf8Strict = new TextDecoder('utf-8', { fatal: true })\n\nlet gbkStrict: TextDecoder | null = null\nlet gbkInit = false\nfunction getGbk(): TextDecoder | null {\n if (!gbkInit) {\n gbkInit = true\n try {\n // 'gbk' maps to the gb18030 decoder per the Encoding spec — present in every browser; a minimal Node\n // ICU may lack it, in which case we just skip the GBK probe.\n gbkStrict = new TextDecoder('gbk', { fatal: true })\n } catch {\n gbkStrict = null\n }\n }\n return gbkStrict\n}\n\n/** Escape each raw byte as `\\u00XX` (printable ASCII kept verbatim) — a deterministic, reversible last resort. */\nfunction escapeBytes(bytes: Uint8Array): string {\n let out = ''\n for (const b of bytes) {\n if (b >= 0x20 && b < 0x7f) out += String.fromCharCode(b)\n else out += '\\\\u' + b.toString(16).padStart(4, '0')\n }\n return out\n}\n\n/**\n * Decode a TEXT file's bytes, sniffing the encoding: UTF-8 → GBK → lenient UTF-8.\n *\n * Same probe as {@link decodeFileNameBytes}, but for file *content* (e.g. a `.lrc` lyrics file, which on\n * Chinese Windows is almost always GBK and turns to mojibake if force-decoded as UTF-8). The last resort\n * differs: instead of a `\\uXXXX` escape (right for a filename) we fall back to a non-fatal UTF-8 decode,\n * which yields the readable text with `�` for any stray invalid byte — the sane default for prose.\n */\nexport function decodeTextSniff(bytes: Uint8Array): string {\n if (bytes.length === 0) return ''\n try {\n return utf8Strict.decode(bytes)\n } catch {\n /* not valid UTF-8 → try GBK */\n }\n const gbk = getGbk()\n if (gbk) {\n try {\n return gbk.decode(bytes)\n } catch {\n /* not clean GBK either → lenient UTF-8 below */\n }\n }\n return new TextDecoder('utf-8').decode(bytes)\n}\n\n/**\n * Decode filename bytes as UTF-8 → GBK → `\\uXXXX` escape (in that order of preference).\n *\n * `normalizeSeparators` rewrites backslashes to '/' on a *successfully decoded* name (ZIP from Windows'\n * Compress-Archive emits '\\'), but never on the escaped fallback — whose own `\\u…` backslashes must survive.\n */\nexport function decodeFileNameBytes(bytes: Uint8Array, normalizeSeparators = false): string {\n if (bytes.length === 0) return ''\n try {\n const s = utf8Strict.decode(bytes)\n return normalizeSeparators ? s.replace(/\\\\/g, '/') : s\n } catch {\n /* not valid UTF-8 → try GBK */\n }\n const gbk = getGbk()\n if (gbk) {\n try {\n const s = gbk.decode(bytes)\n return normalizeSeparators ? s.replace(/\\\\/g, '/') : s\n } catch {\n /* not valid GBK either → escape */\n }\n }\n return escapeBytes(bytes)\n}\n"],"mappings":";AAaA,IAAM,IAAa,IAAI,YAAY,SAAS,EAAE,OAAO,GAAK,CAAC,GAEvD,IAAgC,MAChC,IAAU;AACd,SAAS,IAA6B;CACpC,IAAI,CAAC,GAAS;EACZ,IAAU;EACV,IAAI;GAGF,IAAY,IAAI,YAAY,OAAO,EAAE,OAAO,GAAK,CAAC;EACpD,QAAQ;GACN,IAAY;EACd;CACF;CACA,OAAO;AACT;AAGA,SAAS,EAAY,GAA2B;CAC9C,IAAI,IAAM;CACV,KAAK,IAAM,KAAK,GACd,AAAI,KAAK,MAAQ,IAAI,MAAM,KAAO,OAAO,aAAa,CAAC,IAClD,KAAO,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CAEpD,OAAO;AACT;AAUA,SAAgB,EAAgB,GAA2B;CACzD,IAAI,EAAM,WAAW,GAAG,OAAO;CAC/B,IAAI;EACF,OAAO,EAAW,OAAO,CAAK;CAChC,QAAQ,CAER;CACA,IAAM,IAAM,EAAO;CACnB,IAAI,GACF,IAAI;EACF,OAAO,EAAI,OAAO,CAAK;CACzB,QAAQ,CAER;CAEF,OAAO,IAAI,YAAY,OAAO,CAAC,CAAC,OAAO,CAAK;AAC9C;AAQA,SAAgB,EAAoB,GAAmB,IAAsB,IAAe;CAC1F,IAAI,EAAM,WAAW,GAAG,OAAO;CAC/B,IAAI;EACF,IAAM,IAAI,EAAW,OAAO,CAAK;EACjC,OAAO,IAAsB,EAAE,QAAQ,OAAO,GAAG,IAAI;CACvD,QAAQ,CAER;CACA,IAAM,IAAM,EAAO;CACnB,IAAI,GACF,IAAI;EACF,IAAM,IAAI,EAAI,OAAO,CAAK;EAC1B,OAAO,IAAsB,EAAE,QAAQ,OAAO,GAAG,IAAI;CACvD,QAAQ,CAER;CAEF,OAAO,EAAY,CAAK;AAC1B"}
@@ -328,3 +328,5 @@ function f(e, t = !0) {
328
328
  }
329
329
  //#endregion
330
330
  export { c as parseStatementBlock };
331
+
332
+ //# sourceMappingURL=codeDataParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codeDataParser.js","names":[],"sources":["../../../../src/lib/epl/code/codeDataParser.ts"],"sourcesContent":["/**\n * Code-data decompiler — port of `CodeDataParser.cs` (read path).\n *\n * Turns a method's `expressionData` (a bytecode-like stream) back into a {@link StatementBlock} tree. The C#\n * also rebuilds the line/block offset tables as a side effect; those writers are pure outputs that never\n * influence the reads, so this port drops them entirely and only produces the statement tree.\n *\n * Statement opcodes: 0x50 否则 / 0x51 如果结束 / 0x52 如果真结束 / 0x53 判断Case结束 / 0x54 判断结束 /\n * 0x55 循环体结束 / 0x6A 常规Call / 0x6B 如果 / 0x6C 如果真 / 0x6D 判断开始 / 0x6E 判断Case / 0x6F 默认 /\n * 0x70 循环首 / 0x71 循环尾. Expression opcodes are handled in {@link parseExpression}.\n */\nimport { BinaryReader } from '../io/binaryReader'\nimport { gbk } from '../io/encoding'\nimport { decomposeLibDataTypeId, ID_NAV, isLibDataType } from '../model/eplSystemId'\nimport type { CallExpr, CaseInfo, Expr, LoopCommon, StatementBlock, Stmt } from './ast'\n\nconst KNOWN_TYPE_ID = new Set([\n 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,\n])\n\n// Transient singletons that mark the end of a param list / array literal during parsing (never rendered).\nconst PARAM_LIST_END: Expr = { kind: 'paramListEnd' }\nconst ARRAY_LITERAL_END: Expr = { kind: 'arrayLiteralEnd' }\nconst DEFAULT_VALUE: Expr = { kind: 'default' }\n\ninterface ParsedCall {\n exp: CallExpr\n unexaminedCode: string | null\n comment: string | null\n mask: boolean\n}\n\n/** Parse a method's expression-data bytes into a statement block. `dec` = the method's codepage decoder. */\nexport function parseStatementBlock(\n expressionData: Uint8Array,\n dec: TextDecoder = gbk(),\n): StatementBlock {\n const r = new BinaryReader(expressionData, dec)\n return parseBlock(r)\n}\n\nfunction parseBlock(r: BinaryReader): StatementBlock {\n const statements: Stmt[] = []\n for (;;) {\n let type: number\n // Skip bytes that aren't a known statement opcode (the gaps between statements).\n for (;;) {\n if (r.pos >= r.length) return { statements }\n type = r.readByte()\n if (KNOWN_TYPE_ID.has(type)) break\n }\n const startOffset = r.pos - 1\n\n // Block terminators: rewind one byte and hand control back to the caller.\n if (\n type === 0x50 ||\n type === 0x51 ||\n type === 0x52 ||\n type === 0x53 ||\n type === 0x54 ||\n type === 0x6f ||\n type === 0x71\n ) {\n r.pos = startOffset\n return { statements }\n }\n if (type === 0x55) continue // loop-body end marker — nothing to emit\n\n if (type === 0x6d) {\n // .判断开始 — a switch. The 0x6E (first case) follows immediately.\n if (r.readByte() !== 0x6e) throw new Error('CodeData: expected 0x6E after 0x6D')\n const cases: CaseInfo[] = []\n let switchType: number\n do {\n const c = parseCall(r)\n const condition = c.exp.paramList?.[0] ?? null\n const block = parseBlock(r)\n cases.push({\n condition,\n unexaminedCode: c.unexaminedCode,\n block,\n comment: c.comment,\n mask: c.mask,\n })\n if (r.readByte() !== 0x53) throw new Error('CodeData: expected 0x53 (case end)')\n switchType = r.readByte()\n } while (switchType === 0x6e)\n if (switchType !== 0x6f) throw new Error('CodeData: expected 0x6F (default)')\n const defaultBlock = parseBlock(r)\n if (r.readByte() !== 0x54) throw new Error('CodeData: expected 0x54 (switch end)')\n r.readByte() // 0x74\n statements.push({ kind: 'switch', cases, defaultBlock })\n continue\n }\n\n // 0x70 loop / 0x6C if-true / 0x6B if-else / 0x6A expression — all start with a call expression.\n const { exp, unexaminedCode, comment, mask } = parseCall(r)\n switch (type) {\n case 0x70: {\n const block = parseBlock(r)\n if (r.readByte() !== 0x71) throw new Error('CodeData: expected 0x71 (loop end)')\n const end = parseCall(r)\n if (exp.libraryId !== 0) throw new Error('CodeData: bad loop library id')\n const common: LoopCommon = {\n block,\n unexaminedCode,\n commentOnStart: comment,\n commentOnEnd: end.comment,\n maskOnStart: mask,\n maskOnEnd: end.mask,\n }\n switch (exp.methodId) {\n case 3:\n statements.push({ kind: 'while', condition: exp.paramList?.[0] ?? null, ...common })\n break\n case 5:\n statements.push({\n kind: 'doWhile',\n condition: end.exp.paramList?.[0] ?? null,\n ...common,\n unexaminedCode: end.unexaminedCode,\n })\n break\n case 7:\n statements.push({\n kind: 'counter',\n count: exp.paramList?.[0] ?? null,\n var: exp.paramList?.[1] ?? null,\n ...common,\n })\n break\n case 9:\n statements.push({\n kind: 'for',\n start: exp.paramList?.[0] ?? null,\n end: exp.paramList?.[1] ?? null,\n step: exp.paramList?.[2] ?? null,\n var: exp.paramList?.[3] ?? null,\n ...common,\n })\n break\n default:\n throw new Error(`CodeData: unknown loop methodId ${exp.methodId}`)\n }\n break\n }\n case 0x6c: {\n const block = parseBlock(r)\n if (r.readByte() !== 0x52) throw new Error('CodeData: expected 0x52')\n r.readByte() // 0x73\n statements.push({\n kind: 'if',\n condition: exp.paramList?.[0] ?? null,\n unexaminedCode,\n block,\n comment,\n mask,\n })\n break\n }\n case 0x6b: {\n const blockOnTrue = parseBlock(r)\n if (r.readByte() !== 0x50) throw new Error('CodeData: expected 0x50')\n const blockOnFalse = parseBlock(r)\n if (r.readByte() !== 0x51) throw new Error('CodeData: expected 0x51')\n r.readByte() // 0x72\n statements.push({\n kind: 'ifElse',\n condition: exp.paramList?.[0] ?? null,\n unexaminedCode,\n blockOnTrue,\n blockOnFalse,\n comment,\n mask,\n })\n break\n }\n case 0x6a: {\n if (unexaminedCode != null) {\n statements.push({ kind: 'unexamined', code: unexaminedCode, mask })\n } else if (exp.libraryId === -1) {\n statements.push({ kind: 'expression', expr: null, mask: false, comment })\n } else {\n statements.push({ kind: 'expression', expr: exp, mask, comment })\n }\n break\n }\n }\n }\n}\n\nfunction parseCall(r: BinaryReader): ParsedCall {\n const methodId = r.readInt32()\n const libraryId = r.readInt16()\n const flag = r.readInt16()\n let unexaminedCode = r.readBStr()\n let comment = r.readBStr()\n const mask = (flag & 0x20) !== 0\n const invokeSpecial = (flag & 0x10) !== 0\n if (unexaminedCode != null) {\n const z = unexaminedCode.indexOf('\\0')\n if (z !== -1) unexaminedCode = unexaminedCode.slice(0, z)\n if (unexaminedCode === '') unexaminedCode = null\n }\n if (comment != null) {\n const z = comment.indexOf('\\0')\n if (z !== -1) comment = comment.slice(0, z)\n if (comment === '') comment = null\n }\n const exp: CallExpr = {\n kind: 'call',\n libraryId,\n methodId,\n invokeSpecial,\n target: null,\n paramList: null,\n }\n if (r.pos !== r.length) {\n const b = r.readByte()\n if (b === 0x36) {\n exp.paramList = parseParamList(r)\n } else if (b === 0x38) {\n // ThisCall\n r.pos -= 1\n exp.target = parseExpression(r)\n exp.paramList = parseParamList(r)\n } else {\n r.pos -= 1\n throw new Error('CodeData: bad call terminator')\n }\n }\n return { exp, unexaminedCode, comment, mask }\n}\n\nfunction parseParamList(r: BinaryReader): Expr[] {\n const params: Expr[] = []\n for (;;) {\n const e = parseExpression(r)\n if (e === PARAM_LIST_END) break\n params.push(e)\n }\n return params\n}\n\nfunction parseExpression(r: BinaryReader, parseMember = true): Expr {\n let result!: Expr\n for (;;) {\n const type = r.readByte()\n if (type === 0x01) {\n result = PARAM_LIST_END\n break\n } else if (type === 0x16) {\n result = DEFAULT_VALUE\n break\n } else if (type === 0x17) {\n result = { kind: 'number', value: r.readFloat64() }\n break\n } else if (type === 0x18) {\n result = { kind: 'bool', value: r.readInt16() !== 0 }\n break\n } else if (type === 0x19) {\n result = { kind: 'datetime', oa: r.readFloat64() }\n break\n } else if (type === 0x1a) {\n result = { kind: 'string', value: r.readBStr() ?? '' }\n break\n } else if (type === 0x1b) {\n result = { kind: 'constant', libraryId: -2, constantId: r.readInt32() }\n break\n } else if (type === 0x1c) {\n result = { kind: 'constant', libraryId: r.readInt16() - 1, constantId: r.readInt16() - 1 }\n break\n } else if (type === 0x1d) {\n continue // 0x1D prefixes a 0x38 var access — skip and re-read\n } else if (type === 0x1e) {\n result = { kind: 'methodPtr', methodId: r.readInt32() }\n break\n } else if (type === 0x21) {\n result = parseCall(r).exp\n break\n } else if (type === 0x23) {\n const structId = r.readInt16() - 1\n const libraryId = r.readInt16() - 1\n const memberId = r.readInt32() - 1\n result = { kind: 'emnu', libraryId, structId, memberId }\n break\n } else if (type === 0x37) {\n continue\n } else if (type === 0x1f) {\n const items: Expr[] = []\n let e: Expr\n while ((e = parseExpression(r)) !== ARRAY_LITERAL_END) items.push(e)\n result = { kind: 'array', items }\n break\n } else if (type === 0x20) {\n result = ARRAY_LITERAL_END\n break\n } else if (type === 0x38) {\n const variable = r.readInt32()\n if (variable === ID_NAV) {\n r.readByte() // 0x3A\n return parseExpression(r, true)\n }\n result = { kind: 'variable', id: variable }\n parseMember = true\n break\n } else if (type === 0x3b) {\n result = { kind: 'number', value: r.readInt32() }\n break\n } else {\n throw new Error(`CodeData: unknown expression type 0x${type.toString(16)}`)\n }\n }\n\n if (\n parseMember &&\n (result.kind === 'variable' ||\n result.kind === 'call' ||\n result.kind === 'accessMember' ||\n result.kind === 'accessArray')\n ) {\n while (r.pos !== r.length) {\n const b = r.readByte()\n if (b === 0x39) {\n const memberId = r.readInt32()\n const structId = r.readInt32()\n if (isLibDataType(structId)) {\n const { lib, type } = decomposeLibDataTypeId(structId)\n result = {\n kind: 'accessMember',\n target: result,\n libraryId: lib,\n structId: type,\n memberId: memberId - 1,\n }\n } else {\n result = { kind: 'accessMember', target: result, libraryId: -2, structId, memberId }\n }\n } else if (b === 0x3a) {\n result = { kind: 'accessArray', target: result, index: parseExpression(r, false) }\n } else if (b === 0x37) {\n break\n } else {\n r.pos -= 1\n break\n }\n }\n }\n return result\n}\n"],"mappings":"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"}