@lobehub/lobehub 2.0.0-next.343 → 2.0.0-next.345

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 (169) hide show
  1. package/.cursor/rules/i18n.mdc +1 -1
  2. package/.cursor/rules/modal-imperative.mdc +162 -0
  3. package/.cursor/rules/rules-index.mdc +1 -0
  4. package/.env.example +0 -14
  5. package/.eslintrc.js +8 -1
  6. package/CHANGELOG.md +66 -0
  7. package/Dockerfile +3 -13
  8. package/README.md +3 -5
  9. package/README.zh-CN.md +3 -5
  10. package/changelog/v1.json +24 -0
  11. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx +11 -42
  12. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx +10 -41
  13. package/e2e/src/support/webServer.ts +2 -0
  14. package/locales/ar/error.json +0 -4
  15. package/locales/bg-BG/error.json +0 -4
  16. package/locales/de-DE/error.json +0 -4
  17. package/locales/en-US/error.json +0 -4
  18. package/locales/es-ES/error.json +0 -4
  19. package/locales/fa-IR/error.json +0 -4
  20. package/locales/fr-FR/error.json +0 -4
  21. package/locales/it-IT/error.json +0 -4
  22. package/locales/ja-JP/error.json +0 -4
  23. package/locales/ko-KR/error.json +0 -4
  24. package/locales/nl-NL/error.json +0 -4
  25. package/locales/pl-PL/error.json +0 -4
  26. package/locales/pt-BR/error.json +0 -4
  27. package/locales/ru-RU/error.json +0 -4
  28. package/locales/tr-TR/error.json +0 -4
  29. package/locales/vi-VN/error.json +0 -4
  30. package/locales/zh-CN/error.json +0 -4
  31. package/locales/zh-TW/error.json +0 -4
  32. package/package.json +7 -9
  33. package/packages/builtin-agents/package.json +2 -0
  34. package/packages/builtin-agents/src/agents/agent-builder/index.ts +4 -2
  35. package/packages/builtin-agents/src/agents/group-agent-builder/index.ts +4 -2
  36. package/packages/builtin-agents/src/agents/page-agent/index.ts +5 -2
  37. package/packages/builtin-tool-cloud-sandbox/src/ExecutionRuntime/index.ts +161 -12
  38. package/packages/context-engine/src/engine/messages/MessagesEngine.ts +9 -9
  39. package/packages/context-engine/src/providers/GroupContextInjector.ts +19 -33
  40. package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +79 -43
  41. package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +5 -15
  42. package/packages/database/src/repositories/userMemory/__tests__/UserMemoryTopicRepository.test.ts +24 -3
  43. package/packages/model-bank/src/modelProviders/comfyui.ts +0 -1
  44. package/packages/model-bank/src/modelProviders/fal.ts +0 -1
  45. package/packages/types/src/fetch.ts +1 -2
  46. package/packages/utils/src/server/__tests__/auth.test.ts +0 -47
  47. package/packages/utils/src/server/auth.ts +1 -9
  48. package/scripts/_shared/checkDeprecatedClerkEnv.js +42 -0
  49. package/scripts/changelogWorkflow/buildStaticChangelog.ts +2 -1
  50. package/scripts/clerk-to-betterauth/_internal/types.ts +53 -20
  51. package/scripts/clerk-to-betterauth/export-clerk-users-with-api.ts +43 -36
  52. package/scripts/countEnWord.ts +1 -1
  53. package/scripts/electronWorkflow/modifiers/appCode.mts +2 -131
  54. package/scripts/i18nWorkflow/protectedPatterns.ts +1 -2
  55. package/scripts/prebuild.mts +10 -8
  56. package/scripts/serverLauncher/startServer.js +23 -5
  57. package/src/app/(backend)/middleware/auth/index.test.ts +8 -4
  58. package/src/app/(backend)/middleware/auth/index.ts +0 -15
  59. package/src/app/(backend)/middleware/auth/utils.test.ts +0 -28
  60. package/src/app/(backend)/middleware/auth/utils.ts +2 -17
  61. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +3 -51
  62. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -4
  63. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +7 -6
  64. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -16
  65. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/index.tsx +1 -1
  66. package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +13 -13
  67. package/src/app/[variants]/(main)/home/features/RecentPage/Item.tsx +2 -2
  68. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
  69. package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -2
  70. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -22
  71. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +12 -14
  72. package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +8 -14
  73. package/src/app/[variants]/(main)/settings/skill/index.tsx +7 -5
  74. package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +2 -35
  75. package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -20
  76. package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -2
  77. package/src/app/[variants]/(mobile)/me/profile/features/Category.tsx +3 -13
  78. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +2 -3
  79. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
  80. package/src/app/[variants]/share/t/[id]/index.tsx +1 -1
  81. package/src/app/robots.tsx +1 -1
  82. package/src/envs/auth.ts +2 -27
  83. package/src/envs/llm.ts +2 -2
  84. package/src/features/AgentSetting/AgentPlugin/index.tsx +9 -12
  85. package/src/features/ChatInput/ActionBar/Tools/index.tsx +7 -5
  86. package/src/features/ChatMiniMap/utils.ts +1 -1
  87. package/src/features/CommandMenu/SearchResults.tsx +1 -1
  88. package/src/features/Conversation/ChatList/components/AutoScroll/DebugInspector.tsx +166 -0
  89. package/src/features/Conversation/ChatList/components/AutoScroll/index.tsx +86 -0
  90. package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +11 -17
  91. package/src/features/Conversation/Messages/AgentCouncil/components/AutoScrollShadow.tsx +25 -14
  92. package/src/features/Conversation/Messages/AgentCouncil/components/CouncilMember.tsx +1 -1
  93. package/src/features/IntegrationDetailModal/IntegrationDetailContent.tsx +305 -0
  94. package/src/features/IntegrationDetailModal/index.tsx +21 -283
  95. package/src/features/MCPPluginDetail/Deployment/index.tsx +1 -1
  96. package/src/features/MCPPluginDetail/Schema/Prompts.tsx +1 -1
  97. package/src/features/MCPPluginDetail/Schema/Tools.tsx +1 -1
  98. package/src/features/ProfileEditor/AgentTool.tsx +14 -20
  99. package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/NoteFileItem.tsx +1 -1
  100. package/src/features/SkillStore/LobeHubList/index.tsx +50 -87
  101. package/src/features/SkillStore/Search/index.tsx +1 -1
  102. package/src/features/SkillStore/{Content.tsx → SkillStoreContent.tsx} +3 -8
  103. package/src/features/SkillStore/index.tsx +15 -33
  104. package/src/features/User/UserPanel/PanelContent.tsx +0 -8
  105. package/src/features/User/__tests__/PanelContent.test.tsx +1 -35
  106. package/src/features/User/__tests__/UserAvatar.test.tsx +30 -57
  107. package/src/features/User/__tests__/useMenu.test.tsx +2 -43
  108. package/src/layout/AuthProvider/index.tsx +0 -5
  109. package/src/libs/next/config/define-config.ts +6 -0
  110. package/src/libs/next/proxy/createRouteMatcher.test.ts +121 -0
  111. package/src/libs/next/proxy/createRouteMatcher.ts +18 -0
  112. package/src/libs/next/proxy/define-config.ts +4 -53
  113. package/src/libs/next-auth/adapter/index.ts +1 -2
  114. package/src/libs/oidc-provider/provider.test.ts +5 -316
  115. package/src/libs/trpc/lambda/context.test.ts +0 -13
  116. package/src/libs/trpc/lambda/context.ts +3 -22
  117. package/src/libs/trpc/middleware/userAuth.ts +2 -4
  118. package/src/libs/trusted-client/getSessionUser.ts +2 -17
  119. package/src/locales/default/error.ts +0 -6
  120. package/src/locales/default/index.ts +0 -2
  121. package/src/proxy.ts +0 -1
  122. package/src/server/routers/lambda/__tests__/user.test.ts +0 -71
  123. package/src/server/routers/lambda/user.ts +6 -63
  124. package/src/server/services/changelog/index.test.ts +3 -2
  125. package/src/server/services/changelog/index.ts +1 -1
  126. package/src/server/services/user/index.ts +0 -83
  127. package/src/services/chat/index.ts +1 -2
  128. package/src/services/chat/mecha/agentConfigResolver.test.ts +43 -0
  129. package/src/services/chat/mecha/agentConfigResolver.ts +3 -1
  130. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +58 -14
  131. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +10 -2
  132. package/src/store/user/slices/auth/action.test.ts +1 -81
  133. package/src/store/user/slices/auth/action.ts +3 -28
  134. package/src/store/user/slices/auth/initialState.ts +1 -18
  135. package/src/store/user/slices/auth/selectors.test.ts +2 -127
  136. package/src/store/user/slices/auth/selectors.ts +1 -21
  137. package/src/utils/errorResponse.ts +1 -4
  138. package/src/utils/markdownToTxt.ts +20 -0
  139. package/locales/ar/clerk.json +0 -545
  140. package/locales/bg-BG/clerk.json +0 -545
  141. package/locales/de-DE/clerk.json +0 -545
  142. package/locales/en-US/clerk.json +0 -545
  143. package/locales/es-ES/clerk.json +0 -545
  144. package/locales/fa-IR/clerk.json +0 -545
  145. package/locales/fr-FR/clerk.json +0 -545
  146. package/locales/it-IT/clerk.json +0 -545
  147. package/locales/ja-JP/clerk.json +0 -545
  148. package/locales/ko-KR/clerk.json +0 -545
  149. package/locales/nl-NL/clerk.json +0 -545
  150. package/locales/pl-PL/clerk.json +0 -545
  151. package/locales/pt-BR/clerk.json +0 -545
  152. package/locales/ru-RU/clerk.json +0 -545
  153. package/locales/tr-TR/clerk.json +0 -545
  154. package/locales/vi-VN/clerk.json +0 -545
  155. package/locales/zh-CN/clerk.json +0 -545
  156. package/locales/zh-TW/clerk.json +0 -545
  157. package/src/app/(backend)/api/webhooks/clerk/__tests__/fixtures/createUser.json +0 -73
  158. package/src/app/(backend)/api/webhooks/clerk/route.ts +0 -95
  159. package/src/app/(backend)/api/webhooks/clerk/validateRequest.ts +0 -22
  160. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +0 -27
  161. package/src/app/[variants]/(main)/settings/security/features/ClerkProfile.tsx +0 -67
  162. package/src/features/Conversation/ChatList/components/AutoScroll.tsx +0 -25
  163. package/src/layout/AuthProvider/Clerk/UserUpdater.tsx +0 -40
  164. package/src/layout/AuthProvider/Clerk/index.tsx +0 -54
  165. package/src/layout/AuthProvider/Clerk/useAppearance.ts +0 -133
  166. package/src/libs/clerk-auth/index.test.ts +0 -216
  167. package/src/libs/clerk-auth/index.ts +0 -80
  168. package/src/locales/default/clerk.ts +0 -677
  169. package/src/server/services/user/index.test.ts +0 -220
@@ -0,0 +1,305 @@
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, Tag, Text, Typography, useModalContext } from '@lobehub/ui';
10
+ import { Button, Divider } from 'antd';
11
+ import { createStyles, cssVar } from 'antd-style';
12
+ import type { Klavis } from 'klavis';
13
+ import { ExternalLink, Loader2, SquareArrowOutUpRight } from 'lucide-react';
14
+ import { useEffect, useMemo, useRef } from 'react';
15
+ import { useTranslation } from 'react-i18next';
16
+
17
+ import { useSkillConnect } from '@/features/SkillStore/LobeHubList/useSkillConnect';
18
+ import { useToolStore } from '@/store/tool';
19
+ import { klavisStoreSelectors, lobehubSkillStoreSelectors } from '@/store/tool/selectors';
20
+ import { KlavisServerStatus } from '@/store/tool/slices/klavisStore';
21
+ import { LobehubSkillStatus } from '@/store/tool/slices/lobehubSkillStore/types';
22
+
23
+ const useStyles = createStyles(({ css, token }) => ({
24
+ authorLink: css`
25
+ cursor: pointer;
26
+
27
+ display: inline-flex;
28
+ gap: 4px;
29
+ align-items: center;
30
+
31
+ color: ${token.colorPrimary};
32
+
33
+ &:hover {
34
+ text-decoration: underline;
35
+ }
36
+ `,
37
+ detailItem: css`
38
+ display: flex;
39
+ flex-direction: column;
40
+ gap: 4px;
41
+ `,
42
+ detailLabel: css`
43
+ font-size: 12px;
44
+ color: ${token.colorTextTertiary};
45
+ `,
46
+ header: css`
47
+ display: flex;
48
+ gap: 16px;
49
+ align-items: center;
50
+
51
+ padding: 16px;
52
+ border-radius: 12px;
53
+
54
+ background: ${token.colorFillTertiary};
55
+ `,
56
+ icon: css`
57
+ display: flex;
58
+ flex-shrink: 0;
59
+ align-items: center;
60
+ justify-content: center;
61
+
62
+ width: 56px;
63
+ height: 56px;
64
+ border-radius: 12px;
65
+
66
+ background: ${token.colorBgContainer};
67
+ `,
68
+ introduction: css`
69
+ font-size: 14px;
70
+ line-height: 1.8;
71
+ color: ${token.colorText};
72
+ `,
73
+ sectionTitle: css`
74
+ font-size: 14px;
75
+ font-weight: 600;
76
+ color: ${token.colorText};
77
+ `,
78
+ title: css`
79
+ font-size: 18px;
80
+ font-weight: 600;
81
+ color: ${token.colorText};
82
+ `,
83
+ toolTag: css`
84
+ font-family: ${token.fontFamilyCode};
85
+ font-size: 12px;
86
+ `,
87
+ toolsContainer: css`
88
+ display: flex;
89
+ flex-wrap: wrap;
90
+ gap: 8px;
91
+ `,
92
+ trustWarning: css`
93
+ font-size: 12px;
94
+ line-height: 1.6;
95
+ color: ${token.colorTextTertiary};
96
+ `,
97
+ }));
98
+
99
+ export type IntegrationType = 'klavis' | 'lobehub';
100
+
101
+ export interface IntegrationDetailContentProps {
102
+ identifier: string;
103
+ serverName?: Klavis.McpServerName;
104
+ type: IntegrationType;
105
+ }
106
+
107
+ export const IntegrationDetailContent = ({
108
+ type,
109
+ identifier,
110
+ serverName,
111
+ }: IntegrationDetailContentProps) => {
112
+ const { styles } = useStyles();
113
+ const { t } = useTranslation(['plugin', 'setting']);
114
+ const { close } = useModalContext();
115
+
116
+ const {
117
+ handleConnect,
118
+ isConnecting,
119
+ isConnected: hookIsConnected,
120
+ } = useSkillConnect({
121
+ identifier,
122
+ serverName,
123
+ type,
124
+ });
125
+
126
+ const hasTriggeredConnectRef = useRef(false);
127
+
128
+ useEffect(() => {
129
+ if (hasTriggeredConnectRef.current && hookIsConnected) {
130
+ close();
131
+ }
132
+ }, [hookIsConnected, close]);
133
+
134
+ const handleConnectWithTracking = async () => {
135
+ hasTriggeredConnectRef.current = true;
136
+ await handleConnect();
137
+ };
138
+
139
+ const config = useMemo((): KlavisServerType | LobehubSkillProviderType | undefined => {
140
+ if (type === 'klavis') {
141
+ return getKlavisServerByServerIdentifier(identifier);
142
+ }
143
+ return getLobehubSkillProviderById(identifier);
144
+ }, [type, identifier]);
145
+
146
+ const klavisServers = useToolStore(klavisStoreSelectors.getServers);
147
+ const lobehubSkillServers = useToolStore(lobehubSkillStoreSelectors.getServers);
148
+
149
+ const serverState = useMemo(() => {
150
+ if (type === 'klavis') {
151
+ return klavisServers.find((s) => s.identifier === identifier);
152
+ }
153
+ return lobehubSkillServers.find((s) => s.identifier === identifier);
154
+ }, [type, identifier, klavisServers, lobehubSkillServers]);
155
+
156
+ const isConnected = useMemo(() => {
157
+ if (!serverState) return false;
158
+ if (type === 'klavis') {
159
+ return serverState.status === KlavisServerStatus.CONNECTED;
160
+ }
161
+ return serverState.status === LobehubSkillStatus.CONNECTED;
162
+ }, [type, serverState]);
163
+
164
+ const tools = useMemo(() => {
165
+ return serverState?.tools?.map((tool) => tool.name) || [];
166
+ }, [serverState]);
167
+
168
+ if (!config) return null;
169
+
170
+ const { author, authorUrl, description, icon, introduction, label } = config;
171
+
172
+ const i18nIdentifier =
173
+ type === 'klavis'
174
+ ? (config as KlavisServerType).identifier
175
+ : (config as LobehubSkillProviderType).id;
176
+ const i18nPrefix = type === 'klavis' ? 'tools.klavis.servers' : 'tools.lobehubSkill.providers';
177
+
178
+ const localizedDescription = t(`${i18nPrefix}.${i18nIdentifier}.description`, {
179
+ defaultValue: description,
180
+ ns: 'setting',
181
+ });
182
+ const localizedIntroduction = t(`${i18nPrefix}.${i18nIdentifier}.introduction`, {
183
+ defaultValue: introduction,
184
+ ns: 'setting',
185
+ });
186
+
187
+ const renderIcon = () => {
188
+ if (typeof icon === 'string') {
189
+ return <Image alt={label} height={36} src={icon} width={36} />;
190
+ }
191
+ return <Icon fill={cssVar.colorText} icon={icon} size={36} />;
192
+ };
193
+
194
+ const handleAuthorClick = () => {
195
+ if (authorUrl) {
196
+ window.open(authorUrl, '_blank', 'noopener,noreferrer');
197
+ }
198
+ };
199
+
200
+ const renderConnectButton = () => {
201
+ if (isConnected) return null;
202
+
203
+ if (isConnecting) {
204
+ return (
205
+ <Button disabled icon={<Icon icon={Loader2} spin />} type="default">
206
+ {t('tools.klavis.connect', { defaultValue: 'Connect', ns: 'setting' })}
207
+ </Button>
208
+ );
209
+ }
210
+
211
+ return (
212
+ <Button
213
+ icon={<Icon icon={SquareArrowOutUpRight} />}
214
+ onClick={handleConnectWithTracking}
215
+ type="primary"
216
+ >
217
+ {t('tools.klavis.connect', { defaultValue: 'Connect', ns: 'setting' })}
218
+ </Button>
219
+ );
220
+ };
221
+
222
+ return (
223
+ <Flexbox gap={20}>
224
+ {/* Header */}
225
+ <Flexbox
226
+ align="center"
227
+ className={styles.header}
228
+ horizontal
229
+ justify="space-between"
230
+ style={{ flexWrap: 'nowrap' }}
231
+ >
232
+ <Flexbox align="center" gap={16} horizontal>
233
+ <div className={styles.icon}>{renderIcon()}</div>
234
+ <Flexbox gap={4}>
235
+ <span className={styles.title}>{label}</span>
236
+ <Text style={{ fontSize: 14 }} type="secondary">
237
+ {localizedDescription}
238
+ </Text>
239
+ </Flexbox>
240
+ </Flexbox>
241
+ {renderConnectButton()}
242
+ </Flexbox>
243
+
244
+ {/* Introduction */}
245
+ <Typography className={styles.introduction}>{localizedIntroduction}</Typography>
246
+
247
+ {/* Developed by */}
248
+ <Flexbox gap={8}>
249
+ <Flexbox align="center" gap={4} horizontal>
250
+ <span className={styles.sectionTitle}>{t('integrationDetail.developedBy')}</span>
251
+ <span
252
+ className={styles.authorLink}
253
+ onClick={handleAuthorClick}
254
+ style={{ cursor: authorUrl ? 'pointer' : 'default' }}
255
+ >
256
+ {author}
257
+ {authorUrl && <Icon icon={ExternalLink} size={12} />}
258
+ </span>
259
+ </Flexbox>
260
+ <Text className={styles.trustWarning} type="secondary">
261
+ {t('integrationDetail.trustWarning')}
262
+ </Text>
263
+ </Flexbox>
264
+
265
+ {/* Tools */}
266
+ {tools.length > 0 && (
267
+ <>
268
+ <Divider style={{ margin: 0 }} />
269
+ <Flexbox gap={12}>
270
+ <Flexbox align="center" gap={8} horizontal>
271
+ <span className={styles.sectionTitle}>{t('integrationDetail.tools')}</span>
272
+ <Tag>{tools.length}</Tag>
273
+ </Flexbox>
274
+ <div className={styles.toolsContainer}>
275
+ {tools.map((tool) => (
276
+ <Tag className={styles.toolTag} key={tool}>
277
+ {tool}
278
+ </Tag>
279
+ ))}
280
+ </div>
281
+ </Flexbox>
282
+ </>
283
+ )}
284
+
285
+ {/* Details */}
286
+ <Divider style={{ margin: 0 }} />
287
+ <Flexbox gap={12}>
288
+ <span className={styles.sectionTitle}>{t('integrationDetail.details')}</span>
289
+ <Flexbox gap={16} horizontal>
290
+ <div className={styles.detailItem}>
291
+ <span className={styles.detailLabel}>{t('integrationDetail.author')}</span>
292
+ <span
293
+ className={styles.authorLink}
294
+ onClick={handleAuthorClick}
295
+ style={{ cursor: authorUrl ? 'pointer' : 'default' }}
296
+ >
297
+ {author}
298
+ {authorUrl && <Icon icon={ExternalLink} size={12} />}
299
+ </span>
300
+ </div>
301
+ </Flexbox>
302
+ </Flexbox>
303
+ </Flexbox>
304
+ );
305
+ };
@@ -1,292 +1,30 @@
1
1
  'use client';
2
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 { createStaticStyles, cssVar } from 'antd-style';
12
- import { ExternalLink, Loader2, SquareArrowOutUpRight } from 'lucide-react';
13
- import { memo, useMemo } from 'react';
14
- import { useTranslation } from 'react-i18next';
3
+ import { createModal } from '@lobehub/ui';
4
+ import { t } from 'i18next';
5
+ import type { Klavis } from 'klavis';
15
6
 
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';
7
+ import { IntegrationDetailContent, type IntegrationType } from './IntegrationDetailContent';
20
8
 
21
- const styles = createStaticStyles(({ css, cssVar }) => ({
22
- authorLink: css`
23
- cursor: pointer;
9
+ export type { IntegrationType } from './IntegrationDetailContent';
24
10
 
25
- display: inline-flex;
26
- gap: 4px;
27
- align-items: center;
28
-
29
- color: ${cssVar.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: ${cssVar.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: ${cssVar.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: ${cssVar.colorBgContainer};
65
- `,
66
- introduction: css`
67
- font-size: 14px;
68
- line-height: 1.8;
69
- color: ${cssVar.colorText};
70
- `,
71
- sectionTitle: css`
72
- font-size: 14px;
73
- font-weight: 600;
74
- color: ${cssVar.colorText};
75
- `,
76
- title: css`
77
- font-size: 18px;
78
- font-weight: 600;
79
- color: ${cssVar.colorText};
80
- `,
81
- toolTag: css`
82
- font-family: ${cssVar.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: ${cssVar.colorTextTertiary};
94
- `,
95
- }));
96
-
97
- export type IntegrationType = 'klavis' | 'lobehub';
98
-
99
- export interface IntegrationDetailModalProps {
11
+ export interface CreateIntegrationDetailModalOptions {
100
12
  identifier: string;
101
- isConnecting?: boolean;
102
- onClose: () => void;
103
- onConnect?: () => void;
104
- open: boolean;
13
+ serverName?: Klavis.McpServerName;
105
14
  type: IntegrationType;
106
15
  }
107
16
 
108
- const IntegrationDetailModal = memo<IntegrationDetailModalProps>(
109
- ({ open, onClose, type, identifier, isConnecting, onConnect }) => {
110
- const { t } = useTranslation(['plugin', 'setting']);
111
-
112
- // Get static config based on type
113
- const config = useMemo((): KlavisServerType | LobehubSkillProviderType | undefined => {
114
- if (type === 'klavis') {
115
- return getKlavisServerByServerIdentifier(identifier);
116
- }
117
- return getLobehubSkillProviderById(identifier);
118
- }, [type, identifier]);
119
-
120
- // Get dynamic state from store
121
- const klavisServers = useToolStore(klavisStoreSelectors.getServers);
122
- const lobehubSkillServers = useToolStore(lobehubSkillStoreSelectors.getServers);
123
-
124
- const serverState = useMemo(() => {
125
- if (type === 'klavis') {
126
- return klavisServers.find((s) => s.identifier === identifier);
127
- }
128
- return lobehubSkillServers.find((s) => s.identifier === identifier);
129
- }, [type, identifier, klavisServers, lobehubSkillServers]);
130
-
131
- const isConnected = useMemo(() => {
132
- if (!serverState) return false;
133
- if (type === 'klavis') {
134
- return serverState.status === KlavisServerStatus.CONNECTED;
135
- }
136
- return serverState.status === LobehubSkillStatus.CONNECTED;
137
- }, [type, serverState]);
138
-
139
- const tools = useMemo(() => {
140
- return serverState?.tools?.map((tool) => tool.name) || [];
141
- }, [serverState]);
142
-
143
- if (!config) return null;
144
-
145
- const { author, authorUrl, description, icon, introduction, label } = config;
146
-
147
- // Get identifier for i18n keys
148
- const i18nIdentifier =
149
- type === 'klavis'
150
- ? (config as KlavisServerType).identifier
151
- : (config as LobehubSkillProviderType).id;
152
- const i18nPrefix = type === 'klavis' ? 'tools.klavis.servers' : 'tools.lobehubSkill.providers';
153
-
154
- const localizedDescription = t(`${i18nPrefix}.${i18nIdentifier}.description`, {
155
- defaultValue: description,
156
- ns: 'setting',
157
- });
158
- const localizedIntroduction = t(`${i18nPrefix}.${i18nIdentifier}.introduction`, {
159
- defaultValue: introduction,
160
- ns: 'setting',
161
- });
162
-
163
- const renderIcon = () => {
164
- if (typeof icon === 'string') {
165
- return <Image alt={label} height={36} src={icon} width={36} />;
166
- }
167
- return <Icon fill={cssVar.colorText} icon={icon} size={36} />;
168
- };
169
-
170
- const handleAuthorClick = () => {
171
- if (authorUrl) {
172
- window.open(authorUrl, '_blank', 'noopener,noreferrer');
173
- }
174
- };
175
-
176
- const renderConnectButton = () => {
177
- if (isConnected) return null;
178
- if (!onConnect) return null;
179
-
180
- if (isConnecting) {
181
- return (
182
- <Button disabled icon={<Icon icon={Loader2} spin />} type="default">
183
- {t('tools.klavis.connect', { defaultValue: 'Connect', ns: 'setting' })}
184
- </Button>
185
- );
186
- }
187
-
188
- return (
189
- <Button icon={<Icon icon={SquareArrowOutUpRight} />} onClick={onConnect} type="primary">
190
- {t('tools.klavis.connect', { defaultValue: 'Connect', ns: 'setting' })}
191
- </Button>
192
- );
193
- };
194
-
195
- return (
196
- <Modal
197
- destroyOnHidden
198
- footer={null}
199
- onCancel={onClose}
200
- open={open}
201
- title={t('dev.title.skillDetails')}
202
- width={800}
203
- >
204
- <Flexbox gap={20}>
205
- {/* Header */}
206
- <Flexbox
207
- align="center"
208
- className={styles.header}
209
- horizontal
210
- justify="space-between"
211
- style={{ flexWrap: 'nowrap' }}
212
- >
213
- <Flexbox align="center" gap={16} horizontal>
214
- <div className={styles.icon}>{renderIcon()}</div>
215
- <Flexbox gap={4}>
216
- <span className={styles.title}>{label}</span>
217
- <Text style={{ fontSize: 14 }} type="secondary">
218
- {localizedDescription}
219
- </Text>
220
- </Flexbox>
221
- </Flexbox>
222
- {renderConnectButton()}
223
- </Flexbox>
224
-
225
- {/* Introduction */}
226
- <Typography className={styles.introduction}>{localizedIntroduction}</Typography>
227
-
228
- {/* Developed by */}
229
- <Flexbox gap={8}>
230
- <Flexbox align="center" gap={4} horizontal>
231
- <span className={styles.sectionTitle}>{t('integrationDetail.developedBy')}</span>
232
- <span
233
- className={styles.authorLink}
234
- onClick={handleAuthorClick}
235
- style={{ cursor: authorUrl ? 'pointer' : 'default' }}
236
- >
237
- {author}
238
- {authorUrl && <Icon icon={ExternalLink} size={12} />}
239
- </span>
240
- </Flexbox>
241
- <Text className={styles.trustWarning} type="secondary">
242
- {t('integrationDetail.trustWarning')}
243
- </Text>
244
- </Flexbox>
245
-
246
- {/* Tools */}
247
- {tools.length > 0 && (
248
- <>
249
- <Divider style={{ margin: 0 }} />
250
- <Flexbox gap={12}>
251
- <Flexbox align="center" gap={8} horizontal>
252
- <span className={styles.sectionTitle}>{t('integrationDetail.tools')}</span>
253
- <Tag>{tools.length}</Tag>
254
- </Flexbox>
255
- <div className={styles.toolsContainer}>
256
- {tools.map((tool) => (
257
- <Tag className={styles.toolTag} key={tool}>
258
- {tool}
259
- </Tag>
260
- ))}
261
- </div>
262
- </Flexbox>
263
- </>
264
- )}
265
-
266
- {/* Details */}
267
- <Divider style={{ margin: 0 }} />
268
- <Flexbox gap={12}>
269
- <span className={styles.sectionTitle}>{t('integrationDetail.details')}</span>
270
- <Flexbox gap={16} horizontal>
271
- <div className={styles.detailItem}>
272
- <span className={styles.detailLabel}>{t('integrationDetail.author')}</span>
273
- <span
274
- className={styles.authorLink}
275
- onClick={handleAuthorClick}
276
- style={{ cursor: authorUrl ? 'pointer' : 'default' }}
277
- >
278
- {author}
279
- {authorUrl && <Icon icon={ExternalLink} size={12} />}
280
- </span>
281
- </div>
282
- </Flexbox>
283
- </Flexbox>
284
- </Flexbox>
285
- </Modal>
286
- );
287
- },
288
- );
289
-
290
- IntegrationDetailModal.displayName = 'IntegrationDetailModal';
291
-
292
- export default IntegrationDetailModal;
17
+ export const createIntegrationDetailModal = ({
18
+ identifier,
19
+ serverName,
20
+ type,
21
+ }: CreateIntegrationDetailModalOptions) =>
22
+ createModal({
23
+ children: (
24
+ <IntegrationDetailContent identifier={identifier} serverName={serverName} type={type} />
25
+ ),
26
+ destroyOnHidden: true,
27
+ footer: null,
28
+ title: t('dev.title.skillDetails', { ns: 'plugin' }),
29
+ width: 800,
30
+ });
@@ -23,12 +23,12 @@ import {
23
23
  Package,
24
24
  TerminalIcon,
25
25
  } from 'lucide-react';
26
- import { markdownToTxt } from 'markdown-to-txt';
27
26
  import { memo, useState } from 'react';
28
27
  import { useTranslation } from 'react-i18next';
29
28
 
30
29
  import Descriptions from '@/components/Descriptions';
31
30
  import InlineTable from '@/components/InlineTable';
31
+ import { markdownToTxt } from '@/utils/markdownToTxt';
32
32
 
33
33
  import Title from '../../../app/[variants]/(main)/community/features/Title';
34
34
  import InstallationIcon from '../../../components/MCPDepsIcon';
@@ -1,11 +1,11 @@
1
1
  import { Block, Collapse, Empty, Highlighter, Icon, Markdown } from '@lobehub/ui';
2
2
  import { cssVar } from 'antd-style';
3
3
  import { CheckIcon, MessageSquare, MinusIcon } from 'lucide-react';
4
- import { markdownToTxt } from 'markdown-to-txt';
5
4
  import { memo } from 'react';
6
5
  import { useTranslation } from 'react-i18next';
7
6
 
8
7
  import InlineTable from '@/components/InlineTable';
8
+ import { markdownToTxt } from '@/utils/markdownToTxt';
9
9
 
10
10
  import Title from '../../../app/[variants]/(main)/community/features/Title';
11
11
  import CollapseDesc from '../CollapseDesc';
@@ -1,11 +1,11 @@
1
1
  import { Block, Collapse, Empty, Highlighter, Icon, Markdown, Tag } from '@lobehub/ui';
2
2
  import { cssVar } from 'antd-style';
3
3
  import { CheckIcon, MinusIcon, Wrench } from 'lucide-react';
4
- import { markdownToTxt } from 'markdown-to-txt';
5
4
  import { memo } from 'react';
6
5
  import { useTranslation } from 'react-i18next';
7
6
 
8
7
  import InlineTable from '@/components/InlineTable';
8
+ import { markdownToTxt } from '@/utils/markdownToTxt';
9
9
 
10
10
  import Title from '../../../app/[variants]/(main)/community/features/Title';
11
11
  import CollapseDesc from '../CollapseDesc';