@lobehub/lobehub 2.0.0-next.344 → 2.0.0-next.346

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 (185) 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/CLAUDE.md +4 -2
  8. package/Dockerfile +3 -13
  9. package/README.md +3 -5
  10. package/README.zh-CN.md +3 -5
  11. package/changelog/v1.json +20 -0
  12. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx +11 -42
  13. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx +10 -41
  14. package/e2e/src/support/webServer.ts +2 -0
  15. package/locales/ar/error.json +0 -4
  16. package/locales/bg-BG/error.json +0 -4
  17. package/locales/de-DE/error.json +0 -4
  18. package/locales/en-US/error.json +0 -4
  19. package/locales/es-ES/error.json +0 -4
  20. package/locales/fa-IR/error.json +0 -4
  21. package/locales/fr-FR/error.json +0 -4
  22. package/locales/it-IT/error.json +0 -4
  23. package/locales/ja-JP/error.json +0 -4
  24. package/locales/ko-KR/error.json +0 -4
  25. package/locales/nl-NL/error.json +0 -4
  26. package/locales/pl-PL/error.json +0 -4
  27. package/locales/pt-BR/error.json +0 -4
  28. package/locales/ru-RU/error.json +0 -4
  29. package/locales/tr-TR/error.json +0 -4
  30. package/locales/vi-VN/error.json +0 -4
  31. package/locales/zh-CN/error.json +0 -4
  32. package/locales/zh-TW/error.json +0 -4
  33. package/package.json +12 -12
  34. package/packages/builtin-agents/package.json +2 -0
  35. package/packages/builtin-agents/src/agents/agent-builder/index.ts +4 -2
  36. package/packages/builtin-agents/src/agents/group-agent-builder/index.ts +4 -2
  37. package/packages/builtin-agents/src/agents/page-agent/index.ts +5 -2
  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/file-loaders/package.json +1 -1
  44. package/packages/file-loaders/src/loadFile.ts +10 -15
  45. package/packages/file-loaders/src/loaders/index.ts +68 -19
  46. package/packages/file-loaders/src/loaders/pdf/__snapshots__/index.test.ts.snap +1 -1
  47. package/packages/file-loaders/test/__snapshots__/loaders.test.ts.snap +1 -1
  48. package/packages/model-bank/src/modelProviders/comfyui.ts +0 -1
  49. package/packages/model-bank/src/modelProviders/fal.ts +0 -1
  50. package/packages/types/src/fetch.ts +1 -2
  51. package/packages/utils/src/server/__tests__/auth.test.ts +0 -47
  52. package/packages/utils/src/server/auth.ts +1 -9
  53. package/pnpm-workspace.yaml +1 -0
  54. package/scripts/_shared/checkDeprecatedClerkEnv.js +42 -0
  55. package/scripts/changelogWorkflow/buildStaticChangelog.ts +2 -1
  56. package/scripts/clerk-to-betterauth/_internal/types.ts +53 -20
  57. package/scripts/clerk-to-betterauth/export-clerk-users-with-api.ts +43 -36
  58. package/scripts/countEnWord.ts +1 -1
  59. package/scripts/electronWorkflow/modifiers/appCode.mts +2 -131
  60. package/scripts/i18nWorkflow/protectedPatterns.ts +1 -2
  61. package/scripts/prebuild.mts +10 -8
  62. package/scripts/serverLauncher/startServer.js +23 -5
  63. package/src/app/(backend)/middleware/auth/index.test.ts +8 -4
  64. package/src/app/(backend)/middleware/auth/index.ts +0 -15
  65. package/src/app/(backend)/middleware/auth/utils.test.ts +0 -28
  66. package/src/app/(backend)/middleware/auth/utils.ts +2 -17
  67. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +3 -51
  68. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -4
  69. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +7 -6
  70. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -16
  71. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/index.tsx +1 -1
  72. package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +13 -13
  73. package/src/app/[variants]/(main)/home/features/RecentPage/Item.tsx +2 -2
  74. package/src/app/[variants]/(main)/resource/features/store/action.ts +2 -2
  75. package/src/app/[variants]/(main)/resource/features/store/initialState.ts +2 -2
  76. package/src/app/[variants]/(main)/resource/store/action.ts +2 -2
  77. package/src/app/[variants]/(main)/resource/store/initialState.ts +2 -2
  78. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
  79. package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -2
  80. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -22
  81. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +12 -14
  82. package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +8 -14
  83. package/src/app/[variants]/(main)/settings/skill/index.tsx +7 -5
  84. package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +2 -35
  85. package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -20
  86. package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -2
  87. package/src/app/[variants]/(mobile)/me/profile/features/Category.tsx +3 -13
  88. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +2 -3
  89. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
  90. package/src/app/[variants]/share/t/[id]/index.tsx +1 -1
  91. package/src/app/robots.tsx +1 -1
  92. package/src/envs/auth.ts +2 -27
  93. package/src/envs/llm.ts +2 -2
  94. package/src/features/AgentSetting/AgentPlugin/index.tsx +9 -12
  95. package/src/features/ChatInput/ActionBar/Tools/index.tsx +7 -5
  96. package/src/features/ChatMiniMap/utils.ts +1 -1
  97. package/src/features/CommandMenu/SearchResults.tsx +1 -1
  98. package/src/features/Conversation/ChatList/components/AutoScroll/DebugInspector.tsx +166 -0
  99. package/src/features/Conversation/ChatList/components/AutoScroll/index.tsx +86 -0
  100. package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +11 -17
  101. package/src/features/Conversation/Messages/AgentCouncil/components/AutoScrollShadow.tsx +25 -14
  102. package/src/features/Conversation/Messages/AgentCouncil/components/CouncilMember.tsx +1 -1
  103. package/src/features/FileViewer/Renderer/PDF/index.tsx +5 -8
  104. package/src/features/IntegrationDetailModal/IntegrationDetailContent.tsx +305 -0
  105. package/src/features/IntegrationDetailModal/index.tsx +21 -283
  106. package/src/features/MCPPluginDetail/Deployment/index.tsx +1 -1
  107. package/src/features/MCPPluginDetail/Schema/Prompts.tsx +1 -1
  108. package/src/features/MCPPluginDetail/Schema/Tools.tsx +1 -1
  109. package/src/features/ProfileEditor/AgentTool.tsx +14 -20
  110. package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +0 -8
  111. package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/NoteFileItem.tsx +1 -1
  112. package/src/features/ResourceManager/index.tsx +1 -1
  113. package/src/features/ShareModal/SharePdf/PdfPreview.tsx +4 -4
  114. package/src/features/SkillStore/LobeHubList/index.tsx +50 -87
  115. package/src/features/SkillStore/Search/index.tsx +1 -1
  116. package/src/features/SkillStore/{Content.tsx → SkillStoreContent.tsx} +3 -8
  117. package/src/features/SkillStore/index.tsx +15 -33
  118. package/src/features/User/UserPanel/PanelContent.tsx +0 -8
  119. package/src/features/User/__tests__/PanelContent.test.tsx +1 -35
  120. package/src/features/User/__tests__/UserAvatar.test.tsx +30 -57
  121. package/src/features/User/__tests__/useMenu.test.tsx +2 -43
  122. package/src/layout/AuthProvider/index.tsx +0 -5
  123. package/src/libs/next/config/define-config.ts +20 -15
  124. package/src/libs/next/proxy/createRouteMatcher.test.ts +121 -0
  125. package/src/libs/next/proxy/createRouteMatcher.ts +18 -0
  126. package/src/libs/next/proxy/define-config.ts +4 -53
  127. package/src/libs/next-auth/adapter/index.ts +1 -2
  128. package/src/libs/oidc-provider/provider.test.ts +5 -316
  129. package/src/libs/pdfjs/pdf.worker.ts +1 -0
  130. package/src/libs/pdfjs/worker.ts +12 -0
  131. package/src/libs/trpc/lambda/context.test.ts +0 -13
  132. package/src/libs/trpc/lambda/context.ts +3 -22
  133. package/src/libs/trpc/middleware/userAuth.ts +2 -4
  134. package/src/libs/trusted-client/getSessionUser.ts +2 -17
  135. package/src/locales/default/error.ts +0 -6
  136. package/src/locales/default/index.ts +0 -2
  137. package/src/proxy.ts +0 -1
  138. package/src/server/routers/lambda/__tests__/user.test.ts +0 -71
  139. package/src/server/routers/lambda/user.ts +6 -63
  140. package/src/server/services/changelog/index.test.ts +3 -2
  141. package/src/server/services/changelog/index.ts +1 -1
  142. package/src/server/services/user/index.ts +0 -83
  143. package/src/services/chat/index.ts +1 -2
  144. package/src/services/chat/mecha/agentConfigResolver.test.ts +43 -0
  145. package/src/services/chat/mecha/agentConfigResolver.ts +3 -1
  146. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +58 -14
  147. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +10 -2
  148. package/src/store/user/slices/auth/action.test.ts +1 -81
  149. package/src/store/user/slices/auth/action.ts +3 -28
  150. package/src/store/user/slices/auth/initialState.ts +1 -18
  151. package/src/store/user/slices/auth/selectors.test.ts +2 -127
  152. package/src/store/user/slices/auth/selectors.ts +1 -21
  153. package/src/utils/errorResponse.ts +1 -4
  154. package/src/utils/markdownToTxt.ts +20 -0
  155. package/locales/ar/clerk.json +0 -545
  156. package/locales/bg-BG/clerk.json +0 -545
  157. package/locales/de-DE/clerk.json +0 -545
  158. package/locales/en-US/clerk.json +0 -545
  159. package/locales/es-ES/clerk.json +0 -545
  160. package/locales/fa-IR/clerk.json +0 -545
  161. package/locales/fr-FR/clerk.json +0 -545
  162. package/locales/it-IT/clerk.json +0 -545
  163. package/locales/ja-JP/clerk.json +0 -545
  164. package/locales/ko-KR/clerk.json +0 -545
  165. package/locales/nl-NL/clerk.json +0 -545
  166. package/locales/pl-PL/clerk.json +0 -545
  167. package/locales/pt-BR/clerk.json +0 -545
  168. package/locales/ru-RU/clerk.json +0 -545
  169. package/locales/tr-TR/clerk.json +0 -545
  170. package/locales/vi-VN/clerk.json +0 -545
  171. package/locales/zh-CN/clerk.json +0 -545
  172. package/locales/zh-TW/clerk.json +0 -545
  173. package/src/app/(backend)/api/webhooks/clerk/__tests__/fixtures/createUser.json +0 -73
  174. package/src/app/(backend)/api/webhooks/clerk/route.ts +0 -95
  175. package/src/app/(backend)/api/webhooks/clerk/validateRequest.ts +0 -22
  176. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +0 -27
  177. package/src/app/[variants]/(main)/settings/security/features/ClerkProfile.tsx +0 -67
  178. package/src/features/Conversation/ChatList/components/AutoScroll.tsx +0 -25
  179. package/src/layout/AuthProvider/Clerk/UserUpdater.tsx +0 -40
  180. package/src/layout/AuthProvider/Clerk/index.tsx +0 -54
  181. package/src/layout/AuthProvider/Clerk/useAppearance.ts +0 -133
  182. package/src/libs/clerk-auth/index.test.ts +0 -216
  183. package/src/libs/clerk-auth/index.ts +0 -80
  184. package/src/locales/default/clerk.ts +0 -677
  185. package/src/server/services/user/index.test.ts +0 -220
@@ -3,10 +3,9 @@
3
3
  import { KLAVIS_SERVER_TYPES, LOBEHUB_SKILL_PROVIDERS } from '@lobechat/const';
4
4
  import { createStaticStyles } from 'antd-style';
5
5
  import isEqual from 'fast-deep-equal';
6
- import type { Klavis } from 'klavis';
7
- import { memo, useMemo, useState } from 'react';
6
+ import { memo, useCallback, useMemo } from 'react';
8
7
 
9
- import IntegrationDetailModal from '@/features/IntegrationDetailModal';
8
+ import { createIntegrationDetailModal } from '@/features/IntegrationDetailModal';
10
9
  import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
11
10
  import { useToolStore } from '@/store/tool';
12
11
  import { klavisStoreSelectors, lobehubSkillStoreSelectors } from '@/store/tool/selectors';
@@ -15,7 +14,6 @@ import { LobehubSkillStatus } from '@/store/tool/slices/lobehubSkillStore/types'
15
14
 
16
15
  import Empty from '../Empty';
17
16
  import Item from './Item';
18
- import { useSkillConnect } from './useSkillConnect';
19
17
 
20
18
  const styles = createStaticStyles(({ css }) => ({
21
19
  grid: css`
@@ -36,40 +34,7 @@ interface LobeHubListProps {
36
34
  keywords: string;
37
35
  }
38
36
 
39
- interface DetailState {
40
- identifier: string;
41
- serverName?: Klavis.McpServerName;
42
- type: 'klavis' | 'lobehub';
43
- }
44
-
45
- interface DetailModalWithConnectProps {
46
- detailState: DetailState;
47
- onClose: () => void;
48
- }
49
-
50
- const DetailModalWithConnect = memo<DetailModalWithConnectProps>(({ detailState, onClose }) => {
51
- const { handleConnect, isConnecting } = useSkillConnect({
52
- identifier: detailState.identifier,
53
- serverName: detailState.serverName,
54
- type: detailState.type,
55
- });
56
-
57
- return (
58
- <IntegrationDetailModal
59
- identifier={detailState.identifier}
60
- isConnecting={isConnecting}
61
- onClose={onClose}
62
- onConnect={handleConnect}
63
- open
64
- type={detailState.type}
65
- />
66
- );
67
- });
68
-
69
- DetailModalWithConnect.displayName = 'DetailModalWithConnect';
70
-
71
37
  export const LobeHubList = memo<LobeHubListProps>(({ keywords }) => {
72
- const [detailState, setDetailState] = useState<DetailState | null>(null);
73
38
 
74
39
  const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
75
40
  const isKlavisEnabled = useServerConfigStore(serverConfigSelectors.enableKlavis);
@@ -84,13 +49,19 @@ export const LobeHubList = memo<LobeHubListProps>(({ keywords }) => {
84
49
  useFetchLobehubSkillConnections(isLobehubSkillEnabled);
85
50
  useFetchUserKlavisServers(isKlavisEnabled);
86
51
 
87
- const getLobehubSkillServerByProvider = (providerId: string) => {
88
- return allLobehubSkillServers.find((server) => server.identifier === providerId);
89
- };
52
+ const getLobehubSkillServerByProvider = useCallback(
53
+ (providerId: string) => {
54
+ return allLobehubSkillServers.find((server) => server.identifier === providerId);
55
+ },
56
+ [allLobehubSkillServers],
57
+ );
90
58
 
91
- const getKlavisServerByIdentifier = (identifier: string) => {
92
- return allKlavisServers.find((server) => server.identifier === identifier);
93
- };
59
+ const getKlavisServerByIdentifier = useCallback(
60
+ (identifier: string) => {
61
+ return allKlavisServers.find((server) => server.identifier === identifier);
62
+ },
63
+ [allKlavisServers],
64
+ );
94
65
 
95
66
  const filteredItems = useMemo(() => {
96
67
  const items: Array<
@@ -127,57 +98,49 @@ export const LobeHubList = memo<LobeHubListProps>(({ keywords }) => {
127
98
  if (filteredItems.length === 0) return <Empty search={hasSearchKeywords} />;
128
99
 
129
100
  return (
130
- <>
131
- <div className={styles.grid}>
132
- {filteredItems.map((item) => {
133
- if (item.type === 'lobehub') {
134
- const server = getLobehubSkillServerByProvider(item.provider.id);
135
- const isConnected = server?.status === LobehubSkillStatus.CONNECTED;
136
- return (
137
- <Item
138
- description={item.provider.description}
139
- icon={item.provider.icon}
140
- identifier={item.provider.id}
141
- isConnected={isConnected}
142
- key={item.provider.id}
143
- label={item.provider.label}
144
- onOpenDetail={() =>
145
- setDetailState({ identifier: item.provider.id, type: 'lobehub' })
146
- }
147
- type="lobehub"
148
- />
149
- );
150
- }
151
- const server = getKlavisServerByIdentifier(item.serverType.identifier);
152
- const isConnected = server?.status === KlavisServerStatus.CONNECTED;
101
+ <div className={styles.grid}>
102
+ {filteredItems.map((item) => {
103
+ if (item.type === 'lobehub') {
104
+ const server = getLobehubSkillServerByProvider(item.provider.id);
105
+ const isConnected = server?.status === LobehubSkillStatus.CONNECTED;
153
106
  return (
154
107
  <Item
155
- description={item.serverType.description}
156
- icon={item.serverType.icon}
157
- identifier={item.serverType.identifier}
108
+ description={item.provider.description}
109
+ icon={item.provider.icon}
110
+ identifier={item.provider.id}
158
111
  isConnected={isConnected}
159
- key={item.serverType.identifier}
160
- label={item.serverType.label}
112
+ key={item.provider.id}
113
+ label={item.provider.label}
161
114
  onOpenDetail={() =>
162
- setDetailState({
163
- identifier: item.serverType.identifier,
164
- serverName: item.serverType.serverName,
165
- type: 'klavis',
166
- })
115
+ createIntegrationDetailModal({ identifier: item.provider.id, type: 'lobehub' })
167
116
  }
168
- serverName={item.serverType.serverName}
169
- type="klavis"
117
+ type="lobehub"
170
118
  />
171
119
  );
172
- })}
173
- </div>
174
- {detailState && (
175
- <DetailModalWithConnect
176
- detailState={detailState}
177
- onClose={() => setDetailState(null)}
178
- />
179
- )}
180
- </>
120
+ }
121
+ const server = getKlavisServerByIdentifier(item.serverType.identifier);
122
+ const isConnected = server?.status === KlavisServerStatus.CONNECTED;
123
+ return (
124
+ <Item
125
+ description={item.serverType.description}
126
+ icon={item.serverType.icon}
127
+ identifier={item.serverType.identifier}
128
+ isConnected={isConnected}
129
+ key={item.serverType.identifier}
130
+ label={item.serverType.label}
131
+ onOpenDetail={() =>
132
+ createIntegrationDetailModal({
133
+ identifier: item.serverType.identifier,
134
+ serverName: item.serverType.serverName,
135
+ type: 'klavis',
136
+ })
137
+ }
138
+ serverName={item.serverType.serverName}
139
+ type="klavis"
140
+ />
141
+ );
142
+ })}
143
+ </div>
181
144
  );
182
145
  });
183
146
 
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import { useToolStore } from '@/store/tool';
8
8
 
9
- import { SkillStoreTab } from '../Content';
9
+ import { SkillStoreTab } from '../SkillStoreContent';
10
10
 
11
11
  interface SearchProps {
12
12
  activeTab: SkillStoreTab;
@@ -2,11 +2,10 @@
2
2
 
3
3
  import { Flexbox, Segmented } from '@lobehub/ui';
4
4
  import { type SegmentedOptions } from 'antd/es/segmented';
5
- import { memo, useState } from 'react';
5
+ import { useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
8
  import AddSkillButton from './AddSkillButton';
9
-
10
9
  import CommunityList from './CommunityList';
11
10
  import LobeHubList from './LobeHubList';
12
11
  import Search from './Search';
@@ -16,7 +15,7 @@ export enum SkillStoreTab {
16
15
  LobeHub = 'lobehub',
17
16
  }
18
17
 
19
- export const Content = memo(() => {
18
+ export const SkillStoreContent = () => {
20
19
  const { t } = useTranslation('setting');
21
20
  const [activeTab, setActiveTab] = useState<SkillStoreTab>(SkillStoreTab.LobeHub);
22
21
  const [lobehubKeywords, setLobehubKeywords] = useState('');
@@ -52,8 +51,4 @@ export const Content = memo(() => {
52
51
  </Flexbox>
53
52
  </Flexbox>
54
53
  );
55
- });
56
-
57
- Content.displayName = 'SkillStoreContent';
58
-
59
- export default Content;
54
+ };
@@ -1,37 +1,19 @@
1
1
  'use client';
2
2
 
3
- import { Modal } from '@lobehub/ui';
4
- import { memo } from 'react';
5
- import { useTranslation } from 'react-i18next';
3
+ import { createModal } from '@lobehub/ui';
4
+ import { t } from 'i18next';
6
5
 
7
- import Content from './Content';
6
+ import { SkillStoreContent } from './SkillStoreContent';
8
7
 
9
- interface SkillStoreProps {
10
- open: boolean;
11
- setOpen: (open: boolean) => void;
12
- }
13
-
14
- export const SkillStore = memo<SkillStoreProps>(({ open, setOpen }) => {
15
- const { t } = useTranslation('setting');
16
-
17
- return (
18
- <Modal
19
- allowFullscreen
20
- destroyOnClose={false}
21
- footer={null}
22
- onCancel={() => setOpen(false)}
23
- open={open}
24
- styles={{
25
- body: { overflow: 'hidden', padding: 0 },
26
- }}
27
- title={t('skillStore.title')}
28
- width={'min(80%, 800px)'}
29
- >
30
- <Content />
31
- </Modal>
32
- );
33
- });
34
-
35
- SkillStore.displayName = 'SkillStore';
36
-
37
- export default SkillStore;
8
+ export const createSkillStoreModal = () =>
9
+ createModal({
10
+ allowFullscreen: true,
11
+ children: <SkillStoreContent />,
12
+ destroyOnHidden: false,
13
+ footer: null,
14
+ styles: {
15
+ body: { overflow: 'hidden', padding: 0 },
16
+ },
17
+ title: t('skillStore.title', { ns: 'setting' }),
18
+ width: 'min(80%, 800px)',
19
+ });
@@ -10,8 +10,6 @@ import BusinessPanelContent from '@/business/client/features/User/BusinessPanelC
10
10
  import BrandWatermark from '@/components/BrandWatermark';
11
11
  import Menu from '@/components/Menu';
12
12
  import { isDesktop } from '@/const/version';
13
- import { enableBetterAuth, enableNextAuth } from '@/envs/auth';
14
- import { useRouter } from '@/libs/next/navigation';
15
13
  import { useUserStore } from '@/store/user';
16
14
  import { authSelectors } from '@/store/user/selectors';
17
15
 
@@ -22,8 +20,6 @@ import LangButton from './LangButton';
22
20
  import { useMenu } from './useMenu';
23
21
 
24
22
  const PanelContent = memo<{ closePopover: () => void }>(({ closePopover }) => {
25
- const router = useRouter();
26
-
27
23
  const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
28
24
  const [openSignIn, signOut] = useUserStore((s) => [s.openLogin, s.logout]);
29
25
  const { mainItems, logoutItems } = useMenu();
@@ -52,10 +48,6 @@ const PanelContent = memo<{ closePopover: () => void }>(({ closePopover }) => {
52
48
 
53
49
  signOut();
54
50
  closePopover();
55
- // NextAuth and Better Auth handle redirect in their own signOut methods
56
- if (enableNextAuth || enableBetterAuth) return;
57
- // Clerk uses /login page
58
- router.push('/login');
59
51
  };
60
52
 
61
53
  return (
@@ -68,19 +68,11 @@ vi.mock('@/const/version', () => ({
68
68
  }));
69
69
 
70
70
  // Use vi.hoisted to ensure variables exist before vi.mock factory executes
71
- const { enableAuth, enableClerk, enableNextAuth } = vi.hoisted(() => ({
72
- enableAuth: { value: true },
73
- enableClerk: { value: false },
71
+ const { enableNextAuth } = vi.hoisted(() => ({
74
72
  enableNextAuth: { value: false },
75
73
  }));
76
74
 
77
75
  vi.mock('@/envs/auth', () => ({
78
- get enableAuth() {
79
- return enableAuth.value;
80
- },
81
- get enableClerk() {
82
- return enableClerk.value;
83
- },
84
76
  get enableNextAuth() {
85
77
  return enableNextAuth.value;
86
78
  },
@@ -140,32 +132,6 @@ describe('PanelContent', () => {
140
132
  });
141
133
  });
142
134
 
143
- describe('disable auth', () => {
144
- it('should render UserInfo', () => {
145
- act(() => {
146
- useUserStore.setState({ isSignedIn: true });
147
- });
148
-
149
- renderWithRouter(<PanelContent closePopover={closePopover} />);
150
-
151
- expect(screen.getByText('Mocked UserInfo')).toBeInTheDocument();
152
- expect(screen.getByText('Mocked DataStatistics')).toBeInTheDocument();
153
- expect(screen.queryByText('Mocked SignInBlock')).not.toBeInTheDocument();
154
- });
155
-
156
- it('should render BrandWatermark when disable auth', () => {
157
- enableAuth.value = false;
158
-
159
- act(() => {
160
- useUserStore.setState({ isSignedIn: false });
161
- });
162
-
163
- renderWithRouter(<PanelContent closePopover={closePopover} />);
164
-
165
- expect(screen.getByText('Mocked BrandWatermark')).toBeInTheDocument();
166
- });
167
- });
168
-
169
135
  it('should render Menu with main items', () => {
170
136
  renderWithRouter(<PanelContent closePopover={closePopover} />);
171
137
 
@@ -10,87 +10,60 @@ import UserAvatar from '../UserAvatar';
10
10
  vi.mock('zustand/traditional');
11
11
 
12
12
  // Use vi.hoisted to ensure variables exist before vi.mock factory executes
13
- const { enableAuth, enableClerk, enableNextAuth } = vi.hoisted(() => ({
14
- enableAuth: { value: true },
15
- enableClerk: { value: false },
13
+ const { enableNextAuth } = vi.hoisted(() => ({
16
14
  enableNextAuth: { value: false },
17
15
  }));
18
16
 
19
17
  vi.mock('@/envs/auth', () => ({
20
- get enableAuth() {
21
- return enableAuth.value;
22
- },
23
- get enableClerk() {
24
- return enableClerk.value;
25
- },
26
18
  get enableNextAuth() {
27
19
  return enableNextAuth.value;
28
20
  },
29
21
  }));
30
22
 
31
23
  afterEach(() => {
32
- enableAuth.value = true;
33
- enableClerk.value = false;
34
24
  enableNextAuth.value = false;
35
25
  });
36
26
 
37
27
  describe('UserAvatar', () => {
38
- describe('enable Auth', () => {
39
- it('should show the username and avatar are displayed when the user is logged in', async () => {
40
- const mockAvatar = 'https://example.com/avatar.png';
41
- const mockUsername = 'teeeeeestuser';
42
-
43
- act(() => {
44
- useUserStore.setState({
45
- enableAuth: () => true,
46
- isSignedIn: true,
47
- user: { avatar: mockAvatar, id: 'abc', username: mockUsername },
48
- });
28
+ it('should show the username and avatar are displayed when the user is logged in', async () => {
29
+ const mockAvatar = 'https://example.com/avatar.png';
30
+ const mockUsername = 'teeeeeestuser';
31
+
32
+ act(() => {
33
+ useUserStore.setState({
34
+ isSignedIn: true,
35
+ user: { avatar: mockAvatar, id: 'abc', username: mockUsername },
49
36
  });
50
-
51
- render(<UserAvatar />);
52
-
53
- expect(screen.getByAltText(mockUsername)).toBeInTheDocument();
54
- expect(screen.getByAltText(mockUsername)).toHaveAttribute('src', mockAvatar);
55
37
  });
56
38
 
57
- it('should show default avatar when the user is logged in but have no custom avatar', () => {
58
- const mockUsername = 'testuser';
39
+ render(<UserAvatar />);
59
40
 
60
- act(() => {
61
- useUserStore.setState({
62
- enableAuth: () => true,
63
- isSignedIn: true,
64
- user: { id: 'bbb', username: mockUsername },
65
- });
66
- });
41
+ expect(screen.getByAltText(mockUsername)).toBeInTheDocument();
42
+ expect(screen.getByAltText(mockUsername)).toHaveAttribute('src', mockAvatar);
43
+ });
67
44
 
68
- render(<UserAvatar />);
69
- // When user has no avatar url, <Avatar /> falls back to initials rendering (not an <img />)
70
- expect(screen.getByText('TE')).toBeInTheDocument();
71
- });
45
+ it('should show default avatar when the user is logged in but have no custom avatar', () => {
46
+ const mockUsername = 'testuser';
72
47
 
73
- it('should show LobeChat and default avatar when the user is not logged in and enable auth', () => {
74
- act(() => {
75
- useUserStore.setState({ enableAuth: () => true, isSignedIn: false, user: undefined });
48
+ act(() => {
49
+ useUserStore.setState({
50
+ isSignedIn: true,
51
+ user: { id: 'bbb', username: mockUsername },
76
52
  });
77
-
78
- render(<UserAvatar />);
79
- expect(screen.getByAltText(BRANDING_NAME)).toBeInTheDocument();
80
- expect(screen.getByAltText(BRANDING_NAME)).toHaveAttribute('src', DEFAULT_USER_AVATAR_URL);
81
53
  });
82
- });
83
54
 
84
- describe('disable Auth', () => {
85
- it('should show LobeChat and default avatar when the user is not logged in and disabled auth', () => {
86
- enableAuth.value = false;
87
- act(() => {
88
- useUserStore.setState({ enableAuth: () => false, isSignedIn: false, user: undefined });
89
- });
55
+ render(<UserAvatar />);
56
+ // When user has no avatar url, <Avatar /> falls back to initials rendering (not an <img />)
57
+ expect(screen.getByText('TE')).toBeInTheDocument();
58
+ });
90
59
 
91
- render(<UserAvatar />);
92
- expect(screen.getByAltText(BRANDING_NAME)).toBeInTheDocument();
93
- expect(screen.getByAltText(BRANDING_NAME)).toHaveAttribute('src', DEFAULT_USER_AVATAR_URL);
60
+ it('should show LobeChat and default avatar when the user is not logged in', () => {
61
+ act(() => {
62
+ useUserStore.setState({ isSignedIn: false, user: undefined });
94
63
  });
64
+
65
+ render(<UserAvatar />);
66
+ expect(screen.getByAltText(BRANDING_NAME)).toBeInTheDocument();
67
+ expect(screen.getByAltText(BRANDING_NAME)).toHaveAttribute('src', DEFAULT_USER_AVATAR_URL);
95
68
  });
96
69
  });
@@ -48,33 +48,11 @@ vi.mock('./useNewVersion', () => ({
48
48
  useNewVersion: vi.fn(() => false),
49
49
  }));
50
50
 
51
- // Use vi.hoisted to ensure variables exist before vi.mock factory executes
52
- const { enableAuth, enableClerk } = vi.hoisted(() => ({
53
- enableAuth: { value: true },
54
- enableClerk: { value: true },
55
- }));
56
-
57
- vi.mock('@/envs/auth', () => ({
58
- get enableAuth() {
59
- return enableAuth.value;
60
- },
61
- get enableClerk() {
62
- return enableClerk.value;
63
- },
64
- }));
65
-
66
- afterEach(() => {
67
- enableAuth.value = true;
68
- enableClerk.value = true;
69
- });
70
-
71
51
  describe('useMenu', () => {
72
52
  it('should provide correct menu items when user is logged in with auth', () => {
73
53
  act(() => {
74
- useUserStore.setState({ isSignedIn: true, enableAuth: () => true });
54
+ useUserStore.setState({ isSignedIn: true });
75
55
  });
76
- enableAuth.value = true;
77
- enableClerk.value = false;
78
56
 
79
57
  const { result } = renderHook(() => useMenu(), { wrapper });
80
58
 
@@ -88,29 +66,10 @@ describe('useMenu', () => {
88
66
  });
89
67
  });
90
68
 
91
- it('should provide correct menu items when user is logged in without auth', () => {
92
- act(() => {
93
- useUserStore.setState({ isSignedIn: false, enableAuth: () => false });
94
- });
95
- enableAuth.value = false;
96
-
97
- const { result } = renderHook(() => useMenu(), { wrapper });
98
-
99
- act(() => {
100
- const { mainItems, logoutItems } = result.current;
101
- // When not logged in (isLogin = false), these items should not be shown
102
- // isLogin checks isSignedIn, so with isSignedIn: false, isLogin should be false
103
- expect(mainItems?.some((item) => item?.key === 'setting')).toBe(false);
104
- expect(mainItems?.some((item) => item?.key === 'import')).toBe(false);
105
- expect(logoutItems.some((item) => item?.key === 'logout')).toBe(false);
106
- });
107
- });
108
-
109
69
  it('should provide correct menu items when user is not logged in', () => {
110
70
  act(() => {
111
- useUserStore.setState({ isSignedIn: false, enableAuth: () => true });
71
+ useUserStore.setState({ isSignedIn: false });
112
72
  });
113
- enableAuth.value = true;
114
73
 
115
74
  const { result } = renderHook(() => useMenu(), { wrapper });
116
75
 
@@ -4,7 +4,6 @@ import { type PropsWithChildren } from 'react';
4
4
  import { authEnv } from '@/envs/auth';
5
5
 
6
6
  import BetterAuth from './BetterAuth';
7
- import Clerk from './Clerk';
8
7
  import Desktop from './Desktop';
9
8
  import NextAuth from './NextAuth';
10
9
  import NoAuth from './NoAuth';
@@ -14,10 +13,6 @@ const AuthProvider = ({ children }: PropsWithChildren) => {
14
13
  return <Desktop>{children}</Desktop>;
15
14
  }
16
15
 
17
- if (authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH) {
18
- return <Clerk>{children}</Clerk>;
19
- }
20
-
21
16
  if (authEnv.NEXT_PUBLIC_ENABLE_BETTER_AUTH) {
22
17
  return <BetterAuth>{children}</BetterAuth>;
23
18
  }
@@ -17,16 +17,14 @@ interface CustomNextConfig {
17
17
  export function defineConfig(config: CustomNextConfig) {
18
18
  const isProd = process.env.NODE_ENV === 'production';
19
19
  const buildWithDocker = process.env.DOCKER === 'true';
20
- const isDesktop = process.env.NEXT_PUBLIC_IS_DESKTOP_APP === '1';
20
+
21
21
  const enableReactScan = !!process.env.REACT_SCAN_MONITOR_API_KEY;
22
22
  const shouldUseCSP = process.env.ENABLED_CSP === '1';
23
23
 
24
24
  const isTest =
25
25
  process.env.NODE_ENV === 'test' || process.env.TEST === '1' || process.env.E2E === '1';
26
26
 
27
- // if you need to proxy the api endpoint to remote server
28
-
29
- const isStandaloneMode = buildWithDocker || isDesktop;
27
+ const isStandaloneMode = buildWithDocker || process.env.NEXT_BUILD_STANDALONE === '1';
30
28
 
31
29
  const standaloneConfig: NextConfig = {
32
30
  output: 'standalone',
@@ -38,6 +36,7 @@ export function defineConfig(config: CustomNextConfig) {
38
36
  const nextConfig: NextConfig = {
39
37
  ...(isStandaloneMode ? standaloneConfig : {}),
40
38
  assetPrefix,
39
+
41
40
  compiler: {
42
41
  emotion: true,
43
42
  },
@@ -313,15 +312,22 @@ export function defineConfig(config: CustomNextConfig) {
313
312
  permanent: true,
314
313
  source: '/chat',
315
314
  },
315
+ // Redirect old Clerk login route to Better Auth signin
316
+ {
317
+ destination: '/signin',
318
+ permanent: true,
319
+ source: '/login',
320
+ },
316
321
  ...(config.redirects ?? []),
317
322
  ],
318
-
319
323
  // when external packages in dev mode with turbopack, this config will lead to bundle error
324
+ // @napi-rs/canvas is a native module that can't be bundled by Turbopack
325
+ // pdfjs-dist uses @napi-rs/canvas for DOMMatrix polyfill in Node.js environment
320
326
  serverExternalPackages: config.serverExternalPackages
321
327
  ? config.serverExternalPackages
322
- : ['pdfkit'],
328
+ : ['pdfkit', '@napi-rs/canvas', 'pdfjs-dist'],
323
329
 
324
- transpilePackages: ['pdfjs-dist', 'mermaid', 'better-auth-harmony'],
330
+ transpilePackages: ['mermaid', 'better-auth-harmony'],
325
331
  turbopack: {
326
332
  rules: isTest
327
333
  ? void 0
@@ -400,14 +406,13 @@ export function defineConfig(config: CustomNextConfig) {
400
406
 
401
407
  const withBundleAnalyzer = process.env.ANALYZE === 'true' ? analyzer() : noWrapper;
402
408
 
403
- const withPWA =
404
- isProd && !isDesktop
405
- ? withSerwistInit({
406
- register: false,
407
- swDest: 'public/sw.js',
408
- swSrc: 'src/app/sw.ts',
409
- })
410
- : noWrapper;
409
+ const withPWA = isProd
410
+ ? withSerwistInit({
411
+ register: false,
412
+ swDest: 'public/sw.js',
413
+ swSrc: 'src/app/sw.ts',
414
+ })
415
+ : noWrapper;
411
416
 
412
417
  return withBundleAnalyzer(withPWA(nextConfig as NextConfig));
413
418
  }