@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
@@ -3,6 +3,8 @@ import {
3
3
  type KlavisServerType,
4
4
  LOBEHUB_SKILL_PROVIDERS,
5
5
  type LobehubSkillProviderType,
6
+ RECOMMENDED_SKILLS,
7
+ RecommendedSkillType,
6
8
  } from '@lobechat/const';
7
9
  import { Avatar, Flexbox, Icon, Image, type ItemType } from '@lobehub/ui';
8
10
  import { cssVar } from 'antd-style';
@@ -123,11 +125,44 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
123
125
  [builtinList, allKlavisTypeIdentifiers, isKlavisEnabledInEnv],
124
126
  );
125
127
 
126
- // Klavis 服务器列表项
128
+ // 获取推荐的 Klavis skill IDs
129
+ const recommendedKlavisIds = useMemo(
130
+ () =>
131
+ new Set(
132
+ RECOMMENDED_SKILLS.filter((s) => s.type === RecommendedSkillType.Klavis).map((s) => s.id),
133
+ ),
134
+ [],
135
+ );
136
+
137
+ // 获取推荐的 Lobehub skill IDs
138
+ const recommendedLobehubIds = useMemo(
139
+ () =>
140
+ new Set(
141
+ RECOMMENDED_SKILLS.filter((s) => s.type === RecommendedSkillType.Lobehub).map((s) => s.id),
142
+ ),
143
+ [],
144
+ );
145
+
146
+ // 获取已安装的 Klavis server IDs
147
+ const installedKlavisIds = useMemo(
148
+ () => new Set(allKlavisServers.map((s) => s.identifier)),
149
+ [allKlavisServers],
150
+ );
151
+
152
+ // 获取已安装的 Lobehub skill IDs
153
+ const installedLobehubIds = useMemo(
154
+ () => new Set(allLobehubSkillServers.map((s) => s.identifier)),
155
+ [allLobehubSkillServers],
156
+ );
157
+
158
+ // Klavis 服务器列表项 - 只展示已安装或推荐的
127
159
  const klavisServerItems = useMemo(
128
160
  () =>
129
161
  isKlavisEnabledInEnv
130
- ? KLAVIS_SERVER_TYPES.map((type) => ({
162
+ ? KLAVIS_SERVER_TYPES.filter(
163
+ (type) =>
164
+ installedKlavisIds.has(type.identifier) || recommendedKlavisIds.has(type.identifier),
165
+ ).map((type) => ({
131
166
  icon: <KlavisIcon icon={type.icon} label={type.label} />,
132
167
  key: type.identifier,
133
168
  label: (
@@ -140,27 +175,29 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
140
175
  ),
141
176
  }))
142
177
  : [],
143
- [isKlavisEnabledInEnv, allKlavisServers],
178
+ [isKlavisEnabledInEnv, allKlavisServers, installedKlavisIds, recommendedKlavisIds],
144
179
  );
145
180
 
146
- // LobeHub Skill Provider 列表项
181
+ // LobeHub Skill Provider 列表项 - 只展示已安装或推荐的
147
182
  const lobehubSkillItems = useMemo(
148
183
  () =>
149
184
  isLobehubSkillEnabled
150
- ? LOBEHUB_SKILL_PROVIDERS.map((provider) => ({
185
+ ? LOBEHUB_SKILL_PROVIDERS.filter(
186
+ (provider) =>
187
+ installedLobehubIds.has(provider.id) || recommendedLobehubIds.has(provider.id),
188
+ ).map((provider) => ({
151
189
  icon: <LobehubSkillIcon icon={provider.icon} label={provider.label} />,
152
190
  key: provider.id, // 使用 provider.id 作为 key,与 pluginId 保持一致
153
191
  label: <LobehubSkillServerItem label={provider.label} provider={provider.id} />,
154
192
  }))
155
193
  : [],
156
- [isLobehubSkillEnabled, allLobehubSkillServers],
194
+ [isLobehubSkillEnabled, allLobehubSkillServers, installedLobehubIds, recommendedLobehubIds],
157
195
  );
158
196
 
159
- // 合并 builtin 工具、Klavis 服务器和 LobeHub Skill Provider
197
+ // Builtin 工具列表项(不包含 Klavis LobeHub Skill
160
198
  const builtinItems = useMemo(
161
- () => [
162
- // 原有的 builtin 工具
163
- ...filteredBuiltinList.map((item) => ({
199
+ () =>
200
+ filteredBuiltinList.map((item) => ({
164
201
  icon: (
165
202
  <Avatar avatar={item.meta.avatar} shape={'square'} size={20} style={{ flex: 'none' }} />
166
203
  ),
@@ -178,14 +215,59 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
178
215
  />
179
216
  ),
180
217
  })),
181
- // LobeHub Skill Providers
182
- ...lobehubSkillItems,
183
- // Klavis 服务器
184
- ...klavisServerItems,
185
- ],
186
- [filteredBuiltinList, klavisServerItems, lobehubSkillItems, checked, togglePlugin, setUpdating],
218
+ [filteredBuiltinList, checked, togglePlugin, setUpdating],
187
219
  );
188
220
 
221
+ // Skills 列表项(包含 LobeHub Skill 和 Klavis)
222
+ const skillItems = useMemo(
223
+ () => [...lobehubSkillItems, ...klavisServerItems],
224
+ [lobehubSkillItems, klavisServerItems],
225
+ );
226
+
227
+ // 区分社区插件和自定义插件
228
+ const communityPlugins = list.filter((item) => item.type !== 'customPlugin');
229
+ const customPlugins = list.filter((item) => item.type === 'customPlugin');
230
+
231
+ // 生成插件列表项的函数
232
+ const mapPluginToItem = (item: (typeof list)[0]) => ({
233
+ icon: item?.avatar ? (
234
+ <PluginAvatar avatar={item.avatar} size={20} />
235
+ ) : (
236
+ <Icon icon={ToyBrick} size={20} />
237
+ ),
238
+ key: item.identifier,
239
+ label: (
240
+ <ToolItem
241
+ checked={checked.includes(item.identifier)}
242
+ id={item.identifier}
243
+ label={item.title}
244
+ onUpdate={async () => {
245
+ setUpdating(true);
246
+ await togglePlugin(item.identifier);
247
+ setUpdating(false);
248
+ }}
249
+ />
250
+ ),
251
+ });
252
+
253
+ // 构建 Skills 分组的 children
254
+ const skillGroupChildren: ItemType[] = [
255
+ // 1. LobeHub Skill 和 Klavis
256
+ ...skillItems,
257
+ // 2. divider (如果有 skillItems 且有社区插件)
258
+ ...(skillItems.length > 0 && communityPlugins.length > 0
259
+ ? [{ key: 'divider-skill-community', type: 'divider' as const }]
260
+ : []),
261
+ // 3. 社区插件
262
+ ...communityPlugins.map(mapPluginToItem),
263
+ // 4. divider (如果有自定义插件)
264
+ ...(customPlugins.length > 0
265
+ ? [{ key: 'divider-community-custom', type: 'divider' as const }]
266
+ : []),
267
+ // 5. 自定义插件
268
+ ...customPlugins.map(mapPluginToItem),
269
+ ];
270
+
189
271
  // 市场 tab 的 items
190
272
  const marketItems: ItemType[] = [
191
273
  {
@@ -195,26 +277,7 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
195
277
  type: 'group',
196
278
  },
197
279
  {
198
- children: list.map((item) => ({
199
- icon: item?.avatar ? (
200
- <PluginAvatar avatar={item.avatar} size={20} />
201
- ) : (
202
- <Icon icon={ToyBrick} size={20} />
203
- ),
204
- key: item.identifier,
205
- label: (
206
- <ToolItem
207
- checked={checked.includes(item.identifier)}
208
- id={item.identifier}
209
- label={item.title}
210
- onUpdate={async () => {
211
- setUpdating(true);
212
- await togglePlugin(item.identifier);
213
- setUpdating(false);
214
- }}
215
- />
216
- ),
217
- })),
280
+ children: skillGroupChildren,
218
281
  key: 'plugins',
219
282
  label: (
220
283
  <Flexbox align={'center'} gap={40} horizontal justify={'space-between'}>
@@ -256,7 +319,16 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
256
319
  ),
257
320
  }));
258
321
 
259
- // 已连接的 Klavis 服务器(放在 builtin 里面)
322
+ if (enabledBuiltinItems.length > 0) {
323
+ installedItems.push({
324
+ children: enabledBuiltinItems,
325
+ key: 'installed-builtins',
326
+ label: t('tools.builtins.groupName'),
327
+ type: 'group',
328
+ });
329
+ }
330
+
331
+ // 已连接的 Klavis 服务器
260
332
  const connectedKlavisItems = klavisServerItems.filter((item) =>
261
333
  checked.includes(item.key as string),
262
334
  );
@@ -266,24 +338,35 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
266
338
  checked.includes(item.key as string),
267
339
  );
268
340
 
269
- // 合并 builtin、KlavisLobeHub Skill
270
- const allBuiltinItems = [
271
- ...enabledBuiltinItems,
272
- ...connectedKlavisItems,
273
- ...connectedLobehubSkillItems,
274
- ];
341
+ // 合并已启用的 LobeHub Skill Klavis
342
+ const enabledSkillItems = [...connectedLobehubSkillItems, ...connectedKlavisItems];
275
343
 
276
- if (allBuiltinItems.length > 0) {
277
- installedItems.push({
278
- children: allBuiltinItems,
279
- key: 'installed-builtins',
280
- label: t('tools.builtins.groupName'),
281
- type: 'group',
282
- });
283
- }
344
+ // 已启用的社区插件
345
+ const enabledCommunityPlugins = communityPlugins
346
+ .filter((item) => checked.includes(item.identifier))
347
+ .map((item) => ({
348
+ icon: item?.avatar ? (
349
+ <PluginAvatar avatar={item.avatar} size={20} />
350
+ ) : (
351
+ <Icon icon={ToyBrick} size={20} />
352
+ ),
353
+ key: item.identifier,
354
+ label: (
355
+ <ToolItem
356
+ checked={true}
357
+ id={item.identifier}
358
+ label={item.title}
359
+ onUpdate={async () => {
360
+ setUpdating(true);
361
+ await togglePlugin(item.identifier);
362
+ setUpdating(false);
363
+ }}
364
+ />
365
+ ),
366
+ }));
284
367
 
285
- // 已安装的插件
286
- const installedPlugins = list
368
+ // 已启用的自定义插件
369
+ const enabledCustomPlugins = customPlugins
287
370
  .filter((item) => checked.includes(item.identifier))
288
371
  .map((item) => ({
289
372
  icon: item?.avatar ? (
@@ -306,9 +389,27 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
306
389
  ),
307
390
  }));
308
391
 
309
- if (installedPlugins.length > 0) {
392
+ // 构建 Skills 分组的 children(带 divider)
393
+ const allSkillItems: ItemType[] = [
394
+ // 1. LobeHub Skill 和 Klavis
395
+ ...enabledSkillItems,
396
+ // 2. divider (如果有 skillItems 且有社区插件)
397
+ ...(enabledSkillItems.length > 0 && enabledCommunityPlugins.length > 0
398
+ ? [{ key: 'installed-divider-skill-community', type: 'divider' as const }]
399
+ : []),
400
+ // 3. 社区插件
401
+ ...enabledCommunityPlugins,
402
+ // 4. divider (如果有自定义插件)
403
+ ...(enabledCustomPlugins.length > 0
404
+ ? [{ key: 'installed-divider-community-custom', type: 'divider' as const }]
405
+ : []),
406
+ // 5. 自定义插件
407
+ ...enabledCustomPlugins,
408
+ ];
409
+
410
+ if (allSkillItems.length > 0) {
310
411
  installedItems.push({
311
- children: installedPlugins,
412
+ children: allSkillItems,
312
413
  key: 'installed-plugins',
313
414
  label: t('tools.plugins.groupName'),
314
415
  type: 'group',
@@ -318,7 +419,8 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean)
318
419
  return installedItems;
319
420
  }, [
320
421
  filteredBuiltinList,
321
- list,
422
+ communityPlugins,
423
+ customPlugins,
322
424
  klavisServerItems,
323
425
  lobehubSkillItems,
324
426
  checked,
@@ -0,0 +1,293 @@
1
+ 'use client';
2
+
3
+ import {
4
+ type KlavisServerType,
5
+ type LobehubSkillProviderType,
6
+ getKlavisServerByServerIdentifier,
7
+ getLobehubSkillProviderById,
8
+ } from '@lobechat/const';
9
+ import { Flexbox, Icon, Image, Modal, Tag, Text, Typography } from '@lobehub/ui';
10
+ import { Button, Divider } from 'antd';
11
+ import { createStyles, cssVar } from 'antd-style';
12
+ import { ExternalLink, Loader2, SquareArrowOutUpRight } from 'lucide-react';
13
+ import { memo, useMemo } from 'react';
14
+ import { useTranslation } from 'react-i18next';
15
+
16
+ import { useToolStore } from '@/store/tool';
17
+ import { klavisStoreSelectors, lobehubSkillStoreSelectors } from '@/store/tool/selectors';
18
+ import { KlavisServerStatus } from '@/store/tool/slices/klavisStore';
19
+ import { LobehubSkillStatus } from '@/store/tool/slices/lobehubSkillStore/types';
20
+
21
+ const useStyles = createStyles(({ css, token }) => ({
22
+ authorLink: css`
23
+ cursor: pointer;
24
+
25
+ display: inline-flex;
26
+ gap: 4px;
27
+ align-items: center;
28
+
29
+ color: ${token.colorPrimary};
30
+
31
+ &:hover {
32
+ text-decoration: underline;
33
+ }
34
+ `,
35
+ detailItem: css`
36
+ display: flex;
37
+ flex-direction: column;
38
+ gap: 4px;
39
+ `,
40
+ detailLabel: css`
41
+ font-size: 12px;
42
+ color: ${token.colorTextTertiary};
43
+ `,
44
+ header: css`
45
+ display: flex;
46
+ gap: 16px;
47
+ align-items: center;
48
+
49
+ padding: 16px;
50
+ border-radius: 12px;
51
+
52
+ background: ${token.colorFillTertiary};
53
+ `,
54
+ icon: css`
55
+ display: flex;
56
+ flex-shrink: 0;
57
+ align-items: center;
58
+ justify-content: center;
59
+
60
+ width: 56px;
61
+ height: 56px;
62
+ border-radius: 12px;
63
+
64
+ background: ${token.colorBgContainer};
65
+ `,
66
+ introduction: css`
67
+ font-size: 14px;
68
+ line-height: 1.8;
69
+ color: ${token.colorText};
70
+ `,
71
+ sectionTitle: css`
72
+ font-size: 14px;
73
+ font-weight: 600;
74
+ color: ${token.colorText};
75
+ `,
76
+ title: css`
77
+ font-size: 18px;
78
+ font-weight: 600;
79
+ color: ${token.colorText};
80
+ `,
81
+ toolTag: css`
82
+ font-family: ${token.fontFamilyCode};
83
+ font-size: 12px;
84
+ `,
85
+ toolsContainer: css`
86
+ display: flex;
87
+ flex-wrap: wrap;
88
+ gap: 8px;
89
+ `,
90
+ trustWarning: css`
91
+ font-size: 12px;
92
+ line-height: 1.6;
93
+ color: ${token.colorTextTertiary};
94
+ `,
95
+ }));
96
+
97
+ export type IntegrationType = 'klavis' | 'lobehub';
98
+
99
+ export interface IntegrationDetailModalProps {
100
+ identifier: string;
101
+ isConnecting?: boolean;
102
+ onClose: () => void;
103
+ onConnect?: () => void;
104
+ open: boolean;
105
+ type: IntegrationType;
106
+ }
107
+
108
+ const IntegrationDetailModal = memo<IntegrationDetailModalProps>(
109
+ ({ open, onClose, type, identifier, isConnecting, onConnect }) => {
110
+ const { styles } = useStyles();
111
+ const { t } = useTranslation(['plugin', 'setting']);
112
+
113
+ // Get static config based on type
114
+ const config = useMemo((): KlavisServerType | LobehubSkillProviderType | undefined => {
115
+ if (type === 'klavis') {
116
+ return getKlavisServerByServerIdentifier(identifier);
117
+ }
118
+ return getLobehubSkillProviderById(identifier);
119
+ }, [type, identifier]);
120
+
121
+ // Get dynamic state from store
122
+ const klavisServers = useToolStore(klavisStoreSelectors.getServers);
123
+ const lobehubSkillServers = useToolStore(lobehubSkillStoreSelectors.getServers);
124
+
125
+ const serverState = useMemo(() => {
126
+ if (type === 'klavis') {
127
+ return klavisServers.find((s) => s.identifier === identifier);
128
+ }
129
+ return lobehubSkillServers.find((s) => s.identifier === identifier);
130
+ }, [type, identifier, klavisServers, lobehubSkillServers]);
131
+
132
+ const isConnected = useMemo(() => {
133
+ if (!serverState) return false;
134
+ if (type === 'klavis') {
135
+ return serverState.status === KlavisServerStatus.CONNECTED;
136
+ }
137
+ return serverState.status === LobehubSkillStatus.CONNECTED;
138
+ }, [type, serverState]);
139
+
140
+ const tools = useMemo(() => {
141
+ return serverState?.tools?.map((tool) => tool.name) || [];
142
+ }, [serverState]);
143
+
144
+ if (!config) return null;
145
+
146
+ const { author, authorUrl, description, icon, introduction, label } = config;
147
+
148
+ // Get identifier for i18n keys
149
+ const i18nIdentifier =
150
+ type === 'klavis'
151
+ ? (config as KlavisServerType).identifier
152
+ : (config as LobehubSkillProviderType).id;
153
+ const i18nPrefix = type === 'klavis' ? 'tools.klavis.servers' : 'tools.lobehubSkill.providers';
154
+
155
+ const localizedDescription = t(`${i18nPrefix}.${i18nIdentifier}.description`, {
156
+ defaultValue: description,
157
+ ns: 'setting',
158
+ });
159
+ const localizedIntroduction = t(`${i18nPrefix}.${i18nIdentifier}.introduction`, {
160
+ defaultValue: introduction,
161
+ ns: 'setting',
162
+ });
163
+
164
+ const renderIcon = () => {
165
+ if (typeof icon === 'string') {
166
+ return <Image alt={label} height={36} src={icon} width={36} />;
167
+ }
168
+ return <Icon fill={cssVar.colorText} icon={icon} size={36} />;
169
+ };
170
+
171
+ const handleAuthorClick = () => {
172
+ if (authorUrl) {
173
+ window.open(authorUrl, '_blank', 'noopener,noreferrer');
174
+ }
175
+ };
176
+
177
+ const renderConnectButton = () => {
178
+ if (isConnected) return null;
179
+ if (!onConnect) return null;
180
+
181
+ if (isConnecting) {
182
+ return (
183
+ <Button disabled icon={<Icon icon={Loader2} spin />} type="default">
184
+ {t('tools.klavis.connect', { defaultValue: 'Connect', ns: 'setting' })}
185
+ </Button>
186
+ );
187
+ }
188
+
189
+ return (
190
+ <Button icon={<Icon icon={SquareArrowOutUpRight} />} onClick={onConnect} type="primary">
191
+ {t('tools.klavis.connect', { defaultValue: 'Connect', ns: 'setting' })}
192
+ </Button>
193
+ );
194
+ };
195
+
196
+ return (
197
+ <Modal
198
+ destroyOnHidden
199
+ footer={null}
200
+ onCancel={onClose}
201
+ open={open}
202
+ title={t('dev.title.skillDetails')}
203
+ width={800}
204
+ >
205
+ <Flexbox gap={20}>
206
+ {/* Header */}
207
+ <Flexbox
208
+ align="center"
209
+ className={styles.header}
210
+ horizontal
211
+ justify="space-between"
212
+ style={{ flexWrap: 'nowrap' }}
213
+ >
214
+ <Flexbox align="center" gap={16} horizontal>
215
+ <div className={styles.icon}>{renderIcon()}</div>
216
+ <Flexbox gap={4}>
217
+ <span className={styles.title}>{label}</span>
218
+ <Text style={{ fontSize: 14 }} type="secondary">
219
+ {localizedDescription}
220
+ </Text>
221
+ </Flexbox>
222
+ </Flexbox>
223
+ {renderConnectButton()}
224
+ </Flexbox>
225
+
226
+ {/* Introduction */}
227
+ <Typography className={styles.introduction}>{localizedIntroduction}</Typography>
228
+
229
+ {/* Developed by */}
230
+ <Flexbox gap={8}>
231
+ <Flexbox align="center" gap={4} horizontal>
232
+ <span className={styles.sectionTitle}>{t('integrationDetail.developedBy')}</span>
233
+ <span
234
+ className={styles.authorLink}
235
+ onClick={handleAuthorClick}
236
+ style={{ cursor: authorUrl ? 'pointer' : 'default' }}
237
+ >
238
+ {author}
239
+ {authorUrl && <Icon icon={ExternalLink} size={12} />}
240
+ </span>
241
+ </Flexbox>
242
+ <Text className={styles.trustWarning} type="secondary">
243
+ {t('integrationDetail.trustWarning')}
244
+ </Text>
245
+ </Flexbox>
246
+
247
+ {/* Tools */}
248
+ {tools.length > 0 && (
249
+ <>
250
+ <Divider style={{ margin: 0 }} />
251
+ <Flexbox gap={12}>
252
+ <Flexbox align="center" gap={8} horizontal>
253
+ <span className={styles.sectionTitle}>{t('integrationDetail.tools')}</span>
254
+ <Tag>{tools.length}</Tag>
255
+ </Flexbox>
256
+ <div className={styles.toolsContainer}>
257
+ {tools.map((tool) => (
258
+ <Tag className={styles.toolTag} key={tool}>
259
+ {tool}
260
+ </Tag>
261
+ ))}
262
+ </div>
263
+ </Flexbox>
264
+ </>
265
+ )}
266
+
267
+ {/* Details */}
268
+ <Divider style={{ margin: 0 }} />
269
+ <Flexbox gap={12}>
270
+ <span className={styles.sectionTitle}>{t('integrationDetail.details')}</span>
271
+ <Flexbox gap={16} horizontal>
272
+ <div className={styles.detailItem}>
273
+ <span className={styles.detailLabel}>{t('integrationDetail.author')}</span>
274
+ <span
275
+ className={styles.authorLink}
276
+ onClick={handleAuthorClick}
277
+ style={{ cursor: authorUrl ? 'pointer' : 'default' }}
278
+ >
279
+ {author}
280
+ {authorUrl && <Icon icon={ExternalLink} size={12} />}
281
+ </span>
282
+ </div>
283
+ </Flexbox>
284
+ </Flexbox>
285
+ </Flexbox>
286
+ </Modal>
287
+ );
288
+ },
289
+ );
290
+
291
+ IntegrationDetailModal.displayName = 'IntegrationDetailModal';
292
+
293
+ export default IntegrationDetailModal;
@@ -11,18 +11,20 @@ import Nav from '@/features/MCPPluginDetail/Nav';
11
11
  import Overview from '@/features/MCPPluginDetail/Overview';
12
12
  import Schema from '@/features/MCPPluginDetail/Schema';
13
13
  import Score from '@/features/MCPPluginDetail/Score';
14
- import DetailLoading from '@/features/PluginStore/McpList/Detail/Loading';
15
14
  import { useDiscoverStore } from '@/store/discover';
16
15
  import { useToolStore } from '@/store/tool';
17
16
  import { McpNavKey } from '@/types/discover';
18
17
 
19
- import Settings from './Settings';
18
+ import Settings from '../MCPSettings';
19
+ import Loading from './Loading';
20
20
 
21
21
  interface DetailProps {
22
+ defaultTab?: McpNavKey;
22
23
  identifier?: string;
24
+ noSettings?: boolean;
23
25
  }
24
- const Detail = memo<DetailProps>(({ identifier: defaultIdentifier }) => {
25
- const [activeTab, setActiveTab] = useState(McpNavKey.Overview);
26
+ const Detail = memo<DetailProps>(({ identifier: defaultIdentifier, defaultTab, noSettings }) => {
27
+ const [activeTab, setActiveTab] = useState(defaultTab ?? McpNavKey.Overview);
26
28
  const { t } = useTranslation('plugin');
27
29
 
28
30
  const theme = useTheme(); // Keep for colorBgContainerSecondary (not in cssVar)
@@ -36,7 +38,9 @@ const Detail = memo<DetailProps>(({ identifier: defaultIdentifier }) => {
36
38
  const useMcpDetail = useDiscoverStore((s) => s.useFetchMcpDetail);
37
39
  const { data, isLoading } = useMcpDetail({ identifier });
38
40
 
39
- if (!isMcpListInit || isLoading) return <DetailLoading />;
41
+ // 如果有明确传入的 identifier,跳过 isMcpListInit 检查
42
+ const shouldWaitForInit = !defaultIdentifier && !isMcpListInit;
43
+ if (shouldWaitForInit || isLoading) return <Loading />;
40
44
 
41
45
  if (!identifier)
42
46
  return (
@@ -60,7 +64,12 @@ const Detail = memo<DetailProps>(({ identifier: defaultIdentifier }) => {
60
64
  <DetailProvider config={data}>
61
65
  <Flexbox gap={16}>
62
66
  <Header inModal />
63
- <Nav activeTab={activeTab as McpNavKey} inModal setActiveTab={setActiveTab} />
67
+ <Nav
68
+ activeTab={activeTab as McpNavKey}
69
+ inModal
70
+ noSettings={noSettings}
71
+ setActiveTab={setActiveTab}
72
+ />
64
73
  <Flexbox gap={24}>
65
74
  {activeTab === McpNavKey.Settings && <Settings identifier={identifier} />}
66
75
  {activeTab === McpNavKey.Overview && <Overview inModal />}
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+
3
+ import { Button, Flexbox, Modal } from '@lobehub/ui';
4
+ import { memo, useRef } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import Settings, { type SettingsRef } from './index';
8
+
9
+ interface McpSettingsModalProps {
10
+ identifier: string;
11
+ onClose: () => void;
12
+ open: boolean;
13
+ }
14
+
15
+ const McpSettingsModal = memo<McpSettingsModalProps>(({ identifier, open, onClose }) => {
16
+ const { t } = useTranslation(['plugin', 'common']);
17
+ const settingsRef = useRef<SettingsRef>(null);
18
+
19
+ const footer = (
20
+ <Flexbox horizontal justify="space-between" style={{ width: '100%' }}>
21
+ <Button
22
+ onClick={() => {
23
+ settingsRef.current?.reset();
24
+ }}
25
+ >
26
+ {t('common:reset')}
27
+ </Button>
28
+ <Flexbox gap={8} horizontal>
29
+ <Button onClick={onClose}>{t('common:cancel')}</Button>
30
+ <Button
31
+ onClick={() => {
32
+ settingsRef.current?.save();
33
+ }}
34
+ type="primary"
35
+ >
36
+ {t('common:save')}
37
+ </Button>
38
+ </Flexbox>
39
+ </Flexbox>
40
+ );
41
+
42
+ return (
43
+ <Modal
44
+ destroyOnHidden
45
+ footer={footer}
46
+ onCancel={onClose}
47
+ open={open}
48
+ title={t('plugin:dev.title.skillSettings')}
49
+ width={600}
50
+ >
51
+ <Settings hideFooter identifier={identifier} ref={settingsRef} />
52
+ </Modal>
53
+ );
54
+ });
55
+
56
+ McpSettingsModal.displayName = 'McpSettingsModal';
57
+
58
+ export default McpSettingsModal;