@lobehub/chat 1.81.2 → 1.81.4
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.
- package/CHANGELOG.md +59 -0
- package/changelog/v1.json +21 -0
- package/locales/ar/common.json +2 -0
- package/locales/ar/electron.json +32 -0
- package/locales/ar/models.json +126 -3
- package/locales/ar/plugin.json +1 -0
- package/locales/ar/tool.json +25 -0
- package/locales/bg-BG/common.json +2 -0
- package/locales/bg-BG/electron.json +32 -0
- package/locales/bg-BG/models.json +126 -3
- package/locales/bg-BG/plugin.json +1 -0
- package/locales/bg-BG/tool.json +25 -0
- package/locales/de-DE/common.json +2 -0
- package/locales/de-DE/electron.json +32 -0
- package/locales/de-DE/models.json +126 -3
- package/locales/de-DE/plugin.json +1 -0
- package/locales/de-DE/tool.json +25 -0
- package/locales/en-US/common.json +2 -0
- package/locales/en-US/electron.json +32 -0
- package/locales/en-US/models.json +126 -3
- package/locales/en-US/plugin.json +1 -0
- package/locales/en-US/tool.json +25 -0
- package/locales/es-ES/common.json +2 -0
- package/locales/es-ES/electron.json +32 -0
- package/locales/es-ES/models.json +126 -3
- package/locales/es-ES/plugin.json +1 -0
- package/locales/es-ES/tool.json +25 -0
- package/locales/fa-IR/common.json +2 -0
- package/locales/fa-IR/electron.json +32 -0
- package/locales/fa-IR/models.json +126 -3
- package/locales/fa-IR/plugin.json +1 -0
- package/locales/fa-IR/tool.json +25 -0
- package/locales/fr-FR/common.json +2 -0
- package/locales/fr-FR/electron.json +32 -0
- package/locales/fr-FR/models.json +126 -3
- package/locales/fr-FR/plugin.json +1 -0
- package/locales/fr-FR/tool.json +25 -0
- package/locales/it-IT/common.json +2 -0
- package/locales/it-IT/electron.json +32 -0
- package/locales/it-IT/models.json +126 -3
- package/locales/it-IT/plugin.json +1 -0
- package/locales/it-IT/tool.json +25 -0
- package/locales/ja-JP/common.json +2 -0
- package/locales/ja-JP/electron.json +32 -0
- package/locales/ja-JP/models.json +126 -3
- package/locales/ja-JP/plugin.json +1 -0
- package/locales/ja-JP/tool.json +25 -0
- package/locales/ko-KR/common.json +2 -0
- package/locales/ko-KR/electron.json +32 -0
- package/locales/ko-KR/models.json +126 -3
- package/locales/ko-KR/plugin.json +1 -0
- package/locales/ko-KR/tool.json +25 -0
- package/locales/nl-NL/common.json +2 -0
- package/locales/nl-NL/electron.json +32 -0
- package/locales/nl-NL/models.json +126 -3
- package/locales/nl-NL/plugin.json +1 -0
- package/locales/nl-NL/tool.json +25 -0
- package/locales/pl-PL/common.json +2 -0
- package/locales/pl-PL/electron.json +32 -0
- package/locales/pl-PL/models.json +126 -3
- package/locales/pl-PL/plugin.json +1 -0
- package/locales/pl-PL/tool.json +25 -0
- package/locales/pt-BR/common.json +2 -0
- package/locales/pt-BR/electron.json +32 -0
- package/locales/pt-BR/models.json +126 -3
- package/locales/pt-BR/plugin.json +1 -0
- package/locales/pt-BR/tool.json +25 -0
- package/locales/ru-RU/common.json +2 -0
- package/locales/ru-RU/electron.json +32 -0
- package/locales/ru-RU/models.json +126 -3
- package/locales/ru-RU/plugin.json +1 -0
- package/locales/ru-RU/tool.json +25 -0
- package/locales/tr-TR/common.json +2 -0
- package/locales/tr-TR/electron.json +32 -0
- package/locales/tr-TR/models.json +126 -3
- package/locales/tr-TR/plugin.json +1 -0
- package/locales/tr-TR/tool.json +25 -0
- package/locales/vi-VN/common.json +2 -0
- package/locales/vi-VN/electron.json +32 -0
- package/locales/vi-VN/models.json +126 -3
- package/locales/vi-VN/plugin.json +1 -0
- package/locales/vi-VN/tool.json +25 -0
- package/locales/zh-CN/common.json +2 -0
- package/locales/zh-CN/electron.json +32 -0
- package/locales/zh-CN/models.json +131 -8
- package/locales/zh-CN/plugin.json +1 -0
- package/locales/zh-CN/tool.json +25 -0
- package/locales/zh-TW/common.json +2 -0
- package/locales/zh-TW/electron.json +32 -0
- package/locales/zh-TW/models.json +126 -3
- package/locales/zh-TW/plugin.json +1 -0
- package/locales/zh-TW/tool.json +25 -0
- package/package.json +3 -2
- package/packages/electron-client-ipc/src/events/index.ts +5 -5
- package/packages/electron-client-ipc/src/events/localFile.ts +22 -0
- package/packages/electron-client-ipc/src/events/{file.ts → upload.ts} +1 -1
- package/packages/electron-client-ipc/src/types/index.ts +2 -1
- package/packages/electron-client-ipc/src/types/localFile.ts +52 -0
- package/scripts/prebuild.mts +5 -1
- package/src/app/(backend)/trpc/desktop/[trpc]/route.ts +26 -0
- package/src/config/aiModels/cloudflare.ts +41 -37
- package/src/config/aiModels/github.ts +90 -0
- package/src/config/aiModels/google.ts +25 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/ObjectEntity.tsx +81 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/ValueCell.tsx +43 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/index.tsx +120 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +75 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/KeyValueEditor.tsx +214 -0
- package/src/features/User/UserPanel/useMenu.tsx +8 -1
- package/src/libs/agent-runtime/google/index.ts +3 -0
- package/src/libs/trpc/client/desktop.ts +14 -0
- package/src/locales/default/common.ts +2 -0
- package/src/locales/default/electron.ts +34 -0
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/tool.ts +25 -0
- package/src/server/routers/desktop/index.ts +9 -0
- package/src/server/routers/desktop/pgTable.ts +43 -0
- package/src/services/electron/autoUpdate.ts +17 -0
- package/src/services/electron/file.ts +31 -0
- package/src/services/electron/localFileService.ts +39 -0
- package/src/services/electron/remoteServer.ts +40 -0
- package/src/store/chat/index.ts +1 -1
- package/src/store/chat/slices/builtinTool/actions/index.ts +3 -1
- package/src/store/chat/slices/builtinTool/actions/localFile.ts +129 -0
- package/src/store/chat/slices/builtinTool/initialState.ts +2 -0
- package/src/store/chat/slices/builtinTool/selectors.ts +2 -0
- package/src/store/chat/slices/plugin/action.ts +3 -3
- package/src/store/chat/store.ts +2 -0
- package/src/store/electron/actions/sync.ts +117 -0
- package/src/store/electron/index.ts +1 -0
- package/src/store/electron/initialState.ts +18 -0
- package/src/store/electron/selectors/index.ts +1 -0
- package/src/store/electron/selectors/sync.ts +9 -0
- package/src/store/electron/store.ts +29 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/local-files/Render/ListFiles/Result.tsx +42 -0
- package/src/tools/local-files/Render/ListFiles/index.tsx +68 -0
- package/src/tools/local-files/Render/ReadLocalFile/ReadFileSkeleton.tsx +50 -0
- package/src/tools/local-files/Render/ReadLocalFile/ReadFileView.tsx +197 -0
- package/src/tools/local-files/Render/ReadLocalFile/index.tsx +31 -0
- package/src/tools/local-files/Render/ReadLocalFile/style.ts +37 -0
- package/src/tools/local-files/Render/SearchFiles/Result.tsx +42 -0
- package/src/tools/local-files/Render/SearchFiles/SearchQuery/SearchView.tsx +77 -0
- package/src/tools/local-files/Render/SearchFiles/SearchQuery/index.tsx +72 -0
- package/src/tools/local-files/Render/SearchFiles/index.tsx +32 -0
- package/src/tools/local-files/Render/index.tsx +36 -0
- package/src/tools/local-files/components/FileItem.tsx +117 -0
- package/src/tools/local-files/index.ts +149 -0
- package/src/tools/local-files/systemRole.ts +46 -0
- package/src/tools/local-files/type.ts +33 -0
- package/src/tools/renders.ts +3 -0
- package/packages/electron-client-ipc/src/events/search.ts +0 -4
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments.tsx +0 -165
- /package/packages/electron-client-ipc/src/types/{file.ts → upload.ts} +0 -0
@@ -0,0 +1,129 @@
|
|
1
|
+
import {
|
2
|
+
ListLocalFileParams,
|
3
|
+
LocalReadFileParams,
|
4
|
+
LocalReadFilesParams,
|
5
|
+
LocalSearchFilesParams,
|
6
|
+
} from '@lobechat/electron-client-ipc';
|
7
|
+
import { StateCreator } from 'zustand/vanilla';
|
8
|
+
|
9
|
+
import { localFileService } from '@/services/electron/localFileService';
|
10
|
+
import { ChatStore } from '@/store/chat/store';
|
11
|
+
import {
|
12
|
+
LocalFileListState,
|
13
|
+
LocalFileSearchState,
|
14
|
+
LocalReadFileState,
|
15
|
+
LocalReadFilesState,
|
16
|
+
} from '@/tools/local-files/type';
|
17
|
+
|
18
|
+
export interface LocalFileAction {
|
19
|
+
listLocalFiles: (id: string, params: ListLocalFileParams) => Promise<boolean>;
|
20
|
+
reSearchLocalFiles: (id: string, params: LocalSearchFilesParams) => Promise<boolean>;
|
21
|
+
readLocalFile: (id: string, params: LocalReadFileParams) => Promise<boolean>;
|
22
|
+
readLocalFiles: (id: string, params: LocalReadFilesParams) => Promise<boolean>;
|
23
|
+
searchLocalFiles: (id: string, params: LocalSearchFilesParams) => Promise<boolean>;
|
24
|
+
toggleLocalFileLoading: (id: string, loading: boolean) => void;
|
25
|
+
}
|
26
|
+
|
27
|
+
export const localFileSlice: StateCreator<
|
28
|
+
ChatStore,
|
29
|
+
[['zustand/devtools', never]],
|
30
|
+
[],
|
31
|
+
LocalFileAction
|
32
|
+
> = (set, get) => ({
|
33
|
+
listLocalFiles: async (id, params) => {
|
34
|
+
get().toggleLocalFileLoading(id, true);
|
35
|
+
try {
|
36
|
+
const data = await localFileService.listLocalFiles(params);
|
37
|
+
console.log(data);
|
38
|
+
await get().updatePluginState(id, { listResults: data } as LocalFileListState);
|
39
|
+
await get().internal_updateMessageContent(id, JSON.stringify(data));
|
40
|
+
} catch (error) {
|
41
|
+
console.error('Error listing local files:', error);
|
42
|
+
await get().internal_updateMessagePluginError(id, {
|
43
|
+
body: error,
|
44
|
+
message: (error as Error).message,
|
45
|
+
type: 'PluginServerError',
|
46
|
+
});
|
47
|
+
}
|
48
|
+
get().toggleLocalFileLoading(id, false);
|
49
|
+
|
50
|
+
return true;
|
51
|
+
},
|
52
|
+
|
53
|
+
reSearchLocalFiles: async (id, params) => {
|
54
|
+
get().toggleLocalFileLoading(id, true);
|
55
|
+
|
56
|
+
await get().updatePluginArguments(id, params);
|
57
|
+
|
58
|
+
return get().searchLocalFiles(id, params);
|
59
|
+
},
|
60
|
+
|
61
|
+
readLocalFile: async (id, params) => {
|
62
|
+
get().toggleLocalFileLoading(id, true);
|
63
|
+
|
64
|
+
try {
|
65
|
+
const result = await localFileService.readLocalFile(params);
|
66
|
+
|
67
|
+
await get().updatePluginState(id, { fileContent: result } as LocalReadFileState);
|
68
|
+
await get().internal_updateMessageContent(id, JSON.stringify(result));
|
69
|
+
} catch (error) {
|
70
|
+
console.error('Error reading local file:', error);
|
71
|
+
await get().internal_updateMessagePluginError(id, {
|
72
|
+
body: error,
|
73
|
+
message: (error as Error).message,
|
74
|
+
type: 'PluginServerError',
|
75
|
+
});
|
76
|
+
}
|
77
|
+
get().toggleLocalFileLoading(id, false);
|
78
|
+
return true;
|
79
|
+
},
|
80
|
+
|
81
|
+
readLocalFiles: async (id, params) => {
|
82
|
+
get().toggleLocalFileLoading(id, true);
|
83
|
+
|
84
|
+
try {
|
85
|
+
const results = await localFileService.readLocalFiles(params);
|
86
|
+
await get().updatePluginState(id, { filesContent: results } as LocalReadFilesState);
|
87
|
+
await get().internal_updateMessageContent(id, JSON.stringify(results));
|
88
|
+
} catch (error) {
|
89
|
+
console.error('Error reading local files:', error);
|
90
|
+
await get().internal_updateMessagePluginError(id, {
|
91
|
+
body: error,
|
92
|
+
message: (error as Error).message,
|
93
|
+
type: 'PluginServerError',
|
94
|
+
});
|
95
|
+
}
|
96
|
+
get().toggleLocalFileLoading(id, false);
|
97
|
+
|
98
|
+
return true;
|
99
|
+
},
|
100
|
+
|
101
|
+
searchLocalFiles: async (id, params) => {
|
102
|
+
get().toggleLocalFileLoading(id, true);
|
103
|
+
try {
|
104
|
+
const data = await localFileService.searchLocalFiles(params);
|
105
|
+
await get().updatePluginState(id, { searchResults: data } as LocalFileSearchState);
|
106
|
+
await get().internal_updateMessageContent(id, JSON.stringify(data));
|
107
|
+
} catch (error) {
|
108
|
+
console.error('Error searching local files:', error);
|
109
|
+
await get().internal_updateMessagePluginError(id, {
|
110
|
+
body: error,
|
111
|
+
message: (error as Error).message,
|
112
|
+
type: 'PluginServerError',
|
113
|
+
});
|
114
|
+
}
|
115
|
+
get().toggleLocalFileLoading(id, false);
|
116
|
+
|
117
|
+
return true;
|
118
|
+
},
|
119
|
+
toggleLocalFileLoading: (id, loading) => {
|
120
|
+
// Assuming a loading state structure similar to searchLoading
|
121
|
+
set(
|
122
|
+
(state) => ({
|
123
|
+
localFileLoading: { ...state.localFileLoading, [id]: loading },
|
124
|
+
}),
|
125
|
+
false,
|
126
|
+
`toggleLocalFileLoading/${loading ? 'start' : 'end'}`,
|
127
|
+
);
|
128
|
+
},
|
129
|
+
});
|
@@ -4,11 +4,13 @@ export interface ChatToolState {
|
|
4
4
|
activePageContentUrl?: string;
|
5
5
|
dalleImageLoading: Record<string, boolean>;
|
6
6
|
dalleImageMap: Record<string, FileItem>;
|
7
|
+
localFileLoading: Record<string, boolean>;
|
7
8
|
searchLoading: Record<string, boolean>;
|
8
9
|
}
|
9
10
|
|
10
11
|
export const initialToolState: ChatToolState = {
|
11
12
|
dalleImageLoading: {},
|
12
13
|
dalleImageMap: {},
|
14
|
+
localFileLoading: {},
|
13
15
|
searchLoading: {},
|
14
16
|
};
|
@@ -6,9 +6,11 @@ const isGeneratingDallEImage = (s: ChatStoreState) =>
|
|
6
6
|
Object.values(s.dalleImageLoading).some(Boolean);
|
7
7
|
|
8
8
|
const isSearXNGSearching = (id: string) => (s: ChatStoreState) => s.searchLoading[id];
|
9
|
+
const isSearchingLocalFiles = (id: string) => (s: ChatStoreState) => s.localFileLoading[id];
|
9
10
|
|
10
11
|
export const chatToolSelectors = {
|
11
12
|
isDallEImageGenerating,
|
12
13
|
isGeneratingDallEImage,
|
13
14
|
isSearXNGSearching,
|
15
|
+
isSearchingLocalFiles,
|
14
16
|
};
|
@@ -59,7 +59,7 @@ export interface ChatPluginAction {
|
|
59
59
|
params?: { threadId?: string; inPortalThread?: boolean; inSearchWorkflow?: boolean },
|
60
60
|
) => Promise<void>;
|
61
61
|
updatePluginState: (id: string, value: any) => Promise<void>;
|
62
|
-
updatePluginArguments: <T = any>(id: string, value: T) => Promise<void>;
|
62
|
+
updatePluginArguments: <T = any>(id: string, value: T, replace?: boolean) => Promise<void>;
|
63
63
|
|
64
64
|
internal_addToolToAssistantMessage: (id: string, tool: ChatToolPayload) => Promise<void>;
|
65
65
|
internal_removeToolToAssistantMessage: (id: string, tool_call_id?: string) => Promise<void>;
|
@@ -296,7 +296,7 @@ export const chatPlugin: StateCreator<
|
|
296
296
|
await refreshMessages();
|
297
297
|
},
|
298
298
|
|
299
|
-
updatePluginArguments: async (id, value) => {
|
299
|
+
updatePluginArguments: async (id, value, replace = false) => {
|
300
300
|
const { refreshMessages } = get();
|
301
301
|
const toolMessage = chatSelectors.getMessageById(id)(get());
|
302
302
|
if (!toolMessage || !toolMessage?.tool_call_id) return;
|
@@ -305,7 +305,7 @@ export const chatPlugin: StateCreator<
|
|
305
305
|
|
306
306
|
const prevArguments = toolMessage?.plugin?.arguments;
|
307
307
|
const prevJson = safeParseJSON(prevArguments || '');
|
308
|
-
const nextValue = merge(prevJson || {}, value);
|
308
|
+
const nextValue = replace ? (value as any) : merge(prevJson || {}, value);
|
309
309
|
if (isEqual(prevJson, nextValue)) return;
|
310
310
|
|
311
311
|
// optimistic update
|
package/src/store/chat/store.ts
CHANGED
@@ -0,0 +1,117 @@
|
|
1
|
+
import { RemoteServerConfig } from '@lobechat/electron-client-ipc';
|
2
|
+
import useSWR, { SWRResponse, mutate } from 'swr';
|
3
|
+
import type { StateCreator } from 'zustand/vanilla';
|
4
|
+
|
5
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
6
|
+
import { remoteServerService } from '@/services/electron/remoteServer';
|
7
|
+
|
8
|
+
import { initialState } from '../initialState';
|
9
|
+
import type { ElectronStore } from '../store';
|
10
|
+
|
11
|
+
/**
|
12
|
+
* 设置操作
|
13
|
+
*/
|
14
|
+
export interface ElectronRemoteServerAction {
|
15
|
+
connectRemoteServer: (params: { isSelfHosted: boolean; serverUrl?: string }) => Promise<void>;
|
16
|
+
disconnectRemoteServer: () => Promise<void>;
|
17
|
+
refreshServerConfig: () => Promise<void>;
|
18
|
+
refreshUserData: () => Promise<void>;
|
19
|
+
useRemoteServerConfig: () => SWRResponse;
|
20
|
+
}
|
21
|
+
|
22
|
+
const REMOTE_SERVER_CONFIG_KEY = 'electron:getRemoteServerConfig';
|
23
|
+
|
24
|
+
export const remoteSyncSlice: StateCreator<
|
25
|
+
ElectronStore,
|
26
|
+
[['zustand/devtools', never]],
|
27
|
+
[],
|
28
|
+
ElectronRemoteServerAction
|
29
|
+
> = (set, get) => ({
|
30
|
+
connectRemoteServer: async (values) => {
|
31
|
+
if (!values.serverUrl) return;
|
32
|
+
|
33
|
+
set({ isConnectingServer: true });
|
34
|
+
try {
|
35
|
+
// 获取当前配置
|
36
|
+
const config = await remoteServerService.getRemoteServerConfig();
|
37
|
+
|
38
|
+
// 如果已经激活,需要先清除
|
39
|
+
if (config.active) {
|
40
|
+
await remoteServerService.clearRemoteServerConfig();
|
41
|
+
}
|
42
|
+
|
43
|
+
// 请求授权
|
44
|
+
const result = await remoteServerService.requestAuthorization(values.serverUrl);
|
45
|
+
|
46
|
+
if (!result.success) {
|
47
|
+
console.error('请求授权失败:', result.error);
|
48
|
+
|
49
|
+
set({
|
50
|
+
remoteServerSyncError: { message: result.error, type: 'AUTH_ERROR' },
|
51
|
+
});
|
52
|
+
}
|
53
|
+
// 刷新状态
|
54
|
+
await get().refreshServerConfig();
|
55
|
+
} catch (error) {
|
56
|
+
console.error('远程服务器配置出错:', error);
|
57
|
+
set({
|
58
|
+
remoteServerSyncError: { message: (error as Error).message, type: 'CONFIG_ERROR' },
|
59
|
+
});
|
60
|
+
} finally {
|
61
|
+
set({ isConnectingServer: false });
|
62
|
+
}
|
63
|
+
},
|
64
|
+
|
65
|
+
disconnectRemoteServer: async () => {
|
66
|
+
set({ isConnectingServer: false });
|
67
|
+
try {
|
68
|
+
await remoteServerService.clearRemoteServerConfig();
|
69
|
+
// 更新表单URL为空
|
70
|
+
set({ remoteServerConfig: initialState.remoteServerConfig });
|
71
|
+
// 刷新状态
|
72
|
+
await get().refreshServerConfig();
|
73
|
+
} catch (error) {
|
74
|
+
console.error('断开连接失败:', error);
|
75
|
+
set({
|
76
|
+
remoteServerSyncError: { message: (error as Error).message, type: 'DISCONNECT_ERROR' },
|
77
|
+
});
|
78
|
+
} finally {
|
79
|
+
set({ isConnectingServer: false });
|
80
|
+
}
|
81
|
+
},
|
82
|
+
|
83
|
+
refreshServerConfig: async () => {
|
84
|
+
await mutate(REMOTE_SERVER_CONFIG_KEY);
|
85
|
+
},
|
86
|
+
|
87
|
+
refreshUserData: async () => {
|
88
|
+
const { getSessionStoreState } = await import('@/store/session');
|
89
|
+
const { getChatStoreState } = await import('@/store/chat');
|
90
|
+
const { getUserStoreState } = await import('@/store/user');
|
91
|
+
|
92
|
+
await getSessionStoreState().refreshSessions();
|
93
|
+
await getChatStoreState().refreshMessages();
|
94
|
+
await getChatStoreState().refreshTopic();
|
95
|
+
await getUserStoreState().refreshUserState();
|
96
|
+
getSessionStoreState().switchSession(INBOX_SESSION_ID);
|
97
|
+
},
|
98
|
+
|
99
|
+
useRemoteServerConfig: () =>
|
100
|
+
useSWR<RemoteServerConfig>(
|
101
|
+
REMOTE_SERVER_CONFIG_KEY,
|
102
|
+
async () => {
|
103
|
+
try {
|
104
|
+
return await remoteServerService.getRemoteServerConfig();
|
105
|
+
} catch (error) {
|
106
|
+
console.error('获取远程服务器配置失败:', error);
|
107
|
+
throw error;
|
108
|
+
}
|
109
|
+
},
|
110
|
+
{
|
111
|
+
onSuccess: (data) => {
|
112
|
+
set({ isInitRemoteServerConfig: true, remoteServerConfig: data });
|
113
|
+
get().refreshUserData();
|
114
|
+
},
|
115
|
+
},
|
116
|
+
),
|
117
|
+
});
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './store';
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { RemoteServerConfig } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
export type RemoteServerError = 'CONFIG_ERROR' | 'AUTH_ERROR' | 'DISCONNECT_ERROR';
|
4
|
+
|
5
|
+
export interface ElectronState {
|
6
|
+
isConnectingServer?: boolean;
|
7
|
+
isInitRemoteServerConfig: boolean;
|
8
|
+
isSyncActive?: boolean;
|
9
|
+
remoteServerConfig: RemoteServerConfig;
|
10
|
+
remoteServerSyncError?: { message?: string; type: RemoteServerError };
|
11
|
+
}
|
12
|
+
|
13
|
+
export const initialState: ElectronState = {
|
14
|
+
isConnectingServer: false,
|
15
|
+
isInitRemoteServerConfig: false,
|
16
|
+
isSyncActive: false,
|
17
|
+
remoteServerConfig: { active: false, isSelfHosted: false },
|
18
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './sync';
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { shallow } from 'zustand/shallow';
|
2
|
+
import { createWithEqualityFn } from 'zustand/traditional';
|
3
|
+
import { StateCreator } from 'zustand/vanilla';
|
4
|
+
|
5
|
+
import { createDevtools } from '../middleware/createDevtools';
|
6
|
+
import { type ElectronRemoteServerAction, remoteSyncSlice } from './actions/sync';
|
7
|
+
import { type ElectronState, initialState } from './initialState';
|
8
|
+
|
9
|
+
// =============== 聚合 createStoreFn ============ //
|
10
|
+
|
11
|
+
export interface ElectronStore extends ElectronState, ElectronRemoteServerAction {
|
12
|
+
/* empty */
|
13
|
+
}
|
14
|
+
|
15
|
+
const createStore: StateCreator<ElectronStore, [['zustand/devtools', never]]> = (
|
16
|
+
...parameters
|
17
|
+
) => ({
|
18
|
+
...initialState,
|
19
|
+
...remoteSyncSlice(...parameters),
|
20
|
+
});
|
21
|
+
|
22
|
+
// =============== 实装 useStore ============ //
|
23
|
+
|
24
|
+
const devtools = createDevtools('electron');
|
25
|
+
|
26
|
+
export const useElectronStore = createWithEqualityFn<ElectronStore>()(
|
27
|
+
devtools(createStore),
|
28
|
+
shallow,
|
29
|
+
);
|
package/src/tools/index.ts
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
import { isDesktop } from '@/const/version';
|
1
2
|
import { LobeBuiltinTool } from '@/types/tool';
|
2
3
|
|
3
4
|
import { ArtifactsManifest } from './artifacts';
|
4
5
|
import { DalleManifest } from './dalle';
|
6
|
+
import { LocalFilesManifest } from './local-files';
|
5
7
|
import { WebBrowsingManifest } from './web-browsing';
|
6
8
|
|
7
9
|
export const builtinTools: LobeBuiltinTool[] = [
|
@@ -15,6 +17,12 @@ export const builtinTools: LobeBuiltinTool[] = [
|
|
15
17
|
manifest: DalleManifest,
|
16
18
|
type: 'builtin',
|
17
19
|
},
|
20
|
+
{
|
21
|
+
hidden: !isDesktop,
|
22
|
+
identifier: LocalFilesManifest.identifier,
|
23
|
+
manifest: LocalFilesManifest,
|
24
|
+
type: 'builtin',
|
25
|
+
},
|
18
26
|
{
|
19
27
|
hidden: true,
|
20
28
|
identifier: WebBrowsingManifest.identifier,
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import { LocalFileItem } from '@lobechat/electron-client-ipc';
|
2
|
+
import { Skeleton } from 'antd';
|
3
|
+
import { memo } from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
import { useChatStore } from '@/store/chat';
|
7
|
+
import { chatToolSelectors } from '@/store/chat/selectors';
|
8
|
+
import FileItem from '@/tools/local-files/components/FileItem';
|
9
|
+
import { ChatMessagePluginError } from '@/types/message';
|
10
|
+
|
11
|
+
interface SearchFilesProps {
|
12
|
+
listResults?: LocalFileItem[];
|
13
|
+
messageId: string;
|
14
|
+
pluginError: ChatMessagePluginError;
|
15
|
+
}
|
16
|
+
|
17
|
+
const SearchFiles = memo<SearchFilesProps>(({ listResults = [], messageId }) => {
|
18
|
+
const loading = useChatStore(chatToolSelectors.isSearchingLocalFiles(messageId));
|
19
|
+
|
20
|
+
if (loading) {
|
21
|
+
return (
|
22
|
+
<Flexbox gap={4}>
|
23
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
24
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
25
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
26
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
27
|
+
</Flexbox>
|
28
|
+
);
|
29
|
+
}
|
30
|
+
|
31
|
+
return (
|
32
|
+
<Flexbox gap={2} style={{ maxHeight: 260, overflow: 'scroll' }}>
|
33
|
+
{listResults.map((item) => (
|
34
|
+
<FileItem key={item.path} {...item} showTime />
|
35
|
+
))}
|
36
|
+
</Flexbox>
|
37
|
+
);
|
38
|
+
});
|
39
|
+
|
40
|
+
SearchFiles.displayName = 'SearchFiles';
|
41
|
+
|
42
|
+
export default SearchFiles;
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
|
2
|
+
import { ActionIcon } from '@lobehub/ui';
|
3
|
+
import { Typography } from 'antd';
|
4
|
+
import { createStyles } from 'antd-style';
|
5
|
+
import { FolderOpen } from 'lucide-react';
|
6
|
+
import React, { memo } from 'react';
|
7
|
+
import { useTranslation } from 'react-i18next';
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
9
|
+
|
10
|
+
import { localFileService } from '@/services/electron/localFileService';
|
11
|
+
import { LocalFileListState } from '@/tools/local-files/type';
|
12
|
+
import { ChatMessagePluginError } from '@/types/message';
|
13
|
+
|
14
|
+
import SearchResult from './Result';
|
15
|
+
|
16
|
+
const useStyles = createStyles(({ css, token, cx }) => ({
|
17
|
+
actions: cx(css`
|
18
|
+
cursor: pointer;
|
19
|
+
color: ${token.colorTextTertiary};
|
20
|
+
opacity: 1;
|
21
|
+
transition: opacity 0.2s ${token.motionEaseInOut};
|
22
|
+
`),
|
23
|
+
path: css`
|
24
|
+
padding-inline-start: 8px;
|
25
|
+
color: ${token.colorTextSecondary};
|
26
|
+
`,
|
27
|
+
}));
|
28
|
+
|
29
|
+
interface ListFilesProps {
|
30
|
+
args: ListLocalFileParams;
|
31
|
+
messageId: string;
|
32
|
+
pluginError: ChatMessagePluginError;
|
33
|
+
pluginState?: LocalFileListState;
|
34
|
+
}
|
35
|
+
|
36
|
+
const ListFiles = memo<ListFilesProps>(({ messageId, pluginError, args, pluginState }) => {
|
37
|
+
const { t } = useTranslation('tool');
|
38
|
+
|
39
|
+
const { styles } = useStyles();
|
40
|
+
return (
|
41
|
+
<>
|
42
|
+
<Flexbox gap={8} horizontal>
|
43
|
+
<Typography.Text className={styles.path} ellipsis>
|
44
|
+
{args.path}
|
45
|
+
</Typography.Text>
|
46
|
+
<Flexbox className={styles.actions} gap={8} horizontal style={{ marginLeft: 8 }}>
|
47
|
+
<ActionIcon
|
48
|
+
icon={FolderOpen}
|
49
|
+
onClick={() => {
|
50
|
+
localFileService.openLocalFolder({ isDirectory: true, path: args.path });
|
51
|
+
}}
|
52
|
+
size="small"
|
53
|
+
title={t('localFiles.openFolder')}
|
54
|
+
/>
|
55
|
+
</Flexbox>
|
56
|
+
</Flexbox>
|
57
|
+
<SearchResult
|
58
|
+
listResults={pluginState?.listResults}
|
59
|
+
messageId={messageId}
|
60
|
+
pluginError={pluginError}
|
61
|
+
/>
|
62
|
+
</>
|
63
|
+
);
|
64
|
+
});
|
65
|
+
|
66
|
+
ListFiles.displayName = 'ListFiles';
|
67
|
+
|
68
|
+
export default ListFiles;
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import { Skeleton } from 'antd';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import React, { memo } from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
const useStyles = createStyles(({ css, token }) => ({
|
7
|
+
container: css`
|
8
|
+
padding: 8px;
|
9
|
+
border: 1px solid ${token.colorBorderSecondary};
|
10
|
+
border-radius: ${token.borderRadiusLG}px;
|
11
|
+
`,
|
12
|
+
header: css`
|
13
|
+
margin-block-end: 4px;
|
14
|
+
`,
|
15
|
+
meta: css`
|
16
|
+
font-size: 12px;
|
17
|
+
`,
|
18
|
+
path: css`
|
19
|
+
margin-block-start: 4px;
|
20
|
+
`,
|
21
|
+
}));
|
22
|
+
|
23
|
+
const ReadFileSkeleton = memo(() => {
|
24
|
+
const { styles } = useStyles();
|
25
|
+
|
26
|
+
return (
|
27
|
+
<Flexbox className={styles.container}>
|
28
|
+
<Flexbox
|
29
|
+
align={'center'}
|
30
|
+
className={styles.header}
|
31
|
+
gap={24}
|
32
|
+
horizontal
|
33
|
+
justify={'space-between'}
|
34
|
+
>
|
35
|
+
<Flexbox align={'center'} flex={1} gap={8} horizontal style={{ overflow: 'hidden' }}>
|
36
|
+
<Skeleton.Avatar active shape="square" size={24} style={{ borderRadius: 4 }} />
|
37
|
+
<Skeleton.Input active size="small" style={{ flex: 1, minWidth: 100 }} />
|
38
|
+
</Flexbox>
|
39
|
+
<Flexbox align={'center'} className={styles.meta} gap={16}>
|
40
|
+
<Skeleton.Input active size="small" style={{ maxWidth: 40 }} />
|
41
|
+
</Flexbox>
|
42
|
+
</Flexbox>
|
43
|
+
|
44
|
+
{/* Path */}
|
45
|
+
<Skeleton.Input active block className={styles.path} size="small" />
|
46
|
+
</Flexbox>
|
47
|
+
);
|
48
|
+
});
|
49
|
+
|
50
|
+
export default ReadFileSkeleton;
|