@lobehub/lobehub 2.0.0-next.244 → 2.0.0-next.246

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 (28) hide show
  1. package/.github/workflows/bundle-analyzer.yml +2 -2
  2. package/CHANGELOG.md +58 -0
  3. package/apps/desktop/build/Icon-beta.Assets.car +0 -0
  4. package/changelog/v1.json +18 -0
  5. package/docs/development/database-schema.dbml +34 -1
  6. package/package.json +2 -2
  7. package/packages/database/migrations/0067_add_agent_cron_tables.sql +54 -0
  8. package/packages/database/migrations/meta/0067_snapshot.json +10238 -0
  9. package/packages/database/migrations/meta/_journal.json +8 -1
  10. package/packages/database/src/models/__tests__/topics/topic.create.test.ts +4 -0
  11. package/packages/database/src/models/agentCronJob.ts +286 -0
  12. package/packages/database/src/schemas/agentCronJob.ts +138 -0
  13. package/packages/database/src/schemas/index.ts +1 -0
  14. package/packages/database/src/schemas/topic.ts +2 -0
  15. package/packages/database/src/utils/idGenerator.ts +1 -0
  16. package/src/app/[variants]/(main)/chat/_layout/Sidebar/Header/Agent/SwitchPanel.tsx +1 -0
  17. package/src/app/[variants]/(main)/chat/features/Conversation/Header/HeaderActions/index.tsx +1 -1
  18. package/src/features/ChatInput/ActionBar/Knowledge/useControls.tsx +1 -1
  19. package/src/features/ChatInput/ActionBar/Tools/PopoverContent.tsx +92 -0
  20. package/src/features/ChatInput/ActionBar/Tools/ToolItem.tsx +2 -1
  21. package/src/features/ChatInput/ActionBar/Tools/ToolsList.tsx +107 -0
  22. package/src/features/ChatInput/ActionBar/Tools/index.tsx +17 -74
  23. package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +2 -20
  24. package/src/features/ChatInput/ActionBar/Upload/ServerMode.tsx +1 -1
  25. package/src/features/ChatInput/ActionBar/components/ActionPopover.tsx +1 -0
  26. package/src/features/ChatInput/ActionBar/components/CheckboxWithLoading.tsx +60 -0
  27. package/src/features/ModelSwitchPanel/index.tsx +1 -0
  28. package/src/features/ChatInput/ActionBar/components/CheckbokWithLoading.tsx +0 -53
@@ -0,0 +1,107 @@
1
+ import { Flexbox, Icon, type ItemType, Text } from '@lobehub/ui';
2
+ import { Divider } from 'antd';
3
+ import { createStaticStyles, cssVar } from 'antd-style';
4
+ import type { ReactNode } from 'react';
5
+ import { Fragment, isValidElement, memo } from 'react';
6
+
7
+ export const toolsListStyles = createStaticStyles(({ css }) => ({
8
+ groupLabel: css`
9
+ padding-block: 4px;
10
+ padding-inline: 12px;
11
+ `,
12
+ item: css`
13
+ cursor: pointer;
14
+
15
+ display: flex;
16
+ gap: 12px;
17
+ align-items: center;
18
+
19
+ padding-block: 8px;
20
+ padding-inline: 12px;
21
+ border-radius: 6px;
22
+
23
+ transition: background-color 0.2s;
24
+
25
+ &:hover {
26
+ background: ${cssVar.colorFillTertiary};
27
+ }
28
+ `,
29
+ itemContent: css`
30
+ flex: 1;
31
+ min-width: 0;
32
+ `,
33
+ itemIcon: css`
34
+ display: flex;
35
+ flex-shrink: 0;
36
+ align-items: center;
37
+ justify-content: center;
38
+
39
+ width: 24px;
40
+ height: 24px;
41
+ `,
42
+ }));
43
+
44
+ interface ToolItemData {
45
+ children?: ToolItemData[];
46
+ extra?: ReactNode;
47
+ icon?: ReactNode;
48
+ key?: string;
49
+ label?: ReactNode;
50
+ onClick?: () => void;
51
+ type?: 'group' | 'divider';
52
+ }
53
+
54
+ interface ToolsListProps {
55
+ items: ItemType[];
56
+ }
57
+
58
+ const ToolsList = memo<ToolsListProps>(({ items }) => {
59
+ const renderItem = (item: ToolItemData, index: number) => {
60
+ if (item.type === 'divider') {
61
+ return <Divider key={`divider-${index}`} style={{ margin: '4px 0' }} />;
62
+ }
63
+
64
+ if (item.type === 'group') {
65
+ return (
66
+ <Fragment key={item.key || `group-${index}`}>
67
+ <Text className={toolsListStyles.groupLabel} fontSize={12} type="secondary">
68
+ {item.label}
69
+ </Text>
70
+ {item.children?.map((child, childIndex) => renderItem(child, childIndex))}
71
+ </Fragment>
72
+ );
73
+ }
74
+
75
+ // Regular item
76
+ // icon can be: ReactNode (already rendered), LucideIcon/ForwardRef (needs Icon wrapper), or undefined
77
+ const iconNode = item.icon ? (
78
+ isValidElement(item.icon) ? (
79
+ item.icon
80
+ ) : (
81
+ <Icon icon={item.icon as any} size={20} />
82
+ )
83
+ ) : null;
84
+
85
+ return (
86
+ <div
87
+ className={toolsListStyles.item}
88
+ key={item.key || `item-${index}`}
89
+ onClick={item.onClick}
90
+ role="button"
91
+ tabIndex={0}
92
+ >
93
+ {iconNode && <div className={toolsListStyles.itemIcon}>{iconNode}</div>}
94
+ <div className={toolsListStyles.itemContent}>{item.label}</div>
95
+ {item.extra}
96
+ </div>
97
+ );
98
+ };
99
+
100
+ return (
101
+ <Flexbox gap={0} padding={4}>
102
+ {items.map((item, index) => renderItem(item as ToolItemData, index))}
103
+ </Flexbox>
104
+ );
105
+ });
106
+
107
+ export default ToolsList;
@@ -1,5 +1,3 @@
1
- import { Segmented } from '@lobehub/ui';
2
- import { createStaticStyles, cssVar } from 'antd-style';
3
1
  import { Blocks } from 'lucide-react';
4
2
  import { Suspense, memo, useEffect, useRef, useState } from 'react';
5
3
  import { useTranslation } from 'react-i18next';
@@ -12,46 +10,17 @@ import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfi
12
10
 
13
11
  import { useAgentId } from '../../hooks/useAgentId';
14
12
  import Action from '../components/Action';
13
+ import PopoverContent from './PopoverContent';
15
14
  import { useControls } from './useControls';
16
15
 
17
16
  type TabType = 'all' | 'installed';
18
17
 
19
- const prefixCls = 'ant';
20
-
21
- const styles = createStaticStyles(({ css }) => ({
22
- dropdown: css`
23
- overflow: hidden;
24
-
25
- width: 100%;
26
- border: 1px solid ${cssVar.colorBorderSecondary};
27
- border-radius: ${cssVar.borderRadiusLG};
28
-
29
- background: ${cssVar.colorBgElevated};
30
- box-shadow: ${cssVar.boxShadowSecondary};
31
-
32
- .${prefixCls}-dropdown-menu {
33
- border-radius: 0 !important;
34
- background: transparent !important;
35
- box-shadow: none !important;
36
- }
37
- `,
38
- header: css`
39
- padding: ${cssVar.paddingXS};
40
- border-block-end: 1px solid ${cssVar.colorBorderSecondary};
41
- background: transparent;
42
- `,
43
- scroller: css`
44
- overflow: hidden auto;
45
- `,
46
- }));
47
-
48
18
  const Tools = memo(() => {
49
19
  const { t } = useTranslation('setting');
50
20
  const [modalOpen, setModalOpen] = useState(false);
51
21
  const [updating, setUpdating] = useState(false);
52
22
  const [activeTab, setActiveTab] = useState<TabType | null>(null);
53
23
  const { marketItems, installedPluginItems } = useControls({
54
- setModalOpen,
55
24
  setUpdating,
56
25
  });
57
26
 
@@ -82,52 +51,26 @@ const Tools = memo(() => {
82
51
  return (
83
52
  <Suspense fallback={<Action disabled icon={Blocks} title={t('tools.title')} />}>
84
53
  <Action
85
- dropdown={{
54
+ icon={Blocks}
55
+ loading={updating}
56
+ popover={{
57
+ content: (
58
+ <PopoverContent
59
+ activeTab={effectiveTab}
60
+ currentItems={currentItems}
61
+ enableKlavis={enableKlavis}
62
+ onOpenStore={() => setModalOpen(true)}
63
+ onTabChange={setActiveTab}
64
+ />
65
+ ),
86
66
  maxWidth: 320,
87
- menu: {
88
- items: [...currentItems],
89
- style: {
90
- // let only the custom scroller scroll
91
- maxHeight: 'unset',
92
- overflowY: 'visible',
67
+ minWidth: 320,
68
+ styles: {
69
+ content: {
70
+ padding: 0,
93
71
  },
94
72
  },
95
- minHeight: enableKlavis ? 500 : undefined,
96
- minWidth: 320,
97
- popupRender: (menu) => (
98
- <div className={styles.dropdown}>
99
- <div className={styles.header}>
100
- <Segmented
101
- block
102
- onChange={(v) => setActiveTab(v as TabType)}
103
- options={[
104
- {
105
- label: t('tools.tabs.all', { defaultValue: 'all' }),
106
- value: 'all',
107
- },
108
- {
109
- label: t('tools.tabs.installed', { defaultValue: 'Installed' }),
110
- value: 'installed',
111
- },
112
- ]}
113
- size="small"
114
- value={effectiveTab}
115
- />
116
- </div>
117
- <div
118
- className={styles.scroller}
119
- style={{
120
- maxHeight: 500,
121
- minHeight: enableKlavis ? 500 : undefined,
122
- }}
123
- >
124
- {menu}
125
- </div>
126
- </div>
127
- ),
128
73
  }}
129
- icon={Blocks}
130
- loading={updating}
131
74
  showTooltip={false}
132
75
  title={t('tools.title')}
133
76
  />
@@ -7,7 +7,7 @@ import {
7
7
  import { Avatar, Flexbox, Icon, Image, type ItemType } from '@lobehub/ui';
8
8
  import { cssVar } from 'antd-style';
9
9
  import isEqual from 'fast-deep-equal';
10
- import { ArrowRight, Store, ToyBrick } from 'lucide-react';
10
+ import { ToyBrick } from 'lucide-react';
11
11
  import { memo, useMemo } from 'react';
12
12
  import { useTranslation } from 'react-i18next';
13
13
 
@@ -61,13 +61,7 @@ const LobehubSkillIcon = memo<Pick<LobehubSkillProviderType, 'icon' | 'label'>>(
61
61
 
62
62
  LobehubSkillIcon.displayName = 'LobehubSkillIcon';
63
63
 
64
- export const useControls = ({
65
- setModalOpen,
66
- setUpdating,
67
- }: {
68
- setModalOpen: (open: boolean) => void;
69
- setUpdating: (updating: boolean) => void;
70
- }) => {
64
+ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean) => void }) => {
71
65
  const { t } = useTranslation('setting');
72
66
  const agentId = useAgentId();
73
67
  const list = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
@@ -234,18 +228,6 @@ export const useControls = ({
234
228
  ),
235
229
  type: 'group',
236
230
  },
237
- {
238
- type: 'divider',
239
- },
240
- {
241
- extra: <Icon icon={ArrowRight} />,
242
- icon: Store,
243
- key: 'plugin-store',
244
- label: t('tools.plugins.store'),
245
- onClick: () => {
246
- setModalOpen(true);
247
- },
248
- },
249
231
  ];
250
232
 
251
233
  // 已安装 tab 的 items - 只显示已安装的插件
@@ -21,7 +21,7 @@ import { preferenceSelectors } from '@/store/user/selectors';
21
21
 
22
22
  import { useAgentId } from '../../hooks/useAgentId';
23
23
  import Action from '../components/Action';
24
- import CheckboxItem from '../components/CheckbokWithLoading';
24
+ import CheckboxItem from '../components/CheckboxWithLoading';
25
25
 
26
26
  const hotArea = css`
27
27
  &::before {
@@ -84,6 +84,7 @@ const ActionPopover = memo<ActionPopoverProps>(
84
84
  content: contentClassName,
85
85
  }}
86
86
  content={popoverContent}
87
+ nativeButton={false}
87
88
  placement={isMobile ? 'top' : placement}
88
89
  styles={{
89
90
  ...(typeof resolvedStyles === 'object' ? resolvedStyles : {}),
@@ -0,0 +1,60 @@
1
+ import { Center, Checkbox, Flexbox, Icon } from '@lobehub/ui';
2
+ import { Loader2 } from 'lucide-react';
3
+ import { type ReactNode, memo, useState } from 'react';
4
+
5
+ export interface CheckboxItemProps {
6
+ checked?: boolean;
7
+ hasPadding?: boolean;
8
+ id: string;
9
+ label?: ReactNode;
10
+ onUpdate: (id: string, enabled: boolean) => Promise<void>;
11
+ }
12
+
13
+ const CheckboxItem = memo<CheckboxItemProps>(
14
+ ({ id, onUpdate, label, checked, hasPadding = true }) => {
15
+ const [loading, setLoading] = useState(false);
16
+
17
+ const updateState = async () => {
18
+ setLoading(true);
19
+ await onUpdate(id, !checked);
20
+ setLoading(false);
21
+ };
22
+
23
+ return (
24
+ <Flexbox
25
+ align={'center'}
26
+ gap={24}
27
+ horizontal
28
+ justify={'space-between'}
29
+ onClick={async (e) => {
30
+ e.stopPropagation();
31
+ updateState();
32
+ }}
33
+ style={
34
+ hasPadding
35
+ ? {
36
+ paddingLeft: 8,
37
+ }
38
+ : void 0
39
+ }
40
+ >
41
+ {label || id}
42
+ {loading ? (
43
+ <Center width={18}>
44
+ <Icon icon={Loader2} spin />
45
+ </Center>
46
+ ) : (
47
+ <Checkbox
48
+ checked={checked}
49
+ onClick={async (e) => {
50
+ e.stopPropagation();
51
+ await updateState();
52
+ }}
53
+ />
54
+ )}
55
+ </Flexbox>
56
+ );
57
+ },
58
+ );
59
+
60
+ export default CheckboxItem;
@@ -43,6 +43,7 @@ const ModelSwitchPanel = memo<ModelSwitchPanelProps>(
43
43
  provider={providerProp}
44
44
  />
45
45
  }
46
+ nativeButton={false}
46
47
  onOpenChange={handleOpenChange}
47
48
  open={isOpen}
48
49
  placement={placement}
@@ -1,53 +0,0 @@
1
- import { Center, Flexbox, Icon , Checkbox } from '@lobehub/ui';
2
- import { Loader2 } from 'lucide-react';
3
- import { type ReactNode, memo, useState } from 'react';
4
-
5
- export interface CheckboxItemProps {
6
- checked?: boolean;
7
- id: string;
8
- label?: ReactNode;
9
- onUpdate: (id: string, enabled: boolean) => Promise<void>;
10
- }
11
-
12
- const CheckboxItem = memo<CheckboxItemProps>(({ id, onUpdate, label, checked }) => {
13
- const [loading, setLoading] = useState(false);
14
-
15
- const updateState = async () => {
16
- setLoading(true);
17
- await onUpdate(id, !checked);
18
- setLoading(false);
19
- };
20
-
21
- return (
22
- <Flexbox
23
- align={'center'}
24
- gap={24}
25
- horizontal
26
- justify={'space-between'}
27
- onClick={async (e) => {
28
- e.stopPropagation();
29
- updateState();
30
- }}
31
- style={{
32
- paddingLeft: 8,
33
- }}
34
- >
35
- {label || id}
36
- {loading ? (
37
- <Center width={18}>
38
- <Icon icon={Loader2} spin />
39
- </Center>
40
- ) : (
41
- <Checkbox
42
- checked={checked}
43
- onClick={async (e) => {
44
- e.stopPropagation();
45
- await updateState();
46
- }}
47
- />
48
- )}
49
- </Flexbox>
50
- );
51
- });
52
-
53
- export default CheckboxItem;