@lobehub/lobehub 2.1.13 → 2.1.15
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/apps/desktop/src/main/menus/impls/linux.ts +1 -1
- package/apps/desktop/src/main/menus/impls/macOS.ts +8 -8
- package/apps/desktop/src/main/menus/impls/windows.ts +1 -1
- package/changelog/v2.json +18 -0
- package/docs/development/database-schema.dbml +1 -0
- package/locales/en-US/chat.json +2 -0
- package/locales/en-US/discover.json +9 -0
- package/locales/zh-CN/chat.json +2 -0
- package/locales/zh-CN/discover.json +9 -0
- package/package.json +1 -1
- package/packages/database/migrations/0076_add_message_group_index.sql +1 -0
- package/packages/database/migrations/meta/0076_snapshot.json +11270 -0
- package/packages/database/migrations/meta/_journal.json +8 -1
- package/packages/database/src/schemas/message.ts +1 -0
- package/packages/types/src/discover/assistants.ts +1 -0
- package/packages/types/src/discover/index.ts +8 -0
- package/packages/utils/src/chunkers/trimBatchProbe/trimBatchProbe.ts +1 -1
- package/packages/utils/src/pricing.ts +6 -6
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx +1 -2
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Header.tsx +3 -3
- package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +2 -0
- package/src/app/[variants]/(main)/community/(detail)/user/features/StatusFilter.tsx +36 -0
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserAgentList.tsx +69 -13
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserContent.tsx +0 -11
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupList.tsx +68 -14
- package/src/app/[variants]/(main)/community/(detail)/user/index.tsx +3 -1
- package/src/app/[variants]/(main)/community/(list)/agent/features/List/Item.tsx +2 -1
- package/src/features/Conversation/Messages/CompressedGroup/index.tsx +26 -6
- package/src/features/Conversation/store/slices/message/action/state.ts +25 -0
- package/src/locales/default/chat.ts +3 -0
- package/src/locales/default/discover.ts +12 -0
- package/src/server/routers/lambda/market/social.ts +1 -1
- package/src/server/routers/lambda/message.ts +37 -6
- package/src/server/services/discover/index.ts +57 -2
- package/src/server/services/message/index.ts +19 -0
- package/src/services/message/index.ts +14 -0
- package/src/services/models.ts +0 -1
- package/src/services/social.ts +1 -1
|
@@ -532,7 +532,14 @@
|
|
|
532
532
|
"when": 1769362978088,
|
|
533
533
|
"tag": "0075_add_user_memory_persona",
|
|
534
534
|
"breakpoints": true
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
"idx": 76,
|
|
538
|
+
"version": "7",
|
|
539
|
+
"when": 1770179814971,
|
|
540
|
+
"tag": "0076_add_message_group_index",
|
|
541
|
+
"breakpoints": true
|
|
535
542
|
}
|
|
536
543
|
],
|
|
537
544
|
"version": "6"
|
|
538
|
-
}
|
|
545
|
+
}
|
|
@@ -149,6 +149,7 @@ export const messages = pgTable(
|
|
|
149
149
|
index('messages_thread_id_idx').on(table.threadId),
|
|
150
150
|
index('messages_agent_id_idx').on(table.agentId),
|
|
151
151
|
index('messages_group_id_idx').on(table.groupId),
|
|
152
|
+
index('messages_message_group_id_idx').on(table.messageGroupId),
|
|
152
153
|
],
|
|
153
154
|
);
|
|
154
155
|
|
|
@@ -67,6 +67,14 @@ export interface DiscoverUserInfo {
|
|
|
67
67
|
export interface DiscoverUserProfile {
|
|
68
68
|
agentGroups?: DiscoverGroupAgentItem[];
|
|
69
69
|
agents: DiscoverAssistantItem[];
|
|
70
|
+
/**
|
|
71
|
+
* Agent groups favorited by the user
|
|
72
|
+
*/
|
|
73
|
+
favoriteAgentGroups?: DiscoverGroupAgentItem[];
|
|
74
|
+
/**
|
|
75
|
+
* Agents favorited by the user
|
|
76
|
+
*/
|
|
77
|
+
favoriteAgents?: DiscoverAssistantItem[];
|
|
70
78
|
/**
|
|
71
79
|
* Agent groups forked by the user
|
|
72
80
|
*/
|
|
@@ -202,7 +202,7 @@ export const handleSingle = async (
|
|
|
202
202
|
* to avoid breaking structured XML/JSON-like content mid-way.
|
|
203
203
|
*
|
|
204
204
|
* Bisection example (8 segments, keep newest):
|
|
205
|
-
* try 4 (fits?)
|
|
205
|
+
* try 4 (fits?) -> yes -> try 6 -> no -> try 5 -> yes => best=5
|
|
206
206
|
* if compact retry needed, repeat with build(true) and pick the better fit.
|
|
207
207
|
*
|
|
208
208
|
* This minimizes structural breakage by preferring whole built segments and only truncating the last one as a last resort.
|
|
@@ -2,9 +2,9 @@ import { Pricing, PricingUnit, PricingUnitName } from 'model-bank';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Internal helper to extract the displayed unit rate from a pricing unit by strategy
|
|
5
|
-
* - fixed
|
|
6
|
-
* - tiered
|
|
7
|
-
* - lookup
|
|
5
|
+
* - fixed: rate
|
|
6
|
+
* - tiered: tiers[0].rate
|
|
7
|
+
* - lookup: first price value
|
|
8
8
|
*/
|
|
9
9
|
const getRateFromUnit = (unit: PricingUnit): number | undefined => {
|
|
10
10
|
switch (unit.strategy) {
|
|
@@ -39,9 +39,9 @@ export const getUnitRateByName = (
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Get text input unit rate from pricing
|
|
42
|
-
* - fixed
|
|
43
|
-
* - tiered
|
|
44
|
-
* - lookup
|
|
42
|
+
* - fixed: rate
|
|
43
|
+
* - tiered: tiers[0].rate
|
|
44
|
+
* - lookup: Object.values(lookup.prices)[0]
|
|
45
45
|
*/
|
|
46
46
|
export function getTextInputUnitRate(pricing?: Pricing): number | undefined {
|
|
47
47
|
return getUnitRateByName(pricing, 'textInput');
|
|
@@ -33,7 +33,7 @@ const Nav = memo(() => {
|
|
|
33
33
|
const switchTopic = useChatStore((s) => s.switchTopic);
|
|
34
34
|
const [openNewTopicOrSaveTopic] = useChatStore((s) => [s.openNewTopicOrSaveTopic]);
|
|
35
35
|
|
|
36
|
-
const { mutate
|
|
36
|
+
const { mutate } = useActionSWR('openNewTopicOrSaveTopic', openNewTopicOrSaveTopic);
|
|
37
37
|
const handleNewTopic = () => {
|
|
38
38
|
// If in agent sub-route, navigate back to agent chat first
|
|
39
39
|
if (isProfileActive && agentId) {
|
|
@@ -46,7 +46,6 @@ const Nav = memo(() => {
|
|
|
46
46
|
<Flexbox gap={1} paddingInline={4}>
|
|
47
47
|
<NavItem
|
|
48
48
|
icon={MessageSquarePlusIcon}
|
|
49
|
-
loading={isValidating}
|
|
50
49
|
onClick={handleNewTopic}
|
|
51
50
|
title={tTopic('actions.addNewTopic')}
|
|
52
51
|
/>
|
|
@@ -75,7 +75,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
|
|
75
75
|
// Fetch favorite status
|
|
76
76
|
const { data: favoriteStatus, mutate: mutateFavorite } = useSWR(
|
|
77
77
|
identifier && isAuthenticated ? ['favorite-status', 'agent', identifier] : null,
|
|
78
|
-
() => socialService.checkFavoriteStatus('agent', identifier!),
|
|
78
|
+
() => socialService.checkFavoriteStatus('agent-group', identifier!),
|
|
79
79
|
{ revalidateOnFocus: false },
|
|
80
80
|
);
|
|
81
81
|
|
|
@@ -100,10 +100,10 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
|
|
100
100
|
setFavoriteLoading(true);
|
|
101
101
|
try {
|
|
102
102
|
if (isFavorited) {
|
|
103
|
-
await socialService.removeFavorite('agent', identifier);
|
|
103
|
+
await socialService.removeFavorite('agent-group', identifier);
|
|
104
104
|
message.success(t('assistant.unfavoriteSuccess'));
|
|
105
105
|
} else {
|
|
106
|
-
await socialService.addFavorite('agent', identifier);
|
|
106
|
+
await socialService.addFavorite('agent-group', identifier);
|
|
107
107
|
message.success(t('assistant.favoriteSuccess'));
|
|
108
108
|
}
|
|
109
109
|
await mutateFavorite();
|
|
@@ -13,6 +13,8 @@ export interface UserDetailContextConfig {
|
|
|
13
13
|
agentCount: number;
|
|
14
14
|
agentGroups?: DiscoverGroupAgentItem[];
|
|
15
15
|
agents: DiscoverAssistantItem[];
|
|
16
|
+
favoriteAgentGroups?: DiscoverGroupAgentItem[];
|
|
17
|
+
favoriteAgents?: DiscoverAssistantItem[];
|
|
16
18
|
forkedAgentGroups?: DiscoverGroupAgentItem[];
|
|
17
19
|
forkedAgents?: DiscoverAssistantItem[];
|
|
18
20
|
groupCount: number;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Select } from 'antd';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
export type StatusFilterValue = 'published' | 'unpublished' | 'deprecated' | 'archived' | 'forked' | 'favorite';
|
|
8
|
+
|
|
9
|
+
interface StatusFilterProps {
|
|
10
|
+
onChange: (value: StatusFilterValue) => void;
|
|
11
|
+
value: StatusFilterValue;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const StatusFilter = memo<StatusFilterProps>(({ value, onChange }) => {
|
|
15
|
+
const { t } = useTranslation('discover');
|
|
16
|
+
|
|
17
|
+
const options = [
|
|
18
|
+
{ label: t('user.statusFilter.published'), value: 'published' as const },
|
|
19
|
+
{ label: t('user.statusFilter.unpublished'), value: 'unpublished' as const },
|
|
20
|
+
{ label: t('user.statusFilter.deprecated'), value: 'deprecated' as const },
|
|
21
|
+
{ label: t('user.statusFilter.archived'), value: 'archived' as const },
|
|
22
|
+
{ label: t('user.statusFilter.forked'), value: 'forked' as const },
|
|
23
|
+
{ label: t('user.statusFilter.favorite'), value: 'favorite' as const },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Select
|
|
28
|
+
onChange={onChange}
|
|
29
|
+
options={options}
|
|
30
|
+
style={{ minWidth: 120 }}
|
|
31
|
+
value={value}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export default StatusFilter;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Flexbox, Grid, Tag, Text } from '@lobehub/ui';
|
|
4
|
-
import { Pagination } from 'antd';
|
|
4
|
+
import { Input, Pagination } from 'antd';
|
|
5
5
|
import { memo, useMemo, useState } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import AssistantEmpty from '../../../features/AssistantEmpty';
|
|
9
9
|
import { useUserDetailContext } from './DetailProvider';
|
|
10
|
+
import StatusFilter, { type StatusFilterValue } from './StatusFilter';
|
|
10
11
|
import UserAgentCard from './UserAgentCard';
|
|
11
12
|
|
|
12
13
|
interface UserAgentListProps {
|
|
@@ -14,27 +15,82 @@ interface UserAgentListProps {
|
|
|
14
15
|
rows?: number;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const UserAgentList = memo<UserAgentListProps>(({ rows = 4, pageSize =
|
|
18
|
+
const UserAgentList = memo<UserAgentListProps>(({ rows = 4, pageSize = 8 }) => {
|
|
18
19
|
const { t } = useTranslation('discover');
|
|
19
|
-
const { agents, agentCount } = useUserDetailContext();
|
|
20
|
+
const { agents, agentCount, forkedAgents = [], favoriteAgents = [], isOwner } = useUserDetailContext();
|
|
20
21
|
const [currentPage, setCurrentPage] = useState(1);
|
|
22
|
+
const [statusFilter, setStatusFilter] = useState<StatusFilterValue>('published');
|
|
23
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
24
|
+
|
|
25
|
+
// Combine agents and forked agents, then filter based on status and search
|
|
26
|
+
const filteredAgents = useMemo(() => {
|
|
27
|
+
let allAgents = [...agents];
|
|
28
|
+
|
|
29
|
+
if (statusFilter === 'forked') {
|
|
30
|
+
// Show only forked agents (those with forkedFromAgentId)
|
|
31
|
+
allAgents = forkedAgents;
|
|
32
|
+
} else if (statusFilter === 'favorite') {
|
|
33
|
+
// Show only favorited agents
|
|
34
|
+
allAgents = favoriteAgents;
|
|
35
|
+
} else {
|
|
36
|
+
// Filter by status for non-forked agents
|
|
37
|
+
allAgents = allAgents.filter((agent) => {
|
|
38
|
+
return agent.status === statusFilter;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Apply search filter
|
|
43
|
+
if (searchQuery.trim()) {
|
|
44
|
+
const query = searchQuery.toLowerCase();
|
|
45
|
+
allAgents = allAgents.filter((agent) => {
|
|
46
|
+
console.log('agent', agent);
|
|
47
|
+
const name = agent?.title?.toLowerCase() || '';
|
|
48
|
+
const description = agent?.description?.toLowerCase() || '';
|
|
49
|
+
return name.includes(query) || description.includes(query);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return allAgents;
|
|
54
|
+
}, [agents, forkedAgents, statusFilter, searchQuery]);
|
|
21
55
|
|
|
22
56
|
const paginatedAgents = useMemo(() => {
|
|
23
57
|
const startIndex = (currentPage - 1) * pageSize;
|
|
24
|
-
return
|
|
25
|
-
}, [
|
|
58
|
+
return filteredAgents.slice(startIndex, startIndex + pageSize);
|
|
59
|
+
}, [filteredAgents, currentPage, pageSize]);
|
|
60
|
+
|
|
61
|
+
// Reset to page 1 when filter or search changes
|
|
62
|
+
useMemo(() => {
|
|
63
|
+
setCurrentPage(1);
|
|
64
|
+
}, [statusFilter, searchQuery]);
|
|
26
65
|
|
|
27
|
-
if (agents.length === 0) return <AssistantEmpty />;
|
|
66
|
+
if (agents.length === 0 && forkedAgents.length === 0) return <AssistantEmpty />;
|
|
28
67
|
|
|
29
|
-
const showPagination =
|
|
68
|
+
const showPagination = filteredAgents.length > pageSize;
|
|
30
69
|
|
|
31
70
|
return (
|
|
32
71
|
<Flexbox gap={16}>
|
|
33
|
-
<Flexbox align={'center'} gap={8} horizontal>
|
|
34
|
-
<
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
72
|
+
<Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
|
|
73
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
74
|
+
<Text fontSize={16} weight={500}>
|
|
75
|
+
{t('user.publishedAgents')}
|
|
76
|
+
</Text>
|
|
77
|
+
{agentCount > 0 && <Tag>{filteredAgents.length}</Tag>}
|
|
78
|
+
</Flexbox>
|
|
79
|
+
{isOwner && (
|
|
80
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
81
|
+
<Input.Search
|
|
82
|
+
allowClear
|
|
83
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
84
|
+
placeholder={t('user.searchPlaceholder')}
|
|
85
|
+
style={{ width: 200 }}
|
|
86
|
+
value={searchQuery}
|
|
87
|
+
/>
|
|
88
|
+
<StatusFilter
|
|
89
|
+
onChange={(value) => setStatusFilter(value)}
|
|
90
|
+
value={statusFilter}
|
|
91
|
+
/>
|
|
92
|
+
</Flexbox>
|
|
93
|
+
)}
|
|
38
94
|
</Flexbox>
|
|
39
95
|
<Grid rows={rows} width={'100%'}>
|
|
40
96
|
{paginatedAgents.map((item, index) => (
|
|
@@ -48,7 +104,7 @@ const UserAgentList = memo<UserAgentListProps>(({ rows = 4, pageSize = 10 }) =>
|
|
|
48
104
|
onChange={(page) => setCurrentPage(page)}
|
|
49
105
|
pageSize={pageSize}
|
|
50
106
|
showSizeChanger={false}
|
|
51
|
-
total={
|
|
107
|
+
total={filteredAgents.length}
|
|
52
108
|
/>
|
|
53
109
|
</Flexbox>
|
|
54
110
|
)}
|
|
@@ -3,25 +3,14 @@
|
|
|
3
3
|
import { Flexbox } from '@lobehub/ui';
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
|
|
6
|
-
import { useUserDetailContext } from './DetailProvider';
|
|
7
6
|
import UserAgentList from './UserAgentList';
|
|
8
|
-
import UserFavoriteAgents from './UserFavoriteAgents';
|
|
9
|
-
import UserFavoritePlugins from './UserFavoritePlugins';
|
|
10
|
-
import UserForkedAgentGroups from './UserForkedAgentGroups';
|
|
11
|
-
import UserForkedAgents from './UserForkedAgents';
|
|
12
7
|
import UserGroupList from './UserGroupList';
|
|
13
8
|
|
|
14
9
|
const UserContent = memo(() => {
|
|
15
|
-
const { forkedAgents, forkedAgentGroups } = useUserDetailContext();
|
|
16
|
-
|
|
17
10
|
return (
|
|
18
11
|
<Flexbox gap={32}>
|
|
19
12
|
<UserAgentList />
|
|
20
13
|
<UserGroupList />
|
|
21
|
-
<UserForkedAgents agents={forkedAgents} />
|
|
22
|
-
<UserForkedAgentGroups agentGroups={forkedAgentGroups} />
|
|
23
|
-
<UserFavoriteAgents />
|
|
24
|
-
<UserFavoritePlugins />
|
|
25
14
|
</Flexbox>
|
|
26
15
|
);
|
|
27
16
|
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Flexbox, Grid, Tag, Text } from '@lobehub/ui';
|
|
4
|
-
import { Pagination } from 'antd';
|
|
4
|
+
import { Input, Pagination } from 'antd';
|
|
5
5
|
import { memo, useMemo, useState } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import { useUserDetailContext } from './DetailProvider';
|
|
9
|
+
import StatusFilter, { type StatusFilterValue } from './StatusFilter';
|
|
9
10
|
import UserGroupCard from './UserGroupCard';
|
|
10
11
|
|
|
11
12
|
interface UserGroupListProps {
|
|
@@ -13,28 +14,81 @@ interface UserGroupListProps {
|
|
|
13
14
|
rows?: number;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
const UserGroupList = memo<UserGroupListProps>(({ rows = 4, pageSize =
|
|
17
|
+
const UserGroupList = memo<UserGroupListProps>(({ rows = 4, pageSize = 8 }) => {
|
|
17
18
|
const { t } = useTranslation('discover');
|
|
18
|
-
const { agentGroups, groupCount } = useUserDetailContext();
|
|
19
|
+
const { agentGroups = [], groupCount, forkedAgentGroups = [], favoriteAgentGroups = [], isOwner } = useUserDetailContext();
|
|
19
20
|
const [currentPage, setCurrentPage] = useState(1);
|
|
21
|
+
const [statusFilter, setStatusFilter] = useState<StatusFilterValue>('published');
|
|
22
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
23
|
+
|
|
24
|
+
// Combine groups and forked groups, then filter based on status and search
|
|
25
|
+
const filteredGroups = useMemo(() => {
|
|
26
|
+
let allGroups = [...agentGroups];
|
|
27
|
+
|
|
28
|
+
if (statusFilter === 'forked') {
|
|
29
|
+
// Show only forked groups (those with forkedFromAgentId)
|
|
30
|
+
allGroups = forkedAgentGroups;
|
|
31
|
+
} else if (statusFilter === 'favorite') {
|
|
32
|
+
// Show only favorited groups
|
|
33
|
+
allGroups = favoriteAgentGroups;
|
|
34
|
+
} else {
|
|
35
|
+
// Filter by status for non-forked groups
|
|
36
|
+
allGroups = allGroups.filter((group) => {
|
|
37
|
+
return group.status === statusFilter;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Apply search filter
|
|
42
|
+
if (searchQuery.trim()) {
|
|
43
|
+
const query = searchQuery.toLowerCase();
|
|
44
|
+
allGroups = allGroups.filter((group) => {
|
|
45
|
+
const name = group?.title?.toLowerCase() || '';
|
|
46
|
+
const description = group?.description?.toLowerCase() || '';
|
|
47
|
+
return name.includes(query) || description.includes(query);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return allGroups;
|
|
52
|
+
}, [agentGroups, forkedAgentGroups, statusFilter, searchQuery]);
|
|
20
53
|
|
|
21
54
|
const paginatedGroups = useMemo(() => {
|
|
22
|
-
if (!agentGroups) return [];
|
|
23
55
|
const startIndex = (currentPage - 1) * pageSize;
|
|
24
|
-
return
|
|
25
|
-
}, [
|
|
56
|
+
return filteredGroups.slice(startIndex, startIndex + pageSize);
|
|
57
|
+
}, [filteredGroups, currentPage, pageSize]);
|
|
58
|
+
|
|
59
|
+
// Reset to page 1 when filter or search changes
|
|
60
|
+
useMemo(() => {
|
|
61
|
+
setCurrentPage(1);
|
|
62
|
+
}, [statusFilter, searchQuery]);
|
|
26
63
|
|
|
27
|
-
if (
|
|
64
|
+
if (agentGroups.length === 0 && forkedAgentGroups.length === 0) return null;
|
|
28
65
|
|
|
29
|
-
const showPagination =
|
|
66
|
+
const showPagination = filteredGroups.length > pageSize;
|
|
30
67
|
|
|
31
68
|
return (
|
|
32
69
|
<Flexbox gap={16}>
|
|
33
|
-
<Flexbox align={'center'} gap={8} horizontal>
|
|
34
|
-
<
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
<Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
|
|
71
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
72
|
+
<Text fontSize={16} weight={500}>
|
|
73
|
+
{t('user.publishedGroups', { defaultValue: '创作的群组' })}
|
|
74
|
+
</Text>
|
|
75
|
+
{groupCount > 0 && <Tag>{filteredGroups.length}</Tag>}
|
|
76
|
+
</Flexbox>
|
|
77
|
+
{isOwner && (
|
|
78
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
79
|
+
<Input.Search
|
|
80
|
+
allowClear
|
|
81
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
82
|
+
placeholder={t('user.searchPlaceholder')}
|
|
83
|
+
style={{ width: 200 }}
|
|
84
|
+
value={searchQuery}
|
|
85
|
+
/>
|
|
86
|
+
<StatusFilter
|
|
87
|
+
onChange={(value) => setStatusFilter(value)}
|
|
88
|
+
value={statusFilter}
|
|
89
|
+
/>
|
|
90
|
+
</Flexbox>
|
|
91
|
+
)}
|
|
38
92
|
</Flexbox>
|
|
39
93
|
<Grid rows={rows} width={'100%'}>
|
|
40
94
|
{paginatedGroups.map((item, index) => (
|
|
@@ -48,7 +102,7 @@ const UserGroupList = memo<UserGroupListProps>(({ rows = 4, pageSize = 10 }) =>
|
|
|
48
102
|
onChange={(page) => setCurrentPage(page)}
|
|
49
103
|
pageSize={pageSize}
|
|
50
104
|
showSizeChanger={false}
|
|
51
|
-
total={
|
|
105
|
+
total={filteredGroups.length}
|
|
52
106
|
/>
|
|
53
107
|
</Flexbox>
|
|
54
108
|
)}
|
|
@@ -61,12 +61,14 @@ const UserDetailPage = memo<UserDetailPageProps>(({ mobile }) => {
|
|
|
61
61
|
|
|
62
62
|
const contextConfig = useMemo(() => {
|
|
63
63
|
if (!data || !data.user) return null;
|
|
64
|
-
const { user, agents, agentGroups, forkedAgents, forkedAgentGroups } = data;
|
|
64
|
+
const { user, agents, agentGroups, forkedAgents, forkedAgentGroups, favoriteAgents, favoriteAgentGroups } = data;
|
|
65
65
|
const totalInstalls = agents.reduce((sum, agent) => sum + (agent.installCount || 0), 0);
|
|
66
66
|
return {
|
|
67
67
|
agentCount: agents.length,
|
|
68
68
|
agentGroups: agentGroups || [],
|
|
69
69
|
agents,
|
|
70
|
+
favoriteAgentGroups: favoriteAgentGroups || [],
|
|
71
|
+
favoriteAgents: favoriteAgents || [],
|
|
70
72
|
forkedAgentGroups: forkedAgentGroups || [],
|
|
71
73
|
forkedAgents: forkedAgents || [],
|
|
72
74
|
groupCount: agentGroups?.length || 0,
|
|
@@ -56,6 +56,7 @@ const styles = createStaticStyles(({ css, cssVar }) => {
|
|
|
56
56
|
const AssistantItem = memo<DiscoverAssistantItem>(
|
|
57
57
|
({
|
|
58
58
|
createdAt,
|
|
59
|
+
updatedAt,
|
|
59
60
|
author,
|
|
60
61
|
avatar,
|
|
61
62
|
title,
|
|
@@ -225,7 +226,7 @@ const AssistantItem = memo<DiscoverAssistantItem>(
|
|
|
225
226
|
<Icon icon={ClockIcon} size={14} />
|
|
226
227
|
<PublishedTime
|
|
227
228
|
className={styles.secondaryDesc}
|
|
228
|
-
date={createdAt}
|
|
229
|
+
date={updatedAt || createdAt}
|
|
229
230
|
template={'MMM DD, YYYY'}
|
|
230
231
|
/>
|
|
231
232
|
</Flexbox>
|
|
@@ -10,9 +10,10 @@ import {
|
|
|
10
10
|
Tabs,
|
|
11
11
|
type TabsProps,
|
|
12
12
|
} from '@lobehub/ui';
|
|
13
|
+
import { App } from 'antd';
|
|
13
14
|
import { createStaticStyles, cx } from 'antd-style';
|
|
14
15
|
import isEqual from 'fast-deep-equal';
|
|
15
|
-
import { ChevronDown, ChevronUp, History, Sparkles } from 'lucide-react';
|
|
16
|
+
import { ChevronDown, ChevronUp, History, Sparkles, Undo2 } from 'lucide-react';
|
|
16
17
|
import { memo, useCallback, useMemo, useState } from 'react';
|
|
17
18
|
import { useTranslation } from 'react-i18next';
|
|
18
19
|
|
|
@@ -66,6 +67,7 @@ export interface CompressedGroupMessageProps {
|
|
|
66
67
|
|
|
67
68
|
const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
|
68
69
|
const { t } = useTranslation('chat');
|
|
70
|
+
const { modal } = App.useApp();
|
|
69
71
|
const [activeTab, setActiveTab] = useState<string>(() => getStoredTab(id));
|
|
70
72
|
|
|
71
73
|
const handleTabChange = useCallback(
|
|
@@ -80,6 +82,16 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
|
|
80
82
|
const toggleCompressedGroupExpanded = useConversationStore(
|
|
81
83
|
(s) => s.toggleCompressedGroupExpanded,
|
|
82
84
|
);
|
|
85
|
+
const cancelCompression = useConversationStore((s) => s.cancelCompression);
|
|
86
|
+
|
|
87
|
+
const handleCancelCompression = useCallback(() => {
|
|
88
|
+
modal.confirm({
|
|
89
|
+
centered: true,
|
|
90
|
+
content: t('compression.cancelConfirm'),
|
|
91
|
+
onOk: () => cancelCompression(id),
|
|
92
|
+
title: t('compression.cancel'),
|
|
93
|
+
});
|
|
94
|
+
}, [id, cancelCompression, modal, t]);
|
|
83
95
|
|
|
84
96
|
const content = message?.content;
|
|
85
97
|
const rawCompressedMessages = (message as UIChatMessage)?.compressedMessages;
|
|
@@ -145,11 +157,19 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
|
|
145
157
|
onChange={handleTabChange}
|
|
146
158
|
variant={'rounded'}
|
|
147
159
|
/>
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
160
|
+
<Flexbox gap={4} horizontal>
|
|
161
|
+
<ActionIcon
|
|
162
|
+
icon={Undo2}
|
|
163
|
+
onClick={handleCancelCompression}
|
|
164
|
+
size={'small'}
|
|
165
|
+
title={t('compression.cancel')}
|
|
166
|
+
/>
|
|
167
|
+
<ActionIcon
|
|
168
|
+
icon={expanded ? ChevronUp : ChevronDown}
|
|
169
|
+
onClick={() => toggleCompressedGroupExpanded(id)}
|
|
170
|
+
size={'small'}
|
|
171
|
+
/>
|
|
172
|
+
</Flexbox>
|
|
153
173
|
</Flexbox>
|
|
154
174
|
)}
|
|
155
175
|
{!showContent ? null : activeTab === 'summary' ? (
|
|
@@ -13,6 +13,11 @@ import { dataSelectors } from '../../data/selectors';
|
|
|
13
13
|
* Handles message state operations like loading, collapsed, etc.
|
|
14
14
|
*/
|
|
15
15
|
export interface MessageStateAction {
|
|
16
|
+
/**
|
|
17
|
+
* Cancel compression and restore original messages
|
|
18
|
+
*/
|
|
19
|
+
cancelCompression: (id: string) => Promise<void>;
|
|
20
|
+
|
|
16
21
|
/**
|
|
17
22
|
* Copy message content to clipboard
|
|
18
23
|
*/
|
|
@@ -50,6 +55,26 @@ export const messageStateSlice: StateCreator<
|
|
|
50
55
|
[],
|
|
51
56
|
MessageStateAction
|
|
52
57
|
> = (set, get) => ({
|
|
58
|
+
cancelCompression: async (id) => {
|
|
59
|
+
const message = dataSelectors.getDisplayMessageById(id)(get());
|
|
60
|
+
if (!message || message.role !== 'compressedGroup') return;
|
|
61
|
+
|
|
62
|
+
const { context, replaceMessages } = get();
|
|
63
|
+
if (!context.agentId || !context.topicId) return;
|
|
64
|
+
|
|
65
|
+
// Call service to cancel compression
|
|
66
|
+
const { messages } = await messageService.cancelCompression({
|
|
67
|
+
agentId: context.agentId,
|
|
68
|
+
groupId: context.groupId,
|
|
69
|
+
messageGroupId: id,
|
|
70
|
+
threadId: context.threadId,
|
|
71
|
+
topicId: context.topicId,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Replace messages with restored original messages
|
|
75
|
+
replaceMessages(messages);
|
|
76
|
+
},
|
|
77
|
+
|
|
53
78
|
copyMessage: async (id, content) => {
|
|
54
79
|
const { hooks } = get();
|
|
55
80
|
|
|
@@ -41,6 +41,9 @@ export default {
|
|
|
41
41
|
'chatList.longMessageDetail': 'View Details',
|
|
42
42
|
'clearCurrentMessages': 'Clear current session messages',
|
|
43
43
|
'compressedHistory': 'Compressed History',
|
|
44
|
+
'compression.cancel': 'Uncompress',
|
|
45
|
+
'compression.cancelConfirm':
|
|
46
|
+
'Are you sure you want to uncompress? This will restore the original messages.',
|
|
44
47
|
'compression.history': 'History',
|
|
45
48
|
'compression.summary': 'Summary',
|
|
46
49
|
'confirmClearCurrentMessages':
|
|
@@ -891,6 +891,18 @@ export default {
|
|
|
891
891
|
|
|
892
892
|
'user.publishedAgents': 'Created Agents',
|
|
893
893
|
|
|
894
|
+
'user.publishedGroups': 'Created Groups',
|
|
895
|
+
|
|
896
|
+
'user.searchPlaceholder': 'Search by name or description...',
|
|
897
|
+
|
|
898
|
+
'user.statusFilter.all': 'All',
|
|
899
|
+
'user.statusFilter.archived': 'Archived',
|
|
900
|
+
'user.statusFilter.deprecated': 'Deprecated',
|
|
901
|
+
'user.statusFilter.favorite': 'Favorite',
|
|
902
|
+
'user.statusFilter.forked': 'Forked',
|
|
903
|
+
'user.statusFilter.published': 'Published',
|
|
904
|
+
'user.statusFilter.unpublished': 'Under Review',
|
|
905
|
+
|
|
894
906
|
'user.tabs.favorites': 'Favorites',
|
|
895
907
|
|
|
896
908
|
'user.tabs.forkedAgents': 'Forked',
|
|
@@ -17,7 +17,7 @@ const socialPublicProcedure = publicProcedure
|
|
|
17
17
|
.use(marketSDK);
|
|
18
18
|
|
|
19
19
|
// Schema definitions
|
|
20
|
-
const targetTypeSchema = z.enum(['agent', 'plugin']);
|
|
20
|
+
const targetTypeSchema = z.enum(['agent', 'plugin', 'agent-group']);
|
|
21
21
|
|
|
22
22
|
const paginationSchema = z.object({
|
|
23
23
|
limit: z.number().optional(),
|