@lobehub/chat 1.11.9 → 1.12.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 (356) hide show
  1. package/.github/workflows/release.yml +2 -1
  2. package/.github/workflows/test.yml +2 -1
  3. package/CHANGELOG.md +50 -0
  4. package/Dockerfile.database +2 -0
  5. package/docs/self-hosting/advanced/knowledge-base.zh-CN.mdx +65 -0
  6. package/locales/ar/chat.json +13 -3
  7. package/locales/ar/components.json +2 -0
  8. package/locales/bg-BG/chat.json +13 -3
  9. package/locales/bg-BG/components.json +2 -0
  10. package/locales/de-DE/chat.json +13 -3
  11. package/locales/de-DE/components.json +2 -0
  12. package/locales/en-US/chat.json +13 -3
  13. package/locales/en-US/components.json +2 -0
  14. package/locales/es-ES/chat.json +13 -3
  15. package/locales/es-ES/components.json +2 -0
  16. package/locales/fr-FR/chat.json +13 -3
  17. package/locales/fr-FR/components.json +2 -0
  18. package/locales/it-IT/chat.json +13 -3
  19. package/locales/it-IT/components.json +2 -0
  20. package/locales/ja-JP/chat.json +13 -3
  21. package/locales/ja-JP/components.json +2 -0
  22. package/locales/ko-KR/chat.json +13 -3
  23. package/locales/ko-KR/components.json +2 -0
  24. package/locales/nl-NL/chat.json +13 -3
  25. package/locales/nl-NL/components.json +2 -0
  26. package/locales/pl-PL/chat.json +13 -3
  27. package/locales/pl-PL/components.json +2 -0
  28. package/locales/pt-BR/chat.json +13 -3
  29. package/locales/pt-BR/components.json +2 -0
  30. package/locales/ru-RU/chat.json +13 -3
  31. package/locales/ru-RU/components.json +2 -0
  32. package/locales/tr-TR/chat.json +13 -3
  33. package/locales/tr-TR/components.json +2 -0
  34. package/locales/vi-VN/chat.json +13 -3
  35. package/locales/vi-VN/components.json +2 -0
  36. package/locales/zh-CN/chat.json +13 -3
  37. package/locales/zh-CN/components.json +2 -0
  38. package/locales/zh-TW/chat.json +13 -3
  39. package/locales/zh-TW/components.json +2 -0
  40. package/package.json +3 -2
  41. package/scripts/migrateServerDB/docker.cjs +6 -0
  42. package/scripts/migrateServerDB/errorHint.js +17 -0
  43. package/scripts/migrateServerDB/index.ts +6 -0
  44. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileItem/Content.tsx +37 -0
  45. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileItem/index.tsx +87 -0
  46. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileItem/style.ts +4 -0
  47. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileItem/utils.ts +28 -0
  48. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileList.tsx +41 -0
  49. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/index.tsx +40 -0
  50. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx +1 -1
  51. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +7 -22
  52. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx +1 -1
  53. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +2 -4
  54. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/FileItem/File.tsx +72 -0
  55. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/FileItem/Image.tsx +74 -0
  56. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/FileItem/index.tsx +39 -0
  57. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/FileItem/style.ts +1 -0
  58. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/index.tsx +33 -0
  59. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/Container.tsx +41 -0
  60. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/index.tsx +154 -0
  61. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Send.tsx +34 -0
  62. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +24 -11
  63. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/components/UploadDetail/UploadStatus.tsx +71 -0
  64. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/components/UploadDetail/index.tsx +49 -0
  65. package/src/app/(main)/chat/(workspace)/@portal/FilePreview/index.tsx +26 -0
  66. package/src/app/(main)/chat/(workspace)/@portal/Home/Files/FileList/Item.tsx +53 -0
  67. package/src/app/(main)/chat/(workspace)/@portal/Home/Files/FileList/index.tsx +50 -0
  68. package/src/app/(main)/chat/(workspace)/@portal/Home/Files/index.tsx +21 -0
  69. package/src/app/(main)/chat/(workspace)/@portal/Home/index.tsx +2 -0
  70. package/src/app/(main)/chat/(workspace)/@portal/router.tsx +4 -0
  71. package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/KnowledgeTag.tsx +41 -0
  72. package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags.tsx +7 -2
  73. package/src/app/(main)/chat/(workspace)/_layout/Desktop/HotKeys.tsx +1 -1
  74. package/src/app/(main)/files/(content)/@menu/default.tsx +27 -0
  75. package/src/app/(main)/files/(content)/@menu/features/FileMenu/index.tsx +97 -0
  76. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/EmptyStatus.tsx +53 -0
  77. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/Item/Content.tsx +175 -0
  78. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/Item/index.tsx +69 -0
  79. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/KnowledgeBaseList.tsx +30 -0
  80. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/SkeletonList.tsx +57 -0
  81. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/index.tsx +54 -0
  82. package/src/app/(main)/files/(content)/@modal/(.)[id]/FileDetail.tsx +16 -0
  83. package/src/app/(main)/files/(content)/@modal/(.)[id]/FilePreview.tsx +15 -0
  84. package/src/app/(main)/files/(content)/@modal/(.)[id]/FullscreenModal.tsx +85 -0
  85. package/src/app/(main)/files/(content)/@modal/(.)[id]/page.tsx +19 -0
  86. package/src/app/(main)/files/(content)/@modal/default.tsx +3 -0
  87. package/src/app/(main)/files/(content)/NotSupportClient.tsx +152 -0
  88. package/src/app/(main)/files/(content)/_layout/Desktop/index.tsx +28 -0
  89. package/src/app/(main)/files/(content)/_layout/Mobile.tsx +47 -0
  90. package/src/app/(main)/files/(content)/_layout/type.ts +7 -0
  91. package/src/app/(main)/files/(content)/layout.tsx +18 -0
  92. package/src/app/(main)/files/(content)/page.tsx +14 -0
  93. package/src/app/(main)/files/[id]/Header.tsx +63 -0
  94. package/src/app/(main)/files/[id]/page.tsx +44 -0
  95. package/src/app/(main)/files/features/FileDetail.tsx +93 -0
  96. package/src/app/(main)/files/hooks/useFileCategory.ts +9 -0
  97. package/src/app/(main)/files/layout.tsx +12 -0
  98. package/src/app/(main)/files/loading.tsx +21 -0
  99. package/src/app/(main)/repos/[id]/@menu/Head/index.tsx +33 -0
  100. package/src/app/(main)/repos/[id]/@menu/Menu/index.tsx +56 -0
  101. package/src/app/(main)/repos/[id]/@menu/default.tsx +25 -0
  102. package/src/app/(main)/repos/[id]/_layout/Desktop/index.tsx +25 -0
  103. package/src/app/(main)/repos/[id]/_layout/Mobile.tsx +47 -0
  104. package/src/app/(main)/repos/[id]/_layout/type.ts +6 -0
  105. package/src/app/(main)/repos/[id]/hooks/useKnowledgeItem.ts +7 -0
  106. package/src/app/(main)/repos/[id]/layout.tsx +13 -0
  107. package/src/app/(main)/repos/[id]/page.tsx +18 -0
  108. package/src/app/(main)/repos/layout.tsx +13 -0
  109. package/src/app/(main)/repos/page.tsx +5 -0
  110. package/src/app/trpc/async/[trpc]/route.ts +28 -0
  111. package/src/chains/abstractChunk.ts +19 -0
  112. package/src/chains/answerWithContext.ts +34 -0
  113. package/src/chains/rewriteQuery.ts +22 -0
  114. package/src/components/DragUpload/index.tsx +6 -99
  115. package/src/components/DragUpload/useDragUpload.tsx +146 -0
  116. package/src/components/FeatureList/index.tsx +64 -0
  117. package/src/components/FileParsingStatus/index.tsx +230 -0
  118. package/src/components/ImageItem/index.tsx +10 -2
  119. package/src/components/KnowledgeIcon/index.tsx +28 -0
  120. package/src/config/app.ts +6 -1
  121. package/src/config/featureFlags/schema.ts +1 -1
  122. package/src/const/file.ts +1 -0
  123. package/src/const/url.ts +1 -0
  124. package/src/database/client/models/file.ts +8 -2
  125. package/src/database/server/migrations/0005_pgvector.sql +2 -0
  126. package/src/database/server/migrations/0006_add_knowledge_base.sql +307 -0
  127. package/src/database/server/migrations/0007_fix_embedding_table.sql +18 -0
  128. package/src/database/server/migrations/meta/0005_snapshot.json +2119 -0
  129. package/src/database/server/migrations/meta/0006_snapshot.json +3006 -0
  130. package/src/database/server/migrations/meta/0007_snapshot.json +3012 -0
  131. package/src/database/server/migrations/meta/_journal.json +21 -0
  132. package/src/database/server/models/__tests__/_test_template.ts +155 -0
  133. package/src/database/server/models/__tests__/agent.test.ts +226 -0
  134. package/src/database/server/models/__tests__/asyncTask.test.ts +176 -0
  135. package/src/database/server/models/__tests__/chunk.test.ts +336 -0
  136. package/src/database/server/models/__tests__/file.test.ts +317 -29
  137. package/src/database/server/models/__tests__/fixtures/embedding.ts +568 -0
  138. package/src/database/server/models/__tests__/knowledgeBase.test.ts +132 -0
  139. package/src/database/server/models/__tests__/message.test.ts +7 -4
  140. package/src/database/server/models/_template.ts +10 -1
  141. package/src/database/server/models/agent.ts +165 -0
  142. package/src/database/server/models/asyncTask.ts +96 -0
  143. package/src/database/server/models/chunk.ts +203 -0
  144. package/src/database/server/models/embedding.ts +50 -0
  145. package/src/database/server/models/file.ts +231 -12
  146. package/src/database/server/models/knowledgeBase.ts +94 -0
  147. package/src/database/server/models/message.ts +156 -30
  148. package/src/database/server/models/user.ts +12 -1
  149. package/src/database/server/schemas/lobechat/agent.ts +93 -0
  150. package/src/database/server/schemas/lobechat/discover.ts +1 -1
  151. package/src/database/server/schemas/lobechat/file.ts +118 -1
  152. package/src/database/server/schemas/lobechat/index.ts +5 -1
  153. package/src/database/server/schemas/lobechat/message.ts +169 -0
  154. package/src/database/server/schemas/lobechat/rag.ts +53 -0
  155. package/src/database/server/schemas/lobechat/relations.ts +68 -48
  156. package/src/database/server/schemas/lobechat/session.ts +77 -0
  157. package/src/database/server/schemas/lobechat/topic.ts +32 -0
  158. package/src/database/server/schemas/lobechat/user.ts +40 -25
  159. package/src/database/server/utils/idGenerator.ts +1 -0
  160. package/src/features/ChatInput/ActionBar/Clear.tsx +1 -1
  161. package/src/features/ChatInput/ActionBar/Knowledge/Dropdown.tsx +160 -0
  162. package/src/features/ChatInput/ActionBar/Knowledge/ListItem.tsx +52 -0
  163. package/src/features/ChatInput/ActionBar/Knowledge/index.tsx +54 -0
  164. package/src/features/ChatInput/ActionBar/Tools/index.tsx +1 -1
  165. package/src/features/ChatInput/ActionBar/Upload/ClientMode.tsx +52 -0
  166. package/src/features/ChatInput/ActionBar/Upload/ServerMode.tsx +104 -0
  167. package/src/features/ChatInput/ActionBar/Upload/index.tsx +8 -0
  168. package/src/features/ChatInput/ActionBar/config.ts +14 -5
  169. package/src/features/ChatInput/useSend.ts +16 -7
  170. package/src/features/Conversation/Messages/Assistant/FileChunks/Item/index.tsx +51 -0
  171. package/src/features/Conversation/Messages/Assistant/FileChunks/Item/style.ts +38 -0
  172. package/src/features/Conversation/Messages/Assistant/FileChunks/index.tsx +76 -0
  173. package/src/features/Conversation/Messages/Assistant/index.tsx +13 -4
  174. package/src/features/Conversation/Messages/Default.tsx +4 -0
  175. package/src/features/Conversation/Messages/User/BelowMessage.tsx +78 -0
  176. package/src/features/Conversation/Messages/User/FileListViewer/Item.tsx +53 -0
  177. package/src/features/Conversation/Messages/User/FileListViewer/index.tsx +21 -0
  178. package/src/{components/FileList/FileListViewer.tsx → features/Conversation/Messages/User/ImageFileListViewer.tsx} +10 -2
  179. package/src/features/Conversation/Messages/{User.tsx → User/index.tsx} +11 -2
  180. package/src/features/Conversation/Messages/index.ts +8 -3
  181. package/src/features/Conversation/components/ChatItem/index.tsx +33 -10
  182. package/src/features/Conversation/components/InboxWelcome/QuestionSuggest.tsx +1 -1
  183. package/src/features/Conversation/types/index.tsx +1 -0
  184. package/src/features/FileManager/ChunkDrawer/ChunkList/ChunkItem.tsx +61 -0
  185. package/src/features/FileManager/ChunkDrawer/ChunkList/index.tsx +42 -0
  186. package/src/features/FileManager/ChunkDrawer/Content.tsx +38 -0
  187. package/src/features/FileManager/ChunkDrawer/Loading/index.tsx +16 -0
  188. package/src/features/FileManager/ChunkDrawer/SimilaritySearchList/Item.tsx +62 -0
  189. package/src/features/FileManager/ChunkDrawer/SimilaritySearchList/index.tsx +31 -0
  190. package/src/features/FileManager/ChunkDrawer/index.tsx +48 -0
  191. package/src/features/FileManager/FileList/EmptyStatus.tsx +153 -0
  192. package/src/features/FileManager/FileList/FileListItem/ChunkTag.tsx +35 -0
  193. package/src/features/FileManager/FileList/FileListItem/DropdownMenu.tsx +150 -0
  194. package/src/features/FileManager/FileList/FileListItem/index.tsx +211 -0
  195. package/src/features/FileManager/FileList/FileSkeleton.tsx +25 -0
  196. package/src/features/FileManager/FileList/ToolBar/Config.tsx +28 -0
  197. package/src/features/FileManager/FileList/ToolBar/MultiSelectActions.tsx +152 -0
  198. package/src/features/FileManager/FileList/ToolBar/index.tsx +114 -0
  199. package/src/features/FileManager/FileList/index.tsx +143 -0
  200. package/src/features/FileManager/FileList/useCheckTaskStatus.ts +27 -0
  201. package/src/features/FileManager/Header/FilesSearchBar.tsx +41 -0
  202. package/src/features/FileManager/Header/UploadFileButton.tsx +79 -0
  203. package/src/features/FileManager/Header/index.tsx +39 -0
  204. package/src/features/FileManager/UploadDock/Item.tsx +124 -0
  205. package/src/features/FileManager/UploadDock/index.tsx +183 -0
  206. package/src/features/FileManager/index.tsx +38 -0
  207. package/src/features/FileSidePanel/index.tsx +79 -0
  208. package/src/features/FileViewer/NotSupport/index.tsx +54 -0
  209. package/src/features/FileViewer/PDFViewer/HighlightLayer.tsx +81 -0
  210. package/src/features/FileViewer/PDFViewer/index.tsx +93 -0
  211. package/src/features/FileViewer/PDFViewer/style.ts +20 -0
  212. package/src/features/FileViewer/PDFViewer/useResizeObserver.ts +33 -0
  213. package/src/features/FileViewer/TXTViewer/index.tsx +41 -0
  214. package/src/features/FileViewer/index.tsx +45 -0
  215. package/src/features/KnowledgeBaseModal/AddFilesToKnowledgeBase/SelectForm.tsx +115 -0
  216. package/src/features/KnowledgeBaseModal/AddFilesToKnowledgeBase/index.tsx +43 -0
  217. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/Action.tsx +103 -0
  218. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/EditCustomPlugin.tsx +55 -0
  219. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/PluginTag.tsx +58 -0
  220. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/index.tsx +70 -0
  221. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/List.tsx +49 -0
  222. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Loading.tsx +13 -0
  223. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/index.tsx +39 -0
  224. package/src/features/KnowledgeBaseModal/CreateNew/CreateForm.tsx +73 -0
  225. package/src/features/KnowledgeBaseModal/CreateNew/index.tsx +35 -0
  226. package/src/features/KnowledgeBaseModal/index.ts +3 -0
  227. package/src/libs/langchain/loaders/pdf/index.ts +2 -2
  228. package/src/libs/trpc/async/asyncAuth.ts +24 -0
  229. package/src/libs/trpc/async/index.ts +10 -0
  230. package/src/libs/trpc/async/init.ts +11 -0
  231. package/src/libs/trpc/client/async.ts +14 -0
  232. package/src/libs/trpc/client/index.ts +1 -0
  233. package/src/libs/trpc/client/lambda.ts +9 -0
  234. package/src/libs/trpc/middleware/keyVaults.ts +18 -0
  235. package/src/libs/unstructured/__tests__/index.test.ts +0 -10
  236. package/src/libs/unstructured/index.ts +6 -11
  237. package/src/locales/default/chat.ts +47 -3
  238. package/src/locales/default/common.ts +1 -0
  239. package/src/locales/default/components.ts +2 -0
  240. package/src/locales/default/error.ts +6 -1
  241. package/src/locales/default/file.ts +92 -0
  242. package/src/locales/default/index.ts +4 -0
  243. package/src/locales/default/knowledgeBase.ts +31 -0
  244. package/src/locales/default/portal.ts +1 -0
  245. package/src/middleware.ts +3 -0
  246. package/src/server/asyncContext.ts +40 -0
  247. package/src/server/modules/ContentChunk/index.ts +135 -0
  248. package/src/server/modules/S3/index.ts +30 -5
  249. package/src/server/routers/async/caller.ts +27 -0
  250. package/src/server/routers/async/file.ts +247 -0
  251. package/src/server/routers/async/index.ts +12 -0
  252. package/src/server/routers/lambda/_template.ts +77 -0
  253. package/src/server/routers/lambda/agent.ts +159 -0
  254. package/src/server/routers/lambda/chunk.ts +189 -0
  255. package/src/server/routers/lambda/file.ts +129 -5
  256. package/src/server/routers/lambda/index.ts +6 -0
  257. package/src/server/routers/lambda/knowledgeBase.ts +79 -0
  258. package/src/server/routers/lambda/message.ts +6 -0
  259. package/src/server/routers/lambda/session.ts +0 -25
  260. package/src/server/routers/lambda/user.ts +5 -1
  261. package/src/server/services/chunk/index.ts +74 -0
  262. package/src/server/utils/files.ts +9 -0
  263. package/src/services/__tests__/chat.test.ts +18 -20
  264. package/src/services/__tests__/{upload.test.ts → upload_legacy.test.ts} +1 -1
  265. package/src/services/agent.ts +45 -0
  266. package/src/services/chat.ts +17 -15
  267. package/src/services/file/client.test.ts +1 -50
  268. package/src/services/file/client.ts +12 -25
  269. package/src/services/file/server.ts +25 -3
  270. package/src/services/file/type.ts +7 -4
  271. package/src/services/knowledgeBase.ts +34 -0
  272. package/src/services/message/client.test.ts +1 -1
  273. package/src/services/message/client.ts +29 -3
  274. package/src/services/message/index.ts +0 -2
  275. package/src/services/message/server.ts +9 -3
  276. package/src/services/message/type.ts +1 -13
  277. package/src/services/rag.ts +29 -0
  278. package/src/services/session/server.ts +1 -1
  279. package/src/services/upload.ts +89 -84
  280. package/src/services/upload_legacy.ts +104 -0
  281. package/src/services/user/client.ts +7 -2
  282. package/src/services/user/server.ts +6 -2
  283. package/src/services/user/type.ts +3 -2
  284. package/src/store/agent/slices/chat/action.ts +90 -18
  285. package/src/store/agent/slices/chat/initialState.ts +1 -0
  286. package/src/store/agent/slices/chat/selectors.ts +58 -0
  287. package/src/store/chat/slices/builtinTool/action.test.ts +2 -2
  288. package/src/store/chat/slices/builtinTool/action.ts +2 -2
  289. package/src/store/chat/slices/message/action.test.ts +2 -1
  290. package/src/store/chat/slices/message/action.ts +102 -26
  291. package/src/store/chat/slices/message/actions/rag.ts +148 -0
  292. package/src/store/chat/slices/message/initialState.ts +7 -0
  293. package/src/store/chat/slices/message/reducer.ts +6 -2
  294. package/src/store/chat/slices/message/selectors.ts +38 -3
  295. package/src/store/chat/slices/plugin/action.ts +8 -2
  296. package/src/store/chat/slices/portal/action.ts +8 -0
  297. package/src/store/chat/slices/portal/initialState.ts +3 -0
  298. package/src/store/chat/slices/portal/selectors.ts +8 -2
  299. package/src/store/file/initialState.ts +5 -1
  300. package/src/store/file/reducers/uploadFileList.ts +133 -0
  301. package/src/store/file/selectors.ts +3 -0
  302. package/src/store/file/slices/chat/action.test.ts +90 -90
  303. package/src/store/file/slices/chat/action.ts +164 -109
  304. package/src/store/file/slices/chat/initialState.ts +7 -2
  305. package/src/store/file/slices/chat/selectors.test.ts +84 -61
  306. package/src/store/file/slices/chat/selectors.ts +22 -32
  307. package/src/store/file/slices/chunk/action.ts +36 -0
  308. package/src/store/file/slices/chunk/index.ts +3 -0
  309. package/src/store/file/slices/chunk/initialState.ts +15 -0
  310. package/src/store/file/slices/chunk/selectors.ts +10 -0
  311. package/src/store/file/slices/fileManager/action.ts +187 -0
  312. package/src/store/file/slices/fileManager/index.ts +3 -0
  313. package/src/store/file/slices/fileManager/initialState.ts +18 -0
  314. package/src/store/file/slices/fileManager/selectors.ts +58 -0
  315. package/src/store/file/slices/tts/action.test.ts +1 -1
  316. package/src/store/file/slices/tts/action.ts +2 -2
  317. package/src/store/file/slices/upload/action.ts +164 -0
  318. package/src/store/file/store.ts +12 -1
  319. package/src/store/knowledgeBase/index.ts +2 -0
  320. package/src/store/knowledgeBase/initialState.ts +7 -0
  321. package/src/store/knowledgeBase/selectors.ts +1 -0
  322. package/src/store/knowledgeBase/slices/content/action.ts +27 -0
  323. package/src/store/knowledgeBase/slices/content/index.ts +1 -0
  324. package/src/store/knowledgeBase/slices/crud/action.ts +78 -0
  325. package/src/store/knowledgeBase/slices/crud/index.ts +3 -0
  326. package/src/store/knowledgeBase/slices/crud/initialState.ts +12 -0
  327. package/src/store/knowledgeBase/slices/crud/selectors.ts +7 -0
  328. package/src/store/knowledgeBase/store.ts +30 -0
  329. package/src/store/serverConfig/selectors.test.ts +1 -1
  330. package/src/store/user/slices/preference/selectors.ts +8 -0
  331. package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
  332. package/src/tools/dalle/Render/Item/ImageFileItem.tsx +3 -23
  333. package/src/types/agent/index.ts +9 -0
  334. package/src/types/asyncTask.ts +31 -0
  335. package/src/types/chunk/document.ts +9 -0
  336. package/src/types/chunk/index.ts +52 -0
  337. package/src/types/files/index.ts +35 -0
  338. package/src/types/files/list.ts +44 -0
  339. package/src/types/files/upload.ts +91 -0
  340. package/src/types/knowledgeBase/index.ts +45 -0
  341. package/src/types/message/index.ts +54 -5
  342. package/src/types/rag.ts +16 -0
  343. package/src/utils/filter.test.ts +2 -0
  344. package/src/utils/server/auth.ts +23 -0
  345. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/LocalFiles.tsx +0 -46
  346. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files.tsx +0 -19
  347. package/src/components/FileList/EditableFileList.tsx +0 -47
  348. package/src/components/FileList/index.ts +0 -2
  349. package/src/components/FileList/type.tsx +0 -7
  350. package/src/database/server/schemas/lobechat/chat.ts +0 -331
  351. package/src/features/ChatInput/ActionBar/FileUpload.tsx +0 -69
  352. package/src/features/ChatInput/useChatInput.ts +0 -45
  353. package/src/features/FileList/EditableFileList.tsx +0 -31
  354. package/src/features/FileList/FileListPreviewer.tsx +0 -17
  355. package/src/features/FileList/index.tsx +0 -2
  356. package/src/types/files.ts +0 -42
@@ -0,0 +1,81 @@
1
+ import { FC, memo } from 'react';
2
+
3
+ import { useFileStore } from '@/store/file';
4
+ import { ChunkMetadata, Coordinates, FileChunk } from '@/types/chunk';
5
+
6
+ interface HighlightRectProps {
7
+ coordinates: Coordinates;
8
+ highlight: boolean;
9
+ }
10
+
11
+ const HighlightRect: FC<HighlightRectProps> = ({ coordinates, highlight }) => {
12
+ const { points } = coordinates;
13
+
14
+ // 假设points数组包含矩形的四个顶点坐标
15
+ const [topLeft, topRight, bottomRight, bottomLeft] = points;
16
+
17
+ // 计算矩形的属性
18
+ const minX = Math.min(topLeft[0], topRight[0], bottomRight[0], bottomLeft[0]);
19
+ const minY = Math.min(topLeft[1], topRight[1], bottomRight[1], bottomLeft[1]);
20
+ const width = Math.max(topLeft[0], topRight[0], bottomRight[0], bottomLeft[0]) - minX;
21
+ const height = Math.max(topLeft[1], topRight[1], bottomRight[1], bottomLeft[1]) - minY;
22
+
23
+ return (
24
+ <rect
25
+ fill={highlight ? 'rgba(255, 255, 0, 0.5)' : 'rgba(255, 255, 0, 0.3)'}
26
+ height={height}
27
+ stroke="rgba(255, 255, 0, 0.7)"
28
+ strokeWidth="1"
29
+ width={width}
30
+ x={minX}
31
+ y={minY}
32
+ />
33
+ );
34
+ };
35
+
36
+ interface HighlightLayerProps {
37
+ dataSource: FileChunk[];
38
+ pageNumber: number;
39
+ width: number;
40
+ }
41
+
42
+ const HighlightLayer = memo<HighlightLayerProps>(({ dataSource, pageNumber, width }) => {
43
+ const chunks = dataSource
44
+ .filter((chunk) => chunk.pageNumber && chunk.pageNumber === pageNumber)
45
+ .filter(Boolean);
46
+ const highlightChunkIds = useFileStore((s) => s.highlightChunkIds);
47
+
48
+ const isExist = chunks.length > 0;
49
+
50
+ if (!isExist) return null;
51
+
52
+ const metadata = chunks[0].metadata as ChunkMetadata;
53
+ if (!metadata.coordinates) return;
54
+
55
+ const { layout_width, layout_height } = metadata.coordinates;
56
+
57
+ const height = metadata.coordinates.layout_height * (width / metadata.coordinates.layout_width);
58
+
59
+ return (
60
+ <svg
61
+ height={height}
62
+ style={{ left: 0, position: 'absolute', top: 0, zIndex: 100 }}
63
+ viewBox={`0 0 ${layout_width} ${layout_height}`}
64
+ width={width}
65
+ >
66
+ {chunks.map(
67
+ (chunk, index) =>
68
+ chunk.metadata && (
69
+ <HighlightRect
70
+ coordinates={chunk.metadata.coordinates}
71
+ highlight={highlightChunkIds.includes(chunk.id)}
72
+ key={index}
73
+ />
74
+ ),
75
+ )}
76
+ s
77
+ </svg>
78
+ );
79
+ });
80
+
81
+ export default HighlightLayer;
@@ -0,0 +1,93 @@
1
+ 'use client';
2
+
3
+ import type { PDFDocumentProxy } from 'pdfjs-dist';
4
+ import { Fragment, memo, useCallback, useState } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+ import { Document, Page, pdfjs } from 'react-pdf';
7
+ import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
8
+ import 'react-pdf/dist/esm/Page/TextLayer.css';
9
+
10
+ import { lambdaQuery } from '@/libs/trpc/client';
11
+
12
+ import HighlightLayer from './HighlightLayer';
13
+ import { useStyles } from './style';
14
+ import useResizeObserver from './useResizeObserver';
15
+
16
+ // 如果海外的地址: https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs
17
+ pdfjs.GlobalWorkerOptions.workerSrc = `https://registry.npmmirror.com/pdfjs-dist/${pdfjs.version}/files/build/pdf.worker.min.mjs`;
18
+
19
+ const options = {
20
+ cMapUrl: '/cmaps/',
21
+ standardFontDataUrl: '/standard_fonts/',
22
+ };
23
+
24
+ const maxWidth = 1200;
25
+
26
+ interface PDFViewerProps {
27
+ fileId: string;
28
+ url: string | null;
29
+ }
30
+
31
+ const PDFViewer = memo<PDFViewerProps>(({ url, fileId }) => {
32
+ const { styles } = useStyles();
33
+ const [numPages, setNumPages] = useState<number>(0);
34
+ const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);
35
+ const [containerWidth, setContainerWidth] = useState<number>();
36
+ const [isLoaded, setIsLoaded] = useState(false);
37
+
38
+ // eslint-disable-next-line no-undef
39
+ const onResize = useCallback<ResizeObserverCallback>((entries) => {
40
+ const [entry] = entries;
41
+
42
+ if (entry) {
43
+ setContainerWidth(entry.contentRect.width);
44
+ }
45
+ }, []);
46
+
47
+ useResizeObserver(containerRef, onResize);
48
+
49
+ const onDocumentLoadSuccess = ({ numPages: nextNumPages }: PDFDocumentProxy) => {
50
+ setNumPages(nextNumPages);
51
+ setIsLoaded(true);
52
+ };
53
+
54
+ const { data } = lambdaQuery.chunk.getChunksByFileId.useInfiniteQuery(
55
+ { id: fileId },
56
+ { getNextPageParam: (lastPage) => lastPage.nextCursor },
57
+ );
58
+
59
+ const dataSource = data?.pages.flatMap((page) => page.items) || [];
60
+
61
+ return (
62
+ <Flexbox className={styles.container}>
63
+ <Flexbox
64
+ align={'center'}
65
+ className={styles.documentContainer}
66
+ padding={24}
67
+ ref={setContainerRef}
68
+ style={{ height: isLoaded ? undefined : '100%' }}
69
+ >
70
+ <Document
71
+ className={styles.document}
72
+ file={url}
73
+ onLoadSuccess={onDocumentLoadSuccess}
74
+ options={options}
75
+ >
76
+ {Array.from({ length: numPages }, (el, index) => {
77
+ const width = containerWidth ? Math.min(containerWidth, maxWidth) : maxWidth;
78
+
79
+ return (
80
+ <Fragment key={`page_${index + 1}`}>
81
+ <Page className={styles.page} pageNumber={index + 1} width={width}>
82
+ <HighlightLayer dataSource={dataSource} pageNumber={index + 1} width={width} />
83
+ </Page>
84
+ </Fragment>
85
+ );
86
+ })}
87
+ </Document>
88
+ </Flexbox>
89
+ </Flexbox>
90
+ );
91
+ });
92
+
93
+ export default PDFViewer;
@@ -0,0 +1,20 @@
1
+ import { createStyles } from 'antd-style';
2
+
3
+ export const useStyles = createStyles(({ css, token }) => ({
4
+ container: css`
5
+ min-height: 100%;
6
+ `,
7
+ document: css`
8
+ position: relative;
9
+ `,
10
+ documentContainer: css`
11
+ padding-block: 10px;
12
+ background-color: ${token.colorBgLayout};
13
+ `,
14
+ page: css`
15
+ overflow: hidden;
16
+ margin-block-end: 12px;
17
+ border-radius: 4px;
18
+ box-shadow: ${token.boxShadowTertiary};
19
+ `,
20
+ }));
@@ -0,0 +1,33 @@
1
+ import { useEffect } from 'react';
2
+
3
+ /**
4
+ * Observes a given element using ResizeObserver.
5
+ *
6
+ * @param {Element} [element] Element to attach ResizeObserver to
7
+ * @param {ResizeObserverOptions} [options] ResizeObserver options. WARNING! If you define the
8
+ * object in component body, make sure to memoize it.
9
+ * @param {ResizeObserverCallback} observerCallback ResizeObserver callback. WARNING! If you define
10
+ * the function in component body, make sure to memoize it.
11
+ * @returns {void}
12
+ */
13
+ export default function useResizeObserver(
14
+ element: Element | null,
15
+ // eslint-disable-next-line no-undef
16
+ observerCallback: ResizeObserverCallback,
17
+ // eslint-disable-next-line no-undef
18
+ options?: ResizeObserverOptions,
19
+ ): void {
20
+ useEffect(() => {
21
+ if (!element || !('ResizeObserver' in window)) {
22
+ return undefined;
23
+ }
24
+
25
+ const observer = new ResizeObserver(observerCallback);
26
+
27
+ observer.observe(element, options);
28
+
29
+ return () => {
30
+ observer.disconnect();
31
+ };
32
+ }, [element, observerCallback, options]);
33
+ }
@@ -0,0 +1,41 @@
1
+ import { DocRenderer, textFileLoader } from '@cyntler/react-doc-viewer';
2
+ import { Highlighter } from '@lobehub/ui';
3
+ import { createStyles } from 'antd-style';
4
+ import React from 'react';
5
+ import { Center, Flexbox } from 'react-layout-kit';
6
+
7
+ import CircleLoading from '@/components/CircleLoading';
8
+
9
+ const useStyles = createStyles(({ css, token }) => ({
10
+ page: css`
11
+ width: 100%;
12
+ padding: 24px;
13
+
14
+ background: ${token.colorBgContainer};
15
+ border-radius: 4px;
16
+ box-shadow: ${token.boxShadowTertiary};
17
+ `,
18
+ }));
19
+
20
+ const TXTViewer: DocRenderer = ({ mainState: { currentDocument } }) => {
21
+ const { styles } = useStyles();
22
+ return (
23
+ <Flexbox className={styles.page} id="txt-renderer">
24
+ {!!currentDocument?.fileData ? (
25
+ <Highlighter language={'txt'} showLanguage={false} style={{ height: '100%' }} type={'pure'}>
26
+ {currentDocument?.fileData as string}
27
+ </Highlighter>
28
+ ) : (
29
+ <Center height={'100%'}>
30
+ <CircleLoading />
31
+ </Center>
32
+ )}
33
+ </Flexbox>
34
+ );
35
+ };
36
+
37
+ export default TXTViewer;
38
+
39
+ TXTViewer.fileTypes = ['txt', 'text/plain'];
40
+ TXTViewer.weight = 0;
41
+ TXTViewer.fileLoader = textFileLoader;
@@ -0,0 +1,45 @@
1
+ 'use client';
2
+
3
+ import DocViewer, { MSDocRenderer } from '@cyntler/react-doc-viewer';
4
+ import { createStyles } from 'antd-style';
5
+ import { CSSProperties, memo } from 'react';
6
+
7
+ import { FileListItem } from '@/types/files';
8
+
9
+ import NotSupport from './NotSupport';
10
+ import PDFViewer from './PDFViewer';
11
+ import TXTViewer from './TXTViewer';
12
+
13
+ const useStyles = createStyles(({ css, token }) => ({
14
+ container: css`
15
+ padding: 12px;
16
+ background: ${token.colorBgLayout} !important;
17
+ `,
18
+ }));
19
+
20
+ interface FileViewerProps extends FileListItem {
21
+ className?: string;
22
+ style?: CSSProperties;
23
+ }
24
+
25
+ const FileViewer = memo<FileViewerProps>(({ id, style, fileType, url, name }) => {
26
+ const { styles } = useStyles();
27
+ if (fileType === 'pdf' || name.endsWith('.pdf')) {
28
+ return <PDFViewer fileId={id} url={url} />;
29
+ }
30
+
31
+ return (
32
+ <DocViewer
33
+ className={styles.container}
34
+ config={{
35
+ header: { disableHeader: true },
36
+ noRenderer: { overrideComponent: NotSupport },
37
+ }}
38
+ documents={[{ fileName: name, fileType, uri: url }]}
39
+ pluginRenderers={[TXTViewer, MSDocRenderer]}
40
+ style={style}
41
+ />
42
+ );
43
+ });
44
+
45
+ export default FileViewer;
@@ -0,0 +1,115 @@
1
+ import { MaterialFileTypeIcon } from '@lobehub/ui';
2
+ import { App, Button, Form, Select } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import Link from 'next/link';
5
+ import { memo, useState } from 'react';
6
+ import { Trans, useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import RepoIcon from '@/components/RepoIcon';
10
+ import { useKnowledgeBaseStore } from '@/store/knowledgeBase';
11
+
12
+ const useStyles = createStyles(({ css, token }) => ({
13
+ files: css`
14
+ height: 48px;
15
+ padding-block: 4px;
16
+ padding-inline: 8px;
17
+
18
+ border: 1px solid ${token.colorSplit};
19
+ border-radius: 6px;
20
+ `,
21
+ formItem: css`
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: 12px;
25
+
26
+ .ant-form-item {
27
+ margin-block-end: 0;
28
+ }
29
+ `,
30
+ }));
31
+
32
+ interface CreateFormProps {
33
+ fileIds: string[];
34
+ knowledgeBaseId?: string;
35
+ onClose?: () => void;
36
+ }
37
+
38
+ const SelectForm = memo<CreateFormProps>(({ onClose, knowledgeBaseId, fileIds }) => {
39
+ const { t } = useTranslation('knowledgeBase');
40
+ const { styles } = useStyles();
41
+ const [loading, setLoading] = useState(false);
42
+
43
+ const { message } = App.useApp();
44
+ const [useFetchKnowledgeBaseList, addFilesToKnowledgeBase] = useKnowledgeBaseStore((s) => [
45
+ s.useFetchKnowledgeBaseList,
46
+ s.addFilesToKnowledgeBase,
47
+ ]);
48
+ const { data, isLoading } = useFetchKnowledgeBaseList();
49
+ const onFinish = async (values: { id: string }) => {
50
+ setLoading(true);
51
+
52
+ try {
53
+ await addFilesToKnowledgeBase(values.id, fileIds);
54
+ setLoading(false);
55
+ message.success({
56
+ content: (
57
+ <Trans i18nKey={'addToKnowledgeBase.addSuccess'} ns={'knowledgeBase'}>
58
+ 文件添加成功,<Link href={`/repos/${values.id}`}>立即查看</Link>
59
+ </Trans>
60
+ ),
61
+ });
62
+
63
+ onClose?.();
64
+ } catch (e) {
65
+ console.error(e);
66
+ setLoading(false);
67
+ }
68
+ };
69
+
70
+ return (
71
+ <Flexbox gap={24}>
72
+ <Flexbox align={'center'} className={styles.files} gap={8} horizontal>
73
+ <MaterialFileTypeIcon filename={''} size={32} />
74
+ {t('addToKnowledgeBase.totalFiles', { count: fileIds.length })}
75
+ </Flexbox>
76
+ <Form className={styles.formItem} layout={'vertical'} onFinish={onFinish}>
77
+ <Form.Item
78
+ label={t('addToKnowledgeBase.id.title')}
79
+ name={'id'}
80
+ required={false}
81
+ rules={[{ message: t('addToKnowledgeBase.id.required'), required: true }]}
82
+ >
83
+ <Select
84
+ autoFocus
85
+ loading={isLoading}
86
+ options={(data || [])
87
+ .filter((item) => item.id !== knowledgeBaseId)
88
+ .map((item) => ({
89
+ label: (
90
+ <Flexbox gap={8} horizontal>
91
+ <RepoIcon />
92
+ {item.name}
93
+ </Flexbox>
94
+ ),
95
+ value: item.id,
96
+ }))}
97
+ placeholder={t('addToKnowledgeBase.id.placeholder')}
98
+ />
99
+ </Form.Item>
100
+
101
+ <Button
102
+ block
103
+ htmlType={'submit'}
104
+ loading={loading}
105
+ style={{ marginTop: 16 }}
106
+ type={'primary'}
107
+ >
108
+ {t('addToKnowledgeBase.confirm')}
109
+ </Button>
110
+ </Form>
111
+ </Flexbox>
112
+ );
113
+ });
114
+
115
+ export default SelectForm;
@@ -0,0 +1,43 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { BookUp2Icon } from 'lucide-react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import { createModal } from '@/components/FunctionModal';
7
+
8
+ import SelectForm from './SelectForm';
9
+
10
+ const Title = () => {
11
+ const { t } = useTranslation('knowledgeBase');
12
+
13
+ return (
14
+ <Flexbox gap={8} horizontal>
15
+ <Icon icon={BookUp2Icon} />
16
+ {t('addToKnowledgeBase.title')}
17
+ </Flexbox>
18
+ );
19
+ };
20
+
21
+ interface AddFilesToKnowledgeBaseModalProps {
22
+ fileIds: string[];
23
+ knowledgeBaseId?: string;
24
+ onClose?: () => void;
25
+ }
26
+
27
+ export const useAddFilesToKnowledgeBaseModal = createModal<AddFilesToKnowledgeBaseModalProps>(
28
+ (instance, params) => ({
29
+ content: (
30
+ <Flexbox paddingInline={16} style={{ marginBlock: 24 }}>
31
+ <SelectForm
32
+ fileIds={params?.fileIds || []}
33
+ knowledgeBaseId={params?.knowledgeBaseId}
34
+ onClose={() => {
35
+ instance.current?.destroy();
36
+ params?.onClose?.();
37
+ }}
38
+ />
39
+ </Flexbox>
40
+ ),
41
+ title: <Title />,
42
+ }),
43
+ );
@@ -0,0 +1,103 @@
1
+ import { ActionIcon, Icon } from '@lobehub/ui';
2
+ import { Button, Dropdown } from 'antd';
3
+ import { InfoIcon, MoreVerticalIcon, Trash2 } from 'lucide-react';
4
+ import { memo, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { useAgentStore } from '@/store/agent';
9
+ import { useServerConfigStore } from '@/store/serverConfig';
10
+ import { KnowledgeType } from '@/types/knowledgeBase';
11
+
12
+ interface ActionsProps {
13
+ enabled?: boolean;
14
+ id: string;
15
+ type: KnowledgeType;
16
+ }
17
+
18
+ const Actions = memo<ActionsProps>(({ id, type, enabled }) => {
19
+ const { t } = useTranslation('chat');
20
+
21
+ const mobile = useServerConfigStore((s) => s.isMobile);
22
+ const [
23
+ addFilesToAgent,
24
+ addKnowledgeBasesToAgent,
25
+ removeFilesFromAgent,
26
+ removeKnowledgeBasesFromAgent,
27
+ ] = useAgentStore((s) => [
28
+ s.addFilesToAgent,
29
+ s.addKnowledgeBaseToAgent,
30
+ s.removeFileFromAgent,
31
+ s.removeKnowledgeBaseFromAgent,
32
+ ]);
33
+
34
+ const [loading, setLoading] = useState(false);
35
+
36
+ const assignKnowledge = async () => {
37
+ setLoading(true);
38
+ if (type === KnowledgeType.KnowledgeBase) {
39
+ await addKnowledgeBasesToAgent(id);
40
+ } else {
41
+ await addFilesToAgent([id], true);
42
+ }
43
+ setLoading(false);
44
+ };
45
+
46
+ const removeKnowledge = async () => {
47
+ setLoading(true);
48
+ if (type === KnowledgeType.KnowledgeBase) {
49
+ await removeKnowledgeBasesFromAgent(id);
50
+ } else {
51
+ await removeFilesFromAgent(id);
52
+ }
53
+ setLoading(false);
54
+ };
55
+
56
+ return (
57
+ <Flexbox align={'center'} horizontal>
58
+ {enabled ? (
59
+ <Dropdown
60
+ menu={{
61
+ items: [
62
+ {
63
+ icon: <Icon icon={InfoIcon} />,
64
+ key: 'detail',
65
+ label: t('knowledgeBase.library.action.detail'),
66
+ onClick: () => {
67
+ if (type === KnowledgeType.KnowledgeBase) {
68
+ window.open(`/repos/${id}`);
69
+ return;
70
+ }
71
+
72
+ window.open(`/files/${id}`);
73
+ },
74
+ },
75
+ {
76
+ danger: true,
77
+ icon: <Icon icon={Trash2} />,
78
+ key: 'remove',
79
+ label: t('knowledgeBase.library.action.remove'),
80
+ onClick: removeKnowledge,
81
+ },
82
+ ],
83
+ }}
84
+ placement="bottomRight"
85
+ trigger={['click']}
86
+ >
87
+ <ActionIcon icon={MoreVerticalIcon} loading={loading} />
88
+ </Dropdown>
89
+ ) : (
90
+ <Button
91
+ loading={loading}
92
+ onClick={assignKnowledge}
93
+ size={mobile ? 'small' : undefined}
94
+ type={'primary'}
95
+ >
96
+ {t('knowledgeBase.library.action.add')}
97
+ </Button>
98
+ )}
99
+ </Flexbox>
100
+ );
101
+ });
102
+
103
+ export default Actions;
@@ -0,0 +1,55 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { PackageSearch } from 'lucide-react';
4
+ import { memo, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import DevModal from '@/features/PluginDevModal';
8
+ import { useToolStore } from '@/store/tool';
9
+ import { pluginSelectors } from '@/store/tool/slices/plugin/selectors';
10
+
11
+ const EditCustomPlugin = memo<{ identifier: string }>(({ identifier }) => {
12
+ const { t } = useTranslation('plugin');
13
+ const [showModal, setModal] = useState(false);
14
+
15
+ const [installCustomPlugin, updateNewDevPlugin, uninstallCustomPlugin] = useToolStore((s) => [
16
+ s.installCustomPlugin,
17
+ s.updateNewCustomPlugin,
18
+ s.uninstallCustomPlugin,
19
+ ]);
20
+
21
+ const customPlugin = useToolStore(pluginSelectors.getCustomPluginById(identifier), isEqual);
22
+
23
+ return (
24
+ <div
25
+ onClick={(e) => {
26
+ e.stopPropagation();
27
+ }}
28
+ >
29
+ <DevModal
30
+ mode={'edit'}
31
+ onDelete={() => {
32
+ uninstallCustomPlugin(identifier);
33
+ setModal(false);
34
+ }}
35
+ onOpenChange={setModal}
36
+ onSave={async (devPlugin) => {
37
+ await installCustomPlugin(devPlugin);
38
+ setModal(false);
39
+ }}
40
+ onValueChange={updateNewDevPlugin}
41
+ open={showModal}
42
+ value={customPlugin}
43
+ />
44
+ <ActionIcon
45
+ icon={PackageSearch}
46
+ onClick={() => {
47
+ setModal(true);
48
+ }}
49
+ title={t('store.actions.manifest')}
50
+ />
51
+ </div>
52
+ );
53
+ });
54
+
55
+ export default EditCustomPlugin;
@@ -0,0 +1,58 @@
1
+ import { Icon, Tag } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { BadgeCheck, CircleUser, Package } from 'lucide-react';
4
+ import { rgba } from 'polished';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ import { InstallPluginMeta } from '@/types/tool/plugin';
9
+
10
+ const useStyles = createStyles(({ css, token }) => ({
11
+ community: css`
12
+ color: ${rgba(token.colorInfo, 0.75)};
13
+ background: ${token.colorInfoBg};
14
+
15
+ &:hover {
16
+ color: ${token.colorInfo};
17
+ }
18
+ `,
19
+ custom: css`
20
+ color: ${rgba(token.colorWarning, 0.75)};
21
+ background: ${token.colorWarningBg};
22
+
23
+ &:hover {
24
+ color: ${token.colorWarning};
25
+ }
26
+ `,
27
+ official: css`
28
+ color: ${rgba(token.colorSuccess, 0.75)};
29
+ background: ${token.colorSuccessBg};
30
+
31
+ &:hover {
32
+ color: ${token.colorSuccess};
33
+ }
34
+ `,
35
+ }));
36
+
37
+ interface PluginTagProps extends Pick<InstallPluginMeta, 'author' | 'type'> {
38
+ showIcon?: boolean;
39
+ showText?: boolean;
40
+ }
41
+
42
+ const PluginTag = memo<PluginTagProps>(({ showIcon = true, author, type, showText = true }) => {
43
+ const { t } = useTranslation('plugin');
44
+ const { styles, cx } = useStyles();
45
+ const isCustom = type === 'customPlugin';
46
+ const isOfficial = author === 'LobeHub';
47
+
48
+ return (
49
+ <Tag
50
+ className={cx(isCustom ? styles.custom : isOfficial ? styles.official : styles.community)}
51
+ icon={showIcon && <Icon icon={isCustom ? Package : isOfficial ? BadgeCheck : CircleUser} />}
52
+ >
53
+ {showText && (author || t(isCustom ? 'store.customPlugin' : 'store.communityPlugin'))}
54
+ </Tag>
55
+ );
56
+ });
57
+
58
+ export default PluginTag;