@lobehub/chat 0.111.0 → 0.111.2
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/.i18nrc.js +0 -2
- package/CHANGELOG.md +58 -0
- package/locales/en_US/plugin.json +2 -1
- package/locales/fr_FR/plugin.json +2 -1
- package/locales/ja_JP/plugin.json +2 -1
- package/locales/ko_KR/plugin.json +2 -1
- package/locales/ru_RU/plugin.json +77 -76
- package/locales/zh_CN/plugin.json +1 -0
- package/locales/zh_TW/plugin.json +2 -1
- package/package.json +1 -1
- package/src/app/chat/features/ChatInput/ActionBar/Clear.tsx +2 -2
- package/src/app/chat/features/Migration/Modal.tsx +2 -2
- package/src/components/FileList/FileItem.tsx +2 -0
- package/src/components/HotKeys/index.tsx +13 -8
- package/src/const/hotkeys.ts +1 -0
- package/src/features/DataImporter/index.tsx +2 -2
- package/src/features/PluginSettingsModal/index.tsx +2 -0
- package/src/features/PluginStore/PluginItem.tsx +38 -37
- package/src/features/PluginStore/PluginSettings.tsx +2 -2
- package/src/features/PluginStore/index.tsx +56 -23
- package/src/layout/GlobalLayout/Locale.tsx +1 -1
- package/src/locales/default/plugin.ts +1 -0
- package/src/store/middleware/createHyperStorage/keyMapper.ts +0 -2
package/.i18nrc.js
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.111.2](https://github.com/lobehub/lobe-chat/compare/v0.111.1...v0.111.2)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-12-13**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Change topic-deletion hotkey.
|
|
12
|
+
|
|
13
|
+
#### 💄 Styles
|
|
14
|
+
|
|
15
|
+
- **misc**: Fix image display in safari (fix.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### What's fixed
|
|
23
|
+
|
|
24
|
+
- **misc**: Change topic-deletion hotkey, closes [#616](https://github.com/lobehub/lobe-chat/issues/616) ([912ff45](https://github.com/lobehub/lobe-chat/commit/912ff45))
|
|
25
|
+
|
|
26
|
+
#### Styles
|
|
27
|
+
|
|
28
|
+
- **misc**: Fix image display in safari (fix, closes [#571](https://github.com/lobehub/lobe-chat/issues/571) ([4beefa7](https://github.com/lobehub/lobe-chat/commit/4beefa7))
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<div align="right">
|
|
33
|
+
|
|
34
|
+
[](#readme-top)
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
### [Version 0.111.1](https://github.com/lobehub/lobe-chat/compare/v0.111.0...v0.111.1)
|
|
39
|
+
|
|
40
|
+
<sup>Released on **2023-12-13**</sup>
|
|
41
|
+
|
|
42
|
+
#### 🐛 Bug Fixes
|
|
43
|
+
|
|
44
|
+
- **misc**: Fix locale typo.
|
|
45
|
+
|
|
46
|
+
<br/>
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
50
|
+
|
|
51
|
+
#### What's fixed
|
|
52
|
+
|
|
53
|
+
- **misc**: Fix locale typo ([f44b41e](https://github.com/lobehub/lobe-chat/commit/f44b41e))
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<div align="right">
|
|
58
|
+
|
|
59
|
+
[](#readme-top)
|
|
60
|
+
|
|
61
|
+
</div>
|
|
62
|
+
|
|
5
63
|
## [Version 0.111.0](https://github.com/lobehub/lobe-chat/compare/v0.110.10...v0.111.0)
|
|
6
64
|
|
|
7
65
|
<sup>Released on **2023-12-13**</sup>
|
|
@@ -1,128 +1,129 @@
|
|
|
1
1
|
{
|
|
2
2
|
"debug": {
|
|
3
|
-
"arguments": "
|
|
4
|
-
"function_call": "
|
|
5
|
-
"off": "
|
|
6
|
-
"on": "
|
|
7
|
-
"response": "
|
|
3
|
+
"arguments": "Аргументы вызова",
|
|
4
|
+
"function_call": "Вызов функции",
|
|
5
|
+
"off": "Отключить отладку",
|
|
6
|
+
"on": "Показать информацию о вызове плагина",
|
|
7
|
+
"response": "Ответ"
|
|
8
8
|
},
|
|
9
9
|
"dev": {
|
|
10
|
-
"confirmDeleteDevPlugin": "
|
|
11
|
-
"deleteSuccess": "
|
|
10
|
+
"confirmDeleteDevPlugin": "Вы уверены, что хотите удалить этот локальный плагин? После удаления его будет невозможно восстановить.",
|
|
11
|
+
"deleteSuccess": "Плагин успешно удален",
|
|
12
12
|
"manifest": {
|
|
13
13
|
"identifier": {
|
|
14
|
-
"desc": "
|
|
15
|
-
"label": "
|
|
14
|
+
"desc": "Уникальный идентификатор плагина",
|
|
15
|
+
"label": "Идентификатор"
|
|
16
16
|
},
|
|
17
17
|
"mode": {
|
|
18
|
-
"local": "
|
|
19
|
-
"local-tooltip": "
|
|
20
|
-
"url": "
|
|
18
|
+
"local": "Локальная настройка",
|
|
19
|
+
"local-tooltip": "Визуальная настройка временно не поддерживается",
|
|
20
|
+
"url": "Онлайн-ссылка"
|
|
21
21
|
},
|
|
22
22
|
"name": {
|
|
23
|
-
"desc": "
|
|
24
|
-
"label": "
|
|
25
|
-
"placeholder": "
|
|
23
|
+
"desc": "Название плагина",
|
|
24
|
+
"label": "Название",
|
|
25
|
+
"placeholder": "Поиск поисковой системы"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"meta": {
|
|
29
29
|
"author": {
|
|
30
|
-
"desc": "
|
|
31
|
-
"label": "
|
|
30
|
+
"desc": "Автор плагина",
|
|
31
|
+
"label": "Автор"
|
|
32
32
|
},
|
|
33
33
|
"avatar": {
|
|
34
|
-
"desc": "
|
|
35
|
-
"label": "
|
|
34
|
+
"desc": "Иконка плагина, можно использовать Emoji или URL",
|
|
35
|
+
"label": "Иконка"
|
|
36
36
|
},
|
|
37
37
|
"description": {
|
|
38
|
-
"desc": "
|
|
39
|
-
"label": "
|
|
40
|
-
"placeholder": "
|
|
38
|
+
"desc": "Описание плагина",
|
|
39
|
+
"label": "Описание",
|
|
40
|
+
"placeholder": "Получение информации из поисковой системы"
|
|
41
41
|
},
|
|
42
|
-
"formFieldRequired": "
|
|
42
|
+
"formFieldRequired": "Это обязательное поле",
|
|
43
43
|
"homepage": {
|
|
44
|
-
"desc": "
|
|
45
|
-
"label": "
|
|
44
|
+
"desc": "Домашняя страница плагина",
|
|
45
|
+
"label": "Домашняя страница"
|
|
46
46
|
},
|
|
47
47
|
"identifier": {
|
|
48
|
-
"desc": "
|
|
49
|
-
"errorDuplicate": "
|
|
50
|
-
"label": "
|
|
51
|
-
"pattenErrorMessage": "
|
|
48
|
+
"desc": "Уникальный идентификатор плагина, будет автоматически определен из манифеста",
|
|
49
|
+
"errorDuplicate": "Идентификатор совпадает с существующим плагином, измените идентификатор",
|
|
50
|
+
"label": "Идентификатор",
|
|
51
|
+
"pattenErrorMessage": "Можно вводить только буквы, цифры, - и _"
|
|
52
52
|
},
|
|
53
53
|
"manifest": {
|
|
54
|
-
"desc": "LobeChat
|
|
55
|
-
"label": "
|
|
56
|
-
"preview": "
|
|
57
|
-
"refresh": "
|
|
54
|
+
"desc": "LobeChat установит плагин через эту ссылку",
|
|
55
|
+
"label": "URL манифеста плагина",
|
|
56
|
+
"preview": "Предпросмотр манифеста",
|
|
57
|
+
"refresh": "Обновить"
|
|
58
58
|
},
|
|
59
59
|
"title": {
|
|
60
|
-
"desc": "
|
|
61
|
-
"label": "
|
|
62
|
-
"placeholder": "
|
|
60
|
+
"desc": "Название плагина",
|
|
61
|
+
"label": "Название",
|
|
62
|
+
"placeholder": "Поиск поисковой системы"
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
-
"metaConfig": "
|
|
66
|
-
"modalDesc": "
|
|
65
|
+
"metaConfig": "Настройка метаданных плагина",
|
|
66
|
+
"modalDesc": "После добавления пользовательского плагина его можно использовать для проверки разработки плагина или непосредственно в сеансе. См. <1>документацию по разработке↗</> для разработки плагинов.",
|
|
67
67
|
"preview": {
|
|
68
|
-
"card": "
|
|
69
|
-
"desc": "
|
|
70
|
-
"title": "
|
|
68
|
+
"card": "Предпросмотр отображения плагина",
|
|
69
|
+
"desc": "Предпросмотр описания плагина",
|
|
70
|
+
"title": "Предпросмотр названия плагина"
|
|
71
71
|
},
|
|
72
|
-
"save": "
|
|
73
|
-
"saveSuccess": "
|
|
72
|
+
"save": "Установить плагин",
|
|
73
|
+
"saveSuccess": "Настройки плагина успешно сохранены",
|
|
74
74
|
"tabs": {
|
|
75
|
-
"manifest": "
|
|
76
|
-
"meta": "
|
|
75
|
+
"manifest": "Описание функций (манифест)",
|
|
76
|
+
"meta": "Метаданные плагина"
|
|
77
77
|
},
|
|
78
|
-
"title": "
|
|
79
|
-
"update": "
|
|
80
|
-
"updateSuccess": "
|
|
78
|
+
"title": "Добавить пользовательский плагин",
|
|
79
|
+
"update": "Обновить",
|
|
80
|
+
"updateSuccess": "Настройки плагина успешно обновлены"
|
|
81
81
|
},
|
|
82
82
|
"error": {
|
|
83
|
-
"fetchError": "
|
|
84
|
-
"installError": "
|
|
85
|
-
"manifestInvalid": "
|
|
86
|
-
"noManifest": "
|
|
87
|
-
"reinstallError": "
|
|
88
|
-
"urlError": "
|
|
83
|
+
"fetchError": "Не удалось получить доступ к этой ссылке на манифест. Убедитесь, что ссылка действительна, и проверьте, разрешен ли кросс-доменный доступ.",
|
|
84
|
+
"installError": "Ошибка установки плагина {{name}}",
|
|
85
|
+
"manifestInvalid": "Манифест не соответствует стандартам. Результат проверки: \n\n {{error}}",
|
|
86
|
+
"noManifest": "Файл манифеста не существует",
|
|
87
|
+
"reinstallError": "Ошибка обновления плагина {{name}}",
|
|
88
|
+
"urlError": "Эта ссылка не возвращает содержимое в формате JSON. Убедитесь, что ссылка действительна."
|
|
89
89
|
},
|
|
90
90
|
"list": {
|
|
91
91
|
"item": {
|
|
92
|
-
"deprecated.title": "
|
|
93
|
-
"local.config": "
|
|
94
|
-
"local.title": "
|
|
92
|
+
"deprecated.title": "Удалено",
|
|
93
|
+
"local.config": "Настройка",
|
|
94
|
+
"local.title": "Пользовательский"
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
97
|
"loading": {
|
|
98
|
-
"content": "
|
|
99
|
-
"plugin": "
|
|
98
|
+
"content": "Вызов плагина...",
|
|
99
|
+
"plugin": "Выполнение плагина..."
|
|
100
100
|
},
|
|
101
|
-
"pluginList": "
|
|
101
|
+
"pluginList": "Список плагинов",
|
|
102
102
|
"plugins": {
|
|
103
|
-
"loading": "
|
|
104
|
-
"unknown": "
|
|
103
|
+
"loading": "Проверка плагинов...",
|
|
104
|
+
"unknown": "Неизвестный плагин"
|
|
105
105
|
},
|
|
106
|
-
"setting": "
|
|
106
|
+
"setting": "Настройки плагина",
|
|
107
107
|
"settings": {
|
|
108
108
|
"indexUrl": {
|
|
109
|
-
"title": "
|
|
110
|
-
"tooltip": "
|
|
109
|
+
"title": "Индекс рынка",
|
|
110
|
+
"tooltip": "В настоящее время не поддерживается онлайн-редактирование. Установите через переменные среды при развертывании"
|
|
111
111
|
},
|
|
112
|
-
"modalDesc": "
|
|
113
|
-
"title": "
|
|
112
|
+
"modalDesc": "После настройки адреса рынка плагинов можно использовать настраиваемый рынок плагинов",
|
|
113
|
+
"title": "Настройки рынка плагинов"
|
|
114
114
|
},
|
|
115
115
|
"store": {
|
|
116
|
-
"empty": "
|
|
117
|
-
"install": "
|
|
118
|
-
"installAllPlugins": "
|
|
119
|
-
"networkError": "
|
|
120
|
-
"releasedAt": "
|
|
116
|
+
"empty": "Пока нет установленных плагинов",
|
|
117
|
+
"install": "Установить",
|
|
118
|
+
"installAllPlugins": "Установить все",
|
|
119
|
+
"networkError": "Не удалось получить доступ к магазину плагинов. Пожалуйста, проверьте подключение к сети и повторите попытку",
|
|
120
|
+
"releasedAt": "Опубликовано {{createdAt}}",
|
|
121
121
|
"tabs": {
|
|
122
|
-
"all": "
|
|
123
|
-
"installed": "
|
|
122
|
+
"all": "Все",
|
|
123
|
+
"installed": "Установленные"
|
|
124
124
|
},
|
|
125
|
-
"title": "
|
|
126
|
-
"uninstall": "
|
|
125
|
+
"title": "Магазин плагинов",
|
|
126
|
+
"uninstall": "Удалить",
|
|
127
|
+
"placeholder": "Введите название или ключевое слово плагина..."
|
|
127
128
|
}
|
|
128
129
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.111.
|
|
3
|
+
"version": "0.111.2",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -6,7 +6,7 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import HotKeys from '@/components/HotKeys';
|
|
9
|
-
import { CLEAN_MESSAGE_KEY, PREFIX_KEY } from '@/const/hotkeys';
|
|
9
|
+
import { CLEAN_MESSAGE_KEY, META_KEY, PREFIX_KEY } from '@/const/hotkeys';
|
|
10
10
|
import { useChatStore } from '@/store/chat';
|
|
11
11
|
import { useFileStore } from '@/store/file';
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ const Clear = memo(() => {
|
|
|
14
14
|
const { t } = useTranslation('setting');
|
|
15
15
|
const [clearMessage] = useChatStore((s) => [s.clearMessage]);
|
|
16
16
|
const [clearImageList] = useFileStore((s) => [s.clearImageList]);
|
|
17
|
-
const hotkeys = [PREFIX_KEY, CLEAN_MESSAGE_KEY].join('+');
|
|
17
|
+
const hotkeys = [META_KEY, PREFIX_KEY, CLEAN_MESSAGE_KEY].join('+');
|
|
18
18
|
|
|
19
19
|
const resetConversation = useCallback(() => {
|
|
20
20
|
clearMessage();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Icon } from '@lobehub/ui';
|
|
2
2
|
import { Button, Result } from 'antd';
|
|
3
|
-
import {
|
|
3
|
+
import { CheckCircle, CpuIcon } from 'lucide-react';
|
|
4
4
|
import { memo, useState } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Center } from 'react-layout-kit';
|
|
@@ -48,7 +48,7 @@ const MigrationModal = memo<MigrationModalProps>(({ setOpen, open, state: dbStat
|
|
|
48
48
|
{t('dbV1.action.start')}
|
|
49
49
|
</Button>
|
|
50
50
|
}
|
|
51
|
-
icon={<Icon icon={
|
|
51
|
+
icon={<Icon icon={CheckCircle} />}
|
|
52
52
|
status={'success'}
|
|
53
53
|
style={{ paddingBlock: 24 }}
|
|
54
54
|
subTitle={t('dbV1.upgrade.success.subTitle')}
|
|
@@ -62,9 +62,11 @@ const FileItem = memo<FileItemProps>(({ editable, id, alwaysShowClose }) => {
|
|
|
62
62
|
}
|
|
63
63
|
alt={data?.name || id || ''}
|
|
64
64
|
alwaysShowActions={alwaysShowClose}
|
|
65
|
+
height={'auto'}
|
|
65
66
|
isLoading={isLoading}
|
|
66
67
|
size={IMAGE_SIZE as any}
|
|
67
68
|
src={data?.url}
|
|
69
|
+
style={{ height: 'auto' }}
|
|
68
70
|
wrapperClassName={cx(styles.image, editable && styles.editableImage)}
|
|
69
71
|
/>
|
|
70
72
|
);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { Icon } from '@lobehub/ui';
|
|
3
4
|
import { createStyles } from 'antd-style';
|
|
5
|
+
import { isString } from 'lodash-es';
|
|
6
|
+
import { Command, Delete, Option } from 'lucide-react';
|
|
4
7
|
import { memo, useEffect, useState } from 'react';
|
|
5
8
|
import { Flexbox } from 'react-layout-kit';
|
|
6
9
|
|
|
7
|
-
import { CLEAN_MESSAGE_KEY, PREFIX_KEY } from '@/const/hotkeys';
|
|
10
|
+
import { CLEAN_MESSAGE_KEY, META_KEY, PREFIX_KEY } from '@/const/hotkeys';
|
|
8
11
|
import { isApplePlatform } from '@/utils/platform';
|
|
9
12
|
|
|
10
13
|
const useStyles = createStyles(
|
|
@@ -13,14 +16,15 @@ const useStyles = createStyles(
|
|
|
13
16
|
|
|
14
17
|
span {
|
|
15
18
|
font-weight: 600;
|
|
16
|
-
opacity: 0.5;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
kbd {
|
|
20
|
-
|
|
22
|
+
min-width: 16px;
|
|
23
|
+
padding: 3px 6px;
|
|
21
24
|
|
|
22
25
|
line-height: 1;
|
|
23
|
-
color: ${token.
|
|
26
|
+
color: ${token.colorTextDescription};
|
|
27
|
+
text-align: center;
|
|
24
28
|
|
|
25
29
|
background: ${token.colorBgContainer};
|
|
26
30
|
border: 1px solid ${token.colorBorderSecondary};
|
|
@@ -42,9 +46,10 @@ const HotKeys = memo<HotKeysProps>(({ keys, desc }) => {
|
|
|
42
46
|
const visibility = typeof window === 'undefined' ? 'hidden' : 'visible';
|
|
43
47
|
|
|
44
48
|
useEffect(() => {
|
|
45
|
-
const mapping: Record<string,
|
|
46
|
-
[CLEAN_MESSAGE_KEY]: isApplePlatform() ?
|
|
47
|
-
[
|
|
49
|
+
const mapping: Record<string, any> = {
|
|
50
|
+
[CLEAN_MESSAGE_KEY]: isApplePlatform() ? <Icon icon={Delete} /> : 'backspace',
|
|
51
|
+
[META_KEY]: isApplePlatform() ? <Icon icon={Command} /> : 'ctrl',
|
|
52
|
+
[PREFIX_KEY]: isApplePlatform() ? <Icon icon={Option} /> : 'alt',
|
|
48
53
|
};
|
|
49
54
|
const newValue = keys
|
|
50
55
|
.split('+')
|
|
@@ -57,7 +62,7 @@ const HotKeys = memo<HotKeysProps>(({ keys, desc }) => {
|
|
|
57
62
|
<Flexbox align={'center'} className={styles} gap={2} horizontal>
|
|
58
63
|
{keysGroup.map((key, index) => (
|
|
59
64
|
<kbd key={index}>
|
|
60
|
-
<span style={{ visibility }}>{key.toUpperCase()}</span>
|
|
65
|
+
<span style={{ visibility }}>{isString(key) ? key.toUpperCase() : key}</span>
|
|
61
66
|
</kbd>
|
|
62
67
|
))}
|
|
63
68
|
</Flexbox>
|
package/src/const/hotkeys.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Icon } from '@lobehub/ui';
|
|
2
2
|
import { Button, Result, Table, Upload } from 'antd';
|
|
3
3
|
import { createStyles } from 'antd-style';
|
|
4
|
-
import {
|
|
4
|
+
import { CheckCircle, ImportIcon } from 'lucide-react';
|
|
5
5
|
import React, { ReactNode, memo, useState } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { Center, Flexbox } from 'react-layout-kit';
|
|
@@ -169,7 +169,7 @@ const DataImporter = memo<DataImporterProps>(({ children, onFinishImport }) => {
|
|
|
169
169
|
{t('importModal.finish.start')}
|
|
170
170
|
</Button>
|
|
171
171
|
}
|
|
172
|
-
icon={<Icon icon={
|
|
172
|
+
icon={<Icon icon={CheckCircle} />}
|
|
173
173
|
status={'success'}
|
|
174
174
|
style={{ paddingBlock: 24 }}
|
|
175
175
|
subTitle={
|
|
@@ -25,6 +25,8 @@ const PluginSettingsModal = memo<PluginSettingsModalProps>(({ schema, onClose, i
|
|
|
25
25
|
const theme = useTheme();
|
|
26
26
|
return (
|
|
27
27
|
<Modal
|
|
28
|
+
cancelText={t('cancel', { ns: 'common' })}
|
|
29
|
+
okText={t('ok', { ns: 'common' })}
|
|
28
30
|
onCancel={onClose}
|
|
29
31
|
onOk={() => {
|
|
30
32
|
onClose();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
|
|
2
|
-
import { Avatar, Tag } from '@lobehub/ui';
|
|
2
|
+
import { Avatar, Icon, Tag } from '@lobehub/ui';
|
|
3
3
|
import { Button } from 'antd';
|
|
4
|
-
import { createStyles } from 'antd-style';
|
|
4
|
+
import { createStyles, useResponsive } from 'antd-style';
|
|
5
|
+
import { BadgeCheck, CircleUser } from 'lucide-react';
|
|
5
6
|
import Link from 'next/link';
|
|
6
7
|
import { memo } from 'react';
|
|
7
8
|
import { useTranslation } from 'react-i18next';
|
|
@@ -14,13 +15,13 @@ import PluginSettings from './PluginSettings';
|
|
|
14
15
|
|
|
15
16
|
const useStyles = createStyles(({ css, token }) => ({
|
|
16
17
|
desc: css`
|
|
17
|
-
color: ${token.
|
|
18
|
+
color: ${token.colorTextDescription};
|
|
18
19
|
`,
|
|
19
20
|
link: css`
|
|
20
|
-
color: ${token.
|
|
21
|
+
color: ${token.colorText};
|
|
21
22
|
`,
|
|
22
23
|
tag: css`
|
|
23
|
-
|
|
24
|
+
color: ${token.colorTextSecondary};
|
|
24
25
|
`,
|
|
25
26
|
title: css`
|
|
26
27
|
font-size: ${token.fontSizeLG}px;
|
|
@@ -28,52 +29,52 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
28
29
|
line-height: 2;
|
|
29
30
|
`,
|
|
30
31
|
}));
|
|
31
|
-
const PluginItem = memo<LobeChatPluginMeta>(({ identifier,
|
|
32
|
+
const PluginItem = memo<LobeChatPluginMeta>(({ identifier, homepage, author, meta }) => {
|
|
32
33
|
const [installed, installing, installPlugin, unInstallPlugin] = useToolStore((s) => [
|
|
33
34
|
pluginSelectors.isPluginInstalled(identifier)(s),
|
|
34
35
|
pluginStoreSelectors.isPluginInstallLoading(identifier)(s),
|
|
35
36
|
s.installPlugin,
|
|
36
37
|
s.uninstallPlugin,
|
|
37
38
|
]);
|
|
39
|
+
const { mobile } = useResponsive();
|
|
38
40
|
const { styles } = useStyles();
|
|
39
41
|
|
|
40
42
|
const { t } = useTranslation('plugin');
|
|
41
43
|
return (
|
|
42
|
-
<Flexbox gap={8}
|
|
43
|
-
<Flexbox
|
|
44
|
-
<Avatar avatar={meta.avatar} size={56} style={{ flex: 'none' }} />
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<PluginSettings identifier={identifier} />
|
|
48
|
-
<Button
|
|
49
|
-
loading={installing}
|
|
50
|
-
onClick={() => {
|
|
51
|
-
if (installed) {
|
|
52
|
-
unInstallPlugin(identifier);
|
|
53
|
-
} else installPlugin(identifier);
|
|
54
|
-
}}
|
|
55
|
-
type={installed ? 'default' : 'primary'}
|
|
56
|
-
>
|
|
57
|
-
{t(installed ? 'store.uninstall' : 'store.install')}
|
|
58
|
-
</Button>
|
|
59
|
-
</Flexbox>
|
|
60
|
-
</Flexbox>
|
|
61
|
-
|
|
62
|
-
<Flexbox gap={4}>
|
|
63
|
-
<Flexbox align={'center'} gap={8} horizontal>
|
|
64
|
-
<div className={styles.title}>{meta.title}</div>
|
|
65
|
-
<Tag className={styles.tag}> {identifier}</Tag>
|
|
66
|
-
</Flexbox>
|
|
67
|
-
<div className={styles.desc}>{meta.description}</div>
|
|
68
|
-
<Flexbox className={styles.link} gap={8} horizontal>
|
|
69
|
-
{author && (
|
|
44
|
+
<Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
|
|
45
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
46
|
+
<Avatar avatar={meta.avatar} size={mobile ? 40 : 56} style={{ flex: 'none' }} />
|
|
47
|
+
<Flexbox gap={4}>
|
|
48
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
70
49
|
<Link className={styles.link} href={homepage} target={'_blank'}>
|
|
71
|
-
|
|
50
|
+
<div className={styles.title}>{meta.title}</div>
|
|
72
51
|
</Link>
|
|
73
|
-
|
|
74
|
-
|
|
52
|
+
|
|
53
|
+
<Tag
|
|
54
|
+
className={styles.tag}
|
|
55
|
+
icon={<Icon icon={author === 'LobeHub' ? BadgeCheck : CircleUser} />}
|
|
56
|
+
>
|
|
57
|
+
{author}
|
|
58
|
+
</Tag>
|
|
59
|
+
</Flexbox>
|
|
60
|
+
<div className={styles.desc}>{meta.description} </div>
|
|
75
61
|
</Flexbox>
|
|
76
62
|
</Flexbox>
|
|
63
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
64
|
+
<PluginSettings identifier={identifier} />
|
|
65
|
+
<Button
|
|
66
|
+
loading={installing}
|
|
67
|
+
onClick={() => {
|
|
68
|
+
if (installed) {
|
|
69
|
+
unInstallPlugin(identifier);
|
|
70
|
+
} else installPlugin(identifier);
|
|
71
|
+
}}
|
|
72
|
+
size={mobile ? 'small' : undefined}
|
|
73
|
+
type={installed ? 'default' : 'primary'}
|
|
74
|
+
>
|
|
75
|
+
{t(installed ? 'store.uninstall' : 'store.install')}
|
|
76
|
+
</Button>
|
|
77
|
+
</Flexbox>
|
|
77
78
|
</Flexbox>
|
|
78
79
|
);
|
|
79
80
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ActionIcon } from '@lobehub/ui';
|
|
2
|
-
import {
|
|
2
|
+
import { Settings2 } from 'lucide-react';
|
|
3
3
|
import { memo, useState } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
@@ -19,7 +19,7 @@ const PluginSettings = memo<{ identifier: string }>(({ identifier }) => {
|
|
|
19
19
|
return (
|
|
20
20
|
<>
|
|
21
21
|
<ActionIcon
|
|
22
|
-
icon={
|
|
22
|
+
icon={Settings2}
|
|
23
23
|
onClick={() => {
|
|
24
24
|
setOpen(true);
|
|
25
25
|
}}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Icon, Modal,
|
|
2
|
-
import { Button, Empty } from 'antd';
|
|
1
|
+
import { Icon, Modal, SearchBar } from '@lobehub/ui';
|
|
2
|
+
import { Button, Empty, Segmented } from 'antd';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
3
4
|
import isEqual from 'fast-deep-equal';
|
|
4
5
|
import { ServerCrash } from 'lucide-react';
|
|
5
|
-
import { memo } from 'react';
|
|
6
|
+
import { memo, useState } from 'react';
|
|
6
7
|
import { useTranslation } from 'react-i18next';
|
|
7
8
|
import { Center, Flexbox } from 'react-layout-kit';
|
|
8
9
|
|
|
@@ -13,13 +14,24 @@ import { pluginStoreSelectors } from '@/store/tool/selectors';
|
|
|
13
14
|
import Loading from './Loading';
|
|
14
15
|
import PluginItem from './PluginItem';
|
|
15
16
|
|
|
17
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
18
|
+
header: css`
|
|
19
|
+
color: ${token.colorTextDescription};
|
|
20
|
+
|
|
21
|
+
> div {
|
|
22
|
+
flex: 1;
|
|
23
|
+
}
|
|
24
|
+
`,
|
|
25
|
+
}));
|
|
26
|
+
|
|
16
27
|
interface PluginStoreProps {
|
|
17
28
|
open?: boolean;
|
|
18
29
|
setOpen: (open: boolean) => void;
|
|
19
30
|
}
|
|
20
31
|
export const PluginStore = memo<PluginStoreProps>(({ setOpen, open }) => {
|
|
32
|
+
const [keywords, setKeywords] = useState<string>();
|
|
21
33
|
const { t } = useTranslation('plugin');
|
|
22
|
-
|
|
34
|
+
const { styles } = useStyles();
|
|
23
35
|
const [listType, useFetchPluginList, installPlugins] = useToolStore((s) => [
|
|
24
36
|
s.listType,
|
|
25
37
|
s.useFetchPluginStore,
|
|
@@ -43,28 +55,19 @@ export const PluginStore = memo<PluginStoreProps>(({ setOpen, open }) => {
|
|
|
43
55
|
<MobilePadding>
|
|
44
56
|
<Center>
|
|
45
57
|
<Flexbox gap={24} width={'100%'}>
|
|
46
|
-
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
items={[
|
|
50
|
-
{ key: 'all', label: t('store.tabs.all') },
|
|
51
|
-
{ key: 'installed', label: t('store.tabs.installed') },
|
|
52
|
-
]}
|
|
58
|
+
<Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
|
|
59
|
+
<Segmented
|
|
60
|
+
block
|
|
53
61
|
onChange={(v) => {
|
|
54
62
|
useToolStore.setState({ listType: v as any });
|
|
55
63
|
}}
|
|
64
|
+
options={[
|
|
65
|
+
{ label: t('store.tabs.all'), value: 'all' },
|
|
66
|
+
{ label: t('store.tabs.installed'), value: 'installed' },
|
|
67
|
+
]}
|
|
68
|
+
style={{ flex: 1 }}
|
|
69
|
+
value={listType}
|
|
56
70
|
/>
|
|
57
|
-
<Flexbox gap={8} horizontal>
|
|
58
|
-
{listType === 'all' && (
|
|
59
|
-
<Button
|
|
60
|
-
onClick={() => {
|
|
61
|
-
installPlugins(pluginStoreList.map((item) => item.identifier));
|
|
62
|
-
}}
|
|
63
|
-
>
|
|
64
|
-
{t('store.installAllPlugins')}
|
|
65
|
-
</Button>
|
|
66
|
-
)}
|
|
67
|
-
</Flexbox>
|
|
68
71
|
</Flexbox>
|
|
69
72
|
{isLoading ? (
|
|
70
73
|
<Loading />
|
|
@@ -83,7 +86,37 @@ export const PluginStore = memo<PluginStoreProps>(({ setOpen, open }) => {
|
|
|
83
86
|
)}
|
|
84
87
|
</Center>
|
|
85
88
|
) : (
|
|
86
|
-
|
|
89
|
+
<>
|
|
90
|
+
<Flexbox className={styles.header} gap={8} horizontal>
|
|
91
|
+
<SearchBar
|
|
92
|
+
allowClear
|
|
93
|
+
onChange={(e) => setKeywords(e.target.value)}
|
|
94
|
+
placeholder={t('store.placeholder')}
|
|
95
|
+
type={'block'}
|
|
96
|
+
value={keywords}
|
|
97
|
+
/>
|
|
98
|
+
{listType === 'all' && (
|
|
99
|
+
<Button
|
|
100
|
+
onClick={() => {
|
|
101
|
+
installPlugins(pluginStoreList.map((item) => item.identifier));
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{t('store.installAllPlugins')}
|
|
105
|
+
</Button>
|
|
106
|
+
)}
|
|
107
|
+
</Flexbox>
|
|
108
|
+
|
|
109
|
+
{pluginStoreList
|
|
110
|
+
.filter((item) =>
|
|
111
|
+
[item.meta.title, item.meta.description, item.author, ...(item.meta.tags || [])]
|
|
112
|
+
.join('')
|
|
113
|
+
.toLowerCase()
|
|
114
|
+
.includes((keywords || '')?.toLowerCase()),
|
|
115
|
+
)
|
|
116
|
+
.map((item) => (
|
|
117
|
+
<PluginItem key={item.identifier} {...item} />
|
|
118
|
+
))}
|
|
119
|
+
</>
|
|
87
120
|
)}
|
|
88
121
|
</Flexbox>
|
|
89
122
|
</Center>
|
|
@@ -15,7 +15,7 @@ const InnerLocale = memo<LocaleLayoutProps>(({ children, lang }) => {
|
|
|
15
15
|
const { data: locale } = useSWR(
|
|
16
16
|
lang,
|
|
17
17
|
async () =>
|
|
18
|
-
await import(`antd/locale/${lang?.includes('-') ? lang?.replace('-', '_') : '
|
|
18
|
+
await import(`antd/locale/${lang?.includes('-') ? lang?.replace('-', '_') : 'en_US'}.js`),
|
|
19
19
|
{ revalidateOnFocus: false },
|
|
20
20
|
);
|
|
21
21
|
const [i18n] = useState(createI18nNext(lang));
|
|
@@ -13,8 +13,6 @@ export const createKeyMapper = (options: HyperStorageOptions) => {
|
|
|
13
13
|
|
|
14
14
|
let storageKey: string | undefined;
|
|
15
15
|
|
|
16
|
-
if (!selectors) return key;
|
|
17
|
-
|
|
18
16
|
for (const selector of selectors) {
|
|
19
17
|
if (typeof selector === 'string') {
|
|
20
18
|
if (selector === key) storageKey = key;
|