@lobehub/chat 0.118.10 → 0.119.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +8 -8
  3. package/README.zh-CN.md +8 -8
  4. package/docs/Home.md +1 -1
  5. package/docs/_Sidebar.md +1 -1
  6. package/locales/de-DE/chat.json +10 -0
  7. package/locales/de-DE/common.json +1 -0
  8. package/locales/de-DE/error.json +4 -4
  9. package/locales/en-US/chat.json +10 -0
  10. package/locales/en-US/common.json +1 -0
  11. package/locales/en-US/error.json +1 -1
  12. package/locales/es-ES/chat.json +10 -0
  13. package/locales/es-ES/common.json +1 -0
  14. package/locales/fr-FR/chat.json +10 -0
  15. package/locales/fr-FR/common.json +1 -0
  16. package/locales/fr-FR/error.json +1 -2
  17. package/locales/it-IT/chat.json +10 -0
  18. package/locales/it-IT/common.json +1 -0
  19. package/locales/it-IT/error.json +5 -5
  20. package/locales/ja-JP/chat.json +10 -0
  21. package/locales/ja-JP/common.json +1 -0
  22. package/locales/ko-KR/chat.json +10 -0
  23. package/locales/ko-KR/common.json +1 -0
  24. package/locales/ko-KR/error.json +4 -4
  25. package/locales/nl-NL/chat.json +10 -0
  26. package/locales/nl-NL/common.json +1 -0
  27. package/locales/pl-PL/chat.json +10 -0
  28. package/locales/pl-PL/common.json +1 -0
  29. package/locales/pl-PL/error.json +2 -3
  30. package/locales/pt-BR/chat.json +10 -0
  31. package/locales/pt-BR/common.json +1 -0
  32. package/locales/ru-RU/chat.json +10 -0
  33. package/locales/ru-RU/common.json +1 -0
  34. package/locales/ru-RU/error.json +3 -3
  35. package/locales/tr-TR/chat.json +10 -0
  36. package/locales/tr-TR/common.json +1 -0
  37. package/locales/vi-VN/chat.json +10 -0
  38. package/locales/vi-VN/common.json +1 -0
  39. package/locales/vi-VN/error.json +1 -1
  40. package/locales/zh-CN/chat.json +10 -0
  41. package/locales/zh-CN/common.json +1 -0
  42. package/locales/zh-TW/chat.json +10 -0
  43. package/locales/zh-TW/common.json +1 -0
  44. package/package.json +6 -6
  45. package/src/app/chat/(desktop)/features/Conversation.tsx +17 -0
  46. package/src/app/chat/(desktop)/features/HotKeys.tsx +44 -0
  47. package/src/app/chat/(desktop)/index.tsx +2 -3
  48. package/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx +1 -1
  49. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +24 -3
  50. package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +43 -2
  51. package/src/database/models/__tests__/message.test.ts +48 -0
  52. package/src/database/models/__tests__/topic.test.ts +135 -0
  53. package/src/database/models/message.ts +35 -0
  54. package/src/database/models/session.ts +11 -1
  55. package/src/database/models/topic.ts +34 -2
  56. package/src/features/ChatInput/ActionBar/Clear.tsx +0 -6
  57. package/src/features/Conversation/Actions/Assistant.tsx +16 -5
  58. package/src/features/Conversation/Actions/Error.tsx +2 -2
  59. package/src/features/Conversation/Actions/Fallback.tsx +4 -3
  60. package/src/features/Conversation/Actions/Function.tsx +3 -3
  61. package/src/features/Conversation/Actions/User.tsx +3 -3
  62. package/src/features/Conversation/Actions/customAction.ts +2 -4
  63. package/src/features/Conversation/Actions/index.ts +50 -45
  64. package/src/features/Conversation/Error/InvalidAccess.tsx +1 -1
  65. package/src/features/Conversation/Error/OpenAPIKey.tsx +1 -1
  66. package/src/features/Conversation/Error/OpenAiBizError.tsx +1 -1
  67. package/src/features/Conversation/Error/Plugin/PluginError.tsx +1 -1
  68. package/src/features/Conversation/Error/Plugin/PluginSettings.tsx +1 -1
  69. package/src/features/Conversation/Error/index.tsx +2 -2
  70. package/src/features/Conversation/Extras/Assistant.tsx +32 -29
  71. package/src/features/Conversation/Extras/User.tsx +2 -1
  72. package/src/features/Conversation/Extras/index.ts +4 -4
  73. package/src/features/Conversation/Messages/index.ts +3 -3
  74. package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +58 -0
  75. package/src/features/Conversation/components/ChatItem/index.tsx +126 -0
  76. package/src/features/Conversation/components/ChatList/HistoryDivider.tsx +6 -3
  77. package/src/features/Conversation/components/ChatList/index.tsx +35 -79
  78. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +43 -31
  79. package/src/features/Conversation/hooks/useInitConversation.ts +4 -1
  80. package/src/features/Conversation/index.tsx +4 -3
  81. package/src/features/Conversation/types/index.tsx +35 -0
  82. package/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx +7 -2
  83. package/src/locales/default/chat.ts +10 -1
  84. package/src/locales/default/common.ts +1 -0
  85. package/src/services/config.ts +22 -0
  86. package/src/services/session.ts +6 -0
  87. package/src/services/topic.ts +4 -0
  88. package/src/store/chat/slices/message/action.ts +89 -2
  89. package/src/store/chat/slices/message/selectors.ts +10 -0
  90. package/src/store/chat/slices/topic/action.ts +22 -0
  91. package/src/store/global/slices/common/action.ts +2 -0
  92. package/src/store/session/slices/session/action.test.ts +136 -0
  93. package/src/store/session/slices/session/action.ts +26 -6
  94. package/src/types/topic.ts +1 -0
  95. package/src/utils/fetch.test.ts +131 -0
  96. package/src/utils/fetch.ts +26 -10
  97. package/src/features/Conversation/components/ChatList/ActionsBar.tsx +0 -27
  98. package/src/features/Conversation/components/ChatList/Item.tsx +0 -236
  99. package/src/features/Conversation/container/index.tsx +0 -83
  100. /package/docs/Development/{Steup-Development.md → Setup-Development.md} +0 -0
  101. /package/docs/Development/{Steup-Development.zh-CN.md → Setup-Development.zh-CN.md} +0 -0
@@ -8,41 +8,44 @@ import { useSessionStore } from '@/store/session';
8
8
  import { agentSelectors } from '@/store/session/selectors';
9
9
  import { ChatMessage } from '@/types/message';
10
10
 
11
+ import { RenderMessageExtra } from '../types';
11
12
  import ExtraContainer from './ExtraContainer';
12
13
  import TTS from './TTS';
13
14
  import Translate from './Translate';
14
15
 
15
- export const AssistantMessageExtra = memo<ChatMessage>(({ extra, id, content }) => {
16
- const model = useSessionStore(agentSelectors.currentAgentModel);
17
- const loading = useChatStore((s) => s.chatLoadingId === id);
16
+ export const AssistantMessageExtra: RenderMessageExtra = memo<ChatMessage>(
17
+ ({ extra, id, content }) => {
18
+ const model = useSessionStore(agentSelectors.currentAgentModel);
19
+ const loading = useChatStore((s) => s.chatLoadingId === id);
18
20
 
19
- const showModelTag = extra?.fromModel && model !== extra?.fromModel;
20
- const showTranslate = !!extra?.translate;
21
- const showTTS = !!extra?.tts;
21
+ const showModelTag = extra?.fromModel && model !== extra?.fromModel;
22
+ const showTranslate = !!extra?.translate;
23
+ const showTTS = !!extra?.tts;
22
24
 
23
- const showExtra = showModelTag || showTranslate || showTTS;
25
+ const showExtra = showModelTag || showTranslate || showTTS;
24
26
 
25
- if (!showExtra) return;
27
+ if (!showExtra) return;
26
28
 
27
- return (
28
- <Flexbox gap={8} style={{ marginTop: 8 }}>
29
- {showModelTag && (
30
- <div>
31
- <Tag icon={<SiOpenai size={'1em'} />}>{extra?.fromModel as string}</Tag>
32
- </div>
33
- )}
34
- <>
35
- {extra?.tts && (
36
- <ExtraContainer>
37
- <TTS content={content} id={id} loading={loading} {...extra?.tts} />
38
- </ExtraContainer>
29
+ return (
30
+ <Flexbox gap={8} style={{ marginTop: 8 }}>
31
+ {showModelTag && (
32
+ <div>
33
+ <Tag icon={<SiOpenai size={'1em'} />}>{extra?.fromModel as string}</Tag>
34
+ </div>
39
35
  )}
40
- {extra?.translate && (
41
- <ExtraContainer>
42
- <Translate id={id} loading={loading} {...extra?.translate} />
43
- </ExtraContainer>
44
- )}
45
- </>
46
- </Flexbox>
47
- );
48
- });
36
+ <>
37
+ {extra?.tts && (
38
+ <ExtraContainer>
39
+ <TTS content={content} id={id} loading={loading} {...extra?.tts} />
40
+ </ExtraContainer>
41
+ )}
42
+ {extra?.translate && (
43
+ <ExtraContainer>
44
+ <Translate id={id} loading={loading} {...extra?.translate} />
45
+ </ExtraContainer>
46
+ )}
47
+ </>
48
+ </Flexbox>
49
+ );
50
+ },
51
+ );
@@ -3,11 +3,12 @@ import { memo } from 'react';
3
3
  import { useChatStore } from '@/store/chat';
4
4
  import { ChatMessage } from '@/types/message';
5
5
 
6
+ import { RenderMessageExtra } from '../types';
6
7
  import ExtraContainer from './ExtraContainer';
7
8
  import TTS from './TTS';
8
9
  import Translate from './Translate';
9
10
 
10
- export const UserMessageExtra = memo<ChatMessage>(({ extra, id, content }) => {
11
+ export const UserMessageExtra: RenderMessageExtra = memo<ChatMessage>(({ extra, id, content }) => {
11
12
  const loading = useChatStore((s) => s.chatLoadingId === id);
12
13
 
13
14
  const showTranslate = !!extra?.translate;
@@ -1,8 +1,8 @@
1
- import { ChatListProps, RenderMessageExtra } from '../components/ChatList';
1
+ import { RenderMessageExtra } from '../types';
2
2
  import { AssistantMessageExtra } from './Assistant';
3
3
  import { UserMessageExtra } from './User';
4
4
 
5
- export const renderMessagesExtra: ChatListProps['renderMessagesExtra'] = {
6
- assistant: AssistantMessageExtra as unknown as RenderMessageExtra,
7
- user: UserMessageExtra as unknown as RenderMessageExtra,
5
+ export const renderMessagesExtra: Record<string, RenderMessageExtra> = {
6
+ assistant: AssistantMessageExtra,
7
+ user: UserMessageExtra,
8
8
  };
@@ -6,20 +6,20 @@ import { useSessionStore } from '@/store/session';
6
6
  import { sessionSelectors } from '@/store/session/selectors';
7
7
  import { pathString } from '@/utils/url';
8
8
 
9
- import { ChatListProps } from '../components/ChatList';
9
+ import { OnAvatarsClick, RenderMessage } from '../types';
10
10
  import { AssistantMessage } from './Assistant';
11
11
  import { DefaultMessage } from './Default';
12
12
  import { FunctionMessage } from './Function';
13
13
  import { UserMessage } from './User';
14
14
 
15
- export const renderMessages: ChatListProps['renderMessages'] = {
15
+ export const renderMessages: Record<string, RenderMessage> = {
16
16
  assistant: AssistantMessage,
17
17
  default: DefaultMessage,
18
18
  function: FunctionMessage,
19
19
  user: UserMessage,
20
20
  };
21
21
 
22
- export const useAvatarsClick = (): ChatListProps['onAvatarsClick'] => {
22
+ export const useAvatarsClick = (): OnAvatarsClick => {
23
23
  const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
24
24
  const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
25
25
  const { mobile } = useResponsive();
@@ -0,0 +1,58 @@
1
+ import { ActionEvent, ActionIconGroup, type ActionIconGroupProps } from '@lobehub/ui';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { memo, useCallback } from 'react';
4
+
5
+ import { useChatStore } from '@/store/chat';
6
+ import { chatSelectors } from '@/store/chat/selectors';
7
+ import { useSessionStore } from '@/store/session';
8
+ import { agentSelectors } from '@/store/session/selectors';
9
+
10
+ import { renderActions, useActionsClick } from '../../Actions';
11
+ import { useChatListActionsBar } from '../../hooks/useChatListActionsBar';
12
+
13
+ export type ActionsBarProps = ActionIconGroupProps;
14
+
15
+ const ActionsBar = memo<ActionsBarProps>((props) => {
16
+ const { regenerate, edit, copy, divider, del } = useChatListActionsBar();
17
+ return (
18
+ <ActionIconGroup
19
+ dropdownMenu={[edit, copy, regenerate, divider, del]}
20
+ items={[regenerate, edit]}
21
+ type="ghost"
22
+ {...props}
23
+ />
24
+ );
25
+ });
26
+
27
+ interface ActionsProps {
28
+ index: number;
29
+ setEditing: (edit: boolean) => void;
30
+ }
31
+ const Actions = memo<ActionsProps>(({ index, setEditing }) => {
32
+ const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
33
+
34
+ const item = useChatStore(
35
+ (s) => chatSelectors.currentChatsWithGuideMessage(meta)(s)[index],
36
+ isEqual,
37
+ );
38
+ const onActionsClick = useActionsClick();
39
+
40
+ const handleActionClick = useCallback(
41
+ async (action: ActionEvent) => {
42
+ switch (action.key) {
43
+ case 'edit': {
44
+ setEditing(true);
45
+ }
46
+ }
47
+
48
+ onActionsClick(action, item);
49
+ },
50
+ [item],
51
+ );
52
+
53
+ const RenderFunction = renderActions[item?.role] ?? ActionsBar;
54
+
55
+ return <RenderFunction {...item} onActionClick={handleActionClick} />;
56
+ });
57
+
58
+ export default Actions;
@@ -0,0 +1,126 @@
1
+ import { type AlertProps, ChatItem } from '@lobehub/ui';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { ReactNode, memo, useCallback, useMemo, useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { useChatStore } from '@/store/chat';
7
+ import { chatSelectors } from '@/store/chat/selectors';
8
+ import { useSessionStore } from '@/store/session';
9
+ import { agentSelectors } from '@/store/session/selectors';
10
+ import { ChatMessage } from '@/types/message';
11
+
12
+ import { renderErrorMessages } from '../../Error';
13
+ import { renderMessagesExtra } from '../../Extras';
14
+ import { renderMessages, useAvatarsClick } from '../../Messages';
15
+ import ActionsBar from './ActionsBar';
16
+
17
+ export interface ChatListItemProps {
18
+ index: number;
19
+ }
20
+
21
+ const Item = memo<ChatListItemProps>(({ index }) => {
22
+ const { t } = useTranslation('common');
23
+
24
+ const [editing, setEditing] = useState(false);
25
+ const [type = 'chat'] = useSessionStore((s) => {
26
+ const config = agentSelectors.currentAgentConfig(s);
27
+ return [config.displayMode];
28
+ });
29
+
30
+ const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
31
+ const item = useChatStore(
32
+ (s) => chatSelectors.currentChatsWithGuideMessage(meta)(s)[index],
33
+ isEqual,
34
+ );
35
+
36
+ const [loading, onMessageChange] = useChatStore((s) => [
37
+ s.chatLoadingId === item.id,
38
+ s.updateMessageContent,
39
+ ]);
40
+
41
+ const onAvatarsClick = useAvatarsClick();
42
+
43
+ const RenderMessage = useCallback(
44
+ ({ editableContent, data }: { data: ChatMessage; editableContent: ReactNode }) => {
45
+ if (!item?.role) return;
46
+ const RenderFunction = renderMessages[item.role] ?? renderMessages['default'];
47
+
48
+ if (!RenderFunction) return;
49
+
50
+ return <RenderFunction {...data} editableContent={editableContent} />;
51
+ },
52
+ [item.role],
53
+ );
54
+
55
+ const MessageExtra = useCallback(
56
+ ({ data }: { data: ChatMessage }) => {
57
+ if (!renderMessagesExtra || !item?.role) return;
58
+ let RenderFunction;
59
+ if (renderMessagesExtra?.[item.role]) RenderFunction = renderMessagesExtra[item.role];
60
+
61
+ if (!RenderFunction) return;
62
+ return <RenderFunction {...data} />;
63
+ },
64
+ [item.role],
65
+ );
66
+
67
+ const ErrorMessage = useCallback(
68
+ ({ data }: { data: ChatMessage }) => {
69
+ if (!renderErrorMessages || !item?.error?.type) return;
70
+ let RenderFunction;
71
+ if (renderErrorMessages?.[item.error.type])
72
+ RenderFunction = renderErrorMessages[item.error.type].Render;
73
+ if (!RenderFunction && renderErrorMessages?.['default'])
74
+ RenderFunction = renderErrorMessages['default'].Render;
75
+ if (!RenderFunction) return;
76
+ return <RenderFunction {...data} />;
77
+ },
78
+ [item.error],
79
+ );
80
+
81
+ const error = useMemo(() => {
82
+ if (!item.error) return;
83
+ const message = item.error?.message;
84
+ let alertConfig = {};
85
+ if (item.error.type && renderErrorMessages?.[item.error.type]) {
86
+ alertConfig = renderErrorMessages[item.error.type]?.config as AlertProps;
87
+ }
88
+ return { message, ...alertConfig };
89
+ }, [item.error]);
90
+
91
+ return (
92
+ <ChatItem
93
+ actions={<ActionsBar index={index} setEditing={setEditing} />}
94
+ avatar={item.meta}
95
+ editing={editing}
96
+ error={error}
97
+ errorMessage={<ErrorMessage data={item} />}
98
+ loading={loading}
99
+ message={item.content}
100
+ messageExtra={<MessageExtra data={item} />}
101
+ onAvatarClick={onAvatarsClick?.(item.role)}
102
+ onChange={(value) => onMessageChange(item.id, value)}
103
+ onDoubleClick={(e) => {
104
+ if (item.id === 'default' || item.error) return;
105
+ if (item.role && ['assistant', 'user'].includes(item.role) && e.altKey) {
106
+ setEditing(true);
107
+ }
108
+ }}
109
+ onEditingChange={setEditing}
110
+ placement={type === 'chat' ? (item.role === 'user' ? 'right' : 'left') : 'left'}
111
+ primary={item.role === 'user'}
112
+ renderMessage={(editableContent) => (
113
+ <RenderMessage data={item} editableContent={editableContent} />
114
+ )}
115
+ text={{
116
+ cancel: t('cancel'),
117
+ confirm: t('ok'),
118
+ edit: t('edit'),
119
+ }}
120
+ time={item.updatedAt || item.createdAt}
121
+ type={type === 'chat' ? 'block' : 'pure'}
122
+ />
123
+ );
124
+ });
125
+
126
+ export default Item;
@@ -2,19 +2,22 @@ import { Icon, Tag } from '@lobehub/ui';
2
2
  import { Divider } from 'antd';
3
3
  import { Timer } from 'lucide-react';
4
4
  import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
5
6
 
6
7
  interface HistoryDividerProps {
7
8
  enable?: boolean;
8
- text?: string;
9
9
  }
10
10
 
11
- const HistoryDivider = memo<HistoryDividerProps>(({ enable, text }) => {
11
+ const HistoryDivider = memo<HistoryDividerProps>(({ enable }) => {
12
+ const { t } = useTranslation('common');
12
13
  if (!enable) return null;
13
14
 
14
15
  return (
15
16
  <div style={{ padding: '0 20px' }}>
16
17
  <Divider>
17
- <Tag icon={<Icon icon={Timer} />}>{text || 'History Message'}</Tag>
18
+ <Tag icon={<Icon icon={Timer} />}>
19
+ {t('historyRange', { defaultValue: 'History Message' })}
20
+ </Tag>
18
21
  </Divider>
19
22
  </div>
20
23
  );
@@ -1,88 +1,44 @@
1
+ import isEqual from 'fast-deep-equal';
1
2
  import { Fragment, memo } from 'react';
2
3
  import { Flexbox } from 'react-layout-kit';
3
4
 
4
- import type { ChatMessage } from '@/types/message';
5
+ import { useChatStore } from '@/store/chat';
6
+ import { chatSelectors } from '@/store/chat/selectors';
7
+ import { useSessionStore } from '@/store/session';
8
+ import { agentSelectors } from '@/store/session/selectors';
5
9
 
10
+ import Item from '../ChatItem';
6
11
  import HistoryDivider from './HistoryDivider';
7
- import Item, { ListItemProps } from './Item';
8
12
  import { useStyles } from './style';
9
13
 
10
- export interface ChatListProps extends ListItemProps {
11
- className?: string;
12
- /**
13
- * @description Data of chat messages to be displayed
14
- */
15
- data: ChatMessage[];
16
- enableHistoryCount?: boolean;
17
- historyCount?: number;
18
- loadingId?: string;
19
- }
20
- export type {
21
- OnActionsClick,
22
- OnAvatatsClick,
23
- OnMessageChange,
24
- RenderAction,
25
- RenderErrorMessage,
26
- RenderItem,
27
- RenderMessage,
28
- RenderMessageExtra,
29
- } from './Item';
30
-
31
- const ChatList = memo<ChatListProps>(
32
- ({
33
- onActionsClick,
34
- onAvatarsClick,
35
- renderMessagesExtra,
36
- className,
37
- data,
38
- type = 'chat',
39
- text,
40
- showTitle,
41
- onMessageChange,
42
- renderMessages,
43
- renderErrorMessages,
44
- loadingId,
45
- renderItems,
46
- enableHistoryCount,
47
- renderActions,
48
- historyCount = 0,
49
- }) => {
50
- const { cx, styles } = useStyles();
51
-
52
- return (
53
- <Flexbox className={cx(styles.container, className)}>
54
- {data.map((item, index) => {
55
- const itemProps = {
56
- loading: loadingId === item.id,
57
- onActionsClick,
58
- onAvatarsClick,
59
- onMessageChange,
60
- renderActions,
61
- renderErrorMessages,
62
- renderItems,
63
- renderMessages,
64
- renderMessagesExtra,
65
- showTitle,
66
- text,
67
- type,
68
- };
69
-
70
- const historyLength = data.length;
71
- const enableHistoryDivider =
72
- enableHistoryCount &&
73
- historyLength > historyCount &&
74
- historyCount === historyLength - index + 1;
75
-
76
- return (
77
- <Fragment key={item.id}>
78
- <HistoryDivider enable={enableHistoryDivider} text={text?.history} />
79
- <Item {...itemProps} {...item} />
80
- </Fragment>
81
- );
82
- })}
83
- </Flexbox>
84
- );
85
- },
86
- );
14
+ const ChatList = memo(() => {
15
+ const { styles } = useStyles();
16
+
17
+ const data = useChatStore(chatSelectors.currentChatIDsWithGuideMessage, isEqual);
18
+
19
+ const [enableHistoryCount, historyCount = 0] = useSessionStore((s) => {
20
+ const config = agentSelectors.currentAgentConfig(s);
21
+ return [config.enableHistoryCount, config.historyCount];
22
+ });
23
+
24
+ return (
25
+ <Flexbox className={styles.container}>
26
+ {data.map((item, index) => {
27
+ const historyLength = data.length;
28
+ const enableHistoryDivider =
29
+ enableHistoryCount &&
30
+ historyLength > historyCount &&
31
+ historyCount === historyLength - index + 1;
32
+
33
+ return (
34
+ <Fragment key={item}>
35
+ <HistoryDivider enable={enableHistoryDivider} />
36
+ <Item index={index} />
37
+ </Fragment>
38
+ );
39
+ })}
40
+ </Flexbox>
41
+ );
42
+ });
87
43
 
88
44
  export default ChatList;
@@ -1,43 +1,55 @@
1
1
  import { ActionIconGroupItems } from '@lobehub/ui/es/ActionIconGroup';
2
2
  import { Copy, Edit, RotateCw, Trash } from 'lucide-react';
3
+ import { useMemo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
3
5
 
4
6
  interface ChatListActionsBar {
5
7
  copy: ActionIconGroupItems;
6
8
  del: ActionIconGroupItems;
9
+ delAndRegenerate: ActionIconGroupItems;
7
10
  divider: { type: 'divider' };
8
11
  edit: ActionIconGroupItems;
9
12
  regenerate: ActionIconGroupItems;
10
13
  }
11
14
 
12
- export const useChatListActionsBar = (text?: {
13
- copy?: string;
14
- delete?: string;
15
- edit?: string;
16
- regenerate?: string;
17
- }): ChatListActionsBar => {
18
- return {
19
- copy: {
20
- icon: Copy,
21
- key: 'copy',
22
- label: text?.copy || 'Copy',
23
- },
24
- del: {
25
- icon: Trash,
26
- key: 'del',
27
- label: text?.delete || 'Delete',
28
- },
29
- divider: {
30
- type: 'divider',
31
- },
32
- edit: {
33
- icon: Edit,
34
- key: 'edit',
35
- label: text?.edit || 'Edit',
36
- },
37
- regenerate: {
38
- icon: RotateCw,
39
- key: 'regenerate',
40
- label: text?.regenerate || 'Regenerate',
41
- },
42
- };
15
+ export const useChatListActionsBar = (): ChatListActionsBar => {
16
+ const { t } = useTranslation('common');
17
+
18
+ return useMemo(
19
+ () => ({
20
+ copy: {
21
+ icon: Copy,
22
+ key: 'copy',
23
+ label: t('copy', { defaultValue: 'Copy' }),
24
+ },
25
+ del: {
26
+ danger: true,
27
+ icon: Trash,
28
+ key: 'del',
29
+ label: t('delete', { defaultValue: 'Delete' }),
30
+ },
31
+ delAndRegenerate: {
32
+ icon: RotateCw,
33
+ key: 'delAndRegenerate',
34
+ label: t('messageAction.delAndRegenerate', {
35
+ defaultValue: 'Delete and regenerate',
36
+ ns: 'chat',
37
+ }),
38
+ },
39
+ divider: {
40
+ type: 'divider',
41
+ },
42
+ edit: {
43
+ icon: Edit,
44
+ key: 'edit',
45
+ label: t('edit', { defaultValue: 'Edit' }),
46
+ },
47
+ regenerate: {
48
+ icon: RotateCw,
49
+ key: 'regenerate',
50
+ label: t('regenerate', { defaultValue: 'Regenerate' }),
51
+ },
52
+ }),
53
+ [],
54
+ );
43
55
  };
@@ -8,7 +8,8 @@ import { useToolStore } from '@/store/tool';
8
8
  export const useInitConversation = () => {
9
9
  const [sessionId] = useSessionStore((s) => [s.activeId]);
10
10
  const plugins = useSessionStore((s) => agentSelectors.currentAgentPlugins(s));
11
- const [activeTopicId, switchTopic, useFetchMessages, useFetchTopics] = useChatStore((s) => [
11
+ const [init, activeTopicId, switchTopic, useFetchMessages, useFetchTopics] = useChatStore((s) => [
12
+ s.messagesInit,
12
13
  s.activeTopicId,
13
14
  s.switchTopic,
14
15
  s.useFetchMessages,
@@ -40,4 +41,6 @@ export const useInitConversation = () => {
40
41
  unsubscribe();
41
42
  };
42
43
  }, []);
44
+
45
+ return init;
43
46
  };
@@ -8,8 +8,9 @@ import SafeSpacing from '@/components/SafeSpacing';
8
8
  import { useGlobalStore } from '@/store/global';
9
9
  import { settingsSelectors } from '@/store/global/selectors';
10
10
 
11
+ import ChatList from './components/ChatList';
11
12
  import ChatScrollAnchor from './components/ScrollAnchor';
12
- import ChatList from './container';
13
+ import SkeletonList from './components/SkeletonList';
13
14
  import { useInitConversation } from './hooks/useInitConversation';
14
15
 
15
16
  const useStyles = createStyles(({ css, responsive, stylish, cx }, fontSize: number = 14) =>
@@ -52,14 +53,14 @@ const Conversation = memo<ConversationProps>(({ mobile, chatInput }) => {
52
53
  const { styles } = useStyles(fontSize);
53
54
 
54
55
  // init conversation
55
- useInitConversation();
56
+ const init = useInitConversation();
56
57
 
57
58
  return (
58
59
  <Flexbox flex={1} style={{ position: 'relative' }}>
59
60
  <div style={{ flex: 1, overflow: 'hidden', position: 'relative' }}>
60
61
  <div className={styles} ref={ref}>
61
62
  {!mobile && <SafeSpacing />}
62
- <ChatList />
63
+ {init ? <ChatList /> : <SkeletonList />}
63
64
  <ChatScrollAnchor />
64
65
  </div>
65
66
  <BackBottom target={ref} text={t('backToBottom')} />
@@ -0,0 +1,35 @@
1
+ import { type AlertProps, type ChatItemProps } from '@lobehub/ui';
2
+ import { ActionEvent } from '@lobehub/ui';
3
+ import { FC, ReactNode } from 'react';
4
+
5
+ import { LLMRoleType } from '@/types/llm';
6
+ import { ChatMessage } from '@/types/message';
7
+
8
+ import { type ActionsBarProps } from '../components/ChatItem/ActionsBar';
9
+
10
+ export type OnActionsClick = (action: ActionEvent, message: ChatMessage) => void;
11
+ export type OnAvatarsClick = (role: RenderRole) => ChatItemProps['onAvatarClick'];
12
+ export type RenderRole = LLMRoleType | 'default' | string;
13
+ export type RenderMessage = FC<ChatMessage & { editableContent: ReactNode }>;
14
+ export type RenderMessageExtra = FC<ChatMessage>;
15
+ export interface RenderErrorMessage {
16
+ Render?: FC<ChatMessage>;
17
+ config?: AlertProps;
18
+ }
19
+ export type RenderAction = FC<ActionsBarProps & ChatMessage>;
20
+
21
+ export type RenderItem = FC<{ key: string } & ChatMessage & ListItemProps>;
22
+
23
+ export interface ListItemProps {
24
+ groupNav?: ChatItemProps['avatarAddon'];
25
+
26
+ renderItems?: {
27
+ [role: RenderRole]: RenderItem;
28
+ };
29
+
30
+ /**
31
+ * @description 是否显示聊天项的名称
32
+ * @default false
33
+ */
34
+ showTitle?: boolean;
35
+ }
@@ -12,9 +12,10 @@ const EditCustomPlugin = memo<{ identifier: string }>(({ identifier }) => {
12
12
  const { t } = useTranslation('plugin');
13
13
  const [showModal, setModal] = useState(false);
14
14
 
15
- const [installCustomPlugin, updateNewDevPlugin] = useToolStore((s) => [
15
+ const [installCustomPlugin, updateNewDevPlugin, uninstallCustomPlugin] = useToolStore((s) => [
16
16
  s.installCustomPlugin,
17
17
  s.updateNewCustomPlugin,
18
+ s.uninstallCustomPlugin,
18
19
  ]);
19
20
 
20
21
  const customPlugin = useToolStore(pluginSelectors.getCustomPluginById(identifier), isEqual);
@@ -27,10 +28,14 @@ const EditCustomPlugin = memo<{ identifier: string }>(({ identifier }) => {
27
28
  >
28
29
  <DevModal
29
30
  mode={'edit'}
31
+ onDelete={() => {
32
+ uninstallCustomPlugin(identifier);
33
+ setModal(false);
34
+ }}
30
35
  onOpenChange={setModal}
31
36
  onSave={async (devPlugin) => {
32
37
  await installCustomPlugin(devPlugin);
33
- // toggleAgentPlugin(devPlugin.identifier);
38
+ setModal(false);
34
39
  }}
35
40
  onValueChange={updateNewDevPlugin}
36
41
  open={showModal}