@lobehub/chat 0.118.9 → 0.119.0

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 (103) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/locales/de-DE/chat.json +10 -0
  3. package/locales/de-DE/common.json +1 -0
  4. package/locales/de-DE/error.json +19 -0
  5. package/locales/en-US/chat.json +10 -0
  6. package/locales/en-US/common.json +1 -0
  7. package/locales/en-US/error.json +19 -0
  8. package/locales/es-ES/chat.json +10 -0
  9. package/locales/es-ES/common.json +1 -0
  10. package/locales/es-ES/error.json +20 -1
  11. package/locales/fr-FR/chat.json +10 -0
  12. package/locales/fr-FR/common.json +1 -0
  13. package/locales/fr-FR/error.json +19 -0
  14. package/locales/it-IT/chat.json +10 -0
  15. package/locales/it-IT/common.json +1 -0
  16. package/locales/it-IT/error.json +19 -0
  17. package/locales/ja-JP/chat.json +10 -0
  18. package/locales/ja-JP/common.json +1 -0
  19. package/locales/ja-JP/error.json +20 -1
  20. package/locales/ko-KR/chat.json +10 -0
  21. package/locales/ko-KR/common.json +1 -0
  22. package/locales/ko-KR/error.json +20 -1
  23. package/locales/nl-NL/chat.json +10 -0
  24. package/locales/nl-NL/common.json +1 -0
  25. package/locales/nl-NL/error.json +20 -1
  26. package/locales/pl-PL/chat.json +10 -0
  27. package/locales/pl-PL/common.json +1 -0
  28. package/locales/pl-PL/error.json +20 -1
  29. package/locales/pt-BR/chat.json +10 -0
  30. package/locales/pt-BR/common.json +1 -0
  31. package/locales/pt-BR/error.json +20 -1
  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 +19 -0
  35. package/locales/tr-TR/chat.json +10 -0
  36. package/locales/tr-TR/common.json +1 -0
  37. package/locales/tr-TR/error.json +20 -1
  38. package/locales/vi-VN/chat.json +10 -0
  39. package/locales/vi-VN/common.json +1 -0
  40. package/locales/vi-VN/error.json +19 -0
  41. package/locales/zh-CN/chat.json +10 -0
  42. package/locales/zh-CN/common.json +1 -0
  43. package/locales/zh-CN/error.json +19 -0
  44. package/locales/zh-TW/chat.json +10 -0
  45. package/locales/zh-TW/common.json +1 -0
  46. package/locales/zh-TW/error.json +20 -1
  47. package/package.json +3 -3
  48. package/scripts/readmeWorkflow/syncPluginIndex.ts +9 -7
  49. package/src/app/chat/(desktop)/features/Conversation.tsx +17 -0
  50. package/src/app/chat/(desktop)/features/HotKeys.tsx +44 -0
  51. package/src/app/chat/(desktop)/index.tsx +2 -3
  52. package/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx +1 -1
  53. package/src/app/chat/features/ChatHeader/ShareButton/ShareModal.tsx +40 -41
  54. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +24 -3
  55. package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +43 -2
  56. package/src/database/models/__tests__/message.test.ts +48 -0
  57. package/src/database/models/__tests__/topic.test.ts +135 -0
  58. package/src/database/models/message.ts +35 -0
  59. package/src/database/models/session.ts +11 -1
  60. package/src/database/models/topic.ts +34 -2
  61. package/src/features/ChatInput/ActionBar/Clear.tsx +0 -6
  62. package/src/features/Conversation/Actions/Assistant.tsx +16 -5
  63. package/src/features/Conversation/Actions/Error.tsx +2 -2
  64. package/src/features/Conversation/Actions/Fallback.tsx +4 -3
  65. package/src/features/Conversation/Actions/Function.tsx +3 -3
  66. package/src/features/Conversation/Actions/User.tsx +3 -3
  67. package/src/features/Conversation/Actions/customAction.ts +2 -4
  68. package/src/features/Conversation/Actions/index.ts +50 -45
  69. package/src/features/Conversation/Error/InvalidAccess.tsx +1 -1
  70. package/src/features/Conversation/Error/OpenAPIKey.tsx +1 -1
  71. package/src/features/Conversation/Error/OpenAiBizError.tsx +1 -1
  72. package/src/features/Conversation/Error/Plugin/PluginError.tsx +1 -1
  73. package/src/features/Conversation/Error/Plugin/PluginSettings.tsx +1 -1
  74. package/src/features/Conversation/Error/index.tsx +2 -2
  75. package/src/features/Conversation/Extras/Assistant.tsx +32 -29
  76. package/src/features/Conversation/Extras/User.tsx +2 -1
  77. package/src/features/Conversation/Extras/index.ts +4 -4
  78. package/src/features/Conversation/Messages/index.ts +3 -3
  79. package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +58 -0
  80. package/src/features/Conversation/components/ChatItem/index.tsx +126 -0
  81. package/src/features/Conversation/components/ChatList/HistoryDivider.tsx +6 -3
  82. package/src/features/Conversation/components/ChatList/index.tsx +35 -79
  83. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +43 -31
  84. package/src/features/Conversation/hooks/useInitConversation.ts +4 -1
  85. package/src/features/Conversation/index.tsx +4 -3
  86. package/src/features/Conversation/types/index.tsx +35 -0
  87. package/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx +7 -2
  88. package/src/locales/create.ts +1 -1
  89. package/src/locales/default/chat.ts +10 -1
  90. package/src/locales/default/common.ts +1 -0
  91. package/src/locales/default/error.ts +19 -0
  92. package/src/services/config.ts +22 -0
  93. package/src/services/session.ts +6 -0
  94. package/src/services/topic.ts +4 -0
  95. package/src/store/chat/slices/message/selectors.ts +10 -0
  96. package/src/store/chat/slices/topic/action.ts +22 -0
  97. package/src/store/global/slices/common/action.ts +2 -0
  98. package/src/store/session/slices/session/action.test.ts +136 -0
  99. package/src/store/session/slices/session/action.ts +26 -6
  100. package/src/types/topic.ts +1 -0
  101. package/src/features/Conversation/components/ChatList/ActionsBar.tsx +0 -27
  102. package/src/features/Conversation/components/ChatList/Item.tsx +0 -236
  103. package/src/features/Conversation/container/index.tsx +0 -83
@@ -1,21 +1,32 @@
1
1
  import { ActionIconGroup } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
3
 
4
- import { RenderAction } from '../components/ChatList';
5
4
  import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
5
+ import { RenderAction } from '../types';
6
6
  import { ErrorActionsBar } from './Error';
7
7
  import { useCustomActions } from './customAction';
8
8
 
9
- export const AssistantActionsBar: RenderAction = memo(({ text, id, onActionClick, error }) => {
10
- const { regenerate, edit, copy, divider, del } = useChatListActionsBar(text);
9
+ export const AssistantActionsBar: RenderAction = memo(({ id, onActionClick, error }) => {
10
+ const { regenerate, edit, delAndRegenerate, copy, divider, del } = useChatListActionsBar();
11
11
  const { translate, tts } = useCustomActions();
12
+
12
13
  if (id === 'default') return;
13
14
 
14
- if (error) return <ErrorActionsBar onActionClick={onActionClick} text={text} />;
15
+ if (error) return <ErrorActionsBar onActionClick={onActionClick} />;
15
16
 
16
17
  return (
17
18
  <ActionIconGroup
18
- dropdownMenu={[edit, copy, regenerate, divider, tts, translate, divider, del]}
19
+ dropdownMenu={[
20
+ edit,
21
+ copy,
22
+ regenerate,
23
+ divider,
24
+ tts,
25
+ translate,
26
+ divider,
27
+ delAndRegenerate,
28
+ del,
29
+ ]}
19
30
  items={[regenerate, copy]}
20
31
  onActionClick={onActionClick}
21
32
  type="ghost"
@@ -4,8 +4,8 @@ import { memo } from 'react';
4
4
 
5
5
  import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
6
6
 
7
- export const ErrorActionsBar = memo<ActionsBarProps>(({ text, onActionClick }) => {
8
- const { regenerate, del } = useChatListActionsBar(text);
7
+ export const ErrorActionsBar = memo<ActionsBarProps>(({ onActionClick }) => {
8
+ const { regenerate, del } = useChatListActionsBar();
9
9
 
10
10
  return <ActionIconGroup items={[regenerate, del]} onActionClick={onActionClick} type="ghost" />;
11
11
  });
@@ -1,11 +1,12 @@
1
1
  import { ActionIconGroup } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
3
 
4
- import { RenderAction } from '../components/ChatList';
5
4
  import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
5
+ import { RenderAction } from '../types';
6
+
7
+ export const DefaultActionsBar: RenderAction = memo(({ onActionClick }) => {
8
+ const { del } = useChatListActionsBar();
6
9
 
7
- export const DefaultActionsBar: RenderAction = memo(({ text, onActionClick }) => {
8
- const { del } = useChatListActionsBar(text);
9
10
  return (
10
11
  <ActionIconGroup dropdownMenu={[del]} items={[]} onActionClick={onActionClick} type="ghost" />
11
12
  );
@@ -1,11 +1,11 @@
1
1
  import { ActionIconGroup } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
3
 
4
- import { RenderAction } from '../components/ChatList';
5
4
  import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
5
+ import { RenderAction } from '../types';
6
6
 
7
- export const FunctionActionsBar: RenderAction = memo(({ text, onActionClick }) => {
8
- const { regenerate, divider, del } = useChatListActionsBar(text);
7
+ export const FunctionActionsBar: RenderAction = memo(({ onActionClick }) => {
8
+ const { regenerate, divider, del } = useChatListActionsBar();
9
9
  return (
10
10
  <ActionIconGroup
11
11
  dropdownMenu={[regenerate, divider, del]}
@@ -1,12 +1,12 @@
1
1
  import { ActionIconGroup } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
3
 
4
- import { RenderAction } from '../components/ChatList';
5
4
  import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
5
+ import { RenderAction } from '../types';
6
6
  import { useCustomActions } from './customAction';
7
7
 
8
- export const UserActionsBar: RenderAction = memo(({ text, onActionClick }) => {
9
- const { regenerate, edit, copy, divider, del } = useChatListActionsBar(text);
8
+ export const UserActionsBar: RenderAction = memo(({ onActionClick }) => {
9
+ const { regenerate, edit, copy, divider, del } = useChatListActionsBar();
10
10
  const { translate, tts } = useCustomActions();
11
11
 
12
12
  return (
@@ -1,5 +1,6 @@
1
1
  import { ActionIconGroupItems } from '@lobehub/ui/es/ActionIconGroup';
2
2
  import { LanguagesIcon, Play } from 'lucide-react';
3
+ import { useMemo } from 'react';
3
4
  import { useTranslation } from 'react-i18next';
4
5
 
5
6
  import { localeOptions } from '@/locales/resources';
@@ -23,8 +24,5 @@ export const useCustomActions = () => {
23
24
  label: t('tts.action'),
24
25
  } as ActionIconGroupItems;
25
26
 
26
- return {
27
- translate,
28
- tts,
29
- };
27
+ return useMemo(() => ({ translate, tts }), []);
30
28
  };
@@ -1,67 +1,72 @@
1
+ import { copyToClipboard } from '@lobehub/ui';
2
+ import { App } from 'antd';
3
+ import { useCallback } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
1
6
  import { useChatStore } from '@/store/chat';
7
+ import { LLMRoleType } from '@/types/llm';
2
8
 
3
- import { ChatListProps } from '../components/ChatList';
9
+ import { OnActionsClick, RenderAction } from '../types';
4
10
  import { AssistantActionsBar } from './Assistant';
5
11
  import { DefaultActionsBar } from './Fallback';
6
12
  import { FunctionActionsBar } from './Function';
7
13
  import { UserActionsBar } from './User';
8
14
 
9
- export const renderActions: ChatListProps['renderActions'] = {
15
+ export const renderActions: Record<LLMRoleType, RenderAction> = {
10
16
  assistant: AssistantActionsBar,
11
17
  function: FunctionActionsBar,
12
18
  system: DefaultActionsBar,
13
19
  user: UserActionsBar,
14
20
  };
15
21
 
16
- interface ActionsClick {
17
- onClick: () => void;
18
- trigger: boolean;
19
- }
20
-
21
- export const useActionsClick = (): ChatListProps['onActionsClick'] => {
22
+ export const useActionsClick = (): OnActionsClick => {
23
+ const { t } = useTranslation('common');
22
24
  const [deleteMessage, resendMessage, translateMessage, ttsMessage] = useChatStore((s) => [
23
25
  s.deleteMessage,
24
26
  s.resendMessage,
25
27
  s.translateMessage,
26
28
  s.ttsMessage,
27
29
  ]);
30
+ const { message } = App.useApp();
31
+
32
+ return useCallback<OnActionsClick>(async (action, { id, content, error }) => {
33
+ switch (action.key) {
34
+ case 'copy': {
35
+ await copyToClipboard(content);
36
+ message.success(t('copySuccess', { defaultValue: 'Copy Success' }));
37
+ break;
38
+ }
39
+
40
+ case 'del': {
41
+ deleteMessage(id);
42
+ break;
43
+ }
44
+
45
+ case 'regenerate': {
46
+ resendMessage(id);
47
+ // if this message is an error message, we need to delete it
48
+ if (error) deleteMessage(id);
49
+ break;
50
+ }
51
+
52
+ case 'delAndRegenerate': {
53
+ resendMessage(id);
54
+ deleteMessage(id);
55
+ break;
56
+ }
28
57
 
29
- return (action, { id, error }) => {
30
- const actionsClick: ActionsClick[] = [
31
- {
32
- onClick: () => {
33
- deleteMessage(id);
34
- },
35
- trigger: action.key === 'del',
36
- },
37
- {
38
- onClick: () => {
39
- resendMessage(id);
40
- // if this message is an error message, we need to delete it
41
- if (error) deleteMessage(id);
42
- },
43
- trigger: action.key === 'regenerate',
44
- },
45
- {
46
- onClick: () => {
47
- ttsMessage(id);
48
- },
49
- trigger: action.key === 'tts',
50
- },
51
- {
52
- onClick: () => {
53
- /**
54
- * @description Click the menu item with translate item, the result is:
55
- * @key 'en-US'
56
- * @keyPath ['en-US','translate']
57
- */
58
- const lang = action.keyPath[0];
59
- translateMessage(id, lang);
60
- },
61
- trigger: action.keyPath.at(-1) === 'translate',
62
- },
63
- ];
58
+ case 'tts': {
59
+ ttsMessage(id);
60
+ break;
61
+ }
62
+ }
64
63
 
65
- actionsClick.find((item) => item.trigger)?.onClick();
66
- };
64
+ if (action.keyPath.at(-1) === 'translate') {
65
+ // click the menu item with translate item, the result is:
66
+ // key: 'en-US'
67
+ // keyPath: ['en-US','translate']
68
+ const lang = action.keyPath[0];
69
+ translateMessage(id, lang);
70
+ }
71
+ }, []);
67
72
  };
@@ -8,7 +8,7 @@ import { Flexbox } from 'react-layout-kit';
8
8
  import { useChatStore } from '@/store/chat';
9
9
  import { useGlobalStore } from '@/store/global';
10
10
 
11
- import { RenderErrorMessage } from '../components/ChatList';
11
+ import { RenderErrorMessage } from '../types';
12
12
  import APIKeyForm from './ApiKeyForm';
13
13
  import { ErrorActionContainer, FormAction } from './style';
14
14
 
@@ -1,6 +1,6 @@
1
1
  import { memo } from 'react';
2
2
 
3
- import { RenderErrorMessage } from '../components/ChatList';
3
+ import { RenderErrorMessage } from '../types';
4
4
  import APIKeyForm from './ApiKeyForm';
5
5
  import { ErrorActionContainer } from './style';
6
6
 
@@ -2,7 +2,7 @@ import { Highlighter } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
 
5
- import { RenderErrorMessage } from '../components/ChatList';
5
+ import { RenderErrorMessage } from '../types';
6
6
  import OpenAPIKey from './OpenAPIKey';
7
7
 
8
8
  interface OpenAIError {
@@ -2,7 +2,7 @@ import { Highlighter } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
 
5
- import { RenderErrorMessage } from '../../components/ChatList';
5
+ import { RenderErrorMessage } from '../../types';
6
6
 
7
7
  interface OpenAIError {
8
8
  code: 'invalid_api_key' | string;
@@ -11,7 +11,7 @@ import { useChatStore } from '@/store/chat';
11
11
  import { pluginHelpers, useToolStore } from '@/store/tool';
12
12
  import { pluginSelectors } from '@/store/tool/selectors';
13
13
 
14
- import { RenderErrorMessage } from '../../components/ChatList';
14
+ import { RenderErrorMessage } from '../../types';
15
15
  import { ErrorActionContainer, useStyles } from '../style';
16
16
 
17
17
  const PluginSettings: RenderErrorMessage['Render'] = memo(({ id, plugin }) => {
@@ -2,14 +2,14 @@ import { PluginErrorType } from '@lobehub/chat-plugin-sdk';
2
2
 
3
3
  import { ChatErrorType } from '@/types/fetch';
4
4
 
5
- import { ChatListProps } from '../components/ChatList';
5
+ import { RenderErrorMessage } from '../types';
6
6
  import InvalidAccess from './InvalidAccess';
7
7
  import OpenAPIKey from './OpenAPIKey';
8
8
  import OpenAiBizError from './OpenAiBizError';
9
9
  import PluginError from './Plugin/PluginError';
10
10
  import PluginSettings from './Plugin/PluginSettings';
11
11
 
12
- export const renderErrorMessages: ChatListProps['renderErrorMessages'] = {
12
+ export const renderErrorMessages: Record<string, RenderErrorMessage> = {
13
13
  [PluginErrorType.PluginMarketIndexNotFound]: {
14
14
  Render: PluginError,
15
15
  },
@@ -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
  );