@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.
Files changed (151) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/changelog/v1.json +12 -0
  3. package/locales/ar/common.json +2 -0
  4. package/locales/ar/electron.json +32 -0
  5. package/locales/ar/models.json +126 -3
  6. package/locales/ar/plugin.json +1 -0
  7. package/locales/ar/tool.json +25 -0
  8. package/locales/bg-BG/common.json +2 -0
  9. package/locales/bg-BG/electron.json +32 -0
  10. package/locales/bg-BG/models.json +126 -3
  11. package/locales/bg-BG/plugin.json +1 -0
  12. package/locales/bg-BG/tool.json +25 -0
  13. package/locales/de-DE/common.json +2 -0
  14. package/locales/de-DE/electron.json +32 -0
  15. package/locales/de-DE/models.json +126 -3
  16. package/locales/de-DE/plugin.json +1 -0
  17. package/locales/de-DE/tool.json +25 -0
  18. package/locales/en-US/common.json +2 -0
  19. package/locales/en-US/electron.json +32 -0
  20. package/locales/en-US/models.json +126 -3
  21. package/locales/en-US/plugin.json +1 -0
  22. package/locales/en-US/tool.json +25 -0
  23. package/locales/es-ES/common.json +2 -0
  24. package/locales/es-ES/electron.json +32 -0
  25. package/locales/es-ES/models.json +126 -3
  26. package/locales/es-ES/plugin.json +1 -0
  27. package/locales/es-ES/tool.json +25 -0
  28. package/locales/fa-IR/common.json +2 -0
  29. package/locales/fa-IR/electron.json +32 -0
  30. package/locales/fa-IR/models.json +126 -3
  31. package/locales/fa-IR/plugin.json +1 -0
  32. package/locales/fa-IR/tool.json +25 -0
  33. package/locales/fr-FR/common.json +2 -0
  34. package/locales/fr-FR/electron.json +32 -0
  35. package/locales/fr-FR/models.json +126 -3
  36. package/locales/fr-FR/plugin.json +1 -0
  37. package/locales/fr-FR/tool.json +25 -0
  38. package/locales/it-IT/common.json +2 -0
  39. package/locales/it-IT/electron.json +32 -0
  40. package/locales/it-IT/models.json +126 -3
  41. package/locales/it-IT/plugin.json +1 -0
  42. package/locales/it-IT/tool.json +25 -0
  43. package/locales/ja-JP/common.json +2 -0
  44. package/locales/ja-JP/electron.json +32 -0
  45. package/locales/ja-JP/models.json +126 -3
  46. package/locales/ja-JP/plugin.json +1 -0
  47. package/locales/ja-JP/tool.json +25 -0
  48. package/locales/ko-KR/common.json +2 -0
  49. package/locales/ko-KR/electron.json +32 -0
  50. package/locales/ko-KR/models.json +126 -3
  51. package/locales/ko-KR/plugin.json +1 -0
  52. package/locales/ko-KR/tool.json +25 -0
  53. package/locales/nl-NL/common.json +2 -0
  54. package/locales/nl-NL/electron.json +32 -0
  55. package/locales/nl-NL/models.json +126 -3
  56. package/locales/nl-NL/plugin.json +1 -0
  57. package/locales/nl-NL/tool.json +25 -0
  58. package/locales/pl-PL/common.json +2 -0
  59. package/locales/pl-PL/electron.json +32 -0
  60. package/locales/pl-PL/models.json +126 -3
  61. package/locales/pl-PL/plugin.json +1 -0
  62. package/locales/pl-PL/tool.json +25 -0
  63. package/locales/pt-BR/common.json +2 -0
  64. package/locales/pt-BR/electron.json +32 -0
  65. package/locales/pt-BR/models.json +126 -3
  66. package/locales/pt-BR/plugin.json +1 -0
  67. package/locales/pt-BR/tool.json +25 -0
  68. package/locales/ru-RU/common.json +2 -0
  69. package/locales/ru-RU/electron.json +32 -0
  70. package/locales/ru-RU/models.json +126 -3
  71. package/locales/ru-RU/plugin.json +1 -0
  72. package/locales/ru-RU/tool.json +25 -0
  73. package/locales/tr-TR/common.json +2 -0
  74. package/locales/tr-TR/electron.json +32 -0
  75. package/locales/tr-TR/models.json +126 -3
  76. package/locales/tr-TR/plugin.json +1 -0
  77. package/locales/tr-TR/tool.json +25 -0
  78. package/locales/vi-VN/common.json +2 -0
  79. package/locales/vi-VN/electron.json +32 -0
  80. package/locales/vi-VN/models.json +126 -3
  81. package/locales/vi-VN/plugin.json +1 -0
  82. package/locales/vi-VN/tool.json +25 -0
  83. package/locales/zh-CN/common.json +2 -0
  84. package/locales/zh-CN/electron.json +32 -0
  85. package/locales/zh-CN/models.json +131 -8
  86. package/locales/zh-CN/plugin.json +1 -0
  87. package/locales/zh-CN/tool.json +25 -0
  88. package/locales/zh-TW/common.json +2 -0
  89. package/locales/zh-TW/electron.json +32 -0
  90. package/locales/zh-TW/models.json +126 -3
  91. package/locales/zh-TW/plugin.json +1 -0
  92. package/locales/zh-TW/tool.json +25 -0
  93. package/package.json +3 -2
  94. package/packages/electron-client-ipc/src/events/index.ts +5 -5
  95. package/packages/electron-client-ipc/src/events/localFile.ts +22 -0
  96. package/packages/electron-client-ipc/src/events/{file.ts → upload.ts} +1 -1
  97. package/packages/electron-client-ipc/src/types/index.ts +2 -1
  98. package/packages/electron-client-ipc/src/types/localFile.ts +52 -0
  99. package/scripts/prebuild.mts +5 -1
  100. package/src/app/(backend)/trpc/desktop/[trpc]/route.ts +26 -0
  101. package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/ObjectEntity.tsx +81 -0
  102. package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/ValueCell.tsx +43 -0
  103. package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/index.tsx +120 -0
  104. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +75 -2
  105. package/src/features/Conversation/Messages/Assistant/Tool/Render/KeyValueEditor.tsx +214 -0
  106. package/src/features/User/UserPanel/useMenu.tsx +8 -1
  107. package/src/libs/agent-runtime/google/index.ts +3 -0
  108. package/src/libs/trpc/client/desktop.ts +14 -0
  109. package/src/locales/default/common.ts +2 -0
  110. package/src/locales/default/electron.ts +34 -0
  111. package/src/locales/default/index.ts +2 -0
  112. package/src/locales/default/tool.ts +25 -0
  113. package/src/server/routers/desktop/index.ts +9 -0
  114. package/src/server/routers/desktop/pgTable.ts +43 -0
  115. package/src/services/electron/autoUpdate.ts +17 -0
  116. package/src/services/electron/file.ts +31 -0
  117. package/src/services/electron/localFileService.ts +39 -0
  118. package/src/services/electron/remoteServer.ts +40 -0
  119. package/src/store/chat/index.ts +1 -1
  120. package/src/store/chat/slices/builtinTool/actions/index.ts +3 -1
  121. package/src/store/chat/slices/builtinTool/actions/localFile.ts +129 -0
  122. package/src/store/chat/slices/builtinTool/initialState.ts +2 -0
  123. package/src/store/chat/slices/builtinTool/selectors.ts +2 -0
  124. package/src/store/chat/slices/plugin/action.ts +3 -3
  125. package/src/store/chat/store.ts +2 -0
  126. package/src/store/electron/actions/sync.ts +117 -0
  127. package/src/store/electron/index.ts +1 -0
  128. package/src/store/electron/initialState.ts +18 -0
  129. package/src/store/electron/selectors/index.ts +1 -0
  130. package/src/store/electron/selectors/sync.ts +9 -0
  131. package/src/store/electron/store.ts +29 -0
  132. package/src/tools/index.ts +8 -0
  133. package/src/tools/local-files/Render/ListFiles/Result.tsx +42 -0
  134. package/src/tools/local-files/Render/ListFiles/index.tsx +68 -0
  135. package/src/tools/local-files/Render/ReadLocalFile/ReadFileSkeleton.tsx +50 -0
  136. package/src/tools/local-files/Render/ReadLocalFile/ReadFileView.tsx +197 -0
  137. package/src/tools/local-files/Render/ReadLocalFile/index.tsx +31 -0
  138. package/src/tools/local-files/Render/ReadLocalFile/style.ts +37 -0
  139. package/src/tools/local-files/Render/SearchFiles/Result.tsx +42 -0
  140. package/src/tools/local-files/Render/SearchFiles/SearchQuery/SearchView.tsx +77 -0
  141. package/src/tools/local-files/Render/SearchFiles/SearchQuery/index.tsx +72 -0
  142. package/src/tools/local-files/Render/SearchFiles/index.tsx +32 -0
  143. package/src/tools/local-files/Render/index.tsx +36 -0
  144. package/src/tools/local-files/components/FileItem.tsx +117 -0
  145. package/src/tools/local-files/index.ts +149 -0
  146. package/src/tools/local-files/systemRole.ts +46 -0
  147. package/src/tools/local-files/type.ts +33 -0
  148. package/src/tools/renders.ts +3 -0
  149. package/packages/electron-client-ipc/src/events/search.ts +0 -4
  150. package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments.tsx +0 -165
  151. /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 { memo, useEffect } from 'react';
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 arguments={requestArgs} />
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
+ });
@@ -287,6 +287,8 @@ export default {
287
287
  rename: '重命名',
288
288
  reset: '重置',
289
289
  retry: '重试',
290
+ run: '运行',
291
+ save: '保存',
290
292
  send: '发送',
291
293
  setting: '设置',
292
294
  share: '分享',
@@ -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,9 @@
1
+ import { router } from '@/libs/trpc/lambda';
2
+
3
+ import { pgTableRouter } from './pgTable';
4
+
5
+ export const desktopRouter = router({
6
+ pgTable: pgTableRouter,
7
+ });
8
+
9
+ export type DesktopRouter = typeof desktopRouter;
@@ -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();