@lobehub/lobehub 2.0.0-next.23 → 2.0.0-next.25
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/labs.json +4 -0
- package/locales/bg-BG/labs.json +4 -0
- package/locales/de-DE/labs.json +4 -0
- package/locales/en-US/labs.json +4 -0
- package/locales/es-ES/labs.json +4 -0
- package/locales/fa-IR/labs.json +4 -0
- package/locales/fr-FR/labs.json +4 -0
- package/locales/it-IT/labs.json +4 -0
- package/locales/ja-JP/labs.json +4 -0
- package/locales/ko-KR/labs.json +4 -0
- package/locales/nl-NL/labs.json +4 -0
- package/locales/pl-PL/labs.json +4 -0
- package/locales/pt-BR/labs.json +4 -0
- package/locales/ru-RU/labs.json +4 -0
- package/locales/tr-TR/labs.json +4 -0
- package/locales/vi-VN/labs.json +4 -0
- package/locales/zh-CN/labs.json +4 -0
- package/locales/zh-TW/labs.json +4 -0
- package/package.json +1 -1
- package/packages/const/src/user.ts +5 -2
- package/packages/types/src/index.ts +0 -1
- package/packages/types/src/user/index.ts +2 -88
- package/packages/types/src/user/preference.ts +105 -0
- package/renovate.json +1 -6
- package/src/app/[variants]/(main)/labs/components/LabCard.tsx +5 -5
- package/src/app/[variants]/(main)/labs/page.tsx +19 -22
- package/src/app/[variants]/(main)/settings/provider/detail/azure/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/azureai/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/bedrock/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/cloudflare/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/comfyui/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/github/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/vertexai/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +2 -4
- package/src/components/Skeleton/SkeletonSwitch.tsx +13 -0
- package/src/components/Skeleton/index.ts +2 -0
- package/src/features/ChatInput/ActionBar/index.tsx +2 -2
- package/src/features/ChatInput/InputEditor/index.tsx +2 -2
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +152 -0
- package/src/features/Conversation/Messages/Group/Actions/WithoutContentId.tsx +70 -0
- package/src/features/Conversation/Messages/Group/Actions/index.tsx +21 -0
- package/src/features/Conversation/Messages/Group/ContentBlock.tsx +91 -0
- package/src/features/Conversation/Messages/Group/EditState.tsx +51 -0
- package/src/features/Conversation/Messages/Group/Error/index.tsx +53 -0
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +73 -0
- package/src/features/Conversation/Messages/Group/MessageContent.tsx +39 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/BuiltinPluginTitle.tsx +49 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/Debug.tsx +70 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/PluginResult.tsx +34 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/PluginState.tsx +18 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/Settings.tsx +40 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/ToolTitle.tsx +92 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +176 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/ObjectEntity.tsx +81 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/ValueCell.tsx +43 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/index.tsx +134 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/CustomRender.tsx +88 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/ErrorResponse.tsx +35 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/LoadingPlaceholder/index.tsx +29 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +66 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +105 -0
- package/src/features/Conversation/Messages/Group/Tool/index.tsx +75 -0
- package/src/features/Conversation/Messages/Group/Tools.tsx +46 -0
- package/src/features/Conversation/Messages/Group/index.tsx +140 -0
- package/src/features/Conversation/Messages/index.tsx +12 -0
- package/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx +2 -2
- package/src/locales/default/labs.ts +4 -0
- package/src/server/routers/lambda/message.ts +5 -20
- package/src/services/chat/contextEngineering.ts +6 -5
- package/src/services/message/server.ts +10 -10
- package/src/services/message/type.ts +0 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +309 -2
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +2 -22
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +272 -14
- package/src/store/user/selectors.ts +1 -1
- package/src/store/user/slices/preference/action.ts +8 -1
- package/src/store/user/slices/preference/selectors/index.ts +2 -0
- package/src/store/user/slices/preference/selectors/labPrefer.ts +13 -0
- package/src/store/user/slices/preference/{selectors.ts → selectors/preference.ts} +0 -2
- /package/src/{app/[variants]/(main)/settings/provider/features/ProviderConfig → components/Skeleton}/SkeletonInput.tsx +0 -0
|
@@ -6,11 +6,11 @@ import { ModelProvider } from 'model-bank';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import { FormInput, FormPassword } from '@/components/FormInput';
|
|
9
|
+
import { SkeletonInput } from '@/components/Skeleton';
|
|
9
10
|
import { AzureProviderCard } from '@/config/modelProviders';
|
|
10
11
|
import { aiModelSelectors, aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
|
11
12
|
|
|
12
13
|
import { KeyVaultsConfigKey, LLMProviderApiTokenKey, LLMProviderBaseUrlKey } from '../../const';
|
|
13
|
-
import { SkeletonInput } from '../../features/ProviderConfig';
|
|
14
14
|
import { ProviderItem } from '../../type';
|
|
15
15
|
import ProviderDetail from '../default';
|
|
16
16
|
|
|
@@ -4,11 +4,11 @@ import { ModelProvider } from 'model-bank';
|
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { FormInput, FormPassword } from '@/components/FormInput';
|
|
7
|
+
import { SkeletonInput } from '@/components/Skeleton';
|
|
7
8
|
import { AzureAIProviderCard } from '@/config/modelProviders';
|
|
8
9
|
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
|
9
10
|
|
|
10
11
|
import { KeyVaultsConfigKey, LLMProviderApiTokenKey, LLMProviderBaseUrlKey } from '../../const';
|
|
11
|
-
import { SkeletonInput } from '../../features/ProviderConfig';
|
|
12
12
|
import { ProviderItem } from '../../type';
|
|
13
13
|
import ProviderDetail from '../default';
|
|
14
14
|
|
|
@@ -4,12 +4,12 @@ import { Select } from '@lobehub/ui';
|
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { FormPassword } from '@/components/FormInput';
|
|
7
|
+
import { SkeletonInput } from '@/components/Skeleton';
|
|
7
8
|
import { BedrockProviderCard } from '@/config/modelProviders';
|
|
8
9
|
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
|
9
10
|
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
10
11
|
|
|
11
12
|
import { KeyVaultsConfigKey } from '../../const';
|
|
12
|
-
import { SkeletonInput } from '../../features/ProviderConfig';
|
|
13
13
|
import { ProviderItem } from '../../type';
|
|
14
14
|
import ProviderDetail from '../default';
|
|
15
15
|
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
|
|
5
5
|
import { FormInput, FormPassword } from '@/components/FormInput';
|
|
6
|
+
import { SkeletonInput } from '@/components/Skeleton';
|
|
6
7
|
import { CloudflareProviderCard } from '@/config/modelProviders';
|
|
7
8
|
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
|
8
9
|
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
9
10
|
|
|
10
11
|
import { KeyVaultsConfigKey } from '../../const';
|
|
11
|
-
import { SkeletonInput } from '../../features/ProviderConfig';
|
|
12
12
|
import { ProviderItem } from '../../type';
|
|
13
13
|
import ProviderDetail from '../default';
|
|
14
14
|
|
|
@@ -5,12 +5,12 @@ import { useTranslation } from 'react-i18next';
|
|
|
5
5
|
|
|
6
6
|
import { FormInput, FormPassword } from '@/components/FormInput';
|
|
7
7
|
import KeyValueEditor from '@/components/KeyValueEditor';
|
|
8
|
+
import { SkeletonInput } from '@/components/Skeleton';
|
|
8
9
|
import { ComfyUIProviderCard } from '@/config/modelProviders';
|
|
9
10
|
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
|
10
11
|
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
11
12
|
|
|
12
13
|
import { KeyVaultsConfigKey } from '../../const';
|
|
13
|
-
import { SkeletonInput } from '../../features/ProviderConfig';
|
|
14
14
|
import { ProviderItem } from '../../type';
|
|
15
15
|
import ProviderDetail from '../default';
|
|
16
16
|
|
|
@@ -5,12 +5,12 @@ import { createStyles } from 'antd-style';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { FormPassword } from '@/components/FormInput';
|
|
8
|
+
import { SkeletonInput } from '@/components/Skeleton';
|
|
8
9
|
import { GithubProviderCard } from '@/config/modelProviders';
|
|
9
10
|
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
|
10
11
|
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
11
12
|
|
|
12
13
|
import { KeyVaultsConfigKey, LLMProviderApiTokenKey } from '../../const';
|
|
13
|
-
import { SkeletonInput } from '../../features/ProviderConfig';
|
|
14
14
|
import { ProviderItem } from '../../type';
|
|
15
15
|
import ProviderDetail from '../default';
|
|
16
16
|
|
|
@@ -5,12 +5,12 @@ import { createStyles } from 'antd-style';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { FormPassword } from '@/components/FormInput';
|
|
8
|
+
import { SkeletonInput } from '@/components/Skeleton';
|
|
8
9
|
import { VertexAIProviderCard } from '@/config/modelProviders';
|
|
9
10
|
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
|
10
11
|
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
11
12
|
|
|
12
13
|
import { KeyVaultsConfigKey, LLMProviderApiTokenKey } from '../../const';
|
|
13
|
-
import { SkeletonInput } from '../../features/ProviderConfig';
|
|
14
14
|
import { ProviderItem } from '../../type';
|
|
15
15
|
import ProviderDetail from '../default';
|
|
16
16
|
|
|
@@ -21,6 +21,7 @@ import urlJoin from 'url-join';
|
|
|
21
21
|
import { z } from 'zod';
|
|
22
22
|
|
|
23
23
|
import { FormInput, FormPassword } from '@/components/FormInput';
|
|
24
|
+
import { SkeletonInput, SkeletonSwitch } from '@/components/Skeleton';
|
|
24
25
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
25
26
|
import { AES_GCM_URL, BASE_PROVIDER_DOC_URL } from '@/const/url';
|
|
26
27
|
import { isDesktop, isServerMode } from '@/const/version';
|
|
@@ -34,7 +35,6 @@ import {
|
|
|
34
35
|
import { KeyVaultsConfigKey, LLMProviderApiTokenKey, LLMProviderBaseUrlKey } from '../../const';
|
|
35
36
|
import Checker, { CheckErrorRender } from './Checker';
|
|
36
37
|
import EnableSwitch from './EnableSwitch';
|
|
37
|
-
import { SkeletonInput } from './SkeletonInput';
|
|
38
38
|
import UpdateProviderInfo from './UpdateProviderInfo';
|
|
39
39
|
|
|
40
40
|
const useStyles = createStyles(({ css, prefixCls, responsive, token }) => ({
|
|
@@ -298,7 +298,7 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
|
298
298
|
(showApiKey && isProviderApiKeyNotEmpty));
|
|
299
299
|
const clientFetchItem = showClientFetch && {
|
|
300
300
|
children: isLoading ? (
|
|
301
|
-
<
|
|
301
|
+
<SkeletonSwitch />
|
|
302
302
|
) : (
|
|
303
303
|
<Switch checked={isFetchOnClient} disabled={configUpdating} />
|
|
304
304
|
),
|
|
@@ -424,5 +424,3 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
|
424
424
|
);
|
|
425
425
|
|
|
426
426
|
export default ProviderConfig;
|
|
427
|
-
|
|
428
|
-
export { SkeletonInput } from './SkeletonInput';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Skeleton } from 'antd';
|
|
2
|
+
import { css, cx } from 'antd-style';
|
|
3
|
+
|
|
4
|
+
const switchLoading = cx(css`
|
|
5
|
+
width: 44px !important;
|
|
6
|
+
min-width: 44px !important;
|
|
7
|
+
height: 22px !important;
|
|
8
|
+
border-radius: 12px !important;
|
|
9
|
+
`);
|
|
10
|
+
|
|
11
|
+
export const SkeletonSwitch = () => {
|
|
12
|
+
return <Skeleton.Button active className={switchLoading} />;
|
|
13
|
+
};
|
|
@@ -4,7 +4,7 @@ import { memo, useMemo } from 'react';
|
|
|
4
4
|
import { useGlobalStore } from '@/store/global';
|
|
5
5
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
6
6
|
import { useUserStore } from '@/store/user';
|
|
7
|
-
import {
|
|
7
|
+
import { labPreferSelectors } from '@/store/user/slices/preference/selectors';
|
|
8
8
|
|
|
9
9
|
import { ActionKeys, actionMap } from '../ActionBar/config';
|
|
10
10
|
import { useChatInputStore } from '../store';
|
|
@@ -44,7 +44,7 @@ const ActionToolbar = memo(() => {
|
|
|
44
44
|
systemStatusSelectors.expandInputActionbar(s),
|
|
45
45
|
s.toggleExpandInputActionbar,
|
|
46
46
|
]);
|
|
47
|
-
const enableRichRender = useUserStore(
|
|
47
|
+
const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
|
|
48
48
|
|
|
49
49
|
const leftActions = useChatInputStore((s) =>
|
|
50
50
|
s.leftActions.filter((item) => (enableRichRender ? true : item !== 'typo')),
|
|
@@ -21,7 +21,7 @@ import { useHotkeysContext } from 'react-hotkeys-hook';
|
|
|
21
21
|
import { useTranslation } from 'react-i18next';
|
|
22
22
|
|
|
23
23
|
import { useUserStore } from '@/store/user';
|
|
24
|
-
import { preferenceSelectors, settingsSelectors } from '@/store/user/selectors';
|
|
24
|
+
import { labPreferSelectors, preferenceSelectors, settingsSelectors } from '@/store/user/selectors';
|
|
25
25
|
|
|
26
26
|
import { useChatInputStore, useStoreApi } from '../store';
|
|
27
27
|
import Placeholder from './Placeholder';
|
|
@@ -69,7 +69,7 @@ const InputEditor = memo<{ defaultRows?: number }>(({ defaultRows = 2 }) => {
|
|
|
69
69
|
};
|
|
70
70
|
}, [state.isEmpty]);
|
|
71
71
|
|
|
72
|
-
const enableRichRender = useUserStore(
|
|
72
|
+
const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
|
|
73
73
|
|
|
74
74
|
const richRenderProps = useMemo(
|
|
75
75
|
() =>
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { AssistantContentBlock, UIChatMessage } from '@lobechat/types';
|
|
2
|
+
import { ActionIconGroup, type ActionIconGroupEvent, ActionIconGroupItemType } from '@lobehub/ui';
|
|
3
|
+
import { App } from 'antd';
|
|
4
|
+
import { useSearchParams } from 'next/navigation';
|
|
5
|
+
import { memo, use, useCallback, useContext, useMemo, useState } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
8
|
+
import ShareMessageModal from '@/features/Conversation/components/ShareMessageModal';
|
|
9
|
+
import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
10
|
+
import { useChatStore } from '@/store/chat';
|
|
11
|
+
import { threadSelectors } from '@/store/chat/selectors';
|
|
12
|
+
import { useSessionStore } from '@/store/session';
|
|
13
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
14
|
+
|
|
15
|
+
import { InPortalThreadContext } from '../../../context/InPortalThreadContext';
|
|
16
|
+
import { useChatListActionsBar } from '../../../hooks/useChatListActionsBar';
|
|
17
|
+
|
|
18
|
+
interface GroupActionsProps {
|
|
19
|
+
contentBlock?: AssistantContentBlock;
|
|
20
|
+
data: UIChatMessage;
|
|
21
|
+
id: string;
|
|
22
|
+
index: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const WithContentId = memo<GroupActionsProps>(({ id, data, index, contentBlock }) => {
|
|
26
|
+
const { tools } = data;
|
|
27
|
+
const [isThreadMode, hasThread] = useChatStore((s) => [
|
|
28
|
+
!!s.activeThreadId,
|
|
29
|
+
threadSelectors.hasThreadBySourceMsgId(id)(s),
|
|
30
|
+
]);
|
|
31
|
+
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
32
|
+
const [showShareModal, setShareModal] = useState(false);
|
|
33
|
+
|
|
34
|
+
const { edit, delAndRegenerate, copy, divider, del, branching, share } = useChatListActionsBar({
|
|
35
|
+
hasThread,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const hasTools = !!tools;
|
|
39
|
+
|
|
40
|
+
const inPortalThread = useContext(InPortalThreadContext);
|
|
41
|
+
const inThread = isThreadMode || inPortalThread;
|
|
42
|
+
|
|
43
|
+
const items = useMemo(() => {
|
|
44
|
+
if (hasTools) return [delAndRegenerate, copy];
|
|
45
|
+
|
|
46
|
+
return [edit, copy, inThread || isGroupSession ? null : branching].filter(
|
|
47
|
+
Boolean,
|
|
48
|
+
) as ActionIconGroupItemType[];
|
|
49
|
+
}, [inThread, hasTools, isGroupSession]);
|
|
50
|
+
|
|
51
|
+
const { t } = useTranslation('common');
|
|
52
|
+
const searchParams = useSearchParams();
|
|
53
|
+
const topic = searchParams.get('topic');
|
|
54
|
+
const [
|
|
55
|
+
deleteMessage,
|
|
56
|
+
translateMessage,
|
|
57
|
+
delAndRegenerateMessage,
|
|
58
|
+
copyMessage,
|
|
59
|
+
openThreadCreator,
|
|
60
|
+
delAndResendThreadMessage,
|
|
61
|
+
toggleMessageEditing,
|
|
62
|
+
] = useChatStore((s) => [
|
|
63
|
+
s.deleteMessage,
|
|
64
|
+
s.translateMessage,
|
|
65
|
+
s.delAndRegenerateMessage,
|
|
66
|
+
s.copyMessage,
|
|
67
|
+
s.openThreadCreator,
|
|
68
|
+
s.delAndResendThreadMessage,
|
|
69
|
+
s.toggleMessageEditing,
|
|
70
|
+
]);
|
|
71
|
+
const { message } = App.useApp();
|
|
72
|
+
const virtuosoRef = use(VirtuosoContext);
|
|
73
|
+
|
|
74
|
+
const onActionClick = useCallback(
|
|
75
|
+
async (action: ActionIconGroupEvent) => {
|
|
76
|
+
switch (action.key) {
|
|
77
|
+
case 'edit': {
|
|
78
|
+
toggleMessageEditing(id, true);
|
|
79
|
+
|
|
80
|
+
virtuosoRef?.current?.scrollIntoView({ align: 'start', behavior: 'auto', index });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!data) return;
|
|
84
|
+
|
|
85
|
+
switch (action.key) {
|
|
86
|
+
case 'copy': {
|
|
87
|
+
if (!contentBlock) return;
|
|
88
|
+
await copyMessage(id, contentBlock.content);
|
|
89
|
+
message.success(t('copySuccess', { defaultValue: 'Copy Success' }));
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case 'branching': {
|
|
93
|
+
if (!topic) {
|
|
94
|
+
message.warning(t('branchingRequiresSavedTopic'));
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
openThreadCreator(id);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'del': {
|
|
102
|
+
deleteMessage(id);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
case 'delAndRegenerate': {
|
|
107
|
+
if (inPortalThread) {
|
|
108
|
+
delAndResendThreadMessage(id);
|
|
109
|
+
} else {
|
|
110
|
+
delAndRegenerateMessage(id);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
case 'share': {
|
|
116
|
+
setShareModal(true);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (action.keyPath.at(-1) === 'translate') {
|
|
122
|
+
// click the menu data with translate data, the result is:
|
|
123
|
+
// key: 'en-US'
|
|
124
|
+
// keyPath: ['en-US','translate']
|
|
125
|
+
const lang = action.keyPath[0];
|
|
126
|
+
translateMessage(id, lang);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
[data, topic],
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<>
|
|
134
|
+
<ActionIconGroup
|
|
135
|
+
items={items}
|
|
136
|
+
menu={{
|
|
137
|
+
items: [edit, copy, divider, share, divider, delAndRegenerate, del],
|
|
138
|
+
}}
|
|
139
|
+
onActionClick={onActionClick}
|
|
140
|
+
/>
|
|
141
|
+
<ShareMessageModal
|
|
142
|
+
message={data!}
|
|
143
|
+
onCancel={() => {
|
|
144
|
+
setShareModal(false);
|
|
145
|
+
}}
|
|
146
|
+
open={showShareModal}
|
|
147
|
+
/>
|
|
148
|
+
</>
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
export default WithContentId;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { UIChatMessage } from '@lobechat/types';
|
|
2
|
+
import { ActionIconGroup, type ActionIconGroupEvent, ActionIconGroupItemType } from '@lobehub/ui';
|
|
3
|
+
import { useSearchParams } from 'next/navigation';
|
|
4
|
+
import { memo, useCallback, useContext, useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
import { useChatStore } from '@/store/chat';
|
|
7
|
+
import { threadSelectors } from '@/store/chat/selectors';
|
|
8
|
+
import { useSessionStore } from '@/store/session';
|
|
9
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
10
|
+
|
|
11
|
+
import { InPortalThreadContext } from '../../../context/InPortalThreadContext';
|
|
12
|
+
import { useChatListActionsBar } from '../../../hooks/useChatListActionsBar';
|
|
13
|
+
|
|
14
|
+
interface GroupActionsProps {
|
|
15
|
+
data: UIChatMessage;
|
|
16
|
+
id: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const WithoutContentId = memo<GroupActionsProps>(({ id, data }) => {
|
|
20
|
+
const [isThreadMode, hasThread] = useChatStore((s) => [
|
|
21
|
+
!!s.activeThreadId,
|
|
22
|
+
threadSelectors.hasThreadBySourceMsgId(id)(s),
|
|
23
|
+
]);
|
|
24
|
+
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
25
|
+
|
|
26
|
+
const { delAndRegenerate, del } = useChatListActionsBar({ hasThread });
|
|
27
|
+
|
|
28
|
+
const inPortalThread = useContext(InPortalThreadContext);
|
|
29
|
+
const inThread = isThreadMode || inPortalThread;
|
|
30
|
+
|
|
31
|
+
const items = useMemo(() => {
|
|
32
|
+
return [delAndRegenerate, del].filter(Boolean) as ActionIconGroupItemType[];
|
|
33
|
+
}, [inThread, isGroupSession]);
|
|
34
|
+
|
|
35
|
+
const searchParams = useSearchParams();
|
|
36
|
+
const topic = searchParams.get('topic');
|
|
37
|
+
|
|
38
|
+
const [deleteMessage, delAndRegenerateMessage, delAndResendThreadMessage] = useChatStore((s) => [
|
|
39
|
+
s.deleteMessage,
|
|
40
|
+
s.delAndRegenerateMessage,
|
|
41
|
+
s.delAndResendThreadMessage,
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
const onActionClick = useCallback(
|
|
45
|
+
async (action: ActionIconGroupEvent) => {
|
|
46
|
+
if (!data) return;
|
|
47
|
+
|
|
48
|
+
switch (action.key) {
|
|
49
|
+
case 'del': {
|
|
50
|
+
deleteMessage(id);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 'delAndRegenerate': {
|
|
55
|
+
if (inPortalThread) {
|
|
56
|
+
delAndResendThreadMessage(id);
|
|
57
|
+
} else {
|
|
58
|
+
delAndRegenerateMessage(id);
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[data, topic],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return <ActionIconGroup items={items} onActionClick={onActionClick} />;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export default WithoutContentId;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AssistantContentBlock, UIChatMessage } from '@lobechat/types';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
|
|
4
|
+
import WithContentId from './WithContentId';
|
|
5
|
+
import WithoutContentId from './WithoutContentId';
|
|
6
|
+
|
|
7
|
+
interface GroupActionsProps {
|
|
8
|
+
contentBlock?: AssistantContentBlock;
|
|
9
|
+
contentId?: string;
|
|
10
|
+
data: UIChatMessage;
|
|
11
|
+
id: string;
|
|
12
|
+
index: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const GroupActionsBar = memo<GroupActionsProps>(
|
|
16
|
+
({ id, data, contentBlock, index, contentId }) => {
|
|
17
|
+
if (!contentId) return <WithoutContentId data={data} id={id} />;
|
|
18
|
+
|
|
19
|
+
return <WithContentId contentBlock={contentBlock} data={data} id={contentId} index={index} />;
|
|
20
|
+
},
|
|
21
|
+
);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { AssistantContentBlock } from '@lobechat/types';
|
|
2
|
+
import { MarkdownProps } from '@lobehub/ui';
|
|
3
|
+
import { memo, useMemo } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { LOADING_FLAT } from '@/const/message';
|
|
7
|
+
import { markdownElements } from '@/features/Conversation/MarkdownElements';
|
|
8
|
+
import Reasoning from '@/features/Conversation/Messages/Assistant/Reasoning';
|
|
9
|
+
import { useChatStore } from '@/store/chat';
|
|
10
|
+
import { aiChatSelectors, messageStateSelectors } from '@/store/chat/selectors';
|
|
11
|
+
import { useUserStore } from '@/store/user';
|
|
12
|
+
import { userGeneralSettingsSelectors } from '@/store/user/selectors';
|
|
13
|
+
|
|
14
|
+
import ImageFileListViewer from '../User/ImageFileListViewer';
|
|
15
|
+
import ErrorContent from './Error';
|
|
16
|
+
import MessageContent from './MessageContent';
|
|
17
|
+
import { Tools } from './Tools';
|
|
18
|
+
|
|
19
|
+
const rehypePlugins = markdownElements.map((element) => element.rehypePlugin).filter(Boolean);
|
|
20
|
+
const remarkPlugins = markdownElements.map((element) => element.remarkPlugin).filter(Boolean);
|
|
21
|
+
|
|
22
|
+
interface ContentBlockProps extends AssistantContentBlock {
|
|
23
|
+
index: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const ContentBlock = memo<ContentBlockProps>((props) => {
|
|
27
|
+
const { id, tools, content, imageList, reasoning, error } = props;
|
|
28
|
+
const showImageItems = !!imageList && imageList.length > 0;
|
|
29
|
+
const isReasoning = useChatStore(aiChatSelectors.isMessageInReasoning(id));
|
|
30
|
+
|
|
31
|
+
const hasTools = tools && tools.length > 0;
|
|
32
|
+
const showReasoning =
|
|
33
|
+
(!!reasoning && reasoning.content?.trim() !== '') || (!reasoning && isReasoning);
|
|
34
|
+
|
|
35
|
+
const { transitionMode, highlighterTheme, mermaidTheme } = useUserStore(
|
|
36
|
+
userGeneralSettingsSelectors.config,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const generating = useChatStore(messageStateSelectors.isMessageGenerating(id));
|
|
40
|
+
|
|
41
|
+
const animated = transitionMode === 'fadeIn' && generating;
|
|
42
|
+
|
|
43
|
+
const components = useMemo(
|
|
44
|
+
() =>
|
|
45
|
+
Object.fromEntries(
|
|
46
|
+
markdownElements.map((element) => {
|
|
47
|
+
const Component = element.Component;
|
|
48
|
+
|
|
49
|
+
return [element.tag, (props: any) => <Component {...props} id={id} />];
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
[id],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const markdownProps: Omit<MarkdownProps, 'className' | 'style' | 'children'> = useMemo(
|
|
56
|
+
() => ({
|
|
57
|
+
animated,
|
|
58
|
+
componentProps: {
|
|
59
|
+
highlight: {
|
|
60
|
+
theme: highlighterTheme,
|
|
61
|
+
},
|
|
62
|
+
mermaid: { theme: mermaidTheme },
|
|
63
|
+
},
|
|
64
|
+
components,
|
|
65
|
+
enableCustomFootnotes: true,
|
|
66
|
+
rehypePlugins,
|
|
67
|
+
remarkPlugins,
|
|
68
|
+
}),
|
|
69
|
+
[animated, components, highlighterTheme, mermaidTheme],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (error && (content === LOADING_FLAT || !content))
|
|
73
|
+
return <ErrorContent error={error} id={id} />;
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Flexbox gap={8} id={id}>
|
|
77
|
+
{showReasoning && <Reasoning {...reasoning} id={id} />}
|
|
78
|
+
|
|
79
|
+
{/* Content - markdown text */}
|
|
80
|
+
{content && (
|
|
81
|
+
<MessageContent content={content} hasTools={hasTools} markdownProps={markdownProps} />
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
{/* Image files */}
|
|
85
|
+
{showImageItems && <ImageFileListViewer items={imageList} />}
|
|
86
|
+
|
|
87
|
+
{/* Tools */}
|
|
88
|
+
{hasTools && <Tools messageId={id} tools={tools} />}
|
|
89
|
+
</Flexbox>
|
|
90
|
+
);
|
|
91
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { MessageInput } from '@lobehub/ui/chat';
|
|
2
|
+
import { memo, useMemo } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { useChatStore } from '@/store/chat';
|
|
7
|
+
|
|
8
|
+
export interface EditStateProps {
|
|
9
|
+
content: string;
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const EditState = memo<EditStateProps>(({ id, content }) => {
|
|
14
|
+
const { t } = useTranslation('common');
|
|
15
|
+
|
|
16
|
+
const text = useMemo(
|
|
17
|
+
() => ({
|
|
18
|
+
cancel: t('cancel'),
|
|
19
|
+
confirm: t('ok'),
|
|
20
|
+
edit: t('edit'),
|
|
21
|
+
}),
|
|
22
|
+
[],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const [toggleMessageEditing, updateMessageContent] = useChatStore((s) => [
|
|
26
|
+
s.toggleMessageEditing,
|
|
27
|
+
s.modifyMessageContent,
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const onEditingChange = (value: string) => {
|
|
31
|
+
updateMessageContent(id, value);
|
|
32
|
+
toggleMessageEditing(id, false);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Flexbox paddingBlock={'0 8px'}>
|
|
37
|
+
<MessageInput
|
|
38
|
+
defaultValue={content ? String(content) : ''}
|
|
39
|
+
editButtonSize={'small'}
|
|
40
|
+
onCancel={() => {
|
|
41
|
+
toggleMessageEditing(id, false);
|
|
42
|
+
}}
|
|
43
|
+
onConfirm={onEditingChange}
|
|
44
|
+
text={text}
|
|
45
|
+
variant={'outlined'}
|
|
46
|
+
/>
|
|
47
|
+
</Flexbox>
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export default EditState;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ChatMessageError } from '@lobechat/types';
|
|
2
|
+
import { Alert } from '@lobehub/ui';
|
|
3
|
+
import { Button } from 'antd';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import ErrorMessageExtra, { useErrorContent } from '@/features/Conversation/Error';
|
|
9
|
+
import { useChatStore } from '@/store/chat';
|
|
10
|
+
|
|
11
|
+
export interface ErrorContentProps {
|
|
12
|
+
error?: ChatMessageError;
|
|
13
|
+
id: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ErrorContent = memo<ErrorContentProps>(({ error, id }) => {
|
|
17
|
+
const { t } = useTranslation('common');
|
|
18
|
+
const errorProps = useErrorContent(error);
|
|
19
|
+
|
|
20
|
+
const [deleteMessage] = useChatStore((s) => [s.deleteMessage]);
|
|
21
|
+
const message = <ErrorMessageExtra block data={{ error, id }} />;
|
|
22
|
+
|
|
23
|
+
if (!error?.message) {
|
|
24
|
+
if (!message) return null;
|
|
25
|
+
return <Flexbox>{message}</Flexbox>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Flexbox>
|
|
30
|
+
<Alert
|
|
31
|
+
action={
|
|
32
|
+
<Button
|
|
33
|
+
color={'default'}
|
|
34
|
+
onClick={() => {
|
|
35
|
+
deleteMessage(id);
|
|
36
|
+
}}
|
|
37
|
+
size={'small'}
|
|
38
|
+
variant={'filled'}
|
|
39
|
+
>
|
|
40
|
+
{t('delete')}
|
|
41
|
+
</Button>
|
|
42
|
+
}
|
|
43
|
+
closable={false}
|
|
44
|
+
extra={message}
|
|
45
|
+
showIcon
|
|
46
|
+
type={'error'}
|
|
47
|
+
{...errorProps}
|
|
48
|
+
/>
|
|
49
|
+
</Flexbox>
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export default ErrorContent;
|