@lobehub/lobehub 2.0.0-next.324 → 2.0.0-next.325

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 (66) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/CLAUDE.md +4 -0
  3. package/apps/desktop/src/main/core/browser/Browser.ts +40 -1
  4. package/apps/desktop/src/main/core/infrastructure/I18nManager.ts +0 -11
  5. package/changelog/v1.json +5 -0
  6. package/package.json +2 -2
  7. package/packages/database/src/models/__tests__/session.test.ts +0 -29
  8. package/src/app/[variants]/(main)/agent/features/Conversation/AgentWelcome/OpeningQuestions.tsx +0 -2
  9. package/src/app/[variants]/(main)/community/(detail)/agent/features/Sidebar/TocList/index.tsx +0 -36
  10. package/src/app/[variants]/(main)/community/(list)/_layout/Header.tsx +0 -2
  11. package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx +0 -4
  12. package/src/app/[variants]/(main)/group/features/Conversation/ConversationArea.tsx +0 -7
  13. package/src/app/[variants]/(main)/group/features/Conversation/MainChatInput/GroupChat.tsx +0 -2
  14. package/src/app/[variants]/(main)/home/_layout/Body/index.tsx +0 -2
  15. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/useDropdownMenu.tsx +0 -6
  16. package/src/app/[variants]/(main)/page/_layout/Body/useDropdownMenu.tsx +0 -15
  17. package/src/app/[variants]/(main)/resource/features/DndContextWrapper.tsx +0 -5
  18. package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/index.tsx +0 -1
  19. package/src/app/[variants]/(main)/settings/provider/features/ModelList/ModelItem.tsx +0 -10
  20. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/Checker.tsx +1 -1
  21. package/src/app/[variants]/(mobile)/(home)/features/SessionListContent/List/Item/Actions.tsx +0 -1
  22. package/src/app/[variants]/layout.tsx +0 -2
  23. package/src/envs/__tests__/app.test.ts +0 -6
  24. package/src/features/ChatInput/ActionBar/Knowledge/useControls.tsx +0 -22
  25. package/src/features/ChatInput/store/action.ts +0 -2
  26. package/src/features/Conversation/Messages/Task/TaskDetailPanel/index.tsx +1 -13
  27. package/src/features/DataImporter/ImportDetail.tsx +0 -20
  28. package/src/features/DevPanel/features/Table/TableCell.tsx +1 -36
  29. package/src/features/DevPanel/index.tsx +0 -9
  30. package/src/features/ModelSwitchPanel/__mocks__/mockEnabledChatModels.ts +159 -0
  31. package/src/features/ModelSwitchPanel/components/List/{VirtualItemRenderer.tsx → ListItemRenderer.tsx} +15 -25
  32. package/src/features/ModelSwitchPanel/components/List/MultipleProvidersModelItem.tsx +95 -69
  33. package/src/features/ModelSwitchPanel/components/List/index.tsx +39 -40
  34. package/src/features/ModelSwitchPanel/components/PanelContent.tsx +0 -8
  35. package/src/features/ModelSwitchPanel/hooks/{useBuildVirtualItems.ts → useBuildListItems.ts} +7 -17
  36. package/src/features/ModelSwitchPanel/index.tsx +24 -23
  37. package/src/features/ModelSwitchPanel/styles.ts +3 -0
  38. package/src/features/ModelSwitchPanel/types.ts +3 -8
  39. package/src/features/ModelSwitchPanel/utils.ts +2 -2
  40. package/src/features/NavPanel/SideBarDrawer.tsx +12 -2
  41. package/src/features/Portal/GroupThread/Body/index.tsx +0 -6
  42. package/src/features/ResourceManager/components/Header/AddButton.tsx +0 -16
  43. package/src/features/ShareModal/ShareImage/index.tsx +0 -8
  44. package/src/hooks/useProviderName.ts +0 -1
  45. package/src/layout/GlobalProvider/Locale.tsx +0 -12
  46. package/src/layout/GlobalProvider/index.tsx +0 -1
  47. package/src/libs/better-auth/sso/helpers.ts +0 -1
  48. package/src/libs/next/config/define-config.ts +5 -0
  49. package/src/locales/create.ts +0 -17
  50. package/src/services/aiChat.ts +0 -4
  51. package/src/services/debug.ts +1 -34
  52. package/src/services/models.ts +0 -15
  53. package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +0 -9
  54. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +0 -3
  55. package/src/store/chat/slices/aiChat/actions/index.ts +1 -3
  56. package/src/store/file/slices/chat/action.test.ts +0 -89
  57. package/src/store/file/slices/chunk/selectors.ts +0 -1
  58. package/src/store/file/slices/fileManager/selectors.ts +0 -1
  59. package/src/store/file/slices/tts/selectors.ts +0 -2
  60. package/src/store/tool/slices/customPlugin/index.ts +0 -1
  61. package/src/store/tool/slices/mcpStore/index.ts +0 -1
  62. package/src/store/tool/slices/oldStore/index.ts +0 -1
  63. package/src/store/tool/slices/plugin/index.ts +0 -1
  64. package/src/styles/global.ts +6 -0
  65. package/src/utils/router.tsx +1 -7
  66. package/src/utils/server/parseModels.ts +0 -1
@@ -1,4 +1,10 @@
1
- import { Popover } from '@lobehub/ui';
1
+ import {
2
+ DropdownMenuPopup,
3
+ DropdownMenuPortal,
4
+ DropdownMenuPositioner,
5
+ DropdownMenuRoot,
6
+ DropdownMenuTrigger,
7
+ } from '@lobehub/ui';
2
8
  import { memo, useCallback, useState } from 'react';
3
9
 
4
10
  import { PanelContent } from './components/PanelContent';
@@ -16,8 +22,6 @@ const ModelSwitchPanel = memo<ModelSwitchPanelProps>(
16
22
  provider: providerProp,
17
23
  }) => {
18
24
  const [internalOpen, setInternalOpen] = useState(false);
19
-
20
- // Use controlled open if provided, otherwise use internal state
21
25
  const isOpen = open ?? internalOpen;
22
26
 
23
27
  const handleOpenChange = useCallback(
@@ -29,26 +33,23 @@ const ModelSwitchPanel = memo<ModelSwitchPanelProps>(
29
33
  );
30
34
 
31
35
  return (
32
- <Popover
33
- classNames={{
34
- content: styles.container,
35
- }}
36
- content={
37
- <PanelContent
38
- isOpen={isOpen}
39
- model={modelProp}
40
- onModelChange={onModelChange}
41
- onOpenChange={handleOpenChange}
42
- provider={providerProp}
43
- />
44
- }
45
- nativeButton={false}
46
- onOpenChange={handleOpenChange}
47
- open={isOpen}
48
- placement={placement}
49
- >
50
- {children}
51
- </Popover>
36
+ <DropdownMenuRoot onOpenChange={handleOpenChange} open={isOpen}>
37
+ <DropdownMenuTrigger nativeButton={false} openOnHover>
38
+ {children}
39
+ </DropdownMenuTrigger>
40
+ <DropdownMenuPortal>
41
+ <DropdownMenuPositioner hoverTrigger placement={placement}>
42
+ <DropdownMenuPopup className={styles.container}>
43
+ <PanelContent
44
+ model={modelProp}
45
+ onModelChange={onModelChange}
46
+ onOpenChange={handleOpenChange}
47
+ provider={providerProp}
48
+ />
49
+ </DropdownMenuPopup>
50
+ </DropdownMenuPositioner>
51
+ </DropdownMenuPortal>
52
+ </DropdownMenuRoot>
52
53
  );
53
54
  },
54
55
  );
@@ -69,6 +69,9 @@ export const styles = createStaticStyles(({ css, cssVar }) => ({
69
69
  }
70
70
  }
71
71
  `,
72
+ menuItemActive: css`
73
+ background: ${cssVar.colorFillTertiary};
74
+ `,
72
75
  toolbar: css`
73
76
  border-block-end: 1px solid ${cssVar.colorBorderSecondary};
74
77
  `,
@@ -1,3 +1,4 @@
1
+ import type { DropdownMenuPlacement } from '@lobehub/ui';
1
2
  import type { AiModelForSelect } from 'model-bank';
2
3
  import type { ReactNode } from 'react';
3
4
 
@@ -16,7 +17,7 @@ export interface ModelWithProviders {
16
17
  }>;
17
18
  }
18
19
 
19
- export type VirtualItem =
20
+ export type ListItem =
20
21
  | {
21
22
  data: ModelWithProviders;
22
23
  type: 'model-item-single';
@@ -42,13 +43,7 @@ export type VirtualItem =
42
43
  type: 'no-provider';
43
44
  };
44
45
 
45
- export type DropdownPlacement =
46
- | 'bottom'
47
- | 'bottomLeft'
48
- | 'bottomRight'
49
- | 'top'
50
- | 'topLeft'
51
- | 'topRight';
46
+ export type DropdownPlacement = DropdownMenuPlacement;
52
47
 
53
48
  export interface ModelSwitchPanelProps {
54
49
  children?: ReactNode;
@@ -1,8 +1,8 @@
1
- import type { VirtualItem } from './types';
1
+ import type { ListItem } from './types';
2
2
 
3
3
  export const menuKey = (provider: string, model: string) => `${provider}-${model}`;
4
4
 
5
- export const getVirtualItemKey = (item: VirtualItem): string => {
5
+ export const getListItemKey = (item: ListItem): string => {
6
6
  switch (item.type) {
7
7
  case 'model-item-single':
8
8
  case 'model-item-multiple': {
@@ -23,6 +23,7 @@ interface SideBarDrawerProps {
23
23
 
24
24
  const SideBarDrawer = memo<SideBarDrawerProps>(
25
25
  ({ subHeader, open, onClose, children, title, action }) => {
26
+ const size = 280;
26
27
  return (
27
28
  <Drawer
28
29
  closable={false}
@@ -33,9 +34,13 @@ const SideBarDrawer = memo<SideBarDrawerProps>(
33
34
  open={open}
34
35
  placement="left"
35
36
  rootStyle={{
37
+ bottom: 0,
38
+ overflow: 'hidden',
36
39
  position: 'absolute',
40
+ top: 0,
41
+ width: `${size}px`,
37
42
  }}
38
- size={280}
43
+ size={size}
39
44
  styles={{
40
45
  body: {
41
46
  background: cssVar.colorBgLayout,
@@ -58,7 +63,12 @@ const SideBarDrawer = memo<SideBarDrawerProps>(
58
63
  <SideBarHeaderLayout
59
64
  left={
60
65
  typeof title === 'string' ? (
61
- <Text ellipsis fontSize={14} style={{ paddingLeft: 4 }} weight={400}>
66
+ <Text
67
+ ellipsis
68
+ fontSize={14}
69
+ style={{ fontWeight: 600, paddingLeft: 8 }}
70
+ weight={400}
71
+ >
62
72
  {title}
63
73
  </Text>
64
74
  ) : (
@@ -3,20 +3,14 @@
3
3
  import { Flexbox } from '@lobehub/ui';
4
4
  import { memo } from 'react';
5
5
 
6
- // import ChatInput from '@/app/[variants]/(main)/chat/features/Conversation/ChatInput';
7
- // import { useChatGroupStore } from '@/store/chatGroup';
8
-
9
6
  import ThreadChatList from './ThreadChatList';
10
7
 
11
8
  const Body = memo(() => {
12
- // const activeThreadAgentId = useChatGroupStore((s) => s.activeThreadAgentId);
13
-
14
9
  return (
15
10
  <Flexbox height={'100%'}>
16
11
  <Flexbox flex={1} style={{ overflow: 'hidden', position: 'relative' }}>
17
12
  <ThreadChatList />
18
13
  </Flexbox>
19
- {/*{activeThreadAgentId && <ChatInput targetMemberId={activeThreadAgentId} />}*/}
20
14
  </Flexbox>
21
15
  );
22
16
  });
@@ -187,22 +187,6 @@ const AddButton = () => {
187
187
  label: 'Notion',
188
188
  onClick: handleOpenNotionGuide,
189
189
  },
190
- // {
191
- // icon: <Icon icon={Notion} />,
192
- // key: 'connect-google-drive',
193
- // label: 'Google Drive',
194
- // onClick: () => {
195
- // // TODO: Implement Google Drive connection
196
- // },
197
- // },
198
- // {
199
- // icon: <Icon icon={Notion} />,
200
- // key: 'connect-onedrive',
201
- // label: 'OneDrive',
202
- // onClick: () => {
203
- // // TODO: Implement OneDrive connection
204
- // },
205
- // },
206
190
  ],
207
191
  icon: <Icon icon={Link} />,
208
192
  key: 'connect',
@@ -56,14 +56,6 @@ const ShareImage = memo<{ mobile?: boolean }>(() => {
56
56
  name: 'withSystemRole',
57
57
  valuePropName: 'checked',
58
58
  },
59
- // {
60
- // children: <Switch />,
61
- // label: t('shareModal.withBackground'),
62
- // layout: 'horizontal',
63
- // minWidth: undefined,
64
- // name: 'withBackground',
65
- // valuePropName: 'checked',
66
- // },
67
59
  {
68
60
  children: <Switch />,
69
61
  label: t('shareModal.withFooter'),
@@ -1,7 +1,6 @@
1
1
  import { DEFAULT_MODEL_PROVIDER_LIST } from 'model-bank/modelProviders';
2
2
 
3
3
  export const useProviderName = (provider: string) => {
4
- // const { t } = useTranslation('modelProvider');
5
4
  const providerCard = DEFAULT_MODEL_PROVIDER_LIST.find((p) => p.id === provider);
6
5
 
7
6
  return providerCard?.name || provider;
@@ -12,7 +12,6 @@ import { getAntdLocale } from '@/utils/locale';
12
12
  import Editor from './Editor';
13
13
 
14
14
  const updateDayjs = async (lang: string) => {
15
- // load default lang
16
15
  let dayJSLocale;
17
16
  try {
18
17
  // dayjs locale is using `en` instead of `en-US`
@@ -38,21 +37,10 @@ const Locale = memo<LocaleLayoutProps>(({ children, defaultLang, antdLocale }) =
38
37
  const [lang, setLang] = useState(defaultLang);
39
38
  const [locale, setLocale] = useState(antdLocale);
40
39
 
41
- // if run on server side, init i18n instance everytime
42
40
  if (isOnServerSide) {
43
- // use sync mode to init instantly
44
41
  i18n.init({ initAsync: false });
45
-
46
- // load the dayjs locale
47
- // if (lang) {
48
- // const dayJSLocale = require(`dayjs/locale/${lang!.toLowerCase()}.js`);
49
- //
50
- // dayjs.locale(dayJSLocale);
51
- // }
52
42
  } else {
53
- // if on browser side, init i18n instance only once
54
43
  if (!i18n.instance.isInitialized)
55
- // console.debug('locale', lang);
56
44
  i18n.init().then(async () => {
57
45
  if (!lang) return;
58
46
 
@@ -67,7 +67,6 @@ const GlobalLayout = async ({
67
67
  <QueryProvider>
68
68
  <StoreInitialization />
69
69
  <FaviconProvider>
70
- {/* {process.env.NODE_ENV === 'development' && <FaviconTestPanel />} */}
71
70
  <GroupWizardProvider>
72
71
  <DragUploadProvider>
73
72
  <LazyMotion features={domMax}>
@@ -55,7 +55,6 @@ export const buildOidcConfig = ({
55
55
  pkce,
56
56
  providerId,
57
57
  scopes,
58
- // ...fallbackEndpoints,
59
58
  ...overrides,
60
59
  } satisfies GenericOAuthConfig;
61
60
  };
@@ -308,6 +308,11 @@ export function defineConfig(config: CustomNextConfig) {
308
308
  permanent: false,
309
309
  source: '/repos',
310
310
  },
311
+ {
312
+ destination: '/',
313
+ permanent: true,
314
+ source: '/chat',
315
+ },
311
316
  ...(config.redirects ?? []),
312
317
  ],
313
318
 
@@ -45,21 +45,6 @@ export const createI18nNext = (lang?: string) => {
45
45
  return instance.init({
46
46
  debug: debugMode,
47
47
  defaultNS: ['error', 'common', 'chat'],
48
-
49
- // detection: {
50
- // caches: ['cookie'],
51
- // cookieMinutes: 60 * 24 * COOKIE_CACHE_DAYS,
52
- // /**
53
- // Set `sameSite` to `lax` so that the i18n cookie can be passed to the
54
- // server side when returning from the OAuth authorization website.
55
- // ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
56
- // discussion: https://github.com/lobehub/lobe-chat/pull/1474
57
- // */
58
- // cookieOptions: {
59
- // sameSite: 'lax',
60
- // },
61
- // lookupCookie: LOBE_LOCALE_COOKIE,
62
- // },
63
48
  fallbackLng: DEFAULT_LANG,
64
49
 
65
50
  initAsync,
@@ -67,8 +52,6 @@ export const createI18nNext = (lang?: string) => {
67
52
  interpolation: {
68
53
  escapeValue: false,
69
54
  },
70
- // Use flat keys with dots (e.g. "notFound.title") instead of nested objects.
71
- // This keeps both runtime lookup and TS key inference consistent.
72
55
  keySeparator: false,
73
56
 
74
57
  lng: lang,
@@ -20,10 +20,6 @@ class AiChatService {
20
20
  signal: abortController?.signal,
21
21
  });
22
22
  };
23
-
24
- // sendGroupMessageInServer = async (params: SendMessageServerParams) => {
25
- // return lambdaClient.aiChat.sendGroupMessageInServer.mutate(cleanObject(params));
26
- // };
27
23
  }
28
24
 
29
25
  export const aiChatService = new AiChatService();
@@ -1,38 +1,5 @@
1
1
  class DebugService {
2
- async insertLargeDataToDB() {
3
- // await DEBUG_MODEL.createRandomData({
4
- // messageCount: 100_000,
5
- // sessionCount: 40,
6
- // startIndex: 0,
7
- // topicCount: 200,
8
- // });
9
- //
10
- // console.log('Inserted 100k');
11
- //
12
- // await DEBUG_MODEL.createRandomData({
13
- // messageCount: 300_000,
14
- // sessionCount: 40,
15
- // startIndex: 100_001,
16
- // topicCount: 200,
17
- // });
18
- // console.log('Inserted 400k');
19
- //
20
- // await DEBUG_MODEL.createRandomData({
21
- // messageCount: 300_000,
22
- // sessionCount: 40,
23
- // startIndex: 400_001,
24
- // topicCount: 200,
25
- // });
26
- // console.log('Inserted 700k');
27
- //
28
- // await DEBUG_MODEL.createRandomData({
29
- // messageCount: 300_000,
30
- // sessionCount: 40,
31
- // startIndex: 700_001,
32
- // topicCount: 200,
33
- // });
34
- // console.log('Inserted 1M');
35
- }
2
+ async insertLargeDataToDB() {}
36
3
  }
37
4
 
38
5
  export const debugService = new DebugService();
@@ -25,10 +25,8 @@ export type ProgressCallback = (progress: ModelProgressInfo) => void;
25
25
  export type ErrorCallback = (error: { message: string }) => void;
26
26
 
27
27
  export class ModelsService {
28
- // Controller for aborting downloads
29
28
  private _abortController: AbortController | null = null;
30
29
 
31
- // Get model list
32
30
  getModels = async (provider: string): Promise<ChatModelCard[] | undefined> => {
33
31
  const headers = await createHeaderWithAuth({
34
32
  headers: { 'Content-Type': 'application/json' },
@@ -66,7 +64,6 @@ export class ModelsService {
66
64
  { onProgress }: { onError?: ErrorCallback; onProgress?: ProgressCallback } = {},
67
65
  ): Promise<void> => {
68
66
  try {
69
- // Create a new AbortController
70
67
  this._abortController = new AbortController();
71
68
  const signal = this._abortController.signal;
72
69
 
@@ -99,7 +96,6 @@ export class ModelsService {
99
96
  throw await getMessageError(res);
100
97
  }
101
98
 
102
- // Process response stream
103
99
  if (res.body) {
104
100
  await this.processModelPullStream(res, { onProgress });
105
101
  }
@@ -112,14 +108,11 @@ export class ModelsService {
112
108
  console.error('download model error:', error);
113
109
  throw error;
114
110
  } finally {
115
- // Clean up AbortController
116
111
  this._abortController = null;
117
112
  }
118
113
  };
119
114
 
120
- // Abort model download
121
115
  abortPull = () => {
122
- // Use AbortController to abort download
123
116
  if (this._abortController) {
124
117
  this._abortController.abort();
125
118
  this._abortController = null;
@@ -136,17 +129,14 @@ export class ModelsService {
136
129
  response: Response,
137
130
  { onProgress, onError }: { onError?: ErrorCallback; onProgress?: ProgressCallback },
138
131
  ): Promise<void> => {
139
- // Process response stream
140
132
  const reader = response.body?.getReader();
141
133
  if (!reader) return;
142
134
 
143
- // Read and process stream data
144
135
  // eslint-disable-next-line no-constant-condition
145
136
  while (true) {
146
137
  const { done, value } = await reader.read();
147
138
  if (done) break;
148
139
 
149
- // Parse progress data
150
140
  const progressText = new TextDecoder().decode(value);
151
141
  // One line may contain multiple progress updates
152
142
  const progressUpdates = progressText.trim().split('\n');
@@ -162,10 +152,6 @@ export class ModelsService {
162
152
 
163
153
  if (progress.status === 'canceled') {
164
154
  console.log('progress:', progress);
165
- // const abortError = new Error('abort');
166
- // abortError.name = 'AbortError';
167
- //
168
- // throw abortError;
169
155
  }
170
156
 
171
157
  if (progress.status === 'error') {
@@ -173,7 +159,6 @@ export class ModelsService {
173
159
  throw new Error(progress.error);
174
160
  }
175
161
 
176
- // Call progress callback
177
162
  if (progress.completed !== undefined || progress.status) {
178
163
  onProgress?.(progress);
179
164
  }
@@ -120,11 +120,6 @@ export const createGroupOrchestrationExecutors = (
120
120
  };
121
121
  }
122
122
 
123
- // Variable to capture the decision from tool handler
124
- // let decision: ExecutorResult['type'] | undefined;
125
- // let decisionParams: Record<string, unknown> = {};
126
- // let skipCallSupervisor = false;
127
-
128
123
  // Execute Supervisor agent with the supervisor's agentId in context
129
124
  // Mark isSupervisor=true so assistant messages get metadata.isSupervisor for UI rendering
130
125
  // Note: Don't pass operationId - let it create a new child operation (same as call_agent)
@@ -139,10 +134,6 @@ export const createGroupOrchestrationExecutors = (
139
134
 
140
135
  log(`[${sessionLogId}] Supervisor agent finished`);
141
136
 
142
- // Check what decision was made by the supervisor
143
- // This is captured from the groupOrchestration callbacks registered by tools
144
- // const orchestrationCallbacks = get().getGroupOrchestrationCallbacks();
145
-
146
137
  // If no tool was called (supervisor finished normally), end orchestration
147
138
  // The actual decision is captured via the afterCompletion callbacks
148
139
  // For now, return a finish decision if we reach here
@@ -399,9 +399,6 @@ export const conversationLifecycle: StateCreator<
399
399
  skipCreateFirstMessage: true,
400
400
  });
401
401
 
402
- //
403
- // // if there is relative files, then add files to agent
404
- // // only available in server mode
405
402
  const userFiles = dbMessageSelectors
406
403
  .dbUserFiles(get())
407
404
  .map((f) => f?.id)
@@ -14,9 +14,7 @@ export interface ChatAIChatAction
14
14
  ConversationLifecycleAction,
15
15
  ConversationControlAction,
16
16
  StreamingExecutorAction,
17
- StreamingStatesAction {
18
- /**/
19
- }
17
+ StreamingStatesAction {}
20
18
 
21
19
  export const chatAiChat: StateCreator<
22
20
  ChatStore,
@@ -16,7 +16,6 @@ vi.mock('@/components/AntdStaticMethods', () => ({
16
16
  },
17
17
  }));
18
18
 
19
- // mock the arrayBuffer
20
19
  beforeAll(() => {
21
20
  Object.defineProperty(File.prototype, 'arrayBuffer', {
22
21
  writable: true,
@@ -33,7 +32,6 @@ beforeAll(() => {
33
32
  });
34
33
 
35
34
  beforeEach(() => {
36
- // Reset all mocks before each test
37
35
  vi.resetAllMocks();
38
36
  });
39
37
 
@@ -41,7 +39,6 @@ describe('useFileStore:chat', () => {
41
39
  it('clearChatUploadFileList should clear the inputFilesList', () => {
42
40
  const { result } = renderHook(() => useStore());
43
41
 
44
- // Populate the list to clear it later
45
42
  act(() => {
46
43
  useStore.setState({ chatUploadFileList: [{ id: 'abc' }] as any });
47
44
  });
@@ -54,90 +51,4 @@ describe('useFileStore:chat', () => {
54
51
 
55
52
  expect(result.current.chatUploadFileList).toEqual([]);
56
53
  });
57
-
58
- // it('removeFile should call fileService.removeFile and update the store', async () => {
59
- // const { result } = renderHook(() => useStore());
60
- //
61
- // const fileId = 'test-id';
62
- //
63
- // // Mock the fileService.removeFile to resolve
64
- // vi.spyOn(fileService, 'removeFile').mockResolvedValue(undefined);
65
- //
66
- // // Populate the list to remove an item later
67
- // act(() => {
68
- // useStore.setState(({ inputFilesList }) => ({ inputFilesList: [...inputFilesList, fileId] }));
69
- // // // result.current.inputFilesList.push(fileId);
70
- // });
71
- //
72
- // await act(async () => {
73
- // await result.current.removeFile(fileId);
74
- // });
75
- //
76
- // expect(fileService.removeFile).toHaveBeenCalledWith(fileId);
77
- // expect(result.current.inputFilesList).toEqual([]);
78
- // });
79
-
80
- // describe('uploadFile', () => {
81
- // it('uploadFile should handle errors', async () => {
82
- // const { result } = renderHook(() => useStore());
83
- // const testFile = new File(['content'], 'test.png', { type: 'image/png' });
84
- //
85
- // // 模拟 fileService.uploadFile 抛出错误
86
- // const errorMessage = 'Upload failed';
87
- // vi.spyOn(uploadService, 'uploadFile').mockRejectedValue(new Error(errorMessage));
88
- //
89
- // // Mock console.error for testing
90
- //
91
- // await act(async () => {
92
- // await result.current.uploadFile(testFile);
93
- // });
94
- //
95
- // expect(uploadService.uploadFile).toHaveBeenCalledWith({
96
- // createdAt: testFile.lastModified,
97
- // data: await testFile.arrayBuffer(),
98
- // fileType: testFile.type,
99
- // name: testFile.name,
100
- // saveMode: 'local',
101
- // size: testFile.size,
102
- // });
103
- // // 由于上传失败,inputFilesList 应该没有变化
104
- // expect(result.current.inputFilesList).toEqual([]);
105
- //
106
- // // 确保错误提示被调用
107
- // expect(notification.error).toHaveBeenCalled();
108
- // });
109
- //
110
- // it('uploadFile should upload the file and update inputFilesList', async () => {
111
- // const { result } = renderHook(() => useStore());
112
- // const testFile = new File(['content'], 'test.png', { type: 'image/png' });
113
- //
114
- // // 模拟 fileService.uploadFile 返回的数据
115
- // const uploadedFileData = {
116
- // createdAt: testFile.lastModified,
117
- // data: await testFile.arrayBuffer(),
118
- // fileType: testFile.type,
119
- // name: testFile.name,
120
- // saveMode: 'local',
121
- // size: testFile.size,
122
- // };
123
- //
124
- // // Mock the fileService.uploadFile to resolve with uploadedFileData
125
- // vi.spyOn(uploadService, 'uploadFile').mockResolvedValue(uploadedFileData as DB_File);
126
- // vi.spyOn(fileService, 'createFile').mockResolvedValue({ id: 'new-file-id', url: '' });
127
- //
128
- // await act(async () => {
129
- // await result.current.uploadFile(testFile);
130
- // });
131
- //
132
- // expect(fileService.createFile).toHaveBeenCalledWith({
133
- // createdAt: testFile.lastModified,
134
- // data: await testFile.arrayBuffer(),
135
- // fileType: testFile.type,
136
- // name: testFile.name,
137
- // saveMode: 'local',
138
- // size: testFile.size,
139
- // });
140
- // expect(result.current.inputFilesList).toContain('new-file-id');
141
- // });
142
- // });
143
54
  });
@@ -1,4 +1,3 @@
1
- // import { FileStore } from '../../store';
2
1
  import { type FilesStoreState } from '@/store/file/initialState';
3
2
 
4
3
  const showSimilaritySearchResult = (s: FilesStoreState) => s.isSimilaritySearch;
@@ -1,4 +1,3 @@
1
- // import { FileStore } from '../../store';
2
1
  import { type FilesStoreState } from '@/store/file/initialState';
3
2
  import { type FileUploadStatus } from '@/types/files/upload';
4
3
 
@@ -1,3 +1 @@
1
- // import { FileStore } from '../../store';
2
-
3
1
  export const ttsFilesSelectors = {};
@@ -1,3 +1,2 @@
1
1
  export * from './action';
2
2
  export * from './initialState';
3
- // export * from './selectors';
@@ -1,3 +1,2 @@
1
1
  export * from './action';
2
2
  export * from './initialState';
3
- // export * from './selectors';
@@ -1,3 +1,2 @@
1
1
  export * from './action';
2
2
  export * from './initialState';
3
- // export * from './selectors';
@@ -1,3 +1,2 @@
1
1
  export * from './action';
2
2
  export * from './initialState';
3
- // export * from './selectors';
@@ -59,4 +59,10 @@ export default ({ token }: { prefixCls: string; token: Theme }) => css`
59
59
  .${CLASSNAMES.DropdownMenuTrigger}[data-popup-open]:not([data-no-highlight]) {
60
60
  background: ${token.colorFillTertiary};
61
61
  }
62
+
63
+ .ant-form-item-control:has([role='combobox'][aria-controls^='base-ui-']),
64
+ .ant-form-item-control:has([role='combobox'][aria-haspopup='listbox']) {
65
+ width: min(70%, 800px);
66
+ min-width: min(70%, 800px) !important;
67
+ }
62
68
  `;