@lobehub/lobehub 2.0.0-next.265 → 2.0.0-next.267
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.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +14 -0
- package/e2e/CLAUDE.md +34 -73
- package/e2e/docs/local-setup.md +67 -219
- package/e2e/scripts/setup.ts +529 -0
- package/e2e/src/features/home/sidebarAgent.feature +62 -0
- package/e2e/src/features/home/sidebarGroup.feature +62 -0
- package/e2e/src/steps/home/sidebarAgent.steps.ts +373 -0
- package/e2e/src/steps/home/sidebarGroup.steps.ts +168 -0
- package/e2e/src/steps/hooks.ts +2 -0
- package/locales/ar/chat.json +1 -0
- package/locales/ar/modelProvider.json +20 -0
- package/locales/ar/models.json +33 -10
- package/locales/ar/plugin.json +1 -0
- package/locales/ar/providers.json +1 -0
- package/locales/ar/setting.json +2 -0
- package/locales/bg-BG/chat.json +1 -0
- package/locales/bg-BG/modelProvider.json +20 -0
- package/locales/bg-BG/models.json +27 -7
- package/locales/bg-BG/plugin.json +1 -0
- package/locales/bg-BG/providers.json +1 -0
- package/locales/bg-BG/setting.json +2 -0
- package/locales/de-DE/chat.json +1 -0
- package/locales/de-DE/modelProvider.json +20 -0
- package/locales/de-DE/models.json +44 -10
- package/locales/de-DE/plugin.json +1 -0
- package/locales/de-DE/providers.json +1 -0
- package/locales/de-DE/setting.json +2 -0
- package/locales/en-US/chat.json +1 -0
- package/locales/en-US/modelProvider.json +20 -0
- package/locales/en-US/models.json +10 -10
- package/locales/en-US/providers.json +1 -0
- package/locales/en-US/setting.json +2 -1
- package/locales/es-ES/chat.json +1 -0
- package/locales/es-ES/modelProvider.json +20 -0
- package/locales/es-ES/models.json +53 -10
- package/locales/es-ES/plugin.json +1 -0
- package/locales/es-ES/providers.json +1 -0
- package/locales/es-ES/setting.json +2 -0
- package/locales/fa-IR/chat.json +1 -0
- package/locales/fa-IR/modelProvider.json +20 -0
- package/locales/fa-IR/models.json +33 -10
- package/locales/fa-IR/plugin.json +1 -0
- package/locales/fa-IR/providers.json +1 -0
- package/locales/fa-IR/setting.json +2 -0
- package/locales/fr-FR/chat.json +1 -0
- package/locales/fr-FR/modelProvider.json +20 -0
- package/locales/fr-FR/models.json +27 -7
- package/locales/fr-FR/plugin.json +1 -0
- package/locales/fr-FR/providers.json +1 -0
- package/locales/fr-FR/setting.json +2 -0
- package/locales/it-IT/chat.json +1 -0
- package/locales/it-IT/modelProvider.json +20 -0
- package/locales/it-IT/models.json +10 -10
- package/locales/it-IT/plugin.json +1 -0
- package/locales/it-IT/providers.json +1 -0
- package/locales/it-IT/setting.json +2 -0
- package/locales/ja-JP/chat.json +1 -0
- package/locales/ja-JP/modelProvider.json +20 -0
- package/locales/ja-JP/models.json +5 -10
- package/locales/ja-JP/plugin.json +1 -0
- package/locales/ja-JP/providers.json +1 -0
- package/locales/ja-JP/setting.json +2 -0
- package/locales/ko-KR/chat.json +1 -0
- package/locales/ko-KR/modelProvider.json +20 -0
- package/locales/ko-KR/models.json +36 -10
- package/locales/ko-KR/plugin.json +1 -0
- package/locales/ko-KR/providers.json +1 -0
- package/locales/ko-KR/setting.json +2 -0
- package/locales/nl-NL/chat.json +1 -0
- package/locales/nl-NL/modelProvider.json +20 -0
- package/locales/nl-NL/models.json +35 -4
- package/locales/nl-NL/plugin.json +1 -0
- package/locales/nl-NL/providers.json +1 -0
- package/locales/nl-NL/setting.json +2 -0
- package/locales/pl-PL/chat.json +1 -0
- package/locales/pl-PL/modelProvider.json +20 -0
- package/locales/pl-PL/models.json +37 -7
- package/locales/pl-PL/plugin.json +1 -0
- package/locales/pl-PL/providers.json +1 -0
- package/locales/pl-PL/setting.json +2 -0
- package/locales/pt-BR/chat.json +1 -0
- package/locales/pt-BR/modelProvider.json +20 -0
- package/locales/pt-BR/models.json +51 -9
- package/locales/pt-BR/plugin.json +1 -0
- package/locales/pt-BR/providers.json +1 -0
- package/locales/pt-BR/setting.json +2 -0
- package/locales/ru-RU/chat.json +1 -0
- package/locales/ru-RU/modelProvider.json +20 -0
- package/locales/ru-RU/models.json +48 -7
- package/locales/ru-RU/plugin.json +1 -0
- package/locales/ru-RU/providers.json +1 -0
- package/locales/ru-RU/setting.json +2 -0
- package/locales/tr-TR/chat.json +1 -0
- package/locales/tr-TR/modelProvider.json +20 -0
- package/locales/tr-TR/models.json +48 -7
- package/locales/tr-TR/plugin.json +1 -0
- package/locales/tr-TR/providers.json +1 -0
- package/locales/tr-TR/setting.json +2 -0
- package/locales/vi-VN/chat.json +1 -0
- package/locales/vi-VN/modelProvider.json +20 -0
- package/locales/vi-VN/models.json +5 -5
- package/locales/vi-VN/plugin.json +1 -0
- package/locales/vi-VN/providers.json +1 -0
- package/locales/vi-VN/setting.json +2 -0
- package/locales/zh-CN/modelProvider.json +20 -20
- package/locales/zh-CN/models.json +49 -8
- package/locales/zh-CN/providers.json +1 -0
- package/locales/zh-CN/setting.json +2 -1
- package/locales/zh-TW/chat.json +1 -0
- package/locales/zh-TW/modelProvider.json +20 -0
- package/locales/zh-TW/models.json +29 -10
- package/locales/zh-TW/plugin.json +1 -0
- package/locales/zh-TW/providers.json +1 -0
- package/locales/zh-TW/setting.json +2 -0
- package/package.json +3 -3
- package/packages/utils/src/multimodalContent.test.ts +302 -0
- package/packages/utils/src/server/__tests__/sse.test.ts +353 -0
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Body.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/CronTopicGroup.tsx +84 -0
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/{Topic/CronTopicList → Cron}/CronTopicItem.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/{Topic/CronTopicList → Cron}/index.tsx +23 -33
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/Editing.tsx +12 -49
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/index.tsx +3 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/TopicListContent/ThreadList/ThreadItem/Editing.tsx +12 -40
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts +5 -1
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/CronJobCards.tsx +1 -1
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/CronJobForm.tsx +1 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/AddGroupMemberModal/AvailableAgentList.tsx +0 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/AddGroupMemberModal/index.tsx +5 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentGroupItem/Editing.tsx +4 -11
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentGroupItem/index.tsx +3 -3
- package/src/components/InlineRename/index.tsx +121 -0
- package/src/features/ChatInput/ActionBar/Params/Controls.tsx +42 -7
- package/src/features/NavPanel/components/NavItem.tsx +1 -1
- package/src/locales/default/setting.ts +2 -0
- package/src/store/agent/slices/cron/action.ts +108 -0
- package/src/store/agent/slices/cron/index.ts +1 -0
- package/src/store/agent/store.ts +3 -0
- package/src/store/home/slices/sidebarUI/action.ts +9 -0
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/CronTopicList/CronTopicGroup.tsx +0 -74
- package/src/app/[variants]/(main)/group/features/ChangelogModal.tsx +0 -11
- package/src/hooks/useFetchCronTopicsWithJobInfo.ts +0 -56
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { AccordionItem, ActionIcon, Flexbox, Icon, Text } from '@lobehub/ui';
|
|
4
|
+
import { Settings2Icon, TimerIcon, TimerOffIcon } from 'lucide-react';
|
|
5
|
+
import { memo, useCallback } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { useParams } from 'react-router-dom';
|
|
8
|
+
|
|
9
|
+
import { useRouter } from '@/app/[variants]/(main)/hooks/useRouter';
|
|
10
|
+
import type { AgentCronJob } from '@/database/schemas/agentCronJob';
|
|
11
|
+
|
|
12
|
+
import CronTopicItem from './CronTopicItem';
|
|
13
|
+
|
|
14
|
+
interface CronTopicGroupProps {
|
|
15
|
+
cronJob: AgentCronJob | null;
|
|
16
|
+
cronJobId: string;
|
|
17
|
+
topics: Array<{
|
|
18
|
+
createdAt: Date | string;
|
|
19
|
+
favorite?: boolean | null;
|
|
20
|
+
historySummary?: string | null;
|
|
21
|
+
id: string;
|
|
22
|
+
metadata?: any;
|
|
23
|
+
title?: string | null;
|
|
24
|
+
trigger?: string | null;
|
|
25
|
+
updatedAt: Date | string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const CronTopicGroup = memo<CronTopicGroupProps>(({ cronJob, cronJobId, topics }) => {
|
|
30
|
+
const { t } = useTranslation('setting');
|
|
31
|
+
const { aid, cronId } = useParams<{ aid?: string; cronId?: string }>();
|
|
32
|
+
const router = useRouter();
|
|
33
|
+
|
|
34
|
+
const handleOpenCronJob = useCallback(() => {
|
|
35
|
+
if (!aid) return;
|
|
36
|
+
router.push(`/agent/${aid}/cron/${cronJobId}`);
|
|
37
|
+
}, [aid, cronJobId, router]);
|
|
38
|
+
|
|
39
|
+
const cronJobName = cronJob?.name || t('agentCronJobs.unnamedTask');
|
|
40
|
+
const isEnabled = cronJob?.enabled ?? false;
|
|
41
|
+
const isActive = cronId === cronJobId;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<AccordionItem
|
|
45
|
+
action={
|
|
46
|
+
<ActionIcon
|
|
47
|
+
icon={Settings2Icon}
|
|
48
|
+
onClick={handleOpenCronJob}
|
|
49
|
+
size="small"
|
|
50
|
+
title={t('agentCronJobs.editJob')}
|
|
51
|
+
/>
|
|
52
|
+
}
|
|
53
|
+
itemKey={cronJobId}
|
|
54
|
+
paddingBlock={4}
|
|
55
|
+
paddingInline={'8px 4px'}
|
|
56
|
+
title={
|
|
57
|
+
<Flexbox align="center" gap={6} height={24} horizontal style={{ overflow: 'hidden' }}>
|
|
58
|
+
<Icon icon={isEnabled ? TimerIcon : TimerOffIcon} style={{ opacity: 0.5 }} />
|
|
59
|
+
<Text ellipsis style={{ flex: 1 }} type={isActive ? undefined : 'secondary'}>
|
|
60
|
+
{cronJobName}
|
|
61
|
+
</Text>
|
|
62
|
+
{topics.length > 0 && (
|
|
63
|
+
<Text fontSize={11} type="secondary">
|
|
64
|
+
{topics.length}
|
|
65
|
+
</Text>
|
|
66
|
+
)}
|
|
67
|
+
</Flexbox>
|
|
68
|
+
}
|
|
69
|
+
variant={isActive ? 'filled' : 'borderless'}
|
|
70
|
+
>
|
|
71
|
+
<Flexbox gap={1} paddingBlock={1}>
|
|
72
|
+
{topics.length > 0 ? (
|
|
73
|
+
topics.map((topic) => <CronTopicItem key={topic.id} topic={topic} />)
|
|
74
|
+
) : (
|
|
75
|
+
<Text fontSize={12} style={{ padding: '8px 12px' }} type="secondary">
|
|
76
|
+
{t('agentCronJobs.noExecutionResults')}
|
|
77
|
+
</Text>
|
|
78
|
+
)}
|
|
79
|
+
</Flexbox>
|
|
80
|
+
</AccordionItem>
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export default CronTopicGroup;
|
package/src/app/[variants]/(main)/agent/_layout/Sidebar/{Topic/CronTopicList → Cron}/index.tsx
RENAMED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
4
|
-
import { AccordionItem, ActionIcon, Flexbox,
|
|
5
|
-
import {
|
|
6
|
-
import { Calendar, Plus } from 'lucide-react';
|
|
4
|
+
import { Accordion, AccordionItem, ActionIcon, Flexbox, Text } from '@lobehub/ui';
|
|
5
|
+
import { Plus } from 'lucide-react';
|
|
7
6
|
import { memo, useCallback } from 'react';
|
|
8
7
|
import { useTranslation } from 'react-i18next';
|
|
9
8
|
import urlJoin from 'url-join';
|
|
@@ -11,9 +10,7 @@ import urlJoin from 'url-join';
|
|
|
11
10
|
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
12
11
|
import EmptyNavItem from '@/features/NavPanel/components/EmptyNavItem';
|
|
13
12
|
import SkeletonList from '@/features/NavPanel/components/SkeletonList';
|
|
14
|
-
import { useFetchCronTopicsWithJobInfo } from '@/hooks/useFetchCronTopicsWithJobInfo';
|
|
15
13
|
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
16
|
-
import { agentCronJobService } from '@/services/agentCronJob';
|
|
17
14
|
import { useAgentStore } from '@/store/agent';
|
|
18
15
|
|
|
19
16
|
import CronTopicGroup from './CronTopicGroup';
|
|
@@ -25,33 +22,22 @@ interface CronTopicListProps {
|
|
|
25
22
|
const CronTopicList = memo<CronTopicListProps>(({ itemKey }) => {
|
|
26
23
|
const { t } = useTranslation('setting');
|
|
27
24
|
const router = useQueryRoute();
|
|
28
|
-
const agentId = useAgentStore((s) =>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
const [agentId, createAgentCronJob, useFetchCronTopicsWithJobInfo] = useAgentStore((s) => [
|
|
26
|
+
s.activeAgentId,
|
|
27
|
+
s.createAgentCronJob,
|
|
28
|
+
s.useFetchCronTopicsWithJobInfo,
|
|
29
|
+
]);
|
|
30
|
+
const { data: cronTopicsGroupsWithJobInfo = [], isLoading } =
|
|
31
|
+
useFetchCronTopicsWithJobInfo(agentId);
|
|
34
32
|
|
|
35
33
|
const handleCreateCronJob = useCallback(async () => {
|
|
36
34
|
if (!agentId) return;
|
|
37
|
-
try {
|
|
38
|
-
const result = await agentCronJobService.create({
|
|
39
|
-
agentId,
|
|
40
|
-
content: t('agentCronJobs.form.content.placeholder') || 'This is a cron job',
|
|
41
|
-
cronPattern: '*/30 * * * *',
|
|
42
|
-
enabled: true,
|
|
43
|
-
name: t('agentCronJobs.addJob') || 'Cron Job Task',
|
|
44
|
-
});
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.error('Failed to create cron job:', error);
|
|
52
|
-
message.error('Failed to create scheduled task');
|
|
36
|
+
const cronJobId = await createAgentCronJob();
|
|
37
|
+
if (cronJobId) {
|
|
38
|
+
router.push(urlJoin('/agent', agentId, 'cron', cronJobId));
|
|
53
39
|
}
|
|
54
|
-
}, [agentId,
|
|
40
|
+
}, [agentId, createAgentCronJob, router]);
|
|
55
41
|
|
|
56
42
|
if (!ENABLE_BUSINESS_FEATURES) return null;
|
|
57
43
|
|
|
@@ -74,7 +60,6 @@ const CronTopicList = memo<CronTopicListProps>(({ itemKey }) => {
|
|
|
74
60
|
paddingInline={'8px 4px'}
|
|
75
61
|
title={
|
|
76
62
|
<Flexbox align="center" gap={4} horizontal>
|
|
77
|
-
<Icon icon={Calendar} size={12} />
|
|
78
63
|
<Text ellipsis fontSize={12} type={'secondary'} weight={500}>
|
|
79
64
|
{t('agentCronJobs.title')}
|
|
80
65
|
</Text>
|
|
@@ -96,7 +81,6 @@ const CronTopicList = memo<CronTopicListProps>(({ itemKey }) => {
|
|
|
96
81
|
paddingInline={'8px 4px'}
|
|
97
82
|
title={
|
|
98
83
|
<Flexbox align="center" gap={4} horizontal>
|
|
99
|
-
<Icon icon={Calendar} size={12} />
|
|
100
84
|
<Text ellipsis fontSize={12} type={'secondary'} weight={500}>
|
|
101
85
|
{t('agentCronJobs.title')}
|
|
102
86
|
</Text>
|
|
@@ -108,6 +92,8 @@ const CronTopicList = memo<CronTopicListProps>(({ itemKey }) => {
|
|
|
108
92
|
);
|
|
109
93
|
}
|
|
110
94
|
|
|
95
|
+
const totalCronJobs = cronTopicsGroupsWithJobInfo.length;
|
|
96
|
+
|
|
111
97
|
return (
|
|
112
98
|
<AccordionItem
|
|
113
99
|
action={addAction}
|
|
@@ -116,14 +102,18 @@ const CronTopicList = memo<CronTopicListProps>(({ itemKey }) => {
|
|
|
116
102
|
paddingInline={'8px 4px'}
|
|
117
103
|
title={
|
|
118
104
|
<Flexbox align="center" gap={4} horizontal>
|
|
119
|
-
<Icon icon={Calendar} size={12} />
|
|
120
105
|
<Text ellipsis fontSize={12} type={'secondary'} weight={500}>
|
|
121
|
-
{
|
|
106
|
+
{t('agentCronJobs.title')}
|
|
122
107
|
</Text>
|
|
108
|
+
{totalCronJobs > 0 && (
|
|
109
|
+
<Text fontSize={11} type="secondary">
|
|
110
|
+
{totalCronJobs}
|
|
111
|
+
</Text>
|
|
112
|
+
)}
|
|
123
113
|
</Flexbox>
|
|
124
114
|
}
|
|
125
115
|
>
|
|
126
|
-
<
|
|
116
|
+
<Accordion defaultExpandedKeys={cronTopicsGroupsWithJobInfo.map((g) => g.cronJobId)} gap={2}>
|
|
127
117
|
{cronTopicsGroupsWithJobInfo.map((group) => (
|
|
128
118
|
<CronTopicGroup
|
|
129
119
|
cronJob={group.cronJob}
|
|
@@ -132,7 +122,7 @@ const CronTopicList = memo<CronTopicListProps>(({ itemKey }) => {
|
|
|
132
122
|
topics={group.topics}
|
|
133
123
|
/>
|
|
134
124
|
))}
|
|
135
|
-
</
|
|
125
|
+
</Accordion>
|
|
136
126
|
</AccordionItem>
|
|
137
127
|
);
|
|
138
128
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { InputRef } from 'antd';
|
|
3
|
-
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { memo, useCallback } from 'react';
|
|
4
2
|
|
|
3
|
+
import InlineRename from '@/components/InlineRename';
|
|
5
4
|
import { useChatStore } from '@/store/chat';
|
|
6
5
|
|
|
7
6
|
interface EditingProps {
|
|
@@ -10,27 +9,14 @@ interface EditingProps {
|
|
|
10
9
|
toggleEditing: (visible?: boolean) => void;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
function FocusableInput({ ...props }: InputProps) {
|
|
14
|
-
const ref = useRef<InputRef>(null);
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
queueMicrotask(() => {
|
|
17
|
-
if (ref.current) {
|
|
18
|
-
ref.current.input?.focus();
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}, []);
|
|
22
|
-
return <Input {...props} ref={ref} />;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
12
|
const Editing = memo<EditingProps>(({ id, title, toggleEditing }) => {
|
|
26
|
-
const [newTitle, setNewTitle] = useState(title);
|
|
27
13
|
const [editing, updateTopicTitle] = useChatStore((s) => [
|
|
28
14
|
s.topicRenamingId === id,
|
|
29
15
|
s.updateTopicTitle,
|
|
30
16
|
]);
|
|
31
17
|
|
|
32
|
-
const
|
|
33
|
-
|
|
18
|
+
const handleSave = useCallback(
|
|
19
|
+
async (newTitle: string) => {
|
|
34
20
|
try {
|
|
35
21
|
// Set loading state
|
|
36
22
|
useChatStore.setState(
|
|
@@ -53,40 +39,17 @@ const Editing = memo<EditingProps>(({ id, title, toggleEditing }) => {
|
|
|
53
39
|
'clearTopicUpdating',
|
|
54
40
|
);
|
|
55
41
|
}
|
|
56
|
-
}
|
|
57
|
-
|
|
42
|
+
},
|
|
43
|
+
[id, updateTopicTitle],
|
|
44
|
+
);
|
|
58
45
|
|
|
59
46
|
return (
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
defaultValue={title}
|
|
64
|
-
onBlur={handleUpdate}
|
|
65
|
-
onChange={(e) => setNewTitle(e.target.value)}
|
|
66
|
-
onClick={(e) => e.stopPropagation()}
|
|
67
|
-
onPressEnter={() => {
|
|
68
|
-
handleUpdate();
|
|
69
|
-
toggleEditing(false);
|
|
70
|
-
}}
|
|
71
|
-
/>
|
|
72
|
-
}
|
|
73
|
-
onOpenChange={(open) => {
|
|
74
|
-
if (!open) handleUpdate();
|
|
75
|
-
|
|
76
|
-
toggleEditing(open);
|
|
77
|
-
}}
|
|
47
|
+
<InlineRename
|
|
48
|
+
onOpenChange={(open) => toggleEditing(open)}
|
|
49
|
+
onSave={handleSave}
|
|
78
50
|
open={editing}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
content: {
|
|
82
|
-
padding: 4,
|
|
83
|
-
width: 320,
|
|
84
|
-
},
|
|
85
|
-
}}
|
|
86
|
-
trigger="click"
|
|
87
|
-
>
|
|
88
|
-
<div />
|
|
89
|
-
</Popover>
|
|
51
|
+
title={title}
|
|
52
|
+
/>
|
|
90
53
|
);
|
|
91
54
|
});
|
|
92
55
|
|
|
@@ -18,6 +18,8 @@ import AllTopicsDrawer from '../AllTopicsDrawer';
|
|
|
18
18
|
import ByTimeMode from '../TopicListContent/ByTimeMode';
|
|
19
19
|
import FlatMode from '../TopicListContent/FlatMode';
|
|
20
20
|
|
|
21
|
+
const fetchParams = { excludeTriggers: ['cron'] };
|
|
22
|
+
|
|
21
23
|
const TopicList = memo(() => {
|
|
22
24
|
const { t } = useTranslation('topic');
|
|
23
25
|
const router = useQueryRoute();
|
|
@@ -32,7 +34,7 @@ const TopicList = memo(() => {
|
|
|
32
34
|
|
|
33
35
|
const [topicDisplayMode] = useUserStore((s) => [preferenceSelectors.topicDisplayMode(s)]);
|
|
34
36
|
|
|
35
|
-
useFetchTopics(
|
|
37
|
+
useFetchTopics(fetchParams);
|
|
36
38
|
|
|
37
39
|
// Show skeleton when current session's topic data is not yet loaded
|
|
38
40
|
if (isUndefinedTopics) return <SkeletonList />;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { memo, useCallback, useState } from 'react';
|
|
1
|
+
import { memo, useCallback } from 'react';
|
|
3
2
|
|
|
3
|
+
import InlineRename from '@/components/InlineRename';
|
|
4
4
|
import { useChatStore } from '@/store/chat';
|
|
5
5
|
|
|
6
6
|
interface EditingProps {
|
|
@@ -10,53 +10,25 @@ interface EditingProps {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const Editing = memo<EditingProps>(({ id, title, toggleEditing }) => {
|
|
13
|
-
const [newTitle, setNewTitle] = useState(title);
|
|
14
13
|
const [editing, updateThreadTitle] = useChatStore((s) => [
|
|
15
14
|
s.threadRenamingId === id,
|
|
16
15
|
s.updateThreadTitle,
|
|
17
16
|
]);
|
|
18
17
|
|
|
19
|
-
const
|
|
20
|
-
|
|
18
|
+
const handleSave = useCallback(
|
|
19
|
+
async (newTitle: string) => {
|
|
21
20
|
await updateThreadTitle(id, newTitle);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
},
|
|
22
|
+
[id, updateThreadTitle],
|
|
23
|
+
);
|
|
25
24
|
|
|
26
25
|
return (
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
autoFocus
|
|
31
|
-
defaultValue={title}
|
|
32
|
-
onBlur={() => {
|
|
33
|
-
handleUpdate();
|
|
34
|
-
toggleEditing(false);
|
|
35
|
-
}}
|
|
36
|
-
onChange={(e) => setNewTitle(e.target.value)}
|
|
37
|
-
onClick={(e) => e.stopPropagation()}
|
|
38
|
-
onPressEnter={() => {
|
|
39
|
-
handleUpdate();
|
|
40
|
-
toggleEditing(false);
|
|
41
|
-
}}
|
|
42
|
-
/>
|
|
43
|
-
}
|
|
44
|
-
onOpenChange={(open) => {
|
|
45
|
-
if (!open) handleUpdate();
|
|
46
|
-
toggleEditing(open);
|
|
47
|
-
}}
|
|
26
|
+
<InlineRename
|
|
27
|
+
onOpenChange={(open) => toggleEditing(open)}
|
|
28
|
+
onSave={handleSave}
|
|
48
29
|
open={editing}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
content: {
|
|
52
|
-
padding: 4,
|
|
53
|
-
width: 320,
|
|
54
|
-
},
|
|
55
|
-
}}
|
|
56
|
-
trigger="click"
|
|
57
|
-
>
|
|
58
|
-
<div />
|
|
59
|
-
</Popover>
|
|
30
|
+
title={title}
|
|
31
|
+
/>
|
|
60
32
|
);
|
|
61
33
|
});
|
|
62
34
|
|
|
@@ -32,7 +32,11 @@ export const useTopicNavigation = () => {
|
|
|
32
32
|
(topicId?: string) => {
|
|
33
33
|
// If in agent sub-route, navigate back to agent chat first
|
|
34
34
|
if (isInAgentSubRoute() && activeAgentId) {
|
|
35
|
-
|
|
35
|
+
const basePath = urlJoin('/agent', activeAgentId as string);
|
|
36
|
+
// Include topicId in URL when navigating from sub-route
|
|
37
|
+
router.push(topicId ? `${basePath}?topic=${topicId}` : basePath);
|
|
38
|
+
toggleConfig(false);
|
|
39
|
+
return;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
switchTopic(topicId);
|
|
@@ -99,7 +99,7 @@ const CronJobCards = memo<CronJobCardsProps>(({ cronJobs, loading, onDelete, onE
|
|
|
99
99
|
whiteSpace: 'nowrap',
|
|
100
100
|
}}
|
|
101
101
|
>
|
|
102
|
-
{job.name || '
|
|
102
|
+
{job.name || t('agentCronJobs.unnamedTask')}
|
|
103
103
|
</span>
|
|
104
104
|
<Badge status={statusInfo.status} />
|
|
105
105
|
</Flexbox>
|
|
@@ -96,7 +96,7 @@ const CronJobForm = memo<CronJobFormProps>(({ editingJob, formRef, onSubmit }) =
|
|
|
96
96
|
const data: CronJobFormData = {
|
|
97
97
|
content: values.content,
|
|
98
98
|
cronPattern: values.cronPattern,
|
|
99
|
-
enabled:
|
|
99
|
+
enabled: false,
|
|
100
100
|
executionConditions: Object.keys(executionConditions).length > 0 ? executionConditions : null,
|
|
101
101
|
maxExecutions: values.maxExecutions || null,
|
|
102
102
|
name: values.name,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Button, Flexbox, Modal } from '@lobehub/ui';
|
|
4
|
+
import { Divider } from 'antd';
|
|
4
5
|
import { createStaticStyles } from 'antd-style';
|
|
5
6
|
import { memo, useEffect, useMemo, useState } from 'react';
|
|
6
7
|
import { useTranslation } from 'react-i18next';
|
|
@@ -19,6 +20,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
19
20
|
flex-direction: row;
|
|
20
21
|
|
|
21
22
|
height: 500px;
|
|
23
|
+
padding: 12px;
|
|
22
24
|
border: 1px solid ${cssVar.colorBorderSecondary};
|
|
23
25
|
border-radius: ${cssVar.borderRadius}px;
|
|
24
26
|
`,
|
|
@@ -104,10 +106,12 @@ const AddGroupMemberModal = memo<AddGroupMemberModalProps>(
|
|
|
104
106
|
title={t('memberSelection.addMember')}
|
|
105
107
|
width={800}
|
|
106
108
|
>
|
|
107
|
-
<Flexbox className={styles.container} horizontal>
|
|
109
|
+
<Flexbox className={styles.container} gap={8} horizontal>
|
|
108
110
|
{/* Left Column - Available Agents */}
|
|
109
111
|
<AvailableAgentList agents={availableAgents} isLoading={isLoadingAgents} />
|
|
110
112
|
|
|
113
|
+
<Divider orientation={'vertical'} style={{ height: '100%' }} />
|
|
114
|
+
|
|
111
115
|
{/* Right Column - Selected Agents */}
|
|
112
116
|
<SelectedAgentList agents={allAgents} />
|
|
113
117
|
</Flexbox>
|
|
@@ -10,7 +10,7 @@ interface EditingProps {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const Editing = memo<EditingProps>(({ id, title, toggleEditing }) => {
|
|
13
|
-
const editing = useHomeStore((s) => s.
|
|
13
|
+
const editing = useHomeStore((s) => s.groupRenamingId === id);
|
|
14
14
|
|
|
15
15
|
const [newTitle, setNewTitle] = useState(title);
|
|
16
16
|
|
|
@@ -19,17 +19,10 @@ const Editing = memo<EditingProps>(({ id, title, toggleEditing }) => {
|
|
|
19
19
|
|
|
20
20
|
if (hasChanges) {
|
|
21
21
|
try {
|
|
22
|
-
|
|
23
|
-
useHomeStore.getState().
|
|
24
|
-
|
|
25
|
-
// TODO: Add group title update logic here
|
|
26
|
-
// await updateGroupTitle(id, newTitle);
|
|
27
|
-
|
|
28
|
-
// Refresh agent list to update sidebar display
|
|
29
|
-
await useHomeStore.getState().refreshAgentList();
|
|
22
|
+
useHomeStore.getState().setGroupUpdatingId(id);
|
|
23
|
+
await useHomeStore.getState().renameAgentGroup(id, newTitle);
|
|
30
24
|
} finally {
|
|
31
|
-
|
|
32
|
-
useHomeStore.getState().setAgentUpdatingId(null);
|
|
25
|
+
useHomeStore.getState().setGroupUpdatingId(null);
|
|
33
26
|
}
|
|
34
27
|
}
|
|
35
28
|
toggleEditing(false);
|
|
@@ -30,8 +30,8 @@ const GroupItem = memo<GroupItemProps>(({ item, style, className }) => {
|
|
|
30
30
|
|
|
31
31
|
// Get UI state from homeStore (editing, updating)
|
|
32
32
|
const [editing, isUpdating] = useHomeStore((s) => [
|
|
33
|
-
s.
|
|
34
|
-
s.
|
|
33
|
+
s.groupRenamingId === id,
|
|
34
|
+
s.groupUpdatingId === id,
|
|
35
35
|
]);
|
|
36
36
|
|
|
37
37
|
// Get display title with fallback
|
|
@@ -63,7 +63,7 @@ const GroupItem = memo<GroupItemProps>(({ item, style, className }) => {
|
|
|
63
63
|
|
|
64
64
|
const toggleEditing = useCallback(
|
|
65
65
|
(visible?: boolean) => {
|
|
66
|
-
useHomeStore.getState().
|
|
66
|
+
useHomeStore.getState().setGroupRenamingId(visible ? id : null);
|
|
67
67
|
},
|
|
68
68
|
[id],
|
|
69
69
|
);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Input, type InputProps, Popover } from '@lobehub/ui';
|
|
4
|
+
import type { InputRef, PopoverProps } from 'antd';
|
|
5
|
+
import { KeyboardEvent, memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
function FocusableInput(props: InputProps) {
|
|
8
|
+
const ref = useRef<InputRef>(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
queueMicrotask(() => {
|
|
11
|
+
ref.current?.input?.focus();
|
|
12
|
+
});
|
|
13
|
+
}, []);
|
|
14
|
+
return <Input {...props} ref={ref} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface InlineRenameProps {
|
|
18
|
+
/**
|
|
19
|
+
* Callback when editing is cancelled (Escape key)
|
|
20
|
+
*/
|
|
21
|
+
onCancel?: () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Callback when open state changes
|
|
24
|
+
*/
|
|
25
|
+
onOpenChange: (open: boolean) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Callback to save the new title
|
|
28
|
+
*/
|
|
29
|
+
onSave: (newTitle: string) => void | Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the popover is open (editing mode)
|
|
32
|
+
*/
|
|
33
|
+
open: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Popover placement
|
|
36
|
+
*/
|
|
37
|
+
placement?: PopoverProps['placement'];
|
|
38
|
+
/**
|
|
39
|
+
* Current title
|
|
40
|
+
*/
|
|
41
|
+
title: string;
|
|
42
|
+
/**
|
|
43
|
+
* Popover width
|
|
44
|
+
*/
|
|
45
|
+
width?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const InlineRename = memo<InlineRenameProps>(
|
|
49
|
+
({ open, title, onOpenChange, onSave, onCancel, placement = 'bottomLeft', width = 320 }) => {
|
|
50
|
+
const [newTitle, setNewTitle] = useState(title);
|
|
51
|
+
const savedRef = useRef(false);
|
|
52
|
+
|
|
53
|
+
// Reset state when opening
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (open) {
|
|
56
|
+
setNewTitle(title);
|
|
57
|
+
savedRef.current = false;
|
|
58
|
+
}
|
|
59
|
+
}, [open, title]);
|
|
60
|
+
|
|
61
|
+
const handleSave = useCallback(async () => {
|
|
62
|
+
if (savedRef.current) return;
|
|
63
|
+
|
|
64
|
+
if (newTitle && title !== newTitle) {
|
|
65
|
+
savedRef.current = true;
|
|
66
|
+
await onSave(newTitle);
|
|
67
|
+
}
|
|
68
|
+
}, [newTitle, title, onSave]);
|
|
69
|
+
|
|
70
|
+
const handleClose = useCallback(() => {
|
|
71
|
+
onOpenChange(false);
|
|
72
|
+
}, [onOpenChange]);
|
|
73
|
+
|
|
74
|
+
const handleKeyDown = useCallback(
|
|
75
|
+
(e: KeyboardEvent) => {
|
|
76
|
+
if (e.key === 'Escape') {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
e.stopPropagation();
|
|
79
|
+
onCancel?.();
|
|
80
|
+
handleClose();
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
[onCancel, handleClose],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Popover
|
|
88
|
+
content={
|
|
89
|
+
<FocusableInput
|
|
90
|
+
defaultValue={title}
|
|
91
|
+
onBlur={handleSave}
|
|
92
|
+
onChange={(e) => setNewTitle(e.target.value)}
|
|
93
|
+
onClick={(e) => e.stopPropagation()}
|
|
94
|
+
onKeyDown={handleKeyDown}
|
|
95
|
+
onPressEnter={() => {
|
|
96
|
+
handleSave();
|
|
97
|
+
handleClose();
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
}
|
|
101
|
+
onOpenChange={(nextOpen) => {
|
|
102
|
+
if (!nextOpen) handleSave();
|
|
103
|
+
onOpenChange(nextOpen);
|
|
104
|
+
}}
|
|
105
|
+
open={open}
|
|
106
|
+
placement={placement}
|
|
107
|
+
styles={{
|
|
108
|
+
content: {
|
|
109
|
+
padding: 4,
|
|
110
|
+
width,
|
|
111
|
+
},
|
|
112
|
+
}}
|
|
113
|
+
trigger="click"
|
|
114
|
+
>
|
|
115
|
+
<div />
|
|
116
|
+
</Popover>
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
export default InlineRename;
|