@spacecorexdev/core 0.0.2 → 0.0.5

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 (345) hide show
  1. package/package.json +20 -38
  2. package/public/cmaps/78-EUC-H.bcmap +0 -0
  3. package/public/cmaps/78-EUC-V.bcmap +0 -0
  4. package/public/cmaps/78-H.bcmap +0 -0
  5. package/public/cmaps/78-RKSJ-H.bcmap +0 -0
  6. package/public/cmaps/78-RKSJ-V.bcmap +0 -0
  7. package/public/cmaps/78-V.bcmap +0 -0
  8. package/public/cmaps/78ms-RKSJ-H.bcmap +0 -0
  9. package/public/cmaps/78ms-RKSJ-V.bcmap +0 -0
  10. package/public/cmaps/83pv-RKSJ-H.bcmap +0 -0
  11. package/public/cmaps/90ms-RKSJ-H.bcmap +0 -0
  12. package/public/cmaps/90ms-RKSJ-V.bcmap +0 -0
  13. package/public/cmaps/90msp-RKSJ-H.bcmap +0 -0
  14. package/public/cmaps/90msp-RKSJ-V.bcmap +0 -0
  15. package/public/cmaps/90pv-RKSJ-H.bcmap +0 -0
  16. package/public/cmaps/90pv-RKSJ-V.bcmap +0 -0
  17. package/public/cmaps/Add-H.bcmap +0 -0
  18. package/public/cmaps/Add-RKSJ-H.bcmap +0 -0
  19. package/public/cmaps/Add-RKSJ-V.bcmap +0 -0
  20. package/public/cmaps/Add-V.bcmap +0 -0
  21. package/public/cmaps/Adobe-CNS1-0.bcmap +0 -0
  22. package/public/cmaps/Adobe-CNS1-1.bcmap +0 -0
  23. package/public/cmaps/Adobe-CNS1-2.bcmap +0 -0
  24. package/public/cmaps/Adobe-CNS1-3.bcmap +0 -0
  25. package/public/cmaps/Adobe-CNS1-4.bcmap +0 -0
  26. package/public/cmaps/Adobe-CNS1-5.bcmap +0 -0
  27. package/public/cmaps/Adobe-CNS1-6.bcmap +0 -0
  28. package/public/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  29. package/public/cmaps/Adobe-GB1-0.bcmap +0 -0
  30. package/public/cmaps/Adobe-GB1-1.bcmap +0 -0
  31. package/public/cmaps/Adobe-GB1-2.bcmap +0 -0
  32. package/public/cmaps/Adobe-GB1-3.bcmap +0 -0
  33. package/public/cmaps/Adobe-GB1-4.bcmap +0 -0
  34. package/public/cmaps/Adobe-GB1-5.bcmap +0 -0
  35. package/public/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  36. package/public/cmaps/Adobe-Japan1-0.bcmap +0 -0
  37. package/public/cmaps/Adobe-Japan1-1.bcmap +0 -0
  38. package/public/cmaps/Adobe-Japan1-2.bcmap +0 -0
  39. package/public/cmaps/Adobe-Japan1-3.bcmap +0 -0
  40. package/public/cmaps/Adobe-Japan1-4.bcmap +0 -0
  41. package/public/cmaps/Adobe-Japan1-5.bcmap +0 -0
  42. package/public/cmaps/Adobe-Japan1-6.bcmap +0 -0
  43. package/public/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  44. package/public/cmaps/Adobe-Korea1-0.bcmap +0 -0
  45. package/public/cmaps/Adobe-Korea1-1.bcmap +0 -0
  46. package/public/cmaps/Adobe-Korea1-2.bcmap +0 -0
  47. package/public/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  48. package/public/cmaps/B5-H.bcmap +0 -0
  49. package/public/cmaps/B5-V.bcmap +0 -0
  50. package/public/cmaps/B5pc-H.bcmap +0 -0
  51. package/public/cmaps/B5pc-V.bcmap +0 -0
  52. package/public/cmaps/CNS-EUC-H.bcmap +0 -0
  53. package/public/cmaps/CNS-EUC-V.bcmap +0 -0
  54. package/public/cmaps/CNS1-H.bcmap +0 -0
  55. package/public/cmaps/CNS1-V.bcmap +0 -0
  56. package/public/cmaps/CNS2-H.bcmap +0 -0
  57. package/public/cmaps/CNS2-V.bcmap +3 -0
  58. package/public/cmaps/ETHK-B5-H.bcmap +0 -0
  59. package/public/cmaps/ETHK-B5-V.bcmap +0 -0
  60. package/public/cmaps/ETen-B5-H.bcmap +0 -0
  61. package/public/cmaps/ETen-B5-V.bcmap +0 -0
  62. package/public/cmaps/ETenms-B5-H.bcmap +3 -0
  63. package/public/cmaps/ETenms-B5-V.bcmap +0 -0
  64. package/public/cmaps/EUC-H.bcmap +0 -0
  65. package/public/cmaps/EUC-V.bcmap +0 -0
  66. package/public/cmaps/Ext-H.bcmap +0 -0
  67. package/public/cmaps/Ext-RKSJ-H.bcmap +0 -0
  68. package/public/cmaps/Ext-RKSJ-V.bcmap +0 -0
  69. package/public/cmaps/Ext-V.bcmap +0 -0
  70. package/public/cmaps/GB-EUC-H.bcmap +0 -0
  71. package/public/cmaps/GB-EUC-V.bcmap +0 -0
  72. package/public/cmaps/GB-H.bcmap +4 -0
  73. package/public/cmaps/GB-V.bcmap +0 -0
  74. package/public/cmaps/GBK-EUC-H.bcmap +0 -0
  75. package/public/cmaps/GBK-EUC-V.bcmap +0 -0
  76. package/public/cmaps/GBK2K-H.bcmap +0 -0
  77. package/public/cmaps/GBK2K-V.bcmap +0 -0
  78. package/public/cmaps/GBKp-EUC-H.bcmap +0 -0
  79. package/public/cmaps/GBKp-EUC-V.bcmap +0 -0
  80. package/public/cmaps/GBT-EUC-H.bcmap +0 -0
  81. package/public/cmaps/GBT-EUC-V.bcmap +0 -0
  82. package/public/cmaps/GBT-H.bcmap +0 -0
  83. package/public/cmaps/GBT-V.bcmap +0 -0
  84. package/public/cmaps/GBTpc-EUC-H.bcmap +0 -0
  85. package/public/cmaps/GBTpc-EUC-V.bcmap +0 -0
  86. package/public/cmaps/GBpc-EUC-H.bcmap +0 -0
  87. package/public/cmaps/GBpc-EUC-V.bcmap +0 -0
  88. package/public/cmaps/H.bcmap +0 -0
  89. package/public/cmaps/HKdla-B5-H.bcmap +0 -0
  90. package/public/cmaps/HKdla-B5-V.bcmap +0 -0
  91. package/public/cmaps/HKdlb-B5-H.bcmap +0 -0
  92. package/public/cmaps/HKdlb-B5-V.bcmap +0 -0
  93. package/public/cmaps/HKgccs-B5-H.bcmap +0 -0
  94. package/public/cmaps/HKgccs-B5-V.bcmap +0 -0
  95. package/public/cmaps/HKm314-B5-H.bcmap +0 -0
  96. package/public/cmaps/HKm314-B5-V.bcmap +0 -0
  97. package/public/cmaps/HKm471-B5-H.bcmap +0 -0
  98. package/public/cmaps/HKm471-B5-V.bcmap +0 -0
  99. package/public/cmaps/HKscs-B5-H.bcmap +0 -0
  100. package/public/cmaps/HKscs-B5-V.bcmap +0 -0
  101. package/public/cmaps/Hankaku.bcmap +0 -0
  102. package/public/cmaps/Hiragana.bcmap +0 -0
  103. package/public/cmaps/KSC-EUC-H.bcmap +0 -0
  104. package/public/cmaps/KSC-EUC-V.bcmap +0 -0
  105. package/public/cmaps/KSC-H.bcmap +0 -0
  106. package/public/cmaps/KSC-Johab-H.bcmap +0 -0
  107. package/public/cmaps/KSC-Johab-V.bcmap +0 -0
  108. package/public/cmaps/KSC-V.bcmap +0 -0
  109. package/public/cmaps/KSCms-UHC-H.bcmap +0 -0
  110. package/public/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  111. package/public/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  112. package/public/cmaps/KSCms-UHC-V.bcmap +0 -0
  113. package/public/cmaps/KSCpc-EUC-H.bcmap +0 -0
  114. package/public/cmaps/KSCpc-EUC-V.bcmap +0 -0
  115. package/public/cmaps/Katakana.bcmap +0 -0
  116. package/public/cmaps/LICENSE +36 -0
  117. package/public/cmaps/NWP-H.bcmap +0 -0
  118. package/public/cmaps/NWP-V.bcmap +0 -0
  119. package/public/cmaps/RKSJ-H.bcmap +0 -0
  120. package/public/cmaps/RKSJ-V.bcmap +0 -0
  121. package/public/cmaps/Roman.bcmap +0 -0
  122. package/public/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  123. package/public/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  124. package/public/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  125. package/public/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  126. package/public/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  127. package/public/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  128. package/public/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  129. package/public/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  130. package/public/cmaps/UniGB-UCS2-H.bcmap +0 -0
  131. package/public/cmaps/UniGB-UCS2-V.bcmap +0 -0
  132. package/public/cmaps/UniGB-UTF16-H.bcmap +0 -0
  133. package/public/cmaps/UniGB-UTF16-V.bcmap +0 -0
  134. package/public/cmaps/UniGB-UTF32-H.bcmap +0 -0
  135. package/public/cmaps/UniGB-UTF32-V.bcmap +0 -0
  136. package/public/cmaps/UniGB-UTF8-H.bcmap +0 -0
  137. package/public/cmaps/UniGB-UTF8-V.bcmap +0 -0
  138. package/public/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  139. package/public/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  140. package/public/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  141. package/public/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  142. package/public/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  143. package/public/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  144. package/public/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  145. package/public/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  146. package/public/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  147. package/public/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  148. package/public/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  149. package/public/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  150. package/public/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  151. package/public/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  152. package/public/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  153. package/public/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  154. package/public/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  155. package/public/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  156. package/public/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  157. package/public/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  158. package/public/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  159. package/public/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  160. package/public/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  161. package/public/cmaps/UniKS-UCS2-H.bcmap +0 -0
  162. package/public/cmaps/UniKS-UCS2-V.bcmap +0 -0
  163. package/public/cmaps/UniKS-UTF16-H.bcmap +0 -0
  164. package/public/cmaps/UniKS-UTF16-V.bcmap +0 -0
  165. package/public/cmaps/UniKS-UTF32-H.bcmap +0 -0
  166. package/public/cmaps/UniKS-UTF32-V.bcmap +0 -0
  167. package/public/cmaps/UniKS-UTF8-H.bcmap +0 -0
  168. package/public/cmaps/UniKS-UTF8-V.bcmap +0 -0
  169. package/public/cmaps/V.bcmap +0 -0
  170. package/public/cmaps/WP-Symbol.bcmap +0 -0
  171. package/public/polyfills/polyfills.js +802 -0
  172. package/public/vendor/docx/docx.worker.js +5 -0
  173. package/public/vendor/docx/jszip.min.js +13 -0
  174. package/public/vendor/libarchive/libarchive.wasm +0 -0
  175. package/public/vendor/libarchive/worker-bundle.js +6 -0
  176. package/public/vendor/pptx/pptx.worker.js +26 -0
  177. package/public/wasm/cad/0.8.2/dwfv-render.wasm +0 -0
  178. package/public/wasm/cad/0.8.2/dwg-worker.js +24 -0
  179. package/public/wasm/cad/0.8.2/dwg-worker.js.map +1 -0
  180. package/public/wasm/cad/0.8.2/libredwg-web.js +2 -0
  181. package/public/wasm/cad/0.8.2/libredwg-web.wasm +0 -0
  182. package/public/wasm/cad/dwfv-render.wasm +0 -0
  183. package/public/wasm/cad/dwg-worker.js +24 -0
  184. package/public/wasm/cad/dwg-worker.js.map +1 -0
  185. package/public/wasm/cad/libredwg-web.js +2 -0
  186. package/public/wasm/cad/libredwg-web.wasm +0 -0
  187. package/public/wasm/data/sql-wasm.wasm +0 -0
  188. package/public/wasm/hdiff.wasm +0 -0
  189. package/public/wasm/model/LICENSE.occt-import-js.txt +504 -0
  190. package/public/wasm/model/LICENSE.occt.txt +502 -0
  191. package/public/wasm/model/occt-import-js.js +19 -0
  192. package/public/wasm/model/occt-import-js.wasm +0 -0
  193. package/public/wasm/model/occt-worker.js +83 -0
  194. package/public/wasm/typst/fonts/DejaVuSansMono-Bold.ttf +0 -0
  195. package/public/wasm/typst/fonts/DejaVuSansMono-BoldOblique.ttf +0 -0
  196. package/public/wasm/typst/fonts/DejaVuSansMono-Oblique.ttf +0 -0
  197. package/public/wasm/typst/fonts/DejaVuSansMono.ttf +0 -0
  198. package/public/wasm/typst/fonts/LibertinusSerif-Bold.otf +0 -0
  199. package/public/wasm/typst/fonts/LibertinusSerif-BoldItalic.otf +0 -0
  200. package/public/wasm/typst/fonts/LibertinusSerif-Italic.otf +0 -0
  201. package/public/wasm/typst/fonts/LibertinusSerif-Regular.otf +0 -0
  202. package/public/wasm/typst/fonts/LibertinusSerif-Semibold.otf +0 -0
  203. package/public/wasm/typst/fonts/LibertinusSerif-SemiboldItalic.otf +0 -0
  204. package/public/wasm/typst/fonts/NewCM10-Bold.otf +0 -0
  205. package/public/wasm/typst/fonts/NewCM10-BoldItalic.otf +0 -0
  206. package/public/wasm/typst/fonts/NewCM10-Italic.otf +0 -0
  207. package/public/wasm/typst/fonts/NewCM10-Regular.otf +0 -0
  208. package/public/wasm/typst/fonts/NewCMMath-Bold.otf +0 -0
  209. package/public/wasm/typst/fonts/NewCMMath-Book.otf +0 -0
  210. package/public/wasm/typst/fonts/NewCMMath-Regular.otf +0 -0
  211. package/public/wasm/typst/fonts/README.md +7 -0
  212. package/public/wasm/typst/typst_ts_renderer_bg.wasm +0 -0
  213. package/public/wasm/typst/typst_ts_web_compiler_bg.wasm +0 -0
  214. package/scripts/patch-docx-grid.cjs +412 -0
  215. package/src/api/_base.ts +243 -0
  216. package/src/api/index.ts +17 -0
  217. package/src/api/project.ts +1345 -0
  218. package/src/api/user.ts +1174 -0
  219. package/src/contract.ts +50 -0
  220. package/src/hooks/index.ts +2 -0
  221. package/{dist/hooks/index.js → src/hooks/useLongPress.ts} +53 -49
  222. package/src/hooks/useOnlineStatus.ts +22 -0
  223. package/src/index.ts +14 -0
  224. package/src/types/id.ts +2 -0
  225. package/src/types/index.ts +5 -0
  226. package/src/types/project/base.ts +63 -0
  227. package/src/types/project/contract.ts +79 -0
  228. package/src/types/project/contractStatus.tsx +15 -0
  229. package/src/types/project/index.ts +5 -0
  230. package/src/types/project/member.ts +46 -0
  231. package/src/types/project/progress.ts +43 -0
  232. package/src/types/projectRole.ts +17 -0
  233. package/src/types/theme.ts +1 -0
  234. package/src/types/user/index.ts +1 -0
  235. package/src/types/user/role.ts +7 -0
  236. package/src/ui/CommentTimeline/index.tsx +330 -0
  237. package/src/ui/FileManager/FileContextMenu.tsx +628 -0
  238. package/src/ui/FileManager/FileGridView.tsx +529 -0
  239. package/src/ui/FileManager/FileTableView.tsx +405 -0
  240. package/src/ui/FileManager/FileToolbar.tsx +119 -0
  241. package/src/ui/FileManager/FileVersionHistoryModal.tsx +175 -0
  242. package/src/ui/FileManager/MoveCopyToProjectModal.tsx +119 -0
  243. package/src/ui/FileManager/ShareToWechatModal.tsx +419 -0
  244. package/src/ui/FileManager/hooks/useFileManagerActions.ts +2532 -0
  245. package/src/ui/FileManager/hooks/useFileManagerState.ts +419 -0
  246. package/src/ui/FileManager/index.tsx +580 -0
  247. package/src/ui/FileManager/projectFileManagerApi.ts +57 -0
  248. package/src/ui/FileManager/types.ts +184 -0
  249. package/src/ui/FileManager/utils/fileDeltaWorker.ts +193 -0
  250. package/src/ui/FileManager/utils/fileTree.ts +178 -0
  251. package/src/ui/FileManager/utils/fileUpload.ts +714 -0
  252. package/src/ui/FileManager/utils/fileUtils.ts +65 -0
  253. package/src/ui/InvoiceRecordsList/index.tsx +813 -0
  254. package/src/ui/MyFileSelectorModal.tsx +261 -0
  255. package/src/ui/ProjectSelector/index.tsx +472 -0
  256. package/src/ui/ProjectSelector/projectCache.ts +40 -0
  257. package/src/ui/RoleSelector/index.tsx +473 -0
  258. package/src/ui/RoleSelector/roleCache.ts +40 -0
  259. package/src/ui/UIAlert.tsx +399 -0
  260. package/src/ui/UIAlertExample.tsx +129 -0
  261. package/src/ui/UIConfirm.tsx +335 -0
  262. package/src/ui/UIConfirmExample.tsx +165 -0
  263. package/src/ui/UIDatePicker.tsx +146 -0
  264. package/src/ui/UIDateRangePicker.tsx +38 -0
  265. package/src/ui/UIFilePreview/ExcelEditor/index.tsx +617 -0
  266. package/src/ui/UIFilePreview/ExcelEditor/types.ts +24 -0
  267. package/src/ui/UIFilePreview/ExportDropdown.css +85 -0
  268. package/src/ui/UIFilePreview/ExportDropdown.tsx +109 -0
  269. package/src/ui/UIFilePreview/IndexedDBUtils.ts +564 -0
  270. package/src/ui/UIFilePreview/MarkdownEditor/exportMarkdownPdf.ts +576 -0
  271. package/src/ui/UIFilePreview/MarkdownEditor/index.tsx +677 -0
  272. package/src/ui/UIFilePreview/MarkdownEditor/markdownEditor.css +1005 -0
  273. package/src/ui/UIFilePreview/MarkdownEditor/types.ts +15 -0
  274. package/src/ui/UIFilePreview/PPTXEditor/index.tsx +602 -0
  275. package/src/ui/UIFilePreview/RichDocEditor/exportRDocMarkdown.ts +77 -0
  276. package/src/ui/UIFilePreview/RichDocEditor/exportRDocPdf.ts +374 -0
  277. package/src/ui/UIFilePreview/RichDocEditor/index.tsx +730 -0
  278. package/src/ui/UIFilePreview/RichDocEditor/richdoc-editor.css +59 -0
  279. package/src/ui/UIFilePreview/UIFilePreview.tsx +611 -0
  280. package/src/ui/UIFilePreview/WordEditor/index.tsx +3933 -0
  281. package/src/ui/UIFilePreview/WordEditor/style.ts +1213 -0
  282. package/src/ui/UIFilePreview/WordEditor/word-editor.css +999 -0
  283. package/src/ui/UIFilePreview/filePreview.css +193 -0
  284. package/src/ui/UIFilePreview/filetypes/archive.tsx +66 -0
  285. package/src/ui/UIFilePreview/filetypes/cad.tsx +287 -0
  286. package/src/ui/UIFilePreview/filetypes/excel.tsx +355 -0
  287. package/src/ui/UIFilePreview/filetypes/image.tsx +20 -0
  288. package/src/ui/UIFilePreview/filetypes/imageLoader.ts +63 -0
  289. package/src/ui/UIFilePreview/filetypes/pdf.tsx +156 -0
  290. package/src/ui/UIFilePreview/filetypes/ppt.tsx +535 -0
  291. package/src/ui/UIFilePreview/filetypes/pptExportPdf.ts +523 -0
  292. package/src/ui/UIFilePreview/filetypes/video.tsx +482 -0
  293. package/src/ui/UIFilePreview/filetypes/word.tsx +245 -0
  294. package/src/ui/UIFilePreview/filetypes/wordExportMarkdown.ts +86 -0
  295. package/src/ui/UIFilePreview/filetypes/wordExportPdf.tsx +427 -0
  296. package/src/ui/UIFilePreview/index.ts +35 -0
  297. package/src/ui/UIFilePreview/pdfPagination.ts +143 -0
  298. package/src/ui/UIFilePreview/previewUtils.tsx +531 -0
  299. package/src/ui/UIFileUpload.tsx +382 -0
  300. package/src/ui/UIImageUpload.tsx +195 -0
  301. package/src/ui/UILoading.tsx +255 -0
  302. package/src/ui/UILoadingExample.tsx +139 -0
  303. package/src/ui/UISelect.tsx +105 -0
  304. package/src/ui/UIToast.tsx +381 -0
  305. package/src/ui/UIToastExample.tsx +69 -0
  306. package/src/ui/UIToastSingle.tsx +279 -0
  307. package/src/ui/UIViewToggle.tsx +64 -0
  308. package/src/ui/UserSelector/index.tsx +583 -0
  309. package/src/ui/index.ts +84 -0
  310. package/src/ui/upload/image.ts +135 -0
  311. package/src/ui/upload/index.ts +73 -0
  312. package/src/ui/upload/submitFile.ts +106 -0
  313. package/src/ui/upload/submitForm.ts +124 -0
  314. package/src/ui/upload/upload.err.ts +15 -0
  315. package/src/ui/upload/upload.ts +250 -0
  316. package/src/utils/CacheDepartmentInfo.ts +372 -0
  317. package/src/utils/CacheFileContent.ts +315 -0
  318. package/src/utils/CachePermissionInfo.ts +235 -0
  319. package/src/utils/CacheRoleInfo.ts +303 -0
  320. package/src/utils/CacheUserInfo.ts +818 -0
  321. package/src/utils/CacheViewToggleInfo.ts +182 -0
  322. package/src/utils/MessageContentUtils.ts +268 -0
  323. package/src/utils/clipboard.ts +70 -0
  324. package/src/utils/companyEmail.ts +19 -0
  325. package/src/utils/forbidDebugger.ts +59 -0
  326. package/src/utils/formatters.ts +94 -0
  327. package/src/utils/htmlUtils.tsx +59 -0
  328. package/src/utils/index.ts +18 -0
  329. package/src/utils/keyboard.ts +114 -0
  330. package/src/utils/mammothApi.ts +16 -0
  331. package/src/utils/polyfills.ts +928 -0
  332. package/src/utils/regex.ts +14 -0
  333. package/src/utils/storage.ts +95 -0
  334. package/src/utils/workerBootstrap.ts +87 -0
  335. package/dist/api/index.js +0 -1553
  336. package/dist/contract.js +0 -5
  337. package/dist/index.js +0 -4492
  338. package/dist/types/index.js +0 -49
  339. package/dist/ui/index.css +0 -1876
  340. package/dist/ui/index.js +0 -22245
  341. package/dist/utils/index.js +0 -4296
  342. /package/{dist → src}/styles/ant-modal.css +0 -0
  343. /package/{dist → src}/styles/ant-spin.css +0 -0
  344. /package/{dist → src}/styles/ant-table.css +0 -0
  345. /package/{dist → src}/styles/index.css +0 -0
@@ -0,0 +1,523 @@
1
+ import { PptxViewer } from '@file-viewer/pptx';
2
+ import { withPolyfillWorkerBootstrap } from '../../../utils/workerBootstrap';
3
+
4
+ /**
5
+ * PPTX/PPT 导出 PDF:离屏挂载 @file-viewer/pptx 原生渲染器(fitMode:none,
6
+ * content 不缩放)→ 逐张幻灯片用 SVG foreignObject 光栅化 → jsPDF 直接下载
7
+ * (不经过浏览器打印对话框)。与 wordExportPdf 同一套思路:排版交给浏览器自身,
8
+ * 保证与屏幕预览像素级一致。
9
+ *
10
+ * 与 Word 版的差异(PPT 渲染器的真实 DOM 结构决定):
11
+ * 1. 每张幻灯片是 .flyfish-pptx-content 下一个固定 px 尺寸的 div.slide
12
+ * (inline width/height,如 1280×720),一 slide 对应 PDF 一页;
13
+ * 2. 引擎把生成样式(._css_* / ._svg_css_* 等,均已被 DOMPurify 清洗、
14
+ * 无外链无 @font-face)以一个 <style> 注入 content;渲染器自身样式在
15
+ * document.head(#flyfish-pptx-native-style)。这两个样式表体积极小且
16
+ * 选择器全部带 .flyfish-pptx-content 作用域,直接随 SVG 带走即可
17
+ * (克隆节点保留 class),无需逐元素全量内联计算样式——也避免了对内联
18
+ * SVG 图形做全量内联时几何属性被覆盖的风险;
19
+ * 3. 幻灯片图片在 worker 内已转成 data: URL;仅媒体(视频等)可能被渲染器
20
+ * hydrate 成 blob: URL,序列化前统一把残留 blob:/http 引用(img.src、
21
+ * poster、SVG <image> href、CSS url())内联为 data URL;
22
+ * 4. SVG 作为 <img> 加载时安全模式禁止任何外链,任何残留外链都会让整页
23
+ * onerror——内联失败的引用直接移除(宁可缺单张图,不让整页失败)。
24
+ */
25
+
26
+ const PPTX_MIME_TYPE =
27
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
28
+ /**
29
+ * 光栅化倍率:幻灯片原生多为 1280×720(96dpi),2× 即 2560×1440,
30
+ * 铺到 13.33in×7.5in 的 PDF 页面约 192DPI,文字清晰;
31
+ * 3×(3840×2160)对图片型大 deck 内存/体积代价过高,故取 2。
32
+ */
33
+ const SCALE = 2;
34
+ /** 渲染最长等待时间(ms),14MB 级别大 deck 在 worker 里解析较慢 */
35
+ const RENDER_TIMEOUT = 120000;
36
+ /** 页数稳定多久视为渲染完成(ms) */
37
+ const STABLE_MS = 500;
38
+
39
+ const PPTX_VIEWER_STYLE_ID = 'flyfish-pptx-native-style';
40
+ /** 每次扫描新建实例,避免共享 lastIndex 状态 */
41
+ const newCssUrlRe = () => /url\(\s*(['"]?)([^'")]+)\1\s*\)/gi;
42
+
43
+ export interface ExportPptxPdfOptions {
44
+ /** pptx/ppt 文件(编辑器侧应传入含最新修改的导出产物) */
45
+ file: Blob;
46
+ /** 文件名(.pptx/.ppt 后缀会被替换为 .pdf) */
47
+ fileName: string;
48
+ /** 逐页进度回调(当前页 / 总页数) */
49
+ onProgress?: (page: number, total: number) => void;
50
+ }
51
+
52
+ export interface PptxSlideImage {
53
+ /** PNG data URL(SCALE 倍分辨率) */
54
+ dataUrl: string;
55
+ /** 幻灯片原始 CSS 像素尺寸 */
56
+ widthPx: number;
57
+ heightPx: number;
58
+ /** PDF 页面尺寸(pt,px × 0.75) */
59
+ widthPt: number;
60
+ heightPt: number;
61
+ }
62
+
63
+ interface MountedViewer {
64
+ host: HTMLElement;
65
+ viewer: PptxViewer;
66
+ unmount: () => void;
67
+ /** worker 致命错误(onError 回调) */
68
+ getFatal: () => unknown;
69
+ }
70
+
71
+ /** 离屏挂载 PPTX 渲染器(与在线预览同配置:classic worker、非窗口化全部渲染) */
72
+ const mountExportViewer = async (file: Blob): Promise<MountedViewer> => {
73
+ const host = document.createElement('div');
74
+ host.setAttribute('data-ppt-export-root', 'true');
75
+ Object.assign(host.style, {
76
+ position: 'fixed',
77
+ top: '0',
78
+ left: '-100000px',
79
+ // 标准幻灯片最大宽 1280px(16:9 / 96dpi),留余量避免任何裁切
80
+ width: '1600px',
81
+ background: '#ffffff',
82
+ pointerEvents: 'none',
83
+ } as Partial<CSSStyleDeclaration>);
84
+ document.body.appendChild(host);
85
+
86
+ const buffer = await file.arrayBuffer();
87
+
88
+ let fatal: unknown = null;
89
+ const viewer = await PptxViewer.open(buffer, host, {
90
+ fitMode: 'none',
91
+ workerType: 'classic',
92
+ workerUrl: withPolyfillWorkerBootstrap('/vendor/pptx/pptx.worker.js'),
93
+ lazySlides: false,
94
+ listOptions: undefined,
95
+ onError: (error) => {
96
+ fatal = error;
97
+ },
98
+ });
99
+
100
+ return {
101
+ host,
102
+ viewer,
103
+ getFatal: () => fatal,
104
+ unmount: () => {
105
+ try {
106
+ viewer.destroy();
107
+ } finally {
108
+ host.remove();
109
+ }
110
+ },
111
+ };
112
+ };
113
+
114
+ /**
115
+ * 等待所有幻灯片渲染完成:
116
+ * slide 数与 worker 总数一致、每页已有尺寸,且稳定 STABLE_MS,
117
+ * 然后等全部 <img> 解码完成(带超时兜底),再留一个短延时给图表后处理提交。
118
+ * 不依赖 document.fonts.ready / requestAnimationFrame:后台标签或无头环境下
119
+ * 二者都可能无限挂起;PPT 渲染只用系统字体,布局在轮询期间已完成。
120
+ */
121
+ const waitForSlidesReady = async (
122
+ viewer: PptxViewer,
123
+ host: HTMLElement,
124
+ getFatal: () => unknown,
125
+ ): Promise<HTMLElement[]> =>
126
+ new Promise((resolve, reject) => {
127
+ const start = Date.now();
128
+ let lastCount = -1;
129
+ let stableSince = 0;
130
+ let settled = false;
131
+
132
+ const finish = async (slides: HTMLElement[]) => {
133
+ if (settled) return;
134
+ settled = true;
135
+ window.clearInterval(timer);
136
+ try {
137
+ // 等图片解码(data: URL 也要解码完成,SVG <img> 内才能瞬时可用)
138
+ const imgs = Array.from(
139
+ host.querySelectorAll<HTMLImageElement>('.flyfish-pptx-content img'),
140
+ );
141
+ // 个别媒体(失效 blob 等)可能永远不 complete,解码等待必须有超时兜底
142
+ const decodeWithTimeout = (img: HTMLImageElement) =>
143
+ new Promise<void>((resolveDecode) => {
144
+ const done = () => {
145
+ window.clearTimeout(timerDecode);
146
+ resolveDecode();
147
+ };
148
+ const timerDecode = window.setTimeout(done, 5000);
149
+ img.decode().then(done).catch(done);
150
+ });
151
+ await Promise.all(
152
+ imgs.map((img) => (img.complete ? Promise.resolve() : decodeWithTimeout(img))),
153
+ );
154
+ // 图片此时均已 complete;后台标签/无头环境会挂起 rAF,长时间 setTimeout
155
+ // 链也可能被冻结,这里只留一个短延时给图表后处理提交。
156
+ await new Promise<void>((r) => window.setTimeout(r, 120));
157
+ resolve(slides);
158
+ } catch (error) {
159
+ reject(error);
160
+ }
161
+ };
162
+
163
+ const timer = window.setInterval(() => {
164
+ if (settled) return;
165
+ const f = getFatal();
166
+ if (f) {
167
+ settled = true;
168
+ window.clearInterval(timer);
169
+ reject(f instanceof Error ? f : new Error(String(f)));
170
+ return;
171
+ }
172
+ const content = host.querySelector<HTMLElement>('.flyfish-pptx-content');
173
+ if (!content) {
174
+ if (Date.now() - start > RENDER_TIMEOUT) {
175
+ settled = true;
176
+ window.clearInterval(timer);
177
+ reject(new Error('PPT 渲染超时'));
178
+ }
179
+ return;
180
+ }
181
+ // 非窗口化模式:slide 是 content 的直接子元素
182
+ const slides = Array.from(
183
+ content.querySelectorAll<HTMLElement>(':scope > .slide'),
184
+ );
185
+ const total = viewer.slideCount;
186
+ const ready =
187
+ total > 0 &&
188
+ slides.length === total &&
189
+ content.dataset.renderState === 'ready' &&
190
+ slides.every((s) => {
191
+ const rect = s.getBoundingClientRect();
192
+ return rect.width > 100 && rect.height > 100;
193
+ });
194
+
195
+ if (ready) {
196
+ if (lastCount !== slides.length) stableSince = 0;
197
+ if (!stableSince) stableSince = Date.now();
198
+ if (Date.now() - stableSince >= STABLE_MS) {
199
+ void finish(slides);
200
+ }
201
+ } else {
202
+ stableSince = 0;
203
+ }
204
+ lastCount = slides.length;
205
+
206
+ if (Date.now() - start > RENDER_TIMEOUT) {
207
+ settled = true;
208
+ window.clearInterval(timer);
209
+ reject(new Error('PPT 渲染超时'));
210
+ }
211
+ }, 120);
212
+ });
213
+
214
+ /** 收集幻灯片渲染所需的全部样式文本(head 渲染器样式 + content 内引擎样式) */
215
+ const collectPptxCss = (content: HTMLElement): string => {
216
+ const parts: string[] = [];
217
+ const headStyle = document.getElementById(PPTX_VIEWER_STYLE_ID);
218
+ if (headStyle?.textContent) parts.push(headStyle.textContent);
219
+ content
220
+ .querySelectorAll('style')
221
+ .forEach((s) => s.textContent && parts.push(s.textContent));
222
+ return parts.join('\n');
223
+ };
224
+
225
+ /** 把一批 url 文本(data: 与锚点 # 除外)转成 data URL,返回替换映射 */
226
+ const fetchUrlsAsDataUrl = async (urls: string[]): Promise<Map<string, string>> => {
227
+ const map = new Map<string, string>();
228
+ await Promise.all(
229
+ Array.from(new Set(urls)).map(async (raw) => {
230
+ const u = raw.trim();
231
+ if (!u || u.startsWith('data:') || u.startsWith('#')) return;
232
+ try {
233
+ const blob = await fetch(u).then((r) => {
234
+ if (!r.ok) throw new Error(`HTTP ${r.status}`);
235
+ return r.blob();
236
+ });
237
+ const dataUrl = await new Promise<string>((resolve, reject) => {
238
+ const fr = new FileReader();
239
+ fr.onload = () => resolve(fr.result as string);
240
+ fr.onerror = reject;
241
+ fr.readAsDataURL(blob);
242
+ });
243
+ map.set(u, dataUrl);
244
+ } catch {
245
+ // 取不到(跨域/失效 blob)标记为 null:调用方负责移除该引用
246
+ map.set(u, '');
247
+ }
248
+ }),
249
+ );
250
+ return map;
251
+ };
252
+
253
+ /** 元素属性里的外链(img.src / poster / SVG image href 等)统一内联;失败则移除 */
254
+ const inlineCloneAttrRefs = async (root: HTMLElement): Promise<void> => {
255
+ const candidates: Array<{ el: Element; attr: Attr }> = [];
256
+ root
257
+ .querySelectorAll('[src],[poster],[href],[xlink\\:href]')
258
+ .forEach((el) => {
259
+ for (const attr of Array.from(el.attributes)) {
260
+ if (!['src', 'poster', 'href', 'xlink:href'].includes(attr.name)) continue;
261
+ const v = attr.value.trim();
262
+ if (!v || v.startsWith('data:') || v.startsWith('#')) continue;
263
+ candidates.push({ el, attr });
264
+ }
265
+ });
266
+ if (candidates.length === 0) return;
267
+ const urlMap = await fetchUrlsAsDataUrl(candidates.map((c) => c.attr.value.trim()));
268
+ candidates.forEach(({ el, attr }) => {
269
+ const key = attr.value.trim();
270
+ const next = urlMap.get(key);
271
+ if (next === undefined) return; // 未处理(理论不会)
272
+ if (next === '') {
273
+ // 内联失败:移除引用,避免 SVG-as-img 因外链整体 onerror
274
+ el.removeAttributeNode(attr);
275
+ return;
276
+ }
277
+ if (attr.namespaceURI) {
278
+ el.setAttributeNS(attr.namespaceURI, attr.name, next);
279
+ } else {
280
+ el.setAttribute(attr.name, next);
281
+ }
282
+ });
283
+ };
284
+
285
+ /** 克隆树内联样式里的 CSS url() 外链(背景图等);锚点 url(#id) 是 SVG 渐变引用,必须保留 */
286
+ const inlineCloneStyleUrls = async (root: HTMLElement): Promise<void> => {
287
+ const styled: HTMLElement[] = [
288
+ root,
289
+ ...Array.from(root.querySelectorAll<HTMLElement>('[style]')),
290
+ ];
291
+ const urls: string[] = [];
292
+ styled.forEach((el) => {
293
+ const text = el.getAttribute('style') || '';
294
+ const re = newCssUrlRe();
295
+ let m: RegExpExecArray | null;
296
+ while ((m = re.exec(text))) {
297
+ const u = m[2].trim();
298
+ if (u && !u.startsWith('data:') && !u.startsWith('#')) urls.push(u);
299
+ }
300
+ });
301
+ if (urls.length === 0) return;
302
+ const urlMap = await fetchUrlsAsDataUrl(urls);
303
+ const replace = (text: string) =>
304
+ text.replace(newCssUrlRe(), (full, _q: string, u: string) => {
305
+ const key = u.trim();
306
+ if (key.startsWith('#') || key.startsWith('data:')) return full;
307
+ const next = urlMap.get(key);
308
+ if (!next) return 'none'; // 取不到的背景图直接去掉
309
+ return `url("${next}")`;
310
+ });
311
+ styled.forEach((el) => {
312
+ const text = el.getAttribute('style');
313
+ if (text && newCssUrlRe().test(text)) el.setAttribute('style', replace(text));
314
+ });
315
+ };
316
+
317
+ /**
318
+ * cloneNode 不复制 <canvas> 位图。把原树中每个 canvas 的当前画面转成 dataURL,
319
+ * 按顺序写到克隆树的对应 canvas 上(转成 <img>,几何/样式沿用原 canvas)。
320
+ */
321
+ const flattenCloneCanvases = (src: HTMLElement, dst: HTMLElement): void => {
322
+ const srcCanvases = Array.from(src.querySelectorAll('canvas'));
323
+ const dstCanvases = Array.from(dst.querySelectorAll('canvas'));
324
+ srcCanvases.forEach((srcCanvas, i) => {
325
+ const dstCanvas = dstCanvases[i] as HTMLCanvasElement | undefined;
326
+ if (!dstCanvas) return;
327
+ let dataUrl = '';
328
+ try {
329
+ dataUrl = srcCanvas.toDataURL('image/png');
330
+ } catch {
331
+ dataUrl = ''; // 被污染的 canvas:留透明
332
+ }
333
+ const img = dst.ownerDocument.createElement('img');
334
+ img.setAttribute(
335
+ 'style',
336
+ `${dstCanvas.getAttribute('style') || ''};display:inline-block!important;`,
337
+ );
338
+ if (dstCanvas.className) img.className = dstCanvas.className;
339
+ img.setAttribute('width', String(srcCanvas.width || dstCanvas.width || 1));
340
+ img.setAttribute('height', String(srcCanvas.height || dstCanvas.height || 1));
341
+ img.src = dataUrl || TRANSPARENT_PNG;
342
+ dstCanvas.replaceWith(img);
343
+ });
344
+ };
345
+
346
+ const TRANSPARENT_PNG =
347
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
348
+
349
+ const captureSlide = async (
350
+ slide: HTMLElement,
351
+ cssBundle: string,
352
+ ): Promise<{ dataUrl: string; widthPx: number; heightPx: number }> => {
353
+ const inlineW = parseFloat(slide.style.width || '');
354
+ const inlineH = parseFloat(slide.style.height || '');
355
+ const rect = slide.getBoundingClientRect();
356
+ const w = Math.max(1, Math.round(Number.isFinite(inlineW) && inlineW > 0 ? inlineW : rect.width));
357
+ const h = Math.max(1, Math.round(Number.isFinite(inlineH) && inlineH > 0 ? inlineH : rect.height));
358
+
359
+ const clone = slide.cloneNode(true) as HTMLElement;
360
+ flattenCloneCanvases(slide, clone);
361
+ await inlineCloneAttrRefs(clone);
362
+ await inlineCloneStyleUrls(clone);
363
+
364
+ // 保留 .flyfish-pptx-content 作用域祖先:样式表里的选择器全部依赖它
365
+ const wrapper = clone.ownerDocument.createElement('div');
366
+ wrapper.className = 'flyfish-pptx-content';
367
+ wrapper.setAttribute('data-export-wrapper', 'true');
368
+ wrapper.setAttribute(
369
+ 'style',
370
+ `position:relative!important;top:auto!important;left:auto!important;transform:none!important;` +
371
+ `width:${w}px!important;height:${h}px!important;margin:0!important;overflow:hidden!important;`,
372
+ );
373
+ wrapper.appendChild(clone);
374
+
375
+ const xhtml = new XMLSerializer().serializeToString(wrapper);
376
+
377
+ // 覆盖视图专属规则:去掉居中 margin/纸张阴影;content 不再绝对定位/缩放。
378
+ // 样式表用 CDATA 包裹(含选择器特殊字符)。
379
+ const overrideCss =
380
+ `.flyfish-pptx-content .slide{margin:0!important;box-shadow:none!important;border-radius:0!important}`;
381
+ const cdataSafe = `${cssBundle}\n${overrideCss}`.replace(/\]\]>/g, ']] >');
382
+ // 注意:foreignObject 内 <body> 的 xmlns 必须是 1999/xhtml。
383
+ // 命名空间写错时 <style> 不会被识别为样式表,CSS 源码会被当成可见文本
384
+ // 渲染到每一页(排查此问题时记录:曾误写成 2000/xhtml)。
385
+ const svg =
386
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${w * SCALE}" height="${h * SCALE}">` +
387
+ `<g transform="scale(${SCALE})">` +
388
+ `<foreignObject x="0" y="0" width="${w}" height="${h}">` +
389
+ `<body xmlns="http://www.w3.org/1999/xhtml" style="margin:0;padding:0;background:#ffffff">` +
390
+ `<style><![CDATA[${cdataSafe}]]></style>` +
391
+ `${xhtml}</body>` +
392
+ `</foreignObject></g></svg>`;
393
+
394
+ // 必须 data URL:Blob URL 加载的 SVG 会污染 canvas,toDataURL 抛 SecurityError
395
+ const url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
396
+ const img = new Image();
397
+ await new Promise<void>((resolve, reject) => {
398
+ img.onload = () => resolve();
399
+ img.onerror = () => {
400
+ const parts: string[] = [];
401
+ try {
402
+ const parsed = new DOMParser().parseFromString(svg, 'image/svg+xml');
403
+ const err = parsed.querySelector('parsererror');
404
+ if (err) {
405
+ parts.push(`XML错误: ${(err.textContent || '').slice(0, 150)}`);
406
+ // 定位错误行列附近的原始标记
407
+ const loc = /line (\d+) at column (\d+)/.exec(err.textContent || '');
408
+ if (loc) {
409
+ const line = Number(loc[1]);
410
+ const col = Number(loc[2]);
411
+ const lines = svg.split('\n');
412
+ const around = lines[line - 1] || '';
413
+ parts.push(
414
+ `上下文:${around.slice(Math.max(0, col - 160), col + 160)}`,
415
+ );
416
+ }
417
+ }
418
+ } catch {
419
+ /* 忽略诊断异常 */
420
+ }
421
+ const blobRefs = (svg.match(/blob:/g) || []).length;
422
+ const httpRefs = (svg.match(/(?:src|poster|href)=["']https?:/g) || []).length;
423
+ if (blobRefs || httpRefs) parts.push(`残留外链 blob:${blobRefs} http:${httpRefs}`);
424
+ parts.push(`SVG大小:${(svg.length / 1024).toFixed(0)}KB`);
425
+ reject(new Error(`幻灯片光栅化失败(${parts.join(';')})`));
426
+ };
427
+ img.src = url;
428
+ });
429
+
430
+ const canvas = document.createElement('canvas');
431
+ canvas.width = w * SCALE;
432
+ canvas.height = h * SCALE;
433
+ const ctx = canvas.getContext('2d');
434
+ if (!ctx) throw new Error('无法创建绘图上下文');
435
+ ctx.fillStyle = '#ffffff';
436
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
437
+ ctx.drawImage(img, 0, 0);
438
+ const dataUrl = canvas.toDataURL('image/png');
439
+ // 立即释放位图内存
440
+ canvas.width = 0;
441
+ canvas.height = 0;
442
+ return { dataUrl, widthPx: w, heightPx: h };
443
+ };
444
+
445
+ /**
446
+ * 把 PPT 渲染成逐页图片(导出 PDF 的核心,也可单独用于预览/其他导出格式)。
447
+ * 离屏独立渲染一份,避免用户当前视图的缩放/样式干扰光栅化,结束后自动卸载。
448
+ */
449
+ export const renderPptxSlidesToImages = async (
450
+ file: Blob,
451
+ onProgress?: (page: number, total: number) => void,
452
+ ): Promise<PptxSlideImage[]> => {
453
+ const mounted = await mountExportViewer(file);
454
+ try {
455
+ const slides = await waitForSlidesReady(mounted.viewer, mounted.host, mounted.getFatal);
456
+ if (slides.length === 0) throw new Error('未获取到可导出的幻灯片');
457
+ const content = mounted.host.querySelector<HTMLElement>('.flyfish-pptx-content');
458
+ if (!content) throw new Error('未获取到幻灯片容器');
459
+ const cssBundle = collectPptxCss(content);
460
+
461
+ const total = slides.length;
462
+ const images: PptxSlideImage[] = [];
463
+ for (let i = 0; i < total; i++) {
464
+ onProgress?.(i + 1, total);
465
+ const { dataUrl, widthPx, heightPx } = await captureSlide(slides[i], cssBundle);
466
+ images.push({
467
+ dataUrl,
468
+ widthPx,
469
+ heightPx,
470
+ widthPt: widthPx * 0.75,
471
+ heightPt: heightPx * 0.75,
472
+ });
473
+ }
474
+ return images;
475
+ } finally {
476
+ mounted.unmount();
477
+ }
478
+ };
479
+
480
+ /**
481
+ * PPT 导出 PDF(逐张幻灯片 SVG foreignObject 光栅化 → jsPDF 直接下载)。
482
+ * @returns 生成的 PDF 文件名
483
+ */
484
+ export const exportPptxPdf = async ({
485
+ file,
486
+ fileName,
487
+ onProgress,
488
+ }: ExportPptxPdfOptions): Promise<string> => {
489
+ const baseName = /\.(pptx|pptm|ppt|ppsx|pps|potx|potm)$/i.test(fileName)
490
+ ? fileName.replace(/\.(pptx|pptm|ppt|ppsx|pps|potx|potm)$/i, '')
491
+ : fileName;
492
+ const pptxName = `${baseName}.pptx`;
493
+ const pdfName = `${baseName}.pdf`;
494
+ const pptxFile =
495
+ file instanceof File ? file : new File([file], pptxName, { type: PPTX_MIME_TYPE });
496
+
497
+ // jsPDF 仅在点击导出时加载(vite 会把它归入 vendor-md-pdf-export chunk)
498
+ const { jsPDF } = await import('jspdf');
499
+
500
+ const images = await renderPptxSlidesToImages(pptxFile, onProgress);
501
+ if (images.length === 0) throw new Error('未获取到可导出的幻灯片');
502
+
503
+ const first = images[0];
504
+ const pdf = new jsPDF({
505
+ orientation: first.widthPt > first.heightPt ? 'landscape' : 'portrait',
506
+ unit: 'pt',
507
+ format: [first.widthPt, first.heightPt],
508
+ compress: true,
509
+ });
510
+
511
+ images.forEach((img, i) => {
512
+ if (i > 0) {
513
+ pdf.addPage(
514
+ [img.widthPt, img.heightPt],
515
+ img.widthPt > img.heightPt ? 'landscape' : 'portrait',
516
+ );
517
+ }
518
+ pdf.addImage(img.dataUrl, 'PNG', 0, 0, img.widthPt, img.heightPt, undefined, 'FAST');
519
+ });
520
+
521
+ pdf.save(pdfName);
522
+ return pdfName;
523
+ };