@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.
Files changed (31) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +68 -0
  3. package/README-zh_CN.md +4 -1
  4. package/README.md +4 -1
  5. package/locales/en_US/plugin.json +8 -2
  6. package/package.json +2 -2
  7. package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +10 -8
  8. package/src/features/AgentSetting/AgentPlugin/MarketList.tsx +29 -13
  9. package/src/features/PluginDevModal/ManifestForm.tsx +150 -86
  10. package/src/features/PluginDevModal/MetaForm.tsx +16 -17
  11. package/src/features/PluginDevModal/PluginPreview.tsx +9 -8
  12. package/src/features/PluginDevModal/index.tsx +26 -23
  13. package/src/locales/default/plugin.ts +9 -5
  14. package/src/pages/chat/features/Conversation/ChatList/Plugins/IFrameRender/hooks.ts +29 -0
  15. package/src/pages/chat/features/Conversation/ChatList/Plugins/IFrameRender/index.tsx +62 -0
  16. package/src/pages/chat/features/Conversation/ChatList/Plugins/IFrameRender/utils.ts +17 -0
  17. package/src/pages/chat/features/Conversation/ChatList/Plugins/PluginMessage.tsx +20 -4
  18. package/src/pages/chat/features/Conversation/ChatList/Plugins/{CustomRender.tsx → SystemJsRender/index.tsx} +5 -4
  19. package/src/store/plugin/initialState.ts +5 -34
  20. package/src/store/plugin/selectors.ts +12 -7
  21. package/src/store/plugin/slices/customPlugin/action.ts +64 -0
  22. package/src/store/plugin/slices/customPlugin/index.ts +3 -0
  23. package/src/store/plugin/slices/customPlugin/initialState.ts +16 -0
  24. package/src/store/plugin/{reducers/devPluginList.ts → slices/customPlugin/reducers/customPluginList.ts} +6 -6
  25. package/src/store/plugin/{action.ts → slices/plugin/action.ts} +13 -25
  26. package/src/store/plugin/slices/plugin/index.ts +3 -0
  27. package/src/store/plugin/slices/plugin/initialState.ts +22 -0
  28. package/src/store/plugin/store.ts +6 -4
  29. package/src/types/plugin.ts +8 -1
  30. /package/src/pages/chat/features/Conversation/ChatList/Plugins/{dynamticLoader.ts → SystemJsRender/utils.ts} +0 -0
  31. /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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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>**4**</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>**4**</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.66.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.13.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, dispatchDevPluginList] = usePluginStore((s) => [
21
- s.useFetchPluginList,
22
- s.fetchPluginManifest,
23
- s.dispatchDevPluginList,
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
- dispatchDevPluginList({ id, type: 'deleteItem' });
38
+ deleteCustomPlugin(id);
36
39
  }}
37
40
  onOpenChange={setModal}
38
41
  onSave={(value) => {
39
- dispatchDevPluginList({ id, plugin: value, type: 'updateItem' });
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 updateConfig = useStore((s) => s.toggleAgentPlugin);
45
- const [plugins, hasPlugin] = useStore((s) => [s.config.plugins || [], !!s.config.plugins]);
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.saveToDevList,
52
- s.updateNewDevPlugin,
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 devPluginList = usePluginStore((s) => s.devPluginList, isEqual);
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
- updateConfig(identifier);
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
- const devList = devPluginList.map(({ identifier, meta }) => ({
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={saveToDevList}
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 : [...devList, ...list],
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 { memo } from 'react';
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: 'url' | 'local' }>(({ form, mode }) => {
8
- const { t } = useTranslation('plugin');
9
+ const ManifestForm = memo<{ form: FormInstance; mode?: 'url' | 'local' }>(
10
+ ({ form, mode = 'url' }) => {
11
+ const { t } = useTranslation('plugin');
9
12
 
10
- const isUrl = mode === 'url';
13
+ const [manifest, setManifest] = useState<LobeChatPluginManifest>();
11
14
 
12
- const configItem: FormItemProps[] = isUrl
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
- const json = await res.json();
34
- pluginManifestSchema.parse(json);
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
- return (
67
- <Form
68
- form={form}
69
- items={[
70
- {
71
- children: configItem,
72
- extra: (
73
- <Radio.Group
74
- onChange={(v) => {
75
- form.setFieldValue('manifestMode', v.target.value);
76
- }}
77
- size={'small'}
78
- value={mode}
79
- >
80
- <Radio.Button value={'url'}>{t('dev.manifest.mode.url')}</Radio.Button>
81
- <Tooltip title={t('dev.manifest.mode.local-tooltip')}>
82
- <Radio.Button disabled value={'local'}>
83
- {t('dev.manifest.mode.local')}
84
- </Radio.Button>
85
- </Tooltip>
86
- </Radio.Group>
87
- ),
88
- title: t('dev.tabs.manifest'),
89
- },
90
- ]}
91
- layout={isUrl ? 'vertical' : undefined}
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, useEffect } from 'react';
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
- message: t('dev.meta.identifier.errorDuplicate'),
33
- validator: async (_, value) => {
34
- if (plugins.includes(value)) {
35
- throw new Error('Duplicate');
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, usePluginStore } from '@/store/plugin';
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 meta = usePluginStore((s) => s.newDevPlugin);
12
+ const plugin: CustomPlugin = AForm.useWatch([], form);
12
13
 
13
14
  const items = {
14
- avatar: <Avatar avatar={pluginHelpers.getPluginAvatar(meta?.meta) || '🧩'} />,
15
+ avatar: <Avatar avatar={pluginHelpers.getPluginAvatar(plugin?.meta) || '🧩'} />,
15
16
  children: <Switch disabled />,
16
- desc: pluginHelpers.getPluginDesc(meta?.meta),
17
+ desc: pluginHelpers.getPluginDesc(plugin?.meta),
17
18
  label: (
18
19
  <Flexbox align={'center'} gap={8} horizontal>
19
- {pluginHelpers.getPluginTitle(meta?.meta) ?? t('dev.preview.title')}
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: !!meta?.identifier ? meta?.identifier : 'id',
27
+ tag: !!plugin?.identifier ? plugin?.identifier : 'id',
27
28
  };
28
29
 
29
30
  return (