@lobehub/chat 1.33.5 → 1.34.1
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 +18 -0
- package/locales/ar/chat.json +7 -0
- package/locales/ar/common.json +2 -0
- package/locales/ar/models.json +24 -0
- package/locales/ar/setting.json +5 -0
- package/locales/ar/thread.json +5 -0
- package/locales/bg-BG/chat.json +7 -0
- package/locales/bg-BG/common.json +2 -0
- package/locales/bg-BG/models.json +24 -0
- package/locales/bg-BG/setting.json +5 -0
- package/locales/bg-BG/thread.json +5 -0
- package/locales/de-DE/chat.json +7 -0
- package/locales/de-DE/common.json +2 -0
- package/locales/de-DE/models.json +24 -0
- package/locales/de-DE/setting.json +5 -0
- package/locales/de-DE/thread.json +5 -0
- package/locales/en-US/chat.json +7 -0
- package/locales/en-US/common.json +2 -0
- package/locales/en-US/models.json +24 -0
- package/locales/en-US/setting.json +5 -0
- package/locales/en-US/thread.json +5 -0
- package/locales/es-ES/chat.json +7 -0
- package/locales/es-ES/common.json +2 -0
- package/locales/es-ES/models.json +24 -0
- package/locales/es-ES/setting.json +5 -0
- package/locales/es-ES/thread.json +5 -0
- package/locales/fa-IR/chat.json +7 -0
- package/locales/fa-IR/common.json +2 -0
- package/locales/fa-IR/models.json +24 -0
- package/locales/fa-IR/setting.json +5 -0
- package/locales/fa-IR/thread.json +5 -0
- package/locales/fr-FR/chat.json +7 -0
- package/locales/fr-FR/common.json +2 -0
- package/locales/fr-FR/models.json +24 -0
- package/locales/fr-FR/setting.json +5 -0
- package/locales/fr-FR/thread.json +5 -0
- package/locales/it-IT/chat.json +7 -0
- package/locales/it-IT/common.json +2 -0
- package/locales/it-IT/models.json +24 -0
- package/locales/it-IT/setting.json +5 -0
- package/locales/it-IT/thread.json +5 -0
- package/locales/ja-JP/chat.json +7 -0
- package/locales/ja-JP/common.json +2 -0
- package/locales/ja-JP/models.json +24 -0
- package/locales/ja-JP/setting.json +5 -0
- package/locales/ja-JP/thread.json +5 -0
- package/locales/ko-KR/chat.json +7 -0
- package/locales/ko-KR/common.json +2 -0
- package/locales/ko-KR/models.json +24 -0
- package/locales/ko-KR/setting.json +5 -0
- package/locales/ko-KR/thread.json +5 -0
- package/locales/nl-NL/chat.json +7 -0
- package/locales/nl-NL/common.json +2 -0
- package/locales/nl-NL/models.json +24 -0
- package/locales/nl-NL/setting.json +5 -0
- package/locales/nl-NL/thread.json +5 -0
- package/locales/pl-PL/chat.json +7 -0
- package/locales/pl-PL/common.json +2 -0
- package/locales/pl-PL/models.json +24 -0
- package/locales/pl-PL/setting.json +5 -0
- package/locales/pl-PL/thread.json +5 -0
- package/locales/pt-BR/chat.json +7 -0
- package/locales/pt-BR/common.json +2 -0
- package/locales/pt-BR/models.json +24 -0
- package/locales/pt-BR/setting.json +5 -0
- package/locales/pt-BR/thread.json +5 -0
- package/locales/ru-RU/chat.json +7 -0
- package/locales/ru-RU/common.json +2 -0
- package/locales/ru-RU/models.json +24 -0
- package/locales/ru-RU/setting.json +5 -0
- package/locales/ru-RU/thread.json +5 -0
- package/locales/tr-TR/chat.json +7 -0
- package/locales/tr-TR/common.json +2 -0
- package/locales/tr-TR/models.json +24 -0
- package/locales/tr-TR/setting.json +5 -0
- package/locales/tr-TR/thread.json +5 -0
- package/locales/vi-VN/chat.json +7 -0
- package/locales/vi-VN/common.json +2 -0
- package/locales/vi-VN/models.json +24 -0
- package/locales/vi-VN/setting.json +5 -0
- package/locales/vi-VN/thread.json +5 -0
- package/locales/zh-CN/chat.json +7 -0
- package/locales/zh-CN/common.json +2 -0
- package/locales/zh-CN/models.json +24 -0
- package/locales/zh-CN/setting.json +5 -0
- package/locales/zh-CN/thread.json +5 -0
- package/locales/zh-TW/chat.json +7 -0
- package/locales/zh-TW/common.json +2 -0
- package/locales/zh-TW/models.json +24 -0
- package/locales/zh-TW/setting.json +5 -0
- package/locales/zh-TW/thread.json +5 -0
- package/package.json +1 -1
- package/src/app/(main)/chat/(workspace)/@conversation/default.tsx +2 -0
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatHydration/index.tsx +11 -2
- package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/index.tsx +7 -9
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +7 -2
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/Thread.tsx +62 -0
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/ThreadItem.tsx +68 -0
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +62 -2
- package/src/app/(main)/chat/(workspace)/@conversation/features/ThreadHydration.tsx +47 -0
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Desktop.tsx +3 -2
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Mobile.tsx +47 -6
- package/src/app/(main)/chat/(workspace)/@topic/features/SkeletonList.tsx +3 -2
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/index.tsx +10 -3
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/FlatMode/index.tsx +1 -1
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ThreadItem/Content.tsx +164 -0
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ThreadItem/index.tsx +98 -0
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{TopicItem.tsx → TopicItem/index.tsx} +33 -22
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +12 -5
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx +1 -2
- package/src/const/message.ts +2 -0
- package/src/const/settings/systemAgent.ts +1 -0
- package/src/database/server/migrations/0012_add_thread.sql +39 -0
- package/src/database/server/migrations/meta/0012_snapshot.json +3671 -0
- package/src/database/server/migrations/meta/_journal.json +7 -0
- package/src/database/server/models/_template.ts +2 -2
- package/src/database/server/models/message.ts +1 -0
- package/src/database/server/models/thread.ts +79 -0
- package/src/database/server/schemas/lobechat/message.ts +2 -1
- package/src/database/server/schemas/lobechat/relations.ts +13 -1
- package/src/database/server/schemas/lobechat/topic.ts +30 -1
- package/src/database/server/utils/idGenerator.ts +1 -0
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +6 -4
- package/src/features/ChatInput/ActionBar/Token/index.tsx +24 -5
- package/src/features/ChatInput/ActionBar/config.ts +3 -2
- package/src/features/ChatInput/Desktop/index.tsx +15 -7
- package/src/features/ChatInput/Mobile/index.tsx +4 -4
- package/src/features/Conversation/Actions/Assistant.tsx +24 -5
- package/src/features/Conversation/Actions/User.tsx +21 -4
- package/src/features/Conversation/Actions/index.ts +1 -66
- package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/index.tsx +3 -1
- package/src/features/Conversation/Messages/{Tool/index.tsx → Assistant/ToolCallItem/Tool.tsx} +10 -11
- package/src/features/Conversation/Messages/Assistant/ToolCallItem/index.tsx +5 -3
- package/src/features/Conversation/Messages/Assistant/index.tsx +22 -14
- package/src/features/Conversation/Messages/index.ts +0 -2
- package/src/features/Conversation/components/AutoScroll.tsx +1 -1
- package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +79 -5
- package/src/features/Conversation/components/ChatItem/InPortalThreadContext.ts +3 -0
- package/src/features/Conversation/components/ChatItem/index.tsx +16 -5
- package/src/features/Conversation/components/MarkdownElements/LobeArtifact/Render/index.tsx +9 -1
- package/src/features/Conversation/components/ThreadDivider/index.tsx +19 -0
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +19 -4
- package/src/features/Portal/Thread/Chat/ChatInput/Footer.tsx +90 -0
- package/src/features/Portal/Thread/Chat/ChatInput/TextArea.tsx +30 -0
- package/src/features/Portal/Thread/Chat/ChatInput/index.tsx +66 -0
- package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +50 -0
- package/src/features/Portal/Thread/Chat/ChatItem.tsx +62 -0
- package/src/features/Portal/Thread/Chat/ChatList.tsx +49 -0
- package/src/features/Portal/Thread/Chat/ThreadDivider/index.tsx +19 -0
- package/src/features/Portal/Thread/Chat/index.tsx +28 -0
- package/src/features/Portal/Thread/Header/Active.tsx +35 -0
- package/src/features/Portal/Thread/Header/New.tsx +37 -0
- package/src/features/Portal/Thread/Header/Title.tsx +18 -0
- package/src/features/Portal/Thread/Header/index.tsx +20 -0
- package/src/features/Portal/Thread/hook.ts +8 -0
- package/src/features/Portal/Thread/index.ts +12 -0
- package/src/features/Portal/router.tsx +2 -1
- package/src/hooks/useFetchTopics.ts +7 -1
- package/src/locales/default/chat.ts +8 -1
- package/src/locales/default/common.ts +3 -0
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/setting.ts +5 -0
- package/src/locales/default/thread.ts +5 -0
- package/src/server/modules/AgentRuntime/index.test.ts +10 -0
- package/src/server/modules/AgentRuntime/index.ts +1 -1
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/thread.ts +83 -0
- package/src/services/thread.ts +54 -0
- package/src/store/chat/initialState.ts +3 -0
- package/src/store/chat/selectors.ts +2 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +31 -8
- package/src/store/chat/slices/aiChat/actions/rag.ts +1 -1
- package/src/store/chat/slices/message/selectors.test.ts +3 -3
- package/src/store/chat/slices/message/selectors.ts +50 -29
- package/src/store/chat/slices/plugin/action.ts +26 -8
- package/src/store/chat/slices/portal/action.ts +1 -0
- package/src/store/chat/slices/portal/initialState.ts +1 -0
- package/src/store/chat/slices/portal/selectors/thread.ts +17 -0
- package/src/store/chat/slices/portal/selectors.ts +2 -0
- package/src/store/chat/slices/thread/action.ts +326 -0
- package/src/store/chat/slices/thread/initialState.ts +34 -0
- package/src/store/chat/slices/thread/reducer.ts +48 -0
- package/src/store/chat/slices/thread/selectors/index.ts +202 -0
- package/src/store/chat/slices/thread/selectors/util.ts +22 -0
- package/src/store/chat/slices/topic/action.ts +5 -1
- package/src/store/chat/store.ts +5 -2
- package/src/store/global/initialState.ts +4 -0
- package/src/store/global/selectors.ts +4 -0
- package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
- package/src/types/message/index.ts +17 -1
- package/src/types/topic/index.ts +1 -0
- package/src/types/topic/thread.ts +42 -0
- package/src/types/user/settings/systemAgent.ts +1 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Header.tsx +0 -11
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/PortalModal.tsx +0 -35
- /package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/SendMore.tsx +0 -0
- /package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/ShortcutHint.tsx +0 -0
- /package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{DefaultContent.tsx → TopicItem/DefaultContent.tsx} +0 -0
- /package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{TopicContent.tsx → TopicItem/TopicContent.tsx} +0 -0
- /package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/PluginResultJSON.tsx +0 -0
- /package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/Settings.tsx +0 -0
- /package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/style.ts +0 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
import { Typography } from 'antd';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import isEqual from 'fast-deep-equal';
|
4
|
+
import { CSSProperties, memo } from 'react';
|
5
|
+
import { useTranslation } from 'react-i18next';
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
7
|
+
|
8
|
+
import { useChatStore } from '@/store/chat';
|
9
|
+
import { threadSelectors } from '@/store/chat/selectors';
|
10
|
+
|
11
|
+
import ThreadItem from './ThreadItem';
|
12
|
+
|
13
|
+
const useStyles = createStyles(({ css, token, isDarkMode }) => ({
|
14
|
+
container: css`
|
15
|
+
cursor: pointer;
|
16
|
+
|
17
|
+
padding-block: 8px 4px;
|
18
|
+
padding-inline: 4px;
|
19
|
+
|
20
|
+
background: ${isDarkMode ? token.colorFillTertiary : token.colorFillQuaternary};
|
21
|
+
border-radius: 6px;
|
22
|
+
`,
|
23
|
+
}));
|
24
|
+
|
25
|
+
interface ThreadProps {
|
26
|
+
id: string;
|
27
|
+
placement: 'start' | 'end';
|
28
|
+
style?: CSSProperties;
|
29
|
+
}
|
30
|
+
|
31
|
+
const Thread = memo<ThreadProps>(({ id, placement, style }) => {
|
32
|
+
const { t } = useTranslation('chat');
|
33
|
+
const { styles } = useStyles();
|
34
|
+
|
35
|
+
const threads = useChatStore(threadSelectors.getThreadsBySourceMsgId(id), isEqual);
|
36
|
+
|
37
|
+
return (
|
38
|
+
<Flexbox
|
39
|
+
direction={placement === 'end' ? 'horizontal-reverse' : 'horizontal'}
|
40
|
+
gap={12}
|
41
|
+
paddingInline={16}
|
42
|
+
style={{ paddingBottom: 16, ...style }}
|
43
|
+
>
|
44
|
+
<div style={{ width: 40 }} />
|
45
|
+
<Flexbox className={styles.container} gap={4} padding={4} style={{ width: 'fit-content' }}>
|
46
|
+
<Flexbox gap={8} horizontal paddingInline={6}>
|
47
|
+
<Typography.Text style={{ fontSize: 12 }} type={'secondary'}>
|
48
|
+
{t('thread.title')}
|
49
|
+
{threads.length}
|
50
|
+
</Typography.Text>
|
51
|
+
</Flexbox>
|
52
|
+
<Flexbox>
|
53
|
+
{threads.map((thread) => (
|
54
|
+
<ThreadItem key={thread.id} {...thread} />
|
55
|
+
))}
|
56
|
+
</Flexbox>
|
57
|
+
</Flexbox>
|
58
|
+
</Flexbox>
|
59
|
+
);
|
60
|
+
});
|
61
|
+
|
62
|
+
export default Thread;
|
package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/ThreadItem.tsx
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
import { Icon } from '@lobehub/ui';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import dayjs from 'dayjs';
|
4
|
+
import { ChevronRight } from 'lucide-react';
|
5
|
+
import { memo } from 'react';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
8
|
+
|
9
|
+
import { useIsMobile } from '@/hooks/useIsMobile';
|
10
|
+
import { useChatStore } from '@/store/chat';
|
11
|
+
import { chatSelectors } from '@/store/chat/selectors';
|
12
|
+
import { ThreadItem } from '@/types/topic';
|
13
|
+
|
14
|
+
const useStyles = createStyles(({ css, token }) => ({
|
15
|
+
active: css`
|
16
|
+
background: ${token.colorFillTertiary};
|
17
|
+
`,
|
18
|
+
container: css`
|
19
|
+
cursor: pointer;
|
20
|
+
|
21
|
+
padding-block: 4px;
|
22
|
+
padding-inline: 6px;
|
23
|
+
|
24
|
+
font-size: 12px;
|
25
|
+
|
26
|
+
border-radius: 6px;
|
27
|
+
|
28
|
+
&:hover {
|
29
|
+
background: ${token.colorFillTertiary};
|
30
|
+
}
|
31
|
+
`,
|
32
|
+
extra: css`
|
33
|
+
color: ${token.colorTextSecondary};
|
34
|
+
`,
|
35
|
+
}));
|
36
|
+
|
37
|
+
const Item = memo<ThreadItem>(({ id, title, lastActiveAt, sourceMessageId }) => {
|
38
|
+
const { t } = useTranslation('chat');
|
39
|
+
const openThreadInPortal = useChatStore((s) => s.openThreadInPortal);
|
40
|
+
const { styles, cx } = useStyles();
|
41
|
+
const [isActive, messageCount] = useChatStore((s) => [
|
42
|
+
s.activeThreadId === id,
|
43
|
+
chatSelectors.countMessagesByThreadId(id)(s),
|
44
|
+
]);
|
45
|
+
const mobile = useIsMobile();
|
46
|
+
return (
|
47
|
+
<Flexbox
|
48
|
+
align={'baseline'}
|
49
|
+
className={cx(styles.container, isActive && styles.active)}
|
50
|
+
gap={8}
|
51
|
+
horizontal
|
52
|
+
onClick={() => {
|
53
|
+
if (isActive) return;
|
54
|
+
|
55
|
+
openThreadInPortal(id, sourceMessageId);
|
56
|
+
}}
|
57
|
+
>
|
58
|
+
{title}
|
59
|
+
<Flexbox className={styles.extra} horizontal>
|
60
|
+
{!!messageCount && t('thread.threadMessageCount', { messageCount })}
|
61
|
+
{!mobile && ` · ${dayjs(lastActiveAt).format('YYYY-MM-DD')}`}
|
62
|
+
<Icon icon={ChevronRight} />
|
63
|
+
</Flexbox>
|
64
|
+
</Flexbox>
|
65
|
+
);
|
66
|
+
});
|
67
|
+
|
68
|
+
export default Item;
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { createStyles } from 'antd-style';
|
1
2
|
import React, { memo, useMemo } from 'react';
|
2
3
|
|
3
4
|
import { ChatItem } from '@/features/Conversation';
|
@@ -5,7 +6,42 @@ import ActionsBar from '@/features/Conversation/components/ChatItem/ActionsBar';
|
|
5
6
|
import { useAgentStore } from '@/store/agent';
|
6
7
|
import { agentSelectors } from '@/store/agent/selectors';
|
7
8
|
import { useChatStore } from '@/store/chat';
|
8
|
-
import { chatSelectors } from '@/store/chat/selectors';
|
9
|
+
import { chatSelectors, threadSelectors } from '@/store/chat/selectors';
|
10
|
+
|
11
|
+
import Thread from './Thread';
|
12
|
+
|
13
|
+
const useStyles = createStyles(({ css, token, isDarkMode }) => {
|
14
|
+
const borderColor = isDarkMode ? token.colorFillSecondary : token.colorFillTertiary;
|
15
|
+
|
16
|
+
return {
|
17
|
+
end: css`
|
18
|
+
&::after {
|
19
|
+
inset-inline-end: 36px;
|
20
|
+
border-inline-end: 2px solid ${borderColor};
|
21
|
+
border-end-end-radius: 8px;
|
22
|
+
}
|
23
|
+
`,
|
24
|
+
line: css`
|
25
|
+
&::after {
|
26
|
+
content: '';
|
27
|
+
|
28
|
+
position: absolute;
|
29
|
+
inset-block: 56px 50px;
|
30
|
+
|
31
|
+
width: 32px;
|
32
|
+
|
33
|
+
border-block-end: 2px solid ${borderColor};
|
34
|
+
}
|
35
|
+
`,
|
36
|
+
start: css`
|
37
|
+
&::after {
|
38
|
+
inset-inline-start: 36px;
|
39
|
+
border-inline-start: 2px solid ${borderColor};
|
40
|
+
border-end-start-radius: 8px;
|
41
|
+
}
|
42
|
+
`,
|
43
|
+
};
|
44
|
+
});
|
9
45
|
|
10
46
|
export interface ThreadChatItemProps {
|
11
47
|
id: string;
|
@@ -13,7 +49,21 @@ export interface ThreadChatItemProps {
|
|
13
49
|
}
|
14
50
|
|
15
51
|
const MainChatItem = memo<ThreadChatItemProps>(({ id, index }) => {
|
16
|
-
const
|
52
|
+
const { styles, cx } = useStyles();
|
53
|
+
|
54
|
+
const [displayMode] = useAgentStore((s) => {
|
55
|
+
const config = agentSelectors.currentAgentChatConfig(s);
|
56
|
+
return [config.displayMode || 'chat'];
|
57
|
+
});
|
58
|
+
|
59
|
+
const userRole = useChatStore((s) => chatSelectors.getMessageById(id)(s)?.role);
|
60
|
+
|
61
|
+
const placement = displayMode === 'chat' && userRole === 'user' ? 'end' : 'start';
|
62
|
+
|
63
|
+
const [showThread, historyLength] = useChatStore((s) => [
|
64
|
+
threadSelectors.hasThreadBySourceMsgId(id)(s),
|
65
|
+
chatSelectors.mainDisplayChatIDs(s).length,
|
66
|
+
]);
|
17
67
|
|
18
68
|
const enableHistoryDivider = useAgentStore((s) => {
|
19
69
|
const config = agentSelectors.currentAgentChatConfig(s);
|
@@ -29,7 +79,17 @@ const MainChatItem = memo<ThreadChatItemProps>(({ id, index }) => {
|
|
29
79
|
return (
|
30
80
|
<ChatItem
|
31
81
|
actionBar={actionBar}
|
82
|
+
className={showThread ? cx(styles.line, styles[placement]) : ''}
|
32
83
|
enableHistoryDivider={enableHistoryDivider}
|
84
|
+
endRender={
|
85
|
+
showThread && (
|
86
|
+
<Thread
|
87
|
+
id={id}
|
88
|
+
placement={placement}
|
89
|
+
style={{ marginTop: displayMode === 'docs' ? 12 : undefined }}
|
90
|
+
/>
|
91
|
+
)
|
92
|
+
}
|
33
93
|
id={id}
|
34
94
|
index={index}
|
35
95
|
/>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { useQueryState } from 'nuqs';
|
4
|
+
import { memo, useEffect, useLayoutEffect } from 'react';
|
5
|
+
import { createStoreUpdater } from 'zustand-utils';
|
6
|
+
|
7
|
+
import { useChatStore } from '@/store/chat';
|
8
|
+
|
9
|
+
// sync outside state to useChatStore
|
10
|
+
const ThreadHydration = memo(() => {
|
11
|
+
const useStoreUpdater = createStoreUpdater(useChatStore);
|
12
|
+
|
13
|
+
// two-way bindings the topic params to chat store
|
14
|
+
const [portalThread, setThread] = useQueryState('portalThread');
|
15
|
+
useStoreUpdater('portalThreadId', portalThread);
|
16
|
+
|
17
|
+
useLayoutEffect(() => {
|
18
|
+
const unsubscribe = useChatStore.subscribe(
|
19
|
+
(s) => s.portalThreadId,
|
20
|
+
(state) => {
|
21
|
+
setThread(!state ? null : state);
|
22
|
+
},
|
23
|
+
);
|
24
|
+
|
25
|
+
return () => {
|
26
|
+
unsubscribe();
|
27
|
+
};
|
28
|
+
}, []);
|
29
|
+
|
30
|
+
// should open portal automatically when portalThread is set
|
31
|
+
useEffect(() => {
|
32
|
+
if (!!portalThread && !useChatStore.getState().showPortal) {
|
33
|
+
useChatStore.getState().togglePortal(true);
|
34
|
+
}
|
35
|
+
}, [portalThread]);
|
36
|
+
|
37
|
+
const [activeTopicId, useFetchThreads] = useChatStore((s) => [
|
38
|
+
s.activeTopicId,
|
39
|
+
s.useFetchThreads,
|
40
|
+
]);
|
41
|
+
|
42
|
+
useFetchThreads(activeTopicId);
|
43
|
+
|
44
|
+
return null;
|
45
|
+
});
|
46
|
+
|
47
|
+
export default ThreadHydration;
|
@@ -1,12 +1,13 @@
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
2
2
|
|
3
|
+
import { PortalHeader } from '@/features/Portal/router';
|
4
|
+
|
3
5
|
import Body from '../features/Body';
|
4
|
-
import Header from '../features/Header';
|
5
6
|
|
6
7
|
const Layout = ({ children }: PropsWithChildren) => {
|
7
8
|
return (
|
8
9
|
<>
|
9
|
-
<
|
10
|
+
<PortalHeader />
|
10
11
|
<Body>{children}</Body>
|
11
12
|
</>
|
12
13
|
);
|
@@ -1,17 +1,58 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { Modal } from '@lobehub/ui';
|
4
|
+
import { createStyles } from 'antd-style';
|
1
5
|
import { PropsWithChildren } from 'react';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
2
7
|
import { Flexbox } from 'react-layout-kit';
|
3
8
|
|
9
|
+
import { PortalHeader } from '@/features/Portal/router';
|
10
|
+
import { useChatStore } from '@/store/chat';
|
11
|
+
|
12
|
+
const useStyles = createStyles(({ css, token }) => ({
|
13
|
+
container: css`
|
14
|
+
background: linear-gradient(${token.colorBgElevated}, ${token.colorBgLayout}) !important;
|
15
|
+
`,
|
16
|
+
}));
|
17
|
+
|
4
18
|
const Layout = ({ children }: PropsWithChildren) => {
|
19
|
+
const { styles, cx } = useStyles();
|
20
|
+
const [showMobilePortal, isPortalThread, togglePortal] = useChatStore((s) => [
|
21
|
+
s.showPortal,
|
22
|
+
!!s.portalThreadId,
|
23
|
+
s.togglePortal,
|
24
|
+
]);
|
25
|
+
const { t } = useTranslation('portal');
|
26
|
+
|
5
27
|
return (
|
6
|
-
<
|
28
|
+
<Modal
|
29
|
+
allowFullscreen
|
30
|
+
className={cx(isPortalThread && styles.container)}
|
31
|
+
height={'95%'}
|
32
|
+
onCancel={() => togglePortal(false)}
|
33
|
+
open={showMobilePortal}
|
34
|
+
styles={{
|
35
|
+
body: { padding: 0 },
|
36
|
+
header: { display: 'none' },
|
37
|
+
}}
|
38
|
+
title={t('title')}
|
39
|
+
>
|
40
|
+
<PortalHeader />
|
7
41
|
<Flexbox
|
8
|
-
|
9
|
-
|
10
|
-
|
42
|
+
gap={8}
|
43
|
+
height={'calc(100% - 52px)'}
|
44
|
+
padding={'0 8px'}
|
45
|
+
style={{ overflow: 'hidden' }}
|
11
46
|
>
|
12
|
-
|
47
|
+
<Flexbox
|
48
|
+
height={'100%'}
|
49
|
+
style={{ marginInline: -8, overflow: 'hidden', position: 'relative' }}
|
50
|
+
width={'calc(100% + 16px)'}
|
51
|
+
>
|
52
|
+
{children}
|
53
|
+
</Flexbox>
|
13
54
|
</Flexbox>
|
14
|
-
</
|
55
|
+
</Modal>
|
15
56
|
);
|
16
57
|
};
|
17
58
|
|
@@ -12,7 +12,8 @@ const useStyles = createStyles(({ css, prefixCls }) => ({
|
|
12
12
|
justify-content: center;
|
13
13
|
|
14
14
|
height: 44px;
|
15
|
-
padding: 8px;
|
15
|
+
padding-block: 8px;
|
16
|
+
padding-inline: 12px;
|
16
17
|
|
17
18
|
.${prefixCls}-skeleton-content {
|
18
19
|
display: flex;
|
@@ -48,7 +49,7 @@ export const Placeholder = memo(() => {
|
|
48
49
|
|
49
50
|
export const SkeletonList = memo(() => (
|
50
51
|
<Flexbox style={{ paddingTop: 6 }}>
|
51
|
-
{Array.from({ length:
|
52
|
+
{Array.from({ length: 6 }).map((_, i) => (
|
52
53
|
<Placeholder key={i} />
|
53
54
|
))}
|
54
55
|
</Flexbox>
|
package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/index.tsx
CHANGED
@@ -15,7 +15,7 @@ import TopicGroupItem from './GroupItem';
|
|
15
15
|
const ByTimeMode = memo(() => {
|
16
16
|
const { t } = useTranslation('topic');
|
17
17
|
const virtuosoRef = useRef<VirtuosoHandle>(null);
|
18
|
-
const [activeTopicId] = useChatStore((s) => [s.activeTopicId]);
|
18
|
+
const [activeTopicId, activeThreadId] = useChatStore((s) => [s.activeTopicId, s.activeThreadId]);
|
19
19
|
const groupTopics = useChatStore(topicSelectors.groupedTopicsSelector, isEqual);
|
20
20
|
|
21
21
|
const { groups, groupCounts, topics } = useMemo(() => {
|
@@ -39,10 +39,17 @@ const ByTimeMode = memo(() => {
|
|
39
39
|
return index === 0 ? (
|
40
40
|
<TopicItem active={!activeTopicId} fav={favorite} title={title} />
|
41
41
|
) : (
|
42
|
-
<TopicItem
|
42
|
+
<TopicItem
|
43
|
+
active={activeTopicId === id}
|
44
|
+
fav={favorite}
|
45
|
+
id={id}
|
46
|
+
key={id}
|
47
|
+
threadId={activeThreadId}
|
48
|
+
title={title}
|
49
|
+
/>
|
43
50
|
);
|
44
51
|
},
|
45
|
-
[activeTopicId, topics],
|
52
|
+
[activeTopicId, topics, activeThreadId],
|
46
53
|
);
|
47
54
|
|
48
55
|
const groupContent = useCallback(
|
@@ -42,7 +42,7 @@ const FlatMode = memo(() => {
|
|
42
42
|
// components={{ ScrollSeekPlaceholder: Placeholder }}
|
43
43
|
computeItemKey={(_, item) => item.id}
|
44
44
|
data={topics}
|
45
|
-
|
45
|
+
defaultItemHeight={44}
|
46
46
|
initialTopMostItemIndex={Math.max(activeIndex, 0)}
|
47
47
|
itemContent={itemContent}
|
48
48
|
overscan={44 * 10}
|
package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ThreadItem/Content.tsx
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
import { ActionIcon, EditableText, Icon } from '@lobehub/ui';
|
2
|
+
import { App, Dropdown, type MenuProps, Typography } from 'antd';
|
3
|
+
import { createStyles } from 'antd-style';
|
4
|
+
import { MoreVertical, PencilLine, Trash } from 'lucide-react';
|
5
|
+
import { memo, useMemo } from 'react';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
8
|
+
|
9
|
+
import BubblesLoading from '@/components/BubblesLoading';
|
10
|
+
import { LOADING_FLAT } from '@/const/message';
|
11
|
+
import { useIsMobile } from '@/hooks/useIsMobile';
|
12
|
+
import { useChatStore } from '@/store/chat';
|
13
|
+
|
14
|
+
const useStyles = createStyles(({ css, token }) => ({
|
15
|
+
active: css`
|
16
|
+
color: ${token.colorText};
|
17
|
+
`,
|
18
|
+
content: css`
|
19
|
+
position: relative;
|
20
|
+
overflow: hidden;
|
21
|
+
flex: 1;
|
22
|
+
`,
|
23
|
+
title: css`
|
24
|
+
flex: 1;
|
25
|
+
|
26
|
+
height: 28px;
|
27
|
+
|
28
|
+
line-height: 28px;
|
29
|
+
color: ${token.colorTextSecondary};
|
30
|
+
text-align: start;
|
31
|
+
`,
|
32
|
+
}));
|
33
|
+
const { Paragraph } = Typography;
|
34
|
+
|
35
|
+
interface TopicContentProps {
|
36
|
+
active?: boolean;
|
37
|
+
id: string;
|
38
|
+
showMore?: boolean;
|
39
|
+
title: string;
|
40
|
+
}
|
41
|
+
|
42
|
+
const Content = memo<TopicContentProps>(({ id, title, active, showMore }) => {
|
43
|
+
const { t } = useTranslation(['thread', 'common']);
|
44
|
+
|
45
|
+
const mobile = useIsMobile();
|
46
|
+
|
47
|
+
const [editing, updateThreadTitle, removeThread] = useChatStore((s) => [
|
48
|
+
s.threadRenamingId === id,
|
49
|
+
s.updateThreadTitle,
|
50
|
+
s.removeThread,
|
51
|
+
]);
|
52
|
+
const { styles, cx } = useStyles();
|
53
|
+
|
54
|
+
const toggleEditing = (visible?: boolean) => {
|
55
|
+
useChatStore.setState({ threadRenamingId: visible ? id : '' });
|
56
|
+
};
|
57
|
+
|
58
|
+
const { modal } = App.useApp();
|
59
|
+
|
60
|
+
const items = useMemo<MenuProps['items']>(
|
61
|
+
() => [
|
62
|
+
{
|
63
|
+
icon: <Icon icon={PencilLine} />,
|
64
|
+
key: 'rename',
|
65
|
+
label: t('rename', { ns: 'common' }),
|
66
|
+
onClick: () => {
|
67
|
+
toggleEditing(true);
|
68
|
+
},
|
69
|
+
},
|
70
|
+
{
|
71
|
+
type: 'divider',
|
72
|
+
},
|
73
|
+
{
|
74
|
+
danger: true,
|
75
|
+
icon: <Icon icon={Trash} />,
|
76
|
+
key: 'delete',
|
77
|
+
label: t('delete', { ns: 'common' }),
|
78
|
+
onClick: () => {
|
79
|
+
if (!id) return;
|
80
|
+
|
81
|
+
modal.confirm({
|
82
|
+
centered: true,
|
83
|
+
okButtonProps: { danger: true },
|
84
|
+
onOk: async () => {
|
85
|
+
await removeThread(id);
|
86
|
+
},
|
87
|
+
title: t('actions.confirmRemoveThread'),
|
88
|
+
});
|
89
|
+
},
|
90
|
+
},
|
91
|
+
],
|
92
|
+
[],
|
93
|
+
);
|
94
|
+
|
95
|
+
return (
|
96
|
+
<Flexbox
|
97
|
+
align={'center'}
|
98
|
+
gap={8}
|
99
|
+
horizontal
|
100
|
+
justify={'space-between'}
|
101
|
+
onDoubleClick={(e) => {
|
102
|
+
if (!id) return;
|
103
|
+
if (e.altKey) toggleEditing(true);
|
104
|
+
}}
|
105
|
+
>
|
106
|
+
{!editing ? (
|
107
|
+
title === LOADING_FLAT ? (
|
108
|
+
<Flexbox flex={1} height={28} justify={'center'}>
|
109
|
+
<BubblesLoading />
|
110
|
+
</Flexbox>
|
111
|
+
) : (
|
112
|
+
<Paragraph
|
113
|
+
className={cx(styles.title, active && styles.active)}
|
114
|
+
ellipsis={{ rows: 1, tooltip: { placement: 'left', title } }}
|
115
|
+
style={{ margin: 0 }}
|
116
|
+
>
|
117
|
+
{title}
|
118
|
+
</Paragraph>
|
119
|
+
)
|
120
|
+
) : (
|
121
|
+
<EditableText
|
122
|
+
editing={editing}
|
123
|
+
onChangeEnd={(v) => {
|
124
|
+
if (title !== v) {
|
125
|
+
updateThreadTitle(id, v);
|
126
|
+
}
|
127
|
+
toggleEditing(false);
|
128
|
+
}}
|
129
|
+
onEditingChange={toggleEditing}
|
130
|
+
showEditIcon={false}
|
131
|
+
size={'small'}
|
132
|
+
style={{
|
133
|
+
height: 28,
|
134
|
+
}}
|
135
|
+
type={'pure'}
|
136
|
+
value={title}
|
137
|
+
/>
|
138
|
+
)}
|
139
|
+
{(showMore || mobile) && !editing && (
|
140
|
+
<Dropdown
|
141
|
+
arrow={false}
|
142
|
+
menu={{
|
143
|
+
items: items,
|
144
|
+
onClick: ({ domEvent }) => {
|
145
|
+
domEvent.stopPropagation();
|
146
|
+
},
|
147
|
+
}}
|
148
|
+
trigger={['click']}
|
149
|
+
>
|
150
|
+
<ActionIcon
|
151
|
+
className="topic-more"
|
152
|
+
icon={MoreVertical}
|
153
|
+
onClick={(e) => {
|
154
|
+
e.stopPropagation();
|
155
|
+
}}
|
156
|
+
size={'small'}
|
157
|
+
/>
|
158
|
+
</Dropdown>
|
159
|
+
)}
|
160
|
+
</Flexbox>
|
161
|
+
);
|
162
|
+
});
|
163
|
+
|
164
|
+
export default Content;
|
@@ -0,0 +1,98 @@
|
|
1
|
+
import { createStyles } from 'antd-style';
|
2
|
+
import { memo, useState } from 'react';
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
4
|
+
|
5
|
+
import { useChatStore } from '@/store/chat';
|
6
|
+
import { useGlobalStore } from '@/store/global';
|
7
|
+
|
8
|
+
import Content from './Content';
|
9
|
+
|
10
|
+
const useStyles = createStyles(({ css, token, isDarkMode }, index: number) => ({
|
11
|
+
active: css`
|
12
|
+
background: ${isDarkMode ? token.colorFillSecondary : token.colorFillTertiary};
|
13
|
+
transition: background 200ms ${token.motionEaseOut};
|
14
|
+
|
15
|
+
&:hover {
|
16
|
+
background: ${token.colorFill};
|
17
|
+
}
|
18
|
+
`,
|
19
|
+
container: css`
|
20
|
+
margin-inline: 8px;
|
21
|
+
|
22
|
+
&::after {
|
23
|
+
content: '';
|
24
|
+
|
25
|
+
position: absolute;
|
26
|
+
inset-block: 50px ${index * 40 + 20}px;
|
27
|
+
inset-inline-start: 26px;
|
28
|
+
|
29
|
+
width: 18px;
|
30
|
+
|
31
|
+
border-block-end: 2px solid ${token.colorBorderSecondary};
|
32
|
+
border-inline-start: 2px solid ${token.colorBorderSecondary};
|
33
|
+
border-end-start-radius: 8px;
|
34
|
+
}
|
35
|
+
|
36
|
+
&.thread-item {
|
37
|
+
width: calc(100% - 16px);
|
38
|
+
}
|
39
|
+
`,
|
40
|
+
split: css`
|
41
|
+
border-block-end: 1px solid ${token.colorSplit};
|
42
|
+
`,
|
43
|
+
wrapper: css`
|
44
|
+
cursor: pointer;
|
45
|
+
|
46
|
+
width: calc(100% - 36px);
|
47
|
+
margin-block: 2px;
|
48
|
+
padding-block: 4px;
|
49
|
+
padding-inline: 8px;
|
50
|
+
|
51
|
+
border-radius: ${token.borderRadius}px;
|
52
|
+
|
53
|
+
&:hover {
|
54
|
+
background: ${token.colorFillSecondary};
|
55
|
+
}
|
56
|
+
`,
|
57
|
+
}));
|
58
|
+
|
59
|
+
export interface ThreadItemProps {
|
60
|
+
id: string;
|
61
|
+
index: number;
|
62
|
+
title: string;
|
63
|
+
}
|
64
|
+
|
65
|
+
const ThreadItem = memo<ThreadItemProps>(({ title, id, index }) => {
|
66
|
+
const { styles, cx } = useStyles(index);
|
67
|
+
const toggleConfig = useGlobalStore((s) => s.toggleMobileTopic);
|
68
|
+
const [toggleThread, activeThreadId] = useChatStore((s) => [s.switchThread, s.activeThreadId]);
|
69
|
+
const [isHover, setHovering] = useState(false);
|
70
|
+
|
71
|
+
const active = id === activeThreadId;
|
72
|
+
return (
|
73
|
+
<Flexbox className={cx(styles.container, 'thread-item')} horizontal>
|
74
|
+
<Flexbox height={36} width={36} />
|
75
|
+
<Flexbox
|
76
|
+
align={'center'}
|
77
|
+
className={cx(styles.wrapper, active && styles.active)}
|
78
|
+
distribution={'space-between'}
|
79
|
+
flex={1}
|
80
|
+
horizontal
|
81
|
+
onClick={() => {
|
82
|
+
toggleThread(id);
|
83
|
+
toggleConfig(false);
|
84
|
+
}}
|
85
|
+
onMouseEnter={() => {
|
86
|
+
setHovering(true);
|
87
|
+
}}
|
88
|
+
onMouseLeave={() => {
|
89
|
+
setHovering(false);
|
90
|
+
}}
|
91
|
+
>
|
92
|
+
{<Content active={active} id={id} showMore={isHover} title={title} />}
|
93
|
+
</Flexbox>
|
94
|
+
</Flexbox>
|
95
|
+
);
|
96
|
+
});
|
97
|
+
|
98
|
+
export default ThreadItem;
|