@lobehub/lobehub 2.0.0-next.344 → 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 (168) 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 +41 -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 +15 -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/context-engine/src/engine/messages/MessagesEngine.ts +9 -9
  38. package/packages/context-engine/src/providers/GroupContextInjector.ts +19 -33
  39. package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +79 -43
  40. package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +5 -15
  41. package/packages/database/src/repositories/userMemory/__tests__/UserMemoryTopicRepository.test.ts +24 -3
  42. package/packages/model-bank/src/modelProviders/comfyui.ts +0 -1
  43. package/packages/model-bank/src/modelProviders/fal.ts +0 -1
  44. package/packages/types/src/fetch.ts +1 -2
  45. package/packages/utils/src/server/__tests__/auth.test.ts +0 -47
  46. package/packages/utils/src/server/auth.ts +1 -9
  47. package/scripts/_shared/checkDeprecatedClerkEnv.js +42 -0
  48. package/scripts/changelogWorkflow/buildStaticChangelog.ts +2 -1
  49. package/scripts/clerk-to-betterauth/_internal/types.ts +53 -20
  50. package/scripts/clerk-to-betterauth/export-clerk-users-with-api.ts +43 -36
  51. package/scripts/countEnWord.ts +1 -1
  52. package/scripts/electronWorkflow/modifiers/appCode.mts +2 -131
  53. package/scripts/i18nWorkflow/protectedPatterns.ts +1 -2
  54. package/scripts/prebuild.mts +10 -8
  55. package/scripts/serverLauncher/startServer.js +23 -5
  56. package/src/app/(backend)/middleware/auth/index.test.ts +8 -4
  57. package/src/app/(backend)/middleware/auth/index.ts +0 -15
  58. package/src/app/(backend)/middleware/auth/utils.test.ts +0 -28
  59. package/src/app/(backend)/middleware/auth/utils.ts +2 -17
  60. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +3 -51
  61. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -4
  62. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +7 -6
  63. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -16
  64. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/index.tsx +1 -1
  65. package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +13 -13
  66. package/src/app/[variants]/(main)/home/features/RecentPage/Item.tsx +2 -2
  67. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
  68. package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -2
  69. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -22
  70. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +12 -14
  71. package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +8 -14
  72. package/src/app/[variants]/(main)/settings/skill/index.tsx +7 -5
  73. package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +2 -35
  74. package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -20
  75. package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -2
  76. package/src/app/[variants]/(mobile)/me/profile/features/Category.tsx +3 -13
  77. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +2 -3
  78. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
  79. package/src/app/[variants]/share/t/[id]/index.tsx +1 -1
  80. package/src/app/robots.tsx +1 -1
  81. package/src/envs/auth.ts +2 -27
  82. package/src/envs/llm.ts +2 -2
  83. package/src/features/AgentSetting/AgentPlugin/index.tsx +9 -12
  84. package/src/features/ChatInput/ActionBar/Tools/index.tsx +7 -5
  85. package/src/features/ChatMiniMap/utils.ts +1 -1
  86. package/src/features/CommandMenu/SearchResults.tsx +1 -1
  87. package/src/features/Conversation/ChatList/components/AutoScroll/DebugInspector.tsx +166 -0
  88. package/src/features/Conversation/ChatList/components/AutoScroll/index.tsx +86 -0
  89. package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +11 -17
  90. package/src/features/Conversation/Messages/AgentCouncil/components/AutoScrollShadow.tsx +25 -14
  91. package/src/features/Conversation/Messages/AgentCouncil/components/CouncilMember.tsx +1 -1
  92. package/src/features/IntegrationDetailModal/IntegrationDetailContent.tsx +305 -0
  93. package/src/features/IntegrationDetailModal/index.tsx +21 -283
  94. package/src/features/MCPPluginDetail/Deployment/index.tsx +1 -1
  95. package/src/features/MCPPluginDetail/Schema/Prompts.tsx +1 -1
  96. package/src/features/MCPPluginDetail/Schema/Tools.tsx +1 -1
  97. package/src/features/ProfileEditor/AgentTool.tsx +14 -20
  98. package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/NoteFileItem.tsx +1 -1
  99. package/src/features/SkillStore/LobeHubList/index.tsx +50 -87
  100. package/src/features/SkillStore/Search/index.tsx +1 -1
  101. package/src/features/SkillStore/{Content.tsx → SkillStoreContent.tsx} +3 -8
  102. package/src/features/SkillStore/index.tsx +15 -33
  103. package/src/features/User/UserPanel/PanelContent.tsx +0 -8
  104. package/src/features/User/__tests__/PanelContent.test.tsx +1 -35
  105. package/src/features/User/__tests__/UserAvatar.test.tsx +30 -57
  106. package/src/features/User/__tests__/useMenu.test.tsx +2 -43
  107. package/src/layout/AuthProvider/index.tsx +0 -5
  108. package/src/libs/next/config/define-config.ts +6 -0
  109. package/src/libs/next/proxy/createRouteMatcher.test.ts +121 -0
  110. package/src/libs/next/proxy/createRouteMatcher.ts +18 -0
  111. package/src/libs/next/proxy/define-config.ts +4 -53
  112. package/src/libs/next-auth/adapter/index.ts +1 -2
  113. package/src/libs/oidc-provider/provider.test.ts +5 -316
  114. package/src/libs/trpc/lambda/context.test.ts +0 -13
  115. package/src/libs/trpc/lambda/context.ts +3 -22
  116. package/src/libs/trpc/middleware/userAuth.ts +2 -4
  117. package/src/libs/trusted-client/getSessionUser.ts +2 -17
  118. package/src/locales/default/error.ts +0 -6
  119. package/src/locales/default/index.ts +0 -2
  120. package/src/proxy.ts +0 -1
  121. package/src/server/routers/lambda/__tests__/user.test.ts +0 -71
  122. package/src/server/routers/lambda/user.ts +6 -63
  123. package/src/server/services/changelog/index.test.ts +3 -2
  124. package/src/server/services/changelog/index.ts +1 -1
  125. package/src/server/services/user/index.ts +0 -83
  126. package/src/services/chat/index.ts +1 -2
  127. package/src/services/chat/mecha/agentConfigResolver.test.ts +43 -0
  128. package/src/services/chat/mecha/agentConfigResolver.ts +3 -1
  129. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +58 -14
  130. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +10 -2
  131. package/src/store/user/slices/auth/action.test.ts +1 -81
  132. package/src/store/user/slices/auth/action.ts +3 -28
  133. package/src/store/user/slices/auth/initialState.ts +1 -18
  134. package/src/store/user/slices/auth/selectors.test.ts +2 -127
  135. package/src/store/user/slices/auth/selectors.ts +1 -21
  136. package/src/utils/errorResponse.ts +1 -4
  137. package/src/utils/markdownToTxt.ts +20 -0
  138. package/locales/ar/clerk.json +0 -545
  139. package/locales/bg-BG/clerk.json +0 -545
  140. package/locales/de-DE/clerk.json +0 -545
  141. package/locales/en-US/clerk.json +0 -545
  142. package/locales/es-ES/clerk.json +0 -545
  143. package/locales/fa-IR/clerk.json +0 -545
  144. package/locales/fr-FR/clerk.json +0 -545
  145. package/locales/it-IT/clerk.json +0 -545
  146. package/locales/ja-JP/clerk.json +0 -545
  147. package/locales/ko-KR/clerk.json +0 -545
  148. package/locales/nl-NL/clerk.json +0 -545
  149. package/locales/pl-PL/clerk.json +0 -545
  150. package/locales/pt-BR/clerk.json +0 -545
  151. package/locales/ru-RU/clerk.json +0 -545
  152. package/locales/tr-TR/clerk.json +0 -545
  153. package/locales/vi-VN/clerk.json +0 -545
  154. package/locales/zh-CN/clerk.json +0 -545
  155. package/locales/zh-TW/clerk.json +0 -545
  156. package/src/app/(backend)/api/webhooks/clerk/__tests__/fixtures/createUser.json +0 -73
  157. package/src/app/(backend)/api/webhooks/clerk/route.ts +0 -95
  158. package/src/app/(backend)/api/webhooks/clerk/validateRequest.ts +0 -22
  159. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +0 -27
  160. package/src/app/[variants]/(main)/settings/security/features/ClerkProfile.tsx +0 -67
  161. package/src/features/Conversation/ChatList/components/AutoScroll.tsx +0 -25
  162. package/src/layout/AuthProvider/Clerk/UserUpdater.tsx +0 -40
  163. package/src/layout/AuthProvider/Clerk/index.tsx +0 -54
  164. package/src/layout/AuthProvider/Clerk/useAppearance.ts +0 -133
  165. package/src/libs/clerk-auth/index.test.ts +0 -216
  166. package/src/libs/clerk-auth/index.ts +0 -80
  167. package/src/locales/default/clerk.ts +0 -677
  168. package/src/server/services/user/index.test.ts +0 -220
@@ -1,5 +1,4 @@
1
1
  // @vitest-environment node
2
- import { getAuth } from '@clerk/nextjs/server';
3
2
  import { LobeRuntimeAI, ModelRuntime } from '@lobechat/model-runtime';
4
3
  import { ChatErrorType } from '@lobechat/types';
5
4
  import { getXorPayload } from '@lobechat/utils/server';
@@ -11,10 +10,6 @@ import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
11
10
 
12
11
  import { POST } from './route';
13
12
 
14
- vi.mock('@clerk/nextjs/server', () => ({
15
- getAuth: vi.fn(),
16
- }));
17
-
18
13
  vi.mock('@/app/(backend)/middleware/auth/utils', () => ({
19
14
  checkAuthMethod: vi.fn(),
20
15
  }));
@@ -28,17 +23,11 @@ vi.mock('@/server/modules/ModelRuntime', () => ({
28
23
  createTraceOptions: vi.fn().mockReturnValue({}),
29
24
  }));
30
25
 
31
- // Use vi.hoisted to ensure mockState is initialized before mocks are set up
32
- const mockState = vi.hoisted(() => ({ enableClerk: false }));
33
-
34
- // 模拟 @/const/auth 模块
35
26
  vi.mock('@/envs/auth', async (importOriginal) => {
36
- const modules = await importOriginal();
27
+ const actual = await importOriginal<typeof import('@/envs/auth')>();
37
28
  return {
38
- ...(modules as any),
39
- get enableClerk() {
40
- return mockState.enableClerk;
41
- },
29
+ ...actual,
30
+ enableBetterAuth: false,
42
31
  };
43
32
  });
44
33
 
@@ -58,7 +47,6 @@ beforeEach(() => {
58
47
  afterEach(() => {
59
48
  // 清除模拟调用历史
60
49
  vi.clearAllMocks();
61
- mockState.enableClerk = false;
62
50
  });
63
51
 
64
52
  describe('POST handler', () => {
@@ -108,42 +96,6 @@ describe('POST handler', () => {
108
96
  });
109
97
  });
110
98
 
111
- it('should have pass clerk Auth when enable clerk', async () => {
112
- mockState.enableClerk = true;
113
-
114
- vi.mocked(getXorPayload).mockReturnValueOnce({
115
- apiKey: 'test-api-key',
116
- azureApiVersion: 'v1',
117
- });
118
-
119
- const mockParams = Promise.resolve({ provider: 'test-provider' });
120
- vi.mocked(getAuth).mockReturnValue({} as any);
121
- vi.mocked(checkAuthMethod).mockReset();
122
-
123
- const mockRuntime: LobeRuntimeAI = { baseURL: 'abc', chat: vi.fn() };
124
-
125
- // Mock initModelRuntimeFromDB
126
- vi.mocked(initModelRuntimeFromDB).mockResolvedValue(new ModelRuntime(mockRuntime));
127
-
128
- const request = new Request(new URL('https://test.com'), {
129
- method: 'POST',
130
- body: JSON.stringify({ model: 'test-model' }),
131
- headers: {
132
- [LOBE_CHAT_AUTH_HEADER]: 'some-valid-token',
133
- [OAUTH_AUTHORIZED]: '1',
134
- },
135
- });
136
-
137
- await POST(request, { params: mockParams });
138
-
139
- expect(checkAuthMethod).toBeCalledWith({
140
- apiKey: 'test-api-key',
141
- betterAuthAuthorized: false,
142
- clerkAuth: {},
143
- nextAuthAuthorized: true,
144
- });
145
- });
146
-
147
99
  it('should return InternalServerError error when throw a unknown error', async () => {
148
100
  const mockParams = Promise.resolve({ provider: 'test-provider' });
149
101
  vi.mocked(getXorPayload).mockImplementationOnce(() => {
@@ -9,10 +9,6 @@ import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
9
9
 
10
10
  import { GET } from './route';
11
11
 
12
- vi.mock('@clerk/nextjs/server', () => ({
13
- getAuth: vi.fn(),
14
- }));
15
-
16
12
  vi.mock('@/app/(backend)/middleware/auth/utils', () => ({
17
13
  checkAuthMethod: vi.fn(),
18
14
  }));
@@ -21,6 +17,14 @@ vi.mock('@lobechat/utils/server', () => ({
21
17
  getXorPayload: vi.fn(),
22
18
  }));
23
19
 
20
+ vi.mock('@/envs/auth', async (importOriginal) => {
21
+ const actual = await importOriginal<typeof import('@/envs/auth')>();
22
+ return {
23
+ ...actual,
24
+ enableBetterAuth: false,
25
+ };
26
+ });
27
+
24
28
  vi.mock('@/server/modules/ModelRuntime', () => ({
25
29
  initModelRuntimeFromDB: vi.fn(),
26
30
  }));
@@ -68,7 +68,8 @@ const BtnListLoading = memo(() => {
68
68
  * ref: https://authjs.dev/guides/pages/signin
69
69
  */
70
70
  export default memo(() => {
71
- const { t } = useTranslation('clerk');
71
+ const { t } = useTranslation('auth');
72
+ const { t: tCommon } = useTranslation('common');
72
73
  const router = useRouter();
73
74
  const [loadingProvider, setLoadingProvider] = useState<string | null>(null);
74
75
 
@@ -100,9 +101,9 @@ export default memo(() => {
100
101
  };
101
102
 
102
103
  const footerBtns = [
103
- { href: DOCUMENTS_REFER_URL, id: 0, label: t('footerPageLink__help') },
104
- { href: PRIVACY_URL, id: 1, label: t('footerPageLink__privacy') },
105
- { href: TERMS_URL, id: 2, label: t('footerPageLink__terms') },
104
+ { href: DOCUMENTS_REFER_URL, id: 0, label: tCommon('document') },
105
+ { href: PRIVACY_URL, id: 1, label: t('footer.privacy') },
106
+ { href: TERMS_URL, id: 2, label: t('footer.terms') },
106
107
  ];
107
108
 
108
109
  return (
@@ -116,10 +117,10 @@ export default memo(() => {
116
117
  <div>
117
118
  <LobeHub size={48} />
118
119
  </div>
119
- {t('signIn.start.title', { applicationName: BRANDING_NAME })}
120
+ {t('signin.title')}
120
121
  </Text>
121
122
  <Text as={'p'} className={styles.description}>
122
- {t('signIn.start.subtitle')}
123
+ {t('signin.subtitle', { appName: BRANDING_NAME })}
123
124
  </Text>
124
125
  </div>
125
126
  {/* Content */}
@@ -1,6 +1,4 @@
1
- import { SignUp } from '@clerk/nextjs';
2
-
3
- import { enableBetterAuth, enableClerk } from '@/envs/auth';
1
+ import { enableBetterAuth } from '@/envs/auth';
4
2
  import { notFound } from '@/libs/next/navigation';
5
3
  import { metadataModule } from '@/server/metadata';
6
4
  import { translation } from '@/server/translation';
@@ -12,15 +10,6 @@ import BetterAuthSignUpForm from './BetterAuthSignUpForm';
12
10
  export const generateMetadata = async (props: DynamicLayoutProps) => {
13
11
  const locale = await RouteVariants.getLocale(props);
14
12
 
15
- if (enableClerk) {
16
- const { t } = await translation('clerk', locale);
17
- return metadataModule.generate({
18
- description: t('signUp.start.subtitle'),
19
- title: t('signUp.start.title'),
20
- url: '/signup',
21
- });
22
- }
23
-
24
13
  if (enableBetterAuth) {
25
14
  const { t } = await translation('auth', locale);
26
15
  return metadataModule.generate({
@@ -37,10 +26,6 @@ export const generateMetadata = async (props: DynamicLayoutProps) => {
37
26
  };
38
27
 
39
28
  const Page = () => {
40
- if (enableClerk) {
41
- return <SignUp path="/signup" />;
42
- }
43
-
44
29
  if (enableBetterAuth) {
45
30
  return <BetterAuthSignUpForm />;
46
31
  }
@@ -42,7 +42,7 @@ const AgentList = memo<{ onMoreClick?: () => void }>(({ onMoreClick }) => {
42
42
 
43
43
  return (
44
44
  <>
45
- <InboxItem />
45
+ <InboxItem style={{ minHeight: 36 }} />
46
46
  {showPinned && <SessionList dataSource={pinnedList!} />}
47
47
  {showCustom && <Group dataSource={customList!} />}
48
48
  {showDefault && (
@@ -4,10 +4,10 @@ import { getKlavisServerByServerIdentifier, getLobehubSkillProviderById } from '
4
4
  import { Avatar, Flexbox, Icon } from '@lobehub/ui';
5
5
  import { createStaticStyles } from 'antd-style';
6
6
  import { Blocks } from 'lucide-react';
7
- import { type ReactNode, createElement, memo, useMemo, useState } from 'react';
7
+ import { type ReactNode, createElement, memo, useCallback, useMemo } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
- import SkillStore from '@/features/SkillStore';
10
+ import { createSkillStoreModal } from '@/features/SkillStore';
11
11
  import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
12
12
  import { useToolStore } from '@/store/tool';
13
13
 
@@ -61,7 +61,6 @@ const BANNER_SKILL_IDS = [
61
61
 
62
62
  const SkillInstallBanner = memo(() => {
63
63
  const { t } = useTranslation('plugin');
64
- const [open, setOpen] = useState(false);
65
64
 
66
65
  const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
67
66
  const isKlavisEnabled = useServerConfigStore(serverConfigSelectors.enableKlavis);
@@ -108,20 +107,21 @@ const SkillInstallBanner = memo(() => {
108
107
  return items;
109
108
  }, []);
110
109
 
110
+ const handleOpenStore = useCallback(() => {
111
+ createSkillStoreModal();
112
+ }, []);
113
+
111
114
  // Don't show banner if no skills are enabled
112
115
  if (!isLobehubSkillEnabled && !isKlavisEnabled) return null;
113
116
 
114
117
  return (
115
- <>
116
- <div className={styles.banner} onClick={() => setOpen(true)}>
117
- <Flexbox align="center" gap={8} horizontal>
118
- <Icon className={styles.icon} icon={Blocks} size={18} />
119
- <span className={styles.text}>{t('skillInstallBanner.title')}</span>
120
- </Flexbox>
121
- {avatarItems.length > 0 && <Avatar.Group items={avatarItems} shape="circle" size={24} />}
122
- </div>
123
- <SkillStore open={open} setOpen={setOpen} />
124
- </>
118
+ <div className={styles.banner} onClick={handleOpenStore}>
119
+ <Flexbox align="center" gap={8} horizontal>
120
+ <Icon className={styles.icon} icon={Blocks} size={18} />
121
+ <span className={styles.text}>{t('skillInstallBanner.title')}</span>
122
+ </Flexbox>
123
+ {avatarItems.length > 0 && <Avatar.Group items={avatarItems} shape="circle" size={24} />}
124
+ </div>
125
125
  );
126
126
  });
127
127
 
@@ -3,12 +3,12 @@
3
3
  import { Avatar, Block, Center, Flexbox, Icon, Text } from '@lobehub/ui';
4
4
  import { cssVar } from 'antd-style';
5
5
  import { FileTextIcon } from 'lucide-react';
6
- import markdownToTxt from 'markdown-to-txt';
7
6
  import { memo } from 'react';
8
7
 
9
8
  import Time from '@/app/[variants]/(main)/home/features/components/Time';
10
9
  import { RECENT_BLOCK_SIZE } from '@/app/[variants]/(main)/home/features/const';
11
10
  import { type FileListItem } from '@/types/files';
11
+ import markdownToTxt from '@/utils/markdownToTxt';
12
12
 
13
13
  // Helper to extract title from markdown content
14
14
  const extractTitle = (content: string): string | null => {
@@ -24,7 +24,7 @@ const getPreviewText = (item: FileListItem): string => {
24
24
  if (!item.content) return '';
25
25
 
26
26
  // Convert markdown to plain text
27
- let plainText = markdownToTxt(item.content);
27
+ let plainText = markdownToTxt(item.content.slice(0, 120));
28
28
 
29
29
  // Remove the title line if it exists
30
30
  const title = extractTitle(item.content);
@@ -20,7 +20,6 @@ import {
20
20
  Mic2,
21
21
  PaletteIcon,
22
22
  PieChart,
23
- ShieldCheck,
24
23
  Sparkles,
25
24
  UserCircle,
26
25
  } from 'lucide-react';
@@ -32,7 +31,7 @@ import { electronSyncSelectors } from '@/store/electron/selectors';
32
31
  import { SettingsTabs } from '@/store/global/initialState';
33
32
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
34
33
  import { useUserStore } from '@/store/user';
35
- import { authSelectors, userProfileSelectors } from '@/store/user/slices/auth/selectors';
34
+ import { userProfileSelectors } from '@/store/user/slices/auth/selectors';
36
35
 
37
36
  export enum SettingsGroupKey {
38
37
  AIConfig = 'ai-config',
@@ -61,8 +60,7 @@ export const useCategory = () => {
61
60
  const mobile = useServerConfigStore((s) => s.isMobile);
62
61
  const { enableSTT, hideDocs, showAiImage, showApiKeyManage } =
63
62
  useServerConfigStore(featureFlagsSelectors);
64
- const [isLoginWithClerk, avatar, username] = useUserStore((s) => [
65
- authSelectors.isLoginWithClerk(s),
63
+ const [avatar, username] = useUserStore((s) => [
66
64
  userProfileSelectors.userAvatar(s),
67
65
  userProfileSelectors.nickName(s),
68
66
  ]);
@@ -87,11 +85,6 @@ export const useCategory = () => {
87
85
  key: SettingsTabs.Profile,
88
86
  label: username ? username : tAuth('tab.profile'),
89
87
  },
90
- isLoginWithClerk && {
91
- icon: ShieldCheck,
92
- key: SettingsTabs.Security,
93
- label: tAuth('tab.security'),
94
- },
95
88
  {
96
89
  icon: ChartColumnBigIcon,
97
90
  key: SettingsTabs.Stats,
@@ -237,18 +230,7 @@ export const useCategory = () => {
237
230
  });
238
231
 
239
232
  return groups;
240
- }, [
241
- t,
242
- tAuth,
243
- enableSTT,
244
- hideDocs,
245
- mobile,
246
- showAiImage,
247
- showApiKeyManage,
248
- isLoginWithClerk,
249
- avatarUrl,
250
- username,
251
- ]);
233
+ }, [t, tAuth, enableSTT, hideDocs, mobile, showAiImage, showApiKeyManage, avatarUrl, username]);
252
234
 
253
235
  return categoryGroups;
254
236
  };
@@ -7,7 +7,6 @@ import { useCallback, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
 
9
9
  import { fetchErrorNotification } from '@/components/Error/fetchErrorNotification';
10
- import { enableAuth } from '@/envs/auth';
11
10
  import UserAvatar from '@/features/User/UserAvatar';
12
11
  import { useUserStore } from '@/store/user';
13
12
  import { authSelectors } from '@/store/user/selectors';
@@ -54,7 +53,7 @@ const AvatarRow = ({ mobile }: AvatarRowProps) => {
54
53
  [updateAvatar],
55
54
  );
56
55
 
57
- const canUpload = !enableAuth || isLogin;
56
+ const canUpload = isLogin;
58
57
 
59
58
  const avatarContent = canUpload ? (
60
59
  <Spin indicator={<LoadingOutlined spin />} spinning={uploading}>
@@ -1,30 +1,9 @@
1
1
  'use client';
2
2
 
3
- import { Skeleton } from '@lobehub/ui';
4
- import { useTranslation } from 'react-i18next';
5
3
  import { Navigate } from 'react-router-dom';
6
4
 
7
- import SettingHeader from '@/app/[variants]/(main)/settings/features/SettingHeader';
8
- import { enableClerk } from '@/envs/auth';
9
- import dynamic from '@/libs/next/dynamic';
10
-
11
- const ClerkProfile = dynamic(() => import('./features/ClerkProfile'), {
12
- loading: () => (
13
- <div style={{ flex: 1 }}>
14
- <Skeleton paragraph={{ rows: 8 }} title={false} />
15
- </div>
16
- ),
17
- });
18
-
19
5
  const Page = () => {
20
- const { t } = useTranslation('setting');
21
- if (!enableClerk) return <Navigate replace to="/settings" />;
22
- return (
23
- <>
24
- <SettingHeader title={t('tab.security')} />
25
- <ClerkProfile />
26
- </>
27
- );
6
+ return <Navigate replace to="/settings" />;
28
7
  };
29
8
 
30
9
  export default Page;
@@ -8,7 +8,7 @@ import { Loader2, MoreVerticalIcon, SquareArrowOutUpRight, Unplug } from 'lucide
8
8
  import { memo, useCallback, useEffect, useRef, useState } from 'react';
9
9
  import { useTranslation } from 'react-i18next';
10
10
 
11
- import IntegrationDetailModal from '@/features/IntegrationDetailModal';
11
+ import { createIntegrationDetailModal } from '@/features/IntegrationDetailModal';
12
12
  import { useToolStore } from '@/store/tool';
13
13
  import { type KlavisServer, KlavisServerStatus } from '@/store/tool/slices/klavisStore';
14
14
  import { useUserStore } from '@/store/user';
@@ -72,7 +72,6 @@ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
72
72
  const { modal } = App.useApp();
73
73
  const [isConnecting, setIsConnecting] = useState(false);
74
74
  const [isWaitingAuth, setIsWaitingAuth] = useState(false);
75
- const [detailOpen, setDetailOpen] = useState(false);
76
75
 
77
76
  const oauthWindowRef = useRef<Window | null>(null);
78
77
  const windowCheckIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
@@ -313,8 +312,7 @@ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
313
312
  const isConnected = server?.status === KlavisServerStatus.CONNECTED;
314
313
 
315
314
  return (
316
- <>
317
- <Flexbox
315
+ <Flexbox
318
316
  align="center"
319
317
  className={styles.container}
320
318
  gap={16}
@@ -324,7 +322,16 @@ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
324
322
  <Flexbox align="center" gap={16} horizontal style={{ flex: 1, overflow: 'hidden' }}>
325
323
  <div className={styles.icon}>{renderIcon()}</div>
326
324
  <Flexbox gap={4} style={{ overflow: 'hidden' }}>
327
- <span className={styles.title} onClick={() => setDetailOpen(true)}>
325
+ <span
326
+ className={styles.title}
327
+ onClick={() =>
328
+ createIntegrationDetailModal({
329
+ identifier: serverType.identifier,
330
+ serverName: serverType.serverName,
331
+ type: 'klavis',
332
+ })
333
+ }
334
+ >
328
335
  {serverType.label}
329
336
  </span>
330
337
  {!isConnected && renderStatus()}
@@ -335,15 +342,6 @@ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
335
342
  {renderAction()}
336
343
  </Flexbox>
337
344
  </Flexbox>
338
- <IntegrationDetailModal
339
- identifier={serverType.identifier}
340
- isConnecting={isConnecting || isWaitingAuth}
341
- onClose={() => setDetailOpen(false)}
342
- onConnect={handleConnect}
343
- open={detailOpen}
344
- type="klavis"
345
- />
346
- </>
347
345
  );
348
346
  });
349
347
 
@@ -8,7 +8,7 @@ import { Loader2, MoreVerticalIcon, SquareArrowOutUpRight, Unplug } from 'lucide
8
8
  import { memo, useCallback, useEffect, useRef, useState } from 'react';
9
9
  import { useTranslation } from 'react-i18next';
10
10
 
11
- import IntegrationDetailModal from '@/features/IntegrationDetailModal';
11
+ import { createIntegrationDetailModal } from '@/features/IntegrationDetailModal';
12
12
  import { useToolStore } from '@/store/tool';
13
13
  import {
14
14
  type LobehubSkillServer,
@@ -75,7 +75,6 @@ const LobehubSkillItem = memo<LobehubSkillItemProps>(({ provider, server }) => {
75
75
  const { modal } = App.useApp();
76
76
  const [isConnecting, setIsConnecting] = useState(false);
77
77
  const [isWaitingAuth, setIsWaitingAuth] = useState(false);
78
- const [detailOpen, setDetailOpen] = useState(false);
79
78
 
80
79
  const oauthWindowRef = useRef<Window | null>(null);
81
80
  const windowCheckIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
@@ -299,8 +298,7 @@ const LobehubSkillItem = memo<LobehubSkillItemProps>(({ provider, server }) => {
299
298
  const isConnected = server?.status === LobehubSkillStatus.CONNECTED;
300
299
 
301
300
  return (
302
- <>
303
- <Flexbox
301
+ <Flexbox
304
302
  align="center"
305
303
  className={styles.container}
306
304
  gap={16}
@@ -314,7 +312,12 @@ const LobehubSkillItem = memo<LobehubSkillItemProps>(({ provider, server }) => {
314
312
  <Flexbox gap={4} style={{ overflow: 'hidden' }}>
315
313
  <span
316
314
  className={`${styles.title} ${!isConnected ? styles.disconnectedTitle : ''}`}
317
- onClick={() => setDetailOpen(true)}
315
+ onClick={() =>
316
+ createIntegrationDetailModal({
317
+ identifier: provider.id,
318
+ type: 'lobehub',
319
+ })
320
+ }
318
321
  >
319
322
  {provider.label}
320
323
  </span>
@@ -326,15 +329,6 @@ const LobehubSkillItem = memo<LobehubSkillItemProps>(({ provider, server }) => {
326
329
  {renderAction()}
327
330
  </Flexbox>
328
331
  </Flexbox>
329
- <IntegrationDetailModal
330
- identifier={provider.id}
331
- isConnecting={isConnecting || isWaitingAuth}
332
- onClose={() => setDetailOpen(false)}
333
- onConnect={handleConnect}
334
- open={detailOpen}
335
- type="lobehub"
336
- />
337
- </>
338
332
  );
339
333
  });
340
334
 
@@ -2,30 +2,32 @@
2
2
 
3
3
  import { Button, Icon } from '@lobehub/ui';
4
4
  import { Store } from 'lucide-react';
5
- import { useState } from 'react';
5
+ import { useCallback } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
8
  import SettingHeader from '@/app/[variants]/(main)/settings/features/SettingHeader';
9
- import SkillStore from '@/features/SkillStore';
9
+ import { createSkillStoreModal } from '@/features/SkillStore';
10
10
 
11
11
  import SkillList from './features/SkillList';
12
12
 
13
13
  const Page = () => {
14
14
  const { t } = useTranslation('setting');
15
- const [open, setOpen] = useState(false);
15
+
16
+ const handleOpenStore = useCallback(() => {
17
+ createSkillStoreModal();
18
+ }, []);
16
19
 
17
20
  return (
18
21
  <>
19
22
  <SettingHeader
20
23
  extra={
21
- <Button icon={<Icon icon={Store} />} onClick={() => setOpen(true)}>
24
+ <Button icon={<Icon icon={Store} />} onClick={handleOpenStore}>
22
25
  {t('skillStore.button')}
23
26
  </Button>
24
27
  }
25
28
  title={t('tab.skill')}
26
29
  />
27
30
  <SkillList />
28
- <SkillStore open={open} setOpen={setOpen} />
29
31
  </>
30
32
  );
31
33
  };
@@ -31,48 +31,16 @@ vi.mock('@/const/version', () => ({
31
31
  isDesktop: false,
32
32
  }));
33
33
 
34
- // Use vi.hoisted to ensure variables exist before vi.mock factory executes
35
- const { enableAuth, enableClerk } = vi.hoisted(() => ({
36
- enableAuth: { value: true },
37
- enableClerk: { value: false },
38
- }));
39
-
40
- vi.mock('@/envs/auth', () => ({
41
- get enableAuth() {
42
- return enableAuth.value;
43
- },
44
- get enableClerk() {
45
- return enableClerk.value;
46
- },
47
- }));
48
-
49
34
  afterEach(() => {
50
- enableAuth.value = true;
51
- enableClerk.value = false;
52
35
  mockNavigate.mockReset();
53
36
  });
54
37
 
55
38
  describe('UserBanner', () => {
56
- it('should render UserInfo and DataStatistics when auth is disabled', () => {
57
- act(() => {
58
- useUserStore.setState({ isSignedIn: false });
59
- });
60
- enableAuth.value = false;
61
-
62
- render(<UserBanner />);
63
-
64
- expect(screen.getByText('Mocked UserInfo')).toBeInTheDocument();
65
- expect(screen.getByText('Mocked DataStatistics')).toBeInTheDocument();
66
- expect(screen.queryByText('Mocked UserLoginOrSignup')).not.toBeInTheDocument();
67
- });
68
-
69
- it('should render UserInfo and DataStatistics when user is logged in with auth enabled', () => {
39
+ it('should render UserInfo and DataStatistics when user is logged in', () => {
70
40
  act(() => {
71
41
  useUserStore.setState({ isSignedIn: true });
72
42
  });
73
43
 
74
- enableClerk.value = true;
75
-
76
44
  render(<UserBanner />);
77
45
 
78
46
  expect(screen.getByText('Mocked UserInfo')).toBeInTheDocument();
@@ -80,11 +48,10 @@ describe('UserBanner', () => {
80
48
  expect(screen.queryByText('Mocked UserLoginOrSignup')).not.toBeInTheDocument();
81
49
  });
82
50
 
83
- it('should render UserLoginOrSignup when user is not logged in with auth enabled', () => {
51
+ it('should render UserLoginOrSignup when user is not logged in', () => {
84
52
  act(() => {
85
53
  useUserStore.setState({ isSignedIn: false });
86
54
  });
87
- enableClerk.value = true;
88
55
 
89
56
  render(<UserBanner />);
90
57
 
@@ -22,21 +22,6 @@ vi.mock('react-i18next', () => ({
22
22
  })),
23
23
  }));
24
24
 
25
- // Use vi.hoisted to ensure variables exist before vi.mock factory executes
26
- const { enableAuth, enableClerk } = vi.hoisted(() => ({
27
- enableAuth: { value: true },
28
- enableClerk: { value: true },
29
- }));
30
-
31
- vi.mock('@/envs/auth', () => ({
32
- get enableAuth() {
33
- return enableAuth.value;
34
- },
35
- get enableClerk() {
36
- return enableClerk.value;
37
- },
38
- }));
39
-
40
25
  // Mock version constants
41
26
  vi.mock('@/const/version', async (importOriginal) => {
42
27
  const actual = await importOriginal<typeof import('@/const/version')>();
@@ -47,8 +32,6 @@ vi.mock('@/const/version', async (importOriginal) => {
47
32
  });
48
33
 
49
34
  afterEach(() => {
50
- enableAuth.value = true;
51
- enableClerk.value = true;
52
35
  mockNavigate.mockReset();
53
36
  });
54
37
 
@@ -57,8 +40,6 @@ describe('useCategory', () => {
57
40
  act(() => {
58
41
  useUserStore.setState({ isSignedIn: true });
59
42
  });
60
- enableAuth.value = true;
61
- enableClerk.value = false;
62
43
 
63
44
  const mockOpenChangelogModal = vi.fn();
64
45
  const { result } = renderHook(() => useCategory(mockOpenChangelogModal), { wrapper });
@@ -77,7 +58,6 @@ describe('useCategory', () => {
77
58
  act(() => {
78
59
  useUserStore.setState({ isSignedIn: false });
79
60
  });
80
- enableAuth.value = true;
81
61
 
82
62
  const mockOpenChangelogModal = vi.fn();
83
63
  const { result } = renderHook(() => useCategory(mockOpenChangelogModal), { wrapper });
@@ -4,7 +4,6 @@ import { Flexbox } from '@lobehub/ui';
4
4
  import { memo } from 'react';
5
5
  import { Link } from 'react-router-dom';
6
6
 
7
- import { enableAuth } from '@/envs/auth';
8
7
  import DataStatistics from '@/features/User/DataStatistics';
9
8
  import UserInfo from '@/features/User/UserInfo';
10
9
  import UserLoginOrSignup from '@/features/User/UserLoginOrSignup/Community';
@@ -17,7 +16,7 @@ const UserBanner = memo(() => {
17
16
 
18
17
  return (
19
18
  <Flexbox gap={12} paddingBlock={8}>
20
- {!enableAuth || (enableAuth && isLoginWithAuth) ? (
19
+ {isLoginWithAuth ? (
21
20
  <>
22
21
  <Link style={{ color: 'inherit' }} to="/settings/profile">
23
22
  <UserInfo />