@lobehub/lobehub 2.0.0-next.142 → 2.0.0-next.143

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 (54) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/apps/desktop/package.json +1 -0
  3. package/apps/desktop/src/main/core/ui/__tests__/MenuManager.test.ts +320 -0
  4. package/apps/desktop/src/main/core/ui/__tests__/Tray.test.ts +518 -0
  5. package/apps/desktop/src/main/core/ui/__tests__/TrayManager.test.ts +360 -0
  6. package/apps/desktop/src/main/menus/impls/BaseMenuPlatform.test.ts +49 -0
  7. package/apps/desktop/src/main/menus/impls/linux.test.ts +552 -0
  8. package/apps/desktop/src/main/menus/impls/macOS.test.ts +464 -0
  9. package/apps/desktop/src/main/menus/impls/windows.test.ts +429 -0
  10. package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +2 -2
  11. package/apps/desktop/src/main/services/__tests__/fileSearchSrv.test.ts +402 -0
  12. package/apps/desktop/src/main/utils/__tests__/file-system.test.ts +91 -0
  13. package/apps/desktop/src/main/utils/__tests__/logger.test.ts +229 -0
  14. package/apps/desktop/src/preload/electronApi.test.ts +142 -0
  15. package/apps/desktop/src/preload/invoke.test.ts +145 -0
  16. package/apps/desktop/src/preload/routeInterceptor.test.ts +374 -0
  17. package/apps/desktop/src/preload/streamer.test.ts +365 -0
  18. package/apps/desktop/vitest.config.mts +1 -0
  19. package/changelog/v1.json +9 -0
  20. package/locales/ar/marketAuth.json +13 -0
  21. package/locales/bg-BG/marketAuth.json +13 -0
  22. package/locales/de-DE/marketAuth.json +13 -0
  23. package/locales/en-US/marketAuth.json +13 -0
  24. package/locales/es-ES/marketAuth.json +13 -0
  25. package/locales/fa-IR/marketAuth.json +13 -0
  26. package/locales/fr-FR/marketAuth.json +13 -0
  27. package/locales/it-IT/marketAuth.json +13 -0
  28. package/locales/ja-JP/marketAuth.json +13 -0
  29. package/locales/ko-KR/marketAuth.json +13 -0
  30. package/locales/nl-NL/marketAuth.json +13 -0
  31. package/locales/pl-PL/marketAuth.json +13 -0
  32. package/locales/pt-BR/marketAuth.json +13 -0
  33. package/locales/ru-RU/marketAuth.json +13 -0
  34. package/locales/tr-TR/marketAuth.json +13 -0
  35. package/locales/vi-VN/marketAuth.json +13 -0
  36. package/locales/zh-CN/marketAuth.json +13 -0
  37. package/locales/zh-TW/marketAuth.json +13 -0
  38. package/package.json +1 -1
  39. package/packages/database/src/models/user.ts +2 -0
  40. package/packages/types/src/discover/mcp.ts +2 -1
  41. package/packages/types/src/tool/plugin.ts +2 -1
  42. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishButton.tsx +0 -2
  43. package/src/app/[variants]/(main)/discover/(detail)/mcp/features/Sidebar/ActionButton/index.tsx +33 -7
  44. package/src/features/PluginStore/McpList/List/Action.tsx +20 -1
  45. package/src/layout/AuthProvider/MarketAuth/MarketAuthConfirmModal.tsx +158 -0
  46. package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +130 -14
  47. package/src/libs/mcp/types.ts +8 -0
  48. package/src/locales/default/marketAuth.ts +13 -0
  49. package/src/server/routers/lambda/market/index.ts +85 -2
  50. package/src/server/services/discover/index.ts +45 -4
  51. package/src/services/discover.ts +1 -1
  52. package/src/services/mcp.ts +18 -3
  53. package/src/store/tool/slices/mcpStore/action.test.ts +141 -0
  54. package/src/store/tool/slices/mcpStore/action.ts +153 -11
@@ -1,4 +1,15 @@
1
1
  {
2
+ "authorize": {
3
+ "cancel": "Anuluj",
4
+ "confirm": "Autoryzuj użycie",
5
+ "description": {
6
+ "and": "oraz",
7
+ "prefix": "Klikając „Autoryzuj użycie”, wyrażasz zgodę na",
8
+ "privacy": "Politykę prywatności",
9
+ "terms": "Warunki korzystania z usługi"
10
+ },
11
+ "title": "Potwierdź autoryzację"
12
+ },
2
13
  "callback": {
3
14
  "buttons": {
4
15
  "close": "Zamknij okno"
@@ -33,8 +44,10 @@
33
44
  "stateMissing": "Nie znaleziono stanu autoryzacji, spróbuj ponownie."
34
45
  },
35
46
  "messages": {
47
+ "authorized": "Autoryzacja usługi LobeHub zakończona pomyślnie",
36
48
  "loading": "Uruchamianie procesu autoryzacji...",
37
49
  "success": {
50
+ "cloudMcpInstall": "Autoryzacja zakończona sukcesem! Możesz teraz zainstalować wtyczkę Cloud MCP.",
38
51
  "submit": "Autoryzacja zakończona sukcesem! Możesz teraz opublikować asystenta.",
39
52
  "upload": "Autoryzacja zakończona sukcesem! Możesz teraz opublikować nową wersję."
40
53
  }
@@ -1,4 +1,15 @@
1
1
  {
2
+ "authorize": {
3
+ "cancel": "Cancelar",
4
+ "confirm": "Autorizar uso",
5
+ "description": {
6
+ "and": "e",
7
+ "prefix": "Ao clicar em autorizar uso, você concorda com",
8
+ "privacy": "Política de Privacidade",
9
+ "terms": "Termos de Serviço"
10
+ },
11
+ "title": "Confirmar Autorização"
12
+ },
2
13
  "callback": {
3
14
  "buttons": {
4
15
  "close": "Fechar janela"
@@ -33,8 +44,10 @@
33
44
  "stateMissing": "Estado de autorização não encontrado, tente novamente."
34
45
  },
35
46
  "messages": {
47
+ "authorized": "Autorização do serviço LobeHub realizada com sucesso",
36
48
  "loading": "Iniciando o processo de autorização...",
37
49
  "success": {
50
+ "cloudMcpInstall": "Autorização bem-sucedida! Agora você pode instalar o plugin Cloud MCP.",
38
51
  "submit": "Autorização bem-sucedida! Agora você pode publicar o assistente.",
39
52
  "upload": "Autorização bem-sucedida! Agora você pode publicar uma nova versão."
40
53
  }
@@ -1,4 +1,15 @@
1
1
  {
2
+ "authorize": {
3
+ "cancel": "Отмена",
4
+ "confirm": "Подтвердить доступ",
5
+ "description": {
6
+ "and": "и",
7
+ "prefix": "Нажимая «Подтвердить доступ», вы соглашаетесь с",
8
+ "privacy": "Политикой конфиденциальности",
9
+ "terms": "Условиями обслуживания"
10
+ },
11
+ "title": "Подтвердить авторизацию"
12
+ },
2
13
  "callback": {
3
14
  "buttons": {
4
15
  "close": "Закрыть окно"
@@ -33,8 +44,10 @@
33
44
  "stateMissing": "Состояние авторизации не найдено, пожалуйста, попробуйте снова."
34
45
  },
35
46
  "messages": {
47
+ "authorized": "Авторизация сервиса LobeHub прошла успешно",
36
48
  "loading": "Запуск процесса авторизации...",
37
49
  "success": {
50
+ "cloudMcpInstall": "Доступ успешно предоставлен! Теперь вы можете установить плагин Cloud MCP.",
38
51
  "submit": "Авторизация прошла успешно! Теперь вы можете опубликовать помощника.",
39
52
  "upload": "Авторизация прошла успешно! Теперь вы можете опубликовать новую версию."
40
53
  }
@@ -1,4 +1,15 @@
1
1
  {
2
+ "authorize": {
3
+ "cancel": "İptal",
4
+ "confirm": "Kullanıma Yetki Ver",
5
+ "description": {
6
+ "and": "ve",
7
+ "prefix": "Kullanıma yetki ver'e tıklayarak şu şartları kabul etmiş olursunuz:",
8
+ "privacy": "Gizlilik Politikası",
9
+ "terms": "Hizmet Şartları"
10
+ },
11
+ "title": "Yetkilendirmeyi Onayla"
12
+ },
2
13
  "callback": {
3
14
  "buttons": {
4
15
  "close": "Pencereyi Kapat"
@@ -33,8 +44,10 @@
33
44
  "stateMissing": "Yetkilendirme durumu bulunamadı, lütfen tekrar deneyin."
34
45
  },
35
46
  "messages": {
47
+ "authorized": "LobeHub hizmet yetkilendirmesi başarıyla tamamlandı",
36
48
  "loading": "Yetkilendirme süreci başlatılıyor...",
37
49
  "success": {
50
+ "cloudMcpInstall": "Yetkilendirme başarılı! Artık Cloud MCP eklentisini yükleyebilirsiniz.",
38
51
  "submit": "Yetkilendirme başarılı! Artık asistan yayınlayabilirsiniz.",
39
52
  "upload": "Yetkilendirme başarılı! Artık yeni bir sürüm yayınlayabilirsiniz."
40
53
  }
@@ -1,4 +1,15 @@
1
1
  {
2
+ "authorize": {
3
+ "cancel": "Hủy",
4
+ "confirm": "Ủy quyền sử dụng",
5
+ "description": {
6
+ "and": "và",
7
+ "prefix": "Bằng cách nhấp vào ủy quyền sử dụng, bạn đồng ý với",
8
+ "privacy": "Chính sách quyền riêng tư",
9
+ "terms": "Điều khoản dịch vụ"
10
+ },
11
+ "title": "Xác nhận ủy quyền"
12
+ },
2
13
  "callback": {
3
14
  "buttons": {
4
15
  "close": "Đóng cửa sổ"
@@ -33,8 +44,10 @@
33
44
  "stateMissing": "Không tìm thấy trạng thái xác thực, vui lòng thử lại."
34
45
  },
35
46
  "messages": {
47
+ "authorized": "Dịch vụ LobeHub đã được cấp quyền thành công",
36
48
  "loading": "Đang khởi động quy trình xác thực...",
37
49
  "success": {
50
+ "cloudMcpInstall": "Ủy quyền thành công! Bây giờ bạn có thể cài đặt plugin Cloud MCP.",
38
51
  "submit": "Xác thực thành công! Bây giờ bạn có thể xuất bản trợ lý.",
39
52
  "upload": "Xác thực thành công! Bây giờ bạn có thể xuất bản phiên bản mới."
40
53
  }
@@ -1,4 +1,15 @@
1
1
  {
2
+ "authorize": {
3
+ "cancel": "取消",
4
+ "confirm": "授权使用",
5
+ "description": {
6
+ "and": "和",
7
+ "prefix": "点击授权使用即视为同意",
8
+ "privacy": "隐私协议",
9
+ "terms": "服务条款"
10
+ },
11
+ "title": "确认授权"
12
+ },
2
13
  "callback": {
3
14
  "buttons": {
4
15
  "close": "关闭窗口"
@@ -33,8 +44,10 @@
33
44
  "stateMissing": "未找到授权状态,请重试。"
34
45
  },
35
46
  "messages": {
47
+ "authorized": "LobeHub 服务授权成功",
36
48
  "loading": "正在启动授权流程...",
37
49
  "success": {
50
+ "cloudMcpInstall": "授权成功!现在可以安装 Cloud MCP 插件了。",
38
51
  "submit": "授权成功!现在可以发布助手了。",
39
52
  "upload": "授权成功!现在可以发布新版本了。"
40
53
  }
@@ -1,4 +1,15 @@
1
1
  {
2
+ "authorize": {
3
+ "cancel": "取消",
4
+ "confirm": "授權使用",
5
+ "description": {
6
+ "and": "和",
7
+ "prefix": "點擊授權使用即視為同意",
8
+ "privacy": "隱私協議",
9
+ "terms": "服務條款"
10
+ },
11
+ "title": "確認授權"
12
+ },
2
13
  "callback": {
3
14
  "buttons": {
4
15
  "close": "關閉視窗"
@@ -33,8 +44,10 @@
33
44
  "stateMissing": "找不到授權狀態,請重試。"
34
45
  },
35
46
  "messages": {
47
+ "authorized": "LobeHub 服務授權成功",
36
48
  "loading": "正在啟動授權流程⋯⋯",
37
49
  "success": {
50
+ "cloudMcpInstall": "授權成功!現在可以安裝 Cloud MCP 外掛了。",
38
51
  "submit": "授權成功!現在可以發佈助手了。",
39
52
  "upload": "授權成功!現在可以發佈新版本了。"
40
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.142",
3
+ "version": "2.0.0-next.143",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -80,6 +80,7 @@ export class UserModel {
80
80
  settingsImage: userSettings.image,
81
81
  settingsKeyVaults: userSettings.keyVaults,
82
82
  settingsLanguageModel: userSettings.languageModel,
83
+ settingsMarket: userSettings.market,
83
84
  settingsSystemAgent: userSettings.systemAgent,
84
85
  settingsTTS: userSettings.tts,
85
86
  settingsTool: userSettings.tool,
@@ -112,6 +113,7 @@ export class UserModel {
112
113
  image: state.settingsImage || {},
113
114
  keyVaults: decryptKeyVaults,
114
115
  languageModel: state.settingsLanguageModel || {},
116
+ market: state.settingsMarket || undefined,
115
117
  systemAgent: state.settingsSystemAgent || {},
116
118
  tool: state.settingsTool || {},
117
119
  tts: state.settingsTTS || {},
@@ -41,7 +41,7 @@ export enum McpNavKey {
41
41
 
42
42
  export enum McpConnectionType {
43
43
  http = 'http',
44
- stdio = 'stdio'
44
+ stdio = 'stdio',
45
45
  }
46
46
 
47
47
  export type DiscoverMcpItem = PluginItem;
@@ -60,6 +60,7 @@ export interface McpQueryParams {
60
60
  export type McpListResponse = PluginListResponse;
61
61
 
62
62
  export interface DiscoverMcpDetail extends PluginItemDetail {
63
+ haveCloudEndpoint?: boolean;
63
64
  isClaimed?: boolean;
64
65
  related: DiscoverMcpItem[];
65
66
  }
@@ -25,8 +25,9 @@ export interface CustomPluginParams {
25
25
  args?: string[];
26
26
  env?: Record<string, string>;
27
27
  command?: string;
28
- type: 'http' | 'stdio';
28
+ type: 'http' | 'stdio' | 'cloud';
29
29
  url?: string;
30
+ cloudEndPoint?: string; // Cloud gateway endpoint for cloud type
30
31
  // Added authentication configuration support
31
32
  auth?: {
32
33
  type: 'none' | 'bearer' | 'oauth2';
@@ -87,9 +87,7 @@ const MarketPublishButton = memo<MarketPublishButtonProps>(
87
87
  `[MarketPublishButton][${action}] User not authenticated, starting authorization`,
88
88
  );
89
89
  try {
90
- message.loading({ content: tMarketAuth('messages.loading'), key: 'market-auth' });
91
90
  const accountId = await signIn();
92
- message.success({ content: buttonCopy.successMessage, key: 'market-auth' });
93
91
 
94
92
  let targetAction: MarketPublishAction = action;
95
93
 
@@ -10,6 +10,7 @@ import { Flexbox } from 'react-layout-kit';
10
10
 
11
11
  import MCPInstallProgress from '@/features/MCP/MCPInstallProgress';
12
12
  import { useDetailContext } from '@/features/MCPPluginDetail/DetailProvider';
13
+ import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
13
14
  import { useToolStore } from '@/store/tool';
14
15
  import { pluginSelectors } from '@/store/tool/slices/plugin/selectors';
15
16
 
@@ -23,9 +24,11 @@ const useStyles = createStyles(({ css }) => ({
23
24
 
24
25
  const ActionButton = memo(() => {
25
26
  const { t } = useTranslation(['discover', 'plugin']);
26
- const { identifier } = useDetailContext();
27
+ const detailContext = useDetailContext();
28
+ const { identifier, haveCloudEndpoint } = detailContext;
27
29
  const { styles } = useStyles();
28
30
  const [isLoading, setIsLoading] = useState(false);
31
+ const { isAuthenticated, isLoading: isAuthLoading, signIn } = useMarketAuth();
29
32
 
30
33
  const [installed, installMCPPlugin, uninstallMCPPlugin] = useToolStore((s) => [
31
34
  pluginSelectors.isPluginInstalled(identifier!)(s),
@@ -33,24 +36,47 @@ const ActionButton = memo(() => {
33
36
  s.uninstallMCPPlugin,
34
37
  ]);
35
38
 
39
+ // Check if this is a cloud MCP plugin
40
+ const isCloudMcp = haveCloudEndpoint;
41
+
36
42
  const installPlugin = async () => {
37
43
  if (!identifier) return;
38
- setIsLoading(true);
39
44
 
40
- await installMCPPlugin(identifier);
45
+ // If this is a cloud MCP and user is not authenticated, request authorization first
46
+ if (isCloudMcp && !isAuthenticated) {
47
+ try {
48
+ await signIn();
49
+ } catch {
50
+ return; // Don't proceed with installation if auth fails
51
+ }
52
+ }
41
53
 
42
- setIsLoading(false);
54
+ // Proceed with installation
55
+ setIsLoading(true);
56
+ try {
57
+ await installMCPPlugin(identifier);
58
+ } finally {
59
+ setIsLoading(false);
60
+ }
43
61
  };
44
62
 
63
+ const buttonLoading = isLoading || isAuthLoading;
64
+
45
65
  return installed ? (
46
66
  <Flexbox gap={8} horizontal>
47
- <Button block className={styles.button} disabled={isLoading} size={'large'} type={'default'}>
67
+ <Button
68
+ block
69
+ className={styles.button}
70
+ disabled={buttonLoading}
71
+ size={'large'}
72
+ type={'default'}
73
+ >
48
74
  {t('plugins.installed')}
49
75
  </Button>
50
76
 
51
77
  <Button
52
78
  icon={<Icon icon={Trash2Icon} size={20} />}
53
- loading={isLoading}
79
+ loading={buttonLoading}
54
80
  onClick={async () => {
55
81
  setIsLoading(true);
56
82
  await uninstallMCPPlugin(identifier!);
@@ -68,7 +94,7 @@ const ActionButton = memo(() => {
68
94
  <Button
69
95
  block
70
96
  className={styles.button}
71
- loading={isLoading}
97
+ loading={buttonLoading}
72
98
  onClick={installPlugin}
73
99
  size={'large'}
74
100
  type={'primary'}
@@ -5,6 +5,7 @@ import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
+ import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
8
9
  import { useAgentStore } from '@/store/agent';
9
10
  import { agentSelectors } from '@/store/agent/selectors';
10
11
  import { useToolStore } from '@/store/tool';
@@ -15,13 +16,14 @@ interface ActionsProps {
15
16
  }
16
17
 
17
18
  const Actions = memo<ActionsProps>(({ identifier }) => {
18
- const [installed, installing, unInstallPlugin, installMCPPlugin, cancelInstallMCPPlugin] =
19
+ const [installed, installing, unInstallPlugin, installMCPPlugin, cancelInstallMCPPlugin, plugin] =
19
20
  useToolStore((s) => [
20
21
  pluginSelectors.isPluginInstalled(identifier)(s),
21
22
  mcpStoreSelectors.isMCPInstalling(identifier)(s),
22
23
  s.uninstallPlugin,
23
24
  s.installMCPPlugin,
24
25
  s.cancelInstallMCPPlugin,
26
+ mcpStoreSelectors.getPluginById(identifier)(s),
25
27
  ]);
26
28
 
27
29
  const { t } = useTranslation('plugin');
@@ -30,6 +32,10 @@ const Actions = memo<ActionsProps>(({ identifier }) => {
30
32
  agentSelectors.currentAgentPlugins(s).includes(identifier),
31
33
  ]);
32
34
  const { modal } = App.useApp();
35
+ const { isAuthenticated, signIn } = useMarketAuth();
36
+
37
+ // Check if this is a cloud MCP plugin
38
+ const isCloudMcp = !!((plugin as any)?.cloudEndPoint || (plugin as any)?.haveCloudEndpoint);
33
39
 
34
40
  return (
35
41
  <Flexbox align={'center'} horizontal>
@@ -86,6 +92,19 @@ const Actions = memo<ActionsProps>(({ identifier }) => {
86
92
  onClick={async (e) => {
87
93
  e.stopPropagation();
88
94
 
95
+ // If this is a cloud MCP and user is not authenticated, request authorization first
96
+ if (isCloudMcp && !isAuthenticated) {
97
+ console.log(
98
+ '[MCPListAction] Cloud MCP detected, user not authenticated, starting authorization',
99
+ );
100
+
101
+ try {
102
+ await signIn();
103
+ } catch {
104
+ return; // Don't proceed with installation if auth fails
105
+ }
106
+ }
107
+
89
108
  const isSuccess = await installMCPPlugin(identifier);
90
109
 
91
110
  if (isSuccess) {
@@ -0,0 +1,158 @@
1
+ 'use client';
2
+
3
+ import { Modal } from '@lobehub/ui';
4
+ import { createStyles } from 'antd-style';
5
+ import { ArrowRight, ShieldCheck } from 'lucide-react';
6
+ import { memo } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+
9
+ const useStyles = createStyles(({ css, token }) => ({
10
+ content: css`
11
+ .ant-modal-content {
12
+ overflow: hidden;
13
+ padding: 0;
14
+ }
15
+
16
+ .ant-modal-header {
17
+ margin-block-end: 0;
18
+ padding: 0;
19
+ border-block-end: none;
20
+ }
21
+
22
+ .ant-modal-body {
23
+ padding: 0;
24
+ }
25
+
26
+ .ant-modal-footer {
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: space-between;
30
+
31
+ margin-block-start: 0;
32
+ padding-block: 16px;
33
+ padding-inline: 24px;
34
+ border-block-start: 1px solid ${token.colorBorder};
35
+
36
+ background: ${token.colorBgContainer};
37
+
38
+ .ant-btn {
39
+ margin: 0;
40
+ }
41
+ }
42
+ `,
43
+ description: css`
44
+ font-size: 14px;
45
+ line-height: 1.5;
46
+ color: ${token.colorTextSecondary};
47
+ text-align: center;
48
+
49
+ a {
50
+ color: ${token.colorPrimary};
51
+ text-decoration: none;
52
+
53
+ &:hover {
54
+ text-decoration: underline;
55
+ }
56
+ }
57
+
58
+ .highlight {
59
+ font-weight: 500;
60
+ color: ${token.colorText};
61
+ }
62
+ `,
63
+ header: css`
64
+ padding-block: 24px 16px;
65
+ padding-inline: 24px;
66
+ text-align: center;
67
+ `,
68
+ iconWrapper: css`
69
+ display: flex;
70
+ flex-direction: column;
71
+ align-items: center;
72
+
73
+ padding-block: 32px 0;
74
+ padding-inline: 0;
75
+ `,
76
+ okButton: css`
77
+ display: flex;
78
+ gap: 8px;
79
+ align-items: center;
80
+ `,
81
+ shieldIcon: css`
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+
86
+ width: 64px;
87
+ height: 64px;
88
+ border-radius: 50%;
89
+
90
+ background: ${token.colorPrimaryBg};
91
+
92
+ svg {
93
+ width: 36px;
94
+ height: 36px;
95
+ color: ${token.colorPrimary};
96
+ }
97
+ `,
98
+ title: css`
99
+ margin-block-end: 24px;
100
+ font-size: 18px;
101
+ font-weight: 600;
102
+ color: ${token.colorText};
103
+ `,
104
+ }));
105
+
106
+ interface MarketAuthConfirmModalProps {
107
+ onCancel: () => void;
108
+ onConfirm: () => void;
109
+ open: boolean;
110
+ }
111
+
112
+ const MarketAuthConfirmModal = memo<MarketAuthConfirmModalProps>(
113
+ ({ open, onConfirm, onCancel }) => {
114
+ const { t } = useTranslation('marketAuth');
115
+ const { styles } = useStyles();
116
+
117
+ return (
118
+ <Modal
119
+ cancelText={t('authorize.cancel')}
120
+ className={styles.content}
121
+ okButtonProps={{
122
+ className: styles.okButton,
123
+ icon: <ArrowRight size={16} />,
124
+ }}
125
+ okText={t('authorize.confirm')}
126
+ onCancel={onCancel}
127
+ onOk={onConfirm}
128
+ open={open}
129
+ title={null}
130
+ width={440}
131
+ >
132
+ <div className={styles.iconWrapper}>
133
+ <div className={styles.shieldIcon}>
134
+ <ShieldCheck />
135
+ </div>
136
+ </div>
137
+
138
+ <div className={styles.header}>
139
+ <div className={styles.title}>{t('authorize.title')}</div>
140
+ <div className={styles.description}>
141
+ {t('authorize.description.prefix')} <span className="highlight">LobeHub</span>{' '}
142
+ <a href="https://lobehub.com/terms" rel="noopener noreferrer" target="_blank">
143
+ {t('authorize.description.terms')}
144
+ </a>{' '}
145
+ {t('authorize.description.and')}{' '}
146
+ <a href="https://lobehub.com/privacy" rel="noopener noreferrer" target="_blank">
147
+ {t('authorize.description.privacy')}
148
+ </a>
149
+ </div>
150
+ </div>
151
+ </Modal>
152
+ );
153
+ },
154
+ );
155
+
156
+ MarketAuthConfirmModal.displayName = 'MarketAuthConfirmModal';
157
+
158
+ export default MarketAuthConfirmModal;