@lobehub/chat 0.111.5 → 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 (148) hide show
  1. package/.env.example +1 -1
  2. package/.i18nrc.js +1 -0
  3. package/CHANGELOG.md +84 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +2 -2
  6. package/docs/Deployment/Environment-Variable.md +1 -1
  7. package/docs/Deployment/Environment-Variable.zh-CN.md +1 -1
  8. package/docs/Deployment/Upstream-Sync.md +1 -1
  9. package/docs/Deployment/Upstream-Sync.zh-CN.md +1 -1
  10. package/docs/Home.md +26 -26
  11. package/docs/Plugins/Plugin-Development.md +1 -1
  12. package/docs/Plugins/Plugin-Development.zh-CN.md +1 -1
  13. package/docs/_Sidebar.md +20 -20
  14. package/locales/en_US/chat.json +12 -12
  15. package/locales/en_US/common.json +13 -13
  16. package/locales/en_US/error.json +16 -14
  17. package/locales/en_US/market.json +2 -2
  18. package/locales/en_US/migration.json +4 -4
  19. package/locales/en_US/plugin.json +75 -38
  20. package/locales/en_US/setting.json +7 -7
  21. package/locales/fr_FR/chat.json +11 -11
  22. package/locales/fr_FR/common.json +14 -14
  23. package/locales/fr_FR/error.json +12 -10
  24. package/locales/fr_FR/plugin.json +42 -5
  25. package/locales/fr_FR/setting.json +12 -12
  26. package/locales/fr_FR/welcome.json +3 -3
  27. package/locales/ja_JP/chat.json +1 -1
  28. package/locales/ja_JP/common.json +3 -3
  29. package/locales/ja_JP/error.json +16 -14
  30. package/locales/ja_JP/migration.json +4 -4
  31. package/locales/ja_JP/plugin.json +70 -33
  32. package/locales/ja_JP/setting.json +1 -1
  33. package/locales/ko_KR/chat.json +1 -1
  34. package/locales/ko_KR/common.json +3 -3
  35. package/locales/ko_KR/error.json +16 -14
  36. package/locales/ko_KR/migration.json +4 -4
  37. package/locales/ko_KR/plugin.json +71 -34
  38. package/locales/ko_KR/setting.json +1 -1
  39. package/locales/ru_RU/chat.json +1 -1
  40. package/locales/ru_RU/common.json +3 -3
  41. package/locales/ru_RU/error.json +16 -14
  42. package/locales/ru_RU/market.json +2 -2
  43. package/locales/ru_RU/migration.json +4 -4
  44. package/locales/ru_RU/plugin.json +71 -34
  45. package/locales/ru_RU/setting.json +1 -1
  46. package/locales/zh_CN/chat.json +1 -1
  47. package/locales/zh_CN/common.json +1 -1
  48. package/locales/zh_CN/error.json +2 -0
  49. package/locales/zh_CN/plugin.json +42 -5
  50. package/locales/zh_CN/setting.json +1 -1
  51. package/locales/zh_TW/chat.json +1 -1
  52. package/locales/zh_TW/common.json +3 -3
  53. package/locales/zh_TW/error.json +16 -14
  54. package/locales/zh_TW/market.json +2 -2
  55. package/locales/zh_TW/migration.json +4 -4
  56. package/locales/zh_TW/plugin.json +98 -61
  57. package/locales/zh_TW/setting.json +1 -1
  58. package/package.json +3 -1
  59. package/scripts/docsWorkflow/index.ts +5 -2
  60. package/scripts/docsWorkflow/toc.ts +2 -2
  61. package/scripts/i18nWorkflow/genDiff.ts +7 -8
  62. package/scripts/i18nWorkflow/utils.ts +0 -14
  63. package/scripts/readmeWorkflow/syncPluginIndex.ts +9 -11
  64. package/src/app/api/plugins/route.ts +2 -4
  65. package/src/app/api/proxy/route.ts +10 -0
  66. package/src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx +3 -8
  67. package/src/app/chat/features/ChatInput/ActionBar/Tools/index.tsx +3 -3
  68. package/src/app/chat/features/Conversation/ChatList/Error/index.tsx +6 -0
  69. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +1 -1
  70. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +7 -5
  71. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/index.tsx +2 -2
  72. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/DefaultType/index.tsx +6 -4
  73. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/index.tsx +2 -2
  74. package/src/app/chat/features/Migration/ExportConfigButton.tsx +1 -1
  75. package/src/app/market/features/AgentDetailContent/AgentInfo/index.tsx +1 -0
  76. package/src/app/market/features/AgentDetailContent/AgentInfo/style.ts +1 -9
  77. package/src/app/settings/common/Common.tsx +5 -7
  78. package/src/app/settings/features/UpgradeAlert.tsx +2 -2
  79. package/src/components/FileList/FileItem.tsx +4 -2
  80. package/src/components/HotKeys/index.tsx +5 -4
  81. package/src/const/plugin.ts +1 -0
  82. package/src/const/url.ts +1 -0
  83. package/src/const/version.ts +0 -3
  84. package/src/features/AgentSetting/AgentPlugin/AddPluginButton.tsx +2 -2
  85. package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +19 -53
  86. package/src/features/AgentSetting/AgentPlugin/index.tsx +5 -11
  87. package/src/features/AgentSetting/store/initialState.ts +1 -1
  88. package/src/features/PluginDetailModal/APIs.tsx +44 -0
  89. package/src/features/PluginDetailModal/Meta.tsx +59 -0
  90. package/src/features/PluginDetailModal/index.tsx +79 -0
  91. package/src/features/PluginDevModal/PluginPreview.tsx +8 -10
  92. package/src/features/PluginDevModal/UrlManifestForm.tsx +131 -0
  93. package/src/features/PluginDevModal/index.tsx +31 -32
  94. package/src/features/PluginSettings/index.tsx +1 -1
  95. package/src/features/PluginStore/AddPluginButton.tsx +47 -0
  96. package/src/features/PluginStore/InstalledPluginList.tsx +54 -0
  97. package/src/features/PluginStore/OnlineList.tsx +95 -0
  98. package/src/features/PluginStore/PluginItem/Action.tsx +118 -0
  99. package/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx +50 -0
  100. package/src/features/PluginStore/PluginItem/PluginTag.tsx +58 -0
  101. package/src/features/PluginStore/PluginItem/index.tsx +73 -0
  102. package/src/features/PluginStore/index.tsx +20 -92
  103. package/src/hooks/usePlatform.ts +15 -0
  104. package/src/locales/default/chat.ts +1 -1
  105. package/src/locales/default/common.ts +1 -1
  106. package/src/locales/default/error.ts +2 -0
  107. package/src/locales/default/plugin.ts +41 -4
  108. package/src/locales/default/setting.ts +1 -1
  109. package/src/services/__tests__/__snapshots__/plugin.test.ts.snap +154 -0
  110. package/src/services/__tests__/chat.test.ts +1 -1
  111. package/src/services/__tests__/global.test.ts +57 -0
  112. package/src/services/__tests__/openai/OpenAPI_V3.json +245 -0
  113. package/src/services/__tests__/openai/plugin.json +22 -0
  114. package/src/services/__tests__/plugin.test.ts +90 -1
  115. package/src/services/__tests__/share.test.ts +86 -0
  116. package/src/services/_url.ts +2 -0
  117. package/src/services/global.ts +15 -0
  118. package/src/services/plugin.ts +99 -15
  119. package/src/services/share.ts +34 -0
  120. package/src/store/chat/actions/enhance.test.ts +15 -15
  121. package/src/store/chat/actions/message.test.ts +3 -2
  122. package/src/store/chat/actions/plugin.test.ts +281 -0
  123. package/src/store/chat/actions/plugin.ts +22 -3
  124. package/src/store/chat/actions/share.ts +2 -2
  125. package/src/store/chat/actions/topic.test.ts +12 -4
  126. package/src/store/global/slices/common.ts +2 -2
  127. package/src/store/session/selectors.ts +1 -1
  128. package/src/store/session/slices/agent/selectors.ts +1 -1
  129. package/src/store/tool/helpers.ts +8 -1
  130. package/src/store/tool/slices/customPlugin/action.ts +4 -1
  131. package/src/store/tool/slices/plugin/selectors.test.ts +22 -3
  132. package/src/store/tool/slices/plugin/selectors.ts +24 -5
  133. package/src/store/tool/slices/store/selectors.test.ts +18 -1
  134. package/src/store/tool/slices/store/selectors.ts +14 -3
  135. package/src/styles/antdOverride.ts +7 -0
  136. package/src/types/openai/chat.ts +0 -1
  137. package/src/types/openai/plugin.ts +57 -0
  138. package/src/types/tool/index.ts +1 -2
  139. package/src/types/tool/plugin.ts +13 -1
  140. package/src/types/tool/tool.ts +1 -0
  141. package/src/types/translate.ts +0 -1
  142. package/src/utils/platform.ts +8 -5
  143. package/src/features/PluginDevModal/UrlModeForm.tsx +0 -98
  144. package/src/features/PluginSettingsModal/index.tsx +0 -59
  145. package/src/features/PluginStore/PluginItem.tsx +0 -82
  146. package/src/features/PluginStore/PluginSettings.tsx +0 -40
  147. package/src/services/latestVersion.ts +0 -11
  148. package/src/services/shareGPT.ts +0 -28
@@ -1,5 +1,5 @@
1
1
  import { Alert, Icon, Modal, Tooltip } from '@lobehub/ui';
2
- import { App, Button, Form, Popconfirm, Segmented } from 'antd';
2
+ import { App, Button, Divider, Form, Popconfirm, Segmented } from 'antd';
3
3
  import { useResponsive } from 'antd-style';
4
4
  import { MoveUpRight } from 'lucide-react';
5
5
  import { memo, useEffect, useState } from 'react';
@@ -10,9 +10,8 @@ import MobilePadding from '@/components/MobilePadding';
10
10
  import { WIKI_PLUGIN_GUIDE } from '@/const/url';
11
11
  import { LobeToolCustomPlugin } from '@/types/tool/plugin';
12
12
 
13
- import LocalForm from './LocalForm';
14
13
  import PluginPreview from './PluginPreview';
15
- import UrlModeForm from './UrlModeForm';
14
+ import UrlManifestForm from './UrlManifestForm';
16
15
 
17
16
  interface DevModalProps {
18
17
  mode?: 'edit' | 'create';
@@ -108,7 +107,7 @@ const DevModal = memo<DevModalProps>(
108
107
  form.submit();
109
108
  }}
110
109
  open={open}
111
- title={t('dev.title')}
110
+ title={t(isEditMode ? 'dev.title.edit' : 'dev.title.create')}
112
111
  >
113
112
  <Flexbox
114
113
  gap={mobile ? 0 : 16}
@@ -135,35 +134,35 @@ const DevModal = memo<DevModalProps>(
135
134
  showIcon
136
135
  type={'info'}
137
136
  />
137
+ <Segmented
138
+ block
139
+ onChange={(e) => {
140
+ setConfigMode(e as any);
141
+ }}
142
+ options={[
143
+ {
144
+ label: t('dev.manifest.mode.url'),
145
+ value: 'url',
146
+ },
147
+ {
148
+ disabled: true,
149
+ label: (
150
+ <Tooltip title={t('dev.manifest.mode.local-tooltip')}>
151
+ {t('dev.manifest.mode.local')}
152
+ </Tooltip>
153
+ ),
154
+ value: 'local',
155
+ },
156
+ ]}
157
+ />
158
+ <Flexbox>
159
+ {configMode === 'url' ? (
160
+ <UrlManifestForm form={form} isEditMode={mode === 'edit'} />
161
+ ) : null}
162
+ <Divider />
163
+ <PluginPreview form={form} />
164
+ </Flexbox>
138
165
  </MobilePadding>
139
- <PluginPreview form={form} />
140
- <Segmented
141
- block
142
- onChange={(e) => {
143
- setConfigMode(e as any);
144
- }}
145
- options={[
146
- {
147
- label: t('dev.manifest.mode.url'),
148
- value: 'url',
149
- },
150
- {
151
- disabled: true,
152
- label: (
153
- <Tooltip title={t('dev.manifest.mode.local-tooltip')}>
154
- {t('dev.manifest.mode.local')}
155
- </Tooltip>
156
- ),
157
- value: 'local',
158
- },
159
- ]}
160
- />
161
- {configMode === 'url' ? (
162
- <UrlModeForm form={form} isEditMode={mode === 'edit'} />
163
- ) : (
164
- <LocalForm form={form} mode={mode} />
165
- )}
166
- {/*<MetaForm form={form} mode={mode} />*/}
167
166
  </Flexbox>
168
167
  </Modal>
169
168
  </Form.Provider>
@@ -53,7 +53,7 @@ const PluginSettingsConfig = memo<PluginSettingsConfigProps>(({ schema, id }) =>
53
53
  const items = transformPluginSettings(schema);
54
54
 
55
55
  return (
56
- <Form form={form} layout={'vertical'}>
56
+ <Form form={form} layout={'vertical'} style={{ width: '100%' }}>
57
57
  {items.map((item) => (
58
58
  <Form.Item
59
59
  desc={item.desc && <Markdown className={styles.markdown}>{item.desc as string}</Markdown>}
@@ -0,0 +1,47 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { Button } from 'antd';
3
+ import { PackagePlus } from 'lucide-react';
4
+ import { forwardRef, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import DevModal from '@/features/PluginDevModal';
8
+ import { useToolStore } from '@/store/tool';
9
+
10
+ const AddPluginButton = forwardRef<HTMLButtonElement>((props, ref) => {
11
+ const { t } = useTranslation('setting');
12
+ const [showModal, setModal] = useState(false);
13
+
14
+ const [installCustomPlugin, updateNewDevPlugin] = useToolStore((s) => [
15
+ s.installCustomPlugin,
16
+ s.updateNewCustomPlugin,
17
+ ]);
18
+
19
+ return (
20
+ <div
21
+ onClick={(e) => {
22
+ e.stopPropagation();
23
+ }}
24
+ >
25
+ <DevModal
26
+ onOpenChange={setModal}
27
+ onSave={async (devPlugin) => {
28
+ await installCustomPlugin(devPlugin);
29
+ // toggleAgentPlugin(devPlugin.identifier);
30
+ }}
31
+ onValueChange={updateNewDevPlugin}
32
+ open={showModal}
33
+ />
34
+ <Button
35
+ icon={<Icon icon={PackagePlus} />}
36
+ onClick={() => {
37
+ setModal(true);
38
+ }}
39
+ ref={ref}
40
+ >
41
+ {t('plugin.addTooltip')}
42
+ </Button>
43
+ </div>
44
+ );
45
+ });
46
+
47
+ export default AddPluginButton;
@@ -0,0 +1,54 @@
1
+ import { SearchBar } from '@lobehub/ui';
2
+ import { useResponsive } from 'antd-style';
3
+ import isEqual from 'fast-deep-equal';
4
+ import { memo, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { useToolStore } from '@/store/tool';
9
+ import { pluginSelectors } from '@/store/tool/selectors';
10
+
11
+ import AddPluginButton from './AddPluginButton';
12
+ import PluginItem from './PluginItem';
13
+
14
+ export const InstalledPluginList = memo(() => {
15
+ const { t } = useTranslation('plugin');
16
+ const [keywords, setKeywords] = useState<string>();
17
+ const { mobile } = useResponsive();
18
+ const installedPlugins = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
19
+
20
+ return (
21
+ <Flexbox gap={16}>
22
+ <Flexbox align={'center'} gap={16} horizontal justify={'space-between'}>
23
+ <Flexbox flex={1}>
24
+ <SearchBar
25
+ allowClear
26
+ onChange={(e) => setKeywords(e.target.value)}
27
+ placeholder={t('store.placeholder')}
28
+ type={mobile ? 'block' : 'ghost'}
29
+ value={keywords}
30
+ />
31
+ </Flexbox>
32
+ <Flexbox gap={8} horizontal>
33
+ <AddPluginButton />
34
+ </Flexbox>
35
+ </Flexbox>
36
+
37
+ <Flexbox gap={24}>
38
+ {installedPlugins
39
+ .filter((item) =>
40
+ [item.meta?.title, item.meta?.description, item.author, ...(item.meta?.tags || [])]
41
+ .filter(Boolean)
42
+ .join('')
43
+ .toLowerCase()
44
+ .includes((keywords || '')?.toLowerCase()),
45
+ )
46
+ .map((i) => (
47
+ <PluginItem {...i} key={i.identifier} />
48
+ ))}
49
+ </Flexbox>
50
+ </Flexbox>
51
+ );
52
+ });
53
+
54
+ export default InstalledPluginList;
@@ -0,0 +1,95 @@
1
+ import { Icon, SearchBar } from '@lobehub/ui';
2
+ import { Button, Empty } from 'antd';
3
+ import { useResponsive } from 'antd-style';
4
+ import isEqual from 'fast-deep-equal';
5
+ import { ServerCrash } from 'lucide-react';
6
+ import { memo, useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Center, Flexbox } from 'react-layout-kit';
9
+
10
+ import AddPluginButton from '@/features/PluginStore/AddPluginButton';
11
+ import { useToolStore } from '@/store/tool';
12
+ import { pluginSelectors, pluginStoreSelectors } from '@/store/tool/selectors';
13
+
14
+ import Loading from './Loading';
15
+ import PluginItem from './PluginItem';
16
+
17
+ export const OnlineList = memo(() => {
18
+ const { t } = useTranslation('plugin');
19
+ const [keywords, setKeywords] = useState<string>();
20
+ const { mobile } = useResponsive();
21
+ const pluginStoreList = useToolStore((s) => {
22
+ const custom = pluginSelectors.installedCustomPluginMetaList(s);
23
+ const store = pluginStoreSelectors.onlinePluginStore(s);
24
+
25
+ return [...custom, ...store];
26
+ }, isEqual);
27
+ const storePluginIds = useToolStore(
28
+ (s) => pluginStoreSelectors.onlinePluginStore(s).map((s) => s.identifier),
29
+ isEqual,
30
+ );
31
+
32
+ const [useFetchPluginList, installPlugins] = useToolStore((s) => [
33
+ s.useFetchPluginStore,
34
+ s.installPlugins,
35
+ ]);
36
+
37
+ const { isLoading, error } = useFetchPluginList();
38
+
39
+ const isEmpty = pluginStoreList.length === 0;
40
+
41
+ return (
42
+ <Flexbox gap={16}>
43
+ <Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
44
+ <Flexbox flex={1}>
45
+ <SearchBar
46
+ allowClear
47
+ onChange={(e) => setKeywords(e.target.value)}
48
+ placeholder={t('store.placeholder')}
49
+ type={mobile ? 'block' : 'ghost'}
50
+ value={keywords}
51
+ />
52
+ </Flexbox>
53
+ <AddPluginButton />
54
+ <Button
55
+ onClick={() => {
56
+ installPlugins(storePluginIds);
57
+ }}
58
+ >
59
+ {t('store.installAllPlugins')}
60
+ </Button>
61
+ </Flexbox>
62
+
63
+ {isLoading ? (
64
+ <Loading />
65
+ ) : isEmpty ? (
66
+ <Center gap={12} padding={40}>
67
+ {error ? (
68
+ <>
69
+ <Icon icon={ServerCrash} size={{ fontSize: 80 }} />
70
+ {t('store.networkError')}
71
+ </>
72
+ ) : (
73
+ <Empty description={t('store.empty')} image={Empty.PRESENTED_IMAGE_SIMPLE} />
74
+ )}
75
+ </Center>
76
+ ) : (
77
+ <Flexbox gap={24}>
78
+ {pluginStoreList
79
+ .filter((item) =>
80
+ [item.meta?.title, item.meta?.description, item.author, ...(item.meta?.tags || [])]
81
+ .filter(Boolean)
82
+ .join('')
83
+ .toLowerCase()
84
+ .includes((keywords || '')?.toLowerCase()),
85
+ )
86
+ .map((item) => (
87
+ <PluginItem key={item.identifier} {...item} />
88
+ ))}
89
+ </Flexbox>
90
+ )}
91
+ </Flexbox>
92
+ );
93
+ });
94
+
95
+ export default OnlineList;
@@ -0,0 +1,118 @@
1
+ import { ActionIcon, Icon } from '@lobehub/ui';
2
+ import { Button, Dropdown, Popconfirm } from 'antd';
3
+ import { useResponsive } from 'antd-style';
4
+ import { InfoIcon, MoreVerticalIcon, Settings, Trash2 } from 'lucide-react';
5
+ import { memo, useState } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import PluginDetailModal from '@/features/PluginDetailModal';
10
+ import { pluginHelpers, useToolStore } from '@/store/tool';
11
+ import { pluginSelectors, pluginStoreSelectors } from '@/store/tool/selectors';
12
+ import { LobeToolType } from '@/types/tool/tool';
13
+
14
+ import EditCustomPlugin from './EditCustomPlugin';
15
+
16
+ interface ActionsProps {
17
+ identifier: string;
18
+ type: LobeToolType;
19
+ }
20
+
21
+ const Actions = memo<ActionsProps>(({ identifier, type }) => {
22
+ const [installed, installing, installPlugin, unInstallPlugin] = useToolStore((s) => [
23
+ pluginSelectors.isPluginInstalled(identifier)(s),
24
+ pluginStoreSelectors.isPluginInstallLoading(identifier)(s),
25
+ s.installPlugin,
26
+ s.uninstallPlugin,
27
+ ]);
28
+ const { mobile } = useResponsive();
29
+ const isCustomPlugin = type === 'customPlugin';
30
+ const { t } = useTranslation('plugin');
31
+ const [open, setOpen] = useState(false);
32
+ const plugin = useToolStore(pluginSelectors.getPluginManifestById(identifier));
33
+
34
+ const [tab, setTab] = useState('info');
35
+ const hasSettings = pluginHelpers.isSettingSchemaNonEmpty(plugin?.settings);
36
+
37
+ return (
38
+ <>
39
+ <Flexbox align={'center'} horizontal>
40
+ {installed ? (
41
+ <>
42
+ {isCustomPlugin && <EditCustomPlugin identifier={identifier} />}
43
+ {hasSettings && (
44
+ <ActionIcon
45
+ icon={Settings}
46
+ onClick={() => {
47
+ setOpen(true);
48
+ setTab('settings');
49
+ }}
50
+ title={t('store.actions.settings')}
51
+ />
52
+ )}
53
+ <Dropdown
54
+ menu={{
55
+ items: [
56
+ {
57
+ icon: <Icon icon={InfoIcon} />,
58
+ key: 'detail',
59
+ label: t('store.actions.detail'),
60
+ onClick: () => {
61
+ setOpen(true);
62
+ setTab('info');
63
+ },
64
+ },
65
+ {
66
+ danger: true,
67
+ icon: <Icon icon={Trash2} />,
68
+ key: 'uninstall',
69
+ label: (
70
+ <Popconfirm
71
+ arrow={false}
72
+ okButtonProps={{ danger: true }}
73
+ onConfirm={() => {
74
+ unInstallPlugin(identifier);
75
+ }}
76
+ placement={'topRight'}
77
+ title={t('store.actions.confirmUninstall')}
78
+ >
79
+ {t('store.actions.uninstall')}
80
+ </Popconfirm>
81
+ ),
82
+ },
83
+ ],
84
+ }}
85
+ placement="bottomRight"
86
+ trigger={['click']}
87
+ >
88
+ <ActionIcon icon={MoreVerticalIcon} loading={installing} />
89
+ </Dropdown>
90
+ </>
91
+ ) : (
92
+ <Button
93
+ loading={installing}
94
+ onClick={() => {
95
+ installPlugin(identifier);
96
+ }}
97
+ size={mobile ? 'small' : undefined}
98
+ type={'primary'}
99
+ >
100
+ {t('store.actions.install')}
101
+ </Button>
102
+ )}
103
+ </Flexbox>
104
+ <PluginDetailModal
105
+ id={identifier}
106
+ onClose={() => {
107
+ setOpen(false);
108
+ }}
109
+ onTabChange={setTab}
110
+ open={open}
111
+ schema={plugin?.settings}
112
+ tab={tab}
113
+ />
114
+ </>
115
+ );
116
+ });
117
+
118
+ export default Actions;
@@ -0,0 +1,50 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { PackageSearch } from 'lucide-react';
4
+ import { memo, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import DevModal from '@/features/PluginDevModal';
8
+ import { useToolStore } from '@/store/tool';
9
+ import { pluginSelectors } from '@/store/tool/slices/plugin/selectors';
10
+
11
+ const EditCustomPlugin = memo<{ identifier: string }>(({ identifier }) => {
12
+ const { t } = useTranslation('plugin');
13
+ const [showModal, setModal] = useState(false);
14
+
15
+ const [installCustomPlugin, updateNewDevPlugin] = useToolStore((s) => [
16
+ s.installCustomPlugin,
17
+ s.updateNewCustomPlugin,
18
+ ]);
19
+
20
+ const customPlugin = useToolStore(pluginSelectors.getCustomPluginById(identifier), isEqual);
21
+
22
+ return (
23
+ <div
24
+ onClick={(e) => {
25
+ e.stopPropagation();
26
+ }}
27
+ >
28
+ <DevModal
29
+ mode={'edit'}
30
+ onOpenChange={setModal}
31
+ onSave={async (devPlugin) => {
32
+ await installCustomPlugin(devPlugin);
33
+ // toggleAgentPlugin(devPlugin.identifier);
34
+ }}
35
+ onValueChange={updateNewDevPlugin}
36
+ open={showModal}
37
+ value={customPlugin}
38
+ />
39
+ <ActionIcon
40
+ icon={PackageSearch}
41
+ onClick={() => {
42
+ setModal(true);
43
+ }}
44
+ title={t('store.actions.manifest')}
45
+ />
46
+ </div>
47
+ );
48
+ });
49
+
50
+ export default EditCustomPlugin;
@@ -0,0 +1,58 @@
1
+ import { Icon, Tag } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { BadgeCheck, CircleUser, Package } from 'lucide-react';
4
+ import { rgba } from 'polished';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ import { InstallPluginMeta } from '@/types/tool/plugin';
9
+
10
+ const useStyles = createStyles(({ css, token }) => ({
11
+ community: css`
12
+ color: ${rgba(token.colorInfo, 0.75)};
13
+ background: ${token.colorInfoBg};
14
+
15
+ &:hover {
16
+ color: ${token.colorInfo};
17
+ }
18
+ `,
19
+ custom: css`
20
+ color: ${rgba(token.colorWarning, 0.75)};
21
+ background: ${token.colorWarningBg};
22
+
23
+ &:hover {
24
+ color: ${token.colorWarning};
25
+ }
26
+ `,
27
+ official: css`
28
+ color: ${rgba(token.colorSuccess, 0.75)};
29
+ background: ${token.colorSuccessBg};
30
+
31
+ &:hover {
32
+ color: ${token.colorSuccess};
33
+ }
34
+ `,
35
+ }));
36
+
37
+ interface PluginTagProps extends Pick<InstallPluginMeta, 'author' | 'type'> {
38
+ showIcon?: boolean;
39
+ showText?: boolean;
40
+ }
41
+
42
+ const PluginTag = memo<PluginTagProps>(({ showIcon = true, author, type, showText = true }) => {
43
+ const { t } = useTranslation('plugin');
44
+ const { styles, cx } = useStyles();
45
+ const isCustom = type === 'customPlugin';
46
+ const isOfficial = author === 'LobeHub';
47
+
48
+ return (
49
+ <Tag
50
+ className={cx(isCustom ? styles.custom : isOfficial ? styles.official : styles.community)}
51
+ icon={showIcon && <Icon icon={isCustom ? Package : isOfficial ? BadgeCheck : CircleUser} />}
52
+ >
53
+ {showText && (author || t(isCustom ? 'store.customPlugin' : 'store.communityPlugin'))}
54
+ </Tag>
55
+ );
56
+ });
57
+
58
+ export default PluginTag;
@@ -0,0 +1,73 @@
1
+ import { Avatar, Tooltip } from '@lobehub/ui';
2
+ import { Typography } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import Link from 'next/link';
5
+ import { memo } from 'react';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import PluginTag from '@/features/PluginStore/PluginItem/PluginTag';
9
+ import { InstallPluginMeta } from '@/types/tool/plugin';
10
+
11
+ import Actions from './Action';
12
+
13
+ const { Paragraph } = Typography;
14
+
15
+ const useStyles = createStyles(({ css, token }) => ({
16
+ desc: css`
17
+ margin: 0 !important;
18
+ font-size: 12px;
19
+ color: ${token.colorTextDescription};
20
+ `,
21
+ link: css`
22
+ color: ${token.colorText};
23
+ `,
24
+ title: css`
25
+ font-size: 14px;
26
+ font-weight: bold;
27
+ line-height: 2;
28
+ `,
29
+ }));
30
+
31
+ const PluginItem = memo<InstallPluginMeta>(({ identifier, homepage, author, type, meta = {} }) => {
32
+ const { styles } = useStyles();
33
+
34
+ return (
35
+ <Flexbox
36
+ align={'center'}
37
+ gap={8}
38
+ horizontal
39
+ justify={'space-between'}
40
+ style={{ position: 'relative' }}
41
+ >
42
+ <Flexbox
43
+ align={'center'}
44
+ flex={1}
45
+ gap={8}
46
+ horizontal
47
+ style={{ overflow: 'hidden', position: 'relative' }}
48
+ >
49
+ <Avatar avatar={meta.avatar} style={{ flex: 'none', overflow: 'hidden' }} />
50
+ <Flexbox flex={1} style={{ overflow: 'hidden', position: 'relative' }}>
51
+ <Flexbox align={'center'} gap={8} horizontal>
52
+ <Tooltip title={identifier}>
53
+ {homepage ? (
54
+ <Link className={styles.link} href={homepage} target={'_blank'}>
55
+ <div className={styles.title}>{meta.title}</div>
56
+ </Link>
57
+ ) : (
58
+ <div className={styles.title}>{meta.title}</div>
59
+ )}
60
+ </Tooltip>
61
+ <PluginTag author={author} type={type} />
62
+ </Flexbox>
63
+ <Paragraph className={styles.desc} ellipsis={{ rows: 1 }}>
64
+ {meta.description}
65
+ </Paragraph>
66
+ </Flexbox>
67
+ </Flexbox>
68
+ <Actions identifier={identifier} type={type} />
69
+ </Flexbox>
70
+ );
71
+ });
72
+
73
+ export default PluginItem;