@lobehub/chat 0.63.3 → 0.64.1

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 (33) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/locales/en_US/common.json +7 -3
  3. package/locales/en_US/error.json +15 -1
  4. package/locales/en_US/plugin.json +79 -1
  5. package/locales/en_US/setting.json +4 -1
  6. package/locales/zh_CN/common.json +7 -3
  7. package/locales/zh_CN/error.json +14 -0
  8. package/locales/zh_CN/plugin.json +79 -20
  9. package/locales/zh_CN/setting.json +3 -0
  10. package/package.json +1 -1
  11. package/src/components/EmojiPicker/index.tsx +13 -5
  12. package/src/features/AgentSetting/AgentMeta/index.tsx +1 -1
  13. package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +72 -0
  14. package/src/features/AgentSetting/AgentPlugin/MarketList.tsx +160 -0
  15. package/src/features/AgentSetting/AgentPlugin/PluginSettings.tsx +76 -0
  16. package/src/features/AgentSetting/AgentPlugin/index.tsx +8 -143
  17. package/src/features/PluginDevModal/ManifestForm.tsx +96 -0
  18. package/src/features/PluginDevModal/MetaForm.tsx +90 -0
  19. package/src/features/PluginDevModal/PluginPreview.tsx +36 -0
  20. package/src/features/PluginDevModal/index.tsx +153 -0
  21. package/src/locales/default/error.ts +15 -23
  22. package/src/locales/default/plugin.ts +79 -3
  23. package/src/locales/default/setting.ts +3 -0
  24. package/src/locales/resources/en_US.ts +2 -0
  25. package/src/pages/chat/features/Conversation/ChatList/Error/index.tsx +2 -1
  26. package/src/pages/chat/features/Conversation/ChatList/Plugins/FunctionCall.tsx +7 -4
  27. package/src/pages/chat/features/Conversation/ChatList/Plugins/PluginMessage.tsx +27 -33
  28. package/src/services/plugin.ts +6 -3
  29. package/src/store/plugin/action.ts +23 -2
  30. package/src/store/plugin/initialState.ts +17 -1
  31. package/src/store/plugin/reducers/devPluginList.ts +56 -0
  32. package/src/store/plugin/selectors.ts +9 -2
  33. package/src/store/plugin/store.ts +5 -1
@@ -0,0 +1,153 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { App, Button, ConfigProvider, Form, Modal, Popconfirm } from 'antd';
3
+ import { createStyles, useTheme } from 'antd-style';
4
+ import { LucideBlocks } from 'lucide-react';
5
+ import { lighten } from 'polished';
6
+ import { memo, useEffect } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ import { DevPlugin } from '@/store/plugin/initialState';
11
+
12
+ import ManifestForm from './ManifestForm';
13
+ import MetaForm from './MetaForm';
14
+ import PluginPreview from './PluginPreview';
15
+
16
+ const useStyles = createStyles(({ css, token, prefixCls }) => ({
17
+ content: css`
18
+ .${prefixCls}-modal-content {
19
+ border: 1px solid ${token.colorSplit};
20
+ border-radius: 12px;
21
+ }
22
+ `,
23
+ root: css`
24
+ backdrop-filter: blur(5px);
25
+ `,
26
+ }));
27
+
28
+ interface DevModalProps {
29
+ onDelete?: () => void;
30
+ onOpenChange: (open: boolean) => void;
31
+ onSave?: (value: DevPlugin) => void;
32
+ onValueChange?: (value: Partial<DevPlugin>) => void;
33
+ open?: boolean;
34
+ showDelete?: boolean;
35
+ value?: DevPlugin;
36
+ }
37
+
38
+ const DevModal = memo<DevModalProps>(
39
+ ({ open, showDelete, value, onValueChange, onSave, onOpenChange, onDelete }) => {
40
+ const { t } = useTranslation('plugin');
41
+
42
+ const [form] = Form.useForm();
43
+ const theme = useTheme();
44
+
45
+ const { styles } = useStyles();
46
+
47
+ const { message } = App.useApp();
48
+
49
+ useEffect(() => {
50
+ form.setFieldsValue(value);
51
+ }, []);
52
+
53
+ const footer = (
54
+ <Flexbox horizontal justify={'space-between'} style={{ marginTop: 24 }}>
55
+ {showDelete ? (
56
+ <Popconfirm
57
+ okButtonProps={{
58
+ danger: true,
59
+ type: 'primary',
60
+ }}
61
+ onConfirm={() => {
62
+ onDelete?.();
63
+ message.success(t('dev.deleteSuccess'));
64
+ }}
65
+ placement={'topLeft'}
66
+ title={t('dev.confirmDeleteDevPlugin')}
67
+ >
68
+ <Button danger>{t('delete', { ns: 'common' })}</Button>
69
+ </Popconfirm>
70
+ ) : (
71
+ <div />
72
+ )}
73
+
74
+ <Flexbox horizontal>
75
+ <Button
76
+ onClick={() => {
77
+ onOpenChange(false);
78
+ }}
79
+ >
80
+ {t('cancel', { ns: 'common' })}
81
+ </Button>
82
+ <Button
83
+ onClick={() => {
84
+ form.submit();
85
+ }}
86
+ type={'primary'}
87
+ >
88
+ {t('dev.save')}
89
+ </Button>
90
+ </Flexbox>
91
+ </Flexbox>
92
+ );
93
+ return (
94
+ <Form.Provider
95
+ onFormChange={() => {
96
+ onValueChange?.(form.getFieldsValue());
97
+ }}
98
+ onFormFinish={(_, info) => {
99
+ onSave?.(info.values as DevPlugin);
100
+ message.success(t('dev.saveSuccess'));
101
+ onOpenChange(false);
102
+ }}
103
+ >
104
+ <ConfigProvider
105
+ theme={{
106
+ token: {
107
+ colorBgElevated: lighten(0.005, theme.colorBgContainer),
108
+ },
109
+ }}
110
+ >
111
+ <Modal
112
+ centered
113
+ closable
114
+ footer={footer}
115
+ maskClosable
116
+ okText={t('dev.save')}
117
+ onCancel={() => {
118
+ onOpenChange(false);
119
+ }}
120
+ onOk={() => {
121
+ form.submit();
122
+ }}
123
+ open={open}
124
+ style={{ height: 750 }}
125
+ title={
126
+ <Flexbox gap={8} horizontal>
127
+ <Icon icon={LucideBlocks} />
128
+ {t('dev.title')}
129
+ </Flexbox>
130
+ }
131
+ width={700}
132
+ wrapClassName={styles.root}
133
+ >
134
+ <Flexbox gap={12}>
135
+ {t('dev.modalDesc')}
136
+ {/*<Tabs*/}
137
+ {/* items={[*/}
138
+ {/* { children: <MetaForm />, key: 'meta', label: t('dev.tabs.meta') },*/}
139
+ {/* { children: <ManifestForm />, key: 'manifest', label: t('dev.tabs.manifest') },*/}
140
+ {/* ]}*/}
141
+ {/*/>*/}
142
+ <PluginPreview />
143
+ <ManifestForm form={form} mode={'url'} />
144
+ <MetaForm form={form} />
145
+ </Flexbox>
146
+ </Modal>
147
+ </ConfigProvider>
148
+ </Form.Provider>
149
+ );
150
+ },
151
+ );
152
+
153
+ export default DevModal;
@@ -1,7 +1,3 @@
1
- import { PluginErrorType } from '@lobehub/chat-plugin-sdk';
2
-
3
- import { ChatErrorType } from '@/types/fetch';
4
-
5
1
  export default {
6
2
  pluginSettings: {
7
3
  desc: '完成以下配置,即可开始使用该插件',
@@ -19,28 +15,24 @@ export default {
19
15
  503: '很抱歉,服务器当前无法处理您的请求,可能是由于过载或正在进行维护,请稍后再试',
20
16
  504: '很抱歉,服务器没有等到上游服务器的回应,请稍后再试',
21
17
 
22
- [PluginErrorType.PluginMarketIndexNotFound]:
23
- '很抱歉,服务器没有找到插件索引,请检查索引地址是否正确',
24
- [PluginErrorType.PluginMarketIndexInvalid]:
25
- '很抱歉,插件索引校验未通过,请检查索引文件格式是否规范',
26
- [PluginErrorType.PluginMetaNotFound]:
27
- '很抱歉,没有在索引中发现该插件,请插件在索引中的配置信息',
28
- [PluginErrorType.PluginMetaInvalid]:
29
- '很抱歉,该插件的元信息校验未通过,请检查插件元信息格式是否规范',
30
- [PluginErrorType.PluginManifestNotFound]:
18
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
19
+ PluginMarketIndexNotFound: '很抱歉,服务器没有找到插件索引,请检查索引地址是否正确',
20
+ PluginMarketIndexInvalid: '很抱歉,插件索引校验未通过,请检查索引文件格式是否规范',
21
+ PluginMetaNotFound: '很抱歉,没有在索引中发现该插件,请插件在索引中的配置信息',
22
+ PluginMetaInvalid: '很抱歉,该插件的元信息校验未通过,请检查插件元信息格式是否规范',
23
+ PluginManifestNotFound:
31
24
  '很抱歉,服务器没有找到该插件的描述清单 (manifest.json),请检查插件描述文件地址是否正确',
32
- [PluginErrorType.PluginManifestInvalid]:
33
- '很抱歉,该插件的描述清单校验未通过,请检查描述清单格式是否规范',
34
- [PluginErrorType.PluginApiNotFound]:
25
+ PluginManifestInvalid: '很抱歉,该插件的描述清单校验未通过,请检查描述清单格式是否规范',
26
+ PluginApiNotFound:
35
27
  '很抱歉,插件描述清单中不存在该 API ,请检查你的请求方法与插件清单 API 是否匹配',
36
- [PluginErrorType.PluginApiParamsError]:
37
- '很抱歉,该插件请求的入参校验未通过,请检查入参与 Api 描述信息是否匹配',
38
- [PluginErrorType.PluginSettingsInvalid]:
39
- '该插件需要正确配置后才可以使用,请检查你的配置是否正确',
28
+ PluginApiParamsError: '很抱歉,该插件请求的入参校验未通过,请检查入参与 Api 描述信息是否匹配',
29
+ PluginSettingsInvalid: '该插件需要正确配置后才可以使用,请检查你的配置是否正确',
30
+ PluginServerError:
31
+ '插件服务端请求返回出错,请检查根据下面的报错信息检查你的插件描述文件、插件配置或服务端实现',
40
32
 
41
- [ChatErrorType.InvalidAccessCode]:
42
- '密码不正确或为空,请输入正确的访问密码,或者添加自定义 OpenAI API Key',
43
- [ChatErrorType.OpenAIBizError]: '请求 OpenAI 服务出错,请根据以下信息排查或重试',
33
+ InvalidAccessCode: '密码不正确或为空,请输入正确的访问密码,或者添加自定义 OpenAI API Key',
34
+ OpenAIBizError: '请求 OpenAI 服务出错,请根据以下信息排查或重试',
35
+ /* eslint-enable */
44
36
  },
45
37
  unlock: {
46
38
  apikey: {
@@ -4,15 +4,91 @@ export default {
4
4
  function_call: '函数调用',
5
5
  response: '返回结果',
6
6
  },
7
+ dev: {
8
+ confirmDeleteDevPlugin: '即将删除该本地插件,删除后将无法找回,是否删除该插件?',
9
+ deleteSuccess: '插件删除成功',
10
+ manifest: {
11
+ identifier: {
12
+ desc: '插件的唯一标识',
13
+ label: '标识符',
14
+ },
15
+ mode: {
16
+ 'local': '可视化配置',
17
+ 'local-tooltip': '暂时不支持可视化配置',
18
+ 'url': '在线链接',
19
+ },
20
+ name: {
21
+ desc: '插件标题',
22
+ label: '标题',
23
+ placeholder: '搜索引擎',
24
+ },
25
+ },
26
+ meta: {
27
+ author: {
28
+ desc: '插件的作者',
29
+ label: '作者',
30
+ },
31
+ avatar: {
32
+ desc: '插件的图标,可以使用 Emoji,也可以使用 URL',
33
+ label: '图标',
34
+ },
35
+ description: {
36
+ desc: '插件描述',
37
+ label: '描述',
38
+ placeholder: '查询搜索引擎获取信息',
39
+ },
40
+ formFieldRequired: '该字段为必填项',
41
+ homepage: {
42
+ desc: '插件的首页',
43
+ label: '首页',
44
+ },
45
+ identifier: {
46
+ desc: '插件的唯一标识,仅支持英文字符、数字、破折号 - 、下划线 _',
47
+ errorDuplicate: '标识符和已有插件重复,请修改标识符',
48
+ label: '标识符',
49
+ pattenErrorMessage: '只能输入英文字符、数字 、- 和_ 这两个符号',
50
+ },
51
+ manifest: {
52
+ desc: 'LobeChat 将会通过该链接安装插件',
53
+ invalid: '输入的 manifest 链接无效,或 manifest 不符合规范',
54
+ label: '插件描述文件 Url 地址',
55
+ urlError: '请输入一个有效的网址',
56
+ },
57
+ title: {
58
+ desc: '插件标题',
59
+ label: '标题',
60
+ placeholder: '搜索引擎',
61
+ },
62
+ },
63
+ metaConfig: '插件元信息配置',
64
+ modalDesc: '添加自定义插件后,可用于插件开发验证,也可直接在会话中使用。插件开发文档请参考',
65
+ preview: {
66
+ card: '预览插件展示效果',
67
+ desc: '预览插件描述',
68
+ title: '插件名称预览',
69
+ },
70
+ save: '保存',
71
+ saveSuccess: '插件设置保存成功',
72
+ tabs: {
73
+ manifest: '功能描述清单 (Manifest)',
74
+ meta: '插件元信息',
75
+ },
76
+
77
+ title: '添加自定义插件',
78
+ },
79
+ list: {
80
+ item: {
81
+ 'local.config': '配置',
82
+ 'local.title': '本地',
83
+ },
84
+ },
85
+
7
86
  loading: {
8
87
  content: '数据获取中...',
9
88
  plugin: '插件运行中...',
10
89
  },
11
90
  pluginList: '插件列表',
12
91
  plugins: {
13
- realtimeWeather: '实时天气预报',
14
- searchEngine: '搜索引擎',
15
92
  unknown: '插件检测中...',
16
- websiteCrawler: '网页内容提取',
17
93
  },
18
94
  };
@@ -1,3 +1,4 @@
1
+
1
2
  export default {
2
3
  danger: {
3
4
  clear: {
@@ -128,6 +129,8 @@ export default {
128
129
  },
129
130
  },
130
131
  settingPlugin: {
132
+ add: '添加',
133
+ addTooltip: '添加自定义插件',
131
134
  config: '{{id}} 插件配置',
132
135
  title: '插件列表',
133
136
  },
@@ -3,6 +3,7 @@ import empty from '../../../locales/en_US/empty.json';
3
3
  import error from '../../../locales/en_US/error.json';
4
4
  import plugin from '../../../locales/en_US/plugin.json';
5
5
  import setting from '../../../locales/en_US/setting.json';
6
+ import welcome from '../../../locales/en_US/welcome.json';
6
7
 
7
8
  const resources = {
8
9
  common,
@@ -10,6 +11,7 @@ const resources = {
10
11
  error,
11
12
  plugin,
12
13
  setting,
14
+ welcome,
13
15
  } as const;
14
16
 
15
17
  export default resources;
@@ -18,7 +18,8 @@ export const renderErrorMessage: RenderErrorMessage = (error, message: ChatMessa
18
18
  case PluginErrorType.PluginManifestInvalid:
19
19
  case PluginErrorType.PluginManifestNotFound:
20
20
  case PluginErrorType.PluginApiNotFound:
21
- case PluginErrorType.PluginApiParamsError: {
21
+ case PluginErrorType.PluginApiParamsError:
22
+ case PluginErrorType.PluginServerError: {
22
23
  return <PluginError content={(error as any).body} id={message.id} />;
23
24
  }
24
25
  case PluginErrorType.PluginSettingsInvalid: {
@@ -6,7 +6,7 @@ import { memo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
- import { pluginSelectors, usePluginStore } from '@/store/plugin';
9
+ import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
10
10
 
11
11
  import PluginResult from './PluginResultRender';
12
12
  import { useStyles } from './style';
@@ -27,8 +27,11 @@ const FunctionCall = memo<FunctionCallProps>(
27
27
 
28
28
  const item = usePluginStore(pluginSelectors.getPluginMetaById(id));
29
29
 
30
- const avatar = item?.meta.avatar ? (
31
- <Avatar avatar={item?.meta.avatar} size={32} />
30
+ const pluginAvatar = pluginHelpers.getPluginAvatar(item?.meta);
31
+ const pluginTitle = pluginHelpers.getPluginTitle(item?.meta);
32
+
33
+ const avatar = pluginAvatar ? (
34
+ <Avatar avatar={pluginAvatar} size={32} />
32
35
  ) : (
33
36
  <Icon icon={LucideToyBrick} />
34
37
  );
@@ -54,7 +57,7 @@ const FunctionCall = memo<FunctionCallProps>(
54
57
  ) : (
55
58
  avatar
56
59
  )}
57
- {t(`plugins.${id}` as any, { ns: 'plugin' })}
60
+ {pluginTitle ?? t('plugins.unknown')}
58
61
  <Icon icon={open ? LucideChevronUp : LucideChevronDown} />
59
62
  </Flexbox>
60
63
  {open && (
@@ -12,41 +12,35 @@ export interface FunctionMessageProps extends ChatMessage {
12
12
  loading?: boolean;
13
13
  }
14
14
 
15
- const PluginMessage = memo<FunctionMessageProps>(
16
- ({
17
- content,
18
- // loading,
19
- name,
20
- }) => {
21
- const { t } = useTranslation('plugin');
22
-
23
- const manifest = usePluginStore((s) => s.pluginManifestMap[name || '']);
24
- let isJSON = true;
25
- try {
26
- JSON.parse(content);
27
- } catch {
28
- isJSON = false;
29
- }
30
-
31
- // if (!loading)
32
-
33
- if (!isJSON) {
34
- return (
35
- <Flexbox gap={8} horizontal>
36
- <div>
37
- <Loading3QuartersOutlined spin />
38
- </div>
39
- {t('loading.content')}
40
- </Flexbox>
41
- );
42
- }
43
-
44
- if (!manifest?.ui?.url) return;
15
+ const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
16
+ const { t } = useTranslation('plugin');
45
17
 
18
+ const manifest = usePluginStore((s) => s.pluginManifestMap[name || '']);
19
+ let isJSON = true;
20
+ try {
21
+ JSON.parse(content);
22
+ } catch {
23
+ isJSON = false;
24
+ }
25
+
26
+ // if (!loading)
27
+
28
+ if (!isJSON) {
46
29
  return (
47
- <CustomRender content={JSON.parse(content)} name={name || 'unknown'} url={manifest.ui?.url} />
30
+ <Flexbox gap={8} horizontal>
31
+ <div>
32
+ <Loading3QuartersOutlined spin />
33
+ </div>
34
+ {t('loading.content')}
35
+ </Flexbox>
48
36
  );
49
- },
50
- );
37
+ }
38
+
39
+ if (!manifest?.ui?.url) return;
40
+
41
+ return (
42
+ <CustomRender content={JSON.parse(content)} name={name || 'unknown'} url={manifest.ui?.url} />
43
+ );
44
+ });
51
45
 
52
46
  export default PluginMessage;
@@ -1,6 +1,6 @@
1
1
  import { PluginRequestPayload, createHeadersWithPluginSettings } from '@lobehub/chat-plugin-sdk';
2
2
 
3
- import { usePluginStore } from '@/store/plugin';
3
+ import { pluginSelectors, usePluginStore } from '@/store/plugin';
4
4
  import { getMessageError } from '@/utils/fetch';
5
5
 
6
6
  import { URLS } from './url';
@@ -17,10 +17,13 @@ export const fetchPlugin = async (
17
17
  params: PluginRequestPayload,
18
18
  options?: FetchChatModelOptions,
19
19
  ) => {
20
- const settings = usePluginStore.getState().pluginsSettings?.[params.identifier];
20
+ const s = usePluginStore.getState();
21
+
22
+ const settings = pluginSelectors.getPluginSettingsById(params.identifier)(s);
23
+ const manifest = pluginSelectors.getPluginManifestById(params.identifier)(s);
21
24
 
22
25
  const res = await fetch(URLS.plugins, {
23
- body: JSON.stringify(params),
26
+ body: JSON.stringify({ ...params, manifest }),
24
27
  headers: createHeadersWithPluginSettings(settings),
25
28
  method: 'POST',
26
29
  signal: options?.signal,
@@ -14,6 +14,8 @@ import { pluginSelectors } from '@/store/plugin/selectors';
14
14
  import { LobeSessions } from '@/types/session';
15
15
  import { setNamespace } from '@/utils/storeDebug';
16
16
 
17
+ import { DevPlugin, defaultDevPlugin } from './initialState';
18
+ import { DevListDispatch, devPluginListReducer } from './reducers/devPluginList';
17
19
  import { PluginDispatch, pluginManifestReducer } from './reducers/manifest';
18
20
  import { PluginStore } from './store';
19
21
 
@@ -24,9 +26,12 @@ const t = setNamespace('plugin');
24
26
  */
25
27
  export interface PluginAction {
26
28
  checkLocalEnabledPlugins: (sessions: LobeSessions) => void;
29
+ dispatchDevPluginList: (payload: DevListDispatch) => void;
27
30
  dispatchPluginManifest: (payload: PluginDispatch) => void;
28
31
  fetchPluginManifest: (name: string) => Promise<void>;
32
+ saveToDevList: (value: DevPlugin) => void;
29
33
  updateManifestLoadingState: (key: string, value: boolean | undefined) => void;
34
+ updateNewDevPlugin: (value: Partial<DevPlugin>) => void;
30
35
  updatePluginSettings: <T>(id: string, settings: Partial<T>) => void;
31
36
  useFetchPluginList: () => SWRResponse<LobeChatPluginsMarketIndex>;
32
37
  }
@@ -55,13 +60,18 @@ export const createPluginSlice: StateCreator<
55
60
 
56
61
  set({ manifestPrepared: true }, false, t('checkLocalEnabledPlugins'));
57
62
  },
63
+ dispatchDevPluginList: (payload) => {
64
+ const { devPluginList } = get();
65
+
66
+ const nextList = devPluginListReducer(devPluginList, payload);
67
+ set({ devPluginList: nextList }, false, t('dispatchDevList', payload));
68
+ },
58
69
  dispatchPluginManifest: (payload) => {
59
70
  const { pluginManifestMap } = get();
60
71
  const nextManifest = pluginManifestReducer(pluginManifestMap, payload);
61
72
 
62
73
  set({ pluginManifestMap: nextManifest }, false, t('dispatchPluginManifest', payload));
63
74
  },
64
-
65
75
  fetchPluginManifest: async (name) => {
66
76
  const plugin = pluginSelectors.getPluginMetaById(name)(get());
67
77
  // 1. 校验文件
@@ -103,6 +113,10 @@ export const createPluginSlice: StateCreator<
103
113
  // 4. 存储 manifest 信息
104
114
  get().dispatchPluginManifest({ id: plugin.identifier, plugin: data, type: 'addManifest' });
105
115
  },
116
+ saveToDevList: (value) => {
117
+ get().dispatchDevPluginList({ plugin: value, type: 'addItem' });
118
+ set({ newDevPlugin: defaultDevPlugin }, false, t('saveToDevList'));
119
+ },
106
120
 
107
121
  updateManifestLoadingState: (key, value) => {
108
122
  set(
@@ -113,7 +127,13 @@ export const createPluginSlice: StateCreator<
113
127
  t('updateManifestLoadingState'),
114
128
  );
115
129
  },
116
-
130
+ updateNewDevPlugin: (newDevPlugin) => {
131
+ set(
132
+ { newDevPlugin: merge({}, get().newDevPlugin, newDevPlugin) },
133
+ false,
134
+ t('updateNewDevPlugin'),
135
+ );
136
+ },
117
137
  updatePluginSettings: (id, settings) => {
118
138
  set(
119
139
  produce((draft) => {
@@ -123,6 +143,7 @@ export const createPluginSlice: StateCreator<
123
143
  t('updatePluginSettings'),
124
144
  );
125
145
  },
146
+
126
147
  useFetchPluginList: () =>
127
148
  useSWR<LobeChatPluginsMarketIndex>('fetchPluginList', getPluginList, {
128
149
  onSuccess: (pluginMarketIndex) => {
@@ -1,20 +1,36 @@
1
- import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
1
+ import { LobeChatPluginManifest, LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
2
2
 
3
3
  import { PluginManifestMap } from '@/types/plugin';
4
4
 
5
5
  export type PluginManifestLoadingState = Record<string, boolean>;
6
6
  export type PluginsSettings = Record<string, any>;
7
7
 
8
+ export interface DevPlugin extends LobeChatPluginMeta {
9
+ apiMode: 'openapi' | 'simple';
10
+ enableSettings: boolean;
11
+ manifestConfig?: LobeChatPluginManifest;
12
+ manifestMode: 'local' | 'url';
13
+ }
14
+
8
15
  export interface PluginStoreState {
16
+ devPluginList: DevPlugin[];
9
17
  manifestPrepared: boolean;
18
+ newDevPlugin: Partial<DevPlugin>;
10
19
  pluginList: LobeChatPluginMeta[];
11
20
  pluginManifestLoading: PluginManifestLoadingState;
12
21
  pluginManifestMap: PluginManifestMap;
13
22
  pluginsSettings: PluginsSettings;
14
23
  }
24
+ export const defaultDevPlugin: Partial<DevPlugin> = {
25
+ apiMode: 'simple',
26
+ enableSettings: false,
27
+ manifestMode: 'url',
28
+ };
15
29
 
16
30
  export const initialState: PluginStoreState = {
31
+ devPluginList: [],
17
32
  manifestPrepared: false,
33
+ newDevPlugin: defaultDevPlugin,
18
34
  pluginList: [],
19
35
  pluginManifestLoading: {},
20
36
  pluginManifestMap: {},
@@ -0,0 +1,56 @@
1
+ import { produce } from 'immer';
2
+
3
+ import { DevPlugin } from '../initialState';
4
+
5
+ export type DevListState = DevPlugin[];
6
+
7
+ export type AddPluginAction = {
8
+ plugin: DevPlugin;
9
+ type: 'addItem';
10
+ };
11
+
12
+ export type DeletePluginAction = {
13
+ id: string;
14
+ type: 'deleteItem';
15
+ };
16
+
17
+ export type UpdatePluginAction = {
18
+ id: string;
19
+ plugin: DevPlugin;
20
+ type: 'updateItem';
21
+ };
22
+
23
+ export type DevListDispatch = AddPluginAction | DeletePluginAction | UpdatePluginAction;
24
+
25
+ export const devPluginListReducer = (
26
+ state: DevListState,
27
+ payload: DevListDispatch,
28
+ ): DevListState => {
29
+ switch (payload.type) {
30
+ case 'addItem': {
31
+ return [...state, payload.plugin];
32
+ }
33
+
34
+ case 'deleteItem': {
35
+ return produce(state, (draftState) => {
36
+ const deleteIndex = state.findIndex((plugin) => plugin.identifier === payload.id);
37
+ if (deleteIndex !== -1) {
38
+ draftState.splice(deleteIndex, 1);
39
+ }
40
+ });
41
+ }
42
+
43
+ case 'updateItem': {
44
+ return produce(state, (draftState) => {
45
+ const updateIndex = state.findIndex((plugin) => plugin.identifier === payload.id);
46
+ if (updateIndex !== -1) {
47
+ draftState[updateIndex] = payload.plugin;
48
+ }
49
+ });
50
+ }
51
+
52
+ default: {
53
+ return state;
54
+ }
55
+ }
56
+ };
@@ -23,8 +23,13 @@ const enabledSchema =
23
23
  );
24
24
  };
25
25
 
26
+ const pluginList = (s: PluginStoreState) => [...s.pluginList, ...s.devPluginList];
27
+
26
28
  const getPluginMetaById = (id: string) => (s: PluginStoreState) =>
27
- pluginHelpers.getPluginFormList(s.pluginList, id);
29
+ pluginHelpers.getPluginFormList(pluginList(s), id);
30
+
31
+ const getDevPluginById = (id: string) => (s: PluginStoreState) =>
32
+ s.devPluginList.find((i) => i.identifier === id);
28
33
 
29
34
  const getPluginManifestById = (id: string) => (s: PluginStoreState) => s.pluginManifestMap[id];
30
35
  const getPluginSettingsById = (id: string) => (s: PluginStoreState) => s.pluginsSettings[id];
@@ -41,7 +46,7 @@ const getPluginManifestLoadingStatus = (id: string) => (s: PluginStoreState) =>
41
46
  };
42
47
 
43
48
  const displayPluginList = (s: PluginStoreState) =>
44
- s.pluginList.map((p) => ({
49
+ pluginList(s).map((p) => ({
45
50
  author: p.author,
46
51
  avatar: p.meta?.avatar,
47
52
  createAt: p.createAt,
@@ -54,8 +59,10 @@ const displayPluginList = (s: PluginStoreState) =>
54
59
  export const pluginSelectors = {
55
60
  displayPluginList,
56
61
  enabledSchema,
62
+ getDevPluginById,
57
63
  getPluginManifestById,
58
64
  getPluginManifestLoadingStatus,
59
65
  getPluginMetaById,
60
66
  getPluginSettingsById,
67
+ pluginList,
61
68
  };