@lobehub/chat 1.84.9 → 1.84.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/apps/desktop/electron.vite.config.ts +3 -0
  3. package/changelog/v1.json +18 -0
  4. package/locales/ar/components.json +2 -1
  5. package/locales/ar/models.json +63 -0
  6. package/locales/bg-BG/components.json +2 -1
  7. package/locales/bg-BG/models.json +63 -0
  8. package/locales/de-DE/components.json +2 -1
  9. package/locales/de-DE/models.json +63 -0
  10. package/locales/en-US/components.json +2 -1
  11. package/locales/en-US/models.json +63 -0
  12. package/locales/es-ES/components.json +2 -1
  13. package/locales/es-ES/models.json +63 -0
  14. package/locales/fa-IR/components.json +2 -1
  15. package/locales/fa-IR/models.json +63 -0
  16. package/locales/fr-FR/components.json +2 -1
  17. package/locales/fr-FR/models.json +63 -0
  18. package/locales/it-IT/components.json +2 -1
  19. package/locales/it-IT/models.json +63 -0
  20. package/locales/ja-JP/components.json +2 -1
  21. package/locales/ja-JP/models.json +63 -0
  22. package/locales/ko-KR/components.json +2 -1
  23. package/locales/ko-KR/models.json +63 -0
  24. package/locales/nl-NL/components.json +2 -1
  25. package/locales/nl-NL/models.json +63 -0
  26. package/locales/pl-PL/components.json +2 -1
  27. package/locales/pl-PL/models.json +63 -0
  28. package/locales/pt-BR/components.json +2 -1
  29. package/locales/pt-BR/models.json +63 -0
  30. package/locales/ru-RU/components.json +2 -1
  31. package/locales/ru-RU/models.json +63 -0
  32. package/locales/tr-TR/components.json +2 -1
  33. package/locales/tr-TR/models.json +63 -0
  34. package/locales/vi-VN/components.json +2 -1
  35. package/locales/vi-VN/models.json +63 -0
  36. package/locales/zh-CN/components.json +2 -1
  37. package/locales/zh-CN/models.json +63 -0
  38. package/locales/zh-TW/components.json +2 -1
  39. package/locales/zh-TW/models.json +63 -0
  40. package/package.json +2 -2
  41. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +1 -1
  42. package/src/app/[variants]/(main)/chat/(workspace)/@topic/features/Header.tsx +5 -1
  43. package/src/app/[variants]/(main)/chat/settings/page.tsx +1 -0
  44. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +4 -1
  45. package/src/app/[variants]/(main)/settings/provider/(detail)/ollama/CheckError.tsx +4 -2
  46. package/src/app/[variants]/(main)/settings/provider/(detail)/ollama/Container.tsx +2 -2
  47. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/index.tsx +1 -0
  48. package/src/components/FileParsingStatus/index.tsx +1 -7
  49. package/src/components/ModelSelect/index.tsx +2 -2
  50. package/src/config/aiModels/siliconcloud.ts +89 -7
  51. package/src/config/modelProviders/google.ts +16 -0
  52. package/src/features/ChatInput/ActionBar/{Clear.tsx → Clear/index.tsx} +3 -2
  53. package/src/features/ChatInput/ActionBar/History/Controls.tsx +72 -0
  54. package/src/features/ChatInput/ActionBar/History/index.tsx +46 -0
  55. package/src/features/ChatInput/ActionBar/Knowledge/index.tsx +31 -25
  56. package/src/features/ChatInput/ActionBar/Knowledge/{Dropdown.tsx → useControls.tsx} +20 -40
  57. package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +8 -1
  58. package/src/features/ChatInput/ActionBar/Model/index.tsx +27 -19
  59. package/src/features/ChatInput/ActionBar/Params/{ParamsControls.tsx → Controls.tsx} +9 -9
  60. package/src/features/ChatInput/ActionBar/Params/index.tsx +17 -20
  61. package/src/features/ChatInput/{STT → ActionBar/STT}/common.tsx +1 -0
  62. package/src/features/ChatInput/ActionBar/Search/{SwitchPanel.tsx → Controls.tsx} +12 -11
  63. package/src/features/ChatInput/ActionBar/Search/index.tsx +20 -25
  64. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +1 -1
  65. package/src/features/ChatInput/ActionBar/Tools/ToolItem.tsx +15 -6
  66. package/src/features/ChatInput/ActionBar/Tools/index.tsx +26 -18
  67. package/src/features/ChatInput/ActionBar/Tools/{Dropdown.tsx → useControls.tsx} +38 -63
  68. package/src/features/ChatInput/ActionBar/Upload/ServerMode.tsx +10 -11
  69. package/src/features/ChatInput/ActionBar/components/Action.tsx +90 -0
  70. package/src/features/ChatInput/{components → ActionBar/components}/ActionDropdown.tsx +4 -4
  71. package/src/features/ChatInput/{components → ActionBar/components}/ActionPopover.tsx +5 -4
  72. package/src/features/ChatInput/ActionBar/{Knowledge/ListItem.tsx → components/CheckbokWithLoading.tsx} +14 -12
  73. package/src/features/ChatInput/ActionBar/config.ts +1 -1
  74. package/src/features/Conversation/Actions/Error.tsx +10 -2
  75. package/src/features/Conversation/Error/OllamaBizError/index.tsx +2 -2
  76. package/src/features/Conversation/Error/index.tsx +3 -10
  77. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Loading.tsx +1 -1
  78. package/src/features/ModelSwitchPanel/index.tsx +18 -5
  79. package/src/features/{Conversation/Error/OllamaDesktopSetupGuide/index.tsx → OllamaSetupGuide/Desktop.tsx} +25 -20
  80. package/src/features/OllamaSetupGuide/index.tsx +17 -0
  81. package/src/features/ShareModal/ShareImage/ChatList/index.tsx +1 -1
  82. package/src/features/ShareModal/ShareImage/Preview.tsx +2 -2
  83. package/src/features/ShareModal/ShareImage/index.tsx +8 -6
  84. package/src/hooks/useImgToClipboard.ts +4 -1
  85. package/src/layout/GlobalProvider/Locale.tsx +0 -8
  86. package/src/libs/agent-runtime/siliconcloud/index.ts +17 -1
  87. package/src/locales/default/components.ts +1 -0
  88. package/src/utils/server/auth.ts +6 -0
  89. package/src/features/ChatInput/ActionBar/History.tsx +0 -78
  90. package/src/features/Conversation/Error/OllamaBizError/SetupGuide.tsx +0 -14
  91. /package/src/features/ChatInput/{STT → ActionBar/STT}/browser.tsx +0 -0
  92. /package/src/features/ChatInput/{STT → ActionBar/STT}/index.tsx +0 -0
  93. /package/src/features/ChatInput/{STT → ActionBar/STT}/openai.tsx +0 -0
@@ -1,33 +1,30 @@
1
- import { ActionIcon } from '@lobehub/ui';
2
1
  import { SlidersHorizontal } from 'lucide-react';
3
2
  import { memo, useState } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
5
4
 
6
- import ActionPopover from '../../components/ActionPopover';
7
- import ParamsControls from './ParamsControls';
5
+ import { useAgentStore } from '@/store/agent';
6
+ import { agentSelectors } from '@/store/agent/slices/chat';
7
+
8
+ import Action from '../components/Action';
9
+ import Controls from './Controls';
8
10
 
9
11
  const Params = memo(() => {
12
+ const [isLoading] = useAgentStore((s) => [agentSelectors.isAgentConfigLoading(s)]);
13
+ const [updating, setUpdating] = useState(false);
10
14
  const { t } = useTranslation('setting');
11
- const [popoverOpen, setPopoverOpen] = useState(false);
12
- const [isUpdating, setUpdating] = useState(false);
15
+
16
+ if (isLoading) return <Action disabled icon={SlidersHorizontal} />;
13
17
 
14
18
  return (
15
- <ActionPopover
16
- content={<ParamsControls setUpdating={setUpdating} />}
17
- loading={isUpdating}
18
- minWidth={400}
19
- onOpenChange={setPopoverOpen}
20
- open={popoverOpen}
19
+ <Action
20
+ icon={SlidersHorizontal}
21
+ loading={updating}
22
+ popover={{
23
+ content: <Controls setUpdating={setUpdating} updating={updating} />,
24
+ }}
25
+ showTooltip={false}
21
26
  title={t('settingModel.params.title')}
22
- >
23
- <ActionIcon
24
- icon={SlidersHorizontal}
25
- title={t('settingModel.params.title')}
26
- tooltipProps={{
27
- placement: 'bottom',
28
- }}
29
- />
30
- </ActionPopover>
27
+ />
31
28
  );
32
29
  });
33
30
 
@@ -116,6 +116,7 @@ const CommonSTT = memo<{
116
116
  tooltipProps={{
117
117
  placement: 'bottom',
118
118
  }}
119
+ variant={mobile ? 'outlined' : 'borderless'}
119
120
  />
120
121
  </Dropdown>
121
122
  );
@@ -29,7 +29,7 @@ const useStyles = createStyles(({ css, token }) => ({
29
29
  color: ${token.colorTextDescription};
30
30
  `,
31
31
  icon: css`
32
- border: 1px solid ${token.colorBorderSecondary};
32
+ border: 1px solid ${token.colorFillTertiary};
33
33
  border-radius: ${token.borderRadius}px;
34
34
  background: ${token.colorBgElevated};
35
35
  `,
@@ -59,11 +59,11 @@ interface NetworkOption {
59
59
  disable?: boolean;
60
60
  icon: LucideIcon;
61
61
  label: string;
62
- setLoading?: (loading: boolean) => void;
62
+ setUpdating?: (loading: boolean) => void;
63
63
  value: SearchMode;
64
64
  }
65
65
 
66
- const Item = memo<NetworkOption>(({ value, description, icon, label, setLoading }) => {
66
+ const Item = memo<NetworkOption>(({ value, description, icon, label, setUpdating }) => {
67
67
  const { cx, styles } = useStyles();
68
68
  const [mode, updateAgentChatConfig] = useAgentStore((s) => [
69
69
  agentChatConfigSelectors.agentSearchMode(s),
@@ -78,9 +78,9 @@ const Item = memo<NetworkOption>(({ value, description, icon, label, setLoading
78
78
  horizontal
79
79
  key={value}
80
80
  onClick={async () => {
81
- setLoading?.(true);
81
+ setUpdating?.(true);
82
82
  await updateAgentChatConfig({ searchMode: value });
83
- setLoading?.(false);
83
+ setUpdating?.(false);
84
84
  }}
85
85
  >
86
86
  <Center className={styles.icon} flex={'none'} height={32} width={32}>
@@ -94,11 +94,12 @@ const Item = memo<NetworkOption>(({ value, description, icon, label, setLoading
94
94
  );
95
95
  });
96
96
 
97
- interface AINetworkSettingsProps {
98
- setLoading?: (loading: boolean) => void;
97
+ interface ControlsProps {
98
+ setUpdating: (updating: boolean) => void;
99
+ updating: boolean;
99
100
  }
100
101
 
101
- const AINetworkSettings = memo<AINetworkSettingsProps>(({ setLoading }) => {
102
+ const Controls = memo<ControlsProps>(({ setUpdating }) => {
102
103
  const { t } = useTranslation('chat');
103
104
  const [model, provider] = useAgentStore((s) => [
104
105
  agentSelectors.currentAgentModel(s),
@@ -130,13 +131,13 @@ const AINetworkSettings = memo<AINetworkSettingsProps>(({ setLoading }) => {
130
131
  return (
131
132
  <Flexbox gap={4}>
132
133
  {options.map((option) => (
133
- <Item setLoading={setLoading} {...option} key={option.value} />
134
+ <Item setUpdating={setUpdating} {...option} key={option.value} />
134
135
  ))}
135
136
  {showDivider && <Divider style={{ margin: 0 }} />}
136
137
  {isModelHasBuiltinSearchConfig && <ModelBuiltinSearch />}
137
- {!supportFC && <FCSearchModel setLoading={setLoading} />}
138
+ {!supportFC && <FCSearchModel setLoading={setUpdating} />}
138
139
  </Flexbox>
139
140
  );
140
141
  });
141
142
 
142
- export default AINetworkSettings;
143
+ export default Controls;
@@ -1,4 +1,3 @@
1
- import { ActionIcon } from '@lobehub/ui';
2
1
  import { GlobeOffIcon } from '@lobehub/ui/icons';
3
2
  import { useTheme } from 'antd-style';
4
3
  import { Globe } from 'lucide-react';
@@ -10,42 +9,38 @@ import { useAgentEnableSearch } from '@/hooks/useAgentEnableSearch';
10
9
  import { useAgentStore } from '@/store/agent';
11
10
  import { agentSelectors } from '@/store/agent/selectors';
12
11
 
13
- import ActionPopover from '../../components/ActionPopover';
14
- import AINetworkSettings from './SwitchPanel';
12
+ import Action from '../components/Action';
13
+ import Controls from './Controls';
15
14
 
16
15
  const Search = memo(() => {
17
16
  const { t } = useTranslation('chat');
18
- const [loading, setLoading] = useState(false);
19
17
  const [isLoading] = useAgentStore((s) => [agentSelectors.isAgentConfigLoading(s)]);
18
+ const [updating, setUpdating] = useState(false);
20
19
  const isAgentEnableSearch = useAgentEnableSearch();
21
20
  const theme = useTheme();
22
21
 
23
- if (isLoading) return <ActionIcon disabled icon={GlobeOffIcon} />;
22
+ if (isDeprecatedEdition) return null;
23
+ if (isLoading) return <Action disabled icon={GlobeOffIcon} />;
24
24
 
25
25
  return (
26
- !isDeprecatedEdition && (
27
- <ActionPopover
28
- content={<AINetworkSettings setLoading={setLoading} />}
29
- maxWidth={320}
30
- minWidth={320}
31
- placement={'topLeft'}
32
- styles={{
26
+ <Action
27
+ color={isAgentEnableSearch ? theme.colorInfo : undefined}
28
+ icon={isAgentEnableSearch ? Globe : GlobeOffIcon}
29
+ loading={updating}
30
+ popover={{
31
+ content: <Controls setUpdating={setUpdating} updating={updating} />,
32
+ maxWidth: 320,
33
+ minWidth: 320,
34
+ placement: 'topLeft',
35
+ styles: {
33
36
  body: {
34
37
  padding: 4,
35
38
  },
36
- }}
37
- >
38
- <ActionIcon
39
- color={isAgentEnableSearch ? theme.colorInfo : undefined}
40
- icon={isAgentEnableSearch ? Globe : GlobeOffIcon}
41
- loading={loading}
42
- title={t('search.title')}
43
- tooltipProps={{
44
- placement: 'bottom',
45
- }}
46
- />
47
- </ActionPopover>
48
- )
39
+ },
40
+ }}
41
+ showTooltip={false}
42
+ title={t('search.title')}
43
+ />
49
44
  );
50
45
  });
51
46
 
@@ -16,7 +16,7 @@ import { chatSelectors, topicSelectors } from '@/store/chat/selectors';
16
16
  import { useToolStore } from '@/store/tool';
17
17
  import { toolSelectors } from '@/store/tool/selectors';
18
18
 
19
- import ActionPopover from '../../components/ActionPopover';
19
+ import ActionPopover from '../components/ActionPopover';
20
20
  import TokenProgress from './TokenProgress';
21
21
 
22
22
  interface TokenTagProps {
@@ -5,14 +5,23 @@ import PluginTag from '@/features/PluginStore/PluginItem/PluginTag';
5
5
  import { useToolStore } from '@/store/tool';
6
6
  import { customPluginSelectors } from '@/store/tool/selectors';
7
7
 
8
- const ToolItem = memo<{ identifier: string; label?: string }>(({ identifier, label }) => {
9
- const isCustom = useToolStore((s) => customPluginSelectors.isCustomPlugin(identifier)(s));
8
+ import CheckboxItem, { CheckboxItemProps } from '../components/CheckbokWithLoading';
9
+
10
+ const ToolItem = memo<CheckboxItemProps>(({ id, onUpdate, label, checked }) => {
11
+ const isCustom = useToolStore((s) => customPluginSelectors.isCustomPlugin(id)(s));
10
12
 
11
13
  return (
12
- <Flexbox align={'center'} gap={8} horizontal>
13
- {label || identifier}
14
- {isCustom && <PluginTag showText={false} type={'customPlugin'} />}
15
- </Flexbox>
14
+ <CheckboxItem
15
+ checked={checked}
16
+ id={id}
17
+ label={
18
+ <Flexbox align={'center'} gap={8} horizontal>
19
+ {label || id}
20
+ {isCustom && <PluginTag showText={false} type={'customPlugin'} />}
21
+ </Flexbox>
22
+ }
23
+ onUpdate={onUpdate}
24
+ />
16
25
  );
17
26
  });
18
27
 
@@ -1,17 +1,21 @@
1
- import { ActionIcon } from '@lobehub/ui';
2
- import { Blocks, LucideLoader2 } from 'lucide-react';
3
- import { Suspense, memo } from 'react';
1
+ import { Blocks } from 'lucide-react';
2
+ import { Suspense, memo, useState } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
5
4
 
5
+ import PluginStore from '@/features/PluginStore';
6
6
  import { useModelSupportToolUse } from '@/hooks/useModelSupportToolUse';
7
7
  import { useAgentStore } from '@/store/agent';
8
8
  import { agentSelectors } from '@/store/agent/selectors';
9
9
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
10
10
 
11
- import DropdownMenu from './Dropdown';
11
+ import Action from '../components/Action';
12
+ import { useControls } from './useControls';
12
13
 
13
14
  const Tools = memo(() => {
14
15
  const { t } = useTranslation('setting');
16
+ const [modalOpen, setModalOpen] = useState(false);
17
+ const [updating, setUpdating] = useState(false);
18
+ const items = useControls({ setModalOpen, setUpdating });
15
19
  const { enablePlugins } = useServerConfigStore(featureFlagsSelectors);
16
20
 
17
21
  const model = useAgentStore(agentSelectors.currentAgentModel);
@@ -19,21 +23,25 @@ const Tools = memo(() => {
19
23
 
20
24
  const enableFC = useModelSupportToolUse(model, provider);
21
25
 
26
+ if (!enablePlugins) return null;
27
+ if (!enableFC)
28
+ return <Action disabled icon={Blocks} showTooltip={true} title={t('tools.disabled')} />;
29
+
22
30
  return (
23
- enablePlugins && (
24
- <Suspense fallback={<ActionIcon icon={LucideLoader2} spin />}>
25
- <DropdownMenu disabled={!enableFC}>
26
- <ActionIcon
27
- disabled={!enableFC}
28
- icon={Blocks}
29
- title={t(enableFC ? 'tools.title' : 'tools.disabled')}
30
- tooltipProps={{
31
- placement: 'bottom',
32
- }}
33
- />
34
- </DropdownMenu>
35
- </Suspense>
36
- )
31
+ <Suspense fallback={<Action disabled icon={Blocks} title={t('tools.title')} />}>
32
+ <Action
33
+ dropdown={{
34
+ maxWidth: 320,
35
+ menu: { items },
36
+ minWidth: 240,
37
+ }}
38
+ icon={Blocks}
39
+ loading={updating}
40
+ showTooltip={false}
41
+ title={t('tools.title')}
42
+ />
43
+ <PluginStore open={modalOpen} setOpen={setModalOpen} />
44
+ </Suspense>
37
45
  );
38
46
  });
39
47
 
@@ -1,31 +1,30 @@
1
- import { Avatar, DropdownProps, Icon, ItemType } from '@lobehub/ui';
2
- import { Checkbox } from 'antd';
1
+ import { Avatar, Icon, ItemType } from '@lobehub/ui';
3
2
  import isEqual from 'fast-deep-equal';
4
3
  import { ArrowRight, Store, ToyBrick } from 'lucide-react';
5
- import { PropsWithChildren, memo, useState } from 'react';
6
4
  import { useTranslation } from 'react-i18next';
7
5
  import { Flexbox } from 'react-layout-kit';
8
6
 
9
- import PluginStore from '@/features/PluginStore';
10
7
  import PluginAvatar from '@/features/PluginStore/PluginItem/PluginAvatar';
11
8
  import { useCheckPluginsIsInstalled } from '@/hooks/useCheckPluginsIsInstalled';
12
9
  import { useFetchInstalledPlugins } from '@/hooks/useFetchInstalledPlugins';
13
- import { useWorkspaceModal } from '@/hooks/useWorkspaceModal';
14
10
  import { useAgentStore } from '@/store/agent';
15
11
  import { agentSelectors } from '@/store/agent/selectors';
16
12
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
17
13
  import { pluginHelpers, useToolStore } from '@/store/tool';
18
14
  import { builtinToolSelectors, pluginSelectors } from '@/store/tool/selectors';
19
15
 
20
- import ActionDropdown from '../../components/ActionDropdown';
21
16
  import ToolItem from './ToolItem';
22
17
 
23
- const DropdownMenu = memo<PropsWithChildren<{ disabled?: boolean }>>(({ children, disabled }) => {
18
+ export const useControls = ({
19
+ setModalOpen,
20
+ setUpdating,
21
+ }: {
22
+ setModalOpen: (open: boolean) => void;
23
+ setUpdating: (updating: boolean) => void;
24
+ }) => {
24
25
  const { t } = useTranslation('setting');
25
26
  const list = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
26
27
  const { showDalle } = useServerConfigStore(featureFlagsSelectors);
27
- const [dropdownOpen, setDropdownOpen] = useState(false);
28
- const [open, setOpen] = useWorkspaceModal();
29
28
  const [checked, togglePlugin] = useAgentStore((s) => [
30
29
  agentSelectors.currentAgentPlugins(s),
31
30
  s.togglePlugin,
@@ -37,23 +36,31 @@ const DropdownMenu = memo<PropsWithChildren<{ disabled?: boolean }>>(({ children
37
36
  .currentAgentPlugins(s)
38
37
  .filter((i) => !builtinList.some((b) => b.identifier === i)).length,
39
38
  );
39
+ const plugins = useAgentStore((s) => agentSelectors.currentAgentPlugins(s));
40
+
41
+ const [useFetchPluginStore] = useToolStore((s) => [s.useFetchPluginStore]);
42
+
43
+ useFetchPluginStore();
44
+ useFetchInstalledPlugins();
45
+ useCheckPluginsIsInstalled(plugins);
40
46
 
41
47
  const items: ItemType[] = [
42
48
  {
43
49
  children: builtinList.map((item) => ({
44
- extra: (
45
- <Checkbox
50
+ icon: <Avatar avatar={item.meta.avatar} size={20} style={{ flex: 'none' }} />,
51
+ key: item.identifier,
52
+ label: (
53
+ <ToolItem
46
54
  checked={checked.includes(item.identifier)}
47
- onClick={(e) => {
48
- e.stopPropagation();
49
- togglePlugin(item.identifier);
55
+ id={item.identifier}
56
+ label={item.meta?.title}
57
+ onUpdate={async () => {
58
+ setUpdating(true);
59
+ await togglePlugin(item.identifier);
60
+ setUpdating(false);
50
61
  }}
51
62
  />
52
63
  ),
53
- icon: <Avatar avatar={item.meta.avatar} size={20} style={{ flex: 'none' }} />,
54
- key: item.identifier,
55
- label: <ToolItem identifier={item.identifier} label={item.meta?.title} />,
56
- onClick: () => togglePlugin(item.identifier),
57
64
  })),
58
65
 
59
66
  key: 'builtins',
@@ -62,15 +69,6 @@ const DropdownMenu = memo<PropsWithChildren<{ disabled?: boolean }>>(({ children
62
69
  },
63
70
  {
64
71
  children: list.map((item) => ({
65
- extra: (
66
- <Checkbox
67
- checked={checked.includes(item.identifier)}
68
- onClick={(e) => {
69
- e.stopPropagation();
70
- togglePlugin(item.identifier);
71
- }}
72
- />
73
- ),
74
72
  icon: item.meta?.avatar ? (
75
73
  <PluginAvatar avatar={pluginHelpers.getPluginAvatar(item.meta)} size={20} />
76
74
  ) : (
@@ -78,9 +76,17 @@ const DropdownMenu = memo<PropsWithChildren<{ disabled?: boolean }>>(({ children
78
76
  ),
79
77
  key: item.identifier,
80
78
  label: (
81
- <ToolItem identifier={item.identifier} label={pluginHelpers.getPluginTitle(item?.meta)} />
79
+ <ToolItem
80
+ checked={checked.includes(item.identifier)}
81
+ id={item.identifier}
82
+ label={pluginHelpers.getPluginTitle(item?.meta)}
83
+ onUpdate={async () => {
84
+ setUpdating(true);
85
+ await togglePlugin(item.identifier);
86
+ setUpdating(false);
87
+ }}
88
+ />
82
89
  ),
83
- onClick: () => togglePlugin(item.identifier),
84
90
  })),
85
91
  key: 'plugins',
86
92
  label: (
@@ -104,41 +110,10 @@ const DropdownMenu = memo<PropsWithChildren<{ disabled?: boolean }>>(({ children
104
110
  key: 'plugin-store',
105
111
  label: t('tools.plugins.store'),
106
112
  onClick: () => {
107
- setDropdownOpen(false);
108
- setOpen(true);
113
+ setModalOpen(true);
109
114
  },
110
115
  },
111
116
  ];
112
117
 
113
- const plugins = useAgentStore((s) => agentSelectors.currentAgentPlugins(s));
114
-
115
- const [useFetchPluginStore] = useToolStore((s) => [s.useFetchPluginStore]);
116
-
117
- useFetchPluginStore();
118
- useFetchInstalledPlugins();
119
- useCheckPluginsIsInstalled(plugins);
120
-
121
- const handleOpenChange: DropdownProps['onOpenChange'] = (nextOpen, info) => {
122
- if (info.source === 'trigger' || nextOpen) {
123
- setDropdownOpen(nextOpen);
124
- }
125
- };
126
-
127
- return (
128
- <>
129
- <ActionDropdown
130
- disabled={disabled}
131
- maxHeight={500}
132
- menu={{ items }}
133
- minWidth={240}
134
- onOpenChange={handleOpenChange}
135
- open={dropdownOpen}
136
- >
137
- {children}
138
- </ActionDropdown>
139
- <PluginStore open={open} setOpen={setOpen} />
140
- </>
141
- );
142
- });
143
-
144
- export default DropdownMenu;
118
+ return items;
119
+ };
@@ -1,4 +1,4 @@
1
- import { ActionIcon, MenuProps, Tooltip } from '@lobehub/ui';
1
+ import { MenuProps, Tooltip } from '@lobehub/ui';
2
2
  import { Upload } from 'antd';
3
3
  import { css, cx } from 'antd-style';
4
4
  import { FileUp, FolderUp, ImageUp, Paperclip } from 'lucide-react';
@@ -10,7 +10,7 @@ import { useAgentStore } from '@/store/agent';
10
10
  import { agentSelectors } from '@/store/agent/slices/chat';
11
11
  import { useFileStore } from '@/store/file';
12
12
 
13
- import ActionDropdown from '../../components/ActionDropdown';
13
+ import Action from '../components/Action';
14
14
 
15
15
  const hotArea = css`
16
16
  &::before {
@@ -97,15 +97,14 @@ const FileUpload = memo(() => {
97
97
  ];
98
98
 
99
99
  return (
100
- <ActionDropdown menu={{ items }}>
101
- <ActionIcon
102
- icon={Paperclip}
103
- title={t('upload.action.tooltip')}
104
- tooltipProps={{
105
- placement: 'bottom',
106
- }}
107
- />
108
- </ActionDropdown>
100
+ <Action
101
+ dropdown={{
102
+ menu: { items },
103
+ }}
104
+ icon={Paperclip}
105
+ showTooltip={false}
106
+ title={t('upload.action.tooltip')}
107
+ />
109
108
  );
110
109
  });
111
110
 
@@ -0,0 +1,90 @@
1
+ 'use client';
2
+
3
+ import { ActionIcon, type ActionIconProps } from '@lobehub/ui';
4
+ import { isUndefined } from 'lodash-es';
5
+ import { memo } from 'react';
6
+ import useMergeState from 'use-merge-value';
7
+
8
+ import { useServerConfigStore } from '@/store/serverConfig';
9
+
10
+ import ActionDropdown, { ActionDropdownProps } from './ActionDropdown';
11
+ import ActionPopover, { ActionPopoverProps } from './ActionPopover';
12
+
13
+ interface ActionProps extends Omit<ActionIconProps, 'popover'> {
14
+ dropdown?: ActionDropdownProps;
15
+ onOpenChange?: (open: boolean) => void;
16
+ open?: boolean;
17
+ popover?: ActionPopoverProps;
18
+ showTooltip?: boolean;
19
+ trigger?: ActionDropdownProps['trigger'];
20
+ }
21
+
22
+ const Action = memo<ActionProps>(
23
+ ({
24
+ showTooltip,
25
+ loading,
26
+ icon,
27
+ title,
28
+ dropdown,
29
+ popover,
30
+ open,
31
+ onOpenChange,
32
+ trigger,
33
+ disabled,
34
+ ...rest
35
+ }) => {
36
+ const [show, setShow] = useMergeState(false, {
37
+ onChange: onOpenChange,
38
+ value: open,
39
+ });
40
+ const mobile = useServerConfigStore((s) => s.isMobile);
41
+ const iconNode = (
42
+ <ActionIcon
43
+ disabled={disabled}
44
+ icon={icon}
45
+ loading={loading}
46
+ onClick={() => setShow(true)}
47
+ title={
48
+ isUndefined(showTooltip) ? (mobile ? undefined : title) : showTooltip ? title : undefined
49
+ }
50
+ tooltipProps={{
51
+ placement: 'bottom',
52
+ }}
53
+ {...rest}
54
+ />
55
+ );
56
+
57
+ if (disabled) return iconNode;
58
+
59
+ if (dropdown)
60
+ return (
61
+ <ActionDropdown
62
+ onOpenChange={setShow}
63
+ open={show}
64
+ trigger={trigger}
65
+ {...dropdown}
66
+ minWidth={mobile ? '100%' : dropdown.minWidth}
67
+ placement={mobile ? 'top' : dropdown.placement}
68
+ >
69
+ {iconNode}
70
+ </ActionDropdown>
71
+ );
72
+ if (popover)
73
+ return (
74
+ <ActionPopover
75
+ onOpenChange={setShow}
76
+ open={show}
77
+ trigger={trigger}
78
+ {...popover}
79
+ minWidth={mobile ? '100%' : popover.minWidth}
80
+ placement={mobile ? 'top' : popover.placement}
81
+ >
82
+ {iconNode}
83
+ </ActionPopover>
84
+ );
85
+
86
+ return iconNode;
87
+ },
88
+ );
89
+
90
+ export default Action;
@@ -19,10 +19,10 @@ const useStyles = createStyles(({ css, prefixCls }) => ({
19
19
  `,
20
20
  }));
21
21
 
22
- interface ActionDropdownProps extends DropdownProps {
23
- maxHeight?: number;
24
- maxWidth?: number;
25
- minWidth?: number;
22
+ export interface ActionDropdownProps extends DropdownProps {
23
+ maxHeight?: number | string;
24
+ maxWidth?: number | string;
25
+ minWidth?: number | string;
26
26
  }
27
27
 
28
28
  const ActionDropdown = memo<ActionDropdownProps>(
@@ -21,12 +21,13 @@ const useStyles = createStyles(({ css, prefixCls }) => ({
21
21
  `,
22
22
  }));
23
23
 
24
- interface ActionPopoverProps extends Omit<PopoverProps, 'title'> {
24
+ export interface ActionPopoverProps extends Omit<PopoverProps, 'title' | 'content'> {
25
+ content?: ReactNode;
25
26
  extra?: ReactNode;
26
27
  loading?: boolean;
27
- maxHeight?: number;
28
- maxWidth?: number;
29
- minWidth?: number;
28
+ maxHeight?: number | string;
29
+ maxWidth?: number | string;
30
+ minWidth?: number | string;
30
31
  title?: ReactNode;
31
32
  }
32
33