@lobehub/lobehub 2.0.0-next.220 → 2.0.0-next.222

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 (123) hide show
  1. package/.github/workflows/claude-auto-testing.yml +6 -3
  2. package/.github/workflows/claude-dedupe-issues.yml +8 -1
  3. package/.github/workflows/claude-issue-triage.yml +8 -14
  4. package/.github/workflows/claude-translate-comments.yml +6 -3
  5. package/.github/workflows/claude-translator.yml +12 -14
  6. package/.github/workflows/claude.yml +10 -20
  7. package/.github/workflows/test.yml +26 -0
  8. package/.i18nrc.js +4 -2
  9. package/CHANGELOG.md +66 -0
  10. package/apps/desktop/src/main/core/browser/Browser.ts +48 -20
  11. package/apps/desktop/src/main/core/browser/__tests__/Browser.test.ts +1 -0
  12. package/changelog/v1.json +21 -0
  13. package/docs/glossary.md +11 -0
  14. package/locales/zh-CN/components.json +1 -0
  15. package/locales/zh-CN/topic.json +5 -5
  16. package/package.json +3 -3
  17. package/packages/const/src/index.ts +0 -1
  18. package/packages/memory-user-memory/package.json +1 -0
  19. package/packages/memory-user-memory/src/extractors/context.test.ts +3 -2
  20. package/packages/memory-user-memory/src/extractors/experience.test.ts +3 -2
  21. package/packages/memory-user-memory/src/extractors/identity.test.ts +23 -6
  22. package/packages/memory-user-memory/src/extractors/preference.test.ts +3 -2
  23. package/packages/memory-user-memory/vitest.config.ts +4 -0
  24. package/packages/model-runtime/src/providers/replicate/index.ts +1 -1
  25. package/packages/ssrf-safe-fetch/index.test.ts +2 -2
  26. package/packages/ssrf-safe-fetch/package.json +3 -2
  27. package/packages/types/src/serverConfig.ts +2 -0
  28. package/packages/utils/package.json +1 -1
  29. package/packages/utils/src/client/xor-obfuscation.test.ts +32 -32
  30. package/packages/utils/src/client/xor-obfuscation.ts +3 -4
  31. package/packages/utils/src/imageToBase64.ts +1 -1
  32. package/packages/utils/src/server/__tests__/auth.test.ts +1 -1
  33. package/packages/utils/src/server/auth.ts +1 -1
  34. package/packages/utils/src/server/xor.test.ts +9 -7
  35. package/packages/utils/src/server/xor.ts +1 -1
  36. package/packages/web-crawler/package.json +1 -1
  37. package/packages/web-crawler/src/crawImpl/__tests__/naive.test.ts +1 -1
  38. package/packages/web-crawler/src/crawImpl/naive.ts +1 -1
  39. package/scripts/prebuild.mts +58 -1
  40. package/src/app/(backend)/api/auth/[...all]/route.ts +2 -1
  41. package/src/app/(backend)/middleware/auth/index.ts +3 -3
  42. package/src/app/(backend)/middleware/auth/utils.test.ts +1 -1
  43. package/src/app/(backend)/middleware/auth/utils.ts +1 -1
  44. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +2 -2
  45. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +1 -1
  46. package/src/app/(backend)/webapi/plugin/gateway/route.ts +1 -1
  47. package/src/app/(backend)/webapi/proxy/route.ts +1 -1
  48. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -1
  49. package/src/app/[variants]/(auth)/reset-password/layout.tsx +1 -1
  50. package/src/app/[variants]/(auth)/signin/layout.tsx +1 -1
  51. package/src/app/[variants]/(auth)/signin/useSignIn.ts +2 -2
  52. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -1
  53. package/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.tsx +12 -6
  54. package/src/app/[variants]/(auth)/verify-email/layout.tsx +1 -1
  55. package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -1
  56. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -1
  57. package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +1 -1
  58. package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +1 -1
  59. package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -1
  60. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +1 -1
  61. package/src/components/ModelSelect/index.tsx +103 -72
  62. package/src/envs/auth.ts +30 -9
  63. package/src/features/Conversation/Messages/AssistantGroup/components/EditState.tsx +15 -32
  64. package/src/features/Conversation/Messages/AssistantGroup/index.tsx +9 -7
  65. package/src/features/EditorModal/EditorCanvas.tsx +31 -50
  66. package/src/features/EditorModal/TextareCanvas.tsx +3 -1
  67. package/src/features/EditorModal/index.tsx +14 -4
  68. package/src/features/ModelSwitchPanel/components/Footer.tsx +42 -0
  69. package/src/features/ModelSwitchPanel/components/List/MultipleProvidersModelItem.tsx +103 -0
  70. package/src/features/ModelSwitchPanel/components/List/SingleProviderModelItem.tsx +24 -0
  71. package/src/features/ModelSwitchPanel/components/List/VirtualItemRenderer.tsx +180 -0
  72. package/src/features/ModelSwitchPanel/components/List/index.tsx +99 -0
  73. package/src/features/ModelSwitchPanel/components/PanelContent.tsx +77 -0
  74. package/src/features/ModelSwitchPanel/components/Toolbar.tsx +54 -0
  75. package/src/features/ModelSwitchPanel/const.ts +29 -0
  76. package/src/features/ModelSwitchPanel/hooks/useBuildVirtualItems.ts +122 -0
  77. package/src/features/ModelSwitchPanel/hooks/useCurrentModelName.ts +18 -0
  78. package/src/features/ModelSwitchPanel/hooks/useDelayedRender.ts +18 -0
  79. package/src/features/ModelSwitchPanel/hooks/useModelAndProvider.ts +14 -0
  80. package/src/features/ModelSwitchPanel/hooks/usePanelHandlers.ts +33 -0
  81. package/src/features/ModelSwitchPanel/hooks/usePanelSize.ts +33 -0
  82. package/src/features/ModelSwitchPanel/hooks/usePanelState.ts +20 -0
  83. package/src/features/ModelSwitchPanel/index.tsx +25 -706
  84. package/src/features/ModelSwitchPanel/styles.ts +58 -0
  85. package/src/features/ModelSwitchPanel/types.ts +73 -0
  86. package/src/features/ModelSwitchPanel/utils.ts +24 -0
  87. package/src/features/User/UserPanel/PanelContent.tsx +1 -1
  88. package/src/features/User/__tests__/PanelContent.test.tsx +1 -1
  89. package/src/features/User/__tests__/UserAvatar.test.tsx +1 -1
  90. package/src/features/User/__tests__/useMenu.test.tsx +1 -1
  91. package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -1
  92. package/src/libs/better-auth/auth-client.ts +7 -3
  93. package/src/libs/better-auth/define-config.ts +2 -2
  94. package/src/libs/next/proxy/define-config.ts +1 -2
  95. package/src/libs/oidc-provider/provider.test.ts +1 -1
  96. package/src/libs/trpc/async/context.ts +1 -1
  97. package/src/libs/trpc/lambda/context.ts +7 -8
  98. package/src/libs/trpc/middleware/userAuth.ts +1 -1
  99. package/src/libs/trusted-client/getSessionUser.ts +1 -1
  100. package/src/locales/default/components.ts +1 -0
  101. package/src/server/globalConfig/index.ts +2 -0
  102. package/src/server/routers/async/caller.ts +1 -1
  103. package/src/server/routers/lambda/__tests__/user.test.ts +2 -2
  104. package/src/server/routers/lambda/notebook.ts +4 -2
  105. package/src/server/routers/lambda/user.ts +2 -1
  106. package/src/services/_auth.ts +3 -3
  107. package/src/services/chat/index.ts +1 -1
  108. package/src/services/chat/mecha/contextEngineering.ts +1 -1
  109. package/src/services/notebook.ts +2 -0
  110. package/src/store/global/initialState.ts +10 -0
  111. package/src/store/global/selectors/systemStatus.ts +5 -0
  112. package/src/store/serverConfig/selectors.ts +5 -1
  113. package/src/store/tool/slices/builtin/executors/lobe-web-browsing.ts +2 -0
  114. package/src/store/tool/slices/mcpStore/action.ts +74 -75
  115. package/src/store/user/slices/auth/action.test.ts +1 -1
  116. package/src/store/user/slices/auth/action.ts +1 -1
  117. package/src/store/user/slices/auth/initialState.ts +1 -1
  118. package/src/store/user/slices/auth/selectors.test.ts +1 -1
  119. package/src/store/user/slices/auth/selectors.ts +1 -1
  120. package/src/store/user/slices/common/action.ts +1 -1
  121. package/src/store/userMemory/slices/context/action.ts +6 -6
  122. package/glossary.json +0 -8
  123. package/packages/const/src/auth.ts +0 -14
@@ -2,7 +2,7 @@ import { type AuthObject } from '@clerk/backend';
2
2
  import { AgentRuntimeError } from '@lobechat/model-runtime';
3
3
  import { ChatErrorType } from '@lobechat/types';
4
4
 
5
- import { enableBetterAuth, enableClerk, enableNextAuth } from '@/const/auth';
5
+ import { enableBetterAuth, enableClerk, enableNextAuth } from '@/envs/auth';
6
6
 
7
7
  interface CheckAuthParams {
8
8
  apiKey?: string;
@@ -6,7 +6,7 @@ import { getXorPayload } from '@lobechat/utils/server';
6
6
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
7
 
8
8
  import { checkAuthMethod } from '@/app/(backend)/middleware/auth/utils';
9
- import { LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED } from '@/const/auth';
9
+ import { LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED } from '@/envs/auth';
10
10
  import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
11
11
 
12
12
  import { POST } from './route';
@@ -32,7 +32,7 @@ vi.mock('@/server/modules/ModelRuntime', () => ({
32
32
  const mockState = vi.hoisted(() => ({ enableClerk: false }));
33
33
 
34
34
  // 模拟 @/const/auth 模块
35
- vi.mock('@/const/auth', async (importOriginal) => {
35
+ vi.mock('@/envs/auth', async (importOriginal) => {
36
36
  const modules = await importOriginal();
37
37
  return {
38
38
  ...(modules as any),
@@ -4,7 +4,7 @@ import { ChatErrorType } from '@lobechat/types';
4
4
  import { getXorPayload } from '@lobechat/utils/server';
5
5
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
6
 
7
- import { LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
7
+ import { LOBE_CHAT_AUTH_HEADER } from '@/envs/auth';
8
8
  import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
9
9
 
10
10
  import { GET } from './route';
@@ -3,9 +3,9 @@ import { ChatErrorType, TraceNameMap } from '@lobechat/types';
3
3
  import type { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
4
4
  import { createGatewayOnEdgeRuntime } from '@lobehub/chat-plugins-gateway';
5
5
 
6
- import { LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
7
6
  import { LOBE_CHAT_TRACE_ID } from '@/const/trace';
8
7
  import { getAppConfig } from '@/envs/app';
8
+ import { LOBE_CHAT_AUTH_HEADER } from '@/envs/auth';
9
9
  import { TraceClient } from '@/libs/traces';
10
10
  import { parserPluginSettings } from '@/server/services/pluginGateway/settings';
11
11
  import { getTracePayload } from '@/utils/trace';
@@ -1,5 +1,5 @@
1
1
  import { NextResponse } from 'next/server';
2
- import { ssrfSafeFetch } from 'ssrf-safe-fetch';
2
+ import { ssrfSafeFetch } from '@lobechat/ssrf-safe-fetch';
3
3
 
4
4
  /**
5
5
  * just for a proxy
@@ -2,7 +2,7 @@ import { SignIn } from '@clerk/nextjs';
2
2
  import { BRANDING_NAME } from '@lobechat/business-const';
3
3
  import { notFound } from 'next/navigation';
4
4
 
5
- import { enableClerk } from '@/const/auth';
5
+ import { enableClerk } from '@/envs/auth';
6
6
  import { metadataModule } from '@/server/metadata';
7
7
  import { translation } from '@/server/translation';
8
8
  import { type DynamicLayoutProps } from '@/types/next';
@@ -1,7 +1,7 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import { type PropsWithChildren } from 'react';
3
3
 
4
- import { enableBetterAuth } from '@/const/auth';
4
+ import { enableBetterAuth } from '@/envs/auth';
5
5
 
6
6
  const Layout = ({ children }: PropsWithChildren) => {
7
7
  if (!enableBetterAuth) return notFound();
@@ -1,7 +1,7 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import { type PropsWithChildren } from 'react';
3
3
 
4
- import { enableBetterAuth } from '@/const/auth';
4
+ import { enableBetterAuth } from '@/envs/auth';
5
5
 
6
6
  const Layout = ({ children }: PropsWithChildren) => {
7
7
  if (!enableBetterAuth) return notFound();
@@ -8,10 +8,10 @@ import type { CheckUserResponseData } from '@/app/(backend)/api/auth/check-user/
8
8
  import type { ResolveUsernameResponseData } from '@/app/(backend)/api/auth/resolve-username/route';
9
9
  import { useBusinessSignin } from '@/business/client/hooks/useBusinessSignin';
10
10
  import { message } from '@/components/AntdStaticMethods';
11
- import { getAuthConfig } from '@/envs/auth';
12
11
  import { requestPasswordReset, signIn } from '@/libs/better-auth/auth-client';
13
12
  import { isBuiltinProvider, normalizeProviderId } from '@/libs/better-auth/utils/client';
14
13
  import { useServerConfigStore } from '@/store/serverConfig';
14
+ import { serverConfigSelectors } from '@/store/serverConfig/selectors';
15
15
 
16
16
  import { EMAIL_REGEX, USERNAME_REGEX } from './SignInEmailStep';
17
17
 
@@ -31,7 +31,7 @@ export const useSignIn = () => {
31
31
  const { t } = useTranslation('auth');
32
32
  const router = useRouter();
33
33
  const searchParams = useSearchParams();
34
- const { NEXT_PUBLIC_ENABLE_MAGIC_LINK: enableMagicLink } = getAuthConfig();
34
+ const enableMagicLink = useServerConfigStore(serverConfigSelectors.enableMagicLink);
35
35
  const [form] = Form.useForm<SignInFormValues>();
36
36
  const [loading, setLoading] = useState(false);
37
37
  const [socialLoading, setSocialLoading] = useState<string | null>(null);
@@ -1,7 +1,7 @@
1
1
  import { SignUp } from '@clerk/nextjs';
2
2
  import { notFound } from 'next/navigation';
3
3
 
4
- import { enableBetterAuth, enableClerk } from '@/const/auth';
4
+ import { enableBetterAuth, enableClerk } from '@/envs/auth';
5
5
  import { metadataModule } from '@/server/metadata';
6
6
  import { translation } from '@/server/translation';
7
7
  import { type DynamicLayoutProps } from '@/types/next';
@@ -2,24 +2,30 @@ import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
2
2
  import { form } from 'motion/react-m';
3
3
  import { useRouter, useSearchParams } from 'next/navigation';
4
4
  import { useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
5
6
 
6
7
  import {
7
8
  BusinessSignupFomData,
8
9
  useBusinessSignup,
9
10
  } from '@/business/client/hooks/useBusinessSignup';
10
11
  import { message } from '@/components/AntdStaticMethods';
11
- import { authEnv } from '@/envs/auth';
12
12
  import { signUp } from '@/libs/better-auth/auth-client';
13
+ import { useServerConfigStore } from '@/store/serverConfig';
14
+ import { serverConfigSelectors } from '@/store/serverConfig/selectors';
13
15
 
14
16
  import { BaseSignUpFormValues } from './types';
15
17
 
16
18
  export type SignUpFormValues = BaseSignUpFormValues & BusinessSignupFomData;
17
19
 
18
20
  export const useSignUp = () => {
21
+ const { t } = useTranslation('auth');
19
22
  const router = useRouter();
20
23
  const searchParams = useSearchParams();
21
24
  const [loading, setLoading] = useState(false);
22
25
  const { getFetchOptions, preSocialSignupCheck, businessElement } = useBusinessSignup(form);
26
+ const enableEmailVerification = useServerConfigStore(
27
+ serverConfigSelectors.enableEmailVerification,
28
+ );
23
29
 
24
30
  const handleSignUp = async (values: SignUpFormValues) => {
25
31
  setLoading(true);
@@ -46,20 +52,20 @@ export const useSignUp = () => {
46
52
  (error as any)?.details?.cause?.code === '23505';
47
53
 
48
54
  if (isEmailDuplicate) {
49
- message.error('betterAuth.errors.emailExists');
55
+ message.error(t('betterAuth.errors.emailExists'));
50
56
  return;
51
57
  }
52
58
 
53
59
  if (error.code === 'INVALID_EMAIL') {
54
- message.error('betterAuth.errors.emailInvalid');
60
+ message.error(t('betterAuth.errors.emailInvalid'));
55
61
  return;
56
62
  }
57
63
 
58
- message.error(error.message || 'betterAuth.signup.error');
64
+ message.error(error.message || t('betterAuth.signup.error'));
59
65
  return;
60
66
  }
61
67
 
62
- if (authEnv.NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION) {
68
+ if (enableEmailVerification) {
63
69
  router.push(
64
70
  `/verify-email?email=${encodeURIComponent(values.email)}&callbackUrl=${encodeURIComponent(callbackUrl)}`,
65
71
  );
@@ -67,7 +73,7 @@ export const useSignUp = () => {
67
73
  router.push(callbackUrl);
68
74
  }
69
75
  } catch {
70
- message.error('betterAuth.signup.error');
76
+ message.error(t('betterAuth.signup.error'));
71
77
  } finally {
72
78
  setLoading(false);
73
79
  }
@@ -1,7 +1,7 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import { type PropsWithChildren } from 'react';
3
3
 
4
- import { enableBetterAuth } from '@/const/auth';
4
+ import { enableBetterAuth } from '@/envs/auth';
5
5
 
6
6
  const Layout = ({ children }: PropsWithChildren) => {
7
7
  if (!enableBetterAuth) return notFound();
@@ -7,7 +7,7 @@ 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 '@/const/auth';
10
+ import { enableAuth } from '@/envs/auth';
11
11
  import UserAvatar from '@/features/User/UserAvatar';
12
12
  import { useUserStore } from '@/store/user';
13
13
  import { authSelectors } from '@/store/user/selectors';
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
6
6
  import { Navigate } from 'react-router-dom';
7
7
 
8
8
  import SettingHeader from '@/app/[variants]/(main)/settings/features/SettingHeader';
9
- import { enableClerk } from '@/const/auth';
9
+ import { enableClerk } from '@/envs/auth';
10
10
 
11
11
  const ClerkProfile = dynamic(() => import('./features/ClerkProfile'), {
12
12
  loading: () => (
@@ -37,7 +37,7 @@ const { enableAuth, enableClerk } = vi.hoisted(() => ({
37
37
  enableClerk: { value: false },
38
38
  }));
39
39
 
40
- vi.mock('@/const/auth', () => ({
40
+ vi.mock('@/envs/auth', () => ({
41
41
  get enableAuth() {
42
42
  return enableAuth.value;
43
43
  },
@@ -28,7 +28,7 @@ const { enableAuth, enableClerk } = vi.hoisted(() => ({
28
28
  enableClerk: { value: true },
29
29
  }));
30
30
 
31
- vi.mock('@/const/auth', () => ({
31
+ vi.mock('@/envs/auth', () => ({
32
32
  get enableAuth() {
33
33
  return enableAuth.value;
34
34
  },
@@ -4,7 +4,7 @@ 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 '@/const/auth';
7
+ import { enableAuth } from '@/envs/auth';
8
8
  import DataStatistics from '@/features/User/DataStatistics';
9
9
  import UserInfo from '@/features/User/UserInfo';
10
10
  import UserLoginOrSignup from '@/features/User/UserLoginOrSignup/Community';
@@ -6,7 +6,7 @@ import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { useNavigate } from 'react-router-dom';
8
8
 
9
- import { enableAuth } from '@/const/auth';
9
+ import { enableAuth } from '@/envs/auth';
10
10
  import { useQueryState } from '@/hooks/useQueryParam';
11
11
  import { useShowMobileWorkspace } from '@/hooks/useShowMobileWorkspace';
12
12
  import { type SettingsTabs } from '@/store/global/initialState';
@@ -1,6 +1,6 @@
1
1
  import { type ChatModelCard } from '@lobechat/types';
2
- import { type IconAvatarProps, ModelIcon, ProviderIcon } from '@lobehub/icons';
3
- import { Avatar, Flexbox, Icon, Tag, Text, Tooltip } from '@lobehub/ui';
2
+ import { type IconAvatarProps, LobeHub, ModelIcon, ProviderIcon } from '@lobehub/icons';
3
+ import { Avatar, Flexbox, FlexboxProps, Icon, Tag, Text, Tooltip } from '@lobehub/ui';
4
4
  import { createStaticStyles, useResponsive } from 'antd-style';
5
5
  import {
6
6
  Infinity,
@@ -14,7 +14,7 @@ import {
14
14
  } from 'lucide-react';
15
15
  import { type ModelAbilities } from 'model-bank';
16
16
  import numeral from 'numeral';
17
- import { type ComponentProps, type FC, memo, useState } from 'react';
17
+ import { CSSProperties, type ComponentProps, type FC, memo, useState } from 'react';
18
18
  import { useTranslation } from 'react-i18next';
19
19
 
20
20
  import { type AiProviderSourceType } from '@/types/aiProvider';
@@ -64,6 +64,7 @@ interface ModelInfoTagsProps extends ModelAbilities {
64
64
  directionReverse?: boolean;
65
65
  isCustom?: boolean;
66
66
  placement?: 'top' | 'right';
67
+ style?: CSSProperties;
67
68
  /**
68
69
  * Whether to render tooltip overlays for each tag.
69
70
  * Disable this when rendering a large list (e.g. dropdown menus) to avoid mounting hundreds of Tooltip instances.
@@ -237,13 +238,13 @@ const Context = memo(
237
238
  );
238
239
 
239
240
  export const ModelInfoTags = memo<ModelInfoTagsProps>(
240
- ({ directionReverse, placement = 'top', withTooltip = true, ...model }) => {
241
+ ({ directionReverse, placement = 'top', withTooltip = true, style, ...model }) => {
241
242
  return (
242
243
  <Flexbox
243
244
  className={TAG_CLASSNAME}
244
245
  direction={directionReverse ? 'horizontal-reverse' : 'horizontal'}
245
- gap={4}
246
- style={{ marginLeft: 'auto' }}
246
+ gap={2}
247
+ style={{ marginLeft: 'auto', ...style }}
247
248
  width={'fit-content'}
248
249
  >
249
250
  <FeatureTags
@@ -271,7 +272,7 @@ export const ModelInfoTags = memo<ModelInfoTagsProps>(
271
272
  },
272
273
  );
273
274
 
274
- interface ModelItemRenderProps extends ChatModelCard {
275
+ interface ModelItemRenderProps extends ChatModelCard, Partial<Omit<FlexboxProps, 'id' | 'title'>> {
275
276
  abilities?: ModelAbilities;
276
277
  infoTagTooltip?: boolean;
277
278
  /**
@@ -286,10 +287,9 @@ interface ModelItemRenderProps extends ChatModelCard {
286
287
  showInfoTag?: boolean;
287
288
  }
288
289
 
289
- export const ModelItemRender = memo<ModelItemRenderProps>(({ showInfoTag = true, ...model }) => {
290
- const { mobile } = useResponsive();
291
- const [hovered, setHovered] = useState(false);
292
- const {
290
+ export const ModelItemRender = memo<ModelItemRenderProps>(
291
+ ({
292
+ showInfoTag = true,
293
293
  abilities,
294
294
  infoTagTooltip = true,
295
295
  infoTagTooltipOnHover = false,
@@ -302,88 +302,119 @@ export const ModelItemRender = memo<ModelItemRenderProps>(({ showInfoTag = true,
302
302
  search,
303
303
  video,
304
304
  vision,
305
- } = model;
305
+ id,
306
+ displayName,
307
+ releasedAt,
308
+ ...rest
309
+ }) => {
310
+ const { mobile } = useResponsive();
311
+ const [hovered, setHovered] = useState(false);
306
312
 
307
- const shouldLazyMountTooltip = infoTagTooltipOnHover && !mobile;
308
- /**
309
- * When `infoTagTooltipOnHover` is enabled, we don't mount Tooltip components until the row is hovered.
310
- * This avoids creating many overlays on dropdown open, while keeping the tooltip UX on demand.
311
- */
312
- const withTooltip = infoTagTooltip && (!shouldLazyMountTooltip || hovered);
313
- const displayName = model.displayName || model.id;
314
-
315
- return (
316
- <Flexbox
317
- align={'center'}
318
- gap={32}
319
- horizontal
320
- justify={'space-between'}
321
- onMouseEnter={shouldLazyMountTooltip && !hovered ? () => setHovered(true) : undefined}
322
- style={{
323
- overflow: 'hidden',
324
- position: 'relative',
325
- width: '100%',
326
- }}
327
- >
313
+ const shouldLazyMountTooltip = infoTagTooltipOnHover && !mobile;
314
+ /**
315
+ * When `infoTagTooltipOnHover` is enabled, we don't mount Tooltip components until the row is hovered.
316
+ * This avoids creating many overlays on dropdown open, while keeping the tooltip UX on demand.
317
+ */
318
+ const withTooltip = infoTagTooltip && (!shouldLazyMountTooltip || hovered);
319
+ const displayNameOrId = displayName || id;
320
+
321
+ return (
328
322
  <Flexbox
329
323
  align={'center'}
330
- gap={8}
324
+ gap={32}
331
325
  horizontal
332
- style={{ flexShrink: 1, minWidth: 0, overflow: 'hidden' }}
326
+ justify={'space-between'}
327
+ onMouseEnter={shouldLazyMountTooltip && !hovered ? () => setHovered(true) : undefined}
328
+ {...rest}
329
+ style={{
330
+ overflow: 'hidden',
331
+ position: 'relative',
332
+ width: '100%',
333
+ ...rest.style,
334
+ }}
333
335
  >
334
- <ModelIcon model={model.id} size={20} />
335
- <Text
336
- ellipsis={
337
- withTooltip
338
- ? {
339
- tooltip: displayName,
340
- }
341
- : true
342
- }
343
- style={mobile ? { maxWidth: '60vw' } : { minWidth: 0, overflow: 'hidden' }}
336
+ <Flexbox
337
+ align={'center'}
338
+ gap={8}
339
+ horizontal
340
+ style={{ flexShrink: 1, minWidth: 0, overflow: 'hidden' }}
344
341
  >
345
- {displayName}
346
- </Text>
347
- {newBadgeLabel ? (
348
- <NewModelBadgeCore label={newBadgeLabel} releasedAt={model.releasedAt} />
349
- ) : (
350
- <NewModelBadgeI18n releasedAt={model.releasedAt} />
342
+ <ModelIcon model={id} size={20} />
343
+ <Text
344
+ ellipsis={
345
+ withTooltip
346
+ ? {
347
+ tooltip: displayNameOrId,
348
+ }
349
+ : true
350
+ }
351
+ style={mobile ? { maxWidth: '60vw' } : { minWidth: 0, overflow: 'hidden' }}
352
+ >
353
+ {displayNameOrId}
354
+ </Text>
355
+ {newBadgeLabel ? (
356
+ <NewModelBadgeCore label={newBadgeLabel} releasedAt={releasedAt} />
357
+ ) : (
358
+ <NewModelBadgeI18n releasedAt={releasedAt} />
359
+ )}
360
+ </Flexbox>
361
+ {showInfoTag && (
362
+ <ModelInfoTags
363
+ contextWindowTokens={contextWindowTokens}
364
+ files={files ?? abilities?.files}
365
+ functionCall={functionCall ?? abilities?.functionCall}
366
+ imageOutput={imageOutput ?? abilities?.imageOutput}
367
+ reasoning={reasoning ?? abilities?.reasoning}
368
+ search={search ?? abilities?.search}
369
+ style={{ zoom: 0.9 }}
370
+ video={video ?? abilities?.video}
371
+ vision={vision ?? abilities?.vision}
372
+ withTooltip={withTooltip}
373
+ />
351
374
  )}
352
375
  </Flexbox>
353
- {showInfoTag && (
354
- <ModelInfoTags
355
- contextWindowTokens={contextWindowTokens}
356
- files={files ?? abilities?.files}
357
- functionCall={functionCall ?? abilities?.functionCall}
358
- imageOutput={imageOutput ?? abilities?.imageOutput}
359
- reasoning={reasoning ?? abilities?.reasoning}
360
- search={search ?? abilities?.search}
361
- video={video ?? abilities?.video}
362
- vision={vision ?? abilities?.vision}
363
- withTooltip={withTooltip}
364
- />
365
- )}
366
- </Flexbox>
367
- );
368
- });
376
+ );
377
+ },
378
+ );
369
379
 
370
380
  interface ProviderItemRenderProps {
371
381
  logo?: string;
372
382
  name: string;
373
383
  provider: string;
384
+ size?: number;
374
385
  source?: AiProviderSourceType;
386
+ type?: 'mono' | 'color' | 'avatar';
375
387
  }
376
388
 
377
389
  export const ProviderItemRender = memo<ProviderItemRenderProps>(
378
- ({ provider, name, source, logo }) => {
390
+ ({ provider, name, source, logo, type = 'mono', size = 16 }) => {
391
+ const isMono = type === 'mono';
379
392
  return (
380
- <Flexbox align={'center'} gap={4} horizontal>
393
+ <Flexbox
394
+ align={'center'}
395
+ gap={6}
396
+ horizontal
397
+ style={{
398
+ overflow: 'hidden',
399
+ }}
400
+ width={'100%'}
401
+ >
381
402
  {source === 'custom' && !!logo ? (
382
- <Avatar avatar={logo} size={20} style={{ filter: 'grayscale(1)' }} title={name} />
403
+ <Avatar
404
+ avatar={logo}
405
+ shape={'circle'}
406
+ size={size}
407
+ style={isMono ? { filter: 'grayscale(1)' } : {}}
408
+ title={name}
409
+ />
410
+ ) : provider === 'lobehub' ? (
411
+ <LobeHub.Morden size={size} />
383
412
  ) : (
384
- <ProviderIcon provider={provider} size={20} type={'mono'} />
413
+ <ProviderIcon provider={provider} size={size} type={type} />
385
414
  )}
386
- {name}
415
+ <Text color={'inherit'} ellipsis>
416
+ {name}
417
+ </Text>
387
418
  </Flexbox>
388
419
  );
389
420
  },
package/src/envs/auth.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
2
- import { enableBetterAuth, enableClerk, enableNextAuth } from '@lobechat/const';
3
2
  import { createEnv } from '@t3-oss/env-nextjs';
4
3
  import { z } from 'zod';
5
4
 
@@ -52,7 +51,8 @@ declare global {
52
51
  // ===== Auth (shared by Better Auth / Next Auth) ===== //
53
52
  AUTH_SECRET?: string;
54
53
  NEXT_PUBLIC_AUTH_URL?: string;
55
- NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION?: string;
54
+ AUTH_EMAIL_VERIFICATION?: string;
55
+ ENABLE_MAGIC_LINK?: string;
56
56
  AUTH_SSO_PROVIDERS?: string;
57
57
  AUTH_TRUSTED_ORIGINS?: string;
58
58
 
@@ -172,8 +172,6 @@ export const getAuthConfig = () => {
172
172
  // ---------------------------------- better auth ----------------------------------
173
173
  NEXT_PUBLIC_ENABLE_BETTER_AUTH: z.boolean().optional(),
174
174
  NEXT_PUBLIC_AUTH_URL: z.string().optional(),
175
- NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION: z.boolean().optional().default(false),
176
- NEXT_PUBLIC_ENABLE_MAGIC_LINK: z.boolean().optional().default(false),
177
175
 
178
176
  // ---------------------------------- next auth ----------------------------------
179
177
  NEXT_PUBLIC_ENABLE_NEXT_AUTH: z.boolean().optional(),
@@ -187,6 +185,8 @@ export const getAuthConfig = () => {
187
185
  AUTH_SECRET: z.string().optional(),
188
186
  AUTH_SSO_PROVIDERS: z.string().optional().default(''),
189
187
  AUTH_TRUSTED_ORIGINS: z.string().optional(),
188
+ AUTH_EMAIL_VERIFICATION: z.boolean().optional().default(false),
189
+ ENABLE_MAGIC_LINK: z.boolean().optional().default(false),
190
190
 
191
191
  // ---------------------------------- next auth ----------------------------------
192
192
  NEXT_AUTH_SECRET: z.string().optional(),
@@ -289,17 +289,23 @@ export const getAuthConfig = () => {
289
289
 
290
290
  runtimeEnv: {
291
291
  // Clerk
292
- NEXT_PUBLIC_ENABLE_CLERK_AUTH: enableClerk,
292
+ NEXT_PUBLIC_ENABLE_CLERK_AUTH:
293
+ process.env.NEXT_PUBLIC_ENABLE_CLERK_AUTH === '1' ||
294
+ !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
293
295
  NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
294
296
  CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,
295
297
  CLERK_WEBHOOK_SECRET: process.env.CLERK_WEBHOOK_SECRET,
296
298
 
297
299
  // ---------------------------------- better auth ----------------------------------
298
- NEXT_PUBLIC_ENABLE_BETTER_AUTH: enableBetterAuth,
300
+ NEXT_PUBLIC_ENABLE_BETTER_AUTH: process.env.NEXT_PUBLIC_ENABLE_BETTER_AUTH === '1',
299
301
  // Fallback to NEXTAUTH_URL origin or Vercel deployment domain for seamless migration from next-auth
300
302
  NEXT_PUBLIC_AUTH_URL: resolvePublicAuthUrl(),
301
- NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION: process.env.NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION === '1',
302
- NEXT_PUBLIC_ENABLE_MAGIC_LINK: process.env.NEXT_PUBLIC_ENABLE_MAGIC_LINK === '1',
303
+ // Fallback to NEXT_PUBLIC_* for seamless migration
304
+ AUTH_EMAIL_VERIFICATION:
305
+ process.env.AUTH_EMAIL_VERIFICATION === '1' ||
306
+ process.env.NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION === '1',
307
+ ENABLE_MAGIC_LINK:
308
+ process.env.ENABLE_MAGIC_LINK === '1' || process.env.NEXT_PUBLIC_ENABLE_MAGIC_LINK === '1',
303
309
  // Fallback to NEXT_AUTH_SECRET for seamless migration from next-auth
304
310
  AUTH_SECRET: process.env.AUTH_SECRET || process.env.NEXT_AUTH_SECRET,
305
311
  // Fallback to NEXT_AUTH_SSO_PROVIDERS for seamless migration from next-auth
@@ -312,7 +318,7 @@ export const getAuthConfig = () => {
312
318
  AUTH_COGNITO_USERPOOL_ID: process.env.AUTH_COGNITO_USERPOOL_ID,
313
319
 
314
320
  // ---------------------------------- next auth ----------------------------------
315
- NEXT_PUBLIC_ENABLE_NEXT_AUTH: enableNextAuth,
321
+ NEXT_PUBLIC_ENABLE_NEXT_AUTH: process.env.NEXT_PUBLIC_ENABLE_NEXT_AUTH === '1',
316
322
  NEXT_AUTH_SSO_PROVIDERS: process.env.NEXT_AUTH_SSO_PROVIDERS,
317
323
  NEXT_AUTH_SECRET: process.env.NEXT_AUTH_SECRET,
318
324
  NEXT_AUTH_DEBUG: !!process.env.NEXT_AUTH_DEBUG,
@@ -414,3 +420,18 @@ export const getAuthConfig = () => {
414
420
  };
415
421
 
416
422
  export const authEnv = getAuthConfig();
423
+
424
+ // Auth flags - use process.env directly for build-time dead code elimination
425
+ export const enableClerk =
426
+ process.env.NEXT_PUBLIC_ENABLE_CLERK_AUTH === '1'
427
+ ? true
428
+ : !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
429
+ export const enableBetterAuth = process.env.NEXT_PUBLIC_ENABLE_BETTER_AUTH === '1';
430
+ export const enableNextAuth = process.env.NEXT_PUBLIC_ENABLE_NEXT_AUTH === '1';
431
+ export const enableAuth = enableClerk || enableBetterAuth || enableNextAuth || false;
432
+
433
+ // Auth headers and constants
434
+ export const LOBE_CHAT_AUTH_HEADER = 'X-lobe-chat-auth';
435
+ export const LOBE_CHAT_OIDC_AUTH_HEADER = 'Oidc-Auth';
436
+ export const OAUTH_AUTHORIZED = 'X-oauth-authorized';
437
+ export const SECRET_XOR_KEY = 'LobeHub · LobeHub';
@@ -1,7 +1,6 @@
1
- import { Flexbox } from '@lobehub/ui';
2
- import { MessageInput } from '@lobehub/ui/chat';
3
- import { memo, useMemo } from 'react';
4
- import { useTranslation } from 'react-i18next';
1
+ import { memo } from 'react';
2
+
3
+ import { EditorModal } from '@/features/EditorModal';
5
4
 
6
5
  import { useConversationStore } from '../../../store';
7
6
 
@@ -11,40 +10,24 @@ export interface EditStateProps {
11
10
  }
12
11
 
13
12
  const EditState = memo<EditStateProps>(({ id, content }) => {
14
- const { t } = useTranslation('common');
15
-
16
- const text = useMemo(
17
- () => ({
18
- cancel: t('cancel'),
19
- confirm: t('ok'),
20
- edit: t('edit'),
21
- }),
22
- [],
23
- );
24
-
25
13
  const [toggleMessageEditing, updateMessageContent] = useConversationStore((s) => [
26
14
  s.toggleMessageEditing,
27
15
  s.modifyMessageContent,
28
16
  ]);
29
17
 
30
- const onEditingChange = (value: string) => {
31
- updateMessageContent(id, value);
32
- toggleMessageEditing(id, false);
33
- };
34
-
35
18
  return (
36
- <Flexbox paddingBlock={'0 8px'}>
37
- <MessageInput
38
- defaultValue={content ? String(content) : ''}
39
- editButtonSize={'small'}
40
- onCancel={() => {
41
- toggleMessageEditing(id, false);
42
- }}
43
- onConfirm={onEditingChange}
44
- text={text}
45
- variant={'outlined'}
46
- />
47
- </Flexbox>
19
+ <EditorModal
20
+ onCancel={() => {
21
+ toggleMessageEditing(id, false);
22
+ }}
23
+ onConfirm={async (value) => {
24
+ if (!id) return;
25
+ await updateMessageContent(id, value);
26
+ toggleMessageEditing(id, false);
27
+ }}
28
+ open={!!id}
29
+ value={content ? String(content) : ''}
30
+ />
48
31
  );
49
32
  });
50
33