@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
@@ -1,13 +1,13 @@
1
1
  import { Icon } from '@lobehub/ui';
2
2
  import { App, Button, ConfigProvider, Form, Modal, Popconfirm } from 'antd';
3
- import { createStyles, useTheme } from 'antd-style';
3
+ import { createStyles } from 'antd-style';
4
4
  import { LucideBlocks } from 'lucide-react';
5
5
  import { lighten } from 'polished';
6
- import { memo, useEffect } from 'react';
6
+ import { memo, useEffect, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { Flexbox } from 'react-layout-kit';
9
9
 
10
- import { DevPlugin } from '@/store/plugin/initialState';
10
+ import { CustomPlugin } from '@/types/plugin';
11
11
 
12
12
  import ManifestForm from './ManifestForm';
13
13
  import MetaForm from './MetaForm';
@@ -26,33 +26,31 @@ const useStyles = createStyles(({ css, token, prefixCls }) => ({
26
26
  }));
27
27
 
28
28
  interface DevModalProps {
29
+ mode?: 'edit' | 'create';
29
30
  onDelete?: () => void;
30
31
  onOpenChange: (open: boolean) => void;
31
- onSave?: (value: DevPlugin) => void;
32
- onValueChange?: (value: Partial<DevPlugin>) => void;
32
+ onSave?: (value: CustomPlugin) => Promise<void> | void;
33
+ onValueChange?: (value: Partial<CustomPlugin>) => void;
33
34
  open?: boolean;
34
- showDelete?: boolean;
35
- value?: DevPlugin;
35
+ value?: CustomPlugin;
36
36
  }
37
37
 
38
38
  const DevModal = memo<DevModalProps>(
39
- ({ open, showDelete, value, onValueChange, onSave, onOpenChange, onDelete }) => {
39
+ ({ open, mode = 'create', value, onValueChange, onSave, onOpenChange, onDelete }) => {
40
+ const isEditMode = mode === 'edit';
40
41
  const { t } = useTranslation('plugin');
41
-
42
- const [form] = Form.useForm();
43
- const theme = useTheme();
44
-
45
- const { styles } = useStyles();
46
-
42
+ const { styles, theme } = useStyles();
47
43
  const { message } = App.useApp();
48
44
 
45
+ const [submitting, setSubmitting] = useState(false);
46
+ const [form] = Form.useForm();
49
47
  useEffect(() => {
50
48
  form.setFieldsValue(value);
51
49
  }, []);
52
50
 
53
51
  const footer = (
54
52
  <Flexbox horizontal justify={'space-between'} style={{ marginTop: 24 }}>
55
- {showDelete ? (
53
+ {isEditMode ? (
56
54
  <Popconfirm
57
55
  okButtonProps={{
58
56
  danger: true,
@@ -80,24 +78,30 @@ const DevModal = memo<DevModalProps>(
80
78
  {t('cancel', { ns: 'common' })}
81
79
  </Button>
82
80
  <Button
81
+ loading={submitting}
83
82
  onClick={() => {
84
83
  form.submit();
85
84
  }}
86
85
  type={'primary'}
87
86
  >
88
- {t('dev.save')}
87
+ {t(isEditMode ? 'dev.update' : 'dev.save')}
89
88
  </Button>
90
89
  </Flexbox>
91
90
  </Flexbox>
92
91
  );
92
+
93
93
  return (
94
94
  <Form.Provider
95
95
  onFormChange={() => {
96
96
  onValueChange?.(form.getFieldsValue());
97
97
  }}
98
- onFormFinish={(_, info) => {
99
- onSave?.(info.values as DevPlugin);
100
- message.success(t('dev.saveSuccess'));
98
+ onFormFinish={async (_, info) => {
99
+ if (onSave) {
100
+ setSubmitting(true);
101
+ await onSave?.(info.values as CustomPlugin);
102
+ setSubmitting(false);
103
+ }
104
+ message.success(t(isEditMode ? 'dev.updateSuccess' : 'dev.saveSuccess'));
101
105
  onOpenChange(false);
102
106
  }}
103
107
  >
@@ -121,7 +125,6 @@ const DevModal = memo<DevModalProps>(
121
125
  form.submit();
122
126
  }}
123
127
  open={open}
124
- style={{ height: 750 }}
125
128
  title={
126
129
  <Flexbox gap={8} horizontal>
127
130
  <Icon icon={LucideBlocks} />
@@ -139,9 +142,9 @@ const DevModal = memo<DevModalProps>(
139
142
  {/* { children: <ManifestForm />, key: 'manifest', label: t('dev.tabs.manifest') },*/}
140
143
  {/* ]}*/}
141
144
  {/*/>*/}
142
- <PluginPreview />
143
- <ManifestForm form={form} mode={'url'} />
144
- <MetaForm form={form} />
145
+ <PluginPreview form={form} />
146
+ <ManifestForm form={form} />
147
+ <MetaForm form={form} mode={mode} />
145
148
  </Flexbox>
146
149
  </Modal>
147
150
  </ConfigProvider>
@@ -43,16 +43,19 @@ export default {
43
43
  label: '首页',
44
44
  },
45
45
  identifier: {
46
- desc: '插件的唯一标识,仅支持英文字符、数字、破折号 - 、下划线 _',
46
+ desc: '插件的唯一标识,将从 manifest 中自动识别',
47
47
  errorDuplicate: '标识符和已有插件重复,请修改标识符',
48
48
  label: '标识符',
49
49
  pattenErrorMessage: '只能输入英文字符、数字 、- 和_ 这两个符号',
50
50
  },
51
51
  manifest: {
52
52
  desc: 'LobeChat 将会通过该链接安装插件',
53
- invalid: '输入的 manifest 链接无效,或 manifest 不符合规范',
53
+ jsonInvalid: ' manifest 不符合规范,校验结果: \n\n {{error}}',
54
54
  label: '插件描述文件 Url 地址',
55
- urlError: '请输入一个有效的网址',
55
+ preview: '预览 Manifest',
56
+ refresh: '刷新',
57
+ requestError: '请求该链接失败,请输入一个有效的链接,并检查链接是否允许跨域访问',
58
+ urlError: '该链接没有返回 JSON 格式的内容, 请输入一个有效的链接',
56
59
  },
57
60
  title: {
58
61
  desc: '插件标题',
@@ -73,13 +76,14 @@ export default {
73
76
  manifest: '功能描述清单 (Manifest)',
74
77
  meta: '插件元信息',
75
78
  },
76
-
77
79
  title: '添加自定义插件',
80
+ update: '更新',
81
+ updateSuccess: '插件设置更新成功',
78
82
  },
79
83
  list: {
80
84
  item: {
81
85
  'local.config': '配置',
82
- 'local.title': '本地',
86
+ 'local.title': '自定义',
83
87
  },
84
88
  },
85
89
 
@@ -0,0 +1,29 @@
1
+ import { useEffect } from 'react';
2
+
3
+ import { onPluginFetchMessage, onPluginReady } from './utils';
4
+
5
+ export const useOnPluginReady = (onReady: () => void) => {
6
+ useEffect(() => {
7
+ const fn = (e: MessageEvent) => {
8
+ onPluginReady(e, onReady);
9
+ };
10
+
11
+ window.addEventListener('message', fn);
12
+ return () => {
13
+ window.removeEventListener('message', fn);
14
+ };
15
+ }, []);
16
+ };
17
+
18
+ export const useOnPluginFetchMessage = (onRequest: (data: any) => void, deps: any[] = []) => {
19
+ useEffect(() => {
20
+ const fn = (e: MessageEvent) => {
21
+ onPluginFetchMessage(e, onRequest);
22
+ };
23
+
24
+ window.addEventListener('message', fn);
25
+ return () => {
26
+ window.removeEventListener('message', fn);
27
+ };
28
+ }, deps);
29
+ };
@@ -0,0 +1,62 @@
1
+ import { PluginRenderProps } from '@lobehub/chat-plugin-sdk';
2
+ import { Skeleton } from 'antd';
3
+ import { memo, useEffect, useRef, useState } from 'react';
4
+
5
+ import { useOnPluginFetchMessage, useOnPluginReady } from './hooks';
6
+ import { sendMessageToPlugin } from './utils';
7
+
8
+ interface IFrameRenderProps extends PluginRenderProps {
9
+ height?: number;
10
+ url: string;
11
+ width?: number;
12
+ }
13
+
14
+ const IFrameRender = memo<IFrameRenderProps>(({ url, width = 800, height = 300, ...props }) => {
15
+ const [loading, setLoading] = useState(true);
16
+ const [readyForRender, setReady] = useState(false);
17
+ const iframeRef = useRef<HTMLIFrameElement>(null);
18
+
19
+ useOnPluginReady(() => setReady(true));
20
+
21
+ // 当 props 发生变化时,主动向 iframe 发送数据
22
+ useEffect(() => {
23
+ const iframeWin = iframeRef.current?.contentWindow;
24
+
25
+ if (iframeWin && readyForRender) {
26
+ sendMessageToPlugin(iframeWin, props);
27
+ }
28
+ }, [readyForRender, props]);
29
+
30
+ // 当接收到来自 iframe 的请求时,触发发送数据
31
+ useOnPluginFetchMessage(() => {
32
+ const iframeWin = iframeRef.current?.contentWindow;
33
+ if (iframeWin) {
34
+ sendMessageToPlugin(iframeWin, props);
35
+ }
36
+ }, [props]);
37
+
38
+ return (
39
+ <>
40
+ {loading && <Skeleton active style={{ width }} />}
41
+ <iframe
42
+ // @ts-ignore
43
+ allowtransparency="true"
44
+ height={height}
45
+ hidden={loading}
46
+ onLoad={() => {
47
+ setLoading(false);
48
+ }}
49
+ ref={iframeRef}
50
+ src={url}
51
+ style={{
52
+ border: 0,
53
+ // iframe 在 color-scheme:dark 模式下无法透明
54
+ // refs: https://www.jianshu.com/p/bc5a37bb6a7b
55
+ colorScheme: 'light',
56
+ }}
57
+ width={width}
58
+ />
59
+ </>
60
+ );
61
+ });
62
+ export default IFrameRender;
@@ -0,0 +1,17 @@
1
+ import { PluginChannel } from '@lobehub/chat-plugin-sdk';
2
+
3
+ export const onPluginReady = (e: MessageEvent, onReady: () => void) => {
4
+ if (e.data.type === PluginChannel.pluginReadyForRender) {
5
+ onReady();
6
+ }
7
+ };
8
+
9
+ export const onPluginFetchMessage = (e: MessageEvent, onRequest: (data: any) => void) => {
10
+ if (e.data.type === PluginChannel.fetchPluginMessage) {
11
+ onRequest(e.data);
12
+ }
13
+ };
14
+
15
+ export const sendMessageToPlugin = (window: Window, props: any) => {
16
+ window.postMessage({ props, type: PluginChannel.renderPlugin }, '*');
17
+ };
@@ -1,12 +1,13 @@
1
1
  import { Loading3QuartersOutlined } from '@ant-design/icons';
2
- import { memo } from 'react';
2
+ import { memo, useMemo } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Flexbox } from 'react-layout-kit';
5
5
 
6
6
  import { usePluginStore } from '@/store/plugin';
7
7
  import { ChatMessage } from '@/types/chatMessage';
8
8
 
9
- import CustomRender from './CustomRender';
9
+ import IFrameRender from './IFrameRender';
10
+ import SystemJsRender from './SystemJsRender';
10
11
 
11
12
  export interface FunctionMessageProps extends ChatMessage {
12
13
  loading?: boolean;
@@ -23,6 +24,8 @@ const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
23
24
  isJSON = false;
24
25
  }
25
26
 
27
+ const contentObj = useMemo(() => (isJSON ? JSON.parse(content) : content), [content]);
28
+
26
29
  // if (!loading)
27
30
 
28
31
  if (!isJSON) {
@@ -36,10 +39,23 @@ const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
36
39
  );
37
40
  }
38
41
 
39
- if (!manifest?.ui?.url) return;
42
+ if (!manifest?.ui) return;
43
+
44
+ const ui = manifest.ui;
45
+
46
+ if (!ui.url) return;
47
+
48
+ if (ui.mode === 'module')
49
+ return <SystemJsRender content={contentObj} name={name || 'unknown'} url={ui.url} />;
40
50
 
41
51
  return (
42
- <CustomRender content={JSON.parse(content)} name={name || 'unknown'} url={manifest.ui?.url} />
52
+ <IFrameRender
53
+ content={contentObj}
54
+ height={ui.height}
55
+ name={name || 'unknown'}
56
+ url={ui.url}
57
+ width={ui.width}
58
+ />
43
59
  );
44
60
  });
45
61
 
@@ -2,12 +2,13 @@ import { PluginRender, PluginRenderProps } from '@lobehub/chat-plugin-sdk';
2
2
  import { Skeleton } from 'antd';
3
3
  import { memo, useEffect, useState } from 'react';
4
4
 
5
- import { system } from './dynamticLoader';
5
+ import { system } from './utils';
6
6
 
7
- interface CustomRenderProps extends PluginRenderProps {
7
+ interface SystemJsRenderProps extends PluginRenderProps {
8
8
  url: string;
9
9
  }
10
- const CustomRender = memo<CustomRenderProps>(({ url, ...props }) => {
10
+
11
+ const SystemJsRender = memo<SystemJsRenderProps>(({ url, ...props }) => {
11
12
  const [component, setComp] = useState<PluginRender | null>(null);
12
13
 
13
14
  useEffect(() => {
@@ -31,4 +32,4 @@ const CustomRender = memo<CustomRenderProps>(({ url, ...props }) => {
31
32
 
32
33
  return <Render {...props} />;
33
34
  });
34
- export default CustomRender;
35
+ export default SystemJsRender;
@@ -1,38 +1,9 @@
1
- import { LobeChatPluginManifest, LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
1
+ import { CustomPluginState, initialCustomPluginState } from './slices/customPlugin';
2
+ import { PluginState, initialPluginState } from './slices/plugin';
2
3
 
3
- import { PluginManifestMap } from '@/types/plugin';
4
-
5
- export type PluginManifestLoadingState = Record<string, boolean>;
6
- export type PluginsSettings = Record<string, any>;
7
-
8
- export interface DevPlugin extends LobeChatPluginMeta {
9
- apiMode: 'openapi' | 'simple';
10
- enableSettings: boolean;
11
- manifestConfig?: LobeChatPluginManifest;
12
- manifestMode: 'local' | 'url';
13
- }
14
-
15
- export interface PluginStoreState {
16
- devPluginList: DevPlugin[];
17
- manifestPrepared: boolean;
18
- newDevPlugin: Partial<DevPlugin>;
19
- pluginList: LobeChatPluginMeta[];
20
- pluginManifestLoading: PluginManifestLoadingState;
21
- pluginManifestMap: PluginManifestMap;
22
- pluginsSettings: PluginsSettings;
23
- }
24
- export const defaultDevPlugin: Partial<DevPlugin> = {
25
- apiMode: 'simple',
26
- enableSettings: false,
27
- manifestMode: 'url',
28
- };
4
+ export type PluginStoreState = PluginState & CustomPluginState;
29
5
 
30
6
  export const initialState: PluginStoreState = {
31
- devPluginList: [],
32
- manifestPrepared: false,
33
- newDevPlugin: defaultDevPlugin,
34
- pluginList: [],
35
- pluginManifestLoading: {},
36
- pluginManifestMap: {},
37
- pluginsSettings: {},
7
+ ...initialPluginState,
8
+ ...initialCustomPluginState,
38
9
  };
@@ -1,3 +1,6 @@
1
+ import { uniqBy } from 'lodash-es';
2
+ import { ChatCompletionFunctions } from 'openai-edge/types/api';
3
+
1
4
  import { PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
2
5
  import { pluginHelpers } from '@/store/plugin/helpers';
3
6
 
@@ -5,12 +8,12 @@ import { PluginStoreState } from './initialState';
5
8
 
6
9
  const enabledSchema =
7
10
  (enabledPlugins: string[] = []) =>
8
- (s: PluginStoreState) => {
9
- return Object.values(s.pluginManifestMap)
10
- .filter((p) => {
11
- // 如果不存在 enabledPlugins,那么全部不启用
12
- if (!enabledPlugins) return false;
11
+ (s: PluginStoreState): ChatCompletionFunctions[] => {
12
+ // 如果不存在 enabledPlugins,那么全部不启用
13
+ if (!enabledPlugins) return [];
13
14
 
15
+ const list = Object.values(s.pluginManifestMap)
16
+ .filter((p) => {
14
17
  // 如果存在 enabledPlugins,那么只启用 enabledPlugins 中的插件
15
18
  return enabledPlugins.includes(p.identifier);
16
19
  })
@@ -21,15 +24,17 @@ const enabledSchema =
21
24
  name: manifest.identifier + PLUGIN_SCHEMA_SEPARATOR + m.name,
22
25
  })),
23
26
  );
27
+
28
+ return uniqBy(list, 'name');
24
29
  };
25
30
 
26
- const pluginList = (s: PluginStoreState) => [...s.pluginList, ...s.devPluginList];
31
+ const pluginList = (s: PluginStoreState) => [...s.pluginList, ...s.customPluginList];
27
32
 
28
33
  const getPluginMetaById = (id: string) => (s: PluginStoreState) =>
29
34
  pluginHelpers.getPluginFormList(pluginList(s), id);
30
35
 
31
36
  const getDevPluginById = (id: string) => (s: PluginStoreState) =>
32
- s.devPluginList.find((i) => i.identifier === id);
37
+ s.customPluginList.find((i) => i.identifier === id);
33
38
 
34
39
  const getPluginManifestById = (id: string) => (s: PluginStoreState) => s.pluginManifestMap[id];
35
40
  const getPluginSettingsById = (id: string) => (s: PluginStoreState) => s.pluginsSettings[id];
@@ -0,0 +1,64 @@
1
+ import { merge } from 'lodash-es';
2
+ import { StateCreator } from 'zustand/vanilla';
3
+
4
+ import { CustomPlugin } from '@/types/plugin';
5
+ import { setNamespace } from '@/utils/storeDebug';
6
+
7
+ import { PluginStore } from '../../store';
8
+ import { defaultCustomPlugin } from './initialState';
9
+ import { CustomPluginListDispatch, devPluginListReducer } from './reducers/customPluginList';
10
+
11
+ const t = setNamespace('customPlugin');
12
+
13
+ /**
14
+ * 代理行为接口
15
+ */
16
+ export interface CustomPluginAction {
17
+ deleteCustomPlugin: (id: string) => void;
18
+ dispatchCustomPluginList: (payload: CustomPluginListDispatch) => void;
19
+ saveToCustomPluginList: (value: CustomPlugin) => void;
20
+ updateCustomPlugin: (id: string, value: CustomPlugin) => void;
21
+ updateNewCustomPlugin: (value: Partial<CustomPlugin>) => void;
22
+ }
23
+
24
+ export const createCustomPluginSlice: StateCreator<
25
+ PluginStore,
26
+ [['zustand/devtools', never]],
27
+ [],
28
+ CustomPluginAction
29
+ > = (set, get) => ({
30
+ deleteCustomPlugin: (id) => {
31
+ const { dispatchCustomPluginList, dispatchPluginManifest, deletePluginSettings } = get();
32
+ // 1.删除插件项
33
+ dispatchCustomPluginList({ id, type: 'deleteItem' });
34
+ // 2.删除本地 manifest 记录
35
+ dispatchPluginManifest({ id, type: 'deleteManifest' });
36
+ // 3.删除插件配置
37
+ deletePluginSettings(id);
38
+ },
39
+ dispatchCustomPluginList: (payload) => {
40
+ const { customPluginList } = get();
41
+
42
+ const nextList = devPluginListReducer(customPluginList, payload);
43
+ set({ customPluginList: nextList }, false, t('dispatchCustomPluginList', payload));
44
+ },
45
+ saveToCustomPluginList: (value) => {
46
+ get().dispatchCustomPluginList({ plugin: value, type: 'addItem' });
47
+ set({ newCustomPlugin: defaultCustomPlugin }, false, t('saveToCustomPluginList'));
48
+ },
49
+ updateCustomPlugin: (id, value) => {
50
+ const { dispatchCustomPluginList, fetchPluginManifest } = get();
51
+ // 1. 更新 list 项信息
52
+ dispatchCustomPluginList({ id, plugin: value, type: 'updateItem' });
53
+ // 2. 更新 重新拉取 manifest
54
+ fetchPluginManifest(id);
55
+ },
56
+
57
+ updateNewCustomPlugin: (newCustomPlugin) => {
58
+ set(
59
+ { newCustomPlugin: merge({}, get().newCustomPlugin, newCustomPlugin) },
60
+ false,
61
+ t('updateNewDevPlugin'),
62
+ );
63
+ },
64
+ });
@@ -0,0 +1,3 @@
1
+ export * from './action';
2
+ export * from './initialState';
3
+ // export * from './selectors';
@@ -0,0 +1,16 @@
1
+ import { CustomPlugin } from '@/types/plugin';
2
+
3
+ export interface CustomPluginState {
4
+ customPluginList: CustomPlugin[];
5
+ newCustomPlugin: Partial<CustomPlugin>;
6
+ }
7
+ export const defaultCustomPlugin: Partial<CustomPlugin> = {
8
+ apiMode: 'simple',
9
+ enableSettings: false,
10
+ manifestMode: 'url',
11
+ };
12
+
13
+ export const initialCustomPluginState: CustomPluginState = {
14
+ customPluginList: [],
15
+ newCustomPlugin: defaultCustomPlugin,
16
+ };
@@ -1,11 +1,11 @@
1
1
  import { produce } from 'immer';
2
2
 
3
- import { DevPlugin } from '../initialState';
3
+ import { CustomPlugin } from '@/types/plugin';
4
4
 
5
- export type DevListState = DevPlugin[];
5
+ export type DevListState = CustomPlugin[];
6
6
 
7
7
  export type AddPluginAction = {
8
- plugin: DevPlugin;
8
+ plugin: CustomPlugin;
9
9
  type: 'addItem';
10
10
  };
11
11
 
@@ -16,15 +16,15 @@ export type DeletePluginAction = {
16
16
 
17
17
  export type UpdatePluginAction = {
18
18
  id: string;
19
- plugin: DevPlugin;
19
+ plugin: CustomPlugin;
20
20
  type: 'updateItem';
21
21
  };
22
22
 
23
- export type DevListDispatch = AddPluginAction | DeletePluginAction | UpdatePluginAction;
23
+ export type CustomPluginListDispatch = AddPluginAction | DeletePluginAction | UpdatePluginAction;
24
24
 
25
25
  export const devPluginListReducer = (
26
26
  state: DevListState,
27
- payload: DevListDispatch,
27
+ payload: CustomPluginListDispatch,
28
28
  ): DevListState => {
29
29
  switch (payload.type) {
30
30
  case 'addItem': {
@@ -14,10 +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
+ import { PluginStore } from '../../store';
19
18
  import { PluginDispatch, pluginManifestReducer } from './reducers/manifest';
20
- import { PluginStore } from './store';
21
19
 
22
20
  const t = setNamespace('plugin');
23
21
 
@@ -26,12 +24,10 @@ const t = setNamespace('plugin');
26
24
  */
27
25
  export interface PluginAction {
28
26
  checkLocalEnabledPlugins: (sessions: LobeSessions) => void;
29
- dispatchDevPluginList: (payload: DevListDispatch) => void;
27
+ deletePluginSettings: (id: string) => void;
30
28
  dispatchPluginManifest: (payload: PluginDispatch) => void;
31
- fetchPluginManifest: (name: string) => Promise<void>;
32
- saveToDevList: (value: DevPlugin) => void;
29
+ fetchPluginManifest: (identifier: string) => Promise<void>;
33
30
  updateManifestLoadingState: (key: string, value: boolean | undefined) => void;
34
- updateNewDevPlugin: (value: Partial<DevPlugin>) => void;
35
31
  updatePluginSettings: <T>(id: string, settings: Partial<T>) => void;
36
32
  useFetchPluginList: () => SWRResponse<LobeChatPluginsMarketIndex>;
37
33
  }
@@ -56,15 +52,18 @@ export const createPluginSlice: StateCreator<
56
52
 
57
53
  const plugins = uniq(enabledPlugins);
58
54
 
59
- await Promise.all(plugins.map((name) => fetchPluginManifest(name)));
55
+ await Promise.all(plugins.map((identifier) => fetchPluginManifest(identifier)));
60
56
 
61
57
  set({ manifestPrepared: true }, false, t('checkLocalEnabledPlugins'));
62
58
  },
63
- dispatchDevPluginList: (payload) => {
64
- const { devPluginList } = get();
65
-
66
- const nextList = devPluginListReducer(devPluginList, payload);
67
- set({ devPluginList: nextList }, false, t('dispatchDevList', payload));
59
+ deletePluginSettings: (id) => {
60
+ set(
61
+ produce((draft) => {
62
+ draft.pluginsSettings[id] = undefined;
63
+ }),
64
+ false,
65
+ t('deletePluginSettings'),
66
+ );
68
67
  },
69
68
  dispatchPluginManifest: (payload) => {
70
69
  const { pluginManifestMap } = get();
@@ -72,6 +71,7 @@ export const createPluginSlice: StateCreator<
72
71
 
73
72
  set({ pluginManifestMap: nextManifest }, false, t('dispatchPluginManifest', payload));
74
73
  },
74
+
75
75
  fetchPluginManifest: async (name) => {
76
76
  const plugin = pluginSelectors.getPluginMetaById(name)(get());
77
77
  // 1. 校验文件
@@ -113,11 +113,6 @@ export const createPluginSlice: StateCreator<
113
113
  // 4. 存储 manifest 信息
114
114
  get().dispatchPluginManifest({ id: plugin.identifier, plugin: data, type: 'addManifest' });
115
115
  },
116
- saveToDevList: (value) => {
117
- get().dispatchDevPluginList({ plugin: value, type: 'addItem' });
118
- set({ newDevPlugin: defaultDevPlugin }, false, t('saveToDevList'));
119
- },
120
-
121
116
  updateManifestLoadingState: (key, value) => {
122
117
  set(
123
118
  produce((draft) => {
@@ -127,13 +122,6 @@ export const createPluginSlice: StateCreator<
127
122
  t('updateManifestLoadingState'),
128
123
  );
129
124
  },
130
- updateNewDevPlugin: (newDevPlugin) => {
131
- set(
132
- { newDevPlugin: merge({}, get().newDevPlugin, newDevPlugin) },
133
- false,
134
- t('updateNewDevPlugin'),
135
- );
136
- },
137
125
  updatePluginSettings: (id, settings) => {
138
126
  set(
139
127
  produce((draft) => {
@@ -0,0 +1,3 @@
1
+ export * from './action';
2
+ export * from './initialState';
3
+ // export * from './selectors';
@@ -0,0 +1,22 @@
1
+ import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
2
+
3
+ import { PluginManifestMap } from '@/types/plugin';
4
+
5
+ export type PluginManifestLoadingState = Record<string, boolean>;
6
+ export type PluginsSettings = Record<string, any>;
7
+
8
+ export interface PluginState {
9
+ manifestPrepared: boolean;
10
+ pluginList: LobeChatPluginMeta[];
11
+ pluginManifestLoading: PluginManifestLoadingState;
12
+ pluginManifestMap: PluginManifestMap;
13
+ pluginsSettings: PluginsSettings;
14
+ }
15
+
16
+ export const initialPluginState: PluginState = {
17
+ manifestPrepared: false,
18
+ pluginList: [],
19
+ pluginManifestLoading: {},
20
+ pluginManifestMap: {},
21
+ pluginsSettings: {},
22
+ };