@lobehub/lobehub 2.0.0-next.270 → 2.0.0-next.272

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.
@@ -1,395 +1,13 @@
1
1
  'use client';
2
2
 
3
- import { KLAVIS_SERVER_TYPES, type KlavisServerType } from '@lobechat/const';
4
- import { Avatar, Button, Flexbox, Icon, type ItemType, Segmented } from '@lobehub/ui';
5
- import { cssVar } from 'antd-style';
6
- import isEqual from 'fast-deep-equal';
7
- import { ArrowRight, PlusIcon, Store, ToyBrick } from 'lucide-react';
8
- import Image from 'next/image';
9
- import React, { Suspense, memo, useEffect, useMemo, useRef, useState } from 'react';
10
- import { useTranslation } from 'react-i18next';
11
-
12
- import PluginAvatar from '@/components/Plugins/PluginAvatar';
13
- import KlavisServerItem from '@/features/ChatInput/ActionBar/Tools/KlavisServerItem';
14
- import ToolItem from '@/features/ChatInput/ActionBar/Tools/ToolItem';
15
- import ActionDropdown from '@/features/ChatInput/ActionBar/components/ActionDropdown';
16
- import PluginStore from '@/features/PluginStore';
17
- import { useCheckPluginsIsInstalled } from '@/hooks/useCheckPluginsIsInstalled';
18
- import { useFetchInstalledPlugins } from '@/hooks/useFetchInstalledPlugins';
19
- import { useAgentStore } from '@/store/agent';
20
- import { agentSelectors } from '@/store/agent/selectors';
21
- import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
22
- import { useToolStore } from '@/store/tool';
23
- import {
24
- builtinToolSelectors,
25
- klavisStoreSelectors,
26
- pluginSelectors,
27
- } from '@/store/tool/selectors';
28
-
29
- import PluginTag from './PluginTag';
30
-
31
- type TabType = 'all' | 'installed';
3
+ import { AgentTool as SharedAgentTool } from '@/features/ProfileEditor';
32
4
 
33
5
  /**
34
- * Klavis 服务器图标组件
35
- * 对于 string 类型的 icon,使用 Image 组件渲染
36
- * 对于 IconType 类型的 icon,使用 Icon 组件渲染,并根据主题设置填充色
6
+ * AgentTool for group profile editor
7
+ * - Uses default settings (no web browsing, no filterAvailableInWeb, uses metaList)
37
8
  */
38
- const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label }) => {
39
- if (typeof icon === 'string') {
40
- return (
41
- <Image alt={label} height={18} src={icon} style={{ flex: 'none' }} unoptimized width={18} />
42
- );
43
- }
44
-
45
- // 使用主题色填充,在深色模式下自动适应
46
- return <Icon fill={cssVar.colorText} icon={icon} size={18} />;
47
- });
48
-
49
- const AgentTool = memo(() => {
50
- const { t } = useTranslation('setting');
51
- const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
52
-
53
- // Plugin state management
54
- const plugins = config?.plugins || [];
55
-
56
- const toggleAgentPlugin = useAgentStore((s) => s.toggleAgentPlugin);
57
- const installedPluginList = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
58
- const builtinList = useToolStore(builtinToolSelectors.metaList, isEqual);
59
-
60
- // Klavis 相关状态
61
- const allKlavisServers = useToolStore(klavisStoreSelectors.getServers, isEqual);
62
- const isKlavisEnabledInEnv = useServerConfigStore(serverConfigSelectors.enableKlavis);
63
-
64
- // Plugin store modal state
65
- const [modalOpen, setModalOpen] = useState(false);
66
- const [updating, setUpdating] = useState(false);
67
-
68
- // Tab state for dual-column layout
69
- const [activeTab, setActiveTab] = useState<TabType | null>(null);
70
- const isInitializedRef = useRef(false);
71
-
72
- // Fetch plugins
73
- const [useFetchPluginStore, useFetchUserKlavisServers] = useToolStore((s) => [
74
- s.useFetchPluginStore,
75
- s.useFetchUserKlavisServers,
76
- ]);
77
- useFetchPluginStore();
78
- useFetchInstalledPlugins();
79
- useCheckPluginsIsInstalled(plugins);
80
-
81
- // 使用 SWR 加载用户的 Klavis 集成(从数据库)
82
- useFetchUserKlavisServers(isKlavisEnabledInEnv);
83
-
84
- // Set default tab based on installed plugins (only on first load)
85
- useEffect(() => {
86
- if (!isInitializedRef.current && plugins.length >= 0) {
87
- isInitializedRef.current = true;
88
- setActiveTab(plugins.length > 0 ? 'installed' : 'all');
89
- }
90
- }, [plugins.length]);
91
-
92
- // 根据 identifier 获取已连接的服务器
93
- const getServerByName = (identifier: string) => {
94
- return allKlavisServers.find((server) => server.identifier === identifier);
95
- };
96
-
97
- // 获取所有 Klavis 服务器类型的 identifier 集合(用于过滤 builtinList)
98
- const allKlavisTypeIdentifiers = useMemo(
99
- () => new Set(KLAVIS_SERVER_TYPES.map((type) => type.identifier)),
100
- [],
101
- );
102
-
103
- // 过滤掉 builtinList 中的 klavis 工具(它们会单独显示在 Klavis 区域)
104
- const filteredBuiltinList = useMemo(
105
- () =>
106
- isKlavisEnabledInEnv
107
- ? builtinList.filter((item) => !allKlavisTypeIdentifiers.has(item.identifier))
108
- : builtinList,
109
- [builtinList, allKlavisTypeIdentifiers, isKlavisEnabledInEnv],
110
- );
111
-
112
- // Klavis 服务器列表项
113
- const klavisServerItems = useMemo(
114
- () =>
115
- isKlavisEnabledInEnv
116
- ? KLAVIS_SERVER_TYPES.map((type) => ({
117
- icon: <KlavisIcon icon={type.icon} label={type.label} />,
118
- key: type.identifier,
119
- label: (
120
- <KlavisServerItem
121
- identifier={type.identifier}
122
- label={type.label}
123
- server={getServerByName(type.identifier)}
124
- serverName={type.serverName}
125
- />
126
- ),
127
- }))
128
- : [],
129
- [isKlavisEnabledInEnv, allKlavisServers],
130
- );
131
-
132
- // Handle plugin remove via Tag close
133
- const handleRemovePlugin =
134
- (pluginId: string | { enabled: boolean; identifier: string; settings: Record<string, any> }) =>
135
- (e: React.MouseEvent) => {
136
- e.preventDefault();
137
- e.stopPropagation();
138
- const identifier = typeof pluginId === 'string' ? pluginId : pluginId?.identifier;
139
- toggleAgentPlugin(identifier, false);
140
- };
141
-
142
- // Build dropdown menu items (adapted from useControls)
143
- const enablePluginCount = plugins.filter(
144
- (id) => !builtinList.some((b) => b.identifier === id),
145
- ).length;
146
-
147
- // 合并 builtin 工具和 Klavis 服务器
148
- const builtinItems = useMemo(
149
- () => [
150
- // 原有的 builtin 工具
151
- ...filteredBuiltinList.map((item) => ({
152
- icon: <Avatar avatar={item.meta.avatar} size={20} style={{ flex: 'none' }} />,
153
- key: item.identifier,
154
- label: (
155
- <ToolItem
156
- checked={plugins.includes(item.identifier)}
157
- id={item.identifier}
158
- label={item.meta?.title}
159
- onUpdate={async () => {
160
- setUpdating(true);
161
- await toggleAgentPlugin(item.identifier);
162
- setUpdating(false);
163
- }}
164
- />
165
- ),
166
- })),
167
- // Klavis 服务器
168
- ...klavisServerItems,
169
- ],
170
- [filteredBuiltinList, klavisServerItems, plugins, toggleAgentPlugin],
171
- );
172
-
173
- // Plugin items for dropdown
174
- const pluginItems = useMemo(
175
- () =>
176
- installedPluginList.map((item) => ({
177
- icon: item?.avatar ? (
178
- <PluginAvatar avatar={item.avatar} size={20} />
179
- ) : (
180
- <Icon icon={ToyBrick} size={20} />
181
- ),
182
- key: item.identifier,
183
- label: (
184
- <ToolItem
185
- checked={plugins.includes(item.identifier)}
186
- id={item.identifier}
187
- label={item.title}
188
- onUpdate={async () => {
189
- setUpdating(true);
190
- await toggleAgentPlugin(item.identifier);
191
- setUpdating(false);
192
- }}
193
- />
194
- ),
195
- })),
196
- [installedPluginList, plugins, toggleAgentPlugin],
197
- );
198
-
199
- // All tab items (市场 tab)
200
- const allTabItems: ItemType[] = useMemo(
201
- () => [
202
- {
203
- children: builtinItems,
204
- key: 'builtins',
205
- label: t('tools.builtins.groupName'),
206
- type: 'group',
207
- },
208
- {
209
- children: pluginItems,
210
- key: 'plugins',
211
- label: (
212
- <Flexbox align={'center'} gap={40} horizontal justify={'space-between'}>
213
- {t('tools.plugins.groupName')}
214
- {enablePluginCount === 0 ? null : (
215
- <div style={{ fontSize: 12, marginInlineEnd: 4 }}>
216
- {t('tools.plugins.enabled', { num: enablePluginCount })}
217
- </div>
218
- )}
219
- </Flexbox>
220
- ),
221
- type: 'group',
222
- },
223
- {
224
- type: 'divider',
225
- },
226
- {
227
- extra: <Icon icon={ArrowRight} />,
228
- icon: Store,
229
- key: 'plugin-store',
230
- label: t('tools.plugins.store'),
231
- onClick: () => {
232
- setModalOpen(true);
233
- },
234
- },
235
- ],
236
- [builtinItems, pluginItems, enablePluginCount, t],
237
- );
238
-
239
- // Installed tab items - 只显示已启用的
240
- const installedTabItems: ItemType[] = useMemo(() => {
241
- const items: ItemType[] = [];
242
-
243
- // 已启用的 builtin 工具
244
- const enabledBuiltinItems = filteredBuiltinList
245
- .filter((item) => plugins.includes(item.identifier))
246
- .map((item) => ({
247
- icon: <Avatar avatar={item.meta.avatar} size={20} style={{ flex: 'none' }} />,
248
- key: item.identifier,
249
- label: (
250
- <ToolItem
251
- checked={true}
252
- id={item.identifier}
253
- label={item.meta?.title}
254
- onUpdate={async () => {
255
- setUpdating(true);
256
- await toggleAgentPlugin(item.identifier);
257
- setUpdating(false);
258
- }}
259
- />
260
- ),
261
- }));
262
-
263
- // 已连接且已启用的 Klavis 服务器
264
- const connectedKlavisItems = klavisServerItems.filter((item) =>
265
- plugins.includes(item.key as string),
266
- );
267
-
268
- // 合并 builtin 和 Klavis
269
- const allBuiltinItems = [...enabledBuiltinItems, ...connectedKlavisItems];
270
-
271
- if (allBuiltinItems.length > 0) {
272
- items.push({
273
- children: allBuiltinItems,
274
- key: 'installed-builtins',
275
- label: t('tools.builtins.groupName'),
276
- type: 'group',
277
- });
278
- }
279
-
280
- // 已启用的插件
281
- const installedPlugins = installedPluginList
282
- .filter((item) => plugins.includes(item.identifier))
283
- .map((item) => ({
284
- icon: item?.avatar ? (
285
- <PluginAvatar avatar={item.avatar} size={20} />
286
- ) : (
287
- <Icon icon={ToyBrick} size={20} />
288
- ),
289
- key: item.identifier,
290
- label: (
291
- <ToolItem
292
- checked={true}
293
- id={item.identifier}
294
- label={item.title}
295
- onUpdate={async () => {
296
- setUpdating(true);
297
- await toggleAgentPlugin(item.identifier);
298
- setUpdating(false);
299
- }}
300
- />
301
- ),
302
- }));
303
-
304
- if (installedPlugins.length > 0) {
305
- items.push({
306
- children: installedPlugins,
307
- key: 'installed-plugins',
308
- label: t('tools.plugins.groupName'),
309
- type: 'group',
310
- });
311
- }
312
-
313
- return items;
314
- }, [filteredBuiltinList, klavisServerItems, installedPluginList, plugins, toggleAgentPlugin, t]);
315
-
316
- // Use effective tab for display (default to all while initializing)
317
- const effectiveTab = activeTab ?? 'all';
318
- const currentItems = effectiveTab === 'all' ? allTabItems : installedTabItems;
319
-
320
- // Final menu items with tab segmented control
321
- const menuItems: ItemType[] = useMemo(
322
- () => [
323
- {
324
- key: 'tabs',
325
- label: (
326
- <Segmented
327
- block
328
- onChange={(v) => setActiveTab(v as TabType)}
329
- options={[
330
- {
331
- label: t('tools.tabs.all', { defaultValue: 'All' }),
332
- value: 'all',
333
- },
334
- {
335
- label: t('tools.tabs.installed', { defaultValue: 'Installed' }),
336
- value: 'installed',
337
- },
338
- ]}
339
- size="small"
340
- value={effectiveTab}
341
- />
342
- ),
343
- type: 'group',
344
- },
345
- ...currentItems,
346
- ],
347
- [currentItems, effectiveTab, t],
348
- );
349
-
350
- const button = (
351
- <Button
352
- icon={PlusIcon}
353
- loading={updating}
354
- size={'small'}
355
- style={{ color: cssVar.colorTextSecondary }}
356
- type={'text'}
357
- >
358
- {t('tools.add', { defaultValue: 'Add' })}
359
- </Button>
360
- );
361
-
362
- return (
363
- <>
364
- {/* Plugin Selector and Tags */}
365
- <Flexbox align="center" gap={8} horizontal wrap={'wrap'}>
366
- {/* Second Row: Selected Plugins as Tags */}
367
- {plugins?.map((pluginId) => {
368
- return (
369
- <PluginTag key={pluginId} onRemove={handleRemovePlugin(pluginId)} pluginId={pluginId} />
370
- );
371
- })}
372
- {/* Plugin Selector Dropdown - Using Action component pattern */}
373
-
374
- <Suspense fallback={button}>
375
- <ActionDropdown
376
- maxHeight={500}
377
- maxWidth={480}
378
- menu={{ items: menuItems }}
379
- minHeight={isKlavisEnabledInEnv ? 500 : undefined}
380
- minWidth={320}
381
- placement={'bottomLeft'}
382
- trigger={['click']}
383
- >
384
- {button}
385
- </ActionDropdown>
386
- </Suspense>
387
- </Flexbox>
388
-
389
- {/* PluginStore Modal */}
390
- <PluginStore open={modalOpen} setOpen={setModalOpen} />
391
- </>
392
- );
393
- });
9
+ const AgentTool = () => {
10
+ return <SharedAgentTool />;
11
+ };
394
12
 
395
13
  export default AgentTool;
@@ -1,4 +1,4 @@
1
- import { Flexbox, Icon , Checkbox } from '@lobehub/ui';
1
+ import { Checkbox, Flexbox, Icon } from '@lobehub/ui';
2
2
  import { Loader2, SquareArrowOutUpRight, Unplug } from 'lucide-react';
3
3
  import { memo, useCallback, useEffect, useRef, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
@@ -336,7 +336,6 @@ const KlavisServerItem = memo<KlavisServerItemProps>(
336
336
  handleToggle();
337
337
  }
338
338
  }}
339
- style={{ paddingLeft: 8 }}
340
339
  >
341
340
  <Flexbox align={'center'} gap={8} horizontal>
342
341
  {label}
@@ -331,7 +331,6 @@ const LobehubSkillServerItem = memo<LobehubSkillServerItemProps>(({ provider, la
331
331
  handleToggle();
332
332
  }
333
333
  }}
334
- style={{ paddingLeft: 8 }}
335
334
  >
336
335
  <Flexbox align={'center'} gap={8} horizontal>
337
336
  {label}