@docyrus/ui-pro-ai-assistant 0.0.1

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 (308) hide show
  1. package/dist/index.js +26161 -0
  2. package/dist/index.js.map +1 -0
  3. package/package.json +155 -0
  4. package/src/components/assistant-animations.tsx +29 -0
  5. package/src/components/assistant-dialogs.tsx +235 -0
  6. package/src/components/code-view.tsx +278 -0
  7. package/src/components/create-agent-task.tsx +104 -0
  8. package/src/components/create-new-work-version.tsx +30 -0
  9. package/src/components/extract-web.tsx +160 -0
  10. package/src/components/forward-to-agent.tsx +90 -0
  11. package/src/components/generate-chart.tsx +101 -0
  12. package/src/components/generative-action-button.tsx +122 -0
  13. package/src/components/generative-tool.tsx +685 -0
  14. package/src/components/generative-ui-object.tsx +210 -0
  15. package/src/components/input-area.tsx +1209 -0
  16. package/src/components/json-schema-layout.tsx +326 -0
  17. package/src/components/list-item-card.tsx +92 -0
  18. package/src/components/mermaid-diagram.tsx +192 -0
  19. package/src/components/preview-image.tsx +47 -0
  20. package/src/components/request-approval.tsx +48 -0
  21. package/src/components/request-user-input.tsx +270 -0
  22. package/src/components/search-web.tsx +319 -0
  23. package/src/components/sheet-command.tsx +88 -0
  24. package/src/components/shell-canvas.tsx +122 -0
  25. package/src/components/show-advanced-data-table.tsx +352 -0
  26. package/src/components/show-generated-content-options.tsx +93 -0
  27. package/src/components/show-people-cards.tsx +180 -0
  28. package/src/components/subagent-tool.tsx +180 -0
  29. package/src/components/text-editor-tool.tsx +328 -0
  30. package/src/components/work-canvas.tsx +88 -0
  31. package/src/components/work-card.tsx +42 -0
  32. package/src/declarations.d.ts +1 -0
  33. package/src/docy-assistant.tsx +1962 -0
  34. package/src/hooks/index.ts +7 -0
  35. package/src/hooks/use-assistant-api.ts +507 -0
  36. package/src/hooks/use-deployment-data.ts +162 -0
  37. package/src/hooks/use-project-state.ts +347 -0
  38. package/src/hooks/use-session-state.ts +207 -0
  39. package/src/hooks/use-speech-recognition.ts +137 -0
  40. package/src/hooks/use-ui-state.ts +185 -0
  41. package/src/hooks/use-works-state.ts +146 -0
  42. package/src/i18n/context.tsx +253 -0
  43. package/src/i18n/index.ts +19 -0
  44. package/src/i18n/locales/de.json +198 -0
  45. package/src/i18n/locales/el.json +198 -0
  46. package/src/i18n/locales/en.json +226 -0
  47. package/src/i18n/locales/es.json +198 -0
  48. package/src/i18n/locales/fr.json +198 -0
  49. package/src/i18n/locales/it.json +198 -0
  50. package/src/i18n/locales/pt.json +198 -0
  51. package/src/i18n/locales/sl.json +198 -0
  52. package/src/i18n/locales/tr.json +211 -0
  53. package/src/i18n/types.ts +23 -0
  54. package/src/i18n/use-translation.ts +17 -0
  55. package/src/index.ts +18 -0
  56. package/src/internal/plate-editor/editor/auth-context.ts +11 -0
  57. package/src/internal/plate-editor/editor/editor-base-kit.tsx +39 -0
  58. package/src/internal/plate-editor/editor/editor-kit.tsx +89 -0
  59. package/src/internal/plate-editor/editor/plate-editor.tsx +75 -0
  60. package/src/internal/plate-editor/editor/plate-types.ts +126 -0
  61. package/src/internal/plate-editor/editor/plugins/ai-kit.tsx +172 -0
  62. package/src/internal/plate-editor/editor/plugins/autoformat-kit.tsx +211 -0
  63. package/src/internal/plate-editor/editor/plugins/basic-blocks-base-kit.tsx +26 -0
  64. package/src/internal/plate-editor/editor/plugins/basic-blocks-kit.tsx +51 -0
  65. package/src/internal/plate-editor/editor/plugins/basic-marks-base-kit.tsx +24 -0
  66. package/src/internal/plate-editor/editor/plugins/basic-marks-kit.tsx +38 -0
  67. package/src/internal/plate-editor/editor/plugins/basic-nodes-kit.tsx +6 -0
  68. package/src/internal/plate-editor/editor/plugins/block-menu-kit.tsx +14 -0
  69. package/src/internal/plate-editor/editor/plugins/block-placeholder-kit.tsx +17 -0
  70. package/src/internal/plate-editor/editor/plugins/block-selection-kit.tsx +31 -0
  71. package/src/internal/plate-editor/editor/plugins/callout-base-kit.tsx +5 -0
  72. package/src/internal/plate-editor/editor/plugins/callout-kit.tsx +7 -0
  73. package/src/internal/plate-editor/editor/plugins/code-block-base-kit.tsx +23 -0
  74. package/src/internal/plate-editor/editor/plugins/code-block-kit.tsx +26 -0
  75. package/src/internal/plate-editor/editor/plugins/column-base-kit.tsx +8 -0
  76. package/src/internal/plate-editor/editor/plugins/column-kit.tsx +7 -0
  77. package/src/internal/plate-editor/editor/plugins/comment-base-kit.tsx +5 -0
  78. package/src/internal/plate-editor/editor/plugins/comment-kit.tsx +174 -0
  79. package/src/internal/plate-editor/editor/plugins/copilot-kit.tsx +68 -0
  80. package/src/internal/plate-editor/editor/plugins/cursor-overlay-kit.tsx +13 -0
  81. package/src/internal/plate-editor/editor/plugins/date-base-kit.tsx +5 -0
  82. package/src/internal/plate-editor/editor/plugins/date-kit.tsx +7 -0
  83. package/src/internal/plate-editor/editor/plugins/discussion-kit.tsx +36 -0
  84. package/src/internal/plate-editor/editor/plugins/dnd-kit.tsx +27 -0
  85. package/src/internal/plate-editor/editor/plugins/docx-export-kit.tsx +43 -0
  86. package/src/internal/plate-editor/editor/plugins/docx-kit.tsx +6 -0
  87. package/src/internal/plate-editor/editor/plugins/emoji-kit.tsx +15 -0
  88. package/src/internal/plate-editor/editor/plugins/exit-break-kit.tsx +12 -0
  89. package/src/internal/plate-editor/editor/plugins/floating-toolbar-kit.tsx +19 -0
  90. package/src/internal/plate-editor/editor/plugins/font-base-kit.tsx +36 -0
  91. package/src/internal/plate-editor/editor/plugins/font-kit.tsx +47 -0
  92. package/src/internal/plate-editor/editor/plugins/indent-base-kit.tsx +19 -0
  93. package/src/internal/plate-editor/editor/plugins/indent-kit.tsx +22 -0
  94. package/src/internal/plate-editor/editor/plugins/link-base-kit.tsx +5 -0
  95. package/src/internal/plate-editor/editor/plugins/link-kit.tsx +35 -0
  96. package/src/internal/plate-editor/editor/plugins/list-base-kit.tsx +24 -0
  97. package/src/internal/plate-editor/editor/plugins/list-kit.tsx +27 -0
  98. package/src/internal/plate-editor/editor/plugins/markdown-kit.tsx +18 -0
  99. package/src/internal/plate-editor/editor/plugins/math-base-kit.tsx +8 -0
  100. package/src/internal/plate-editor/editor/plugins/math-kit.tsx +10 -0
  101. package/src/internal/plate-editor/editor/plugins/media-base-kit.tsx +37 -0
  102. package/src/internal/plate-editor/editor/plugins/media-kit.tsx +53 -0
  103. package/src/internal/plate-editor/editor/plugins/mention-base-kit.tsx +5 -0
  104. package/src/internal/plate-editor/editor/plugins/mention-kit.tsx +36 -0
  105. package/src/internal/plate-editor/editor/plugins/slash-kit.tsx +17 -0
  106. package/src/internal/plate-editor/editor/plugins/suggestion-base-kit.tsx +5 -0
  107. package/src/internal/plate-editor/editor/plugins/suggestion-kit.tsx +95 -0
  108. package/src/internal/plate-editor/editor/plugins/table-base-kit.tsx +20 -0
  109. package/src/internal/plate-editor/editor/plugins/table-kit.tsx +22 -0
  110. package/src/internal/plate-editor/editor/plugins/toc-base-kit.tsx +5 -0
  111. package/src/internal/plate-editor/editor/plugins/toc-kit.tsx +14 -0
  112. package/src/internal/plate-editor/editor/plugins/toggle-base-kit.tsx +5 -0
  113. package/src/internal/plate-editor/editor/plugins/toggle-kit.tsx +9 -0
  114. package/src/internal/plate-editor/editor/transforms.ts +165 -0
  115. package/src/internal/plate-editor/editor/use-chat.ts +152 -0
  116. package/src/internal/plate-editor/hooks/index.ts +3 -0
  117. package/src/internal/plate-editor/hooks/use-copy-to-clipboard.ts +31 -0
  118. package/src/internal/plate-editor/hooks/use-is-touch-device.ts +26 -0
  119. package/src/internal/plate-editor/hooks/use-lock-scroll.ts +21 -0
  120. package/src/internal/plate-editor/hooks/use-media-query.ts +44 -0
  121. package/src/internal/plate-editor/hooks/use-mounted.ts +18 -0
  122. package/src/internal/plate-editor/hooks/use-on-click-outside.ts +114 -0
  123. package/src/internal/plate-editor/hooks/use-upload-file.ts +81 -0
  124. package/src/internal/plate-editor/i18n/context.tsx +58 -0
  125. package/src/internal/plate-editor/i18n/index.ts +3 -0
  126. package/src/internal/plate-editor/i18n/locales/de.json +57 -0
  127. package/src/internal/plate-editor/i18n/locales/el.json +57 -0
  128. package/src/internal/plate-editor/i18n/locales/en.json +57 -0
  129. package/src/internal/plate-editor/i18n/locales/es.json +57 -0
  130. package/src/internal/plate-editor/i18n/locales/fr.json +57 -0
  131. package/src/internal/plate-editor/i18n/locales/it.json +57 -0
  132. package/src/internal/plate-editor/i18n/locales/pt.json +57 -0
  133. package/src/internal/plate-editor/i18n/locales/sl.json +57 -0
  134. package/src/internal/plate-editor/i18n/locales/tr.json +57 -0
  135. package/src/internal/plate-editor/i18n/types.ts +59 -0
  136. package/src/internal/plate-editor/i18n/use-translation.ts +22 -0
  137. package/src/internal/plate-editor/index.ts +39 -0
  138. package/src/internal/plate-editor/lib/ai-output-converter.ts +153 -0
  139. package/src/internal/plate-editor/lib/download-file.ts +17 -0
  140. package/src/internal/plate-editor/plate-ui/ai-chat-editor.tsx +26 -0
  141. package/src/internal/plate-editor/plate-ui/ai-menu.tsx +828 -0
  142. package/src/internal/plate-editor/plate-ui/ai-node.tsx +41 -0
  143. package/src/internal/plate-editor/plate-ui/ai-toolbar-button.tsx +27 -0
  144. package/src/internal/plate-editor/plate-ui/alert-dialog.tsx +147 -0
  145. package/src/internal/plate-editor/plate-ui/align-toolbar-button.tsx +90 -0
  146. package/src/internal/plate-editor/plate-ui/avatar.tsx +3 -0
  147. package/src/internal/plate-editor/plate-ui/block-context-menu.tsx +106 -0
  148. package/src/internal/plate-editor/plate-ui/block-discussion.tsx +364 -0
  149. package/src/internal/plate-editor/plate-ui/block-draggable.tsx +556 -0
  150. package/src/internal/plate-editor/plate-ui/block-list-static.tsx +78 -0
  151. package/src/internal/plate-editor/plate-ui/block-list.tsx +85 -0
  152. package/src/internal/plate-editor/plate-ui/block-menu.tsx +557 -0
  153. package/src/internal/plate-editor/plate-ui/block-selection.tsx +47 -0
  154. package/src/internal/plate-editor/plate-ui/block-suggestion.tsx +469 -0
  155. package/src/internal/plate-editor/plate-ui/blockquote-node-static.tsx +10 -0
  156. package/src/internal/plate-editor/plate-ui/blockquote-node.tsx +11 -0
  157. package/src/internal/plate-editor/plate-ui/button.tsx +190 -0
  158. package/src/internal/plate-editor/plate-ui/calendar.tsx +3 -0
  159. package/src/internal/plate-editor/plate-ui/callout-node-static.tsx +76 -0
  160. package/src/internal/plate-editor/plate-ui/callout-node.tsx +54 -0
  161. package/src/internal/plate-editor/plate-ui/caption.tsx +48 -0
  162. package/src/internal/plate-editor/plate-ui/checkbox.tsx +3 -0
  163. package/src/internal/plate-editor/plate-ui/code-block-node-static.tsx +172 -0
  164. package/src/internal/plate-editor/plate-ui/code-block-node.tsx +228 -0
  165. package/src/internal/plate-editor/plate-ui/code-node-static.tsx +11 -0
  166. package/src/internal/plate-editor/plate-ui/code-node.tsx +12 -0
  167. package/src/internal/plate-editor/plate-ui/column-node-static.tsx +65 -0
  168. package/src/internal/plate-editor/plate-ui/column-node.tsx +24 -0
  169. package/src/internal/plate-editor/plate-ui/command.tsx +204 -0
  170. package/src/internal/plate-editor/plate-ui/comment-node-static.tsx +12 -0
  171. package/src/internal/plate-editor/plate-ui/comment-node.tsx +45 -0
  172. package/src/internal/plate-editor/plate-ui/comment-toolbar-button.tsx +24 -0
  173. package/src/internal/plate-editor/plate-ui/comment.tsx +619 -0
  174. package/src/internal/plate-editor/plate-ui/cursor-overlay.tsx +85 -0
  175. package/src/internal/plate-editor/plate-ui/date-node-static.tsx +43 -0
  176. package/src/internal/plate-editor/plate-ui/date-node.tsx +56 -0
  177. package/src/internal/plate-editor/plate-ui/dialog.tsx +426 -0
  178. package/src/internal/plate-editor/plate-ui/dropdown-menu.tsx +266 -0
  179. package/src/internal/plate-editor/plate-ui/editor-static.tsx +40 -0
  180. package/src/internal/plate-editor/plate-ui/editor.tsx +148 -0
  181. package/src/internal/plate-editor/plate-ui/emoji-node.tsx +48 -0
  182. package/src/internal/plate-editor/plate-ui/emoji-toolbar-button.tsx +626 -0
  183. package/src/internal/plate-editor/plate-ui/equation-node-static.tsx +155 -0
  184. package/src/internal/plate-editor/plate-ui/equation-node.tsx +226 -0
  185. package/src/internal/plate-editor/plate-ui/equation-toolbar-button.tsx +26 -0
  186. package/src/internal/plate-editor/plate-ui/export-toolbar-button.tsx +208 -0
  187. package/src/internal/plate-editor/plate-ui/fixed-toolbar-buttons.tsx +157 -0
  188. package/src/internal/plate-editor/plate-ui/fixed-toolbar.tsx +27 -0
  189. package/src/internal/plate-editor/plate-ui/floating-discussion.tsx +1129 -0
  190. package/src/internal/plate-editor/plate-ui/floating-toolbar-buttons.tsx +129 -0
  191. package/src/internal/plate-editor/plate-ui/floating-toolbar.tsx +99 -0
  192. package/src/internal/plate-editor/plate-ui/font-color-toolbar-button.tsx +211 -0
  193. package/src/internal/plate-editor/plate-ui/font-size-toolbar-button.tsx +154 -0
  194. package/src/internal/plate-editor/plate-ui/ghost-text.tsx +20 -0
  195. package/src/internal/plate-editor/plate-ui/heading-node-static.tsx +52 -0
  196. package/src/internal/plate-editor/plate-ui/heading-node.tsx +56 -0
  197. package/src/internal/plate-editor/plate-ui/highlight-node-static.tsx +9 -0
  198. package/src/internal/plate-editor/plate-ui/highlight-node.tsx +11 -0
  199. package/src/internal/plate-editor/plate-ui/history-toolbar-button.tsx +52 -0
  200. package/src/internal/plate-editor/plate-ui/hover-card.tsx +7 -0
  201. package/src/internal/plate-editor/plate-ui/hr-node-static.tsx +18 -0
  202. package/src/internal/plate-editor/plate-ui/hr-node.tsx +28 -0
  203. package/src/internal/plate-editor/plate-ui/import-toolbar-button.tsx +124 -0
  204. package/src/internal/plate-editor/plate-ui/indent-toolbar-button.tsx +34 -0
  205. package/src/internal/plate-editor/plate-ui/inline-combobox.tsx +409 -0
  206. package/src/internal/plate-editor/plate-ui/input.tsx +39 -0
  207. package/src/internal/plate-editor/plate-ui/insert-toolbar-button.tsx +260 -0
  208. package/src/internal/plate-editor/plate-ui/label.tsx +1 -0
  209. package/src/internal/plate-editor/plate-ui/line-height-toolbar-button.tsx +71 -0
  210. package/src/internal/plate-editor/plate-ui/link-node-static.tsx +15 -0
  211. package/src/internal/plate-editor/plate-ui/link-node.tsx +33 -0
  212. package/src/internal/plate-editor/plate-ui/link-toolbar-button.tsx +30 -0
  213. package/src/internal/plate-editor/plate-ui/link-toolbar.tsx +149 -0
  214. package/src/internal/plate-editor/plate-ui/list-toolbar-button.tsx +179 -0
  215. package/src/internal/plate-editor/plate-ui/mark-toolbar-button.tsx +36 -0
  216. package/src/internal/plate-editor/plate-ui/media-audio-node-static.tsx +21 -0
  217. package/src/internal/plate-editor/plate-ui/media-audio-node.tsx +32 -0
  218. package/src/internal/plate-editor/plate-ui/media-embed-node.tsx +103 -0
  219. package/src/internal/plate-editor/plate-ui/media-file-node-static.tsx +30 -0
  220. package/src/internal/plate-editor/plate-ui/media-file-node.tsx +52 -0
  221. package/src/internal/plate-editor/plate-ui/media-image-node-static.tsx +37 -0
  222. package/src/internal/plate-editor/plate-ui/media-image-node.tsx +183 -0
  223. package/src/internal/plate-editor/plate-ui/media-placeholder-node.tsx +441 -0
  224. package/src/internal/plate-editor/plate-ui/media-preview-dialog.tsx +127 -0
  225. package/src/internal/plate-editor/plate-ui/media-toolbar-button.tsx +229 -0
  226. package/src/internal/plate-editor/plate-ui/media-toolbar.tsx +216 -0
  227. package/src/internal/plate-editor/plate-ui/media-upload-toast.tsx +73 -0
  228. package/src/internal/plate-editor/plate-ui/media-video-node-static.tsx +35 -0
  229. package/src/internal/plate-editor/plate-ui/media-video-node.tsx +119 -0
  230. package/src/internal/plate-editor/plate-ui/mention-node-static.tsx +46 -0
  231. package/src/internal/plate-editor/plate-ui/mention-node.tsx +79 -0
  232. package/src/internal/plate-editor/plate-ui/menu.tsx +532 -0
  233. package/src/internal/plate-editor/plate-ui/mode-toolbar-button.tsx +126 -0
  234. package/src/internal/plate-editor/plate-ui/more-toolbar-button.tsx +34 -0
  235. package/src/internal/plate-editor/plate-ui/paragraph-node-static.tsx +15 -0
  236. package/src/internal/plate-editor/plate-ui/paragraph-node.tsx +16 -0
  237. package/src/internal/plate-editor/plate-ui/popover.tsx +77 -0
  238. package/src/internal/plate-editor/plate-ui/progress.tsx +1 -0
  239. package/src/internal/plate-editor/plate-ui/remote-cursor-overlay.tsx +81 -0
  240. package/src/internal/plate-editor/plate-ui/resize-handle.tsx +88 -0
  241. package/src/internal/plate-editor/plate-ui/separator.tsx +43 -0
  242. package/src/internal/plate-editor/plate-ui/slash-node.tsx +435 -0
  243. package/src/internal/plate-editor/plate-ui/spinner.tsx +1 -0
  244. package/src/internal/plate-editor/plate-ui/suggestion-node-static.tsx +35 -0
  245. package/src/internal/plate-editor/plate-ui/suggestion-node.tsx +168 -0
  246. package/src/internal/plate-editor/plate-ui/suggestion-toolbar-button.tsx +24 -0
  247. package/src/internal/plate-editor/plate-ui/table-node-static.tsx +85 -0
  248. package/src/internal/plate-editor/plate-ui/table-node.tsx +285 -0
  249. package/src/internal/plate-editor/plate-ui/table-toolbar-button.tsx +254 -0
  250. package/src/internal/plate-editor/plate-ui/tabs.tsx +3 -0
  251. package/src/internal/plate-editor/plate-ui/textarea.tsx +58 -0
  252. package/src/internal/plate-editor/plate-ui/toc-node-static.tsx +142 -0
  253. package/src/internal/plate-editor/plate-ui/toc-node.tsx +57 -0
  254. package/src/internal/plate-editor/plate-ui/toc-sidebar.tsx +50 -0
  255. package/src/internal/plate-editor/plate-ui/toggle-node-static.tsx +18 -0
  256. package/src/internal/plate-editor/plate-ui/toggle-node.tsx +33 -0
  257. package/src/internal/plate-editor/plate-ui/toggle-toolbar-button.tsx +26 -0
  258. package/src/internal/plate-editor/plate-ui/toggle.tsx +3 -0
  259. package/src/internal/plate-editor/plate-ui/toolbar.tsx +380 -0
  260. package/src/internal/plate-editor/plate-ui/tooltip.tsx +149 -0
  261. package/src/internal/plate-editor/plate-ui/turn-into-toolbar-button.tsx +177 -0
  262. package/src/internal/plate-editor/types/index.ts +22 -0
  263. package/src/internal/plate-editor/vite.ts +284 -0
  264. package/src/internal/sheets/components/univer-sheets.tsx +1104 -0
  265. package/src/internal/sheets/i18n/context.tsx +183 -0
  266. package/src/internal/sheets/i18n/index.ts +19 -0
  267. package/src/internal/sheets/i18n/locales/de.json +21 -0
  268. package/src/internal/sheets/i18n/locales/el.json +21 -0
  269. package/src/internal/sheets/i18n/locales/en.json +21 -0
  270. package/src/internal/sheets/i18n/locales/es.json +21 -0
  271. package/src/internal/sheets/i18n/locales/fr.json +21 -0
  272. package/src/internal/sheets/i18n/locales/it.json +21 -0
  273. package/src/internal/sheets/i18n/locales/pt.json +21 -0
  274. package/src/internal/sheets/i18n/locales/sl.json +21 -0
  275. package/src/internal/sheets/i18n/locales/tr.json +21 -0
  276. package/src/internal/sheets/i18n/types.ts +23 -0
  277. package/src/internal/sheets/i18n/use-translation.ts +17 -0
  278. package/src/internal/sheets/index.ts +14 -0
  279. package/src/internal/sheets/types/css.d.ts +11 -0
  280. package/src/internal/sheets/types/index.ts +260 -0
  281. package/src/internal/sheets/xlsx.ts +1169 -0
  282. package/src/lib/api-client.ts +77 -0
  283. package/src/lib/assistant-api-actions.ts +549 -0
  284. package/src/lib/assistant-config.tsx +71 -0
  285. package/src/lib/class-utils.ts +40 -0
  286. package/src/lib/index.ts +7 -0
  287. package/src/lib/message-utils.ts +131 -0
  288. package/src/tools/tools-schema.json +512 -0
  289. package/src/types/index.ts +235 -0
  290. package/src/views/assistant-view.tsx +1137 -0
  291. package/src/views/canvas-app.tsx +839 -0
  292. package/src/views/canvas-code.tsx +93 -0
  293. package/src/views/canvas-deep-research.tsx +44 -0
  294. package/src/views/canvas-image.tsx +25 -0
  295. package/src/views/canvas-record-view.tsx +285 -0
  296. package/src/views/canvas-spreadsheet.tsx +125 -0
  297. package/src/views/canvas-text.tsx +52 -0
  298. package/src/views/canvas.tsx +274 -0
  299. package/src/views/chat-panel.tsx +149 -0
  300. package/src/views/index.ts +20 -0
  301. package/src/views/memories-panel.tsx +365 -0
  302. package/src/views/message-list.tsx +370 -0
  303. package/src/views/project-detail.tsx +257 -0
  304. package/src/views/projects-panel.tsx +131 -0
  305. package/src/views/sessions-list.tsx +98 -0
  306. package/src/views/sidebar-content.tsx +256 -0
  307. package/src/views/work-detail.tsx +98 -0
  308. package/src/vite.ts +284 -0
@@ -0,0 +1,1169 @@
1
+ /**
2
+ * Browser-compatible XLSX ↔ Univer Converter
3
+ *
4
+ * Uses SheetJS (xlsx) for browser-side Excel file processing.
5
+ * Works in both browser and Cloudflare Workers (no Node.js dependencies).
6
+ */
7
+
8
+ import { type CellObject } from 'xlsx';
9
+
10
+ import * as XLSX from 'xlsx';
11
+
12
+ import {
13
+ type AIWorkbookOutput,
14
+ type IBorderData,
15
+ type ICellData,
16
+ type ICellDataMatrix,
17
+ type IColumnData,
18
+ type IMergeRange,
19
+ type IRowData,
20
+ type IStyleData,
21
+ type IWorkbookData,
22
+ type IWorksheetData
23
+ } from './types';
24
+
25
+ /*
26
+ * ============================================================================
27
+ * Utility Functions
28
+ * ============================================================================
29
+ */
30
+
31
+ function generateId(): string {
32
+ return Math.random().toString(36).substring(2, 15);
33
+ }
34
+
35
+ function colIndexToLetter(colIndex: number): string {
36
+ let result = '';
37
+ let index = colIndex + 1;
38
+
39
+ while (index > 0) {
40
+ const remainder = (index - 1) % 26;
41
+
42
+ result = String.fromCharCode(65 + remainder) + result;
43
+ index = Math.floor((index - 1) / 26);
44
+ }
45
+
46
+ return result;
47
+ }
48
+
49
+ function letterToColIndex(letter: string): number {
50
+ let col = 0;
51
+
52
+ for (let i = 0; i < letter.length; i++) {
53
+ col = col * 26 + (letter.charCodeAt(i) - 64);
54
+ }
55
+
56
+ return col - 1;
57
+ }
58
+
59
+ function cellRefToRowCol(ref: string): { row: number; col: number } {
60
+ const match = ref.match(/^([A-Z]+)(\d+)$/);
61
+
62
+ if (!match) return { row: 0, col: 0 };
63
+
64
+ return {
65
+ row: parseInt(match[2], 10) - 1,
66
+ col: letterToColIndex(match[1])
67
+ };
68
+ }
69
+
70
+ function rangeToMerge(range: string): IMergeRange | null {
71
+ const [start, end] = range.split(':');
72
+
73
+ if (!start) return null;
74
+
75
+ const startPos = cellRefToRowCol(start);
76
+ const endPos = end ? cellRefToRowCol(end) : startPos;
77
+
78
+ return {
79
+ startRow: startPos.row,
80
+ startColumn: startPos.col,
81
+ endRow: endPos.row,
82
+ endColumn: endPos.col
83
+ };
84
+ }
85
+
86
+ // SheetJS color type
87
+ interface XLSXColor {
88
+ rgb?: string;
89
+ theme?: number;
90
+ tint?: number;
91
+ }
92
+
93
+ // SheetJS style to hex color
94
+ function styleColorToHex(color: XLSXColor | undefined): string | undefined {
95
+ if (!color) return undefined;
96
+
97
+ if (color.rgb) {
98
+ // RGB format: RRGGBB
99
+ return `#${color.rgb}`;
100
+ }
101
+ if (color.theme !== undefined) {
102
+ // Theme colors - return a default, actual theme resolution needs workbook context
103
+ const themeColors = [
104
+ '#000000',
105
+ '#FFFFFF',
106
+ '#1F4E79',
107
+ '#2E75B6',
108
+ '#5B9BD5',
109
+ '#ED7D31',
110
+ '#70AD47',
111
+ '#FFC000'
112
+ ];
113
+
114
+ return themeColors[color.theme] || '#000000';
115
+ }
116
+
117
+ return undefined;
118
+ }
119
+
120
+ function convertBorderStyle(style: string | undefined): number {
121
+ const styleMap: Record<string, number> = {
122
+ thin: 1,
123
+ medium: 2,
124
+ thick: 3,
125
+ dotted: 4,
126
+ dashed: 5,
127
+ double: 6,
128
+ hair: 7,
129
+ mediumDashed: 8,
130
+ dashDot: 9,
131
+ mediumDashDot: 10,
132
+ dashDotDot: 11,
133
+ mediumDashDotDot: 12,
134
+ slantDashDot: 13
135
+ };
136
+
137
+ return styleMap[style || 'thin'] || 1;
138
+ }
139
+
140
+ function convertHAlign(align: string | undefined): 1 | 2 | 3 | undefined {
141
+ const map: Record<string, 1 | 2 | 3> = { left: 1, center: 2, right: 3 };
142
+
143
+ return align ? map[align] : undefined;
144
+ }
145
+
146
+ function convertVAlign(align: string | undefined): 1 | 2 | 3 | undefined {
147
+ const map: Record<string, 1 | 2 | 3> = { top: 1, center: 2, bottom: 3 };
148
+
149
+ return align ? map[align] : undefined;
150
+ }
151
+
152
+ /*
153
+ * ============================================================================
154
+ * XLSX → Univer Converter
155
+ * ============================================================================
156
+ */
157
+
158
+ export interface XlsxToUniverOptions {
159
+ workbookId?: string;
160
+ workbookName?: string;
161
+ }
162
+
163
+ export function xlsxToUniver(data: ArrayBuffer | Uint8Array, options: XlsxToUniverOptions = {}): IWorkbookData {
164
+ const workbook = XLSX.read(data, {
165
+ type: 'array',
166
+ cellStyles: true,
167
+ cellFormula: true,
168
+ cellDates: true,
169
+ cellNF: true
170
+ });
171
+
172
+ const workbookId = options.workbookId || generateId();
173
+ const workbookName = options.workbookName || workbook.Props?.Title || 'Workbook';
174
+
175
+ const sheetOrder: string[] = [];
176
+ const sheets: Record<string, IWorksheetData> = {};
177
+ const styles: Record<string, IStyleData> = {};
178
+ const styleCache = new Map<string, string>();
179
+
180
+ for (const sheetName of workbook.SheetNames) {
181
+ const ws = workbook.Sheets[sheetName];
182
+
183
+ if (!ws) continue;
184
+
185
+ const sheetId = generateId();
186
+
187
+ sheetOrder.push(sheetId);
188
+
189
+ // Get sheet range
190
+ const range = ws['!ref'] ? XLSX.utils.decode_range(ws['!ref']) : { s: { r: 0, c: 0 }, e: { r: 999, c: 25 } };
191
+ const rowCount = Math.max(range.e.r + 1, 1000);
192
+ const columnCount = Math.max(range.e.c + 1, 26);
193
+
194
+ const cellData: ICellDataMatrix = {};
195
+ const rowData: Record<number, IRowData> = {};
196
+ const columnData: Record<number, IColumnData> = {};
197
+ const mergeData: IMergeRange[] = [];
198
+
199
+ // Process cells
200
+ for (let { r } = range.s; r <= range.e.r; r++) {
201
+ for (let { c } = range.s; c <= range.e.c; c++) {
202
+ const cellAddress = XLSX.utils.encode_cell({ r, c });
203
+ const cell = ws[cellAddress] as CellObject | undefined;
204
+
205
+ if (!cell) continue;
206
+
207
+ if (!cellData[r]) {
208
+ cellData[r] = {};
209
+ }
210
+
211
+ const cellObj: ICellData = {};
212
+
213
+ // Value and type
214
+ if (cell.f) {
215
+ // Formula
216
+ cellObj.f = cell.f;
217
+ if (cell.v !== undefined) {
218
+ cellObj.v = cell.v as string | number | boolean;
219
+ }
220
+ } else if (cell.v !== undefined) {
221
+ cellObj.v = cell.v as string | number | boolean;
222
+ }
223
+
224
+ // Cell type
225
+ switch (cell.t) {
226
+ case 's':
227
+ cellObj.t = 1;
228
+ break; // STRING
229
+
230
+ case 'n':
231
+ cellObj.t = 2;
232
+ break; // NUMBER
233
+
234
+ case 'b':
235
+ cellObj.t = 3;
236
+ break; // BOOLEAN
237
+
238
+ case 'd':
239
+ cellObj.t = 2;
240
+ break; // DATE (stored as number)
241
+
242
+ default:
243
+ cellObj.t = 1;
244
+ }
245
+
246
+ // Style
247
+ if (cell.s) {
248
+ const style: IStyleData = {};
249
+ let hasStyle = false;
250
+
251
+ // Font
252
+ if (cell.s.font) {
253
+ const { font } = cell.s;
254
+
255
+ if (font.name) {
256
+ style.ff = font.name;
257
+ hasStyle = true;
258
+ }
259
+ if (font.sz) {
260
+ style.fs = font.sz;
261
+ hasStyle = true;
262
+ }
263
+ if (font.bold) {
264
+ style.bl = 1;
265
+ hasStyle = true;
266
+ }
267
+ if (font.italic) {
268
+ style.it = 1;
269
+ hasStyle = true;
270
+ }
271
+ if (font.underline) {
272
+ style.ul = { s: 1 };
273
+ hasStyle = true;
274
+ }
275
+ if (font.strike) {
276
+ style.st = { s: 1 };
277
+ hasStyle = true;
278
+ }
279
+ if (font.color) {
280
+ const rgb = styleColorToHex(font.color);
281
+
282
+ if (rgb) {
283
+ style.cl = { rgb };
284
+ hasStyle = true;
285
+ }
286
+ }
287
+ }
288
+
289
+ // Fill
290
+ if (cell.s.fill?.fgColor) {
291
+ const rgb = styleColorToHex(cell.s.fill.fgColor);
292
+
293
+ if (rgb) {
294
+ style.bg = { rgb };
295
+ hasStyle = true;
296
+ }
297
+ }
298
+
299
+ // Alignment
300
+ if (cell.s.alignment) {
301
+ const align = cell.s.alignment;
302
+ const ht = convertHAlign(align.horizontal);
303
+ const vt = convertVAlign(align.vertical);
304
+
305
+ if (ht) {
306
+ style.ht = ht;
307
+ hasStyle = true;
308
+ }
309
+ if (vt) {
310
+ style.vt = vt;
311
+ hasStyle = true;
312
+ }
313
+ if (align.wrapText) {
314
+ style.tb = 2;
315
+ hasStyle = true;
316
+ }
317
+ if (align.textRotation) {
318
+ style.tr = { a: align.textRotation };
319
+ hasStyle = true;
320
+ }
321
+ }
322
+
323
+ // Border
324
+ if (cell.s.border) {
325
+ const bd: IBorderData = {};
326
+ let hasBorder = false;
327
+
328
+ for (const side of [
329
+ 'top',
330
+ 'bottom',
331
+ 'left',
332
+ 'right'
333
+ ] as const) {
334
+ const b = cell.s.border[side];
335
+
336
+ if (b?.style) {
337
+ const key = side === 'top' ? 't' : side === 'bottom' ? 'b' : side === 'left' ? 'l' : 'r';
338
+
339
+ bd[key] = {
340
+ s: convertBorderStyle(b.style),
341
+ cl: { rgb: styleColorToHex(b.color) || '#000000' }
342
+ };
343
+ hasBorder = true;
344
+ }
345
+ }
346
+
347
+ if (hasBorder) {
348
+ style.bd = bd;
349
+ hasStyle = true;
350
+ }
351
+ }
352
+
353
+ // Number format
354
+ if (cell.z && cell.z !== 'General') {
355
+ style.n = { pattern: String(cell.z) };
356
+ hasStyle = true;
357
+ }
358
+
359
+ if (hasStyle) {
360
+ const styleJson = JSON.stringify(style);
361
+ let styleId = styleCache.get(styleJson);
362
+
363
+ if (!styleId) {
364
+ styleId = generateId();
365
+ styleCache.set(styleJson, styleId);
366
+ styles[styleId] = style;
367
+ }
368
+ cellObj.s = styleId;
369
+ }
370
+ }
371
+
372
+ // Hyperlink
373
+ if (cell.l) {
374
+ cellObj.custom = { hyperlink: cell.l.Target };
375
+ }
376
+
377
+ if (Object.keys(cellObj).length > 0) {
378
+ cellData[r][c] = cellObj;
379
+ }
380
+ }
381
+ }
382
+
383
+ // Row heights
384
+ if (ws['!rows']) {
385
+ ws['!rows'].forEach((row, idx) => {
386
+ if (row) {
387
+ const rd: IRowData = {};
388
+
389
+ if (row.hpt) rd.h = row.hpt;
390
+ if (row.hidden) rd.hd = 1;
391
+ if (Object.keys(rd).length > 0) {
392
+ rowData[idx] = rd;
393
+ }
394
+ }
395
+ });
396
+ }
397
+
398
+ // Column widths
399
+ if (ws['!cols']) {
400
+ ws['!cols'].forEach((col, idx) => {
401
+ if (col) {
402
+ const cd: IColumnData = {};
403
+
404
+ if (col.wpx) cd.w = col.wpx;
405
+ else if (col.wch) cd.w = col.wch * 7; // Approximate pixels
406
+ if (col.hidden) cd.hd = 1;
407
+ if (Object.keys(cd).length > 0) {
408
+ columnData[idx] = cd;
409
+ }
410
+ }
411
+ });
412
+ }
413
+
414
+ // Merged cells
415
+ if (ws['!merges']) {
416
+ for (const merge of ws['!merges']) {
417
+ mergeData.push({
418
+ startRow: merge.s.r,
419
+ startColumn: merge.s.c,
420
+ endRow: merge.e.r,
421
+ endColumn: merge.e.c
422
+ });
423
+ }
424
+ }
425
+
426
+ // Build sheet
427
+ const sheetData: IWorksheetData = {
428
+ id: sheetId,
429
+ name: sheetName,
430
+ rowCount,
431
+ columnCount,
432
+ defaultColumnWidth: 88,
433
+ defaultRowHeight: 24,
434
+ cellData
435
+ };
436
+
437
+ if (Object.keys(rowData).length > 0) sheetData.rowData = rowData;
438
+ if (Object.keys(columnData).length > 0) sheetData.columnData = columnData;
439
+ if (mergeData.length > 0) sheetData.mergeData = mergeData;
440
+
441
+ // Freeze panes
442
+ if (ws['!freeze']) {
443
+ const freeze = ws['!freeze'];
444
+
445
+ sheetData.freeze = {
446
+ xSplit: freeze.xSplit || 0,
447
+ ySplit: freeze.ySplit || 0,
448
+ startRow: freeze.ySplit || 0,
449
+ startColumn: freeze.xSplit || 0
450
+ };
451
+ }
452
+
453
+ // Auto filter
454
+ if (ws['!autofilter']) {
455
+ const filterRef = ws['!autofilter'].ref;
456
+ const filterRange = rangeToMerge(filterRef);
457
+
458
+ if (filterRange) {
459
+ sheetData.autoFilter = { ref: filterRange };
460
+ }
461
+ }
462
+
463
+ // Data validations (if available)
464
+ if ((ws as any)['!dataValidations']) {
465
+ const dvs = (ws as any)['!dataValidations'] as any[];
466
+
467
+ sheetData.dataValidations = dvs.map(dv => ({
468
+ uid: generateId(),
469
+ type: dv.type === 'list' ? 'list' : 'custom',
470
+ ranges: dv.sqref ? (dv.sqref.split(' ').map(rangeToMerge).filter(Boolean) as IMergeRange[]) : [],
471
+ formula1: dv.formula1,
472
+ formula2: dv.formula2,
473
+ allowBlank: dv.allowBlank,
474
+ showErrorMessage: dv.showErrorMessage,
475
+ showInputMessage: dv.showInputMessage,
476
+ promptTitle: dv.promptTitle,
477
+ prompt: dv.prompt,
478
+ errorTitle: dv.errorTitle,
479
+ error: dv.error
480
+ }));
481
+ }
482
+
483
+ sheets[sheetId] = sheetData;
484
+ }
485
+
486
+ return {
487
+ id: workbookId,
488
+ name: workbookName,
489
+ locale: 'EN_US',
490
+ sheetOrder,
491
+ sheets,
492
+ styles: Object.keys(styles).length > 0 ? styles : undefined
493
+ };
494
+ }
495
+
496
+ /*
497
+ * ============================================================================
498
+ * Univer → XLSX Converter
499
+ * ============================================================================
500
+ */
501
+
502
+ export function univerToXlsx(data: IWorkbookData): ArrayBuffer {
503
+ const workbook = XLSX.utils.book_new();
504
+
505
+ // Set workbook properties
506
+ workbook.Props = {
507
+ Title: data.name,
508
+ CreatedDate: new Date()
509
+ };
510
+
511
+ for (const sheetId of data.sheetOrder) {
512
+ const sheetData = data.sheets[sheetId];
513
+
514
+ if (!sheetData) continue;
515
+
516
+ // Build AOA (Array of Arrays) for better compatibility
517
+ const aoa: (string | number | boolean | null | undefined)[][] = [];
518
+
519
+ // Find actual data bounds
520
+ let maxRow = 0;
521
+ let maxCol = 0;
522
+
523
+ if (sheetData.cellData) {
524
+ for (const rowStr of Object.keys(sheetData.cellData)) {
525
+ const r = parseInt(rowStr, 10);
526
+
527
+ maxRow = Math.max(maxRow, r);
528
+ const rowCells = sheetData.cellData[r];
529
+
530
+ for (const colStr of Object.keys(rowCells)) {
531
+ const c = parseInt(colStr, 10);
532
+
533
+ maxCol = Math.max(maxCol, c);
534
+ }
535
+ }
536
+ }
537
+
538
+ // Initialize AOA with proper dimensions
539
+ for (let r = 0; r <= maxRow; r++) {
540
+ aoa[r] = new Array(maxCol + 1).fill(null);
541
+ }
542
+
543
+ // Fill AOA with values
544
+ if (sheetData.cellData) {
545
+ for (const rowStr of Object.keys(sheetData.cellData)) {
546
+ const r = parseInt(rowStr, 10);
547
+ const rowCells = sheetData.cellData[r];
548
+
549
+ for (const colStr of Object.keys(rowCells)) {
550
+ const c = parseInt(colStr, 10);
551
+ const cellData = rowCells[c];
552
+
553
+ // Get value (prefer calculated value over formula for simple export)
554
+ if (cellData.v !== undefined && cellData.v !== null) {
555
+ aoa[r][c] = cellData.v;
556
+ } else if (cellData.f) {
557
+ // For formulas without cached value, store the formula result placeholder
558
+ aoa[r][c] = null;
559
+ }
560
+ }
561
+ }
562
+ }
563
+
564
+ // Create worksheet from AOA
565
+ const ws = XLSX.utils.aoa_to_sheet(aoa);
566
+
567
+ // Now add formulas and number formats to cells
568
+ if (sheetData.cellData) {
569
+ for (const rowStr of Object.keys(sheetData.cellData)) {
570
+ const r = parseInt(rowStr, 10);
571
+ const rowCells = sheetData.cellData[r];
572
+
573
+ for (const colStr of Object.keys(rowCells)) {
574
+ const c = parseInt(colStr, 10);
575
+ const cellData = rowCells[c];
576
+ const cellAddress = XLSX.utils.encode_cell({ r, c });
577
+
578
+ // Ensure cell exists
579
+ if (!ws[cellAddress]) {
580
+ ws[cellAddress] = { t: 's', v: '' };
581
+ }
582
+
583
+ const cell = ws[cellAddress] as CellObject;
584
+
585
+ // Add formula
586
+ if (cellData.f) {
587
+ cell.f = cellData.f;
588
+ }
589
+
590
+ // Add number format from style
591
+ const styleData
592
+ = typeof cellData.s === 'string' ? data.styles?.[cellData.s] : (cellData.s as IStyleData | undefined);
593
+
594
+ if (styleData?.n?.pattern) {
595
+ // Convert Univer pattern to Excel format if needed
596
+ cell.z = convertNumberFormatPattern(styleData.n.pattern);
597
+ }
598
+
599
+ // Add hyperlink
600
+ if (cellData.custom?.hyperlink) {
601
+ cell.l = { Target: cellData.custom.hyperlink as string };
602
+ }
603
+ }
604
+ }
605
+ }
606
+
607
+ // Row heights
608
+ if (sheetData.rowData) {
609
+ ws['!rows'] = [];
610
+ for (const rowStr of Object.keys(sheetData.rowData)) {
611
+ const r = parseInt(rowStr, 10);
612
+ const rd = sheetData.rowData[r];
613
+
614
+ if (rd.h || rd.hd) {
615
+ ws['!rows'][r] = {};
616
+ if (rd.h) ws['!rows'][r].hpt = rd.h;
617
+ if (rd.hd === 1) ws['!rows'][r].hidden = true;
618
+ }
619
+ }
620
+ }
621
+
622
+ // Column widths
623
+ if (sheetData.columnData) {
624
+ ws['!cols'] = [];
625
+ for (const colStr of Object.keys(sheetData.columnData)) {
626
+ const c = parseInt(colStr, 10);
627
+ const cd = sheetData.columnData[c];
628
+
629
+ if (cd.w || cd.hd) {
630
+ ws['!cols'][c] = {};
631
+ if (cd.w) ws['!cols'][c].wpx = cd.w;
632
+ if (cd.hd === 1) ws['!cols'][c].hidden = true;
633
+ }
634
+ }
635
+ }
636
+
637
+ // Merged cells
638
+ if (sheetData.mergeData && sheetData.mergeData.length > 0) {
639
+ ws['!merges'] = sheetData.mergeData.map(m => ({
640
+ s: { r: m.startRow, c: m.startColumn },
641
+ e: { r: m.endRow, c: m.endColumn }
642
+ }));
643
+ }
644
+
645
+ // Auto filter
646
+ if (sheetData.autoFilter) {
647
+ const { ref } = sheetData.autoFilter;
648
+
649
+ ws['!autofilter'] = {
650
+ ref: `${colIndexToLetter(ref.startColumn)}${ref.startRow + 1}:${colIndexToLetter(ref.endColumn)}${ref.endRow + 1}`
651
+ };
652
+ }
653
+
654
+ XLSX.utils.book_append_sheet(workbook, ws, sheetData.name);
655
+ }
656
+
657
+ // Generate buffer - Note: cellStyles requires SheetJS Pro, so we don't use it
658
+ const buffer = XLSX.write(workbook, {
659
+ type: 'array',
660
+ bookType: 'xlsx',
661
+ compression: true
662
+ });
663
+
664
+ return buffer;
665
+ }
666
+
667
+ /**
668
+ * Convert Univer number format pattern to Excel-compatible format
669
+ */
670
+ function convertNumberFormatPattern(pattern: string): string {
671
+ /*
672
+ * Most patterns should work directly, but some need conversion
673
+ * Remove any locale-specific prefixes like [$-804]
674
+ */
675
+ let result = pattern;
676
+
677
+ // Remove Chinese locale prefix if present
678
+ result = result.replace(/\[\$-[0-9A-Fa-f]+\]/g, '');
679
+
680
+ // Convert common Univer patterns to Excel patterns
681
+ const patternMap: Record<string, string> = {
682
+ // Date patterns - ensure Excel compatibility
683
+ 'yyyy/m/d': 'yyyy/m/d',
684
+ 'yyyy-mm-dd': 'yyyy-mm-dd',
685
+ 'dd/mm/yyyy': 'dd/mm/yyyy',
686
+ 'dd.mm.yyyy': 'dd.mm.yyyy',
687
+ 'm/d/yyyy': 'm/d/yyyy',
688
+ // Time patterns
689
+ 'h:mm:ss': 'h:mm:ss',
690
+ 'hh:mm:ss': 'hh:mm:ss',
691
+ 'h:mm': 'h:mm',
692
+ 'hh:mm': 'hh:mm',
693
+ // Percentage
694
+ '0%': '0%',
695
+ '0.00%': '0.00%'
696
+ };
697
+
698
+ return patternMap[result.toLowerCase()] || result;
699
+ }
700
+
701
+ /*
702
+ * ============================================================================
703
+ * CSV Converters
704
+ * ============================================================================
705
+ */
706
+
707
+ export function csvToUniver(
708
+ csvContent: string,
709
+ options: {
710
+ delimiter?: string;
711
+ sheetName?: string;
712
+ workbookName?: string;
713
+ firstRowIsHeader?: boolean;
714
+ } = {}
715
+ ): IWorkbookData {
716
+ const {
717
+ delimiter = ',', sheetName = 'Sheet1', workbookName = 'CSV Import', firstRowIsHeader = false
718
+ } = options;
719
+
720
+ const workbook = XLSX.read(csvContent, {
721
+ type: 'string',
722
+ FS: delimiter
723
+ });
724
+
725
+ // Convert using xlsx parser, then transform
726
+ const ws = workbook.Sheets[workbook.SheetNames[0]];
727
+
728
+ if (!ws) {
729
+ return {
730
+ id: generateId(),
731
+ name: workbookName,
732
+ locale: 'EN_US',
733
+ sheetOrder: [generateId()],
734
+ sheets: {
735
+ [generateId()]: {
736
+ id: generateId(),
737
+ name: sheetName,
738
+ rowCount: 1000,
739
+ columnCount: 26,
740
+ cellData: {}
741
+ }
742
+ }
743
+ };
744
+ }
745
+
746
+ // Use xlsxToUniver with proper array buffer
747
+ const buffer = XLSX.write(workbook, { type: 'array', bookType: 'xlsx' });
748
+ const result = xlsxToUniver(buffer, { workbookName });
749
+
750
+ // Apply header styling if needed
751
+ if (firstRowIsHeader && result.sheetOrder[0]) {
752
+ const sheet = result.sheets[result.sheetOrder[0]];
753
+
754
+ if (sheet.cellData[0]) {
755
+ const headerStyle: IStyleData = {
756
+ bl: 1,
757
+ bg: { rgb: '#F3F4F6' }
758
+ };
759
+ const styleId = generateId();
760
+
761
+ if (!result.styles) result.styles = {};
762
+ result.styles[styleId] = headerStyle;
763
+
764
+ for (const colKey of Object.keys(sheet.cellData[0])) {
765
+ const cell = sheet.cellData[0][parseInt(colKey, 10)];
766
+
767
+ if (cell) {
768
+ cell.s = styleId;
769
+ }
770
+ }
771
+
772
+ // Add freeze
773
+ sheet.freeze = {
774
+ xSplit: 0,
775
+ ySplit: 1,
776
+ startRow: 1,
777
+ startColumn: 0
778
+ };
779
+ }
780
+ }
781
+
782
+ return result;
783
+ }
784
+
785
+ export function univerToCsv(data: IWorkbookData, sheetId?: string): string {
786
+ const targetSheetId = sheetId || data.sheetOrder[0];
787
+ const sheetData = data.sheets[targetSheetId];
788
+
789
+ if (!sheetData?.cellData) {
790
+ return '';
791
+ }
792
+
793
+ const rows: string[][] = [];
794
+
795
+ // Find bounds
796
+ let maxRow = 0;
797
+ let maxCol = 0;
798
+
799
+ for (const rowStr of Object.keys(sheetData.cellData)) {
800
+ const r = parseInt(rowStr, 10);
801
+
802
+ maxRow = Math.max(maxRow, r);
803
+ for (const colStr of Object.keys(sheetData.cellData[r])) {
804
+ maxCol = Math.max(maxCol, parseInt(colStr, 10));
805
+ }
806
+ }
807
+
808
+ for (let r = 0; r <= maxRow; r++) {
809
+ const row: string[] = [];
810
+
811
+ for (let c = 0; c <= maxCol; c++) {
812
+ const cell = sheetData.cellData[r]?.[c];
813
+ let value = '';
814
+
815
+ if (cell) {
816
+ if (cell.p?.body?.dataStream) {
817
+ value = cell.p.body.dataStream.replace(/\r\n$/, '');
818
+ } else if (cell.v !== undefined) {
819
+ value = String(cell.v);
820
+ }
821
+ }
822
+
823
+ // Escape for CSV
824
+ if (value.includes(',') || value.includes('"') || value.includes('\n')) {
825
+ value = `"${value.replace(/"/g, '""')}"`;
826
+ }
827
+
828
+ row.push(value);
829
+ }
830
+ rows.push(row);
831
+ }
832
+
833
+ return rows.map(r => r.join(',')).join('\n');
834
+ }
835
+
836
+ /*
837
+ * ============================================================================
838
+ * File Helper Functions (Browser)
839
+ * ============================================================================
840
+ */
841
+
842
+ export async function fileToUniver(file: File): Promise<IWorkbookData> {
843
+ const buffer = await file.arrayBuffer();
844
+ const ext = file.name.split('.').pop()?.toLowerCase();
845
+
846
+ if (ext === 'csv') {
847
+ const text = new TextDecoder().decode(buffer);
848
+
849
+ return csvToUniver(text, {
850
+ workbookName: file.name.replace(/\.[^.]+$/, '')
851
+ });
852
+ }
853
+
854
+ return xlsxToUniver(buffer, {
855
+ workbookName: file.name.replace(/\.[^.]+$/, '')
856
+ });
857
+ }
858
+
859
+ export function univerToBlob(data: IWorkbookData, format: 'xlsx' | 'csv' = 'xlsx'): Blob {
860
+ if (format === 'csv') {
861
+ const csv = univerToCsv(data);
862
+
863
+ return new Blob([csv], { type: 'text/csv;charset=utf-8;' });
864
+ }
865
+
866
+ const buffer = univerToXlsx(data);
867
+
868
+ return new Blob([buffer], {
869
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
870
+ });
871
+ }
872
+
873
+ export function downloadWorkbook(data: IWorkbookData, filename: string, format: 'xlsx' | 'csv' = 'xlsx'): void {
874
+ const blob = univerToBlob(data, format);
875
+ const url = URL.createObjectURL(blob);
876
+ const link = document.createElement('a');
877
+
878
+ link.href = url;
879
+ link.download = filename.endsWith(`.${format}`) ? filename : `${filename}.${format}`;
880
+ document.body.appendChild(link);
881
+ link.click();
882
+ document.body.removeChild(link);
883
+ URL.revokeObjectURL(url);
884
+ }
885
+
886
+ /*
887
+ * ============================================================================
888
+ * AI Output → Univer Native Format Converter
889
+ * ============================================================================
890
+ */
891
+
892
+ /**
893
+ * Convert AI output (array-based format) to Univer native format (sparse matrix)
894
+ *
895
+ * @param aiOutput - The AI-generated workbook data in array-based format
896
+ * @returns Univer-compatible IWorkbookData in sparse matrix format
897
+ */
898
+ export function aiOutputToUniver(aiOutput: AIWorkbookOutput): IWorkbookData {
899
+ // Support both 'workbook' (new) and 'workbookData' (legacy) keys
900
+ const workbookData = aiOutput.workbook || aiOutput.workbookData;
901
+
902
+ if (!workbookData) {
903
+ throw new Error('Missing workbook data in AI output');
904
+ }
905
+
906
+ // Validate sheets array exists and is not empty
907
+ if (!workbookData.sheets || !Array.isArray(workbookData.sheets) || workbookData.sheets.length === 0) {
908
+ console.warn('[aiOutputToUniver] Missing or empty sheets array, creating default sheet');
909
+
910
+ // Create a minimal valid workbook with empty sheet
911
+ return {
912
+ id: workbookData.id || 'workbook-1',
913
+ name: workbookData.name || 'Untitled Workbook',
914
+ sheetOrder: ['sheet-1'],
915
+ sheets: {
916
+ 'sheet-1': {
917
+ id: 'sheet-1',
918
+ name: 'Sheet1',
919
+ rowCount: 100,
920
+ columnCount: 20,
921
+ cellData: {}
922
+ }
923
+ }
924
+ };
925
+ }
926
+
927
+ // Convert sheets array to object map
928
+ const sheets: Record<string, IWorksheetData> = {};
929
+
930
+ for (const sheetEntry of workbookData.sheets) {
931
+ // Skip null/undefined entries (malformed AI output)
932
+ if (!sheetEntry || !sheetEntry.sheet) {
933
+ console.warn('[aiOutputToUniver] Skipping invalid sheet entry:', sheetEntry);
934
+ continue;
935
+ }
936
+ const aiSheet = sheetEntry.sheet;
937
+
938
+ // Convert 2D array cellData to sparse matrix
939
+ const cellData: ICellDataMatrix = {};
940
+
941
+ if (aiSheet.cellData) {
942
+ for (let rowIndex = 0; rowIndex < aiSheet.cellData.length; rowIndex++) {
943
+ const row = aiSheet.cellData[rowIndex];
944
+
945
+ if (!row) continue;
946
+
947
+ for (let colIndex = 0; colIndex < row.length; colIndex++) {
948
+ const cell = row[colIndex];
949
+
950
+ if (!cell) continue;
951
+
952
+ // Initialize row if needed
953
+ if (!cellData[rowIndex]) {
954
+ cellData[rowIndex] = {};
955
+ }
956
+
957
+ // Copy cell data, removing null values
958
+ const cleanCell: ICellData = {};
959
+
960
+ if (cell.v !== undefined && cell.v !== null) cleanCell.v = cell.v;
961
+ if (cell.t !== undefined && cell.t !== null) cleanCell.t = cell.t;
962
+ if (cell.f !== undefined && cell.f !== null) cleanCell.f = cell.f;
963
+ if (cell.s !== undefined && cell.s !== null) cleanCell.s = cell.s;
964
+
965
+ if (Object.keys(cleanCell).length > 0) {
966
+ cellData[rowIndex][colIndex] = cleanCell;
967
+ }
968
+ }
969
+ }
970
+ }
971
+
972
+ // Convert rowData array to object map
973
+ let rowData: Record<number, IRowData> | undefined;
974
+
975
+ if (aiSheet.rowData && aiSheet.rowData.length > 0) {
976
+ rowData = {};
977
+ for (const rd of aiSheet.rowData) {
978
+ const rowEntry: IRowData = {};
979
+
980
+ if (rd.h !== undefined && rd.h !== null) rowEntry.h = rd.h;
981
+ if (rd.hd !== undefined && rd.hd !== null) rowEntry.hd = rd.hd;
982
+ if (Object.keys(rowEntry).length > 0) {
983
+ rowData[rd.row] = rowEntry;
984
+ }
985
+ }
986
+ }
987
+
988
+ // Convert columnData array to object map
989
+ let columnData: Record<number, IColumnData> | undefined;
990
+
991
+ if (aiSheet.columnData && aiSheet.columnData.length > 0) {
992
+ columnData = {};
993
+ for (const cd of aiSheet.columnData) {
994
+ const colEntry: IColumnData = {};
995
+
996
+ if (cd.w !== undefined && cd.w !== null) colEntry.w = cd.w;
997
+ if (cd.hd !== undefined && cd.hd !== null) colEntry.hd = cd.hd;
998
+ if (Object.keys(colEntry).length > 0) {
999
+ columnData[cd.col] = colEntry;
1000
+ }
1001
+ }
1002
+ }
1003
+
1004
+ // Build sheet data
1005
+ const sheetData: IWorksheetData = {
1006
+ id: aiSheet.id,
1007
+ name: aiSheet.name,
1008
+ rowCount: aiSheet.rowCount,
1009
+ columnCount: aiSheet.columnCount,
1010
+ cellData
1011
+ };
1012
+
1013
+ // Optional properties
1014
+ if (aiSheet.tabColor) sheetData.tabColor = aiSheet.tabColor;
1015
+ if (aiSheet.hidden !== undefined && aiSheet.hidden !== null) sheetData.hidden = aiSheet.hidden;
1016
+ if (aiSheet.defaultColumnWidth !== undefined && aiSheet.defaultColumnWidth !== null) {
1017
+ sheetData.defaultColumnWidth = aiSheet.defaultColumnWidth;
1018
+ }
1019
+ if (aiSheet.defaultRowHeight !== undefined && aiSheet.defaultRowHeight !== null) {
1020
+ sheetData.defaultRowHeight = aiSheet.defaultRowHeight;
1021
+ }
1022
+ if (aiSheet.showGridlines !== undefined && aiSheet.showGridlines !== null) {
1023
+ sheetData.showGridlines = aiSheet.showGridlines;
1024
+ }
1025
+ if (aiSheet.freeze) sheetData.freeze = aiSheet.freeze;
1026
+ if (rowData && Object.keys(rowData).length > 0) sheetData.rowData = rowData;
1027
+ if (columnData && Object.keys(columnData).length > 0) sheetData.columnData = columnData;
1028
+ if (aiSheet.mergeData && aiSheet.mergeData.length > 0) sheetData.mergeData = aiSheet.mergeData;
1029
+ if (aiSheet.dataValidations && aiSheet.dataValidations.length > 0) {
1030
+ sheetData.dataValidations = aiSheet.dataValidations;
1031
+ }
1032
+ if (aiSheet.conditionalFormattingRules && aiSheet.conditionalFormattingRules.length > 0) {
1033
+ sheetData.conditionalFormattingRules = aiSheet.conditionalFormattingRules;
1034
+ }
1035
+ if (aiSheet.autoFilter) sheetData.autoFilter = aiSheet.autoFilter;
1036
+
1037
+ sheets[sheetEntry.id] = sheetData;
1038
+ }
1039
+
1040
+ // Ensure we have at least one valid sheet after filtering
1041
+ if (Object.keys(sheets).length === 0) {
1042
+ console.warn('[aiOutputToUniver] No valid sheets after filtering, creating default sheet');
1043
+ sheets['sheet-1'] = {
1044
+ id: 'sheet-1',
1045
+ name: 'Sheet1',
1046
+ rowCount: 100,
1047
+ columnCount: 20,
1048
+ cellData: {}
1049
+ };
1050
+ // Fix sheetOrder if empty
1051
+ if (!workbookData.sheetOrder || workbookData.sheetOrder.length === 0) {
1052
+ workbookData.sheetOrder = ['sheet-1'];
1053
+ }
1054
+ }
1055
+
1056
+ // Convert styles array to object map (filter out null entries from malformed AI output)
1057
+ let styles: Record<string, IStyleData> | undefined;
1058
+
1059
+ if (workbookData.styles && Array.isArray(workbookData.styles) && workbookData.styles.length > 0) {
1060
+ styles = {};
1061
+ for (const styleEntry of workbookData.styles) {
1062
+ // Skip null/undefined entries (malformed AI output often has [null, null, ...])
1063
+ if (!styleEntry || !styleEntry.id || !styleEntry.style) {
1064
+ continue;
1065
+ }
1066
+ styles[styleEntry.id] = styleEntry.style;
1067
+ }
1068
+ // If all entries were null, set styles to undefined
1069
+ if (Object.keys(styles).length === 0) {
1070
+ styles = undefined;
1071
+ }
1072
+ }
1073
+
1074
+ // Ensure sheetOrder is valid and contains actual sheet IDs
1075
+ const validSheetIds = Object.keys(sheets);
1076
+ let { sheetOrder } = workbookData;
1077
+
1078
+ if (!sheetOrder || !Array.isArray(sheetOrder) || sheetOrder.length === 0) {
1079
+ sheetOrder = validSheetIds;
1080
+ } else {
1081
+ // Filter sheetOrder to only include valid sheet IDs
1082
+ sheetOrder = sheetOrder.filter(id => validSheetIds.includes(id));
1083
+ if (sheetOrder.length === 0) {
1084
+ sheetOrder = validSheetIds;
1085
+ }
1086
+ }
1087
+
1088
+ // Build workbook
1089
+ const workbook: IWorkbookData = {
1090
+ id: workbookData.id || 'workbook-1',
1091
+ name: workbookData.name || 'Untitled Workbook',
1092
+ sheetOrder,
1093
+ sheets
1094
+ };
1095
+
1096
+ if (workbookData.locale) workbook.locale = workbookData.locale;
1097
+ if (styles && Object.keys(styles).length > 0) workbook.styles = styles;
1098
+
1099
+ return workbook;
1100
+ }
1101
+
1102
+ /**
1103
+ * Validate AI output structure before conversion
1104
+ *
1105
+ * @param data - Raw parsed JSON from AI
1106
+ * @returns Validation result with errors if any
1107
+ */
1108
+ export function validateAIOutput(data: unknown): {
1109
+ valid: boolean;
1110
+ errors: string[];
1111
+ } {
1112
+ const errors: string[] = [];
1113
+
1114
+ if (!data || typeof data !== 'object') {
1115
+ errors.push('Response must be an object');
1116
+
1117
+ return { valid: false, errors };
1118
+ }
1119
+
1120
+ const obj = data as Record<string, unknown>;
1121
+
1122
+ // Check required top-level fields
1123
+ if (typeof obj.title !== 'string') errors.push('Missing or invalid "title"');
1124
+ if (typeof obj.description !== 'string') errors.push('Missing or invalid "description"');
1125
+
1126
+ // Support both 'workbook' (new) and 'workbookData' (legacy) keys
1127
+ const workbookObj = obj.workbook || obj.workbookData;
1128
+
1129
+ if (!workbookObj || typeof workbookObj !== 'object') {
1130
+ errors.push('Missing or invalid "workbook" (or "workbookData")');
1131
+
1132
+ return { valid: false, errors };
1133
+ }
1134
+
1135
+ const wb = workbookObj as Record<string, unknown>;
1136
+
1137
+ // Check workbook fields
1138
+ if (typeof wb.id !== 'string') errors.push('workbook.id must be a string');
1139
+ if (typeof wb.name !== 'string') errors.push('workbook.name must be a string');
1140
+ if (!Array.isArray(wb.sheetOrder)) errors.push('workbook.sheetOrder must be an array');
1141
+ if (!Array.isArray(wb.sheets)) errors.push('workbook.sheets must be an array');
1142
+
1143
+ // Check sheets
1144
+ if (Array.isArray(wb.sheets)) {
1145
+ for (let i = 0; i < wb.sheets.length; i++) {
1146
+ const entry = wb.sheets[i] as Record<string, unknown>;
1147
+
1148
+ if (!entry || typeof entry !== 'object') {
1149
+ errors.push(`sheets[${i}] must be an object`);
1150
+ continue;
1151
+ }
1152
+ if (typeof entry.id !== 'string') errors.push(`sheets[${i}].id must be a string`);
1153
+ if (!entry.sheet || typeof entry.sheet !== 'object') {
1154
+ errors.push(`sheets[${i}].sheet must be an object`);
1155
+ continue;
1156
+ }
1157
+
1158
+ const sheet = entry.sheet as Record<string, unknown>;
1159
+
1160
+ if (typeof sheet.id !== 'string') errors.push(`sheets[${i}].sheet.id must be a string`);
1161
+ if (typeof sheet.name !== 'string') errors.push(`sheets[${i}].sheet.name must be a string`);
1162
+ if (typeof sheet.rowCount !== 'number') errors.push(`sheets[${i}].sheet.rowCount must be a number`);
1163
+ if (typeof sheet.columnCount !== 'number') errors.push(`sheets[${i}].sheet.columnCount must be a number`);
1164
+ if (!Array.isArray(sheet.cellData)) errors.push(`sheets[${i}].sheet.cellData must be a 2D array`);
1165
+ }
1166
+ }
1167
+
1168
+ return { valid: errors.length === 0, errors };
1169
+ }