@lobehub/chat 0.108.0 → 0.109.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/docs/Environment-Variable.md +2 -2
- package/docs/Environment-Variable.zh-CN.md +2 -2
- package/locales/en_US/plugin.json +19 -0
- package/locales/en_US/setting.json +26 -0
- package/locales/ja_JP/plugin.json +19 -0
- package/locales/ja_JP/setting.json +26 -0
- package/locales/ko_KR/plugin.json +19 -0
- package/locales/ko_KR/setting.json +26 -0
- package/locales/ru_RU/plugin.json +19 -0
- package/locales/ru_RU/setting.json +26 -0
- package/locales/zh_CN/plugin.json +19 -0
- package/locales/zh_CN/setting.json +26 -5
- package/locales/zh_TW/plugin.json +19 -0
- package/locales/zh_TW/setting.json +26 -0
- package/package.json +3 -3
- package/src/app/chat/features/ChatHeader/PluginTag/PluginStatus.tsx +6 -6
- package/src/app/chat/features/ChatHeader/PluginTag/index.tsx +4 -4
- package/src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx +37 -0
- package/src/app/chat/features/ChatInput/ActionBar/Tools/index.tsx +116 -0
- package/src/app/chat/features/ChatInput/ActionBar/config.ts +3 -1
- package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +4 -4
- package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +3 -3
- package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/index.tsx +4 -4
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/DefaultType/index.tsx +2 -2
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +4 -4
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/index.tsx +2 -2
- package/src/app/chat/features/Conversation/useInitConversation.ts +2 -2
- package/src/app/settings/common/Common.tsx +2 -2
- package/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx +56 -0
- package/src/features/AgentSetting/AgentPlugin/LoadingList.tsx +48 -0
- package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +5 -5
- package/src/features/AgentSetting/AgentPlugin/PluginAction/index.tsx +36 -0
- package/src/features/AgentSetting/AgentPlugin/index.tsx +157 -9
- package/src/features/AgentSetting/store/action.ts +0 -1
- package/src/features/PluginDevModal/MetaForm.tsx +3 -3
- package/src/features/PluginDevModal/PluginPreview.tsx +1 -1
- package/src/features/PluginDevModal/index.tsx +4 -1
- package/src/features/PluginSettings/index.tsx +3 -3
- package/src/features/PluginSettingsModal/index.tsx +22 -17
- package/src/features/PluginStore/Loading.tsx +13 -0
- package/src/features/PluginStore/PluginItem.tsx +81 -0
- package/src/features/PluginStore/PluginSettings.tsx +40 -0
- package/src/features/{AgentSetting/AgentPlugin/MarketSettingModal.tsx → PluginStore/StoreSetting.tsx} +2 -2
- package/src/features/PluginStore/index.tsx +95 -0
- package/src/layout/GlobalLayout/StoreHydration.tsx +3 -3
- package/src/locales/default/plugin.ts +19 -0
- package/src/locales/default/setting.ts +26 -5
- package/src/services/__tests__/chat.test.ts +221 -0
- package/src/services/__tests__/message.test.ts +333 -0
- package/src/services/__tests__/plugin.test.ts +88 -0
- package/src/services/__tests__/session.test.ts +227 -0
- package/src/services/__tests__/topic.test.ts +220 -0
- package/src/services/chat.ts +4 -6
- package/src/services/plugin.ts +12 -1
- package/src/store/chat/actions/message.test.ts +0 -1
- package/src/store/session/slices/agent/action.ts +24 -8
- package/src/store/{plugin → tool}/helpers.ts +2 -13
- package/src/store/tool/index.ts +2 -0
- package/src/store/tool/initialState.ts +11 -0
- package/src/store/tool/selectors.ts +3 -0
- package/src/store/tool/slices/customPlugin/action.test.ts +164 -0
- package/src/store/{plugin → tool}/slices/customPlugin/action.ts +2 -2
- package/src/store/tool/slices/customPlugin/selectors.test.ts +59 -0
- package/src/store/tool/slices/customPlugin/selectors.ts +9 -0
- package/src/store/tool/slices/plugin/action.test.ts +141 -0
- package/src/store/tool/slices/plugin/action.ts +95 -0
- package/src/store/{plugin → tool}/slices/plugin/initialState.ts +0 -7
- package/src/store/{plugin → tool/slices/plugin}/selectors.test.ts +4 -41
- package/src/store/{plugin → tool/slices/plugin}/selectors.ts +17 -19
- package/src/store/tool/slices/store/action.test.ts +396 -0
- package/src/store/tool/slices/store/action.ts +103 -0
- package/src/store/tool/slices/store/index.ts +3 -0
- package/src/store/tool/slices/store/initialState.ts +17 -0
- package/src/store/tool/slices/store/selectors.test.ts +48 -0
- package/src/store/tool/slices/store/selectors.ts +21 -0
- package/src/store/{plugin → tool}/store.ts +8 -6
- package/vitest.config.ts +1 -0
- package/src/features/AgentSetting/AgentPlugin/MarketList.tsx +0 -232
- package/src/features/AgentSetting/AgentPlugin/PluginSettings.tsx +0 -80
- package/src/store/chat/reducers/files.test.ts +0 -38
- package/src/store/chat/reducers/files.ts +0 -37
- package/src/store/plugin/hooks/useSessionHydrated.ts +0 -21
- package/src/store/plugin/index.ts +0 -2
- package/src/store/plugin/initialState.ts +0 -9
- package/src/store/plugin/slices/plugin/action.ts +0 -173
- /package/src/store/{plugin → tool}/slices/customPlugin/index.ts +0 -0
- /package/src/store/{plugin → tool}/slices/customPlugin/initialState.ts +0 -0
- /package/src/store/{plugin → tool}/slices/customPlugin/reducers/customPluginList.ts +0 -0
- /package/src/store/{plugin → tool}/slices/plugin/index.ts +0 -0
- /package/src/store/{plugin → tool}/slices/plugin/reducers/manifest.ts +0 -0
|
@@ -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,
|
|
12
|
-
import { pluginSelectors } from '@/store/
|
|
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 =
|
|
23
|
-
const manifest =
|
|
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 {
|
|
8
|
-
import { pluginSelectors } from '@/store/
|
|
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 =
|
|
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,
|
|
18
|
-
import { pluginSelectors } from '@/store/
|
|
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 =
|
|
51
|
-
const showRightAction =
|
|
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 {
|
|
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 =
|
|
75
|
+
const manifest = useToolStore((s) => s.pluginManifestMap[name || '']);
|
|
76
76
|
let isJSON = true;
|
|
77
77
|
try {
|
|
78
78
|
JSON.parse(content);
|
package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx
CHANGED
|
@@ -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 {
|
|
8
|
-
import { pluginSelectors } from '@/store/
|
|
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)(
|
|
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 =
|
|
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 {
|
|
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 =
|
|
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] =
|
|
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 =
|
|
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 {
|
|
9
|
-
import { pluginSelectors } from '@/store/
|
|
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
|
-
|
|
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 =
|
|
30
|
-
const devPlugin =
|
|
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;
|
|
@@ -1,13 +1,161 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Avatar, Form, Icon, Tooltip } from '@lobehub/ui';
|
|
2
|
+
import { Button, Empty, Space, Switch, Tag, Typography } from 'antd';
|
|
3
|
+
import isEqual from 'fast-deep-equal';
|
|
4
|
+
import { LucideToyBrick, LucideTrash2, Store } from 'lucide-react';
|
|
5
|
+
import { memo, useState } from 'react';
|
|
6
|
+
import { Trans, useTranslation } from 'react-i18next';
|
|
7
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
2
8
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
9
|
+
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
10
|
+
import PluginStore from '@/features/PluginStore';
|
|
11
|
+
import { pluginHelpers, useToolStore } from '@/store/tool';
|
|
12
|
+
import { pluginSelectors } from '@/store/tool/selectors';
|
|
5
13
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
)
|
|
14
|
+
import { useStore } from '../store';
|
|
15
|
+
import AddPluginButton from './AddPluginButton';
|
|
16
|
+
import LocalPluginItem from './LocalPluginItem';
|
|
17
|
+
import PluginAction from './PluginAction';
|
|
18
|
+
|
|
19
|
+
const AgentPlugin = memo(() => {
|
|
20
|
+
const { t } = useTranslation('setting');
|
|
21
|
+
|
|
22
|
+
const [showStore, setShowStore] = useState(false);
|
|
23
|
+
|
|
24
|
+
const [userEnabledPlugins, toggleAgentPlugin] = useStore((s) => [
|
|
25
|
+
s.config.plugins || [],
|
|
26
|
+
s.toggleAgentPlugin,
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const installedPlugins = useToolStore(pluginSelectors.installedPlugins, isEqual);
|
|
30
|
+
const customPluginList = useToolStore((s) => s.customPluginList, isEqual);
|
|
31
|
+
|
|
32
|
+
const isEmpty = installedPlugins.length === 0 && userEnabledPlugins.length === 0;
|
|
33
|
+
|
|
34
|
+
// =========== Plugin List =========== //
|
|
35
|
+
|
|
36
|
+
const list = installedPlugins.map(({ identifier, meta }) => ({
|
|
37
|
+
avatar: <Avatar avatar={meta.avatar} />,
|
|
38
|
+
children: <PluginAction identifier={identifier} />,
|
|
39
|
+
desc: pluginHelpers.getPluginDesc(meta),
|
|
40
|
+
label: pluginHelpers.getPluginTitle(meta),
|
|
41
|
+
minWidth: undefined,
|
|
42
|
+
tag: identifier,
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
// =========== Custom Plugin List =========== //
|
|
46
|
+
|
|
47
|
+
const customList = customPluginList.map(({ identifier, meta }) => ({
|
|
48
|
+
avatar: <Avatar avatar={pluginHelpers.getPluginAvatar(meta) || '🧩'} />,
|
|
49
|
+
children: <LocalPluginItem id={identifier} />,
|
|
50
|
+
desc: pluginHelpers.getPluginDesc(meta),
|
|
51
|
+
label: (
|
|
52
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
53
|
+
{pluginHelpers.getPluginTitle(meta)}
|
|
54
|
+
<Tag bordered={false} color={'gold'}>
|
|
55
|
+
{t('list.item.local.title', { ns: 'plugin' })}
|
|
56
|
+
</Tag>
|
|
57
|
+
</Flexbox>
|
|
58
|
+
),
|
|
59
|
+
minWidth: undefined,
|
|
60
|
+
tag: identifier,
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
// =========== Deprecated Plugin List =========== //
|
|
64
|
+
|
|
65
|
+
// 基于 userEnabledPlugins 和完整的 installedPlugins,检查出不在 totalList 中的插件
|
|
66
|
+
const deprecatedList = userEnabledPlugins
|
|
67
|
+
.filter((pluginId) => installedPlugins.findIndex((p) => p.identifier === pluginId) < 0)
|
|
68
|
+
.map((id) => ({
|
|
69
|
+
avatar: <Avatar avatar={'♻️'} />,
|
|
70
|
+
children: (
|
|
71
|
+
<Switch
|
|
72
|
+
checked={true}
|
|
73
|
+
onChange={() => {
|
|
74
|
+
toggleAgentPlugin(id);
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
),
|
|
78
|
+
label: (
|
|
79
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
80
|
+
{id}
|
|
81
|
+
<Tag bordered={false} color={'red'}>
|
|
82
|
+
{t('plugin.installStatus.deprecated')}
|
|
83
|
+
</Tag>
|
|
84
|
+
</Flexbox>
|
|
85
|
+
),
|
|
86
|
+
minWidth: undefined,
|
|
87
|
+
tag: id,
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
const hasDeprecated = deprecatedList.length > 0;
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
<PluginStore open={showStore} setOpen={setShowStore} />
|
|
95
|
+
<Form
|
|
96
|
+
items={[
|
|
97
|
+
{
|
|
98
|
+
children: isEmpty ? (
|
|
99
|
+
<Center padding={40}>
|
|
100
|
+
<Empty
|
|
101
|
+
description={
|
|
102
|
+
<Trans i18nKey={'plugin.empty'} ns={'setting'}>
|
|
103
|
+
暂无安装插件,
|
|
104
|
+
<Typography.Link
|
|
105
|
+
href={'/'}
|
|
106
|
+
onClick={(e) => {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
setShowStore(true);
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
前往插件市场
|
|
112
|
+
</Typography.Link>
|
|
113
|
+
安装
|
|
114
|
+
</Trans>
|
|
115
|
+
}
|
|
116
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
117
|
+
/>
|
|
118
|
+
</Center>
|
|
119
|
+
) : (
|
|
120
|
+
[...deprecatedList, ...customList, ...list]
|
|
121
|
+
),
|
|
122
|
+
extra: (
|
|
123
|
+
<Space.Compact style={{ width: 'auto' }}>
|
|
124
|
+
<AddPluginButton />
|
|
125
|
+
{hasDeprecated ? (
|
|
126
|
+
<Tooltip title={t('plugin.clearDeprecated')}>
|
|
127
|
+
<Button
|
|
128
|
+
icon={<Icon icon={LucideTrash2} />}
|
|
129
|
+
onClick={(e) => {
|
|
130
|
+
e.stopPropagation();
|
|
131
|
+
for (const i of deprecatedList) {
|
|
132
|
+
toggleAgentPlugin(i.tag as string);
|
|
133
|
+
}
|
|
134
|
+
}}
|
|
135
|
+
size={'small'}
|
|
136
|
+
/>
|
|
137
|
+
</Tooltip>
|
|
138
|
+
) : null}
|
|
139
|
+
<Tooltip title={t('plugin.store')}>
|
|
140
|
+
<Button
|
|
141
|
+
icon={<Icon icon={Store} />}
|
|
142
|
+
onClick={(e) => {
|
|
143
|
+
e.stopPropagation();
|
|
144
|
+
setShowStore(true);
|
|
145
|
+
}}
|
|
146
|
+
size={'small'}
|
|
147
|
+
/>
|
|
148
|
+
</Tooltip>
|
|
149
|
+
</Space.Compact>
|
|
150
|
+
),
|
|
151
|
+
icon: LucideToyBrick,
|
|
152
|
+
title: t('settingPlugin.title'),
|
|
153
|
+
},
|
|
154
|
+
]}
|
|
155
|
+
{...FORM_STYLE}
|
|
156
|
+
/>
|
|
157
|
+
</>
|
|
158
|
+
);
|
|
159
|
+
});
|
|
12
160
|
|
|
13
161
|
export default AgentPlugin;
|
|
@@ -232,7 +232,6 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
232
232
|
get().dispatchConfig({ config, type: 'update' });
|
|
233
233
|
},
|
|
234
234
|
setAgentMeta: (meta) => {
|
|
235
|
-
console.log(meta);
|
|
236
235
|
get().dispatchMeta({ type: 'update', value: meta });
|
|
237
236
|
},
|
|
238
237
|
|