@lobehub/chat 0.111.6 → 0.112.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 (130) hide show
  1. package/.i18nrc.js +1 -0
  2. package/CHANGELOG.md +59 -0
  3. package/locales/en_US/chat.json +12 -12
  4. package/locales/en_US/common.json +4 -4
  5. package/locales/en_US/error.json +16 -14
  6. package/locales/en_US/market.json +2 -2
  7. package/locales/en_US/migration.json +4 -4
  8. package/locales/en_US/plugin.json +75 -38
  9. package/locales/en_US/setting.json +7 -7
  10. package/locales/fr_FR/chat.json +11 -11
  11. package/locales/fr_FR/common.json +4 -4
  12. package/locales/fr_FR/error.json +12 -10
  13. package/locales/fr_FR/plugin.json +42 -5
  14. package/locales/fr_FR/setting.json +12 -12
  15. package/locales/fr_FR/welcome.json +3 -3
  16. package/locales/ja_JP/chat.json +1 -1
  17. package/locales/ja_JP/common.json +4 -4
  18. package/locales/ja_JP/error.json +16 -14
  19. package/locales/ja_JP/migration.json +4 -4
  20. package/locales/ja_JP/plugin.json +70 -33
  21. package/locales/ja_JP/setting.json +1 -1
  22. package/locales/ko_KR/chat.json +1 -1
  23. package/locales/ko_KR/common.json +4 -4
  24. package/locales/ko_KR/error.json +16 -14
  25. package/locales/ko_KR/migration.json +4 -4
  26. package/locales/ko_KR/plugin.json +71 -34
  27. package/locales/ko_KR/setting.json +1 -1
  28. package/locales/ru_RU/chat.json +1 -1
  29. package/locales/ru_RU/common.json +4 -4
  30. package/locales/ru_RU/error.json +16 -14
  31. package/locales/ru_RU/market.json +2 -2
  32. package/locales/ru_RU/migration.json +4 -4
  33. package/locales/ru_RU/plugin.json +71 -34
  34. package/locales/ru_RU/setting.json +1 -1
  35. package/locales/zh_CN/chat.json +1 -1
  36. package/locales/zh_CN/error.json +2 -0
  37. package/locales/zh_CN/plugin.json +42 -5
  38. package/locales/zh_CN/setting.json +1 -1
  39. package/locales/zh_TW/chat.json +1 -1
  40. package/locales/zh_TW/common.json +4 -4
  41. package/locales/zh_TW/error.json +16 -14
  42. package/locales/zh_TW/market.json +2 -2
  43. package/locales/zh_TW/migration.json +4 -4
  44. package/locales/zh_TW/plugin.json +98 -61
  45. package/locales/zh_TW/setting.json +1 -1
  46. package/package.json +4 -2
  47. package/scripts/i18nWorkflow/genDiff.ts +7 -8
  48. package/scripts/i18nWorkflow/utils.ts +0 -14
  49. package/scripts/readmeWorkflow/syncPluginIndex.ts +9 -11
  50. package/src/app/api/plugins/route.ts +2 -4
  51. package/src/app/api/proxy/route.ts +10 -0
  52. package/src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx +3 -8
  53. package/src/app/chat/features/ChatInput/ActionBar/Tools/index.tsx +3 -3
  54. package/src/app/chat/features/Conversation/ChatList/Error/index.tsx +6 -0
  55. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +1 -1
  56. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +7 -5
  57. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/index.tsx +2 -2
  58. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/DefaultType/index.tsx +6 -4
  59. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/index.tsx +2 -2
  60. package/src/app/chat/features/Migration/ExportConfigButton.tsx +1 -1
  61. package/src/app/market/features/AgentDetailContent/AgentInfo/index.tsx +1 -0
  62. package/src/app/market/features/AgentDetailContent/AgentInfo/style.ts +1 -9
  63. package/src/app/settings/common/Common.tsx +5 -7
  64. package/src/components/FileList/FileItem.tsx +4 -2
  65. package/src/components/HotKeys/index.tsx +5 -4
  66. package/src/const/plugin.ts +1 -0
  67. package/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx +2 -2
  68. package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +19 -53
  69. package/src/features/AgentSetting/AgentPlugin/index.tsx +5 -11
  70. package/src/features/AgentSetting/store/initialState.ts +1 -1
  71. package/src/features/PluginDetailModal/APIs.tsx +44 -0
  72. package/src/features/PluginDetailModal/Meta.tsx +59 -0
  73. package/src/features/PluginDetailModal/index.tsx +79 -0
  74. package/src/features/PluginDevModal/PluginPreview.tsx +8 -10
  75. package/src/features/PluginDevModal/UrlManifestForm.tsx +131 -0
  76. package/src/features/PluginDevModal/index.tsx +31 -32
  77. package/src/features/PluginSettings/index.tsx +1 -1
  78. package/src/features/PluginStore/AddPluginButton.tsx +47 -0
  79. package/src/features/PluginStore/InstalledPluginList.tsx +54 -0
  80. package/src/features/PluginStore/OnlineList.tsx +95 -0
  81. package/src/features/PluginStore/PluginItem/Action.tsx +118 -0
  82. package/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx +50 -0
  83. package/src/features/PluginStore/PluginItem/PluginTag.tsx +58 -0
  84. package/src/features/PluginStore/PluginItem/index.tsx +73 -0
  85. package/src/features/PluginStore/index.tsx +20 -92
  86. package/src/hooks/usePlatform.ts +15 -0
  87. package/src/locales/default/chat.ts +1 -1
  88. package/src/locales/default/error.ts +2 -0
  89. package/src/locales/default/plugin.ts +41 -4
  90. package/src/locales/default/setting.ts +1 -1
  91. package/src/services/__tests__/__snapshots__/plugin.test.ts.snap +154 -0
  92. package/src/services/__tests__/chat.test.ts +1 -1
  93. package/src/services/__tests__/global.test.ts +57 -0
  94. package/src/services/__tests__/openai/OpenAPI_V3.json +245 -0
  95. package/src/services/__tests__/openai/plugin.json +22 -0
  96. package/src/services/__tests__/plugin.test.ts +90 -1
  97. package/src/services/__tests__/share.test.ts +86 -0
  98. package/src/services/_url.ts +2 -0
  99. package/src/services/global.ts +15 -0
  100. package/src/services/plugin.ts +99 -15
  101. package/src/services/share.ts +34 -0
  102. package/src/store/chat/actions/enhance.test.ts +15 -15
  103. package/src/store/chat/actions/message.test.ts +3 -2
  104. package/src/store/chat/actions/plugin.test.ts +281 -0
  105. package/src/store/chat/actions/plugin.ts +22 -3
  106. package/src/store/chat/actions/share.ts +2 -2
  107. package/src/store/chat/actions/topic.test.ts +12 -4
  108. package/src/store/global/slices/common.ts +2 -2
  109. package/src/store/session/selectors.ts +1 -1
  110. package/src/store/session/slices/agent/selectors.ts +1 -1
  111. package/src/store/tool/helpers.ts +8 -1
  112. package/src/store/tool/slices/customPlugin/action.ts +4 -1
  113. package/src/store/tool/slices/plugin/selectors.test.ts +22 -3
  114. package/src/store/tool/slices/plugin/selectors.ts +24 -5
  115. package/src/store/tool/slices/store/selectors.test.ts +18 -1
  116. package/src/store/tool/slices/store/selectors.ts +14 -3
  117. package/src/styles/antdOverride.ts +7 -0
  118. package/src/types/openai/chat.ts +0 -1
  119. package/src/types/openai/plugin.ts +57 -0
  120. package/src/types/tool/index.ts +1 -2
  121. package/src/types/tool/plugin.ts +13 -1
  122. package/src/types/tool/tool.ts +1 -0
  123. package/src/types/translate.ts +0 -1
  124. package/src/utils/platform.ts +8 -5
  125. package/src/features/PluginDevModal/UrlModeForm.tsx +0 -98
  126. package/src/features/PluginSettingsModal/index.tsx +0 -59
  127. package/src/features/PluginStore/PluginItem.tsx +0 -82
  128. package/src/features/PluginStore/PluginSettings.tsx +0 -40
  129. package/src/services/latestVersion.ts +0 -11
  130. package/src/services/shareGPT.ts +0 -28
@@ -1,15 +1,14 @@
1
- import { Checkbox, Tag } from 'antd';
1
+ import { Checkbox } from 'antd';
2
2
  import { memo } from 'react';
3
- import { useTranslation } from 'react-i18next';
4
3
  import { Flexbox } from 'react-layout-kit';
5
4
 
5
+ import PluginTag from '@/features/PluginStore/PluginItem/PluginTag';
6
6
  import { useSessionStore } from '@/store/session';
7
7
  import { agentSelectors } from '@/store/session/selectors';
8
8
  import { useToolStore } from '@/store/tool';
9
9
  import { customPluginSelectors } from '@/store/tool/selectors';
10
10
 
11
11
  const ToolItem = memo<{ identifier: string; label: string }>(({ identifier, label }) => {
12
- const { t } = useTranslation('plugin');
13
12
  const [checked, togglePlugin] = useSessionStore((s) => [
14
13
  agentSelectors.currentAgentPlugins(s).includes(identifier),
15
14
  s.togglePlugin,
@@ -30,11 +29,7 @@ const ToolItem = memo<{ identifier: string; label: string }>(({ identifier, labe
30
29
  >
31
30
  <Flexbox align={'center'} gap={8} horizontal>
32
31
  {label}
33
- {isCustom && (
34
- <Tag bordered={false} color={'gold'}>
35
- {t('list.item.local.title')}
36
- </Tag>
37
- )}
32
+ {isCustom && <PluginTag showText={false} type={'customPlugin'} />}
38
33
  </Flexbox>
39
34
  <Checkbox
40
35
  checked={checked}
@@ -2,7 +2,7 @@ import { ActionIcon, Avatar, Icon } from '@lobehub/ui';
2
2
  import { Dropdown } from 'antd';
3
3
  import { createStyles } from 'antd-style';
4
4
  import isEqual from 'fast-deep-equal';
5
- import { ArrowRight, PencilRuler, Store, ToyBrick } from 'lucide-react';
5
+ import { ArrowRight, Blocks, Store, ToyBrick } from 'lucide-react';
6
6
  import { memo, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { Flexbox } from 'react-layout-kit';
@@ -103,10 +103,10 @@ const Tools = memo(() => {
103
103
  e.domEvent.preventDefault();
104
104
  },
105
105
  }}
106
- placement={'topLeft'}
106
+ placement={'top'}
107
107
  trigger={['click']}
108
108
  >
109
- <ActionIcon icon={PencilRuler} placement={'bottom'} title={t('tools.title')} />
109
+ <ActionIcon icon={Blocks} placement={'bottom'} title={t('tools.title')} />
110
110
  </Dropdown>
111
111
  <PluginStore open={open} setOpen={setOpen} />
112
112
  </>
@@ -37,6 +37,12 @@ export const renderErrorMessages: ChatListProps['renderErrorMessages'] = {
37
37
  [PluginErrorType.PluginServerError]: {
38
38
  Render: PluginError,
39
39
  },
40
+ [PluginErrorType.PluginGatewayError]: {
41
+ Render: PluginError,
42
+ },
43
+ [PluginErrorType.PluginOpenApiInitError]: {
44
+ Render: PluginError,
45
+ },
40
46
  [PluginErrorType.PluginSettingsInvalid]: {
41
47
  Render: PluginSettings,
42
48
  config: {
@@ -18,7 +18,7 @@ export const FunctionMessage = memo<ChatMessage>(({ id, content, plugin }) => {
18
18
  const [showRender, setShow] = useState(true);
19
19
 
20
20
  return (
21
- <Flexbox gap={12} id={id}>
21
+ <Flexbox gap={12} id={id} width={'100%'}>
22
22
  <Inspector showRender={showRender} {...fcProps} setShow={setShow} />
23
23
  {showRender && (
24
24
  <PluginRender
@@ -2,17 +2,19 @@ import { ActionIcon } from '@lobehub/ui';
2
2
  import { LucideSettings } from 'lucide-react';
3
3
  import { memo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
+ import PluginDetailModal from 'src/features/PluginDetailModal';
5
6
 
6
- import PluginSettingsModal from '@/features/PluginSettingsModal';
7
- import { useToolStore } from '@/store/tool';
7
+ import { pluginHelpers, useToolStore } from '@/store/tool';
8
8
  import { pluginSelectors } from '@/store/tool/selectors';
9
9
 
10
10
  const Settings = memo<{ id: string }>(({ id }) => {
11
11
  const item = useToolStore(pluginSelectors.getPluginManifestById(id));
12
12
  const [open, setOpen] = useState(false);
13
13
  const { t } = useTranslation('plugin');
14
+ const hasSettings = pluginHelpers.isSettingSchemaNonEmpty(item?.settings);
15
+
14
16
  return (
15
- item?.settings && (
17
+ hasSettings && (
16
18
  <>
17
19
  <ActionIcon
18
20
  icon={LucideSettings}
@@ -21,13 +23,13 @@ const Settings = memo<{ id: string }>(({ id }) => {
21
23
  }}
22
24
  title={t('setting')}
23
25
  />
24
- <PluginSettingsModal
26
+ <PluginDetailModal
25
27
  id={id}
26
28
  onClose={() => {
27
29
  setOpen(false);
28
30
  }}
29
31
  open={open}
30
- schema={item.settings}
32
+ schema={item?.settings}
31
33
  />
32
34
  </>
33
35
  )
@@ -50,7 +50,7 @@ const Inspector = memo<InspectorProps>(
50
50
  const pluginMeta = useToolStore(pluginSelectors.getPluginMetaById(id), isEqual);
51
51
  const showRightAction = useToolStore(pluginSelectors.isPluginHasUI(id));
52
52
  const pluginAvatar = pluginHelpers.getPluginAvatar(pluginMeta);
53
- const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('plugins.unknown');
53
+ const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('plugins.loading');
54
54
 
55
55
  const avatar = pluginAvatar ? (
56
56
  <Avatar avatar={pluginAvatar} size={32} />
@@ -80,7 +80,7 @@ const Inspector = memo<InspectorProps>(
80
80
  ) : (
81
81
  avatar
82
82
  )}
83
- {loading ? t('plugins.loading') : pluginTitle}
83
+ {pluginTitle}
84
84
  {showRightAction && <Icon icon={showRender ? LucideChevronUp : LucideChevronDown} />}
85
85
  </Flexbox>
86
86
  {
@@ -72,7 +72,7 @@ export interface PluginDefaultTypeProps {
72
72
  name?: string;
73
73
  }
74
74
 
75
- const PluginDefaultType = memo<PluginDefaultTypeProps>(({ content, name }) => {
75
+ const PluginDefaultType = memo<PluginDefaultTypeProps>(({ content, name, loading }) => {
76
76
  const manifest = useToolStore(pluginSelectors.getPluginManifestById(name || ''));
77
77
  let isJSON = true;
78
78
  try {
@@ -85,9 +85,11 @@ const PluginDefaultType = memo<PluginDefaultTypeProps>(({ content, name }) => {
85
85
 
86
86
  if (!isJSON) {
87
87
  return (
88
- <Flexbox gap={8}>
89
- <Loading />
90
- </Flexbox>
88
+ loading && (
89
+ <Flexbox gap={8}>
90
+ <Loading />
91
+ </Flexbox>
92
+ )
91
93
  );
92
94
  }
93
95
 
@@ -13,14 +13,14 @@ export interface PluginRenderProps {
13
13
  type?: LobePluginType;
14
14
  }
15
15
 
16
- const PluginRender = memo<PluginRenderProps>(({ content, id, payload, name, type }) => {
16
+ const PluginRender = memo<PluginRenderProps>(({ content, id, payload, name, type, loading }) => {
17
17
  switch (type) {
18
18
  case 'standalone': {
19
19
  return <Standalone id={id} name={name} payload={payload} />;
20
20
  }
21
21
 
22
22
  default: {
23
- return <DefaultType content={content} name={name} />;
23
+ return <DefaultType content={content} loading={loading} name={name} />;
24
24
  }
25
25
  }
26
26
  });
@@ -2,7 +2,7 @@ import { Button } from 'antd';
2
2
  import { memo } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
 
5
- const ExportConfigButton = memo<{ primary?: boolean, state: any; }>(({ state, primary }) => {
5
+ const ExportConfigButton = memo<{ primary?: boolean; state: any }>(({ state, primary }) => {
6
6
  const { t } = useTranslation('migration');
7
7
 
8
8
  const exportData = () => {
@@ -55,6 +55,7 @@ const AgentModalInner = memo(() => {
55
55
  },
56
56
  ]}
57
57
  onChange={setTab}
58
+ variant={'compact'}
58
59
  />
59
60
  </Flexbox>
60
61
  <Flexbox style={{ padding: 16 }}>
@@ -29,15 +29,7 @@ export const useStyles = createStyles(({ css, token, prefixCls, stylish }) => ({
29
29
  `,
30
30
  markdown: stylish.markdownInChat,
31
31
  nav: css`
32
- padding-top: 4px;
33
-
34
- .${prefixCls}-tabs-tab {
35
- margin: 4px !important;
36
-
37
- + .${prefixCls}-tabs-tab {
38
- margin: 4px !important;
39
- }
40
- }
32
+ padding-top: 8px;
41
33
  `,
42
34
  title: css`
43
35
  font-size: 20px;
@@ -69,13 +69,11 @@ const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig }) => {
69
69
  },
70
70
  okText: t('ok', { ns: 'common' }),
71
71
  onOk: async () => {
72
- await Promise.all([
73
- clearSessions,
74
- removeAllPlugins,
75
- clearTopics,
76
- removeAllFiles,
77
- clearAllMessages,
78
- ]);
72
+ await clearSessions();
73
+ await removeAllPlugins();
74
+ await clearTopics();
75
+ await removeAllFiles();
76
+ await clearAllMessages();
79
77
 
80
78
  message.success(t('danger.clear.success'));
81
79
  },
@@ -3,6 +3,7 @@ import { createStyles } from 'antd-style';
3
3
  import { Trash } from 'lucide-react';
4
4
  import { CSSProperties, memo, useCallback } from 'react';
5
5
 
6
+ import { usePlatform } from '@/hooks/usePlatform';
6
7
  import { useFileStore } from '@/store/file';
7
8
 
8
9
  import { MIN_IMAGE_SIZE } from './style';
@@ -38,6 +39,7 @@ const FileItem = memo<FileItemProps>(({ editable, id, alwaysShowClose }) => {
38
39
  const IMAGE_SIZE = editable ? MIN_IMAGE_SIZE : '100%';
39
40
  const { data, isLoading } = useFetchFile(id);
40
41
  const { styles, cx } = useStyles();
42
+ const { isSafari } = usePlatform();
41
43
 
42
44
  const handleRemoveFile = useCallback(
43
45
  (e: any) => {
@@ -62,11 +64,11 @@ const FileItem = memo<FileItemProps>(({ editable, id, alwaysShowClose }) => {
62
64
  }
63
65
  alt={data?.name || id || ''}
64
66
  alwaysShowActions={alwaysShowClose}
65
- height={'auto'}
67
+ height={isSafari ? 'auto' : '100%'}
66
68
  isLoading={isLoading}
67
69
  size={IMAGE_SIZE as any}
68
70
  src={data?.url}
69
- style={{ height: 'auto' }}
71
+ style={{ height: isSafari ? 'auto' : '100%' }}
70
72
  wrapperClassName={cx(styles.image, editable && styles.editableImage)}
71
73
  />
72
74
  );
@@ -8,7 +8,7 @@ import { memo, useEffect, useState } from 'react';
8
8
  import { Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { CLEAN_MESSAGE_KEY, META_KEY, PREFIX_KEY } from '@/const/hotkeys';
11
- import { isApplePlatform } from '@/utils/platform';
11
+ import { usePlatform } from '@/hooks/usePlatform';
12
12
 
13
13
  const useStyles = createStyles(
14
14
  ({ css, token }) => css`
@@ -44,12 +44,13 @@ const HotKeys = memo<HotKeysProps>(({ keys, desc }) => {
44
44
  const { styles } = useStyles();
45
45
  const [keysGroup, setKeysGroup] = useState(keys.split('+'));
46
46
  const visibility = typeof window === 'undefined' ? 'hidden' : 'visible';
47
+ const { isApple } = usePlatform();
47
48
 
48
49
  useEffect(() => {
49
50
  const mapping: Record<string, any> = {
50
- [CLEAN_MESSAGE_KEY]: isApplePlatform() ? <Icon icon={Delete} /> : 'backspace',
51
- [META_KEY]: isApplePlatform() ? <Icon icon={Command} /> : 'ctrl',
52
- [PREFIX_KEY]: isApplePlatform() ? <Icon icon={Option} /> : 'alt',
51
+ [CLEAN_MESSAGE_KEY]: isApple ? <Icon icon={Delete} /> : 'backspace',
52
+ [META_KEY]: isApple ? <Icon icon={Command} /> : 'ctrl',
53
+ [PREFIX_KEY]: isApple ? <Icon icon={Option} /> : 'alt',
53
54
  };
54
55
  const newValue = keys
55
56
  .split('+')
@@ -1 +1,2 @@
1
1
  export const PLUGIN_SCHEMA_SEPARATOR = '____';
2
+ export const PLUGIN_SCHEMA_API_MD5_PREFIX = 'MD5HASH_';
@@ -1,6 +1,6 @@
1
1
  import { Icon } from '@lobehub/ui';
2
2
  import { Button } from 'antd';
3
- import { LucideBlocks } from 'lucide-react';
3
+ import { PackagePlus } from 'lucide-react';
4
4
  import { forwardRef, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
@@ -33,7 +33,7 @@ const AddPluginButton = forwardRef<HTMLButtonElement>((props, ref) => {
33
33
  open={showModal}
34
34
  />
35
35
  <Button
36
- icon={<Icon icon={LucideBlocks} />}
36
+ icon={<Icon icon={PackagePlus} />}
37
37
  onClick={() => {
38
38
  setModal(true);
39
39
  }}
@@ -1,75 +1,41 @@
1
- import { Button, Switch } from 'antd';
1
+ import { Switch } from 'antd';
2
2
  import isEqual from 'fast-deep-equal';
3
- import { memo, useState } from 'react';
4
- import { useTranslation } from 'react-i18next';
3
+ import { memo } from 'react';
5
4
  import { Flexbox } from 'react-layout-kit';
6
5
 
7
- import DevModal from '@/features/PluginDevModal';
8
6
  import { useToolStore } from '@/store/tool';
9
- import { pluginSelectors } from '@/store/tool/selectors';
10
7
 
11
8
  import { useStore } from '../store';
12
9
 
13
10
  const MarketList = memo<{ id: string }>(({ id }) => {
14
- const { t } = useTranslation('plugin');
15
-
16
- const [showModal, setModal] = useState(false);
17
-
18
11
  const [toggleAgentPlugin, hasPlugin] = useStore((s) => [s.toggleAgentPlugin, !!s.config.plugins]);
19
12
  const plugins = useStore((s) => s.config.plugins || []);
20
13
 
21
- const [useFetchPluginList, fetchPluginManifest, deleteCustomPlugin, updateCustomPlugin] =
22
- useToolStore((s) => [
23
- s.useFetchPluginStore,
24
- s.installPlugin,
25
- s.uninstallCustomPlugin,
26
- s.updateCustomPlugin,
27
- ]);
14
+ const [useFetchPluginList, fetchPluginManifest] = useToolStore((s) => [
15
+ s.useFetchPluginStore,
16
+ s.installPlugin,
17
+ ]);
28
18
 
29
19
  const pluginManifestLoading = useToolStore((s) => s.pluginInstallLoading, isEqual);
30
- const devPlugin = useToolStore(pluginSelectors.getCustomPluginById(id), isEqual);
31
20
 
32
21
  useFetchPluginList();
33
22
 
34
23
  return (
35
- <>
36
- <DevModal
37
- mode={'edit'}
38
- onDelete={() => {
39
- deleteCustomPlugin(id);
40
- toggleAgentPlugin(id, false);
41
- }}
42
- onOpenChange={setModal}
43
- onSave={(value) => {
44
- updateCustomPlugin(id, value);
24
+ <Flexbox align={'center'} gap={8} horizontal>
25
+ <Switch
26
+ checked={
27
+ // 如果在加载中,说明激活了
28
+ pluginManifestLoading[id] || !hasPlugin ? false : plugins.includes(id)
29
+ }
30
+ loading={pluginManifestLoading[id]}
31
+ onChange={(checked) => {
32
+ toggleAgentPlugin(id);
33
+ if (checked) {
34
+ fetchPluginManifest(id);
35
+ }
45
36
  }}
46
- open={showModal}
47
- value={devPlugin}
48
37
  />
49
-
50
- <Flexbox align={'center'} gap={8} horizontal>
51
- <Switch
52
- checked={
53
- // 如果在加载中,说明激活了
54
- pluginManifestLoading[id] || !hasPlugin ? false : plugins.includes(id)
55
- }
56
- loading={pluginManifestLoading[id]}
57
- onChange={(checked) => {
58
- toggleAgentPlugin(id);
59
- if (checked) {
60
- fetchPluginManifest(id);
61
- }
62
- }}
63
- />
64
- <Button
65
- onClick={() => {
66
- setModal(true);
67
- }}
68
- >
69
- {t('list.item.local.config')}
70
- </Button>
71
- </Flexbox>
72
- </>
38
+ </Flexbox>
73
39
  );
74
40
  });
75
41
 
@@ -8,6 +8,7 @@ import { Center, Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import { FORM_STYLE } from '@/const/layoutTokens';
10
10
  import PluginStore from '@/features/PluginStore';
11
+ import PluginTag from '@/features/PluginStore/PluginItem/PluginTag';
11
12
  import { pluginHelpers, useToolStore } from '@/store/tool';
12
13
  import { pluginSelectors } from '@/store/tool/selectors';
13
14
 
@@ -36,31 +37,24 @@ const AgentPlugin = memo(() => {
36
37
 
37
38
  // =========== Plugin List =========== //
38
39
 
39
- const list = installedPlugins.map(({ identifier, type, meta }) => {
40
+ const list = installedPlugins.map(({ identifier, type, meta, author }) => {
40
41
  const isCustomPlugin = type === 'customPlugin';
41
42
 
42
43
  return {
43
- avatar: <Avatar avatar={pluginHelpers.getPluginAvatar(meta)} />,
44
+ avatar: <Avatar avatar={pluginHelpers.getPluginAvatar(meta)} style={{ flex: 'none' }} />,
44
45
  children: isCustomPlugin ? (
45
46
  <LocalPluginItem id={identifier} />
46
47
  ) : (
47
48
  <PluginAction identifier={identifier} />
48
49
  ),
49
-
50
50
  desc: pluginHelpers.getPluginDesc(meta),
51
-
52
- label: isCustomPlugin ? (
51
+ label: (
53
52
  <Flexbox align={'center'} gap={8} horizontal>
54
53
  {pluginHelpers.getPluginTitle(meta)}
55
- <Tag bordered={false} color={'gold'}>
56
- {t('list.item.local.title', { ns: 'plugin' })}
57
- </Tag>
54
+ <PluginTag author={author} type={type} />
58
55
  </Flexbox>
59
- ) : (
60
- pluginHelpers.getPluginTitle(meta)
61
56
  ),
62
57
  minWidth: undefined,
63
- tag: identifier,
64
58
  };
65
59
  });
66
60
 
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_AGENT_META } from '@/const/meta';
2
2
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
3
- import { MetaData } from '@/types/meta';
4
3
  import { LobeAgentConfig } from '@/types/agent';
4
+ import { MetaData } from '@/types/meta';
5
5
 
6
6
  export interface State {
7
7
  autocompleteLoading: SessionLoadingState;
@@ -0,0 +1,44 @@
1
+ import { Empty, Table } from 'antd';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { useToolStore } from '@/store/tool';
8
+ import { pluginSelectors } from '@/store/tool/selectors';
9
+
10
+ const APIs = memo<{
11
+ id: string;
12
+ }>(({ id }) => {
13
+ const { t } = useTranslation('plugin');
14
+ const pluginManifest = useToolStore(pluginSelectors.getPluginManifestById(id), isEqual);
15
+
16
+ if (!pluginManifest?.api) return <Empty />;
17
+
18
+ return (
19
+ <Flexbox paddingBlock={16} width={'100%'}>
20
+ <Table
21
+ bordered
22
+ columns={[
23
+ {
24
+ dataIndex: 'name',
25
+ ellipsis: true,
26
+ render: (name: string) => <code>{name}</code>,
27
+ title: t('detailModal.info.name'),
28
+ },
29
+ {
30
+ dataIndex: 'description',
31
+ ellipsis: true,
32
+ title: t('detailModal.info.description'),
33
+ },
34
+ ]}
35
+ dataSource={pluginManifest.api}
36
+ pagination={false}
37
+ rowKey={'name'}
38
+ size={'small'}
39
+ />
40
+ </Flexbox>
41
+ );
42
+ });
43
+
44
+ export default APIs;
@@ -0,0 +1,59 @@
1
+ import { Avatar, Tag } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import isEqual from 'fast-deep-equal';
4
+ import { startCase } from 'lodash-es';
5
+ import { memo } from 'react';
6
+ import { Center } from 'react-layout-kit';
7
+
8
+ import { pluginHelpers, useToolStore } from '@/store/tool';
9
+ import { pluginSelectors } from '@/store/tool/selectors';
10
+
11
+ const useStyles = createStyles(({ css, token, stylish }) => ({
12
+ avatar: css`
13
+ flex: none;
14
+ `,
15
+ desc: css`
16
+ color: ${token.colorTextDescription};
17
+ text-align: center;
18
+ `,
19
+ markdown: stylish.markdownInChat,
20
+
21
+ title: css`
22
+ font-size: 20px;
23
+ font-weight: 600;
24
+ text-align: center;
25
+ `,
26
+ }));
27
+
28
+ const Meta = memo<{
29
+ id: string;
30
+ }>(({ id }) => {
31
+ const pluginMeta = useToolStore(pluginSelectors.getPluginMetaById(id), isEqual);
32
+ const { styles, theme } = useStyles();
33
+
34
+ return (
35
+ <Center gap={16}>
36
+ <Avatar
37
+ animation
38
+ avatar={pluginHelpers.getPluginAvatar(pluginMeta)}
39
+ background={theme.colorFillTertiary}
40
+ className={styles.avatar}
41
+ size={100}
42
+ />
43
+
44
+ <div className={styles.title}>{pluginHelpers.getPluginTitle(pluginMeta)}</div>
45
+ {(pluginMeta?.tags as string[])?.length > 0 && (
46
+ <Center gap={6} horizontal style={{ flexWrap: 'wrap' }}>
47
+ {(pluginHelpers.getPluginTags(pluginMeta) as string[]).map((tag: string, index) => (
48
+ <Tag key={index} style={{ margin: 0 }}>
49
+ {startCase(tag).trim()}
50
+ </Tag>
51
+ ))}
52
+ </Center>
53
+ )}
54
+ <div className={styles.desc}>{pluginHelpers.getPluginDesc(pluginMeta)}</div>
55
+ </Center>
56
+ );
57
+ });
58
+
59
+ export default Meta;
@@ -0,0 +1,79 @@
1
+ import { Modal, TabsNav } from '@lobehub/ui';
2
+ import { Divider, TabsProps } from 'antd';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Center } from 'react-layout-kit';
6
+ import useMergeState from 'use-merge-value';
7
+
8
+ import MobilePadding from '@/components/MobilePadding';
9
+ import PluginSettingsConfig from '@/features/PluginSettings';
10
+ import { pluginHelpers } from '@/store/tool';
11
+
12
+ import APIs from './APIs';
13
+ import Meta from './Meta';
14
+
15
+ interface PluginDetailModalProps {
16
+ id: string;
17
+ onClose: () => void;
18
+ onTabChange?: (key: string) => void;
19
+ open?: boolean;
20
+ schema: any;
21
+ tab?: string;
22
+ }
23
+
24
+ const PluginDetailModal = memo<PluginDetailModalProps>(
25
+ ({ schema, onClose, id, onTabChange, open, tab }) => {
26
+ const [tabKey, setTabKey] = useMergeState('info', {
27
+ onChange: onTabChange,
28
+ value: tab,
29
+ });
30
+ const { t } = useTranslation('plugin');
31
+
32
+ const hasSettings = pluginHelpers.isSettingSchemaNonEmpty(schema);
33
+
34
+ return (
35
+ <Modal
36
+ cancelText={t('cancel', { ns: 'common' })}
37
+ okText={t('ok', { ns: 'common' })}
38
+ onCancel={onClose}
39
+ onOk={() => {
40
+ onClose();
41
+ }}
42
+ open={open}
43
+ title={t('detailModal.title')}
44
+ width={650}
45
+ >
46
+ <MobilePadding>
47
+ <Center gap={8}>
48
+ <Meta id={id} />
49
+ <Divider style={{ marginBottom: 0, marginTop: 8 }} />
50
+ <TabsNav
51
+ activeKey={tabKey}
52
+ items={
53
+ [
54
+ {
55
+ key: 'info',
56
+ label: t('detailModal.tabs.info'),
57
+ },
58
+ hasSettings && {
59
+ key: 'settings',
60
+ label: t('detailModal.tabs.settings'),
61
+ },
62
+ ].filter(Boolean) as TabsProps['items']
63
+ }
64
+ onChange={setTabKey}
65
+ variant={'compact'}
66
+ />
67
+ {tabKey === 'settings' ? (
68
+ hasSettings && <PluginSettingsConfig id={id} schema={schema} />
69
+ ) : (
70
+ <APIs id={id} />
71
+ )}
72
+ </Center>
73
+ </MobilePadding>
74
+ </Modal>
75
+ );
76
+ },
77
+ );
78
+
79
+ export default PluginDetailModal;