@lobehub/lobehub 2.0.0-next.337 → 2.0.0-next.339

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 (270) hide show
  1. package/.gitattributes +35 -0
  2. package/CHANGELOG.md +69 -0
  3. package/changelog/v1.json +24 -0
  4. package/locales/ar/plugin.json +12 -2
  5. package/locales/ar/providers.json +1 -0
  6. package/locales/ar/setting.json +77 -1
  7. package/locales/bg-BG/models.json +5 -10
  8. package/locales/bg-BG/plugin.json +12 -2
  9. package/locales/bg-BG/providers.json +1 -0
  10. package/locales/bg-BG/setting.json +78 -2
  11. package/locales/de-DE/models.json +51 -9
  12. package/locales/de-DE/plugin.json +12 -2
  13. package/locales/de-DE/providers.json +1 -0
  14. package/locales/de-DE/setting.json +78 -2
  15. package/locales/en-US/models.json +11 -10
  16. package/locales/en-US/plugin.json +14 -4
  17. package/locales/en-US/providers.json +1 -0
  18. package/locales/en-US/setting.json +97 -2
  19. package/locales/es-ES/plugin.json +12 -2
  20. package/locales/es-ES/providers.json +1 -0
  21. package/locales/es-ES/setting.json +78 -2
  22. package/locales/fa-IR/plugin.json +12 -2
  23. package/locales/fa-IR/providers.json +1 -0
  24. package/locales/fa-IR/setting.json +78 -2
  25. package/locales/fr-FR/plugin.json +12 -2
  26. package/locales/fr-FR/providers.json +1 -0
  27. package/locales/fr-FR/setting.json +78 -2
  28. package/locales/it-IT/plugin.json +12 -2
  29. package/locales/it-IT/providers.json +1 -0
  30. package/locales/it-IT/setting.json +78 -2
  31. package/locales/ja-JP/plugin.json +12 -2
  32. package/locales/ja-JP/providers.json +1 -0
  33. package/locales/ja-JP/setting.json +78 -2
  34. package/locales/ko-KR/plugin.json +12 -2
  35. package/locales/ko-KR/providers.json +1 -0
  36. package/locales/ko-KR/setting.json +78 -2
  37. package/locales/nl-NL/models.json +4 -9
  38. package/locales/nl-NL/plugin.json +12 -2
  39. package/locales/nl-NL/providers.json +1 -0
  40. package/locales/nl-NL/setting.json +78 -2
  41. package/locales/pl-PL/plugin.json +12 -2
  42. package/locales/pl-PL/providers.json +1 -0
  43. package/locales/pl-PL/setting.json +78 -2
  44. package/locales/pt-BR/plugin.json +12 -2
  45. package/locales/pt-BR/providers.json +1 -0
  46. package/locales/pt-BR/setting.json +78 -2
  47. package/locales/ru-RU/plugin.json +12 -2
  48. package/locales/ru-RU/providers.json +1 -0
  49. package/locales/ru-RU/setting.json +78 -2
  50. package/locales/tr-TR/plugin.json +12 -2
  51. package/locales/tr-TR/providers.json +1 -0
  52. package/locales/tr-TR/setting.json +78 -2
  53. package/locales/vi-VN/plugin.json +12 -2
  54. package/locales/vi-VN/providers.json +1 -0
  55. package/locales/vi-VN/setting.json +77 -1
  56. package/locales/zh-CN/auth.json +1 -0
  57. package/locales/zh-CN/plugin.json +12 -2
  58. package/locales/zh-CN/providers.json +1 -0
  59. package/locales/zh-CN/setting.json +97 -2
  60. package/locales/zh-TW/plugin.json +12 -2
  61. package/locales/zh-TW/providers.json +1 -0
  62. package/locales/zh-TW/setting.json +78 -2
  63. package/package.json +1 -1
  64. package/packages/agent-runtime/src/groupOrchestration/GroupOrchestrationSupervisor.ts +2 -0
  65. package/packages/agent-runtime/src/groupOrchestration/__tests__/GroupOrchestrationSupervisor.test.ts +3 -1
  66. package/packages/agent-runtime/src/groupOrchestration/types.ts +5 -0
  67. package/packages/const/src/index.ts +1 -0
  68. package/packages/const/src/klavis.ts +144 -0
  69. package/packages/const/src/lobehubSkill.ts +34 -0
  70. package/packages/const/src/recommendedSkill.ts +17 -0
  71. package/packages/model-runtime/src/core/contextBuilders/anthropic.test.ts +38 -0
  72. package/packages/model-runtime/src/core/contextBuilders/anthropic.ts +20 -1
  73. package/packages/model-runtime/src/core/contextBuilders/google.test.ts +42 -0
  74. package/packages/model-runtime/src/core/contextBuilders/google.ts +17 -0
  75. package/packages/model-runtime/src/providers/google/index.ts +14 -14
  76. package/packages/model-runtime/src/providers/moonshot/index.ts +1 -1
  77. package/packages/model-runtime/src/providers/openai/index.ts +3 -3
  78. package/packages/types/src/discover/index.ts +1 -1
  79. package/scripts/electronWorkflow/modifiers/dynamicToStatic.mts +273 -0
  80. package/scripts/electronWorkflow/modifiers/index.mts +10 -0
  81. package/scripts/electronWorkflow/modifiers/nextConfig.mts +1 -0
  82. package/scripts/electronWorkflow/modifiers/nextDynamicToStatic.mts +233 -0
  83. package/scripts/electronWorkflow/modifiers/removeSuspense.mts +124 -0
  84. package/scripts/electronWorkflow/modifiers/routes.mts +14 -2
  85. package/scripts/electronWorkflow/modifiers/settingsContentToStatic.mts +148 -0
  86. package/scripts/electronWorkflow/modifiers/wrapChildrenWithClientOnly.mts +73 -0
  87. package/src/app/[variants]/(main)/agent/cron/[cronId]/CronConfig.ts +16 -16
  88. package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobSaveButton.tsx +1 -1
  89. package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobScheduleConfig.tsx +5 -2
  90. package/src/app/[variants]/(main)/community/features/Search.tsx +1 -1
  91. package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +131 -0
  92. package/src/app/[variants]/(main)/home/features/InputArea/index.tsx +34 -27
  93. package/src/app/[variants]/(main)/settings/features/SettingHeader.tsx +8 -4
  94. package/src/app/[variants]/(main)/settings/features/SettingsContent.tsx +3 -0
  95. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +6 -0
  96. package/src/{features/PluginStore/InstalledList/List/Item/Action.tsx → app/[variants]/(main)/settings/skill/features/Actions.tsx} +45 -40
  97. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +353 -0
  98. package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +344 -0
  99. package/src/app/[variants]/(main)/settings/skill/features/McpSkillItem.tsx +116 -0
  100. package/src/app/[variants]/(main)/settings/skill/features/SkillList.tsx +244 -0
  101. package/src/app/[variants]/(main)/settings/skill/index.tsx +35 -0
  102. package/src/app/[variants]/(mobile)/router/mobileRouter.config.tsx +27 -0
  103. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +8 -17
  104. package/src/app/[variants]/(mobile)/settings/_layout/index.tsx +6 -1
  105. package/src/app/[variants]/(mobile)/settings/provider/_layout/index.tsx +22 -0
  106. package/src/components/Plugins/PluginTag.tsx +23 -35
  107. package/src/components/client/ClientOnly.tsx +6 -2
  108. package/src/features/AgentSetting/AgentPlugin/index.tsx +2 -2
  109. package/src/features/ChatInput/ActionBar/Tools/KlavisServerItem.tsx +8 -32
  110. package/src/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem.tsx +8 -30
  111. package/src/features/ChatInput/ActionBar/Tools/PopoverContent.tsx +48 -59
  112. package/src/features/ChatInput/ActionBar/Tools/index.tsx +5 -23
  113. package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +158 -56
  114. package/src/features/IntegrationDetailModal/index.tsx +293 -0
  115. package/src/features/{PluginStore/McpList/Detail → MCP/MCPDetail}/index.tsx +15 -6
  116. package/src/features/MCP/MCPSettings/McpSettingsModal.tsx +58 -0
  117. package/src/features/{PluginStore/McpList/Detail/Settings → MCP/MCPSettings}/index.tsx +39 -27
  118. package/src/features/PluginDetailModal/index.tsx +2 -2
  119. package/src/features/PluginDevModal/index.tsx +16 -40
  120. package/src/features/ProfileEditor/AgentTool.tsx +2 -2
  121. package/src/features/ProtocolUrlHandler/InstallPlugin/OfficialPluginInstallModal/index.tsx +1 -1
  122. package/src/features/{PluginStore/AddPluginButton.tsx → SkillStore/AddSkillButton.tsx} +3 -3
  123. package/src/features/SkillStore/CommunityList/Item.tsx +158 -0
  124. package/src/features/SkillStore/CommunityList/index.tsx +101 -0
  125. package/src/features/SkillStore/Content.tsx +59 -0
  126. package/src/features/{PluginStore/PluginEmpty.tsx → SkillStore/Empty.tsx} +8 -8
  127. package/src/features/SkillStore/LobeHubList/Item.tsx +118 -0
  128. package/src/features/SkillStore/LobeHubList/index.tsx +187 -0
  129. package/src/features/SkillStore/LobeHubList/useSkillConnect.ts +239 -0
  130. package/src/features/SkillStore/Search/index.tsx +43 -0
  131. package/src/features/{PluginStore → SkillStore}/index.tsx +14 -10
  132. package/src/features/SkillStore/style.ts +27 -0
  133. package/src/locales/default/plugin.ts +15 -4
  134. package/src/locales/default/setting.ts +204 -2
  135. package/src/services/chat/mecha/agentConfigResolver.test.ts +197 -0
  136. package/src/services/chat/mecha/agentConfigResolver.ts +44 -17
  137. package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +40 -37
  138. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +78 -0
  139. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +50 -16
  140. package/src/store/global/initialState.ts +1 -0
  141. package/src/store/tool/slices/lobehubSkillStore/action.test.ts +914 -0
  142. package/src/store/tool/slices/lobehubSkillStore/selectors.test.ts +548 -0
  143. package/.cursor/skills/vercel-react-best-practices/AGENTS.md +0 -2410
  144. package/.cursor/skills/vercel-react-best-practices/SKILL.md +0 -125
  145. package/.cursor/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  146. package/.cursor/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -49
  147. package/.cursor/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
  148. package/.cursor/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
  149. package/.cursor/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -36
  150. package/.cursor/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
  151. package/.cursor/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
  152. package/.cursor/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
  153. package/.cursor/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
  154. package/.cursor/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
  155. package/.cursor/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  156. package/.cursor/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
  157. package/.cursor/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
  158. package/.cursor/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
  159. package/.cursor/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
  160. package/.cursor/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
  161. package/.cursor/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -57
  162. package/.cursor/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
  163. package/.cursor/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
  164. package/.cursor/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
  165. package/.cursor/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
  166. package/.cursor/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
  167. package/.cursor/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
  168. package/.cursor/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
  169. package/.cursor/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -49
  170. package/.cursor/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
  171. package/.cursor/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
  172. package/.cursor/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
  173. package/.cursor/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
  174. package/.cursor/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  175. package/.cursor/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
  176. package/.cursor/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
  177. package/.cursor/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  178. package/.cursor/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  179. package/.cursor/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
  180. package/.cursor/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
  181. package/.cursor/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
  182. package/.cursor/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
  183. package/.cursor/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
  184. package/.cursor/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  185. package/.cursor/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
  186. package/.cursor/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
  187. package/.cursor/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
  188. package/.cursor/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
  189. package/.cursor/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
  190. package/.cursor/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
  191. package/.cursor/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
  192. package/src/features/PluginStore/Content.tsx +0 -54
  193. package/src/features/PluginStore/InstalledList/Detail/CustomPluginEmptyState.tsx +0 -79
  194. package/src/features/PluginStore/InstalledList/Detail/index.tsx +0 -21
  195. package/src/features/PluginStore/InstalledList/List/Item/index.tsx +0 -61
  196. package/src/features/PluginStore/InstalledList/List/index.tsx +0 -72
  197. package/src/features/PluginStore/InstalledList/index.tsx +0 -90
  198. package/src/features/PluginStore/McpList/List/Action.tsx +0 -119
  199. package/src/features/PluginStore/McpList/List/Item.tsx +0 -83
  200. package/src/features/PluginStore/McpList/List/index.tsx +0 -93
  201. package/src/features/PluginStore/McpList/index.tsx +0 -58
  202. package/src/features/PluginStore/PluginList/Detail/DetailProvider.tsx +0 -19
  203. package/src/features/PluginStore/PluginList/Detail/EmptyState.tsx +0 -56
  204. package/src/features/PluginStore/PluginList/Detail/Header.tsx +0 -130
  205. package/src/features/PluginStore/PluginList/Detail/InstallDetail/Nav.tsx +0 -73
  206. package/src/features/PluginStore/PluginList/Detail/InstallDetail/Settings.tsx +0 -19
  207. package/src/features/PluginStore/PluginList/Detail/InstallDetail/Tools.tsx +0 -111
  208. package/src/features/PluginStore/PluginList/Detail/InstallDetail/index.tsx +0 -24
  209. package/src/features/PluginStore/PluginList/Detail/Loading.tsx +0 -42
  210. package/src/features/PluginStore/PluginList/Detail/TagList.tsx +0 -35
  211. package/src/features/PluginStore/PluginList/Detail/index.tsx +0 -39
  212. package/src/features/PluginStore/PluginList/Detail/useCategory.tsx +0 -76
  213. package/src/features/PluginStore/PluginList/List/Action.tsx +0 -78
  214. package/src/features/PluginStore/PluginList/List/Item.tsx +0 -92
  215. package/src/features/PluginStore/PluginList/List/index.tsx +0 -94
  216. package/src/features/PluginStore/PluginList/index.tsx +0 -46
  217. package/src/features/PluginStore/Search/index.tsx +0 -40
  218. /package/{.codex/skills → .agents}/vercel-react-best-practices/AGENTS.md +0 -0
  219. /package/{.codex/skills → .agents}/vercel-react-best-practices/SKILL.md +0 -0
  220. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -0
  221. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/advanced-use-latest.md +0 -0
  222. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/async-api-routes.md +0 -0
  223. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/async-defer-await.md +0 -0
  224. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/async-dependencies.md +0 -0
  225. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/async-parallel.md +0 -0
  226. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -0
  227. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -0
  228. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/bundle-conditional.md +0 -0
  229. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -0
  230. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -0
  231. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/bundle-preload.md +0 -0
  232. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/client-event-listeners.md +0 -0
  233. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -0
  234. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -0
  235. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/client-swr-dedup.md +0 -0
  236. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -0
  237. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-cache-function-results.md +0 -0
  238. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-cache-property-access.md +0 -0
  239. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-cache-storage.md +0 -0
  240. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-combine-iterations.md +0 -0
  241. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-early-exit.md +0 -0
  242. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -0
  243. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-index-maps.md +0 -0
  244. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-length-check-first.md +0 -0
  245. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-min-max-loop.md +0 -0
  246. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -0
  247. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -0
  248. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rendering-activity.md +0 -0
  249. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -0
  250. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -0
  251. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -0
  252. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -0
  253. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -0
  254. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -0
  255. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -0
  256. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rerender-dependencies.md +0 -0
  257. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rerender-derived-state.md +0 -0
  258. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -0
  259. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -0
  260. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rerender-memo.md +0 -0
  261. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/rerender-transitions.md +0 -0
  262. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -0
  263. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/server-cache-lru.md +0 -0
  264. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/server-cache-react.md +0 -0
  265. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -0
  266. /package/{.codex/skills → .agents}/vercel-react-best-practices/rules/server-serialization.md +0 -0
  267. /package/src/{features/PluginStore/InstalledList → app/[variants]/(main)/settings/skill/features}/EditCustomPlugin.tsx +0 -0
  268. /package/src/features/{PluginStore/McpList/Detail → MCP/MCPDetail}/Loading.tsx +0 -0
  269. /package/src/features/{PluginStore → SkillStore}/Loading.tsx +0 -0
  270. /package/src/features/{PluginStore → SkillStore}/VirtuosoLoading.tsx +0 -0
@@ -1,10 +1,12 @@
1
1
  import { ActionIcon, Button, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
2
2
  import { App } from 'antd';
3
- import { InfoIcon, MoreVerticalIcon, PackageSearch, Settings, Trash2 } from 'lucide-react';
3
+ import { MoreVerticalIcon, Trash2 } from 'lucide-react';
4
4
  import { memo, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
+ import McpSettingsModal from '@/features/MCP/MCPSettings/McpSettingsModal';
7
8
  import PluginDetailModal from '@/features/PluginDetailModal';
9
+ import EditCustomPlugin from './EditCustomPlugin';
8
10
  import { useAgentStore } from '@/store/agent';
9
11
  import { agentSelectors } from '@/store/agent/selectors';
10
12
  import { useServerConfigStore } from '@/store/serverConfig';
@@ -12,8 +14,6 @@ import { pluginHelpers, useToolStore } from '@/store/tool';
12
14
  import { pluginSelectors, pluginStoreSelectors } from '@/store/tool/selectors';
13
15
  import { type LobeToolType } from '@/types/tool/tool';
14
16
 
15
- import EditCustomPlugin from '../../EditCustomPlugin';
16
-
17
17
  interface ActionsProps {
18
18
  identifier: string;
19
19
  isMCP?: boolean;
@@ -34,55 +34,56 @@ const Actions = memo<ActionsProps>(({ identifier, type, isMCP }) => {
34
34
 
35
35
  const isCustomPlugin = type === 'customPlugin';
36
36
  const { t } = useTranslation('plugin');
37
- const [open, setOpen] = useState(false);
37
+ const [settingsOpen, setSettingsOpen] = useState(false);
38
38
  const plugin = useToolStore(pluginSelectors.getToolManifestById(identifier));
39
39
  const [togglePlugin, isPluginEnabledInAgent] = useAgentStore((s) => [
40
40
  s.togglePlugin,
41
41
  agentSelectors.currentAgentPlugins(s).includes(identifier),
42
42
  ]);
43
43
  const { modal } = App.useApp();
44
- const [tab, setTab] = useState('info');
45
44
  const hasSettings = pluginHelpers.isSettingSchemaNonEmpty(plugin?.settings);
46
45
 
47
46
  const [showModal, setModal] = useState(false);
47
+ const [mcpSettingsOpen, setMcpSettingsOpen] = useState(false);
48
+
49
+ // 自定义插件(包括自定义 MCP)使用 EditCustomPlugin
50
+ // 社区 MCP 使用 McpSettingsModal
51
+ // 传统插件使用 PluginDetailModal
52
+ const isCommunityMCP = !isCustomPlugin && isMCP;
53
+ const showConfigureButton = isCustomPlugin || isMCP || hasSettings;
54
+
55
+ const configureButton = (
56
+ <Button
57
+ onClick={() => {
58
+ if (isCustomPlugin) {
59
+ setModal(true);
60
+ } else if (isCommunityMCP) {
61
+ setMcpSettingsOpen(true);
62
+ } else {
63
+ setSettingsOpen(true);
64
+ }
65
+ }}
66
+ type="default"
67
+ >
68
+ {t('store.actions.configure')}
69
+ </Button>
70
+ );
48
71
 
49
72
  return (
50
73
  <>
51
- <Flexbox align={'center'} horizontal>
74
+ <Flexbox align={'center'} gap={8} horizontal onClick={(e) => e.stopPropagation()}>
52
75
  {installed ? (
53
76
  <>
54
- {isCustomPlugin && (
55
- <EditCustomPlugin identifier={identifier} onOpenChange={setModal} open={showModal}>
56
- <ActionIcon
57
- icon={PackageSearch}
58
- onClick={() => {
59
- setModal(true);
60
- }}
61
- title={t('store.actions.manifest')}
62
- />
63
- </EditCustomPlugin>
64
- )}
65
- {hasSettings && (
66
- <ActionIcon
67
- icon={Settings}
68
- onClick={() => {
69
- setOpen(true);
70
- setTab('settings');
71
- }}
72
- title={t('store.actions.settings')}
73
- />
74
- )}
77
+ {showConfigureButton &&
78
+ (isCustomPlugin ? (
79
+ <EditCustomPlugin identifier={identifier} onOpenChange={setModal} open={showModal}>
80
+ {configureButton}
81
+ </EditCustomPlugin>
82
+ ) : (
83
+ configureButton
84
+ ))}
75
85
  <DropdownMenu
76
86
  items={[
77
- {
78
- icon: <Icon icon={InfoIcon} />,
79
- key: 'detail',
80
- label: t('store.actions.detail'),
81
- onClick: () => {
82
- setOpen(true);
83
- setTab('info');
84
- },
85
- },
86
87
  {
87
88
  danger: true,
88
89
  icon: <Icon icon={Trash2} />,
@@ -131,12 +132,16 @@ const Actions = memo<ActionsProps>(({ identifier, type, isMCP }) => {
131
132
  <PluginDetailModal
132
133
  id={identifier}
133
134
  onClose={() => {
134
- setOpen(false);
135
+ setSettingsOpen(false);
135
136
  }}
136
- onTabChange={setTab}
137
- open={open}
137
+ open={settingsOpen}
138
138
  schema={plugin?.settings}
139
- tab={tab}
139
+ tab="settings"
140
+ />
141
+ <McpSettingsModal
142
+ identifier={identifier}
143
+ onClose={() => setMcpSettingsOpen(false)}
144
+ open={mcpSettingsOpen}
140
145
  />
141
146
  </>
142
147
  );
@@ -0,0 +1,353 @@
1
+ 'use client';
2
+
3
+ import { type KlavisServerType } from '@lobechat/const';
4
+ import { ActionIcon, Avatar, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
5
+ import { App, Button } from 'antd';
6
+ import { createStyles, cssVar } from 'antd-style';
7
+ import { Loader2, MoreVerticalIcon, SquareArrowOutUpRight, Unplug } from 'lucide-react';
8
+ import { memo, useCallback, useEffect, useRef, useState } from 'react';
9
+ import { useTranslation } from 'react-i18next';
10
+
11
+ import IntegrationDetailModal from '@/features/IntegrationDetailModal';
12
+ import { useToolStore } from '@/store/tool';
13
+ import { type KlavisServer, KlavisServerStatus } from '@/store/tool/slices/klavisStore';
14
+ import { useUserStore } from '@/store/user';
15
+ import { userProfileSelectors } from '@/store/user/selectors';
16
+
17
+ const POLL_INTERVAL_MS = 1000;
18
+ const POLL_TIMEOUT_MS = 15_000;
19
+
20
+ const useStyles = createStyles(({ css, token }) => ({
21
+ connected: css`
22
+ font-size: 14px;
23
+ color: ${token.colorSuccess};
24
+ `,
25
+ container: css`
26
+ padding-block: 12px;
27
+ padding-inline: 0;
28
+ `,
29
+ disconnected: css`
30
+ font-size: 14px;
31
+ color: ${token.colorTextTertiary};
32
+ `,
33
+ error: css`
34
+ font-size: 14px;
35
+ color: ${token.colorError};
36
+ `,
37
+ icon: css`
38
+ display: flex;
39
+ flex-shrink: 0;
40
+ align-items: center;
41
+ justify-content: center;
42
+
43
+ width: 48px;
44
+ height: 48px;
45
+ border-radius: 12px;
46
+
47
+ background: ${token.colorFillTertiary};
48
+ `,
49
+ pending: css`
50
+ font-size: 14px;
51
+ color: ${token.colorWarning};
52
+ `,
53
+ title: css`
54
+ cursor: pointer;
55
+ font-size: 15px;
56
+ font-weight: 500;
57
+ color: ${token.colorText};
58
+
59
+ &:hover {
60
+ color: ${token.colorPrimary};
61
+ }
62
+ `,
63
+ }));
64
+
65
+ interface KlavisSkillItemProps {
66
+ server?: KlavisServer;
67
+ serverType: KlavisServerType;
68
+ }
69
+
70
+ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
71
+ const { t } = useTranslation('setting');
72
+ const { styles } = useStyles();
73
+ const { modal } = App.useApp();
74
+ const [isConnecting, setIsConnecting] = useState(false);
75
+ const [isWaitingAuth, setIsWaitingAuth] = useState(false);
76
+ const [detailOpen, setDetailOpen] = useState(false);
77
+
78
+ const oauthWindowRef = useRef<Window | null>(null);
79
+ const windowCheckIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
80
+ const pollIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
81
+ const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
82
+
83
+ const userId = useUserStore(userProfileSelectors.userId);
84
+ const createKlavisServer = useToolStore((s) => s.createKlavisServer);
85
+ const refreshKlavisServerTools = useToolStore((s) => s.refreshKlavisServerTools);
86
+ const removeKlavisServer = useToolStore((s) => s.removeKlavisServer);
87
+
88
+ const cleanup = useCallback(() => {
89
+ if (windowCheckIntervalRef.current) {
90
+ clearInterval(windowCheckIntervalRef.current);
91
+ windowCheckIntervalRef.current = null;
92
+ }
93
+ if (pollIntervalRef.current) {
94
+ clearInterval(pollIntervalRef.current);
95
+ pollIntervalRef.current = null;
96
+ }
97
+ if (pollTimeoutRef.current) {
98
+ clearTimeout(pollTimeoutRef.current);
99
+ pollTimeoutRef.current = null;
100
+ }
101
+ oauthWindowRef.current = null;
102
+ setIsWaitingAuth(false);
103
+ }, []);
104
+
105
+ useEffect(() => {
106
+ return () => {
107
+ cleanup();
108
+ };
109
+ }, [cleanup]);
110
+
111
+ useEffect(() => {
112
+ if (server?.status === KlavisServerStatus.CONNECTED && isWaitingAuth) {
113
+ cleanup();
114
+ }
115
+ }, [server?.status, isWaitingAuth, cleanup]);
116
+
117
+ const startFallbackPolling = useCallback(
118
+ (serverName: string) => {
119
+ if (pollIntervalRef.current) return;
120
+
121
+ pollIntervalRef.current = setInterval(async () => {
122
+ try {
123
+ await refreshKlavisServerTools(serverName);
124
+ } catch (error) {
125
+ console.error('[Klavis] Failed to check auth status:', error);
126
+ }
127
+ }, POLL_INTERVAL_MS);
128
+
129
+ pollTimeoutRef.current = setTimeout(() => {
130
+ if (pollIntervalRef.current) {
131
+ clearInterval(pollIntervalRef.current);
132
+ pollIntervalRef.current = null;
133
+ }
134
+ setIsWaitingAuth(false);
135
+ }, POLL_TIMEOUT_MS);
136
+ },
137
+ [refreshKlavisServerTools],
138
+ );
139
+
140
+ const startWindowMonitor = useCallback(
141
+ (oauthWindow: Window, serverName: string) => {
142
+ windowCheckIntervalRef.current = setInterval(async () => {
143
+ try {
144
+ if (oauthWindow.closed) {
145
+ if (windowCheckIntervalRef.current) {
146
+ clearInterval(windowCheckIntervalRef.current);
147
+ windowCheckIntervalRef.current = null;
148
+ }
149
+ oauthWindowRef.current = null;
150
+ await refreshKlavisServerTools(serverName);
151
+ setIsWaitingAuth(false);
152
+ }
153
+ } catch {
154
+ console.log('[Klavis] COOP blocked window.closed access, falling back to polling');
155
+ if (windowCheckIntervalRef.current) {
156
+ clearInterval(windowCheckIntervalRef.current);
157
+ windowCheckIntervalRef.current = null;
158
+ }
159
+ startFallbackPolling(serverName);
160
+ }
161
+ }, 500);
162
+ },
163
+ [refreshKlavisServerTools, startFallbackPolling],
164
+ );
165
+
166
+ const openOAuthWindow = useCallback(
167
+ (oauthUrl: string, serverName: string) => {
168
+ cleanup();
169
+ setIsWaitingAuth(true);
170
+
171
+ const oauthWindow = window.open(oauthUrl, '_blank', 'width=600,height=700');
172
+ if (oauthWindow) {
173
+ oauthWindowRef.current = oauthWindow;
174
+ startWindowMonitor(oauthWindow, serverName);
175
+ } else {
176
+ startFallbackPolling(serverName);
177
+ }
178
+ },
179
+ [cleanup, startWindowMonitor, startFallbackPolling],
180
+ );
181
+
182
+ const handleConnect = async () => {
183
+ if (!userId) return;
184
+ if (server) return;
185
+
186
+ setIsConnecting(true);
187
+ try {
188
+ const newServer = await createKlavisServer({
189
+ identifier: serverType.identifier,
190
+ serverName: serverType.serverName,
191
+ userId,
192
+ });
193
+
194
+ if (newServer) {
195
+ if (newServer.isAuthenticated) {
196
+ await refreshKlavisServerTools(newServer.identifier);
197
+ } else if (newServer.oauthUrl) {
198
+ openOAuthWindow(newServer.oauthUrl, newServer.identifier);
199
+ }
200
+ }
201
+ } catch (error) {
202
+ console.error('[Klavis] Failed to connect server:', error);
203
+ } finally {
204
+ setIsConnecting(false);
205
+ }
206
+ };
207
+
208
+ const handleDisconnect = () => {
209
+ if (!server) return;
210
+ modal.confirm({
211
+ cancelText: t('cancel', { ns: 'common' }),
212
+ centered: true,
213
+ content: t('tools.lobehubSkill.disconnectConfirm.desc', { name: serverType.label }),
214
+ okButtonProps: { danger: true },
215
+ okText: t('tools.lobehubSkill.disconnect'),
216
+ onOk: async () => {
217
+ await removeKlavisServer(server.identifier);
218
+ },
219
+ title: t('tools.lobehubSkill.disconnectConfirm.title', { name: serverType.label }),
220
+ });
221
+ };
222
+
223
+ const renderIcon = () => {
224
+ const { icon, label } = serverType;
225
+ if (typeof icon === 'string') {
226
+ return <Avatar alt={label} avatar={icon} size={32} />;
227
+ }
228
+ return <Icon fill={cssVar.colorText} icon={icon} size={32} />;
229
+ };
230
+
231
+ const renderStatus = () => {
232
+ if (!server) {
233
+ return (
234
+ <span className={styles.disconnected}>
235
+ {t('tools.klavis.disconnected', { defaultValue: 'Disconnected' })}
236
+ </span>
237
+ );
238
+ }
239
+
240
+ switch (server.status) {
241
+ case KlavisServerStatus.CONNECTED: {
242
+ return <span className={styles.connected}>{t('tools.klavis.connected')}</span>;
243
+ }
244
+ case KlavisServerStatus.PENDING_AUTH: {
245
+ return <span className={styles.pending}>{t('tools.klavis.authRequired')}</span>;
246
+ }
247
+ case KlavisServerStatus.ERROR: {
248
+ return <span className={styles.error}>{t('tools.klavis.error')}</span>;
249
+ }
250
+ default: {
251
+ return (
252
+ <span className={styles.disconnected}>
253
+ {t('tools.klavis.disconnected', { defaultValue: 'Disconnected' })}
254
+ </span>
255
+ );
256
+ }
257
+ }
258
+ };
259
+
260
+ const renderAction = () => {
261
+ if (isConnecting || isWaitingAuth) {
262
+ return (
263
+ <Button disabled icon={<Icon icon={Loader2} spin />} type="default">
264
+ {t('tools.klavis.connect', { defaultValue: 'Connect' })}
265
+ </Button>
266
+ );
267
+ }
268
+
269
+ if (!server) {
270
+ return (
271
+ <Button icon={<Icon icon={SquareArrowOutUpRight} />} onClick={handleConnect} type="default">
272
+ {t('tools.klavis.connect', { defaultValue: 'Connect' })}
273
+ </Button>
274
+ );
275
+ }
276
+
277
+ if (server.status === KlavisServerStatus.PENDING_AUTH) {
278
+ return (
279
+ <Button
280
+ icon={<Icon icon={SquareArrowOutUpRight} />}
281
+ onClick={() => {
282
+ if (server.oauthUrl) {
283
+ openOAuthWindow(server.oauthUrl, server.identifier);
284
+ }
285
+ }}
286
+ type="default"
287
+ >
288
+ {t('tools.klavis.pendingAuth', { defaultValue: 'Authorize' })}
289
+ </Button>
290
+ );
291
+ }
292
+
293
+ if (server.status === KlavisServerStatus.CONNECTED) {
294
+ return (
295
+ <DropdownMenu
296
+ items={[
297
+ {
298
+ icon: <Icon icon={Unplug} />,
299
+ key: 'disconnect',
300
+ label: t('tools.klavis.disconnect', { defaultValue: 'Disconnect' }),
301
+ onClick: handleDisconnect,
302
+ },
303
+ ]}
304
+ placement="bottomRight"
305
+ >
306
+ <ActionIcon icon={MoreVerticalIcon} />
307
+ </DropdownMenu>
308
+ );
309
+ }
310
+
311
+ return null;
312
+ };
313
+
314
+ const isConnected = server?.status === KlavisServerStatus.CONNECTED;
315
+
316
+ return (
317
+ <>
318
+ <Flexbox
319
+ align="center"
320
+ className={styles.container}
321
+ gap={16}
322
+ horizontal
323
+ justify="space-between"
324
+ >
325
+ <Flexbox align="center" gap={16} horizontal style={{ flex: 1, overflow: 'hidden' }}>
326
+ <div className={styles.icon}>{renderIcon()}</div>
327
+ <Flexbox gap={4} style={{ overflow: 'hidden' }}>
328
+ <span className={styles.title} onClick={() => setDetailOpen(true)}>
329
+ {serverType.label}
330
+ </span>
331
+ {!isConnected && renderStatus()}
332
+ </Flexbox>
333
+ </Flexbox>
334
+ <Flexbox align="center" gap={12} horizontal>
335
+ {isConnected && renderStatus()}
336
+ {renderAction()}
337
+ </Flexbox>
338
+ </Flexbox>
339
+ <IntegrationDetailModal
340
+ identifier={serverType.identifier}
341
+ isConnecting={isConnecting || isWaitingAuth}
342
+ onClose={() => setDetailOpen(false)}
343
+ onConnect={handleConnect}
344
+ open={detailOpen}
345
+ type="klavis"
346
+ />
347
+ </>
348
+ );
349
+ });
350
+
351
+ KlavisSkillItem.displayName = 'KlavisSkillItem';
352
+
353
+ export default KlavisSkillItem;