@lobehub/chat 0.107.16 → 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.
Files changed (98) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  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/(desktop)/index.tsx +3 -2
  32. package/src/app/settings/common/Common.tsx +8 -3
  33. package/src/app/settings/common/index.tsx +3 -3
  34. package/src/app/settings/common/page.tsx +7 -1
  35. package/src/app/settings/page.tsx +4 -1
  36. package/src/config/server.ts +5 -1
  37. package/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx +56 -0
  38. package/src/features/AgentSetting/AgentPlugin/LoadingList.tsx +48 -0
  39. package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +5 -5
  40. package/src/features/AgentSetting/AgentPlugin/PluginAction/index.tsx +36 -0
  41. package/src/features/AgentSetting/AgentPlugin/index.tsx +157 -9
  42. package/src/features/AgentSetting/store/action.ts +0 -1
  43. package/src/features/PluginDevModal/MetaForm.tsx +3 -3
  44. package/src/features/PluginDevModal/PluginPreview.tsx +1 -1
  45. package/src/features/PluginDevModal/index.tsx +4 -1
  46. package/src/features/PluginSettings/index.tsx +3 -3
  47. package/src/features/PluginSettingsModal/index.tsx +22 -17
  48. package/src/features/PluginStore/Loading.tsx +13 -0
  49. package/src/features/PluginStore/PluginItem.tsx +81 -0
  50. package/src/features/PluginStore/PluginSettings.tsx +40 -0
  51. package/src/features/{AgentSetting/AgentPlugin/MarketSettingModal.tsx → PluginStore/StoreSetting.tsx} +2 -2
  52. package/src/features/PluginStore/index.tsx +95 -0
  53. package/src/layout/GlobalLayout/StoreHydration.tsx +3 -3
  54. package/src/locales/default/plugin.ts +19 -0
  55. package/src/locales/default/setting.ts +26 -5
  56. package/src/services/__tests__/chat.test.ts +221 -0
  57. package/src/services/__tests__/message.test.ts +333 -0
  58. package/src/services/__tests__/plugin.test.ts +88 -0
  59. package/src/services/__tests__/session.test.ts +227 -0
  60. package/src/services/__tests__/topic.test.ts +220 -0
  61. package/src/services/chat.ts +4 -6
  62. package/src/services/plugin.ts +12 -1
  63. package/src/store/chat/actions/message.test.ts +0 -1
  64. package/src/store/session/slices/agent/action.ts +24 -8
  65. package/src/store/{plugin → tool}/helpers.ts +2 -13
  66. package/src/store/tool/index.ts +2 -0
  67. package/src/store/tool/initialState.ts +11 -0
  68. package/src/store/tool/selectors.ts +3 -0
  69. package/src/store/tool/slices/customPlugin/action.test.ts +164 -0
  70. package/src/store/{plugin → tool}/slices/customPlugin/action.ts +2 -2
  71. package/src/store/tool/slices/customPlugin/selectors.test.ts +59 -0
  72. package/src/store/tool/slices/customPlugin/selectors.ts +9 -0
  73. package/src/store/tool/slices/plugin/action.test.ts +141 -0
  74. package/src/store/tool/slices/plugin/action.ts +95 -0
  75. package/src/store/{plugin → tool}/slices/plugin/initialState.ts +0 -7
  76. package/src/store/{plugin → tool/slices/plugin}/selectors.test.ts +4 -41
  77. package/src/store/{plugin → tool/slices/plugin}/selectors.ts +17 -19
  78. package/src/store/tool/slices/store/action.test.ts +396 -0
  79. package/src/store/tool/slices/store/action.ts +103 -0
  80. package/src/store/tool/slices/store/index.ts +3 -0
  81. package/src/store/tool/slices/store/initialState.ts +17 -0
  82. package/src/store/tool/slices/store/selectors.test.ts +48 -0
  83. package/src/store/tool/slices/store/selectors.ts +21 -0
  84. package/src/store/{plugin → tool}/store.ts +8 -6
  85. package/vitest.config.ts +1 -0
  86. package/src/features/AgentSetting/AgentPlugin/MarketList.tsx +0 -232
  87. package/src/features/AgentSetting/AgentPlugin/PluginSettings.tsx +0 -80
  88. package/src/store/chat/reducers/files.test.ts +0 -38
  89. package/src/store/chat/reducers/files.ts +0 -37
  90. package/src/store/plugin/hooks/useSessionHydrated.ts +0 -21
  91. package/src/store/plugin/index.ts +0 -2
  92. package/src/store/plugin/initialState.ts +0 -9
  93. package/src/store/plugin/slices/plugin/action.ts +0 -173
  94. /package/src/store/{plugin → tool}/slices/customPlugin/index.ts +0 -0
  95. /package/src/store/{plugin → tool}/slices/customPlugin/initialState.ts +0 -0
  96. /package/src/store/{plugin → tool}/slices/customPlugin/reducers/customPluginList.ts +0 -0
  97. /package/src/store/{plugin → tool}/slices/plugin/index.ts +0 -0
  98. /package/src/store/{plugin → tool}/slices/plugin/reducers/manifest.ts +0 -0
@@ -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
  ]);
@@ -6,14 +6,15 @@ import { FC, memo } from 'react';
6
6
  import ResponsiveIndex from '@/components/ResponsiveIndex';
7
7
 
8
8
  import Common from '../common';
9
+ import { SettingsCommonProps } from '../common/Common';
9
10
  import Layout from './layout.desktop';
10
11
 
11
12
  const Mobile: FC = dynamic(() => import('../(mobile)'), { ssr: false }) as FC;
12
13
 
13
- export default memo(() => (
14
+ export default memo<SettingsCommonProps>((props) => (
14
15
  <ResponsiveIndex Mobile={Mobile}>
15
16
  <Layout>
16
- <Common />
17
+ <Common {...props} />
17
18
  </Layout>
18
19
  </ResponsiveIndex>
19
20
  ));
@@ -13,15 +13,19 @@ 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';
21
21
 
22
22
  type SettingItemGroup = ItemGroup;
23
23
 
24
- const Common = memo(() => {
24
+ export interface SettingsCommonProps {
25
+ showAccessCodeConfig: boolean;
26
+ }
27
+
28
+ const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig }) => {
25
29
  const { t } = useTranslation('setting');
26
30
  const [form] = AntForm.useForm();
27
31
 
@@ -31,7 +35,7 @@ const Common = memo(() => {
31
35
  s.clearAllMessages,
32
36
  ]);
33
37
  const [removeAllFiles] = useFileStore((s) => [s.removeAllFiles]);
34
- const resetPluginSettings = usePluginStore((s) => s.resetPluginSettings);
38
+ const resetPluginSettings = useToolStore((s) => s.resetPluginSettings);
35
39
 
36
40
  const settings = useGlobalStore(settingsSelectors.currentSettings, isEqual);
37
41
  const [setThemeMode, setSettings, resetSettings] = useGlobalStore((s) => [
@@ -154,6 +158,7 @@ const Common = memo(() => {
154
158
  {
155
159
  children: <Input.Password placeholder={t('settingSystem.accessCode.placeholder')} />,
156
160
  desc: t('settingSystem.accessCode.desc'),
161
+ hidden: !showAccessCodeConfig,
157
162
  label: t('settingSystem.accessCode.title'),
158
163
  name: 'password',
159
164
  },
@@ -9,16 +9,16 @@ import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOn
9
9
  import { SettingsTabs } from '@/store/global/initialState';
10
10
 
11
11
  import Footer from '../features/Footer';
12
- import Common from './Common';
12
+ import Common, { SettingsCommonProps } from './Common';
13
13
 
14
- export default memo(() => {
14
+ export default memo<SettingsCommonProps>((props) => {
15
15
  useSwitchSideBarOnInit(SettingsTabs.Common);
16
16
  const { t } = useTranslation('setting');
17
17
 
18
18
  return (
19
19
  <>
20
20
  <PageTitle title={t('tab.common')} />
21
- <Common />
21
+ <Common {...props} />
22
22
  <Footer>LobeChat v{CURRENT_VERSION}</Footer>
23
23
  </>
24
24
  );
@@ -1,3 +1,9 @@
1
+ import { getServerConfig } from '@/config/server';
2
+
1
3
  import Index from './index';
2
4
 
3
- export default () => <Index />;
5
+ export default () => {
6
+ const { SHOW_ACCESS_CODE_CONFIG } = getServerConfig();
7
+
8
+ return <Index showAccessCodeConfig={SHOW_ACCESS_CODE_CONFIG} />;
9
+ };
@@ -1,3 +1,4 @@
1
+ import { getServerConfig } from '@/config/server';
1
2
  import { isMobileDevice } from '@/utils/responsive';
2
3
 
3
4
  import DesktopPage from './(desktop)';
@@ -8,7 +9,9 @@ const Page = () => {
8
9
 
9
10
  const Page = mobile ? MobilePage : DesktopPage;
10
11
 
11
- return <Page />;
12
+ const { SHOW_ACCESS_CODE_CONFIG } = getServerConfig();
13
+
14
+ return <Page showAccessCodeConfig={SHOW_ACCESS_CODE_CONFIG} />;
12
15
  };
13
16
 
14
17
  export default Page;
@@ -33,8 +33,12 @@ export const getServerConfig = () => {
33
33
  regions = process.env.OPENAI_FUNCTION_REGIONS.split(',');
34
34
  }
35
35
 
36
+ const ACCESS_CODE = process.env.ACCESS_CODE;
37
+
36
38
  return {
37
- ACCESS_CODE: process.env.ACCESS_CODE,
39
+ ACCESS_CODE,
40
+
41
+ SHOW_ACCESS_CODE_CONFIG: !!ACCESS_CODE,
38
42
 
39
43
  OPENAI_API_KEY: process.env.OPENAI_API_KEY,
40
44
  OPENAI_PROXY_URL: process.env.OPENAI_PROXY_URL,
@@ -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;