@lobehub/chat 0.157.1 → 0.158.0
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/.eslintignore +1 -2
- package/CHANGELOG.md +50 -0
- package/README.md +14 -14
- package/README.zh-CN.md +14 -14
- package/locales/ar/auth.json +2 -0
- package/locales/ar/chat.json +1 -0
- package/locales/ar/common.json +1 -0
- package/locales/bg-BG/auth.json +2 -0
- package/locales/bg-BG/chat.json +1 -0
- package/locales/bg-BG/common.json +1 -0
- package/locales/bg-BG/error.json +3 -3
- package/locales/de-DE/auth.json +2 -0
- package/locales/de-DE/chat.json +1 -0
- package/locales/de-DE/common.json +1 -0
- package/locales/en-US/auth.json +2 -0
- package/locales/en-US/chat.json +1 -0
- package/locales/en-US/common.json +1 -0
- package/locales/es-ES/auth.json +2 -0
- package/locales/es-ES/chat.json +1 -0
- package/locales/es-ES/common.json +1 -0
- package/locales/fr-FR/auth.json +2 -0
- package/locales/fr-FR/chat.json +1 -0
- package/locales/fr-FR/common.json +1 -0
- package/locales/it-IT/auth.json +2 -0
- package/locales/it-IT/chat.json +1 -0
- package/locales/it-IT/common.json +1 -0
- package/locales/ja-JP/auth.json +2 -0
- package/locales/ja-JP/chat.json +1 -0
- package/locales/ja-JP/common.json +1 -0
- package/locales/ko-KR/auth.json +2 -0
- package/locales/ko-KR/chat.json +1 -0
- package/locales/ko-KR/common.json +1 -0
- package/locales/nl-NL/auth.json +2 -0
- package/locales/nl-NL/chat.json +1 -0
- package/locales/nl-NL/common.json +50 -49
- package/locales/pl-PL/auth.json +2 -0
- package/locales/pl-PL/chat.json +1 -0
- package/locales/pl-PL/common.json +1 -0
- package/locales/pl-PL/error.json +3 -3
- package/locales/pt-BR/auth.json +2 -0
- package/locales/pt-BR/chat.json +1 -0
- package/locales/pt-BR/common.json +1 -0
- package/locales/ru-RU/auth.json +2 -0
- package/locales/ru-RU/chat.json +1 -0
- package/locales/ru-RU/common.json +1 -0
- package/locales/ru-RU/error.json +3 -3
- package/locales/tr-TR/auth.json +2 -0
- package/locales/tr-TR/chat.json +1 -0
- package/locales/tr-TR/common.json +1 -0
- package/locales/vi-VN/auth.json +2 -0
- package/locales/vi-VN/chat.json +1 -0
- package/locales/vi-VN/common.json +1 -0
- package/locales/zh-CN/auth.json +2 -0
- package/locales/zh-CN/chat.json +1 -0
- package/locales/zh-CN/common.json +1 -0
- package/locales/zh-TW/auth.json +2 -0
- package/locales/zh-TW/chat.json +1 -0
- package/locales/zh-TW/common.json +1 -0
- package/package.json +2 -2
- package/src/app/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +80 -0
- package/src/app/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +116 -0
- package/src/app/(main)/(mobile)/me/(home)/features/Category.tsx +15 -0
- package/src/app/(main)/(mobile)/me/(home)/features/UserBanner.tsx +37 -0
- package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +95 -0
- package/src/app/(main)/(mobile)/me/{page.tsx → (home)/page.tsx} +6 -10
- package/src/app/(main)/(mobile)/me/data/features/Category.tsx +48 -0
- package/src/app/(main)/(mobile)/me/data/features/Header.tsx +33 -0
- package/src/app/(main)/(mobile)/me/data/layout.tsx +13 -0
- package/src/app/(main)/(mobile)/me/data/loading.tsx +5 -0
- package/src/app/(main)/(mobile)/me/data/page.tsx +17 -0
- package/src/app/(main)/(mobile)/me/profile/features/Category.tsx +45 -0
- package/src/app/(main)/(mobile)/me/profile/features/Header.tsx +33 -0
- package/src/app/(main)/(mobile)/me/profile/layout.tsx +16 -0
- package/src/app/(main)/(mobile)/me/profile/loading.tsx +5 -0
- package/src/app/(main)/(mobile)/me/profile/page.tsx +17 -0
- package/src/app/(main)/(mobile)/me/settings/features/Category.tsx +15 -0
- package/src/app/(main)/(mobile)/me/settings/features/Header.tsx +33 -0
- package/src/app/(main)/(mobile)/me/settings/features/useCategory.tsx +57 -0
- package/src/app/(main)/(mobile)/me/settings/layout.tsx +13 -0
- package/src/app/(main)/(mobile)/me/settings/loading.tsx +5 -0
- package/src/app/(main)/(mobile)/me/settings/page.tsx +17 -0
- package/src/app/(main)/_layout/Mobile.tsx +5 -4
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +21 -1
- package/src/app/(main)/profile/[[...slugs]]/Client.tsx +74 -0
- package/src/app/(main)/profile/[[...slugs]]/page.tsx +18 -0
- package/src/app/(main)/profile/_layout/Mobile/Header.tsx +26 -0
- package/src/app/(main)/profile/_layout/Mobile/index.tsx +16 -0
- package/src/app/(main)/profile/layout.tsx +20 -0
- package/src/app/(main)/profile/loading.tsx +23 -0
- package/src/app/(main)/settings/_layout/Mobile/Header.tsx +2 -1
- package/src/app/(main)/settings/hooks/useCategory.tsx +7 -13
- package/src/app/@modal/layout.tsx +3 -0
- package/src/components/Cell/Divider.tsx +3 -2
- package/src/components/Cell/index.tsx +28 -18
- package/src/features/User/DataStatistics.tsx +3 -1
- package/src/features/User/UserLoginOrSignup.tsx +2 -2
- package/src/features/User/UserPanel/PanelContent.tsx +9 -3
- package/src/features/User/UserPanel/useMenu.tsx +29 -29
- package/src/features/User/__tests__/PanelContent.test.tsx +7 -0
- package/src/features/User/__tests__/useMenu.test.tsx +142 -0
- package/src/layout/AuthProvider/Clerk/useAppearance.ts +5 -4
- package/src/libs/agent-runtime/azureOpenai/index.test.ts +161 -27
- package/src/libs/agent-runtime/utils/streams/openai.ts +4 -0
- package/src/locales/default/auth.ts +2 -0
- package/src/locales/default/chat.ts +1 -0
- package/src/locales/default/common.ts +1 -0
- package/src/store/user/slices/auth/selectors.ts +2 -1
- package/src/app/(auth)/profile/[[...slugs]]/PageTitle.tsx +0 -13
- package/src/app/(auth)/profile/[[...slugs]]/page.tsx +0 -14
- package/src/app/(main)/(mobile)/me/features/Cate.tsx +0 -33
- package/src/app/(main)/(mobile)/me/features/ExtraCate.tsx +0 -24
- package/src/app/(main)/(mobile)/me/features/useExtraCate.tsx +0 -62
- /package/src/app/(main)/(mobile)/me/{features → (home)/features}/Header.tsx +0 -0
- /package/src/app/(main)/(mobile)/me/{layout.tsx → (home)/layout.tsx} +0 -0
- /package/src/app/(main)/(mobile)/me/{loading.tsx → (home)/loading.tsx} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @vitest-environment node
|
|
2
2
|
import { AzureKeyCredential, OpenAIClient } from '@azure/openai';
|
|
3
|
+
import OpenAI from 'openai';
|
|
3
4
|
import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
5
|
|
|
5
6
|
import * as debugStreamModule from '../utils/debugStream';
|
|
@@ -50,7 +51,7 @@ describe('LobeAzureOpenAI', () => {
|
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
describe('chat', () => {
|
|
53
|
-
it('should return a
|
|
54
|
+
it('should return a Response on successful API call', async () => {
|
|
54
55
|
// Arrange
|
|
55
56
|
const mockStream = new ReadableStream();
|
|
56
57
|
const mockResponse = Promise.resolve(mockStream);
|
|
@@ -68,6 +69,140 @@ describe('LobeAzureOpenAI', () => {
|
|
|
68
69
|
expect(result).toBeInstanceOf(Response);
|
|
69
70
|
});
|
|
70
71
|
|
|
72
|
+
describe('streaming response', () => {
|
|
73
|
+
it('should handle multiple data chunks correctly', async () => {
|
|
74
|
+
const data = [
|
|
75
|
+
{
|
|
76
|
+
choices: [],
|
|
77
|
+
created: 0,
|
|
78
|
+
id: '',
|
|
79
|
+
model: '',
|
|
80
|
+
object: '',
|
|
81
|
+
prompt_filter_results: [
|
|
82
|
+
{
|
|
83
|
+
prompt_index: 0,
|
|
84
|
+
content_filter_results: {
|
|
85
|
+
hate: { filtered: false, severity: 'safe' },
|
|
86
|
+
self_harm: { filtered: false, severity: 'safe' },
|
|
87
|
+
sexual: { filtered: false, severity: 'safe' },
|
|
88
|
+
violence: { filtered: false, severity: 'safe' },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
choices: [
|
|
95
|
+
{
|
|
96
|
+
content_filter_results: {
|
|
97
|
+
hate: { filtered: false, severity: 'safe' },
|
|
98
|
+
self_harm: { filtered: false, severity: 'safe' },
|
|
99
|
+
sexual: { filtered: false, severity: 'safe' },
|
|
100
|
+
violence: { filtered: false, severity: 'safe' },
|
|
101
|
+
},
|
|
102
|
+
delta: { content: '你' },
|
|
103
|
+
finish_reason: null,
|
|
104
|
+
index: 0,
|
|
105
|
+
logprobs: null,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
created: 1715516381,
|
|
109
|
+
id: 'chatcmpl-9O2SzeGv5xy6yz0TcQNA1DHHLJ8N1',
|
|
110
|
+
model: 'gpt-35-turbo-16k',
|
|
111
|
+
object: 'chat.completion.chunk',
|
|
112
|
+
system_fingerprint: null,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
choices: [
|
|
116
|
+
{
|
|
117
|
+
content_filter_results: {
|
|
118
|
+
hate: { filtered: false, severity: 'safe' },
|
|
119
|
+
self_harm: { filtered: false, severity: 'safe' },
|
|
120
|
+
sexual: { filtered: false, severity: 'safe' },
|
|
121
|
+
violence: { filtered: false, severity: 'safe' },
|
|
122
|
+
},
|
|
123
|
+
delta: { content: '好' },
|
|
124
|
+
finish_reason: null,
|
|
125
|
+
index: 0,
|
|
126
|
+
logprobs: null,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
created: 1715516381,
|
|
130
|
+
id: 'chatcmpl-9O2SzeGv5xy6yz0TcQNA1DHHLJ8N1',
|
|
131
|
+
model: 'gpt-35-turbo-16k',
|
|
132
|
+
object: 'chat.completion.chunk',
|
|
133
|
+
system_fingerprint: null,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
choices: [
|
|
137
|
+
{
|
|
138
|
+
content_filter_results: {
|
|
139
|
+
hate: { filtered: false, severity: 'safe' },
|
|
140
|
+
self_harm: { filtered: false, severity: 'safe' },
|
|
141
|
+
sexual: { filtered: false, severity: 'safe' },
|
|
142
|
+
violence: { filtered: false, severity: 'safe' },
|
|
143
|
+
},
|
|
144
|
+
delta: { content: '!' },
|
|
145
|
+
finish_reason: null,
|
|
146
|
+
index: 0,
|
|
147
|
+
logprobs: null,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
created: 1715516381,
|
|
151
|
+
id: 'chatcmpl-9O2SzeGv5xy6yz0TcQNA1DHHLJ8N1',
|
|
152
|
+
model: 'gpt-35-turbo-16k',
|
|
153
|
+
object: 'chat.completion.chunk',
|
|
154
|
+
system_fingerprint: null,
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
const mockStream = new ReadableStream({
|
|
159
|
+
start(controller) {
|
|
160
|
+
data.forEach((chunk) => controller.enqueue(chunk));
|
|
161
|
+
controller.close();
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
vi.spyOn(instance['client'], 'streamChatCompletions').mockResolvedValue(mockStream as any);
|
|
165
|
+
|
|
166
|
+
const result = await instance.chat({
|
|
167
|
+
stream: true,
|
|
168
|
+
max_tokens: 2048,
|
|
169
|
+
temperature: 0.6,
|
|
170
|
+
top_p: 1,
|
|
171
|
+
model: 'gpt-35-turbo-16k',
|
|
172
|
+
presence_penalty: 0,
|
|
173
|
+
frequency_penalty: 0,
|
|
174
|
+
messages: [{ role: 'user', content: '你好' }],
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const decoder = new TextDecoder();
|
|
178
|
+
const reader = result.body!.getReader();
|
|
179
|
+
const stream: string[] = [];
|
|
180
|
+
|
|
181
|
+
while (true) {
|
|
182
|
+
const { value, done } = await reader.read();
|
|
183
|
+
if (done) break;
|
|
184
|
+
stream.push(decoder.decode(value));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
expect(stream).toEqual(
|
|
188
|
+
[
|
|
189
|
+
'id: ',
|
|
190
|
+
'event: data',
|
|
191
|
+
'data: {"choices":[],"created":0,"id":"","model":"","object":"","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}]}\n',
|
|
192
|
+
'id: chatcmpl-9O2SzeGv5xy6yz0TcQNA1DHHLJ8N1',
|
|
193
|
+
'event: text',
|
|
194
|
+
'data: "你"\n',
|
|
195
|
+
'id: chatcmpl-9O2SzeGv5xy6yz0TcQNA1DHHLJ8N1',
|
|
196
|
+
'event: text',
|
|
197
|
+
'data: "好"\n',
|
|
198
|
+
'id: chatcmpl-9O2SzeGv5xy6yz0TcQNA1DHHLJ8N1',
|
|
199
|
+
'event: text',
|
|
200
|
+
'data: "!"\n',
|
|
201
|
+
].map((item) => `${item}\n`),
|
|
202
|
+
);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
71
206
|
describe('Error', () => {
|
|
72
207
|
it('should return AzureBizError with DeploymentNotFound error', async () => {
|
|
73
208
|
// Arrange
|
|
@@ -165,7 +300,6 @@ describe('LobeAzureOpenAI', () => {
|
|
|
165
300
|
});
|
|
166
301
|
|
|
167
302
|
describe('private method', () => {
|
|
168
|
-
|
|
169
303
|
describe('tocamelCase', () => {
|
|
170
304
|
it('should convert string to camel case', () => {
|
|
171
305
|
const key = 'image_url';
|
|
@@ -179,41 +313,41 @@ describe('LobeAzureOpenAI', () => {
|
|
|
179
313
|
describe('camelCaseKeys', () => {
|
|
180
314
|
it('should convert object keys to camel case', () => {
|
|
181
315
|
const obj = {
|
|
182
|
-
|
|
183
|
-
|
|
316
|
+
frequency_penalty: 0,
|
|
317
|
+
messages: [
|
|
184
318
|
{
|
|
185
|
-
|
|
186
|
-
|
|
319
|
+
role: 'user',
|
|
320
|
+
content: [
|
|
187
321
|
{
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
]
|
|
194
|
-
}
|
|
195
|
-
]
|
|
322
|
+
type: 'image_url',
|
|
323
|
+
image_url: {
|
|
324
|
+
url: '<image URL>',
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
],
|
|
196
330
|
};
|
|
197
331
|
|
|
198
332
|
const newObj = instance['camelCaseKeys'](obj);
|
|
199
333
|
|
|
200
334
|
expect(newObj).toEqual({
|
|
201
|
-
|
|
202
|
-
|
|
335
|
+
frequencyPenalty: 0,
|
|
336
|
+
messages: [
|
|
203
337
|
{
|
|
204
|
-
|
|
205
|
-
|
|
338
|
+
role: 'user',
|
|
339
|
+
content: [
|
|
206
340
|
{
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
]
|
|
213
|
-
}
|
|
214
|
-
]
|
|
341
|
+
type: 'image_url',
|
|
342
|
+
imageUrl: {
|
|
343
|
+
url: '<image URL>',
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
],
|
|
215
349
|
});
|
|
216
350
|
});
|
|
217
351
|
});
|
|
218
|
-
})
|
|
352
|
+
});
|
|
219
353
|
});
|
|
@@ -14,7 +14,11 @@ import {
|
|
|
14
14
|
|
|
15
15
|
export const transformOpenAIStream = (chunk: OpenAI.ChatCompletionChunk): StreamProtocolChunk => {
|
|
16
16
|
// maybe need another structure to add support for multiple choices
|
|
17
|
+
|
|
17
18
|
const item = chunk.choices[0];
|
|
19
|
+
if (!item) {
|
|
20
|
+
return { data: chunk, id: chunk.id, type: 'data' };
|
|
21
|
+
}
|
|
18
22
|
|
|
19
23
|
if (typeof item.delta?.content === 'string') {
|
|
20
24
|
return { data: item.delta.content, id: chunk.id, type: 'text' };
|
|
@@ -5,6 +5,7 @@ export default {
|
|
|
5
5
|
agentDefaultMessage:
|
|
6
6
|
'你好,我是 **{{name}}**,你可以立即与我开始对话,也可以前往 [助手设置](/chat/settings#session={{id}}) 完善我的信息。',
|
|
7
7
|
agentDefaultMessageWithSystemRole: '你好,我是 **{{name}}**,{{systemRole}},让我们开始对话吧!',
|
|
8
|
+
agentsAndConversations: '助手与会话',
|
|
8
9
|
backToBottom: '跳转至当前',
|
|
9
10
|
clearCurrentMessages: '清空当前会话消息',
|
|
10
11
|
confirmClearCurrentMessages: '即将清空当前会话消息,清空后将无法找回,请确认你的操作',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t } from 'i18next';
|
|
2
2
|
|
|
3
|
-
import { enableAuth } from '@/const/auth';
|
|
3
|
+
import { enableAuth, enableClerk } from '@/const/auth';
|
|
4
4
|
import { UserStore } from '@/store/user';
|
|
5
5
|
import { LobeUser } from '@/types/user';
|
|
6
6
|
|
|
@@ -43,4 +43,5 @@ const isLogin = (s: UserStore) => {
|
|
|
43
43
|
export const authSelectors = {
|
|
44
44
|
isLogin,
|
|
45
45
|
isLoginWithAuth: (s: UserStore) => s.isSignedIn,
|
|
46
|
+
isLoginWithClerk: (s: UserStore) => s.isSignedIn && enableClerk,
|
|
46
47
|
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { memo } from 'react';
|
|
4
|
-
import { useTranslation } from 'react-i18next';
|
|
5
|
-
|
|
6
|
-
import PageTitle from '@/components/PageTitle';
|
|
7
|
-
|
|
8
|
-
const Title = memo(() => {
|
|
9
|
-
const { t } = useTranslation('auth');
|
|
10
|
-
|
|
11
|
-
return <PageTitle title={t('signup')} />;
|
|
12
|
-
});
|
|
13
|
-
export default Title;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useRouter } from 'next/navigation';
|
|
4
|
-
import { memo } from 'react';
|
|
5
|
-
import { Flexbox } from 'react-layout-kit';
|
|
6
|
-
import urlJoin from 'url-join';
|
|
7
|
-
|
|
8
|
-
import { useCategory } from '@/app/(main)/settings/hooks/useCategory';
|
|
9
|
-
import Cell from '@/components/Cell';
|
|
10
|
-
import Divider from '@/components/Cell/Divider';
|
|
11
|
-
|
|
12
|
-
const SettingCate = memo(() => {
|
|
13
|
-
const settingItems = useCategory({ mobile: true });
|
|
14
|
-
const router = useRouter();
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<Flexbox width={'100%'}>
|
|
18
|
-
{settingItems?.map(({ key, icon, label, type }: any, index) => {
|
|
19
|
-
if (type === 'divider') return <Divider key={index} />;
|
|
20
|
-
return (
|
|
21
|
-
<Cell
|
|
22
|
-
icon={icon}
|
|
23
|
-
key={key}
|
|
24
|
-
label={label}
|
|
25
|
-
onClick={() => router.push(urlJoin('/settings', key))}
|
|
26
|
-
/>
|
|
27
|
-
);
|
|
28
|
-
})}
|
|
29
|
-
</Flexbox>
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
export default SettingCate;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { memo } from 'react';
|
|
4
|
-
import { Flexbox } from 'react-layout-kit';
|
|
5
|
-
|
|
6
|
-
import Cell from '@/components/Cell';
|
|
7
|
-
import Divider from '@/components/Cell/Divider';
|
|
8
|
-
|
|
9
|
-
import { useExtraCate } from './useExtraCate';
|
|
10
|
-
|
|
11
|
-
const ExtraCate = memo(() => {
|
|
12
|
-
const mainItems = useExtraCate();
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<Flexbox width={'100%'}>
|
|
16
|
-
{mainItems?.map(({ key, icon, label, type, onClick }: any, index) => {
|
|
17
|
-
if (type === 'divider') return <Divider key={index} />;
|
|
18
|
-
return <Cell icon={icon} key={key} label={label} onClick={onClick} />;
|
|
19
|
-
})}
|
|
20
|
-
</Flexbox>
|
|
21
|
-
);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
export default ExtraCate;
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { DiscordIcon, Icon } from '@lobehub/ui';
|
|
2
|
-
import { Book, Feather, HardDriveDownload, HardDriveUpload } from 'lucide-react';
|
|
3
|
-
import { useTranslation } from 'react-i18next';
|
|
4
|
-
|
|
5
|
-
import { type MenuProps } from '@/components/Menu';
|
|
6
|
-
import { DISCORD, DOCUMENTS, FEEDBACK } from '@/const/url';
|
|
7
|
-
import DataImporter from '@/features/DataImporter';
|
|
8
|
-
import { configService } from '@/services/config';
|
|
9
|
-
|
|
10
|
-
export const useExtraCate = () => {
|
|
11
|
-
const { t } = useTranslation(['common', 'setting']);
|
|
12
|
-
|
|
13
|
-
const iconSize = { fontSize: 20 };
|
|
14
|
-
|
|
15
|
-
const exports: MenuProps['items'] = [
|
|
16
|
-
{
|
|
17
|
-
icon: <Icon icon={HardDriveUpload} size={iconSize} />,
|
|
18
|
-
key: 'import',
|
|
19
|
-
label: <DataImporter>{t('import')}</DataImporter>,
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
icon: <Icon icon={HardDriveDownload} size={iconSize} />,
|
|
23
|
-
key: 'export',
|
|
24
|
-
label: t('export'),
|
|
25
|
-
onClick: configService.exportAll,
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
type: 'divider',
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
const helps: MenuProps['items'] = [
|
|
33
|
-
{
|
|
34
|
-
icon: <Icon icon={Book} size={iconSize} />,
|
|
35
|
-
key: 'docs',
|
|
36
|
-
label: t('document'),
|
|
37
|
-
onClick: () => window.open(DOCUMENTS, '__blank'),
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
icon: <Icon icon={Feather} size={iconSize} />,
|
|
41
|
-
key: 'feedback',
|
|
42
|
-
label: t('feedback'),
|
|
43
|
-
onClick: () => window.open(FEEDBACK, '__blank'),
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
icon: <Icon icon={DiscordIcon} size={iconSize} />,
|
|
47
|
-
key: 'discord',
|
|
48
|
-
label: 'Discord',
|
|
49
|
-
onClick: () => window.open(DISCORD, '__blank'),
|
|
50
|
-
},
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
const mainItems = [
|
|
54
|
-
{
|
|
55
|
-
type: 'divider',
|
|
56
|
-
},
|
|
57
|
-
...exports,
|
|
58
|
-
...helps,
|
|
59
|
-
].filter(Boolean) as MenuProps['items'];
|
|
60
|
-
|
|
61
|
-
return mainItems;
|
|
62
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|