@lobehub/chat 0.66.0 → 0.68.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/.eslintrc.js +1 -0
- package/CHANGELOG.md +68 -0
- package/README-zh_CN.md +4 -1
- package/README.md +4 -1
- package/locales/en_US/plugin.json +8 -2
- package/package.json +2 -2
- package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +10 -8
- package/src/features/AgentSetting/AgentPlugin/MarketList.tsx +29 -13
- package/src/features/PluginDevModal/ManifestForm.tsx +150 -86
- package/src/features/PluginDevModal/MetaForm.tsx +16 -17
- package/src/features/PluginDevModal/PluginPreview.tsx +9 -8
- package/src/features/PluginDevModal/index.tsx +26 -23
- package/src/locales/default/plugin.ts +9 -5
- package/src/pages/chat/features/Conversation/ChatList/Plugins/IFrameRender/hooks.ts +29 -0
- package/src/pages/chat/features/Conversation/ChatList/Plugins/IFrameRender/index.tsx +62 -0
- package/src/pages/chat/features/Conversation/ChatList/Plugins/IFrameRender/utils.ts +17 -0
- package/src/pages/chat/features/Conversation/ChatList/Plugins/PluginMessage.tsx +20 -4
- package/src/pages/chat/features/Conversation/ChatList/Plugins/{CustomRender.tsx → SystemJsRender/index.tsx} +5 -4
- package/src/store/plugin/initialState.ts +5 -34
- package/src/store/plugin/selectors.ts +12 -7
- package/src/store/plugin/slices/customPlugin/action.ts +64 -0
- package/src/store/plugin/slices/customPlugin/index.ts +3 -0
- package/src/store/plugin/slices/customPlugin/initialState.ts +16 -0
- package/src/store/plugin/{reducers/devPluginList.ts → slices/customPlugin/reducers/customPluginList.ts} +6 -6
- package/src/store/plugin/{action.ts → slices/plugin/action.ts} +13 -25
- package/src/store/plugin/slices/plugin/index.ts +3 -0
- package/src/store/plugin/slices/plugin/initialState.ts +22 -0
- package/src/store/plugin/store.ts +6 -4
- package/src/types/plugin.ts +8 -1
- /package/src/pages/chat/features/Conversation/ChatList/Plugins/{dynamticLoader.ts → SystemJsRender/utils.ts} +0 -0
- /package/src/store/plugin/{reducers → slices/plugin/reducers}/manifest.ts +0 -0
package/.eslintrc.js
CHANGED
|
@@ -11,5 +11,6 @@ config.rules['unicorn/explicit-length-check'] = 0;
|
|
|
11
11
|
config.rules['unicorn/prefer-code-point'] = 0;
|
|
12
12
|
config.rules['no-extra-boolean-cast'] = 0;
|
|
13
13
|
config.rules['unicorn/no-useless-undefined'] = 0;
|
|
14
|
+
config.rules['react/no-unknown-property'] = 0;
|
|
14
15
|
|
|
15
16
|
module.exports = config;
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,74 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.68.0](https://github.com/lobehub/lobe-chat/compare/v0.67.0...v0.68.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-09-03**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Plugin default use iframe render.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Plugin default use iframe render, closes [#141](https://github.com/lobehub/lobe-chat/issues/141) ([35a3a16](https://github.com/lobehub/lobe-chat/commit/35a3a16))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 0.67.0](https://github.com/lobehub/lobe-chat/compare/v0.66.0...v0.67.0)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2023-09-02**</sup>
|
|
33
|
+
|
|
34
|
+
#### ♻ Code Refactoring
|
|
35
|
+
|
|
36
|
+
- **plugin**: 重构 plugin Store 组织结构,便于开发与迭代维护.
|
|
37
|
+
|
|
38
|
+
#### ✨ Features
|
|
39
|
+
|
|
40
|
+
- **plugin-dev**: 优化 manifest 报错原因提示,并支持 id 从 manifest 自动获取.
|
|
41
|
+
|
|
42
|
+
#### 🐛 Bug Fixes
|
|
43
|
+
|
|
44
|
+
- **plugin-dev**: 修正编辑模式下预览展示问题和 id 重复校验问题.
|
|
45
|
+
- **plugin**: 修正开启插件后会话无效的问题.
|
|
46
|
+
|
|
47
|
+
<br/>
|
|
48
|
+
|
|
49
|
+
<details>
|
|
50
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
51
|
+
|
|
52
|
+
#### Code refactoring
|
|
53
|
+
|
|
54
|
+
- **plugin**: 重构 plugin Store 组织结构,便于开发与迭代维护 ([ec527cb](https://github.com/lobehub/lobe-chat/commit/ec527cb))
|
|
55
|
+
|
|
56
|
+
#### What's improved
|
|
57
|
+
|
|
58
|
+
- **plugin-dev**: 优化 manifest 报错原因提示,并支持 id 从 manifest 自动获取 ([7f0787d](https://github.com/lobehub/lobe-chat/commit/7f0787d))
|
|
59
|
+
|
|
60
|
+
#### What's fixed
|
|
61
|
+
|
|
62
|
+
- **plugin-dev**: 修正编辑模式下预览展示问题和 id 重复校验问题 ([17c39ef](https://github.com/lobehub/lobe-chat/commit/17c39ef))
|
|
63
|
+
- **plugin**: 修正开启插件后会话无效的问题 ([82e3beb](https://github.com/lobehub/lobe-chat/commit/82e3beb))
|
|
64
|
+
|
|
65
|
+
</details>
|
|
66
|
+
|
|
67
|
+
<div align="right">
|
|
68
|
+
|
|
69
|
+
[](#readme-top)
|
|
70
|
+
|
|
71
|
+
</div>
|
|
72
|
+
|
|
5
73
|
## [Version 0.66.0](https://github.com/lobehub/lobe-chat/compare/v0.65.1...v0.66.0)
|
|
6
74
|
|
|
7
75
|
<sup>Released on **2023-09-02**</sup>
|
package/README-zh_CN.md
CHANGED
|
@@ -168,7 +168,7 @@ $ pnpm dev
|
|
|
168
168
|
|
|
169
169
|
<!-- CONTRIBUTION GROUP -->
|
|
170
170
|
|
|
171
|
-
> 📊 Total: <kbd>**
|
|
171
|
+
> 📊 Total: <kbd>**5**</kbd>
|
|
172
172
|
|
|
173
173
|
<a href="https://github.com/arvinxx" title="arvinxx">
|
|
174
174
|
<img src="https://avatars.githubusercontent.com/u/28616219?v=4" width="50" />
|
|
@@ -182,6 +182,9 @@ $ pnpm dev
|
|
|
182
182
|
<a href="https://github.com/actions-user" title="actions-user">
|
|
183
183
|
<img src="https://avatars.githubusercontent.com/u/65916846?v=4" width="50" />
|
|
184
184
|
</a>
|
|
185
|
+
<a href="https://github.com/bropines" title="bropines">
|
|
186
|
+
<img src="https://avatars.githubusercontent.com/u/57861007?v=4" width="50" />
|
|
187
|
+
</a>
|
|
185
188
|
|
|
186
189
|
<!-- CONTRIBUTION END -->
|
|
187
190
|
|
package/README.md
CHANGED
|
@@ -168,7 +168,7 @@ Contributions of all types are more than welcome, if you are interested in contr
|
|
|
168
168
|
|
|
169
169
|
<!-- CONTRIBUTION GROUP -->
|
|
170
170
|
|
|
171
|
-
> 📊 Total: <kbd>**
|
|
171
|
+
> 📊 Total: <kbd>**5**</kbd>
|
|
172
172
|
|
|
173
173
|
<a href="https://github.com/arvinxx" title="arvinxx">
|
|
174
174
|
<img src="https://avatars.githubusercontent.com/u/28616219?v=4" width="50" />
|
|
@@ -182,6 +182,9 @@ Contributions of all types are more than welcome, if you are interested in contr
|
|
|
182
182
|
<a href="https://github.com/actions-user" title="actions-user">
|
|
183
183
|
<img src="https://avatars.githubusercontent.com/u/65916846?v=4" width="50" />
|
|
184
184
|
</a>
|
|
185
|
+
<a href="https://github.com/bropines" title="bropines">
|
|
186
|
+
<img src="https://avatars.githubusercontent.com/u/57861007?v=4" width="50" />
|
|
187
|
+
</a>
|
|
185
188
|
|
|
186
189
|
<!-- CONTRIBUTION END -->
|
|
187
190
|
|
|
@@ -52,7 +52,11 @@
|
|
|
52
52
|
"desc": "LobeChat will install the plugin using this link",
|
|
53
53
|
"invalid": "The input manifest link is invalid or does not comply with the specification",
|
|
54
54
|
"label": "Plugin Manifest URL",
|
|
55
|
-
"urlError": "Please enter a valid URL"
|
|
55
|
+
"urlError": "Please enter a valid URL",
|
|
56
|
+
"jsonInvalid": "The manifest is not valid, validation result: \n\n {{error}}",
|
|
57
|
+
"preview": "Preview Manifest",
|
|
58
|
+
"refresh": "Refresh",
|
|
59
|
+
"requestError": "Failed to request the link, please enter a valid link and check if the link allows cross-origin access"
|
|
56
60
|
},
|
|
57
61
|
"title": {
|
|
58
62
|
"desc": "The title of the plugin",
|
|
@@ -73,7 +77,9 @@
|
|
|
73
77
|
"manifest": "Function Description Manifest (Manifest)",
|
|
74
78
|
"meta": "Plugin Metadata"
|
|
75
79
|
},
|
|
76
|
-
"title": "Add Custom Plugin"
|
|
80
|
+
"title": "Add Custom Plugin",
|
|
81
|
+
"update": "Update",
|
|
82
|
+
"updateSuccess": "Plugin settings updated successfully"
|
|
77
83
|
},
|
|
78
84
|
"list": {
|
|
79
85
|
"item": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.68.0",
|
|
4
4
|
"description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@emoji-mart/data": "^1",
|
|
67
67
|
"@emoji-mart/react": "^1",
|
|
68
68
|
"@icons-pack/react-simple-icons": "^9",
|
|
69
|
-
"@lobehub/chat-plugin-sdk": "^1.
|
|
69
|
+
"@lobehub/chat-plugin-sdk": "^1.15.0",
|
|
70
70
|
"@lobehub/chat-plugins-gateway": "^1.5.0",
|
|
71
71
|
"@lobehub/ui": "latest",
|
|
72
72
|
"@vercel/analytics": "^1",
|
|
@@ -17,11 +17,13 @@ const MarketList = memo<{ id: string }>(({ id }) => {
|
|
|
17
17
|
const updateConfig = useStore((s) => s.toggleAgentPlugin);
|
|
18
18
|
const [plugins, hasPlugin] = useStore((s) => [s.config.plugins || [], !!s.config.plugins]);
|
|
19
19
|
|
|
20
|
-
const [useFetchPluginList, fetchPluginManifest,
|
|
21
|
-
s
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
const [useFetchPluginList, fetchPluginManifest, deleteCustomPlugin, updateCustomPlugin] =
|
|
21
|
+
usePluginStore((s) => [
|
|
22
|
+
s.useFetchPluginList,
|
|
23
|
+
s.fetchPluginManifest,
|
|
24
|
+
s.deleteCustomPlugin,
|
|
25
|
+
s.updateCustomPlugin,
|
|
26
|
+
]);
|
|
25
27
|
|
|
26
28
|
const pluginManifestLoading = usePluginStore((s) => s.pluginManifestLoading, isEqual);
|
|
27
29
|
const devPlugin = usePluginStore(pluginSelectors.getDevPluginById(id), isEqual);
|
|
@@ -31,15 +33,15 @@ const MarketList = memo<{ id: string }>(({ id }) => {
|
|
|
31
33
|
return (
|
|
32
34
|
<>
|
|
33
35
|
<DevModal
|
|
36
|
+
mode={'edit'}
|
|
34
37
|
onDelete={() => {
|
|
35
|
-
|
|
38
|
+
deleteCustomPlugin(id);
|
|
36
39
|
}}
|
|
37
40
|
onOpenChange={setModal}
|
|
38
41
|
onSave={(value) => {
|
|
39
|
-
|
|
42
|
+
updateCustomPlugin(id, value);
|
|
40
43
|
}}
|
|
41
44
|
open={showModal}
|
|
42
|
-
showDelete
|
|
43
45
|
value={devPlugin}
|
|
44
46
|
/>
|
|
45
47
|
|
|
@@ -41,22 +41,33 @@ const MarketList = memo(() => {
|
|
|
41
41
|
// setModal(true);
|
|
42
42
|
// }, []);
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
|
|
44
|
+
const [plugins, hasPlugin, toggleAgentPlugin] = useStore((s) => [
|
|
45
|
+
s.config.plugins || [],
|
|
46
|
+
!!s.config.plugins,
|
|
47
|
+
s.toggleAgentPlugin,
|
|
48
|
+
]);
|
|
46
49
|
|
|
47
50
|
const [useFetchPluginList, fetchPluginManifest, saveToDevList, updateNewDevPlugin] =
|
|
48
51
|
usePluginStore((s) => [
|
|
49
52
|
s.useFetchPluginList,
|
|
50
53
|
s.fetchPluginManifest,
|
|
51
|
-
s.
|
|
52
|
-
s.
|
|
54
|
+
s.saveToCustomPluginList,
|
|
55
|
+
s.updateNewCustomPlugin,
|
|
53
56
|
]);
|
|
54
57
|
const pluginManifestLoading = usePluginStore((s) => s.pluginManifestLoading, isEqual);
|
|
55
58
|
const pluginList = usePluginStore((s) => s.pluginList, isEqual);
|
|
56
|
-
const
|
|
59
|
+
const customPluginList = usePluginStore((s) => s.customPluginList, isEqual);
|
|
57
60
|
|
|
58
61
|
useFetchPluginList();
|
|
59
62
|
|
|
63
|
+
const togglePlugin = async (pluginId: string, fetchManifest?: boolean) => {
|
|
64
|
+
toggleAgentPlugin(pluginId);
|
|
65
|
+
if (fetchManifest) {
|
|
66
|
+
await fetchPluginManifest(pluginId);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// =========== Skeleton Loading =========== //
|
|
60
71
|
const loadingItem = {
|
|
61
72
|
avatar: (
|
|
62
73
|
<Skeleton
|
|
@@ -77,11 +88,12 @@ const MarketList = memo(() => {
|
|
|
77
88
|
/>
|
|
78
89
|
),
|
|
79
90
|
};
|
|
80
|
-
|
|
81
91
|
const loadingList = [loadingItem, loadingItem, loadingItem];
|
|
82
92
|
|
|
83
93
|
const isEmpty = pluginList.length === 0;
|
|
84
94
|
|
|
95
|
+
// =========== Plugin List =========== //
|
|
96
|
+
|
|
85
97
|
const list = pluginList.map(({ identifier, meta }) => ({
|
|
86
98
|
avatar: <Avatar avatar={meta.avatar} />,
|
|
87
99
|
children: (
|
|
@@ -92,10 +104,7 @@ const MarketList = memo(() => {
|
|
|
92
104
|
}
|
|
93
105
|
loading={pluginManifestLoading[identifier]}
|
|
94
106
|
onChange={(checked) => {
|
|
95
|
-
|
|
96
|
-
if (checked) {
|
|
97
|
-
fetchPluginManifest(identifier);
|
|
98
|
-
}
|
|
107
|
+
togglePlugin(identifier, checked);
|
|
99
108
|
}}
|
|
100
109
|
/>
|
|
101
110
|
),
|
|
@@ -105,7 +114,9 @@ const MarketList = memo(() => {
|
|
|
105
114
|
tag: identifier,
|
|
106
115
|
}));
|
|
107
116
|
|
|
108
|
-
|
|
117
|
+
// =========== Custom Plugin List =========== //
|
|
118
|
+
|
|
119
|
+
const customList = customPluginList.map(({ identifier, meta }) => ({
|
|
109
120
|
avatar: <Avatar avatar={pluginHelpers.getPluginAvatar(meta) || '🧩'} />,
|
|
110
121
|
children: <LocalPluginItem id={identifier} />,
|
|
111
122
|
desc: pluginHelpers.getPluginDesc(meta),
|
|
@@ -125,14 +136,19 @@ const MarketList = memo(() => {
|
|
|
125
136
|
<>
|
|
126
137
|
<DevModal
|
|
127
138
|
onOpenChange={setModal}
|
|
128
|
-
onSave={
|
|
139
|
+
onSave={async (devPlugin) => {
|
|
140
|
+
// 先保存
|
|
141
|
+
saveToDevList(devPlugin);
|
|
142
|
+
// 再开启插件
|
|
143
|
+
await togglePlugin(devPlugin.identifier, true);
|
|
144
|
+
}}
|
|
129
145
|
onValueChange={updateNewDevPlugin}
|
|
130
146
|
open={showModal}
|
|
131
147
|
/>
|
|
132
148
|
<Form
|
|
133
149
|
items={[
|
|
134
150
|
{
|
|
135
|
-
children: isEmpty ? loadingList : [...
|
|
151
|
+
children: isEmpty ? loadingList : [...customList, ...list],
|
|
136
152
|
extra: (
|
|
137
153
|
<Tooltip title={t('settingPlugin.addTooltip')}>
|
|
138
154
|
<Button
|
|
@@ -1,96 +1,160 @@
|
|
|
1
|
-
import { pluginManifestSchema } from '@lobehub/chat-plugin-sdk';
|
|
2
|
-
import { Form, FormItemProps, Input, Tooltip } from '@lobehub/ui';
|
|
3
|
-
import { FormInstance, Radio } from 'antd';
|
|
4
|
-
import {
|
|
1
|
+
import { LobeChatPluginManifest, pluginManifestSchema } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
import { ActionIcon, Form, FormItemProps, Highlighter, Input, Tooltip } from '@lobehub/ui';
|
|
3
|
+
import { FormInstance, Popover, Radio } from 'antd';
|
|
4
|
+
import { FileCode, RotateCwIcon } from 'lucide-react';
|
|
5
|
+
import { memo, useState } from 'react';
|
|
5
6
|
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
|
6
8
|
|
|
7
|
-
const ManifestForm = memo<{ form: FormInstance; mode
|
|
8
|
-
|
|
9
|
+
const ManifestForm = memo<{ form: FormInstance; mode?: 'url' | 'local' }>(
|
|
10
|
+
({ form, mode = 'url' }) => {
|
|
11
|
+
const { t } = useTranslation('plugin');
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
const [manifest, setManifest] = useState<LobeChatPluginManifest>();
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
? [
|
|
14
|
-
{
|
|
15
|
-
children: <Input placeholder={'http://localhost/manifest.json'} />,
|
|
16
|
-
desc: t('dev.meta.manifest.desc'),
|
|
17
|
-
hasFeedback: true,
|
|
18
|
-
label: t('dev.meta.manifest.label'),
|
|
19
|
-
name: 'manifest',
|
|
20
|
-
required: true,
|
|
21
|
-
rules: [
|
|
22
|
-
{ required: true },
|
|
23
|
-
{
|
|
24
|
-
message: t('dev.meta.manifest.urlError'),
|
|
25
|
-
pattern: /^https?:\/\/.*/,
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
message: t('dev.meta.manifest.invalid'),
|
|
29
|
-
validator: async (_, value) => {
|
|
30
|
-
const res = await fetch(value);
|
|
31
|
-
if (!res.ok) return true;
|
|
15
|
+
const isUrl = mode === 'url';
|
|
32
16
|
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
const configItem: FormItemProps[] = isUrl
|
|
18
|
+
? [
|
|
19
|
+
{
|
|
20
|
+
children: (
|
|
21
|
+
<Input
|
|
22
|
+
placeholder={'http://localhost:3400/manifest-dev.json'}
|
|
23
|
+
suffix={
|
|
24
|
+
manifest && (
|
|
25
|
+
<ActionIcon
|
|
26
|
+
icon={RotateCwIcon}
|
|
27
|
+
onClick={(e) => {
|
|
28
|
+
e.stopPropagation();
|
|
29
|
+
form.validateFields(['manifest']);
|
|
30
|
+
}}
|
|
31
|
+
size={'small'}
|
|
32
|
+
title={t('dev.meta.manifest.refresh')}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
/>
|
|
37
|
+
),
|
|
38
|
+
extra: (
|
|
39
|
+
<Flexbox horizontal justify={'space-between'} style={{ marginTop: 8 }}>
|
|
40
|
+
{t('dev.meta.manifest.desc')}
|
|
41
|
+
{manifest && (
|
|
42
|
+
<Popover
|
|
43
|
+
arrow={false}
|
|
44
|
+
content={
|
|
45
|
+
<Highlighter language={'json'}>
|
|
46
|
+
{JSON.stringify(manifest, null, 2)}
|
|
47
|
+
</Highlighter>
|
|
48
|
+
}
|
|
49
|
+
placement={'right'}
|
|
50
|
+
style={{ width: 400 }}
|
|
51
|
+
title={'Manifest JSON'}
|
|
52
|
+
trigger={'click'}
|
|
53
|
+
>
|
|
54
|
+
<ActionIcon
|
|
55
|
+
icon={FileCode}
|
|
56
|
+
size={'small'}
|
|
57
|
+
title={t('dev.meta.manifest.preview')}
|
|
58
|
+
/>
|
|
59
|
+
</Popover>
|
|
60
|
+
)}
|
|
61
|
+
</Flexbox>
|
|
62
|
+
),
|
|
63
|
+
// extra: <div>123</div>,
|
|
64
|
+
hasFeedback: true,
|
|
65
|
+
label: t('dev.meta.manifest.label'),
|
|
66
|
+
name: 'manifest',
|
|
67
|
+
required: true,
|
|
68
|
+
rules: [
|
|
69
|
+
{ required: true },
|
|
70
|
+
{
|
|
71
|
+
message: t('dev.meta.manifest.urlError'),
|
|
72
|
+
pattern: /^https?:\/\/.*/,
|
|
35
73
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
: // TODO: 后续做成本地配置模式
|
|
41
|
-
[
|
|
42
|
-
{
|
|
43
|
-
children: <Input placeholder={'searchEngine'} />,
|
|
44
|
-
desc: t('dev.meta.identifier.desc'),
|
|
45
|
-
label: t('dev.meta.identifier.label'),
|
|
46
|
-
name: 'name',
|
|
47
|
-
required: true,
|
|
48
|
-
},
|
|
74
|
+
{
|
|
75
|
+
// message: t('dev.meta.manifest.invalid'),
|
|
76
|
+
validator: async (_, value) => {
|
|
77
|
+
if (!value) return true;
|
|
49
78
|
|
|
50
|
-
|
|
51
|
-
children: <Input placeholder={t('dev.meta.description.placeholder')} />,
|
|
52
|
-
desc: t('dev.meta.description.desc'),
|
|
53
|
-
label: t('dev.meta.description.label'),
|
|
54
|
-
name: 'description',
|
|
55
|
-
required: true,
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
children: <Input placeholder={'searchEngine'} />,
|
|
59
|
-
desc: t('dev.meta.identifier.desc'),
|
|
60
|
-
label: t('dev.meta.identifier.label'),
|
|
61
|
-
name: 'identifier',
|
|
62
|
-
required: true,
|
|
63
|
-
},
|
|
64
|
-
];
|
|
79
|
+
let res: Response;
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
81
|
+
try {
|
|
82
|
+
res = await fetch(value);
|
|
83
|
+
} catch {
|
|
84
|
+
throw t('dev.meta.manifest.requestError');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const json = await res.json().catch(() => {
|
|
88
|
+
throw t('dev.meta.manifest.urlError');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const valid = pluginManifestSchema.safeParse(json);
|
|
92
|
+
if (!valid.success) {
|
|
93
|
+
throw t('dev.meta.manifest.jsonInvalid', { error: valid.error });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setManifest(json);
|
|
97
|
+
form.setFieldValue('identifier', valid.data.identifier);
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
]
|
|
103
|
+
: // TODO: 后续做成本地配置模式
|
|
104
|
+
[
|
|
105
|
+
{
|
|
106
|
+
children: <Input placeholder={'searchEngine'} />,
|
|
107
|
+
desc: t('dev.meta.identifier.desc'),
|
|
108
|
+
label: t('dev.meta.identifier.label'),
|
|
109
|
+
name: 'name',
|
|
110
|
+
required: true,
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
children: <Input placeholder={t('dev.meta.description.placeholder')} />,
|
|
115
|
+
desc: t('dev.meta.description.desc'),
|
|
116
|
+
label: t('dev.meta.description.label'),
|
|
117
|
+
name: 'description',
|
|
118
|
+
required: true,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
children: <Input placeholder={'searchEngine'} />,
|
|
122
|
+
desc: t('dev.meta.identifier.desc'),
|
|
123
|
+
label: t('dev.meta.identifier.label'),
|
|
124
|
+
name: 'identifier',
|
|
125
|
+
required: true,
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<Form
|
|
131
|
+
form={form}
|
|
132
|
+
items={[
|
|
133
|
+
{
|
|
134
|
+
children: configItem,
|
|
135
|
+
extra: (
|
|
136
|
+
<Radio.Group
|
|
137
|
+
onChange={(v) => {
|
|
138
|
+
form.setFieldValue('manifestMode', v.target.value);
|
|
139
|
+
}}
|
|
140
|
+
size={'small'}
|
|
141
|
+
value={mode}
|
|
142
|
+
>
|
|
143
|
+
<Radio.Button value={'url'}>{t('dev.manifest.mode.url')}</Radio.Button>
|
|
144
|
+
<Tooltip title={t('dev.manifest.mode.local-tooltip')}>
|
|
145
|
+
<Radio.Button disabled value={'local'}>
|
|
146
|
+
{t('dev.manifest.mode.local')}
|
|
147
|
+
</Radio.Button>
|
|
148
|
+
</Tooltip>
|
|
149
|
+
</Radio.Group>
|
|
150
|
+
),
|
|
151
|
+
title: t('dev.tabs.manifest'),
|
|
152
|
+
},
|
|
153
|
+
]}
|
|
154
|
+
layout={isUrl ? 'vertical' : undefined}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
},
|
|
158
|
+
);
|
|
95
159
|
|
|
96
160
|
export default ManifestForm;
|
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
import { Form, FormItemProps, Input } from '@lobehub/ui';
|
|
2
2
|
import { FormInstance } from 'antd';
|
|
3
|
-
import { memo
|
|
3
|
+
import { memo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import EmojiPicker from '@/components/EmojiPicker';
|
|
7
7
|
import { pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
8
8
|
|
|
9
|
-
const MetaForm = memo<{ form: FormInstance }>(({ form }) => {
|
|
9
|
+
const MetaForm = memo<{ form: FormInstance; mode?: 'edit' | 'create' }>(({ form, mode }) => {
|
|
10
|
+
const isEditMode = mode === 'edit';
|
|
11
|
+
|
|
10
12
|
const { t } = useTranslation('plugin');
|
|
11
13
|
const [plugins] = usePluginStore((s) => [pluginSelectors.pluginList(s).map((i) => i.identifier)]);
|
|
12
14
|
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
if (usePluginStore.getState().newDevPlugin) {
|
|
15
|
-
form.setFieldsValue(usePluginStore.getState().newDevPlugin);
|
|
16
|
-
}
|
|
17
|
-
}, []);
|
|
18
|
-
|
|
19
15
|
const configItem: FormItemProps[] = [
|
|
20
16
|
{
|
|
21
|
-
children: <Input placeholder={'searchEngine'} />,
|
|
17
|
+
children: <Input disabled placeholder={'searchEngine'} />,
|
|
22
18
|
desc: t('dev.meta.identifier.desc'),
|
|
23
19
|
label: t('dev.meta.identifier.label'),
|
|
24
20
|
name: 'identifier',
|
|
@@ -28,14 +24,17 @@ const MetaForm = memo<{ form: FormInstance }>(({ form }) => {
|
|
|
28
24
|
message: t('dev.meta.identifier.pattenErrorMessage'),
|
|
29
25
|
pattern: /^[\w-]+$/,
|
|
30
26
|
},
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
// 编辑模式下,不进行重复校验
|
|
28
|
+
isEditMode
|
|
29
|
+
? {}
|
|
30
|
+
: {
|
|
31
|
+
message: t('dev.meta.identifier.errorDuplicate'),
|
|
32
|
+
validator: async (_, value) => {
|
|
33
|
+
if (plugins.includes(value)) {
|
|
34
|
+
throw new Error('Duplicate');
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
},
|
|
39
38
|
],
|
|
40
39
|
},
|
|
41
40
|
{
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import { Avatar, Form } from '@lobehub/ui';
|
|
2
|
-
import { Card, Switch, Tag } from 'antd';
|
|
2
|
+
import { Form as AForm, Card, FormInstance, Switch, Tag } from 'antd';
|
|
3
3
|
import { memo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import { Flexbox } from 'react-layout-kit';
|
|
6
6
|
|
|
7
|
-
import { pluginHelpers
|
|
7
|
+
import { pluginHelpers } from '@/store/plugin';
|
|
8
|
+
import { CustomPlugin } from '@/types/plugin';
|
|
8
9
|
|
|
9
|
-
const PluginPreview = memo(() => {
|
|
10
|
+
const PluginPreview = memo<{ form: FormInstance }>(({ form }) => {
|
|
10
11
|
const { t } = useTranslation('plugin');
|
|
11
|
-
const
|
|
12
|
+
const plugin: CustomPlugin = AForm.useWatch([], form);
|
|
12
13
|
|
|
13
14
|
const items = {
|
|
14
|
-
avatar: <Avatar avatar={pluginHelpers.getPluginAvatar(
|
|
15
|
+
avatar: <Avatar avatar={pluginHelpers.getPluginAvatar(plugin?.meta) || '🧩'} />,
|
|
15
16
|
children: <Switch disabled />,
|
|
16
|
-
desc: pluginHelpers.getPluginDesc(
|
|
17
|
+
desc: pluginHelpers.getPluginDesc(plugin?.meta),
|
|
17
18
|
label: (
|
|
18
19
|
<Flexbox align={'center'} gap={8} horizontal>
|
|
19
|
-
{pluginHelpers.getPluginTitle(
|
|
20
|
+
{pluginHelpers.getPluginTitle(plugin?.meta) ?? t('dev.preview.title')}
|
|
20
21
|
<Tag bordered={false} color={'gold'}>
|
|
21
22
|
{t('list.item.local.title', { ns: 'plugin' })}
|
|
22
23
|
</Tag>
|
|
23
24
|
</Flexbox>
|
|
24
25
|
),
|
|
25
26
|
minWidth: undefined,
|
|
26
|
-
tag: !!
|
|
27
|
+
tag: !!plugin?.identifier ? plugin?.identifier : 'id',
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
return (
|