@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
@@ -17,6 +17,7 @@ export const modifyRoutes = async (TEMP_DIR: string) => {
17
17
 
18
18
  // Auth & User routes
19
19
  'src/app/[variants]/(auth)',
20
+ 'src/app/[variants]/(mobile)',
20
21
  'src/app/[variants]/(main)/(mobile)/me',
21
22
  'src/app/[variants]/(main)/changelog',
22
23
  'src/app/[variants]/oauth',
@@ -45,13 +46,25 @@ export const modifyRoutes = async (TEMP_DIR: string) => {
45
46
  });
46
47
  }
47
48
 
48
- // 2. Modify desktopRouter.config.tsx
49
+ // 2. Delete root loading.tsx files(not needed in Electron SPA)
50
+ const loadingFiles = ['src/app/loading.tsx', 'src/app/[variants]/loading.tsx'];
51
+ console.log(` Removing ${loadingFiles.length} root loading.tsx files...`);
52
+ for (const file of loadingFiles) {
53
+ const fullPath = path.join(TEMP_DIR, file);
54
+ await removePathEnsuring({
55
+ name: `modifyRoutes:delete:loading:${file}`,
56
+ path: fullPath,
57
+ });
58
+ }
59
+
60
+ // 3. Modify desktopRouter.config.tsx
49
61
  const routerConfigPath = path.join(
50
62
  TEMP_DIR,
51
63
  'src/app/[variants]/router/desktopRouter.config.tsx',
52
64
  );
53
65
  console.log(' Processing src/app/[variants]/router/desktopRouter.config.tsx...');
54
66
  await updateFile({
67
+ assertAfter: (code) => !/\bchangelog\b/.test(code),
55
68
  filePath: routerConfigPath,
56
69
  name: 'modifyRoutes:desktopRouterConfig',
57
70
  transformer: (code) => {
@@ -86,7 +99,6 @@ export const modifyRoutes = async (TEMP_DIR: string) => {
86
99
 
87
100
  return root.text();
88
101
  },
89
- assertAfter: (code) => !/\bchangelog\b/.test(code),
90
102
  });
91
103
  };
92
104
 
@@ -0,0 +1,148 @@
1
+ /* eslint-disable no-undef */
2
+ import { Lang, parse } from '@ast-grep/napi';
3
+ import path from 'node:path';
4
+
5
+ import { invariant, isDirectRun, runStandalone, updateFile } from './utils.mjs';
6
+
7
+ interface DynamicImportInfo {
8
+ componentName: string;
9
+ end: number;
10
+ importPath: string;
11
+ key: string;
12
+ start: number;
13
+ }
14
+
15
+ const isBusinessFeaturesEnabled = () => {
16
+ const raw = process.env.ENABLE_BUSINESS_FEATURES;
17
+ if (!raw) return false;
18
+ const normalized = raw.trim().toLowerCase();
19
+ return normalized === 'true' || normalized === '1';
20
+ };
21
+
22
+ const extractDynamicImportsFromMap = (code: string): DynamicImportInfo[] => {
23
+ const results: DynamicImportInfo[] = [];
24
+
25
+ const regex = /\[SettingsTabs\.(\w+)]:\s*dynamic\(\s*\(\)\s*=>\s*import\(\s*["']([^"']+)["']\s*\)/g;
26
+
27
+ let match;
28
+ while ((match = regex.exec(code)) !== null) {
29
+ const key = match[1];
30
+ const importPath = match[2];
31
+
32
+ const componentName = key.charAt(0).toUpperCase() + key.slice(1) + 'Tab';
33
+
34
+ results.push({
35
+ componentName,
36
+ end: 0,
37
+ importPath,
38
+ key,
39
+ start: 0,
40
+ });
41
+ }
42
+
43
+ return results;
44
+ };
45
+
46
+ const generateStaticImports = (imports: DynamicImportInfo[], keepBusinessTabs: boolean): string => {
47
+ return imports
48
+ .filter((imp) => keepBusinessTabs || !imp.importPath.includes('@/business/'))
49
+ .map((imp) => `import ${imp.componentName} from '${imp.importPath}';`)
50
+ .join('\n');
51
+ };
52
+
53
+ const generateStaticComponentMap = (
54
+ imports: DynamicImportInfo[],
55
+ keepBusinessTabs: boolean,
56
+ ): string => {
57
+ const entries = imports
58
+ .filter((imp) => keepBusinessTabs || !imp.importPath.includes('@/business/'))
59
+ .map((imp) => ` [SettingsTabs.${imp.key}]: ${imp.componentName},`);
60
+
61
+ return `const componentMap: Record<string, React.ComponentType<{ mobile?: boolean }>> = {\n${entries.join('\n')}\n}`;
62
+ };
63
+
64
+ export const convertSettingsContentToStatic = async (TEMP_DIR: string) => {
65
+ const filePath = path.join(
66
+ TEMP_DIR,
67
+ 'src/app/[variants]/(main)/settings/features/SettingsContent.tsx',
68
+ );
69
+
70
+ console.log(' Processing SettingsContent.tsx dynamic imports...');
71
+
72
+ await updateFile({
73
+ assertAfter: (code) => {
74
+ const noDynamic = !/dynamic\(\s*\(\)\s*=>\s*import/.test(code);
75
+ const hasStaticMap = /componentMap:\s*Record<string,/.test(code);
76
+ return noDynamic && hasStaticMap;
77
+ },
78
+ filePath,
79
+ name: 'convertSettingsContentToStatic',
80
+ transformer: (code) => {
81
+ const keepBusinessTabs = isBusinessFeaturesEnabled();
82
+ if (keepBusinessTabs) {
83
+ console.log(
84
+ ' ENABLE_BUSINESS_FEATURES is enabled, preserving business Settings tabs in componentMap',
85
+ );
86
+ }
87
+
88
+ const imports = extractDynamicImportsFromMap(code);
89
+
90
+ invariant(
91
+ imports.length > 0,
92
+ '[convertSettingsContentToStatic] No dynamic imports found in SettingsContent.tsx',
93
+ );
94
+
95
+ console.log(` Found ${imports.length} dynamic imports in componentMap`);
96
+
97
+ const staticImports = generateStaticImports(imports, keepBusinessTabs);
98
+ const staticComponentMap = generateStaticComponentMap(imports, keepBusinessTabs);
99
+
100
+ let result = code;
101
+
102
+ result = result.replace(
103
+ /import dynamic from ["']@\/libs\/next\/dynamic["'];\n?/,
104
+ '',
105
+ );
106
+
107
+ result = result.replace(
108
+ /import Loading from ["']@\/components\/Loading\/BrandTextLoading["'];\n?/,
109
+ '',
110
+ );
111
+
112
+ const ast = parse(Lang.Tsx, result);
113
+ const root = ast.root();
114
+
115
+ const lastImport = root.findAll({
116
+ rule: {
117
+ kind: 'import_statement',
118
+ },
119
+ }).at(-1);
120
+
121
+ invariant(
122
+ lastImport,
123
+ '[convertSettingsContentToStatic] No import statements found in SettingsContent.tsx',
124
+ );
125
+
126
+ const insertPos = lastImport!.range().end.index;
127
+ result = result.slice(0, insertPos) + '\nimport type React from \'react\';\n' + staticImports + result.slice(insertPos);
128
+
129
+ const componentMapRegex = /const componentMap = {[\S\s]*?\n};/;
130
+ invariant(
131
+ componentMapRegex.test(result),
132
+ '[convertSettingsContentToStatic] componentMap declaration not found in SettingsContent.tsx',
133
+ );
134
+
135
+ result = result.replace(componentMapRegex, staticComponentMap + ';');
136
+
137
+ result = result.replaceAll(/\n{3,}/g, '\n\n');
138
+
139
+ return result;
140
+ },
141
+ });
142
+ };
143
+
144
+ if (isDirectRun(import.meta.url)) {
145
+ await runStandalone('convertSettingsContentToStatic', convertSettingsContentToStatic, [
146
+ { lang: Lang.Tsx, path: 'src/app/[variants]/(main)/settings/features/SettingsContent.tsx' },
147
+ ]);
148
+ }
@@ -0,0 +1,73 @@
1
+ import { Lang, parse } from '@ast-grep/napi';
2
+ import path from 'node:path';
3
+
4
+ import { invariant, isDirectRun, runStandalone, updateFile } from './utils.mjs';
5
+
6
+ export const wrapChildrenWithClientOnly = async (TEMP_DIR: string) => {
7
+ const layoutPath = path.join(TEMP_DIR, 'src/app/[variants]/layout.tsx');
8
+
9
+ console.log(' Wrapping children with ClientOnly in layout.tsx...');
10
+
11
+ await updateFile({
12
+ assertAfter: (code) => {
13
+ const hasClientOnlyImport = /import ClientOnly from ["']@\/components\/client\/ClientOnly["']/.test(code);
14
+ const hasLoadingImport = /import Loading from ["']@\/components\/Loading\/BrandTextLoading["']/.test(code);
15
+ const hasClientOnlyWrapper = /<ClientOnly fallback={<Loading/.test(code);
16
+ return hasClientOnlyImport && hasLoadingImport && hasClientOnlyWrapper;
17
+ },
18
+ filePath: layoutPath,
19
+ name: 'wrapChildrenWithClientOnly',
20
+ transformer: (code) => {
21
+ const ast = parse(Lang.Tsx, code);
22
+ const root = ast.root();
23
+
24
+ let result = code;
25
+
26
+ const hasClientOnlyImport = /import ClientOnly from ["']@\/components\/client\/ClientOnly["']/.test(code);
27
+ const hasLoadingImport = /import Loading from ["']@\/components\/Loading\/BrandTextLoading["']/.test(code);
28
+
29
+ const lastImport = root.findAll({
30
+ rule: {
31
+ kind: 'import_statement',
32
+ },
33
+ }).at(-1);
34
+
35
+ invariant(lastImport, '[wrapChildrenWithClientOnly] No import statements found in layout.tsx');
36
+
37
+ const insertPos = lastImport!.range().end.index;
38
+ let importsToAdd = '';
39
+
40
+ if (!hasClientOnlyImport) {
41
+ importsToAdd += "\nimport ClientOnly from '@/components/client/ClientOnly';";
42
+ }
43
+ if (!hasLoadingImport) {
44
+ importsToAdd += "\nimport Loading from '@/components/Loading/BrandTextLoading';";
45
+ }
46
+
47
+ if (importsToAdd) {
48
+ result = result.slice(0, insertPos) + importsToAdd + result.slice(insertPos);
49
+ }
50
+
51
+ const authProviderPattern = /<AuthProvider>\s*{children}\s*<\/AuthProvider>/;
52
+ invariant(
53
+ authProviderPattern.test(result),
54
+ '[wrapChildrenWithClientOnly] Pattern <AuthProvider>{children}</AuthProvider> not found in layout.tsx',
55
+ );
56
+
57
+ result = result.replace(
58
+ authProviderPattern,
59
+ `<AuthProvider>
60
+ <ClientOnly fallback={<Loading />}>{children}</ClientOnly>
61
+ </AuthProvider>`,
62
+ );
63
+
64
+ return result;
65
+ },
66
+ });
67
+ };
68
+
69
+ if (isDirectRun(import.meta.url)) {
70
+ await runStandalone('wrapChildrenWithClientOnly', wrapChildrenWithClientOnly, [
71
+ { lang: Lang.Tsx, path: 'src/app/[variants]/layout.tsx' },
72
+ ]);
73
+ }
@@ -61,24 +61,24 @@ export const TIMEZONE_OPTIONS = [
61
61
 
62
62
  // Weekday options for checkbox group
63
63
  export const WEEKDAY_OPTIONS = [
64
- { label: 'Mon', value: 1 },
65
- { label: 'Tue', value: 2 },
66
- { label: 'Wed', value: 3 },
67
- { label: 'Thu', value: 4 },
68
- { label: 'Fri', value: 5 },
69
- { label: 'Sat', value: 6 },
70
- { label: 'Sun', value: 0 },
71
- ];
64
+ { label: 'agentCronJobs.weekday.short.monday', value: 1 },
65
+ { label: 'agentCronJobs.weekday.short.tuesday', value: 2 },
66
+ { label: 'agentCronJobs.weekday.short.wednesday', value: 3 },
67
+ { label: 'agentCronJobs.weekday.short.thursday', value: 4 },
68
+ { label: 'agentCronJobs.weekday.short.friday', value: 5 },
69
+ { label: 'agentCronJobs.weekday.short.saturday', value: 6 },
70
+ { label: 'agentCronJobs.weekday.short.sunday', value: 0 },
71
+ ] as const;
72
72
 
73
- // Weekday labels for display
73
+ // Weekday labels for display (i18n keys)
74
74
  export const WEEKDAY_LABELS: Record<number, string> = {
75
- 0: 'Sunday',
76
- 1: 'Monday',
77
- 2: 'Tuesday',
78
- 3: 'Wednesday',
79
- 4: 'Thursday',
80
- 5: 'Friday',
81
- 6: 'Saturday',
75
+ 0: 'agentCronJobs.weekday.sunday',
76
+ 1: 'agentCronJobs.weekday.monday',
77
+ 2: 'agentCronJobs.weekday.tuesday',
78
+ 3: 'agentCronJobs.weekday.wednesday',
79
+ 4: 'agentCronJobs.weekday.thursday',
80
+ 5: 'agentCronJobs.weekday.friday',
81
+ 6: 'agentCronJobs.weekday.saturday',
82
82
  };
83
83
 
84
84
  /**
@@ -22,7 +22,7 @@ const CronJobSaveButton = memo<CronJobSaveButtonProps>(({ disabled, loading, onS
22
22
  style={{ width: 200 }}
23
23
  type="primary"
24
24
  >
25
- {t('agentCronJobs.saveAsNew', { defaultValue: 'Save as New Scheduled Task' })}
25
+ {t('agentCronJobs.saveAsNew')}
26
26
  </Button>
27
27
  </Flexbox>
28
28
  );
@@ -81,7 +81,7 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
81
81
  if (scheduleType === 'weekly' && weekdays.length > 0) {
82
82
  result.push({
83
83
  key: 'weekdays',
84
- label: weekdays.map((day) => WEEKDAY_LABELS[day]).join(', '),
84
+ label: weekdays.map((day) => t(WEEKDAY_LABELS[day] as any)).join(', '),
85
85
  });
86
86
  }
87
87
 
@@ -126,7 +126,10 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
126
126
  maxTagCount="responsive"
127
127
  mode="multiple"
128
128
  onChange={(values: number[]) => onScheduleChange({ weekdays: values })}
129
- options={WEEKDAY_OPTIONS}
129
+ options={WEEKDAY_OPTIONS.map((opt) => ({
130
+ label: t(opt.label as any),
131
+ value: opt.value,
132
+ }))}
130
133
  placeholder="Select days"
131
134
  size="small"
132
135
  style={{ minWidth: 150 }}
@@ -33,7 +33,7 @@ const Search = memo<StoreSearchBarProps>(() => {
33
33
  const { q } = useQuery() as { q?: string };
34
34
  const router = useQueryRoute();
35
35
  const [word, setWord] = useState<string>(q || '');
36
- const activeTab = pathname.split('/')[2] || 'assistant';
36
+ const activeTab = pathname.split('/')[2] || 'agent';
37
37
  const handleSearch = (value: string) => {
38
38
  router.push(urlJoin('/community', activeTab), {
39
39
  query: value ? { q: value } : {},
@@ -0,0 +1,131 @@
1
+ 'use client';
2
+
3
+ import { getKlavisServerByServerIdentifier, getLobehubSkillProviderById } from '@lobechat/const';
4
+ import { Avatar, Flexbox, Icon } from '@lobehub/ui';
5
+ import { createStyles } from 'antd-style';
6
+ import { Blocks } from 'lucide-react';
7
+ import { type ReactNode, createElement, memo, useMemo, useState } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+
10
+ import SkillStore from '@/features/SkillStore';
11
+ import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
12
+ import { useToolStore } from '@/store/tool';
13
+
14
+ const useStyles = createStyles(({ css, token }) => ({
15
+ banner: css`
16
+ cursor: pointer;
17
+
18
+ position: absolute;
19
+ z-index: 0;
20
+ inset-block-end: 0;
21
+ inset-inline: 0 0;
22
+
23
+ display: flex;
24
+ gap: 12px;
25
+ align-items: center;
26
+ justify-content: space-between;
27
+
28
+ margin-block-end: 6px;
29
+ padding-block: 42px 10px;
30
+ padding-inline: 16px;
31
+ border: 1px solid ${token.colorBorderSecondary};
32
+ border-radius: 20px;
33
+
34
+ background: ${token.colorFillQuaternary};
35
+ box-shadow: 0 12px 32px rgb(0 0 0 / 4%);
36
+
37
+ transition: background 0.2s ease-in-out;
38
+
39
+ &:hover {
40
+ background: ${token.colorFillQuaternary};
41
+ }
42
+ `,
43
+ icon: css`
44
+ color: ${token.colorTextSecondary};
45
+ `,
46
+ text: css`
47
+ font-size: 13px;
48
+ color: ${token.colorTextSecondary};
49
+ `,
50
+ }));
51
+
52
+ const BANNER_SKILL_IDS = [
53
+ { id: 'gmail', type: 'klavis' },
54
+ { id: 'notion', type: 'klavis' },
55
+ { id: 'google-drive', type: 'klavis' },
56
+ { id: 'google-calendar', type: 'klavis' },
57
+ { id: 'slack', type: 'klavis' },
58
+ { id: 'twitter', type: 'lobehub' },
59
+ { id: 'github', type: 'klavis' },
60
+ ] as const;
61
+
62
+ const SkillInstallBanner = memo(() => {
63
+ const { styles } = useStyles();
64
+ const { t } = useTranslation('plugin');
65
+ const [open, setOpen] = useState(false);
66
+
67
+ const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
68
+ const isKlavisEnabled = useServerConfigStore(serverConfigSelectors.enableKlavis);
69
+
70
+ // Prefetch skill connections data so SkillStore opens faster
71
+ const [useFetchLobehubSkillConnections, useFetchUserKlavisServers] = useToolStore((s) => [
72
+ s.useFetchLobehubSkillConnections,
73
+ s.useFetchUserKlavisServers,
74
+ ]);
75
+ useFetchLobehubSkillConnections(isLobehubSkillEnabled);
76
+ useFetchUserKlavisServers(isKlavisEnabled);
77
+
78
+ const avatarItems = useMemo(() => {
79
+ const items: Array<{ avatar: ReactNode; key: string; title: string }> = [];
80
+
81
+ for (const skill of BANNER_SKILL_IDS) {
82
+ if (skill.type === 'lobehub') {
83
+ const provider = getLobehubSkillProviderById(skill.id);
84
+ if (provider) {
85
+ items.push({
86
+ avatar:
87
+ typeof provider.icon === 'string'
88
+ ? provider.icon
89
+ : createElement(provider.icon, { size: 14 }),
90
+ key: provider.id,
91
+ title: provider.label,
92
+ });
93
+ }
94
+ } else {
95
+ const server = getKlavisServerByServerIdentifier(skill.id);
96
+ if (server) {
97
+ items.push({
98
+ avatar:
99
+ typeof server.icon === 'string'
100
+ ? server.icon
101
+ : createElement(server.icon, { size: 14 }),
102
+ key: server.identifier,
103
+ title: server.label,
104
+ });
105
+ }
106
+ }
107
+ }
108
+
109
+ return items;
110
+ }, []);
111
+
112
+ // Don't show banner if no skills are enabled
113
+ if (!isLobehubSkillEnabled && !isKlavisEnabled) return null;
114
+
115
+ return (
116
+ <>
117
+ <div className={styles.banner} onClick={() => setOpen(true)}>
118
+ <Flexbox align="center" gap={8} horizontal>
119
+ <Icon className={styles.icon} icon={Blocks} size={18} />
120
+ <span className={styles.text}>{t('skillInstallBanner.title')}</span>
121
+ </Flexbox>
122
+ {avatarItems.length > 0 && <Avatar.Group items={avatarItems} shape="circle" size={24} />}
123
+ </div>
124
+ <SkillStore open={open} setOpen={setOpen} />
125
+ </>
126
+ );
127
+ });
128
+
129
+ SkillInstallBanner.displayName = 'SkillInstallBanner';
130
+
131
+ export default SkillInstallBanner;
@@ -9,6 +9,7 @@ import { useChatStore } from '@/store/chat';
9
9
  import { useHomeStore } from '@/store/home';
10
10
 
11
11
  import ModeHeader from './ModeHeader';
12
+ import SkillInstallBanner from './SkillInstallBanner';
12
13
  import StarterList from './StarterList';
13
14
  import { useSend } from './useSend';
14
15
 
@@ -36,38 +37,44 @@ const InputArea = () => {
36
37
  boxShadow: '0 12px 32px rgba(0,0,0,.04)',
37
38
  },
38
39
  }),
39
- [inputActiveMode],
40
+ [],
40
41
  );
41
42
 
42
43
  return (
43
44
  <Flexbox gap={16} style={{ marginBottom: 16 }}>
44
- <DragUploadZone onUploadFiles={handleUploadFiles}>
45
- <ChatInputProvider
46
- agentId={inboxAgentId}
47
- allowExpand={false}
48
- chatInputEditorRef={(instance) => {
49
- if (!instance) return;
50
- useChatStore.setState({ mainInputEditor: instance });
51
- }}
52
- leftActions={leftActions}
53
- onMarkdownContentChange={(content) => {
54
- useChatStore.setState({ inputMessage: content });
55
- }}
56
- onSend={send}
57
- sendButtonProps={{
58
- disabled: loading,
59
- generating: loading,
60
- onStop: () => {},
61
- shape: 'round',
62
- }}
45
+ <Flexbox style={{ paddingBottom: 32, position: 'relative' }}>
46
+ <SkillInstallBanner />
47
+ <DragUploadZone
48
+ onUploadFiles={handleUploadFiles}
49
+ style={{ position: 'relative', zIndex: 1 }}
63
50
  >
64
- <DesktopChatInput
65
- dropdownPlacement="bottomLeft"
66
- extenHeaderContent={inputActiveMode ? <ModeHeader /> : undefined}
67
- inputContainerProps={inputContainerProps}
68
- />
69
- </ChatInputProvider>
70
- </DragUploadZone>
51
+ <ChatInputProvider
52
+ agentId={inboxAgentId}
53
+ allowExpand={false}
54
+ chatInputEditorRef={(instance) => {
55
+ if (!instance) return;
56
+ useChatStore.setState({ mainInputEditor: instance });
57
+ }}
58
+ leftActions={leftActions}
59
+ onMarkdownContentChange={(content) => {
60
+ useChatStore.setState({ inputMessage: content });
61
+ }}
62
+ onSend={send}
63
+ sendButtonProps={{
64
+ disabled: loading,
65
+ generating: loading,
66
+ onStop: () => {},
67
+ shape: 'round',
68
+ }}
69
+ >
70
+ <DesktopChatInput
71
+ dropdownPlacement="bottomLeft"
72
+ extenHeaderContent={inputActiveMode ? <ModeHeader /> : undefined}
73
+ inputContainerProps={inputContainerProps}
74
+ />
75
+ </ChatInputProvider>
76
+ </DragUploadZone>
77
+ </Flexbox>
71
78
 
72
79
  <StarterList />
73
80
  </Flexbox>
@@ -3,15 +3,19 @@ import { Divider } from 'antd';
3
3
  import { FC, ReactNode } from 'react';
4
4
 
5
5
  interface SettingHeaderProps {
6
+ extra?: ReactNode;
6
7
  title: ReactNode;
7
8
  }
8
9
 
9
- const SettingHeader: FC<SettingHeaderProps> = ({ title }) => {
10
+ const SettingHeader: FC<SettingHeaderProps> = ({ title, extra }) => {
10
11
  return (
11
12
  <Flexbox gap={24} style={{ paddingTop: 12 }}>
12
- <Text fontSize={24} strong>
13
- {title}
14
- </Text>
13
+ <Flexbox align={'center'} horizontal justify={'space-between'}>
14
+ <Text fontSize={24} strong>
15
+ {title}
16
+ </Text>
17
+ {extra}
18
+ </Flexbox>
15
19
  <Divider style={{ margin: 0 }} />
16
20
  </Flexbox>
17
21
  );
@@ -56,6 +56,9 @@ const componentMap = {
56
56
  [SettingsTabs.Security]: dynamic(() => import('../security'), {
57
57
  loading: () => <Loading debugId="Settings > Security" />,
58
58
  }),
59
+ [SettingsTabs.Skill]: dynamic(() => import('../skill'), {
60
+ loading: () => <Loading debugId="Settings > Skill" />,
61
+ }),
59
62
  ...(ENABLE_BUSINESS_FEATURES
60
63
  ? ({
61
64
  [SettingsTabs.Plans]: dynamic(
@@ -2,6 +2,7 @@ import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
2
2
  import { isDesktop } from '@lobechat/const';
3
3
  import { Avatar } from '@lobehub/ui';
4
4
  import {
5
+ Blocks,
5
6
  Brain,
6
7
  BrainCircuit,
7
8
  ChartColumnBigIcon,
@@ -182,6 +183,11 @@ export const useCategory = () => {
182
183
  key: SettingsTabs.Agent,
183
184
  label: t('tab.agent'),
184
185
  },
186
+ {
187
+ icon: Blocks,
188
+ key: SettingsTabs.Skill,
189
+ label: t('tab.skill'),
190
+ },
185
191
  {
186
192
  icon: BrainCircuit,
187
193
  key: SettingsTabs.Memory,