@lobehub/chat 0.108.0 → 0.109.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 (93) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +24 -4
  3. package/README.zh-CN.md +24 -4
  4. package/docs/Environment-Variable.md +2 -2
  5. package/docs/Environment-Variable.zh-CN.md +2 -2
  6. package/locales/en_US/plugin.json +19 -0
  7. package/locales/en_US/setting.json +26 -0
  8. package/locales/ja_JP/plugin.json +19 -0
  9. package/locales/ja_JP/setting.json +26 -0
  10. package/locales/ko_KR/plugin.json +19 -0
  11. package/locales/ko_KR/setting.json +26 -0
  12. package/locales/ru_RU/plugin.json +19 -0
  13. package/locales/ru_RU/setting.json +26 -0
  14. package/locales/zh_CN/plugin.json +19 -0
  15. package/locales/zh_CN/setting.json +26 -5
  16. package/locales/zh_TW/plugin.json +19 -0
  17. package/locales/zh_TW/setting.json +26 -0
  18. package/package.json +3 -3
  19. package/src/app/chat/features/ChatHeader/PluginTag/PluginStatus.tsx +6 -6
  20. package/src/app/chat/features/ChatHeader/PluginTag/index.tsx +4 -4
  21. package/src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx +37 -0
  22. package/src/app/chat/features/ChatInput/ActionBar/Tools/index.tsx +116 -0
  23. package/src/app/chat/features/ChatInput/ActionBar/config.ts +3 -1
  24. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +4 -4
  25. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +3 -3
  26. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/index.tsx +4 -4
  27. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/DefaultType/index.tsx +2 -2
  28. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +4 -4
  29. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/index.tsx +2 -2
  30. package/src/app/chat/features/Conversation/useInitConversation.ts +2 -2
  31. package/src/app/settings/common/Common.tsx +2 -2
  32. package/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx +56 -0
  33. package/src/features/AgentSetting/AgentPlugin/LoadingList.tsx +48 -0
  34. package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +5 -5
  35. package/src/features/AgentSetting/AgentPlugin/PluginAction/index.tsx +36 -0
  36. package/src/features/AgentSetting/AgentPlugin/index.tsx +157 -9
  37. package/src/features/AgentSetting/store/action.ts +0 -1
  38. package/src/features/PluginDevModal/MetaForm.tsx +3 -3
  39. package/src/features/PluginDevModal/PluginPreview.tsx +1 -1
  40. package/src/features/PluginDevModal/index.tsx +4 -1
  41. package/src/features/PluginSettings/index.tsx +3 -3
  42. package/src/features/PluginSettingsModal/index.tsx +22 -17
  43. package/src/features/PluginStore/Loading.tsx +13 -0
  44. package/src/features/PluginStore/PluginItem.tsx +81 -0
  45. package/src/features/PluginStore/PluginSettings.tsx +40 -0
  46. package/src/features/{AgentSetting/AgentPlugin/MarketSettingModal.tsx → PluginStore/StoreSetting.tsx} +2 -2
  47. package/src/features/PluginStore/index.tsx +95 -0
  48. package/src/layout/GlobalLayout/StoreHydration.tsx +3 -3
  49. package/src/locales/default/plugin.ts +19 -0
  50. package/src/locales/default/setting.ts +26 -5
  51. package/src/services/__tests__/chat.test.ts +221 -0
  52. package/src/services/__tests__/message.test.ts +333 -0
  53. package/src/services/__tests__/plugin.test.ts +88 -0
  54. package/src/services/__tests__/session.test.ts +227 -0
  55. package/src/services/__tests__/topic.test.ts +220 -0
  56. package/src/services/chat.ts +4 -6
  57. package/src/services/plugin.ts +12 -1
  58. package/src/store/chat/actions/message.test.ts +0 -1
  59. package/src/store/session/slices/agent/action.ts +24 -8
  60. package/src/store/{plugin → tool}/helpers.ts +3 -14
  61. package/src/store/tool/index.ts +2 -0
  62. package/src/store/tool/initialState.ts +11 -0
  63. package/src/store/tool/selectors.ts +3 -0
  64. package/src/store/tool/slices/customPlugin/action.test.ts +164 -0
  65. package/src/store/{plugin → tool}/slices/customPlugin/action.ts +2 -2
  66. package/src/store/tool/slices/customPlugin/selectors.test.ts +59 -0
  67. package/src/store/tool/slices/customPlugin/selectors.ts +9 -0
  68. package/src/store/tool/slices/plugin/action.test.ts +141 -0
  69. package/src/store/tool/slices/plugin/action.ts +95 -0
  70. package/src/store/{plugin → tool}/slices/plugin/initialState.ts +0 -7
  71. package/src/store/{plugin → tool/slices/plugin}/selectors.test.ts +4 -41
  72. package/src/store/{plugin → tool/slices/plugin}/selectors.ts +17 -19
  73. package/src/store/tool/slices/store/action.test.ts +396 -0
  74. package/src/store/tool/slices/store/action.ts +103 -0
  75. package/src/store/tool/slices/store/index.ts +3 -0
  76. package/src/store/tool/slices/store/initialState.ts +17 -0
  77. package/src/store/tool/slices/store/selectors.test.ts +48 -0
  78. package/src/store/tool/slices/store/selectors.ts +21 -0
  79. package/src/store/{plugin → tool}/store.ts +8 -6
  80. package/vitest.config.ts +1 -0
  81. package/src/features/AgentSetting/AgentPlugin/MarketList.tsx +0 -232
  82. package/src/features/AgentSetting/AgentPlugin/PluginSettings.tsx +0 -80
  83. package/src/store/chat/reducers/files.test.ts +0 -38
  84. package/src/store/chat/reducers/files.ts +0 -37
  85. package/src/store/plugin/hooks/useSessionHydrated.ts +0 -21
  86. package/src/store/plugin/index.ts +0 -2
  87. package/src/store/plugin/initialState.ts +0 -9
  88. package/src/store/plugin/slices/plugin/action.ts +0 -173
  89. /package/src/store/{plugin → tool}/slices/customPlugin/index.ts +0 -0
  90. /package/src/store/{plugin → tool}/slices/customPlugin/initialState.ts +0 -0
  91. /package/src/store/{plugin → tool}/slices/customPlugin/reducers/customPluginList.ts +0 -0
  92. /package/src/store/{plugin → tool}/slices/plugin/index.ts +0 -0
  93. /package/src/store/{plugin → tool}/slices/plugin/reducers/manifest.ts +0 -0
@@ -84,6 +84,20 @@
84
84
  },
85
85
  "waitingForMore": "<1>計劃支援其他模型</1>,敬請期待 ✨"
86
86
  },
87
+ "plugin": {
88
+ "addTooltip": "新增自訂外掛程式",
89
+ "clearDeprecated": "清除已棄用的外掛程式",
90
+ "empty": "目前沒有安裝任何外掛程式,歡迎前往 <1>外掛程式商店</1> 進行探索",
91
+ "installStatus": {
92
+ "deprecated": "已移除"
93
+ },
94
+ "settings": {
95
+ "hint": "請根據描述填寫以下設定",
96
+ "title": "{{id}} 外掛程式設定",
97
+ "tooltip": "外掛程式設定"
98
+ },
99
+ "store": "外掛程式商店"
100
+ },
87
101
  "settingAgent": {
88
102
  "avatar": {
89
103
  "title": "頭像"
@@ -293,5 +307,17 @@
293
307
  "common": "通用設定",
294
308
  "llm": "語言模型",
295
309
  "tts": "語音服務"
310
+ },
311
+ "tools": {
312
+ "builtins": {
313
+ "groupName": "內建"
314
+ },
315
+ "plugins": {
316
+ "enabled": "已啟用 {{num}}",
317
+ "groupName": "外掛程式",
318
+ "noEnabled": "目前沒有已啟用的外掛程式",
319
+ "store": "外掛程式商店"
320
+ },
321
+ "title": "擴充工具"
296
322
  }
297
323
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.108.0",
3
+ "version": "0.109.1",
4
4
  "description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -144,7 +144,7 @@
144
144
  "@types/ua-parser-js": "^0.7",
145
145
  "@types/uuid": "^9",
146
146
  "@umijs/lint": "^4",
147
- "@vitest/coverage-v8": "latest",
147
+ "@vitest/coverage-v8": "^1",
148
148
  "commitlint": "^18",
149
149
  "consola": "^3",
150
150
  "dpdm": "^3",
@@ -162,7 +162,7 @@
162
162
  "stylelint": "^15",
163
163
  "ts-node": "^10",
164
164
  "typescript": "^5",
165
- "vitest": "latest"
165
+ "vitest": "^1"
166
166
  },
167
167
  "publishConfig": {
168
168
  "access": "public",
@@ -6,9 +6,9 @@ import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
8
  import ManifestPreviewer from '@/components/ManifestPreviewer';
9
- import { usePluginStore } from '@/store/plugin';
10
- import { pluginSelectors } from '@/store/plugin/selectors';
11
9
  import { useSessionStore } from '@/store/session';
10
+ import { useToolStore } from '@/store/tool';
11
+ import { customPluginSelectors, pluginSelectors } from '@/store/tool/selectors';
12
12
 
13
13
  interface PluginStatusProps {
14
14
  deprecated?: boolean;
@@ -17,13 +17,13 @@ interface PluginStatusProps {
17
17
  }
18
18
  const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
19
19
  const { t } = useTranslation('common');
20
- const [status, isCustom, fetchPluginManifest] = usePluginStore((s) => [
20
+ const [status, isCustom, fetchPluginManifest] = useToolStore((s) => [
21
21
  pluginSelectors.getPluginManifestLoadingStatus(id)(s),
22
- pluginSelectors.isCustomPlugin(id)(s),
22
+ customPluginSelectors.isCustomPlugin(id)(s),
23
23
  s.installPlugin,
24
24
  ]);
25
25
 
26
- const manifest = usePluginStore(pluginSelectors.getPluginManifestById(id));
26
+ const manifest = useToolStore(pluginSelectors.getPluginManifestById(id));
27
27
 
28
28
  const removePlugin = useSessionStore((s) => s.removePlugin);
29
29
 
@@ -78,7 +78,7 @@ const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
78
78
  removePlugin(id);
79
79
  }}
80
80
  size={'small'}
81
- title={t('settingPlugin.clearDeprecated', { ns: 'setting' })}
81
+ title={t('plugin.clearDeprecated', { ns: 'setting' })}
82
82
  />
83
83
  ) : (
84
84
  <Flexbox align={'center'} horizontal>
@@ -5,8 +5,8 @@ import isEqual from 'fast-deep-equal';
5
5
  import { LucideToyBrick } from 'lucide-react';
6
6
  import { memo } from 'react';
7
7
 
8
- import { pluginHelpers, usePluginStore } from '@/store/plugin';
9
- import { pluginSelectors } from '@/store/plugin/selectors';
8
+ import { pluginHelpers, useToolStore } from '@/store/tool';
9
+ import { pluginSelectors } from '@/store/tool/selectors';
10
10
 
11
11
  import PluginStatus from './PluginStatus';
12
12
 
@@ -15,8 +15,8 @@ export interface PluginTagProps {
15
15
  }
16
16
 
17
17
  const PluginTag = memo<PluginTagProps>(({ plugins }) => {
18
- const list = usePluginStore(pluginSelectors.displayPluginList);
19
- const displayPlugin = usePluginStore(pluginSelectors.getPluginMetaById(plugins[0]), isEqual);
18
+ const list = useToolStore(pluginSelectors.displayPluginList);
19
+ const displayPlugin = useToolStore(pluginSelectors.getPluginMetaById(plugins[0]), isEqual);
20
20
 
21
21
  if (plugins.length === 0) return null;
22
22
 
@@ -0,0 +1,37 @@
1
+ import { Checkbox } from 'antd';
2
+ import { memo } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import { useSessionStore } from '@/store/session';
6
+ import { agentSelectors } from '@/store/session/selectors';
7
+
8
+ const ToolItem = memo<{ identifier: string; label: string }>(({ identifier, label }) => {
9
+ const [checked, togglePlugin] = useSessionStore((s) => [
10
+ agentSelectors.currentAgentPlugins(s).includes(identifier),
11
+ s.togglePlugin,
12
+ ]);
13
+
14
+ return (
15
+ <Flexbox
16
+ gap={40}
17
+ horizontal
18
+ justify={'space-between'}
19
+ onClick={(e) => {
20
+ e.stopPropagation();
21
+ togglePlugin(identifier);
22
+ }}
23
+ padding={'8px 12px'}
24
+ >
25
+ {label}
26
+ <Checkbox
27
+ checked={checked}
28
+ onClick={(e) => {
29
+ e.stopPropagation();
30
+ togglePlugin(identifier);
31
+ }}
32
+ />
33
+ </Flexbox>
34
+ );
35
+ });
36
+
37
+ export default ToolItem;
@@ -0,0 +1,116 @@
1
+ import { ActionIcon, Avatar, Icon } from '@lobehub/ui';
2
+ import { Dropdown } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import isEqual from 'fast-deep-equal';
5
+ import { ArrowRight, PocketKnife, Store, ToyBrick } from 'lucide-react';
6
+ import { memo, useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ import PluginStore from '@/features/PluginStore';
11
+ import { useSessionStore } from '@/store/session';
12
+ import { agentSelectors } from '@/store/session/selectors';
13
+ import { useToolStore } from '@/store/tool';
14
+ import { pluginSelectors } from '@/store/tool/selectors';
15
+
16
+ import ToolItem from './ToolItem';
17
+
18
+ const useStyles = createStyles(({ css, prefixCls }) => ({
19
+ menu: css`
20
+ &.${prefixCls}-dropdown-menu {
21
+ padding-block: 8px;
22
+ }
23
+
24
+ .${prefixCls}-dropdown-menu-item-group-list .${prefixCls}-dropdown-menu-item {
25
+ padding: 0;
26
+ border-radius: 4px;
27
+ }
28
+ `,
29
+ }));
30
+
31
+ const Tools = memo(() => {
32
+ const { t } = useTranslation('setting');
33
+ const list = useToolStore(pluginSelectors.installedPlugins, isEqual);
34
+ const enablePluginCount = useSessionStore((s) => agentSelectors.currentAgentPlugins(s).length);
35
+ const [open, setOpen] = useState(false);
36
+ const { styles } = useStyles();
37
+
38
+ return (
39
+ <>
40
+ <Dropdown
41
+ arrow={false}
42
+ menu={{
43
+ className: styles.menu,
44
+ items: [
45
+ // {
46
+ // children: [
47
+ // {
48
+ // icon: <Icon icon={PaletteIcon} size={{ fontSize: 16 }} style={{ padding: 4 }} />,
49
+ // key: 'dalle3',
50
+ // label: <ToolItem identifier={'dalle3'} label={'DALL·E 3'} />,
51
+ // },
52
+ // ],
53
+ // key: 'builtins',
54
+ // label: t('tools.builtins.groupName'),
55
+ // type: 'group',
56
+ // },
57
+ {
58
+ children: [
59
+ ...list.map((item) => ({
60
+ icon: item.meta?.avatar ? (
61
+ <Avatar avatar={item.meta?.avatar} size={24} />
62
+ ) : (
63
+ <Icon icon={ToyBrick} size={{ fontSize: 16 }} style={{ padding: 4 }} />
64
+ ),
65
+ key: item.identifier,
66
+ label: (
67
+ <ToolItem
68
+ identifier={item.identifier}
69
+ label={item.meta?.title || item.identifier}
70
+ />
71
+ ),
72
+ })),
73
+ {
74
+ icon: <Icon icon={Store} size={{ fontSize: 16 }} style={{ padding: 4 }} />,
75
+
76
+ key: 'plugin-store',
77
+ label: (
78
+ <Flexbox gap={40} horizontal justify={'space-between'} padding={'8px 12px'}>
79
+ {t('tools.plugins.store')} <Icon icon={ArrowRight} />
80
+ </Flexbox>
81
+ ),
82
+ onClick: (e) => {
83
+ e.domEvent.stopPropagation();
84
+ setOpen(true);
85
+ },
86
+ },
87
+ ],
88
+ key: 'plugins',
89
+ label: (
90
+ <Flexbox align={'center'} gap={40} horizontal justify={'space-between'}>
91
+ {t('tools.plugins.groupName')}
92
+ {enablePluginCount === 0 ? null : (
93
+ <div style={{ fontSize: 12, marginInlineEnd: 4 }}>
94
+ {t('tools.plugins.enabled', { num: enablePluginCount })}
95
+ </div>
96
+ )}
97
+ </Flexbox>
98
+ ),
99
+ type: 'group',
100
+ },
101
+ ],
102
+ onClick: (e) => {
103
+ e.domEvent.preventDefault();
104
+ },
105
+ }}
106
+ placement={'topLeft'}
107
+ trigger={['click']}
108
+ >
109
+ <ActionIcon icon={PocketKnife} placement={'bottom'} title={t('tools.title')} />
110
+ </Dropdown>
111
+ <PluginStore open={open} setOpen={setOpen} />
112
+ </>
113
+ );
114
+ });
115
+
116
+ export default Tools;
@@ -5,6 +5,7 @@ import History from './History';
5
5
  import ModelSwitch from './ModelSwitch';
6
6
  import Temperature from './Temperature';
7
7
  import Token from './Token';
8
+ import Tools from './Tools';
8
9
 
9
10
  export const actionMap = {
10
11
  clear: Clear,
@@ -14,6 +15,7 @@ export const actionMap = {
14
15
  stt: STT,
15
16
  temperature: Temperature,
16
17
  token: Token,
18
+ tools: Tools,
17
19
  } as const;
18
20
 
19
21
  type ActionMap = typeof actionMap;
@@ -24,7 +26,7 @@ type getActionList = (mobile?: boolean) => ActionKeys[];
24
26
 
25
27
  // we can make these action lists configurable in the future
26
28
  export const getLeftActionList: getActionList = (mobile) =>
27
- ['model', 'fileUpload', 'temperature', 'history', !mobile && 'stt', 'token'].filter(
29
+ ['model', 'fileUpload', 'temperature', 'history', !mobile && 'stt', 'tools', 'token'].filter(
28
30
  Boolean,
29
31
  ) as ActionKeys[];
30
32
 
@@ -8,8 +8,8 @@ import { Center, Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import PluginSettingsConfig from '@/features/PluginSettings';
10
10
  import { useChatStore } from '@/store/chat';
11
- import { pluginHelpers, usePluginStore } from '@/store/plugin';
12
- import { pluginSelectors } from '@/store/plugin/selectors';
11
+ import { pluginHelpers, useToolStore } from '@/store/tool';
12
+ import { pluginSelectors } from '@/store/tool/selectors';
13
13
 
14
14
  import { ErrorActionContainer, useStyles } from '../style';
15
15
 
@@ -19,8 +19,8 @@ const PluginSettings: RenderErrorMessage['Render'] = memo(({ id, plugin }) => {
19
19
  const theme = useTheme();
20
20
  const [resend, deleteMessage] = useChatStore((s) => [s.resendMessage, s.deleteMessage]);
21
21
  const pluginIdentifier = plugin?.identifier as string;
22
- const pluginMeta = usePluginStore(pluginSelectors.getPluginMetaById(pluginIdentifier), isEqual);
23
- const manifest = usePluginStore(pluginSelectors.getPluginManifestById(pluginIdentifier), isEqual);
22
+ const pluginMeta = useToolStore(pluginSelectors.getPluginMetaById(pluginIdentifier), isEqual);
23
+ const manifest = useToolStore(pluginSelectors.getPluginManifestById(pluginIdentifier), isEqual);
24
24
 
25
25
  return (
26
26
  <ErrorActionContainer>
@@ -4,11 +4,11 @@ import { memo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import PluginSettingsModal from '@/features/PluginSettingsModal';
7
- import { usePluginStore } from '@/store/plugin';
8
- import { pluginSelectors } from '@/store/plugin/selectors';
7
+ import { useToolStore } from '@/store/tool';
8
+ import { pluginSelectors } from '@/store/tool/selectors';
9
9
 
10
10
  const Settings = memo<{ id: string }>(({ id }) => {
11
- const item = usePluginStore(pluginSelectors.getPluginManifestById(id));
11
+ const item = useToolStore(pluginSelectors.getPluginManifestById(id));
12
12
  const [open, setOpen] = useState(false);
13
13
  const { t } = useTranslation('plugin');
14
14
  return (
@@ -14,8 +14,8 @@ import { memo, useState } from 'react';
14
14
  import { useTranslation } from 'react-i18next';
15
15
  import { Flexbox } from 'react-layout-kit';
16
16
 
17
- import { pluginHelpers, usePluginStore } from '@/store/plugin';
18
- import { pluginSelectors } from '@/store/plugin/selectors';
17
+ import { pluginHelpers, useToolStore } from '@/store/tool';
18
+ import { pluginSelectors } from '@/store/tool/selectors';
19
19
 
20
20
  import PluginResult from './PluginResultJSON';
21
21
  import Settings from './Settings';
@@ -47,8 +47,8 @@ const Inspector = memo<InspectorProps>(
47
47
  const { styles } = useStyles();
48
48
  const [open, setOpen] = useState(false);
49
49
 
50
- const item = usePluginStore(pluginSelectors.getPluginMetaById(id), isEqual);
51
- const showRightAction = usePluginStore(pluginSelectors.hasPluginUI(id));
50
+ const item = useToolStore(pluginSelectors.getPluginMetaById(id), isEqual);
51
+ const showRightAction = useToolStore(pluginSelectors.hasPluginUI(id));
52
52
  const pluginAvatar = pluginHelpers.getPluginAvatar(item?.meta);
53
53
  const pluginTitle = pluginHelpers.getPluginTitle(item?.meta);
54
54
 
@@ -5,7 +5,7 @@ import { Suspense, memo, useMemo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
- import { usePluginStore } from '@/store/plugin';
8
+ import { useToolStore } from '@/store/tool';
9
9
 
10
10
  import IFrameRender from './IFrameRender';
11
11
 
@@ -72,7 +72,7 @@ export interface PluginDefaultTypeProps {
72
72
  }
73
73
 
74
74
  const PluginDefaultType = memo<PluginDefaultTypeProps>(({ content, name }) => {
75
- const manifest = usePluginStore((s) => s.pluginManifestMap[name || '']);
75
+ const manifest = useToolStore((s) => s.pluginManifestMap[name || '']);
76
76
  let isJSON = true;
77
77
  try {
78
78
  JSON.parse(content);
@@ -4,8 +4,8 @@ import { memo, useRef, useState } from 'react';
4
4
 
5
5
  import { useChatStore } from '@/store/chat';
6
6
  import { chatSelectors } from '@/store/chat/selectors';
7
- import { usePluginStore } from '@/store/plugin';
8
- import { pluginSelectors } from '@/store/plugin/selectors';
7
+ import { useToolStore } from '@/store/tool';
8
+ import { pluginSelectors } from '@/store/tool/selectors';
9
9
 
10
10
  import { useOnPluginReadyForInteraction } from '../utils/iframeOnReady';
11
11
  import {
@@ -25,7 +25,7 @@ import {
25
25
 
26
26
  // just to simplify code a little, don't use this pattern everywhere
27
27
  const getSettings = (identifier: string) =>
28
- pluginSelectors.getPluginSettingsById(identifier)(usePluginStore.getState());
28
+ pluginSelectors.getPluginSettingsById(identifier)(useToolStore.getState());
29
29
  const getMessage = (id: string) => chatSelectors.getMessageById(id)(useChatStore.getState());
30
30
 
31
31
  interface IFrameRenderProps {
@@ -111,7 +111,7 @@ const IFrameRender = memo<IFrameRenderProps>(({ url, id, payload, width = 600, h
111
111
  });
112
112
 
113
113
  // when plugin update settings, we should update it to the plugin settings
114
- const updatePluginSettings = usePluginStore((s) => s.updatePluginSettings);
114
+ const updatePluginSettings = useToolStore((s) => s.updatePluginSettings);
115
115
  useOnPluginSettingsUpdate((value) => {
116
116
  if (!payload?.identifier) return;
117
117
 
@@ -1,7 +1,7 @@
1
1
  import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
2
2
  import { memo } from 'react';
3
3
 
4
- import { usePluginStore } from '@/store/plugin';
4
+ import { useToolStore } from '@/store/tool';
5
5
 
6
6
  import IFrameRender from './Iframe';
7
7
 
@@ -12,7 +12,7 @@ export interface PluginStandaloneTypeProps {
12
12
  }
13
13
 
14
14
  const PluginDefaultType = memo<PluginStandaloneTypeProps>(({ payload, id, name = 'unknown' }) => {
15
- const manifest = usePluginStore((s) => s.pluginManifestMap[name]);
15
+ const manifest = useToolStore((s) => s.pluginManifestMap[name]);
16
16
 
17
17
  if (!manifest?.ui) return;
18
18
 
@@ -1,9 +1,9 @@
1
1
  import { useEffect } from 'react';
2
2
 
3
3
  import { useChatStore } from '@/store/chat';
4
- import { usePluginStore } from '@/store/plugin';
5
4
  import { useSessionStore } from '@/store/session';
6
5
  import { agentSelectors } from '@/store/session/selectors';
6
+ import { useToolStore } from '@/store/tool';
7
7
 
8
8
  export const useInitConversation = () => {
9
9
  const [sessionId] = useSessionStore((s) => [s.activeId]);
@@ -18,7 +18,7 @@ export const useInitConversation = () => {
18
18
  useFetchMessages(sessionId, activeTopicId);
19
19
  useFetchTopics(sessionId);
20
20
 
21
- const [useFetchPluginStore, checkPluginsIsInstalled] = usePluginStore((s) => [
21
+ const [useFetchPluginStore, checkPluginsIsInstalled] = useToolStore((s) => [
22
22
  s.useFetchPluginStore,
23
23
  s.useCheckPluginsIsInstalled,
24
24
  ]);
@@ -13,8 +13,8 @@ import { localeOptions } from '@/locales/options';
13
13
  import { useChatStore } from '@/store/chat';
14
14
  import { useFileStore } from '@/store/file';
15
15
  import { settingsSelectors, useGlobalStore } from '@/store/global';
16
- import { usePluginStore } from '@/store/plugin';
17
16
  import { useSessionStore } from '@/store/session';
17
+ import { useToolStore } from '@/store/tool';
18
18
  import { switchLang } from '@/utils/switchLang';
19
19
 
20
20
  import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from '../features/ThemeSwatches';
@@ -35,7 +35,7 @@ const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig }) => {
35
35
  s.clearAllMessages,
36
36
  ]);
37
37
  const [removeAllFiles] = useFileStore((s) => [s.removeAllFiles]);
38
- const resetPluginSettings = usePluginStore((s) => s.resetPluginSettings);
38
+ const resetPluginSettings = useToolStore((s) => s.resetPluginSettings);
39
39
 
40
40
  const settings = useGlobalStore(settingsSelectors.currentSettings, isEqual);
41
41
  const [setThemeMode, setSettings, resetSettings] = useGlobalStore((s) => [
@@ -0,0 +1,56 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { Button } from 'antd';
3
+ import { LucideBlocks } from 'lucide-react';
4
+ import { forwardRef, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import { useStore } from '@/features/AgentSetting/store';
8
+ import DevModal from '@/features/PluginDevModal';
9
+ import { useToolStore } from '@/store/tool';
10
+
11
+ const AddPluginButton = forwardRef<HTMLButtonElement>((props, ref) => {
12
+ const { t } = useTranslation('setting');
13
+ const [showModal, setModal] = useState(false);
14
+ const [toggleAgentPlugin] = useStore((s) => [s.toggleAgentPlugin]);
15
+ const [installPlugin, saveToDevList, updateNewDevPlugin] = useToolStore((s) => [
16
+ s.installPlugin,
17
+ s.saveToCustomPluginList,
18
+ s.updateNewCustomPlugin,
19
+ ]);
20
+
21
+ const togglePlugin = async (pluginId: string, fetchManifest?: boolean) => {
22
+ toggleAgentPlugin(pluginId);
23
+ if (fetchManifest) {
24
+ await installPlugin(pluginId);
25
+ }
26
+ };
27
+
28
+ return (
29
+ <>
30
+ <DevModal
31
+ onOpenChange={setModal}
32
+ onSave={async (devPlugin) => {
33
+ // 先保存
34
+ saveToDevList(devPlugin);
35
+ // 再开启插件
36
+ await togglePlugin(devPlugin.identifier, true);
37
+ }}
38
+ onValueChange={updateNewDevPlugin}
39
+ open={showModal}
40
+ />
41
+ <Button
42
+ icon={<Icon icon={LucideBlocks} />}
43
+ onClick={(e) => {
44
+ e.stopPropagation();
45
+ setModal(true);
46
+ }}
47
+ ref={ref}
48
+ size={'small'}
49
+ >
50
+ {t('plugin.addTooltip')}
51
+ </Button>
52
+ </>
53
+ );
54
+ });
55
+
56
+ export default AddPluginButton;
@@ -0,0 +1,48 @@
1
+ import { Skeleton } from 'antd';
2
+ import { createStyles } from 'antd-style';
3
+
4
+ const useStyles = createStyles(({ css, prefixCls }) => ({
5
+ avatar: css`
6
+ .${prefixCls}-skeleton-header {
7
+ padding-right: 0;
8
+ }
9
+ `,
10
+ label: css`
11
+ li {
12
+ height: 14px !important;
13
+ }
14
+
15
+ li + li {
16
+ margin-block-start: 12px !important;
17
+ }
18
+ `,
19
+ }));
20
+
21
+ const LoadingList = () => {
22
+ const { styles } = useStyles();
23
+
24
+ const loadingItem = {
25
+ avatar: (
26
+ <Skeleton
27
+ active
28
+ avatar={{ size: 40 }}
29
+ className={styles.avatar}
30
+ paragraph={false}
31
+ title={false}
32
+ />
33
+ ),
34
+ label: (
35
+ <Skeleton
36
+ active
37
+ avatar={false}
38
+ paragraph={{ className: styles.label, style: { marginBottom: 0 } }}
39
+ style={{ width: 300 }}
40
+ title={false}
41
+ />
42
+ ),
43
+ };
44
+
45
+ return [loadingItem, loadingItem, loadingItem];
46
+ };
47
+
48
+ export default LoadingList;
@@ -5,8 +5,8 @@ import { useTranslation } from 'react-i18next';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
7
7
  import DevModal from '@/features/PluginDevModal';
8
- import { usePluginStore } from '@/store/plugin';
9
- import { pluginSelectors } from '@/store/plugin/selectors';
8
+ import { useToolStore } from '@/store/tool';
9
+ import { pluginSelectors } from '@/store/tool/selectors';
10
10
 
11
11
  import { useStore } from '../store';
12
12
 
@@ -19,15 +19,15 @@ const MarketList = memo<{ id: string }>(({ id }) => {
19
19
  const plugins = useStore((s) => s.config.plugins || []);
20
20
 
21
21
  const [useFetchPluginList, fetchPluginManifest, deleteCustomPlugin, updateCustomPlugin] =
22
- usePluginStore((s) => [
22
+ useToolStore((s) => [
23
23
  s.useFetchPluginStore,
24
24
  s.installPlugin,
25
25
  s.deleteCustomPlugin,
26
26
  s.updateCustomPlugin,
27
27
  ]);
28
28
 
29
- const pluginManifestLoading = usePluginStore((s) => s.pluginManifestLoading, isEqual);
30
- const devPlugin = usePluginStore(pluginSelectors.getDevPluginById(id), isEqual);
29
+ const pluginManifestLoading = useToolStore((s) => s.pluginInstallLoading, isEqual);
30
+ const devPlugin = useToolStore(pluginSelectors.getDevPluginById(id), isEqual);
31
31
 
32
32
  useFetchPluginList();
33
33
 
@@ -0,0 +1,36 @@
1
+ import { Switch } from 'antd';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { memo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import { useToolStore } from '@/store/tool';
7
+
8
+ import { useStore } from '../../store';
9
+
10
+ const PluginSwitch = memo<{ identifier: string }>(({ identifier }) => {
11
+ const pluginManifestLoading = useToolStore((s) => s.pluginInstallLoading, isEqual);
12
+ const [userEnabledPlugins, hasPlugin, toggleAgentPlugin] = useStore((s) => [
13
+ s.config.plugins || [],
14
+ !!s.config.plugins,
15
+ s.toggleAgentPlugin,
16
+ ]);
17
+
18
+ return (
19
+ <Flexbox align={'center'} gap={8} horizontal>
20
+ <Switch
21
+ checked={
22
+ // 如果在加载中,说明激活了
23
+ pluginManifestLoading[identifier] || !hasPlugin
24
+ ? false
25
+ : userEnabledPlugins.includes(identifier)
26
+ }
27
+ loading={pluginManifestLoading[identifier]}
28
+ onChange={() => {
29
+ toggleAgentPlugin(identifier);
30
+ }}
31
+ />
32
+ </Flexbox>
33
+ );
34
+ });
35
+
36
+ export default PluginSwitch;