@lobehub/lobehub 2.0.0-next.51 → 2.0.0-next.52

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 (84) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/apps/desktop/src/main/controllers/LocalFileCtr.ts +25 -5
  3. package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +4 -1
  4. package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +357 -0
  5. package/apps/desktop/src/main/modules/fileSearch/impl/macOS.ts +30 -22
  6. package/changelog/v1.json +9 -0
  7. package/locales/ar/models.json +119 -126
  8. package/locales/ar/plugin.json +1 -1
  9. package/locales/bg-BG/models.json +104 -132
  10. package/locales/bg-BG/plugin.json +1 -1
  11. package/locales/de-DE/models.json +119 -126
  12. package/locales/de-DE/plugin.json +1 -1
  13. package/locales/en-US/models.json +167 -126
  14. package/locales/en-US/plugin.json +1 -1
  15. package/locales/es-ES/models.json +119 -126
  16. package/locales/es-ES/plugin.json +1 -1
  17. package/locales/fa-IR/models.json +119 -126
  18. package/locales/fa-IR/plugin.json +1 -1
  19. package/locales/fr-FR/models.json +119 -126
  20. package/locales/fr-FR/plugin.json +1 -1
  21. package/locales/it-IT/models.json +119 -126
  22. package/locales/it-IT/plugin.json +1 -1
  23. package/locales/ja-JP/models.json +119 -126
  24. package/locales/ja-JP/plugin.json +1 -1
  25. package/locales/ko-KR/models.json +119 -126
  26. package/locales/ko-KR/plugin.json +1 -1
  27. package/locales/nl-NL/models.json +119 -126
  28. package/locales/nl-NL/plugin.json +1 -1
  29. package/locales/pl-PL/models.json +119 -126
  30. package/locales/pl-PL/plugin.json +1 -1
  31. package/locales/pt-BR/models.json +119 -126
  32. package/locales/pt-BR/plugin.json +1 -1
  33. package/locales/ru-RU/models.json +119 -126
  34. package/locales/ru-RU/plugin.json +1 -1
  35. package/locales/tr-TR/models.json +119 -126
  36. package/locales/tr-TR/plugin.json +1 -1
  37. package/locales/vi-VN/models.json +119 -126
  38. package/locales/vi-VN/plugin.json +1 -1
  39. package/locales/zh-CN/models.json +173 -80
  40. package/locales/zh-CN/plugin.json +1 -1
  41. package/locales/zh-TW/models.json +119 -126
  42. package/locales/zh-TW/plugin.json +1 -1
  43. package/package.json +1 -1
  44. package/packages/electron-client-ipc/src/types/localSystem.ts +26 -2
  45. package/packages/model-runtime/src/core/contextBuilders/openai.test.ts +58 -0
  46. package/packages/model-runtime/src/core/contextBuilders/openai.ts +24 -10
  47. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +3 -2
  48. package/packages/model-runtime/src/providers/openai/index.test.ts +44 -0
  49. package/packages/types/src/tool/builtin.ts +6 -4
  50. package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx +3 -3
  51. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/index.tsx +2 -2
  52. package/src/features/Conversation/Messages/Group/Tool/Render/LoadingPlaceholder/index.tsx +3 -3
  53. package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +10 -4
  54. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +2 -2
  55. package/src/locales/default/plugin.ts +1 -1
  56. package/src/services/chat/chat.test.ts +1 -0
  57. package/src/store/aiInfra/slices/aiProvider/__tests__/selectors.test.ts +62 -0
  58. package/src/store/aiInfra/slices/aiProvider/selectors.ts +1 -1
  59. package/src/tools/code-interpreter/Render/index.tsx +1 -1
  60. package/src/tools/interventions.ts +28 -4
  61. package/src/tools/local-system/Placeholder/ListFiles.tsx +3 -5
  62. package/src/tools/local-system/Placeholder/SearchFiles.tsx +2 -5
  63. package/src/tools/local-system/Render/ListFiles/index.tsx +16 -21
  64. package/src/tools/local-system/Render/RenameLocalFile/index.tsx +15 -20
  65. package/src/tools/local-system/Render/RunCommand/index.tsx +67 -70
  66. package/src/tools/local-system/Render/SearchFiles/SearchQuery/index.tsx +0 -1
  67. package/src/tools/local-system/Render/SearchFiles/index.tsx +15 -20
  68. package/src/tools/local-system/Render/WriteFile/index.tsx +2 -8
  69. package/src/tools/local-system/index.ts +4 -4
  70. package/src/tools/local-system/systemRole.ts +1 -1
  71. package/src/tools/placeholders.ts +39 -8
  72. package/src/tools/renders.ts +56 -9
  73. package/src/tools/web-browsing/Placeholder/{PageContent.tsx → CrawlMultiPages.tsx} +4 -1
  74. package/src/tools/web-browsing/Placeholder/CrawlSinglePage.tsx +12 -0
  75. package/src/tools/web-browsing/Placeholder/Search.tsx +4 -4
  76. package/src/tools/web-browsing/Render/CrawlMultiPages.tsx +15 -0
  77. package/src/tools/web-browsing/Render/CrawlSinglePage.tsx +15 -0
  78. package/src/tools/web-browsing/Render/Search/index.tsx +39 -44
  79. package/packages/database/migrations/0044_add_tool_intervention.sql +0 -1
  80. package/src/tools/local-system/Intervention/index.tsx +0 -17
  81. package/src/tools/local-system/Placeholder/index.tsx +0 -25
  82. package/src/tools/local-system/Render/index.tsx +0 -42
  83. package/src/tools/web-browsing/Placeholder/index.tsx +0 -40
  84. package/src/tools/web-browsing/Render/index.tsx +0 -57
@@ -409,6 +409,50 @@ describe('LobeOpenAI', () => {
409
409
  const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
410
410
  expect(createCall.reasoning).toEqual({ effort: 'high', summary: 'auto' });
411
411
  });
412
+
413
+ it('should convert max_tokens to max_output_tokens for responses API', async () => {
414
+ const payload = {
415
+ max_tokens: 2048,
416
+ messages: [{ content: 'Hello', role: 'user' as const }],
417
+ model: 'o1-pro',
418
+ temperature: 0.7,
419
+ };
420
+
421
+ await instance.chat(payload);
422
+
423
+ const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
424
+ expect(createCall.max_output_tokens).toBe(2048);
425
+ expect(createCall.max_tokens).toBeUndefined();
426
+ });
427
+
428
+ it('should not include max_output_tokens when max_tokens is undefined', async () => {
429
+ const payload = {
430
+ messages: [{ content: 'Hello', role: 'user' as const }],
431
+ model: 'o1-pro',
432
+ temperature: 0.7,
433
+ };
434
+
435
+ await instance.chat(payload);
436
+
437
+ const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
438
+ expect(createCall.max_output_tokens).toBeUndefined();
439
+ });
440
+
441
+ it('should convert max_tokens to max_output_tokens for search-enabled models', async () => {
442
+ const payload = {
443
+ enabledSearch: true,
444
+ max_tokens: 4096,
445
+ messages: [{ content: 'Hello', role: 'user' as const }],
446
+ model: 'gpt-4o',
447
+ temperature: 0.7,
448
+ };
449
+
450
+ await instance.chat(payload);
451
+
452
+ const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
453
+ expect(createCall.max_output_tokens).toBe(4096);
454
+ expect(createCall.max_tokens).toBeUndefined();
455
+ });
412
456
  });
413
457
 
414
458
  describe('supportsFlexTier', () => {
@@ -115,7 +115,7 @@ export const LobeBuiltinToolSchema = z.object({
115
115
  type: z.literal('builtin'),
116
116
  });
117
117
 
118
- export interface BuiltinRenderProps<Content = any, Arguments = any, State = any> {
118
+ export interface BuiltinRenderProps<Arguments = any, State = any, Content = any> {
119
119
  apiName?: string;
120
120
  args: Arguments;
121
121
  content: Content;
@@ -125,7 +125,9 @@ export interface BuiltinRenderProps<Content = any, Arguments = any, State = any>
125
125
  pluginState?: State;
126
126
  }
127
127
 
128
- export type BuiltinRender = <T = any>(props: BuiltinRenderProps<T>) => ReactNode;
128
+ export type BuiltinRender = <A = any, S = any, C = any>(
129
+ props: BuiltinRenderProps<A, S, C>,
130
+ ) => ReactNode;
129
131
 
130
132
  export interface BuiltinPortalProps<Arguments = Record<string, any>, State = any> {
131
133
  apiName?: string;
@@ -137,9 +139,9 @@ export interface BuiltinPortalProps<Arguments = Record<string, any>, State = any
137
139
 
138
140
  export type BuiltinPortal = <T = any>(props: BuiltinPortalProps<T>) => ReactNode;
139
141
 
140
- export interface BuiltinPlaceholderProps {
142
+ export interface BuiltinPlaceholderProps<T extends Record<string, any> = any> {
141
143
  apiName: string;
142
- args?: Record<string, any>;
144
+ args?: T;
143
145
  identifier: string;
144
146
  }
145
147
 
@@ -1,7 +1,7 @@
1
1
  import { safeParseJSON } from '@lobechat/utils';
2
2
  import { memo } from 'react';
3
3
 
4
- import { BuiltinToolPlaceholders } from '@/tools/placeholders';
4
+ import { getBuiltinPlaceholder } from '@/tools/placeholders';
5
5
 
6
6
  import Arguments from '../Arguments';
7
7
 
@@ -14,9 +14,9 @@ interface LoadingPlaceholderProps {
14
14
 
15
15
  const LoadingPlaceholder = memo<LoadingPlaceholderProps>(
16
16
  ({ identifier, requestArgs, apiName, loading }) => {
17
- const Render = BuiltinToolPlaceholders[identifier || ''];
17
+ const Render = getBuiltinPlaceholder(identifier, apiName);
18
18
 
19
- if (identifier && Render) {
19
+ if (Render) {
20
20
  return (
21
21
  <Render apiName={apiName} args={safeParseJSON(requestArgs) || {}} identifier={identifier} />
22
22
  );
@@ -5,7 +5,7 @@ import { Flexbox } from 'react-layout-kit';
5
5
  import { useChatStore } from '@/store/chat';
6
6
  import { useUserStore } from '@/store/user';
7
7
  import { toolInterventionSelectors } from '@/store/user/selectors';
8
- import { BuiltinToolInterventions } from '@/tools/interventions';
8
+ import { getBuiltinIntervention } from '@/tools/interventions';
9
9
 
10
10
  import Arguments from '../Arguments';
11
11
  import ApprovalActions from './ApprovalActions';
@@ -52,7 +52,7 @@ const Intervention = memo<InterventionProps>(
52
52
  },
53
53
  [requestArgs, id],
54
54
  );
55
- const BuiltinToolInterventionRender = BuiltinToolInterventions[identifier];
55
+ const BuiltinToolInterventionRender = getBuiltinIntervention(identifier, apiName);
56
56
 
57
57
  if (BuiltinToolInterventionRender) {
58
58
  if (isEditing)
@@ -1,7 +1,7 @@
1
1
  import { safeParseJSON } from '@lobechat/utils';
2
2
  import { memo } from 'react';
3
3
 
4
- import { BuiltinToolPlaceholders } from '@/tools/placeholders';
4
+ import { getBuiltinPlaceholder } from '@/tools/placeholders';
5
5
 
6
6
  import Arguments from '../Arguments';
7
7
 
@@ -14,9 +14,9 @@ interface LoadingPlaceholderProps {
14
14
 
15
15
  const LoadingPlaceholder = memo<LoadingPlaceholderProps>(
16
16
  ({ identifier, requestArgs, apiName, loading }) => {
17
- const Render = BuiltinToolPlaceholders[identifier || ''];
17
+ const Render = getBuiltinPlaceholder(identifier, apiName);
18
18
 
19
- if (identifier && Render) {
19
+ if (Render) {
20
20
  return (
21
21
  <Render apiName={apiName} args={safeParseJSON(requestArgs) || {}} identifier={identifier} />
22
22
  );
@@ -4,11 +4,17 @@ import { describe, expect, it, vi } from 'vitest';
4
4
  import BuiltinType from './index';
5
5
 
6
6
  // Mock renders module
7
+ const mockWebBrowsingRender = vi.fn(({ content }) => <div>WebBrowsingRender: {content}</div>);
8
+ const mockCodeInterpreterRender = vi.fn(({ content }) => (
9
+ <div>CodeInterpreterRender: {content}</div>
10
+ ));
11
+
7
12
  vi.mock('@/tools/renders', () => ({
8
- BuiltinToolsRenders: {
9
- 'lobe-web-browsing': vi.fn(({ content }) => <div>WebBrowsingRender: {content}</div>),
10
- 'lobe-code-interpreter': vi.fn(({ content }) => <div>CodeInterpreterRender: {content}</div>),
11
- },
13
+ getBuiltinRender: vi.fn((identifier, apiName) => {
14
+ if (identifier === 'lobe-web-browsing') return mockWebBrowsingRender;
15
+ if (identifier === 'lobe-code-interpreter') return mockCodeInterpreterRender;
16
+ return undefined;
17
+ }),
12
18
  }));
13
19
 
14
20
  // Mock useParseContent hook
@@ -1,7 +1,7 @@
1
1
  import { safeParseJSON } from '@lobechat/utils';
2
2
  import { memo } from 'react';
3
3
 
4
- import { BuiltinToolsRenders } from '@/tools/renders';
4
+ import { getBuiltinRender } from '@/tools/renders';
5
5
 
6
6
  import { useParseContent } from '../useParseContent';
7
7
 
@@ -28,7 +28,7 @@ const BuiltinType = memo<BuiltinTypeProps>(
28
28
  }) => {
29
29
  const { data } = useParseContent(content);
30
30
 
31
- const Render = BuiltinToolsRenders[identifier || ''];
31
+ const Render = getBuiltinRender(identifier, apiName);
32
32
 
33
33
  if (!Render) return;
34
34
 
@@ -260,7 +260,7 @@ export default {
260
260
  searchLocalFiles: '搜索文件',
261
261
  writeLocalFile: '写入文件',
262
262
  },
263
- title: '本地文件',
263
+ title: '本地系统',
264
264
  },
265
265
  mcpInstall: {
266
266
  CHECKING_INSTALLATION: '检查安装环境...',
@@ -1004,6 +1004,7 @@ describe('ChatService', () => {
1004
1004
  stream: true,
1005
1005
  ...DEFAULT_AGENT_CONFIG.params,
1006
1006
  ...params,
1007
+ apiMode: 'responses',
1007
1008
  };
1008
1009
 
1009
1010
  await chatService.getChatCompletion(params, options);
@@ -246,4 +246,66 @@ describe('aiProviderSelectors', () => {
246
246
  );
247
247
  });
248
248
  });
249
+
250
+ describe('isProviderEnableResponseApi', () => {
251
+ it('should return true when config explicitly sets enableResponseApi to true', () => {
252
+ const state = {
253
+ ...mockState,
254
+ aiProviderRuntimeConfig: {
255
+ test: {
256
+ config: { enableResponseApi: true },
257
+ keyVaults: {},
258
+ settings: {},
259
+ },
260
+ },
261
+ };
262
+ expect(aiProviderSelectors.isProviderEnableResponseApi('test')(state)).toBe(true);
263
+ });
264
+
265
+ it('should return false when config explicitly sets enableResponseApi to false', () => {
266
+ const state = {
267
+ ...mockState,
268
+ aiProviderRuntimeConfig: {
269
+ test: {
270
+ config: { enableResponseApi: false },
271
+ keyVaults: {},
272
+ settings: {},
273
+ },
274
+ },
275
+ };
276
+ expect(aiProviderSelectors.isProviderEnableResponseApi('test')(state)).toBe(false);
277
+ });
278
+
279
+ it('should return true by default for openai provider', () => {
280
+ const state = {
281
+ ...mockState,
282
+ aiProviderRuntimeConfig: {
283
+ openai: {
284
+ keyVaults: {},
285
+ settings: {},
286
+ },
287
+ },
288
+ };
289
+ expect(aiProviderSelectors.isProviderEnableResponseApi('openai')(state)).toBe(true);
290
+ });
291
+
292
+ it('should return false by default for non-openai provider', () => {
293
+ const state = {
294
+ ...mockState,
295
+ aiProviderRuntimeConfig: {
296
+ anthropic: {
297
+ keyVaults: {},
298
+ settings: {},
299
+ },
300
+ },
301
+ };
302
+ expect(aiProviderSelectors.isProviderEnableResponseApi('anthropic')(state)).toBe(false);
303
+ });
304
+
305
+ it('should return false for provider without config', () => {
306
+ expect(aiProviderSelectors.isProviderEnableResponseApi('non-existing')(mockState)).toBe(
307
+ false,
308
+ );
309
+ });
310
+ });
249
311
  });
@@ -108,7 +108,7 @@ const isProviderEnableResponseApi = (id: string) => (s: AIProviderStoreState) =>
108
108
 
109
109
  if (typeof enableResponseApi === 'boolean') return enableResponseApi;
110
110
 
111
- return false;
111
+ return id === 'openai';
112
112
  };
113
113
 
114
114
  const isInitAiProviderRuntimeState = (s: AIProviderStoreState) => !!s.isInitAiProviderRuntimeState;
@@ -17,7 +17,7 @@ import { chatToolSelectors } from '@/store/chat/slices/builtinTool/selectors';
17
17
  import ResultFileGallery from './components/ResultFileGallery';
18
18
 
19
19
  const CodeInterpreter = memo<
20
- BuiltinRenderProps<CodeInterpreterResponse, CodeInterpreterParams, CodeInterpreterState>
20
+ BuiltinRenderProps<CodeInterpreterParams, CodeInterpreterState, CodeInterpreterResponse>
21
21
  >(({ content, args, pluginState, messageId, apiName }) => {
22
22
  const { t } = useTranslation('tool');
23
23
  const theme = useTheme();
@@ -1,8 +1,32 @@
1
1
  import { BuiltinIntervention } from '@lobechat/types';
2
2
 
3
- import { LocalSystemManifest } from './local-system';
4
- import LocalSystem from './local-system/Intervention';
3
+ import { LocalSystemApiName, LocalSystemManifest } from './local-system';
4
+ import RunCommand from './local-system/Intervention/RunCommand';
5
5
 
6
- export const BuiltinToolInterventions: Record<string, BuiltinIntervention> = {
7
- [LocalSystemManifest.identifier]: LocalSystem as BuiltinIntervention,
6
+ /**
7
+ * Builtin tools interventions registry
8
+ * Organized by toolset (identifier) -> API name
9
+ * Only register APIs that have custom intervention UI
10
+ */
11
+ export const BuiltinToolInterventions: Record<string, Record<string, any>> = {
12
+ [LocalSystemManifest.identifier]: {
13
+ [LocalSystemApiName.runCommand]: RunCommand,
14
+ },
15
+ };
16
+
17
+ /**
18
+ * Get builtin intervention component for a specific API
19
+ * @param identifier - Tool identifier (e.g., 'lobe-local-system')
20
+ * @param apiName - API name (e.g., 'runCommand')
21
+ */
22
+ export const getBuiltinIntervention = (
23
+ identifier?: string,
24
+ apiName?: string,
25
+ ): BuiltinIntervention | undefined => {
26
+ if (!identifier || !apiName) return undefined;
27
+
28
+ const toolset = BuiltinToolInterventions[identifier];
29
+ if (!toolset) return undefined;
30
+
31
+ return toolset[apiName];
8
32
  };
@@ -1,17 +1,15 @@
1
1
  import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
2
+ import { BuiltinPlaceholderProps } from '@lobechat/types';
2
3
  import { Skeleton } from 'antd';
3
4
  import React, { memo } from 'react';
4
5
  import { Center, Flexbox } from 'react-layout-kit';
5
6
 
6
7
  import { LocalFolder } from '@/features/LocalFile';
7
8
 
8
- interface ListFilesProps {
9
- args: ListLocalFileParams;
10
- }
11
- export const ListFiles = memo<ListFilesProps>(({ args }) => {
9
+ export const ListFiles = memo<BuiltinPlaceholderProps<ListLocalFileParams>>(({ args }) => {
12
10
  return (
13
11
  <Flexbox gap={12}>
14
- <LocalFolder path={args.path} />
12
+ {args?.path && <LocalFolder path={args.path} />}
15
13
  <Center height={140}>
16
14
  <Flexbox gap={4} width={'90%'}>
17
15
  <Skeleton.Button active block style={{ height: 16 }} />
@@ -1,4 +1,5 @@
1
1
  import { LocalSearchFilesParams } from '@lobechat/electron-client-ipc';
2
+ import { BuiltinPlaceholderProps } from '@lobechat/types';
2
3
  import { Icon } from '@lobehub/ui';
3
4
  import { Skeleton } from 'antd';
4
5
  import { createStyles } from 'antd-style';
@@ -21,11 +22,7 @@ const useStyles = createStyles(({ css, token, cx }) => ({
21
22
  `),
22
23
  }));
23
24
 
24
- interface SearchFilesProps {
25
- args: LocalSearchFilesParams;
26
- }
27
-
28
- const SearchFiles = memo<SearchFilesProps>(({ args }) => {
25
+ const SearchFiles = memo<BuiltinPlaceholderProps<LocalSearchFilesParams>>(({ args = {} }) => {
29
26
  const { styles } = useStyles();
30
27
 
31
28
  return (
@@ -1,31 +1,26 @@
1
1
  import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
2
- import { ChatMessagePluginError } from '@lobechat/types';
2
+ import { BuiltinRenderProps } from '@lobechat/types';
3
3
  import React, { memo } from 'react';
4
4
 
5
5
  import { LocalFolder } from '@/features/LocalFile';
6
- import { LocalFileListState } from '@/tools/local-system/type';
7
6
 
7
+ import { LocalFileListState } from '../../type';
8
8
  import SearchResult from './Result';
9
9
 
10
- interface ListFilesProps {
11
- args: ListLocalFileParams;
12
- messageId: string;
13
- pluginError: ChatMessagePluginError;
14
- pluginState?: LocalFileListState;
15
- }
16
-
17
- const ListFiles = memo<ListFilesProps>(({ messageId, pluginError, args, pluginState }) => {
18
- return (
19
- <>
20
- <LocalFolder path={args.path} />
21
- <SearchResult
22
- listResults={pluginState?.listResults}
23
- messageId={messageId}
24
- pluginError={pluginError}
25
- />
26
- </>
27
- );
28
- });
10
+ const ListFiles = memo<BuiltinRenderProps<ListLocalFileParams, LocalFileListState>>(
11
+ ({ messageId, pluginError, args, pluginState }) => {
12
+ return (
13
+ <>
14
+ <LocalFolder path={args.path} />
15
+ <SearchResult
16
+ listResults={pluginState?.listResults}
17
+ messageId={messageId}
18
+ pluginError={pluginError}
19
+ />
20
+ </>
21
+ );
22
+ },
23
+ );
29
24
 
30
25
  ListFiles.displayName = 'ListFiles';
31
26
 
@@ -1,5 +1,5 @@
1
1
  import { RenameLocalFileParams } from '@lobechat/electron-client-ipc';
2
- import { ChatMessagePluginError } from '@lobechat/types';
2
+ import { BuiltinRenderProps } from '@lobechat/types';
3
3
  import { Icon } from '@lobehub/ui';
4
4
  import { createStyles } from 'antd-style';
5
5
  import { ArrowRightIcon } from 'lucide-react';
@@ -19,27 +19,22 @@ const useStyles = createStyles(({ css, token }) => ({
19
19
  `,
20
20
  }));
21
21
 
22
- interface RenameLocalFileProps {
23
- args: RenameLocalFileParams;
24
- messageId: string;
25
- pluginError: ChatMessagePluginError;
26
- pluginState: LocalReadFileState;
27
- }
22
+ const RenameLocalFile = memo<BuiltinRenderProps<RenameLocalFileParams, LocalReadFileState>>(
23
+ ({ args }) => {
24
+ const { styles } = useStyles();
28
25
 
29
- const RenameLocalFile = memo<RenameLocalFileProps>(({ args }) => {
30
- const { styles } = useStyles();
26
+ const { base: oldFileName, dir } = path.parse(args.path);
31
27
 
32
- const { base: oldFileName, dir } = path.parse(args.path);
33
-
34
- return (
35
- <Flexbox align={'center'} className={styles.container} gap={8} horizontal paddingInline={12}>
36
- <Flexbox>{oldFileName}</Flexbox>
37
- <Flexbox>
38
- <Icon icon={ArrowRightIcon} />
28
+ return (
29
+ <Flexbox align={'center'} className={styles.container} gap={8} horizontal paddingInline={12}>
30
+ <Flexbox>{oldFileName}</Flexbox>
31
+ <Flexbox>
32
+ <Icon icon={ArrowRightIcon} />
33
+ </Flexbox>
34
+ <LocalFile name={args.newName} path={path.join(dir, args.newName)} />
39
35
  </Flexbox>
40
- <LocalFile name={args.newName} path={path.join(dir, args.newName)} />
41
- </Flexbox>
42
- );
43
- });
36
+ );
37
+ },
38
+ );
44
39
 
45
40
  export default RenameLocalFile;
@@ -1,6 +1,6 @@
1
1
  import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
2
2
  import { RunCommandParams, RunCommandResult } from '@lobechat/electron-client-ipc';
3
- import { ChatMessagePluginError } from '@lobechat/types';
3
+ import { BuiltinRenderProps } from '@lobechat/types';
4
4
  import { ActionIcon, Block, Highlighter, Text } from '@lobehub/ui';
5
5
  import { createStyles } from 'antd-style';
6
6
  import { ChevronDown, ChevronUp } from 'lucide-react';
@@ -33,82 +33,79 @@ const useStyles = createStyles(({ css, token }) => ({
33
33
  `,
34
34
  }));
35
35
 
36
- interface RunCommandProps {
37
- args: RunCommandParams;
38
- messageId: string;
39
- pluginError: ChatMessagePluginError;
40
- pluginState: {
41
- message: string;
42
- result: RunCommandResult;
43
- };
36
+ interface RunCommandState {
37
+ message: string;
38
+ result: RunCommandResult;
44
39
  }
45
40
 
46
- const RunCommand = memo<RunCommandProps>(({ args, pluginState }) => {
47
- const { styles, theme } = useStyles();
48
- const { result, message } = pluginState || {};
49
- const isSuccess = result?.success;
50
- const [expanded, setExpanded] = useState(false);
41
+ const RunCommand = memo<BuiltinRenderProps<RunCommandParams, RunCommandState>>(
42
+ ({ args, pluginState }) => {
43
+ const { styles, theme } = useStyles();
44
+ const { result, message } = pluginState || {};
45
+ const isSuccess = result?.success;
46
+ const [expanded, setExpanded] = useState(false);
51
47
 
52
- return (
53
- <Flexbox className={styles.container} gap={8}>
54
- {/* Header: Description + Status */}
55
- <Flexbox align={'center'} className={styles.header} horizontal justify={'space-between'}>
56
- <Flexbox gap={8} horizontal>
57
- <Flexbox gap={4} horizontal>
58
- {!result ? null : isSuccess ? (
59
- <CheckCircleFilled
60
- className={styles.statusIcon}
61
- style={{ color: theme.colorSuccess }}
62
- />
63
- ) : (
64
- <CloseCircleFilled
65
- className={styles.statusIcon}
66
- style={{ color: theme.colorError }}
67
- />
48
+ return (
49
+ <Flexbox className={styles.container} gap={8}>
50
+ {/* Header: Description + Status */}
51
+ <Flexbox align={'center'} className={styles.header} horizontal justify={'space-between'}>
52
+ <Flexbox gap={8} horizontal>
53
+ <Flexbox gap={4} horizontal>
54
+ {!result ? null : isSuccess ? (
55
+ <CheckCircleFilled
56
+ className={styles.statusIcon}
57
+ style={{ color: theme.colorSuccess }}
58
+ />
59
+ ) : (
60
+ <CloseCircleFilled
61
+ className={styles.statusIcon}
62
+ style={{ color: theme.colorError }}
63
+ />
64
+ )}
65
+ {args.description && <Text className={styles.head}>{args.description}</Text>}
66
+ </Flexbox>
67
+ {message && (
68
+ <Flexbox align={'center'} gap={4} horizontal>
69
+ <Text className={styles.head} type={'secondary'}>
70
+ {message}
71
+ </Text>
72
+ </Flexbox>
68
73
  )}
69
- {args.description && <Text className={styles.head}>{args.description}</Text>}
70
74
  </Flexbox>
71
- {message && (
72
- <Flexbox align={'center'} gap={4} horizontal>
73
- <Text className={styles.head} type={'secondary'}>
74
- {message}
75
- </Text>
76
- </Flexbox>
77
- )}
78
- </Flexbox>
79
- <Flexbox align={'center'} gap={8} horizontal>
80
- <ActionIcon
81
- className={`action-icon`}
82
- icon={expanded ? ChevronUp : ChevronDown}
83
- onClick={() => setExpanded(!expanded)}
84
- size={'small'}
85
- style={{ opacity: expanded ? 1 : undefined }}
86
- title={expanded ? 'Collapse' : 'Expand'}
87
- />
75
+ <Flexbox align={'center'} gap={8} horizontal>
76
+ <ActionIcon
77
+ className={`action-icon`}
78
+ icon={expanded ? ChevronUp : ChevronDown}
79
+ onClick={() => setExpanded(!expanded)}
80
+ size={'small'}
81
+ style={{ opacity: expanded ? 1 : undefined }}
82
+ title={expanded ? 'Collapse' : 'Expand'}
83
+ />
84
+ </Flexbox>
88
85
  </Flexbox>
89
- </Flexbox>
90
86
 
91
- {/* Command & Output */}
92
- {expanded && (
93
- <Block gap={8} padding={8} variant={'outlined'}>
94
- <Highlighter
95
- language={'sh'}
96
- showLanguage={false}
97
- style={{ paddingInline: 8 }}
98
- variant={'borderless'}
99
- wrap
100
- >
101
- {args.command}
102
- </Highlighter>
103
- {result?.output && (
104
- <Highlighter language={'text'} showLanguage={false} variant={'filled'} wrap>
105
- {result.output}
87
+ {/* Command & Output */}
88
+ {expanded && (
89
+ <Block gap={8} padding={8} variant={'outlined'}>
90
+ <Highlighter
91
+ language={'sh'}
92
+ showLanguage={false}
93
+ style={{ paddingInline: 8 }}
94
+ variant={'borderless'}
95
+ wrap
96
+ >
97
+ {args.command}
106
98
  </Highlighter>
107
- )}
108
- </Block>
109
- )}
110
- </Flexbox>
111
- );
112
- });
99
+ {result?.output && (
100
+ <Highlighter language={'text'} showLanguage={false} variant={'filled'} wrap>
101
+ {result.output}
102
+ </Highlighter>
103
+ )}
104
+ </Block>
105
+ )}
106
+ </Flexbox>
107
+ );
108
+ },
109
+ );
113
110
 
114
111
  export default RunCommand;
@@ -13,7 +13,6 @@ import SearchView from './SearchView';
13
13
 
14
14
  interface SearchQueryViewProps {
15
15
  args: LocalSearchFilesParams;
16
-
17
16
  messageId: string;
18
17
  pluginState?: LocalFileSearchState;
19
18
  }