@lobehub/chat 1.81.3 → 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 +33 -0
- package/changelog/v1.json +12 -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/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,120 @@
|
|
1
|
+
import { Highlighter } from '@lobehub/ui';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import { parse } from 'partial-json';
|
4
|
+
import { ReactNode, memo, useMemo } from 'react';
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
6
|
+
|
7
|
+
import { useYamlArguments } from '@/hooks/useYamlArguments';
|
8
|
+
|
9
|
+
import ObjectEntity from './ObjectEntity';
|
10
|
+
|
11
|
+
const useStyles = createStyles(({ css, token, cx }) => ({
|
12
|
+
button: css`
|
13
|
+
color: ${token.colorTextSecondary};
|
14
|
+
|
15
|
+
&:hover {
|
16
|
+
color: ${token.colorText};
|
17
|
+
}
|
18
|
+
`,
|
19
|
+
container: css`
|
20
|
+
position: relative;
|
21
|
+
|
22
|
+
padding-block: 4px;
|
23
|
+
padding-inline: 12px 64px;
|
24
|
+
border-radius: ${token.borderRadiusLG}px;
|
25
|
+
|
26
|
+
font-family: ${token.fontFamilyCode};
|
27
|
+
font-size: 13px;
|
28
|
+
line-height: 1.5;
|
29
|
+
|
30
|
+
background: ${token.colorFillQuaternary};
|
31
|
+
|
32
|
+
pre {
|
33
|
+
margin: 0 !important;
|
34
|
+
background: none !important;
|
35
|
+
}
|
36
|
+
|
37
|
+
&:hover {
|
38
|
+
.actions {
|
39
|
+
opacity: 1;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
`,
|
43
|
+
editButton: cx(
|
44
|
+
'actions',
|
45
|
+
css`
|
46
|
+
position: absolute;
|
47
|
+
z-index: 10;
|
48
|
+
inset-block-start: 4px;
|
49
|
+
inset-inline-end: 4px;
|
50
|
+
|
51
|
+
opacity: 0;
|
52
|
+
|
53
|
+
transition: opacity 0.2s ${token.motionEaseInOut};
|
54
|
+
`,
|
55
|
+
),
|
56
|
+
}));
|
57
|
+
|
58
|
+
export interface ArgumentsProps {
|
59
|
+
actions?: ReactNode;
|
60
|
+
arguments?: string;
|
61
|
+
shine?: boolean;
|
62
|
+
}
|
63
|
+
|
64
|
+
const Arguments = memo<ArgumentsProps>(({ arguments: args = '', shine, actions }) => {
|
65
|
+
const { styles } = useStyles();
|
66
|
+
|
67
|
+
const displayArgs = useMemo(() => {
|
68
|
+
try {
|
69
|
+
const obj = parse(args);
|
70
|
+
if (Object.keys(obj).length === 0) return {};
|
71
|
+
return obj;
|
72
|
+
} catch {
|
73
|
+
return args;
|
74
|
+
}
|
75
|
+
}, [args]);
|
76
|
+
|
77
|
+
const yaml = useYamlArguments(args);
|
78
|
+
|
79
|
+
const showActions = !!actions;
|
80
|
+
|
81
|
+
if (typeof displayArgs === 'string') {
|
82
|
+
return (
|
83
|
+
!!yaml && (
|
84
|
+
<div className={styles.container}>
|
85
|
+
<Highlighter language={'yaml'} showLanguage={false}>
|
86
|
+
{yaml}
|
87
|
+
</Highlighter>
|
88
|
+
</div>
|
89
|
+
)
|
90
|
+
);
|
91
|
+
}
|
92
|
+
|
93
|
+
const hasMinWidth = Object.keys(displayArgs).length > 1;
|
94
|
+
|
95
|
+
if (Object.keys(displayArgs).length === 0) return null;
|
96
|
+
|
97
|
+
return (
|
98
|
+
<div className={styles.container}>
|
99
|
+
{showActions && (
|
100
|
+
<Flexbox className={styles.editButton} gap={4} horizontal>
|
101
|
+
{actions}
|
102
|
+
</Flexbox>
|
103
|
+
)}
|
104
|
+
{Object.entries(displayArgs).map(([key, value]) => {
|
105
|
+
return (
|
106
|
+
<ObjectEntity
|
107
|
+
editable={false}
|
108
|
+
hasMinWidth={hasMinWidth}
|
109
|
+
key={key}
|
110
|
+
objectKey={key}
|
111
|
+
shine={shine}
|
112
|
+
value={value}
|
113
|
+
/>
|
114
|
+
);
|
115
|
+
})}
|
116
|
+
</div>
|
117
|
+
);
|
118
|
+
});
|
119
|
+
|
120
|
+
export default Arguments;
|
@@ -1,4 +1,9 @@
|
|
1
|
-
import {
|
1
|
+
import { ActionIcon } from '@lobehub/ui';
|
2
|
+
import { App } from 'antd';
|
3
|
+
import { Edit3Icon, PlayCircleIcon } from 'lucide-react';
|
4
|
+
import { parse } from 'partial-json';
|
5
|
+
import { memo, useCallback, useEffect, useState } from 'react';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
2
7
|
import { Flexbox } from 'react-layout-kit';
|
3
8
|
|
4
9
|
import PluginRender from '@/features/PluginsUI/Render';
|
@@ -7,6 +12,16 @@ import { chatSelectors } from '@/store/chat/selectors';
|
|
7
12
|
import { ChatMessage } from '@/types/message';
|
8
13
|
|
9
14
|
import Arguments from './Arguments';
|
15
|
+
import KeyValueEditor from './KeyValueEditor';
|
16
|
+
|
17
|
+
const safeParseJson = (str: string): Record<string, any> => {
|
18
|
+
try {
|
19
|
+
const obj = parse(str);
|
20
|
+
return typeof obj === 'object' && obj !== null ? obj : {};
|
21
|
+
} catch {
|
22
|
+
return {};
|
23
|
+
}
|
24
|
+
};
|
10
25
|
|
11
26
|
interface CustomRenderProps extends ChatMessage {
|
12
27
|
requestArgs?: string;
|
@@ -25,7 +40,37 @@ const CustomRender = memo<CustomRenderProps>(
|
|
25
40
|
setShowPluginRender,
|
26
41
|
pluginError,
|
27
42
|
}) => {
|
43
|
+
const { t } = useTranslation(['tool', 'common']);
|
28
44
|
const [loading] = useChatStore((s) => [chatSelectors.isPluginApiInvoking(id)(s)]);
|
45
|
+
const [isEditing, setIsEditing] = useState(false);
|
46
|
+
const { message } = App.useApp();
|
47
|
+
const [updatePluginArguments, reInvokeToolMessage] = useChatStore((s) => [
|
48
|
+
s.updatePluginArguments,
|
49
|
+
s.reInvokeToolMessage,
|
50
|
+
]);
|
51
|
+
const handleCancel = useCallback(() => {
|
52
|
+
setIsEditing(false);
|
53
|
+
}, []);
|
54
|
+
|
55
|
+
const handleFinish = useCallback(
|
56
|
+
async (editedObject: Record<string, any>) => {
|
57
|
+
if (!id) return;
|
58
|
+
|
59
|
+
try {
|
60
|
+
const newArgsString = JSON.stringify(editedObject, null, 2);
|
61
|
+
|
62
|
+
if (newArgsString !== requestArgs) {
|
63
|
+
await updatePluginArguments(id, editedObject, true);
|
64
|
+
await reInvokeToolMessage(id);
|
65
|
+
}
|
66
|
+
setIsEditing(false);
|
67
|
+
} catch (error) {
|
68
|
+
console.error('Error stringifying arguments:', error);
|
69
|
+
message.error(t('updateArgs.stringifyError'));
|
70
|
+
}
|
71
|
+
},
|
72
|
+
[requestArgs, id],
|
73
|
+
);
|
29
74
|
|
30
75
|
useEffect(() => {
|
31
76
|
if (!plugin?.type || loading) return;
|
@@ -49,8 +94,36 @@ const CustomRender = memo<CustomRenderProps>(
|
|
49
94
|
pluginState={pluginState}
|
50
95
|
type={plugin?.type}
|
51
96
|
/>
|
97
|
+
) : isEditing ? (
|
98
|
+
<KeyValueEditor
|
99
|
+
initialValue={safeParseJson(requestArgs || '')}
|
100
|
+
onCancel={handleCancel}
|
101
|
+
onFinish={handleFinish}
|
102
|
+
/>
|
52
103
|
) : (
|
53
|
-
<Arguments
|
104
|
+
<Arguments
|
105
|
+
actions={
|
106
|
+
<>
|
107
|
+
<ActionIcon
|
108
|
+
icon={Edit3Icon}
|
109
|
+
onClick={() => {
|
110
|
+
setIsEditing(true);
|
111
|
+
}}
|
112
|
+
size={'small'}
|
113
|
+
title={t('edit', { ns: 'common' })}
|
114
|
+
/>
|
115
|
+
<ActionIcon
|
116
|
+
icon={PlayCircleIcon}
|
117
|
+
onClick={async () => {
|
118
|
+
await reInvokeToolMessage(id);
|
119
|
+
}}
|
120
|
+
size={'small'}
|
121
|
+
title={t('run', { ns: 'common' })}
|
122
|
+
/>
|
123
|
+
</>
|
124
|
+
}
|
125
|
+
arguments={requestArgs}
|
126
|
+
/>
|
54
127
|
)}
|
55
128
|
</Flexbox>
|
56
129
|
);
|
@@ -0,0 +1,214 @@
|
|
1
|
+
import { ActionIcon, Icon } from '@lobehub/ui';
|
2
|
+
import { App, Button, Form, FormInstance, Input } from 'antd';
|
3
|
+
import { createStyles } from 'antd-style';
|
4
|
+
import { LucidePlus, LucideTrash } from 'lucide-react';
|
5
|
+
import { memo, useEffect, useRef, useState } from 'react';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
8
|
+
|
9
|
+
const useStyles = createStyles(({ css, token }) => ({
|
10
|
+
form: css`
|
11
|
+
position: relative;
|
12
|
+
|
13
|
+
width: 100%;
|
14
|
+
min-width: 600px;
|
15
|
+
padding-block: 8px;
|
16
|
+
padding-inline: 12px;
|
17
|
+
border: 1px solid ${token.colorBorder};
|
18
|
+
border-radius: ${token.borderRadiusLG}px;
|
19
|
+
`,
|
20
|
+
formItem: css`
|
21
|
+
margin-block-end: 4px !important;
|
22
|
+
`,
|
23
|
+
input: css`
|
24
|
+
font-family: ${token.fontFamilyCode};
|
25
|
+
font-size: 12px;
|
26
|
+
`,
|
27
|
+
row: css`
|
28
|
+
position: relative;
|
29
|
+
`,
|
30
|
+
title: css`
|
31
|
+
color: ${token.colorTextTertiary};
|
32
|
+
`,
|
33
|
+
}));
|
34
|
+
|
35
|
+
interface KeyValueItem {
|
36
|
+
id: string;
|
37
|
+
key?: string;
|
38
|
+
value?: string;
|
39
|
+
}
|
40
|
+
|
41
|
+
interface KeyValueEditorProps {
|
42
|
+
initialValue?: Record<string, any>;
|
43
|
+
onCancel?: () => void;
|
44
|
+
onFinish?: (value: Record<string, any>) => Promise<void>;
|
45
|
+
}
|
46
|
+
|
47
|
+
const recordToFormList = (record: Record<string, any>): KeyValueItem[] =>
|
48
|
+
Object.entries(record)
|
49
|
+
.map(([key, val], index) => ({
|
50
|
+
id: `${key}-${index}`,
|
51
|
+
key,
|
52
|
+
value: typeof val === 'string' ? val : JSON.stringify(val),
|
53
|
+
}))
|
54
|
+
.filter((item) => item.key);
|
55
|
+
|
56
|
+
const formListToRecord = (list: KeyValueItem[]): Record<string, any> => {
|
57
|
+
const record: Record<string, any> = {};
|
58
|
+
list.forEach((item) => {
|
59
|
+
if (item.key) {
|
60
|
+
try {
|
61
|
+
record[item.key] = JSON.parse(item.value || '""');
|
62
|
+
} catch {
|
63
|
+
record[item.key] = item.value || '';
|
64
|
+
}
|
65
|
+
}
|
66
|
+
});
|
67
|
+
return record;
|
68
|
+
};
|
69
|
+
|
70
|
+
const KeyValueEditor = memo<KeyValueEditorProps>(({ initialValue = {}, onFinish, onCancel }) => {
|
71
|
+
const { styles } = useStyles();
|
72
|
+
const { t } = useTranslation(['tool', 'common']);
|
73
|
+
const [form] = Form.useForm();
|
74
|
+
const { message } = App.useApp();
|
75
|
+
const formRef = useRef<FormInstance>(null);
|
76
|
+
|
77
|
+
useEffect(() => {
|
78
|
+
form.setFieldsValue({ items: recordToFormList(initialValue) });
|
79
|
+
}, [initialValue, form]);
|
80
|
+
|
81
|
+
const [updating, setUpdating] = useState(false);
|
82
|
+
const handleFinish = async () => {
|
83
|
+
setUpdating(true);
|
84
|
+
try {
|
85
|
+
await form.validateFields();
|
86
|
+
const values = form.getFieldsValue();
|
87
|
+
const record = formListToRecord(values.items || []);
|
88
|
+
await onFinish?.(record);
|
89
|
+
} catch (errorInfo) {
|
90
|
+
console.error('Validation Failed:', errorInfo);
|
91
|
+
message.error(t('updateArgs.formValidationFailed') || 'Please check the form for errors.');
|
92
|
+
}
|
93
|
+
setUpdating(false);
|
94
|
+
};
|
95
|
+
|
96
|
+
const handleCancel = () => {
|
97
|
+
onCancel?.();
|
98
|
+
};
|
99
|
+
|
100
|
+
const validateKeys = (_: any, item: KeyValueItem, items: KeyValueItem[]) => {
|
101
|
+
if (!item?.key) {
|
102
|
+
return Promise.resolve();
|
103
|
+
}
|
104
|
+
const keys = items.map((i) => i?.key).filter(Boolean);
|
105
|
+
if (keys.filter((k) => k === item.key).length > 1) {
|
106
|
+
return Promise.reject(new Error(t('updateArgs.duplicateKeyError')));
|
107
|
+
}
|
108
|
+
|
109
|
+
return Promise.resolve();
|
110
|
+
};
|
111
|
+
|
112
|
+
return (
|
113
|
+
<Form
|
114
|
+
autoComplete="off"
|
115
|
+
className={styles.form}
|
116
|
+
form={form}
|
117
|
+
initialValues={{ items: recordToFormList(initialValue) }}
|
118
|
+
ref={formRef}
|
119
|
+
>
|
120
|
+
<Flexbox className={styles.title} gap={8} horizontal>
|
121
|
+
<Flexbox flex={1}>key</Flexbox>
|
122
|
+
<Flexbox flex={4}>value</Flexbox>
|
123
|
+
</Flexbox>
|
124
|
+
<Form.List name="items">
|
125
|
+
{(fields, { add, remove }) => (
|
126
|
+
<Flexbox width={'100%'}>
|
127
|
+
{fields.map(({ key, name, ...restField }, index) => (
|
128
|
+
<Flexbox
|
129
|
+
align="center"
|
130
|
+
className={styles.row}
|
131
|
+
gap={8}
|
132
|
+
horizontal
|
133
|
+
key={key}
|
134
|
+
width={'100%'}
|
135
|
+
>
|
136
|
+
<Form.Item
|
137
|
+
{...restField}
|
138
|
+
className={styles.formItem}
|
139
|
+
name={[name, 'key']}
|
140
|
+
rules={[
|
141
|
+
{ message: t('updateArgs.keyRequired'), required: true },
|
142
|
+
{
|
143
|
+
validator: (rule) =>
|
144
|
+
validateKeys(
|
145
|
+
rule,
|
146
|
+
form.getFieldValue(['items', index]),
|
147
|
+
form.getFieldValue('items'),
|
148
|
+
),
|
149
|
+
},
|
150
|
+
]}
|
151
|
+
style={{ flex: 1 }}
|
152
|
+
validateTrigger={['onChange', 'onBlur']}
|
153
|
+
>
|
154
|
+
<Input
|
155
|
+
allowClear
|
156
|
+
className={styles.input}
|
157
|
+
placeholder={t('updateArgs.form.key')}
|
158
|
+
variant={'filled'}
|
159
|
+
/>
|
160
|
+
</Form.Item>
|
161
|
+
<Form.Item
|
162
|
+
{...restField}
|
163
|
+
className={styles.formItem}
|
164
|
+
name={[name, 'value']}
|
165
|
+
style={{ flex: 4 }}
|
166
|
+
>
|
167
|
+
<Input
|
168
|
+
allowClear
|
169
|
+
className={styles.input}
|
170
|
+
placeholder={t('updateArgs.form.value')}
|
171
|
+
variant={'filled'}
|
172
|
+
/>
|
173
|
+
</Form.Item>
|
174
|
+
<ActionIcon
|
175
|
+
icon={LucideTrash}
|
176
|
+
onClick={() => remove(name)}
|
177
|
+
size={'small'}
|
178
|
+
style={{
|
179
|
+
marginBottom: 6,
|
180
|
+
}}
|
181
|
+
title={t('delete', { ns: 'common' })}
|
182
|
+
/>
|
183
|
+
</Flexbox>
|
184
|
+
))}
|
185
|
+
<Form.Item style={{ marginBottom: 0, marginTop: 8 }}>
|
186
|
+
<Flexbox gap={8} horizontal justify={'space-between'}>
|
187
|
+
<Button
|
188
|
+
color={'default'}
|
189
|
+
icon={<Icon icon={LucidePlus} />}
|
190
|
+
onClick={() => add({ id: `new-${Date.now()}`, key: '', value: '' })}
|
191
|
+
size={'small'}
|
192
|
+
variant="filled"
|
193
|
+
>
|
194
|
+
{t('updateArgs.form.add')}
|
195
|
+
</Button>
|
196
|
+
|
197
|
+
<Flexbox gap={8} horizontal>
|
198
|
+
<Button onClick={handleCancel} size={'small'}>
|
199
|
+
{t('cancel', { ns: 'common' })}
|
200
|
+
</Button>
|
201
|
+
<Button loading={updating} onClick={handleFinish} size={'small'} type={'primary'}>
|
202
|
+
{t('save', { ns: 'common' })}
|
203
|
+
</Button>
|
204
|
+
</Flexbox>
|
205
|
+
</Flexbox>
|
206
|
+
</Form.Item>
|
207
|
+
</Flexbox>
|
208
|
+
)}
|
209
|
+
</Form.List>
|
210
|
+
</Form>
|
211
|
+
);
|
212
|
+
});
|
213
|
+
|
214
|
+
export default KeyValueEditor;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { DiscordIcon, Icon } from '@lobehub/ui';
|
1
|
+
import { DiscordIcon, Hotkey, Icon } from '@lobehub/ui';
|
2
2
|
import { Badge } from 'antd';
|
3
3
|
import { ItemType } from 'antd/es/menu/interface';
|
4
4
|
import {
|
@@ -22,6 +22,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
22
22
|
import type { MenuProps } from '@/components/Menu';
|
23
23
|
import { enableAuth } from '@/const/auth';
|
24
24
|
import { LOBE_CHAT_CLOUD } from '@/const/branding';
|
25
|
+
import { DEFAULT_HOTKEY_CONFIG } from '@/const/settings';
|
25
26
|
import {
|
26
27
|
DISCORD,
|
27
28
|
DOCUMENTS_REFER_URL,
|
@@ -31,6 +32,7 @@ import {
|
|
31
32
|
UTM_SOURCE,
|
32
33
|
mailTo,
|
33
34
|
} from '@/const/url';
|
35
|
+
import { isDesktop } from '@/const/version';
|
34
36
|
import DataImporter from '@/features/DataImporter';
|
35
37
|
import { usePWAInstall } from '@/hooks/usePWAInstall';
|
36
38
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
@@ -81,6 +83,11 @@ export const useMenu = () => {
|
|
81
83
|
|
82
84
|
const settings: MenuProps['items'] = [
|
83
85
|
{
|
86
|
+
extra: isDesktop ? (
|
87
|
+
<div>
|
88
|
+
<Hotkey keys={DEFAULT_HOTKEY_CONFIG.openSettings} />
|
89
|
+
</div>
|
90
|
+
) : undefined,
|
84
91
|
icon: <Icon icon={Settings2} />,
|
85
92
|
key: 'setting',
|
86
93
|
label: (
|
@@ -368,6 +368,9 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
368
368
|
payload?: ChatStreamPayload,
|
369
369
|
): GoogleFunctionCallTool[] | undefined {
|
370
370
|
// 目前 Tools (例如 googleSearch) 无法与其他 FunctionCall 同时使用
|
371
|
+
if (payload?.messages?.some(m => m.tool_calls?.length)) {
|
372
|
+
return; // 若历史消息中已有 function calling,则不再注入任何 Tools
|
373
|
+
}
|
371
374
|
if (payload?.enabledSearch) {
|
372
375
|
return [{ googleSearch: {} } as GoogleSearchRetrievalTool];
|
373
376
|
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { createTRPCClient, httpBatchLink } from '@trpc/client';
|
2
|
+
import superjson from 'superjson';
|
3
|
+
|
4
|
+
import type { DesktopRouter } from '@/server/routers/desktop';
|
5
|
+
|
6
|
+
export const desktopClient = createTRPCClient<DesktopRouter>({
|
7
|
+
links: [
|
8
|
+
httpBatchLink({
|
9
|
+
maxURLLength: 2083,
|
10
|
+
transformer: superjson,
|
11
|
+
url: '/trpc/desktop',
|
12
|
+
}),
|
13
|
+
],
|
14
|
+
});
|
@@ -0,0 +1,34 @@
|
|
1
|
+
const electron = {
|
2
|
+
remoteServer: {
|
3
|
+
authError: '授权失败: {{error}}',
|
4
|
+
authPending: '请在浏览器中完成授权',
|
5
|
+
configDesc: '连接到远程LobeChat服务器,启用数据同步',
|
6
|
+
configError: '配置出错',
|
7
|
+
configTitle: '配置云同步',
|
8
|
+
connect: '连接并授权',
|
9
|
+
connected: '已连接',
|
10
|
+
disconnect: '断开连接',
|
11
|
+
disconnectError: '断开连接失败',
|
12
|
+
disconnected: '未连接',
|
13
|
+
fetchError: '获取配置失败',
|
14
|
+
invalidUrl: '请输入有效的URL地址',
|
15
|
+
serverUrl: '服务器地址',
|
16
|
+
statusConnected: '已连接',
|
17
|
+
statusDisconnected: '未连接',
|
18
|
+
urlRequired: '请输入服务器地址',
|
19
|
+
},
|
20
|
+
updater: {
|
21
|
+
downloadingUpdate: '正在下载更新',
|
22
|
+
downloadingUpdateDesc: '更新正在下载中,请稍候...',
|
23
|
+
later: '稍后更新',
|
24
|
+
newVersionAvailable: '新版本可用',
|
25
|
+
newVersionAvailableDesc: '发现新版本 {{version}},是否立即下载?',
|
26
|
+
restartAndInstall: '重启并安装',
|
27
|
+
updateError: '更新错误',
|
28
|
+
updateReady: '更新已就绪',
|
29
|
+
updateReadyDesc: 'Lobe Chat {{version}} 已下载完成,重启应用后即可完成安装。',
|
30
|
+
upgradeNow: '立即更新',
|
31
|
+
},
|
32
|
+
};
|
33
|
+
|
34
|
+
export default electron;
|
@@ -5,6 +5,7 @@ import clerk from './clerk';
|
|
5
5
|
import common from './common';
|
6
6
|
import components from './components';
|
7
7
|
import discover from './discover';
|
8
|
+
import electron from './electron';
|
8
9
|
import error from './error';
|
9
10
|
import file from './file';
|
10
11
|
import hotkey from './hotkey';
|
@@ -32,6 +33,7 @@ const resources = {
|
|
32
33
|
common,
|
33
34
|
components,
|
34
35
|
discover,
|
36
|
+
electron,
|
35
37
|
error,
|
36
38
|
file,
|
37
39
|
hotkey,
|
@@ -7,6 +7,20 @@ export default {
|
|
7
7
|
images: '图片:',
|
8
8
|
prompt: '提示词',
|
9
9
|
},
|
10
|
+
localFiles: {
|
11
|
+
file: '文件',
|
12
|
+
folder: '文件夹',
|
13
|
+
open: '打开',
|
14
|
+
openFile: '打开文件',
|
15
|
+
openFolder: '打开文件夹',
|
16
|
+
read: {
|
17
|
+
more: '查看更多',
|
18
|
+
},
|
19
|
+
readFile: '读取文件',
|
20
|
+
readFileError: '读取文件失败,请检查文件路径是否正确',
|
21
|
+
readFiles: '读取文件',
|
22
|
+
readFilesError: '读取文件失败,请检查文件路径是否正确',
|
23
|
+
},
|
10
24
|
search: {
|
11
25
|
createNewSearch: '创建新的搜索记录',
|
12
26
|
emptyResult: '没有搜索到结果,请修改关键词后重试',
|
@@ -54,4 +68,15 @@ export default {
|
|
54
68
|
summaryTooltip: '总结当前内容',
|
55
69
|
viewMoreResults: '查看更多 {{results}} 个结果',
|
56
70
|
},
|
71
|
+
updateArgs: {
|
72
|
+
duplicateKeyError: '字段键必须唯一',
|
73
|
+
form: {
|
74
|
+
add: '添加一项',
|
75
|
+
key: '字段键',
|
76
|
+
value: '字段值',
|
77
|
+
},
|
78
|
+
formValidationFailed: '表单验证失败,请检查参数格式',
|
79
|
+
keyRequired: '字段键不能为空',
|
80
|
+
stringifyError: '无法序列化参数,请检查参数格式',
|
81
|
+
},
|
57
82
|
};
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
|
3
|
+
import { DESKTOP_USER_ID } from '@/const/desktop';
|
4
|
+
import { TableViewerRepo } from '@/database/repositories/tableViewer';
|
5
|
+
import { publicProcedure, router } from '@/libs/trpc/lambda';
|
6
|
+
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
7
|
+
|
8
|
+
const pgTableProcedure = publicProcedure.use(serverDatabase).use(async ({ ctx, next }) => {
|
9
|
+
return next({
|
10
|
+
ctx: {
|
11
|
+
tableViewerRepo: new TableViewerRepo(ctx.serverDB, DESKTOP_USER_ID),
|
12
|
+
},
|
13
|
+
});
|
14
|
+
});
|
15
|
+
|
16
|
+
export const pgTableRouter = router({
|
17
|
+
getAllTables: pgTableProcedure.query(async ({ ctx }) => {
|
18
|
+
return ctx.tableViewerRepo.getAllTables();
|
19
|
+
}),
|
20
|
+
getTableData: pgTableProcedure
|
21
|
+
.input(
|
22
|
+
z.object({
|
23
|
+
page: z.number(),
|
24
|
+
pageSize: z.number(),
|
25
|
+
tableName: z.string(),
|
26
|
+
}),
|
27
|
+
)
|
28
|
+
.query(async ({ input, ctx }) => {
|
29
|
+
return ctx.tableViewerRepo.getTableData(input.tableName, {
|
30
|
+
page: input.page,
|
31
|
+
pageSize: input.pageSize,
|
32
|
+
});
|
33
|
+
}),
|
34
|
+
getTableDetails: pgTableProcedure
|
35
|
+
.input(
|
36
|
+
z.object({
|
37
|
+
tableName: z.string(),
|
38
|
+
}),
|
39
|
+
)
|
40
|
+
.query(async ({ input, ctx }) => {
|
41
|
+
return ctx.tableViewerRepo.getTableDetails(input.tableName);
|
42
|
+
}),
|
43
|
+
});
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { dispatch } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
class AutoUpdateService {
|
4
|
+
checkUpdate = async () => {
|
5
|
+
return dispatch('checkUpdate');
|
6
|
+
};
|
7
|
+
|
8
|
+
installNow = async () => {
|
9
|
+
return dispatch('installNow');
|
10
|
+
};
|
11
|
+
|
12
|
+
installLater = async () => {
|
13
|
+
return dispatch('installLater');
|
14
|
+
};
|
15
|
+
}
|
16
|
+
|
17
|
+
export const autoUpdateService = new AutoUpdateService();
|