@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,15 @@
1
+ export interface MarkdownEditorProps {
2
+ value?: string;
3
+ visible?: boolean;
4
+ onClose?: () => void;
5
+ onSave?: (value: string) => void;
6
+ placeholder?: string;
7
+ readOnly?: boolean;
8
+ allowFullscreen?: boolean;
9
+ defaultPreviewMode?: boolean;
10
+ }
11
+
12
+ export interface MarkdownEditorState {
13
+ content: string;
14
+ isPreviewMode: boolean;
15
+ }
@@ -0,0 +1,602 @@
1
+ import React, { useCallback, useRef, useEffect, useState } from 'react';
2
+ import { Modal, Button, Space, message, Popconfirm, Tag } from 'antd';
3
+ import { SaveOutlined, FilePptFilled, UploadOutlined, DeleteOutlined, CloseOutlined, CloudOutlined, CloudServerOutlined } from '@ant-design/icons';
4
+ import { PptEditor, PptxBlobExporter, PptxParser, Presentation } from 'slidecanvas';
5
+ import { ArrowLeft } from 'lucide-react';
6
+ import { saveFile, loadFile, deleteFile } from '../IndexedDBUtils';
7
+ import { exportPptxPdf } from '../filetypes/pptExportPdf';
8
+ import { ExportDropdown } from '../ExportDropdown';
9
+ import { downloadBlob } from '../previewUtils';
10
+ import { CacheUserInfo } from '../../../utils/CacheUserInfo';
11
+ import uiLoading from '../../UILoading';
12
+ import uiConfirm from '../../UIConfirm';
13
+
14
+ interface PPTXEditorProps {
15
+ visible: boolean;
16
+ hiddenClose?: boolean;
17
+ isCreate?: boolean; // 是否为新建模式
18
+ onClose: () => void;
19
+ fileName: string;
20
+ onUpload?: (initialBlob: Blob | undefined, blob: Blob, fileName: string) => Promise<void>;
21
+ initialBlob?: Blob;
22
+ readOnly?: boolean;
23
+ uploadPercent?: number;
24
+ uploadSuccess?: boolean
25
+ // ---- 新增:本地文件 / 离线模式支持 ----
26
+ isOffline?: boolean;
27
+ isLocalOnly?: boolean;
28
+ hasUnsyncedChanges?: boolean;
29
+ onSaveLocalBlob?: (blob: Blob, fileName: string, markUnsynced?: boolean) => Promise<void>;
30
+ }
31
+
32
+ const PPTX_BUILTIN_TEXT_TRANSLATIONS: Record<string, string> = {
33
+ 'Create stunning presentations in seconds.': '快速创建精美演示文稿',
34
+ 'Start New Deck': '新建演示文稿',
35
+ 'Upload PPTX': '上传 PPTX',
36
+ 'Paste presentation URL...': '粘贴演示文稿链接...',
37
+ 'Load': '加载',
38
+ 'Supports S3, public links, and versioned assets': '支持 S3、公开链接和版本化资源',
39
+ 'Present': '放映',
40
+ 'Delete': '删除',
41
+ 'Generate Image': '生成图片',
42
+ 'Describe the image you want to create for this slide.': '描述要为当前幻灯片创建的图片。',
43
+ 'A futuristic city in the style of cyberpunk with neon lights...': '带有霓虹灯的赛博朋克风未来城市...',
44
+ 'Cancel': '取消',
45
+ 'Generate': '生成',
46
+ 'Generating...': '生成中...',
47
+ 'Preview Image': '预览图片',
48
+ 'Failed to load preview': '预览加载失败',
49
+ "The URL might be invalid or restricted. Ensure it's a valid image format.": '链接可能无效或受限,请确认它是有效的图片格式。',
50
+ 'Insert image': '插入图片',
51
+ 'Replace existing': '替换现有内容',
52
+ 'Discard': '放弃',
53
+ 'Generate Infographic': '生成信息图',
54
+ 'Describe the infographic you want to create for this slide.': '描述要为当前幻灯片创建的信息图。',
55
+ 'A pie chart showing market share distribution...': '展示市场份额分布的饼图...',
56
+ 'Generating Infographic...': '正在生成信息图...',
57
+ 'Polishing your prompt with AI...': '正在优化提示词...',
58
+ 'Step 1: AI Refinement': '第 1 步:AI 优化',
59
+ 'The AI has refined your selected text into a visual prompt. You can adjust it below.': 'AI 已将选中文本优化为视觉提示词,你可以在下方调整。',
60
+ 'Preview Infographic': '预览信息图',
61
+ 'Generated': '已生成',
62
+ 'Generated Infographic': '已生成信息图',
63
+ 'Insert infographic': '插入信息图',
64
+ };
65
+
66
+ export const PPTXEditor: React.FC<PPTXEditorProps> = ({ visible, hiddenClose = false, isCreate, onClose, fileName, onUpload, initialBlob, readOnly, uploadPercent, uploadSuccess, isOffline, isLocalOnly, hasUnsyncedChanges, onSaveLocalBlob }) => {
67
+ const presentationRef = useRef<Presentation | null>(null);
68
+ const editorContentRef = useRef<HTMLDivElement>(null);
69
+ const saveTimerRef = useRef<NodeJS.Timeout | null>(null);
70
+ const changeTimerRef = useRef<NodeJS.Timeout | null>(null);
71
+ const [initialPresentation, setInitialPresentation] = useState<Presentation | undefined>(undefined);
72
+ const [editorKey, setEditorKey] = useState(0);
73
+ const [userId, setUserId] = useState<string | null>(null);
74
+ const [hasLocalCache, setHasLocalCache] = useState(false);
75
+ const [hasUploaded, setHasUploaded] = useState(!isCreate);
76
+ const [loadingInitialBlob, setLoadingInitialBlob] = useState(false);
77
+ // 跟踪"标记为未同步"的调用是否已在本次编辑会话中执行过,避免每次 onChange 都写 IndexedDB
78
+ const markUnsyncedTriggeredRef = useRef(false);
79
+ // 跟踪编辑器是否已完成初始加载。编辑器初始化时会触发 onChange,需抑制以免误标"有修改未同步"。
80
+ const isEditorReadyRef = useRef(false);
81
+ // 跟踪当前差分上传的基准 blob(即服务器上最新的文件内容)。
82
+ // 上传成功后会更新为本次上传的 blob,确保下次差分基准与服务器一致。
83
+ const baseBlobRef = useRef<Blob | undefined>(initialBlob);
84
+
85
+ // 用 ref 跟踪这些"状态标记"属性,避免它们的变化(如保存/上传后更新标记)
86
+ // 触发编辑器相关 useEffect 重新执行,导致画面闪动。
87
+ const hasUnsyncedChangesRef = useRef(hasUnsyncedChanges);
88
+ const onSaveLocalBlobRef = useRef(onSaveLocalBlob);
89
+ hasUnsyncedChangesRef.current = hasUnsyncedChanges;
90
+ onSaveLocalBlobRef.current = onSaveLocalBlob;
91
+
92
+ // 获取当前登录用户
93
+ useEffect(() => {
94
+ if (visible) {
95
+ const getUserId = async () => {
96
+ try {
97
+ const userInfo = CacheUserInfo.currentLocalUser();
98
+ if (userInfo?.id) {
99
+ setUserId(userInfo.id);
100
+ } else {
101
+ setUserId(null);
102
+ }
103
+ } catch (error) {
104
+ console.error('获取用户信息失败:', error);
105
+ setUserId(null);
106
+ }
107
+ };
108
+ getUserId();
109
+ }
110
+ }, [visible]);
111
+
112
+ // 加载初始文件
113
+ useEffect(() => {
114
+ const loadInitialFile = async () => {
115
+ if (visible && userId) {
116
+ // 如果有传入的 initialBlob,优先加载它
117
+ if (initialBlob) {
118
+ try {
119
+ setLoadingInitialBlob(true);
120
+ const parser = new PptxParser();
121
+
122
+ // 需求3:彻底移除旧缓存检查。既然有了 initialBlob,直接解析
123
+ const presentation = await parser.parse(initialBlob);
124
+ setInitialPresentation(presentation);
125
+ setEditorKey((prev) => prev + 1);
126
+ setHasLocalCache(false);
127
+ } catch (error) {
128
+ console.error('加载文件失败:', error);
129
+ message.error('加载文件失败');
130
+ setInitialPresentation(undefined);
131
+ setHasLocalCache(false);
132
+ } finally {
133
+ setLoadingInitialBlob(false);
134
+ }
135
+ } else if (isCreate && userId) {
136
+ // 需求3:新建模式下不再自动检查/加载旧的本地缓存
137
+ // 新建的本地文件会通过 initialBlob 从 localFiles store 传入内容
138
+ setInitialPresentation(undefined);
139
+ setHasLocalCache(false);
140
+ } else {
141
+ // 没有用户ID,重置状态
142
+ setInitialPresentation(undefined);
143
+ setHasLocalCache(false);
144
+ }
145
+ }
146
+ };
147
+
148
+ loadInitialFile();
149
+ }, [visible, fileName, userId, initialBlob, isCreate, readOnly]);
150
+
151
+ // 将 presentation 导出为 blob
152
+ const exportToBlob = useCallback(async (): Promise<Blob | null> => {
153
+ if (!presentationRef.current) {
154
+ message.error('编辑器未初始化');
155
+ return null;
156
+ }
157
+ try {
158
+ const exporter = new PptxBlobExporter();
159
+ const blob = await exporter.exportToBlob(presentationRef.current);
160
+ return blob;
161
+ } catch (error) {
162
+ console.error('导出失败:', error);
163
+ message.error('导出失败');
164
+ return null;
165
+ }
166
+ }, []);
167
+
168
+ // 自动保存到 IndexedDB(防抖)+ 同步 localFiles store
169
+ const autoSaveToIndexedDB = useCallback((presentation: Presentation) => {
170
+ if (!fileName || !userId) return;
171
+
172
+ // 清除之前的定时器
173
+ if (saveTimerRef.current) {
174
+ clearTimeout(saveTimerRef.current);
175
+ }
176
+
177
+ setHasLocalCache(true);
178
+
179
+ // 设置新的定时器保存
180
+ saveTimerRef.current = setTimeout(async () => {
181
+ try {
182
+ uiLoading.show({tip: '正在导出文件'});
183
+ await saveFile(userId, fileName, 'pptx', presentation);
184
+ setHasLocalCache(false);
185
+ // 同步更新 localFiles store,确保自动保存也持久化到 localFiles
186
+ const fn = onSaveLocalBlobRef.current;
187
+ if (fn) {
188
+ try {
189
+ const blob = await exportToBlob();
190
+ if (blob) {
191
+ await fn(blob, fileName, false);
192
+ }
193
+ } catch (err) {
194
+ console.warn('自动保存时同步 localFiles store 失败(不影响本地保存):', err);
195
+ }
196
+ }
197
+ } catch (error) {
198
+ console.error('自动保存到 IndexedDB 失败:', error);
199
+ } finally {
200
+ uiLoading.hide();
201
+ }
202
+ }, 100000);
203
+ }, [fileName, userId, exportToBlob]);
204
+
205
+ // 清除本地缓存
206
+ const handleClearCache = useCallback(async () => {
207
+ try {
208
+ if (!userId) {
209
+ message.error('无法获取用户信息');
210
+ return;
211
+ }
212
+ await deleteFile(userId, fileName, 'pptx');
213
+ setHasLocalCache(false);
214
+ message.success('已清除本地缓存');
215
+ // 重新加载页面以清除编辑器中的数据
216
+ setInitialPresentation(undefined);
217
+ setEditorKey((prev) => prev + 1);
218
+ } catch (error) {
219
+ console.error('清除本地缓存失败:', error);
220
+ message.error('清除本地缓存失败');
221
+ }
222
+ }, [fileName, userId]);
223
+
224
+ // 保存到 IndexedDB
225
+ const handleSave = useCallback(async () => {
226
+ try {
227
+ if (!presentationRef.current) {
228
+ message.error('编辑器未初始化');
229
+ return;
230
+ }
231
+ if (!userId) {
232
+ message.error('无法获取用户信息');
233
+ return;
234
+ }
235
+
236
+ uiLoading.show({tip: '正在导出文件'});
237
+
238
+ await saveFile(userId, fileName, 'pptx', presentationRef.current);
239
+ setHasLocalCache(false);
240
+
241
+ // 通知父组件更新 localFiles store
242
+ const fn = onSaveLocalBlobRef.current;
243
+ if (fn) {
244
+ try {
245
+ const blob = await exportToBlob();
246
+ if (blob) {
247
+ await fn(blob, fileName, false);
248
+ }
249
+ } catch (err) {
250
+ console.warn('同步本地文件内容到 localFiles store 失败(不影响本地保存):', err);
251
+ }
252
+ }
253
+
254
+ message.success('已保存到本地缓存');
255
+ } catch (error) {
256
+ console.error('保存到本地缓存失败:', error);
257
+ message.error('保存失败');
258
+ } finally {
259
+ uiLoading.hide();
260
+ }
261
+ }, [fileName, userId]);
262
+
263
+ // 上传到服务器
264
+ const handleUpload = useCallback(async () => {
265
+ if (!onUpload) {
266
+ message.error('未提供上传功能');
267
+ return;
268
+ }
269
+ try {
270
+ uiLoading.show({tip: '正在导出文件'});
271
+ const pptxBlob = await exportToBlob();
272
+ if (!pptxBlob) return;
273
+
274
+ // 使用 baseBlobRef 作为差分基准,而非 initialBlob。
275
+ // 上传成功后 baseBlobRef 会更新为本次上传的 blob,确保下次差分基准与服务端一致。
276
+ const baseBlob = baseBlobRef.current ?? initialBlob;
277
+ await onUpload(baseBlob, pptxBlob, fileName);
278
+
279
+ // 上传成功后更新差分基准,使下次上传的差分基准与服务器一致
280
+ baseBlobRef.current = pptxBlob;
281
+
282
+ // 上传成功后,清空本地缓存
283
+ if (userId) {
284
+ await deleteFile(userId, fileName, 'pptx');
285
+ setHasLocalCache(false);
286
+ setHasUploaded(true);
287
+ }
288
+ // 上传成功后重置"立即标记未同步"触发器,使后续编辑可再次立即触发
289
+ markUnsyncedTriggeredRef.current = false;
290
+ } catch (error) {
291
+ console.error('上传失败:', error);
292
+ message.error('上传失败');
293
+ } finally {
294
+ uiLoading.hide();
295
+ }
296
+ }, [fileName, onUpload, exportToBlob, userId, initialBlob]);
297
+
298
+ // ============================================================================
299
+ // 导出 PDF:
300
+ // slidecanvas 编辑器本身没有 PDF 能力,先导出含最新修改的 pptx blob,
301
+ // 再离屏挂载 @file-viewer/pptx 逐页 SVG foreignObject 光栅化直接下载 PDF。
302
+ // ============================================================================
303
+ const [exportingPdf, setExportingPdf] = useState(false);
304
+ const handleExportPdf = useCallback(async () => {
305
+ setExportingPdf(true);
306
+ message.loading({ key: 'ppt-export-pdf', content: '正在保存演示文稿内容…', duration: 0 });
307
+ try {
308
+ const blob = await exportToBlob();
309
+ if (!blob) {
310
+ message.error('导出失败:无法获取文档内容');
311
+ return;
312
+ }
313
+ await exportPptxPdf({
314
+ file: blob,
315
+ fileName,
316
+ onProgress: (page, total) => {
317
+ message.loading({ key: 'ppt-export-pdf', content: `正在导出第 ${page} / ${total} 页…`, duration: 0 });
318
+ },
319
+ });
320
+ } catch (error) {
321
+ console.error('PPT 导出 PDF 失败:', error);
322
+ message.error(`导出 PDF 失败:${(error as Error)?.message || '未知错误'}`);
323
+ } finally {
324
+ message.destroy('ppt-export-pdf');
325
+ setExportingPdf(false);
326
+ }
327
+ }, [exportToBlob, fileName]);
328
+
329
+ // 下载原始文件:直接下载上传时的原始 pptx(initialBlob),不是编辑后的内容
330
+ const handleDownloadOriginal = useCallback(() => {
331
+ if (!initialBlob) return;
332
+ downloadBlob(initialBlob, fileName);
333
+ }, [initialBlob, fileName]);
334
+ const onDownload = !isCreate && initialBlob ? handleDownloadOriginal : undefined;
335
+
336
+ // 监听 presentation 变化
337
+ const handleChange = useCallback((presentation: Presentation) => {
338
+ // 抑制编辑器初始化期间触发的 onChange,避免误标"有修改未同步"
339
+ if (!isEditorReadyRef.current) return;
340
+ if (changeTimerRef.current) {
341
+ clearTimeout(changeTimerRef.current);
342
+ }
343
+
344
+ // slidecanvas 可能在内部组件 render 阶段触发 onChange,延迟更新避免 React 跨组件 render 更新警告。
345
+ changeTimerRef.current = setTimeout(() => {
346
+ setHasUploaded(false);
347
+ presentationRef.current = presentation;
348
+ autoSaveToIndexedDB(presentation);
349
+
350
+ // 首次编辑(且 hasUnsyncedChanges 目前为 false)时,立即标记未同步。
351
+ // 上传成功后 hasUnsyncedChanges 被清除,用户继续编辑后应立即重新显示"有未同步修改"。
352
+ if (!markUnsyncedTriggeredRef.current && !hasUnsyncedChangesRef.current && onSaveLocalBlobRef.current) {
353
+ markUnsyncedTriggeredRef.current = true;
354
+ const fn = onSaveLocalBlobRef.current;
355
+ (async () => {
356
+ try {
357
+ const blob = await exportToBlob();
358
+ if (blob) {
359
+ await fn(blob, fileName);
360
+ } else {
361
+ markUnsyncedTriggeredRef.current = false;
362
+ }
363
+ } catch (err) {
364
+ markUnsyncedTriggeredRef.current = false;
365
+ console.warn('编辑时立即标记未同步失败(不影响编辑,等待自动/手动保存即可):', err);
366
+ }
367
+ })();
368
+ }
369
+ }, 0);
370
+ }, [autoSaveToIndexedDB, exportToBlob, fileName]);
371
+
372
+ useEffect(() => {
373
+ if (!visible || !editorContentRef.current) return;
374
+
375
+ const editorRoot = editorContentRef.current;
376
+ const normalizeText = (value: string) => value.replace(/\s+/g, ' ').trim();
377
+ const translateText = (value: string) => PPTX_BUILTIN_TEXT_TRANSLATIONS[normalizeText(value)];
378
+ const isBuiltinExportText = (value: string) => /^export(?:\s*\(?pptx\)?)?$/i.test(normalizeText(value));
379
+ const isBuiltinSavedText = (value: string) => /^saved$/i.test(normalizeText(value));
380
+ const applyBuiltinChineseText = () => {
381
+ const elements = Array.from(editorRoot.querySelectorAll('button, [role="button"], span, div, input, textarea, img'));
382
+
383
+ elements.forEach((item) => {
384
+ const text = item.textContent || '';
385
+ const label = item.getAttribute('aria-label') || item.getAttribute('title') || '';
386
+
387
+ if (isBuiltinExportText(text) || isBuiltinExportText(label) || isBuiltinSavedText(text) || isBuiltinSavedText(label)) {
388
+ (item as HTMLElement).style.display = 'none';
389
+ item.setAttribute('aria-hidden', 'true');
390
+ return;
391
+ }
392
+
393
+ ['aria-label', 'title', 'placeholder', 'alt'].forEach((attributeName) => {
394
+ const attributeValue = item.getAttribute(attributeName);
395
+ const translatedValue = attributeValue ? translateText(attributeValue) : '';
396
+ if (translatedValue) {
397
+ item.setAttribute(attributeName, translatedValue);
398
+ }
399
+ });
400
+ });
401
+
402
+ const textWalker = document.createTreeWalker(editorRoot, NodeFilter.SHOW_TEXT);
403
+ let currentNode = textWalker.nextNode();
404
+
405
+ while (currentNode) {
406
+ const translatedValue = translateText(currentNode.nodeValue || '');
407
+ if (translatedValue) {
408
+ currentNode.nodeValue = translatedValue;
409
+ }
410
+
411
+ currentNode = textWalker.nextNode();
412
+ }
413
+ };
414
+
415
+ applyBuiltinChineseText();
416
+ const observer = new MutationObserver(applyBuiltinChineseText);
417
+ observer.observe(editorRoot, {
418
+ childList: true,
419
+ subtree: true,
420
+ });
421
+
422
+ return () => observer.disconnect();
423
+ }, [visible, editorKey]);
424
+
425
+ // 编辑器初始化后,延迟标记为"就绪",让初始化期间的 onChange 事件先过去
426
+ useEffect(() => {
427
+ if (!visible || loadingInitialBlob) return;
428
+ const timer = setTimeout(() => {
429
+ isEditorReadyRef.current = true;
430
+ }, 2000);
431
+ return () => {
432
+ clearTimeout(timer);
433
+ isEditorReadyRef.current = false;
434
+ };
435
+ }, [editorKey, visible, loadingInitialBlob]);
436
+
437
+ // initialBlob 变化时(文件切换/加载)重置差分基准
438
+ useEffect(() => {
439
+ baseBlobRef.current = initialBlob;
440
+ }, [initialBlob]);
441
+
442
+ // Ctrl+S / Cmd+S 保存到本地缓存
443
+ useEffect(() => {
444
+ if (!visible) return;
445
+ const handleKeyDown = (e: KeyboardEvent) => {
446
+ if ((e.ctrlKey || e.metaKey) && e.key === 's') {
447
+ e.preventDefault();
448
+ handleSave();
449
+ }
450
+ };
451
+ window.addEventListener('keydown', handleKeyDown);
452
+ return () => window.removeEventListener('keydown', handleKeyDown);
453
+ }, [visible, handleSave]);
454
+
455
+ // 重置状态和清理定时器(当编辑器打开/关闭时)
456
+ useEffect(() => {
457
+ if (visible) {
458
+ setHasUploaded(!isCreate);
459
+ // 重置"立即标记未同步"触发器
460
+ markUnsyncedTriggeredRef.current = false;
461
+ // 清理之前的定时器
462
+ if (saveTimerRef.current) {
463
+ clearTimeout(saveTimerRef.current);
464
+ saveTimerRef.current = null;
465
+ }
466
+ }
467
+
468
+ // 组件卸载时清理定时器
469
+ return () => {
470
+ if (changeTimerRef.current) {
471
+ clearTimeout(changeTimerRef.current);
472
+ changeTimerRef.current = null;
473
+ }
474
+ if (saveTimerRef.current) {
475
+ clearTimeout(saveTimerRef.current);
476
+ saveTimerRef.current = null;
477
+ }
478
+ };
479
+ }, [visible, isCreate]);
480
+
481
+ // 处理关闭确认
482
+ const handleClose = useCallback(() => {
483
+ // 如果没有上传过且有本地缓存,显示确认
484
+ if (!hasUploaded && hasLocalCache) {
485
+ // 直接调用 onClose,使用 Popconfirm 处理确认
486
+ onClose();
487
+ } else {
488
+ onClose();
489
+ }
490
+ }, [hasUploaded, hasLocalCache, onClose]);
491
+
492
+ return (
493
+ <Modal
494
+ open={visible}
495
+ onCancel={onClose}
496
+ keyboard={false}
497
+ closeIcon={null}
498
+ footer={null}
499
+ width="100%"
500
+ style={{ top: 0, maxWidth: '100vw' }}
501
+ styles={{
502
+ body: {
503
+ height: '100dvh',
504
+ padding: 0,
505
+ },
506
+ }}
507
+ className="ant-modal-nopadding ant-modal-fullscreen"
508
+ transitionName='none'
509
+ >
510
+ <div className="h-full flex flex-col bg-white p-0 m-0">
511
+ {/* 工具栏 */}
512
+ <div className={`flex items-center justify-between px-4 py-2 pr-0 border-b flex-shrink-0 ${isOffline ? 'bg-orange-50 border-orange-200' : 'bg-gray-50 border-gray-200'}`}>
513
+ {/* 只读模式下直接关闭,不需要提示保存 */}
514
+ <span>&nbsp;</span>
515
+ {!readOnly && <span>&nbsp;</span>}
516
+ <div className="flex items-center gap-2 flex-wrap min-w-[40%]">
517
+ {!isOffline && isLocalOnly && (
518
+ <Tag icon={<CloudOutlined />} color="orange" style={{ margin: 0 }}>
519
+ 本地文件
520
+ </Tag>
521
+ )}
522
+ {!isOffline && !isLocalOnly && hasUnsyncedChanges && (
523
+ <Tag icon={<CloudOutlined />} color="warning" style={{ margin: 0 }}>
524
+ 有未同步修改
525
+ </Tag>
526
+ )}
527
+ {!isOffline && !isLocalOnly && !hasUnsyncedChanges && !onUpload ? (
528
+ <Tag icon={<CloudServerOutlined />} color="default" style={{ margin: 0 }}>
529
+ 同步完成
530
+ </Tag>
531
+ ) : null}
532
+ <FilePptFilled className="text-orange-500" />
533
+ <span className="text-sm font-medium">{hasLocalCache ? '*' : ''}{fileName}</span>
534
+ </div>
535
+ {!readOnly && !hiddenClose && (
536
+ <Space wrap={true} align="center" direction="horizontal">
537
+ {/* <Button icon={<DeleteOutlined />} onClick={handleClearCache}>
538
+ 清除缓存
539
+ </Button> */}
540
+ <ExportDropdown
541
+ onExportPdf={handleExportPdf}
542
+ onDownload={onDownload}
543
+ exportingPdf={exportingPdf}
544
+ buttonProps={{ style: { paddingLeft: 4, paddingRight: 4 } }}
545
+ />
546
+ {onUpload && (
547
+ <Button type="text" style={{ paddingLeft: 4, paddingRight: 4 }} title="每次上传将保留一个历史版本,最多保留10个版本" icon={<UploadOutlined />} onClick={handleUpload} disabled={uploadPercent !== undefined && uploadPercent < 1}>
548
+ {uploadPercent !== undefined && uploadPercent < 1 ? `上传中 ${Math.round(uploadPercent * 100)}%` : '上传'}
549
+ </Button>
550
+ )}
551
+ <Button type="primary" icon={<SaveOutlined />} onClick={handleSave}>
552
+ 保存
553
+ </Button>
554
+ {(!readOnly && (hasLocalCache || !hasUploaded)) ?
555
+ (<Popconfirm
556
+ title="确定要退出吗?"
557
+ description={hasLocalCache ? "修改未保存,确定要退出吗?" : "修改未上传,确定要退出吗?"}
558
+ onConfirm={onClose}
559
+ okText="确定"
560
+ cancelText="取消"
561
+ >
562
+ <Button type="text" className="ml-3 mr-2" icon={<CloseOutlined className="w-6 h-6" />} />
563
+ </Popconfirm>) : (<CloseOutlined onClick={onClose} className='ml-3 mr-2 w-6 h-6' />)}
564
+ </Space>
565
+ )}
566
+ {readOnly && !hiddenClose && (
567
+ <Space>
568
+ <ExportDropdown
569
+ onExportPdf={handleExportPdf}
570
+ onDownload={onDownload}
571
+ exportingPdf={exportingPdf}
572
+ buttonProps={{ style: { paddingLeft: 4, paddingRight: 4 } }}
573
+ />
574
+ <CloseOutlined onClick={onClose} className='ml-3 mr-2 w-6 h-6' />
575
+ </Space>)
576
+ }
577
+ {hiddenClose && <span>&nbsp;</span>}
578
+ </div>
579
+
580
+ {/* SlideCanvas 编辑器容器 */}
581
+ <div
582
+ ref={editorContentRef}
583
+ className="relative flex-1 overflow-hidden"
584
+ style={{ height: 'calc(100% - 48px)', minHeight: 0 }}
585
+ >
586
+ {visible && (
587
+ <PptEditor
588
+ key={editorKey}
589
+ appName="PPT 编辑器"
590
+ width="100%"
591
+ height="100%"
592
+ initialPresentation={initialPresentation}
593
+ onChange={handleChange}
594
+ />
595
+ )}
596
+ </div>
597
+ </div>
598
+ </Modal>
599
+ );
600
+ };
601
+
602
+ export default PPTXEditor;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 将块文档(.rdoc)导出为 Markdown 文件。
3
+ *
4
+ * 图片块不参与导出:.rdoc 里的图片是 base64 内嵌在 JSON 中,直接写进 Markdown
5
+ * 会让 .md 文件体积暴涨且难以在其他编辑器里使用,因此导出时整体跳过图片块。
6
+ */
7
+
8
+ import type { Block } from '@blocknote/core';
9
+
10
+ /** 只需要改造文档为 Markdown 的能力,避免依赖编辑器的完整类型 */
11
+ interface MarkdownSerializableEditor {
12
+ blocksToMarkdownLossy(blocks?: Block[]): string;
13
+ }
14
+
15
+ export interface ExportRDocMarkdownResult {
16
+ /** 生成的 Markdown 文本 */
17
+ markdown: string;
18
+ /** 被跳过的图片块数量 */
19
+ skippedImages: number;
20
+ }
21
+
22
+ const IMAGE_BLOCK_TYPE = 'image';
23
+
24
+ /** 递归剔除图片块(图片也可能挂在列表项等块的 children 里) */
25
+ const stripImageBlocks = (blocks: Block[], skipped: { count: number }): Block[] =>
26
+ blocks
27
+ .filter((block) => {
28
+ if ((block.type as string) === IMAGE_BLOCK_TYPE) {
29
+ skipped.count += 1;
30
+ return false;
31
+ }
32
+ return true;
33
+ })
34
+ .map((block) =>
35
+ block.children && block.children.length > 0
36
+ ? { ...block, children: stripImageBlocks(block.children, skipped) }
37
+ : block,
38
+ );
39
+
40
+ const toMarkdownFileName = (fileName: string): string => {
41
+ const base = fileName.replace(/\.rdoc$/i, '').trim() || 'document';
42
+ return `${base}.md`;
43
+ };
44
+
45
+ /** 触发浏览器下载(a[download] 方式,导出量小,无需分片) */
46
+ const downloadMarkdown = (markdown: string, fileName: string): void => {
47
+ const blob = new Blob([markdown], { type: 'text/markdown;charset=utf-8' });
48
+ const url = URL.createObjectURL(blob);
49
+ const anchor = document.createElement('a');
50
+ anchor.href = url;
51
+ anchor.download = toMarkdownFileName(fileName);
52
+ anchor.style.display = 'none';
53
+ document.body.appendChild(anchor);
54
+ anchor.click();
55
+ anchor.remove();
56
+ window.setTimeout(() => URL.revokeObjectURL(url), 0);
57
+ };
58
+
59
+ /**
60
+ * 将块文档导出为 Markdown 并触发浏览器下载。
61
+ * @throws 没有可导出内容时抛出异常,由调用方提示用户
62
+ */
63
+ export const exportRDocMarkdown = (
64
+ editor: MarkdownSerializableEditor,
65
+ blocks: Block[],
66
+ fileName: string,
67
+ ): ExportRDocMarkdownResult => {
68
+ const skipped = { count: 0 };
69
+ const markdown = editor.blocksToMarkdownLossy(stripImageBlocks(blocks, skipped));
70
+
71
+ if (!markdown.trim()) {
72
+ throw new Error('没有可导出的内容(图片不参与导出)');
73
+ }
74
+
75
+ downloadMarkdown(markdown, fileName);
76
+ return { markdown, skippedImages: skipped.count };
77
+ };