@docyrus/ui-pro-ai-assistant 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 (314) hide show
  1. package/dist/components/assistant-animations.d.ts +3 -3
  2. package/dist/docy-assistant.d.ts +1 -1
  3. package/dist/index.js +74 -47
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +5137 -0
  6. package/dist/types/index.d.ts +3 -3
  7. package/dist/views/assistant-view.d.ts +2 -2
  8. package/package.json +38 -36
  9. package/src/components/assistant-animations.tsx +0 -29
  10. package/src/components/assistant-dialogs.tsx +0 -235
  11. package/src/components/code-view.tsx +0 -278
  12. package/src/components/create-agent-task.tsx +0 -104
  13. package/src/components/create-new-work-version.tsx +0 -30
  14. package/src/components/extract-web.tsx +0 -160
  15. package/src/components/forward-to-agent.tsx +0 -90
  16. package/src/components/generate-chart.tsx +0 -101
  17. package/src/components/generative-action-button.tsx +0 -122
  18. package/src/components/generative-tool.tsx +0 -685
  19. package/src/components/generative-ui-object.tsx +0 -210
  20. package/src/components/input-area.tsx +0 -1209
  21. package/src/components/json-schema-layout.tsx +0 -326
  22. package/src/components/list-item-card.tsx +0 -92
  23. package/src/components/mermaid-diagram.tsx +0 -192
  24. package/src/components/preview-image.tsx +0 -47
  25. package/src/components/request-approval.tsx +0 -48
  26. package/src/components/request-user-input.tsx +0 -270
  27. package/src/components/search-web.tsx +0 -319
  28. package/src/components/sheet-command.tsx +0 -88
  29. package/src/components/shell-canvas.tsx +0 -122
  30. package/src/components/show-advanced-data-table.tsx +0 -352
  31. package/src/components/show-generated-content-options.tsx +0 -93
  32. package/src/components/show-people-cards.tsx +0 -180
  33. package/src/components/subagent-tool.tsx +0 -180
  34. package/src/components/text-editor-tool.tsx +0 -328
  35. package/src/components/work-canvas.tsx +0 -88
  36. package/src/components/work-card.tsx +0 -42
  37. package/src/declarations.d.ts +0 -1
  38. package/src/docy-assistant.tsx +0 -1962
  39. package/src/hooks/index.ts +0 -7
  40. package/src/hooks/use-assistant-api.ts +0 -507
  41. package/src/hooks/use-deployment-data.ts +0 -162
  42. package/src/hooks/use-project-state.ts +0 -347
  43. package/src/hooks/use-session-state.ts +0 -207
  44. package/src/hooks/use-speech-recognition.ts +0 -137
  45. package/src/hooks/use-ui-state.ts +0 -185
  46. package/src/hooks/use-works-state.ts +0 -146
  47. package/src/i18n/context.tsx +0 -253
  48. package/src/i18n/index.ts +0 -19
  49. package/src/i18n/locales/de.json +0 -198
  50. package/src/i18n/locales/el.json +0 -198
  51. package/src/i18n/locales/en.json +0 -226
  52. package/src/i18n/locales/es.json +0 -198
  53. package/src/i18n/locales/fr.json +0 -198
  54. package/src/i18n/locales/it.json +0 -198
  55. package/src/i18n/locales/pt.json +0 -198
  56. package/src/i18n/locales/sl.json +0 -198
  57. package/src/i18n/locales/tr.json +0 -211
  58. package/src/i18n/types.ts +0 -23
  59. package/src/i18n/use-translation.ts +0 -17
  60. package/src/index.ts +0 -18
  61. package/src/internal/plate-editor/editor/auth-context.ts +0 -11
  62. package/src/internal/plate-editor/editor/editor-base-kit.tsx +0 -39
  63. package/src/internal/plate-editor/editor/editor-kit.tsx +0 -89
  64. package/src/internal/plate-editor/editor/plate-editor.tsx +0 -75
  65. package/src/internal/plate-editor/editor/plate-types.ts +0 -126
  66. package/src/internal/plate-editor/editor/plugins/ai-kit.tsx +0 -172
  67. package/src/internal/plate-editor/editor/plugins/autoformat-kit.tsx +0 -211
  68. package/src/internal/plate-editor/editor/plugins/basic-blocks-base-kit.tsx +0 -26
  69. package/src/internal/plate-editor/editor/plugins/basic-blocks-kit.tsx +0 -51
  70. package/src/internal/plate-editor/editor/plugins/basic-marks-base-kit.tsx +0 -24
  71. package/src/internal/plate-editor/editor/plugins/basic-marks-kit.tsx +0 -38
  72. package/src/internal/plate-editor/editor/plugins/basic-nodes-kit.tsx +0 -6
  73. package/src/internal/plate-editor/editor/plugins/block-menu-kit.tsx +0 -14
  74. package/src/internal/plate-editor/editor/plugins/block-placeholder-kit.tsx +0 -17
  75. package/src/internal/plate-editor/editor/plugins/block-selection-kit.tsx +0 -31
  76. package/src/internal/plate-editor/editor/plugins/callout-base-kit.tsx +0 -5
  77. package/src/internal/plate-editor/editor/plugins/callout-kit.tsx +0 -7
  78. package/src/internal/plate-editor/editor/plugins/code-block-base-kit.tsx +0 -23
  79. package/src/internal/plate-editor/editor/plugins/code-block-kit.tsx +0 -26
  80. package/src/internal/plate-editor/editor/plugins/column-base-kit.tsx +0 -8
  81. package/src/internal/plate-editor/editor/plugins/column-kit.tsx +0 -7
  82. package/src/internal/plate-editor/editor/plugins/comment-base-kit.tsx +0 -5
  83. package/src/internal/plate-editor/editor/plugins/comment-kit.tsx +0 -174
  84. package/src/internal/plate-editor/editor/plugins/copilot-kit.tsx +0 -68
  85. package/src/internal/plate-editor/editor/plugins/cursor-overlay-kit.tsx +0 -13
  86. package/src/internal/plate-editor/editor/plugins/date-base-kit.tsx +0 -5
  87. package/src/internal/plate-editor/editor/plugins/date-kit.tsx +0 -7
  88. package/src/internal/plate-editor/editor/plugins/discussion-kit.tsx +0 -36
  89. package/src/internal/plate-editor/editor/plugins/dnd-kit.tsx +0 -27
  90. package/src/internal/plate-editor/editor/plugins/docx-export-kit.tsx +0 -43
  91. package/src/internal/plate-editor/editor/plugins/docx-kit.tsx +0 -6
  92. package/src/internal/plate-editor/editor/plugins/emoji-kit.tsx +0 -15
  93. package/src/internal/plate-editor/editor/plugins/exit-break-kit.tsx +0 -12
  94. package/src/internal/plate-editor/editor/plugins/floating-toolbar-kit.tsx +0 -19
  95. package/src/internal/plate-editor/editor/plugins/font-base-kit.tsx +0 -36
  96. package/src/internal/plate-editor/editor/plugins/font-kit.tsx +0 -47
  97. package/src/internal/plate-editor/editor/plugins/indent-base-kit.tsx +0 -19
  98. package/src/internal/plate-editor/editor/plugins/indent-kit.tsx +0 -22
  99. package/src/internal/plate-editor/editor/plugins/link-base-kit.tsx +0 -5
  100. package/src/internal/plate-editor/editor/plugins/link-kit.tsx +0 -35
  101. package/src/internal/plate-editor/editor/plugins/list-base-kit.tsx +0 -24
  102. package/src/internal/plate-editor/editor/plugins/list-kit.tsx +0 -27
  103. package/src/internal/plate-editor/editor/plugins/markdown-kit.tsx +0 -18
  104. package/src/internal/plate-editor/editor/plugins/math-base-kit.tsx +0 -8
  105. package/src/internal/plate-editor/editor/plugins/math-kit.tsx +0 -10
  106. package/src/internal/plate-editor/editor/plugins/media-base-kit.tsx +0 -37
  107. package/src/internal/plate-editor/editor/plugins/media-kit.tsx +0 -53
  108. package/src/internal/plate-editor/editor/plugins/mention-base-kit.tsx +0 -5
  109. package/src/internal/plate-editor/editor/plugins/mention-kit.tsx +0 -36
  110. package/src/internal/plate-editor/editor/plugins/slash-kit.tsx +0 -17
  111. package/src/internal/plate-editor/editor/plugins/suggestion-base-kit.tsx +0 -5
  112. package/src/internal/plate-editor/editor/plugins/suggestion-kit.tsx +0 -95
  113. package/src/internal/plate-editor/editor/plugins/table-base-kit.tsx +0 -20
  114. package/src/internal/plate-editor/editor/plugins/table-kit.tsx +0 -22
  115. package/src/internal/plate-editor/editor/plugins/toc-base-kit.tsx +0 -5
  116. package/src/internal/plate-editor/editor/plugins/toc-kit.tsx +0 -14
  117. package/src/internal/plate-editor/editor/plugins/toggle-base-kit.tsx +0 -5
  118. package/src/internal/plate-editor/editor/plugins/toggle-kit.tsx +0 -9
  119. package/src/internal/plate-editor/editor/transforms.ts +0 -165
  120. package/src/internal/plate-editor/editor/use-chat.ts +0 -152
  121. package/src/internal/plate-editor/hooks/index.ts +0 -3
  122. package/src/internal/plate-editor/hooks/use-copy-to-clipboard.ts +0 -31
  123. package/src/internal/plate-editor/hooks/use-is-touch-device.ts +0 -26
  124. package/src/internal/plate-editor/hooks/use-lock-scroll.ts +0 -21
  125. package/src/internal/plate-editor/hooks/use-media-query.ts +0 -44
  126. package/src/internal/plate-editor/hooks/use-mounted.ts +0 -18
  127. package/src/internal/plate-editor/hooks/use-on-click-outside.ts +0 -114
  128. package/src/internal/plate-editor/hooks/use-upload-file.ts +0 -81
  129. package/src/internal/plate-editor/i18n/context.tsx +0 -58
  130. package/src/internal/plate-editor/i18n/index.ts +0 -3
  131. package/src/internal/plate-editor/i18n/locales/de.json +0 -57
  132. package/src/internal/plate-editor/i18n/locales/el.json +0 -57
  133. package/src/internal/plate-editor/i18n/locales/en.json +0 -57
  134. package/src/internal/plate-editor/i18n/locales/es.json +0 -57
  135. package/src/internal/plate-editor/i18n/locales/fr.json +0 -57
  136. package/src/internal/plate-editor/i18n/locales/it.json +0 -57
  137. package/src/internal/plate-editor/i18n/locales/pt.json +0 -57
  138. package/src/internal/plate-editor/i18n/locales/sl.json +0 -57
  139. package/src/internal/plate-editor/i18n/locales/tr.json +0 -57
  140. package/src/internal/plate-editor/i18n/types.ts +0 -59
  141. package/src/internal/plate-editor/i18n/use-translation.ts +0 -22
  142. package/src/internal/plate-editor/index.ts +0 -39
  143. package/src/internal/plate-editor/lib/ai-output-converter.ts +0 -153
  144. package/src/internal/plate-editor/lib/download-file.ts +0 -17
  145. package/src/internal/plate-editor/plate-ui/ai-chat-editor.tsx +0 -24
  146. package/src/internal/plate-editor/plate-ui/ai-menu.tsx +0 -828
  147. package/src/internal/plate-editor/plate-ui/ai-node.tsx +0 -41
  148. package/src/internal/plate-editor/plate-ui/ai-toolbar-button.tsx +0 -25
  149. package/src/internal/plate-editor/plate-ui/alert-dialog.tsx +0 -145
  150. package/src/internal/plate-editor/plate-ui/align-toolbar-button.tsx +0 -88
  151. package/src/internal/plate-editor/plate-ui/avatar.tsx +0 -3
  152. package/src/internal/plate-editor/plate-ui/block-context-menu.tsx +0 -104
  153. package/src/internal/plate-editor/plate-ui/block-discussion.tsx +0 -364
  154. package/src/internal/plate-editor/plate-ui/block-draggable.tsx +0 -557
  155. package/src/internal/plate-editor/plate-ui/block-list-static.tsx +0 -77
  156. package/src/internal/plate-editor/plate-ui/block-list.tsx +0 -85
  157. package/src/internal/plate-editor/plate-ui/block-menu.tsx +0 -555
  158. package/src/internal/plate-editor/plate-ui/block-selection.tsx +0 -47
  159. package/src/internal/plate-editor/plate-ui/block-suggestion.tsx +0 -469
  160. package/src/internal/plate-editor/plate-ui/blockquote-node-static.tsx +0 -10
  161. package/src/internal/plate-editor/plate-ui/blockquote-node.tsx +0 -11
  162. package/src/internal/plate-editor/plate-ui/button.tsx +0 -201
  163. package/src/internal/plate-editor/plate-ui/calendar.tsx +0 -3
  164. package/src/internal/plate-editor/plate-ui/callout-node-static.tsx +0 -76
  165. package/src/internal/plate-editor/plate-ui/callout-node.tsx +0 -54
  166. package/src/internal/plate-editor/plate-ui/caption.tsx +0 -47
  167. package/src/internal/plate-editor/plate-ui/checkbox.tsx +0 -3
  168. package/src/internal/plate-editor/plate-ui/code-block-node-static.tsx +0 -172
  169. package/src/internal/plate-editor/plate-ui/code-block-node.tsx +0 -226
  170. package/src/internal/plate-editor/plate-ui/code-node-static.tsx +0 -11
  171. package/src/internal/plate-editor/plate-ui/code-node.tsx +0 -12
  172. package/src/internal/plate-editor/plate-ui/column-node-static.tsx +0 -65
  173. package/src/internal/plate-editor/plate-ui/column-node.tsx +0 -24
  174. package/src/internal/plate-editor/plate-ui/command.tsx +0 -202
  175. package/src/internal/plate-editor/plate-ui/comment-node-static.tsx +0 -12
  176. package/src/internal/plate-editor/plate-ui/comment-node.tsx +0 -45
  177. package/src/internal/plate-editor/plate-ui/comment-toolbar-button.tsx +0 -24
  178. package/src/internal/plate-editor/plate-ui/comment.tsx +0 -619
  179. package/src/internal/plate-editor/plate-ui/cursor-overlay.tsx +0 -85
  180. package/src/internal/plate-editor/plate-ui/date-node-static.tsx +0 -43
  181. package/src/internal/plate-editor/plate-ui/date-node.tsx +0 -54
  182. package/src/internal/plate-editor/plate-ui/dialog.tsx +0 -445
  183. package/src/internal/plate-editor/plate-ui/dropdown-menu.tsx +0 -264
  184. package/src/internal/plate-editor/plate-ui/editor-static.tsx +0 -40
  185. package/src/internal/plate-editor/plate-ui/editor.tsx +0 -146
  186. package/src/internal/plate-editor/plate-ui/emoji-node.tsx +0 -48
  187. package/src/internal/plate-editor/plate-ui/emoji-toolbar-button.tsx +0 -626
  188. package/src/internal/plate-editor/plate-ui/equation-node-static.tsx +0 -155
  189. package/src/internal/plate-editor/plate-ui/equation-node.tsx +0 -226
  190. package/src/internal/plate-editor/plate-ui/equation-toolbar-button.tsx +0 -26
  191. package/src/internal/plate-editor/plate-ui/export-toolbar-button.tsx +0 -206
  192. package/src/internal/plate-editor/plate-ui/fixed-toolbar-buttons.tsx +0 -157
  193. package/src/internal/plate-editor/plate-ui/fixed-toolbar.tsx +0 -25
  194. package/src/internal/plate-editor/plate-ui/floating-discussion.tsx +0 -1129
  195. package/src/internal/plate-editor/plate-ui/floating-toolbar-buttons.tsx +0 -129
  196. package/src/internal/plate-editor/plate-ui/floating-toolbar.tsx +0 -97
  197. package/src/internal/plate-editor/plate-ui/font-color-toolbar-button.tsx +0 -209
  198. package/src/internal/plate-editor/plate-ui/font-size-toolbar-button.tsx +0 -152
  199. package/src/internal/plate-editor/plate-ui/ghost-text.tsx +0 -20
  200. package/src/internal/plate-editor/plate-ui/heading-node-static.tsx +0 -52
  201. package/src/internal/plate-editor/plate-ui/heading-node.tsx +0 -56
  202. package/src/internal/plate-editor/plate-ui/highlight-node-static.tsx +0 -9
  203. package/src/internal/plate-editor/plate-ui/highlight-node.tsx +0 -11
  204. package/src/internal/plate-editor/plate-ui/history-toolbar-button.tsx +0 -50
  205. package/src/internal/plate-editor/plate-ui/hover-card.tsx +0 -7
  206. package/src/internal/plate-editor/plate-ui/hr-node-static.tsx +0 -18
  207. package/src/internal/plate-editor/plate-ui/hr-node.tsx +0 -28
  208. package/src/internal/plate-editor/plate-ui/import-toolbar-button.tsx +0 -122
  209. package/src/internal/plate-editor/plate-ui/indent-toolbar-button.tsx +0 -32
  210. package/src/internal/plate-editor/plate-ui/inline-combobox.tsx +0 -409
  211. package/src/internal/plate-editor/plate-ui/input.tsx +0 -37
  212. package/src/internal/plate-editor/plate-ui/insert-toolbar-button.tsx +0 -258
  213. package/src/internal/plate-editor/plate-ui/label.tsx +0 -1
  214. package/src/internal/plate-editor/plate-ui/line-height-toolbar-button.tsx +0 -69
  215. package/src/internal/plate-editor/plate-ui/link-node-static.tsx +0 -15
  216. package/src/internal/plate-editor/plate-ui/link-node.tsx +0 -33
  217. package/src/internal/plate-editor/plate-ui/link-toolbar-button.tsx +0 -28
  218. package/src/internal/plate-editor/plate-ui/link-toolbar.tsx +0 -147
  219. package/src/internal/plate-editor/plate-ui/list-toolbar-button.tsx +0 -177
  220. package/src/internal/plate-editor/plate-ui/mark-toolbar-button.tsx +0 -34
  221. package/src/internal/plate-editor/plate-ui/media-audio-node-static.tsx +0 -21
  222. package/src/internal/plate-editor/plate-ui/media-audio-node.tsx +0 -32
  223. package/src/internal/plate-editor/plate-ui/media-embed-node.tsx +0 -103
  224. package/src/internal/plate-editor/plate-ui/media-file-node-static.tsx +0 -30
  225. package/src/internal/plate-editor/plate-ui/media-file-node.tsx +0 -52
  226. package/src/internal/plate-editor/plate-ui/media-image-node-static.tsx +0 -37
  227. package/src/internal/plate-editor/plate-ui/media-image-node.tsx +0 -183
  228. package/src/internal/plate-editor/plate-ui/media-placeholder-node.tsx +0 -441
  229. package/src/internal/plate-editor/plate-ui/media-preview-dialog.tsx +0 -127
  230. package/src/internal/plate-editor/plate-ui/media-toolbar-button.tsx +0 -227
  231. package/src/internal/plate-editor/plate-ui/media-toolbar.tsx +0 -214
  232. package/src/internal/plate-editor/plate-ui/media-upload-toast.tsx +0 -73
  233. package/src/internal/plate-editor/plate-ui/media-video-node-static.tsx +0 -35
  234. package/src/internal/plate-editor/plate-ui/media-video-node.tsx +0 -119
  235. package/src/internal/plate-editor/plate-ui/mention-node-static.tsx +0 -46
  236. package/src/internal/plate-editor/plate-ui/mention-node.tsx +0 -79
  237. package/src/internal/plate-editor/plate-ui/menu.tsx +0 -539
  238. package/src/internal/plate-editor/plate-ui/mode-toolbar-button.tsx +0 -124
  239. package/src/internal/plate-editor/plate-ui/more-toolbar-button.tsx +0 -34
  240. package/src/internal/plate-editor/plate-ui/paragraph-node-static.tsx +0 -15
  241. package/src/internal/plate-editor/plate-ui/paragraph-node.tsx +0 -16
  242. package/src/internal/plate-editor/plate-ui/popover.tsx +0 -75
  243. package/src/internal/plate-editor/plate-ui/progress.tsx +0 -1
  244. package/src/internal/plate-editor/plate-ui/remote-cursor-overlay.tsx +0 -79
  245. package/src/internal/plate-editor/plate-ui/resize-handle.tsx +0 -86
  246. package/src/internal/plate-editor/plate-ui/separator.tsx +0 -41
  247. package/src/internal/plate-editor/plate-ui/slash-node.tsx +0 -433
  248. package/src/internal/plate-editor/plate-ui/spinner.tsx +0 -1
  249. package/src/internal/plate-editor/plate-ui/suggestion-node-static.tsx +0 -35
  250. package/src/internal/plate-editor/plate-ui/suggestion-node.tsx +0 -166
  251. package/src/internal/plate-editor/plate-ui/suggestion-toolbar-button.tsx +0 -24
  252. package/src/internal/plate-editor/plate-ui/table-node-static.tsx +0 -84
  253. package/src/internal/plate-editor/plate-ui/table-node.tsx +0 -283
  254. package/src/internal/plate-editor/plate-ui/table-toolbar-button.tsx +0 -252
  255. package/src/internal/plate-editor/plate-ui/tabs.tsx +0 -3
  256. package/src/internal/plate-editor/plate-ui/textarea.tsx +0 -57
  257. package/src/internal/plate-editor/plate-ui/toc-node-static.tsx +0 -142
  258. package/src/internal/plate-editor/plate-ui/toc-node.tsx +0 -57
  259. package/src/internal/plate-editor/plate-ui/toc-sidebar.tsx +0 -48
  260. package/src/internal/plate-editor/plate-ui/toggle-node-static.tsx +0 -18
  261. package/src/internal/plate-editor/plate-ui/toggle-node.tsx +0 -33
  262. package/src/internal/plate-editor/plate-ui/toggle-toolbar-button.tsx +0 -24
  263. package/src/internal/plate-editor/plate-ui/toggle.tsx +0 -3
  264. package/src/internal/plate-editor/plate-ui/toolbar.tsx +0 -378
  265. package/src/internal/plate-editor/plate-ui/tooltip.tsx +0 -148
  266. package/src/internal/plate-editor/plate-ui/turn-into-toolbar-button.tsx +0 -175
  267. package/src/internal/plate-editor/types/index.ts +0 -22
  268. package/src/internal/plate-editor/vite.ts +0 -284
  269. package/src/internal/sheets/components/univer-sheets.tsx +0 -1104
  270. package/src/internal/sheets/i18n/context.tsx +0 -183
  271. package/src/internal/sheets/i18n/index.ts +0 -19
  272. package/src/internal/sheets/i18n/locales/de.json +0 -21
  273. package/src/internal/sheets/i18n/locales/el.json +0 -21
  274. package/src/internal/sheets/i18n/locales/en.json +0 -21
  275. package/src/internal/sheets/i18n/locales/es.json +0 -21
  276. package/src/internal/sheets/i18n/locales/fr.json +0 -21
  277. package/src/internal/sheets/i18n/locales/it.json +0 -21
  278. package/src/internal/sheets/i18n/locales/pt.json +0 -21
  279. package/src/internal/sheets/i18n/locales/sl.json +0 -21
  280. package/src/internal/sheets/i18n/locales/tr.json +0 -21
  281. package/src/internal/sheets/i18n/types.ts +0 -23
  282. package/src/internal/sheets/i18n/use-translation.ts +0 -17
  283. package/src/internal/sheets/index.ts +0 -14
  284. package/src/internal/sheets/types/css.d.ts +0 -11
  285. package/src/internal/sheets/types/index.ts +0 -260
  286. package/src/internal/sheets/xlsx.ts +0 -1169
  287. package/src/lib/api-client.ts +0 -77
  288. package/src/lib/assistant-api-actions.ts +0 -549
  289. package/src/lib/assistant-config.tsx +0 -75
  290. package/src/lib/class-utils.ts +0 -40
  291. package/src/lib/index.ts +0 -7
  292. package/src/lib/message-utils.ts +0 -131
  293. package/src/tools/tools-schema.json +0 -512
  294. package/src/types/index.ts +0 -235
  295. package/src/views/assistant-view.tsx +0 -1137
  296. package/src/views/canvas-app.tsx +0 -839
  297. package/src/views/canvas-code.tsx +0 -93
  298. package/src/views/canvas-deep-research.tsx +0 -44
  299. package/src/views/canvas-image.tsx +0 -25
  300. package/src/views/canvas-record-view.tsx +0 -285
  301. package/src/views/canvas-spreadsheet.tsx +0 -125
  302. package/src/views/canvas-text.tsx +0 -52
  303. package/src/views/canvas.tsx +0 -274
  304. package/src/views/chat-panel.tsx +0 -149
  305. package/src/views/index.ts +0 -20
  306. package/src/views/memories-panel.tsx +0 -365
  307. package/src/views/message-list.tsx +0 -370
  308. package/src/views/project-detail.tsx +0 -257
  309. package/src/views/projects-panel.tsx +0 -131
  310. package/src/views/sessions-list.tsx +0 -98
  311. package/src/views/sidebar-content.tsx +0 -256
  312. package/src/views/work-detail.tsx +0 -98
  313. package/src/vite.ts +0 -64
  314. package/src/worker.ts +0 -203
@@ -1,1962 +0,0 @@
1
- 'use client';
2
-
3
- import {
4
- useCallback, useEffect, useMemo, useRef, useState, type ChangeEvent, type FormEvent, type KeyboardEvent, type Ref, type RefObject
5
- } from 'react';
6
-
7
- import { useChat } from '@ai-sdk/react';
8
- import { Button } from '@docyrus/ui-pro-shared/components/button';
9
- import { toast } from 'sonner';
10
- import {
11
- Dialog, DialogContent, DialogTitle
12
- } from '@docyrus/ui-pro-shared/components/dialog';
13
- import {
14
- DropdownMenu,
15
- DropdownMenuContent,
16
- DropdownMenuItem,
17
- DropdownMenuSeparator,
18
- DropdownMenuSub,
19
- DropdownMenuSubContent,
20
- DropdownMenuSubTrigger,
21
- DropdownMenuTrigger
22
- } from '@docyrus/ui-pro-shared/components/dropdown-menu';
23
- import { Textarea } from '@docyrus/ui-pro-shared/components/textarea';
24
- import { Input } from '@docyrus/ui-pro-shared/components/input';
25
- import {
26
- Tabs, TabsContent, TabsList, TabsTrigger
27
- } from '@docyrus/ui-pro-shared/components/tabs';
28
- import { cn } from '@docyrus/ui-pro-shared/lib/utils';
29
- import {
30
- DefaultChatTransport,
31
- type FinishReason,
32
- lastAssistantMessageIsCompleteWithToolCalls,
33
- type UIMessage
34
- } from 'ai';
35
- import {
36
- Archive,
37
- ArrowLeft,
38
- ArrowRight,
39
- Edit,
40
- FolderInput,
41
- Maximize2,
42
- Minimize2,
43
- MoreHorizontal,
44
- PanelLeft,
45
- Plus,
46
- Search,
47
- Trash,
48
- X
49
- } from 'lucide-react';
50
-
51
- import { AssistantDialogs } from './components/assistant-dialogs';
52
- import { WorkCard } from './components/work-card';
53
- import {
54
- useAssistantApi, useProjectState, useSessionState, useSpeechRecognition, useUIState, useWorksState
55
- } from './hooks';
56
- import { AssistantCanvasView } from './views/canvas';
57
- import { ConversationView, InlineView } from './views/assistant-view';
58
- import { MemoriesPanel } from './views/memories-panel';
59
- import { AssistantProjectDetailView } from './views/project-detail';
60
- import { AssistantProjectsPanel } from './views/projects-panel';
61
- import { SessionsListView } from './views/sessions-list';
62
- import { SidebarContent } from './views/sidebar-content';
63
- import { AssistantWorkDetailView } from './views/work-detail';
64
- import { useAssistantTranslation } from './i18n';
65
- import { useApiClient } from './lib/api-client';
66
- import {
67
- createThread,
68
- deleteProject as deleteProjectApi,
69
- deleteSession as deleteSessionApi,
70
- fetchAgentThreads,
71
- fetchProjectThreads as fetchProjectThreadsApi,
72
- fetchProjects as fetchProjectsApi,
73
- fetchProjectWorks as fetchProjectWorksApi,
74
- fetchWorks as fetchWorksApi,
75
- loadThreadMessages as loadThreadMessagesApi,
76
- updateProject as updateProjectApi,
77
- updateProjectInstructions as updateProjectInstructionsApi,
78
- updateSession as updateSessionApi,
79
- uploadThreadFile
80
- } from './lib/assistant-api-actions';
81
- import { useAssistantConfig } from './lib/assistant-config';
82
- import { getContainerClassName, getDialogClassName } from './lib/class-utils';
83
-
84
- import {
85
- type AssistantSession,
86
- type DocyAssistantProps,
87
- type Project,
88
- type Work
89
- } from './types';
90
-
91
- interface ThreadHeaderInlineProps {
92
- session: AssistantSession;
93
- projects: Project[];
94
- projectContext: { id: string; name: string } | null;
95
- onSaveTitle: (newTitle: string) => Promise<void>;
96
- onDelete: () => void;
97
- onArchive: () => Promise<void>;
98
- onMoveToProject: (projectId: string) => Promise<void>;
99
- onProjectContextClick?: () => void;
100
- isFullscreen?: boolean;
101
- t: (key: string) => string;
102
- }
103
-
104
- function ThreadHeaderInline({
105
- session,
106
- projects,
107
- projectContext,
108
- onSaveTitle,
109
- onDelete,
110
- onArchive,
111
- onMoveToProject,
112
- onProjectContextClick,
113
- isFullscreen = false,
114
- t
115
- }: ThreadHeaderInlineProps) {
116
- const dropdownZClass = isFullscreen ? 'z-[10000]' : '';
117
- const [editValue, setEditValue] = useState<string | null>(null);
118
-
119
- const handleBlur = async () => {
120
- if (editValue === null) return;
121
- const trimmed = editValue.trim();
122
-
123
- if (trimmed && trimmed !== session.title) {
124
- await onSaveTitle(trimmed);
125
- }
126
-
127
- setEditValue(null);
128
- };
129
-
130
- const handleKeyDown = async (e: KeyboardEvent<HTMLInputElement>) => {
131
- if (e.key === 'Enter') {
132
- e.currentTarget.blur();
133
- } else if (e.key === 'Escape') {
134
- setEditValue(null);
135
- }
136
- };
137
-
138
- return (
139
- <div className="flex-none h-10 flex bg-background items-center justify-between border-b border-x border-border px-3 mx-3 rounded-b-md shrink-0">
140
- <div className="flex items-center gap-2 min-w-0 flex-1">
141
- {projectContext?.id && onProjectContextClick && (
142
- <Button
143
- variant="ghost"
144
- onClick={onProjectContextClick}
145
- className="inline-flex items-center gap-1 px-2 py-0.5 h-auto rounded-full bg-sky-100 text-sky-700 dark:bg-sky-900/40 dark:text-sky-300 text-xs font-medium flex-none hover:bg-sky-200 transition-colors">
146
- {projectContext.name}
147
- </Button>
148
- )}
149
- {editValue !== null ? (
150
- <input
151
- autoFocus
152
- value={editValue}
153
- onChange={e => setEditValue(e.target.value)}
154
- onBlur={handleBlur}
155
- onKeyDown={handleKeyDown}
156
- className="text-sm font-medium text-foreground bg-transparent border-b border-border focus:outline-none min-w-0 flex-1" />
157
- ) : (
158
- <div className="group/title flex items-center gap-1 min-w-0">
159
- <span className="text-sm font-medium text-foreground truncate min-w-0">
160
- {session.title || t('common.untitled')}
161
- </span>
162
- <Button
163
- variant="ghost"
164
- size="icon"
165
- onClick={() => setEditValue(session.title || '')}
166
- className="opacity-0 group-hover/title:opacity-100 transition-opacity shrink-0 h-5 w-5 text-muted-foreground hover:text-foreground">
167
- <Edit className="w-3.5 h-3.5" />
168
- </Button>
169
- </div>
170
- )}
171
- </div>
172
- <div className="flex items-center flex-none">
173
- <DropdownMenu>
174
- <DropdownMenuTrigger asChild>
175
- <Button variant="ghost" size="icon" className="h-7 w-7 text-muted-foreground hover:text-foreground">
176
- <MoreHorizontal className="w-4 h-4" />
177
- </Button>
178
- </DropdownMenuTrigger>
179
- <DropdownMenuContent align="end" className={cn('w-48', dropdownZClass)}>
180
- <DropdownMenuSub>
181
- <DropdownMenuSubTrigger className="cursor-pointer">
182
- <FolderInput className="w-4 h-4 mr-2" />
183
- {t('actions.move_to_project')}
184
- </DropdownMenuSubTrigger>
185
- <DropdownMenuSubContent className={cn('w-48', dropdownZClass)}>
186
- {projects.length === 0 ? (
187
- <DropdownMenuItem disabled className="text-muted-foreground text-xs">
188
- {t('messages.no_projects_yet')}
189
- </DropdownMenuItem>
190
- ) : (
191
- projects.map(project => (
192
- <DropdownMenuItem
193
- key={project.id}
194
- className="cursor-pointer"
195
- onClick={() => onMoveToProject(project.id)}>
196
- {project.name}
197
- </DropdownMenuItem>
198
- ))
199
- )}
200
- </DropdownMenuSubContent>
201
- </DropdownMenuSub>
202
- <DropdownMenuItem
203
- className="cursor-pointer"
204
- onClick={onArchive}>
205
- <Archive className="w-4 h-4 mr-2" />
206
- {t('actions.archive')}
207
- </DropdownMenuItem>
208
- <DropdownMenuSeparator />
209
- <DropdownMenuItem
210
- className="text-destructive focus:text-destructive cursor-pointer"
211
- onClick={onDelete}>
212
- <Trash className="w-4 h-4 mr-2" />
213
- {t('common.delete')}
214
- </DropdownMenuItem>
215
- </DropdownMenuContent>
216
- </DropdownMenu>
217
- </div>
218
- </div>
219
- );
220
- }
221
-
222
- function dataUrlToFile(dataUrl: string, filename: string): File {
223
- const arr = dataUrl.split(',');
224
- const mime = arr[0].match(/:(.*?);/)?.[1] || 'application/octet-stream';
225
- const bstr = atob(arr[1]);
226
- let n = bstr.length;
227
- const u8arr = new Uint8Array(n);
228
-
229
- while (n--) {
230
- u8arr[n] = bstr.charCodeAt(n);
231
- }
232
-
233
- return new File([u8arr], filename, { type: mime });
234
- }
235
-
236
- export const DocyAssistant = (
237
- {
238
- ref, open, onOpenChange, supportWebSearch = false, supportThinking = false, supportFiles = false, supportDocumentSearch = false, supportDeepResearch = false, supportMultiModels = false, supportWorkCanvas = false, apiEndpoint = '/ai/agents/:agentId/chat', title: titleProp, description: descriptionProp, placeholder: placeholderProp, logo, footerText: footerTextProp, variant = 'default', renderMode = 'modal', enableSidebar = true, enableNavDropdown = false, enableVoice = false, enableMicrophone = true, tenantAiAgentId, deploymentId, onMessageSend, onVoiceStart, onVoiceEnd, className, defaultFullscreen = false, hideExpand = false, agentSelectorUrl, baseAgentSelectorUrl, onAgentChange, ...props
239
- }: DocyAssistantProps & { ref?: RefObject<HTMLDivElement | null> }
240
- ) => {
241
- const config = useAssistantConfig();
242
- const { apiBaseUrl: baseUrl, getAuthToken, user: configUser } = config;
243
- const apiClient = useApiClient();
244
- const { t } = useAssistantTranslation();
245
-
246
- // Localized default values
247
- const title = titleProp || 'DocyAssistant';
248
- const description = descriptionProp || t('descriptions.default');
249
- const placeholder = placeholderProp || t('placeholders.type_message');
250
- const footerText = footerTextProp || t('descriptions.footer_default');
251
-
252
- // Custom hooks for grouped state management
253
- const { state: uiState, actions: uiActions } = useUIState(variant, renderMode);
254
- const { state: sessionState, actions: sessionActions } = useSessionState();
255
- const { state: projectState, actions: projectActions } = useProjectState();
256
- const { state: worksState, actions: worksActions } = useWorksState();
257
-
258
- // Remaining local state
259
- const [input, setInput] = useState('');
260
- const currentUserId = configUser?.id || null;
261
- const [projectSearchQuery, setProjectSearchQuery] = useState('');
262
- const [isInlineFullscreen, setIsInlineFullscreen] = useState(defaultFullscreen);
263
-
264
- // Speech recognition hook
265
- const { isRecording, recognition, handleMicrophoneClick } = useSpeechRecognition({
266
- enabled: enableMicrophone,
267
- onTranscript: transcript => setInput(prev => prev + (prev ? ' ' : '') + transcript),
268
- onStart: onVoiceStart,
269
- onEnd: onVoiceEnd
270
- });
271
-
272
- // Refs
273
- const selectedSessionIdRef = useRef<string | null>(null);
274
- const sidebarCloseTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
275
- const rightSidebarCloseTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
276
- const authTokenRef = useRef<string>('');
277
- const messageOptionsRef = useRef<any>(null);
278
-
279
- // Keep refs in sync with state
280
- useEffect(() => {
281
- selectedSessionIdRef.current = sessionState.selectedSessionId;
282
- }, [sessionState.selectedSessionId]);
283
-
284
- // Get and maintain auth token in ref
285
- useEffect(() => {
286
- getAuthToken().then((token: string) => {
287
- if (token) {
288
- authTokenRef.current = token;
289
- }
290
- });
291
- }, [getAuthToken]);
292
-
293
- // Create transport with headers and body using functions for dynamic values
294
- const transport = useMemo(() => {
295
- // Build URL from apiEndpoint template, replacing :agentId and optionally adding deployment
296
- const endpointPath = apiEndpoint.replace(':agentId', tenantAiAgentId);
297
- const apiUrl = deploymentId ? `${baseUrl}${endpointPath.replace('/chat', `/deployments/${deploymentId}/chat`)}` : `${baseUrl}${endpointPath}`;
298
-
299
- return new DefaultChatTransport({
300
- api: apiUrl,
301
- prepareSendMessagesRequest: ({ messages, body }: { messages: any[]; body: any }) => {
302
- const options = messageOptionsRef.current;
303
-
304
- return {
305
- body: {
306
- ...body,
307
- agentId: tenantAiAgentId,
308
- threadId: selectedSessionIdRef.current, // Use ref for immediate access
309
- messages: messages.slice(-10), // Only send last 10 messages
310
- modelId: options?.modelId,
311
- supportMultipleModels: options?.supportMultipleModels,
312
- supportFiles: options?.supportFiles,
313
- supportWebSearch: options?.supportWebSearch,
314
- supportDeepResearch: options?.supportDeepResearch,
315
- supportDocumentSearch: options?.supportDocumentSearch,
316
- supportThinking: options?.supportThinking,
317
- supportWorkCanvas: options?.supportWorkCanvas,
318
- ...(options?.filePaths?.length ? { files: options.filePaths } : {})
319
- }
320
- };
321
- },
322
- headers: () => {
323
- const token = authTokenRef.current;
324
-
325
- return {
326
- 'Content-Type': 'application/json',
327
- ...(token && { Authorization: `Bearer ${token}` })
328
- };
329
- },
330
- body: () => ({
331
- agentId: tenantAiAgentId,
332
- threadId: selectedSessionIdRef.current
333
- })
334
- });
335
- }, [
336
- baseUrl,
337
- apiEndpoint,
338
- tenantAiAgentId,
339
- deploymentId
340
- ]);
341
-
342
- // Use the AI SDK's useChat hook with agent-specific endpoint
343
- const {
344
- messages = [],
345
- status,
346
- sendMessage,
347
- setMessages,
348
- stop,
349
- addToolOutput
350
- } = useChat({
351
- id: `docy-assistant:${tenantAiAgentId}`,
352
- transport,
353
- sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
354
- onFinish: (
355
- message: UIMessage,
356
- options?: {
357
- usage?: {
358
- inputTokens?: number;
359
- outputTokens?: number;
360
- totalTokens?: number;
361
- cacheReadTokens?: number;
362
- cacheWriteTokens?: number;
363
- };
364
- finishReason?: FinishReason;
365
- }
366
- ) => {
367
- const { usage, finishReason } = options || {};
368
-
369
- if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
370
- console.info('[AI] Message finished:', {
371
- messageId: message.id,
372
- role: message.role,
373
- finishReason,
374
- inputTokens: usage?.inputTokens,
375
- outputTokens: usage?.outputTokens,
376
- totalTokens: usage?.totalTokens,
377
- cacheReadTokens: usage?.cacheReadTokens,
378
- cacheWriteTokens: usage?.cacheWriteTokens
379
- });
380
- }
381
- },
382
- onError: (error: Error) => {
383
- console.error('[AI] Chat error:', error);
384
- }
385
- } as any);
386
-
387
- // AI SDK v6 status handling
388
- const isStreaming = status === 'streaming';
389
- const isSubmitting = status === 'submitted';
390
- const isLoading = isStreaming || isSubmitting;
391
-
392
- const handleToolAction = useCallback((event: {
393
- tool: string;
394
- toolCallId: string;
395
- decision: string;
396
- input: any;
397
- }) => {
398
- switch (event.decision) {
399
- case 'approve':
400
-
401
- case 'continue':
402
- addToolOutput({
403
- tool: event.tool as any,
404
- toolCallId: event.toolCallId,
405
- output: { action: event.decision }
406
- });
407
- break;
408
-
409
- case 'submit':
410
- addToolOutput({
411
- tool: event.tool as any,
412
- toolCallId: event.toolCallId,
413
- output: event.input
414
- });
415
- break;
416
-
417
- default:
418
- addToolOutput({
419
- tool: event.tool as any,
420
- toolCallId: event.toolCallId,
421
- output: { decision: event.decision }
422
- });
423
- }
424
- }, [addToolOutput]);
425
-
426
- // Compute user display info from config
427
- const userDisplayName = configUser ? `${configUser.firstname || ''} ${configUser.lastname || ''}`.trim() || configUser.email?.split('@')[0] || 'User' : 'User';
428
- const userPhoto = configUser?.photo || '';
429
-
430
- // Computed values from state
431
- const isInlineMode = uiState.currentRenderMode === 'inline';
432
-
433
- // Auto-open create view when no projects
434
- useEffect(() => {
435
- if (
436
- uiState.activeTab === 2
437
- && projectState.projectsLoaded
438
- && projectState.projects.length === 0
439
- && projectState.view === 'list'
440
- ) {
441
- projectActions.setView('create');
442
- }
443
- }, [
444
- uiState.activeTab,
445
- projectState.projectsLoaded,
446
- projectState.projects.length,
447
- projectState.view,
448
- projectActions
449
- ]);
450
-
451
- /*
452
- * Fetch agent details with request deduplication
453
- * Uses module-level cache to prevent multiple requests across component instances
454
- */
455
- const apiClientRef = useRef(apiClient);
456
-
457
- apiClientRef.current = apiClient;
458
-
459
- // Use the assistant API hook for agent details
460
- const { agentDetails, isLoadingAgentDetails } = useAssistantApi({
461
- tenantAiAgentId,
462
- deploymentId,
463
- logo,
464
- title,
465
- t
466
- });
467
-
468
- const fetchThreads = useCallback(async () => {
469
- await fetchAgentThreads(apiClient, tenantAiAgentId, sessionActions.setSessions, currentUserId, deploymentId);
470
- }, [
471
- apiClient,
472
- tenantAiAgentId,
473
- sessionActions,
474
- currentUserId,
475
- deploymentId
476
- ]);
477
-
478
- const fetchProjectThreads = useCallback(async (projectId: string) => {
479
- await fetchProjectThreadsApi(apiClient, projectId, projectActions.setProjectThreads);
480
- }, [apiClient, projectActions]);
481
-
482
- const fetchProjectWorks = useCallback(async (projectId: string) => {
483
- await fetchProjectWorksApi(apiClient, projectId, projectActions.setProjectWorks, tenantAiAgentId, currentUserId);
484
- }, [
485
- apiClient,
486
- projectActions,
487
- tenantAiAgentId,
488
- currentUserId
489
- ]);
490
-
491
- const fetchProjects = useCallback(async () => {
492
- await fetchProjectsApi(
493
- apiClient,
494
- configUser?.id || null,
495
- projectActions.setProjects,
496
- (project) => {
497
- fetchProjectThreads(project.id);
498
- fetchProjectWorks(project.id);
499
- },
500
- () => projectActions.setProjectsLoaded(true),
501
- tenantAiAgentId
502
- );
503
- }, [
504
- apiClient,
505
- configUser?.id,
506
- projectActions,
507
- fetchProjectThreads,
508
- fetchProjectWorks,
509
- tenantAiAgentId
510
- ]);
511
-
512
- const fetchWorks = useCallback(async () => {
513
- await fetchWorksApi(apiClient, worksActions.setWorks, tenantAiAgentId, currentUserId);
514
- }, [
515
- apiClient,
516
- worksActions,
517
- tenantAiAgentId,
518
- currentUserId
519
- ]);
520
-
521
- // Fetch threads from API
522
- useEffect(() => {
523
- if (open && (enableSidebar || enableNavDropdown)) {
524
- fetchThreads();
525
- }
526
- }, [
527
- open,
528
- enableSidebar,
529
- enableNavDropdown,
530
- fetchThreads
531
- ]);
532
-
533
- // Fetch projects when projects tab is active
534
- useEffect(() => {
535
- if (open && (enableSidebar || enableNavDropdown) && uiState.activeTab === 3) {
536
- fetchWorks();
537
- } else if (open && (enableSidebar || enableNavDropdown)) {
538
- projectActions.setProjectsLoaded(false);
539
- fetchProjects();
540
- }
541
- }, [
542
- open,
543
- enableSidebar,
544
- enableNavDropdown,
545
- uiState.activeTab,
546
- projectActions,
547
- fetchProjects,
548
- fetchWorks
549
- ]);
550
-
551
- // Fetch works when right sidebar opens
552
- useEffect(() => {
553
- if (open && uiState.isRightSidebarOpen) {
554
- fetchWorks();
555
- }
556
- }, [open, uiState.isRightSidebarOpen, fetchWorks]);
557
-
558
- const updateProject = async () => {
559
- await updateProjectApi(apiClient, projectState, projectActions, fetchProjects, t);
560
- };
561
-
562
- const deleteProject = async () => {
563
- await deleteProjectApi(apiClient, projectState, projectActions, fetchProjects, t);
564
- };
565
-
566
- const loadThreadMessages = async (threadId: string) => {
567
- await loadThreadMessagesApi(apiClient, threadId, setMessages);
568
- };
569
-
570
- const handleSessionClick = async (session: AssistantSession) => {
571
- sessionActions.selectSessionId(session.id);
572
- sessionActions.selectSession(session);
573
- uiActions.setActiveTab(0); // Switch to Home tab when a session is clicked
574
- await loadThreadMessages(session.id);
575
- };
576
-
577
- const createNewThread = () => {
578
- sessionActions.selectSessionId(null);
579
- sessionActions.selectSession(null);
580
- uiActions.setActiveTab(0); // Switch to Home tab when creating a new thread
581
- setMessages([]); // Clear messages for new thread
582
- };
583
-
584
- const handleSendMessage = async (e?: FormEvent, options?: any) => {
585
- e?.preventDefault();
586
- if (!input.trim()) return;
587
-
588
- // Store options in ref for access in prepareSendMessagesRequest
589
- messageOptionsRef.current = options;
590
-
591
- const messageText = input.trim();
592
- let currentThreadId = selectedSessionIdRef.current;
593
-
594
- // If no thread ID exists, create a new one
595
- if (!currentThreadId) {
596
- const subject = messageText.substring(0, 100);
597
-
598
- const newThread = await createThread(apiClient, {
599
- subject,
600
- body_text: messageText,
601
- sender_name: userDisplayName,
602
- deploymentId,
603
- tenantAiAgentId,
604
- projectId: projectState.projectContext?.id
605
- });
606
-
607
- if (newThread) {
608
- currentThreadId = newThread.id;
609
-
610
- // Update the selected session ID
611
- sessionActions.selectSessionId(currentThreadId);
612
- selectedSessionIdRef.current = currentThreadId;
613
-
614
- // Add the new session to the list
615
- const session: AssistantSession = {
616
- id: newThread.id,
617
- title: newThread.subject || subject,
618
- messages: [],
619
- createdAt: new Date(newThread.created_on || new Date()),
620
- updatedAt: new Date(newThread.last_modified_on || new Date())
621
- };
622
-
623
- sessionActions.addSession(session);
624
-
625
- fetchThreads();
626
-
627
- if (projectState.projectContext?.id) {
628
- fetchProjectThreads(projectState.projectContext.id);
629
- }
630
- } else {
631
- return;
632
- }
633
- }
634
-
635
- onMessageSend?.(messageText);
636
- setInput('');
637
-
638
- // Upload files to thread storage via REST API
639
- const rawFiles: { url: string; mediaType: string; filename: string }[] = options?.files || [];
640
- const filePaths: string[] = [];
641
-
642
- if (rawFiles.length > 0 && currentThreadId) {
643
- for (const rawFile of rawFiles) {
644
- try {
645
- const file = dataUrlToFile(rawFile.url, rawFile.filename);
646
- const path = await uploadThreadFile(apiClient, currentThreadId, file);
647
-
648
- if (path) {
649
- filePaths.push(path);
650
- }
651
- } catch (error) {
652
- console.error('[FILE_UPLOAD] Error:', error);
653
- }
654
- }
655
- }
656
-
657
- // Store file paths in ref for prepareSendMessagesRequest
658
- messageOptionsRef.current = {
659
- ...options,
660
- filePaths: filePaths.length > 0 ? filePaths : undefined
661
- };
662
-
663
- await sendMessage({
664
- text: messageText,
665
- metadata: {
666
- modelId: options?.modelId,
667
- supportMultipleModels: options?.supportMultipleModels,
668
- supportFiles: options?.supportFiles,
669
- supportWebSearch: options?.supportWebSearch,
670
- supportDeepResearch: options?.supportDeepResearch,
671
- supportDocumentSearch: options?.supportDocumentSearch,
672
- supportThinking: options?.supportThinking,
673
- supportWorkCanvas: options?.supportWorkCanvas
674
- }
675
- });
676
- };
677
-
678
- const handleInputChange = useCallback((e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
679
- setInput(e.target.value);
680
- }, []);
681
-
682
- const handleProjectMessage = async (e?: FormEvent) => {
683
- e?.preventDefault();
684
- if (!input.trim() || !projectState.selectedProject) return;
685
-
686
- // Clear everything and switch to home tab
687
- sessionActions.selectSessionId(null);
688
- selectedSessionIdRef.current = null;
689
- setMessages([]);
690
- uiActions.setActiveTab(0);
691
-
692
- const originalMessage = input.trim();
693
-
694
- // Store project context for breadcrumb
695
- projectActions.setContext({
696
- name: projectState.selectedProject.name,
697
- id: projectState.selectedProject.id,
698
- message: originalMessage
699
- });
700
-
701
- // Set input and use existing handleSendMessage logic
702
- setInput(originalMessage);
703
-
704
- // Small delay to ensure state updates, then send message
705
- setTimeout(async () => {
706
- // Double-check that session is cleared before sending
707
- sessionActions.selectSessionId(null);
708
- selectedSessionIdRef.current = null;
709
- await handleSendMessage();
710
- }, 200);
711
- };
712
-
713
- const handleDockLeft = () => {
714
- // If in inline mode, switch to modal mode when docking
715
- if (isInlineMode) {
716
- uiActions.setRenderMode('modal');
717
- uiActions.setDocked(true);
718
- uiActions.setExpanded(false);
719
-
720
- return;
721
- }
722
- // If currently docked and original renderMode was inline, return to inline
723
- if (uiState.isDocked && renderMode === 'inline') {
724
- uiActions.setRenderMode('inline');
725
- uiActions.setDocked(false);
726
- uiActions.setExpanded(false);
727
-
728
- return;
729
- }
730
- uiActions.setDocked(!uiState.isDocked);
731
- uiActions.setExpanded(false);
732
- };
733
-
734
- const handleExpand = () => {
735
- // In inline mode, always toggle fullscreen overlay (InlineView handles it via portal)
736
- if (isInlineMode) {
737
- setIsInlineFullscreen(prev => !prev);
738
-
739
- return;
740
- }
741
- // If currently expanded and original renderMode was inline, return to inline
742
- if (uiState.isExpanded && renderMode === 'inline') {
743
- uiActions.setRenderMode('inline');
744
- uiActions.setExpanded(false);
745
- uiActions.setDocked(false);
746
-
747
- return;
748
- }
749
- uiActions.setExpanded(!uiState.isExpanded);
750
- uiActions.setDocked(false);
751
- };
752
-
753
- const handleCreateProject = async (e: FormEvent) => {
754
- e.preventDefault();
755
-
756
- if (!projectState.form.name.trim()) return;
757
-
758
- projectActions.setCreating(true);
759
-
760
- try {
761
- const response: any = await apiClient.post('/ai/projects', {
762
- name: projectState.form.name,
763
- tenant_ai_agent_id: tenantAiAgentId,
764
- description: projectState.form.description || undefined
765
- });
766
-
767
- if (response.success && response.data) {
768
- const newProject = response.data;
769
-
770
- // Set the selected project
771
- projectActions.selectProject(newProject);
772
-
773
- // Reset form
774
- projectActions.setForm({ name: '', description: '' });
775
-
776
- // Navigate to conversation view
777
- projectActions.setView('conversation');
778
-
779
- // Refresh projects list
780
- fetchProjects();
781
- }
782
- } catch (error) {
783
- console.error('Failed to create project:', error);
784
- // Could add error handling/toast here
785
- } finally {
786
- projectActions.setCreating(false);
787
- }
788
- };
789
-
790
- const updateSession = async () => {
791
- await updateSessionApi(
792
- apiClient,
793
- sessionState,
794
- sessionActions,
795
- fetchThreads,
796
- fetchProjectThreads,
797
- projectState.projectContext?.id || null,
798
- projectState.selectedProject?.id || null,
799
- t
800
- );
801
- };
802
-
803
- const deleteSession = async () => {
804
- await deleteSessionApi(
805
- apiClient,
806
- sessionState,
807
- sessionActions,
808
- fetchThreads,
809
- fetchProjectThreads,
810
- projectState.projectContext?.id || null,
811
- projectState.selectedProject?.id || null,
812
- t
813
- );
814
- };
815
-
816
- const updateProjectInstructions = async () => {
817
- await updateProjectInstructionsApi(apiClient, projectState, projectActions, fetchProjects, t);
818
- };
819
-
820
- // Filter sessions based on search query
821
- const filteredSessions = sessionState.sessions.filter((session) => {
822
- if (!sessionState.searchQuery.trim()) return true;
823
-
824
- const query = sessionState.searchQuery.toLowerCase();
825
-
826
- return session.title.toLowerCase().includes(query);
827
- });
828
-
829
- // Sidebar content render helper
830
- const renderSidebarContent = () => (
831
- <SidebarContent
832
- sessions={sessionState.sessions}
833
- works={worksState.works}
834
- projects={projectState.projects}
835
- selectedSessionId={sessionState.selectedSessionId}
836
- selectedWorkId={worksState.selectedWork?.id}
837
- activeTab={uiState.activeTab}
838
- onNewChat={() => {
839
- createNewThread();
840
- projectActions.setContext(null);
841
- }}
842
- onTabChange={uiActions.setActiveTab}
843
- onSessionClick={handleSessionClick}
844
- onDeleteSession={(session) => {
845
- sessionActions.selectSession(session);
846
- sessionActions.setDeleteDialogOpen(true);
847
- }}
848
- onWorkClick={async (work) => {
849
- if (work.base_thread_id) {
850
- sessionActions.selectSessionId(work.base_thread_id);
851
- uiActions.setActiveTab(0);
852
- await loadThreadMessages(work.base_thread_id);
853
- }
854
- }}
855
- onProjectClick={(project) => {
856
- projectActions.selectProject(project);
857
- projectActions.setView('conversation');
858
- uiActions.setActiveTab(2);
859
- }}
860
- onShowMoreProjects={() => {
861
- uiActions.setActiveTab(2);
862
- projectActions.setView('list');
863
- }}
864
- onNewProject={() => {
865
- uiActions.setActiveTab(2);
866
- projectActions.setView('create');
867
- }}
868
- onToggleSidebar={uiState.isExpanded ? undefined : () => uiActions.setSidebarOpen(false)}
869
- t={t} />
870
- );
871
-
872
- const handleWorkNavigation = (work: Work | null) => {
873
- worksActions.selectWork(work);
874
- uiActions.setRightSidebarView(work ? 'detail' : 'list');
875
- };
876
-
877
- // Inline mode: render directly without Dialog wrapper
878
- if (isInlineMode) {
879
- return (
880
- <>
881
- <InlineView
882
- ref={ref as Ref<HTMLDivElement>}
883
- open={open}
884
- onOpenChange={onOpenChange}
885
- messages={messages}
886
- isLoading={isLoading}
887
- input={input}
888
- onInputChange={handleInputChange}
889
- onSendMessage={handleSendMessage}
890
- onStop={stop}
891
- title={agentDetails?.name ?? title}
892
- description={description}
893
- welcomeMessage={agentDetails?.welcomeMessage}
894
- isLoadingAgent={isLoadingAgentDetails}
895
- logo={agentDetails?.logo ?? logo}
896
- placeholder={placeholder}
897
- footerText={footerText}
898
- className={className}
899
- userPhoto={userPhoto}
900
- userDisplayName={userDisplayName}
901
- enableSidebar={isInlineFullscreen ? true : enableSidebar}
902
- supportFiles={supportFiles}
903
- supportWebSearch={supportWebSearch}
904
- supportDocumentSearch={supportDocumentSearch}
905
- supportDeepResearch={supportDeepResearch}
906
- supportThinking={supportThinking}
907
- supportWorkCanvas={supportWorkCanvas}
908
- supportMultiModels={supportMultiModels}
909
- deploymentId={deploymentId}
910
- tenantAiAgentId={tenantAiAgentId}
911
- enableMicrophone={enableMicrophone}
912
- enableVoice={enableVoice}
913
- isRecording={isRecording}
914
- recognition={recognition}
915
- onMicrophoneClick={handleMicrophoneClick}
916
- isSidebarOpen={uiState.isSidebarOpen}
917
- onToggleSidebar={() => uiActions.setSidebarOpen(!uiState.isSidebarOpen)}
918
- renderSidebar={renderSidebarContent}
919
- activeTab={uiState.activeTab}
920
- onTabChange={uiActions.setActiveTab}
921
- onNewChat={() => {
922
- createNewThread();
923
- projectActions.setContext(null);
924
- }}
925
- enableNavDropdown={isInlineFullscreen ? false : enableNavDropdown}
926
- isFullscreen={isInlineFullscreen}
927
- onExpand={hideExpand ? undefined : handleExpand}
928
- renderThreadHeader={sessionState.selectedSession && sessionState.selectedSession.id === sessionState.selectedSessionId ? () => (
929
- <ThreadHeaderInline
930
- session={sessionState.selectedSession!}
931
- projects={projectState.projects}
932
- projectContext={projectState.projectContext}
933
- isFullscreen={isInlineFullscreen}
934
- onSaveTitle={async (newTitle) => {
935
- await updateSessionApi(
936
- apiClient,
937
- { ...sessionState, name: newTitle },
938
- sessionActions,
939
- fetchThreads,
940
- fetchProjectThreads,
941
- projectState.projectContext?.id || null,
942
- projectState.selectedProject?.id || null,
943
- t
944
- );
945
- sessionActions.updateSession({ ...sessionState.selectedSession!, title: newTitle });
946
- }}
947
- onDelete={() => sessionActions.setDeleteDialogOpen(true)}
948
- onArchive={async () => {
949
- try {
950
- await apiClient.patch(
951
- `/apps/base/data-sources/thread/items/${sessionState.selectedSession!.id}`,
952
- { archived: true }
953
- );
954
- sessionActions.removeSession(sessionState.selectedSession!.id);
955
- sessionActions.selectSession(null);
956
- sessionActions.selectSessionId(null);
957
- toast.success(t('toast.session_archived_success'));
958
- } catch {
959
- toast.error(t('toast.failed_archive_session'));
960
- }
961
- }}
962
- onMoveToProject={async (projectId) => {
963
- try {
964
- await apiClient.patch(
965
- `/apps/base/data-sources/thread/items/${sessionState.selectedSession!.id}`,
966
- { tenant_ai_project_id: projectId }
967
- );
968
- sessionActions.removeSession(sessionState.selectedSession!.id);
969
- sessionActions.selectSession(null);
970
- sessionActions.selectSessionId(null);
971
- toast.success(t('toast.session_moved_success'));
972
- } catch {
973
- toast.error(t('toast.failed_move_session'));
974
- }
975
- }}
976
- onProjectContextClick={() => {
977
- const project = projectState.projects.find(p => p.id === projectState.projectContext!.id);
978
-
979
- if (project) {
980
- projectActions.selectProject(project);
981
- projectActions.setView('conversation');
982
- uiActions.setActiveTab(2);
983
- projectActions.setContext(null);
984
- }
985
- }}
986
- t={t} />
987
- ) : undefined}
988
- renderSessionsView={() => (
989
- <SessionsListView
990
- sessions={sessionState.sessions}
991
- filteredSessions={filteredSessions}
992
- searchQuery={sessionState.searchQuery}
993
- onSearchChange={sessionActions.setSearchQuery}
994
- onNewSession={createNewThread}
995
- onSessionClick={handleSessionClick}
996
- onEditSession={(session) => {
997
- sessionActions.selectSession(session);
998
- sessionActions.setName(session.title || '');
999
- sessionActions.startEditing();
1000
- }}
1001
- onDeleteSession={(session) => {
1002
- sessionActions.selectSession(session);
1003
- sessionActions.setDeleteDialogOpen(true);
1004
- }}
1005
- t={t} />
1006
- )}
1007
- renderProjectsView={() => (
1008
- projectState.view === 'create' ? (
1009
- <div className="flex-1 flex flex-col p-6">
1010
- <div className="flex items-center gap-4 mb-6">
1011
- {projectState.projects && projectState.projects.length > 0 && (
1012
- <Button
1013
- variant="ghost"
1014
- size="icon"
1015
- onClick={() => projectActions.setView('list')}
1016
- className="text-muted-foreground cursor-pointer hover:text-foreground">
1017
- <ArrowLeft className="w-5 h-5" />
1018
- </Button>
1019
- )}
1020
- </div>
1021
- <div className="max-w-lg mx-auto w-full">
1022
- <h1 className="text-2xl font-semibold mb-4 ps-2">{t('dialogs.create_project')}</h1>
1023
- <form onSubmit={handleCreateProject} className="grid grid-cols-1 gap-4">
1024
- <div>
1025
- <label className="text-sm ps-2 mb-1 block font-base">
1026
- {t('placeholders.what_are_you_working')}
1027
- </label>
1028
- <Input
1029
- value={projectState.form.name}
1030
- onChange={(e: ChangeEvent<HTMLInputElement>) => projectActions.setForm({ ...projectState.form, name: e.target.value })}
1031
- className="border border-border-300 hover:border-border-200 transition-colors font-large h-11 px-3 rounded-[0.6rem] w-full"
1032
- placeholder={t('placeholders.name_your_project')}
1033
- required />
1034
- </div>
1035
- <div>
1036
- <label className="text-sm ps-2 mb-1 block font-base">
1037
- {t('placeholders.what_are_you_trying')}
1038
- </label>
1039
- <Textarea
1040
- value={projectState.form.description}
1041
- onChange={(e: ChangeEvent<HTMLTextAreaElement>) => projectActions.setForm({ ...projectState.form, description: e.target.value })}
1042
- className="border border-border-300 min-h-25 resize-none p-2 hover:border-border-200 transition-colors font-large px-3 rounded-[0.6rem] w-full"
1043
- placeholder={t('placeholders.describe_project')} />
1044
- </div>
1045
- <div className="mt-2 flex justify-end gap-2">
1046
- <Button
1047
- type="button"
1048
- variant="outline"
1049
- onClick={() => projectActions.setView('list')}>
1050
- {t('buttons.cancel')}
1051
- </Button>
1052
- <Button
1053
- type="submit"
1054
- disabled={projectState.isCreating || !projectState.form.name.trim()}>
1055
- {projectState.isCreating ? t('buttons.creating') : t('buttons.create_project')}
1056
- </Button>
1057
- </div>
1058
- </form>
1059
- </div>
1060
- </div>
1061
- ) : projectState.view === 'conversation' && projectState.selectedProject ? (
1062
- <AssistantProjectDetailView
1063
- project={projectState.selectedProject}
1064
- projectThreads={projectState.projectThreads}
1065
- projectWorks={projectState.projectWorks}
1066
- onBack={() => projectActions.setView('list')}
1067
- onEditInstructions={(instructions) => {
1068
- projectActions.setInstructions(instructions);
1069
- projectActions.startEditingInstructions();
1070
- }}
1071
- onProjectMessage={handleProjectMessage}
1072
- onThreadClick={(thread) => {
1073
- sessionActions.selectSessionId(thread.id);
1074
- sessionActions.selectSession(thread);
1075
- projectActions.setContext({
1076
- name: projectState.selectedProject!.name,
1077
- id: projectState.selectedProject!.id,
1078
- message: thread.title || 'Untitled'
1079
- });
1080
- loadThreadMessages(thread.id);
1081
- uiActions.setActiveTab(0);
1082
- }}
1083
- onEditSession={(thread) => {
1084
- sessionActions.selectSession(thread);
1085
- sessionActions.setName(thread.title || '');
1086
- sessionActions.startEditing();
1087
- }}
1088
- onDeleteSession={(thread) => {
1089
- sessionActions.selectSession(thread);
1090
- sessionActions.setDeleteDialogOpen(true);
1091
- }}
1092
- input={input}
1093
- onChange={handleInputChange}
1094
- isLoading={isLoading}
1095
- supportFiles={supportFiles}
1096
- supportWebSearch={supportWebSearch}
1097
- supportDocumentSearch={supportDocumentSearch}
1098
- supportDeepResearch={supportDeepResearch}
1099
- supportThinking={supportThinking}
1100
- supportWorkCanvas={supportWorkCanvas}
1101
- supportMultiModels={supportMultiModels}
1102
- deploymentId={deploymentId}
1103
- enableMicrophone={enableMicrophone}
1104
- enableVoice={enableVoice}
1105
- isRecording={isRecording}
1106
- recognition={recognition}
1107
- onMicrophoneClick={handleMicrophoneClick} />
1108
- ) : (
1109
- <AssistantProjectsPanel
1110
- projects={projectState.projects}
1111
- searchQuery={projectSearchQuery}
1112
- onSearchChange={setProjectSearchQuery}
1113
- onNewProject={() => projectActions.setView('create')}
1114
- onProjectClick={(project) => {
1115
- projectActions.selectProject(project);
1116
- projectActions.setView('conversation');
1117
- }}
1118
- onEditProject={(project) => {
1119
- projectActions.selectProject(project);
1120
- projectActions.setName(project.name || '');
1121
- projectActions.setDescription(project.description || '');
1122
- projectActions.startEditing();
1123
- }}
1124
- onDeleteProject={(project) => {
1125
- projectActions.selectProject(project);
1126
- projectActions.setDeleteDialogOpen(true);
1127
- }}
1128
- compact={enableNavDropdown}
1129
- t={t} />
1130
- )
1131
- )}
1132
- renderMemoriesView={() => <MemoriesPanel t={t} />}
1133
- renderWorksView={() => (
1134
- worksState.detailViewWork ? (
1135
- <div className="flex-1 flex flex-col overflow-y-auto">
1136
- <AssistantWorkDetailView
1137
- work={worksState.detailViewWork}
1138
- onClose={() => worksActions.setDetailViewWork(null)}
1139
- onBack={() => worksActions.setDetailViewWork(null)} />
1140
- </div>
1141
- ) : (
1142
- <div className="flex-1 flex flex-col overflow-y-auto p-6">
1143
- {/* Header */}
1144
- <div className="flex items-center justify-between mb-6">
1145
- <div className="flex flex-col min-w-0">
1146
- <h1 className="text-2xl font-semibold ps-2">{t('tabs.works')}</h1>
1147
- <div className="text-sm text-muted-foreground ps-2">{t('descriptions.manage_works')}</div>
1148
- </div>
1149
- <Button size="sm" className="shrink-0">
1150
- <Plus className="w-4 h-4" />
1151
- {t('buttons.new_work')}
1152
- </Button>
1153
- </div>
1154
-
1155
- {/* Search */}
1156
- <div className="flex items-center gap-4 mb-6">
1157
- <div className="relative flex-1">
1158
- <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
1159
- <Input
1160
- type="text"
1161
- placeholder={t('placeholders.search_works')}
1162
- value={worksState.searchQuery}
1163
- onChange={(e: ChangeEvent<HTMLInputElement>) => worksActions.setSearchQuery(e.target.value)}
1164
- className="pl-10 pr-4 py-2 w-full border border-border rounded-lg focus:outline-none" />
1165
- </div>
1166
- </div>
1167
-
1168
- {/* Works Tabs */}
1169
- <Tabs value={worksState.tabValue} onValueChange={worksActions.setTabValue}>
1170
- <TabsList className="mb-4 p-1">
1171
- <TabsTrigger className="py-2" value="1">
1172
- {t('tabs.my_works')}
1173
- </TabsTrigger>
1174
- <TabsTrigger className="py-2" value="2">
1175
- {t('tabs.shared_to_me')}
1176
- </TabsTrigger>
1177
- <TabsTrigger className="py-2" value="3">
1178
- {t('tabs.library')}
1179
- </TabsTrigger>
1180
- </TabsList>
1181
- <TabsContent value="1">
1182
- {worksState.works.length === 0 ? (
1183
- <div className="text-center py-8 text-muted-foreground">{t('messages.no_works_start')}</div>
1184
- ) : (
1185
- <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
1186
- {worksState.works
1187
- .filter(
1188
- work => work.title.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1189
- || work.content_text?.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1190
- )
1191
- .map(work => (
1192
- <WorkCard
1193
- key={work.id}
1194
- work={work}
1195
- onClick={async () => {
1196
- if (work.base_thread_id) {
1197
- sessionActions.selectSessionId(work.base_thread_id);
1198
- uiActions.setActiveTab(0);
1199
- await loadThreadMessages(work.base_thread_id);
1200
- }
1201
- }}
1202
- t={t} />
1203
- ))}
1204
- </div>
1205
- )}
1206
- </TabsContent>
1207
- <TabsContent value="2">
1208
- {(() => {
1209
- const sharedWorks = worksState.works.filter(
1210
- work => work.shared_to
1211
- && Array.isArray(work.shared_to)
1212
- && currentUserId
1213
- && work.shared_to.includes(currentUserId)
1214
- );
1215
-
1216
- return sharedWorks.length === 0 ? (
1217
- <div className="text-center py-8 text-muted-foreground">
1218
- {t('messages.no_works_shared')}
1219
- </div>
1220
- ) : (
1221
- <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
1222
- {worksState.works
1223
- .filter((work) => {
1224
- const isSharedToMe
1225
- = work.shared_to
1226
- && Array.isArray(work.shared_to)
1227
- && currentUserId
1228
- && work.shared_to.includes(currentUserId);
1229
- const matchesSearch
1230
- = work.title.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1231
- || work.content_text?.toLowerCase().includes(worksState.searchQuery.toLowerCase());
1232
-
1233
- return isSharedToMe && matchesSearch;
1234
- })
1235
- .map(work => (
1236
- <WorkCard
1237
- key={work.id}
1238
- work={work}
1239
- onClick={async () => {
1240
- if (work.base_thread_id) {
1241
- sessionActions.selectSessionId(work.base_thread_id);
1242
- uiActions.setActiveTab(0);
1243
- await loadThreadMessages(work.base_thread_id);
1244
- }
1245
- }}
1246
- t={t} />
1247
- ))}
1248
- </div>
1249
- );
1250
- })()}
1251
- </TabsContent>
1252
- <TabsContent value="3">
1253
- {worksState.works.length === 0 ? (
1254
- <div className="text-center py-8 text-muted-foreground">{t('messages.no_works_found')}</div>
1255
- ) : (
1256
- <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
1257
- {worksState.works
1258
- .filter(
1259
- work => work.title.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1260
- || work.content_text?.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1261
- )
1262
- .map(work => (
1263
- <WorkCard
1264
- key={work.id}
1265
- work={work}
1266
- onClick={() => worksActions.setDetailViewWork(work)}
1267
- t={t} />
1268
- ))}
1269
- </div>
1270
- )}
1271
- </TabsContent>
1272
- </Tabs>
1273
- </div>
1274
- )
1275
- )}
1276
- onToolAction={handleToolAction}
1277
- threadId={sessionState.selectedSessionId ?? undefined}
1278
- agentSelectorUrl={agentSelectorUrl}
1279
- baseAgentSelectorUrl={baseAgentSelectorUrl}
1280
- onAgentChange={(agentId, agentType) => {
1281
- createNewThread();
1282
- onAgentChange?.(agentId, agentType);
1283
- }}
1284
- t={t} />
1285
- <AssistantDialogs
1286
- projectState={projectState}
1287
- projectActions={projectActions}
1288
- sessionState={sessionState}
1289
- sessionActions={sessionActions}
1290
- onUpdateProject={updateProject}
1291
- onDeleteProject={deleteProject}
1292
- onUpdateSession={updateSession}
1293
- onDeleteSession={deleteSession}
1294
- onUpdateProjectInstructions={updateProjectInstructions}
1295
- t={t} />
1296
- </>
1297
- );
1298
- }
1299
-
1300
- // Modal mode: render with Dialog wrapper (original implementation)
1301
- return (
1302
- <>
1303
- <Dialog open={open} onOpenChange={onOpenChange}>
1304
- <DialogTitle className="sr-only">DocyAssistant</DialogTitle>
1305
- <DialogContent
1306
- className={cn(getDialogClassName({ isExpanded: uiState.isExpanded, isDocked: uiState.isDocked }), className, '@container')}
1307
- data-docked={uiState.isDocked}
1308
- data-expanded={uiState.isExpanded}
1309
- data-container-query="true"
1310
- showCloseButton={!uiState.isRightSidebarOpen}
1311
- style={{
1312
- position: 'fixed',
1313
- ...(uiState.isExpanded && {
1314
- top: 0,
1315
- left: 0,
1316
- right: 0,
1317
- bottom: 0,
1318
- width: '100vw',
1319
- height: '100vh',
1320
- transform: 'none',
1321
- maxWidth: 'none',
1322
- maxHeight: 'none'
1323
- }),
1324
- ...(uiState.isDocked && {
1325
- top: 0,
1326
- left: 0,
1327
- width: '700px',
1328
- height: '100vh',
1329
- transform: 'none',
1330
- maxWidth: 'none',
1331
- maxHeight: 'none'
1332
- })
1333
- }}
1334
- {...props}
1335
- ref={ref}>
1336
- {/* Floating Sidebar with Overlay - shown only when container < 3xl */}
1337
- {enableSidebar && uiState.isSidebarOpen && (
1338
- <>
1339
- {/* Background Mask */}
1340
- <div
1341
- className={cn(
1342
- 'absolute inset-0 bg-black/30 z-99',
1343
- // Show only when container is smaller than 3xl
1344
- '@3xl:hidden'
1345
- )}
1346
- onClick={() => uiActions.setSidebarOpen(false)} />
1347
-
1348
- {/* Floating Sidebar */}
1349
- <div
1350
- className={cn(
1351
- 'absolute left-0 top-0 h-full w-72 rounded-lg z-100 shadow-xl',
1352
- // Show floating sidebar when container is smaller than 3xl
1353
- '@3xl:hidden'
1354
- )}
1355
- onMouseLeave={() => {
1356
- // Auto-close when mouse leaves sidebar (not in fullscreen/expanded mode)
1357
- if (uiState.isExpanded) return;
1358
- if (sidebarCloseTimeoutRef.current) {
1359
- clearTimeout(sidebarCloseTimeoutRef.current);
1360
- }
1361
- sidebarCloseTimeoutRef.current = setTimeout(() => {
1362
- uiActions.setSidebarOpen(false);
1363
- }, 300);
1364
- }}
1365
- onMouseEnter={() => {
1366
- // Clear any pending close timeout when mouse enters
1367
- if (sidebarCloseTimeoutRef.current) {
1368
- clearTimeout(sidebarCloseTimeoutRef.current);
1369
- sidebarCloseTimeoutRef.current = null;
1370
- }
1371
- }}>
1372
- <div className="w-full h-full flex flex-col">
1373
- {/* Header with Close Button */}
1374
- <div className="flex items-center justify-end h-10 px-2 border-b">
1375
- {/* Close Button */}
1376
- <Button
1377
- variant="ghost"
1378
- size="icon"
1379
- onClick={() => uiActions.setSidebarOpen(false)}
1380
- className="h-8 w-8 text-muted-foreground hover:text-foreground">
1381
- <X className="w-4 h-4" />
1382
- </Button>
1383
- </div>
1384
-
1385
- <div className="flex-1 p-4 flex flex-col min-h-0">
1386
- {renderSidebarContent()}
1387
- </div>
1388
- </div>
1389
- </div>
1390
- </>
1391
- )}
1392
-
1393
- <div className={getContainerClassName({ isExpanded: uiState.isExpanded, isDocked: uiState.isDocked })}>
1394
- {/* Top Left Controls - Sidebar Toggle & New Thread */}
1395
- {enableSidebar && (
1396
- <div className="absolute z-10 top-2.5 left-3 flex items-center gap-2">
1397
- {/* Sidebar Toggle */}
1398
- <Button
1399
- variant="ghost"
1400
- size="icon"
1401
- onClick={() => uiActions.setSidebarOpen(!uiState.isSidebarOpen)}
1402
- data-sidebar-open={uiState.isSidebarOpen}
1403
- className={cn(
1404
- 'h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground rounded-md transition-all duration-300',
1405
- // Hide button when sidebar is open
1406
- uiState.isSidebarOpen && 'opacity-0 pointer-events-none'
1407
- )}>
1408
- <PanelLeft
1409
- className={cn(
1410
- 'w-5 h-5 transition-transform duration-300',
1411
- uiState.isSidebarOpen && 'scale-x-[-1]'
1412
- )} />
1413
- </Button>
1414
-
1415
- {/* New Thread Button */}
1416
- {!uiState.isSidebarOpen && (
1417
- <Button
1418
- variant="ghost"
1419
- size="icon"
1420
- onClick={createNewThread}
1421
- className="h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground rounded-md transition-all duration-300"
1422
- title={t('buttons.start_new_thread')}>
1423
- <Edit className="w-5 h-5" />
1424
- </Button>
1425
- )}
1426
- </div>
1427
- )}
1428
-
1429
- {/* Top Right Controls - Dock & Expand */}
1430
- {!uiState.isRightSidebarOpen && (
1431
- <div className="absolute top-2 right-12 flex items-center gap-2 z-50">
1432
- {!uiState.isExpanded && (
1433
- <Button
1434
- variant="ghost"
1435
- size="icon"
1436
- className="h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
1437
- onClick={handleDockLeft}
1438
- title={uiState.isDocked ? t('actions.undock') : t('actions.dock_left')}>
1439
- {uiState.isDocked ? <ArrowRight className="w-5 h-5" /> : <ArrowLeft className="w-5 h-5" />}
1440
- </Button>
1441
- )}
1442
-
1443
- {!uiState.isDocked && (
1444
- <Button
1445
- variant="ghost"
1446
- size="icon"
1447
- className="h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
1448
- onClick={handleExpand}
1449
- title={uiState.isExpanded ? t('actions.minimize') : t('actions.expand')}>
1450
- {uiState.isExpanded ? <Minimize2 className="w-5 h-5" /> : <Maximize2 className="w-5 h-5" />}
1451
- </Button>
1452
- )}
1453
- </div>
1454
- )}
1455
-
1456
- {/* Inline Sidebar - shown only when container >= 3xl */}
1457
- {enableSidebar && (
1458
- <div
1459
- className={cn(
1460
- 'relative flex-none transition-all duration-300',
1461
- // Show inline sidebar when container is 3xl or larger (48rem/768px)
1462
- 'hidden @3xl:block',
1463
- uiState.isSidebarOpen ? 'w-72' : 'w-0 overflow-hidden'
1464
- )}>
1465
- <div className="w-72 h-full flex flex-col">
1466
- <div className="flex-1 p-4 flex flex-col min-h-0">
1467
- {renderSidebarContent()}
1468
- </div>
1469
- </div>
1470
- </div>
1471
- )}
1472
-
1473
- {/* Main Content Area */}
1474
- <div className={cn('flex-1 flex flex-col bg-background', uiState.isDocked ? 'min-w-0' : 'min-w-96')}>
1475
- {/* Conditionally render content based on activeTab */}
1476
- {uiState.activeTab === 1 ? (
1477
- <SessionsListView
1478
- sessions={sessionState.sessions}
1479
- filteredSessions={filteredSessions}
1480
- searchQuery={sessionState.searchQuery}
1481
- onSearchChange={sessionActions.setSearchQuery}
1482
- onNewSession={createNewThread}
1483
- onSessionClick={handleSessionClick}
1484
- onEditSession={(session) => {
1485
- sessionActions.selectSession(session);
1486
- sessionActions.setName(session.title || '');
1487
- sessionActions.startEditing();
1488
- }}
1489
- onDeleteSession={(session) => {
1490
- sessionActions.selectSession(session);
1491
- sessionActions.setDeleteDialogOpen(true);
1492
- }}
1493
- t={t} />
1494
- ) : uiState.activeTab === 2 ? (
1495
- /* Projects View */
1496
- projectState.view === 'list' ? (
1497
- <AssistantProjectsPanel
1498
- projects={projectState.projects}
1499
- searchQuery={projectSearchQuery}
1500
- onSearchChange={setProjectSearchQuery}
1501
- onNewProject={() => projectActions.setView('create')}
1502
- onProjectClick={(project) => {
1503
- projectActions.selectProject(project);
1504
- projectActions.setView('conversation');
1505
- }}
1506
- onEditProject={(project) => {
1507
- projectActions.selectProject(project);
1508
- projectActions.setName(project.name || '');
1509
- projectActions.setDescription(project.description || '');
1510
- projectActions.startEditing();
1511
- }}
1512
- onDeleteProject={(project) => {
1513
- projectActions.selectProject(project);
1514
- projectActions.setDeleteDialogOpen(true);
1515
- }}
1516
- compact={enableNavDropdown}
1517
- t={t} />
1518
- ) : projectState.view === 'create' ? (
1519
- /* Create Project Form */
1520
- <div className="flex-1 flex flex-col p-6">
1521
- {/* Header with Back Button */}
1522
- <div className="flex items-center gap-4 mb-6">
1523
- {projectState.projects && projectState.projects.length > 0 && (
1524
- <Button
1525
- variant="ghost"
1526
- size="icon"
1527
- onClick={() => projectActions.setView('list')}
1528
- className="text-muted-foreground cursor-pointer hover:text-foreground">
1529
- <ArrowLeft className="w-5 h-5" />
1530
- </Button>
1531
- )}
1532
- </div>
1533
-
1534
- <div className="max-w-lg mx-auto w-full">
1535
- <h1 className="text-2xl font-semibold mb-4 ps-2">{t('dialogs.create_project')}</h1>
1536
- <form onSubmit={handleCreateProject} className="grid grid-cols-1 gap-4">
1537
- <div>
1538
- <label className="text-sm ps-2 mb-1 block font-base">
1539
- {t('placeholders.what_are_you_working')}
1540
- </label>
1541
- <Input
1542
- value={projectState.form.name}
1543
- onChange={(e: ChangeEvent<HTMLInputElement>) => projectActions.setForm({ ...projectState.form, name: e.target.value })}
1544
- className="border border-border-300 hover:border-border-200 transition-colors font-large h-11 px-3 rounded-[0.6rem] w-full"
1545
- placeholder={t('placeholders.name_your_project')}
1546
- required />
1547
- </div>
1548
-
1549
- <div>
1550
- <label className="text-sm ps-2 mb-1 block font-base">
1551
- {t('placeholders.what_are_you_trying')}
1552
- </label>
1553
- <Textarea
1554
- value={projectState.form.description}
1555
- onChange={(e: ChangeEvent<HTMLTextAreaElement>) => projectActions.setForm({ ...projectState.form, description: e.target.value })}
1556
- className="border border-border-300 min-h-25 resize-none p-2 hover:border-border-200 transition-colors font-large px-3 rounded-[0.6rem] w-full"
1557
- placeholder={t('placeholders.describe_project')} />
1558
- </div>
1559
-
1560
- <div className="mt-2 flex justify-end gap-2">
1561
- <Button
1562
- type="button"
1563
- variant="outline"
1564
- onClick={() => projectActions.setView('list')}>
1565
- {t('buttons.cancel')}
1566
- </Button>
1567
- <Button
1568
- type="submit"
1569
- disabled={projectState.isCreating || !projectState.form.name.trim()}>
1570
- {projectState.isCreating ? t('buttons.creating') : t('buttons.create_project')}
1571
- </Button>
1572
- </div>
1573
- </form>
1574
- </div>
1575
- </div>
1576
- ) : (
1577
- /* Project Conversation View */
1578
- projectState.selectedProject && (
1579
- <AssistantProjectDetailView
1580
- project={projectState.selectedProject}
1581
- projectThreads={projectState.projectThreads}
1582
- projectWorks={projectState.projectWorks}
1583
- onBack={() => projectActions.setView('list')}
1584
- onEditInstructions={(instructions) => {
1585
- projectActions.setInstructions(instructions);
1586
- projectActions.startEditingInstructions();
1587
- }}
1588
- onProjectMessage={handleProjectMessage}
1589
- onThreadClick={(thread) => {
1590
- sessionActions.selectSessionId(thread.id);
1591
- sessionActions.selectSession(thread);
1592
- projectActions.setContext({
1593
- name: projectState.selectedProject!.name,
1594
- id: projectState.selectedProject!.id,
1595
- message: thread.title || 'Untitled'
1596
- });
1597
- loadThreadMessages(thread.id);
1598
- uiActions.setActiveTab(0); // Switch to Home tab
1599
- }}
1600
- onEditSession={(thread) => {
1601
- sessionActions.selectSession(thread);
1602
- sessionActions.setName(thread.title || '');
1603
- sessionActions.startEditing();
1604
- }}
1605
- onDeleteSession={(thread) => {
1606
- sessionActions.selectSession(thread);
1607
- sessionActions.setDeleteDialogOpen(true);
1608
- }}
1609
- input={input}
1610
- onChange={handleInputChange}
1611
- isLoading={isLoading}
1612
- supportFiles={supportFiles}
1613
- supportWebSearch={supportWebSearch}
1614
- supportDocumentSearch={supportDocumentSearch}
1615
- supportDeepResearch={supportDeepResearch}
1616
- supportThinking={supportThinking}
1617
- supportWorkCanvas={supportWorkCanvas}
1618
- supportMultiModels={supportMultiModels}
1619
- deploymentId={deploymentId}
1620
- enableMicrophone={enableMicrophone}
1621
- enableVoice={enableVoice}
1622
- isRecording={isRecording}
1623
- recognition={recognition}
1624
- onMicrophoneClick={handleMicrophoneClick} />
1625
- )
1626
- )
1627
- ) : uiState.activeTab === 3 ? (
1628
- /* Works View */
1629
- worksState.detailViewWork ? (
1630
- /* Work Detail View */
1631
- <div className="flex-1 flex flex-col overflow-y-auto">
1632
- <AssistantWorkDetailView
1633
- work={worksState.detailViewWork}
1634
- onClose={() => worksActions.setDetailViewWork(null)}
1635
- onBack={() => worksActions.setDetailViewWork(null)} />
1636
- </div>
1637
- ) : (
1638
- <div className="flex-1 flex flex-col overflow-y-auto p-6">
1639
- {/* Header */}
1640
- <div className="flex items-center justify-between mb-6">
1641
- <div className="flex flex-col min-w-0">
1642
- <h1 className="text-2xl font-semibold ps-2">{t('tabs.works')}</h1>
1643
- <div className="text-sm text-muted-foreground ps-2">{t('descriptions.manage_works')}</div>
1644
- </div>
1645
- <Button size="sm" className="shrink-0">
1646
- <Plus className="w-4 h-4" />
1647
- {t('buttons.new_work')}
1648
- </Button>
1649
- </div>
1650
-
1651
- {/* Search */}
1652
- <div className="flex items-center gap-4 mb-6">
1653
- <div className="relative flex-1">
1654
- <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
1655
- <Input
1656
- type="text"
1657
- placeholder={t('placeholders.search_works')}
1658
- value={worksState.searchQuery}
1659
- onChange={(e: ChangeEvent<HTMLInputElement>) => worksActions.setSearchQuery(e.target.value)}
1660
- className="pl-10 pr-4 py-2 w-full border border-border rounded-lg focus:outline-none" />
1661
- </div>
1662
- </div>
1663
-
1664
- {/* Works Tabs */}
1665
- <Tabs value={worksState.tabValue} onValueChange={worksActions.setTabValue}>
1666
- <TabsList className="mb-4 p-1">
1667
- <TabsTrigger className="py-2" value="1">
1668
- {t('tabs.my_works')}
1669
- </TabsTrigger>
1670
- <TabsTrigger className="py-2" value="2">
1671
- {t('tabs.shared_to_me')}
1672
- </TabsTrigger>
1673
- <TabsTrigger className="py-2" value="3">
1674
- {t('tabs.library')}
1675
- </TabsTrigger>
1676
- </TabsList>
1677
- <TabsContent value="1">
1678
- {worksState.works.length === 0 ? (
1679
- <div className="text-center py-8 text-muted-foreground">{t('messages.no_works_start')}</div>
1680
- ) : (
1681
- <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
1682
- {worksState.works
1683
- .filter(
1684
- work => work.title.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1685
- || work.content_text?.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1686
- )
1687
- .map(work => (
1688
- <WorkCard
1689
- key={work.id}
1690
- work={work}
1691
- onClick={async () => {
1692
- if (work.base_thread_id) {
1693
- sessionActions.selectSessionId(work.base_thread_id);
1694
- uiActions.setActiveTab(0);
1695
- await loadThreadMessages(work.base_thread_id);
1696
- }
1697
- }}
1698
- t={t} />
1699
- ))}
1700
- </div>
1701
- )}
1702
- </TabsContent>
1703
- <TabsContent value="2">
1704
- {(() => {
1705
- const sharedWorks = worksState.works.filter(
1706
- work => work.shared_to
1707
- && Array.isArray(work.shared_to)
1708
- && currentUserId
1709
- && work.shared_to.includes(currentUserId)
1710
- );
1711
-
1712
- return sharedWorks.length === 0 ? (
1713
- <div className="text-center py-8 text-muted-foreground">
1714
- {t('messages.no_works_shared')}
1715
- </div>
1716
- ) : (
1717
- <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
1718
- {worksState.works
1719
- .filter((work) => {
1720
- // Filter by shared_to field containing current user ID
1721
- const isSharedToMe
1722
- = work.shared_to
1723
- && Array.isArray(work.shared_to)
1724
- && currentUserId
1725
- && work.shared_to.includes(currentUserId);
1726
- // Also filter by search query
1727
- const matchesSearch
1728
- = work.title.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1729
- || work.content_text?.toLowerCase().includes(worksState.searchQuery.toLowerCase());
1730
-
1731
- return isSharedToMe && matchesSearch;
1732
- })
1733
- .map(work => (
1734
- <WorkCard
1735
- key={work.id}
1736
- work={work}
1737
- onClick={async () => {
1738
- if (work.base_thread_id) {
1739
- sessionActions.selectSessionId(work.base_thread_id);
1740
- uiActions.setActiveTab(0);
1741
- await loadThreadMessages(work.base_thread_id);
1742
- }
1743
- }}
1744
- t={t} />
1745
- ))}
1746
- </div>
1747
- );
1748
- })()}
1749
- </TabsContent>
1750
- <TabsContent value="3">
1751
- {worksState.works.length === 0 ? (
1752
- <div className="text-center py-8 text-muted-foreground">{t('messages.no_works_found')}</div>
1753
- ) : (
1754
- <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
1755
- {worksState.works
1756
- .filter(
1757
- work => work.title.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1758
- || work.content_text?.toLowerCase().includes(worksState.searchQuery.toLowerCase())
1759
- )
1760
- .map(work => (
1761
- <WorkCard
1762
- key={work.id}
1763
- work={work}
1764
- onClick={() => worksActions.setDetailViewWork(work)}
1765
- t={t} />
1766
- ))}
1767
- </div>
1768
- )}
1769
- </TabsContent>
1770
- </Tabs>
1771
- </div>
1772
- )
1773
- ) : uiState.activeTab === 4 ? (
1774
- /* Memories View */
1775
- <MemoriesPanel t={t} />
1776
- ) : (
1777
- <>
1778
- {sessionState.selectedSession && sessionState.selectedSession.id === sessionState.selectedSessionId && (
1779
- <ThreadHeaderInline
1780
- session={sessionState.selectedSession}
1781
- projects={projectState.projects}
1782
- projectContext={projectState.projectContext}
1783
- onSaveTitle={async (newTitle) => {
1784
- await updateSessionApi(
1785
- apiClient,
1786
- { ...sessionState, name: newTitle },
1787
- sessionActions,
1788
- fetchThreads,
1789
- fetchProjectThreads,
1790
- projectState.projectContext?.id || null,
1791
- projectState.selectedProject?.id || null,
1792
- t
1793
- );
1794
- sessionActions.updateSession({ ...sessionState.selectedSession!, title: newTitle });
1795
- }}
1796
- onDelete={() => sessionActions.setDeleteDialogOpen(true)}
1797
- onArchive={async () => {
1798
- try {
1799
- await apiClient.patch(
1800
- `/apps/base/data-sources/thread/items/${sessionState.selectedSession!.id}`,
1801
- { archived: true }
1802
- );
1803
- sessionActions.removeSession(sessionState.selectedSession!.id);
1804
- sessionActions.selectSession(null);
1805
- sessionActions.selectSessionId(null);
1806
- toast.success(t('toast.session_archived_success'));
1807
- } catch {
1808
- toast.error(t('toast.failed_archive_session'));
1809
- }
1810
- }}
1811
- onMoveToProject={async (projectId) => {
1812
- try {
1813
- await apiClient.patch(
1814
- `/apps/base/data-sources/thread/items/${sessionState.selectedSession!.id}`,
1815
- { tenant_ai_project_id: projectId }
1816
- );
1817
- sessionActions.removeSession(sessionState.selectedSession!.id);
1818
- sessionActions.selectSession(null);
1819
- sessionActions.selectSessionId(null);
1820
- toast.success(t('toast.session_moved_success'));
1821
- } catch {
1822
- toast.error(t('toast.failed_move_session'));
1823
- }
1824
- }}
1825
- onProjectContextClick={() => {
1826
- const project = projectState.projects.find(p => p.id === projectState.projectContext!.id);
1827
-
1828
- if (project) {
1829
- projectActions.selectProject(project);
1830
- projectActions.setView('conversation');
1831
- uiActions.setActiveTab(2);
1832
- projectActions.setContext(null);
1833
- }
1834
- }}
1835
- t={t} />
1836
- )}
1837
- <ConversationView
1838
- messages={messages}
1839
- isLoading={isLoading}
1840
- input={input}
1841
- onInputChange={handleInputChange}
1842
- onSendMessage={handleSendMessage}
1843
- onStop={stop}
1844
- logo={logo}
1845
- userPhoto={userPhoto}
1846
- userDisplayName={userDisplayName}
1847
- description={description}
1848
- title={agentDetails?.name ?? title}
1849
- welcomeMessage={agentDetails?.welcomeMessage}
1850
- isLoadingAgent={isLoadingAgentDetails}
1851
- placeholder={placeholder}
1852
- footerText={footerText}
1853
- onWorkSelect={(work) => {
1854
- worksActions.updateWork(work);
1855
- worksActions.selectWork(work);
1856
- }}
1857
- onOpenRightSidebar={() => {
1858
- uiActions.setRightSidebarOpen(true);
1859
- uiActions.setRightSidebarView('detail');
1860
- }}
1861
- supportFiles={supportFiles}
1862
- supportWebSearch={supportWebSearch}
1863
- supportDocumentSearch={supportDocumentSearch}
1864
- supportDeepResearch={supportDeepResearch}
1865
- supportThinking={supportThinking}
1866
- supportWorkCanvas={supportWorkCanvas}
1867
- supportMultiModels={supportMultiModels}
1868
- deploymentId={deploymentId}
1869
- tenantAiAgentId={tenantAiAgentId}
1870
- enableMicrophone={enableMicrophone}
1871
- enableVoice={enableVoice}
1872
- isRecording={isRecording}
1873
- recognition={recognition}
1874
- onMicrophoneClick={handleMicrophoneClick}
1875
- onToolAction={handleToolAction}
1876
- threadId={sessionState.selectedSessionId ?? undefined} />
1877
- </>
1878
- )}
1879
- </div>
1880
-
1881
- {/* Floating Right Sidebar with Overlay - shown only when container < 3xl */}
1882
- {uiState.isRightSidebarOpen && (
1883
- <>
1884
- {/* Background Mask */}
1885
- <div
1886
- className={cn(
1887
- 'absolute inset-0 bg-black/30 z-99',
1888
- // Show only when container is smaller than 3xl
1889
- '@3xl:hidden'
1890
- )}
1891
- onClick={() => uiActions.setRightSidebarOpen(false)} />
1892
-
1893
- {/* Floating Right Sidebar */}
1894
- <div
1895
- className={cn(
1896
- 'absolute right-0 top-0 h-full w-80 rounded-lg bg-card z-100 shadow-xl',
1897
- // Show floating sidebar when container is smaller than 3xl
1898
- '@3xl:hidden'
1899
- )}
1900
- onMouseLeave={() => {
1901
- // Auto-close when mouse leaves sidebar
1902
- if (rightSidebarCloseTimeoutRef.current) {
1903
- clearTimeout(rightSidebarCloseTimeoutRef.current);
1904
- }
1905
- rightSidebarCloseTimeoutRef.current = setTimeout(() => {
1906
- uiActions.setRightSidebarOpen(false);
1907
- }, 300);
1908
- }}
1909
- onMouseEnter={() => {
1910
- // Clear any pending close timeout when mouse enters
1911
- if (rightSidebarCloseTimeoutRef.current) {
1912
- clearTimeout(rightSidebarCloseTimeoutRef.current);
1913
- rightSidebarCloseTimeoutRef.current = null;
1914
- }
1915
- }}>
1916
- <AssistantCanvasView
1917
- works={worksState.works}
1918
- selectedWork={worksState.selectedWork}
1919
- rightSidebarView={uiState.rightSidebarView}
1920
- onWorkSelect={handleWorkNavigation}
1921
- onBackToList={() => handleWorkNavigation(null)}
1922
- onClose={() => uiActions.setRightSidebarOpen(false)} />
1923
- </div>
1924
- </>
1925
- )}
1926
-
1927
- {/* Inline Right Sidebar - shown only when container >= 3xl */}
1928
- <div
1929
- className={cn(
1930
- 'relative bg-card flex-none transition-all duration-300 border-l',
1931
- // Show inline sidebar when container is 3xl or larger
1932
- 'hidden @3xl:block',
1933
- uiState.isRightSidebarOpen ? 'w-80' : 'w-0 overflow-hidden'
1934
- )}>
1935
- <AssistantCanvasView
1936
- works={worksState.works}
1937
- selectedWork={worksState.selectedWork}
1938
- rightSidebarView={uiState.rightSidebarView}
1939
- onWorkSelect={handleWorkNavigation}
1940
- onBackToList={() => handleWorkNavigation(null)}
1941
- onClose={() => uiActions.setRightSidebarOpen(false)} />
1942
- </div>
1943
- </div>
1944
- </DialogContent>
1945
- </Dialog>
1946
-
1947
- {/* CRUD Dialogs */}
1948
- <AssistantDialogs
1949
- projectState={projectState}
1950
- projectActions={projectActions}
1951
- sessionState={sessionState}
1952
- sessionActions={sessionActions}
1953
- onUpdateProject={updateProject}
1954
- onDeleteProject={deleteProject}
1955
- onUpdateSession={updateSession}
1956
- onDeleteSession={deleteSession}
1957
- onUpdateProjectInstructions={updateProjectInstructions}
1958
- t={t} />
1959
-
1960
- </>
1961
- );
1962
- };