@lobehub/lobehub 2.0.0-next.213 → 2.0.0-next.214

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 (81) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/apps/desktop/package.json +3 -2
  3. package/apps/desktop/src/main/const/store.ts +1 -1
  4. package/apps/desktop/src/main/controllers/SystemCtr.ts +2 -3
  5. package/apps/desktop/src/main/core/App.ts +10 -3
  6. package/apps/desktop/src/main/types/store.ts +1 -1
  7. package/changelog/v1.json +5 -0
  8. package/package.json +4 -3
  9. package/packages/builtin-tool-knowledge-base/src/client/Render/SearchKnowledgeBase/Item/index.tsx +4 -2
  10. package/packages/builtin-tool-local-system/src/client/Intervention/EditLocalFile/index.tsx +3 -2
  11. package/packages/builtin-tool-local-system/src/client/Render/EditLocalFile/index.tsx +3 -2
  12. package/packages/const/src/theme.ts +0 -2
  13. package/packages/desktop-bridge/src/routeVariants.ts +2 -9
  14. package/packages/electron-client-ipc/src/types/system.ts +1 -1
  15. package/scripts/electronWorkflow/modifiers/nextConfig.mts +41 -13
  16. package/src/app/[variants]/(auth)/_layout/index.tsx +3 -2
  17. package/src/app/[variants]/(auth)/_layout/style.ts +8 -18
  18. package/src/app/[variants]/(auth)/layout.tsx +7 -3
  19. package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/index.tsx +4 -2
  20. package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/style.ts +3 -0
  21. package/src/app/[variants]/(main)/_layout/DesktopLayoutContainer.tsx +3 -2
  22. package/src/app/[variants]/(main)/chat/profile/features/ProfileEditor/PluginTag.tsx +3 -2
  23. package/src/app/[variants]/(main)/community/(list)/_layout/Footer.tsx +3 -2
  24. package/src/app/[variants]/(main)/group/features/Conversation/ChatItem/Thread.tsx +3 -2
  25. package/src/app/[variants]/(main)/group/profile/features/AgentBuilder/index.tsx +0 -1
  26. package/src/app/[variants]/(main)/group/profile/features/ProfileEditor/PluginTag.tsx +3 -2
  27. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/Editing.tsx +2 -2
  28. package/src/app/[variants]/(main)/home/_layout/Footer/index.tsx +1 -1
  29. package/src/app/[variants]/(main)/home/_layout/index.tsx +3 -2
  30. package/src/app/[variants]/(main)/home/features/CommunityAgents/Item.tsx +3 -2
  31. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/AspectRatioSelect/index.tsx +4 -2
  32. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ModelSelect/ImageModelItem.tsx +3 -2
  33. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/Select/index.tsx +4 -2
  34. package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +3 -2
  35. package/src/app/[variants]/(main)/memory/features/TimeLineView/index.tsx +9 -4
  36. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/Editing.tsx +2 -2
  37. package/src/app/[variants]/(main)/settings/common/features/Common/Common.tsx +11 -11
  38. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +3 -2
  39. package/src/app/[variants]/(main)/settings/stats/features/overview/ShareButton/TotalCard.tsx +4 -2
  40. package/src/app/[variants]/(mobile)/me/(home)/features/Header.tsx +6 -8
  41. package/src/app/[variants]/layout.tsx +10 -15
  42. package/src/app/[variants]/onboarding/_layout/index.tsx +3 -2
  43. package/src/app/[variants]/onboarding/features/ModeSelectionStep.tsx +3 -2
  44. package/src/app/[variants]/router/index.tsx +12 -8
  45. package/src/components/Cell/Divider.tsx +4 -2
  46. package/src/components/DataStyleModal/index.tsx +4 -2
  47. package/src/components/FeatureList/index.tsx +4 -2
  48. package/src/components/FileParsingStatus/EmbeddingStatus.tsx +3 -2
  49. package/src/components/FileParsingStatus/index.tsx +3 -2
  50. package/src/components/Notification/index.tsx +4 -2
  51. package/src/components/client/ClientOnly.tsx +17 -0
  52. package/src/features/AlertBanner/CloudBanner.tsx +4 -3
  53. package/src/features/CommandMenu/ThemeMenu.tsx +1 -1
  54. package/src/features/CommandMenu/types.ts +5 -2
  55. package/src/features/CommandMenu/useCommandMenu.ts +3 -2
  56. package/src/features/Conversation/Markdown/plugins/LobeArtifact/Render/index.tsx +3 -2
  57. package/src/features/Conversation/Messages/components/FileChunks/ChunkItem.tsx +3 -2
  58. package/src/features/Conversation/Messages/components/FileChunks/index.tsx +4 -2
  59. package/src/features/Conversation/Messages/components/SearchGrounding.tsx +3 -2
  60. package/src/features/ElectronTitlebar/hooks/useWatchThemeUpdate.ts +21 -38
  61. package/src/features/GroupChatSettings/AgentCard.tsx +3 -2
  62. package/src/features/GroupChatSettings/HostMemberCard.tsx +3 -2
  63. package/src/features/PageEditor/DiffAllToolbar.tsx +4 -2
  64. package/src/features/User/UserPanel/ThemeButton.tsx +18 -29
  65. package/src/hooks/useIsDark.ts +11 -0
  66. package/src/layout/AuthProvider/Clerk/useAppearance.ts +4 -2
  67. package/src/layout/AuthProvider/MarketAuth/MarketAuthConfirmModal.tsx +3 -2
  68. package/src/layout/GlobalProvider/AppTheme.tsx +15 -19
  69. package/src/layout/GlobalProvider/NextThemeProvider.tsx +22 -0
  70. package/src/layout/GlobalProvider/StyleRegistry.tsx +18 -13
  71. package/src/layout/GlobalProvider/index.tsx +38 -36
  72. package/src/libs/next/proxy/define-config.ts +2 -11
  73. package/src/store/global/action.test.ts +0 -15
  74. package/src/store/global/actions/__tests__/general.test.ts +0 -37
  75. package/src/store/global/actions/general.ts +0 -21
  76. package/src/store/global/initialState.ts +0 -6
  77. package/src/store/global/selectors/systemStatus.test.ts +0 -20
  78. package/src/store/global/selectors/systemStatus.ts +0 -2
  79. package/src/styles/global.ts +0 -2
  80. package/src/utils/server/routeVariants.test.ts +17 -51
  81. package/src/utils/server/routeVariants.ts +0 -1
@@ -17,12 +17,12 @@ import AppTheme from './AppTheme';
17
17
  import { GroupWizardProvider } from './GroupWizardProvider';
18
18
  import ImportSettings from './ImportSettings';
19
19
  import Locale from './Locale';
20
+ import NextThemeProvider from './NextThemeProvider';
20
21
  import QueryProvider from './Query';
21
22
  import StoreInitialization from './StoreInitialization';
22
23
  import StyleRegistry from './StyleRegistry';
23
24
 
24
25
  interface GlobalLayoutProps {
25
- appearance: string;
26
26
  children: ReactNode;
27
27
  isMobile: boolean;
28
28
  locale: string;
@@ -36,7 +36,7 @@ const GlobalLayout = async ({
36
36
  neutralColor,
37
37
  primaryColor,
38
38
  locale: userLocale,
39
- appearance,
39
+
40
40
  isMobile,
41
41
  variants,
42
42
  }: GlobalLayoutProps) => {
@@ -45,44 +45,46 @@ const GlobalLayout = async ({
45
45
  // get default feature flags to use with ssr
46
46
  const serverFeatureFlags = getServerFeatureFlagsValue();
47
47
  const serverConfig = await getServerGlobalConfig();
48
+
48
49
  return (
49
50
  <StyleRegistry>
50
51
  <Locale antdLocale={antdLocale} defaultLang={userLocale}>
51
- <AppTheme
52
- customFontFamily={appEnv.CUSTOM_FONT_FAMILY}
53
- customFontURL={appEnv.CUSTOM_FONT_URL}
54
- defaultAppearance={appearance}
55
- defaultNeutralColor={neutralColor as any}
56
- defaultPrimaryColor={primaryColor as any}
57
- globalCDN={appEnv.CDN_USE_GLOBAL}
58
- >
59
- <ServerConfigStoreProvider
60
- featureFlags={serverFeatureFlags}
61
- isMobile={isMobile}
62
- segmentVariants={variants}
63
- serverConfig={serverConfig}
52
+ <NextThemeProvider>
53
+ <AppTheme
54
+ customFontFamily={appEnv.CUSTOM_FONT_FAMILY}
55
+ customFontURL={appEnv.CUSTOM_FONT_URL}
56
+ defaultNeutralColor={neutralColor as any}
57
+ defaultPrimaryColor={primaryColor as any}
58
+ globalCDN={appEnv.CDN_USE_GLOBAL}
64
59
  >
65
- <QueryProvider>
66
- <GroupWizardProvider>
67
- <DragUploadProvider>
68
- <LazyMotion features={domMax}>
69
- <TooltipGroup layoutAnimation={false}>
70
- <LobeAnalyticsProviderWrapper>{children}</LobeAnalyticsProviderWrapper>
71
- </TooltipGroup>
72
- <ModalHost />
73
- <ContextMenuHost />
74
- </LazyMotion>
75
- </DragUploadProvider>
76
- </GroupWizardProvider>
77
- </QueryProvider>
78
- <StoreInitialization />
79
- <Suspense>
80
- {ENABLE_BUSINESS_FEATURES ? <ReferralProvider /> : null}
81
- <ImportSettings />
82
- {process.env.NODE_ENV === 'development' && <DevPanel />}
83
- </Suspense>
84
- </ServerConfigStoreProvider>
85
- </AppTheme>
60
+ <ServerConfigStoreProvider
61
+ featureFlags={serverFeatureFlags}
62
+ isMobile={isMobile}
63
+ segmentVariants={variants}
64
+ serverConfig={serverConfig}
65
+ >
66
+ <QueryProvider>
67
+ <GroupWizardProvider>
68
+ <DragUploadProvider>
69
+ <LazyMotion features={domMax}>
70
+ <TooltipGroup layoutAnimation={false}>
71
+ <LobeAnalyticsProviderWrapper>{children}</LobeAnalyticsProviderWrapper>
72
+ </TooltipGroup>
73
+ <ModalHost />
74
+ <ContextMenuHost />
75
+ </LazyMotion>
76
+ </DragUploadProvider>
77
+ </GroupWizardProvider>
78
+ </QueryProvider>
79
+ <StoreInitialization />
80
+ <Suspense>
81
+ {ENABLE_BUSINESS_FEATURES ? <ReferralProvider /> : null}
82
+ <ImportSettings />
83
+ {process.env.NODE_ENV === 'development' && <DevPanel />}
84
+ </Suspense>
85
+ </ServerConfigStoreProvider>
86
+ </AppTheme>
87
+ </NextThemeProvider>
86
88
  </Locale>
87
89
  </StyleRegistry>
88
90
  );
@@ -1,5 +1,4 @@
1
1
  import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
2
- import { parseDefaultThemeFromCountry } from '@lobechat/utils/server';
3
2
  import debug from 'debug';
4
3
  import { type NextRequest, NextResponse } from 'next/server';
5
4
  import { UAParser } from 'ua-parser-js';
@@ -8,7 +7,6 @@ import urlJoin from 'url-join';
8
7
  import { auth } from '@/auth';
9
8
  import { OAUTH_AUTHORIZED } from '@/const/auth';
10
9
  import { LOBE_LOCALE_COOKIE } from '@/const/locale';
11
- import { LOBE_THEME_APPEARANCE } from '@/const/theme';
12
10
  import { isDesktop } from '@/const/version';
13
11
  import { appEnv } from '@/envs/app';
14
12
  import { authEnv } from '@/envs/auth';
@@ -39,10 +37,6 @@ export function defineConfig() {
39
37
  return NextResponse.next();
40
38
  }
41
39
 
42
- // 1. Read user preferences from cookies
43
- const theme = (request.cookies.get(LOBE_THEME_APPEARANCE)?.value ||
44
- parseDefaultThemeFromCountry(request)) as 'dark' | 'light';
45
-
46
40
  // locale has three levels
47
41
  // 1. search params
48
42
  // 2. cookie
@@ -67,17 +61,14 @@ export function defineConfig() {
67
61
  deviceType: device.type,
68
62
  hasCookies: {
69
63
  locale: !!request.cookies.get(LOBE_LOCALE_COOKIE)?.value,
70
- theme: !!request.cookies.get(LOBE_THEME_APPEARANCE)?.value,
71
64
  },
72
65
  locale,
73
- theme,
74
66
  });
75
67
 
76
68
  // 2. Create normalized preference values
77
69
  const route = RouteVariants.serializeVariants({
78
70
  isMobile: device.type === 'mobile',
79
71
  locale,
80
- theme,
81
72
  });
82
73
 
83
74
  logDefault('Serialized route variant: %s', route);
@@ -99,8 +90,8 @@ export function defineConfig() {
99
90
 
100
91
  // refs: https://github.com/lobehub/lobe-chat/pull/5866
101
92
  // new handle segment rewrite: /${route}${originalPathname}
102
- // / -> /zh-CN__0__dark
103
- // /discover -> /zh-CN__0__dark/discover
93
+ // / -> /zh-CN__0
94
+ // /discover -> /zh-CN__0/discover
104
95
  // All SPA routes that use react-router-dom should be rewritten to just /${route}
105
96
  const spaRoutes = [
106
97
  '/chat',
@@ -408,19 +408,4 @@ describe('createPreferenceSlice', () => {
408
408
  expect(result.current.status.noWideScreen).toEqual(false);
409
409
  });
410
410
  });
411
-
412
- describe('switchThemeMode', () => {
413
- it('should switch theme mode', async () => {
414
- const { result } = renderHook(() => useGlobalStore());
415
-
416
- // Perform the action
417
- act(() => {
418
- useGlobalStore.setState({ isStatusInit: true });
419
- result.current.switchThemeMode('light');
420
- });
421
-
422
- // Assert that updateUserSettings was called with the correct theme mode
423
- expect(result.current.status.themeMode).toEqual('light');
424
- });
425
- });
426
411
  });
@@ -1,5 +1,4 @@
1
1
  import { act, renderHook } from '@testing-library/react';
2
- import { ThemeMode } from 'antd-style';
3
2
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
3
  import { withSWR } from '~test-utils';
5
4
 
@@ -117,42 +116,6 @@ describe('generalActionSlice', () => {
117
116
  });
118
117
  });
119
118
 
120
- describe('switchThemeMode', () => {
121
- it('should update theme mode in system status', () => {
122
- const { result } = renderHook(() => useGlobalStore());
123
- const themeMode: ThemeMode = 'dark';
124
-
125
- act(() => {
126
- useGlobalStore.setState({ isStatusInit: true });
127
- result.current.switchThemeMode(themeMode);
128
- });
129
-
130
- expect(result.current.status.themeMode).toBe(themeMode);
131
- });
132
-
133
- it('should not update theme mode if status is not initialized', () => {
134
- const { result } = renderHook(() => useGlobalStore());
135
- const themeMode: ThemeMode = 'dark';
136
-
137
- act(() => {
138
- result.current.switchThemeMode(themeMode);
139
- });
140
-
141
- expect(result.current.status.themeMode).toBe(initialState.status.themeMode);
142
- });
143
-
144
- it('should handle light theme mode', () => {
145
- const { result } = renderHook(() => useGlobalStore());
146
-
147
- act(() => {
148
- useGlobalStore.setState({ isStatusInit: true });
149
- result.current.switchThemeMode('light');
150
- });
151
-
152
- expect(result.current.status.themeMode).toBe('light');
153
- });
154
- });
155
-
156
119
  describe('useInitSystemStatus', () => {
157
120
  it('should reset transient UI states when loading from localStorage', async () => {
158
121
  const mockStatus = {
@@ -1,16 +1,13 @@
1
- import { type ThemeMode } from 'antd-style';
2
1
  import isEqual from 'fast-deep-equal';
3
2
  import { gt, parse, valid } from 'semver';
4
3
  import { type SWRResponse } from 'swr';
5
4
  import type { StateCreator } from 'zustand/vanilla';
6
5
 
7
- import { LOBE_THEME_APPEARANCE } from '@/const/theme';
8
6
  import { CURRENT_VERSION, isDesktop } from '@/const/version';
9
7
  import { useOnlyFetchOnceSWR } from '@/libs/swr';
10
8
  import { globalService } from '@/services/global';
11
9
  import type { SystemStatus } from '@/store/global/initialState';
12
10
  import { type LocaleMode } from '@/types/locale';
13
- import { setCookie } from '@/utils/client/cookie';
14
11
  import { switchLang } from '@/utils/client/switchLang';
15
12
  import { merge } from '@/utils/merge';
16
13
  import { setNamespace } from '@/utils/storeDebug';
@@ -23,7 +20,6 @@ export interface GlobalGeneralAction {
23
20
  openAgentInNewWindow: (agentId: string) => Promise<void>;
24
21
  openTopicInNewWindow: (agentId: string, topicId: string) => Promise<void>;
25
22
  switchLocale: (locale: LocaleMode, params?: { skipBroadcast?: boolean }) => void;
26
- switchThemeMode: (themeMode: ThemeMode, params?: { skipBroadcast?: boolean }) => void;
27
23
  updateSystemStatus: (status: Partial<SystemStatus>, action?: any) => void;
28
24
  useCheckLatestVersion: (enabledCheck?: boolean) => SWRResponse<string>;
29
25
  useInitSystemStatus: () => SWRResponse;
@@ -114,23 +110,6 @@ export const generalActionSlice: StateCreator<
114
110
  })();
115
111
  }
116
112
  },
117
- switchThemeMode: (themeMode, { skipBroadcast } = {}) => {
118
- get().updateSystemStatus({ themeMode });
119
-
120
- setCookie(LOBE_THEME_APPEARANCE, themeMode === 'auto' ? undefined : themeMode);
121
-
122
- if (isDesktop && !skipBroadcast) {
123
- (async () => {
124
- try {
125
- const { ensureElectronIpc } = await import('@/utils/electron/ipc');
126
-
127
- await ensureElectronIpc().system.updateThemeModeHandler(themeMode);
128
- } catch (error) {
129
- console.error('Failed to update theme in main process:', error);
130
- }
131
- })();
132
- }
133
- },
134
113
  updateSystemStatus: (status, action) => {
135
114
  if (!get().isStatusInit) return;
136
115
 
@@ -1,4 +1,3 @@
1
- import type { ThemeMode } from 'antd-style';
2
1
  import type { NavigateFunction } from 'react-router-dom';
3
2
 
4
3
  import { DatabaseLoadingState, type MigrationSQL, type MigrationTableItem } from '@/types/clientDB';
@@ -129,10 +128,6 @@ export interface SystemStatus {
129
128
  showRightPanel?: boolean;
130
129
  showSystemRole?: boolean;
131
130
  systemRoleExpandedMap: Record<string, boolean>;
132
- /**
133
- * theme mode
134
- */
135
- themeMode?: ThemeMode;
136
131
  /**
137
132
  * 是否使用短格式显示 token
138
133
  */
@@ -196,7 +191,6 @@ export const INITIAL_STATUS = {
196
191
  showRightPanel: true,
197
192
  showSystemRole: false,
198
193
  systemRoleExpandedMap: {},
199
- themeMode: 'auto',
200
194
  tokenDisplayFormatShort: true,
201
195
  topicPageSize: 20,
202
196
  zenMode: false,
@@ -87,24 +87,4 @@ describe('systemStatusSelectors', () => {
87
87
  expect(systemStatusSelectors.portalWidth(noPortalWidth)).toBe(400);
88
88
  });
89
89
  });
90
-
91
- describe('theme mode', () => {
92
- it('should return the correct theme', () => {
93
- const s: GlobalState = merge(initialState, {
94
- status: {
95
- themeMode: 'light',
96
- },
97
- });
98
- expect(systemStatusSelectors.themeMode(s)).toBe('light');
99
- });
100
-
101
- it('should return auto if not set', () => {
102
- const s: GlobalState = merge(initialState, {
103
- status: {
104
- themeMode: undefined,
105
- },
106
- });
107
- expect(systemStatusSelectors.themeMode(s)).toBe('auto');
108
- });
109
- });
110
90
  });
@@ -23,7 +23,6 @@ const showImagePanel = (s: GlobalState) => s.status.showImagePanel;
23
23
  const showImageTopicPanel = (s: GlobalState) => s.status.showImageTopicPanel;
24
24
  const hidePWAInstaller = (s: GlobalState) => s.status.hidePWAInstaller;
25
25
  const isShowCredit = (s: GlobalState) => s.status.isShowCredit;
26
- const themeMode = (s: GlobalState) => s.status.themeMode || 'auto';
27
26
  const language = (s: GlobalState) => s.status.language || 'auto';
28
27
 
29
28
  const showChatHeader = (s: GlobalState) => !s.status.zenMode;
@@ -80,7 +79,6 @@ export const systemStatusSelectors = {
80
79
  showRightPanel,
81
80
  showSystemRole,
82
81
  systemStatus,
83
- themeMode,
84
82
  tokenDisplayFormatShort,
85
83
  topicGroupKeys,
86
84
  topicPageSize,
@@ -16,8 +16,6 @@ export default ({ token }: { prefixCls: string; token: Theme }) => css`
16
16
  min-height: 100dvh;
17
17
  max-height: 100dvh;
18
18
 
19
- background: ${token.colorBgLayout};
20
-
21
19
  @media (min-device-width: 576px) {
22
20
  overflow: hidden;
23
21
  }
@@ -11,7 +11,6 @@ describe('RouteVariants', () => {
11
11
  expect(DEFAULT_VARIANTS).toEqual({
12
12
  isMobile: false,
13
13
  locale: DEFAULT_LANG,
14
- theme: 'light',
15
14
  });
16
15
  });
17
16
  });
@@ -21,30 +20,27 @@ describe('RouteVariants', () => {
21
20
  const variants: IRouteVariants = {
22
21
  isMobile: false,
23
22
  locale: 'en-US',
24
- theme: 'light',
25
23
  };
26
24
  const result = RouteVariants.serializeVariants(variants);
27
- expect(result).toBe('en-US__0__light');
25
+ expect(result).toBe('en-US__0');
28
26
  });
29
27
 
30
28
  it('should serialize variants with mobile enabled', () => {
31
29
  const variants: IRouteVariants = {
32
30
  isMobile: true,
33
31
  locale: 'zh-CN',
34
- theme: 'dark',
35
32
  };
36
33
  const result = RouteVariants.serializeVariants(variants);
37
- expect(result).toBe('zh-CN__1__dark');
34
+ expect(result).toBe('zh-CN__1');
38
35
  });
39
36
 
40
37
  it('should serialize variants with different locales', () => {
41
38
  const variants: IRouteVariants = {
42
39
  isMobile: false,
43
40
  locale: 'ja-JP',
44
- theme: 'light',
45
41
  };
46
42
  const result = RouteVariants.serializeVariants(variants);
47
- expect(result).toBe('ja-JP__0__light');
43
+ expect(result).toBe('ja-JP__0');
48
44
  });
49
45
 
50
46
  it('should serialize variants with custom colors', () => {
@@ -53,31 +49,28 @@ describe('RouteVariants', () => {
53
49
  locale: 'en-US',
54
50
  neutralColor: '#cccccc',
55
51
  primaryColor: '#ff0000',
56
- theme: 'dark',
57
52
  };
58
53
  const result = RouteVariants.serializeVariants(variants);
59
- expect(result).toBe('en-US__1__dark');
54
+ expect(result).toBe('en-US__1');
60
55
  });
61
56
  });
62
57
 
63
58
  describe('deserializeVariants', () => {
64
59
  it('should deserialize valid serialized string', () => {
65
- const serialized = 'en-US__0__light';
60
+ const serialized = 'en-US__0';
66
61
  const result = RouteVariants.deserializeVariants(serialized);
67
62
  expect(result).toEqual({
68
63
  isMobile: false,
69
64
  locale: 'en-US',
70
- theme: 'light',
71
65
  });
72
66
  });
73
67
 
74
68
  it('should deserialize mobile variants', () => {
75
- const serialized = 'zh-CN__1__dark';
69
+ const serialized = 'zh-CN__1';
76
70
  const result = RouteVariants.deserializeVariants(serialized);
77
71
  expect(result).toEqual({
78
72
  isMobile: true,
79
73
  locale: 'zh-CN',
80
- theme: 'dark',
81
74
  });
82
75
  });
83
76
 
@@ -94,22 +87,11 @@ describe('RouteVariants', () => {
94
87
  });
95
88
 
96
89
  it('should handle invalid locale by falling back to default', () => {
97
- const serialized = 'invalid-locale__0__light';
90
+ const serialized = 'invalid-locale__0';
98
91
  const result = RouteVariants.deserializeVariants(serialized);
99
92
  expect(result).toEqual({
100
93
  isMobile: false,
101
94
  locale: DEFAULT_LANG,
102
- theme: 'light',
103
- });
104
- });
105
-
106
- it('should handle invalid theme by falling back to default', () => {
107
- const serialized = 'en-US__0__invalid-theme';
108
- const result = RouteVariants.deserializeVariants(serialized);
109
- expect(result).toEqual({
110
- isMobile: false,
111
- locale: 'en-US',
112
- theme: 'light',
113
95
  });
114
96
  });
115
97
 
@@ -120,19 +102,19 @@ describe('RouteVariants', () => {
120
102
  });
121
103
 
122
104
  it('should handle isMobile value correctly for "0"', () => {
123
- const serialized = 'en-US__0__dark';
105
+ const serialized = 'en-US__0';
124
106
  const result = RouteVariants.deserializeVariants(serialized);
125
107
  expect(result.isMobile).toBe(false);
126
108
  });
127
109
 
128
110
  it('should handle isMobile value correctly for "1"', () => {
129
- const serialized = 'en-US__1__dark';
111
+ const serialized = 'en-US__1';
130
112
  const result = RouteVariants.deserializeVariants(serialized);
131
113
  expect(result.isMobile).toBe(true);
132
114
  });
133
115
 
134
116
  it('should handle isMobile value correctly for other values', () => {
135
- const serialized = 'en-US__2__dark';
117
+ const serialized = 'en-US__2';
136
118
  const result = RouteVariants.deserializeVariants(serialized);
137
119
  expect(result.isMobile).toBe(false);
138
120
  });
@@ -141,25 +123,23 @@ describe('RouteVariants', () => {
141
123
  describe('getVariantsFromProps', () => {
142
124
  it('should extract and deserialize variants from props', async () => {
143
125
  const props: DynamicLayoutProps = {
144
- params: Promise.resolve({ variants: 'en-US__0__light' }),
126
+ params: Promise.resolve({ variants: 'en-US__0' }),
145
127
  };
146
128
  const result = await RouteVariants.getVariantsFromProps(props);
147
129
  expect(result).toEqual({
148
130
  isMobile: false,
149
131
  locale: 'en-US',
150
- theme: 'light',
151
132
  });
152
133
  });
153
134
 
154
135
  it('should handle mobile variants from props', async () => {
155
136
  const props: DynamicLayoutProps = {
156
- params: Promise.resolve({ variants: 'zh-CN__1__dark' }),
137
+ params: Promise.resolve({ variants: 'zh-CN__1' }),
157
138
  };
158
139
  const result = await RouteVariants.getVariantsFromProps(props);
159
140
  expect(result).toEqual({
160
141
  isMobile: true,
161
142
  locale: 'zh-CN',
162
- theme: 'dark',
163
143
  });
164
144
  });
165
145
 
@@ -175,7 +155,7 @@ describe('RouteVariants', () => {
175
155
  describe('getIsMobile', () => {
176
156
  it('should extract isMobile as false from props', async () => {
177
157
  const props: DynamicLayoutProps = {
178
- params: Promise.resolve({ variants: 'en-US__0__light' }),
158
+ params: Promise.resolve({ variants: 'en-US__0' }),
179
159
  };
180
160
  const result = await RouteVariants.getIsMobile(props);
181
161
  expect(result).toBe(false);
@@ -183,7 +163,7 @@ describe('RouteVariants', () => {
183
163
 
184
164
  it('should extract isMobile as true from props', async () => {
185
165
  const props: DynamicLayoutProps = {
186
- params: Promise.resolve({ variants: 'en-US__1__dark' }),
166
+ params: Promise.resolve({ variants: 'en-US__1' }),
187
167
  };
188
168
  const result = await RouteVariants.getIsMobile(props);
189
169
  expect(result).toBe(true);
@@ -201,7 +181,7 @@ describe('RouteVariants', () => {
201
181
  describe('getLocale', () => {
202
182
  it('should extract locale from props', async () => {
203
183
  const props: DynamicLayoutProps = {
204
- params: Promise.resolve({ variants: 'zh-CN__0__light' }),
184
+ params: Promise.resolve({ variants: 'zh-CN__0' }),
205
185
  };
206
186
  const result = await RouteVariants.getLocale(props);
207
187
  expect(result).toBe('zh-CN');
@@ -209,7 +189,7 @@ describe('RouteVariants', () => {
209
189
 
210
190
  it('should extract different locale from props', async () => {
211
191
  const props: DynamicLayoutProps = {
212
- params: Promise.resolve({ variants: 'ja-JP__1__dark' }),
192
+ params: Promise.resolve({ variants: 'ja-JP__1' }),
213
193
  };
214
194
  const result = await RouteVariants.getLocale(props);
215
195
  expect(result).toBe('ja-JP');
@@ -225,7 +205,7 @@ describe('RouteVariants', () => {
225
205
 
226
206
  it('should return default locale for invalid locale in props', async () => {
227
207
  const props: DynamicLayoutProps = {
228
- params: Promise.resolve({ variants: 'invalid-locale__0__light' }),
208
+ params: Promise.resolve({ variants: 'invalid-locale__0' }),
229
209
  };
230
210
  const result = await RouteVariants.getLocale(props);
231
211
  expect(result).toBe(DEFAULT_LANG);
@@ -254,24 +234,14 @@ describe('RouteVariants', () => {
254
234
  });
255
235
  });
256
236
 
257
- it('should create variants with custom theme', () => {
258
- const result = RouteVariants.createVariants({ theme: 'dark' });
259
- expect(result).toEqual({
260
- ...DEFAULT_VARIANTS,
261
- theme: 'dark',
262
- });
263
- });
264
-
265
237
  it('should create variants with multiple custom options', () => {
266
238
  const result = RouteVariants.createVariants({
267
239
  isMobile: true,
268
240
  locale: 'ja-JP',
269
- theme: 'dark',
270
241
  });
271
242
  expect(result).toEqual({
272
243
  isMobile: true,
273
244
  locale: 'ja-JP',
274
- theme: 'dark',
275
245
  });
276
246
  });
277
247
 
@@ -293,14 +263,12 @@ describe('RouteVariants', () => {
293
263
  locale: 'zh-CN',
294
264
  neutralColor: '#aaaaaa',
295
265
  primaryColor: '#00ff00',
296
- theme: 'dark',
297
266
  });
298
267
  expect(result).toEqual({
299
268
  isMobile: true,
300
269
  locale: 'zh-CN',
301
270
  neutralColor: '#aaaaaa',
302
271
  primaryColor: '#00ff00',
303
- theme: 'dark',
304
272
  });
305
273
  });
306
274
  });
@@ -310,7 +278,6 @@ describe('RouteVariants', () => {
310
278
  const original: IRouteVariants = {
311
279
  isMobile: true,
312
280
  locale: 'zh-CN',
313
- theme: 'dark',
314
281
  };
315
282
  const serialized = RouteVariants.serializeVariants(original);
316
283
  const deserialized = RouteVariants.deserializeVariants(serialized);
@@ -329,7 +296,6 @@ describe('RouteVariants', () => {
329
296
  const original: IRouteVariants = {
330
297
  isMobile: false,
331
298
  locale: locale as any,
332
- theme: 'light',
333
299
  };
334
300
  const serialized = RouteVariants.serializeVariants(original);
335
301
  const deserialized = RouteVariants.deserializeVariants(serialized);
@@ -3,7 +3,6 @@ import { RouteVariants } from '@lobechat/desktop-bridge';
3
3
  import { type DynamicLayoutProps } from '@/types/next';
4
4
 
5
5
  export { LOBE_LOCALE_COOKIE } from '@/const/locale';
6
- export { LOBE_THEME_APPEARANCE } from '@/const/theme';
7
6
  export {
8
7
  DEFAULT_LANG,
9
8
  DEFAULT_VARIANTS,